@gtkx/components 0.21.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +173 -0
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.d.ts.map +1 -0
- package/dist/adw/index.js +2 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/toast.d.ts +10 -0
- package/dist/adw/toast.d.ts.map +1 -0
- package/dist/adw/toast.js +51 -0
- package/dist/adw/toast.js.map +1 -0
- package/dist/adw/types.d.ts +28 -0
- package/dist/adw/types.d.ts.map +1 -0
- package/dist/adw/types.js +2 -0
- package/dist/adw/types.js.map +1 -0
- package/dist/column-view.d.ts +6 -14
- package/dist/column-view.d.ts.map +1 -1
- package/dist/column-view.js +98 -100
- package/dist/column-view.js.map +1 -1
- package/dist/drop-down.d.ts +8 -37
- package/dist/drop-down.d.ts.map +1 -1
- package/dist/drop-down.js +109 -118
- package/dist/drop-down.js.map +1 -1
- package/dist/grid-view.d.ts +4 -11
- package/dist/grid-view.d.ts.map +1 -1
- package/dist/grid-view.js +25 -19
- package/dist/grid-view.js.map +1 -1
- package/dist/index.d.ts +6 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -14
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +50 -0
- package/dist/internal/cells.d.ts.map +1 -0
- package/dist/internal/cells.js +185 -0
- package/dist/internal/cells.js.map +1 -0
- package/dist/internal/collection-index.d.ts +21 -0
- package/dist/internal/collection-index.d.ts.map +1 -0
- package/dist/internal/collection-index.js +106 -0
- package/dist/internal/collection-index.js.map +1 -0
- package/dist/internal/collection-model.d.ts +12 -0
- package/dist/internal/collection-model.d.ts.map +1 -0
- package/dist/internal/collection-model.js +164 -0
- package/dist/internal/collection-model.js.map +1 -0
- package/dist/internal/collection.d.ts +18 -0
- package/dist/internal/collection.d.ts.map +1 -0
- package/dist/internal/collection.js +64 -0
- package/dist/internal/collection.js.map +1 -0
- package/dist/internal/expansion.d.ts +9 -0
- package/dist/internal/expansion.d.ts.map +1 -0
- package/dist/internal/expansion.js +75 -0
- package/dist/internal/expansion.js.map +1 -0
- package/dist/internal/selection.d.ts +12 -0
- package/dist/internal/selection.d.ts.map +1 -0
- package/dist/internal/selection.js +99 -0
- package/dist/internal/selection.js.map +1 -0
- package/dist/internal/use-collection.d.ts +18 -0
- package/dist/internal/use-collection.d.ts.map +1 -0
- package/dist/internal/use-collection.js +42 -0
- package/dist/internal/use-collection.js.map +1 -0
- package/dist/internal/use-widget-ref.d.ts +4 -0
- package/dist/internal/use-widget-ref.d.ts.map +1 -0
- package/dist/internal/use-widget-ref.js +8 -0
- package/dist/internal/use-widget-ref.js.map +1 -0
- package/dist/list-view.d.ts +4 -11
- package/dist/list-view.d.ts.map +1 -1
- package/dist/list-view.js +32 -28
- package/dist/list-view.js.map +1 -1
- package/dist/size-group.d.ts +12 -0
- package/dist/size-group.d.ts.map +1 -0
- package/dist/size-group.js +41 -0
- package/dist/size-group.js.map +1 -0
- package/dist/types.d.ts +104 -161
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +62 -52
- package/src/adw/index.ts +2 -0
- package/src/adw/toast.tsx +74 -0
- package/src/adw/types.ts +32 -0
- package/src/column-view.tsx +184 -184
- package/src/drop-down.tsx +173 -227
- package/src/grid-view.tsx +29 -65
- package/src/index.ts +16 -25
- package/src/internal/cells.tsx +345 -0
- package/src/internal/collection-index.ts +168 -0
- package/src/internal/collection-model.ts +246 -0
- package/src/internal/collection.ts +104 -0
- package/src/internal/expansion.ts +123 -0
- package/src/internal/selection.tsx +159 -0
- package/src/internal/use-collection.tsx +70 -0
- package/src/internal/use-widget-ref.ts +11 -0
- package/src/list-view.tsx +45 -78
- package/src/size-group.tsx +77 -0
- package/src/types.ts +171 -191
- 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/column-view-column.d.ts +0 -17
- package/dist/column-view-column.d.ts.map +0 -1
- package/dist/column-view-column.js +0 -51
- package/dist/column-view-column.js.map +0 -1
- package/dist/constraint-layout-apply.d.ts +0 -6
- package/dist/constraint-layout-apply.d.ts.map +0 -1
- package/dist/constraint-layout-apply.js +0 -107
- package/dist/constraint-layout-apply.js.map +0 -1
- package/dist/constraint-layout.d.ts +0 -13
- package/dist/constraint-layout.d.ts.map +0 -1
- package/dist/constraint-layout.js +0 -85
- package/dist/constraint-layout.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-drop-down-selection.d.ts +0 -15
- package/dist/hooks/use-drop-down-selection.d.ts.map +0 -1
- package/dist/hooks/use-drop-down-selection.js +0 -35
- package/dist/hooks/use-drop-down-selection.js.map +0 -1
- package/dist/hooks/use-header-menu.d.ts +0 -4
- package/dist/hooks/use-header-menu.d.ts.map +0 -1
- package/dist/hooks/use-header-menu.js +0 -32
- package/dist/hooks/use-header-menu.js.map +0 -1
- package/dist/hooks/use-list-model.d.ts +0 -20
- package/dist/hooks/use-list-model.d.ts.map +0 -1
- package/dist/hooks/use-list-model.js +0 -70
- package/dist/hooks/use-list-model.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 +0 -66
- package/dist/hooks/use-realized-slots.js.map +0 -1
- package/dist/hooks/use-selection-model.d.ts +0 -12
- package/dist/hooks/use-selection-model.d.ts.map +0 -1
- package/dist/hooks/use-selection-model.js +0 -111
- package/dist/hooks/use-selection-model.js.map +0 -1
- package/dist/hooks/use-sort-handler.d.ts +0 -12
- package/dist/hooks/use-sort-handler.d.ts.map +0 -1
- package/dist/hooks/use-sort-handler.js +0 -87
- package/dist/hooks/use-sort-handler.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/menu-items.d.ts +0 -5
- package/dist/menu-items.d.ts.map +0 -1
- package/dist/menu-items.js +0 -48
- package/dist/menu-items.js.map +0 -1
- package/dist/menu.d.ts +0 -18
- package/dist/menu.d.ts.map +0 -1
- package/dist/menu.js +0 -26
- package/dist/menu.js.map +0 -1
- package/dist/utils/item-resolver.d.ts +0 -28
- package/dist/utils/item-resolver.d.ts.map +0 -1
- package/dist/utils/item-resolver.js +0 -111
- package/dist/utils/item-resolver.js.map +0 -1
- package/dist/utils/list-item-flatten.d.ts +0 -32
- package/dist/utils/list-item-flatten.d.ts.map +0 -1
- package/dist/utils/list-item-flatten.js +0 -88
- package/dist/utils/list-item-flatten.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 +0 -83
- package/dist/utils/position-only-model.js.map +0 -1
- package/dist/utils/realized-slot-store.d.ts +0 -30
- package/dist/utils/realized-slot-store.d.ts.map +0 -1
- package/dist/utils/realized-slot-store.js +0 -103
- package/dist/utils/realized-slot-store.js.map +0 -1
- package/src/collection-view.tsx +0 -125
- package/src/column-view-column.tsx +0 -73
- package/src/constraint-layout-apply.ts +0 -121
- package/src/constraint-layout.tsx +0 -130
- package/src/contexts/column-view-context.ts +0 -28
- package/src/hooks/use-drop-down-selection.ts +0 -53
- package/src/hooks/use-header-menu.ts +0 -33
- package/src/hooks/use-list-model.ts +0 -145
- package/src/hooks/use-model-installation.ts +0 -25
- package/src/hooks/use-realized-slots.ts +0 -93
- package/src/hooks/use-selection-model.ts +0 -121
- package/src/hooks/use-sort-handler.ts +0 -102
- package/src/list-portal-host.tsx +0 -27
- package/src/list-slot.tsx +0 -56
- package/src/menu-items.ts +0 -48
- package/src/menu.tsx +0 -34
- package/src/utils/item-resolver.ts +0 -143
- package/src/utils/list-item-flatten.ts +0 -113
- package/src/utils/position-only-model.ts +0 -115
- package/src/utils/realized-slot-store.ts +0 -126
package/src/drop-down.tsx
CHANGED
|
@@ -1,259 +1,205 @@
|
|
|
1
|
-
import type * as
|
|
2
|
-
import type
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export interface DropDownWidget extends Gtk.Widget {
|
|
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
|
-
}
|
|
1
|
+
import type * as GObject from "@gtkx/gi/gobject";
|
|
2
|
+
import type { ElementType, ReactNode, Ref, RefObject } from "react";
|
|
3
|
+
import { GtkDropDown, GtkLabel, GtkSignalListItemFactory } from "@gtkx/jsx/gtk";
|
|
4
|
+
import { useSignal } from "@gtkx/react";
|
|
5
|
+
import { omit } from "@gtkx/utils";
|
|
6
|
+
import { useEffectEvent, useLayoutEffect, useRef } from "react";
|
|
7
|
+
import type { Collection } from "./internal/collection.js";
|
|
8
|
+
import type { DropDownProps, ItemRenderer, RenderItemArgs } from "./types.js";
|
|
9
|
+
import { HeaderPortals, ItemPortals, useHeaderCells, useItemCells } from "./internal/cells.js";
|
|
10
|
+
import { useCollectionData } from "./internal/use-collection.js";
|
|
11
|
+
import { useWidgetRef } from "./internal/use-widget-ref.js";
|
|
12
|
+
|
|
13
|
+
type SelectableWidget = GObject.Object & { getSelected: () => number; selected: number };
|
|
14
|
+
|
|
15
|
+
type DropDownRuntimeProps = DropDownProps & {
|
|
16
|
+
component?: ElementType | undefined;
|
|
17
|
+
ref?: Ref<SelectableWidget | null> | undefined;
|
|
18
|
+
} & Record<string, unknown>;
|
|
19
|
+
|
|
20
|
+
type SelectionOptions = {
|
|
21
|
+
widget: SelectableWidget | null;
|
|
22
|
+
collection: Collection;
|
|
23
|
+
applying: RefObject<boolean>;
|
|
24
|
+
props: DropDownRuntimeProps;
|
|
25
|
+
};
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
type KnownSelection = {
|
|
28
|
+
known: { current: string | null };
|
|
29
|
+
onSelectionChanged: ((id: string) => void) | null | undefined;
|
|
39
30
|
};
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
type DropDownComponent = <T = unknown, S = unknown, C extends ElementType = typeof GtkDropDown>(
|
|
33
|
+
props: DropDownProps<T, S, C>,
|
|
34
|
+
) => ReactNode;
|
|
35
|
+
|
|
36
|
+
const DROP_DOWN_PROPS: string[] = [
|
|
37
|
+
"component",
|
|
38
|
+
"items",
|
|
39
|
+
"sections",
|
|
40
|
+
"selectedId",
|
|
41
|
+
"onSelectionChanged",
|
|
42
|
+
"renderItem",
|
|
43
|
+
"renderListItem",
|
|
44
|
+
"renderHeader",
|
|
45
|
+
"ref",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Renders a drop-down backed by a collection model, with customizable renderers for the
|
|
50
|
+
* collapsed display, popup rows, and popup section headers, and controlled selection.
|
|
51
|
+
* The backing widget defaults to GtkDropDown and is swappable through `component`.
|
|
52
|
+
*/
|
|
53
|
+
const DropDown: DropDownComponent = DropDownImpl as DropDownComponent;
|
|
54
|
+
|
|
55
|
+
const describeValue = (value: unknown): string => {
|
|
56
|
+
if (typeof value === "string") {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
return JSON.stringify(value);
|
|
62
|
+
} catch {
|
|
63
|
+
return String(value);
|
|
64
|
+
}
|
|
44
65
|
};
|
|
45
66
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
const defaultItemContent = (value: unknown): ReactNode =>
|
|
68
|
+
value == null ? null : <GtkLabel>{describeValue(value)}</GtkLabel>;
|
|
69
|
+
|
|
70
|
+
const defaultRenderItem = ({ item }: RenderItemArgs<unknown>): ReactNode => defaultItemContent(item);
|
|
71
|
+
|
|
72
|
+
const faceRenderer = (props: DropDownRuntimeProps): ItemRenderer<never> =>
|
|
73
|
+
props.renderItem ?? defaultRenderItem;
|
|
74
|
+
|
|
75
|
+
const listRenderer = (props: DropDownRuntimeProps): ItemRenderer<never> =>
|
|
76
|
+
props.renderListItem ?? props.renderItem ?? defaultRenderItem;
|
|
77
|
+
|
|
78
|
+
const resolvePosition = (
|
|
79
|
+
widget: SelectableWidget,
|
|
80
|
+
collection: Collection,
|
|
81
|
+
selectedId: string | null | undefined,
|
|
82
|
+
): number => {
|
|
83
|
+
const requested = selectedId == null ? -1 : collection.positionFor(selectedId);
|
|
84
|
+
|
|
85
|
+
return requested >= 0 ? requested : widget.getSelected();
|
|
49
86
|
};
|
|
50
87
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
88
|
+
const updateKnownSelection = (
|
|
89
|
+
tracker: KnownSelection,
|
|
90
|
+
effectiveId: string,
|
|
91
|
+
selectedId: string | null | undefined,
|
|
92
|
+
): void => {
|
|
93
|
+
const { known, onSelectionChanged } = tracker;
|
|
94
|
+
const expectedId = selectedId ?? known.current;
|
|
95
|
+
const isNew = effectiveId !== known.current;
|
|
96
|
+
known.current = effectiveId;
|
|
97
|
+
|
|
98
|
+
if (expectedId !== null && effectiveId !== expectedId && isNew) {
|
|
99
|
+
onSelectionChanged?.(effectiveId);
|
|
100
|
+
}
|
|
54
101
|
};
|
|
55
102
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
}
|
|
103
|
+
const reportKnownSelection = (tracker: KnownSelection, id: string | null): void => {
|
|
104
|
+
const { known, onSelectionChanged } = tracker;
|
|
105
|
+
|
|
106
|
+
if (id === null || id === known.current) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
68
109
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return (value, _treeRow, isHeader) => (isHeader ? null : renderItem(value as T));
|
|
110
|
+
known.current = id;
|
|
111
|
+
onSelectionChanged?.(id);
|
|
72
112
|
};
|
|
73
113
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
114
|
+
const reportSelectedNotify = (
|
|
115
|
+
options: SelectionOptions,
|
|
116
|
+
tracker: KnownSelection,
|
|
117
|
+
applying: RefObject<boolean>,
|
|
118
|
+
): void => {
|
|
119
|
+
const { widget, collection } = options;
|
|
120
|
+
|
|
121
|
+
if (widget === null || applying.current) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
reportKnownSelection(tracker, collection.idAt(widget.getSelected()));
|
|
81
126
|
};
|
|
82
127
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
128
|
+
const applySelectedPosition = (widget: SelectableWidget, position: number, applying: RefObject<boolean>): void => {
|
|
129
|
+
applying.current = true;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
widget.selected = position;
|
|
133
|
+
} finally {
|
|
134
|
+
applying.current = false;
|
|
135
|
+
}
|
|
86
136
|
};
|
|
87
137
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
headerResolver: ItemResolver<T, S>;
|
|
93
|
-
faceStore: RealizedSlotStore;
|
|
94
|
-
popupStore: RealizedSlotStore;
|
|
95
|
-
headerStore: RealizedSlotStore;
|
|
96
|
-
useHeader: boolean;
|
|
97
|
-
}
|
|
138
|
+
const useDropDownSelection = (options: SelectionOptions): void => {
|
|
139
|
+
const { widget, collection, applying } = options;
|
|
140
|
+
const { selectedId } = options.props;
|
|
141
|
+
const known = useRef<string | null>(null);
|
|
98
142
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
positionOf: (id) => resolver.positionOf(id),
|
|
102
|
-
idOf: (position) => resolver.idOf(position),
|
|
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) => {
|
|
112
|
-
widgetRef.current = value;
|
|
113
|
-
setWidget(value);
|
|
114
|
-
}, []);
|
|
115
|
-
const [, setRef] = useForwardedRef<W>(props.ref, captureWidget);
|
|
116
|
-
|
|
117
|
-
const externalModel = props.model;
|
|
118
|
-
const listModel = useListModel<T, S>(
|
|
119
|
-
externalModel === undefined ? { items: props.items ?? undefined } : { model: externalModel },
|
|
120
|
-
);
|
|
143
|
+
const syncKnownSelection = useEffectEvent((position: number): void => {
|
|
144
|
+
const effectiveId = collection.idAt(position);
|
|
121
145
|
|
|
122
|
-
|
|
146
|
+
if (effectiveId === null) {
|
|
147
|
+
known.current = null;
|
|
123
148
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const { onSelectionChanged } = options.props;
|
|
153
|
+
updateKnownSelection({ known, onSelectionChanged }, effectiveId, selectedId);
|
|
129
154
|
});
|
|
130
155
|
|
|
131
|
-
|
|
156
|
+
useLayoutEffect(() => {
|
|
157
|
+
if (widget === null) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
132
160
|
|
|
133
|
-
|
|
134
|
-
widget,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
onSelectionChanged: props.onSelectionChanged,
|
|
138
|
-
});
|
|
161
|
+
const position = resolvePosition(widget, collection, selectedId);
|
|
162
|
+
applySelectedPosition(widget, position, applying);
|
|
163
|
+
syncKnownSelection(position);
|
|
164
|
+
}, [widget, collection, selectedId, applying]);
|
|
139
165
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
: listModel.resolver.positionOf(props.selectedId);
|
|
144
|
-
const facePosition = controlledPosition >= 0 ? controlledPosition : selectedPosition < 0 ? 0 : selectedPosition;
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
setRef,
|
|
148
|
-
resolver: listModel.resolver,
|
|
149
|
-
faceResolver: createFaceResolver(listModel.resolver, facePosition),
|
|
150
|
-
headerResolver: listModel.headerResolver,
|
|
151
|
-
faceStore: face.store,
|
|
152
|
-
popupStore: popup.store,
|
|
153
|
-
headerStore: header.store,
|
|
154
|
-
useHeader,
|
|
155
|
-
};
|
|
166
|
+
useSignal(widget, "notify::selected", (): void => {
|
|
167
|
+
reportSelectedNotify(options, { known, onSelectionChanged: options.props.onSelectionChanged }, applying);
|
|
168
|
+
});
|
|
156
169
|
};
|
|
157
170
|
|
|
158
|
-
|
|
159
|
-
const {
|
|
160
|
-
|
|
161
|
-
const
|
|
171
|
+
function DropDownImpl(props: DropDownRuntimeProps): ReactNode {
|
|
172
|
+
const { component, items, sections, renderListItem, renderHeader, ref } = props;
|
|
173
|
+
const rest = omit(props, DROP_DOWN_PROPS);
|
|
174
|
+
const [widget, refCallback] = useWidgetRef<SelectableWidget>(ref);
|
|
175
|
+
const applying = useRef(false);
|
|
176
|
+
const collection = useCollectionData({ items, sections, applying });
|
|
177
|
+
const faceCells = useItemCells({ width: -1, height: -1 });
|
|
178
|
+
const listCells = useItemCells({ width: -1, height: -1 });
|
|
179
|
+
const headerCells = useHeaderCells({ width: -1, height: -1 });
|
|
180
|
+
useDropDownSelection({ widget, collection, applying, props });
|
|
181
|
+
const Component = component ?? GtkDropDown;
|
|
182
|
+
const hasHeader = typeof renderHeader === "function";
|
|
162
183
|
|
|
163
184
|
return (
|
|
164
185
|
<>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
<Component
|
|
187
|
+
ref={refCallback}
|
|
188
|
+
model={collection.model}
|
|
189
|
+
factory={<GtkSignalListItemFactory {...faceCells.handlers} />}
|
|
190
|
+
{...(renderListItem != null && {
|
|
191
|
+
listFactory: <GtkSignalListItemFactory {...listCells.handlers} />,
|
|
192
|
+
})}
|
|
193
|
+
{...(hasHeader && { headerFactory: <GtkSignalListItemFactory {...headerCells.handlers} /> })}
|
|
194
|
+
{...rest}
|
|
171
195
|
/>
|
|
172
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
/>
|
|
178
|
-
) : null}
|
|
196
|
+
<ItemPortals store={faceCells} render={faceRenderer(props)} collection={collection} />
|
|
197
|
+
{renderListItem != null && (
|
|
198
|
+
<ItemPortals store={listCells} render={listRenderer(props)} collection={collection} />
|
|
199
|
+
)}
|
|
200
|
+
{hasHeader && <HeaderPortals store={headerCells} render={renderHeader} collection={collection} />}
|
|
179
201
|
</>
|
|
180
202
|
);
|
|
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
|
-
} => {
|
|
193
|
-
const {
|
|
194
|
-
ref,
|
|
195
|
-
items,
|
|
196
|
-
model,
|
|
197
|
-
renderItem,
|
|
198
|
-
renderListItem,
|
|
199
|
-
renderHeader,
|
|
200
|
-
selectedId,
|
|
201
|
-
onSelectionChanged,
|
|
202
|
-
...intrinsicProps
|
|
203
|
-
} = props as DropDownProps<T, S> & {
|
|
204
|
-
ref?: Ref<W | null>;
|
|
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>;
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* A `GtkDropDown` driven by a declarative `items` model with a controlled
|
|
241
|
-
* `selectedId` and per-slot renderers for the selected face, the popup list,
|
|
242
|
-
* and section headers. Supplying an external `model` switches to the
|
|
243
|
-
* uncontrolled form.
|
|
244
|
-
*/
|
|
245
|
-
export const DropDown = <T = unknown, S = unknown>(props: DropDownComponentProps<T, S>): ReactNode => {
|
|
246
|
-
const { impl, intrinsicProps } = extractDropDownProps<T, S, Gtk.DropDown>(props);
|
|
247
|
-
return <DropDownImpl<T, S, Gtk.DropDown> element={GtkDropDown} intrinsicProps={intrinsicProps} {...impl} />;
|
|
248
|
-
};
|
|
203
|
+
}
|
|
249
204
|
|
|
250
|
-
|
|
251
|
-
* An `AdwComboRow` driven by a declarative `items` model with a controlled
|
|
252
|
-
* `selectedId` and per-slot renderers for the selected face, the popup list,
|
|
253
|
-
* and section headers. Supplying an external `model` switches to the
|
|
254
|
-
* uncontrolled form.
|
|
255
|
-
*/
|
|
256
|
-
export const ComboRow = <T = unknown, S = unknown>(props: ComboRowComponentProps<T, S>): ReactNode => {
|
|
257
|
-
const { impl, intrinsicProps } = extractDropDownProps<T, S, Adw.ComboRow>(props);
|
|
258
|
-
return <DropDownImpl<T, S, Adw.ComboRow> element={AdwComboRow} intrinsicProps={intrinsicProps} {...impl} />;
|
|
259
|
-
};
|
|
205
|
+
export { DropDown };
|
package/src/grid-view.tsx
CHANGED
|
@@ -1,73 +1,37 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type { ReactNode, Ref } from "react";
|
|
5
|
-
import { CollectionView, type ModelBinding } from "./collection-view.js";
|
|
6
|
-
import type { SlotRenderer } from "./list-slot.js";
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { GtkGridView, GtkSignalListItemFactory } from "@gtkx/jsx/gtk";
|
|
3
|
+
import { omit } from "@gtkx/utils";
|
|
7
4
|
import type { GridViewProps } from "./types.js";
|
|
5
|
+
import { ItemPortals, useItemCells } from "./internal/cells.js";
|
|
6
|
+
import { useCollection } from "./internal/use-collection.js";
|
|
8
7
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Props for the {@link GridView} component: the raw `GtkGridView` element
|
|
20
|
-
* surface with its factory/model wiring replaced by the declarative
|
|
21
|
-
* {@link GridViewProps} API.
|
|
22
|
-
*/
|
|
23
|
-
export type GridViewComponentProps<T = unknown> = Omit<GtkGridViewProps, keyof GridViewProps<T>> & GridViewProps<T>;
|
|
8
|
+
const GRID_VIEW_PROPS = [
|
|
9
|
+
"items",
|
|
10
|
+
"renderItem",
|
|
11
|
+
"selectedIds",
|
|
12
|
+
"onSelectionChanged",
|
|
13
|
+
"selectionMode",
|
|
14
|
+
"estimatedItemHeight",
|
|
15
|
+
"estimatedItemWidth",
|
|
16
|
+
] as const satisfies (keyof GridViewProps)[];
|
|
24
17
|
|
|
25
18
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* uncontrolled form.
|
|
19
|
+
* Renders a Gtk.GridView of uniform cells from declarative items, with per-cell
|
|
20
|
+
* rendering, controlled selection, and estimated item sizing.
|
|
29
21
|
*/
|
|
30
|
-
|
|
31
|
-
const {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
selected,
|
|
37
|
-
selectionMode,
|
|
38
|
-
onSelectionChanged,
|
|
39
|
-
estimatedItemHeight,
|
|
40
|
-
estimatedItemWidth,
|
|
41
|
-
...intrinsicProps
|
|
42
|
-
} = props as GridViewProps<T> & {
|
|
43
|
-
ref?: Ref<Gtk.GridView | null>;
|
|
44
|
-
estimatedItemHeight?: number;
|
|
45
|
-
estimatedItemWidth?: number;
|
|
46
|
-
[key: string]: unknown;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const renderItemFn = renderItem as (item: T) => ReactNode;
|
|
50
|
-
const slotRenderer: SlotRenderer<T, unknown> = (value, _treeRow, isHeader) =>
|
|
51
|
-
isHeader ? null : renderItemFn(value as T);
|
|
22
|
+
function GridView<T = unknown>(props: GridViewProps<T>): ReactNode {
|
|
23
|
+
const { renderItem, estimatedItemHeight, estimatedItemWidth } = props;
|
|
24
|
+
const rest = omit(props, GRID_VIEW_PROPS);
|
|
25
|
+
const size = { width: estimatedItemWidth ?? -1, height: estimatedItemHeight ?? -1 };
|
|
26
|
+
const { collection, selection } = useCollection({ ...props, flat: true });
|
|
27
|
+
const itemCells = useItemCells(size);
|
|
52
28
|
|
|
53
29
|
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
|
-
/>
|
|
30
|
+
<>
|
|
31
|
+
<GtkGridView model={selection} factory={<GtkSignalListItemFactory {...itemCells.handlers} />} {...rest} />
|
|
32
|
+
<ItemPortals store={itemCells} render={renderItem} collection={collection} />
|
|
33
|
+
</>
|
|
72
34
|
);
|
|
73
|
-
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { GridView };
|
package/src/index.ts
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* under an unprefixed name to distinguish it from the raw `Gtk*`/`Adw*`/`G*`
|
|
7
|
-
* intrinsic element it builds upon.
|
|
8
|
-
*
|
|
9
|
-
* @packageDocumentation
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export { ColumnView, type ColumnViewComponentProps } from "./column-view.js";
|
|
13
|
-
export { ColumnViewColumn, type ColumnViewColumnComponentProps } from "./column-view-column.js";
|
|
14
|
-
export { ConstraintLayout, type ConstraintLayoutProps } from "./constraint-layout.js";
|
|
15
|
-
export { ComboRow, type ComboRowComponentProps, DropDown, type DropDownComponentProps } from "./drop-down.js";
|
|
16
|
-
export { GridView, type GridViewComponentProps } from "./grid-view.js";
|
|
17
|
-
export { ListView, type ListViewComponentProps } from "./list-view.js";
|
|
18
|
-
export { Menu, type MenuProps } from "./menu.js";
|
|
1
|
+
export { ColumnView } from "./column-view.js";
|
|
2
|
+
export { DropDown } from "./drop-down.js";
|
|
3
|
+
export { GridView } from "./grid-view.js";
|
|
4
|
+
export { ListView } from "./list-view.js";
|
|
5
|
+
export { SizeGroup } from "./size-group.js";
|
|
19
6
|
export type {
|
|
20
|
-
|
|
7
|
+
ChildProps,
|
|
8
|
+
Column,
|
|
21
9
|
ColumnViewProps,
|
|
22
|
-
ConstraintGuideProps,
|
|
23
|
-
ConstraintProps,
|
|
24
|
-
ConstraintVflProps,
|
|
25
10
|
DropDownProps,
|
|
26
11
|
GridViewProps,
|
|
27
|
-
|
|
12
|
+
HeaderRenderer,
|
|
13
|
+
Item,
|
|
14
|
+
ItemRenderer,
|
|
28
15
|
ListViewProps,
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
RenderHeaderArgs,
|
|
17
|
+
RenderItemArgs,
|
|
18
|
+
Section,
|
|
19
|
+
SizeGroupChildProps,
|
|
20
|
+
SizeGroupProps,
|
|
21
|
+
WidgetProps,
|
|
31
22
|
} from "./types.js";
|