@lattice-php/lattice 0.12.0 → 0.13.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.
@@ -0,0 +1,3 @@
1
+ import { RendererComponent } from '../types';
2
+ declare const CollapsibleComponent: RendererComponent<"collapsible">;
3
+ export default CollapsibleComponent;
@@ -0,0 +1,61 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import { Icon } from "../../icons/sprite.js";
3
+ import { Renderer } from "../renderer.js";
4
+ import { nodeIdentity, prefixedTestId } from "../test-id.js";
5
+ import { toNodes } from "../nodes.js";
6
+ import { useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region resources/js/core/components/collapsible.tsx
9
+ function readStored(key, remember, fallback) {
10
+ if (!remember || typeof window === "undefined") return fallback;
11
+ const stored = window.localStorage.getItem(key);
12
+ return stored === null ? fallback : stored === "true";
13
+ }
14
+ var CollapsibleComponent = ({ children, node }) => {
15
+ const rememberState = node.props.rememberState === true;
16
+ const trigger = toNodes(node.props.trigger);
17
+ const identity = nodeIdentity(node);
18
+ const storageKey = `lattice:collapsible:${identity ?? "default"}`;
19
+ const contentId = `${identity ?? "collapsible"}-content`;
20
+ const [open, setOpen] = useState(() => readStored(storageKey, rememberState, node.props.collapsed === false));
21
+ function toggle() {
22
+ setOpen((value) => {
23
+ const next = !value;
24
+ if (rememberState && typeof window !== "undefined") window.localStorage.setItem(storageKey, String(next));
25
+ return next;
26
+ });
27
+ }
28
+ return /* @__PURE__ */ jsxs("div", {
29
+ "data-lattice-component": identity,
30
+ children: [/* @__PURE__ */ jsxs("div", {
31
+ "aria-controls": contentId,
32
+ "aria-expanded": open,
33
+ "data-test": prefixedTestId("collapsible-toggle", identity) ?? "collapsible-toggle-default",
34
+ className: "flex w-full cursor-pointer items-center justify-between gap-4 rounded-lt-sm py-2 text-left text-lt-fg transition-colors hover:bg-lt-muted",
35
+ onClick: toggle,
36
+ onKeyDown: (event) => {
37
+ if (event.key === "Enter" || event.key === " ") {
38
+ event.preventDefault();
39
+ toggle();
40
+ }
41
+ },
42
+ role: "button",
43
+ tabIndex: 0,
44
+ children: [/* @__PURE__ */ jsx("div", {
45
+ className: "flex min-w-0 flex-1 items-center gap-2",
46
+ children: /* @__PURE__ */ jsx(Renderer, { nodes: trigger })
47
+ }), /* @__PURE__ */ jsx(Icon, {
48
+ name: "chevron-down",
49
+ className: cn("size-lt-icon-md shrink-0 text-lt-muted-fg transition-transform", !open && "-rotate-90")
50
+ })]
51
+ }), open && children && /* @__PURE__ */ jsx("div", {
52
+ id: contentId,
53
+ className: "flex flex-col gap-4 pt-2",
54
+ children
55
+ })]
56
+ });
57
+ };
58
+ //#endregion
59
+ export { CollapsibleComponent as default };
60
+
61
+ //# sourceMappingURL=collapsible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapsible.js","names":[],"sources":["../../../resources/js/core/components/collapsible.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport { nodeIdentity, prefixedTestId } from \"@lattice-php/lattice/core/test-id\";\nimport { toNodes } from \"@lattice-php/lattice/core/nodes\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nfunction readStored(key: string, remember: boolean, fallback: boolean): boolean {\n if (!remember || typeof window === \"undefined\") {\n return fallback;\n }\n\n const stored = window.localStorage.getItem(key);\n\n return stored === null ? fallback : stored === \"true\";\n}\n\nconst CollapsibleComponent: RendererComponent<\"collapsible\"> = ({ children, node }) => {\n const rememberState = node.props.rememberState === true;\n const trigger = toNodes(node.props.trigger);\n const identity = nodeIdentity(node);\n const storageKey = `lattice:collapsible:${identity ?? \"default\"}`;\n const contentId = `${identity ?? \"collapsible\"}-content`;\n\n const [open, setOpen] = useState(() =>\n readStored(storageKey, rememberState, node.props.collapsed === false),\n );\n\n function toggle(): void {\n setOpen((value) => {\n const next = !value;\n\n if (rememberState && typeof window !== \"undefined\") {\n window.localStorage.setItem(storageKey, String(next));\n }\n\n return next;\n });\n }\n\n return (\n <div data-lattice-component={identity}>\n <div\n aria-controls={contentId}\n aria-expanded={open}\n data-test={prefixedTestId(\"collapsible-toggle\", identity) ?? \"collapsible-toggle-default\"}\n className=\"flex w-full cursor-pointer items-center justify-between gap-4 rounded-lt-sm py-2 text-left text-lt-fg transition-colors hover:bg-lt-muted\"\n onClick={toggle}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n toggle();\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n <Renderer nodes={trigger} />\n </div>\n <Icon\n name=\"chevron-down\"\n className={cn(\n \"size-lt-icon-md shrink-0 text-lt-muted-fg transition-transform\",\n !open && \"-rotate-90\",\n )}\n />\n </div>\n\n {open && children && (\n <div id={contentId} className=\"flex flex-col gap-4 pt-2\">\n {children}\n </div>\n )}\n </div>\n );\n};\n\nexport default CollapsibleComponent;\n"],"mappings":";;;;;;;;AAQA,SAAS,WAAW,KAAa,UAAmB,UAA4B;CAC9E,IAAI,CAAC,YAAY,OAAO,WAAW,aACjC,OAAO;CAGT,MAAM,SAAS,OAAO,aAAa,QAAQ,GAAG;CAE9C,OAAO,WAAW,OAAO,WAAW,WAAW;AACjD;AAEA,IAAM,wBAA0D,EAAE,UAAU,WAAW;CACrF,MAAM,gBAAgB,KAAK,MAAM,kBAAkB;CACnD,MAAM,UAAU,QAAQ,KAAK,MAAM,OAAO;CAC1C,MAAM,WAAW,aAAa,IAAI;CAClC,MAAM,aAAa,uBAAuB,YAAY;CACtD,MAAM,YAAY,GAAG,YAAY,cAAc;CAE/C,MAAM,CAAC,MAAM,WAAW,eACtB,WAAW,YAAY,eAAe,KAAK,MAAM,cAAc,KAAK,CACtE;CAEA,SAAS,SAAe;EACtB,SAAS,UAAU;GACjB,MAAM,OAAO,CAAC;GAEd,IAAI,iBAAiB,OAAO,WAAW,aACrC,OAAO,aAAa,QAAQ,YAAY,OAAO,IAAI,CAAC;GAGtD,OAAO;EACT,CAAC;CACH;CAEA,OACE,qBAAC,OAAD;EAAK,0BAAwB;YAA7B,CACE,qBAAC,OAAD;GACE,iBAAe;GACf,iBAAe;GACf,aAAW,eAAe,sBAAsB,QAAQ,KAAK;GAC7D,WAAU;GACV,SAAS;GACT,YAAY,UAAU;IACpB,IAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;KAC9C,MAAM,eAAe;KACrB,OAAO;IACT;GACF;GACA,MAAK;GACL,UAAU;aAbZ,CAeE,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,UAAD,EAAU,OAAO,QAAU,CAAA;GACxB,CAAA,GACL,oBAAC,MAAD;IACE,MAAK;IACL,WAAW,GACT,kEACA,CAAC,QAAQ,YACX;GACD,CAAA,CACE;MAEJ,QAAQ,YACP,oBAAC,OAAD;GAAK,IAAI;GAAW,WAAU;GAC3B;EACE,CAAA,CAEJ;;AAET"}
@@ -2,6 +2,7 @@ import { createPlugin, eagerComponent, lazyComponent } from "../registry.js";
2
2
  import ButtonComponent from "./button.js";
3
3
  import BadgeComponent from "./badge.js";
4
4
  import CardComponent from "./card.js";
5
+ import CollapsibleComponent from "./collapsible.js";
5
6
  import FloatingPanelComponent from "./floating-panel.js";
6
7
  import FragmentComponent from "./fragment.js";
7
8
  import GridComponent from "./grid.js";
@@ -22,6 +23,7 @@ var coreComponents = createPlugin({
22
23
  button: eagerComponent(ButtonComponent),
23
24
  card: eagerComponent(CardComponent),
24
25
  chart: lazyComponent(() => import("./chart.js")),
26
+ collapsible: eagerComponent(CollapsibleComponent),
25
27
  "floating-panel": eagerComponent(FloatingPanelComponent),
26
28
  fragment: eagerComponent(FragmentComponent),
27
29
  grid: eagerComponent(GridComponent),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../resources/js/core/components/index.ts"],"sourcesContent":["import { createPlugin, eagerComponent, lazyComponent } from \"@lattice-php/lattice/core/registry\";\nimport type { RendererComponentModule } from \"@lattice-php/lattice/core/types\";\nimport BadgeComponent from \"./badge\";\nimport ButtonComponent from \"./button\";\nimport CardComponent from \"./card\";\nimport FloatingPanelComponent from \"./floating-panel\";\nimport FragmentComponent from \"./fragment\";\nimport GridComponent from \"./grid\";\nimport HeadingComponent from \"./heading\";\nimport IconComponent from \"./icon\";\nimport LinkComponent from \"./link\";\nimport ModalComponent from \"./modal\";\nimport RawBlockComponent from \"./raw-block\";\nimport SectionComponent from \"./section\";\nimport SegmentedControlComponent from \"./segmented-control\";\nimport StackComponent from \"./stack\";\nimport TabComponent, { TabsComponent } from \"./tabs\";\nimport TextComponent from \"./text\";\n\nexport const coreComponents = createPlugin({\n components: {\n badge: eagerComponent(BadgeComponent),\n button: eagerComponent(ButtonComponent),\n card: eagerComponent(CardComponent),\n chart: lazyComponent(\n () => import(\"./chart\") as unknown as Promise<RendererComponentModule<\"chart\">>,\n ),\n \"floating-panel\": eagerComponent(FloatingPanelComponent),\n fragment: eagerComponent(FragmentComponent),\n grid: eagerComponent(GridComponent),\n heading: eagerComponent(HeadingComponent),\n icon: eagerComponent(IconComponent),\n link: eagerComponent(LinkComponent),\n modal: eagerComponent(ModalComponent),\n \"raw-block\": eagerComponent(RawBlockComponent),\n section: eagerComponent(SectionComponent),\n \"segmented-control\": eagerComponent(SegmentedControlComponent),\n stack: eagerComponent(StackComponent),\n tab: eagerComponent(TabComponent),\n tabs: eagerComponent(TabsComponent),\n text: eagerComponent(TextComponent),\n },\n name: \"lattice/core\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,OAAO,eAAe,cAAc;EACpC,QAAQ,eAAe,eAAe;EACtC,MAAM,eAAe,aAAa;EAClC,OAAO,oBACC,OAAO,aACf;EACA,kBAAkB,eAAe,sBAAsB;EACvD,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;EACxC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,aAAa,eAAe,iBAAiB;EAC7C,SAAS,eAAe,gBAAgB;EACxC,qBAAqB,eAAe,yBAAyB;EAC7D,OAAO,eAAe,cAAc;EACpC,KAAK,eAAe,YAAY;EAChC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;CACpC;CACA,MAAM;AACR,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../resources/js/core/components/index.ts"],"sourcesContent":["import { createPlugin, eagerComponent, lazyComponent } from \"@lattice-php/lattice/core/registry\";\nimport type { RendererComponentModule } from \"@lattice-php/lattice/core/types\";\nimport BadgeComponent from \"./badge\";\nimport ButtonComponent from \"./button\";\nimport CardComponent from \"./card\";\nimport CollapsibleComponent from \"./collapsible\";\nimport FloatingPanelComponent from \"./floating-panel\";\nimport FragmentComponent from \"./fragment\";\nimport GridComponent from \"./grid\";\nimport HeadingComponent from \"./heading\";\nimport IconComponent from \"./icon\";\nimport LinkComponent from \"./link\";\nimport ModalComponent from \"./modal\";\nimport RawBlockComponent from \"./raw-block\";\nimport SectionComponent from \"./section\";\nimport SegmentedControlComponent from \"./segmented-control\";\nimport StackComponent from \"./stack\";\nimport TabComponent, { TabsComponent } from \"./tabs\";\nimport TextComponent from \"./text\";\n\nexport const coreComponents = createPlugin({\n components: {\n badge: eagerComponent(BadgeComponent),\n button: eagerComponent(ButtonComponent),\n card: eagerComponent(CardComponent),\n chart: lazyComponent(\n () => import(\"./chart\") as unknown as Promise<RendererComponentModule<\"chart\">>,\n ),\n collapsible: eagerComponent(CollapsibleComponent),\n \"floating-panel\": eagerComponent(FloatingPanelComponent),\n fragment: eagerComponent(FragmentComponent),\n grid: eagerComponent(GridComponent),\n heading: eagerComponent(HeadingComponent),\n icon: eagerComponent(IconComponent),\n link: eagerComponent(LinkComponent),\n modal: eagerComponent(ModalComponent),\n \"raw-block\": eagerComponent(RawBlockComponent),\n section: eagerComponent(SectionComponent),\n \"segmented-control\": eagerComponent(SegmentedControlComponent),\n stack: eagerComponent(StackComponent),\n tab: eagerComponent(TabComponent),\n tabs: eagerComponent(TabsComponent),\n text: eagerComponent(TextComponent),\n },\n name: \"lattice/core\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,OAAO,eAAe,cAAc;EACpC,QAAQ,eAAe,eAAe;EACtC,MAAM,eAAe,aAAa;EAClC,OAAO,oBACC,OAAO,aACf;EACA,aAAa,eAAe,oBAAoB;EAChD,kBAAkB,eAAe,sBAAsB;EACvD,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;EACxC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,aAAa,eAAe,iBAAiB;EAC7C,SAAS,eAAe,gBAAgB;EACxC,qBAAqB,eAAe,yBAAyB;EAC7D,OAAO,eAAe,cAAc;EACpC,KAAK,eAAe,YAAY;EAChC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;CACpC;CACA,MAAM;AACR,CAAC"}
@@ -153,8 +153,7 @@ function readStoredOverrides(storageKey, columns) {
153
153
  const raw = window.localStorage.getItem(storageKey);
154
154
  if (raw === null) return {};
155
155
  const stored = JSON.parse(raw);
156
- const columnKeys = columns.map((column) => column.key);
157
- if (!isStoredColumnWidths(stored) || !sameColumnKeys(stored.columns, columnKeys)) {
156
+ if (!isStoredColumnWidths(stored)) {
158
157
  removeStoredOverrides(storageKey);
159
158
  return {};
160
159
  }
@@ -197,9 +196,6 @@ function isStoredColumnWidths(value) {
197
196
  const record = value;
198
197
  return Array.isArray(record.columns) && record.columns.every((column) => typeof column === "string") && typeof record.overrides === "object" && record.overrides !== null && !Array.isArray(record.overrides);
199
198
  }
200
- function sameColumnKeys(stored, current) {
201
- return stored.length === current.length && stored.every((key, index) => key === current[index]);
202
- }
203
199
  function sanitizeOverrides(overrides, columns) {
204
200
  const next = {};
205
201
  for (const column of columns) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-column-resizing.js","names":[],"sources":["../../resources/js/core/use-column-resizing.ts"],"sourcesContent":["import type { HTMLAttributes, KeyboardEvent, PointerEvent } from \"react\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport {\n buildColumnGridTemplate,\n defaultColumnWidthPx,\n maxColumnWidthPx,\n minColumnWidthPx,\n type SizableColumn,\n} from \"./column-sizing\";\n\ntype DragState = {\n key: string;\n maxWidth: number;\n startWidth: number;\n startX: number;\n};\n\ntype ResizeHandleProps = HTMLAttributes<HTMLDivElement> & {\n \"aria-label\": string;\n \"aria-orientation\": \"vertical\";\n \"aria-valuemax\": number;\n \"aria-valuemin\": number;\n \"aria-valuenow\": number;\n role: \"separator\";\n tabIndex: number;\n};\n\ntype StoredColumnWidths = {\n columns: string[];\n overrides: Record<string, number>;\n};\n\nconst emptyTracks: string[] = [];\n\nexport function useColumnResizing({\n columnGapPx = 0,\n columns,\n enabled,\n leadingTracks = emptyTracks,\n showIndicator = false,\n storageKey,\n trailingTracks = emptyTracks,\n}: {\n columnGapPx?: number;\n columns: SizableColumn[];\n enabled: boolean;\n leadingTracks?: string[];\n showIndicator?: boolean;\n storageKey?: string;\n trailingTracks?: string[];\n}) {\n const columnKeys = useMemo(() => columns.map((column) => column.key), [columns]);\n const [overrides, setOverrides] = useState<Record<string, number | undefined>>(() =>\n readStoredOverrides(storageKey, columns),\n );\n const overridesRef = useRef(overrides);\n const drag = useRef<DragState | null>(null);\n\n const gridTemplateColumns = useMemo(\n () =>\n buildColumnGridTemplate({\n columns,\n leadingTracks,\n trailingTracks,\n overrides: enabled ? overrides : {},\n }),\n [columns, enabled, leadingTracks, overrides, trailingTracks],\n );\n\n const setColumnWidth = useCallback(\n (column: SizableColumn, width: number, maxWidth?: number) => {\n setOverrides((current) => {\n const next = {\n ...current,\n [column.key]: Math.min(\n maxWidth ?? maxColumnWidthPx(column),\n Math.max(minColumnWidthPx(column), width),\n ),\n };\n\n overridesRef.current = next;\n writeStoredOverrides(storageKey, columnKeys, next);\n\n return next;\n });\n },\n [columnKeys, storageKey],\n );\n\n const resetColumnWidth = useCallback(\n (column: SizableColumn) => {\n setOverrides((current) => {\n const next = { ...current };\n delete next[column.key];\n\n overridesRef.current = next;\n writeStoredOverrides(storageKey, columnKeys, next);\n\n return next;\n });\n },\n [columnKeys, storageKey],\n );\n\n const resetColumns = useCallback(() => {\n setOverrides({});\n overridesRef.current = {};\n writeStoredOverrides(storageKey, columnKeys, {});\n }, [columnKeys, storageKey]);\n\n const hasOverrides =\n enabled && Object.values(overrides).some((width) => typeof width === \"number\");\n\n const currentColumnWidth = useCallback(\n (column: SizableColumn): number =>\n overridesRef.current[column.key] ?? defaultColumnWidthPx(column),\n [],\n );\n\n const getResizeHandleProps = useCallback(\n (column: SizableColumn): ResizeHandleProps => {\n const max = maxColumnWidthPx(column);\n const min = minColumnWidthPx(column);\n const current = currentColumnWidth(column);\n const label = column.label ?? column.key;\n const indicatorClass = showIndicator ? \"after:bg-lt-border\" : \"after:bg-transparent\";\n\n const maxWidthForHandle = (handle: HTMLDivElement): number =>\n maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid: handle.parentElement?.parentElement,\n leadingTracks,\n trailingTracks,\n });\n\n const resizeBy = (handle: HTMLDivElement, delta: number): void =>\n setColumnWidth(column, current + delta, maxWidthForHandle(handle));\n\n return {\n \"aria-label\": `Resize ${label}`,\n \"aria-orientation\": \"vertical\",\n \"aria-valuemax\": max,\n \"aria-valuemin\": min,\n \"aria-valuenow\": current,\n className: `absolute inset-y-0 right-0 hidden w-2 cursor-col-resize touch-none items-stretch justify-center md:flex after:my-1 after:w-px ${indicatorClass} hover:after:bg-lt-border focus-visible:outline-none focus-visible:after:bg-lt-ring`,\n onDoubleClick: () => resetColumnWidth(column),\n onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const step = event.shiftKey ? 32 : 8;\n\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n resizeBy(event.currentTarget, -step);\n }\n\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n resizeBy(event.currentTarget, step);\n }\n\n if (event.key === \"Home\") {\n event.preventDefault();\n setColumnWidth(column, min);\n }\n\n if (event.key === \"End\") {\n event.preventDefault();\n const handleMax = maxWidthForHandle(event.currentTarget);\n setColumnWidth(column, handleMax, handleMax);\n }\n\n if (event.key === \"Enter\" || event.key === \"Escape\") {\n event.preventDefault();\n resetColumnWidth(column);\n }\n },\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const parentWidth = event.currentTarget.parentElement?.getBoundingClientRect().width ?? 0;\n drag.current = {\n key: column.key,\n maxWidth: maxWidthForHandle(event.currentTarget),\n startWidth: parentWidth > 0 ? parentWidth : current,\n startX: event.clientX,\n };\n event.currentTarget.setPointerCapture?.(event.pointerId);\n event.preventDefault();\n },\n onPointerMove: (event: PointerEvent<HTMLDivElement>) => {\n const active = drag.current;\n\n if (!enabled || active?.key !== column.key) {\n return;\n }\n\n setColumnWidth(\n column,\n active.startWidth + event.clientX - active.startX,\n active.maxWidth,\n );\n },\n onPointerUp: (event: PointerEvent<HTMLDivElement>) => {\n const active = drag.current;\n\n if (active?.key !== column.key) {\n return;\n }\n\n drag.current = null;\n event.currentTarget.releasePointerCapture?.(event.pointerId);\n },\n role: \"separator\",\n tabIndex: 0,\n };\n },\n [\n columnGapPx,\n columns,\n currentColumnWidth,\n enabled,\n leadingTracks,\n resetColumnWidth,\n setColumnWidth,\n showIndicator,\n trailingTracks,\n ],\n );\n\n return {\n getResizeHandleProps,\n gridTemplateColumns,\n hasOverrides,\n resetColumns,\n resetColumnWidth,\n };\n}\n\nfunction maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid,\n leadingTracks,\n trailingTracks,\n}: {\n column: SizableColumn;\n columnGapPx: number;\n columns: SizableColumn[];\n grid: Element | null | undefined;\n leadingTracks: string[];\n trailingTracks: string[];\n}): number {\n const gridWidth = grid?.getBoundingClientRect().width ?? 0;\n\n if (gridWidth <= 0) {\n return maxColumnWidthPx(column);\n }\n\n const utilityWidth = [...leadingTracks, ...trailingTracks].reduce(\n (sum, track) => sum + fixedTrackWidthPx(track),\n 0,\n );\n const siblingMinWidth = columns.reduce(\n (sum, sibling) => (sibling.key === column.key ? sum : sum + minColumnWidthPx(sibling)),\n 0,\n );\n const trackCount = columns.length + leadingTracks.length + trailingTracks.length;\n const gapWidth = Math.max(0, trackCount - 1) * columnGapPx;\n const available = gridWidth - utilityWidth - siblingMinWidth - gapWidth;\n\n return Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), available));\n}\n\nfunction readStoredOverrides(\n storageKey: string | undefined,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n if (!storageKey || typeof window === \"undefined\") {\n return {};\n }\n\n try {\n const raw = window.localStorage.getItem(storageKey);\n\n if (raw === null) {\n return {};\n }\n\n const stored = JSON.parse(raw) as unknown;\n const columnKeys = columns.map((column) => column.key);\n\n if (!isStoredColumnWidths(stored) || !sameColumnKeys(stored.columns, columnKeys)) {\n removeStoredOverrides(storageKey);\n\n return {};\n }\n\n const overrides = sanitizeOverrides(stored.overrides, columns);\n\n if (Object.keys(overrides).length === 0) {\n removeStoredOverrides(storageKey);\n }\n\n return overrides;\n } catch {\n removeStoredOverrides(storageKey);\n\n return {};\n }\n}\n\nfunction writeStoredOverrides(\n storageKey: string | undefined,\n columnKeys: string[],\n overrides: Record<string, number | undefined>,\n): void {\n if (!storageKey || typeof window === \"undefined\") {\n return;\n }\n\n const stored: Record<string, number> = {};\n const knownKeys = new Set(columnKeys);\n\n for (const [key, value] of Object.entries(overrides)) {\n if (knownKeys.has(key) && typeof value === \"number\" && Number.isFinite(value)) {\n stored[key] = value;\n }\n }\n\n if (Object.keys(stored).length === 0) {\n removeStoredOverrides(storageKey);\n\n return;\n }\n\n try {\n window.localStorage.setItem(\n storageKey,\n JSON.stringify({\n columns: columnKeys,\n overrides: stored,\n }),\n );\n } catch {\n return;\n }\n}\n\nfunction removeStoredOverrides(storageKey: string): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n return;\n }\n}\n\nfunction isStoredColumnWidths(value: unknown): value is StoredColumnWidths {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n\n return (\n Array.isArray(record.columns) &&\n record.columns.every((column) => typeof column === \"string\") &&\n typeof record.overrides === \"object\" &&\n record.overrides !== null &&\n !Array.isArray(record.overrides)\n );\n}\n\nfunction sameColumnKeys(stored: string[], current: string[]): boolean {\n return stored.length === current.length && stored.every((key, index) => key === current[index]);\n}\n\nfunction sanitizeOverrides(\n overrides: Record<string, unknown>,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n const next: Record<string, number | undefined> = {};\n\n for (const column of columns) {\n const value = overrides[column.key];\n\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n continue;\n }\n\n next[column.key] = Math.min(\n maxColumnWidthPx(column),\n Math.max(minColumnWidthPx(column), value),\n );\n }\n\n return next;\n}\n\nfunction fixedTrackWidthPx(track: string): number {\n const value = track.trim();\n const px = value.match(/^([0-9.]+)px$/);\n\n if (px) {\n return Number.parseFloat(px[1]);\n }\n\n const rem = value.match(/^([0-9.]+)rem$/);\n\n if (rem) {\n return Number.parseFloat(rem[1]) * rootFontSizePx();\n }\n\n return 0;\n}\n\nfunction rootFontSizePx(): number {\n if (typeof window === \"undefined\") {\n return 16;\n }\n\n const parsed = Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize);\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 16;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-column-resizing.js","names":[],"sources":["../../resources/js/core/use-column-resizing.ts"],"sourcesContent":["import type { HTMLAttributes, KeyboardEvent, PointerEvent } from \"react\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport {\n buildColumnGridTemplate,\n defaultColumnWidthPx,\n maxColumnWidthPx,\n minColumnWidthPx,\n type SizableColumn,\n} from \"./column-sizing\";\n\ntype DragState = {\n key: string;\n maxWidth: number;\n startWidth: number;\n startX: number;\n};\n\ntype ResizeHandleProps = HTMLAttributes<HTMLDivElement> & {\n \"aria-label\": string;\n \"aria-orientation\": \"vertical\";\n \"aria-valuemax\": number;\n \"aria-valuemin\": number;\n \"aria-valuenow\": number;\n role: \"separator\";\n tabIndex: number;\n};\n\ntype StoredColumnWidths = {\n columns: string[];\n overrides: Record<string, number>;\n};\n\nconst emptyTracks: string[] = [];\n\nexport function useColumnResizing({\n columnGapPx = 0,\n columns,\n enabled,\n leadingTracks = emptyTracks,\n showIndicator = false,\n storageKey,\n trailingTracks = emptyTracks,\n}: {\n columnGapPx?: number;\n columns: SizableColumn[];\n enabled: boolean;\n leadingTracks?: string[];\n showIndicator?: boolean;\n storageKey?: string;\n trailingTracks?: string[];\n}) {\n const columnKeys = useMemo(() => columns.map((column) => column.key), [columns]);\n const [overrides, setOverrides] = useState<Record<string, number | undefined>>(() =>\n readStoredOverrides(storageKey, columns),\n );\n const overridesRef = useRef(overrides);\n const drag = useRef<DragState | null>(null);\n\n const gridTemplateColumns = useMemo(\n () =>\n buildColumnGridTemplate({\n columns,\n leadingTracks,\n trailingTracks,\n overrides: enabled ? overrides : {},\n }),\n [columns, enabled, leadingTracks, overrides, trailingTracks],\n );\n\n const setColumnWidth = useCallback(\n (column: SizableColumn, width: number, maxWidth?: number) => {\n setOverrides((current) => {\n const next = {\n ...current,\n [column.key]: Math.min(\n maxWidth ?? maxColumnWidthPx(column),\n Math.max(minColumnWidthPx(column), width),\n ),\n };\n\n overridesRef.current = next;\n writeStoredOverrides(storageKey, columnKeys, next);\n\n return next;\n });\n },\n [columnKeys, storageKey],\n );\n\n const resetColumnWidth = useCallback(\n (column: SizableColumn) => {\n setOverrides((current) => {\n const next = { ...current };\n delete next[column.key];\n\n overridesRef.current = next;\n writeStoredOverrides(storageKey, columnKeys, next);\n\n return next;\n });\n },\n [columnKeys, storageKey],\n );\n\n const resetColumns = useCallback(() => {\n setOverrides({});\n overridesRef.current = {};\n writeStoredOverrides(storageKey, columnKeys, {});\n }, [columnKeys, storageKey]);\n\n const hasOverrides =\n enabled && Object.values(overrides).some((width) => typeof width === \"number\");\n\n const currentColumnWidth = useCallback(\n (column: SizableColumn): number =>\n overridesRef.current[column.key] ?? defaultColumnWidthPx(column),\n [],\n );\n\n const getResizeHandleProps = useCallback(\n (column: SizableColumn): ResizeHandleProps => {\n const max = maxColumnWidthPx(column);\n const min = minColumnWidthPx(column);\n const current = currentColumnWidth(column);\n const label = column.label ?? column.key;\n const indicatorClass = showIndicator ? \"after:bg-lt-border\" : \"after:bg-transparent\";\n\n const maxWidthForHandle = (handle: HTMLDivElement): number =>\n maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid: handle.parentElement?.parentElement,\n leadingTracks,\n trailingTracks,\n });\n\n const resizeBy = (handle: HTMLDivElement, delta: number): void =>\n setColumnWidth(column, current + delta, maxWidthForHandle(handle));\n\n return {\n \"aria-label\": `Resize ${label}`,\n \"aria-orientation\": \"vertical\",\n \"aria-valuemax\": max,\n \"aria-valuemin\": min,\n \"aria-valuenow\": current,\n className: `absolute inset-y-0 right-0 hidden w-2 cursor-col-resize touch-none items-stretch justify-center md:flex after:my-1 after:w-px ${indicatorClass} hover:after:bg-lt-border focus-visible:outline-none focus-visible:after:bg-lt-ring`,\n onDoubleClick: () => resetColumnWidth(column),\n onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const step = event.shiftKey ? 32 : 8;\n\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n resizeBy(event.currentTarget, -step);\n }\n\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n resizeBy(event.currentTarget, step);\n }\n\n if (event.key === \"Home\") {\n event.preventDefault();\n setColumnWidth(column, min);\n }\n\n if (event.key === \"End\") {\n event.preventDefault();\n const handleMax = maxWidthForHandle(event.currentTarget);\n setColumnWidth(column, handleMax, handleMax);\n }\n\n if (event.key === \"Enter\" || event.key === \"Escape\") {\n event.preventDefault();\n resetColumnWidth(column);\n }\n },\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => {\n if (!enabled) {\n return;\n }\n\n const parentWidth = event.currentTarget.parentElement?.getBoundingClientRect().width ?? 0;\n drag.current = {\n key: column.key,\n maxWidth: maxWidthForHandle(event.currentTarget),\n startWidth: parentWidth > 0 ? parentWidth : current,\n startX: event.clientX,\n };\n event.currentTarget.setPointerCapture?.(event.pointerId);\n event.preventDefault();\n },\n onPointerMove: (event: PointerEvent<HTMLDivElement>) => {\n const active = drag.current;\n\n if (!enabled || active?.key !== column.key) {\n return;\n }\n\n setColumnWidth(\n column,\n active.startWidth + event.clientX - active.startX,\n active.maxWidth,\n );\n },\n onPointerUp: (event: PointerEvent<HTMLDivElement>) => {\n const active = drag.current;\n\n if (active?.key !== column.key) {\n return;\n }\n\n drag.current = null;\n event.currentTarget.releasePointerCapture?.(event.pointerId);\n },\n role: \"separator\",\n tabIndex: 0,\n };\n },\n [\n columnGapPx,\n columns,\n currentColumnWidth,\n enabled,\n leadingTracks,\n resetColumnWidth,\n setColumnWidth,\n showIndicator,\n trailingTracks,\n ],\n );\n\n return {\n getResizeHandleProps,\n gridTemplateColumns,\n hasOverrides,\n resetColumns,\n resetColumnWidth,\n };\n}\n\nfunction maxColumnWidthForGrid({\n column,\n columnGapPx,\n columns,\n grid,\n leadingTracks,\n trailingTracks,\n}: {\n column: SizableColumn;\n columnGapPx: number;\n columns: SizableColumn[];\n grid: Element | null | undefined;\n leadingTracks: string[];\n trailingTracks: string[];\n}): number {\n const gridWidth = grid?.getBoundingClientRect().width ?? 0;\n\n if (gridWidth <= 0) {\n return maxColumnWidthPx(column);\n }\n\n const utilityWidth = [...leadingTracks, ...trailingTracks].reduce(\n (sum, track) => sum + fixedTrackWidthPx(track),\n 0,\n );\n const siblingMinWidth = columns.reduce(\n (sum, sibling) => (sibling.key === column.key ? sum : sum + minColumnWidthPx(sibling)),\n 0,\n );\n const trackCount = columns.length + leadingTracks.length + trailingTracks.length;\n const gapWidth = Math.max(0, trackCount - 1) * columnGapPx;\n const available = gridWidth - utilityWidth - siblingMinWidth - gapWidth;\n\n return Math.min(maxColumnWidthPx(column), Math.max(minColumnWidthPx(column), available));\n}\n\nfunction readStoredOverrides(\n storageKey: string | undefined,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n if (!storageKey || typeof window === \"undefined\") {\n return {};\n }\n\n try {\n const raw = window.localStorage.getItem(storageKey);\n\n if (raw === null) {\n return {};\n }\n\n const stored = JSON.parse(raw) as unknown;\n\n if (!isStoredColumnWidths(stored)) {\n removeStoredOverrides(storageKey);\n\n return {};\n }\n\n const overrides = sanitizeOverrides(stored.overrides, columns);\n\n if (Object.keys(overrides).length === 0) {\n removeStoredOverrides(storageKey);\n }\n\n return overrides;\n } catch {\n removeStoredOverrides(storageKey);\n\n return {};\n }\n}\n\nfunction writeStoredOverrides(\n storageKey: string | undefined,\n columnKeys: string[],\n overrides: Record<string, number | undefined>,\n): void {\n if (!storageKey || typeof window === \"undefined\") {\n return;\n }\n\n const stored: Record<string, number> = {};\n const knownKeys = new Set(columnKeys);\n\n for (const [key, value] of Object.entries(overrides)) {\n if (knownKeys.has(key) && typeof value === \"number\" && Number.isFinite(value)) {\n stored[key] = value;\n }\n }\n\n if (Object.keys(stored).length === 0) {\n removeStoredOverrides(storageKey);\n\n return;\n }\n\n try {\n window.localStorage.setItem(\n storageKey,\n JSON.stringify({\n columns: columnKeys,\n overrides: stored,\n }),\n );\n } catch {\n return;\n }\n}\n\nfunction removeStoredOverrides(storageKey: string): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n return;\n }\n}\n\nfunction isStoredColumnWidths(value: unknown): value is StoredColumnWidths {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n\n return (\n Array.isArray(record.columns) &&\n record.columns.every((column) => typeof column === \"string\") &&\n typeof record.overrides === \"object\" &&\n record.overrides !== null &&\n !Array.isArray(record.overrides)\n );\n}\n\nfunction sanitizeOverrides(\n overrides: Record<string, unknown>,\n columns: SizableColumn[],\n): Record<string, number | undefined> {\n const next: Record<string, number | undefined> = {};\n\n for (const column of columns) {\n const value = overrides[column.key];\n\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n continue;\n }\n\n next[column.key] = Math.min(\n maxColumnWidthPx(column),\n Math.max(minColumnWidthPx(column), value),\n );\n }\n\n return next;\n}\n\nfunction fixedTrackWidthPx(track: string): number {\n const value = track.trim();\n const px = value.match(/^([0-9.]+)px$/);\n\n if (px) {\n return Number.parseFloat(px[1]);\n }\n\n const rem = value.match(/^([0-9.]+)rem$/);\n\n if (rem) {\n return Number.parseFloat(rem[1]) * rootFontSizePx();\n }\n\n return 0;\n}\n\nfunction rootFontSizePx(): number {\n if (typeof window === \"undefined\") {\n return 16;\n }\n\n const parsed = Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize);\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 16;\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export type ToggleableColumn = {
2
+ key: string;
3
+ label?: string;
4
+ toggleable?: boolean | null;
5
+ hiddenByDefault?: boolean | null;
6
+ };
7
+ export declare function useColumnVisibility<TColumn extends ToggleableColumn>({ columns, storageKey, }: {
8
+ columns: TColumn[];
9
+ storageKey?: string;
10
+ }): {
11
+ hasHidden: boolean;
12
+ hasToggleableColumns: boolean;
13
+ isVisible: (column: ToggleableColumn) => boolean;
14
+ resetVisibility: () => void;
15
+ setColumnVisible: (key: string, visible: boolean) => void;
16
+ toggleableColumns: TColumn[];
17
+ visibleColumns: TColumn[];
18
+ };
@@ -0,0 +1,91 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ //#region resources/js/core/use-column-visibility.ts
3
+ function useColumnVisibility({ columns, storageKey }) {
4
+ const toggleableColumns = useMemo(() => columns.filter((column) => column.toggleable === true), [columns]);
5
+ const toggleableKeys = useMemo(() => toggleableColumns.map((column) => column.key), [toggleableColumns]);
6
+ const [overrides, setOverrides] = useState(() => readStoredVisibility(storageKey, toggleableKeys));
7
+ const isVisible = useCallback((column) => {
8
+ if (column.toggleable !== true) return true;
9
+ return overrides[column.key] ?? column.hiddenByDefault !== true;
10
+ }, [overrides]);
11
+ const visibleColumns = useMemo(() => columns.filter((column) => isVisible(column)), [columns, isVisible]);
12
+ const setColumnVisible = useCallback((key, visible) => {
13
+ setOverrides((current) => {
14
+ const next = {
15
+ ...current,
16
+ [key]: visible
17
+ };
18
+ writeStoredVisibility(storageKey, toggleableKeys, next);
19
+ return next;
20
+ });
21
+ }, [storageKey, toggleableKeys]);
22
+ const resetVisibility = useCallback(() => {
23
+ setOverrides({});
24
+ writeStoredVisibility(storageKey, toggleableKeys, {});
25
+ }, [storageKey, toggleableKeys]);
26
+ const hasToggleableColumns = toggleableColumns.length > 0;
27
+ return {
28
+ hasHidden: toggleableColumns.some((column) => !isVisible(column)),
29
+ hasToggleableColumns,
30
+ isVisible,
31
+ resetVisibility,
32
+ setColumnVisible,
33
+ toggleableColumns,
34
+ visibleColumns
35
+ };
36
+ }
37
+ function readStoredVisibility(storageKey, toggleableKeys) {
38
+ if (!storageKey || typeof window === "undefined") return {};
39
+ try {
40
+ const raw = window.localStorage.getItem(storageKey);
41
+ if (raw === null) return {};
42
+ const stored = JSON.parse(raw);
43
+ if (!isStoredColumnVisibility(stored)) {
44
+ removeStoredVisibility(storageKey);
45
+ return {};
46
+ }
47
+ const known = new Set(toggleableKeys);
48
+ const overrides = {};
49
+ for (const [key, value] of Object.entries(stored.overrides)) if (known.has(key) && typeof value === "boolean") overrides[key] = value;
50
+ if (Object.keys(overrides).length === 0) removeStoredVisibility(storageKey);
51
+ return overrides;
52
+ } catch {
53
+ removeStoredVisibility(storageKey);
54
+ return {};
55
+ }
56
+ }
57
+ function writeStoredVisibility(storageKey, toggleableKeys, overrides) {
58
+ if (!storageKey || typeof window === "undefined") return;
59
+ const known = new Set(toggleableKeys);
60
+ const stored = {};
61
+ for (const [key, value] of Object.entries(overrides)) if (known.has(key) && typeof value === "boolean") stored[key] = value;
62
+ if (Object.keys(stored).length === 0) {
63
+ removeStoredVisibility(storageKey);
64
+ return;
65
+ }
66
+ try {
67
+ window.localStorage.setItem(storageKey, JSON.stringify({
68
+ columns: toggleableKeys,
69
+ overrides: stored
70
+ }));
71
+ } catch {
72
+ return;
73
+ }
74
+ }
75
+ function removeStoredVisibility(storageKey) {
76
+ if (typeof window === "undefined") return;
77
+ try {
78
+ window.localStorage.removeItem(storageKey);
79
+ } catch {
80
+ return;
81
+ }
82
+ }
83
+ function isStoredColumnVisibility(value) {
84
+ if (typeof value !== "object" || value === null) return false;
85
+ const record = value;
86
+ return Array.isArray(record.columns) && record.columns.every((column) => typeof column === "string") && typeof record.overrides === "object" && record.overrides !== null && !Array.isArray(record.overrides);
87
+ }
88
+ //#endregion
89
+ export { useColumnVisibility };
90
+
91
+ //# sourceMappingURL=use-column-visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-column-visibility.js","names":[],"sources":["../../resources/js/core/use-column-visibility.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\n\nexport type ToggleableColumn = {\n key: string;\n label?: string;\n toggleable?: boolean | null;\n hiddenByDefault?: boolean | null;\n};\n\ntype StoredColumnVisibility = {\n columns: string[];\n overrides: Record<string, boolean>;\n};\n\nexport function useColumnVisibility<TColumn extends ToggleableColumn>({\n columns,\n storageKey,\n}: {\n columns: TColumn[];\n storageKey?: string;\n}) {\n const toggleableColumns = useMemo(\n () => columns.filter((column) => column.toggleable === true),\n [columns],\n );\n const toggleableKeys = useMemo(\n () => toggleableColumns.map((column) => column.key),\n [toggleableColumns],\n );\n\n const [overrides, setOverrides] = useState<Record<string, boolean>>(() =>\n readStoredVisibility(storageKey, toggleableKeys),\n );\n\n const isVisible = useCallback(\n (column: ToggleableColumn): boolean => {\n if (column.toggleable !== true) {\n return true;\n }\n\n return overrides[column.key] ?? column.hiddenByDefault !== true;\n },\n [overrides],\n );\n\n const visibleColumns = useMemo(\n () => columns.filter((column) => isVisible(column)),\n [columns, isVisible],\n );\n\n const setColumnVisible = useCallback(\n (key: string, visible: boolean) => {\n setOverrides((current) => {\n const next = { ...current, [key]: visible };\n writeStoredVisibility(storageKey, toggleableKeys, next);\n\n return next;\n });\n },\n [storageKey, toggleableKeys],\n );\n\n const resetVisibility = useCallback(() => {\n setOverrides({});\n writeStoredVisibility(storageKey, toggleableKeys, {});\n }, [storageKey, toggleableKeys]);\n\n const hasToggleableColumns = toggleableColumns.length > 0;\n const hasHidden = toggleableColumns.some((column) => !isVisible(column));\n\n return {\n hasHidden,\n hasToggleableColumns,\n isVisible,\n resetVisibility,\n setColumnVisible,\n toggleableColumns,\n visibleColumns,\n };\n}\n\nfunction readStoredVisibility(\n storageKey: string | undefined,\n toggleableKeys: string[],\n): Record<string, boolean> {\n if (!storageKey || typeof window === \"undefined\") {\n return {};\n }\n\n try {\n const raw = window.localStorage.getItem(storageKey);\n\n if (raw === null) {\n return {};\n }\n\n const stored = JSON.parse(raw) as unknown;\n\n if (!isStoredColumnVisibility(stored)) {\n removeStoredVisibility(storageKey);\n\n return {};\n }\n\n const known = new Set(toggleableKeys);\n const overrides: Record<string, boolean> = {};\n\n for (const [key, value] of Object.entries(stored.overrides)) {\n if (known.has(key) && typeof value === \"boolean\") {\n overrides[key] = value;\n }\n }\n\n if (Object.keys(overrides).length === 0) {\n removeStoredVisibility(storageKey);\n }\n\n return overrides;\n } catch {\n removeStoredVisibility(storageKey);\n\n return {};\n }\n}\n\nfunction writeStoredVisibility(\n storageKey: string | undefined,\n toggleableKeys: string[],\n overrides: Record<string, boolean>,\n): void {\n if (!storageKey || typeof window === \"undefined\") {\n return;\n }\n\n const known = new Set(toggleableKeys);\n const stored: Record<string, boolean> = {};\n\n for (const [key, value] of Object.entries(overrides)) {\n if (known.has(key) && typeof value === \"boolean\") {\n stored[key] = value;\n }\n }\n\n if (Object.keys(stored).length === 0) {\n removeStoredVisibility(storageKey);\n\n return;\n }\n\n try {\n window.localStorage.setItem(\n storageKey,\n JSON.stringify({ columns: toggleableKeys, overrides: stored }),\n );\n } catch {\n return;\n }\n}\n\nfunction removeStoredVisibility(storageKey: string): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n return;\n }\n}\n\nfunction isStoredColumnVisibility(value: unknown): value is StoredColumnVisibility {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n\n return (\n Array.isArray(record.columns) &&\n record.columns.every((column) => typeof column === \"string\") &&\n typeof record.overrides === \"object\" &&\n record.overrides !== null &&\n !Array.isArray(record.overrides)\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { ToggleableColumn } from '../../core/use-column-visibility';
2
+ export declare function ColumnVisibilityMenu({ columns, hasHidden, isVisible, onReset, onToggle, processing, visibleColumnCount, }: {
3
+ columns: ToggleableColumn[];
4
+ hasHidden: boolean;
5
+ isVisible: (column: ToggleableColumn) => boolean;
6
+ onReset: () => void;
7
+ onToggle: (key: string, visible: boolean) => void;
8
+ processing: boolean;
9
+ visibleColumnCount: number;
10
+ }): import("react").JSX.Element;
@@ -0,0 +1,53 @@
1
+ import { Icon } from "../../icons/sprite.js";
2
+ import { useT } from "../../i18n/instance.js";
3
+ import { Popover, PopoverContent, PopoverTrigger } from "../../core/components/popover.js";
4
+ import { Checkbox } from "../../core/components/checkbox.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region resources/js/table/components/column-visibility-menu.tsx
7
+ function ColumnVisibilityMenu({ columns, hasHidden, isVisible, onReset, onToggle, processing, visibleColumnCount }) {
8
+ const { t } = useT("lattice");
9
+ return /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
10
+ asChild: true,
11
+ children: /* @__PURE__ */ jsx("button", {
12
+ type: "button",
13
+ "aria-label": t("table.columns.label", "Columns"),
14
+ "data-test": "table-columns-menu",
15
+ className: "relative inline-flex size-7 shrink-0 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted hover:text-lt-fg disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50 data-[state=open]:bg-lt-muted data-[state=open]:text-lt-fg",
16
+ disabled: processing,
17
+ children: /* @__PURE__ */ jsx(Icon, {
18
+ name: "columns-3",
19
+ "aria-hidden": "true",
20
+ className: "size-lt-icon-md"
21
+ })
22
+ })
23
+ }), /* @__PURE__ */ jsx(PopoverContent, {
24
+ align: "end",
25
+ className: "w-64 p-4",
26
+ children: /* @__PURE__ */ jsxs("div", {
27
+ className: "grid gap-3",
28
+ children: [columns.map((column) => {
29
+ const visible = isVisible(column);
30
+ const lockedLast = visible && visibleColumnCount <= 1;
31
+ return /* @__PURE__ */ jsxs("label", {
32
+ className: "flex items-center gap-2 text-base text-lt-fg",
33
+ children: [/* @__PURE__ */ jsx(Checkbox, {
34
+ "data-test": `table-column-toggle-${column.key}`,
35
+ checked: visible,
36
+ disabled: lockedLast,
37
+ onCheckedChange: (next) => onToggle(column.key, next === true)
38
+ }), /* @__PURE__ */ jsx("span", { children: column.label ?? column.key })]
39
+ }, column.key);
40
+ }), hasHidden && /* @__PURE__ */ jsx("button", {
41
+ type: "button",
42
+ "data-test": "table-columns-reset",
43
+ className: "mt-1 justify-self-start text-sm text-lt-muted-fg hover:text-lt-fg",
44
+ onClick: onReset,
45
+ children: t("table.columns.reset", "Reset")
46
+ })]
47
+ })
48
+ })] });
49
+ }
50
+ //#endregion
51
+ export { ColumnVisibilityMenu };
52
+
53
+ //# sourceMappingURL=column-visibility-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-visibility-menu.js","names":[],"sources":["../../../resources/js/table/components/column-visibility-menu.tsx"],"sourcesContent":["import { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport type { ToggleableColumn } from \"@lattice-php/lattice/core/use-column-visibility\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/core/components/popover\";\n\nexport function ColumnVisibilityMenu({\n columns,\n hasHidden,\n isVisible,\n onReset,\n onToggle,\n processing,\n visibleColumnCount,\n}: {\n columns: ToggleableColumn[];\n hasHidden: boolean;\n isVisible: (column: ToggleableColumn) => boolean;\n onReset: () => void;\n onToggle: (key: string, visible: boolean) => void;\n processing: boolean;\n visibleColumnCount: number;\n}) {\n const { t } = useT(\"lattice\");\n const columnsLabel = t(\"table.columns.label\", \"Columns\");\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={columnsLabel}\n data-test=\"table-columns-menu\"\n className=\"relative inline-flex size-7 shrink-0 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted hover:text-lt-fg disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50 data-[state=open]:bg-lt-muted data-[state=open]:text-lt-fg\"\n disabled={processing}\n >\n <Icon name=\"columns-3\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-64 p-4\">\n <div className=\"grid gap-3\">\n {columns.map((column) => {\n const visible = isVisible(column);\n const lockedLast = visible && visibleColumnCount <= 1;\n\n return (\n <label key={column.key} className=\"flex items-center gap-2 text-base text-lt-fg\">\n <Checkbox\n data-test={`table-column-toggle-${column.key}`}\n checked={visible}\n disabled={lockedLast}\n onCheckedChange={(next) => onToggle(column.key, next === true)}\n />\n <span>{column.label ?? column.key}</span>\n </label>\n );\n })}\n {hasHidden && (\n <button\n type=\"button\"\n data-test=\"table-columns-reset\"\n className=\"mt-1 justify-self-start text-sm text-lt-muted-fg hover:text-lt-fg\"\n onClick={onReset}\n >\n {t(\"table.columns.reset\", \"Reset\")}\n </button>\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"mappings":";;;;;;AAUA,SAAgB,qBAAqB,EACnC,SACA,WACA,WACA,SACA,UACA,YACA,sBASC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAG5B,OACE,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,gBAAD;EAAgB,SAAA;YACd,oBAAC,UAAD;GACE,MAAK;GACL,cAPa,EAAE,uBAAuB,SAO1B;GACZ,aAAU;GACV,WAAU;GACV,UAAU;aAEV,oBAAC,MAAD;IAAM,MAAK;IAAY,eAAY;IAAO,WAAU;GAAmB,CAAA;EACjE,CAAA;CACM,CAAA,GAChB,oBAAC,gBAAD;EAAgB,OAAM;EAAM,WAAU;YACpC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,QAAQ,KAAK,WAAW;IACvB,MAAM,UAAU,UAAU,MAAM;IAChC,MAAM,aAAa,WAAW,sBAAsB;IAEpD,OACE,qBAAC,SAAD;KAAwB,WAAU;eAAlC,CACE,oBAAC,UAAD;MACE,aAAW,uBAAuB,OAAO;MACzC,SAAS;MACT,UAAU;MACV,kBAAkB,SAAS,SAAS,OAAO,KAAK,SAAS,IAAI;KAC9D,CAAA,GACD,oBAAC,QAAD,EAAA,UAAO,OAAO,SAAS,OAAO,IAAU,CAAA,CACnC;OARK,OAAO,GAQZ;GAEX,CAAC,GACA,aACC,oBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;IACV,SAAS;cAER,EAAE,uBAAuB,OAAO;GAC3B,CAAA,CAEP;;CACS,CAAA,CACT,EAAA,CAAA;AAEb"}
@@ -3,6 +3,7 @@ import { Icon } from "../../icons/sprite.js";
3
3
  import { useT } from "../../i18n/instance.js";
4
4
  import { nodeIdentity } from "../../core/test-id.js";
5
5
  import { useColumnResizing } from "../../core/use-column-resizing.js";
6
+ import { useColumnVisibility } from "../../core/use-column-visibility.js";
6
7
  import { alignJustifyItems, alignText } from "../align.js";
7
8
  import { getBulkActions } from "../bulk.js";
8
9
  import { flattenColumns, getRowActions, getRowKey } from "../payload.js";
@@ -13,6 +14,7 @@ import { Checkbox } from "../../core/components/checkbox.js";
13
14
  import { BulkBar } from "./bulk-bar.js";
14
15
  import { ColumnFilterControl } from "./column-filter-control.js";
15
16
  import { ColumnHeader } from "./column-header.js";
17
+ import { ColumnVisibilityMenu } from "./column-visibility-menu.js";
16
18
  import { FilterBar, FilterMenu } from "./filter-bar.js";
17
19
  import { FilterStackBar } from "./filter-stack-bar.js";
18
20
  import { TablePagination } from "./pagination.js";
@@ -34,6 +36,11 @@ var TableComponent = ({ node }) => {
34
36
  })), [rows]);
35
37
  const selection = useTableSelection(rowEntries.map((entry) => entry.key));
36
38
  const columnsByKey = useMemo(() => new Map(flattenColumns(columns).map((column) => [column.key, column])), [columns]);
39
+ const visibilityIdentity = nodeIdentity(node);
40
+ const { hasToggleableColumns, hasHidden, isVisible, resetVisibility, setColumnVisible, toggleableColumns, visibleColumns } = useColumnVisibility({
41
+ columns,
42
+ storageKey: visibilityIdentity ? `lattice:table-column-visibility:${visibilityIdentity}` : void 0
43
+ });
37
44
  const currentPage = pagination.currentPage ?? state.page;
38
45
  const lastPage = pagination.lastPage ?? currentPage;
39
46
  const mode = pagination.mode ?? "table";
@@ -41,7 +48,7 @@ var TableComponent = ({ node }) => {
41
48
  const hasNextPage = pagination.hasMore ?? currentPage < lastPage;
42
49
  const hasActions = rowEntries.some((entry) => entry.actions.length > 0);
43
50
  const striped = node.props?.striped === true;
44
- const hasFilters = columns.some((column) => column.filter?.enabled);
51
+ const hasFilters = visibleColumns.some((column) => column.filter?.enabled);
45
52
  const filterEntries = filters.map((clause, index) => ({
46
53
  clause,
47
54
  index
@@ -49,8 +56,8 @@ var TableComponent = ({ node }) => {
49
56
  const filterDefinitions = useMemo(() => Array.isArray(node.props?.filters) ? node.props.filters : [], [node.props?.filters]);
50
57
  const hasDedicatedFilters = filterDefinitions.length > 0;
51
58
  const hasActiveFilters = filters.length > 0 || Object.keys(tableFilters).length > 0;
52
- const hasTrailingUtility = hasActions || hasDedicatedFilters;
53
- const sizingColumns = useMemo(() => getTableSizingColumns(columns), [columns]);
59
+ const hasTrailingUtility = hasActions || hasDedicatedFilters || hasToggleableColumns;
60
+ const sizingColumns = useMemo(() => getTableSizingColumns(visibleColumns), [visibleColumns]);
54
61
  const utilityTracks = useMemo(() => getTableUtilityTracks(hasTrailingUtility, hasBulkActions), [hasTrailingUtility, hasBulkActions]);
55
62
  const resizingEnabled = node.props?.resizableColumns === true;
56
63
  const resizeStorageIdentity = nodeIdentity(node);
@@ -130,7 +137,7 @@ var TableComponent = ({ node }) => {
130
137
  onCheckedChange: () => selection.toggleAll()
131
138
  })
132
139
  }),
133
- columns.map((column, index) => /* @__PURE__ */ jsx(ColumnHeader, {
140
+ visibleColumns.map((column, index) => /* @__PURE__ */ jsx(ColumnHeader, {
134
141
  column,
135
142
  processing,
136
143
  resizeHandleProps: resizingEnabled ? getResizeHandleProps(sizingColumns[index]) : void 0,
@@ -140,16 +147,28 @@ var TableComponent = ({ node }) => {
140
147
  hasTrailingUtility && /* @__PURE__ */ jsxs("div", {
141
148
  className: "flex items-center justify-end gap-2 px-4 py-2 align-middle font-semibold text-lt-fg",
142
149
  role: "columnheader",
143
- children: [hasDedicatedFilters && /* @__PURE__ */ jsx(FilterMenu, {
144
- filters: filterDefinitions,
145
- values: tableFilters,
146
- processing,
147
- onChange: setTableFilter,
148
- onSearch: searchFilterOptions
149
- }), hasActions && /* @__PURE__ */ jsx("span", {
150
- className: "sr-only",
151
- children: node.props?.actionsLabel
152
- })]
150
+ children: [
151
+ hasDedicatedFilters && /* @__PURE__ */ jsx(FilterMenu, {
152
+ filters: filterDefinitions,
153
+ values: tableFilters,
154
+ processing,
155
+ onChange: setTableFilter,
156
+ onSearch: searchFilterOptions
157
+ }),
158
+ hasToggleableColumns && /* @__PURE__ */ jsx(ColumnVisibilityMenu, {
159
+ columns: toggleableColumns,
160
+ isVisible,
161
+ visibleColumnCount: visibleColumns.length,
162
+ hasHidden,
163
+ onToggle: setColumnVisible,
164
+ onReset: resetVisibility,
165
+ processing
166
+ }),
167
+ hasActions && /* @__PURE__ */ jsx("span", {
168
+ className: "sr-only",
169
+ children: node.props?.actionsLabel
170
+ })
171
+ ]
153
172
  })
154
173
  ]
155
174
  }), hasFilters && /* @__PURE__ */ jsxs("div", {
@@ -161,7 +180,7 @@ var TableComponent = ({ node }) => {
161
180
  className: "px-4 py-2",
162
181
  role: "cell"
163
182
  }),
164
- columns.map((column) => /* @__PURE__ */ jsx("div", {
183
+ visibleColumns.map((column) => /* @__PURE__ */ jsx("div", {
165
184
  className: "min-w-0 px-2 py-2",
166
185
  role: "cell",
167
186
  children: column.filter?.enabled && /* @__PURE__ */ jsx(ColumnFilterControl, {
@@ -213,7 +232,7 @@ var TableComponent = ({ node }) => {
213
232
  onCheckedChange: () => selection.toggle(key)
214
233
  })
215
234
  }),
216
- columns.map((column) => /* @__PURE__ */ jsxs("div", {
235
+ visibleColumns.map((column) => /* @__PURE__ */ jsxs("div", {
217
236
  className: cn("grid min-w-0 gap-1 overflow-hidden p-4 align-middle", alignText(column.align), alignJustifyItems(column.align)),
218
237
  role: "cell",
219
238
  children: [/* @__PURE__ */ jsx("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","names":[],"sources":["../../../resources/js/table/components/table.tsx"],"sourcesContent":["import { type ReactNode, useMemo } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/use-column-resizing\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { alignJustifyItems, alignText } from \"../align\";\nimport type { TableNode } from \"../types\";\nimport { getBulkActions } from \"../bulk\";\nimport { flattenColumns, getRowActions, getRowKey } from \"../payload\";\nimport {\n getQueryParams,\n getTableSizingColumns,\n getTableUtilityTracks,\n getVisiblePages,\n} from \"../query\";\nimport { useTable } from \"../use-table\";\nimport { useTableSelection } from \"../use-table-selection\";\nimport { BulkBar } from \"./bulk-bar\";\nimport { ColumnFilterControl } from \"./column-filter-control\";\nimport { ColumnHeader } from \"./column-header\";\nimport { FilterBar, FilterMenu } from \"./filter-bar\";\nimport { FilterStackBar } from \"./filter-stack-bar\";\nimport { TablePagination } from \"./pagination\";\nimport { SortBar } from \"./sort-bar\";\nimport { TableActionNode } from \"./table-action-node\";\nimport { ColumnCell } from \"./table-cell\";\n\nconst TableComponent = ({ node }: { children?: ReactNode; node: TableNode }) => {\n const { t } = useT(\"lattice\");\n const {\n columns,\n rows,\n pagination,\n state,\n filters,\n tableFilters,\n addFilter,\n updateFilter,\n removeFilter,\n replaceColumnFilters,\n setTableFilter,\n resetFilters,\n searchFilterOptions,\n processing,\n hasLoaded,\n infiniteLoaderRef,\n sort,\n clearSort,\n goToPage,\n loadMore,\n } = useTable(node);\n\n const bulkActions = useMemo(\n () => getBulkActions(node.props?.bulkActions),\n [node.props?.bulkActions],\n );\n const hasBulkActions = bulkActions.length > 0;\n const rowEntries = useMemo(\n () =>\n rows.map((row, index) => ({ row, actions: getRowActions(row), key: getRowKey(row, index) })),\n [rows],\n );\n const selection = useTableSelection(rowEntries.map((entry) => entry.key));\n\n const columnsByKey = useMemo(\n () => new Map(flattenColumns(columns).map((column) => [column.key, column])),\n [columns],\n );\n const currentPage = pagination.currentPage ?? state.page;\n const lastPage = pagination.lastPage ?? currentPage;\n const mode = pagination.mode ?? \"table\";\n const visiblePages = getVisiblePages(currentPage, lastPage);\n const hasNextPage = pagination.hasMore ?? currentPage < lastPage;\n const hasActions = rowEntries.some((entry) => entry.actions.length > 0);\n const striped = node.props?.striped === true;\n const hasFilters = columns.some((column) => column.filter?.enabled);\n const filterEntries = filters.map((clause, index) => ({ clause, index }));\n const filterDefinitions = useMemo(\n () => (Array.isArray(node.props?.filters) ? node.props.filters : []),\n [node.props?.filters],\n );\n const hasDedicatedFilters = filterDefinitions.length > 0;\n const hasActiveFilters = filters.length > 0 || Object.keys(tableFilters).length > 0;\n const hasTrailingUtility = hasActions || hasDedicatedFilters;\n const sizingColumns = useMemo(() => getTableSizingColumns(columns), [columns]);\n const utilityTracks = useMemo(\n () => getTableUtilityTracks(hasTrailingUtility, hasBulkActions),\n [hasTrailingUtility, hasBulkActions],\n );\n const resizingEnabled = node.props?.resizableColumns === true;\n const resizeStorageIdentity = nodeIdentity(node);\n const { getResizeHandleProps, gridTemplateColumns, hasOverrides, resetColumns } =\n useColumnResizing({\n columns: sizingColumns,\n enabled: resizingEnabled,\n leadingTracks: utilityTracks.leadingTracks,\n showIndicator: node.props?.resizeIndicator === true,\n storageKey:\n resizingEnabled && resizeStorageIdentity\n ? `lattice:table-columns:${resizeStorageIdentity}`\n : undefined,\n trailingTracks: utilityTracks.trailingTracks,\n });\n\n return (\n <div data-lattice-component={node.id} className=\"relative\">\n {hasOverrides && (\n <button\n aria-label={t(\"table.resetColumnWidths\", \"Reset column widths\")}\n className=\"absolute right-1 top-1 z-10 hidden rounded-lt-sm p-1 text-lt-muted-fg hover:text-lt-fg md:inline-flex\"\n data-test=\"table-reset-columns\"\n onClick={resetColumns}\n title={t(\"table.resetColumnWidths\", \"Reset column widths\")}\n type=\"button\"\n >\n <Icon name=\"rotate-ccw\" className=\"size-lt-icon-sm\" />\n </button>\n )}\n <div className=\"overflow-x-auto rounded-lt-sm border border-lt-border\">\n {hasDedicatedFilters && hasActiveFilters && (\n <FilterBar\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n hasActiveFilters={hasActiveFilters}\n onChange={setTableFilter}\n onReset={resetFilters}\n />\n )}\n {hasBulkActions && selection.active && (\n <BulkBar\n actions={bulkActions}\n selectedKeys={selection.selectedKeys}\n allMatching={selection.allMatching}\n total={pagination.total ?? undefined}\n query={getQueryParams(state)}\n canSelectAllMatching={\n selection.allVisibleSelected &&\n !selection.allMatching &&\n pagination.total != null &&\n pagination.total > selection.selectedKeys.length\n }\n onSelectAllMatching={selection.selectAllMatching}\n onCompleted={selection.clear}\n />\n )}\n {filters.length > 0 && (\n <FilterStackBar\n filters={filters}\n columnsByKey={columnsByKey}\n processing={processing}\n onRemove={removeFilter}\n />\n )}\n {state.sorts.length > 0 && (\n <SortBar\n columnsByKey={columnsByKey}\n state={state}\n processing={processing}\n onClear={clearSort}\n />\n )}\n <div className=\"min-w-full text-base\" role=\"table\">\n <div className=\"border-b border-lt-border bg-lt-muted/50\" role=\"rowgroup\">\n <div\n className=\"hidden min-w-full md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center px-4 py-3\" role=\"columnheader\">\n <Checkbox\n aria-label={t(\"table.selectAllRows\", \"Select all rows\")}\n data-test=\"select-all\"\n checked={selection.allSelected}\n onCheckedChange={() => selection.toggleAll()}\n />\n </div>\n )}\n {columns.map((column, index) => (\n <ColumnHeader\n column={column}\n key={column.key}\n processing={processing}\n resizeHandleProps={\n resizingEnabled ? getResizeHandleProps(sizingColumns[index]) : undefined\n }\n sort={sort}\n state={state}\n />\n ))}\n {hasTrailingUtility && (\n <div\n className=\"flex items-center justify-end gap-2 px-4 py-2 align-middle font-semibold text-lt-fg\"\n role=\"columnheader\"\n >\n {hasDedicatedFilters && (\n <FilterMenu\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n onChange={setTableFilter}\n onSearch={searchFilterOptions}\n />\n )}\n {hasActions && <span className=\"sr-only\">{node.props?.actionsLabel}</span>}\n </div>\n )}\n </div>\n {hasFilters && (\n <div\n className=\"hidden min-w-full border-t border-lt-border md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && <div className=\"px-4 py-2\" role=\"cell\" />}\n {columns.map((column) => (\n <div key={column.key} className=\"min-w-0 px-2 py-2\" role=\"cell\">\n {column.filter?.enabled && (\n <ColumnFilterControl\n column={column}\n clauses={filterEntries.filter((entry) => entry.clause.field === column.key)}\n processing={processing}\n onAdd={addFilter}\n onUpdate={updateFilter}\n onRemove={removeFilter}\n onReplace={replaceColumnFilters}\n onSearch={(query) => searchFilterOptions(column.key, query)}\n />\n )}\n </div>\n ))}\n {hasTrailingUtility && <div className=\"px-4 py-2\" role=\"cell\" />}\n </div>\n )}\n </div>\n <div role=\"rowgroup\">\n {!hasLoaded ? (\n <div className=\"p-4 text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{t(\"table.loading\", \"Loading rows...\")}</div>\n </div>\n ) : rowEntries.length === 0 ? (\n <div className=\"p-8 text-center text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{node.props?.emptyLabel}</div>\n </div>\n ) : (\n rowEntries.map(({ row, actions, key }) => {\n return (\n <div\n key={key}\n className={`grid grid-cols-1 border-b border-lt-border last:border-b-0 md:grid-cols-[var(--lattice-table-columns)] ${\n striped ? \"odd:bg-lt-muted/30\" : \"\"\n }`}\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center p-4\" role=\"cell\">\n <Checkbox\n aria-label={t(\"table.selectRow\", \"Select row {{key}}\", { key })}\n data-test={`select-row-${key}`}\n checked={selection.isSelected(key)}\n onCheckedChange={() => selection.toggle(key)}\n />\n </div>\n )}\n {columns.map((column) => (\n <div\n key={column.key}\n className={cn(\n \"grid min-w-0 gap-1 overflow-hidden p-4 align-middle\",\n alignText(column.align),\n alignJustifyItems(column.align),\n )}\n role=\"cell\"\n >\n <span\n aria-hidden=\"true\"\n className=\"text-xs font-medium text-lt-muted-fg md:hidden\"\n >\n {column.label}\n </span>\n <div\n data-slot=\"table-cell-content\"\n className=\"min-w-0 max-w-full overflow-hidden truncate\"\n >\n <ColumnCell column={column} row={row} />\n </div>\n </div>\n ))}\n {hasTrailingUtility && (\n <div\n className={cn(\n \"items-center justify-start gap-2 p-4 md:justify-end\",\n actions.length > 0 ? \"flex\" : \"hidden md:flex\",\n )}\n role=\"cell\"\n >\n {actions.map((action, actionIndex) => (\n <TableActionNode\n key={action.key ?? action.id ?? actionIndex}\n node={action}\n />\n ))}\n </div>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n {hasLoaded && (\n <TablePagination\n pagination={pagination}\n currentPage={currentPage}\n processing={processing}\n mode={mode}\n hasNextPage={hasNextPage}\n visiblePages={visiblePages}\n infiniteLoaderRef={infiniteLoaderRef}\n onPage={goToPage}\n onLoadMore={loadMore}\n />\n )}\n </div>\n </div>\n );\n};\n\nexport default TableComponent;\n"],"mappings":"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"}
1
+ {"version":3,"file":"table.js","names":[],"sources":["../../../resources/js/table/components/table.tsx"],"sourcesContent":["import { type ReactNode, useMemo } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/use-column-resizing\";\nimport { useColumnVisibility } from \"@lattice-php/lattice/core/use-column-visibility\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { alignJustifyItems, alignText } from \"../align\";\nimport type { TableNode } from \"../types\";\nimport { getBulkActions } from \"../bulk\";\nimport { flattenColumns, getRowActions, getRowKey } from \"../payload\";\nimport {\n getQueryParams,\n getTableSizingColumns,\n getTableUtilityTracks,\n getVisiblePages,\n} from \"../query\";\nimport { useTable } from \"../use-table\";\nimport { useTableSelection } from \"../use-table-selection\";\nimport { BulkBar } from \"./bulk-bar\";\nimport { ColumnFilterControl } from \"./column-filter-control\";\nimport { ColumnHeader } from \"./column-header\";\nimport { ColumnVisibilityMenu } from \"./column-visibility-menu\";\nimport { FilterBar, FilterMenu } from \"./filter-bar\";\nimport { FilterStackBar } from \"./filter-stack-bar\";\nimport { TablePagination } from \"./pagination\";\nimport { SortBar } from \"./sort-bar\";\nimport { TableActionNode } from \"./table-action-node\";\nimport { ColumnCell } from \"./table-cell\";\n\nconst TableComponent = ({ node }: { children?: ReactNode; node: TableNode }) => {\n const { t } = useT(\"lattice\");\n const {\n columns,\n rows,\n pagination,\n state,\n filters,\n tableFilters,\n addFilter,\n updateFilter,\n removeFilter,\n replaceColumnFilters,\n setTableFilter,\n resetFilters,\n searchFilterOptions,\n processing,\n hasLoaded,\n infiniteLoaderRef,\n sort,\n clearSort,\n goToPage,\n loadMore,\n } = useTable(node);\n\n const bulkActions = useMemo(\n () => getBulkActions(node.props?.bulkActions),\n [node.props?.bulkActions],\n );\n const hasBulkActions = bulkActions.length > 0;\n const rowEntries = useMemo(\n () =>\n rows.map((row, index) => ({ row, actions: getRowActions(row), key: getRowKey(row, index) })),\n [rows],\n );\n const selection = useTableSelection(rowEntries.map((entry) => entry.key));\n\n const columnsByKey = useMemo(\n () => new Map(flattenColumns(columns).map((column) => [column.key, column])),\n [columns],\n );\n const visibilityIdentity = nodeIdentity(node);\n const {\n hasToggleableColumns,\n hasHidden,\n isVisible,\n resetVisibility,\n setColumnVisible,\n toggleableColumns,\n visibleColumns,\n } = useColumnVisibility({\n columns,\n storageKey: visibilityIdentity\n ? `lattice:table-column-visibility:${visibilityIdentity}`\n : undefined,\n });\n const currentPage = pagination.currentPage ?? state.page;\n const lastPage = pagination.lastPage ?? currentPage;\n const mode = pagination.mode ?? \"table\";\n const visiblePages = getVisiblePages(currentPage, lastPage);\n const hasNextPage = pagination.hasMore ?? currentPage < lastPage;\n const hasActions = rowEntries.some((entry) => entry.actions.length > 0);\n const striped = node.props?.striped === true;\n const hasFilters = visibleColumns.some((column) => column.filter?.enabled);\n const filterEntries = filters.map((clause, index) => ({ clause, index }));\n const filterDefinitions = useMemo(\n () => (Array.isArray(node.props?.filters) ? node.props.filters : []),\n [node.props?.filters],\n );\n const hasDedicatedFilters = filterDefinitions.length > 0;\n const hasActiveFilters = filters.length > 0 || Object.keys(tableFilters).length > 0;\n const hasTrailingUtility = hasActions || hasDedicatedFilters || hasToggleableColumns;\n const sizingColumns = useMemo(() => getTableSizingColumns(visibleColumns), [visibleColumns]);\n const utilityTracks = useMemo(\n () => getTableUtilityTracks(hasTrailingUtility, hasBulkActions),\n [hasTrailingUtility, hasBulkActions],\n );\n const resizingEnabled = node.props?.resizableColumns === true;\n const resizeStorageIdentity = nodeIdentity(node);\n const { getResizeHandleProps, gridTemplateColumns, hasOverrides, resetColumns } =\n useColumnResizing({\n columns: sizingColumns,\n enabled: resizingEnabled,\n leadingTracks: utilityTracks.leadingTracks,\n showIndicator: node.props?.resizeIndicator === true,\n storageKey:\n resizingEnabled && resizeStorageIdentity\n ? `lattice:table-columns:${resizeStorageIdentity}`\n : undefined,\n trailingTracks: utilityTracks.trailingTracks,\n });\n\n return (\n <div data-lattice-component={node.id} className=\"relative\">\n {hasOverrides && (\n <button\n aria-label={t(\"table.resetColumnWidths\", \"Reset column widths\")}\n className=\"absolute right-1 top-1 z-10 hidden rounded-lt-sm p-1 text-lt-muted-fg hover:text-lt-fg md:inline-flex\"\n data-test=\"table-reset-columns\"\n onClick={resetColumns}\n title={t(\"table.resetColumnWidths\", \"Reset column widths\")}\n type=\"button\"\n >\n <Icon name=\"rotate-ccw\" className=\"size-lt-icon-sm\" />\n </button>\n )}\n <div className=\"overflow-x-auto rounded-lt-sm border border-lt-border\">\n {hasDedicatedFilters && hasActiveFilters && (\n <FilterBar\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n hasActiveFilters={hasActiveFilters}\n onChange={setTableFilter}\n onReset={resetFilters}\n />\n )}\n {hasBulkActions && selection.active && (\n <BulkBar\n actions={bulkActions}\n selectedKeys={selection.selectedKeys}\n allMatching={selection.allMatching}\n total={pagination.total ?? undefined}\n query={getQueryParams(state)}\n canSelectAllMatching={\n selection.allVisibleSelected &&\n !selection.allMatching &&\n pagination.total != null &&\n pagination.total > selection.selectedKeys.length\n }\n onSelectAllMatching={selection.selectAllMatching}\n onCompleted={selection.clear}\n />\n )}\n {filters.length > 0 && (\n <FilterStackBar\n filters={filters}\n columnsByKey={columnsByKey}\n processing={processing}\n onRemove={removeFilter}\n />\n )}\n {state.sorts.length > 0 && (\n <SortBar\n columnsByKey={columnsByKey}\n state={state}\n processing={processing}\n onClear={clearSort}\n />\n )}\n <div className=\"min-w-full text-base\" role=\"table\">\n <div className=\"border-b border-lt-border bg-lt-muted/50\" role=\"rowgroup\">\n <div\n className=\"hidden min-w-full md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center px-4 py-3\" role=\"columnheader\">\n <Checkbox\n aria-label={t(\"table.selectAllRows\", \"Select all rows\")}\n data-test=\"select-all\"\n checked={selection.allSelected}\n onCheckedChange={() => selection.toggleAll()}\n />\n </div>\n )}\n {visibleColumns.map((column, index) => (\n <ColumnHeader\n column={column}\n key={column.key}\n processing={processing}\n resizeHandleProps={\n resizingEnabled ? getResizeHandleProps(sizingColumns[index]) : undefined\n }\n sort={sort}\n state={state}\n />\n ))}\n {hasTrailingUtility && (\n <div\n className=\"flex items-center justify-end gap-2 px-4 py-2 align-middle font-semibold text-lt-fg\"\n role=\"columnheader\"\n >\n {hasDedicatedFilters && (\n <FilterMenu\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n onChange={setTableFilter}\n onSearch={searchFilterOptions}\n />\n )}\n {hasToggleableColumns && (\n <ColumnVisibilityMenu\n columns={toggleableColumns}\n isVisible={isVisible}\n visibleColumnCount={visibleColumns.length}\n hasHidden={hasHidden}\n onToggle={setColumnVisible}\n onReset={resetVisibility}\n processing={processing}\n />\n )}\n {hasActions && <span className=\"sr-only\">{node.props?.actionsLabel}</span>}\n </div>\n )}\n </div>\n {hasFilters && (\n <div\n className=\"hidden min-w-full border-t border-lt-border md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && <div className=\"px-4 py-2\" role=\"cell\" />}\n {visibleColumns.map((column) => (\n <div key={column.key} className=\"min-w-0 px-2 py-2\" role=\"cell\">\n {column.filter?.enabled && (\n <ColumnFilterControl\n column={column}\n clauses={filterEntries.filter((entry) => entry.clause.field === column.key)}\n processing={processing}\n onAdd={addFilter}\n onUpdate={updateFilter}\n onRemove={removeFilter}\n onReplace={replaceColumnFilters}\n onSearch={(query) => searchFilterOptions(column.key, query)}\n />\n )}\n </div>\n ))}\n {hasTrailingUtility && <div className=\"px-4 py-2\" role=\"cell\" />}\n </div>\n )}\n </div>\n <div role=\"rowgroup\">\n {!hasLoaded ? (\n <div className=\"p-4 text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{t(\"table.loading\", \"Loading rows...\")}</div>\n </div>\n ) : rowEntries.length === 0 ? (\n <div className=\"p-8 text-center text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{node.props?.emptyLabel}</div>\n </div>\n ) : (\n rowEntries.map(({ row, actions, key }) => {\n return (\n <div\n key={key}\n className={`grid grid-cols-1 border-b border-lt-border last:border-b-0 md:grid-cols-[var(--lattice-table-columns)] ${\n striped ? \"odd:bg-lt-muted/30\" : \"\"\n }`}\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center p-4\" role=\"cell\">\n <Checkbox\n aria-label={t(\"table.selectRow\", \"Select row {{key}}\", { key })}\n data-test={`select-row-${key}`}\n checked={selection.isSelected(key)}\n onCheckedChange={() => selection.toggle(key)}\n />\n </div>\n )}\n {visibleColumns.map((column) => (\n <div\n key={column.key}\n className={cn(\n \"grid min-w-0 gap-1 overflow-hidden p-4 align-middle\",\n alignText(column.align),\n alignJustifyItems(column.align),\n )}\n role=\"cell\"\n >\n <span\n aria-hidden=\"true\"\n className=\"text-xs font-medium text-lt-muted-fg md:hidden\"\n >\n {column.label}\n </span>\n <div\n data-slot=\"table-cell-content\"\n className=\"min-w-0 max-w-full overflow-hidden truncate\"\n >\n <ColumnCell column={column} row={row} />\n </div>\n </div>\n ))}\n {hasTrailingUtility && (\n <div\n className={cn(\n \"items-center justify-start gap-2 p-4 md:justify-end\",\n actions.length > 0 ? \"flex\" : \"hidden md:flex\",\n )}\n role=\"cell\"\n >\n {actions.map((action, actionIndex) => (\n <TableActionNode\n key={action.key ?? action.id ?? actionIndex}\n node={action}\n />\n ))}\n </div>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n {hasLoaded && (\n <TablePagination\n pagination={pagination}\n currentPage={currentPage}\n processing={processing}\n mode={mode}\n hasNextPage={hasNextPage}\n visiblePages={visiblePages}\n infiniteLoaderRef={infiniteLoaderRef}\n onPage={goToPage}\n onLoadMore={loadMore}\n />\n )}\n </div>\n </div>\n );\n};\n\nexport default TableComponent;\n"],"mappings":"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"}
@@ -228,6 +228,11 @@ export type Choice = {
228
228
  export type CloseModalEffect = {
229
229
  readonly modal: string | null;
230
230
  };
231
+ export type Collapsible = {
232
+ collapsed: boolean;
233
+ rememberState: boolean;
234
+ trigger: Node[];
235
+ };
231
236
  export type Color = "default" | "muted" | "primary" | "success" | "info" | "warning" | "danger";
232
237
  export type ColumnAlign = "start" | "center" | "end";
233
238
  export type ColumnData = {
@@ -237,6 +242,8 @@ export type ColumnData = {
237
242
  readonly width: ColumnWidth;
238
243
  readonly align: ColumnAlign;
239
244
  readonly sortable: boolean | null;
245
+ readonly toggleable: boolean | null;
246
+ readonly hiddenByDefault: boolean | null;
240
247
  readonly filter: ColumnFilter | null;
241
248
  readonly columns: ColumnData[] | null;
242
249
  readonly props: Record<string, unknown> | null;
@@ -300,6 +307,11 @@ export type CoreNode = {
300
307
  type: "chart";
301
308
  key?: string;
302
309
  props: Chart;
310
+ } | {
311
+ type: "collapsible";
312
+ key?: string;
313
+ props: Collapsible;
314
+ schema?: Node[];
303
315
  } | {
304
316
  type: "floating-panel";
305
317
  key?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lattice-php/lattice",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Server-driven React components for Laravel and Inertia.",
5
5
  "license": "MIT",
6
6
  "author": "Manuel Christlieb <manuel@christlieb.eu>",