flet-box 1.0.4 → 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 (147) 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 +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
@@ -3,11 +3,16 @@
3
3
  *
4
4
  * Provides two tiers of animation helpers:
5
5
  * - **Fire-and-forget** (`animate`, `fadeOut`, `fadeIn`, `pulse`): start an
6
- * animation and move on — no way to await completion.
6
+ * animation and move on — each returns a cancel function instead of awaiting.
7
7
  * - **Async/awaitable** (`animateAsync`, `fadeOutAsync`, `fadeInAsync`,
8
8
  * `pulseAsync`): return a Promise that resolves when the animation finishes,
9
9
  * allowing sequential composition with `await`.
10
10
  *
11
+ * Every loop is registered per widget and property, so starting a second
12
+ * animation on the same property replaces the first (two loops writing one style
13
+ * fight each other), and {@link stopAnimation} ends whatever is still running on
14
+ * a widget — the usual cleanup an unmount needs when the caller kept no handle.
15
+ *
11
16
  * All functions drive the browser's `requestAnimationFrame` loop directly and
12
17
  * do not rely on CSS transitions or the Web Animations API.
13
18
  *
@@ -162,6 +167,151 @@ const writeAnimatedValue = (widget, property, value) => {
162
167
  widget[property] = value;
163
168
  };
164
169
 
170
+ // ========== ACTIVE LOOP REGISTRY ==========
171
+
172
+ /**
173
+ * Live loops by widget and property.
174
+ *
175
+ * Two loops writing the same style property fight each other: the older one keeps
176
+ * overwriting the newer value on every frame and neither can be stopped. The
177
+ * registry lets a fresh animation replace whatever already animates that
178
+ * property, and lets {@link stopAnimation} end loops whose caller never kept the
179
+ * handle — the usual case for a widget that unmounts mid-fade.
180
+ *
181
+ * @type {WeakMap<object, Map<string, { raf: number, running: boolean, cancel: () => boolean }>>}
182
+ */
183
+ const activeLoops = new WeakMap();
184
+
185
+ const isTrackable = (widget) =>
186
+ widget !== null &&
187
+ (typeof widget === "object" || typeof widget === "function");
188
+
189
+ const registerLoop = (widget, property, state) => {
190
+ if (!isTrackable(widget)) return;
191
+ let byProperty = activeLoops.get(widget);
192
+ if (!byProperty) {
193
+ byProperty = new Map();
194
+ activeLoops.set(widget, byProperty);
195
+ }
196
+ byProperty.set(property, state);
197
+ };
198
+
199
+ const unregisterLoop = (widget, property, state) => {
200
+ if (!isTrackable(widget)) return;
201
+ const byProperty = activeLoops.get(widget);
202
+ if (byProperty && byProperty.get(property) === state) byProperty.delete(property);
203
+ };
204
+
205
+ /**
206
+ * Stops the animation currently running on a widget.
207
+ *
208
+ * @param {HTMLElement|object} widget - The animated widget or DOM element.
209
+ * @param {string} [property] - Stop only this property; every animated property when omitted.
210
+ * @returns {boolean} True when at least one loop was stopped.
211
+ */
212
+ export const stopAnimation = (widget, property) => {
213
+ if (!isTrackable(widget)) return false;
214
+ const byProperty = activeLoops.get(widget);
215
+ if (!byProperty) return false;
216
+
217
+ if (property !== undefined) {
218
+ const state = byProperty.get(property);
219
+ if (!state) return false;
220
+ return state.cancel();
221
+ }
222
+
223
+ const loops = Array.from(byProperty.values());
224
+ loops.forEach((state) => state.cancel());
225
+ return loops.length > 0;
226
+ };
227
+
228
+ /**
229
+ * Runs one property animation and returns its cancel function.
230
+ *
231
+ * Shared engine behind `animate`, `animateAsync` and `pulse`, so all three get
232
+ * the same guarantees: the loop is bounded by a sanitised duration, it replaces
233
+ * any animation already driving that property, and it stops the moment its
234
+ * handle is called.
235
+ *
236
+ * @param {HTMLElement|object} widget
237
+ * @param {string} property
238
+ * @param {number|string} from - Starting value; reads the widget when `undefined`.
239
+ * @param {number|string} to
240
+ * @param {number} duration
241
+ * @param {string} easing
242
+ * @param {(progress: number) => number|string} [valueAt] - Custom value curve,
243
+ * receiving eased progress in `[0, 1]`. Used by `pulse` to keep the whole
244
+ * up-and-down gesture inside a single cancelable loop.
245
+ * @param {(progress: number) => number|string} [valueAt] - Custom value curve,
246
+ * receiving eased progress in `[0, 1]`. Used by `pulse` to keep the whole
247
+ * up-and-down gesture inside a single cancelable loop.
248
+ * @param {(widget: HTMLElement|object) => void} [onDone] - Called when the loop
249
+ * ends — also when it is cancelled or replaced, so awaiting code never hangs.
250
+ * @returns {() => boolean} Cancel function; true when it stopped a live loop.
251
+ */
252
+ const runAnimation = (
253
+ widget,
254
+ property,
255
+ from,
256
+ to,
257
+ duration,
258
+ easing,
259
+ valueAt,
260
+ onDone,
261
+ ) => {
262
+ // A negative duration makes elapsed/duration increasingly negative, so progress
263
+ // never reaches 1 and the loop re-arms forever (and `animateAsync` never
264
+ // resolves). Clamping the progress is not enough — it just sits at 0 and
265
+ // `0 < 1` stays true — so sanitise the duration itself. `0` collapses to one frame.
266
+ const safeDuration = Math.max(0, Number(duration) || 0);
267
+ const startValue = from !== undefined ? from : readAnimatedValue(widget, property);
268
+
269
+ // Replace the loop already animating this property instead of racing it.
270
+ stopAnimation(widget, property);
271
+
272
+ const startTime = performance.now();
273
+ const state = { raf: 0, running: true, cancel: null };
274
+
275
+ const finish = () => {
276
+ if (!state.running) return;
277
+ state.running = false;
278
+ unregisterLoop(widget, property, state);
279
+ if (onDone) onDone(widget);
280
+ };
281
+
282
+ state.cancel = () => {
283
+ if (!state.running) return false;
284
+ cancelAnimationFrame(state.raf);
285
+ finish();
286
+ return true;
287
+ };
288
+
289
+ registerLoop(widget, property, state);
290
+
291
+ const step = (currentTime) => {
292
+ if (!state.running) return;
293
+ const progress = Math.max(
294
+ 0,
295
+ Math.min(1, (currentTime - startTime) / safeDuration),
296
+ );
297
+ const easedProgress = getEasing(easing, progress);
298
+ const currentValue = valueAt
299
+ ? valueAt(easedProgress)
300
+ : interpolate(startValue, to, easedProgress);
301
+
302
+ writeAnimatedValue(widget, property, currentValue);
303
+
304
+ if (progress < 1) {
305
+ state.raf = requestAnimationFrame(step);
306
+ return;
307
+ }
308
+ finish();
309
+ };
310
+
311
+ state.raf = requestAnimationFrame(step);
312
+ return state.cancel;
313
+ };
314
+
165
315
  // ========== FIRE AND FORGET (No Promise) ==========
166
316
 
167
317
  /**
@@ -176,7 +326,9 @@ const writeAnimatedValue = (widget, property, value) => {
176
326
  * @param {number|string} to - Ending value.
177
327
  * @param {number} [duration=300] - Animation duration in milliseconds.
178
328
  * @param {string} [easing="linear"] - Easing curve name (see {@link getEasing}).
179
- * @returns {void}
329
+ * @returns {() => boolean} Cancel function — true if it stopped a loop still running.
330
+ * The animation also stops on its own when a new animation takes the same property,
331
+ * or via {@link stopAnimation}.
180
332
  */
181
333
  export const animate = (
182
334
  widget,
@@ -185,34 +337,7 @@ export const animate = (
185
337
  to,
186
338
  duration = 300,
187
339
  easing = "linear",
188
- ) => {
189
- // A negative duration makes elapsed/duration increasingly negative, so the
190
- // loop re-armed forever and the property was driven backwards. Clamping the
191
- // progress is not enough (it just sits at 0 and `0 < 1` stays true), so
192
- // sanitise the duration itself. `0` collapses to a single frame.
193
- duration = Math.max(0, Number(duration) || 0);
194
- const startTime = performance.now();
195
- const startValue = from !== undefined ? from : readAnimatedValue(widget, property);
196
- const endValue = to;
197
-
198
- const step = (currentTime) => {
199
- const elapsed = currentTime - startTime;
200
- // Clamp both ends. A negative `duration` makes elapsed/duration increasingly
201
- // negative, so `progress` never reached 1 and the loop re-armed forever (and
202
- // `animateAsync` never resolved). `duration: 0` yields Infinity, clamped to 1.
203
- const progress = Math.max(0, Math.min(1, elapsed / duration));
204
- const easedProgress = getEasing(easing, progress);
205
- const currentValue = interpolate(startValue, endValue, easedProgress);
206
-
207
- writeAnimatedValue(widget, property, currentValue);
208
-
209
- if (progress < 1) {
210
- requestAnimationFrame(step);
211
- }
212
- };
213
-
214
- requestAnimationFrame(step);
215
- };
340
+ ) => runAnimation(widget, property, from, to, duration, easing, null);
216
341
 
217
342
  // ========== ASYNC ANIMATION (Returns Promise) ==========
218
343
 
@@ -228,7 +353,8 @@ export const animate = (
228
353
  * @param {number|string} to - Ending value.
229
354
  * @param {number} [duration=300] - Animation duration in milliseconds.
230
355
  * @param {string} [easing="linear"] - Easing curve name (see {@link getEasing}).
231
- * @returns {Promise<HTMLElement|object>} Resolves with `widget` when the animation finishes.
356
+ * @returns {Promise<HTMLElement|object>} Resolves with `widget` when the animation
357
+ * finishes — or as soon as it is cancelled or replaced, so `await` never hangs.
232
358
  */
233
359
  export const animateAsync = (
234
360
  widget,
@@ -237,35 +363,12 @@ export const animateAsync = (
237
363
  to,
238
364
  duration = 300,
239
365
  easing = "linear",
240
- ) => {
241
- // See `animate`: a negative duration would keep the loop alive forever and this
242
- // promise would never resolve.
243
- duration = Math.max(0, Number(duration) || 0);
244
- return new Promise((resolve) => {
245
- const startTime = performance.now();
246
- const startValue = from !== undefined ? from : readAnimatedValue(widget, property);
247
- const endValue = to;
248
-
249
- const step = (currentTime) => {
250
- const elapsed = currentTime - startTime;
251
- // Same clamp as `animate`: a negative `duration` would otherwise never
252
- // reach progress 1 and this promise would never resolve.
253
- const progress = Math.max(0, Math.min(1, elapsed / duration));
254
- const easedProgress = getEasing(easing, progress);
255
- const currentValue = interpolate(startValue, endValue, easedProgress);
256
-
257
- writeAnimatedValue(widget, property, currentValue);
258
-
259
- if (progress < 1) {
260
- requestAnimationFrame(step);
261
- } else {
262
- resolve(widget);
263
- }
264
- };
265
-
266
- requestAnimationFrame(step);
366
+ ) =>
367
+ new Promise((resolve) => {
368
+ // `resolve` runs on completion AND when the loop is cancelled or replaced, so
369
+ // an `await` chain cannot hang on an animation that never got to finish.
370
+ runAnimation(widget, property, from, to, duration, easing, null, resolve);
267
371
  });
268
- };
269
372
 
270
373
  // ========== HELPER FUNCTIONS (Fire and Forget) ==========
271
374
 
@@ -275,10 +378,10 @@ export const animateAsync = (
275
378
  *
276
379
  * @param {HTMLElement|object} widget - The target widget.
277
380
  * @param {number} [duration=300] - Fade duration in milliseconds.
278
- * @returns {void}
381
+ * @returns {() => boolean} Cancel function (see {@link animate}).
279
382
  */
280
383
  export const fadeOut = (widget, duration = 300) => {
281
- animate(widget, "opacity", 1, 0, duration);
384
+ return animate(widget, "opacity", 1, 0, duration);
282
385
  };
283
386
 
284
387
  /**
@@ -287,10 +390,10 @@ export const fadeOut = (widget, duration = 300) => {
287
390
  *
288
391
  * @param {HTMLElement|object} widget - The target widget.
289
392
  * @param {number} [duration=300] - Fade duration in milliseconds.
290
- * @returns {void}
393
+ * @returns {() => boolean} Cancel function (see {@link animate}).
291
394
  */
292
395
  export const fadeIn = (widget, duration = 300) => {
293
- animate(widget, "opacity", 0, 1, duration);
396
+ return animate(widget, "opacity", 0, 1, duration);
294
397
  };
295
398
 
296
399
  /**
@@ -301,14 +404,21 @@ export const fadeIn = (widget, duration = 300) => {
301
404
  * @param {HTMLElement|object} widget - The target widget.
302
405
  * @param {number} [duration=300] - Total pulse duration in milliseconds
303
406
  * (split evenly between scale-up and scale-down).
304
- * @returns {void}
407
+ * @returns {() => boolean} Cancel function (see {@link animate}).
305
408
  */
306
- export const pulse = (widget, duration = 300) => {
307
- animate(widget, "scale", 1, 1.1, duration / 2);
308
- setTimeout(() => {
309
- animate(widget, "scale", 1.1, 1, duration / 2);
310
- }, duration / 2);
311
- };
409
+ export const pulse = (widget, duration = 300) =>
410
+ runAnimation(
411
+ widget,
412
+ "scale",
413
+ 1,
414
+ 1,
415
+ duration,
416
+ "linear",
417
+ // One loop with a triangular curve instead of an animation plus a
418
+ // `setTimeout`: the timer used to start phase two on a widget that had
419
+ // already been removed from the DOM, and nothing could cancel it.
420
+ (progress) => interpolate(1, 1.1, progress < 0.5 ? progress * 2 : 2 - progress * 2),
421
+ );
312
422
 
313
423
  // ========== ASYNC HELPER FUNCTIONS (Returns Promise) ==========
314
424
 
@@ -16,6 +16,7 @@ export { MatrixRain } from "./MatrixRain.js";
16
16
  export {
17
17
  animate,
18
18
  animateAsync,
19
+ stopAnimation,
19
20
  fadeOut,
20
21
  fadeIn,
21
22
  pulse,
@@ -32,7 +32,7 @@ export {
32
32
  useState, useWatchState,
33
33
  tokenize, generateHighlightedHtml, highlightColors,
34
34
  parseMarkdown, parseInlineMarkdown, escapeHtml, wrapListItems,
35
- animate, fadeOut, fadeIn, pulse,
35
+ animate, stopAnimation, fadeOut, fadeIn, pulse,
36
36
  applyStripes, removeStripes, applyShimmer, applyGlow,
37
37
  applyIndeterminate, applyPulse, injectKeyframes,
38
38
  Gradient, Shadow, rgba, stackPosition,
package/src/index.d.ts CHANGED
@@ -36,10 +36,15 @@ declare module 'flet-box' {
36
36
  export interface CommonProps {
37
37
  width?: Size;
38
38
  height?: Size;
39
- size?: number;
39
+ // `Size`, not `number`: for a Text this is a font size and for a Button it is
40
+ // the `small | medium | large` preset — the widget decides which it reads.
41
+ size?: Size;
40
42
  padding?: Padding;
41
43
  margin?: Margin;
42
44
  bgColor?: Color;
45
+ // Alias kept because the engine routes it and every generated template uses
46
+ // it: `bgColor` is the Flet-style short name for the same style.
47
+ backgroundColor?: Color;
43
48
  color?: Color;
44
49
  borderRadius?: number | string;
45
50
  elevation?: number;
@@ -203,10 +208,10 @@ declare module 'flet-box' {
203
208
  gap?: number | string;
204
209
  justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
205
210
  alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
206
- wrap?: boolean;
211
+ wrap?: boolean | 'wrap' | 'nowrap' | 'wrap-reverse';
207
212
  child?: Widget;
208
213
  children?: Widget[];
209
- expand?: boolean;
214
+ expand?: boolean | number;
210
215
  flex?: number;
211
216
  minHeight?: Size;
212
217
  maxHeight?: Size;
@@ -220,7 +225,7 @@ declare module 'flet-box' {
220
225
  gap?: number | string;
221
226
  justifyContent?: ContainerProps['justifyContent'];
222
227
  alignItems?: ContainerProps['alignItems'];
223
- wrap?: boolean;
228
+ wrap?: boolean | 'wrap' | 'nowrap' | 'wrap-reverse';
224
229
  children?: Widget[];
225
230
  onPress?: (widget: Widget) => void;
226
231
  }
@@ -259,7 +264,7 @@ declare module 'flet-box' {
259
264
  ListEmptyComponent?: (() => Widget) | Widget;
260
265
  showsScrollIndicator?: boolean;
261
266
  bufferSize?: number;
262
- expand?: boolean;
267
+ expand?: boolean | number;
263
268
  }
264
269
  export function ListView(props: ListViewProps): Widget & {
265
270
  data: any[];
@@ -375,6 +380,12 @@ declare module 'flet-box' {
375
380
  title?: string | Widget;
376
381
  subtitle?: string | Widget;
377
382
  description?: string | Widget;
383
+ /** Text props merged into the title, e.g. `{ size: 16, weight: "700" }`. */
384
+ titleProps?: Partial<TextProps>;
385
+ /** Text props merged into the subtitle. */
386
+ subtitleProps?: Partial<TextProps>;
387
+ /** Text props merged into the description line. */
388
+ descriptionProps?: Partial<TextProps>;
378
389
  rightItem?: Widget;
379
390
  onPress?: (widget: Widget) => void;
380
391
  selected?: boolean;
@@ -522,7 +533,7 @@ declare module 'flet-box' {
522
533
  updateValue(newValue: string | number): void;
523
534
  };
524
535
 
525
- export interface SliderProps extends CommonProps {
536
+ export interface SliderProps extends Omit<CommonProps, 'onPress'> {
526
537
  value?: number;
527
538
  min?: number;
528
539
  max?: number;
@@ -544,6 +555,9 @@ declare module 'flet-box' {
544
555
  animatedStripes?: boolean;
545
556
  stripeColor?: string;
546
557
  glow?: boolean;
558
+ /** Canonical press/change callback, like every other FletBox widget. */
559
+ onPress?: (value: number) => void;
560
+ /** @deprecated Alias of `onPress`. */
547
561
  onChanged?: (value: number) => void;
548
562
  onChangeEnd?: (value: number) => void;
549
563
  }
@@ -588,7 +602,7 @@ declare module 'flet-box' {
588
602
  update(props: Partial<AccordionProps>): void;
589
603
  };
590
604
 
591
- export interface DividerProps extends CommonProps {
605
+ export interface DividerProps extends Omit<CommonProps, 'margin'> {
592
606
  color?: Color;
593
607
  thickness?: number;
594
608
  margin?: number | { top?: number; right?: number; bottom?: number; left?: number };
@@ -857,6 +871,7 @@ declare module 'flet-box' {
857
871
  navSideBarWidth?: number;
858
872
  navSideBarPosition?: 'left' | 'right';
859
873
  routes?: Record<string, any>;
874
+ basePath?: string;
860
875
  backgroundColor?: Color;
861
876
  closeDrawerOnNavigate?: boolean;
862
877
  }
@@ -893,7 +908,7 @@ declare module 'flet-box' {
893
908
  closeDrawer?(): void;
894
909
  };
895
910
 
896
- export interface AppBarProps extends CommonProps {
911
+ export interface AppBarProps extends Omit<CommonProps, 'shadow'> {
897
912
  title?: string | Widget;
898
913
  leading?: Widget;
899
914
  actions?: Widget[];
@@ -915,7 +930,7 @@ declare module 'flet-box' {
915
930
  margin?: Margin;
916
931
  padding?: Padding;
917
932
  borderRadius?: number;
918
- shadow?: boolean;
933
+ shadow?: boolean | string;
919
934
  }
920
935
  export function AppBar(props: AppBarProps): Widget & {
921
936
  setTitle(title: string): void;
@@ -971,7 +986,7 @@ declare module 'flet-box' {
971
986
  export function toggleDrawer(): void;
972
987
  export function destroyDrawer(): void;
973
988
 
974
- export interface BottomNavigationProps extends CommonProps {
989
+ export interface BottomNavigationProps extends Omit<CommonProps, 'shadow'> {
975
990
  items: Array<{ icon: string; label?: string; route?: string; onPress?: () => void }>;
976
991
  currentIndex?: number;
977
992
  onTabChange?: (index: number) => void;
@@ -984,7 +999,7 @@ declare module 'flet-box' {
984
999
  margin?: Margin;
985
1000
  borderRadius?: number;
986
1001
  padding?: Padding;
987
- shadow?: boolean;
1002
+ shadow?: boolean | string;
988
1003
  elevation?: number;
989
1004
  useRouter?: boolean;
990
1005
  }
@@ -1141,7 +1156,8 @@ declare module 'flet-box' {
1141
1156
  to: any,
1142
1157
  duration?: number,
1143
1158
  easing?: string
1144
- ): void;
1159
+ ): () => boolean;
1160
+ export function stopAnimation(widget: Widget, property?: string): boolean;
1145
1161
  export function animateAsync(
1146
1162
  widget: Widget,
1147
1163
  property: string,
@@ -1150,9 +1166,9 @@ declare module 'flet-box' {
1150
1166
  duration?: number,
1151
1167
  easing?: string
1152
1168
  ): Promise<Widget>;
1153
- export function fadeOut(widget: Widget, duration?: number): void;
1154
- export function fadeIn(widget: Widget, duration?: number): void;
1155
- export function pulse(widget: Widget, duration?: number): void;
1169
+ export function fadeOut(widget: Widget, duration?: number): () => boolean;
1170
+ export function fadeIn(widget: Widget, duration?: number): () => boolean;
1171
+ export function pulse(widget: Widget, duration?: number): () => boolean;
1156
1172
  export function fadeOutAsync(widget: Widget, duration?: number): Promise<Widget>;
1157
1173
  export function fadeInAsync(widget: Widget, duration?: number): Promise<Widget>;
1158
1174
  export function pulseAsync(widget: Widget, duration?: number): Promise<Widget>;
@@ -1192,7 +1208,7 @@ declare module 'flet-box' {
1192
1208
  updateColumns(columns: DataTableColumn[]): void;
1193
1209
  };
1194
1210
 
1195
- export interface ChartProps extends CommonProps {
1211
+ export interface ChartProps extends Omit<CommonProps, 'padding'> {
1196
1212
  type?: 'bar' | 'line' | 'area' | 'candle';
1197
1213
  data: number[] | Array<{ open: number; high: number; low: number; close: number }>;
1198
1214
  labels?: string[];
@@ -1494,7 +1510,7 @@ declare module 'flet-box' {
1494
1510
  }
1495
1511
  export function InstallButton(props: InstallButtonProps): Widget;
1496
1512
 
1497
- export interface VideoProps extends CommonProps {
1513
+ export interface VideoProps extends Omit<CommonProps, 'ref'> {
1498
1514
  src: string;
1499
1515
  width?: number | string;
1500
1516
  height?: number | string;
@@ -1516,7 +1532,7 @@ declare module 'flet-box' {
1516
1532
  unmuteVideo(): void;
1517
1533
  };
1518
1534
 
1519
- export interface AudioProps extends CommonProps {
1535
+ export interface AudioProps extends Omit<CommonProps, 'ref'> {
1520
1536
  src: string;
1521
1537
  autoplay?: boolean;
1522
1538
  controls?: boolean;
@@ -1547,7 +1563,7 @@ declare module 'flet-box' {
1547
1563
  isPlaying(): boolean;
1548
1564
  };
1549
1565
 
1550
- export interface CircularBarProps extends CommonProps {
1566
+ export interface CircularBarProps extends Omit<CommonProps, 'onClick' | 'ref'> {
1551
1567
  value?: number;
1552
1568
  max?: number;
1553
1569
  size?: number;
@@ -1597,7 +1613,7 @@ declare module 'flet-box' {
1597
1613
  color?: Color;
1598
1614
  }
1599
1615
 
1600
- export interface CircularChartProps extends CommonProps {
1616
+ export interface CircularChartProps extends Omit<CommonProps, 'onClick' | 'ref'> {
1601
1617
  data?: CircularChartSlice[];
1602
1618
  size?: number;
1603
1619
  strokeWidth?: number;
@@ -1920,7 +1936,12 @@ declare module 'flet-box' {
1920
1936
  export function padding(value: number | string | { all?: number; horizontal?: number; vertical?: number; top?: number; right?: number; bottom?: number; left?: number }): string;
1921
1937
  export function gradient(type: 'linear' | 'circle' | 'conic', colors: string[], angle?: number): string;
1922
1938
  export function shadow(x: number, y: number, blur: number, spread: number, color: string): string;
1923
- export const color: (value: string) => {
1939
+ /**
1940
+ * Fluent color instance returned by `color()`. The chained methods mutate this
1941
+ * very instance and return it, which is why their type is `this` and not a
1942
+ * fixed interface name.
1943
+ */
1944
+ export interface ColorInstance {
1924
1945
  hex(): string;
1925
1946
  rgb(): string;
1926
1947
  rgba(): string;
@@ -1931,7 +1952,9 @@ declare module 'flet-box' {
1931
1952
  isDark(): boolean;
1932
1953
  isLight(): boolean;
1933
1954
  contrast(): string;
1934
- };
1955
+ }
1956
+
1957
+ export const color: (value: string) => ColorInstance;
1935
1958
  export function rgba(r: number, g: number, b: number, a?: number): string;
1936
1959
  export function stackPosition(widget: Widget, props: { top?: any; right?: any; bottom?: any; left?: any }): Widget;
1937
1960
  export function flex(grow: number | { grow?: number; shrink?: number; basis?: string }, shrink?: number, basis?: string): string;
package/src/index.js CHANGED
@@ -280,6 +280,7 @@ export {
280
280
  ParallaxBox,
281
281
  animate,
282
282
  animateAsync,
283
+ stopAnimation,
283
284
  fadeIn,
284
285
  fadeOut,
285
286
  pulse,
@@ -81,7 +81,10 @@ export const AdaptiveScaffold = (props) => {
81
81
  // button to drawerInstance.open().
82
82
  drawerInstance = Drawer({
83
83
  header: null,
84
- body: [content],
84
+ // `body` is an array of widgets, and `leftNavBar` may itself be one, so
85
+ // the content is flattened here. A nested array would reach `children`
86
+ // as a single item and break the append.
87
+ body: Array.isArray(content) ? content : [content],
85
88
  footer: null,
86
89
  position: "left",
87
90
  width: leftNavBarWidth,
@@ -125,6 +128,12 @@ export const AdaptiveScaffold = (props) => {
125
128
  widthExpanded: rightNavBarWidth,
126
129
  widthCollapsed: 50,
127
130
  bgColor: colors.surface,
131
+ // FIX: CollapsibleSideBar aplica `borderRight` por defecto (default
132
+ // `1px solid ${colors.border}`), así que un sidebar derecho acababa
133
+ // con doble borde: `borderRight` (heredado del default) + `borderLeft`
134
+ // (el que pedimos aquí). Forzamos `borderRight: "none"` para que solo
135
+ // tenga el borde izquierdo (el que da al contenido central).
136
+ borderRight: "none",
128
137
  borderLeft: `1px solid ${colors.border}`,
129
138
  children: content,
130
139
  });
@@ -146,4 +155,4 @@ export const AdaptiveScaffold = (props) => {
146
155
  });
147
156
  };
148
157
 
149
- export default AdaptiveScaffold;
158
+ export default AdaptiveScaffold;
@@ -149,6 +149,7 @@ export const BottomNavigation = (props) => {
149
149
  if (!useRouter) return;
150
150
 
151
151
  const currentPath = getCurrentPath();
152
+ const previousIndex = activeIndex;
152
153
 
153
154
  buttons.forEach((btnData, idx) => {
154
155
  const isSelected = btnData.route === currentPath;
@@ -163,7 +164,10 @@ export const BottomNavigation = (props) => {
163
164
  if (isSelected) activeIndex = idx;
164
165
  });
165
166
 
166
- if (onTabChange) onTabChange(activeIndex);
167
+ // Fire only when the route actually moved the selection. A consumer that
168
+ // navigates from `onTabChange` (the normal case) would otherwise recurse:
169
+ // route change -> callback -> goTo -> route change, until the stack blows.
170
+ if (activeIndex !== previousIndex && onTabChange) onTabChange(activeIndex);
167
171
  };
168
172
 
169
173
  // ========== BUILD TAB BUTTONS ==========
@@ -214,8 +218,11 @@ export const BottomNavigation = (props) => {
214
218
 
215
219
  let labelWidget = null;
216
220
  if (showLabels && item.label) {
221
+ // FIX: `value` es una prop `special` que `applySpecial` solo maneja
222
+ // en <input>/<textarea>. En un <span> no hace nada → el label quedaba
223
+ // vacío. La prop correcta para contenido de texto es `text`.
217
224
  labelWidget = Text({
218
- value: item.label,
225
+ text: item.label,
219
226
  size: 11,
220
227
  color: isActive ? selectedColor : unselectedColor,
221
228
  fontWeight: isActive ? "500" : "400",
@@ -307,4 +314,4 @@ export const BottomNavigation = (props) => {
307
314
  return container;
308
315
  };
309
316
 
310
- export default BottomNavigation;
317
+ export default BottomNavigation;
@@ -67,7 +67,11 @@ export const Tabs = (props) => {
67
67
  size = "medium",
68
68
  color = colors.primary,
69
69
  textColor = colors.text,
70
- activeTextColor = colors.white,
70
+ // FIX: `colors.white` no existe en themes.js → caía a `undefined`
71
+ // y el tab activo no se distinguía visualmente. Uso `colors.background`
72
+ // (que es "#ffffff" en el tema claro y "#0f172a" en el oscuro, que es
73
+ // lo correcto: contraste sobre el fondo del tab activo).
74
+ activeTextColor = colors.background,
71
75
  bgColor = colors.gray100,
72
76
  buttonColor = colors.secondary,
73
77
  alignment = "left",
@@ -409,4 +413,4 @@ export const Tabs = (props) => {
409
413
  return container;
410
414
  };
411
415
 
412
- export default Tabs;
416
+ export default Tabs;