@gtkx/react 1.3.0 → 1.5.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/adw/element-behaviors.js +19 -107
- package/dist/adw/element-behaviors.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 +55 -60
- 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 +19 -3
- package/dist/reconciler/behaviors.d.ts.map +1 -1
- package/dist/reconciler/behaviors.js +73 -17
- 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/adw/element-behaviors.ts +22 -162
- package/src/components/application.tsx +18 -3
- package/src/components/window.tsx +3 -3
- package/src/element-behaviors.ts +75 -96
- 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 +130 -33
- 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
|
@@ -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
|
});
|