@santi020k/lumen-react 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +666 -13
  2. package/dist/ai-surfaces.d.ts +49 -0
  3. package/dist/ai-surfaces.d.ts.map +1 -0
  4. package/dist/ai-surfaces.js +88 -0
  5. package/dist/amount-field.d.ts +11 -0
  6. package/dist/amount-field.d.ts.map +1 -0
  7. package/dist/amount-field.js +37 -0
  8. package/dist/attachments.d.ts +17 -0
  9. package/dist/attachments.d.ts.map +1 -0
  10. package/dist/attachments.js +23 -0
  11. package/dist/bullet-chart.d.ts +14 -0
  12. package/dist/bullet-chart.d.ts.map +1 -0
  13. package/dist/bullet-chart.js +22 -0
  14. package/dist/change-summary.d.ts +14 -0
  15. package/dist/change-summary.d.ts.map +1 -0
  16. package/dist/change-summary.js +4 -0
  17. package/dist/chart-inspection.d.ts +10 -0
  18. package/dist/chart-inspection.d.ts.map +1 -0
  19. package/dist/chart-inspection.js +11 -0
  20. package/dist/chart-interaction.d.ts +17 -0
  21. package/dist/chart-interaction.d.ts.map +1 -0
  22. package/dist/chart-interaction.js +50 -0
  23. package/dist/chart-label.d.ts +3 -0
  24. package/dist/chart-label.d.ts.map +1 -0
  25. package/dist/chart-label.js +1 -0
  26. package/dist/chart-recipes.d.ts +8 -0
  27. package/dist/chart-recipes.d.ts.map +1 -1
  28. package/dist/chart-recipes.js +35 -0
  29. package/dist/combobox.d.ts +9 -0
  30. package/dist/combobox.d.ts.map +1 -0
  31. package/dist/combobox.js +137 -0
  32. package/dist/comparison-chart.d.ts +13 -0
  33. package/dist/comparison-chart.d.ts.map +1 -0
  34. package/dist/comparison-chart.js +18 -0
  35. package/dist/components.d.ts +106 -70
  36. package/dist/components.d.ts.map +1 -1
  37. package/dist/components.js +4 -1949
  38. package/dist/dashboard.d.ts +20 -0
  39. package/dist/dashboard.d.ts.map +1 -0
  40. package/dist/dashboard.js +19 -0
  41. package/dist/data-table-range-model.d.ts +8 -0
  42. package/dist/data-table-range-model.d.ts.map +1 -0
  43. package/dist/data-table-range-model.js +31 -0
  44. package/dist/data-table-range.d.ts +17 -0
  45. package/dist/data-table-range.d.ts.map +1 -0
  46. package/dist/data-table-range.js +8 -0
  47. package/dist/data-table-saved-views.d.ts +30 -0
  48. package/dist/data-table-saved-views.d.ts.map +1 -0
  49. package/dist/data-table-saved-views.js +49 -0
  50. package/dist/data-table-selection-model.d.ts +23 -0
  51. package/dist/data-table-selection-model.d.ts.map +1 -0
  52. package/dist/data-table-selection-model.js +36 -0
  53. package/dist/data-table-selection.d.ts +11 -0
  54. package/dist/data-table-selection.d.ts.map +1 -0
  55. package/dist/data-table-selection.js +10 -0
  56. package/dist/data-table-state.d.ts +6 -0
  57. package/dist/data-table-state.d.ts.map +1 -0
  58. package/dist/data-table-state.js +38 -0
  59. package/dist/data-table-view.d.ts +88 -0
  60. package/dist/data-table-view.d.ts.map +1 -0
  61. package/dist/data-table-view.js +1 -0
  62. package/dist/data-table.d.ts +60 -0
  63. package/dist/data-table.d.ts.map +1 -0
  64. package/dist/data-table.js +1 -0
  65. package/dist/date-range-calendar.d.ts +29 -0
  66. package/dist/date-range-calendar.d.ts.map +1 -0
  67. package/dist/date-range-calendar.js +69 -0
  68. package/dist/date-range-input.d.ts +25 -0
  69. package/dist/date-range-input.d.ts.map +1 -0
  70. package/dist/date-range-input.js +195 -0
  71. package/dist/device-frame.d.ts +13 -0
  72. package/dist/device-frame.d.ts.map +1 -0
  73. package/dist/device-frame.js +23 -0
  74. package/dist/dialog-lifecycle.d.ts +3 -0
  75. package/dist/dialog-lifecycle.d.ts.map +1 -0
  76. package/dist/dialog-lifecycle.js +70 -0
  77. package/dist/expanded-charts.d.ts +26 -0
  78. package/dist/expanded-charts.d.ts.map +1 -0
  79. package/dist/expanded-charts.js +46 -0
  80. package/dist/floating-panel.d.ts +11 -0
  81. package/dist/floating-panel.d.ts.map +1 -0
  82. package/dist/floating-panel.js +1 -0
  83. package/dist/form-workflow.d.ts +77 -0
  84. package/dist/form-workflow.d.ts.map +1 -0
  85. package/dist/form-workflow.js +337 -0
  86. package/dist/hooks.d.ts +24 -21
  87. package/dist/hooks.d.ts.map +1 -1
  88. package/dist/hooks.js +1 -2468
  89. package/dist/image-comparison.d.ts +20 -0
  90. package/dist/image-comparison.d.ts.map +1 -0
  91. package/dist/image-comparison.js +55 -0
  92. package/dist/index.d.ts +25 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +24 -0
  95. package/dist/interval-charts.d.ts +21 -0
  96. package/dist/interval-charts.d.ts.map +1 -0
  97. package/dist/interval-charts.js +23 -0
  98. package/dist/media-selection.d.ts +18 -0
  99. package/dist/media-selection.d.ts.map +1 -0
  100. package/dist/media-selection.js +12 -0
  101. package/dist/media-viewport.d.ts +16 -0
  102. package/dist/media-viewport.d.ts.map +1 -0
  103. package/dist/media-viewport.js +37 -0
  104. package/dist/motion.d.ts +597 -0
  105. package/dist/motion.d.ts.map +1 -0
  106. package/dist/motion.js +22 -0
  107. package/dist/review-workflow.d.ts +32 -0
  108. package/dist/review-workflow.d.ts.map +1 -0
  109. package/dist/review-workflow.js +76 -0
  110. package/dist/rich-text-editor.d.ts +21 -0
  111. package/dist/rich-text-editor.d.ts.map +1 -0
  112. package/dist/rich-text-editor.js +143 -0
  113. package/dist/select-form.d.ts +3 -0
  114. package/dist/select-form.d.ts.map +1 -0
  115. package/dist/select-form.js +1 -0
  116. package/dist/server-components.d.ts +24 -4
  117. package/dist/server-components.d.ts.map +1 -1
  118. package/dist/server-components.js +11 -3
  119. package/dist/server.d.ts +1 -0
  120. package/dist/server.d.ts.map +1 -1
  121. package/dist/server.js +1 -0
  122. package/dist/toast-context.d.ts +4 -0
  123. package/dist/toast-context.d.ts.map +1 -0
  124. package/dist/toast-context.js +10 -0
  125. package/dist/toast-provider.d.ts +3 -0
  126. package/dist/toast-provider.d.ts.map +1 -0
  127. package/dist/toast-provider.js +3 -0
  128. package/dist/use-copy-feedback.d.ts +17 -0
  129. package/dist/use-copy-feedback.d.ts.map +1 -0
  130. package/dist/use-copy-feedback.js +54 -0
  131. package/dist/virtual-list-data.d.ts +13 -0
  132. package/dist/virtual-list-data.d.ts.map +1 -0
  133. package/dist/virtual-list-data.js +87 -0
  134. package/dist/virtual-list.d.ts +14 -0
  135. package/dist/virtual-list.d.ts.map +1 -0
  136. package/dist/virtual-list.js +24 -0
  137. package/dist/visual-interactions.d.ts +16 -0
  138. package/dist/visual-interactions.d.ts.map +1 -0
  139. package/dist/visual-interactions.js +30 -0
  140. package/dist/world-map.d.ts +26 -0
  141. package/dist/world-map.d.ts.map +1 -0
  142. package/dist/world-map.js +81 -0
  143. package/package.json +99 -11
  144. package/styles/critical.css +634 -61
  145. package/styles/data-table-view.css +36 -0
@@ -0,0 +1,20 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { type LumenActiveFilter } from '@santi020k/lumen-core';
3
+ export interface FilterBarProps extends ComponentPropsWithRef<'section'> {
4
+ label?: string;
5
+ filters?: readonly LumenActiveFilter[];
6
+ onRemoveFilter?: (id: string) => void;
7
+ onReset?: () => void;
8
+ resetLabel?: string;
9
+ removeLabel?: (filter: LumenActiveFilter) => string;
10
+ /** Pass a complete localized count message; zero and unknown are different. */
11
+ resultLabel?: string;
12
+ pending?: boolean;
13
+ /** Controlled filters disclosure; omit for native browser ownership. */
14
+ open?: boolean;
15
+ defaultOpen?: boolean;
16
+ onOpenChange?: (open: boolean) => void;
17
+ actions?: ReactNode;
18
+ }
19
+ export declare const FilterBar: ({ label, filters, onRemoveFilter, onReset, resetLabel, removeLabel, resultLabel, pending, open, defaultOpen, onOpenChange, actions, className, children, ...props }: FilterBarProps) => import("react").JSX.Element;
20
+ //# sourceMappingURL=dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAE7D,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAIhF,MAAM,WAAW,cAAe,SAAQ,qBAAqB,CAAC,SAAS,CAAC;IACtE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACtC,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,MAAM,CAAA;IACnD,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB;AAKD,eAAO,MAAM,SAAS,GAAI,qKAIvB,cAAc,gCA8ChB,CAAA"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { composeClassName } from '@santi020k/lumen-core';
4
+ import { Button } from './components.js';
5
+ const empty = [];
6
+ const removeFilterLabel = (filter) => `Remove ${filter.label}: ${filter.value}`;
7
+ export const FilterBar = ({ label = 'Filters', filters = empty, onRemoveFilter, onReset, resetLabel = 'Reset filters', removeLabel = removeFilterLabel, resultLabel, pending = false, open, defaultOpen = true, onOpenChange, actions, className, children, ...props }) => (_jsxs("section", { className: composeClassName('ui-filter-bar', className), "aria-label": label, "aria-busy": pending, ...props, children: [_jsxs("details", { open: open ?? defaultOpen, onToggle: event => {
8
+ if (open === undefined)
9
+ onOpenChange?.(event.currentTarget.open);
10
+ }, children: [_jsx("summary", { onClick: event => {
11
+ if (open === undefined)
12
+ return;
13
+ event.preventDefault();
14
+ onOpenChange?.(!open);
15
+ }, children: label }), _jsx("div", { className: "ui-filter-bar__controls", children: children })] }), _jsxs("div", { className: "ui-filter-bar__active", children: [filters.map(filter => onRemoveFilter ?
16
+ (_jsx(Button, { variant: "outline", size: "sm", disabled: pending, "aria-label": removeLabel(filter), onClick: () => {
17
+ onRemoveFilter(filter.id);
18
+ }, children: `${filter.label}: ${filter.value} ×` }, filter.id)) :
19
+ (_jsx("span", { children: `${filter.label}: ${filter.value}` }, filter.id))), onReset && filters.length > 0 && _jsx(Button, { variant: "ghost", size: "sm", disabled: pending, onClick: onReset, children: resetLabel }), actions] }), _jsx("p", { className: "ui-filter-bar__results", role: "status", "aria-live": "polite", "aria-atomic": "true", children: resultLabel })] }));
@@ -0,0 +1,8 @@
1
+ export interface DataTableViewRange {
2
+ id: string;
3
+ from: string;
4
+ to: string;
5
+ }
6
+ export declare const isDataTableRange: (value: unknown) => value is DataTableViewRange;
7
+ export declare const matchesDataTableRange: (value: unknown, range: unknown, kind: "number" | "date") => boolean;
8
+ //# sourceMappingURL=data-table-range-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-range-model.d.ts","sourceRoot":"","sources":["../src/data-table-range-model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,kBAG1D,CAAA;AA8BD,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,EAAE,OAAO,OAAO,EAAE,MAAM,QAAQ,GAAG,MAAM,KAAG,OAO/F,CAAA"}
@@ -0,0 +1,31 @@
1
+ export const isDataTableRange = (value) => (typeof value === 'object' && value !== null && 'id' in value && typeof value.id === 'string' &&
2
+ 'from' in value && typeof value.from === 'string' && 'to' in value && typeof value.to === 'string');
3
+ const comparable = (value, kind) => {
4
+ if (typeof value !== 'string' && typeof value !== 'number')
5
+ return undefined;
6
+ if (String(value).trim() === '')
7
+ return undefined;
8
+ if (kind === 'number') {
9
+ const parsed = Number(value);
10
+ return Number.isFinite(parsed) ? parsed : undefined;
11
+ }
12
+ if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/u.test(value))
13
+ return undefined;
14
+ const parsed = Date.parse(value);
15
+ return Number.isFinite(parsed) && new Date(parsed).toISOString().slice(0, 10) === value ? parsed : undefined;
16
+ };
17
+ const matchesBound = (value, bound, kind, lower) => {
18
+ if (bound === '')
19
+ return true;
20
+ const parsed = comparable(bound, kind);
21
+ if (parsed === undefined)
22
+ return false;
23
+ return lower ? value >= parsed : value <= parsed;
24
+ };
25
+ export const matchesDataTableRange = (value, range, kind) => {
26
+ if (!isDataTableRange(range))
27
+ return false;
28
+ const current = comparable(value, kind);
29
+ return current !== undefined && matchesBound(current, range.from, kind, true) &&
30
+ matchesBound(current, range.to, kind, false);
31
+ };
@@ -0,0 +1,17 @@
1
+ import type { DataTableViewRange } from './data-table-range-model.js';
2
+ interface RangeControlsProps {
3
+ columns: readonly {
4
+ key: string;
5
+ label: string;
6
+ rangeFilter?: 'number' | 'date';
7
+ }[];
8
+ ranges: readonly DataTableViewRange[];
9
+ id: string;
10
+ disabled: boolean;
11
+ fromLabel: (label: string) => string;
12
+ toLabel: (label: string) => string;
13
+ onChange: (ranges: DataTableViewRange[]) => void;
14
+ }
15
+ export declare const DataTableRangeControls: ({ columns, ranges, id, disabled, fromLabel, toLabel, onChange }: RangeControlsProps) => import("react").JSX.Element[];
16
+ export {};
17
+ //# sourceMappingURL=data-table-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-range.d.ts","sourceRoot":"","sources":["../src/data-table-range.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAErE,UAAU,kBAAkB;IAC1B,OAAO,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,EAAE,CAAA;IACnF,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACpC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,KAAK,IAAI,CAAA;CACjD;AAED,eAAO,MAAM,sBAAsB,GAAI,iEAEpC,kBAAkB,kCAsBnB,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Field, Input, Label } from './components.js';
3
+ export const DataTableRangeControls = ({ columns, ranges, id, disabled, fromLabel, toLabel, onChange }) => columns.filter(column => column.rangeFilter).map((column, index) => (_jsxs("fieldset", { className: "ui-data-table-view__range", disabled: disabled, children: [_jsx("legend", { children: column.label }), ['from', 'to'].map(bound => (_jsxs(Field, { controlId: `${id}-range-${index}-${bound}`, children: [_jsx(Label, { htmlFor: `${id}-range-${index}-${bound}`, children: (bound === 'from' ? fromLabel : toLabel)(column.label) }), _jsx(Input, { id: `${id}-range-${index}-${bound}`, type: column.rangeFilter === 'date' ? 'date' : 'number', step: column.rangeFilter === 'number' ? 'any' : undefined, value: ranges.find(range => range.id === column.key)?.[bound] ?? '', onChange: event => {
4
+ const existing = ranges.find(range => range.id === column.key) ?? { id: column.key, from: '', to: '' };
5
+ const next = { ...existing, [bound]: event.currentTarget.value };
6
+ const remaining = ranges.filter(range => range.id !== column.key);
7
+ onChange(next.from || next.to ? [...remaining, next] : remaining);
8
+ } })] }, bound)))] }, column.key)));
@@ -0,0 +1,30 @@
1
+ import type { DataTableViewState } from './data-table-view.js';
2
+ export interface DataTableSavedView {
3
+ id: string;
4
+ label: string;
5
+ state: DataTableViewState;
6
+ }
7
+ export interface DataTableSavedViewsProps {
8
+ views: readonly DataTableSavedView[];
9
+ state: DataTableViewState;
10
+ activeId?: string | undefined;
11
+ resetState: DataTableViewState;
12
+ onApply: (state: DataTableViewState, id: string | undefined) => void;
13
+ onSave: (label: string, state: DataTableViewState) => void;
14
+ onUpdate?: (id: string, state: DataTableViewState) => void;
15
+ onRemove?: (id: string) => void;
16
+ disabled?: boolean;
17
+ labels?: Partial<{
18
+ region: string;
19
+ view: string;
20
+ custom: string;
21
+ name: string;
22
+ save: string;
23
+ update: string;
24
+ remove: string;
25
+ reset: string;
26
+ }>;
27
+ }
28
+ /** Persistence and account scoping are explicit host callbacks. No browser storage is accessed. */
29
+ export declare const DataTableSavedViews: ({ views, state, activeId, resetState, onApply, onSave, onUpdate, onRemove, disabled, labels: overrides }: DataTableSavedViewsProps) => import("react").JSX.Element;
30
+ //# sourceMappingURL=data-table-saved-views.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-saved-views.d.ts","sourceRoot":"","sources":["../src/data-table-saved-views.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,kBAAkB,CAAA;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACpC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,UAAU,EAAE,kBAAkB,CAAA;IAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAA;IACpE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC1D,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC1D,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,MAAM,CAAA;KACd,CAAC,CAAA;CACH;AA0BD,mGAAmG;AACnG,eAAO,MAAM,mBAAmB,GAAI,0GAEjC,wBAAwB,gCAoF1B,CAAA"}
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId, useState } from 'react';
4
+ import { Button, Field, Input, Label, NativeSelect } from './components.js';
5
+ import { parseDataTableViewState } from './data-table-state.js';
6
+ const labels = {
7
+ region: 'Saved record views',
8
+ view: 'Saved view',
9
+ custom: 'Custom view',
10
+ name: 'New view name',
11
+ save: 'Save new view',
12
+ update: 'Update view',
13
+ remove: 'Remove view',
14
+ reset: 'Reset view'
15
+ };
16
+ const savedState = (state) => {
17
+ const copied = parseDataTableViewState(state);
18
+ return { ...copied, pagination: { ...copied.pagination, pageIndex: 0 } };
19
+ };
20
+ const assertSavedViews = (views) => {
21
+ const ids = views.map(view => view.id);
22
+ if (ids.some(value => typeof value !== 'string' || !value.trim()) || new Set(ids).size !== ids.length)
23
+ throw new TypeError('Saved views require unique, nonempty IDs.');
24
+ };
25
+ /** Persistence and account scoping are explicit host callbacks. No browser storage is accessed. */
26
+ export const DataTableSavedViews = ({ views, state, activeId, resetState, onApply, onSave, onUpdate, onRemove, disabled = false, labels: overrides }) => {
27
+ const id = useId();
28
+ const [name, setName] = useState('');
29
+ const text = { ...labels, ...overrides };
30
+ assertSavedViews(views);
31
+ const active = views.find(view => view.id === activeId);
32
+ return (_jsxs("section", { className: "ui-data-table-view__saved", "aria-label": text.region, "aria-busy": disabled, children: [_jsxs(Field, { controlId: `${id}-view`, children: [_jsx(Label, { htmlFor: `${id}-view`, children: text.view }), _jsxs(NativeSelect, { id: `${id}-view`, value: active?.id ?? '', disabled: disabled, onChange: event => {
33
+ const view = views.find(item => item.id === event.currentTarget.value);
34
+ if (view)
35
+ onApply(savedState(view.state), view.id);
36
+ }, children: [_jsx("option", { value: "", disabled: true, children: text.custom }), views.map(view => _jsx("option", { value: view.id, children: view.label }, view.id))] })] }), _jsxs(Field, { controlId: `${id}-name`, children: [_jsx(Label, { htmlFor: `${id}-name`, children: text.name }), _jsx(Input, { id: `${id}-name`, value: name, maxLength: 120, disabled: disabled, onChange: event => {
37
+ setName(event.currentTarget.value);
38
+ } })] }), _jsx(Button, { type: "button", disabled: disabled || !name.trim(), onClick: () => {
39
+ onSave(name.trim(), savedState(state));
40
+ }, children: text.save }), onUpdate && (_jsx(Button, { type: "button", variant: "outline", disabled: disabled || !active, onClick: () => {
41
+ if (active)
42
+ onUpdate(active.id, savedState(state));
43
+ }, children: text.update })), onRemove && (_jsx(Button, { type: "button", variant: "outline", disabled: disabled || !active, onClick: () => {
44
+ if (active)
45
+ onRemove(active.id);
46
+ }, children: text.remove })), _jsx(Button, { type: "button", variant: "outline", disabled: disabled, onClick: () => {
47
+ onApply(savedState(resetState), undefined);
48
+ }, children: text.reset })] }));
49
+ };
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface DataTableViewSelection<T> {
3
+ selectedIds: readonly string[];
4
+ onChange: (ids: string[]) => void;
5
+ /** Return a localized reason to prevent selection. Permissions remain server-owned. */
6
+ unavailableReason?: (row: T) => string | undefined;
7
+ actions?: (selection: DataTableViewSelectionResult<T>) => ReactNode;
8
+ }
9
+ export interface DataTableViewSelectionResult<T> {
10
+ selectedIds: readonly string[];
11
+ /** Only records supplied by the host; not a complete server-side selection. */
12
+ selectedRows: readonly T[];
13
+ pageSelected: boolean;
14
+ pagePartiallySelected: boolean;
15
+ eligiblePageCount: number;
16
+ isSelected: (row: T) => boolean;
17
+ unavailableReason: (row: T) => string | undefined;
18
+ toggle: (row: T) => void;
19
+ togglePage: () => void;
20
+ clear: () => void;
21
+ }
22
+ export declare const createDataTableSelection: <T>(rows: readonly T[], page: readonly T[], getId: (row: T) => string, selection: DataTableViewSelection<T> | undefined, disabled: boolean) => DataTableViewSelectionResult<T> | undefined;
23
+ //# sourceMappingURL=data-table-selection-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-selection-model.d.ts","sourceRoot":"","sources":["../src/data-table-selection-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACjC,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC,KAAK,SAAS,CAAA;CACpE;AAED,MAAM,WAAW,4BAA4B,CAAC,CAAC;IAC7C,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,+EAA+E;IAC/E,YAAY,EAAE,SAAS,CAAC,EAAE,CAAA;IAC1B,YAAY,EAAE,OAAO,CAAA;IACrB,qBAAqB,EAAE,OAAO,CAAA;IAC9B,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAA;IAC/B,iBAAiB,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;IACjD,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAA;IACxB,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,eAAO,MAAM,wBAAwB,GAAI,CAAC,EACxC,MAAM,SAAS,CAAC,EAAE,EAAE,MAAM,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,EACjE,WAAW,sBAAsB,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,UAAU,OAAO,KAClE,4BAA4B,CAAC,CAAC,CAAC,GAAG,SAsCpC,CAAA"}
@@ -0,0 +1,36 @@
1
+ export const createDataTableSelection = (rows, page, getId, selection, disabled) => {
2
+ if (!selection)
3
+ return undefined;
4
+ const ids = Array.from(selection.selectedIds);
5
+ if (ids.some(id => typeof id !== 'string' || !id.trim()) || new Set(ids).size !== ids.length)
6
+ throw new TypeError('DataTableView selection requires unique, nonempty IDs.');
7
+ const selected = new Set(ids);
8
+ const reason = (row) => selection.unavailableReason?.(row);
9
+ const eligible = page.filter(row => reason(row) === undefined).map(getId);
10
+ const selectedOnPage = eligible.filter(id => selected.has(id)).length;
11
+ const pageSelected = eligible.length > 0 && selectedOnPage === eligible.length;
12
+ return {
13
+ selectedIds: ids,
14
+ selectedRows: rows.filter(row => selected.has(getId(row))),
15
+ eligiblePageCount: eligible.length,
16
+ pageSelected,
17
+ pagePartiallySelected: selectedOnPage > 0 && !pageSelected,
18
+ isSelected: row => selected.has(getId(row)),
19
+ unavailableReason: reason,
20
+ toggle: row => {
21
+ if (disabled || reason(row) !== undefined || !page.some(item => getId(item) === getId(row)))
22
+ return;
23
+ selection.onChange(selected.has(getId(row)) ? ids.filter(id => id !== getId(row)) : [...ids, getId(row)]);
24
+ },
25
+ togglePage: () => {
26
+ if (disabled)
27
+ return;
28
+ const onPage = new Set(eligible);
29
+ selection.onChange(pageSelected ? ids.filter(id => !onPage.has(id)) : [...new Set([...ids, ...eligible])]);
30
+ },
31
+ clear: () => {
32
+ if (!disabled)
33
+ selection.onChange([]);
34
+ }
35
+ };
36
+ };
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { DataTableViewSelectionResult } from './data-table-selection-model.js';
3
+ export declare const DataTableSelectionControls: <T>({ selection, disabled, selectPageLabel, clearLabel, countLabel, actions }: {
4
+ selection: DataTableViewSelectionResult<T> | undefined;
5
+ disabled: boolean;
6
+ selectPageLabel: string;
7
+ clearLabel: string;
8
+ countLabel: (count: number) => string;
9
+ actions: ((selection: DataTableViewSelectionResult<T>) => ReactNode) | undefined;
10
+ }) => import("react").JSX.Element | null;
11
+ //# sourceMappingURL=data-table-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-selection.d.ts","sourceRoot":"","sources":["../src/data-table-selection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAA;AAEnF,eAAO,MAAM,0BAA0B,GAAI,CAAC,EAAG,2EAE5C;IACD,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;IACtD,QAAQ,EAAE,OAAO,CAAA;IACjB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,SAAS,CAAA;CACjF,uCAqBA,CAAA"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Checkbox, Label } from './components.js';
3
+ export const DataTableSelectionControls = ({ selection, disabled, selectPageLabel, clearLabel, countLabel, actions }) => {
4
+ if (!selection)
5
+ return null;
6
+ return (_jsxs("div", { className: "ui-data-table-view__selection", role: "group", "aria-label": countLabel(selection.selectedIds.length), children: [_jsxs(Label, { children: [_jsx(Checkbox, { checked: selection.pageSelected, ref: input => {
7
+ if (input)
8
+ input.indeterminate = selection.pagePartiallySelected;
9
+ }, disabled: disabled || selection.eligiblePageCount === 0, onChange: selection.togglePage }), selectPageLabel] }), _jsx("span", { role: "status", children: countLabel(selection.selectedIds.length) }), _jsx(Button, { type: "button", variant: "outline", disabled: disabled || selection.selectedIds.length === 0, onClick: selection.clear, children: clearLabel }), actions?.(selection)] }));
10
+ };
@@ -0,0 +1,6 @@
1
+ import type { DataTableViewState } from './data-table-view.js';
2
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
3
+ export declare const assertViewState: (state: unknown) => asserts state is DataTableViewState;
4
+ /** Validate an untrusted saved state and copy only view preferences, never records or selection. */
5
+ export declare const parseDataTableViewState: (value: unknown) => DataTableViewState;
6
+ //# sourceMappingURL=data-table-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-state.d.ts","sourceRoot":"","sources":["../src/data-table-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAExE,CAAA;AA2BD,eAAO,MAAM,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,KAAK,IAAI,kBAMlE,CAAA;AAED,oGAAoG;AACpG,eAAO,MAAM,uBAAuB,GAAI,OAAO,OAAO,KAAG,kBAgBxD,CAAA"}
@@ -0,0 +1,38 @@
1
+ import { isDataTableRange } from './data-table-range-model.js';
2
+ export const isRecord = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value));
3
+ const assertPagination = (pagination) => {
4
+ if (!isRecord(pagination) || !Number.isSafeInteger(pagination.pageSize) ||
5
+ typeof pagination.pageSize !== 'number' || pagination.pageSize <= 0 ||
6
+ !Number.isSafeInteger(pagination.pageIndex) || typeof pagination.pageIndex !== 'number' || pagination.pageIndex < 0)
7
+ throw new RangeError('DataTableView requires a positive integer page size and a nonnegative integer page index.');
8
+ };
9
+ const isFilter = (filter) => (isRecord(filter) && typeof filter.id === 'string' && typeof filter.value === 'string');
10
+ const isSort = (sort) => (isRecord(sort) && typeof sort.id === 'string' && typeof sort.desc === 'boolean');
11
+ const isDensity = (density) => density === 'comfortable' || density === 'compact';
12
+ const everyItem = (value, validate) => Array.isArray(value) && Array.from(value).every(validate);
13
+ const validPreferences = (state) => typeof state.search === 'string' && isRecord(state.visibility) &&
14
+ Object.values(state.visibility).every(value => typeof value === 'boolean') &&
15
+ everyItem(state.filters, isFilter) && everyItem(state.sorting, isSort) && isDensity(state.density);
16
+ export const assertViewState = state => {
17
+ if (!isRecord(state) || !validPreferences(state) ||
18
+ (state.ranges !== undefined && !everyItem(state.ranges, isDataTableRange)))
19
+ throw new TypeError('DataTableView requires valid search, filters, visibility, sorting and density state.');
20
+ assertPagination(state.pagination);
21
+ };
22
+ /** Validate an untrusted saved state and copy only view preferences, never records or selection. */
23
+ export const parseDataTableViewState = (value) => {
24
+ assertViewState(value);
25
+ return {
26
+ search: value.search,
27
+ filters: value.filters.map(filter => ({ id: filter.id, value: filter.value })),
28
+ visibility: { ...value.visibility },
29
+ pagination: { pageIndex: value.pagination.pageIndex, pageSize: value.pagination.pageSize },
30
+ sorting: value.sorting.map(sort => ({ id: sort.id, desc: sort.desc })),
31
+ density: value.density,
32
+ ...(value.ranges === undefined ?
33
+ {} :
34
+ {
35
+ ranges: value.ranges.map(range => ({ id: range.id, from: range.from, to: range.to }))
36
+ })
37
+ };
38
+ };
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type DataTableViewRange } from './data-table-range-model.js';
3
+ import { type DataTableViewSelection, type DataTableViewSelectionResult } from './data-table-selection-model.js';
4
+ export type { DataTableViewRange } from './data-table-range-model.js';
5
+ export type { DataTableViewSelection, DataTableViewSelectionResult } from './data-table-selection-model.js';
6
+ export interface DataTableViewColumn<T> {
7
+ key: string;
8
+ label: string;
9
+ value?: (row: T) => string | number | null;
10
+ canHide?: boolean;
11
+ sortable?: boolean;
12
+ filterOptions?: readonly {
13
+ value: string;
14
+ label: string;
15
+ }[];
16
+ rangeFilter?: 'number' | 'date';
17
+ }
18
+ /** Serializable view state; record objects remain outside this state. */
19
+ export interface DataTableViewState {
20
+ search: string;
21
+ filters: {
22
+ id: string;
23
+ value: string;
24
+ }[];
25
+ ranges?: DataTableViewRange[];
26
+ visibility: Record<string, boolean>;
27
+ pagination: {
28
+ pageIndex: number;
29
+ pageSize: number;
30
+ };
31
+ sorting: {
32
+ id: string;
33
+ desc: boolean;
34
+ }[];
35
+ density: 'comfortable' | 'compact';
36
+ }
37
+ export interface DataTableViewResult<T> {
38
+ rows: readonly T[];
39
+ isColumnVisible: (key: string) => boolean;
40
+ visibleColumnCount: number;
41
+ filteredCount: number;
42
+ state: DataTableViewState;
43
+ toggleSort: (key: string, multiple?: boolean) => void;
44
+ selection: DataTableViewSelectionResult<T> | undefined;
45
+ }
46
+ export interface DataTableViewLabels {
47
+ search: string;
48
+ columns: string;
49
+ all: string;
50
+ density: string;
51
+ comfortable: string;
52
+ compact: string;
53
+ pageSize: string;
54
+ previous: string;
55
+ next: string;
56
+ empty: string;
57
+ rangeFrom: (column: string) => string;
58
+ rangeTo: (column: string) => string;
59
+ selectPage: string;
60
+ clearSelection: string;
61
+ selected: (count: number) => string;
62
+ /** Receives one-based page and at least one page, including empty results. */
63
+ page: (page: number, pages: number, matchingRows: number) => string;
64
+ }
65
+ export interface DataTableViewProps<T> {
66
+ rows: readonly T[];
67
+ columns: readonly DataTableViewColumn<T>[];
68
+ getRowId: (row: T) => string;
69
+ children: (view: DataTableViewResult<T>) => ReactNode;
70
+ labels?: Partial<DataTableViewLabels>;
71
+ label: string;
72
+ state?: DataTableViewState;
73
+ defaultState?: Partial<DataTableViewState>;
74
+ onStateChange?: (state: DataTableViewState) => void;
75
+ /** Server mode never filters, sorts or slices the supplied page. */
76
+ mode?: 'client' | 'server';
77
+ rowCount?: number;
78
+ /** Keep existing application/server ordering while filtering and paging locally. */
79
+ manualSorting?: boolean;
80
+ disabled?: boolean;
81
+ toolbar?: ReactNode;
82
+ searchable?: boolean;
83
+ selection?: DataTableViewSelection<T>;
84
+ }
85
+ export declare const DataTableView: <T extends {
86
+ id: string;
87
+ }>(props: DataTableViewProps<T>) => import("react").JSX.Element;
88
+ //# sourceMappingURL=data-table-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-view.d.ts","sourceRoot":"","sources":["../src/data-table-view.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ "use client";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useId,useMemo,useRef,useState}from"react";import{columnFilteringFeature,columnVisibilityFeature,createFilteredRowModel,createPaginatedRowModel,createSortedRowModel,globalFilteringFeature,rowPaginationFeature,rowSortingFeature,tableFeatures,useTable}from"@tanstack/react-table";import{Button,Checkbox,Field,Input,Label,NativeSelect}from"./components.js";import{DataTableRangeControls}from"./data-table-range.js";import{matchesDataTableRange}from"./data-table-range-model.js";import{DataTableSelectionControls}from"./data-table-selection.js";import{createDataTableSelection}from"./data-table-selection-model.js";import{assertViewState,isRecord}from"./data-table-state.js";const createFeatures=()=>tableFeatures({columnFilteringFeature,globalFilteringFeature,columnVisibilityFeature,rowSortingFeature,rowPaginationFeature,filteredRowModel:createFilteredRowModel(),sortedRowModel:createSortedRowModel(),paginatedRowModel:createPaginatedRowModel()}),english={search:"Search records",columns:"Columns",all:"All",density:"Density",comfortable:"Comfortable",compact:"Compact",pageSize:"Rows per page",previous:"Previous page",next:"Next page",empty:"No matching records",rangeFrom:column=>`${column}: from`,rangeTo:column=>`${column}: to`,selectPage:"Select eligible records on this page",clearSelection:"Clear selection",selected:count=>`${count} selected records`,page:(page,pages,count)=>`Page ${page} of ${pages} \xB7 ${count} records`},defaultView=(initial={})=>({search:"",filters:[],visibility:{},pagination:{pageIndex:0,pageSize:25},sorting:[],density:"comfortable",...initial}),textValue=value=>typeof value=="string"||typeof value=="number"?String(value):"",stableRows=(rows,getRowId)=>{const data=Array.from(rows);if(!data.every(row=>typeof row=="object"&&row!==null&&"id"in row&&typeof row.id=="string"))throw new TypeError("DataTableView requires dense records with string IDs.");const ids=data.map(getRowId);if(ids.some(id=>typeof id!="string"||id.length===0)||new Set(ids).size!==ids.length)throw new TypeError("DataTableView requires unique, nonempty row IDs.");return data},isFilterOptions=options=>options===void 0||Array.isArray(options)&&Array.from(options).every(option=>isRecord(option)&&typeof option.value=="string"&&typeof option.label=="string"),isColumnFiltering=column=>isFilterOptions(column.filterOptions)&&(column.rangeFilter===void 0||column.rangeFilter==="number"||column.rangeFilter==="date")&&!(column.rangeFilter&&column.filterOptions),isColumnConfiguration=column=>typeof column.label=="string"&&(column.value===void 0||typeof column.value=="function")&&(column.canHide===void 0||typeof column.canHide=="boolean")&&(column.sortable===void 0||typeof column.sortable=="boolean")&&isColumnFiltering(column),assertColumns=columns=>{if(!Array.isArray(columns))throw new TypeError("DataTableView requires an array of columns.");const keys=Array.from(columns,column=>{if(!isRecord(column)||typeof column.key!="string"||column.key.trim().length===0)throw new TypeError("DataTableView requires unique, nonempty column keys.");if(!isColumnConfiguration(column))throw new TypeError("DataTableView requires valid column labels, accessors, flags and filter options.");return column.key});if(new Set(keys).size!==keys.length)throw new TypeError("DataTableView requires unique, nonempty column keys.")},useViewState=(controlled,initial,onChange)=>{if(initial!==void 0&&!isRecord(initial))throw new TypeError("DataTableView requires an object for default state.");const initialView=defaultView(initial);assertViewState(initialView),controlled!==void 0&&assertViewState(controlled);const[local,setLocal]=useState(()=>initialView),state=controlled??local;return{state,update:patch=>{const next={...state,...patch};controlled===void 0&&setLocal(next),onChange?.(next)}}},matchingCount=(table,mode,total)=>{if(mode!=="server")return table.getFilteredRowModel().rows.length;if(total===void 0||!Number.isSafeInteger(total)||total<0)throw new RangeError("Server DataTableView requires a nonnegative integer matching rowCount.");return total},useViewModel=({rows,columns,getRowId,state:controlled,defaultState,onStateChange,mode="client",rowCount,manualSorting=!1})=>{assertColumns(columns);const{state,update}=useViewState(controlled,defaultState,onStateChange),features=useMemo(createFeatures,[]),definitions=useMemo(()=>columns.map(column=>({id:column.key,accessorFn:row=>column.value?.(row)??"",enableHiding:column.canHide!==!1,enableSorting:column.sortable===!0,enableGlobalFilter:!!column.value,filterFn:(row,key,value)=>column.rangeFilter?matchesDataTableRange(row.getValue(key),value,column.rangeFilter):textValue(row.getValue(key))===textValue(value),sortFn:(left,right,key)=>{const a=left.getValue(key),b=right.getValue(key);return typeof a=="number"&&typeof b=="number"?a-b:textValue(a).localeCompare(textValue(b),void 0,{numeric:!0,sensitivity:"base"})}})),[columns]),data=useMemo(()=>stableRows(rows,getRowId),[rows,getRowId]),table=useTable({features,data,columns:definitions,getRowId,state:{globalFilter:state.search,columnFilters:[...state.filters.filter(filter=>!columns.some(column=>column.key===filter.id&&column.rangeFilter)),...(state.ranges??[]).filter(range=>columns.some(column=>column.key===range.id&&column.rangeFilter)).map(range=>({id:range.id,value:range}))],columnVisibility:state.visibility,pagination:state.pagination,sorting:state.sorting},globalFilterFn:(row,key,value)=>textValue(row.getValue(key)).toLocaleLowerCase().includes(textValue(value).toLocaleLowerCase()),manualFiltering:mode==="server",manualPagination:mode==="server",manualSorting:mode==="server"||manualSorting,...rowCount===void 0?{}:{rowCount},autoResetPageIndex:!1}),count=matchingCount(table,mode,rowCount),pages=Math.max(1,Math.ceil(count/state.pagination.pageSize)),pageIndex=Math.min(state.pagination.pageIndex,pages-1),lastPageRequestRef=useRef(void 0);useEffect(()=>{if(mode!=="server"||pageIndex===state.pagination.pageIndex){lastPageRequestRef.current=void 0;return}const request=`${state.pagination.pageIndex}:${pageIndex}:${state.pagination.pageSize}`;lastPageRequestRef.current!==request&&(lastPageRequestRef.current=request,update({pagination:{...state.pagination,pageIndex}}))},[mode,pageIndex,state.pagination,update]);const visibleRows=mode==="server"?data:table.getSortedRowModel().rows.slice(pageIndex*state.pagination.pageSize,(pageIndex+1)*state.pagination.pageSize).map(row=>row.original);return{state,update,visibleRows,count,pages,pageIndex,visible:key=>columns.some(column=>column.key===key&&(column.canHide===!1||state.visibility[key]!==!1)),matching:key=>state.filters.find(filter=>filter.id===key)?.value??"",changeQuery:patch=>{update({...patch,pagination:{...patch.pagination??state.pagination,pageIndex:0}})}}},DataTableView=props=>{const{children,columns,label,labels,toolbar,disabled=!1,searchable=!0}=props,{state,update,visibleRows,count,pages,pageIndex,visible,matching,changeQuery}=useViewModel(props),id=useId(),text={...english,...labels},selection=createDataTableSelection(props.rows,visibleRows,props.getRowId,props.selection,disabled);return _jsxs("section",{role:"group",className:"ui-data-table-view","data-density":state.density,"aria-label":label,children:[_jsxs("div",{className:"ui-data-table-view__toolbar",children:[searchable&&_jsxs(Field,{controlId:`${id}-search`,children:[_jsx(Label,{htmlFor:`${id}-search`,children:text.search}),_jsx(Input,{id:`${id}-search`,type:"search",value:state.search,disabled,onChange:event=>{changeQuery({search:event.currentTarget.value})}})]}),columns.filter(column=>column.filterOptions).map((column,index)=>_jsxs(Field,{controlId:`${id}-column-${index}`,children:[_jsx(Label,{htmlFor:`${id}-column-${index}`,children:column.label}),_jsxs(NativeSelect,{id:`${id}-column-${index}`,value:matching(column.key),disabled,onChange:event=>{const value=event.currentTarget.value,filters=state.filters.filter(filter=>filter.id!==column.key);changeQuery({filters:value?[...filters,{id:column.key,value}]:filters})},children:[_jsx("option",{value:"",children:text.all}),column.filterOptions?.map(option=>_jsx("option",{value:option.value,children:option.label},option.value))]})]},column.key)),_jsx(DataTableRangeControls,{columns,ranges:state.ranges??[],id,disabled,fromLabel:text.rangeFrom,toLabel:text.rangeTo,onChange:ranges=>{changeQuery({ranges})}}),_jsxs(Field,{controlId:`${id}-density`,children:[_jsx(Label,{htmlFor:`${id}-density`,children:text.density}),_jsxs(NativeSelect,{id:`${id}-density`,value:state.density,disabled,onChange:event=>{update({density:event.currentTarget.value==="compact"?"compact":"comfortable"})},children:[_jsx("option",{value:"comfortable",children:text.comfortable}),_jsx("option",{value:"compact",children:text.compact})]})]}),toolbar]}),_jsxs("fieldset",{className:"ui-data-table-view__columns",disabled,children:[_jsx("legend",{children:text.columns}),columns.filter(column=>column.canHide!==!1).map(column=>_jsxs(Label,{children:[_jsx(Checkbox,{checked:visible(column.key),disabled:visible(column.key)&&columns.filter(item=>visible(item.key)).length<=1,onChange:event=>{update({visibility:{...state.visibility,[column.key]:event.currentTarget.checked}})}}),column.label]},column.key))]}),_jsx(DataTableSelectionControls,{selection,disabled,selectPageLabel:text.selectPage,clearLabel:text.clearSelection,countLabel:text.selected,actions:props.selection?.actions}),children({selection,rows:visibleRows,isColumnVisible:visible,visibleColumnCount:columns.filter(column=>visible(column.key)).length,filteredCount:count,state,toggleSort:(key,multiple=!1)=>{if(disabled||!columns.some(column=>column.key===key&&column.sortable))return;const active=state.sorting.find(sort=>sort.id===key),sorting=multiple?state.sorting.filter(sort=>sort.id!==key):[];changeQuery({sorting:[...sorting,{id:key,desc:active?!active.desc:!1}]})}}),count===0&&_jsx("p",{role:"status",children:text.empty}),_jsxs("div",{className:"ui-data-table-view__pagination",children:[_jsxs(Field,{controlId:`${id}-size`,children:[_jsx(Label,{htmlFor:`${id}-size`,children:text.pageSize}),_jsx(NativeSelect,{id:`${id}-size`,value:state.pagination.pageSize,disabled,onChange:event=>{changeQuery({pagination:{pageIndex:0,pageSize:Number(event.currentTarget.value)}})},children:[...new Set([10,25,50,100,state.pagination.pageSize])].sort((a,b)=>a-b).map(size=>_jsx("option",{value:size,children:size},size))})]}),_jsx("span",{role:"status","aria-live":"polite",children:text.page(pageIndex+1,pages,count)}),_jsx(Button,{variant:"outline",disabled:disabled||pageIndex===0,onClick:()=>{update({pagination:{...state.pagination,pageIndex:pageIndex-1}})},children:text.previous}),_jsx(Button,{variant:"outline",disabled:disabled||pageIndex>=pages-1,onClick:()=>{update({pagination:{...state.pagination,pageIndex:pageIndex+1}})},children:text.next})]})]})};export{DataTableView};
@@ -0,0 +1,60 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from 'react';
2
+ import { type LumenGlassProp } from './components.js';
3
+ export type DataTableCell = boolean | null | number | string | undefined | {
4
+ label?: boolean | null | number | string;
5
+ sortValue?: boolean | null | number | string;
6
+ value?: boolean | null | number | string;
7
+ };
8
+ export interface DataTableColumn {
9
+ header?: string;
10
+ key: string;
11
+ label?: string;
12
+ sort?: 'number' | 'string';
13
+ sortable?: boolean;
14
+ /** Rich display content does not change the underlying sortable value. */
15
+ render?: (cell: DataTableCell, row: DataTableRow) => ReactNode;
16
+ wide?: boolean;
17
+ }
18
+ export type DataTableRow = Record<string, DataTableCell> & {
19
+ id?: number | string;
20
+ rowValue?: number | string;
21
+ value?: number | string;
22
+ };
23
+ export interface DataTableSort {
24
+ direction: 'ascending' | 'descending';
25
+ key: string;
26
+ }
27
+ export interface DataTableProps extends ComponentPropsWithoutRef<'div'> {
28
+ columns?: DataTableColumn[];
29
+ defaultSort?: DataTableSort | null;
30
+ glass?: LumenGlassProp;
31
+ name?: string;
32
+ onSortChange?: (sort: DataTableSort | null) => void;
33
+ rows?: DataTableRow[];
34
+ sort?: DataTableSort | null;
35
+ sortMode?: 'client' | 'manual';
36
+ selectable?: boolean;
37
+ layout?: 'records' | 'scroll';
38
+ renderDetails?: (row: DataTableRow) => ReactNode;
39
+ expandedRowIds?: readonly string[];
40
+ defaultExpandedRowIds?: readonly string[];
41
+ onExpandedRowIdsChange?: (ids: string[]) => void;
42
+ expandLabel?: string;
43
+ collapseLabel?: string;
44
+ detailsLabel?: string;
45
+ }
46
+ export declare const DataTable: ({ children, className, columns: rawColumns, defaultSort, defaultExpandedRowIds, expandedRowIds, expandLabel, collapseLabel, detailsLabel, glass, layout, name, onExpandedRowIdsChange, onSortChange, renderDetails, rows: rawRows, sort: controlledSort, sortMode, selectable, ...props }: DataTableProps) => import("react").JSX.Element;
47
+ export interface DataTableSortControlsProps extends ComponentPropsWithoutRef<'div'> {
48
+ columns: readonly DataTableColumn[];
49
+ sort: DataTableSort | null;
50
+ onSortChange: (sort: DataTableSort | null) => void;
51
+ disabled?: boolean;
52
+ label?: string;
53
+ directionLabel?: string;
54
+ ascendingLabel?: string;
55
+ descendingLabel?: string;
56
+ placeholder?: string;
57
+ }
58
+ /** A named mobile/toolbar path to the same controlled server sort as table headers. */
59
+ export declare const DataTableSortControls: ({ columns: rawColumns, sort, onSortChange, disabled, label, directionLabel, ascendingLabel, descendingLabel, placeholder, className, ...props }: DataTableSortControlsProps) => import("react").JSX.Element;
60
+ //# sourceMappingURL=data-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../src/data-table.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ "use client";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useId,useMemo,useState}from"react";import{composeClassName}from"@santi020k/lumen-core";import{Button,Field,Icon,Label,NativeSelect}from"./components.js";const noColumns=[],noRows=[],isRecord=value=>typeof value=="object"&&value!==null&&!Array.isArray(value),primitive=value=>value==null||typeof value=="boolean"||typeof value=="number"||typeof value=="string",isCell=value=>primitive(value)||isRecord(value)&&[value.label,value.sortValue,value.value].every(primitive),isRow=value=>isRecord(value)&&[value.id,value.rowValue,value.value].every(id=>id===void 0||typeof id=="number"||typeof id=="string")&&Object.values(value).every(isCell),optionalString=value=>value===void 0||typeof value=="string",optionalBoolean=value=>value===void 0||typeof value=="boolean",isColumn=value=>isRecord(value)&&typeof value.key=="string"&&value.key.length>0&&[value.header,value.label].every(optionalString)&&[value.sortable,value.wide].every(optionalBoolean)&&(value.sort===void 0||value.sort==="number"||value.sort==="string")&&(value.render===void 0||typeof value.render=="function"),validColumns=value=>{if(!Array.isArray(value))return noColumns;const columns=Array.from(value);return columns.every(isColumn)&&new Set(columns.map(column=>column.key)).size===columns.length?columns:noColumns},rowValue=(row,index)=>String(row.rowValue??row.id??row.value??index),isRows=value=>{if(!Array.isArray(value)||!Array.from(value).every(isRow))return!1;const ids=value.map(rowValue);return!ids.includes("")&&new Set(ids).size===ids.length},classes=(glass,layout,className)=>composeClassName("ui-data-table",glass&&"ui-data-table--glass",glass==="subtle"&&"ui-glass-subtle",glass==="strong"&&"ui-glass-strong",layout==="records"&&"ui-table-wrap--records",className),isObject=cell=>typeof cell=="object"&&cell!==null,formatCell=cell=>{const value=isObject(cell)?cell.label??cell.value:cell;return String(value??"")},sortValue=cell=>isObject(cell)&&cell.sortValue!==void 0&&cell.sortValue!==null?String(cell.sortValue):void 0,compare=(left,right,sortType)=>{const leftValue=sortValue(left)??formatCell(left),rightValue=sortValue(right)??formatCell(right);return sortType==="number"?Number(leftValue.replaceAll(",",""))-Number(rightValue.replaceAll(",","")):leftValue.localeCompare(rightValue,void 0,{numeric:!0,sensitivity:"base"})},emptyIds=[],validIds=value=>Array.isArray(value)&&Array.from(value).every(id=>typeof id=="string")?value:emptyIds,validSort=value=>isRecord(value)&&typeof value.key=="string"&&(value.direction==="ascending"||value.direction==="descending")?{key:value.key,direction:value.direction}:null,useExpansion=({defaultExpandedRowIds,expandedRowIds,onExpandedRowIdsChange})=>{const[uncontrolled,setUncontrolled]=useState(()=>validIds(defaultExpandedRowIds)),expanded=expandedRowIds===void 0?uncontrolled:validIds(expandedRowIds);return{expanded,toggleDetails:id=>{const next=expanded.includes(id)?expanded.filter(value=>value!==id):[...expanded,id];expandedRowIds===void 0&&setUncontrolled(next),onExpandedRowIdsChange?.(next)}}},columnTitle=column=>column.header??column.label??column.key,renderToggle=(ctx,column,rowId,open,id)=>{const{row,toggleDetails,expandLabel,collapseLabel}=ctx;return _jsx(Button,{variant:"ghost",size:"icon","aria-label":`${open?collapseLabel??"Collapse record":expandLabel??"Expand record"}: ${formatCell(row[column.key])||rowId}`,"aria-expanded":open,"aria-controls":open?id:void 0,onClick:()=>{toggleDetails(rowId)},children:_jsx(Icon,{name:open?"chevron-down":"chevron-right"})})},renderCell=(ctx,column,columnIndex,rowId,open,id)=>{const{row,layout,renderDetails}=ctx,disclosure=columnIndex===0&&!!renderDetails;return _jsxs("td",{className:column.wide?"ui-table__cell--wide":void 0,"data-sort-value":sortValue(row[column.key]),role:"cell",children:[layout==="records"&&_jsx("span",{className:"ui-table__label","aria-hidden":"true",children:columnTitle(column)}),_jsxs("div",{className:"ui-table__value",children:[disclosure&&renderToggle(ctx,column,rowId,open,id),column.render?column.render(row[column.key],row):formatCell(row[column.key])]})]},column.key)},renderRow=ctx=>{const{row,index,columns,expanded,detailsId,renderDetails}=ctx,rowId=rowValue(row,index),open=expanded.includes(rowId),id=`${detailsId}-${Array.from(rowId,character=>character.codePointAt(0)?.toString(16)).join("-")}`,record=_jsx("tr",{"data-ui-datatable-row":!0,"data-value":rowId,role:"row",children:columns.map((column,columnIndex)=>renderCell(ctx,column,columnIndex,rowId,open,id))},`record:${rowId}`);if(!open||!renderDetails)return[record];const detail=_jsx("tr",{role:"row","data-ui-datatable-detail":!0,children:_jsx("td",{colSpan:columns.length,role:"cell",className:"ui-table__cell--wide",children:_jsx("section",{id,"aria-label":`${ctx.detailsLabel??"Record details"}: ${rowId}`,children:renderDetails(row)})})},`detail:${rowId}`);return[record,detail]},attrs=(name,sortMode,selectable,glass)=>({"data-ui-datatable":!0,"data-ui-datatable-name":name,"data-ui-datatable-sort-mode":sortMode,"data-ui-datatable-selectable":selectable?"true":void 0,"data-ui-glass-track":glass?!0:void 0}),DataTable=({children,className,columns:rawColumns=noColumns,defaultSort=null,defaultExpandedRowIds,expandedRowIds,expandLabel,collapseLabel,detailsLabel,glass=!1,layout,name,onExpandedRowIdsChange,onSortChange,renderDetails,rows:rawRows=noRows,sort:controlledSort,sortMode="client",selectable=!1,...props})=>{const columns=validColumns(rawColumns),rows=isRows(rawRows)?rawRows:noRows,[uncontrolledSort,setUncontrolledSort]=useState(()=>validSort(defaultSort)),sort=validSort(controlledSort===void 0?uncontrolledSort:controlledSort),{expanded,toggleDetails}=useExpansion({defaultExpandedRowIds,expandedRowIds,onExpandedRowIdsChange}),detailsId=useId(),sortedRows=useMemo(()=>{const entries=rows.map((row,index)=>({row,index}));if(sortMode==="manual"||!sort)return entries;const column=columns.find(candidate=>candidate.sortable&&candidate.key===sort.key);if(!column)return entries;const direction=sort.direction==="ascending"?1:-1;return entries.sort((left,right)=>compare(left.row[column.key],right.row[column.key],column.sort)*direction)},[columns,rows,sort,sortMode]),toggleSort=column=>{const next={direction:sort?.key===column.key&&sort.direction==="ascending"?"descending":"ascending",key:column.key};controlledSort===void 0&&setUncontrolledSort(next),onSortChange?.(next)};return _jsx("div",{className:classes(glass,layout,className),...attrs(name,sortMode,selectable,glass),...props,children:columns.length>0?_jsxs("table",{role:"table",children:[_jsx("thead",{role:"rowgroup",children:_jsx("tr",{role:"row",children:columns.map(column=>{const direction=sort?.key===column.key?sort.direction:void 0,label=columnTitle(column);return _jsx("th",{"aria-sort":direction??(column.sortable?"none":void 0),"data-ui-datatable-sort-bound":"true","data-ui-datatable-sort-type":column.sort,"data-ui-datatable-sortable":column.sortable?"true":void 0,role:"columnheader",scope:"col",children:column.sortable?_jsx("button",{className:"ui-data-table__sort","data-ui-datatable-sort":!0,onClick:()=>{toggleSort(column)},type:"button",children:label}):label},column.key)})})}),_jsx("tbody",{role:"rowgroup",children:sortedRows.flatMap(({row,index})=>renderRow({row,index,columns,layout,renderDetails,expanded,detailsId,toggleDetails,expandLabel,collapseLabel,detailsLabel}))})]}):children})},sortLabels=labels=>({label:labels.label??"Sort by",directionLabel:labels.directionLabel??"Sort direction",ascendingLabel:labels.ascendingLabel??"Ascending",descendingLabel:labels.descendingLabel??"Descending",placeholder:labels.placeholder??"Default order"}),DataTableSortControls=({columns:rawColumns,sort,onSortChange,disabled=!1,label,directionLabel,ascendingLabel,descendingLabel,placeholder,className,...props})=>{const columns=validColumns(rawColumns),text=sortLabels({label,directionLabel,ascendingLabel,descendingLabel,placeholder}),id=useId(),sortable=columns.filter(column=>column.sortable),selected=sortable.find(column=>column.key===sort?.key),descending=sort?.direction==="descending",directionText=descending?text.descendingLabel:text.ascendingLabel;return _jsxs("div",{...props,className:composeClassName("ui-data-table__sort-controls",className),children:[_jsxs(Field,{controlId:id,children:[_jsx(Label,{htmlFor:id,children:text.label}),_jsxs(NativeSelect,{id,disabled,value:selected?.key??"",onChange:event=>{const column=sortable.find(item=>item.key===event.currentTarget.value);onSortChange(column?{key:column.key,direction:"ascending"}:null)},children:[_jsx("option",{value:"",children:text.placeholder}),sortable.map(column=>_jsx("option",{value:column.key,children:columnTitle(column)},column.key))]})]}),_jsxs(Button,{variant:"outline",disabled:disabled||!selected,"aria-label":`${text.directionLabel}: ${directionText}`,"aria-pressed":descending,onClick:()=>{selected&&onSortChange({key:selected.key,direction:sort?.direction==="ascending"?"descending":"ascending"})},children:[_jsx(Icon,{name:descending?"arrow-down":"arrow-up"}),directionText]})]})};export{DataTable,DataTableSortControls};
@@ -0,0 +1,29 @@
1
+ import type { ComponentPropsWithoutRef } from 'react';
2
+ export interface CalendarRange {
3
+ start: string;
4
+ end: string;
5
+ }
6
+ export interface DateRangeCalendarProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'> {
7
+ value: CalendarRange;
8
+ onValueChange: (value: CalendarRange) => void;
9
+ locale?: string;
10
+ min?: string;
11
+ max?: string;
12
+ disabled?: boolean;
13
+ readOnly?: boolean;
14
+ labels: {
15
+ start: string;
16
+ end: string;
17
+ presets: string;
18
+ previousMonth?: string;
19
+ nextMonth?: string;
20
+ };
21
+ presets?: readonly {
22
+ label: string;
23
+ value: CalendarRange;
24
+ }[];
25
+ formatDate?: (value: string) => string;
26
+ }
27
+ /** Inline range editor. Consumers own draft state and Apply/Cancel actions. */
28
+ export declare const DateRangeCalendar: ({ value, onValueChange, locale, min, max, labels, presets: rawPresets, disabled, readOnly, formatDate, className, ...props }: DateRangeCalendarProps) => import("react").JSX.Element;
29
+ //# sourceMappingURL=date-range-calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-range-calendar.d.ts","sourceRoot":"","sources":["../src/date-range-calendar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAA;AAQrD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC/F,KAAK,EAAE,aAAa,CAAA;IACpB,aAAa,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnG,OAAO,CAAC,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,EAAE,CAAA;IAC5D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CACvC;AAuHD,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,GAAI,8HAG/B,sBAAsB,gCAuDxB,CAAA"}