feat(core): hide all sidebars when resizing to a small screen (#11105)
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
import { observeResize } from '@affine/component';
|
||||||
|
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
|
import { useService } from '@toeverything/infra';
|
||||||
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
let OBSERVED = false;
|
||||||
|
|
||||||
|
export const useResponsiveSidebar = (
|
||||||
|
hideThreshold = 540,
|
||||||
|
floatThreshold = 768
|
||||||
|
) => {
|
||||||
|
const previousWidthRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
const appSidebarService = useService(AppSidebarService);
|
||||||
|
const workbenchService = useService(WorkbenchService);
|
||||||
|
|
||||||
|
const handleHideSidebar = useCallback(() => {
|
||||||
|
appSidebarService.sidebar.setOpen(false);
|
||||||
|
workbenchService.workbench.setSidebarOpen(false);
|
||||||
|
}, [appSidebarService, workbenchService]);
|
||||||
|
|
||||||
|
const handleFloatSidebar = useCallback(
|
||||||
|
(float: boolean) => {
|
||||||
|
appSidebarService.sidebar.setSmallScreenMode(float);
|
||||||
|
},
|
||||||
|
[appSidebarService.sidebar]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (OBSERVED) {
|
||||||
|
console.warn(
|
||||||
|
`"${useResponsiveSidebar.name}" already observed, do not call it multiple times`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSERVED = true;
|
||||||
|
const unobserve = observeResize(document.body, entry => {
|
||||||
|
if (BUILD_CONFIG.isMobileEdition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const width = entry.contentRect.width;
|
||||||
|
const previousWidth = previousWidthRef.current;
|
||||||
|
|
||||||
|
if (previousWidth === null) {
|
||||||
|
previousWidthRef.current = width;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// should hide sidebar
|
||||||
|
if (width <= hideThreshold && previousWidth > hideThreshold) {
|
||||||
|
handleHideSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BUILD_CONFIG.isElectron) {
|
||||||
|
if (width >= floatThreshold && previousWidth < floatThreshold) {
|
||||||
|
handleFloatSidebar(false);
|
||||||
|
}
|
||||||
|
if (width <= floatThreshold && previousWidth > floatThreshold) {
|
||||||
|
handleFloatSidebar(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousWidthRef.current = width;
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
OBSERVED = false;
|
||||||
|
unobserve();
|
||||||
|
};
|
||||||
|
}, [floatThreshold, handleFloatSidebar, handleHideSidebar, hideThreshold]);
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
CloudQuotaModal,
|
CloudQuotaModal,
|
||||||
LocalQuotaModal,
|
LocalQuotaModal,
|
||||||
} from '@affine/core/components/affine/quota-reached-modal';
|
} from '@affine/core/components/affine/quota-reached-modal';
|
||||||
|
import { useResponsiveSidebar } from '@affine/core/components/hooks/use-responsive-siedebar';
|
||||||
import { SWRConfigProvider } from '@affine/core/components/providers/swr-config-provider';
|
import { SWRConfigProvider } from '@affine/core/components/providers/swr-config-provider';
|
||||||
import { WorkspaceSideEffects } from '@affine/core/components/providers/workspace-side-effects';
|
import { WorkspaceSideEffects } from '@affine/core/components/providers/workspace-side-effects';
|
||||||
import { AIIsland } from '@affine/core/desktop/components/ai-island';
|
import { AIIsland } from '@affine/core/desktop/components/ai-island';
|
||||||
@@ -59,6 +60,7 @@ const WorkspaceLayoutUIContainer = ({ children }: PropsWithChildren) => {
|
|||||||
return get(workbench.basename$) + get(workbench.location$).pathname;
|
return get(workbench.basename$) + get(workbench.location$).pathname;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
useResponsiveSidebar();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContainer data-current-path={currentPath}>{children}</AppContainer>
|
<AppContainer data-current-path={currentPath}>{children}</AppContainer>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
export const floatingMaxWidth = 768;
|
|
||||||
export const navWrapperStyle = style({
|
export const navWrapperStyle = style({
|
||||||
'@media': {
|
'@media': {
|
||||||
print: {
|
print: {
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ import {
|
|||||||
useServiceOptional,
|
useServiceOptional,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { debounce } from 'lodash-es';
|
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { WorkbenchService } from '../../workbench';
|
import { WorkbenchService } from '../../workbench';
|
||||||
import { allowedSplitViewEntityTypes } from '../../workbench/view/split-view/types';
|
import { allowedSplitViewEntityTypes } from '../../workbench/view/split-view/types';
|
||||||
@@ -21,7 +20,6 @@ import { WorkspaceService } from '../../workspace';
|
|||||||
import { AppSidebarService } from '../services/app-sidebar';
|
import { AppSidebarService } from '../services/app-sidebar';
|
||||||
import * as styles from './fallback.css';
|
import * as styles from './fallback.css';
|
||||||
import {
|
import {
|
||||||
floatingMaxWidth,
|
|
||||||
hoverNavWrapperStyle,
|
hoverNavWrapperStyle,
|
||||||
navBodyStyle,
|
navBodyStyle,
|
||||||
navHeaderStyle,
|
navHeaderStyle,
|
||||||
@@ -54,21 +52,6 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
const smallScreenMode = useLiveData(appSidebarService.smallScreenMode$);
|
const smallScreenMode = useLiveData(appSidebarService.smallScreenMode$);
|
||||||
const hovering = useLiveData(appSidebarService.hovering$) && open !== true;
|
const hovering = useLiveData(appSidebarService.hovering$) && open !== true;
|
||||||
const resizing = useLiveData(appSidebarService.resizing$);
|
const resizing = useLiveData(appSidebarService.resizing$);
|
||||||
const [initialized, setInitialized] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (BUILD_CONFIG.isElectron) {
|
|
||||||
setInitialized(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const shouldFloating = window.matchMedia(
|
|
||||||
`(max-width: ${floatingMaxWidth}px)`
|
|
||||||
).matches;
|
|
||||||
|
|
||||||
appSidebarService.setSmallScreenMode(shouldFloating);
|
|
||||||
setInitialized(true);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const sidebarState = smallScreenMode
|
const sidebarState = smallScreenMode
|
||||||
? open
|
? open
|
||||||
@@ -80,27 +63,6 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
? 'floating'
|
? 'floating'
|
||||||
: 'close';
|
: 'close';
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// do not float app sidebar on desktop
|
|
||||||
if (BUILD_CONFIG.isElectron) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onResize() {
|
|
||||||
const isFloatingMaxWidth = window.matchMedia(
|
|
||||||
`(max-width: ${floatingMaxWidth}px)`
|
|
||||||
).matches;
|
|
||||||
const isFloating = isFloatingMaxWidth;
|
|
||||||
appSidebarService.setSmallScreenMode(isFloating);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dOnResize = debounce(onResize, 50);
|
|
||||||
window.addEventListener('resize', dOnResize);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('resize', dOnResize);
|
|
||||||
};
|
|
||||||
}, [appSidebarService]);
|
|
||||||
|
|
||||||
const hasRightBorder = !BUILD_CONFIG.isElectron && !clientBorder;
|
const hasRightBorder = !BUILD_CONFIG.isElectron && !clientBorder;
|
||||||
|
|
||||||
const handleOpenChange = useCallback(
|
const handleOpenChange = useCallback(
|
||||||
@@ -176,10 +138,6 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
});
|
});
|
||||||
}, [workbenchService.views$.value]);
|
}, [workbenchService.views$.value]);
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResizePanel
|
<ResizePanel
|
||||||
|
|||||||
Reference in New Issue
Block a user