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)" />,
|
||||
autoSaveId: 'left-preserved',
|
||||
defaultLayout: ['300px', 'auto'],
|
||||
initialLayout: ['100px', 'auto'],
|
||||
preserveSide: 'left',
|
||||
leftPanelMinSize: 200,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -89,6 +91,7 @@ export const RightPanelPreserved: Story = {
|
|||
autoSaveId: 'right-preserved',
|
||||
defaultLayout: ['auto', '200px'],
|
||||
preserveSide: 'right',
|
||||
rightPanelMinSize: 200,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -216,6 +219,7 @@ export const PercentageBasedSizing: Story = {
|
|||
rightChildren: <RightContent title="70% Width Panel" />,
|
||||
autoSaveId: 'percentage-sizing',
|
||||
defaultLayout: ['30%', 'auto'],
|
||||
leftPanelMinSize: '200px',
|
||||
preserveSide: 'left',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -40,13 +40,13 @@ interface IAppSplitterProps {
|
|||
/**
|
||||
* Initial preserved-side size from cookies (in pixels)
|
||||
*/
|
||||
initialLayout?: number | null;
|
||||
initialLayout?: (`${number}px` | `${number}%` | 'auto' | number)[];
|
||||
|
||||
/**
|
||||
* Default layout configuration as [left, right] sizes
|
||||
* 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
|
||||
|
@ -218,7 +218,12 @@ const AppSplitterWrapper = forwardRef<AppSplitterRef, IAppSplitterProps>(
|
|||
({ autoSaveId, style, className, split = 'vertical', ...props }, componentRef) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
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: parentSplitterAutoSaveId } = useAppSplitterContext();
|
||||
|
@ -293,7 +298,6 @@ const AppSplitterBase = forwardRef<
|
|||
isVertical,
|
||||
splitterAutoSaveId,
|
||||
containerRef,
|
||||
bustStorageOnInit,
|
||||
split = 'vertical',
|
||||
},
|
||||
ref
|
||||
|
@ -322,23 +326,7 @@ const AppSplitterBase = forwardRef<
|
|||
// STORAGE MANAGEMENT
|
||||
// ================================
|
||||
|
||||
const bustStorageOnInitSplitter = (preservedSideValue: number | null) => {
|
||||
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 defaultValue = useCallback(() => {
|
||||
const [leftValue, rightValue] = defaultLayout;
|
||||
const containerSize =
|
||||
split === 'vertical'
|
||||
|
@ -354,13 +342,65 @@ const AppSplitterBase = forwardRef<
|
|||
const preserveValue = preserveSide === 'left' ? leftValue : rightValue;
|
||||
const result = sizeToPixels(preserveValue, containerSize);
|
||||
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
|
||||
|
||||
const [savedLayout, setSavedLayout] = useCookieState<number | null>(splitterAutoSaveId, {
|
||||
defaultValue,
|
||||
initialValue: initialLayout ?? undefined,
|
||||
bustStorageOnInit: bustStorageOnInitSplitter,
|
||||
initialValue,
|
||||
});
|
||||
|
||||
// ================================
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { isServer } from '@tanstack/react-query';
|
||||
import cookies from 'js-cookie';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useMemoizedFn } from './useMemoizedFn';
|
||||
import { useMount } from './useMount';
|
||||
|
||||
|
@ -25,11 +25,10 @@ interface CookieOptions {
|
|||
|
||||
interface Options<T> {
|
||||
defaultValue?: T | (() => T);
|
||||
initialValue?: T;
|
||||
initialValue?: T | (() => T);
|
||||
serializer?: (value: T) => string;
|
||||
deserializer?: (value: string) => T;
|
||||
onError?: (error: unknown) => void;
|
||||
bustStorageOnInit?: boolean | ((layout: T) => boolean);
|
||||
expirationTime?: number;
|
||||
cookieOptions?: CookieOptions;
|
||||
}
|
||||
|
@ -73,7 +72,6 @@ export function useCookieState<T>(
|
|||
serializer = JSON.stringify,
|
||||
deserializer = JSON.parse,
|
||||
onError,
|
||||
bustStorageOnInit = false,
|
||||
expirationTime = DEFAULT_EXPIRATION_TIME,
|
||||
cookieOptions = {},
|
||||
} = options || {};
|
||||
|
@ -85,14 +83,9 @@ export function useCookieState<T>(
|
|||
|
||||
// Get initial value from cookies or use default
|
||||
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
|
||||
if (initialValue !== undefined) {
|
||||
return initialValue;
|
||||
return typeof initialValue === 'function' ? (initialValue as () => T)() : initialValue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -128,10 +121,6 @@ export function useCookieState<T>(
|
|||
// Data is still valid, deserialize and return the value
|
||||
const deserializedValue = deserializer(JSON.stringify(storageData.value));
|
||||
|
||||
if (typeof bustStorageOnInit === 'function' && bustStorageOnInit(deserializedValue)) {
|
||||
return executeBustStorage();
|
||||
}
|
||||
|
||||
return deserializedValue;
|
||||
} catch (error) {
|
||||
onError?.(error);
|
||||
|
@ -146,35 +135,28 @@ export function useCookieState<T>(
|
|||
setState(getInitialValue());
|
||||
});
|
||||
|
||||
// Update cookies when state changes
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (state === undefined && !isServer) {
|
||||
removeCookie(key, cookieOptions);
|
||||
} else {
|
||||
// Create storage data with current timestamp
|
||||
const storageData: StorageData<T> = {
|
||||
value: JSON.parse(serializer(state)),
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
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;
|
||||
});
|
||||
} else {
|
||||
setState(value);
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
// Create storage data with current timestamp
|
||||
const storageData: StorageData<T> = {
|
||||
value: JSON.parse(serializer(newState)),
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
setCookie(key, JSON.stringify(storageData), expirationTime, cookieOptions);
|
||||
}
|
||||
|
||||
return newState;
|
||||
});
|
||||
} catch (error) {
|
||||
onError?.(error);
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({ children }) => {
|
|||
<ChatContextProvider>
|
||||
<AppSplitter
|
||||
ref={appSplitterRef}
|
||||
leftChildren={mounted && <ChatContainer />}
|
||||
rightChildren={mounted && <FileContainer>{children}</FileContainer>}
|
||||
leftChildren={mounted && renderLeftPanel && <ChatContainer />}
|
||||
rightChildren={mounted && renderRightPanel && <FileContainer>{children}</FileContainer>}
|
||||
autoSaveId={autoSaveId}
|
||||
defaultLayout={defaultSplitterLayout}
|
||||
allowResize={selectedLayout === 'both'}
|
||||
|
@ -61,8 +61,6 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({ children }) => {
|
|||
leftPanelMaxSize={leftPanelMaxSize}
|
||||
rightPanelMinSize={rightPanelMinSize}
|
||||
rightPanelMaxSize={rightPanelMaxSize}
|
||||
renderLeftPanel={renderLeftPanel}
|
||||
renderRightPanel={renderRightPanel}
|
||||
bustStorageOnInit={bustStorageOnInit}
|
||||
/>
|
||||
</ChatContextProvider>
|
||||
|
|
|
@ -70,7 +70,7 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
|||
}
|
||||
});
|
||||
|
||||
const rightChildren = useMemo(() => {
|
||||
const rightChildren: React.ReactNode = useMemo(() => {
|
||||
if (!debouncedSelectedFileViewSecondary) {
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue