Change sidebar on mobile layout to be hidden behind hamburger menu in web UI
This commit is contained in:
parent
8224178e8c
commit
0d055e7de4
7
app/javascript/mastodon/actions/navigation.ts
Normal file
7
app/javascript/mastodon/actions/navigation.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
export const openNavigation = createAction('navigation/open');
|
||||
|
||||
export const closeNavigation = createAction('navigation/close');
|
||||
|
||||
export const toggleNavigation = createAction('navigation/toggle');
|
@ -7,7 +7,7 @@ interface Props {
|
||||
id: string;
|
||||
icon: IconProp;
|
||||
count: number;
|
||||
issueBadge: boolean;
|
||||
issueBadge?: boolean;
|
||||
className: string;
|
||||
}
|
||||
export const IconWithBadge: React.FC<Props> = ({
|
||||
|
@ -31,7 +31,7 @@ import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions';
|
||||
|
||||
import { me, showTrends } from '../../initial_state';
|
||||
import { NavigationBar } from '../compose/components/navigation_bar';
|
||||
import ColumnLink from '../ui/components/column_link';
|
||||
import { ColumnLink } from '../ui/components/column_link';
|
||||
import ColumnSubheading from '../ui/components/column_subheading';
|
||||
|
||||
import TrendsContainer from './containers/trends_container';
|
||||
|
@ -1,49 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useRouteMatch, NavLink } from 'react-router-dom';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, href, method, badge, transparent, optional, ...other }) => {
|
||||
const match = useRouteMatch(to);
|
||||
const className = classNames('column-link', { 'column-link--transparent': transparent, 'column-link--optional': optional });
|
||||
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
|
||||
const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon;
|
||||
const activeIconElement = activeIcon ?? (activeIconComponent ? <Icon id={icon} icon={activeIconComponent} className='column-link__icon' /> : iconElement);
|
||||
const active = match?.isExact;
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a href={href} className={className} data-method={method} {...other}>
|
||||
{active ? activeIconElement : iconElement}
|
||||
<span>{text}</span>
|
||||
{badgeElement}
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<NavLink to={to} className={className} exact {...other}>
|
||||
{active ? activeIconElement : iconElement}
|
||||
<span>{text}</span>
|
||||
{badgeElement}
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ColumnLink.propTypes = {
|
||||
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
|
||||
iconComponent: PropTypes.func,
|
||||
activeIcon: PropTypes.node,
|
||||
activeIconComponent: PropTypes.func,
|
||||
text: PropTypes.string.isRequired,
|
||||
to: PropTypes.string,
|
||||
href: PropTypes.string,
|
||||
method: PropTypes.string,
|
||||
badge: PropTypes.node,
|
||||
transparent: PropTypes.bool,
|
||||
optional: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default ColumnLink;
|
@ -0,0 +1,84 @@
|
||||
import classNames from 'classnames';
|
||||
import { useRouteMatch, NavLink } from 'react-router-dom';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import type { IconProp } from 'mastodon/components/icon';
|
||||
|
||||
export const ColumnLink: React.FC<{
|
||||
icon: React.ReactNode;
|
||||
iconComponent?: IconProp;
|
||||
activeIcon?: React.ReactNode;
|
||||
activeIconComponent?: IconProp;
|
||||
isActive?: (match: unknown, location: { pathname: string }) => boolean;
|
||||
text: string;
|
||||
to?: string;
|
||||
href?: string;
|
||||
method?: string;
|
||||
badge?: React.ReactNode;
|
||||
transparent?: boolean;
|
||||
optional?: boolean;
|
||||
}> = ({
|
||||
icon,
|
||||
activeIcon,
|
||||
iconComponent,
|
||||
activeIconComponent,
|
||||
text,
|
||||
to,
|
||||
href,
|
||||
method,
|
||||
badge,
|
||||
transparent,
|
||||
optional,
|
||||
...other
|
||||
}) => {
|
||||
const match = useRouteMatch(to ?? '');
|
||||
const className = classNames('column-link', {
|
||||
'column-link--transparent': transparent,
|
||||
'column-link--optional': optional,
|
||||
});
|
||||
const badgeElement =
|
||||
typeof badge !== 'undefined' ? (
|
||||
<span className='column-link__badge'>{badge}</span>
|
||||
) : null;
|
||||
const iconElement = iconComponent ? (
|
||||
<Icon
|
||||
id={typeof icon === 'string' ? icon : ''}
|
||||
icon={iconComponent}
|
||||
className='column-link__icon'
|
||||
/>
|
||||
) : (
|
||||
icon
|
||||
);
|
||||
const activeIconElement =
|
||||
activeIcon ??
|
||||
(activeIconComponent ? (
|
||||
<Icon
|
||||
id={typeof icon === 'string' ? icon : ''}
|
||||
icon={activeIconComponent}
|
||||
className='column-link__icon'
|
||||
/>
|
||||
) : (
|
||||
iconElement
|
||||
));
|
||||
const active = match?.isExact;
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a href={href} className={className} data-method={method} {...other}>
|
||||
{active ? activeIconElement : iconElement}
|
||||
<span>{text}</span>
|
||||
{badgeElement}
|
||||
</a>
|
||||
);
|
||||
} else if (to) {
|
||||
return (
|
||||
<NavLink to={to} className={className} exact {...other}>
|
||||
{active ? activeIconElement : iconElement}
|
||||
<span>{text}</span>
|
||||
{badgeElement}
|
||||
</NavLink>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
@ -25,7 +25,7 @@ import BundleColumnError from './bundle_column_error';
|
||||
import { ColumnLoading } from './column_loading';
|
||||
import ComposePanel from './compose_panel';
|
||||
import DrawerLoading from './drawer_loading';
|
||||
import NavigationPanel from './navigation_panel';
|
||||
import { NavigationPanel } from './navigation_panel';
|
||||
|
||||
const componentMap = {
|
||||
'COMPOSE': Compose,
|
||||
@ -132,11 +132,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||
<div className='columns-area columns-area--mobile'>{children}</div>
|
||||
</div>
|
||||
|
||||
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
<NavigationPanel />
|
||||
</div>
|
||||
</div>
|
||||
<NavigationPanel />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,121 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { fetchServer } from 'mastodon/actions/server';
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
|
||||
|
||||
const Account = connect(state => ({
|
||||
account: state.getIn(['accounts', me]),
|
||||
}))(({ account }) => (
|
||||
<Link to={`/@${account.get('acct')}`} title={account.get('acct')}>
|
||||
<Avatar account={account} size={35} />
|
||||
</Link>
|
||||
));
|
||||
|
||||
const messages = defineMessages({
|
||||
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openClosedRegistrationsModal() {
|
||||
dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
|
||||
},
|
||||
dispatchServer() {
|
||||
dispatch(fetchServer());
|
||||
}
|
||||
});
|
||||
|
||||
class Header extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
openClosedRegistrationsModal: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
signupUrl: PropTypes.string.isRequired,
|
||||
dispatchServer: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatchServer } = this.props;
|
||||
dispatchServer();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { signedIn } = this.props.identity;
|
||||
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
|
||||
|
||||
let content;
|
||||
|
||||
if (signedIn) {
|
||||
content = (
|
||||
<>
|
||||
{location.pathname !== '/search' && <Link to='/search' className='button button-secondary' aria-label={intl.formatMessage(messages.search)}><Icon id='search' icon={SearchIcon} /></Link>}
|
||||
{location.pathname !== '/publish' && <Link to='/publish' className='button button-secondary'><FormattedMessage id='compose_form.publish_form' defaultMessage='New post' /></Link>}
|
||||
<Account />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
|
||||
if (sso_redirect) {
|
||||
content = (
|
||||
<a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
|
||||
);
|
||||
} else {
|
||||
let signupButton;
|
||||
|
||||
if (registrationsOpen) {
|
||||
signupButton = (
|
||||
<a href={signupUrl} className='button'>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
signupButton = (
|
||||
<button className='button' onClick={openClosedRegistrationsModal}>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
content = (
|
||||
<>
|
||||
{signupButton}
|
||||
<a href='/auth/sign_in' className='button button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ui__header'>
|
||||
<Link to='/' className='ui__header__logo'>
|
||||
<WordmarkLogo />
|
||||
<SymbolLogo />
|
||||
</Link>
|
||||
|
||||
<div className='ui__header__links'>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header))));
|
148
app/javascript/mastodon/features/ui/components/header.tsx
Normal file
148
app/javascript/mastodon/features/ui/components/header.tsx
Normal file
@ -0,0 +1,148 @@
|
||||
import { useEffect, useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { toggleNavigation } from 'mastodon/actions/navigation';
|
||||
import { fetchServer } from 'mastodon/actions/server';
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
|
||||
import { useIdentity } from 'mastodon/identity_context';
|
||||
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
const Account: React.FC = () => {
|
||||
const account = useAppSelector((state) =>
|
||||
me ? state.accounts.get(me) : undefined,
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
dispatch(toggleNavigation());
|
||||
}, [dispatch]);
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div role='button' tabIndex={0} onClick={handleClick}>
|
||||
<Avatar account={account} size={35} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const messages = defineMessages({
|
||||
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
||||
});
|
||||
|
||||
export const Header: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const { signedIn } = useIdentity();
|
||||
const signupUrl = useAppSelector(
|
||||
(state) =>
|
||||
(state.server.getIn(['server', 'registrations', 'url'], null) as
|
||||
| string
|
||||
| null) ?? '/auth/sign_up',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchServer());
|
||||
}, [dispatch]);
|
||||
|
||||
const openClosedRegistrationsModal = useCallback(() => {
|
||||
dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS', modalProps: {} }));
|
||||
}, [dispatch]);
|
||||
|
||||
let content;
|
||||
|
||||
if (signedIn) {
|
||||
content = (
|
||||
<>
|
||||
{location.pathname !== '/search' && (
|
||||
<Link
|
||||
to='/search'
|
||||
className='button button-secondary'
|
||||
aria-label={intl.formatMessage(messages.search)}
|
||||
>
|
||||
<Icon id='search' icon={SearchIcon} />
|
||||
</Link>
|
||||
)}
|
||||
{location.pathname !== '/publish' && (
|
||||
<Link to='/publish' className='button button-secondary'>
|
||||
<FormattedMessage
|
||||
id='compose_form.publish_form'
|
||||
defaultMessage='New post'
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
<Account />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
if (sso_redirect) {
|
||||
content = (
|
||||
<a
|
||||
href={sso_redirect}
|
||||
data-method='post'
|
||||
className='button button--block button-tertiary'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='sign_in_banner.sso_redirect'
|
||||
defaultMessage='Login or Register'
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
let signupButton;
|
||||
|
||||
if (registrationsOpen) {
|
||||
signupButton = (
|
||||
<a href={signupUrl} className='button'>
|
||||
<FormattedMessage
|
||||
id='sign_in_banner.create_account'
|
||||
defaultMessage='Create account'
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
signupButton = (
|
||||
<button className='button' onClick={openClosedRegistrationsModal}>
|
||||
<FormattedMessage
|
||||
id='sign_in_banner.create_account'
|
||||
defaultMessage='Create account'
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
content = (
|
||||
<>
|
||||
{signupButton}
|
||||
<a href='/auth/sign_in' className='button button-tertiary'>
|
||||
<FormattedMessage
|
||||
id='sign_in_banner.sign_in'
|
||||
defaultMessage='Login'
|
||||
/>
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ui__header'>
|
||||
<Link to='/' className='ui__header__logo'>
|
||||
<WordmarkLogo />
|
||||
<SymbolLogo />
|
||||
</Link>
|
||||
|
||||
<div className='ui__header__links'>{content}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -7,7 +7,7 @@ import ListAltActiveIcon from '@/material-icons/400-24px/list_alt-fill.svg?react
|
||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||
import { fetchLists } from 'mastodon/actions/lists';
|
||||
|
||||
import ColumnLink from './column_link';
|
||||
import { ColumnLink } from './column_link';
|
||||
|
||||
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
||||
if (!lists) {
|
||||
|
@ -1,206 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Component, useEffect } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, useIntl } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react';
|
||||
import ExploreActiveIcon from '@/material-icons/400-24px/explore-fill.svg?react';
|
||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||
import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react';
|
||||
import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home.svg?react';
|
||||
import ListAltActiveIcon from '@/material-icons/400-24px/list_alt-fill.svg?react';
|
||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||
import AdministrationIcon from '@/material-icons/400-24px/manufacturing.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
|
||||
import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
|
||||
import PersonAddActiveIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||
import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
||||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||
import { WordmarkLogo } from 'mastodon/components/logo';
|
||||
import { NavigationPortal } from 'mastodon/components/navigation_portal';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
|
||||
import { transientSingleColumn } from 'mastodon/is_mobile';
|
||||
import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions';
|
||||
import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications';
|
||||
|
||||
import ColumnLink from './column_link';
|
||||
import DisabledAccountBanner from './disabled_account_banner';
|
||||
import { ListPanel } from './list_panel';
|
||||
import SignInBanner from './sign_in_banner';
|
||||
|
||||
const messages = defineMessages({
|
||||
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||
explore: { id: 'explore.title', defaultMessage: 'Explore' },
|
||||
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
||||
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||
administration: { id: 'navigation_bar.administration', defaultMessage: 'Administration' },
|
||||
moderation: { id: 'navigation_bar.moderation', defaultMessage: 'Moderation' },
|
||||
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
|
||||
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
|
||||
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
||||
advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' },
|
||||
openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' },
|
||||
followRequests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||
});
|
||||
|
||||
const NotificationsLink = () => {
|
||||
|
||||
const count = useSelector(selectUnreadNotificationGroupsCount);
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<ColumnLink
|
||||
key='notifications'
|
||||
transparent
|
||||
to='/notifications'
|
||||
icon={<IconWithBadge id='bell' icon={NotificationsIcon} count={count} className='column-link__icon' />}
|
||||
activeIcon={<IconWithBadge id='bell' icon={NotificationsActiveIcon} count={count} className='column-link__icon' />}
|
||||
text={intl.formatMessage(messages.notifications)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const FollowRequestsLink = () => {
|
||||
const count = useSelector(state => state.getIn(['user_lists', 'follow_requests', 'items'])?.size ?? 0);
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchFollowRequests());
|
||||
}, [dispatch]);
|
||||
|
||||
if (count === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/follow_requests'
|
||||
icon={<IconWithBadge id='user-plus' icon={PersonAddIcon} count={count} className='column-link__icon' />}
|
||||
activeIcon={<IconWithBadge id='user-plus' icon={PersonAddActiveIcon} count={count} className='column-link__icon' />}
|
||||
text={intl.formatMessage(messages.followRequests)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
class NavigationPanel extends Component {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
isFirehoseActive = (match, location) => {
|
||||
return match || location.pathname.startsWith('/public');
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
const { signedIn, disabledAccountId, permissions } = this.props.identity;
|
||||
|
||||
let banner = undefined;
|
||||
|
||||
if (transientSingleColumn) {
|
||||
banner = (
|
||||
<div className='switch-to-advanced'>
|
||||
{intl.formatMessage(messages.openedInClassicInterface)}
|
||||
{" "}
|
||||
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
|
||||
{intl.formatMessage(messages.advancedInterface)}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='navigation-panel'>
|
||||
<div className='navigation-panel__logo'>
|
||||
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
|
||||
</div>
|
||||
|
||||
{banner &&
|
||||
<div className='navigation-panel__banner'>
|
||||
{banner}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className='navigation-panel__menu'>
|
||||
{signedIn && (
|
||||
<>
|
||||
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} />
|
||||
<NotificationsLink />
|
||||
<FollowRequestsLink />
|
||||
</>
|
||||
)}
|
||||
|
||||
{trendsEnabled ? (
|
||||
<ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} />
|
||||
) : (
|
||||
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
|
||||
)}
|
||||
|
||||
{(signedIn || timelinePreview) && (
|
||||
<ColumnLink transparent to='/public/local' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
|
||||
)}
|
||||
|
||||
{!signedIn && (
|
||||
<div className='navigation-panel__sign-in-banner'>
|
||||
<hr />
|
||||
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> }
|
||||
</div>
|
||||
)}
|
||||
|
||||
{signedIn && (
|
||||
<>
|
||||
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
|
||||
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
|
||||
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />
|
||||
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
|
||||
|
||||
<ListPanel />
|
||||
|
||||
<hr />
|
||||
|
||||
<ColumnLink transparent href='/settings/preferences' icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />
|
||||
|
||||
{canManageReports(permissions) && <ColumnLink optional transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
|
||||
{canViewAdminDashboard(permissions) && <ColumnLink optional transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className='navigation-panel__legal'>
|
||||
<hr />
|
||||
<ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex-spacer' />
|
||||
|
||||
<NavigationPortal />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(withIdentity(NavigationPanel));
|
@ -0,0 +1,415 @@
|
||||
import { useEffect, useCallback, useState } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import { animated, useSpring } from '@react-spring/web';
|
||||
import { useDrag } from '@use-gesture/react';
|
||||
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react';
|
||||
import ExploreActiveIcon from '@/material-icons/400-24px/explore-fill.svg?react';
|
||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||
import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react';
|
||||
import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home.svg?react';
|
||||
import ListAltActiveIcon from '@/material-icons/400-24px/list_alt-fill.svg?react';
|
||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||
import AdministrationIcon from '@/material-icons/400-24px/manufacturing.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
|
||||
import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
|
||||
import PersonAddActiveIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||
import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
||||
import { openNavigation, closeNavigation } from 'mastodon/actions/navigation';
|
||||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||
import { WordmarkLogo } from 'mastodon/components/logo';
|
||||
import { NavigationPortal } from 'mastodon/components/navigation_portal';
|
||||
import { useIdentity } from 'mastodon/identity_context';
|
||||
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
|
||||
import { transientSingleColumn } from 'mastodon/is_mobile';
|
||||
import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions';
|
||||
import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import { ColumnLink } from './column_link';
|
||||
import DisabledAccountBanner from './disabled_account_banner';
|
||||
import { ListPanel } from './list_panel';
|
||||
import SignInBanner from './sign_in_banner';
|
||||
|
||||
const messages = defineMessages({
|
||||
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||
notifications: {
|
||||
id: 'tabs_bar.notifications',
|
||||
defaultMessage: 'Notifications',
|
||||
},
|
||||
explore: { id: 'explore.title', defaultMessage: 'Explore' },
|
||||
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
||||
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||
preferences: {
|
||||
id: 'navigation_bar.preferences',
|
||||
defaultMessage: 'Preferences',
|
||||
},
|
||||
administration: {
|
||||
id: 'navigation_bar.administration',
|
||||
defaultMessage: 'Administration',
|
||||
},
|
||||
moderation: { id: 'navigation_bar.moderation', defaultMessage: 'Moderation' },
|
||||
followsAndFollowers: {
|
||||
id: 'navigation_bar.follows_and_followers',
|
||||
defaultMessage: 'Follows and followers',
|
||||
},
|
||||
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
|
||||
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
||||
advancedInterface: {
|
||||
id: 'navigation_bar.advanced_interface',
|
||||
defaultMessage: 'Open in advanced web interface',
|
||||
},
|
||||
openedInClassicInterface: {
|
||||
id: 'navigation_bar.opened_in_classic_interface',
|
||||
defaultMessage:
|
||||
'Posts, accounts, and other specific pages are opened by default in the classic web interface.',
|
||||
},
|
||||
followRequests: {
|
||||
id: 'navigation_bar.follow_requests',
|
||||
defaultMessage: 'Follow requests',
|
||||
},
|
||||
});
|
||||
|
||||
const NotificationsLink = () => {
|
||||
const count = useAppSelector(selectUnreadNotificationGroupsCount);
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<ColumnLink
|
||||
key='notifications'
|
||||
transparent
|
||||
to='/notifications'
|
||||
icon={
|
||||
<IconWithBadge
|
||||
id='bell'
|
||||
icon={NotificationsIcon}
|
||||
count={count}
|
||||
className='column-link__icon'
|
||||
/>
|
||||
}
|
||||
activeIcon={
|
||||
<IconWithBadge
|
||||
id='bell'
|
||||
icon={NotificationsActiveIcon}
|
||||
count={count}
|
||||
className='column-link__icon'
|
||||
/>
|
||||
}
|
||||
text={intl.formatMessage(messages.notifications)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const FollowRequestsLink: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const count = useAppSelector(
|
||||
(state) =>
|
||||
(
|
||||
state.user_lists.getIn(['follow_requests', 'items']) as
|
||||
| ImmutableMap<string, unknown>
|
||||
| undefined
|
||||
)?.size ?? 0,
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchFollowRequests());
|
||||
}, [dispatch]);
|
||||
|
||||
if (count === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/follow_requests'
|
||||
icon={
|
||||
<IconWithBadge
|
||||
id='user-plus'
|
||||
icon={PersonAddIcon}
|
||||
count={count}
|
||||
className='column-link__icon'
|
||||
/>
|
||||
}
|
||||
activeIcon={
|
||||
<IconWithBadge
|
||||
id='user-plus'
|
||||
icon={PersonAddActiveIcon}
|
||||
count={count}
|
||||
className='column-link__icon'
|
||||
/>
|
||||
}
|
||||
text={intl.formatMessage(messages.followRequests)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationPanel: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { signedIn, disabledAccountId, permissions } = useIdentity();
|
||||
const open = useAppSelector((state) => state.navigation.open);
|
||||
const dispatch = useAppDispatch();
|
||||
const [openable, setOpenable] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Corresponds to $no-gap-breakpoint - 285px - 1px
|
||||
const mediaWatcher = window.matchMedia('(max-width: 889px)');
|
||||
|
||||
setOpenable(mediaWatcher.matches);
|
||||
|
||||
const handleChange = (e: MediaQueryListEvent) => {
|
||||
setOpenable(e.matches);
|
||||
};
|
||||
|
||||
mediaWatcher.addEventListener('change', handleChange);
|
||||
|
||||
return () => {
|
||||
mediaWatcher.removeEventListener('change', handleChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [{ x }, spring] = useSpring(
|
||||
() => ({
|
||||
x: open ? 0 : 284,
|
||||
onRest: {
|
||||
x({ value }: { value: number }) {
|
||||
if (value === 0) {
|
||||
dispatch(openNavigation());
|
||||
} else {
|
||||
dispatch(closeNavigation());
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
[open],
|
||||
);
|
||||
|
||||
const bind = useDrag(
|
||||
({ last, offset: [ox], velocity: [vx], direction: [dx], cancel }) => {
|
||||
if (ox < -70) {
|
||||
cancel();
|
||||
}
|
||||
|
||||
if (last) {
|
||||
if (ox > 284 / 2 || (vx > 0.5 && dx > 0)) {
|
||||
void spring.start({ x: 284 });
|
||||
} else {
|
||||
void spring.start({ x: 0 });
|
||||
}
|
||||
} else {
|
||||
void spring.start({ x: ox, immediate: true });
|
||||
}
|
||||
},
|
||||
{
|
||||
from: () => [x.get(), 0],
|
||||
filterTaps: true,
|
||||
bounds: { left: 0 },
|
||||
rubberband: true,
|
||||
},
|
||||
);
|
||||
|
||||
const isFirehoseActive = useCallback(
|
||||
(match: unknown, location: { pathname: string }): boolean => {
|
||||
return !!match || location.pathname.startsWith('/public');
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
let banner = undefined;
|
||||
|
||||
if (transientSingleColumn) {
|
||||
banner = (
|
||||
<div className='switch-to-advanced'>
|
||||
{intl.formatMessage(messages.openedInClassicInterface)}{' '}
|
||||
<a
|
||||
href={`/deck${location.pathname}`}
|
||||
className='switch-to-advanced__toggle'
|
||||
>
|
||||
{intl.formatMessage(messages.advancedInterface)}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'
|
||||
{...bind()}
|
||||
style={openable ? { x } : undefined}
|
||||
>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
<div className='navigation-panel'>
|
||||
<div className='navigation-panel__logo'>
|
||||
<Link to='/' className='column-link column-link--logo'>
|
||||
<WordmarkLogo />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{banner && <div className='navigation-panel__banner'>{banner}</div>}
|
||||
|
||||
<div className='navigation-panel__menu'>
|
||||
{signedIn && (
|
||||
<>
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/home'
|
||||
icon='home'
|
||||
iconComponent={HomeIcon}
|
||||
activeIconComponent={HomeActiveIcon}
|
||||
text={intl.formatMessage(messages.home)}
|
||||
/>
|
||||
<NotificationsLink />
|
||||
<FollowRequestsLink />
|
||||
</>
|
||||
)}
|
||||
|
||||
{trendsEnabled ? (
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/explore'
|
||||
icon='explore'
|
||||
iconComponent={ExploreIcon}
|
||||
activeIconComponent={ExploreActiveIcon}
|
||||
text={intl.formatMessage(messages.explore)}
|
||||
/>
|
||||
) : (
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/search'
|
||||
icon='search'
|
||||
iconComponent={SearchIcon}
|
||||
text={intl.formatMessage(messages.search)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{(signedIn || timelinePreview) && (
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/public/local'
|
||||
isActive={isFirehoseActive}
|
||||
icon='globe'
|
||||
iconComponent={PublicIcon}
|
||||
text={intl.formatMessage(messages.firehose)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!signedIn && (
|
||||
<div className='navigation-panel__sign-in-banner'>
|
||||
<hr />
|
||||
{disabledAccountId ? (
|
||||
<DisabledAccountBanner />
|
||||
) : (
|
||||
<SignInBanner />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{signedIn && (
|
||||
<>
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/conversations'
|
||||
icon='at'
|
||||
iconComponent={AlternateEmailIcon}
|
||||
text={intl.formatMessage(messages.direct)}
|
||||
/>
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/bookmarks'
|
||||
icon='bookmarks'
|
||||
iconComponent={BookmarksIcon}
|
||||
activeIconComponent={BookmarksActiveIcon}
|
||||
text={intl.formatMessage(messages.bookmarks)}
|
||||
/>
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/favourites'
|
||||
icon='star'
|
||||
iconComponent={StarIcon}
|
||||
activeIconComponent={StarActiveIcon}
|
||||
text={intl.formatMessage(messages.favourites)}
|
||||
/>
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/lists'
|
||||
icon='list-ul'
|
||||
iconComponent={ListAltIcon}
|
||||
activeIconComponent={ListAltActiveIcon}
|
||||
text={intl.formatMessage(messages.lists)}
|
||||
/>
|
||||
|
||||
<ListPanel />
|
||||
|
||||
<hr />
|
||||
|
||||
<ColumnLink
|
||||
transparent
|
||||
href='/settings/preferences'
|
||||
icon='cog'
|
||||
iconComponent={SettingsIcon}
|
||||
text={intl.formatMessage(messages.preferences)}
|
||||
/>
|
||||
|
||||
{canManageReports(permissions) && (
|
||||
<ColumnLink
|
||||
optional
|
||||
transparent
|
||||
href='/admin/reports'
|
||||
icon='flag'
|
||||
iconComponent={ModerationIcon}
|
||||
text={intl.formatMessage(messages.moderation)}
|
||||
/>
|
||||
)}
|
||||
{canViewAdminDashboard(permissions) && (
|
||||
<ColumnLink
|
||||
optional
|
||||
transparent
|
||||
href='/admin/dashboard'
|
||||
icon='tachometer'
|
||||
iconComponent={AdministrationIcon}
|
||||
text={intl.formatMessage(messages.administration)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className='navigation-panel__legal'>
|
||||
<hr />
|
||||
<ColumnLink
|
||||
transparent
|
||||
to='/about'
|
||||
icon='ellipsis-h'
|
||||
iconComponent={MoreHorizIcon}
|
||||
text={intl.formatMessage(messages.about)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex-spacer' />
|
||||
|
||||
<NavigationPortal />
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
);
|
||||
};
|
@ -29,7 +29,7 @@ import { expandHomeTimeline } from '../../actions/timelines';
|
||||
import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding, disableHoverCards } from '../../initial_state';
|
||||
|
||||
import BundleColumnError from './components/bundle_column_error';
|
||||
import Header from './components/header';
|
||||
import { Header } from './components/header';
|
||||
import { UploadArea } from './components/upload_area';
|
||||
import { HashtagMenuController } from './components/hashtag_menu_controller';
|
||||
import ColumnsAreaContainer from './containers/columns_area_container';
|
||||
|
@ -21,6 +21,7 @@ import { markersReducer } from './markers';
|
||||
import media_attachments from './media_attachments';
|
||||
import meta from './meta';
|
||||
import { modalReducer } from './modal';
|
||||
import { navigationReducer } from './navigation';
|
||||
import { notificationGroupsReducer } from './notification_groups';
|
||||
import { notificationPolicyReducer } from './notification_policy';
|
||||
import { notificationRequestsReducer } from './notification_requests';
|
||||
@ -76,6 +77,7 @@ const reducers = {
|
||||
history,
|
||||
notificationPolicy: notificationPolicyReducer,
|
||||
notificationRequests: notificationRequestsReducer,
|
||||
navigation: navigationReducer,
|
||||
};
|
||||
|
||||
// We want the root state to be an ImmutableRecord, which is an object with a defined list of keys,
|
||||
|
28
app/javascript/mastodon/reducers/navigation.ts
Normal file
28
app/javascript/mastodon/reducers/navigation.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
|
||||
import {
|
||||
openNavigation,
|
||||
closeNavigation,
|
||||
toggleNavigation,
|
||||
} from 'mastodon/actions/navigation';
|
||||
|
||||
interface State {
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
open: false,
|
||||
};
|
||||
|
||||
export const navigationReducer = createReducer(initialState, (builder) => {
|
||||
builder
|
||||
.addCase(openNavigation, (state) => {
|
||||
state.open = true;
|
||||
})
|
||||
.addCase(closeNavigation, (state) => {
|
||||
state.open = false;
|
||||
})
|
||||
.addCase(toggleNavigation, (state) => {
|
||||
state.open = !state.open;
|
||||
});
|
||||
});
|
@ -3203,26 +3203,15 @@ $ui-header-logo-wordmark-width: 99px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint - 285px - 1px) {
|
||||
$sidebar-width: 55px;
|
||||
|
||||
.columns-area__panels__main {
|
||||
width: calc(100% - $sidebar-width);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.columns-area__panels__pane--navigational {
|
||||
min-width: $sidebar-width;
|
||||
|
||||
.columns-area__panels__pane__inner {
|
||||
width: $sidebar-width;
|
||||
}
|
||||
|
||||
.column-link span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list-panel {
|
||||
display: none;
|
||||
}
|
||||
position: fixed;
|
||||
inset-inline-end: 0;
|
||||
background: var(--background-color);
|
||||
box-shadow: var(--dropdown-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user