SubNavigation: refactor to be contextual based on Column
This commit is contained in:
parent
db4a0d33c8
commit
60c3243fcb
11 changed files with 127 additions and 160 deletions
|
@ -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>
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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' : ''}`}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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' />}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue