SubNavigation: refactor to be contextual based on Column

This commit is contained in:
Alex Gleason 2021-09-27 13:38:02 -05:00
parent db4a0d33c8
commit 60c3243fcb
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
11 changed files with 127 additions and 160 deletions

View file

@ -2,17 +2,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { injectIntl, defineMessages } from 'react-intl';
import Icon from 'soapbox/components/icon';
// import classNames from 'classnames';
// import { injectIntl, defineMessages } from 'react-intl';
// import Icon from 'soapbox/components/icon';
import SubNavigation from 'soapbox/components/sub_navigation';
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
});
// const messages = defineMessages({
// show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
// hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
// });
export default @injectIntl
class ColumnHeader extends React.PureComponent {
export default class ColumnHeader extends React.PureComponent {
static contextTypes = {
router: PropTypes.object,
@ -54,69 +54,75 @@ class ColumnHeader extends React.PureComponent {
}
render() {
const { title, icon, active, children, extraButton, intl: { formatMessage } } = this.props;
const { collapsed, animating } = this.state;
const { title } = this.props;
const wrapperClassName = classNames('column-header__wrapper', {
'active': active,
});
const buttonClassName = classNames('column-header', {
'active': active,
});
const collapsibleClassName = classNames('column-header__collapsible', {
'collapsed': collapsed,
'animating': animating,
});
const collapsibleButtonClassName = classNames('column-header__button', {
'active': !collapsed,
});
let extraContent, collapseButton;
if (children) {
extraContent = (
<div key='extra-content' className='column-header__collapsible__extra'>
{children}
</div>
);
}
const collapsedContent = [
extraContent,
];
if (children) {
collapseButton = <button className={collapsibleButtonClassName} title={formatMessage(collapsed ? messages.show : messages.hide)} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><Icon id='sliders' /></button>;
}
const hasTitle = icon && title;
return (
<div className={wrapperClassName}>
<h1 className={buttonClassName}>
{hasTitle && (
<button>
<Icon id={icon} fixedWidth className='column-header__icon' />
{title}
</button>
)}
<div className='column-header__buttons'>
{extraButton}
{collapseButton}
</div>
</h1>
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
<div className='column-header__collapsible-inner'>
{(!collapsed || animating) && collapsedContent}
</div>
</div>
</div>
);
return <SubNavigation message={title} />;
}
// render() {
// const { title, icon, active, children, extraButton, intl: { formatMessage } } = this.props;
// const { collapsed, animating } = this.state;
//
// const wrapperClassName = classNames('column-header__wrapper', {
// 'active': active,
// });
//
// const buttonClassName = classNames('column-header', {
// 'active': active,
// });
//
// const collapsibleClassName = classNames('column-header__collapsible', {
// 'collapsed': collapsed,
// 'animating': animating,
// });
//
// const collapsibleButtonClassName = classNames('column-header__button', {
// 'active': !collapsed,
// });
//
// let extraContent, collapseButton;
//
// if (children) {
// extraContent = (
// <div key='extra-content' className='column-header__collapsible__extra'>
// {children}
// </div>
// );
// }
//
// const collapsedContent = [
// extraContent,
// ];
//
// if (children) {
// collapseButton = <button className={collapsibleButtonClassName} title={formatMessage(collapsed ? messages.show : messages.hide)} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><Icon id='cog' /></button>;
// }
//
// const hasTitle = icon && title;
//
// return (
// <div className={wrapperClassName}>
// <h1 className={buttonClassName}>
// {hasTitle && (
// <button>
// <Icon id={icon} fixedWidth className='column-header__icon' />
// {title}
// </button>
// )}
//
// <div className='column-header__buttons'>
// {extraButton}
// {collapseButton}
// </div>
// </h1>
//
// <div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
// <div className='column-header__collapsible-inner'>
// {(!collapsed || animating) && collapsedContent}
// </div>
// </div>
// </div>
// );
// }
}

View file

@ -1,52 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import Icon from 'soapbox/components/icon';
import { withRouter, matchPath } from 'react-router-dom';
const routes = [
['status', { path: '/@:username/posts/:statusId' }],
['account', { path: '/@:username' }],
['local_timeline', { path: '/timeline/local' }],
['fediverse_timeline', { path: '/timeline/fediverse' }],
['remote_timeline', { path: '/timeline/:instance' }],
];
const findRoute = path => routes.find(v => matchPath(path, v[1]));
const findRouteType = path => {
const route = findRoute(path) || [];
return route[0];
};
const findMatch = path => {
const route = findRoute(path) || [];
return matchPath(path, route[1]);
};
const messages = defineMessages({
status: { id: 'sub_navigation.status', defaultMessage: 'Post' },
account: { id: 'sub_navigation.account', defaultMessage: 'Profile' },
local_timeline: { id: 'sub_navigation.local_timeline', defaultMessage: '{siteTitle}' },
fediverse_timeline: { id: 'sub_navigation.fediverse_timeline', defaultMessage: 'Fediverse' },
remote_timeline: { id: 'sub_navigation.remote_timeline', defaultMessage: '{instance}' },
});
const mapStateToProps = state => {
return {
siteTitle: state.getIn(['instance', 'title']),
};
};
export default @withRouter
@connect(mapStateToProps)
@injectIntl
class SubNavigation extends React.PureComponent {
export default class SubNavigation extends React.PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
siteTitle: PropTypes.string,
message: PropTypes.string,
}
static contextTypes = {
@ -67,24 +28,8 @@ class SubNavigation extends React.PureComponent {
}
}
getMessage = () => {
const path = this.context.router.history.location.pathname;
const type = findRouteType(path);
return messages[type] || null;
}
getParams = () => {
const path = this.context.router.history.location.pathname;
return (findMatch(path) || {}).params;
}
render() {
const { intl, siteTitle } = this.props;
const message = this.getMessage();
const params = this.getParams();
if (!message) return null;
const { message } = this.props;
return (
<div className='sub-navigation'>
@ -98,7 +43,7 @@ class SubNavigation extends React.PureComponent {
<FormattedMessage id='sub_navigation.back' defaultMessage='Back' />
</button>
<div className='sub-navigation__message'>
{intl.formatMessage(message, { siteTitle, ...params })}
{message}
</div>
</div>
</div>

View file

@ -7,6 +7,7 @@ import Column from '../../components/column';
import { expandCommunityTimeline } from '../../actions/timelines';
import { connectCommunityStream } from '../../actions/streaming';
import { getSettings } from 'soapbox/actions/settings';
import SubNavigation from 'soapbox/components/sub_navigation';
const messages = defineMessages({
title: { id: 'column.community', defaultMessage: 'Local timeline' },
@ -73,6 +74,7 @@ class CommunityTimeline extends React.PureComponent {
return (
<Column label={intl.formatMessage(messages.title)}>
<SubNavigation message={intl.formatMessage(messages.title)} />
<StatusListContainer
scrollKey={`${timelineId}_timeline`}
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}

View file

@ -10,6 +10,7 @@ import { expandPublicTimeline } from '../../actions/timelines';
import { connectPublicStream } from '../../actions/streaming';
import { Link } from 'react-router-dom';
import { changeSetting, getSettings } from 'soapbox/actions/settings';
import SubNavigation from 'soapbox/components/sub_navigation';
const messages = defineMessages({
title: { id: 'column.public', defaultMessage: 'Federated timeline' },
@ -97,6 +98,7 @@ class CommunityTimeline extends React.PureComponent {
return (
<Column label={intl.formatMessage(messages.title)}>
<SubNavigation message={intl.formatMessage(messages.title)} />
<PinnedHostsPicker />
{showExplanationBox && <div className='explanation-box'>
<Accordion

View file

@ -49,8 +49,10 @@ import { textForScreenReader, defaultMediaVisibility } from '../../components/st
import { getSettings } from 'soapbox/actions/settings';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import { deactivateUserModal, deleteUserModal, deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation';
import SubNavigation from 'soapbox/components/sub_navigation';
const messages = defineMessages({
title: { id: 'status.title', defaultMessage: 'Post' },
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this post?' },
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
@ -569,6 +571,7 @@ class Status extends ImmutablePureComponent {
return (
<Column label={intl.formatMessage(messages.detailedStatus)} showBackBtn={false}>
<SubNavigation message={intl.formatMessage(messages.title)} />
{/*
Eye icon to show/hide all CWs in a thread.
I'm not convinced of the value of this. It needs a better design at the very least.

View file

@ -1,8 +1,6 @@
import React from 'react';
import ColumnHeader from './column_header';
import PropTypes from 'prop-types';
import ColumnBackButton from '../../../components/column_back_button';
import ColumnBackButtonSlim from '../../../components/column_back_button_slim';
export default class Column extends React.PureComponent {
@ -11,9 +9,7 @@ export default class Column extends React.PureComponent {
icon: PropTypes.string,
children: PropTypes.node,
active: PropTypes.bool,
backBtnSlim: PropTypes.bool,
showBackBtn: PropTypes.bool,
back: PropTypes.string,
};
static defaultProps = {
@ -21,14 +17,12 @@ export default class Column extends React.PureComponent {
}
render() {
const { heading, icon, children, active, backBtnSlim, showBackBtn, back } = this.props;
const { heading, icon, children, active, showBackBtn } = this.props;
const columnHeaderId = heading && heading.replace(/ /g, '-');
const backBtn = backBtnSlim ? (<ColumnBackButtonSlim to={back} />) : (<ColumnBackButton to={back} />);
return (
<div role='region' aria-labelledby={columnHeaderId} className='column'>
{heading && <ColumnHeader icon={icon} active={active} type={heading} columnHeaderId={columnHeaderId} />}
{showBackBtn ? backBtn : null}
{heading && <ColumnHeader icon={icon} active={active} type={heading} columnHeaderId={columnHeaderId} showBackBtn={showBackBtn} />}
{children}
</div>
);

View file

@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Icon from 'soapbox/components/icon';
// import classNames from 'classnames';
// import Icon from 'soapbox/components/icon';
import SubNavigation from 'soapbox/components/sub_navigation';
export default class ColumnHeader extends React.PureComponent {
@ -18,16 +19,21 @@ export default class ColumnHeader extends React.PureComponent {
}
render() {
const { icon, type, active, columnHeaderId } = this.props;
return (
<h1 className={classNames('column-header', { active })} id={columnHeaderId || null}>
<button onClick={this.handleClick}>
{icon && <Icon id={icon} fixedWidth className='column-header__icon' />}
{type}
</button>
</h1>
);
const { type } = this.props;
return <SubNavigation message={type} />;
}
// render() {
// const { icon, type, active, columnHeaderId } = this.props;
//
// return (
// <h1 className={classNames('column-header', { active })} id={columnHeaderId || null}>
// <button onClick={this.handleClick}>
// {icon && <Icon id={icon} fixedWidth className='column-header__icon' />}
// {type}
// </button>
// </h1>
// );
// }
}

View file

@ -41,7 +41,6 @@ import { getAccessToken } from 'soapbox/utils/auth';
import { getFeatures } from 'soapbox/utils/features';
import { fetchCustomEmojis } from 'soapbox/actions/custom_emojis';
import ThumbNavigation from 'soapbox/components/thumb_navigation';
import SubNavigation from 'soapbox/components/sub_navigation';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import {
@ -669,7 +668,6 @@ class UI extends React.PureComponent {
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
<div className={classnames} ref={this.setRef} style={style}>
<TabsBar />
<SubNavigation />
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange} soapbox={soapbox}>
{children}

View file

@ -39,7 +39,7 @@ class StatusPage extends ImmutablePureComponent {
<div className='columns-area__panels__pane columns-area__panels__pane--left'>
<div className='columns-area__panels__pane__inner'>
<Sticky top={106}>
<Sticky top={65}>
<PrimaryNavigation />
</Sticky>
</div>
@ -53,7 +53,7 @@ class StatusPage extends ImmutablePureComponent {
<div className='columns-area__panels__pane columns-area__panels__pane--right'>
<div className='columns-area__panels__pane__inner'>
<Sticky top={106}>
<Sticky top={65}>
{me ? (
<BundleContainer fetchComponent={FeaturesPanel}>
{Component => <Component key='features-panel' />}

View file

@ -42,9 +42,11 @@
width: 100%;
max-width: 600px;
padding: 0 20px;
@media screen and (max-width: 375px) {
padding: 0 10px;
@media screen and (max-width: 580px) {
padding: 0;
}
@media screen and (min-width: 896px) {
margin: 0 20px;
padding: 0;
@ -105,7 +107,9 @@
padding: 0;
}
.autosuggest-textarea__textarea { font-size: 16px; }
.autosuggest-textarea__textarea {
font-size: 16px;
}
.search__input {
line-height: 18px;
@ -116,7 +120,14 @@
background-color: var(--foreground-color);
}
.search__icon .fa { top: 15px; }
.search__icon .fa {
top: 15px;
}
@media (max-width: 580px) {
padding: 0;
}
@media screen and (min-width: 630px) {
.detailed-status {
padding: 15px;

View file

@ -329,7 +329,7 @@
.ui {
display: block;
width: 100%;
padding: 0 0 100px;
padding: 0 0 calc(var(--thumb-navigation-height) + 86px);
.page {
display: flex;