flet-box 1.0.3 → 1.0.5

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 (172) hide show
  1. package/README.md +10 -0
  2. package/bin/cli.js +15 -0
  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 +4 -2
  120. package/src/animations/AnimatedBox.js +32 -1
  121. package/src/animations/AnimatedText.js +7 -3
  122. package/src/animations/MatrixRain.js +37 -12
  123. package/src/animations/ParallaxBox.js +16 -3
  124. package/src/animations/animate.js +187 -58
  125. package/src/animations/index.js +1 -0
  126. package/src/core/runApp.js +13 -1
  127. package/src/create-index.sh +1 -1
  128. package/src/index.d.ts +227 -91
  129. package/src/index.js +1 -0
  130. package/src/navigations/AdaptiveScaffold.js +11 -2
  131. package/src/navigations/AppBar.js +8 -2
  132. package/src/navigations/BottomNavigation.js +24 -5
  133. package/src/navigations/DrawerItem.js +16 -3
  134. package/src/navigations/Scaffold.js +8 -1
  135. package/src/navigations/Tabs.js +40 -8
  136. package/src/tools/dimensions.js +11 -3
  137. package/src/tools/useState.js +139 -101
  138. package/src/utils/getWidgetProps.js +5 -3
  139. package/src/widget-factory/assignProps.js +30 -11
  140. package/src/widget-factory/processProps.js +17 -5
  141. package/src/widget-factory/reactiveValue.js +172 -0
  142. package/src/widget-factory/translateProps.js +777 -901
  143. package/src/widget-factory/warnings.js +15 -0
  144. package/src/widget-factory/widgetFactory.js +27 -0
  145. package/src/widgets/Accordion.js +6 -5
  146. package/src/widgets/AlertDialog.js +7 -5
  147. package/src/widgets/Audio.js +30 -8
  148. package/src/widgets/BottomSheet.js +24 -1
  149. package/src/widgets/Chart.js +4 -1
  150. package/src/widgets/Checkbox.js +16 -8
  151. package/src/widgets/CircularBar.js +12 -1
  152. package/src/widgets/CircularChart.js +4 -1
  153. package/src/widgets/CodeViewer.js +26 -8
  154. package/src/widgets/DataTable.js +11 -2
  155. package/src/widgets/Dropdown.js +5 -1
  156. package/src/widgets/FloatingActionButton.js +4 -7
  157. package/src/widgets/Image.js +7 -2
  158. package/src/widgets/Input.js +119 -6
  159. package/src/widgets/Inspector.js +16 -10
  160. package/src/widgets/ListView.js +80 -30
  161. package/src/widgets/Markdown.js +53 -8
  162. package/src/widgets/Modal.js +24 -1
  163. package/src/widgets/Pagination.js +6 -3
  164. package/src/widgets/ProgressBar.js +16 -13
  165. package/src/widgets/QRCode.js +9 -2
  166. package/src/widgets/Radio.js +11 -8
  167. package/src/widgets/Slider.js +26 -4
  168. package/src/widgets/SnackBar.js +20 -2
  169. package/src/widgets/Stack.js +36 -3
  170. package/src/widgets/Stepper.js +4 -3
  171. package/src/widgets/Switch.js +8 -5
  172. package/src/widgets/TreeView.js +3 -2
@@ -1,5 +1,6 @@
1
1
  // core/assignProps.js
2
2
  import { setStyles } from "./tools.js";
3
+ import { resolveReactive } from "./reactiveValue.js";
3
4
 
4
5
  /**
5
6
  * Extracts the real value from a prop that may be a reactive accessor or a
@@ -14,15 +15,7 @@ import { setStyles } from "./tools.js";
14
15
  * @param {*} val - The raw prop value (may be reactive or plain).
15
16
  * @returns {*} The resolved runtime value.
16
17
  */
17
- const getValue = (val) => {
18
- if (typeof val === "function" && val._isReactive) {
19
- return val();
20
- }
21
- if (val && typeof val === "object" && val._isReactive === true) {
22
- return val._value !== undefined ? val._value : val.valueOf();
23
- }
24
- return val;
25
- };
18
+ const getValue = resolveReactive;
26
19
 
27
20
  /**
28
21
  * HTML attributes whose mere presence means "true". Writing them through
@@ -66,6 +59,28 @@ const applySpecial = (widget, key, value) => {
66
59
  case "variant":
67
60
  widget.dataset.variant = val;
68
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
+ }
69
84
  }
70
85
  };
71
86
 
@@ -79,7 +94,7 @@ const applySpecial = (widget, key, value) => {
79
94
  * `click` is assigned via `onclick` for reliable override behaviour.
80
95
  * 4. **attributes** – applied with `setAttribute`, with special handling for
81
96
  * `className` and boolean attributes (`disabled`, `readOnly`, `hidden`, …).
82
- * 5. **special** – delegated to `applySpecial` (e.g. `value`, `variant`).
97
+ * 5. **special** – delegated to `applySpecial` (`value`, `variant`, `visible`).
83
98
  *
84
99
  * Any keys not in the known set are handled as a flat-prop fallback: `on*`
85
100
  * functions become event listeners, common HTML attributes are set directly, and
@@ -110,7 +125,11 @@ export const assignProps = (widget, props) => {
110
125
  // ============================================================
111
126
  // 2. TEXT
112
127
  // ============================================================
113
- if (props.textContent !== undefined) {
128
+ // `null` is `processProps`' sentinel for "this prop set carries no text".
129
+ // Only an explicitly present value (including "") may touch textContent:
130
+ // assigning null stringifies to "" and therefore REMOVES every child, which
131
+ // silently wiped widgets on any `update()` that did not re-pass `child`.
132
+ if (props.textContent !== undefined && props.textContent !== null) {
114
133
  const textValue = getValue(props.textContent);
115
134
  if (widget.tagName === "INPUT" || widget.tagName === "TEXTAREA") {
116
135
  if (widget.value !== textValue) widget.value = textValue;
@@ -1,6 +1,7 @@
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
+ import { resolveReactive } from "./reactiveValue.js";
4
5
 
5
6
  /**
6
7
  * Processes raw props and classifies them into:
@@ -26,8 +27,12 @@ export const processProps = (props, tag = "div", widgetName = null) => {
26
27
  // Derive widget name from tag (e.g., 'div' → 'Div', 'span' → 'Span')
27
28
  const name = widgetName || tag.charAt(0).toUpperCase() + tag.slice(1);
28
29
 
29
- for (const [key, value] of Object.entries(props)) {
30
- if (value === undefined || value === null) continue;
30
+ for (const [key, rawValue] of Object.entries(props)) {
31
+ if (rawValue === undefined || rawValue === null) continue;
32
+ // Resolve the reactive-prop protocol once, here. A live value must be READ
33
+ // now so `width: count` yields a number (and gets its px/rem unit), and a
34
+ // function must never reach a CSS assignment.
35
+ const value = resolveReactive(rawValue);
31
36
 
32
37
  // `style` is deliberately NOT part of the API: FletBox uses flat, aliased
33
38
  // CSS props. Fail loudly instead of silently ignoring the object.
@@ -48,6 +53,7 @@ export const processProps = (props, tag = "div", widgetName = null) => {
48
53
  // Shorthands may fan out to several CSS props (e.g. `px` →
49
54
  // paddingLeft + paddingRight). Everything else writes to its domProp.
50
55
  const cssValue = applyUnit(def, value);
56
+ if (cssValue === undefined) break;
51
57
  const targets = def.targets || [def.domProp];
52
58
  for (const target of targets) style[target] = cssValue;
53
59
  break;
@@ -79,7 +85,7 @@ export const processProps = (props, tag = "div", widgetName = null) => {
79
85
  }
80
86
  } else {
81
87
  // Unknown prop → intelligent fallback
82
- if (key.startsWith("data-") || key.startsWith("aria-")) {
88
+ if (PASSTHROUGH_PREFIXES.some((prefix) => key.startsWith(prefix))) {
83
89
  attributes[key] = value;
84
90
  } else if (key.startsWith("on") && typeof value === "function") {
85
91
  const eventName = key.slice(2).toLowerCase();
@@ -98,6 +104,9 @@ export const processProps = (props, tag = "div", widgetName = null) => {
98
104
  /**
99
105
  * Applies the correct CSS unit to a numeric value based on the prop definition.
100
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.
101
110
  * - `true` / `false` are coerced to `1` / `0` (unitless).
102
111
  * - Non-numeric values pass through unchanged.
103
112
  * - `unit: "none"` returns the raw number without a suffix.
@@ -106,9 +115,12 @@ export const processProps = (props, tag = "div", widgetName = null) => {
106
115
  *
107
116
  * @param {Object} def - Prop definition from the prop database (see `translateProps.js`).
108
117
  * @param {*} value - The value to convert.
109
- * @returns {string|number} Value with the correct CSS unit applied.
118
+ * @returns {string|number|undefined} Value with the correct CSS unit applied.
110
119
  */
111
120
  function applyUnit(def, value) {
121
+ if (def.boolValues && typeof value === "boolean") {
122
+ return def.boolValues[String(value)];
123
+ }
112
124
  if (value === true) return 1;
113
125
  if (value === false) return 0;
114
126
  if (typeof value !== "number") return value;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * @file reactiveValue.js
3
+ * @description
4
+ * The reactive-prop protocol shared by `useState`, the widget factory and the
5
+ * prop pipeline.
6
+ *
7
+ * A prop value may be a *reactive value*: an object or function flagged with
8
+ * `_isReactive` that reports the current value of a state key. The factory
9
+ * resolves it whenever the prop is applied, and — when the value carries a
10
+ * `_stateKey` — it also **subscribes** the widget to that key so a later change
11
+ * updates only this widget instead of re-rendering the whole app.
12
+ *
13
+ * Two shapes are supported:
14
+ *
15
+ * - **Live** (what `useState` returns): a callable that reads the store on every
16
+ * call. `valueOf`/`toString` also read live, so `${count}`, `count + 1` and
17
+ * CSS assignment all get the current value.
18
+ * - **Snapshot** (legacy): an object with a frozen `_value`. Still resolved, but
19
+ * it cannot auto-subscribe because it has no live source.
20
+ *
21
+ * @module widget-factory/reactiveValue
22
+ */
23
+
24
+ /**
25
+ * True while `resolveReactive` is reading a value on behalf of the factory.
26
+ *
27
+ * The distinction matters: a read the FACTORY performs (to fill a prop) is a
28
+ * direct binding, but a read the COMPONENT performs (`${count}`, `count > 3`,
29
+ * `count.valueOf()`) means the value also drives structure or computation, so the
30
+ * app has to re-run as well. Reconciling the two is what keeps
31
+ * `Text({ text: count })` surgical without breaking a sibling conditional.
32
+ */
33
+ let resolving = false;
34
+
35
+ /**
36
+ * Depth of the component-render pass, so reads in an event handler or a timer
37
+ * (which run outside the pass) do not mark a key as structurally read.
38
+ */
39
+ let renderPassDepth = 0;
40
+
41
+ /**
42
+ * Keys the component read structurally during the pass that just finished.
43
+ *
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.
48
+ */
49
+ let structuralKeys = new Set();
50
+
51
+ /** The keys collected by the pass currently running, or `null` between passes. */
52
+ let passKeys = null;
53
+
54
+ /** Marks the start of a component-render pass. */
55
+ export const beginRenderPass = () => {
56
+ if (renderPassDepth === 0) passKeys = new Set();
57
+ renderPassDepth++;
58
+ };
59
+
60
+ /** Marks the end of a component-render pass and publishes what it observed. */
61
+ export const endRenderPass = () => {
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;
83
+ };
84
+
85
+ /**
86
+ * True when `val` is a reactive value (live or snapshot).
87
+ *
88
+ * @param {*} val
89
+ * @returns {boolean}
90
+ */
91
+ export const isReactive = (val) => Boolean(val) && val._isReactive === true;
92
+
93
+ /**
94
+ * The state key a reactive value is bound to, or `null` when it is not bound.
95
+ *
96
+ * @param {*} val
97
+ * @returns {string|null}
98
+ */
99
+ export const reactiveKeyOf = (val) => (isReactive(val) ? val._stateKey ?? null : null);
100
+
101
+ /**
102
+ * Resolves a reactive value to its current value. Plain values pass through.
103
+ *
104
+ * A live value is invoked, which is what makes it stay current: every time the
105
+ * prop is re-applied the store is read again. The read is flagged as a factory
106
+ * read so it is not mistaken for a structural one.
107
+ *
108
+ * @param {*} val
109
+ * @returns {*}
110
+ */
111
+ export const resolveReactive = (val) => {
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;
117
+ resolving = true;
118
+ try {
119
+ return val();
120
+ } finally {
121
+ resolving = wasResolving;
122
+ }
123
+ }
124
+ if (val && typeof val === "object" && val._isReactive === true) {
125
+ return val._value !== undefined ? val._value : val.valueOf();
126
+ }
127
+ return val;
128
+ };
129
+
130
+ /**
131
+ * Builds the live reactive value returned by `useState`.
132
+ *
133
+ * It is a **function** (so `resolveReactive` calls it and always gets the
134
+ * current value) decorated with `valueOf`/`toString` (so template literals and
135
+ * arithmetic work) and a `_value` getter (so the legacy `state._value` read
136
+ * keeps working — now live instead of frozen).
137
+ *
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.
141
+ *
142
+ * @param {() => *} read - Reads the current value from the store.
143
+ * @param {string} key - The state key this value is bound to.
144
+ * @returns {Function} The live reactive value.
145
+ */
146
+ export const createReactiveValue = (read, key) => {
147
+ const live = () => {
148
+ const value = read();
149
+ if (!resolving && passKeys) passKeys.add(key);
150
+ return value;
151
+ };
152
+ live._isReactive = true;
153
+ live._stateKey = key;
154
+ live.valueOf = () => live();
155
+ live.toString = () => String(live());
156
+ Object.defineProperty(live, "_value", {
157
+ get: () => live(),
158
+ configurable: true,
159
+ });
160
+ return live;
161
+ };
162
+
163
+ export default {
164
+ isReactive,
165
+ reactiveKeyOf,
166
+ resolveReactive,
167
+ createReactiveValue,
168
+ isStructuralRead,
169
+ resetStructuralReads,
170
+ beginRenderPass,
171
+ endRenderPass,
172
+ };