@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.
Files changed (77) hide show
  1. package/README.md +5 -4
  2. package/dist/adw/dialog.js +2 -2
  3. package/dist/adw/dialog.js.map +1 -1
  4. package/dist/adw/element-behaviors.js +19 -107
  5. package/dist/adw/element-behaviors.js.map +1 -1
  6. package/dist/components/application.d.ts.map +1 -1
  7. package/dist/components/application.js +3 -3
  8. package/dist/components/application.js.map +1 -1
  9. package/dist/components/window.js +3 -3
  10. package/dist/components/window.js.map +1 -1
  11. package/dist/element-behaviors.js +55 -60
  12. package/dist/element-behaviors.js.map +1 -1
  13. package/dist/element-config.js +1 -1
  14. package/dist/element-config.js.map +1 -1
  15. package/dist/hooks/use-bind-setting.d.ts.map +1 -1
  16. package/dist/hooks/use-bind-setting.js +3 -2
  17. package/dist/hooks/use-bind-setting.js.map +1 -1
  18. package/dist/hooks/use-property.d.ts +1 -1
  19. package/dist/hooks/use-property.d.ts.map +1 -1
  20. package/dist/hooks/use-property.js +5 -1
  21. package/dist/hooks/use-property.js.map +1 -1
  22. package/dist/prop-types.d.ts +9 -1
  23. package/dist/prop-types.d.ts.map +1 -1
  24. package/dist/prop-types.js.map +1 -1
  25. package/dist/reconciler/apply-props.d.ts +2 -1
  26. package/dist/reconciler/apply-props.d.ts.map +1 -1
  27. package/dist/reconciler/apply-props.js +59 -24
  28. package/dist/reconciler/apply-props.js.map +1 -1
  29. package/dist/reconciler/behaviors.d.ts +19 -3
  30. package/dist/reconciler/behaviors.d.ts.map +1 -1
  31. package/dist/reconciler/behaviors.js +73 -17
  32. package/dist/reconciler/behaviors.js.map +1 -1
  33. package/dist/reconciler/host-config.d.ts.map +1 -1
  34. package/dist/reconciler/host-config.js +2 -1
  35. package/dist/reconciler/host-config.js.map +1 -1
  36. package/dist/reconciler/metadata.d.ts +6 -1
  37. package/dist/reconciler/metadata.d.ts.map +1 -1
  38. package/dist/reconciler/metadata.js +39 -6
  39. package/dist/reconciler/metadata.js.map +1 -1
  40. package/dist/reconciler/placement.js +4 -4
  41. package/dist/reconciler/placement.js.map +1 -1
  42. package/dist/reconciler/registry.d.ts +2 -0
  43. package/dist/reconciler/registry.d.ts.map +1 -1
  44. package/dist/reconciler/registry.js.map +1 -1
  45. package/dist/reconciler/root.d.ts.map +1 -1
  46. package/dist/reconciler/root.js.map +1 -1
  47. package/dist/reconciler/signals.d.ts +3 -2
  48. package/dist/reconciler/signals.d.ts.map +1 -1
  49. package/dist/reconciler/signals.js +49 -16
  50. package/dist/reconciler/signals.js.map +1 -1
  51. package/dist/reconciler/style.d.ts +2 -1
  52. package/dist/reconciler/style.d.ts.map +1 -1
  53. package/dist/reconciler/style.js +4 -3
  54. package/dist/reconciler/style.js.map +1 -1
  55. package/dist/reconciler/text.js +3 -3
  56. package/dist/reconciler/text.js.map +1 -1
  57. package/env.d.ts +10 -7
  58. package/package.json +6 -6
  59. package/src/adw/dialog.tsx +2 -2
  60. package/src/adw/element-behaviors.ts +22 -162
  61. package/src/components/application.tsx +18 -3
  62. package/src/components/window.tsx +3 -3
  63. package/src/element-behaviors.ts +75 -96
  64. package/src/element-config.ts +1 -1
  65. package/src/hooks/use-bind-setting.ts +3 -2
  66. package/src/hooks/use-property.ts +6 -2
  67. package/src/prop-types.ts +10 -0
  68. package/src/reconciler/apply-props.ts +73 -26
  69. package/src/reconciler/behaviors.ts +130 -33
  70. package/src/reconciler/host-config.ts +3 -1
  71. package/src/reconciler/metadata.ts +62 -6
  72. package/src/reconciler/placement.ts +4 -4
  73. package/src/reconciler/registry.ts +2 -0
  74. package/src/reconciler/root.ts +1 -1
  75. package/src/reconciler/signals.ts +62 -17
  76. package/src/reconciler/style.ts +4 -3
  77. 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 { camelCase } from "@gtkx/utils";
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 writeState: { depth: number } = { depth: 0 };
11
+ const pendingWrites: (string | null)[] = [];
12
+ const canonicalNames: Map<string, string> = new Map();
8
13
 
9
- const isApplyingWrite = (): boolean => writeState.depth > 0;
14
+ const isApplyingWrite = (): boolean => pendingWrites.length > 0;
15
+ const canonicalName = (property: string): string => getOrInsert(canonicalNames, property, toCamelIdentifier);
10
16
 
11
- const applyWrite = <T>(write: () => T): T => {
12
- writeState.depth += 1;
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
- writeState.depth = Math.max(0, writeState.depth - 1);
23
+ pendingWrites.pop();
18
24
  }
19
25
  };
20
26
 
21
- const getNotifyProperty = (signal: string): string | null =>
22
- signal.startsWith(NOTIFY_DETAIL_PREFIX) ? camelCase(signal.slice(NOTIFY_DETAIL_PREFIX.length)) : null;
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
- notifyProperty: string | null,
72
+ notify: NotifyBinding | null,
31
73
  args: unknown[],
32
- ): unknown =>
33
- notifyProperty === null
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, notifyProperty), target.object);
79
+ : record.handler(Reflect.get(target.object, property), target.object);
80
+ };
36
81
 
37
- const wrapHandler = (target: SignalTarget, record: HandlerRecord, notifyProperty: string | null): SignalHandler =>
82
+ const wrapHandler = (target: SignalTarget, record: HandlerRecord, notify: NotifyBinding | null): SignalHandler =>
38
83
  (...args: unknown[]): unknown => {
39
- if (record.isBlockable && isApplyingWrite()) {
84
+ if (isSuppressed(record, notify, args)) {
40
85
  return undefined;
41
86
  }
42
87
 
43
- return target.dispatch(() => invokeHandler(target, record, notifyProperty, args));
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, notifyProperty);
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,
@@ -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 };
@@ -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 { applyWrite } from "./signals.js";
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
- applyWrite(() => {
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
- applyWrite(() => {
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
  });