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.
- package/LICENSE +21 -0
- package/dist/elementdrawing.min.js +3 -0
- package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
- package/dist/elementdrawing.min.js.map +1 -0
- package/dist/index.html +1 -0
- package/package.json +127 -0
- package/src/core/bridge.h +855 -0
- package/src/core/diff.c +900 -0
- package/src/core/element.c +1078 -0
- package/src/core/event.c +813 -0
- package/src/core/fiber.c +1027 -0
- package/src/core/hooks.c +919 -0
- package/src/core/renderer.c +963 -0
- package/src/core/scheduler.c +702 -0
- package/src/core/state.c +803 -0
- package/src/css/animations.css +779 -0
- package/src/css/base.css +615 -0
- package/src/css/components.css +1311 -0
- package/src/css/tailwind.css +370 -0
- package/src/css/themes.css +517 -0
- package/src/css/utilities.css +475 -0
- package/src/index.js +746 -0
- package/src/js/animation.js +655 -0
- package/src/js/dom.js +665 -0
- package/src/js/events.js +585 -0
- package/src/js/http.js +446 -0
- package/src/js/index.js +26 -0
- package/src/js/router.js +483 -0
- package/src/js/store.js +539 -0
- package/src/js/utils.js +593 -0
- package/src/js/validator.js +529 -0
- package/src/jsx/components/Accordion.jsx +210 -0
- package/src/jsx/components/Alert.jsx +169 -0
- package/src/jsx/components/Avatar.jsx +214 -0
- package/src/jsx/components/Badge.jsx +136 -0
- package/src/jsx/components/Breadcrumb.jsx +200 -0
- package/src/jsx/components/Button.jsx +188 -0
- package/src/jsx/components/Card.jsx +192 -0
- package/src/jsx/components/Carousel.jsx +278 -0
- package/src/jsx/components/Checkbox.jsx +215 -0
- package/src/jsx/components/Dialog.jsx +242 -0
- package/src/jsx/components/Drawer.jsx +190 -0
- package/src/jsx/components/Dropdown.jsx +268 -0
- package/src/jsx/components/Form.jsx +274 -0
- package/src/jsx/components/Input.jsx +285 -0
- package/src/jsx/components/Menu.jsx +276 -0
- package/src/jsx/components/Modal.jsx +274 -0
- package/src/jsx/components/Navbar.jsx +292 -0
- package/src/jsx/components/Pagination.jsx +268 -0
- package/src/jsx/components/Progress.jsx +252 -0
- package/src/jsx/components/Radio.jsx +208 -0
- package/src/jsx/components/Select.jsx +397 -0
- package/src/jsx/components/Sidebar.jsx +250 -0
- package/src/jsx/components/Slider.jsx +310 -0
- package/src/jsx/components/Spinner.jsx +198 -0
- package/src/jsx/components/Switch.jsx +201 -0
- package/src/jsx/components/Table.jsx +332 -0
- package/src/jsx/components/Tabs.jsx +227 -0
- package/src/jsx/components/Textarea.jsx +212 -0
- package/src/jsx/components/Toast.jsx +270 -0
- package/src/jsx/components/Tooltip.jsx +178 -0
- package/src/jsx/components/Typography.jsx +299 -0
- package/src/jsx/components/index.jsx +70 -0
- package/src/jsx/core/element.js +3 -0
- package/src/jsx/hooks/index.js +356 -0
- package/src/jsx/hooks/useCallback.js +472 -0
- package/src/jsx/hooks/useContext.js +586 -0
- package/src/jsx/hooks/useEffect.js +704 -0
- package/src/jsx/hooks/useLayoutEffect.js +508 -0
- package/src/jsx/hooks/useMemo.js +689 -0
- package/src/jsx/hooks/useReducer.js +729 -0
- package/src/jsx/hooks/useRef.js +542 -0
- package/src/jsx/hooks/useState.js +854 -0
- package/src/jsx/runtime/commit.js +903 -0
- package/src/jsx/runtime/createElement.js +860 -0
- package/src/jsx/runtime/index.js +356 -0
- package/src/jsx/runtime/reconcile.js +687 -0
- 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;
|