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.
- package/README.md +10 -0
- package/bin/cli.js +15 -0
- package/bin/commands/checkApi.js +761 -0
- package/bin/commands/create.js +77 -5
- package/bin/utils/dtsProps.js +191 -0
- package/bin/utils/moduleExports.js +165 -0
- package/bin/utils/neutralise.js +64 -0
- package/bin/utils/templates.js +366 -27
- package/docs/CONTRIBUTING.md +156 -0
- package/docs/README.md +68 -0
- package/docs/arquitectura.md +164 -0
- package/docs/cli/README.md +542 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/app-templates.md +197 -0
- package/docs/guides/compatibility.md +95 -0
- package/docs/guides/embedding.md +284 -0
- package/docs/guides/first-app.md +208 -0
- package/docs/guides/frontend-services.md +171 -0
- package/docs/guides/minification-and-protection.md +260 -0
- package/docs/guides/mobile-and-platforms.md +439 -0
- package/docs/guides/online-updates.md +196 -0
- package/docs/guides/router.md +282 -0
- package/docs/guides/state.md +283 -0
- package/docs/guides/syntax-philosophy.md +150 -0
- package/docs/guides/utilities.md +241 -0
- package/docs/guides/widget-structure.md +163 -0
- package/docs/server/README.md +68 -0
- package/docs/server/START_HERE.md +121 -0
- package/docs/server/api.md +225 -0
- package/docs/server/data-services.md +300 -0
- package/docs/server/deployment.md +113 -0
- package/docs/server/security.md +196 -0
- package/docs/services/README.md +79 -0
- package/docs/services/RamStore.md +84 -0
- package/docs/services/Session.md +84 -0
- package/docs/services/Storage.md +88 -0
- package/docs/services/http.md +86 -0
- package/docs/tools/README.md +138 -0
- package/docs/tools/animation.md +87 -0
- package/docs/tools/async-ids-log.md +80 -0
- package/docs/tools/device.md +132 -0
- package/docs/tools/introspection.md +67 -0
- package/docs/tools/lists.md +96 -0
- package/docs/tools/markdown.md +79 -0
- package/docs/tools/reactivity.md +288 -0
- package/docs/tools/state.md +103 -0
- package/docs/tools/styling.md +92 -0
- package/docs/tools/text-and-time.md +89 -0
- package/docs/tools/theme.md +102 -0
- package/docs/tools/validation.md +75 -0
- package/docs/tools/visual-effects.md +84 -0
- package/docs/widget/Accordion.md +144 -0
- package/docs/widget/AdMob.md +144 -0
- package/docs/widget/AdSense.md +133 -0
- package/docs/widget/AdaptiveScaffold.md +105 -0
- package/docs/widget/AlertDialog.md +103 -0
- package/docs/widget/AnimatedBox.md +130 -0
- package/docs/widget/AnimatedText.md +123 -0
- package/docs/widget/AppBar.md +118 -0
- package/docs/widget/Audio.md +135 -0
- package/docs/widget/Avatar.md +119 -0
- package/docs/widget/Badge.md +128 -0
- package/docs/widget/BottomNavigation.md +116 -0
- package/docs/widget/BottomSheet.md +125 -0
- package/docs/widget/Button.md +137 -0
- package/docs/widget/COMMON_PROPS.md +150 -0
- package/docs/widget/Card.md +125 -0
- package/docs/widget/Carousel.md +151 -0
- package/docs/widget/Chart.md +151 -0
- package/docs/widget/Checkbox.md +118 -0
- package/docs/widget/Chip.md +126 -0
- package/docs/widget/CircularBar.md +168 -0
- package/docs/widget/CircularChart.md +164 -0
- package/docs/widget/CodeViewer.md +130 -0
- package/docs/widget/CollapsibleSideBar.md +112 -0
- package/docs/widget/Column.md +129 -0
- package/docs/widget/Container.md +136 -0
- package/docs/widget/DataTable.md +150 -0
- package/docs/widget/Divider.md +109 -0
- package/docs/widget/DraggBox.md +129 -0
- package/docs/widget/Drawer.md +135 -0
- package/docs/widget/DrawerItem.md +98 -0
- package/docs/widget/Dropdown.md +139 -0
- package/docs/widget/DroppBox.md +152 -0
- package/docs/widget/FloatingActionButton.md +120 -0
- package/docs/widget/GridView.md +140 -0
- package/docs/widget/Icon.md +117 -0
- package/docs/widget/Image.md +125 -0
- package/docs/widget/Input.md +151 -0
- package/docs/widget/Inspector.md +92 -0
- package/docs/widget/InstallButton.md +128 -0
- package/docs/widget/ListTile.md +132 -0
- package/docs/widget/ListView.md +138 -0
- package/docs/widget/Markdown.md +175 -0
- package/docs/widget/MatrixRain.md +122 -0
- package/docs/widget/Modal.md +129 -0
- package/docs/widget/Pagination.md +135 -0
- package/docs/widget/ParallaxBox.md +128 -0
- package/docs/widget/ProgressBar.md +130 -0
- package/docs/widget/QRCode.md +117 -0
- package/docs/widget/README.md +188 -0
- package/docs/widget/Radio.md +123 -0
- package/docs/widget/Rating.md +131 -0
- package/docs/widget/Row.md +131 -0
- package/docs/widget/START_HERE.md +146 -0
- package/docs/widget/Scaffold.md +152 -0
- package/docs/widget/Skeleton.md +130 -0
- package/docs/widget/Slider.md +146 -0
- package/docs/widget/SnackBar.md +102 -0
- package/docs/widget/Stack.md +128 -0
- package/docs/widget/Stepper.md +145 -0
- package/docs/widget/Switch.md +118 -0
- package/docs/widget/Tabs.md +124 -0
- package/docs/widget/Text.md +133 -0
- package/docs/widget/Tooltip.md +145 -0
- package/docs/widget/TreeView.md +176 -0
- package/docs/widget/Video.md +127 -0
- package/docs/widget/_template.md +61 -0
- package/package.json +4 -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 +187 -58
- package/src/animations/index.js +1 -0
- package/src/core/runApp.js +13 -1
- package/src/create-index.sh +1 -1
- package/src/index.d.ts +227 -91
- package/src/index.js +1 -0
- package/src/navigations/AdaptiveScaffold.js +11 -2
- package/src/navigations/AppBar.js +8 -2
- package/src/navigations/BottomNavigation.js +24 -5
- package/src/navigations/DrawerItem.js +16 -3
- package/src/navigations/Scaffold.js +8 -1
- package/src/navigations/Tabs.js +40 -8
- package/src/tools/dimensions.js +11 -3
- package/src/tools/useState.js +139 -101
- package/src/utils/getWidgetProps.js +5 -3
- package/src/widget-factory/assignProps.js +30 -11
- package/src/widget-factory/processProps.js +17 -5
- package/src/widget-factory/reactiveValue.js +172 -0
- package/src/widget-factory/translateProps.js +777 -901
- 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/AlertDialog.js +7 -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 +16 -8
- package/src/widgets/CircularBar.js +12 -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/FloatingActionButton.js +4 -7
- package/src/widgets/Image.js +7 -2
- package/src/widgets/Input.js +119 -6
- package/src/widgets/Inspector.js +16 -10
- package/src/widgets/ListView.js +80 -30
- package/src/widgets/Markdown.js +53 -8
- package/src/widgets/Modal.js +24 -1
- package/src/widgets/Pagination.js +6 -3
- package/src/widgets/ProgressBar.js +16 -13
- package/src/widgets/QRCode.js +9 -2
- package/src/widgets/Radio.js +11 -8
- package/src/widgets/Slider.js +26 -4
- package/src/widgets/SnackBar.js +20 -2
- package/src/widgets/Stack.js +36 -3
- package/src/widgets/Stepper.js +4 -3
- package/src/widgets/Switch.js +8 -5
- package/src/widgets/TreeView.js +3 -2
|
@@ -341,12 +341,16 @@ export const ParallaxBox = (props = {}) => {
|
|
|
341
341
|
|
|
342
342
|
const originalCleanup = container._cleanup;
|
|
343
343
|
/**
|
|
344
|
-
* Removes all event listeners, cancels pending animation frames
|
|
345
|
-
* timers,
|
|
344
|
+
* Removes all event listeners, cancels pending animation frames and clears
|
|
345
|
+
* timers. Idempotent, because it runs from BOTH teardown paths: the lifecycle
|
|
346
|
+
* observer fires `onUnmount`, while runApp/Scaffold call `_cleanup`.
|
|
346
347
|
*
|
|
347
348
|
* @returns {void}
|
|
348
349
|
*/
|
|
349
|
-
|
|
350
|
+
let disposed = false;
|
|
351
|
+
const dispose = () => {
|
|
352
|
+
if (disposed) return;
|
|
353
|
+
disposed = true;
|
|
350
354
|
if (type === "scroll") {
|
|
351
355
|
window.removeEventListener("scroll", handleScroll);
|
|
352
356
|
} else {
|
|
@@ -360,6 +364,15 @@ export const ParallaxBox = (props = {}) => {
|
|
|
360
364
|
if (initialTimer) clearTimeout(initialTimer);
|
|
361
365
|
transitionTimers.forEach((timer) => clearTimeout(timer));
|
|
362
366
|
transitionTimers.clear();
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// The lifecycle observer fires `onUnmount` on a plain DOM disconnect but never
|
|
370
|
+
// calls `_cleanup`, so without this the window scroll listener and the timers
|
|
371
|
+
// survived every removal that was not a full app rebuild.
|
|
372
|
+
container.onUnmount(dispose);
|
|
373
|
+
|
|
374
|
+
container._cleanup = () => {
|
|
375
|
+
dispose();
|
|
363
376
|
if (originalCleanup) originalCleanup();
|
|
364
377
|
};
|
|
365
378
|
|
|
@@ -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 —
|
|
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
|
*
|
|
@@ -114,7 +119,13 @@ const getEasing = (type, t) => {
|
|
|
114
119
|
const readAnimatedValue = (widget, property) => {
|
|
115
120
|
if (property === "scale") return 1;
|
|
116
121
|
if (widget?.style && property in widget.style) {
|
|
117
|
-
|
|
122
|
+
const inline = widget.style[property];
|
|
123
|
+
// An inline-unset property reads as "" and `interpolate` turns that into the
|
|
124
|
+
// string "NaN", which the browser silently drops — so `animate(el, "opacity",
|
|
125
|
+
// undefined, 1, 300)` (the documented API) wrote "NaN" every frame and never
|
|
126
|
+
// animated anything. Fall back to the property's neutral value.
|
|
127
|
+
if (inline !== "" && inline !== null && inline !== undefined) return inline;
|
|
128
|
+
return property === "opacity" ? 1 : 0;
|
|
118
129
|
}
|
|
119
130
|
return widget?.[property];
|
|
120
131
|
};
|
|
@@ -156,6 +167,151 @@ const writeAnimatedValue = (widget, property, value) => {
|
|
|
156
167
|
widget[property] = value;
|
|
157
168
|
};
|
|
158
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
|
+
|
|
159
315
|
// ========== FIRE AND FORGET (No Promise) ==========
|
|
160
316
|
|
|
161
317
|
/**
|
|
@@ -170,7 +326,9 @@ const writeAnimatedValue = (widget, property, value) => {
|
|
|
170
326
|
* @param {number|string} to - Ending value.
|
|
171
327
|
* @param {number} [duration=300] - Animation duration in milliseconds.
|
|
172
328
|
* @param {string} [easing="linear"] - Easing curve name (see {@link getEasing}).
|
|
173
|
-
* @returns {
|
|
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}.
|
|
174
332
|
*/
|
|
175
333
|
export const animate = (
|
|
176
334
|
widget,
|
|
@@ -179,26 +337,7 @@ export const animate = (
|
|
|
179
337
|
to,
|
|
180
338
|
duration = 300,
|
|
181
339
|
easing = "linear",
|
|
182
|
-
) =>
|
|
183
|
-
const startTime = performance.now();
|
|
184
|
-
const startValue = from !== undefined ? from : readAnimatedValue(widget, property);
|
|
185
|
-
const endValue = to;
|
|
186
|
-
|
|
187
|
-
const step = (currentTime) => {
|
|
188
|
-
const elapsed = currentTime - startTime;
|
|
189
|
-
const progress = Math.min(1, elapsed / duration);
|
|
190
|
-
const easedProgress = getEasing(easing, progress);
|
|
191
|
-
const currentValue = interpolate(startValue, endValue, easedProgress);
|
|
192
|
-
|
|
193
|
-
writeAnimatedValue(widget, property, currentValue);
|
|
194
|
-
|
|
195
|
-
if (progress < 1) {
|
|
196
|
-
requestAnimationFrame(step);
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
requestAnimationFrame(step);
|
|
201
|
-
};
|
|
340
|
+
) => runAnimation(widget, property, from, to, duration, easing, null);
|
|
202
341
|
|
|
203
342
|
// ========== ASYNC ANIMATION (Returns Promise) ==========
|
|
204
343
|
|
|
@@ -214,7 +353,8 @@ export const animate = (
|
|
|
214
353
|
* @param {number|string} to - Ending value.
|
|
215
354
|
* @param {number} [duration=300] - Animation duration in milliseconds.
|
|
216
355
|
* @param {string} [easing="linear"] - Easing curve name (see {@link getEasing}).
|
|
217
|
-
* @returns {Promise<HTMLElement|object>} Resolves with `widget` when the animation
|
|
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.
|
|
218
358
|
*/
|
|
219
359
|
export const animateAsync = (
|
|
220
360
|
widget,
|
|
@@ -223,30 +363,12 @@ export const animateAsync = (
|
|
|
223
363
|
to,
|
|
224
364
|
duration = 300,
|
|
225
365
|
easing = "linear",
|
|
226
|
-
) =>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const step = (currentTime) => {
|
|
233
|
-
const elapsed = currentTime - startTime;
|
|
234
|
-
const progress = Math.min(1, elapsed / duration);
|
|
235
|
-
const easedProgress = getEasing(easing, progress);
|
|
236
|
-
const currentValue = interpolate(startValue, endValue, easedProgress);
|
|
237
|
-
|
|
238
|
-
writeAnimatedValue(widget, property, currentValue);
|
|
239
|
-
|
|
240
|
-
if (progress < 1) {
|
|
241
|
-
requestAnimationFrame(step);
|
|
242
|
-
} else {
|
|
243
|
-
resolve(widget);
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
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);
|
|
248
371
|
});
|
|
249
|
-
};
|
|
250
372
|
|
|
251
373
|
// ========== HELPER FUNCTIONS (Fire and Forget) ==========
|
|
252
374
|
|
|
@@ -256,10 +378,10 @@ export const animateAsync = (
|
|
|
256
378
|
*
|
|
257
379
|
* @param {HTMLElement|object} widget - The target widget.
|
|
258
380
|
* @param {number} [duration=300] - Fade duration in milliseconds.
|
|
259
|
-
* @returns {
|
|
381
|
+
* @returns {() => boolean} Cancel function (see {@link animate}).
|
|
260
382
|
*/
|
|
261
383
|
export const fadeOut = (widget, duration = 300) => {
|
|
262
|
-
animate(widget, "opacity", 1, 0, duration);
|
|
384
|
+
return animate(widget, "opacity", 1, 0, duration);
|
|
263
385
|
};
|
|
264
386
|
|
|
265
387
|
/**
|
|
@@ -268,10 +390,10 @@ export const fadeOut = (widget, duration = 300) => {
|
|
|
268
390
|
*
|
|
269
391
|
* @param {HTMLElement|object} widget - The target widget.
|
|
270
392
|
* @param {number} [duration=300] - Fade duration in milliseconds.
|
|
271
|
-
* @returns {
|
|
393
|
+
* @returns {() => boolean} Cancel function (see {@link animate}).
|
|
272
394
|
*/
|
|
273
395
|
export const fadeIn = (widget, duration = 300) => {
|
|
274
|
-
animate(widget, "opacity", 0, 1, duration);
|
|
396
|
+
return animate(widget, "opacity", 0, 1, duration);
|
|
275
397
|
};
|
|
276
398
|
|
|
277
399
|
/**
|
|
@@ -282,14 +404,21 @@ export const fadeIn = (widget, duration = 300) => {
|
|
|
282
404
|
* @param {HTMLElement|object} widget - The target widget.
|
|
283
405
|
* @param {number} [duration=300] - Total pulse duration in milliseconds
|
|
284
406
|
* (split evenly between scale-up and scale-down).
|
|
285
|
-
* @returns {
|
|
407
|
+
* @returns {() => boolean} Cancel function (see {@link animate}).
|
|
286
408
|
*/
|
|
287
|
-
export const pulse = (widget, duration = 300) =>
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
+
);
|
|
293
422
|
|
|
294
423
|
// ========== ASYNC HELPER FUNCTIONS (Returns Promise) ==========
|
|
295
424
|
|
package/src/animations/index.js
CHANGED
package/src/core/runApp.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "../utils/themes.js";
|
|
10
10
|
import { Container } from "../widgets/Container.js";
|
|
11
11
|
import { setGlobalRender } from "../tools/useState.js";
|
|
12
|
+
import { beginRenderPass, endRenderPass } from "../widget-factory/reactiveValue.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Mounts a FletBox application into the DOM and manages its full lifecycle.
|
|
@@ -182,7 +183,18 @@ export const runApp = (
|
|
|
182
183
|
currentMainContainer = null;
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
|
|
186
|
+
// Wrap the component call in a render pass. A reactive value the component
|
|
187
|
+
// reads *here* — a template literal, a conditional, a list — is a structural
|
|
188
|
+
// read, so its key is flagged for an app render. Reads the factory performs
|
|
189
|
+
// while filling props are not flagged, which is what keeps a directly-passed
|
|
190
|
+
// state value surgical.
|
|
191
|
+
beginRenderPass();
|
|
192
|
+
let appContent;
|
|
193
|
+
try {
|
|
194
|
+
appContent = typeof App === "function" ? App() : App;
|
|
195
|
+
} finally {
|
|
196
|
+
endRenderPass();
|
|
197
|
+
}
|
|
186
198
|
|
|
187
199
|
const mainContainer = Container({
|
|
188
200
|
margin: 0,
|
package/src/create-index.sh
CHANGED
|
@@ -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,
|