@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
|
@@ -2,107 +2,131 @@ import type * as Gio from "@gtkx/gi/gio";
|
|
|
2
2
|
import type * as GObject from "@gtkx/gi/gobject";
|
|
3
3
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
4
4
|
import { useLayoutEffect, useMemo, useRef } from "react";
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
7
|
-
createControlledResolver,
|
|
8
|
-
createModelResolver,
|
|
9
|
-
createSectionHeaderResolver,
|
|
10
|
-
type ItemResolver,
|
|
11
|
-
type RowValue,
|
|
12
|
-
} from "../utils/item-resolver.js";
|
|
13
|
-
import { detectStructure, type ListStructure, structuralSignature } from "../utils/list-item-flatten.js";
|
|
5
|
+
import type { ItemNode, SectionNode } from "../types.js";
|
|
14
6
|
import {
|
|
15
7
|
createFlatModel,
|
|
16
8
|
createSectionModel,
|
|
17
9
|
createTreeModel,
|
|
18
10
|
resizeFlatModel,
|
|
19
11
|
retagRows,
|
|
20
|
-
} from "../utils/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
} from "../utils/item-models.js";
|
|
13
|
+
import {
|
|
14
|
+
createItemResolver,
|
|
15
|
+
createSectionHeaderResolver,
|
|
16
|
+
createTreeResolver,
|
|
17
|
+
type ItemResolver,
|
|
18
|
+
type RowValue,
|
|
19
|
+
} from "../utils/item-resolver.js";
|
|
20
|
+
import { detectStructure, type ListStructure, structuralSignature } from "../utils/list-item-flatten.js";
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
22
|
+
type ListModelInput<T, S> = {
|
|
23
|
+
items: ItemNode<T>[] | undefined;
|
|
24
|
+
sections?: SectionNode<S, T>[] | undefined;
|
|
25
|
+
};
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
type ListModelResult<T, S> = {
|
|
35
28
|
model: Gio.ListModel;
|
|
29
|
+
treeModel: Gtk.TreeListModel | null;
|
|
36
30
|
resolver: ItemResolver<T, S>;
|
|
37
31
|
headerResolver: ItemResolver<T, S>;
|
|
38
|
-
|
|
32
|
+
rowValues: WeakMap<GObject.Object, RowValue<T>>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type ListModelStructure = "sections" | ListStructure;
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
type ListModelState<T, S> = {
|
|
41
38
|
model: Gio.ListModel;
|
|
39
|
+
treeModel: Gtk.TreeListModel | null;
|
|
42
40
|
flatModel: Gtk.StringList | undefined;
|
|
43
|
-
items:
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
items: ItemNode<T>[] | undefined;
|
|
42
|
+
sections: SectionNode<S, T>[] | undefined;
|
|
43
|
+
structure: ListModelStructure;
|
|
46
44
|
signature: string;
|
|
47
|
-
rowValues: WeakMap<GObject.Object, RowValue<T
|
|
45
|
+
rowValues: WeakMap<GObject.Object, RowValue<T>>;
|
|
48
46
|
placeholdersById: Map<string, GObject.Object>;
|
|
49
|
-
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const sectionRows = <T, S>(sections: SectionNode<S, T>[] | undefined): ItemNode<T>[] => {
|
|
50
|
+
if (sections === undefined) return [];
|
|
51
|
+
const rows: ItemNode<T>[] = [];
|
|
52
|
+
for (const section of sections) rows.push(...section.data);
|
|
53
|
+
return rows;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const sectionSignature = <T, S>(sections: SectionNode<S, T>[] | undefined): string => {
|
|
57
|
+
if (sections === undefined) return "";
|
|
58
|
+
const parts: string[] = [];
|
|
59
|
+
for (const section of sections) {
|
|
60
|
+
parts.push(`${section.id}{${structuralSignature(section.data)}}`);
|
|
61
|
+
}
|
|
62
|
+
return parts.join(",");
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const signatureOf = <T, S>(
|
|
66
|
+
structure: ListModelStructure,
|
|
67
|
+
items: ItemNode<T>[] | undefined,
|
|
68
|
+
sections: SectionNode<S, T>[] | undefined,
|
|
69
|
+
): string => {
|
|
70
|
+
if (structure === "sections") return `sections|${sectionSignature(sections)}`;
|
|
71
|
+
return structure === "flat" ? "" : `${structure}|${structuralSignature(items)}`;
|
|
72
|
+
};
|
|
50
73
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
type ListModelInternalInput<T, S> = {
|
|
75
|
+
items: ItemNode<T>[] | undefined;
|
|
76
|
+
sections: SectionNode<S, T>[] | undefined;
|
|
77
|
+
structure: ListModelStructure;
|
|
78
|
+
signature: string;
|
|
79
|
+
};
|
|
56
80
|
|
|
57
|
-
const
|
|
58
|
-
items
|
|
59
|
-
|
|
60
|
-
structure: ListStructure,
|
|
61
|
-
signature: string,
|
|
62
|
-
): ControlledState<T, S> => {
|
|
63
|
-
const rowValues = new WeakMap<GObject.Object, RowValue<T, S>>();
|
|
81
|
+
const buildState = <T, S>(input: ListModelInternalInput<T, S>): ListModelState<T, S> => {
|
|
82
|
+
const { items, sections, structure, signature } = input;
|
|
83
|
+
const rowValues = new WeakMap<GObject.Object, RowValue<T>>();
|
|
64
84
|
const placeholdersById = new Map<string, GObject.Object>();
|
|
65
85
|
let model: Gio.ListModel;
|
|
86
|
+
let treeModel: Gtk.TreeListModel | null = null;
|
|
66
87
|
let flatModel: Gtk.StringList | undefined;
|
|
67
88
|
if (structure === "sections") {
|
|
68
|
-
model = createSectionModel(
|
|
89
|
+
model = createSectionModel(sections ?? []);
|
|
69
90
|
} else if (structure === "tree") {
|
|
70
|
-
|
|
91
|
+
treeModel = createTreeModel(items ?? [], rowValues, placeholdersById);
|
|
92
|
+
model = treeModel;
|
|
71
93
|
} else {
|
|
72
94
|
flatModel = createFlatModel(items?.length ?? 0);
|
|
73
95
|
model = flatModel;
|
|
74
96
|
}
|
|
75
|
-
return { model, flatModel, items,
|
|
97
|
+
return { model, treeModel, flatModel, items, sections, structure, signature, rowValues, placeholdersById };
|
|
76
98
|
};
|
|
77
99
|
|
|
78
|
-
const
|
|
79
|
-
prev:
|
|
80
|
-
items:
|
|
81
|
-
|
|
82
|
-
):
|
|
83
|
-
if (prev !== null && prev.items === items && prev.
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
const resolveState = <T, S>(
|
|
101
|
+
prev: ListModelState<T, S> | null,
|
|
102
|
+
items: ItemNode<T>[] | undefined,
|
|
103
|
+
sections: SectionNode<S, T>[] | undefined,
|
|
104
|
+
): ListModelState<T, S> => {
|
|
105
|
+
if (prev !== null && prev.items === items && prev.sections === sections) {
|
|
106
|
+
return prev;
|
|
107
|
+
}
|
|
108
|
+
const structure: ListModelStructure = sections !== undefined ? "sections" : detectStructure(items);
|
|
109
|
+
const signature = signatureOf(structure, items, sections);
|
|
110
|
+
const input: ListModelInternalInput<T, S> = { items, sections, structure, signature };
|
|
86
111
|
if (prev === null || prev.structure !== structure) {
|
|
87
|
-
return
|
|
112
|
+
return buildState(input);
|
|
88
113
|
}
|
|
89
114
|
if (structure !== "flat" && prev.signature !== signature) {
|
|
90
|
-
return
|
|
115
|
+
return buildState(input);
|
|
91
116
|
}
|
|
92
|
-
if (structure
|
|
117
|
+
if (structure === "tree") {
|
|
93
118
|
retagRows(items ?? [], prev.rowValues, prev.placeholdersById);
|
|
94
119
|
}
|
|
95
120
|
prev.items = items;
|
|
96
|
-
prev.
|
|
121
|
+
prev.sections = sections;
|
|
97
122
|
prev.signature = signature;
|
|
98
123
|
return prev;
|
|
99
124
|
};
|
|
100
125
|
|
|
101
|
-
const
|
|
102
|
-
const { items } =
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const state = resolveControlledState(stateRef.current, items, autoexpand);
|
|
126
|
+
export const useListModel = <T, S>(input: ListModelInput<T, S>): ListModelResult<T, S> => {
|
|
127
|
+
const { items, sections } = input;
|
|
128
|
+
const stateRef = useRef<ListModelState<T, S> | null>(null);
|
|
129
|
+
const state = resolveState(stateRef.current, items, sections);
|
|
106
130
|
stateRef.current = state;
|
|
107
131
|
const { structure, flatModel } = state;
|
|
108
132
|
|
|
@@ -112,34 +136,26 @@ const useControlledModel = <T, S>(mode: ControlledListMode<T, S>): ListModelResu
|
|
|
112
136
|
}
|
|
113
137
|
}, [flatModel, items]);
|
|
114
138
|
|
|
115
|
-
const resolver = useMemo(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
139
|
+
const resolver = useMemo(() => {
|
|
140
|
+
if (structure === "sections") {
|
|
141
|
+
return createItemResolver<T, S>(sectionRows(sections), true, state.rowValues);
|
|
142
|
+
}
|
|
143
|
+
if (structure === "tree" && state.treeModel !== null) {
|
|
144
|
+
return createTreeResolver<T, S>(items, state.rowValues, state.treeModel);
|
|
145
|
+
}
|
|
146
|
+
return createItemResolver<T, S>(items, false, state.rowValues);
|
|
147
|
+
}, [items, sections, structure, state.rowValues, state.treeModel]);
|
|
119
148
|
|
|
120
149
|
const headerResolver = useMemo(
|
|
121
|
-
() => createSectionHeaderResolver(structure === "sections" ?
|
|
122
|
-
[
|
|
150
|
+
() => createSectionHeaderResolver<T, S>(structure === "sections" ? sections : undefined),
|
|
151
|
+
[sections, structure],
|
|
123
152
|
);
|
|
124
153
|
|
|
125
|
-
return {
|
|
154
|
+
return {
|
|
155
|
+
model: state.model,
|
|
156
|
+
treeModel: state.treeModel,
|
|
157
|
+
resolver,
|
|
158
|
+
headerResolver,
|
|
159
|
+
rowValues: state.rowValues,
|
|
160
|
+
};
|
|
126
161
|
};
|
|
127
|
-
|
|
128
|
-
const useUncontrolledModel = <T, S>(mode: UncontrolledListMode): ListModelResult<T, S> => {
|
|
129
|
-
const { model } = mode;
|
|
130
|
-
const resolver = useMemo<ItemResolver<T, S>>(() => createModelResolver(model), [model]);
|
|
131
|
-
const headerResolver = useMemo<ItemResolver<T, S>>(() => createSectionHeaderResolver<T, S>(undefined), []);
|
|
132
|
-
return { model, resolver, headerResolver };
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
export const useListModel = <T, S>(mode: ControlledListMode<T, S> | UncontrolledListMode): ListModelResult<T, S> => {
|
|
136
|
-
const controlled = useControlledModel<T, S>(
|
|
137
|
-
mode.model === undefined ? { items: mode.items, autoexpand: mode.autoexpand } : { items: [] },
|
|
138
|
-
);
|
|
139
|
-
const uncontrolled = useUncontrolledModel<T, S>(
|
|
140
|
-
mode.model === undefined ? { model: EMPTY_MODEL } : { model: mode.model },
|
|
141
|
-
);
|
|
142
|
-
return mode.model === undefined ? controlled : uncontrolled;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const EMPTY_MODEL: Gio.ListModel = createFlatModel(0);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type * as GObject from "@gtkx/gi/gobject";
|
|
2
|
+
import { createPortal, rootElement } from "@gtkx/react";
|
|
3
|
+
import { useMergeRefs } from "@gtkx/react/internal";
|
|
4
|
+
import {
|
|
5
|
+
type Context,
|
|
6
|
+
createContext,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
type Ref,
|
|
9
|
+
type RefCallback,
|
|
10
|
+
type RefObject,
|
|
11
|
+
useCallback,
|
|
12
|
+
useContext,
|
|
13
|
+
useLayoutEffect,
|
|
14
|
+
useRef,
|
|
15
|
+
} from "react";
|
|
16
|
+
|
|
17
|
+
export type ParentContext<T> = {
|
|
18
|
+
Context: Context<RefObject<T | null> | null>;
|
|
19
|
+
useParentRef: () => RefObject<T | null>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const createParentContext = <T>(orphanMessage: string): ParentContext<T> => {
|
|
23
|
+
const context = createContext<RefObject<T | null> | null>(null);
|
|
24
|
+
return {
|
|
25
|
+
Context: context,
|
|
26
|
+
useParentRef: (): RefObject<T | null> => {
|
|
27
|
+
const ref = useContext(context);
|
|
28
|
+
if (!ref) throw new Error(orphanMessage);
|
|
29
|
+
return ref;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type PlacedChildRender<T> = (ref: RefCallback<T>) => ReactNode;
|
|
35
|
+
|
|
36
|
+
export type PlacedChildOptions<T extends GObject.Object, P> = {
|
|
37
|
+
render: PlacedChildRender<T>;
|
|
38
|
+
ref?: Ref<T | null> | undefined;
|
|
39
|
+
placement: P;
|
|
40
|
+
samePlacement: (a: P, b: P) => boolean;
|
|
41
|
+
place: (object: T, placement: P, previous: P | undefined) => void;
|
|
42
|
+
release: (object: T) => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const usePlacedChild = <T extends GObject.Object, P>(options: PlacedChildOptions<T, P>): ReactNode => {
|
|
46
|
+
const { render, ref, placement, samePlacement, place, release } = options;
|
|
47
|
+
const objectRef = useRef<T | null>(null);
|
|
48
|
+
const appliedRef = useRef<{ object: T; placement: P } | null>(null);
|
|
49
|
+
const captureObject = useCallback<RefCallback<T>>((object) => {
|
|
50
|
+
objectRef.current = object;
|
|
51
|
+
}, []);
|
|
52
|
+
const setObjectRef = useMergeRefs<T>(ref, captureObject);
|
|
53
|
+
|
|
54
|
+
useLayoutEffect(() => {
|
|
55
|
+
const object = objectRef.current;
|
|
56
|
+
const applied = appliedRef.current;
|
|
57
|
+
if (applied !== null && applied.object !== object) {
|
|
58
|
+
release(applied.object);
|
|
59
|
+
appliedRef.current = null;
|
|
60
|
+
}
|
|
61
|
+
if (object === null) return;
|
|
62
|
+
const current = appliedRef.current;
|
|
63
|
+
if (current === null || !samePlacement(current.placement, placement)) {
|
|
64
|
+
place(object, placement, current?.placement);
|
|
65
|
+
appliedRef.current = { object, placement };
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
useLayoutEffect(
|
|
70
|
+
() => () => {
|
|
71
|
+
const applied = appliedRef.current;
|
|
72
|
+
if (applied !== null) release(applied.object);
|
|
73
|
+
appliedRef.current = null;
|
|
74
|
+
},
|
|
75
|
+
[],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return createPortal(render(setObjectRef), rootElement);
|
|
79
|
+
};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type * as Gio from "@gtkx/gi/gio";
|
|
2
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
3
|
import { useSignal } from "@gtkx/react";
|
|
4
|
+
import { isSameArray } from "@gtkx/utils";
|
|
4
5
|
import { useLayoutEffect, useRef } from "react";
|
|
5
6
|
import type { ItemResolver } from "../utils/item-resolver.js";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
type SelectionModelOptions<T, S> = {
|
|
8
9
|
base: Gio.ListModel;
|
|
9
10
|
selectionMode: Gtk.SelectionMode | null | undefined;
|
|
10
|
-
|
|
11
|
+
selectedIds: string[] | null | undefined;
|
|
11
12
|
onSelectionChanged: ((ids: string[]) => void) | null | undefined;
|
|
12
13
|
resolver: ItemResolver<T, S>;
|
|
13
|
-
}
|
|
14
|
+
};
|
|
14
15
|
|
|
15
16
|
type WrappingSelectionModel = (Gtk.SingleSelection | Gtk.MultiSelection | Gtk.NoSelection) & Gtk.SelectionModel;
|
|
16
17
|
|
|
@@ -36,7 +37,7 @@ const applySelectedPositions = (model: Gtk.SelectionModel, positions: number[]):
|
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
const requested = bitsetOf(positions);
|
|
39
|
-
const changed = model.getSelection();
|
|
40
|
+
const changed = model.getSelection().copy();
|
|
40
41
|
changed.difference(requested);
|
|
41
42
|
if (changed.isEmpty()) return;
|
|
42
43
|
model.setSelection(requested, changed);
|
|
@@ -66,22 +67,14 @@ const idsToPositions = <T, S>(ids: string[] | null | undefined, resolver: ItemRe
|
|
|
66
67
|
if (ids === undefined || ids === null) return [];
|
|
67
68
|
const positions: number[] = [];
|
|
68
69
|
for (const id of ids) {
|
|
69
|
-
const position = resolver.
|
|
70
|
+
const position = resolver.positionOfId(id);
|
|
70
71
|
if (position >= 0) positions.push(position);
|
|
71
72
|
}
|
|
72
73
|
return positions;
|
|
73
74
|
};
|
|
74
75
|
|
|
75
|
-
const sameIds = (a: string[], b: string[]): boolean => {
|
|
76
|
-
if (a.length !== b.length) return false;
|
|
77
|
-
for (let index = 0; index < a.length; index++) {
|
|
78
|
-
if (a[index] !== b[index]) return false;
|
|
79
|
-
}
|
|
80
|
-
return true;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
76
|
export const useSelectionModel = <T, S>(options: SelectionModelOptions<T, S>): Gtk.SelectionModel => {
|
|
84
|
-
const { base, selectionMode,
|
|
77
|
+
const { base, selectionMode, selectedIds, onSelectionChanged, resolver } = options;
|
|
85
78
|
const mode = selectionMode ?? Gtk.SelectionMode.SINGLE;
|
|
86
79
|
|
|
87
80
|
const modelRef = useRef<WrappingSelectionModel | null>(null);
|
|
@@ -102,7 +95,7 @@ export const useSelectionModel = <T, S>(options: SelectionModelOptions<T, S>): G
|
|
|
102
95
|
const callback = onChangedRef.current;
|
|
103
96
|
if (!callback) return;
|
|
104
97
|
const ids = readSelectedIds(model, resolverRef.current);
|
|
105
|
-
if (lastReportedRef.current !== null &&
|
|
98
|
+
if (lastReportedRef.current !== null && isSameArray(lastReportedRef.current, ids)) return;
|
|
106
99
|
lastReportedRef.current = ids;
|
|
107
100
|
callback(ids);
|
|
108
101
|
};
|
|
@@ -111,11 +104,11 @@ export const useSelectionModel = <T, S>(options: SelectionModelOptions<T, S>): G
|
|
|
111
104
|
|
|
112
105
|
useLayoutEffect(() => {
|
|
113
106
|
if (model.getModel() !== base) model.setModel(base);
|
|
114
|
-
if (
|
|
115
|
-
applySelectedPositions(model, idsToPositions(
|
|
107
|
+
if (selectedIds !== undefined && selectedIds !== null) {
|
|
108
|
+
applySelectedPositions(model, idsToPositions(selectedIds, resolver));
|
|
116
109
|
}
|
|
117
110
|
report();
|
|
118
|
-
}, [model, base,
|
|
111
|
+
}, [model, base, selectedIds, resolver]);
|
|
119
112
|
|
|
120
113
|
return model;
|
|
121
114
|
};
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
-
import { type
|
|
2
|
+
import { type ObjectProp, resolveObjectProp } from "@gtkx/react/internal";
|
|
3
3
|
import { useLayoutEffect, useRef } from "react";
|
|
4
|
-
import type { ColumnRegistration } from "../contexts/column-view-context.js";
|
|
5
4
|
|
|
6
|
-
export
|
|
7
|
-
|
|
5
|
+
export type ColumnRegistration = {
|
|
6
|
+
id: string;
|
|
7
|
+
column: Gtk.ColumnViewColumn;
|
|
8
|
+
sortable: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type SortHandlerOptions = {
|
|
12
|
+
columnView: ObjectProp<Gtk.ColumnView>;
|
|
8
13
|
sortColumn: string | null | undefined;
|
|
9
14
|
sortOrder: Gtk.SortType | null | undefined;
|
|
10
15
|
onSortChanged: ((column: string | null, order: Gtk.SortType) => void) | null | undefined;
|
|
11
16
|
columns: ColumnRegistration[];
|
|
12
|
-
}
|
|
17
|
+
};
|
|
13
18
|
|
|
14
19
|
const neutralSorter = (): Gtk.CustomSorter => Gtk.CustomSorter.new(() => 0);
|
|
15
20
|
|
|
@@ -46,10 +51,10 @@ const primarySort = (sorter: Gtk.Sorter): { column: Gtk.ColumnViewColumn | null;
|
|
|
46
51
|
? { column: sorter.getPrimarySortColumn(), order: sorter.getPrimarySortOrder() }
|
|
47
52
|
: { column: null, order: Gtk.SortType.ASCENDING };
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
type SorterSubscription = {
|
|
50
55
|
sorter: Gtk.Sorter | null;
|
|
51
56
|
handler: ((change: Gtk.SorterChange) => void) | null;
|
|
52
|
-
}
|
|
57
|
+
};
|
|
53
58
|
|
|
54
59
|
const disconnectSorter = (subscription: SorterSubscription): void => {
|
|
55
60
|
if (subscription.sorter !== null && subscription.handler !== null) {
|
|
@@ -89,7 +94,7 @@ export const useSortHandler = (options: SortHandlerOptions): void => {
|
|
|
89
94
|
};
|
|
90
95
|
|
|
91
96
|
useLayoutEffect(() => {
|
|
92
|
-
const view =
|
|
97
|
+
const view = resolveObjectProp(columnView);
|
|
93
98
|
if (view === null) return;
|
|
94
99
|
ensureColumnSorters(columns, activeId);
|
|
95
100
|
suppressRef.current = true;
|
package/src/index.ts
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* @packageDocumentation
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export { ColumnView, type ColumnViewComponentProps } from "./column-view.js";
|
|
13
|
-
export { ColumnViewColumn, type ColumnViewColumnComponentProps } from "./column-view-column.js";
|
|
1
|
+
export {
|
|
2
|
+
type ColumnDef,
|
|
3
|
+
type ColumnDefDeclarativeProps,
|
|
4
|
+
ColumnView,
|
|
5
|
+
type ColumnViewDeclarativeProps,
|
|
6
|
+
type ColumnViewProps,
|
|
7
|
+
type ColumnViewSortProps,
|
|
8
|
+
} from "./column-view.js";
|
|
14
9
|
export { ConstraintLayout, type ConstraintLayoutProps } from "./constraint-layout.js";
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
10
|
+
export type { ConstraintGuideProps, ConstraintProps, ConstraintVflProps } from "./constraint-layout-apply.js";
|
|
11
|
+
export { DropDown, type DropDownDeclarativeProps, type DropDownItemRenderer, type DropDownProps } from "./drop-down.js";
|
|
12
|
+
export { Fixed, type FixedChildProps, type FixedPlacementProps, type FixedProps } from "./fixed.js";
|
|
13
|
+
export { Grid, type GridChildProps, type GridPlacement, type GridProps } from "./grid.js";
|
|
14
|
+
export { GridView, type GridViewDeclarativeProps, type GridViewProps } from "./grid-view.js";
|
|
15
|
+
export { ListView, type ListViewDeclarativeProps, type ListViewProps } from "./list-view.js";
|
|
16
|
+
export { Menu, type MenuItemsProps, type MenuProps } from "./menu.js";
|
|
17
|
+
export { Overlay, type OverlayChildProps, type OverlayPlacementProps, type OverlayProps } from "./overlay.js";
|
|
18
|
+
export { SizeGroup, type SizeGroupChildProps, type SizeGroupProps } from "./size-group.js";
|
|
19
19
|
export type {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
DropDownProps,
|
|
26
|
-
GridViewProps,
|
|
27
|
-
ListItem,
|
|
28
|
-
ListViewProps,
|
|
20
|
+
ChildProps,
|
|
21
|
+
CollectionItemSizeProps,
|
|
22
|
+
ControlledExpansionProps,
|
|
23
|
+
ControlledSelectionProps,
|
|
24
|
+
ItemNode,
|
|
29
25
|
MenuEntry,
|
|
30
|
-
|
|
26
|
+
RenderItemProps,
|
|
27
|
+
SectionNode,
|
|
28
|
+
WidgetProps,
|
|
31
29
|
} from "./types.js";
|