elementdrawing 1.0.0

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/elementdrawing.min.js +3 -0
  3. package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
  4. package/dist/elementdrawing.min.js.map +1 -0
  5. package/dist/index.html +1 -0
  6. package/package.json +127 -0
  7. package/src/core/bridge.h +855 -0
  8. package/src/core/diff.c +900 -0
  9. package/src/core/element.c +1078 -0
  10. package/src/core/event.c +813 -0
  11. package/src/core/fiber.c +1027 -0
  12. package/src/core/hooks.c +919 -0
  13. package/src/core/renderer.c +963 -0
  14. package/src/core/scheduler.c +702 -0
  15. package/src/core/state.c +803 -0
  16. package/src/css/animations.css +779 -0
  17. package/src/css/base.css +615 -0
  18. package/src/css/components.css +1311 -0
  19. package/src/css/tailwind.css +370 -0
  20. package/src/css/themes.css +517 -0
  21. package/src/css/utilities.css +475 -0
  22. package/src/index.js +746 -0
  23. package/src/js/animation.js +655 -0
  24. package/src/js/dom.js +665 -0
  25. package/src/js/events.js +585 -0
  26. package/src/js/http.js +446 -0
  27. package/src/js/index.js +26 -0
  28. package/src/js/router.js +483 -0
  29. package/src/js/store.js +539 -0
  30. package/src/js/utils.js +593 -0
  31. package/src/js/validator.js +529 -0
  32. package/src/jsx/components/Accordion.jsx +210 -0
  33. package/src/jsx/components/Alert.jsx +169 -0
  34. package/src/jsx/components/Avatar.jsx +214 -0
  35. package/src/jsx/components/Badge.jsx +136 -0
  36. package/src/jsx/components/Breadcrumb.jsx +200 -0
  37. package/src/jsx/components/Button.jsx +188 -0
  38. package/src/jsx/components/Card.jsx +192 -0
  39. package/src/jsx/components/Carousel.jsx +278 -0
  40. package/src/jsx/components/Checkbox.jsx +215 -0
  41. package/src/jsx/components/Dialog.jsx +242 -0
  42. package/src/jsx/components/Drawer.jsx +190 -0
  43. package/src/jsx/components/Dropdown.jsx +268 -0
  44. package/src/jsx/components/Form.jsx +274 -0
  45. package/src/jsx/components/Input.jsx +285 -0
  46. package/src/jsx/components/Menu.jsx +276 -0
  47. package/src/jsx/components/Modal.jsx +274 -0
  48. package/src/jsx/components/Navbar.jsx +292 -0
  49. package/src/jsx/components/Pagination.jsx +268 -0
  50. package/src/jsx/components/Progress.jsx +252 -0
  51. package/src/jsx/components/Radio.jsx +208 -0
  52. package/src/jsx/components/Select.jsx +397 -0
  53. package/src/jsx/components/Sidebar.jsx +250 -0
  54. package/src/jsx/components/Slider.jsx +310 -0
  55. package/src/jsx/components/Spinner.jsx +198 -0
  56. package/src/jsx/components/Switch.jsx +201 -0
  57. package/src/jsx/components/Table.jsx +332 -0
  58. package/src/jsx/components/Tabs.jsx +227 -0
  59. package/src/jsx/components/Textarea.jsx +212 -0
  60. package/src/jsx/components/Toast.jsx +270 -0
  61. package/src/jsx/components/Tooltip.jsx +178 -0
  62. package/src/jsx/components/Typography.jsx +299 -0
  63. package/src/jsx/components/index.jsx +70 -0
  64. package/src/jsx/core/element.js +3 -0
  65. package/src/jsx/hooks/index.js +356 -0
  66. package/src/jsx/hooks/useCallback.js +472 -0
  67. package/src/jsx/hooks/useContext.js +586 -0
  68. package/src/jsx/hooks/useEffect.js +704 -0
  69. package/src/jsx/hooks/useLayoutEffect.js +508 -0
  70. package/src/jsx/hooks/useMemo.js +689 -0
  71. package/src/jsx/hooks/useReducer.js +729 -0
  72. package/src/jsx/hooks/useRef.js +542 -0
  73. package/src/jsx/hooks/useState.js +854 -0
  74. package/src/jsx/runtime/commit.js +903 -0
  75. package/src/jsx/runtime/createElement.js +860 -0
  76. package/src/jsx/runtime/index.js +356 -0
  77. package/src/jsx/runtime/reconcile.js +687 -0
  78. package/src/jsx/runtime/render.js +914 -0
@@ -0,0 +1,472 @@
1
+ /**
2
+ * useCallback Hook Implementation
3
+ * ElementDrawing Framework - Callback memoization with dependency comparison,
4
+ * stable references, debouncing, throttling, profiling, and callback composition.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ // ─── Internal State ───────────────────────────────────────────────────────────
10
+
11
+ let currentlyRenderingComponent = null;
12
+ let hookIndex = 0;
13
+
14
+ // ─── Callback Profiling ───────────────────────────────────────────────────────
15
+
16
+ const callbackProfiles = new WeakMap();
17
+ let profilingEnabled = false;
18
+
19
+ /**
20
+ * Enable or disable callback profiling.
21
+ * @param {boolean} enabled
22
+ */
23
+ function setCallbackProfiling(enabled) {
24
+ profilingEnabled = enabled;
25
+ }
26
+
27
+ /**
28
+ * Get profiling data for a component's callbacks.
29
+ * @param {Object} component
30
+ * @returns {Object|null}
31
+ */
32
+ function getCallbackProfile(component) {
33
+ return callbackProfiles.get(component) || null;
34
+ }
35
+
36
+ // ─── Dependency Comparison ────────────────────────────────────────────────────
37
+
38
+ /**
39
+ * Compare two dependency arrays for shallow equality.
40
+ * @param {Array|null} prevDeps
41
+ * @param {Array|null} nextDeps
42
+ * @returns {boolean}
43
+ */
44
+ function areDepsEqual(prevDeps, nextDeps) {
45
+ if (prevDeps === null || nextDeps === null) return false;
46
+ if (prevDeps.length !== nextDeps.length) return false;
47
+ for (let i = 0; i < prevDeps.length; i++) {
48
+ if (Object.is(prevDeps[i], nextDeps[i])) continue;
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+
54
+ /**
55
+ * Deep dependency comparison for complex dependency values.
56
+ */
57
+ function areDepsDeepEqual(prevDeps, nextDeps) {
58
+ if (prevDeps === null || nextDeps === null) return false;
59
+ if (prevDeps.length !== nextDeps.length) return false;
60
+ for (let i = 0; i < prevDeps.length; i++) {
61
+ if (deepEqualVal(prevDeps[i], nextDeps[i])) continue;
62
+ return false;
63
+ }
64
+ return true;
65
+ }
66
+
67
+ function deepEqualVal(a, b) {
68
+ if (Object.is(a, b)) return true;
69
+ if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false;
70
+ const keysA = Object.keys(a);
71
+ const keysB = Object.keys(b);
72
+ if (keysA.length !== keysB.length) return false;
73
+ for (const key of keysA) {
74
+ if (!deepEqualVal(a[key], b[key])) return false;
75
+ }
76
+ return true;
77
+ }
78
+
79
+ /**
80
+ * Validate dependency array.
81
+ * @param {*} deps
82
+ */
83
+ function validateDeps(deps) {
84
+ if (deps !== undefined && !Array.isArray(deps)) {
85
+ throw new Error(
86
+ '[useCallback] Dependencies must be an array or undefined. ' +
87
+ `Received: ${typeof deps}`
88
+ );
89
+ }
90
+ }
91
+
92
+ // ─── Hook Context Management ──────────────────────────────────────────────────
93
+
94
+ function setCurrentComponent(component) {
95
+ currentlyRenderingComponent = component;
96
+ hookIndex = 0;
97
+ }
98
+
99
+ function resetHookIndex() {
100
+ hookIndex = 0;
101
+ currentlyRenderingComponent = null;
102
+ }
103
+
104
+ // ─── useCallback Hook ─────────────────────────────────────────────────────────
105
+
106
+ /**
107
+ * useCallback - Memoize a callback function based on dependencies.
108
+ * Returns a stable reference that only changes when dependencies change.
109
+ *
110
+ * @param {Function} callback - The callback function to memoize
111
+ * @param {Array} deps - Dependency array; callback updates when deps change
112
+ * @returns {Function} Memoized callback with stable reference
113
+ *
114
+ * @example
115
+ * const handleClick = useCallback((event) => {
116
+ * setCount(prev => prev + 1);
117
+ * }, [setCount]);
118
+ *
119
+ * @example
120
+ * // Preventing unnecessary child re-renders
121
+ * const handleSubmit = useCallback((data) => {
122
+ * onSubmit(data);
123
+ * }, [onSubmit]);
124
+ *
125
+ * <ChildComponent onSubmit={handleSubmit} />
126
+ */
127
+ function useCallback(callback, deps) {
128
+ const component = currentlyRenderingComponent;
129
+ if (!component) {
130
+ throw new Error('[useCallback] Must be called within a component render phase');
131
+ }
132
+
133
+ if (typeof callback !== 'function') {
134
+ throw new Error(
135
+ '[useCallback] First argument must be a function. ' +
136
+ `Received: ${typeof callback}`
137
+ );
138
+ }
139
+
140
+ validateDeps(deps);
141
+
142
+ // Initialize hooks array
143
+ if (!component._hooks) {
144
+ component._hooks = [];
145
+ }
146
+
147
+ const currentHookIndex = hookIndex;
148
+ const resolvedDeps = deps !== undefined ? deps : null;
149
+ let hook;
150
+
151
+ if (component._hooks[currentHookIndex]) {
152
+ // Re-render: check if dependencies changed
153
+ hook = component._hooks[currentHookIndex];
154
+
155
+ const prevDeps = hook.deps;
156
+ const depsChanged = !areDepsEqual(prevDeps, resolvedDeps);
157
+
158
+ if (depsChanged) {
159
+ // Dependencies changed - update the callback
160
+ hook.callback = callback;
161
+ hook.deps = resolvedDeps;
162
+ hook.memoizedCallback = createStableCallback(callback, component, currentHookIndex);
163
+ hook._updateCount++;
164
+ }
165
+ // If deps unchanged, keep the existing memoizedCallback
166
+ } else {
167
+ // First render: create the hook
168
+ hook = {
169
+ type: 'callback',
170
+ callback,
171
+ deps: resolvedDeps,
172
+ memoizedCallback: null,
173
+ _callCount: 0,
174
+ _lastCalledAt: null,
175
+ _updateCount: 0,
176
+ _createdAt: Date.now(),
177
+ };
178
+
179
+ hook.memoizedCallback = createStableCallback(callback, component, currentHookIndex);
180
+ component._hooks[currentHookIndex] = hook;
181
+ }
182
+
183
+ // Update profiling data
184
+ if (profilingEnabled) {
185
+ if (!callbackProfiles.has(component)) {
186
+ callbackProfiles.set(component, {});
187
+ }
188
+ const profile = callbackProfiles.get(component);
189
+ profile[currentHookIndex] = {
190
+ callCount: hook._callCount,
191
+ updateCount: hook._updateCount,
192
+ lastCalledAt: hook._lastCalledAt,
193
+ };
194
+ }
195
+
196
+ hookIndex++;
197
+ return hook.memoizedCallback;
198
+ }
199
+
200
+ // ─── Stable Callback Creation ─────────────────────────────────────────────────
201
+
202
+ /**
203
+ * Create a stable callback wrapper that delegates to the current callback.
204
+ * The wrapper function reference stays the same across renders while
205
+ * always calling the latest callback version.
206
+ *
207
+ * @param {Function} initialCallback - The initial callback
208
+ * @param {Object} component - The owning component
209
+ * @param {number} hookIdx - The hook index
210
+ * @returns {Function} Stable callback reference
211
+ */
212
+ function createStableCallback(initialCallback, component, hookIdx) {
213
+ // The stable wrapper always reads the latest callback from the hook
214
+ const stableCallback = function stableCallbackWrapper() {
215
+ const hook = component._hooks?.[hookIdx];
216
+ if (!hook) {
217
+ console.warn('[useCallback] Callback called after component unmount');
218
+ return undefined;
219
+ }
220
+
221
+ const currentCallback = hook.callback;
222
+ hook._callCount++;
223
+ hook._lastCalledAt = Date.now();
224
+
225
+ try {
226
+ return currentCallback.apply(this, arguments);
227
+ } catch (error) {
228
+ console.error('[useCallback] Callback execution error:', error);
229
+ if (component._handleError) {
230
+ component._handleError(error);
231
+ }
232
+ throw error;
233
+ }
234
+ };
235
+
236
+ // Attach metadata for debugging and introspection
237
+ Object.defineProperty(stableCallback, '_isStable', { value: true });
238
+ Object.defineProperty(stableCallback, '_hookIndex', { value: hookIdx });
239
+ Object.defineProperty(stableCallback, '_component', { value: component });
240
+
241
+ /**
242
+ * Get the number of times this callback has been invoked.
243
+ * @returns {number}
244
+ */
245
+ stableCallback.getCallCount = function getCallCount() {
246
+ const hook = component._hooks?.[hookIdx];
247
+ return hook ? hook._callCount : 0;
248
+ };
249
+
250
+ /**
251
+ * Get the timestamp of the last invocation.
252
+ * @returns {number|null}
253
+ */
254
+ stableCallback.getLastCalledAt = function getLastCalledAt() {
255
+ const hook = component._hooks?.[hookIdx];
256
+ return hook ? hook._lastCalledAt : null;
257
+ };
258
+
259
+ /**
260
+ * Get the current underlying callback without calling it.
261
+ * @returns {Function}
262
+ */
263
+ stableCallback.getCurrentCallback = function getCurrentCallback() {
264
+ const hook = component._hooks?.[hookIdx];
265
+ return hook ? hook.callback : null;
266
+ };
267
+
268
+ /**
269
+ * Check if this is the same stable reference as another callback.
270
+ * @param {Function} other - Another callback to compare
271
+ * @returns {boolean}
272
+ */
273
+ stableCallback.isSameRef = function isSameRef(other) {
274
+ return other === stableCallback;
275
+ };
276
+
277
+ return stableCallback;
278
+ }
279
+
280
+ // ─── Extended useCallback Features ────────────────────────────────────────────
281
+
282
+ /**
283
+ * Create a debounced version of useCallback.
284
+ * The returned callback will only execute after the specified delay
285
+ * since the last invocation.
286
+ *
287
+ * @param {Function} callback - The callback to debounce
288
+ * @param {Array} deps - Dependency array
289
+ * @param {number} delay - Debounce delay in milliseconds
290
+ * @returns {Function} Debounced memoized callback
291
+ */
292
+ function useDebouncedCallback(callback, deps, delay) {
293
+ const memoizedCallback = useCallback(callback, deps);
294
+
295
+ // Simple debounce wrapper
296
+ let timeoutId = null;
297
+ const debouncedCallback = function debouncedWrapper() {
298
+ const args = arguments;
299
+ const context = this;
300
+ if (timeoutId) clearTimeout(timeoutId);
301
+ timeoutId = setTimeout(() => {
302
+ memoizedCallback.apply(context, args);
303
+ }, delay);
304
+ };
305
+
306
+ debouncedCallback.cancel = function cancel() {
307
+ if (timeoutId) {
308
+ clearTimeout(timeoutId);
309
+ timeoutId = null;
310
+ }
311
+ };
312
+
313
+ debouncedCallback.flush = function flush() {
314
+ if (timeoutId) {
315
+ clearTimeout(timeoutId);
316
+ timeoutId = null;
317
+ memoizedCallback();
318
+ }
319
+ };
320
+
321
+ return debouncedCallback;
322
+ }
323
+
324
+ /**
325
+ * Create a throttled version of useCallback.
326
+ * The returned callback will execute at most once per specified interval.
327
+ *
328
+ * @param {Function} callback - The callback to throttle
329
+ * @param {Array} deps - Dependency array
330
+ * @param {number} interval - Throttle interval in milliseconds
331
+ * @returns {Function} Throttled memoized callback
332
+ */
333
+ function useThrottledCallback(callback, deps, interval) {
334
+ const memoizedCallback = useCallback(callback, deps);
335
+
336
+ let lastExecTime = 0;
337
+ let timeoutId = null;
338
+
339
+ const throttledCallback = function throttledWrapper() {
340
+ const args = arguments;
341
+ const context = this;
342
+ const now = Date.now();
343
+ const elapsed = now - lastExecTime;
344
+
345
+ if (elapsed >= interval) {
346
+ lastExecTime = now;
347
+ memoizedCallback.apply(context, args);
348
+ } else if (!timeoutId) {
349
+ timeoutId = setTimeout(() => {
350
+ lastExecTime = Date.now();
351
+ timeoutId = null;
352
+ memoizedCallback.apply(context, args);
353
+ }, interval - elapsed);
354
+ }
355
+ };
356
+
357
+ throttledCallback.cancel = function cancel() {
358
+ if (timeoutId) {
359
+ clearTimeout(timeoutId);
360
+ timeoutId = null;
361
+ }
362
+ };
363
+
364
+ return throttledCallback;
365
+ }
366
+
367
+ /**
368
+ * Create a callback that only executes once and then becomes a no-op.
369
+ *
370
+ * @param {Function} callback - The callback
371
+ * @param {Array} deps - Dependency array
372
+ * @returns {Function} One-time callback
373
+ */
374
+ function useOnceCallback(callback, deps) {
375
+ let called = false;
376
+ return useCallback(function onceWrapper() {
377
+ if (called) return;
378
+ called = true;
379
+ return callback.apply(this, arguments);
380
+ }, deps);
381
+ }
382
+
383
+ /**
384
+ * Create a callback with deep dependency comparison.
385
+ * Useful when dependencies are objects or arrays that may be recreated.
386
+ *
387
+ * @param {Function} callback - The callback function
388
+ * @param {Array} deps - Dependency array (compared deeply)
389
+ * @returns {Function} Memoized callback
390
+ */
391
+ function useDeepCompareCallback(callback, deps) {
392
+ const component = currentlyRenderingComponent;
393
+ if (!component) {
394
+ throw new Error('[useDeepCompareCallback] Must be called within a component render phase');
395
+ }
396
+
397
+ if (!component._hooks) {
398
+ component._hooks = [];
399
+ }
400
+
401
+ const currentHookIndex = hookIndex;
402
+ const resolvedDeps = deps !== undefined ? deps : null;
403
+ let hook;
404
+
405
+ if (component._hooks[currentHookIndex]) {
406
+ hook = component._hooks[currentHookIndex];
407
+
408
+ const prevDeps = hook.deps;
409
+ const depsChanged = !areDepsDeepEqual(prevDeps, resolvedDeps);
410
+
411
+ if (depsChanged) {
412
+ hook.callback = callback;
413
+ hook.deps = resolvedDeps;
414
+ hook.memoizedCallback = createStableCallback(callback, component, currentHookIndex);
415
+ hook._updateCount++;
416
+ }
417
+ } else {
418
+ hook = {
419
+ type: 'callback',
420
+ callback,
421
+ deps: resolvedDeps,
422
+ memoizedCallback: null,
423
+ _callCount: 0,
424
+ _lastCalledAt: null,
425
+ _updateCount: 0,
426
+ _createdAt: Date.now(),
427
+ };
428
+
429
+ hook.memoizedCallback = createStableCallback(callback, component, currentHookIndex);
430
+ component._hooks[currentHookIndex] = hook;
431
+ }
432
+
433
+ hookIndex++;
434
+ return hook.memoizedCallback;
435
+ }
436
+
437
+ /**
438
+ * Compose multiple callbacks into a single callback.
439
+ * Each callback receives the same arguments and results are collected.
440
+ *
441
+ * @param {Function[]} callbacks - Array of callbacks to compose
442
+ * @param {Array} deps - Dependency array
443
+ * @returns {Function} Composed callback
444
+ */
445
+ function useComposedCallback(callbacks, deps) {
446
+ return useCallback(function composedCallback() {
447
+ const args = arguments;
448
+ return callbacks.map((cb) => {
449
+ if (typeof cb === 'function') {
450
+ return cb.apply(this, args);
451
+ }
452
+ return undefined;
453
+ });
454
+ }, deps);
455
+ }
456
+
457
+ // ─── Exports ──────────────────────────────────────────────────────────────────
458
+
459
+ module.exports = {
460
+ useCallback,
461
+ useDebouncedCallback,
462
+ useThrottledCallback,
463
+ useOnceCallback,
464
+ useDeepCompareCallback,
465
+ useComposedCallback,
466
+ setCurrentComponent,
467
+ resetHookIndex,
468
+ areDepsEqual,
469
+ areDepsDeepEqual,
470
+ setCallbackProfiling,
471
+ getCallbackProfile,
472
+ };