mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): bump react-resizable-panels & improve usePanel hook
This commit is contained in:
parent
ab3e689ee0
commit
793cf39964
@ -95,7 +95,7 @@
|
||||
"react-icons": "^4.12.0",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-redux": "^9.0.4",
|
||||
"react-resizable-panels": "^1.0.6",
|
||||
"react-resizable-panels": "^1.0.7",
|
||||
"react-select": "5.8.0",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"react-use": "^17.4.2",
|
||||
|
@ -135,8 +135,8 @@ dependencies:
|
||||
specifier: ^9.0.4
|
||||
version: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
|
||||
react-resizable-panels:
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6(react-dom@18.2.0)(react@18.2.0)
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(react-dom@18.2.0)(react@18.2.0)
|
||||
react-select:
|
||||
specifier: 5.8.0
|
||||
version: 5.8.0(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -11569,8 +11569,8 @@ packages:
|
||||
use-sidecar: 1.1.2(@types/react@18.2.46)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-resizable-panels@1.0.6(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-yZQiOP/uW2nTSdESDUBlBkQ1NQjUABpRKfBqonUQnNYSur7qRDy3W2wEEmrEyUY+W3opshpMiHf45zngIduJ0g==}
|
||||
/react-resizable-panels@1.0.7(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-CluJkHQheeNqIJly2FYDfri3ME+2h2nCXpf0Y+hTO1K1eVtNxXFA5hVp5cUD6NS70iiufswOmnku9QZiLr1hYg==}
|
||||
peerDependencies:
|
||||
react: ^16.14.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
|
||||
|
@ -11,33 +11,100 @@ import {
|
||||
getResizeHandleElementsForGroup,
|
||||
} from 'react-resizable-panels';
|
||||
|
||||
type Direction = 'horizontal' | 'vertical';
|
||||
|
||||
export type UsePanelOptions =
|
||||
| { minSize: number; unit: 'percentages' }
|
||||
| {
|
||||
/**
|
||||
* The minimum size of the panel as a percentage.
|
||||
*/
|
||||
minSize: number;
|
||||
/**
|
||||
* The unit of the minSize
|
||||
*/
|
||||
unit: 'percentages';
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* The minimum size of the panel in pixels.
|
||||
*/
|
||||
minSize: number;
|
||||
/**
|
||||
* The unit of the minSize.
|
||||
*/
|
||||
unit: 'pixels';
|
||||
fallbackMinSizePct: number;
|
||||
/**
|
||||
* The direction of the panel group.
|
||||
* This is required to accurately calculate the available space for the panel, minus the space taken by the handles.
|
||||
*/
|
||||
panelGroupDirection: Direction;
|
||||
/**
|
||||
* A ref to the panel group.
|
||||
*/
|
||||
panelGroupRef: RefObject<ImperativePanelGroupHandle>;
|
||||
panelGroupDirection: 'horizontal' | 'vertical';
|
||||
};
|
||||
|
||||
export const usePanel = (arg: UsePanelOptions) => {
|
||||
export type UsePanelReturn = {
|
||||
/**
|
||||
* The ref to the panel handle.
|
||||
*/
|
||||
ref: RefObject<ImperativePanelHandle>;
|
||||
/**
|
||||
* The dynamically calculated minimum size of the panel.
|
||||
*/
|
||||
minSize: number;
|
||||
/**
|
||||
* Whether the panel is collapsed.
|
||||
*/
|
||||
isCollapsed: boolean;
|
||||
/**
|
||||
* The onCollapse callback. This is required to update the isCollapsed state.
|
||||
* This should be passed to the panel as the onCollapse prop. Wrap it if additional logic is required.
|
||||
*/
|
||||
onCollapse: PanelOnCollapse;
|
||||
/**
|
||||
* The onExpand callback. This is required to update the isCollapsed state.
|
||||
* This should be passed to the panel as the onExpand prop. Wrap it if additional logic is required.
|
||||
*/
|
||||
onExpand: PanelOnExpand;
|
||||
/**
|
||||
* Reset the panel to the minSize. If the panel is already at the minSize, collapse it.
|
||||
* This can be provided to the `onDoubleClick` prop of the panel's nearest resize handle.
|
||||
*/
|
||||
reset: () => void;
|
||||
/**
|
||||
* Toggle the panel between collapsed and expanded.
|
||||
*/
|
||||
toggle: () => void;
|
||||
/**
|
||||
* Expand the panel.
|
||||
*/
|
||||
expand: () => void;
|
||||
/**
|
||||
* Collapse the panel.
|
||||
*/
|
||||
collapse: () => void;
|
||||
/**
|
||||
* Resize the panel to the given size in the same units as the minSize.
|
||||
*/
|
||||
resize: (size: number) => void;
|
||||
};
|
||||
|
||||
export const usePanel = (arg: UsePanelOptions): UsePanelReturn => {
|
||||
const panelHandleRef = useRef<ImperativePanelHandle>(null);
|
||||
const [_minSize, _setMinSize] = useState<number>(
|
||||
arg.unit === 'percentages' ? arg.minSize : arg.fallbackMinSizePct
|
||||
arg.unit === 'percentages' ? arg.minSize : 0
|
||||
);
|
||||
|
||||
// If the units are pixels, we need to calculate the min size as a percentage of the available space
|
||||
// If the units are pixels, we need to calculate the min size as a percentage of the available space,
|
||||
// then resize the panel if it is too small.
|
||||
useLayoutEffect(() => {
|
||||
if (arg.unit === 'percentages' || !arg.panelGroupRef.current) {
|
||||
return;
|
||||
}
|
||||
const panelGroupElement = getPanelGroupElement(
|
||||
arg.panelGroupRef.current.getId()
|
||||
);
|
||||
const panelGroupHandleElements = getResizeHandleElementsForGroup(
|
||||
arg.panelGroupRef.current.getId()
|
||||
);
|
||||
const id = arg.panelGroupRef.current.getId();
|
||||
const panelGroupElement = getPanelGroupElement(id);
|
||||
const panelGroupHandleElements = getResizeHandleElementsForGroup(id);
|
||||
if (!panelGroupElement) {
|
||||
return;
|
||||
}
|
||||
@ -45,33 +112,23 @@ export const usePanel = (arg: UsePanelOptions) => {
|
||||
if (!panelHandleRef?.current) {
|
||||
return;
|
||||
}
|
||||
// Calculate the available space for the panel, minus the space taken by the handles
|
||||
let dim =
|
||||
arg.panelGroupDirection === 'horizontal'
|
||||
? panelGroupElement.offsetWidth
|
||||
: panelGroupElement.offsetHeight;
|
||||
|
||||
panelGroupHandleElements.forEach(
|
||||
(el) =>
|
||||
(dim -=
|
||||
arg.panelGroupDirection === 'horizontal'
|
||||
? el.offsetWidth
|
||||
: el.offsetHeight)
|
||||
const minSizePct = getSizeAsPercentage(
|
||||
arg.minSize,
|
||||
arg.panelGroupRef,
|
||||
arg.panelGroupDirection
|
||||
);
|
||||
|
||||
// Calculate the min size as a percentage of the available space
|
||||
const minSize = (arg.minSize * 100) / dim;
|
||||
// Must store this to avoid race conditions
|
||||
const currentSize = panelHandleRef.current.getSize();
|
||||
// Resize if the current size is smaller than the new min size - happens when the window is resized smaller
|
||||
if (currentSize < minSize) {
|
||||
panelHandleRef.current.resize(minSize);
|
||||
}
|
||||
_setMinSize(minSizePct);
|
||||
|
||||
_setMinSize(minSize);
|
||||
// Resize if the current size is smaller than the new min size - happens when the window is resized smaller
|
||||
if (panelHandleRef.current.getSize() < minSizePct) {
|
||||
panelHandleRef.current.resize(minSizePct);
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(panelGroupElement);
|
||||
panelGroupHandleElements.forEach((el) => resizeObserver.observe(el));
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
@ -106,15 +163,34 @@ export const usePanel = (arg: UsePanelOptions) => {
|
||||
panelHandleRef.current?.collapse();
|
||||
}, []);
|
||||
|
||||
const resize = useCallback(
|
||||
(size: number) => {
|
||||
// If we are using percentages, we can just resize to the given size
|
||||
if (arg.unit === 'percentages') {
|
||||
panelHandleRef.current?.resize(size);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are using pixels, we need to calculate the size as a percentage of the available space
|
||||
const sizeAsPct = getSizeAsPercentage(
|
||||
size,
|
||||
arg.panelGroupRef,
|
||||
arg.panelGroupDirection
|
||||
);
|
||||
panelHandleRef.current?.resize(sizeAsPct);
|
||||
},
|
||||
[arg]
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
// If the panel is really super close to the min size, collapse it
|
||||
const shouldCollapse =
|
||||
Math.abs((panelHandleRef.current?.getSize() ?? 0) - _minSize) < 0.01;
|
||||
if (shouldCollapse) {
|
||||
if (Math.abs((panelHandleRef.current?.getSize() ?? 0) - _minSize) < 0.01) {
|
||||
collapse();
|
||||
} else {
|
||||
panelHandleRef.current?.resize(_minSize);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, resize to the min size
|
||||
panelHandleRef.current?.resize(_minSize);
|
||||
}, [_minSize, collapse]);
|
||||
|
||||
return {
|
||||
@ -127,5 +203,45 @@ export const usePanel = (arg: UsePanelOptions) => {
|
||||
toggle,
|
||||
expand,
|
||||
collapse,
|
||||
resize,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* For a desired size in pixels, calculates the size of the panel as a percentage of the available space.
|
||||
* @param sizeInPixels The desired size of the panel in pixels.
|
||||
* @param panelGroupHandleRef The ref to the panel group handle.
|
||||
* @param panelGroupDirection The direction of the panel group.
|
||||
* @returns The size of the panel as a percentage.
|
||||
*/
|
||||
const getSizeAsPercentage = (
|
||||
sizeInPixels: number,
|
||||
panelGroupHandleRef: RefObject<ImperativePanelGroupHandle>,
|
||||
panelGroupDirection: Direction
|
||||
) => {
|
||||
if (!panelGroupHandleRef.current) {
|
||||
// No panel group handle ref, so we can't calculate the size
|
||||
return 0;
|
||||
}
|
||||
const id = panelGroupHandleRef.current.getId();
|
||||
const panelGroupElement = getPanelGroupElement(id);
|
||||
if (!panelGroupElement) {
|
||||
// No panel group element, size is 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The available space is the width/height of the panel group...
|
||||
let availableSpace =
|
||||
panelGroupDirection === 'horizontal'
|
||||
? panelGroupElement.offsetWidth
|
||||
: panelGroupElement.offsetHeight;
|
||||
|
||||
// ...minus the width/height of the resize handles
|
||||
getResizeHandleElementsForGroup(id).forEach((el) => {
|
||||
availableSpace -=
|
||||
panelGroupDirection === 'horizontal' ? el.offsetWidth : el.offsetHeight;
|
||||
});
|
||||
|
||||
// The final value is a percentage of the available space
|
||||
return (sizeInPixels / availableSpace) * 100;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user