volleyball-dev-frontend/node_modules/react-resizable-panels/src/Panel.ts
2025-06-02 16:42:16 +00:00

260 lines
6.4 KiB
TypeScript

import { isBrowser } from "#is-browser";
import { isDevelopment } from "#is-development";
import { PanelGroupContext } from "./PanelGroupContext";
import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
import useUniqueId from "./hooks/useUniqueId";
import {
ForwardedRef,
HTMLAttributes,
PropsWithChildren,
ReactElement,
createElement,
forwardRef,
useContext,
useImperativeHandle,
useRef,
} from "./vendor/react";
export type PanelOnCollapse = () => void;
export type PanelOnExpand = () => void;
export type PanelOnResize = (
size: number,
prevSize: number | undefined
) => void;
export type PanelCallbacks = {
onCollapse?: PanelOnCollapse;
onExpand?: PanelOnExpand;
onResize?: PanelOnResize;
};
export type PanelConstraints = {
collapsedSize?: number | undefined;
collapsible?: boolean | undefined;
defaultSize?: number | undefined;
maxSize?: number | undefined;
minSize?: number | undefined;
};
export type PanelData = {
callbacks: PanelCallbacks;
constraints: PanelConstraints;
id: string;
idIsFromProps: boolean;
order: number | undefined;
};
export type ImperativePanelHandle = {
collapse: () => void;
expand: (minSize?: number) => void;
getId(): string;
getSize(): number;
isCollapsed: () => boolean;
isExpanded: () => boolean;
resize: (size: number) => void;
};
export type PanelProps<
T extends keyof HTMLElementTagNameMap = keyof HTMLElementTagNameMap,
> = Omit<HTMLAttributes<HTMLElementTagNameMap[T]>, "id" | "onResize"> &
PropsWithChildren<{
className?: string;
collapsedSize?: number | undefined;
collapsible?: boolean | undefined;
defaultSize?: number | undefined;
id?: string;
maxSize?: number | undefined;
minSize?: number | undefined;
onCollapse?: PanelOnCollapse;
onExpand?: PanelOnExpand;
onResize?: PanelOnResize;
order?: number;
style?: object;
tagName?: T;
}>;
export function PanelWithForwardedRef({
children,
className: classNameFromProps = "",
collapsedSize,
collapsible,
defaultSize,
forwardedRef,
id: idFromProps,
maxSize,
minSize,
onCollapse,
onExpand,
onResize,
order,
style: styleFromProps,
tagName: Type = "div",
...rest
}: PanelProps & {
forwardedRef: ForwardedRef<ImperativePanelHandle>;
}): ReactElement {
const context = useContext(PanelGroupContext);
if (context === null) {
throw Error(
`Panel components must be rendered within a PanelGroup container`
);
}
const {
collapsePanel,
expandPanel,
getPanelSize,
getPanelStyle,
groupId,
isPanelCollapsed,
reevaluatePanelConstraints,
registerPanel,
resizePanel,
unregisterPanel,
} = context;
const panelId = useUniqueId(idFromProps);
const panelDataRef = useRef<PanelData>({
callbacks: {
onCollapse,
onExpand,
onResize,
},
constraints: {
collapsedSize,
collapsible,
defaultSize,
maxSize,
minSize,
},
id: panelId,
idIsFromProps: idFromProps !== undefined,
order,
});
const devWarningsRef = useRef<{
didLogMissingDefaultSizeWarning: boolean;
}>({
didLogMissingDefaultSizeWarning: false,
});
// Normally we wouldn't log a warning during render,
// but effects don't run on the server, so we can't do it there
if (isDevelopment) {
if (!devWarningsRef.current.didLogMissingDefaultSizeWarning) {
if (!isBrowser && defaultSize == null) {
devWarningsRef.current.didLogMissingDefaultSizeWarning = true;
console.warn(
`WARNING: Panel defaultSize prop recommended to avoid layout shift after server rendering`
);
}
}
}
useIsomorphicLayoutEffect(() => {
const { callbacks, constraints } = panelDataRef.current;
const prevConstraints = { ...constraints };
panelDataRef.current.id = panelId;
panelDataRef.current.idIsFromProps = idFromProps !== undefined;
panelDataRef.current.order = order;
callbacks.onCollapse = onCollapse;
callbacks.onExpand = onExpand;
callbacks.onResize = onResize;
constraints.collapsedSize = collapsedSize;
constraints.collapsible = collapsible;
constraints.defaultSize = defaultSize;
constraints.maxSize = maxSize;
constraints.minSize = minSize;
// If constraints have changed, we should revisit panel sizes.
// This is uncommon but may happen if people are trying to implement pixel based constraints.
if (
prevConstraints.collapsedSize !== constraints.collapsedSize ||
prevConstraints.collapsible !== constraints.collapsible ||
prevConstraints.maxSize !== constraints.maxSize ||
prevConstraints.minSize !== constraints.minSize
) {
reevaluatePanelConstraints(panelDataRef.current, prevConstraints);
}
});
useIsomorphicLayoutEffect(() => {
const panelData = panelDataRef.current;
registerPanel(panelData);
return () => {
unregisterPanel(panelData);
};
}, [order, panelId, registerPanel, unregisterPanel]);
useImperativeHandle(
forwardedRef,
() => ({
collapse: () => {
collapsePanel(panelDataRef.current);
},
expand: (minSize?: number) => {
expandPanel(panelDataRef.current, minSize);
},
getId() {
return panelId;
},
getSize() {
return getPanelSize(panelDataRef.current);
},
isCollapsed() {
return isPanelCollapsed(panelDataRef.current);
},
isExpanded() {
return !isPanelCollapsed(panelDataRef.current);
},
resize: (size: number) => {
resizePanel(panelDataRef.current, size);
},
}),
[
collapsePanel,
expandPanel,
getPanelSize,
isPanelCollapsed,
panelId,
resizePanel,
]
);
const style = getPanelStyle(panelDataRef.current, defaultSize);
return createElement(Type, {
...rest,
children,
className: classNameFromProps,
id: idFromProps,
style: {
...style,
...styleFromProps,
},
// CSS selectors
"data-panel": "",
"data-panel-collapsible": collapsible || undefined,
"data-panel-group-id": groupId,
"data-panel-id": panelId,
"data-panel-size": parseFloat("" + style.flexGrow).toFixed(1),
});
}
export const Panel = forwardRef<ImperativePanelHandle, PanelProps>(
(props: PanelProps, ref: ForwardedRef<ImperativePanelHandle>) =>
createElement(PanelWithForwardedRef, { ...props, forwardedRef: ref })
);
PanelWithForwardedRef.displayName = "Panel";
Panel.displayName = "forwardRef(Panel)";