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,397 @@
1
+ /**
2
+ * Select Component for ElementDrawing Framework
3
+ * Supports search, multiple, tags, group options, custom render, remote search, clearable, creatable, virtual scroll
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ const SELECT_SIZES = {
8
+ sm: 'ed-text-sm ed-px-2 ed-py-1',
9
+ md: 'ed-text-sm ed-px-3 ed-py-1.5',
10
+ lg: 'ed-text-base ed-px-3 ed-py-2.5',
11
+ };
12
+
13
+ function Option(props) {
14
+ const {
15
+ children,
16
+ value,
17
+ label,
18
+ disabled = false,
19
+ selected = false,
20
+ active = false,
21
+ icon,
22
+ description,
23
+ className = '',
24
+ style = {},
25
+ onClick,
26
+ onMouseEnter,
27
+ } = props;
28
+
29
+ const optionClasses = [
30
+ 'ed-flex ed-items-center ed-px-3 ed-py-2 ed-text-sm ed-cursor-pointer ed-transition-colors ed-duration-150 ed-gap-2',
31
+ disabled
32
+ ? 'ed-text-gray-400 ed-cursor-not-allowed'
33
+ : selected
34
+ ? 'ed-bg-blue-50 ed-text-blue-700 ed-font-medium'
35
+ : active
36
+ ? 'ed-bg-gray-50 ed-text-gray-900'
37
+ : 'ed-text-gray-700 hover:ed-bg-gray-50',
38
+ className,
39
+ ].filter(Boolean).join(' ');
40
+
41
+ const checkIcon = selected
42
+ ? ED.createElement('svg', {
43
+ className: 'ed-w-4 ed-h-4 ed-text-blue-600 ed-flex-shrink-0',
44
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
45
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M5 13l4 4L19 7' }),
46
+ })
47
+ : null;
48
+
49
+ const iconElement = icon
50
+ ? ED.createElement('span', { className: 'ed-w-4 ed-h-4 ed-flex ed-items-center ed-justify-center ed-flex-shrink-0' },
51
+ typeof icon === 'string' ? ED.createElement('i', { className: icon }) : icon
52
+ )
53
+ : null;
54
+
55
+ return ED.createElement('div', {
56
+ className: optionClasses,
57
+ style,
58
+ onClick: disabled ? undefined : onClick,
59
+ onMouseEnter,
60
+ role: 'option',
61
+ 'aria-selected': selected,
62
+ 'aria-disabled': disabled,
63
+ children: [
64
+ checkIcon,
65
+ iconElement,
66
+ ED.createElement('div', { className: 'ed-flex-1 ed-min-w-0' }, [
67
+ ED.createElement('span', { key: 'label', className: 'ed-truncate ed-block' }, children || label),
68
+ description
69
+ ? ED.createElement('span', { key: 'desc', className: 'ed-text-xs ed-text-gray-400 ed-block' }, description)
70
+ : null,
71
+ ].filter(Boolean)),
72
+ ],
73
+ });
74
+ }
75
+
76
+ Option.displayName = 'Option';
77
+
78
+ function OptionGroup(props) {
79
+ const {
80
+ children,
81
+ label,
82
+ className = '',
83
+ style = {},
84
+ } = props;
85
+
86
+ return ED.createElement('div', { className, style, role: 'optgroup' }, [
87
+ label
88
+ ? ED.createElement('div', {
89
+ key: 'label',
90
+ className: 'ed-px-3 ed-py-1.5 ed-text-xs ed-font-semibold ed-text-gray-400 ed-uppercase ed-tracking-wider',
91
+ }, label)
92
+ : null,
93
+ children,
94
+ ]);
95
+ }
96
+
97
+ OptionGroup.displayName = 'OptionGroup';
98
+
99
+ function Select(props) {
100
+ const {
101
+ value,
102
+ defaultValue,
103
+ onChange,
104
+ options = [],
105
+ placeholder = 'Select...',
106
+ searchable = false,
107
+ multiple = false,
108
+ tags = false,
109
+ clearable = false,
110
+ disabled = false,
111
+ loading = false,
112
+ size = 'md',
113
+ className = '',
114
+ style = {},
115
+ dropdownClassName = '',
116
+ dropdownStyle = {},
117
+ open: controlledOpen,
118
+ onDropdownVisibleChange,
119
+ onSearch,
120
+ remoteSearch = false,
121
+ creatable = false,
122
+ onCreateOption,
123
+ virtual = false,
124
+ maxTagCount,
125
+ maxTagTextLength,
126
+ label,
127
+ labelKey = 'label',
128
+ valueKey = 'value',
129
+ groupByKey,
130
+ renderOption,
131
+ renderValue,
132
+ suffixIcon,
133
+ prefixIcon,
134
+ emptyText = 'No options',
135
+ notFoundContent,
136
+ showArrow = true,
137
+ allowClear,
138
+ filterOption,
139
+ menuItemSelectedIcon,
140
+ dropdownRender,
141
+ onClear,
142
+ onFocus,
143
+ onBlur,
144
+ id,
145
+ name,
146
+ required = false,
147
+ error = false,
148
+ bordered = true,
149
+ status,
150
+ } = props;
151
+
152
+ const isOpen = controlledOpen !== undefined ? controlledOpen : false;
153
+ const currentValue = value !== undefined ? value : defaultValue;
154
+ const isMultiple = multiple || tags;
155
+ const currentError = error || status === 'error';
156
+
157
+ const sizeClass = SELECT_SIZES[size] || SELECT_SIZES.md;
158
+
159
+ const selectedOptions = Array.isArray(currentValue)
160
+ ? options.filter(opt => currentValue.includes(typeof opt === 'object' ? opt[valueKey] : opt))
161
+ : options.filter(opt => (typeof opt === 'object' ? opt[valueKey] : opt) === currentValue);
162
+
163
+ const selectedLabel = isMultiple
164
+ ? selectedOptions.map(opt => typeof opt === 'object' ? opt[labelKey] : opt)
165
+ : selectedOptions[0]
166
+ ? (typeof selectedOptions[0] === 'object' ? selectedOptions[0][labelKey] : selectedOptions[0])
167
+ : '';
168
+
169
+ const triggerClasses = [
170
+ 'ed-flex ed-items-center ed-w-full ed-rounded-md ed-transition-colors ed-duration-200 ed-gap-1',
171
+ sizeClass,
172
+ bordered ? 'ed-border' : 'ed-border-0',
173
+ currentError ? 'ed-border-red-500' : 'ed-border-gray-300',
174
+ disabled ? 'ed-bg-gray-100 ed-cursor-not-allowed ed-text-gray-400' : 'ed-bg-white ed-cursor-pointer ed-text-gray-900',
175
+ 'hover:ed-border-gray-400 focus-within:ed-ring-2 focus-within:ed-ring-blue-500 focus-within:ed-border-blue-500',
176
+ isOpen ? 'ed-ring-2 ed-ring-blue-500 ed-border-blue-500' : '',
177
+ className,
178
+ ].filter(Boolean).join(' ');
179
+
180
+ const arrowIcon = showArrow && !loading
181
+ ? ED.createElement('svg', {
182
+ className: `ed-w-4 ed-h-4 ed-text-gray-400 ed-transition-transform ed-duration-200 ed-flex-shrink-0 ${isOpen ? 'ed-rotate-180' : ''}`,
183
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
184
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M19 9l-7 7-7-7' }),
185
+ })
186
+ : null;
187
+
188
+ const loadingIcon = loading
189
+ ? ED.createElement('div', { className: 'ed-w-4 ed-h-4 ed-border-2 ed-border-blue-500 ed-border-t-transparent ed-rounded-full ed-animate-spin ed-flex-shrink-0' })
190
+ : null;
191
+
192
+ const clearIcon = (clearable || allowClear) && currentValue && !disabled
193
+ ? ED.createElement('button', {
194
+ className: 'ed-flex ed-items-center ed-justify-center ed-text-gray-400 hover:ed-text-gray-600 ed-transition-colors ed-flex-shrink-0',
195
+ onClick: (e) => { e.stopPropagation(); onClear?.(); onChange?.(isMultiple ? [] : undefined); },
196
+ tabIndex: -1,
197
+ children: ED.createElement('svg', {
198
+ className: 'ed-w-4 ed-h-4',
199
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
200
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M6 18L18 6M6 6l12 12' }),
201
+ }),
202
+ })
203
+ : null;
204
+
205
+ const prefixElement = prefixIcon
206
+ ? ED.createElement('span', { className: 'ed-text-gray-400 ed-flex-shrink-0' },
207
+ typeof prefixIcon === 'string' ? ED.createElement('i', { className: prefixIcon }) : prefixIcon
208
+ )
209
+ : null;
210
+
211
+ const searchInput = searchable && isOpen
212
+ ? ED.createElement('input', {
213
+ className: 'ed-flex-1 ed-outline-none ed-bg-transparent ed-text-sm ed-placeholder-ed-text-gray-400',
214
+ placeholder: selectedLabel || placeholder,
215
+ onChange: (e) => onSearch?.(e.target.value),
216
+ autoFocus: true,
217
+ })
218
+ : null;
219
+
220
+ const tagElements = isMultiple && selectedOptions.length > 0
221
+ ? selectedOptions.slice(0, maxTagCount || selectedOptions.length).map((opt, idx) =>
222
+ ED.createElement('span', {
223
+ key: idx,
224
+ className: 'ed-inline-flex ed-items-center ed-gap-1 ed-px-2 ed-py-0.5 ed-text-xs ed-bg-blue-100 ed-text-blue-800 ed-rounded ed-font-medium',
225
+ children: [
226
+ ED.createElement('span', { key: 'text' },
227
+ maxTagTextLength && (typeof opt === 'object' ? opt[labelKey] : opt).length > maxTagTextLength
228
+ ? (typeof opt === 'object' ? opt[labelKey] : opt).slice(0, maxTagTextLength) + '...'
229
+ : typeof opt === 'object' ? opt[labelKey] : opt
230
+ ),
231
+ ED.createElement('button', {
232
+ key: 'remove',
233
+ className: 'ed-hover:ed-text-red-500 ed-transition-colors',
234
+ onClick: (e) => {
235
+ e.stopPropagation();
236
+ const optVal = typeof opt === 'object' ? opt[valueKey] : opt;
237
+ onChange?.(currentValue.filter(v => v !== optVal));
238
+ },
239
+ children: ED.createElement('svg', { className: 'ed-w-3 ed-h-3', fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor' },
240
+ ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M6 18L18 6M6 6l12 12' })
241
+ ),
242
+ }),
243
+ ],
244
+ })
245
+ )
246
+ : null;
247
+
248
+ const maxTagElement = isMultiple && maxTagCount && selectedOptions.length > maxTagCount
249
+ ? ED.createElement('span', {
250
+ className: 'ed-text-xs ed-text-gray-500',
251
+ }, `+${selectedOptions.length - maxTagCount}`)
252
+ : null;
253
+
254
+ const displayValue = !isMultiple && !searchable
255
+ ? renderValue
256
+ ? renderValue(selectedOptions[0])
257
+ : selectedLabel || ED.createElement('span', { className: 'ed-text-gray-400' }, placeholder)
258
+ : null;
259
+
260
+ const filteredOptions = options;
261
+ const grouped = groupByKey && filteredOptions.some(opt => typeof opt === 'object' && opt[groupByKey]);
262
+
263
+ const renderOptions = () => {
264
+ if (grouped) {
265
+ const groups = {};
266
+ filteredOptions.forEach(opt => {
267
+ const group = typeof opt === 'object' ? opt[groupByKey] : 'Other';
268
+ if (!groups[group]) groups[group] = [];
269
+ groups[group].push(opt);
270
+ });
271
+ return Object.entries(groups).flatMap(([groupLabel, groupOptions]) =>
272
+ ED.createElement(OptionGroup, { key: groupLabel, label: groupLabel },
273
+ ...groupOptions.map((opt, idx) => {
274
+ const optVal = typeof opt === 'object' ? opt[valueKey] : opt;
275
+ const optLabel = typeof opt === 'object' ? opt[labelKey] : opt;
276
+ return ED.createElement(Option, {
277
+ key: optVal || idx,
278
+ value: optVal,
279
+ label: optLabel,
280
+ disabled: opt.disabled,
281
+ selected: isMultiple
282
+ ? currentValue?.includes(optVal)
283
+ : currentValue === optVal,
284
+ icon: opt.icon,
285
+ description: opt.description,
286
+ onClick: () => {
287
+ if (isMultiple) {
288
+ const newVal = currentValue?.includes(optVal)
289
+ ? currentValue.filter(v => v !== optVal)
290
+ : [...(currentValue || []), optVal];
291
+ onChange?.(newVal);
292
+ } else {
293
+ onChange?.(optVal);
294
+ onDropdownVisibleChange?.(false);
295
+ }
296
+ },
297
+ }, renderOption ? renderOption(opt) : optLabel);
298
+ })
299
+ )
300
+ );
301
+ }
302
+
303
+ return filteredOptions.map((opt, idx) => {
304
+ const optVal = typeof opt === 'object' ? opt[valueKey] : opt;
305
+ const optLabel = typeof opt === 'object' ? opt[labelKey] : opt;
306
+ return ED.createElement(Option, {
307
+ key: optVal || idx,
308
+ value: optVal,
309
+ label: optLabel,
310
+ disabled: opt.disabled,
311
+ selected: isMultiple
312
+ ? currentValue?.includes(optVal)
313
+ : currentValue === optVal,
314
+ icon: opt.icon,
315
+ description: opt.description,
316
+ onClick: () => {
317
+ if (isMultiple) {
318
+ const newVal = currentValue?.includes(optVal)
319
+ ? currentValue.filter(v => v !== optVal)
320
+ : [...(currentValue || []), optVal];
321
+ onChange?.(newVal);
322
+ } else {
323
+ onChange?.(optVal);
324
+ onDropdownVisibleChange?.(false);
325
+ }
326
+ },
327
+ }, renderOption ? renderOption(opt) : optLabel);
328
+ });
329
+ };
330
+
331
+ const createOption = creatable && searchable
332
+ ? ED.createElement('div', {
333
+ className: 'ed-px-3 ed-py-2 ed-text-sm ed-text-blue-600 ed-cursor-pointer hover:ed-bg-blue-50 ed-transition-colors',
334
+ }, `Create option`)
335
+ : null;
336
+
337
+ const dropdownContent = ED.createElement('div', {
338
+ className: [
339
+ 'ed-absolute ed-left-0 ed-right-0 ed-top-full ed-mt-1 ed-bg-white ed-rounded-md ed-shadow-lg ed-border ed-border-gray-200 ed-z-50',
340
+ 'ed-max-h-60 ed-overflow-y-auto ed-py-1',
341
+ dropdownClassName,
342
+ ].filter(Boolean).join(' '),
343
+ style: dropdownStyle,
344
+ role: 'listbox',
345
+ children: [
346
+ filteredOptions.length > 0
347
+ ? renderOptions()
348
+ : ED.createElement('div', { className: 'ed-px-3 ed-py-4 ed-text-center ed-text-sm ed-text-gray-400' },
349
+ notFoundContent || emptyText
350
+ ),
351
+ createOption,
352
+ ],
353
+ });
354
+
355
+ return ED.createElement('div', {
356
+ className: 'ed-relative ed-inline-flex ed-flex-col',
357
+ style,
358
+ }, [
359
+ label
360
+ ? ED.createElement('label', {
361
+ key: 'label',
362
+ htmlFor: id,
363
+ className: `ed-block ed-text-sm ed-font-medium ed-mb-1 ${required ? "ed-after:content-['*'] ed-after:ed-text-red-500" : ''} ed-text-gray-700`,
364
+ }, label)
365
+ : null,
366
+ ED.createElement('div', {
367
+ key: 'trigger',
368
+ className: triggerClasses,
369
+ onClick: () => { if (!disabled) onDropdownVisibleChange?.(!isOpen); },
370
+ onFocus,
371
+ onBlur,
372
+ role: 'combobox',
373
+ 'aria-expanded': isOpen,
374
+ 'aria-haspopup': 'listbox',
375
+ tabIndex: disabled ? -1 : 0,
376
+ children: [
377
+ prefixElement,
378
+ ED.createElement('div', { key: 'value-area', className: 'ed-flex-1 ed-flex ed-items-center ed-flex-wrap ed-gap-1 ed-min-w-0' },
379
+ tagElements,
380
+ maxTagElement,
381
+ searchInput,
382
+ displayValue,
383
+ ),
384
+ clearIcon,
385
+ loadingIcon || arrowIcon,
386
+ ],
387
+ }),
388
+ isOpen ? dropdownContent : null,
389
+ ]);
390
+ }
391
+
392
+ Select.displayName = 'Select';
393
+ Select.Option = Option;
394
+ Select.OptionGroup = OptionGroup;
395
+ Select.SIZES = SELECT_SIZES;
396
+
397
+ module.exports = Select;
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Sidebar Component for ElementDrawing Framework
3
+ * Supports collapsible, menu items, nested items, icons, badges, active state, mini mode, overlay, responsive
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ function SidebarItem(props) {
8
+ const {
9
+ label,
10
+ icon,
11
+ badge,
12
+ badgeColor = 'blue',
13
+ active = false,
14
+ disabled = false,
15
+ children: nestedItems,
16
+ href,
17
+ onClick,
18
+ className = '',
19
+ style = {},
20
+ indent = 0,
21
+ mini = false,
22
+ expanded = false,
23
+ onToggle,
24
+ depth = 0,
25
+ } = props;
26
+
27
+ const hasChildren = nestedItems && nestedItems.length > 0;
28
+ const badgeColorClasses = {
29
+ blue: 'ed-bg-blue-500 ed-text-white',
30
+ red: 'ed-bg-red-500 ed-text-white',
31
+ green: 'ed-bg-green-500 ed-text-white',
32
+ yellow: 'ed-bg-yellow-500 ed-text-white',
33
+ gray: 'ed-bg-gray-500 ed-text-white',
34
+ };
35
+
36
+ const itemClasses = [
37
+ 'ed-flex ed-items-center ed-px-3 ed-py-2.5 ed-rounded-lg ed-text-sm ed-transition-all ed-duration-200 ed-group',
38
+ active
39
+ ? 'ed-bg-blue-50 ed-text-blue-700 ed-font-medium'
40
+ : 'ed-text-gray-700 hover:ed-bg-gray-100 hover:ed-text-gray-900',
41
+ disabled ? 'ed-opacity-50 ed-cursor-not-allowed ed-pointer-events-none' : 'ed-cursor-pointer',
42
+ mini ? 'ed-justify-center ed-px-2' : '',
43
+ className,
44
+ ].filter(Boolean).join(' ');
45
+
46
+ const badgeElement = badge !== undefined && !mini
47
+ ? ED.createElement('span', {
48
+ className: [
49
+ 'ed-ml-auto ed-px-2 ed-py-0.5 ed-text-xs ed-font-medium ed-rounded-full',
50
+ badgeColorClasses[badgeColor] || badgeColorClasses.blue,
51
+ ].join(' '),
52
+ }, typeof badge === 'number' && badge > 99 ? '99+' : badge)
53
+ : null;
54
+
55
+ const iconElement = icon
56
+ ? ED.createElement('span', {
57
+ className: `${mini ? '' : 'ed-mr-3'} ed-flex-shrink-0 ed-w-5 ed-h-5 ed-flex ed-items-center ed-justify-center`,
58
+ children: typeof icon === 'string' ? ED.createElement('i', { className: icon }) : icon,
59
+ })
60
+ : null;
61
+
62
+ const expandIcon = hasChildren && !mini
63
+ ? ED.createElement('svg', {
64
+ className: `ed-ml-auto ed-w-4 ed-h-4 ed-transition-transform ed-duration-200 ${expanded ? 'ed-rotate-90' : ''} ${active ? 'ed-text-blue-500' : 'ed-text-gray-400'}`,
65
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
66
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M9 5l7 7-7 7' }),
67
+ })
68
+ : null;
69
+
70
+ const labelElement = !mini
71
+ ? ED.createElement('span', { className: 'ed-flex-1 ed-truncate' }, label)
72
+ : null;
73
+
74
+ const tooltipElement = mini
75
+ ? ED.createElement('div', {
76
+ className: 'ed-absolute ed-left-full ed-ml-2 ed-px-2 ed-py-1 ed-bg-gray-900 ed-text-white ed-text-xs ed-rounded ed-whitespace-nowrap ed-opacity-0 group-hover:ed-opacity-100 ed-transition-opacity ed-pointer-events-none ed-z-50',
77
+ }, label)
78
+ : null;
79
+
80
+ const handleClick = (e) => {
81
+ if (disabled) return;
82
+ if (hasChildren) {
83
+ onToggle?.();
84
+ } else {
85
+ onClick?.(e);
86
+ }
87
+ };
88
+
89
+ const nestedElement = hasChildren && expanded && !mini
90
+ ? ED.createElement('div', {
91
+ className: 'ed-ml-4 ed-mt-1 ed-space-y-0.5 ed-border-l ed-border-gray-200 ed-pl-3',
92
+ children: nestedItems,
93
+ })
94
+ : null;
95
+
96
+ return ED.createElement('div', {
97
+ className: 'ed-relative',
98
+ style: { paddingLeft: `${indent * 8}px` },
99
+ }, [
100
+ ED.createElement('a', {
101
+ key: 'item',
102
+ href: disabled ? undefined : href,
103
+ className: itemClasses,
104
+ style,
105
+ onClick: handleClick,
106
+ 'aria-current': active ? 'page' : undefined,
107
+ role: 'menuitem',
108
+ tabIndex: disabled ? -1 : 0,
109
+ children: [iconElement, labelElement, badgeElement, expandIcon, tooltipElement].filter(Boolean),
110
+ }),
111
+ nestedElement,
112
+ ]);
113
+ }
114
+
115
+ SidebarItem.displayName = 'SidebarItem';
116
+
117
+ function Sidebar(props) {
118
+ const {
119
+ children,
120
+ items = [],
121
+ collapsed = false,
122
+ mini = false,
123
+ overlay = false,
124
+ responsive = true,
125
+ width = 256,
126
+ miniWidth = 64,
127
+ className = '',
128
+ style = {},
129
+ header,
130
+ footer,
131
+ theme = 'light',
132
+ bordered = true,
133
+ floating = false,
134
+ onClose,
135
+ open = true,
136
+ onToggle,
137
+ activeKey,
138
+ onItemClick,
139
+ expandedKeys = [],
140
+ onExpand,
141
+ breakPoint = 'lg',
142
+ zIndex = 30,
143
+ overlayOpacity = 0.5,
144
+ } = props;
145
+
146
+ const effectiveWidth = mini ? miniWidth : (collapsed ? miniWidth : width);
147
+ const isDark = theme === 'dark';
148
+
149
+ const sidebarClasses = [
150
+ 'ed-flex ed-flex-col ed-h-full ed-transition-all ed-duration-300 ed-ease-in-out',
151
+ overlay ? 'ed-fixed ed-top-0 ed-left-0 ed-bottom-0' : '',
152
+ isDark ? 'ed-bg-gray-900 ed-text-gray-100' : 'ed-bg-white ed-text-gray-800',
153
+ bordered ? 'ed-border-r ed-border-gray-200' : '',
154
+ floating && !overlay ? 'ed-rounded-lg ed-m-2 ed-shadow-lg' : '',
155
+ !open && !overlay ? 'ed--translate-x-full' : '',
156
+ 'ed-overflow-y-auto ed-overflow-x-hidden',
157
+ className,
158
+ ].filter(Boolean).join(' ');
159
+
160
+ const renderItems = (menuItems, depth = 0) =>
161
+ menuItems.map((item, idx) =>
162
+ ED.createElement(SidebarItem, {
163
+ key: item.key || idx,
164
+ label: item.label,
165
+ icon: item.icon,
166
+ badge: item.badge,
167
+ badgeColor: item.badgeColor,
168
+ active: activeKey === item.key || item.active,
169
+ disabled: item.disabled,
170
+ href: item.href,
171
+ onClick: () => onItemClick?.(item),
172
+ mini,
173
+ expanded: expandedKeys.includes(item.key),
174
+ onToggle: () => onExpand?.(item.key),
175
+ depth,
176
+ children: item.children ? renderItems(item.children, depth + 1) : undefined,
177
+ })
178
+ );
179
+
180
+ const headerElement = header
181
+ ? ED.createElement('div', {
182
+ className: [
183
+ 'ed-flex ed-items-center ed-px-4 ed-py-4 ed-border-b',
184
+ isDark ? 'ed-border-gray-700' : 'ed-border-gray-200',
185
+ mini ? 'ed-justify-center ed-px-2' : 'ed-justify-between',
186
+ ].filter(Boolean).join(' '),
187
+ }, [
188
+ typeof header === 'string'
189
+ ? ED.createElement('span', { key: 'header-text', className: 'ed-font-bold ed-text-lg ed-truncate' }, header)
190
+ : header,
191
+ !mini && onToggle
192
+ ? ED.createElement('button', {
193
+ key: 'collapse-btn',
194
+ className: 'ed-p-1 ed-rounded ed-text-gray-400 hover:ed-text-gray-600 hover:ed-bg-gray-100 ed-transition-colors',
195
+ onClick: onToggle,
196
+ 'aria-label': 'Toggle sidebar',
197
+ children: ED.createElement('svg', {
198
+ className: 'ed-w-5 ed-h-5',
199
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
200
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M11 19l-7-7 7-7m8 14l-7-7 7-7' }),
201
+ }),
202
+ })
203
+ : null,
204
+ ].filter(Boolean))
205
+ : null;
206
+
207
+ const bodyElement = ED.createElement('div', {
208
+ className: 'ed-flex-1 ed-overflow-y-auto ed-py-2 ed-px-2',
209
+ role: 'menu',
210
+ children: items.length > 0 ? renderItems(items) : children,
211
+ });
212
+
213
+ const footerElement = footer
214
+ ? ED.createElement('div', {
215
+ className: [
216
+ 'ed-px-4 ed-py-3 ed-border-t',
217
+ isDark ? 'ed-border-gray-700' : 'ed-border-gray-200',
218
+ mini ? 'ed-px-2' : '',
219
+ ].filter(Boolean).join(' '),
220
+ }, footer)
221
+ : null;
222
+
223
+ const overlayElement = overlay && open
224
+ ? ED.createElement('div', {
225
+ className: 'ed-fixed ed-inset-0 ed-bg-black ed-transition-opacity ed-duration-300',
226
+ style: { zIndex: zIndex - 1, opacity: overlayOpacity },
227
+ onClick: onClose,
228
+ })
229
+ : null;
230
+
231
+ return ED.createElement(ED.Fragment, null, [
232
+ overlayElement,
233
+ ED.createElement('aside', {
234
+ key: 'sidebar',
235
+ className: sidebarClasses,
236
+ style: {
237
+ width: effectiveWidth,
238
+ minWidth: effectiveWidth,
239
+ zIndex,
240
+ ...style,
241
+ },
242
+ children: [headerElement, bodyElement, footerElement].filter(Boolean),
243
+ }),
244
+ ]);
245
+ }
246
+
247
+ Sidebar.displayName = 'Sidebar';
248
+ Sidebar.Item = SidebarItem;
249
+
250
+ module.exports = Sidebar;