@ai-matrx/design-system 0.86.357 → 0.86.359
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/CHANGELOG.md +30 -2
- package/dist/carousel.js +6 -6
- package/dist/{chunk-T4D7WAV7.js → chunk-42GFQMLA.js} +2 -2
- package/dist/{chunk-POJ3RR3K.js → chunk-B5D3VIMC.js} +1 -1
- package/dist/{chunk-POJ3RR3K.js.map → chunk-B5D3VIMC.js.map} +1 -1
- package/dist/{chunk-FRJ4PPA2.js → chunk-CZGZV35G.js} +2 -2
- package/dist/{chunk-ACZALXD6.js → chunk-K3XWUACX.js} +3 -3
- package/dist/{chunk-TV2IDHPZ.js → chunk-K5ZQROWN.js} +4 -4
- package/dist/{chunk-U46XUQI7.js → chunk-KSCSH7AO.js} +16 -14
- package/dist/chunk-KSCSH7AO.js.map +1 -0
- package/dist/{chunk-MTE6N7XU.js → chunk-NFJ63LIH.js} +2 -2
- package/dist/{chunk-XZGYKKCE.js → chunk-OUQT72UL.js} +3 -3
- package/dist/{chunk-WUD23YTG.js → chunk-P56Y43RL.js} +6 -6
- package/dist/{chunk-GMNROZQJ.js → chunk-QTPW44IJ.js} +2 -2
- package/dist/{chunk-QA7EHFRX.js → chunk-T3FKFPCW.js} +5 -5
- package/dist/{chunk-X6ZRHJQP.js → chunk-T57DLONB.js} +20 -11
- package/dist/chunk-T57DLONB.js.map +1 -0
- package/dist/{chunk-SOCNWQMX.js → chunk-TEMAM5ET.js} +3 -3
- package/dist/{chunk-CH2NWSES.js → chunk-TIV6M7Q7.js} +3 -3
- package/dist/{chunk-6XNYYROM.js → chunk-WVEDW5VO.js} +2 -2
- package/dist/clipboard-fallback-dialog.js +2 -2
- package/dist/controls.cjs +19 -10
- package/dist/controls.cjs.map +1 -1
- package/dist/controls.js +6 -6
- package/dist/data-table/data-row-inspector.cjs +99 -11
- package/dist/data-table/data-row-inspector.cjs.map +1 -1
- package/dist/data-table/data-row-inspector.js +8 -7
- package/dist/data-table/data-row-window.cjs +99 -11
- package/dist/data-table/data-row-window.cjs.map +1 -1
- package/dist/data-table/data-row-window.js +9 -8
- package/dist/data-table/drill-chart.js +1 -1
- package/dist/data-table/host.cjs.map +1 -1
- package/dist/data-table/host.d.cts +6 -2
- package/dist/data-table/host.d.ts +6 -2
- package/dist/data-table/host.js +1 -1
- package/dist/data-table/index.cjs +39 -37
- package/dist/data-table/index.cjs.map +1 -1
- package/dist/data-table/index.d.cts +6 -2
- package/dist/data-table/index.d.ts +6 -2
- package/dist/data-table/index.js +19 -23
- package/dist/data-table/index.js.map +1 -1
- package/dist/data-table/pagination.cjs +19 -10
- package/dist/data-table/pagination.cjs.map +1 -1
- package/dist/data-table/pagination.js +4 -4
- package/dist/data-table/uuid-cell.cjs +99 -11
- package/dist/data-table/uuid-cell.cjs.map +1 -1
- package/dist/data-table/uuid-cell.js +5 -4
- package/dist/desktop.cjs +20 -11
- package/dist/desktop.cjs.map +1 -1
- package/dist/desktop.d.cts +1 -1
- package/dist/desktop.d.ts +1 -1
- package/dist/desktop.js +13 -13
- package/dist/desktop.js.map +1 -1
- package/dist/email-input-dialog.js +1 -1
- package/dist/index.cjs +31 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +20 -20
- package/dist/item.cjs +19 -10
- package/dist/item.cjs.map +1 -1
- package/dist/item.js +5 -5
- package/dist/lite-table.js +3 -3
- package/dist/menu-look.cjs +19 -10
- package/dist/menu-look.cjs.map +1 -1
- package/dist/menu-look.js +2 -2
- package/dist/record-list.cjs +19 -10
- package/dist/record-list.cjs.map +1 -1
- package/dist/record-list.js +6 -6
- package/dist/settings.js +3 -3
- package/dist/swipe-deck.cjs +11 -2
- package/dist/swipe-deck.cjs.map +1 -1
- package/dist/swipe-deck.js +12 -3
- package/dist/swipe-deck.js.map +1 -1
- package/dist/text-input-dialog.js +1 -1
- package/dist/writing-field.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-U46XUQI7.js.map +0 -1
- package/dist/chunk-X6ZRHJQP.js.map +0 -1
- /package/dist/{chunk-T4D7WAV7.js.map → chunk-42GFQMLA.js.map} +0 -0
- /package/dist/{chunk-FRJ4PPA2.js.map → chunk-CZGZV35G.js.map} +0 -0
- /package/dist/{chunk-ACZALXD6.js.map → chunk-K3XWUACX.js.map} +0 -0
- /package/dist/{chunk-TV2IDHPZ.js.map → chunk-K5ZQROWN.js.map} +0 -0
- /package/dist/{chunk-MTE6N7XU.js.map → chunk-NFJ63LIH.js.map} +0 -0
- /package/dist/{chunk-XZGYKKCE.js.map → chunk-OUQT72UL.js.map} +0 -0
- /package/dist/{chunk-WUD23YTG.js.map → chunk-P56Y43RL.js.map} +0 -0
- /package/dist/{chunk-GMNROZQJ.js.map → chunk-QTPW44IJ.js.map} +0 -0
- /package/dist/{chunk-QA7EHFRX.js.map → chunk-T3FKFPCW.js.map} +0 -0
- /package/dist/{chunk-SOCNWQMX.js.map → chunk-TEMAM5ET.js.map} +0 -0
- /package/dist/{chunk-CH2NWSES.js.map → chunk-TIV6M7Q7.js.map} +0 -0
- /package/dist/{chunk-6XNYYROM.js.map → chunk-WVEDW5VO.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
DataRowWindow
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-OUQT72UL.js";
|
|
5
5
|
import "../chunk-YC5GAO35.js";
|
|
6
6
|
import "../chunk-VLLUUOYJ.js";
|
|
7
7
|
import "../chunk-TH6DDCWF.js";
|
|
@@ -10,30 +10,31 @@ import "../chunk-LEPD6TV7.js";
|
|
|
10
10
|
import "../chunk-IMQ3RX35.js";
|
|
11
11
|
import "../chunk-U24MOJWK.js";
|
|
12
12
|
import "../chunk-NDTDBBMM.js";
|
|
13
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-P56Y43RL.js";
|
|
14
14
|
import "../chunk-UEQK6PFD.js";
|
|
15
15
|
import "../chunk-PTYKILZE.js";
|
|
16
|
-
import "../chunk-
|
|
16
|
+
import "../chunk-CZGZV35G.js";
|
|
17
17
|
import "../chunk-KPZX2M2C.js";
|
|
18
18
|
import "../chunk-GLZJ7WZK.js";
|
|
19
19
|
import "../chunk-B3LDW3IQ.js";
|
|
20
20
|
import "../chunk-FXT7PZAF.js";
|
|
21
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-NFJ63LIH.js";
|
|
22
22
|
import "../chunk-WFBPCMPF.js";
|
|
23
23
|
import "../chunk-KGW373MK.js";
|
|
24
24
|
import "../chunk-7ZCD4AZS.js";
|
|
25
25
|
import "../chunk-OPF265PU.js";
|
|
26
26
|
import "../chunk-QFP4BUKZ.js";
|
|
27
|
-
import "../chunk-
|
|
28
|
-
import "../chunk-
|
|
27
|
+
import "../chunk-KSCSH7AO.js";
|
|
28
|
+
import "../chunk-WVEDW5VO.js";
|
|
29
29
|
import "../chunk-BD77DHUO.js";
|
|
30
30
|
import "../chunk-BKCJUFD3.js";
|
|
31
31
|
import "../chunk-PIQJULQB.js";
|
|
32
32
|
import "../chunk-MOZRIHJF.js";
|
|
33
|
-
import "../chunk-
|
|
33
|
+
import "../chunk-T57DLONB.js";
|
|
34
34
|
import "../chunk-BNXNIN2S.js";
|
|
35
|
+
import "../chunk-HS6GMHKZ.js";
|
|
35
36
|
import "../chunk-ORXB4A5E.js";
|
|
36
|
-
import "../chunk-
|
|
37
|
+
import "../chunk-B5D3VIMC.js";
|
|
37
38
|
import "../chunk-4JAYMFQO.js";
|
|
38
39
|
import "../chunk-WFITMVDB.js";
|
|
39
40
|
import "../chunk-WAT2XW7L.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-table/host.tsx","../../src/table-host/host.tsx","../../src/table-host/row-drawing.ts"],"sourcesContent":["\"use client\";\n\n// The host contract lives in ../table-host so sections that only DRAW a table's row (item, peek) never\n// load the data-table directory. This file keeps the public `@ai-matrx/design-system/data-table/host` path.\nexport * from \"../table-host/host\";\n","\"use client\";\nimport { createContext, useContext, type Context, type ComponentType, type ReactNode, type MouseEventHandler } from \"react\";\nimport type { CopyControlProps } from \"../data-table/copy-types\";\nimport type { MatrxColumnDef, MatrxDataTableDensity, MatrxDataTableLook, MatrxDataTableRecordControls, TableSavedViewsProps } from \"../data-table/types\";\nimport type {\n MatrxTableMenuIconName,\n MatrxTableMenuItem,\n MatrxTableMenuPayload,\n MatrxTableMenuSectionsResolver,\n MatrxTableMenuTarget,\n} from \"../data-table/menu-targets\";\n\nexport type {\n MatrxTableMenuIconName,\n MatrxTableMenuItem,\n MatrxTableMenuSection,\n MatrxTableMenuSectionsResolver,\n} from \"../data-table/menu-targets\";\n\nexport type {\n AgentPayloadInput,\n AiCustomSource,\n AiVariant,\n ContentTransferReference,\n CopyControlProps,\n CopyEnvelopeMeta,\n CopyExportConfig,\n TableCopyController,\n} from \"../data-table/copy-types\";\n\nexport interface TableLinkProps { href: string; children: ReactNode; className?: string; onClick?: MouseEventHandler<HTMLAnchorElement>; }\n/**\n * Host-owned toolbar affordance. The table package keeps operation state and\n * labels; an application can supply its canonical icon-button primitive\n * without making the shared package depend on an application UI package.\n */\nexport interface TableToolbarActionProps { ariaLabel: string; tooltip?: string | undefined; disabled?: boolean | undefined; busy?: boolean | undefined; /** Indicates a selected mutually-exclusive action such as the current page. */ active?: boolean | undefined; onClick: () => void; children: ReactNode; }\nexport interface TableSidePanelProps { title: string; titleNode?: ReactNode; description?: ReactNode; onClose: () => void; /** Increments on every open request, including a repeat open of the row already shown — a host whose panel can sit in the background (a tab) brings it forward when this changes. */ openRequest?: number; defaultWidth?: number; headerActions?: ReactNode; children: ReactNode; }\nexport interface TableWindowPanelProps { id: string; title: string; onClose: () => void; width?: number; height?: number; minWidth?: number; minHeight?: number; actionsRight?: ReactNode; bodyClassName?: string; sidebarDefaultSize?: number; sidebarMinSize?: number; sidebar?: ReactNode; children: ReactNode; /** The table look the window opens from (lane mac-blockers): `\"mac\"` asks the host window for the Mac header. Absent: the default look. */ look?: MatrxDataTableLook; }\nexport interface TableEntityRefProps { token: string; id: string; href?: string; children: ReactNode; className?: string; /** Opening it starts a new tab. */ openInNewTab?: boolean; /** Its open / peek buttons stay shown, not only on hover. */ alwaysShowActions?: boolean; }\nexport interface TablePeekProps { kind: string; id: string; href?: string; onClose: () => void; }\nexport interface TableDoors {\n href?: string;\n peekKind?: string;\n canPeek?: boolean;\n /**\n * THE RECORD'S OWN MENU (lane DRILL-PARITY-LAST): what its kind offers wherever a table names it —\n * a person's admin destinations, an organization's pages — as neutral items the table draws in a\n * row's menu and on a right-click. Absent = the record has only its open and preview doors.\n */\n menu?: MatrxTableMenuItem[];\n}\n/**\n * The host's door onto the table's right-click levels.\n *\n * A canonical table registers ONE resolver per instance. The host's own menu\n * reads which of the four levels was clicked (`readMenuTarget`) and asks for\n * that level's descriptor — the package never interprets what comes back, and\n * the host never reaches into the table for the row or the range.\n */\nexport interface TableRowContextRegistry { register(tableId: string, resolve: (target: MatrxTableMenuTarget) => unknown): () => void; }\nexport interface TableContextMenuBoundaryProps {\n label: string;\n children: ReactNode;\n /**\n * The consumer's own sections, asked per right-click. Present only when the\n * table's consumer declared `contextMenu.sections`; the host reads the level\n * off the clicked element (`readMenuTarget`), calls this, and draws what comes\n * back in its own menu. Absent, the boundary behaves exactly as before.\n */\n sections?: MatrxTableMenuSectionsResolver | undefined;\n /** The table asked for the host's designed menu (`MatrxDataTableContextMenuConfig.designedMenu`). */\n designedMenu?: boolean | undefined;\n}\n/**\n * The host's view of a failed read with NO rows to show (RC-B12 round 13):\n * its error display (with the host's actions — matrx-frontend's Alchemy Menu)\n * and a retry. Without a host port the table draws a package `ErrorBox`.\n */\nexport interface TableReadFailureProps { error: unknown; what: string; onRetry?: (() => void) | undefined; }\n/**\n * The host's notice above rows kept from an earlier read when the latest read\n * failed: the rows may be out of date. Without a host port the table draws a\n * package `ErrorBox` in warning colors.\n */\nexport interface TableStaleNoticeProps { error: unknown; what: string; onRetry?: (() => void) | undefined; }\n/** The host's glyph for a menu icon name. A host without one draws labels only. */\nexport interface TableMenuIconProps { name: MatrxTableMenuIconName; className?: string | undefined; }\n/** The host's custom-field columns for a registry token over the given organizations (lane 7 W5). */\nexport interface TableCustomFieldColumns<T> { columns: MatrxColumnDef<T>[]; }\nexport interface MatrxDataTableHost { /** A HOOK, bound once by the host: the custom-field columns of `token` (null = none) for `organizationIds`. */ useCustomFieldColumns?: <T>(token: string | null, organizationIds: readonly string[]) => TableCustomFieldColumns<T> | null; JsonViewer?: ComponentType<{ data: object | string }>; Link?: ComponentType<TableLinkProps>; CopyControls?: ComponentType<CopyControlProps>; ToolbarAction?: ComponentType<TableToolbarActionProps>; SidePanelSurface?: ComponentType<TableSidePanelProps>; WindowPanel?: ComponentType<TableWindowPanelProps>; EntityRef?: ComponentType<TableEntityRefProps>; resolveEntityDoors?: (token: string, id: string, href?: string | null) => TableDoors | null; ResourcePeek?: ComponentType<TablePeekProps>; SavedViews?: ComponentType<TableSavedViewsProps>; rowContextRegistry?: TableRowContextRegistry; createDefaultMenuContext?: (payload: MatrxTableMenuPayload) => unknown; ContextMenuBoundary?: ComponentType<TableContextMenuBoundaryProps>; MenuIcon?: ComponentType<TableMenuIconProps>; /** A failed read with no rows (RC-B12 r13). */ ReadFailure?: ComponentType<TableReadFailureProps>; /** A failed refresh over kept rows (RC-B12 r13). */ StaleNotice?: ComponentType<TableStaleNoticeProps>; /** Host default; a table's explicit density takes precedence. */ defaultDensity?: MatrxDataTableDensity; /** Host default look (`default` | `mac`); a table's explicit `look` takes precedence. One switch turns every table under the host, and every mode built on it, to the Mac look. */ defaultLook?: MatrxDataTableLook; notify?: { success(message: string): void; error(message: string): void }; /** Client-side navigation (the app router), for `rowClick=\"page\"`. Absent: a full page load. */ navigate?: (href: string) => void; }\n// Public entrypoints are independently bundled in ESM and CJS. The context\n// identity must survive those bundle boundaries; values stay provider-scoped.\nconst HOST_SLOT = Symbol.for(\"ai-matrx.design-system.table-host-context\");\nconst hostRegistry = globalThis as typeof globalThis & { [HOST_SLOT]?: Context<MatrxDataTableHost> };\nconst HostContext = hostRegistry[HOST_SLOT] ??= createContext<MatrxDataTableHost>({});\nexport function MatrxDataTableProvider({ value, children }: { value: MatrxDataTableHost; children: ReactNode }) { return <HostContext.Provider value={value}>{children}</HostContext.Provider>; }\nexport function useMatrxDataTableHost(): MatrxDataTableHost { return useContext(HostContext); }\n\nexport interface MatrxTableRowAlchemy {\n copy: CopyControlProps;\n controls?: MatrxDataTableRecordControls;\n placement: \"inline\" | \"menu\";\n ready: boolean;\n activate(): void;\n /** Mount Alchemy's canonical palette in the host row menu. */\n mountMenu(target: HTMLElement | null, onClose?: () => void, restoreFocus?: () => void): void;\n prepare(restoreFocus?: () => void): Promise<void>;\n}\n\nconst ROW_ALCHEMY_SLOT = Symbol.for(\"ai-matrx.design-system.table-row-alchemy-context\");\nconst rowAlchemyRegistry = globalThis as typeof globalThis & { [ROW_ALCHEMY_SLOT]?: Context<MatrxTableRowAlchemy | null> };\nconst RowAlchemyContext = rowAlchemyRegistry[ROW_ALCHEMY_SLOT] ??= createContext<MatrxTableRowAlchemy | null>(null);\n\nexport function MatrxTableRowAlchemyScope({ value, children }: { value: MatrxTableRowAlchemy; children: ReactNode }) { return <RowAlchemyContext.Provider value={value}>{children}</RowAlchemyContext.Provider>; }\n\nexport function useMatrxTableRowAlchemy(): MatrxTableRowAlchemy | null { return useContext(RowAlchemyContext); }\n\nconst ROW_CONTROLS_SLOT = Symbol.for(\"ai-matrx.design-system.table-row-controls-context\");\nconst rowControlsRegistry = globalThis as typeof globalThis & { [ROW_CONTROLS_SLOT]?: Context<MatrxDataTableRecordControls | null> };\nconst RowControlsContext = rowControlsRegistry[ROW_CONTROLS_SLOT] ??= createContext<MatrxDataTableRecordControls | null>(null);\nexport function MatrxTableRowControlsScope({ value, children }: { value: MatrxDataTableRecordControls; children: ReactNode }) { return <RowControlsContext.Provider value={value}>{children}</RowControlsContext.Provider>; }\nexport function useMatrxTableRowControls(): MatrxDataTableRecordControls | null { return useContext(RowControlsContext); }\n\n/** Light door for components a row draws (a row menu): declare a lazy data prop without loading the table. */\nexport { declareLazyDataProps } from \"./row-drawing\";\n","/**\n * A ROW IS REDRAWN WHEN WHAT IT SHOWS CHANGED — NEVER BECAUSE A FUNCTION WAS BORN AGAIN\n * (lane TABLE-RERENDER, 2026-10-03).\n *\n * Hosts write `cell`, `customActions`, `getRowId`, `rowClassName` and their handlers inline, so every\n * host render hands the table new functions. Identity cannot say whether a new function draws\n * anything new, so the table used to redraw every row: /artifacts (560 rows) spent ~2.6 s redrawing\n * 1,120 rows to open one artifact. Asking every host to stabilise its callbacks fixed one screen of\n * ~85 at a time.\n *\n * The table now ASKS the row instead. When only the host's functions changed, a row builds its\n * elements (cheap — no component runs) and compares them with what it drew last:\n * · equal → it returns the SAME element object it committed, and React does not descend into it;\n * · different → it draws, and only that row pays.\n * Handlers inside a row that was not redrawn still call the host's LATEST function: every event\n * handler (`on[A-Z]…`) and every function the table marks `lazyCallback` is handed to the DOM as a\n * stable trampoline whose target is refreshed on each comparison (the latest-ref pattern, applied\n * to every handler a row holds instead of to a list the table has to keep in sync).\n *\n * Any other function in the tree (a render prop, a formatter a child calls while it renders, a\n * component type) is compared by identity, so a new one redraws the row — exactly as before. That\n * keeps the rule honest: a row is never left showing what an old closure drew. The one exception is\n * a prop its component DECLARED a lazy data getter (`declareLazyDataProps`): it is compared by what\n * it returns.\n */\nimport { cloneElement, isValidElement, type ReactElement, type ReactNode } from \"react\";\n\nconst LAZY = Symbol.for(\"@ai-matrx/design-system/data-table/lazy-callback\");\nconst MAX_DEPTH = 32;\nconst HANDLER_KEY = /^on[A-Z]/;\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Marks a function the table hands into a row as one that runs LATER (on a click, a copy, a menu\n * choice) and never while a child renders — so a row may keep a trampoline to it instead of\n * redrawing when only its identity changed.\n */\nexport function lazyCallback<F extends AnyFunction>(fn: F): F {\n (fn as F & { [LAZY]?: true })[LAZY] = true;\n return fn;\n}\n\nfunction isLazy(fn: unknown): boolean {\n return (fn as { [LAZY]?: true })[LAZY] === true;\n}\n\nconst LAZY_DATA_PROPS = Symbol.for(\"@ai-matrx/design-system/data-table/lazy-data-props\");\n\n/**\n * Declares the props of `component` that take a GETTER of data the component reads only later —\n * a row menu's `config={() => menu}`, resolved when the menu opens. Without it a getter is just an\n * unknown function, compared by identity, and a host that builds it inline (every row menu does)\n * redraws every row on every render of its own: /crm redrew all 25 rows on each checkbox tick.\n *\n * A declared prop is compared by what the getter RETURNS (its handlers by trampoline, everything\n * else exactly as a drawing is): the same data → the row keeps its drawing and the component is\n * handed a stable trampoline to the host's LATEST getter, so it opens with fresh items; different\n * data (a checkbox in an open menu flipped) → that row alone redraws. Declare once, on the\n * component — never per call site.\n *\n * The contract: a declared getter is PURE and CHEAP — the row calls the previous and the new one on\n * every host render to compare them. A getter that fetches, dispatches or builds something heavy\n * must not be declared.\n */\nexport function declareLazyDataProps<C extends object>(component: C, keys: readonly string[]): C {\n (component as C & { [LAZY_DATA_PROPS]?: ReadonlySet<string> })[LAZY_DATA_PROPS] = new Set(keys);\n return component;\n}\n\nfunction lazyDataKeys(type: unknown): ReadonlySet<string> | undefined {\n if ((typeof type !== \"function\" && typeof type !== \"object\") || type === null) return undefined;\n return (type as { [LAZY_DATA_PROPS]?: ReadonlySet<string> })[LAZY_DATA_PROPS];\n}\n\nconst UNRESOLVED = Symbol(\"unresolved\");\n\n/** A declared getter's data — or `UNRESOLVED` when calling it threw (the row then simply redraws). */\nfunction resolveLazyData(value: unknown): unknown {\n if (typeof value !== \"function\") return value;\n try {\n return (value as () => unknown)();\n } catch {\n return UNRESOLVED;\n }\n}\n\nfunction trampolinable(key: string, fn: unknown): boolean {\n return isLazy(fn) || HANDLER_KEY.test(key);\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\ninterface Slot {\n fn: (...args: unknown[]) => unknown;\n readonly stable: (...args: unknown[]) => unknown;\n}\n\nfunction makeSlot(fn: (...args: unknown[]) => unknown): Slot {\n const slot: Slot = {\n fn,\n // A Proxy rather than a bare wrapper: a handler that carries its own members (a debounced\n // function's `cancel`) keeps them, read from the latest function.\n stable: new Proxy((() => undefined) as (...args: unknown[]) => unknown, {\n apply: (_target, thisArg, args: unknown[]) => Reflect.apply(slot.fn, thisArg, args),\n get: (_target, key) => Reflect.get(slot.fn, key),\n }),\n };\n return slot;\n}\n\ntype Updates = Array<[path: string, fn: (...args: unknown[]) => unknown]>;\n\nfunction sameEntries(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n path: string,\n depth: number,\n updates: Updates,\n lazyKeys?: ReadonlySet<string>,\n): boolean {\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length) return false;\n for (const key of keys) {\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false;\n // A ref is never a handler: a new callback ref must be attached by React.\n if (key === \"ref\") {\n if (!Object.is(a[key], b[key])) return false;\n continue;\n }\n if (lazyKeys?.has(key) && (typeof a[key] === \"function\" || typeof b[key] === \"function\")) {\n if (Object.is(a[key], b[key])) continue;\n // A getter that became a plain value (or the reverse) is a new drawing: the committed element\n // still holds a trampoline to the OLD getter, which nothing would re-point.\n if (typeof a[key] !== typeof b[key]) return false;\n const before = resolveLazyData(a[key]);\n const after = resolveLazyData(b[key]);\n if (before === UNRESOLVED || after === UNRESOLVED) return false;\n // Handlers inside the data are reached through the getter's own trampoline, so the ones this\n // comparison collects are dropped: the component calls the LATEST getter and gets them fresh.\n if (!sameDrawing(before, after, key, `${path}/${key}()`, depth + 1, [])) return false;\n if (typeof b[key] === \"function\") updates.push([`${path}/${key}`, b[key] as (...args: unknown[]) => unknown]);\n continue;\n }\n if (!sameDrawing(a[key], b[key], key, `${path}/${key}`, depth + 1, updates)) return false;\n }\n return true;\n}\n\n/**\n * Whether `next` draws exactly what `previous` drew. Handlers that differ only by identity are\n * collected into `updates` (by path) so the caller can point the committed trampolines at them.\n */\nfunction sameDrawing(previous: unknown, next: unknown, key: string, path: string, depth: number, updates: Updates): boolean {\n if (Object.is(previous, next)) return true;\n if (typeof previous === \"function\" && typeof next === \"function\") {\n if (trampolinable(key, previous) && trampolinable(key, next)) {\n updates.push([path, next as (...args: unknown[]) => unknown]);\n return true;\n }\n return false;\n }\n if (depth > MAX_DEPTH) return false;\n if (isValidElement(previous) && isValidElement(next)) {\n if (previous.type !== next.type || previous.key !== next.key) return false;\n return sameEntries(\n previous.props as Record<string, unknown>,\n next.props as Record<string, unknown>,\n path,\n depth,\n updates,\n lazyDataKeys(previous.type),\n );\n }\n if (Array.isArray(previous) && Array.isArray(next)) {\n if (previous.length !== next.length) return false;\n for (let i = 0; i < previous.length; i += 1) {\n if (!sameDrawing(previous[i], next[i], key, `${path}/${i}`, depth + 1, updates)) return false;\n }\n return true;\n }\n if (isPlainObject(previous) && isPlainObject(next)) return sameEntries(previous, next, path, depth, updates);\n return false;\n}\n\n/** `value` with every trampolinable function replaced by its stable trampoline. */\nfunction stabilize(value: unknown, key: string, path: string, depth: number, slots: Map<string, Slot>, reuse: Map<string, Slot> | undefined, lazyData = false): unknown {\n if (typeof value === \"function\") {\n if (!lazyData && !trampolinable(key, value)) return value;\n const fn = value as (...args: unknown[]) => unknown;\n const kept = reuse?.get(path);\n const slot = kept ?? makeSlot(fn);\n slot.fn = fn;\n slots.set(path, slot);\n return slot.stable;\n }\n if (depth > MAX_DEPTH) return value;\n if (isValidElement(value)) {\n const props = value.props as Record<string, unknown>;\n const lazyKeys = lazyDataKeys(value.type);\n let changed: Record<string, unknown> | null = null;\n for (const propKey of Object.keys(props)) {\n if (propKey === \"ref\") continue;\n const before = props[propKey];\n const after = stabilize(before, propKey, `${path}/${propKey}`, depth + 1, slots, reuse, lazyKeys?.has(propKey) ?? false);\n if (after !== before) (changed ??= {})[propKey] = after;\n }\n if (!changed) return value;\n const clone = cloneElement(value as ReactElement, changed);\n // React (dev) marks JSX's static children as key-checked on the ELEMENT; a clone starts\n // unmarked, and a clone inside a static children array would then warn about a missing key.\n const before = (value as { _store?: { validated?: unknown } })._store;\n const after = (clone as { _store?: { validated?: unknown } })._store;\n if (before && after && \"validated\" in before) {\n try { after.validated = before.validated; } catch { /* frozen in some builds: only a dev warning is at stake */ }\n }\n return clone;\n }\n if (Array.isArray(value)) {\n let out: unknown[] | null = null;\n for (let i = 0; i < value.length; i += 1) {\n const after = stabilize(value[i], key, `${path}/${i}`, depth + 1, slots, reuse);\n if (after !== value[i]) (out ??= value.slice())[i] = after;\n }\n return out ?? value;\n }\n if (isPlainObject(value)) {\n let out: Record<string, unknown> | null = null;\n for (const entryKey of Object.keys(value)) {\n const before = value[entryKey];\n const after = stabilize(before, entryKey, `${path}/${entryKey}`, depth + 1, slots, reuse);\n if (after !== before) (out ??= { ...value })[entryKey] = after;\n }\n return out ?? value;\n }\n return value;\n}\n\ninterface RowDrawing {\n /** What the host and the table built last time — compared, never rendered. */\n raw: unknown;\n /** What React was handed: `raw` with trampolines. Returned unchanged while the drawing holds. */\n out: ReactNode;\n slots: Map<string, Slot>;\n}\n\n/** One table's drawings, by row id. */\nexport class RowDrawings {\n private readonly rows = new Map<string, RowDrawing>();\n\n /**\n * The element to hand React for this row. The SAME object as last time when `raw` draws what the\n * last one drew (its handlers are re-pointed); a new one otherwise.\n */\n draw(rowId: string, raw: ReactNode, fresh = false): ReactNode {\n const previous = this.rows.get(rowId);\n // `fresh`: the row's own facts moved — what it shows may differ even when its elements match.\n if (previous && !fresh) {\n const updates: Updates = [];\n if (sameDrawing(previous.raw, raw, \"\", \"\", 0, updates) && updates.every(([path]) => previous.slots.has(path))) {\n for (const [path, fn] of updates) previous.slots.get(path)!.fn = fn;\n previous.raw = raw;\n return previous.out;\n }\n }\n const slots = new Map<string, Slot>();\n const out = stabilize(raw, \"\", \"\", 0, slots, previous?.slots) as ReactNode;\n this.rows.set(rowId, { raw, out, slots });\n return out;\n }\n\n /** Forget rows that are no longer in the table (bounded by what was ever drawn). */\n prune(liveIds: ReadonlySet<string>): void {\n for (const id of this.rows.keys()) if (!liveIds.has(id)) this.rows.delete(id);\n }\n\n get size(): number {\n return this.rows.size;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,IAAAA,gBAAoH;;;ACwBpH,mBAAgF;AAsBhF,IAAM,kBAAkB,uBAAO,IAAI,oDAAoD;AAkBhF,SAAS,qBAAuC,WAAc,MAA4B;AAC/F,EAAC,UAA8D,eAAe,IAAI,IAAI,IAAI,IAAI;AAC9F,SAAO;AACT;;;AD4ByH;AAHzH,IAAM,YAAY,uBAAO,IAAI,2CAA2C;AACxE,IAAM,eAAe;AACrB,IAAM,cAAc,aAAa,SAAS,UAAM,6BAAkC,CAAC,CAAC;AAC7E,SAAS,uBAAuB,EAAE,OAAO,SAAS,GAAuD;AAAE,SAAO,4CAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AAAyB;AACzL,SAAS,wBAA4C;AAAE,aAAO,0BAAW,WAAW;AAAG;AAa9F,IAAM,mBAAmB,uBAAO,IAAI,kDAAkD;AACtF,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB,mBAAmB,gBAAgB,UAAM,6BAA2C,IAAI;AAE3G,SAAS,0BAA0B,EAAE,OAAO,SAAS,GAAyD;AAAE,SAAO,4CAAC,kBAAkB,UAAlB,EAA2B,OAAe,UAAS;AAA+B;AAE1M,SAAS,0BAAuD;AAAE,aAAO,0BAAW,iBAAiB;AAAG;AAE/G,IAAM,oBAAoB,uBAAO,IAAI,mDAAmD;AACxF,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB,oBAAoB,iBAAiB,UAAM,6BAAmD,IAAI;AACtH,SAAS,2BAA2B,EAAE,OAAO,SAAS,GAAiE;AAAE,SAAO,4CAAC,mBAAmB,UAAnB,EAA4B,OAAe,UAAS;AAAgC;AACrN,SAAS,2BAAgE;AAAE,aAAO,0BAAW,kBAAkB;AAAG;","names":["import_react"]}
|
|
1
|
+
{"version":3,"sources":["../../src/data-table/host.tsx","../../src/table-host/host.tsx","../../src/table-host/row-drawing.ts"],"sourcesContent":["\"use client\";\n\n// The host contract lives in ../table-host so sections that only DRAW a table's row (item, peek) never\n// load the data-table directory. This file keeps the public `@ai-matrx/design-system/data-table/host` path.\nexport * from \"../table-host/host\";\n","\"use client\";\nimport { createContext, useContext, type Context, type ComponentType, type ReactNode, type MouseEventHandler } from \"react\";\nimport type { CopyControlProps } from \"../data-table/copy-types\";\nimport type { MatrxColumnDef, MatrxDataTableDensity, MatrxDataTableLook, MatrxDataTableRecordControls, TableSavedViewsProps } from \"../data-table/types\";\nimport type {\n MatrxTableMenuIconName,\n MatrxTableMenuItem,\n MatrxTableMenuPayload,\n MatrxTableMenuSectionsResolver,\n MatrxTableMenuTarget,\n} from \"../data-table/menu-targets\";\n\nexport type {\n MatrxTableMenuIconName,\n MatrxTableMenuItem,\n MatrxTableMenuSection,\n MatrxTableMenuSectionsResolver,\n} from \"../data-table/menu-targets\";\n\nexport type {\n AgentPayloadInput,\n AiCustomSource,\n AiVariant,\n ContentTransferReference,\n CopyControlProps,\n CopyEnvelopeMeta,\n CopyExportConfig,\n TableCopyController,\n} from \"../data-table/copy-types\";\n\nexport interface TableLinkProps { href: string; children: ReactNode; className?: string; onClick?: MouseEventHandler<HTMLAnchorElement>; }\n/**\n * Host-owned toolbar affordance. The table package keeps operation state and\n * labels; an application can supply its canonical icon-button primitive\n * without making the shared package depend on an application UI package.\n */\nexport interface TableToolbarActionProps { ariaLabel: string; tooltip?: string | undefined; disabled?: boolean | undefined; busy?: boolean | undefined; /** Indicates a selected mutually-exclusive action such as the current page. */ active?: boolean | undefined; onClick: () => void; children: ReactNode; }\nexport interface TableSidePanelProps { title: string; titleNode?: ReactNode; description?: ReactNode; onClose: () => void; /** Increments on every open request, including a repeat open of the row already shown — a host whose panel can sit in the background (a tab) brings it forward when this changes. */ openRequest?: number; defaultWidth?: number; headerActions?: ReactNode; children: ReactNode; }\nexport interface TableWindowPanelProps { id: string; title: string; onClose: () => void; width?: number; height?: number; minWidth?: number; minHeight?: number; actionsRight?: ReactNode; bodyClassName?: string; sidebarDefaultSize?: number; sidebarMinSize?: number; sidebar?: ReactNode; children: ReactNode; /** The table look the window opens from (lane mac-blockers): `\"mac\"` asks the host window for the Mac header. Absent: the default look. */ look?: MatrxDataTableLook; }\nexport interface TableEntityRefProps { token: string; id: string; href?: string; children: ReactNode; className?: string; /** Opening it starts a new tab. */ openInNewTab?: boolean; /** Its open / peek buttons stay shown, not only on hover. */ alwaysShowActions?: boolean; }\nexport interface TablePeekProps { kind: string; id: string; href?: string; onClose: () => void; }\nexport interface TableDoors {\n href?: string;\n peekKind?: string;\n canPeek?: boolean;\n /**\n * THE RECORD'S OWN MENU (lane DRILL-PARITY-LAST): what its kind offers wherever a table names it —\n * a person's admin destinations, an organization's pages — as neutral items the table draws in a\n * row's menu and on a right-click. Absent = the record has only its open and preview doors.\n */\n menu?: MatrxTableMenuItem[];\n}\n/**\n * The host's door onto the table's right-click levels.\n *\n * A canonical table registers ONE resolver per instance. The host's own menu\n * reads which of the four levels was clicked (`readMenuTarget`) and asks for\n * that level's descriptor — the package never interprets what comes back, and\n * the host never reaches into the table for the row or the range.\n */\nexport interface TableRowContextRegistry { register(tableId: string, resolve: (target: MatrxTableMenuTarget) => unknown): () => void; }\nexport interface TableContextMenuBoundaryProps {\n label: string;\n children: ReactNode;\n /**\n * The consumer's own sections, asked per right-click. Present only when the\n * table's consumer declared `contextMenu.sections`; the host reads the level\n * off the clicked element (`readMenuTarget`), calls this, and draws what comes\n * back in its own menu. Absent, the boundary behaves exactly as before.\n */\n sections?: MatrxTableMenuSectionsResolver | undefined;\n /** The table asked for the host's designed menu (`MatrxDataTableContextMenuConfig.designedMenu`). */\n designedMenu?: boolean | undefined;\n}\n/**\n * The host's view of a failed read with NO rows to show (RC-B12 round 13):\n * its error display (with the host's actions — matrx-frontend's Alchemy Menu)\n * and a retry. Without a host port the table draws a package `ErrorBox`.\n */\nexport interface TableReadFailureProps { error: unknown; what: string; onRetry?: (() => void) | undefined; }\n/**\n * The host's notice above rows kept from an earlier read when the latest read\n * failed: the rows may be out of date. Without a host port the table draws a\n * package `ErrorBox` in warning colors.\n */\nexport interface TableStaleNoticeProps { error: unknown; what: string; onRetry?: (() => void) | undefined; }\n/** The host's glyph for a menu icon name. A host without one draws labels only. */\nexport interface TableMenuIconProps { name: MatrxTableMenuIconName; className?: string | undefined; }\n/** The host's custom-field columns for a registry token over the given organizations (lane 7 W5). */\nexport interface TableCustomFieldColumns<T> { columns: MatrxColumnDef<T>[]; }\nexport interface MatrxDataTableHost { /** A HOOK, bound once by the host: the custom-field columns of `token` (null = none) for `organizationIds`. */ useCustomFieldColumns?: <T>(token: string | null, organizationIds: readonly string[]) => TableCustomFieldColumns<T> | null; JsonViewer?: ComponentType<{ data: object | string }>; Link?: ComponentType<TableLinkProps>; CopyControls?: ComponentType<CopyControlProps>; ToolbarAction?: ComponentType<TableToolbarActionProps>; SidePanelSurface?: ComponentType<TableSidePanelProps>; WindowPanel?: ComponentType<TableWindowPanelProps>; EntityRef?: ComponentType<TableEntityRefProps>; resolveEntityDoors?: (token: string, id: string, href?: string | null) => TableDoors | null; ResourcePeek?: ComponentType<TablePeekProps>; SavedViews?: ComponentType<TableSavedViewsProps>; rowContextRegistry?: TableRowContextRegistry; createDefaultMenuContext?: (payload: MatrxTableMenuPayload) => unknown; ContextMenuBoundary?: ComponentType<TableContextMenuBoundaryProps>; MenuIcon?: ComponentType<TableMenuIconProps>; /** A failed read with no rows (RC-B12 r13). */ ReadFailure?: ComponentType<TableReadFailureProps>; /** A failed refresh over kept rows (RC-B12 r13). */ StaleNotice?: ComponentType<TableStaleNoticeProps>; /** Host default; a table's explicit density takes precedence. */ defaultDensity?: MatrxDataTableDensity; /** Host default look (`default` | `mac`); a table's explicit `look` takes precedence. One switch turns every table under the host, and every mode built on it, to the Mac look. */ defaultLook?: MatrxDataTableLook; notify?: { success(message: string, options?: { description?: string }): void; error(message: string, options?: { description?: string }): void }; /** Client-side navigation (the app router), for `rowClick=\"page\"`. Absent: a full page load. */ navigate?: (href: string) => void; }\n// Public entrypoints are independently bundled in ESM and CJS. The context\n// identity must survive those bundle boundaries; values stay provider-scoped.\nconst HOST_SLOT = Symbol.for(\"ai-matrx.design-system.table-host-context\");\nconst hostRegistry = globalThis as typeof globalThis & { [HOST_SLOT]?: Context<MatrxDataTableHost> };\nconst HostContext = hostRegistry[HOST_SLOT] ??= createContext<MatrxDataTableHost>({});\nexport function MatrxDataTableProvider({ value, children }: { value: MatrxDataTableHost; children: ReactNode }) { return <HostContext.Provider value={value}>{children}</HostContext.Provider>; }\nexport function useMatrxDataTableHost(): MatrxDataTableHost { return useContext(HostContext); }\n\nexport interface MatrxTableRowAlchemy {\n copy: CopyControlProps;\n controls?: MatrxDataTableRecordControls;\n placement: \"inline\" | \"menu\";\n ready: boolean;\n activate(): void;\n /** Mount Alchemy's canonical palette in the host row menu. */\n mountMenu(target: HTMLElement | null, onClose?: () => void, restoreFocus?: () => void): void;\n prepare(restoreFocus?: () => void): Promise<void>;\n}\n\nconst ROW_ALCHEMY_SLOT = Symbol.for(\"ai-matrx.design-system.table-row-alchemy-context\");\nconst rowAlchemyRegistry = globalThis as typeof globalThis & { [ROW_ALCHEMY_SLOT]?: Context<MatrxTableRowAlchemy | null> };\nconst RowAlchemyContext = rowAlchemyRegistry[ROW_ALCHEMY_SLOT] ??= createContext<MatrxTableRowAlchemy | null>(null);\n\nexport function MatrxTableRowAlchemyScope({ value, children }: { value: MatrxTableRowAlchemy; children: ReactNode }) { return <RowAlchemyContext.Provider value={value}>{children}</RowAlchemyContext.Provider>; }\n\nexport function useMatrxTableRowAlchemy(): MatrxTableRowAlchemy | null { return useContext(RowAlchemyContext); }\n\nconst ROW_CONTROLS_SLOT = Symbol.for(\"ai-matrx.design-system.table-row-controls-context\");\nconst rowControlsRegistry = globalThis as typeof globalThis & { [ROW_CONTROLS_SLOT]?: Context<MatrxDataTableRecordControls | null> };\nconst RowControlsContext = rowControlsRegistry[ROW_CONTROLS_SLOT] ??= createContext<MatrxDataTableRecordControls | null>(null);\nexport function MatrxTableRowControlsScope({ value, children }: { value: MatrxDataTableRecordControls; children: ReactNode }) { return <RowControlsContext.Provider value={value}>{children}</RowControlsContext.Provider>; }\nexport function useMatrxTableRowControls(): MatrxDataTableRecordControls | null { return useContext(RowControlsContext); }\n\n/** Light door for components a row draws (a row menu): declare a lazy data prop without loading the table. */\nexport { declareLazyDataProps } from \"./row-drawing\";\n","/**\n * A ROW IS REDRAWN WHEN WHAT IT SHOWS CHANGED — NEVER BECAUSE A FUNCTION WAS BORN AGAIN\n * (lane TABLE-RERENDER, 2026-10-03).\n *\n * Hosts write `cell`, `customActions`, `getRowId`, `rowClassName` and their handlers inline, so every\n * host render hands the table new functions. Identity cannot say whether a new function draws\n * anything new, so the table used to redraw every row: /artifacts (560 rows) spent ~2.6 s redrawing\n * 1,120 rows to open one artifact. Asking every host to stabilise its callbacks fixed one screen of\n * ~85 at a time.\n *\n * The table now ASKS the row instead. When only the host's functions changed, a row builds its\n * elements (cheap — no component runs) and compares them with what it drew last:\n * · equal → it returns the SAME element object it committed, and React does not descend into it;\n * · different → it draws, and only that row pays.\n * Handlers inside a row that was not redrawn still call the host's LATEST function: every event\n * handler (`on[A-Z]…`) and every function the table marks `lazyCallback` is handed to the DOM as a\n * stable trampoline whose target is refreshed on each comparison (the latest-ref pattern, applied\n * to every handler a row holds instead of to a list the table has to keep in sync).\n *\n * Any other function in the tree (a render prop, a formatter a child calls while it renders, a\n * component type) is compared by identity, so a new one redraws the row — exactly as before. That\n * keeps the rule honest: a row is never left showing what an old closure drew. The one exception is\n * a prop its component DECLARED a lazy data getter (`declareLazyDataProps`): it is compared by what\n * it returns.\n */\nimport { cloneElement, isValidElement, type ReactElement, type ReactNode } from \"react\";\n\nconst LAZY = Symbol.for(\"@ai-matrx/design-system/data-table/lazy-callback\");\nconst MAX_DEPTH = 32;\nconst HANDLER_KEY = /^on[A-Z]/;\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Marks a function the table hands into a row as one that runs LATER (on a click, a copy, a menu\n * choice) and never while a child renders — so a row may keep a trampoline to it instead of\n * redrawing when only its identity changed.\n */\nexport function lazyCallback<F extends AnyFunction>(fn: F): F {\n (fn as F & { [LAZY]?: true })[LAZY] = true;\n return fn;\n}\n\nfunction isLazy(fn: unknown): boolean {\n return (fn as { [LAZY]?: true })[LAZY] === true;\n}\n\nconst LAZY_DATA_PROPS = Symbol.for(\"@ai-matrx/design-system/data-table/lazy-data-props\");\n\n/**\n * Declares the props of `component` that take a GETTER of data the component reads only later —\n * a row menu's `config={() => menu}`, resolved when the menu opens. Without it a getter is just an\n * unknown function, compared by identity, and a host that builds it inline (every row menu does)\n * redraws every row on every render of its own: /crm redrew all 25 rows on each checkbox tick.\n *\n * A declared prop is compared by what the getter RETURNS (its handlers by trampoline, everything\n * else exactly as a drawing is): the same data → the row keeps its drawing and the component is\n * handed a stable trampoline to the host's LATEST getter, so it opens with fresh items; different\n * data (a checkbox in an open menu flipped) → that row alone redraws. Declare once, on the\n * component — never per call site.\n *\n * The contract: a declared getter is PURE and CHEAP — the row calls the previous and the new one on\n * every host render to compare them. A getter that fetches, dispatches or builds something heavy\n * must not be declared.\n */\nexport function declareLazyDataProps<C extends object>(component: C, keys: readonly string[]): C {\n (component as C & { [LAZY_DATA_PROPS]?: ReadonlySet<string> })[LAZY_DATA_PROPS] = new Set(keys);\n return component;\n}\n\nfunction lazyDataKeys(type: unknown): ReadonlySet<string> | undefined {\n if ((typeof type !== \"function\" && typeof type !== \"object\") || type === null) return undefined;\n return (type as { [LAZY_DATA_PROPS]?: ReadonlySet<string> })[LAZY_DATA_PROPS];\n}\n\nconst UNRESOLVED = Symbol(\"unresolved\");\n\n/** A declared getter's data — or `UNRESOLVED` when calling it threw (the row then simply redraws). */\nfunction resolveLazyData(value: unknown): unknown {\n if (typeof value !== \"function\") return value;\n try {\n return (value as () => unknown)();\n } catch {\n return UNRESOLVED;\n }\n}\n\nfunction trampolinable(key: string, fn: unknown): boolean {\n return isLazy(fn) || HANDLER_KEY.test(key);\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\ninterface Slot {\n fn: (...args: unknown[]) => unknown;\n readonly stable: (...args: unknown[]) => unknown;\n}\n\nfunction makeSlot(fn: (...args: unknown[]) => unknown): Slot {\n const slot: Slot = {\n fn,\n // A Proxy rather than a bare wrapper: a handler that carries its own members (a debounced\n // function's `cancel`) keeps them, read from the latest function.\n stable: new Proxy((() => undefined) as (...args: unknown[]) => unknown, {\n apply: (_target, thisArg, args: unknown[]) => Reflect.apply(slot.fn, thisArg, args),\n get: (_target, key) => Reflect.get(slot.fn, key),\n }),\n };\n return slot;\n}\n\ntype Updates = Array<[path: string, fn: (...args: unknown[]) => unknown]>;\n\nfunction sameEntries(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n path: string,\n depth: number,\n updates: Updates,\n lazyKeys?: ReadonlySet<string>,\n): boolean {\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length) return false;\n for (const key of keys) {\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false;\n // A ref is never a handler: a new callback ref must be attached by React.\n if (key === \"ref\") {\n if (!Object.is(a[key], b[key])) return false;\n continue;\n }\n if (lazyKeys?.has(key) && (typeof a[key] === \"function\" || typeof b[key] === \"function\")) {\n if (Object.is(a[key], b[key])) continue;\n // A getter that became a plain value (or the reverse) is a new drawing: the committed element\n // still holds a trampoline to the OLD getter, which nothing would re-point.\n if (typeof a[key] !== typeof b[key]) return false;\n const before = resolveLazyData(a[key]);\n const after = resolveLazyData(b[key]);\n if (before === UNRESOLVED || after === UNRESOLVED) return false;\n // Handlers inside the data are reached through the getter's own trampoline, so the ones this\n // comparison collects are dropped: the component calls the LATEST getter and gets them fresh.\n if (!sameDrawing(before, after, key, `${path}/${key}()`, depth + 1, [])) return false;\n if (typeof b[key] === \"function\") updates.push([`${path}/${key}`, b[key] as (...args: unknown[]) => unknown]);\n continue;\n }\n if (!sameDrawing(a[key], b[key], key, `${path}/${key}`, depth + 1, updates)) return false;\n }\n return true;\n}\n\n/**\n * Whether `next` draws exactly what `previous` drew. Handlers that differ only by identity are\n * collected into `updates` (by path) so the caller can point the committed trampolines at them.\n */\nfunction sameDrawing(previous: unknown, next: unknown, key: string, path: string, depth: number, updates: Updates): boolean {\n if (Object.is(previous, next)) return true;\n if (typeof previous === \"function\" && typeof next === \"function\") {\n if (trampolinable(key, previous) && trampolinable(key, next)) {\n updates.push([path, next as (...args: unknown[]) => unknown]);\n return true;\n }\n return false;\n }\n if (depth > MAX_DEPTH) return false;\n if (isValidElement(previous) && isValidElement(next)) {\n if (previous.type !== next.type || previous.key !== next.key) return false;\n return sameEntries(\n previous.props as Record<string, unknown>,\n next.props as Record<string, unknown>,\n path,\n depth,\n updates,\n lazyDataKeys(previous.type),\n );\n }\n if (Array.isArray(previous) && Array.isArray(next)) {\n if (previous.length !== next.length) return false;\n for (let i = 0; i < previous.length; i += 1) {\n if (!sameDrawing(previous[i], next[i], key, `${path}/${i}`, depth + 1, updates)) return false;\n }\n return true;\n }\n if (isPlainObject(previous) && isPlainObject(next)) return sameEntries(previous, next, path, depth, updates);\n return false;\n}\n\n/** `value` with every trampolinable function replaced by its stable trampoline. */\nfunction stabilize(value: unknown, key: string, path: string, depth: number, slots: Map<string, Slot>, reuse: Map<string, Slot> | undefined, lazyData = false): unknown {\n if (typeof value === \"function\") {\n if (!lazyData && !trampolinable(key, value)) return value;\n const fn = value as (...args: unknown[]) => unknown;\n const kept = reuse?.get(path);\n const slot = kept ?? makeSlot(fn);\n slot.fn = fn;\n slots.set(path, slot);\n return slot.stable;\n }\n if (depth > MAX_DEPTH) return value;\n if (isValidElement(value)) {\n const props = value.props as Record<string, unknown>;\n const lazyKeys = lazyDataKeys(value.type);\n let changed: Record<string, unknown> | null = null;\n for (const propKey of Object.keys(props)) {\n if (propKey === \"ref\") continue;\n const before = props[propKey];\n const after = stabilize(before, propKey, `${path}/${propKey}`, depth + 1, slots, reuse, lazyKeys?.has(propKey) ?? false);\n if (after !== before) (changed ??= {})[propKey] = after;\n }\n if (!changed) return value;\n const clone = cloneElement(value as ReactElement, changed);\n // React (dev) marks JSX's static children as key-checked on the ELEMENT; a clone starts\n // unmarked, and a clone inside a static children array would then warn about a missing key.\n const before = (value as { _store?: { validated?: unknown } })._store;\n const after = (clone as { _store?: { validated?: unknown } })._store;\n if (before && after && \"validated\" in before) {\n try { after.validated = before.validated; } catch { /* frozen in some builds: only a dev warning is at stake */ }\n }\n return clone;\n }\n if (Array.isArray(value)) {\n let out: unknown[] | null = null;\n for (let i = 0; i < value.length; i += 1) {\n const after = stabilize(value[i], key, `${path}/${i}`, depth + 1, slots, reuse);\n if (after !== value[i]) (out ??= value.slice())[i] = after;\n }\n return out ?? value;\n }\n if (isPlainObject(value)) {\n let out: Record<string, unknown> | null = null;\n for (const entryKey of Object.keys(value)) {\n const before = value[entryKey];\n const after = stabilize(before, entryKey, `${path}/${entryKey}`, depth + 1, slots, reuse);\n if (after !== before) (out ??= { ...value })[entryKey] = after;\n }\n return out ?? value;\n }\n return value;\n}\n\ninterface RowDrawing {\n /** What the host and the table built last time — compared, never rendered. */\n raw: unknown;\n /** What React was handed: `raw` with trampolines. Returned unchanged while the drawing holds. */\n out: ReactNode;\n slots: Map<string, Slot>;\n}\n\n/** One table's drawings, by row id. */\nexport class RowDrawings {\n private readonly rows = new Map<string, RowDrawing>();\n\n /**\n * The element to hand React for this row. The SAME object as last time when `raw` draws what the\n * last one drew (its handlers are re-pointed); a new one otherwise.\n */\n draw(rowId: string, raw: ReactNode, fresh = false): ReactNode {\n const previous = this.rows.get(rowId);\n // `fresh`: the row's own facts moved — what it shows may differ even when its elements match.\n if (previous && !fresh) {\n const updates: Updates = [];\n if (sameDrawing(previous.raw, raw, \"\", \"\", 0, updates) && updates.every(([path]) => previous.slots.has(path))) {\n for (const [path, fn] of updates) previous.slots.get(path)!.fn = fn;\n previous.raw = raw;\n return previous.out;\n }\n }\n const slots = new Map<string, Slot>();\n const out = stabilize(raw, \"\", \"\", 0, slots, previous?.slots) as ReactNode;\n this.rows.set(rowId, { raw, out, slots });\n return out;\n }\n\n /** Forget rows that are no longer in the table (bounded by what was ever drawn). */\n prune(liveIds: ReadonlySet<string>): void {\n for (const id of this.rows.keys()) if (!liveIds.has(id)) this.rows.delete(id);\n }\n\n get size(): number {\n return this.rows.size;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,IAAAA,gBAAoH;;;ACwBpH,mBAAgF;AAsBhF,IAAM,kBAAkB,uBAAO,IAAI,oDAAoD;AAkBhF,SAAS,qBAAuC,WAAc,MAA4B;AAC/F,EAAC,UAA8D,eAAe,IAAI,IAAI,IAAI,IAAI;AAC9F,SAAO;AACT;;;AD4ByH;AAHzH,IAAM,YAAY,uBAAO,IAAI,2CAA2C;AACxE,IAAM,eAAe;AACrB,IAAM,cAAc,aAAa,SAAS,UAAM,6BAAkC,CAAC,CAAC;AAC7E,SAAS,uBAAuB,EAAE,OAAO,SAAS,GAAuD;AAAE,SAAO,4CAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AAAyB;AACzL,SAAS,wBAA4C;AAAE,aAAO,0BAAW,WAAW;AAAG;AAa9F,IAAM,mBAAmB,uBAAO,IAAI,kDAAkD;AACtF,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB,mBAAmB,gBAAgB,UAAM,6BAA2C,IAAI;AAE3G,SAAS,0BAA0B,EAAE,OAAO,SAAS,GAAyD;AAAE,SAAO,4CAAC,kBAAkB,UAAlB,EAA2B,OAAe,UAAS;AAA+B;AAE1M,SAAS,0BAAuD;AAAE,aAAO,0BAAW,iBAAiB;AAAG;AAE/G,IAAM,oBAAoB,uBAAO,IAAI,mDAAmD;AACxF,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB,oBAAoB,iBAAiB,UAAM,6BAAmD,IAAI;AACtH,SAAS,2BAA2B,EAAE,OAAO,SAAS,GAAiE;AAAE,SAAO,4CAAC,mBAAmB,UAAnB,EAA4B,OAAe,UAAS;AAAgC;AACrN,SAAS,2BAAgE;AAAE,aAAO,0BAAW,kBAAkB;AAAG;","names":["import_react"]}
|
|
@@ -1257,8 +1257,12 @@ interface MatrxDataTableHost {
|
|
|
1257
1257
|
defaultDensity?: MatrxDataTableDensity; /** Host default look (`default` | `mac`); a table's explicit `look` takes precedence. One switch turns every table under the host, and every mode built on it, to the Mac look. */
|
|
1258
1258
|
defaultLook?: MatrxDataTableLook;
|
|
1259
1259
|
notify?: {
|
|
1260
|
-
success(message: string
|
|
1261
|
-
|
|
1260
|
+
success(message: string, options?: {
|
|
1261
|
+
description?: string;
|
|
1262
|
+
}): void;
|
|
1263
|
+
error(message: string, options?: {
|
|
1264
|
+
description?: string;
|
|
1265
|
+
}): void;
|
|
1262
1266
|
}; /** Client-side navigation (the app router), for `rowClick="page"`. Absent: a full page load. */
|
|
1263
1267
|
navigate?: (href: string) => void;
|
|
1264
1268
|
}
|
|
@@ -1257,8 +1257,12 @@ interface MatrxDataTableHost {
|
|
|
1257
1257
|
defaultDensity?: MatrxDataTableDensity; /** Host default look (`default` | `mac`); a table's explicit `look` takes precedence. One switch turns every table under the host, and every mode built on it, to the Mac look. */
|
|
1258
1258
|
defaultLook?: MatrxDataTableLook;
|
|
1259
1259
|
notify?: {
|
|
1260
|
-
success(message: string
|
|
1261
|
-
|
|
1260
|
+
success(message: string, options?: {
|
|
1261
|
+
description?: string;
|
|
1262
|
+
}): void;
|
|
1263
|
+
error(message: string, options?: {
|
|
1264
|
+
description?: string;
|
|
1265
|
+
}): void;
|
|
1262
1266
|
}; /** Client-side navigation (the app router), for `rowClick="page"`. Absent: a full page load. */
|
|
1263
1267
|
navigate?: (href: string) => void;
|
|
1264
1268
|
}
|
package/dist/data-table/host.js
CHANGED
|
@@ -1120,7 +1120,7 @@ var DropdownMenuSubTrigger = React6.forwardRef(({ className, inset, children, ..
|
|
|
1120
1120
|
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
1121
1121
|
var DropdownMenuSubContent = React6.forwardRef(({ className, container, surface = "default", sideOffset = 2, alignOffset = -4, collisionPadding, onFocusOutside, children, ...props }, ref) => {
|
|
1122
1122
|
const portalContainer = usePortalContainer(container);
|
|
1123
|
-
const [bottomClearance] = React6.useState(
|
|
1123
|
+
const [bottomClearance] = React6.useState(sharedBottomClearance);
|
|
1124
1124
|
const padding = menuCollisionPadding(collisionPadding ?? 8, 0, bottomClearance);
|
|
1125
1125
|
const nested = useNestedSubmenuPlacement(ref, padding);
|
|
1126
1126
|
const keep = useSubmenuSurvivesParentPanelFocus(nested.ref, onFocusOutside);
|
|
@@ -1142,18 +1142,27 @@ var DropdownMenuSubContent = React6.forwardRef(({ className, container, surface
|
|
|
1142
1142
|
) });
|
|
1143
1143
|
});
|
|
1144
1144
|
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
1145
|
-
function readTopClearance() {
|
|
1146
|
-
return readClearance("var(--matrx-floating-top-clearance, var(--shell-header-h, 0px))");
|
|
1147
|
-
}
|
|
1148
|
-
function readBottomClearance() {
|
|
1149
|
-
return readClearance("var(--matrx-floating-clearance, 0px)");
|
|
1150
|
-
}
|
|
1151
1145
|
function menuCollisionPadding(caller, top, bottom, fallback = 0) {
|
|
1152
1146
|
const side = (key) => typeof caller === "number" ? caller : caller?.[key] ?? fallback;
|
|
1153
1147
|
return { top: Math.max(side("top"), top), right: side("right"), bottom: Math.max(side("bottom"), bottom), left: side("left") };
|
|
1154
1148
|
}
|
|
1155
|
-
|
|
1149
|
+
var CLEARANCE_REUSE_MS = 500;
|
|
1150
|
+
var clearanceCache = /* @__PURE__ */ new Map();
|
|
1151
|
+
var clock = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1152
|
+
function readClearance(height, shared = false) {
|
|
1156
1153
|
if (typeof document === "undefined") return 0;
|
|
1154
|
+
const now = clock();
|
|
1155
|
+
const held = shared ? clearanceCache.get(height) : void 0;
|
|
1156
|
+
if (held && now - held.at < CLEARANCE_REUSE_MS) return held.px;
|
|
1157
|
+
const px2 = measureClearance(height);
|
|
1158
|
+
clearanceCache.set(height, { px: px2, at: now });
|
|
1159
|
+
return px2;
|
|
1160
|
+
}
|
|
1161
|
+
var TOP_CLEARANCE = "var(--matrx-floating-top-clearance, var(--shell-header-h, 0px))";
|
|
1162
|
+
var BOTTOM_CLEARANCE = "var(--matrx-floating-clearance, 0px)";
|
|
1163
|
+
var sharedTopClearance = () => readClearance(TOP_CLEARANCE, true);
|
|
1164
|
+
var sharedBottomClearance = () => readClearance(BOTTOM_CLEARANCE, true);
|
|
1165
|
+
function measureClearance(height) {
|
|
1157
1166
|
const probe2 = document.createElement("div");
|
|
1158
1167
|
probe2.style.cssText = `position:fixed;top:0;left:0;width:0;visibility:hidden;pointer-events:none;height:${height}`;
|
|
1159
1168
|
document.body.appendChild(probe2);
|
|
@@ -1163,8 +1172,8 @@ function readClearance(height) {
|
|
|
1163
1172
|
}
|
|
1164
1173
|
var DropdownMenuContent = React6.forwardRef(({ className, sideOffset = 4, container, surface = "default", children, onCloseAutoFocus, collisionPadding, ...props }, ref) => {
|
|
1165
1174
|
const portalContainer = usePortalContainer(container);
|
|
1166
|
-
const [topClearance] = React6.useState(
|
|
1167
|
-
const [bottomClearance] = React6.useState(
|
|
1175
|
+
const [topClearance] = React6.useState(sharedTopClearance);
|
|
1176
|
+
const [bottomClearance] = React6.useState(sharedBottomClearance);
|
|
1168
1177
|
const edges = useMenuScrollEdges(ref);
|
|
1169
1178
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DropdownMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1170
1179
|
DropdownMenuPrimitive.Content,
|
|
@@ -6442,13 +6451,14 @@ function useTableNotice() {
|
|
|
6442
6451
|
const [message, setMessage] = (0, import_react17.useState)(null);
|
|
6443
6452
|
const notify = (0, import_react17.useMemo)(
|
|
6444
6453
|
() => ({
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6454
|
+
// A card is a short title and an optional line (NOTICE_TEXT_BUDGET); the inline fallback joins them.
|
|
6455
|
+
success(text, options) {
|
|
6456
|
+
if (host.notify) host.notify.success(text, options);
|
|
6457
|
+
else setMessage({ text: options?.description ? `${text}. ${options.description}` : text, error: false });
|
|
6448
6458
|
},
|
|
6449
|
-
error(text) {
|
|
6450
|
-
if (host.notify) host.notify.error(text);
|
|
6451
|
-
else setMessage({ text, error: true });
|
|
6459
|
+
error(text, options) {
|
|
6460
|
+
if (host.notify) host.notify.error(text, options);
|
|
6461
|
+
else setMessage({ text: options?.description ? `${text}. ${options.description}` : text, error: true });
|
|
6452
6462
|
}
|
|
6453
6463
|
}),
|
|
6454
6464
|
[host]
|
|
@@ -11888,9 +11898,9 @@ function drillWindowMoment(value) {
|
|
|
11888
11898
|
function momentWords(end) {
|
|
11889
11899
|
const [y, m, d] = end.slice(0, 10).split("-").map(Number);
|
|
11890
11900
|
const month = new Date(Date.UTC(y, m - 1, d)).toLocaleString("en-US", { month: "short", timeZone: "UTC" });
|
|
11891
|
-
const
|
|
11901
|
+
const clock2 = end.length > 10 ? end.slice(11, 16) : null;
|
|
11892
11902
|
const zone = end.endsWith("Z") ? "UTC" : end.length > 16 ? `UTC${end.slice(-6)}`.replace("UTC+00:00", "UTC") : "";
|
|
11893
|
-
return { day: `${month} ${d}, ${y}`, clock, zone };
|
|
11903
|
+
return { day: `${month} ${d}, ${y}`, clock: clock2, zone };
|
|
11894
11904
|
}
|
|
11895
11905
|
function drillWindowLabel(window2) {
|
|
11896
11906
|
if (!window2) return "All time";
|
|
@@ -14090,9 +14100,9 @@ function drillConfigFromColumns(columns, rows = [], options = {}) {
|
|
|
14090
14100
|
{ key: `max_${id}`, label: `${label} (max)`, additive: false, op: "max", of: id, ...fmt }
|
|
14091
14101
|
);
|
|
14092
14102
|
} else if (kind === "time") {
|
|
14093
|
-
const
|
|
14094
|
-
if (!
|
|
14095
|
-
dimensions.push({ key: id, label, kind: "time", grains:
|
|
14103
|
+
const clock2 = sample.some((v) => v instanceof Date || typeof v === "string" && v.length > 10 && !/T00:00(:00(\.0+)?)?(Z|[+-]00:?00)?$/.test(v));
|
|
14104
|
+
if (!clock2) calendarDates.add(id);
|
|
14105
|
+
dimensions.push({ key: id, label, kind: "time", grains: clock2 ? MATRX_DRILL_GRAINS : MATRX_DRILL_DEFAULT_GRAINS });
|
|
14096
14106
|
} else if (kind === "boolean") {
|
|
14097
14107
|
dimensions.push({ key: id, label, kind: "boolean", cardinality: "low" });
|
|
14098
14108
|
} else if (kind === "text") {
|
|
@@ -18306,7 +18316,7 @@ function RecordId({
|
|
|
18306
18316
|
e.preventDefault();
|
|
18307
18317
|
void (0, import_clipboard2.copyText)(value).then((ok) => {
|
|
18308
18318
|
if (!ok) {
|
|
18309
|
-
notify?.error(
|
|
18319
|
+
notify?.error("Not copied", { description: copyLabel });
|
|
18310
18320
|
return;
|
|
18311
18321
|
}
|
|
18312
18322
|
setCopied(true);
|
|
@@ -18322,12 +18332,10 @@ function RecordId({
|
|
|
18322
18332
|
try {
|
|
18323
18333
|
const result = await onOpen(value);
|
|
18324
18334
|
if (result === "forbidden") {
|
|
18325
|
-
notify?.error("
|
|
18335
|
+
notify?.error("No access", { description: "This account can't open this record" });
|
|
18326
18336
|
}
|
|
18327
18337
|
} catch (err) {
|
|
18328
|
-
notify?.error(
|
|
18329
|
-
`Couldn't open: ${err instanceof Error ? err.message : String(err)}`
|
|
18330
|
-
);
|
|
18338
|
+
notify?.error("Not opened", { description: peopleErrorSentence(err, null, { card: true }) });
|
|
18331
18339
|
} finally {
|
|
18332
18340
|
setOpening(false);
|
|
18333
18341
|
}
|
|
@@ -23147,9 +23155,7 @@ function MatrxDataTableCore(coreProps) {
|
|
|
23147
23155
|
});
|
|
23148
23156
|
toast.success("Change saved");
|
|
23149
23157
|
}).catch((error) => {
|
|
23150
|
-
toast.error(
|
|
23151
|
-
`Couldn't save: ${error instanceof Error ? error.message : String(error)}`
|
|
23152
|
-
);
|
|
23158
|
+
toast.error("Not saved", { description: peopleErrorSentence(error, null, { card: true }) });
|
|
23153
23159
|
}).finally(() => finishSaving(scope));
|
|
23154
23160
|
}
|
|
23155
23161
|
};
|
|
@@ -23295,9 +23301,7 @@ function MatrxDataTableCore(coreProps) {
|
|
|
23295
23301
|
if (!hierarchy) return;
|
|
23296
23302
|
void Promise.resolve(hierarchy.onMove(row, move)).catch(
|
|
23297
23303
|
(error) => {
|
|
23298
|
-
toast.error(
|
|
23299
|
-
`Couldn't move row: ${error instanceof Error ? error.message : String(error)}`
|
|
23300
|
-
);
|
|
23304
|
+
toast.error("Row not moved", { description: peopleErrorSentence(error, null, { card: true }) });
|
|
23301
23305
|
}
|
|
23302
23306
|
);
|
|
23303
23307
|
};
|
|
@@ -23425,9 +23429,7 @@ function MatrxDataTableCore(coreProps) {
|
|
|
23425
23429
|
});
|
|
23426
23430
|
toast.success("Changes saved");
|
|
23427
23431
|
} catch (e) {
|
|
23428
|
-
toast.error(
|
|
23429
|
-
`Couldn't save: ${e instanceof Error ? e.message : String(e)}`
|
|
23430
|
-
);
|
|
23432
|
+
toast.error("Not saved", { description: peopleErrorSentence(e, null, { card: true }) });
|
|
23431
23433
|
} finally {
|
|
23432
23434
|
savingScopesRef.current.delete(scope);
|
|
23433
23435
|
finishSaving(scope);
|
|
@@ -25004,7 +25006,7 @@ function MatrxDataTableCore(coreProps) {
|
|
|
25004
25006
|
void Promise.resolve().then(() => toolbar.refresh?.onRefresh()).catch((cause) => {
|
|
25005
25007
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
25006
25008
|
setToolbarActionError(`Refresh failed: ${message}`);
|
|
25007
|
-
toast.error(
|
|
25009
|
+
toast.error("Not refreshed", { description: peopleErrorSentence(cause, message, { card: true }) });
|
|
25008
25010
|
}).finally(() => {
|
|
25009
25011
|
toolbarActionPendingRef.current.refresh = false;
|
|
25010
25012
|
setToolbarActionPending((current) => ({ ...current, refresh: false }));
|
|
@@ -25029,7 +25031,7 @@ function MatrxDataTableCore(coreProps) {
|
|
|
25029
25031
|
void Promise.resolve().then(() => toolbar.add?.onAdd()).catch((cause) => {
|
|
25030
25032
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
25031
25033
|
setToolbarActionError(`Add failed: ${message}`);
|
|
25032
|
-
toast.error(
|
|
25034
|
+
toast.error("Not added", { description: peopleErrorSentence(cause, message, { card: true }) });
|
|
25033
25035
|
}).finally(() => {
|
|
25034
25036
|
toolbarActionPendingRef.current.add = false;
|
|
25035
25037
|
setToolbarActionPending((current) => ({ ...current, add: false }));
|