niuma-ui 1.0.2 → 1.1.0
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/CHANGELOG.md +28 -1
- package/package.json +1 -1
- package/src/__tests__/RsDatePicker.spec.ts +21 -0
- package/src/__tests__/RsDialog.spec.ts +61 -0
- package/src/__tests__/RsForm.spec.ts +5 -5
- package/src/__tests__/RsFormItem.spec.ts +478 -0
- package/src/__tests__/RsFormList.spec.ts +109 -0
- package/src/__tests__/RsInput.spec.ts +93 -0
- package/src/__tests__/RsInputNumber.spec.ts +33 -0
- package/src/__tests__/RsSelect.spec.ts +423 -1
- package/src/__tests__/RsSwitch.spec.ts +30 -0
- package/src/__tests__/RsTimePicker.spec.ts +9 -0
- package/src/__tests__/RsTooltip.spec.ts +43 -0
- package/src/__tests__/css-token.spec.ts +51 -0
- package/src/__tests__/date-picker-utils.spec.ts +49 -0
- package/src/__tests__/dialog-utils.spec.ts +43 -0
- package/src/__tests__/form-path.spec.ts +46 -0
- package/src/__tests__/form-rules.spec.ts +63 -0
- package/src/__tests__/theme.spec.ts +2 -0
- package/src/components/RsAlert.vue +1 -1
- package/src/components/RsAvatar.vue +1 -1
- package/src/components/RsBadge.vue +1 -1
- package/src/components/RsBreadcrumb.vue +1 -1
- package/src/components/RsButton.vue +4 -4
- package/src/components/RsCalendarGrid.vue +3 -3
- package/src/components/RsCard.vue +4 -4
- package/src/components/RsCodeBlock.vue +4 -4
- package/src/components/RsCodeEditor.vue +8 -8
- package/src/components/RsConfirmDialog.vue +2 -2
- package/src/components/RsContextMenu.vue +6 -4
- package/src/components/RsDatePicker.vue +75 -31
- package/src/components/RsDateTimePicker.vue +9 -1
- package/src/components/RsDescriptions.vue +2 -2
- package/src/components/RsDialog.vue +26 -16
- package/src/components/RsDrawer.vue +1 -1
- package/src/components/RsDropdown.vue +1 -1
- package/src/components/RsDynamicTags.vue +18 -4
- package/src/components/RsEmpty.vue +1 -1
- package/src/components/RsForm.vue +68 -1
- package/src/components/RsFormItem.vue +423 -0
- package/src/components/RsFormItemControl.vue +21 -0
- package/src/components/RsFormList.vue +117 -0
- package/src/components/RsInput.vue +411 -18
- package/src/components/RsInputNumber.vue +13 -4
- package/src/components/RsLink.vue +1 -1
- package/src/components/RsMarkdown.vue +5 -5
- package/src/components/RsMenu.vue +2 -2
- package/src/components/RsMonacoEditor.vue +9 -3
- package/src/components/RsPagination.vue +1 -1
- package/src/components/RsProseEditor.vue +3 -3
- package/src/components/RsSelect.vue +332 -611
- package/src/components/RsSidebarGroup.vue +1 -1
- package/src/components/RsSplitPane.vue +22 -1
- package/src/components/RsStatCard.vue +2 -2
- package/src/components/RsSteps.vue +2 -2
- package/src/components/RsSwitch.vue +20 -7
- package/src/components/RsTabs.vue +9 -9
- package/src/components/RsTag.vue +1 -1
- package/src/components/RsTerminal.vue +58 -19
- package/src/components/RsTimePicker.vue +28 -4
- package/src/components/RsTimePickerColumns.vue +4 -4
- package/src/components/RsToaster.vue +7 -7
- package/src/components/RsTooltip.vue +10 -1
- package/src/components/RsTooltipProvider.vue +11 -1
- package/src/components/RsTree.vue +6 -6
- package/src/components/RsUpload.vue +2 -2
- package/src/components/date-picker-utils.ts +113 -2
- package/src/components/dialog-utils.ts +37 -2
- package/src/components/form-item-control.ts +55 -0
- package/src/components/form-path.ts +92 -0
- package/src/components/form-rules.ts +64 -10
- package/src/components/form-utils.ts +94 -2
- package/src/components/select-utils.ts +272 -12
- package/src/components/table/RsTableHeaderFilter.vue +1 -1
- package/src/components/table/rs-table.css +1 -1
- package/src/components/use-rs-select.ts +436 -0
- package/src/composables/useRsConfig.ts +10 -3
- package/src/composables/useRsI18n.ts +16 -5
- package/src/index.ts +69 -1
- package/src/locale/interpolate.ts +32 -0
- package/src/locale/messages.ts +10 -0
- package/src/styles.css +68 -12
- package/src/theme/brand.example.css +4 -1
- package/src/theme/css-token.ts +107 -0
- package/src/theme/types.ts +1 -0
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
import { computed, ref, watch } from 'vue'
|
|
2
|
+
import { computed, ref, useAttrs } from 'vue'
|
|
4
3
|
|
|
5
4
|
import { useRsI18n } from '../composables/useRsI18n'
|
|
6
5
|
import type { RsComponentSize, RsRadius } from '../theme/types'
|
|
7
6
|
import { RS_COMPONENT_SIZE_ICON_PX } from '../theme/types'
|
|
8
7
|
|
|
9
8
|
import RsIcon from './RsIcon.vue'
|
|
10
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
isRsFormItemBoundControl,
|
|
11
|
+
useRsFormContext,
|
|
12
|
+
useRsFormField,
|
|
13
|
+
useRsFormItemContext,
|
|
14
|
+
} from './form-utils'
|
|
11
15
|
import {
|
|
12
16
|
buildLocalInputRules,
|
|
13
17
|
runFormFieldRules,
|
|
@@ -15,371 +19,206 @@ import {
|
|
|
15
19
|
} from './form-rules'
|
|
16
20
|
import { useResolvedRsComponentSize } from './resolve-size'
|
|
17
21
|
import { rsRadiusCss, useResolvedRsRadius } from './resolve-radius'
|
|
18
|
-
|
|
19
22
|
import {
|
|
20
|
-
buildOptionDisabledMap,
|
|
21
|
-
buildOptionLabelMap,
|
|
22
|
-
filterSelectOptions,
|
|
23
|
-
flattenSelectOptions,
|
|
24
|
-
flattenSelectValues,
|
|
25
|
-
fromComboboxValue,
|
|
26
23
|
isSelectOptionGroup,
|
|
27
24
|
toComboboxValue,
|
|
28
|
-
type
|
|
25
|
+
type RsSelectFieldNames,
|
|
26
|
+
type RsSelectFilterOption,
|
|
27
|
+
type RsSelectFilterSort,
|
|
28
|
+
type RsSelectGetPopupContainer,
|
|
29
|
+
type RsSelectModelValue,
|
|
30
|
+
type RsSelectOption,
|
|
31
|
+
type RsSelectOptionFilterProp,
|
|
32
|
+
type RsSelectOptionsInput,
|
|
33
|
+
type RsSelectPlacement,
|
|
34
|
+
type RsSelectStatus,
|
|
35
|
+
type RsSelectValue,
|
|
29
36
|
} from './select-utils'
|
|
30
|
-
|
|
37
|
+
import { useRsSelect } from './use-rs-select'
|
|
31
38
|
import {
|
|
32
|
-
|
|
33
39
|
ComboboxAnchor,
|
|
34
|
-
|
|
35
40
|
ComboboxContent,
|
|
36
|
-
|
|
37
41
|
ComboboxEmpty,
|
|
38
|
-
|
|
39
42
|
ComboboxGroup,
|
|
40
|
-
|
|
41
43
|
ComboboxInput,
|
|
42
|
-
|
|
43
44
|
ComboboxItem,
|
|
44
|
-
|
|
45
45
|
ComboboxItemIndicator,
|
|
46
|
-
|
|
47
46
|
ComboboxLabel,
|
|
48
|
-
|
|
49
47
|
ComboboxPortal,
|
|
50
|
-
|
|
51
48
|
ComboboxRoot,
|
|
52
|
-
|
|
53
49
|
ComboboxTrigger,
|
|
54
|
-
|
|
55
50
|
ComboboxViewport,
|
|
56
|
-
|
|
57
51
|
ComboboxVirtualizer,
|
|
58
|
-
|
|
59
|
-
useFilter,
|
|
60
|
-
|
|
61
52
|
} from './reka'
|
|
62
53
|
|
|
54
|
+
defineOptions({ inheritAttrs: false })
|
|
63
55
|
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
const model = defineModel<RsSelectModelValue>({ default: '' })
|
|
57
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
58
|
+
const searchQuery = defineModel<string>('searchValue', { default: '' })
|
|
68
59
|
|
|
69
60
|
const props = withDefaults(
|
|
70
|
-
|
|
71
61
|
defineProps<{
|
|
72
|
-
|
|
73
|
-
options: RsSelectOptions
|
|
74
|
-
|
|
62
|
+
options: RsSelectOptionsInput
|
|
75
63
|
placeholder?: string
|
|
76
|
-
|
|
77
64
|
disabled?: boolean
|
|
78
|
-
|
|
79
65
|
id?: string
|
|
80
|
-
|
|
81
|
-
/** 下拉内搜索过滤 */
|
|
82
|
-
|
|
83
66
|
searchable?: boolean
|
|
84
|
-
|
|
85
67
|
/**
|
|
86
68
|
* 允许把搜索框内容作为自定义值提交(Enter 或点「使用 xxx」)。
|
|
87
|
-
* 专业工具常见:类型名 / 枚举等不在预设列表时仍可手输。
|
|
88
69
|
* 开启后若未显式关 searchable,将自动启用搜索框。
|
|
89
70
|
*/
|
|
90
71
|
creatable?: boolean
|
|
91
|
-
|
|
92
|
-
/** 多选,v-model 为 string[] */
|
|
93
|
-
|
|
94
72
|
multiple?: boolean
|
|
95
73
|
required?: boolean
|
|
96
|
-
/** 字段名:匹配 RsForm.rules[name] */
|
|
97
74
|
name?: string
|
|
98
|
-
|
|
99
|
-
/** 显示清空按钮 */
|
|
100
|
-
|
|
101
75
|
clearable?: boolean
|
|
102
|
-
|
|
103
|
-
/** 启用虚拟滚动(长列表) */
|
|
104
|
-
|
|
105
76
|
virtual?: boolean
|
|
106
|
-
|
|
107
|
-
/** 超过该数量自动启用虚拟滚动 */
|
|
108
|
-
|
|
109
77
|
virtualThreshold?: number
|
|
110
|
-
|
|
111
|
-
/** 远程搜索:关闭内置过滤,通过 @search 拉取 options */
|
|
112
|
-
|
|
113
78
|
remote?: boolean
|
|
114
|
-
|
|
115
|
-
/** 远程加载中 */
|
|
116
|
-
|
|
117
79
|
loading?: boolean
|
|
118
|
-
|
|
119
80
|
searchPlaceholder?: string
|
|
120
|
-
|
|
121
81
|
emptyText?: string
|
|
122
|
-
|
|
123
82
|
loadingText?: string
|
|
124
|
-
|
|
125
83
|
size?: RsComponentSize
|
|
126
|
-
|
|
127
|
-
/** 圆角档位;默认 sm。直角 UI 传 `none`。 */
|
|
128
|
-
|
|
129
84
|
radius?: RsRadius
|
|
130
|
-
|
|
131
85
|
/**
|
|
132
86
|
* 下拉面板是否与触发器等宽。
|
|
133
|
-
*
|
|
134
|
-
* - 默认 `false`:面板 `min-width` 对齐触发器,可随选项文案变宽(受 max-width 限制),避免长文案大量省略。
|
|
135
|
-
* - `true`:面板强制与触发器同宽,适合表单栅格、筛选条等需要对齐的场景;长文案仍会 ellipsis。
|
|
136
|
-
*
|
|
137
|
-
* 注意:开启虚拟滚动时,列表项多为绝对定位,无法可靠把面板撑到「全局最长文案」宽度,
|
|
138
|
-
* 宽度可能随可见项变化或退回到触发器宽度;长列表若强依赖内容撑宽,请关闭 virtual,
|
|
139
|
-
* 或改用 `matchTriggerWidth` + 截断项 Tooltip。
|
|
87
|
+
* 默认 false:面板可随文案变宽;true:与触发器同宽。
|
|
140
88
|
*/
|
|
141
89
|
matchTriggerWidth?: boolean
|
|
142
|
-
|
|
143
|
-
/** 根节点占满父级宽度(与按钮并排时用)。 */
|
|
144
90
|
block?: boolean
|
|
145
|
-
|
|
91
|
+
invalid?: boolean
|
|
92
|
+
showValidateMessage?: boolean
|
|
93
|
+
filterOption?: RsSelectFilterOption | boolean
|
|
94
|
+
optionFilterProp?: RsSelectOptionFilterProp
|
|
95
|
+
maxTagCount?: number
|
|
96
|
+
maxTagPlaceholder?: string | ((omitted: number) => string)
|
|
97
|
+
multipleLimit?: number
|
|
98
|
+
labelInValue?: boolean
|
|
99
|
+
filterSort?: RsSelectFilterSort
|
|
100
|
+
maxTagTextLength?: number
|
|
101
|
+
maxTagTooltip?: boolean
|
|
102
|
+
tokenSeparators?: string[]
|
|
103
|
+
autoClearSearchValue?: boolean
|
|
104
|
+
showArrow?: boolean
|
|
105
|
+
listHeight?: number
|
|
106
|
+
placement?: RsSelectPlacement
|
|
107
|
+
popupClassName?: string
|
|
108
|
+
status?: RsSelectStatus
|
|
109
|
+
/** 把业务字段映射为 label / value / options,对齐 Ant fieldNames */
|
|
110
|
+
fieldNames?: RsSelectFieldNames
|
|
111
|
+
/** 触发器展示用的选项字段,默认 label。对齐 Ant optionLabelProp */
|
|
112
|
+
optionLabelProp?: string
|
|
113
|
+
/** 下拉挂载容器,对齐 Ant getPopupContainer */
|
|
114
|
+
getPopupContainer?: RsSelectGetPopupContainer
|
|
146
115
|
}>(),
|
|
147
|
-
|
|
148
116
|
{
|
|
149
|
-
|
|
150
117
|
disabled: false,
|
|
151
|
-
|
|
152
118
|
searchable: false,
|
|
153
|
-
|
|
154
119
|
creatable: false,
|
|
155
|
-
|
|
156
120
|
multiple: false,
|
|
157
121
|
required: false,
|
|
158
|
-
|
|
159
122
|
clearable: false,
|
|
160
|
-
|
|
161
123
|
virtual: false,
|
|
162
|
-
|
|
163
124
|
virtualThreshold: 50,
|
|
164
|
-
|
|
165
125
|
remote: false,
|
|
166
|
-
|
|
167
126
|
loading: false,
|
|
168
|
-
|
|
169
127
|
matchTriggerWidth: false,
|
|
170
|
-
|
|
171
128
|
block: false,
|
|
172
|
-
|
|
129
|
+
optionFilterProp: 'label',
|
|
130
|
+
optionLabelProp: 'label',
|
|
131
|
+
labelInValue: false,
|
|
132
|
+
maxTagTooltip: true,
|
|
133
|
+
autoClearSearchValue: true,
|
|
134
|
+
showArrow: true,
|
|
135
|
+
listHeight: 256,
|
|
136
|
+
placement: 'bottom',
|
|
137
|
+
status: '',
|
|
173
138
|
},
|
|
174
|
-
|
|
175
139
|
)
|
|
176
140
|
|
|
177
|
-
|
|
178
|
-
|
|
179
141
|
const emit = defineEmits<{
|
|
180
|
-
|
|
181
142
|
search: [query: string]
|
|
182
|
-
|
|
143
|
+
select: [value: RsSelectValue, option?: RsSelectOption]
|
|
144
|
+
deselect: [value: RsSelectValue, option?: RsSelectOption]
|
|
145
|
+
clear: []
|
|
183
146
|
}>()
|
|
184
147
|
|
|
185
|
-
|
|
186
|
-
|
|
187
148
|
const { t } = useRsI18n()
|
|
149
|
+
const attrs = useAttrs()
|
|
188
150
|
const formContext = useRsFormContext()
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const searchQuery = ref('')
|
|
195
|
-
|
|
196
|
-
/** 下拉显隐;支持 v-model:open(如表格多选关闭时提交) */
|
|
197
|
-
const open = defineModel<boolean>('open', { default: false })
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const resolvedPlaceholder = computed(() => props.placeholder ?? t('select.placeholder'))
|
|
202
|
-
|
|
203
|
-
const resolvedSearchPlaceholder = computed(
|
|
204
|
-
|
|
205
|
-
() => props.searchPlaceholder ?? t('select.searchPlaceholder'),
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
const resolvedEmptyText = computed(() => props.emptyText ?? t('select.empty'))
|
|
210
|
-
|
|
211
|
-
const resolvedLoadingText = computed(() => props.loadingText ?? t('select.loading'))
|
|
212
|
-
|
|
213
|
-
/** creatable 默认带搜索框,便于手输 */
|
|
214
|
-
const isSearchable = computed(() => props.searchable || props.creatable)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const labelMap = computed(() => buildOptionLabelMap(props.options))
|
|
219
|
-
|
|
220
|
-
const disabledMap = computed(() => buildOptionDisabledMap(props.options))
|
|
221
|
-
|
|
222
|
-
const flatOptions = computed(() => flattenSelectOptions(props.options))
|
|
223
|
-
|
|
224
|
-
const flatCount = computed(() => flatOptions.value.length)
|
|
225
|
-
|
|
226
|
-
const useVirtual = computed(
|
|
227
|
-
() => props.virtual || flatCount.value > props.virtualThreshold,
|
|
151
|
+
const formItem = useRsFormItemContext()
|
|
152
|
+
const boundToItem = computed(() =>
|
|
153
|
+
isRsFormItemBoundControl(formItem, { id: props.id, name: props.name }),
|
|
228
154
|
)
|
|
155
|
+
const anchorRef = ref<{ $el?: HTMLElement } | HTMLElement | null>(null)
|
|
156
|
+
|
|
157
|
+
const {
|
|
158
|
+
resolvedPlaceholder,
|
|
159
|
+
resolvedSearchPlaceholder,
|
|
160
|
+
resolvedEmptyText,
|
|
161
|
+
resolvedLoadingText,
|
|
162
|
+
isSearchable,
|
|
163
|
+
labelMap,
|
|
164
|
+
useVirtual,
|
|
165
|
+
useManualFilter,
|
|
166
|
+
displayOptions,
|
|
167
|
+
createValue,
|
|
168
|
+
canCreate,
|
|
169
|
+
createOptionLabel,
|
|
170
|
+
virtualValues,
|
|
171
|
+
selectedValues,
|
|
172
|
+
hasValue,
|
|
173
|
+
visibleTagTokens,
|
|
174
|
+
omittedTagCount,
|
|
175
|
+
omittedTagLabel,
|
|
176
|
+
omittedTagTitle,
|
|
177
|
+
singleDisplayLabel,
|
|
178
|
+
comboboxModel,
|
|
179
|
+
tokenLabel,
|
|
180
|
+
truncateTagLabel,
|
|
181
|
+
optionFromToken,
|
|
182
|
+
optionOrCreate,
|
|
183
|
+
isOptionLimited,
|
|
184
|
+
isTokenDisabled,
|
|
185
|
+
restoreTokenValue,
|
|
186
|
+
onSearchKeydown,
|
|
187
|
+
onClear,
|
|
188
|
+
removeTag,
|
|
189
|
+
setValue,
|
|
190
|
+
resetSearch,
|
|
191
|
+
} = useRsSelect(props, model, open, searchQuery, emit, t)
|
|
229
192
|
|
|
230
|
-
const useManualFilter = computed(() => props.remote || useVirtual.value)
|
|
231
|
-
|
|
232
|
-
const displayOptions = computed(() => {
|
|
233
|
-
if (props.remote) return props.options
|
|
234
|
-
if (!useManualFilter.value) return props.options
|
|
235
|
-
return filterSelectOptions(props.options, searchQuery.value, contains)
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
const createValue = computed(() => searchQuery.value.trim())
|
|
239
|
-
|
|
240
|
-
const canCreate = computed(() => {
|
|
241
|
-
if (!props.creatable) return false
|
|
242
|
-
const q = createValue.value
|
|
243
|
-
if (!q) return false
|
|
244
|
-
const lower = q.toLowerCase()
|
|
245
|
-
return !flatOptions.value.some(
|
|
246
|
-
(opt) => opt.value.toLowerCase() === lower || String(opt.label).toLowerCase() === lower,
|
|
247
|
-
)
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
const createOptionLabel = computed(() =>
|
|
251
|
-
t('select.createOption', '使用 “{query}”').replace('{query}', createValue.value),
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
const virtualValues = computed(() => {
|
|
255
|
-
const values = flattenSelectValues(displayOptions.value)
|
|
256
|
-
if (!canCreate.value) return values
|
|
257
|
-
return [createValue.value, ...values.filter((v) => String(v) !== createValue.value)]
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
const selectedValues = computed<string[]>(() => {
|
|
261
|
-
if (props.multiple) {
|
|
262
|
-
const value = model.value
|
|
263
|
-
if (Array.isArray(value)) return value.map(String)
|
|
264
|
-
return value ? [String(value)] : []
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const value = model.value
|
|
268
|
-
// '' 表示未选中
|
|
269
|
-
return value !== '' && value !== undefined && value !== null ? [String(value)] : []
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
const hasValue = computed(() => selectedValues.value.length > 0)
|
|
273
193
|
const resolvedDisabled = computed(() => props.disabled || formContext?.disabled.value || false)
|
|
274
194
|
const resolvedSize = useResolvedRsComponentSize(() => props.size)
|
|
275
195
|
const resolvedRadius = useResolvedRsRadius(() => props.radius, 'sm')
|
|
276
196
|
const triggerIconSize = computed(() => RS_COMPONENT_SIZE_ICON_PX[resolvedSize.value])
|
|
277
197
|
const rootStyle = computed(() => ({
|
|
278
198
|
'--rs-select-radius': rsRadiusCss(resolvedRadius.value),
|
|
199
|
+
'--rs-select-list-height': `${props.listHeight}px`,
|
|
279
200
|
}))
|
|
280
201
|
|
|
281
|
-
const
|
|
282
|
-
if (props.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const comboboxModel = computed<string | string[] | undefined>({
|
|
287
|
-
get() {
|
|
288
|
-
if (props.multiple) return selectedValues.value.map(toComboboxValue)
|
|
289
|
-
return hasValue.value ? toComboboxValue(selectedValues.value[0]!) : undefined
|
|
290
|
-
},
|
|
291
|
-
set(value) {
|
|
292
|
-
if (props.multiple) {
|
|
293
|
-
model.value = Array.isArray(value)
|
|
294
|
-
? value.map((item) => fromComboboxValue(String(item)))
|
|
295
|
-
: []
|
|
296
|
-
return
|
|
297
|
-
}
|
|
298
|
-
// Reka 清空 / 空串哨兵 → 对外 ''
|
|
299
|
-
if (value == null || value === '') {
|
|
300
|
-
model.value = ''
|
|
301
|
-
return
|
|
302
|
-
}
|
|
303
|
-
model.value = fromComboboxValue(String(value))
|
|
304
|
-
},
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
watch(searchQuery, (query) => {
|
|
310
|
-
|
|
311
|
-
if (props.remote) emit('search', query)
|
|
312
|
-
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
watch(open, (isOpen) => {
|
|
318
|
-
if (isOpen) searchQuery.value = ''
|
|
202
|
+
const portalTo = computed(() => {
|
|
203
|
+
if (!props.getPopupContainer) return undefined
|
|
204
|
+
const raw = anchorRef.value
|
|
205
|
+
const el = raw instanceof HTMLElement ? raw : raw?.$el
|
|
206
|
+
return props.getPopupContainer(el instanceof HTMLElement ? el : undefined)
|
|
319
207
|
})
|
|
320
208
|
|
|
321
|
-
function commitCreatedValue(value: string): void {
|
|
322
|
-
const next = value.trim()
|
|
323
|
-
if (!next) return
|
|
324
|
-
if (props.multiple) {
|
|
325
|
-
const current = selectedValues.value
|
|
326
|
-
model.value = current.includes(next) ? current : [...current, next]
|
|
327
|
-
} else {
|
|
328
|
-
model.value = next
|
|
329
|
-
open.value = false
|
|
330
|
-
}
|
|
331
|
-
searchQuery.value = ''
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function onSearchKeydown(event: KeyboardEvent): void {
|
|
335
|
-
if (event.key !== 'Enter' || event.isComposing) return
|
|
336
|
-
if (!canCreate.value) return
|
|
337
|
-
// 无精确匹配时 Enter 提交手输值;有匹配时交给 Combobox 默认选中行为
|
|
338
|
-
event.preventDefault()
|
|
339
|
-
event.stopPropagation()
|
|
340
|
-
commitCreatedValue(createValue.value)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
function onClear(event: MouseEvent) {
|
|
346
|
-
event.preventDefault()
|
|
347
|
-
event.stopPropagation()
|
|
348
|
-
model.value = props.multiple ? [] : ''
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
function removeTag(value: string, event: MouseEvent) {
|
|
354
|
-
|
|
355
|
-
event.preventDefault()
|
|
356
|
-
|
|
357
|
-
event.stopPropagation()
|
|
358
|
-
|
|
359
|
-
if (!props.multiple) return
|
|
360
|
-
|
|
361
|
-
model.value = selectedValues.value.filter((item) => item !== value)
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function setValue(value: unknown): void {
|
|
366
|
-
if (props.multiple) {
|
|
367
|
-
if (Array.isArray(value)) {
|
|
368
|
-
model.value = value.map(String)
|
|
369
|
-
} else if (value) {
|
|
370
|
-
model.value = [String(value)]
|
|
371
|
-
} else {
|
|
372
|
-
model.value = []
|
|
373
|
-
}
|
|
374
|
-
return
|
|
375
|
-
}
|
|
376
|
-
model.value = value == null ? '' : String(value)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
209
|
const autoMessage = ref('')
|
|
210
|
+
const isInvalid = computed(() =>
|
|
211
|
+
Boolean(
|
|
212
|
+
props.invalid ||
|
|
213
|
+
props.status === 'error' ||
|
|
214
|
+
(boundToItem.value && formItem?.invalid.value) ||
|
|
215
|
+
autoMessage.value,
|
|
216
|
+
),
|
|
217
|
+
)
|
|
218
|
+
const isWarning = computed(() => props.status === 'warning' && !isInvalid.value)
|
|
380
219
|
|
|
381
220
|
function clearValidation(): void {
|
|
382
|
-
|
|
221
|
+
resetSearch()
|
|
383
222
|
autoMessage.value = ''
|
|
384
223
|
}
|
|
385
224
|
|
|
@@ -414,358 +253,298 @@ defineExpose({
|
|
|
414
253
|
clearValidation,
|
|
415
254
|
validate: runValidate,
|
|
416
255
|
})
|
|
417
|
-
|
|
418
256
|
</script>
|
|
419
257
|
|
|
420
|
-
|
|
421
|
-
|
|
422
258
|
<template>
|
|
423
|
-
|
|
424
259
|
<ComboboxRoot
|
|
425
|
-
|
|
426
260
|
v-model="comboboxModel"
|
|
427
|
-
|
|
428
261
|
v-model:open="open"
|
|
429
|
-
|
|
430
262
|
class="rs-select"
|
|
431
|
-
|
|
432
263
|
:class="{
|
|
433
|
-
|
|
434
264
|
'rs-select--multiple': multiple,
|
|
435
|
-
|
|
436
265
|
'rs-select--searchable': isSearchable,
|
|
437
266
|
'rs-select--creatable': creatable,
|
|
438
|
-
|
|
439
267
|
'rs-select--block': block,
|
|
440
|
-
|
|
441
268
|
[`rs-select--${resolvedSize}`]: true,
|
|
442
|
-
|
|
443
269
|
}"
|
|
444
|
-
|
|
445
270
|
:style="rootStyle"
|
|
446
|
-
|
|
447
271
|
:multiple="multiple"
|
|
448
|
-
|
|
449
272
|
:disabled="resolvedDisabled"
|
|
450
|
-
|
|
451
273
|
:ignore-filter="useManualFilter"
|
|
452
|
-
|
|
453
|
-
:reset-search-term-on-select="!multiple"
|
|
454
|
-
|
|
274
|
+
:reset-search-term-on-select="autoClearSearchValue"
|
|
455
275
|
open-on-click
|
|
456
|
-
|
|
457
276
|
>
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
277
|
+
<ComboboxAnchor ref="anchorRef" as-child>
|
|
278
|
+
<ComboboxTrigger
|
|
279
|
+
v-bind="attrs"
|
|
280
|
+
:id="id"
|
|
281
|
+
class="rs-select__trigger"
|
|
282
|
+
:class="{
|
|
283
|
+
'rs-select__trigger--invalid': isInvalid,
|
|
284
|
+
'rs-select__trigger--warning': isWarning,
|
|
285
|
+
}"
|
|
286
|
+
:aria-invalid="isInvalid || undefined"
|
|
287
|
+
>
|
|
288
|
+
<span v-if="$slots.prefix" class="rs-select__prefix">
|
|
289
|
+
<slot name="prefix" />
|
|
290
|
+
</span>
|
|
462
291
|
|
|
463
292
|
<span v-if="multiple" class="rs-select__value rs-select__value--multiple">
|
|
464
|
-
|
|
465
293
|
<template v-if="hasValue">
|
|
466
|
-
|
|
467
294
|
<span
|
|
468
|
-
|
|
469
|
-
v-for="value in selectedValues"
|
|
470
|
-
|
|
295
|
+
v-for="value in visibleTagTokens"
|
|
471
296
|
:key="value"
|
|
472
|
-
|
|
473
297
|
class="rs-select__tag"
|
|
474
|
-
|
|
475
298
|
>
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
type="button"
|
|
482
|
-
|
|
483
|
-
class="rs-select__tag-remove"
|
|
484
|
-
|
|
485
|
-
:aria-label="t('select.clear')"
|
|
486
|
-
|
|
487
|
-
@pointerdown.stop
|
|
488
|
-
|
|
489
|
-
@click="removeTag(value, $event)"
|
|
490
|
-
|
|
299
|
+
<slot
|
|
300
|
+
name="tag"
|
|
301
|
+
:value="restoreTokenValue(value)"
|
|
302
|
+
:label="tokenLabel(value)"
|
|
303
|
+
:closable="true"
|
|
491
304
|
>
|
|
492
|
-
|
|
493
|
-
<
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
305
|
+
<span class="rs-select__tag-label">{{ truncateTagLabel(tokenLabel(value)) }}</span>
|
|
306
|
+
<button
|
|
307
|
+
type="button"
|
|
308
|
+
class="rs-select__tag-remove"
|
|
309
|
+
:aria-label="t('select.clear')"
|
|
310
|
+
@pointerdown.stop
|
|
311
|
+
@click="removeTag(value, $event)"
|
|
312
|
+
>
|
|
313
|
+
<RsIcon name="x" :size="12" />
|
|
314
|
+
</button>
|
|
315
|
+
</slot>
|
|
316
|
+
</span>
|
|
317
|
+
<span
|
|
318
|
+
v-if="omittedTagCount > 0"
|
|
319
|
+
class="rs-select__tag rs-select__tag--rest"
|
|
320
|
+
:title="omittedTagTitle"
|
|
321
|
+
>
|
|
322
|
+
<slot name="maxTagPlaceholder" :omitted="omittedTagCount">
|
|
323
|
+
{{ omittedTagLabel }}
|
|
324
|
+
</slot>
|
|
497
325
|
</span>
|
|
498
|
-
|
|
499
326
|
</template>
|
|
500
|
-
|
|
501
327
|
<span v-else class="rs-select__placeholder">{{ resolvedPlaceholder }}</span>
|
|
502
|
-
|
|
503
328
|
</span>
|
|
504
329
|
|
|
505
330
|
<span v-else class="rs-select__value">
|
|
506
|
-
|
|
507
331
|
<span v-if="hasValue" class="rs-select__single-label">{{ singleDisplayLabel }}</span>
|
|
508
|
-
|
|
509
332
|
<span v-else class="rs-select__placeholder">{{ resolvedPlaceholder }}</span>
|
|
510
|
-
|
|
511
333
|
</span>
|
|
512
334
|
|
|
513
|
-
|
|
514
|
-
|
|
515
335
|
<span class="rs-select__actions">
|
|
516
|
-
|
|
517
336
|
<button
|
|
518
|
-
|
|
519
337
|
v-if="clearable && hasValue && !resolvedDisabled"
|
|
520
|
-
|
|
521
338
|
type="button"
|
|
522
|
-
|
|
523
339
|
class="rs-select__clear"
|
|
524
|
-
|
|
525
340
|
:aria-label="t('select.clear')"
|
|
526
|
-
|
|
527
341
|
@pointerdown.stop
|
|
528
|
-
|
|
529
342
|
@click="onClear"
|
|
530
|
-
|
|
531
343
|
>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
344
|
+
<slot name="clearIcon">
|
|
345
|
+
<RsIcon name="x" :size="14" />
|
|
346
|
+
</slot>
|
|
535
347
|
</button>
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
348
|
+
<span v-if="showArrow" class="rs-select__suffix">
|
|
349
|
+
<slot name="suffixIcon">
|
|
350
|
+
<RsIcon name="chevron-down" :size="triggerIconSize" class="rs-select__icon" />
|
|
351
|
+
</slot>
|
|
352
|
+
</span>
|
|
539
353
|
</span>
|
|
540
|
-
|
|
541
354
|
</ComboboxTrigger>
|
|
542
|
-
|
|
543
355
|
</ComboboxAnchor>
|
|
544
356
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
<ComboboxPortal>
|
|
548
|
-
|
|
357
|
+
<ComboboxPortal :to="portalTo">
|
|
549
358
|
<ComboboxContent
|
|
550
359
|
class="rs-select__content"
|
|
551
|
-
:class="
|
|
360
|
+
:class="[
|
|
361
|
+
popupClassName,
|
|
362
|
+
{ 'rs-select__content--match-trigger': matchTriggerWidth },
|
|
363
|
+
]"
|
|
364
|
+
:style="{ '--rs-select-list-height': `${listHeight}px` }"
|
|
552
365
|
align="start"
|
|
366
|
+
:side="placement"
|
|
553
367
|
:side-offset="4"
|
|
554
368
|
position="popper"
|
|
555
369
|
>
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
<RsIcon name="search" :size="14" class="rs-select__search-icon" aria-hidden="true" />
|
|
560
|
-
<ComboboxInput
|
|
561
|
-
v-model="searchQuery"
|
|
562
|
-
class="rs-select__search"
|
|
563
|
-
:placeholder="resolvedSearchPlaceholder"
|
|
564
|
-
auto-focus
|
|
565
|
-
@keydown="onSearchKeydown"
|
|
566
|
-
/>
|
|
370
|
+
<slot name="dropdownRender">
|
|
371
|
+
<div v-if="$slots.header" class="rs-select__panel-header" @mousedown.prevent>
|
|
372
|
+
<slot name="header" />
|
|
567
373
|
</div>
|
|
568
|
-
</div>
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
<div v-if="loading" class="rs-select__status">{{ resolvedLoadingText }}</div>
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
<ComboboxEmpty v-if="!loading && !canCreate" class="rs-select__empty">
|
|
577
|
-
|
|
578
|
-
{{ resolvedEmptyText }}
|
|
579
|
-
|
|
580
|
-
</ComboboxEmpty>
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
<ComboboxViewport class="rs-select__viewport">
|
|
585
|
-
|
|
586
|
-
<ComboboxVirtualizer
|
|
587
|
-
|
|
588
|
-
v-if="useVirtual"
|
|
589
|
-
|
|
590
|
-
v-slot="{ option }"
|
|
591
|
-
|
|
592
|
-
:options="virtualValues"
|
|
593
|
-
|
|
594
|
-
:text-content="(value) => labelMap.get(String(value)) ?? String(value)"
|
|
595
|
-
|
|
596
|
-
:estimate-size="36"
|
|
597
|
-
|
|
598
|
-
>
|
|
599
|
-
|
|
600
|
-
<ComboboxItem
|
|
601
|
-
:value="option"
|
|
602
|
-
:disabled="disabledMap.get(String(option))"
|
|
603
|
-
:text-value="labelMap.get(String(option)) ?? String(option)"
|
|
604
|
-
class="rs-select__item"
|
|
605
|
-
:class="{ 'rs-select__item--create': canCreate && String(option) === createValue }"
|
|
606
|
-
>
|
|
607
|
-
<span class="rs-select__item-label">
|
|
608
|
-
{{
|
|
609
|
-
canCreate && String(option) === createValue
|
|
610
|
-
? createOptionLabel
|
|
611
|
-
: (labelMap.get(String(option)) ?? fromComboboxValue(String(option)))
|
|
612
|
-
}}
|
|
613
|
-
</span>
|
|
614
|
-
<ComboboxItemIndicator class="rs-select__item-check">
|
|
615
|
-
<RsIcon name="check" :size="14" />
|
|
616
|
-
</ComboboxItemIndicator>
|
|
617
|
-
</ComboboxItem>
|
|
618
|
-
|
|
619
|
-
</ComboboxVirtualizer>
|
|
620
|
-
|
|
621
374
|
|
|
375
|
+
<div v-if="isSearchable" class="rs-select__search-bar">
|
|
376
|
+
<div class="rs-select__search-wrap">
|
|
377
|
+
<RsIcon name="search" :size="14" class="rs-select__search-icon" aria-hidden="true" />
|
|
378
|
+
<ComboboxInput
|
|
379
|
+
v-model="searchQuery"
|
|
380
|
+
class="rs-select__search"
|
|
381
|
+
:placeholder="resolvedSearchPlaceholder"
|
|
382
|
+
auto-focus
|
|
383
|
+
@keydown="onSearchKeydown"
|
|
384
|
+
/>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
622
387
|
|
|
623
|
-
<
|
|
388
|
+
<div v-if="loading" class="rs-select__status">
|
|
389
|
+
<slot name="loading">{{ resolvedLoadingText }}</slot>
|
|
390
|
+
</div>
|
|
624
391
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
392
|
+
<ComboboxEmpty v-if="!loading && !canCreate" class="rs-select__empty">
|
|
393
|
+
<slot name="empty">{{ resolvedEmptyText }}</slot>
|
|
394
|
+
</ComboboxEmpty>
|
|
395
|
+
|
|
396
|
+
<ComboboxViewport class="rs-select__viewport">
|
|
397
|
+
<ComboboxVirtualizer
|
|
398
|
+
v-if="useVirtual"
|
|
399
|
+
v-slot="{ option }"
|
|
400
|
+
:options="virtualValues"
|
|
401
|
+
:text-content="(value) => labelMap.get(String(value)) ?? String(value)"
|
|
402
|
+
:estimate-size="36"
|
|
630
403
|
>
|
|
631
|
-
<
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
404
|
+
<ComboboxItem
|
|
405
|
+
:value="option"
|
|
406
|
+
:disabled="isTokenDisabled(String(option))"
|
|
407
|
+
:text-value="labelMap.get(String(option)) ?? String(option)"
|
|
408
|
+
class="rs-select__item"
|
|
409
|
+
:class="{ 'rs-select__item--create': canCreate && String(option) === createValue }"
|
|
410
|
+
:title="optionFromToken(String(option))?.title"
|
|
411
|
+
>
|
|
412
|
+
<span class="rs-select__item-label">
|
|
413
|
+
<slot
|
|
414
|
+
name="option"
|
|
415
|
+
:option="optionOrCreate(String(option))"
|
|
416
|
+
:selected="selectedValues.includes(String(option))"
|
|
417
|
+
>
|
|
418
|
+
{{
|
|
419
|
+
canCreate && String(option) === createValue
|
|
420
|
+
? createOptionLabel
|
|
421
|
+
: (labelMap.get(String(option)) ?? String(option))
|
|
422
|
+
}}
|
|
423
|
+
</slot>
|
|
424
|
+
</span>
|
|
425
|
+
<ComboboxItemIndicator class="rs-select__item-check">
|
|
426
|
+
<RsIcon name="check" :size="14" />
|
|
427
|
+
</ComboboxItemIndicator>
|
|
428
|
+
</ComboboxItem>
|
|
429
|
+
</ComboboxVirtualizer>
|
|
642
430
|
|
|
643
|
-
|
|
431
|
+
<template v-else>
|
|
432
|
+
<ComboboxItem
|
|
433
|
+
v-if="canCreate"
|
|
434
|
+
:value="createValue"
|
|
435
|
+
:text-value="createValue"
|
|
436
|
+
class="rs-select__item rs-select__item--create"
|
|
437
|
+
>
|
|
438
|
+
<span class="rs-select__item-label">{{ createOptionLabel }}</span>
|
|
439
|
+
<ComboboxItemIndicator class="rs-select__item-check">
|
|
440
|
+
<RsIcon name="check" :size="14" />
|
|
441
|
+
</ComboboxItemIndicator>
|
|
442
|
+
</ComboboxItem>
|
|
644
443
|
|
|
645
|
-
|
|
444
|
+
<template v-for="(entry, index) in displayOptions" :key="index">
|
|
445
|
+
<ComboboxGroup v-if="isSelectOptionGroup(entry)" class="rs-select__group">
|
|
446
|
+
<ComboboxLabel class="rs-select__group-label">
|
|
447
|
+
{{ entry.label }}
|
|
448
|
+
</ComboboxLabel>
|
|
449
|
+
<ComboboxItem
|
|
450
|
+
v-for="opt in entry.options"
|
|
451
|
+
:key="toComboboxValue(opt.value)"
|
|
452
|
+
:value="toComboboxValue(opt.value)"
|
|
453
|
+
:disabled="opt.disabled || isOptionLimited(opt)"
|
|
454
|
+
:text-value="opt.label"
|
|
455
|
+
class="rs-select__item"
|
|
456
|
+
:title="opt.title"
|
|
457
|
+
>
|
|
458
|
+
<span class="rs-select__item-label">
|
|
459
|
+
<slot
|
|
460
|
+
name="option"
|
|
461
|
+
:option="opt"
|
|
462
|
+
:selected="selectedValues.includes(toComboboxValue(opt.value))"
|
|
463
|
+
>
|
|
464
|
+
{{ opt.label }}
|
|
465
|
+
</slot>
|
|
466
|
+
</span>
|
|
467
|
+
<ComboboxItemIndicator class="rs-select__item-check">
|
|
468
|
+
<RsIcon name="check" :size="14" />
|
|
469
|
+
</ComboboxItemIndicator>
|
|
470
|
+
</ComboboxItem>
|
|
471
|
+
</ComboboxGroup>
|
|
646
472
|
|
|
647
473
|
<ComboboxItem
|
|
648
|
-
v-
|
|
649
|
-
:key="toComboboxValue(
|
|
650
|
-
:value="toComboboxValue(
|
|
651
|
-
:disabled="
|
|
652
|
-
:text-value="
|
|
474
|
+
v-else
|
|
475
|
+
:key="toComboboxValue(entry.value)"
|
|
476
|
+
:value="toComboboxValue(entry.value)"
|
|
477
|
+
:disabled="entry.disabled || isOptionLimited(entry)"
|
|
478
|
+
:text-value="entry.label"
|
|
653
479
|
class="rs-select__item"
|
|
480
|
+
:title="entry.title"
|
|
654
481
|
>
|
|
655
|
-
<span class="rs-select__item-label">
|
|
482
|
+
<span class="rs-select__item-label">
|
|
483
|
+
<slot
|
|
484
|
+
name="option"
|
|
485
|
+
:option="entry"
|
|
486
|
+
:selected="selectedValues.includes(toComboboxValue(entry.value))"
|
|
487
|
+
>
|
|
488
|
+
{{ entry.label }}
|
|
489
|
+
</slot>
|
|
490
|
+
</span>
|
|
656
491
|
<ComboboxItemIndicator class="rs-select__item-check">
|
|
657
492
|
<RsIcon name="check" :size="14" />
|
|
658
493
|
</ComboboxItemIndicator>
|
|
659
494
|
</ComboboxItem>
|
|
660
|
-
|
|
661
|
-
</ComboboxGroup>
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
<ComboboxItem
|
|
666
|
-
v-else
|
|
667
|
-
:key="toComboboxValue(entry.value)"
|
|
668
|
-
:value="toComboboxValue(entry.value)"
|
|
669
|
-
:disabled="entry.disabled"
|
|
670
|
-
:text-value="entry.label"
|
|
671
|
-
class="rs-select__item"
|
|
672
|
-
>
|
|
673
|
-
<span class="rs-select__item-label">{{ entry.label }}</span>
|
|
674
|
-
<ComboboxItemIndicator class="rs-select__item-check">
|
|
675
|
-
<RsIcon name="check" :size="14" />
|
|
676
|
-
</ComboboxItemIndicator>
|
|
677
|
-
</ComboboxItem>
|
|
678
|
-
|
|
495
|
+
</template>
|
|
679
496
|
</template>
|
|
497
|
+
</ComboboxViewport>
|
|
680
498
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
499
|
+
<div v-if="$slots.footer" class="rs-select__panel-footer" @mousedown.prevent>
|
|
500
|
+
<slot name="footer" />
|
|
501
|
+
</div>
|
|
502
|
+
</slot>
|
|
685
503
|
</ComboboxContent>
|
|
686
|
-
|
|
687
504
|
</ComboboxPortal>
|
|
688
|
-
|
|
689
505
|
</ComboboxRoot>
|
|
690
|
-
|
|
691
506
|
</template>
|
|
692
507
|
|
|
693
|
-
|
|
694
|
-
|
|
695
508
|
<style scoped>
|
|
696
|
-
|
|
697
509
|
.rs-select {
|
|
698
|
-
|
|
699
510
|
display: inline-flex;
|
|
700
|
-
|
|
701
511
|
width: 100%;
|
|
702
|
-
|
|
703
512
|
max-width: 100%;
|
|
704
|
-
|
|
705
513
|
vertical-align: middle;
|
|
706
|
-
|
|
707
514
|
box-sizing: border-box;
|
|
708
|
-
|
|
709
515
|
}
|
|
710
516
|
|
|
711
517
|
.rs-select__trigger {
|
|
712
|
-
|
|
713
518
|
display: inline-flex;
|
|
714
|
-
|
|
715
519
|
align-items: center;
|
|
716
|
-
|
|
717
520
|
justify-content: space-between;
|
|
718
|
-
|
|
719
521
|
gap: var(--rs-space-sm);
|
|
720
|
-
|
|
721
522
|
box-sizing: border-box;
|
|
722
|
-
|
|
723
523
|
width: 100%;
|
|
724
|
-
|
|
725
524
|
height: var(--rs-control-height-md);
|
|
726
|
-
|
|
727
525
|
min-height: var(--rs-control-height-md);
|
|
728
|
-
|
|
729
526
|
padding: 0 var(--rs-space-md);
|
|
730
|
-
|
|
731
527
|
border-radius: var(--rs-select-radius, var(--rs-radius-sm));
|
|
732
|
-
|
|
733
528
|
border: 1px solid var(--rs-input-border, var(--rs-border));
|
|
734
|
-
|
|
735
529
|
background: var(--rs-input-bg);
|
|
736
|
-
|
|
737
530
|
color: var(--rs-text);
|
|
738
|
-
|
|
739
531
|
font-size: var(--rs-font-size-sm);
|
|
740
|
-
|
|
741
532
|
line-height: var(--rs-line-height-tight);
|
|
742
|
-
|
|
743
533
|
text-align: left;
|
|
744
|
-
|
|
745
534
|
cursor: pointer;
|
|
746
|
-
|
|
535
|
+
outline: none;
|
|
747
536
|
box-shadow: var(--rs-input-shadow, none);
|
|
748
|
-
|
|
749
537
|
transition:
|
|
750
|
-
|
|
751
538
|
border-color var(--rs-transition-fast),
|
|
752
|
-
|
|
753
539
|
box-shadow var(--rs-transition-fast),
|
|
754
|
-
|
|
755
540
|
background var(--rs-transition-fast);
|
|
756
|
-
|
|
757
541
|
}
|
|
758
542
|
|
|
759
543
|
.rs-select--multiple .rs-select__trigger {
|
|
760
|
-
|
|
761
544
|
min-height: var(--rs-control-height-md);
|
|
762
|
-
|
|
763
545
|
height: auto;
|
|
764
|
-
|
|
765
546
|
padding-top: var(--rs-space-xs);
|
|
766
|
-
|
|
767
547
|
padding-bottom: var(--rs-space-xs);
|
|
768
|
-
|
|
769
548
|
}
|
|
770
549
|
|
|
771
550
|
.rs-select--block {
|
|
@@ -817,238 +596,180 @@ defineExpose({
|
|
|
817
596
|
}
|
|
818
597
|
|
|
819
598
|
.rs-select__trigger:hover:not([data-disabled]) {
|
|
820
|
-
|
|
821
599
|
border-color: var(--rs-input-border-hover, var(--rs-border));
|
|
822
|
-
|
|
823
600
|
}
|
|
824
601
|
|
|
825
602
|
.rs-select__trigger:focus-visible {
|
|
826
|
-
|
|
827
603
|
outline: none;
|
|
828
|
-
|
|
829
604
|
border-color: var(--rs-focus-border, var(--rs-primary));
|
|
830
|
-
|
|
831
605
|
background: var(--rs-input-bg);
|
|
832
|
-
|
|
833
606
|
box-shadow:
|
|
834
|
-
|
|
835
607
|
var(--rs-input-shadow, none),
|
|
836
|
-
|
|
837
608
|
0 0 0 var(--rs-focus-ring-width, 2px) var(--rs-focus-ring);
|
|
838
|
-
|
|
839
609
|
}
|
|
840
610
|
|
|
841
611
|
.rs-select__trigger[data-disabled] {
|
|
842
|
-
|
|
843
612
|
opacity: 0.38;
|
|
844
|
-
|
|
845
613
|
cursor: not-allowed;
|
|
846
|
-
|
|
847
614
|
background: var(--rs-surface-hover);
|
|
615
|
+
}
|
|
848
616
|
|
|
617
|
+
.rs-select__trigger--invalid {
|
|
618
|
+
border-color: var(--rs-danger);
|
|
849
619
|
}
|
|
850
620
|
|
|
851
|
-
.rs-
|
|
621
|
+
.rs-select__trigger--invalid:focus-visible {
|
|
622
|
+
border-color: var(--rs-danger);
|
|
623
|
+
box-shadow:
|
|
624
|
+
var(--rs-input-shadow, none),
|
|
625
|
+
0 0 0 var(--rs-focus-ring-width, 2px)
|
|
626
|
+
color-mix(in srgb, var(--rs-danger) 14%, transparent);
|
|
627
|
+
}
|
|
852
628
|
|
|
853
|
-
|
|
629
|
+
.rs-select__trigger--warning {
|
|
630
|
+
border-color: var(--rs-warning, #d97706);
|
|
631
|
+
}
|
|
854
632
|
|
|
855
|
-
|
|
633
|
+
.rs-select__trigger--warning:focus-visible {
|
|
634
|
+
border-color: var(--rs-warning, #d97706);
|
|
635
|
+
box-shadow:
|
|
636
|
+
var(--rs-input-shadow, none),
|
|
637
|
+
0 0 0 var(--rs-focus-ring-width, 2px)
|
|
638
|
+
color-mix(in srgb, var(--rs-warning, #d97706) 14%, transparent);
|
|
639
|
+
}
|
|
856
640
|
|
|
641
|
+
.rs-select__value {
|
|
642
|
+
flex: 1;
|
|
643
|
+
min-width: 0;
|
|
857
644
|
text-align: left;
|
|
858
|
-
|
|
859
645
|
overflow: hidden;
|
|
860
|
-
|
|
861
646
|
text-overflow: ellipsis;
|
|
862
|
-
|
|
863
647
|
white-space: nowrap;
|
|
864
|
-
|
|
865
648
|
}
|
|
866
649
|
|
|
867
650
|
.rs-select__value--multiple {
|
|
868
|
-
|
|
869
651
|
display: flex;
|
|
870
|
-
|
|
871
652
|
flex-wrap: nowrap;
|
|
872
|
-
|
|
873
653
|
gap: var(--rs-space-xs);
|
|
874
|
-
|
|
875
654
|
align-items: center;
|
|
876
|
-
|
|
877
655
|
white-space: nowrap;
|
|
878
|
-
|
|
879
656
|
overflow: hidden;
|
|
657
|
+
}
|
|
880
658
|
|
|
659
|
+
.rs-select__prefix {
|
|
660
|
+
display: inline-flex;
|
|
661
|
+
flex-shrink: 0;
|
|
662
|
+
align-items: center;
|
|
663
|
+
color: var(--rs-muted);
|
|
881
664
|
}
|
|
882
665
|
|
|
883
|
-
.rs-
|
|
666
|
+
.rs-select__tag--rest {
|
|
667
|
+
padding-right: 0.5rem;
|
|
668
|
+
color: var(--rs-muted);
|
|
669
|
+
background: var(--rs-surface-hover);
|
|
670
|
+
border-color: var(--rs-border);
|
|
671
|
+
}
|
|
884
672
|
|
|
673
|
+
.rs-select__placeholder {
|
|
885
674
|
color: var(--rs-placeholder);
|
|
886
|
-
|
|
887
675
|
}
|
|
888
676
|
|
|
889
677
|
.rs-select__single-label {
|
|
890
|
-
|
|
891
678
|
display: block;
|
|
892
|
-
|
|
893
679
|
overflow: hidden;
|
|
894
|
-
|
|
895
680
|
text-overflow: ellipsis;
|
|
896
|
-
|
|
897
681
|
white-space: nowrap;
|
|
898
|
-
|
|
899
682
|
}
|
|
900
683
|
|
|
901
684
|
.rs-select__tag {
|
|
902
|
-
|
|
903
685
|
display: inline-flex;
|
|
904
|
-
|
|
905
686
|
align-items: center;
|
|
906
|
-
|
|
907
687
|
gap: 0.125rem;
|
|
908
|
-
|
|
909
688
|
flex: 0 0 auto;
|
|
910
|
-
|
|
911
689
|
max-width: 10rem;
|
|
912
|
-
|
|
913
690
|
padding: 0.125rem 0.25rem 0.125rem 0.5rem;
|
|
914
|
-
|
|
915
691
|
border-radius: var(--rs-radius-xs);
|
|
916
|
-
|
|
917
692
|
border: 1px solid color-mix(in srgb, var(--rs-primary) 24%, transparent);
|
|
918
|
-
|
|
919
693
|
background: var(--rs-primary-container);
|
|
920
|
-
|
|
921
694
|
color: var(--rs-on-primary-container);
|
|
922
|
-
|
|
923
695
|
font-size: var(--rs-font-size-xs);
|
|
924
|
-
|
|
925
696
|
line-height: var(--rs-line-height-tight);
|
|
926
|
-
|
|
927
697
|
}
|
|
928
698
|
|
|
929
699
|
.rs-select__tag-label {
|
|
930
|
-
|
|
931
700
|
overflow: hidden;
|
|
932
|
-
|
|
933
701
|
text-overflow: ellipsis;
|
|
934
|
-
|
|
935
702
|
white-space: nowrap;
|
|
936
|
-
|
|
937
703
|
}
|
|
938
704
|
|
|
939
705
|
.rs-select__tag-remove {
|
|
940
|
-
|
|
941
706
|
display: inline-flex;
|
|
942
|
-
|
|
943
707
|
align-items: center;
|
|
944
|
-
|
|
945
708
|
justify-content: center;
|
|
946
|
-
|
|
947
709
|
width: 1rem;
|
|
948
|
-
|
|
949
710
|
height: 1rem;
|
|
950
|
-
|
|
951
711
|
padding: 0;
|
|
952
|
-
|
|
953
712
|
border: none;
|
|
954
|
-
|
|
955
713
|
border-radius: var(--rs-radius-xs);
|
|
956
|
-
|
|
957
714
|
background: transparent;
|
|
958
|
-
|
|
959
715
|
color: var(--rs-muted);
|
|
960
|
-
|
|
961
716
|
cursor: pointer;
|
|
962
|
-
|
|
963
717
|
transition:
|
|
964
|
-
|
|
965
718
|
color var(--rs-transition-fast),
|
|
966
|
-
|
|
967
719
|
background var(--rs-transition-fast);
|
|
968
|
-
|
|
969
720
|
}
|
|
970
721
|
|
|
971
722
|
.rs-select__tag-remove:hover {
|
|
972
|
-
|
|
973
723
|
color: var(--rs-on-primary-container);
|
|
974
|
-
|
|
975
724
|
background: color-mix(in srgb, var(--rs-primary) 16%, transparent);
|
|
976
|
-
|
|
977
725
|
}
|
|
978
726
|
|
|
979
727
|
.rs-select__actions {
|
|
980
|
-
|
|
981
728
|
display: inline-flex;
|
|
982
|
-
|
|
983
729
|
align-items: center;
|
|
984
|
-
|
|
985
730
|
gap: var(--rs-space-xs);
|
|
986
|
-
|
|
987
731
|
flex-shrink: 0;
|
|
988
|
-
|
|
989
732
|
}
|
|
990
733
|
|
|
991
734
|
.rs-select__clear {
|
|
992
|
-
|
|
993
735
|
display: inline-flex;
|
|
994
|
-
|
|
995
736
|
align-items: center;
|
|
996
|
-
|
|
997
737
|
justify-content: center;
|
|
998
|
-
|
|
999
738
|
width: 1.25rem;
|
|
1000
|
-
|
|
1001
739
|
height: 1.25rem;
|
|
1002
|
-
|
|
1003
740
|
padding: 0;
|
|
1004
|
-
|
|
1005
741
|
border: none;
|
|
1006
|
-
|
|
1007
742
|
border-radius: var(--rs-radius-xs);
|
|
1008
|
-
|
|
1009
743
|
background: transparent;
|
|
1010
|
-
|
|
1011
744
|
color: var(--rs-muted);
|
|
1012
|
-
|
|
1013
745
|
cursor: pointer;
|
|
1014
|
-
|
|
1015
746
|
transition:
|
|
1016
|
-
|
|
1017
747
|
color var(--rs-transition-fast),
|
|
1018
|
-
|
|
1019
748
|
background var(--rs-transition-fast);
|
|
1020
|
-
|
|
1021
749
|
}
|
|
1022
750
|
|
|
1023
751
|
.rs-select__clear:hover {
|
|
1024
|
-
|
|
1025
752
|
color: var(--rs-text);
|
|
1026
|
-
|
|
1027
753
|
background: var(--rs-item-hover);
|
|
754
|
+
}
|
|
1028
755
|
|
|
756
|
+
.rs-select__suffix {
|
|
757
|
+
display: inline-flex;
|
|
758
|
+
align-items: center;
|
|
1029
759
|
}
|
|
1030
760
|
|
|
1031
761
|
.rs-select__icon {
|
|
1032
|
-
|
|
1033
762
|
color: var(--rs-muted);
|
|
1034
|
-
|
|
1035
763
|
flex-shrink: 0;
|
|
1036
|
-
|
|
1037
764
|
transition: transform var(--rs-transition-fast);
|
|
1038
|
-
|
|
1039
765
|
}
|
|
1040
766
|
|
|
1041
767
|
.rs-select__trigger[data-state='open'] .rs-select__icon {
|
|
1042
|
-
|
|
1043
768
|
transform: rotate(180deg);
|
|
1044
|
-
|
|
1045
769
|
}
|
|
1046
770
|
|
|
1047
771
|
.rs-select__item--create .rs-select__item-label {
|
|
1048
772
|
color: var(--rs-primary);
|
|
1049
|
-
font-weight:
|
|
773
|
+
font-weight: var(--rs-font-weight-medium);
|
|
1050
774
|
}
|
|
1051
|
-
|
|
1052
775
|
</style>
|
|
1053
|
-
|
|
1054
|
-
|