@gtkx/components 1.0.0-rc.1 → 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 (210) hide show
  1. package/dist/adw/index.d.ts +3 -0
  2. package/dist/adw/index.d.ts.map +1 -0
  3. package/dist/adw/index.js +2 -0
  4. package/dist/adw/index.js.map +1 -0
  5. package/dist/adw/toast.d.ts +10 -0
  6. package/dist/adw/toast.d.ts.map +1 -0
  7. package/dist/adw/toast.js +51 -0
  8. package/dist/adw/toast.js.map +1 -0
  9. package/dist/adw/types.d.ts +28 -0
  10. package/dist/adw/types.d.ts.map +1 -0
  11. package/dist/adw/types.js +2 -0
  12. package/dist/adw/types.js.map +1 -0
  13. package/dist/column-view.d.ts +7 -29
  14. package/dist/column-view.d.ts.map +1 -1
  15. package/dist/column-view.js +98 -70
  16. package/dist/column-view.js.map +1 -1
  17. package/dist/drop-down.d.ts +8 -27
  18. package/dist/drop-down.d.ts.map +1 -1
  19. package/dist/drop-down.js +112 -81
  20. package/dist/drop-down.js.map +1 -1
  21. package/dist/grid-view.d.ts +6 -16
  22. package/dist/grid-view.d.ts.map +1 -1
  23. package/dist/grid-view.js +25 -33
  24. package/dist/grid-view.js.map +1 -1
  25. package/dist/index.d.ts +6 -12
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -6
  28. package/dist/index.js.map +1 -1
  29. package/dist/internal/cells.d.ts +50 -0
  30. package/dist/internal/cells.d.ts.map +1 -0
  31. package/dist/internal/cells.js +185 -0
  32. package/dist/internal/cells.js.map +1 -0
  33. package/dist/internal/collection-index.d.ts +21 -0
  34. package/dist/internal/collection-index.d.ts.map +1 -0
  35. package/dist/internal/collection-index.js +106 -0
  36. package/dist/internal/collection-index.js.map +1 -0
  37. package/dist/internal/collection-model.d.ts +12 -0
  38. package/dist/internal/collection-model.d.ts.map +1 -0
  39. package/dist/internal/collection-model.js +164 -0
  40. package/dist/internal/collection-model.js.map +1 -0
  41. package/dist/internal/collection.d.ts +18 -0
  42. package/dist/internal/collection.d.ts.map +1 -0
  43. package/dist/internal/collection.js +64 -0
  44. package/dist/internal/collection.js.map +1 -0
  45. package/dist/internal/expansion.d.ts +9 -0
  46. package/dist/internal/expansion.d.ts.map +1 -0
  47. package/dist/internal/expansion.js +75 -0
  48. package/dist/internal/expansion.js.map +1 -0
  49. package/dist/internal/selection.d.ts +12 -0
  50. package/dist/internal/selection.d.ts.map +1 -0
  51. package/dist/internal/selection.js +99 -0
  52. package/dist/internal/selection.js.map +1 -0
  53. package/dist/internal/use-collection.d.ts +18 -0
  54. package/dist/internal/use-collection.d.ts.map +1 -0
  55. package/dist/internal/use-collection.js +42 -0
  56. package/dist/internal/use-collection.js.map +1 -0
  57. package/dist/internal/use-widget-ref.d.ts +4 -0
  58. package/dist/internal/use-widget-ref.d.ts.map +1 -0
  59. package/dist/internal/use-widget-ref.js +8 -0
  60. package/dist/internal/use-widget-ref.js.map +1 -0
  61. package/dist/list-view.d.ts +5 -20
  62. package/dist/list-view.d.ts.map +1 -1
  63. package/dist/list-view.js +32 -53
  64. package/dist/list-view.js.map +1 -1
  65. package/dist/size-group.d.ts +8 -16
  66. package/dist/size-group.d.ts.map +1 -1
  67. package/dist/size-group.js +35 -51
  68. package/dist/size-group.js.map +1 -1
  69. package/dist/types.d.ts +97 -24
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/package.json +17 -6
  73. package/src/adw/index.ts +2 -0
  74. package/src/adw/toast.tsx +74 -0
  75. package/src/adw/types.ts +32 -0
  76. package/src/column-view.tsx +176 -160
  77. package/src/drop-down.tsx +180 -167
  78. package/src/grid-view.tsx +29 -73
  79. package/src/index.ts +18 -25
  80. package/src/internal/cells.tsx +345 -0
  81. package/src/internal/collection-index.ts +168 -0
  82. package/src/internal/collection-model.ts +246 -0
  83. package/src/internal/collection.ts +104 -0
  84. package/src/internal/expansion.ts +123 -0
  85. package/src/internal/selection.tsx +159 -0
  86. package/src/internal/use-collection.tsx +70 -0
  87. package/src/internal/use-widget-ref.ts +11 -0
  88. package/src/list-view.tsx +43 -139
  89. package/src/size-group.tsx +63 -82
  90. package/src/types.ts +155 -24
  91. package/dist/cell.d.ts +0 -30
  92. package/dist/cell.d.ts.map +0 -1
  93. package/dist/cell.js +0 -45
  94. package/dist/cell.js.map +0 -1
  95. package/dist/column-view-column.d.ts +0 -27
  96. package/dist/column-view-column.d.ts.map +0 -1
  97. package/dist/column-view-column.js +0 -30
  98. package/dist/column-view-column.js.map +0 -1
  99. package/dist/constraint-layout-apply.d.ts +0 -55
  100. package/dist/constraint-layout-apply.d.ts.map +0 -1
  101. package/dist/constraint-layout-apply.js +0 -107
  102. package/dist/constraint-layout-apply.js.map +0 -1
  103. package/dist/constraint-layout.d.ts +0 -20
  104. package/dist/constraint-layout.d.ts.map +0 -1
  105. package/dist/constraint-layout.js +0 -88
  106. package/dist/constraint-layout.js.map +0 -1
  107. package/dist/fixed.d.ts +0 -26
  108. package/dist/fixed.d.ts.map +0 -1
  109. package/dist/fixed.js +0 -40
  110. package/dist/fixed.js.map +0 -1
  111. package/dist/grid.d.ts +0 -27
  112. package/dist/grid.d.ts.map +0 -1
  113. package/dist/grid.js +0 -48
  114. package/dist/grid.js.map +0 -1
  115. package/dist/hooks/use-cell-containers.d.ts +0 -18
  116. package/dist/hooks/use-cell-containers.d.ts.map +0 -1
  117. package/dist/hooks/use-cell-containers.js +0 -67
  118. package/dist/hooks/use-cell-containers.js.map +0 -1
  119. package/dist/hooks/use-collection-model.d.ts +0 -26
  120. package/dist/hooks/use-collection-model.d.ts.map +0 -1
  121. package/dist/hooks/use-collection-model.js +0 -35
  122. package/dist/hooks/use-collection-model.js.map +0 -1
  123. package/dist/hooks/use-collection-widget.d.ts +0 -28
  124. package/dist/hooks/use-collection-widget.d.ts.map +0 -1
  125. package/dist/hooks/use-collection-widget.js +0 -21
  126. package/dist/hooks/use-collection-widget.js.map +0 -1
  127. package/dist/hooks/use-drop-down-selection.d.ts +0 -16
  128. package/dist/hooks/use-drop-down-selection.d.ts.map +0 -1
  129. package/dist/hooks/use-drop-down-selection.js +0 -35
  130. package/dist/hooks/use-drop-down-selection.js.map +0 -1
  131. package/dist/hooks/use-expansion-model.d.ts +0 -12
  132. package/dist/hooks/use-expansion-model.d.ts.map +0 -1
  133. package/dist/hooks/use-expansion-model.js +0 -71
  134. package/dist/hooks/use-expansion-model.js.map +0 -1
  135. package/dist/hooks/use-header-menu.d.ts +0 -4
  136. package/dist/hooks/use-header-menu.d.ts.map +0 -1
  137. package/dist/hooks/use-header-menu.js +0 -20
  138. package/dist/hooks/use-header-menu.js.map +0 -1
  139. package/dist/hooks/use-installed-model.d.ts +0 -4
  140. package/dist/hooks/use-installed-model.d.ts.map +0 -1
  141. package/dist/hooks/use-installed-model.js +0 -15
  142. package/dist/hooks/use-installed-model.js.map +0 -1
  143. package/dist/hooks/use-list-model.d.ts +0 -19
  144. package/dist/hooks/use-list-model.d.ts.map +0 -1
  145. package/dist/hooks/use-list-model.js +0 -97
  146. package/dist/hooks/use-list-model.js.map +0 -1
  147. package/dist/hooks/use-placed-child.d.ts +0 -18
  148. package/dist/hooks/use-placed-child.d.ts.map +0 -1
  149. package/dist/hooks/use-placed-child.js +0 -47
  150. package/dist/hooks/use-placed-child.js.map +0 -1
  151. package/dist/hooks/use-selection-model.d.ts +0 -13
  152. package/dist/hooks/use-selection-model.d.ts.map +0 -1
  153. package/dist/hooks/use-selection-model.js +0 -103
  154. package/dist/hooks/use-selection-model.js.map +0 -1
  155. package/dist/hooks/use-sort-handler.d.ts +0 -17
  156. package/dist/hooks/use-sort-handler.d.ts.map +0 -1
  157. package/dist/hooks/use-sort-handler.js +0 -87
  158. package/dist/hooks/use-sort-handler.js.map +0 -1
  159. package/dist/menu-items.d.ts +0 -5
  160. package/dist/menu-items.d.ts.map +0 -1
  161. package/dist/menu-items.js +0 -48
  162. package/dist/menu-items.js.map +0 -1
  163. package/dist/menu.d.ts +0 -14
  164. package/dist/menu.d.ts.map +0 -1
  165. package/dist/menu.js +0 -24
  166. package/dist/menu.js.map +0 -1
  167. package/dist/overlay.d.ts +0 -24
  168. package/dist/overlay.d.ts.map +0 -1
  169. package/dist/overlay.js +0 -49
  170. package/dist/overlay.js.map +0 -1
  171. package/dist/utils/cell-container-store.d.ts +0 -28
  172. package/dist/utils/cell-container-store.d.ts.map +0 -1
  173. package/dist/utils/cell-container-store.js +0 -98
  174. package/dist/utils/cell-container-store.js.map +0 -1
  175. package/dist/utils/item-models.d.ts +0 -10
  176. package/dist/utils/item-models.d.ts.map +0 -1
  177. package/dist/utils/item-models.js +0 -82
  178. package/dist/utils/item-models.js.map +0 -1
  179. package/dist/utils/item-resolver.d.ts +0 -25
  180. package/dist/utils/item-resolver.d.ts.map +0 -1
  181. package/dist/utils/item-resolver.js +0 -102
  182. package/dist/utils/item-resolver.js.map +0 -1
  183. package/dist/utils/list-item-flatten.d.ts +0 -24
  184. package/dist/utils/list-item-flatten.d.ts.map +0 -1
  185. package/dist/utils/list-item-flatten.js +0 -68
  186. package/dist/utils/list-item-flatten.js.map +0 -1
  187. package/src/cell.tsx +0 -144
  188. package/src/column-view-column.tsx +0 -82
  189. package/src/constraint-layout-apply.ts +0 -173
  190. package/src/constraint-layout.tsx +0 -135
  191. package/src/fixed.tsx +0 -86
  192. package/src/grid.tsx +0 -88
  193. package/src/hooks/use-cell-containers.ts +0 -93
  194. package/src/hooks/use-collection-model.ts +0 -60
  195. package/src/hooks/use-collection-widget.ts +0 -47
  196. package/src/hooks/use-drop-down-selection.ts +0 -52
  197. package/src/hooks/use-expansion-model.ts +0 -82
  198. package/src/hooks/use-header-menu.ts +0 -34
  199. package/src/hooks/use-installed-model.ts +0 -20
  200. package/src/hooks/use-list-model.ts +0 -161
  201. package/src/hooks/use-placed-child.ts +0 -79
  202. package/src/hooks/use-selection-model.ts +0 -114
  203. package/src/hooks/use-sort-handler.ts +0 -107
  204. package/src/menu-items.ts +0 -48
  205. package/src/menu.tsx +0 -32
  206. package/src/overlay.tsx +0 -88
  207. package/src/utils/cell-container-store.ts +0 -114
  208. package/src/utils/item-models.ts +0 -113
  209. package/src/utils/item-resolver.ts +0 -138
  210. package/src/utils/list-item-flatten.ts +0 -90
@@ -1,190 +1,206 @@
1
- import type * as Gtk from "@gtkx/gi/gtk";
2
- import { GtkColumnView, type GtkColumnViewProps } from "@gtkx/jsx/gtk";
3
- import { type ReactNode, type Ref, useCallback, useMemo, useState } from "react";
4
- import { createTreeRenderContext, HeaderRenderHost, type TreeRenderContext } from "./cell.js";
5
- import { type ColumnDef, ColumnViewColumn } from "./column-view-column.js";
6
- import { makeFactoryInstaller, useCellContainers } from "./hooks/use-cell-containers.js";
7
- import { useCollectionWidget } from "./hooks/use-collection-widget.js";
8
- import { type ColumnRegistration, useSortHandler } from "./hooks/use-sort-handler.js";
9
- import type {
10
- CollectionItemSizeProps,
11
- ControlledExpansionProps,
12
- ControlledSelectionProps,
13
- ItemNode,
14
- SectionNode,
15
- } from "./types.js";
16
- import type { CellContainerStore } from "./utils/cell-container-store.js";
17
- import type { ItemResolver } from "./utils/item-resolver.js";
18
-
19
- export type { ColumnDef, ColumnDefDeclarativeProps } from "./column-view-column.js";
20
-
21
- const headerFactoryInstaller = makeFactoryInstaller<Gtk.ColumnView>((widget, factory) =>
22
- widget.setHeaderFactory(factory),
23
- );
24
-
25
- export type ColumnViewSortProps = {
1
+ import type { ReactNode } from "react";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
+ import { GtkColumnView, GtkColumnViewColumn, GtkCustomSorter, GtkSignalListItemFactory } from "@gtkx/jsx/gtk";
4
+ import { useProperty, useSignal } from "@gtkx/react";
5
+ import { omit } from "@gtkx/utils";
6
+ import { useLayoutEffect, useRef } from "react";
7
+ import type { CellSize } from "./internal/cells.js";
8
+ import type { Collection } from "./internal/collection.js";
9
+ import type { Column, ColumnViewProps } from "./types.js";
10
+ import { HeaderPortals, ItemPortals, useHeaderCells, useItemCells } from "./internal/cells.js";
11
+ import { useCollection } from "./internal/use-collection.js";
12
+ import { useWidgetRef } from "./internal/use-widget-ref.js";
13
+
14
+ type SortProps = {
26
15
  sortColumn?: string | null | undefined;
27
16
  sortOrder?: Gtk.SortType | null | undefined;
28
17
  onSortChanged?: ((column: string | null, order: Gtk.SortType) => void) | null | undefined;
29
18
  };
30
19
 
31
- export type ColumnViewDeclarativeProps<T = unknown, S = unknown> = ColumnViewSortProps &
32
- Omit<CollectionItemSizeProps, "estimatedItemWidth"> &
33
- ControlledSelectionProps &
34
- ControlledExpansionProps & {
35
- items?: ItemNode<T>[] | undefined;
36
- sections?: SectionNode<S, T>[] | undefined;
37
- renderHeader?: ((info: { section: S }) => ReactNode) | null | undefined;
38
- columns: ColumnDef<T>[];
39
- };
20
+ type SortTarget = { view: Gtk.ColumnView; column: Gtk.ColumnViewColumn | null };
40
21
 
41
- /**
42
- * Props for {@link ColumnView}. Combines the underlying Gtk.ColumnView props with
43
- * declarative collection props: flat items or grouped sections, controlled selection
44
- * and expansion, sorting (sortColumn, sortOrder, onSortChanged), an optional section
45
- * header renderer, and the columns to render.
46
- */
47
- export type ColumnViewProps<T = unknown, S = unknown> = Omit<
48
- GtkColumnViewProps,
49
- "columns" | "model" | "headerFactory" | keyof ColumnViewDeclarativeProps<T, S>
50
- > &
51
- ColumnViewDeclarativeProps<T, S>;
52
-
53
- type NormalizedColumnViewProps<T, S> = ColumnViewDeclarativeProps<T, S> & {
54
- ref?: Ref<Gtk.ColumnView | null>;
55
- [key: string]: unknown;
22
+ type ColumnCellsProps = {
23
+ column: Column<never>;
24
+ collection: Collection;
25
+ expandedIds: string[] | null | undefined;
26
+ size: CellSize;
56
27
  };
57
28
 
58
- interface ColumnViewWiring<T, S> {
59
- setRef: (value: Gtk.ColumnView | null) => void;
60
- useHeader: boolean;
61
- headerStore: CellContainerStore;
62
- headerResolver: ItemResolver<T, S>;
63
- resolver: ItemResolver<unknown, unknown>;
64
- tree: TreeRenderContext;
65
- setInstance: (id: string, column: Gtk.ColumnViewColumn | null) => void;
66
- }
29
+ type ColumnListProps = Omit<ColumnCellsProps, "column"> & {
30
+ columns: Column<never>[];
31
+ };
32
+
33
+ const COLUMN_VIEW_PROPS = [
34
+ "items",
35
+ "sections",
36
+ "renderHeader",
37
+ "columns",
38
+ "selectedIds",
39
+ "onSelectionChanged",
40
+ "selectionMode",
41
+ "expandedIds",
42
+ "onExpandedChange",
43
+ "sortColumn",
44
+ "sortOrder",
45
+ "onSortChanged",
46
+ "estimatedItemHeight",
47
+ "children",
48
+ "ref",
49
+ ] as const satisfies (keyof ColumnViewProps)[];
50
+
51
+ const columnById = (view: Gtk.ColumnView, id: string): Gtk.ColumnViewColumn | null => {
52
+ const columns = view.getColumns();
53
+
54
+ for (let index = 0; index < columns.getNItems(); index++) {
55
+ const column = columns.getItem(index);
56
+
57
+ if (column instanceof Gtk.ColumnViewColumn && column.getId() === id) {
58
+ return column;
59
+ }
60
+ }
67
61
 
68
- const useColumnInstances = (): {
69
- instances: Map<string, Gtk.ColumnViewColumn>;
70
- setInstance: (id: string, column: Gtk.ColumnViewColumn | null) => void;
71
- } => {
72
- const [instances, setInstances] = useState<Map<string, Gtk.ColumnViewColumn>>(() => new Map());
73
- const setInstance = useCallback((id: string, column: Gtk.ColumnViewColumn | null): void => {
74
- setInstances((current) => {
75
- if (column === null) {
76
- if (!current.has(id)) return current;
77
- const next = new Map(current);
78
- next.delete(id);
79
- return next;
80
- }
81
- if (current.get(id) === column) return current;
82
- const next = new Map(current);
83
- next.set(id, column);
84
- return next;
85
- });
86
- }, []);
87
- return { instances, setInstance };
62
+ return null;
88
63
  };
89
64
 
90
- const buildRegistrations = <T,>(
91
- columns: ColumnDef<T>[],
92
- instances: Map<string, Gtk.ColumnViewColumn>,
93
- ): ColumnRegistration[] => {
94
- const result: ColumnRegistration[] = [];
95
- for (const column of columns) {
96
- const instance = instances.get(column.id);
97
- if (instance) result.push({ id: column.id, column: instance, sortable: column.sortable ?? false });
65
+ const getSortTarget = (view: Gtk.ColumnView | null, sortColumn: string | null | undefined): SortTarget | null => {
66
+ if (view === null || sortColumn === undefined) {
67
+ return null;
68
+ }
69
+
70
+ if (sortColumn === null) {
71
+ return { view, column: null };
98
72
  }
99
- return result;
73
+
74
+ const column = columnById(view, sortColumn);
75
+
76
+ return column === null ? null : { view, column };
100
77
  };
101
78
 
102
- const useColumnViewWiring = <T, S>(props: NormalizedColumnViewProps<T, S>): ColumnViewWiring<T, S> => {
103
- const { widgetRef, setRef, collection } = useCollectionWidget<Gtk.ColumnView, T, S>(props);
79
+ const applySort = (
80
+ sorting: { current: boolean },
81
+ view: Gtk.ColumnView,
82
+ column: Gtk.ColumnViewColumn | null,
83
+ order: Gtk.SortType | null | undefined,
84
+ ): void => {
85
+ sorting.current = true;
86
+
87
+ try {
88
+ view.sortByColumn(column, order ?? Gtk.SortType.ASCENDING);
89
+ } finally {
90
+ sorting.current = false;
91
+ }
92
+ };
104
93
 
105
- const useHeader = collection.useHeader;
106
- const headerStore = useCellContainers<Gtk.ColumnView>({
107
- object: useHeader ? widgetRef : null,
108
- installer: headerFactoryInstaller,
109
- estimatedHeight: props.estimatedItemHeight,
110
- });
94
+ const emitSortChanged = (
95
+ sorter: Gtk.ColumnViewSorter | null,
96
+ sorting: { current: boolean },
97
+ sort: SortProps,
98
+ ): void => {
99
+ if (sorter === null || sorting.current) {
100
+ return;
101
+ }
111
102
 
112
- const { instances, setInstance } = useColumnInstances();
113
- const registrations = useMemo<ColumnRegistration[]>(
114
- () => buildRegistrations(props.columns, instances),
115
- [props.columns, instances],
116
- );
103
+ sort.onSortChanged?.(sorter.getPrimarySortColumn()?.getId() ?? null, sorter.getPrimarySortOrder());
104
+ };
105
+
106
+ const syncSort = (
107
+ sorting: { current: boolean },
108
+ view: Gtk.ColumnView | null,
109
+ sortColumn: string | null | undefined,
110
+ sortOrder: Gtk.SortType | null | undefined,
111
+ ): void => {
112
+ const target = getSortTarget(view, sortColumn);
113
+
114
+ if (target === null) {
115
+ return;
116
+ }
117
117
 
118
- useSortHandler({
119
- columnView: widgetRef,
120
- sortColumn: props.sortColumn,
121
- sortOrder: props.sortOrder,
122
- onSortChanged: props.onSortChanged,
123
- columns: registrations,
118
+ applySort(sorting, target.view, target.column, sortOrder);
119
+ };
120
+
121
+ const useColumnSorting = (view: Gtk.ColumnView | null, sort: SortProps, columns: Column<never>[]): void => {
122
+ const sorting = useRef(false);
123
+ const sorter = useProperty(view, "sorter");
124
+ const columnSorter = sorter instanceof Gtk.ColumnViewSorter ? sorter : null;
125
+
126
+ useSignal(columnSorter, "changed", (): void => {
127
+ emitSortChanged(columnSorter, sorting, sort);
124
128
  });
125
129
 
126
- const expandedIds = props.expandedIds;
127
- const tree = useMemo<TreeRenderContext>(
128
- () => createTreeRenderContext(expandedIds, collection.rowId),
129
- [expandedIds, collection.rowId],
130
- );
130
+ const { sortColumn, sortOrder } = sort;
131
131
 
132
- return {
133
- setRef,
134
- useHeader,
135
- headerStore,
136
- headerResolver: collection.headerResolver,
137
- resolver: collection.resolver as ItemResolver<unknown, unknown>,
138
- tree,
139
- setInstance,
140
- };
132
+ useLayoutEffect(() => {
133
+ syncSort(sorting, view, sortColumn, sortOrder);
134
+ }, [view, sortColumn, sortOrder, columns]);
135
+ };
136
+
137
+ const ColumnCells = ({ column, collection, expandedIds, size }: ColumnCellsProps): ReactNode => {
138
+ const cells = useItemCells(size);
139
+ const rest = omit(column, ["id", "renderCell", "sortable"]);
140
+
141
+ return (
142
+ <>
143
+ <GtkColumnViewColumn
144
+ id={column.id}
145
+ factory={<GtkSignalListItemFactory {...cells.handlers} />}
146
+ sorter={column.sortable === true ? <GtkCustomSorter /> : null}
147
+ {...rest}
148
+ />
149
+ <ItemPortals
150
+ store={cells}
151
+ render={column.renderCell}
152
+ collection={collection}
153
+ expandedIds={expandedIds}
154
+ />
155
+ </>
156
+ );
141
157
  };
142
158
 
159
+ const ColumnList = ({ columns, collection, expandedIds, size }: ColumnListProps): ReactNode =>
160
+ columns.map((column) => (
161
+ <ColumnCells
162
+ key={column.id}
163
+ column={column}
164
+ collection={collection}
165
+ expandedIds={expandedIds}
166
+ size={size}
167
+ />
168
+ ));
169
+
143
170
  /**
144
- * Renders a Gtk.ColumnView: a multi-column, scrollable list backed by a collection
145
- * model. Columns are declared through the columns prop, each a {@link ColumnDef}.
171
+ * Renders a Gtk.ColumnView from declarative items or sections and a columns array,
172
+ * with controlled selection, expansion, and sorting, per-column header menus, and
173
+ * section header rendering.
146
174
  */
147
- export const ColumnView = <T = unknown, S = unknown>(props: ColumnViewProps<T, S>): ReactNode => {
148
- const normalized = props as NormalizedColumnViewProps<T, S>;
149
- const wiring = useColumnViewWiring<T, S>(normalized);
150
- const {
151
- ref,
152
- items,
153
- sections,
154
- columns,
155
- selectedIds,
156
- selectionMode,
157
- onSelectionChanged,
158
- expandedIds,
159
- onExpandedChange,
160
- sortColumn,
161
- sortOrder,
162
- onSortChanged,
163
- renderHeader,
164
- estimatedItemHeight,
165
- ...intrinsicProps
166
- } = normalized;
175
+ function ColumnView<T = unknown, S = unknown>(props: ColumnViewProps<T, S>): ReactNode {
176
+ const { renderHeader, columns, expandedIds, sortColumn, sortOrder, onSortChanged, estimatedItemHeight, ref } =
177
+ props;
178
+
179
+ const rest = omit(props, COLUMN_VIEW_PROPS);
180
+ const [view, refCallback] = useWidgetRef<Gtk.ColumnView>(ref);
181
+ const size = { width: -1, height: estimatedItemHeight ?? -1 };
182
+ const { collection, selection } = useCollection(props);
183
+ const headerCells = useHeaderCells(size);
184
+ const columnList = columns as Column<never>[];
185
+ useColumnSorting(view, { sortColumn, sortOrder, onSortChanged }, columnList);
167
186
 
168
187
  return (
169
188
  <>
170
- <GtkColumnView {...intrinsicProps} ref={wiring.setRef}>
171
- {columns.map((column) => (
172
- <ColumnViewColumn
173
- key={column.id}
174
- {...column}
175
- resolver={wiring.resolver}
176
- tree={wiring.tree}
177
- estimatedItemHeight={estimatedItemHeight}
178
- onInstance={wiring.setInstance}
179
- />
180
- ))}
189
+ <GtkColumnView
190
+ ref={refCallback}
191
+ model={selection}
192
+ {...(renderHeader != null && {
193
+ headerFactory: <GtkSignalListItemFactory {...headerCells.handlers} />,
194
+ })}
195
+ {...rest}
196
+ >
197
+ <ColumnList columns={columnList} collection={collection} expandedIds={expandedIds} size={size} />
181
198
  </GtkColumnView>
182
- <HeaderRenderHost
183
- useHeader={wiring.useHeader}
184
- store={wiring.headerStore}
185
- resolver={wiring.headerResolver}
186
- renderHeader={renderHeader}
187
- />
199
+ {renderHeader != null && (
200
+ <HeaderPortals store={headerCells} render={renderHeader} collection={collection} />
201
+ )}
188
202
  </>
189
203
  );
190
- };
204
+ }
205
+
206
+ export { ColumnView };