@gtkx/components 0.21.0 → 1.0.0-rc.2

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 (192) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +173 -0
  3. package/dist/adw/index.d.ts +3 -0
  4. package/dist/adw/index.d.ts.map +1 -0
  5. package/dist/adw/index.js +2 -0
  6. package/dist/adw/index.js.map +1 -0
  7. package/dist/adw/toast.d.ts +10 -0
  8. package/dist/adw/toast.d.ts.map +1 -0
  9. package/dist/adw/toast.js +51 -0
  10. package/dist/adw/toast.js.map +1 -0
  11. package/dist/adw/types.d.ts +28 -0
  12. package/dist/adw/types.d.ts.map +1 -0
  13. package/dist/adw/types.js +2 -0
  14. package/dist/adw/types.js.map +1 -0
  15. package/dist/column-view.d.ts +6 -14
  16. package/dist/column-view.d.ts.map +1 -1
  17. package/dist/column-view.js +98 -100
  18. package/dist/column-view.js.map +1 -1
  19. package/dist/drop-down.d.ts +8 -37
  20. package/dist/drop-down.d.ts.map +1 -1
  21. package/dist/drop-down.js +109 -118
  22. package/dist/drop-down.js.map +1 -1
  23. package/dist/grid-view.d.ts +4 -11
  24. package/dist/grid-view.d.ts.map +1 -1
  25. package/dist/grid-view.js +25 -19
  26. package/dist/grid-view.js.map +1 -1
  27. package/dist/index.d.ts +6 -18
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -14
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal/cells.d.ts +50 -0
  32. package/dist/internal/cells.d.ts.map +1 -0
  33. package/dist/internal/cells.js +185 -0
  34. package/dist/internal/cells.js.map +1 -0
  35. package/dist/internal/collection-index.d.ts +21 -0
  36. package/dist/internal/collection-index.d.ts.map +1 -0
  37. package/dist/internal/collection-index.js +106 -0
  38. package/dist/internal/collection-index.js.map +1 -0
  39. package/dist/internal/collection-model.d.ts +12 -0
  40. package/dist/internal/collection-model.d.ts.map +1 -0
  41. package/dist/internal/collection-model.js +164 -0
  42. package/dist/internal/collection-model.js.map +1 -0
  43. package/dist/internal/collection.d.ts +18 -0
  44. package/dist/internal/collection.d.ts.map +1 -0
  45. package/dist/internal/collection.js +64 -0
  46. package/dist/internal/collection.js.map +1 -0
  47. package/dist/internal/expansion.d.ts +9 -0
  48. package/dist/internal/expansion.d.ts.map +1 -0
  49. package/dist/internal/expansion.js +75 -0
  50. package/dist/internal/expansion.js.map +1 -0
  51. package/dist/internal/selection.d.ts +12 -0
  52. package/dist/internal/selection.d.ts.map +1 -0
  53. package/dist/internal/selection.js +99 -0
  54. package/dist/internal/selection.js.map +1 -0
  55. package/dist/internal/use-collection.d.ts +18 -0
  56. package/dist/internal/use-collection.d.ts.map +1 -0
  57. package/dist/internal/use-collection.js +42 -0
  58. package/dist/internal/use-collection.js.map +1 -0
  59. package/dist/internal/use-widget-ref.d.ts +4 -0
  60. package/dist/internal/use-widget-ref.d.ts.map +1 -0
  61. package/dist/internal/use-widget-ref.js +8 -0
  62. package/dist/internal/use-widget-ref.js.map +1 -0
  63. package/dist/list-view.d.ts +4 -11
  64. package/dist/list-view.d.ts.map +1 -1
  65. package/dist/list-view.js +32 -28
  66. package/dist/list-view.js.map +1 -1
  67. package/dist/size-group.d.ts +12 -0
  68. package/dist/size-group.d.ts.map +1 -0
  69. package/dist/size-group.js +41 -0
  70. package/dist/size-group.js.map +1 -0
  71. package/dist/types.d.ts +104 -161
  72. package/dist/types.d.ts.map +1 -1
  73. package/dist/types.js.map +1 -1
  74. package/package.json +62 -52
  75. package/src/adw/index.ts +2 -0
  76. package/src/adw/toast.tsx +74 -0
  77. package/src/adw/types.ts +32 -0
  78. package/src/column-view.tsx +184 -184
  79. package/src/drop-down.tsx +173 -227
  80. package/src/grid-view.tsx +29 -65
  81. package/src/index.ts +16 -25
  82. package/src/internal/cells.tsx +345 -0
  83. package/src/internal/collection-index.ts +168 -0
  84. package/src/internal/collection-model.ts +246 -0
  85. package/src/internal/collection.ts +104 -0
  86. package/src/internal/expansion.ts +123 -0
  87. package/src/internal/selection.tsx +159 -0
  88. package/src/internal/use-collection.tsx +70 -0
  89. package/src/internal/use-widget-ref.ts +11 -0
  90. package/src/list-view.tsx +45 -78
  91. package/src/size-group.tsx +77 -0
  92. package/src/types.ts +171 -191
  93. package/dist/collection-view.d.ts +0 -30
  94. package/dist/collection-view.d.ts.map +0 -1
  95. package/dist/collection-view.js +0 -52
  96. package/dist/collection-view.js.map +0 -1
  97. package/dist/column-view-column.d.ts +0 -17
  98. package/dist/column-view-column.d.ts.map +0 -1
  99. package/dist/column-view-column.js +0 -51
  100. package/dist/column-view-column.js.map +0 -1
  101. package/dist/constraint-layout-apply.d.ts +0 -6
  102. package/dist/constraint-layout-apply.d.ts.map +0 -1
  103. package/dist/constraint-layout-apply.js +0 -107
  104. package/dist/constraint-layout-apply.js.map +0 -1
  105. package/dist/constraint-layout.d.ts +0 -13
  106. package/dist/constraint-layout.d.ts.map +0 -1
  107. package/dist/constraint-layout.js +0 -85
  108. package/dist/constraint-layout.js.map +0 -1
  109. package/dist/contexts/column-view-context.d.ts +0 -17
  110. package/dist/contexts/column-view-context.d.ts.map +0 -1
  111. package/dist/contexts/column-view-context.js +0 -10
  112. package/dist/contexts/column-view-context.js.map +0 -1
  113. package/dist/hooks/use-drop-down-selection.d.ts +0 -15
  114. package/dist/hooks/use-drop-down-selection.d.ts.map +0 -1
  115. package/dist/hooks/use-drop-down-selection.js +0 -35
  116. package/dist/hooks/use-drop-down-selection.js.map +0 -1
  117. package/dist/hooks/use-header-menu.d.ts +0 -4
  118. package/dist/hooks/use-header-menu.d.ts.map +0 -1
  119. package/dist/hooks/use-header-menu.js +0 -32
  120. package/dist/hooks/use-header-menu.js.map +0 -1
  121. package/dist/hooks/use-list-model.d.ts +0 -20
  122. package/dist/hooks/use-list-model.d.ts.map +0 -1
  123. package/dist/hooks/use-list-model.js +0 -70
  124. package/dist/hooks/use-list-model.js.map +0 -1
  125. package/dist/hooks/use-model-installation.d.ts +0 -9
  126. package/dist/hooks/use-model-installation.d.ts.map +0 -1
  127. package/dist/hooks/use-model-installation.js +0 -20
  128. package/dist/hooks/use-model-installation.js.map +0 -1
  129. package/dist/hooks/use-realized-slots.d.ts +0 -20
  130. package/dist/hooks/use-realized-slots.d.ts.map +0 -1
  131. package/dist/hooks/use-realized-slots.js +0 -66
  132. package/dist/hooks/use-realized-slots.js.map +0 -1
  133. package/dist/hooks/use-selection-model.d.ts +0 -12
  134. package/dist/hooks/use-selection-model.d.ts.map +0 -1
  135. package/dist/hooks/use-selection-model.js +0 -111
  136. package/dist/hooks/use-selection-model.js.map +0 -1
  137. package/dist/hooks/use-sort-handler.d.ts +0 -12
  138. package/dist/hooks/use-sort-handler.d.ts.map +0 -1
  139. package/dist/hooks/use-sort-handler.js +0 -87
  140. package/dist/hooks/use-sort-handler.js.map +0 -1
  141. package/dist/list-portal-host.d.ts +0 -11
  142. package/dist/list-portal-host.d.ts.map +0 -1
  143. package/dist/list-portal-host.js +0 -8
  144. package/dist/list-portal-host.js.map +0 -1
  145. package/dist/list-slot.d.ts +0 -14
  146. package/dist/list-slot.d.ts.map +0 -1
  147. package/dist/list-slot.js +0 -29
  148. package/dist/list-slot.js.map +0 -1
  149. package/dist/menu-items.d.ts +0 -5
  150. package/dist/menu-items.d.ts.map +0 -1
  151. package/dist/menu-items.js +0 -48
  152. package/dist/menu-items.js.map +0 -1
  153. package/dist/menu.d.ts +0 -18
  154. package/dist/menu.d.ts.map +0 -1
  155. package/dist/menu.js +0 -26
  156. package/dist/menu.js.map +0 -1
  157. package/dist/utils/item-resolver.d.ts +0 -28
  158. package/dist/utils/item-resolver.d.ts.map +0 -1
  159. package/dist/utils/item-resolver.js +0 -111
  160. package/dist/utils/item-resolver.js.map +0 -1
  161. package/dist/utils/list-item-flatten.d.ts +0 -32
  162. package/dist/utils/list-item-flatten.d.ts.map +0 -1
  163. package/dist/utils/list-item-flatten.js +0 -88
  164. package/dist/utils/list-item-flatten.js.map +0 -1
  165. package/dist/utils/position-only-model.d.ts +0 -10
  166. package/dist/utils/position-only-model.d.ts.map +0 -1
  167. package/dist/utils/position-only-model.js +0 -83
  168. package/dist/utils/position-only-model.js.map +0 -1
  169. package/dist/utils/realized-slot-store.d.ts +0 -30
  170. package/dist/utils/realized-slot-store.d.ts.map +0 -1
  171. package/dist/utils/realized-slot-store.js +0 -103
  172. package/dist/utils/realized-slot-store.js.map +0 -1
  173. package/src/collection-view.tsx +0 -125
  174. package/src/column-view-column.tsx +0 -73
  175. package/src/constraint-layout-apply.ts +0 -121
  176. package/src/constraint-layout.tsx +0 -130
  177. package/src/contexts/column-view-context.ts +0 -28
  178. package/src/hooks/use-drop-down-selection.ts +0 -53
  179. package/src/hooks/use-header-menu.ts +0 -33
  180. package/src/hooks/use-list-model.ts +0 -145
  181. package/src/hooks/use-model-installation.ts +0 -25
  182. package/src/hooks/use-realized-slots.ts +0 -93
  183. package/src/hooks/use-selection-model.ts +0 -121
  184. package/src/hooks/use-sort-handler.ts +0 -102
  185. package/src/list-portal-host.tsx +0 -27
  186. package/src/list-slot.tsx +0 -56
  187. package/src/menu-items.ts +0 -48
  188. package/src/menu.tsx +0 -34
  189. package/src/utils/item-resolver.ts +0 -143
  190. package/src/utils/list-item-flatten.ts +0 -113
  191. package/src/utils/position-only-model.ts +0 -115
  192. package/src/utils/realized-slot-store.ts +0 -126
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-index.js","sourceRoot":"","sources":["../../src/internal/collection-index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Item, Section } from \"../types.js\";\n\ntype Level = {\n key: string;\n ids: string[];\n isExpandable: boolean[];\n};\n\ntype CollectionIndex = {\n isTree: boolean;\n size: number;\n groups: Level[];\n children: Map<string, Level>;\n has: (id: string) => boolean;\n itemFor: (id: string) => Item | undefined;\n sectionFor: (id: string) => unknown;\n positionFor: (id: string) => number;\n};\n\ntype IndexState = {\n isTree: boolean;\n groups: Level[];\n children: Map<string, Level>;\n itemsById: Map<string, Item>;\n sections: Section[] | undefined;\n positions: Map<string, number> | null;\n starts: number[] | null;\n};\n\nconst ROOT_LEVEL_KEY = \"\";\n\nconst hasChildren = (item: Item): boolean => item.children !== undefined && item.children.length > 0;\nconst childLevelKey = (id: string): string => `child:${id}`;\n\nfunction collectChildren(state: IndexState, level: Level, item: Item): void {\n if (!state.isTree) {\n return;\n }\n\n const canExpand = hasChildren(item);\n level.isExpandable.push(canExpand);\n\n if (!canExpand) {\n return;\n }\n\n const key = childLevelKey(item.id);\n state.children.set(key, collectLevel(state, key, item.children ?? []));\n}\n\nfunction collectLevel(state: IndexState, key: string, items: Item[]): Level {\n const level: Level = { key, ids: [], isExpandable: [] };\n\n for (const item of items) {\n level.ids.push(item.id);\n state.itemsById.set(item.id, item);\n collectChildren(state, level, item);\n }\n\n return level;\n}\n\nfunction buildPositions(state: IndexState): void {\n const positions: Map<string, number> = new Map();\n const starts: number[] = [];\n let offset = 0;\n\n for (const level of state.groups) {\n starts.push(offset);\n\n for (const id of level.ids) {\n positions.set(id, offset);\n offset += 1;\n }\n }\n\n state.positions = positions;\n state.starts = starts;\n}\n\nfunction positionFor(state: IndexState, id: string): number {\n if (state.isTree) {\n return -1;\n }\n\n if (state.positions === null) {\n buildPositions(state);\n }\n\n return state.positions?.get(id) ?? -1;\n}\n\nfunction sectionAt(starts: number[], position: number): number {\n let low = 0;\n let high = starts.length - 1;\n\n while (low < high) {\n const middle = Math.ceil((low + high) / 2);\n\n if ((starts[middle] ?? 0) <= position) {\n low = middle;\n } else {\n high = middle - 1;\n }\n }\n\n return low;\n}\n\nfunction sectionFor(state: IndexState, id: string): unknown {\n const sections = state.sections;\n const position = positionFor(state, id);\n\n if (sections === undefined || position < 0 || state.starts === null) {\n return undefined;\n }\n\n return sections[sectionAt(state.starts, position)]?.value;\n}\n\nfunction buildGroups(state: IndexState, source: Item[], sections: Section[] | undefined): Level[] {\n if (sections === undefined) {\n return [collectLevel(state, ROOT_LEVEL_KEY, source)];\n }\n\n return sections.map((section) => collectLevel(state, section.id, section.data));\n}\n\nfunction isTreeSource(source: Item[], sections: Section[] | undefined, isFlat: boolean): boolean {\n if (sections !== undefined || isFlat) {\n return false;\n }\n\n return source.some((item) => hasChildren(item));\n}\n\nfunction createCollectionIndex(\n items: Item[] | undefined,\n sections: Section[] | undefined,\n isFlat: boolean,\n): CollectionIndex {\n const source = items ?? [];\n\n const state: IndexState = {\n isTree: isTreeSource(source, sections, isFlat),\n groups: [],\n children: new Map(),\n itemsById: new Map(),\n sections,\n positions: null,\n starts: null,\n };\n\n state.groups = buildGroups(state, source, sections);\n\n return {\n isTree: state.isTree,\n size: state.itemsById.size,\n groups: state.groups,\n children: state.children,\n has: (id) => state.itemsById.has(id),\n itemFor: (id) => state.itemsById.get(id),\n sectionFor: (id) => sectionFor(state, id),\n positionFor: (id) => positionFor(state, id),\n };\n}\n\nexport { createCollectionIndex, childLevelKey, ROOT_LEVEL_KEY, type CollectionIndex, type Level };\n"]}
@@ -0,0 +1,12 @@
1
+ import * as GObject from "@gtkx/gi/gobject";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
+ import type { CollectionIndex } from "./collection-index.js";
4
+ type CollectionModel = {
5
+ model: Gtk.FlattenListModel;
6
+ treeModel: () => Gtk.TreeListModel | null;
7
+ sync: (index: CollectionIndex) => void;
8
+ };
9
+ declare function getId(value: GObject.Object | null): string | null;
10
+ declare function createCollectionModel(): CollectionModel;
11
+ export { createCollectionModel, getId, type CollectionModel };
12
+ //# sourceMappingURL=collection-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-model.d.ts","sourceRoot":"","sources":["../../src/internal/collection-model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAS,MAAM,uBAAuB,CAAC;AAwBpE,KAAK,eAAe,GAAG;IACnB,KAAK,EAAE,GAAG,CAAC,gBAAgB,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC;IAC1C,IAAI,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC1C,CAAC;AAMF,iBAAS,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAI1D;AAuLD,iBAAS,qBAAqB,IAAI,eAAe,CAmBhD;AAED,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,KAAK,eAAe,EAAE,CAAC"}
@@ -0,0 +1,164 @@
1
+ import * as Gio from "@gtkx/gi/gio";
2
+ import * as GObject from "@gtkx/gi/gobject";
3
+ import * as Gtk from "@gtkx/gi/gtk";
4
+ import { childLevelKey, ROOT_LEVEL_KEY } from "./collection-index.js";
5
+ const OBJECT_CACHE_MAX = 1 << 20;
6
+ const newStore = () => new Gio.ListStore({ itemType: GObject.TYPE_OBJECT });
7
+ function getId(value) {
8
+ const item = value instanceof Gtk.TreeListRow ? value.getItem() : value;
9
+ return item instanceof Gtk.StringObject ? item.getString() : null;
10
+ }
11
+ function objectFor(state, id) {
12
+ const existing = state.objects.get(id);
13
+ if (existing !== undefined) {
14
+ return existing;
15
+ }
16
+ const created = Gtk.StringObject.new(id);
17
+ state.objects.set(id, created);
18
+ return created;
19
+ }
20
+ function commonPrefix(previous, next, max) {
21
+ let count = 0;
22
+ while (count < max && previous[count] === next[count]) {
23
+ count += 1;
24
+ }
25
+ return count;
26
+ }
27
+ function commonSuffix(previous, next, max) {
28
+ let count = 0;
29
+ while (count < max && previous[previous.length - 1 - count] === next[next.length - 1 - count]) {
30
+ count += 1;
31
+ }
32
+ return count;
33
+ }
34
+ function spliceRange(previous, next) {
35
+ const shared = Math.min(previous.length, next.length);
36
+ const start = commonPrefix(previous, next, shared);
37
+ if (start === previous.length && start === next.length) {
38
+ return null;
39
+ }
40
+ const tail = commonSuffix(previous, next, shared - start);
41
+ return { start, removed: previous.length - start - tail, added: next.slice(start, next.length - tail) };
42
+ }
43
+ function levelFor(state, key) {
44
+ const existing = state.levels.get(key);
45
+ if (existing !== undefined) {
46
+ return existing;
47
+ }
48
+ const created = { store: newStore(), ids: [], canExpand: new Map() };
49
+ state.levels.set(key, created);
50
+ return created;
51
+ }
52
+ function refreshExpandable(current, level) {
53
+ const next = new Map();
54
+ for (const [index, id] of level.ids.entries()) {
55
+ const isWanted = level.isExpandable[index] ?? false;
56
+ const previous = current.canExpand.get(id);
57
+ next.set(id, isWanted);
58
+ if (previous !== undefined && previous !== isWanted) {
59
+ current.store.itemsChanged(index, 1, 1);
60
+ }
61
+ }
62
+ current.canExpand = next;
63
+ }
64
+ function didSpliceLevel(state, level) {
65
+ const current = levelFor(state, level.key);
66
+ const range = spliceRange(current.ids, level.ids);
67
+ if (range === null) {
68
+ return false;
69
+ }
70
+ current.store.splice(range.start, range.removed, range.added.map((id) => objectFor(state, id)));
71
+ current.ids = [...level.ids];
72
+ return true;
73
+ }
74
+ function dropUnvisited(state, visited) {
75
+ for (const key of state.levels.keys()) {
76
+ if (!visited.has(key)) {
77
+ state.levels.delete(key);
78
+ }
79
+ }
80
+ }
81
+ // Items keep their identity by id, so a collection that shrinks keeps its objects cached: filtering a long
82
+ // list down and back up reuses them instead of rebuilding one GObject per row. Incremental filters shrink and
83
+ // grow the list many times per keystroke, so the cache is only swept once it passes an absolute bound.
84
+ function pruneObjects(state, index) {
85
+ if (state.objects.size <= OBJECT_CACHE_MAX) {
86
+ return;
87
+ }
88
+ for (const id of state.objects.keys()) {
89
+ if (!index.has(id)) {
90
+ state.objects.delete(id);
91
+ }
92
+ }
93
+ }
94
+ function childStoreFor(state, object) {
95
+ const id = getId(object);
96
+ if (id === null) {
97
+ return null;
98
+ }
99
+ return state.levels.get(childLevelKey(id))?.store ?? null;
100
+ }
101
+ function ensureTree(state) {
102
+ state.tree ??= Gtk.TreeListModel.new(levelFor(state, ROOT_LEVEL_KEY).store, false, false, (object) => childStoreFor(state, object));
103
+ return state.tree;
104
+ }
105
+ function desiredRootModels(state, index) {
106
+ if (index.isTree) {
107
+ return [ensureTree(state)];
108
+ }
109
+ return index.groups.map((level) => levelFor(state, level.key).store);
110
+ }
111
+ function hasSameModels(previous, next) {
112
+ return previous.length === next.length && next.every((model, index) => previous[index] === model);
113
+ }
114
+ function syncRoot(state, index) {
115
+ const next = desiredRootModels(state, index);
116
+ if (hasSameModels(state.rootModels, next)) {
117
+ return;
118
+ }
119
+ state.root.splice(0, state.rootModels.length, next);
120
+ state.rootModels = next;
121
+ }
122
+ function syncModel(state, index) {
123
+ const levels = [...index.groups, ...index.children.values()];
124
+ const visited = new Set();
125
+ let didChange = false;
126
+ for (const level of levels) {
127
+ didChange = didSpliceLevel(state, level) || didChange;
128
+ visited.add(level.key);
129
+ }
130
+ if (didChange || visited.size !== state.levels.size) {
131
+ dropUnvisited(state, visited);
132
+ pruneObjects(state, index);
133
+ }
134
+ syncRoot(state, index);
135
+ refreshExpandableLevels(state, index, levels);
136
+ }
137
+ function refreshExpandableLevels(state, index, levels) {
138
+ if (!index.isTree) {
139
+ return;
140
+ }
141
+ for (const level of levels) {
142
+ refreshExpandable(levelFor(state, level.key), level);
143
+ }
144
+ }
145
+ function createCollectionModel() {
146
+ const root = newStore();
147
+ const state = {
148
+ root,
149
+ rootModels: [],
150
+ model: Gtk.FlattenListModel.new(root),
151
+ objects: new Map(),
152
+ levels: new Map(),
153
+ tree: null,
154
+ };
155
+ return {
156
+ model: state.model,
157
+ treeModel: () => state.tree,
158
+ sync: (index) => {
159
+ syncModel(state, index);
160
+ },
161
+ };
162
+ }
163
+ export { createCollectionModel, getId };
164
+ //# sourceMappingURL=collection-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-model.js","sourceRoot":"","sources":["../../src/internal/collection-model.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gio from \"@gtkx/gi/gio\";\nimport * as GObject from \"@gtkx/gi/gobject\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport type { CollectionIndex, Level } from \"./collection-index.js\";\nimport { childLevelKey, ROOT_LEVEL_KEY } from \"./collection-index.js\";\n\ntype LevelState = {\n store: Gio.ListStore;\n ids: string[];\n canExpand: Map<string, boolean>;\n};\n\ntype SpliceRange = {\n start: number;\n removed: number;\n added: string[];\n};\n\ntype ModelState = {\n root: Gio.ListStore;\n rootModels: GObject.Object[];\n model: Gtk.FlattenListModel;\n objects: Map<string, Gtk.StringObject>;\n levels: Map<string, LevelState>;\n tree: Gtk.TreeListModel | null;\n};\n\ntype CollectionModel = {\n model: Gtk.FlattenListModel;\n treeModel: () => Gtk.TreeListModel | null;\n sync: (index: CollectionIndex) => void;\n};\n\nconst OBJECT_CACHE_MAX = 1 << 20;\n\nconst newStore = (): Gio.ListStore => new Gio.ListStore({ itemType: GObject.TYPE_OBJECT });\n\nfunction getId(value: GObject.Object | null): string | null {\n const item = value instanceof Gtk.TreeListRow ? value.getItem() : value;\n\n return item instanceof Gtk.StringObject ? item.getString() : null;\n}\n\nfunction objectFor(state: ModelState, id: string): Gtk.StringObject {\n const existing = state.objects.get(id);\n\n if (existing !== undefined) {\n return existing;\n }\n\n const created = Gtk.StringObject.new(id);\n state.objects.set(id, created);\n\n return created;\n}\n\nfunction commonPrefix(previous: string[], next: string[], max: number): number {\n let count = 0;\n\n while (count < max && previous[count] === next[count]) {\n count += 1;\n }\n\n return count;\n}\n\nfunction commonSuffix(previous: string[], next: string[], max: number): number {\n let count = 0;\n\n while (count < max && previous[previous.length - 1 - count] === next[next.length - 1 - count]) {\n count += 1;\n }\n\n return count;\n}\n\nfunction spliceRange(previous: string[], next: string[]): SpliceRange | null {\n const shared = Math.min(previous.length, next.length);\n const start = commonPrefix(previous, next, shared);\n\n if (start === previous.length && start === next.length) {\n return null;\n }\n\n const tail = commonSuffix(previous, next, shared - start);\n\n return { start, removed: previous.length - start - tail, added: next.slice(start, next.length - tail) };\n}\n\nfunction levelFor(state: ModelState, key: string): LevelState {\n const existing = state.levels.get(key);\n\n if (existing !== undefined) {\n return existing;\n }\n\n const created: LevelState = { store: newStore(), ids: [], canExpand: new Map() };\n state.levels.set(key, created);\n\n return created;\n}\n\nfunction refreshExpandable(current: LevelState, level: Level): void {\n const next: Map<string, boolean> = new Map();\n\n for (const [index, id] of level.ids.entries()) {\n const isWanted = level.isExpandable[index] ?? false;\n const previous = current.canExpand.get(id);\n next.set(id, isWanted);\n\n if (previous !== undefined && previous !== isWanted) {\n current.store.itemsChanged(index, 1, 1);\n }\n }\n\n current.canExpand = next;\n}\n\nfunction didSpliceLevel(state: ModelState, level: Level): boolean {\n const current = levelFor(state, level.key);\n const range = spliceRange(current.ids, level.ids);\n\n if (range === null) {\n return false;\n }\n\n current.store.splice(range.start, range.removed, range.added.map((id) => objectFor(state, id)));\n current.ids = [...level.ids];\n\n return true;\n}\n\nfunction dropUnvisited(state: ModelState, visited: Set<string>): void {\n for (const key of state.levels.keys()) {\n if (!visited.has(key)) {\n state.levels.delete(key);\n }\n }\n}\n\n// Items keep their identity by id, so a collection that shrinks keeps its objects cached: filtering a long\n// list down and back up reuses them instead of rebuilding one GObject per row. Incremental filters shrink and\n// grow the list many times per keystroke, so the cache is only swept once it passes an absolute bound.\nfunction pruneObjects(state: ModelState, index: CollectionIndex): void {\n if (state.objects.size <= OBJECT_CACHE_MAX) {\n return;\n }\n\n for (const id of state.objects.keys()) {\n if (!index.has(id)) {\n state.objects.delete(id);\n }\n }\n}\n\nfunction childStoreFor(state: ModelState, object: GObject.Object): Gio.ListStore | null {\n const id = getId(object);\n\n if (id === null) {\n return null;\n }\n\n return state.levels.get(childLevelKey(id))?.store ?? null;\n}\n\nfunction ensureTree(state: ModelState): Gtk.TreeListModel {\n state.tree ??= Gtk.TreeListModel.new(levelFor(state, ROOT_LEVEL_KEY).store, false, false, (object) =>\n childStoreFor(state, object));\n\n return state.tree;\n}\n\nfunction desiredRootModels(state: ModelState, index: CollectionIndex): GObject.Object[] {\n if (index.isTree) {\n return [ensureTree(state)];\n }\n\n return index.groups.map((level) => levelFor(state, level.key).store);\n}\n\nfunction hasSameModels(previous: GObject.Object[], next: GObject.Object[]): boolean {\n return previous.length === next.length && next.every((model, index) => previous[index] === model);\n}\n\nfunction syncRoot(state: ModelState, index: CollectionIndex): void {\n const next = desiredRootModels(state, index);\n\n if (hasSameModels(state.rootModels, next)) {\n return;\n }\n\n state.root.splice(0, state.rootModels.length, next);\n state.rootModels = next;\n}\n\nfunction syncModel(state: ModelState, index: CollectionIndex): void {\n const levels = [...index.groups, ...index.children.values()];\n const visited: Set<string> = new Set();\n let didChange = false;\n\n for (const level of levels) {\n didChange = didSpliceLevel(state, level) || didChange;\n visited.add(level.key);\n }\n\n if (didChange || visited.size !== state.levels.size) {\n dropUnvisited(state, visited);\n pruneObjects(state, index);\n }\n\n syncRoot(state, index);\n refreshExpandableLevels(state, index, levels);\n}\n\nfunction refreshExpandableLevels(state: ModelState, index: CollectionIndex, levels: Level[]): void {\n if (!index.isTree) {\n return;\n }\n\n for (const level of levels) {\n refreshExpandable(levelFor(state, level.key), level);\n }\n}\n\nfunction createCollectionModel(): CollectionModel {\n const root = newStore();\n\n const state: ModelState = {\n root,\n rootModels: [],\n model: Gtk.FlattenListModel.new(root),\n objects: new Map(),\n levels: new Map(),\n tree: null,\n };\n\n return {\n model: state.model,\n treeModel: () => state.tree,\n sync: (index) => {\n syncModel(state, index);\n },\n };\n}\n\nexport { createCollectionModel, getId, type CollectionModel };\n"]}
@@ -0,0 +1,18 @@
1
+ import type * as Gtk from "@gtkx/gi/gtk";
2
+ import type { Item } from "../types.js";
3
+ import type { CollectionIndex } from "./collection-index.js";
4
+ import type { CollectionModel } from "./collection-model.js";
5
+ type Collection = {
6
+ isTree: boolean;
7
+ model: Gtk.FlattenListModel;
8
+ treeModel: () => Gtk.TreeListModel | null;
9
+ itemFor: (id: string) => Item | undefined;
10
+ sectionFor: (id: string) => unknown;
11
+ idAt: (position: number) => string | null;
12
+ positionFor: (id: string) => number;
13
+ positionsFor: (ids: string[]) => number[];
14
+ };
15
+ declare function eachRow(tree: Gtk.TreeListModel, visit: (row: Gtk.TreeListRow, id: string | null) => void): void;
16
+ declare function createCollection(gtk: CollectionModel, index: CollectionIndex): Collection;
17
+ export { createCollection, eachRow, type Collection };
18
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,KAAK,UAAU,GAAG;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,GAAG,CAAC,gBAAgB,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;CAC7C,CAAC;AAkBF,iBAAS,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAIxG;AAoDD,iBAAS,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAWlF;AAED,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { getId } from "./collection-model.js";
2
+ const ascending = (a, b) => a - b;
3
+ function visitRow(tree, position, visit) {
4
+ const row = tree.getRow(position);
5
+ if (row === null) {
6
+ return;
7
+ }
8
+ visit(row, getId(row.getItem()));
9
+ }
10
+ function eachRow(tree, visit) {
11
+ for (let position = 0; position < tree.getNItems(); position++) {
12
+ visitRow(tree, position, visit);
13
+ }
14
+ }
15
+ function collectWanted(item, position, wanted, out) {
16
+ const id = getId(item);
17
+ if (id !== null && wanted.has(id)) {
18
+ out.push(position);
19
+ }
20
+ }
21
+ function scanPositions(tree, ids) {
22
+ const wanted = new Set(ids);
23
+ const positions = [];
24
+ const count = tree.getNItems();
25
+ for (let position = 0; position < count; position++) {
26
+ collectWanted(tree.getItem(position), position, wanted, positions);
27
+ }
28
+ return positions;
29
+ }
30
+ function indexPositions(index, ids) {
31
+ const positions = [];
32
+ for (const id of ids) {
33
+ const position = index.positionFor(id);
34
+ if (position >= 0) {
35
+ positions.push(position);
36
+ }
37
+ }
38
+ return positions.toSorted(ascending);
39
+ }
40
+ function positionsFor(gtk, index, ids) {
41
+ if (!index.isTree) {
42
+ return indexPositions(index, ids);
43
+ }
44
+ const tree = gtk.treeModel();
45
+ return tree === null ? [] : scanPositions(tree, ids);
46
+ }
47
+ function positionFor(gtk, index, id) {
48
+ const [first = -1] = positionsFor(gtk, index, [id]);
49
+ return first;
50
+ }
51
+ function createCollection(gtk, index) {
52
+ return {
53
+ isTree: index.isTree,
54
+ model: gtk.model,
55
+ treeModel: () => (index.isTree ? gtk.treeModel() : null),
56
+ itemFor: index.itemFor,
57
+ sectionFor: index.sectionFor,
58
+ idAt: (position) => getId(gtk.model.getItem(position)),
59
+ positionFor: (id) => positionFor(gtk, index, id),
60
+ positionsFor: (ids) => positionsFor(gtk, index, ids),
61
+ };
62
+ }
63
+ export { createCollection, eachRow };
64
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as GObject from \"@gtkx/gi/gobject\";\nimport type * as Gtk from \"@gtkx/gi/gtk\";\nimport type { Item } from \"../types.js\";\nimport type { CollectionIndex } from \"./collection-index.js\";\nimport type { CollectionModel } from \"./collection-model.js\";\nimport { getId } from \"./collection-model.js\";\n\ntype Collection = {\n isTree: boolean;\n model: Gtk.FlattenListModel;\n treeModel: () => Gtk.TreeListModel | null;\n itemFor: (id: string) => Item | undefined;\n sectionFor: (id: string) => unknown;\n idAt: (position: number) => string | null;\n positionFor: (id: string) => number;\n positionsFor: (ids: string[]) => number[];\n};\n\nconst ascending = (a: number, b: number): number => a - b;\n\nfunction visitRow(\n tree: Gtk.TreeListModel,\n position: number,\n visit: (row: Gtk.TreeListRow, id: string | null) => void,\n): void {\n const row = tree.getRow(position);\n\n if (row === null) {\n return;\n }\n\n visit(row, getId(row.getItem()));\n}\n\nfunction eachRow(tree: Gtk.TreeListModel, visit: (row: Gtk.TreeListRow, id: string | null) => void): void {\n for (let position = 0; position < tree.getNItems(); position++) {\n visitRow(tree, position, visit);\n }\n}\n\nfunction collectWanted(item: GObject.Object | null, position: number, wanted: Set<string>, out: number[]): void {\n const id = getId(item);\n\n if (id !== null && wanted.has(id)) {\n out.push(position);\n }\n}\n\nfunction scanPositions(tree: Gtk.TreeListModel, ids: string[]): number[] {\n const wanted = new Set(ids);\n const positions: number[] = [];\n const count = tree.getNItems();\n\n for (let position = 0; position < count; position++) {\n collectWanted(tree.getItem(position), position, wanted, positions);\n }\n\n return positions;\n}\n\nfunction indexPositions(index: CollectionIndex, ids: string[]): number[] {\n const positions: number[] = [];\n\n for (const id of ids) {\n const position = index.positionFor(id);\n\n if (position >= 0) {\n positions.push(position);\n }\n }\n\n return positions.toSorted(ascending);\n}\n\nfunction positionsFor(gtk: CollectionModel, index: CollectionIndex, ids: string[]): number[] {\n if (!index.isTree) {\n return indexPositions(index, ids);\n }\n\n const tree = gtk.treeModel();\n\n return tree === null ? [] : scanPositions(tree, ids);\n}\n\nfunction positionFor(gtk: CollectionModel, index: CollectionIndex, id: string): number {\n const [first = -1] = positionsFor(gtk, index, [id]);\n\n return first;\n}\n\nfunction createCollection(gtk: CollectionModel, index: CollectionIndex): Collection {\n return {\n isTree: index.isTree,\n model: gtk.model,\n treeModel: () => (index.isTree ? gtk.treeModel() : null),\n itemFor: index.itemFor,\n sectionFor: index.sectionFor,\n idAt: (position) => getId(gtk.model.getItem(position)),\n positionFor: (id) => positionFor(gtk, index, id),\n positionsFor: (ids) => positionsFor(gtk, index, ids),\n };\n}\n\nexport { createCollection, eachRow, type Collection };\n"]}
@@ -0,0 +1,9 @@
1
+ import type { Collection } from "./collection.js";
2
+ type ExpansionOptions = {
3
+ collection: Collection;
4
+ expandedIds?: string[] | null | undefined;
5
+ onExpandedChange?: ((ids: string[]) => void) | null | undefined;
6
+ };
7
+ declare function useExpansion(options: ExpansionOptions): void;
8
+ export { useExpansion, type ExpansionOptions };
9
+ //# sourceMappingURL=expansion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expansion.d.ts","sourceRoot":"","sources":["../../src/internal/expansion.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,KAAK,gBAAgB,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,gBAAgB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CACnE,CAAC;AA6FF,iBAAS,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAgBrD;AAED,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { useSignal } from "@gtkx/react";
2
+ import { useEffectEvent, useLayoutEffect, useRef, useState } from "react";
3
+ import { eachRow } from "./collection.js";
4
+ function newLastExpansion() {
5
+ return { key: "" };
6
+ }
7
+ function collectExpanded(row, id, ids) {
8
+ if (id !== null && row.getExpanded()) {
9
+ ids.push(id);
10
+ }
11
+ }
12
+ function expandRow(row, id, wanted) {
13
+ const isDesired = id !== null && wanted.has(id);
14
+ if (row.isExpandable() && row.getExpanded() !== isDesired) {
15
+ row.setExpanded(isDesired);
16
+ }
17
+ }
18
+ function applyExpansion(tree, expandedIds) {
19
+ const wanted = new Set(expandedIds);
20
+ eachRow(tree, (row, id) => {
21
+ expandRow(row, id, wanted);
22
+ });
23
+ }
24
+ function reportExpansion(collection, last, onExpandedChange) {
25
+ const tree = collection.treeModel();
26
+ if (tree === null) {
27
+ return;
28
+ }
29
+ const ids = [];
30
+ eachRow(tree, (row, id) => {
31
+ collectExpanded(row, id, ids);
32
+ });
33
+ const key = ids.join(" ");
34
+ if (last.key === key) {
35
+ return;
36
+ }
37
+ last.key = key;
38
+ onExpandedChange?.(ids);
39
+ }
40
+ function reportWhenIdle(collection, last, expanding, onExpandedChange) {
41
+ if (expanding.current) {
42
+ return;
43
+ }
44
+ reportExpansion(collection, last, onExpandedChange);
45
+ }
46
+ function runControlledExpansion(collection, expandedIds, expanding, report) {
47
+ const tree = collection.treeModel();
48
+ if (tree === null || expandedIds == null) {
49
+ return;
50
+ }
51
+ expanding.current = true;
52
+ try {
53
+ applyExpansion(tree, expandedIds);
54
+ }
55
+ finally {
56
+ expanding.current = false;
57
+ }
58
+ report();
59
+ }
60
+ function useExpansion(options) {
61
+ const { collection, expandedIds, onExpandedChange } = options;
62
+ const [last] = useState(newLastExpansion);
63
+ const expanding = useRef(false);
64
+ const report = useEffectEvent(() => {
65
+ reportWhenIdle(collection, last, expanding, onExpandedChange);
66
+ });
67
+ useSignal(collection.isTree ? collection.model : null, "items-changed", () => {
68
+ reportWhenIdle(collection, last, expanding, onExpandedChange);
69
+ });
70
+ useLayoutEffect(() => {
71
+ runControlledExpansion(collection, expandedIds, expanding, report);
72
+ }, [collection, expandedIds]);
73
+ }
74
+ export { useExpansion };
75
+ //# sourceMappingURL=expansion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/internal/expansion.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport type { RefObject } from \"react\";\nimport { useSignal } from \"@gtkx/react\";\nimport { useEffectEvent, useLayoutEffect, useRef, useState } from \"react\";\nimport type { Collection } from \"./collection.js\";\nimport { eachRow } from \"./collection.js\";\n\ntype ExpansionOptions = {\n collection: Collection;\n expandedIds?: string[] | null | undefined;\n onExpandedChange?: ((ids: string[]) => void) | null | undefined;\n};\n\ntype LastExpansion = { key: string };\n\nfunction newLastExpansion(): LastExpansion {\n return { key: \"\" };\n}\n\nfunction collectExpanded(row: Gtk.TreeListRow, id: string | null, ids: string[]): void {\n if (id !== null && row.getExpanded()) {\n ids.push(id);\n }\n}\n\nfunction expandRow(row: Gtk.TreeListRow, id: string | null, wanted: Set<string>): void {\n const isDesired = id !== null && wanted.has(id);\n\n if (row.isExpandable() && row.getExpanded() !== isDesired) {\n row.setExpanded(isDesired);\n }\n}\n\nfunction applyExpansion(tree: Gtk.TreeListModel, expandedIds: string[]): void {\n const wanted = new Set(expandedIds);\n\n eachRow(tree, (row, id) => {\n expandRow(row, id, wanted);\n });\n}\n\nfunction reportExpansion(\n collection: Collection,\n last: LastExpansion,\n onExpandedChange: ((ids: string[]) => void) | null | undefined,\n): void {\n const tree = collection.treeModel();\n\n if (tree === null) {\n return;\n }\n\n const ids: string[] = [];\n\n eachRow(tree, (row, id) => {\n collectExpanded(row, id, ids);\n });\n\n const key = ids.join(\" \");\n\n if (last.key === key) {\n return;\n }\n\n last.key = key;\n onExpandedChange?.(ids);\n}\n\nfunction reportWhenIdle(\n collection: Collection,\n last: LastExpansion,\n expanding: RefObject<boolean>,\n onExpandedChange: ((ids: string[]) => void) | null | undefined,\n): void {\n if (expanding.current) {\n return;\n }\n\n reportExpansion(collection, last, onExpandedChange);\n}\n\nfunction runControlledExpansion(\n collection: Collection,\n expandedIds: string[] | null | undefined,\n expanding: RefObject<boolean>,\n report: () => void,\n): void {\n const tree = collection.treeModel();\n\n if (tree === null || expandedIds == null) {\n return;\n }\n\n expanding.current = true;\n\n try {\n applyExpansion(tree, expandedIds);\n } finally {\n expanding.current = false;\n }\n\n report();\n}\n\nfunction useExpansion(options: ExpansionOptions): void {\n const { collection, expandedIds, onExpandedChange } = options;\n const [last] = useState<LastExpansion>(newLastExpansion);\n const expanding = useRef(false);\n\n const report = useEffectEvent((): void => {\n reportWhenIdle(collection, last, expanding, onExpandedChange);\n });\n\n useSignal(collection.isTree ? collection.model : null, \"items-changed\", () => {\n reportWhenIdle(collection, last, expanding, onExpandedChange);\n });\n\n useLayoutEffect(() => {\n runControlledExpansion(collection, expandedIds, expanding, report);\n }, [collection, expandedIds]);\n}\n\nexport { useExpansion, type ExpansionOptions };\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ReactElement } from "react";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
+ import type { Collection } from "./collection.js";
4
+ type SelectionOptions = {
5
+ collection: Collection;
6
+ selectedIds?: string[] | null | undefined;
7
+ onSelectionChanged?: ((ids: string[]) => void) | null | undefined;
8
+ selectionMode?: Gtk.SelectionMode | null | undefined;
9
+ };
10
+ declare function useSelection(options: SelectionOptions): ReactElement;
11
+ export { useSelection, type SelectionOptions };
12
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,KAAK,gBAAgB,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAClE,aAAa,CAAC,EAAE,GAAG,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC;CACxD,CAAC;AA2HF,iBAAS,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAqB7D;AAED,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
+ import { GtkMultiSelection, GtkNoSelection, GtkSingleSelection } from "@gtkx/jsx/gtk";
4
+ import { useEffectEvent, useLayoutEffect, useState } from "react";
5
+ function getSelectedIds(selection, collection) {
6
+ const bitset = selection.getSelection();
7
+ const size = Number(bitset.getSize());
8
+ const ids = [];
9
+ for (let index = 0; index < size; index++) {
10
+ pushId(collection.idAt(bitset.getNth(index)), ids);
11
+ }
12
+ return ids;
13
+ }
14
+ function pushId(id, ids) {
15
+ if (id !== null) {
16
+ ids.push(id);
17
+ }
18
+ }
19
+ function applySingleSelection(selection, positions) {
20
+ const [first] = positions;
21
+ if (first === undefined) {
22
+ selection.unselectAll();
23
+ return;
24
+ }
25
+ selection.selectItem(first, true);
26
+ }
27
+ function applyMultiSelection(selection, positions) {
28
+ const selected = Gtk.Bitset.newEmpty();
29
+ for (const position of positions) {
30
+ selected.add(position);
31
+ }
32
+ selection.setSelection(selected, Gtk.Bitset.newRange(0, selection.getNItems()));
33
+ }
34
+ function applySelection(selection, collection, ids) {
35
+ if (selection instanceof Gtk.NoSelection) {
36
+ return;
37
+ }
38
+ const positions = collection.positionsFor(ids);
39
+ if (ids.length > 0 && positions.length === 0) {
40
+ return;
41
+ }
42
+ if (selection instanceof Gtk.SingleSelection) {
43
+ applySingleSelection(selection, positions);
44
+ return;
45
+ }
46
+ applyMultiSelection(selection, positions);
47
+ }
48
+ function reportSelection(selection, collection, last, onSelectionChanged) {
49
+ if (selection === null) {
50
+ return;
51
+ }
52
+ const ids = getSelectedIds(selection, collection);
53
+ const key = ids.join(" ");
54
+ if (last.selection === selection && last.key === key) {
55
+ return;
56
+ }
57
+ last.selection = selection;
58
+ last.key = key;
59
+ onSelectionChanged?.(ids);
60
+ }
61
+ function selectionElement(mode, props) {
62
+ if (mode === Gtk.SelectionMode.MULTIPLE) {
63
+ return _jsx(GtkMultiSelection, { ...props });
64
+ }
65
+ if (mode === Gtk.SelectionMode.NONE) {
66
+ return _jsx(GtkNoSelection, { ...props });
67
+ }
68
+ return _jsx(GtkSingleSelection, { ...props, autoselect: false, canUnselect: true });
69
+ }
70
+ function syncSelection(selection, collection, selectedIds) {
71
+ if (selection === null || selectedIds == null) {
72
+ return;
73
+ }
74
+ applySelection(selection, collection, selectedIds);
75
+ }
76
+ function newLastSelection() {
77
+ return { selection: null, key: null };
78
+ }
79
+ function useSelection(options) {
80
+ const { collection, selectedIds, onSelectionChanged, selectionMode } = options;
81
+ const [selection, setSelection] = useState(null);
82
+ const [last] = useState(newLastSelection);
83
+ const report = useEffectEvent(() => {
84
+ reportSelection(selection, collection, last, onSelectionChanged);
85
+ });
86
+ useLayoutEffect(() => {
87
+ syncSelection(selection, collection, selectedIds);
88
+ report();
89
+ }, [selection, collection, selectedIds]);
90
+ return selectionElement(selectionMode, {
91
+ ref: setSelection,
92
+ model: collection.model,
93
+ onSelectionChanged: () => {
94
+ reportSelection(selection, collection, last, onSelectionChanged);
95
+ },
96
+ });
97
+ }
98
+ export { useSelection };
99
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"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","sourcesContent":["import type * as Gio from \"@gtkx/gi/gio\";\nimport type { ReactElement } from \"react\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { GtkMultiSelection, GtkNoSelection, GtkSingleSelection } from \"@gtkx/jsx/gtk\";\nimport { useEffectEvent, useLayoutEffect, useState } from \"react\";\nimport type { Collection } from \"./collection.js\";\n\ntype SelectionOptions = {\n collection: Collection;\n selectedIds?: string[] | null | undefined;\n onSelectionChanged?: ((ids: string[]) => void) | null | undefined;\n selectionMode?: Gtk.SelectionMode | null | undefined;\n};\n\ntype LastSelection = {\n selection: Gtk.SelectionModel | null;\n key: string | null;\n};\n\ntype SelectionElementProps = {\n ref: (value: Gtk.SelectionModel | null) => void;\n model: Gio.ListModel;\n onSelectionChanged: () => void;\n};\n\nfunction getSelectedIds(selection: Gtk.SelectionModel, collection: Collection): string[] {\n const bitset = selection.getSelection();\n const size = Number(bitset.getSize());\n const ids: string[] = [];\n\n for (let index = 0; index < size; index++) {\n pushId(collection.idAt(bitset.getNth(index)), ids);\n }\n\n return ids;\n}\n\nfunction pushId(id: string | null, ids: string[]): void {\n if (id !== null) {\n ids.push(id);\n }\n}\n\nfunction applySingleSelection(selection: Gtk.SingleSelection, positions: number[]): void {\n const [first] = positions;\n\n if (first === undefined) {\n selection.unselectAll();\n\n return;\n }\n\n selection.selectItem(first, true);\n}\n\nfunction applyMultiSelection(selection: Gtk.SelectionModel, positions: number[]): void {\n const selected = Gtk.Bitset.newEmpty();\n\n for (const position of positions) {\n selected.add(position);\n }\n\n selection.setSelection(selected, Gtk.Bitset.newRange(0, selection.getNItems()));\n}\n\nfunction applySelection(selection: Gtk.SelectionModel, collection: Collection, ids: string[]): void {\n if (selection instanceof Gtk.NoSelection) {\n return;\n }\n\n const positions = collection.positionsFor(ids);\n\n if (ids.length > 0 && positions.length === 0) {\n return;\n }\n\n if (selection instanceof Gtk.SingleSelection) {\n applySingleSelection(selection, positions);\n\n return;\n }\n\n applyMultiSelection(selection, positions);\n}\n\nfunction reportSelection(\n selection: Gtk.SelectionModel | null,\n collection: Collection,\n last: LastSelection,\n onSelectionChanged: ((ids: string[]) => void) | null | undefined,\n): void {\n if (selection === null) {\n return;\n }\n\n const ids = getSelectedIds(selection, collection);\n const key = ids.join(\" \");\n\n if (last.selection === selection && last.key === key) {\n return;\n }\n\n last.selection = selection;\n last.key = key;\n onSelectionChanged?.(ids);\n}\n\nfunction selectionElement(mode: Gtk.SelectionMode | null | undefined, props: SelectionElementProps): ReactElement {\n if (mode === Gtk.SelectionMode.MULTIPLE) {\n return <GtkMultiSelection {...props} />;\n }\n\n if (mode === Gtk.SelectionMode.NONE) {\n return <GtkNoSelection {...props} />;\n }\n\n return <GtkSingleSelection {...props} autoselect={false} canUnselect />;\n}\n\nfunction syncSelection(\n selection: Gtk.SelectionModel | null,\n collection: Collection,\n selectedIds: string[] | null | undefined,\n): void {\n if (selection === null || selectedIds == null) {\n return;\n }\n\n applySelection(selection, collection, selectedIds);\n}\n\nfunction newLastSelection(): LastSelection {\n return { selection: null, key: null };\n}\n\nfunction useSelection(options: SelectionOptions): ReactElement {\n const { collection, selectedIds, onSelectionChanged, selectionMode } = options;\n const [selection, setSelection] = useState<Gtk.SelectionModel | null>(null);\n const [last] = useState<LastSelection>(newLastSelection);\n\n const report = useEffectEvent((): void => {\n reportSelection(selection, collection, last, onSelectionChanged);\n });\n\n useLayoutEffect(() => {\n syncSelection(selection, collection, selectedIds);\n report();\n }, [selection, collection, selectedIds]);\n\n return selectionElement(selectionMode, {\n ref: setSelection,\n model: collection.model,\n onSelectionChanged: () => {\n reportSelection(selection, collection, last, onSelectionChanged);\n },\n });\n}\n\nexport { useSelection, type SelectionOptions };\n"]}
@@ -0,0 +1,18 @@
1
+ import type { ReactElement, RefObject } from "react";
2
+ import type { ExpansionProps, Item, Section, SelectionProps } from "../types.js";
3
+ import type { Collection } from "./collection.js";
4
+ type CollectionDataOptions = {
5
+ items?: Item[] | undefined;
6
+ sections?: Section[] | undefined;
7
+ flat?: boolean | undefined;
8
+ applying?: RefObject<boolean> | undefined;
9
+ };
10
+ type CollectionOptions = CollectionDataOptions & SelectionProps & ExpansionProps;
11
+ type CollectionResult = {
12
+ collection: Collection;
13
+ selection: ReactElement;
14
+ };
15
+ declare function useCollectionData(options: CollectionDataOptions): Collection;
16
+ declare function useCollection(options: CollectionOptions): CollectionResult;
17
+ export { useCollection, useCollectionData, type CollectionDataOptions, type CollectionOptions };
18
+ //# sourceMappingURL=use-collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-collection.d.ts","sourceRoot":"","sources":["../../src/internal/use-collection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAOlD,KAAK,qBAAqB,GAAG;IACzB,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,KAAK,iBAAiB,GAAG,qBAAqB,GAAG,cAAc,GAAG,cAAc,CAAC;AAEjF,KAAK,gBAAgB,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,YAAY,CAAC;CAC3B,CAAC;AAkBF,iBAAS,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAWrE;AAED,iBAAS,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAYnE;AAED,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { useLayoutEffect, useMemo, useState } from "react";
2
+ import { createCollectionIndex } from "./collection-index.js";
3
+ import { createCollectionModel } from "./collection-model.js";
4
+ import { createCollection } from "./collection.js";
5
+ import { useExpansion } from "./expansion.js";
6
+ import { useSelection } from "./selection.js";
7
+ function runSync(gtk, index, applying) {
8
+ if (applying === undefined) {
9
+ gtk.sync(index);
10
+ return;
11
+ }
12
+ applying.current = true;
13
+ try {
14
+ gtk.sync(index);
15
+ }
16
+ finally {
17
+ applying.current = false;
18
+ }
19
+ }
20
+ function useCollectionData(options) {
21
+ const { items, sections, flat, applying } = options;
22
+ const [gtk] = useState(createCollectionModel);
23
+ const index = useMemo(() => createCollectionIndex(items, sections, flat === true), [items, sections, flat]);
24
+ const collection = useMemo(() => createCollection(gtk, index), [gtk, index]);
25
+ useLayoutEffect(() => {
26
+ runSync(gtk, index, applying);
27
+ }, [gtk, index, applying]);
28
+ return collection;
29
+ }
30
+ function useCollection(options) {
31
+ const collection = useCollectionData(options);
32
+ useExpansion({ collection, expandedIds: options.expandedIds, onExpandedChange: options.onExpandedChange });
33
+ const selection = useSelection({
34
+ collection,
35
+ selectedIds: options.selectedIds,
36
+ onSelectionChanged: options.onSelectionChanged,
37
+ selectionMode: options.selectionMode,
38
+ });
39
+ return { collection, selection };
40
+ }
41
+ export { useCollection, useCollectionData };
42
+ //# sourceMappingURL=use-collection.js.map