@justin_evo/evo-ui 1.1.0 → 1.2.1

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 (80) hide show
  1. package/README.md +3 -3
  2. package/dist/TopNav/TopNav.d.ts +19 -0
  3. package/dist/declarations.d.ts +6 -6
  4. package/dist/evo-ui.css +1 -1
  5. package/dist/index.cjs.js +1 -1
  6. package/dist/index.es.js +3301 -3197
  7. package/package.json +52 -52
  8. package/src/Alert/Alert.tsx +49 -49
  9. package/src/AutoComplete/AutoComplete.tsx +810 -810
  10. package/src/Badge/Badge.tsx +53 -53
  11. package/src/Breadcrumb/Breadcrumb.tsx +53 -53
  12. package/src/Button/Button.tsx +125 -125
  13. package/src/Card/Card.tsx +257 -257
  14. package/src/Checkbox/Checkbox.tsx +59 -59
  15. package/src/CommandPalette/CommandPalette.tsx +185 -185
  16. package/src/Container/Container.tsx +31 -31
  17. package/src/Divider/Divider.tsx +31 -31
  18. package/src/Form/Form.tsx +185 -185
  19. package/src/Grid/Grid.tsx +66 -66
  20. package/src/ImageCropper/ImageCropper.tsx +911 -911
  21. package/src/Input/Input.tsx +74 -74
  22. package/src/Modal/Modal.tsx +77 -77
  23. package/src/Nav/Nav.tsx +708 -708
  24. package/src/Notification/Notification.tsx +1503 -1503
  25. package/src/Pagination/Pagination.tsx +76 -76
  26. package/src/Radio/Radio.tsx +69 -69
  27. package/src/RichTextArea/RichTextArea.tsx +886 -869
  28. package/src/Select/Select.tsx +515 -515
  29. package/src/Skeleton/Skeleton.tsx +70 -70
  30. package/src/Stack/Stack.tsx +52 -52
  31. package/src/Table/Table.tsx +335 -335
  32. package/src/Tabs/Tabs.tsx +90 -90
  33. package/src/Theme/ThemeProvider.tsx +253 -253
  34. package/src/Theme/ThemeToggle.tsx +79 -79
  35. package/src/Toggle/Toggle.tsx +48 -48
  36. package/src/Tooltip/Tooltip.tsx +38 -38
  37. package/src/TopNav/TopNav.tsx +1163 -994
  38. package/src/TreeSelect/TreeSelect.tsx +825 -825
  39. package/src/css/alert.module.scss +93 -93
  40. package/src/css/autocomplete.module.scss +416 -416
  41. package/src/css/badge.module.scss +82 -82
  42. package/src/css/base/_color.scss +159 -159
  43. package/src/css/base/_theme.scss +237 -237
  44. package/src/css/base/_variables.scss +161 -161
  45. package/src/css/breadcrumb.module.scss +50 -50
  46. package/src/css/button.module.scss +385 -385
  47. package/src/css/card.module.scss +217 -217
  48. package/src/css/checkbox.module.scss +123 -120
  49. package/src/css/commandpalette.module.scss +211 -211
  50. package/src/css/container.module.scss +18 -18
  51. package/src/css/divider.module.scss +41 -41
  52. package/src/css/form.module.scss +245 -245
  53. package/src/css/imagecropper.module.scss +397 -397
  54. package/src/css/input.module.scss +89 -89
  55. package/src/css/modal.module.scss +105 -105
  56. package/src/css/nav.module.scss +494 -494
  57. package/src/css/notification.module.scss +691 -691
  58. package/src/css/pagination.module.scss +63 -63
  59. package/src/css/radio.module.scss +89 -89
  60. package/src/css/richtextarea.module.scss +307 -307
  61. package/src/css/select.module.scss +525 -525
  62. package/src/css/skeleton.module.scss +30 -30
  63. package/src/css/table.module.scss +386 -386
  64. package/src/css/tabs.module.scss +63 -63
  65. package/src/css/theme-toggle.module.scss +83 -83
  66. package/src/css/toggle.module.scss +54 -54
  67. package/src/css/tooltip.module.scss +97 -97
  68. package/src/css/topnav.module.scss +568 -396
  69. package/src/css/treeselect.module.scss +558 -558
  70. package/src/css/utilities/_borders.scss +111 -111
  71. package/src/css/utilities/_colors.scss +66 -66
  72. package/src/css/utilities/_effects.scss +216 -216
  73. package/src/css/utilities/_layout.scss +181 -181
  74. package/src/css/utilities/_position.scss +75 -75
  75. package/src/css/utilities/_sizing.scss +138 -138
  76. package/src/css/utilities/_spacing.scss +99 -99
  77. package/src/css/utilities/_typography.scss +121 -121
  78. package/src/css/utilities/index.scss +24 -24
  79. package/src/declarations.d.ts +6 -6
  80. package/src/index.ts +60 -60
@@ -1,558 +1,558 @@
1
- @use 'base/variables' as *;
2
- @use 'base/color' as *;
3
-
4
- /* =====================================================================
5
- * EvoTreeSelect — Hierarchical dropdown selector.
6
- *
7
- * Synthesizes the strongest ideas from Ant Design TreeSelect, PrimeReact,
8
- * Element Plus, Naive UI and the W3C ARIA tree pattern, tuned to the
9
- * Linear / Vercel / shadcn dark aesthetic already shared by EvoSelect.
10
- *
11
- * Highlights
12
- * - Single OR multi-select with tri-state cascade checkboxes
13
- * - Expand / collapse with rotating chevrons + subtle indent guides
14
- * - In-menu search that auto-expands ancestors of matches
15
- * - Chip rendering for multi-select with maxTagCount overflow
16
- * - Full keyboard nav (W3C tree pattern, adapted for a combobox shell)
17
- * ===================================================================== */
18
-
19
- .field {
20
- display: inline-flex;
21
- flex-direction: column;
22
- gap: 0.4rem;
23
- font-family: $font-sans;
24
- min-width: 260px;
25
- }
26
-
27
- .fullWidth { width: 100%; }
28
-
29
- .disabled .trigger {
30
- opacity: 0.5;
31
- cursor: not-allowed;
32
- pointer-events: none;
33
- }
34
-
35
- .label {
36
- font-size: $text-sm;
37
- font-weight: 500;
38
- color: $color-text-primary;
39
- letter-spacing: -0.005em;
40
- }
41
-
42
- .selectWrapper {
43
- position: relative;
44
- width: 100%;
45
- }
46
-
47
- /* ---------------- Trigger button ---------------- */
48
- .trigger {
49
- display: inline-flex;
50
- align-items: center;
51
- justify-content: space-between;
52
- gap: 0.5rem;
53
- width: 100%;
54
-
55
- background: $color-surface;
56
- border: 1px solid $color-border;
57
- border-radius: $radius-sm;
58
- color: $color-text-primary;
59
- font-family: $font-sans;
60
- font-size: $text-sm;
61
- line-height: 1.4;
62
- cursor: pointer;
63
- user-select: none;
64
- text-align: left;
65
- outline: none;
66
-
67
- transition:
68
- background-color $transition-fast,
69
- border-color $transition-fast,
70
- box-shadow $transition-fast;
71
-
72
- &:hover:not(:disabled) {
73
- background: $color-surface-hover;
74
- border-color: $color-border-strong;
75
- }
76
-
77
- &:focus-visible,
78
- &.open {
79
- border-color: $evo-primary-color;
80
- box-shadow:
81
- 0 0 0 3px color-mix(in srgb, $evo-primary-color 18%, transparent),
82
- 0 1px 2px rgb(0 0 0 / 0.15);
83
- }
84
-
85
- &.hasError {
86
- border-color: $evo-danger-color;
87
-
88
- &.open,
89
- &:focus-visible {
90
- box-shadow:
91
- 0 0 0 3px color-mix(in srgb, $evo-danger-color 18%, transparent),
92
- 0 1px 2px rgb(0 0 0 / 0.15);
93
- }
94
- }
95
-
96
- &:disabled {
97
- opacity: 0.5;
98
- cursor: not-allowed;
99
- }
100
- }
101
-
102
- /* ---------------- Sizes ---------------- */
103
- .sm {
104
- padding: 0.3rem 0.5rem;
105
- min-height: 32px;
106
- font-size: $text-xs;
107
- border-radius: $radius-sm;
108
- }
109
-
110
- .md {
111
- padding: 0.4rem 0.625rem;
112
- min-height: 38px;
113
- font-size: $text-sm;
114
- }
115
-
116
- .lg {
117
- padding: 0.5rem 0.75rem;
118
- min-height: 44px;
119
- font-size: $text-base;
120
- }
121
-
122
- /* ---------------- Trigger content ---------------- */
123
- .triggerValue,
124
- .triggerPlaceholder {
125
- display: inline-flex;
126
- align-items: center;
127
- flex-wrap: wrap;
128
- gap: 0.3rem;
129
- min-width: 0;
130
- flex: 1;
131
- }
132
-
133
- .triggerPlaceholder {
134
- color: $color-text-muted;
135
- }
136
-
137
- .triggerText {
138
- overflow: hidden;
139
- text-overflow: ellipsis;
140
- white-space: nowrap;
141
- min-width: 0;
142
- }
143
-
144
- .triggerActions {
145
- display: inline-flex;
146
- align-items: center;
147
- gap: 0.25rem;
148
- flex-shrink: 0;
149
- margin-left: auto;
150
- }
151
-
152
- /* ---------------- Chips (multi-select) ---------------- */
153
- .chip {
154
- display: inline-flex;
155
- align-items: center;
156
- gap: 0.3rem;
157
- padding: 0.1rem 0.45rem;
158
- height: 22px;
159
- border-radius: 999px;
160
- background: color-mix(in srgb, $evo-primary-color 12%, transparent);
161
- border: 1px solid color-mix(in srgb, $evo-primary-color 25%, transparent);
162
- color: $evo-primary-color;
163
- font-size: $text-xs;
164
- font-weight: 500;
165
- max-width: 180px;
166
- line-height: 1;
167
- }
168
-
169
- .chipLabel {
170
- overflow: hidden;
171
- text-overflow: ellipsis;
172
- white-space: nowrap;
173
- }
174
-
175
- .chipRemove {
176
- display: inline-flex;
177
- align-items: center;
178
- justify-content: center;
179
- width: 14px;
180
- height: 14px;
181
- border-radius: 50%;
182
- color: $evo-primary-color;
183
- cursor: pointer;
184
- transition: color $transition-fast, background-color $transition-fast;
185
-
186
- &:hover {
187
- color: $color-text-primary;
188
- background: $color-surface-hover;
189
- }
190
- }
191
-
192
- .chipOverflow {
193
- display: inline-flex;
194
- align-items: center;
195
- padding: 0.1rem 0.45rem;
196
- height: 22px;
197
- border-radius: 999px;
198
- background: $color-surface-active;
199
- border: 1px solid $color-border;
200
- color: $color-text-secondary;
201
- font-size: $text-xs;
202
- font-weight: 500;
203
- }
204
-
205
- /* ---------------- Clear & chevron ---------------- */
206
- .clearBtn {
207
- display: inline-flex;
208
- align-items: center;
209
- justify-content: center;
210
- width: 18px;
211
- height: 18px;
212
- border-radius: 50%;
213
- color: $color-text-muted;
214
- cursor: pointer;
215
- transition: color $transition-fast, background-color $transition-fast;
216
-
217
- &:hover {
218
- color: $color-text-primary;
219
- background: $color-surface-hover;
220
- }
221
- }
222
-
223
- .chevron {
224
- display: inline-flex;
225
- align-items: center;
226
- justify-content: center;
227
- color: $color-text-muted;
228
- transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), color $transition-fast;
229
- }
230
-
231
- .chevronOpen {
232
- transform: rotate(180deg);
233
- color: $evo-primary-color;
234
- }
235
-
236
- .trigger:hover:not(:disabled) .chevron {
237
- color: $color-text-secondary;
238
- }
239
-
240
- /* =====================================================================
241
- * Popover menu
242
- * ===================================================================== */
243
- .menu {
244
- position: absolute;
245
- top: calc(100% + 6px);
246
- left: 0;
247
- right: 0;
248
- z-index: 100;
249
-
250
- background: $color-surface-elevated;
251
- border: 1px solid $color-border;
252
- border-radius: $radius-md;
253
- overflow: hidden;
254
-
255
- box-shadow:
256
- 0 16px 32px -12px rgb(0 0 0 / 0.35),
257
- 0 6px 12px -4px rgb(0 0 0 / 0.18);
258
-
259
- transform-origin: top center;
260
- animation: menuOpen 160ms cubic-bezier(0.16, 1, 0.3, 1);
261
- }
262
-
263
- @keyframes menuOpen {
264
- from { opacity: 0; transform: translateY(-6px) scale(0.97); }
265
- to { opacity: 1; transform: translateY(0) scale(1); }
266
- }
267
-
268
- /* ---------------- In-menu search row ---------------- */
269
- .searchRow {
270
- display: flex;
271
- align-items: center;
272
- gap: 0.5rem;
273
- padding: 0.5rem 0.75rem;
274
- border-bottom: 1px solid $color-border-subtle;
275
- background: $color-surface-sunken;
276
- }
277
-
278
- .searchIconWrap {
279
- display: inline-flex;
280
- color: $color-text-muted;
281
- flex-shrink: 0;
282
- }
283
-
284
- .searchInput {
285
- flex: 1;
286
- background: none;
287
- border: none;
288
- outline: none;
289
- font-family: $font-sans;
290
- font-size: $text-sm;
291
- color: $color-text-primary;
292
- caret-color: $evo-primary-color;
293
- padding: 0;
294
-
295
- &::placeholder { color: $color-text-muted; }
296
- }
297
-
298
- /* ---------------- Tree list ---------------- */
299
- .tree {
300
- max-height: 300px;
301
- overflow-y: auto;
302
- padding: 0.35rem 0.3rem;
303
-
304
- scrollbar-width: thin;
305
- scrollbar-color: $color-border-strong transparent;
306
-
307
- &::-webkit-scrollbar { width: 8px; }
308
- &::-webkit-scrollbar-track { background: transparent; }
309
- &::-webkit-scrollbar-thumb {
310
- background: $color-border-strong;
311
- border-radius: 4px;
312
- background-clip: padding-box;
313
- border: 2px solid transparent;
314
- }
315
- &::-webkit-scrollbar-thumb:hover {
316
- background: $color-text-muted;
317
- background-clip: padding-box;
318
- border: 2px solid transparent;
319
- }
320
- }
321
-
322
- .empty {
323
- padding: 1.5rem 0.75rem;
324
- text-align: center;
325
- color: $color-text-muted;
326
- font-size: $text-sm;
327
- }
328
-
329
- /* ---------------- Tree row ---------------- */
330
- .row {
331
- position: relative;
332
- display: flex;
333
- align-items: center;
334
- gap: 0.4rem;
335
- width: 100%;
336
- padding: 0.35rem 0.4rem;
337
- border-radius: 6px;
338
- color: $color-text-primary;
339
- font-family: $font-sans;
340
- font-size: $text-sm;
341
- text-align: left;
342
- cursor: pointer;
343
- transition: background-color 120ms ease, color 120ms ease;
344
- user-select: none;
345
- }
346
-
347
- .row:hover:not(.rowDisabled) {
348
- background: $color-surface-hover;
349
- }
350
-
351
- .rowActive:not(.rowDisabled) {
352
- background: color-mix(in srgb, $evo-primary-color 10%, transparent);
353
- box-shadow: inset 0 0 0 1px $color-border-subtle;
354
- }
355
-
356
- .rowSelected {
357
- color: $evo-primary-color;
358
-
359
- .rowLabel { font-weight: 600; }
360
- }
361
-
362
- .rowDisabled {
363
- opacity: 0.4;
364
- cursor: not-allowed;
365
- }
366
-
367
- /* indent guides */
368
- .indent {
369
- display: inline-flex;
370
- align-items: stretch;
371
- flex-shrink: 0;
372
- }
373
-
374
- .indentUnit {
375
- width: 14px;
376
- position: relative;
377
- display: inline-block;
378
-
379
- &::before {
380
- content: '';
381
- position: absolute;
382
- top: -2px;
383
- bottom: -2px;
384
- left: 6px;
385
- width: 1px;
386
- background: $color-border-subtle;
387
- }
388
- }
389
-
390
- /* expand/collapse toggle */
391
- .toggle {
392
- display: inline-flex;
393
- align-items: center;
394
- justify-content: center;
395
- width: 16px;
396
- height: 16px;
397
- flex-shrink: 0;
398
- color: $color-text-muted;
399
- border-radius: 4px;
400
- transition: transform 160ms cubic-bezier(0.4, 0, 0.2, 1), background-color $transition-fast, color $transition-fast;
401
-
402
- &:hover {
403
- background: $color-surface-hover;
404
- color: $color-text-primary;
405
- }
406
- }
407
-
408
- .toggleOpen {
409
- transform: rotate(90deg);
410
- color: $evo-primary-color;
411
- }
412
-
413
- .togglePlaceholder {
414
- display: inline-block;
415
- width: 16px;
416
- height: 16px;
417
- flex-shrink: 0;
418
- }
419
-
420
- .spinner {
421
- width: 12px;
422
- height: 12px;
423
- border-radius: 50%;
424
- border: 1.5px solid $color-border;
425
- border-top-color: $evo-primary-color;
426
- animation: spin 700ms linear infinite;
427
- }
428
-
429
- @keyframes spin {
430
- to { transform: rotate(360deg); }
431
- }
432
-
433
- /* checkbox */
434
- .checkbox {
435
- display: inline-flex;
436
- align-items: center;
437
- justify-content: center;
438
- width: 16px;
439
- height: 16px;
440
- flex-shrink: 0;
441
- background: $color-surface;
442
- border: 1px solid $color-border-strong;
443
- border-radius: 4px;
444
- color: $evo-primary-color;
445
- transition:
446
- background-color $transition-fast,
447
- border-color $transition-fast,
448
- color $transition-fast;
449
- }
450
-
451
- .row:hover:not(.rowDisabled) .checkbox {
452
- border-color: $evo-primary-color;
453
- }
454
-
455
- .checkboxChecked {
456
- background: $evo-primary-color;
457
- border-color: $evo-primary-color;
458
- color: $evo-primary-fg;
459
- }
460
-
461
- .checkboxMixed {
462
- background: color-mix(in srgb, $evo-primary-color 18%, transparent);
463
- border-color: $evo-primary-color;
464
- color: $evo-primary-color;
465
- }
466
-
467
- /* icon + label */
468
- .rowIcon {
469
- display: inline-flex;
470
- align-items: center;
471
- justify-content: center;
472
- width: 1.1rem;
473
- height: 1.1rem;
474
- flex-shrink: 0;
475
- color: $color-text-secondary;
476
- }
477
-
478
- .rowText {
479
- display: flex;
480
- flex-direction: column;
481
- flex: 1;
482
- min-width: 0;
483
- gap: 1px;
484
- }
485
-
486
- .rowLabel {
487
- overflow: hidden;
488
- text-overflow: ellipsis;
489
- white-space: nowrap;
490
- font-weight: 500;
491
- }
492
-
493
- .rowDesc {
494
- font-size: $text-xs;
495
- color: $color-text-muted;
496
- font-weight: 400;
497
- overflow: hidden;
498
- text-overflow: ellipsis;
499
- white-space: nowrap;
500
- }
501
-
502
- .match {
503
- color: $evo-primary-color;
504
- background: color-mix(in srgb, $evo-primary-color 16%, transparent);
505
- border-radius: 3px;
506
- padding: 0 2px;
507
- }
508
-
509
- /* trailing single-select check */
510
- .check {
511
- display: inline-flex;
512
- align-items: center;
513
- justify-content: center;
514
- width: 16px;
515
- height: 16px;
516
- color: $evo-primary-color;
517
- flex-shrink: 0;
518
- }
519
-
520
- /* ---------------- Footer / helpers ---------------- */
521
- .menuFooter {
522
- display: flex;
523
- justify-content: space-between;
524
- align-items: center;
525
- padding: 0.4rem 0.75rem;
526
- border-top: 1px solid $color-border-subtle;
527
- background: $color-surface-sunken;
528
- font-size: $text-xs;
529
- color: $color-text-muted;
530
- }
531
-
532
- .footerBtn {
533
- background: none;
534
- border: none;
535
- color: $evo-primary-color;
536
- font-size: $text-xs;
537
- font-weight: 500;
538
- cursor: pointer;
539
- padding: 2px 6px;
540
- border-radius: 4px;
541
- transition: background-color $transition-fast, color $transition-fast;
542
-
543
- &:hover { background: color-mix(in srgb, $evo-primary-color 10%, transparent); color: $evo-primary-hover; }
544
- }
545
-
546
- .errorText {
547
- font-size: $text-xs;
548
- color: $evo-danger-color;
549
- margin: 0;
550
- line-height: 1.4;
551
- }
552
-
553
- .helperText {
554
- font-size: $text-xs;
555
- color: $color-text-muted;
556
- margin: 0;
557
- line-height: 1.4;
558
- }
1
+ @use 'base/variables' as *;
2
+ @use 'base/color' as *;
3
+
4
+ /* =====================================================================
5
+ * EvoTreeSelect — Hierarchical dropdown selector.
6
+ *
7
+ * Synthesizes the strongest ideas from Ant Design TreeSelect, PrimeReact,
8
+ * Element Plus, Naive UI and the W3C ARIA tree pattern, tuned to the
9
+ * Linear / Vercel / shadcn dark aesthetic already shared by EvoSelect.
10
+ *
11
+ * Highlights
12
+ * - Single OR multi-select with tri-state cascade checkboxes
13
+ * - Expand / collapse with rotating chevrons + subtle indent guides
14
+ * - In-menu search that auto-expands ancestors of matches
15
+ * - Chip rendering for multi-select with maxTagCount overflow
16
+ * - Full keyboard nav (W3C tree pattern, adapted for a combobox shell)
17
+ * ===================================================================== */
18
+
19
+ .field {
20
+ display: inline-flex;
21
+ flex-direction: column;
22
+ gap: 0.4rem;
23
+ font-family: $font-sans;
24
+ min-width: 260px;
25
+ }
26
+
27
+ .fullWidth { width: 100%; }
28
+
29
+ .disabled .trigger {
30
+ opacity: 0.5;
31
+ cursor: not-allowed;
32
+ pointer-events: none;
33
+ }
34
+
35
+ .label {
36
+ font-size: $text-sm;
37
+ font-weight: 500;
38
+ color: $color-text-primary;
39
+ letter-spacing: -0.005em;
40
+ }
41
+
42
+ .selectWrapper {
43
+ position: relative;
44
+ width: 100%;
45
+ }
46
+
47
+ /* ---------------- Trigger button ---------------- */
48
+ .trigger {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+ gap: 0.5rem;
53
+ width: 100%;
54
+
55
+ background: $color-surface;
56
+ border: 1px solid $color-border;
57
+ border-radius: $radius-sm;
58
+ color: $color-text-primary;
59
+ font-family: $font-sans;
60
+ font-size: $text-sm;
61
+ line-height: 1.4;
62
+ cursor: pointer;
63
+ user-select: none;
64
+ text-align: left;
65
+ outline: none;
66
+
67
+ transition:
68
+ background-color $transition-fast,
69
+ border-color $transition-fast,
70
+ box-shadow $transition-fast;
71
+
72
+ &:hover:not(:disabled) {
73
+ background: $color-surface-hover;
74
+ border-color: $color-border-strong;
75
+ }
76
+
77
+ &:focus-visible,
78
+ &.open {
79
+ border-color: $evo-primary-color;
80
+ box-shadow:
81
+ 0 0 0 3px color-mix(in srgb, $evo-primary-color 18%, transparent),
82
+ 0 1px 2px rgb(0 0 0 / 0.15);
83
+ }
84
+
85
+ &.hasError {
86
+ border-color: $evo-danger-color;
87
+
88
+ &.open,
89
+ &:focus-visible {
90
+ box-shadow:
91
+ 0 0 0 3px color-mix(in srgb, $evo-danger-color 18%, transparent),
92
+ 0 1px 2px rgb(0 0 0 / 0.15);
93
+ }
94
+ }
95
+
96
+ &:disabled {
97
+ opacity: 0.5;
98
+ cursor: not-allowed;
99
+ }
100
+ }
101
+
102
+ /* ---------------- Sizes ---------------- */
103
+ .sm {
104
+ padding: 0.3rem 0.5rem;
105
+ min-height: 32px;
106
+ font-size: $text-xs;
107
+ border-radius: $radius-sm;
108
+ }
109
+
110
+ .md {
111
+ padding: 0.4rem 0.625rem;
112
+ min-height: 38px;
113
+ font-size: $text-sm;
114
+ }
115
+
116
+ .lg {
117
+ padding: 0.5rem 0.75rem;
118
+ min-height: 44px;
119
+ font-size: $text-base;
120
+ }
121
+
122
+ /* ---------------- Trigger content ---------------- */
123
+ .triggerValue,
124
+ .triggerPlaceholder {
125
+ display: inline-flex;
126
+ align-items: center;
127
+ flex-wrap: wrap;
128
+ gap: 0.3rem;
129
+ min-width: 0;
130
+ flex: 1;
131
+ }
132
+
133
+ .triggerPlaceholder {
134
+ color: $color-text-muted;
135
+ }
136
+
137
+ .triggerText {
138
+ overflow: hidden;
139
+ text-overflow: ellipsis;
140
+ white-space: nowrap;
141
+ min-width: 0;
142
+ }
143
+
144
+ .triggerActions {
145
+ display: inline-flex;
146
+ align-items: center;
147
+ gap: 0.25rem;
148
+ flex-shrink: 0;
149
+ margin-left: auto;
150
+ }
151
+
152
+ /* ---------------- Chips (multi-select) ---------------- */
153
+ .chip {
154
+ display: inline-flex;
155
+ align-items: center;
156
+ gap: 0.3rem;
157
+ padding: 0.1rem 0.45rem;
158
+ height: 22px;
159
+ border-radius: 999px;
160
+ background: color-mix(in srgb, $evo-primary-color 12%, transparent);
161
+ border: 1px solid color-mix(in srgb, $evo-primary-color 25%, transparent);
162
+ color: $evo-primary-color;
163
+ font-size: $text-xs;
164
+ font-weight: 500;
165
+ max-width: 180px;
166
+ line-height: 1;
167
+ }
168
+
169
+ .chipLabel {
170
+ overflow: hidden;
171
+ text-overflow: ellipsis;
172
+ white-space: nowrap;
173
+ }
174
+
175
+ .chipRemove {
176
+ display: inline-flex;
177
+ align-items: center;
178
+ justify-content: center;
179
+ width: 14px;
180
+ height: 14px;
181
+ border-radius: 50%;
182
+ color: $evo-primary-color;
183
+ cursor: pointer;
184
+ transition: color $transition-fast, background-color $transition-fast;
185
+
186
+ &:hover {
187
+ color: $color-text-primary;
188
+ background: $color-surface-hover;
189
+ }
190
+ }
191
+
192
+ .chipOverflow {
193
+ display: inline-flex;
194
+ align-items: center;
195
+ padding: 0.1rem 0.45rem;
196
+ height: 22px;
197
+ border-radius: 999px;
198
+ background: $color-surface-active;
199
+ border: 1px solid $color-border;
200
+ color: $color-text-secondary;
201
+ font-size: $text-xs;
202
+ font-weight: 500;
203
+ }
204
+
205
+ /* ---------------- Clear & chevron ---------------- */
206
+ .clearBtn {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ width: 18px;
211
+ height: 18px;
212
+ border-radius: 50%;
213
+ color: $color-text-muted;
214
+ cursor: pointer;
215
+ transition: color $transition-fast, background-color $transition-fast;
216
+
217
+ &:hover {
218
+ color: $color-text-primary;
219
+ background: $color-surface-hover;
220
+ }
221
+ }
222
+
223
+ .chevron {
224
+ display: inline-flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ color: $color-text-muted;
228
+ transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), color $transition-fast;
229
+ }
230
+
231
+ .chevronOpen {
232
+ transform: rotate(180deg);
233
+ color: $evo-primary-color;
234
+ }
235
+
236
+ .trigger:hover:not(:disabled) .chevron {
237
+ color: $color-text-secondary;
238
+ }
239
+
240
+ /* =====================================================================
241
+ * Popover menu
242
+ * ===================================================================== */
243
+ .menu {
244
+ position: absolute;
245
+ top: calc(100% + 6px);
246
+ left: 0;
247
+ right: 0;
248
+ z-index: 100;
249
+
250
+ background: $color-surface-elevated;
251
+ border: 1px solid $color-border;
252
+ border-radius: $radius-md;
253
+ overflow: hidden;
254
+
255
+ box-shadow:
256
+ 0 16px 32px -12px rgb(0 0 0 / 0.35),
257
+ 0 6px 12px -4px rgb(0 0 0 / 0.18);
258
+
259
+ transform-origin: top center;
260
+ animation: menuOpen 160ms cubic-bezier(0.16, 1, 0.3, 1);
261
+ }
262
+
263
+ @keyframes menuOpen {
264
+ from { opacity: 0; transform: translateY(-6px) scale(0.97); }
265
+ to { opacity: 1; transform: translateY(0) scale(1); }
266
+ }
267
+
268
+ /* ---------------- In-menu search row ---------------- */
269
+ .searchRow {
270
+ display: flex;
271
+ align-items: center;
272
+ gap: 0.5rem;
273
+ padding: 0.5rem 0.75rem;
274
+ border-bottom: 1px solid $color-border-subtle;
275
+ background: $color-surface-sunken;
276
+ }
277
+
278
+ .searchIconWrap {
279
+ display: inline-flex;
280
+ color: $color-text-muted;
281
+ flex-shrink: 0;
282
+ }
283
+
284
+ .searchInput {
285
+ flex: 1;
286
+ background: none;
287
+ border: none;
288
+ outline: none;
289
+ font-family: $font-sans;
290
+ font-size: $text-sm;
291
+ color: $color-text-primary;
292
+ caret-color: $evo-primary-color;
293
+ padding: 0;
294
+
295
+ &::placeholder { color: $color-text-muted; }
296
+ }
297
+
298
+ /* ---------------- Tree list ---------------- */
299
+ .tree {
300
+ max-height: 300px;
301
+ overflow-y: auto;
302
+ padding: 0.35rem 0.3rem;
303
+
304
+ scrollbar-width: thin;
305
+ scrollbar-color: $color-border-strong transparent;
306
+
307
+ &::-webkit-scrollbar { width: 8px; }
308
+ &::-webkit-scrollbar-track { background: transparent; }
309
+ &::-webkit-scrollbar-thumb {
310
+ background: $color-border-strong;
311
+ border-radius: 4px;
312
+ background-clip: padding-box;
313
+ border: 2px solid transparent;
314
+ }
315
+ &::-webkit-scrollbar-thumb:hover {
316
+ background: $color-text-muted;
317
+ background-clip: padding-box;
318
+ border: 2px solid transparent;
319
+ }
320
+ }
321
+
322
+ .empty {
323
+ padding: 1.5rem 0.75rem;
324
+ text-align: center;
325
+ color: $color-text-muted;
326
+ font-size: $text-sm;
327
+ }
328
+
329
+ /* ---------------- Tree row ---------------- */
330
+ .row {
331
+ position: relative;
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 0.4rem;
335
+ width: 100%;
336
+ padding: 0.35rem 0.4rem;
337
+ border-radius: 6px;
338
+ color: $color-text-primary;
339
+ font-family: $font-sans;
340
+ font-size: $text-sm;
341
+ text-align: left;
342
+ cursor: pointer;
343
+ transition: background-color 120ms ease, color 120ms ease;
344
+ user-select: none;
345
+ }
346
+
347
+ .row:hover:not(.rowDisabled) {
348
+ background: $color-surface-hover;
349
+ }
350
+
351
+ .rowActive:not(.rowDisabled) {
352
+ background: color-mix(in srgb, $evo-primary-color 10%, transparent);
353
+ box-shadow: inset 0 0 0 1px $color-border-subtle;
354
+ }
355
+
356
+ .rowSelected {
357
+ color: $evo-primary-color;
358
+
359
+ .rowLabel { font-weight: 600; }
360
+ }
361
+
362
+ .rowDisabled {
363
+ opacity: 0.4;
364
+ cursor: not-allowed;
365
+ }
366
+
367
+ /* indent guides */
368
+ .indent {
369
+ display: inline-flex;
370
+ align-items: stretch;
371
+ flex-shrink: 0;
372
+ }
373
+
374
+ .indentUnit {
375
+ width: 14px;
376
+ position: relative;
377
+ display: inline-block;
378
+
379
+ &::before {
380
+ content: '';
381
+ position: absolute;
382
+ top: -2px;
383
+ bottom: -2px;
384
+ left: 6px;
385
+ width: 1px;
386
+ background: $color-border-subtle;
387
+ }
388
+ }
389
+
390
+ /* expand/collapse toggle */
391
+ .toggle {
392
+ display: inline-flex;
393
+ align-items: center;
394
+ justify-content: center;
395
+ width: 16px;
396
+ height: 16px;
397
+ flex-shrink: 0;
398
+ color: $color-text-muted;
399
+ border-radius: 4px;
400
+ transition: transform 160ms cubic-bezier(0.4, 0, 0.2, 1), background-color $transition-fast, color $transition-fast;
401
+
402
+ &:hover {
403
+ background: $color-surface-hover;
404
+ color: $color-text-primary;
405
+ }
406
+ }
407
+
408
+ .toggleOpen {
409
+ transform: rotate(90deg);
410
+ color: $evo-primary-color;
411
+ }
412
+
413
+ .togglePlaceholder {
414
+ display: inline-block;
415
+ width: 16px;
416
+ height: 16px;
417
+ flex-shrink: 0;
418
+ }
419
+
420
+ .spinner {
421
+ width: 12px;
422
+ height: 12px;
423
+ border-radius: 50%;
424
+ border: 1.5px solid $color-border;
425
+ border-top-color: $evo-primary-color;
426
+ animation: spin 700ms linear infinite;
427
+ }
428
+
429
+ @keyframes spin {
430
+ to { transform: rotate(360deg); }
431
+ }
432
+
433
+ /* checkbox */
434
+ .checkbox {
435
+ display: inline-flex;
436
+ align-items: center;
437
+ justify-content: center;
438
+ width: 16px;
439
+ height: 16px;
440
+ flex-shrink: 0;
441
+ background: $color-surface;
442
+ border: 1px solid $color-border-strong;
443
+ border-radius: 4px;
444
+ color: $evo-primary-color;
445
+ transition:
446
+ background-color $transition-fast,
447
+ border-color $transition-fast,
448
+ color $transition-fast;
449
+ }
450
+
451
+ .row:hover:not(.rowDisabled) .checkbox {
452
+ border-color: $evo-primary-color;
453
+ }
454
+
455
+ .checkboxChecked {
456
+ background: $evo-primary-color;
457
+ border-color: $evo-primary-color;
458
+ color: $evo-primary-fg;
459
+ }
460
+
461
+ .checkboxMixed {
462
+ background: color-mix(in srgb, $evo-primary-color 18%, transparent);
463
+ border-color: $evo-primary-color;
464
+ color: $evo-primary-color;
465
+ }
466
+
467
+ /* icon + label */
468
+ .rowIcon {
469
+ display: inline-flex;
470
+ align-items: center;
471
+ justify-content: center;
472
+ width: 1.1rem;
473
+ height: 1.1rem;
474
+ flex-shrink: 0;
475
+ color: $color-text-secondary;
476
+ }
477
+
478
+ .rowText {
479
+ display: flex;
480
+ flex-direction: column;
481
+ flex: 1;
482
+ min-width: 0;
483
+ gap: 1px;
484
+ }
485
+
486
+ .rowLabel {
487
+ overflow: hidden;
488
+ text-overflow: ellipsis;
489
+ white-space: nowrap;
490
+ font-weight: 500;
491
+ }
492
+
493
+ .rowDesc {
494
+ font-size: $text-xs;
495
+ color: $color-text-muted;
496
+ font-weight: 400;
497
+ overflow: hidden;
498
+ text-overflow: ellipsis;
499
+ white-space: nowrap;
500
+ }
501
+
502
+ .match {
503
+ color: $evo-primary-color;
504
+ background: color-mix(in srgb, $evo-primary-color 16%, transparent);
505
+ border-radius: 3px;
506
+ padding: 0 2px;
507
+ }
508
+
509
+ /* trailing single-select check */
510
+ .check {
511
+ display: inline-flex;
512
+ align-items: center;
513
+ justify-content: center;
514
+ width: 16px;
515
+ height: 16px;
516
+ color: $evo-primary-color;
517
+ flex-shrink: 0;
518
+ }
519
+
520
+ /* ---------------- Footer / helpers ---------------- */
521
+ .menuFooter {
522
+ display: flex;
523
+ justify-content: space-between;
524
+ align-items: center;
525
+ padding: 0.4rem 0.75rem;
526
+ border-top: 1px solid $color-border-subtle;
527
+ background: $color-surface-sunken;
528
+ font-size: $text-xs;
529
+ color: $color-text-muted;
530
+ }
531
+
532
+ .footerBtn {
533
+ background: none;
534
+ border: none;
535
+ color: $evo-primary-color;
536
+ font-size: $text-xs;
537
+ font-weight: 500;
538
+ cursor: pointer;
539
+ padding: 2px 6px;
540
+ border-radius: 4px;
541
+ transition: background-color $transition-fast, color $transition-fast;
542
+
543
+ &:hover { background: color-mix(in srgb, $evo-primary-color 10%, transparent); color: $evo-primary-hover; }
544
+ }
545
+
546
+ .errorText {
547
+ font-size: $text-xs;
548
+ color: $evo-danger-color;
549
+ margin: 0;
550
+ line-height: 1.4;
551
+ }
552
+
553
+ .helperText {
554
+ font-size: $text-xs;
555
+ color: $color-text-muted;
556
+ margin: 0;
557
+ line-height: 1.4;
558
+ }