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,284 @@
|
|
|
1
|
+
// core/warnings.js
|
|
2
|
+
// FletBox Warning System – Elegant, minimal, helpful.
|
|
3
|
+
|
|
4
|
+
// 🚀 Gate of entorno: desactiva warnings in producción
|
|
5
|
+
const IS_DEV = typeof FLETBOX_DEV !== 'undefined' ? FLETBOX_DEV : true;
|
|
6
|
+
|
|
7
|
+
const suggestionCache = new Map();
|
|
8
|
+
let propNames = [];
|
|
9
|
+
|
|
10
|
+
export function setPropNames(names) {
|
|
11
|
+
propNames = names;
|
|
12
|
+
try { if (typeof window !== 'undefined') window.__FLETBOX_PROP_NAMES = names; } catch(e){}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getSuggestions(name, limit = 3) {
|
|
16
|
+
const key = `${name}_${limit}`;
|
|
17
|
+
if (suggestionCache.has(key)) return suggestionCache.get(key);
|
|
18
|
+
|
|
19
|
+
const input = name.toLowerCase();
|
|
20
|
+
const scored = propNames.map((p) => {
|
|
21
|
+
const lower = p.toLowerCase();
|
|
22
|
+
let score = 0;
|
|
23
|
+
if (lower === input) score = 100;
|
|
24
|
+
else if (lower.startsWith(input)) score = 80;
|
|
25
|
+
else if (input.startsWith(lower)) score = 70;
|
|
26
|
+
else if (lower.includes(input)) score = 50;
|
|
27
|
+
else if (input.includes(lower)) score = 40;
|
|
28
|
+
else {
|
|
29
|
+
const parts = input.split(/(?=[A-Z])/);
|
|
30
|
+
for (const part of parts) {
|
|
31
|
+
if (lower.includes(part)) score += 10;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { prop: p, score };
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
scored.sort((a, b) => b.score - a.score);
|
|
38
|
+
const result = scored
|
|
39
|
+
.filter((s) => s.score > 0 && s.prop !== name)
|
|
40
|
+
.slice(0, limit)
|
|
41
|
+
.map((s) => s.prop);
|
|
42
|
+
suggestionCache.set(key, result);
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getCallLocation() {
|
|
47
|
+
const stack = new Error().stack;
|
|
48
|
+
const lines = stack.split("\n");
|
|
49
|
+
const skip = [
|
|
50
|
+
"warnings.js",
|
|
51
|
+
"translateProps.js",
|
|
52
|
+
"processProps.js",
|
|
53
|
+
"widgetFactory.js",
|
|
54
|
+
"createWidget.js",
|
|
55
|
+
"assignProps.js",
|
|
56
|
+
"addChildren.js",
|
|
57
|
+
"makeParentable.js",
|
|
58
|
+
"effects.js",
|
|
59
|
+
"lifecycle.js",
|
|
60
|
+
"reactivity.js",
|
|
61
|
+
"stackPosition.js",
|
|
62
|
+
"tools.js",
|
|
63
|
+
"Container.js",
|
|
64
|
+
"Row.js",
|
|
65
|
+
"Column.js",
|
|
66
|
+
"Stack.js",
|
|
67
|
+
"Text.js",
|
|
68
|
+
"Icon.js",
|
|
69
|
+
"Button.js",
|
|
70
|
+
"Image.js",
|
|
71
|
+
"Card.js",
|
|
72
|
+
"ListTile.js",
|
|
73
|
+
"ListView.js",
|
|
74
|
+
"GridView.js",
|
|
75
|
+
"ProgressBar.js",
|
|
76
|
+
"Rating.js",
|
|
77
|
+
"Chip.js",
|
|
78
|
+
"Badge.js",
|
|
79
|
+
"Divider.js",
|
|
80
|
+
"Accordion.js",
|
|
81
|
+
"Input.js",
|
|
82
|
+
"Radio.js",
|
|
83
|
+
"Switch.js",
|
|
84
|
+
"Checkbox.js",
|
|
85
|
+
"Dropdown.js",
|
|
86
|
+
"SnackBar.js",
|
|
87
|
+
"Modal.js",
|
|
88
|
+
"BottomSheet.js",
|
|
89
|
+
"AlertDialog.js",
|
|
90
|
+
"FloatingActionButton.js",
|
|
91
|
+
"CodeViewer.js",
|
|
92
|
+
"Inspector.js",
|
|
93
|
+
"DraggBox.js",
|
|
94
|
+
"DroppBox.js",
|
|
95
|
+
"DataTable.js",
|
|
96
|
+
"Carousel.js",
|
|
97
|
+
"Tooltip.js",
|
|
98
|
+
"Pagination.js",
|
|
99
|
+
"TreeView.js",
|
|
100
|
+
"Stepper.js",
|
|
101
|
+
"Skeleton.js",
|
|
102
|
+
"Chart.js",
|
|
103
|
+
"QRCode.js",
|
|
104
|
+
"Slider.js",
|
|
105
|
+
"InstallButton.js",
|
|
106
|
+
"Video.js",
|
|
107
|
+
"Audio.js",
|
|
108
|
+
"CircularBar.js",
|
|
109
|
+
"CircularChart.js",
|
|
110
|
+
"Markdown.js",
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
let shouldSkip = false;
|
|
115
|
+
for (const pattern of skip) {
|
|
116
|
+
if (line.includes(pattern)) {
|
|
117
|
+
shouldSkip = true;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (shouldSkip) continue;
|
|
122
|
+
if (line.includes("<anonymous>") && !line.includes("app.js")) continue;
|
|
123
|
+
const match = line.match(/at\s+(.*):(\d+):(\d+)/);
|
|
124
|
+
if (match) {
|
|
125
|
+
const file = match[1];
|
|
126
|
+
const fileName = file.split("/").pop() || file.split("\\").pop() || file;
|
|
127
|
+
if (file.includes("node_modules")) continue;
|
|
128
|
+
return { file, fileName, line: match[2], column: match[3] };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const line of lines) {
|
|
133
|
+
const match = line.match(/at\s+(.*):(\d+):(\d+)/);
|
|
134
|
+
if (match) {
|
|
135
|
+
const file = match[1];
|
|
136
|
+
const fileName = file.split("/").pop() || file.split("\\").pop() || file;
|
|
137
|
+
if (!fileName.includes("node_modules") && !fileName.includes("core")) {
|
|
138
|
+
return { file, fileName, line: match[2], column: match[3] };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { file: null, fileName: "unknown", line: "?", column: "?" };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function printHeader(label) {
|
|
146
|
+
console.group(
|
|
147
|
+
`%c⚠️ FLETBOX: ${label}`,
|
|
148
|
+
"color: #fbbf24; font-weight: 900; font-size: 18px; background: #1e293b; padding: 6px 12px; border-radius: 6px;",
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function printProp(name, status = "") {
|
|
153
|
+
console.log(
|
|
154
|
+
` %c📦 Prop: %c"${name}"%c ${status}`,
|
|
155
|
+
"color: #94a3b8; font-size: 13px;",
|
|
156
|
+
"color: #f87171; font-weight: 900; font-size: 16px;",
|
|
157
|
+
"color: #94a3b8; font-size: 13px;",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function printLocation(location) {
|
|
162
|
+
if (location.file) {
|
|
163
|
+
console.log(
|
|
164
|
+
` %c📍 Error at: %c${location.fileName}:${location.line}%c (click to go to code)`,
|
|
165
|
+
"color: #94a3b8; font-size: 13px;",
|
|
166
|
+
"color: #f87171; font-weight: 700; font-size: 14px; text-decoration: underline; cursor: pointer;",
|
|
167
|
+
"color: #94a3b8; font-size: 13px;",
|
|
168
|
+
);
|
|
169
|
+
console.log(` 🔗 ${location.file}:${location.line}:${location.column}`);
|
|
170
|
+
} else {
|
|
171
|
+
console.log(
|
|
172
|
+
` %c📍 Error at: %c${location.fileName}:${location.line}`,
|
|
173
|
+
"color: #94a3b8; font-size: 13px;",
|
|
174
|
+
"color: #f87171; font-weight: 700; font-size: 14px;",
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function printSuggestions(suggestions) {
|
|
180
|
+
if (suggestions.length) {
|
|
181
|
+
console.log(
|
|
182
|
+
` %c💡 Did you mean?`,
|
|
183
|
+
"color: #fbbf24; font-weight: 700; font-size: 14px;",
|
|
184
|
+
);
|
|
185
|
+
suggestions.forEach((s) =>
|
|
186
|
+
console.log(
|
|
187
|
+
` %c• "${s}"`,
|
|
188
|
+
"color: #34d399; font-weight: 600; font-size: 14px;",
|
|
189
|
+
),
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
console.log(
|
|
193
|
+
` %c💡 No similar props found. Check spelling.`,
|
|
194
|
+
"color: #94a3b8; font-size: 13px;",
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function printWidget(name) {
|
|
200
|
+
if (name) {
|
|
201
|
+
console.log(
|
|
202
|
+
` %c🧩 Widget: %c${name}`,
|
|
203
|
+
"color: #94a3b8; font-size: 13px;",
|
|
204
|
+
"color: #c084fc; font-weight: 600; font-size: 14px;",
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function printDocs(prop) {
|
|
210
|
+
console.log(
|
|
211
|
+
` %c📚 Docs: %chttps://fletbox.dev/docs/props#${prop.toLowerCase()}`,
|
|
212
|
+
"color: #94a3b8; font-size: 13px;",
|
|
213
|
+
"color: #60a5fa; text-decoration: underline; font-weight: 500; font-size: 13px;",
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function warnUnknownProp(propName, context = {}) {
|
|
218
|
+
if (!IS_DEV) return;
|
|
219
|
+
|
|
220
|
+
const suggestions = getSuggestions(propName);
|
|
221
|
+
const location = getCallLocation();
|
|
222
|
+
|
|
223
|
+
printHeader("UNKNOWN PROP");
|
|
224
|
+
printProp(propName, "(not in database)");
|
|
225
|
+
printLocation(location);
|
|
226
|
+
printSuggestions(suggestions);
|
|
227
|
+
printWidget(context.widgetName);
|
|
228
|
+
printDocs(propName);
|
|
229
|
+
console.groupEnd();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function warnDeprecatedProp(propName, replacement = null, context = {}) {
|
|
233
|
+
if (!IS_DEV) return;
|
|
234
|
+
|
|
235
|
+
const location = getCallLocation();
|
|
236
|
+
|
|
237
|
+
printHeader("DEPRECATED PROP");
|
|
238
|
+
printProp(propName, "is deprecated");
|
|
239
|
+
if (replacement) {
|
|
240
|
+
console.log(
|
|
241
|
+
` %c🔄 Use: %c"${replacement}"%c instead`,
|
|
242
|
+
"color: #94a3b8; font-size: 13px;",
|
|
243
|
+
"color: #34d399; font-weight: 700; font-size: 16px;",
|
|
244
|
+
"color: #94a3b8; font-size: 13px;",
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
printLocation(location);
|
|
248
|
+
printWidget(context.widgetName);
|
|
249
|
+
console.groupEnd();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function warnInvalidPropType(propName, expected, actual, context = {}) {
|
|
253
|
+
if (!IS_DEV) return;
|
|
254
|
+
|
|
255
|
+
const location = getCallLocation();
|
|
256
|
+
|
|
257
|
+
printHeader("INVALID PROP TYPE");
|
|
258
|
+
printProp(propName, "");
|
|
259
|
+
console.log(
|
|
260
|
+
` %c📊 Expected: %c${expected}`,
|
|
261
|
+
"color: #94a3b8; font-size: 13px;",
|
|
262
|
+
"color: #34d399; font-weight: 600; font-size: 14px;",
|
|
263
|
+
);
|
|
264
|
+
console.log(
|
|
265
|
+
` %c📊 Got: %c${typeof actual}`,
|
|
266
|
+
"color: #94a3b8; font-size: 13px;",
|
|
267
|
+
"color: #f87171; font-weight: 600; font-size: 14px;",
|
|
268
|
+
);
|
|
269
|
+
printLocation(location);
|
|
270
|
+
printWidget(context.widgetName);
|
|
271
|
+
console.groupEnd();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function clearSuggestionCache() {
|
|
275
|
+
suggestionCache.clear();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export default {
|
|
279
|
+
warnUnknownProp,
|
|
280
|
+
warnDeprecatedProp,
|
|
281
|
+
warnInvalidPropType,
|
|
282
|
+
clearSuggestionCache,
|
|
283
|
+
setPropNames,
|
|
284
|
+
};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// core/widgetFactory.js
|
|
2
|
+
import { createWidget } from "./createWidget.js";
|
|
3
|
+
import { makeParentable } from "./makeParentable.js";
|
|
4
|
+
import { addLifecycle } from "./lifecycle.js";
|
|
5
|
+
import { applyEffects } from "./effects.js";
|
|
6
|
+
import { makeReactive } from "./reactivity.js";
|
|
7
|
+
import { processProps } from "./processProps.js";
|
|
8
|
+
import { assignProps } from "./assignProps.js";
|
|
9
|
+
import { stackPosition } from "./stackPosition.js";
|
|
10
|
+
import { addChildren, removePropChildren } from "./addChildren.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Main widget factory. Creates a fully functional DOM widget with reactivity,
|
|
14
|
+
* lifecycle hooks, prop processing, and child management.
|
|
15
|
+
*
|
|
16
|
+
* Accepts either a tag string or a config object (with an optional `tag` key).
|
|
17
|
+
* When both `child` and `children` are provided, `children` takes precedence.
|
|
18
|
+
*
|
|
19
|
+
* @param {string|Object} tag - HTML tag name (e.g. `'div'`) or a config object
|
|
20
|
+
* whose `tag` property specifies the element type. Defaults to `'div'` when
|
|
21
|
+
* the value is not a string.
|
|
22
|
+
* @param {Object} [customProps={}] - Additional props merged on top of any
|
|
23
|
+
* props already present in a config object passed as `tag`.
|
|
24
|
+
* @returns {HTMLElement} The constructed widget element, extended with reactive
|
|
25
|
+
* accessors, lifecycle hooks, and `update` / `getProps` methods.
|
|
26
|
+
*/
|
|
27
|
+
export function WidgetFactory(tag, customProps = {}) {
|
|
28
|
+
// ============================================================
|
|
29
|
+
// 1. NORMALIZE TAG AND PROPS
|
|
30
|
+
// ============================================================
|
|
31
|
+
let finalTag = tag;
|
|
32
|
+
let finalProps = customProps;
|
|
33
|
+
|
|
34
|
+
if (typeof tag === "object" && tag !== null) {
|
|
35
|
+
if (tag.tag) {
|
|
36
|
+
finalTag = tag.tag;
|
|
37
|
+
finalProps = { ...tag, ...customProps };
|
|
38
|
+
delete finalProps.tag;
|
|
39
|
+
} else {
|
|
40
|
+
finalTag = "div";
|
|
41
|
+
finalProps = { ...tag, ...customProps };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (typeof finalTag !== "string") {
|
|
46
|
+
finalTag = "div";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ------------------------------------------------------------
|
|
50
|
+
// Reserved prop: `widgetName`
|
|
51
|
+
// ------------------------------------------------------------
|
|
52
|
+
// Labels the widget with its real FletBox name (e.g. "QRCode", "Button")
|
|
53
|
+
// instead of the capitalized HTML tag. Consumed by Inspector, navigation
|
|
54
|
+
// helpers and warnings; it never reaches the DOM. When omitted it falls
|
|
55
|
+
// back to the capitalized tag, so bare factory calls keep working.
|
|
56
|
+
//
|
|
57
|
+
// NOTE: the key is `widgetName`, not `name`, because `name` is a legitimate
|
|
58
|
+
// public prop of Icon/Avatar (Icon({ name: "add" })).
|
|
59
|
+
let explicitName = null;
|
|
60
|
+
if (finalProps && typeof finalProps === "object" && "widgetName" in finalProps) {
|
|
61
|
+
if (typeof finalProps.widgetName === "string" && finalProps.widgetName) {
|
|
62
|
+
explicitName = finalProps.widgetName;
|
|
63
|
+
}
|
|
64
|
+
finalProps = { ...finalProps };
|
|
65
|
+
delete finalProps.widgetName;
|
|
66
|
+
}
|
|
67
|
+
const widgetName =
|
|
68
|
+
explicitName || finalTag.charAt(0).toUpperCase() + finalTag.slice(1);
|
|
69
|
+
|
|
70
|
+
// ============================================================
|
|
71
|
+
// 2. NORMALIZE child/children IN INITIAL PROPS
|
|
72
|
+
// ============================================================
|
|
73
|
+
// When both are present, prefer `children` (expected behavior).
|
|
74
|
+
if (
|
|
75
|
+
finalProps.child !== undefined &&
|
|
76
|
+
finalProps.children !== undefined
|
|
77
|
+
) {
|
|
78
|
+
delete finalProps.child;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================
|
|
82
|
+
// 3. CREATE BASE WIDGET
|
|
83
|
+
// ============================================================
|
|
84
|
+
const widget = createWidget(finalTag, {});
|
|
85
|
+
widget._widgetName = widgetName;
|
|
86
|
+
widget._updating = false;
|
|
87
|
+
widget._events = [];
|
|
88
|
+
|
|
89
|
+
makeParentable(widget);
|
|
90
|
+
addLifecycle(widget);
|
|
91
|
+
|
|
92
|
+
widget._originalProps = { ...finalProps };
|
|
93
|
+
|
|
94
|
+
// ============================================================
|
|
95
|
+
// 4. APPLY INITIAL PROPS
|
|
96
|
+
// ============================================================
|
|
97
|
+
const initialProps = processProps(finalProps, finalTag, widgetName);
|
|
98
|
+
assignProps(widget, initialProps);
|
|
99
|
+
stackPosition(widget, finalProps);
|
|
100
|
+
|
|
101
|
+
if (finalProps.child !== undefined) {
|
|
102
|
+
addChildren(widget, finalProps.child);
|
|
103
|
+
}
|
|
104
|
+
if (finalProps.children !== undefined) {
|
|
105
|
+
addChildren(widget, finalProps.children);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
applyEffects(widget);
|
|
109
|
+
|
|
110
|
+
if (finalProps.ref && typeof finalProps.ref === "function") {
|
|
111
|
+
finalProps.ref(widget);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================
|
|
115
|
+
// 5. update METHOD WITH NORMALIZATION
|
|
116
|
+
// ============================================================
|
|
117
|
+
let updateDepth = 0;
|
|
118
|
+
const MAX_UPDATE_DEPTH = 10;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Updates the widget with new props. Merges the new props into the stored
|
|
122
|
+
* original props and re-applies styles, attributes, children, and effects.
|
|
123
|
+
*
|
|
124
|
+
* Prevents infinite loops via a recursion depth guard (`MAX_UPDATE_DEPTH`).
|
|
125
|
+
* When both `child` and `children` are passed, `children` wins (same rule as
|
|
126
|
+
* the constructor).
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} [newProps={}] - Partial or full prop set to apply.
|
|
129
|
+
* @returns {HTMLElement} The widget itself (for chaining).
|
|
130
|
+
*/
|
|
131
|
+
widget.update = (newProps = {}) => {
|
|
132
|
+
if (updateDepth >= MAX_UPDATE_DEPTH) {
|
|
133
|
+
console.warn("⚠️ [WidgetFactory] Max update depth reached, skipping");
|
|
134
|
+
return widget;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
updateDepth++;
|
|
138
|
+
try {
|
|
139
|
+
// Normalize: never allow child AND children simultaneously
|
|
140
|
+
const normalized = { ...newProps };
|
|
141
|
+
// Reserved: `widgetName` labels the widget for Inspector and must never
|
|
142
|
+
// persist in props. It may re-label the widget on update, nothing more.
|
|
143
|
+
if ("widgetName" in normalized) {
|
|
144
|
+
const nextName = normalized.widgetName;
|
|
145
|
+
if (typeof nextName === "string" && nextName) widget._widgetName = nextName;
|
|
146
|
+
delete normalized.widgetName;
|
|
147
|
+
}
|
|
148
|
+
const hasChild = "child" in normalized;
|
|
149
|
+
const hasChildren = "children" in normalized;
|
|
150
|
+
|
|
151
|
+
if (hasChild || hasChildren) {
|
|
152
|
+
delete widget._originalProps.child;
|
|
153
|
+
delete widget._originalProps.children;
|
|
154
|
+
if (hasChildren) delete normalized.child;
|
|
155
|
+
else if (hasChild) delete normalized.children;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const merged = { ...widget._originalProps, ...normalized };
|
|
159
|
+
widget._originalProps = merged;
|
|
160
|
+
|
|
161
|
+
const processed = processProps(merged, finalTag, widgetName);
|
|
162
|
+
assignProps(widget, processed);
|
|
163
|
+
stackPosition(widget, merged);
|
|
164
|
+
|
|
165
|
+
if (hasChild || hasChildren) {
|
|
166
|
+
removePropChildren(widget);
|
|
167
|
+
if (hasChild) addChildren(widget, newProps.child);
|
|
168
|
+
if (hasChildren) addChildren(widget, newProps.children);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
applyEffects(widget);
|
|
172
|
+
|
|
173
|
+
if (newProps.ref && typeof newProps.ref === "function") {
|
|
174
|
+
newProps.ref(widget);
|
|
175
|
+
}
|
|
176
|
+
} finally {
|
|
177
|
+
updateDepth--;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return widget;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Returns a shallow copy of the widget's current stored props.
|
|
185
|
+
*
|
|
186
|
+
* @returns {Object} Copy of `_originalProps`.
|
|
187
|
+
*/
|
|
188
|
+
widget.getProps = () => ({ ...widget._originalProps });
|
|
189
|
+
|
|
190
|
+
// ============================================================
|
|
191
|
+
// 6. REACTIVITY
|
|
192
|
+
// ============================================================
|
|
193
|
+
makeReactive(widget, (changedProps) => {
|
|
194
|
+
widget.update(changedProps);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return widget;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export default WidgetFactory;
|