kopia lustrzana https://github.com/wagtail/wagtail
Co-authored-by: Thibaud Colas <thibaudcolas@gmail.com> - Removing the peeking attribute so the sidebar only opens when intentionally set to expanded mode by using expand or search or account functionalities - Adding tooltips on link item hovers - Expanding of slim sidebar when search is clicked and when account options are clickedpull/8239/head
rodzic
2fb2629ba3
commit
af4c4d0653
|
|
@ -148,6 +148,7 @@ These are classes that provide overrides.
|
|||
// VENDOR: overrides of vendor styles.
|
||||
@import 'overrides/vendor.datetimepicker';
|
||||
@import 'overrides/vendor.tagit';
|
||||
@import 'overrides/vendor.tippy';
|
||||
|
||||
// UTILITIES: classes that do one simple thing.
|
||||
@import 'overrides/utilities.hidden';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
@import '../../../node_modules/tippy.js/dist/tippy';
|
||||
|
||||
.tippy-box {
|
||||
@apply w-bg-primary w-text-white w-text-14;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='top'] > .tippy-arrow::before {
|
||||
@apply w-border-t-primary;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='bottom'] > .tippy-arrow::before {
|
||||
@apply w-border-b-primary;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='left'] > .tippy-arrow::before {
|
||||
@apply w-border-l-primary;
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^='right'] > .tippy-arrow::before {
|
||||
@apply w-border-r-primary;
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ $font-wagtail-icons: wagtail;
|
|||
// misc sizing
|
||||
$thumbnail-width: 130px;
|
||||
$menu-width: 200px;
|
||||
$menu-width-slim: 60px;
|
||||
$menu-width-slim: 65px;
|
||||
|
||||
$menu-width-max: 320px;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
}
|
||||
|
||||
.c-page-explorer__item__link {
|
||||
@apply w-inline-flex w-items-start sm:w-items-center w-flex-wrap w-grow w-cursor-pointer w-gap-1;
|
||||
@apply w-inline-flex w-items-start sm:w-items-center w-flex-wrap w-grow w-cursor-pointer w-gap-1 w-transition;
|
||||
padding: 1.45em 1em;
|
||||
|
||||
&:focus,
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
.c-page-explorer__item__action {
|
||||
@apply w-text-white/85;
|
||||
@apply w-text-white/85 w-transition;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
position: static;
|
||||
inset-inline-end: $sidebar-toggle-spacing;
|
||||
|
||||
// Remove once we drop support for Safari 13.
|
||||
|
|
@ -79,23 +80,13 @@
|
|||
|
||||
&__collapse-toggle {
|
||||
@include sidebar-toggle;
|
||||
display: grid;
|
||||
// All other styling is done with utility classes on this element
|
||||
}
|
||||
|
||||
// When in mobile mode, hide the collapse-toggle and show the nav-toggle (which is defined in the .sidebar-nav-toggle class below)
|
||||
&--mobile &__collapse-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// This element should cover all the area beneath the collapse toggle
|
||||
// It's only used to attach mouse enter/exit event handlers to control peeking
|
||||
&__peek-hover-area {
|
||||
margin-top: $sidebar-toggle-size;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a separate component as it needs to display in the header
|
||||
|
|
@ -118,5 +109,4 @@
|
|||
@import 'menu/MenuItem';
|
||||
@import 'menu/SubMenuItem';
|
||||
@import 'modules/MainMenu';
|
||||
@import 'modules/Search';
|
||||
@import 'modules/WagtailBranding';
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ export interface ModuleRenderContext {
|
|||
key: number;
|
||||
slim: boolean;
|
||||
expandingOrCollapsing: boolean;
|
||||
onAccountExpand: () => void;
|
||||
onSearchClick: () => void;
|
||||
currentPath: string;
|
||||
strings: Strings;
|
||||
navigate(url: string): Promise<void>;
|
||||
|
|
@ -46,7 +48,6 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
// 'collapsed' is a persistent state that is controlled by the arrow icon at the top
|
||||
// It records the user's general preference for a collapsed/uncollapsed menu
|
||||
// This is just a hint though, and we may still collapse the menu if the screen is too small
|
||||
// Also, we may display the full menu temporarily in collapsed mode (see 'peeking' below)
|
||||
const [collapsed, setCollapsed] = React.useState(collapsedOnLoad);
|
||||
|
||||
// Call onExpandCollapse(true) if menu is initialised in collapsed state
|
||||
|
|
@ -56,11 +57,6 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
}
|
||||
}, []);
|
||||
|
||||
// 'peeking' is a temporary state to allow the user to peek in the menu while it is collapsed, or hidden.
|
||||
// When peeking is true, the menu renders as if it's not collapsed, but as an overlay instead of occupying
|
||||
// space next to the content
|
||||
const [peeking, setPeeking] = React.useState(false);
|
||||
|
||||
// 'visibleOnMobile' indicates whether the sidebar is currently visible on mobile
|
||||
// On mobile, the sidebar is completely hidden by default and must be opened manually
|
||||
const [visibleOnMobile, setVisibleOnMobile] = React.useState(false);
|
||||
|
|
@ -80,6 +76,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
setVisibleOnMobile(false);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
handleResize();
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
|
|
@ -88,7 +85,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
// Whether or not to display the menu with slim layout.
|
||||
// Separate from 'collapsed' as the menu can still be displayed with an expanded
|
||||
// layout while in 'collapsed' mode if the user is 'peeking' into it (see above)
|
||||
const slim = collapsed && !peeking && !isMobile;
|
||||
const slim = collapsed && !isMobile;
|
||||
|
||||
// 'expandingOrCollapsing' is set to true whilst the the menu is transitioning between slim and expanded layouts
|
||||
const [expandingOrCollapsing, setExpandingOrCollapsing] =
|
||||
|
|
@ -124,40 +121,33 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
};
|
||||
};
|
||||
|
||||
// Switch peeking on/off when the mouse cursor hovers the sidebar or focus is on the sidebar
|
||||
const [mouseHover, setMouseHover] = React.useState(false);
|
||||
const [focused, setFocused] = React.useState(false);
|
||||
|
||||
const onMouseEnterHandler = () => {
|
||||
setMouseHover(true);
|
||||
};
|
||||
|
||||
const onMouseLeaveHandler = () => {
|
||||
setMouseHover(false);
|
||||
const onBlurHandler = () => {
|
||||
if (focused) {
|
||||
setFocused(false);
|
||||
setCollapsed(true);
|
||||
}
|
||||
};
|
||||
|
||||
const onFocusHandler = () => {
|
||||
setFocused(true);
|
||||
};
|
||||
|
||||
const onBlurHandler = () => {
|
||||
setFocused(false);
|
||||
};
|
||||
|
||||
// We need a stop peeking timeout to stop the sidebar moving as someone tab's though the menu
|
||||
const stopPeekingTimeout = React.useRef<any>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (mouseHover || focused) {
|
||||
clearTimeout(stopPeekingTimeout.current);
|
||||
setPeeking(true);
|
||||
} else {
|
||||
clearTimeout(stopPeekingTimeout.current);
|
||||
stopPeekingTimeout.current = setTimeout(() => {
|
||||
setPeeking(false);
|
||||
}, SIDEBAR_TRANSITION_DURATION);
|
||||
if (focused) {
|
||||
setCollapsed(false);
|
||||
setFocused(true);
|
||||
}
|
||||
}, [mouseHover, focused]);
|
||||
};
|
||||
|
||||
const onSearchClick = () => {
|
||||
if (slim) {
|
||||
onClickCollapseToggle();
|
||||
}
|
||||
};
|
||||
|
||||
const onAccountExpand = () => {
|
||||
if (slim) {
|
||||
onClickCollapseToggle();
|
||||
}
|
||||
};
|
||||
|
||||
// Render modules
|
||||
const renderedModules = modules.map((module, index) =>
|
||||
|
|
@ -165,6 +155,8 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
key: index,
|
||||
slim,
|
||||
expandingOrCollapsing,
|
||||
onAccountExpand,
|
||||
onSearchClick,
|
||||
currentPath,
|
||||
strings,
|
||||
navigate,
|
||||
|
|
@ -181,31 +173,43 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
|
|||
(isMobile && !visibleOnMobile ? ' sidebar--hidden' : '')
|
||||
}
|
||||
>
|
||||
<div className="sidebar__inner">
|
||||
<button
|
||||
onClick={onClickCollapseToggle}
|
||||
aria-label={strings.TOGGLE_SIDEBAR}
|
||||
aria-expanded={slim ? 'false' : 'true'}
|
||||
type="button"
|
||||
className="button sidebar__collapse-toggle hover:w-bg-primary-200 hover:text-white hover:opacity-100"
|
||||
<div
|
||||
className="sidebar__inner"
|
||||
onFocus={onFocusHandler}
|
||||
onBlur={onBlurHandler}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
slim ? 'w-justify-center' : 'w-justify-end'
|
||||
} w-flex w-items-center`}
|
||||
>
|
||||
<Icon
|
||||
name="expand-right"
|
||||
className={`w-transition motion-reduce:w-transition-none
|
||||
<button
|
||||
onClick={onClickCollapseToggle}
|
||||
aria-label={strings.TOGGLE_SIDEBAR}
|
||||
aria-expanded={slim ? 'false' : 'true'}
|
||||
type="button"
|
||||
className={`
|
||||
${!slim ? 'w-mr-4' : ''}
|
||||
button
|
||||
sidebar__collapse-toggle
|
||||
w-flex
|
||||
w-justify-center
|
||||
w-items-center
|
||||
sm:w-mt-4
|
||||
hover:w-bg-primary-200
|
||||
hover:text-white
|
||||
hover:opacity-100`}
|
||||
>
|
||||
<Icon
|
||||
name="expand-right"
|
||||
className={`w-transition motion-reduce:w-transition-none
|
||||
${!collapsed ? '-w-rotate-180' : ''}
|
||||
`}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="sidebar__peek-hover-area"
|
||||
onMouseEnter={onMouseEnterHandler}
|
||||
onMouseLeave={onMouseLeaveHandler}
|
||||
onFocus={onFocusHandler}
|
||||
onBlur={onBlurHandler}
|
||||
>
|
||||
{renderedModules}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{renderedModules}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
.sidebar-panel {
|
||||
@apply w-transition w-duration-150;
|
||||
// With CSS variable allows panels with different widths to animate properly
|
||||
--width: #{$menu-width};
|
||||
|
||||
visibility: hidden;
|
||||
transform: translate3d(0, 0, 0);
|
||||
transform: translateX(-100%);
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
|
|
@ -17,20 +18,9 @@
|
|||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
@include transition(
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left $menu-transition-duration ease,
|
||||
inset-inline-start $menu-transition-duration ease
|
||||
);
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
z-index: var(--z-index);
|
||||
width: var(--width);
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: calc(#{$menu-width} - var(--width));
|
||||
inset-inline-start: calc(#{$menu-width} - var(--width));
|
||||
}
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
|
|
@ -43,12 +33,18 @@
|
|||
box-shadow: 2px 0 2px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
// Showing the submenu options panel in mobile mode
|
||||
.sidebar--mobile .sidebar-sub-menu-item--open &,
|
||||
.sidebar--mobile .sidebar-page-explorer-item.sidebar-menu-item--active & {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
@at-root .sidebar--slim #{&} {
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: calc(#{$menu-width-slim} - var(--width));
|
||||
inset-inline-start: calc(#{$menu-width-slim} - var(--width));
|
||||
left: $menu-width-slim;
|
||||
inset-inline-start: $menu-width-slim;
|
||||
}
|
||||
// Don't apply this to nested submenus though
|
||||
@at-root .sidebar--slim .sidebar-panel #{&} {
|
||||
|
|
@ -63,13 +59,15 @@
|
|||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: $menu-width;
|
||||
inset-inline-start: $menu-width;
|
||||
transform: translateX(0);
|
||||
|
||||
// Don't apply this to nested submenus though
|
||||
@at-root .sidebar--slim .sidebar-panel #{&} {
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: $menu-width;
|
||||
inset-inline-start: $menu-width;
|
||||
left: $menu-width-slim;
|
||||
inset-inline-start: $menu-width-slim;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,27 +7,36 @@ exports[`Sidebar should render with the minimum required props 1`] = `
|
|||
>
|
||||
<div
|
||||
className="sidebar__inner"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-expanded="true"
|
||||
className="button sidebar__collapse-toggle hover:w-bg-primary-200 hover:text-white hover:opacity-100"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<div
|
||||
className="w-justify-end w-flex w-items-center"
|
||||
>
|
||||
<Icon
|
||||
className="w-transition motion-reduce:w-transition-none
|
||||
<button
|
||||
aria-expanded="true"
|
||||
className="
|
||||
w-mr-4
|
||||
button
|
||||
sidebar__collapse-toggle
|
||||
w-flex
|
||||
w-justify-center
|
||||
w-items-center
|
||||
sm:w-mt-4
|
||||
hover:w-bg-primary-200
|
||||
hover:text-white
|
||||
hover:opacity-100"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<Icon
|
||||
className="w-transition motion-reduce:w-transition-none
|
||||
-w-rotate-180
|
||||
"
|
||||
name="expand-right"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="sidebar__peek-hover-area"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
/>
|
||||
name="expand-right"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import * as React from 'react';
|
|||
|
||||
import Icon from '../../Icon/Icon';
|
||||
import { MenuItemDefinition, MenuItemProps } from './MenuItem';
|
||||
import Tippy from '@tippyjs/react';
|
||||
|
||||
export const LinkMenuItem: React.FunctionComponent<
|
||||
MenuItemProps<LinkMenuItemDefinition>
|
||||
> = ({ item, path, state, dispatch, navigate }) => {
|
||||
> = ({ item, slim, path, state, dispatch, navigate }) => {
|
||||
const isCurrent = state.activePath === path;
|
||||
const isActive = state.activePath.startsWith(path);
|
||||
const isInSubMenu = path.split('.').length > 2;
|
||||
|
|
@ -40,17 +41,23 @@ export const LinkMenuItem: React.FunctionComponent<
|
|||
|
||||
return (
|
||||
<li className={className}>
|
||||
<a
|
||||
href={item.url}
|
||||
aria-current={isCurrent ? 'page' : undefined}
|
||||
onClick={onClick}
|
||||
className={`sidebar-menu-item__link ${item.classNames}`}
|
||||
<Tippy
|
||||
disabled={!slim || isInSubMenu}
|
||||
content={item.label}
|
||||
placement="right"
|
||||
>
|
||||
{item.iconName && (
|
||||
<Icon name={item.iconName} className="icon--menuitem" />
|
||||
)}
|
||||
<span className="menuitem-label">{item.label}</span>
|
||||
</a>
|
||||
<a
|
||||
href={item.url}
|
||||
aria-current={isCurrent ? 'page' : undefined}
|
||||
onClick={onClick}
|
||||
className={`sidebar-menu-item__link ${item.classNames}`}
|
||||
>
|
||||
{item.iconName && (
|
||||
<Icon name={item.iconName} className="icon--menuitem" />
|
||||
)}
|
||||
<span className="menuitem-label">{item.label}</span>
|
||||
</a>
|
||||
</Tippy>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
|
@ -76,12 +83,13 @@ export class LinkMenuItemDefinition implements MenuItemDefinition {
|
|||
this.classNames = classnames;
|
||||
}
|
||||
|
||||
render({ path, state, dispatch, navigate }) {
|
||||
render({ path, slim, state, dispatch, navigate }) {
|
||||
return (
|
||||
<LinkMenuItem
|
||||
key={this.name}
|
||||
item={this}
|
||||
path={path}
|
||||
slim={slim}
|
||||
state={state}
|
||||
dispatch={dispatch}
|
||||
navigate={navigate}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
position: relative;
|
||||
|
||||
&__link {
|
||||
@apply w-text-14;
|
||||
@include transition(
|
||||
border-color $menu-transition-duration ease,
|
||||
background-color $menu-transition-duration ease
|
||||
|
|
@ -15,14 +16,12 @@
|
|||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
border-inline-start: 3px solid transparent;
|
||||
|
||||
-webkit-font-smoothing: auto;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
text-align: start;
|
||||
color: $color-menu-text;
|
||||
padding: 11px 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
|
||||
// Note, font-weights lower than normal,
|
||||
|
|
@ -88,7 +87,15 @@
|
|||
}
|
||||
|
||||
.sidebar--slim {
|
||||
.menuitem-label {
|
||||
opacity: 0;
|
||||
.sidebar-menu-item {
|
||||
.menuitem-label {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu-item--in-sub-menu {
|
||||
.menuitem-label {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ export interface MenuItemDefinition {
|
|||
|
||||
export interface MenuItemProps<T> {
|
||||
path: string;
|
||||
slim: boolean;
|
||||
state: MenuState;
|
||||
item: T;
|
||||
dispatch(action: MenuAction): void;
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ import {
|
|||
} from '../../PageExplorer/actions';
|
||||
import { SidebarPanel } from '../SidebarPanel';
|
||||
import { SIDEBAR_TRANSITION_DURATION } from '../Sidebar';
|
||||
import Tippy from '@tippyjs/react';
|
||||
|
||||
export const PageExplorerMenuItem: React.FunctionComponent<
|
||||
MenuItemProps<PageExplorerMenuItemDefinition>
|
||||
> = ({ path, item, state, dispatch, navigate }) => {
|
||||
> = ({ path, slim, item, state, dispatch, navigate }) => {
|
||||
const isOpen = state.navigationPath.startsWith(path);
|
||||
const isActive = isOpen || state.activePath.startsWith(path);
|
||||
const depth = path.split('.').length;
|
||||
|
|
@ -72,17 +73,19 @@ export const PageExplorerMenuItem: React.FunctionComponent<
|
|||
|
||||
return (
|
||||
<li className={className}>
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="sidebar-menu-item__link"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={isOpen ? 'true' : 'false'}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="folder-open-inverse" className="icon--menuitem" />
|
||||
<span className="menuitem-label">{item.label}</span>
|
||||
<Icon className={sidebarTriggerIconClassName} name="arrow-right" />
|
||||
</button>
|
||||
<Tippy disabled={isOpen || !slim} content={item.label} placement="right">
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="sidebar-menu-item__link"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={isOpen ? 'true' : 'false'}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="folder-open-inverse" className="icon--menuitem" />
|
||||
<span className="menuitem-label">{item.label}</span>
|
||||
<Icon className={sidebarTriggerIconClassName} name="arrow-right" />
|
||||
</button>
|
||||
</Tippy>
|
||||
<div>
|
||||
<SidebarPanel
|
||||
isVisible={isVisible}
|
||||
|
|
@ -112,12 +115,13 @@ export class PageExplorerMenuItemDefinition extends LinkMenuItemDefinition {
|
|||
this.startPageId = startPageId;
|
||||
}
|
||||
|
||||
render({ path, state, dispatch, navigate }) {
|
||||
render({ path, slim, state, dispatch, navigate }) {
|
||||
return (
|
||||
<PageExplorerMenuItem
|
||||
key={this.name}
|
||||
item={this}
|
||||
path={path}
|
||||
slim={slim}
|
||||
state={state}
|
||||
dispatch={dispatch}
|
||||
navigate={navigate}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
.sidebar-sub-menu-trigger-icon {
|
||||
$root: &;
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
|
@ -27,6 +28,10 @@
|
|||
height: 16px;
|
||||
transform: translate3d(13px, 0, 0);
|
||||
}
|
||||
|
||||
.sidebar--slim &--open {
|
||||
transform: translate3d(13px, 0, 0) rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-sub-menu-panel {
|
||||
|
|
@ -40,7 +45,7 @@
|
|||
|
||||
> h2 {
|
||||
// w-min-h-[160px] and w-mt-[35px] classes are to vertically align the title and icon combination to the search input on the left
|
||||
@apply w-min-h-[160px] w-mt-[35px] w-text-white w-mb-0 w-inline-flex w-flex-col w-justify-center w-items-center;
|
||||
@apply w-min-h-[160px] w-mt-[45px] w-px-4 w-box-border w-text-center w-text-white w-mb-0 w-inline-flex w-flex-col w-justify-center w-items-center;
|
||||
|
||||
&:before {
|
||||
font-size: 4em;
|
||||
|
|
@ -50,6 +55,10 @@
|
|||
width: 100%;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
@at-root .sidebar--slim & {
|
||||
@apply w-mt-3;
|
||||
}
|
||||
}
|
||||
|
||||
ul > li {
|
||||
|
|
@ -81,9 +90,6 @@
|
|||
box-shadow: 2px 0 2px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
@at-root .sidebar--slim #{&} {
|
||||
transform: translate3d($menu-width-slim - $menu-width, 0, 0);
|
||||
}
|
||||
// Don't apply this to nested submenus though
|
||||
@at-root .sidebar--slim .sidebar-sub-menu-panel #{&} {
|
||||
transform: translate3d(0, 0, 0);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { renderMenu } from '../modules/MainMenu';
|
|||
import { SidebarPanel } from '../SidebarPanel';
|
||||
import { SIDEBAR_TRANSITION_DURATION } from '../Sidebar';
|
||||
import { MenuItemDefinition, MenuItemProps } from './MenuItem';
|
||||
import Tippy from '@tippyjs/react';
|
||||
|
||||
interface SubMenuItemProps extends MenuItemProps<SubMenuItemDefinition> {
|
||||
slim: boolean;
|
||||
|
|
@ -65,19 +66,21 @@ export const SubMenuItem: React.FunctionComponent<SubMenuItemProps> = ({
|
|||
|
||||
return (
|
||||
<li className={className}>
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`sidebar-menu-item__link ${item.classNames}`}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={isOpen ? 'true' : 'false'}
|
||||
type="button"
|
||||
>
|
||||
{item.iconName && (
|
||||
<Icon name={item.iconName} className="icon--menuitem" />
|
||||
)}
|
||||
<span className="menuitem-label">{item.label}</span>
|
||||
<Icon className={sidebarTriggerIconClassName} name="arrow-right" />
|
||||
</button>
|
||||
<Tippy disabled={isOpen || !slim} content={item.label} placement="right">
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`sidebar-menu-item__link ${item.classNames}`}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={isOpen ? 'true' : 'false'}
|
||||
type="button"
|
||||
>
|
||||
{item.iconName && (
|
||||
<Icon name={item.iconName} className="icon--menuitem" />
|
||||
)}
|
||||
<span className="menuitem-label">{item.label}</span>
|
||||
<Icon className={sidebarTriggerIconClassName} name="arrow-right" />
|
||||
</button>
|
||||
</Tippy>
|
||||
<SidebarPanel isVisible={isVisible} isOpen={isOpen} depth={depth}>
|
||||
<div className="sidebar-sub-menu-panel">
|
||||
<h2
|
||||
|
|
|
|||
|
|
@ -4,25 +4,30 @@ exports[`PageExplorerMenuItem should render with the minimum required props 1`]
|
|||
<li
|
||||
className="sidebar-menu-item sidebar-page-explorer-item"
|
||||
>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
className="sidebar-menu-item__link"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<ForwardRef(TippyWrapper)
|
||||
disabled={true}
|
||||
placement="right"
|
||||
>
|
||||
<Icon
|
||||
className="icon--menuitem"
|
||||
name="folder-open-inverse"
|
||||
/>
|
||||
<span
|
||||
className="menuitem-label"
|
||||
/>
|
||||
<Icon
|
||||
className="sidebar-sub-menu-trigger-icon"
|
||||
name="arrow-right"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
className="sidebar-menu-item__link"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<Icon
|
||||
className="icon--menuitem"
|
||||
name="folder-open-inverse"
|
||||
/>
|
||||
<span
|
||||
className="menuitem-label"
|
||||
/>
|
||||
<Icon
|
||||
className="sidebar-sub-menu-trigger-icon"
|
||||
name="arrow-right"
|
||||
/>
|
||||
</button>
|
||||
</ForwardRef(TippyWrapper)>
|
||||
<div>
|
||||
<SidebarPanel
|
||||
depth={2}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,26 @@ exports[`SubMenuItem should render with the minimum required props 1`] = `
|
|||
<li
|
||||
className="sidebar-menu-item sidebar-sub-menu-item sidebar-menu-item--active"
|
||||
>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
className="sidebar-menu-item__link "
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<ForwardRef(TippyWrapper)
|
||||
disabled={true}
|
||||
placement="right"
|
||||
>
|
||||
<span
|
||||
className="menuitem-label"
|
||||
/>
|
||||
<Icon
|
||||
className="sidebar-sub-menu-trigger-icon"
|
||||
name="arrow-right"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
className="sidebar-menu-item__link "
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="menuitem-label"
|
||||
/>
|
||||
<Icon
|
||||
className="sidebar-sub-menu-trigger-icon"
|
||||
name="arrow-right"
|
||||
/>
|
||||
</button>
|
||||
</ForwardRef(TippyWrapper)>
|
||||
<SidebarPanel
|
||||
depth={2}
|
||||
isOpen={false}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,32 @@
|
|||
.sidebar-main-menu {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
margin-bottom: 60px;
|
||||
// So the last items in the menu will be seen when the menu is vertically scrollable
|
||||
margin-bottom: 52px;
|
||||
// Scrollbar styling for firefox
|
||||
scrollbar-color: theme('colors.grey.200');
|
||||
scrollbar-width: thin;
|
||||
|
||||
@include transition(margin-bottom $menu-transition-duration ease);
|
||||
|
||||
//Custom scrollbar styling for windows/mac and slim mode
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
@apply w-hidden;
|
||||
// Hide the scrollbar arrows on windows
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply w-bg-grey-200 w-rounded-sm;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply w-bg-transparent;
|
||||
}
|
||||
|
||||
&--open-footer {
|
||||
margin-bottom: 127px;
|
||||
}
|
||||
|
|
@ -84,6 +107,8 @@
|
|||
}
|
||||
|
||||
&__account {
|
||||
@include show-focus-outline-inside();
|
||||
|
||||
&-toggle {
|
||||
@apply w-pl-2 w-inline-flex w-justify-between w-w-full w-translate-x-0 w-transition w-duration-150;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Menu } from './MainMenu';
|
|||
describe('Menu', () => {
|
||||
const strings = {};
|
||||
const user = { avatarUrl: 'https://gravatar/profile' };
|
||||
const onAccountExpand = jest.fn();
|
||||
|
||||
it('should render with the minimum required props', () => {
|
||||
const wrapper = shallow(
|
||||
|
|
@ -13,6 +14,7 @@ describe('Menu', () => {
|
|||
menuItems={[]}
|
||||
strings={strings}
|
||||
user={user}
|
||||
onAccountExpand={onAccountExpand}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
|
@ -26,6 +28,7 @@ describe('Menu', () => {
|
|||
menuItems={[]}
|
||||
strings={strings}
|
||||
user={user}
|
||||
onAccountExpand={onAccountExpand}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { LinkMenuItemDefinition } from '../menu/LinkMenuItem';
|
|||
import { MenuItemDefinition } from '../menu/MenuItem';
|
||||
import { SubMenuItemDefinition } from '../menu/SubMenuItem';
|
||||
import { ModuleDefinition, Strings } from '../Sidebar';
|
||||
import Tippy from '@tippyjs/react';
|
||||
|
||||
export function renderMenu(
|
||||
path: string,
|
||||
|
|
@ -64,6 +65,7 @@ interface MenuProps {
|
|||
user: MainMenuModuleDefinition['user'];
|
||||
slim: boolean;
|
||||
expandingOrCollapsing: boolean;
|
||||
onAccountExpand: () => void;
|
||||
currentPath: string;
|
||||
strings: Strings;
|
||||
|
||||
|
|
@ -75,6 +77,7 @@ export const Menu: React.FunctionComponent<MenuProps> = ({
|
|||
accountMenuItems,
|
||||
user,
|
||||
expandingOrCollapsing,
|
||||
onAccountExpand,
|
||||
slim,
|
||||
currentPath,
|
||||
strings,
|
||||
|
|
@ -90,6 +93,7 @@ export const Menu: React.FunctionComponent<MenuProps> = ({
|
|||
activePath: '',
|
||||
});
|
||||
const accountSettingsOpen = state.navigationPath.startsWith('.account');
|
||||
const isVisible = !slim || expandingOrCollapsing;
|
||||
|
||||
// Whenever currentPath or menu changes, work out new activePath
|
||||
React.useEffect(() => {
|
||||
|
|
@ -161,17 +165,30 @@ export const Menu: React.FunctionComponent<MenuProps> = ({
|
|||
};
|
||||
}, []);
|
||||
|
||||
// Determine if the sidebar is expanded from account button click
|
||||
const [expandedFromAccountClick, setExpandedFromAccountClick] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
// Whenever the parent Sidebar component collapses or expands, close any open menus
|
||||
React.useEffect(() => {
|
||||
if (expandingOrCollapsing) {
|
||||
if (expandingOrCollapsing && !expandedFromAccountClick) {
|
||||
dispatch({
|
||||
type: 'set-navigation-path',
|
||||
path: '',
|
||||
});
|
||||
}
|
||||
if (expandedFromAccountClick) {
|
||||
setExpandedFromAccountClick(false);
|
||||
}
|
||||
}, [expandingOrCollapsing]);
|
||||
|
||||
const onClickAccountSettings = () => {
|
||||
// Pass account expand information to Sidebar component
|
||||
onAccountExpand();
|
||||
if (slim) {
|
||||
setExpandedFromAccountClick(true);
|
||||
}
|
||||
|
||||
if (accountSettingsOpen) {
|
||||
dispatch({
|
||||
type: 'set-navigation-path',
|
||||
|
|
@ -199,47 +216,53 @@ export const Menu: React.FunctionComponent<MenuProps> = ({
|
|||
<div
|
||||
className={
|
||||
'sidebar-footer' +
|
||||
(accountSettingsOpen ? ' sidebar-footer--open' : '')
|
||||
(accountSettingsOpen ? ' sidebar-footer--open' : '') +
|
||||
(isVisible ? ' sidebar-footer--visible' : '')
|
||||
}
|
||||
>
|
||||
<button
|
||||
className="
|
||||
sidebar-footer__account
|
||||
w-bg-primary
|
||||
w-text-white
|
||||
w-flex
|
||||
w-items-center
|
||||
w-relative
|
||||
w-p-0
|
||||
w-w-full
|
||||
w-appearance-none
|
||||
w-border-0
|
||||
w-overflow-hidden
|
||||
w-px-5
|
||||
w-py-3
|
||||
hover:w-bg-primary-200
|
||||
focus:w-bg-primary-200
|
||||
w-transition"
|
||||
title={strings.EDIT_YOUR_ACCOUNT}
|
||||
onClick={onClickAccountSettings}
|
||||
aria-label={strings.EDIT_YOUR_ACCOUNT}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={accountSettingsOpen ? 'true' : 'false'}
|
||||
type="button"
|
||||
<Tippy
|
||||
disabled={!slim}
|
||||
content={strings.EDIT_YOUR_ACCOUNT}
|
||||
placement="right"
|
||||
>
|
||||
<div className="avatar avatar-on-dark w-flex-shrink-0 !w-w-[28px] !w-h-[28px]">
|
||||
<img src={user.avatarUrl} alt="" />
|
||||
</div>
|
||||
<div className="sidebar-footer__account-toggle">
|
||||
<div className="sidebar-footer__account-label w-label-3">
|
||||
{user.name}
|
||||
<button
|
||||
className="
|
||||
sidebar-footer__account
|
||||
w-bg-primary
|
||||
w-text-white
|
||||
w-flex
|
||||
w-items-center
|
||||
w-relative
|
||||
w-w-full
|
||||
w-appearance-none
|
||||
w-border-0
|
||||
w-overflow-hidden
|
||||
w-px-5
|
||||
w-py-3
|
||||
hover:w-bg-primary-200
|
||||
focus:w-bg-primary-200
|
||||
w-transition"
|
||||
title={strings.EDIT_YOUR_ACCOUNT}
|
||||
onClick={onClickAccountSettings}
|
||||
aria-label={strings.EDIT_YOUR_ACCOUNT}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={accountSettingsOpen ? 'true' : 'false'}
|
||||
type="button"
|
||||
>
|
||||
<div className="avatar avatar-on-dark w-flex-shrink-0 !w-w-[28px] !w-h-[28px]">
|
||||
<img src={user.avatarUrl} alt="" />
|
||||
</div>
|
||||
<Icon
|
||||
className="w-w-4 w-h-4 w-text-white"
|
||||
name={accountSettingsOpen ? 'arrow-down' : 'arrow-up'}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<div className="sidebar-footer__account-toggle">
|
||||
<div className="sidebar-footer__account-label w-label-3">
|
||||
{user.name}
|
||||
</div>
|
||||
<Icon
|
||||
className="w-w-4 w-h-4 w-text-white"
|
||||
name={accountSettingsOpen ? 'arrow-down' : 'arrow-up'}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</Tippy>
|
||||
|
||||
<ul>
|
||||
{renderMenu('', accountMenuItems, slim, state, dispatch, navigate)}
|
||||
|
|
@ -267,7 +290,15 @@ export class MainMenuModuleDefinition implements ModuleDefinition {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
render({ slim, expandingOrCollapsing, key, currentPath, strings, navigate }) {
|
||||
render({
|
||||
slim,
|
||||
expandingOrCollapsing,
|
||||
onAccountExpand,
|
||||
key,
|
||||
currentPath,
|
||||
strings,
|
||||
navigate,
|
||||
}) {
|
||||
return (
|
||||
<Menu
|
||||
menuItems={this.menuItems}
|
||||
|
|
@ -275,6 +306,7 @@ export class MainMenuModuleDefinition implements ModuleDefinition {
|
|||
user={this.user}
|
||||
slim={slim}
|
||||
expandingOrCollapsing={expandingOrCollapsing}
|
||||
onAccountExpand={onAccountExpand}
|
||||
key={key}
|
||||
currentPath={currentPath}
|
||||
strings={strings}
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
// stylelint-disable declaration-no-important
|
||||
.sidebar-search {
|
||||
@apply w-relative w-box-border w-flex w-items-center w-flex-row w-h-[42px] w-px-5;
|
||||
$root: &;
|
||||
|
||||
.sidebar--slim & {
|
||||
@apply w-justify-center w-p-0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
@include visuallyhidden;
|
||||
}
|
||||
|
||||
// Beat specificity
|
||||
input:not([type='submit']) {
|
||||
@apply w-pl-[45px];
|
||||
@include show-focus-outline-inside();
|
||||
position: absolute;
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: 0;
|
||||
inset-inline-start: 0;
|
||||
top: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
color: $color-menu-text;
|
||||
-webkit-font-smoothing: auto;
|
||||
|
||||
.sidebar--slim & {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $color-menu-text;
|
||||
}
|
||||
}
|
||||
|
||||
&__submit {
|
||||
@include show-focus-outline-inside();
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
color: #ccc;
|
||||
padding: 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
transition: opacity $menu-transition-duration ease,
|
||||
width $menu-transition-duration ease;
|
||||
|
||||
svg {
|
||||
margin-inline-end: 20px;
|
||||
transition: margin-inline-end $menu-transition-duration ease;
|
||||
}
|
||||
|
||||
.sidebar--slim & {
|
||||
svg {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import Icon from '../../Icon/Icon';
|
||||
import { ModuleDefinition, Strings } from '../Sidebar';
|
||||
import {
|
||||
ModuleDefinition,
|
||||
Strings,
|
||||
SIDEBAR_TRANSITION_DURATION,
|
||||
} from '../Sidebar';
|
||||
|
||||
import Tippy from '@tippyjs/react';
|
||||
|
||||
interface SearchInputProps {
|
||||
slim: boolean;
|
||||
expandingOrCollapsing: boolean;
|
||||
onSearchClick: () => void;
|
||||
searchUrl: string;
|
||||
strings: Strings;
|
||||
|
||||
|
|
@ -15,11 +22,13 @@ interface SearchInputProps {
|
|||
export const SearchInput: React.FunctionComponent<SearchInputProps> = ({
|
||||
slim,
|
||||
expandingOrCollapsing,
|
||||
onSearchClick,
|
||||
searchUrl,
|
||||
strings,
|
||||
navigate,
|
||||
}) => {
|
||||
const isVisible = !slim || expandingOrCollapsing;
|
||||
const searchInput = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const onSubmitForm = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
if (e.target instanceof HTMLFormElement) {
|
||||
|
|
@ -36,36 +45,84 @@ export const SearchInput: React.FunctionComponent<SearchInputProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const className =
|
||||
'sidebar-search' +
|
||||
(slim ? ' sidebar-search--slim' : '') +
|
||||
(isVisible ? ' sidebar-search--visible' : '');
|
||||
|
||||
return (
|
||||
<form
|
||||
role="search"
|
||||
className={className}
|
||||
className={`w-h-[42px] w-relative w-box-border w-flex w-items-center w-justify-start w-flex-row w-flex-shrink-0`}
|
||||
action={searchUrl}
|
||||
method="get"
|
||||
onSubmit={onSubmitForm}
|
||||
>
|
||||
<button
|
||||
className="button sidebar-search__submit"
|
||||
type="submit"
|
||||
aria-label={strings.SEARCH}
|
||||
>
|
||||
<Icon className="icon--menuitem" name="search" />
|
||||
</button>
|
||||
<label className="sidebar-search__label" htmlFor="menu-search-q">
|
||||
{strings.SEARCH}
|
||||
</label>
|
||||
<input
|
||||
className="sidebar-search__input"
|
||||
type="text"
|
||||
id="menu-search-q"
|
||||
name="q"
|
||||
placeholder={strings.SEARCH}
|
||||
/>
|
||||
<div className="w-flex w-flex-row w-items-center w-h-full">
|
||||
<Tippy
|
||||
disabled={isVisible || !slim}
|
||||
content={strings.SEARCH}
|
||||
placement="right"
|
||||
>
|
||||
{/* Use padding left 23px to align icon in slim mode and padding right 18px to ensure focus is full width */}
|
||||
<button
|
||||
className={`
|
||||
${slim ? 'w-pr-[18px]' : 'w-pr-0'}
|
||||
w-w-full
|
||||
w-pl-[23px]
|
||||
w-h-[35px]
|
||||
w-bg-transparent
|
||||
w-outline-offset-inside
|
||||
w-border-0
|
||||
w-rounded-none
|
||||
w-text-white/80
|
||||
w-z-10
|
||||
hover:w-text-white
|
||||
focus:w-text-white
|
||||
hover:w-bg-transparent`}
|
||||
type="submit"
|
||||
aria-label={strings.SEARCH}
|
||||
onClick={(e) => {
|
||||
if (slim) {
|
||||
e.preventDefault();
|
||||
onSearchClick();
|
||||
|
||||
// Focus search input after transition when button is clicked in slim mode
|
||||
setTimeout(() => {
|
||||
if (searchInput.current) {
|
||||
searchInput.current.focus();
|
||||
}
|
||||
}, SIDEBAR_TRANSITION_DURATION);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon className="icon--menuitem" name="search" />
|
||||
</button>
|
||||
</Tippy>
|
||||
|
||||
<label className="w-sr-only" htmlFor="menu-search-q">
|
||||
{strings.SEARCH}
|
||||
</label>
|
||||
|
||||
{/* Classes marked important to trump the base input styling set in _forms.scss */}
|
||||
<input
|
||||
className={`
|
||||
${slim || !isVisible ? 'w-hidden' : ''}
|
||||
!w-pl-[45px]
|
||||
!w-subpixel-antialiased
|
||||
!w-absolute
|
||||
!w-left-0
|
||||
!w-font-normal
|
||||
!w-top-0
|
||||
!w-text-14
|
||||
!w-bg-transparent
|
||||
!w-border-0
|
||||
!w-rounded-none
|
||||
!w-text-white/80
|
||||
!w-outline-offset-inside
|
||||
placeholder:!w-text-white/80`}
|
||||
type="text"
|
||||
id="menu-search-q"
|
||||
name="q"
|
||||
placeholder={strings.SEARCH}
|
||||
ref={searchInput}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
|
@ -77,13 +134,21 @@ export class SearchModuleDefinition implements ModuleDefinition {
|
|||
this.searchUrl = searchUrl;
|
||||
}
|
||||
|
||||
render({ slim, key, expandingOrCollapsing, strings, navigate }) {
|
||||
render({
|
||||
slim,
|
||||
key,
|
||||
expandingOrCollapsing,
|
||||
onSearchClick,
|
||||
strings,
|
||||
navigate,
|
||||
}) {
|
||||
return (
|
||||
<SearchInput
|
||||
searchUrl={this.searchUrl}
|
||||
slim={slim}
|
||||
key={key}
|
||||
expandingOrCollapsing={expandingOrCollapsing}
|
||||
onSearchClick={onSearchClick}
|
||||
strings={strings}
|
||||
navigate={navigate}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
align-items: center;
|
||||
color: #aaa;
|
||||
-webkit-font-smoothing: auto;
|
||||
margin: 1.8em auto 2.5em;
|
||||
margin: 4em auto 1.8em;
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
|
|
@ -28,10 +28,15 @@
|
|||
box-sizing: border-box;
|
||||
border-radius: 100%;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin: 1.8em auto 2.5em;
|
||||
}
|
||||
|
||||
// Reduce overall size when in slim mode
|
||||
.sidebar--slim & {
|
||||
@include show-focus-outline-inside();
|
||||
width: 60px;
|
||||
transform: none;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
// Remove background on 404 page
|
||||
|
|
@ -63,30 +68,13 @@
|
|||
|
||||
// Bird wrapper
|
||||
&__icon-wrapper {
|
||||
@apply w-bg-white/15 w-overflow-hidden hover:w-overflow-visible;
|
||||
@apply w-bg-white/15 w-relative w-overflow-hidden hover:w-overflow-visible;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: 0;
|
||||
inset-inline-start: 0;
|
||||
top: 0;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
transition: left $menu-transition-duration ease,
|
||||
inset-inline-start $menu-transition-duration ease,
|
||||
top $menu-transition-duration ease, width $menu-transition-duration ease,
|
||||
height $menu-transition-duration ease;
|
||||
|
||||
.sidebar--slim & {
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: 10px;
|
||||
inset-inline-start: 10px;
|
||||
top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
|
@ -97,28 +85,6 @@
|
|||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
// Bird icons
|
||||
&__icon {
|
||||
.sidebar--slim & {
|
||||
width: 42px;
|
||||
height: 51px;
|
||||
top: 10px;
|
||||
// Remove once we drop support for Safari 13.
|
||||
// stylelint-disable-next-line property-disallowed-list
|
||||
left: -9px;
|
||||
inset-inline-start: -9px;
|
||||
}
|
||||
|
||||
// TODO: Fix legacy specificity issues
|
||||
&[data-part='eye--open'] {
|
||||
display: inline !important;
|
||||
}
|
||||
|
||||
&[data-part='eye--closed'] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-custom-branding {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import WagtailLogo from './WagtailLogo';
|
|||
interface WagtailBrandingProps {
|
||||
homeUrl: string;
|
||||
strings: Strings;
|
||||
slim: boolean;
|
||||
currentPath: string;
|
||||
navigate(url: string): void;
|
||||
}
|
||||
|
|
@ -12,6 +13,7 @@ interface WagtailBrandingProps {
|
|||
const WagtailBranding: React.FunctionComponent<WagtailBrandingProps> = ({
|
||||
homeUrl,
|
||||
strings,
|
||||
slim,
|
||||
currentPath,
|
||||
navigate,
|
||||
}) => {
|
||||
|
|
@ -79,7 +81,7 @@ const WagtailBranding: React.FunctionComponent<WagtailBrandingProps> = ({
|
|||
};
|
||||
|
||||
const desktopClassName =
|
||||
'sidebar-wagtail-branding' +
|
||||
'sidebar-wagtail-branding w-transition-all w-duration-150' +
|
||||
(isWagging ? ' sidebar-wagtail-branding--wagging' : '');
|
||||
|
||||
return (
|
||||
|
|
@ -92,8 +94,8 @@ const WagtailBranding: React.FunctionComponent<WagtailBrandingProps> = ({
|
|||
onMouseMove={onMouseMove}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<div className="sidebar-wagtail-branding__icon-wrapper">
|
||||
<WagtailLogo />
|
||||
<div className="sidebar-wagtail-branding__icon-wrapper w-transition-all w-duration-150">
|
||||
<WagtailLogo slim={slim} />
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
|
|
@ -106,11 +108,12 @@ export class WagtailBrandingModuleDefinition implements ModuleDefinition {
|
|||
this.homeUrl = homeUrl;
|
||||
}
|
||||
|
||||
render({ strings, key, navigate, currentPath }) {
|
||||
render({ strings, slim, key, navigate, currentPath }) {
|
||||
return (
|
||||
<WagtailBranding
|
||||
key={key}
|
||||
homeUrl={this.homeUrl}
|
||||
slim={slim}
|
||||
strings={strings}
|
||||
navigate={navigate}
|
||||
currentPath={currentPath}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,32 @@ import React from 'react';
|
|||
|
||||
interface WagtailLogoProps {
|
||||
className?: string;
|
||||
slim: boolean;
|
||||
}
|
||||
|
||||
const WagtailLogo = ({ className }: WagtailLogoProps) => {
|
||||
const feathersClasses = 'group-hover:w-text-black';
|
||||
const WagtailLogo = ({ className, slim }: WagtailLogoProps) => {
|
||||
const feathersClasses =
|
||||
'group-hover:w-text-black w-transition-all w-duration-150';
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={`
|
||||
${className || ''}
|
||||
sidebar-wagtail-branding__icon !w-overflow-visible w-group w-text-primary w-transition w-delay-150 w-duration-150 hover:w-scale-75 hover:w-rotate-6 hover:w-translate-y-[-20px] hover:w-translate-x-[10px] w-z-10 w-absolute w-w-[100px] w-h-[125px] w-top-[25px] w-left-[-20px]
|
||||
sidebar-wagtail-branding__icon
|
||||
!w-overflow-visible
|
||||
w-group
|
||||
w-text-primary
|
||||
w-z-10
|
||||
w-absolute
|
||||
w-transition-all
|
||||
w-duration-150
|
||||
hover:w-scale-75
|
||||
hover:w-rotate-6
|
||||
${className || ''}
|
||||
${
|
||||
slim
|
||||
? 'w-w-[42px] w-h-[51px] w-top-2.5 w-left-[-9px] hover:-w-translate-y-1.5 hover:w-translate-x-1'
|
||||
: 'w-w-[100px] w-h-[125px] w-top-[25px] -w-left-5 hover:w-translate-x-2.5 hover:-w-translate-y-5'
|
||||
}
|
||||
`}
|
||||
width="430"
|
||||
height="537"
|
||||
|
|
|
|||
|
|
@ -10,51 +10,55 @@ exports[`Menu should render with the minimum required props 1`] = `
|
|||
/>
|
||||
</nav>
|
||||
<div
|
||||
className="sidebar-footer"
|
||||
className="sidebar-footer sidebar-footer--visible"
|
||||
>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
className="
|
||||
sidebar-footer__account
|
||||
w-bg-primary
|
||||
w-text-white
|
||||
w-flex
|
||||
w-items-center
|
||||
w-relative
|
||||
w-p-0
|
||||
w-w-full
|
||||
w-appearance-none
|
||||
w-border-0
|
||||
w-overflow-hidden
|
||||
w-px-5
|
||||
w-py-3
|
||||
hover:w-bg-primary-200
|
||||
focus:w-bg-primary-200
|
||||
w-transition"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
<ForwardRef(TippyWrapper)
|
||||
disabled={true}
|
||||
placement="right"
|
||||
>
|
||||
<div
|
||||
className="avatar avatar-on-dark w-flex-shrink-0 !w-w-[28px] !w-h-[28px]"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
src="https://gravatar/profile"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="sidebar-footer__account-toggle"
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
className="
|
||||
sidebar-footer__account
|
||||
w-bg-primary
|
||||
w-text-white
|
||||
w-flex
|
||||
w-items-center
|
||||
w-relative
|
||||
w-w-full
|
||||
w-appearance-none
|
||||
w-border-0
|
||||
w-overflow-hidden
|
||||
w-px-5
|
||||
w-py-3
|
||||
hover:w-bg-primary-200
|
||||
focus:w-bg-primary-200
|
||||
w-transition"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
className="sidebar-footer__account-label w-label-3"
|
||||
/>
|
||||
<Icon
|
||||
className="w-w-4 w-h-4 w-text-white"
|
||||
name="arrow-up"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
className="avatar avatar-on-dark w-flex-shrink-0 !w-w-[28px] !w-h-[28px]"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
src="https://gravatar/profile"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="sidebar-footer__account-toggle"
|
||||
>
|
||||
<div
|
||||
className="sidebar-footer__account-label w-label-3"
|
||||
/>
|
||||
<Icon
|
||||
className="w-w-4 w-h-4 w-text-white"
|
||||
name="arrow-up"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</ForwardRef(TippyWrapper)>
|
||||
<ul />
|
||||
</div>
|
||||
</Fragment>
|
||||
|
|
|
|||
|
|
@ -73,6 +73,9 @@ module.exports = {
|
|||
15: '0.15',
|
||||
85: '0.85',
|
||||
},
|
||||
outlineOffset: {
|
||||
inside: '-3px',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"name": "wagtail",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"draft-js": "^0.10.5",
|
||||
"draftail": "^1.4.1",
|
||||
"draftjs-filters": "^2.5.0",
|
||||
|
|
@ -23,6 +24,7 @@
|
|||
"redux-thunk": "^2.3.0",
|
||||
"reselect": "^4.0.0",
|
||||
"telepath-unpack": "^0.0.3",
|
||||
"tippy.js": "^6.3.7",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -3112,7 +3114,6 @@
|
|||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
|
||||
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
|
|
@ -9436,6 +9437,18 @@
|
|||
"react-dom": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tippyjs/react": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
|
||||
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
|
||||
"dependencies": {
|
||||
"tippy.js": "^6.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
|
@ -27477,6 +27490,14 @@
|
|||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tippy.js": {
|
||||
"version": "6.3.7",
|
||||
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||
|
|
@ -31768,8 +31789,7 @@
|
|||
"@popperjs/core": {
|
||||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
|
||||
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.8.3",
|
||||
|
|
@ -36771,6 +36791,14 @@
|
|||
"store2": "^2.12.0"
|
||||
}
|
||||
},
|
||||
"@tippyjs/react": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
|
||||
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
|
||||
"requires": {
|
||||
"tippy.js": "^6.3.1"
|
||||
}
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
|
@ -50810,6 +50838,14 @@
|
|||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||
"dev": true
|
||||
},
|
||||
"tippy.js": {
|
||||
"version": "6.3.7",
|
||||
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||
"requires": {
|
||||
"@popperjs/core": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@
|
|||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"draft-js": "^0.10.5",
|
||||
"draftail": "^1.4.1",
|
||||
"draftjs-filters": "^2.5.0",
|
||||
|
|
@ -110,6 +111,7 @@
|
|||
"redux-thunk": "^2.3.0",
|
||||
"reselect": "^4.0.0",
|
||||
"telepath-unpack": "^0.0.3",
|
||||
"tippy.js": "^6.3.7",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue