webcake-ui-kit 1.0.0 → 1.0.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +358 -8
  3. package/package.json +68 -5
  4. package/src/components/accordion/Accordion.vue +70 -0
  5. package/src/components/accordion/accordion.css +5 -0
  6. package/src/components/accordion-item/AccordionItem.vue +98 -0
  7. package/src/components/accordion-item/accordion-item.css +143 -0
  8. package/src/components/alert-dialog/AlertDialog.vue +82 -0
  9. package/src/components/alert-dialog/alert-dialog.css +33 -0
  10. package/src/components/badge/Badge.vue +2 -2
  11. package/src/components/badge/badge.css +1 -4
  12. package/src/components/breadcrumb/Breadcrumb.vue +85 -0
  13. package/src/components/breadcrumb/breadcrumb.css +90 -0
  14. package/src/components/button/Button.vue +77 -10
  15. package/src/components/button/button.css +258 -24
  16. package/src/components/button-group/ButtonGroup.vue +25 -0
  17. package/src/components/button-group/button-group.css +30 -0
  18. package/src/components/checkbox/Checkbox.vue +55 -0
  19. package/src/components/checkbox/checkbox.css +86 -0
  20. package/src/components/checkbox-group/CheckboxGroup.vue +50 -0
  21. package/src/components/checkbox-group/checkbox-group.css +35 -0
  22. package/src/components/dialog/Dialog.vue +355 -0
  23. package/src/components/dialog/dialog.css +255 -0
  24. package/src/components/divider/Divider.vue +35 -0
  25. package/src/components/divider/divider.css +38 -0
  26. package/src/components/input/Input.vue +99 -0
  27. package/src/components/input/input.css +123 -0
  28. package/src/components/pagination/Pagination.vue +211 -0
  29. package/src/components/pagination/pagination.css +13 -0
  30. package/src/components/radio/Radio.vue +74 -0
  31. package/src/components/radio/radio.css +89 -0
  32. package/src/components/radio-group/RadioGroup.vue +70 -0
  33. package/src/components/radio-group/radio_group.css +11 -0
  34. package/src/components/rich-checkbox-group/RichCheckboxGroup.vue +59 -0
  35. package/src/components/rich-checkbox-group/rich-checkbox-group.css +54 -0
  36. package/src/components/rich-switch-group/RichSwitchGroup.vue +49 -0
  37. package/src/components/rich-switch-group/rich_switch_group.css +45 -0
  38. package/src/components/select/Select.vue +262 -0
  39. package/src/components/select/select.css +207 -0
  40. package/src/components/select-option/SelectOption.vue +82 -0
  41. package/src/components/select-option/select_option.css +60 -0
  42. package/src/components/sidebar-group-label/SidebarGroupLabel.vue +68 -0
  43. package/src/components/sidebar-group-label/sidebar_group_label.css +61 -0
  44. package/src/components/sidebar-item/SidebarItem.vue +110 -0
  45. package/src/components/sidebar-item/sidebar_item.css +142 -0
  46. package/src/components/slider/Slider.vue +255 -0
  47. package/src/components/slider/slider.css +89 -0
  48. package/src/components/spinner/Spinner.vue +47 -0
  49. package/src/components/spinner/spinner.css +48 -0
  50. package/src/components/switch/Switch.vue +32 -0
  51. package/src/components/switch/switch.css +46 -0
  52. package/src/components/switch-group/SwitchGroup.vue +32 -0
  53. package/src/components/switch-group/switch_group.css +28 -0
  54. package/src/components/tabs/Tabs.vue +57 -0
  55. package/src/components/tabs/tabs.css +118 -0
  56. package/src/components/tag/Tag.vue +47 -0
  57. package/src/components/tag/tag.css +115 -0
  58. package/src/components/toggle/Toggle.vue +112 -0
  59. package/src/components/toggle/toggle.css +174 -0
  60. package/src/components/toggle-group/ToggleGroup.vue +57 -0
  61. package/src/components/toggle-group/toggle-group.css +68 -0
  62. package/src/icons/LoaderIcon.vue +22 -0
  63. package/src/index.js +29 -2
  64. package/src/styles/border_radius.css +3 -3
  65. package/src/styles/color_general.css +21 -14
  66. package/src/styles/shadow.css +2 -2
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <label
3
+ :class="['ui-rich-checkbox', flipped && 'ui-rich-checkbox--flipped', disabled && 'ui-rich-checkbox--disabled']"
4
+ >
5
+ <span class="ui-rich-checkbox__aligner">
6
+ <Checkbox
7
+ class="ui-rich-checkbox__box"
8
+ :checked="isChecked"
9
+ :error="error"
10
+ :disabled="disabled"
11
+ @change="onChange"
12
+ />
13
+ </span>
14
+ <span class="ui-rich-checkbox__content">
15
+ <span class="ui-rich-checkbox__label">
16
+ <slot>{{ label }}</slot>
17
+ </span>
18
+ <span v-if="hasDescription" class="ui-rich-checkbox__description">
19
+ <slot name="description">{{ description }}</slot>
20
+ </span>
21
+ </span>
22
+ </label>
23
+ </template>
24
+
25
+ <script>
26
+ import Checkbox from '../checkbox/Checkbox.vue'
27
+
28
+ export default {
29
+ name: 'RichCheckboxGroup',
30
+ components: { Checkbox },
31
+ model: { prop: 'checked', event: 'change' },
32
+ props: {
33
+ checked: { type: Boolean, default: false },
34
+ modelValue: { type: Boolean, default: undefined },
35
+ error: { type: Boolean, default: false },
36
+ disabled: { type: Boolean, default: false },
37
+ flipped: { type: Boolean, default: false },
38
+ label: { type: String, default: '' },
39
+ description: { type: String, default: '' }
40
+ },
41
+ emits: ['change', 'update:modelValue'],
42
+ computed: {
43
+ isChecked() {
44
+ return this.modelValue !== undefined ? this.modelValue : this.checked
45
+ },
46
+ hasDescription() {
47
+ return !!this.$slots['description'] || !!this.description
48
+ }
49
+ },
50
+ methods: {
51
+ onChange(next) {
52
+ this.$emit('change', next)
53
+ this.$emit('update:modelValue', next)
54
+ }
55
+ }
56
+ }
57
+ </script>
58
+
59
+ <style src="./rich-checkbox-group.css" scoped></style>
@@ -0,0 +1,54 @@
1
+ .ui-rich-checkbox {
2
+ display: flex;
3
+ align-items: flex-start;
4
+ gap: var(--spacing-sm);
5
+ padding: var(--spacing-sm);
6
+ background: var(--card);
7
+ border: 1px solid var(--border-primary);
8
+ border-radius: var(--radius-10);
9
+ cursor: pointer;
10
+ font-family: var(--font-family-body);
11
+ }
12
+
13
+ .ui-rich-checkbox--flipped {
14
+ flex-direction: row-reverse;
15
+ }
16
+
17
+ .ui-rich-checkbox--disabled {
18
+ cursor: not-allowed;
19
+ opacity: 0.6;
20
+ }
21
+
22
+ .ui-rich-checkbox__aligner {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ flex-shrink: 0;
26
+ padding-top: 2.5px;
27
+ }
28
+
29
+ .ui-rich-checkbox__content {
30
+ display: flex;
31
+ flex: 1 0 0;
32
+ min-width: 0;
33
+ flex-direction: column;
34
+ gap: var(--spacing-6);
35
+ text-align: left;
36
+ }
37
+
38
+ .ui-rich-checkbox__label {
39
+ width: 100%;
40
+ font-size: var(--paragraph-small-font-size);
41
+ line-height: var(--paragraph-small-line-height);
42
+ letter-spacing: var(--paragraph-small-letter-spacing);
43
+ font-weight: var(--paragraph-font-weight);
44
+ color: var(--foreground-alt);
45
+ }
46
+
47
+ .ui-rich-checkbox__description {
48
+ width: 100%;
49
+ font-size: var(--paragraph-mini-font-size);
50
+ line-height: var(--paragraph-mini-line-height);
51
+ letter-spacing: var(--paragraph-mini-letter-spacing);
52
+ font-weight: var(--paragraph-font-weight);
53
+ color: var(--muted-fg);
54
+ }
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <div
3
+ class="ui-rich-switch-group"
4
+ :class="{
5
+ 'ui-rich-switch-group--flipped': flipped,
6
+ 'ui-rich-switch-group--disabled': disabled
7
+ }"
8
+ >
9
+ <div v-if="!flipped" class="ui-rich-switch-group__aligner">
10
+ <Switch :value="value" :disabled="disabled" @change="$emit('change', $event)" @input="$emit('input', $event)" />
11
+ </div>
12
+ <div class="ui-rich-switch-group__content">
13
+ <span class="ui-rich-switch-group__label"
14
+ ><slot>{{ label }}</slot></span
15
+ >
16
+ <span v-if="hasDescription" class="ui-rich-switch-group__description">
17
+ <slot name="description">{{ description }}</slot>
18
+ </span>
19
+ </div>
20
+ <div v-if="flipped" class="ui-rich-switch-group__aligner">
21
+ <Switch :value="value" :disabled="disabled" @change="$emit('change', $event)" @input="$emit('input', $event)" />
22
+ </div>
23
+ </div>
24
+ </template>
25
+
26
+ <script>
27
+ import Switch from '../switch/Switch.vue'
28
+
29
+ export default {
30
+ name: 'RichSwitchGroup',
31
+ components: { Switch: Switch },
32
+ props: {
33
+ value: { type: Boolean, default: false },
34
+ disabled: { type: Boolean, default: false },
35
+ flipped: { type: Boolean, default: false },
36
+ label: { type: String, default: '' },
37
+ description: { type: String, default: '' }
38
+ },
39
+ emits: ['change', 'input'],
40
+ computed: {
41
+ hasDescription: function () {
42
+ var hasSlot = !!((this.$scopedSlots && this.$scopedSlots['description']) || this.$slots['description'])
43
+ return hasSlot || !!this.description
44
+ }
45
+ }
46
+ }
47
+ </script>
48
+
49
+ <style src="./rich_switch_group.css" scoped></style>
@@ -0,0 +1,45 @@
1
+ .ui-rich-switch-group {
2
+ display: flex;
3
+ align-items: flex-start;
4
+ gap: 12px;
5
+ padding: 12px;
6
+ background: var(--inverse-fg);
7
+ border: 1px solid var(--border-primary);
8
+ border-radius: 10px;
9
+ width: 240px;
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ .ui-rich-switch-group--disabled {
14
+ opacity: 0.4;
15
+ pointer-events: none;
16
+ }
17
+
18
+ .ui-rich-switch-group__aligner {
19
+ padding-top: 2px;
20
+ flex-shrink: 0;
21
+ }
22
+
23
+ .ui-rich-switch-group__content {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: 6px;
27
+ flex: 1;
28
+ min-width: 0;
29
+ }
30
+
31
+ .ui-rich-switch-group__label {
32
+ display: block;
33
+ font-family: var(--font-family-body);
34
+ font-size: var(--paragraph-small-font-size);
35
+ line-height: var(--paragraph-small-line-height);
36
+ color: var(--secondary-fg);
37
+ }
38
+
39
+ .ui-rich-switch-group__description {
40
+ display: block;
41
+ font-family: var(--font-family-body);
42
+ font-size: var(--paragraph-mini-font-size);
43
+ line-height: var(--paragraph-mini-line-height);
44
+ color: var(--muted-fg);
45
+ }
@@ -0,0 +1,262 @@
1
+ <template>
2
+ <div
3
+ class="ui-select"
4
+ :class="[
5
+ `ui-select--${size}`,
6
+ {
7
+ 'ui-select--open': isOpen,
8
+ 'ui-select--disabled': disabled,
9
+ 'ui-select--error': error,
10
+ 'ui-select--loading': loading
11
+ }
12
+ ]"
13
+ tabindex="0"
14
+ @click="toggle"
15
+ @keydown.esc="close"
16
+ @keydown.enter.prevent="toggle"
17
+ @keydown.space.prevent="toggle"
18
+ >
19
+ <span class="ui-select__left">
20
+ <span v-if="hasIconSlot" class="ui-select__icon">
21
+ <slot name="icon" />
22
+ </span>
23
+ <span v-if="prepend" class="ui-select__prepend">{{ prepend }}</span>
24
+ <span class="ui-select__value" :class="{ 'ui-select__value--placeholder': !selectedLabel }">
25
+ {{ selectedLabel || placeholder }}
26
+ </span>
27
+ </span>
28
+
29
+ <spinner v-if="loading" type="mirrored" size="sm" />
30
+ <span v-else class="ui-select__chevron">
31
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
32
+ <path
33
+ d="M4 6L8 10L12 6"
34
+ stroke="currentColor"
35
+ stroke-width="1.5"
36
+ stroke-linecap="round"
37
+ stroke-linejoin="round"
38
+ />
39
+ </svg>
40
+ </span>
41
+
42
+ <div ref="dropdown" v-show="isOpen" class="ui-select__dropdown" :style="dropdownStyle" @click.stop>
43
+ <div v-if="canScrollUp" class="ui-select__scroll-indicator ui-select__scroll-indicator--up">
44
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
45
+ <path
46
+ d="M4 10L8 6L12 10"
47
+ stroke="currentColor"
48
+ stroke-width="1.5"
49
+ stroke-linecap="round"
50
+ stroke-linejoin="round"
51
+ />
52
+ </svg>
53
+ </div>
54
+ <div
55
+ ref="options"
56
+ class="ui-select__options"
57
+ :style="listHeight ? { maxHeight: listHeight + 'px' } : {}"
58
+ @scroll="updateScrollIndicators"
59
+ >
60
+ <slot>
61
+ <select-option
62
+ v-for="opt in normalizedOptions"
63
+ :key="opt.value"
64
+ :value="opt.value"
65
+ :label="opt.label"
66
+ :disabled="opt.disabled"
67
+ />
68
+ </slot>
69
+ </div>
70
+ <div v-if="canScrollDown" class="ui-select__scroll-indicator ui-select__scroll-indicator--down">
71
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
72
+ <path
73
+ d="M4 6L8 10L12 6"
74
+ stroke="currentColor"
75
+ stroke-width="1.5"
76
+ stroke-linecap="round"
77
+ stroke-linejoin="round"
78
+ />
79
+ </svg>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </template>
84
+
85
+ <script>
86
+ import SelectOption from '../select-option/SelectOption.vue'
87
+ import Spinner from '../spinner/Spinner.vue'
88
+
89
+ export default {
90
+ name: 'Select',
91
+
92
+ components: { SelectOption, Spinner },
93
+
94
+ provide() {
95
+ return { select: this }
96
+ },
97
+
98
+ model: {
99
+ prop: 'value',
100
+ event: 'change'
101
+ },
102
+
103
+ props: {
104
+ size: {
105
+ type: String,
106
+ default: 'default',
107
+ validator: function (v) {
108
+ return ['mini', 'sm', 'default', 'lg'].includes(v)
109
+ }
110
+ },
111
+ value: {
112
+ type: String,
113
+ default: ''
114
+ },
115
+ options: {
116
+ type: Array,
117
+ default: () => []
118
+ },
119
+ placeholder: {
120
+ type: String,
121
+ default: 'Select an item'
122
+ },
123
+ prepend: {
124
+ type: String,
125
+ default: ''
126
+ },
127
+ disabled: {
128
+ type: Boolean,
129
+ default: false
130
+ },
131
+ error: {
132
+ type: Boolean,
133
+ default: false
134
+ },
135
+ loading: {
136
+ type: Boolean,
137
+ default: false
138
+ },
139
+ listHeight: {
140
+ type: Number,
141
+ default: 256
142
+ }
143
+ },
144
+
145
+ emits: ['change'],
146
+
147
+ data() {
148
+ return {
149
+ isOpen: false,
150
+ canScrollUp: false,
151
+ canScrollDown: false,
152
+ labelCache: {},
153
+ dropdownStyle: {}
154
+ }
155
+ },
156
+
157
+ watch: {
158
+ isOpen(val) {
159
+ if (val) {
160
+ this.$nextTick(() => {
161
+ this.positionDropdown()
162
+ this.updateScrollIndicators()
163
+ })
164
+ } else {
165
+ this.canScrollUp = false
166
+ this.canScrollDown = false
167
+ }
168
+ }
169
+ },
170
+
171
+ computed: {
172
+ normalizedOptions() {
173
+ return this.options.map(opt =>
174
+ typeof opt === 'string'
175
+ ? { label: opt, value: opt, disabled: false }
176
+ : { label: opt.label || opt.value, value: opt.value, disabled: !!opt.disabled }
177
+ )
178
+ },
179
+ selectedLabel() {
180
+ if (!this.value) return ''
181
+ if (this.labelCache[this.value]) return this.labelCache[this.value]
182
+ const opt = this.normalizedOptions.find(o => o.value === this.value)
183
+ return opt ? opt.label : this.value
184
+ },
185
+ hasIconSlot() {
186
+ return !!((this.$scopedSlots && this.$scopedSlots['icon']) || this.$slots['icon'])
187
+ }
188
+ },
189
+
190
+ mounted() {
191
+ document.addEventListener('click', this.handleOutsideClick)
192
+ window.addEventListener('scroll', this.handleScroll, true)
193
+ window.addEventListener('resize', this.handleResize)
194
+ if (typeof document !== 'undefined' && this.$refs.dropdown) {
195
+ document.body.appendChild(this.$refs.dropdown)
196
+ }
197
+ if (typeof this.$on === 'function') {
198
+ // eslint-disable-next-line vue/no-deprecated-events-api
199
+ this.$on('hook:beforeDestroy', this.cleanup)
200
+ }
201
+ },
202
+
203
+ beforeUnmount() {
204
+ this.cleanup()
205
+ },
206
+
207
+ methods: {
208
+ toggle() {
209
+ if (this.disabled || this.loading) return
210
+ this.isOpen = !this.isOpen
211
+ },
212
+ close() {
213
+ this.isOpen = false
214
+ },
215
+ select(val) {
216
+ this.$emit('change', val)
217
+ this.close()
218
+ },
219
+ registerOption(value, label) {
220
+ this.labelCache = Object.assign({}, this.labelCache, { [value]: label })
221
+ },
222
+ positionDropdown() {
223
+ if (!this.$el || !this.$refs.dropdown) return
224
+ var rect = this.$el.getBoundingClientRect()
225
+ this.dropdownStyle = {
226
+ position: 'fixed',
227
+ top: rect.bottom + 4 + 'px',
228
+ left: rect.left + 'px',
229
+ width: rect.width + 'px',
230
+ zIndex: 1050
231
+ }
232
+ },
233
+ updateScrollIndicators() {
234
+ const el = this.$refs.options
235
+ if (!el) return
236
+ this.canScrollUp = el.scrollTop > 0
237
+ this.canScrollDown = el.scrollTop + el.clientHeight < el.scrollHeight
238
+ },
239
+ handleOutsideClick(e) {
240
+ if (!this.$el.contains(e.target) && !(this.$refs.dropdown && this.$refs.dropdown.contains(e.target))) {
241
+ this.close()
242
+ }
243
+ },
244
+ handleScroll() {
245
+ if (this.isOpen) this.positionDropdown()
246
+ },
247
+ handleResize() {
248
+ if (this.isOpen) this.positionDropdown()
249
+ },
250
+ cleanup() {
251
+ document.removeEventListener('click', this.handleOutsideClick)
252
+ window.removeEventListener('scroll', this.handleScroll, true)
253
+ window.removeEventListener('resize', this.handleResize)
254
+ if (this.$refs.dropdown && this.$refs.dropdown.parentNode === document.body) {
255
+ document.body.removeChild(this.$refs.dropdown)
256
+ }
257
+ }
258
+ }
259
+ }
260
+ </script>
261
+
262
+ <style src="./select.css" scoped></style>
@@ -0,0 +1,207 @@
1
+ .ui-select {
2
+ position: relative;
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--spacing-xs);
6
+ min-height: 36px;
7
+ width: 100%;
8
+ padding: var(--spacing-xs) var(--spacing-xs) var(--spacing-xs) var(--spacing-sm);
9
+ border: 1px solid var(--border-primary);
10
+ border-radius: var(--radius-10);
11
+ background: var(--input);
12
+ box-shadow: var(--shadow-xs);
13
+ cursor: pointer;
14
+ user-select: none;
15
+ box-sizing: border-box;
16
+ outline: none;
17
+ transition:
18
+ border-color 0.15s,
19
+ box-shadow 0.15s;
20
+ }
21
+
22
+ .ui-select:hover:not(.ui-select--disabled) {
23
+ border-color: var(--border-focus);
24
+ }
25
+
26
+ .ui-select:focus-visible,
27
+ .ui-select--open {
28
+ border-color: var(--border-focus);
29
+ box-shadow: var(--shadow-xs), var(--shadow-focus-ring);
30
+ }
31
+
32
+ .ui-select--disabled {
33
+ opacity: 0.5;
34
+ cursor: not-allowed;
35
+ pointer-events: none;
36
+ }
37
+
38
+ .ui-select--loading {
39
+ cursor: wait;
40
+ pointer-events: none;
41
+ }
42
+
43
+ .ui-select--error {
44
+ border-color: var(--destructive-border);
45
+ }
46
+
47
+ .ui-select--error:hover:not(.ui-select--disabled),
48
+ .ui-select--error:focus-visible,
49
+ .ui-select--error.ui-select--open {
50
+ border-color: var(--destructive-border);
51
+ box-shadow: var(--shadow-xs), var(--shadow-focus-ring-error);
52
+ }
53
+
54
+ .ui-select__left {
55
+ flex: 1;
56
+ min-width: 0;
57
+ display: flex;
58
+ align-items: center;
59
+ gap: var(--spacing-xs);
60
+ overflow: hidden;
61
+ }
62
+
63
+ .ui-select__icon {
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ width: 20px;
68
+ height: 20px;
69
+ flex-shrink: 0;
70
+ color: var(--muted-fg);
71
+ }
72
+
73
+ .ui-select__prepend {
74
+ font-family: var(--font-family-body);
75
+ font-size: var(--paragraph-small-font-size);
76
+ line-height: var(--paragraph-small-line-height);
77
+ color: var(--muted-fg);
78
+ white-space: nowrap;
79
+ flex-shrink: 0;
80
+ }
81
+
82
+ .ui-select__value {
83
+ font-family: var(--font-family-body);
84
+ font-size: var(--paragraph-small-font-size);
85
+ line-height: var(--paragraph-small-line-height);
86
+ color: var(--primary-fg);
87
+ overflow: hidden;
88
+ text-overflow: ellipsis;
89
+ white-space: nowrap;
90
+ flex: 1;
91
+ min-width: 0;
92
+ }
93
+
94
+ .ui-select__value--placeholder {
95
+ color: var(--muted-fg);
96
+ }
97
+
98
+ .ui-select__chevron {
99
+ display: flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ width: 16px;
103
+ height: 16px;
104
+ flex-shrink: 0;
105
+ color: var(--muted-fg);
106
+ transition: transform 0.2s ease;
107
+ }
108
+
109
+ .ui-select--open .ui-select__chevron {
110
+ transform: rotate(180deg);
111
+ }
112
+
113
+ .ui-select__spinner {
114
+ display: flex;
115
+ align-items: center;
116
+ justify-content: center;
117
+ width: 16px;
118
+ height: 16px;
119
+ flex-shrink: 0;
120
+ color: var(--muted-fg);
121
+ animation: ui-select-spin 0.75s linear infinite;
122
+ }
123
+
124
+ @keyframes ui-select-spin {
125
+ to {
126
+ transform: rotate(360deg);
127
+ }
128
+ }
129
+
130
+ /* — sizes — */
131
+ .ui-select--lg {
132
+ min-height: 40px;
133
+ padding: var(--spacing-10) var(--spacing-xs) var(--spacing-10) var(--spacing-md);
134
+ gap: var(--spacing-sm);
135
+ }
136
+ .ui-select--lg .ui-select__left {
137
+ gap: var(--spacing-sm);
138
+ }
139
+
140
+ /* default size is the base — no extra rules needed */
141
+
142
+ .ui-select--sm {
143
+ min-height: 32px;
144
+ padding: var(--spacing-6) var(--spacing-xs);
145
+ gap: var(--spacing-6);
146
+ }
147
+ .ui-select--sm .ui-select__left {
148
+ gap: var(--spacing-6);
149
+ }
150
+
151
+ .ui-select--mini {
152
+ min-height: 28px;
153
+ padding: var(--spacing-2xs) var(--spacing-6);
154
+ gap: var(--spacing-2xs);
155
+ border-radius: var(--rounded-lg);
156
+ }
157
+ .ui-select--mini .ui-select__left {
158
+ gap: var(--spacing-2xs);
159
+ }
160
+ .ui-select--mini .ui-select__value,
161
+ .ui-select--mini .ui-select__prepend {
162
+ font-size: var(--paragraph-mini-font-size);
163
+ line-height: var(--paragraph-mini-line-height);
164
+ }
165
+
166
+ /* Dropdown — position/size set inline via JS (portal to body) */
167
+ .ui-select__dropdown {
168
+ background: var(--input);
169
+ border: 1px solid var(--border-primary);
170
+ border-radius: var(--radius-10);
171
+ box-shadow: var(--shadow-md);
172
+ overflow: hidden;
173
+ display: flex;
174
+ flex-direction: column;
175
+ scrollbar-width: none;
176
+ -ms-overflow-style: none;
177
+ }
178
+
179
+ .ui-select__dropdown .ui-select__options::-webkit-scrollbar {
180
+ display: none;
181
+ }
182
+
183
+ .ui-select__options {
184
+ overflow-y: auto;
185
+ padding: var(--spacing-2xs);
186
+ }
187
+
188
+ .ui-select__scroll-indicator {
189
+ position: absolute;
190
+ left: 0;
191
+ right: 0;
192
+ display: flex;
193
+ align-items: center;
194
+ justify-content: center;
195
+ flex-shrink: 0;
196
+ color: var(--muted-fg);
197
+ background: var(--input);
198
+ padding: var(--spacing-2xs) 0;
199
+ }
200
+
201
+ .ui-select__scroll-indicator--up {
202
+ top: 0;
203
+ }
204
+
205
+ .ui-select__scroll-indicator--down {
206
+ bottom: 0;
207
+ }