@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
|
@@ -1,72 +1,81 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import { GtkColumnViewColumn, type GtkColumnViewColumnProps } from "@gtkx/jsx/gtk";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { useMergeRefs } from "@gtkx/react/internal";
|
|
4
|
+
import { type ReactNode, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import { type CellRenderer, CellRenderHost, itemRenderer, type TreeRenderContext } from "./cell.js";
|
|
6
|
+
import { makeFactoryInstaller, useCellContainers } from "./hooks/use-cell-containers.js";
|
|
5
7
|
import { useHeaderMenu } from "./hooks/use-header-menu.js";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import type { SlotRenderer } from "./list-slot.js";
|
|
9
|
-
import type { ColumnViewColumnProps } from "./types.js";
|
|
8
|
+
import type { RenderItemProps } from "./types.js";
|
|
9
|
+
import type { ItemResolver } from "./utils/item-resolver.js";
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const factoryInstaller = makeFactoryInstaller<Gtk.ColumnViewColumn>((column, factory) => column.setFactory(factory));
|
|
12
|
+
|
|
13
|
+
export type ColumnDefDeclarativeProps<T = unknown> = {
|
|
14
|
+
title: string;
|
|
15
|
+
expand?: boolean | undefined;
|
|
16
|
+
resizable?: boolean | undefined;
|
|
17
|
+
fixedWidth?: number | undefined;
|
|
18
|
+
id: string;
|
|
19
|
+
sortable?: boolean | undefined;
|
|
20
|
+
visible?: boolean | undefined;
|
|
21
|
+
renderCell: (props: RenderItemProps<T>) => ReactNode;
|
|
22
|
+
headerMenu?: ReactNode;
|
|
14
23
|
};
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
export type ColumnDef<T = unknown> = Omit<GtkColumnViewColumnProps, "factory" | "sorter"> &
|
|
26
|
+
ColumnDefDeclarativeProps<T>;
|
|
27
|
+
|
|
28
|
+
type ColumnViewColumnProps<T = unknown> = ColumnDef<T> & {
|
|
29
|
+
resolver: ItemResolver<unknown, unknown>;
|
|
30
|
+
tree: TreeRenderContext;
|
|
31
|
+
estimatedItemHeight?: number | undefined;
|
|
32
|
+
onInstance: (id: string, column: Gtk.ColumnViewColumn | null) => void;
|
|
33
|
+
};
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
export const ColumnViewColumn = <T = unknown>(props: ColumnViewColumnProps<T>): ReactNode => {
|
|
36
|
+
const {
|
|
37
|
+
id,
|
|
38
|
+
title,
|
|
39
|
+
sortable,
|
|
40
|
+
renderCell,
|
|
41
|
+
headerMenu,
|
|
42
|
+
resolver,
|
|
43
|
+
tree,
|
|
44
|
+
estimatedItemHeight,
|
|
45
|
+
onInstance,
|
|
46
|
+
ref,
|
|
47
|
+
...intrinsicProps
|
|
48
|
+
} = props as ColumnViewColumnProps<T> & { [key: string]: unknown };
|
|
33
49
|
const [column, setColumn] = useState<Gtk.ColumnViewColumn | null>(null);
|
|
34
50
|
|
|
35
|
-
const captureColumn =
|
|
36
|
-
setColumn(value);
|
|
37
|
-
}).current;
|
|
51
|
+
const captureColumn = useMergeRefs<Gtk.ColumnViewColumn>(ref, setColumn);
|
|
38
52
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
const store = useCellContainers<Gtk.ColumnViewColumn>({
|
|
54
|
+
object: column,
|
|
55
|
+
installer: factoryInstaller,
|
|
56
|
+
estimatedHeight: estimatedItemHeight,
|
|
42
57
|
});
|
|
43
58
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const unregisterRef = useRef(context.unregister);
|
|
47
|
-
unregisterRef.current = context.unregister;
|
|
59
|
+
const onInstanceRef = useRef(onInstance);
|
|
60
|
+
onInstanceRef.current = onInstance;
|
|
48
61
|
|
|
49
62
|
useLayoutEffect(() => {
|
|
50
63
|
if (column === null) return;
|
|
51
|
-
|
|
52
|
-
return () =>
|
|
53
|
-
}, [column, id
|
|
64
|
+
onInstanceRef.current(id, column);
|
|
65
|
+
return () => onInstanceRef.current(id, null);
|
|
66
|
+
}, [column, id]);
|
|
54
67
|
|
|
55
68
|
const headerMenuPortal = useHeaderMenu(column, headerMenu);
|
|
56
69
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (expand !== undefined) intrinsicProps["expand"] = expand;
|
|
62
|
-
if (resizable !== undefined) intrinsicProps["resizable"] = resizable;
|
|
63
|
-
if (fixedWidth !== undefined) intrinsicProps["fixedWidth"] = fixedWidth;
|
|
64
|
-
if (visible !== undefined) intrinsicProps["visible"] = visible;
|
|
70
|
+
const cellRenderer: CellRenderer<unknown, unknown> = itemRenderer<unknown, unknown>(
|
|
71
|
+
(cellProps) => renderCell({ ...cellProps, item: cellProps.item as T }),
|
|
72
|
+
tree,
|
|
73
|
+
);
|
|
65
74
|
|
|
66
75
|
return (
|
|
67
76
|
<>
|
|
68
|
-
|
|
69
|
-
<
|
|
77
|
+
<GtkColumnViewColumn {...intrinsicProps} id={id} title={title} ref={captureColumn} />
|
|
78
|
+
<CellRenderHost store={store} resolver={resolver} render={cellRenderer} />
|
|
70
79
|
{headerMenuPortal}
|
|
71
80
|
</>
|
|
72
81
|
);
|
package/src/column-view.tsx
CHANGED
|
@@ -1,206 +1,190 @@
|
|
|
1
|
-
import type * as Gio from "@gtkx/gi/gio";
|
|
2
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
3
2
|
import { GtkColumnView, type GtkColumnViewProps } from "@gtkx/jsx/gtk";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import type {
|
|
18
|
-
import type { ColumnViewProps } from "./types.js";
|
|
3
|
+
import { type ReactNode, type Ref, useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { createTreeRenderContext, HeaderRenderHost, type TreeRenderContext } from "./cell.js";
|
|
5
|
+
import { type ColumnDef, ColumnViewColumn } from "./column-view-column.js";
|
|
6
|
+
import { makeFactoryInstaller, useCellContainers } from "./hooks/use-cell-containers.js";
|
|
7
|
+
import { useCollectionWidget } from "./hooks/use-collection-widget.js";
|
|
8
|
+
import { type ColumnRegistration, useSortHandler } from "./hooks/use-sort-handler.js";
|
|
9
|
+
import type {
|
|
10
|
+
CollectionItemSizeProps,
|
|
11
|
+
ControlledExpansionProps,
|
|
12
|
+
ControlledSelectionProps,
|
|
13
|
+
ItemNode,
|
|
14
|
+
SectionNode,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
import type { CellContainerStore } from "./utils/cell-container-store.js";
|
|
19
17
|
import type { ItemResolver } from "./utils/item-resolver.js";
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
install: (widget, factory) => widget.setHeaderFactory(factory),
|
|
23
|
-
uninstall: (widget) => widget.setHeaderFactory(null),
|
|
24
|
-
};
|
|
19
|
+
export type { ColumnDef, ColumnDefDeclarativeProps } from "./column-view-column.js";
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
unregister(id: string): void;
|
|
30
|
-
}
|
|
21
|
+
const headerFactoryInstaller = makeFactoryInstaller<Gtk.ColumnView>((widget, factory) =>
|
|
22
|
+
widget.setHeaderFactory(factory),
|
|
23
|
+
);
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const next = new Map(current);
|
|
37
|
-
next.set(registration.id, registration);
|
|
38
|
-
return next;
|
|
39
|
-
});
|
|
40
|
-
}, []);
|
|
41
|
-
const unregister = useCallback((id: string): void => {
|
|
42
|
-
setRegistrations((current) => {
|
|
43
|
-
if (!current.has(id)) return current;
|
|
44
|
-
const next = new Map(current);
|
|
45
|
-
next.delete(id);
|
|
46
|
-
return next;
|
|
47
|
-
});
|
|
48
|
-
}, []);
|
|
49
|
-
const columns = useMemo(() => [...registrations.values()], [registrations]);
|
|
50
|
-
return { columns, register, unregister };
|
|
25
|
+
export type ColumnViewSortProps = {
|
|
26
|
+
sortColumn?: string | null | undefined;
|
|
27
|
+
sortOrder?: Gtk.SortType | null | undefined;
|
|
28
|
+
onSortChanged?: ((column: string | null, order: Gtk.SortType) => void) | null | undefined;
|
|
51
29
|
};
|
|
52
30
|
|
|
31
|
+
export type ColumnViewDeclarativeProps<T = unknown, S = unknown> = ColumnViewSortProps &
|
|
32
|
+
Omit<CollectionItemSizeProps, "estimatedItemWidth"> &
|
|
33
|
+
ControlledSelectionProps &
|
|
34
|
+
ControlledExpansionProps & {
|
|
35
|
+
items?: ItemNode<T>[] | undefined;
|
|
36
|
+
sections?: SectionNode<S, T>[] | undefined;
|
|
37
|
+
renderHeader?: ((info: { section: S }) => ReactNode) | null | undefined;
|
|
38
|
+
columns: ColumnDef<T>[];
|
|
39
|
+
};
|
|
40
|
+
|
|
53
41
|
/**
|
|
54
|
-
* Props for
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
42
|
+
* Props for {@link ColumnView}. Combines the underlying Gtk.ColumnView props with
|
|
43
|
+
* declarative collection props: flat items or grouped sections, controlled selection
|
|
44
|
+
* and expansion, sorting (sortColumn, sortOrder, onSortChanged), an optional section
|
|
45
|
+
* header renderer, and the columns to render.
|
|
58
46
|
*/
|
|
59
|
-
export type
|
|
47
|
+
export type ColumnViewProps<T = unknown, S = unknown> = Omit<
|
|
60
48
|
GtkColumnViewProps,
|
|
61
|
-
"columns" | keyof
|
|
49
|
+
"columns" | "model" | "headerFactory" | keyof ColumnViewDeclarativeProps<T, S>
|
|
62
50
|
> &
|
|
63
|
-
|
|
51
|
+
ColumnViewDeclarativeProps<T, S>;
|
|
64
52
|
|
|
65
|
-
type NormalizedColumnViewProps<T, S> =
|
|
53
|
+
type NormalizedColumnViewProps<T, S> = ColumnViewDeclarativeProps<T, S> & {
|
|
66
54
|
ref?: Ref<Gtk.ColumnView | null>;
|
|
67
|
-
renderHeader?: ((value: S) => ReactNode) | null;
|
|
68
|
-
children?: ReactNode;
|
|
69
55
|
[key: string]: unknown;
|
|
70
56
|
};
|
|
71
57
|
|
|
72
|
-
const headerRenderer =
|
|
73
|
-
<T, S>(renderHeader: ((value: S) => ReactNode) | null | undefined): SlotRenderer<T, S> =>
|
|
74
|
-
(value) =>
|
|
75
|
-
renderHeader ? renderHeader(value as S) : null;
|
|
76
|
-
|
|
77
58
|
interface ColumnViewWiring<T, S> {
|
|
78
59
|
setRef: (value: Gtk.ColumnView | null) => void;
|
|
79
|
-
resolver: ItemResolver<T, S>;
|
|
80
|
-
headerResolver: ItemResolver<T, S>;
|
|
81
|
-
headerStore: ReturnType<typeof useRealizedSlots>["store"];
|
|
82
60
|
useHeader: boolean;
|
|
83
|
-
|
|
61
|
+
headerStore: CellContainerStore;
|
|
62
|
+
headerResolver: ItemResolver<T, S>;
|
|
63
|
+
resolver: ItemResolver<unknown, unknown>;
|
|
64
|
+
tree: TreeRenderContext;
|
|
65
|
+
setInstance: (id: string, column: Gtk.ColumnViewColumn | null) => void;
|
|
84
66
|
}
|
|
85
67
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
68
|
+
const useColumnInstances = (): {
|
|
69
|
+
instances: Map<string, Gtk.ColumnViewColumn>;
|
|
70
|
+
setInstance: (id: string, column: Gtk.ColumnViewColumn | null) => void;
|
|
71
|
+
} => {
|
|
72
|
+
const [instances, setInstances] = useState<Map<string, Gtk.ColumnViewColumn>>(() => new Map());
|
|
73
|
+
const setInstance = useCallback((id: string, column: Gtk.ColumnViewColumn | null): void => {
|
|
74
|
+
setInstances((current) => {
|
|
75
|
+
if (column === null) {
|
|
76
|
+
if (!current.has(id)) return current;
|
|
77
|
+
const next = new Map(current);
|
|
78
|
+
next.delete(id);
|
|
79
|
+
return next;
|
|
80
|
+
}
|
|
81
|
+
if (current.get(id) === column) return current;
|
|
82
|
+
const next = new Map(current);
|
|
83
|
+
next.set(id, column);
|
|
84
|
+
return next;
|
|
85
|
+
});
|
|
93
86
|
}, []);
|
|
94
|
-
|
|
87
|
+
return { instances, setInstance };
|
|
88
|
+
};
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
const buildRegistrations = <T,>(
|
|
91
|
+
columns: ColumnDef<T>[],
|
|
92
|
+
instances: Map<string, Gtk.ColumnViewColumn>,
|
|
93
|
+
): ColumnRegistration[] => {
|
|
94
|
+
const result: ColumnRegistration[] = [];
|
|
95
|
+
for (const column of columns) {
|
|
96
|
+
const instance = instances.get(column.id);
|
|
97
|
+
if (instance) result.push({ id: column.id, column: instance, sortable: column.sortable ?? false });
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
};
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
externalModel === undefined ? controlledSelection : (externalModel as Gtk.SelectionModel);
|
|
110
|
-
useModelInstallation(widgetRef, installedModel, (widget, model) => widget.setModel(model));
|
|
111
|
-
|
|
112
|
-
const useHeader = externalModel === undefined && typeof props.renderHeader === "function";
|
|
113
|
-
const headers = useRealizedSlots<Gtk.ColumnView>({
|
|
114
|
-
target: useHeader ? widgetRef : null,
|
|
115
|
-
binding: headerFactoryBinding,
|
|
116
|
-
estimatedHeight: props.estimatedRowHeight ?? undefined,
|
|
102
|
+
const useColumnViewWiring = <T, S>(props: NormalizedColumnViewProps<T, S>): ColumnViewWiring<T, S> => {
|
|
103
|
+
const { widgetRef, setRef, collection } = useCollectionWidget<Gtk.ColumnView, T, S>(props);
|
|
104
|
+
|
|
105
|
+
const useHeader = collection.useHeader;
|
|
106
|
+
const headerStore = useCellContainers<Gtk.ColumnView>({
|
|
107
|
+
object: useHeader ? widgetRef : null,
|
|
108
|
+
installer: headerFactoryInstaller,
|
|
109
|
+
estimatedHeight: props.estimatedItemHeight,
|
|
117
110
|
});
|
|
118
111
|
|
|
112
|
+
const { instances, setInstance } = useColumnInstances();
|
|
113
|
+
const registrations = useMemo<ColumnRegistration[]>(
|
|
114
|
+
() => buildRegistrations(props.columns, instances),
|
|
115
|
+
[props.columns, instances],
|
|
116
|
+
);
|
|
117
|
+
|
|
119
118
|
useSortHandler({
|
|
120
119
|
columnView: widgetRef,
|
|
121
120
|
sortColumn: props.sortColumn,
|
|
122
121
|
sortOrder: props.sortOrder,
|
|
123
122
|
onSortChanged: props.onSortChanged,
|
|
124
|
-
columns:
|
|
123
|
+
columns: registrations,
|
|
125
124
|
});
|
|
126
125
|
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
register: registry.register,
|
|
132
|
-
unregister: registry.unregister,
|
|
133
|
-
}),
|
|
134
|
-
[listModel.resolver, registry.register, registry.unregister],
|
|
126
|
+
const expandedIds = props.expandedIds;
|
|
127
|
+
const tree = useMemo<TreeRenderContext>(
|
|
128
|
+
() => createTreeRenderContext(expandedIds, collection.rowId),
|
|
129
|
+
[expandedIds, collection.rowId],
|
|
135
130
|
);
|
|
136
131
|
|
|
137
132
|
return {
|
|
138
133
|
setRef,
|
|
139
|
-
resolver: listModel.resolver,
|
|
140
|
-
headerResolver: listModel.headerResolver,
|
|
141
|
-
headerStore: headers.store,
|
|
142
134
|
useHeader,
|
|
143
|
-
|
|
135
|
+
headerStore,
|
|
136
|
+
headerResolver: collection.headerResolver,
|
|
137
|
+
resolver: collection.resolver as ItemResolver<unknown, unknown>,
|
|
138
|
+
tree,
|
|
139
|
+
setInstance,
|
|
144
140
|
};
|
|
145
141
|
};
|
|
146
142
|
|
|
147
143
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* declared as {@link ColumnViewColumn} children. Supplying an external `model`
|
|
151
|
-
* switches to the uncontrolled form.
|
|
144
|
+
* Renders a Gtk.ColumnView: a multi-column, scrollable list backed by a collection
|
|
145
|
+
* model. Columns are declared through the columns prop, each a {@link ColumnDef}.
|
|
152
146
|
*/
|
|
153
|
-
export const ColumnView = <T = unknown, S = unknown>(props:
|
|
147
|
+
export const ColumnView = <T = unknown, S = unknown>(props: ColumnViewProps<T, S>): ReactNode => {
|
|
148
|
+
const normalized = props as NormalizedColumnViewProps<T, S>;
|
|
149
|
+
const wiring = useColumnViewWiring<T, S>(normalized);
|
|
154
150
|
const {
|
|
155
151
|
ref,
|
|
156
152
|
items,
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
sections,
|
|
154
|
+
columns,
|
|
155
|
+
selectedIds,
|
|
159
156
|
selectionMode,
|
|
160
157
|
onSelectionChanged,
|
|
158
|
+
expandedIds,
|
|
159
|
+
onExpandedChange,
|
|
161
160
|
sortColumn,
|
|
162
161
|
sortOrder,
|
|
163
162
|
onSortChanged,
|
|
164
163
|
renderHeader,
|
|
165
|
-
|
|
166
|
-
children,
|
|
164
|
+
estimatedItemHeight,
|
|
167
165
|
...intrinsicProps
|
|
168
|
-
} =
|
|
169
|
-
|
|
170
|
-
const registry = useColumnRegistry();
|
|
171
|
-
const wiring = useColumnViewWiring<T, S>(
|
|
172
|
-
{
|
|
173
|
-
ref,
|
|
174
|
-
items,
|
|
175
|
-
model,
|
|
176
|
-
selected,
|
|
177
|
-
selectionMode,
|
|
178
|
-
onSelectionChanged,
|
|
179
|
-
sortColumn,
|
|
180
|
-
sortOrder,
|
|
181
|
-
onSortChanged,
|
|
182
|
-
renderHeader,
|
|
183
|
-
estimatedRowHeight,
|
|
184
|
-
} as NormalizedColumnViewProps<T, S>,
|
|
185
|
-
registry,
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const intrinsic = createElement(
|
|
189
|
-
GtkColumnView,
|
|
190
|
-
{ ...intrinsicProps, ref: wiring.setRef },
|
|
191
|
-
<ColumnViewContext.Provider value={wiring.contextValue}>{children}</ColumnViewContext.Provider>,
|
|
192
|
-
);
|
|
166
|
+
} = normalized;
|
|
193
167
|
|
|
194
168
|
return (
|
|
195
169
|
<>
|
|
196
|
-
{
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
170
|
+
<GtkColumnView {...intrinsicProps} ref={wiring.setRef}>
|
|
171
|
+
{columns.map((column) => (
|
|
172
|
+
<ColumnViewColumn
|
|
173
|
+
key={column.id}
|
|
174
|
+
{...column}
|
|
175
|
+
resolver={wiring.resolver}
|
|
176
|
+
tree={wiring.tree}
|
|
177
|
+
estimatedItemHeight={estimatedItemHeight}
|
|
178
|
+
onInstance={wiring.setInstance}
|
|
179
|
+
/>
|
|
180
|
+
))}
|
|
181
|
+
</GtkColumnView>
|
|
182
|
+
<HeaderRenderHost
|
|
183
|
+
useHeader={wiring.useHeader}
|
|
184
|
+
store={wiring.headerStore}
|
|
185
|
+
resolver={wiring.headerResolver}
|
|
186
|
+
renderHeader={renderHeader}
|
|
187
|
+
/>
|
|
204
188
|
</>
|
|
205
189
|
);
|
|
206
190
|
};
|
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
import * as GObject from "@gtkx/gi/gobject";
|
|
2
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Describes one constraint added by `<ConstraintLayout.Constraint>`, relating a
|
|
6
|
+
* target widget attribute to a source attribute of another widget or guide.
|
|
7
|
+
*/
|
|
8
|
+
export type ConstraintProps = {
|
|
9
|
+
/** Name of the target widget or guide. Use "super" or omit to reference the layout's own widget. */
|
|
10
|
+
target?: string;
|
|
11
|
+
targetAttribute: Gtk.ConstraintAttribute;
|
|
12
|
+
/** Relation between the target and source attributes (defaults to equality). */
|
|
13
|
+
relation?: Gtk.ConstraintRelation;
|
|
14
|
+
/** Name of the source widget or guide. Use "super" or omit for a constant constraint. */
|
|
15
|
+
source?: string;
|
|
16
|
+
sourceAttribute?: Gtk.ConstraintAttribute;
|
|
17
|
+
/** Factor applied to the source attribute (defaults to 1). */
|
|
18
|
+
multiplier?: number;
|
|
19
|
+
/** Constant offset added to the relation (defaults to 0). */
|
|
20
|
+
constant?: number;
|
|
21
|
+
/** Constraint strength, higher values winning conflicts (defaults to required). */
|
|
22
|
+
strength?: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Describes an invisible spacing guide added by `<ConstraintLayout.Guide>`,
|
|
27
|
+
* usable as a constraint target under its id.
|
|
28
|
+
*/
|
|
29
|
+
export type ConstraintGuideProps = {
|
|
30
|
+
/** Identifier used to reference this guide from constraints. */
|
|
31
|
+
id: string;
|
|
32
|
+
minWidth?: number;
|
|
33
|
+
minHeight?: number;
|
|
34
|
+
/** Preferred (natural) width. */
|
|
35
|
+
natWidth?: number;
|
|
36
|
+
/** Preferred (natural) height. */
|
|
37
|
+
natHeight?: number;
|
|
38
|
+
maxWidth?: number;
|
|
39
|
+
maxHeight?: number;
|
|
40
|
+
/** Strength of the guide's own size constraints. */
|
|
41
|
+
strength?: Gtk.ConstraintStrength;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Describes constraints authored with the Visual Format Language (VFL), applied by
|
|
46
|
+
* `<ConstraintLayout.Vfl>`.
|
|
47
|
+
*/
|
|
48
|
+
export type ConstraintVflProps = {
|
|
49
|
+
/** VFL lines describing the constraints between named widgets and guides. */
|
|
50
|
+
lines: string[];
|
|
51
|
+
/** Default horizontal spacing used by the layout operator (defaults to 0). */
|
|
52
|
+
hspacing?: number;
|
|
53
|
+
/** Default vertical spacing used by the layout operator (defaults to 0). */
|
|
54
|
+
vspacing?: number;
|
|
55
|
+
};
|
|
4
56
|
|
|
5
57
|
const SUPER_ID = "super";
|
|
6
58
|
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import { GtkConstraintLayout } from "@gtkx/jsx/gtk";
|
|
3
|
-
import {
|
|
3
|
+
import { useMergeRefs } from "@gtkx/react/internal";
|
|
4
4
|
import {
|
|
5
5
|
type Context,
|
|
6
6
|
createContext,
|
|
7
|
-
createElement,
|
|
8
7
|
type ReactNode,
|
|
9
8
|
type Ref,
|
|
10
9
|
type RefObject,
|
|
11
|
-
useCallback,
|
|
12
10
|
useContext,
|
|
13
11
|
useEffect,
|
|
14
12
|
useLayoutEffect,
|
|
15
13
|
useRef,
|
|
16
14
|
} from "react";
|
|
17
|
-
import {
|
|
18
|
-
|
|
15
|
+
import {
|
|
16
|
+
applyConstraint,
|
|
17
|
+
applyGuide,
|
|
18
|
+
applyVfl,
|
|
19
|
+
type ConstraintGuideProps,
|
|
20
|
+
type ConstraintProps,
|
|
21
|
+
type ConstraintVflProps,
|
|
22
|
+
} from "./constraint-layout-apply.js";
|
|
19
23
|
|
|
20
24
|
const ORPHAN_MESSAGE = "<ConstraintLayout.Constraint> / <Guide> / <Vfl> must be a child of <ConstraintLayout>";
|
|
21
25
|
|
|
@@ -28,6 +32,7 @@ const useConstraintLayoutRef = (): RefObject<Gtk.ConstraintLayout | null> => {
|
|
|
28
32
|
return ref;
|
|
29
33
|
};
|
|
30
34
|
|
|
35
|
+
/** Props for {@link ConstraintLayout}. */
|
|
31
36
|
export type ConstraintLayoutProps = {
|
|
32
37
|
children?: ReactNode;
|
|
33
38
|
ref?: Ref<Gtk.ConstraintLayout | null>;
|
|
@@ -68,6 +73,12 @@ const useDeferredContribution = (apply: (layout: Gtk.ConstraintLayout) => () =>
|
|
|
68
73
|
}, deps);
|
|
69
74
|
};
|
|
70
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Installs a Gtk.ConstraintLayout on a host widget and exposes child components to
|
|
78
|
+
* populate it: {@link ConstraintProps | ConstraintLayout.Constraint} for individual constraints,
|
|
79
|
+
* {@link ConstraintGuideProps | ConstraintLayout.Guide} for spacing guides, and {@link ConstraintVflProps | ConstraintLayout.Vfl}
|
|
80
|
+
* for constraints authored in the Visual Format Language.
|
|
81
|
+
*/
|
|
71
82
|
export const ConstraintLayout: ((props: ConstraintLayoutProps) => ReactNode) & {
|
|
72
83
|
Guide: (props: ConstraintGuideProps) => ReactNode;
|
|
73
84
|
Constraint: (props: ConstraintProps) => ReactNode;
|
|
@@ -75,16 +86,10 @@ export const ConstraintLayout: ((props: ConstraintLayoutProps) => ReactNode) & {
|
|
|
75
86
|
} = Object.assign(
|
|
76
87
|
({ children, ref }: ConstraintLayoutProps): ReactNode => {
|
|
77
88
|
const layoutRef = useRef<Gtk.ConstraintLayout | null>(null);
|
|
78
|
-
const
|
|
79
|
-
(layout: Gtk.ConstraintLayout | null): void => {
|
|
80
|
-
layoutRef.current = layout;
|
|
81
|
-
},
|
|
82
|
-
[layoutRef],
|
|
83
|
-
);
|
|
84
|
-
const [, mergedRef] = useForwardedRef<Gtk.ConstraintLayout>(ref, captureLayout);
|
|
89
|
+
const mergedRef = useMergeRefs<Gtk.ConstraintLayout>(ref, layoutRef);
|
|
85
90
|
return (
|
|
86
91
|
<>
|
|
87
|
-
|
|
92
|
+
<GtkConstraintLayout ref={mergedRef} />
|
|
88
93
|
<ConstraintLayoutContext.Provider value={layoutRef}>{children}</ConstraintLayoutContext.Provider>
|
|
89
94
|
</>
|
|
90
95
|
);
|