fix: fix layout drag width

pull/32/head
fantasticit 2022-05-04 12:07:09 +08:00
parent 8847133683
commit af358c1e04
5 changed files with 77 additions and 47 deletions

View File

@ -0,0 +1,9 @@
export function clamp(val: number, min: number, max: number): number {
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}

View File

@ -1,8 +1,8 @@
export function getStorage(key) { export function getStorage(key, defaultValue = null) {
if (typeof window === 'undefined') throw new Error(); if (typeof window === 'undefined') throw new Error();
const value = localStorage.getItem(key); const value = localStorage.getItem(key);
if (!value) return undefined; if (!value) return defaultValue;
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch (e) { } catch (e) {

View File

@ -1,56 +1,85 @@
import { useCallback, useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { useWindowSize } from 'hooks/use-window-size'; import { useWindowSize } from 'hooks/use-window-size';
import { setStorage, getStorage } from 'helpers/storage'; import { setStorage, getStorage } from 'helpers/storage';
import { clamp } from 'helpers/clamp';
const key = 'dragable-menu-width'; const key = 'dragable-menu-width';
export const MIN_WIDTH = 240;
export const MAX_WIDTH = 600;
const DEFAULT_PC_MIN_WIDTH = 240;
const DEFAULT_PC_MAX_WIDTH = 600;
const DEFAULT_MOBILE_MIN_WIDTH = 24;
const DEFAULT_MOBILE_MAX_WIDTH = 240;
// 收起宽度24
const COLLAPSED_WIDTH = 24; const COLLAPSED_WIDTH = 24;
const PC_MOBILE_CRITICAL_WIDTH = 765;
export const useDragableWidth = () => { export const useDragableWidth = () => {
const runTimeWidthRef = useRef(null); const { width: windowWidth } = useWindowSize();
const { data, mutate } = useSWR<number>(key, getStorage); const [minWidth, setMinWidth] = useState(DEFAULT_MOBILE_MIN_WIDTH);
const windowSize = useWindowSize(); const [maxWidth, setMaxWidth] = useState(DEFAULT_MOBILE_MAX_WIDTH);
const isCollapsed = data <= COLLAPSED_WIDTH; const { data: currentWidth, mutate } = useSWR<number>(key, () => {
const nextWidth = getStorage(key, minWidth);
const updateWidth = (size) => { if (nextWidth <= COLLAPSED_WIDTH) {
return COLLAPSED_WIDTH;
}
return clamp(nextWidth, minWidth, maxWidth);
});
const prevWidthRef = useRef<number>(maxWidth);
const updateWidth = useCallback(
(size) => {
const isMobile = windowWidth <= PC_MOBILE_CRITICAL_WIDTH;
if (isMobile && size < maxWidth) {
size = minWidth;
}
setStorage(key, size); setStorage(key, size);
prevWidthRef.current = size;
mutate(); mutate();
runTimeWidthRef.current = size;
};
const toggleCollapsed = useCallback(
(collapsed = null) => {
const isBool = typeof collapsed === 'boolean';
const nextCollapsed = isBool ? collapsed : !isCollapsed;
const nextWidth = nextCollapsed ? COLLAPSED_WIDTH : MIN_WIDTH;
setStorage(key, nextWidth);
mutate();
runTimeWidthRef.current = nextWidth;
}, },
[isCollapsed, mutate] [mutate, windowWidth, minWidth, maxWidth]
); );
useEffect(() => { const toggleCollapsed = useCallback(() => {
mutate(); const isCollapsed = currentWidth <= COLLAPSED_WIDTH;
return () => { if (!isCollapsed) {
runTimeWidthRef.current = null; prevWidthRef.current = currentWidth;
}; setStorage(key, COLLAPSED_WIDTH);
}, [mutate]); } else {
let nextWidth = prevWidthRef.current;
useEffect(() => { if (nextWidth >= maxWidth) {
if (!windowSize.width) return; nextWidth = maxWidth;
if (windowSize.width <= 765) {
toggleCollapsed(true);
} }
}, [windowSize.width, toggleCollapsed]);
if (nextWidth <= minWidth) {
nextWidth = minWidth;
}
setStorage(key, nextWidth);
}
mutate();
}, [mutate, currentWidth, minWidth]);
useEffect(() => {
const min = windowWidth <= PC_MOBILE_CRITICAL_WIDTH ? DEFAULT_MOBILE_MIN_WIDTH : DEFAULT_PC_MIN_WIDTH;
const max = windowWidth <= PC_MOBILE_CRITICAL_WIDTH ? DEFAULT_MOBILE_MAX_WIDTH : DEFAULT_PC_MAX_WIDTH;
setMinWidth(min);
setMaxWidth(max);
}, [windowWidth, mutate, currentWidth]);
return { return {
width: data, minWidth,
isCollapsed, maxWidth,
width: currentWidth,
isCollapsed: currentWidth <= COLLAPSED_WIDTH,
toggleCollapsed, toggleCollapsed,
updateWidth, updateWidth,
}; };

View File

@ -3,7 +3,7 @@ import cls from 'classnames';
import { Layout as SemiLayout, Button } from '@douyinfe/semi-ui'; import { Layout as SemiLayout, Button } from '@douyinfe/semi-ui';
import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons'; import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons';
import SplitPane from 'react-split-pane'; import SplitPane from 'react-split-pane';
import { useDragableWidth, MIN_WIDTH, MAX_WIDTH } from 'hooks/use-dragable-width'; import { useDragableWidth } from 'hooks/use-dragable-width';
import { RouterHeader } from '../router-header'; import { RouterHeader } from '../router-header';
import styles from './index.module.scss'; import styles from './index.module.scss';
@ -15,13 +15,13 @@ interface IProps {
} }
export const DoubleColumnLayout: React.FC<IProps> = ({ leftNode, rightNode }) => { export const DoubleColumnLayout: React.FC<IProps> = ({ leftNode, rightNode }) => {
const { width, isCollapsed, updateWidth, toggleCollapsed } = useDragableWidth(); const { minWidth, maxWidth, width, isCollapsed, updateWidth, toggleCollapsed } = useDragableWidth();
return ( return (
<SemiLayout className={styles.wrap}> <SemiLayout className={styles.wrap}>
<RouterHeader /> <RouterHeader />
<SemiLayout className={styles.contentWrap}> <SemiLayout className={styles.contentWrap}>
<SplitPane minSize={MIN_WIDTH} maxSize={MAX_WIDTH} size={width} onChange={updateWidth}> <SplitPane minSize={minWidth} maxSize={maxWidth} size={width} onChange={updateWidth}>
<Sider style={{ width: '100%', height: '100%' }} className={styles.leftWrap}> <Sider style={{ width: '100%', height: '100%' }} className={styles.leftWrap}>
<Button <Button
size="small" size="small"

View File

@ -1,9 +1 @@
export function clamp(val: number, min: number, max: number): number { export * from 'helpers/clamp';
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}