@gtkx/components 1.0.0-rc.4 → 1.1.0
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 +5 -5
- package/dist/column-view.d.ts +2 -2
- package/dist/column-view.d.ts.map +1 -1
- package/dist/column-view.js +14 -12
- package/dist/column-view.js.map +1 -1
- package/dist/grid-view.js +1 -1
- package/dist/grid-view.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +31 -23
- package/dist/internal/cells.d.ts.map +1 -1
- package/dist/internal/cells.js +168 -77
- package/dist/internal/cells.js.map +1 -1
- package/dist/internal/collection-index.d.ts +8 -11
- package/dist/internal/collection-index.d.ts.map +1 -1
- package/dist/internal/collection-index.js +53 -73
- package/dist/internal/collection-index.js.map +1 -1
- package/dist/internal/collection-model.d.ts +24 -4
- package/dist/internal/collection-model.d.ts.map +1 -1
- package/dist/internal/collection-model.js +247 -96
- package/dist/internal/collection-model.js.map +1 -1
- package/dist/internal/collection.d.ts +8 -10
- package/dist/internal/collection.d.ts.map +1 -1
- package/dist/internal/collection.js +38 -48
- package/dist/internal/collection.js.map +1 -1
- package/dist/internal/controlled-sync.d.ts +11 -0
- package/dist/internal/controlled-sync.d.ts.map +1 -0
- package/dist/internal/controlled-sync.js +19 -0
- package/dist/internal/controlled-sync.js.map +1 -0
- package/dist/internal/drop-down.d.ts.map +1 -1
- package/dist/internal/drop-down.js +27 -15
- package/dist/internal/drop-down.js.map +1 -1
- package/dist/internal/expansion.d.ts +3 -2
- package/dist/internal/expansion.d.ts.map +1 -1
- package/dist/internal/expansion.js +73 -48
- package/dist/internal/expansion.js.map +1 -1
- package/dist/internal/keys.d.ts +5 -0
- package/dist/internal/keys.d.ts.map +1 -0
- package/dist/internal/keys.js +13 -0
- package/dist/internal/keys.js.map +1 -0
- package/dist/internal/selection.d.ts +3 -2
- package/dist/internal/selection.d.ts.map +1 -1
- package/dist/internal/selection.js +68 -35
- package/dist/internal/selection.js.map +1 -1
- package/dist/internal/slots.d.ts +10 -0
- package/dist/internal/slots.d.ts.map +1 -0
- package/dist/internal/slots.js +43 -0
- package/dist/internal/slots.js.map +1 -0
- package/dist/internal/tree-expansion.d.ts +19 -0
- package/dist/internal/tree-expansion.d.ts.map +1 -0
- package/dist/internal/tree-expansion.js +72 -0
- package/dist/internal/tree-expansion.js.map +1 -0
- package/dist/internal/tree-order.d.ts +26 -0
- package/dist/internal/tree-order.d.ts.map +1 -0
- package/dist/internal/tree-order.js +109 -0
- package/dist/internal/tree-order.js.map +1 -0
- package/dist/internal/use-collection.d.ts +1 -1
- package/dist/internal/use-collection.d.ts.map +1 -1
- package/dist/internal/use-collection.js +4 -4
- package/dist/internal/use-collection.js.map +1 -1
- package/dist/list-view.d.ts.map +1 -1
- package/dist/list-view.js +6 -6
- package/dist/list-view.js.map +1 -1
- package/dist/types.d.ts +72 -22
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +17 -9
- package/src/column-view.tsx +28 -25
- package/src/grid-view.tsx +1 -1
- package/src/index.ts +3 -0
- package/src/internal/cells.tsx +339 -117
- package/src/internal/collection-index.ts +68 -92
- package/src/internal/collection-model.ts +327 -112
- package/src/internal/collection.ts +45 -65
- package/src/internal/controlled-sync.ts +34 -0
- package/src/internal/drop-down.tsx +41 -20
- package/src/internal/expansion.ts +95 -70
- package/src/internal/keys.ts +18 -0
- package/src/internal/selection.tsx +100 -51
- package/src/internal/slots.ts +67 -0
- package/src/internal/tree-expansion.ts +115 -0
- package/src/internal/tree-order.ts +208 -0
- package/src/internal/use-collection.tsx +5 -5
- package/src/list-view.tsx +14 -10
- package/src/types.ts +78 -20
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/internal/collection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ListItem } from \"../types.js\";\nimport type { CollectionIndex } from \"./collection-index.js\";\nimport type { CollectionModel, SlotRef } from \"./collection-model.js\";\nimport { slotPathAt, slotRefFor } from \"./collection-model.js\";\nimport { findPositions } from \"./tree-order.js\";\n\ntype Collection = Pick<CollectionModel, \"model\" | \"expansion\" | \"rowAt\"> & {\n isTree: boolean;\n itemAt: (ref: SlotRef) => ListItem | undefined;\n sectionFor: (levelPath: string) => unknown;\n idAt: (position: number) => string | null;\n pathAt: (position: number) => string | null;\n positionFor: (id: string) => number;\n positionsFor: (ids: string[]) => number[];\n};\n\nconst NO_POSITIONS: number[] = [];\n\nfunction positionsFor(collectionModel: CollectionModel, ids: string[]): number[] {\n if (ids.length === 0) {\n return NO_POSITIONS;\n }\n\n const { expansion } = collectionModel;\n\n return findPositions(expansion.index, expansion.slots, new Set(ids));\n}\n\nfunction positionFor(collectionModel: CollectionModel, id: string): number {\n const [first = -1] = positionsFor(collectionModel, [id]);\n\n return first;\n}\n\nfunction refAt(collectionModel: CollectionModel, position: number): SlotRef | null {\n if (position < 0) {\n return null;\n }\n\n return slotRefFor(collectionModel.model.getItem(position));\n}\n\nfunction itemAt(index: CollectionIndex, ref: SlotRef): ListItem | undefined {\n return index.itemAt(ref.store.level.path, ref.slot);\n}\n\nfunction idAt(collectionModel: CollectionModel, index: CollectionIndex, position: number): string | null {\n const ref = refAt(collectionModel, position);\n\n return ref === null ? null : (itemAt(index, ref)?.id ?? null);\n}\n\nfunction pathAt(collectionModel: CollectionModel, position: number): string | null {\n const ref = refAt(collectionModel, position);\n\n if (ref === null) {\n return null;\n }\n\n return slotPathAt(ref.store, ref.slot);\n}\n\nfunction isCollectionIdle(collection: Collection): boolean {\n const { expansion } = collection;\n\n return !expansion.isApplying && !expansion.isSyncing;\n}\n\nfunction createCollection(collectionModel: CollectionModel, index: CollectionIndex): Collection {\n return {\n model: collectionModel.model,\n expansion: collectionModel.expansion,\n isTree: index.isTree,\n rowAt: collectionModel.rowAt,\n itemAt: (ref) => itemAt(index, ref),\n sectionFor: index.sectionFor,\n idAt: (position) => idAt(collectionModel, index, position),\n pathAt: (position) => pathAt(collectionModel, position),\n positionFor: (id) => positionFor(collectionModel, id),\n positionsFor: (ids) => positionsFor(collectionModel, ids),\n };\n}\n\nexport { createCollection, isCollectionIdle, type Collection };\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Collection } from "./collection.js";
|
|
2
|
+
type ControlledIds = string[] | null | undefined;
|
|
3
|
+
type ControlledSyncOptions = {
|
|
4
|
+
ids: ControlledIds;
|
|
5
|
+
collection: Collection;
|
|
6
|
+
widget?: object | null | undefined;
|
|
7
|
+
apply: (ids: ControlledIds) => void;
|
|
8
|
+
};
|
|
9
|
+
declare function useControlledSync(options: ControlledSyncOptions): () => void;
|
|
10
|
+
export { useControlledSync };
|
|
11
|
+
//# sourceMappingURL=controlled-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlled-sync.d.ts","sourceRoot":"","sources":["../../src/internal/controlled-sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,KAAK,aAAa,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;AAEjD,KAAK,qBAAqB,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,iBAAS,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,IAAI,CAmBrE;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { startTransition, useEffectEvent, useLayoutEffect, useState } from "react";
|
|
2
|
+
function useControlledSync(options) {
|
|
3
|
+
const { ids, collection, widget } = options;
|
|
4
|
+
const [drift, setDrift] = useState(0);
|
|
5
|
+
const markDrift = () => {
|
|
6
|
+
startTransition(() => {
|
|
7
|
+
setDrift((count) => count + 1);
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
const apply = useEffectEvent((next) => {
|
|
11
|
+
options.apply(next);
|
|
12
|
+
});
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
apply(ids);
|
|
15
|
+
}, [widget, collection, ids, drift]);
|
|
16
|
+
return markDrift;
|
|
17
|
+
}
|
|
18
|
+
export { useControlledSync };
|
|
19
|
+
//# sourceMappingURL=controlled-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlled-sync.js","sourceRoot":"","sources":["../../src/internal/controlled-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAYnF,SAAS,iBAAiB,CAAC,OAA8B;IACrD,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC5C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtC,MAAM,SAAS,GAAG,GAAS,EAAE;QACzB,eAAe,CAAC,GAAG,EAAE;YACjB,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,IAAmB,EAAQ,EAAE;QACvD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,eAAe,CAAC,GAAG,EAAE;QACjB,KAAK,CAAC,GAAG,CAAC,CAAC;IACf,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAErC,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC","sourcesContent":["import { startTransition, useEffectEvent, useLayoutEffect, useState } from \"react\";\nimport type { Collection } from \"./collection.js\";\n\ntype ControlledIds = string[] | null | undefined;\n\ntype ControlledSyncOptions = {\n ids: ControlledIds;\n collection: Collection;\n widget?: object | null | undefined;\n apply: (ids: ControlledIds) => void;\n};\n\nfunction useControlledSync(options: ControlledSyncOptions): () => void {\n const { ids, collection, widget } = options;\n const [drift, setDrift] = useState(0);\n\n const markDrift = (): void => {\n startTransition(() => {\n setDrift((count) => count + 1);\n });\n };\n\n const apply = useEffectEvent((next: ControlledIds): void => {\n options.apply(next);\n });\n\n useLayoutEffect(() => {\n apply(ids);\n }, [widget, collection, ids, drift]);\n\n return markDrift;\n}\n\nexport { useControlledSync };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drop-down.d.ts","sourceRoot":"","sources":["../../src/internal/drop-down.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,OAAO,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"drop-down.d.ts","sourceRoot":"","sources":["../../src/internal/drop-down.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,OAAO,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAIzD,OAAO,KAAK,EAAE,gBAAgB,EAAwC,MAAM,aAAa,CAAC;AAM1F,KAAK,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF,KAAK,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/F,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;CAClD,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA8J5B,iBAAS,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAAE,SAAS,EAAE,WAAW,CAAA;CAAE,GAAG,SAAS,CA8BtF;AAED,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,CAAC"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { GtkLabel, GtkSignalListItemFactory } from "@gtkx/jsx/gtk";
|
|
3
|
-
import { applyWrite } from "@gtkx/react/internal";
|
|
4
3
|
import { omit } from "@gtkx/utils";
|
|
5
|
-
import { useEffectEvent, useLayoutEffect, useRef } from "react";
|
|
6
|
-
import {
|
|
4
|
+
import { useEffectEvent, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import { ItemPortals, useItemCells, useSectionHeader } from "./cells.js";
|
|
7
6
|
import { useCollectionData } from "./use-collection.js";
|
|
8
7
|
import { useWidgetRef } from "./use-widget-ref.js";
|
|
9
8
|
const DROP_DOWN_PROPS = [
|
|
@@ -18,6 +17,9 @@ const DROP_DOWN_PROPS = [
|
|
|
18
17
|
"renderHeader",
|
|
19
18
|
"ref",
|
|
20
19
|
];
|
|
20
|
+
function newApplyState() {
|
|
21
|
+
return { isApplying: false };
|
|
22
|
+
}
|
|
21
23
|
const describeValue = (value) => {
|
|
22
24
|
if (typeof value === "string") {
|
|
23
25
|
return value;
|
|
@@ -32,7 +34,6 @@ const describeValue = (value) => {
|
|
|
32
34
|
const defaultItemContent = (value) => value == null ? null : _jsx(GtkLabel, { children: describeValue(value) });
|
|
33
35
|
const defaultRenderItem = ({ item }) => defaultItemContent(item);
|
|
34
36
|
const faceRenderer = (props) => props.renderItem ?? defaultRenderItem;
|
|
35
|
-
const listRenderer = (props) => props.renderListItem ?? props.renderItem ?? defaultRenderItem;
|
|
36
37
|
const resolvePosition = (widget, collection, selectedId) => {
|
|
37
38
|
const requested = selectedId == null ? -1 : collection.positionFor(selectedId);
|
|
38
39
|
return requested >= 0 ? requested : widget.getSelected();
|
|
@@ -61,15 +62,28 @@ const reportSelectedNotify = (options, tracker) => {
|
|
|
61
62
|
}
|
|
62
63
|
reportKnownSelection(tracker, collection.idAt(widget.getSelected()));
|
|
63
64
|
};
|
|
64
|
-
const applySelectedPosition = (widget, position) => {
|
|
65
|
-
|
|
65
|
+
const applySelectedPosition = (widget, position, state) => {
|
|
66
|
+
state.isApplying = true;
|
|
67
|
+
try {
|
|
66
68
|
widget.selected = position;
|
|
67
|
-
}
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
state.isApplying = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const dispatchSelectedNotify = (context, value, self) => {
|
|
75
|
+
const { options, known, state } = context;
|
|
76
|
+
if (state.isApplying) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
reportSelectedNotify(options, { known, onSelectionChanged: options.props.onSelectionChanged });
|
|
80
|
+
options.props.onNotifySelected?.(value, self);
|
|
68
81
|
};
|
|
69
82
|
const useDropDownSelection = (options) => {
|
|
70
83
|
const { widget, collection } = options;
|
|
71
84
|
const { selectedId } = options.props;
|
|
72
85
|
const known = useRef(null);
|
|
86
|
+
const [applyState] = useState(newApplyState);
|
|
73
87
|
const syncKnownSelection = useEffectEvent((position) => {
|
|
74
88
|
const effectiveId = collection.idAt(position);
|
|
75
89
|
if (effectiveId === null) {
|
|
@@ -84,27 +98,25 @@ const useDropDownSelection = (options) => {
|
|
|
84
98
|
return;
|
|
85
99
|
}
|
|
86
100
|
const position = resolvePosition(widget, collection, selectedId);
|
|
87
|
-
applySelectedPosition(widget, position);
|
|
101
|
+
applySelectedPosition(widget, position, applyState);
|
|
88
102
|
syncKnownSelection(position);
|
|
89
|
-
}, [widget, collection, selectedId]);
|
|
103
|
+
}, [widget, collection, selectedId, applyState]);
|
|
90
104
|
return (value, self) => {
|
|
91
|
-
|
|
92
|
-
options.props.onNotifySelected?.(value, self);
|
|
105
|
+
dispatchSelectedNotify({ options, known, state: applyState }, value, self);
|
|
93
106
|
};
|
|
94
107
|
};
|
|
95
108
|
function DropDownBase(props) {
|
|
96
109
|
const { component: Component, items, sections, renderListItem, renderHeader, ref } = props;
|
|
97
110
|
const rest = omit(props, DROP_DOWN_PROPS);
|
|
98
111
|
const [widget, refCallback] = useWidgetRef(ref);
|
|
99
|
-
const collection = useCollectionData({ items, sections });
|
|
112
|
+
const collection = useCollectionData({ items, sections, isFlat: true });
|
|
100
113
|
const faceCells = useItemCells({ width: -1, height: -1 });
|
|
101
114
|
const listCells = useItemCells({ width: -1, height: -1 });
|
|
102
|
-
const
|
|
115
|
+
const header = useSectionHeader(renderHeader, collection, { width: -1, height: -1 });
|
|
103
116
|
const handleNotifySelected = useDropDownSelection({ widget, collection, props });
|
|
104
|
-
const hasHeader = typeof renderHeader === "function";
|
|
105
117
|
return (_jsxs(_Fragment, { children: [_jsx(Component, { ref: refCallback, model: collection.model, factory: _jsx(GtkSignalListItemFactory, { ...faceCells.handlers }), ...(renderListItem != null && {
|
|
106
118
|
listFactory: _jsx(GtkSignalListItemFactory, { ...listCells.handlers }),
|
|
107
|
-
}), ...
|
|
119
|
+
}), ...header.factoryProps, ...rest, onNotifySelected: handleNotifySelected }), _jsx(ItemPortals, { registry: faceCells, render: faceRenderer(props), collection: collection }), renderListItem != null && (_jsx(ItemPortals, { registry: listCells, render: renderListItem, collection: collection })), header.portals] }));
|
|
108
120
|
}
|
|
109
121
|
export { DropDownBase };
|
|
110
122
|
//# sourceMappingURL=drop-down.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drop-down.js","sourceRoot":"","sources":["../../src/internal/drop-down.tsx"],"names":[],"mappings":"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","sourcesContent":["import type * as GObject from \"@gtkx/gi/gobject\";\nimport type { ElementType, ReactNode, Ref } from \"react\";\nimport { GtkLabel, GtkSignalListItemFactory } from \"@gtkx/jsx/gtk\";\nimport { applyWrite } from \"@gtkx/react/internal\";\nimport { omit } from \"@gtkx/utils\";\nimport { useEffectEvent, useLayoutEffect, useRef } from \"react\";\nimport type { DropDownOwnProps, ListItemRenderArgs, ListItemRenderer } from \"../types.js\";\nimport type { Collection } from \"./collection.js\";\nimport { HeaderPortals, ItemPortals, useHeaderCells, useItemCells } from \"./cells.js\";\nimport { useCollectionData } from \"./use-collection.js\";\nimport { useWidgetRef } from \"./use-widget-ref.js\";\n\ntype SelectableWidget = GObject.Object & { getSelected: () => number; selected: number };\n\ntype DropDownBaseProps = DropDownOwnProps<unknown, unknown> & {\n onNotifySelected?: ((value: number | null, self: SelectableWidget) => void) | null | undefined;\n ref?: Ref<SelectableWidget | null> | undefined;\n} & Record<string, unknown>;\n\ntype SelectionOptions = {\n widget: SelectableWidget | null;\n collection: Collection;\n props: DropDownBaseProps;\n};\n\ntype NotifySelectedHandler = NonNullable<DropDownBaseProps[\"onNotifySelected\"]>;\n\ntype KnownSelection = {\n known: { current: string | null };\n onSelectionChanged: ((id: string) => void) | null | undefined;\n};\n\nconst DROP_DOWN_PROPS: string[] = [\n \"component\",\n \"items\",\n \"sections\",\n \"selectedId\",\n \"onSelectionChanged\",\n \"onNotifySelected\",\n \"renderItem\",\n \"renderListItem\",\n \"renderHeader\",\n \"ref\",\n];\n\nconst describeValue = (value: unknown): string => {\n if (typeof value === \"string\") {\n return value;\n }\n\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n};\n\nconst defaultItemContent = (value: unknown): ReactNode =>\n value == null ? null : <GtkLabel>{describeValue(value)}</GtkLabel>;\n\nconst defaultRenderItem = ({ item }: ListItemRenderArgs<unknown>): ReactNode => defaultItemContent(item);\nconst faceRenderer = (props: DropDownBaseProps): ListItemRenderer<never> => props.renderItem ?? defaultRenderItem;\n\nconst listRenderer = (props: DropDownBaseProps): ListItemRenderer<never> =>\n props.renderListItem ?? props.renderItem ?? defaultRenderItem;\n\nconst resolvePosition = (\n widget: SelectableWidget,\n collection: Collection,\n selectedId: string | null | undefined,\n): number => {\n const requested = selectedId == null ? -1 : collection.positionFor(selectedId);\n\n return requested >= 0 ? requested : widget.getSelected();\n};\n\nconst updateKnownSelection = (\n tracker: KnownSelection,\n effectiveId: string,\n selectedId: string | null | undefined,\n): void => {\n const { known, onSelectionChanged } = tracker;\n const expectedId = selectedId ?? known.current;\n const isNew = effectiveId !== known.current;\n known.current = effectiveId;\n\n if (expectedId !== null && effectiveId !== expectedId && isNew) {\n onSelectionChanged?.(effectiveId);\n }\n};\n\nconst reportKnownSelection = (tracker: KnownSelection, id: string | null): void => {\n const { known, onSelectionChanged } = tracker;\n\n if (id === null || id === known.current) {\n return;\n }\n\n known.current = id;\n onSelectionChanged?.(id);\n};\n\nconst reportSelectedNotify = (options: SelectionOptions, tracker: KnownSelection): void => {\n const { widget, collection } = options;\n\n if (widget === null) {\n return;\n }\n\n reportKnownSelection(tracker, collection.idAt(widget.getSelected()));\n};\n\nconst applySelectedPosition = (widget: SelectableWidget, position: number): void => {\n applyWrite(() => {\n widget.selected = position;\n });\n};\n\nconst useDropDownSelection = (options: SelectionOptions): NotifySelectedHandler => {\n const { widget, collection } = options;\n const { selectedId } = options.props;\n const known = useRef<string | null>(null);\n\n const syncKnownSelection = useEffectEvent((position: number): void => {\n const effectiveId = collection.idAt(position);\n\n if (effectiveId === null) {\n known.current = null;\n\n return;\n }\n\n const { onSelectionChanged } = options.props;\n updateKnownSelection({ known, onSelectionChanged }, effectiveId, selectedId);\n });\n\n useLayoutEffect(() => {\n if (widget === null) {\n return;\n }\n\n const position = resolvePosition(widget, collection, selectedId);\n applySelectedPosition(widget, position);\n syncKnownSelection(position);\n }, [widget, collection, selectedId]);\n\n return (value, self) => {\n reportSelectedNotify(options, { known, onSelectionChanged: options.props.onSelectionChanged });\n options.props.onNotifySelected?.(value, self);\n };\n};\n\nfunction DropDownBase(props: DropDownBaseProps & { component: ElementType }): ReactNode {\n const { component: Component, items, sections, renderListItem, renderHeader, ref } = props;\n const rest = omit(props, DROP_DOWN_PROPS);\n const [widget, refCallback] = useWidgetRef<SelectableWidget>(ref);\n const collection = useCollectionData({ items, sections });\n const faceCells = useItemCells({ width: -1, height: -1 });\n const listCells = useItemCells({ width: -1, height: -1 });\n const headerCells = useHeaderCells({ width: -1, height: -1 });\n const handleNotifySelected = useDropDownSelection({ widget, collection, props });\n const hasHeader = typeof renderHeader === \"function\";\n\n return (\n <>\n <Component\n ref={refCallback}\n model={collection.model}\n factory={<GtkSignalListItemFactory {...faceCells.handlers} />}\n {...(renderListItem != null && {\n listFactory: <GtkSignalListItemFactory {...listCells.handlers} />,\n })}\n {...(hasHeader && { headerFactory: <GtkSignalListItemFactory {...headerCells.handlers} /> })}\n {...rest}\n onNotifySelected={handleNotifySelected}\n />\n <ItemPortals store={faceCells} render={faceRenderer(props)} collection={collection} />\n {renderListItem != null && (\n <ItemPortals store={listCells} render={listRenderer(props)} collection={collection} />\n )}\n {hasHeader && <HeaderPortals store={headerCells} render={renderHeader} collection={collection} />}\n </>\n );\n}\n\nexport { DropDownBase, type DropDownBaseProps };\n"]}
|
|
1
|
+
{"version":3,"file":"drop-down.js","sourceRoot":"","sources":["../../src/internal/drop-down.tsx"],"names":[],"mappings":"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","sourcesContent":["import type * as GObject from \"@gtkx/gi/gobject\";\nimport type { ElementType, ReactNode, Ref } from \"react\";\nimport { GtkLabel, GtkSignalListItemFactory } from \"@gtkx/jsx/gtk\";\nimport { omit } from \"@gtkx/utils\";\nimport { useEffectEvent, useLayoutEffect, useRef, useState } from \"react\";\nimport type { DropDownOwnProps, ListItemRenderArgs, ListItemRenderer } from \"../types.js\";\nimport type { Collection } from \"./collection.js\";\nimport { ItemPortals, useItemCells, useSectionHeader } from \"./cells.js\";\nimport { useCollectionData } from \"./use-collection.js\";\nimport { useWidgetRef } from \"./use-widget-ref.js\";\n\ntype SelectableWidget = GObject.Object & { getSelected: () => number; selected: number };\n\ntype DropDownBaseProps = DropDownOwnProps<unknown, unknown> & {\n onNotifySelected?: ((value: number | null, self: SelectableWidget) => void) | null | undefined;\n ref?: Ref<SelectableWidget | null> | undefined;\n} & Record<string, unknown>;\n\ntype SelectionOptions = {\n widget: SelectableWidget | null;\n collection: Collection;\n props: DropDownBaseProps;\n};\n\ntype NotifySelectedHandler = NonNullable<DropDownBaseProps[\"onNotifySelected\"]>;\ntype ApplyState = { isApplying: boolean };\n\ntype NotifyContext = {\n options: SelectionOptions;\n known: { current: string | null };\n state: ApplyState;\n};\n\ntype KnownSelection = {\n known: { current: string | null };\n onSelectionChanged: ((id: string) => void) | null | undefined;\n};\n\nconst DROP_DOWN_PROPS: string[] = [\n \"component\",\n \"items\",\n \"sections\",\n \"selectedId\",\n \"onSelectionChanged\",\n \"onNotifySelected\",\n \"renderItem\",\n \"renderListItem\",\n \"renderHeader\",\n \"ref\",\n];\n\nfunction newApplyState(): ApplyState {\n return { isApplying: false };\n}\n\nconst describeValue = (value: unknown): string => {\n if (typeof value === \"string\") {\n return value;\n }\n\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n};\n\nconst defaultItemContent = (value: unknown): ReactNode =>\n value == null ? null : <GtkLabel>{describeValue(value)}</GtkLabel>;\n\nconst defaultRenderItem = ({ item }: ListItemRenderArgs<unknown>): ReactNode => defaultItemContent(item);\nconst faceRenderer = (props: DropDownBaseProps): ListItemRenderer<never> => props.renderItem ?? defaultRenderItem;\n\nconst resolvePosition = (\n widget: SelectableWidget,\n collection: Collection,\n selectedId: string | null | undefined,\n): number => {\n const requested = selectedId == null ? -1 : collection.positionFor(selectedId);\n\n return requested >= 0 ? requested : widget.getSelected();\n};\n\nconst updateKnownSelection = (\n tracker: KnownSelection,\n effectiveId: string,\n selectedId: string | null | undefined,\n): void => {\n const { known, onSelectionChanged } = tracker;\n const expectedId = selectedId ?? known.current;\n const isNew = effectiveId !== known.current;\n known.current = effectiveId;\n\n if (expectedId !== null && effectiveId !== expectedId && isNew) {\n onSelectionChanged?.(effectiveId);\n }\n};\n\nconst reportKnownSelection = (tracker: KnownSelection, id: string | null): void => {\n const { known, onSelectionChanged } = tracker;\n\n if (id === null || id === known.current) {\n return;\n }\n\n known.current = id;\n onSelectionChanged?.(id);\n};\n\nconst reportSelectedNotify = (options: SelectionOptions, tracker: KnownSelection): void => {\n const { widget, collection } = options;\n\n if (widget === null) {\n return;\n }\n\n reportKnownSelection(tracker, collection.idAt(widget.getSelected()));\n};\n\nconst applySelectedPosition = (widget: SelectableWidget, position: number, state: ApplyState): void => {\n state.isApplying = true;\n\n try {\n widget.selected = position;\n } finally {\n state.isApplying = false;\n }\n};\n\nconst dispatchSelectedNotify = (context: NotifyContext, value: number | null, self: SelectableWidget): void => {\n const { options, known, state } = context;\n\n if (state.isApplying) {\n return;\n }\n\n reportSelectedNotify(options, { known, onSelectionChanged: options.props.onSelectionChanged });\n options.props.onNotifySelected?.(value, self);\n};\n\nconst useDropDownSelection = (options: SelectionOptions): NotifySelectedHandler => {\n const { widget, collection } = options;\n const { selectedId } = options.props;\n const known = useRef<string | null>(null);\n const [applyState] = useState<ApplyState>(newApplyState);\n\n const syncKnownSelection = useEffectEvent((position: number): void => {\n const effectiveId = collection.idAt(position);\n\n if (effectiveId === null) {\n known.current = null;\n\n return;\n }\n\n const { onSelectionChanged } = options.props;\n updateKnownSelection({ known, onSelectionChanged }, effectiveId, selectedId);\n });\n\n useLayoutEffect(() => {\n if (widget === null) {\n return;\n }\n\n const position = resolvePosition(widget, collection, selectedId);\n applySelectedPosition(widget, position, applyState);\n syncKnownSelection(position);\n }, [widget, collection, selectedId, applyState]);\n\n return (value, self) => {\n dispatchSelectedNotify({ options, known, state: applyState }, value, self);\n };\n};\n\nfunction DropDownBase(props: DropDownBaseProps & { component: ElementType }): ReactNode {\n const { component: Component, items, sections, renderListItem, renderHeader, ref } = props;\n const rest = omit(props, DROP_DOWN_PROPS);\n const [widget, refCallback] = useWidgetRef<SelectableWidget>(ref);\n const collection = useCollectionData({ items, sections, isFlat: true });\n const faceCells = useItemCells({ width: -1, height: -1 });\n const listCells = useItemCells({ width: -1, height: -1 });\n const header = useSectionHeader(renderHeader, collection, { width: -1, height: -1 });\n const handleNotifySelected = useDropDownSelection({ widget, collection, props });\n\n return (\n <>\n <Component\n ref={refCallback}\n model={collection.model}\n factory={<GtkSignalListItemFactory {...faceCells.handlers} />}\n {...(renderListItem != null && {\n listFactory: <GtkSignalListItemFactory {...listCells.handlers} />,\n })}\n {...header.factoryProps}\n {...rest}\n onNotifySelected={handleNotifySelected}\n />\n <ItemPortals registry={faceCells} render={faceRenderer(props)} collection={collection} />\n {renderListItem != null && (\n <ItemPortals registry={listCells} render={renderListItem} collection={collection} />\n )}\n {header.portals}\n </>\n );\n}\n\nexport { DropDownBase, type DropDownBaseProps };\n"]}
|
|
@@ -4,6 +4,7 @@ type ExpansionOptions = {
|
|
|
4
4
|
expandedIds?: string[] | null | undefined;
|
|
5
5
|
onExpandedChange?: ((ids: string[]) => void) | null | undefined;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
type ItemsChangeHandler = (position: number, removed: number, added: number) => void;
|
|
8
|
+
declare function useExpansion(options: ExpansionOptions): ItemsChangeHandler;
|
|
9
|
+
export { useExpansion, type ItemsChangeHandler };
|
|
9
10
|
//# sourceMappingURL=expansion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expansion.d.ts","sourceRoot":"","sources":["../../src/internal/expansion.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"expansion.d.ts","sourceRoot":"","sources":["../../src/internal/expansion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAUlD,KAAK,gBAAgB,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,gBAAgB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CACnE,CAAC;AAQF,KAAK,kBAAkB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAyGrF,iBAAS,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,kBAAkB,CAgBnE;AAED,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,CAAC"}
|
|
@@ -1,73 +1,98 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { isCollectionIdle } from "./collection.js";
|
|
3
|
+
import { useControlledSync } from "./controlled-sync.js";
|
|
4
|
+
import { joinParts } from "./keys.js";
|
|
5
|
+
import { trackPaths } from "./slots.js";
|
|
6
|
+
import { adoptOrder, markExpanded, orderFor } from "./tree-expansion.js";
|
|
7
|
+
import { expandedPathsFor, walkVisible } from "./tree-order.js";
|
|
3
8
|
function newLastExpansion() {
|
|
4
9
|
return { key: "" };
|
|
5
10
|
}
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
ids.push(id);
|
|
9
|
-
}
|
|
11
|
+
function hasSameExpansion(expanded, wanted) {
|
|
12
|
+
return expanded.size === wanted.size && wanted.isSubsetOf(expanded);
|
|
10
13
|
}
|
|
11
|
-
function
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
function toggleOptions(collection, wanted) {
|
|
15
|
+
const { expansion } = collection;
|
|
16
|
+
return {
|
|
17
|
+
index: expansion.index,
|
|
18
|
+
slots: trackPaths(wanted),
|
|
19
|
+
marks: trackPaths(wanted.symmetricDifference(expansion.expanded)),
|
|
20
|
+
visit: (row) => {
|
|
21
|
+
if (row.isMarked) {
|
|
22
|
+
collection.rowAt(row.position)?.setExpanded(row.isOpen);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
};
|
|
16
26
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
27
|
+
function walkApplying(expansion, options) {
|
|
28
|
+
expansion.isApplying = true;
|
|
29
|
+
try {
|
|
30
|
+
return walkVisible(options);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
expansion.isApplying = false;
|
|
34
|
+
}
|
|
22
35
|
}
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
|
|
36
|
+
function applyWanted(collection, expandedIds) {
|
|
37
|
+
const { expansion } = collection;
|
|
38
|
+
const wanted = expandedPathsFor(expansion.index, new Set(expandedIds));
|
|
39
|
+
if (hasSameExpansion(expansion.expanded, wanted)) {
|
|
26
40
|
return;
|
|
27
41
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
const key =
|
|
33
|
-
if (last.key === key) {
|
|
42
|
+
adoptOrder(expansion, walkApplying(expansion, toggleOptions(collection, wanted)));
|
|
43
|
+
}
|
|
44
|
+
function reportExpansion(context) {
|
|
45
|
+
const { expandedIds, expandedPaths } = orderFor(context.collection.expansion);
|
|
46
|
+
const key = joinParts(expandedPaths);
|
|
47
|
+
if (context.last.key === key) {
|
|
34
48
|
return;
|
|
35
49
|
}
|
|
36
|
-
last.key = key;
|
|
37
|
-
onExpandedChange?.(
|
|
50
|
+
context.last.key = key;
|
|
51
|
+
context.onExpandedChange?.([...expandedIds]);
|
|
38
52
|
}
|
|
39
|
-
function
|
|
40
|
-
if (
|
|
53
|
+
function applyControlledExpansion(context, expandedIds) {
|
|
54
|
+
if (!context.collection.isTree) {
|
|
41
55
|
return;
|
|
42
56
|
}
|
|
43
|
-
|
|
57
|
+
applyWanted(context.collection, expandedIds ?? []);
|
|
58
|
+
reportExpansion(context);
|
|
44
59
|
}
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
function isExpansionIdle(collection) {
|
|
61
|
+
return collection.isTree && isCollectionIdle(collection);
|
|
62
|
+
}
|
|
63
|
+
function didRowDrift(collection, change) {
|
|
64
|
+
const path = collection.pathAt(change.position - 1);
|
|
65
|
+
const isExpanded = change.removed === 0 && change.added > 0;
|
|
66
|
+
const isCollapsed = change.added === 0 && change.removed > 0;
|
|
67
|
+
if (path === null || isExpanded === isCollapsed) {
|
|
68
|
+
return false;
|
|
49
69
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
markExpanded(collection.expansion, path, isExpanded);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function observeExpansion(context, change, onDrift) {
|
|
74
|
+
if (!isExpansionIdle(context.collection)) {
|
|
75
|
+
return;
|
|
53
76
|
}
|
|
54
|
-
|
|
55
|
-
|
|
77
|
+
const didDrift = didRowDrift(context.collection, change);
|
|
78
|
+
reportExpansion(context);
|
|
79
|
+
if (didDrift) {
|
|
80
|
+
onDrift();
|
|
56
81
|
}
|
|
57
|
-
report();
|
|
58
82
|
}
|
|
59
83
|
function useExpansion(options) {
|
|
60
84
|
const { collection, expandedIds, onExpandedChange } = options;
|
|
61
85
|
const [last] = useState(newLastExpansion);
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
86
|
+
const context = { collection, last, onExpandedChange };
|
|
87
|
+
const markDrift = useControlledSync({
|
|
88
|
+
ids: expandedIds,
|
|
89
|
+
collection,
|
|
90
|
+
apply: (ids) => {
|
|
91
|
+
applyControlledExpansion(context, ids);
|
|
92
|
+
},
|
|
65
93
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}, [collection, expandedIds]);
|
|
69
|
-
return () => {
|
|
70
|
-
reportWhenIdle(collection, last, expanding, onExpandedChange);
|
|
94
|
+
return (position, removed, added) => {
|
|
95
|
+
observeExpansion(context, { position, removed, added }, markDrift);
|
|
71
96
|
};
|
|
72
97
|
}
|
|
73
98
|
export { useExpansion };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/internal/expansion.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/internal/expansion.ts"],"names":[],"mappings":"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","sourcesContent":["import { useState } from \"react\";\nimport type { Collection } from \"./collection.js\";\nimport type { TreeExpansion } from \"./tree-expansion.js\";\nimport type { VisibleOrder, WalkOptions } from \"./tree-order.js\";\nimport { isCollectionIdle } from \"./collection.js\";\nimport { useControlledSync } from \"./controlled-sync.js\";\nimport { joinParts } from \"./keys.js\";\nimport { trackPaths } from \"./slots.js\";\nimport { adoptOrder, markExpanded, orderFor } from \"./tree-expansion.js\";\nimport { expandedPathsFor, walkVisible } from \"./tree-order.js\";\n\ntype ExpansionOptions = {\n collection: Collection;\n expandedIds?: string[] | null | undefined;\n onExpandedChange?: ((ids: string[]) => void) | null | undefined;\n};\n\ntype ItemsChange = {\n position: number;\n removed: number;\n added: number;\n};\n\ntype ItemsChangeHandler = (position: number, removed: number, added: number) => void;\ntype LastExpansion = { key: string };\n\ntype ExpansionContext = {\n collection: Collection;\n last: LastExpansion;\n onExpandedChange?: ((ids: string[]) => void) | null | undefined;\n};\n\nfunction newLastExpansion(): LastExpansion {\n return { key: \"\" };\n}\n\nfunction hasSameExpansion(expanded: Set<string>, wanted: Set<string>): boolean {\n return expanded.size === wanted.size && wanted.isSubsetOf(expanded);\n}\n\nfunction toggleOptions(collection: Collection, wanted: Set<string>): WalkOptions {\n const { expansion } = collection;\n\n return {\n index: expansion.index,\n slots: trackPaths(wanted),\n marks: trackPaths(wanted.symmetricDifference(expansion.expanded)),\n visit: (row) => {\n if (row.isMarked) {\n collection.rowAt(row.position)?.setExpanded(row.isOpen);\n }\n },\n };\n}\n\nfunction walkApplying(expansion: TreeExpansion, options: WalkOptions): VisibleOrder {\n expansion.isApplying = true;\n\n try {\n return walkVisible(options);\n } finally {\n expansion.isApplying = false;\n }\n}\n\nfunction applyWanted(collection: Collection, expandedIds: string[]): void {\n const { expansion } = collection;\n const wanted = expandedPathsFor(expansion.index, new Set(expandedIds));\n\n if (hasSameExpansion(expansion.expanded, wanted)) {\n return;\n }\n\n adoptOrder(expansion, walkApplying(expansion, toggleOptions(collection, wanted)));\n}\n\nfunction reportExpansion(context: ExpansionContext): void {\n const { expandedIds, expandedPaths } = orderFor(context.collection.expansion);\n const key = joinParts(expandedPaths);\n\n if (context.last.key === key) {\n return;\n }\n\n context.last.key = key;\n context.onExpandedChange?.([...expandedIds]);\n}\n\nfunction applyControlledExpansion(context: ExpansionContext, expandedIds: string[] | null | undefined): void {\n if (!context.collection.isTree) {\n return;\n }\n\n applyWanted(context.collection, expandedIds ?? []);\n reportExpansion(context);\n}\n\nfunction isExpansionIdle(collection: Collection): boolean {\n return collection.isTree && isCollectionIdle(collection);\n}\n\nfunction didRowDrift(collection: Collection, change: ItemsChange): boolean {\n const path = collection.pathAt(change.position - 1);\n const isExpanded = change.removed === 0 && change.added > 0;\n const isCollapsed = change.added === 0 && change.removed > 0;\n\n if (path === null || isExpanded === isCollapsed) {\n return false;\n }\n\n markExpanded(collection.expansion, path, isExpanded);\n\n return true;\n}\n\nfunction observeExpansion(context: ExpansionContext, change: ItemsChange, onDrift: () => void): void {\n if (!isExpansionIdle(context.collection)) {\n return;\n }\n\n const didDrift = didRowDrift(context.collection, change);\n reportExpansion(context);\n\n if (didDrift) {\n onDrift();\n }\n}\n\nfunction useExpansion(options: ExpansionOptions): ItemsChangeHandler {\n const { collection, expandedIds, onExpandedChange } = options;\n const [last] = useState<LastExpansion>(newLastExpansion);\n const context: ExpansionContext = { collection, last, onExpandedChange };\n\n const markDrift = useControlledSync({\n ids: expandedIds,\n collection,\n apply: (ids) => {\n applyControlledExpansion(context, ids);\n },\n });\n\n return (position, removed, added) => {\n observeExpansion(context, { position, removed, added }, markDrift);\n };\n}\n\nexport { useExpansion, type ItemsChangeHandler };\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare const encodePart: (value: string) => string;
|
|
2
|
+
declare const joinParts: (values: string[]) => string;
|
|
3
|
+
declare const decodePartAt: (value: string, offset: number) => string | null;
|
|
4
|
+
export { decodePartAt, encodePart, joinParts };
|
|
5
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/internal/keys.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAA8D,CAAC;AACnG,QAAA,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,KAAG,MAA2D,CAAC;AAElG,QAAA,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAAM,GAAG,IAU9D,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const LENGTH_SEPARATOR = "\u{1}";
|
|
2
|
+
const encodePart = (value) => `${String(value.length)}${LENGTH_SEPARATOR}${value}`;
|
|
3
|
+
const joinParts = (values) => values.map((value) => encodePart(value)).join("");
|
|
4
|
+
const decodePartAt = (value, offset) => {
|
|
5
|
+
const separator = value.indexOf(LENGTH_SEPARATOR, offset);
|
|
6
|
+
if (separator === -1) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const length = Number(value.slice(offset, separator));
|
|
10
|
+
return value.slice(separator + 1, separator + 1 + length);
|
|
11
|
+
};
|
|
12
|
+
export { decodePartAt, encodePart, joinParts };
|
|
13
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/internal/keys.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAEjC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,gBAAgB,GAAG,KAAK,EAAE,CAAC;AACnG,MAAM,SAAS,GAAG,CAAC,MAAgB,EAAU,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAElG,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,MAAc,EAAiB,EAAE;IAClE,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAE1D,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAEtD,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;AAC9D,CAAC,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC","sourcesContent":["const LENGTH_SEPARATOR = \"\\u{1}\";\n\nconst encodePart = (value: string): string => `${String(value.length)}${LENGTH_SEPARATOR}${value}`;\nconst joinParts = (values: string[]): string => values.map((value) => encodePart(value)).join(\"\");\n\nconst decodePartAt = (value: string, offset: number): string | null => {\n const separator = value.indexOf(LENGTH_SEPARATOR, offset);\n\n if (separator === -1) {\n return null;\n }\n\n const length = Number(value.slice(offset, separator));\n\n return value.slice(separator + 1, separator + 1 + length);\n};\n\nexport { decodePartAt, encodePart, joinParts };\n"]}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
3
|
import type { Collection } from "./collection.js";
|
|
4
|
+
import type { ItemsChangeHandler } from "./expansion.js";
|
|
4
5
|
type SelectionOptions = {
|
|
5
6
|
collection: Collection;
|
|
6
7
|
selectedIds?: string[] | null | undefined;
|
|
7
8
|
onSelectionChanged?: ((ids: string[]) => void) | null | undefined;
|
|
8
9
|
selectionMode?: Gtk.SelectionMode | null | undefined;
|
|
9
|
-
onItemsChanged:
|
|
10
|
+
onItemsChanged: ItemsChangeHandler;
|
|
10
11
|
};
|
|
11
12
|
declare function useSelection(options: SelectionOptions): ReactElement;
|
|
12
|
-
export { useSelection
|
|
13
|
+
export { useSelection };
|
|
13
14
|
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/internal/selection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAKzD,KAAK,gBAAgB,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAClE,aAAa,CAAC,EAAE,GAAG,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC;IACrD,cAAc,EAAE,kBAAkB,CAAC;CACtC,CAAC;AAwKF,iBAAS,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAuB7D;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|