Файли
openidea/src/ui/Splitter.tsx
T
Arnaud Ritti 4a78310f76 feat(plugins): support detail panes for tree and list views (#2033)
Closes #1950

Plugins can declare `detail = true` on a tree or list view (any tool-window side, grouped or not) to show the selected item's detail-provider tabs beside the list. The pane follows selection and refresh, clears stale content when the item, project or remote host changes, shows an empty state when nothing is selected, and remembers its resizable divider. Services keeps its single split. Released WIT and IPC shapes are unchanged. Verified on macOS in light and dark themes.

Co-authored-by: arnaud-ritti <[email protected]>
2026-11-08 03:18:44 +08:00

70 рядки
2.2 KiB
TypeScript

/**
* Thin wrappers over react-resizable-panels with IDE styling: 1px hairline
* separators with a wider invisible hit area and an accent hover state.
*/
import { Group, Panel, Separator, type GroupProps, type PanelProps, type SeparatorProps } from "react-resizable-panels";
import clsx from "clsx";
import { useState } from "react";
import styles from "./Splitter.module.css";
export function SplitGroup({
className,
storageKey,
defaultLayout,
onLayoutChanged,
...rest
}: GroupProps & { storageKey?: string }) {
const [saved] = useState(() => {
try {
const value: unknown = storageKey ? JSON.parse(localStorage.getItem(storageKey) ?? "null") : null;
return value &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.values(value).every((n) => typeof n === "number" && Number.isFinite(n) && n >= 0 && n <= 100)
? (value as Record<string, number>)
: undefined;
} catch {
return undefined;
}
});
if (!storageKey)
return (
<Group
className={clsx(styles.group, className)}
{...rest}
defaultLayout={defaultLayout}
onLayoutChanged={onLayoutChanged}
/>
);
return (
<Group
className={clsx(styles.group, className)}
{...rest}
defaultLayout={saved ?? defaultLayout}
onLayoutChanged={(layout, meta) => {
if (storageKey && meta.isUserInteraction) {
try {
localStorage.setItem(storageKey, JSON.stringify(meta.requestedLayout ?? layout));
} catch {
/* Storage may be unavailable. */
}
}
onLayoutChanged?.(layout, meta);
}}
/>
);
}
export function SplitPanel({ className, ...rest }: PanelProps) {
return <Panel className={clsx(styles.panel, className)} {...rest} />;
}
export interface SplitHandleProps extends SeparatorProps {
/** `false`: no visible line (neighbouring panels draw their own borders); only the hit area remains. */
hairline?: boolean;
}
export function SplitHandle({ className, hairline = true, ...rest }: SplitHandleProps) {
return <Separator className={clsx(styles.handle, !hairline && styles.invisible, className)} {...rest} />;
}