@gtkx/components 1.0.0-rc.4 → 1.1.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 (86) hide show
  1. package/README.md +5 -5
  2. package/dist/column-view.d.ts +2 -2
  3. package/dist/column-view.d.ts.map +1 -1
  4. package/dist/column-view.js +14 -12
  5. package/dist/column-view.js.map +1 -1
  6. package/dist/grid-view.js +1 -1
  7. package/dist/grid-view.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal/cells.d.ts +31 -23
  12. package/dist/internal/cells.d.ts.map +1 -1
  13. package/dist/internal/cells.js +168 -77
  14. package/dist/internal/cells.js.map +1 -1
  15. package/dist/internal/collection-index.d.ts +8 -11
  16. package/dist/internal/collection-index.d.ts.map +1 -1
  17. package/dist/internal/collection-index.js +53 -73
  18. package/dist/internal/collection-index.js.map +1 -1
  19. package/dist/internal/collection-model.d.ts +24 -4
  20. package/dist/internal/collection-model.d.ts.map +1 -1
  21. package/dist/internal/collection-model.js +247 -96
  22. package/dist/internal/collection-model.js.map +1 -1
  23. package/dist/internal/collection.d.ts +8 -10
  24. package/dist/internal/collection.d.ts.map +1 -1
  25. package/dist/internal/collection.js +38 -48
  26. package/dist/internal/collection.js.map +1 -1
  27. package/dist/internal/controlled-sync.d.ts +11 -0
  28. package/dist/internal/controlled-sync.d.ts.map +1 -0
  29. package/dist/internal/controlled-sync.js +19 -0
  30. package/dist/internal/controlled-sync.js.map +1 -0
  31. package/dist/internal/drop-down.d.ts.map +1 -1
  32. package/dist/internal/drop-down.js +27 -15
  33. package/dist/internal/drop-down.js.map +1 -1
  34. package/dist/internal/expansion.d.ts +3 -2
  35. package/dist/internal/expansion.d.ts.map +1 -1
  36. package/dist/internal/expansion.js +73 -48
  37. package/dist/internal/expansion.js.map +1 -1
  38. package/dist/internal/keys.d.ts +5 -0
  39. package/dist/internal/keys.d.ts.map +1 -0
  40. package/dist/internal/keys.js +13 -0
  41. package/dist/internal/keys.js.map +1 -0
  42. package/dist/internal/selection.d.ts +3 -2
  43. package/dist/internal/selection.d.ts.map +1 -1
  44. package/dist/internal/selection.js +68 -35
  45. package/dist/internal/selection.js.map +1 -1
  46. package/dist/internal/slots.d.ts +10 -0
  47. package/dist/internal/slots.d.ts.map +1 -0
  48. package/dist/internal/slots.js +43 -0
  49. package/dist/internal/slots.js.map +1 -0
  50. package/dist/internal/tree-expansion.d.ts +19 -0
  51. package/dist/internal/tree-expansion.d.ts.map +1 -0
  52. package/dist/internal/tree-expansion.js +72 -0
  53. package/dist/internal/tree-expansion.js.map +1 -0
  54. package/dist/internal/tree-order.d.ts +26 -0
  55. package/dist/internal/tree-order.d.ts.map +1 -0
  56. package/dist/internal/tree-order.js +109 -0
  57. package/dist/internal/tree-order.js.map +1 -0
  58. package/dist/internal/use-collection.d.ts +1 -1
  59. package/dist/internal/use-collection.d.ts.map +1 -1
  60. package/dist/internal/use-collection.js +4 -4
  61. package/dist/internal/use-collection.js.map +1 -1
  62. package/dist/list-view.d.ts.map +1 -1
  63. package/dist/list-view.js +6 -6
  64. package/dist/list-view.js.map +1 -1
  65. package/dist/types.d.ts +72 -22
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js.map +1 -1
  68. package/package.json +17 -9
  69. package/src/column-view.tsx +28 -25
  70. package/src/grid-view.tsx +1 -1
  71. package/src/index.ts +3 -0
  72. package/src/internal/cells.tsx +339 -117
  73. package/src/internal/collection-index.ts +68 -92
  74. package/src/internal/collection-model.ts +327 -112
  75. package/src/internal/collection.ts +45 -65
  76. package/src/internal/controlled-sync.ts +34 -0
  77. package/src/internal/drop-down.tsx +41 -20
  78. package/src/internal/expansion.ts +95 -70
  79. package/src/internal/keys.ts +18 -0
  80. package/src/internal/selection.tsx +100 -51
  81. package/src/internal/slots.ts +67 -0
  82. package/src/internal/tree-expansion.ts +115 -0
  83. package/src/internal/tree-order.ts +208 -0
  84. package/src/internal/use-collection.tsx +5 -5
  85. package/src/list-view.tsx +14 -10
  86. package/src/types.ts +78 -20
@@ -1,137 +1,118 @@
1
1
  import type { ListItem, ListSection } from "../types.js";
2
+ import { decodePartAt, encodePart } from "./keys.js";
2
3
 
3
4
  type Level = {
4
- key: string;
5
- ids: string[];
5
+ path: string;
6
+ items: ListItem[];
6
7
  expandableFlags: boolean[];
7
8
  };
8
9
 
9
10
  type CollectionIndex = {
10
11
  isTree: boolean;
11
- size: number;
12
12
  groups: Level[];
13
- children: Map<string, Level>;
14
- hasId: (id: string) => boolean;
15
- itemFor: (id: string) => ListItem | undefined;
16
- sectionFor: (id: string) => unknown;
17
- positionFor: (id: string) => number;
13
+ childLevel: (level: Level, slot: number) => Level | undefined;
14
+ levelFor: (levelPath: string) => Level | undefined;
15
+ itemAt: (levelPath: string, slot: number) => ListItem | undefined;
16
+ sectionFor: (levelPath: string) => unknown;
18
17
  };
19
18
 
20
19
  type IndexState = {
21
20
  isTree: boolean;
22
21
  groups: Level[];
23
- children: Map<string, Level>;
24
- itemsById: Map<string, ListItem>;
25
- sections: ListSection[] | undefined;
26
- positions: Map<string, number> | null;
27
- starts: number[] | null;
22
+ levels: Map<string, Level>;
23
+ sectionValues: Map<string, unknown>;
28
24
  };
29
25
 
30
- const ROOT_LEVEL_KEY = "";
26
+ type ParentItem = ListItem & { children: ListItem[] };
31
27
 
32
- const hasChildren = (item: ListItem): boolean => item.children !== undefined && item.children.length > 0;
33
- const childLevelKey = (id: string): string => `child:${id}`;
28
+ const emptyLevel = (): Level => ({ path: "", items: [], expandableFlags: [] });
29
+ const hasChildren = (item: ListItem): item is ParentItem => item.children !== undefined && item.children.length > 0;
34
30
 
35
- function collectChildren(state: IndexState, level: Level, item: ListItem): void {
36
- if (!state.isTree) {
37
- return;
38
- }
39
-
40
- const canExpand = hasChildren(item);
41
- level.expandableFlags.push(canExpand);
42
-
43
- if (!canExpand) {
44
- return;
45
- }
46
-
47
- const key = childLevelKey(item.id);
48
- state.children.set(key, collectLevel(state, key, item.children ?? []));
49
- }
50
-
51
- function collectLevel(state: IndexState, key: string, items: ListItem[]): Level {
52
- const level: Level = { key, ids: [], expandableFlags: [] };
53
-
54
- for (const item of items) {
55
- level.ids.push(item.id);
56
- state.itemsById.set(item.id, item);
57
- collectChildren(state, level, item);
58
- }
31
+ function newLevel(state: IndexState, path: string, items: ListItem[]): Level {
32
+ const expandableFlags = state.isTree ? items.map((item) => hasChildren(item)) : [];
33
+ const level: Level = { path, items, expandableFlags };
34
+ state.levels.set(path, level);
59
35
 
60
36
  return level;
61
37
  }
62
38
 
63
- function buildPositions(state: IndexState): void {
64
- const positions: Map<string, number> = new Map();
65
- const starts: number[] = [];
66
- let offset = 0;
39
+ function slotItems(state: IndexState, parent: Level, slot: number): ListItem[] | undefined {
40
+ const owner = state.levels.get(parent.path) === parent ? parent : levelFor(state, parent.path);
41
+ const item = owner?.items[slot];
67
42
 
68
- for (const level of state.groups) {
69
- starts.push(offset);
43
+ return item !== undefined && hasChildren(item) ? item.children : undefined;
44
+ }
70
45
 
71
- for (const id of level.ids) {
72
- positions.set(id, offset);
73
- offset += 1;
74
- }
46
+ function childLevel(state: IndexState, parent: Level, slot: number): Level | undefined {
47
+ if (!state.isTree) {
48
+ return undefined;
75
49
  }
76
50
 
77
- state.positions = positions;
78
- state.starts = starts;
79
- }
51
+ const path = parent.path + encodePart(String(slot));
52
+ const cached = state.levels.get(path);
80
53
 
81
- function positionFor(state: IndexState, id: string): number {
82
- if (state.isTree) {
83
- return -1;
54
+ if (cached !== undefined) {
55
+ return cached;
84
56
  }
85
57
 
86
- if (state.positions === null) {
87
- buildPositions(state);
88
- }
58
+ const items = slotItems(state, parent, slot);
89
59
 
90
- return state.positions?.get(id) ?? -1;
60
+ return items === undefined ? undefined : newLevel(state, path, items);
91
61
  }
92
62
 
93
- function sectionAt(starts: number[], position: number): number {
94
- let low = 0;
95
- let high = starts.length - 1;
63
+ function descendPath(state: IndexState, levelPath: string, group: string): Level | undefined {
64
+ let level = state.levels.get(encodePart(group));
65
+ let offset = encodePart(group).length;
96
66
 
97
- while (low < high) {
98
- const middle = Math.ceil((low + high) / 2);
67
+ while (level !== undefined && offset < levelPath.length) {
68
+ const part = decodePartAt(levelPath, offset);
99
69
 
100
- if ((starts[middle] ?? 0) <= position) {
101
- low = middle;
102
- } else {
103
- high = middle - 1;
70
+ if (part === null) {
71
+ return undefined;
104
72
  }
73
+
74
+ level = childLevel(state, level, Number(part));
75
+ offset += encodePart(part).length;
105
76
  }
106
77
 
107
- return low;
78
+ return level;
108
79
  }
109
80
 
110
- function sectionFor(state: IndexState, id: string): unknown {
111
- const sections = state.sections;
112
- const position = positionFor(state, id);
81
+ function levelFor(state: IndexState, levelPath: string): Level | undefined {
82
+ const cached = state.levels.get(levelPath);
113
83
 
114
- if (sections === undefined || position < 0 || state.starts === null) {
115
- return undefined;
84
+ if (cached !== undefined) {
85
+ return cached;
116
86
  }
117
87
 
118
- return sections[sectionAt(state.starts, position)]?.value;
88
+ const group = decodePartAt(levelPath, 0);
89
+
90
+ return group === null ? undefined : descendPath(state, levelPath, group);
119
91
  }
120
92
 
121
93
  function buildGroups(state: IndexState, source: ListItem[], sections: ListSection[] | undefined): Level[] {
122
94
  if (sections === undefined) {
123
- return [collectLevel(state, ROOT_LEVEL_KEY, source)];
95
+ return [newLevel(state, encodePart("0"), source)];
124
96
  }
125
97
 
126
- return sections.map((section) => collectLevel(state, section.id, section.data));
98
+ return sections.map((section, group) => {
99
+ const path = encodePart(String(group));
100
+ state.sectionValues.set(path, section.value);
101
+
102
+ return newLevel(state, path, section.data);
103
+ });
127
104
  }
128
105
 
129
106
  function isTreeSource(source: ListItem[], sections: ListSection[] | undefined, isFlat: boolean): boolean {
130
- if (sections !== undefined || isFlat) {
107
+ if (isFlat) {
131
108
  return false;
132
109
  }
133
110
 
134
- return source.some((item) => hasChildren(item));
111
+ if (sections === undefined) {
112
+ return source.some((item) => hasChildren(item));
113
+ }
114
+
115
+ return sections.some((section) => section.data.some((item) => hasChildren(item)));
135
116
  }
136
117
 
137
118
  function createCollectionIndex(
@@ -144,25 +125,20 @@ function createCollectionIndex(
144
125
  const state: IndexState = {
145
126
  isTree: isTreeSource(source, sections, isFlat),
146
127
  groups: [],
147
- children: new Map(),
148
- itemsById: new Map(),
149
- sections,
150
- positions: null,
151
- starts: null,
128
+ levels: new Map(),
129
+ sectionValues: new Map(),
152
130
  };
153
131
 
154
132
  state.groups = buildGroups(state, source, sections);
155
133
 
156
134
  return {
157
135
  isTree: state.isTree,
158
- size: state.itemsById.size,
159
136
  groups: state.groups,
160
- children: state.children,
161
- hasId: (id) => state.itemsById.has(id),
162
- itemFor: (id) => state.itemsById.get(id),
163
- sectionFor: (id) => sectionFor(state, id),
164
- positionFor: (id) => positionFor(state, id),
137
+ childLevel: (level, slot) => childLevel(state, level, slot),
138
+ levelFor: (levelPath) => levelFor(state, levelPath),
139
+ itemAt: (levelPath, slot) => levelFor(state, levelPath)?.items[slot],
140
+ sectionFor: (levelPath) => state.sectionValues.get(levelPath),
165
141
  };
166
142
  }
167
143
 
168
- export { createCollectionIndex, childLevelKey, ROOT_LEVEL_KEY, type CollectionIndex, type Level };
144
+ export { createCollectionIndex, emptyLevel, type CollectionIndex, type Level };