flet-box 1.0.3 → 1.0.4
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.
- package/package.json +3 -2
- package/src/animations/AnimatedBox.js +32 -1
- package/src/animations/AnimatedText.js +7 -3
- package/src/animations/MatrixRain.js +37 -12
- package/src/animations/ParallaxBox.js +16 -3
- package/src/animations/animate.js +22 -3
- package/src/core/runApp.js +13 -1
- package/src/index.d.ts +191 -78
- package/src/navigations/AppBar.js +8 -2
- package/src/navigations/BottomNavigation.js +14 -2
- package/src/navigations/DrawerItem.js +16 -3
- package/src/navigations/Scaffold.js +8 -1
- package/src/navigations/Tabs.js +34 -6
- package/src/tools/dimensions.js +11 -3
- package/src/tools/useState.js +115 -68
- package/src/utils/getWidgetProps.js +5 -3
- package/src/widget-factory/assignProps.js +7 -10
- package/src/widget-factory/processProps.js +7 -2
- package/src/widget-factory/reactiveValue.js +147 -0
- package/src/widget-factory/warnings.js +15 -0
- package/src/widget-factory/widgetFactory.js +27 -0
- package/src/widgets/Accordion.js +6 -5
- package/src/widgets/Audio.js +30 -8
- package/src/widgets/BottomSheet.js +24 -1
- package/src/widgets/Chart.js +4 -1
- package/src/widgets/Checkbox.js +7 -1
- package/src/widgets/CircularChart.js +4 -1
- package/src/widgets/CodeViewer.js +26 -8
- package/src/widgets/DataTable.js +11 -2
- package/src/widgets/Dropdown.js +5 -1
- package/src/widgets/Image.js +8 -1
- package/src/widgets/Input.js +27 -6
- package/src/widgets/Inspector.js +16 -10
- package/src/widgets/ListView.js +7 -1
- package/src/widgets/Markdown.js +53 -8
- package/src/widgets/Modal.js +24 -1
- package/src/widgets/QRCode.js +9 -2
- package/src/widgets/Radio.js +5 -1
- package/src/widgets/Slider.js +3 -0
- package/src/widgets/SnackBar.js +20 -2
- package/src/widgets/Switch.js +5 -1
- package/src/widgets/TreeView.js +3 -1
package/src/widgets/Modal.js
CHANGED
|
@@ -312,6 +312,8 @@ export const Modal = (props = {}) => {
|
|
|
312
312
|
|
|
313
313
|
let isOpen = false;
|
|
314
314
|
let overlay = null;
|
|
315
|
+
// Pending "hide after the close animation" timer, so it can be cancelled.
|
|
316
|
+
let closeTimer = null;
|
|
315
317
|
let modalBody = null;
|
|
316
318
|
|
|
317
319
|
// Handlers
|
|
@@ -323,7 +325,11 @@ export const Modal = (props = {}) => {
|
|
|
323
325
|
modalBody.style.opacity = "0";
|
|
324
326
|
}
|
|
325
327
|
if (overlay) overlay.style.opacity = "0";
|
|
326
|
-
|
|
328
|
+
// Keep the id: `handleOpen` clears it, otherwise a reopen within 300 ms gets
|
|
329
|
+
// a stale timer that hides an OPEN modal, leaving it invisible and unusable.
|
|
330
|
+
if (closeTimer) clearTimeout(closeTimer);
|
|
331
|
+
closeTimer = setTimeout(() => {
|
|
332
|
+
closeTimer = null;
|
|
327
333
|
if (overlay) overlay.style.visibility = "hidden";
|
|
328
334
|
}, 300);
|
|
329
335
|
if (closeOnEsc) document.removeEventListener("keydown", handleKeyDown);
|
|
@@ -333,6 +339,11 @@ export const Modal = (props = {}) => {
|
|
|
333
339
|
const handleOpen = () => {
|
|
334
340
|
if (isOpen) return;
|
|
335
341
|
isOpen = true;
|
|
342
|
+
// Cancel any pending hide from a close that is still animating out.
|
|
343
|
+
if (closeTimer) {
|
|
344
|
+
clearTimeout(closeTimer);
|
|
345
|
+
closeTimer = null;
|
|
346
|
+
}
|
|
336
347
|
if (overlay) {
|
|
337
348
|
overlay.style.visibility = "visible";
|
|
338
349
|
overlay.style.opacity = "1";
|
|
@@ -405,6 +416,18 @@ export const Modal = (props = {}) => {
|
|
|
405
416
|
overlay.appendChild(modalBody);
|
|
406
417
|
document.body.appendChild(overlay);
|
|
407
418
|
|
|
419
|
+
// The overlay lives in `document.body` and can be removed without `close()`
|
|
420
|
+
// ever running (a state rebuild, a route change, a plain removeChild of the
|
|
421
|
+
// host). `_cleanup` is not called on that path, so release the document
|
|
422
|
+
// listener and any pending animation timer through the lifecycle hook too.
|
|
423
|
+
overlay.onUnmount(() => {
|
|
424
|
+
if (closeTimer) {
|
|
425
|
+
clearTimeout(closeTimer);
|
|
426
|
+
closeTimer = null;
|
|
427
|
+
}
|
|
428
|
+
if (closeOnEsc) document.removeEventListener("keydown", handleKeyDown);
|
|
429
|
+
});
|
|
430
|
+
|
|
408
431
|
// Public API
|
|
409
432
|
const instance = {
|
|
410
433
|
open: handleOpen,
|
package/src/widgets/QRCode.js
CHANGED
|
@@ -116,7 +116,9 @@ function buildQRMatrix(text, ecLevel = "M") {
|
|
|
116
116
|
for (let i = len - 1; i >= 0; i--) bits.push((val >> i) & 1);
|
|
117
117
|
};
|
|
118
118
|
pushBits(0b0100, 4); // mode indicator: byte
|
|
119
|
-
|
|
119
|
+
// Character count: 8 bits for versions 1-9, 16 bits for versions 10-40
|
|
120
|
+
// (ISO/IEC 18004). Emitting 8 bits at version 10 misaligns every later bit.
|
|
121
|
+
pushBits(bytes.length, version < 10 ? 8 : 16);
|
|
120
122
|
for (const b of bytes) pushBits(b, 8);
|
|
121
123
|
// Terminator
|
|
122
124
|
for (let i = 0; i < 4 && bits.length < dataCodewords * 8; i++) bits.push(0);
|
|
@@ -161,7 +163,12 @@ function buildQRMatrix(text, ecLevel = "M") {
|
|
|
161
163
|
|
|
162
164
|
// 7. Build final bit sequence
|
|
163
165
|
const finalBits = [];
|
|
164
|
-
|
|
166
|
+
// NOTE: `pushBits` writes into `bits` (the pre-interleave stream), so it must
|
|
167
|
+
// not be used here. Using it left `finalBits` empty, and the placement pass
|
|
168
|
+
// then wrote every data module as 0 — the symbol carried no payload at all.
|
|
169
|
+
for (const cw of finalCW) {
|
|
170
|
+
for (let i = 7; i >= 0; i--) finalBits.push((cw >> i) & 1);
|
|
171
|
+
}
|
|
165
172
|
// Remainder bits (version-dependent, versions 2–6 need 7)
|
|
166
173
|
const remainderBitsCount = version >= 2 && version <= 6 ? 7 : 0;
|
|
167
174
|
for (let i = 0; i < remainderBitsCount; i++) finalBits.push(0);
|
package/src/widgets/Radio.js
CHANGED
|
@@ -67,7 +67,7 @@ export const Radio = (props) => {
|
|
|
67
67
|
|
|
68
68
|
// Handle click
|
|
69
69
|
if (!disabled) {
|
|
70
|
-
|
|
70
|
+
const handleClick = (e) => {
|
|
71
71
|
e.stopPropagation();
|
|
72
72
|
if (internalSelected) return;
|
|
73
73
|
|
|
@@ -88,6 +88,10 @@ export const Radio = (props) => {
|
|
|
88
88
|
|
|
89
89
|
if (onPress) onPress(true);
|
|
90
90
|
};
|
|
91
|
+
// Routed through the factory props: `assignProps` clears `onclick` on every
|
|
92
|
+
// update and only re-applies handlers present in the merged props, so a
|
|
93
|
+
// direct assignment died on the first `update()` or reactive setter.
|
|
94
|
+
radio.update({ onPress: handleClick });
|
|
91
95
|
|
|
92
96
|
// Hover effect
|
|
93
97
|
radio.addEventListener("mouseenter", () => {
|
package/src/widgets/Slider.js
CHANGED
|
@@ -299,6 +299,9 @@ export const Slider = (props) => {
|
|
|
299
299
|
|
|
300
300
|
function generateDefaultMarks() {
|
|
301
301
|
const stepMark = (currentMax - currentMin) / 4;
|
|
302
|
+
// With `min === max` (or non-numeric bounds) stepMark is 0/NaN, so
|
|
303
|
+
// `i += stepMark` never advances and the loop hangs the tab forever.
|
|
304
|
+
if (!(stepMark > 0)) return [currentMin];
|
|
302
305
|
const marksList = [];
|
|
303
306
|
for (let i = currentMin; i <= currentMax + 0.1; i += stepMark) {
|
|
304
307
|
marksList.push(Math.round(i));
|
package/src/widgets/SnackBar.js
CHANGED
|
@@ -124,9 +124,18 @@ export function SnackBar(options) {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
let timeoutId = null;
|
|
127
|
+
let rafId = null;
|
|
128
|
+
// `close()` is final: it must fire `onClose` exactly once and must win any
|
|
129
|
+
// race against the pending `show()` frame, otherwise closing a toast right
|
|
130
|
+
// after creating it would flash it back to opacity 1 for a full animation.
|
|
131
|
+
let closed = false;
|
|
127
132
|
|
|
128
133
|
const show = () => {
|
|
129
|
-
|
|
134
|
+
if (closed) return;
|
|
135
|
+
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
136
|
+
rafId = requestAnimationFrame(() => {
|
|
137
|
+
rafId = null;
|
|
138
|
+
if (closed) return;
|
|
130
139
|
snackbar.style.opacity = "1";
|
|
131
140
|
snackbar.style.transform = "translateY(0)";
|
|
132
141
|
});
|
|
@@ -134,7 +143,16 @@ export function SnackBar(options) {
|
|
|
134
143
|
};
|
|
135
144
|
|
|
136
145
|
const close = () => {
|
|
137
|
-
if (
|
|
146
|
+
if (closed) return;
|
|
147
|
+
closed = true;
|
|
148
|
+
if (timeoutId) {
|
|
149
|
+
clearTimeout(timeoutId);
|
|
150
|
+
timeoutId = null;
|
|
151
|
+
}
|
|
152
|
+
if (rafId !== null) {
|
|
153
|
+
cancelAnimationFrame(rafId);
|
|
154
|
+
rafId = null;
|
|
155
|
+
}
|
|
138
156
|
snackbar.style.opacity = "0";
|
|
139
157
|
snackbar.style.transform =
|
|
140
158
|
position === "bottom"
|
package/src/widgets/Switch.js
CHANGED
|
@@ -79,13 +79,17 @@ export const Switch = (props) => {
|
|
|
79
79
|
|
|
80
80
|
// Handle click
|
|
81
81
|
if (!disabled) {
|
|
82
|
-
|
|
82
|
+
const handleClick = (e) => {
|
|
83
83
|
e.stopPropagation();
|
|
84
84
|
const newValue = !internalValue;
|
|
85
85
|
internalValue = newValue;
|
|
86
86
|
updateUI(newValue);
|
|
87
87
|
if (onPress) onPress(newValue);
|
|
88
88
|
};
|
|
89
|
+
// Routed through the factory props: `assignProps` clears `onclick` on every
|
|
90
|
+
// update and only re-applies handlers present in the merged props, so a
|
|
91
|
+
// direct assignment died on the first `update()` or reactive setter.
|
|
92
|
+
switchContainer.update({ onPress: handleClick });
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
// Public methods
|
package/src/widgets/TreeView.js
CHANGED
|
@@ -280,7 +280,9 @@ export const TreeView = (props) => {
|
|
|
280
280
|
|
|
281
281
|
// ========== PUBLIC METHODS ==========
|
|
282
282
|
|
|
283
|
-
|
|
283
|
+
// NOTE: no `selectedId = null` default. The guard below tests `!== undefined`,
|
|
284
|
+
// so a `null` default made it always true and every call wiped the selection.
|
|
285
|
+
container.updateNodes = (newNodes, selectedId) => {
|
|
284
286
|
nodes.length = 0;
|
|
285
287
|
nodes.push(...newNodes);
|
|
286
288
|
if (selectedId !== undefined) currentSelectedId = selectedId;
|