@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
@@ -1,143 +0,0 @@
1
- import type * as Gio from "@gtkx/gi/gio";
2
- import type * as GObject from "@gtkx/gi/gobject";
3
- import type * as Gtk from "@gtkx/gi/gtk";
4
- import type { ListItem } from "../types.js";
5
- import { flattenListItems, type TreeItemMetadata } from "./list-item-flatten.js";
6
-
7
- const NO_TREE_METADATA: TreeItemMetadata = { hideExpander: false, indentForDepth: true, indentForIcon: true };
8
-
9
- const treeItemCache = new WeakMap<Gtk.TreeListRow, GObject.Object>();
10
-
11
- const treeRowItem = (treeRow: Gtk.TreeListRow): GObject.Object | null => {
12
- const cached = treeItemCache.get(treeRow);
13
- if (cached !== undefined) return cached;
14
- const item = treeRow.getItem();
15
- if (item !== null) treeItemCache.set(treeRow, item);
16
- return item;
17
- };
18
-
19
- export interface Resolved<T = unknown, S = unknown> {
20
- value: T | S | undefined;
21
- present: boolean;
22
- isHeader: boolean;
23
- treeRow: Gtk.TreeListRow | null;
24
- metadata: TreeItemMetadata;
25
- }
26
-
27
- export interface ItemResolver<T = unknown, S = unknown> {
28
- resolve(position: number, treeRow: Gtk.TreeListRow | null, boundItem: GObject.Object | null): Resolved<T, S>;
29
- positionOf(id: string): number;
30
- idOf(position: number): string | undefined;
31
- count: number;
32
- }
33
-
34
- export interface RowValue<T = unknown, S = unknown> {
35
- id: string;
36
- value: T | S;
37
- isHeader: boolean;
38
- metadata: TreeItemMetadata;
39
- }
40
-
41
- export const createControlledResolver = <T, S>(
42
- items: ListItem<T, S>[] | undefined,
43
- flattenTreeChildren: boolean,
44
- rowValues: WeakMap<GObject.Object, RowValue<T, S>>,
45
- ): ItemResolver<T, S> => {
46
- const flattened = flattenListItems(items, flattenTreeChildren);
47
- return {
48
- count: flattened.records.length,
49
- positionOf(id: string): number {
50
- const position = flattened.idToPosition.get(id);
51
- return position === undefined ? -1 : position;
52
- },
53
- idOf(position: number): string | undefined {
54
- return flattened.positionToId.get(position);
55
- },
56
- resolve(position: number, treeRow: Gtk.TreeListRow | null, _boundItem: GObject.Object | null): Resolved<T, S> {
57
- if (treeRow !== null && !flattenTreeChildren) {
58
- const rowItem = treeRowItem(treeRow);
59
- if (rowItem !== null) {
60
- const tagged = rowValues.get(rowItem);
61
- if (tagged !== undefined) {
62
- return {
63
- value: tagged.value,
64
- present: true,
65
- isHeader: tagged.isHeader,
66
- treeRow,
67
- metadata: tagged.metadata,
68
- };
69
- }
70
- }
71
- }
72
- const record = flattened.records[position];
73
- if (record === undefined) {
74
- return {
75
- value: undefined,
76
- present: false,
77
- isHeader: false,
78
- treeRow,
79
- metadata: NO_TREE_METADATA,
80
- };
81
- }
82
- return {
83
- value: record.value,
84
- present: true,
85
- isHeader: record.isHeader,
86
- treeRow,
87
- metadata: record.metadata,
88
- };
89
- },
90
- };
91
- };
92
-
93
- export const createModelResolver = <T, S>(model: Gio.ListModel): ItemResolver<T, S> => {
94
- return {
95
- get count(): number {
96
- return model.getNItems();
97
- },
98
- positionOf(id: string): number {
99
- const numeric = Number(id);
100
- return Number.isInteger(numeric) ? numeric : -1;
101
- },
102
- idOf(position: number): string {
103
- return String(position);
104
- },
105
- resolve(position: number, treeRow: Gtk.TreeListRow | null, boundItem: GObject.Object | null): Resolved<T, S> {
106
- const item = boundItem ?? model.getItem(position);
107
- return {
108
- value: (item ?? undefined) as T | undefined,
109
- present: item !== null,
110
- isHeader: false,
111
- treeRow,
112
- metadata: NO_TREE_METADATA,
113
- };
114
- },
115
- };
116
- };
117
-
118
- export const createSectionHeaderResolver = <T, S>(items: ListItem<T, S>[] | undefined): ItemResolver<T, S> => {
119
- const valueByStart = new Map<number, S>();
120
- let start = 0;
121
- for (const section of items ?? []) {
122
- if (section.section !== true) continue;
123
- valueByStart.set(start, section.value);
124
- start += section.children?.length ?? 0;
125
- }
126
- return {
127
- count: valueByStart.size,
128
- positionOf: () => -1,
129
- idOf: () => undefined,
130
- resolve(position: number): Resolved<T, S> {
131
- if (!valueByStart.has(position)) {
132
- return { value: undefined, present: false, isHeader: true, treeRow: null, metadata: NO_TREE_METADATA };
133
- }
134
- return {
135
- value: valueByStart.get(position),
136
- present: true,
137
- isHeader: true,
138
- treeRow: null,
139
- metadata: NO_TREE_METADATA,
140
- };
141
- },
142
- };
143
- };
@@ -1,113 +0,0 @@
1
- import type { ListItem } from "../types.js";
2
-
3
- export interface TreeItemMetadata {
4
- hideExpander: boolean;
5
- indentForDepth: boolean;
6
- indentForIcon: boolean;
7
- }
8
-
9
- export interface FlattenedRecord<T = unknown, S = unknown> {
10
- id: string;
11
- value: T | S;
12
- isHeader: boolean;
13
- hasChildren: boolean;
14
- children: ListItem<T, S>[];
15
- metadata: TreeItemMetadata;
16
- }
17
-
18
- export interface FlattenResult<T = unknown, S = unknown> {
19
- records: FlattenedRecord<T, S>[];
20
- idToPosition: Map<string, number>;
21
- positionToId: Map<number, string>;
22
- isTree: boolean;
23
- isSectioned: boolean;
24
- }
25
-
26
- export type ListStructure = "flat" | "tree" | "sections";
27
-
28
- /**
29
- * Classify a controlled item list as flat, tree, or sectioned without building a
30
- * {@link FlattenResult}. A list is sectioned if any top-level item is a section, a
31
- * tree if any top-level item carries children, and flat otherwise.
32
- */
33
- export const detectStructure = <T, S>(items: ListItem<T, S>[] | undefined): ListStructure => {
34
- if (items === undefined) return "flat";
35
- let sawTree = false;
36
- for (const item of items) {
37
- if (item.section === true) return "sections";
38
- if (item.children !== undefined && item.children.length > 0) sawTree = true;
39
- }
40
- return sawTree ? "tree" : "flat";
41
- };
42
-
43
- export const structuralSignature = <T, S>(items: ListItem<T, S>[] | undefined): string => {
44
- if (items === undefined) return "";
45
- const parts: string[] = [];
46
- const walk = (list: ListItem<T, S>[]): void => {
47
- parts.push("[");
48
- for (const item of list) {
49
- const hidden = item.section === true ? false : item.hideExpander === true;
50
- parts.push(`${item.id}|${item.section === true ? 1 : 0}|${hidden ? 1 : 0}`);
51
- if (item.children !== undefined && item.children.length > 0) walk(item.children);
52
- }
53
- parts.push("]");
54
- };
55
- walk(items);
56
- return parts.join(",");
57
- };
58
-
59
- export const treeItemMetadata = <T, S>(item: ListItem<T, S>): TreeItemMetadata => {
60
- if (item.section === true) {
61
- return { hideExpander: false, indentForDepth: false, indentForIcon: false };
62
- }
63
- return {
64
- hideExpander: item.hideExpander ?? false,
65
- indentForDepth: item.indentForDepth ?? true,
66
- indentForIcon: item.indentForIcon ?? true,
67
- };
68
- };
69
-
70
- const hasChildren = <T, S>(item: ListItem<T, S>): boolean => item.children !== undefined && item.children.length > 0;
71
-
72
- const appendRecord = <T, S>(item: ListItem<T, S>, result: FlattenResult<T, S>, includeChildren: boolean): void => {
73
- const position = result.records.length;
74
- result.idToPosition.set(item.id, position);
75
- result.positionToId.set(position, item.id);
76
- result.records.push({
77
- id: item.id,
78
- value: item.value,
79
- isHeader: item.section === true,
80
- hasChildren: hasChildren(item),
81
- children: item.children ?? [],
82
- metadata: treeItemMetadata(item),
83
- });
84
- if (includeChildren && item.children !== undefined) {
85
- for (const child of item.children) appendRecord(child, result, includeChildren);
86
- }
87
- };
88
-
89
- export const flattenListItems = <T, S>(
90
- items: ListItem<T, S>[] | undefined,
91
- flattenTreeChildren: boolean,
92
- ): FlattenResult<T, S> => {
93
- const result: FlattenResult<T, S> = {
94
- records: [],
95
- idToPosition: new Map(),
96
- positionToId: new Map(),
97
- isTree: false,
98
- isSectioned: false,
99
- };
100
- if (items === undefined) return result;
101
- for (const item of items) {
102
- if (item.section === true) {
103
- result.isSectioned = true;
104
- if (item.children !== undefined) {
105
- for (const child of item.children) appendRecord(child, result, true);
106
- }
107
- continue;
108
- }
109
- if (hasChildren(item)) result.isTree = true;
110
- appendRecord(item, result, flattenTreeChildren);
111
- }
112
- return result;
113
- };
@@ -1,115 +0,0 @@
1
- import * as Gio from "@gtkx/gi/gio";
2
- import type * as GObject from "@gtkx/gi/gobject";
3
- import * as Gtk from "@gtkx/gi/gtk";
4
- import type { ListItem } from "../types.js";
5
- import type { RowValue } from "./item-resolver.js";
6
- import { treeItemMetadata } from "./list-item-flatten.js";
7
-
8
- const emptyStrings = (count: number): string[] => new Array<string>(Math.max(0, count)).fill("");
9
-
10
- export const retagRows = <T, S>(
11
- items: ListItem<T, S>[],
12
- rowValues: WeakMap<GObject.Object, RowValue<T, S>>,
13
- placeholdersById: Map<string, GObject.Object>,
14
- ): void => {
15
- const walk = (list: ListItem<T, S>[]): void => {
16
- for (const item of list) {
17
- const placeholder = placeholdersById.get(item.id);
18
- if (placeholder !== undefined) {
19
- rowValues.set(placeholder, {
20
- id: item.id,
21
- value: item.value,
22
- isHeader: item.section === true,
23
- metadata: treeItemMetadata(item),
24
- });
25
- }
26
- if (item.children !== undefined && item.children.length > 0) walk(item.children);
27
- }
28
- };
29
- walk(items);
30
- };
31
-
32
- export const createFlatModel = (count: number): Gtk.StringList => Gtk.StringList.new(emptyStrings(count));
33
-
34
- export const resizeFlatModel = (model: Gtk.StringList, count: number): void => {
35
- const target = Math.max(0, count);
36
- const current = model.getNItems();
37
- if (current === target) return;
38
- if (target > current) {
39
- model.splice(current, 0, emptyStrings(target - current));
40
- return;
41
- }
42
- model.splice(target, current - target, null);
43
- };
44
-
45
- const createTaggedRow = <T, S>(
46
- row: RowValue<T, S>,
47
- rowValues: WeakMap<GObject.Object, RowValue<T, S>>,
48
- placeholdersById?: Map<string, GObject.Object>,
49
- ): GObject.Object => {
50
- const placeholder = Gtk.StringObject.new("");
51
- rowValues.set(placeholder, row);
52
- if (placeholdersById !== undefined) placeholdersById.set(row.id, placeholder);
53
- return placeholder;
54
- };
55
-
56
- const childModelOf = <T, S>(
57
- children: ListItem<T, S>[],
58
- rowValues: WeakMap<GObject.Object, RowValue<T, S>>,
59
- placeholdersById: Map<string, GObject.Object>,
60
- ): Gio.ListStore => {
61
- const store = Gio.ListStore.new(Gtk.StringObject.prototype.__gtype__);
62
- for (const child of children) {
63
- store.append(
64
- createTaggedRow(
65
- { id: child.id, value: child.value, isHeader: false, metadata: treeItemMetadata(child) },
66
- rowValues,
67
- placeholdersById,
68
- ),
69
- );
70
- }
71
- return store;
72
- };
73
-
74
- export const createTreeModel = <T, S>(
75
- items: ListItem<T, S>[],
76
- autoexpand: boolean,
77
- rowValues: WeakMap<GObject.Object, RowValue<T, S>>,
78
- placeholdersById: Map<string, GObject.Object>,
79
- ): Gtk.TreeListModel => {
80
- const childrenByRow = new WeakMap<GObject.Object, ListItem<T, S>[]>();
81
- const root = Gio.ListStore.new(Gtk.StringObject.prototype.__gtype__);
82
- for (const item of items) {
83
- const placeholder = createTaggedRow(
84
- { id: item.id, value: item.value, isHeader: item.section === true, metadata: treeItemMetadata(item) },
85
- rowValues,
86
- placeholdersById,
87
- );
88
- if (item.children !== undefined && item.children.length > 0) {
89
- childrenByRow.set(placeholder, item.children);
90
- }
91
- root.append(placeholder);
92
- }
93
- const createFunc = (rowItem: GObject.Object): Gio.ListModel | null => {
94
- const children = childrenByRow.get(rowItem);
95
- if (children === undefined) return null;
96
- const store = childModelOf(children, rowValues, placeholdersById);
97
- for (let index = 0; index < children.length; index++) {
98
- const child = children[index];
99
- if (child !== undefined && child.children !== undefined && child.children.length > 0) {
100
- const placeholder = store.getItem(index);
101
- if (placeholder !== null) childrenByRow.set(placeholder, child.children);
102
- }
103
- }
104
- return store;
105
- };
106
- return Gtk.TreeListModel.new(root, false, autoexpand, createFunc);
107
- };
108
-
109
- export const createSectionModel = <T, S>(items: ListItem<T, S>[]): Gtk.FlattenListModel => {
110
- const sections = Gio.ListStore.new(Gtk.StringList.prototype.__gtype__);
111
- for (const section of items) {
112
- sections.append(Gtk.StringList.new(emptyStrings(section.children?.length ?? 0)));
113
- }
114
- return Gtk.FlattenListModel.new(sections);
115
- };
@@ -1,126 +0,0 @@
1
- import type * as GObject from "@gtkx/gi/gobject";
2
- import type * as Gtk from "@gtkx/gi/gtk";
3
-
4
- export interface SlotEntry {
5
- container: GObject.Object;
6
- position: number;
7
- treeRow: Gtk.TreeListRow | null;
8
- item: GObject.Object | null;
9
- }
10
-
11
- let nextKey = 0;
12
-
13
- export class RealizedSlotStore {
14
- private entries = new Map<GObject.Object, SlotEntry>();
15
-
16
- private published = new Map<GObject.Object, SlotEntry>();
17
-
18
- private setListeners = new Set<() => void>();
19
-
20
- private positionListeners = new Map<GObject.Object, Set<() => void>>();
21
-
22
- private positionFlushers = new Map<GObject.Object, () => void>();
23
-
24
- private keys = new WeakMap<GObject.Object, string>();
25
-
26
- private snapshot: GObject.Object[] = [];
27
-
28
- subscribeSet = (onChange: () => void): (() => void) => {
29
- this.setListeners.add(onChange);
30
- return () => {
31
- this.setListeners.delete(onChange);
32
- };
33
- };
34
-
35
- getSlotsSnapshot = (): GObject.Object[] => this.snapshot;
36
-
37
- subscribePosition = (container: GObject.Object, onChange: () => void): (() => void) => {
38
- let listeners = this.positionListeners.get(container);
39
- if (listeners === undefined) {
40
- listeners = new Set();
41
- this.positionListeners.set(container, listeners);
42
- }
43
- listeners.add(onChange);
44
- return () => {
45
- const current = this.positionListeners.get(container);
46
- if (current === undefined) return;
47
- current.delete(onChange);
48
- if (current.size === 0) this.positionListeners.delete(container);
49
- };
50
- };
51
-
52
- getPosition = (container: GObject.Object): SlotEntry => {
53
- let entry = this.published.get(container);
54
- if (entry === undefined) {
55
- entry = { container, position: -1, treeRow: null, item: null };
56
- this.published.set(container, entry);
57
- }
58
- return entry;
59
- };
60
-
61
- keyFor = (container: GObject.Object): string => {
62
- const existing = this.keys.get(container);
63
- if (existing !== undefined) return existing;
64
- const key = `slot-${nextKey++}`;
65
- this.keys.set(container, key);
66
- return key;
67
- };
68
-
69
- addContainer = (container: GObject.Object): void => {
70
- if (this.entries.has(container)) return;
71
- this.entries.set(container, { container, position: -1, treeRow: null, item: null });
72
- this.notifySet();
73
- };
74
-
75
- bind = (
76
- container: GObject.Object,
77
- position: number,
78
- treeRow: Gtk.TreeListRow | null,
79
- item: GObject.Object | null,
80
- ): void => {
81
- if (!this.entries.has(container)) {
82
- this.entries.set(container, { container, position, treeRow, item });
83
- this.notifySet();
84
- return;
85
- }
86
- this.entries.set(container, { container, position, treeRow, item });
87
- this.notifyPosition(container);
88
- };
89
-
90
- unbind = (container: GObject.Object): void => {
91
- if (!this.entries.has(container)) return;
92
- this.entries.set(container, { container, position: -1, treeRow: null, item: null });
93
- this.notifyPosition(container);
94
- };
95
-
96
- removeContainer = (container: GObject.Object): void => {
97
- if (!this.entries.delete(container)) return;
98
- this.published.delete(container);
99
- this.positionFlushers.delete(container);
100
- this.notifySet();
101
- };
102
-
103
- private flushSet = (): void => {
104
- this.snapshot = [...this.entries.keys()];
105
- for (const listener of [...this.setListeners]) listener();
106
- };
107
-
108
- private notifySet(): void {
109
- this.flushSet();
110
- }
111
-
112
- private notifyPosition(container: GObject.Object): void {
113
- let flush = this.positionFlushers.get(container);
114
- if (flush === undefined) {
115
- flush = (): void => {
116
- const live = this.entries.get(container);
117
- if (live !== undefined) this.published.set(container, live);
118
- const listeners = this.positionListeners.get(container);
119
- if (listeners === undefined) return;
120
- for (const listener of [...listeners]) listener();
121
- };
122
- this.positionFlushers.set(container, flush);
123
- }
124
- flush();
125
- }
126
- }