This commit is contained in:
Sean Yesmunt 2020-08-11 16:32:03 -04:00
parent 692862c769
commit a31f14b016
19 changed files with 256 additions and 169 deletions

View file

@ -171,7 +171,6 @@
"react-router-dom": "^5.1.0", "react-router-dom": "^5.1.0",
"react-simplemde-editor": "^4.0.0", "react-simplemde-editor": "^4.0.0",
"react-spring": "^8.0.20", "react-spring": "^8.0.20",
"react-sticky-box": "^0.8.0",
"reakit": "^1.0.0-beta.13", "reakit": "^1.0.0-beta.13",
"redux": "^3.6.0", "redux": "^3.6.0",
"redux-persist": "^5.10.0", "redux-persist": "^5.10.0",

View file

@ -2,7 +2,7 @@
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import { SETTINGS } from 'lbry-redux'; import { SETTINGS } from 'lbry-redux';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import React, { Fragment } from 'react'; import React from 'react';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import classnames from 'classnames'; import classnames from 'classnames';
import Button from 'component/button'; import Button from 'component/button';
@ -206,14 +206,17 @@ const Header = (props: Props) => {
) : ( ) : (
<> <>
<div className="header__navigation"> <div className="header__navigation">
<span style={{ position: 'relative' }}> {!authHeader && (
<Button <span style={{ position: 'relative' }}>
className="header__navigation-item menu__title header__navigation-item--icon" <Button
icon={ICONS.MENU} className="header__navigation-item menu__title header__navigation-item--icon"
onClick={() => setSidebarOpen(!sidebarOpen)} icon={ICONS.MENU}
/> onClick={() => setSidebarOpen(!sidebarOpen)}
{isAbsoluteSideNavHidden && <NotificationBubble />} >
</span> {isAbsoluteSideNavHidden && <NotificationBubble />}
</Button>
</span>
)}
<Button <Button
className="header__navigation-item header__navigation-item--lbry header__navigation-item--button-mobile" className="header__navigation-item header__navigation-item--lbry header__navigation-item--button-mobile"
// @if TARGET='app' // @if TARGET='app'
@ -317,18 +320,7 @@ const Header = (props: Props) => {
</div> </div>
<span className="menu__link-help">{email}</span> <span className="menu__link-help">{email}</span>
</MenuItem> </MenuItem>
) : ( ) : null}
<React.Fragment>
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.AUTH}`)}>
<Icon aria-hidden icon={ICONS.SIGN_UP} />
{__('Register')}
</MenuItem>
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.AUTH_SIGNIN}`)}>
<Icon aria-hidden icon={ICONS.SIGN_IN} />
{__('Sign In')}
</MenuItem>
</React.Fragment>
)}
</MenuList> </MenuList>
</Menu> </Menu>
<Menu> <Menu>
@ -364,22 +356,20 @@ const Header = (props: Props) => {
)} )}
</div> </div>
{!authHeader ? ( {!authHeader && !backout ? (
<div className={classnames('header__menu', { 'header__menu--with-balance': !IS_WEB || authenticated })}> <div className={classnames('header__menu', { 'header__menu--with-balance': !IS_WEB || authenticated })}>
{(!IS_WEB || authenticated) && ( {(!IS_WEB || authenticated) && (
<Fragment> <Button
<Button aria-label={__('Your wallet')}
aria-label={__('Your wallet')} navigate={`/$/${PAGES.WALLET}`}
navigate={`/$/${PAGES.WALLET}`} className="header__navigation-item menu__title header__navigation-item--balance"
className="header__navigation-item menu__title header__navigation-item--balance" label={getWalletTitle()}
label={getWalletTitle()} // @if TARGET='app'
// @if TARGET='app' onDoubleClick={e => {
onDoubleClick={e => { e.stopPropagation();
e.stopPropagation(); }}
}} // @endif
// @endif />
/>
</Fragment>
)} )}
{IS_WEB && !authenticated && ( {IS_WEB && !authenticated && (

View file

@ -1,9 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectUnreadNotificationCount } from 'redux/selectors/notifications'; import { selectUnreadNotificationCount } from 'redux/selectors/notifications';
import NotificationHeaderButton from './view'; import { selectUser } from 'redux/selectors/user';
import NotificationBubble from './view';
const select = state => ({ const select = state => ({
unreadCount: selectUnreadNotificationCount(state), unreadCount: selectUnreadNotificationCount(state),
user: selectUser(state),
}); });
export default connect(select)(NotificationHeaderButton); export default connect(select)(NotificationBubble);

View file

@ -1,19 +1,27 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import classnames from 'classnames';
type Props = { type Props = {
unreadCount: number, unreadCount: number,
inline: boolean,
user: ?User,
}; };
export default function NotificationHeaderButton(props: Props) { export default function NotificationHeaderButton(props: Props) {
const { unreadCount } = props; const { unreadCount, inline = false, user } = props;
const notificationsEnabled = user && user.experimental_ui;
if (unreadCount === 0) { if (unreadCount === 0 || !notificationsEnabled) {
return null; return null;
} }
return ( return (
<span className="notification__bubble"> <span
className={classnames('notification__bubble', {
'notification__bubble--inline': inline,
})}
>
<span className="notification__count">{unreadCount}</span> <span className="notification__count">{unreadCount}</span>
</span> </span>
); );

View file

@ -4,6 +4,7 @@ import * as ICONS from 'constants/icons';
import React from 'react'; import React from 'react';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import Notification from 'component/notification'; import Notification from 'component/notification';
import NotificationBubble from 'component/notificationBubble';
import Button from 'component/button'; import Button from 'component/button';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
// import { Menu, MenuList, MenuButton, MenuPopover, MenuItems, MenuItem } from '@reach/menu-button'; // import { Menu, MenuList, MenuButton, MenuPopover, MenuItems, MenuItem } from '@reach/menu-button';
@ -48,7 +49,7 @@ export default function NotificationHeaderButton(props: Props) {
className="header__navigation-item menu__title header__navigation-item--icon" className="header__navigation-item menu__title header__navigation-item--icon"
> >
<Icon size={18} icon={ICONS.NOTIFICATION} aria-hidden /> <Icon size={18} icon={ICONS.NOTIFICATION} aria-hidden />
{unreadCount > 0 && <span className="notification__bubble">{unreadCount}</span>} <NotificationBubble />
</Button> </Button>
); );

View file

@ -229,15 +229,17 @@ function AppRouter(props: Props) {
<Route path={`/$/${PAGES.INVITE}/:referrer`} exact component={InvitedPage} /> <Route path={`/$/${PAGES.INVITE}/:referrer`} exact component={InvitedPage} />
<Route path={`/$/${PAGES.CHECKOUT}`} exact component={CheckoutPage} /> <Route path={`/$/${PAGES.CHECKOUT}`} exact component={CheckoutPage} />
<PrivateRoute {...props} path={`/$/${PAGES.TAGS_FOLLOWING}`} component={TagsFollowingPage} /> <PrivateRoute {...props} exact path={`/$/${PAGES.TAGS_FOLLOWING}`} component={TagsFollowingPage} />
<PrivateRoute <PrivateRoute
{...props} {...props}
exact
path={`/$/${PAGES.CHANNELS_FOLLOWING}`} path={`/$/${PAGES.CHANNELS_FOLLOWING}`}
component={isAuthenticated || !IS_WEB ? ChannelsFollowingPage : DiscoverPage} component={isAuthenticated || !IS_WEB ? ChannelsFollowingPage : DiscoverPage}
/> />
<PrivateRoute {...props} path={`/$/${PAGES.SETTINGS_NOTIFICATIONS}`} component={SettingsNotificationsPage} /> <PrivateRoute {...props} path={`/$/${PAGES.SETTINGS_NOTIFICATIONS}`} component={SettingsNotificationsPage} />
<PrivateRoute <PrivateRoute
{...props} {...props}
exact
path={`/$/${PAGES.CHANNELS_FOLLOWING_DISCOVER}`} path={`/$/${PAGES.CHANNELS_FOLLOWING_DISCOVER}`}
component={ChannelsFollowingDiscoverPage} component={ChannelsFollowingDiscoverPage}
/> />

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectSubscriptions } from 'redux/selectors/subscriptions'; import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { selectPurchaseUriSuccess, doClearPurchasedUriSuccess, SETTINGS } from 'lbry-redux'; import { selectPurchaseUriSuccess, doClearPurchasedUriSuccess, SETTINGS } from 'lbry-redux';
import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doSignOut } from 'redux/actions/app'; import { doSignOut } from 'redux/actions/app';
import { selectUnreadNotificationCount } from 'redux/selectors/notifications'; import { selectUnreadNotificationCount } from 'redux/selectors/notifications';
@ -14,6 +14,7 @@ const select = state => ({
email: selectUserVerifiedEmail(state), email: selectUserVerifiedEmail(state),
purchaseSuccess: selectPurchaseUriSuccess(state), purchaseSuccess: selectPurchaseUriSuccess(state),
unreadCount: selectUnreadNotificationCount(state), unreadCount: selectUnreadNotificationCount(state),
user: selectUser(state),
}); });
export default connect(select, { export default connect(select, {

View file

@ -4,7 +4,6 @@ import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React from 'react'; import React from 'react';
import Button from 'component/button'; import Button from 'component/button';
import StickyBox from 'react-sticky-box/dist/esnext';
import classnames from 'classnames'; import classnames from 'classnames';
import NotificationBubble from 'component/notificationBubble'; import NotificationBubble from 'component/notificationBubble';
@ -62,6 +61,7 @@ const ABSOLUTE_LINKS: Array<{
label: __('New Channel'), label: __('New Channel'),
navigate: `/$/${PAGES.CHANNEL_NEW}`, navigate: `/$/${PAGES.CHANNEL_NEW}`,
icon: ICONS.CHANNEL, icon: ICONS.CHANNEL,
hideForUnauth: true,
}, },
{ {
label: __('Uploads'), label: __('Uploads'),
@ -78,14 +78,21 @@ const ABSOLUTE_LINKS: Array<{
}, },
{ {
label: __('Creator Analytics'), label: __('Creator Analytics'),
navigate: `/$/${PAGES.CREATOR_ANALYTICS}`, navigate: `/$/${PAGES.CREATOR_DASHBOARD}`,
icon: ICONS.ANALYTICS, icon: ICONS.ANALYTICS,
hideForUnauth: true,
},
{
label: __('Wallet'),
navigate: `/$/${PAGES.WALLET}`,
icon: ICONS.WALLET,
hideForUnauth: true,
}, },
{ {
label: __('Notifications'), label: __('Notifications'),
navigate: `/$/${PAGES.NOTIFICATIONS}`, navigate: `/$/${PAGES.NOTIFICATIONS}`,
icon: ICONS.NOTIFICATION, icon: ICONS.NOTIFICATION,
extra: <NotificationBubble />, extra: <NotificationBubble inline />,
hideForUnauth: true, hideForUnauth: true,
}, },
{ {
@ -96,7 +103,7 @@ const ABSOLUTE_LINKS: Array<{
}, },
{ {
label: __('Invites'), label: __('Invites'),
navigate: `/$/${PAGES.INVITES}`, navigate: `/$/${PAGES.INVITE}`,
icon: ICONS.INVITE, icon: ICONS.INVITE,
hideForUnauth: true, hideForUnauth: true,
}, },
@ -114,6 +121,35 @@ const ABSOLUTE_LINKS: Array<{
}, },
]; ];
const UNAUTH_LINKS: Array<{
label: string,
navigate: string,
icon: string,
extra?: Node,
hideForUnauth?: boolean,
}> = [
{
label: __('Sign In'),
navigate: `/$/${PAGES.AUTH_SIGNIN}`,
icon: ICONS.SIGN_IN,
},
{
label: __('Register'),
navigate: `/$/${PAGES.AUTH}`,
icon: ICONS.SIGN_UP,
},
{
label: __('Settings'),
navigate: `/$/${PAGES.SETTINGS}`,
icon: ICONS.SETTINGS,
},
{
label: __('Help'),
navigate: `/$/${PAGES.HELP}`,
icon: ICONS.HELP,
},
];
type Props = { type Props = {
subscriptions: Array<Subscription>, subscriptions: Array<Subscription>,
email: ?string, email: ?string,
@ -126,6 +162,7 @@ type Props = {
unreadCount: number, unreadCount: number,
purchaseSuccess: boolean, purchaseSuccess: boolean,
doClearPurchasedUriSuccess: () => void, doClearPurchasedUriSuccess: () => void,
user: ?User,
}; };
function SideNavigation(props: Props) { function SideNavigation(props: Props) {
@ -140,11 +177,23 @@ function SideNavigation(props: Props) {
isMediumScreen, isMediumScreen,
isOnFilePage, isOnFilePage,
unreadCount, unreadCount,
user,
} = props; } = props;
const notificationsEnabled = user && user.experimental_ui;
const isAuthenticated = Boolean(email); const isAuthenticated = Boolean(email);
const [pulseLibrary, setPulseLibrary] = React.useState(false); const [pulseLibrary, setPulseLibrary] = React.useState(false);
const isPersonalized = !IS_WEB || isAuthenticated; const isPersonalized = !IS_WEB || isAuthenticated;
const isAbsolute = isOnFilePage || isMediumScreen; const isAbsolute = isOnFilePage || isMediumScreen;
const microNavigation = !sidebarOpen || isMediumScreen;
const subLinks = email
? ABSOLUTE_LINKS.filter(link => {
if (!notificationsEnabled && link.icon === ICONS.NOTIFICATION) {
return false;
}
return true;
})
: UNAUTH_LINKS;
React.useEffect(() => { React.useEffect(() => {
if (purchaseSuccess) { if (purchaseSuccess) {
@ -176,20 +225,16 @@ function SideNavigation(props: Props) {
return () => window.removeEventListener('keydown', handleKeydown); return () => window.removeEventListener('keydown', handleKeydown);
}, [sidebarOpen, setSidebarOpen, isAbsolute]); }, [sidebarOpen, setSidebarOpen, isAbsolute]);
const Wrapper = ({ children }: any) =>
!isOnFilePage && !isMediumScreen ? (
<StickyBox offsetTop={100} offsetBottom={20}>
{children}
</StickyBox>
) : (
children
);
return ( return (
<Wrapper> <div
className={classnames('navigation__wrapper', {
'navigation__wrapper--micro': microNavigation && !isOnFilePage,
'navigation__wrapper--absolute': isAbsolute,
})}
>
{!isOnFilePage && ( {!isOnFilePage && (
<nav className={classnames('navigation', { 'navigation--micro': !sidebarOpen || isMediumScreen })}> <nav className={classnames('navigation', { 'navigation--micro': microNavigation })}>
<ul className={classnames('navigation-links--relative', { 'navigation-links--micro': !sidebarOpen })}> <ul className={classnames('navigation-links', { 'navigation-links--micro': !sidebarOpen })}>
{TOP_LEVEL_LINKS.map(linkProps => {TOP_LEVEL_LINKS.map(linkProps =>
!email && linkProps.hideForUnauth && IS_WEB ? null : ( !email && linkProps.hideForUnauth && IS_WEB ? null : (
<li key={linkProps.navigate}> <li key={linkProps.navigate}>
@ -198,7 +243,7 @@ function SideNavigation(props: Props) {
icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon} icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon}
className={classnames('navigation-link', { className={classnames('navigation-link', {
'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary, 'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary,
'navigation-link--extra': linkProps.icon === ICONS.NOTIFICATION && unreadCount > 0, 'navigation-link--highlighted': linkProps.icon === ICONS.NOTIFICATION && unreadCount > 0,
})} })}
activeClass="navigation-link--active" activeClass="navigation-link--active"
/> />
@ -209,7 +254,7 @@ function SideNavigation(props: Props) {
</ul> </ul>
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && ( {sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
<ul className="navigation__secondary navigation-links--relative navigation-links--small"> <ul className="navigation__secondary navigation-links navigation-links--small">
{subscriptions.map(({ uri, channelName }, index) => ( {subscriptions.map(({ uri, channelName }, index) => (
<li key={uri} className="navigation-link__wrapper"> <li key={uri} className="navigation-link__wrapper">
<Button <Button
@ -229,28 +274,32 @@ function SideNavigation(props: Props) {
<> <>
<nav className={classnames('navigation--filepage')}> <nav className={classnames('navigation--filepage')}>
<ul className="navigation-links--absolute"> <ul className="navigation-links--absolute">
{TOP_LEVEL_LINKS.map(linkProps => ( {TOP_LEVEL_LINKS.map(linkProps =>
<li key={linkProps.navigate}> !email && linkProps.hideForUnauth && IS_WEB ? null : (
<Button <li key={linkProps.navigate}>
{...linkProps} <Button
icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon} {...linkProps}
className={classnames('navigation-link', { icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon}
'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary, className={classnames('navigation-link', {
'navigation-link--extra': linkProps.icon === ICONS.NOTIFICATION && unreadCount > 0, 'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary,
})} 'navigation-link--highlighted': linkProps.icon === ICONS.NOTIFICATION && unreadCount > 0,
activeClass="navigation-link--active" })}
/> activeClass="navigation-link--active"
{linkProps.extra} />
</li> {linkProps.extra}
))} </li>
)
)}
</ul> </ul>
<ul className="navigation-links--absolute"> <ul className="navigation-links--absolute">
{ABSOLUTE_LINKS.map(linkProps => ( {subLinks.map(linkProps =>
<li key={linkProps.navigate}> !email && linkProps.hideForUnauth && IS_WEB ? null : (
<Button {...linkProps} className="navigation-link" activeClass="navigation-link--active" /> <li key={linkProps.navigate} className="mobile-only">
{linkProps.extra} <Button {...linkProps} className="navigation-link" activeClass="navigation-link--active" />
</li> {linkProps.extra}
))} </li>
)
)}
</ul> </ul>
{isPersonalized && subscriptions && subscriptions.length > 0 && ( {isPersonalized && subscriptions && subscriptions.length > 0 && (
<ul className="navigation__secondary navigation-links--small"> <ul className="navigation__secondary navigation-links--small">
@ -270,7 +319,7 @@ function SideNavigation(props: Props) {
<div className="navigation__overlay" onClick={() => setSidebarOpen(false)} /> <div className="navigation__overlay" onClick={() => setSidebarOpen(false)} />
</> </>
)} )}
</Wrapper> </div>
); );
} }

View file

@ -1,10 +1,13 @@
// @flow // @flow
import * as ICONS from 'constants/icons';
import { NOTIFICATION_COMMENT } from 'constants/notifications'; import { NOTIFICATION_COMMENT } from 'constants/notifications';
import React from 'react'; import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import Card from 'component/common/card'; import Card from 'component/common/card';
import Spinner from 'component/spinner'; import Spinner from 'component/spinner';
import Notification from 'component/notification'; import Notification from 'component/notification';
import Yrbl from 'component/yrbl';
import Button from 'component/button';
type Props = { type Props = {
notifications: ?Array<Notification>, notifications: ?Array<Notification>,
@ -91,7 +94,19 @@ export default function NotificationsPage(props: Props) {
} }
/> />
) : ( ) : (
<div>{__('No notifications')}</div> <div className="main--empty">
<Yrbl
title={__('No Notifications')}
subtitle={
<div>
<p>{__("You don't have any notifications yet, but they will be here when you do!")}</p>
<div className="section__actions">
<Button button="primary" icon={ICONS.HOME} label={__('Go Home')} navigate="/" />
</div>
</div>
}
/>
</div>
)} )}
</Page> </Page>
); );

View file

@ -350,7 +350,7 @@
} }
@media (max-width: $breakpoint-medium) and (min-width: $breakpoint-small) { @media (max-width: $breakpoint-medium) and (min-width: $breakpoint-small) {
$width: calc((100vw - var(--side-nav-width) - (var(--spacing-m) * 3)) / 3); $width: calc((100vw - var(--side-nav-width--micro) - (var(--spacing-l) * 3)) / 3);
width: $width; width: $width;
@include handleClaimTileGifThumbnail($width); @include handleClaimTileGifThumbnail($width);
@ -365,7 +365,7 @@
} }
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
$width: calc((100vw / 2) - var(--spacing-s) * 2 - var(--spacing-m) / 2); $width: calc((100vw / 2) - var(--spacing-xs) - var(--spacing-m) / 2);
width: $width; width: $width;
@include handleClaimTileGifThumbnail($width); @include handleClaimTileGifThumbnail($width);
margin-bottom: var(--spacing-l); margin-bottom: var(--spacing-l);

View file

@ -1,11 +1,7 @@
.content__viewer { .content__viewer {
@extend .card; @extend .card;
position: absolute; position: absolute;
top: var(--spacing-l); top: var(--spacing-s);
@media (max-width: $breakpoint-small) {
top: var(--spacing-s);
}
} }
.content__viewer--inline { .content__viewer--inline {

View file

@ -110,7 +110,7 @@
.header__navigation-item--icon { .header__navigation-item--icon {
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
margin-left: 0; margin: 0;
} }
} }

View file

@ -20,6 +20,10 @@
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
> :first-child {
flex-shrink: 0;
}
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
padding: var(--spacing-xs); padding: var(--spacing-xs);
padding-top: var(--spacing-m); padding-top: var(--spacing-m);
@ -43,10 +47,12 @@
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
@media (max-width: $breakpoint-medium) and (min-width: $breakpoint-small) {
margin: 0 var(--spacing-l);
}
@media (max-width: $breakpoint-medium) { @media (max-width: $breakpoint-medium) {
width: 100%; width: 100%;
margin-right: var(--spacing-s);
margin-left: var(--spacing-s);
} }
} }
@ -67,6 +73,11 @@
.file-page__recommended { .file-page__recommended {
width: 25rem; width: 25rem;
height: 0%; height: 0%;
@media (max-width: $breakpoint-medium) {
width: auto;
margin-top: var(--spacing-l);
}
} }
@media (max-width: $breakpoint-medium) { @media (max-width: $breakpoint-medium) {

View file

@ -1,25 +1,41 @@
.navigation { .navigation__wrapper {
width: var(--side-nav-width); width: var(--side-nav-width);
height: calc(100vh - var(--header-height));
}
.navigation__wrapper--micro {
width: var(--side-nav-width--micro);
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
display: none; width: 0;
} }
} }
.navigation--filepage { .navigation__wrapper--absolute {
z-index: 4; &:not(.navigation__wrapper--micro) {
width: 0;
}
}
.navigation {
position: fixed; position: fixed;
top: var(--header-height);
left: 0; left: 0;
top: var(--header-height);
width: var(--side-nav-width);
height: calc(100vh - var(--header-height)); height: calc(100vh - var(--header-height));
overflow-y: auto;
border-right: 1px solid var(--color-border);
padding-top: var(--spacing-l);
padding-bottom: var(--spacing-l);
}
.navigation--filepage {
@extend .navigation;
z-index: 4;
width: var(--side-nav-width--large); width: var(--side-nav-width--large);
overflow-y: scroll; padding-top: 0;
margin-top: 0;
padding: var(--spacing-l);
padding-top: var(--spacing-s);
padding-left: 0;
border-top: 1px solid var(--color-border);
background-color: var(--color-card-background); background-color: var(--color-card-background);
border-top: 1px solid var(--color-border);
box-shadow: var(--card-box-shadow); box-shadow: var(--card-box-shadow);
.navigation-link { .navigation-link {
@ -28,7 +44,12 @@
} }
.navigation--micro { .navigation--micro {
@extend .navigation;
width: var(--side-nav-width--micro); width: var(--side-nav-width--micro);
@media (max-width: $breakpoint-small) {
display: none;
}
} }
.navigation__secondary { .navigation__secondary {
@ -38,18 +59,18 @@
.navigation-link { .navigation-link {
display: block; display: block;
position: relative; position: relative;
transition: color 0.2s;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
color: var(--color-navigation-link); color: var(--color-navigation-link);
margin-bottom: var(--spacing-s);
padding-left: var(--spacing-s); padding-left: var(--spacing-s);
font-size: var(--font-body);
font-weight: var(--font-weight-bold);
.icon { .icon {
height: 1.5rem; height: 1.5rem;
width: 1.5rem; width: 1.5rem;
stroke: var(--color-gray-5); stroke: var(--color-navigation-icon);
} }
.button__content { .button__content {
@ -58,17 +79,9 @@
flex-direction: column; flex-direction: column;
} }
.button__label { &:hover:not(.navigation-link--active),
font-size: var(--font-small); &:focus {
} @extend .navigation-link--highlighted;
&:hover {
@extend .navigation-link--extra;
color: var(--color-gray-5);
.icon {
stroke: var(--color-gray-5);
}
} }
@media (min-width: $breakpoint-medium) { @media (min-width: $breakpoint-medium) {
@ -86,8 +99,6 @@
.button__label { .button__label {
margin-top: 0; margin-top: 0;
font-size: var(--font-body);
font-weight: var(--font-weight-bold);
} }
&:focus { &:focus {
@ -97,14 +108,11 @@
} }
.navigation-link--active { .navigation-link--active {
background-color: var(--color-primary-alt); background-color: var(--color-navigation-active);
border-radius: var(--border-radius); color: var(--color-navigation-active-text);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
color: var(--color-primary);
.icon { .icon {
stroke: var(--color-primary); stroke: var(--color-navigation-active-text);
} }
} }
@ -116,11 +124,13 @@
} }
} }
.navigation-link--extra { .navigation-link--highlighted {
border-radius: var(--border-radius); background-color: var(--color-navigation-hover);
border-top-left-radius: 0; color: var(--color-navigation-hover-text);
border-bottom-left-radius: 0;
background-color: var(--color-gray-1); .icon {
stroke: var(--color-navigation-hover-text);
}
} }
.navigation-links { .navigation-links {
@ -130,15 +140,6 @@
list-style: none; list-style: none;
} }
.navigation-links--relative {
@extend .navigation-links;
margin-right: var(--spacing-m);
.navigation-link {
padding-left: var(--spacing-s);
}
}
.navigation-links--micro { .navigation-links--micro {
.icon { .icon {
height: 1.5rem; height: 1.5rem;
@ -158,7 +159,6 @@
} }
.navigation-link { .navigation-link {
margin-bottom: var(--spacing-xs);
padding-left: 0; padding-left: 0;
} }

View file

@ -68,3 +68,9 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.notification__bubble--inline {
@extend .notification__bubble;
top: 0.75rem;
right: 1rem;
}

View file

@ -313,6 +313,14 @@ textarea {
} }
} }
.mobile-only {
display: none;
@media (max-width: $breakpoint-small) {
display: block;
}
}
.mobile-hidden { .mobile-hidden {
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
display: none !important; display: none !important;

View file

@ -12,8 +12,14 @@
--color-link: var(--color-primary); --color-link: var(--color-primary);
--color-link-hover: #60e1ba; --color-link-hover: #60e1ba;
--color-link-active: #60e1ba; --color-link-active: #60e1ba;
--color-link-icon: #6a7580;
--color-navigation-link: #b3bcc6; --color-navigation-icon: #76808a;
--color-navigation-link: #b9c3ce;
--color-navigation-active: #333a41;
--color-navigation-active-text: #bac5d0;
--color-navigation-hover: #272c32;
--color-navigation-hover-text: #bac5d0;
--color-button-primary-bg: var(--color-primary-alt); --color-button-primary-bg: var(--color-primary-alt);
--color-button-primary-bg-hover: #44796c; --color-button-primary-bg-hover: #44796c;
--color-button-primary-text: var(--color-primary); --color-button-primary-text: var(--color-primary);
@ -24,18 +30,18 @@
--color-button-alt-bg: #4d5660; --color-button-alt-bg: #4d5660;
--color-button-alt-bg-hover: #3e464d; --color-button-alt-bg-hover: #3e464d;
--color-button-alt-text: #e2e9f0; --color-button-alt-text: #e2e9f0;
--color-header-button: var(--color-link-icon); --color-header-button: #434b54;
--color-button-border: var(--color-gray-5); --color-button-border: var(--color-gray-5);
// Color // Color
--color-focus: #2d69a5; --color-focus: #2d69a5;
--color-background-overlay: #212529d7; --color-background-overlay: #212529d7;
--color-background: #212529; --color-background: #1c1f22;
--color-background-overlay: #21252980; --color-background-overlay: #21252980;
--color-border: #4f5b64; --color-border: #343c43;
--color-card-background: #2c3237; --color-card-background: #23292e;
--color-card-background-highlighted: #384046; --color-card-background-highlighted: #384046;
--color-header-background: #434b53; --color-header-background: #2c3137;
--color-tab-text: var(--color-white); --color-tab-text: var(--color-white);
--color-tabs-background: #434b53; --color-tabs-background: #434b53;
--color-tab-divider: var(--color-white); --color-tab-divider: var(--color-white);
@ -64,8 +70,8 @@
--color-input: #f4f4f5; --color-input: #f4f4f5;
--color-input-label: #d4d4d4; --color-input-label: #d4d4d4;
--color-input-placeholder: #f4f4f5; --color-input-placeholder: #f4f4f5;
--color-input-bg: #5d6772; --color-input-bg: #4f5861;
--color-input-bg-copyable: #434b53; --color-input-bg-copyable: #4f5861;
--color-input-border: var(--color-border); --color-input-border: var(--color-border);
--color-input-border-active: var(--color-secondary); --color-input-border-active: var(--color-secondary);
--color-input-toggle: var(--color-primary); --color-input-toggle: var(--color-primary);
@ -74,8 +80,8 @@
// Menu // Menu
--color-menu-background: var(--color-header-background); --color-menu-background: var(--color-header-background);
--color-menu-background--selected: #89939e; --color-menu-background--selected: #5d646c;
--color-menu-background--active: #89939e; --color-menu-background--active: #3a3f44;
--color-menu-icon: #a7a7a7; --color-menu-icon: #a7a7a7;
--color-menu-icon-active: #d6d6d6; --color-menu-icon-active: #d6d6d6;
@ -83,8 +89,8 @@
--color-table-highlight: #3a444e; --color-table-highlight: #3a444e;
// Search // Search
--color-search-suggestion: #212529; --color-search-suggestion: var(--color-text);
--color-search-suggestion-background: #cce6fb; --color-search-suggestion-background: #313d46;
--color-placeholder-background: #4e5862; --color-placeholder-background: #4e5862;
--color-spinner-light: #5a6570; --color-spinner-light: #5a6570;
--color-spinner-dark: #212529; --color-spinner-dark: #212529;

View file

@ -1,8 +1,13 @@
:root { :root {
// Button // Button
--color-link-icon: var(--color-gray-4); --color-navigation-icon: var(--color-gray-5);
--color-link-active: var(--color-primary); --color-link-active: var(--color-primary);
--color-navigation-link: var(--color-gray-5); --color-navigation-link: var(--color-gray-5);
--color-navigation-active: var(--color-primary-alt);
--color-navigation-active-text: var(--color-primary);
--color-navigation-hover: var(--color-gray-1);
--color-navigation-hover-text: #3f3f3f;
--color-header-button: var(--color-button-alt-bg); --color-header-button: var(--color-button-alt-bg);
--color-button-border: var(--color-gray-3); --color-button-border: var(--color-gray-3);

View file

@ -881,7 +881,7 @@
core-js-pure "^3.0.0" core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.1.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4": "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4":
version "7.10.2" version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
@ -9049,14 +9049,6 @@ react-spring@^8.0.20, react-spring@^8.0.27:
"@babel/runtime" "^7.3.1" "@babel/runtime" "^7.3.1"
prop-types "^15.5.8" prop-types "^15.5.8"
react-sticky-box@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/react-sticky-box/-/react-sticky-box-0.8.0.tgz#1c191936af8f5420087b703ec6da4ef46060076c"
dependencies:
"@babel/runtime" "^7.1.5"
prop-types "^15.6.2"
resize-observer-polyfill "^1.5.1"
react@^16.8.2: react@^16.8.2:
version "16.13.0" version "16.13.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.0.tgz#d046eabcdf64e457bbeed1e792e235e1b9934cf7" resolved "https://registry.yarnpkg.com/react/-/react-16.13.0.tgz#d046eabcdf64e457bbeed1e792e235e1b9934cf7"
@ -9514,10 +9506,6 @@ reselect@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
resolve-cwd@^2.0.0: resolve-cwd@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"