mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 03:56:23 +08:00
feat(core): show floating sidebar when hovering sidebar swtich
This commit is contained in:
@@ -6,6 +6,7 @@ export const resizeHandleVerticalPadding = createVar(
|
||||
'resize-handle-vertical-padding'
|
||||
);
|
||||
export const animationTimeout = createVar();
|
||||
|
||||
export const root = style({
|
||||
vars: {
|
||||
[panelWidthVar]: '256px',
|
||||
@@ -30,8 +31,11 @@ export const root = style({
|
||||
'&[data-open="false"][data-handle-position="left"]': {
|
||||
marginRight: `calc(${panelWidthVar} * -1)`,
|
||||
},
|
||||
'&[data-enable-animation="true"]': {
|
||||
transition: `margin-left ${animationTimeout} .05s, margin-right ${animationTimeout} .05s, width ${animationTimeout} .05s`,
|
||||
'&[data-enable-animation="true"][data-is-floating="false"]': {
|
||||
transition: `margin-left ${animationTimeout} .05s, margin-right ${animationTimeout} .05s, width ${animationTimeout} .05s,background ${animationTimeout} .05s,scale ${animationTimeout} .05s`,
|
||||
},
|
||||
'&[data-enable-animation="true"][data-is-floating="true"]': {
|
||||
transition: 'margin-left 0.5s cubic-bezier(0.22,1,0.36,1)',
|
||||
},
|
||||
'&[data-transition-state="exited"]': {
|
||||
// avoid focus on hidden panel
|
||||
|
||||
@@ -185,7 +185,7 @@ const DesktopLayout = ({ children }: PropsWithChildren) => {
|
||||
<AppTabsHeader
|
||||
left={
|
||||
<>
|
||||
<SidebarSwitch show />
|
||||
<SidebarSwitch show enableOpenHoverSidebar />
|
||||
<NavigationButtons />
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@ export const Header = ({ left, center, right }: HeaderPros) => {
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const open = useLiveData(appSidebarService.open$);
|
||||
const appSidebarFloating = useLiveData(appSidebarService.responsiveFloating$);
|
||||
const hoverFloating = useLiveData(appSidebarService.hoverFloating$);
|
||||
return (
|
||||
<div
|
||||
className={clsx(style.header)}
|
||||
data-open={open}
|
||||
data-sidebar-floating={appSidebarFloating}
|
||||
data-sidebar-floating={appSidebarFloating || hoverFloating}
|
||||
data-testid="header"
|
||||
>
|
||||
<div className={clsx(style.headerSideContainer)}>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { cssVar, lightCssVariables } from '@toeverything/theme';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
import { createVar, globalStyle, keyframes, style } from '@vanilla-extract/css';
|
||||
|
||||
export const panelWidthVar = createVar('panel-width');
|
||||
|
||||
export const appStyle = style({
|
||||
width: '100%',
|
||||
@@ -40,7 +42,19 @@ globalStyle(`html[data-theme="dark"] ${appStyle}`, {
|
||||
},
|
||||
});
|
||||
|
||||
const anime = keyframes({
|
||||
'0%': {
|
||||
marginLeft: '8px',
|
||||
},
|
||||
'100%': {
|
||||
marginLeft: '0',
|
||||
},
|
||||
});
|
||||
|
||||
export const mainContainerStyle = style({
|
||||
vars: {
|
||||
[panelWidthVar]: '256px',
|
||||
},
|
||||
position: 'relative',
|
||||
zIndex: 0,
|
||||
width: '100%',
|
||||
@@ -48,7 +62,7 @@ export const mainContainerStyle = style({
|
||||
flex: 1,
|
||||
overflow: 'clip',
|
||||
maxWidth: '100%',
|
||||
transition: 'margin-left 0.2s ease',
|
||||
|
||||
selectors: {
|
||||
'&[data-client-border="true"]': {
|
||||
borderRadius: 6,
|
||||
@@ -62,9 +76,14 @@ export const mainContainerStyle = style({
|
||||
},
|
||||
},
|
||||
},
|
||||
'&[data-client-border="true"][data-side-bar-open="true"]': {
|
||||
marginLeft: 0,
|
||||
'&[data-side-bar-open="true"][data-show-pin-sidebar-animation="true"]': {
|
||||
marginLeft: panelWidthVar,
|
||||
transition: 'margin-left 0.5s ease-in-out',
|
||||
},
|
||||
'&[data-client-border="true"][data-side-bar-open="true"][data-side-bar-floating="false"]':
|
||||
{
|
||||
animation: `${anime} 0.5s ease-in-out forwards`,
|
||||
},
|
||||
'&[data-client-border="true"][data-is-desktop="true"]': {
|
||||
marginTop: 0,
|
||||
},
|
||||
|
||||
@@ -6,11 +6,17 @@ import {
|
||||
useLiveData,
|
||||
useService,
|
||||
} from '@toeverything/infra';
|
||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||
import { clsx } from 'clsx';
|
||||
import type { HTMLAttributes, PropsWithChildren, ReactElement } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { forwardRef, useMemo } from 'react';
|
||||
|
||||
import { appStyle, mainContainerStyle, toolStyle } from './index.css';
|
||||
import {
|
||||
appStyle,
|
||||
mainContainerStyle,
|
||||
panelWidthVar,
|
||||
toolStyle,
|
||||
} from './index.css';
|
||||
|
||||
export type WorkspaceRootProps = PropsWithChildren<{
|
||||
className?: string;
|
||||
@@ -48,19 +54,41 @@ export interface MainContainerProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
export const MainContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
PropsWithChildren<MainContainerProps>
|
||||
>(function MainContainer({ className, children, ...props }, ref): ReactElement {
|
||||
>(function MainContainer(
|
||||
{ className, children, style, ...props },
|
||||
ref
|
||||
): ReactElement {
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const appSideBarOpen = useLiveData(appSidebarService.open$);
|
||||
const appSidebarHoverFloating = useLiveData(appSidebarService.hoverFloating$);
|
||||
const appSideBarWidth = useLiveData(appSidebarService.width$);
|
||||
|
||||
const showAppSideBarPinAnimation = useLiveData(
|
||||
appSidebarService.showFloatToPinAnimation$
|
||||
);
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
|
||||
const combinedStyle = useMemo(() => {
|
||||
const dynamicStyle = assignInlineVars({
|
||||
[panelWidthVar]: `${appSideBarWidth}px`,
|
||||
});
|
||||
return {
|
||||
...style,
|
||||
...dynamicStyle,
|
||||
};
|
||||
}, [appSideBarWidth, style]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
style={combinedStyle}
|
||||
className={clsx(mainContainerStyle, className)}
|
||||
data-is-desktop={BUILD_CONFIG.isElectron}
|
||||
data-transparent={false}
|
||||
data-client-border={appSettings.clientBorder}
|
||||
data-side-bar-open={appSideBarOpen}
|
||||
data-side-bar-floating={appSidebarHoverFloating}
|
||||
data-show-pin-sidebar-animation={showAppSideBarPinAnimation}
|
||||
data-testid="main-container"
|
||||
ref={ref}
|
||||
>
|
||||
|
||||
+1
-10
@@ -16,7 +16,6 @@ import { useRegisterCopyLinkCommands } from '@affine/core/components/hooks/affin
|
||||
import { useDocCollectionPageTitle } from '@affine/core/components/hooks/use-block-suite-workspace-page-title';
|
||||
import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal';
|
||||
import { HeaderDivider } from '@affine/core/components/pure/header';
|
||||
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
||||
import type { Doc } from '@blocksuite/affine/store';
|
||||
@@ -34,16 +33,8 @@ const Header = forwardRef<
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
>(({ children, style, className }, ref) => {
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const appSidebarFloating = useLiveData(appSidebarService.responsiveFloating$);
|
||||
return (
|
||||
<div
|
||||
data-testid="header"
|
||||
style={style}
|
||||
className={className}
|
||||
ref={ref}
|
||||
data-sidebar-floating={appSidebarFloating}
|
||||
>
|
||||
<div data-testid="header" style={style} className={className} ref={ref}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -32,6 +32,8 @@ export class AppSidebar extends Entity {
|
||||
hoverFloating$ = new LiveData<boolean>(false);
|
||||
resizing$ = new LiveData<boolean>(false);
|
||||
|
||||
showFloatToPinAnimation$ = new LiveData<boolean>(false);
|
||||
|
||||
getCachedAppSidebarOpenState = () => {
|
||||
return this.appSidebarState.get<boolean>(APP_SIDEBAR_STATE.OPEN);
|
||||
};
|
||||
@@ -58,6 +60,9 @@ export class AppSidebar extends Entity {
|
||||
};
|
||||
|
||||
setHoverFloating = (hoverFloating: boolean) => {
|
||||
if (hoverFloating) {
|
||||
this.showFloatToPinAnimation$.next(false);
|
||||
}
|
||||
this.hoverFloating$.next(hoverFloating);
|
||||
};
|
||||
|
||||
@@ -68,4 +73,8 @@ export class AppSidebar extends Entity {
|
||||
setWidth = (width: number) => {
|
||||
this.appSidebarState.set(APP_SIDEBAR_STATE.WIDTH, width);
|
||||
};
|
||||
|
||||
setShowFloatToPinAnimation = (show: boolean) => {
|
||||
this.showFloatToPinAnimation$.next(show);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
export const floatingMaxWidth = 768;
|
||||
export const navWrapperStyle = style({
|
||||
@@ -10,16 +11,48 @@ export const navWrapperStyle = style({
|
||||
},
|
||||
selectors: {
|
||||
'&[data-has-border=true]': {
|
||||
borderRight: `0.5px solid ${cssVar('borderColor')}`,
|
||||
borderRight: `0.5px solid ${cssVarV2('layer/insideBorder/border')}`,
|
||||
},
|
||||
'&[data-is-floating="true"]': {
|
||||
backgroundColor: cssVar('backgroundPrimaryColor'),
|
||||
backgroundColor: cssVarV2('layer/background/primary'),
|
||||
},
|
||||
'&[data-client-border="true"]': {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
},
|
||||
});
|
||||
export const hoverNavWrapperStyle = style({
|
||||
selectors: {
|
||||
'&[data-is-floating="true"]': {
|
||||
backgroundColor: cssVarV2('layer/background/primary'),
|
||||
height: 'calc(100% - 60px)',
|
||||
marginTop: '52px',
|
||||
marginLeft: '4px',
|
||||
boxShadow: cssVar('--affine-popover-shadow'),
|
||||
borderRadius: '6px',
|
||||
},
|
||||
'&[data-is-floating="true"][data-show-pin-animation="true"]': {
|
||||
marginLeft: '0',
|
||||
},
|
||||
'&[data-is-floating="true"][data-is-electron="true"]': {
|
||||
height: '100%',
|
||||
marginTop: '-4px',
|
||||
},
|
||||
'&[data-is-floating="true"][data-client-border="true"]': {
|
||||
backgroundColor: cssVarV2('layer/background/overlayPanel'),
|
||||
},
|
||||
'&[data-is-floating="true"][data-client-border="true"]::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
opacity: `var(--affine-noise-opacity, 0)`,
|
||||
backgroundRepeat: 'repeat',
|
||||
backgroundSize: '50px',
|
||||
// TODO(@Peng): figure out how to use vanilla-extract webpack plugin to inject img url
|
||||
backgroundImage: `var(--noise-background)`,
|
||||
},
|
||||
},
|
||||
});
|
||||
export const navHeaderButton = style({
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
@@ -62,7 +95,7 @@ export const sidebarFloatMaskStyle = style({
|
||||
left: 0,
|
||||
right: '100%',
|
||||
bottom: 0,
|
||||
background: cssVar('backgroundModalColor'),
|
||||
background: cssVarV2('layer/background/modal'),
|
||||
selectors: {
|
||||
'&[data-open="true"][data-is-floating="true"]': {
|
||||
opacity: 1,
|
||||
|
||||
@@ -9,14 +9,16 @@ import {
|
||||
useServiceOptional,
|
||||
WorkspaceService,
|
||||
} from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import { debounce } from 'lodash-es';
|
||||
import type { PropsWithChildren, ReactElement } from 'react';
|
||||
import { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import { AppSidebarService } from '../services/app-sidebar';
|
||||
import * as styles from './fallback.css';
|
||||
import {
|
||||
floatingMaxWidth,
|
||||
hoverNavWrapperStyle,
|
||||
navBodyStyle,
|
||||
navHeaderStyle,
|
||||
navStyle,
|
||||
@@ -32,6 +34,7 @@ export type History = {
|
||||
|
||||
const MAX_WIDTH = 480;
|
||||
const MIN_WIDTH = 248;
|
||||
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
||||
|
||||
export function AppSidebar({ children }: PropsWithChildren) {
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
@@ -42,8 +45,21 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
||||
|
||||
const open = useLiveData(appSidebarService.open$);
|
||||
const width = useLiveData(appSidebarService.width$);
|
||||
const floating = useLiveData(appSidebarService.responsiveFloating$);
|
||||
const responsiveFloating = useLiveData(appSidebarService.responsiveFloating$);
|
||||
const hoverFloating = useLiveData(appSidebarService.hoverFloating$);
|
||||
const resizing = useLiveData(appSidebarService.resizing$);
|
||||
const showFloatToPinAnimation = useLiveData(
|
||||
appSidebarService.showFloatToPinAnimation$
|
||||
);
|
||||
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const clearExistingTimeout = useCallback(() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// do not float app sidebar on desktop
|
||||
@@ -78,7 +94,6 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
||||
}, [appSidebarService, open, width]);
|
||||
|
||||
const hasRightBorder = !BUILD_CONFIG.isElectron && !clientBorder;
|
||||
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
||||
|
||||
const handleOpenChange = useCallback(
|
||||
(open: boolean) => {
|
||||
@@ -105,10 +120,34 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
||||
appSidebarService.setOpen(false);
|
||||
}, [appSidebarService]);
|
||||
|
||||
const onMouseEnter = useCallback(() => {
|
||||
if (!timeoutRef.current) {
|
||||
return;
|
||||
}
|
||||
clearExistingTimeout();
|
||||
}, [clearExistingTimeout]);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
if (!hoverFloating) {
|
||||
clearExistingTimeout();
|
||||
return;
|
||||
}
|
||||
clearExistingTimeout();
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
appSidebarService.setOpen(false);
|
||||
}, 1500);
|
||||
}, [hoverFloating, clearExistingTimeout, appSidebarService]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearExistingTimeout();
|
||||
};
|
||||
}, [clearExistingTimeout]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResizePanel
|
||||
floating={floating}
|
||||
floating={responsiveFloating || hoverFloating}
|
||||
open={open}
|
||||
resizing={resizing}
|
||||
maxWidth={MAX_WIDTH}
|
||||
@@ -118,30 +157,38 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
||||
onOpen={handleOpenChange}
|
||||
onResizing={handleResizing}
|
||||
onWidthChange={handleWidthChange}
|
||||
className={navWrapperStyle}
|
||||
className={clsx(navWrapperStyle, {
|
||||
[hoverNavWrapperStyle]: hoverFloating,
|
||||
})}
|
||||
resizeHandleOffset={0}
|
||||
resizeHandleVerticalPadding={clientBorder ? 16 : 0}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
data-transparent
|
||||
data-open={open}
|
||||
data-has-border={hasRightBorder}
|
||||
data-testid="app-sidebar-wrapper"
|
||||
data-is-macos-electron={isMacosDesktop}
|
||||
data-client-border={clientBorder}
|
||||
data-is-electron={BUILD_CONFIG.isElectron}
|
||||
data-show-pin-animation={showFloatToPinAnimation}
|
||||
>
|
||||
<nav className={navStyle} data-testid="app-sidebar">
|
||||
{!BUILD_CONFIG.isElectron && <SidebarHeader />}
|
||||
{!BUILD_CONFIG.isElectron && !hoverFloating && <SidebarHeader />}
|
||||
<div className={navBodyStyle} data-testid="sliderBar-inner">
|
||||
{children}
|
||||
</div>
|
||||
</nav>
|
||||
</ResizePanel>
|
||||
<div
|
||||
data-testid="app-sidebar-float-mask"
|
||||
data-open={open}
|
||||
data-is-floating={floating}
|
||||
className={sidebarFloatMaskStyle}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
{!hoverFloating && (
|
||||
<div
|
||||
data-testid="app-sidebar-float-mask"
|
||||
data-open={open}
|
||||
data-is-floating={responsiveFloating}
|
||||
className={sidebarFloatMaskStyle}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+32
-7
@@ -2,46 +2,71 @@ import { IconButton } from '@affine/component';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { SidebarIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
|
||||
import { AppSidebarService } from '../../services/app-sidebar';
|
||||
import * as styles from './sidebar-switch.css';
|
||||
|
||||
export const SidebarSwitch = ({
|
||||
show,
|
||||
enableOpenHoverSidebar,
|
||||
className,
|
||||
}: {
|
||||
show: boolean;
|
||||
enableOpenHoverSidebar?: boolean;
|
||||
className?: string;
|
||||
}) => {
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const open = useLiveData(appSidebarService.open$);
|
||||
const hoverFloating = useLiveData(appSidebarService.hoverFloating$);
|
||||
const switchRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
if (!enableOpenHoverSidebar || open) {
|
||||
return;
|
||||
}
|
||||
appSidebarService.setHoverFloating(true);
|
||||
appSidebarService.setOpen(true);
|
||||
}, [appSidebarService, enableOpenHoverSidebar, open]);
|
||||
|
||||
const handleClickSwitch = useCallback(() => {
|
||||
if (open && hoverFloating) {
|
||||
appSidebarService.setShowFloatToPinAnimation(true);
|
||||
const timeout = setTimeout(() => {
|
||||
appSidebarService.setShowFloatToPinAnimation(false);
|
||||
appSidebarService.setHoverFloating(false);
|
||||
}, 500);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
return appSidebarService.toggleSidebar();
|
||||
}, [appSidebarService, hoverFloating, open]);
|
||||
|
||||
const t = useI18n();
|
||||
const tooltipContent = open
|
||||
? t['com.affine.sidebarSwitch.collapse']()
|
||||
: t['com.affine.sidebarSwitch.expand']();
|
||||
|
||||
const toggleSidebar = useCallback(() => {
|
||||
appSidebarService.toggleSidebar();
|
||||
}, [appSidebarService]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={switchRef}
|
||||
data-show={show}
|
||||
className={styles.sidebarSwitchClip}
|
||||
data-testid={`app-sidebar-arrow-button-${open ? 'collapse' : 'expand'}`}
|
||||
data-enable-open-hover-sidebar={enableOpenHoverSidebar}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
>
|
||||
<IconButton
|
||||
tooltip={tooltipContent}
|
||||
tooltipShortcut={['$mod', '/']}
|
||||
tooltipOptions={{ side: open ? 'bottom' : 'right' }}
|
||||
tooltipOptions={{ side: open && !hoverFloating ? 'bottom' : 'right' }}
|
||||
className={className}
|
||||
size="24"
|
||||
style={{
|
||||
zIndex: 1,
|
||||
}}
|
||||
onClick={toggleSidebar}
|
||||
onClick={handleClickSwitch}
|
||||
>
|
||||
<SidebarIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -297,6 +297,7 @@ export const AppTabsHeader = ({
|
||||
const sidebarWidth = useLiveData(appSidebarService.width$);
|
||||
const sidebarOpen = useLiveData(appSidebarService.open$);
|
||||
const sidebarResizing = useLiveData(appSidebarService.resizing$);
|
||||
const hoverFloating = useLiveData(appSidebarService.hoverFloating$);
|
||||
|
||||
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
||||
const isWindowsDesktop = BUILD_CONFIG.isElectron && environment.isWindows;
|
||||
@@ -414,9 +415,12 @@ export const AppTabsHeader = ({
|
||||
style={{
|
||||
transition: sidebarResizing ? 'none' : undefined,
|
||||
paddingLeft: 12 + trafficLightOffset,
|
||||
width: sidebarOpen ? sidebarWidth : 120 + trafficLightOffset,
|
||||
width:
|
||||
sidebarOpen && !hoverFloating
|
||||
? sidebarWidth
|
||||
: 120 + trafficLightOffset,
|
||||
// minus 16 to account for the padding on the right side of the header (for box shadow)
|
||||
marginRight: sidebarOpen ? -16 : 0,
|
||||
marginRight: sidebarOpen && !hoverFloating ? -16 : 0,
|
||||
}}
|
||||
className={styles.headerLeft}
|
||||
>
|
||||
|
||||
@@ -44,6 +44,9 @@ export const RouteContainer = () => {
|
||||
const viewPosition = useViewPosition();
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const leftSidebarOpen = useLiveData(appSidebarService.open$);
|
||||
const leftSidebarHoverFloating = useLiveData(
|
||||
appSidebarService.hoverFloating$
|
||||
);
|
||||
const workbench = useService(WorkbenchService).workbench;
|
||||
const view = useService(ViewService).view;
|
||||
const sidebarOpen = useLiveData(workbench.sidebarOpen$);
|
||||
@@ -56,7 +59,8 @@ export const RouteContainer = () => {
|
||||
<div className={styles.header}>
|
||||
{!BUILD_CONFIG.isElectron && viewPosition.isFirst && (
|
||||
<SidebarSwitch
|
||||
show={!leftSidebarOpen}
|
||||
show={leftSidebarHoverFloating || !leftSidebarOpen}
|
||||
enableOpenHoverSidebar
|
||||
className={styles.leftSidebarButton}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,9 @@ test('Collapse Sidebar', async ({ page }) => {
|
||||
.locator('[data-testid=app-sidebar-arrow-button-collapse][data-show=true]')
|
||||
.click();
|
||||
const sliderBarArea = page.getByTestId('app-sidebar');
|
||||
await sliderBarArea.hover();
|
||||
await page.mouse.move(300, 300);
|
||||
await page.waitForTimeout(5000);
|
||||
await expect(sliderBarArea).not.toBeInViewport();
|
||||
});
|
||||
|
||||
@@ -20,6 +23,9 @@ test('Expand Sidebar', async ({ page }) => {
|
||||
.locator('[data-testid=app-sidebar-arrow-button-collapse][data-show=true]')
|
||||
.click();
|
||||
const sliderBarArea = page.getByTestId('sliderBar-inner');
|
||||
await sliderBarArea.hover();
|
||||
await page.mouse.move(300, 300);
|
||||
await page.waitForTimeout(5000);
|
||||
await expect(sliderBarArea).not.toBeInViewport();
|
||||
|
||||
await page
|
||||
|
||||
Reference in New Issue
Block a user