@miozu/jera 0.0.2 → 0.4.2

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 (82) hide show
  1. package/CLAUDE.md +734 -0
  2. package/README.md +219 -1
  3. package/llms.txt +97 -0
  4. package/package.json +54 -14
  5. package/src/actions/index.js +375 -0
  6. package/src/components/docs/CodeBlock.svelte +203 -0
  7. package/src/components/docs/DocSection.svelte +120 -0
  8. package/src/components/docs/PropsTable.svelte +136 -0
  9. package/src/components/docs/SplitPane.svelte +98 -0
  10. package/src/components/docs/index.js +14 -0
  11. package/src/components/feedback/Alert.svelte +234 -0
  12. package/src/components/feedback/EmptyState.svelte +179 -0
  13. package/src/components/feedback/ProgressBar.svelte +116 -0
  14. package/src/components/feedback/Skeleton.svelte +107 -0
  15. package/src/components/feedback/Spinner.svelte +77 -0
  16. package/src/components/feedback/Toast.svelte +261 -0
  17. package/src/components/forms/Checkbox.svelte +147 -0
  18. package/src/components/forms/Dropzone.svelte +248 -0
  19. package/src/components/forms/FileUpload.svelte +266 -0
  20. package/src/components/forms/IconInput.svelte +184 -0
  21. package/src/components/forms/Input.svelte +121 -0
  22. package/src/components/forms/NumberInput.svelte +225 -0
  23. package/src/components/forms/PinInput.svelte +169 -0
  24. package/src/components/forms/Radio.svelte +143 -0
  25. package/src/components/forms/RadioGroup.svelte +62 -0
  26. package/src/components/forms/RangeSlider.svelte +212 -0
  27. package/src/components/forms/SearchInput.svelte +175 -0
  28. package/src/components/forms/Select.svelte +324 -0
  29. package/src/components/forms/Switch.svelte +159 -0
  30. package/src/components/forms/Textarea.svelte +122 -0
  31. package/src/components/navigation/Accordion.svelte +65 -0
  32. package/src/components/navigation/AccordionItem.svelte +146 -0
  33. package/src/components/navigation/NavigationContainer.svelte +344 -0
  34. package/src/components/navigation/Sidebar.svelte +334 -0
  35. package/src/components/navigation/SidebarAccountGroup.svelte +495 -0
  36. package/src/components/navigation/SidebarAccountItem.svelte +492 -0
  37. package/src/components/navigation/SidebarGroup.svelte +230 -0
  38. package/src/components/navigation/SidebarGroupSwitcher.svelte +262 -0
  39. package/src/components/navigation/SidebarItem.svelte +210 -0
  40. package/src/components/navigation/SidebarNavigationItem.svelte +470 -0
  41. package/src/components/navigation/SidebarPopover.svelte +145 -0
  42. package/src/components/navigation/SidebarSearch.svelte +236 -0
  43. package/src/components/navigation/SidebarSection.svelte +158 -0
  44. package/src/components/navigation/SidebarToggle.svelte +86 -0
  45. package/src/components/navigation/Tabs.svelte +239 -0
  46. package/src/components/navigation/WorkspaceMenu.svelte +416 -0
  47. package/src/components/navigation/blocks/NavigationAccountGroup.svelte +396 -0
  48. package/src/components/navigation/blocks/NavigationCustomBlock.svelte +74 -0
  49. package/src/components/navigation/blocks/NavigationGroupSwitcher.svelte +277 -0
  50. package/src/components/navigation/blocks/NavigationSearch.svelte +300 -0
  51. package/src/components/navigation/blocks/NavigationSection.svelte +230 -0
  52. package/src/components/navigation/index.js +22 -0
  53. package/src/components/overlays/ConfirmDialog.svelte +272 -0
  54. package/src/components/overlays/Dropdown.svelte +153 -0
  55. package/src/components/overlays/DropdownDivider.svelte +23 -0
  56. package/src/components/overlays/DropdownItem.svelte +97 -0
  57. package/src/components/overlays/Modal.svelte +232 -0
  58. package/src/components/overlays/Popover.svelte +206 -0
  59. package/src/components/primitives/Avatar.svelte +132 -0
  60. package/src/components/primitives/Badge.svelte +118 -0
  61. package/src/components/primitives/Button.svelte +214 -0
  62. package/src/components/primitives/Card.svelte +104 -0
  63. package/src/components/primitives/Divider.svelte +105 -0
  64. package/src/components/primitives/LazyImage.svelte +104 -0
  65. package/src/components/primitives/Link.svelte +122 -0
  66. package/src/components/primitives/Stat.svelte +197 -0
  67. package/src/components/primitives/StatusBadge.svelte +122 -0
  68. package/src/index.js +183 -0
  69. package/src/tokens/colors.css +157 -0
  70. package/src/tokens/effects.css +128 -0
  71. package/src/tokens/index.css +81 -0
  72. package/src/tokens/spacing.css +49 -0
  73. package/src/tokens/typography.css +79 -0
  74. package/src/utils/cn.svelte.js +175 -0
  75. package/src/utils/highlighter.js +124 -0
  76. package/src/utils/index.js +22 -0
  77. package/src/utils/navigation.svelte.js +423 -0
  78. package/src/utils/reactive.svelte.js +328 -0
  79. package/src/utils/sidebar.svelte.js +211 -0
  80. package/jera.js +0 -135
  81. package/www/components/jera/Input/Input.svelte +0 -63
  82. package/www/components/jera/Input/index.js +0 -1
@@ -0,0 +1,212 @@
1
+ <!--
2
+ @component RangeSlider
3
+
4
+ A range slider input for selecting numeric values.
5
+
6
+ @example
7
+ <RangeSlider bind:value={volume} min={0} max={100} />
8
+
9
+ @example With label and formatting
10
+ <RangeSlider
11
+ bind:value={price}
12
+ min={0}
13
+ max={1000}
14
+ label="Price"
15
+ formatValue={(v) => `$${v}`}
16
+ />
17
+ -->
18
+ <script>
19
+ let {
20
+ value = $bindable(50),
21
+ min = 0,
22
+ max = 100,
23
+ step = 1,
24
+ label = '',
25
+ disabled = false,
26
+ showValues = true,
27
+ showCurrentValue = true,
28
+ formatValue = (val) => val.toString(),
29
+ size = 'md',
30
+ class: className = '',
31
+ id,
32
+ oninput,
33
+ onchange,
34
+ ...rest
35
+ } = $props();
36
+
37
+ const inputId = id || `slider-${Math.random().toString(36).slice(2, 9)}`;
38
+ const percentage = $derived(((value - min) / (max - min)) * 100);
39
+ </script>
40
+
41
+ <div class="slider-container slider-{size} {className}">
42
+ {#if label}
43
+ <label class="slider-label" for={inputId}>{label}</label>
44
+ {/if}
45
+
46
+ <div class="slider-wrapper">
47
+ <input
48
+ type="range"
49
+ id={inputId}
50
+ bind:value
51
+ {min}
52
+ {max}
53
+ {step}
54
+ {disabled}
55
+ class="slider"
56
+ style="--percentage: {percentage}%"
57
+ {oninput}
58
+ {onchange}
59
+ {...rest}
60
+ />
61
+ <div class="slider-track">
62
+ <div class="slider-fill" style="width: {percentage}%"></div>
63
+ </div>
64
+ </div>
65
+
66
+ {#if showValues}
67
+ <div class="slider-values">
68
+ <span class="slider-min">{formatValue(min)}</span>
69
+ {#if showCurrentValue}
70
+ <span class="slider-current">{formatValue(value)}</span>
71
+ {/if}
72
+ <span class="slider-max">{formatValue(max)}</span>
73
+ </div>
74
+ {/if}
75
+ </div>
76
+
77
+ <style>
78
+ .slider-container {
79
+ width: 100%;
80
+ }
81
+
82
+ .slider-label {
83
+ display: block;
84
+ font-size: var(--text-sm);
85
+ font-weight: 500;
86
+ color: var(--color-base05);
87
+ margin-bottom: var(--space-2);
88
+ }
89
+
90
+ .slider-wrapper {
91
+ position: relative;
92
+ width: 100%;
93
+ height: 0.5rem;
94
+ }
95
+
96
+ .slider-sm .slider-wrapper { height: 0.375rem; }
97
+ .slider-lg .slider-wrapper { height: 0.625rem; }
98
+
99
+ .slider {
100
+ position: absolute;
101
+ inset: 0;
102
+ width: 100%;
103
+ height: 100%;
104
+ appearance: none;
105
+ background: transparent;
106
+ cursor: pointer;
107
+ z-index: 10;
108
+ outline: none;
109
+ }
110
+
111
+ .slider:disabled {
112
+ cursor: not-allowed;
113
+ opacity: 0.5;
114
+ }
115
+
116
+ .slider-track {
117
+ position: absolute;
118
+ inset: 0;
119
+ width: 100%;
120
+ height: 100%;
121
+ background: var(--color-base02);
122
+ border-radius: 9999px;
123
+ pointer-events: none;
124
+ }
125
+
126
+ .slider-fill {
127
+ height: 100%;
128
+ background: var(--color-base0D);
129
+ border-radius: 9999px;
130
+ }
131
+
132
+ /* Webkit (Chrome, Safari, Edge) */
133
+ .slider::-webkit-slider-thumb {
134
+ appearance: none;
135
+ width: 1.25rem;
136
+ height: 1.25rem;
137
+ border-radius: 0.375rem;
138
+ background: var(--color-base00);
139
+ border: 2px solid var(--color-base0D);
140
+ cursor: pointer;
141
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
142
+ margin-top: -0.375rem;
143
+ }
144
+
145
+ .slider-sm .slider::-webkit-slider-thumb {
146
+ width: 1rem;
147
+ height: 1rem;
148
+ margin-top: -0.3125rem;
149
+ }
150
+
151
+ .slider-lg .slider::-webkit-slider-thumb {
152
+ width: 1.5rem;
153
+ height: 1.5rem;
154
+ margin-top: -0.4375rem;
155
+ }
156
+
157
+ .slider::-webkit-slider-thumb:hover {
158
+ transform: scale(1.1);
159
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-base0D) 20%, transparent);
160
+ }
161
+
162
+ .slider:active::-webkit-slider-thumb {
163
+ transform: scale(0.95);
164
+ }
165
+
166
+ .slider:focus-visible::-webkit-slider-thumb {
167
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-base0D) 30%, transparent);
168
+ }
169
+
170
+ /* Firefox */
171
+ .slider::-moz-range-thumb {
172
+ appearance: none;
173
+ width: 1.25rem;
174
+ height: 1.25rem;
175
+ border-radius: 0.375rem;
176
+ background: var(--color-base00);
177
+ border: 2px solid var(--color-base0D);
178
+ cursor: pointer;
179
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
180
+ }
181
+
182
+ .slider::-moz-range-thumb:hover {
183
+ transform: scale(1.1);
184
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-base0D) 20%, transparent);
185
+ }
186
+
187
+ .slider::-webkit-slider-runnable-track {
188
+ appearance: none;
189
+ background: transparent;
190
+ height: 100%;
191
+ }
192
+
193
+ .slider::-moz-range-track {
194
+ appearance: none;
195
+ background: transparent;
196
+ height: 100%;
197
+ }
198
+
199
+ .slider-values {
200
+ display: flex;
201
+ justify-content: space-between;
202
+ align-items: center;
203
+ margin-top: var(--space-2);
204
+ font-size: var(--text-xs);
205
+ color: var(--color-base04);
206
+ }
207
+
208
+ .slider-current {
209
+ font-weight: 500;
210
+ color: var(--color-base05);
211
+ }
212
+ </style>
@@ -0,0 +1,175 @@
1
+ <!--
2
+ @component SearchInput
3
+
4
+ A search input with icon and clearable functionality.
5
+
6
+ @example
7
+ <SearchInput bind:value={query} placeholder="Search..." />
8
+
9
+ @example With loading state
10
+ <SearchInput bind:value={query} loading={isSearching} />
11
+ -->
12
+ <script>
13
+ import { cn } from '../../utils/cn.svelte.js';
14
+
15
+ let {
16
+ value = $bindable(''),
17
+ placeholder = 'Search...',
18
+ disabled = false,
19
+ loading = false,
20
+ size = 'md',
21
+ class: className = '',
22
+ oninput,
23
+ onchange,
24
+ onclear,
25
+ ...rest
26
+ } = $props();
27
+
28
+ function handleClear() {
29
+ value = '';
30
+ onclear?.();
31
+ }
32
+
33
+ function handleKeydown(e) {
34
+ if (e.key === 'Escape' && value) {
35
+ handleClear();
36
+ }
37
+ }
38
+ </script>
39
+
40
+ <div class={cn('search-input', `search-input-${size}`, className)}>
41
+ <span class="search-icon">
42
+ {#if loading}
43
+ <svg class="search-spinner" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
44
+ <circle cx="12" cy="12" r="10" opacity="0.2" />
45
+ <path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round" />
46
+ </svg>
47
+ {:else}
48
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
49
+ <circle cx="11" cy="11" r="8"></circle>
50
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
51
+ </svg>
52
+ {/if}
53
+ </span>
54
+
55
+ <input
56
+ type="search"
57
+ bind:value
58
+ {placeholder}
59
+ {disabled}
60
+ class="search-field"
61
+ onkeydown={handleKeydown}
62
+ {oninput}
63
+ {onchange}
64
+ {...rest}
65
+ />
66
+
67
+ {#if value && !loading}
68
+ <button
69
+ type="button"
70
+ class="search-clear"
71
+ onclick={handleClear}
72
+ aria-label="Clear search"
73
+ >
74
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
75
+ <line x1="18" y1="6" x2="6" y2="18"></line>
76
+ <line x1="6" y1="6" x2="18" y2="18"></line>
77
+ </svg>
78
+ </button>
79
+ {/if}
80
+ </div>
81
+
82
+ <style>
83
+ .search-input {
84
+ position: relative;
85
+ display: flex;
86
+ align-items: center;
87
+ width: 100%;
88
+ }
89
+
90
+ .search-icon {
91
+ position: absolute;
92
+ left: 0.75rem;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ color: var(--color-base04);
97
+ pointer-events: none;
98
+ }
99
+
100
+ .search-spinner {
101
+ animation: spin 1s linear infinite;
102
+ }
103
+
104
+ @keyframes spin {
105
+ from { transform: rotate(0deg); }
106
+ to { transform: rotate(360deg); }
107
+ }
108
+
109
+ .search-field {
110
+ width: 100%;
111
+ padding: var(--space-2) var(--space-3);
112
+ padding-left: 2.5rem;
113
+ padding-right: 2.25rem;
114
+ font-size: var(--text-sm);
115
+ color: var(--color-base07);
116
+ background: var(--color-base00);
117
+ border: 1px solid var(--color-base02);
118
+ border-radius: var(--radius-lg);
119
+ transition: var(--transition-colors);
120
+ }
121
+
122
+ .search-input-sm .search-field {
123
+ padding: var(--space-1) var(--space-2);
124
+ padding-left: 2rem;
125
+ padding-right: 2rem;
126
+ font-size: var(--text-xs);
127
+ }
128
+
129
+ .search-input-lg .search-field {
130
+ padding: var(--space-3) var(--space-4);
131
+ padding-left: 3rem;
132
+ padding-right: 2.75rem;
133
+ font-size: var(--text-base);
134
+ }
135
+
136
+ .search-field::placeholder {
137
+ color: var(--color-base04);
138
+ }
139
+
140
+ .search-field:focus {
141
+ outline: none;
142
+ border-color: var(--color-base0D);
143
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-base0D) 20%, transparent);
144
+ }
145
+
146
+ .search-field:disabled {
147
+ opacity: 0.5;
148
+ cursor: not-allowed;
149
+ }
150
+
151
+ /* Hide native search clear button */
152
+ .search-field::-webkit-search-cancel-button {
153
+ display: none;
154
+ }
155
+
156
+ .search-clear {
157
+ position: absolute;
158
+ right: 0.5rem;
159
+ display: flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ padding: 0.25rem;
163
+ background: transparent;
164
+ border: none;
165
+ border-radius: var(--radius-sm);
166
+ color: var(--color-base04);
167
+ cursor: pointer;
168
+ transition: var(--transition-colors);
169
+ }
170
+
171
+ .search-clear:hover {
172
+ color: var(--color-base05);
173
+ background: var(--color-base02);
174
+ }
175
+ </style>
@@ -0,0 +1,324 @@
1
+ <!--
2
+ @component Select
3
+
4
+ An accessible dropdown select component with keyboard navigation.
5
+
6
+ @example
7
+ <Select
8
+ options={[
9
+ { value: 'opt1', label: 'Option 1' },
10
+ { value: 'opt2', label: 'Option 2' }
11
+ ]}
12
+ bind:value={selected}
13
+ placeholder="Select an option"
14
+ />
15
+ -->
16
+ <script>
17
+ import { cn } from '../../utils/cn.svelte.js';
18
+ import { clickOutside } from '../../actions/index.js';
19
+ import { generateId } from '../../utils/reactive.svelte.js';
20
+
21
+ let {
22
+ options = [],
23
+ value = $bindable(null),
24
+ placeholder = 'Select...',
25
+ labelKey = 'label',
26
+ valueKey = 'value',
27
+ disabled = false,
28
+ error = false,
29
+ class: className = '',
30
+ onchange
31
+ } = $props();
32
+
33
+ const triggerId = generateId();
34
+ const listboxId = generateId();
35
+
36
+ let isOpen = $state(false);
37
+ let highlightedIndex = $state(-1);
38
+ let triggerRef = $state(null);
39
+ let listRef = $state(null);
40
+
41
+ const selectedOption = $derived(
42
+ options.find(opt => opt[valueKey] === value) ?? null
43
+ );
44
+ const selectedLabel = $derived(
45
+ selectedOption ? selectedOption[labelKey] : placeholder
46
+ );
47
+ const hasValue = $derived(value !== null && value !== undefined);
48
+
49
+ $effect(() => {
50
+ if (!isOpen) {
51
+ highlightedIndex = -1;
52
+ return;
53
+ }
54
+ listRef?.focus();
55
+ const selectedIdx = options.findIndex(opt => opt[valueKey] === value);
56
+ highlightedIndex = selectedIdx >= 0 ? selectedIdx : 0;
57
+ });
58
+
59
+ $effect(() => {
60
+ if (!isOpen || highlightedIndex < 0) return;
61
+ const highlighted = listRef?.querySelector(`[data-index="${highlightedIndex}"]`);
62
+ highlighted?.scrollIntoView({ block: 'nearest' });
63
+ });
64
+
65
+ function toggle() {
66
+ if (disabled) return;
67
+ isOpen = !isOpen;
68
+ }
69
+
70
+ function close() {
71
+ isOpen = false;
72
+ }
73
+
74
+ function select(option) {
75
+ value = option[valueKey];
76
+ onchange?.(option);
77
+ close();
78
+ triggerRef?.focus();
79
+ }
80
+
81
+ function handleKeydown(event) {
82
+ if (disabled) return;
83
+
84
+ switch (event.key) {
85
+ case 'ArrowDown':
86
+ event.preventDefault();
87
+ if (!isOpen) isOpen = true;
88
+ else highlightedIndex = Math.min(highlightedIndex + 1, options.length - 1);
89
+ break;
90
+ case 'ArrowUp':
91
+ event.preventDefault();
92
+ if (!isOpen) isOpen = true;
93
+ else highlightedIndex = Math.max(highlightedIndex - 1, 0);
94
+ break;
95
+ case 'Enter':
96
+ case ' ':
97
+ event.preventDefault();
98
+ if (isOpen && highlightedIndex >= 0) select(options[highlightedIndex]);
99
+ else toggle();
100
+ break;
101
+ case 'Escape':
102
+ event.preventDefault();
103
+ close();
104
+ triggerRef?.focus();
105
+ break;
106
+ case 'Home':
107
+ event.preventDefault();
108
+ highlightedIndex = 0;
109
+ break;
110
+ case 'End':
111
+ event.preventDefault();
112
+ highlightedIndex = options.length - 1;
113
+ break;
114
+ case 'Tab':
115
+ close();
116
+ break;
117
+ }
118
+ }
119
+ </script>
120
+
121
+ <div class={cn('select-wrapper', className)} use:clickOutside={close}>
122
+ <button
123
+ bind:this={triggerRef}
124
+ id={triggerId}
125
+ type="button"
126
+ class={cn('select-trigger', isOpen && 'select-open', error && 'select-error', hasValue && 'has-value')}
127
+ {disabled}
128
+ aria-haspopup="listbox"
129
+ aria-expanded={isOpen}
130
+ aria-controls={listboxId}
131
+ aria-invalid={error || undefined}
132
+ onclick={toggle}
133
+ onkeydown={handleKeydown}
134
+ >
135
+ <span class="select-value">{selectedLabel}</span>
136
+ <svg class={cn('select-chevron', isOpen && 'rotated')} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
137
+ <path d="m6 9 6 6 6-6" />
138
+ </svg>
139
+ </button>
140
+
141
+ {#if isOpen}
142
+ <div
143
+ bind:this={listRef}
144
+ id={listboxId}
145
+ role="listbox"
146
+ tabindex="-1"
147
+ aria-labelledby={triggerId}
148
+ class="select-dropdown"
149
+ onkeydown={handleKeydown}
150
+ >
151
+ {#if options.length === 0}
152
+ <div class="select-empty">No options available</div>
153
+ {:else}
154
+ {#each options as option, index (option[valueKey])}
155
+ {@const isSelected = option[valueKey] === value}
156
+ {@const isHighlighted = index === highlightedIndex}
157
+ <button
158
+ id="{listboxId}-option-{index}"
159
+ type="button"
160
+ role="option"
161
+ data-index={index}
162
+ aria-selected={isSelected}
163
+ class={cn('select-option', isHighlighted && 'highlighted', isSelected && 'selected')}
164
+ onclick={() => select(option)}
165
+ onmouseenter={() => highlightedIndex = index}
166
+ >
167
+ <span class={cn('select-check', !isSelected && 'invisible')}>
168
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
169
+ <path d="M20 6 9 17l-5-5" />
170
+ </svg>
171
+ </span>
172
+ <span class="select-option-label">{option[labelKey]}</span>
173
+ </button>
174
+ {/each}
175
+ {/if}
176
+ </div>
177
+ {/if}
178
+ </div>
179
+
180
+ <style>
181
+ .select-wrapper {
182
+ position: relative;
183
+ }
184
+
185
+ .select-trigger {
186
+ position: relative;
187
+ width: 100%;
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: space-between;
191
+ gap: 0.5rem;
192
+ height: 2.5rem;
193
+ padding: 0 0.75rem;
194
+ border-radius: 0.5rem;
195
+ border: 1px solid var(--color-base03);
196
+ background-color: var(--color-base00);
197
+ color: var(--color-base04);
198
+ font-size: 0.875rem;
199
+ text-align: left;
200
+ cursor: pointer;
201
+ transition: border-color 150ms, box-shadow 150ms;
202
+ }
203
+
204
+ .select-trigger:hover:not(:disabled) {
205
+ border-color: var(--color-base04);
206
+ }
207
+
208
+ .select-trigger:focus-visible {
209
+ outline: none;
210
+ border-color: var(--color-base0D);
211
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-base0D) 20%, transparent);
212
+ }
213
+
214
+ .select-trigger:disabled {
215
+ opacity: 0.5;
216
+ cursor: not-allowed;
217
+ }
218
+
219
+ .select-trigger.has-value {
220
+ color: var(--color-base07);
221
+ }
222
+
223
+ .select-trigger.select-open {
224
+ border-color: var(--color-base0D);
225
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-base0D) 20%, transparent);
226
+ }
227
+
228
+ .select-trigger.select-error {
229
+ border-color: var(--color-base08);
230
+ }
231
+
232
+ .select-value {
233
+ flex: 1;
234
+ overflow: hidden;
235
+ text-overflow: ellipsis;
236
+ white-space: nowrap;
237
+ }
238
+
239
+ .select-chevron {
240
+ width: 1rem;
241
+ height: 1rem;
242
+ flex-shrink: 0;
243
+ color: var(--color-base04);
244
+ transition: transform 200ms;
245
+ }
246
+
247
+ .select-chevron.rotated {
248
+ transform: rotate(180deg);
249
+ }
250
+
251
+ .select-dropdown {
252
+ position: absolute;
253
+ z-index: 50;
254
+ width: 100%;
255
+ margin-top: 0.25rem;
256
+ max-height: 15rem;
257
+ overflow: auto;
258
+ background-color: var(--color-base01);
259
+ border: 1px solid var(--color-base03);
260
+ border-radius: 0.5rem;
261
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3);
262
+ padding: 0.25rem 0;
263
+ animation: dropdown-in 150ms ease-out;
264
+ }
265
+
266
+ .select-empty {
267
+ padding: 0.5rem 0.75rem;
268
+ font-size: 0.875rem;
269
+ color: var(--color-base04);
270
+ text-align: center;
271
+ }
272
+
273
+ .select-option {
274
+ width: 100%;
275
+ display: flex;
276
+ align-items: center;
277
+ gap: 0.5rem;
278
+ padding: 0.5rem 0.75rem;
279
+ font-size: 0.875rem;
280
+ text-align: left;
281
+ color: var(--color-base05);
282
+ background: transparent;
283
+ border: none;
284
+ cursor: pointer;
285
+ transition: background-color 75ms;
286
+ }
287
+
288
+ .select-option.highlighted {
289
+ background-color: var(--color-base02);
290
+ }
291
+
292
+ .select-option.selected {
293
+ color: var(--color-base0D);
294
+ font-weight: 500;
295
+ }
296
+
297
+ .select-check {
298
+ width: 1rem;
299
+ height: 1rem;
300
+ flex-shrink: 0;
301
+ }
302
+
303
+ .select-check.invisible {
304
+ visibility: hidden;
305
+ }
306
+
307
+ .select-option-label {
308
+ flex: 1;
309
+ overflow: hidden;
310
+ text-overflow: ellipsis;
311
+ white-space: nowrap;
312
+ }
313
+
314
+ @keyframes dropdown-in {
315
+ from {
316
+ opacity: 0;
317
+ transform: translateY(-0.5rem);
318
+ }
319
+ to {
320
+ opacity: 1;
321
+ transform: translateY(0);
322
+ }
323
+ }
324
+ </style>