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.
Files changed (178) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -2
  3. package/bin/cli.js +394 -0
  4. package/bin/commands/buildAndroid.js +200 -0
  5. package/bin/commands/buildIOS.js +237 -0
  6. package/bin/commands/create.js +406 -0
  7. package/bin/commands/createBundle.js +375 -0
  8. package/bin/commands/createComponent.js +115 -0
  9. package/bin/commands/createScreen.js +161 -0
  10. package/bin/commands/killServer.js +107 -0
  11. package/bin/commands/package.js +131 -0
  12. package/bin/commands/release.js +189 -0
  13. package/bin/commands/runBundle.js +62 -0
  14. package/bin/commands/runCloudflare.js +89 -0
  15. package/bin/commands/runNative.js +109 -0
  16. package/bin/commands/runServer.js +390 -0
  17. package/bin/ota/loader.js +361 -0
  18. package/bin/utils/bundleConfig.js +178 -0
  19. package/bin/utils/capacitor.js +137 -0
  20. package/bin/utils/colors.js +215 -0
  21. package/bin/utils/helpers.js +80 -0
  22. package/bin/utils/spinner.js +96 -0
  23. package/bin/utils/templates.js +1824 -0
  24. package/bin/utils/version.js +108 -0
  25. package/package.json +58 -4
  26. package/src/animations/AnimatedBox.js +607 -0
  27. package/src/animations/AnimatedText.js +105 -0
  28. package/src/animations/MatrixRain.js +220 -0
  29. package/src/animations/ParallaxBox.js +369 -0
  30. package/src/animations/animate.js +332 -0
  31. package/src/animations/index.js +25 -0
  32. package/src/app.js +41 -0
  33. package/src/assets/icon-1024-maskable.png +0 -0
  34. package/src/assets/icon-192-maskable.png +0 -0
  35. package/src/assets/icon-192.png +0 -0
  36. package/src/assets/icon-512-maskable.png +0 -0
  37. package/src/assets/icon-512.png +0 -0
  38. package/src/assets/logo.png +0 -0
  39. package/src/core/App.js +44 -0
  40. package/src/core/hmr-client.js +142 -0
  41. package/src/core/index.js +34 -0
  42. package/src/core/pwa.js +100 -0
  43. package/src/core/runApp.js +430 -0
  44. package/src/create-index.sh +237 -0
  45. package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
  46. package/src/fonts/icons.css +23 -0
  47. package/src/index.d.ts +1930 -0
  48. package/src/index.js +288 -0
  49. package/src/navigations/AdaptiveScaffold.js +149 -0
  50. package/src/navigations/AppBar.js +331 -0
  51. package/src/navigations/BottomNavigation.js +298 -0
  52. package/src/navigations/CollapsibleSideBar.js +139 -0
  53. package/src/navigations/Drawer.js +333 -0
  54. package/src/navigations/DrawerItem.js +260 -0
  55. package/src/navigations/Router.js +475 -0
  56. package/src/navigations/Scaffold.js +537 -0
  57. package/src/navigations/Tabs.js +384 -0
  58. package/src/navigations/index.js +57 -0
  59. package/src/services/RamStore.js +213 -0
  60. package/src/services/Session.js +310 -0
  61. package/src/services/Storage.js +309 -0
  62. package/src/services/http.js +350 -0
  63. package/src/services/index.js +57 -0
  64. package/src/tools/animation.js +42 -0
  65. package/src/tools/array.js +95 -0
  66. package/src/tools/border.js +28 -0
  67. package/src/tools/clipboard.js +104 -0
  68. package/src/tools/color.js +329 -0
  69. package/src/tools/createList.js +215 -0
  70. package/src/tools/delay.js +55 -0
  71. package/src/tools/device.js +138 -0
  72. package/src/tools/dict.js +297 -0
  73. package/src/tools/dimensions.js +118 -0
  74. package/src/tools/filter.js +41 -0
  75. package/src/tools/flex.js +26 -0
  76. package/src/tools/gradient.js +34 -0
  77. package/src/tools/grid.js +57 -0
  78. package/src/tools/index.js +38 -0
  79. package/src/tools/mapList.js +107 -0
  80. package/src/tools/margin.js +93 -0
  81. package/src/tools/memo.js +60 -0
  82. package/src/tools/os.js +104 -0
  83. package/src/tools/padding.js +93 -0
  84. package/src/tools/print.js +69 -0
  85. package/src/tools/random.js +299 -0
  86. package/src/tools/ref.js +70 -0
  87. package/src/tools/rgba.js +4 -0
  88. package/src/tools/shadow.js +23 -0
  89. package/src/tools/string.js +85 -0
  90. package/src/tools/time.js +674 -0
  91. package/src/tools/transform.js +68 -0
  92. package/src/tools/transition.js +48 -0
  93. package/src/tools/useState.js +268 -0
  94. package/src/tools/uuid.js +42 -0
  95. package/src/utils/TextInputValidator.js +256 -0
  96. package/src/utils/composeUpdate.js +65 -0
  97. package/src/utils/getWidgetProps.js +35 -0
  98. package/src/utils/index.js +76 -0
  99. package/src/utils/markdownParser.js +368 -0
  100. package/src/utils/mediaTime.js +79 -0
  101. package/src/utils/navigation.js +263 -0
  102. package/src/utils/scriptInjector.js +73 -0
  103. package/src/utils/stackPosition.js +31 -0
  104. package/src/utils/stopWebRefresh.js +77 -0
  105. package/src/utils/styleInjector.js +38 -0
  106. package/src/utils/syntaxHighlight.js +339 -0
  107. package/src/utils/themes.js +347 -0
  108. package/src/utils/units.js +87 -0
  109. package/src/utils/visualEffects.js +169 -0
  110. package/src/widget-factory/addChildren.js +114 -0
  111. package/src/widget-factory/assignProps.js +256 -0
  112. package/src/widget-factory/createWidget.js +28 -0
  113. package/src/widget-factory/effects.js +132 -0
  114. package/src/widget-factory/index.js +7 -0
  115. package/src/widget-factory/lifecycle.js +210 -0
  116. package/src/widget-factory/makeParentable.js +89 -0
  117. package/src/widget-factory/pixTool.js +40 -0
  118. package/src/widget-factory/processProps.js +122 -0
  119. package/src/widget-factory/reactivity.js +98 -0
  120. package/src/widget-factory/remTool.js +34 -0
  121. package/src/widget-factory/stackPosition.js +87 -0
  122. package/src/widget-factory/stringTool.js +26 -0
  123. package/src/widget-factory/tools.js +249 -0
  124. package/src/widget-factory/translateProps.js +1049 -0
  125. package/src/widget-factory/warnings.js +284 -0
  126. package/src/widget-factory/widgetFactory.js +200 -0
  127. package/src/widgets/Accordion.js +562 -0
  128. package/src/widgets/AdMob.js +315 -0
  129. package/src/widgets/AdSense.js +268 -0
  130. package/src/widgets/AlertDialog.js +162 -0
  131. package/src/widgets/Audio.js +311 -0
  132. package/src/widgets/Avatar.js +210 -0
  133. package/src/widgets/Badge.js +189 -0
  134. package/src/widgets/BottomSheet.js +287 -0
  135. package/src/widgets/Button.js +247 -0
  136. package/src/widgets/Card.js +33 -0
  137. package/src/widgets/Carousel.js +301 -0
  138. package/src/widgets/Chart.js +632 -0
  139. package/src/widgets/Checkbox.js +106 -0
  140. package/src/widgets/Chip.js +153 -0
  141. package/src/widgets/CircularBar.js +352 -0
  142. package/src/widgets/CircularChart.js +549 -0
  143. package/src/widgets/CodeViewer.js +236 -0
  144. package/src/widgets/Column.js +29 -0
  145. package/src/widgets/Container.js +31 -0
  146. package/src/widgets/DataTable.js +257 -0
  147. package/src/widgets/Divider.js +93 -0
  148. package/src/widgets/DraggBox.js +115 -0
  149. package/src/widgets/Dropdown.js +505 -0
  150. package/src/widgets/DroppBox.js +180 -0
  151. package/src/widgets/FloatingActionButton.js +91 -0
  152. package/src/widgets/GridView.js +20 -0
  153. package/src/widgets/Icon.js +33 -0
  154. package/src/widgets/Image.js +15 -0
  155. package/src/widgets/Input.js +565 -0
  156. package/src/widgets/Inspector.js +254 -0
  157. package/src/widgets/InstallButton.js +110 -0
  158. package/src/widgets/ListTile.js +224 -0
  159. package/src/widgets/ListView.js +503 -0
  160. package/src/widgets/Markdown.js +1236 -0
  161. package/src/widgets/Modal.js +496 -0
  162. package/src/widgets/Pagination.js +195 -0
  163. package/src/widgets/ProgressBar.js +178 -0
  164. package/src/widgets/QRCode.js +467 -0
  165. package/src/widgets/Radio.js +132 -0
  166. package/src/widgets/Rating.js +261 -0
  167. package/src/widgets/Row.js +29 -0
  168. package/src/widgets/Skeleton.js +243 -0
  169. package/src/widgets/Slider.js +527 -0
  170. package/src/widgets/SnackBar.js +227 -0
  171. package/src/widgets/Stack.js +29 -0
  172. package/src/widgets/Stepper.js +296 -0
  173. package/src/widgets/Switch.js +102 -0
  174. package/src/widgets/Text.js +98 -0
  175. package/src/widgets/Tooltip.js +267 -0
  176. package/src/widgets/TreeView.js +370 -0
  177. package/src/widgets/Video.js +92 -0
  178. 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;