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
@@ -0,0 +1,282 @@
1
+ # Routing with FletBox
2
+
3
+ The FletBox router changes the visible screen without rebuilding a whole HTML page. It supports fixed paths, dynamic parameters, query values, browser history, and route subscriptions.
4
+
5
+ ## Create routes
6
+
7
+ A route map connects URL paths with widgets or screen functions:
8
+
9
+ ```javascript
10
+ import { Container, Text, runApp } from "flet-box";
11
+
12
+ const HomeScreen = () => Container({
13
+ padding: 24,
14
+ child: Text({ text: "Home" }),
15
+ });
16
+
17
+ const AboutScreen = () => Container({
18
+ padding: 24,
19
+ child: Text({ text: "About" }),
20
+ });
21
+
22
+ const routes = {
23
+ "/": HomeScreen,
24
+ "/about": AboutScreen,
25
+ };
26
+
27
+ runApp(HomeScreen, "root", true, routes);
28
+ ```
29
+
30
+ When routes are passed to `runApp`, the router is initialized automatically.
31
+
32
+ ## Navigate with `goTo`
33
+
34
+ ```javascript
35
+ import { Button, goTo } from "flet-box";
36
+
37
+ Button({
38
+ text: "Open About",
39
+ onPress: () => goTo("/about"),
40
+ });
41
+ ```
42
+
43
+ The browser URL and the current route are updated together.
44
+
45
+ ## Use a dynamic parameter
46
+
47
+ Define a parameter with `:name`:
48
+
49
+ ```javascript
50
+ const routes = {
51
+ "/users/:id": UserScreen,
52
+ };
53
+ ```
54
+
55
+ Read it inside the screen:
56
+
57
+ ```javascript
58
+ import { Text, useParams } from "flet-box";
59
+
60
+ const UserScreen = () => {
61
+ const { id } = useParams();
62
+ return Text({ text: `User: ${id}` });
63
+ };
64
+ ```
65
+
66
+ Navigate to it:
67
+
68
+ ```javascript
69
+ goTo("/users/42");
70
+ ```
71
+
72
+ The value is decoded and available as `id: "42"`.
73
+
74
+ ## Query parameters
75
+
76
+ Navigate with a query string:
77
+
78
+ ```javascript
79
+ goTo("/search?term=widgets", { page: 2 });
80
+ ```
81
+
82
+ Read query values:
83
+
84
+ ```javascript
85
+ import { Text, useQueryParams } from "flet-box";
86
+
87
+ const SearchScreen = () => {
88
+ const { term, page } = useQueryParams();
89
+ return Text({ text: `Search: ${term}, page ${page}` });
90
+ };
91
+ ```
92
+
93
+ You can also build URLs safely:
94
+
95
+ ```javascript
96
+ import { buildUrl } from "flet-box";
97
+
98
+ const url = buildUrl(
99
+ "/users/:id",
100
+ { id: 42 },
101
+ { tab: "profile" },
102
+ );
103
+
104
+ // /users/42?tab=profile
105
+ ```
106
+
107
+ ## Go back and forward
108
+
109
+ ```javascript
110
+ import { goBack, goForward } from "flet-box";
111
+
112
+ goBack();
113
+ goForward();
114
+ ```
115
+
116
+ `replace()` changes the current URL without adding another entry to the application history:
117
+
118
+ ```javascript
119
+ import { replace } from "flet-box";
120
+
121
+ replace("/login");
122
+ ```
123
+
124
+ ## Read the current route
125
+
126
+ ```javascript
127
+ import {
128
+ getCurrentPath,
129
+ getCurrentRoute,
130
+ getCurrentRouteConfig,
131
+ isActive,
132
+ } from "flet-box";
133
+
134
+ const path = getCurrentPath();
135
+ const route = getCurrentRoute();
136
+ const config = getCurrentRouteConfig();
137
+ const isHome = isActive("/", true);
138
+ ```
139
+
140
+ `getCurrentRouteConfig()` returns the route, params, query, and path together.
141
+
142
+ ## Subscribe to navigation
143
+
144
+ ```javascript
145
+ import { subscribe } from "flet-box";
146
+
147
+ const unsubscribe = subscribe((route, params, query) => {
148
+ console.log({ route, params, query });
149
+ });
150
+
151
+ // Call when the integration is destroyed.
152
+ unsubscribe();
153
+ ```
154
+
155
+ The router also listens to browser back and forward events.
156
+
157
+ ## Router with `Scaffold`
158
+
159
+ For larger applications, combine routes with `Scaffold`:
160
+
161
+ ```javascript
162
+ import { Scaffold, Text, runApp } from "flet-box";
163
+
164
+ const routes = {
165
+ "/": () => Text({ text: "Home" }),
166
+ "/settings": () => Text({ text: "Settings" }),
167
+ };
168
+
169
+ const App = () => Scaffold({
170
+ routes,
171
+ body: routes,
172
+ });
173
+
174
+ runApp(App, "root", true, routes);
175
+ ```
176
+
177
+ Use `AppBar`, `DrawerItem`, `BottomNavigation`, or `Button` to call `goTo()`.
178
+
179
+ ## Deploy under a subpath
180
+
181
+ A router URL is app-relative: `"/settings"`. Static hosts such as GitHub Pages
182
+ serve a project site under a prefix — `https://user.github.io/my-app/` — so the
183
+ browser sees `/my-app/settings` while the route map only knows `/settings`. The
184
+ router handles that prefix on its own. It reads the deployment base when it
185
+ initializes and adds it back to every URL it writes, so `goTo()` and the
186
+ back/forward buttons stay inside `/my-app/` instead of jumping to the host root.
187
+
188
+ The base is resolved in this order:
189
+
190
+ 1. The explicit option, which wins over everything:
191
+ `initRouter(routes, path, { base: "/my-app" })`, or
192
+ `Scaffold({ routes, basePath: "/my-app" })`.
193
+ 2. `window.__FLETBOX_BASE__`, when the live URL actually sits under it. Set it
194
+ in the page before the app loads: `<script>window.__FLETBOX_BASE__ = "/my-app/";</script>`.
195
+ The guard is what lets one build run both at `/my-app/` on Pages and at the
196
+ origin root inside a Capacitor WebView.
197
+ 3. Auto-detection: the shortest leading segment of the URL whose remainder
198
+ matches a registered route becomes the base (`/my-app/settings` → base
199
+ `/my-app`, route `/settings`). A root-hosted app resolves to `""` and its
200
+ URLs are untouched.
201
+
202
+ Read the result with `getBasePath()` — it returns `"/my-app"` or `""`. Use it
203
+ for anything the router does not touch, such as fetching content files, and
204
+ compute it at call time rather than at import time, because the base only
205
+ exists once the router has initialized:
206
+
207
+ ```javascript
208
+ import { getBasePath } from "flet-box";
209
+
210
+ const assetUrl = (path) =>
211
+ new URL(path, `${window.location.origin}${getBasePath()}/`).href;
212
+ // https://user.github.io/my-app/docs/guides/router.md · http://localhost:8000/docs/…
213
+ ```
214
+
215
+ ## Deep links on a static host
216
+
217
+ Being able to *write* `/my-app/settings` is only half of it: a static host has
218
+ no such file, so a visitor who opens or reloads that URL gets a 404. GitHub
219
+ Pages answers a missing path by serving the project's `404.html` **under the
220
+ requested URL**, which is exactly the hook an SPA needs — make `404.html` a
221
+ copy of `index.html` and the router will read the real path from the address
222
+ bar and render it.
223
+
224
+ Generate it at build time so it never drifts from the real entry point. Two
225
+ details make the copy work at any depth, and both are easy to get wrong:
226
+
227
+ - **Absolute references.** A page served for `/my-app/docs/guides/router.md`
228
+ resolves `"./loader.js"` against `/my-app/docs/guides/`, not the app root.
229
+ Rewrite every relative reference, including the import map and the
230
+ `serviceWorker.register()` path.
231
+ - **Declare the base.** A deep URL is not always recognizable as a route (an
232
+ unknown path matches nothing), so publish `window.__FLETBOX_BASE__` in the
233
+ fallback page.
234
+
235
+ A build script does the whole job in a few lines:
236
+
237
+ ```javascript
238
+ // scripts/spa-fallback.mjs — run after the bundle is written.
239
+ const base = "/my-app/";
240
+ const html = await readFile("www/index.html", "utf8");
241
+
242
+ const fallback = html
243
+ .replace(/(["'])\.\//g, `$1${base}`)
244
+ .replace(
245
+ /register\((["'])service-worker\.js\1\)/g,
246
+ `register($1${base}service-worker.js$1)`,
247
+ )
248
+ .replace(
249
+ "</head>",
250
+ ` <script>window.__FLETBOX_BASE__ = "${base}";</script>\n </head>`,
251
+ );
252
+
253
+ await writeFile("www/404.html", fallback, "utf8");
254
+ ```
255
+
256
+ Keep the service worker in the same scheme: derive its prefix from
257
+ `new URL("./", self.location).pathname` instead of hard-coding `/`, otherwise
258
+ the precache and the offline navigation fallback miss every file under a
259
+ subpath.
260
+
261
+ ## Unknown routes
262
+
263
+ If the initial URL is not found, the router redirects to the first configured route. You can also define a `*` or `/404` route for navigation attempts that do not exist.
264
+
265
+ ## Common mistakes
266
+
267
+ - Forgetting to pass routes to `runApp` or call `initRouter`.
268
+ - Using `/user/:id` but navigating to `/user` without an id.
269
+ - Reading query values from params, or params from query values.
270
+ - Calling `goTo()` before the router has been initialized.
271
+ - Forgetting to unsubscribe from route listeners in a long-lived integration.
272
+ - Expecting `goBack()` to work like a server redirect; it uses browser history.
273
+ - Assuming the app owns the host root: under a subpath, content fetches need
274
+ `getBasePath()` and the host needs a `404.html` fallback for deep links.
275
+
276
+ ---
277
+
278
+ ## Continue reading
279
+
280
+ - **Previous:** [State with `useState`](state.md)
281
+ - **Next:** [Frontend services guide](frontend-services.md) — Chapter 9 · State, Router & Services
282
+ - **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)
@@ -0,0 +1,283 @@
1
+ # State with `useState`
2
+
3
+ FletBox state lets widgets share values and update the interface when those values change.
4
+
5
+ ## The simple idea
6
+
7
+ A state value is a named value stored by a key:
8
+
9
+ ```javascript
10
+ const [count, setCount] = useState("counter", 0);
11
+ ```
12
+
13
+ - `"counter"` is the shared key.
14
+ - `0` is the initial value.
15
+ - `count` is the current reactive value.
16
+ - `setCount` changes the value.
17
+
18
+ ## Small counter example
19
+
20
+ ```javascript
21
+ import { Button, Column, Text, useState } from "flet-box";
22
+
23
+ const Counter = () => {
24
+ const [count, setCount] = useState("counter", 0);
25
+
26
+ return Column({
27
+ gap: 12,
28
+ children: [
29
+ Text({ text: `Count: ${count}` }), // derived text → app render (see below)
30
+ Button({
31
+ text: "Add one",
32
+ onPress: () => setCount((previous) => previous + 1),
33
+ }),
34
+ ],
35
+ });
36
+ };
37
+ ```
38
+
39
+ When `setCount` runs, FletBox writes the new value to the store and then decides how much
40
+ of the UI has to change. That decision is the subject of the next section, and it is worth
41
+ getting right: one style of using the value updates a single element, the other re-runs
42
+ your whole component.
43
+
44
+ ## Surgical updates vs. app renders
45
+
46
+ A state value is a **live** value. If you hand it to a prop, the framework remembers that
47
+ link and, on change, patches only that widget:
48
+
49
+ ```javascript
50
+ const [count, setCount] = useState("counter", 0);
51
+
52
+ Text({ text: count }); // surgical: this element is patched
53
+ Container({ bgColor: theme }); // surgical
54
+ ```
55
+
56
+ If you *compute with it inside the component*, the value is consumed before it ever
57
+ reaches a prop, so there is nothing left to patch — the only way to reflect the change is
58
+ to re-run the app:
59
+
60
+ ```javascript
61
+ Text({ text: `Count: ${count}` }); // app render
62
+ Text({ text: count > 5 ? "MUCHO" : "poco" }); // app render
63
+ items.map((item) => Row({ child: Text({ text: item }) })); // app render
64
+ ```
65
+
66
+ Measured on the same screen, three changes to one key:
67
+
68
+ | How the key is used | App re-renders | Widgets patched |
69
+ | --- | --- | --- |
70
+ | `Text({ text: count })` only | **0** | the one `Text` |
71
+ | mixed with a template literal | 1 | patched, then replaced |
72
+ | template literal only | 1 | — |
73
+
74
+ Two details that make this easier to live with:
75
+
76
+ - **Reads outside the render are free.** Reading the value inside `onPress`, or inside a
77
+ `subscribe` callback, does **not** force an app render — only reads that happen while the
78
+ component is building its tree do.
79
+ - **Renders coalesce.** Several `setState` calls in the same tick produce **one** app
80
+ render, not one per call.
81
+
82
+ If you need derived text but want to stay surgical, derive it in the subscriber:
83
+
84
+ ```javascript
85
+ const App = () => {
86
+ const [count, setCount, onCount] = useState("counter", 0);
87
+ const label = Text({ text: "Count: 0" });
88
+
89
+ onCount((n) => label.update({ text: `Count: ${n}` }));
90
+
91
+ return Column({ children: [label] });
92
+ };
93
+ ```
94
+
95
+ The app render has no Virtual DOM diff behind it: it re-runs the component and **creates
96
+ new elements**, discarding the old ones. That is why passing the value straight into a prop
97
+ is worth the small change in style. See
98
+ [Reactivity in FletBox](../tools/reactivity.md) for the full decision rule.
99
+
100
+ ## Set a direct value
101
+
102
+ ```javascript
103
+ const [theme, setTheme] = useState("theme", "light");
104
+
105
+ setTheme("dark");
106
+ ```
107
+
108
+ ## Use an updater function
109
+
110
+ Use an updater when the new value depends on the previous value:
111
+
112
+ ```javascript
113
+ setCount((previous) => previous + 1);
114
+ ```
115
+
116
+ This is useful for counters, arrays, and objects.
117
+
118
+ ## Store an object
119
+
120
+ ```javascript
121
+ const [user, setUser] = useState("user", {
122
+ name: "Ada",
123
+ online: false,
124
+ });
125
+
126
+ setUser((previous) => ({
127
+ ...previous,
128
+ online: true,
129
+ }));
130
+ ```
131
+
132
+ Create a new object or array when updating nested data. This makes the change clear and prevents accidental mutation.
133
+
134
+ ## Store an array
135
+
136
+ ```javascript
137
+ const [items, setItems] = useState("items", []);
138
+
139
+ setItems((previous) => [
140
+ ...previous,
141
+ { id: Date.now(), title: "New item" },
142
+ ]);
143
+ ```
144
+
145
+ ## Shared state between widgets
146
+
147
+ Two widgets using the same key can read and update the same stored value:
148
+
149
+ ```javascript
150
+ const Status = () => {
151
+ const [enabled] = useState("feature-enabled", false);
152
+ return Text({ text: enabled ? "Enabled" : "Disabled" });
153
+ };
154
+
155
+ const ToggleFeature = () => {
156
+ const [enabled, setEnabled] = useState("feature-enabled", false);
157
+ return Button({
158
+ text: enabled ? "Turn off" : "Turn on",
159
+ onPress: () => setEnabled((value) => !value),
160
+ });
161
+ };
162
+ ```
163
+
164
+ Use descriptive, unique keys. A key is global within the state store, so reusing a key accidentally can connect unrelated widgets.
165
+
166
+ ## Watch a state key
167
+
168
+ `useWatchState` subscribes to changes without creating a state value:
169
+
170
+ ```javascript
171
+ import { useWatchState } from "flet-box";
172
+
173
+ const stopWatching = useWatchState("counter", (newValue, oldValue) => {
174
+ console.log({ newValue, oldValue });
175
+ });
176
+
177
+ // Later:
178
+ stopWatching();
179
+ ```
180
+
181
+ ## Subscribe from `useState`
182
+
183
+ The current implementation returns a third value for subscriptions:
184
+
185
+ ```javascript
186
+ const [value, setValue, subscribe] = useState("status", "idle");
187
+
188
+ const unsubscribe = subscribe((newValue, oldValue) => {
189
+ console.log(newValue, oldValue);
190
+ });
191
+
192
+ // Later:
193
+ unsubscribe();
194
+ ```
195
+
196
+ ## State and widgets
197
+
198
+ Normally you do not register anything: pass the state value into a prop and the widget
199
+ factory binds it to the key for you, releasing the binding when the widget is removed.
200
+
201
+ ```javascript
202
+ const [theme, setTheme] = useState("theme", "light");
203
+
204
+ const panel = Container({ bgColor: theme }); // bound automatically
205
+ setTheme("dark"); // patches `panel` only
206
+ ```
207
+
208
+ There is an explicit form for the case where the widget already exists and you want one of
209
+ its props driven by a key:
210
+
211
+ ```javascript
212
+ const [label, setLabel] = useState(
213
+ "button-label",
214
+ "Save",
215
+ button,
216
+ "text",
217
+ );
218
+ ```
219
+
220
+ Both push the new value through `widget.update({ prop: value })`, so the update touches one
221
+ element instead of rebuilding the screen.
222
+
223
+ ## Reading the value
224
+
225
+ The value returned by `useState` behaves like the current state in most expressions —
226
+ `${count}` and `count + 1` work — but under the hood it is a live function, and that shows
227
+ up in two places:
228
+
229
+ ```javascript
230
+ typeof count; // "function", not "number"
231
+ count.valueOf(); // the current value, explicitly
232
+ ```
233
+
234
+ For objects and arrays, **property access does not go through the state**:
235
+
236
+ ```javascript
237
+ const [user, setUser] = useState("user", { name: "Ada", online: false });
238
+
239
+ user.name; // ✗ "live" — the function's own name, not the state's
240
+ user.online; // ✗ undefined
241
+ user().name; // ✓ "Ada" — call it, or pass it straight into a prop
242
+ ```
243
+
244
+ So for object state, prefer letting a widget resolve it (pass the value itself as the prop,
245
+ not a field of it) or read it by calling the value inside a handler, where reads are free.
246
+
247
+ ## Persistence during the session
248
+
249
+ FletBox stores state through its RAM store. The value is available by key while the application is running:
250
+
251
+ ```javascript
252
+ import { saveRam, getRam } from "flet-box";
253
+
254
+ saveRam("language", "en");
255
+ const language = getRam("language");
256
+ ```
257
+
258
+ Use the storage services when you need browser persistence or a different lifetime.
259
+
260
+ ## Important rules
261
+
262
+ - Call `useState` with a stable key.
263
+ - Do not create random keys on every render.
264
+ - Use updater functions for values based on previous state.
265
+ - Avoid mutating arrays and objects in place.
266
+ - Pass the state value straight into a prop when you can: that is what keeps the update
267
+ surgical instead of re-running the app.
268
+ - Do not read object state with `user.name`; call the value (`user()`) or let a widget
269
+ resolve it.
270
+ - Unsubscribe from manual watchers when the feature is destroyed.
271
+ - Use `runApp()` when you want global re-render behavior for a complete app.
272
+
273
+ ## TypeScript note
274
+
275
+ The runtime currently returns `[value, setValue, subscribe]`. Keep the declaration file and this behavior aligned when changing the public API.
276
+
277
+ ---
278
+
279
+ ## Continue reading
280
+
281
+ - **Previous:** [CollapsibleSideBar](../widget/CollapsibleSideBar.md) — end of the widget book.
282
+ - **Next:** [Routing with FletBox](router.md) — Chapter 9 · State, Router & Services
283
+ - **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)
@@ -0,0 +1,150 @@
1
+ # FletBox syntax philosophy
2
+
3
+ FletBox is designed around the developer, not the other way around.
4
+
5
+ You should be able to describe an interface in a way that feels natural to you. Developers coming from Flet, Flutter, React, or vanilla JavaScript may use slightly different names for the same idea. FletBox is intentionally friendly to those styles.
6
+
7
+ ## One idea, several comfortable names
8
+
9
+ Some props have aliases because different ecosystems use different words.
10
+
11
+ ```javascript
12
+ Container({
13
+ bgColor: "#eff6ff",
14
+ padding: 16,
15
+ });
16
+ ```
17
+
18
+ A developer coming from traditional web development may prefer:
19
+
20
+ ```javascript
21
+ Container({
22
+ backgroundColor: "#eff6ff",
23
+ padding: "16px",
24
+ });
25
+ ```
26
+
27
+ The goal is not to force one vocabulary. The goal is to make the intention obvious.
28
+
29
+ ## Events that read naturally
30
+
31
+ Depending on your background, either event name may feel more familiar:
32
+
33
+ ```javascript
34
+ Button({
35
+ text: "Save",
36
+ onPress: save,
37
+ });
38
+ ```
39
+
40
+ ```javascript
41
+ Button({
42
+ text: "Save",
43
+ onClick: save,
44
+ });
45
+ ```
46
+
47
+ Both express the same human idea: run this function when the user activates the button.
48
+
49
+ ## Children and composition
50
+
51
+ Flet and Flutter developers may think in terms of a single `child`:
52
+
53
+ ```javascript
54
+ Container({
55
+ child: Text({ text: "Hello" }),
56
+ });
57
+ ```
58
+
59
+ React developers may prefer `children`:
60
+
61
+ ```javascript
62
+ Container({
63
+ children: [
64
+ Text({ text: "Hello" }),
65
+ Text({ text: "Welcome" }),
66
+ ],
67
+ });
68
+ ```
69
+
70
+ Use `child` for one child and `children` for several children when the widget supports both.
71
+
72
+ ## Direct values or helper functions
73
+
74
+ FletBox keeps simple values simple:
75
+
76
+ ```javascript
77
+ Container({
78
+ padding: 16,
79
+ margin: "8px 0",
80
+ });
81
+ ```
82
+
83
+ You can also use helpers when you prefer an expressive style:
84
+
85
+ ```javascript
86
+ import { margin, padding } from "flet-box";
87
+
88
+ Container({
89
+ padding: padding({ horizontal: 20, vertical: 12 }),
90
+ margin: margin({ top: 8, bottom: 16 }),
91
+ });
92
+ ```
93
+
94
+ The first style is quick. The second style can make complex spacing more descriptive. Choose the one that makes your code easiest to read.
95
+
96
+ ## Styling vocabulary
97
+
98
+ FletBox accepts familiar CSS-like values:
99
+
100
+ ```javascript
101
+ Container({
102
+ width: "100%",
103
+ minHeight: "100vh",
104
+ borderRadius: 12,
105
+ bgColor: "#ffffff",
106
+ });
107
+ ```
108
+
109
+ Numbers normally represent pixels. Strings can contain CSS units such as `%`, `vh`, `rem`, or `px`.
110
+
111
+ ## Preferred and compatible syntax
112
+
113
+ For new projects, choose one style and use it consistently within a file. The examples in this documentation generally use the short, readable widget style:
114
+
115
+ ```javascript
116
+ Text({ text: "Hello", size: 18, weight: "bold" });
117
+ ```
118
+
119
+ Aliases exist to make migration and adoption easier. They are not a requirement to mix every style in the same component.
120
+
121
+ Think of the API in three layers:
122
+
123
+ - **Preferred syntax**: the clearest style recommended by the current documentation.
124
+ - **Friendly aliases**: equivalent names that help developers coming from another ecosystem.
125
+ - **Compatibility syntax**: older names preserved so existing projects do not break.
126
+
127
+ ## The rule for documentation
128
+
129
+ Every widget page should answer three questions:
130
+
131
+ 1. What does this widget represent?
132
+ 2. What is the smallest example that works?
133
+ 3. Which alternative names or styles can a developer use?
134
+
135
+ That is why each widget page contains a basic example, an everyday example, a full example, common prop explanations, and beginner tips.
136
+
137
+ ## The rule for framework design
138
+
139
+ FletBox should adapt to the developer wherever that flexibility remains clear, testable, and maintainable.
140
+
141
+ Friendly syntax must still have:
142
+
143
+ - Clear documentation.
144
+ - Consistent behavior.
145
+ - Type declarations.
146
+ - Working examples.
147
+ - Tests for important aliases.
148
+ - A migration path when an alias changes.
149
+
150
+ This keeps the experience soft for beginners without making the framework mysterious for experienced developers.