gridsmith-ui 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.js +3 -3
- package/dist/chunk-2RV23OBK.js +1349 -0
- package/dist/chunk-2RV23OBK.js.map +1 -0
- package/dist/chunk-7TOJ4G7S.js +197 -0
- package/dist/chunk-7TOJ4G7S.js.map +1 -0
- package/dist/{chunk-6MXHGH3D.js → chunk-AAYCQXZQ.js} +90 -161
- package/dist/chunk-AAYCQXZQ.js.map +1 -0
- package/dist/{chunk-ILIODA7W.js → chunk-CXMV7SXJ.js} +4 -3
- package/dist/chunk-CXMV7SXJ.js.map +1 -0
- package/dist/{chunk-EQDR57AM.js → chunk-EYMGLK4M.js} +3 -3
- package/dist/{chunk-EQDR57AM.js.map → chunk-EYMGLK4M.js.map} +1 -1
- package/dist/chunk-IVCVNAXG.js +115 -0
- package/dist/chunk-IVCVNAXG.js.map +1 -0
- package/dist/chunk-RG4KUCRS.js +9 -0
- package/dist/chunk-RG4KUCRS.js.map +1 -0
- package/dist/{chunk-JXANCXK6.js → chunk-RT7RYGC3.js} +39 -4
- package/dist/chunk-RT7RYGC3.js.map +1 -0
- package/dist/{chunk-XYDIBW3J.js → chunk-SBTBMAIU.js} +4 -3
- package/dist/chunk-SBTBMAIU.js.map +1 -0
- package/dist/data-grid.d.ts +38 -4
- package/dist/data-grid.js +304 -264
- package/dist/data-grid.js.map +1 -1
- package/dist/editor.d.ts +6 -5
- package/dist/editor.js +47 -11
- package/dist/editor.js.map +1 -1
- package/dist/index.d.ts +120 -153
- package/dist/index.js +1156 -1814
- package/dist/index.js.map +1 -1
- package/dist/kanban.js +2 -2
- package/dist/resizable.js +9 -8
- package/dist/resizable.js.map +1 -1
- package/dist/roots.css +10 -0
- package/dist/{table-utils-GounRE6s.d.ts → table-utils-BkjNEWts.d.ts} +5 -1
- package/dist/use-pagination-DiJo0aW5.d.ts +153 -0
- package/dist/virtualized.d.ts +11 -1
- package/dist/virtualized.js +67 -18
- package/dist/virtualized.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6MXHGH3D.js.map +0 -1
- package/dist/chunk-ILIODA7W.js.map +0 -1
- package/dist/chunk-JN37OVTP.js +0 -67
- package/dist/chunk-JN37OVTP.js.map +0 -1
- package/dist/chunk-JXANCXK6.js.map +0 -1
- package/dist/chunk-TKVTZWBN.js +0 -50
- package/dist/chunk-TKVTZWBN.js.map +0 -1
- package/dist/chunk-XVB55M3N.js +0 -42
- package/dist/chunk-XVB55M3N.js.map +0 -1
- package/dist/chunk-XYDIBW3J.js.map +0 -1
package/dist/kanban.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Badge } from './chunk-
|
|
2
|
-
import { useTextOverflow, Tooltip } from './chunk-
|
|
1
|
+
import { Badge } from './chunk-SBTBMAIU.js';
|
|
2
|
+
import { useTextOverflow, Tooltip } from './chunk-CXMV7SXJ.js';
|
|
3
3
|
import './chunk-7Q46JXSK.js';
|
|
4
4
|
import './chunk-WH2FUFAC.js';
|
|
5
5
|
import { Icon } from './chunk-4LDYR7QP.js';
|
package/dist/resizable.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useCompact, useElementWidth, useBelow, useAppShell,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useCompact, useElementWidth, useBelow, useAppShell, Drawer } from './chunk-AAYCQXZQ.js';
|
|
2
|
+
import { SplitPaneCtx } from './chunk-RG4KUCRS.js';
|
|
3
|
+
import { Button } from './chunk-7TOJ4G7S.js';
|
|
4
4
|
import { useBreakpoint } from './chunk-CRQ6KCRD.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import { isTabStop, tabStops, rememberFocus } from './chunk-RT7RYGC3.js';
|
|
6
|
+
import './chunk-CXMV7SXJ.js';
|
|
6
7
|
import './chunk-7Q46JXSK.js';
|
|
7
8
|
import './chunk-WH2FUFAC.js';
|
|
8
9
|
import { Icon } from './chunk-4LDYR7QP.js';
|
|
@@ -178,16 +179,16 @@ function SplitView({
|
|
|
178
179
|
return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-split-view": showDetail2 ? "detail" : "list", "data-frame": frame, ...region, className: cn(surface, "flex flex-col"), style: fillStyle, children: [
|
|
179
180
|
showDetail2 ? /* @__PURE__ */ jsxs("div", { className: cn("flex-1 min-h-0 flex flex-col", itemSurface), children: [
|
|
180
181
|
/* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between gap-ds-2 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", children: [
|
|
181
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "arrow-left", size: 14 }), onClick: onDetailClose, children: backLabel }),
|
|
182
|
-
phoneAside && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "info", size: 14 }), onClick: () => setAsideWantsOpen(true), "aria-expanded": asideWantsOpen, children: asideTitle })
|
|
182
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "arrow-left", size: 14 }), onClick: onDetailClose, children: backLabel }),
|
|
183
|
+
phoneAside && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "info", size: 14 }), onClick: () => setAsideWantsOpen(true), "aria-expanded": asideWantsOpen, children: asideTitle })
|
|
183
184
|
] }),
|
|
184
185
|
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsx(SplitPaneCtx, { value: true, children: detail }) })
|
|
185
186
|
] }) : /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsx(SplitPaneCtx, { value: true, children: list }) }),
|
|
186
187
|
phoneAside && /* @__PURE__ */ jsx(Drawer, { open: asideWantsOpen, onClose: () => setAsideWantsOpen(false), title: asideTitle, position: "right", children: aside })
|
|
187
188
|
] });
|
|
188
189
|
}
|
|
189
|
-
const asideButton = showAside && asideCollapsed ? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "info", size: 14 }), onClick: () => setAsideWantsOpen(true), "aria-expanded": asideOpen, children: asideTitle }) : null;
|
|
190
|
-
const closeButton = hiding && onDetailClose ? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": closeLabel, onClick: onDetailClose, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) }) : null;
|
|
190
|
+
const asideButton = showAside && asideCollapsed ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "info", size: 14 }), onClick: () => setAsideWantsOpen(true), "aria-expanded": asideOpen, children: asideTitle }) : null;
|
|
191
|
+
const closeButton = hiding && onDetailClose ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": closeLabel, onClick: onDetailClose, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) }) : null;
|
|
191
192
|
const paneBar = asideButton || closeButton ? /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-end gap-ds-1 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", children: [
|
|
192
193
|
asideButton,
|
|
193
194
|
closeButton
|
package/dist/resizable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../showcase/src/components/ResizablePanels.tsx","../../../showcase/src/components/SplitView.tsx"],"names":["PanelGroup","PanelResizeHandle","showDetail","jsx"],"mappings":"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","file":"resizable.js","sourcesContent":["import {\n Panel,\n Group as PanelGroup,\n Separator as PanelResizeHandle,\n} from \"react-resizable-panels\";\nimport { createContext, useContext, type ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useBreakpoint } from \"../lib/use-breakpoint\";\nimport { Icon } from \"../lib/icons\";\n\ntype PanelGroupProps = ComponentProps<typeof PanelGroup>;\n\ntype Direction = \"horizontal\" | \"vertical\";\nconst DirectionCtx = createContext<Direction>(\"horizontal\");\nconst MobileCtx = createContext(false);\n\nexport interface ResizablePanelGroupProps\n extends Omit<PanelGroupProps, \"className\" | \"orientation\"> {\n direction?: Direction;\n stackOnMobile?: boolean;\n className?: string;\n /** Map of panel id → percentage (0–100). Must be an object, never an array. Panels need matching id props. */\n defaultLayout?: { [panelId: string]: number };\n /** Called after resize ends. Receives { [panelId]: number }, not number[]. */\n onLayoutChanged?: (layout: { [panelId: string]: number }) => void;\n}\n\nexport function ResizablePanelGroup({\n className,\n direction = \"horizontal\",\n stackOnMobile = true,\n ...props\n}: ResizablePanelGroupProps) {\n const isMobile = useBreakpoint(\"sm\");\n const shouldStack = stackOnMobile && isMobile && direction === \"horizontal\";\n const effectiveDirection = shouldStack ? \"vertical\" : direction;\n\n return (\n <DirectionCtx.Provider value={effectiveDirection}>\n <MobileCtx.Provider value={isMobile}>\n <PanelGroup\n className={cn(\n \"flex h-full w-full\",\n effectiveDirection === \"vertical\" && \"flex-col\",\n className,\n )}\n orientation={effectiveDirection}\n {...props}\n />\n </MobileCtx.Provider>\n </DirectionCtx.Provider>\n );\n}\n\ntype PanelProps = ComponentProps<typeof Panel>;\n\nexport interface ResizablePanelProps extends Omit<PanelProps, \"className\"> {\n className?: string;\n id?: string;\n /** number = px. \"30%\" or \"30\" = percent. \"28rem\" / \"400px\" also valid. */\n defaultSize?: number | string;\n minSize?: number | string;\n maxSize?: number | string;\n collapsible?: boolean;\n}\n\nexport function ResizablePanel({ className, ...props }: ResizablePanelProps) {\n return <Panel className={cn(\"\", className)} {...props} />;\n}\n\ntype PanelResizeHandleProps = ComponentProps<typeof PanelResizeHandle>;\n\nexport interface ResizableHandleProps\n extends Omit<PanelResizeHandleProps, \"className\"> {\n withHandle?: boolean;\n className?: string;\n}\n\nexport function ResizableHandle({\n withHandle,\n className,\n ...props\n}: ResizableHandleProps) {\n const direction = useContext(DirectionCtx);\n const isMobile = useContext(MobileCtx);\n const isVertical = direction === \"vertical\";\n\n return (\n <PanelResizeHandle\n className={cn(\n \"relative flex items-center justify-center bg-[var(--ds-border-default)]\",\n isVertical\n ? cn(\n \"h-[var(--ds-border-width)] w-full after:absolute after:left-0 after:right-0 after:content-['']\",\n isMobile ? \"after:-top-3 after:-bottom-3\" : \"after:-top-1 after:-bottom-1\"\n )\n : cn(\n \"w-[var(--ds-border-width)] after:absolute after:inset-y-0 after:content-['']\",\n isMobile ? \"after:-left-3 after:-right-3\" : \"after:-left-1 after:-right-1\"\n ),\n \"focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]\",\n \"[&[data-resize-handle-state=drag]]:bg-[var(--ds-accent-primary)]\",\n \"[&[data-resize-handle-state=hover]]:bg-[var(--ds-accent-primary)]\",\n className,\n )}\n {...props}\n >\n {withHandle && (\n <div\n className={cn(\n \"z-10 flex items-center justify-center rounded-[var(--ds-radius-sm)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\",\n \"bg-[var(--ds-bg-surface)]\",\n isVertical\n ? (isMobile ? \"h-4 w-8\" : \"h-3 w-4\")\n : (isMobile ? \"h-8 w-4\" : \"h-4 w-3\"),\n )}\n >\n <Icon\n name=\"grip-vertical\"\n size={isMobile ? 12 : 10}\n className={cn(\n \"text-[var(--ds-text-muted)]\",\n isVertical && \"rotate-90\",\n )}\n />\n </div>\n )}\n </PanelResizeHandle>\n );\n}\n","import { useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useBelow, useCompact, useElementWidth } from \"../lib/use-compact\";\nimport { ResizablePanelGroup, ResizablePanel, ResizableHandle } from \"./ResizablePanels\";\nimport { Button } from \"./Button\";\nimport { Drawer } from \"./Drawer\";\nimport { Icon } from \"../lib/icons\";\nimport { useAppShell } from \"./AppShell\";\nimport { SplitPaneCtx } from \"../lib/surface-context\";\nimport { isTabStop, rememberFocus, tabStops, type RememberedFocus } from \"../lib/focus\";\n\nexport type SplitViewFrame = \"fill\" | \"page\";\n\n/**\n * How far past a threshold the layout must go before a collapsed part comes back, in px: a classic scrollbar's width\n * (15 px on a Mac with a mouse, 17 on Windows), whose coming and going with the aside moved the layout back and forth\n * across the threshold (round 27 review). No wider: inside the band the answer depends on the way the width came, so a\n * wider band could keep the aside collapsed at a width where it fits.\n */\nconst THRESHOLD_BAND = 17;\n\nexport interface SplitViewProps {\n /** The list (an inbox, a queue): it scrolls on its own. */\n list: ReactNode;\n /** The selected item (a thread, a record): it fills the height and scrolls on its own. */\n detail: ReactNode;\n /** Context beside the detail (a contact, properties). The pane pads it (16 px, like the Drawer it collapses into), so pass content with no padding classes (round 8). Collapses to a toggle that opens it in a Drawer when the layout is narrower than `asideCollapseBelow`. */\n aside?: ReactNode;\n /** Names the aside's toggle and Drawer. */\n asideTitle?: string;\n /**\n * Below `collapseBelow` the list shows alone; with `detailOpen` the detail replaces it, with a Back button that calls\n * `onDetailClose` (detailMode \"stack\"). The page owns the selection, so it owns this flag.\n */\n detailOpen?: boolean;\n onDetailClose?: () => void;\n /** \"stack\" (default): the detail stacks over the list below the collapse width. \"route\": the app navigates to the detail's own route instead, and this renders the list alone there. */\n detailMode?: \"stack\" | \"route\";\n /**\n * \"shown\" (default): above the collapse width the detail pane is always there — an inbox's reading pane may show a\n * placeholder. \"hidden\" (Sprint 25.7, C3): `detailOpen` decides at every width. Closed, the list takes the whole\n * width; open, the pane appears beside it at the width the user last gave it, with a close control that calls\n * `onDetailClose`. For boards, timelines and lists of records. The list stays mounted, so a board or a timeline keeps\n * its scroll and zoom.\n */\n closedDetail?: \"shown\" | \"hidden\";\n /** The open pane's close control, named for a screen reader (closedDetail=\"hidden\"). Default \"Close\". */\n closeLabel?: string;\n backLabel?: string;\n /** Percent of the width the list takes; the divider is draggable. */\n listWidth?: number;\n listMinWidth?: number;\n asideWidth?: number;\n /** Below this width of the layout itself (px) the list and detail stop sitting side by side. */\n collapseBelow?: number;\n /** Below this width of the layout itself (px) the aside becomes a toggle. */\n asideCollapseBelow?: number;\n /**\n * The narrowest the open item's pane may be beside an aside (px). When the list and the aside would leave it less,\n * the aside becomes its toggle and Drawer, so the item keeps the room its content needs: the aside is the secondary\n * content (round 27, R27-13, the owner: \"which information is more important\"). An item that holds a wide table or a\n * card grid may need more: pass it.\n */\n detailMinWidth?: number;\n /** A bordered, rounded surface (inside a page with padding) instead of edge to edge (filling <main>). */\n bordered?: boolean;\n /** \"fill\": the page root, flush to the shell, h-full against main, each pane owns its inset. \"page\" (default): inside a PageContainer under a PageHeader. */\n frame?: SplitViewFrame;\n \"aria-label\"?: string;\n className?: string;\n}\n\n/**\n * The reading-pane composition (Sprint 25.2, R3): one surface with dividers —\n * list, detail and an optional aside — that fills the height it is given (an\n * AppShell's <main> already gives it) and never three cards with gaps. The\n * panels compose ResizablePanelGroup, so the dividers drag. Below the collapse\n * width, measured on the layout itself, the list shows alone and the detail\n * stacks over it (or the app routes to it); the aside collapses to a toggle.\n * The page owns the selection and assembles the contents; keyboard movement\n * through the list is the list's job (StackedList selectedId).\n */\nexport function SplitView({\n list,\n detail,\n aside,\n asideTitle = \"Details\",\n detailOpen = false,\n onDetailClose,\n detailMode = \"stack\",\n closedDetail = \"shown\",\n closeLabel = \"Close\",\n backLabel = \"Back\",\n listWidth = 35,\n listMinWidth = 25,\n asideWidth = 25,\n collapseBelow = 768,\n asideCollapseBelow = 1024,\n detailMinWidth = 480,\n bordered = false,\n frame = \"page\",\n \"aria-label\": ariaLabel,\n className,\n}: SplitViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const compact = useCompact(rootRef, collapseBelow, THRESHOLD_BAND);\n const layoutWidth = useElementWidth(rootRef);\n // The panels' shares as the user left them (the review: the defaults were read, so a list dragged wider left the item\n // under its minimum beside the aside); the aside's last share stands in while it is collapsed.\n const [shares, setShares] = useState<{ list?: number; aside?: number }>({});\n const listShare = shares.list ?? listWidth;\n const asideShare = shares.aside ?? asideWidth;\n // the aside yields when the layout is narrow, or when beside it the item would have less than its minimum\n const itemShare = (100 - listShare - asideShare) / 100;\n const itemBesideAside = aside ? layoutWidth * itemShare : 0;\n // the item moves by its share of the scrollbar's width, so its band is that share of the layout's\n const itemSqueezed = useBelow(itemBesideAside, detailMinWidth, THRESHOLD_BAND * Math.max(0, itemShare));\n const asideCollapsed = useBelow(layoutWidth, asideCollapseBelow, THRESHOLD_BAND) || itemSqueezed;\n const [asideWantsOpen, setAsideWantsOpen] = useState(false);\n const hiding = closedDetail === \"hidden\";\n // Hidden: the aside belongs to the open item, so it (its pane, its toggle, its Drawer) goes with the pane, and a\n // Drawer left open does not come back with the next item (render-time state adjustment).\n const showAside = !!aside && (!hiding || detailOpen);\n if (hiding && !detailOpen && asideWantsOpen) setAsideWantsOpen(false);\n // on a phone the aside's Drawer belongs to the item on screen: Back closes it with the item\n if (compact && !detailOpen && asideWantsOpen) setAsideWantsOpen(false);\n const asideOpen = asideCollapsed && asideWantsOpen;\n const region = ariaLabel ? { role: \"region\" as const, \"aria-label\": ariaLabel } : {};\n // fill: the page root, flush to the shell on every side (no border, no radius, no padding of its own); h-full\n // resolves against <main> because the shell is a flex column down to it. page: inside a PageContainer under a\n // PageHeader, where `bordered` draws the surface. Each pane owns its own inset either way.\n const isFill = frame === \"fill\";\n // In a shell with an app footer the children wrapper has no definite height, so h-full cannot resolve; the shell\n // publishes main's height and fill takes it (the footer then sits below the fold, as it must under a full pane).\n const shell = useAppShell();\n const fillStyle = isFill && shell?.hasFooter ? { height: \"var(--ds-main-height, 100%)\" } : undefined;\n // Edge to edge the list sits on the page's canvas, like every other page, and the open item (its detail and its aside)\n // on the surface (round 27, R27-12, the owner: the whole split on the surface read as a different page). A page frame,\n // bordered or not, stays one surface: a card on the canvas.\n const itemSurface = isFill ? \"bg-[var(--ds-bg-surface)]\" : undefined;\n const surface = cn(\n \"h-full min-h-0 w-full\",\n !isFill && \"bg-[var(--ds-bg-surface)]\",\n isFill && \"rounded-none border-0\",\n bordered && !isFill && \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-[var(--ds-radius-md)] overflow-hidden\",\n className,\n );\n\n // closedDetail=\"hidden\" (C3): the pane opens beside the list at the width the user last gave it; closing it returns\n // focus to the item the pane shows, or its neighbour if that item is gone (D14(e)): the list's selected item, else\n // the last list item that took focus, checked on each render while the pane is open, so switching items is followed.\n // A selection kept in the URL changes the path, so the item is remembered whatever the route does. With no item\n // (opened on load, or from outside the list) focus goes to the list's first Tab stop. No overlay layer: an\n // assistant opened earlier keeps Escape.\n const [listPct, setListPct] = useState(listWidth);\n const paneRef = useRef<HTMLDivElement>(null);\n const shown = useRef<RememberedFocus | null>(null);\n const lastInList = useRef<HTMLElement | null>(null);\n const wasOpen = useRef(false);\n useLayoutEffect(() => {\n if (!hiding) return;\n const listPane = rootRef.current?.querySelector<HTMLElement>('[data-pane=\"list\"]') ?? null;\n if (detailOpen) {\n wasOpen.current = true;\n const selected = listPane?.querySelector<HTMLElement>('[aria-current=\"true\"], [aria-selected=\"true\"]');\n const last = lastInList.current;\n const item = (selected && (isTabStop(selected) ? selected : tabStops(selected)[0]))\n || (last?.isConnected && listPane?.contains(last) ? last : null);\n if (item && item !== shown.current?.el) shown.current = rememberFocus(item, { ignoreRoute: true });\n return;\n }\n if (!wasOpen.current) return;\n wasOpen.current = false;\n const r = shown.current;\n shown.current = null;\n const active = document.activeElement;\n if (active && active !== document.body && active.isConnected) return;\n const first = listPane ? tabStops(listPane)[0] : undefined;\n if (!r?.restore(first)) first?.focus();\n });\n const onPaneKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key !== \"Escape\" || e.defaultPrevented || !onDetailClose) return;\n const t = e.target as HTMLElement;\n // the pane's own DOM only — a portalled popup inside it bubbles here in React's tree — and never from a field\n if (!paneRef.current?.contains(t) || t.closest(\"input, textarea, select, [contenteditable='true']\")) return;\n e.preventDefault();\n onDetailClose();\n };\n\n if (compact) {\n const showDetail = detailMode === \"stack\" && detailOpen;\n // on a phone the item's aside is a toggle beside Back that opens it in a Drawer, as it is on a narrow desktop (the\n // round 27 review: below the collapse width it could not be reached at all)\n const phoneAside = showDetail && !!aside;\n return (\n <div ref={rootRef} data-split-view={showDetail ? \"detail\" : \"list\"} data-frame={frame} {...region} className={cn(surface, \"flex flex-col\")} style={fillStyle}>\n {showDetail ? (\n <div className={cn(\"flex-1 min-h-0 flex flex-col\", itemSurface)}>\n <div className=\"shrink-0 flex items-center justify-between gap-ds-2 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\">\n <Button variant=\"ghost\" size=\"sm\" iconLeft={<Icon name=\"arrow-left\" size={14} />} onClick={onDetailClose}>{backLabel}</Button>\n {phoneAside && <Button variant=\"ghost\" size=\"sm\" iconLeft={<Icon name=\"info\" size={14} />} onClick={() => setAsideWantsOpen(true)} aria-expanded={asideWantsOpen}>{asideTitle}</Button>}\n </div>\n <div className=\"flex-1 min-h-0 overflow-y-auto\"><SplitPaneCtx value={true}>{detail}</SplitPaneCtx></div>\n </div>\n ) : (\n <div className=\"flex-1 min-h-0 overflow-y-auto\"><SplitPaneCtx value={true}>{list}</SplitPaneCtx></div>\n )}\n {phoneAside && (\n <Drawer open={asideWantsOpen} onClose={() => setAsideWantsOpen(false)} title={asideTitle} position=\"right\">\n {aside}\n </Drawer>\n )}\n </div>\n );\n }\n\n const asideButton = showAside && asideCollapsed\n ? <Button variant=\"ghost\" size=\"sm\" iconLeft={<Icon name=\"info\" size={14} />} onClick={() => setAsideWantsOpen(true)} aria-expanded={asideOpen}>{asideTitle}</Button>\n : null;\n const closeButton = hiding && onDetailClose\n ? <Button variant=\"ghost\" size=\"icon-sm\" aria-label={closeLabel} onClick={onDetailClose}><Icon name=\"close\" size={14} /></Button>\n : null;\n const paneBar = asideButton || closeButton ? (\n <div className=\"shrink-0 flex items-center justify-end gap-ds-1 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\">\n {asideButton}\n {closeButton}\n </div>\n ) : null;\n const showDetail = !hiding || detailOpen;\n\n return (\n <div ref={rootRef} data-split-view={hiding && !detailOpen ? \"list\" : \"split\"} data-frame={frame} {...region} className={surface} style={fillStyle}>\n <ResizablePanelGroup\n direction=\"horizontal\"\n stackOnMobile={false}\n className=\"h-full\"\n // the width the user gives the list is kept, so a closed pane reopens at it; the shares decide when the aside yields\n onLayoutChanged={(layout) => {\n setShares((prev) => (prev.list === (layout.list ?? prev.list) && prev.aside === (layout.aside ?? prev.aside) ? prev : { list: layout.list ?? prev.list, aside: layout.aside ?? prev.aside }));\n if (hiding && layout.detail != null && layout.list != null) setListPct(layout.list);\n }}\n >\n <ResizablePanel id=\"list\" defaultSize={`${hiding ? (detailOpen ? listPct : 100) : listWidth}%`} minSize={`${listMinWidth}%`}>\n <div data-pane=\"list\" className=\"h-full min-h-0 overflow-y-auto\" onFocus={hiding ? (e) => { lastInList.current = e.target; } : undefined}>\n <SplitPaneCtx value={true}>{list}</SplitPaneCtx>\n </div>\n </ResizablePanel>\n {showDetail && (\n <>\n <ResizableHandle withHandle />\n <ResizablePanel id=\"detail\" minSize=\"30%\">\n <div ref={paneRef} data-pane=\"detail\" className={cn(\"h-full min-h-0 flex flex-col\", itemSurface)} onKeyDown={hiding ? onPaneKeyDown : undefined}>\n {paneBar}\n <div className=\"flex-1 min-h-0 overflow-y-auto\"><SplitPaneCtx value={true}>{detail}</SplitPaneCtx></div>\n </div>\n </ResizablePanel>\n </>\n )}\n {showAside && !asideCollapsed && (\n <>\n <ResizableHandle withHandle />\n <ResizablePanel id=\"aside\" defaultSize={`${asideWidth}%`} minSize=\"15%\">\n <div data-pane=\"aside\" className={cn(\"h-full min-h-0 overflow-y-auto p-ds-4\", itemSurface)}><SplitPaneCtx value={true}>{aside}</SplitPaneCtx></div>\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n {showAside && asideCollapsed && (\n <Drawer open={asideOpen} onClose={() => setAsideWantsOpen(false)} title={asideTitle} position=\"right\">\n {aside}\n </Drawer>\n )}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../showcase/src/components/ResizablePanels.tsx","../../../showcase/src/components/SplitView.tsx"],"names":["PanelGroup","PanelResizeHandle","showDetail","jsx"],"mappings":"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","file":"resizable.js","sourcesContent":["import {\n Panel,\n Group as PanelGroup,\n Separator as PanelResizeHandle,\n} from \"react-resizable-panels\";\nimport { createContext, useContext, type ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useBreakpoint } from \"../lib/use-breakpoint\";\nimport { Icon } from \"../lib/icons\";\n\ntype PanelGroupProps = ComponentProps<typeof PanelGroup>;\n\ntype Direction = \"horizontal\" | \"vertical\";\nconst DirectionCtx = createContext<Direction>(\"horizontal\");\nconst MobileCtx = createContext(false);\n\nexport interface ResizablePanelGroupProps\n extends Omit<PanelGroupProps, \"className\" | \"orientation\"> {\n direction?: Direction;\n stackOnMobile?: boolean;\n className?: string;\n /** Map of panel id → percentage (0–100). Must be an object, never an array. Panels need matching id props. */\n defaultLayout?: { [panelId: string]: number };\n /** Called after resize ends. Receives { [panelId]: number }, not number[]. */\n onLayoutChanged?: (layout: { [panelId: string]: number }) => void;\n}\n\nexport function ResizablePanelGroup({\n className,\n direction = \"horizontal\",\n stackOnMobile = true,\n ...props\n}: ResizablePanelGroupProps) {\n const isMobile = useBreakpoint(\"sm\");\n const shouldStack = stackOnMobile && isMobile && direction === \"horizontal\";\n const effectiveDirection = shouldStack ? \"vertical\" : direction;\n\n return (\n <DirectionCtx.Provider value={effectiveDirection}>\n <MobileCtx.Provider value={isMobile}>\n <PanelGroup\n className={cn(\n \"flex h-full w-full\",\n effectiveDirection === \"vertical\" && \"flex-col\",\n className,\n )}\n orientation={effectiveDirection}\n {...props}\n />\n </MobileCtx.Provider>\n </DirectionCtx.Provider>\n );\n}\n\ntype PanelProps = ComponentProps<typeof Panel>;\n\nexport interface ResizablePanelProps extends Omit<PanelProps, \"className\"> {\n className?: string;\n id?: string;\n /** number = px. \"30%\" or \"30\" = percent. \"28rem\" / \"400px\" also valid. */\n defaultSize?: number | string;\n minSize?: number | string;\n maxSize?: number | string;\n collapsible?: boolean;\n}\n\nexport function ResizablePanel({ className, ...props }: ResizablePanelProps) {\n return <Panel className={cn(\"\", className)} {...props} />;\n}\n\ntype PanelResizeHandleProps = ComponentProps<typeof PanelResizeHandle>;\n\nexport interface ResizableHandleProps\n extends Omit<PanelResizeHandleProps, \"className\"> {\n withHandle?: boolean;\n className?: string;\n}\n\nexport function ResizableHandle({\n withHandle,\n className,\n ...props\n}: ResizableHandleProps) {\n const direction = useContext(DirectionCtx);\n const isMobile = useContext(MobileCtx);\n const isVertical = direction === \"vertical\";\n\n return (\n <PanelResizeHandle\n className={cn(\n \"relative flex items-center justify-center bg-[var(--ds-border-default)]\",\n isVertical\n ? cn(\n \"h-[var(--ds-border-width)] w-full after:absolute after:left-0 after:right-0 after:content-['']\",\n isMobile ? \"after:-top-3 after:-bottom-3\" : \"after:-top-1 after:-bottom-1\"\n )\n : cn(\n \"w-[var(--ds-border-width)] after:absolute after:inset-y-0 after:content-['']\",\n isMobile ? \"after:-left-3 after:-right-3\" : \"after:-left-1 after:-right-1\"\n ),\n \"focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]\",\n \"[&[data-resize-handle-state=drag]]:bg-[var(--ds-accent-primary)]\",\n \"[&[data-resize-handle-state=hover]]:bg-[var(--ds-accent-primary)]\",\n className,\n )}\n {...props}\n >\n {withHandle && (\n <div\n className={cn(\n \"z-10 flex items-center justify-center rounded-[var(--ds-radius-sm)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\",\n \"bg-[var(--ds-bg-surface)]\",\n isVertical\n ? (isMobile ? \"h-4 w-8\" : \"h-3 w-4\")\n : (isMobile ? \"h-8 w-4\" : \"h-4 w-3\"),\n )}\n >\n <Icon\n name=\"grip-vertical\"\n size={isMobile ? 12 : 10}\n className={cn(\n \"text-[var(--ds-text-muted)]\",\n isVertical && \"rotate-90\",\n )}\n />\n </div>\n )}\n </PanelResizeHandle>\n );\n}\n","import { useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { useBelow, useCompact, useElementWidth } from \"../lib/use-compact\";\nimport { ResizablePanelGroup, ResizablePanel, ResizableHandle } from \"./ResizablePanels\";\nimport { Button } from \"./Button\";\nimport { Drawer } from \"./Drawer\";\nimport { Icon } from \"../lib/icons\";\nimport { useAppShell } from \"./AppShell\";\nimport { SplitPaneCtx } from \"../lib/surface-context\";\nimport { isTabStop, rememberFocus, tabStops, type RememberedFocus } from \"../lib/focus\";\n\nexport type SplitViewFrame = \"fill\" | \"page\";\n\n/**\n * How far past a threshold the layout must go before a collapsed part comes back, in px: a classic scrollbar's width\n * (15 px on a Mac with a mouse, 17 on Windows), whose coming and going with the aside moved the layout back and forth\n * across the threshold (round 27 review). No wider: inside the band the answer depends on the way the width came, so a\n * wider band could keep the aside collapsed at a width where it fits.\n */\nconst THRESHOLD_BAND = 17;\n\nexport interface SplitViewProps {\n /** The list (an inbox, a queue): it scrolls on its own. */\n list: ReactNode;\n /** The selected item (a thread, a record): it fills the height and scrolls on its own. */\n detail: ReactNode;\n /** Context beside the detail (a contact, properties). The pane pads it (16 px, like the Drawer it collapses into), so pass content with no padding classes (round 8). Collapses to a toggle that opens it in a Drawer when the layout is narrower than `asideCollapseBelow`. */\n aside?: ReactNode;\n /** Names the aside's toggle and Drawer. */\n asideTitle?: string;\n /**\n * Below `collapseBelow` the list shows alone; with `detailOpen` the detail replaces it, with a Back button that calls\n * `onDetailClose` (detailMode \"stack\"). The page owns the selection, so it owns this flag.\n */\n detailOpen?: boolean;\n onDetailClose?: () => void;\n /** \"stack\" (default): the detail stacks over the list below the collapse width. \"route\": the app navigates to the detail's own route instead, and this renders the list alone there. */\n detailMode?: \"stack\" | \"route\";\n /**\n * \"shown\" (default): above the collapse width the detail pane is always there — an inbox's reading pane may show a\n * placeholder. \"hidden\" (Sprint 25.7, C3): `detailOpen` decides at every width. Closed, the list takes the whole\n * width; open, the pane appears beside it at the width the user last gave it, with a close control that calls\n * `onDetailClose`. For boards, timelines and lists of records. The list stays mounted, so a board or a timeline keeps\n * its scroll and zoom.\n */\n closedDetail?: \"shown\" | \"hidden\";\n /** The open pane's close control, named for a screen reader (closedDetail=\"hidden\"). Default \"Close\". */\n closeLabel?: string;\n backLabel?: string;\n /** Percent of the width the list takes; the divider is draggable. */\n listWidth?: number;\n listMinWidth?: number;\n asideWidth?: number;\n /** Below this width of the layout itself (px) the list and detail stop sitting side by side. */\n collapseBelow?: number;\n /** Below this width of the layout itself (px) the aside becomes a toggle. */\n asideCollapseBelow?: number;\n /**\n * The narrowest the open item's pane may be beside an aside (px). When the list and the aside would leave it less,\n * the aside becomes its toggle and Drawer, so the item keeps the room its content needs: the aside is the secondary\n * content (round 27, R27-13, the owner: \"which information is more important\"). An item that holds a wide table or a\n * card grid may need more: pass it.\n */\n detailMinWidth?: number;\n /** A bordered, rounded surface (inside a page with padding) instead of edge to edge (filling <main>). */\n bordered?: boolean;\n /** \"fill\": the page root, flush to the shell, h-full against main, each pane owns its inset. \"page\" (default): inside a PageContainer under a PageHeader. */\n frame?: SplitViewFrame;\n \"aria-label\"?: string;\n className?: string;\n}\n\n/**\n * The reading-pane composition (Sprint 25.2, R3): one surface with dividers —\n * list, detail and an optional aside — that fills the height it is given (an\n * AppShell's <main> already gives it) and never three cards with gaps. The\n * panels compose ResizablePanelGroup, so the dividers drag. Below the collapse\n * width, measured on the layout itself, the list shows alone and the detail\n * stacks over it (or the app routes to it); the aside collapses to a toggle.\n * The page owns the selection and assembles the contents; keyboard movement\n * through the list is the list's job (StackedList selectedId).\n */\nexport function SplitView({\n list,\n detail,\n aside,\n asideTitle = \"Details\",\n detailOpen = false,\n onDetailClose,\n detailMode = \"stack\",\n closedDetail = \"shown\",\n closeLabel = \"Close\",\n backLabel = \"Back\",\n listWidth = 35,\n listMinWidth = 25,\n asideWidth = 25,\n collapseBelow = 768,\n asideCollapseBelow = 1024,\n detailMinWidth = 480,\n bordered = false,\n frame = \"page\",\n \"aria-label\": ariaLabel,\n className,\n}: SplitViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const compact = useCompact(rootRef, collapseBelow, THRESHOLD_BAND);\n const layoutWidth = useElementWidth(rootRef);\n // The panels' shares as the user left them (the review: the defaults were read, so a list dragged wider left the item\n // under its minimum beside the aside); the aside's last share stands in while it is collapsed.\n const [shares, setShares] = useState<{ list?: number; aside?: number }>({});\n const listShare = shares.list ?? listWidth;\n const asideShare = shares.aside ?? asideWidth;\n // the aside yields when the layout is narrow, or when beside it the item would have less than its minimum\n const itemShare = (100 - listShare - asideShare) / 100;\n const itemBesideAside = aside ? layoutWidth * itemShare : 0;\n // the item moves by its share of the scrollbar's width, so its band is that share of the layout's\n const itemSqueezed = useBelow(itemBesideAside, detailMinWidth, THRESHOLD_BAND * Math.max(0, itemShare));\n const asideCollapsed = useBelow(layoutWidth, asideCollapseBelow, THRESHOLD_BAND) || itemSqueezed;\n const [asideWantsOpen, setAsideWantsOpen] = useState(false);\n const hiding = closedDetail === \"hidden\";\n // Hidden: the aside belongs to the open item, so it (its pane, its toggle, its Drawer) goes with the pane, and a\n // Drawer left open does not come back with the next item (render-time state adjustment).\n const showAside = !!aside && (!hiding || detailOpen);\n if (hiding && !detailOpen && asideWantsOpen) setAsideWantsOpen(false);\n // on a phone the aside's Drawer belongs to the item on screen: Back closes it with the item\n if (compact && !detailOpen && asideWantsOpen) setAsideWantsOpen(false);\n const asideOpen = asideCollapsed && asideWantsOpen;\n const region = ariaLabel ? { role: \"region\" as const, \"aria-label\": ariaLabel } : {};\n // fill: the page root, flush to the shell on every side (no border, no radius, no padding of its own); h-full\n // resolves against <main> because the shell is a flex column down to it. page: inside a PageContainer under a\n // PageHeader, where `bordered` draws the surface. Each pane owns its own inset either way.\n const isFill = frame === \"fill\";\n // In a shell with an app footer the children wrapper has no definite height, so h-full cannot resolve; the shell\n // publishes main's height and fill takes it (the footer then sits below the fold, as it must under a full pane).\n const shell = useAppShell();\n const fillStyle = isFill && shell?.hasFooter ? { height: \"var(--ds-main-height, 100%)\" } : undefined;\n // Edge to edge the list sits on the page's canvas, like every other page, and the open item (its detail and its aside)\n // on the surface (round 27, R27-12, the owner: the whole split on the surface read as a different page). A page frame,\n // bordered or not, stays one surface: a card on the canvas.\n const itemSurface = isFill ? \"bg-[var(--ds-bg-surface)]\" : undefined;\n const surface = cn(\n \"h-full min-h-0 w-full\",\n !isFill && \"bg-[var(--ds-bg-surface)]\",\n isFill && \"rounded-none border-0\",\n bordered && !isFill && \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-[var(--ds-radius-md)] overflow-hidden\",\n className,\n );\n\n // closedDetail=\"hidden\" (C3): the pane opens beside the list at the width the user last gave it; closing it returns\n // focus to the item the pane shows, or its neighbour if that item is gone (D14(e)): the list's selected item, else\n // the last list item that took focus, checked on each render while the pane is open, so switching items is followed.\n // A selection kept in the URL changes the path, so the item is remembered whatever the route does. With no item\n // (opened on load, or from outside the list) focus goes to the list's first Tab stop. No overlay layer: an\n // assistant opened earlier keeps Escape.\n const [listPct, setListPct] = useState(listWidth);\n const paneRef = useRef<HTMLDivElement>(null);\n const shown = useRef<RememberedFocus | null>(null);\n const lastInList = useRef<HTMLElement | null>(null);\n const wasOpen = useRef(false);\n useLayoutEffect(() => {\n if (!hiding) return;\n const listPane = rootRef.current?.querySelector<HTMLElement>('[data-pane=\"list\"]') ?? null;\n if (detailOpen) {\n wasOpen.current = true;\n const selected = listPane?.querySelector<HTMLElement>('[aria-current=\"true\"], [aria-selected=\"true\"]');\n const last = lastInList.current;\n const item = (selected && (isTabStop(selected) ? selected : tabStops(selected)[0]))\n || (last?.isConnected && listPane?.contains(last) ? last : null);\n if (item && item !== shown.current?.el) shown.current = rememberFocus(item, { ignoreRoute: true });\n return;\n }\n if (!wasOpen.current) return;\n wasOpen.current = false;\n const r = shown.current;\n shown.current = null;\n const active = document.activeElement;\n if (active && active !== document.body && active.isConnected) return;\n const first = listPane ? tabStops(listPane)[0] : undefined;\n if (!r?.restore(first)) first?.focus();\n });\n const onPaneKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key !== \"Escape\" || e.defaultPrevented || !onDetailClose) return;\n const t = e.target as HTMLElement;\n // the pane's own DOM only — a portalled popup inside it bubbles here in React's tree — and never from a field\n if (!paneRef.current?.contains(t) || t.closest(\"input, textarea, select, [contenteditable='true']\")) return;\n e.preventDefault();\n onDetailClose();\n };\n\n if (compact) {\n const showDetail = detailMode === \"stack\" && detailOpen;\n // on a phone the item's aside is a toggle beside Back that opens it in a Drawer, as it is on a narrow desktop (the\n // round 27 review: below the collapse width it could not be reached at all)\n const phoneAside = showDetail && !!aside;\n return (\n <div ref={rootRef} data-split-view={showDetail ? \"detail\" : \"list\"} data-frame={frame} {...region} className={cn(surface, \"flex flex-col\")} style={fillStyle}>\n {showDetail ? (\n <div className={cn(\"flex-1 min-h-0 flex flex-col\", itemSurface)}>\n <div className=\"shrink-0 flex items-center justify-between gap-ds-2 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" iconLeft={<Icon name=\"arrow-left\" size={14} />} onClick={onDetailClose}>{backLabel}</Button>\n {phoneAside && <Button type=\"button\" variant=\"ghost\" size=\"sm\" iconLeft={<Icon name=\"info\" size={14} />} onClick={() => setAsideWantsOpen(true)} aria-expanded={asideWantsOpen}>{asideTitle}</Button>}\n </div>\n <div className=\"flex-1 min-h-0 overflow-y-auto\"><SplitPaneCtx value={true}>{detail}</SplitPaneCtx></div>\n </div>\n ) : (\n <div className=\"flex-1 min-h-0 overflow-y-auto\"><SplitPaneCtx value={true}>{list}</SplitPaneCtx></div>\n )}\n {phoneAside && (\n <Drawer open={asideWantsOpen} onClose={() => setAsideWantsOpen(false)} title={asideTitle} position=\"right\">\n {aside}\n </Drawer>\n )}\n </div>\n );\n }\n\n const asideButton = showAside && asideCollapsed\n ? <Button type=\"button\" variant=\"ghost\" size=\"sm\" iconLeft={<Icon name=\"info\" size={14} />} onClick={() => setAsideWantsOpen(true)} aria-expanded={asideOpen}>{asideTitle}</Button>\n : null;\n const closeButton = hiding && onDetailClose\n ? <Button type=\"button\" variant=\"ghost\" size=\"icon-sm\" aria-label={closeLabel} onClick={onDetailClose}><Icon name=\"close\" size={14} /></Button>\n : null;\n const paneBar = asideButton || closeButton ? (\n <div className=\"shrink-0 flex items-center justify-end gap-ds-1 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\">\n {asideButton}\n {closeButton}\n </div>\n ) : null;\n const showDetail = !hiding || detailOpen;\n\n return (\n <div ref={rootRef} data-split-view={hiding && !detailOpen ? \"list\" : \"split\"} data-frame={frame} {...region} className={surface} style={fillStyle}>\n <ResizablePanelGroup\n direction=\"horizontal\"\n stackOnMobile={false}\n className=\"h-full\"\n // the width the user gives the list is kept, so a closed pane reopens at it; the shares decide when the aside yields\n onLayoutChanged={(layout) => {\n setShares((prev) => (prev.list === (layout.list ?? prev.list) && prev.aside === (layout.aside ?? prev.aside) ? prev : { list: layout.list ?? prev.list, aside: layout.aside ?? prev.aside }));\n if (hiding && layout.detail != null && layout.list != null) setListPct(layout.list);\n }}\n >\n <ResizablePanel id=\"list\" defaultSize={`${hiding ? (detailOpen ? listPct : 100) : listWidth}%`} minSize={`${listMinWidth}%`}>\n <div data-pane=\"list\" className=\"h-full min-h-0 overflow-y-auto\" onFocus={hiding ? (e) => { lastInList.current = e.target; } : undefined}>\n <SplitPaneCtx value={true}>{list}</SplitPaneCtx>\n </div>\n </ResizablePanel>\n {showDetail && (\n <>\n <ResizableHandle withHandle />\n <ResizablePanel id=\"detail\" minSize=\"30%\">\n <div ref={paneRef} data-pane=\"detail\" className={cn(\"h-full min-h-0 flex flex-col\", itemSurface)} onKeyDown={hiding ? onPaneKeyDown : undefined}>\n {paneBar}\n <div className=\"flex-1 min-h-0 overflow-y-auto\"><SplitPaneCtx value={true}>{detail}</SplitPaneCtx></div>\n </div>\n </ResizablePanel>\n </>\n )}\n {showAside && !asideCollapsed && (\n <>\n <ResizableHandle withHandle />\n <ResizablePanel id=\"aside\" defaultSize={`${asideWidth}%`} minSize=\"15%\">\n <div data-pane=\"aside\" className={cn(\"h-full min-h-0 overflow-y-auto p-ds-4\", itemSurface)}><SplitPaneCtx value={true}>{aside}</SplitPaneCtx></div>\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n {showAside && asideCollapsed && (\n <Drawer open={asideOpen} onClose={() => setAsideWantsOpen(false)} title={asideTitle} position=\"right\">\n {aside}\n </Drawer>\n )}\n </div>\n );\n}\n"]}
|
package/dist/roots.css
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
tailwindcss still gets preflight (base) under the components layer and the utilities over it. */
|
|
3
3
|
@layer theme, base, components, utilities;
|
|
4
4
|
|
|
5
|
+
/* A table row's tint (Sprint 25.13 A35), registered as a colour so it can be animated: a row's pinned cells paint it over
|
|
6
|
+
their solid ground while the other cells show the row's own background, and the two fade in together when the row is
|
|
7
|
+
hovered or selected. Unregistered, the pinned cells changed at once and the rest faded in after them (the owner, 30
|
|
8
|
+
September 2026: "a tiny delay in setting each side"). */
|
|
9
|
+
@property --gs-row-tint {
|
|
10
|
+
syntax: "<color>";
|
|
11
|
+
inherits: true;
|
|
12
|
+
initial-value: transparent;
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
/* ==========================================================================
|
|
6
16
|
Root Control Engine
|
|
7
17
|
Derives all design tokens from user-configurable root values.
|
|
@@ -31,6 +31,10 @@ declare function BreakpointWidthProvider({ width, children }: {
|
|
|
31
31
|
declare function useBreakpoint(key: BreakpointKey): boolean;
|
|
32
32
|
|
|
33
33
|
/** Shared column-kind inference for Table and DataGrid. */
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* What a column holds, which decides how its cells lay out: numbers and dates shrink to their content and stay whole,
|
|
36
|
+
* an identifier (a long run of digits: a barcode, an ID) may wrap anywhere, an email truncates, text wraps at spaces.
|
|
37
|
+
*/
|
|
38
|
+
type ColumnKind = "date" | "number" | "email" | "label" | "text" | "identifier";
|
|
35
39
|
|
|
36
40
|
export { type BreakpointKey as B, type ColumnKind as C, BreakpointWidthProvider as a, useBreakpoint as u };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
type PreferenceValidationTiming = "onBlur" | "onSubmit" | "onChange";
|
|
4
|
+
type PreferenceBreakpoint = "sm" | "md" | "lg";
|
|
5
|
+
type PreferenceEmptyStyle = "illustration-cta" | "text-cta" | "redirect";
|
|
6
|
+
type PreferenceLoadingStyle = "skeleton" | "spinner" | "progress-bar";
|
|
7
|
+
type PreferenceDestructiveMode = "dialog" | "undo";
|
|
8
|
+
/** dataDisplay.paginationStyle — how a long list shows more: page numbers, a Load more button, or loading as the user scrolls. */
|
|
9
|
+
type PreferencePaginationStyle = "pagination" | "load-more" | "infinite-scroll";
|
|
10
|
+
interface AppShellPreferences {
|
|
11
|
+
/** forms.validationTiming — when a field first shows its error. */
|
|
12
|
+
validationTiming?: PreferenceValidationTiming;
|
|
13
|
+
/** forms.actions — where a form's buttons sit and how they align. */
|
|
14
|
+
formActions?: {
|
|
15
|
+
align?: "start" | "end";
|
|
16
|
+
placement?: "auto" | "inline";
|
|
17
|
+
};
|
|
18
|
+
/** dataDisplay.itemsPerPage — the page size of a paged or load-more list. */
|
|
19
|
+
itemsPerPage?: number;
|
|
20
|
+
/** dataDisplay.paginationStyle — page numbers, a Load more button, or loading on scroll; usePagination and Pagination follow it (round 22). */
|
|
21
|
+
paginationStyle?: PreferencePaginationStyle;
|
|
22
|
+
/** responsive.tableToCardsBreakpoint — below this width a Table becomes stacked cards. */
|
|
23
|
+
tableToCardsBreakpoint?: PreferenceBreakpoint;
|
|
24
|
+
/** emptyStates.emptyStyle — illustration with an action, text with an action, or redirect to creation. */
|
|
25
|
+
emptyStyle?: PreferenceEmptyStyle;
|
|
26
|
+
/** emptyStates.loadingStyle — what a page shows while data loads. */
|
|
27
|
+
loadingStyle?: PreferenceLoadingStyle;
|
|
28
|
+
/** destructiveActions.mode — confirm in a dialog, or act now with an Undo toast. */
|
|
29
|
+
destructiveMode?: PreferenceDestructiveMode;
|
|
30
|
+
/** destructiveActions.highSeverityActions — always confirm in a dialog, even in undo mode. */
|
|
31
|
+
highSeverityActions?: string[];
|
|
32
|
+
/** destructiveActions.typeToConfirmActions — the user types the name before the confirm enables. */
|
|
33
|
+
typeToConfirmActions?: string[];
|
|
34
|
+
/** destructiveActions.undoWindowSeconds — how long the Undo toast stays. */
|
|
35
|
+
undoWindowSeconds?: number;
|
|
36
|
+
}
|
|
37
|
+
declare const PreferencesContext: react.Context<AppShellPreferences | null>;
|
|
38
|
+
/** Every preference in force, the shell's value or the house default — what app code reads. */
|
|
39
|
+
interface ResolvedPreferences {
|
|
40
|
+
validationTiming: PreferenceValidationTiming;
|
|
41
|
+
formActions: {
|
|
42
|
+
align: "start" | "end";
|
|
43
|
+
placement: "auto" | "inline";
|
|
44
|
+
};
|
|
45
|
+
itemsPerPage: number;
|
|
46
|
+
paginationStyle: PreferencePaginationStyle;
|
|
47
|
+
tableToCardsBreakpoint: PreferenceBreakpoint;
|
|
48
|
+
emptyStyle: PreferenceEmptyStyle;
|
|
49
|
+
loadingStyle: PreferenceLoadingStyle;
|
|
50
|
+
destructiveMode: PreferenceDestructiveMode;
|
|
51
|
+
highSeverityActions: string[];
|
|
52
|
+
typeToConfirmActions: string[];
|
|
53
|
+
undoWindowSeconds: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* For app code: every preference in force — the shell's value, else the house default — so a page never writes a
|
|
57
|
+
* fallback of its own. Round 21 (run 58): with every field optional, TypeScript made the builder write
|
|
58
|
+
* `const { itemsPerPage = 20 } = usePreferences()`, a second copy of the preference it then edited by hand when the
|
|
59
|
+
* rule changed. Paged lists use `usePagination`, which reads the page size itself.
|
|
60
|
+
*/
|
|
61
|
+
declare function usePreferences(): ResolvedPreferences;
|
|
62
|
+
/**
|
|
63
|
+
* "Remove range" → "remove_range": the key the high-severity and type-to-confirm lists use. The engine's `actionKey`
|
|
64
|
+
* (platform/src/lib/destructive-mode.ts) is the same rule for compliance; a platform test keeps the two identical.
|
|
65
|
+
*/
|
|
66
|
+
declare function preferenceActionKey(label: string): string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A long list in one hook (round 21; all three list styles since round 22). The page size and the list style are the
|
|
70
|
+
* project's `dataDisplay.itemsPerPage` and `dataDisplay.paginationStyle`, read from the shell, so a mid-build
|
|
71
|
+
* `update_rule` on either reaches every list with no page edited. The page gets what to show, the props for
|
|
72
|
+
* `<Pagination>` — page numbers, a Load more button or loading on scroll, whichever the project's style is — and the
|
|
73
|
+
* numbers the count line needs ("Showing 51–100 of 200 events", or "Showing 40 of 120 members" while a list grows).
|
|
74
|
+
*
|
|
75
|
+
* Run 58 hand-wrote the paging; runs 59 and 60 hand-wrote load-more and scroll loading — an IntersectionObserver, a
|
|
76
|
+
* visible count and a page size copied in as a constant, twice in one app — because the library had no way to.
|
|
77
|
+
*
|
|
78
|
+
* const rows = events.map(toRow)
|
|
79
|
+
* const pager = usePagination(rows)
|
|
80
|
+
* <TableToolbar resultCount={rows.length} resultNoun={["event", "events"]} pagination={pager} />
|
|
81
|
+
* <Table columns={columns} data={rows} pager={pager} />
|
|
82
|
+
*
|
|
83
|
+
* The list takes the whole pager (Sprint 25.13 A3): Table, DataGrid, StackedList, Timeline and VirtualizedList draw the
|
|
84
|
+
* pager's part of every row and its control beneath, and a sortable Table sorts every row, not one page. For anything
|
|
85
|
+
* else, draw the page's items and the control yourself: `pager.items.map(…)` then `<Pagination {...pager.props} />`.
|
|
86
|
+
*
|
|
87
|
+
* A long page that must mount only the rows on screen (logs, a feed) still pages: the size the user asked for goes to
|
|
88
|
+
* the hook, with its override note, and the list mounts only what is on screen of each page (A49):
|
|
89
|
+
*
|
|
90
|
+
* // @gridsmith-override dataDisplay.itemsPerPage: <the user's words>
|
|
91
|
+
* const pager = usePagination(entries, { pageSize: 100 })
|
|
92
|
+
* <VirtualizedList items={entries} pager={pager} estimateSize={() => 40} renderItem={(entry) => <LogLine entry={entry} />} />
|
|
93
|
+
*/
|
|
94
|
+
interface Pager<T> {
|
|
95
|
+
/** The list style this list follows: the project's, unless `style` was passed. */
|
|
96
|
+
style: PreferencePaginationStyle;
|
|
97
|
+
/** What to show: the current page's rows, or every row loaded so far on a list that grows. */
|
|
98
|
+
items: T[];
|
|
99
|
+
/** The current page, from 1; on a growing list, how many pages are loaded. */
|
|
100
|
+
page: number;
|
|
101
|
+
pageSize: number;
|
|
102
|
+
totalItems: number;
|
|
103
|
+
totalPages: number;
|
|
104
|
+
/** The first and last row numbers on screen, from 1 ("Showing 51–100"); [0, 0] for an empty list. */
|
|
105
|
+
range: [number, number];
|
|
106
|
+
/** How many rows are on screen. */
|
|
107
|
+
shown: number;
|
|
108
|
+
/** Rows left to show: a next page, or more to load. */
|
|
109
|
+
hasMore: boolean;
|
|
110
|
+
/** The next page, or the next `pageSize` rows on a growing list. */
|
|
111
|
+
loadMore: () => void;
|
|
112
|
+
setPage: (page: number) => void;
|
|
113
|
+
/** A page-size selector's choice; it wins over the project's page size and starts again at page 1. */
|
|
114
|
+
setPageSize: (size: number) => void;
|
|
115
|
+
/**
|
|
116
|
+
* Spread onto <Pagination>: it renders page numbers, a Load more button or the scroll trigger, as `style` says. A list
|
|
117
|
+
* given the whole pager (`<Table pager={pager}>`, Sprint 25.13 A3) draws this control itself.
|
|
118
|
+
*/
|
|
119
|
+
props: {
|
|
120
|
+
currentPage: number;
|
|
121
|
+
totalPages: number;
|
|
122
|
+
onPageChange: (page: number) => void;
|
|
123
|
+
pageSize: number;
|
|
124
|
+
onPageSizeChange: (size: number) => void;
|
|
125
|
+
mode: PreferencePaginationStyle;
|
|
126
|
+
hasMore: boolean;
|
|
127
|
+
onLoadMore: () => void;
|
|
128
|
+
/** The page sizes the user may choose between, when the page offers the choice (`pageSizeOptions` below). */
|
|
129
|
+
pageSizeOptions?: number[];
|
|
130
|
+
/** Which pager the control belongs to, so a second control for the same list is caught in development (A3). */
|
|
131
|
+
pagerToken: object;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
interface PaginationOptions {
|
|
135
|
+
/** A fixed page size for this list, over the project's. Leave it out: the project's page size is the rule. */
|
|
136
|
+
pageSize?: number;
|
|
137
|
+
/** Values that start the list again at page 1 when they change — the sort column and direction, a search query. */
|
|
138
|
+
resetOn?: unknown;
|
|
139
|
+
/**
|
|
140
|
+
* A list style for this list only, over the project's. Leave it out: the project's style is the rule. Passing one that
|
|
141
|
+
* differs is overriding a rule in force — ask the user first, and put `// @gridsmith-override
|
|
142
|
+
* dataDisplay.paginationStyle: <their reason>` above the call.
|
|
143
|
+
*/
|
|
144
|
+
style?: PreferencePaginationStyle;
|
|
145
|
+
/**
|
|
146
|
+
* The page sizes the user may choose between, beside the page numbers (`[10, 25, 50]`). The choice goes with the
|
|
147
|
+
* control, so a list given the pager (A3) offers it too. Leave it out for the project's page size alone.
|
|
148
|
+
*/
|
|
149
|
+
pageSizeOptions?: number[];
|
|
150
|
+
}
|
|
151
|
+
declare function usePagination<T>(list: readonly T[], options?: PaginationOptions): Pager<T>;
|
|
152
|
+
|
|
153
|
+
export { type AppShellPreferences as A, type Pager as P, type ResolvedPreferences as R, type PaginationOptions as a, PreferencesContext as b, usePreferences as c, preferenceActionKey as p, usePagination as u };
|
package/dist/virtualized.d.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode, Ref } from 'react';
|
|
3
3
|
import { VirtualItem } from '@tanstack/react-virtual';
|
|
4
|
+
import { P as Pager } from './use-pagination-DiJo0aW5.js';
|
|
4
5
|
|
|
5
6
|
interface VirtualizedListProps<T> {
|
|
6
7
|
items: T[];
|
|
7
8
|
estimateSize: (index: number) => number;
|
|
9
|
+
/** An item, with its index in `items` (the whole list, when the list is paged). */
|
|
8
10
|
renderItem: (item: T, index: number, virtualItem: VirtualItem) => ReactNode;
|
|
9
11
|
overscan?: number;
|
|
10
12
|
height?: number | string;
|
|
11
13
|
className?: string;
|
|
12
14
|
ref?: Ref<HTMLDivElement>;
|
|
15
|
+
/**
|
|
16
|
+
* The list's pager (Sprint 25.13 A49): paging and drawing only the rows on screen combine. Pass every item and
|
|
17
|
+
* `usePagination(items)`'s pager: the list mounts only the rows on screen of the pager's part, and beneath it draws the
|
|
18
|
+
* control in the project's style. A new page starts at its top; with infinite scroll, scrolling inside the list loads
|
|
19
|
+
* more. For a long page (logs, feeds) the user asked to page differently, pass the size to the hook
|
|
20
|
+
* (`usePagination(rows, { pageSize: 100 })`) with its override note.
|
|
21
|
+
*/
|
|
22
|
+
pager?: Pager<unknown>;
|
|
13
23
|
}
|
|
14
|
-
declare function VirtualizedList<T>({ items, estimateSize, renderItem, overscan, height, className, ref, }: VirtualizedListProps<T>): react_jsx_runtime.JSX.Element;
|
|
24
|
+
declare function VirtualizedList<T>({ items: allItems, estimateSize, renderItem, overscan, height, className, ref, pager, }: VirtualizedListProps<T>): react_jsx_runtime.JSX.Element;
|
|
15
25
|
|
|
16
26
|
export { VirtualizedList };
|
package/dist/virtualized.js
CHANGED
|
@@ -1,25 +1,65 @@
|
|
|
1
|
+
import { useWholeListCheck, pagerPart, Pagination } from './chunk-2RV23OBK.js';
|
|
2
|
+
import './chunk-7TOJ4G7S.js';
|
|
3
|
+
import './chunk-RT7RYGC3.js';
|
|
4
|
+
import './chunk-CXMV7SXJ.js';
|
|
5
|
+
import './chunk-7Q46JXSK.js';
|
|
6
|
+
import { useLatest } from './chunk-WH2FUFAC.js';
|
|
7
|
+
import './chunk-4LDYR7QP.js';
|
|
1
8
|
import { cn } from './chunk-M2QRNBDQ.js';
|
|
2
|
-
import { useRef } from 'react';
|
|
9
|
+
import { useRef, useEffect } from 'react';
|
|
3
10
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
4
|
-
import { jsx } from 'react/jsx-runtime';
|
|
11
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
12
|
|
|
6
13
|
function VirtualizedList({
|
|
7
|
-
items,
|
|
14
|
+
items: allItems,
|
|
8
15
|
estimateSize,
|
|
9
16
|
renderItem,
|
|
10
17
|
overscan = 5,
|
|
11
18
|
height = 400,
|
|
12
19
|
className,
|
|
13
|
-
ref
|
|
20
|
+
ref,
|
|
21
|
+
pager
|
|
14
22
|
}) {
|
|
15
23
|
const parentRef = useRef(null);
|
|
24
|
+
const endRef = useRef(null);
|
|
25
|
+
useWholeListCheck("VirtualizedList", allItems.length, pager);
|
|
26
|
+
const items = pagerPart(allItems, pager);
|
|
27
|
+
const offset = pager ? Math.max(0, pager.range[0] - 1) : 0;
|
|
16
28
|
const virtualizer = useVirtualizer({
|
|
17
29
|
count: items.length,
|
|
18
30
|
getScrollElement: () => parentRef.current,
|
|
19
|
-
estimateSize,
|
|
31
|
+
estimateSize: (index) => estimateSize(offset + index),
|
|
32
|
+
// a row is known by its place in the whole list, so page 2's third row isn't page 1's (the review of 30 September:
|
|
33
|
+
// an entry opened on page 1 showed opened on page 2, and page 1's heights carried over)
|
|
34
|
+
getItemKey: (index) => offset + index,
|
|
20
35
|
overscan
|
|
21
36
|
});
|
|
22
|
-
|
|
37
|
+
const page = pager ? pager.page : null;
|
|
38
|
+
const pageBefore = useRef(page);
|
|
39
|
+
const numbered = pager?.style === "pagination";
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const before = pageBefore.current;
|
|
42
|
+
pageBefore.current = page;
|
|
43
|
+
if (page === null || !parentRef.current) return;
|
|
44
|
+
if (numbered ? page !== before : before !== null && page < before) parentRef.current.scrollTop = 0;
|
|
45
|
+
}, [page, numbered]);
|
|
46
|
+
const loadMore = useLatest(pager?.loadMore);
|
|
47
|
+
const growing = pager?.style === "infinite-scroll" && pager.hasMore;
|
|
48
|
+
const loadedAt = useRef(-1);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const root = parentRef.current;
|
|
51
|
+
const end = endRef.current;
|
|
52
|
+
if (loadedAt.current !== items.length) loadedAt.current = -1;
|
|
53
|
+
if (!growing || !root || !end || typeof IntersectionObserver === "undefined") return;
|
|
54
|
+
const observer = new IntersectionObserver((entries) => {
|
|
55
|
+
if (!entries.some((e) => e.isIntersecting) || loadedAt.current === items.length) return;
|
|
56
|
+
loadedAt.current = items.length;
|
|
57
|
+
loadMore.current?.();
|
|
58
|
+
}, { root, rootMargin: "200px 0px" });
|
|
59
|
+
observer.observe(end);
|
|
60
|
+
return () => observer.disconnect();
|
|
61
|
+
}, [growing, items.length, loadMore]);
|
|
62
|
+
const list = /* @__PURE__ */ jsxs(
|
|
23
63
|
"div",
|
|
24
64
|
{
|
|
25
65
|
ref: (node) => {
|
|
@@ -27,27 +67,36 @@ function VirtualizedList({
|
|
|
27
67
|
if (typeof ref === "function") ref(node);
|
|
28
68
|
else if (ref) ref.current = node;
|
|
29
69
|
},
|
|
70
|
+
"data-ds-list": pager ? "" : void 0,
|
|
30
71
|
className: cn(
|
|
31
72
|
"overflow-auto",
|
|
32
73
|
"rounded-[var(--ds-radius-md)]",
|
|
33
74
|
"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
34
75
|
"bg-[var(--ds-bg-surface)]",
|
|
35
|
-
className
|
|
76
|
+
!pager && className
|
|
36
77
|
),
|
|
37
78
|
style: { height },
|
|
38
|
-
children:
|
|
39
|
-
"div",
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsx("div", { className: "relative w-full", style: { height: virtualizer.getTotalSize() }, children: virtualizer.getVirtualItems().map((virtualItem) => /* @__PURE__ */ jsx(
|
|
81
|
+
"div",
|
|
82
|
+
{
|
|
83
|
+
"data-index": virtualItem.index,
|
|
84
|
+
ref: virtualizer.measureElement,
|
|
85
|
+
className: "absolute top-0 left-0 w-full",
|
|
86
|
+
style: { transform: `translateY(${virtualItem.start}px)` },
|
|
87
|
+
children: renderItem(items[virtualItem.index], offset + virtualItem.index, virtualItem)
|
|
88
|
+
},
|
|
89
|
+
virtualItem.key
|
|
90
|
+
)) }),
|
|
91
|
+
growing && /* @__PURE__ */ jsx("div", { ref: endRef, "data-load-more-trigger": "", "aria-hidden": "true", className: "h-px w-full" })
|
|
92
|
+
]
|
|
49
93
|
}
|
|
50
94
|
);
|
|
95
|
+
if (!pager) return list;
|
|
96
|
+
return /* @__PURE__ */ jsxs("div", { "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
|
|
97
|
+
list,
|
|
98
|
+
allItems.length > 0 && /* @__PURE__ */ jsx(Pagination, { ...pager.props, mode: pager.style === "infinite-scroll" ? "load-more" : pager.props.mode })
|
|
99
|
+
] });
|
|
51
100
|
}
|
|
52
101
|
|
|
53
102
|
export { VirtualizedList };
|
package/dist/virtualized.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../showcase/src/components/VirtualizedList.tsx"],"names":[],"mappings":";;;;;AAcO,SAAS,eAAA,CAAmB;AAAA,EACjC,KAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA,GAAW,CAAA;AAAA,EACX,MAAA,GAAS,GAAA;AAAA,EACT,SAAA;AAAA,EACA;AACF,CAAA,EAA4B;AAC1B,EAAA,MAAM,SAAA,GAAY,OAAuB,IAAI,CAAA;AAG7C,EAAA,MAAM,cAAc,cAAA,CAAe;AAAA,IACjC,OAAO,KAAA,CAAM,MAAA;AAAA,IACb,gBAAA,EAAkB,MAAM,SAAA,CAAU,OAAA;AAAA,IAClC,YAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,CAAC,IAAA,KAAS;AACb,QAAC,UAA4D,OAAA,GAAU,IAAA;AACvE,QAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,GAAA,CAAI,IAAI,CAAA;AAAA,aAAA,IAC9B,GAAA,EAAM,GAAA,CAAsD,OAAA,GAAU,IAAA;AAAA,MACjF,CAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,eAAA;AAAA,QACA,+BAAA;AAAA,QACA,yEAAA;AAAA,QACA,2BAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,MAEhB,8BAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iBAAA,EAAkB,KAAA,EAAO,EAAE,MAAA,EAAQ,WAAA,CAAY,YAAA,EAAa,IACxE,QAAA,EAAA,WAAA,CAAY,eAAA,EAAgB,CAAE,GAAA,CAAI,CAAC,WAAA,qBAClC,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UAEC,cAAY,WAAA,CAAY,KAAA;AAAA,UACxB,KAAK,WAAA,CAAY,cAAA;AAAA,UACjB,SAAA,EAAU,8BAAA;AAAA,UACV,OAAO,EAAE,SAAA,EAAW,CAAA,WAAA,EAAc,WAAA,CAAY,KAAK,CAAA,GAAA,CAAA,EAAM;AAAA,UAExD,qBAAW,KAAA,CAAM,WAAA,CAAY,KAAK,CAAA,EAAG,WAAA,CAAY,OAAO,WAAW;AAAA,SAAA;AAAA,QAN/D,WAAA,CAAY;AAAA,OAQpB,CAAA,EACH;AAAA;AAAA,GACF;AAEJ","file":"virtualized.js","sourcesContent":["import { useRef, type ReactNode, type Ref } from \"react\";\nimport { useVirtualizer, type VirtualItem } from \"@tanstack/react-virtual\";\nimport { cn } from \"../lib/utils\";\n\nexport interface VirtualizedListProps<T> {\n items: T[];\n estimateSize: (index: number) => number;\n renderItem: (item: T, index: number, virtualItem: VirtualItem) => ReactNode;\n overscan?: number;\n height?: number | string;\n className?: string;\n ref?: Ref<HTMLDivElement>;\n}\n\nexport function VirtualizedList<T>({\n items,\n estimateSize,\n renderItem,\n overscan = 5,\n height = 400,\n className,\n ref,\n}: VirtualizedListProps<T>) {\n const parentRef = useRef<HTMLDivElement>(null);\n\n // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Virtual is not compiler-compatible (informational; the project does not use the React Compiler)\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => parentRef.current,\n estimateSize,\n overscan,\n });\n\n return (\n <div\n ref={(node) => {\n (parentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }}\n className={cn(\n \"overflow-auto\",\n \"rounded-[var(--ds-radius-md)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)]\",\n className,\n )}\n style={{ height }}\n >\n <div className=\"relative w-full\" style={{ height: virtualizer.getTotalSize() }}>\n {virtualizer.getVirtualItems().map((virtualItem) => (\n <div\n key={virtualItem.key}\n data-index={virtualItem.index}\n ref={virtualizer.measureElement}\n className=\"absolute top-0 left-0 w-full\"\n style={{ transform: `translateY(${virtualItem.start}px)` }}\n >\n {renderItem(items[virtualItem.index], virtualItem.index, virtualItem)}\n </div>\n ))}\n </div>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../showcase/src/components/VirtualizedList.tsx"],"names":[],"mappings":"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","file":"virtualized.js","sourcesContent":["import { useEffect, useRef, type ReactNode, type Ref } from \"react\";\nimport { useVirtualizer, type VirtualItem } from \"@tanstack/react-virtual\";\nimport { cn } from \"../lib/utils\";\nimport { useLatest } from \"../lib/use-latest\";\nimport { Pagination } from \"./Pagination\";\nimport { pagerPart, useWholeListCheck, type Pager } from \"../lib/use-pagination\";\n\nexport interface VirtualizedListProps<T> {\n items: T[];\n estimateSize: (index: number) => number;\n /** An item, with its index in `items` (the whole list, when the list is paged). */\n renderItem: (item: T, index: number, virtualItem: VirtualItem) => ReactNode;\n overscan?: number;\n height?: number | string;\n className?: string;\n ref?: Ref<HTMLDivElement>;\n /**\n * The list's pager (Sprint 25.13 A49): paging and drawing only the rows on screen combine. Pass every item and\n * `usePagination(items)`'s pager: the list mounts only the rows on screen of the pager's part, and beneath it draws the\n * control in the project's style. A new page starts at its top; with infinite scroll, scrolling inside the list loads\n * more. For a long page (logs, feeds) the user asked to page differently, pass the size to the hook\n * (`usePagination(rows, { pageSize: 100 })`) with its override note.\n */\n pager?: Pager<unknown>;\n}\n\nexport function VirtualizedList<T>({\n items: allItems,\n estimateSize,\n renderItem,\n overscan = 5,\n height = 400,\n className,\n ref,\n pager,\n}: VirtualizedListProps<T>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const endRef = useRef<HTMLDivElement>(null);\n useWholeListCheck(\"VirtualizedList\", allItems.length, pager);\n const items = pagerPart(allItems, pager);\n const offset = pager ? Math.max(0, pager.range[0] - 1) : 0;\n\n // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Virtual is not compiler-compatible (informational; the project does not use the React Compiler)\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => parentRef.current,\n estimateSize: (index) => estimateSize(offset + index),\n // a row is known by its place in the whole list, so page 2's third row isn't page 1's (the review of 30 September:\n // an entry opened on page 1 showed opened on page 2, and page 1's heights carried over)\n getItemKey: (index) => offset + index,\n overscan,\n });\n\n // A49: a new page starts at its top, and so does a growing list that starts again at its first page (a new order, a\n // search): left scrolled to its end, it loaded the next page at once (the review of 30 September)\n const page = pager ? pager.page : null;\n const pageBefore = useRef(page);\n const numbered = pager?.style === \"pagination\";\n useEffect(() => {\n const before = pageBefore.current;\n pageBefore.current = page;\n if (page === null || !parentRef.current) return;\n if (numbered ? page !== before : before !== null && page < before) parentRef.current.scrollTop = 0;\n }, [page, numbered]);\n\n // A49: with infinite scroll, the trigger sits at the end of the list's own content and watches the list's scroller\n // (it watched the viewport, so scrolling inside the list never loaded more); it loads once per growth of the list\n const loadMore = useLatest(pager?.loadMore);\n const growing = pager?.style === \"infinite-scroll\" && pager.hasMore;\n const loadedAt = useRef(-1);\n useEffect(() => {\n const root = parentRef.current;\n const end = endRef.current;\n // the list's length changed since the last load (it grew, or started again at page 1): it may load again (the\n // review of 30 September: after a reset to the first page it never loaded again)\n if (loadedAt.current !== items.length) loadedAt.current = -1;\n if (!growing || !root || !end || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver((entries) => {\n if (!entries.some((e) => e.isIntersecting) || loadedAt.current === items.length) return;\n loadedAt.current = items.length;\n loadMore.current?.();\n }, { root, rootMargin: \"200px 0px\" });\n observer.observe(end);\n return () => observer.disconnect();\n }, [growing, items.length, loadMore]);\n\n const list = (\n <div\n ref={(node) => {\n (parentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }}\n data-ds-list={pager ? \"\" : undefined}\n className={cn(\n \"overflow-auto\",\n \"rounded-[var(--ds-radius-md)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)]\",\n !pager && className,\n )}\n style={{ height }}\n >\n <div className=\"relative w-full\" style={{ height: virtualizer.getTotalSize() }}>\n {virtualizer.getVirtualItems().map((virtualItem) => (\n <div\n key={virtualItem.key}\n data-index={virtualItem.index}\n ref={virtualizer.measureElement}\n className=\"absolute top-0 left-0 w-full\"\n style={{ transform: `translateY(${virtualItem.start}px)` }}\n >\n {renderItem(items[virtualItem.index], offset + virtualItem.index, virtualItem)}\n </div>\n ))}\n </div>\n {growing && <div ref={endRef} data-load-more-trigger=\"\" aria-hidden=\"true\" className=\"h-px w-full\" />}\n </div>\n );\n // A3: the pager's control is the list's next sibling, as <Pagination {...pager.props} /> would be. With infinite\n // scroll it is the Load more button alone, for keyboard users: the list's own trigger loads as it scrolls (A49). The\n // two are one element, which takes the className, so in a grid or a flex row they stay one item, and the list keeps\n // its element while its items come and go (the review of 30 September).\n if (!pager) return list;\n return (\n <div data-gs-paged=\"\" className={cn(\"flex flex-col min-w-0\", className)}>\n {list}\n {allItems.length > 0 && <Pagination {...pager.props} mode={pager.style === \"infinite-scroll\" ? \"load-more\" : pager.props.mode} />}\n </div>\n );\n}\n"]}
|
package/package.json
CHANGED