@gtkx/components 1.0.0-rc.1 → 1.0.0-rc.3

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 (220) hide show
  1. package/dist/adw/combo-row.d.ts +11 -0
  2. package/dist/adw/combo-row.d.ts.map +1 -0
  3. package/dist/adw/combo-row.js +14 -0
  4. package/dist/adw/combo-row.js.map +1 -0
  5. package/dist/adw/index.d.ts +4 -0
  6. package/dist/adw/index.d.ts.map +1 -0
  7. package/dist/adw/index.js +3 -0
  8. package/dist/adw/index.js.map +1 -0
  9. package/dist/adw/toast.d.ts +10 -0
  10. package/dist/adw/toast.d.ts.map +1 -0
  11. package/dist/adw/toast.js +51 -0
  12. package/dist/adw/toast.js.map +1 -0
  13. package/dist/adw/types.d.ts +35 -0
  14. package/dist/adw/types.d.ts.map +1 -0
  15. package/dist/adw/types.js +2 -0
  16. package/dist/adw/types.js.map +1 -0
  17. package/dist/column-view.d.ts +7 -29
  18. package/dist/column-view.d.ts.map +1 -1
  19. package/dist/column-view.js +98 -70
  20. package/dist/column-view.js.map +1 -1
  21. package/dist/drop-down.d.ts +7 -28
  22. package/dist/drop-down.d.ts.map +1 -1
  23. package/dist/drop-down.js +10 -86
  24. package/dist/drop-down.js.map +1 -1
  25. package/dist/grid-view.d.ts +6 -16
  26. package/dist/grid-view.d.ts.map +1 -1
  27. package/dist/grid-view.js +25 -33
  28. package/dist/grid-view.js.map +1 -1
  29. package/dist/index.d.ts +6 -12
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -6
  32. package/dist/index.js.map +1 -1
  33. package/dist/internal/cells.d.ts +50 -0
  34. package/dist/internal/cells.d.ts.map +1 -0
  35. package/dist/internal/cells.js +185 -0
  36. package/dist/internal/cells.js.map +1 -0
  37. package/dist/internal/collection-index.d.ts +21 -0
  38. package/dist/internal/collection-index.d.ts.map +1 -0
  39. package/dist/internal/collection-index.js +106 -0
  40. package/dist/internal/collection-index.js.map +1 -0
  41. package/dist/internal/collection-model.d.ts +12 -0
  42. package/dist/internal/collection-model.d.ts.map +1 -0
  43. package/dist/internal/collection-model.js +164 -0
  44. package/dist/internal/collection-model.js.map +1 -0
  45. package/dist/internal/collection.d.ts +18 -0
  46. package/dist/internal/collection.d.ts.map +1 -0
  47. package/dist/internal/collection.js +64 -0
  48. package/dist/internal/collection.js.map +1 -0
  49. package/dist/internal/drop-down.d.ts +16 -0
  50. package/dist/internal/drop-down.d.ts.map +1 -0
  51. package/dist/internal/drop-down.js +110 -0
  52. package/dist/internal/drop-down.js.map +1 -0
  53. package/dist/internal/expansion.d.ts +9 -0
  54. package/dist/internal/expansion.d.ts.map +1 -0
  55. package/dist/internal/expansion.js +74 -0
  56. package/dist/internal/expansion.js.map +1 -0
  57. package/dist/internal/selection.d.ts +13 -0
  58. package/dist/internal/selection.d.ts.map +1 -0
  59. package/dist/internal/selection.js +100 -0
  60. package/dist/internal/selection.js.map +1 -0
  61. package/dist/internal/use-collection.d.ts +17 -0
  62. package/dist/internal/use-collection.d.ts.map +1 -0
  63. package/dist/internal/use-collection.js +34 -0
  64. package/dist/internal/use-collection.js.map +1 -0
  65. package/dist/internal/use-widget-ref.d.ts +4 -0
  66. package/dist/internal/use-widget-ref.d.ts.map +1 -0
  67. package/dist/internal/use-widget-ref.js +8 -0
  68. package/dist/internal/use-widget-ref.js.map +1 -0
  69. package/dist/list-view.d.ts +5 -20
  70. package/dist/list-view.d.ts.map +1 -1
  71. package/dist/list-view.js +32 -53
  72. package/dist/list-view.js.map +1 -1
  73. package/dist/size-group.d.ts +8 -16
  74. package/dist/size-group.d.ts.map +1 -1
  75. package/dist/size-group.js +35 -51
  76. package/dist/size-group.js.map +1 -1
  77. package/dist/types.d.ts +98 -27
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/types.js.map +1 -1
  80. package/package.json +17 -6
  81. package/src/adw/combo-row.tsx +20 -0
  82. package/src/adw/index.ts +9 -0
  83. package/src/adw/toast.tsx +74 -0
  84. package/src/adw/types.ts +45 -0
  85. package/src/column-view.tsx +176 -160
  86. package/src/drop-down.tsx +12 -185
  87. package/src/grid-view.tsx +29 -73
  88. package/src/index.ts +18 -26
  89. package/src/internal/cells.tsx +345 -0
  90. package/src/internal/collection-index.ts +168 -0
  91. package/src/internal/collection-model.ts +246 -0
  92. package/src/internal/collection.ts +104 -0
  93. package/src/internal/drop-down.tsx +186 -0
  94. package/src/internal/expansion.ts +122 -0
  95. package/src/internal/selection.tsx +162 -0
  96. package/src/internal/use-collection.tsx +57 -0
  97. package/src/internal/use-widget-ref.ts +11 -0
  98. package/src/list-view.tsx +43 -139
  99. package/src/size-group.tsx +63 -82
  100. package/src/types.ts +158 -28
  101. package/dist/cell.d.ts +0 -30
  102. package/dist/cell.d.ts.map +0 -1
  103. package/dist/cell.js +0 -45
  104. package/dist/cell.js.map +0 -1
  105. package/dist/column-view-column.d.ts +0 -27
  106. package/dist/column-view-column.d.ts.map +0 -1
  107. package/dist/column-view-column.js +0 -30
  108. package/dist/column-view-column.js.map +0 -1
  109. package/dist/constraint-layout-apply.d.ts +0 -55
  110. package/dist/constraint-layout-apply.d.ts.map +0 -1
  111. package/dist/constraint-layout-apply.js +0 -107
  112. package/dist/constraint-layout-apply.js.map +0 -1
  113. package/dist/constraint-layout.d.ts +0 -20
  114. package/dist/constraint-layout.d.ts.map +0 -1
  115. package/dist/constraint-layout.js +0 -88
  116. package/dist/constraint-layout.js.map +0 -1
  117. package/dist/fixed.d.ts +0 -26
  118. package/dist/fixed.d.ts.map +0 -1
  119. package/dist/fixed.js +0 -40
  120. package/dist/fixed.js.map +0 -1
  121. package/dist/grid.d.ts +0 -27
  122. package/dist/grid.d.ts.map +0 -1
  123. package/dist/grid.js +0 -48
  124. package/dist/grid.js.map +0 -1
  125. package/dist/hooks/use-cell-containers.d.ts +0 -18
  126. package/dist/hooks/use-cell-containers.d.ts.map +0 -1
  127. package/dist/hooks/use-cell-containers.js +0 -67
  128. package/dist/hooks/use-cell-containers.js.map +0 -1
  129. package/dist/hooks/use-collection-model.d.ts +0 -26
  130. package/dist/hooks/use-collection-model.d.ts.map +0 -1
  131. package/dist/hooks/use-collection-model.js +0 -35
  132. package/dist/hooks/use-collection-model.js.map +0 -1
  133. package/dist/hooks/use-collection-widget.d.ts +0 -28
  134. package/dist/hooks/use-collection-widget.d.ts.map +0 -1
  135. package/dist/hooks/use-collection-widget.js +0 -21
  136. package/dist/hooks/use-collection-widget.js.map +0 -1
  137. package/dist/hooks/use-drop-down-selection.d.ts +0 -16
  138. package/dist/hooks/use-drop-down-selection.d.ts.map +0 -1
  139. package/dist/hooks/use-drop-down-selection.js +0 -35
  140. package/dist/hooks/use-drop-down-selection.js.map +0 -1
  141. package/dist/hooks/use-expansion-model.d.ts +0 -12
  142. package/dist/hooks/use-expansion-model.d.ts.map +0 -1
  143. package/dist/hooks/use-expansion-model.js +0 -71
  144. package/dist/hooks/use-expansion-model.js.map +0 -1
  145. package/dist/hooks/use-header-menu.d.ts +0 -4
  146. package/dist/hooks/use-header-menu.d.ts.map +0 -1
  147. package/dist/hooks/use-header-menu.js +0 -20
  148. package/dist/hooks/use-header-menu.js.map +0 -1
  149. package/dist/hooks/use-installed-model.d.ts +0 -4
  150. package/dist/hooks/use-installed-model.d.ts.map +0 -1
  151. package/dist/hooks/use-installed-model.js +0 -15
  152. package/dist/hooks/use-installed-model.js.map +0 -1
  153. package/dist/hooks/use-list-model.d.ts +0 -19
  154. package/dist/hooks/use-list-model.d.ts.map +0 -1
  155. package/dist/hooks/use-list-model.js +0 -97
  156. package/dist/hooks/use-list-model.js.map +0 -1
  157. package/dist/hooks/use-placed-child.d.ts +0 -18
  158. package/dist/hooks/use-placed-child.d.ts.map +0 -1
  159. package/dist/hooks/use-placed-child.js +0 -47
  160. package/dist/hooks/use-placed-child.js.map +0 -1
  161. package/dist/hooks/use-selection-model.d.ts +0 -13
  162. package/dist/hooks/use-selection-model.d.ts.map +0 -1
  163. package/dist/hooks/use-selection-model.js +0 -103
  164. package/dist/hooks/use-selection-model.js.map +0 -1
  165. package/dist/hooks/use-sort-handler.d.ts +0 -17
  166. package/dist/hooks/use-sort-handler.d.ts.map +0 -1
  167. package/dist/hooks/use-sort-handler.js +0 -87
  168. package/dist/hooks/use-sort-handler.js.map +0 -1
  169. package/dist/menu-items.d.ts +0 -5
  170. package/dist/menu-items.d.ts.map +0 -1
  171. package/dist/menu-items.js +0 -48
  172. package/dist/menu-items.js.map +0 -1
  173. package/dist/menu.d.ts +0 -14
  174. package/dist/menu.d.ts.map +0 -1
  175. package/dist/menu.js +0 -24
  176. package/dist/menu.js.map +0 -1
  177. package/dist/overlay.d.ts +0 -24
  178. package/dist/overlay.d.ts.map +0 -1
  179. package/dist/overlay.js +0 -49
  180. package/dist/overlay.js.map +0 -1
  181. package/dist/utils/cell-container-store.d.ts +0 -28
  182. package/dist/utils/cell-container-store.d.ts.map +0 -1
  183. package/dist/utils/cell-container-store.js +0 -98
  184. package/dist/utils/cell-container-store.js.map +0 -1
  185. package/dist/utils/item-models.d.ts +0 -10
  186. package/dist/utils/item-models.d.ts.map +0 -1
  187. package/dist/utils/item-models.js +0 -82
  188. package/dist/utils/item-models.js.map +0 -1
  189. package/dist/utils/item-resolver.d.ts +0 -25
  190. package/dist/utils/item-resolver.d.ts.map +0 -1
  191. package/dist/utils/item-resolver.js +0 -102
  192. package/dist/utils/item-resolver.js.map +0 -1
  193. package/dist/utils/list-item-flatten.d.ts +0 -24
  194. package/dist/utils/list-item-flatten.d.ts.map +0 -1
  195. package/dist/utils/list-item-flatten.js +0 -68
  196. package/dist/utils/list-item-flatten.js.map +0 -1
  197. package/src/cell.tsx +0 -144
  198. package/src/column-view-column.tsx +0 -82
  199. package/src/constraint-layout-apply.ts +0 -173
  200. package/src/constraint-layout.tsx +0 -135
  201. package/src/fixed.tsx +0 -86
  202. package/src/grid.tsx +0 -88
  203. package/src/hooks/use-cell-containers.ts +0 -93
  204. package/src/hooks/use-collection-model.ts +0 -60
  205. package/src/hooks/use-collection-widget.ts +0 -47
  206. package/src/hooks/use-drop-down-selection.ts +0 -52
  207. package/src/hooks/use-expansion-model.ts +0 -82
  208. package/src/hooks/use-header-menu.ts +0 -34
  209. package/src/hooks/use-installed-model.ts +0 -20
  210. package/src/hooks/use-list-model.ts +0 -161
  211. package/src/hooks/use-placed-child.ts +0 -79
  212. package/src/hooks/use-selection-model.ts +0 -114
  213. package/src/hooks/use-sort-handler.ts +0 -107
  214. package/src/menu-items.ts +0 -48
  215. package/src/menu.tsx +0 -32
  216. package/src/overlay.tsx +0 -88
  217. package/src/utils/cell-container-store.ts +0 -114
  218. package/src/utils/item-models.ts +0 -113
  219. package/src/utils/item-resolver.ts +0 -138
  220. package/src/utils/list-item-flatten.ts +0 -90
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-index.d.ts","sourceRoot":"","sources":["../../src/internal/collection-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjD,KAAK,KAAK,GAAG;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,YAAY,EAAE,OAAO,EAAE,CAAC;CAC3B,CAAC;AAEF,KAAK,eAAe,GAAG;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7B,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AAYF,QAAA,MAAM,cAAc,KAAK,CAAC;AAG1B,QAAA,MAAM,aAAa,GAAI,IAAI,MAAM,KAAG,MAAuB,CAAC;AAwG5D,iBAAS,qBAAqB,CAC1B,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,EACzB,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,EAC/B,MAAM,EAAE,OAAO,GAChB,eAAe,CAyBjB;AAED,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,CAAC"}
@@ -0,0 +1,106 @@
1
+ const ROOT_LEVEL_KEY = "";
2
+ const hasChildren = (item) => item.children !== undefined && item.children.length > 0;
3
+ const childLevelKey = (id) => `child:${id}`;
4
+ function collectChildren(state, level, item) {
5
+ if (!state.isTree) {
6
+ return;
7
+ }
8
+ const canExpand = hasChildren(item);
9
+ level.isExpandable.push(canExpand);
10
+ if (!canExpand) {
11
+ return;
12
+ }
13
+ const key = childLevelKey(item.id);
14
+ state.children.set(key, collectLevel(state, key, item.children ?? []));
15
+ }
16
+ function collectLevel(state, key, items) {
17
+ const level = { key, ids: [], isExpandable: [] };
18
+ for (const item of items) {
19
+ level.ids.push(item.id);
20
+ state.itemsById.set(item.id, item);
21
+ collectChildren(state, level, item);
22
+ }
23
+ return level;
24
+ }
25
+ function buildPositions(state) {
26
+ const positions = new Map();
27
+ const starts = [];
28
+ let offset = 0;
29
+ for (const level of state.groups) {
30
+ starts.push(offset);
31
+ for (const id of level.ids) {
32
+ positions.set(id, offset);
33
+ offset += 1;
34
+ }
35
+ }
36
+ state.positions = positions;
37
+ state.starts = starts;
38
+ }
39
+ function positionFor(state, id) {
40
+ if (state.isTree) {
41
+ return -1;
42
+ }
43
+ if (state.positions === null) {
44
+ buildPositions(state);
45
+ }
46
+ return state.positions?.get(id) ?? -1;
47
+ }
48
+ function sectionAt(starts, position) {
49
+ let low = 0;
50
+ let high = starts.length - 1;
51
+ while (low < high) {
52
+ const middle = Math.ceil((low + high) / 2);
53
+ if ((starts[middle] ?? 0) <= position) {
54
+ low = middle;
55
+ }
56
+ else {
57
+ high = middle - 1;
58
+ }
59
+ }
60
+ return low;
61
+ }
62
+ function sectionFor(state, id) {
63
+ const sections = state.sections;
64
+ const position = positionFor(state, id);
65
+ if (sections === undefined || position < 0 || state.starts === null) {
66
+ return undefined;
67
+ }
68
+ return sections[sectionAt(state.starts, position)]?.value;
69
+ }
70
+ function buildGroups(state, source, sections) {
71
+ if (sections === undefined) {
72
+ return [collectLevel(state, ROOT_LEVEL_KEY, source)];
73
+ }
74
+ return sections.map((section) => collectLevel(state, section.id, section.data));
75
+ }
76
+ function isTreeSource(source, sections, isFlat) {
77
+ if (sections !== undefined || isFlat) {
78
+ return false;
79
+ }
80
+ return source.some((item) => hasChildren(item));
81
+ }
82
+ function createCollectionIndex(items, sections, isFlat) {
83
+ const source = items ?? [];
84
+ const state = {
85
+ isTree: isTreeSource(source, sections, isFlat),
86
+ groups: [],
87
+ children: new Map(),
88
+ itemsById: new Map(),
89
+ sections,
90
+ positions: null,
91
+ starts: null,
92
+ };
93
+ state.groups = buildGroups(state, source, sections);
94
+ return {
95
+ isTree: state.isTree,
96
+ size: state.itemsById.size,
97
+ groups: state.groups,
98
+ children: state.children,
99
+ has: (id) => state.itemsById.has(id),
100
+ itemFor: (id) => state.itemsById.get(id),
101
+ sectionFor: (id) => sectionFor(state, id),
102
+ positionFor: (id) => positionFor(state, id),
103
+ };
104
+ }
105
+ export { createCollectionIndex, childLevelKey, ROOT_LEVEL_KEY };
106
+ //# sourceMappingURL=collection-index.js.map
@@ -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,16 @@
1
+ import type * as GObject from "@gtkx/gi/gobject";
2
+ import type { ElementType, ReactNode, Ref } from "react";
3
+ import type { DropDownOwnProps } from "../types.js";
4
+ type SelectableWidget = GObject.Object & {
5
+ getSelected: () => number;
6
+ selected: number;
7
+ };
8
+ type DropDownBaseProps = DropDownOwnProps<unknown, unknown> & {
9
+ onNotifySelected?: ((value: number | null, self: SelectableWidget) => void) | null | undefined;
10
+ ref?: Ref<SelectableWidget | null> | undefined;
11
+ } & Record<string, unknown>;
12
+ declare function DropDownBase(props: DropDownBaseProps & {
13
+ component: ElementType;
14
+ }): ReactNode;
15
+ export { DropDownBase, type DropDownBaseProps };
16
+ //# sourceMappingURL=drop-down.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drop-down.d.ts","sourceRoot":"","sources":["../../src/internal/drop-down.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,OAAO,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAKzD,OAAO,KAAK,EAAE,gBAAgB,EAAgC,MAAM,aAAa,CAAC;AAMlF,KAAK,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF,KAAK,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/F,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;CAClD,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAuI5B,iBAAS,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAAE,SAAS,EAAE,WAAW,CAAA;CAAE,GAAG,SAAS,CA+BtF;AAED,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { GtkLabel, GtkSignalListItemFactory } from "@gtkx/jsx/gtk";
3
+ import { applyWrite } from "@gtkx/react/internal";
4
+ import { omit } from "@gtkx/utils";
5
+ import { useEffectEvent, useLayoutEffect, useRef } from "react";
6
+ import { HeaderPortals, ItemPortals, useHeaderCells, useItemCells } from "./cells.js";
7
+ import { useCollectionData } from "./use-collection.js";
8
+ import { useWidgetRef } from "./use-widget-ref.js";
9
+ const DROP_DOWN_PROPS = [
10
+ "component",
11
+ "items",
12
+ "sections",
13
+ "selectedId",
14
+ "onSelectionChanged",
15
+ "onNotifySelected",
16
+ "renderItem",
17
+ "renderListItem",
18
+ "renderHeader",
19
+ "ref",
20
+ ];
21
+ const describeValue = (value) => {
22
+ if (typeof value === "string") {
23
+ return value;
24
+ }
25
+ try {
26
+ return JSON.stringify(value);
27
+ }
28
+ catch {
29
+ return String(value);
30
+ }
31
+ };
32
+ const defaultItemContent = (value) => value == null ? null : _jsx(GtkLabel, { children: describeValue(value) });
33
+ const defaultRenderItem = ({ item }) => defaultItemContent(item);
34
+ const faceRenderer = (props) => props.renderItem ?? defaultRenderItem;
35
+ const listRenderer = (props) => props.renderListItem ?? props.renderItem ?? defaultRenderItem;
36
+ const resolvePosition = (widget, collection, selectedId) => {
37
+ const requested = selectedId == null ? -1 : collection.positionFor(selectedId);
38
+ return requested >= 0 ? requested : widget.getSelected();
39
+ };
40
+ const updateKnownSelection = (tracker, effectiveId, selectedId) => {
41
+ const { known, onSelectionChanged } = tracker;
42
+ const expectedId = selectedId ?? known.current;
43
+ const isNew = effectiveId !== known.current;
44
+ known.current = effectiveId;
45
+ if (expectedId !== null && effectiveId !== expectedId && isNew) {
46
+ onSelectionChanged?.(effectiveId);
47
+ }
48
+ };
49
+ const reportKnownSelection = (tracker, id) => {
50
+ const { known, onSelectionChanged } = tracker;
51
+ if (id === null || id === known.current) {
52
+ return;
53
+ }
54
+ known.current = id;
55
+ onSelectionChanged?.(id);
56
+ };
57
+ const reportSelectedNotify = (options, tracker) => {
58
+ const { widget, collection } = options;
59
+ if (widget === null) {
60
+ return;
61
+ }
62
+ reportKnownSelection(tracker, collection.idAt(widget.getSelected()));
63
+ };
64
+ const applySelectedPosition = (widget, position) => {
65
+ applyWrite(() => {
66
+ widget.selected = position;
67
+ });
68
+ };
69
+ const useDropDownSelection = (options) => {
70
+ const { widget, collection } = options;
71
+ const { selectedId } = options.props;
72
+ const known = useRef(null);
73
+ const syncKnownSelection = useEffectEvent((position) => {
74
+ const effectiveId = collection.idAt(position);
75
+ if (effectiveId === null) {
76
+ known.current = null;
77
+ return;
78
+ }
79
+ const { onSelectionChanged } = options.props;
80
+ updateKnownSelection({ known, onSelectionChanged }, effectiveId, selectedId);
81
+ });
82
+ useLayoutEffect(() => {
83
+ if (widget === null) {
84
+ return;
85
+ }
86
+ const position = resolvePosition(widget, collection, selectedId);
87
+ applySelectedPosition(widget, position);
88
+ syncKnownSelection(position);
89
+ }, [widget, collection, selectedId]);
90
+ return (value, self) => {
91
+ reportSelectedNotify(options, { known, onSelectionChanged: options.props.onSelectionChanged });
92
+ options.props.onNotifySelected?.(value, self);
93
+ };
94
+ };
95
+ function DropDownBase(props) {
96
+ const { component: Component, items, sections, renderListItem, renderHeader, ref } = props;
97
+ const rest = omit(props, DROP_DOWN_PROPS);
98
+ const [widget, refCallback] = useWidgetRef(ref);
99
+ const collection = useCollectionData({ items, sections });
100
+ const faceCells = useItemCells({ width: -1, height: -1 });
101
+ const listCells = useItemCells({ width: -1, height: -1 });
102
+ const headerCells = useHeaderCells({ width: -1, height: -1 });
103
+ const handleNotifySelected = useDropDownSelection({ widget, collection, props });
104
+ const hasHeader = typeof renderHeader === "function";
105
+ return (_jsxs(_Fragment, { children: [_jsx(Component, { ref: refCallback, model: collection.model, factory: _jsx(GtkSignalListItemFactory, { ...faceCells.handlers }), ...(renderListItem != null && {
106
+ listFactory: _jsx(GtkSignalListItemFactory, { ...listCells.handlers }),
107
+ }), ...(hasHeader && { headerFactory: _jsx(GtkSignalListItemFactory, { ...headerCells.handlers }) }), ...rest, onNotifySelected: handleNotifySelected }), _jsx(ItemPortals, { store: faceCells, render: faceRenderer(props), collection: collection }), renderListItem != null && (_jsx(ItemPortals, { store: listCells, render: listRenderer(props), collection: collection })), hasHeader && _jsx(HeaderPortals, { store: headerCells, render: renderHeader, collection: collection })] }));
108
+ }
109
+ export { DropDownBase };
110
+ //# sourceMappingURL=drop-down.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drop-down.js","sourceRoot":"","sources":["../../src/internal/drop-down.tsx"],"names":[],"mappings":"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","sourcesContent":["import type * as GObject from \"@gtkx/gi/gobject\";\nimport type { ElementType, ReactNode, Ref } from \"react\";\nimport { GtkLabel, GtkSignalListItemFactory } from \"@gtkx/jsx/gtk\";\nimport { applyWrite } from \"@gtkx/react/internal\";\nimport { omit } from \"@gtkx/utils\";\nimport { useEffectEvent, useLayoutEffect, useRef } from \"react\";\nimport type { DropDownOwnProps, ItemRenderer, RenderItemArgs } from \"../types.js\";\nimport type { Collection } from \"./collection.js\";\nimport { HeaderPortals, ItemPortals, useHeaderCells, useItemCells } from \"./cells.js\";\nimport { useCollectionData } from \"./use-collection.js\";\nimport { useWidgetRef } from \"./use-widget-ref.js\";\n\ntype SelectableWidget = GObject.Object & { getSelected: () => number; selected: number };\n\ntype DropDownBaseProps = DropDownOwnProps<unknown, unknown> & {\n onNotifySelected?: ((value: number | null, self: SelectableWidget) => void) | null | undefined;\n ref?: Ref<SelectableWidget | null> | undefined;\n} & Record<string, unknown>;\n\ntype SelectionOptions = {\n widget: SelectableWidget | null;\n collection: Collection;\n props: DropDownBaseProps;\n};\n\ntype NotifySelectedHandler = NonNullable<DropDownBaseProps[\"onNotifySelected\"]>;\n\ntype KnownSelection = {\n known: { current: string | null };\n onSelectionChanged: ((id: string) => void) | null | undefined;\n};\n\nconst DROP_DOWN_PROPS: string[] = [\n \"component\",\n \"items\",\n \"sections\",\n \"selectedId\",\n \"onSelectionChanged\",\n \"onNotifySelected\",\n \"renderItem\",\n \"renderListItem\",\n \"renderHeader\",\n \"ref\",\n];\n\nconst describeValue = (value: unknown): string => {\n if (typeof value === \"string\") {\n return value;\n }\n\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n};\n\nconst defaultItemContent = (value: unknown): ReactNode =>\n value == null ? null : <GtkLabel>{describeValue(value)}</GtkLabel>;\n\nconst defaultRenderItem = ({ item }: RenderItemArgs<unknown>): ReactNode => defaultItemContent(item);\nconst faceRenderer = (props: DropDownBaseProps): ItemRenderer<never> => props.renderItem ?? defaultRenderItem;\n\nconst listRenderer = (props: DropDownBaseProps): ItemRenderer<never> =>\n props.renderListItem ?? props.renderItem ?? defaultRenderItem;\n\nconst resolvePosition = (\n widget: SelectableWidget,\n collection: Collection,\n selectedId: string | null | undefined,\n): number => {\n const requested = selectedId == null ? -1 : collection.positionFor(selectedId);\n\n return requested >= 0 ? requested : widget.getSelected();\n};\n\nconst updateKnownSelection = (\n tracker: KnownSelection,\n effectiveId: string,\n selectedId: string | null | undefined,\n): void => {\n const { known, onSelectionChanged } = tracker;\n const expectedId = selectedId ?? known.current;\n const isNew = effectiveId !== known.current;\n known.current = effectiveId;\n\n if (expectedId !== null && effectiveId !== expectedId && isNew) {\n onSelectionChanged?.(effectiveId);\n }\n};\n\nconst reportKnownSelection = (tracker: KnownSelection, id: string | null): void => {\n const { known, onSelectionChanged } = tracker;\n\n if (id === null || id === known.current) {\n return;\n }\n\n known.current = id;\n onSelectionChanged?.(id);\n};\n\nconst reportSelectedNotify = (options: SelectionOptions, tracker: KnownSelection): void => {\n const { widget, collection } = options;\n\n if (widget === null) {\n return;\n }\n\n reportKnownSelection(tracker, collection.idAt(widget.getSelected()));\n};\n\nconst applySelectedPosition = (widget: SelectableWidget, position: number): void => {\n applyWrite(() => {\n widget.selected = position;\n });\n};\n\nconst useDropDownSelection = (options: SelectionOptions): NotifySelectedHandler => {\n const { widget, collection } = options;\n const { selectedId } = options.props;\n const known = useRef<string | null>(null);\n\n const syncKnownSelection = useEffectEvent((position: number): void => {\n const effectiveId = collection.idAt(position);\n\n if (effectiveId === null) {\n known.current = null;\n\n return;\n }\n\n const { onSelectionChanged } = options.props;\n updateKnownSelection({ known, onSelectionChanged }, effectiveId, selectedId);\n });\n\n useLayoutEffect(() => {\n if (widget === null) {\n return;\n }\n\n const position = resolvePosition(widget, collection, selectedId);\n applySelectedPosition(widget, position);\n syncKnownSelection(position);\n }, [widget, collection, selectedId]);\n\n return (value, self) => {\n reportSelectedNotify(options, { known, onSelectionChanged: options.props.onSelectionChanged });\n options.props.onNotifySelected?.(value, self);\n };\n};\n\nfunction DropDownBase(props: DropDownBaseProps & { component: ElementType }): ReactNode {\n const { component: Component, items, sections, renderListItem, renderHeader, ref } = props;\n const rest = omit(props, DROP_DOWN_PROPS);\n const [widget, refCallback] = useWidgetRef<SelectableWidget>(ref);\n const collection = useCollectionData({ items, sections });\n const faceCells = useItemCells({ width: -1, height: -1 });\n const listCells = useItemCells({ width: -1, height: -1 });\n const headerCells = useHeaderCells({ width: -1, height: -1 });\n const handleNotifySelected = useDropDownSelection({ widget, collection, props });\n const hasHeader = typeof renderHeader === \"function\";\n\n return (\n <>\n <Component\n ref={refCallback}\n model={collection.model}\n factory={<GtkSignalListItemFactory {...faceCells.handlers} />}\n {...(renderListItem != null && {\n listFactory: <GtkSignalListItemFactory {...listCells.handlers} />,\n })}\n {...(hasHeader && { headerFactory: <GtkSignalListItemFactory {...headerCells.handlers} /> })}\n {...rest}\n onNotifySelected={handleNotifySelected}\n />\n <ItemPortals store={faceCells} render={faceRenderer(props)} collection={collection} />\n {renderListItem != null && (\n <ItemPortals store={listCells} render={listRenderer(props)} collection={collection} />\n )}\n {hasHeader && <HeaderPortals store={headerCells} render={renderHeader} collection={collection} />}\n </>\n );\n}\n\nexport { DropDownBase, type DropDownBaseProps };\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":"AAGA,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,MAAM,IAAI,CAgB3D;AAED,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,CAAC"}