@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
@@ -1,135 +0,0 @@
1
- import type * as Gtk from "@gtkx/gi/gtk";
2
- import { GtkConstraintLayout } from "@gtkx/jsx/gtk";
3
- import { useMergeRefs } from "@gtkx/react/internal";
4
- import {
5
- type Context,
6
- createContext,
7
- type ReactNode,
8
- type Ref,
9
- type RefObject,
10
- useContext,
11
- useEffect,
12
- useLayoutEffect,
13
- useRef,
14
- } from "react";
15
- import {
16
- applyConstraint,
17
- applyGuide,
18
- applyVfl,
19
- type ConstraintGuideProps,
20
- type ConstraintProps,
21
- type ConstraintVflProps,
22
- } from "./constraint-layout-apply.js";
23
-
24
- const ORPHAN_MESSAGE = "<ConstraintLayout.Constraint> / <Guide> / <Vfl> must be a child of <ConstraintLayout>";
25
-
26
- const ConstraintLayoutContext: Context<RefObject<Gtk.ConstraintLayout | null> | null> =
27
- createContext<RefObject<Gtk.ConstraintLayout | null> | null>(null);
28
-
29
- const useConstraintLayoutRef = (): RefObject<Gtk.ConstraintLayout | null> => {
30
- const ref = useContext(ConstraintLayoutContext);
31
- if (!ref) throw new Error(ORPHAN_MESSAGE);
32
- return ref;
33
- };
34
-
35
- /** Props for {@link ConstraintLayout}. */
36
- export type ConstraintLayoutProps = {
37
- children?: ReactNode;
38
- ref?: Ref<Gtk.ConstraintLayout | null>;
39
- };
40
-
41
- type ContributionCleanupRef = { current: (() => void) | null };
42
-
43
- const cleanupContribution = (cleanupRef: ContributionCleanupRef): void => {
44
- const cleanup = cleanupRef.current;
45
- if (!cleanup) return;
46
- cleanupRef.current = null;
47
- cleanup();
48
- };
49
-
50
- const useLayoutContribution = (apply: (layout: Gtk.ConstraintLayout) => () => void, deps: unknown[]): void => {
51
- const layoutRef = useConstraintLayoutRef();
52
- useLayoutEffect(() => {
53
- const layout = layoutRef.current;
54
- if (!layout) return;
55
- return apply(layout);
56
- }, deps);
57
- };
58
-
59
- const useDeferredContribution = (apply: (layout: Gtk.ConstraintLayout) => () => void, deps: unknown[]): void => {
60
- const layoutRef = useConstraintLayoutRef();
61
- const cleanupRef = useRef<(() => void) | null>(null);
62
-
63
- useLayoutEffect(() => () => cleanupContribution(cleanupRef), deps);
64
-
65
- useEffect(() => {
66
- const layout = layoutRef.current;
67
- if (!layout) return;
68
- const cleanup = apply(layout);
69
- cleanupRef.current = cleanup;
70
- return () => {
71
- if (cleanupRef.current === cleanup) cleanupContribution(cleanupRef);
72
- };
73
- }, deps);
74
- };
75
-
76
- /**
77
- * Installs a Gtk.ConstraintLayout on a host widget and exposes child components to
78
- * populate it: {@link ConstraintProps | ConstraintLayout.Constraint} for individual constraints,
79
- * {@link ConstraintGuideProps | ConstraintLayout.Guide} for spacing guides, and {@link ConstraintVflProps | ConstraintLayout.Vfl}
80
- * for constraints authored in the Visual Format Language.
81
- */
82
- export const ConstraintLayout: ((props: ConstraintLayoutProps) => ReactNode) & {
83
- Guide: (props: ConstraintGuideProps) => ReactNode;
84
- Constraint: (props: ConstraintProps) => ReactNode;
85
- Vfl: (props: ConstraintVflProps) => ReactNode;
86
- } = Object.assign(
87
- ({ children, ref }: ConstraintLayoutProps): ReactNode => {
88
- const layoutRef = useRef<Gtk.ConstraintLayout | null>(null);
89
- const mergedRef = useMergeRefs<Gtk.ConstraintLayout>(ref, layoutRef);
90
- return (
91
- <>
92
- <GtkConstraintLayout ref={mergedRef} />
93
- <ConstraintLayoutContext.Provider value={layoutRef}>{children}</ConstraintLayoutContext.Provider>
94
- </>
95
- );
96
- },
97
- {
98
- Guide: (props: ConstraintGuideProps): ReactNode => {
99
- useLayoutContribution(
100
- (layout) => applyGuide(layout, props),
101
- [
102
- props.id,
103
- props.minWidth,
104
- props.minHeight,
105
- props.natWidth,
106
- props.natHeight,
107
- props.maxWidth,
108
- props.maxHeight,
109
- props.strength,
110
- ],
111
- );
112
- return null;
113
- },
114
- Constraint: (props: ConstraintProps): ReactNode => {
115
- useDeferredContribution(
116
- (layout) => applyConstraint(layout, props),
117
- [
118
- props.target,
119
- props.targetAttribute,
120
- props.relation,
121
- props.source,
122
- props.sourceAttribute,
123
- props.multiplier,
124
- props.constant,
125
- props.strength,
126
- ],
127
- );
128
- return null;
129
- },
130
- Vfl: (props: ConstraintVflProps): ReactNode => {
131
- useDeferredContribution((layout) => applyVfl(layout, props), [props.lines, props.hspacing, props.vspacing]);
132
- return null;
133
- },
134
- },
135
- );
package/src/fixed.tsx DELETED
@@ -1,86 +0,0 @@
1
- import * as Graphene from "@gtkx/gi/graphene";
2
- import * as Gsk from "@gtkx/gi/gsk";
3
- import type * as Gtk from "@gtkx/gi/gtk";
4
- import { GtkFixed, type GtkFixedProps } from "@gtkx/jsx/gtk";
5
- import { useMergeRefs } from "@gtkx/react/internal";
6
- import {
7
- createContext,
8
- type ElementType,
9
- type ReactNode,
10
- type Ref,
11
- type RefCallback,
12
- useCallback,
13
- useContext,
14
- useLayoutEffect,
15
- useRef,
16
- useState,
17
- } from "react";
18
- import type { ChildProps } from "./types.js";
19
-
20
- const FixedContext = createContext<Gtk.Fixed | null | undefined>(undefined);
21
-
22
- const useFixedInstance = (): Gtk.Fixed | null => {
23
- const fixed = useContext(FixedContext);
24
- if (fixed === undefined) throw new Error("<Fixed.Child> must be a child of <Fixed>");
25
- return fixed;
26
- };
27
-
28
- /** Props for {@link Fixed}. */
29
- export type FixedProps = GtkFixedProps & { ref?: Ref<Gtk.Fixed | null>; children?: ReactNode };
30
-
31
- export type FixedPlacementProps = {
32
- x?: number | null | undefined;
33
- y?: number | null | undefined;
34
- /** Full transform applied to the child, overriding x and y when provided. */
35
- transform?: Gsk.Transform | null | undefined;
36
- };
37
-
38
- /** Positions a single child inside a {@link Fixed} at coordinates x and y, or by an explicit transform. */
39
- export type FixedChildProps<C extends ElementType> = ChildProps<C, FixedPlacementProps>;
40
-
41
- const transformOf = (x: number, y: number, transform: Gsk.Transform | null | undefined): Gsk.Transform | null =>
42
- transform !== undefined ? transform : Gsk.Transform.new().translate(Graphene.Point.create(x, y));
43
-
44
- const FixedChild = <C extends ElementType>({
45
- component,
46
- x,
47
- y,
48
- transform,
49
- ref,
50
- ...rest
51
- }: FixedChildProps<C>): ReactNode => {
52
- const fixed = useFixedInstance();
53
- const Component: ElementType = component;
54
- const widgetRef = useRef<Gtk.Widget | null>(null);
55
- const captureWidget = useCallback<RefCallback<Gtk.Widget>>((node) => {
56
- widgetRef.current = node;
57
- }, []);
58
- const setWidget = useMergeRefs<Gtk.Widget>(ref, captureWidget);
59
-
60
- useLayoutEffect(() => {
61
- const widget = widgetRef.current;
62
- if (widget === null || fixed === null) return;
63
- fixed.setChildTransform(widget, transformOf(x ?? 0, y ?? 0, transform));
64
- });
65
-
66
- return <Component {...rest} ref={setWidget} />;
67
- };
68
-
69
- /**
70
- * Renders a Gtk.Fixed container whose children are placed at explicit positions via
71
- * {@link Fixed.Child}.
72
- */
73
- export const Fixed: ((props: FixedProps) => ReactNode) & {
74
- Child: <C extends ElementType>(props: FixedChildProps<C>) => ReactNode;
75
- } = Object.assign(
76
- ({ children, ref, ...rest }: FixedProps): ReactNode => {
77
- const [fixed, setFixed] = useState<Gtk.Fixed | null>(null);
78
- const mergedRef = useMergeRefs<Gtk.Fixed>(ref, setFixed);
79
- return (
80
- <GtkFixed {...rest} ref={mergedRef}>
81
- <FixedContext.Provider value={fixed}>{children}</FixedContext.Provider>
82
- </GtkFixed>
83
- );
84
- },
85
- { Child: FixedChild },
86
- );
package/src/grid.tsx DELETED
@@ -1,88 +0,0 @@
1
- import type * as Gtk from "@gtkx/gi/gtk";
2
- import { GtkGrid, type GtkGridProps } from "@gtkx/jsx/gtk";
3
- import { useMergeRefs } from "@gtkx/react/internal";
4
- import { Children, type ElementType, type ReactNode, type Ref, useRef } from "react";
5
- import { createParentContext, usePlacedChild } from "./hooks/use-placed-child.js";
6
- import type { ChildProps } from "./types.js";
7
-
8
- const { Context: GridContext, useParentRef: useGridRef } = createParentContext<Gtk.Grid>(
9
- "<Grid.Child> must be a child of <Grid>",
10
- );
11
-
12
- /** Props for {@link Grid}. */
13
- export type GridProps = GtkGridProps & { ref?: Ref<Gtk.Grid | null>; children?: ReactNode };
14
-
15
- export type GridPlacement = {
16
- column?: number | null | undefined;
17
- row?: number | null | undefined;
18
- /** Number of columns the child spans (defaults to 1). */
19
- columnSpan?: number | null | undefined;
20
- /** Number of rows the child spans (defaults to 1). */
21
- rowSpan?: number | null | undefined;
22
- };
23
-
24
- /** Places a single child inside a {@link Grid} at a column and row, optionally spanning multiple cells. */
25
- export type GridChildProps<C extends ElementType> = ChildProps<C, GridPlacement>;
26
-
27
- type Placement = { column: number; row: number; columnSpan: number; rowSpan: number };
28
-
29
- const placementOf = (props: GridPlacement): Placement => ({
30
- column: props.column ?? 0,
31
- row: props.row ?? 0,
32
- columnSpan: props.columnSpan ?? 1,
33
- rowSpan: props.rowSpan ?? 1,
34
- });
35
-
36
- const samePlacement = (a: Placement, b: Placement): boolean =>
37
- a.column === b.column && a.row === b.row && a.columnSpan === b.columnSpan && a.rowSpan === b.rowSpan;
38
-
39
- const GridChild = <C extends ElementType>({
40
- component,
41
- column,
42
- row,
43
- columnSpan,
44
- rowSpan,
45
- ref,
46
- ...rest
47
- }: GridChildProps<C>): ReactNode => {
48
- const gridRef = useGridRef();
49
- const Component: ElementType = component;
50
- return usePlacedChild<Gtk.Widget, Placement>({
51
- render: (placeRef) => <Component {...rest} ref={placeRef} />,
52
- ref,
53
- placement: placementOf({ column, row, columnSpan, rowSpan }),
54
- samePlacement,
55
- place: (widget, placement, previous) => {
56
- const grid = gridRef.current;
57
- if (!grid) return;
58
- if (previous !== undefined && widget.getParent() === grid) grid.remove(widget);
59
- if (widget.getParent() !== grid) {
60
- grid.attach(widget, placement.column, placement.row, placement.columnSpan, placement.rowSpan);
61
- }
62
- },
63
- release: (widget) => {
64
- const grid = gridRef.current;
65
- if (grid && widget.getParent() === grid) grid.remove(widget);
66
- },
67
- });
68
- };
69
-
70
- /**
71
- * Renders a Gtk.Grid whose children are attached at explicit column/row positions via
72
- * {@link Grid.Child}.
73
- */
74
- export const Grid: ((props: GridProps) => ReactNode) & {
75
- Child: <C extends ElementType>(props: GridChildProps<C>) => ReactNode;
76
- } = Object.assign(
77
- ({ children, ref, ...rest }: GridProps): ReactNode => {
78
- const gridRef = useRef<Gtk.Grid | null>(null);
79
- const mergedRef = useMergeRefs<Gtk.Grid>(ref, gridRef);
80
- return (
81
- <>
82
- <GtkGrid {...rest} ref={mergedRef} />
83
- <GridContext.Provider value={gridRef}>{Children.toArray(children)}</GridContext.Provider>
84
- </>
85
- );
86
- },
87
- { Child: GridChild },
88
- );
@@ -1,93 +0,0 @@
1
- import type * as GObject from "@gtkx/gi/gobject";
2
- import * as Gtk from "@gtkx/gi/gtk";
3
- import { type ObjectProp, useObjectAttachment } from "@gtkx/react/internal";
4
- import { useRef } from "react";
5
- import { CellContainerStore } from "../utils/cell-container-store.js";
6
-
7
- type ChildContainer = {
8
- getChild(): Gtk.Widget | null;
9
- setChild(child: Gtk.Widget | null): void;
10
- getItem(): GObject.Object | null;
11
- };
12
-
13
- const isChildContainer = (container: GObject.Object): container is GObject.Object & ChildContainer =>
14
- container instanceof Gtk.ListItem || container instanceof Gtk.ListHeader;
15
-
16
- const positionOf = (container: GObject.Object & ChildContainer): number => {
17
- if (container instanceof Gtk.ListItem) return container.getPosition();
18
- if (container instanceof Gtk.ListHeader) return container.getStart();
19
- return -1;
20
- };
21
-
22
- export type FactoryInstaller<W extends GObject.Object> = {
23
- install(widget: W, factory: Gtk.SignalListItemFactory): void;
24
- uninstall(widget: W): void;
25
- };
26
-
27
- export const makeFactoryInstaller = <W extends GObject.Object>(
28
- setFactory: (widget: W, factory: Gtk.SignalListItemFactory | null) => void,
29
- ): FactoryInstaller<W> => ({
30
- install: (widget, factory) => setFactory(widget, factory),
31
- uninstall: (widget) => setFactory(widget, null),
32
- });
33
-
34
- type CellContainersOptions<W extends GObject.Object> = {
35
- object: ObjectProp<W>;
36
- installer: FactoryInstaller<W>;
37
- estimatedHeight?: number | undefined;
38
- estimatedWidth?: number | undefined;
39
- };
40
-
41
- const applyEstimatedSize = (child: Gtk.Widget, height: number | undefined, width: number | undefined): void => {
42
- if (height === undefined && width === undefined) return;
43
- child.setSizeRequest(width ?? -1, height ?? -1);
44
- };
45
-
46
- export const useCellContainers = <W extends GObject.Object>(options: CellContainersOptions<W>): CellContainerStore => {
47
- const { object, installer, estimatedHeight, estimatedWidth } = options;
48
- const storeRef = useRef<CellContainerStore | null>(null);
49
- if (storeRef.current === null) storeRef.current = new CellContainerStore();
50
- const store = storeRef.current;
51
-
52
- const installerRef = useRef(installer);
53
- installerRef.current = installer;
54
-
55
- const factoryRef = useRef<Gtk.SignalListItemFactory | null>(null);
56
- if (factoryRef.current === null) {
57
- const factory = Gtk.SignalListItemFactory.new();
58
- factory.on("setup", (container) => {
59
- if (isChildContainer(container) && container.getChild() === null) {
60
- const placeholder = new Gtk.Box();
61
- applyEstimatedSize(placeholder, estimatedHeight, estimatedWidth);
62
- container.setChild(placeholder);
63
- }
64
- store.addContainer(container);
65
- });
66
- factory.on("bind", (container) => {
67
- if (isChildContainer(container)) {
68
- const item = container.getItem();
69
- const treeRow = item instanceof Gtk.TreeListRow ? item : null;
70
- store.bind(container, positionOf(container), treeRow);
71
- }
72
- });
73
- factory.on("unbind", (container) => {
74
- store.unbind(container);
75
- });
76
- factory.on("teardown", (container) => {
77
- store.removeContainer(container);
78
- });
79
- factoryRef.current = factory;
80
- }
81
- const factory = factoryRef.current;
82
-
83
- useObjectAttachment<W, { widget: W }>(object, {
84
- attach: (widget) => {
85
- installerRef.current.install(widget, factory);
86
- return { widget };
87
- },
88
- detach: (attachment) => installerRef.current.uninstall(attachment.widget),
89
- isSame: (attachment, widget) => attachment.widget === widget,
90
- });
91
-
92
- return store;
93
- };
@@ -1,60 +0,0 @@
1
- import type * as Gtk from "@gtkx/gi/gtk";
2
- import { type ReactNode, useCallback } from "react";
3
- import type { ItemNode, SectionNode } from "../types.js";
4
- import { type ItemResolver, rowIdOf } from "../utils/item-resolver.js";
5
- import { useExpansionModel } from "./use-expansion-model.js";
6
- import { useListModel } from "./use-list-model.js";
7
- import { useSelectionModel } from "./use-selection-model.js";
8
-
9
- type CollectionModelInput<T, S> = {
10
- items: ItemNode<T>[] | undefined;
11
- sections: SectionNode<S, T>[] | undefined;
12
- selectionMode: Gtk.SelectionMode | null | undefined;
13
- selectedIds: string[] | null | undefined;
14
- onSelectionChanged: ((ids: string[]) => void) | null | undefined;
15
- expandedIds?: string[] | null | undefined;
16
- onExpandedChange?: ((ids: string[]) => void) | null | undefined;
17
- renderHeader: ((info: { section: S }) => ReactNode) | null | undefined;
18
- };
19
-
20
- export type CollectionModelResult<T, S> = {
21
- resolver: ItemResolver<T, S>;
22
- headerResolver: ItemResolver<T, S>;
23
- installedModel: Gtk.SelectionModel;
24
- useHeader: boolean;
25
- rowId: (row: Gtk.TreeListRow) => string | undefined;
26
- };
27
-
28
- export const useCollectionModel = <T, S>(input: CollectionModelInput<T, S>): CollectionModelResult<T, S> => {
29
- const listModel = useListModel<T, S>({
30
- items: input.items,
31
- sections: input.sections,
32
- });
33
-
34
- useExpansionModel<T>({
35
- treeModel: listModel.treeModel,
36
- rowValues: listModel.rowValues,
37
- expandedIds: input.expandedIds,
38
- onExpandedChange: input.onExpandedChange,
39
- });
40
-
41
- const installedModel = useSelectionModel<T, S>({
42
- base: listModel.model,
43
- resolver: listModel.resolver,
44
- selectionMode: input.selectionMode,
45
- selectedIds: input.selectedIds,
46
- onSelectionChanged: input.onSelectionChanged,
47
- });
48
-
49
- const useHeader = typeof input.renderHeader === "function";
50
- const { rowValues } = listModel;
51
- const rowId = useCallback((row: Gtk.TreeListRow): string | undefined => rowIdOf(rowValues, row), [rowValues]);
52
-
53
- return {
54
- resolver: listModel.resolver,
55
- headerResolver: listModel.headerResolver,
56
- installedModel,
57
- useHeader,
58
- rowId,
59
- };
60
- };
@@ -1,47 +0,0 @@
1
- import type * as Gtk from "@gtkx/gi/gtk";
2
- import { useMergeRefs } from "@gtkx/react/internal";
3
- import { type ReactNode, type Ref, type RefCallback, type RefObject, useRef } from "react";
4
- import type { ItemNode, SectionNode } from "../types.js";
5
- import { type CollectionModelResult, useCollectionModel } from "./use-collection-model.js";
6
- import { useInstalledModel } from "./use-installed-model.js";
7
-
8
- type CollectionWidget = Gtk.Widget & {
9
- setModel(model: Gtk.SelectionModel | null): void;
10
- };
11
-
12
- type CollectionWidgetInput<W extends CollectionWidget, T, S> = {
13
- ref?: Ref<W | null> | undefined;
14
- items?: ItemNode<T>[] | undefined;
15
- sections?: SectionNode<S, T>[] | undefined;
16
- selectionMode?: Gtk.SelectionMode | null | undefined;
17
- selectedIds?: string[] | null | undefined;
18
- onSelectionChanged?: ((ids: string[]) => void) | null | undefined;
19
- expandedIds?: string[] | null | undefined;
20
- onExpandedChange?: ((ids: string[]) => void) | null | undefined;
21
- renderHeader?: ((info: { section: S }) => ReactNode) | null | undefined;
22
- };
23
-
24
- type CollectionWidgetResult<W extends CollectionWidget, T, S> = {
25
- widgetRef: RefObject<W | null>;
26
- setRef: RefCallback<W>;
27
- collection: CollectionModelResult<T, S>;
28
- };
29
-
30
- export const useCollectionWidget = <W extends CollectionWidget, T, S>(
31
- props: CollectionWidgetInput<W, T, S>,
32
- ): CollectionWidgetResult<W, T, S> => {
33
- const widgetRef = useRef<W | null>(null);
34
- const setRef = useMergeRefs<W>(props.ref, widgetRef);
35
- const collection = useCollectionModel<T, S>({
36
- items: props.items,
37
- sections: props.sections,
38
- selectionMode: props.selectionMode,
39
- selectedIds: props.selectedIds,
40
- onSelectionChanged: props.onSelectionChanged,
41
- expandedIds: props.expandedIds,
42
- onExpandedChange: props.onExpandedChange,
43
- renderHeader: props.renderHeader,
44
- });
45
- useInstalledModel(widgetRef, collection.installedModel, (widget, model) => widget.setModel(model));
46
- return { widgetRef, setRef, collection };
47
- };
@@ -1,52 +0,0 @@
1
- import type * as GObject from "@gtkx/gi/gobject";
2
- import * as Gtk from "@gtkx/gi/gtk";
3
- import { useSignal } from "@gtkx/react";
4
- import { type ObjectProp, resolveObjectProp, useObjectValue } from "@gtkx/react/internal";
5
- import { useLayoutEffect, useRef } from "react";
6
- import type { ItemResolver } from "../utils/item-resolver.js";
7
-
8
- type DropDownSelectionObject = GObject.Object & {
9
- getSelected(): number;
10
- setSelected(position: number): void;
11
- };
12
-
13
- type DropDownSelectionOptions<T, S> = {
14
- widget: ObjectProp<DropDownSelectionObject>;
15
- resolver: ItemResolver<T, S>;
16
- selectedId: string | null | undefined;
17
- onSelectionChanged: ((id: string) => void) | null | undefined;
18
- };
19
-
20
- const normalizeSelected = (position: number): number =>
21
- position === Gtk.INVALID_LIST_POSITION || position < 0 ? -1 : position;
22
-
23
- export const useDropDownSelection = <T, S>(options: DropDownSelectionOptions<T, S>): number => {
24
- const { widget, resolver, selectedId, onSelectionChanged } = options;
25
- const resolved = resolveObjectProp(widget);
26
-
27
- const selectedPosition = useObjectValue(widget, "notify::selected", (object) =>
28
- object === null ? -1 : normalizeSelected(object.getSelected()),
29
- );
30
-
31
- const resolverRef = useRef(resolver);
32
- resolverRef.current = resolver;
33
- const onSelectionChangedRef = useRef(onSelectionChanged);
34
- onSelectionChangedRef.current = onSelectionChanged;
35
-
36
- useLayoutEffect(() => {
37
- if (resolved === null || selectedId === undefined || selectedId === null) return;
38
- const position = resolverRef.current.positionOfId(selectedId);
39
- if (position < 0) return;
40
- if (normalizeSelected(resolved.getSelected()) !== position) resolved.setSelected(position);
41
- }, [resolved, selectedId, resolver]);
42
-
43
- useSignal(widget, "notify::selected", () => {
44
- if (resolved === null) return;
45
- const position = normalizeSelected(resolved.getSelected());
46
- if (position < 0) return;
47
- const id = resolverRef.current.idOf(position);
48
- if (id !== undefined) onSelectionChangedRef.current?.(id);
49
- });
50
-
51
- return selectedPosition;
52
- };
@@ -1,82 +0,0 @@
1
- import type * as GObject from "@gtkx/gi/gobject";
2
- import type * as Gtk from "@gtkx/gi/gtk";
3
- import { useSignal } from "@gtkx/react";
4
- import { isSameArray } from "@gtkx/utils";
5
- import { useLayoutEffect, useRef } from "react";
6
- import { type RowValue, rowIdOf } from "../utils/item-resolver.js";
7
-
8
- type ExpansionModelOptions<T> = {
9
- treeModel: Gtk.TreeListModel | null;
10
- rowValues: WeakMap<GObject.Object, RowValue<T>>;
11
- expandedIds: string[] | null | undefined;
12
- onExpandedChange: ((ids: string[]) => void) | null | undefined;
13
- };
14
-
15
- const readExpandedIds = <T>(model: Gtk.TreeListModel, rowValues: WeakMap<GObject.Object, RowValue<T>>): string[] => {
16
- const ids: string[] = [];
17
- const count = model.getNItems();
18
- for (let position = 0; position < count; position++) {
19
- const row = model.getRow(position);
20
- if (row?.getExpanded()) {
21
- const id = rowIdOf(rowValues, row);
22
- if (id !== undefined) ids.push(id);
23
- }
24
- }
25
- return ids;
26
- };
27
-
28
- const applyExpanded = <T>(
29
- model: Gtk.TreeListModel,
30
- rowValues: WeakMap<GObject.Object, RowValue<T>>,
31
- ids: string[],
32
- ): void => {
33
- const target = new Set(ids);
34
- let changed = true;
35
- while (changed) {
36
- changed = false;
37
- const count = model.getNItems();
38
- for (let position = 0; position < count; position++) {
39
- const row = model.getRow(position);
40
- if (row === null || !row.isExpandable()) continue;
41
- const id = rowIdOf(rowValues, row);
42
- if (id === undefined) continue;
43
- const desired = target.has(id);
44
- if (row.getExpanded() !== desired) {
45
- row.setExpanded(desired);
46
- changed = true;
47
- }
48
- }
49
- }
50
- };
51
-
52
- export const useExpansionModel = <T>(options: ExpansionModelOptions<T>): void => {
53
- const { treeModel, rowValues, expandedIds, onExpandedChange } = options;
54
- const onChangeRef = useRef(onExpandedChange);
55
- onChangeRef.current = onExpandedChange;
56
- const rowValuesRef = useRef(rowValues);
57
- rowValuesRef.current = rowValues;
58
- const lastReportedRef = useRef<string[] | null>(null);
59
- const applyingRef = useRef(false);
60
-
61
- const report = (): void => {
62
- if (treeModel === null || applyingRef.current) return;
63
- const callback = onChangeRef.current;
64
- if (!callback) return;
65
- const ids = readExpandedIds(treeModel, rowValuesRef.current);
66
- if (lastReportedRef.current !== null && isSameArray(lastReportedRef.current, ids)) return;
67
- lastReportedRef.current = ids;
68
- callback(ids);
69
- };
70
-
71
- useSignal(treeModel, "items-changed", report);
72
-
73
- useLayoutEffect(() => {
74
- if (treeModel === null) return;
75
- if (expandedIds !== undefined && expandedIds !== null) {
76
- applyingRef.current = true;
77
- applyExpanded(treeModel, rowValuesRef.current, expandedIds);
78
- applyingRef.current = false;
79
- }
80
- report();
81
- }, [treeModel, expandedIds]);
82
- };
@@ -1,34 +0,0 @@
1
- import type * as Gio from "@gtkx/gi/gio";
2
- import type * as Gtk from "@gtkx/gi/gtk";
3
- import { createPortal, rootElement } from "@gtkx/react";
4
- import { useMergeRefs } from "@gtkx/react/internal";
5
- import {
6
- cloneElement,
7
- isValidElement,
8
- type ReactElement,
9
- type ReactNode,
10
- type Ref,
11
- type RefCallback,
12
- useCallback,
13
- useLayoutEffect,
14
- useRef,
15
- } from "react";
16
-
17
- type MenuElement = ReactElement<{ ref?: Ref<Gio.MenuModel | null> }>;
18
-
19
- export const useHeaderMenu = (column: Gtk.ColumnViewColumn | null, headerMenu: ReactNode): ReactNode => {
20
- const modelRef = useRef<Gio.MenuModel | null>(null);
21
- const setModel = useCallback<RefCallback<Gio.MenuModel>>((model) => {
22
- modelRef.current = model;
23
- }, []);
24
- const element = isValidElement(headerMenu) ? (headerMenu as MenuElement) : null;
25
- const mergedRef = useMergeRefs<Gio.MenuModel>(setModel, element?.props.ref);
26
-
27
- useLayoutEffect(() => {
28
- if (column === null) return;
29
- column.setHeaderMenu(headerMenu == null ? null : modelRef.current);
30
- });
31
-
32
- if (element === null) return null;
33
- return createPortal(cloneElement(element, { ref: mergedRef }), rootElement);
34
- };