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,192 @@
1
+ /**
2
+ * Card Component for ElementDrawing Framework
3
+ * Supports header, body, footer, shadows, hoverable, clickable, bordered, image, horizontal
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ const CARD_SHADOWS = {
8
+ none: '',
9
+ sm: 'ed-shadow-sm',
10
+ md: 'ed-shadow-md',
11
+ lg: 'ed-shadow-lg',
12
+ xl: 'ed-shadow-xl',
13
+ };
14
+
15
+ function CardHeader(props) {
16
+ const {
17
+ children,
18
+ className = '',
19
+ title,
20
+ subtitle,
21
+ extra,
22
+ bordered = true,
23
+ style = {},
24
+ } = props;
25
+
26
+ return ED.createElement('div', {
27
+ className: [
28
+ 'ed-px-6 ed-py-4',
29
+ bordered ? 'ed-border-b ed-border-gray-200' : '',
30
+ 'ed-card-header',
31
+ className,
32
+ ].filter(Boolean).join(' '),
33
+ style,
34
+ children: [
35
+ title
36
+ ? ED.createElement('div', { className: 'ed-flex ed-items-center ed-justify-between', key: 'header-content' },
37
+ ED.createElement('div', { className: 'ed-flex ed-flex-col' },
38
+ ED.createElement('h3', { className: 'ed-text-lg ed-font-semibold ed-text-gray-900' }, title),
39
+ subtitle
40
+ ? ED.createElement('p', { className: 'ed-text-sm ed-text-gray-500 ed-mt-1' }, subtitle)
41
+ : null
42
+ ),
43
+ extra
44
+ ? ED.createElement('div', { className: 'ed-ml-4 ed-flex ed-items-center' }, extra)
45
+ : null
46
+ )
47
+ : children,
48
+ ].filter(Boolean),
49
+ });
50
+ }
51
+
52
+ CardHeader.displayName = 'CardHeader';
53
+
54
+ function CardBody(props) {
55
+ const { children, className = '', style = {}, padding = true } = props;
56
+
57
+ return ED.createElement('div', {
58
+ className: [
59
+ padding ? 'ed-px-6 ed-py-4' : '',
60
+ 'ed-flex-1 ed-card-body',
61
+ className,
62
+ ].filter(Boolean).join(' '),
63
+ style,
64
+ children,
65
+ });
66
+ }
67
+
68
+ CardBody.displayName = 'CardBody';
69
+
70
+ function CardFooter(props) {
71
+ const {
72
+ children,
73
+ className = '',
74
+ bordered = true,
75
+ style = {},
76
+ align = 'right',
77
+ } = props;
78
+
79
+ const alignClass = {
80
+ left: 'ed-justify-start',
81
+ center: 'ed-justify-center',
82
+ right: 'ed-justify-end',
83
+ between: 'ed-justify-between',
84
+ }[align] || 'ed-justify-end';
85
+
86
+ return ED.createElement('div', {
87
+ className: [
88
+ 'ed-px-6 ed-py-3 ed-flex ed-items-center',
89
+ bordered ? 'ed-border-t ed-border-gray-200' : '',
90
+ alignClass,
91
+ 'ed-card-footer',
92
+ className,
93
+ ].filter(Boolean).join(' '),
94
+ style,
95
+ children,
96
+ });
97
+ }
98
+
99
+ CardFooter.displayName = 'CardFooter';
100
+
101
+ function Card(props) {
102
+ const {
103
+ children,
104
+ className = '',
105
+ style = {},
106
+ shadow = 'sm',
107
+ hoverable = false,
108
+ clickable = false,
109
+ bordered = true,
110
+ cover,
111
+ horizontal = false,
112
+ loading = false,
113
+ size = 'default',
114
+ onClick,
115
+ onMouseEnter,
116
+ onMouseLeave,
117
+ id,
118
+ role,
119
+ tabIndex,
120
+ } = props;
121
+
122
+ const sizeClasses = {
123
+ small: 'ed-text-sm',
124
+ default: '',
125
+ large: 'ed-text-base',
126
+ };
127
+
128
+ const shadowClass = CARD_SHADOWS[shadow] || CARD_SHADOWS.sm;
129
+
130
+ const baseClasses = [
131
+ 'ed-bg-white ed-rounded-lg ed-overflow-hidden ed-transition-all ed-duration-200',
132
+ shadowClass,
133
+ bordered ? 'ed-border ed-border-gray-200' : '',
134
+ hoverable ? 'hover:ed-shadow-lg hover:ed--translate-y-0.5 ed-cursor-default' : '',
135
+ clickable ? 'ed-cursor-pointer hover:ed-shadow-md hover:ed-border-blue-300 active:ed-scale-[0.99]' : '',
136
+ horizontal ? 'ed-flex ed-flex-row' : 'ed-flex ed-flex-col',
137
+ sizeClasses[size] || '',
138
+ 'ed-card',
139
+ className,
140
+ ].filter(Boolean).join(' ');
141
+
142
+ const coverElement = cover
143
+ ? ED.createElement('div', {
144
+ className: [
145
+ 'ed-overflow-hidden',
146
+ horizontal ? 'ed-w-48 ed-flex-shrink-0' : 'ed-w-full',
147
+ 'ed-card-cover',
148
+ ].join(' '),
149
+ children: typeof cover === 'string'
150
+ ? ED.createElement('img', {
151
+ src: cover,
152
+ className: 'ed-w-full ed-h-full ed-object-cover',
153
+ alt: 'card cover',
154
+ })
155
+ : cover,
156
+ })
157
+ : null;
158
+
159
+ const loadingOverlay = loading
160
+ ? ED.createElement('div', {
161
+ className: 'ed-absolute ed-inset-0 ed-bg-white ed-bg-opacity-70 ed-flex ed-items-center ed-justify-center ed-z-10',
162
+ children: ED.createElement('div', {
163
+ className: 'ed-w-8 ed-h-8 ed-border-4 ed-border-blue-500 ed-border-t-transparent ed-rounded-full ed-animate-spin',
164
+ }),
165
+ })
166
+ : null;
167
+
168
+ const cardProps = {
169
+ id,
170
+ className: baseClasses,
171
+ style: { position: 'relative', ...style },
172
+ role: clickable ? 'button' : (role || 'region'),
173
+ tabIndex: clickable ? (tabIndex ?? 0) : tabIndex,
174
+ onClick: clickable ? onClick : undefined,
175
+ onMouseEnter,
176
+ onMouseLeave,
177
+ onKeyDown: clickable
178
+ ? (e) => { if (e.key === 'Enter' || e.key === ' ') onClick?.(e); }
179
+ : undefined,
180
+ children: [coverElement, loadingOverlay, children].filter(Boolean),
181
+ };
182
+
183
+ return ED.createElement('div', cardProps);
184
+ }
185
+
186
+ Card.displayName = 'Card';
187
+ Card.Header = CardHeader;
188
+ Card.Body = CardBody;
189
+ Card.Footer = CardFooter;
190
+ Card.SHADOWS = CARD_SHADOWS;
191
+
192
+ module.exports = Card;
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Carousel Component for ElementDrawing Framework
3
+ * Supports autoplay, dots, arrows, lazy loading, fade/slide transition, thumbnails, keyboard
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ function CarouselSlide(props) {
8
+ const {
9
+ children,
10
+ src,
11
+ alt = '',
12
+ title,
13
+ description,
14
+ className = '',
15
+ style = {},
16
+ active = false,
17
+ } = props;
18
+
19
+ return ED.createElement('div', {
20
+ className: [
21
+ 'ed-absolute ed-inset-0 ed-w-full ed-h-full ed-flex ed-items-center ed-justify-center',
22
+ 'ed-transition-opacity ed-duration-500',
23
+ active ? 'ed-opacity-100 ed-z-10' : 'ed-opacity-0 ed-z-0',
24
+ className,
25
+ ].filter(Boolean).join(' '),
26
+ style,
27
+ role: 'group',
28
+ 'aria-roledescription': 'slide',
29
+ children: src
30
+ ? ED.createElement('div', { className: 'ed-relative ed-w-full ed-h-full' }, [
31
+ ED.createElement('img', {
32
+ key: 'img',
33
+ src,
34
+ alt: alt || title || 'Carousel slide',
35
+ className: 'ed-w-full ed-h-full ed-object-cover',
36
+ loading: 'lazy',
37
+ }),
38
+ title || description
39
+ ? ED.createElement('div', {
40
+ key: 'caption',
41
+ className: 'ed-absolute ed-bottom-0 ed-left-0 ed-right-0 ed-bg-gradient-to-t ed-from-black/60 ed-to-transparent ed-p-6',
42
+ }, [
43
+ title
44
+ ? ED.createElement('h3', {
45
+ key: 'title',
46
+ className: 'ed-text-xl ed-font-bold ed-text-white ed-mb-1',
47
+ }, title)
48
+ : null,
49
+ description
50
+ ? ED.createElement('p', {
51
+ key: 'desc',
52
+ className: 'ed-text-sm ed-text-gray-200',
53
+ }, description)
54
+ : null,
55
+ ].filter(Boolean))
56
+ : null,
57
+ ])
58
+ : children,
59
+ });
60
+ }
61
+
62
+ CarouselSlide.displayName = 'CarouselSlide';
63
+
64
+ function Carousel(props) {
65
+ const {
66
+ children,
67
+ items = [],
68
+ autoplay = false,
69
+ autoplayInterval = 3000,
70
+ dots = true,
71
+ arrows = true,
72
+ lazyLoad = false,
73
+ effect = 'slide',
74
+ thumbnails = false,
75
+ keyboard = true,
76
+ loop = true,
77
+ currentIndex = 0,
78
+ onChange,
79
+ className = '',
80
+ style = {},
81
+ height = 400,
82
+ width,
83
+ pauseOnHover = true,
84
+ showIndicator = true,
85
+ indicatorPosition = 'bottom',
86
+ prevIcon,
87
+ nextIcon,
88
+ dotIcon,
89
+ onSlideClick,
90
+ draggable = false,
91
+ fade = false,
92
+ speed = 500,
93
+ } = props;
94
+
95
+ const currentEffect = fade ? 'fade' : effect;
96
+
97
+ const slides = items.length > 0
98
+ ? items.map((item, idx) => ({
99
+ key: item.key || idx,
100
+ src: item.src,
101
+ alt: item.alt,
102
+ title: item.title,
103
+ description: item.description,
104
+ content: item.children || item.content,
105
+ thumbnail: item.thumbnail || item.src,
106
+ }))
107
+ : (Array.isArray(children) ? children : [children]).map((child, idx) => ({
108
+ key: idx,
109
+ content: child,
110
+ }));
111
+
112
+ const handlePrev = () => {
113
+ const newIndex = currentIndex > 0 ? currentIndex - 1 : (loop ? slides.length - 1 : 0);
114
+ onChange?.(newIndex);
115
+ };
116
+
117
+ const handleNext = () => {
118
+ const newIndex = currentIndex < slides.length - 1 ? currentIndex + 1 : (loop ? 0 : slides.length - 1);
119
+ onChange?.(newIndex);
120
+ };
121
+
122
+ const handleKeyDown = (e) => {
123
+ if (!keyboard) return;
124
+ if (e.key === 'ArrowLeft') handlePrev();
125
+ if (e.key === 'ArrowRight') handleNext();
126
+ };
127
+
128
+ const defaultPrevIcon = ED.createElement('svg', {
129
+ className: 'ed-w-6 ed-h-6',
130
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
131
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M15 19l-7-7 7-7' }),
132
+ });
133
+
134
+ const defaultNextIcon = ED.createElement('svg', {
135
+ className: 'ed-w-6 ed-h-6',
136
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
137
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M9 5l7 7-7 7' }),
138
+ });
139
+
140
+ const arrowPrev = arrows
141
+ ? ED.createElement('button', {
142
+ key: 'prev',
143
+ className: [
144
+ 'ed-absolute ed-left-3 ed-top-1/2 ed--translate-y-1/2 ed-z-20',
145
+ 'ed-w-10 ed-h-10 ed-rounded-full ed-bg-white/80 ed-shadow-md',
146
+ 'ed-flex ed-items-center ed-justify-center',
147
+ 'ed-text-gray-700 hover:ed-bg-white hover:ed-text-gray-900',
148
+ 'ed-transition-all ed-duration-200',
149
+ 'focus:ed-outline-none focus:ed-ring-2 focus:ed-ring-blue-500',
150
+ currentIndex === 0 && !loop ? 'ed-opacity-50 ed-cursor-not-allowed' : '',
151
+ ].filter(Boolean).join(' '),
152
+ onClick: handlePrev,
153
+ 'aria-label': 'Previous slide',
154
+ children: prevIcon || defaultPrevIcon,
155
+ })
156
+ : null;
157
+
158
+ const arrowNext = arrows
159
+ ? ED.createElement('button', {
160
+ key: 'next',
161
+ className: [
162
+ 'ed-absolute ed-right-3 ed-top-1/2 ed--translate-y-1/2 ed-z-20',
163
+ 'ed-w-10 ed-h-10 ed-rounded-full ed-bg-white/80 ed-shadow-md',
164
+ 'ed-flex ed-items-center ed-justify-center',
165
+ 'ed-text-gray-700 hover:ed-bg-white hover:ed-text-gray-900',
166
+ 'ed-transition-all ed-duration-200',
167
+ 'focus:ed-outline-none focus:ed-ring-2 focus:ed-ring-blue-500',
168
+ currentIndex === slides.length - 1 && !loop ? 'ed-opacity-50 ed-cursor-not-allowed' : '',
169
+ ].filter(Boolean).join(' '),
170
+ onClick: handleNext,
171
+ 'aria-label': 'Next slide',
172
+ children: nextIcon || defaultNextIcon,
173
+ })
174
+ : null;
175
+
176
+ const dotsElement = dots || showIndicator
177
+ ? ED.createElement('div', {
178
+ key: 'dots',
179
+ className: [
180
+ 'ed-absolute ed-left-1/2 ed--translate-x-1/2 ed-z-20 ed-flex ed-gap-2',
181
+ indicatorPosition === 'top' ? 'ed-top-3' : 'ed-bottom-3',
182
+ ].join(' '),
183
+ children: slides.map((_, idx) =>
184
+ ED.createElement('button', {
185
+ key: idx,
186
+ className: [
187
+ 'ed-w-2.5 ed-h-2.5 ed-rounded-full ed-transition-all ed-duration-300',
188
+ idx === currentIndex
189
+ ? 'ed-bg-white ed-w-6 ed-shadow-md'
190
+ : 'ed-bg-white/50 hover:ed-bg-white/70',
191
+ ].join(' '),
192
+ onClick: () => onChange?.(idx),
193
+ 'aria-label': `Go to slide ${idx + 1}`,
194
+ 'aria-current': idx === currentIndex,
195
+ })
196
+ ),
197
+ })
198
+ : null;
199
+
200
+ const thumbnailElement = thumbnails
201
+ ? ED.createElement('div', {
202
+ key: 'thumbnails',
203
+ className: 'ed-flex ed-gap-2 ed-mt-3 ed-overflow-x-auto ed-pb-2',
204
+ children: slides.map((slide, idx) =>
205
+ ED.createElement('button', {
206
+ key: idx,
207
+ className: [
208
+ 'ed-flex-shrink-0 ed-w-16 ed-h-12 ed-rounded-md ed-overflow-hidden ed-border-2 ed-transition-all',
209
+ idx === currentIndex
210
+ ? 'ed-border-blue-500 ed-ring-2 ed-ring-blue-200'
211
+ : 'ed-border-transparent ed-opacity-60 hover:ed-opacity-100',
212
+ ].join(' '),
213
+ onClick: () => onChange?.(idx),
214
+ 'aria-label': `Go to slide ${idx + 1}`,
215
+ children: slide.thumbnail
216
+ ? ED.createElement('img', {
217
+ src: slide.thumbnail,
218
+ className: 'ed-w-full ed-h-full ed-object-cover',
219
+ alt: `Thumbnail ${idx + 1}`,
220
+ })
221
+ : ED.createElement('div', {
222
+ className: 'ed-w-full ed-h-full ed-bg-gray-200 ed-flex ed-items-center ed-justify-center ed-text-xs ed-text-gray-500',
223
+ }, idx + 1),
224
+ })
225
+ ),
226
+ })
227
+ : null;
228
+
229
+ const slidesContainer = ED.createElement('div', {
230
+ className: 'ed-relative ed-w-full ed-overflow-hidden ed-rounded-lg',
231
+ style: { height, width },
232
+ onKeyDown: handleKeyDown,
233
+ tabIndex: keyboard ? 0 : undefined,
234
+ role: 'region',
235
+ 'aria-roledescription': 'carousel',
236
+ children: [
237
+ ED.createElement('div', {
238
+ key: 'track',
239
+ className: 'ed-relative ed-w-full ed-h-full',
240
+ style: currentEffect === 'slide'
241
+ ? { transform: `translateX(-${currentIndex * 100}%)`, transition: `transform ${speed}ms ease` }
242
+ : {},
243
+ children: slides.map((slide, idx) =>
244
+ ED.createElement(CarouselSlide, {
245
+ key: slide.key,
246
+ src: slide.src,
247
+ alt: slide.alt,
248
+ title: slide.title,
249
+ description: slide.description,
250
+ active: idx === currentIndex,
251
+ className: currentEffect === 'slide' ? 'ed-relative' : '',
252
+ children: slide.content,
253
+ })
254
+ ),
255
+ }),
256
+ arrowPrev,
257
+ arrowNext,
258
+ dotsElement,
259
+ autoplay && pauseOnHover
260
+ ? ED.createElement('div', {
261
+ key: 'pause-zone',
262
+ className: 'ed-absolute ed-inset-0 ed-z-10',
263
+ })
264
+ : null,
265
+ ],
266
+ });
267
+
268
+ return ED.createElement('div', {
269
+ className: `ed-carousel ${className}`,
270
+ style,
271
+ children: [slidesContainer, thumbnailElement].filter(Boolean),
272
+ });
273
+ }
274
+
275
+ Carousel.displayName = 'Carousel';
276
+ Carousel.Slide = CarouselSlide;
277
+
278
+ module.exports = Carousel;
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Checkbox Component for ElementDrawing Framework
3
+ * Supports sizes, indeterminate, disabled, group, label, custom icon, color variants
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ const CHECKBOX_SIZES = {
8
+ xs: { box: 'ed-w-3.5 ed-h-3.5', label: 'ed-text-xs', icon: 'ed-w-2.5 ed-h-2.5' },
9
+ sm: { box: 'ed-w-4 ed-h-4', label: 'ed-text-sm', icon: 'ed-w-3 ed-h-3' },
10
+ md: { box: 'ed-w-4.5 ed-h-4.5', label: 'ed-text-sm', icon: 'ed-w-3 ed-h-3' },
11
+ lg: { box: 'ed-w-5 ed-h-5', label: 'ed-text-base', icon: 'ed-w-3.5 ed-h-3.5' },
12
+ xl: { box: 'ed-w-6 ed-h-6', label: 'ed-text-lg', icon: 'ed-w-4 ed-h-4' },
13
+ };
14
+
15
+ const CHECKBOX_COLORS = {
16
+ blue: {
17
+ checked: 'ed-bg-blue-600 ed-border-blue-600 focus:ed-ring-blue-500',
18
+ hover: 'hover:ed-border-blue-400',
19
+ },
20
+ green: {
21
+ checked: 'ed-bg-green-600 ed-border-green-600 focus:ed-ring-green-500',
22
+ hover: 'hover:ed-border-green-400',
23
+ },
24
+ red: {
25
+ checked: 'ed-bg-red-600 ed-border-red-600 focus:ed-ring-red-500',
26
+ hover: 'hover:ed-border-red-400',
27
+ },
28
+ yellow: {
29
+ checked: 'ed-bg-yellow-500 ed-border-yellow-500 focus:ed-ring-yellow-400',
30
+ hover: 'hover:ed-border-yellow-400',
31
+ },
32
+ purple: {
33
+ checked: 'ed-bg-purple-600 ed-border-purple-600 focus:ed-ring-purple-500',
34
+ hover: 'hover:ed-border-purple-400',
35
+ },
36
+ gray: {
37
+ checked: 'ed-bg-gray-600 ed-border-gray-600 focus:ed-ring-gray-500',
38
+ hover: 'hover:ed-border-gray-400',
39
+ },
40
+ };
41
+
42
+ function Checkbox(props) {
43
+ const {
44
+ checked,
45
+ defaultChecked = false,
46
+ onChange,
47
+ disabled = false,
48
+ indeterminate = false,
49
+ label,
50
+ children,
51
+ size = 'md',
52
+ color = 'blue',
53
+ className = '',
54
+ style = {},
55
+ id,
56
+ name,
57
+ value,
58
+ icon,
59
+ classNameLabel = '',
60
+ onFocus,
61
+ onBlur,
62
+ autoFocus = false,
63
+ tabIndex,
64
+ required = false,
65
+ error = false,
66
+ wrapperClassName = '',
67
+ } = props;
68
+
69
+ const sizeConfig = CHECKBOX_SIZES[size] || CHECKBOX_SIZES.md;
70
+ const colorConfig = CHECKBOX_COLORS[color] || CHECKBOX_COLORS.blue;
71
+
72
+ const isChecked = checked !== undefined ? checked : defaultChecked;
73
+
74
+ const boxClasses = [
75
+ 'ed-relative ed-inline-flex ed-items-center ed-justify-center ed-border-2 ed-rounded ed-transition-all ed-duration-200',
76
+ sizeConfig.box,
77
+ isChecked || indeterminate ? colorConfig.checked : `ed-border-gray-300 ed-bg-white ${colorConfig.hover}`,
78
+ disabled ? 'ed-opacity-50 ed-cursor-not-allowed' : 'ed-cursor-pointer',
79
+ error && !isChecked ? 'ed-border-red-500' : '',
80
+ 'focus:ed-ring-2 focus:ed-ring-offset-1',
81
+ ].filter(Boolean).join(' ');
82
+
83
+ const checkIcon = indeterminate
84
+ ? ED.createElement('svg', {
85
+ className: `${sizeConfig.icon} ed-text-white`,
86
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
87
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 3, d: 'M5 12h14' }),
88
+ })
89
+ : isChecked
90
+ ? icon
91
+ ? (typeof icon === 'string' ? ED.createElement('i', { className: `${sizeConfig.icon} ed-text-white` }) : icon)
92
+ : ED.createElement('svg', {
93
+ className: `${sizeConfig.icon} ed-text-white`,
94
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
95
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 3, d: 'M5 13l4 4L19 7' }),
96
+ })
97
+ : null;
98
+
99
+ const labelContent = label || children;
100
+
101
+ const labelClasses = [
102
+ sizeConfig.label,
103
+ disabled ? 'ed-text-gray-400 ed-cursor-not-allowed' : 'ed-text-gray-700 ed-cursor-pointer',
104
+ 'ed-select-none ed-transition-colors',
105
+ classNameLabel,
106
+ ].filter(Boolean).join(' ');
107
+
108
+ return ED.createElement('label', {
109
+ className: [
110
+ 'ed-inline-flex ed-items-center ed-gap-2',
111
+ disabled ? 'ed-cursor-not-allowed' : 'ed-cursor-pointer',
112
+ wrapperClassName,
113
+ ].filter(Boolean).join(' '),
114
+ style,
115
+ }, [
116
+ ED.createElement('input', {
117
+ key: 'input',
118
+ type: 'checkbox',
119
+ checked: isChecked,
120
+ defaultChecked,
121
+ onChange: disabled ? undefined : onChange,
122
+ disabled,
123
+ id,
124
+ name,
125
+ value,
126
+ className: 'ed-sr-only',
127
+ onFocus,
128
+ onBlur,
129
+ autoFocus,
130
+ tabIndex,
131
+ required,
132
+ ref: indeterminate ? (el) => { if (el) el.indeterminate = indeterminate; } : undefined,
133
+ 'aria-checked': indeterminate ? 'mixed' : isChecked,
134
+ 'aria-invalid': error,
135
+ }),
136
+ ED.createElement('span', {
137
+ key: 'box',
138
+ className: boxClasses,
139
+ 'aria-hidden': 'true',
140
+ children: checkIcon,
141
+ }),
142
+ labelContent
143
+ ? ED.createElement('span', { key: 'label', className: labelClasses }, labelContent)
144
+ : null,
145
+ ]);
146
+ }
147
+
148
+ Checkbox.displayName = 'Checkbox';
149
+
150
+ function CheckboxGroup(props) {
151
+ const {
152
+ children,
153
+ options = [],
154
+ value = [],
155
+ defaultValue = [],
156
+ onChange,
157
+ disabled = false,
158
+ direction = 'horizontal',
159
+ size = 'md',
160
+ color = 'blue',
161
+ className = '',
162
+ style = {},
163
+ name,
164
+ } = props;
165
+
166
+ const currentValue = value.length > 0 ? value : defaultValue;
167
+
168
+ const handleChange = (optionValue, checked) => {
169
+ const newValue = checked
170
+ ? [...currentValue, optionValue]
171
+ : currentValue.filter(v => v !== optionValue);
172
+ onChange?.(newValue);
173
+ };
174
+
175
+ const directionClasses = {
176
+ horizontal: 'ed-flex ed-flex-wrap ed-gap-4',
177
+ vertical: 'ed-flex ed-flex-col ed-gap-2',
178
+ };
179
+
180
+ const groupClasses = [
181
+ directionClasses[direction] || directionClasses.horizontal,
182
+ className,
183
+ ].filter(Boolean).join(' ');
184
+
185
+ const optionElements = options.length > 0
186
+ ? options.map((option, idx) => {
187
+ const opt = typeof option === 'string' ? { label: option, value: option } : option;
188
+ return ED.createElement(Checkbox, {
189
+ key: opt.value || idx,
190
+ checked: currentValue.includes(opt.value),
191
+ onChange: (e) => handleChange(opt.value, e.target?.checked ?? e),
192
+ disabled: disabled || opt.disabled,
193
+ label: opt.label,
194
+ size,
195
+ color,
196
+ value: opt.value,
197
+ name,
198
+ });
199
+ })
200
+ : children;
201
+
202
+ return ED.createElement('div', {
203
+ className: groupClasses,
204
+ style,
205
+ role: 'group',
206
+ children: optionElements,
207
+ });
208
+ }
209
+
210
+ CheckboxGroup.displayName = 'CheckboxGroup';
211
+ Checkbox.Group = CheckboxGroup;
212
+ Checkbox.SIZES = CHECKBOX_SIZES;
213
+ Checkbox.COLORS = CHECKBOX_COLORS;
214
+
215
+ module.exports = Checkbox;