@gtkx/components 0.21.0 → 1.0.0-rc.1
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.
- package/LICENSE +373 -0
- package/README.md +173 -0
- package/dist/cell.d.ts +30 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +45 -0
- package/dist/cell.js.map +1 -0
- package/dist/column-view-column.d.ts +24 -14
- package/dist/column-view-column.d.ts.map +1 -1
- package/dist/column-view-column.js +19 -40
- package/dist/column-view-column.js.map +1 -1
- package/dist/column-view.d.ts +25 -11
- package/dist/column-view.d.ts.map +1 -1
- package/dist/column-view.js +53 -83
- package/dist/column-view.js.map +1 -1
- package/dist/constraint-layout-apply.d.ts +50 -1
- package/dist/constraint-layout-apply.d.ts.map +1 -1
- package/dist/constraint-layout-apply.js.map +1 -1
- package/dist/constraint-layout.d.ts +8 -1
- package/dist/constraint-layout.d.ts.map +1 -1
- package/dist/constraint-layout.js +12 -9
- package/dist/constraint-layout.js.map +1 -1
- package/dist/drop-down.d.ts +26 -36
- package/dist/drop-down.d.ts.map +1 -1
- package/dist/drop-down.js +47 -87
- package/dist/drop-down.js.map +1 -1
- package/dist/fixed.d.ts +26 -0
- package/dist/fixed.d.ts.map +1 -0
- package/dist/fixed.js +40 -0
- package/dist/fixed.js.map +1 -0
- package/dist/grid-view.d.ts +13 -10
- package/dist/grid-view.d.ts.map +1 -1
- package/dist/grid-view.js +30 -16
- package/dist/grid-view.js.map +1 -1
- package/dist/grid.d.ts +27 -0
- package/dist/grid.d.ts.map +1 -0
- package/dist/grid.js +48 -0
- package/dist/grid.js.map +1 -0
- package/dist/hooks/use-cell-containers.d.ts +18 -0
- package/dist/hooks/use-cell-containers.d.ts.map +1 -0
- package/dist/hooks/{use-realized-slots.js → use-cell-containers.js} +18 -17
- package/dist/hooks/use-cell-containers.js.map +1 -0
- package/dist/hooks/use-collection-model.d.ts +26 -0
- package/dist/hooks/use-collection-model.d.ts.map +1 -0
- package/dist/hooks/use-collection-model.js +35 -0
- package/dist/hooks/use-collection-model.js.map +1 -0
- package/dist/hooks/use-collection-widget.d.ts +28 -0
- package/dist/hooks/use-collection-widget.d.ts.map +1 -0
- package/dist/hooks/use-collection-widget.js +21 -0
- package/dist/hooks/use-collection-widget.js.map +1 -0
- package/dist/hooks/use-drop-down-selection.d.ts +7 -6
- package/dist/hooks/use-drop-down-selection.d.ts.map +1 -1
- package/dist/hooks/use-drop-down-selection.js +6 -6
- package/dist/hooks/use-drop-down-selection.js.map +1 -1
- package/dist/hooks/use-expansion-model.d.ts +12 -0
- package/dist/hooks/use-expansion-model.d.ts.map +1 -0
- package/dist/hooks/use-expansion-model.js +71 -0
- package/dist/hooks/use-expansion-model.js.map +1 -0
- package/dist/hooks/use-header-menu.d.ts.map +1 -1
- package/dist/hooks/use-header-menu.js +12 -24
- package/dist/hooks/use-header-menu.js.map +1 -1
- package/dist/hooks/use-installed-model.d.ts +4 -0
- package/dist/hooks/use-installed-model.d.ts.map +1 -0
- package/dist/hooks/use-installed-model.js +15 -0
- package/dist/hooks/use-installed-model.js.map +1 -0
- package/dist/hooks/use-list-model.d.ts +14 -15
- package/dist/hooks/use-list-model.d.ts.map +1 -1
- package/dist/hooks/use-list-model.js +61 -34
- package/dist/hooks/use-list-model.js.map +1 -1
- package/dist/hooks/use-placed-child.d.ts +18 -0
- package/dist/hooks/use-placed-child.d.ts.map +1 -0
- package/dist/hooks/use-placed-child.js +47 -0
- package/dist/hooks/use-placed-child.js.map +1 -0
- package/dist/hooks/use-selection-model.d.ts +4 -3
- package/dist/hooks/use-selection-model.d.ts.map +1 -1
- package/dist/hooks/use-selection-model.js +8 -16
- package/dist/hooks/use-selection-model.js.map +1 -1
- package/dist/hooks/use-sort-handler.d.ts +10 -5
- package/dist/hooks/use-sort-handler.d.ts.map +1 -1
- package/dist/hooks/use-sort-handler.js +2 -2
- package/dist/hooks/use-sort-handler.js.map +1 -1
- package/dist/index.d.ts +11 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -13
- package/dist/index.js.map +1 -1
- package/dist/list-view.d.ts +17 -9
- package/dist/list-view.d.ts.map +1 -1
- package/dist/list-view.js +49 -24
- package/dist/list-view.js.map +1 -1
- package/dist/menu-items.d.ts.map +1 -1
- package/dist/menu-items.js.map +1 -1
- package/dist/menu.d.ts +6 -10
- package/dist/menu.d.ts.map +1 -1
- package/dist/menu.js +7 -9
- package/dist/menu.js.map +1 -1
- package/dist/overlay.d.ts +24 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +49 -0
- package/dist/overlay.js.map +1 -0
- package/dist/size-group.d.ts +20 -0
- package/dist/size-group.d.ts.map +1 -0
- package/dist/size-group.js +57 -0
- package/dist/size-group.js.map +1 -0
- package/dist/types.d.ts +43 -173
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/{realized-slot-store.d.ts → cell-container-store.d.ts} +7 -9
- package/dist/utils/cell-container-store.d.ts.map +1 -0
- package/dist/utils/{realized-slot-store.js → cell-container-store.js} +9 -14
- package/dist/utils/cell-container-store.js.map +1 -0
- package/dist/utils/item-models.d.ts +10 -0
- package/dist/utils/item-models.d.ts.map +1 -0
- package/dist/utils/{position-only-model.js → item-models.js} +13 -14
- package/dist/utils/item-models.js.map +1 -0
- package/dist/utils/item-resolver.d.ts +14 -17
- package/dist/utils/item-resolver.d.ts.map +1 -1
- package/dist/utils/item-resolver.js +34 -43
- package/dist/utils/item-resolver.js.map +1 -1
- package/dist/utils/list-item-flatten.d.ts +16 -24
- package/dist/utils/list-item-flatten.d.ts.map +1 -1
- package/dist/utils/list-item-flatten.js +16 -36
- package/dist/utils/list-item-flatten.js.map +1 -1
- package/package.json +52 -53
- package/src/cell.tsx +144 -0
- package/src/column-view-column.tsx +57 -48
- package/src/column-view.tsx +130 -146
- package/src/constraint-layout-apply.ts +53 -1
- package/src/constraint-layout.tsx +18 -13
- package/src/drop-down.tsx +129 -196
- package/src/fixed.tsx +86 -0
- package/src/grid-view.tsx +52 -44
- package/src/grid.tsx +88 -0
- package/src/hooks/{use-realized-slots.ts → use-cell-containers.ts} +30 -30
- package/src/hooks/use-collection-model.ts +60 -0
- package/src/hooks/use-collection-widget.ts +47 -0
- package/src/hooks/use-drop-down-selection.ts +12 -13
- package/src/hooks/use-expansion-model.ts +82 -0
- package/src/hooks/use-header-menu.ts +24 -23
- package/src/hooks/use-installed-model.ts +20 -0
- package/src/hooks/use-list-model.ts +103 -87
- package/src/hooks/use-placed-child.ts +79 -0
- package/src/hooks/use-selection-model.ts +11 -18
- package/src/hooks/use-sort-handler.ts +13 -8
- package/src/index.ts +25 -27
- package/src/list-view.tsx +120 -57
- package/src/menu.tsx +12 -14
- package/src/overlay.tsx +88 -0
- package/src/size-group.tsx +96 -0
- package/src/types.ts +55 -206
- package/src/utils/{realized-slot-store.ts → cell-container-store.ts} +13 -25
- package/src/utils/{position-only-model.ts → item-models.ts} +26 -28
- package/src/utils/item-resolver.ts +54 -59
- package/src/utils/list-item-flatten.ts +33 -56
- package/dist/collection-view.d.ts +0 -30
- package/dist/collection-view.d.ts.map +0 -1
- package/dist/collection-view.js +0 -52
- package/dist/collection-view.js.map +0 -1
- package/dist/contexts/column-view-context.d.ts +0 -17
- package/dist/contexts/column-view-context.d.ts.map +0 -1
- package/dist/contexts/column-view-context.js +0 -10
- package/dist/contexts/column-view-context.js.map +0 -1
- package/dist/hooks/use-model-installation.d.ts +0 -9
- package/dist/hooks/use-model-installation.d.ts.map +0 -1
- package/dist/hooks/use-model-installation.js +0 -20
- package/dist/hooks/use-model-installation.js.map +0 -1
- package/dist/hooks/use-realized-slots.d.ts +0 -20
- package/dist/hooks/use-realized-slots.d.ts.map +0 -1
- package/dist/hooks/use-realized-slots.js.map +0 -1
- package/dist/list-portal-host.d.ts +0 -11
- package/dist/list-portal-host.d.ts.map +0 -1
- package/dist/list-portal-host.js +0 -8
- package/dist/list-portal-host.js.map +0 -1
- package/dist/list-slot.d.ts +0 -14
- package/dist/list-slot.d.ts.map +0 -1
- package/dist/list-slot.js +0 -29
- package/dist/list-slot.js.map +0 -1
- package/dist/utils/position-only-model.d.ts +0 -10
- package/dist/utils/position-only-model.d.ts.map +0 -1
- package/dist/utils/position-only-model.js.map +0 -1
- package/dist/utils/realized-slot-store.d.ts.map +0 -1
- package/dist/utils/realized-slot-store.js.map +0 -1
- package/src/collection-view.tsx +0 -125
- package/src/contexts/column-view-context.ts +0 -28
- package/src/hooks/use-model-installation.ts +0 -25
- package/src/list-portal-host.tsx +0 -27
- package/src/list-slot.tsx +0 -56
package/src/drop-down.tsx
CHANGED
|
@@ -1,134 +1,115 @@
|
|
|
1
|
-
import type * as Adw from "@gtkx/gi/adw";
|
|
2
|
-
import type * as Gio from "@gtkx/gi/gio";
|
|
3
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
type ElementType,
|
|
10
|
-
type ReactElement,
|
|
11
|
-
type ReactNode,
|
|
12
|
-
type Ref,
|
|
13
|
-
useCallback,
|
|
14
|
-
useRef,
|
|
15
|
-
useState,
|
|
16
|
-
} from "react";
|
|
2
|
+
import { GtkDropDown, GtkLabel } from "@gtkx/jsx/gtk";
|
|
3
|
+
import { useMergeRefs } from "@gtkx/react/internal";
|
|
4
|
+
import { type ElementType, type ReactNode, type Ref, useCallback, useRef, useState } from "react";
|
|
5
|
+
import { type CellRenderer, CellRenderHost, HeaderRenderHost, itemRenderer } from "./cell.js";
|
|
6
|
+
import { makeFactoryInstaller, useCellContainers } from "./hooks/use-cell-containers.js";
|
|
17
7
|
import { useDropDownSelection } from "./hooks/use-drop-down-selection.js";
|
|
8
|
+
import { useInstalledModel } from "./hooks/use-installed-model.js";
|
|
18
9
|
import { useListModel } from "./hooks/use-list-model.js";
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
21
|
-
import { ListPortalHost } from "./list-portal-host.js";
|
|
22
|
-
import type { SlotRenderer } from "./list-slot.js";
|
|
23
|
-
import type { DropDownProps, ListItem } from "./types.js";
|
|
10
|
+
import type { ItemNode, RenderItemProps, SectionNode, WidgetProps } from "./types.js";
|
|
11
|
+
import type { CellContainerStore } from "./utils/cell-container-store.js";
|
|
24
12
|
import type { ItemResolver } from "./utils/item-resolver.js";
|
|
25
|
-
import type { RealizedSlotStore } from "./utils/realized-slot-store.js";
|
|
26
13
|
|
|
27
|
-
export
|
|
28
|
-
getSelected(): number;
|
|
29
|
-
setSelected(position: number): void;
|
|
30
|
-
setModel(model: Gio.ListModel | null): void;
|
|
31
|
-
setFactory(factory: Gtk.ListItemFactory | null): void;
|
|
32
|
-
setListFactory(factory: Gtk.ListItemFactory | null): void;
|
|
33
|
-
setHeaderFactory(factory: Gtk.ListItemFactory | null): void;
|
|
34
|
-
}
|
|
14
|
+
export type DropDownItemRenderer<T> = (props: RenderItemProps<T>) => ReactNode;
|
|
35
15
|
|
|
36
|
-
const
|
|
37
|
-
install: (widget, factory) => widget.setFactory(factory),
|
|
38
|
-
uninstall: (widget) => widget.setFactory(null),
|
|
39
|
-
};
|
|
16
|
+
const itemFactoryInstaller = makeFactoryInstaller<Gtk.DropDown>((widget, factory) => widget.setFactory(factory));
|
|
40
17
|
|
|
41
|
-
const
|
|
42
|
-
install: (widget, factory) => widget.setListFactory(factory),
|
|
43
|
-
uninstall: (widget) => widget.setListFactory(null),
|
|
44
|
-
};
|
|
18
|
+
const listFactoryInstaller = makeFactoryInstaller<Gtk.DropDown>((widget, factory) => widget.setListFactory(factory));
|
|
45
19
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
};
|
|
20
|
+
const headerFactoryInstaller = makeFactoryInstaller<Gtk.DropDown>((widget, factory) =>
|
|
21
|
+
widget.setHeaderFactory(factory),
|
|
22
|
+
);
|
|
50
23
|
|
|
51
|
-
const defaultRenderer:
|
|
52
|
-
if (
|
|
53
|
-
return
|
|
24
|
+
const defaultRenderer: CellRenderer<unknown, unknown> = (value) => {
|
|
25
|
+
if (value === undefined || value === null) return null;
|
|
26
|
+
return <GtkLabel>{String(value)}</GtkLabel>;
|
|
54
27
|
};
|
|
55
28
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
ref: Ref<W | null> | undefined;
|
|
60
|
-
items: ListItem<T, S>[] | undefined;
|
|
61
|
-
model: Gio.ListModel | undefined;
|
|
62
|
-
renderItem: ((value: T) => ReactNode) | null | undefined;
|
|
63
|
-
renderListItem: ((value: T) => ReactNode) | null | undefined;
|
|
64
|
-
renderHeader: ((value: S) => ReactNode) | null | undefined;
|
|
65
|
-
selectedId: string | null | undefined;
|
|
66
|
-
onSelectionChanged: ((id: string) => void) | null | undefined;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const toItemRenderer = <T, S>(renderItem: ((value: T) => ReactNode) | null | undefined): SlotRenderer<T, S> => {
|
|
70
|
-
if (typeof renderItem !== "function") return defaultRenderer as SlotRenderer<T, S>;
|
|
71
|
-
return (value, _treeRow, isHeader) => (isHeader ? null : renderItem(value as T));
|
|
29
|
+
const toItemRenderer = <T, S>(renderItem: DropDownItemRenderer<T> | null | undefined): CellRenderer<T, S> => {
|
|
30
|
+
if (typeof renderItem !== "function") return defaultRenderer as CellRenderer<T, S>;
|
|
31
|
+
return itemRenderer<T, S>(renderItem);
|
|
72
32
|
};
|
|
73
33
|
|
|
74
|
-
const
|
|
75
|
-
renderListItem:
|
|
76
|
-
renderItem:
|
|
77
|
-
):
|
|
78
|
-
if (typeof renderListItem === "function")
|
|
79
|
-
return (value, _treeRow, isHeader) => (isHeader ? null : renderListItem(value as T));
|
|
34
|
+
const toListRenderer = <T, S>(
|
|
35
|
+
renderListItem: DropDownItemRenderer<T> | null | undefined,
|
|
36
|
+
renderItem: DropDownItemRenderer<T> | null | undefined,
|
|
37
|
+
): CellRenderer<T, S> => {
|
|
38
|
+
if (typeof renderListItem === "function") return itemRenderer<T, S>(renderListItem);
|
|
80
39
|
return toItemRenderer<T, S>(renderItem);
|
|
81
40
|
};
|
|
82
41
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
42
|
+
const createSelectionResolver = <T, S>(resolver: ItemResolver<T, S>, selectedPosition: number): ItemResolver<T, S> => ({
|
|
43
|
+
positionOfId: (id) => resolver.positionOfId(id),
|
|
44
|
+
idOf: (position) => resolver.idOf(position),
|
|
45
|
+
resolve: (_position, treeRow) => resolver.resolve(selectedPosition, treeRow),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/** Declarative props for {@link DropDown}'s backing collection and cell rendering. */
|
|
49
|
+
export type DropDownDeclarativeProps<T = unknown, S = unknown> = {
|
|
50
|
+
items?: ItemNode<T>[] | undefined;
|
|
51
|
+
sections?: SectionNode<S, T>[] | undefined;
|
|
52
|
+
/** Id of the currently selected item, making the selection controlled. */
|
|
53
|
+
selectedId?: string | null | undefined;
|
|
54
|
+
onSelectionChanged?: ((id: string) => void) | null | undefined;
|
|
55
|
+
renderItem?: DropDownItemRenderer<T> | null | undefined;
|
|
56
|
+
/** Renderer for items in the open popup list, falling back to renderItem when omitted. */
|
|
57
|
+
renderListItem?: DropDownItemRenderer<T> | null | undefined;
|
|
58
|
+
/** Renderer for section headers in the popup list. */
|
|
59
|
+
renderHeader?: ((info: { section: S }) => ReactNode) | null | undefined;
|
|
86
60
|
};
|
|
87
61
|
|
|
88
|
-
|
|
89
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Props for {@link DropDown}. The backing widget is chosen through the `component` prop, defaulting to
|
|
64
|
+
* GtkDropDown, and its own props combine with {@link DropDownDeclarativeProps}.
|
|
65
|
+
*/
|
|
66
|
+
export type DropDownProps<T = unknown, S = unknown, C extends ElementType = typeof GtkDropDown> = WidgetProps<
|
|
67
|
+
C,
|
|
68
|
+
DropDownDeclarativeProps<T, S>,
|
|
69
|
+
"model" | "factory" | "listFactory" | "headerFactory"
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
interface NormalizedDropDownProps<T, S> {
|
|
73
|
+
ref: Ref<Gtk.DropDown | null> | undefined;
|
|
74
|
+
items: ItemNode<T>[] | undefined;
|
|
75
|
+
sections: SectionNode<S, T>[] | undefined;
|
|
76
|
+
selectedId: string | null | undefined;
|
|
77
|
+
onSelectionChanged: ((id: string) => void) | null | undefined;
|
|
78
|
+
renderHeader: ((info: { section: S }) => ReactNode) | null | undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface DropDownWiring<T, S> {
|
|
82
|
+
setRef: (value: Gtk.DropDown | null) => void;
|
|
90
83
|
resolver: ItemResolver<T, S>;
|
|
91
|
-
|
|
84
|
+
selectionResolver: ItemResolver<T, S>;
|
|
92
85
|
headerResolver: ItemResolver<T, S>;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
headerStore:
|
|
86
|
+
selectionStore: CellContainerStore;
|
|
87
|
+
listStore: CellContainerStore;
|
|
88
|
+
headerStore: CellContainerStore;
|
|
96
89
|
useHeader: boolean;
|
|
97
90
|
}
|
|
98
91
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
resolve: (_position, treeRow) => resolver.resolve(selectedPosition, treeRow, null),
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const useDropDownWiring = <T, S, W extends DropDownWidget>(
|
|
107
|
-
props: DropDownImplProps<T, S, W>,
|
|
108
|
-
): DropDownWiring<T, S, W> => {
|
|
109
|
-
const widgetRef = useRef<DropDownWidget | null>(null);
|
|
110
|
-
const [widget, setWidget] = useState<DropDownWidget | null>(null);
|
|
111
|
-
const captureWidget = useCallback((value: W | null) => {
|
|
92
|
+
const useDropDownWiring = <T, S>(props: NormalizedDropDownProps<T, S>): DropDownWiring<T, S> => {
|
|
93
|
+
const widgetRef = useRef<Gtk.DropDown | null>(null);
|
|
94
|
+
const [widget, setWidget] = useState<Gtk.DropDown | null>(null);
|
|
95
|
+
const captureWidget = useCallback((value: Gtk.DropDown | null) => {
|
|
112
96
|
widgetRef.current = value;
|
|
113
97
|
setWidget(value);
|
|
114
98
|
}, []);
|
|
115
|
-
const
|
|
99
|
+
const setRef = useMergeRefs<Gtk.DropDown>(props.ref, captureWidget);
|
|
116
100
|
|
|
117
|
-
const
|
|
118
|
-
const listModel = useListModel<T, S>(
|
|
119
|
-
externalModel === undefined ? { items: props.items ?? undefined } : { model: externalModel },
|
|
120
|
-
);
|
|
101
|
+
const listModel = useListModel<T, S>({ items: props.items, sections: props.sections });
|
|
121
102
|
|
|
122
|
-
const useHeader =
|
|
103
|
+
const useHeader = typeof props.renderHeader === "function";
|
|
123
104
|
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
105
|
+
const selectionStore = useCellContainers<Gtk.DropDown>({ object: widgetRef, installer: itemFactoryInstaller });
|
|
106
|
+
const listStore = useCellContainers<Gtk.DropDown>({ object: widgetRef, installer: listFactoryInstaller });
|
|
107
|
+
const headerStore = useCellContainers<Gtk.DropDown>({
|
|
108
|
+
object: useHeader ? widgetRef : null,
|
|
109
|
+
installer: headerFactoryInstaller,
|
|
129
110
|
});
|
|
130
111
|
|
|
131
|
-
|
|
112
|
+
useInstalledModel(widgetRef, listModel.model, (widget, value) => widget.setModel(value));
|
|
132
113
|
|
|
133
114
|
const selectedPosition = useDropDownSelection<T, S>({
|
|
134
115
|
widget,
|
|
@@ -140,120 +121,72 @@ const useDropDownWiring = <T, S, W extends DropDownWidget>(
|
|
|
140
121
|
const controlledPosition =
|
|
141
122
|
props.selectedId === undefined || props.selectedId === null
|
|
142
123
|
? -1
|
|
143
|
-
: listModel.resolver.
|
|
144
|
-
const
|
|
124
|
+
: listModel.resolver.positionOfId(props.selectedId);
|
|
125
|
+
const selectionPosition =
|
|
126
|
+
controlledPosition >= 0 ? controlledPosition : selectedPosition < 0 ? 0 : selectedPosition;
|
|
145
127
|
|
|
146
128
|
return {
|
|
147
129
|
setRef,
|
|
148
130
|
resolver: listModel.resolver,
|
|
149
|
-
|
|
131
|
+
selectionResolver: createSelectionResolver(listModel.resolver, selectionPosition),
|
|
150
132
|
headerResolver: listModel.headerResolver,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
headerStore
|
|
133
|
+
selectionStore,
|
|
134
|
+
listStore,
|
|
135
|
+
headerStore,
|
|
154
136
|
useHeader,
|
|
155
137
|
};
|
|
156
138
|
};
|
|
157
139
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<ListPortalHost store={faceStore} resolver={faceResolver} render={toItemRenderer<T, S>(props.renderItem)} />
|
|
167
|
-
<ListPortalHost
|
|
168
|
-
store={popupStore}
|
|
169
|
-
resolver={resolver}
|
|
170
|
-
render={toPopupRenderer<T, S>(props.renderListItem, props.renderItem)}
|
|
171
|
-
/>
|
|
172
|
-
{useHeader ? (
|
|
173
|
-
<ListPortalHost
|
|
174
|
-
store={headerStore}
|
|
175
|
-
resolver={headerResolver}
|
|
176
|
-
render={toHeaderRenderer<T, S>(props.renderHeader)}
|
|
177
|
-
/>
|
|
178
|
-
) : null}
|
|
179
|
-
</>
|
|
180
|
-
);
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
type DropDownImplComponentProps<T, S, W extends DropDownWidget> = DropDownProps<T, S> & {
|
|
184
|
-
ref?: Ref<W | null> | undefined;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const extractDropDownProps = <T, S, W extends DropDownWidget>(
|
|
188
|
-
props: DropDownImplComponentProps<T, S, W>,
|
|
189
|
-
): {
|
|
190
|
-
impl: Omit<DropDownImplProps<T, S, W>, "element" | "intrinsicProps">;
|
|
191
|
-
intrinsicProps: Record<string, unknown>;
|
|
192
|
-
} => {
|
|
140
|
+
/**
|
|
141
|
+
* Renders a drop-down widget (Gtk.DropDown by default, or the widget given as `component`) backed by a
|
|
142
|
+
* collection model, with customizable rendering for the selected item, the popup list rows, and section
|
|
143
|
+
* headers.
|
|
144
|
+
*/
|
|
145
|
+
export const DropDown = <T = unknown, S = unknown, C extends ElementType = typeof GtkDropDown>(
|
|
146
|
+
props: DropDownProps<T, S, C>,
|
|
147
|
+
): ReactNode => {
|
|
193
148
|
const {
|
|
149
|
+
component,
|
|
194
150
|
ref,
|
|
195
151
|
items,
|
|
196
|
-
|
|
152
|
+
sections,
|
|
197
153
|
renderItem,
|
|
198
154
|
renderListItem,
|
|
199
155
|
renderHeader,
|
|
200
156
|
selectedId,
|
|
201
157
|
onSelectionChanged,
|
|
202
158
|
...intrinsicProps
|
|
203
|
-
} = props
|
|
204
|
-
|
|
205
|
-
[key: string]: unknown;
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
impl: {
|
|
210
|
-
ref,
|
|
211
|
-
items: items as ListItem<T, S>[] | undefined,
|
|
212
|
-
model: model as Gio.ListModel | undefined,
|
|
213
|
-
renderItem: renderItem as ((value: T) => ReactNode) | null | undefined,
|
|
214
|
-
renderListItem: renderListItem as ((value: T) => ReactNode) | null | undefined,
|
|
215
|
-
renderHeader: renderHeader as ((value: S) => ReactNode) | null | undefined,
|
|
216
|
-
selectedId: selectedId as string | null | undefined,
|
|
217
|
-
onSelectionChanged: onSelectionChanged as ((id: string) => void) | null | undefined,
|
|
218
|
-
},
|
|
219
|
-
intrinsicProps: intrinsicProps as Record<string, unknown>,
|
|
220
|
-
};
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Props for the {@link DropDown} component: the raw `GtkDropDown` element
|
|
225
|
-
* surface with its factory/model wiring replaced by the declarative
|
|
226
|
-
* {@link DropDownProps} API.
|
|
227
|
-
*/
|
|
228
|
-
export type DropDownComponentProps<T = unknown, S = unknown> = Omit<GtkDropDownProps, keyof DropDownProps<T, S>> &
|
|
229
|
-
DropDownProps<T, S>;
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Props for the {@link ComboRow} component: the raw `AdwComboRow` element
|
|
233
|
-
* surface with its factory/model wiring replaced by the declarative
|
|
234
|
-
* {@link DropDownProps} API.
|
|
235
|
-
*/
|
|
236
|
-
export type ComboRowComponentProps<T = unknown, S = unknown> = Omit<AdwComboRowProps, keyof DropDownProps<T, S>> &
|
|
237
|
-
DropDownProps<T, S>;
|
|
159
|
+
} = props;
|
|
160
|
+
const Component: ElementType = component ?? GtkDropDown;
|
|
238
161
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return <DropDownImpl<T, S, Gtk.DropDown> element={GtkDropDown} intrinsicProps={intrinsicProps} {...impl} />;
|
|
248
|
-
};
|
|
162
|
+
const wiring = useDropDownWiring<T, S>({
|
|
163
|
+
ref,
|
|
164
|
+
items,
|
|
165
|
+
sections,
|
|
166
|
+
selectedId,
|
|
167
|
+
onSelectionChanged,
|
|
168
|
+
renderHeader,
|
|
169
|
+
});
|
|
249
170
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
171
|
+
return (
|
|
172
|
+
<>
|
|
173
|
+
<Component {...intrinsicProps} ref={wiring.setRef} />
|
|
174
|
+
<CellRenderHost
|
|
175
|
+
store={wiring.selectionStore}
|
|
176
|
+
resolver={wiring.selectionResolver}
|
|
177
|
+
render={toItemRenderer<T, S>(renderItem)}
|
|
178
|
+
/>
|
|
179
|
+
<CellRenderHost
|
|
180
|
+
store={wiring.listStore}
|
|
181
|
+
resolver={wiring.resolver}
|
|
182
|
+
render={toListRenderer<T, S>(renderListItem, renderItem)}
|
|
183
|
+
/>
|
|
184
|
+
<HeaderRenderHost
|
|
185
|
+
useHeader={wiring.useHeader}
|
|
186
|
+
store={wiring.headerStore}
|
|
187
|
+
resolver={wiring.headerResolver}
|
|
188
|
+
renderHeader={renderHeader}
|
|
189
|
+
/>
|
|
190
|
+
</>
|
|
191
|
+
);
|
|
259
192
|
};
|
package/src/fixed.tsx
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
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-view.tsx
CHANGED
|
@@ -1,73 +1,81 @@
|
|
|
1
|
-
import type * as Gio from "@gtkx/gi/gio";
|
|
2
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
3
2
|
import { GtkGridView, type GtkGridViewProps } from "@gtkx/jsx/gtk";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import type
|
|
7
|
-
import
|
|
3
|
+
import { useMergeRefs } from "@gtkx/react/internal";
|
|
4
|
+
import { type ReactNode, type Ref, useRef } from "react";
|
|
5
|
+
import { type CellRenderer, CellRenderHost, itemRenderer } from "./cell.js";
|
|
6
|
+
import { makeFactoryInstaller, useCellContainers } from "./hooks/use-cell-containers.js";
|
|
7
|
+
import { useCollectionModel } from "./hooks/use-collection-model.js";
|
|
8
|
+
import { useInstalledModel } from "./hooks/use-installed-model.js";
|
|
9
|
+
import type { CollectionItemSizeProps, ControlledSelectionProps, ItemNode, RenderItemProps } from "./types.js";
|
|
8
10
|
|
|
9
|
-
const
|
|
10
|
-
install: (widget: Gtk.GridView, factory: Gtk.SignalListItemFactory) => widget.setFactory(factory),
|
|
11
|
-
uninstall: (widget: Gtk.GridView) => widget.setFactory(null),
|
|
12
|
-
};
|
|
11
|
+
const factoryInstaller = makeFactoryInstaller<Gtk.GridView>((widget, factory) => widget.setFactory(factory));
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export type GridViewDeclarativeProps<T = unknown> = CollectionItemSizeProps &
|
|
14
|
+
ControlledSelectionProps & {
|
|
15
|
+
items?: ItemNode<T>[] | undefined;
|
|
16
|
+
renderItem: (props: RenderItemProps<T>) => ReactNode;
|
|
17
|
+
};
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Props for
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* Props for {@link GridView}. Combines the underlying Gtk.GridView props with
|
|
21
|
+
* declarative collection props: items, a per-cell renderItem, controlled selection,
|
|
22
|
+
* and estimated item sizing.
|
|
22
23
|
*/
|
|
23
|
-
export type
|
|
24
|
+
export type GridViewProps<T = unknown> = Omit<
|
|
25
|
+
GtkGridViewProps,
|
|
26
|
+
"model" | "factory" | keyof GridViewDeclarativeProps<T>
|
|
27
|
+
> &
|
|
28
|
+
GridViewDeclarativeProps<T>;
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* uncontrolled form.
|
|
31
|
+
* Renders a Gtk.GridView: a scrollable grid of uniformly sized cells backed by a
|
|
32
|
+
* collection model, with each cell drawn by renderItem.
|
|
29
33
|
*/
|
|
30
|
-
export const GridView = <T = unknown>(props:
|
|
34
|
+
export const GridView = <T = unknown>(props: GridViewProps<T>): ReactNode => {
|
|
31
35
|
const {
|
|
32
36
|
ref,
|
|
33
37
|
items,
|
|
34
|
-
model,
|
|
35
38
|
renderItem,
|
|
36
|
-
|
|
39
|
+
selectedIds,
|
|
37
40
|
selectionMode,
|
|
38
41
|
onSelectionChanged,
|
|
39
42
|
estimatedItemHeight,
|
|
40
43
|
estimatedItemWidth,
|
|
41
44
|
...intrinsicProps
|
|
42
|
-
} = props as
|
|
45
|
+
} = props as GridViewDeclarativeProps<T> & {
|
|
43
46
|
ref?: Ref<Gtk.GridView | null>;
|
|
44
47
|
estimatedItemHeight?: number;
|
|
45
48
|
estimatedItemWidth?: number;
|
|
46
49
|
[key: string]: unknown;
|
|
47
50
|
};
|
|
48
51
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const cellRenderer: CellRenderer<T, unknown> = itemRenderer<T, unknown>(renderItem);
|
|
53
|
+
|
|
54
|
+
const widgetRef = useRef<Gtk.GridView | null>(null);
|
|
55
|
+
const setRef = useMergeRefs<Gtk.GridView>(ref, widgetRef);
|
|
56
|
+
|
|
57
|
+
const collection = useCollectionModel<T, unknown>({
|
|
58
|
+
items,
|
|
59
|
+
sections: undefined,
|
|
60
|
+
selectionMode,
|
|
61
|
+
selectedIds,
|
|
62
|
+
onSelectionChanged,
|
|
63
|
+
renderHeader: undefined,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const itemStore = useCellContainers<Gtk.GridView>({
|
|
67
|
+
object: widgetRef,
|
|
68
|
+
installer: factoryInstaller,
|
|
69
|
+
estimatedHeight: estimatedItemHeight,
|
|
70
|
+
estimatedWidth: estimatedItemWidth,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
useInstalledModel(widgetRef, collection.installedModel, (widget, value) => widget.setModel(value));
|
|
52
74
|
|
|
53
75
|
return (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
items={model === undefined ? items : undefined}
|
|
59
|
-
model={model as Gio.ListModel | undefined}
|
|
60
|
-
renderItem={slotRenderer}
|
|
61
|
-
autoexpand={undefined}
|
|
62
|
-
renderHeader={undefined}
|
|
63
|
-
estimatedHeight={estimatedItemHeight}
|
|
64
|
-
estimatedWidth={estimatedItemWidth}
|
|
65
|
-
selected={selected}
|
|
66
|
-
selectionMode={selectionMode}
|
|
67
|
-
onSelectionChanged={onSelectionChanged}
|
|
68
|
-
factoryBinding={factoryBinding}
|
|
69
|
-
headerFactoryBinding={undefined}
|
|
70
|
-
modelBinding={modelBinding}
|
|
71
|
-
/>
|
|
76
|
+
<>
|
|
77
|
+
<GtkGridView {...intrinsicProps} ref={setRef} />
|
|
78
|
+
<CellRenderHost store={itemStore} resolver={collection.resolver} render={cellRenderer} />
|
|
79
|
+
</>
|
|
72
80
|
);
|
|
73
81
|
};
|