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,542 @@
1
+ /**
2
+ * useRef Hook Implementation
3
+ * ElementDrawing Framework - Mutable ref objects, DOM refs, callback refs,
4
+ * ref forwarding, ref merging, previous value tracking, ref lifecycle,
5
+ * and DOM measurement helpers.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ // ─── Internal State ───────────────────────────────────────────────────────────
11
+
12
+ let currentlyRenderingComponent = null;
13
+ let hookIndex = 0;
14
+ const refRegistry = new WeakMap();
15
+
16
+ // ─── Ref Lifecycle Tracking ───────────────────────────────────────────────────
17
+
18
+ const refLifecycle = new WeakMap();
19
+
20
+ /**
21
+ * Get the lifecycle state of a ref (mounted/unmounted).
22
+ * @param {Object} ref - Ref object
23
+ * @returns {string} 'mounted' | 'unmounted' | 'unknown'
24
+ */
25
+ function getRefLifecycle(ref) {
26
+ if (!ref || !ref._isRef) return 'unknown';
27
+ return refLifecycle.get(ref) || 'unknown';
28
+ }
29
+
30
+ // ─── Hook Context Management ──────────────────────────────────────────────────
31
+
32
+ function setCurrentComponent(component) {
33
+ currentlyRenderingComponent = component;
34
+ hookIndex = 0;
35
+ }
36
+
37
+ function resetHookIndex() {
38
+ hookIndex = 0;
39
+ currentlyRenderingComponent = null;
40
+ }
41
+
42
+ // ─── Ref Object Creation ──────────────────────────────────────────────────────
43
+
44
+ /**
45
+ * Create a mutable ref object with a current property.
46
+ *
47
+ * @param {*} initialValue - Initial value for current
48
+ * @returns {Object} Ref object with { current } property
49
+ *
50
+ * @example
51
+ * const countRef = useRef(0);
52
+ * countRef.current++; // Mutable without re-renders
53
+ */
54
+ function createRef(initialValue) {
55
+ const ref = {
56
+ current: initialValue,
57
+ };
58
+
59
+ Object.defineProperty(ref, '_isRef', {
60
+ enumerable: false,
61
+ configurable: false,
62
+ writable: false,
63
+ value: true,
64
+ });
65
+
66
+ Object.defineProperty(ref, '_createdAt', {
67
+ enumerable: false,
68
+ configurable: false,
69
+ writable: false,
70
+ value: Date.now(),
71
+ });
72
+
73
+ return ref;
74
+ }
75
+
76
+ // ─── useRef Hook ───────────────────────────────────────────────────────────────
77
+
78
+ /**
79
+ * useRef - Create a mutable ref object that persists across renders.
80
+ * Mutating .current does NOT trigger a re-render.
81
+ *
82
+ * @param {*} initialValue - Initial value for the ref's current property
83
+ * @returns {Object} Ref object { current: initialValue }
84
+ *
85
+ * @example
86
+ * // DOM ref
87
+ * const inputRef = useRef(null);
88
+ * <input ref={inputRef} />
89
+ * inputRef.current.focus();
90
+ *
91
+ * @example
92
+ * // Instance variable
93
+ * const timerRef = useRef(null);
94
+ * timerRef.current = setInterval(() => {}, 1000);
95
+ *
96
+ * @example
97
+ * // Previous value tracking
98
+ * const prevCountRef = useRef(count);
99
+ * useEffect(() => { prevCountRef.current = count; }, [count]);
100
+ */
101
+ function useRef(initialValue) {
102
+ const component = currentlyRenderingComponent;
103
+ if (!component) {
104
+ throw new Error('[useRef] Must be called within a component render phase');
105
+ }
106
+
107
+ // Initialize hooks array
108
+ if (!component._hooks) {
109
+ component._hooks = [];
110
+ }
111
+
112
+ const currentHookIndex = hookIndex;
113
+ let hook;
114
+
115
+ if (component._hooks[currentHookIndex]) {
116
+ // Re-render: return existing ref (never changes identity)
117
+ hook = component._hooks[currentHookIndex];
118
+ } else {
119
+ // First render: create the ref object
120
+ const ref = createRef(initialValue);
121
+
122
+ hook = {
123
+ type: 'ref',
124
+ ref,
125
+ _isDOMRef: false,
126
+ _cleanup: null,
127
+ _prevValues: [],
128
+ };
129
+
130
+ component._hooks[currentHookIndex] = hook;
131
+
132
+ // Register in the global ref registry
133
+ if (!refRegistry.has(component)) {
134
+ refRegistry.set(component, new Set());
135
+ }
136
+ refRegistry.get(component).add(currentHookIndex);
137
+ }
138
+
139
+ hookIndex++;
140
+ return hook.ref;
141
+ }
142
+
143
+ // ─── Previous Value Tracking ──────────────────────────────────────────────────
144
+
145
+ /**
146
+ * usePrevious - Track the previous value of a variable across renders.
147
+ *
148
+ * @param {*} value - Current value
149
+ * @returns {*} Previous value (undefined on first render)
150
+ *
151
+ * @example
152
+ * const [count, setCount] = useState(0);
153
+ * const prevCount = usePrevious(count);
154
+ * console.log(`Current: ${count}, Previous: ${prevCount}`);
155
+ */
156
+ function usePrevious(value) {
157
+ const ref = useRef(undefined);
158
+
159
+ // Store the previous value
160
+ const previousValue = ref.current;
161
+
162
+ // Schedule the update for next render (not immediate)
163
+ if (currentlyRenderingComponent) {
164
+ const component = currentlyRenderingComponent;
165
+ // Queue the update to happen after render
166
+ if (!component._pendingPrevUpdates) {
167
+ component._pendingPrevUpdates = [];
168
+ }
169
+ component._pendingPrevUpdates.push(() => {
170
+ ref.current = value;
171
+ });
172
+ }
173
+
174
+ return previousValue;
175
+ }
176
+
177
+ /**
178
+ * usePreviousWithInit - Like usePrevious but with an explicit initial previous value.
179
+ *
180
+ * @param {*} value - Current value
181
+ * @param {*} initPrev - Initial "previous" value for first render
182
+ * @returns {*} Previous value
183
+ */
184
+ function usePreviousWithInit(value, initPrev) {
185
+ const ref = useRef(initPrev);
186
+ const previousValue = ref.current;
187
+
188
+ if (currentlyRenderingComponent) {
189
+ const component = currentlyRenderingComponent;
190
+ if (!component._pendingPrevUpdates) {
191
+ component._pendingPrevUpdates = [];
192
+ }
193
+ component._pendingPrevUpdates.push(() => {
194
+ ref.current = value;
195
+ });
196
+ }
197
+
198
+ return previousValue;
199
+ }
200
+
201
+ // ─── Callback Refs ────────────────────────────────────────────────────────────
202
+
203
+ /**
204
+ * Create a callback ref that invokes a function when the DOM node changes.
205
+ *
206
+ * @param {Function} callback - Called with (node) when the ref value changes
207
+ * @returns {Function} Callback ref function
208
+ *
209
+ * @example
210
+ * const measureRef = useCallbackRef((node) => {
211
+ * if (node) {
212
+ * const { width, height } = node.getBoundingClientRect();
213
+ * setSize({ width, height });
214
+ * }
215
+ * });
216
+ */
217
+ function useCallbackRef(callback) {
218
+ if (typeof callback !== 'function') {
219
+ throw new Error('[useCallbackRef] Argument must be a function');
220
+ }
221
+
222
+ let lastNode = null;
223
+
224
+ const callbackRef = function refFunction(node) {
225
+ // Clean up previous node
226
+ if (lastNode && typeof callbackRef._cleanup === 'function') {
227
+ try {
228
+ callbackRef._cleanup(lastNode);
229
+ } catch (error) {
230
+ console.error('[useCallbackRef] Cleanup error:', error);
231
+ }
232
+ }
233
+
234
+ // Invoke callback with new node
235
+ if (node !== null) {
236
+ try {
237
+ const result = callback(node);
238
+ if (typeof result === 'function') {
239
+ callbackRef._cleanup = result;
240
+ }
241
+ } catch (error) {
242
+ console.error('[useCallbackRef] Callback error:', error);
243
+ }
244
+ } else {
245
+ callbackRef._cleanup = null;
246
+ }
247
+
248
+ lastNode = node;
249
+
250
+ // Track lifecycle
251
+ refLifecycle.set(callbackRef, node ? 'mounted' : 'unmounted');
252
+ };
253
+
254
+ callbackRef._isCallbackRef = true;
255
+ callbackRef._cleanup = null;
256
+
257
+ return callbackRef;
258
+ }
259
+
260
+ // ─── Ref Forwarding ───────────────────────────────────────────────────────────
261
+
262
+ /**
263
+ * Create a forwarded ref component that passes refs through to child components.
264
+ *
265
+ * @param {Function} render - Function (props, ref) => VNode
266
+ * @returns {Function} Forwarded ref component
267
+ *
268
+ * @example
269
+ * const FancyInput = forwardRef((props, ref) => (
270
+ * <input ref={ref} className="fancy-input" {...props} />
271
+ * ));
272
+ *
273
+ * <FancyInput ref={inputRef} />
274
+ */
275
+ function forwardRef(render) {
276
+ if (typeof render !== 'function') {
277
+ throw new Error(
278
+ '[forwardRef] Argument must be a render function. ' +
279
+ `Received: ${typeof render}`
280
+ );
281
+ }
282
+
283
+ const ForwardedComponent = function ForwardRefComponent(props) {
284
+ const ref = props.ref;
285
+ const restProps = Object.assign({}, props);
286
+ delete restProps.ref;
287
+
288
+ return render(restProps, ref);
289
+ };
290
+
291
+ ForwardedComponent._isForwardRef = true;
292
+ ForwardedComponent.displayName = render.displayName || render.name || 'ForwardRef';
293
+ ForwardedComponent.render = render;
294
+
295
+ return ForwardedComponent;
296
+ }
297
+
298
+ /**
299
+ * Check if a component was created with forwardRef.
300
+ * @param {Function} component
301
+ * @returns {boolean}
302
+ */
303
+ function isForwardRef(component) {
304
+ return component && component._isForwardRef === true;
305
+ }
306
+
307
+ // ─── Ref Merging ──────────────────────────────────────────────────────────────
308
+
309
+ /**
310
+ * Merge multiple refs into a single callback ref.
311
+ * Supports object refs, callback refs, and forwarded refs.
312
+ *
313
+ * @param {...(Object|Function|null)} refs - Refs to merge
314
+ * @returns {Function} Callback ref that sets all provided refs
315
+ *
316
+ * @example
317
+ * const mergedRef = useMergedRefs(externalRef, localRef);
318
+ * <div ref={mergedRef} />
319
+ */
320
+ function useMergedRefs() {
321
+ const refs = Array.prototype.slice.call(arguments);
322
+
323
+ const mergedRef = function mergedRefCallback(node) {
324
+ refs.forEach((ref) => {
325
+ if (!ref) return;
326
+
327
+ if (typeof ref === 'function') {
328
+ // Callback ref
329
+ ref(node);
330
+ } else if (typeof ref === 'object' && ref._isRef) {
331
+ // Object ref
332
+ ref.current = node;
333
+ } else if (typeof ref === 'object' && ref !== null) {
334
+ // Plain object ref
335
+ ref.current = node;
336
+ }
337
+ });
338
+ };
339
+
340
+ mergedRef._isMergedRef = true;
341
+ return mergedRef;
342
+ }
343
+
344
+ // ─── DOM Ref Helpers ──────────────────────────────────────────────────────────
345
+
346
+ /**
347
+ * Create a ref that measures the DOM node dimensions.
348
+ *
349
+ * @returns {Array} [ref, dimensions] where dimensions is { width, height, x, y }
350
+ *
351
+ * @example
352
+ * const [measureRef, dimensions] = useMeasureRef();
353
+ * <div ref={measureRef} />
354
+ * console.log(dimensions.width, dimensions.height);
355
+ */
356
+ function useMeasureRef() {
357
+ const component = currentlyRenderingComponent;
358
+ if (!component) {
359
+ throw new Error('[useMeasureRef] Must be called within a component render phase');
360
+ }
361
+
362
+ const dimensionsRef = useRef({ width: 0, height: 0, x: 0, y: 0 });
363
+ const observerRef = useRef(null);
364
+
365
+ const callbackRef = useCallbackRef((node) => {
366
+ if (observerRef.current) {
367
+ observerRef.current.disconnect();
368
+ }
369
+
370
+ if (node) {
371
+ // Measure initial dimensions
372
+ const rect = node.getBoundingClientRect();
373
+ dimensionsRef.current = {
374
+ width: rect.width,
375
+ height: rect.height,
376
+ x: rect.x,
377
+ y: rect.y,
378
+ };
379
+
380
+ // Observe size changes
381
+ if (typeof ResizeObserver !== 'undefined') {
382
+ observerRef.current = new ResizeObserver((entries) => {
383
+ const entry = entries[0];
384
+ if (entry) {
385
+ const { width, height } = entry.contentRect;
386
+ const { x, y } = entry.target.getBoundingClientRect();
387
+ dimensionsRef.current = { width, height, x, y };
388
+ }
389
+ });
390
+ observerRef.current.observe(node);
391
+ }
392
+ }
393
+ });
394
+
395
+ return [callbackRef, dimensionsRef.current];
396
+ }
397
+
398
+ /**
399
+ * Create a ref that tracks whether the element is in the viewport.
400
+ *
401
+ * @param {Object} [options] - IntersectionObserver options
402
+ * @returns {Array} [ref, isIntersecting]
403
+ */
404
+ function useInViewRef(options) {
405
+ const component = currentlyRenderingComponent;
406
+ if (!component) {
407
+ throw new Error('[useInViewRef] Must be called within a component render phase');
408
+ }
409
+
410
+ const inViewRef = useRef(false);
411
+ const observerRef = useRef(null);
412
+
413
+ const callbackRef = useCallbackRef((node) => {
414
+ if (observerRef.current) {
415
+ observerRef.current.disconnect();
416
+ }
417
+
418
+ if (node && typeof IntersectionObserver !== 'undefined') {
419
+ observerRef.current = new IntersectionObserver((entries) => {
420
+ const entry = entries[0];
421
+ if (entry) {
422
+ inViewRef.current = entry.isIntersecting;
423
+ }
424
+ }, options);
425
+ observerRef.current.observe(node);
426
+ }
427
+ });
428
+
429
+ return [callbackRef, inViewRef.current];
430
+ }
431
+
432
+ /**
433
+ * Create a ref that tracks focus state.
434
+ *
435
+ * @returns {Array} [ref, isFocused]
436
+ */
437
+ function useFocusRef() {
438
+ const component = currentlyRenderingComponent;
439
+ if (!component) {
440
+ throw new Error('[useFocusRef] Must be called within a component render phase');
441
+ }
442
+
443
+ const focusRef = useRef(false);
444
+
445
+ const callbackRef = useCallbackRef((node) => {
446
+ if (node) {
447
+ node.addEventListener('focus', () => { focusRef.current = true; });
448
+ node.addEventListener('blur', () => { focusRef.current = false; });
449
+ focusRef.current = document.activeElement === node;
450
+ }
451
+ });
452
+
453
+ return [callbackRef, focusRef.current];
454
+ }
455
+
456
+ /**
457
+ * Create a ref that auto-scrolls to the element when dependencies change.
458
+ *
459
+ * @param {Array} deps - Dependencies that trigger scroll
460
+ * @returns {Function} Ref callback
461
+ */
462
+ function useScrollRef(deps) {
463
+ const scrolledRef = useRef(false);
464
+
465
+ const callbackRef = useCallbackRef((node) => {
466
+ if (node) {
467
+ node.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
468
+ scrolledRef.current = true;
469
+ }
470
+ });
471
+
472
+ return callbackRef;
473
+ }
474
+
475
+ // ─── Cleanup ──────────────────────────────────────────────────────────────────
476
+
477
+ /**
478
+ * Process pending previous value updates after render.
479
+ * @param {Object} component
480
+ */
481
+ function processPendingPrevUpdates(component) {
482
+ if (component._pendingPrevUpdates && component._pendingPrevUpdates.length > 0) {
483
+ component._pendingPrevUpdates.forEach((fn) => fn());
484
+ component._pendingPrevUpdates = [];
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Clean up refs when a component unmounts.
490
+ * @param {Object} component
491
+ */
492
+ function cleanupRefs(component) {
493
+ if (!component._hooks) return;
494
+
495
+ component._hooks.forEach((hook) => {
496
+ if (hook && hook.type === 'ref') {
497
+ // Run cleanup for callback refs
498
+ if (typeof hook._cleanup === 'function') {
499
+ try {
500
+ hook._cleanup(null);
501
+ } catch (error) {
502
+ console.error('[useRef] Cleanup error:', error);
503
+ }
504
+ }
505
+
506
+ // Clear DOM refs
507
+ if (hook._isDOMRef && hook.ref) {
508
+ hook.ref.current = null;
509
+ refLifecycle.set(hook.ref, 'unmounted');
510
+ }
511
+ }
512
+ });
513
+
514
+ refRegistry.delete(component);
515
+
516
+ // Process any remaining pending updates
517
+ if (component._pendingPrevUpdates) {
518
+ component._pendingPrevUpdates = [];
519
+ }
520
+ }
521
+
522
+ // ─── Exports ──────────────────────────────────────────────────────────────────
523
+
524
+ module.exports = {
525
+ useRef,
526
+ usePrevious,
527
+ usePreviousWithInit,
528
+ createRef,
529
+ useCallbackRef,
530
+ forwardRef,
531
+ isForwardRef,
532
+ useMergedRefs,
533
+ useMeasureRef,
534
+ useInViewRef,
535
+ useFocusRef,
536
+ useScrollRef,
537
+ setCurrentComponent,
538
+ resetHookIndex,
539
+ cleanupRefs,
540
+ processPendingPrevUpdates,
541
+ getRefLifecycle,
542
+ };