@ai-matrx/design-system 0.41.0 → 0.42.1

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/data-table/copy-helpers.d.cts +2 -1
  3. package/dist/data-table/copy-helpers.d.ts +2 -1
  4. package/dist/data-table/data-row-inspector.cjs.map +1 -1
  5. package/dist/data-table/data-row-inspector.js.map +1 -1
  6. package/dist/data-table/data-row-window.cjs.map +1 -1
  7. package/dist/data-table/data-row-window.js.map +1 -1
  8. package/dist/data-table/filter-engine.d.cts +2 -1
  9. package/dist/data-table/filter-engine.d.ts +2 -1
  10. package/dist/data-table/host.cjs.map +1 -1
  11. package/dist/data-table/host.d.cts +18 -4
  12. package/dist/data-table/host.d.ts +18 -4
  13. package/dist/data-table/host.js.map +1 -1
  14. package/dist/data-table/index.cjs +2990 -2778
  15. package/dist/data-table/index.cjs.map +1 -1
  16. package/dist/data-table/index.d.cts +4 -4
  17. package/dist/data-table/index.d.ts +4 -4
  18. package/dist/data-table/index.js +2999 -2787
  19. package/dist/data-table/index.js.map +1 -1
  20. package/dist/data-table/infer-filter.d.cts +2 -1
  21. package/dist/data-table/infer-filter.d.ts +2 -1
  22. package/dist/data-table/layered-filters.d.cts +2 -1
  23. package/dist/data-table/layered-filters.d.ts +2 -1
  24. package/dist/data-table/menu-targets.cjs +27 -0
  25. package/dist/data-table/menu-targets.cjs.map +1 -1
  26. package/dist/data-table/menu-targets.d.cts +49 -1
  27. package/dist/data-table/menu-targets.d.ts +49 -1
  28. package/dist/data-table/menu-targets.js +27 -0
  29. package/dist/data-table/menu-targets.js.map +1 -1
  30. package/dist/data-table/pagination.cjs.map +1 -1
  31. package/dist/data-table/pagination.js.map +1 -1
  32. package/dist/data-table/query-control.d.cts +2 -1
  33. package/dist/data-table/query-control.d.ts +2 -1
  34. package/dist/data-table/spreadsheet.cjs +24 -8
  35. package/dist/data-table/spreadsheet.cjs.map +1 -1
  36. package/dist/data-table/spreadsheet.d.cts +20 -3
  37. package/dist/data-table/spreadsheet.d.ts +20 -3
  38. package/dist/data-table/spreadsheet.js +24 -8
  39. package/dist/data-table/spreadsheet.js.map +1 -1
  40. package/dist/data-table/types.cjs.map +1 -1
  41. package/dist/data-table/types.d.cts +2 -1
  42. package/dist/data-table/types.d.ts +2 -1
  43. package/dist/data-table/url-state.d.cts +2 -1
  44. package/dist/data-table/url-state.d.ts +2 -1
  45. package/dist/data-table/uuid-cell.cjs.map +1 -1
  46. package/dist/data-table/uuid-cell.js.map +1 -1
  47. package/dist/data-table/xlsx.d.cts +2 -1
  48. package/dist/data-table/xlsx.d.ts +2 -1
  49. package/dist/index.cjs +534 -468
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +67 -1
  52. package/dist/index.d.ts +67 -1
  53. package/dist/index.js +541 -475
  54. package/dist/index.js.map +1 -1
  55. package/dist/{types-Dx5hJgCi.d.ts → types-afUz93xo.d.ts} +33 -1
  56. package/dist/{types-DNkbHRVK.d.cts → types-q5ficDSo.d.cts} +33 -1
  57. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { M as MatrxColumnDef, f as ColumnFilterKind } from '../types-DNkbHRVK.cjs';
1
+ import { M as MatrxColumnDef, f as ColumnFilterKind } from '../types-q5ficDSo.cjs';
2
2
  import '../content-transfer.cjs';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -12,6 +12,7 @@ import './facets.cjs';
12
12
  import './spreadsheet.cjs';
13
13
  import './grid-selection.cjs';
14
14
  import './date-filter.cjs';
15
+ import './menu-targets.cjs';
15
16
 
16
17
  type ResolvedFilterKind = "text" | "select" | "boolean" | "number" | "date";
17
18
  /**
@@ -1,4 +1,4 @@
1
- import { M as MatrxColumnDef, f as ColumnFilterKind } from '../types-Dx5hJgCi.js';
1
+ import { M as MatrxColumnDef, f as ColumnFilterKind } from '../types-afUz93xo.js';
2
2
  import '../content-transfer.js';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -12,6 +12,7 @@ import './facets.js';
12
12
  import './spreadsheet.js';
13
13
  import './grid-selection.js';
14
14
  import './date-filter.js';
15
+ import './menu-targets.js';
15
16
 
16
17
  type ResolvedFilterKind = "text" | "select" | "boolean" | "number" | "date";
17
18
  /**
@@ -1,4 +1,4 @@
1
- export { _ as LAYERED_FILTER_OPERATORS, $ as LAYERED_FILTER_OPERATOR_LABELS, a0 as LayeredFilterField, a1 as LayeredFilterOperator, L as LayeredFilterRule, a2 as columnFiltersToLayeredRules, a3 as completeLayeredFilterRules, a4 as decodeLayeredFilterRules, a5 as encodeLayeredFilterRules, a6 as isCompleteLayeredFilterRule, a7 as isLayeredFilterOperator, a8 as layeredFilterMatchesValue, a9 as layeredFilterNeedsValue, aa as layeredFilterRuleSummary, ab as operatorsForLayeredField } from '../types-DNkbHRVK.cjs';
1
+ export { _ as LAYERED_FILTER_OPERATORS, $ as LAYERED_FILTER_OPERATOR_LABELS, a0 as LayeredFilterField, a1 as LayeredFilterOperator, L as LayeredFilterRule, a2 as columnFiltersToLayeredRules, a3 as completeLayeredFilterRules, a4 as decodeLayeredFilterRules, a5 as encodeLayeredFilterRules, a6 as isCompleteLayeredFilterRule, a7 as isLayeredFilterOperator, a8 as layeredFilterMatchesValue, a9 as layeredFilterNeedsValue, aa as layeredFilterRuleSummary, ab as operatorsForLayeredField } from '../types-q5ficDSo.cjs';
2
2
  import '../content-transfer.cjs';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -12,3 +12,4 @@ import './facets.cjs';
12
12
  import './spreadsheet.cjs';
13
13
  import './grid-selection.cjs';
14
14
  import './date-filter.cjs';
15
+ import './menu-targets.cjs';
@@ -1,4 +1,4 @@
1
- export { _ as LAYERED_FILTER_OPERATORS, $ as LAYERED_FILTER_OPERATOR_LABELS, a0 as LayeredFilterField, a1 as LayeredFilterOperator, L as LayeredFilterRule, a2 as columnFiltersToLayeredRules, a3 as completeLayeredFilterRules, a4 as decodeLayeredFilterRules, a5 as encodeLayeredFilterRules, a6 as isCompleteLayeredFilterRule, a7 as isLayeredFilterOperator, a8 as layeredFilterMatchesValue, a9 as layeredFilterNeedsValue, aa as layeredFilterRuleSummary, ab as operatorsForLayeredField } from '../types-Dx5hJgCi.js';
1
+ export { _ as LAYERED_FILTER_OPERATORS, $ as LAYERED_FILTER_OPERATOR_LABELS, a0 as LayeredFilterField, a1 as LayeredFilterOperator, L as LayeredFilterRule, a2 as columnFiltersToLayeredRules, a3 as completeLayeredFilterRules, a4 as decodeLayeredFilterRules, a5 as encodeLayeredFilterRules, a6 as isCompleteLayeredFilterRule, a7 as isLayeredFilterOperator, a8 as layeredFilterMatchesValue, a9 as layeredFilterNeedsValue, aa as layeredFilterRuleSummary, ab as operatorsForLayeredField } from '../types-afUz93xo.js';
2
2
  import '../content-transfer.js';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -12,3 +12,4 @@ import './facets.js';
12
12
  import './spreadsheet.js';
13
13
  import './grid-selection.js';
14
14
  import './date-filter.js';
15
+ import './menu-targets.js';
@@ -26,6 +26,7 @@ __export(menu_targets_exports, {
26
26
  MATRX_HEADER_COLUMN_ATTR: () => MATRX_HEADER_COLUMN_ATTR,
27
27
  MATRX_ROW_ID_ATTR: () => MATRX_ROW_ID_ATTR,
28
28
  MATRX_TABLE_ID_ATTR: () => MATRX_TABLE_ID_ATTR,
29
+ MATRX_TABLE_MENU_ICON_NAMES: () => MATRX_TABLE_MENU_ICON_NAMES,
29
30
  describeMenuLevel: () => describeMenuLevel,
30
31
  readMenuTarget: () => readMenuTarget
31
32
  });
@@ -74,4 +75,30 @@ function describeMenuLevel(payload) {
74
75
  return "Table";
75
76
  }
76
77
  }
78
+ var MATRX_TABLE_MENU_ICON_NAMES = [
79
+ "copy",
80
+ "copy-plus",
81
+ "scissors",
82
+ "clipboard-paste",
83
+ "eraser",
84
+ "pencil",
85
+ "plus",
86
+ "trash",
87
+ "eye-off",
88
+ "arrow-up-a-z",
89
+ "arrow-down-a-z",
90
+ "arrow-up-down",
91
+ "arrow-down-to-line",
92
+ "palette",
93
+ "paintbrush",
94
+ "panel-left",
95
+ "panel-right",
96
+ "settings",
97
+ "sigma",
98
+ "zap",
99
+ "history",
100
+ "link",
101
+ "tag",
102
+ "filter"
103
+ ];
77
104
  //# sourceMappingURL=menu-targets.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-table/menu-targets.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/data-table — WHAT WAS RIGHT-CLICKED.\n *\n * A spreadsheet's right-click menu is not one menu: it is four, and which one\n * you get is decided by where the pointer was. A cell, the row it sits in, the\n * column it sits under, and — when the click lands inside an extended range —\n * the selection. The canonical table used to expose exactly ONE of those four\n * (the row), so every surface that wanted the other three forked the table.\n *\n * This module is the pure half of the fix: it reads the four levels off the\n * DOM anchors the table already renders, and nothing more. It does not build a\n * menu, name an action, or know what a menu is — the platform's universal\n * right-click menu is an application concern and stays there. The table's job\n * is to answer, truthfully, WHICH thing the person aimed at.\n *\n * THE PRECEDENCE, and why it is this way:\n *\n * selection > cell > column > row > table\n *\n * A click inside an extended range means the range, because that is what the\n * person is looking at and what every spreadsheet acts on — taking the single\n * cell under the cursor there would silently throw away their selection. A\n * header click is the column even though the header is inside the table. A\n * click on a row but outside any cell (the checkbox gutter, the actions cell)\n * is the row. Anything else in the table is the table.\n *\n * Pure: no React, no package state. It reads `data-*` attributes and returns a\n * plain object. Nothing here throws on any input.\n */\n\n/** Which of the four things the pointer was aimed at. */\nexport type MatrxTableMenuLevel = \"cell\" | \"row\" | \"column\" | \"selection\" | \"table\";\n\nexport interface MatrxTableMenuTarget {\n /** The table instance the click landed in. */\n tableId: string;\n level: MatrxTableMenuLevel;\n /** Present for \"cell\" and \"row\". */\n rowId?: string;\n /** Present for \"cell\" and \"column\". */\n columnId?: string;\n}\n\n/**\n * Attribute names, in one place, so the renderer and the reader cannot drift.\n * `data-row-id` predates this module and keeps its name: it is already read by\n * the row-drag preview and by every host that wired the row menu.\n */\nexport const MATRX_TABLE_ID_ATTR = \"data-matrx-table-id\";\nexport const MATRX_ROW_ID_ATTR = \"data-row-id\";\nexport const MATRX_CELL_ROW_ATTR = \"data-matrx-cell-row\";\nexport const MATRX_CELL_COLUMN_ATTR = \"data-matrx-table-column-id\";\nexport const MATRX_HEADER_COLUMN_ATTR = \"data-matrx-header-column-id\";\n/** Marks a cell that is inside the current extended range. */\nexport const MATRX_CELL_SELECTED_ATTR = \"data-matrx-cell-selected\";\n\ninterface MinimalElement {\n closest(selector: string): MinimalElement | null;\n getAttribute(name: string): string | null;\n}\n\nfunction attr(element: MinimalElement | null, name: string): string | undefined {\n if (!element) return undefined;\n const found = element.closest(`[${name}]`);\n const value = found?.getAttribute(name);\n return value === null || value === undefined || value === \"\" ? undefined : value;\n}\n\n/**\n * Read the four levels off the clicked element.\n *\n * Returns null when the click did not land in a canonical table at all — which\n * is the signal a host needs to leave its own page-level menu alone.\n */\nexport function readMenuTarget(\n target: MinimalElement | null,\n): MatrxTableMenuTarget | null {\n const tableId = attr(target, MATRX_TABLE_ID_ATTR);\n if (!tableId) return null;\n\n const headerColumnId = attr(target, MATRX_HEADER_COLUMN_ATTR);\n if (headerColumnId) return { tableId, level: \"column\", columnId: headerColumnId };\n\n // A cell's row comes from the `<tr>` it is in, which every table renders —\n // the spreadsheet cursor's own row attribute is a second opinion, used only\n // when the table is windowed sideways and the row wrapper is not an ancestor.\n const cellColumnId = attr(target, MATRX_CELL_COLUMN_ATTR);\n const cellRowId = attr(target, MATRX_ROW_ID_ATTR) ?? attr(target, MATRX_CELL_ROW_ATTR);\n const rowId = cellRowId;\n\n if (cellRowId && cellColumnId) {\n // Inside an extended range the SELECTION is what the person sees and what\n // the menu must act on. A range of one cell is not a range — acting on it\n // as a selection would give a lone cell the plural verbs.\n const selected = attr(target, MATRX_CELL_SELECTED_ATTR) === \"true\";\n if (selected) return { tableId, level: \"selection\", rowId: cellRowId, columnId: cellColumnId };\n return { tableId, level: \"cell\", rowId: cellRowId, columnId: cellColumnId };\n }\n\n if (rowId) return { tableId, level: \"row\", rowId };\n return { tableId, level: \"table\" };\n}\n\n/**\n * The payload the table hands its host for the level that was clicked.\n *\n * The host builds the menu from this and never reaches back into the table.\n * Every level carries the table's own count of what it is acting on, because a\n * menu that says \"Clear 12 cells\" and clears 40 is worse than one that says\n * nothing.\n */\nexport type MatrxTableMenuPayload<T = unknown> =\n | {\n level: \"cell\";\n row: T;\n rowId: string;\n columnId: string;\n columnLabel: string;\n value: unknown;\n /** The cell's value as the table displays it, for Copy. */\n text: string;\n controls: unknown;\n }\n | { level: \"row\"; row: T; rowId: string; controls: unknown }\n | {\n level: \"column\";\n columnId: string;\n columnLabel: string;\n /** Every rendered row's value for this column, in visual order. */\n values: unknown[];\n }\n | {\n level: \"selection\";\n rowIds: string[];\n columnIds: string[];\n /** The selected rectangle as an Excel/Sheets-compatible TSV block. */\n tsv: string;\n cellCount: number;\n }\n | { level: \"table\"; rowCount: number; columnIds: string[] };\n\n/** Plain-English name for a level, for a host that labels its sections. */\nexport function describeMenuLevel(payload: MatrxTableMenuPayload): string {\n switch (payload.level) {\n case \"cell\":\n return payload.columnLabel;\n case \"row\":\n return \"Row\";\n case \"column\":\n return payload.columnLabel;\n case \"selection\": {\n const cells = payload.cellCount;\n return `${cells} ${cells === 1 ? \"cell\" : \"cells\"}`;\n }\n case \"table\":\n return \"Table\";\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgDO,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,2BAA2B;AAEjC,IAAM,2BAA2B;AAOxC,SAAS,KAAK,SAAgC,MAAkC;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ,QAAQ,IAAI,IAAI,GAAG;AACzC,QAAM,QAAQ,OAAO,aAAa,IAAI;AACtC,SAAO,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,SAAY;AAC7E;AAQO,SAAS,eACd,QAC6B;AAC7B,QAAM,UAAU,KAAK,QAAQ,mBAAmB;AAChD,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,iBAAiB,KAAK,QAAQ,wBAAwB;AAC5D,MAAI,eAAgB,QAAO,EAAE,SAAS,OAAO,UAAU,UAAU,eAAe;AAKhF,QAAM,eAAe,KAAK,QAAQ,sBAAsB;AACxD,QAAM,YAAY,KAAK,QAAQ,iBAAiB,KAAK,KAAK,QAAQ,mBAAmB;AACrF,QAAM,QAAQ;AAEd,MAAI,aAAa,cAAc;AAI7B,UAAM,WAAW,KAAK,QAAQ,wBAAwB,MAAM;AAC5D,QAAI,SAAU,QAAO,EAAE,SAAS,OAAO,aAAa,OAAO,WAAW,UAAU,aAAa;AAC7F,WAAO,EAAE,SAAS,OAAO,QAAQ,OAAO,WAAW,UAAU,aAAa;AAAA,EAC5E;AAEA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM;AACjD,SAAO,EAAE,SAAS,OAAO,QAAQ;AACnC;AAyCO,SAAS,kBAAkB,SAAwC;AACxE,UAAQ,QAAQ,OAAO;AAAA,IACrB,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK,aAAa;AAChB,YAAM,QAAQ,QAAQ;AACtB,aAAO,GAAG,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO;AAAA,IACnD;AAAA,IACA,KAAK;AACH,aAAO;AAAA,EACX;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/data-table/menu-targets.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/data-table — WHAT WAS RIGHT-CLICKED.\n *\n * A spreadsheet's right-click menu is not one menu: it is four, and which one\n * you get is decided by where the pointer was. A cell, the row it sits in, the\n * column it sits under, and — when the click lands inside an extended range —\n * the selection. The canonical table used to expose exactly ONE of those four\n * (the row), so every surface that wanted the other three forked the table.\n *\n * This module is the pure half of the fix: it reads the four levels off the\n * DOM anchors the table already renders, and nothing more. It does not build a\n * menu, name an action, or know what a menu is — the platform's universal\n * right-click menu is an application concern and stays there. The table's job\n * is to answer, truthfully, WHICH thing the person aimed at.\n *\n * THE PRECEDENCE, and why it is this way:\n *\n * selection > cell > column > row > table\n *\n * A click inside an extended range means the range, because that is what the\n * person is looking at and what every spreadsheet acts on — taking the single\n * cell under the cursor there would silently throw away their selection. A\n * header click is the column even though the header is inside the table. A\n * click on a row but outside any cell (the checkbox gutter, the actions cell)\n * is the row. Anything else in the table is the table.\n *\n * Pure: no React, no package state. It reads `data-*` attributes and returns a\n * plain object. Nothing here throws on any input.\n */\n\n/** Which of the four things the pointer was aimed at. */\nexport type MatrxTableMenuLevel = \"cell\" | \"row\" | \"column\" | \"selection\" | \"table\";\n\nexport interface MatrxTableMenuTarget {\n /** The table instance the click landed in. */\n tableId: string;\n level: MatrxTableMenuLevel;\n /** Present for \"cell\" and \"row\". */\n rowId?: string;\n /** Present for \"cell\" and \"column\". */\n columnId?: string;\n}\n\n/**\n * Attribute names, in one place, so the renderer and the reader cannot drift.\n * `data-row-id` predates this module and keeps its name: it is already read by\n * the row-drag preview and by every host that wired the row menu.\n */\nexport const MATRX_TABLE_ID_ATTR = \"data-matrx-table-id\";\nexport const MATRX_ROW_ID_ATTR = \"data-row-id\";\nexport const MATRX_CELL_ROW_ATTR = \"data-matrx-cell-row\";\nexport const MATRX_CELL_COLUMN_ATTR = \"data-matrx-table-column-id\";\nexport const MATRX_HEADER_COLUMN_ATTR = \"data-matrx-header-column-id\";\n/** Marks a cell that is inside the current extended range. */\nexport const MATRX_CELL_SELECTED_ATTR = \"data-matrx-cell-selected\";\n\ninterface MinimalElement {\n closest(selector: string): MinimalElement | null;\n getAttribute(name: string): string | null;\n}\n\nfunction attr(element: MinimalElement | null, name: string): string | undefined {\n if (!element) return undefined;\n const found = element.closest(`[${name}]`);\n const value = found?.getAttribute(name);\n return value === null || value === undefined || value === \"\" ? undefined : value;\n}\n\n/**\n * Read the four levels off the clicked element.\n *\n * Returns null when the click did not land in a canonical table at all — which\n * is the signal a host needs to leave its own page-level menu alone.\n */\nexport function readMenuTarget(\n target: MinimalElement | null,\n): MatrxTableMenuTarget | null {\n const tableId = attr(target, MATRX_TABLE_ID_ATTR);\n if (!tableId) return null;\n\n const headerColumnId = attr(target, MATRX_HEADER_COLUMN_ATTR);\n if (headerColumnId) return { tableId, level: \"column\", columnId: headerColumnId };\n\n // A cell's row comes from the `<tr>` it is in, which every table renders —\n // the spreadsheet cursor's own row attribute is a second opinion, used only\n // when the table is windowed sideways and the row wrapper is not an ancestor.\n const cellColumnId = attr(target, MATRX_CELL_COLUMN_ATTR);\n const cellRowId = attr(target, MATRX_ROW_ID_ATTR) ?? attr(target, MATRX_CELL_ROW_ATTR);\n const rowId = cellRowId;\n\n if (cellRowId && cellColumnId) {\n // Inside an extended range the SELECTION is what the person sees and what\n // the menu must act on. A range of one cell is not a range — acting on it\n // as a selection would give a lone cell the plural verbs.\n const selected = attr(target, MATRX_CELL_SELECTED_ATTR) === \"true\";\n if (selected) return { tableId, level: \"selection\", rowId: cellRowId, columnId: cellColumnId };\n return { tableId, level: \"cell\", rowId: cellRowId, columnId: cellColumnId };\n }\n\n if (rowId) return { tableId, level: \"row\", rowId };\n return { tableId, level: \"table\" };\n}\n\n/**\n * The payload the table hands its host for the level that was clicked.\n *\n * The host builds the menu from this and never reaches back into the table.\n * Every level carries the table's own count of what it is acting on, because a\n * menu that says \"Clear 12 cells\" and clears 40 is worse than one that says\n * nothing.\n */\nexport type MatrxTableMenuPayload<T = unknown> =\n | {\n level: \"cell\";\n row: T;\n rowId: string;\n columnId: string;\n columnLabel: string;\n value: unknown;\n /** The cell's value as the table displays it, for Copy. */\n text: string;\n controls: unknown;\n }\n | { level: \"row\"; row: T; rowId: string; controls: unknown }\n | {\n level: \"column\";\n columnId: string;\n columnLabel: string;\n /** Every rendered row's value for this column, in visual order. */\n values: unknown[];\n }\n | {\n level: \"selection\";\n rowIds: string[];\n columnIds: string[];\n /** The selected rectangle as an Excel/Sheets-compatible TSV block. */\n tsv: string;\n cellCount: number;\n }\n | { level: \"table\"; rowCount: number; columnIds: string[] };\n\n/** Plain-English name for a level, for a host that labels its sections. */\nexport function describeMenuLevel(payload: MatrxTableMenuPayload): string {\n switch (payload.level) {\n case \"cell\":\n return payload.columnLabel;\n case \"row\":\n return \"Row\";\n case \"column\":\n return payload.columnLabel;\n case \"selection\": {\n const cells = payload.cellCount;\n return `${cells} ${cells === 1 ? \"cell\" : \"cells\"}`;\n }\n case \"table\":\n return \"Table\";\n }\n}\n\n/* ─────────────────────────────────────────────────────────────────────────────\n * WHAT THE MENU SAYS — neutral sections a consumer adds to a level.\n *\n * The levels above answer WHICH thing was aimed at. A consumer that knows what\n * can be DONE to it (cut a cell, insert a row, rename a column, colour by a\n * field) says so with these plain objects, and the host draws them in its own\n * right-click menu — the package never renders a menu and the consumer never\n * learns which menu system the application uses. The same item shape fills the\n * column header's own menu (`MatrxColumnDef.headerMenu`), so a column action\n * reads the same in the header menu and on a right-click.\n *\n * The vocabulary is the one the /data grid's menus already speak (sections\n * Cell, Row, Column, Table; label, icon, shortcut hint, disabled-with-reason,\n * submenu), so those builders can move onto it unchanged in meaning.\n * ─────────────────────────────────────────────────────────────────────────── */\n\n/**\n * The icons a table menu item may name, lucide ids. Names, not glyphs: the\n * host maps each to its own icon set, so the package ships no icon library and\n * a host that has no glyph for a name simply draws the label.\n */\nexport const MATRX_TABLE_MENU_ICON_NAMES = [\n \"copy\",\n \"copy-plus\",\n \"scissors\",\n \"clipboard-paste\",\n \"eraser\",\n \"pencil\",\n \"plus\",\n \"trash\",\n \"eye-off\",\n \"arrow-up-a-z\",\n \"arrow-down-a-z\",\n \"arrow-up-down\",\n \"arrow-down-to-line\",\n \"palette\",\n \"paintbrush\",\n \"panel-left\",\n \"panel-right\",\n \"settings\",\n \"sigma\",\n \"zap\",\n \"history\",\n \"link\",\n \"tag\",\n \"filter\",\n] as const;\nexport type MatrxTableMenuIconName = (typeof MATRX_TABLE_MENU_ICON_NAMES)[number];\n\n/** One thing a person can do from a table menu. */\nexport interface MatrxTableMenuItem {\n /** Stable id, unique within its section. */\n id: string;\n label: string;\n icon?: MatrxTableMenuIconName | undefined;\n /** Right-aligned keyboard hint, e.g. \"⌘C\". A hint only — the table binds no key for it. */\n shortcut?: string | undefined;\n /**\n * PRESENT MEANS UNAVAILABLE HERE, and the sentence says why. The item keeps\n * its place and its label and cannot fire — a menu whose rows appear and\n * vanish per table teaches nothing; a disabled row that says \"View-only\n * table\" teaches where the limit is.\n */\n disabledReason?: string | undefined;\n /** Drawn in the destructive style (delete, clear). */\n destructive?: boolean | undefined;\n /** What choosing it does. Omit only for an item that carries a `submenu`. */\n onSelect?: (() => void) | undefined;\n /** Nested choices (a colour, a column to label by). */\n submenu?: MatrxTableMenuItem[] | undefined;\n}\n\n/** A titled group of items — \"Cell\", \"Row\", \"Column\", \"Table\". */\nexport interface MatrxTableMenuSection {\n /** Stable id, unique within one menu. */\n id: string;\n title: string;\n items: MatrxTableMenuItem[];\n /**\n * The section for the thing that was actually right-clicked. The host draws\n * it first and inline; at most one section per menu should carry it.\n */\n primary?: boolean | undefined;\n}\n\n/**\n * Asked by the host when its menu opens, with the level the pointer was aimed\n * at (read by `readMenuTarget`). The table resolves the row, column or\n * selection itself and answers the consumer's sections for it — an empty list\n * when there are none.\n */\nexport type MatrxTableMenuSectionsResolver = (target: MatrxTableMenuTarget) => MatrxTableMenuSection[];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgDO,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,2BAA2B;AAEjC,IAAM,2BAA2B;AAOxC,SAAS,KAAK,SAAgC,MAAkC;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ,QAAQ,IAAI,IAAI,GAAG;AACzC,QAAM,QAAQ,OAAO,aAAa,IAAI;AACtC,SAAO,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,SAAY;AAC7E;AAQO,SAAS,eACd,QAC6B;AAC7B,QAAM,UAAU,KAAK,QAAQ,mBAAmB;AAChD,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,iBAAiB,KAAK,QAAQ,wBAAwB;AAC5D,MAAI,eAAgB,QAAO,EAAE,SAAS,OAAO,UAAU,UAAU,eAAe;AAKhF,QAAM,eAAe,KAAK,QAAQ,sBAAsB;AACxD,QAAM,YAAY,KAAK,QAAQ,iBAAiB,KAAK,KAAK,QAAQ,mBAAmB;AACrF,QAAM,QAAQ;AAEd,MAAI,aAAa,cAAc;AAI7B,UAAM,WAAW,KAAK,QAAQ,wBAAwB,MAAM;AAC5D,QAAI,SAAU,QAAO,EAAE,SAAS,OAAO,aAAa,OAAO,WAAW,UAAU,aAAa;AAC7F,WAAO,EAAE,SAAS,OAAO,QAAQ,OAAO,WAAW,UAAU,aAAa;AAAA,EAC5E;AAEA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM;AACjD,SAAO,EAAE,SAAS,OAAO,QAAQ;AACnC;AAyCO,SAAS,kBAAkB,SAAwC;AACxE,UAAQ,QAAQ,OAAO;AAAA,IACrB,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK,aAAa;AAChB,YAAM,QAAQ,QAAQ;AACtB,aAAO,GAAG,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO;AAAA,IACnD;AAAA,IACA,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAuBO,IAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
@@ -104,5 +104,53 @@ type MatrxTableMenuPayload<T = unknown> = {
104
104
  };
105
105
  /** Plain-English name for a level, for a host that labels its sections. */
106
106
  declare function describeMenuLevel(payload: MatrxTableMenuPayload): string;
107
+ /**
108
+ * The icons a table menu item may name, lucide ids. Names, not glyphs: the
109
+ * host maps each to its own icon set, so the package ships no icon library and
110
+ * a host that has no glyph for a name simply draws the label.
111
+ */
112
+ declare const MATRX_TABLE_MENU_ICON_NAMES: readonly ["copy", "copy-plus", "scissors", "clipboard-paste", "eraser", "pencil", "plus", "trash", "eye-off", "arrow-up-a-z", "arrow-down-a-z", "arrow-up-down", "arrow-down-to-line", "palette", "paintbrush", "panel-left", "panel-right", "settings", "sigma", "zap", "history", "link", "tag", "filter"];
113
+ type MatrxTableMenuIconName = (typeof MATRX_TABLE_MENU_ICON_NAMES)[number];
114
+ /** One thing a person can do from a table menu. */
115
+ interface MatrxTableMenuItem {
116
+ /** Stable id, unique within its section. */
117
+ id: string;
118
+ label: string;
119
+ icon?: MatrxTableMenuIconName | undefined;
120
+ /** Right-aligned keyboard hint, e.g. "⌘C". A hint only — the table binds no key for it. */
121
+ shortcut?: string | undefined;
122
+ /**
123
+ * PRESENT MEANS UNAVAILABLE HERE, and the sentence says why. The item keeps
124
+ * its place and its label and cannot fire — a menu whose rows appear and
125
+ * vanish per table teaches nothing; a disabled row that says "View-only
126
+ * table" teaches where the limit is.
127
+ */
128
+ disabledReason?: string | undefined;
129
+ /** Drawn in the destructive style (delete, clear). */
130
+ destructive?: boolean | undefined;
131
+ /** What choosing it does. Omit only for an item that carries a `submenu`. */
132
+ onSelect?: (() => void) | undefined;
133
+ /** Nested choices (a colour, a column to label by). */
134
+ submenu?: MatrxTableMenuItem[] | undefined;
135
+ }
136
+ /** A titled group of items — "Cell", "Row", "Column", "Table". */
137
+ interface MatrxTableMenuSection {
138
+ /** Stable id, unique within one menu. */
139
+ id: string;
140
+ title: string;
141
+ items: MatrxTableMenuItem[];
142
+ /**
143
+ * The section for the thing that was actually right-clicked. The host draws
144
+ * it first and inline; at most one section per menu should carry it.
145
+ */
146
+ primary?: boolean | undefined;
147
+ }
148
+ /**
149
+ * Asked by the host when its menu opens, with the level the pointer was aimed
150
+ * at (read by `readMenuTarget`). The table resolves the row, column or
151
+ * selection itself and answers the consumer's sections for it — an empty list
152
+ * when there are none.
153
+ */
154
+ type MatrxTableMenuSectionsResolver = (target: MatrxTableMenuTarget) => MatrxTableMenuSection[];
107
155
 
108
- export { MATRX_CELL_COLUMN_ATTR, MATRX_CELL_ROW_ATTR, MATRX_CELL_SELECTED_ATTR, MATRX_HEADER_COLUMN_ATTR, MATRX_ROW_ID_ATTR, MATRX_TABLE_ID_ATTR, type MatrxTableMenuLevel, type MatrxTableMenuPayload, type MatrxTableMenuTarget, describeMenuLevel, readMenuTarget };
156
+ export { MATRX_CELL_COLUMN_ATTR, MATRX_CELL_ROW_ATTR, MATRX_CELL_SELECTED_ATTR, MATRX_HEADER_COLUMN_ATTR, MATRX_ROW_ID_ATTR, MATRX_TABLE_ID_ATTR, MATRX_TABLE_MENU_ICON_NAMES, type MatrxTableMenuIconName, type MatrxTableMenuItem, type MatrxTableMenuLevel, type MatrxTableMenuPayload, type MatrxTableMenuSection, type MatrxTableMenuSectionsResolver, type MatrxTableMenuTarget, describeMenuLevel, readMenuTarget };
@@ -104,5 +104,53 @@ type MatrxTableMenuPayload<T = unknown> = {
104
104
  };
105
105
  /** Plain-English name for a level, for a host that labels its sections. */
106
106
  declare function describeMenuLevel(payload: MatrxTableMenuPayload): string;
107
+ /**
108
+ * The icons a table menu item may name, lucide ids. Names, not glyphs: the
109
+ * host maps each to its own icon set, so the package ships no icon library and
110
+ * a host that has no glyph for a name simply draws the label.
111
+ */
112
+ declare const MATRX_TABLE_MENU_ICON_NAMES: readonly ["copy", "copy-plus", "scissors", "clipboard-paste", "eraser", "pencil", "plus", "trash", "eye-off", "arrow-up-a-z", "arrow-down-a-z", "arrow-up-down", "arrow-down-to-line", "palette", "paintbrush", "panel-left", "panel-right", "settings", "sigma", "zap", "history", "link", "tag", "filter"];
113
+ type MatrxTableMenuIconName = (typeof MATRX_TABLE_MENU_ICON_NAMES)[number];
114
+ /** One thing a person can do from a table menu. */
115
+ interface MatrxTableMenuItem {
116
+ /** Stable id, unique within its section. */
117
+ id: string;
118
+ label: string;
119
+ icon?: MatrxTableMenuIconName | undefined;
120
+ /** Right-aligned keyboard hint, e.g. "⌘C". A hint only — the table binds no key for it. */
121
+ shortcut?: string | undefined;
122
+ /**
123
+ * PRESENT MEANS UNAVAILABLE HERE, and the sentence says why. The item keeps
124
+ * its place and its label and cannot fire — a menu whose rows appear and
125
+ * vanish per table teaches nothing; a disabled row that says "View-only
126
+ * table" teaches where the limit is.
127
+ */
128
+ disabledReason?: string | undefined;
129
+ /** Drawn in the destructive style (delete, clear). */
130
+ destructive?: boolean | undefined;
131
+ /** What choosing it does. Omit only for an item that carries a `submenu`. */
132
+ onSelect?: (() => void) | undefined;
133
+ /** Nested choices (a colour, a column to label by). */
134
+ submenu?: MatrxTableMenuItem[] | undefined;
135
+ }
136
+ /** A titled group of items — "Cell", "Row", "Column", "Table". */
137
+ interface MatrxTableMenuSection {
138
+ /** Stable id, unique within one menu. */
139
+ id: string;
140
+ title: string;
141
+ items: MatrxTableMenuItem[];
142
+ /**
143
+ * The section for the thing that was actually right-clicked. The host draws
144
+ * it first and inline; at most one section per menu should carry it.
145
+ */
146
+ primary?: boolean | undefined;
147
+ }
148
+ /**
149
+ * Asked by the host when its menu opens, with the level the pointer was aimed
150
+ * at (read by `readMenuTarget`). The table resolves the row, column or
151
+ * selection itself and answers the consumer's sections for it — an empty list
152
+ * when there are none.
153
+ */
154
+ type MatrxTableMenuSectionsResolver = (target: MatrxTableMenuTarget) => MatrxTableMenuSection[];
107
155
 
108
- export { MATRX_CELL_COLUMN_ATTR, MATRX_CELL_ROW_ATTR, MATRX_CELL_SELECTED_ATTR, MATRX_HEADER_COLUMN_ATTR, MATRX_ROW_ID_ATTR, MATRX_TABLE_ID_ATTR, type MatrxTableMenuLevel, type MatrxTableMenuPayload, type MatrxTableMenuTarget, describeMenuLevel, readMenuTarget };
156
+ export { MATRX_CELL_COLUMN_ATTR, MATRX_CELL_ROW_ATTR, MATRX_CELL_SELECTED_ATTR, MATRX_HEADER_COLUMN_ATTR, MATRX_ROW_ID_ATTR, MATRX_TABLE_ID_ATTR, MATRX_TABLE_MENU_ICON_NAMES, type MatrxTableMenuIconName, type MatrxTableMenuItem, type MatrxTableMenuLevel, type MatrxTableMenuPayload, type MatrxTableMenuSection, type MatrxTableMenuSectionsResolver, type MatrxTableMenuTarget, describeMenuLevel, readMenuTarget };
@@ -44,6 +44,32 @@ function describeMenuLevel(payload) {
44
44
  return "Table";
45
45
  }
46
46
  }
47
+ var MATRX_TABLE_MENU_ICON_NAMES = [
48
+ "copy",
49
+ "copy-plus",
50
+ "scissors",
51
+ "clipboard-paste",
52
+ "eraser",
53
+ "pencil",
54
+ "plus",
55
+ "trash",
56
+ "eye-off",
57
+ "arrow-up-a-z",
58
+ "arrow-down-a-z",
59
+ "arrow-up-down",
60
+ "arrow-down-to-line",
61
+ "palette",
62
+ "paintbrush",
63
+ "panel-left",
64
+ "panel-right",
65
+ "settings",
66
+ "sigma",
67
+ "zap",
68
+ "history",
69
+ "link",
70
+ "tag",
71
+ "filter"
72
+ ];
47
73
  export {
48
74
  MATRX_CELL_COLUMN_ATTR,
49
75
  MATRX_CELL_ROW_ATTR,
@@ -51,6 +77,7 @@ export {
51
77
  MATRX_HEADER_COLUMN_ATTR,
52
78
  MATRX_ROW_ID_ATTR,
53
79
  MATRX_TABLE_ID_ATTR,
80
+ MATRX_TABLE_MENU_ICON_NAMES,
54
81
  describeMenuLevel,
55
82
  readMenuTarget
56
83
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-table/menu-targets.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/data-table — WHAT WAS RIGHT-CLICKED.\n *\n * A spreadsheet's right-click menu is not one menu: it is four, and which one\n * you get is decided by where the pointer was. A cell, the row it sits in, the\n * column it sits under, and — when the click lands inside an extended range —\n * the selection. The canonical table used to expose exactly ONE of those four\n * (the row), so every surface that wanted the other three forked the table.\n *\n * This module is the pure half of the fix: it reads the four levels off the\n * DOM anchors the table already renders, and nothing more. It does not build a\n * menu, name an action, or know what a menu is — the platform's universal\n * right-click menu is an application concern and stays there. The table's job\n * is to answer, truthfully, WHICH thing the person aimed at.\n *\n * THE PRECEDENCE, and why it is this way:\n *\n * selection > cell > column > row > table\n *\n * A click inside an extended range means the range, because that is what the\n * person is looking at and what every spreadsheet acts on — taking the single\n * cell under the cursor there would silently throw away their selection. A\n * header click is the column even though the header is inside the table. A\n * click on a row but outside any cell (the checkbox gutter, the actions cell)\n * is the row. Anything else in the table is the table.\n *\n * Pure: no React, no package state. It reads `data-*` attributes and returns a\n * plain object. Nothing here throws on any input.\n */\n\n/** Which of the four things the pointer was aimed at. */\nexport type MatrxTableMenuLevel = \"cell\" | \"row\" | \"column\" | \"selection\" | \"table\";\n\nexport interface MatrxTableMenuTarget {\n /** The table instance the click landed in. */\n tableId: string;\n level: MatrxTableMenuLevel;\n /** Present for \"cell\" and \"row\". */\n rowId?: string;\n /** Present for \"cell\" and \"column\". */\n columnId?: string;\n}\n\n/**\n * Attribute names, in one place, so the renderer and the reader cannot drift.\n * `data-row-id` predates this module and keeps its name: it is already read by\n * the row-drag preview and by every host that wired the row menu.\n */\nexport const MATRX_TABLE_ID_ATTR = \"data-matrx-table-id\";\nexport const MATRX_ROW_ID_ATTR = \"data-row-id\";\nexport const MATRX_CELL_ROW_ATTR = \"data-matrx-cell-row\";\nexport const MATRX_CELL_COLUMN_ATTR = \"data-matrx-table-column-id\";\nexport const MATRX_HEADER_COLUMN_ATTR = \"data-matrx-header-column-id\";\n/** Marks a cell that is inside the current extended range. */\nexport const MATRX_CELL_SELECTED_ATTR = \"data-matrx-cell-selected\";\n\ninterface MinimalElement {\n closest(selector: string): MinimalElement | null;\n getAttribute(name: string): string | null;\n}\n\nfunction attr(element: MinimalElement | null, name: string): string | undefined {\n if (!element) return undefined;\n const found = element.closest(`[${name}]`);\n const value = found?.getAttribute(name);\n return value === null || value === undefined || value === \"\" ? undefined : value;\n}\n\n/**\n * Read the four levels off the clicked element.\n *\n * Returns null when the click did not land in a canonical table at all — which\n * is the signal a host needs to leave its own page-level menu alone.\n */\nexport function readMenuTarget(\n target: MinimalElement | null,\n): MatrxTableMenuTarget | null {\n const tableId = attr(target, MATRX_TABLE_ID_ATTR);\n if (!tableId) return null;\n\n const headerColumnId = attr(target, MATRX_HEADER_COLUMN_ATTR);\n if (headerColumnId) return { tableId, level: \"column\", columnId: headerColumnId };\n\n // A cell's row comes from the `<tr>` it is in, which every table renders —\n // the spreadsheet cursor's own row attribute is a second opinion, used only\n // when the table is windowed sideways and the row wrapper is not an ancestor.\n const cellColumnId = attr(target, MATRX_CELL_COLUMN_ATTR);\n const cellRowId = attr(target, MATRX_ROW_ID_ATTR) ?? attr(target, MATRX_CELL_ROW_ATTR);\n const rowId = cellRowId;\n\n if (cellRowId && cellColumnId) {\n // Inside an extended range the SELECTION is what the person sees and what\n // the menu must act on. A range of one cell is not a range — acting on it\n // as a selection would give a lone cell the plural verbs.\n const selected = attr(target, MATRX_CELL_SELECTED_ATTR) === \"true\";\n if (selected) return { tableId, level: \"selection\", rowId: cellRowId, columnId: cellColumnId };\n return { tableId, level: \"cell\", rowId: cellRowId, columnId: cellColumnId };\n }\n\n if (rowId) return { tableId, level: \"row\", rowId };\n return { tableId, level: \"table\" };\n}\n\n/**\n * The payload the table hands its host for the level that was clicked.\n *\n * The host builds the menu from this and never reaches back into the table.\n * Every level carries the table's own count of what it is acting on, because a\n * menu that says \"Clear 12 cells\" and clears 40 is worse than one that says\n * nothing.\n */\nexport type MatrxTableMenuPayload<T = unknown> =\n | {\n level: \"cell\";\n row: T;\n rowId: string;\n columnId: string;\n columnLabel: string;\n value: unknown;\n /** The cell's value as the table displays it, for Copy. */\n text: string;\n controls: unknown;\n }\n | { level: \"row\"; row: T; rowId: string; controls: unknown }\n | {\n level: \"column\";\n columnId: string;\n columnLabel: string;\n /** Every rendered row's value for this column, in visual order. */\n values: unknown[];\n }\n | {\n level: \"selection\";\n rowIds: string[];\n columnIds: string[];\n /** The selected rectangle as an Excel/Sheets-compatible TSV block. */\n tsv: string;\n cellCount: number;\n }\n | { level: \"table\"; rowCount: number; columnIds: string[] };\n\n/** Plain-English name for a level, for a host that labels its sections. */\nexport function describeMenuLevel(payload: MatrxTableMenuPayload): string {\n switch (payload.level) {\n case \"cell\":\n return payload.columnLabel;\n case \"row\":\n return \"Row\";\n case \"column\":\n return payload.columnLabel;\n case \"selection\": {\n const cells = payload.cellCount;\n return `${cells} ${cells === 1 ? \"cell\" : \"cells\"}`;\n }\n case \"table\":\n return \"Table\";\n }\n}\n"],"mappings":";;AAgDO,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,2BAA2B;AAEjC,IAAM,2BAA2B;AAOxC,SAAS,KAAK,SAAgC,MAAkC;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ,QAAQ,IAAI,IAAI,GAAG;AACzC,QAAM,QAAQ,OAAO,aAAa,IAAI;AACtC,SAAO,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,SAAY;AAC7E;AAQO,SAAS,eACd,QAC6B;AAC7B,QAAM,UAAU,KAAK,QAAQ,mBAAmB;AAChD,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,iBAAiB,KAAK,QAAQ,wBAAwB;AAC5D,MAAI,eAAgB,QAAO,EAAE,SAAS,OAAO,UAAU,UAAU,eAAe;AAKhF,QAAM,eAAe,KAAK,QAAQ,sBAAsB;AACxD,QAAM,YAAY,KAAK,QAAQ,iBAAiB,KAAK,KAAK,QAAQ,mBAAmB;AACrF,QAAM,QAAQ;AAEd,MAAI,aAAa,cAAc;AAI7B,UAAM,WAAW,KAAK,QAAQ,wBAAwB,MAAM;AAC5D,QAAI,SAAU,QAAO,EAAE,SAAS,OAAO,aAAa,OAAO,WAAW,UAAU,aAAa;AAC7F,WAAO,EAAE,SAAS,OAAO,QAAQ,OAAO,WAAW,UAAU,aAAa;AAAA,EAC5E;AAEA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM;AACjD,SAAO,EAAE,SAAS,OAAO,QAAQ;AACnC;AAyCO,SAAS,kBAAkB,SAAwC;AACxE,UAAQ,QAAQ,OAAO;AAAA,IACrB,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK,aAAa;AAChB,YAAM,QAAQ,QAAQ;AACtB,aAAO,GAAG,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO;AAAA,IACnD;AAAA,IACA,KAAK;AACH,aAAO;AAAA,EACX;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/data-table/menu-targets.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/data-table — WHAT WAS RIGHT-CLICKED.\n *\n * A spreadsheet's right-click menu is not one menu: it is four, and which one\n * you get is decided by where the pointer was. A cell, the row it sits in, the\n * column it sits under, and — when the click lands inside an extended range —\n * the selection. The canonical table used to expose exactly ONE of those four\n * (the row), so every surface that wanted the other three forked the table.\n *\n * This module is the pure half of the fix: it reads the four levels off the\n * DOM anchors the table already renders, and nothing more. It does not build a\n * menu, name an action, or know what a menu is — the platform's universal\n * right-click menu is an application concern and stays there. The table's job\n * is to answer, truthfully, WHICH thing the person aimed at.\n *\n * THE PRECEDENCE, and why it is this way:\n *\n * selection > cell > column > row > table\n *\n * A click inside an extended range means the range, because that is what the\n * person is looking at and what every spreadsheet acts on — taking the single\n * cell under the cursor there would silently throw away their selection. A\n * header click is the column even though the header is inside the table. A\n * click on a row but outside any cell (the checkbox gutter, the actions cell)\n * is the row. Anything else in the table is the table.\n *\n * Pure: no React, no package state. It reads `data-*` attributes and returns a\n * plain object. Nothing here throws on any input.\n */\n\n/** Which of the four things the pointer was aimed at. */\nexport type MatrxTableMenuLevel = \"cell\" | \"row\" | \"column\" | \"selection\" | \"table\";\n\nexport interface MatrxTableMenuTarget {\n /** The table instance the click landed in. */\n tableId: string;\n level: MatrxTableMenuLevel;\n /** Present for \"cell\" and \"row\". */\n rowId?: string;\n /** Present for \"cell\" and \"column\". */\n columnId?: string;\n}\n\n/**\n * Attribute names, in one place, so the renderer and the reader cannot drift.\n * `data-row-id` predates this module and keeps its name: it is already read by\n * the row-drag preview and by every host that wired the row menu.\n */\nexport const MATRX_TABLE_ID_ATTR = \"data-matrx-table-id\";\nexport const MATRX_ROW_ID_ATTR = \"data-row-id\";\nexport const MATRX_CELL_ROW_ATTR = \"data-matrx-cell-row\";\nexport const MATRX_CELL_COLUMN_ATTR = \"data-matrx-table-column-id\";\nexport const MATRX_HEADER_COLUMN_ATTR = \"data-matrx-header-column-id\";\n/** Marks a cell that is inside the current extended range. */\nexport const MATRX_CELL_SELECTED_ATTR = \"data-matrx-cell-selected\";\n\ninterface MinimalElement {\n closest(selector: string): MinimalElement | null;\n getAttribute(name: string): string | null;\n}\n\nfunction attr(element: MinimalElement | null, name: string): string | undefined {\n if (!element) return undefined;\n const found = element.closest(`[${name}]`);\n const value = found?.getAttribute(name);\n return value === null || value === undefined || value === \"\" ? undefined : value;\n}\n\n/**\n * Read the four levels off the clicked element.\n *\n * Returns null when the click did not land in a canonical table at all — which\n * is the signal a host needs to leave its own page-level menu alone.\n */\nexport function readMenuTarget(\n target: MinimalElement | null,\n): MatrxTableMenuTarget | null {\n const tableId = attr(target, MATRX_TABLE_ID_ATTR);\n if (!tableId) return null;\n\n const headerColumnId = attr(target, MATRX_HEADER_COLUMN_ATTR);\n if (headerColumnId) return { tableId, level: \"column\", columnId: headerColumnId };\n\n // A cell's row comes from the `<tr>` it is in, which every table renders —\n // the spreadsheet cursor's own row attribute is a second opinion, used only\n // when the table is windowed sideways and the row wrapper is not an ancestor.\n const cellColumnId = attr(target, MATRX_CELL_COLUMN_ATTR);\n const cellRowId = attr(target, MATRX_ROW_ID_ATTR) ?? attr(target, MATRX_CELL_ROW_ATTR);\n const rowId = cellRowId;\n\n if (cellRowId && cellColumnId) {\n // Inside an extended range the SELECTION is what the person sees and what\n // the menu must act on. A range of one cell is not a range — acting on it\n // as a selection would give a lone cell the plural verbs.\n const selected = attr(target, MATRX_CELL_SELECTED_ATTR) === \"true\";\n if (selected) return { tableId, level: \"selection\", rowId: cellRowId, columnId: cellColumnId };\n return { tableId, level: \"cell\", rowId: cellRowId, columnId: cellColumnId };\n }\n\n if (rowId) return { tableId, level: \"row\", rowId };\n return { tableId, level: \"table\" };\n}\n\n/**\n * The payload the table hands its host for the level that was clicked.\n *\n * The host builds the menu from this and never reaches back into the table.\n * Every level carries the table's own count of what it is acting on, because a\n * menu that says \"Clear 12 cells\" and clears 40 is worse than one that says\n * nothing.\n */\nexport type MatrxTableMenuPayload<T = unknown> =\n | {\n level: \"cell\";\n row: T;\n rowId: string;\n columnId: string;\n columnLabel: string;\n value: unknown;\n /** The cell's value as the table displays it, for Copy. */\n text: string;\n controls: unknown;\n }\n | { level: \"row\"; row: T; rowId: string; controls: unknown }\n | {\n level: \"column\";\n columnId: string;\n columnLabel: string;\n /** Every rendered row's value for this column, in visual order. */\n values: unknown[];\n }\n | {\n level: \"selection\";\n rowIds: string[];\n columnIds: string[];\n /** The selected rectangle as an Excel/Sheets-compatible TSV block. */\n tsv: string;\n cellCount: number;\n }\n | { level: \"table\"; rowCount: number; columnIds: string[] };\n\n/** Plain-English name for a level, for a host that labels its sections. */\nexport function describeMenuLevel(payload: MatrxTableMenuPayload): string {\n switch (payload.level) {\n case \"cell\":\n return payload.columnLabel;\n case \"row\":\n return \"Row\";\n case \"column\":\n return payload.columnLabel;\n case \"selection\": {\n const cells = payload.cellCount;\n return `${cells} ${cells === 1 ? \"cell\" : \"cells\"}`;\n }\n case \"table\":\n return \"Table\";\n }\n}\n\n/* ─────────────────────────────────────────────────────────────────────────────\n * WHAT THE MENU SAYS — neutral sections a consumer adds to a level.\n *\n * The levels above answer WHICH thing was aimed at. A consumer that knows what\n * can be DONE to it (cut a cell, insert a row, rename a column, colour by a\n * field) says so with these plain objects, and the host draws them in its own\n * right-click menu — the package never renders a menu and the consumer never\n * learns which menu system the application uses. The same item shape fills the\n * column header's own menu (`MatrxColumnDef.headerMenu`), so a column action\n * reads the same in the header menu and on a right-click.\n *\n * The vocabulary is the one the /data grid's menus already speak (sections\n * Cell, Row, Column, Table; label, icon, shortcut hint, disabled-with-reason,\n * submenu), so those builders can move onto it unchanged in meaning.\n * ─────────────────────────────────────────────────────────────────────────── */\n\n/**\n * The icons a table menu item may name, lucide ids. Names, not glyphs: the\n * host maps each to its own icon set, so the package ships no icon library and\n * a host that has no glyph for a name simply draws the label.\n */\nexport const MATRX_TABLE_MENU_ICON_NAMES = [\n \"copy\",\n \"copy-plus\",\n \"scissors\",\n \"clipboard-paste\",\n \"eraser\",\n \"pencil\",\n \"plus\",\n \"trash\",\n \"eye-off\",\n \"arrow-up-a-z\",\n \"arrow-down-a-z\",\n \"arrow-up-down\",\n \"arrow-down-to-line\",\n \"palette\",\n \"paintbrush\",\n \"panel-left\",\n \"panel-right\",\n \"settings\",\n \"sigma\",\n \"zap\",\n \"history\",\n \"link\",\n \"tag\",\n \"filter\",\n] as const;\nexport type MatrxTableMenuIconName = (typeof MATRX_TABLE_MENU_ICON_NAMES)[number];\n\n/** One thing a person can do from a table menu. */\nexport interface MatrxTableMenuItem {\n /** Stable id, unique within its section. */\n id: string;\n label: string;\n icon?: MatrxTableMenuIconName | undefined;\n /** Right-aligned keyboard hint, e.g. \"⌘C\". A hint only — the table binds no key for it. */\n shortcut?: string | undefined;\n /**\n * PRESENT MEANS UNAVAILABLE HERE, and the sentence says why. The item keeps\n * its place and its label and cannot fire — a menu whose rows appear and\n * vanish per table teaches nothing; a disabled row that says \"View-only\n * table\" teaches where the limit is.\n */\n disabledReason?: string | undefined;\n /** Drawn in the destructive style (delete, clear). */\n destructive?: boolean | undefined;\n /** What choosing it does. Omit only for an item that carries a `submenu`. */\n onSelect?: (() => void) | undefined;\n /** Nested choices (a colour, a column to label by). */\n submenu?: MatrxTableMenuItem[] | undefined;\n}\n\n/** A titled group of items — \"Cell\", \"Row\", \"Column\", \"Table\". */\nexport interface MatrxTableMenuSection {\n /** Stable id, unique within one menu. */\n id: string;\n title: string;\n items: MatrxTableMenuItem[];\n /**\n * The section for the thing that was actually right-clicked. The host draws\n * it first and inline; at most one section per menu should carry it.\n */\n primary?: boolean | undefined;\n}\n\n/**\n * Asked by the host when its menu opens, with the level the pointer was aimed\n * at (read by `readMenuTarget`). The table resolves the row, column or\n * selection itself and answers the consumer's sections for it — an empty list\n * when there are none.\n */\nexport type MatrxTableMenuSectionsResolver = (target: MatrxTableMenuTarget) => MatrxTableMenuSection[];\n"],"mappings":";;AAgDO,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,2BAA2B;AAEjC,IAAM,2BAA2B;AAOxC,SAAS,KAAK,SAAgC,MAAkC;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ,QAAQ,IAAI,IAAI,GAAG;AACzC,QAAM,QAAQ,OAAO,aAAa,IAAI;AACtC,SAAO,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,SAAY;AAC7E;AAQO,SAAS,eACd,QAC6B;AAC7B,QAAM,UAAU,KAAK,QAAQ,mBAAmB;AAChD,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,iBAAiB,KAAK,QAAQ,wBAAwB;AAC5D,MAAI,eAAgB,QAAO,EAAE,SAAS,OAAO,UAAU,UAAU,eAAe;AAKhF,QAAM,eAAe,KAAK,QAAQ,sBAAsB;AACxD,QAAM,YAAY,KAAK,QAAQ,iBAAiB,KAAK,KAAK,QAAQ,mBAAmB;AACrF,QAAM,QAAQ;AAEd,MAAI,aAAa,cAAc;AAI7B,UAAM,WAAW,KAAK,QAAQ,wBAAwB,MAAM;AAC5D,QAAI,SAAU,QAAO,EAAE,SAAS,OAAO,aAAa,OAAO,WAAW,UAAU,aAAa;AAC7F,WAAO,EAAE,SAAS,OAAO,QAAQ,OAAO,WAAW,UAAU,aAAa;AAAA,EAC5E;AAEA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM;AACjD,SAAO,EAAE,SAAS,OAAO,QAAQ;AACnC;AAyCO,SAAS,kBAAkB,SAAwC;AACxE,UAAQ,QAAQ,OAAO;AAAA,IACrB,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK,aAAa;AAChB,YAAM,QAAQ,QAAQ;AACtB,aAAO,GAAG,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO;AAAA,IACnD;AAAA,IACA,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAuBO,IAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}