mirror of https://github.com/buster-so/buster.git
min size updates
This commit is contained in:
parent
01f3b63424
commit
d725be5505
|
@ -77,7 +77,9 @@ export const LeftPanelPreserved: Story = {
|
||||||
rightChildren: <RightContent title="Right Panel (Auto)" />,
|
rightChildren: <RightContent title="Right Panel (Auto)" />,
|
||||||
autoSaveId: 'left-preserved',
|
autoSaveId: 'left-preserved',
|
||||||
defaultLayout: ['300px', 'auto'],
|
defaultLayout: ['300px', 'auto'],
|
||||||
|
initialLayout: ['100px', 'auto'],
|
||||||
preserveSide: 'left',
|
preserveSide: 'left',
|
||||||
|
leftPanelMinSize: 200,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,6 +91,7 @@ export const RightPanelPreserved: Story = {
|
||||||
autoSaveId: 'right-preserved',
|
autoSaveId: 'right-preserved',
|
||||||
defaultLayout: ['auto', '200px'],
|
defaultLayout: ['auto', '200px'],
|
||||||
preserveSide: 'right',
|
preserveSide: 'right',
|
||||||
|
rightPanelMinSize: 200,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,6 +219,7 @@ export const PercentageBasedSizing: Story = {
|
||||||
rightChildren: <RightContent title="70% Width Panel" />,
|
rightChildren: <RightContent title="70% Width Panel" />,
|
||||||
autoSaveId: 'percentage-sizing',
|
autoSaveId: 'percentage-sizing',
|
||||||
defaultLayout: ['30%', 'auto'],
|
defaultLayout: ['30%', 'auto'],
|
||||||
|
leftPanelMinSize: '200px',
|
||||||
preserveSide: 'left',
|
preserveSide: 'left',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,13 +40,13 @@ interface IAppSplitterProps {
|
||||||
/**
|
/**
|
||||||
* Initial preserved-side size from cookies (in pixels)
|
* Initial preserved-side size from cookies (in pixels)
|
||||||
*/
|
*/
|
||||||
initialLayout?: number | null;
|
initialLayout?: (`${number}px` | `${number}%` | 'auto' | number)[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default layout configuration as [left, right] sizes
|
* Default layout configuration as [left, right] sizes
|
||||||
* Can be numbers (pixels), percentages (strings like "50%"), or "auto"
|
* Can be numbers (pixels), percentages (strings like "50%"), or "auto"
|
||||||
*/
|
*/
|
||||||
defaultLayout: (`${number}px` | `${number}&` | 'auto' | number)[];
|
defaultLayout: (`${number}px` | `${number}%` | 'auto' | number)[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum size for the left panel
|
* Minimum size for the left panel
|
||||||
|
@ -218,7 +218,12 @@ const AppSplitterWrapper = forwardRef<AppSplitterRef, IAppSplitterProps>(
|
||||||
({ autoSaveId, style, className, split = 'vertical', ...props }, componentRef) => {
|
({ autoSaveId, style, className, split = 'vertical', ...props }, componentRef) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const isVertical = split === 'vertical';
|
const isVertical = split === 'vertical';
|
||||||
const [mounted, setMounted] = useState(!props.bustStorageOnInit);
|
const [mounted, setMounted] = useState(
|
||||||
|
!props.leftPanelMinSize &&
|
||||||
|
!props.rightPanelMinSize &&
|
||||||
|
!props.leftPanelMaxSize &&
|
||||||
|
!props.rightPanelMaxSize
|
||||||
|
);
|
||||||
const splitterAutoSaveId = createAutoSaveId(autoSaveId);
|
const splitterAutoSaveId = createAutoSaveId(autoSaveId);
|
||||||
|
|
||||||
const { splitterAutoSaveId: parentSplitterAutoSaveId } = useAppSplitterContext();
|
const { splitterAutoSaveId: parentSplitterAutoSaveId } = useAppSplitterContext();
|
||||||
|
@ -293,7 +298,6 @@ const AppSplitterBase = forwardRef<
|
||||||
isVertical,
|
isVertical,
|
||||||
splitterAutoSaveId,
|
splitterAutoSaveId,
|
||||||
containerRef,
|
containerRef,
|
||||||
bustStorageOnInit,
|
|
||||||
split = 'vertical',
|
split = 'vertical',
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
|
@ -322,23 +326,7 @@ const AppSplitterBase = forwardRef<
|
||||||
// STORAGE MANAGEMENT
|
// STORAGE MANAGEMENT
|
||||||
// ================================
|
// ================================
|
||||||
|
|
||||||
const bustStorageOnInitSplitter = (preservedSideValue: number | null) => {
|
const defaultValue = useCallback(() => {
|
||||||
const refSize =
|
|
||||||
split === 'vertical'
|
|
||||||
? containerRef.current?.offsetWidth
|
|
||||||
: containerRef.current?.offsetHeight;
|
|
||||||
// Don't bust storage if container hasn't been sized yet
|
|
||||||
if (!refSize || refSize === 0) {
|
|
||||||
// console.warn('AppSplitter: container not sized yet');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeof bustStorageOnInit === 'function'
|
|
||||||
? bustStorageOnInit(preservedSideValue, refSize)
|
|
||||||
: !!bustStorageOnInit;
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultValue = () => {
|
|
||||||
const [leftValue, rightValue] = defaultLayout;
|
const [leftValue, rightValue] = defaultLayout;
|
||||||
const containerSize =
|
const containerSize =
|
||||||
split === 'vertical'
|
split === 'vertical'
|
||||||
|
@ -354,13 +342,65 @@ const AppSplitterBase = forwardRef<
|
||||||
const preserveValue = preserveSide === 'left' ? leftValue : rightValue;
|
const preserveValue = preserveSide === 'left' ? leftValue : rightValue;
|
||||||
const result = sizeToPixels(preserveValue, containerSize);
|
const result = sizeToPixels(preserveValue, containerSize);
|
||||||
return result;
|
return result;
|
||||||
};
|
}, [defaultLayout, split, preserveSide]);
|
||||||
|
|
||||||
|
const initialValue = useCallback(() => {
|
||||||
|
if (initialLayout) {
|
||||||
|
const [leftValue, rightValue] = initialLayout;
|
||||||
|
const containerSize =
|
||||||
|
split === 'vertical'
|
||||||
|
? (containerRef.current?.offsetWidth ?? 0)
|
||||||
|
: (containerRef.current?.offsetHeight ?? 0);
|
||||||
|
|
||||||
|
if (preserveSide === 'left' && leftValue === 'auto') {
|
||||||
|
return containerSize;
|
||||||
|
}
|
||||||
|
if (preserveSide === 'right' && rightValue === 'auto') {
|
||||||
|
return containerSize;
|
||||||
|
}
|
||||||
|
const preserveValue = preserveSide === 'left' ? leftValue : rightValue;
|
||||||
|
const result = sizeToPixels(preserveValue, containerSize);
|
||||||
|
|
||||||
|
// Check if the result is within min/max bounds
|
||||||
|
if (containerSize > 0) {
|
||||||
|
const minSize =
|
||||||
|
preserveSide === 'left'
|
||||||
|
? sizeToPixels(leftPanelMinSize, containerSize)
|
||||||
|
: sizeToPixels(rightPanelMinSize, containerSize);
|
||||||
|
const maxSize =
|
||||||
|
preserveSide === 'left'
|
||||||
|
? leftPanelMaxSize
|
||||||
|
? sizeToPixels(leftPanelMaxSize, containerSize)
|
||||||
|
: containerSize
|
||||||
|
: rightPanelMaxSize
|
||||||
|
? sizeToPixels(rightPanelMaxSize, containerSize)
|
||||||
|
: containerSize;
|
||||||
|
|
||||||
|
// If the result is outside the min/max bounds, use the default value
|
||||||
|
if (result < minSize || result > maxSize) {
|
||||||
|
return defaultValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return defaultValue();
|
||||||
|
}, [
|
||||||
|
initialLayout,
|
||||||
|
split,
|
||||||
|
preserveSide,
|
||||||
|
leftPanelMinSize,
|
||||||
|
rightPanelMinSize,
|
||||||
|
leftPanelMaxSize,
|
||||||
|
rightPanelMaxSize,
|
||||||
|
defaultValue,
|
||||||
|
]);
|
||||||
|
|
||||||
// Load saved layout from cookies
|
// Load saved layout from cookies
|
||||||
|
|
||||||
const [savedLayout, setSavedLayout] = useCookieState<number | null>(splitterAutoSaveId, {
|
const [savedLayout, setSavedLayout] = useCookieState<number | null>(splitterAutoSaveId, {
|
||||||
defaultValue,
|
defaultValue,
|
||||||
initialValue: initialLayout ?? undefined,
|
initialValue,
|
||||||
bustStorageOnInit: bustStorageOnInitSplitter,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { isServer } from '@tanstack/react-query';
|
import { isServer } from '@tanstack/react-query';
|
||||||
import cookies from 'js-cookie';
|
import cookies from 'js-cookie';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useMemoizedFn } from './useMemoizedFn';
|
import { useMemoizedFn } from './useMemoizedFn';
|
||||||
import { useMount } from './useMount';
|
import { useMount } from './useMount';
|
||||||
|
|
||||||
|
@ -25,11 +25,10 @@ interface CookieOptions {
|
||||||
|
|
||||||
interface Options<T> {
|
interface Options<T> {
|
||||||
defaultValue?: T | (() => T);
|
defaultValue?: T | (() => T);
|
||||||
initialValue?: T;
|
initialValue?: T | (() => T);
|
||||||
serializer?: (value: T) => string;
|
serializer?: (value: T) => string;
|
||||||
deserializer?: (value: string) => T;
|
deserializer?: (value: string) => T;
|
||||||
onError?: (error: unknown) => void;
|
onError?: (error: unknown) => void;
|
||||||
bustStorageOnInit?: boolean | ((layout: T) => boolean);
|
|
||||||
expirationTime?: number;
|
expirationTime?: number;
|
||||||
cookieOptions?: CookieOptions;
|
cookieOptions?: CookieOptions;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +72,6 @@ export function useCookieState<T>(
|
||||||
serializer = JSON.stringify,
|
serializer = JSON.stringify,
|
||||||
deserializer = JSON.parse,
|
deserializer = JSON.parse,
|
||||||
onError,
|
onError,
|
||||||
bustStorageOnInit = false,
|
|
||||||
expirationTime = DEFAULT_EXPIRATION_TIME,
|
expirationTime = DEFAULT_EXPIRATION_TIME,
|
||||||
cookieOptions = {},
|
cookieOptions = {},
|
||||||
} = options || {};
|
} = options || {};
|
||||||
|
@ -85,14 +83,9 @@ export function useCookieState<T>(
|
||||||
|
|
||||||
// Get initial value from cookies or use default
|
// Get initial value from cookies or use default
|
||||||
const getInitialValue = useMemoizedFn((): T | undefined => {
|
const getInitialValue = useMemoizedFn((): T | undefined => {
|
||||||
// If bustStorageOnInit is true, ignore cookies and use default value
|
|
||||||
if (bustStorageOnInit === true) {
|
|
||||||
return executeBustStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer explicitly provided initialValue if present
|
// Prefer explicitly provided initialValue if present
|
||||||
if (initialValue !== undefined) {
|
if (initialValue !== undefined) {
|
||||||
return initialValue;
|
return typeof initialValue === 'function' ? (initialValue as () => T)() : initialValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -128,10 +121,6 @@ export function useCookieState<T>(
|
||||||
// Data is still valid, deserialize and return the value
|
// Data is still valid, deserialize and return the value
|
||||||
const deserializedValue = deserializer(JSON.stringify(storageData.value));
|
const deserializedValue = deserializer(JSON.stringify(storageData.value));
|
||||||
|
|
||||||
if (typeof bustStorageOnInit === 'function' && bustStorageOnInit(deserializedValue)) {
|
|
||||||
return executeBustStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
return deserializedValue;
|
return deserializedValue;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onError?.(error);
|
onError?.(error);
|
||||||
|
@ -146,35 +135,28 @@ export function useCookieState<T>(
|
||||||
setState(getInitialValue());
|
setState(getInitialValue());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update cookies when state changes
|
// Setter function that handles both direct values and function updates
|
||||||
useEffect(() => {
|
const setStoredState = useMemoizedFn((value?: SetState<T>) => {
|
||||||
try {
|
try {
|
||||||
if (state === undefined && !isServer) {
|
setState((prevState) => {
|
||||||
|
// Calculate the new state value
|
||||||
|
const newState =
|
||||||
|
typeof value === 'function' ? (value as (prevState?: T) => T)(prevState) : value;
|
||||||
|
|
||||||
|
// Update cookie with the new state
|
||||||
|
if (newState === undefined && !isServer) {
|
||||||
removeCookie(key, cookieOptions);
|
removeCookie(key, cookieOptions);
|
||||||
} else {
|
} else {
|
||||||
// Create storage data with current timestamp
|
// Create storage data with current timestamp
|
||||||
const storageData: StorageData<T> = {
|
const storageData: StorageData<T> = {
|
||||||
value: JSON.parse(serializer(state)),
|
value: JSON.parse(serializer(newState)),
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
};
|
};
|
||||||
setCookie(key, JSON.stringify(storageData), expirationTime, cookieOptions);
|
setCookie(key, JSON.stringify(storageData), expirationTime, cookieOptions);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
onError?.(error);
|
|
||||||
}
|
|
||||||
}, [key, state, serializer, onError, expirationTime, cookieOptions]);
|
|
||||||
|
|
||||||
// Setter function that handles both direct values and function updates
|
|
||||||
const setStoredState = useMemoizedFn((value?: SetState<T>) => {
|
|
||||||
try {
|
|
||||||
if (typeof value === 'function') {
|
|
||||||
setState((prevState) => {
|
|
||||||
const newState = (value as (prevState?: T) => T)(prevState);
|
|
||||||
return newState;
|
return newState;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
setState(value);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onError?.(error);
|
onError?.(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,8 +51,8 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({ children }) => {
|
||||||
<ChatContextProvider>
|
<ChatContextProvider>
|
||||||
<AppSplitter
|
<AppSplitter
|
||||||
ref={appSplitterRef}
|
ref={appSplitterRef}
|
||||||
leftChildren={mounted && <ChatContainer />}
|
leftChildren={mounted && renderLeftPanel && <ChatContainer />}
|
||||||
rightChildren={mounted && <FileContainer>{children}</FileContainer>}
|
rightChildren={mounted && renderRightPanel && <FileContainer>{children}</FileContainer>}
|
||||||
autoSaveId={autoSaveId}
|
autoSaveId={autoSaveId}
|
||||||
defaultLayout={defaultSplitterLayout}
|
defaultLayout={defaultSplitterLayout}
|
||||||
allowResize={selectedLayout === 'both'}
|
allowResize={selectedLayout === 'both'}
|
||||||
|
@ -61,8 +61,6 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({ children }) => {
|
||||||
leftPanelMaxSize={leftPanelMaxSize}
|
leftPanelMaxSize={leftPanelMaxSize}
|
||||||
rightPanelMinSize={rightPanelMinSize}
|
rightPanelMinSize={rightPanelMinSize}
|
||||||
rightPanelMaxSize={rightPanelMaxSize}
|
rightPanelMaxSize={rightPanelMaxSize}
|
||||||
renderLeftPanel={renderLeftPanel}
|
|
||||||
renderRightPanel={renderRightPanel}
|
|
||||||
bustStorageOnInit={bustStorageOnInit}
|
bustStorageOnInit={bustStorageOnInit}
|
||||||
/>
|
/>
|
||||||
</ChatContextProvider>
|
</ChatContextProvider>
|
||||||
|
|
|
@ -70,7 +70,7 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const rightChildren = useMemo(() => {
|
const rightChildren: React.ReactNode = useMemo(() => {
|
||||||
if (!debouncedSelectedFileViewSecondary) {
|
if (!debouncedSelectedFileViewSecondary) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue