@tokis/react 1.0.1 → 1.2.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.
- package/README.md +8 -6
- package/dist/__tests__/Accordion.test.d.ts +2 -0
- package/dist/__tests__/Accordion.test.d.ts.map +1 -0
- package/dist/__tests__/Accordion.test.js +144 -0
- package/dist/__tests__/Accordion.test.js.map +1 -0
- package/dist/__tests__/Button.test.d.ts +2 -0
- package/dist/__tests__/Button.test.d.ts.map +1 -0
- package/dist/__tests__/Button.test.js +104 -0
- package/dist/__tests__/Button.test.js.map +1 -0
- package/dist/__tests__/Checkbox.test.d.ts +2 -0
- package/dist/__tests__/Checkbox.test.d.ts.map +1 -0
- package/dist/__tests__/Checkbox.test.js +90 -0
- package/dist/__tests__/Checkbox.test.js.map +1 -0
- package/dist/__tests__/Select.test.d.ts +2 -0
- package/dist/__tests__/Select.test.d.ts.map +1 -0
- package/dist/__tests__/Select.test.js +119 -0
- package/dist/__tests__/Select.test.js.map +1 -0
- package/dist/__tests__/datagrid-utils.test.d.ts +2 -0
- package/dist/__tests__/datagrid-utils.test.d.ts.map +1 -0
- package/dist/__tests__/datagrid-utils.test.js +190 -0
- package/dist/__tests__/datagrid-utils.test.js.map +1 -0
- package/dist/__tests__/date-utils.test.d.ts +2 -0
- package/dist/__tests__/date-utils.test.d.ts.map +1 -0
- package/dist/__tests__/date-utils.test.js +180 -0
- package/dist/__tests__/date-utils.test.js.map +1 -0
- package/dist/cjs/components/accordion/index.js +1 -1
- package/dist/cjs/components/checkbox/index.js +2 -1
- package/dist/cjs/components/datagrid/index.js +161 -0
- package/dist/cjs/components/datagrid/types.js +2 -0
- package/dist/cjs/components/datagrid/utils.js +87 -0
- package/dist/cjs/components/datepicker/Calendar.js +117 -0
- package/dist/cjs/components/datepicker/date-utils.js +121 -0
- package/dist/cjs/components/datepicker/index.js +134 -0
- package/dist/cjs/components/extended/index.js +39 -31
- package/dist/cjs/components/layout/index.js +5 -4
- package/dist/cjs/components/treeview/index.js +1 -1
- package/dist/cjs/hooks/useId.js +18 -5
- package/dist/cjs/index.js +2 -0
- package/dist/components/accordion/index.js +1 -1
- package/dist/components/accordion/index.js.map +1 -1
- package/dist/components/checkbox/index.js +2 -1
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/datagrid/index.d.ts +20 -0
- package/dist/components/datagrid/index.d.ts.map +1 -0
- package/dist/components/datagrid/index.js +159 -0
- package/dist/components/datagrid/index.js.map +1 -0
- package/dist/components/datagrid/types.d.ts +92 -0
- package/dist/components/datagrid/types.d.ts.map +1 -0
- package/dist/components/datagrid/types.js +2 -0
- package/dist/components/datagrid/types.js.map +1 -0
- package/dist/components/datagrid/utils.d.ts +14 -0
- package/dist/components/datagrid/utils.d.ts.map +1 -0
- package/dist/components/datagrid/utils.js +80 -0
- package/dist/components/datagrid/utils.js.map +1 -0
- package/dist/components/datepicker/Calendar.d.ts +30 -0
- package/dist/components/datepicker/Calendar.d.ts.map +1 -0
- package/dist/components/datepicker/Calendar.js +115 -0
- package/dist/components/datepicker/Calendar.js.map +1 -0
- package/dist/components/datepicker/date-utils.d.ts +34 -0
- package/dist/components/datepicker/date-utils.d.ts.map +1 -0
- package/dist/components/datepicker/date-utils.js +107 -0
- package/dist/components/datepicker/date-utils.js.map +1 -0
- package/dist/components/datepicker/index.d.ts +52 -0
- package/dist/components/datepicker/index.d.ts.map +1 -0
- package/dist/components/datepicker/index.js +128 -0
- package/dist/components/datepicker/index.js.map +1 -0
- package/dist/components/extended/index.d.ts +14 -29
- package/dist/components/extended/index.d.ts.map +1 -1
- package/dist/components/extended/index.js +40 -27
- package/dist/components/extended/index.js.map +1 -1
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +5 -4
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/treeview/index.js +1 -1
- package/dist/components/treeview/index.js.map +1 -1
- package/dist/hooks/useId.d.ts +13 -3
- package/dist/hooks/useId.d.ts.map +1 -1
- package/dist/hooks/useId.js +19 -6
- package/dist/hooks/useId.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { sortRows, filterRows, nextSortDirection, paginateRows, computeColumnWidths, } from '../components/datagrid/utils.js';
|
|
3
|
+
const people = [
|
|
4
|
+
{ id: 1, name: 'Alice', age: 30, city: 'New York' },
|
|
5
|
+
{ id: 2, name: 'Charlie', age: 25, city: 'Los Angeles' },
|
|
6
|
+
{ id: 3, name: 'Bob', age: 35, city: 'Chicago' },
|
|
7
|
+
{ id: 4, name: 'Diana', age: 28, city: 'New York' },
|
|
8
|
+
];
|
|
9
|
+
const columns = [
|
|
10
|
+
{ field: 'name', header: 'Name', sortable: true, filterable: true },
|
|
11
|
+
{ field: 'age', header: 'Age', sortable: true, filterable: true },
|
|
12
|
+
{ field: 'city', header: 'City', sortable: true, filterable: true },
|
|
13
|
+
];
|
|
14
|
+
// ─── sortRows ─────────────────────────────────────────────────────────────
|
|
15
|
+
describe('sortRows', () => {
|
|
16
|
+
it('returns the original array when direction is null', () => {
|
|
17
|
+
const model = { field: 'name', direction: null };
|
|
18
|
+
const sorted = sortRows(people, model, columns);
|
|
19
|
+
expect(sorted.map((p) => p.id)).toEqual([1, 2, 3, 4]);
|
|
20
|
+
});
|
|
21
|
+
it('sorts strings ascending (A → Z)', () => {
|
|
22
|
+
const model = { field: 'name', direction: 'asc' };
|
|
23
|
+
const sorted = sortRows(people, model, columns);
|
|
24
|
+
expect(sorted.map((p) => p.name)).toEqual(['Alice', 'Bob', 'Charlie', 'Diana']);
|
|
25
|
+
});
|
|
26
|
+
it('sorts strings descending (Z → A)', () => {
|
|
27
|
+
const model = { field: 'name', direction: 'desc' };
|
|
28
|
+
const sorted = sortRows(people, model, columns);
|
|
29
|
+
expect(sorted.map((p) => p.name)).toEqual(['Diana', 'Charlie', 'Bob', 'Alice']);
|
|
30
|
+
});
|
|
31
|
+
it('sorts numbers ascending', () => {
|
|
32
|
+
const model = { field: 'age', direction: 'asc' };
|
|
33
|
+
const sorted = sortRows(people, model, columns);
|
|
34
|
+
expect(sorted.map((p) => p.age)).toEqual([25, 28, 30, 35]);
|
|
35
|
+
});
|
|
36
|
+
it('sorts numbers descending', () => {
|
|
37
|
+
const model = { field: 'age', direction: 'desc' };
|
|
38
|
+
const sorted = sortRows(people, model, columns);
|
|
39
|
+
expect(sorted.map((p) => p.age)).toEqual([35, 30, 28, 25]);
|
|
40
|
+
});
|
|
41
|
+
it('does not mutate the original array', () => {
|
|
42
|
+
const original = [...people];
|
|
43
|
+
const model = { field: 'name', direction: 'asc' };
|
|
44
|
+
sortRows(people, model, columns);
|
|
45
|
+
expect(people).toEqual(original);
|
|
46
|
+
});
|
|
47
|
+
it('returns original rows when field does not match any column', () => {
|
|
48
|
+
const model = { field: 'nonexistent', direction: 'asc' };
|
|
49
|
+
const sorted = sortRows(people, model, columns);
|
|
50
|
+
expect(sorted).toEqual(people);
|
|
51
|
+
});
|
|
52
|
+
it('uses valueGetter when provided on a column', () => {
|
|
53
|
+
const colsWithGetter = [
|
|
54
|
+
...columns,
|
|
55
|
+
{
|
|
56
|
+
field: 'nameLen',
|
|
57
|
+
header: 'Name Length',
|
|
58
|
+
sortable: true,
|
|
59
|
+
valueGetter: (row) => row.name.length,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
const model = { field: 'nameLen', direction: 'asc' };
|
|
63
|
+
const sorted = sortRows(people, model, colsWithGetter);
|
|
64
|
+
// Alice=5, Bob=3, Charlie=7, Diana=5 → Bob(3), Alice(5)|Diana(5), Charlie(7)
|
|
65
|
+
expect(sorted[0].name).toBe('Bob');
|
|
66
|
+
expect(sorted[sorted.length - 1].name).toBe('Charlie');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
// ─── filterRows ───────────────────────────────────────────────────────────
|
|
70
|
+
describe('filterRows', () => {
|
|
71
|
+
it('returns all rows when filterModel is empty', () => {
|
|
72
|
+
const model = {};
|
|
73
|
+
expect(filterRows(people, model, columns)).toHaveLength(4);
|
|
74
|
+
});
|
|
75
|
+
it('filters by quickFilter (case-insensitive, any filterable column)', () => {
|
|
76
|
+
const model = { quickFilter: 'alice' };
|
|
77
|
+
const result = filterRows(people, model, columns);
|
|
78
|
+
expect(result).toHaveLength(1);
|
|
79
|
+
expect(result[0].name).toBe('Alice');
|
|
80
|
+
});
|
|
81
|
+
it('quickFilter matches across multiple columns', () => {
|
|
82
|
+
const model = { quickFilter: 'new york' };
|
|
83
|
+
const result = filterRows(people, model, columns);
|
|
84
|
+
expect(result.map((p) => p.name)).toEqual(['Alice', 'Diana']);
|
|
85
|
+
});
|
|
86
|
+
it('returns empty array when quickFilter matches nothing', () => {
|
|
87
|
+
const model = { quickFilter: 'zzz-no-match' };
|
|
88
|
+
expect(filterRows(people, model, columns)).toHaveLength(0);
|
|
89
|
+
});
|
|
90
|
+
it('filters by columnFilters on a specific field', () => {
|
|
91
|
+
const model = { columnFilters: { city: 'new york' } };
|
|
92
|
+
const result = filterRows(people, model, columns);
|
|
93
|
+
expect(result.map((p) => p.name)).toEqual(['Alice', 'Diana']);
|
|
94
|
+
});
|
|
95
|
+
it('combines quickFilter AND columnFilters (both must match)', () => {
|
|
96
|
+
const model = {
|
|
97
|
+
quickFilter: 'new york',
|
|
98
|
+
columnFilters: { name: 'alice' },
|
|
99
|
+
};
|
|
100
|
+
const result = filterRows(people, model, columns);
|
|
101
|
+
expect(result).toHaveLength(1);
|
|
102
|
+
expect(result[0].name).toBe('Alice');
|
|
103
|
+
});
|
|
104
|
+
it('ignores empty columnFilter values', () => {
|
|
105
|
+
const model = { columnFilters: { name: '' } };
|
|
106
|
+
expect(filterRows(people, model, columns)).toHaveLength(4);
|
|
107
|
+
});
|
|
108
|
+
it('does not filter on columns with filterable=false', () => {
|
|
109
|
+
const noFilterCols = [
|
|
110
|
+
{ field: 'name', header: 'Name', filterable: false },
|
|
111
|
+
{ field: 'age', header: 'Age', filterable: true },
|
|
112
|
+
];
|
|
113
|
+
// Filtering for "alice" should NOT match since name is not filterable
|
|
114
|
+
const model = { quickFilter: 'alice' };
|
|
115
|
+
const result = filterRows(people, model, noFilterCols);
|
|
116
|
+
expect(result).toHaveLength(0);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
// ─── nextSortDirection ────────────────────────────────────────────────────
|
|
120
|
+
describe('nextSortDirection', () => {
|
|
121
|
+
it('first click on a column → asc', () => {
|
|
122
|
+
const current = { field: '', direction: null };
|
|
123
|
+
const next = nextSortDirection('', current, 'name');
|
|
124
|
+
expect(next).toEqual({ field: 'name', direction: 'asc' });
|
|
125
|
+
});
|
|
126
|
+
it('second click on same column → desc', () => {
|
|
127
|
+
const current = { field: 'name', direction: 'asc' };
|
|
128
|
+
const next = nextSortDirection('name', current, 'name');
|
|
129
|
+
expect(next).toEqual({ field: 'name', direction: 'desc' });
|
|
130
|
+
});
|
|
131
|
+
it('third click on same column → null (clear sort)', () => {
|
|
132
|
+
const current = { field: 'name', direction: 'desc' };
|
|
133
|
+
const next = nextSortDirection('name', current, 'name');
|
|
134
|
+
expect(next).toEqual({ field: 'name', direction: null });
|
|
135
|
+
});
|
|
136
|
+
it('clicking a different column resets to asc', () => {
|
|
137
|
+
const current = { field: 'name', direction: 'desc' };
|
|
138
|
+
const next = nextSortDirection('name', current, 'age');
|
|
139
|
+
expect(next).toEqual({ field: 'age', direction: 'asc' });
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// ─── paginateRows ─────────────────────────────────────────────────────────
|
|
143
|
+
describe('paginateRows', () => {
|
|
144
|
+
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
145
|
+
it('returns first page', () => {
|
|
146
|
+
expect(paginateRows(data, 0, 3)).toEqual([1, 2, 3]);
|
|
147
|
+
});
|
|
148
|
+
it('returns second page', () => {
|
|
149
|
+
expect(paginateRows(data, 1, 3)).toEqual([4, 5, 6]);
|
|
150
|
+
});
|
|
151
|
+
it('returns partial last page', () => {
|
|
152
|
+
expect(paginateRows(data, 3, 3)).toEqual([10]);
|
|
153
|
+
});
|
|
154
|
+
it('returns empty array for out-of-range page', () => {
|
|
155
|
+
expect(paginateRows(data, 99, 3)).toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
// ─── computeColumnWidths ──────────────────────────────────────────────────
|
|
159
|
+
describe('computeColumnWidths', () => {
|
|
160
|
+
it('uses fixed widths for columns with explicit width', () => {
|
|
161
|
+
const cols = [
|
|
162
|
+
{ field: 'name', header: 'Name', width: 200 },
|
|
163
|
+
{ field: 'age', header: 'Age', width: 100 },
|
|
164
|
+
];
|
|
165
|
+
const widths = computeColumnWidths(cols, 500);
|
|
166
|
+
expect(widths[0]).toBe(200);
|
|
167
|
+
expect(widths[1]).toBe(100);
|
|
168
|
+
});
|
|
169
|
+
it('distributes remaining space proportionally via flex', () => {
|
|
170
|
+
const cols = [
|
|
171
|
+
{ field: 'name', header: 'Name', width: 100 },
|
|
172
|
+
{ field: 'city', header: 'City', flex: 1 },
|
|
173
|
+
{ field: 'age', header: 'Age', flex: 2 },
|
|
174
|
+
];
|
|
175
|
+
// totalWidth=400, fixed=100, remaining=300, totalFlex=3
|
|
176
|
+
// city = (1/3)*300 = 100, age = (2/3)*300 = 200
|
|
177
|
+
const widths = computeColumnWidths(cols, 400);
|
|
178
|
+
expect(widths[0]).toBe(100);
|
|
179
|
+
expect(widths[1]).toBe(100);
|
|
180
|
+
expect(widths[2]).toBe(200);
|
|
181
|
+
});
|
|
182
|
+
it('falls back to 120px when no width or flex is specified', () => {
|
|
183
|
+
const cols = [
|
|
184
|
+
{ field: 'name', header: 'Name' },
|
|
185
|
+
];
|
|
186
|
+
const widths = computeColumnWidths(cols, 500);
|
|
187
|
+
expect(widths[0]).toBe(120);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
//# sourceMappingURL=datagrid-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datagrid-utils.test.js","sourceRoot":"","sources":["../../src/__tests__/datagrid-utils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/date-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseDate, formatDate, buildCalendarGrid, isSameDay, isBeforeDay, isAfterDay, daysInMonth, parseTime, formatTime, today, } from '../components/datepicker/date-utils.js';
|
|
3
|
+
// ─── parseDate ─────────────────────────────────────────────────────────────
|
|
4
|
+
describe('parseDate', () => {
|
|
5
|
+
it('parses a valid YYYY-MM-DD string into a local Date', () => {
|
|
6
|
+
const d = parseDate('2025-06-15');
|
|
7
|
+
expect(d).not.toBeNull();
|
|
8
|
+
expect(d.getFullYear()).toBe(2025);
|
|
9
|
+
expect(d.getMonth()).toBe(5); // 0-indexed
|
|
10
|
+
expect(d.getDate()).toBe(15);
|
|
11
|
+
});
|
|
12
|
+
it('returns null for undefined input', () => {
|
|
13
|
+
expect(parseDate(undefined)).toBeNull();
|
|
14
|
+
});
|
|
15
|
+
it('returns null for empty string', () => {
|
|
16
|
+
expect(parseDate('')).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
it('returns null for invalid format', () => {
|
|
19
|
+
expect(parseDate('15-06-2025')).toBeNull();
|
|
20
|
+
expect(parseDate('2025/06/15')).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
it('returns null for invalid dates that would roll over (e.g. Feb 30)', () => {
|
|
23
|
+
expect(parseDate('2024-02-30')).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
it('handles leap year Feb 29 correctly', () => {
|
|
26
|
+
const d = parseDate('2024-02-29');
|
|
27
|
+
expect(d).not.toBeNull();
|
|
28
|
+
expect(d.getDate()).toBe(29);
|
|
29
|
+
});
|
|
30
|
+
it('returns null for non-leap year Feb 29', () => {
|
|
31
|
+
expect(parseDate('2023-02-29')).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
// ─── formatDate ───────────────────────────────────────────────────────────
|
|
35
|
+
describe('formatDate', () => {
|
|
36
|
+
it('formats a date as YYYY-MM-DD', () => {
|
|
37
|
+
expect(formatDate(new Date(2025, 0, 5))).toBe('2025-01-05');
|
|
38
|
+
expect(formatDate(new Date(2025, 11, 31))).toBe('2025-12-31');
|
|
39
|
+
});
|
|
40
|
+
it('pads single-digit months and days with a leading zero', () => {
|
|
41
|
+
expect(formatDate(new Date(2025, 0, 1))).toBe('2025-01-01');
|
|
42
|
+
expect(formatDate(new Date(2025, 8, 9))).toBe('2025-09-09');
|
|
43
|
+
});
|
|
44
|
+
it('round-trips with parseDate', () => {
|
|
45
|
+
const original = '2025-03-17';
|
|
46
|
+
const d = parseDate(original);
|
|
47
|
+
expect(formatDate(d)).toBe(original);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
// ─── daysInMonth ──────────────────────────────────────────────────────────
|
|
51
|
+
describe('daysInMonth', () => {
|
|
52
|
+
it('returns 31 for January', () => {
|
|
53
|
+
expect(daysInMonth(2025, 0)).toBe(31);
|
|
54
|
+
});
|
|
55
|
+
it('returns 28 for February in a non-leap year', () => {
|
|
56
|
+
expect(daysInMonth(2023, 1)).toBe(28);
|
|
57
|
+
});
|
|
58
|
+
it('returns 29 for February in a leap year', () => {
|
|
59
|
+
expect(daysInMonth(2024, 1)).toBe(29);
|
|
60
|
+
});
|
|
61
|
+
it('returns 30 for April', () => {
|
|
62
|
+
expect(daysInMonth(2025, 3)).toBe(30);
|
|
63
|
+
});
|
|
64
|
+
it('returns 31 for December', () => {
|
|
65
|
+
expect(daysInMonth(2025, 11)).toBe(31);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
// ─── buildCalendarGrid ────────────────────────────────────────────────────
|
|
69
|
+
describe('buildCalendarGrid', () => {
|
|
70
|
+
it('always produces exactly 6 rows', () => {
|
|
71
|
+
const grid = buildCalendarGrid(2025, 0); // Jan 2025
|
|
72
|
+
expect(grid).toHaveLength(6);
|
|
73
|
+
});
|
|
74
|
+
it('each row has exactly 7 cells', () => {
|
|
75
|
+
const grid = buildCalendarGrid(2025, 0);
|
|
76
|
+
grid.forEach((row) => expect(row).toHaveLength(7));
|
|
77
|
+
});
|
|
78
|
+
it('includes all days of the target month', () => {
|
|
79
|
+
const grid = buildCalendarGrid(2025, 0); // 31 days
|
|
80
|
+
const dates = grid.flat();
|
|
81
|
+
const jan2025Days = dates.filter((d) => d.getFullYear() === 2025 && d.getMonth() === 0);
|
|
82
|
+
expect(jan2025Days).toHaveLength(31);
|
|
83
|
+
});
|
|
84
|
+
it('pads with previous-month days so the grid starts on Sunday', () => {
|
|
85
|
+
// Jan 1, 2025 is a Wednesday (day 3) — so first 3 cells should be Dec days
|
|
86
|
+
const grid = buildCalendarGrid(2025, 0);
|
|
87
|
+
const firstRow = grid[0];
|
|
88
|
+
const prevMonthCells = firstRow.filter((d) => d.getMonth() === 11); // December
|
|
89
|
+
expect(prevMonthCells.length).toBe(3); // Wed=3, so 3 Dec days prepend
|
|
90
|
+
});
|
|
91
|
+
it('pads with next-month days to complete 42 cells', () => {
|
|
92
|
+
const grid = buildCalendarGrid(2025, 0);
|
|
93
|
+
const dates = grid.flat();
|
|
94
|
+
const jan = dates.filter((d) => d.getMonth() === 0 && d.getFullYear() === 2025);
|
|
95
|
+
const dec = dates.filter((d) => d.getMonth() === 11 && d.getFullYear() === 2024);
|
|
96
|
+
const feb = dates.filter((d) => d.getMonth() === 1 && d.getFullYear() === 2025);
|
|
97
|
+
expect(jan.length + dec.length + feb.length).toBe(42);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
// ─── isSameDay ────────────────────────────────────────────────────────────
|
|
101
|
+
describe('isSameDay', () => {
|
|
102
|
+
it('returns true for the same calendar day', () => {
|
|
103
|
+
const a = new Date(2025, 5, 15, 8, 0); // 8am
|
|
104
|
+
const b = new Date(2025, 5, 15, 23, 59); // 11pm
|
|
105
|
+
expect(isSameDay(a, b)).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it('returns false for different days', () => {
|
|
108
|
+
expect(isSameDay(new Date(2025, 5, 15), new Date(2025, 5, 16))).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
it('returns false for same day but different month', () => {
|
|
111
|
+
expect(isSameDay(new Date(2025, 4, 15), new Date(2025, 5, 15))).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
// ─── isBeforeDay / isAfterDay ─────────────────────────────────────────────
|
|
115
|
+
describe('isBeforeDay', () => {
|
|
116
|
+
it('returns true when date is before min', () => {
|
|
117
|
+
expect(isBeforeDay(new Date(2025, 0, 1), new Date(2025, 0, 5))).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
it('returns false when date equals min', () => {
|
|
120
|
+
expect(isBeforeDay(new Date(2025, 0, 5), new Date(2025, 0, 5))).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
it('returns false when date is after min', () => {
|
|
123
|
+
expect(isBeforeDay(new Date(2025, 0, 10), new Date(2025, 0, 5))).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('isAfterDay', () => {
|
|
127
|
+
it('returns true when date is after max', () => {
|
|
128
|
+
expect(isAfterDay(new Date(2025, 0, 10), new Date(2025, 0, 5))).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
it('returns false when date equals max', () => {
|
|
131
|
+
expect(isAfterDay(new Date(2025, 0, 5), new Date(2025, 0, 5))).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
it('returns false when date is before max', () => {
|
|
134
|
+
expect(isAfterDay(new Date(2025, 0, 1), new Date(2025, 0, 5))).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
// ─── today ────────────────────────────────────────────────────────────────
|
|
138
|
+
describe('today', () => {
|
|
139
|
+
it('returns a Date with hours/minutes/seconds set to 0 (local midnight)', () => {
|
|
140
|
+
const t = today();
|
|
141
|
+
expect(t.getHours()).toBe(0);
|
|
142
|
+
expect(t.getMinutes()).toBe(0);
|
|
143
|
+
expect(t.getSeconds()).toBe(0);
|
|
144
|
+
expect(t.getMilliseconds()).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
it('matches the current local date', () => {
|
|
147
|
+
const now = new Date();
|
|
148
|
+
const t = today();
|
|
149
|
+
expect(t.getFullYear()).toBe(now.getFullYear());
|
|
150
|
+
expect(t.getMonth()).toBe(now.getMonth());
|
|
151
|
+
expect(t.getDate()).toBe(now.getDate());
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
// ─── parseTime / formatTime ───────────────────────────────────────────────
|
|
155
|
+
describe('parseTime', () => {
|
|
156
|
+
it('parses HH:MM into hours and minutes', () => {
|
|
157
|
+
expect(parseTime('09:30')).toEqual({ hours: 9, minutes: 30 });
|
|
158
|
+
expect(parseTime('23:59')).toEqual({ hours: 23, minutes: 59 });
|
|
159
|
+
expect(parseTime('00:00')).toEqual({ hours: 0, minutes: 0 });
|
|
160
|
+
});
|
|
161
|
+
it('returns zero hours and minutes for undefined', () => {
|
|
162
|
+
expect(parseTime(undefined)).toEqual({ hours: 0, minutes: 0 });
|
|
163
|
+
});
|
|
164
|
+
it('returns zero hours and minutes for empty string', () => {
|
|
165
|
+
expect(parseTime('')).toEqual({ hours: 0, minutes: 0 });
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('formatTime', () => {
|
|
169
|
+
it('formats as HH:MM with leading zeros', () => {
|
|
170
|
+
expect(formatTime(9, 5)).toBe('09:05');
|
|
171
|
+
expect(formatTime(0, 0)).toBe('00:00');
|
|
172
|
+
expect(formatTime(23, 59)).toBe('23:59');
|
|
173
|
+
});
|
|
174
|
+
it('round-trips with parseTime', () => {
|
|
175
|
+
const original = '14:07';
|
|
176
|
+
const { hours, minutes } = parseTime(original);
|
|
177
|
+
expect(formatTime(hours, minutes)).toBe(original);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=date-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-utils.test.js","sourceRoot":"","sources":["../../src/__tests__/date-utils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,5 +32,5 @@ function AccordionItemComponent({ item, isOpen, onToggle }) {
|
|
|
32
32
|
const triggerId = (0, react_1.useId)();
|
|
33
33
|
const panelId = (0, react_1.useId)();
|
|
34
34
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-accordion-item", children: [(0, jsx_runtime_1.jsxs)("button", { id: triggerId, type: "button", "aria-expanded": isOpen, "aria-controls": panelId, "aria-disabled": item.disabled || undefined, disabled: item.disabled, className: "tokis-accordion-trigger", onClick: () => { if (!item.disabled)
|
|
35
|
-
onToggle(item.value); }, children: [(0, jsx_runtime_1.jsx)("span", { style: { flex: 1, textAlign: '
|
|
35
|
+
onToggle(item.value); }, children: [(0, jsx_runtime_1.jsx)("span", { style: { flex: 1, textAlign: 'start' }, children: item.trigger }), (0, jsx_runtime_1.jsx)("svg", { className: "tokis-accordion-trigger__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })] }), (0, jsx_runtime_1.jsx)("div", { id: panelId, role: "region", "aria-labelledby": triggerId, className: "tokis-accordion-content", "data-open": isOpen ? 'true' : 'false', "aria-hidden": !isOpen, children: (0, jsx_runtime_1.jsx)("div", { className: "tokis-accordion-content-inner", children: item.content }) })] }));
|
|
36
36
|
}
|
|
@@ -14,5 +14,6 @@ function Checkbox({ label, description, checked, defaultChecked, indeterminate =
|
|
|
14
14
|
inputRef.current.indeterminate = indeterminate;
|
|
15
15
|
}
|
|
16
16
|
}, [indeterminate]);
|
|
17
|
-
return ((0, jsx_runtime_1.jsxs)("label", { className: (0, cn_js_1.cn)('tokis-checkbox-root', className), "data-disabled": disabled || undefined, htmlFor: inputId, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, type: "checkbox", id: inputId, name: name, value: value, checked: checked, defaultChecked: defaultChecked, disabled: disabled, onChange: (e) =>
|
|
17
|
+
return ((0, jsx_runtime_1.jsxs)("label", { className: (0, cn_js_1.cn)('tokis-checkbox-root', className), "data-disabled": disabled || undefined, htmlFor: inputId, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, type: "checkbox", id: inputId, name: name, value: value, checked: checked, defaultChecked: defaultChecked, disabled: disabled, onChange: (e) => { if (!disabled)
|
|
18
|
+
onChange?.(e.target.checked); }, className: "tokis-checkbox-native", "aria-label": !label ? ariaLabel : undefined, "aria-describedby": descId }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", className: "tokis-checkbox-control", "data-checked": checked && !indeterminate ? 'true' : undefined, "data-indeterminate": indeterminate ? 'true' : undefined }), (label || description) && ((0, jsx_runtime_1.jsxs)("div", { children: [label && (0, jsx_runtime_1.jsx)("span", { className: "tokis-checkbox-label", children: label }), description && (0, jsx_runtime_1.jsx)("p", { id: descId, className: "tokis-checkbox-description", children: description })] }))] }));
|
|
18
19
|
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DataGrid = DataGrid;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* DataGrid — feature-complete data table for Tokis.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Column sorting (asc/desc/none cycle, click header)
|
|
10
|
+
* - Quick filter toolbar
|
|
11
|
+
* - Per-column text filtering
|
|
12
|
+
* - Client-side pagination with page-size selector
|
|
13
|
+
* - Row selection (single / multi-select checkboxes)
|
|
14
|
+
* - Custom cell rendering (renderCell)
|
|
15
|
+
* - Loading skeleton state
|
|
16
|
+
* - Empty state
|
|
17
|
+
* - Windowed virtual scrolling (fixed height + rowHeight)
|
|
18
|
+
* - Controlled & uncontrolled patterns for sort, filter, page
|
|
19
|
+
* - ARIA: role="grid", aria-sort, aria-rowcount, aria-colcount
|
|
20
|
+
*/
|
|
21
|
+
const react_1 = require("react");
|
|
22
|
+
const cn_js_1 = require("../../utils/cn");
|
|
23
|
+
const utils_js_1 = require("./utils");
|
|
24
|
+
// ─── Sort icon ────────────────────────────────────────────────────────────────
|
|
25
|
+
function SortIcon({ direction }) {
|
|
26
|
+
if (!direction)
|
|
27
|
+
return (0, jsx_runtime_1.jsx)("span", { className: "tokis-dg-sort-icon tokis-dg-sort-icon--none", "aria-hidden": "true", children: "\u21C5" });
|
|
28
|
+
if (direction === 'asc')
|
|
29
|
+
return (0, jsx_runtime_1.jsx)("span", { className: "tokis-dg-sort-icon", "aria-hidden": "true", children: "\u2191" });
|
|
30
|
+
return (0, jsx_runtime_1.jsx)("span", { className: "tokis-dg-sort-icon", "aria-hidden": "true", children: "\u2193" });
|
|
31
|
+
}
|
|
32
|
+
function DataGridToolbar({ quickFilter, onQuickFilter }) {
|
|
33
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-toolbar", children: (0, jsx_runtime_1.jsx)("input", { type: "search", className: "tokis-dg-search", placeholder: "Search\u2026", value: quickFilter, "aria-label": "Quick filter", onChange: (e) => onQuickFilter(e.target.value) }) }));
|
|
34
|
+
}
|
|
35
|
+
function DataGridHeader({ columns, sortModel, onSort, checkboxSelection, allSelected, someSelected, onSelectAll, columnFilters, onColumnFilter, }) {
|
|
36
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-dg-header", role: "rowgroup", children: [(0, jsx_runtime_1.jsxs)("div", { className: "tokis-dg-row tokis-dg-row--header", role: "row", children: [checkboxSelection && ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell tokis-dg-cell--checkbox", role: "columnheader", "aria-label": "Select all", children: (0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: allSelected, ref: (el) => { if (el)
|
|
37
|
+
el.indeterminate = someSelected && !allSelected; }, onChange: (e) => onSelectAll(e.target.checked), "aria-label": "Select all rows" }) })), columns.map((col) => {
|
|
38
|
+
const isSorted = sortModel.field === col.field && sortModel.direction != null;
|
|
39
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)('tokis-dg-cell', 'tokis-dg-cell--header', col.sortable !== false && 'tokis-dg-cell--sortable'), role: "columnheader", "aria-sort": isSorted ? (sortModel.direction === 'asc' ? 'ascending' : 'descending') : 'none', style: { '--dg-align': col.headerAlign ?? col.align ?? 'start' }, onClick: () => col.sortable !== false && onSort(col.field), children: [col.renderHeader ? col.renderHeader(col) : (col.headerName ?? col.field), col.sortable !== false && ((0, jsx_runtime_1.jsx)(SortIcon, { direction: isSorted ? sortModel.direction : null }))] }, col.field));
|
|
40
|
+
})] }), columns.some((c) => c.filterable) && ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-dg-row tokis-dg-row--filter-row", role: "row", "aria-label": "Column filters", children: [checkboxSelection && (0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell tokis-dg-cell--checkbox", role: "cell" }), columns.map((col) => ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell tokis-dg-cell--filter", role: "cell", children: col.filterable && ((0, jsx_runtime_1.jsx)("input", { type: "search", className: "tokis-dg-col-filter", placeholder: `Filter ${col.headerName ?? col.field}…`, value: columnFilters[col.field] ?? '', "aria-label": `Filter by ${col.headerName ?? col.field}`, onChange: (e) => onColumnFilter(col.field, e.target.value) })) }, col.field)))] }))] }));
|
|
41
|
+
}
|
|
42
|
+
function DataGridPagination({ page, pageSize, pageSizeOptions, total, onPageChange, onPageSizeChange }) {
|
|
43
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
44
|
+
const start = page * pageSize + 1;
|
|
45
|
+
const end = Math.min((page + 1) * pageSize, total);
|
|
46
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-dg-pagination", role: "navigation", "aria-label": "Pagination", children: [(0, jsx_runtime_1.jsx)("span", { className: "tokis-dg-pagination-info", children: total === 0 ? '0 rows' : `${start}–${end} of ${total}` }), (0, jsx_runtime_1.jsx)("select", { className: "tokis-dg-page-size", value: pageSize, "aria-label": "Rows per page", onChange: (e) => { onPageSizeChange(Number(e.target.value)); onPageChange(0); }, children: pageSizeOptions.map((s) => (0, jsx_runtime_1.jsxs)("option", { value: s, children: [s, " / page"] }, s)) }), (0, jsx_runtime_1.jsx)("button", { className: "tokis-dg-page-btn", disabled: page === 0, onClick: () => onPageChange(0), "aria-label": "First page", children: "\u00AB" }), (0, jsx_runtime_1.jsx)("button", { className: "tokis-dg-page-btn", disabled: page === 0, onClick: () => onPageChange(page - 1), "aria-label": "Previous page", children: "\u2039" }), (0, jsx_runtime_1.jsx)("button", { className: "tokis-dg-page-btn", disabled: page >= totalPages - 1, onClick: () => onPageChange(page + 1), "aria-label": "Next page", children: "\u203A" }), (0, jsx_runtime_1.jsx)("button", { className: "tokis-dg-page-btn", disabled: page >= totalPages - 1, onClick: () => onPageChange(totalPages - 1), "aria-label": "Last page", children: "\u00BB" })] }));
|
|
47
|
+
}
|
|
48
|
+
// ─── Skeleton loader ──────────────────────────────────────────────────────────
|
|
49
|
+
function DataGridSkeleton({ rows, columns }) {
|
|
50
|
+
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Array.from({ length: rows }).map((_, r) => ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-row tokis-dg-row--skeleton", role: "row", "aria-hidden": "true", children: Array.from({ length: columns }).map((__, c) => ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell", children: (0, jsx_runtime_1.jsx)("span", { className: "tokis-dg-skeleton-bar" }) }, c))) }, r))) }));
|
|
51
|
+
}
|
|
52
|
+
// ─── Main DataGrid ────────────────────────────────────────────────────────────
|
|
53
|
+
function DataGrid({ columns, rows, rowIdField = 'id', loading = false, emptyText = 'No rows', checkboxSelection = false, selectionModel: selectionProp, onSelectionChange, onRowClick, sortModel: sortProp, onSortModelChange, filterModel: filterProp, onFilterModelChange, showToolbar = false, pageSize: pageSizeProp = 25, pageSizeOptions = [10, 25, 50, 100], page: pageProp, onPageChange, height, rowHeight = 48, className, }) {
|
|
54
|
+
// ── Internal state (uncontrolled fallbacks) ──
|
|
55
|
+
const [sortInternal, setSortInternal] = (0, react_1.useState)({ field: '', direction: null });
|
|
56
|
+
const [filterInternal, setFilterInternal] = (0, react_1.useState)({ quickFilter: '', columnFilters: {} });
|
|
57
|
+
const [pageInternal, setPageInternal] = (0, react_1.useState)(0);
|
|
58
|
+
const [pageSizeInternal, setPageSizeInternal] = (0, react_1.useState)(pageSizeProp);
|
|
59
|
+
const [selectionInternal, setSelectionInternal] = (0, react_1.useState)([]);
|
|
60
|
+
const sort = sortProp ?? sortInternal;
|
|
61
|
+
const filterModel = filterProp ?? filterInternal;
|
|
62
|
+
const page = pageProp ?? pageInternal;
|
|
63
|
+
const pageSize = pageSizeInternal;
|
|
64
|
+
const selection = selectionProp ?? selectionInternal;
|
|
65
|
+
const setSort = (0, react_1.useCallback)((model) => {
|
|
66
|
+
if (onSortModelChange)
|
|
67
|
+
onSortModelChange(model);
|
|
68
|
+
else
|
|
69
|
+
setSortInternal(model);
|
|
70
|
+
}, [onSortModelChange]);
|
|
71
|
+
const setFilter = (0, react_1.useCallback)((model) => {
|
|
72
|
+
if (onFilterModelChange)
|
|
73
|
+
onFilterModelChange(model);
|
|
74
|
+
else
|
|
75
|
+
setFilterInternal(model);
|
|
76
|
+
// Reset to page 0 on filter change
|
|
77
|
+
if (onPageChange)
|
|
78
|
+
onPageChange(0);
|
|
79
|
+
else
|
|
80
|
+
setPageInternal(0);
|
|
81
|
+
}, [onFilterModelChange, onPageChange]);
|
|
82
|
+
const setPage = (0, react_1.useCallback)((p) => {
|
|
83
|
+
if (onPageChange)
|
|
84
|
+
onPageChange(p);
|
|
85
|
+
else
|
|
86
|
+
setPageInternal(p);
|
|
87
|
+
}, [onPageChange]);
|
|
88
|
+
const setSelection = (0, react_1.useCallback)((ids) => {
|
|
89
|
+
if (onSelectionChange)
|
|
90
|
+
onSelectionChange(ids);
|
|
91
|
+
else
|
|
92
|
+
setSelectionInternal(ids);
|
|
93
|
+
}, [onSelectionChange]);
|
|
94
|
+
// ── Process rows ──
|
|
95
|
+
const processed = (0, react_1.useMemo)(() => {
|
|
96
|
+
let r = (0, utils_js_1.filterRows)(rows, filterModel, columns);
|
|
97
|
+
r = (0, utils_js_1.sortRows)(r, sort, columns);
|
|
98
|
+
return r;
|
|
99
|
+
}, [rows, filterModel, sort, columns]);
|
|
100
|
+
const paginated = (0, react_1.useMemo)(() => (0, utils_js_1.paginateRows)(processed, page, pageSize), [processed, page, pageSize]);
|
|
101
|
+
// ── Virtualization ──
|
|
102
|
+
const bodyRef = (0, react_1.useRef)(null);
|
|
103
|
+
const [scrollTop, setScrollTop] = (0, react_1.useState)(0);
|
|
104
|
+
const isVirtualized = typeof height === 'number';
|
|
105
|
+
(0, react_1.useEffect)(() => {
|
|
106
|
+
const el = bodyRef.current;
|
|
107
|
+
if (!el || !isVirtualized)
|
|
108
|
+
return;
|
|
109
|
+
const handler = () => setScrollTop(el.scrollTop);
|
|
110
|
+
el.addEventListener('scroll', handler, { passive: true });
|
|
111
|
+
return () => el.removeEventListener('scroll', handler);
|
|
112
|
+
}, [isVirtualized]);
|
|
113
|
+
const OVERSCAN = 5;
|
|
114
|
+
const visibleStart = isVirtualized ? Math.max(0, Math.floor(scrollTop / rowHeight) - OVERSCAN) : 0;
|
|
115
|
+
const visibleCount = isVirtualized ? Math.ceil((Number(height) / rowHeight) + OVERSCAN * 2) : paginated.length;
|
|
116
|
+
const visibleEnd = Math.min(paginated.length, visibleStart + visibleCount);
|
|
117
|
+
const visibleRows = isVirtualized ? paginated.slice(visibleStart, visibleEnd) : paginated;
|
|
118
|
+
const paddingTop = isVirtualized ? visibleStart * rowHeight : 0;
|
|
119
|
+
const paddingBottom = isVirtualized ? Math.max(0, (paginated.length - visibleEnd) * rowHeight) : 0;
|
|
120
|
+
// ── Selection helpers ──
|
|
121
|
+
const allOnPageSelected = paginated.length > 0 && paginated.every((r) => selection.includes(r[rowIdField]));
|
|
122
|
+
const someOnPageSelected = paginated.some((r) => selection.includes(r[rowIdField]));
|
|
123
|
+
const handleSelectAll = (checked) => {
|
|
124
|
+
if (checked) {
|
|
125
|
+
const newIds = paginated.map((r) => r[rowIdField]);
|
|
126
|
+
const merged = Array.from(new Set([...selection, ...newIds]));
|
|
127
|
+
setSelection(merged);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const pageIds = new Set(paginated.map((r) => r[rowIdField]));
|
|
131
|
+
setSelection(selection.filter((id) => !pageIds.has(id)));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const handleRowSelect = (rowId, checked) => {
|
|
135
|
+
if (checked)
|
|
136
|
+
setSelection([...selection, rowId]);
|
|
137
|
+
else
|
|
138
|
+
setSelection(selection.filter((id) => id !== rowId));
|
|
139
|
+
};
|
|
140
|
+
// ── Sort handler ──
|
|
141
|
+
const handleSort = (field) => {
|
|
142
|
+
setSort((0, utils_js_1.nextSortDirection)(sort.field, sort, field));
|
|
143
|
+
};
|
|
144
|
+
// ── Filter handlers ──
|
|
145
|
+
const handleQuickFilter = (v) => setFilter({ ...filterModel, quickFilter: v });
|
|
146
|
+
const handleColumnFilter = (field, v) => {
|
|
147
|
+
setFilter({ ...filterModel, columnFilters: { ...(filterModel.columnFilters ?? {}), [field]: v } });
|
|
148
|
+
};
|
|
149
|
+
// ── Render ──
|
|
150
|
+
const bodyStyle = height
|
|
151
|
+
? { height: typeof height === 'number' ? `${height}px` : height, overflowY: 'auto' }
|
|
152
|
+
: {};
|
|
153
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)('tokis-datagrid', className), role: "grid", "aria-rowcount": processed.length, "aria-colcount": columns.length, "aria-busy": loading, children: [showToolbar && ((0, jsx_runtime_1.jsx)(DataGridToolbar, { quickFilter: filterModel.quickFilter ?? '', onQuickFilter: handleQuickFilter })), (0, jsx_runtime_1.jsx)(DataGridHeader, { columns: columns, sortModel: sort, onSort: handleSort, checkboxSelection: checkboxSelection, allSelected: allOnPageSelected, someSelected: someOnPageSelected, onSelectAll: handleSelectAll, columnFilters: filterModel.columnFilters ?? {}, onColumnFilter: handleColumnFilter }), (0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-body", ref: bodyRef, style: bodyStyle, role: "rowgroup", children: loading ? ((0, jsx_runtime_1.jsx)(DataGridSkeleton, { rows: Math.min(pageSize, 8), columns: columns.length + (checkboxSelection ? 1 : 0) })) : processed.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-empty", role: "row", children: (0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell", role: "cell", style: { gridColumn: '1 / -1' }, children: emptyText }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [paddingTop > 0 && (0, jsx_runtime_1.jsx)("div", { style: { height: paddingTop }, "aria-hidden": "true" }), visibleRows.map((row, idx) => {
|
|
154
|
+
const rowId = row[rowIdField];
|
|
155
|
+
const isSelected = selection.includes(rowId);
|
|
156
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)('tokis-dg-row', isSelected && 'tokis-dg-row--selected'), role: "row", "aria-rowindex": visibleStart + idx + 2, "aria-selected": checkboxSelection ? isSelected : undefined, style: { height: `${rowHeight}px` }, onClick: (e) => onRowClick?.(row, e), children: [checkboxSelection && ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell tokis-dg-cell--checkbox", role: "gridcell", children: (0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: isSelected, "aria-label": `Select row ${rowId}`, onChange: (e) => handleRowSelect(rowId, e.target.checked), onClick: (e) => e.stopPropagation() }) })), columns.map((col) => {
|
|
157
|
+
const value = col.valueGetter ? col.valueGetter(row) : row[col.field];
|
|
158
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "tokis-dg-cell", role: "gridcell", style: { '--dg-align': col.align ?? 'start', ...col.cellStyle }, children: col.renderCell ? col.renderCell({ value, row, field: col.field }) : value }, col.field));
|
|
159
|
+
})] }, rowId ?? visibleStart + idx));
|
|
160
|
+
}), paddingBottom > 0 && (0, jsx_runtime_1.jsx)("div", { style: { height: paddingBottom }, "aria-hidden": "true" })] })) }), (0, jsx_runtime_1.jsx)(DataGridPagination, { page: page, pageSize: pageSize, pageSizeOptions: pageSizeOptions, total: processed.length, onPageChange: setPage, onPageSizeChange: setPageSizeInternal })] }));
|
|
161
|
+
}
|