react-native-gtkx 0.1.0-alpha.1

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 (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/animated/composite.d.ts +5 -0
  4. package/dist/animated/composite.js +132 -0
  5. package/dist/animated/composite.js.map +1 -0
  6. package/dist/animated/create-animated.d.ts +12 -0
  7. package/dist/animated/create-animated.js +17 -0
  8. package/dist/animated/create-animated.js.map +1 -0
  9. package/dist/animated/easing.d.ts +11 -0
  10. package/dist/animated/easing.js +91 -0
  11. package/dist/animated/easing.js.map +1 -0
  12. package/dist/animated/frame-loop.d.ts +6 -0
  13. package/dist/animated/frame-loop.js +25 -0
  14. package/dist/animated/frame-loop.js.map +1 -0
  15. package/dist/animated/index.d.ts +6 -0
  16. package/dist/animated/index.js +7 -0
  17. package/dist/animated/index.js.map +1 -0
  18. package/dist/animated/interpolate.d.ts +2 -0
  19. package/dist/animated/interpolate.js +90 -0
  20. package/dist/animated/interpolate.js.map +1 -0
  21. package/dist/animated/native-driver.d.ts +1 -0
  22. package/dist/animated/native-driver.js +17 -0
  23. package/dist/animated/native-driver.js.map +1 -0
  24. package/dist/animated/spring.d.ts +3 -0
  25. package/dist/animated/spring.js +67 -0
  26. package/dist/animated/spring.js.map +1 -0
  27. package/dist/animated/timing.d.ts +3 -0
  28. package/dist/animated/timing.js +30 -0
  29. package/dist/animated/timing.js.map +1 -0
  30. package/dist/animated/types.d.ts +51 -0
  31. package/dist/animated/types.js +6 -0
  32. package/dist/animated/types.js.map +1 -0
  33. package/dist/animated/value-animation.d.ts +9 -0
  34. package/dist/animated/value-animation.js +65 -0
  35. package/dist/animated/value-animation.js.map +1 -0
  36. package/dist/animated/value.d.ts +46 -0
  37. package/dist/animated/value.js +130 -0
  38. package/dist/animated/value.js.map +1 -0
  39. package/dist/apis/alert.d.ts +16 -0
  40. package/dist/apis/alert.js +33 -0
  41. package/dist/apis/alert.js.map +1 -0
  42. package/dist/apis/app-state.d.ts +11 -0
  43. package/dist/apis/app-state.js +54 -0
  44. package/dist/apis/app-state.js.map +1 -0
  45. package/dist/apis/appearance.d.ts +12 -0
  46. package/dist/apis/appearance.js +33 -0
  47. package/dist/apis/appearance.js.map +1 -0
  48. package/dist/apis/dev-settings.d.ts +9 -0
  49. package/dist/apis/dev-settings.js +25 -0
  50. package/dist/apis/dev-settings.js.map +1 -0
  51. package/dist/apis/dimensions.d.ts +14 -0
  52. package/dist/apis/dimensions.js +80 -0
  53. package/dist/apis/dimensions.js.map +1 -0
  54. package/dist/apis/emitter.d.ts +8 -0
  55. package/dist/apis/emitter.js +35 -0
  56. package/dist/apis/emitter.js.map +1 -0
  57. package/dist/apis/hooks.d.ts +11 -0
  58. package/dist/apis/hooks.js +29 -0
  59. package/dist/apis/hooks.js.map +1 -0
  60. package/dist/apis/host.d.ts +45 -0
  61. package/dist/apis/host.gtkx.d.ts +2 -0
  62. package/dist/apis/host.gtkx.js +301 -0
  63. package/dist/apis/host.gtkx.js.map +1 -0
  64. package/dist/apis/host.js +6 -0
  65. package/dist/apis/host.js.map +1 -0
  66. package/dist/apis/index.d.ts +39 -0
  67. package/dist/apis/index.js +21 -0
  68. package/dist/apis/index.js.map +1 -0
  69. package/dist/apis/linking.d.ts +7 -0
  70. package/dist/apis/linking.js +28 -0
  71. package/dist/apis/linking.js.map +1 -0
  72. package/dist/apis/platform.d.ts +14 -0
  73. package/dist/apis/platform.js +23 -0
  74. package/dist/apis/platform.js.map +1 -0
  75. package/dist/components/activity-indicator.d.ts +10 -0
  76. package/dist/components/activity-indicator.js +21 -0
  77. package/dist/components/activity-indicator.js.map +1 -0
  78. package/dist/components/animated.d.ts +34 -0
  79. package/dist/components/animated.js +152 -0
  80. package/dist/components/animated.js.map +1 -0
  81. package/dist/components/app-registry.d.ts +14 -0
  82. package/dist/components/app-registry.js +61 -0
  83. package/dist/components/app-registry.js.map +1 -0
  84. package/dist/components/flat-list.d.ts +48 -0
  85. package/dist/components/flat-list.js +23 -0
  86. package/dist/components/flat-list.js.map +1 -0
  87. package/dist/components/host-node.d.ts +10 -0
  88. package/dist/components/host-node.js +10 -0
  89. package/dist/components/host-node.js.map +1 -0
  90. package/dist/components/image-loader.d.ts +17 -0
  91. package/dist/components/image-loader.js +54 -0
  92. package/dist/components/image-loader.js.map +1 -0
  93. package/dist/components/image.d.ts +19 -0
  94. package/dist/components/image.js +73 -0
  95. package/dist/components/image.js.map +1 -0
  96. package/dist/components/index.d.ts +14 -0
  97. package/dist/components/index.js +14 -0
  98. package/dist/components/index.js.map +1 -0
  99. package/dist/components/modal.d.ts +12 -0
  100. package/dist/components/modal.js +18 -0
  101. package/dist/components/modal.js.map +1 -0
  102. package/dist/components/pressable.d.ts +33 -0
  103. package/dist/components/pressable.js +116 -0
  104. package/dist/components/pressable.js.map +1 -0
  105. package/dist/components/rect-store.d.ts +17 -0
  106. package/dist/components/rect-store.js +40 -0
  107. package/dist/components/rect-store.js.map +1 -0
  108. package/dist/components/root.d.ts +8 -0
  109. package/dist/components/root.js +59 -0
  110. package/dist/components/root.js.map +1 -0
  111. package/dist/components/scroll-view.d.ts +56 -0
  112. package/dist/components/scroll-view.js +324 -0
  113. package/dist/components/scroll-view.js.map +1 -0
  114. package/dist/components/switch.d.ts +11 -0
  115. package/dist/components/switch.js +20 -0
  116. package/dist/components/switch.js.map +1 -0
  117. package/dist/components/text-input.d.ts +23 -0
  118. package/dist/components/text-input.js +151 -0
  119. package/dist/components/text-input.js.map +1 -0
  120. package/dist/components/text.d.ts +11 -0
  121. package/dist/components/text.js +100 -0
  122. package/dist/components/text.js.map +1 -0
  123. package/dist/components/use-layout-child.d.ts +36 -0
  124. package/dist/components/use-layout-child.js +177 -0
  125. package/dist/components/use-layout-child.js.map +1 -0
  126. package/dist/components/view.d.ts +13 -0
  127. package/dist/components/view.js +60 -0
  128. package/dist/components/view.js.map +1 -0
  129. package/dist/components/virtualized-list.d.ts +67 -0
  130. package/dist/components/virtualized-list.js +519 -0
  131. package/dist/components/virtualized-list.js.map +1 -0
  132. package/dist/contracts.d.ts +119 -0
  133. package/dist/contracts.js +5 -0
  134. package/dist/contracts.js.map +1 -0
  135. package/dist/gtkx/bridge/index.d.ts +16 -0
  136. package/dist/gtkx/bridge/index.js +25 -0
  137. package/dist/gtkx/bridge/index.js.map +1 -0
  138. package/dist/gtkx/bridge/layout-manager.d.ts +11 -0
  139. package/dist/gtkx/bridge/layout-manager.js +87 -0
  140. package/dist/gtkx/bridge/layout-manager.js.map +1 -0
  141. package/dist/gtkx/bridge/measure.d.ts +9 -0
  142. package/dist/gtkx/bridge/measure.js +20 -0
  143. package/dist/gtkx/bridge/measure.js.map +1 -0
  144. package/dist/gtkx/bridge/theme.d.ts +4 -0
  145. package/dist/gtkx/bridge/theme.js +5 -0
  146. package/dist/gtkx/bridge/theme.js.map +1 -0
  147. package/dist/gtkx/bridge/view-box.d.ts +7 -0
  148. package/dist/gtkx/bridge/view-box.js +40 -0
  149. package/dist/gtkx/bridge/view-box.js.map +1 -0
  150. package/dist/index.d.ts +6 -0
  151. package/dist/index.js +7 -0
  152. package/dist/index.js.map +1 -0
  153. package/dist/layout/apply-style.d.ts +4 -0
  154. package/dist/layout/apply-style.js +148 -0
  155. package/dist/layout/apply-style.js.map +1 -0
  156. package/dist/layout/engine.d.ts +22 -0
  157. package/dist/layout/engine.js +90 -0
  158. package/dist/layout/engine.js.map +1 -0
  159. package/dist/layout/index.d.ts +3 -0
  160. package/dist/layout/index.js +4 -0
  161. package/dist/layout/index.js.map +1 -0
  162. package/dist/layout/node.d.ts +33 -0
  163. package/dist/layout/node.js +151 -0
  164. package/dist/layout/node.js.map +1 -0
  165. package/dist/layout/yoga.d.ts +13 -0
  166. package/dist/layout/yoga.js +52 -0
  167. package/dist/layout/yoga.js.map +1 -0
  168. package/dist/metro/index.d.ts +42 -0
  169. package/dist/metro/index.js +119 -0
  170. package/dist/metro/index.js.map +1 -0
  171. package/dist/runner/host-dev.d.ts +1 -0
  172. package/dist/runner/host-dev.js +208 -0
  173. package/dist/runner/host-dev.js.map +1 -0
  174. package/dist/runner/host.d.ts +1 -0
  175. package/dist/runner/host.js +101 -0
  176. package/dist/runner/host.js.map +1 -0
  177. package/dist/runner/index.d.ts +26 -0
  178. package/dist/runner/index.js +179 -0
  179. package/dist/runner/index.js.map +1 -0
  180. package/dist/style/colors.d.ts +17 -0
  181. package/dist/style/colors.js +375 -0
  182. package/dist/style/colors.js.map +1 -0
  183. package/dist/style/dev-warning.d.ts +2 -0
  184. package/dist/style/dev-warning.js +18 -0
  185. package/dist/style/dev-warning.js.map +1 -0
  186. package/dist/style/index.d.ts +7 -0
  187. package/dist/style/index.js +14 -0
  188. package/dist/style/index.js.map +1 -0
  189. package/dist/style/registry.d.ts +12 -0
  190. package/dist/style/registry.gtkx.d.ts +2 -0
  191. package/dist/style/registry.gtkx.js +9 -0
  192. package/dist/style/registry.gtkx.js.map +1 -0
  193. package/dist/style/registry.js +23 -0
  194. package/dist/style/registry.js.map +1 -0
  195. package/dist/style/split-style.d.ts +6 -0
  196. package/dist/style/split-style.js +107 -0
  197. package/dist/style/split-style.js.map +1 -0
  198. package/dist/style/style-sheet.d.ts +43 -0
  199. package/dist/style/style-sheet.js +57 -0
  200. package/dist/style/style-sheet.js.map +1 -0
  201. package/dist/style/text-align.d.ts +7 -0
  202. package/dist/style/text-align.js +22 -0
  203. package/dist/style/text-align.js.map +1 -0
  204. package/dist/style/visual-css.d.ts +6 -0
  205. package/dist/style/visual-css.js +133 -0
  206. package/dist/style/visual-css.js.map +1 -0
  207. package/dist/vite/index.d.ts +53 -0
  208. package/dist/vite/index.js +127 -0
  209. package/dist/vite/index.js.map +1 -0
  210. package/package.json +88 -0
  211. package/react-native.config.js +21 -0
  212. package/types.d.ts +43 -0
  213. package/types.js +4 -0
@@ -0,0 +1,151 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef, useState } from "react";
3
+ import { Gtk, GtkEntry, GtkLabel, GtkScrolledWindow, GtkTextView, } from "../gtkx/bridge/index";
4
+ import { useLayoutChild } from "./use-layout-child";
5
+ import { View } from "./view";
6
+ const INPUT_PURPOSE = {
7
+ default: Gtk.InputPurpose.FREE_FORM,
8
+ numeric: Gtk.InputPurpose.NUMBER,
9
+ "email-address": Gtk.InputPurpose.EMAIL,
10
+ "phone-pad": Gtk.InputPurpose.PHONE,
11
+ url: Gtk.InputPurpose.URL,
12
+ };
13
+ // RC1-WORKAROUND(controllers-as-children): see docs/gtkx-rc1-vs-main.md
14
+ // rc.1: controllers attach imperatively (JSX children work on main only).
15
+ const useFocusController = (widgetRef, onFocus, onBlur) => {
16
+ const handlers = useRef({ onFocus, onBlur });
17
+ handlers.current = { onFocus, onBlur };
18
+ useLayoutEffect(() => {
19
+ const widget = widgetRef.current;
20
+ if (!widget) {
21
+ return;
22
+ }
23
+ const focus = new Gtk.EventControllerFocus();
24
+ const enter = () => handlers.current.onFocus?.();
25
+ const leave = () => handlers.current.onBlur?.();
26
+ focus.on("enter", enter);
27
+ focus.on("leave", leave);
28
+ widget.addController(focus);
29
+ return () => {
30
+ widget.removeController(focus);
31
+ };
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ }, []);
34
+ };
35
+ // Single-line input over GtkEntry. gtkx's controlled-text behavior keeps the
36
+ // widget in sync with the `text` prop (RN controlled semantics); uncontrolled
37
+ // usage passes defaultValue once.
38
+ const SingleLineTextInput = ({ value, defaultValue, onChangeText, onSubmitEditing, onFocus, onBlur, placeholder, secureTextEntry = false, editable = true, keyboardType = "default", style, onLayout, testID, }) => {
39
+ const widgetRef = useRef(null);
40
+ useLayoutChild(widgetRef, {
41
+ style,
42
+ onLayout,
43
+ // The entry measures itself (theme-accurate height); width is driven by
44
+ // the style (flex/width), with a small floor for usability.
45
+ measureFromWidget: true,
46
+ extraLayout: { minWidth: 60 },
47
+ });
48
+ const controlled = value !== undefined;
49
+ useFocusController(widgetRef, onFocus, onBlur);
50
+ return (_jsx(GtkEntry, { ref: widgetRef, name: testID, text: controlled ? value : (defaultValue ?? ""), placeholderText: placeholder ?? "", visibility: !secureTextEntry, editable: editable, sensitive: editable, inputPurpose: INPUT_PURPOSE[keyboardType], onChanged: () => {
51
+ const widget = widgetRef.current;
52
+ if (widget) {
53
+ onChangeText?.(widget.getText());
54
+ }
55
+ }, onActivate: () => {
56
+ const widget = widgetRef.current;
57
+ if (widget) {
58
+ onSubmitEditing?.({ nativeEvent: { text: widget.getText() } });
59
+ }
60
+ } }));
61
+ };
62
+ const readBuffer = (buffer) => buffer.getText(buffer.getStartIter(), buffer.getEndIter(), true);
63
+ // GtkTextView has no native placeholder — a dim, click-transparent label
64
+ // sits over the empty view (RN shows the placeholder while empty, focused
65
+ // or not).
66
+ const MultilinePlaceholder = ({ text }) => {
67
+ const labelRef = useRef(null);
68
+ useLayoutChild(labelRef, {
69
+ style: { position: "absolute", left: 12, top: 8 },
70
+ onLayout: undefined,
71
+ });
72
+ useLayoutEffect(() => {
73
+ labelRef.current?.setCanTarget(false);
74
+ labelRef.current?.addCssClass("dim-label");
75
+ }, []);
76
+ return (_jsx(GtkLabel, { ref: labelRef, label: text }));
77
+ };
78
+ // Multiline input over GtkTextView in a GtkScrolledWindow: word wrap, the
79
+ // style sets the box (RN requires a height for multiline), overflowing
80
+ // content scrolls inside. The buffer syncs with `value` under RN controlled
81
+ // semantics; Enter inserts a newline (onSubmitEditing never fires — RN
82
+ // multiline behavior).
83
+ const MultilineTextInput = ({ value, defaultValue, onChangeText, onFocus, onBlur, placeholder, editable = true, style, onLayout, testID, }) => {
84
+ const scrolledRef = useRef(null);
85
+ const viewRef = useRef(null);
86
+ const controlled = value !== undefined;
87
+ const [empty, setEmpty] = useState((controlled ? (value ?? "") : (defaultValue ?? "")) === "");
88
+ const suppressEcho = useRef(false);
89
+ const latest = useRef({ onChangeText });
90
+ latest.current = { onChangeText };
91
+ useLayoutEffect(() => {
92
+ const view = viewRef.current;
93
+ if (!view) {
94
+ return;
95
+ }
96
+ // Breathing room akin to GtkEntry's built-in padding.
97
+ view.setLeftMargin(10);
98
+ view.setRightMargin(10);
99
+ view.setTopMargin(8);
100
+ view.setBottomMargin(8);
101
+ const buffer = view.getBuffer();
102
+ const initial = controlled ? (value ?? "") : (defaultValue ?? "");
103
+ if (initial !== "") {
104
+ suppressEcho.current = true;
105
+ buffer.setText(initial, -1);
106
+ suppressEcho.current = false;
107
+ }
108
+ const changed = () => {
109
+ const text = readBuffer(buffer);
110
+ setEmpty(text === "");
111
+ if (!suppressEcho.current) {
112
+ latest.current.onChangeText?.(text);
113
+ }
114
+ };
115
+ buffer.on("changed", changed);
116
+ return () => {
117
+ buffer.off("changed", changed);
118
+ };
119
+ // eslint-disable-next-line react-hooks/exhaustive-deps
120
+ }, []);
121
+ // RN controlled semantics: the prop wins over whatever was typed.
122
+ useLayoutEffect(() => {
123
+ if (!controlled) {
124
+ return;
125
+ }
126
+ const view = viewRef.current;
127
+ if (!view) {
128
+ return;
129
+ }
130
+ const buffer = view.getBuffer();
131
+ const next = value ?? "";
132
+ if (readBuffer(buffer) !== next) {
133
+ suppressEcho.current = true;
134
+ buffer.setText(next, -1);
135
+ suppressEcho.current = false;
136
+ setEmpty(next === "");
137
+ }
138
+ }, [controlled, value]);
139
+ useFocusController(viewRef, onFocus, onBlur);
140
+ return (_jsxs(View, { style: [style, { minHeight: 36, minWidth: 60 }], onLayout: onLayout, children: [_jsx(ScrolledFill, { scrolledRef: scrolledRef, children: _jsx(GtkTextView, { ref: viewRef, name: testID, editable: editable, sensitive: editable, wrapMode: Gtk.WrapMode.WORD_CHAR }) }), empty && placeholder ? (_jsx(MultilinePlaceholder, { text: placeholder })) : null] }));
141
+ };
142
+ // The scrolled window fills the styled box exactly (its own layout child).
143
+ const ScrolledFill = ({ scrolledRef, children, }) => {
144
+ useLayoutChild(scrolledRef, {
145
+ style: { position: "absolute", left: 0, right: 0, top: 0, bottom: 0 },
146
+ onLayout: undefined,
147
+ });
148
+ return _jsx(GtkScrolledWindow, { ref: scrolledRef, children: children });
149
+ };
150
+ export const TextInput = (props) => props.multiline ? (_jsx(MultilineTextInput, { ...props })) : (_jsx(SingleLineTextInput, { ...props }));
151
+ //# sourceMappingURL=text-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../src/components/text-input.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gtk,\n GtkEntry,\n GtkLabel,\n GtkScrolledWindow,\n GtkTextView,\n type Gtk as GtkNs,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\nimport { View } from \"./view\"\n\nexport type TextInputProps = {\n value?: string\n defaultValue?: string\n onChangeText?: (text: string) => void\n onSubmitEditing?: (event: { nativeEvent: { text: string } }) => void\n onFocus?: () => void\n onBlur?: () => void\n placeholder?: string\n secureTextEntry?: boolean\n editable?: boolean\n keyboardType?: \"default\" | \"numeric\" | \"email-address\" | \"phone-pad\" | \"url\"\n // RN: a multiline input needs a height in the style; Enter inserts a\n // newline and never fires onSubmitEditing.\n multiline?: boolean\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst INPUT_PURPOSE: Record<\n NonNullable<TextInputProps[\"keyboardType\"]>,\n Gtk.InputPurpose\n> = {\n default: Gtk.InputPurpose.FREE_FORM,\n numeric: Gtk.InputPurpose.NUMBER,\n \"email-address\": Gtk.InputPurpose.EMAIL,\n \"phone-pad\": Gtk.InputPurpose.PHONE,\n url: Gtk.InputPurpose.URL,\n}\n\n// RC1-WORKAROUND(controllers-as-children): see docs/gtkx-rc1-vs-main.md\n// rc.1: controllers attach imperatively (JSX children work on main only).\nconst useFocusController = (\n widgetRef: React.RefObject<GtkNs.Widget | null>,\n onFocus?: () => void,\n onBlur?: () => void,\n): void => {\n const handlers = useRef({ onFocus, onBlur })\n handlers.current = { onFocus, onBlur }\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const focus = new Gtk.EventControllerFocus()\n const enter = (): void => handlers.current.onFocus?.()\n const leave = (): void => handlers.current.onBlur?.()\n focus.on(\"enter\", enter)\n focus.on(\"leave\", leave)\n widget.addController(focus)\n return () => {\n widget.removeController(focus)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n}\n\n// Single-line input over GtkEntry. gtkx's controlled-text behavior keeps the\n// widget in sync with the `text` prop (RN controlled semantics); uncontrolled\n// usage passes defaultValue once.\nconst SingleLineTextInput = ({\n value,\n defaultValue,\n onChangeText,\n onSubmitEditing,\n onFocus,\n onBlur,\n placeholder,\n secureTextEntry = false,\n editable = true,\n keyboardType = \"default\",\n style,\n onLayout,\n testID,\n}: TextInputProps) => {\n const widgetRef = useRef<GtkNs.Entry | null>(null)\n\n useLayoutChild(widgetRef, {\n style,\n onLayout,\n // The entry measures itself (theme-accurate height); width is driven by\n // the style (flex/width), with a small floor for usability.\n measureFromWidget: true,\n extraLayout: { minWidth: 60 },\n })\n\n const controlled = value !== undefined\n\n useFocusController(widgetRef, onFocus, onBlur)\n\n return (\n <GtkEntry\n ref={widgetRef}\n name={testID}\n text={controlled ? value : (defaultValue ?? \"\")}\n placeholderText={placeholder ?? \"\"}\n visibility={!secureTextEntry}\n editable={editable}\n sensitive={editable}\n inputPurpose={INPUT_PURPOSE[keyboardType]}\n onChanged={() => {\n const widget = widgetRef.current\n if (widget) {\n onChangeText?.(widget.getText())\n }\n }}\n onActivate={() => {\n const widget = widgetRef.current\n if (widget) {\n onSubmitEditing?.({ nativeEvent: { text: widget.getText() } })\n }\n }}\n />\n )\n}\n\nconst readBuffer = (buffer: GtkNs.TextBuffer): string =>\n buffer.getText(buffer.getStartIter(), buffer.getEndIter(), true)\n\n// GtkTextView has no native placeholder — a dim, click-transparent label\n// sits over the empty view (RN shows the placeholder while empty, focused\n// or not).\nconst MultilinePlaceholder = ({ text }: { text: string }) => {\n const labelRef = useRef<GtkNs.Label | null>(null)\n useLayoutChild(labelRef, {\n style: { position: \"absolute\", left: 12, top: 8 },\n onLayout: undefined,\n })\n useLayoutEffect(() => {\n labelRef.current?.setCanTarget(false)\n labelRef.current?.addCssClass(\"dim-label\")\n }, [])\n return (\n <GtkLabel\n ref={labelRef}\n label={text}\n />\n )\n}\n\n// Multiline input over GtkTextView in a GtkScrolledWindow: word wrap, the\n// style sets the box (RN requires a height for multiline), overflowing\n// content scrolls inside. The buffer syncs with `value` under RN controlled\n// semantics; Enter inserts a newline (onSubmitEditing never fires — RN\n// multiline behavior).\nconst MultilineTextInput = ({\n value,\n defaultValue,\n onChangeText,\n onFocus,\n onBlur,\n placeholder,\n editable = true,\n style,\n onLayout,\n testID,\n}: TextInputProps) => {\n const scrolledRef = useRef<GtkNs.ScrolledWindow | null>(null)\n const viewRef = useRef<GtkNs.TextView | null>(null)\n const controlled = value !== undefined\n const [empty, setEmpty] = useState(\n (controlled ? (value ?? \"\") : (defaultValue ?? \"\")) === \"\",\n )\n\n const suppressEcho = useRef(false)\n const latest = useRef({ onChangeText })\n latest.current = { onChangeText }\n\n useLayoutEffect(() => {\n const view = viewRef.current\n if (!view) {\n return\n }\n // Breathing room akin to GtkEntry's built-in padding.\n view.setLeftMargin(10)\n view.setRightMargin(10)\n view.setTopMargin(8)\n view.setBottomMargin(8)\n const buffer = view.getBuffer()\n const initial = controlled ? (value ?? \"\") : (defaultValue ?? \"\")\n if (initial !== \"\") {\n suppressEcho.current = true\n buffer.setText(initial, -1)\n suppressEcho.current = false\n }\n const changed = (): void => {\n const text = readBuffer(buffer)\n setEmpty(text === \"\")\n if (!suppressEcho.current) {\n latest.current.onChangeText?.(text)\n }\n }\n buffer.on(\"changed\", changed)\n return () => {\n buffer.off(\"changed\", changed)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // RN controlled semantics: the prop wins over whatever was typed.\n useLayoutEffect(() => {\n if (!controlled) {\n return\n }\n const view = viewRef.current\n if (!view) {\n return\n }\n const buffer = view.getBuffer()\n const next = value ?? \"\"\n if (readBuffer(buffer) !== next) {\n suppressEcho.current = true\n buffer.setText(next, -1)\n suppressEcho.current = false\n setEmpty(next === \"\")\n }\n }, [controlled, value])\n\n useFocusController(viewRef, onFocus, onBlur)\n\n return (\n <View\n style={[style, { minHeight: 36, minWidth: 60 }]}\n onLayout={onLayout}\n >\n <ScrolledFill scrolledRef={scrolledRef}>\n <GtkTextView\n ref={viewRef}\n name={testID}\n editable={editable}\n sensitive={editable}\n wrapMode={Gtk.WrapMode.WORD_CHAR}\n />\n </ScrolledFill>\n {empty && placeholder ? (\n <MultilinePlaceholder text={placeholder} />\n ) : null}\n </View>\n )\n}\n\n// The scrolled window fills the styled box exactly (its own layout child).\nconst ScrolledFill = ({\n scrolledRef,\n children,\n}: {\n scrolledRef: React.RefObject<GtkNs.ScrolledWindow | null>\n children: React.ReactNode\n}) => {\n useLayoutChild(scrolledRef, {\n style: { position: \"absolute\", left: 0, right: 0, top: 0, bottom: 0 },\n onLayout: undefined,\n })\n return <GtkScrolledWindow ref={scrolledRef}>{children}</GtkScrolledWindow>\n}\n\nexport const TextInput = (props: TextInputProps) =>\n props.multiline ? (\n <MultilineTextInput {...props} />\n ) : (\n <SingleLineTextInput {...props} />\n )\n"]}
@@ -0,0 +1,11 @@
1
+ import { type ReactNode } from "react";
2
+ import type { StyleProp } from "../contracts";
3
+ import { type LayoutEvent } from "./use-layout-child";
4
+ export type TextProps = {
5
+ style?: StyleProp;
6
+ onLayout?: (event: LayoutEvent) => void;
7
+ numberOfLines?: number;
8
+ children?: ReactNode;
9
+ testID?: string;
10
+ };
11
+ export declare const Text: ({ style, onLayout, numberOfLines, children, testID, }: TextProps) => React.JSX.Element;
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useMemo, useRef, useState, } from "react";
3
+ import { textAlignToLabelProps } from "../style/index";
4
+ import { createTextProbe, Gtk, GtkLabel, measureWidget, Pango, } from "../gtkx/bridge/index";
5
+ import { useLayoutChild } from "./use-layout-child";
6
+ const flattenToString = (children) => {
7
+ if (children === null || children === undefined || children === false) {
8
+ return "";
9
+ }
10
+ if (Array.isArray(children)) {
11
+ return children.map(flattenToString).join("");
12
+ }
13
+ return String(children);
14
+ };
15
+ const JUSTIFICATION = {
16
+ left: Gtk.Justification.LEFT,
17
+ right: Gtk.Justification.RIGHT,
18
+ center: Gtk.Justification.CENTER,
19
+ fill: Gtk.Justification.FILL,
20
+ };
21
+ // Text is a measured Yoga leaf: an offscreen probe label (with the same CSS
22
+ // class, so fonts match) feeds Pango metrics into the layout engine; the
23
+ // visible GtkLabel is positioned by the commit hook like any other widget.
24
+ export const Text = ({ style, onLayout, numberOfLines, children, testID, }) => {
25
+ const widgetRef = useRef(null);
26
+ const text = flattenToString(children);
27
+ // useState lazy init — see the note in use-layout-child.ts (React Compiler).
28
+ const [probe] = useState(() => createTextProbe());
29
+ const lastProbeClass = useRef(null);
30
+ // GTK collapses wrap+ellipsize labels to a single line unless `lines` is
31
+ // set explicitly — remember how many lines the probe wrapped into, the
32
+ // layout callback pushes it onto the visible label.
33
+ const measuredLines = useRef(1);
34
+ const measure = useMemo(() => {
35
+ return (width, widthMode) => {
36
+ const natural = measureWidget(probe, "horizontal").natural;
37
+ const used = widthMode === "undefined"
38
+ ? natural
39
+ : Math.min(natural, Math.max(1, Math.floor(width)));
40
+ const height = measureWidget(probe, "vertical", used).natural;
41
+ const singleLine = measureWidget(probe, "vertical", natural).natural;
42
+ measuredLines.current =
43
+ singleLine > 0 ? Math.max(1, Math.round(height / singleLine)) : 1;
44
+ return { width: used, height };
45
+ };
46
+ // The probe is stable; re-measure is triggered via markDirty below.
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, []);
49
+ const layoutWithLines = useMemo(() => (event) => {
50
+ const label = widgetRef.current;
51
+ if (label) {
52
+ label.setLines(numberOfLines ?? measuredLines.current);
53
+ }
54
+ onLayout?.(event);
55
+ }, [numberOfLines, onLayout]);
56
+ const { node, cssClass, flat } = useLayoutChild(widgetRef, {
57
+ style,
58
+ onLayout: layoutWithLines,
59
+ measure,
60
+ });
61
+ // RN text clips to its box: an under-allocated GtkLabel would otherwise
62
+ // paint its full text past the allocation (spike finding). Containers keep
63
+ // overflow VISIBLE (paint-overflow); the text leaf clips.
64
+ useLayoutEffect(() => {
65
+ widgetRef.current?.setOverflow(Gtk.Overflow.HIDDEN);
66
+ }, []);
67
+ // Keep the probe in sync with everything that affects metrics, then
68
+ // invalidate the Yoga leaf so the next pass re-measures.
69
+ useLayoutEffect(() => {
70
+ probe.setText(text);
71
+ if (numberOfLines !== undefined) {
72
+ probe.setLines(numberOfLines);
73
+ probe.setEllipsize(Pango.EllipsizeMode.END);
74
+ }
75
+ else {
76
+ probe.setLines(-1);
77
+ probe.setEllipsize(Pango.EllipsizeMode.NONE);
78
+ }
79
+ if (lastProbeClass.current !== cssClass) {
80
+ if (lastProbeClass.current) {
81
+ probe.removeCssClass(lastProbeClass.current);
82
+ }
83
+ if (cssClass) {
84
+ probe.addCssClass(cssClass);
85
+ }
86
+ lastProbeClass.current = cssClass;
87
+ }
88
+ node.markDirty();
89
+ }, [probe, node, text, numberOfLines, cssClass]);
90
+ const align = textAlignToLabelProps(flat.textAlign);
91
+ return (_jsx(GtkLabel, { ref: widgetRef, name: testID, label: text, wrap: true, xalign: align.xalign, yalign: 0, justify: JUSTIFICATION[align.justification], lines: numberOfLines,
92
+ // RN semantics: the ellipsis is opt-in via numberOfLines. Plain text
93
+ // wraps naturally; an unbreakable word wider than the rect just clips
94
+ // (overflow HIDDEN above) — the layout manager allocates the rect
95
+ // regardless of the label's own minimum, so nothing pushes siblings.
96
+ ellipsize: numberOfLines !== undefined
97
+ ? Pango.EllipsizeMode.END
98
+ : Pango.EllipsizeMode.NONE, cssClasses: cssClass ? [cssClass] : [] }));
99
+ };
100
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { textAlignToLabelProps } from \"../style/index\"\nimport type { MeasureFn, StyleProp } from \"../contracts\"\nimport {\n createTextProbe,\n Gtk,\n GtkLabel,\n measureWidget,\n Pango,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\n\nexport type TextProps = {\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n numberOfLines?: number\n children?: ReactNode\n testID?: string\n}\n\nconst flattenToString = (children: ReactNode): string => {\n if (children === null || children === undefined || children === false) {\n return \"\"\n }\n if (Array.isArray(children)) {\n return children.map(flattenToString).join(\"\")\n }\n return String(children)\n}\n\nconst JUSTIFICATION = {\n left: Gtk.Justification.LEFT,\n right: Gtk.Justification.RIGHT,\n center: Gtk.Justification.CENTER,\n fill: Gtk.Justification.FILL,\n} as const\n\n// Text is a measured Yoga leaf: an offscreen probe label (with the same CSS\n// class, so fonts match) feeds Pango metrics into the layout engine; the\n// visible GtkLabel is positioned by the commit hook like any other widget.\nexport const Text = ({\n style,\n onLayout,\n numberOfLines,\n children,\n testID,\n}: TextProps) => {\n const widgetRef = useRef<Gtk.Label | null>(null)\n const text = flattenToString(children)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [probe] = useState<Gtk.Label>(() => createTextProbe())\n\n const lastProbeClass = useRef<string | null>(null)\n\n // GTK collapses wrap+ellipsize labels to a single line unless `lines` is\n // set explicitly — remember how many lines the probe wrapped into, the\n // layout callback pushes it onto the visible label.\n const measuredLines = useRef(1)\n\n const measure = useMemo<MeasureFn>(() => {\n return (width, widthMode) => {\n const natural = measureWidget(probe, \"horizontal\").natural\n const used =\n widthMode === \"undefined\"\n ? natural\n : Math.min(natural, Math.max(1, Math.floor(width)))\n const height = measureWidget(probe, \"vertical\", used).natural\n const singleLine = measureWidget(probe, \"vertical\", natural).natural\n measuredLines.current =\n singleLine > 0 ? Math.max(1, Math.round(height / singleLine)) : 1\n return { width: used, height }\n }\n // The probe is stable; re-measure is triggered via markDirty below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const layoutWithLines = useMemo(\n () => (event: LayoutEvent) => {\n const label = widgetRef.current\n if (label) {\n label.setLines(numberOfLines ?? measuredLines.current)\n }\n onLayout?.(event)\n },\n [numberOfLines, onLayout],\n )\n\n const { node, cssClass, flat } = useLayoutChild(widgetRef, {\n style,\n onLayout: layoutWithLines,\n measure,\n })\n\n // RN text clips to its box: an under-allocated GtkLabel would otherwise\n // paint its full text past the allocation (spike finding). Containers keep\n // overflow VISIBLE (paint-overflow); the text leaf clips.\n useLayoutEffect(() => {\n widgetRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\n }, [])\n\n // Keep the probe in sync with everything that affects metrics, then\n // invalidate the Yoga leaf so the next pass re-measures.\n useLayoutEffect(() => {\n probe.setText(text)\n if (numberOfLines !== undefined) {\n probe.setLines(numberOfLines)\n probe.setEllipsize(Pango.EllipsizeMode.END)\n } else {\n probe.setLines(-1)\n probe.setEllipsize(Pango.EllipsizeMode.NONE)\n }\n if (lastProbeClass.current !== cssClass) {\n if (lastProbeClass.current) {\n probe.removeCssClass(lastProbeClass.current)\n }\n if (cssClass) {\n probe.addCssClass(cssClass)\n }\n lastProbeClass.current = cssClass\n }\n node.markDirty()\n }, [probe, node, text, numberOfLines, cssClass])\n\n const align = textAlignToLabelProps(flat.textAlign)\n\n return (\n <GtkLabel\n ref={widgetRef}\n name={testID}\n label={text}\n wrap\n xalign={align.xalign}\n yalign={0}\n justify={JUSTIFICATION[align.justification]}\n lines={numberOfLines}\n // RN semantics: the ellipsis is opt-in via numberOfLines. Plain text\n // wraps naturally; an unbreakable word wider than the rect just clips\n // (overflow HIDDEN above) — the layout manager allocates the rect\n // regardless of the label's own minimum, so nothing pushes siblings.\n ellipsize={\n numberOfLines !== undefined\n ? Pango.EllipsizeMode.END\n : Pango.EllipsizeMode.NONE\n }\n cssClasses={cssClass ? [cssClass] : []}\n />\n )\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { type RefObject } from "react";
2
+ import type { LayoutNode } from "../layout/index";
3
+ import { StyleSheet } from "../style/index";
4
+ import type { LayoutStyle, MeasureFn, StyleProp, VisualStyle } from "../contracts";
5
+ import { type Gtk } from "../gtkx/bridge/index";
6
+ import { type HostNode } from "./host-node";
7
+ export type LayoutEvent = {
8
+ nativeEvent: {
9
+ layout: {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ };
15
+ };
16
+ };
17
+ export type LayoutChildOptions = {
18
+ style: StyleProp | undefined;
19
+ onLayout: ((event: LayoutEvent) => void) | undefined;
20
+ measure?: MeasureFn;
21
+ measureFromWidget?: boolean;
22
+ extraLayout?: LayoutStyle;
23
+ };
24
+ export type LayoutChild = {
25
+ host: HostNode;
26
+ node: LayoutNode;
27
+ flat: ReturnType<typeof StyleSheet.flatten>;
28
+ visual: VisualStyle;
29
+ cssClass: string | null;
30
+ };
31
+ export declare const useLayoutChild: (widgetRef: RefObject<Gtk.Widget | null>, options: LayoutChildOptions) => LayoutChild;
32
+ export type RnContainerOptions = {
33
+ measure?: (orientation: "horizontal" | "vertical") => number;
34
+ beforeAllocate?: (width: number, height: number) => void;
35
+ };
36
+ export declare const useRnContainer: (widgetRef: RefObject<Gtk.Widget | null>, node: LayoutNode, options?: RnContainerOptions) => void;
@@ -0,0 +1,177 @@
1
+ import { useLayoutEffect, useRef, useState } from "react";
2
+ import { splitStyle, StyleSheet } from "../style/index";
3
+ import { defaultCssRegistry } from "../style/registry.gtkx";
4
+ import { allocateChild, attachRnLayout, detachRnLayout, measureWidget, queueAllocate, queueResize, } from "../gtkx/bridge/index";
5
+ import { useHostNode } from "./host-node";
6
+ import { deferDuringAllocate, getStoredOffset, getStoredRect, setStoredRect, } from "./rect-store";
7
+ // Shared plumbing for every leaf/container component: owns one LayoutNode,
8
+ // keeps it in the parent's shadow tree for the component lifetime, applies
9
+ // the layout half of the style and commits Yoga rects into the rect store,
10
+ // where the parent container's RnGtkxLayout allocate() picks them up.
11
+ export const useLayoutChild = (widgetRef, options) => {
12
+ const host = useHostNode();
13
+ // useState's lazy initializer is guaranteed once-per-instance even under
14
+ // the React Compiler — a lazy ref here got its creation memoized ACROSS
15
+ // component instances, sharing one Yoga node and double-freeing it.
16
+ const [node] = useState(() => host.engine.createNode());
17
+ const flat = StyleSheet.flatten(options.style);
18
+ const { layout, visual } = splitStyle(flat);
19
+ const effectiveLayout = options.extraLayout
20
+ ? { ...layout, ...options.extraLayout }
21
+ : layout;
22
+ // Re-apply layout style only when it actually changed (keyed effect) —
23
+ // setStyle dirties the whole tree.
24
+ const layoutKey = JSON.stringify(effectiveLayout);
25
+ useLayoutEffect(() => {
26
+ node.setStyle(JSON.parse(layoutKey));
27
+ }, [node, layoutKey]);
28
+ const cssClass = defaultCssRegistry.getClassName(visual);
29
+ useLayoutEffect(() => {
30
+ const parent = host.node;
31
+ // The reconciler has already attached our widget in JSX order, so its
32
+ // sibling position is the correct Yoga index — this keeps the shadow tree
33
+ // ordered even for mid-list mounts (conditional children, list inserts).
34
+ const widget = widgetRef.current;
35
+ const parentWidget = host.widgetRef.current;
36
+ let index = parent.children.length;
37
+ if (widget && parentWidget) {
38
+ let position = 0;
39
+ let sibling = parentWidget.getFirstChild();
40
+ while (sibling !== null && sibling !== widget) {
41
+ position += 1;
42
+ sibling = sibling.getNextSibling();
43
+ }
44
+ if (sibling === widget) {
45
+ index = Math.min(position, parent.children.length);
46
+ }
47
+ }
48
+ parent.insertChild(node, index);
49
+ node.setCommit((rect) => {
50
+ const widget = widgetRef.current;
51
+ const parentWidget = host.widgetRef.current;
52
+ if (!widget || !parentWidget) {
53
+ return;
54
+ }
55
+ const previous = getStoredRect(widget);
56
+ setStoredRect(widget, rect);
57
+ const sizeChanged = !previous ||
58
+ previous.width !== rect.width ||
59
+ previous.height !== rect.height;
60
+ const queue = () => {
61
+ if (sizeChanged) {
62
+ // A size change invalidates this widget's cached measure (nested
63
+ // managers, ScrolledWindow ranges) and re-allocates the ancestors.
64
+ queueResize(widget);
65
+ }
66
+ else {
67
+ // Pure move: the parent just needs another allocation pass. GTK
68
+ // dedupes queued allocates, so per-child calls batch into one.
69
+ queueAllocate(parentWidget);
70
+ }
71
+ };
72
+ if (!deferDuringAllocate(widget, queue)) {
73
+ queue();
74
+ }
75
+ });
76
+ return () => {
77
+ node.setCommit(null);
78
+ parent.removeChild(node);
79
+ node.free();
80
+ };
81
+ // eslint-disable-next-line react-hooks/exhaustive-deps
82
+ }, []);
83
+ const onLayout = options.onLayout;
84
+ useLayoutEffect(() => {
85
+ node.setOnLayout(onLayout
86
+ ? (rect) => {
87
+ onLayout({ nativeEvent: { layout: rect } });
88
+ }
89
+ : null);
90
+ }, [node, onLayout]);
91
+ // The explicit return type keeps the inner params contextually typed: with a
92
+ // bare lazy initializer TS may match the outer arrow against MeasureFn
93
+ // itself (useState accepts S | () => S) and drop the context.
94
+ const [widgetMeasure] = useState(() => (width, widthMode) => {
95
+ const widget = widgetRef.current;
96
+ if (!widget) {
97
+ return { width: 0, height: 0 };
98
+ }
99
+ const natural = measureWidget(widget, "horizontal").natural;
100
+ const used = widthMode === "undefined"
101
+ ? natural
102
+ : Math.min(natural, Math.max(1, Math.floor(width)));
103
+ return {
104
+ width: used,
105
+ height: measureWidget(widget, "vertical", used).natural,
106
+ };
107
+ });
108
+ const measure = options.measure ?? (options.measureFromWidget ? widgetMeasure : undefined);
109
+ useLayoutEffect(() => {
110
+ if (!measure) {
111
+ return;
112
+ }
113
+ node.setMeasureFn(measure);
114
+ // Widgets measured before their window is mapped report zero (no realized
115
+ // theme context yet, unlike offscreen probes) and Yoga caches it —
116
+ // invalidate once the widget actually maps.
117
+ const widget = widgetRef.current;
118
+ const remeasure = () => {
119
+ node.markDirty();
120
+ };
121
+ if (options.measureFromWidget && widget) {
122
+ widget.on("map", remeasure);
123
+ }
124
+ return () => {
125
+ if (options.measureFromWidget && widget) {
126
+ widget.off("map", remeasure);
127
+ }
128
+ // Cleared by free() on unmount; explicit unset only when measure is
129
+ // removed while mounted.
130
+ node.setMeasureFn(null);
131
+ };
132
+ // eslint-disable-next-line react-hooks/exhaustive-deps
133
+ }, [node, measure]);
134
+ return { host, node, flat, visual, cssClass };
135
+ };
136
+ // Containers (Root, View, Animated.View, ScrollView content) drive their GTK
137
+ // side with an RnGtkxLayout: measure() reports the engine rect of `node`
138
+ // (minimum == natural — GTK minimums of children never leak upward), and
139
+ // allocate() places every child widget at exactly its stored engine rect
140
+ // plus the Animated offset.
141
+ export const useRnContainer = (widgetRef, node, options) => {
142
+ const optionsRef = useRef(options);
143
+ optionsRef.current = options;
144
+ useLayoutEffect(() => {
145
+ const widget = widgetRef.current;
146
+ if (!widget) {
147
+ return;
148
+ }
149
+ attachRnLayout(widget, {
150
+ measure: (orientation) => {
151
+ const custom = optionsRef.current?.measure;
152
+ if (custom) {
153
+ return custom(orientation);
154
+ }
155
+ const rect = node.getRect();
156
+ return Math.round((orientation === "horizontal" ? rect?.width : rect?.height) ?? 0);
157
+ },
158
+ allocate: (width, height) => {
159
+ optionsRef.current?.beforeAllocate?.(width, height);
160
+ let child = widget.getFirstChild();
161
+ while (child) {
162
+ const rect = getStoredRect(child);
163
+ if (rect) {
164
+ const offset = getStoredOffset(child);
165
+ allocateChild(child, rect.x + offset.dx, rect.y + offset.dy, rect.width, rect.height);
166
+ }
167
+ child = child.getNextSibling();
168
+ }
169
+ },
170
+ });
171
+ return () => {
172
+ detachRnLayout(widget);
173
+ };
174
+ // eslint-disable-next-line react-hooks/exhaustive-deps
175
+ }, []);
176
+ };
177
+ //# sourceMappingURL=use-layout-child.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-layout-child.js","sourceRoot":"","sources":["../../src/components/use-layout-child.ts"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState, type RefObject } from \"react\"\nimport type { LayoutNode } from \"../layout/index\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport { defaultCssRegistry } from \"../style/registry.gtkx\"\nimport type {\n LayoutStyle,\n MeasureFn,\n Rect,\n StyleProp,\n VisualStyle,\n} from \"../contracts\"\nimport {\n allocateChild,\n attachRnLayout,\n detachRnLayout,\n measureWidget,\n queueAllocate,\n queueResize,\n type Gtk,\n} from \"../gtkx/bridge/index\"\nimport { useHostNode, type HostNode } from \"./host-node\"\nimport {\n deferDuringAllocate,\n getStoredOffset,\n getStoredRect,\n setStoredRect,\n} from \"./rect-store\"\n\nexport type LayoutEvent = {\n nativeEvent: {\n layout: { x: number; y: number; width: number; height: number }\n }\n}\n\nexport type LayoutChildOptions = {\n style: StyleProp | undefined\n onLayout: ((event: LayoutEvent) => void) | undefined\n measure?: MeasureFn\n // Leaves with intrinsic GTK sizes (Entry, Switch, Spinner) measure their\n // own widget so Yoga reserves exactly what the theme will draw.\n measureFromWidget?: boolean\n extraLayout?: LayoutStyle\n}\n\nexport type LayoutChild = {\n host: HostNode\n node: LayoutNode\n flat: ReturnType<typeof StyleSheet.flatten>\n visual: VisualStyle\n cssClass: string | null\n}\n\n// Shared plumbing for every leaf/container component: owns one LayoutNode,\n// keeps it in the parent's shadow tree for the component lifetime, applies\n// the layout half of the style and commits Yoga rects into the rect store,\n// where the parent container's RnGtkxLayout allocate() picks them up.\nexport const useLayoutChild = (\n widgetRef: RefObject<Gtk.Widget | null>,\n options: LayoutChildOptions,\n): LayoutChild => {\n const host = useHostNode()\n\n // useState's lazy initializer is guaranteed once-per-instance even under\n // the React Compiler — a lazy ref here got its creation memoized ACROSS\n // component instances, sharing one Yoga node and double-freeing it.\n const [node] = useState<LayoutNode>(() => host.engine.createNode())\n\n const flat = StyleSheet.flatten(options.style)\n const { layout, visual } = splitStyle(flat)\n const effectiveLayout = options.extraLayout\n ? { ...layout, ...options.extraLayout }\n : layout\n\n // Re-apply layout style only when it actually changed (keyed effect) —\n // setStyle dirties the whole tree.\n const layoutKey = JSON.stringify(effectiveLayout)\n useLayoutEffect(() => {\n node.setStyle(JSON.parse(layoutKey) as LayoutStyle)\n }, [node, layoutKey])\n\n const cssClass = defaultCssRegistry.getClassName(visual)\n\n useLayoutEffect(() => {\n const parent = host.node\n // The reconciler has already attached our widget in JSX order, so its\n // sibling position is the correct Yoga index — this keeps the shadow tree\n // ordered even for mid-list mounts (conditional children, list inserts).\n const widget = widgetRef.current\n const parentWidget = host.widgetRef.current\n let index = parent.children.length\n if (widget && parentWidget) {\n let position = 0\n let sibling = parentWidget.getFirstChild()\n while (sibling !== null && sibling !== widget) {\n position += 1\n sibling = sibling.getNextSibling()\n }\n if (sibling === widget) {\n index = Math.min(position, parent.children.length)\n }\n }\n parent.insertChild(node, index)\n node.setCommit((rect: Rect) => {\n const widget = widgetRef.current\n const parentWidget = host.widgetRef.current\n if (!widget || !parentWidget) {\n return\n }\n const previous = getStoredRect(widget)\n setStoredRect(widget, rect)\n const sizeChanged =\n !previous ||\n previous.width !== rect.width ||\n previous.height !== rect.height\n const queue = (): void => {\n if (sizeChanged) {\n // A size change invalidates this widget's cached measure (nested\n // managers, ScrolledWindow ranges) and re-allocates the ancestors.\n queueResize(widget)\n } else {\n // Pure move: the parent just needs another allocation pass. GTK\n // dedupes queued allocates, so per-child calls batch into one.\n queueAllocate(parentWidget)\n }\n }\n if (!deferDuringAllocate(widget, queue)) {\n queue()\n }\n })\n return () => {\n node.setCommit(null)\n parent.removeChild(node)\n node.free()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const onLayout = options.onLayout\n useLayoutEffect(() => {\n node.setOnLayout(\n onLayout\n ? (rect: Rect) => {\n onLayout({ nativeEvent: { layout: rect } })\n }\n : null,\n )\n }, [node, onLayout])\n\n // The explicit return type keeps the inner params contextually typed: with a\n // bare lazy initializer TS may match the outer arrow against MeasureFn\n // itself (useState accepts S | () => S) and drop the context.\n const [widgetMeasure] = useState<MeasureFn>(\n (): MeasureFn => (width, widthMode) => {\n const widget = widgetRef.current\n if (!widget) {\n return { width: 0, height: 0 }\n }\n const natural = measureWidget(widget, \"horizontal\").natural\n const used =\n widthMode === \"undefined\"\n ? natural\n : Math.min(natural, Math.max(1, Math.floor(width)))\n return {\n width: used,\n height: measureWidget(widget, \"vertical\", used).natural,\n }\n },\n )\n const measure =\n options.measure ?? (options.measureFromWidget ? widgetMeasure : undefined)\n useLayoutEffect(() => {\n if (!measure) {\n return\n }\n node.setMeasureFn(measure)\n // Widgets measured before their window is mapped report zero (no realized\n // theme context yet, unlike offscreen probes) and Yoga caches it —\n // invalidate once the widget actually maps.\n const widget = widgetRef.current\n const remeasure = (): void => {\n node.markDirty()\n }\n if (options.measureFromWidget && widget) {\n widget.on(\"map\", remeasure)\n }\n return () => {\n if (options.measureFromWidget && widget) {\n widget.off(\"map\", remeasure)\n }\n // Cleared by free() on unmount; explicit unset only when measure is\n // removed while mounted.\n node.setMeasureFn(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [node, measure])\n\n return { host, node, flat, visual, cssClass }\n}\n\nexport type RnContainerOptions = {\n // Overrides the default engine-rect measure (the window root reports 0 so\n // the window can shrink freely below the current content size).\n measure?: (orientation: \"horizontal\" | \"vertical\") => number\n // Runs before children are placed — the window root syncs the engine\n // viewport to the actual allocation here.\n beforeAllocate?: (width: number, height: number) => void\n}\n\n// Containers (Root, View, Animated.View, ScrollView content) drive their GTK\n// side with an RnGtkxLayout: measure() reports the engine rect of `node`\n// (minimum == natural — GTK minimums of children never leak upward), and\n// allocate() places every child widget at exactly its stored engine rect\n// plus the Animated offset.\nexport const useRnContainer = (\n widgetRef: RefObject<Gtk.Widget | null>,\n node: LayoutNode,\n options?: RnContainerOptions,\n): void => {\n const optionsRef = useRef<RnContainerOptions | undefined>(options)\n optionsRef.current = options\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n attachRnLayout(widget, {\n measure: (orientation) => {\n const custom = optionsRef.current?.measure\n if (custom) {\n return custom(orientation)\n }\n const rect = node.getRect()\n return Math.round(\n (orientation === \"horizontal\" ? rect?.width : rect?.height) ?? 0,\n )\n },\n allocate: (width, height) => {\n optionsRef.current?.beforeAllocate?.(width, height)\n let child = widget.getFirstChild()\n while (child) {\n const rect = getStoredRect(child)\n if (rect) {\n const offset = getStoredOffset(child)\n allocateChild(\n child,\n rect.x + offset.dx,\n rect.y + offset.dy,\n rect.width,\n rect.height,\n )\n }\n child = child.getNextSibling()\n }\n },\n })\n return () => {\n detachRnLayout(widget)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { type ReactNode } from "react";
2
+ import type { PointerEventsValue, StyleProp } from "../contracts";
3
+ import { type LayoutEvent } from "./use-layout-child";
4
+ export type ViewProps = {
5
+ style?: StyleProp;
6
+ pointerEvents?: PointerEventsValue;
7
+ onLayout?: (event: LayoutEvent) => void;
8
+ children?: ReactNode;
9
+ testID?: string;
10
+ };
11
+ export declare const View: ({ style, pointerEvents, onLayout, children, testID, }: ViewProps) => React.JSX.Element;
12
+ export declare const SafeAreaView: ({ style, pointerEvents, onLayout, children, testID, }: ViewProps) => React.JSX.Element;
13
+ export declare const StatusBar: () => null;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef } from "react";
3
+ import { StyleSheet } from "../style/index";
4
+ import { getViewBoxComponent, setBoxPassthrough, } from "../gtkx/bridge/index";
5
+ import { HostNodeContext } from "./host-node";
6
+ import { useLayoutChild, useRnContainer, } from "./use-layout-child";
7
+ // Every View is a GtkBox subclass (RnGtkxViewBox) driven by RnGtkxLayout:
8
+ // Yoga computes the children's rects, the manager's allocate() applies
9
+ // them. Visual styles arrive as a GTK CSS class produced by the style
10
+ // system. pointerEvents maps onto GTK picking:
11
+ // - none: can-target=false — GTK skips the widget WITHOUT descending, the
12
+ // whole subtree is transparent (exact RN semantics);
13
+ // - box-none: the subclass' contains() fails for this widget (see
14
+ // view-box.ts) — the box is never the pick target while children stay
15
+ // pickable, and toggling never remounts the subtree;
16
+ // - box-only: direct children (and thus their subtrees) get
17
+ // can-target=false; restored when the mode changes. Nesting another
18
+ // pointerEvents INSIDE a box-only view is not supported (the restore
19
+ // pass cannot know about it).
20
+ export const View = ({ style, pointerEvents, onLayout, children, testID, }) => {
21
+ const widgetRef = useRef(null);
22
+ const { host, node, cssClass } = useLayoutChild(widgetRef, {
23
+ style,
24
+ onLayout,
25
+ });
26
+ useRnContainer(widgetRef, node);
27
+ const mode = pointerEvents ?? StyleSheet.flatten(style)?.pointerEvents ?? "auto";
28
+ useLayoutEffect(() => {
29
+ const widget = widgetRef.current;
30
+ if (!widget) {
31
+ return;
32
+ }
33
+ widget.setCanTarget(mode !== "none");
34
+ setBoxPassthrough(widget, mode === "box-none");
35
+ }, [mode]);
36
+ // box-only walks the current children every commit (the set changes with
37
+ // renders) and restores once when the mode moves away.
38
+ const wasBoxOnly = useRef(false);
39
+ useLayoutEffect(() => {
40
+ const widget = widgetRef.current;
41
+ if (!widget) {
42
+ return;
43
+ }
44
+ const boxOnly = mode === "box-only";
45
+ if (boxOnly || wasBoxOnly.current) {
46
+ let child = widget.getFirstChild();
47
+ while (child) {
48
+ child.setCanTarget(!boxOnly);
49
+ child = child.getNextSibling();
50
+ }
51
+ }
52
+ wasBoxOnly.current = boxOnly;
53
+ });
54
+ const ViewBox = getViewBoxComponent();
55
+ return (_jsx(ViewBox, { ref: widgetRef, name: testID, cssClasses: cssClass ? [cssClass] : [], children: _jsx(HostNodeContext.Provider, { value: { engine: host.engine, node, widgetRef }, children: children }) }));
56
+ };
57
+ // RN parity aliases (no notches or status bars on the Linux desktop).
58
+ export const SafeAreaView = View;
59
+ export const StatusBar = () => null;
60
+ //# sourceMappingURL=view.js.map