flet-box 0.0.0-stage → 1.0.2
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/LICENSE +21 -0
- package/README.md +190 -2
- package/bin/cli.js +394 -0
- package/bin/commands/buildAndroid.js +200 -0
- package/bin/commands/buildIOS.js +237 -0
- package/bin/commands/create.js +406 -0
- package/bin/commands/createBundle.js +375 -0
- package/bin/commands/createComponent.js +115 -0
- package/bin/commands/createScreen.js +161 -0
- package/bin/commands/killServer.js +107 -0
- package/bin/commands/package.js +131 -0
- package/bin/commands/release.js +189 -0
- package/bin/commands/runBundle.js +62 -0
- package/bin/commands/runCloudflare.js +89 -0
- package/bin/commands/runNative.js +109 -0
- package/bin/commands/runServer.js +390 -0
- package/bin/ota/loader.js +361 -0
- package/bin/utils/bundleConfig.js +178 -0
- package/bin/utils/capacitor.js +137 -0
- package/bin/utils/colors.js +215 -0
- package/bin/utils/helpers.js +80 -0
- package/bin/utils/spinner.js +96 -0
- package/bin/utils/templates.js +1824 -0
- package/bin/utils/version.js +108 -0
- package/package.json +58 -4
- package/src/animations/AnimatedBox.js +607 -0
- package/src/animations/AnimatedText.js +105 -0
- package/src/animations/MatrixRain.js +220 -0
- package/src/animations/ParallaxBox.js +369 -0
- package/src/animations/animate.js +332 -0
- package/src/animations/index.js +25 -0
- package/src/app.js +41 -0
- package/src/assets/icon-1024-maskable.png +0 -0
- package/src/assets/icon-192-maskable.png +0 -0
- package/src/assets/icon-192.png +0 -0
- package/src/assets/icon-512-maskable.png +0 -0
- package/src/assets/icon-512.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/core/App.js +44 -0
- package/src/core/hmr-client.js +142 -0
- package/src/core/index.js +34 -0
- package/src/core/pwa.js +100 -0
- package/src/core/runApp.js +430 -0
- package/src/create-index.sh +237 -0
- package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
- package/src/fonts/icons.css +23 -0
- package/src/index.d.ts +1930 -0
- package/src/index.js +288 -0
- package/src/navigations/AdaptiveScaffold.js +149 -0
- package/src/navigations/AppBar.js +331 -0
- package/src/navigations/BottomNavigation.js +298 -0
- package/src/navigations/CollapsibleSideBar.js +139 -0
- package/src/navigations/Drawer.js +333 -0
- package/src/navigations/DrawerItem.js +260 -0
- package/src/navigations/Router.js +475 -0
- package/src/navigations/Scaffold.js +537 -0
- package/src/navigations/Tabs.js +384 -0
- package/src/navigations/index.js +57 -0
- package/src/services/RamStore.js +213 -0
- package/src/services/Session.js +310 -0
- package/src/services/Storage.js +309 -0
- package/src/services/http.js +350 -0
- package/src/services/index.js +57 -0
- package/src/tools/animation.js +42 -0
- package/src/tools/array.js +95 -0
- package/src/tools/border.js +28 -0
- package/src/tools/clipboard.js +104 -0
- package/src/tools/color.js +329 -0
- package/src/tools/createList.js +215 -0
- package/src/tools/delay.js +55 -0
- package/src/tools/device.js +138 -0
- package/src/tools/dict.js +297 -0
- package/src/tools/dimensions.js +118 -0
- package/src/tools/filter.js +41 -0
- package/src/tools/flex.js +26 -0
- package/src/tools/gradient.js +34 -0
- package/src/tools/grid.js +57 -0
- package/src/tools/index.js +38 -0
- package/src/tools/mapList.js +107 -0
- package/src/tools/margin.js +93 -0
- package/src/tools/memo.js +60 -0
- package/src/tools/os.js +104 -0
- package/src/tools/padding.js +93 -0
- package/src/tools/print.js +69 -0
- package/src/tools/random.js +299 -0
- package/src/tools/ref.js +70 -0
- package/src/tools/rgba.js +4 -0
- package/src/tools/shadow.js +23 -0
- package/src/tools/string.js +85 -0
- package/src/tools/time.js +674 -0
- package/src/tools/transform.js +68 -0
- package/src/tools/transition.js +48 -0
- package/src/tools/useState.js +268 -0
- package/src/tools/uuid.js +42 -0
- package/src/utils/TextInputValidator.js +256 -0
- package/src/utils/composeUpdate.js +65 -0
- package/src/utils/getWidgetProps.js +35 -0
- package/src/utils/index.js +76 -0
- package/src/utils/markdownParser.js +368 -0
- package/src/utils/mediaTime.js +79 -0
- package/src/utils/navigation.js +263 -0
- package/src/utils/scriptInjector.js +73 -0
- package/src/utils/stackPosition.js +31 -0
- package/src/utils/stopWebRefresh.js +77 -0
- package/src/utils/styleInjector.js +38 -0
- package/src/utils/syntaxHighlight.js +339 -0
- package/src/utils/themes.js +347 -0
- package/src/utils/units.js +87 -0
- package/src/utils/visualEffects.js +169 -0
- package/src/widget-factory/addChildren.js +114 -0
- package/src/widget-factory/assignProps.js +256 -0
- package/src/widget-factory/createWidget.js +28 -0
- package/src/widget-factory/effects.js +132 -0
- package/src/widget-factory/index.js +7 -0
- package/src/widget-factory/lifecycle.js +210 -0
- package/src/widget-factory/makeParentable.js +89 -0
- package/src/widget-factory/pixTool.js +40 -0
- package/src/widget-factory/processProps.js +122 -0
- package/src/widget-factory/reactivity.js +98 -0
- package/src/widget-factory/remTool.js +34 -0
- package/src/widget-factory/stackPosition.js +87 -0
- package/src/widget-factory/stringTool.js +26 -0
- package/src/widget-factory/tools.js +249 -0
- package/src/widget-factory/translateProps.js +1049 -0
- package/src/widget-factory/warnings.js +284 -0
- package/src/widget-factory/widgetFactory.js +200 -0
- package/src/widgets/Accordion.js +562 -0
- package/src/widgets/AdMob.js +315 -0
- package/src/widgets/AdSense.js +268 -0
- package/src/widgets/AlertDialog.js +162 -0
- package/src/widgets/Audio.js +311 -0
- package/src/widgets/Avatar.js +210 -0
- package/src/widgets/Badge.js +189 -0
- package/src/widgets/BottomSheet.js +287 -0
- package/src/widgets/Button.js +247 -0
- package/src/widgets/Card.js +33 -0
- package/src/widgets/Carousel.js +301 -0
- package/src/widgets/Chart.js +632 -0
- package/src/widgets/Checkbox.js +106 -0
- package/src/widgets/Chip.js +153 -0
- package/src/widgets/CircularBar.js +352 -0
- package/src/widgets/CircularChart.js +549 -0
- package/src/widgets/CodeViewer.js +236 -0
- package/src/widgets/Column.js +29 -0
- package/src/widgets/Container.js +31 -0
- package/src/widgets/DataTable.js +257 -0
- package/src/widgets/Divider.js +93 -0
- package/src/widgets/DraggBox.js +115 -0
- package/src/widgets/Dropdown.js +505 -0
- package/src/widgets/DroppBox.js +180 -0
- package/src/widgets/FloatingActionButton.js +91 -0
- package/src/widgets/GridView.js +20 -0
- package/src/widgets/Icon.js +33 -0
- package/src/widgets/Image.js +15 -0
- package/src/widgets/Input.js +565 -0
- package/src/widgets/Inspector.js +254 -0
- package/src/widgets/InstallButton.js +110 -0
- package/src/widgets/ListTile.js +224 -0
- package/src/widgets/ListView.js +503 -0
- package/src/widgets/Markdown.js +1236 -0
- package/src/widgets/Modal.js +496 -0
- package/src/widgets/Pagination.js +195 -0
- package/src/widgets/ProgressBar.js +178 -0
- package/src/widgets/QRCode.js +467 -0
- package/src/widgets/Radio.js +132 -0
- package/src/widgets/Rating.js +261 -0
- package/src/widgets/Row.js +29 -0
- package/src/widgets/Skeleton.js +243 -0
- package/src/widgets/Slider.js +527 -0
- package/src/widgets/SnackBar.js +227 -0
- package/src/widgets/Stack.js +29 -0
- package/src/widgets/Stepper.js +296 -0
- package/src/widgets/Switch.js +102 -0
- package/src/widgets/Text.js +98 -0
- package/src/widgets/Tooltip.js +267 -0
- package/src/widgets/TreeView.js +370 -0
- package/src/widgets/Video.js +92 -0
- package/src/widgets/index.js +52 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* animate.js - Imperative animation utilities for DOM widgets.
|
|
3
|
+
*
|
|
4
|
+
* Provides two tiers of animation helpers:
|
|
5
|
+
* - **Fire-and-forget** (`animate`, `fadeOut`, `fadeIn`, `pulse`): start an
|
|
6
|
+
* animation and move on — no way to await completion.
|
|
7
|
+
* - **Async/awaitable** (`animateAsync`, `fadeOutAsync`, `fadeInAsync`,
|
|
8
|
+
* `pulseAsync`): return a Promise that resolves when the animation finishes,
|
|
9
|
+
* allowing sequential composition with `await`.
|
|
10
|
+
*
|
|
11
|
+
* All functions drive the browser's `requestAnimationFrame` loop directly and
|
|
12
|
+
* do not rely on CSS transitions or the Web Animations API.
|
|
13
|
+
*
|
|
14
|
+
* @module animations/animate
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ========== INTERPOLATION HELPERS ==========
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Interpolates between two values at a given progress point.
|
|
21
|
+
* Handles three value types:
|
|
22
|
+
* - `number → number`: straight numeric lerp.
|
|
23
|
+
* - `"#rrggbb" → string`: delegates to {@link interpolateColor}.
|
|
24
|
+
* - `"16px" → string`: parses the numeric part, lerps it, re-attaches the unit.
|
|
25
|
+
*
|
|
26
|
+
* @param {number|string} from - Starting value.
|
|
27
|
+
* @param {number|string} to - Ending value.
|
|
28
|
+
* @param {number} progress - Interpolation factor in [0, 1].
|
|
29
|
+
* @returns {number|string} Interpolated value.
|
|
30
|
+
*/
|
|
31
|
+
const interpolate = (from, to, progress) => {
|
|
32
|
+
// Number to number
|
|
33
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
34
|
+
return from + (to - from) * progress;
|
|
35
|
+
}
|
|
36
|
+
// Color to color (hex → rgb)
|
|
37
|
+
if (typeof from === "string" && from.startsWith("#")) {
|
|
38
|
+
return interpolateColor(from, to, progress);
|
|
39
|
+
}
|
|
40
|
+
// String with units (e.g., "16px", "2rem")
|
|
41
|
+
if (typeof from === "string") {
|
|
42
|
+
const fromNum = parseFloat(from);
|
|
43
|
+
const toNum = parseFloat(to);
|
|
44
|
+
const unit = from.replace(fromNum.toString(), "");
|
|
45
|
+
return fromNum + (toNum - fromNum) * progress + unit;
|
|
46
|
+
}
|
|
47
|
+
return to;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Linearly interpolates between two hex color values, returning an `rgb()` string.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} color1 - Starting color as a hex string (e.g. `"#ff0000"`).
|
|
54
|
+
* @param {string} color2 - Ending color as a hex string.
|
|
55
|
+
* @param {number} progress - Interpolation factor in [0, 1].
|
|
56
|
+
* @returns {string} Interpolated color as `"rgb(r, g, b)"`.
|
|
57
|
+
*/
|
|
58
|
+
const interpolateColor = (color1, color2, progress) => {
|
|
59
|
+
const rgb1 = hexToRgb(color1);
|
|
60
|
+
const rgb2 = hexToRgb(color2);
|
|
61
|
+
const r = Math.round(rgb1.r + (rgb2.r - rgb1.r) * progress);
|
|
62
|
+
const g = Math.round(rgb1.g + (rgb2.g - rgb1.g) * progress);
|
|
63
|
+
const b = Math.round(rgb1.b + (rgb2.b - rgb1.b) * progress);
|
|
64
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parses a hex color string into its red, green, and blue components.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} hex - Hex color string (with or without leading `#`).
|
|
71
|
+
* @returns {{ r: number, g: number, b: number }} RGB components in [0, 255].
|
|
72
|
+
* Returns `{ r: 0, g: 0, b: 0 }` if the string cannot be parsed.
|
|
73
|
+
*/
|
|
74
|
+
const hexToRgb = (hex) => {
|
|
75
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
76
|
+
return result
|
|
77
|
+
? {
|
|
78
|
+
r: parseInt(result[1], 16),
|
|
79
|
+
g: parseInt(result[2], 16),
|
|
80
|
+
b: parseInt(result[3], 16),
|
|
81
|
+
}
|
|
82
|
+
: { r: 0, g: 0, b: 0 };
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// ========== EASING FUNCTIONS ==========
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Applies a named easing curve to a linear progress value.
|
|
89
|
+
* Falls back to linear if `type` is not recognized.
|
|
90
|
+
*
|
|
91
|
+
* @param {"linear"|"easeIn"|"easeOut"|"easeInOut"} type - Easing curve name.
|
|
92
|
+
* @param {number} t - Raw linear progress in [0, 1].
|
|
93
|
+
* @returns {number} Eased progress value in [0, 1].
|
|
94
|
+
*/
|
|
95
|
+
const getEasing = (type, t) => {
|
|
96
|
+
const easings = {
|
|
97
|
+
linear: (t) => t,
|
|
98
|
+
easeIn: (t) => t * t,
|
|
99
|
+
easeOut: (t) => t * (2 - t),
|
|
100
|
+
easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
|
|
101
|
+
};
|
|
102
|
+
return easings[type]?.(t) ?? t;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Reads the current animated value of a property from a widget.
|
|
107
|
+
* For `"scale"`, returns `1` as a safe default since scale is stored
|
|
108
|
+
* inside a `transform` string rather than as a standalone property.
|
|
109
|
+
*
|
|
110
|
+
* @param {HTMLElement|object} widget - The target widget or DOM element.
|
|
111
|
+
* @param {string} property - The property to read.
|
|
112
|
+
* @returns {number|string} The current value, or `1` for `"scale"`.
|
|
113
|
+
*/
|
|
114
|
+
const readAnimatedValue = (widget, property) => {
|
|
115
|
+
if (property === "scale") return 1;
|
|
116
|
+
if (widget?.style && property in widget.style) {
|
|
117
|
+
return widget.style[property];
|
|
118
|
+
}
|
|
119
|
+
return widget?.[property];
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Writes an animated value to a widget property.
|
|
124
|
+
* - `"scale"` is written as `transform: scale(value)`.
|
|
125
|
+
* - Numeric values for style properties that require a unit get `"px"` appended
|
|
126
|
+
* (unitless properties such as `opacity`, `zIndex`, etc. are exempt).
|
|
127
|
+
* - Falls back to setting the property directly on the widget object for
|
|
128
|
+
* non-style properties.
|
|
129
|
+
*
|
|
130
|
+
* @param {HTMLElement|object} widget - The target widget or DOM element.
|
|
131
|
+
* @param {string} property - The property to write.
|
|
132
|
+
* @param {number|string} value - The value to assign.
|
|
133
|
+
* @returns {void}
|
|
134
|
+
*/
|
|
135
|
+
const writeAnimatedValue = (widget, property, value) => {
|
|
136
|
+
if (!widget) return;
|
|
137
|
+
if (property === "scale") {
|
|
138
|
+
widget.style.transform = `scale(${value})`;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (widget.style && property in widget.style) {
|
|
142
|
+
const unitlessProperties = new Set([
|
|
143
|
+
"opacity",
|
|
144
|
+
"zIndex",
|
|
145
|
+
"fontWeight",
|
|
146
|
+
"lineHeight",
|
|
147
|
+
"flexGrow",
|
|
148
|
+
"flexShrink",
|
|
149
|
+
]);
|
|
150
|
+
widget.style[property] =
|
|
151
|
+
typeof value === "number" && !unitlessProperties.has(property)
|
|
152
|
+
? `${value}px`
|
|
153
|
+
: value;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
widget[property] = value;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// ========== FIRE AND FORGET (No Promise) ==========
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Animates a single property of a widget from one value to another.
|
|
163
|
+
* Fire-and-forget: returns immediately without waiting for the animation to end.
|
|
164
|
+
* Use {@link animateAsync} when you need to sequence animations.
|
|
165
|
+
*
|
|
166
|
+
* @param {HTMLElement|object} widget - The target widget or DOM element.
|
|
167
|
+
* @param {string} property - The CSS property (or `"scale"`) to animate.
|
|
168
|
+
* @param {number|string} from - Starting value. If `undefined`, the current
|
|
169
|
+
* value is read from the widget via {@link readAnimatedValue}.
|
|
170
|
+
* @param {number|string} to - Ending value.
|
|
171
|
+
* @param {number} [duration=300] - Animation duration in milliseconds.
|
|
172
|
+
* @param {string} [easing="linear"] - Easing curve name (see {@link getEasing}).
|
|
173
|
+
* @returns {void}
|
|
174
|
+
*/
|
|
175
|
+
export const animate = (
|
|
176
|
+
widget,
|
|
177
|
+
property,
|
|
178
|
+
from,
|
|
179
|
+
to,
|
|
180
|
+
duration = 300,
|
|
181
|
+
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
|
+
};
|
|
202
|
+
|
|
203
|
+
// ========== ASYNC ANIMATION (Returns Promise) ==========
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Animates a single property of a widget from one value to another.
|
|
207
|
+
* Returns a Promise that resolves with the widget once the animation completes.
|
|
208
|
+
* Use this when you need to `await` the end of an animation before starting the next.
|
|
209
|
+
*
|
|
210
|
+
* @param {HTMLElement|object} widget - The target widget or DOM element.
|
|
211
|
+
* @param {string} property - The CSS property (or `"scale"`) to animate.
|
|
212
|
+
* @param {number|string} from - Starting value. If `undefined`, the current
|
|
213
|
+
* value is read from the widget via {@link readAnimatedValue}.
|
|
214
|
+
* @param {number|string} to - Ending value.
|
|
215
|
+
* @param {number} [duration=300] - Animation duration in milliseconds.
|
|
216
|
+
* @param {string} [easing="linear"] - Easing curve name (see {@link getEasing}).
|
|
217
|
+
* @returns {Promise<HTMLElement|object>} Resolves with `widget` when the animation finishes.
|
|
218
|
+
*/
|
|
219
|
+
export const animateAsync = (
|
|
220
|
+
widget,
|
|
221
|
+
property,
|
|
222
|
+
from,
|
|
223
|
+
to,
|
|
224
|
+
duration = 300,
|
|
225
|
+
easing = "linear",
|
|
226
|
+
) => {
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
const startTime = performance.now();
|
|
229
|
+
const startValue = from !== undefined ? from : readAnimatedValue(widget, property);
|
|
230
|
+
const endValue = to;
|
|
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);
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// ========== HELPER FUNCTIONS (Fire and Forget) ==========
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Fades a widget out by animating its `opacity` from `1` to `0`.
|
|
255
|
+
* Fire-and-forget — does not return a Promise.
|
|
256
|
+
*
|
|
257
|
+
* @param {HTMLElement|object} widget - The target widget.
|
|
258
|
+
* @param {number} [duration=300] - Fade duration in milliseconds.
|
|
259
|
+
* @returns {void}
|
|
260
|
+
*/
|
|
261
|
+
export const fadeOut = (widget, duration = 300) => {
|
|
262
|
+
animate(widget, "opacity", 1, 0, duration);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Fades a widget in by animating its `opacity` from `0` to `1`.
|
|
267
|
+
* Fire-and-forget — does not return a Promise.
|
|
268
|
+
*
|
|
269
|
+
* @param {HTMLElement|object} widget - The target widget.
|
|
270
|
+
* @param {number} [duration=300] - Fade duration in milliseconds.
|
|
271
|
+
* @returns {void}
|
|
272
|
+
*/
|
|
273
|
+
export const fadeIn = (widget, duration = 300) => {
|
|
274
|
+
animate(widget, "opacity", 0, 1, duration);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Scales a widget up then back to its original size (a "pulse" effect).
|
|
279
|
+
* Fire-and-forget — does not return a Promise. For an awaitable version use
|
|
280
|
+
* {@link pulseAsync}.
|
|
281
|
+
*
|
|
282
|
+
* @param {HTMLElement|object} widget - The target widget.
|
|
283
|
+
* @param {number} [duration=300] - Total pulse duration in milliseconds
|
|
284
|
+
* (split evenly between scale-up and scale-down).
|
|
285
|
+
* @returns {void}
|
|
286
|
+
*/
|
|
287
|
+
export const pulse = (widget, duration = 300) => {
|
|
288
|
+
animate(widget, "scale", 1, 1.1, duration / 2);
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
animate(widget, "scale", 1.1, 1, duration / 2);
|
|
291
|
+
}, duration / 2);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// ========== ASYNC HELPER FUNCTIONS (Returns Promise) ==========
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Fades a widget out by animating its `opacity` from `1` to `0`.
|
|
298
|
+
* Returns a Promise that resolves when the fade completes.
|
|
299
|
+
*
|
|
300
|
+
* @param {HTMLElement|object} widget - The target widget.
|
|
301
|
+
* @param {number} [duration=300] - Fade duration in milliseconds.
|
|
302
|
+
* @returns {Promise<HTMLElement|object>} Resolves with `widget` on completion.
|
|
303
|
+
*/
|
|
304
|
+
export const fadeOutAsync = (widget, duration = 300) => {
|
|
305
|
+
return animateAsync(widget, "opacity", 1, 0, duration);
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Fades a widget in by animating its `opacity` from `0` to `1`.
|
|
310
|
+
* Returns a Promise that resolves when the fade completes.
|
|
311
|
+
*
|
|
312
|
+
* @param {HTMLElement|object} widget - The target widget.
|
|
313
|
+
* @param {number} [duration=300] - Fade duration in milliseconds.
|
|
314
|
+
* @returns {Promise<HTMLElement|object>} Resolves with `widget` on completion.
|
|
315
|
+
*/
|
|
316
|
+
export const fadeInAsync = (widget, duration = 300) => {
|
|
317
|
+
return animateAsync(widget, "opacity", 0, 1, duration);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Scales a widget up then back to its original size, awaiting each phase.
|
|
322
|
+
* Use this when subsequent code must run only after the full pulse finishes.
|
|
323
|
+
*
|
|
324
|
+
* @param {HTMLElement|object} widget - The target widget.
|
|
325
|
+
* @param {number} [duration=300] - Total pulse duration in milliseconds
|
|
326
|
+
* (split evenly between scale-up and scale-down).
|
|
327
|
+
* @returns {Promise<void>} Resolves when both phases of the pulse have completed.
|
|
328
|
+
*/
|
|
329
|
+
export const pulseAsync = async (widget, duration = 300) => {
|
|
330
|
+
await animateAsync(widget, "scale", 1, 1.1, duration / 2);
|
|
331
|
+
await animateAsync(widget, "scale", 1.1, 1, duration / 2);
|
|
332
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* animations/index.js - Public entry point for the animations module.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all animation widgets and imperative animation utilities so
|
|
5
|
+
* consumers can import from a single path:
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { AnimatedBox, animate, fadeInAsync } from "./animations/index.js";
|
|
9
|
+
*
|
|
10
|
+
* @module animations
|
|
11
|
+
*/
|
|
12
|
+
export { ParallaxBox } from "./ParallaxBox.js";
|
|
13
|
+
export { AnimatedBox } from "./AnimatedBox.js";
|
|
14
|
+
export { AnimatedText } from "./AnimatedText.js";
|
|
15
|
+
export { MatrixRain } from "./MatrixRain.js";
|
|
16
|
+
export {
|
|
17
|
+
animate,
|
|
18
|
+
animateAsync,
|
|
19
|
+
fadeOut,
|
|
20
|
+
fadeIn,
|
|
21
|
+
pulse,
|
|
22
|
+
fadeOutAsync,
|
|
23
|
+
fadeInAsync,
|
|
24
|
+
pulseAsync,
|
|
25
|
+
} from "./animate.js";
|
package/src/app.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// app.js - Direct reactive property assignment demo
|
|
2
|
+
import { runApp, Container, colors, Button } from "./index.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Root application component.
|
|
6
|
+
*
|
|
7
|
+
* Demonstrates FletBox's reactive widget model: instead of triggering a full
|
|
8
|
+
* tree rebuild, individual widget properties (e.g. `text`, `bgColor`) are
|
|
9
|
+
* assigned directly on the returned widget instance. The framework detects the
|
|
10
|
+
* assignment and patches only the affected element in the DOM.
|
|
11
|
+
*
|
|
12
|
+
* @returns {HTMLElement} A full-viewport centered container holding the demo button.
|
|
13
|
+
*/
|
|
14
|
+
const App = () => {
|
|
15
|
+
let clickCount = 0;
|
|
16
|
+
|
|
17
|
+
const miBoton = Button({
|
|
18
|
+
text: "Click me hello",
|
|
19
|
+
bgColor: colors.surface,
|
|
20
|
+
onPress: () => {
|
|
21
|
+
clickCount++;
|
|
22
|
+
|
|
23
|
+
// ✅ Directly assign to the reactive widget — no re-render needed
|
|
24
|
+
miBoton.text = `Clicked ${clickCount} times`;
|
|
25
|
+
|
|
26
|
+
if (clickCount >= 5) {
|
|
27
|
+
miBoton.bgColor = "#ff4444";
|
|
28
|
+
miBoton.textColor = "white";
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return Container({
|
|
34
|
+
justifyContent: "center",
|
|
35
|
+
alignItems: "center",
|
|
36
|
+
height: "100vh",
|
|
37
|
+
child: miBoton,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
runApp(App, "root");
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/core/App.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/core/App.js
|
|
2
|
+
// FletBox Kids - Ultra simple: only receives ONE widget
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mounts a single widget into the application root element.
|
|
6
|
+
*
|
|
7
|
+
* Looks for an existing `#root` element in the document. If none is found, a
|
|
8
|
+
* new `<div id="root">` is created and appended to `document.body` — the body
|
|
9
|
+
* itself is never cleared so that host scripts and markup are preserved.
|
|
10
|
+
*
|
|
11
|
+
* The root is reset on every call (via `innerHTML = ""`), making this function
|
|
12
|
+
* suitable for simple, single-widget apps that do not need the full lifecycle
|
|
13
|
+
* management provided by `runApp`.
|
|
14
|
+
*
|
|
15
|
+
* @param {HTMLElement} widget - The widget element to render inside the root.
|
|
16
|
+
* Must be a valid DOM element (`nodeType === 1`). When provided, its width is
|
|
17
|
+
* stretched to fill the root container.
|
|
18
|
+
* @returns {HTMLElement} The root container element.
|
|
19
|
+
*/
|
|
20
|
+
export const App = (widget) => {
|
|
21
|
+
let root = document.getElementById("root");
|
|
22
|
+
if (!root) {
|
|
23
|
+
// Never clear document.body (it would wipe the host's scripts/markup);
|
|
24
|
+
// create a dedicated root container instead.
|
|
25
|
+
root = document.createElement("div");
|
|
26
|
+
root.id = "root";
|
|
27
|
+
document.body.appendChild(root);
|
|
28
|
+
}
|
|
29
|
+
root.innerHTML = "";
|
|
30
|
+
root.style.margin = "0";
|
|
31
|
+
root.style.padding = "0";
|
|
32
|
+
|
|
33
|
+
root.style.width = "100%";
|
|
34
|
+
root.style.minHeight = "100vh";
|
|
35
|
+
|
|
36
|
+
if (widget && widget.nodeType === 1) {
|
|
37
|
+
widget.style.width = "100%";
|
|
38
|
+
root.appendChild(widget);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return root;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default App;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/core/hmr-client.js
|
|
2
|
+
|
|
3
|
+
/** @type {WebSocket|null} Active WebSocket connection to the HMR server. */
|
|
4
|
+
let ws = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Registry of modules tracked by HMR.
|
|
8
|
+
* Keys are module URLs; values are the imported module objects.
|
|
9
|
+
*
|
|
10
|
+
* @type {Map<string, Object>}
|
|
11
|
+
*/
|
|
12
|
+
let modules = new Map();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialises the Hot Module Replacement (HMR) client.
|
|
16
|
+
*
|
|
17
|
+
* Opens a WebSocket connection to the current host using the appropriate
|
|
18
|
+
* protocol (`ws:` or `wss:` depending on whether the page is served over
|
|
19
|
+
* HTTPS). Handles three server message types:
|
|
20
|
+
*
|
|
21
|
+
* - `request-modules` — the server asks which modules this client has
|
|
22
|
+
* registered; responds with a `register-modules` message.
|
|
23
|
+
* - `hmr` — a file changed; delegates to `handleHMR`.
|
|
24
|
+
* - `reload` — the server requests a full page reload.
|
|
25
|
+
*
|
|
26
|
+
* On disconnect the client waits 500 ms then reloads the page so the
|
|
27
|
+
* developer always sees a fresh state.
|
|
28
|
+
*/
|
|
29
|
+
export const initHMR = () => {
|
|
30
|
+
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
|
31
|
+
ws = new WebSocket(`${protocol}//${location.host}`);
|
|
32
|
+
|
|
33
|
+
ws.onopen = () => {
|
|
34
|
+
console.log("🔥 HMR connected");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
ws.onmessage = (event) => {
|
|
38
|
+
const data = JSON.parse(event.data);
|
|
39
|
+
|
|
40
|
+
if (data.type === "request-modules") {
|
|
41
|
+
// Send the list of registered module URLs back to the server.
|
|
42
|
+
const moduleUrls = Array.from(modules.keys());
|
|
43
|
+
ws.send(
|
|
44
|
+
JSON.stringify({ type: "register-modules", modules: moduleUrls }),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (data.type === "hmr") {
|
|
49
|
+
handleHMR(data);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (data.type === "reload") {
|
|
53
|
+
console.log("🔄 Full reload triggered");
|
|
54
|
+
window.location.reload();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
ws.onclose = () => {
|
|
59
|
+
console.log("🔄 HMR disconnected, reloading...");
|
|
60
|
+
setTimeout(() => window.location.reload(), 500);
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Handles an individual HMR update from the server.
|
|
66
|
+
*
|
|
67
|
+
* Three change types are supported:
|
|
68
|
+
*
|
|
69
|
+
* - `css` — hot-reloads matching `<link>` stylesheets by appending a cache-
|
|
70
|
+
* busting timestamp to their `href`, avoiding a full page reload.
|
|
71
|
+
* - `exports-changed` — the module's public API changed in a way that cannot
|
|
72
|
+
* be patched in-place; triggers a full reload.
|
|
73
|
+
* - anything else — re-imports the changed module via a dynamic `import()` and
|
|
74
|
+
* dispatches an `hmr:update` CustomEvent so components can react. Falls back
|
|
75
|
+
* to a full reload if the import fails.
|
|
76
|
+
*
|
|
77
|
+
* @param {{ file: string, changeType: string }} data - The HMR payload from the server.
|
|
78
|
+
* @param {string} data.file - Relative path of the changed file.
|
|
79
|
+
* @param {string} data.changeType - The kind of change (`"css"`,
|
|
80
|
+
* `"exports-changed"`, or any other string for a JS hot-swap).
|
|
81
|
+
*/
|
|
82
|
+
const handleHMR = (data) => {
|
|
83
|
+
const { file, changeType } = data;
|
|
84
|
+
|
|
85
|
+
if (changeType === "css") {
|
|
86
|
+
// Reload CSS without a full page refresh by replacing each matching <link>
|
|
87
|
+
// element with a clone that has a cache-busting timestamp in its href.
|
|
88
|
+
const links = document.querySelectorAll(
|
|
89
|
+
`link[href*="${file.replace(/\\/g, "/")}"]`,
|
|
90
|
+
);
|
|
91
|
+
links.forEach((link) => {
|
|
92
|
+
const newLink = link.cloneNode();
|
|
93
|
+
newLink.href = link.href.split("?")[0] + "?t=" + Date.now();
|
|
94
|
+
link.parentNode.replaceChild(newLink, link);
|
|
95
|
+
});
|
|
96
|
+
console.log(`🎨 Hot reloaded CSS: ${file}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (changeType === "exports-changed") {
|
|
101
|
+
console.log(`📦 Exports changed in ${file}, reloading...`);
|
|
102
|
+
window.location.reload();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// JS code change — attempt a hot swap via dynamic import.
|
|
107
|
+
console.log(`🔥 Hot reloading: ${file}`);
|
|
108
|
+
const url = `/${file}?t=${Date.now()}`;
|
|
109
|
+
import(url)
|
|
110
|
+
.then((module) => {
|
|
111
|
+
// Notify any listening components that the module was updated.
|
|
112
|
+
window.dispatchEvent(
|
|
113
|
+
new CustomEvent("hmr:update", { detail: { file, module } }),
|
|
114
|
+
);
|
|
115
|
+
})
|
|
116
|
+
.catch((err) => {
|
|
117
|
+
console.error("HMR import failed:", err);
|
|
118
|
+
window.location.reload();
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Registers a module URL in the local HMR module registry so the server can
|
|
124
|
+
* track which modules this client is using.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} url - The URL (path) of the module to register.
|
|
127
|
+
* @param {Object} module - The imported module object.
|
|
128
|
+
*/
|
|
129
|
+
export const registerModule = (url, module) => {
|
|
130
|
+
modules.set(url, module);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns the current WebSocket connection used by the HMR client, or `null`
|
|
135
|
+
* if `initHMR` has not been called yet.
|
|
136
|
+
*
|
|
137
|
+
* @returns {WebSocket|null} The active WebSocket instance.
|
|
138
|
+
*/
|
|
139
|
+
// ✅ Added the getHMR function to match the export in index.js
|
|
140
|
+
export const getHMR = () => ws;
|
|
141
|
+
|
|
142
|
+
export default { initHMR, registerModule, getHMR };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core
|
|
3
|
+
*
|
|
4
|
+
* Public entry point for the FletBox core layer.
|
|
5
|
+
*
|
|
6
|
+
* Re-exports every symbol that consumers of the framework need to bootstrap an
|
|
7
|
+
* application, manage the DOM, use HMR in development, and work with PWA APIs.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link ./runApp.js} - Application mounting and DOM helpers.
|
|
10
|
+
* @see {@link ./App.js} - Minimal single-widget mount helper.
|
|
11
|
+
* @see {@link ./hmr-client.js} - Hot Module Replacement WebSocket client.
|
|
12
|
+
* @see {@link ./pwa.js} - Progressive Web App install / update / remove utilities.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Application bootstrap and DOM placement helpers
|
|
16
|
+
export {
|
|
17
|
+
runApp,
|
|
18
|
+
createApp,
|
|
19
|
+
insertBy,
|
|
20
|
+
prependBy,
|
|
21
|
+
insertBefore,
|
|
22
|
+
insertAfter,
|
|
23
|
+
replaceBy,
|
|
24
|
+
mountAll,
|
|
25
|
+
} from "./runApp.js";
|
|
26
|
+
|
|
27
|
+
// Minimal single-widget mount helper
|
|
28
|
+
export { App } from "./App.js";
|
|
29
|
+
|
|
30
|
+
// HMR client (development only)
|
|
31
|
+
export { initHMR, getHMR } from "./hmr-client.js";
|
|
32
|
+
|
|
33
|
+
// PWA utilities
|
|
34
|
+
export { installPWA, updatePWA, removePWA, isPWAInstalled } from "./pwa.js";
|