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,585 @@
1
+ /**
2
+ * Event System Utilities
3
+ * ElementDrawing Framework - Event binding, creation, normalization,
4
+ * keyboard/mouse/touch/scroll/resize handling, and custom event emitter.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ // ─── Event Binding ────────────────────────────────────────────────────────────
10
+
11
+ /**
12
+ * Bind an event handler to an element.
13
+ * @param {HTMLElement|Window|Document} el
14
+ * @param {string} event - Event type
15
+ * @param {Function} handler
16
+ * @param {Object|boolean} [options=false]
17
+ * @returns {Function} Unbind function
18
+ */
19
+ function on(el, event, handler, options) {
20
+ el.addEventListener(event, handler, options || false);
21
+ return function unbind() {
22
+ el.removeEventListener(event, handler, options || false);
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Remove an event handler from an element.
28
+ * @param {HTMLElement|Window|Document} el
29
+ * @param {string} event
30
+ * @param {Function} handler
31
+ * @param {Object|boolean} [options=false]
32
+ */
33
+ function off(el, event, handler, options) {
34
+ el.removeEventListener(event, handler, options || false);
35
+ }
36
+
37
+ /**
38
+ * Bind an event handler that fires only once.
39
+ * @param {HTMLElement|Window|Document} el
40
+ * @param {string} event
41
+ * @param {Function} handler
42
+ * @param {Object|boolean} [options=false]
43
+ * @returns {Function} Unbind function
44
+ */
45
+ function once(el, event, handler, options) {
46
+ function onceHandler() {
47
+ off(el, event, onceHandler, options);
48
+ return handler.apply(this, arguments);
49
+ }
50
+ on(el, event, onceHandler, options);
51
+ return function unbind() {
52
+ off(el, event, onceHandler, options);
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Delegate an event to matching child elements.
58
+ * @param {HTMLElement} parent - Parent element to listen on
59
+ * @param {string} event - Event type
60
+ * @param {string} selector - CSS selector for child elements
61
+ * @param {Function} handler - Event handler
62
+ * @returns {Function} Unbind function
63
+ */
64
+ function delegate(parent, event, selector, handler) {
65
+ function delegateHandler(e) {
66
+ const target = e.target.closest(selector);
67
+ if (target && parent.contains(target)) {
68
+ handler.call(target, e, target);
69
+ }
70
+ }
71
+ on(parent, event, delegateHandler);
72
+ return function unbind() {
73
+ off(parent, event, delegateHandler);
74
+ };
75
+ }
76
+
77
+ // ─── Event Creation ───────────────────────────────────────────────────────────
78
+
79
+ /**
80
+ * Create a custom event.
81
+ * @param {string} type - Event type
82
+ * @param {Object} [detail] - Custom data
83
+ * @param {Object} [options] - Event options
84
+ * @returns {CustomEvent}
85
+ */
86
+ function createEvent(type, detail, options) {
87
+ options = options || {};
88
+ return new CustomEvent(type, {
89
+ bubbles: options.bubbles !== undefined ? options.bubbles : true,
90
+ cancelable: options.cancelable !== undefined ? options.cancelable : true,
91
+ detail: detail,
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Dispatch an event on an element.
97
+ * @param {HTMLElement} el
98
+ * @param {Event|string} event
99
+ * @param {Object} [detail]
100
+ * @returns {boolean} False if preventDefault was called
101
+ */
102
+ function dispatchEvent(el, event, detail) {
103
+ if (typeof event === 'string') {
104
+ event = createEvent(event, detail);
105
+ }
106
+ return el.dispatchEvent(event);
107
+ }
108
+
109
+ /**
110
+ * Fire a native-like event on an element.
111
+ * @param {HTMLElement} el
112
+ * @param {string} type - e.g. 'click', 'focus', 'blur'
113
+ * @param {Object} [options]
114
+ * @returns {boolean}
115
+ */
116
+ function fireEvent(el, type, options) {
117
+ const event = new Event(type, {
118
+ bubbles: (options && options.bubbles) !== undefined ? options.bubbles : true,
119
+ cancelable: (options && options.cancelable) !== undefined ? options.cancelable : true,
120
+ });
121
+ Object.assign(event, options || {});
122
+ return el.dispatchEvent(event);
123
+ }
124
+
125
+ // ─── Event Normalization ──────────────────────────────────────────────────────
126
+
127
+ /**
128
+ * Get the event target.
129
+ * @param {Event} e
130
+ * @returns {HTMLElement}
131
+ */
132
+ function getTarget(e) {
133
+ return e.target || e.srcElement;
134
+ }
135
+
136
+ /**
137
+ * Get the related target (for mouseenter/mouseleave, focus/blur).
138
+ * @param {Event} e
139
+ * @returns {HTMLElement|null}
140
+ */
141
+ function getRelatedTarget(e) {
142
+ return e.relatedTarget || null;
143
+ }
144
+
145
+ /**
146
+ * Get the offset coordinates of a mouse event relative to the target.
147
+ * @param {MouseEvent} e
148
+ * @returns {{ offsetX: number, offsetY: number }}
149
+ */
150
+ function getOffset(e) {
151
+ return { offsetX: e.offsetX, offsetY: e.offsetY };
152
+ }
153
+
154
+ /**
155
+ * Prevent default behavior.
156
+ * @param {Event} e
157
+ */
158
+ function preventDefault(e) {
159
+ if (e.preventDefault) e.preventDefault();
160
+ e.returnValue = false;
161
+ }
162
+
163
+ /**
164
+ * Stop event propagation.
165
+ * @param {Event} e
166
+ */
167
+ function stopPropagation(e) {
168
+ if (e.stopPropagation) e.stopPropagation();
169
+ e.cancelBubble = true;
170
+ }
171
+
172
+ /**
173
+ * Stop immediate propagation.
174
+ * @param {Event} e
175
+ */
176
+ function stopImmediatePropagation(e) {
177
+ if (e.stopImmediatePropagation) e.stopImmediatePropagation();
178
+ }
179
+
180
+ // ─── Keyboard Events ──────────────────────────────────────────────────────────
181
+
182
+ /**
183
+ * Check if the key is Enter.
184
+ * @param {KeyboardEvent} e
185
+ * @returns {boolean}
186
+ */
187
+ function isEnter(e) {
188
+ return e.key === 'Enter' || e.keyCode === 13;
189
+ }
190
+
191
+ /**
192
+ * Check if the key is Escape.
193
+ * @param {KeyboardEvent} e
194
+ * @returns {boolean}
195
+ */
196
+ function isEscape(e) {
197
+ return e.key === 'Escape' || e.keyCode === 27;
198
+ }
199
+
200
+ /**
201
+ * Check if the key is Tab.
202
+ * @param {KeyboardEvent} e
203
+ * @returns {boolean}
204
+ */
205
+ function isTab(e) {
206
+ return e.key === 'Tab' || e.keyCode === 9;
207
+ }
208
+
209
+ /**
210
+ * Check if the key is an arrow key.
211
+ * @param {KeyboardEvent} e
212
+ * @returns {boolean}
213
+ */
214
+ function isArrow(e) {
215
+ return e.key.startsWith('Arrow') || (e.keyCode >= 37 && e.keyCode <= 40);
216
+ }
217
+
218
+ /**
219
+ * Get the key code from a keyboard event.
220
+ * @param {KeyboardEvent} e
221
+ * @returns {number}
222
+ */
223
+ function getKeyCode(e) {
224
+ return e.keyCode || e.which || 0;
225
+ }
226
+
227
+ /**
228
+ * Check if a modifier key is pressed.
229
+ * @param {KeyboardEvent} e
230
+ * @param {string} modifier - 'ctrl' | 'shift' | 'alt' | 'meta'
231
+ * @returns {boolean}
232
+ */
233
+ function isModifier(e, modifier) {
234
+ switch (modifier) {
235
+ case 'ctrl': return e.ctrlKey;
236
+ case 'shift': return e.shiftKey;
237
+ case 'alt': return e.altKey;
238
+ case 'meta': return e.metaKey;
239
+ default: return false;
240
+ }
241
+ }
242
+
243
+ // ─── Mouse Events ─────────────────────────────────────────────────────────────
244
+
245
+ /**
246
+ * Check if the mouse button is a left click.
247
+ * @param {MouseEvent} e
248
+ * @returns {boolean}
249
+ */
250
+ function isLeftClick(e) {
251
+ return e.button === 0;
252
+ }
253
+
254
+ /**
255
+ * Check if the mouse button is a right click.
256
+ * @param {MouseEvent} e
257
+ * @returns {boolean}
258
+ */
259
+ function isRightClick(e) {
260
+ return e.button === 2;
261
+ }
262
+
263
+ /**
264
+ * Get the mouse button number.
265
+ * @param {MouseEvent} e
266
+ * @returns {number}
267
+ */
268
+ function getButton(e) {
269
+ return e.button;
270
+ }
271
+
272
+ /**
273
+ * Get the mouse coordinates (page and client).
274
+ * @param {MouseEvent} e
275
+ * @returns {{ pageX: number, pageY: number, clientX: number, clientY: number }}
276
+ */
277
+ function getCoordinates(e) {
278
+ return {
279
+ pageX: e.pageX,
280
+ pageY: e.pageY,
281
+ clientX: e.clientX,
282
+ clientY: e.clientY,
283
+ };
284
+ }
285
+
286
+ // ─── Touch Events ─────────────────────────────────────────────────────────────
287
+
288
+ /**
289
+ * Get a specific touch from a touch event.
290
+ * @param {TouchEvent} e
291
+ * @param {number} [index=0]
292
+ * @returns {Touch|null}
293
+ */
294
+ function getTouch(e, index) {
295
+ index = index || 0;
296
+ return e.touches && e.touches[index] ? e.touches[index] : null;
297
+ }
298
+
299
+ /**
300
+ * Get the distance between two touch points.
301
+ * @param {TouchEvent} e
302
+ * @returns {number}
303
+ */
304
+ function getTouchDistance(e) {
305
+ if (!e.touches || e.touches.length < 2) return 0;
306
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
307
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
308
+ return Math.sqrt(dx * dx + dy * dy);
309
+ }
310
+
311
+ /**
312
+ * Check if a touch event is a pinch gesture.
313
+ * @param {TouchEvent} e
314
+ * @returns {boolean}
315
+ */
316
+ function isPinch(e) {
317
+ return e.touches && e.touches.length >= 2;
318
+ }
319
+
320
+ /**
321
+ * Check if a touch event is a swipe.
322
+ * @param {Object} startTouch - Starting touch coordinates
323
+ * @param {Object} endTouch - Ending touch coordinates
324
+ * @param {number} [threshold=50] - Minimum distance for a swipe
325
+ * @returns {{ isSwipe: boolean, direction: string, distance: number }}
326
+ */
327
+ function isSwipe(startTouch, endTouch, threshold) {
328
+ threshold = threshold || 50;
329
+ const dx = endTouch.clientX - startTouch.clientX;
330
+ const dy = endTouch.clientY - startTouch.clientY;
331
+ const absDx = Math.abs(dx);
332
+ const absDy = Math.abs(dy);
333
+ const distance = Math.sqrt(dx * dx + dy * dy);
334
+
335
+ if (distance < threshold) {
336
+ return { isSwipe: false, direction: '', distance };
337
+ }
338
+
339
+ let direction;
340
+ if (absDx > absDy) {
341
+ direction = dx > 0 ? 'right' : 'left';
342
+ } else {
343
+ direction = dy > 0 ? 'down' : 'up';
344
+ }
345
+
346
+ return { isSwipe: true, direction, distance };
347
+ }
348
+
349
+ // ─── Scroll Events ────────────────────────────────────────────────────────────
350
+
351
+ /**
352
+ * Bind a scroll handler with optional throttling.
353
+ * @param {HTMLElement|Window} el
354
+ * @param {Function} handler
355
+ * @param {number} [throttleMs=100]
356
+ * @returns {Function} Unbind function
357
+ */
358
+ function onScroll(el, handler, throttleMs) {
359
+ throttleMs = throttleMs || 100;
360
+ let lastCall = 0;
361
+ let ticking = false;
362
+
363
+ function scrollHandler(e) {
364
+ const now = Date.now();
365
+ if (now - lastCall < throttleMs) {
366
+ if (!ticking) {
367
+ ticking = true;
368
+ requestAnimationFrame(() => {
369
+ handler(e);
370
+ ticking = false;
371
+ });
372
+ }
373
+ return;
374
+ }
375
+ lastCall = now;
376
+ handler(e);
377
+ }
378
+
379
+ return on(el, 'scroll', scrollHandler, { passive: true });
380
+ }
381
+
382
+ /**
383
+ * Bind a handler that fires when scrolling ends.
384
+ * @param {HTMLElement|Window} el
385
+ * @param {Function} handler
386
+ * @param {number} [delay=150]
387
+ * @returns {Function} Unbind function
388
+ */
389
+ function onScrollEnd(el, handler, delay) {
390
+ delay = delay || 150;
391
+ let timeoutId;
392
+
393
+ function scrollHandler() {
394
+ clearTimeout(timeoutId);
395
+ timeoutId = setTimeout(() => handler(), delay);
396
+ }
397
+
398
+ const unbind = on(el, 'scroll', scrollHandler, { passive: true });
399
+ return function unbindScrollEnd() {
400
+ clearTimeout(timeoutId);
401
+ unbind();
402
+ };
403
+ }
404
+
405
+ /**
406
+ * Get scroll direction from previous and current scroll positions.
407
+ * @param {number} prevScrollTop
408
+ * @param {number} currentScrollTop
409
+ * @returns {'up'|'down'|'none'}
410
+ */
411
+ function getScrollDirection(prevScrollTop, currentScrollTop) {
412
+ const diff = currentScrollTop - prevScrollTop;
413
+ if (diff > 0) return 'down';
414
+ if (diff < 0) return 'up';
415
+ return 'none';
416
+ }
417
+
418
+ // ─── Resize Events ────────────────────────────────────────────────────────────
419
+
420
+ /**
421
+ * Bind a resize handler with debouncing.
422
+ * @param {Function} handler
423
+ * @param {number} [debounceMs=150]
424
+ * @returns {Function} Unbind function
425
+ */
426
+ function onResize(handler, debounceMs) {
427
+ debounceMs = debounceMs || 150;
428
+ let timeoutId;
429
+
430
+ function resizeHandler() {
431
+ clearTimeout(timeoutId);
432
+ timeoutId = setTimeout(() => {
433
+ handler({
434
+ width: window.innerWidth,
435
+ height: window.innerHeight,
436
+ });
437
+ }, debounceMs);
438
+ }
439
+
440
+ return on(window, 'resize', resizeHandler);
441
+ }
442
+
443
+ /**
444
+ * Create a debounced resize observer for a specific element.
445
+ * @param {HTMLElement} el
446
+ * @param {Function} handler
447
+ * @returns {{ disconnect: Function }} Observer handle
448
+ */
449
+ function debounceResize(el, handler) {
450
+ if (typeof ResizeObserver === 'undefined') {
451
+ return { disconnect: function () {} };
452
+ }
453
+
454
+ let timeoutId;
455
+ const observer = new ResizeObserver((entries) => {
456
+ clearTimeout(timeoutId);
457
+ timeoutId = setTimeout(() => handler(entries), 100);
458
+ });
459
+
460
+ observer.observe(el);
461
+ return {
462
+ disconnect: function () {
463
+ clearTimeout(timeoutId);
464
+ observer.disconnect();
465
+ },
466
+ };
467
+ }
468
+
469
+ // ─── Custom Event Emitter ─────────────────────────────────────────────────────
470
+
471
+ /**
472
+ * Create a custom event emitter.
473
+ * @returns {Object} Emitter with on, off, emit, once methods
474
+ */
475
+ function emitter() {
476
+ const listeners = {};
477
+
478
+ return {
479
+ /**
480
+ * Subscribe to an event.
481
+ * @param {string} event
482
+ * @param {Function} handler
483
+ * @returns {Function} Unsubscribe function
484
+ */
485
+ on: function (event, handler) {
486
+ if (!listeners[event]) listeners[event] = [];
487
+ listeners[event].push(handler);
488
+ return function () {
489
+ const idx = listeners[event].indexOf(handler);
490
+ if (idx !== -1) listeners[event].splice(idx, 1);
491
+ };
492
+ },
493
+
494
+ /**
495
+ * Unsubscribe from an event.
496
+ * @param {string} event
497
+ * @param {Function} [handler] - If omitted, removes all handlers for the event
498
+ */
499
+ off: function (event, handler) {
500
+ if (!listeners[event]) return;
501
+ if (!handler) {
502
+ delete listeners[event];
503
+ return;
504
+ }
505
+ const idx = listeners[event].indexOf(handler);
506
+ if (idx !== -1) listeners[event].splice(idx, 1);
507
+ },
508
+
509
+ /**
510
+ * Emit an event.
511
+ * @param {string} event
512
+ * @param {...*} args
513
+ * @returns {boolean} True if any handler was called
514
+ */
515
+ emit: function (event) {
516
+ const args = Array.prototype.slice.call(arguments, 1);
517
+ if (!listeners[event]) return false;
518
+
519
+ listeners[event].slice().forEach((handler) => {
520
+ try {
521
+ handler.apply(null, args);
522
+ } catch (error) {
523
+ console.error('[emitter] Handler error for "' + event + '":', error);
524
+ }
525
+ });
526
+ return true;
527
+ },
528
+
529
+ /**
530
+ * Subscribe to an event once.
531
+ * @param {string} event
532
+ * @param {Function} handler
533
+ * @returns {Function} Unsubscribe function
534
+ */
535
+ once: function (event, handler) {
536
+ function onceHandler() {
537
+ emitter.off(event, onceHandler);
538
+ return handler.apply(null, arguments);
539
+ }
540
+ return emitter.on(event, onceHandler);
541
+ },
542
+
543
+ /**
544
+ * Get all listener counts.
545
+ * @returns {Object}
546
+ */
547
+ listenerCounts: function () {
548
+ const counts = {};
549
+ Object.keys(listeners).forEach((event) => {
550
+ counts[event] = listeners[event].length;
551
+ });
552
+ return counts;
553
+ },
554
+
555
+ /**
556
+ * Remove all listeners.
557
+ */
558
+ removeAllListeners: function () {
559
+ Object.keys(listeners).forEach((key) => delete listeners[key]);
560
+ },
561
+ };
562
+ }
563
+
564
+ // ─── Exports ──────────────────────────────────────────────────────────────────
565
+
566
+ module.exports = {
567
+ // Event binding
568
+ on, off, once, delegate,
569
+ // Event creation
570
+ createEvent, dispatchEvent, fireEvent,
571
+ // Event normalization
572
+ getTarget, getRelatedTarget, getOffset, preventDefault, stopPropagation, stopImmediatePropagation,
573
+ // Keyboard events
574
+ isEnter, isEscape, isTab, isArrow, getKeyCode, isModifier,
575
+ // Mouse events
576
+ isLeftClick, isRightClick, getButton, getCoordinates,
577
+ // Touch events
578
+ getTouch, getTouchDistance, isPinch, isSwipe,
579
+ // Scroll events
580
+ onScroll, onScrollEnd, getScrollDirection,
581
+ // Resize events
582
+ onResize, debounceResize,
583
+ // Custom events
584
+ emitter,
585
+ };