impact-nova 2.5.11 → 2.5.13

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 (111) hide show
  1. package/dist/components/data/data-table/build-column-tree-from-grid.d.ts +1 -0
  2. package/dist/components/data/data-table/build-column-tree-from-grid.js +55 -50
  3. package/dist/components/data/data-table/column-picker-authority.d.ts +34 -0
  4. package/dist/components/data/data-table/column-picker-authority.js +116 -0
  5. package/dist/components/data/data-table/column-picker-drop-validation.d.ts +7 -0
  6. package/dist/components/data/data-table/column-picker-drop-validation.js +37 -0
  7. package/dist/components/data/data-table/data-table-column-apply.d.ts +2 -0
  8. package/dist/components/data/data-table/data-table-column-apply.js +84 -73
  9. package/dist/components/data/data-table/data-table-column-list-tree-apply.d.ts +10 -0
  10. package/dist/components/data/data-table/data-table-column-list-tree-apply.js +79 -0
  11. package/dist/components/data/data-table/data-table-column-list.js +94 -64
  12. package/dist/components/data/data-table/data-table-column-tree-cache.d.ts +12 -6
  13. package/dist/components/data/data-table/data-table-column-tree-cache.js +250 -131
  14. package/dist/components/data/data-table/data-table-flat-column-virtual-rows.d.ts +26 -0
  15. package/dist/components/data/data-table/data-table-flat-column-virtual-rows.js +195 -0
  16. package/dist/components/data/data-table/data-table-picker-column-eligibility.d.ts +1 -0
  17. package/dist/components/data/data-table/data-table-picker-column-eligibility.js +7 -0
  18. package/dist/components/data/data-table/patch-column-tree-indicators-from-grid.d.ts +5 -0
  19. package/dist/components/data/data-table/patch-column-tree-indicators-from-grid.js +28 -11
  20. package/dist/components/data/data-table/reconcile-column-tree-from-grid.d.ts +12 -0
  21. package/dist/components/data/data-table/reconcile-column-tree-from-grid.js +12 -0
  22. package/dist/components/data/data-table/use-data-table-column-list-sync.d.ts +10 -0
  23. package/dist/components/data/data-table/use-data-table-column-list-sync.js +250 -196
  24. package/dist/components/data/expandable-list-item/expandable-list-item-attributes.js +2 -3
  25. package/dist/components/data/expandable-list-item/expandable-list-item-metrics.js +2 -3
  26. package/dist/components/data/nested-list/components/NestedListContent.d.ts +10 -2
  27. package/dist/components/data/nested-list/components/NestedListContent.js +253 -138
  28. package/dist/components/data/nested-list/components/SortableItem.d.ts +2 -1
  29. package/dist/components/data/nested-list/components/SortableItem.js +62 -60
  30. package/dist/components/data/nested-list/hooks/useNestedListDragDrop.d.ts +10 -20
  31. package/dist/components/data/nested-list/hooks/useNestedListDragDrop.js +44 -233
  32. package/dist/components/data/nested-list/hooks/useVirtualNestedListDrag.d.ts +24 -0
  33. package/dist/components/data/nested-list/hooks/useVirtualNestedListDrag.js +128 -0
  34. package/dist/components/data/nested-list/nested-list-constants.d.ts +4 -1
  35. package/dist/components/data/nested-list/nested-list-constants.js +10 -4
  36. package/dist/components/data/nested-list/nested-list-display-utils.d.ts +11 -0
  37. package/dist/components/data/nested-list/nested-list-display-utils.js +74 -0
  38. package/dist/components/data/nested-list/nested-list-drop-engine.d.ts +31 -0
  39. package/dist/components/data/nested-list/nested-list-drop-engine.js +209 -0
  40. package/dist/components/data/nested-list/nested-list-flat-virtual-handlers.d.ts +26 -0
  41. package/dist/components/data/nested-list/nested-list-flat-virtual-handlers.js +67 -0
  42. package/dist/components/data/nested-list/nested-list-item-tree-mutations.d.ts +4 -0
  43. package/dist/components/data/nested-list/nested-list-item-tree-mutations.js +43 -0
  44. package/dist/components/data/nested-list/nested-list-lazy-flat-rows.d.ts +3 -0
  45. package/dist/components/data/nested-list/nested-list-lazy-flat-rows.js +34 -0
  46. package/dist/components/data/nested-list/nested-list-row-estimates.d.ts +9 -0
  47. package/dist/components/data/nested-list/nested-list-row-estimates.js +14 -0
  48. package/dist/components/data/nested-list/nested-list-scroll-anchor.d.ts +36 -0
  49. package/dist/components/data/nested-list/nested-list-scroll-anchor.js +47 -0
  50. package/dist/components/data/nested-list/nested-list-virtual-drop.d.ts +59 -0
  51. package/dist/components/data/nested-list/nested-list-virtual-drop.js +341 -0
  52. package/dist/components/data/nested-list/nested-list.js +322 -336
  53. package/dist/components/data/nested-list/nested-list.types.d.ts +17 -0
  54. package/dist/components/data-display/calendar/calendar-apply-validation.d.ts +0 -2
  55. package/dist/components/data-display/calendar/calendar-apply-validation.js +67 -64
  56. package/dist/components/data-display/calendar/calendar-day-picker-components.d.ts +2 -4
  57. package/dist/components/data-display/calendar/calendar-day-picker-components.js +152 -163
  58. package/dist/components/data-display/calendar/calendar-day-picker-view.js +76 -77
  59. package/dist/components/data-display/calendar/calendar-fiscal-period-nav.d.ts +0 -6
  60. package/dist/components/data-display/calendar/calendar-fiscal-period-nav.js +18 -34
  61. package/dist/components/data-display/calendar/calendar-month-utils.d.ts +0 -5
  62. package/dist/components/data-display/calendar/calendar-month-utils.js +64 -85
  63. package/dist/components/data-display/calendar/calendar-selection-appearance.d.ts +0 -1
  64. package/dist/components/data-display/calendar/calendar-selection-appearance.js +30 -31
  65. package/dist/components/data-display/calendar/calendar-week-number-cell.d.ts +1 -2
  66. package/dist/components/data-display/calendar/calendar-week-number-cell.js +41 -55
  67. package/dist/components/data-display/calendar/calendar-year-panes.d.ts +0 -2
  68. package/dist/components/data-display/calendar/calendar-year-panes.js +1 -3
  69. package/dist/components/data-display/calendar/calendar.js +143 -146
  70. package/dist/components/data-display/calendar/calendar.types.d.ts +0 -5
  71. package/dist/components/data-display/calendar/use-calendar-day-picker-config.d.ts +1 -2
  72. package/dist/components/data-display/calendar/use-calendar-day-picker-config.js +74 -79
  73. package/dist/components/data-display/calendar/use-calendar-state.d.ts +1 -3
  74. package/dist/components/data-display/calendar/use-calendar-state.js +325 -403
  75. package/dist/components/feedback/tooltip/tab-tooltip-render.js +2 -3
  76. package/dist/components/flows/command-palette/shortcut-settings.js +10 -10
  77. package/dist/components/flows/filter-panel/filter-panel.js +4 -4
  78. package/dist/components/forms/date-picker/date-picker.js +58 -60
  79. package/dist/components/forms/date-picker/date-picker.types.d.ts +0 -5
  80. package/dist/components/forms/date-picker/date-range-picker.js +64 -66
  81. package/dist/components/forms/date-picker/fiscal-pass-through.d.ts +0 -1
  82. package/dist/components/forms/date-picker/fiscal-pass-through.js +2 -3
  83. package/dist/components/forms/date-picker/month-picker.js +59 -61
  84. package/dist/components/forms/date-picker/month-range-picker.js +61 -63
  85. package/dist/components/forms/date-picker/multi-date-picker.js +49 -51
  86. package/dist/components/forms/date-picker/multi-month-picker.js +47 -49
  87. package/dist/components/forms/date-picker/multi-quarter-picker.js +44 -46
  88. package/dist/components/forms/date-picker/multi-week-picker.js +51 -53
  89. package/dist/components/forms/date-picker/multi-year-picker.js +46 -48
  90. package/dist/components/forms/date-picker/quarter-picker.js +44 -46
  91. package/dist/components/forms/date-picker/quarter-range-picker.js +43 -45
  92. package/dist/components/forms/date-picker/week-picker.js +66 -68
  93. package/dist/components/forms/date-picker/week-range-picker.js +80 -82
  94. package/dist/components/forms/date-picker/year-picker.js +45 -47
  95. package/dist/components/forms/date-picker/year-range-picker.js +45 -47
  96. package/dist/components/forms/select/components/SelectTriggerValue.js +2 -3
  97. package/dist/components/forms/select/components/Submenu.js +2 -3
  98. package/dist/form-react/Fields/DateInputField.js +50 -51
  99. package/dist/form-react/Fields/DateRangeField.js +13 -14
  100. package/dist/form-react/Fields/MonthRangeField.js +28 -29
  101. package/dist/form-react/Fields/MultiMonthPickerField.js +12 -13
  102. package/dist/form-react/Fields/MultiWeekPickerField.js +17 -18
  103. package/dist/form-react/Fields/WeekRangePicker.js +21 -22
  104. package/dist/form-react/types/fields.types.d.ts +0 -12
  105. package/dist/impact-nova.css +1 -1
  106. package/dist/lib/hash/fnv1a128.d.ts +16 -0
  107. package/dist/lib/hash/fnv1a128.js +21 -0
  108. package/dist/llms/rules/ag-grid.js +1 -1
  109. package/package.json +1 -1
  110. package/dist/components/data-display/calendar/calendar-available-view.d.ts +0 -27
  111. package/dist/components/data-display/calendar/calendar-available-view.js +0 -85
@@ -0,0 +1,16 @@
1
+ /**
2
+ * FNV-1a 128-bit hash implemented as two independent 64-bit halves.
3
+ * Produces a 32-char hex string deterministically from a stream of string tokens.
4
+ *
5
+ * Used for column-tree fingerprints where O(N) CPU is acceptable but multi-MB
6
+ * string allocations are not. The 128-bit width makes collision probability
7
+ * negligible (~1 in 2^128) while keeping output fixed at 32 bytes.
8
+ */
9
+ export declare class Fnv1a128Hasher {
10
+ private high;
11
+ private low;
12
+ update(input: string): this;
13
+ digest(): string;
14
+ reset(): this;
15
+ }
16
+ export declare function fnv1a128(input: string): string;
@@ -0,0 +1,21 @@
1
+ const s = BigInt("0x6c62272e07bb0142"), n = BigInt("0x62b821756295c58d"), o = BigInt("0x0000000001000000"), r = BigInt("0x000000000000013b"), f = BigInt("0xffffffffffffffff");
2
+ class g {
3
+ high = s;
4
+ low = n;
5
+ update(h) {
6
+ for (let t = 0; t < h.length; t++) {
7
+ const i = BigInt(h.charCodeAt(t) & 255);
8
+ this.high = (this.high ^ i) * o & f, this.low = (this.low ^ i) * r & f;
9
+ }
10
+ return this;
11
+ }
12
+ digest() {
13
+ return this.high.toString(16).padStart(16, "0") + this.low.toString(16).padStart(16, "0");
14
+ }
15
+ reset() {
16
+ return this.high = s, this.low = n, this;
17
+ }
18
+ }
19
+ export {
20
+ g as Fnv1a128Hasher
21
+ };
@@ -1 +1 @@
1
- export default "# AG Grid & Data Table — Mandatory Rules\n\n**When working with DataTable, or any data table that uses AG Grid (including Impact Nova's grid components), the following rules are mandatory. Do not deviate.**\n\n---\n\n## 1. Use AG Grid documentation only\n\n- **Refer only to AG Grid's official documentation** for grid behavior, APIs, column definitions, filtering, sorting, and all grid features.\n- Do not rely on third-party tutorials or generic \"data grid\" patterns that contradict or bypass AG Grid's docs.\n- Official docs: [ag-grid.com/documentation](https://www.ag-grid.com/documentation/) (React: [AG Grid React](https://www.ag-grid.com/react-data-grid/)).\n\n---\n\n## 2. Follow AG Grid recommended patterns\n\n- **Always follow AG Grid's recommended patterns** for:\n - Column definitions (`ColDef`, `ColGroupDef`)\n - Cell renderers and editors\n - Filtering, sorting, and row selection\n - API ref access (`gridRef.current?.api`)\n - Theming and styling (AG Grid theme / CSS variables)\n- Do not invent custom patterns that bypass or replace AG Grid's intended usage.\n\n---\n\n## 3. Use the AG Grid API only\n\n- **Rely on the AG Grid API only** for all grid operations:\n - Getting/setting data, refreshing cells, updating columns\n - Export (CSV/Excel if using Enterprise)\n - Filter/sort state, row selection, pinned columns\n - Any behavior that AG Grid exposes via its API\n- Do not manipulate the DOM or internal structure of the grid directly. Do not use non-API workarounds unless AG Grid docs explicitly suggest them.\n\n---\n\n## 4. Collaboration with ag-mcp (when installed)\n\n- **If the user has installed the ag-mcp server** (AG Grid MCP), this MCP can collaborate with it:\n - Use **ag-mcp** for AG Grid–specific questions: API reference, column config, React integration, and official examples.\n - Use **impact-nova-mcp** for Impact Nova wrappers (`DataTable`, `DataTableContent`, `processBackendColumnDefs`, `BackendColDef`, cell renderers from `impact-nova/ag-grid-react/cell-renderers`).\n - When generating or editing grid code: prefer fetching AG Grid details from ag-mcp when available, and combine with Impact Nova's DataTable/column/cell-renderer patterns from this MCP.\n- If ag-mcp is not installed, still follow rules 1–3 using AG Grid's official documentation only.\n\n---\n\n**Summary:** For any code or design involving AG Grid or Impact Nova's DataTable (which uses AG Grid), use **only** AG Grid documentation, **only** AG Grid recommended patterns, and **only** the AG Grid API—with no deviation. When ag-mcp is available, use it to get accurate AG Grid API and docs; use this MCP for Impact Nova–specific integration.\n\n---\n\n## 5. Mandatory Data Table component usage\n\n**CRITICAL RULE:** If you see a table in a screenshot, or if the user asks for a table by default, you **MUST** use **Impact Nova DataTable** from `impact-nova/data-table`. **Do not build a raw AG Grid or HTML table.**\n\n```tsx\nimport {\n DataTable,\n DataTableContent,\n DataTableToolbar,\n useDataTable,\n} from 'impact-nova/data-table';\n```\n\nA typical Default Data Table in Impact Nova features this exact structure:\n\n```tsx\nconst DataTableWithFilters = () => {\n const { tStory } = useStorybookStoryI18n();\n const [sheetOpen, setSheetOpen] = useState(false);\n const [activeTab, setActiveTab] = useState(\"columns\");\n const [showFilterStrip, setShowFilterStrip] = useState(false);\n\n return (\n <div className=\"h-[800px] w-full p-8 bg-slate-50 flex flex-col items-center justify-center\">\n <div className=\"w-full max-w-[1200px] h-[600px] bg-white rounded-lg overflow-hidden flex flex-col [box-shadow:0px_0px_4px_0px_rgba(0,0,0,0.12)]\">\n <DataTable className=\"h-full\">\n <DataTableToolbar className=\"border-b border-[#e6e8f0]\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"font-bold text-sm text-slate-800\">Sales Report</h2>\n </div>\n \n <div className=\"flex items-center gap-2\">\n <Button \n variant=\"secondary\" \n size=\"icon\" \n onClick={() => setShowFilterStrip(!showFilterStrip)}\n aria-label={showFilterStrip ? tStory('filterStrip.hideFilters') : tStory('filterStrip.showFilters')}\n >\n {showFilterStrip ? <FunnelHide size=\"xs\" /> : <FunnelShow size=\"xs\" />}\n </Button>\n\n <div className=\"h-4 w-[1px] bg-[#e6e8f0] mx-1\" />\n\n {/* Scoped Sheet for Settings - Renders inside this relative container */}\n <DataTableSheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <DropdownMenu>\n <DataTableViewMenuTrigger />\n <DataTableViewMenuContent>\n <DataTableViewMenuSettingsItem />\n <DataTableViewMenuDensity />\n </DataTableViewMenuContent>\n </DropdownMenu>\n \n <DataTableSheetContent>\n <DataTableSheetHeader title=\"Table Settings\" />\n <Tabs value={activeTab} onValueChange={setActiveTab} variant=\"line\" hideInactiveLabel tooltipClassName=\"z-[110]\" className=\"flex-1 flex flex-col min-h-0 w-full px-4\">\n <TabsList>\n <TabsTrigger \n value=\"columns\" \n icon={<Column size={16} />}\n >\n Columns\n </TabsTrigger>\n <TabsTrigger \n value=\"format\" \n icon={<Font size={16} />}\n >\n Format\n </TabsTrigger>\n <TabsTrigger \n value=\"custom-filters\" \n icon={<Filter size={16} />}\n >\n Filters\n </TabsTrigger>\n </TabsList>\n \n <TabsContent value=\"columns\" className=\"flex-1 min-h-0 relative p-0 data-[state=inactive]:hidden mt-0\">\n <div className=\"flex-1 h-full p-2\">\n <DataTableColumnList />\n </div>\n </TabsContent>\n <TabsContent value=\"format\" className=\"flex-1 min-h-0 relative p-4 pt-1 data-[state=inactive]:hidden overflow-y-auto mt-0\">\n <DataTableFormatOptions />\n </TabsContent>\n <TabsContent value=\"custom-filters\" className=\"flex-1 min-h-0 relative p-4 pt-1 data-[state=inactive]:hidden overflow-y-auto mt-0\">\n <div className=\"flex flex-col gap-4 text-sm text-[#60697d]\">\n <p>Custom filter configuration would go here.</p>\n <Button variant=\"outline\" className=\"w-full justify-start\">\n + Add Condition\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n </DataTableSheetContent>\n </DataTableSheet>\n </div>\n </DataTableToolbar>\n \n <DataTableContent\n rowData={rowData}\n columnDefs={columnDefs}\n // Standard AG Grid props work here\n pagination={true}\n paginationPageSize={20}\n />\n </DataTable>\n </div>\n <p className=\"mt-4 text-slate-400 text-sm\">\n Note: The settings panel opens *inside* the table container, respecting its boundaries.\n </p>\n </div>\n );\n};\n```\nNote: This component (`DataTable`) internally uses `ag-grid-react` as a customized version. Rely on it rather than naked AG Grid.\n\n---\n\n## 6. Built-in Clipboard Handlers for JSON Objects\n\n**All DataTable and AG Grid instances in Impact Nova automatically support copying and pasting JSON objects.**\n\nThe `AgGridWrapper` component includes built-in clipboard handlers that:\n- **When copying**: Automatically stringify object values to JSON format\n- **When pasting**: Automatically parse JSON strings back to objects\n\n### How it works\n\n```tsx\n// When you copy a cell with an object value like:\n{ \n wp: { value: 6479, _isDisabled: true, cellMetadata: {...} },\n iaf: { value: 6090, _isDisabled: true, cellMetadata: {...} }\n}\n\n// It's automatically converted and split into separate Excel columns:\n// Column 1 Column 2\n// WP: 6479 IAF: 6090\n\n// Technical metadata (_isDisabled, cellMetadata) is hidden for clarity\n// Only the meaningful \"value\" is shown - perfect for PMs and non-technical users\n// Tab-separated format automatically expands into multiple columns in Excel\n```\n\n**Example with simple properties:**\n```tsx\n// Copy this object:\n{ name: \"John\", age: 30, status: \"active\" }\n\n// Expands into 3 Excel columns:\n// Column 1 Column 2 Column 3\n// name: John age: 30 status: active\n```\n\n**When pasting:**\n```tsx\n// You can paste JSON strings and they'll be parsed back to objects:\n'{\"name\":\"Jane\",\"age\":25}' → { name: \"Jane\", age: 25 }\n```\n\n**Benefits:**\n- ✅ Simple and readable for non-technical users (PMs, stakeholders)\n- ✅ Extracts only meaningful values, hides technical metadata\n- ✅ Automatically expands object properties into separate Excel columns\n- ✅ Perfect for analysis - each property gets its own column\n- ✅ Works seamlessly for both single-cell and multi-cell copy operations\n- ✅ Still supports pasting JSON back for developers\n\n### Usage\n\n**No configuration needed!** This works automatically for all DataTable instances:\n\n```tsx\n<DataTable>\n <DataTableContent\n rowData={data}\n columnDefs={columns}\n // Clipboard handlers are already active ✅\n />\n</DataTable>\n```\n\n### Override if needed\n\nYou can override the default behavior by passing your own handlers:\n\n```tsx\n<DataTableContent\n rowData={data}\n columnDefs={columns}\n processCellForClipboard={(params) => {\n // Custom copy logic\n return customFormat(params.value);\n }}\n processCellFromClipboard={(params) => {\n // Custom paste logic\n return customParse(params.value);\n }}\n/>\n```\n\n**Key benefits:**\n- ✅ Works automatically for all tables\n- ✅ No code duplication needed\n- ✅ Handles complex object structures\n- ✅ Gracefully falls back to string if JSON parsing fails\n- ✅ Can be overridden when custom behavior is needed\n\n---\n\n## 6. Column autosize on container resize\n\n**AgGridWrapper** (from `impact-nova/ag-grid-react`) automatically re-runs `autoSizeStrategy` when the grid container width changes (accordion expand, viewport resize, responsive layout).\n\n- Pass `autoSizeStrategy` to DataTable/AgGridWrapper props (e.g. `{ type: 'fitCellContents', scaleUpToFitGridWidth: true }`).\n- **Do not** add app-level ResizeObserver hacks or deferred grid mount delays — the framework handles this.\n- When applying saved column views, wait for `firstDataRendered` or `newColumnsLoaded` grid events before applying column state — not `setTimeout(0)`.\n- Strip `width`/`flex` from saved column state when applying structural views so autosize can recalculate correctly.\n\n---\n\n## 6b. Truncated text in custom cell renderers\n\n**Do not** use `OverflowTooltip` inside AG Grid cells. Use the grid-native tooltip API via **`useAgGridTruncationTooltip`** from `impact-nova/ag-grid-react`:\n\n```tsx\nimport { useAgGridTruncationTooltip } from 'impact-nova/ag-grid-react';\n\nconst MyCellRenderer = (params: ICellRendererParams) => {\n const textRef = useRef<HTMLSpanElement>(null);\n const displayValue = String(params.value ?? '');\n\n useAgGridTruncationTooltip(\n params.setTooltip,\n displayValue,\n textRef,\n 'ellipsis',\n 'cell',\n );\n\n return (\n <span ref={textRef} className=\"truncate\">\n {displayValue}\n </span>\n );\n};\n```\n\n- Pass AG Grid's `setTooltip` callback (from `ICellRendererParams` / header APIs).\n- Apply `truncate` (or `line-clamp`) on the measured element.\n- `location` is `'cell' | 'leaf' | 'group'` — affects tooltip show mode wiring.\n- For general (non-grid) truncated labels, use `OverflowTooltip` from `impact-nova/tooltip` instead.\n\n---\n\n## 7. AG Grid v36 version pin (mandatory)\n\nImpact Nova **2.2.0+** targets **AG Grid v36**. Consumer apps must install all three packages at the **exact same version**:\n\n```bash\nnpm install ag-grid-community@36.0.1 ag-grid-react@36.0.1 ag-grid-enterprise@36.0.1\n```\n\n**Rules:**\n\n- Pin `36.0.1` in `package.json` — do not float `^36` across community/react/enterprise.\n- Vite/webpack: `resolve.dedupe: ['ag-grid-community', 'ag-grid-enterprise', 'ag-grid-react']` (see `create-impact-nova` template).\n- **Do not** import legacy AG Grid CSS theme files (`ag-grid.css`, `ag-theme-*`). Impact Nova `AgGridWrapper` applies the v36 Quartz theme (`themeQuartz.withPart(iconSetMaterial)`) via the `theme` prop.\n- Column settings (`DataTableColumnList`) sync pin/sort/filter from the live `GridApi` — do not mirror column state in app-level React state.\n\n**Column settings sheet pattern (scroll-safe — use compound layout components):**\n\n```tsx\n<DataTableSheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <DataTableSheetContent>\n <DataTableSheetHeader title=\"Table Settings\" />\n <DataTableSheetBody>\n <DataTableSheetSection>{savedViews}</DataTableSheetSection>\n <DataTableSheetTabs value={tab} onValueChange={setTab} variant=\"line\" className=\"px-4\">\n <DataTableSheetTabsList>\n <TabsTrigger value=\"columns\">Columns</TabsTrigger>\n <TabsTrigger value=\"format\">Format</TabsTrigger>\n </DataTableSheetTabsList>\n <DataTableSheetTabPanel layout=\"list\" value=\"columns\" className=\"p-0 pt-2\">\n <DataTableColumnList />\n </DataTableSheetTabPanel>\n <DataTableSheetTabPanel layout=\"scroll\" value=\"format\" className=\"p-4 pt-1\">\n <DataTableFormatOptions />\n </DataTableSheetTabPanel>\n </DataTableSheetTabs>\n </DataTableSheetBody>\n </DataTableSheetContent>\n</DataTableSheet>\n```\n\nDo **not** use raw `Tabs` + manual `overflow-y-auto` in sheet bodies — see `impact-nova://data-table-sheet-layout`.\n\nStorybook reference: `Data Display/DataTable/Sheet layout` (`CanonicalComposition`) and `Column settings scenarios` (`PinSortSearchAndReset`).\n\n---\n";
1
+ export default "# AG Grid & Data Table — Mandatory Rules\n\n**When working with DataTable, or any data table that uses AG Grid (including Impact Nova's grid components), the following rules are mandatory. Do not deviate.**\n\n---\n\n## 1. Use AG Grid documentation only\n\n- **Refer only to AG Grid's official documentation** for grid behavior, APIs, column definitions, filtering, sorting, and all grid features.\n- Do not rely on third-party tutorials or generic \"data grid\" patterns that contradict or bypass AG Grid's docs.\n- Official docs: [ag-grid.com/documentation](https://www.ag-grid.com/documentation/) (React: [AG Grid React](https://www.ag-grid.com/react-data-grid/)).\n\n---\n\n## 2. Follow AG Grid recommended patterns\n\n- **Always follow AG Grid's recommended patterns** for:\n - Column definitions (`ColDef`, `ColGroupDef`)\n - Cell renderers and editors\n - Filtering, sorting, and row selection\n - API ref access (`gridRef.current?.api`)\n - Theming and styling (AG Grid theme / CSS variables)\n- Do not invent custom patterns that bypass or replace AG Grid's intended usage.\n\n---\n\n## 3. Use the AG Grid API only\n\n- **Rely on the AG Grid API only** for all grid operations:\n - Getting/setting data, refreshing cells, updating columns\n - Export (CSV/Excel if using Enterprise)\n - Filter/sort state, row selection, pinned columns\n - Any behavior that AG Grid exposes via its API\n- Do not manipulate the DOM or internal structure of the grid directly. Do not use non-API workarounds unless AG Grid docs explicitly suggest them.\n\n---\n\n## 4. Collaboration with ag-mcp (when installed)\n\n- **If the user has installed the ag-mcp server** (AG Grid MCP), this MCP can collaborate with it:\n - Use **ag-mcp** for AG Grid–specific questions: API reference, column config, React integration, and official examples.\n - Use **impact-nova-mcp** for Impact Nova wrappers (`DataTable`, `DataTableContent`, `processBackendColumnDefs`, `BackendColDef`, cell renderers from `impact-nova/ag-grid-react/cell-renderers`).\n - When generating or editing grid code: prefer fetching AG Grid details from ag-mcp when available, and combine with Impact Nova's DataTable/column/cell-renderer patterns from this MCP.\n- If ag-mcp is not installed, still follow rules 1–3 using AG Grid's official documentation only.\n\n---\n\n**Summary:** For any code or design involving AG Grid or Impact Nova's DataTable (which uses AG Grid), use **only** AG Grid documentation, **only** AG Grid recommended patterns, and **only** the AG Grid API—with no deviation. When ag-mcp is available, use it to get accurate AG Grid API and docs; use this MCP for Impact Nova–specific integration.\n\n---\n\n## 5. Mandatory Data Table component usage\n\n**CRITICAL RULE:** If you see a table in a screenshot, or if the user asks for a table by default, you **MUST** use **Impact Nova DataTable** from `impact-nova/data-table`. **Do not build a raw AG Grid or HTML table.**\n\n```tsx\nimport {\n DataTable,\n DataTableContent,\n DataTableToolbar,\n useDataTable,\n} from 'impact-nova/data-table';\n```\n\nA typical Default Data Table in Impact Nova features this exact structure:\n\n```tsx\nconst DataTableWithFilters = () => {\n const { tStory } = useStorybookStoryI18n();\n const [sheetOpen, setSheetOpen] = useState(false);\n const [activeTab, setActiveTab] = useState(\"columns\");\n const [showFilterStrip, setShowFilterStrip] = useState(false);\n\n return (\n <div className=\"h-[800px] w-full p-8 bg-slate-50 flex flex-col items-center justify-center\">\n <div className=\"w-full max-w-[1200px] h-[600px] bg-white rounded-lg overflow-hidden flex flex-col [box-shadow:0px_0px_4px_0px_rgba(0,0,0,0.12)]\">\n <DataTable className=\"h-full\">\n <DataTableToolbar className=\"border-b border-[#e6e8f0]\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"font-bold text-sm text-slate-800\">Sales Report</h2>\n </div>\n \n <div className=\"flex items-center gap-2\">\n <Button \n variant=\"secondary\" \n size=\"icon\" \n onClick={() => setShowFilterStrip(!showFilterStrip)}\n aria-label={showFilterStrip ? tStory('filterStrip.hideFilters') : tStory('filterStrip.showFilters')}\n >\n {showFilterStrip ? <FunnelHide size=\"xs\" /> : <FunnelShow size=\"xs\" />}\n </Button>\n\n <div className=\"h-4 w-[1px] bg-[#e6e8f0] mx-1\" />\n\n {/* Scoped Sheet for Settings - Renders inside this relative container */}\n <DataTableSheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <DropdownMenu>\n <DataTableViewMenuTrigger />\n <DataTableViewMenuContent>\n <DataTableViewMenuSettingsItem />\n <DataTableViewMenuDensity />\n </DataTableViewMenuContent>\n </DropdownMenu>\n \n <DataTableSheetContent>\n <DataTableSheetHeader title=\"Table Settings\" />\n <Tabs value={activeTab} onValueChange={setActiveTab} variant=\"line\" hideInactiveLabel tooltipClassName=\"z-[110]\" className=\"flex-1 flex flex-col min-h-0 w-full px-4\">\n <TabsList>\n <TabsTrigger \n value=\"columns\" \n icon={<Column size={16} />}\n >\n Columns\n </TabsTrigger>\n <TabsTrigger \n value=\"format\" \n icon={<Font size={16} />}\n >\n Format\n </TabsTrigger>\n <TabsTrigger \n value=\"custom-filters\" \n icon={<Filter size={16} />}\n >\n Filters\n </TabsTrigger>\n </TabsList>\n \n <TabsContent value=\"columns\" className=\"flex-1 min-h-0 relative p-0 data-[state=inactive]:hidden mt-0\">\n <div className=\"flex-1 h-full p-2\">\n <DataTableColumnList />\n </div>\n </TabsContent>\n <TabsContent value=\"format\" className=\"flex-1 min-h-0 relative p-4 pt-1 data-[state=inactive]:hidden overflow-y-auto mt-0\">\n <DataTableFormatOptions />\n </TabsContent>\n <TabsContent value=\"custom-filters\" className=\"flex-1 min-h-0 relative p-4 pt-1 data-[state=inactive]:hidden overflow-y-auto mt-0\">\n <div className=\"flex flex-col gap-4 text-sm text-[#60697d]\">\n <p>Custom filter configuration would go here.</p>\n <Button variant=\"outline\" className=\"w-full justify-start\">\n + Add Condition\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n </DataTableSheetContent>\n </DataTableSheet>\n </div>\n </DataTableToolbar>\n \n <DataTableContent\n rowData={rowData}\n columnDefs={columnDefs}\n // Standard AG Grid props work here\n pagination={true}\n paginationPageSize={20}\n />\n </DataTable>\n </div>\n <p className=\"mt-4 text-slate-400 text-sm\">\n Note: The settings panel opens *inside* the table container, respecting its boundaries.\n </p>\n </div>\n );\n};\n```\nNote: This component (`DataTable`) internally uses `ag-grid-react` as a customized version. Rely on it rather than naked AG Grid.\n\n---\n\n## 6. Built-in Clipboard Handlers for JSON Objects\n\n**All DataTable and AG Grid instances in Impact Nova automatically support copying and pasting JSON objects.**\n\nThe `AgGridWrapper` component includes built-in clipboard handlers that:\n- **When copying**: Automatically stringify object values to JSON format\n- **When pasting**: Automatically parse JSON strings back to objects\n\n### How it works\n\n```tsx\n// When you copy a cell with an object value like:\n{ \n wp: { value: 6479, _isDisabled: true, cellMetadata: {...} },\n iaf: { value: 6090, _isDisabled: true, cellMetadata: {...} }\n}\n\n// It's automatically converted and split into separate Excel columns:\n// Column 1 Column 2\n// WP: 6479 IAF: 6090\n\n// Technical metadata (_isDisabled, cellMetadata) is hidden for clarity\n// Only the meaningful \"value\" is shown - perfect for PMs and non-technical users\n// Tab-separated format automatically expands into multiple columns in Excel\n```\n\n**Example with simple properties:**\n```tsx\n// Copy this object:\n{ name: \"John\", age: 30, status: \"active\" }\n\n// Expands into 3 Excel columns:\n// Column 1 Column 2 Column 3\n// name: John age: 30 status: active\n```\n\n**When pasting:**\n```tsx\n// You can paste JSON strings and they'll be parsed back to objects:\n'{\"name\":\"Jane\",\"age\":25}' → { name: \"Jane\", age: 25 }\n```\n\n**Benefits:**\n- ✅ Simple and readable for non-technical users (PMs, stakeholders)\n- ✅ Extracts only meaningful values, hides technical metadata\n- ✅ Automatically expands object properties into separate Excel columns\n- ✅ Perfect for analysis - each property gets its own column\n- ✅ Works seamlessly for both single-cell and multi-cell copy operations\n- ✅ Still supports pasting JSON back for developers\n\n### Usage\n\n**No configuration needed!** This works automatically for all DataTable instances:\n\n```tsx\n<DataTable>\n <DataTableContent\n rowData={data}\n columnDefs={columns}\n // Clipboard handlers are already active ✅\n />\n</DataTable>\n```\n\n### Override if needed\n\nYou can override the default behavior by passing your own handlers:\n\n```tsx\n<DataTableContent\n rowData={data}\n columnDefs={columns}\n processCellForClipboard={(params) => {\n // Custom copy logic\n return customFormat(params.value);\n }}\n processCellFromClipboard={(params) => {\n // Custom paste logic\n return customParse(params.value);\n }}\n/>\n```\n\n**Key benefits:**\n- ✅ Works automatically for all tables\n- ✅ No code duplication needed\n- ✅ Handles complex object structures\n- ✅ Gracefully falls back to string if JSON parsing fails\n- ✅ Can be overridden when custom behavior is needed\n\n---\n\n## 6. Column autosize on container resize\n\n**AgGridWrapper** (from `impact-nova/ag-grid-react`) automatically re-runs `autoSizeStrategy` when the grid container width changes (accordion expand, viewport resize, responsive layout).\n\n- Pass `autoSizeStrategy` to DataTable/AgGridWrapper props (e.g. `{ type: 'fitCellContents', scaleUpToFitGridWidth: true }`).\n- **Do not** add app-level ResizeObserver hacks or deferred grid mount delays — the framework handles this.\n- When applying saved column views, wait for `firstDataRendered` or `newColumnsLoaded` grid events before applying column state — not `setTimeout(0)`.\n- Strip `width`/`flex` from saved column state when applying structural views so autosize can recalculate correctly.\n\n---\n\n## 6b. Truncated text in custom cell renderers\n\n**Do not** use `OverflowTooltip` inside AG Grid cells. Use the grid-native tooltip API via **`useAgGridTruncationTooltip`** from `impact-nova/ag-grid-react`:\n\n```tsx\nimport { useAgGridTruncationTooltip } from 'impact-nova/ag-grid-react';\n\nconst MyCellRenderer = (params: ICellRendererParams) => {\n const textRef = useRef<HTMLSpanElement>(null);\n const displayValue = String(params.value ?? '');\n\n useAgGridTruncationTooltip(\n params.setTooltip,\n displayValue,\n textRef,\n 'ellipsis',\n 'cell',\n );\n\n return (\n <span ref={textRef} className=\"truncate\">\n {displayValue}\n </span>\n );\n};\n```\n\n- Pass AG Grid's `setTooltip` callback (from `ICellRendererParams` / header APIs).\n- Apply `truncate` (or `line-clamp`) on the measured element.\n- `location` is `'cell' | 'leaf' | 'group'` — affects tooltip show mode wiring.\n- For general (non-grid) truncated labels, use `OverflowTooltip` from `impact-nova/tooltip` instead.\n\n---\n\n## 7. AG Grid v36 version pin (mandatory)\n\nImpact Nova **2.2.0+** targets **AG Grid v36**. Consumer apps must install all three packages at the **exact same version**:\n\n```bash\nnpm install ag-grid-community@36.0.1 ag-grid-react@36.0.1 ag-grid-enterprise@36.0.1\n```\n\n**Rules:**\n\n- Pin `36.0.1` in `package.json` — do not float `^36` across community/react/enterprise.\n- Vite/webpack: `resolve.dedupe: ['ag-grid-community', 'ag-grid-enterprise', 'ag-grid-react']` (see `create-impact-nova` template).\n- **Do not** import legacy AG Grid CSS theme files (`ag-grid.css`, `ag-theme-*`). Impact Nova `AgGridWrapper` applies the v36 Quartz theme (`themeQuartz.withPart(iconSetMaterial)`) via the `theme` prop.\n- Column settings (`DataTableColumnList`) sync pin/sort/filter from the live `GridApi` — do not mirror column state in app-level React state.\n\n**Column settings sheet pattern (scroll-safe — use compound layout components):**\n\n```tsx\n<DataTableSheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <DataTableSheetContent>\n <DataTableSheetHeader title=\"Table Settings\" />\n <DataTableSheetBody>\n <DataTableSheetSection>{savedViews}</DataTableSheetSection>\n <DataTableSheetTabs value={tab} onValueChange={setTab} variant=\"line\" className=\"px-4\">\n <DataTableSheetTabsList>\n <TabsTrigger value=\"columns\">Columns</TabsTrigger>\n <TabsTrigger value=\"format\">Format</TabsTrigger>\n </DataTableSheetTabsList>\n <DataTableSheetTabPanel layout=\"list\" value=\"columns\" className=\"p-0 pt-2\">\n <DataTableColumnList />\n </DataTableSheetTabPanel>\n <DataTableSheetTabPanel layout=\"scroll\" value=\"format\" className=\"p-4 pt-1\">\n <DataTableFormatOptions />\n </DataTableSheetTabPanel>\n </DataTableSheetTabs>\n </DataTableSheetBody>\n </DataTableSheetContent>\n</DataTableSheet>\n```\n\nDo **not** use raw `Tabs` + manual `overflow-y-auto` in sheet bodies — see `impact-nova://data-table-sheet-layout`.\n\nStorybook reference: `Data Display/DataTable/Sheet layout` (`CanonicalComposition`) and `Column settings scenarios` (`PinSortSearchAndReset`).\n\n---\n\n## 8. 50k+ Column Consumer Settings\n\nWhen building grids with **10,000+ columns** (planning grids, large attribute tables), apply these settings:\n\n### Required grid props\n\n```tsx\n<DataTableContent\n ensureDomOrder={false} // Skip DOM reordering for screen readers — critical at 10k+\n maintainColumnOrder={true} // Preserve column ordering after runtime column defs changes\n suppressColumnVirtualisation={false} // Ensure column virtualization is ON (default)\n/>\n```\n\n### Column picker (DataTableColumnList)\n\n- At 80+ visible items, `DataTableColumnList` automatically switches to **virtual-index drag mode** (pointer-based reorder with TanStack Virtual) instead of dnd-kit sortable (50k useSortable hooks + DOM nodes).\n- No consumer configuration needed — the switch is automatic.\n- Drag-and-drop, search, select-all, pin switches, apply/discard all remain functional.\n\n### Saved views and state management\n\n- **Use `applyColumnState({ state, applyOrder: true })` for restoring saved column views** — this is the AG Grid recommended pattern for partial column state application.\n- **Evaluate `gridApi.getState()` / `gridApi.setState()` for full grid snapshots** (includes sort, filter, column state, grouping) when persisting complex view configurations.\n- Keep `mergePartialOrderedColumnState` for picker-initiated partial edits (picker only modifies a subset of columns).\n- **Do not** manually diff columnDefs for saved views — AG Grid's state APIs handle runtime-calculated columns.\n\n### Column runtime state guard\n\n- After `newColumnsLoaded` events, Impact Nova's `columnRuntimeStateGuard` automatically restores hide/pin/order from the cached tree to prevent column-def refreshes from resetting user layout.\n- **Use `deferColumnRuntimeStateGuard(gridApi)`** when your app applies external state (e.g., from a backend save) — this skips the guard for that cycle and rebuilds the cache from your new live state.\n\n### Performance budget (CI ratchet)\n\n| Operation | Target at 50k |\n|-----------|---------------|\n| Hash fingerprint (3x) | < 200ms |\n| Tree build from grid | < 2s |\n| Cache resolve skip path | < 300ms |\n| Indicator patch (sort/filter badges) | < 500ms |\n| Authority build + fingerprint | < 500ms |\n| Authority reorder (splice) | < 50ms |\n| Column sheet open (collapsed groups) | < 500ms |\n| Drag frame time (virtual-index) | < 16ms |\n\n---\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impact-nova",
3
- "version": "2.5.11",
3
+ "version": "2.5.13",
4
4
  "description": "Enterprise-grade React design system built with React 19, Vite, Tailwind CSS, and Radix UI. Built-in internationalization (i18n) and comprehensive UI component library for scalable, accessible, and performant applications.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -1,27 +0,0 @@
1
- export type AvailableViewPickerType = "date" | "week" | "month" | "quarter" | "year";
2
- export declare const DEFAULT_YEAR_PAGE_SIZE = 12;
3
- export interface ResolveAvailableViewMonthOptions {
4
- seed: Date;
5
- pickerType: AvailableViewPickerType;
6
- minDate?: Date;
7
- maxDate?: Date;
8
- numberOfMonths?: number;
9
- hasSelection?: boolean;
10
- autoNavigateToAvailable?: boolean;
11
- yearPageSize?: number;
12
- }
13
- export declare function startOfDayDate(date: Date): Date;
14
- export declare function startOfMonthDate(date: Date): Date;
15
- export declare function isSameCalendarMonth(left: Date, right: Date): boolean;
16
- export declare function boundsFromDisabled(disabled?: unknown): {
17
- minDate?: Date;
18
- maxDate?: Date;
19
- };
20
- export declare function hasCalendarValue(value: unknown): boolean;
21
- /** Clamp `from` into `[minDate, maxDate]`. O(1). */
22
- export declare function firstAvailableDate(minDate?: Date, maxDate?: Date, from?: Date): Date;
23
- /**
24
- * When auto-navigate is on and there is no selection, land the visible pane on the
25
- * first enabled period. Does not write a selected value.
26
- */
27
- export declare function resolveAvailableViewMonth(options: ResolveAvailableViewMonthOptions): Date;
@@ -1,85 +0,0 @@
1
- const F = 12;
2
- function u(t) {
3
- return new Date(t.getFullYear(), t.getMonth(), t.getDate());
4
- }
5
- function f(t) {
6
- return new Date(t.getFullYear(), t.getMonth(), 1);
7
- }
8
- function h(t, e) {
9
- return t.getFullYear() === e.getFullYear() && t.getMonth() === e.getMonth();
10
- }
11
- function A(t) {
12
- if (!t || typeof t != "object" || Array.isArray(t))
13
- return {};
14
- const e = t;
15
- return {
16
- ...e.before instanceof Date ? { minDate: e.before } : {},
17
- ...e.after instanceof Date ? { maxDate: e.after } : {}
18
- };
19
- }
20
- function Y(t) {
21
- if (t == null) return !1;
22
- if (Array.isArray(t)) return t.length > 0;
23
- if (t instanceof Date) return !0;
24
- if (typeof t == "object") {
25
- if ("from" in t || "to" in t) {
26
- const e = t;
27
- return e.from != null || e.to != null;
28
- }
29
- return !0;
30
- }
31
- return !1;
32
- }
33
- function g(t, e, n = /* @__PURE__ */ new Date()) {
34
- const r = u(n);
35
- if (t) {
36
- const a = u(t);
37
- if (r < a) return a;
38
- }
39
- if (e) {
40
- const a = u(e);
41
- if (r > a) return a;
42
- }
43
- return r;
44
- }
45
- function D(t, e, n) {
46
- const r = f(t), a = new Date(
47
- t.getFullYear(),
48
- t.getMonth() + e,
49
- 0,
50
- 23,
51
- 59,
52
- 59,
53
- 999
54
- ), i = u(n).getTime();
55
- return i >= r.getTime() && i <= a.getTime();
56
- }
57
- function m(t, e, n) {
58
- const r = Math.floor(t / n) * n;
59
- return e >= r && e <= r + n - 1;
60
- }
61
- function w(t) {
62
- const {
63
- seed: e,
64
- pickerType: n,
65
- minDate: r,
66
- maxDate: a,
67
- numberOfMonths: i = 1,
68
- hasSelection: l = !1,
69
- autoNavigateToAvailable: c = !1,
70
- yearPageSize: s = 12
71
- } = t;
72
- if (!c || l) return e;
73
- const o = g(r, a, e);
74
- return n === "date" || n === "week" ? D(e, i, o) ? e : f(o) : n === "month" || n === "quarter" ? e.getFullYear() === o.getFullYear() ? e : new Date(o.getFullYear(), 0, 1) : m(e.getFullYear(), o.getFullYear(), s) ? e : new Date(o.getFullYear(), 0, 1);
75
- }
76
- export {
77
- F as DEFAULT_YEAR_PAGE_SIZE,
78
- A as boundsFromDisabled,
79
- g as firstAvailableDate,
80
- Y as hasCalendarValue,
81
- h as isSameCalendarMonth,
82
- w as resolveAvailableViewMonth,
83
- u as startOfDayDate,
84
- f as startOfMonthDate
85
- };