@gtkx/components 2.0.0-beta.12 → 2.0.0-beta.13
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/README.md +1 -1
- package/dist/column-view.d.ts.map +1 -1
- package/dist/column-view.js +7 -3
- package/dist/column-view.js.map +1 -1
- package/dist/combo-row.d.ts.map +1 -1
- package/dist/combo-row.js +3 -1
- package/dist/combo-row.js.map +1 -1
- package/dist/grid-view.d.ts.map +1 -1
- package/dist/grid-view.js +3 -2
- package/dist/grid-view.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +5 -2
- package/dist/internal/cells.d.ts.map +1 -1
- package/dist/internal/cells.js +86 -43
- package/dist/internal/cells.js.map +1 -1
- package/dist/internal/collection-index.d.ts +3 -5
- package/dist/internal/collection-index.d.ts.map +1 -1
- package/dist/internal/collection-index.js +3 -7
- package/dist/internal/collection-index.js.map +1 -1
- package/dist/internal/collection-model.d.ts +15 -4
- package/dist/internal/collection-model.d.ts.map +1 -1
- package/dist/internal/collection-model.js +154 -73
- package/dist/internal/collection-model.js.map +1 -1
- package/dist/internal/collection.d.ts +3 -3
- package/dist/internal/collection.d.ts.map +1 -1
- package/dist/internal/collection.js +11 -9
- package/dist/internal/collection.js.map +1 -1
- package/dist/internal/controlled-sync.d.ts +2 -1
- package/dist/internal/controlled-sync.d.ts.map +1 -1
- package/dist/internal/controlled-sync.js +6 -1
- package/dist/internal/controlled-sync.js.map +1 -1
- package/dist/internal/drop-down.d.ts +10 -4
- package/dist/internal/drop-down.d.ts.map +1 -1
- package/dist/internal/drop-down.js +60 -8
- package/dist/internal/drop-down.js.map +1 -1
- package/dist/internal/expansion.d.ts.map +1 -1
- package/dist/internal/expansion.js +46 -19
- package/dist/internal/expansion.js.map +1 -1
- package/dist/internal/first-header.d.ts +11 -0
- package/dist/internal/first-header.d.ts.map +1 -0
- package/dist/internal/first-header.js +21 -0
- package/dist/internal/first-header.js.map +1 -0
- package/dist/internal/level-splice.d.ts +27 -0
- package/dist/internal/level-splice.d.ts.map +1 -0
- package/dist/internal/level-splice.js +98 -0
- package/dist/internal/level-splice.js.map +1 -0
- package/dist/internal/selection.d.ts.map +1 -1
- package/dist/internal/selection.js +23 -16
- package/dist/internal/selection.js.map +1 -1
- package/dist/internal/tree-expansion.d.ts +3 -1
- package/dist/internal/tree-expansion.d.ts.map +1 -1
- package/dist/internal/tree-expansion.js +31 -2
- package/dist/internal/tree-expansion.js.map +1 -1
- package/dist/internal/tree-order.d.ts +1 -0
- package/dist/internal/tree-order.d.ts.map +1 -1
- package/dist/internal/tree-order.js +2 -1
- package/dist/internal/tree-order.js.map +1 -1
- package/dist/internal/use-collection.d.ts +1 -0
- package/dist/internal/use-collection.d.ts.map +1 -1
- package/dist/internal/use-collection.js +6 -2
- package/dist/internal/use-collection.js.map +1 -1
- package/dist/list-item-factory.d.ts +1 -1
- package/dist/list-item-factory.d.ts.map +1 -1
- package/dist/list-item-factory.js +2 -2
- package/dist/list-item-factory.js.map +1 -1
- package/dist/list-view.d.ts.map +1 -1
- package/dist/list-view.js +8 -3
- package/dist/list-view.js.map +1 -1
- package/dist/types.d.ts +37 -47
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -8
- package/src/column-view.tsx +8 -4
- package/src/combo-row.tsx +4 -1
- package/src/grid-view.tsx +3 -2
- package/src/index.ts +0 -4
- package/src/internal/cells.tsx +141 -53
- package/src/internal/collection-index.ts +7 -14
- package/src/internal/collection-model.ts +211 -76
- package/src/internal/collection.ts +14 -13
- package/src/internal/controlled-sync.ts +8 -1
- package/src/internal/drop-down.tsx +111 -14
- package/src/internal/expansion.ts +52 -20
- package/src/internal/first-header.ts +23 -0
- package/src/internal/level-splice.ts +157 -0
- package/src/internal/selection.tsx +24 -19
- package/src/internal/tree-expansion.ts +52 -2
- package/src/internal/tree-order.ts +3 -1
- package/src/internal/use-collection.tsx +8 -2
- package/src/list-item-factory.tsx +2 -1
- package/src/list-view.tsx +10 -2
- package/src/types.ts +39 -51
- package/dist/tab-collection.d.ts +0 -9
- package/dist/tab-collection.d.ts.map +0 -1
- package/dist/tab-collection.js +0 -114
- package/dist/tab-collection.js.map +0 -1
- package/src/tab-collection.tsx +0 -124
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import type { ReactElement } from "react";
|
|
3
3
|
import { GtkFlattenListModel } from "@gtkx/jsx/gtk";
|
|
4
|
+
import { useLatestRef } from "@gtkx/react/internal";
|
|
4
5
|
import { useLayoutEffect, useMemo, useState } from "react";
|
|
5
6
|
import type { ExpansionProps, ListItem, ListSection, SelectionProps } from "../types.js";
|
|
6
7
|
import type { Collection } from "./collection.js";
|
|
@@ -14,6 +15,7 @@ type CollectionDataOptions = {
|
|
|
14
15
|
items?: ListItem[] | undefined;
|
|
15
16
|
sections?: ListSection[] | undefined;
|
|
16
17
|
isFlat?: boolean | undefined;
|
|
18
|
+
onSectionsFilled?: (() => void) | undefined;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
type CollectionOptions = CollectionDataOptions & SelectionProps & ExpansionProps;
|
|
@@ -44,9 +46,13 @@ function useCollectionData(options: CollectionDataOptions): CollectionDataResult
|
|
|
44
46
|
return gtkModel.clear;
|
|
45
47
|
}, [gtkModel, root, model]);
|
|
46
48
|
|
|
49
|
+
const onSectionsFilled = useLatestRef(options.onSectionsFilled);
|
|
50
|
+
|
|
47
51
|
useLayoutEffect(() => {
|
|
48
|
-
gtkModel.sync(index)
|
|
49
|
-
|
|
52
|
+
gtkModel.sync(index, () => {
|
|
53
|
+
onSectionsFilled.current?.();
|
|
54
|
+
});
|
|
55
|
+
}, [gtkModel, index, root, model, onSectionsFilled]);
|
|
50
56
|
|
|
51
57
|
return {
|
|
52
58
|
collection,
|
|
@@ -10,6 +10,7 @@ import { NativeItemPortals, useItemCells } from "./internal/cells.js";
|
|
|
10
10
|
*/
|
|
11
11
|
function ListItemFactory<T extends GObject.Object>({
|
|
12
12
|
renderItem,
|
|
13
|
+
rowProps,
|
|
13
14
|
estimatedItemHeight = -1,
|
|
14
15
|
estimatedItemWidth = -1,
|
|
15
16
|
}: ListItemFactoryProps<T>): ReactNode {
|
|
@@ -19,7 +20,7 @@ function ListItemFactory<T extends GObject.Object>({
|
|
|
19
20
|
return (
|
|
20
21
|
<>
|
|
21
22
|
<GtkSignalListItemFactory {...cells.handlers} />
|
|
22
|
-
<NativeItemPortals registry={cells} render={renderItem} size={size} />
|
|
23
|
+
<NativeItemPortals registry={cells} render={renderItem} rowProps={rowProps} size={size} />
|
|
23
24
|
</>
|
|
24
25
|
);
|
|
25
26
|
}
|
package/src/list-view.tsx
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
1
2
|
import type { ReactNode } from "react";
|
|
2
3
|
import { GtkListView, GtkSignalListItemFactory } from "@gtkx/jsx/gtk";
|
|
3
4
|
import { omit } from "@gtkx/utils";
|
|
4
5
|
import type { ListViewProps } from "./types.js";
|
|
5
6
|
import { ItemPortals, useItemCells, useSectionHeader } from "./internal/cells.js";
|
|
7
|
+
import { showFirstHeader } from "./internal/first-header.js";
|
|
6
8
|
import { useCollection } from "./internal/use-collection.js";
|
|
9
|
+
import { useWidgetRef } from "./internal/use-widget-ref.js";
|
|
7
10
|
|
|
8
11
|
const LIST_VIEW_PROPS = [
|
|
9
12
|
"items",
|
|
@@ -11,6 +14,7 @@ const LIST_VIEW_PROPS = [
|
|
|
11
14
|
"isFlat",
|
|
12
15
|
"renderItem",
|
|
13
16
|
"renderHeader",
|
|
17
|
+
"rowProps",
|
|
14
18
|
"selectedIds",
|
|
15
19
|
"onSelectionChanged",
|
|
16
20
|
"selectionMode",
|
|
@@ -19,6 +23,7 @@ const LIST_VIEW_PROPS = [
|
|
|
19
23
|
"expanderDescriptions",
|
|
20
24
|
"estimatedItemHeight",
|
|
21
25
|
"estimatedItemWidth",
|
|
26
|
+
"ref",
|
|
22
27
|
] as const satisfies (keyof ListViewProps)[];
|
|
23
28
|
|
|
24
29
|
/**
|
|
@@ -26,17 +31,19 @@ const LIST_VIEW_PROPS = [
|
|
|
26
31
|
* controlled selection, controlled tree expansion, and estimated item sizing.
|
|
27
32
|
*/
|
|
28
33
|
function ListView<T = unknown, S = unknown>(props: ListViewProps<T, S>): ReactNode {
|
|
29
|
-
const { renderItem, renderHeader, expandedIds, expanderDescriptions } = props;
|
|
34
|
+
const { renderItem, renderHeader, rowProps, expandedIds, expanderDescriptions, ref } = props;
|
|
30
35
|
const { estimatedItemHeight, estimatedItemWidth } = props;
|
|
31
36
|
const rest = omit(props, LIST_VIEW_PROPS);
|
|
32
37
|
const size = { width: estimatedItemWidth ?? -1, height: estimatedItemHeight ?? -1 };
|
|
33
|
-
const
|
|
38
|
+
const [view, refCallback] = useWidgetRef<Gtk.ListView>(ref);
|
|
39
|
+
const { collection, selection } = useCollection({ ...props, onSectionsFilled: () => showFirstHeader(view) });
|
|
34
40
|
const itemCells = useItemCells(size);
|
|
35
41
|
const header = useSectionHeader(renderHeader, collection, size);
|
|
36
42
|
|
|
37
43
|
return (
|
|
38
44
|
<>
|
|
39
45
|
<GtkListView
|
|
46
|
+
ref={refCallback}
|
|
40
47
|
model={selection}
|
|
41
48
|
factory={<GtkSignalListItemFactory {...itemCells.handlers} />}
|
|
42
49
|
{...header.factoryProps}
|
|
@@ -45,6 +52,7 @@ function ListView<T = unknown, S = unknown>(props: ListViewProps<T, S>): ReactNo
|
|
|
45
52
|
<ItemPortals
|
|
46
53
|
registry={itemCells}
|
|
47
54
|
render={renderItem}
|
|
55
|
+
rowProps={rowProps}
|
|
48
56
|
collection={collection}
|
|
49
57
|
expandedIds={expandedIds}
|
|
50
58
|
expanderDescriptions={expanderDescriptions}
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as Adw from "@gtkx/gi/adw";
|
|
2
2
|
import type * as GObject from "@gtkx/gi/gobject";
|
|
3
3
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
4
|
-
import type { AdwComboRowProps,
|
|
4
|
+
import type { AdwComboRowProps, AdwToastProps } from "@gtkx/jsx/adw";
|
|
5
5
|
import type {
|
|
6
6
|
GtkColumnViewColumnProps,
|
|
7
7
|
GtkColumnViewProps,
|
|
@@ -17,7 +17,10 @@ import type { ReactNode, RefObject } from "react";
|
|
|
17
17
|
* arbitrary value. Nested items form a tree.
|
|
18
18
|
*/
|
|
19
19
|
type ListItem<T = unknown> = {
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Stable identifier that names the item in selection and expansion, so keep it unique across the collection.
|
|
22
|
+
* A row keeps its component state while its id stays among the same siblings.
|
|
23
|
+
*/
|
|
21
24
|
id: string;
|
|
22
25
|
/** Payload handed to the cell renderer as `ListItemRenderArgs.item`. */
|
|
23
26
|
value: T;
|
|
@@ -45,7 +48,10 @@ type ListSection<S = unknown, T = unknown> = {
|
|
|
45
48
|
type ListItemRenderArgs<T> = {
|
|
46
49
|
/** Value of the `ListItem` being rendered. */
|
|
47
50
|
item: T;
|
|
48
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Position of the item in the flattened item list, which excludes section headers and collapsed tree rows.
|
|
53
|
+
* A drop-down popup filtered by `enableSearch` still passes this position, not the row's place in the results.
|
|
54
|
+
*/
|
|
49
55
|
index: number;
|
|
50
56
|
/** Depth of the item within a tree, starting at zero for top-level items. */
|
|
51
57
|
depth?: number | undefined;
|
|
@@ -54,9 +60,10 @@ type ListItemRenderArgs<T> = {
|
|
|
54
60
|
};
|
|
55
61
|
|
|
56
62
|
/**
|
|
57
|
-
* Properties applied to the row that carries one item
|
|
58
|
-
*
|
|
59
|
-
* leaves the accessibility strings unset rather than
|
|
63
|
+
* Properties applied to the row that carries one item: the Gtk.ListItem of a list or grid, or the
|
|
64
|
+
* Gtk.ColumnViewRow of a table. Neither is a widget, so these are the only handles they offer; anything
|
|
65
|
+
* omitted falls back to the row's own default, which leaves the accessibility strings unset rather than
|
|
66
|
+
* setting them to an empty string.
|
|
60
67
|
*/
|
|
61
68
|
type ListRowProps = {
|
|
62
69
|
/** Name a screen reader announces for the row, through `accessible-label`. */
|
|
@@ -94,6 +101,14 @@ type ItemSizeProps = {
|
|
|
94
101
|
|
|
95
102
|
type ListItemFactoryProps<T extends GObject.Object> = ItemSizeProps & {
|
|
96
103
|
renderItem: ListItemRenderer<T>;
|
|
104
|
+
/** Resolves the props of the row carrying one model object, such as its screen-reader label. */
|
|
105
|
+
rowProps?: ListRowPropsResolver<T> | null | undefined;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** Per-row properties shared by the collection views. */
|
|
109
|
+
type RowPropsProps<T> = {
|
|
110
|
+
/** Resolves the props of the row carrying one item, such as its screen-reader label. */
|
|
111
|
+
rowProps?: ListRowPropsResolver<T> | null | undefined;
|
|
97
112
|
};
|
|
98
113
|
|
|
99
114
|
/** The flat-collection hint shared by {@link ListView} and {@link ColumnView}. */
|
|
@@ -113,7 +128,10 @@ type SelectionProps = {
|
|
|
113
128
|
* user's selection is adopted into it.
|
|
114
129
|
*/
|
|
115
130
|
selectedIds?: string[] | null | undefined;
|
|
116
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Called with one id per selected row when the user or the widget changes the selection, or when an item
|
|
133
|
+
* `selectedIds` names is not in the collection; a `selectedIds` change is not reported back.
|
|
134
|
+
*/
|
|
117
135
|
onSelectionChanged?: ((ids: string[]) => void) | null | undefined;
|
|
118
136
|
/** How much the user may select, one item at a time unless `MULTIPLE` or `NONE` is given. */
|
|
119
137
|
selectionMode?: Gtk.SelectionMode | null | undefined;
|
|
@@ -138,7 +156,10 @@ type ExpansionProps = {
|
|
|
138
156
|
* user's expansion is adopted into it.
|
|
139
157
|
*/
|
|
140
158
|
expandedIds?: string[] | null | undefined;
|
|
141
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* Called with one id per expanded row, in visible order, when the user or the widget changes expansion, or
|
|
161
|
+
* when a row `expandedIds` names cannot expand; an `expandedIds` change is not reported back.
|
|
162
|
+
*/
|
|
142
163
|
onExpandedChange?: ((ids: string[]) => void) | null | undefined;
|
|
143
164
|
/**
|
|
144
165
|
* Wording announced for the expander of every row that can expand, describing what activating it does.
|
|
@@ -182,6 +203,11 @@ type ColumnViewColumn<T = unknown> = Omit<GtkColumnViewColumnProps, "factory" |
|
|
|
182
203
|
renderCell: ListItemRenderer<T>;
|
|
183
204
|
/** Makes the column header clickable, reporting the choice through `onSortChanged`. */
|
|
184
205
|
isSortable?: boolean | undefined;
|
|
206
|
+
/**
|
|
207
|
+
* Draws the tree expander and depth indentation in this column. Without a visible column marked this way,
|
|
208
|
+
* the first visible column draws them.
|
|
209
|
+
*/
|
|
210
|
+
hasExpander?: boolean | undefined;
|
|
185
211
|
/** Menu element popped up as the column header's context menu, on a right-click. */
|
|
186
212
|
headerMenu?: ReactNode;
|
|
187
213
|
};
|
|
@@ -192,11 +218,10 @@ type ColumnViewOwnProps<T, S> = SelectionProps &
|
|
|
192
218
|
SortProps &
|
|
193
219
|
SourceProps<T, S> &
|
|
194
220
|
FlatnessProps &
|
|
221
|
+
RowPropsProps<T> &
|
|
195
222
|
Omit<ItemSizeProps, "estimatedItemWidth"> & {
|
|
196
223
|
/** Columns to render, in order; each carries its own cell renderer. */
|
|
197
224
|
columns: ColumnViewColumn<T>[];
|
|
198
|
-
/** Resolves the props of the row carrying one item's cells, such as its screen-reader label. */
|
|
199
|
-
rowProps?: ListRowPropsResolver<T> | null | undefined;
|
|
200
225
|
};
|
|
201
226
|
|
|
202
227
|
/**
|
|
@@ -235,7 +260,8 @@ type DropDownProps<T = unknown, S = unknown> = DropDownWidgetProps<GtkDropDownPr
|
|
|
235
260
|
|
|
236
261
|
/** The declarative collection props {@link GridView} adds on top of Gtk.GridView's own. */
|
|
237
262
|
type GridViewOwnProps<T> = ItemSizeProps &
|
|
238
|
-
SelectionProps &
|
|
263
|
+
SelectionProps &
|
|
264
|
+
RowPropsProps<T> & {
|
|
239
265
|
/** Items to render as cells; a grid is always flat, so `ListItem.children` is ignored. */
|
|
240
266
|
items?: ListItem<T>[] | undefined;
|
|
241
267
|
/** Renders the contents of one cell. */
|
|
@@ -253,7 +279,8 @@ type ListViewOwnProps<T, S> = ItemSizeProps &
|
|
|
253
279
|
SelectionProps &
|
|
254
280
|
ExpansionProps &
|
|
255
281
|
SourceProps<T, S> &
|
|
256
|
-
FlatnessProps &
|
|
282
|
+
FlatnessProps &
|
|
283
|
+
RowPropsProps<T> & {
|
|
257
284
|
/** Renders the contents of one row. */
|
|
258
285
|
renderItem: ListItemRenderer<T>;
|
|
259
286
|
};
|
|
@@ -294,42 +321,6 @@ type ToastProviderProps = {
|
|
|
294
321
|
children?: ReactNode | undefined;
|
|
295
322
|
};
|
|
296
323
|
|
|
297
|
-
/** Describes the JSX page rendered for each value in a {@link TabCollection}. */
|
|
298
|
-
type TabCollectionOptions<T> = {
|
|
299
|
-
/** Native page props and JSX content. Call getPage in event handlers to access the mounted page. The collection owns its ref and parent relationship. */
|
|
300
|
-
pageProps: (item: T, getPage: () => Adw.TabPage | null) => Omit<AdwTabPageProps, "ref" | "parent">;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
/** Options for adding a page to a {@link TabCollection}. */
|
|
304
|
-
type TabAppendOptions = {
|
|
305
|
-
/** Inserts a related page after this parent and its descendants, using native tab ancestry. */
|
|
306
|
-
parent?: Adw.TabPage | undefined;
|
|
307
|
-
/** Selects the new page by default. */
|
|
308
|
-
select?: boolean | undefined;
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Owns independent JSX tab roots across native view transfers. Its lifetime can span several windows.
|
|
313
|
-
* Remove a page after accepting its native close request, remove a view's pages before destroying that
|
|
314
|
-
* view, and dispose the collection when its owner is finished.
|
|
315
|
-
*/
|
|
316
|
-
type TabCollection<T> = {
|
|
317
|
-
/** Renders synchronously and returns the native page, including from an overview's create-tab handler. */
|
|
318
|
-
append: (view: Adw.TabView, item: T, options?: TabAppendOptions) => Adw.TabPage;
|
|
319
|
-
/** Returns a managed page's current value, or undefined for an unmanaged page. */
|
|
320
|
-
get: (page: Adw.TabPage) => T | undefined;
|
|
321
|
-
/** Updates the value and rerenders the existing JSX root, preserving component state and native identity. */
|
|
322
|
-
update: (page: Adw.TabPage, item: T) => void;
|
|
323
|
-
/** Unmounts a managed page from its current native owner; already removed pages are ignored. */
|
|
324
|
-
remove: (page: Adw.TabPage) => void;
|
|
325
|
-
/** Unmounts this collection's pages currently belonging to the given view, including transferred pages. */
|
|
326
|
-
removeView: (view: Adw.TabView) => void;
|
|
327
|
-
/** Moves a managed page to another view, appending within its pinned or unpinned group by default. */
|
|
328
|
-
transfer: (page: Adw.TabPage, target: Adw.TabView, position?: number) => void;
|
|
329
|
-
/** Unmounts every managed page. The collection may subsequently be reused. */
|
|
330
|
-
dispose: () => void;
|
|
331
|
-
};
|
|
332
|
-
|
|
333
324
|
export {
|
|
334
325
|
type SelectionProps,
|
|
335
326
|
type ExpanderDescriptions,
|
|
@@ -354,7 +345,4 @@ export {
|
|
|
354
345
|
type ToastOptions,
|
|
355
346
|
type ToastController,
|
|
356
347
|
type ToastProviderProps,
|
|
357
|
-
type TabAppendOptions,
|
|
358
|
-
type TabCollection,
|
|
359
|
-
type TabCollectionOptions,
|
|
360
348
|
};
|
package/dist/tab-collection.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { TabCollection, TabCollectionOptions } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Creates a collection of JSX pages whose React state survives native transfers and source-window
|
|
4
|
-
* closure. Call append from an event handler because it commits synchronously. Page content lives in
|
|
5
|
-
* independent roots: pass context-dependent values through the item or wrap the content in providers.
|
|
6
|
-
*/
|
|
7
|
-
declare const createTabCollection: <T>(options: TabCollectionOptions<T>) => TabCollection<T>;
|
|
8
|
-
export { createTabCollection };
|
|
9
|
-
//# sourceMappingURL=tab-collection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tab-collection.d.ts","sourceRoot":"","sources":["../src/tab-collection.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AA6BtE;;;;GAIG;AACH,QAAA,MAAM,mBAAmB,GAAI,CAAC,WAAY,oBAAoB,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAkFlF,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
package/dist/tab-collection.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import * as Adw from "@gtkx/gi/adw";
|
|
3
|
-
import { AdwTabPage } from "@gtkx/jsx/adw";
|
|
4
|
-
import { createRoot, flushSync } from "@gtkx/react";
|
|
5
|
-
import { getClassType } from "@gtkx/runtime";
|
|
6
|
-
import { createRef, useEffect } from "react";
|
|
7
|
-
const ManagedTab = ({ handle, item, options, forget }) => {
|
|
8
|
-
const { page: pageRef, parent, getPage } = handle;
|
|
9
|
-
useEffect(() => () => forget(handle), [handle, forget]);
|
|
10
|
-
return _jsx(AdwTabPage, { ...options.pageProps(item, getPage), ref: pageRef, parent: parent });
|
|
11
|
-
};
|
|
12
|
-
const ownerOf = (page) => {
|
|
13
|
-
const owner = page.getChild().getAncestor(getClassType(Adw.TabView));
|
|
14
|
-
if (!(owner instanceof Adw.TabView))
|
|
15
|
-
throw new Error("The tab has no native view");
|
|
16
|
-
return owner;
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Creates a collection of JSX pages whose React state survives native transfers and source-window
|
|
20
|
-
* closure. Call append from an event handler because it commits synchronously. Page content lives in
|
|
21
|
-
* independent roots: pass context-dependent values through the item or wrap the content in providers.
|
|
22
|
-
*/
|
|
23
|
-
const createTabCollection = (options) => {
|
|
24
|
-
const handles = new Set();
|
|
25
|
-
const forget = (handle) => {
|
|
26
|
-
handles.delete(handle);
|
|
27
|
-
};
|
|
28
|
-
const find = (page) => [...handles].find((handle) => handle.page.current === page);
|
|
29
|
-
const render = (handle) => {
|
|
30
|
-
handle.root.render(_jsx(ManagedTab, { handle: handle, item: handle.item, options: options, forget: forget }));
|
|
31
|
-
};
|
|
32
|
-
const remove = (handle) => {
|
|
33
|
-
handles.delete(handle);
|
|
34
|
-
handle.root.unmount();
|
|
35
|
-
};
|
|
36
|
-
return {
|
|
37
|
-
append: (view, item, appendOptions = {}) => {
|
|
38
|
-
if (appendOptions.parent && ownerOf(appendOptions.parent) !== view) {
|
|
39
|
-
throw new RangeError("The parent page belongs to another tab view");
|
|
40
|
-
}
|
|
41
|
-
const pageRef = createRef();
|
|
42
|
-
const handle = {
|
|
43
|
-
root: createRoot(view),
|
|
44
|
-
page: pageRef,
|
|
45
|
-
getPage: () => pageRef.current,
|
|
46
|
-
parent: appendOptions.parent,
|
|
47
|
-
item,
|
|
48
|
-
};
|
|
49
|
-
handles.add(handle);
|
|
50
|
-
try {
|
|
51
|
-
flushSync(() => render(handle));
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
remove(handle);
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
const page = handle.page.current;
|
|
58
|
-
if (page === null) {
|
|
59
|
-
remove(handle);
|
|
60
|
-
throw new Error("The tab page did not render");
|
|
61
|
-
}
|
|
62
|
-
if (appendOptions.parent === undefined) {
|
|
63
|
-
view.reorderPage(page, page.getPinned() ? view.getNPinnedPages() - 1 : view.getNPages() - 1);
|
|
64
|
-
}
|
|
65
|
-
if (appendOptions.select !== false)
|
|
66
|
-
view.setSelectedPage(page);
|
|
67
|
-
return page;
|
|
68
|
-
},
|
|
69
|
-
get: (page) => find(page)?.item,
|
|
70
|
-
update: (page, item) => {
|
|
71
|
-
const handle = find(page);
|
|
72
|
-
if (handle === undefined)
|
|
73
|
-
throw new Error("The tab is not managed by this collection");
|
|
74
|
-
handle.item = item;
|
|
75
|
-
render(handle);
|
|
76
|
-
},
|
|
77
|
-
remove: (page) => {
|
|
78
|
-
const handle = find(page);
|
|
79
|
-
if (handle)
|
|
80
|
-
remove(handle);
|
|
81
|
-
},
|
|
82
|
-
removeView: (view) => {
|
|
83
|
-
const pages = Array.from({ length: view.getNPages() }, (_, index) => view.getNthPage(index));
|
|
84
|
-
for (const page of pages) {
|
|
85
|
-
const handle = find(page);
|
|
86
|
-
if (handle)
|
|
87
|
-
remove(handle);
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
transfer: (page, target, position) => {
|
|
91
|
-
if (find(page) === undefined)
|
|
92
|
-
throw new Error("The tab is not managed by this collection");
|
|
93
|
-
const source = ownerOf(page);
|
|
94
|
-
const minimum = page.getPinned() ? 0 : target.getNPinnedPages();
|
|
95
|
-
const maximum = page.getPinned() ? target.getNPinnedPages() : target.getNPages();
|
|
96
|
-
const index = position ?? maximum;
|
|
97
|
-
if (!Number.isInteger(index) || index < minimum || index > maximum) {
|
|
98
|
-
throw new RangeError("The position is outside the tab's pinned or unpinned group");
|
|
99
|
-
}
|
|
100
|
-
if (source === target) {
|
|
101
|
-
target.reorderPage(page, Math.min(index, maximum - 1));
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
source.transferPage(page, target, index);
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
dispose: () => {
|
|
108
|
-
for (const handle of [...handles])
|
|
109
|
-
remove(handle);
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
};
|
|
113
|
-
export { createTabCollection };
|
|
114
|
-
//# sourceMappingURL=tab-collection.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tab-collection.js","sourceRoot":"","sources":["../src/tab-collection.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as Adw from \"@gtkx/gi/adw\";\nimport { AdwTabPage } from \"@gtkx/jsx/adw\";\nimport { createRoot, flushSync, type Root } from \"@gtkx/react\";\nimport { getClassType } from \"@gtkx/runtime\";\nimport { createRef, type RefObject, useEffect } from \"react\";\nimport type { TabCollection, TabCollectionOptions } from \"./types.js\";\n\ntype TabHandle<T> = {\n root: Root;\n page: RefObject<Adw.TabPage | null>;\n getPage: () => Adw.TabPage | null;\n parent: Adw.TabPage | undefined;\n item: T;\n};\n\ntype ManagedTabProps<T> = {\n handle: TabHandle<T>;\n item: T;\n options: TabCollectionOptions<T>;\n forget: (handle: TabHandle<T>) => void;\n};\n\nconst ManagedTab = <T,>({ handle, item, options, forget }: ManagedTabProps<T>) => {\n const { page: pageRef, parent, getPage } = handle;\n useEffect(() => () => forget(handle), [handle, forget]);\n return <AdwTabPage {...options.pageProps(item, getPage)} ref={pageRef} parent={parent} />;\n};\n\nconst ownerOf = (page: Adw.TabPage): Adw.TabView => {\n const owner = page.getChild().getAncestor(getClassType(Adw.TabView));\n if (!(owner instanceof Adw.TabView)) throw new Error(\"The tab has no native view\");\n return owner;\n};\n\n/**\n * Creates a collection of JSX pages whose React state survives native transfers and source-window\n * closure. Call append from an event handler because it commits synchronously. Page content lives in\n * independent roots: pass context-dependent values through the item or wrap the content in providers.\n */\nconst createTabCollection = <T,>(options: TabCollectionOptions<T>): TabCollection<T> => {\n const handles = new Set<TabHandle<T>>();\n const forget = (handle: TabHandle<T>) => {\n handles.delete(handle);\n };\n const find = (page: Adw.TabPage) => [...handles].find((handle) => handle.page.current === page);\n const render = (handle: TabHandle<T>) => {\n handle.root.render(<ManagedTab handle={handle} item={handle.item} options={options} forget={forget} />);\n };\n const remove = (handle: TabHandle<T>) => {\n handles.delete(handle);\n handle.root.unmount();\n };\n\n return {\n append: (view, item, appendOptions = {}) => {\n if (appendOptions.parent && ownerOf(appendOptions.parent) !== view) {\n throw new RangeError(\"The parent page belongs to another tab view\");\n }\n const pageRef = createRef<Adw.TabPage>();\n const handle: TabHandle<T> = {\n root: createRoot(view),\n page: pageRef,\n getPage: () => pageRef.current,\n parent: appendOptions.parent,\n item,\n };\n handles.add(handle);\n try {\n flushSync(() => render(handle));\n } catch (error) {\n remove(handle);\n throw error;\n }\n const page = handle.page.current;\n if (page === null) {\n remove(handle);\n throw new Error(\"The tab page did not render\");\n }\n if (appendOptions.parent === undefined) {\n view.reorderPage(page, page.getPinned() ? view.getNPinnedPages() - 1 : view.getNPages() - 1);\n }\n if (appendOptions.select !== false) view.setSelectedPage(page);\n return page;\n },\n get: (page) => find(page)?.item,\n update: (page, item) => {\n const handle = find(page);\n if (handle === undefined) throw new Error(\"The tab is not managed by this collection\");\n handle.item = item;\n render(handle);\n },\n remove: (page) => {\n const handle = find(page);\n if (handle) remove(handle);\n },\n removeView: (view) => {\n const pages = Array.from({ length: view.getNPages() }, (_, index) => view.getNthPage(index));\n for (const page of pages) {\n const handle = find(page);\n if (handle) remove(handle);\n }\n },\n transfer: (page, target, position) => {\n if (find(page) === undefined) throw new Error(\"The tab is not managed by this collection\");\n const source = ownerOf(page);\n const minimum = page.getPinned() ? 0 : target.getNPinnedPages();\n const maximum = page.getPinned() ? target.getNPinnedPages() : target.getNPages();\n const index = position ?? maximum;\n if (!Number.isInteger(index) || index < minimum || index > maximum) {\n throw new RangeError(\"The position is outside the tab's pinned or unpinned group\");\n }\n if (source === target) {\n target.reorderPage(page, Math.min(index, maximum - 1));\n } else {\n source.transferPage(page, target, index);\n }\n },\n dispose: () => {\n for (const handle of [...handles]) remove(handle);\n },\n };\n};\n\nexport { createTabCollection };\n"]}
|
package/src/tab-collection.tsx
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import * as Adw from "@gtkx/gi/adw";
|
|
2
|
-
import { AdwTabPage } from "@gtkx/jsx/adw";
|
|
3
|
-
import { createRoot, flushSync, type Root } from "@gtkx/react";
|
|
4
|
-
import { getClassType } from "@gtkx/runtime";
|
|
5
|
-
import { createRef, type RefObject, useEffect } from "react";
|
|
6
|
-
import type { TabCollection, TabCollectionOptions } from "./types.js";
|
|
7
|
-
|
|
8
|
-
type TabHandle<T> = {
|
|
9
|
-
root: Root;
|
|
10
|
-
page: RefObject<Adw.TabPage | null>;
|
|
11
|
-
getPage: () => Adw.TabPage | null;
|
|
12
|
-
parent: Adw.TabPage | undefined;
|
|
13
|
-
item: T;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type ManagedTabProps<T> = {
|
|
17
|
-
handle: TabHandle<T>;
|
|
18
|
-
item: T;
|
|
19
|
-
options: TabCollectionOptions<T>;
|
|
20
|
-
forget: (handle: TabHandle<T>) => void;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const ManagedTab = <T,>({ handle, item, options, forget }: ManagedTabProps<T>) => {
|
|
24
|
-
const { page: pageRef, parent, getPage } = handle;
|
|
25
|
-
useEffect(() => () => forget(handle), [handle, forget]);
|
|
26
|
-
return <AdwTabPage {...options.pageProps(item, getPage)} ref={pageRef} parent={parent} />;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const ownerOf = (page: Adw.TabPage): Adw.TabView => {
|
|
30
|
-
const owner = page.getChild().getAncestor(getClassType(Adw.TabView));
|
|
31
|
-
if (!(owner instanceof Adw.TabView)) throw new Error("The tab has no native view");
|
|
32
|
-
return owner;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Creates a collection of JSX pages whose React state survives native transfers and source-window
|
|
37
|
-
* closure. Call append from an event handler because it commits synchronously. Page content lives in
|
|
38
|
-
* independent roots: pass context-dependent values through the item or wrap the content in providers.
|
|
39
|
-
*/
|
|
40
|
-
const createTabCollection = <T,>(options: TabCollectionOptions<T>): TabCollection<T> => {
|
|
41
|
-
const handles = new Set<TabHandle<T>>();
|
|
42
|
-
const forget = (handle: TabHandle<T>) => {
|
|
43
|
-
handles.delete(handle);
|
|
44
|
-
};
|
|
45
|
-
const find = (page: Adw.TabPage) => [...handles].find((handle) => handle.page.current === page);
|
|
46
|
-
const render = (handle: TabHandle<T>) => {
|
|
47
|
-
handle.root.render(<ManagedTab handle={handle} item={handle.item} options={options} forget={forget} />);
|
|
48
|
-
};
|
|
49
|
-
const remove = (handle: TabHandle<T>) => {
|
|
50
|
-
handles.delete(handle);
|
|
51
|
-
handle.root.unmount();
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
append: (view, item, appendOptions = {}) => {
|
|
56
|
-
if (appendOptions.parent && ownerOf(appendOptions.parent) !== view) {
|
|
57
|
-
throw new RangeError("The parent page belongs to another tab view");
|
|
58
|
-
}
|
|
59
|
-
const pageRef = createRef<Adw.TabPage>();
|
|
60
|
-
const handle: TabHandle<T> = {
|
|
61
|
-
root: createRoot(view),
|
|
62
|
-
page: pageRef,
|
|
63
|
-
getPage: () => pageRef.current,
|
|
64
|
-
parent: appendOptions.parent,
|
|
65
|
-
item,
|
|
66
|
-
};
|
|
67
|
-
handles.add(handle);
|
|
68
|
-
try {
|
|
69
|
-
flushSync(() => render(handle));
|
|
70
|
-
} catch (error) {
|
|
71
|
-
remove(handle);
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
const page = handle.page.current;
|
|
75
|
-
if (page === null) {
|
|
76
|
-
remove(handle);
|
|
77
|
-
throw new Error("The tab page did not render");
|
|
78
|
-
}
|
|
79
|
-
if (appendOptions.parent === undefined) {
|
|
80
|
-
view.reorderPage(page, page.getPinned() ? view.getNPinnedPages() - 1 : view.getNPages() - 1);
|
|
81
|
-
}
|
|
82
|
-
if (appendOptions.select !== false) view.setSelectedPage(page);
|
|
83
|
-
return page;
|
|
84
|
-
},
|
|
85
|
-
get: (page) => find(page)?.item,
|
|
86
|
-
update: (page, item) => {
|
|
87
|
-
const handle = find(page);
|
|
88
|
-
if (handle === undefined) throw new Error("The tab is not managed by this collection");
|
|
89
|
-
handle.item = item;
|
|
90
|
-
render(handle);
|
|
91
|
-
},
|
|
92
|
-
remove: (page) => {
|
|
93
|
-
const handle = find(page);
|
|
94
|
-
if (handle) remove(handle);
|
|
95
|
-
},
|
|
96
|
-
removeView: (view) => {
|
|
97
|
-
const pages = Array.from({ length: view.getNPages() }, (_, index) => view.getNthPage(index));
|
|
98
|
-
for (const page of pages) {
|
|
99
|
-
const handle = find(page);
|
|
100
|
-
if (handle) remove(handle);
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
transfer: (page, target, position) => {
|
|
104
|
-
if (find(page) === undefined) throw new Error("The tab is not managed by this collection");
|
|
105
|
-
const source = ownerOf(page);
|
|
106
|
-
const minimum = page.getPinned() ? 0 : target.getNPinnedPages();
|
|
107
|
-
const maximum = page.getPinned() ? target.getNPinnedPages() : target.getNPages();
|
|
108
|
-
const index = position ?? maximum;
|
|
109
|
-
if (!Number.isInteger(index) || index < minimum || index > maximum) {
|
|
110
|
-
throw new RangeError("The position is outside the tab's pinned or unpinned group");
|
|
111
|
-
}
|
|
112
|
-
if (source === target) {
|
|
113
|
-
target.reorderPage(page, Math.min(index, maximum - 1));
|
|
114
|
-
} else {
|
|
115
|
-
source.transferPage(page, target, index);
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
dispose: () => {
|
|
119
|
-
for (const handle of [...handles]) remove(handle);
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export { createTabCollection };
|