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,68 @@
1
+ // tools/transform.js
2
+
3
+ /**
4
+ * Generate CSS transform string
5
+ * @param {Object} options - Transform options
6
+ * @returns {string} CSS transform value
7
+ *
8
+ * @example
9
+ * transform({ translate: [10, 20], rotate: 45, scale: 1.5 })
10
+ * // "translate(10px, 20px) rotate(45deg) scale(1.5)"
11
+ *
12
+ * @example
13
+ * transform({ translateX: 10, translateY: 20, rotate: 45 })
14
+ * // "translateX(10px) translateY(20px) rotate(45deg)"
15
+ */
16
+ export const transform = (options = {}) => {
17
+ const parts = [];
18
+
19
+ // Translate
20
+ if (options.translate) {
21
+ const [x, y] = options.translate;
22
+ parts.push(
23
+ `translate(${typeof x === "number" ? x + "px" : x}, ${typeof y === "number" ? y + "px" : y})`,
24
+ );
25
+ }
26
+ if (options.translateX)
27
+ parts.push(
28
+ `translateX(${typeof options.translateX === "number" ? options.translateX + "px" : options.translateX})`,
29
+ );
30
+ if (options.translateY)
31
+ parts.push(
32
+ `translateY(${typeof options.translateY === "number" ? options.translateY + "px" : options.translateY})`,
33
+ );
34
+ if (options.translateZ)
35
+ parts.push(
36
+ `translateZ(${typeof options.translateZ === "number" ? options.translateZ + "px" : options.translateZ})`,
37
+ );
38
+
39
+ // Rotate
40
+ if (options.rotate) parts.push(`rotate(${options.rotate}deg)`);
41
+ if (options.rotateX) parts.push(`rotateX(${options.rotateX}deg)`);
42
+ if (options.rotateY) parts.push(`rotateY(${options.rotateY}deg)`);
43
+ if (options.rotateZ) parts.push(`rotateZ(${options.rotateZ}deg)`);
44
+
45
+ // Scale
46
+ if (options.scale) parts.push(`scale(${options.scale})`);
47
+ if (options.scaleX) parts.push(`scaleX(${options.scaleX})`);
48
+ if (options.scaleY) parts.push(`scaleY(${options.scaleY})`);
49
+
50
+ // Skew
51
+ if (options.skew) parts.push(`skew(${options.skew}deg)`);
52
+ if (options.skewX) parts.push(`skewX(${options.skewX}deg)`);
53
+ if (options.skewY) parts.push(`skewY(${options.skewY}deg)`);
54
+
55
+ // Matrix
56
+ if (options.matrix) parts.push(`matrix(${options.matrix.join(", ")})`);
57
+ if (options.matrix3d) parts.push(`matrix3d(${options.matrix3d.join(", ")})`);
58
+
59
+ // Perspective
60
+ if (options.perspective)
61
+ parts.push(
62
+ `perspective(${typeof options.perspective === "number" ? options.perspective + "px" : options.perspective})`,
63
+ );
64
+
65
+ return parts.join(" ");
66
+ };
67
+
68
+ export default transform;
@@ -0,0 +1,48 @@
1
+ // tools/transition.js
2
+
3
+ /**
4
+ * Generates a CSS `transition` shorthand string from a single options object or
5
+ * an array of options objects.
6
+ *
7
+ * Each options object can specify:
8
+ * - `property` — CSS property to animate (default: `"all"`).
9
+ * - `duration` — Duration in seconds (default: `0.3`).
10
+ * - `timing` — Timing function (default: `"ease"`).
11
+ * - `delay` — Delay in seconds before the transition starts (default: `0`).
12
+ *
13
+ * When an array is passed, each item is converted individually and the results
14
+ * are joined with `", "` to produce a multi-transition value.
15
+ *
16
+ * @param {Object|Array<Object>} options - Transition options or an array of them.
17
+ * @param {string} [options.property="all"] - CSS property to animate.
18
+ * @param {number} [options.duration=0.3] - Transition duration in seconds.
19
+ * @param {string} [options.timing="ease"] - CSS timing function.
20
+ * @param {number} [options.delay=0] - Delay before the transition starts, in seconds.
21
+ * @returns {string} CSS transition value.
22
+ *
23
+ * @example
24
+ * transition({ property: 'all', duration: 0.3, timing: 'ease', delay: 0.1 })
25
+ * // "all 0.3s ease 0.1s"
26
+ *
27
+ * @example
28
+ * transition([
29
+ * { property: 'opacity', duration: 0.2 },
30
+ * { property: 'transform', duration: 0.3, timing: 'ease-out' }
31
+ * ])
32
+ * // "opacity 0.2s ease 0s, transform 0.3s ease-out 0s"
33
+ */
34
+ export const transition = (options) => {
35
+ // Handle array of transitions — convert each and join
36
+ if (Array.isArray(options)) {
37
+ return options.map((opt) => transition(opt)).join(", ");
38
+ }
39
+
40
+ const property = options.property || "all";
41
+ const duration = options.duration || 0.3;
42
+ const timing = options.timing || "ease";
43
+ const delay = options.delay || 0;
44
+
45
+ return `${property} ${duration}s ${timing} ${delay}s`;
46
+ };
47
+
48
+ export default transition;
@@ -0,0 +1,268 @@
1
+ // src/tools/useState.js
2
+ import { saveRam, getRam, subscribeRam } from "../services/RamStore.js";
3
+
4
+ // Store callbacks for each state key
5
+ const subscribers = new Map();
6
+
7
+ // 🔥 Store widgets by state key for automatic updates
8
+ const widgetsByKey = new Map();
9
+
10
+ // Global render function (set by runApp)
11
+ let globalRender = null;
12
+ let renderTimeout = null;
13
+
14
+ /**
15
+ * Sets the global render function used to trigger re-renders when state changes.
16
+ * Called by `runApp` to register the application's root render callback.
17
+ *
18
+ * @param {Function} renderFn - The root render function to invoke on state change.
19
+ * @returns {void}
20
+ */
21
+ export const setGlobalRender = (renderFn) => {
22
+ globalRender = renderFn;
23
+ };
24
+
25
+ /**
26
+ * Schedules a debounced global re-render to prevent multiple renders firing in
27
+ * rapid succession within the same synchronous execution frame.
28
+ *
29
+ * @returns {void}
30
+ */
31
+ const triggerRender = () => {
32
+ if (renderTimeout) clearTimeout(renderTimeout);
33
+ renderTimeout = setTimeout(() => {
34
+ if (globalRender) globalRender();
35
+ }, 0);
36
+ };
37
+
38
+ /**
39
+ * Registers a widget instance so it receives automatic prop updates whenever
40
+ * the state value associated with `key` changes.
41
+ *
42
+ * @param {string} key - The state key to watch.
43
+ * @param {Object} widget - The widget instance that exposes an `update` method.
44
+ * @param {string} propName - The prop name on the widget to update with the new value.
45
+ * @returns {void}
46
+ */
47
+ export const registerWidget = (key, widget, propName) => {
48
+ if (!widgetsByKey.has(key)) {
49
+ widgetsByKey.set(key, new Set());
50
+ }
51
+ widgetsByKey.get(key).add({ widget, propName });
52
+
53
+ // Set initial value if the state already exists
54
+ const currentValue = getRam(key);
55
+ if (currentValue !== undefined && widget && widget.update) {
56
+ widget.update({ [propName]: currentValue });
57
+ }
58
+
59
+ // Patch the widget's cleanup to also remove it from the registry when destroyed
60
+ const originalCleanup = widget._cleanup;
61
+ widget._cleanup = () => {
62
+ const widgets = widgetsByKey.get(key);
63
+ if (widgets) {
64
+ for (const item of widgets) {
65
+ if (item.widget === widget) {
66
+ widgets.delete(item);
67
+ break;
68
+ }
69
+ }
70
+ }
71
+ if (originalCleanup) originalCleanup();
72
+ };
73
+ };
74
+
75
+ /**
76
+ * Removes a specific widget from the automatic-update registry for a given key.
77
+ *
78
+ * @param {string} key - The state key the widget was registered under.
79
+ * @param {Object} widget - The widget instance to unregister.
80
+ * @returns {void}
81
+ */
82
+ export const unregisterWidget = (key, widget) => {
83
+ const widgets = widgetsByKey.get(key);
84
+ if (widgets) {
85
+ for (const item of widgets) {
86
+ if (item.widget === widget) {
87
+ widgets.delete(item);
88
+ break;
89
+ }
90
+ }
91
+ }
92
+ };
93
+
94
+ /**
95
+ * React-style state hook backed by the persistent RamStore.
96
+ *
97
+ * Creates (or reuses) a keyed state value that survives re-renders.
98
+ * Returns a reactive value object, a setter, and a subscribe function.
99
+ *
100
+ * @param {string} key - Unique key used to store and retrieve the state value.
101
+ * @param {*|Function} initialState - Initial value, or a factory function that returns it.
102
+ * @param {Object|null} [widget=null] - Optional widget to auto-update when state changes.
103
+ * @param {string|null} [propName=null] - The prop name on the widget to receive the new value.
104
+ * @returns {[Object, Function, Function]} Tuple of [reactiveValue, setState, subscribe].
105
+ *
106
+ * @example
107
+ * const [count, setCount, onCount] = useState('counter', 0);
108
+ * setCount(count + 1);
109
+ * onCount((newVal) => console.log('Counter changed to', newVal));
110
+ */
111
+ export const useState = (key, initialState, widget = null, propName = null) => {
112
+ // Initialize the state in RamStore if it does not exist yet
113
+ let currentValue = getRam(key);
114
+ if (currentValue === undefined || currentValue === null) {
115
+ currentValue =
116
+ typeof initialState === "function" ? initialState() : initialState;
117
+ saveRam(key, currentValue);
118
+ }
119
+
120
+ // Set up a single RamStore subscriber per key
121
+ if (!subscribers.has(key)) {
122
+ subscribers.set(key, new Set());
123
+
124
+ // Listen to RamStore changes for this key
125
+ const unsubscribe = subscribeRam((changedKey, newValue, oldValue) => {
126
+ if (changedKey === key) {
127
+ // 🔥 Push the new value to all registered widgets for this key
128
+ const widgets = widgetsByKey.get(key);
129
+ if (widgets) {
130
+ widgets.forEach(({ widget, propName }) => {
131
+ if (widget && widget.update) {
132
+ widget.update({ [propName]: newValue });
133
+ }
134
+ });
135
+ }
136
+
137
+ // Notify all subscriber callbacks
138
+ const callbacks = subscribers.get(key);
139
+ callbacks.forEach((callback) => callback(newValue, oldValue));
140
+
141
+ // Trigger a global re-render
142
+ triggerRender();
143
+ }
144
+ });
145
+
146
+ // Track the unsubscribe handle for hot-reload cleanup
147
+ if (!window._useStateCleanups) window._useStateCleanups = [];
148
+ window._useStateCleanups.push(unsubscribe);
149
+ }
150
+
151
+ // 🔥 Register the widget for automatic updates if one was provided
152
+ if (widget && propName) {
153
+ registerWidget(key, widget, propName);
154
+ }
155
+
156
+ /**
157
+ * Updates the state value. Accepts either a new value directly or an updater
158
+ * function that receives the current value and returns the next value.
159
+ *
160
+ * @param {*|Function} newValueOrUpdater - New value or `(prevValue) => nextValue` updater.
161
+ * @returns {void}
162
+ */
163
+ const setState = (newValueOrUpdater) => {
164
+ const oldValue = currentValue;
165
+ let newValue;
166
+
167
+ if (typeof newValueOrUpdater === "function") {
168
+ newValue = newValueOrUpdater(oldValue);
169
+ } else {
170
+ newValue = newValueOrUpdater;
171
+ }
172
+
173
+ if (oldValue !== newValue) {
174
+ currentValue = newValue;
175
+ saveRam(key, currentValue);
176
+ }
177
+ };
178
+
179
+ /**
180
+ * Subscribes a callback to state changes for this key.
181
+ *
182
+ * @param {Function} callback - Called with `(newValue, oldValue)` on every change.
183
+ * @returns {Function} Unsubscribe function.
184
+ */
185
+ const subscribe = (callback) => {
186
+ if (!subscribers.has(key)) {
187
+ subscribers.set(key, new Set());
188
+ }
189
+ subscribers.get(key).add(callback);
190
+ return () => {
191
+ if (subscribers.has(key)) {
192
+ subscribers.get(key).delete(callback);
193
+ }
194
+ };
195
+ };
196
+
197
+ // 🔥 Create a reactive value object so WidgetFactory can detect state bindings automatically
198
+ const reactiveValue = {
199
+ _isReactive: true,
200
+ _stateKey: key,
201
+ _value: currentValue,
202
+ valueOf() {
203
+ return this._value;
204
+ },
205
+ toString() {
206
+ return String(this._value);
207
+ },
208
+ };
209
+
210
+ // Return an array matching React's useState pattern: [value, setter, subscriber]
211
+ return [reactiveValue, setState, subscribe];
212
+ };
213
+
214
+ /**
215
+ * Watches a state key and invokes the callback whenever its value changes.
216
+ * Useful for side effects that should not trigger a full re-render.
217
+ *
218
+ * @param {string} key - The state key to watch.
219
+ * @param {Function} callback - Called with `(newValue, oldValue)` on every change.
220
+ * @returns {Function} Unsubscribe function.
221
+ */
222
+ export const useWatchState = (key, callback) => {
223
+ if (!subscribers.has(key)) {
224
+ subscribers.set(key, new Set());
225
+ }
226
+ subscribers.get(key).add(callback);
227
+ return () => subscribers.get(key).delete(callback);
228
+ };
229
+
230
+ /**
231
+ * Cleans up all state subscriptions, widget registrations, and pending render
232
+ * timers. Should be called during hot-reload to reset the module to a clean state.
233
+ *
234
+ * @returns {void}
235
+ */
236
+ export const cleanupUseState = () => {
237
+ if (window._useStateCleanups) {
238
+ window._useStateCleanups.forEach((cleanup) => {
239
+ if (typeof cleanup === "function") cleanup();
240
+ });
241
+ window._useStateCleanups = [];
242
+ }
243
+ subscribers.clear();
244
+ widgetsByKey.clear();
245
+ globalRender = null;
246
+ if (renderTimeout) clearTimeout(renderTimeout);
247
+ };
248
+
249
+ /**
250
+ * Returns diagnostic statistics about the current state of the useState module.
251
+ * Useful for debugging subscription leaks or widget registration issues.
252
+ *
253
+ * @returns {{ subscribersCount: number, widgetsCount: number, globalRender: boolean }}
254
+ */
255
+ export const getUseStateStats = () => {
256
+ return {
257
+ subscribersCount: subscribers.size,
258
+ widgetsCount: widgetsByKey.size,
259
+ globalRender: !!globalRender,
260
+ };
261
+ };
262
+
263
+ // Re-export RamStore functions under convenience aliases
264
+ export {
265
+ saveRam as saveState,
266
+ getRam as getState,
267
+ subscribeRam as subscribeState,
268
+ };
@@ -0,0 +1,42 @@
1
+ // tools/uuid.js
2
+
3
+ /**
4
+ * Generate a UUID v4
5
+ * @returns {string} UUID
6
+ */
7
+ export const uuid = () => {
8
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
9
+ const r = (Math.random() * 16) | 0;
10
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
11
+ return v.toString(16);
12
+ });
13
+ };
14
+
15
+ /**
16
+ * Generate a short ID (8 characters)
17
+ * @returns {string} Short ID
18
+ */
19
+ export const shortId = () => {
20
+ return Math.random().toString(36).substring(2, 10);
21
+ };
22
+
23
+ /**
24
+ * Generate a numeric ID
25
+ * @param {number} length - Length of the ID
26
+ * @returns {string} Numeric ID
27
+ */
28
+ export const numericId = (length = 6) => {
29
+ return Math.random()
30
+ .toString()
31
+ .slice(2, 2 + length);
32
+ };
33
+
34
+ /**
35
+ * Generate a timestamp-based ID
36
+ * @returns {string} Timestamp ID
37
+ */
38
+ export const timestampId = () => {
39
+ return Date.now().toString(36) + Math.random().toString(36).substring(2, 6);
40
+ };
41
+
42
+ export default { uuid, shortId, numericId, timestampId };
@@ -0,0 +1,256 @@
1
+ // src/modules/FletBox/core/TextInputValidator.js
2
+
3
+ /**
4
+ * TextInputValidator - Validates and sanitizes text inputs
5
+ *
6
+ * @description Provides functions to validate and sanitize inputs
7
+ * - onlyLetters() → only letters A-Z a-z (with accents and Ñ)
8
+ * - onlyNumbers() → only numbers 0-9
9
+ * - isEmail() → valid email format
10
+ * - onlyAlphanumeric() → letters and numbers
11
+ * - sanitize() → removes malicious code (XSS protection)
12
+ * - escapeHtml() → escapes HTML characters
13
+ * - safeText() → combines sanitize + escapeHtml
14
+ * - isSafe() → detects malicious patterns
15
+ * - filter() → custom pattern filter
16
+ * - limitLength() → limits text length
17
+ * - isOnlyLetters() → validates if only letters
18
+ * - isOnlyNumbers() → validates if only numbers
19
+ * - filterEmail() → filters email characters
20
+ */
21
+
22
+ export const TextInputValidator = {
23
+ /**
24
+ * Filters only letters (A-Z, a-z, with accents and Ñ)
25
+ * @param {string} value - Text to filter
26
+ * @returns {string} Only letters
27
+ */
28
+ onlyLetters: (value) => {
29
+ if (!value) return "";
30
+ return value.replace(/[^A-Za-záéíóúüñÁÉÍÓÚÜÑ\s]/g, "");
31
+ },
32
+
33
+ /**
34
+ * Filters only numbers (0-9)
35
+ * @param {string} value - Text to filter
36
+ * @returns {string} Only numbers
37
+ */
38
+ onlyNumbers: (value) => {
39
+ if (!value) return "";
40
+ return value.replace(/[^0-9]/g, "");
41
+ },
42
+
43
+ /**
44
+ * Validates email format
45
+ * @param {string} value - Email to validate
46
+ * @returns {boolean} true if valid email
47
+ */
48
+ isEmail: (value) => {
49
+ if (!value) return false;
50
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
51
+ return emailRegex.test(value);
52
+ },
53
+
54
+ /**
55
+ * Filters email (only allowed characters in email)
56
+ * @param {string} value - Text to filter
57
+ * @returns {string} Filtered email
58
+ */
59
+ filterEmail: (value) => {
60
+ if (!value) return "";
61
+ // Only allows: letters, numbers, @, ., _, -, +, %
62
+ return value.replace(/[^a-zA-Z0-9@._\-+%]/g, "");
63
+ },
64
+
65
+ /**
66
+ * Filters only letters and numbers
67
+ * @param {string} value - Text to filter
68
+ * @returns {string} Only alphanumeric
69
+ */
70
+ onlyAlphanumeric: (value) => {
71
+ if (!value) return "";
72
+ return value.replace(/[^a-zA-Z0-9]/g, "");
73
+ },
74
+
75
+ /**
76
+ * Custom pattern filter
77
+ * @param {string} value - Text to filter
78
+ * @param {string} pattern - Allowed characters (without ^)
79
+ * @returns {string} Filtered text
80
+ */
81
+ filter: (value, pattern) => {
82
+ if (!value) return "";
83
+ return value.replace(new RegExp(`[^${pattern}]`, "g"), "");
84
+ },
85
+
86
+ /**
87
+ * Limits text length
88
+ * @param {string} value - Text to limit
89
+ * @param {number} maxLength - Maximum length
90
+ * @returns {string} Truncated text
91
+ */
92
+ limitLength: (value, maxLength) => {
93
+ if (!value) return "";
94
+ return value.slice(0, maxLength);
95
+ },
96
+
97
+ /**
98
+ * Validates if text contains only letters
99
+ * @param {string} value - Text to validate
100
+ * @returns {boolean} true if only letters
101
+ */
102
+ isOnlyLetters: (value) => {
103
+ if (!value) return true;
104
+ return /^[A-Za-záéíóúüñÁÉÍÓÚÜÑ\s]+$/.test(value);
105
+ },
106
+
107
+ /**
108
+ * Validates if text contains only numbers
109
+ * @param {string} value - Text to validate
110
+ * @returns {boolean} true if only numbers
111
+ */
112
+ isOnlyNumbers: (value) => {
113
+ if (!value) return true;
114
+ return /^[0-9]+$/.test(value);
115
+ },
116
+
117
+ /**
118
+ * Sanitizes text to prevent XSS and malicious code
119
+ * Removes/escapes: <script>, javascript:, onload=, onerror=, etc.
120
+ * @param {string} value - Text to sanitize
121
+ * @returns {string} Safe text
122
+ */
123
+ sanitize: (value) => {
124
+ if (!value) return "";
125
+
126
+ let safe = String(value);
127
+
128
+ // Remove <script> tags and content
129
+ safe = safe.replace(
130
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
131
+ "",
132
+ );
133
+
134
+ // Remove javascript: protocol
135
+ safe = safe.replace(/javascript:/gi, "");
136
+
137
+ // Remove onload=, onerror=, onclick=, etc.
138
+ safe = safe.replace(/on\w+\s*=/gi, "");
139
+
140
+ // Remove alert(), confirm(), prompt()
141
+ safe = safe.replace(/alert\s*\(/gi, "");
142
+ safe = safe.replace(/confirm\s*\(/gi, "");
143
+ safe = safe.replace(/prompt\s*\(/gi, "");
144
+
145
+ // Remove eval()
146
+ safe = safe.replace(/eval\s*\(/gi, "");
147
+
148
+ // Remove document.write
149
+ safe = safe.replace(/document\.write/gi, "");
150
+
151
+ // Remove <iframe>
152
+ safe = safe.replace(
153
+ /<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,
154
+ "",
155
+ );
156
+
157
+ // Remove <object>, <embed>
158
+ safe = safe.replace(/<\/?(object|embed)[^>]*>/gi, "");
159
+
160
+ // Remove <img> with onerror
161
+ safe = safe.replace(/<img[^>]+onerror\s*=[^>]*>/gi, "");
162
+
163
+ // Remove <link> with javascript
164
+ safe = safe.replace(
165
+ /<link[^>]+href\s*=\s*['"]javascript:[^'"]*['"][^>]*>/gi,
166
+ "",
167
+ );
168
+
169
+ // Remove <meta> with refresh or http-equiv
170
+ safe = safe.replace(/<meta[^>]+(refresh|http-equiv)[^>]*>/gi, "");
171
+
172
+ // Remove &{...} expressions (template injection)
173
+ safe = safe.replace(/&\{[^}]*\}/g, "");
174
+
175
+ // Remove {{...}} expressions (template injection)
176
+ safe = safe.replace(/\{\{[^}]*\}\}/g, "");
177
+
178
+ // Remove backticks with expressions
179
+ safe = safe.replace(/\${[^}]*}/g, "");
180
+
181
+ return safe;
182
+ },
183
+
184
+ /**
185
+ * Escapes HTML special characters
186
+ * Converts < > & " ' to HTML entities
187
+ * @param {string} value - Text to escape
188
+ * @returns {string} Escaped text safe for innerHTML
189
+ */
190
+ escapeHtml: (value) => {
191
+ if (!value) return "";
192
+
193
+ const htmlEntities = {
194
+ "&": "&amp;",
195
+ "<": "&lt;",
196
+ ">": "&gt;",
197
+ '"': "&quot;",
198
+ "'": "&#39;",
199
+ "/": "&#x2F;",
200
+ "`": "&#x60;",
201
+ "=": "&#x3D;",
202
+ };
203
+
204
+ return String(value).replace(/[&<>"'/`=]/g, (char) => htmlEntities[char]);
205
+ },
206
+
207
+ /**
208
+ * Complete sanitization for user input
209
+ * Combines sanitize() + escapeHtml()
210
+ * @param {string} value - Text to sanitize
211
+ * @returns {string} Completely safe text
212
+ */
213
+ safeText: (value) => {
214
+ if (!value) return "";
215
+ let safe = TextInputValidator.sanitize(value);
216
+ safe = TextInputValidator.escapeHtml(safe);
217
+ return safe;
218
+ },
219
+
220
+ /**
221
+ * Validates if text contains any malicious patterns
222
+ * @param {string} value - Text to check
223
+ * @returns {boolean} true if safe, false if contains malicious code
224
+ */
225
+ isSafe: (value) => {
226
+ if (!value) return true;
227
+
228
+ const maliciousPatterns = [
229
+ /<script\b/i,
230
+ /javascript:/i,
231
+ /on\w+\s*=/i,
232
+ /alert\s*\(/i,
233
+ /confirm\s*\(/i,
234
+ /prompt\s*\(/i,
235
+ /eval\s*\(/i,
236
+ /document\.write/i,
237
+ /<iframe\b/i,
238
+ /<object\b/i,
239
+ /<embed\b/i,
240
+ /<link[^>]+javascript:/i,
241
+ /<meta[^>]+refresh/i,
242
+ /&\{[^}]*\}/,
243
+ /\{\{[^}]*\}\}/,
244
+ /\${[^}]*}/,
245
+ ];
246
+
247
+ for (const pattern of maliciousPatterns) {
248
+ if (pattern.test(value)) {
249
+ return false;
250
+ }
251
+ }
252
+ return true;
253
+ },
254
+ };
255
+
256
+ export default TextInputValidator;