@syuttechnologies/layout 1.0.2 → 1.0.21

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 (53) hide show
  1. package/README.md +536 -0
  2. package/dist/components/ChangePasswordModal.d.ts.map +1 -1
  3. package/dist/components/ChangePasswordModal.js +22 -11
  4. package/dist/components/EnterpriseLayout.d.ts.map +1 -1
  5. package/dist/components/EnterpriseLayout.js +21 -6
  6. package/dist/components/ui/ActionMenu/ActionMenu.d.ts +52 -0
  7. package/dist/components/ui/ActionMenu/ActionMenu.d.ts.map +1 -0
  8. package/dist/components/ui/ActionMenu/ActionMenu.js +116 -0
  9. package/dist/components/ui/ActionMenu/index.d.ts +3 -0
  10. package/dist/components/ui/ActionMenu/index.d.ts.map +1 -0
  11. package/dist/components/ui/ActionMenu/index.js +2 -0
  12. package/dist/components/ui/ModuleHeader/ModuleHeader.d.ts +90 -0
  13. package/dist/components/ui/ModuleHeader/ModuleHeader.d.ts.map +1 -0
  14. package/dist/components/ui/ModuleHeader/ModuleHeader.js +433 -0
  15. package/dist/components/ui/ModuleHeader/index.d.ts +3 -0
  16. package/dist/components/ui/ModuleHeader/index.d.ts.map +1 -0
  17. package/dist/components/ui/ModuleHeader/index.js +1 -0
  18. package/dist/components/ui/SyutGrid/SyutGrid.d.ts +74 -0
  19. package/dist/components/ui/SyutGrid/SyutGrid.d.ts.map +1 -0
  20. package/dist/components/ui/SyutGrid/SyutGrid.js +306 -0
  21. package/dist/components/ui/SyutGrid/index.d.ts +3 -0
  22. package/dist/components/ui/SyutGrid/index.d.ts.map +1 -0
  23. package/dist/components/ui/SyutGrid/index.js +2 -0
  24. package/dist/components/ui/SyutSelect/SyutSelectUnified.d.ts +128 -0
  25. package/dist/components/ui/SyutSelect/SyutSelectUnified.d.ts.map +1 -0
  26. package/dist/components/ui/SyutSelect/SyutSelectUnified.js +679 -0
  27. package/dist/components/ui/SyutSelect/index.d.ts +3 -0
  28. package/dist/components/ui/SyutSelect/index.d.ts.map +1 -0
  29. package/dist/components/ui/SyutSelect/index.js +2 -0
  30. package/dist/icon-collection/icon-systems.d.ts +89 -0
  31. package/dist/icon-collection/icon-systems.d.ts.map +1 -0
  32. package/dist/icon-collection/icon-systems.js +70 -0
  33. package/dist/icon-collection/index.d.ts +4 -0
  34. package/dist/icon-collection/index.d.ts.map +1 -0
  35. package/dist/icon-collection/index.js +8 -0
  36. package/dist/index.d.ts +12 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +19 -1
  39. package/package.json +9 -4
  40. package/src/components/ChangePasswordModal.tsx +26 -14
  41. package/src/components/EnterpriseLayout.tsx +23 -8
  42. package/src/components/ui/ActionMenu/ActionMenu.tsx +222 -0
  43. package/src/components/ui/ActionMenu/index.ts +2 -0
  44. package/src/components/ui/ModuleHeader/ModuleHeader.tsx +722 -0
  45. package/src/components/ui/ModuleHeader/index.ts +9 -0
  46. package/src/components/ui/SyutGrid/SyutGrid.tsx +483 -0
  47. package/src/components/ui/SyutGrid/index.ts +2 -0
  48. package/src/components/ui/SyutSelect/SyutSelectUnified.tsx +1115 -0
  49. package/src/components/ui/SyutSelect/index.ts +3 -0
  50. package/src/icon-collection/icon-systems.tsx +464 -0
  51. package/src/icon-collection/index.ts +13 -0
  52. package/src/index.ts +47 -1
  53. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,679 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
3
+ import Select from 'react-select';
4
+ import CreatableSelect from 'react-select/creatable';
5
+ import AsyncSelect from 'react-select/async';
6
+ import AsyncCreatableSelect from 'react-select/async-creatable';
7
+ // ============================================================================
8
+ // INJECT SELF-CONTAINED STYLES
9
+ // ============================================================================
10
+ if (typeof document !== 'undefined') {
11
+ const styleId = 'syut-select-styles';
12
+ if (!document.getElementById(styleId)) {
13
+ const style = document.createElement('style');
14
+ style.id = styleId;
15
+ style.textContent = `
16
+ /* SyutSelectUnified Component Styles */
17
+ @keyframes spin {
18
+ from { transform: rotate(0deg); }
19
+ to { transform: rotate(360deg); }
20
+ }
21
+
22
+ .syut-select-container {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 0.25rem;
26
+ width: 100%;
27
+ }
28
+
29
+ .syut-select-label {
30
+ font-size: 0.875rem;
31
+ font-weight: 500;
32
+ color: var(--text-primary, #1e293b);
33
+ margin-bottom: 0.25rem;
34
+ }
35
+
36
+ .syut-select-required {
37
+ color: var(--danger, #ef4444);
38
+ margin-left: 0.25rem;
39
+ }
40
+
41
+ .syut-select-error {
42
+ font-size: 0.75rem;
43
+ color: var(--danger, #ef4444);
44
+ margin-top: 0.25rem;
45
+ }
46
+
47
+ .syut-select-helper {
48
+ font-size: 0.75rem;
49
+ color: var(--text-muted, #64748b);
50
+ margin-top: 0.25rem;
51
+ }
52
+
53
+ /* React-select overrides for consistent theming */
54
+ .syut-select__control {
55
+ border-color: var(--border-color, #e2e8f0) !important;
56
+ background: var(--bg-primary, #ffffff) !important;
57
+ }
58
+
59
+ .syut-select__control--is-focused {
60
+ border-color: var(--primary, #3b82f6) !important;
61
+ box-shadow: 0 0 0 1px var(--primary, #3b82f6) !important;
62
+ }
63
+
64
+ .syut-select__menu {
65
+ background: var(--bg-primary, #ffffff) !important;
66
+ border: 1px solid var(--border-color, #e2e8f0) !important;
67
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
68
+ z-index: 9999 !important;
69
+ }
70
+
71
+ .syut-select__option {
72
+ background: transparent !important;
73
+ color: var(--text-primary, #1e293b) !important;
74
+ }
75
+
76
+ .syut-select__option--is-focused {
77
+ background: var(--bg-secondary, #f8fafc) !important;
78
+ }
79
+
80
+ .syut-select__option--is-selected {
81
+ background: var(--primary, #3b82f6) !important;
82
+ color: white !important;
83
+ }
84
+
85
+ .syut-select__single-value {
86
+ color: var(--text-primary, #1e293b) !important;
87
+ }
88
+
89
+ .syut-select__multi-value {
90
+ background: var(--bg-tertiary, #e0ebf7) !important;
91
+ border-radius: 4px !important;
92
+ }
93
+
94
+ .syut-select__multi-value__label {
95
+ color: var(--text-primary, #1e293b) !important;
96
+ }
97
+
98
+ .syut-select__multi-value__remove:hover {
99
+ background: var(--danger, #ef4444) !important;
100
+ color: white !important;
101
+ }
102
+
103
+ .syut-select__placeholder {
104
+ color: var(--text-muted, #64748b) !important;
105
+ }
106
+
107
+ .syut-select__input-container {
108
+ color: var(--text-primary, #1e293b) !important;
109
+ }
110
+ `;
111
+ document.head.appendChild(style);
112
+ }
113
+ }
114
+ // ============================================================================
115
+ // SIZE CONFIGURATIONS
116
+ // ============================================================================
117
+ const sizeConfig = {
118
+ sm: { height: '32px', fontSize: '0.8rem', padding: '0.375rem 0.5rem', checkboxSize: '14px' },
119
+ md: { height: '38px', fontSize: '0.875rem', padding: '0.5rem 0.75rem', checkboxSize: '16px' },
120
+ lg: { height: '44px', fontSize: '1rem', padding: '0.625rem 1rem', checkboxSize: '18px' },
121
+ };
122
+ // ============================================================================
123
+ // REACT-SELECT CUSTOM STYLES
124
+ // ============================================================================
125
+ const getReactSelectStyles = (size, hasError) => {
126
+ const config = sizeConfig[size];
127
+ return {
128
+ control: (base, state) => ({
129
+ ...base,
130
+ minHeight: config.height,
131
+ fontSize: config.fontSize,
132
+ borderColor: hasError
133
+ ? 'var(--danger, #ef4444)'
134
+ : state.isFocused
135
+ ? 'var(--primary)'
136
+ : 'var(--border-color)',
137
+ boxShadow: state.isFocused
138
+ ? `0 0 0 1px ${hasError ? 'var(--danger, #ef4444)' : 'var(--primary)'}`
139
+ : 'none',
140
+ backgroundColor: state.isDisabled ? 'var(--bg-secondary)' : 'var(--bg-primary)',
141
+ '&:hover': {
142
+ borderColor: hasError ? 'var(--danger, #ef4444)' : 'var(--primary)',
143
+ },
144
+ cursor: state.isDisabled ? 'not-allowed' : 'pointer',
145
+ }),
146
+ menu: (base) => ({
147
+ ...base,
148
+ backgroundColor: 'var(--bg-primary)',
149
+ border: '1px solid var(--border-color)',
150
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
151
+ zIndex: 9999,
152
+ }),
153
+ menuList: (base) => ({
154
+ ...base,
155
+ padding: '4px',
156
+ }),
157
+ option: (base, state) => ({
158
+ ...base,
159
+ fontSize: config.fontSize,
160
+ padding: config.padding,
161
+ backgroundColor: state.isSelected
162
+ ? 'var(--primary)'
163
+ : state.isFocused
164
+ ? 'var(--bg-secondary)'
165
+ : 'transparent',
166
+ color: state.isSelected ? 'white' : 'var(--text-primary)',
167
+ cursor: state.isDisabled ? 'not-allowed' : 'pointer',
168
+ borderRadius: '4px',
169
+ }),
170
+ singleValue: (base) => ({
171
+ ...base,
172
+ color: 'var(--text-primary)',
173
+ }),
174
+ multiValue: (base) => ({
175
+ ...base,
176
+ backgroundColor: 'var(--bg-tertiary, #e0ebf7)',
177
+ borderRadius: '4px',
178
+ }),
179
+ multiValueLabel: (base) => ({
180
+ ...base,
181
+ color: 'var(--text-primary)',
182
+ fontSize: config.fontSize,
183
+ padding: '2px 6px',
184
+ }),
185
+ multiValueRemove: (base) => ({
186
+ ...base,
187
+ color: 'var(--text-muted)',
188
+ '&:hover': {
189
+ backgroundColor: 'var(--danger, #ef4444)',
190
+ color: 'white',
191
+ },
192
+ }),
193
+ placeholder: (base) => ({
194
+ ...base,
195
+ color: 'var(--text-muted)',
196
+ }),
197
+ input: (base) => ({
198
+ ...base,
199
+ color: 'var(--text-primary)',
200
+ }),
201
+ dropdownIndicator: (base, state) => ({
202
+ ...base,
203
+ color: 'var(--text-muted)',
204
+ transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined,
205
+ transition: 'transform 0.2s ease',
206
+ }),
207
+ clearIndicator: (base) => ({
208
+ ...base,
209
+ color: 'var(--text-muted)',
210
+ '&:hover': {
211
+ color: 'var(--danger, #ef4444)',
212
+ },
213
+ }),
214
+ };
215
+ };
216
+ // ============================================================================
217
+ // CHECKBOX VARIANT COMPONENT
218
+ // ============================================================================
219
+ const CheckboxVariant = ({ id, options: initialOptions = [], value = [], onChange, placeholder = 'Select...', isDisabled = false, isLoading: externalLoading = false, isMulti = true, isAsync = false, loadOptions, defaultOptions, cacheOptions = true, error, size = 'md', showSelectAll = true, selectAllLabel = 'Select All', maxMenuHeight = 250, searchPlaceholder = 'Search...', noOptionsMessage = 'No options found', debounceDelay = 300, minSearchLength = 1, loadingMessage = 'Loading...', minSearchMessage = 'Type to search...', config, }) => {
220
+ const [isOpen, setIsOpen] = useState(false);
221
+ const [searchQuery, setSearchQuery] = useState('');
222
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
223
+ const [asyncOptions, setAsyncOptions] = useState([]);
224
+ const [isSearching, setIsSearching] = useState(false);
225
+ const [hasSearched, setHasSearched] = useState(false);
226
+ const containerRef = useRef(null);
227
+ const searchInputRef = useRef(null);
228
+ const optionsListRef = useRef(null);
229
+ const debounceTimerRef = useRef(null);
230
+ const optionsCacheRef = useRef(new Map());
231
+ // Determine which options to use (async loaded or initial)
232
+ const options = isAsync ? asyncOptions : initialOptions;
233
+ const isLoading = externalLoading || isSearching;
234
+ // Load default options when dropdown opens (for async mode)
235
+ useEffect(() => {
236
+ if (isAsync && isOpen && !hasSearched) {
237
+ if (defaultOptions === true && loadOptions) {
238
+ // Load initial options with empty string
239
+ setIsSearching(true);
240
+ const result = loadOptions('', (loadedOptions) => {
241
+ setAsyncOptions(loadedOptions);
242
+ setIsSearching(false);
243
+ });
244
+ if (result instanceof Promise) {
245
+ result.then((loadedOptions) => {
246
+ setAsyncOptions(loadedOptions);
247
+ setIsSearching(false);
248
+ }).catch(() => {
249
+ setIsSearching(false);
250
+ });
251
+ }
252
+ }
253
+ else if (Array.isArray(defaultOptions)) {
254
+ setAsyncOptions(defaultOptions);
255
+ }
256
+ }
257
+ }, [isAsync, isOpen, hasSearched, defaultOptions, loadOptions]);
258
+ // Handle async search with debounce
259
+ useEffect(() => {
260
+ if (!isAsync || !loadOptions)
261
+ return;
262
+ // Clear previous timer
263
+ if (debounceTimerRef.current) {
264
+ clearTimeout(debounceTimerRef.current);
265
+ }
266
+ // Check minimum search length
267
+ if (searchQuery.length < minSearchLength) {
268
+ if (searchQuery.length === 0 && Array.isArray(defaultOptions)) {
269
+ setAsyncOptions(defaultOptions);
270
+ }
271
+ return;
272
+ }
273
+ // Check cache first
274
+ if (cacheOptions && optionsCacheRef.current.has(searchQuery)) {
275
+ setAsyncOptions(optionsCacheRef.current.get(searchQuery) || []);
276
+ return;
277
+ }
278
+ // Debounce the API call
279
+ debounceTimerRef.current = setTimeout(() => {
280
+ setIsSearching(true);
281
+ setHasSearched(true);
282
+ const result = loadOptions(searchQuery, (loadedOptions) => {
283
+ setAsyncOptions(loadedOptions);
284
+ if (cacheOptions) {
285
+ optionsCacheRef.current.set(searchQuery, loadedOptions);
286
+ }
287
+ setIsSearching(false);
288
+ });
289
+ if (result instanceof Promise) {
290
+ result.then((loadedOptions) => {
291
+ setAsyncOptions(loadedOptions);
292
+ if (cacheOptions) {
293
+ optionsCacheRef.current.set(searchQuery, loadedOptions);
294
+ }
295
+ setIsSearching(false);
296
+ }).catch(() => {
297
+ setIsSearching(false);
298
+ });
299
+ }
300
+ }, debounceDelay);
301
+ return () => {
302
+ if (debounceTimerRef.current) {
303
+ clearTimeout(debounceTimerRef.current);
304
+ }
305
+ };
306
+ }, [searchQuery, isAsync, loadOptions, debounceDelay, minSearchLength, cacheOptions, defaultOptions]);
307
+ // Normalize value to array
308
+ const selectedValues = useMemo(() => {
309
+ if (!value)
310
+ return [];
311
+ return Array.isArray(value) ? value : [value];
312
+ }, [value]);
313
+ // Filter options based on search query (only for non-async mode)
314
+ const filteredOptions = useMemo(() => {
315
+ if (isAsync)
316
+ return options; // Async filtering is done by the API
317
+ if (!searchQuery.trim())
318
+ return options;
319
+ const query = searchQuery.toLowerCase();
320
+ return options.filter(option => option.label.toLowerCase().includes(query) ||
321
+ option.value.toLowerCase().includes(query));
322
+ }, [options, searchQuery, isAsync]);
323
+ // Check if all filtered options are selected
324
+ const allFilteredSelected = useMemo(() => {
325
+ const selectableOptions = filteredOptions.filter(opt => !opt.isDisabled);
326
+ if (selectableOptions.length === 0)
327
+ return false;
328
+ return selectableOptions.every(opt => selectedValues.includes(opt.value));
329
+ }, [filteredOptions, selectedValues]);
330
+ // Get selected options with their details
331
+ const selectedOptions = useMemo(() => {
332
+ return options.filter(opt => selectedValues.includes(opt.value));
333
+ }, [options, selectedValues]);
334
+ // Handle click outside to close dropdown
335
+ useEffect(() => {
336
+ const handleClickOutside = (event) => {
337
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
338
+ setIsOpen(false);
339
+ setSearchQuery('');
340
+ }
341
+ };
342
+ if (isOpen) {
343
+ document.addEventListener('mousedown', handleClickOutside);
344
+ }
345
+ return () => {
346
+ document.removeEventListener('mousedown', handleClickOutside);
347
+ };
348
+ }, [isOpen]);
349
+ // Focus search input when dropdown opens
350
+ useEffect(() => {
351
+ if (isOpen && searchInputRef.current) {
352
+ searchInputRef.current.focus();
353
+ }
354
+ if (!isOpen) {
355
+ setHighlightedIndex(-1);
356
+ }
357
+ }, [isOpen]);
358
+ // Reset highlighted index when filtered options change
359
+ useEffect(() => {
360
+ setHighlightedIndex(-1);
361
+ }, [filteredOptions.length]);
362
+ // Scroll highlighted option into view
363
+ useEffect(() => {
364
+ if (highlightedIndex >= 0 && optionsListRef.current) {
365
+ const highlightedElement = optionsListRef.current.children[highlightedIndex];
366
+ if (highlightedElement) {
367
+ highlightedElement.scrollIntoView({ block: 'nearest' });
368
+ }
369
+ }
370
+ }, [highlightedIndex]);
371
+ // Handle option toggle
372
+ const handleOptionToggle = useCallback((optionValue) => {
373
+ if (isMulti) {
374
+ const newValue = selectedValues.includes(optionValue)
375
+ ? selectedValues.filter(v => v !== optionValue)
376
+ : [...selectedValues, optionValue];
377
+ onChange?.(newValue);
378
+ }
379
+ else {
380
+ onChange?.(optionValue);
381
+ setIsOpen(false);
382
+ setSearchQuery('');
383
+ }
384
+ }, [selectedValues, onChange, isMulti]);
385
+ // Handle select all toggle
386
+ const handleSelectAllToggle = useCallback(() => {
387
+ const selectableOptions = filteredOptions.filter(opt => !opt.isDisabled);
388
+ const selectableValues = selectableOptions.map(opt => opt.value);
389
+ if (allFilteredSelected) {
390
+ const newValue = selectedValues.filter(v => !selectableValues.includes(v));
391
+ onChange?.(newValue);
392
+ }
393
+ else {
394
+ const newValue = [...new Set([...selectedValues, ...selectableValues])];
395
+ onChange?.(newValue);
396
+ }
397
+ }, [filteredOptions, allFilteredSelected, selectedValues, onChange]);
398
+ // Remove a selected item
399
+ const removeSelection = useCallback((optionValue, e) => {
400
+ e.stopPropagation();
401
+ if (isMulti) {
402
+ onChange?.(selectedValues.filter(v => v !== optionValue));
403
+ }
404
+ else {
405
+ onChange?.(null);
406
+ }
407
+ }, [selectedValues, onChange, isMulti]);
408
+ // Handle keyboard navigation
409
+ const handleKeyDown = useCallback((e) => {
410
+ const selectableOptions = filteredOptions.filter(opt => !opt.isDisabled);
411
+ switch (e.key) {
412
+ case 'ArrowDown':
413
+ e.preventDefault();
414
+ e.stopPropagation();
415
+ setHighlightedIndex(prev => {
416
+ const nextIndex = prev + 1;
417
+ return nextIndex >= selectableOptions.length ? 0 : nextIndex;
418
+ });
419
+ break;
420
+ case 'ArrowUp':
421
+ e.preventDefault();
422
+ e.stopPropagation();
423
+ setHighlightedIndex(prev => {
424
+ const nextIndex = prev - 1;
425
+ return nextIndex < 0 ? selectableOptions.length - 1 : nextIndex;
426
+ });
427
+ break;
428
+ case 'Enter':
429
+ case ' ':
430
+ e.preventDefault();
431
+ e.stopPropagation();
432
+ if (highlightedIndex >= 0 && highlightedIndex < selectableOptions.length) {
433
+ handleOptionToggle(selectableOptions[highlightedIndex].value);
434
+ }
435
+ break;
436
+ case 'Escape':
437
+ e.preventDefault();
438
+ e.stopPropagation();
439
+ setIsOpen(false);
440
+ setSearchQuery('');
441
+ break;
442
+ default:
443
+ e.stopPropagation();
444
+ break;
445
+ }
446
+ }, [filteredOptions, highlightedIndex, handleOptionToggle]);
447
+ return (_jsxs("div", { ref: containerRef, style: { position: 'relative' }, children: [_jsxs("div", { id: id, onClick: () => !isDisabled && setIsOpen(!isOpen), style: {
448
+ width: '100%',
449
+ minHeight: config.height,
450
+ maxHeight: '120px',
451
+ overflowY: 'auto',
452
+ padding: '0.25rem 0.5rem',
453
+ paddingRight: '2rem',
454
+ fontSize: config.fontSize,
455
+ display: 'flex',
456
+ flexWrap: 'wrap',
457
+ alignItems: 'flex-start',
458
+ alignContent: 'flex-start',
459
+ gap: '0.25rem',
460
+ background: isDisabled ? 'var(--bg-secondary)' : 'var(--bg-primary)',
461
+ border: `1px solid ${error ? 'var(--danger, #ef4444)' : isOpen ? 'var(--primary)' : 'var(--border-color)'}`,
462
+ borderRadius: 'var(--radius-sm, 4px)',
463
+ cursor: isDisabled ? 'not-allowed' : 'pointer',
464
+ position: 'relative',
465
+ transition: 'all 0.2s ease',
466
+ boxShadow: isOpen ? '0 0 0 1px var(--primary)' : 'none',
467
+ }, children: [isLoading ? (_jsx("span", { style: { color: 'var(--text-muted)', padding: '0.125rem 0.25rem' }, children: "Loading..." })) : selectedOptions.length === 0 ? (_jsx("span", { style: { color: 'var(--text-muted)', padding: '0.125rem 0.25rem' }, children: placeholder })) : (selectedOptions.map(option => (_jsxs("span", { style: {
468
+ display: 'inline-flex',
469
+ alignItems: 'center',
470
+ gap: '0.25rem',
471
+ padding: '0.125rem 0.375rem',
472
+ background: 'var(--bg-tertiary, #e0ebf7)',
473
+ border: '1px solid var(--border-color)',
474
+ borderRadius: '0.25rem',
475
+ fontSize: '0.75rem',
476
+ color: 'var(--text-primary)',
477
+ fontWeight: 500,
478
+ maxWidth: '150px'
479
+ }, children: [_jsx("span", { style: {
480
+ overflow: 'hidden',
481
+ textOverflow: 'ellipsis',
482
+ whiteSpace: 'nowrap'
483
+ }, children: option.label }), !isDisabled && (_jsx("button", { type: "button", onClick: (e) => removeSelection(option.value, e), style: {
484
+ display: 'inline-flex',
485
+ alignItems: 'center',
486
+ justifyContent: 'center',
487
+ width: '14px',
488
+ height: '14px',
489
+ padding: 0,
490
+ border: 'none',
491
+ background: 'transparent',
492
+ cursor: 'pointer',
493
+ borderRadius: '50%',
494
+ color: 'var(--text-muted)',
495
+ flexShrink: 0
496
+ }, onMouseEnter: (e) => {
497
+ e.currentTarget.style.background = 'var(--border-color)';
498
+ e.currentTarget.style.color = 'var(--text-primary)';
499
+ }, onMouseLeave: (e) => {
500
+ e.currentTarget.style.background = 'transparent';
501
+ e.currentTarget.style.color = 'var(--text-muted)';
502
+ }, children: _jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", children: [_jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) }))] }, option.value)))), _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: {
503
+ position: 'absolute',
504
+ right: '0.5rem',
505
+ top: '50%',
506
+ transform: isOpen ? 'translateY(-50%) rotate(180deg)' : 'translateY(-50%) rotate(0deg)',
507
+ transition: 'transform 0.2s ease',
508
+ color: 'var(--text-muted)',
509
+ flexShrink: 0,
510
+ }, children: _jsx("polyline", { points: "6 9 12 15 18 9" }) })] }), isOpen && (_jsxs("div", { style: {
511
+ position: 'absolute',
512
+ top: '100%',
513
+ left: 0,
514
+ right: 0,
515
+ marginTop: '4px',
516
+ background: 'var(--bg-primary)',
517
+ border: '1px solid var(--border-color)',
518
+ borderRadius: 'var(--radius-sm, 4px)',
519
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
520
+ zIndex: 9999,
521
+ overflow: 'hidden',
522
+ }, children: [_jsx("div", { style: { padding: '0.5rem', borderBottom: '1px solid var(--border-color)' }, children: _jsx("input", { ref: searchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onClick: (e) => e.stopPropagation(), onKeyDown: handleKeyDown, onMouseDown: (e) => e.stopPropagation(), placeholder: searchPlaceholder, style: {
523
+ width: '100%',
524
+ padding: '0.375rem 0.5rem',
525
+ fontSize: config.fontSize,
526
+ border: '1px solid var(--border-color)',
527
+ borderRadius: 'var(--radius-sm, 4px)',
528
+ background: 'var(--bg-primary)',
529
+ color: 'var(--text-primary)',
530
+ outline: 'none',
531
+ }, onFocus: (e) => {
532
+ e.target.style.borderColor = 'var(--primary)';
533
+ }, onBlur: (e) => {
534
+ e.target.style.borderColor = 'var(--border-color)';
535
+ } }) }), isMulti && showSelectAll && filteredOptions.length > 0 && (_jsxs("div", { onClick: handleSelectAllToggle, style: {
536
+ display: 'flex',
537
+ alignItems: 'center',
538
+ gap: '0.5rem',
539
+ padding: config.padding,
540
+ cursor: 'pointer',
541
+ borderBottom: '1px solid var(--border-color)',
542
+ background: 'var(--bg-secondary)',
543
+ fontWeight: 600,
544
+ fontSize: config.fontSize,
545
+ }, onMouseEnter: (e) => {
546
+ e.currentTarget.style.background = 'var(--bg-tertiary, #e2e8f0)';
547
+ }, onMouseLeave: (e) => {
548
+ e.currentTarget.style.background = 'var(--bg-secondary)';
549
+ }, children: [_jsx("input", { type: "checkbox", checked: allFilteredSelected, onChange: () => { }, style: {
550
+ width: config.checkboxSize,
551
+ height: config.checkboxSize,
552
+ accentColor: 'var(--primary)',
553
+ cursor: 'pointer',
554
+ } }), _jsx("span", { style: { color: 'var(--text-primary)' }, children: selectAllLabel })] })), _jsx("div", { ref: optionsListRef, style: {
555
+ maxHeight: `${maxMenuHeight}px`,
556
+ overflowY: 'auto',
557
+ }, children: isLoading ? (_jsxs("div", { style: {
558
+ padding: '1rem',
559
+ textAlign: 'center',
560
+ color: 'var(--text-muted)',
561
+ fontSize: config.fontSize,
562
+ display: 'flex',
563
+ alignItems: 'center',
564
+ justifyContent: 'center',
565
+ gap: '0.5rem',
566
+ }, children: [_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: { animation: 'spin 1s linear infinite' }, children: [_jsx("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: "0.25" }), _jsx("path", { d: "M12 2a10 10 0 0 1 10 10", strokeLinecap: "round" })] }), loadingMessage] })) : isAsync && searchQuery.length < minSearchLength && filteredOptions.length === 0 ? (_jsx("div", { style: {
567
+ padding: '1rem',
568
+ textAlign: 'center',
569
+ color: 'var(--text-muted)',
570
+ fontSize: config.fontSize,
571
+ }, children: minSearchMessage })) : filteredOptions.length === 0 ? (_jsx("div", { style: {
572
+ padding: '1rem',
573
+ textAlign: 'center',
574
+ color: 'var(--text-muted)',
575
+ fontSize: config.fontSize,
576
+ }, children: noOptionsMessage })) : (filteredOptions.filter(opt => !opt.isDisabled).map((option, index) => {
577
+ const isSelected = selectedValues.includes(option.value);
578
+ const isHighlighted = index === highlightedIndex;
579
+ return (_jsxs("div", { onClick: () => handleOptionToggle(option.value), onMouseEnter: () => setHighlightedIndex(index), style: {
580
+ display: 'flex',
581
+ alignItems: 'center',
582
+ gap: '0.5rem',
583
+ padding: config.padding,
584
+ cursor: 'pointer',
585
+ fontSize: config.fontSize,
586
+ background: isHighlighted ? 'var(--bg-secondary)' : 'transparent',
587
+ transition: 'background 0.15s ease',
588
+ }, children: [isMulti && (_jsx("input", { type: "checkbox", checked: isSelected, onChange: () => { }, style: {
589
+ width: config.checkboxSize,
590
+ height: config.checkboxSize,
591
+ accentColor: 'var(--primary)',
592
+ cursor: 'pointer',
593
+ } })), _jsx("span", { style: {
594
+ color: !isMulti && isSelected ? 'var(--primary)' : 'var(--text-primary)',
595
+ flex: 1,
596
+ fontWeight: !isMulti && isSelected ? 600 : 400,
597
+ }, children: option.label }), !isMulti && isSelected && (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "var(--primary)", strokeWidth: "2", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) }))] }, option.value));
598
+ })) })] }))] }));
599
+ };
600
+ // ============================================================================
601
+ // MAIN UNIFIED COMPONENT
602
+ // ============================================================================
603
+ /**
604
+ * SyutSelectUnified - A unified select component with multiple variants
605
+ *
606
+ * @example
607
+ * Single select (default variant):
608
+ * ```tsx
609
+ * <SyutSelectUnified
610
+ * options={options}
611
+ * value={selectedValue}
612
+ * onChange={setSelectedValue}
613
+ * />
614
+ * ```
615
+ *
616
+ * @example
617
+ * Multi-select with checkbox variant:
618
+ * ```tsx
619
+ * <SyutSelectUnified
620
+ * isMulti
621
+ * variant="checkbox"
622
+ * options={options}
623
+ * value={selectedValues}
624
+ * onChange={setSelectedValues}
625
+ * showSelectAll
626
+ * />
627
+ * ```
628
+ *
629
+ * @example
630
+ * Creatable select:
631
+ * ```tsx
632
+ * <SyutSelectUnified
633
+ * isCreatable
634
+ * options={options}
635
+ * onCreateOption={(value) => addOption(value)}
636
+ * />
637
+ * ```
638
+ */
639
+ export function SyutSelectUnified({ id, name, options = [], value, onChange, placeholder = 'Select...', isDisabled = false, isLoading = false, isMulti = false, isSearchable: isSearchableProp = false, searchable, isClearable = true, isCreatable = false, onCreateOption, formatCreateLabel = (inputValue) => `Create "${inputValue}"`, isAsync = false, loadOptions, defaultOptions = true, cacheOptions = true, label, error, helperText, required = false, className = '', size = 'md', autoFocus = false, menuPlacement = 'auto', maxMenuHeight = 300, variant = 'default', showSelectAll = true, selectAllLabel = 'Select All', searchPlaceholder = 'Search...', noOptionsMessage = 'No options found', debounceDelay = 300, minSearchLength = 1, loadingMessage = 'Loading...', minSearchMessage = 'Type to search...', style, }) {
640
+ const config = sizeConfig[size];
641
+ // Handle legacy searchable prop
642
+ const isSearchable = searchable ?? isSearchableProp;
643
+ // Convert value to SelectOption format for react-select
644
+ const reactSelectValue = useMemo(() => {
645
+ if (!value)
646
+ return isMulti ? [] : null;
647
+ if (isMulti) {
648
+ const values = Array.isArray(value) ? value : [value];
649
+ return options.filter(opt => values.includes(opt.value));
650
+ }
651
+ return options.find(opt => opt.value === value) || null;
652
+ }, [value, options, isMulti]);
653
+ // Handle react-select change
654
+ const handleReactSelectChange = useCallback((newValue, actionMeta) => {
655
+ if (isMulti) {
656
+ const values = newValue.map(opt => opt.value);
657
+ onChange?.(values);
658
+ }
659
+ else {
660
+ onChange?.(newValue?.value || null);
661
+ }
662
+ }, [onChange, isMulti]);
663
+ // Get react-select styles
664
+ const customStyles = useMemo(() => getReactSelectStyles(size, !!error), [size, error]);
665
+ // Determine which Select component to use
666
+ const SelectComponent = useMemo(() => {
667
+ if (isAsync && isCreatable)
668
+ return AsyncCreatableSelect;
669
+ if (isAsync)
670
+ return AsyncSelect;
671
+ if (isCreatable)
672
+ return CreatableSelect;
673
+ return Select;
674
+ }, [isAsync, isCreatable]);
675
+ // Use checkbox variant for multi-select checkbox style
676
+ const useCheckboxVariant = variant === 'checkbox';
677
+ return (_jsxs("div", { className: `syut-select-container ${className}`, style: style, children: [label && (_jsxs("label", { className: "syut-select-label", htmlFor: id, children: [label, required && _jsx("span", { className: "syut-select-required", children: "*" })] })), useCheckboxVariant ? (_jsx(CheckboxVariant, { id: id, options: options, value: value, onChange: onChange, placeholder: placeholder, isDisabled: isDisabled, isLoading: isLoading, isMulti: isMulti, isAsync: isAsync, loadOptions: loadOptions, defaultOptions: defaultOptions, cacheOptions: cacheOptions, error: error, size: size, showSelectAll: showSelectAll, selectAllLabel: selectAllLabel, maxMenuHeight: maxMenuHeight, searchPlaceholder: searchPlaceholder, noOptionsMessage: noOptionsMessage, debounceDelay: debounceDelay, minSearchLength: minSearchLength, loadingMessage: loadingMessage, minSearchMessage: minSearchMessage, config: config })) : (_jsx(SelectComponent, { inputId: id, name: name, options: options, value: reactSelectValue, onChange: handleReactSelectChange, placeholder: placeholder, isDisabled: isDisabled, isLoading: isLoading, isMulti: isMulti, isSearchable: isSearchable, isClearable: isClearable, styles: customStyles, noOptionsMessage: () => noOptionsMessage, autoFocus: autoFocus, menuPlacement: menuPlacement, closeMenuOnSelect: !isMulti, maxMenuHeight: maxMenuHeight, classNamePrefix: "syut-select", ...(isCreatable ? { onCreateOption, formatCreateLabel } : {}), ...(isAsync ? { loadOptions, defaultOptions, cacheOptions } : {}) })), error && _jsx("span", { className: "syut-select-error", children: error }), helperText && !error && (_jsx("span", { className: "syut-select-helper", children: helperText }))] }));
678
+ }
679
+ export default SyutSelectUnified;