@gtkx/react 1.3.0 → 1.4.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 -4
- package/dist/adw/dialog.js +2 -2
- package/dist/adw/dialog.js.map +1 -1
- package/dist/components/application.d.ts.map +1 -1
- package/dist/components/application.js +3 -3
- package/dist/components/application.js.map +1 -1
- package/dist/components/window.js +3 -3
- package/dist/components/window.js.map +1 -1
- package/dist/element-behaviors.js +93 -3
- package/dist/element-behaviors.js.map +1 -1
- package/dist/element-config.js +1 -1
- package/dist/element-config.js.map +1 -1
- package/dist/hooks/use-bind-setting.d.ts.map +1 -1
- package/dist/hooks/use-bind-setting.js +3 -2
- package/dist/hooks/use-bind-setting.js.map +1 -1
- package/dist/hooks/use-property.d.ts +1 -1
- package/dist/hooks/use-property.d.ts.map +1 -1
- package/dist/hooks/use-property.js +5 -1
- package/dist/hooks/use-property.js.map +1 -1
- package/dist/prop-types.d.ts +9 -1
- package/dist/prop-types.d.ts.map +1 -1
- package/dist/prop-types.js.map +1 -1
- package/dist/reconciler/apply-props.d.ts +2 -1
- package/dist/reconciler/apply-props.d.ts.map +1 -1
- package/dist/reconciler/apply-props.js +59 -24
- package/dist/reconciler/apply-props.js.map +1 -1
- package/dist/reconciler/behaviors.d.ts.map +1 -1
- package/dist/reconciler/behaviors.js +40 -9
- package/dist/reconciler/behaviors.js.map +1 -1
- package/dist/reconciler/host-config.d.ts.map +1 -1
- package/dist/reconciler/host-config.js +2 -1
- package/dist/reconciler/host-config.js.map +1 -1
- package/dist/reconciler/metadata.d.ts +6 -1
- package/dist/reconciler/metadata.d.ts.map +1 -1
- package/dist/reconciler/metadata.js +39 -6
- package/dist/reconciler/metadata.js.map +1 -1
- package/dist/reconciler/placement.js +4 -4
- package/dist/reconciler/placement.js.map +1 -1
- package/dist/reconciler/registry.d.ts +2 -0
- package/dist/reconciler/registry.d.ts.map +1 -1
- package/dist/reconciler/registry.js.map +1 -1
- package/dist/reconciler/root.d.ts.map +1 -1
- package/dist/reconciler/root.js.map +1 -1
- package/dist/reconciler/signals.d.ts +3 -2
- package/dist/reconciler/signals.d.ts.map +1 -1
- package/dist/reconciler/signals.js +49 -16
- package/dist/reconciler/signals.js.map +1 -1
- package/dist/reconciler/style.d.ts +2 -1
- package/dist/reconciler/style.d.ts.map +1 -1
- package/dist/reconciler/style.js +4 -3
- package/dist/reconciler/style.js.map +1 -1
- package/dist/reconciler/text.js +3 -3
- package/dist/reconciler/text.js.map +1 -1
- package/env.d.ts +10 -7
- package/package.json +6 -6
- package/src/adw/dialog.tsx +2 -2
- package/src/components/application.tsx +18 -3
- package/src/components/window.tsx +3 -3
- package/src/element-behaviors.ts +122 -3
- package/src/element-config.ts +1 -1
- package/src/hooks/use-bind-setting.ts +3 -2
- package/src/hooks/use-property.ts +6 -2
- package/src/prop-types.ts +10 -0
- package/src/reconciler/apply-props.ts +73 -26
- package/src/reconciler/behaviors.ts +60 -12
- package/src/reconciler/host-config.ts +3 -1
- package/src/reconciler/metadata.ts +62 -6
- package/src/reconciler/placement.ts +4 -4
- package/src/reconciler/registry.ts +2 -0
- package/src/reconciler/root.ts +1 -1
- package/src/reconciler/signals.ts +62 -17
- package/src/reconciler/style.ts +4 -3
- package/src/reconciler/text.ts +3 -3
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type PlaceableNode,
|
|
16
16
|
type PlacedChild,
|
|
17
17
|
} from "./node.js";
|
|
18
|
-
import { applyWrite } from "./signals.js";
|
|
18
|
+
import { applyMutation, applyWrite } from "./signals.js";
|
|
19
19
|
import { markTextDirty } from "./text.js";
|
|
20
20
|
|
|
21
21
|
type AttachContext = { parent: ElementNode; entry: PlacedChild; index: number; sibling: GObject.Object | null };
|
|
@@ -78,7 +78,7 @@ const wireBufferView = (node: PlaceableNode, parent: ElementNode): void => {
|
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
const writeSlot = (parent: ElementNode, entry: PlacedChild, value: GObject.Object | null): void => {
|
|
81
|
-
applyWrite(() => {
|
|
81
|
+
applyWrite(entry.slot, () => {
|
|
82
82
|
Reflect.set(parent.object, entry.slot, value);
|
|
83
83
|
});
|
|
84
84
|
};
|
|
@@ -135,7 +135,7 @@ const runAttach = (parent: ElementNode, entry: PlacedChild, index: number, sibli
|
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
const attachEntry = (parent: ElementNode, entry: PlacedChild, index: number, sibling: GObject.Object | null): void => {
|
|
138
|
-
|
|
138
|
+
applyMutation(() => {
|
|
139
139
|
runAttach(parent, entry, index, sibling);
|
|
140
140
|
});
|
|
141
141
|
};
|
|
@@ -160,7 +160,7 @@ const runDetach = (parent: ElementNode, entry: PlacedChild): void => {
|
|
|
160
160
|
};
|
|
161
161
|
|
|
162
162
|
const detachEntry = (parent: ElementNode, entry: PlacedChild): void => {
|
|
163
|
-
|
|
163
|
+
applyMutation(() => {
|
|
164
164
|
runDetach(parent, entry);
|
|
165
165
|
});
|
|
166
166
|
};
|
|
@@ -54,6 +54,8 @@ type ElementBehavior<T extends GObject.Object = GObject.Object> = {
|
|
|
54
54
|
update?: (object: T, prev: Props, next: Props, context: unknown) => Iterable<string> | undefined;
|
|
55
55
|
/** Runs after the commit that touched the node, once every child has been placed. */
|
|
56
56
|
flush?: (object: T, context: unknown) => void;
|
|
57
|
+
/** Releases whatever `initialize` or a later hook acquired, once the node is destroyed. */
|
|
58
|
+
teardown?: (object: T, context: unknown) => void;
|
|
57
59
|
/** Props to withhold from the constructor, leaving them for a later hook to apply. */
|
|
58
60
|
deferred?: string[];
|
|
59
61
|
/**
|
package/src/reconciler/root.ts
CHANGED
|
@@ -171,7 +171,7 @@ const quit = (): typeof Gdk.EVENT_PROPAGATE | typeof Gdk.EVENT_STOP => {
|
|
|
171
171
|
* @param container The GObject to render into, or {@link rootElement} to render at the top level.
|
|
172
172
|
*/
|
|
173
173
|
const createPortal = (children: ReactNode, container: Container, key?: string): ReactPortal =>
|
|
174
|
-
reconciler.createPortal(children, container, null, key ?? null)
|
|
174
|
+
reconciler.createPortal(children, container, null, key ?? null);
|
|
175
175
|
|
|
176
176
|
export {
|
|
177
177
|
setReconcilerErrorHandler,
|
|
@@ -1,25 +1,67 @@
|
|
|
1
|
+
import * as GObject from "@gtkx/gi/gobject";
|
|
1
2
|
import { getSignalBaseName, type SignalHandler } from "@gtkx/runtime";
|
|
2
|
-
import {
|
|
3
|
+
import { getOrInsert, toCamelIdentifier } from "@gtkx/utils";
|
|
3
4
|
import type { HandlerRecord, SignalTarget } from "./node.js";
|
|
4
5
|
import { type TypeInfo, typeInfoFor } from "./metadata.js";
|
|
5
6
|
|
|
7
|
+
type NotifyBinding = { property: string | null };
|
|
8
|
+
|
|
9
|
+
const NOTIFY_SIGNAL = "notify";
|
|
6
10
|
const NOTIFY_DETAIL_PREFIX = "notify::";
|
|
7
|
-
const
|
|
11
|
+
const pendingWrites: (string | null)[] = [];
|
|
12
|
+
const canonicalNames: Map<string, string> = new Map();
|
|
8
13
|
|
|
9
|
-
const isApplyingWrite = (): boolean =>
|
|
14
|
+
const isApplyingWrite = (): boolean => pendingWrites.length > 0;
|
|
15
|
+
const canonicalName = (property: string): string => getOrInsert(canonicalNames, property, toCamelIdentifier);
|
|
10
16
|
|
|
11
|
-
const
|
|
12
|
-
|
|
17
|
+
const runWrite = <T>(property: string | null, write: () => T): T => {
|
|
18
|
+
pendingWrites.push(property === null ? null : canonicalName(property));
|
|
13
19
|
|
|
14
20
|
try {
|
|
15
21
|
return write();
|
|
16
22
|
} finally {
|
|
17
|
-
|
|
23
|
+
pendingWrites.pop();
|
|
18
24
|
}
|
|
19
25
|
};
|
|
20
26
|
|
|
21
|
-
const
|
|
22
|
-
|
|
27
|
+
const applyWrite = <T>(property: string, write: () => T): T => runWrite(property, write);
|
|
28
|
+
const applyMutation = <T>(write: () => T): T => runWrite(null, write);
|
|
29
|
+
|
|
30
|
+
const notifyBindingFor = (signal: string): NotifyBinding | null => {
|
|
31
|
+
if (signal === NOTIFY_SIGNAL) {
|
|
32
|
+
return { property: null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!signal.startsWith(NOTIFY_DETAIL_PREFIX)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { property: canonicalName(signal.slice(NOTIFY_DETAIL_PREFIX.length)) };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const notifiedProperty = (notify: NotifyBinding, args: unknown[]): string | null => {
|
|
43
|
+
if (notify.property !== null) {
|
|
44
|
+
return notify.property;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const [pspec] = args;
|
|
48
|
+
|
|
49
|
+
return pspec instanceof GObject.ParamSpec ? canonicalName(pspec.getName()) : null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const isSuppressedNotify = (notify: NotifyBinding, args: unknown[]): boolean => {
|
|
53
|
+
const notified = notifiedProperty(notify, args);
|
|
54
|
+
|
|
55
|
+
return notified === null || pendingWrites.includes(null) || pendingWrites.includes(notified);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const isSuppressed = (record: HandlerRecord, notify: NotifyBinding | null, args: unknown[]): boolean => {
|
|
59
|
+
if (!record.isBlockable || !isApplyingWrite()) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return notify === null || isSuppressedNotify(notify, args);
|
|
64
|
+
};
|
|
23
65
|
|
|
24
66
|
const isBlockableSignal = (info: TypeInfo, signal: string): boolean =>
|
|
25
67
|
info.userEventSignals.has(getSignalBaseName(signal));
|
|
@@ -27,20 +69,23 @@ const isBlockableSignal = (info: TypeInfo, signal: string): boolean =>
|
|
|
27
69
|
const invokeHandler = (
|
|
28
70
|
target: SignalTarget,
|
|
29
71
|
record: HandlerRecord,
|
|
30
|
-
|
|
72
|
+
notify: NotifyBinding | null,
|
|
31
73
|
args: unknown[],
|
|
32
|
-
): unknown =>
|
|
33
|
-
|
|
74
|
+
): unknown => {
|
|
75
|
+
const property = notify?.property ?? null;
|
|
76
|
+
|
|
77
|
+
return property === null
|
|
34
78
|
? record.handler(...args, target.object)
|
|
35
|
-
: record.handler(Reflect.get(target.object,
|
|
79
|
+
: record.handler(Reflect.get(target.object, property), target.object);
|
|
80
|
+
};
|
|
36
81
|
|
|
37
|
-
const wrapHandler = (target: SignalTarget, record: HandlerRecord,
|
|
82
|
+
const wrapHandler = (target: SignalTarget, record: HandlerRecord, notify: NotifyBinding | null): SignalHandler =>
|
|
38
83
|
(...args: unknown[]): unknown => {
|
|
39
|
-
if (record
|
|
84
|
+
if (isSuppressed(record, notify, args)) {
|
|
40
85
|
return undefined;
|
|
41
86
|
}
|
|
42
87
|
|
|
43
|
-
return target.dispatch(() => invokeHandler(target, record,
|
|
88
|
+
return target.dispatch(() => invokeHandler(target, record, notify, args));
|
|
44
89
|
};
|
|
45
90
|
|
|
46
91
|
const connectHandler = (target: SignalTarget, prop: string, signal: string, handler: SignalHandler): void => {
|
|
@@ -57,9 +102,8 @@ const connectHandler = (target: SignalTarget, prop: string, signal: string, hand
|
|
|
57
102
|
}
|
|
58
103
|
|
|
59
104
|
const isBlockable = isBlockableSignal(typeInfoFor(target.typeName), signal);
|
|
60
|
-
const notifyProperty = getNotifyProperty(signal);
|
|
61
105
|
const record: HandlerRecord = { signal, handler, wrapped: (): undefined => undefined, isBlockable };
|
|
62
|
-
record.wrapped = wrapHandler(target, record,
|
|
106
|
+
record.wrapped = wrapHandler(target, record, notifyBindingFor(signal));
|
|
63
107
|
target.object.on(signal, record.wrapped);
|
|
64
108
|
target.handlers.set(prop, record);
|
|
65
109
|
};
|
|
@@ -84,6 +128,7 @@ const disconnectAllHandlers = (target: SignalTarget): void => {
|
|
|
84
128
|
};
|
|
85
129
|
|
|
86
130
|
export {
|
|
131
|
+
applyMutation,
|
|
87
132
|
applyWrite,
|
|
88
133
|
connectHandler,
|
|
89
134
|
disconnectHandler,
|
package/src/reconciler/style.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { applyWrite } from "./signals.js";
|
|
|
7
7
|
type StyleSlot = { provider: Gtk.CssProvider; className: string; css: string };
|
|
8
8
|
|
|
9
9
|
const CLASS_PREFIX = "gtkx-s";
|
|
10
|
+
const CSS_CLASSES_PROP = "cssClasses";
|
|
10
11
|
const STYLE_PRIORITY = STYLE_PROVIDER_PRIORITY_APPLICATION + 1;
|
|
11
12
|
const log: Logger = createLogger("react");
|
|
12
13
|
const styles: WeakMap<Gtk.Widget, StyleSlot> = new WeakMap();
|
|
@@ -37,7 +38,7 @@ const releaseStyle = (widget: Gtk.Widget): void => {
|
|
|
37
38
|
styles.delete(widget);
|
|
38
39
|
reclaimed.unregister(widget);
|
|
39
40
|
|
|
40
|
-
applyWrite(() => {
|
|
41
|
+
applyWrite(CSS_CLASSES_PROP, () => {
|
|
41
42
|
widget.removeCssClass(slot.className);
|
|
42
43
|
});
|
|
43
44
|
|
|
@@ -55,7 +56,7 @@ const ensureSlot = (widget: Gtk.Widget): StyleSlot => {
|
|
|
55
56
|
styles.set(widget, slot);
|
|
56
57
|
reclaimed.register(widget, slot, widget);
|
|
57
58
|
|
|
58
|
-
applyWrite(() => {
|
|
59
|
+
applyWrite(CSS_CLASSES_PROP, () => {
|
|
59
60
|
widget.addCssClass(slot.className);
|
|
60
61
|
});
|
|
61
62
|
|
|
@@ -80,4 +81,4 @@ const applyStyle = (widget: Gtk.Widget, style: unknown): string | null => {
|
|
|
80
81
|
return slot.className;
|
|
81
82
|
};
|
|
82
83
|
|
|
83
|
-
export { applyStyle, releaseStyle, styleClass };
|
|
84
|
+
export { applyStyle, CSS_CLASSES_PROP, releaseStyle, styleClass };
|
package/src/reconciler/text.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { drain, indexBeforeOrEnd } from "@gtkx/utils";
|
|
|
5
5
|
import type { ContentChild, ContentKind, ElementNode, ParentNode, TextNode } from "./node.js";
|
|
6
6
|
import type { Props } from "./registry.js";
|
|
7
7
|
import { ELEMENT_KIND, TEXT_KIND } from "./node.js";
|
|
8
|
-
import {
|
|
8
|
+
import { applyMutation } from "./signals.js";
|
|
9
9
|
|
|
10
10
|
type OffsetResult = { wasFound: boolean; offset: number };
|
|
11
11
|
|
|
@@ -320,7 +320,7 @@ const rebuildBuffer = (node: ElementNode): void => {
|
|
|
320
320
|
return;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
|
|
323
|
+
applyMutation(() => {
|
|
324
324
|
buffer.setText("", -1);
|
|
325
325
|
const build: BufferBuild = { buffer, view: node.bufferView, marks: [] };
|
|
326
326
|
insertContent(build, node.content);
|
|
@@ -385,7 +385,7 @@ const didUpdateTextSurgically = (host: ElementNode, node: TextNode, oldText: str
|
|
|
385
385
|
|
|
386
386
|
const start = located.offset;
|
|
387
387
|
|
|
388
|
-
|
|
388
|
+
applyMutation(() => {
|
|
389
389
|
buffer.delete(buffer.getIterAtOffset(start), buffer.getIterAtOffset(start + charLength(oldText)));
|
|
390
390
|
buffer.insert(buffer.getIterAtOffset(start), newText, -1);
|
|
391
391
|
});
|