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,263 @@
|
|
|
1
|
+
// components/flet-box/utils/navigation.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds full navigation capabilities to a widget:
|
|
5
|
+
* - parent (go up)
|
|
6
|
+
* - children (go down, by index)
|
|
7
|
+
* - findById (search by id)
|
|
8
|
+
* - siblings (siblings)
|
|
9
|
+
* - tree (tree view)
|
|
10
|
+
* - path (full path to the root)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const navigated = new WeakSet();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Augments a widget object with a full tree-navigation API.
|
|
17
|
+
*
|
|
18
|
+
* The following properties and methods are added (all non-destructive —
|
|
19
|
+
* already-present members are left untouched):
|
|
20
|
+
*
|
|
21
|
+
* **Traversal properties** (all enumerable, computed on access):
|
|
22
|
+
* - `parent` — direct parent widget (readable/writable)
|
|
23
|
+
* - `children` — array of direct children (readable/writable)
|
|
24
|
+
* - `firstChild` — first element of `children`, or `null`
|
|
25
|
+
* - `lastChild` — last element of `children`, or `null`
|
|
26
|
+
* - `siblings` — all siblings excluding this widget
|
|
27
|
+
* - `nextSibling` — next sibling in parent's children list, or `null`
|
|
28
|
+
* - `prevSibling` — previous sibling in parent's children list, or `null`
|
|
29
|
+
* - `index` — zero-based position among siblings (`-1` if no parent)
|
|
30
|
+
* - `root` — topmost ancestor with no parent
|
|
31
|
+
* - `path` — array of `{name, id, index}` objects from root to this widget
|
|
32
|
+
* - `depth` — distance from this widget to the root (0 = root)
|
|
33
|
+
* - `tree` — ASCII tree string of the entire sub-tree (useful for debugging)
|
|
34
|
+
*
|
|
35
|
+
* **Search methods**:
|
|
36
|
+
* - `findById(id)` — depth-first search for a descendant by `id`
|
|
37
|
+
* - `findAll(widgetName)` — collects all descendants whose `_widgetName` matches
|
|
38
|
+
*
|
|
39
|
+
* Already-navigated widgets (tracked by a `WeakSet`) are returned unchanged,
|
|
40
|
+
* making the function safe to call multiple times on the same object.
|
|
41
|
+
*
|
|
42
|
+
* @param {Object} widget - The widget instance to augment.
|
|
43
|
+
* @returns {Object} The same `widget` reference, now enriched with navigation.
|
|
44
|
+
*/
|
|
45
|
+
export const addNavigation = (widget) => {
|
|
46
|
+
if (!widget || navigated.has(widget)) return widget;
|
|
47
|
+
navigated.add(widget);
|
|
48
|
+
|
|
49
|
+
// ========================================================================
|
|
50
|
+
// 1. PARENT (you already have it in parentable, we include it here too)
|
|
51
|
+
// ========================================================================
|
|
52
|
+
if (!widget.hasOwnProperty("parent")) {
|
|
53
|
+
let _parent = null;
|
|
54
|
+
Object.defineProperty(widget, "parent", {
|
|
55
|
+
get: () => _parent,
|
|
56
|
+
set: (p) => {
|
|
57
|
+
_parent = p;
|
|
58
|
+
},
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ========================================================================
|
|
65
|
+
// 2. CHILDREN (array of direct children)
|
|
66
|
+
// ========================================================================
|
|
67
|
+
if (!widget._children) {
|
|
68
|
+
widget._children = [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Object.defineProperty(widget, "children", {
|
|
72
|
+
get: () => widget._children,
|
|
73
|
+
set: (arr) => {
|
|
74
|
+
widget._children = arr;
|
|
75
|
+
},
|
|
76
|
+
enumerable: true,
|
|
77
|
+
configurable: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ========================================================================
|
|
81
|
+
// 3. FIRST CHILD / LAST CHILD (shortcuts)
|
|
82
|
+
// ========================================================================
|
|
83
|
+
Object.defineProperty(widget, "firstChild", {
|
|
84
|
+
get: () => widget._children[0] || null,
|
|
85
|
+
enumerable: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
Object.defineProperty(widget, "lastChild", {
|
|
89
|
+
get: () => widget._children[widget._children.length - 1] || null,
|
|
90
|
+
enumerable: true,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ========================================================================
|
|
94
|
+
// 4. SIBLINGS (excluding itself)
|
|
95
|
+
// ========================================================================
|
|
96
|
+
Object.defineProperty(widget, "siblings", {
|
|
97
|
+
get: () => {
|
|
98
|
+
if (!widget.parent || !widget.parent._children) return [];
|
|
99
|
+
return widget.parent._children.filter((c) => c !== widget);
|
|
100
|
+
},
|
|
101
|
+
enumerable: true,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// ========================================================================
|
|
105
|
+
// 5. NEXT SIBLING / PREV SIBLING
|
|
106
|
+
// ========================================================================
|
|
107
|
+
Object.defineProperty(widget, "nextSibling", {
|
|
108
|
+
get: () => {
|
|
109
|
+
if (!widget.parent) return null;
|
|
110
|
+
const idx = widget.parent._children.indexOf(widget);
|
|
111
|
+
return widget.parent._children[idx + 1] || null;
|
|
112
|
+
},
|
|
113
|
+
enumerable: true,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
Object.defineProperty(widget, "prevSibling", {
|
|
117
|
+
get: () => {
|
|
118
|
+
if (!widget.parent) return null;
|
|
119
|
+
const idx = widget.parent._children.indexOf(widget);
|
|
120
|
+
return widget.parent._children[idx - 1] || null;
|
|
121
|
+
},
|
|
122
|
+
enumerable: true,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ========================================================================
|
|
126
|
+
// 6. INDEX (position among siblings)
|
|
127
|
+
// ========================================================================
|
|
128
|
+
Object.defineProperty(widget, "index", {
|
|
129
|
+
get: () => {
|
|
130
|
+
if (!widget.parent) return -1;
|
|
131
|
+
return widget.parent._children.indexOf(widget);
|
|
132
|
+
},
|
|
133
|
+
enumerable: true,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ========================================================================
|
|
137
|
+
// 7. ROOT (go up to the root)
|
|
138
|
+
// ========================================================================
|
|
139
|
+
Object.defineProperty(widget, "root", {
|
|
140
|
+
get: () => {
|
|
141
|
+
let current = widget;
|
|
142
|
+
while (current.parent) {
|
|
143
|
+
current = current.parent;
|
|
144
|
+
}
|
|
145
|
+
return current;
|
|
146
|
+
},
|
|
147
|
+
enumerable: true,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ========================================================================
|
|
151
|
+
// 8. PATH (path from the root to the widget)
|
|
152
|
+
// ========================================================================
|
|
153
|
+
Object.defineProperty(widget, "path", {
|
|
154
|
+
get: () => {
|
|
155
|
+
const path = [];
|
|
156
|
+
let current = widget;
|
|
157
|
+
while (current) {
|
|
158
|
+
path.unshift({
|
|
159
|
+
name: current._widgetName || current.tagName?.toLowerCase(),
|
|
160
|
+
id: current.id || null,
|
|
161
|
+
index: current.index,
|
|
162
|
+
});
|
|
163
|
+
current = current.parent;
|
|
164
|
+
}
|
|
165
|
+
return path;
|
|
166
|
+
},
|
|
167
|
+
enumerable: true,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// ========================================================================
|
|
171
|
+
// 9. DEPTH (depth in the tree)
|
|
172
|
+
// ========================================================================
|
|
173
|
+
Object.defineProperty(widget, "depth", {
|
|
174
|
+
get: () => {
|
|
175
|
+
let depth = 0;
|
|
176
|
+
let current = widget.parent;
|
|
177
|
+
while (current) {
|
|
178
|
+
depth++;
|
|
179
|
+
current = current.parent;
|
|
180
|
+
}
|
|
181
|
+
return depth;
|
|
182
|
+
},
|
|
183
|
+
enumerable: true,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ========================================================================
|
|
187
|
+
// 10. findById (downward search by id)
|
|
188
|
+
// ========================================================================
|
|
189
|
+
widget.findById = function (id) {
|
|
190
|
+
if (this.id === id) return this;
|
|
191
|
+
for (const child of this._children || []) {
|
|
192
|
+
if (child.findById) {
|
|
193
|
+
const found = child.findById(id);
|
|
194
|
+
if (found) return found;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// ========================================================================
|
|
201
|
+
// 11. findAll (downward search by widget name)
|
|
202
|
+
// ========================================================================
|
|
203
|
+
widget.findAll = function (widgetName) {
|
|
204
|
+
const results = [];
|
|
205
|
+
if (this._widgetName === widgetName) results.push(this);
|
|
206
|
+
for (const child of this._children || []) {
|
|
207
|
+
if (child.findAll) {
|
|
208
|
+
results.push(...child.findAll(widgetName));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return results;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// ========================================================================
|
|
215
|
+
// 12. TREE (tree view for debugging)
|
|
216
|
+
// ========================================================================
|
|
217
|
+
widget._generateTree = function (indent = 0, isLast = true, prefix = "") {
|
|
218
|
+
const connector = indent === 0 ? "" : isLast ? "└── " : "├── ";
|
|
219
|
+
const continuation = indent === 0 ? "" : isLast ? " " : "│ ";
|
|
220
|
+
|
|
221
|
+
const name = this._widgetName || this.tagName?.toLowerCase() || "widget";
|
|
222
|
+
const id = this.id ? ` #${this.id}` : "";
|
|
223
|
+
const props = this._getTreeProps ? this._getTreeProps() : "";
|
|
224
|
+
|
|
225
|
+
let tree = `${prefix}${connector}${name}${id}${props}\n`;
|
|
226
|
+
|
|
227
|
+
if (this._children && this._children.length > 0) {
|
|
228
|
+
this._children.forEach((child, index) => {
|
|
229
|
+
const childIsLast = index === this._children.length - 1;
|
|
230
|
+
if (child._generateTree) {
|
|
231
|
+
tree += child._generateTree(
|
|
232
|
+
indent + 1,
|
|
233
|
+
childIsLast,
|
|
234
|
+
prefix + continuation,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return tree;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
Object.defineProperty(widget, "tree", {
|
|
244
|
+
get: () => widget._generateTree(),
|
|
245
|
+
enumerable: true,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// ========================================================================
|
|
249
|
+
// 13. _getTreeProps (overridable by each widget to display props)
|
|
250
|
+
// ========================================================================
|
|
251
|
+
widget._getTreeProps = function () {
|
|
252
|
+
const name = this._widgetName || "";
|
|
253
|
+
const propsMap = {
|
|
254
|
+
Text: () => (this.textContent ? ` [${this.textContent}]` : ""),
|
|
255
|
+
Image: () => (this.src ? ` [${this.src.split("/").pop()}]` : ""),
|
|
256
|
+
Container: () => "",
|
|
257
|
+
GestureDetector: () => "",
|
|
258
|
+
};
|
|
259
|
+
return propsMap[name] ? propsMap[name]() : "";
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return widget;
|
|
263
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file scriptInjector.js
|
|
3
|
+
* @description
|
|
4
|
+
* Shared, idempotent external `<script>` loading.
|
|
5
|
+
*
|
|
6
|
+
* Like `@keyframes`, a third-party SDK script is global to the document: it must
|
|
7
|
+
* be loaded exactly once even if several widgets request it (for example a page
|
|
8
|
+
* with multiple ad units sharing one vendor SDK). Widgets therefore must not
|
|
9
|
+
* each append their own `<script>` tag — this module centralises the injection
|
|
10
|
+
* and returns the same promise for the same URL.
|
|
11
|
+
*
|
|
12
|
+
* @module scriptInjector
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** URL → Promise<HTMLScriptElement> for scripts already requested. */
|
|
16
|
+
const pending = new Map();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Loads an external script once and resolves when it is ready.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} src - Absolute script URL.
|
|
22
|
+
* @param {Object} [options]
|
|
23
|
+
* @param {boolean} [options.async=true] - Set the `async` attribute.
|
|
24
|
+
* @param {string} [options.crossOrigin] - Value for the `crossorigin` attribute.
|
|
25
|
+
* @param {Record<string,string>} [options.attrs] - Extra attributes to stamp on
|
|
26
|
+
* the tag (e.g. `{ "data-flet-adsense": "ca-pub-…" }`).
|
|
27
|
+
* @returns {Promise<HTMLScriptElement|null>} Resolves with the script element,
|
|
28
|
+
* or `null` outside a DOM environment. Rejects when the script fails to load.
|
|
29
|
+
*/
|
|
30
|
+
export function injectScript(src, options = {}) {
|
|
31
|
+
if (typeof document === "undefined") return Promise.resolve(null);
|
|
32
|
+
if (pending.has(src)) return pending.get(src);
|
|
33
|
+
|
|
34
|
+
const promise = new Promise((resolve, reject) => {
|
|
35
|
+
const existing = document.querySelector(`script[src="${src}"]`);
|
|
36
|
+
if (existing) {
|
|
37
|
+
// A script already in the document was not created by this module (our
|
|
38
|
+
// own tags are cached in `pending`), so it comes from the host page. We
|
|
39
|
+
// cannot observe its load state retroactively, and command-queue SDKs
|
|
40
|
+
// (AdSense) tolerate pushes before the script finishes loading, so treat
|
|
41
|
+
// it as ready instead of hanging on an already-fired `load` event.
|
|
42
|
+
resolve(existing);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const script = document.createElement("script");
|
|
47
|
+
script.src = src;
|
|
48
|
+
script.async = options.async !== false;
|
|
49
|
+
if (options.crossOrigin) script.crossOrigin = options.crossOrigin;
|
|
50
|
+
for (const [name, value] of Object.entries(options.attrs || {})) {
|
|
51
|
+
script.setAttribute(name, value);
|
|
52
|
+
}
|
|
53
|
+
script.addEventListener(
|
|
54
|
+
"load",
|
|
55
|
+
() => {
|
|
56
|
+
script.dataset.fletLoaded = "true";
|
|
57
|
+
resolve(script);
|
|
58
|
+
},
|
|
59
|
+
{ once: true },
|
|
60
|
+
);
|
|
61
|
+
script.addEventListener(
|
|
62
|
+
"error",
|
|
63
|
+
() => reject(new Error(`Failed to load script: ${src}`)),
|
|
64
|
+
{ once: true },
|
|
65
|
+
);
|
|
66
|
+
document.head.appendChild(script);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
pending.set(src, promise);
|
|
70
|
+
return promise;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default injectScript;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// utils/stackPosition.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reads `top`, `right`, `bottom`, and `left` from `props` and stores them as
|
|
5
|
+
* `dataset` attributes on the widget element so that a parent `Stack` widget
|
|
6
|
+
* can position this child absolutely at render time.
|
|
7
|
+
*
|
|
8
|
+
* @param {HTMLElement} widget - The widget element to annotate.
|
|
9
|
+
* @param {Object} props - Props object that may contain `top`, `right`, `bottom`, and/or `left`.
|
|
10
|
+
* @param {number|string} [props.top] - Distance from the top edge (px or CSS string).
|
|
11
|
+
* @param {number|string} [props.right] - Distance from the right edge (px or CSS string).
|
|
12
|
+
* @param {number|string} [props.bottom] - Distance from the bottom edge (px or CSS string).
|
|
13
|
+
* @param {number|string} [props.left] - Distance from the left edge (px or CSS string).
|
|
14
|
+
* @returns {HTMLElement} The same `widget` reference, with dataset attributes set.
|
|
15
|
+
*/
|
|
16
|
+
export const stackPosition = (widget, props) => {
|
|
17
|
+
const top = props.top;
|
|
18
|
+
const right = props.right;
|
|
19
|
+
const bottom = props.bottom;
|
|
20
|
+
const left = props.left;
|
|
21
|
+
|
|
22
|
+
// Store position in dataset so the parent Stack can read it later
|
|
23
|
+
if (top !== undefined) widget.dataset.top = top;
|
|
24
|
+
if (right !== undefined) widget.dataset.right = right;
|
|
25
|
+
if (bottom !== undefined) widget.dataset.bottom = bottom;
|
|
26
|
+
if (left !== undefined) widget.dataset.left = left;
|
|
27
|
+
|
|
28
|
+
return widget;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default stackPosition;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// utils/stopWebRefresh.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Disables common browser refresh gestures so that the FletBox app behaves
|
|
5
|
+
* more like a native application.
|
|
6
|
+
*
|
|
7
|
+
* Three mechanisms are blocked:
|
|
8
|
+
* 1. **Keyboard shortcuts** — F5, Ctrl+R, and Cmd+R are intercepted via a
|
|
9
|
+
* `keydown` listener and their default action is prevented.
|
|
10
|
+
* 2. **Context menu** — Right-click is suppressed on `document.body` to
|
|
11
|
+
* prevent the browser's "Reload" option from appearing.
|
|
12
|
+
* 3. **Pull-to-refresh** (mobile) — A `touchmove` listener prevents the
|
|
13
|
+
* native pull-to-refresh gesture when the page is scrolled to the top and
|
|
14
|
+
* the touch is not inside a scrollable child element.
|
|
15
|
+
*
|
|
16
|
+
* All listeners are attached permanently for the lifetime of the page.
|
|
17
|
+
* Call this function once, typically at app startup.
|
|
18
|
+
*/
|
|
19
|
+
export const stopWebRefresh = () => {
|
|
20
|
+
// 1. Block refresh keys (F5, Ctrl+R, Cmd+R)
|
|
21
|
+
window.addEventListener("keydown", (e) => {
|
|
22
|
+
if (
|
|
23
|
+
e.key === "F5" ||
|
|
24
|
+
(e.ctrlKey && (e.key === "r" || e.key === "R")) ||
|
|
25
|
+
(e.metaKey && (e.key === "r" || e.key === "R"))
|
|
26
|
+
) {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
console.log("🚫 Refresh disabled (keyboard)");
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// 2. Block right-click (context menu)
|
|
33
|
+
document.body.addEventListener("contextmenu", (e) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// 3. Block pull-to-refresh on mobile selectively
|
|
38
|
+
let touchStartY = 0;
|
|
39
|
+
let isAtTop = false;
|
|
40
|
+
|
|
41
|
+
window.addEventListener(
|
|
42
|
+
"touchstart",
|
|
43
|
+
(e) => {
|
|
44
|
+
touchStartY = e.touches[0].clientY;
|
|
45
|
+
// Check if we are at the top of the document
|
|
46
|
+
isAtTop =
|
|
47
|
+
window.scrollY === 0 && document.documentElement.scrollTop === 0;
|
|
48
|
+
},
|
|
49
|
+
{ passive: true },
|
|
50
|
+
); // passive: true improves performance
|
|
51
|
+
|
|
52
|
+
window.addEventListener(
|
|
53
|
+
"touchmove",
|
|
54
|
+
(e) => {
|
|
55
|
+
const deltaY = e.touches[0].clientY - touchStartY;
|
|
56
|
+
// Only prevent if: we are at the top, the movement is downward (deltaY > 0),
|
|
57
|
+
// and the event does not occur inside an element with its own scroll.
|
|
58
|
+
if (isAtTop && deltaY > 0) {
|
|
59
|
+
// Try to block only if the target is not a scrolling element
|
|
60
|
+
let target = e.target;
|
|
61
|
+
while (target && target !== document.body) {
|
|
62
|
+
const overflowY = window.getComputedStyle(target).overflowY;
|
|
63
|
+
if (overflowY === "auto" || overflowY === "scroll") {
|
|
64
|
+
// The touch is inside a scrolling container, allow movement
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
target = target.parentElement;
|
|
68
|
+
}
|
|
69
|
+
// If we get here, there's no internal scroll in the way → block pull-to-refresh
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{ passive: false },
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default stopWebRefresh;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file styleInjector.js
|
|
3
|
+
* @description
|
|
4
|
+
* Shared, idempotent `@keyframes` injection.
|
|
5
|
+
*
|
|
6
|
+
* Keyframes are global by nature (CSS cannot scope them to an element), so
|
|
7
|
+
* widgets must not each append their own `<style>` tag to `document.head` —
|
|
8
|
+
* that duplicates tags and makes ownership unclear. This module centralises the
|
|
9
|
+
* injection and guarantees each animation name is defined exactly once.
|
|
10
|
+
*
|
|
11
|
+
* @module styleInjector
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Animation names already injected in this document. */
|
|
15
|
+
const injected = new Set();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Injects `@keyframes <name> { <frames> }` into `<head>` the first time the
|
|
19
|
+
* given name is requested. Subsequent calls for the same name are no-ops.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} name - The animation name (e.g. `"skeleton-pulse"`).
|
|
22
|
+
* @param {string} frames - The keyframe body, without the `@keyframes` wrapper
|
|
23
|
+
* (e.g. `"0% { opacity: 1; } 100% { opacity: 0.5; }"`).
|
|
24
|
+
* @param {string} [styleId] - Optional `id` for the injected `<style>` tag.
|
|
25
|
+
* Older docs/widgets referenced ids like `#skeleton-styles` / `#indeterminate-progress-style`.
|
|
26
|
+
* @returns {void}
|
|
27
|
+
*/
|
|
28
|
+
export function injectKeyframes(name, frames, styleId) {
|
|
29
|
+
if (injected.has(name) || typeof document === "undefined") return;
|
|
30
|
+
injected.add(name);
|
|
31
|
+
const style = document.createElement("style");
|
|
32
|
+
style.setAttribute("data-flet-keyframes", name);
|
|
33
|
+
if (styleId) style.setAttribute("id", styleId);
|
|
34
|
+
style.textContent = `@keyframes ${name} { ${frames} }`;
|
|
35
|
+
document.head.appendChild(style);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default injectKeyframes;
|