@goplusvn/core 0.1.19 → 0.1.20

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@goplusvn/core",
3
3
  "description": "GoPlusVN Platform Kit - ERP kernel: layout, RBAC, CRUD, multi-tenant, system pages",
4
- "version": "0.1.19",
4
+ "version": "0.1.20",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org",
@@ -16,6 +16,8 @@ export interface MultiSelectOption {
16
16
  disabled?: boolean;
17
17
  /** Optional icon component to display alongside the option */
18
18
  icon?: React.ComponentType<{ className?: string }>;
19
+ /** Optional count to display on the right side of the option */
20
+ count?: number;
19
21
  }
20
22
 
21
23
  interface MultiSelectProps {
@@ -191,11 +193,20 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
191
193
  if (!searchValue.trim()) {
192
194
  return options;
193
195
  }
194
- const searchLower = searchValue.toLowerCase();
196
+ const removeAccents = (str: string) => {
197
+ return str ? str.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : '';
198
+ };
199
+
200
+ const searchLower = removeAccents(searchValue.toLowerCase());
201
+
195
202
  return options.filter(
196
- (option) =>
197
- option.label.toLowerCase().includes(searchLower) ||
198
- String(option.value).toLowerCase().includes(searchLower),
203
+ (option) => {
204
+ const labelLower = option.label ? removeAccents(option.label.toLowerCase()) : '';
205
+ const valueLower = option.value !== undefined && option.value !== null
206
+ ? removeAccents(String(option.value).toLowerCase())
207
+ : '';
208
+ return labelLower.includes(searchLower) || valueLower.includes(searchLower);
209
+ }
199
210
  );
200
211
  }, [options, searchValue]);
201
212
 
@@ -391,78 +402,44 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
391
402
  setOpen(!open);
392
403
  }}
393
404
  >
394
- <div className="flex flex-wrap gap-1">
395
- {selectedOptions.length > 0 ? (
396
- <>
397
- {selectedOptions
398
- .slice(0, responsiveSettings.maxCount)
399
- .map((option, index) => (
400
- <Badge
401
- key={String(option.value)}
402
- className={cn(
403
- "mr-1 mb-1",
404
- getBadgeColor(index),
405
- responsiveSettings.compactMode &&
406
- "text-xs px-1.5 py-0.5",
407
- )}
408
- onClick={(e) => handleRemove(option.value, e)}
409
- >
410
- {option.icon && !responsiveSettings.compactMode && (
411
- <option.icon className="mr-2 h-4 w-4" />
412
- )}
413
- {option.label}
414
- <div
415
- role="button"
416
- tabIndex={0}
417
- className="ml-1 ring-offset-background rounded-full outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 cursor-pointer"
418
- onKeyDown={(e) => {
419
- if (e.key === "Enter") {
420
- handleRemove(option.value, e as any);
421
- }
422
- }}
423
- onMouseDown={(e) => {
424
- e.preventDefault();
425
- e.stopPropagation();
426
- }}
427
- onClick={(e) => handleRemove(option.value, e)}
428
- >
429
- <X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
430
- </div>
431
- </Badge>
432
- ))}
433
- {value.length > responsiveSettings.maxCount && (
434
- <Badge
435
- className={cn(
436
- "mr-1 mb-1 bg-muted text-muted-foreground hover:bg-muted",
437
- responsiveSettings.compactMode && "text-xs px-1.5 py-0.5",
438
- )}
439
- >
440
- +{value.length - responsiveSettings.maxCount} more
441
- <div
442
- role="button"
443
- tabIndex={0}
444
- className="ml-1 ring-offset-background rounded-full outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 cursor-pointer"
445
- onClick={(e) => {
446
- e.stopPropagation();
447
- clearExtraOptions();
448
- }}
449
- >
450
- <X className="h-3 w-3" />
451
- </div>
452
- </Badge>
453
- )}
454
- </>
455
- ) : (
405
+ <div className="flex flex-wrap gap-1 items-center">
406
+ {value.length === 0 ? (
456
407
  <span className="text-muted-foreground">{placeholder}</span>
408
+ ) : value.length === 1 ? (
409
+ <Badge variant="secondary" className="mr-1 bg-secondary hover:bg-secondary border-transparent font-normal truncate max-w-[150px]">
410
+ {options.find((o) => o.value === value[0])?.label}
411
+ </Badge>
412
+ ) : value.length === options.length ? (
413
+ <Badge variant="secondary" className="mr-1 bg-secondary hover:bg-secondary border-transparent font-normal">
414
+ Tất cả lựa chọn
415
+ </Badge>
416
+ ) : (
417
+ <Badge className="mr-1 bg-primary text-primary-foreground hover:bg-primary font-medium">
418
+ {value.length} đã chọn
419
+ </Badge>
457
420
  )}
458
421
  </div>
459
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
422
+ <div className="flex items-center space-x-1 ml-2">
423
+ {value.length > 0 && (
424
+ <div
425
+ role="button"
426
+ className="h-5 w-5 rounded-md flex items-center justify-center text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
427
+ onClick={(e) => {
428
+ e.stopPropagation();
429
+ handleClearAll();
430
+ }}
431
+ >
432
+ <X className="h-3 w-3" />
433
+ </div>
434
+ )}
435
+ <ChevronsUpDown className="h-4 w-4 shrink-0 opacity-50" />
436
+ </div>
460
437
  </Button>
461
438
 
462
439
  {open && (
463
440
  <div
464
441
  ref={dropdownRef}
465
- className="absolute z-[100] mt-1 w-full rounded-md border bg-popover text-popover-foreground shadow-md"
442
+ className="absolute z-[100] mt-1 w-full rounded-md border bg-popover/95 backdrop-blur-md text-popover-foreground shadow-lg shadow-black/5 animate-in fade-in-0 zoom-in-95 slide-in-from-top-1"
466
443
  style={{
467
444
  top: "100%",
468
445
  left: 0,
@@ -516,34 +493,48 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
516
493
 
517
494
  {/* Action Buttons */}
518
495
  <div className="flex items-center justify-between border-b p-1">
496
+ <div className="flex items-center">
497
+ <Button
498
+ type="button"
499
+ variant="ghost"
500
+ size="sm"
501
+ className="h-8 px-2 text-xs"
502
+ onClick={(e) => {
503
+ e.stopPropagation();
504
+ handleSelectAll();
505
+ }}
506
+ >
507
+ Select All
508
+ </Button>
509
+ <Button
510
+ type="button"
511
+ variant="ghost"
512
+ size="sm"
513
+ className="h-8 px-2 text-xs"
514
+ onClick={(e) => {
515
+ e.stopPropagation();
516
+ handleClearAll();
517
+ }}
518
+ >
519
+ Clear All
520
+ </Button>
521
+ </div>
519
522
  <Button
520
523
  type="button"
521
524
  variant="ghost"
522
525
  size="sm"
523
- className="h-8 px-2 text-xs"
524
- onClick={(e) => {
525
- e.stopPropagation();
526
- handleSelectAll();
527
- }}
528
- >
529
- Select All
530
- </Button>
531
- <Button
532
- type="button"
533
- variant="ghost"
534
- size="sm"
535
- className="h-8 px-2 text-xs"
526
+ className="h-8 px-2 text-xs text-muted-foreground hover:text-foreground"
536
527
  onClick={(e) => {
537
528
  e.stopPropagation();
538
- handleClearAll();
529
+ setOpen(false);
539
530
  }}
540
531
  >
541
- Clear All
532
+ Đóng
542
533
  </Button>
543
534
  </div>
544
535
 
545
536
  {/* Options List */}
546
- <ScrollArea className="max-h-[300px]">
537
+ <div className="max-h-[250px] overflow-y-auto overflow-x-hidden">
547
538
  {filteredOptions.length === 0 ? (
548
539
  <div className="py-6 text-center text-sm text-muted-foreground">
549
540
  {emptyText}
@@ -560,13 +551,13 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
560
551
  aria-selected={isSelected}
561
552
  aria-disabled={isDisabled}
562
553
  className={cn(
563
- "relative flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
554
+ "relative flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors duration-150",
564
555
  isDisabled
565
556
  ? "cursor-not-allowed opacity-50"
566
- : "cursor-pointer hover:bg-accent hover:text-accent-foreground",
557
+ : "cursor-pointer hover:bg-accent/80 hover:text-accent-foreground",
567
558
  isSelected &&
568
559
  !isDisabled &&
569
- "bg-accent text-accent-foreground",
560
+ "bg-accent/50 text-accent-foreground",
570
561
  )}
571
562
  onMouseDown={(e) => {
572
563
  e.preventDefault();
@@ -582,13 +573,13 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
582
573
  >
583
574
  <div
584
575
  className={cn(
585
- "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
576
+ "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border transition-all duration-200",
586
577
  isSelected
587
- ? "bg-primary text-primary-foreground"
588
- : "opacity-50 [&_svg]:invisible",
578
+ ? "bg-primary border-primary text-primary-foreground"
579
+ : "border-input opacity-50",
589
580
  )}
590
581
  >
591
- <Check className={cn("h-4 w-4")} />
582
+ <Check className={cn("h-3 w-3 transition-opacity", isSelected ? "opacity-100" : "opacity-0")} />
592
583
  </div>
593
584
  {option.icon && (
594
585
  <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
@@ -596,28 +587,19 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
596
587
  <span className="flex-1 truncate">
597
588
  {option.label}
598
589
  </span>
590
+ {option.count !== undefined && (
591
+ <span className="ml-2 text-xs text-muted-foreground">
592
+ {option.count}
593
+ </span>
594
+ )}
599
595
  </div>
600
596
  );
601
597
  })}
602
598
  </div>
603
599
  )}
604
- </ScrollArea>
605
-
606
- {/* Close Button */}
607
- <div className="border-t p-2">
608
- <Button
609
- type="button"
610
- variant="outline"
611
- size="sm"
612
- className="w-full h-8 text-xs"
613
- onClick={(e) => {
614
- e.stopPropagation();
615
- setOpen(false);
616
- }}
617
- >
618
- Close
619
- </Button>
620
600
  </div>
601
+
602
+
621
603
  </div>
622
604
  </div>
623
605
  )}
@@ -0,0 +1,125 @@
1
+ import re
2
+
3
+ filepath = "/Users/thongle/Downloads/REPO/goerp/goerp-core/packages/core/src/ui/forms/multi-select.tsx"
4
+
5
+ with open(filepath, 'r', encoding='utf-8') as f:
6
+ content = f.read()
7
+
8
+ # 1. Thay thế Action Buttons và thêm Close icon
9
+ old_action_buttons = """ {/* Action Buttons */}
10
+ <div className="flex items-center justify-between border-b p-1">
11
+ <Button
12
+ type="button"
13
+ variant="ghost"
14
+ size="sm"
15
+ className="h-8 px-2 text-xs"
16
+ onClick={(e) => {
17
+ e.stopPropagation();
18
+ handleSelectAll();
19
+ }}
20
+ >
21
+ Select All
22
+ </Button>
23
+ <Button
24
+ type="button"
25
+ variant="ghost"
26
+ size="sm"
27
+ className="h-8 px-2 text-xs"
28
+ onClick={(e) => {
29
+ e.stopPropagation();
30
+ handleClearAll();
31
+ }}
32
+ >
33
+ Clear All
34
+ </Button>
35
+ </div>"""
36
+
37
+ new_action_buttons = """ {/* Action Buttons */}
38
+ <div className="flex items-center justify-between border-b p-1">
39
+ <div className="flex items-center">
40
+ <Button
41
+ type="button"
42
+ variant="ghost"
43
+ size="sm"
44
+ className="h-8 px-2 text-xs"
45
+ onClick={(e) => {
46
+ e.stopPropagation();
47
+ handleSelectAll();
48
+ }}
49
+ >
50
+ Select All
51
+ </Button>
52
+ <Button
53
+ type="button"
54
+ variant="ghost"
55
+ size="sm"
56
+ className="h-8 px-2 text-xs"
57
+ onClick={(e) => {
58
+ e.stopPropagation();
59
+ handleClearAll();
60
+ }}
61
+ >
62
+ Clear All
63
+ </Button>
64
+ </div>
65
+ <Button
66
+ type="button"
67
+ variant="ghost"
68
+ size="sm"
69
+ className="h-8 px-2 text-xs text-muted-foreground hover:text-foreground"
70
+ onClick={(e) => {
71
+ e.stopPropagation();
72
+ setOpen(false);
73
+ }}
74
+ >
75
+ Đóng
76
+ </Button>
77
+ </div>"""
78
+
79
+ content = content.replace(old_action_buttons, new_action_buttons)
80
+
81
+ # 2. Xóa Close button cũ dưới cùng
82
+ old_close_btn = """ {/* Close Button */}
83
+ <div className="border-t p-2">
84
+ <Button
85
+ type="button"
86
+ variant="outline"
87
+ size="sm"
88
+ className="w-full h-8 text-xs"
89
+ onClick={(e) => {
90
+ e.stopPropagation();
91
+ setOpen(false);
92
+ }}
93
+ >
94
+ Close
95
+ </Button>
96
+ </div>"""
97
+
98
+ content = content.replace(old_close_btn, "")
99
+
100
+ # 3. Thay ScrollArea bằng native div cuộn
101
+ old_scroll_start = """ {/* Options List */}
102
+ <ScrollArea className="max-h-[300px]">
103
+ {filteredOptions.length === 0 ? ("""
104
+
105
+ new_scroll_start = """ {/* Options List */}
106
+ <div className="max-h-[250px] overflow-y-auto overflow-x-hidden">
107
+ {filteredOptions.length === 0 ? ("""
108
+
109
+ content = content.replace(old_scroll_start, new_scroll_start)
110
+
111
+ # The end tag for ScrollArea
112
+ old_scroll_end = """ </div>
113
+ )}
114
+ </ScrollArea>"""
115
+
116
+ new_scroll_end = """ </div>
117
+ )}
118
+ </div>"""
119
+
120
+ content = content.replace(old_scroll_end, new_scroll_end)
121
+
122
+ with open(filepath, 'w', encoding='utf-8') as f:
123
+ f.write(content)
124
+
125
+ print("Updated multiselect component")
@@ -0,0 +1,48 @@
1
+ import re
2
+
3
+ filepath = "/Users/thongle/Downloads/REPO/goerp/goerp-core/packages/core/src/ui/forms/multi-select.tsx"
4
+
5
+ with open(filepath, 'r', encoding='utf-8') as f:
6
+ content = f.read()
7
+
8
+ old_search_logic = """ // Filter options based on search value
9
+ const filteredOptions = useMemo(() => {
10
+ if (!searchValue.trim()) {
11
+ return options;
12
+ }
13
+ const searchLower = searchValue.toLowerCase();
14
+ return options.filter(
15
+ (option) =>
16
+ option.label.toLowerCase().includes(searchLower) ||
17
+ String(option.value).toLowerCase().includes(searchLower),
18
+ );
19
+ }, [options, searchValue]);"""
20
+
21
+ new_search_logic = """ // Filter options based on search value
22
+ const filteredOptions = useMemo(() => {
23
+ if (!searchValue.trim()) {
24
+ return options;
25
+ }
26
+ const removeAccents = (str: string) => {
27
+ return str ? str.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '') : '';
28
+ };
29
+
30
+ const searchLower = removeAccents(searchValue.toLowerCase());
31
+
32
+ return options.filter(
33
+ (option) => {
34
+ const labelLower = option.label ? removeAccents(option.label.toLowerCase()) : '';
35
+ const valueLower = option.value !== undefined && option.value !== null
36
+ ? removeAccents(String(option.value).toLowerCase())
37
+ : '';
38
+ return labelLower.includes(searchLower) || valueLower.includes(searchLower);
39
+ }
40
+ );
41
+ }, [options, searchValue]);"""
42
+
43
+ content = content.replace(old_search_logic, new_search_logic)
44
+
45
+ with open(filepath, 'w', encoding='utf-8') as f:
46
+ f.write(content)
47
+
48
+ print("Updated multiselect search logic")
@@ -0,0 +1,119 @@
1
+ import re
2
+
3
+ filepath = "/Users/thongle/Downloads/REPO/goerp/goerp-core/packages/core/src/ui/forms/multi-select.tsx"
4
+
5
+ with open(filepath, 'r', encoding='utf-8') as f:
6
+ content = f.read()
7
+
8
+ # 1. Update MultiSelectOption interface
9
+ old_interface = """ /** Optional icon component to display alongside the option */
10
+ icon?: React.ComponentType<{ className?: string }>;
11
+ }"""
12
+
13
+ new_interface = """ /** Optional icon component to display alongside the option */
14
+ icon?: React.ComponentType<{ className?: string }>;
15
+ /** Optional count to display on the right side of the option */
16
+ count?: number;
17
+ }"""
18
+
19
+ content = content.replace(old_interface, new_interface)
20
+
21
+ # 2. Update trigger button (Compact Badge & Clear Icon)
22
+ # We need to replace the content of `<div className="flex flex-wrap gap-1">`
23
+ # and the ChevronsUpDown area.
24
+ old_trigger_area = r'<div className="flex flex-wrap gap-1">[\s\S]*?<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />'
25
+
26
+ new_trigger_area = """<div className="flex flex-wrap gap-1 items-center">
27
+ {value.length === 0 ? (
28
+ <span className="text-muted-foreground">{placeholder}</span>
29
+ ) : value.length === 1 ? (
30
+ <Badge variant="secondary" className="mr-1 bg-secondary hover:bg-secondary border-transparent font-normal truncate max-w-[150px]">
31
+ {options.find((o) => o.value === value[0])?.label}
32
+ </Badge>
33
+ ) : value.length === options.length ? (
34
+ <Badge variant="secondary" className="mr-1 bg-secondary hover:bg-secondary border-transparent font-normal">
35
+ Tất cả lựa chọn
36
+ </Badge>
37
+ ) : (
38
+ <Badge className="mr-1 bg-primary text-primary-foreground hover:bg-primary font-medium">
39
+ {value.length} đã chọn
40
+ </Badge>
41
+ )}
42
+ </div>
43
+ <div className="flex items-center space-x-1 ml-2">
44
+ {value.length > 0 && (
45
+ <div
46
+ role="button"
47
+ className="h-5 w-5 rounded-md flex items-center justify-center text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
48
+ onClick={(e) => {
49
+ e.stopPropagation();
50
+ handleClearAll();
51
+ }}
52
+ >
53
+ <X className="h-3 w-3" />
54
+ </div>
55
+ )}
56
+ <ChevronsUpDown className="h-4 w-4 shrink-0 opacity-50" />
57
+ </div>"""
58
+
59
+ content = re.sub(old_trigger_area, new_trigger_area, content)
60
+
61
+
62
+ # 3. Update Popover Container for glassmorphism and animation
63
+ old_popover = """className="absolute z-[100] mt-1 w-full rounded-md border bg-popover text-popover-foreground shadow-md\""""
64
+ new_popover = """className="absolute z-[100] mt-1 w-full rounded-md border bg-popover/95 backdrop-blur-md text-popover-foreground shadow-lg shadow-black/5 animate-in fade-in-0 zoom-in-95 slide-in-from-top-1\""""
65
+ content = content.replace(old_popover, new_popover)
66
+
67
+
68
+ # 4. Update Option Checkbox and Count
69
+ old_option_render = r"""<div\s+className=\{cn\(\s*"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",\s*isSelected\s*\?\s*"bg-primary text-primary-foreground"\s*:\s*"opacity-50 \[\&_svg\]:invisible",\s*\)\}\s*>\s*<Check className=\{cn\("h-4 w-4"\)\} />\s*</div>\s*\{option\.icon && \(\s*<option\.icon className="mr-2 h-4 w-4 text-muted-foreground" />\s*\)\}\s*<span className="flex-1 truncate">\s*\{option\.label\}\s*</span>"""
70
+
71
+ new_option_render = """<div
72
+ className={cn(
73
+ "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border transition-all duration-200",
74
+ isSelected
75
+ ? "bg-primary border-primary text-primary-foreground"
76
+ : "border-input opacity-50",
77
+ )}
78
+ >
79
+ <Check className={cn("h-3 w-3 transition-opacity", isSelected ? "opacity-100" : "opacity-0")} />
80
+ </div>
81
+ {option.icon && (
82
+ <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
83
+ )}
84
+ <span className="flex-1 truncate">
85
+ {option.label}
86
+ </span>
87
+ {option.count !== undefined && (
88
+ <span className="ml-2 text-xs text-muted-foreground">
89
+ {option.count}
90
+ </span>
91
+ )}"""
92
+
93
+ content = re.sub(old_option_render, new_option_render, content)
94
+
95
+ # 5. Option Hover Animation
96
+ old_option_wrapper = """className={cn(
97
+ "relative flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
98
+ isDisabled
99
+ ? "cursor-not-allowed opacity-50"
100
+ : "cursor-pointer hover:bg-accent hover:text-accent-foreground",
101
+ isSelected &&
102
+ !isDisabled &&
103
+ "bg-accent text-accent-foreground",
104
+ )}"""
105
+ new_option_wrapper = """className={cn(
106
+ "relative flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors duration-150",
107
+ isDisabled
108
+ ? "cursor-not-allowed opacity-50"
109
+ : "cursor-pointer hover:bg-accent/80 hover:text-accent-foreground",
110
+ isSelected &&
111
+ !isDisabled &&
112
+ "bg-accent/50 text-accent-foreground",
113
+ )}"""
114
+ content = content.replace(old_option_wrapper, new_option_wrapper)
115
+
116
+ with open(filepath, 'w', encoding='utf-8') as f:
117
+ f.write(content)
118
+
119
+ print("UI Upgrade Successful")
@@ -144,14 +144,14 @@ export function Combobox({
144
144
  role="combobox"
145
145
  aria-expanded={open}
146
146
  disabled={disabled}
147
- className={cn("w-full justify-between", className)}
147
+ className={cn("w-full justify-between h-auto min-h-9 text-left whitespace-normal gap-2", className)}
148
148
  id={id}
149
149
  onClick={(e) => {
150
150
  e.stopPropagation();
151
151
  setOpen(!open);
152
152
  }}
153
153
  >
154
- <span className="truncate">
154
+ <span className="break-words min-w-0">
155
155
  {selectedOption ? selectedOption.label : placeholder}
156
156
  </span>
157
157
  <div className="ml-2 flex items-center gap-1 shrink-0">
@@ -37,7 +37,7 @@ export function SelectTrigger({
37
37
  <SelectPrimitive.Trigger
38
38
  data-slot="select-trigger"
39
39
  className={cn(
40
- "cursor-pointer flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
40
+ "cursor-pointer flex min-h-9 h-auto w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm text-left ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&_span]:line-clamp-none [&_span]:whitespace-normal [&_span]:break-words [&_span]:min-w-0",
41
41
  className,
42
42
  )}
43
43
  {...props}