flet-box 1.0.4 → 1.0.6

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 (147) hide show
  1. package/README.md +10 -0
  2. package/bin/cli.js +23 -7
  3. package/bin/commands/checkApi.js +761 -0
  4. package/bin/commands/create.js +77 -5
  5. package/bin/utils/dtsProps.js +191 -0
  6. package/bin/utils/moduleExports.js +165 -0
  7. package/bin/utils/neutralise.js +64 -0
  8. package/bin/utils/templates.js +366 -27
  9. package/docs/CONTRIBUTING.md +156 -0
  10. package/docs/README.md +68 -0
  11. package/docs/arquitectura.md +164 -0
  12. package/docs/cli/README.md +542 -0
  13. package/docs/guides/README.md +33 -0
  14. package/docs/guides/app-templates.md +197 -0
  15. package/docs/guides/compatibility.md +95 -0
  16. package/docs/guides/embedding.md +284 -0
  17. package/docs/guides/first-app.md +208 -0
  18. package/docs/guides/frontend-services.md +171 -0
  19. package/docs/guides/minification-and-protection.md +260 -0
  20. package/docs/guides/mobile-and-platforms.md +439 -0
  21. package/docs/guides/online-updates.md +196 -0
  22. package/docs/guides/router.md +282 -0
  23. package/docs/guides/state.md +283 -0
  24. package/docs/guides/syntax-philosophy.md +150 -0
  25. package/docs/guides/utilities.md +241 -0
  26. package/docs/guides/widget-structure.md +163 -0
  27. package/docs/server/README.md +68 -0
  28. package/docs/server/START_HERE.md +121 -0
  29. package/docs/server/api.md +225 -0
  30. package/docs/server/data-services.md +300 -0
  31. package/docs/server/deployment.md +113 -0
  32. package/docs/server/security.md +196 -0
  33. package/docs/services/README.md +79 -0
  34. package/docs/services/RamStore.md +84 -0
  35. package/docs/services/Session.md +84 -0
  36. package/docs/services/Storage.md +88 -0
  37. package/docs/services/http.md +86 -0
  38. package/docs/tools/README.md +138 -0
  39. package/docs/tools/animation.md +87 -0
  40. package/docs/tools/async-ids-log.md +80 -0
  41. package/docs/tools/device.md +132 -0
  42. package/docs/tools/introspection.md +67 -0
  43. package/docs/tools/lists.md +96 -0
  44. package/docs/tools/markdown.md +79 -0
  45. package/docs/tools/reactivity.md +288 -0
  46. package/docs/tools/state.md +103 -0
  47. package/docs/tools/styling.md +92 -0
  48. package/docs/tools/text-and-time.md +89 -0
  49. package/docs/tools/theme.md +102 -0
  50. package/docs/tools/validation.md +75 -0
  51. package/docs/tools/visual-effects.md +84 -0
  52. package/docs/widget/Accordion.md +144 -0
  53. package/docs/widget/AdMob.md +144 -0
  54. package/docs/widget/AdSense.md +133 -0
  55. package/docs/widget/AdaptiveScaffold.md +105 -0
  56. package/docs/widget/AlertDialog.md +103 -0
  57. package/docs/widget/AnimatedBox.md +130 -0
  58. package/docs/widget/AnimatedText.md +123 -0
  59. package/docs/widget/AppBar.md +118 -0
  60. package/docs/widget/Audio.md +135 -0
  61. package/docs/widget/Avatar.md +119 -0
  62. package/docs/widget/Badge.md +128 -0
  63. package/docs/widget/BottomNavigation.md +116 -0
  64. package/docs/widget/BottomSheet.md +125 -0
  65. package/docs/widget/Button.md +137 -0
  66. package/docs/widget/COMMON_PROPS.md +150 -0
  67. package/docs/widget/Card.md +125 -0
  68. package/docs/widget/Carousel.md +151 -0
  69. package/docs/widget/Chart.md +151 -0
  70. package/docs/widget/Checkbox.md +118 -0
  71. package/docs/widget/Chip.md +126 -0
  72. package/docs/widget/CircularBar.md +168 -0
  73. package/docs/widget/CircularChart.md +164 -0
  74. package/docs/widget/CodeViewer.md +130 -0
  75. package/docs/widget/CollapsibleSideBar.md +112 -0
  76. package/docs/widget/Column.md +129 -0
  77. package/docs/widget/Container.md +136 -0
  78. package/docs/widget/DataTable.md +150 -0
  79. package/docs/widget/Divider.md +109 -0
  80. package/docs/widget/DraggBox.md +129 -0
  81. package/docs/widget/Drawer.md +135 -0
  82. package/docs/widget/DrawerItem.md +98 -0
  83. package/docs/widget/Dropdown.md +139 -0
  84. package/docs/widget/DroppBox.md +152 -0
  85. package/docs/widget/FloatingActionButton.md +120 -0
  86. package/docs/widget/GridView.md +140 -0
  87. package/docs/widget/Icon.md +117 -0
  88. package/docs/widget/Image.md +125 -0
  89. package/docs/widget/Input.md +151 -0
  90. package/docs/widget/Inspector.md +92 -0
  91. package/docs/widget/InstallButton.md +128 -0
  92. package/docs/widget/ListTile.md +132 -0
  93. package/docs/widget/ListView.md +138 -0
  94. package/docs/widget/Markdown.md +175 -0
  95. package/docs/widget/MatrixRain.md +122 -0
  96. package/docs/widget/Modal.md +129 -0
  97. package/docs/widget/Pagination.md +135 -0
  98. package/docs/widget/ParallaxBox.md +128 -0
  99. package/docs/widget/ProgressBar.md +130 -0
  100. package/docs/widget/QRCode.md +117 -0
  101. package/docs/widget/README.md +188 -0
  102. package/docs/widget/Radio.md +123 -0
  103. package/docs/widget/Rating.md +131 -0
  104. package/docs/widget/Row.md +131 -0
  105. package/docs/widget/START_HERE.md +146 -0
  106. package/docs/widget/Scaffold.md +152 -0
  107. package/docs/widget/Skeleton.md +130 -0
  108. package/docs/widget/Slider.md +146 -0
  109. package/docs/widget/SnackBar.md +102 -0
  110. package/docs/widget/Stack.md +128 -0
  111. package/docs/widget/Stepper.md +145 -0
  112. package/docs/widget/Switch.md +118 -0
  113. package/docs/widget/Tabs.md +124 -0
  114. package/docs/widget/Text.md +133 -0
  115. package/docs/widget/Tooltip.md +145 -0
  116. package/docs/widget/TreeView.md +176 -0
  117. package/docs/widget/Video.md +127 -0
  118. package/docs/widget/_template.md +61 -0
  119. package/package.json +3 -2
  120. package/src/animations/animate.js +180 -70
  121. package/src/animations/index.js +1 -0
  122. package/src/create-index.sh +1 -1
  123. package/src/index.d.ts +45 -22
  124. package/src/index.js +1 -0
  125. package/src/navigations/AdaptiveScaffold.js +11 -2
  126. package/src/navigations/BottomNavigation.js +10 -3
  127. package/src/navigations/Tabs.js +6 -2
  128. package/src/tools/useState.js +54 -63
  129. package/src/widget-factory/assignProps.js +23 -1
  130. package/src/widget-factory/processProps.js +10 -3
  131. package/src/widget-factory/reactiveValue.js +43 -18
  132. package/src/widget-factory/translateProps.js +777 -901
  133. package/src/widgets/AlertDialog.js +7 -5
  134. package/src/widgets/Checkbox.js +9 -7
  135. package/src/widgets/CircularBar.js +12 -1
  136. package/src/widgets/FloatingActionButton.js +4 -7
  137. package/src/widgets/Image.js +5 -7
  138. package/src/widgets/Input.js +92 -0
  139. package/src/widgets/ListView.js +73 -29
  140. package/src/widgets/Pagination.js +6 -3
  141. package/src/widgets/ProgressBar.js +16 -13
  142. package/src/widgets/Radio.js +6 -7
  143. package/src/widgets/Slider.js +23 -4
  144. package/src/widgets/Stack.js +36 -3
  145. package/src/widgets/Stepper.js +4 -3
  146. package/src/widgets/Switch.js +3 -4
  147. package/src/widgets/TreeView.js +0 -1
@@ -1,6 +1,10 @@
1
1
  // src/tools/useState.js
2
2
  import { saveRam, getRam, subscribeRam } from "../services/RamStore.js";
3
- import { createReactiveValue, setImperativeReadListener } from "../widget-factory/reactiveValue.js";
3
+ import {
4
+ createReactiveValue,
5
+ isStructuralRead,
6
+ resetStructuralReads,
7
+ } from "../widget-factory/reactiveValue.js";
4
8
 
5
9
  // Store callbacks for each state key
6
10
  const subscribers = new Map();
@@ -8,13 +12,6 @@ const subscribers = new Map();
8
12
  // 🔥 Store widgets by state key for automatic updates
9
13
  const widgetsByKey = new Map();
10
14
 
11
- // Keys the component reads *structurally* — inside a template literal, a
12
- // conditional, a list. Those need the app render even when a widget is bound to
13
- // the key, because a surgical prop patch cannot change what the component
14
- // computes. A key passed straight into a prop is never listed here.
15
- const imperativeKeys = new Set();
16
- setImperativeReadListener((key) => imperativeKeys.add(key));
17
-
18
15
  // Global render function (set by runApp)
19
16
  let globalRender = null;
20
17
  let renderTimeout = null;
@@ -81,10 +78,10 @@ const ensureRamDispatcher = () => {
81
78
  // 2. Notify the manual subscribers.
82
79
  callbacks.forEach((callback) => callback(newValue, oldValue));
83
80
 
84
- // 3. Re-render the app when nothing was bound, or when the component also
85
- // reads this key structurally (a template literal, a conditional, a list):
86
- // the surgical patch above cannot change what the component computes.
87
- if (handled === 0 || imperativeKeys.has(changedKey)) triggerRender();
81
+ // 3. Re-render the app when nothing was bound, or when the last render pass
82
+ // also read this key structurally (a template literal, a conditional, a
83
+ // list): the surgical patch above cannot change what the component computes.
84
+ if (handled === 0 || isStructuralRead(changedKey)) triggerRender();
88
85
  });
89
86
 
90
87
  if (typeof window !== "undefined") {
@@ -131,38 +128,21 @@ export const registerWidget = (key, widget, propName) => {
131
128
  if (already) return;
132
129
 
133
130
  // Release the binding through `onUnmount`, which the framework fires on BOTH
134
- // teardown paths. Patching `_cleanup` here (the previous approach) leaked: the
131
+ // teardown paths. Patching `_cleanup` (the previous approach) leaked: the
135
132
  // lifecycle observer never calls `_cleanup`, so a widget removed without a full
136
133
  // app rebuild stayed registered forever and kept receiving updates.
137
134
  const release = () => {
138
- const map = widgetsByKey.get(key);
139
- const props = map && map.get(widget);
135
+ const perWidget = widgetsByKey.get(key);
136
+ const props = perWidget && perWidget.get(widget);
140
137
  if (!props) return;
141
138
  props.delete(propName);
142
- if (props.size === 0) map.delete(widget);
139
+ if (props.size === 0) perWidget.delete(widget);
140
+ if (perWidget.size === 0) widgetsByKey.delete(key);
143
141
  };
144
142
 
145
- if (typeof widget.onUnmount === "function") {
146
- widget.onUnmount(release);
147
- } else {
148
- const originalCleanup = widget._cleanup;
149
- widget._cleanup = () => {
150
- release();
151
- if (originalCleanup) originalCleanup();
152
- };
153
- }
154
- };
155
-
156
- /**
157
- * Removes a specific widget from the automatic-update registry for a given key.
158
- *
159
- * @param {string} key - The state key the widget was registered under.
160
- * @param {Object} widget - The widget instance to unregister.
161
- * @returns {void}
162
- */
163
- export const unregisterWidget = (key, widget) => {
164
- const perWidget = widgetsByKey.get(key);
165
- if (perWidget) perWidget.delete(widget);
143
+ // The factory installs lifecycle hooks before it binds reactive props, so a
144
+ // widget reaching this point always owns `onUnmount`.
145
+ widget.onUnmount(release);
166
146
  };
167
147
 
168
148
  /**
@@ -184,11 +164,12 @@ export const unregisterWidget = (key, widget) => {
184
164
  */
185
165
  export const useState = (key, initialState, widget = null, propName = null) => {
186
166
  // Initialize the state in RamStore if it does not exist yet
187
- let currentValue = getRam(key);
188
- if (currentValue === undefined || currentValue === null) {
189
- currentValue =
190
- typeof initialState === "function" ? initialState() : initialState;
191
- saveRam(key, currentValue);
167
+ const existing = getRam(key);
168
+ if (existing === undefined || existing === null) {
169
+ saveRam(
170
+ key,
171
+ typeof initialState === "function" ? initialState() : initialState,
172
+ );
192
173
  }
193
174
 
194
175
  // Register the key so the shared dispatcher knows it has listeners. Previously
@@ -212,19 +193,18 @@ export const useState = (key, initialState, widget = null, propName = null) => {
212
193
  * @returns {void}
213
194
  */
214
195
  const setState = (newValueOrUpdater) => {
215
- const oldValue = currentValue;
216
- let newValue;
217
-
218
- if (typeof newValueOrUpdater === "function") {
219
- newValue = newValueOrUpdater(oldValue);
220
- } else {
221
- newValue = newValueOrUpdater;
222
- }
223
-
224
- if (oldValue !== newValue) {
225
- currentValue = newValue;
226
- saveRam(key, currentValue);
227
- }
196
+ // Read the store rather than a value captured when the hook ran: a setter
197
+ // kept across renders would otherwise compare against a stale `oldValue` and
198
+ // skip the dispatch when an external `saveState` had already moved the key to
199
+ // the value being set — leaving the store, the widgets and the callbacks
200
+ // disagreeing about what the state is.
201
+ const oldValue = getRam(key);
202
+ const newValue =
203
+ typeof newValueOrUpdater === "function"
204
+ ? newValueOrUpdater(oldValue)
205
+ : newValueOrUpdater;
206
+
207
+ if (oldValue !== newValue) saveRam(key, newValue);
228
208
  };
229
209
 
230
210
  /**
@@ -239,9 +219,7 @@ export const useState = (key, initialState, widget = null, propName = null) => {
239
219
  }
240
220
  subscribers.get(key).add(callback);
241
221
  return () => {
242
- if (subscribers.has(key)) {
243
- subscribers.get(key).delete(callback);
244
- }
222
+ subscribers.get(key)?.delete(callback);
245
223
  };
246
224
  };
247
225
 
@@ -268,7 +246,9 @@ export const useWatchState = (key, callback) => {
268
246
  subscribers.set(key, new Set());
269
247
  }
270
248
  subscribers.get(key).add(callback);
271
- return () => subscribers.get(key).delete(callback);
249
+ return () => {
250
+ subscribers.get(key)?.delete(callback);
251
+ };
272
252
  };
273
253
 
274
254
  /**
@@ -286,7 +266,7 @@ export const cleanupUseState = () => {
286
266
  }
287
267
  subscribers.clear();
288
268
  widgetsByKey.clear();
289
- imperativeKeys.clear();
269
+ resetStructuralReads();
290
270
  // The dispatcher was unsubscribed above, so a later call must reinstall it.
291
271
  ramDispatcherReady = false;
292
272
  globalRender = null;
@@ -297,12 +277,23 @@ export const cleanupUseState = () => {
297
277
  * Returns diagnostic statistics about the current state of the useState module.
298
278
  * Useful for debugging subscription leaks or widget registration issues.
299
279
  *
300
- * @returns {{ subscribersCount: number, widgetsCount: number, globalRender: boolean }}
280
+ * @returns {{ stateKeys: number, boundWidgets: number, watchers: number, globalRender: boolean }}
301
281
  */
302
282
  export const getUseStateStats = () => {
283
+ // `widgetsByKey` maps key -> widget -> prop names, so its size is the number of
284
+ // watched *keys*, not of widgets. Count one per widget.
285
+ let boundWidgets = 0;
286
+ let watchers = 0;
287
+ widgetsByKey.forEach((perWidget) => {
288
+ boundWidgets += perWidget.size;
289
+ });
290
+ subscribers.forEach((callbacks) => {
291
+ watchers += callbacks.size;
292
+ });
303
293
  return {
304
- subscribersCount: subscribers.size,
305
- widgetsCount: widgetsByKey.size,
294
+ stateKeys: subscribers.size,
295
+ boundWidgets,
296
+ watchers,
306
297
  globalRender: !!globalRender,
307
298
  };
308
299
  };
@@ -59,6 +59,28 @@ const applySpecial = (widget, key, value) => {
59
59
  case "variant":
60
60
  widget.dataset.variant = val;
61
61
  break;
62
+ case "visible": {
63
+ // Hiding must not destroy the `display` the widget's own styles chose
64
+ // (`flex` for a Row, `inline-flex` for an Input…), so the previous inline
65
+ // value is stashed and handed back on show. The stash is only reused while
66
+ // we are still the reason the element is hidden: if an update set a new
67
+ // `display` in the meantime, that value is the truth and is left alone.
68
+ if (val) {
69
+ if (
70
+ widget._hiddenByVisibleProp !== undefined &&
71
+ widget.style.display === "none"
72
+ ) {
73
+ widget.style.display = widget._hiddenByVisibleProp;
74
+ }
75
+ delete widget._hiddenByVisibleProp;
76
+ } else if (widget.style.display !== "none") {
77
+ if (widget._hiddenByVisibleProp === undefined) {
78
+ widget._hiddenByVisibleProp = widget.style.display;
79
+ }
80
+ widget.style.display = "none";
81
+ }
82
+ break;
83
+ }
62
84
  }
63
85
  };
64
86
 
@@ -72,7 +94,7 @@ const applySpecial = (widget, key, value) => {
72
94
  * `click` is assigned via `onclick` for reliable override behaviour.
73
95
  * 4. **attributes** – applied with `setAttribute`, with special handling for
74
96
  * `className` and boolean attributes (`disabled`, `readOnly`, `hidden`, …).
75
- * 5. **special** – delegated to `applySpecial` (e.g. `value`, `variant`).
97
+ * 5. **special** – delegated to `applySpecial` (`value`, `variant`, `visible`).
76
98
  *
77
99
  * Any keys not in the known set are handled as a flat-prop fallback: `on*`
78
100
  * functions become event listeners, common HTML attributes are set directly, and
@@ -1,5 +1,5 @@
1
1
  // core/processProps.js
2
- import { getPropDefinition, ATTRIBUTE_DOM_SET } from "./translateProps.js";
2
+ import { getPropDefinition, ATTRIBUTE_DOM_SET, PASSTHROUGH_PREFIXES } from "./translateProps.js";
3
3
  import { toREM } from "./remTool.js"; // Centralized REM conversion utility
4
4
  import { resolveReactive } from "./reactiveValue.js";
5
5
 
@@ -53,6 +53,7 @@ export const processProps = (props, tag = "div", widgetName = null) => {
53
53
  // Shorthands may fan out to several CSS props (e.g. `px` →
54
54
  // paddingLeft + paddingRight). Everything else writes to its domProp.
55
55
  const cssValue = applyUnit(def, value);
56
+ if (cssValue === undefined) break;
56
57
  const targets = def.targets || [def.domProp];
57
58
  for (const target of targets) style[target] = cssValue;
58
59
  break;
@@ -84,7 +85,7 @@ export const processProps = (props, tag = "div", widgetName = null) => {
84
85
  }
85
86
  } else {
86
87
  // Unknown prop → intelligent fallback
87
- if (key.startsWith("data-") || key.startsWith("aria-")) {
88
+ if (PASSTHROUGH_PREFIXES.some((prefix) => key.startsWith(prefix))) {
88
89
  attributes[key] = value;
89
90
  } else if (key.startsWith("on") && typeof value === "function") {
90
91
  const eventName = key.slice(2).toLowerCase();
@@ -103,6 +104,9 @@ export const processProps = (props, tag = "div", widgetName = null) => {
103
104
  /**
104
105
  * Applies the correct CSS unit to a numeric value based on the prop definition.
105
106
  *
107
+ * - A prop with `boolValues` maps `true`/`false` to the CSS the property
108
+ * actually accepts (e.g. `wrap: true` → `flex-wrap: wrap`); an `undefined`
109
+ * mapping skips the assignment.
106
110
  * - `true` / `false` are coerced to `1` / `0` (unitless).
107
111
  * - Non-numeric values pass through unchanged.
108
112
  * - `unit: "none"` returns the raw number without a suffix.
@@ -111,9 +115,12 @@ export const processProps = (props, tag = "div", widgetName = null) => {
111
115
  *
112
116
  * @param {Object} def - Prop definition from the prop database (see `translateProps.js`).
113
117
  * @param {*} value - The value to convert.
114
- * @returns {string|number} Value with the correct CSS unit applied.
118
+ * @returns {string|number|undefined} Value with the correct CSS unit applied.
115
119
  */
116
120
  function applyUnit(def, value) {
121
+ if (def.boolValues && typeof value === "boolean") {
122
+ return def.boolValues[String(value)];
123
+ }
117
124
  if (value === true) return 1;
118
125
  if (value === false) return 0;
119
126
  if (typeof value !== "number") return value;
@@ -38,28 +38,48 @@ let resolving = false;
38
38
  */
39
39
  let renderPassDepth = 0;
40
40
 
41
- /** Called with the state key when the component reads a reactive value. */
42
- let onImperativeRead = null;
43
-
44
41
  /**
45
- * Registers the listener told about structural reads. The state module uses it to
46
- * know which keys still need the app render in addition to the surgical patch.
42
+ * Keys the component read structurally during the pass that just finished.
47
43
  *
48
- * @param {(key: string) => void} listener
49
- * @returns {void}
44
+ * Rebuilt by every render pass: a component re-reads everything it consumes, so
45
+ * what the last pass saw is the complete picture. Keeping the keys of *earlier*
46
+ * passes instead would pin the app to full re-renders forever after one
47
+ * conditional read, which is exactly what the surgical binding exists to avoid.
50
48
  */
51
- export const setImperativeReadListener = (listener) => {
52
- onImperativeRead = listener;
53
- };
49
+ let structuralKeys = new Set();
50
+
51
+ /** The keys collected by the pass currently running, or `null` between passes. */
52
+ let passKeys = null;
54
53
 
55
54
  /** Marks the start of a component-render pass. */
56
55
  export const beginRenderPass = () => {
56
+ if (renderPassDepth === 0) passKeys = new Set();
57
57
  renderPassDepth++;
58
58
  };
59
59
 
60
- /** Marks the end of a component-render pass. */
60
+ /** Marks the end of a component-render pass and publishes what it observed. */
61
61
  export const endRenderPass = () => {
62
- if (renderPassDepth > 0) renderPassDepth--;
62
+ if (renderPassDepth === 0) return;
63
+ renderPassDepth--;
64
+ if (renderPassDepth === 0) {
65
+ if (passKeys) structuralKeys = passKeys;
66
+ passKeys = null;
67
+ }
68
+ };
69
+
70
+ /**
71
+ * True when the last render pass consumed `key` structurally, so a change to it
72
+ * needs an app render on top of the surgical prop patch.
73
+ *
74
+ * @param {string} key
75
+ * @returns {boolean}
76
+ */
77
+ export const isStructuralRead = (key) => structuralKeys.has(key);
78
+
79
+ /** Forgets every observed key. Used by hot-reload teardown. */
80
+ export const resetStructuralReads = () => {
81
+ structuralKeys = new Set();
82
+ passKeys = renderPassDepth > 0 ? new Set() : null;
63
83
  };
64
84
 
65
85
  /**
@@ -90,11 +110,15 @@ export const reactiveKeyOf = (val) => (isReactive(val) ? val._stateKey ?? null :
90
110
  */
91
111
  export const resolveReactive = (val) => {
92
112
  if (typeof val === "function" && val._isReactive) {
113
+ // Save and restore instead of clearing: a reactive whose `valueOf` reads
114
+ // another one would otherwise leave the inner read flagged as the factory's
115
+ // and the outer one reported as a structural read.
116
+ const wasResolving = resolving;
93
117
  resolving = true;
94
118
  try {
95
119
  return val();
96
120
  } finally {
97
- resolving = false;
121
+ resolving = wasResolving;
98
122
  }
99
123
  }
100
124
  if (val && typeof val === "object" && val._isReactive === true) {
@@ -111,9 +135,9 @@ export const resolveReactive = (val) => {
111
135
  * arithmetic work) and a `_value` getter (so the legacy `state._value` read
112
136
  * keeps working — now live instead of frozen).
113
137
  *
114
- * Every read that the component itself performs during a render pass is reported
115
- * through `onImperativeRead`, because such a read means the value is consumed
116
- * structurally and the app must re-render too.
138
+ * Every read that the component itself performs during a render pass is recorded
139
+ * in the pass's structural-key set, because such a read means the value is
140
+ * consumed structurally and the app must re-render too.
117
141
  *
118
142
  * @param {() => *} read - Reads the current value from the store.
119
143
  * @param {string} key - The state key this value is bound to.
@@ -122,7 +146,7 @@ export const resolveReactive = (val) => {
122
146
  export const createReactiveValue = (read, key) => {
123
147
  const live = () => {
124
148
  const value = read();
125
- if (!resolving && renderPassDepth > 0 && onImperativeRead) onImperativeRead(key);
149
+ if (!resolving && passKeys) passKeys.add(key);
126
150
  return value;
127
151
  };
128
152
  live._isReactive = true;
@@ -141,7 +165,8 @@ export default {
141
165
  reactiveKeyOf,
142
166
  resolveReactive,
143
167
  createReactiveValue,
144
- setImperativeReadListener,
168
+ isStructuralRead,
169
+ resetStructuralReads,
145
170
  beginRenderPass,
146
171
  endRenderPass,
147
172
  };