@lotics/ui 2.6.1 → 3.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 (45) hide show
  1. package/package.json +1 -15
  2. package/src/react_native.d.ts +2 -2
  3. package/src/cell_date.tsx +0 -30
  4. package/src/cell_date_format.test.ts +0 -32
  5. package/src/cell_date_format.ts +0 -73
  6. package/src/cell_number.test.ts +0 -42
  7. package/src/cell_number.tsx +0 -25
  8. package/src/cell_number_format.ts +0 -42
  9. package/src/cell_select.tsx +0 -68
  10. package/src/cell_text.tsx +0 -45
  11. package/src/grid/data_grid.tsx +0 -2003
  12. package/src/grid/data_grid_columns.test.ts +0 -72
  13. package/src/grid/data_grid_columns.ts +0 -30
  14. package/src/grid/data_grid_context.ts +0 -119
  15. package/src/grid/dispatch_safely.ts +0 -39
  16. package/src/grid/engine.module.css +0 -114
  17. package/src/grid/engine.tsx +0 -1042
  18. package/src/grid/helpers.ts +0 -205
  19. package/src/grid/layout.test.ts +0 -515
  20. package/src/grid/layout.ts +0 -425
  21. package/src/grid/recycling.test.ts +0 -236
  22. package/src/grid/recycling.ts +0 -172
  23. package/src/grid/row_cell.module.css +0 -105
  24. package/src/grid/row_cell.tsx +0 -313
  25. package/src/grid/search_highlight.ts +0 -71
  26. package/src/grid/select_cell.tsx +0 -58
  27. package/src/grid/select_group_summary_cell.tsx +0 -76
  28. package/src/grid/select_header_cell.tsx +0 -32
  29. package/src/grid/skeleton_row.module.css +0 -34
  30. package/src/grid/skeleton_row.tsx +0 -20
  31. package/src/grid/use_grid_groups.ts +0 -311
  32. package/src/grid/use_scroll_to_cell.ts +0 -135
  33. package/src/grid/use_virtual_grid.ts +0 -383
  34. package/src/grid/visibility.test.ts +0 -208
  35. package/src/grid/visibility.ts +0 -77
  36. package/src/kanban/constants.ts +0 -18
  37. package/src/kanban/default_renderers.tsx +0 -160
  38. package/src/kanban/drag_preview.tsx +0 -157
  39. package/src/kanban/index.ts +0 -13
  40. package/src/kanban/insert_card_zone.tsx +0 -135
  41. package/src/kanban/kanban_board.tsx +0 -635
  42. package/src/kanban/kanban_card.tsx +0 -321
  43. package/src/kanban/kanban_column.tsx +0 -499
  44. package/src/kanban/placeholders.tsx +0 -54
  45. package/src/kanban/types.ts +0 -116
@@ -1,205 +0,0 @@
1
- export function isNumberString(value: string): boolean {
2
- if (isNaN(Number(value.trim()))) {
3
- return false;
4
- }
5
-
6
- return true;
7
- }
8
-
9
- export function isObject(o: unknown): o is Record<string, unknown> {
10
- if (typeof o === "object") {
11
- return (
12
- (o === null || Array.isArray(o) || typeof o == "function" || o.constructor === Date) === false
13
- );
14
- }
15
-
16
- return false;
17
- }
18
-
19
- export function sum(numbers: number[]): number {
20
- return numbers.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
21
- }
22
-
23
- export function maxBy<T>(a: T[], getValue: (item: T) => number): number {
24
- if (a.length === 0) {
25
- return -Infinity;
26
- }
27
-
28
- let maxVal = getValue(a[0]);
29
- for (let i = 1; i < a.length; i++) {
30
- const val = getValue(a[i]);
31
- if (val > maxVal) {
32
- maxVal = val;
33
- }
34
- }
35
- return maxVal;
36
- }
37
-
38
- export function sumBy<T>(a: T[], getValue: (item: T) => number): number {
39
- let total = 0;
40
- for (let i = 0; i < a.length; i++) {
41
- total += getValue(a[i]);
42
- }
43
- return total;
44
- }
45
-
46
- export function max(...args: number[]): number {
47
- return Math.max(...args);
48
- }
49
-
50
- export function min(...args: number[]): number {
51
- return Math.min(...args);
52
- }
53
-
54
- export function isEqual(a: unknown, b: unknown): boolean {
55
- if (a === b) {
56
- return true;
57
- }
58
-
59
- if (isArray(a) && isArray(b)) {
60
- return isArrayEqual(a, b);
61
- }
62
-
63
- if (isObject(a) && isObject(b)) {
64
- return isObjectEqual(a, b);
65
- }
66
-
67
- return a !== a && b !== b;
68
- }
69
-
70
- function isArrayEqual<T>(a: T[], b: T[]): boolean {
71
- if (a.length !== b.length) {
72
- return false;
73
- }
74
-
75
- for (let i = 0; i < a.length; i++) {
76
- const itemA = a[i];
77
- const itemB = b[i];
78
-
79
- if (isEqual(itemA, itemB) === false) {
80
- return false;
81
- }
82
- }
83
-
84
- return true;
85
- }
86
-
87
- function isObjectEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {
88
- if (a.constructor !== b.constructor) {
89
- return false;
90
- }
91
-
92
- if (a.valueOf !== Object.prototype.valueOf) {
93
- return a.valueOf() === b.valueOf();
94
- }
95
-
96
- if (a.toString !== Object.prototype.toString) {
97
- return a.toString() === b.toString();
98
- }
99
-
100
- const keys = Object.keys(a);
101
- const length = keys.length;
102
- if (length !== Object.keys(b).length) {
103
- return false;
104
- }
105
-
106
- for (let i = length; i-- !== 0; ) {
107
- if (!Object.prototype.hasOwnProperty.call(b, keys[i])) {
108
- return false;
109
- }
110
- }
111
-
112
- for (let i = length; i-- !== 0; ) {
113
- const key = keys[i];
114
-
115
- const subA = a[key];
116
- const subB = b[key];
117
-
118
- if (!isEqual(subA, subB)) {
119
- return false;
120
- }
121
- }
122
-
123
- return true;
124
- }
125
-
126
- export function isEmpty<T extends Record<string, unknown>>(data: T): boolean;
127
- export function isEmpty<T>(data: T[]): boolean;
128
- export function isEmpty(data: unknown[] | Record<string, unknown>): boolean {
129
- if (isArray(data)) {
130
- return data.length === 0;
131
- }
132
-
133
- return Object.keys(data).length === 0;
134
- }
135
-
136
- /**
137
- * Returns array of values found in left and right array.
138
- * Other values not intersected by, will be taken from left array.
139
- *
140
- * If `merge` is true, objects will be merged, with the objects from right array overriding the other array objects.
141
- */
142
- export function intersectBy<T, K>(
143
- a: T[],
144
- b: K[],
145
- getValue: (item: T | K) => string | number,
146
- merge = false,
147
- ): T[] {
148
- const bMap: { [key: string]: K } = {};
149
-
150
- for (let i = 0; i < b.length; i++) {
151
- const itemB = b[i];
152
- const val = getValue(itemB);
153
- bMap[val] = itemB;
154
- }
155
-
156
- const result: T[] = [];
157
-
158
- for (let i = 0; i < a.length; i++) {
159
- const itemA = a[i];
160
- const val = getValue(itemA);
161
- const itemB = bMap[val];
162
-
163
- if (itemB !== undefined) {
164
- if (merge) {
165
- result.push({ ...itemA, ...itemB });
166
- } else {
167
- result.push(itemA);
168
- }
169
- }
170
- }
171
-
172
- return result;
173
- }
174
-
175
- /** Returns array of values in left array that are not in the right array */
176
- export function differenceBy<T, K>(
177
- a: T[],
178
- b: K[],
179
- getValue: (item: T | K) => string | number,
180
- ): T[] {
181
- const bMap: { [key: string]: K } = {};
182
-
183
- for (let i = 0; i < b.length; i++) {
184
- const itemB = b[i];
185
- const val = getValue(itemB);
186
- bMap[val] = itemB;
187
- }
188
-
189
- const result: T[] = [];
190
-
191
- for (let i = 0; i < a.length; i++) {
192
- const itemA = a[i];
193
- const val = getValue(itemA);
194
-
195
- if (bMap[val] === undefined) {
196
- result.push(itemA);
197
- }
198
- }
199
-
200
- return result;
201
- }
202
-
203
- export function isArray<T>(arg: unknown): arg is T[] {
204
- return Array.isArray(arg);
205
- }
@@ -1,515 +0,0 @@
1
- import { getColumns, getRows, GridGroup, GridRow, GridColumn, groupPathToKey } from "./layout";
2
-
3
- test("getColumns - basic columns", () => {
4
- const result = getColumns([100, 200, 100, 200, 100, 200]);
5
-
6
- const expected: GridColumn[] = [
7
- { column: 0, width: 100, x: 0 },
8
- { column: 1, width: 200, x: 100 },
9
- { column: 2, width: 100, x: 300 },
10
- { column: 3, width: 200, x: 400 },
11
- { column: 4, width: 100, x: 600 },
12
- { column: 5, width: 200, x: 700 },
13
- ];
14
-
15
- expect(result).toEqual(expected);
16
- });
17
-
18
- test("getRows - single flat group", () => {
19
- const groups: GridGroup[] = [
20
- {
21
- type: "row_group",
22
- rowCount: 2,
23
- },
24
- ];
25
-
26
- const rows = getRows(groups, 40, 40, 40, 72, null);
27
-
28
- const expected: GridRow[] = [
29
- {
30
- type: "row",
31
- height: 40,
32
- y: 0,
33
- rowPath: [0, 0],
34
- },
35
- {
36
- type: "row",
37
- height: 40,
38
- y: 40,
39
- rowPath: [0, 1],
40
- },
41
- ];
42
-
43
- expect(rows).toEqual(expected);
44
- });
45
-
46
- test("getRows - nested with leaf rows", () => {
47
- const groups: GridGroup[] = [
48
- {
49
- type: "group",
50
- children: [
51
- {
52
- type: "row_group",
53
- rowCount: 2,
54
- },
55
- ],
56
- },
57
- {
58
- type: "group",
59
- children: [
60
- {
61
- type: "row_group",
62
- rowCount: 2,
63
- },
64
- {
65
- type: "row_group",
66
- rowCount: 2,
67
- },
68
- ],
69
- },
70
- ];
71
-
72
- const rows = getRows(groups, 40, 40, 40, 72, null);
73
-
74
- const expected: GridRow[] = [
75
- {
76
- type: "group_heading",
77
- height: 40,
78
- y: 0,
79
- groupPath: [0],
80
- },
81
- {
82
- type: "group_summary",
83
- height: 40,
84
- y: 40,
85
- groupPath: [0],
86
- },
87
- {
88
- type: "group_heading",
89
- height: 40,
90
- y: 80,
91
- groupPath: [0, 0],
92
- },
93
- {
94
- type: "group_summary",
95
- height: 40,
96
- y: 120,
97
- groupPath: [0, 0],
98
- },
99
- {
100
- type: "row",
101
- height: 40,
102
- y: 160,
103
- rowPath: [0, 0, 0],
104
- },
105
- {
106
- type: "row",
107
- height: 40,
108
- y: 200,
109
- rowPath: [0, 0, 1],
110
- },
111
- { type: "spacer", height: 72, y: 240 },
112
- {
113
- type: "group_heading",
114
- height: 40,
115
- y: 312,
116
- groupPath: [1],
117
- },
118
- {
119
- type: "group_summary",
120
- height: 40,
121
- y: 352,
122
- groupPath: [1],
123
- },
124
- {
125
- type: "group_heading",
126
- height: 40,
127
- y: 392,
128
- groupPath: [1, 0],
129
- },
130
- {
131
- type: "group_summary",
132
- height: 40,
133
- y: 432,
134
- groupPath: [1, 0],
135
- },
136
- {
137
- type: "row",
138
- height: 40,
139
- y: 472,
140
- rowPath: [1, 0, 0],
141
- },
142
- {
143
- type: "row",
144
- height: 40,
145
- y: 512,
146
- rowPath: [1, 0, 1],
147
- },
148
- {
149
- type: "group_heading",
150
- height: 40,
151
- y: 552,
152
- groupPath: [1, 1],
153
- },
154
- {
155
- type: "group_summary",
156
- height: 40,
157
- y: 592,
158
- groupPath: [1, 1],
159
- },
160
- {
161
- type: "row",
162
- height: 40,
163
- y: 632,
164
- rowPath: [1, 1, 0],
165
- },
166
- {
167
- type: "row",
168
- height: 40,
169
- y: 672,
170
- rowPath: [1, 1, 1],
171
- },
172
- { type: "spacer", height: 72, y: 712 },
173
- ];
174
-
175
- expect(rows).toEqual(expected);
176
- });
177
-
178
- test("getRows - nested with empty leaf rows", () => {
179
- const groups: GridGroup[] = [
180
- {
181
- type: "group",
182
- children: [
183
- {
184
- type: "row_group",
185
- rowCount: 0,
186
- },
187
- ],
188
- },
189
- ];
190
-
191
- const rows = getRows(groups, 40, 40, 40, 72, null);
192
-
193
- const expected: GridRow[] = [
194
- {
195
- type: "group_heading",
196
- height: 40,
197
- y: 0,
198
- groupPath: [0],
199
- },
200
- {
201
- type: "group_summary",
202
- height: 40,
203
- y: 40,
204
- groupPath: [0],
205
- },
206
- {
207
- type: "group_heading",
208
- height: 40,
209
- y: 80,
210
- groupPath: [0, 0],
211
- },
212
- {
213
- type: "group_summary",
214
- height: 40,
215
- y: 120,
216
- groupPath: [0, 0],
217
- },
218
- { type: "spacer", height: 72, y: 160 },
219
- ];
220
-
221
- expect(rows).toEqual(expected);
222
- });
223
-
224
- test("getRows - nested with collapsed ancestor", () => {
225
- const groups: GridGroup[] = [
226
- {
227
- type: "group",
228
- children: [
229
- {
230
- type: "row_group",
231
- rowCount: 1,
232
- },
233
- ],
234
- },
235
- ];
236
-
237
- const rows = getRows(groups, 40, 40, 40, 72, [groupPathToKey([0])]);
238
-
239
- // Group summary is always shown even when collapsed
240
- const expected: GridRow[] = [
241
- {
242
- type: "group_heading",
243
- height: 40,
244
- y: 0,
245
- groupPath: [0],
246
- },
247
- {
248
- type: "group_summary",
249
- height: 40,
250
- y: 40,
251
- groupPath: [0],
252
- },
253
- { type: "spacer", height: 72, y: 80 },
254
- ];
255
-
256
- expect(rows).toEqual(expected);
257
- });
258
-
259
- test("getRows - nested collapsed child", () => {
260
- const groups: GridGroup[] = [
261
- {
262
- type: "group",
263
- children: [
264
- {
265
- type: "row_group",
266
- rowCount: 5,
267
- },
268
- ],
269
- },
270
- ];
271
-
272
- const rows = getRows(groups, 40, 40, 40, 72, [groupPathToKey([0, 0])]);
273
-
274
- // Group summary is always shown even when collapsed
275
- const expected: GridRow[] = [
276
- {
277
- type: "group_heading",
278
- height: 40,
279
- y: 0,
280
- groupPath: [0],
281
- },
282
- {
283
- type: "group_summary",
284
- height: 40,
285
- y: 40,
286
- groupPath: [0],
287
- },
288
- {
289
- type: "group_heading",
290
- height: 40,
291
- y: 80,
292
- groupPath: [0, 0],
293
- },
294
- {
295
- type: "group_summary",
296
- height: 40,
297
- y: 120,
298
- groupPath: [0, 0],
299
- },
300
- { type: "spacer", height: 72, y: 160 },
301
- ];
302
-
303
- expect(rows).toEqual(expected);
304
- });
305
-
306
- test("getColumns - with offset", () => {
307
- const result = getColumns([100, 200, 100], 3);
308
-
309
- const expected: GridColumn[] = [
310
- { column: 3, width: 100, x: 0 },
311
- { column: 4, width: 200, x: 100 },
312
- { column: 5, width: 100, x: 300 },
313
- ];
314
-
315
- expect(result).toEqual(expected);
316
- });
317
-
318
- test("getColumns - empty columns", () => {
319
- const result = getColumns([]);
320
-
321
- expect(result).toEqual([]);
322
- });
323
-
324
- test("getColumns - single column", () => {
325
- const result = getColumns([150]);
326
-
327
- const expected: GridColumn[] = [{ column: 0, width: 150, x: 0 }];
328
-
329
- expect(result).toEqual(expected);
330
- });
331
-
332
- test("getRows - multiple top-level groups", () => {
333
- const groups: GridGroup[] = [
334
- { type: "row_group", rowCount: 1 },
335
- { type: "row_group", rowCount: 1 },
336
- { type: "row_group", rowCount: 1 },
337
- ];
338
-
339
- const rows = getRows(groups, 40, 40, 40, 72, null);
340
-
341
- expect(rows).toHaveLength(12); // 3 headings + 3 summaries + 3 rows + 3 spacers
342
- expect(rows.filter((r) => r.type === "group_heading")).toHaveLength(3);
343
- expect(rows.filter((r) => r.type === "group_summary")).toHaveLength(3);
344
- expect(rows.filter((r) => r.type === "row")).toHaveLength(3);
345
- expect(rows.filter((r) => r.type === "spacer")).toHaveLength(3);
346
- });
347
-
348
- test("getRows - deeply nested groups", () => {
349
- const groups: GridGroup[] = [
350
- {
351
- type: "group",
352
- children: [
353
- {
354
- type: "group",
355
- children: [
356
- {
357
- type: "group",
358
- children: [
359
- {
360
- type: "row_group",
361
- rowCount: 1,
362
- },
363
- ],
364
- },
365
- ],
366
- },
367
- ],
368
- },
369
- ];
370
-
371
- const rows = getRows(groups, 40, 40, 40, 72, null);
372
-
373
- const expected: GridRow[] = [
374
- { type: "group_heading", height: 40, y: 0, groupPath: [0] },
375
- { type: "group_summary", height: 40, y: 40, groupPath: [0] },
376
- { type: "group_heading", height: 40, y: 80, groupPath: [0, 0] },
377
- { type: "group_summary", height: 40, y: 120, groupPath: [0, 0] },
378
- { type: "group_heading", height: 40, y: 160, groupPath: [0, 0, 0] },
379
- { type: "group_summary", height: 40, y: 200, groupPath: [0, 0, 0] },
380
- { type: "group_heading", height: 40, y: 240, groupPath: [0, 0, 0, 0] },
381
- { type: "group_summary", height: 40, y: 280, groupPath: [0, 0, 0, 0] },
382
- { type: "row", height: 40, y: 320, rowPath: [0, 0, 0, 0, 0] },
383
- { type: "spacer", height: 72, y: 360 },
384
- ];
385
-
386
- expect(rows).toEqual(expected);
387
- });
388
-
389
- test("getRows - multiple collapsed groups", () => {
390
- const groups: GridGroup[] = [
391
- {
392
- type: "group",
393
- children: [{ type: "row_group", rowCount: 2 }],
394
- },
395
- {
396
- type: "group",
397
- children: [{ type: "row_group", rowCount: 2 }],
398
- },
399
- {
400
- type: "group",
401
- children: [{ type: "row_group", rowCount: 2 }],
402
- },
403
- ];
404
-
405
- const rows = getRows(groups, 40, 40, 40, 72, [groupPathToKey([0]), groupPathToKey([2])]);
406
-
407
- // Groups 0 and 2 are collapsed, group 1 is expanded
408
- // Group summary rows are always shown even when collapsed
409
- expect(rows.filter((r) => r.type === "row")).toHaveLength(2); // Only from group 1
410
- expect(rows.filter((r) => r.type === "group_heading")).toHaveLength(4); // [0], [1], [1,0], [2]
411
- expect(rows.filter((r) => r.type === "group_summary")).toHaveLength(4); // [0], [1], [1,0], [2] (all groups have summaries)
412
- expect(rows.filter((r) => r.type === "spacer")).toHaveLength(3);
413
- });
414
-
415
- test("getRows - empty groups array", () => {
416
- const rows = getRows([], 40, 40, 40, 72, null);
417
-
418
- expect(rows).toEqual([]);
419
- });
420
-
421
- test("getRows - default spacerHeight emits a spacer after each top-level group", () => {
422
- const groups: GridGroup[] = [
423
- { type: "row_group", rowCount: 1 },
424
- { type: "row_group", rowCount: 1 },
425
- ];
426
-
427
- const rows = getRows(groups, 40, 40, 40, 56, null);
428
-
429
- const expected: GridRow[] = [
430
- { type: "group_heading", height: 40, y: 0, groupPath: [0] },
431
- { type: "group_summary", height: 40, y: 40, groupPath: [0] },
432
- { type: "row", height: 40, y: 80, rowPath: [0, 0] },
433
- { type: "spacer", height: 56, y: 120 },
434
- { type: "group_heading", height: 40, y: 176, groupPath: [1] },
435
- { type: "group_summary", height: 40, y: 216, groupPath: [1] },
436
- { type: "row", height: 40, y: 256, rowPath: [1, 0] },
437
- { type: "spacer", height: 56, y: 296 },
438
- ];
439
-
440
- expect(rows).toEqual(expected);
441
- });
442
-
443
- test("getRows - spacerHeight 0 emits no spacer descriptor", () => {
444
- const groups: GridGroup[] = [
445
- { type: "row_group", rowCount: 1 },
446
- { type: "row_group", rowCount: 1 },
447
- ];
448
-
449
- const rows = getRows(groups, 40, 40, 40, 0, null);
450
-
451
- const expected: GridRow[] = [
452
- { type: "group_heading", height: 40, y: 0, groupPath: [0] },
453
- { type: "group_summary", height: 40, y: 40, groupPath: [0] },
454
- { type: "row", height: 40, y: 80, rowPath: [0, 0] },
455
- { type: "group_heading", height: 40, y: 120, groupPath: [1] },
456
- { type: "group_summary", height: 40, y: 160, groupPath: [1] },
457
- { type: "row", height: 40, y: 200, rowPath: [1, 0] },
458
- ];
459
-
460
- expect(rows).toEqual(expected);
461
- expect(rows.some((r) => r.type === "spacer")).toBe(false);
462
- });
463
-
464
- test("getRows - spacerHeight 0 with collapsed, single, and zero groups", () => {
465
- // Collapsed top-level group: heading + summary only, no spacer
466
- const collapsed = getRows(
467
- [{ type: "group", children: [{ type: "row_group", rowCount: 3 }] }],
468
- 40,
469
- 40,
470
- 40,
471
- 0,
472
- [groupPathToKey([0])],
473
- );
474
- expect(collapsed).toEqual([
475
- { type: "group_heading", height: 40, y: 0, groupPath: [0] },
476
- { type: "group_summary", height: 40, y: 40, groupPath: [0] },
477
- ]);
478
-
479
- // Single top-level group: no trailing spacer
480
- const single = getRows([{ type: "row_group", rowCount: 2 }], 40, 40, 40, 0, null);
481
- expect(single).toEqual([
482
- { type: "row", height: 40, y: 0, rowPath: [0, 0] },
483
- { type: "row", height: 40, y: 40, rowPath: [0, 1] },
484
- ]);
485
-
486
- // Zero groups: empty layout
487
- expect(getRows([], 40, 40, 40, 0, null)).toEqual([]);
488
- });
489
-
490
- test("getRows - collapsed parent hides all descendants", () => {
491
- const groups: GridGroup[] = [
492
- {
493
- type: "group",
494
- children: [
495
- {
496
- type: "group",
497
- children: [
498
- {
499
- type: "row_group",
500
- rowCount: 5,
501
- },
502
- ],
503
- },
504
- ],
505
- },
506
- ];
507
-
508
- const rows = getRows(groups, 40, 40, 40, 72, [groupPathToKey([0])]);
509
-
510
- // Top-level group heading, summary (always shown), and spacer should appear
511
- expect(rows).toHaveLength(3);
512
- expect(rows[0].type).toBe("group_heading");
513
- expect(rows[1].type).toBe("group_summary");
514
- expect(rows[2].type).toBe("spacer");
515
- });