@gtkx/react 1.0.0-rc.3 → 1.0.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 +8 -9
- package/dist/adw/config.d.ts +1 -0
- package/dist/adw/config.d.ts.map +1 -1
- package/dist/adw/config.js +1 -0
- package/dist/adw/config.js.map +1 -1
- package/dist/adw/dialog.d.ts +4 -4
- package/dist/adw/dialog.d.ts.map +1 -1
- package/dist/adw/dialog.js +16 -21
- package/dist/adw/dialog.js.map +1 -1
- package/dist/adw/element-behaviors.js +104 -57
- package/dist/adw/element-behaviors.js.map +1 -1
- package/dist/adw/element-config.d.ts +0 -5
- package/dist/adw/element-config.d.ts.map +1 -1
- package/dist/adw/element-config.js +25 -26
- package/dist/adw/element-config.js.map +1 -1
- package/dist/adw/index.d.ts +1 -0
- package/dist/adw/index.d.ts.map +1 -1
- package/dist/adw/index.js +1 -0
- package/dist/adw/index.js.map +1 -1
- package/dist/adw/prop-types.d.ts +35 -4
- package/dist/adw/prop-types.d.ts.map +1 -1
- package/dist/adw/prop-types.js.map +1 -1
- package/dist/bootstrap.js +1 -1
- package/dist/bootstrap.js.map +1 -1
- package/dist/components/application-window.d.ts +5 -0
- package/dist/components/application-window.d.ts.map +1 -0
- package/dist/components/application-window.js +7 -0
- package/dist/components/application-window.js.map +1 -0
- package/dist/components/application.d.ts +3 -8
- package/dist/components/application.d.ts.map +1 -1
- package/dist/components/application.js +22 -6
- package/dist/components/application.js.map +1 -1
- package/dist/components/element.d.ts +13 -2
- package/dist/components/element.d.ts.map +1 -1
- package/dist/components/element.js +39 -15
- package/dist/components/element.js.map +1 -1
- package/dist/components/portaled.d.ts +5 -0
- package/dist/components/portaled.d.ts.map +1 -0
- package/dist/components/portaled.js +8 -0
- package/dist/components/portaled.js.map +1 -0
- package/dist/components/window.d.ts +6 -5
- package/dist/components/window.d.ts.map +1 -1
- package/dist/components/window.js +17 -16
- package/dist/components/window.js.map +1 -1
- package/dist/config.d.ts +7 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/element-behaviors.js +11 -2
- package/dist/element-behaviors.js.map +1 -1
- package/dist/element-config.d.ts +4 -3
- package/dist/element-config.d.ts.map +1 -1
- package/dist/element-config.js +19 -15
- package/dist/element-config.js.map +1 -1
- package/dist/hooks/use-bind-setting.d.ts +8 -7
- package/dist/hooks/use-bind-setting.d.ts.map +1 -1
- package/dist/hooks/use-bind-setting.js +2 -7
- package/dist/hooks/use-bind-setting.js.map +1 -1
- package/dist/hooks/use-merged-refs.d.ts +1 -4
- package/dist/hooks/use-merged-refs.d.ts.map +1 -1
- package/dist/hooks/use-merged-refs.js +1 -1
- package/dist/hooks/use-merged-refs.js.map +1 -1
- package/dist/hooks/use-parent-window.d.ts +3 -1
- package/dist/hooks/use-parent-window.d.ts.map +1 -1
- package/dist/hooks/use-parent-window.js +3 -1
- package/dist/hooks/use-parent-window.js.map +1 -1
- package/dist/hooks/use-presented-instance.d.ts +12 -0
- package/dist/hooks/use-presented-instance.d.ts.map +1 -0
- package/dist/hooks/use-presented-instance.js +27 -0
- package/dist/hooks/use-presented-instance.js.map +1 -0
- package/dist/hooks/use-property.d.ts +4 -3
- package/dist/hooks/use-property.d.ts.map +1 -1
- package/dist/hooks/use-property.js +1 -2
- package/dist/hooks/use-property.js.map +1 -1
- package/dist/hooks/use-setting.d.ts +2 -3
- package/dist/hooks/use-setting.d.ts.map +1 -1
- package/dist/hooks/use-setting.js +2 -3
- package/dist/hooks/use-setting.js.map +1 -1
- package/dist/hooks/use-signal.d.ts +15 -8
- package/dist/hooks/use-signal.d.ts.map +1 -1
- package/dist/hooks/use-signal.js +6 -8
- package/dist/hooks/use-signal.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +8 -5
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +7 -5
- package/dist/internal.js.map +1 -1
- package/dist/prop-types.d.ts +88 -4
- package/dist/prop-types.d.ts.map +1 -1
- package/dist/prop-types.js.map +1 -1
- package/dist/reconciler/apply-props.d.ts +4 -4
- package/dist/reconciler/apply-props.d.ts.map +1 -1
- package/dist/reconciler/apply-props.js +62 -20
- package/dist/reconciler/apply-props.js.map +1 -1
- package/dist/reconciler/behaviors.d.ts +9 -20
- package/dist/reconciler/behaviors.d.ts.map +1 -1
- package/dist/reconciler/behaviors.js +44 -41
- package/dist/reconciler/behaviors.js.map +1 -1
- package/dist/reconciler/devtools.d.ts +6 -0
- package/dist/reconciler/devtools.d.ts.map +1 -0
- package/dist/reconciler/devtools.js +11 -0
- package/dist/reconciler/devtools.js.map +1 -0
- package/dist/reconciler/host-config.d.ts +1 -1
- package/dist/reconciler/host-config.d.ts.map +1 -1
- package/dist/reconciler/host-config.js +17 -26
- package/dist/reconciler/host-config.js.map +1 -1
- package/dist/reconciler/instance.d.ts.map +1 -1
- package/dist/reconciler/instance.js +6 -4
- package/dist/reconciler/instance.js.map +1 -1
- package/dist/reconciler/metadata.d.ts +2 -2
- package/dist/reconciler/metadata.d.ts.map +1 -1
- package/dist/reconciler/metadata.js +9 -11
- package/dist/reconciler/metadata.js.map +1 -1
- package/dist/reconciler/node.d.ts +4 -3
- package/dist/reconciler/node.d.ts.map +1 -1
- package/dist/reconciler/node.js +8 -7
- package/dist/reconciler/node.js.map +1 -1
- package/dist/reconciler/placement.js +20 -20
- package/dist/reconciler/placement.js.map +1 -1
- package/dist/reconciler/registry.d.ts +46 -32
- package/dist/reconciler/registry.d.ts.map +1 -1
- package/dist/reconciler/registry.js +10 -19
- package/dist/reconciler/registry.js.map +1 -1
- package/dist/reconciler/root-element.d.ts +1 -1
- package/dist/reconciler/root-element.d.ts.map +1 -1
- package/dist/reconciler/root-element.js +0 -1
- package/dist/reconciler/root-element.js.map +1 -1
- package/dist/reconciler/root.d.ts +9 -20
- package/dist/reconciler/root.d.ts.map +1 -1
- package/dist/reconciler/root.js +6 -19
- package/dist/reconciler/root.js.map +1 -1
- package/dist/reconciler/signals.d.ts +1 -2
- package/dist/reconciler/signals.d.ts.map +1 -1
- package/dist/reconciler/signals.js +3 -3
- package/dist/reconciler/signals.js.map +1 -1
- package/dist/reconciler/text.js +5 -5
- package/dist/reconciler/text.js.map +1 -1
- package/dist/utils/accessible-props.d.ts +57 -9
- package/dist/utils/accessible-props.d.ts.map +1 -1
- package/dist/utils/accessible-props.js +11 -13
- package/dist/utils/accessible-props.js.map +1 -1
- package/dist/utils/ref-prop.d.ts +0 -6
- package/dist/utils/ref-prop.d.ts.map +1 -1
- package/dist/utils/ref-prop.js +0 -6
- package/dist/utils/ref-prop.js.map +1 -1
- package/dist/utils/settings.d.ts +7 -0
- package/dist/utils/settings.d.ts.map +1 -1
- package/dist/utils/settings.js.map +1 -1
- package/dist/utils/variant.d.ts +33 -1
- package/dist/utils/variant.d.ts.map +1 -1
- package/dist/utils/variant.js.map +1 -1
- package/env.d.ts +11 -4
- package/package.json +5 -5
- package/src/adw/config.ts +1 -0
- package/src/adw/dialog.tsx +23 -25
- package/src/adw/element-behaviors.ts +131 -60
- package/src/adw/element-config.ts +25 -26
- package/src/adw/index.ts +1 -0
- package/src/adw/prop-types.ts +36 -3
- package/src/bootstrap.ts +1 -1
- package/src/components/application-window.tsx +10 -0
- package/src/components/application.tsx +32 -5
- package/src/components/element.tsx +52 -17
- package/src/components/portaled.tsx +14 -0
- package/src/components/window.tsx +25 -23
- package/src/config.ts +13 -8
- package/src/element-behaviors.ts +20 -1
- package/src/element-config.ts +19 -15
- package/src/hooks/use-bind-setting.ts +8 -7
- package/src/hooks/use-merged-refs.ts +1 -1
- package/src/hooks/use-parent-window.ts +3 -1
- package/src/hooks/use-presented-instance.tsx +50 -0
- package/src/hooks/use-property.ts +4 -3
- package/src/hooks/use-setting.ts +2 -3
- package/src/hooks/use-signal.ts +18 -11
- package/src/index.ts +10 -2
- package/src/internal.ts +8 -5
- package/src/prop-types.ts +119 -11
- package/src/reconciler/apply-props.ts +77 -24
- package/src/reconciler/behaviors.ts +77 -50
- package/src/reconciler/devtools.ts +18 -0
- package/src/reconciler/host-config.ts +20 -32
- package/src/reconciler/instance.ts +7 -4
- package/src/reconciler/metadata.ts +11 -14
- package/src/reconciler/node.ts +12 -9
- package/src/reconciler/placement.ts +25 -25
- package/src/reconciler/registry.ts +56 -39
- package/src/reconciler/root-element.ts +4 -2
- package/src/reconciler/root.ts +12 -20
- package/src/reconciler/signals.ts +2 -3
- package/src/reconciler/text.ts +6 -6
- package/src/utils/accessible-props.ts +70 -25
- package/src/utils/ref-prop.ts +0 -6
- package/src/utils/settings.ts +7 -0
- package/src/utils/variant.ts +33 -1
- package/dist/utils/accessible-metadata.d.ts +0 -12
- package/dist/utils/accessible-metadata.d.ts.map +0 -1
- package/dist/utils/accessible-metadata.js +0 -25
- package/dist/utils/accessible-metadata.js.map +0 -1
- package/src/utils/accessible-metadata.ts +0 -31
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TYPE_INVALID, typeFromName, typeInterfaces, typeName, typeParent } from "@gtkx/runtime";
|
|
2
2
|
import { getOrInsert } from "@gtkx/utils";
|
|
3
|
-
import {
|
|
3
|
+
import { constructOnlyProps, constructProps, defaultProps, signals, userEventSignals } from "virtual:gtkx-config";
|
|
4
4
|
import { deferredProps, type ElementBehavior, ELEMENTS } from "./registry.js";
|
|
5
5
|
|
|
6
6
|
type TypeInfo = {
|
|
@@ -9,9 +9,9 @@ type TypeInfo = {
|
|
|
9
9
|
userEventSignals: Set<string>;
|
|
10
10
|
behaviors: ElementBehavior[];
|
|
11
11
|
deferred: Set<string>;
|
|
12
|
-
|
|
12
|
+
declaredConstructOnly: Set<string>;
|
|
13
|
+
isLazy: boolean;
|
|
13
14
|
hasFlush: boolean;
|
|
14
|
-
hasMount: boolean;
|
|
15
15
|
constructOnly: Set<string>;
|
|
16
16
|
construct: Set<string>;
|
|
17
17
|
defaults: Record<string, unknown>;
|
|
@@ -58,10 +58,10 @@ const addAll = <T>(target: Set<T>, source: Iterable<T> | undefined): void => {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
const accumulateAncestor = (info: TypeInfo, ancestor: string): void => {
|
|
61
|
-
Object.assign(info.signals,
|
|
61
|
+
Object.assign(info.signals, signals[ancestor] ?? {});
|
|
62
62
|
addAll(info.userEventSignals, userEventSignals[ancestor]);
|
|
63
|
-
addAll(info.constructOnly,
|
|
64
|
-
addAll(info.construct,
|
|
63
|
+
addAll(info.constructOnly, constructOnlyProps[ancestor]);
|
|
64
|
+
addAll(info.construct, constructProps[ancestor]);
|
|
65
65
|
info.behaviors.push(...(ELEMENTS[ancestor]?.behaviors ?? []));
|
|
66
66
|
};
|
|
67
67
|
|
|
@@ -70,11 +70,8 @@ const applyBehaviorFlags = (info: TypeInfo, behavior: ElementBehavior): void =>
|
|
|
70
70
|
info.hasFlush = true;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
if (behavior.mount !== undefined) {
|
|
74
|
-
info.hasMount = true;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
73
|
addAll(info.deferred, deferredProps(behavior));
|
|
74
|
+
addAll(info.declaredConstructOnly, behavior.constructOnly);
|
|
78
75
|
};
|
|
79
76
|
|
|
80
77
|
const resolveBehaviorFlags = (info: TypeInfo): void => {
|
|
@@ -92,9 +89,9 @@ const buildTypeInfo = (name: string): TypeInfo => {
|
|
|
92
89
|
userEventSignals: new Set(),
|
|
93
90
|
behaviors: [],
|
|
94
91
|
deferred: new Set(),
|
|
95
|
-
|
|
92
|
+
declaredConstructOnly: new Set(),
|
|
93
|
+
isLazy: false,
|
|
96
94
|
hasFlush: false,
|
|
97
|
-
hasMount: false,
|
|
98
95
|
constructOnly: new Set(),
|
|
99
96
|
construct: new Set(),
|
|
100
97
|
defaults: {},
|
|
@@ -105,10 +102,10 @@ const buildTypeInfo = (name: string): TypeInfo => {
|
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
resolveBehaviorFlags(info);
|
|
108
|
-
info.
|
|
105
|
+
info.isLazy = chain.some((ancestor) => ELEMENTS[ancestor]?.isLazy === true);
|
|
109
106
|
|
|
110
107
|
for (const ancestor of chain.toReversed()) {
|
|
111
|
-
Object.assign(info.defaults,
|
|
108
|
+
Object.assign(info.defaults, defaultProps[ancestor] ?? {});
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
return info;
|
package/src/reconciler/node.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { getOrInsert } from "@gtkx/utils";
|
|
|
5
5
|
import type { ElementBehavior, Props } from "./registry.js";
|
|
6
6
|
|
|
7
7
|
type ContentKind = "label" | "buffer" | "tag" | "anchor";
|
|
8
|
-
type HandlerRecord = { signal: string; handler: SignalHandler; wrapped: SignalHandler;
|
|
8
|
+
type HandlerRecord = { signal: string; handler: SignalHandler; wrapped: SignalHandler; isBlockable: boolean };
|
|
9
9
|
type Dispatch = (fn: () => unknown) => unknown;
|
|
10
10
|
|
|
11
11
|
type SignalTarget = {
|
|
@@ -18,10 +18,10 @@ type SignalTarget = {
|
|
|
18
18
|
type PlacedChild = {
|
|
19
19
|
node: PlaceableNode;
|
|
20
20
|
object: GObject.Object;
|
|
21
|
+
typeName: string;
|
|
21
22
|
adopted: GObject.Object | null;
|
|
22
23
|
slot: string;
|
|
23
24
|
behavior: ElementBehavior | null;
|
|
24
|
-
attached: boolean;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
type ElementNode = SignalTarget & {
|
|
@@ -111,16 +111,18 @@ const createElementNode = (
|
|
|
111
111
|
|
|
112
112
|
const createTextNode = (text: string): TextNode => ({ kind: TEXT_KIND, text, parent: null });
|
|
113
113
|
|
|
114
|
-
const
|
|
115
|
-
if (node.kind
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return child === undefined ? null : nodeObject(child);
|
|
114
|
+
const leafElement = (node: PlaceableNode): ElementNode | null => {
|
|
115
|
+
if (node.kind !== LAZY_KIND) {
|
|
116
|
+
return node;
|
|
119
117
|
}
|
|
120
118
|
|
|
121
|
-
|
|
119
|
+
const [child] = node.children;
|
|
120
|
+
|
|
121
|
+
return child === undefined ? null : leafElement(child);
|
|
122
122
|
};
|
|
123
123
|
|
|
124
|
+
const nodeObject = (node: PlaceableNode): GObject.Object | null => leafElement(node)?.object ?? null;
|
|
125
|
+
|
|
124
126
|
const lazyTarget = (node: LazyNode, adopted: GObject.Object): SignalTarget => ({
|
|
125
127
|
object: adopted,
|
|
126
128
|
handlers: node.handlers,
|
|
@@ -129,7 +131,7 @@ const lazyTarget = (node: LazyNode, adopted: GObject.Object): SignalTarget => ({
|
|
|
129
131
|
});
|
|
130
132
|
|
|
131
133
|
const getOrCreateContext = (node: ElementNode, behavior: ElementBehavior): unknown =>
|
|
132
|
-
getOrInsert(node.contexts, behavior, () => behavior.
|
|
134
|
+
getOrInsert(node.contexts, behavior, () => behavior.initialize?.(node.object));
|
|
133
135
|
|
|
134
136
|
export {
|
|
135
137
|
ELEMENT_KIND,
|
|
@@ -141,6 +143,7 @@ export {
|
|
|
141
143
|
createLazyNode,
|
|
142
144
|
createElementNode,
|
|
143
145
|
createTextNode,
|
|
146
|
+
leafElement,
|
|
144
147
|
nodeObject,
|
|
145
148
|
lazyTarget,
|
|
146
149
|
getOrCreateContext,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as GObject from "@gtkx/gi/gobject";
|
|
2
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
-
import { getOrInsert, indexBeforeOrEnd
|
|
3
|
+
import { getOrInsert, indexBeforeOrEnd } from "@gtkx/utils";
|
|
4
4
|
import type { DetachInfo, ElementBehavior, PlaceInfo } from "./registry.js";
|
|
5
5
|
import { applyAdoptedProps, markFlush } from "./apply-props.js";
|
|
6
6
|
import { typeInfoFor } from "./metadata.js";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
getOrCreateContext,
|
|
12
12
|
LAZY_KIND,
|
|
13
13
|
lazyTarget,
|
|
14
|
-
|
|
14
|
+
leafElement,
|
|
15
15
|
type PlaceableNode,
|
|
16
16
|
type PlacedChild,
|
|
17
17
|
} from "./node.js";
|
|
@@ -21,13 +21,20 @@ import { markTextDirty } from "./text.js";
|
|
|
21
21
|
type AttachContext = { parent: ElementNode; entry: PlacedChild; index: number; sibling: GObject.Object | null };
|
|
22
22
|
|
|
23
23
|
const createEntry = (slot: string, node: PlaceableNode): PlacedChild | null => {
|
|
24
|
-
const
|
|
24
|
+
const leaf = leafElement(node);
|
|
25
25
|
|
|
26
|
-
if (
|
|
26
|
+
if (leaf === null) {
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
return {
|
|
30
|
+
return {
|
|
31
|
+
node,
|
|
32
|
+
object: leaf.object,
|
|
33
|
+
typeName: leaf.typeName,
|
|
34
|
+
adopted: null,
|
|
35
|
+
slot,
|
|
36
|
+
behavior: null,
|
|
37
|
+
};
|
|
31
38
|
};
|
|
32
39
|
|
|
33
40
|
const siblingAt = (entries: PlacedChild[], index: number): GObject.Object | null =>
|
|
@@ -80,7 +87,6 @@ const setObjectSlot = (parent: ElementNode, entry: PlacedChild): void => {
|
|
|
80
87
|
writeSlot(parent, entry, entry.object);
|
|
81
88
|
wireBufferView(entry.node, parent);
|
|
82
89
|
entry.behavior = null;
|
|
83
|
-
entry.attached = true;
|
|
84
90
|
};
|
|
85
91
|
|
|
86
92
|
const didAttach = (ctx: AttachContext, behavior: ElementBehavior): boolean => {
|
|
@@ -99,12 +105,19 @@ const didAttach = (ctx: AttachContext, behavior: ElementBehavior): boolean => {
|
|
|
99
105
|
|
|
100
106
|
ctx.entry.behavior = behavior;
|
|
101
107
|
adoptedFrom(ctx.parent, ctx.entry, behavior, claim);
|
|
102
|
-
ctx.entry.attached = true;
|
|
103
108
|
applyLazyProps(ctx.entry);
|
|
104
109
|
|
|
105
110
|
return true;
|
|
106
111
|
};
|
|
107
112
|
|
|
113
|
+
const unclaimedChildError = (parentTypeName: string, childTypeName: string): Error =>
|
|
114
|
+
new Error(
|
|
115
|
+
`<${childTypeName}> cannot be a child of <${parentTypeName}>. Pass it to the ` +
|
|
116
|
+
`<${parentTypeName}> prop that takes it, if there is one, portal it to rootElement with createPortal ` +
|
|
117
|
+
`if it does not belong inside <${parentTypeName}>, or register an attach behavior for ` +
|
|
118
|
+
`<${parentTypeName}> with defineElements from "@gtkx/react/config" if it belongs among its children.`,
|
|
119
|
+
);
|
|
120
|
+
|
|
108
121
|
const runAttach = (parent: ElementNode, entry: PlacedChild, index: number, sibling: GObject.Object | null): void => {
|
|
109
122
|
const ctx: AttachContext = { parent, entry, index, sibling };
|
|
110
123
|
|
|
@@ -114,9 +127,11 @@ const runAttach = (parent: ElementNode, entry: PlacedChild, index: number, sibli
|
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
if (entry.slot
|
|
118
|
-
|
|
130
|
+
if (entry.slot === DEFAULT_SLOT) {
|
|
131
|
+
throw unclaimedChildError(parent.typeName, entry.typeName);
|
|
119
132
|
}
|
|
133
|
+
|
|
134
|
+
setObjectSlot(parent, entry);
|
|
120
135
|
};
|
|
121
136
|
|
|
122
137
|
const attachEntry = (parent: ElementNode, entry: PlacedChild, index: number, sibling: GObject.Object | null): void => {
|
|
@@ -136,9 +151,7 @@ const runDetach = (parent: ElementNode, entry: PlacedChild): void => {
|
|
|
136
151
|
const behavior = entry.behavior;
|
|
137
152
|
|
|
138
153
|
if (behavior === null) {
|
|
139
|
-
|
|
140
|
-
writeSlot(parent, entry, null);
|
|
141
|
-
}
|
|
154
|
+
writeSlot(parent, entry, null);
|
|
142
155
|
|
|
143
156
|
return;
|
|
144
157
|
}
|
|
@@ -147,12 +160,6 @@ const runDetach = (parent: ElementNode, entry: PlacedChild): void => {
|
|
|
147
160
|
};
|
|
148
161
|
|
|
149
162
|
const detachEntry = (parent: ElementNode, entry: PlacedChild): void => {
|
|
150
|
-
if (!entry.attached) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
entry.attached = false;
|
|
155
|
-
|
|
156
163
|
applyWrite(() => {
|
|
157
164
|
runDetach(parent, entry);
|
|
158
165
|
});
|
|
@@ -174,13 +181,6 @@ const getPosition = (entries: PlacedChild[], before: PlaceableNode | null): numb
|
|
|
174
181
|
|
|
175
182
|
const placeNew = (parent: ElementNode, entry: PlacedChild, entries: PlacedChild[], index: number): void => {
|
|
176
183
|
attachEntry(parent, entry, index, siblingAt(entries, index));
|
|
177
|
-
|
|
178
|
-
if (!entry.attached) {
|
|
179
|
-
remove(entries, entry);
|
|
180
|
-
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
184
|
const isInsertedBeforeEnd = index < entries.length - 1;
|
|
185
185
|
const isCannotReorderInPlace = entry.behavior !== null && entry.behavior.reorder === undefined;
|
|
186
186
|
|
|
@@ -5,62 +5,88 @@ type Props = Record<string, unknown>;
|
|
|
5
5
|
|
|
6
6
|
/** Per-child values a slot hook receives while placing or moving one child. */
|
|
7
7
|
type PlaceInfo = {
|
|
8
|
+
/** Name of the slot being filled: `children`, or the prop the child was nested under. */
|
|
8
9
|
slot: string;
|
|
10
|
+
/** Position of the child among the slot's children. */
|
|
9
11
|
index: number;
|
|
12
|
+
/** Object of the preceding child in the slot, or null when the child comes first. */
|
|
10
13
|
sibling: GObject.Object | null;
|
|
14
|
+
/** Object adopted for this child by an earlier attach, or null before it has one. */
|
|
11
15
|
adopted: GObject.Object | null;
|
|
16
|
+
/** Props of the child element. */
|
|
12
17
|
props: Props;
|
|
18
|
+
/** Value `initialize` returned for this behavior on the parent. */
|
|
13
19
|
context: unknown;
|
|
14
20
|
};
|
|
15
21
|
|
|
16
22
|
/** Per-child values a slot hook receives while removing one child. */
|
|
17
23
|
type DetachInfo = {
|
|
24
|
+
/** Name of the slot the child is leaving. */
|
|
18
25
|
slot: string;
|
|
26
|
+
/** Object the container adopted for this child, or null when it adopted none. */
|
|
19
27
|
adopted: GObject.Object | null;
|
|
28
|
+
/** Props of the child element. */
|
|
20
29
|
props: Props;
|
|
30
|
+
/** Value `initialize` returned for this behavior on the parent. */
|
|
21
31
|
context: unknown;
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
/**
|
|
25
|
-
* Customizes how one element type places children and applies props.
|
|
26
|
-
* GObject instance and
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* are not also set as plain GObject properties. `create` builds the GObject for types whose
|
|
30
|
-
* constructor does more than set properties, and is consulted for its own type only, never
|
|
31
|
-
* inherited by subtypes.
|
|
35
|
+
* Customizes how one element type places children and applies props. Hooks other than `create` receive the
|
|
36
|
+
* GObject instance; `update` and `flush` also take the private per-node context `initialize` built, and
|
|
37
|
+
* `attach`, `reorder` and `detach` read it off their info object. Subtypes inherit a type's behaviors,
|
|
38
|
+
* except for `create`, which is consulted only for the type it is registered on.
|
|
32
39
|
*/
|
|
33
40
|
type ElementBehavior<T extends GObject.Object = GObject.Object> = {
|
|
41
|
+
/** Builds the GObject from its construct props, for types whose constructor does more than set properties. */
|
|
34
42
|
create?: (props: Props) => GObject.Object;
|
|
35
|
-
|
|
43
|
+
/** Builds the private per-node context the other hooks receive, once per node. */
|
|
44
|
+
initialize?: (object: T) => unknown;
|
|
45
|
+
/** Places a child, claiming it by returning anything other than `undefined`. */
|
|
36
46
|
attach?: (object: T, child: GObject.Object, info: PlaceInfo) => unknown;
|
|
47
|
+
/** Moves an already-attached child; without it, the whole slot is detached and re-attached in order. */
|
|
37
48
|
reorder?: (object: T, child: GObject.Object, info: PlaceInfo) => unknown;
|
|
49
|
+
/** Removes a child this behavior attached. */
|
|
38
50
|
detach?: (object: T, child: GObject.Object, info: DetachInfo) => void;
|
|
51
|
+
/** Returns the object the container adopts for a child, overriding whatever `attach` returned. */
|
|
39
52
|
resolve?: (object: T, child: GObject.Object) => GObject.Object | null;
|
|
53
|
+
/** Applies changed props and returns the names it consumed, which are then not set as GObject properties. */
|
|
40
54
|
update?: (object: T, prev: Props, next: Props, context: unknown) => Iterable<string> | undefined;
|
|
55
|
+
/** Runs after the commit that touched the node, once every child has been placed. */
|
|
41
56
|
flush?: (object: T, context: unknown) => void;
|
|
42
|
-
|
|
43
|
-
unmount?: (object: T, context: unknown) => void;
|
|
57
|
+
/** Props to withhold from the constructor, leaving them for a later hook to apply. */
|
|
44
58
|
deferred?: string[];
|
|
59
|
+
/**
|
|
60
|
+
* Props the behavior can only apply while the element is being built, because the library exposes no
|
|
61
|
+
* counterpart to whatever applied them. Changing one after it has been applied throws instead of
|
|
62
|
+
* silently applying the new value on top of the old.
|
|
63
|
+
*/
|
|
64
|
+
constructOnly?: string[];
|
|
45
65
|
};
|
|
46
66
|
|
|
47
67
|
/** A named export in a module, referenced as plain data (the module is never imported at runtime). */
|
|
48
|
-
type ModuleExport = {
|
|
68
|
+
type ModuleExport = {
|
|
69
|
+
/** Specifier the export is imported from. */
|
|
70
|
+
module: string;
|
|
71
|
+
/** Identifier the module exports it under. */
|
|
72
|
+
export: string;
|
|
73
|
+
};
|
|
49
74
|
|
|
50
75
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* its type, an optional component that wraps the generated element, the base props interface its
|
|
54
|
-
* generated props extend, and the GObject properties to leave out of the generated props (those a
|
|
55
|
-
* behavior already writes from children, such as the `child` and `content` properties). `component`,
|
|
56
|
-
* `props` and `omitProps` are inert at runtime; they are read only by codegen.
|
|
76
|
+
* How one GLib type is rendered. `component`, `props` and `omittedProps` are inert at runtime; they are
|
|
77
|
+
* read only by codegen.
|
|
57
78
|
*/
|
|
58
79
|
type ElementConfig<T extends GObject.Object = GObject.Object> = {
|
|
59
|
-
|
|
80
|
+
/** The element has no GObject of its own; its parent container creates one, as it does for pages. */
|
|
81
|
+
isLazy?: boolean;
|
|
82
|
+
/** Behaviors bound to the type, consulted in registration order and inherited by its subtypes. */
|
|
60
83
|
behaviors?: ElementBehavior<T>[];
|
|
84
|
+
/** Component that wraps the generated element. */
|
|
61
85
|
component?: ModuleExport;
|
|
86
|
+
/** Base props interface the generated props extend. */
|
|
62
87
|
props?: ModuleExport;
|
|
63
|
-
|
|
88
|
+
/** GObject properties to leave out of the generated props, such as those a behavior writes from children. */
|
|
89
|
+
omittedProps?: string[];
|
|
64
90
|
};
|
|
65
91
|
|
|
66
92
|
/**
|
|
@@ -69,7 +95,6 @@ type ElementConfig<T extends GObject.Object = GObject.Object> = {
|
|
|
69
95
|
*/
|
|
70
96
|
const ELEMENTS: Record<string, ElementConfig> = {};
|
|
71
97
|
|
|
72
|
-
/** Props a behavior applies after construction; the constructor is never given them. */
|
|
73
98
|
const deferredProps = (behavior: ElementBehavior): string[] => behavior.deferred ?? [];
|
|
74
99
|
|
|
75
100
|
const mergeBehaviors = (base: ElementConfig, added: ElementBehavior[], isPrepended: boolean): ElementBehavior[] => {
|
|
@@ -79,19 +104,19 @@ const mergeBehaviors = (base: ElementConfig, added: ElementBehavior[], isPrepend
|
|
|
79
104
|
};
|
|
80
105
|
|
|
81
106
|
const mergeConfigEntry = (base: ElementConfig, added: ElementConfig<never>, isPrepended = false): ElementConfig => {
|
|
82
|
-
const { behaviors,
|
|
107
|
+
const { behaviors, isLazy, omittedProps, ...rest } = added;
|
|
83
108
|
const entry: ElementConfig = { ...base, ...rest };
|
|
84
109
|
|
|
85
110
|
if (behaviors !== undefined) {
|
|
86
111
|
entry.behaviors = mergeBehaviors(base, behaviors as ElementBehavior[], isPrepended);
|
|
87
112
|
}
|
|
88
113
|
|
|
89
|
-
if (
|
|
90
|
-
entry.
|
|
114
|
+
if (omittedProps !== undefined) {
|
|
115
|
+
entry.omittedProps = [...(base.omittedProps ?? []), ...omittedProps];
|
|
91
116
|
}
|
|
92
117
|
|
|
93
|
-
if (
|
|
94
|
-
entry.
|
|
118
|
+
if (isLazy === true) {
|
|
119
|
+
entry.isLazy = true;
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
return entry;
|
|
@@ -99,9 +124,9 @@ const mergeConfigEntry = (base: ElementConfig, added: ElementConfig<never>, isPr
|
|
|
99
124
|
|
|
100
125
|
/**
|
|
101
126
|
* Merges maps of {@link ElementConfig} keyed by GLib type name into one, concatenating each type's
|
|
102
|
-
* behaviors and omitted props in the order the maps are given (an earlier map's behaviors come first)
|
|
103
|
-
*
|
|
104
|
-
* with its behaviors.
|
|
127
|
+
* behaviors and omitted props in the order the maps are given (an earlier map's behaviors come first),
|
|
128
|
+
* taking the last component and props seen, and marking a type lazy if any map does. Use it to combine
|
|
129
|
+
* an app's element config with its behaviors.
|
|
105
130
|
*/
|
|
106
131
|
const mergeElementConfigs = (...maps: Record<string, ElementConfig<never>>[]): Record<string, ElementConfig> => {
|
|
107
132
|
const merged: Record<string, ElementConfig> = {};
|
|
@@ -115,23 +140,17 @@ const mergeElementConfigs = (...maps: Record<string, ElementConfig<never>>[]): R
|
|
|
115
140
|
return merged;
|
|
116
141
|
};
|
|
117
142
|
|
|
118
|
-
/**
|
|
119
|
-
* Registers a map of {@link ElementConfig} keyed by GLib type name, merging each entry into the
|
|
120
|
-
* registry. Behaviors are appended by default (the framework's built-ins register this way); pass
|
|
121
|
-
* `{ prepend: true }` for an app's own configuration so its behaviors are consulted before the
|
|
122
|
-
* built-ins for the same slot, letting it override them regardless of registration order.
|
|
123
|
-
*/
|
|
124
143
|
const registerElements = (
|
|
125
144
|
map: Record<string, ElementConfig<never>>,
|
|
126
|
-
options: {
|
|
145
|
+
options: { isPrepended?: boolean } = {},
|
|
127
146
|
): void => {
|
|
128
147
|
for (const [type, config] of Object.entries(map)) {
|
|
129
|
-
ELEMENTS[type] = mergeConfigEntry(ELEMENTS[type] ?? {}, config, options.
|
|
148
|
+
ELEMENTS[type] = mergeConfigEntry(ELEMENTS[type] ?? {}, config, options.isPrepended === true);
|
|
130
149
|
}
|
|
131
150
|
};
|
|
132
151
|
|
|
133
152
|
/**
|
|
134
|
-
* Identity helper that types the module named by
|
|
153
|
+
* Identity helper that types the module named by `elements.behaviors` in `gtkx.config.ts`, enabling
|
|
135
154
|
* editor autocompletion and type checking. Key each entry by GLib type name and write each behavior
|
|
136
155
|
* with {@link defineBehavior} so its hooks receive the concrete GObject class.
|
|
137
156
|
*/
|
|
@@ -150,11 +169,9 @@ const defineElements = (elements: Record<string, ElementConfig<never>>): Record<
|
|
|
150
169
|
*/
|
|
151
170
|
const defineBehavior = <T extends GObject.Object>(hooks: ElementBehavior<T>): ElementBehavior<never> => hooks;
|
|
152
171
|
|
|
153
|
-
/** Spreads one config across many GLib type names. */
|
|
154
172
|
const forTypes = (types: string[], config: ElementConfig<never>): Record<string, ElementConfig<never>> =>
|
|
155
173
|
Object.fromEntries(types.map((type) => [type, config]));
|
|
156
174
|
|
|
157
|
-
/** References a base props interface exported from `@gtkx/react/internal`. */
|
|
158
175
|
const internal = (name: string): ModuleExport => ({ module: "@gtkx/react/internal", export: name });
|
|
159
176
|
|
|
160
177
|
export {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** The type of the shared {@link rootElement} marker used as a default top-level container. */
|
|
2
|
-
type RootElement = {
|
|
2
|
+
type RootElement = {
|
|
3
|
+
/** Marks a container as the top level rather than a GObject; `isRootElement` tests for it. */
|
|
4
|
+
[ROOT_ELEMENT_BRAND]: true;
|
|
5
|
+
};
|
|
3
6
|
|
|
4
7
|
const ROOT_ELEMENT_BRAND: unique symbol = Symbol.for("gtkx:root-element");
|
|
5
8
|
/** A shared marker value used as the default top-level container for rendering and portals. */
|
|
6
9
|
const rootElement: RootElement = { [ROOT_ELEMENT_BRAND]: true };
|
|
7
10
|
|
|
8
|
-
/** Reports whether an arbitrary value is a {@link rootElement} marker. */
|
|
9
11
|
const isRootElement = (value: unknown): value is RootElement =>
|
|
10
12
|
typeof value === "object" && value !== null && Object.hasOwn(value, ROOT_ELEMENT_BRAND);
|
|
11
13
|
|
package/src/reconciler/root.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ErrorInfo, ReactNode, ReactPortal } from "react";
|
|
|
2
2
|
import * as Gdk from "@gtkx/gi/gdk";
|
|
3
3
|
import { createLogger, type Logger } from "@gtkx/utils";
|
|
4
4
|
import { ConcurrentRoot } from "react-reconciler/constants.js";
|
|
5
|
+
import { injectIntoDevTools } from "./devtools.js";
|
|
5
6
|
import { type Container, reconciler } from "./host-config.js";
|
|
6
7
|
import { rootElement } from "./root-element.js";
|
|
7
8
|
|
|
@@ -15,15 +16,19 @@ type RootErrorCallbacks = {
|
|
|
15
16
|
|
|
16
17
|
type ReconcilerRootOptions = RootErrorCallbacks & { containerInfo: Container };
|
|
17
18
|
|
|
18
|
-
/** A root
|
|
19
|
+
/** A render root whose updates and teardown the caller drives, with error handling left to it. */
|
|
19
20
|
type ReconcilerRoot = {
|
|
21
|
+
/** Mounts an element tree into the container, or updates the tree already mounted there. */
|
|
20
22
|
update: (element: ReactNode) => void;
|
|
23
|
+
/** Hands the root to `teardown`, then stops {@link quit} from unmounting the container. */
|
|
21
24
|
unmount: (teardown: (root: ReconcilerRoot) => Promise<void>) => Promise<void>;
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
/** The object {@link createRoot} returns: it renders an element tree into a container and can tear it down. */
|
|
25
28
|
type Root = {
|
|
29
|
+
/** Mounts an element tree into the container, or updates the tree already mounted there. */
|
|
26
30
|
render: (element: ReactNode) => void;
|
|
31
|
+
/** Unmounts the rendered tree and runs its effect cleanups. */
|
|
27
32
|
unmount: () => void;
|
|
28
33
|
};
|
|
29
34
|
|
|
@@ -49,12 +54,6 @@ function createErrorHandlerSlot(): ErrorHandlerSlot {
|
|
|
49
54
|
};
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
/**
|
|
53
|
-
* Installs a process-wide handler for errors thrown while rendering or applying an update.
|
|
54
|
-
*
|
|
55
|
-
* @param handler The handler to install.
|
|
56
|
-
* @returns The previously installed handler, or null.
|
|
57
|
-
*/
|
|
58
57
|
const setReconcilerErrorHandler = (handler: ErrorHandler): ErrorHandler | null => errorHandlerSlot.set(handler);
|
|
59
58
|
|
|
60
59
|
const rethrowUncaughtRenderError = (error: unknown): never => {
|
|
@@ -66,6 +65,8 @@ const logCaughtRenderError = (error: unknown): void => {
|
|
|
66
65
|
};
|
|
67
66
|
|
|
68
67
|
const openContainer = (containerInfo: Container, callbacks: RootErrorCallbacks): OpaqueRoot => {
|
|
68
|
+
injectIntoDevTools(reconciler);
|
|
69
|
+
|
|
69
70
|
const container = reconciler.createContainer(
|
|
70
71
|
containerInfo,
|
|
71
72
|
ConcurrentRoot,
|
|
@@ -97,12 +98,6 @@ const unmountContainer = (container: OpaqueRoot): void => {
|
|
|
97
98
|
activeRoots.delete(container);
|
|
98
99
|
};
|
|
99
100
|
|
|
100
|
-
/**
|
|
101
|
-
* Creates a root that mounts an element tree into a container, routing render errors to the supplied callbacks.
|
|
102
|
-
*
|
|
103
|
-
* @param options The container to render into and the error callbacks to route failures to.
|
|
104
|
-
* @returns A {@link ReconcilerRoot}.
|
|
105
|
-
*/
|
|
106
101
|
const createReconcilerRoot = (options: ReconcilerRootOptions): ReconcilerRoot => {
|
|
107
102
|
const container = openContainer(options.containerInfo, options);
|
|
108
103
|
|
|
@@ -120,10 +115,10 @@ const createReconcilerRoot = (options: ReconcilerRootOptions): ReconcilerRoot =>
|
|
|
120
115
|
};
|
|
121
116
|
|
|
122
117
|
/**
|
|
123
|
-
* Creates a render root for a GTKX application.
|
|
118
|
+
* Creates a render root for a GTKX application. Uncaught render errors are rethrown and errors caught by an
|
|
119
|
+
* error boundary are logged.
|
|
124
120
|
*
|
|
125
|
-
* @param container The
|
|
126
|
-
* @returns A {@link Root} exposing render and unmount.
|
|
121
|
+
* @param container The GObject to render into; defaults to the shared {@link rootElement}, which holds no object.
|
|
127
122
|
*/
|
|
128
123
|
const createRoot = (container: Container = rootElement): Root => {
|
|
129
124
|
const opaque = openContainer(container, {
|
|
@@ -153,10 +148,7 @@ const quit = (): typeof Gdk.EVENT_STOP => {
|
|
|
153
148
|
/**
|
|
154
149
|
* Renders children into a container other than the surrounding tree.
|
|
155
150
|
*
|
|
156
|
-
* @param
|
|
157
|
-
* @param container The GObject, application, or {@link rootElement} to render into.
|
|
158
|
-
* @param key An optional stable key.
|
|
159
|
-
* @returns A React portal.
|
|
151
|
+
* @param container The GObject to render into, or {@link rootElement} to render at the top level.
|
|
160
152
|
*/
|
|
161
153
|
const createPortal = (children: ReactNode, container: Container, key?: string): ReactPortal =>
|
|
162
154
|
reconciler.createPortal(children, container, null, key ?? null) as unknown as ReactPortal;
|
|
@@ -36,7 +36,7 @@ const invokeHandler = (
|
|
|
36
36
|
|
|
37
37
|
const wrapHandler = (target: SignalTarget, record: HandlerRecord, notifyProperty: string | null): SignalHandler =>
|
|
38
38
|
(...args: unknown[]): unknown => {
|
|
39
|
-
if (record.
|
|
39
|
+
if (record.isBlockable && isApplyingWrite()) {
|
|
40
40
|
return undefined;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -58,7 +58,7 @@ const connectHandler = (target: SignalTarget, prop: string, signal: string, hand
|
|
|
58
58
|
|
|
59
59
|
const isBlockable = isBlockableSignal(typeInfoFor(target.typeName), signal);
|
|
60
60
|
const notifyProperty = getNotifyProperty(signal);
|
|
61
|
-
const record: HandlerRecord = { signal, handler, wrapped: (): undefined => undefined,
|
|
61
|
+
const record: HandlerRecord = { signal, handler, wrapped: (): undefined => undefined, isBlockable };
|
|
62
62
|
record.wrapped = wrapHandler(target, record, notifyProperty);
|
|
63
63
|
target.object.on(signal, record.wrapped);
|
|
64
64
|
target.handlers.set(prop, record);
|
|
@@ -85,7 +85,6 @@ const disconnectAllHandlers = (target: SignalTarget): void => {
|
|
|
85
85
|
|
|
86
86
|
export {
|
|
87
87
|
applyWrite,
|
|
88
|
-
isApplyingWrite,
|
|
89
88
|
connectHandler,
|
|
90
89
|
disconnectHandler,
|
|
91
90
|
disconnectAllHandlers,
|
package/src/reconciler/text.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { Props } from "./registry.js";
|
|
|
7
7
|
import { ELEMENT_KIND, TEXT_KIND } from "./node.js";
|
|
8
8
|
import { applyWrite } from "./signals.js";
|
|
9
9
|
|
|
10
|
-
type OffsetResult = {
|
|
10
|
+
type OffsetResult = { wasFound: boolean; offset: number };
|
|
11
11
|
|
|
12
12
|
type BufferBuild = {
|
|
13
13
|
buffer: Gtk.TextBuffer;
|
|
@@ -76,14 +76,14 @@ const isTagElement = (node: ContentChild): node is ElementNode =>
|
|
|
76
76
|
|
|
77
77
|
const stepOffset = (node: ContentChild, target: TextNode, offset: number): OffsetResult => {
|
|
78
78
|
if (node === target) {
|
|
79
|
-
return {
|
|
79
|
+
return { wasFound: true, offset };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
if (isTagElement(node)) {
|
|
83
83
|
return getOffset(node.content, target, offset);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
return {
|
|
86
|
+
return { wasFound: false, offset: offset + contentTextLength(node) };
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
const getOffset = (nodes: ContentChild[], target: TextNode, start: number): OffsetResult => {
|
|
@@ -92,14 +92,14 @@ const getOffset = (nodes: ContentChild[], target: TextNode, start: number): Offs
|
|
|
92
92
|
for (const node of nodes) {
|
|
93
93
|
const step = stepOffset(node, target, offset);
|
|
94
94
|
|
|
95
|
-
if (step.
|
|
95
|
+
if (step.wasFound) {
|
|
96
96
|
return step;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
offset = step.offset;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
return {
|
|
102
|
+
return { wasFound: false, offset };
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
const enclosingTagNodes = (node: TextNode): ElementNode[] => {
|
|
@@ -379,7 +379,7 @@ const didUpdateTextSurgically = (host: ElementNode, node: TextNode, oldText: str
|
|
|
379
379
|
|
|
380
380
|
const located = getOffset(host.content, node, 0);
|
|
381
381
|
|
|
382
|
-
if (!located.
|
|
382
|
+
if (!located.wasFound) {
|
|
383
383
|
return false;
|
|
384
384
|
}
|
|
385
385
|
|