@gtkx/components 0.21.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +173 -0
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.d.ts.map +1 -0
- package/dist/adw/index.js +2 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/toast.d.ts +10 -0
- package/dist/adw/toast.d.ts.map +1 -0
- package/dist/adw/toast.js +51 -0
- package/dist/adw/toast.js.map +1 -0
- package/dist/adw/types.d.ts +28 -0
- package/dist/adw/types.d.ts.map +1 -0
- package/dist/adw/types.js +2 -0
- package/dist/adw/types.js.map +1 -0
- package/dist/column-view.d.ts +6 -14
- package/dist/column-view.d.ts.map +1 -1
- package/dist/column-view.js +98 -100
- package/dist/column-view.js.map +1 -1
- package/dist/drop-down.d.ts +8 -37
- package/dist/drop-down.d.ts.map +1 -1
- package/dist/drop-down.js +109 -118
- package/dist/drop-down.js.map +1 -1
- package/dist/grid-view.d.ts +4 -11
- package/dist/grid-view.d.ts.map +1 -1
- package/dist/grid-view.js +25 -19
- package/dist/grid-view.js.map +1 -1
- package/dist/index.d.ts +6 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -14
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +50 -0
- package/dist/internal/cells.d.ts.map +1 -0
- package/dist/internal/cells.js +185 -0
- package/dist/internal/cells.js.map +1 -0
- package/dist/internal/collection-index.d.ts +21 -0
- package/dist/internal/collection-index.d.ts.map +1 -0
- package/dist/internal/collection-index.js +106 -0
- package/dist/internal/collection-index.js.map +1 -0
- package/dist/internal/collection-model.d.ts +12 -0
- package/dist/internal/collection-model.d.ts.map +1 -0
- package/dist/internal/collection-model.js +164 -0
- package/dist/internal/collection-model.js.map +1 -0
- package/dist/internal/collection.d.ts +18 -0
- package/dist/internal/collection.d.ts.map +1 -0
- package/dist/internal/collection.js +64 -0
- package/dist/internal/collection.js.map +1 -0
- package/dist/internal/expansion.d.ts +9 -0
- package/dist/internal/expansion.d.ts.map +1 -0
- package/dist/internal/expansion.js +75 -0
- package/dist/internal/expansion.js.map +1 -0
- package/dist/internal/selection.d.ts +12 -0
- package/dist/internal/selection.d.ts.map +1 -0
- package/dist/internal/selection.js +99 -0
- package/dist/internal/selection.js.map +1 -0
- package/dist/internal/use-collection.d.ts +18 -0
- package/dist/internal/use-collection.d.ts.map +1 -0
- package/dist/internal/use-collection.js +42 -0
- package/dist/internal/use-collection.js.map +1 -0
- package/dist/internal/use-widget-ref.d.ts +4 -0
- package/dist/internal/use-widget-ref.d.ts.map +1 -0
- package/dist/internal/use-widget-ref.js +8 -0
- package/dist/internal/use-widget-ref.js.map +1 -0
- package/dist/list-view.d.ts +4 -11
- package/dist/list-view.d.ts.map +1 -1
- package/dist/list-view.js +32 -28
- package/dist/list-view.js.map +1 -1
- package/dist/size-group.d.ts +12 -0
- package/dist/size-group.d.ts.map +1 -0
- package/dist/size-group.js +41 -0
- package/dist/size-group.js.map +1 -0
- package/dist/types.d.ts +104 -161
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +62 -52
- package/src/adw/index.ts +2 -0
- package/src/adw/toast.tsx +74 -0
- package/src/adw/types.ts +32 -0
- package/src/column-view.tsx +184 -184
- package/src/drop-down.tsx +173 -227
- package/src/grid-view.tsx +29 -65
- package/src/index.ts +16 -25
- package/src/internal/cells.tsx +345 -0
- package/src/internal/collection-index.ts +168 -0
- package/src/internal/collection-model.ts +246 -0
- package/src/internal/collection.ts +104 -0
- package/src/internal/expansion.ts +123 -0
- package/src/internal/selection.tsx +159 -0
- package/src/internal/use-collection.tsx +70 -0
- package/src/internal/use-widget-ref.ts +11 -0
- package/src/list-view.tsx +45 -78
- package/src/size-group.tsx +77 -0
- package/src/types.ts +171 -191
- package/dist/collection-view.d.ts +0 -30
- package/dist/collection-view.d.ts.map +0 -1
- package/dist/collection-view.js +0 -52
- package/dist/collection-view.js.map +0 -1
- package/dist/column-view-column.d.ts +0 -17
- package/dist/column-view-column.d.ts.map +0 -1
- package/dist/column-view-column.js +0 -51
- package/dist/column-view-column.js.map +0 -1
- package/dist/constraint-layout-apply.d.ts +0 -6
- package/dist/constraint-layout-apply.d.ts.map +0 -1
- package/dist/constraint-layout-apply.js +0 -107
- package/dist/constraint-layout-apply.js.map +0 -1
- package/dist/constraint-layout.d.ts +0 -13
- package/dist/constraint-layout.d.ts.map +0 -1
- package/dist/constraint-layout.js +0 -85
- package/dist/constraint-layout.js.map +0 -1
- package/dist/contexts/column-view-context.d.ts +0 -17
- package/dist/contexts/column-view-context.d.ts.map +0 -1
- package/dist/contexts/column-view-context.js +0 -10
- package/dist/contexts/column-view-context.js.map +0 -1
- package/dist/hooks/use-drop-down-selection.d.ts +0 -15
- package/dist/hooks/use-drop-down-selection.d.ts.map +0 -1
- package/dist/hooks/use-drop-down-selection.js +0 -35
- package/dist/hooks/use-drop-down-selection.js.map +0 -1
- package/dist/hooks/use-header-menu.d.ts +0 -4
- package/dist/hooks/use-header-menu.d.ts.map +0 -1
- package/dist/hooks/use-header-menu.js +0 -32
- package/dist/hooks/use-header-menu.js.map +0 -1
- package/dist/hooks/use-list-model.d.ts +0 -20
- package/dist/hooks/use-list-model.d.ts.map +0 -1
- package/dist/hooks/use-list-model.js +0 -70
- package/dist/hooks/use-list-model.js.map +0 -1
- package/dist/hooks/use-model-installation.d.ts +0 -9
- package/dist/hooks/use-model-installation.d.ts.map +0 -1
- package/dist/hooks/use-model-installation.js +0 -20
- package/dist/hooks/use-model-installation.js.map +0 -1
- package/dist/hooks/use-realized-slots.d.ts +0 -20
- package/dist/hooks/use-realized-slots.d.ts.map +0 -1
- package/dist/hooks/use-realized-slots.js +0 -66
- package/dist/hooks/use-realized-slots.js.map +0 -1
- package/dist/hooks/use-selection-model.d.ts +0 -12
- package/dist/hooks/use-selection-model.d.ts.map +0 -1
- package/dist/hooks/use-selection-model.js +0 -111
- package/dist/hooks/use-selection-model.js.map +0 -1
- package/dist/hooks/use-sort-handler.d.ts +0 -12
- package/dist/hooks/use-sort-handler.d.ts.map +0 -1
- package/dist/hooks/use-sort-handler.js +0 -87
- package/dist/hooks/use-sort-handler.js.map +0 -1
- package/dist/list-portal-host.d.ts +0 -11
- package/dist/list-portal-host.d.ts.map +0 -1
- package/dist/list-portal-host.js +0 -8
- package/dist/list-portal-host.js.map +0 -1
- package/dist/list-slot.d.ts +0 -14
- package/dist/list-slot.d.ts.map +0 -1
- package/dist/list-slot.js +0 -29
- package/dist/list-slot.js.map +0 -1
- package/dist/menu-items.d.ts +0 -5
- package/dist/menu-items.d.ts.map +0 -1
- package/dist/menu-items.js +0 -48
- package/dist/menu-items.js.map +0 -1
- package/dist/menu.d.ts +0 -18
- package/dist/menu.d.ts.map +0 -1
- package/dist/menu.js +0 -26
- package/dist/menu.js.map +0 -1
- package/dist/utils/item-resolver.d.ts +0 -28
- package/dist/utils/item-resolver.d.ts.map +0 -1
- package/dist/utils/item-resolver.js +0 -111
- package/dist/utils/item-resolver.js.map +0 -1
- package/dist/utils/list-item-flatten.d.ts +0 -32
- package/dist/utils/list-item-flatten.d.ts.map +0 -1
- package/dist/utils/list-item-flatten.js +0 -88
- package/dist/utils/list-item-flatten.js.map +0 -1
- package/dist/utils/position-only-model.d.ts +0 -10
- package/dist/utils/position-only-model.d.ts.map +0 -1
- package/dist/utils/position-only-model.js +0 -83
- package/dist/utils/position-only-model.js.map +0 -1
- package/dist/utils/realized-slot-store.d.ts +0 -30
- package/dist/utils/realized-slot-store.d.ts.map +0 -1
- package/dist/utils/realized-slot-store.js +0 -103
- package/dist/utils/realized-slot-store.js.map +0 -1
- package/src/collection-view.tsx +0 -125
- package/src/column-view-column.tsx +0 -73
- package/src/constraint-layout-apply.ts +0 -121
- package/src/constraint-layout.tsx +0 -130
- package/src/contexts/column-view-context.ts +0 -28
- package/src/hooks/use-drop-down-selection.ts +0 -53
- package/src/hooks/use-header-menu.ts +0 -33
- package/src/hooks/use-list-model.ts +0 -145
- package/src/hooks/use-model-installation.ts +0 -25
- package/src/hooks/use-realized-slots.ts +0 -93
- package/src/hooks/use-selection-model.ts +0 -121
- package/src/hooks/use-sort-handler.ts +0 -102
- package/src/list-portal-host.tsx +0 -27
- package/src/list-slot.tsx +0 -56
- package/src/menu-items.ts +0 -48
- package/src/menu.tsx +0 -34
- package/src/utils/item-resolver.ts +0 -143
- package/src/utils/list-item-flatten.ts +0 -113
- package/src/utils/position-only-model.ts +0 -115
- package/src/utils/realized-slot-store.ts +0 -126
package/src/collection-view.tsx
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import type * as Gio from "@gtkx/gi/gio";
|
|
2
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
-
import { useForwardedRef } from "@gtkx/react";
|
|
4
|
-
import {
|
|
5
|
-
createElement,
|
|
6
|
-
type ElementType,
|
|
7
|
-
type ReactElement,
|
|
8
|
-
type ReactNode,
|
|
9
|
-
type Ref,
|
|
10
|
-
useCallback,
|
|
11
|
-
useRef,
|
|
12
|
-
} from "react";
|
|
13
|
-
import { useListModel } from "./hooks/use-list-model.js";
|
|
14
|
-
import { useModelInstallation } from "./hooks/use-model-installation.js";
|
|
15
|
-
import { type FactoryBinding, useRealizedSlots } from "./hooks/use-realized-slots.js";
|
|
16
|
-
import { useSelectionModel } from "./hooks/use-selection-model.js";
|
|
17
|
-
import { ListPortalHost } from "./list-portal-host.js";
|
|
18
|
-
import type { SlotRenderer } from "./list-slot.js";
|
|
19
|
-
import type { ListItem } from "./types.js";
|
|
20
|
-
import type { ItemResolver } from "./utils/item-resolver.js";
|
|
21
|
-
import type { RealizedSlotStore } from "./utils/realized-slot-store.js";
|
|
22
|
-
|
|
23
|
-
export interface ModelBinding<W extends Gtk.Widget> {
|
|
24
|
-
install(widget: W, model: Gio.ListModel): void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface CollectionViewProps<T, S, W extends Gtk.Widget> {
|
|
28
|
-
element: ElementType;
|
|
29
|
-
intrinsicProps: Record<string, unknown>;
|
|
30
|
-
ref: Ref<W | null> | undefined;
|
|
31
|
-
items: ListItem<T, S>[] | undefined;
|
|
32
|
-
model: Gio.ListModel | undefined;
|
|
33
|
-
renderItem: SlotRenderer<T, S>;
|
|
34
|
-
autoexpand: boolean | undefined;
|
|
35
|
-
renderHeader: ((value: S | undefined) => ReactNode) | undefined;
|
|
36
|
-
estimatedHeight: number | undefined;
|
|
37
|
-
estimatedWidth: number | undefined;
|
|
38
|
-
selected: string[] | null | undefined;
|
|
39
|
-
selectionMode: Gtk.SelectionMode | null | undefined;
|
|
40
|
-
onSelectionChanged: ((ids: string[]) => void) | null | undefined;
|
|
41
|
-
factoryBinding: FactoryBinding<W>;
|
|
42
|
-
headerFactoryBinding: FactoryBinding<W> | undefined;
|
|
43
|
-
modelBinding: ModelBinding<W>;
|
|
44
|
-
children?: ReactNode;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const headerRenderer =
|
|
48
|
-
<T, S>(renderHeader: ((value: S | undefined) => ReactNode) | undefined): SlotRenderer<T, S> =>
|
|
49
|
-
(value) =>
|
|
50
|
-
renderHeader ? renderHeader(value as S | undefined) : null;
|
|
51
|
-
|
|
52
|
-
interface CollectionWiring<T, S, W extends Gtk.Widget> {
|
|
53
|
-
setRef: (value: W | null) => void;
|
|
54
|
-
resolver: ItemResolver<T, S>;
|
|
55
|
-
headerResolver: ItemResolver<T, S>;
|
|
56
|
-
itemStore: RealizedSlotStore;
|
|
57
|
-
headerStore: RealizedSlotStore;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const useCollectionWiring = <T, S, W extends Gtk.Widget>(
|
|
61
|
-
props: CollectionViewProps<T, S, W>,
|
|
62
|
-
): CollectionWiring<T, S, W> => {
|
|
63
|
-
const widgetRef = useRef<W | null>(null);
|
|
64
|
-
const captureWidget = useCallback((value: W | null) => {
|
|
65
|
-
widgetRef.current = value;
|
|
66
|
-
}, []);
|
|
67
|
-
const [, setRef] = useForwardedRef<W>(props.ref, captureWidget);
|
|
68
|
-
|
|
69
|
-
const externalModel = props.model;
|
|
70
|
-
const listModel = useListModel<T, S>(
|
|
71
|
-
externalModel === undefined ? { items: props.items, autoexpand: props.autoexpand } : { model: externalModel },
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const controlledSelection = useSelectionModel<T, S>({
|
|
75
|
-
base: listModel.model,
|
|
76
|
-
selectionMode: props.selectionMode,
|
|
77
|
-
selected: props.selected,
|
|
78
|
-
onSelectionChanged: props.onSelectionChanged,
|
|
79
|
-
resolver: listModel.resolver,
|
|
80
|
-
});
|
|
81
|
-
const installedModel: Gio.ListModel = externalModel === undefined ? controlledSelection : externalModel;
|
|
82
|
-
|
|
83
|
-
const items = useRealizedSlots<W>({
|
|
84
|
-
target: widgetRef,
|
|
85
|
-
binding: props.factoryBinding,
|
|
86
|
-
estimatedHeight: props.estimatedHeight,
|
|
87
|
-
estimatedWidth: props.estimatedWidth,
|
|
88
|
-
});
|
|
89
|
-
const headers = useRealizedSlots<W>({
|
|
90
|
-
target: props.headerFactoryBinding ? widgetRef : null,
|
|
91
|
-
binding: props.headerFactoryBinding ?? props.factoryBinding,
|
|
92
|
-
estimatedHeight: props.estimatedHeight,
|
|
93
|
-
estimatedWidth: props.estimatedWidth,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
useModelInstallation(widgetRef, installedModel, (widget, model) => props.modelBinding.install(widget, model));
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
setRef,
|
|
100
|
-
resolver: listModel.resolver,
|
|
101
|
-
headerResolver: listModel.headerResolver,
|
|
102
|
-
itemStore: items.store,
|
|
103
|
-
headerStore: headers.store,
|
|
104
|
-
};
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export const CollectionView = <T, S, W extends Gtk.Widget>(props: CollectionViewProps<T, S, W>): ReactNode => {
|
|
108
|
-
const { setRef, resolver, headerResolver, itemStore, headerStore } = useCollectionWiring(props);
|
|
109
|
-
const intrinsic: ReactElement = createElement(props.element, { ...props.intrinsicProps, ref: setRef });
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<>
|
|
113
|
-
{intrinsic}
|
|
114
|
-
<ListPortalHost store={itemStore} resolver={resolver} render={props.renderItem} />
|
|
115
|
-
{props.headerFactoryBinding ? (
|
|
116
|
-
<ListPortalHost
|
|
117
|
-
store={headerStore}
|
|
118
|
-
resolver={headerResolver}
|
|
119
|
-
render={headerRenderer<T, S>(props.renderHeader)}
|
|
120
|
-
/>
|
|
121
|
-
) : null}
|
|
122
|
-
{props.children}
|
|
123
|
-
</>
|
|
124
|
-
);
|
|
125
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
-
import { GtkColumnViewColumn, type GtkColumnViewColumnProps } from "@gtkx/jsx/gtk";
|
|
3
|
-
import { createElement, type ReactNode, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
-
import { useColumnViewContext } from "./contexts/column-view-context.js";
|
|
5
|
-
import { useHeaderMenu } from "./hooks/use-header-menu.js";
|
|
6
|
-
import { type FactoryBinding, useRealizedSlots } from "./hooks/use-realized-slots.js";
|
|
7
|
-
import { ListPortalHost } from "./list-portal-host.js";
|
|
8
|
-
import type { SlotRenderer } from "./list-slot.js";
|
|
9
|
-
import type { ColumnViewColumnProps } from "./types.js";
|
|
10
|
-
|
|
11
|
-
const factoryBinding: FactoryBinding<Gtk.ColumnViewColumn> = {
|
|
12
|
-
install: (column, factory) => column.setFactory(factory),
|
|
13
|
-
uninstall: (column) => column.setFactory(null),
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Props for the {@link ColumnViewColumn} component: the raw
|
|
18
|
-
* `GtkColumnViewColumn` element surface (minus its imperative `factory` and
|
|
19
|
-
* `sorter` properties) with its cell wiring replaced by the declarative
|
|
20
|
-
* {@link ColumnViewColumnProps} API.
|
|
21
|
-
*/
|
|
22
|
-
export type ColumnViewColumnComponentProps<T = unknown> = Omit<GtkColumnViewColumnProps, "factory" | "sorter"> &
|
|
23
|
-
ColumnViewColumnProps<T>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A single column of a {@link ColumnView}, rendering each row's cell through
|
|
27
|
-
* the declarative `renderCell` callback and optionally attaching a header
|
|
28
|
-
* context menu.
|
|
29
|
-
*/
|
|
30
|
-
export const ColumnViewColumn = <T = unknown>(props: ColumnViewColumnComponentProps<T>): ReactNode => {
|
|
31
|
-
const { id, title, expand, resizable, fixedWidth, visible, sortable, renderCell, headerMenu } = props;
|
|
32
|
-
const context = useColumnViewContext();
|
|
33
|
-
const [column, setColumn] = useState<Gtk.ColumnViewColumn | null>(null);
|
|
34
|
-
|
|
35
|
-
const captureColumn = useRef((value: Gtk.ColumnViewColumn | null) => {
|
|
36
|
-
setColumn(value);
|
|
37
|
-
}).current;
|
|
38
|
-
|
|
39
|
-
const { store } = useRealizedSlots<Gtk.ColumnViewColumn>({
|
|
40
|
-
target: column,
|
|
41
|
-
binding: factoryBinding,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const registerRef = useRef(context.register);
|
|
45
|
-
registerRef.current = context.register;
|
|
46
|
-
const unregisterRef = useRef(context.unregister);
|
|
47
|
-
unregisterRef.current = context.unregister;
|
|
48
|
-
|
|
49
|
-
useLayoutEffect(() => {
|
|
50
|
-
if (column === null) return;
|
|
51
|
-
registerRef.current({ id, column, sortable: sortable ?? false });
|
|
52
|
-
return () => unregisterRef.current(id);
|
|
53
|
-
}, [column, id, sortable]);
|
|
54
|
-
|
|
55
|
-
const headerMenuPortal = useHeaderMenu(column, headerMenu);
|
|
56
|
-
|
|
57
|
-
const slotRenderer: SlotRenderer<unknown, unknown> = (value, _treeRow, isHeader) =>
|
|
58
|
-
isHeader ? null : renderCell(value as T);
|
|
59
|
-
|
|
60
|
-
const intrinsicProps: Record<string, unknown> = { id, title, ref: captureColumn };
|
|
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;
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<>
|
|
68
|
-
{createElement(GtkColumnViewColumn, intrinsicProps)}
|
|
69
|
-
<ListPortalHost store={store} resolver={context.resolver} render={slotRenderer} />
|
|
70
|
-
{headerMenuPortal}
|
|
71
|
-
</>
|
|
72
|
-
);
|
|
73
|
-
};
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import * as GObject from "@gtkx/gi/gobject";
|
|
2
|
-
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
-
import type { ConstraintGuideProps, ConstraintProps, ConstraintVflProps } from "./types.js";
|
|
4
|
-
|
|
5
|
-
const SUPER_ID = "super";
|
|
6
|
-
|
|
7
|
-
const unknownIdMessage = (role: string, id: string): string =>
|
|
8
|
-
`<ConstraintLayout.Constraint> references unknown id '${id}'. ` +
|
|
9
|
-
`Set name="${id}" on the ${role} widget, or add a <ConstraintLayout.Guide id="${id}">.`;
|
|
10
|
-
|
|
11
|
-
const guideRegistry = new WeakMap<Gtk.ConstraintLayout, Map<string, Gtk.ConstraintGuide>>();
|
|
12
|
-
|
|
13
|
-
const guidesFor = (layout: Gtk.ConstraintLayout): Map<string, Gtk.ConstraintGuide> => {
|
|
14
|
-
let map = guideRegistry.get(layout);
|
|
15
|
-
if (!map) {
|
|
16
|
-
map = new Map<string, Gtk.ConstraintGuide>();
|
|
17
|
-
guideRegistry.set(layout, map);
|
|
18
|
-
}
|
|
19
|
-
return map;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const explicitWidgetName = (widget: Gtk.Widget): string | null => {
|
|
23
|
-
const value = new GObject.Value();
|
|
24
|
-
value.init(GObject.TYPE_STRING);
|
|
25
|
-
widget.getProperty("name", value);
|
|
26
|
-
const name = value.getString();
|
|
27
|
-
return name !== null && name.length > 0 ? name : null;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
function* namedChildren(layout: Gtk.ConstraintLayout): Generator<[string, Gtk.Widget]> {
|
|
31
|
-
const host = layout.getWidget();
|
|
32
|
-
if (!host) return;
|
|
33
|
-
for (let child = host.getFirstChild(); child; child = child.getNextSibling()) {
|
|
34
|
-
const name = explicitWidgetName(child);
|
|
35
|
-
if (name !== null) yield [name, child];
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const findChildByName = (layout: Gtk.ConstraintLayout, id: string): Gtk.Widget | undefined => {
|
|
40
|
-
for (const [name, child] of namedChildren(layout)) {
|
|
41
|
-
if (name === id) return child;
|
|
42
|
-
}
|
|
43
|
-
return undefined;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const resolveConstraintTarget = (
|
|
47
|
-
layout: Gtk.ConstraintLayout,
|
|
48
|
-
id: string | undefined,
|
|
49
|
-
): Gtk.ConstraintTarget | null | undefined => {
|
|
50
|
-
if (id === undefined || id === SUPER_ID) return null;
|
|
51
|
-
return findChildByName(layout, id) ?? guidesFor(layout).get(id);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const constraintViews = (layout: Gtk.ConstraintLayout): Map<string, Gtk.ConstraintTarget> => {
|
|
55
|
-
const views = new Map<string, Gtk.ConstraintTarget>();
|
|
56
|
-
for (const [name, child] of namedChildren(layout)) views.set(name, child);
|
|
57
|
-
for (const [name, guide] of guidesFor(layout)) {
|
|
58
|
-
if (!views.has(name)) views.set(name, guide);
|
|
59
|
-
}
|
|
60
|
-
return views;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const isLayoutLive = (layout: Gtk.ConstraintLayout): boolean => layout.getWidget() !== null;
|
|
64
|
-
|
|
65
|
-
export function applyConstraint(layout: Gtk.ConstraintLayout, props: ConstraintProps): () => void {
|
|
66
|
-
const target = resolveConstraintTarget(layout, props.target);
|
|
67
|
-
if (target === undefined) throw new Error(unknownIdMessage("target", String(props.target)));
|
|
68
|
-
const source = resolveConstraintTarget(layout, props.source);
|
|
69
|
-
if (source === undefined) throw new Error(unknownIdMessage("source", String(props.source)));
|
|
70
|
-
|
|
71
|
-
const constraint = new Gtk.Constraint({
|
|
72
|
-
target,
|
|
73
|
-
targetAttribute: props.targetAttribute,
|
|
74
|
-
relation: props.relation ?? Gtk.ConstraintRelation.EQ,
|
|
75
|
-
source,
|
|
76
|
-
sourceAttribute: props.sourceAttribute ?? Gtk.ConstraintAttribute.NONE,
|
|
77
|
-
multiplier: props.multiplier ?? 1,
|
|
78
|
-
constant: props.constant ?? 0,
|
|
79
|
-
strength: props.strength ?? Gtk.ConstraintStrength.REQUIRED,
|
|
80
|
-
});
|
|
81
|
-
layout.addConstraint(constraint);
|
|
82
|
-
|
|
83
|
-
return () => {
|
|
84
|
-
if (isLayoutLive(layout)) layout.removeConstraint(constraint);
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function applyVfl(layout: Gtk.ConstraintLayout, props: ConstraintVflProps): () => void {
|
|
89
|
-
const views = constraintViews(layout);
|
|
90
|
-
const constraints = layout.addConstraintsFromDescription(
|
|
91
|
-
props.lines,
|
|
92
|
-
props.hspacing ?? 0,
|
|
93
|
-
props.vspacing ?? 0,
|
|
94
|
-
views,
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
return () => {
|
|
98
|
-
if (!isLayoutLive(layout)) return;
|
|
99
|
-
for (const constraint of constraints) layout.removeConstraint(constraint);
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function applyGuide(layout: Gtk.ConstraintLayout, props: ConstraintGuideProps): () => void {
|
|
104
|
-
const guide = new Gtk.ConstraintGuide({
|
|
105
|
-
name: props.id,
|
|
106
|
-
minWidth: props.minWidth,
|
|
107
|
-
minHeight: props.minHeight,
|
|
108
|
-
natWidth: props.natWidth,
|
|
109
|
-
natHeight: props.natHeight,
|
|
110
|
-
maxWidth: props.maxWidth,
|
|
111
|
-
maxHeight: props.maxHeight,
|
|
112
|
-
strength: props.strength,
|
|
113
|
-
});
|
|
114
|
-
layout.addGuide(guide);
|
|
115
|
-
guidesFor(layout).set(props.id, guide);
|
|
116
|
-
|
|
117
|
-
return () => {
|
|
118
|
-
guidesFor(layout).delete(props.id);
|
|
119
|
-
if (isLayoutLive(layout)) layout.removeGuide(guide);
|
|
120
|
-
};
|
|
121
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
-
import { GtkConstraintLayout } from "@gtkx/jsx/gtk";
|
|
3
|
-
import { useForwardedRef } from "@gtkx/react";
|
|
4
|
-
import {
|
|
5
|
-
type Context,
|
|
6
|
-
createContext,
|
|
7
|
-
createElement,
|
|
8
|
-
type ReactNode,
|
|
9
|
-
type Ref,
|
|
10
|
-
type RefObject,
|
|
11
|
-
useCallback,
|
|
12
|
-
useContext,
|
|
13
|
-
useEffect,
|
|
14
|
-
useLayoutEffect,
|
|
15
|
-
useRef,
|
|
16
|
-
} from "react";
|
|
17
|
-
import { applyConstraint, applyGuide, applyVfl } from "./constraint-layout-apply.js";
|
|
18
|
-
import type { ConstraintGuideProps, ConstraintProps, ConstraintVflProps } from "./types.js";
|
|
19
|
-
|
|
20
|
-
const ORPHAN_MESSAGE = "<ConstraintLayout.Constraint> / <Guide> / <Vfl> must be a child of <ConstraintLayout>";
|
|
21
|
-
|
|
22
|
-
const ConstraintLayoutContext: Context<RefObject<Gtk.ConstraintLayout | null> | null> =
|
|
23
|
-
createContext<RefObject<Gtk.ConstraintLayout | null> | null>(null);
|
|
24
|
-
|
|
25
|
-
const useConstraintLayoutRef = (): RefObject<Gtk.ConstraintLayout | null> => {
|
|
26
|
-
const ref = useContext(ConstraintLayoutContext);
|
|
27
|
-
if (!ref) throw new Error(ORPHAN_MESSAGE);
|
|
28
|
-
return ref;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type ConstraintLayoutProps = {
|
|
32
|
-
children?: ReactNode;
|
|
33
|
-
ref?: Ref<Gtk.ConstraintLayout | null>;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
type ContributionCleanupRef = { current: (() => void) | null };
|
|
37
|
-
|
|
38
|
-
const cleanupContribution = (cleanupRef: ContributionCleanupRef): void => {
|
|
39
|
-
const cleanup = cleanupRef.current;
|
|
40
|
-
if (!cleanup) return;
|
|
41
|
-
cleanupRef.current = null;
|
|
42
|
-
cleanup();
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const useLayoutContribution = (apply: (layout: Gtk.ConstraintLayout) => () => void, deps: unknown[]): void => {
|
|
46
|
-
const layoutRef = useConstraintLayoutRef();
|
|
47
|
-
useLayoutEffect(() => {
|
|
48
|
-
const layout = layoutRef.current;
|
|
49
|
-
if (!layout) return;
|
|
50
|
-
return apply(layout);
|
|
51
|
-
}, deps);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const useDeferredContribution = (apply: (layout: Gtk.ConstraintLayout) => () => void, deps: unknown[]): void => {
|
|
55
|
-
const layoutRef = useConstraintLayoutRef();
|
|
56
|
-
const cleanupRef = useRef<(() => void) | null>(null);
|
|
57
|
-
|
|
58
|
-
useLayoutEffect(() => () => cleanupContribution(cleanupRef), deps);
|
|
59
|
-
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
const layout = layoutRef.current;
|
|
62
|
-
if (!layout) return;
|
|
63
|
-
const cleanup = apply(layout);
|
|
64
|
-
cleanupRef.current = cleanup;
|
|
65
|
-
return () => {
|
|
66
|
-
if (cleanupRef.current === cleanup) cleanupContribution(cleanupRef);
|
|
67
|
-
};
|
|
68
|
-
}, deps);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const ConstraintLayout: ((props: ConstraintLayoutProps) => ReactNode) & {
|
|
72
|
-
Guide: (props: ConstraintGuideProps) => ReactNode;
|
|
73
|
-
Constraint: (props: ConstraintProps) => ReactNode;
|
|
74
|
-
Vfl: (props: ConstraintVflProps) => ReactNode;
|
|
75
|
-
} = Object.assign(
|
|
76
|
-
({ children, ref }: ConstraintLayoutProps): ReactNode => {
|
|
77
|
-
const layoutRef = useRef<Gtk.ConstraintLayout | null>(null);
|
|
78
|
-
const captureLayout = useCallback(
|
|
79
|
-
(layout: Gtk.ConstraintLayout | null): void => {
|
|
80
|
-
layoutRef.current = layout;
|
|
81
|
-
},
|
|
82
|
-
[layoutRef],
|
|
83
|
-
);
|
|
84
|
-
const [, mergedRef] = useForwardedRef<Gtk.ConstraintLayout>(ref, captureLayout);
|
|
85
|
-
return (
|
|
86
|
-
<>
|
|
87
|
-
{createElement(GtkConstraintLayout, { ref: mergedRef })}
|
|
88
|
-
<ConstraintLayoutContext.Provider value={layoutRef}>{children}</ConstraintLayoutContext.Provider>
|
|
89
|
-
</>
|
|
90
|
-
);
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
Guide: (props: ConstraintGuideProps): ReactNode => {
|
|
94
|
-
useLayoutContribution(
|
|
95
|
-
(layout) => applyGuide(layout, props),
|
|
96
|
-
[
|
|
97
|
-
props.id,
|
|
98
|
-
props.minWidth,
|
|
99
|
-
props.minHeight,
|
|
100
|
-
props.natWidth,
|
|
101
|
-
props.natHeight,
|
|
102
|
-
props.maxWidth,
|
|
103
|
-
props.maxHeight,
|
|
104
|
-
props.strength,
|
|
105
|
-
],
|
|
106
|
-
);
|
|
107
|
-
return null;
|
|
108
|
-
},
|
|
109
|
-
Constraint: (props: ConstraintProps): ReactNode => {
|
|
110
|
-
useDeferredContribution(
|
|
111
|
-
(layout) => applyConstraint(layout, props),
|
|
112
|
-
[
|
|
113
|
-
props.target,
|
|
114
|
-
props.targetAttribute,
|
|
115
|
-
props.relation,
|
|
116
|
-
props.source,
|
|
117
|
-
props.sourceAttribute,
|
|
118
|
-
props.multiplier,
|
|
119
|
-
props.constant,
|
|
120
|
-
props.strength,
|
|
121
|
-
],
|
|
122
|
-
);
|
|
123
|
-
return null;
|
|
124
|
-
},
|
|
125
|
-
Vfl: (props: ConstraintVflProps): ReactNode => {
|
|
126
|
-
useDeferredContribution((layout) => applyVfl(layout, props), [props.lines, props.hspacing, props.vspacing]);
|
|
127
|
-
return null;
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
);
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
-
import { type Context, createContext, type RefObject, useContext } from "react";
|
|
3
|
-
import type { ItemResolver } from "../utils/item-resolver.js";
|
|
4
|
-
|
|
5
|
-
export interface ColumnRegistration {
|
|
6
|
-
id: string;
|
|
7
|
-
column: Gtk.ColumnViewColumn;
|
|
8
|
-
sortable: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ColumnViewContextValue {
|
|
12
|
-
columnView: RefObject<Gtk.ColumnView | null>;
|
|
13
|
-
resolver: ItemResolver<unknown, unknown>;
|
|
14
|
-
register(registration: ColumnRegistration): void;
|
|
15
|
-
unregister(id: string): void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const ColumnViewContext: Context<ColumnViewContextValue | null> = createContext<ColumnViewContextValue | null>(
|
|
19
|
-
null,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
export const useColumnViewContext = (): ColumnViewContextValue => {
|
|
23
|
-
const context = useContext(ColumnViewContext);
|
|
24
|
-
if (context === null) {
|
|
25
|
-
throw new Error("<ColumnViewColumn> must be rendered inside a <ColumnView>.");
|
|
26
|
-
}
|
|
27
|
-
return context;
|
|
28
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type * as GObject from "@gtkx/gi/gobject";
|
|
2
|
-
import { useSignal } from "@gtkx/react";
|
|
3
|
-
import { type GObjectTarget, resolveGobjectTarget, useGObjectSnapshot } from "@gtkx/react/internal";
|
|
4
|
-
import { useLayoutEffect, useRef } from "react";
|
|
5
|
-
import type { ItemResolver } from "../utils/item-resolver.js";
|
|
6
|
-
|
|
7
|
-
export interface DropDownSelectionTarget extends GObject.Object {
|
|
8
|
-
getSelected(): number;
|
|
9
|
-
setSelected(position: number): void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface DropDownSelectionOptions<T, S> {
|
|
13
|
-
widget: GObjectTarget<DropDownSelectionTarget>;
|
|
14
|
-
resolver: ItemResolver<T, S>;
|
|
15
|
-
selectedId: string | null | undefined;
|
|
16
|
-
onSelectionChanged: ((id: string) => void) | null | undefined;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const GTK_INVALID_LIST_POSITION = 4294967295;
|
|
20
|
-
|
|
21
|
-
const normalizeSelected = (position: number): number =>
|
|
22
|
-
position === GTK_INVALID_LIST_POSITION || position < 0 ? -1 : position;
|
|
23
|
-
|
|
24
|
-
export const useDropDownSelection = <T, S>(options: DropDownSelectionOptions<T, S>): number => {
|
|
25
|
-
const { widget, resolver, selectedId, onSelectionChanged } = options;
|
|
26
|
-
const resolved = resolveGobjectTarget(widget);
|
|
27
|
-
|
|
28
|
-
const selectedPosition = useGObjectSnapshot(widget, "notify::selected", (target) =>
|
|
29
|
-
target === null ? -1 : normalizeSelected(target.getSelected()),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const resolverRef = useRef(resolver);
|
|
33
|
-
resolverRef.current = resolver;
|
|
34
|
-
const onSelectionChangedRef = useRef(onSelectionChanged);
|
|
35
|
-
onSelectionChangedRef.current = onSelectionChanged;
|
|
36
|
-
|
|
37
|
-
useLayoutEffect(() => {
|
|
38
|
-
if (resolved === null || selectedId === undefined || selectedId === null) return;
|
|
39
|
-
const position = resolverRef.current.positionOf(selectedId);
|
|
40
|
-
if (position < 0) return;
|
|
41
|
-
if (normalizeSelected(resolved.getSelected()) !== position) resolved.setSelected(position);
|
|
42
|
-
}, [resolved, selectedId, resolver]);
|
|
43
|
-
|
|
44
|
-
useSignal(widget, "notify::selected", () => {
|
|
45
|
-
if (resolved === null) return;
|
|
46
|
-
const position = normalizeSelected(resolved.getSelected());
|
|
47
|
-
if (position < 0) return;
|
|
48
|
-
const id = resolverRef.current.idOf(position);
|
|
49
|
-
if (id !== undefined) onSelectionChangedRef.current?.(id);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
return selectedPosition;
|
|
53
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as Gio from "@gtkx/gi/gio";
|
|
2
|
-
import * as GObject from "@gtkx/gi/gobject";
|
|
3
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
4
|
-
import { createPortal } from "@gtkx/react";
|
|
5
|
-
import { stateOf } from "@gtkx/react/internal";
|
|
6
|
-
import { type ReactNode, useLayoutEffect, useRef } from "react";
|
|
7
|
-
|
|
8
|
-
const findMenuModel = (container: GObject.Object): Gio.MenuModel | null => {
|
|
9
|
-
for (const child of stateOf(container).children) {
|
|
10
|
-
if (child instanceof Gio.MenuModel) return child;
|
|
11
|
-
}
|
|
12
|
-
return null;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const useHeaderMenu = (column: Gtk.ColumnViewColumn | null, headerMenu: ReactNode): ReactNode => {
|
|
16
|
-
const containerRef = useRef<GObject.Object | null>(null);
|
|
17
|
-
if (containerRef.current === null) {
|
|
18
|
-
containerRef.current = new GObject.Object();
|
|
19
|
-
}
|
|
20
|
-
const container = containerRef.current;
|
|
21
|
-
|
|
22
|
-
useLayoutEffect(() => {
|
|
23
|
-
if (column === null) return;
|
|
24
|
-
if (headerMenu === undefined || headerMenu === null) {
|
|
25
|
-
column.setHeaderMenu(null);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
column.setHeaderMenu(findMenuModel(container));
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
if (headerMenu === undefined || headerMenu === null) return null;
|
|
32
|
-
return createPortal(headerMenu, container, "header-menu");
|
|
33
|
-
};
|