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
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
} from './input-rules'
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
+
isRsFormItemBoundControl,
|
|
17
18
|
useRsFormContext,
|
|
18
19
|
useRsFormField,
|
|
20
|
+
useRsFormItemContext,
|
|
19
21
|
type RsFormLabelPosition,
|
|
20
22
|
} from './form-utils'
|
|
21
23
|
|
|
@@ -100,10 +102,31 @@ const props = withDefaults(
|
|
|
100
102
|
|
|
101
103
|
prefix?: string
|
|
102
104
|
|
|
103
|
-
/** 文本后缀(也可用 suffix
|
|
105
|
+
/** 文本后缀(也可用 suffix 插槽);框内,排在清除 / 显隐之后 */
|
|
104
106
|
|
|
105
107
|
suffix?: string
|
|
106
108
|
|
|
109
|
+
/**
|
|
110
|
+
* 框外前置 addon(对齐 Ant addonBefore)。
|
|
111
|
+
* 与 prefix 不同:连体在输入框外侧,用于单位、协议等。
|
|
112
|
+
*/
|
|
113
|
+
addonBefore?: string
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 框外后置 addon(对齐 Ant addonAfter)。
|
|
117
|
+
* 文本单位、协议等;也可配合 addonAfterIcon 做选择器按钮。
|
|
118
|
+
*/
|
|
119
|
+
addonAfter?: string
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 框外后置图标按钮(Lucide 名,如 ellipsis)。
|
|
123
|
+
* 由组件自绘,走连体 addonAfter;无 #addonAfter / addonAfter 文本时生效。
|
|
124
|
+
*/
|
|
125
|
+
addonAfterIcon?: string
|
|
126
|
+
|
|
127
|
+
/** addonAfterIcon 的无障碍文案 / title */
|
|
128
|
+
addonAfterIconLabel?: string
|
|
129
|
+
|
|
107
130
|
/** 有值时展示清除按钮(对齐 Ant allowClear / Element clearable) */
|
|
108
131
|
|
|
109
132
|
clearable?: boolean
|
|
@@ -183,6 +206,9 @@ const emit = defineEmits<{
|
|
|
183
206
|
|
|
184
207
|
clear: []
|
|
185
208
|
|
|
209
|
+
/** 点击 addonAfterIcon 时触发 */
|
|
210
|
+
addonAfterClick: [event: MouseEvent]
|
|
211
|
+
|
|
186
212
|
}>()
|
|
187
213
|
|
|
188
214
|
|
|
@@ -192,6 +218,10 @@ const attrs = useAttrs()
|
|
|
192
218
|
const slots = useSlots()
|
|
193
219
|
|
|
194
220
|
const formContext = useRsFormContext()
|
|
221
|
+
const formItem = useRsFormItemContext()
|
|
222
|
+
const boundToItem = computed(() =>
|
|
223
|
+
isRsFormItemBoundControl(formItem, { id: props.id, name: props.name }),
|
|
224
|
+
)
|
|
195
225
|
|
|
196
226
|
const fieldId = useId()
|
|
197
227
|
|
|
@@ -205,10 +235,26 @@ const autoMessage = ref('')
|
|
|
205
235
|
|
|
206
236
|
const passwordVisible = ref(false)
|
|
207
237
|
|
|
208
|
-
|
|
238
|
+
/** 中文等 IME 合成中:仍写入 model,但不触发校验 / pressEnter */
|
|
239
|
+
const composing = ref(false)
|
|
209
240
|
|
|
210
241
|
const hasPrefix = computed(() => Boolean(props.prefix || slots.prefix))
|
|
211
242
|
|
|
243
|
+
const hasCustomSuffix = computed(() => Boolean(props.suffix || slots.suffix))
|
|
244
|
+
|
|
245
|
+
const hasAddonBefore = computed(() => Boolean(props.addonBefore || slots.addonBefore))
|
|
246
|
+
|
|
247
|
+
/** 插槽或文本 addonAfter 内容(优先于图标按钮) */
|
|
248
|
+
const hasAddonAfterContent = computed(
|
|
249
|
+
() => Boolean(slots.addonAfter) || (props.addonAfter != null && props.addonAfter !== ''),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
const hasAddonAfter = computed(
|
|
253
|
+
() => hasAddonAfterContent.value || Boolean(props.addonAfterIcon),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
const hasAddon = computed(() => hasAddonBefore.value || hasAddonAfter.value)
|
|
257
|
+
|
|
212
258
|
|
|
213
259
|
|
|
214
260
|
const resolvedId = computed(() => props.id ?? fieldId)
|
|
@@ -232,6 +278,7 @@ const errorId = computed(() => `${resolvedId.value}-error`)
|
|
|
232
278
|
|
|
233
279
|
|
|
234
280
|
const displayMessage = computed(() => {
|
|
281
|
+
if (boundToItem.value) return ''
|
|
235
282
|
if (props.errorMessage) return props.errorMessage
|
|
236
283
|
if (props.showValidateMessage && autoInvalid.value) return autoMessage.value
|
|
237
284
|
return ''
|
|
@@ -249,9 +296,15 @@ const formErrorContent = computed(() => {
|
|
|
249
296
|
return formContext.renderError(errorSlotProps.value)
|
|
250
297
|
})
|
|
251
298
|
|
|
299
|
+
const isInvalid = computed(() => {
|
|
300
|
+
if (boundToItem.value) return Boolean(formItem?.invalid.value || props.invalid)
|
|
301
|
+
return props.invalid || autoInvalid.value
|
|
302
|
+
})
|
|
252
303
|
|
|
253
|
-
|
|
254
|
-
|
|
304
|
+
const describedBy = computed(() => {
|
|
305
|
+
if (boundToItem.value) return formItem?.describedBy.value
|
|
306
|
+
return displayMessage.value ? errorId.value : undefined
|
|
307
|
+
})
|
|
255
308
|
|
|
256
309
|
const resolvedDisabled = computed(() => props.disabled || formContext?.disabled.value || false)
|
|
257
310
|
|
|
@@ -299,7 +352,10 @@ const showCountDisplay = computed(() => props.showCount && props.maxlength != nu
|
|
|
299
352
|
|
|
300
353
|
const countText = computed(() => `${model.value.length} / ${props.maxlength}`)
|
|
301
354
|
|
|
302
|
-
|
|
355
|
+
/** 无 addonAfter 内容时用组件自绘的后置图标按钮 */
|
|
356
|
+
const showAddonAfterIcon = computed(
|
|
357
|
+
() => Boolean(props.addonAfterIcon) && !hasAddonAfterContent.value,
|
|
358
|
+
)
|
|
303
359
|
|
|
304
360
|
const hasSuffix = computed(
|
|
305
361
|
|
|
@@ -307,9 +363,7 @@ const hasSuffix = computed(
|
|
|
307
363
|
|
|
308
364
|
Boolean(
|
|
309
365
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
slots.suffix ||
|
|
366
|
+
hasCustomSuffix.value ||
|
|
313
367
|
|
|
314
368
|
showClearButton.value ||
|
|
315
369
|
|
|
@@ -321,6 +375,14 @@ const hasSuffix = computed(
|
|
|
321
375
|
|
|
322
376
|
)
|
|
323
377
|
|
|
378
|
+
/**
|
|
379
|
+
* 点击框外后置图标按钮
|
|
380
|
+
*/
|
|
381
|
+
function onAddonAfterIconClick(event: MouseEvent): void {
|
|
382
|
+
if (resolvedDisabled.value || resolvedReadonly.value) return
|
|
383
|
+
emit('addonAfterClick', event)
|
|
384
|
+
}
|
|
385
|
+
|
|
324
386
|
|
|
325
387
|
|
|
326
388
|
const fieldStyle = computed(() => {
|
|
@@ -369,6 +431,7 @@ function setError(message: string): void {
|
|
|
369
431
|
|
|
370
432
|
|
|
371
433
|
function onInput() {
|
|
434
|
+
if (composing.value) return
|
|
372
435
|
if (
|
|
373
436
|
touched.value &&
|
|
374
437
|
(props.validateTrigger === 'input' || props.validateTrigger === 'both')
|
|
@@ -384,6 +447,15 @@ function onNativeInput(event: Event) {
|
|
|
384
447
|
onInput()
|
|
385
448
|
}
|
|
386
449
|
|
|
450
|
+
function onCompositionStart() {
|
|
451
|
+
composing.value = true
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function onCompositionEnd(event: CompositionEvent) {
|
|
455
|
+
composing.value = false
|
|
456
|
+
onNativeInput(event)
|
|
457
|
+
}
|
|
458
|
+
|
|
387
459
|
|
|
388
460
|
|
|
389
461
|
function onBlur(event: FocusEvent) {
|
|
@@ -405,13 +477,9 @@ function onFocus(event: FocusEvent) {
|
|
|
405
477
|
|
|
406
478
|
|
|
407
479
|
function onKeydown(event: KeyboardEvent) {
|
|
408
|
-
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
emit('pressEnter', event)
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
480
|
+
if (event.key !== 'Enter') return
|
|
481
|
+
if (composing.value || event.isComposing) return
|
|
482
|
+
emit('pressEnter', event)
|
|
415
483
|
}
|
|
416
484
|
|
|
417
485
|
|
|
@@ -516,6 +584,40 @@ defineExpose({
|
|
|
516
584
|
|
|
517
585
|
<div class="rs-input-field">
|
|
518
586
|
|
|
587
|
+
<div
|
|
588
|
+
|
|
589
|
+
class="rs-input-shell"
|
|
590
|
+
|
|
591
|
+
:class="[
|
|
592
|
+
|
|
593
|
+
`rs-input-shell--${resolvedSize}`,
|
|
594
|
+
|
|
595
|
+
{
|
|
596
|
+
|
|
597
|
+
'rs-input-shell--combined': hasAddon,
|
|
598
|
+
|
|
599
|
+
'rs-input-shell--invalid': isInvalid,
|
|
600
|
+
|
|
601
|
+
'rs-input-shell--disabled': resolvedDisabled,
|
|
602
|
+
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
]"
|
|
606
|
+
|
|
607
|
+
>
|
|
608
|
+
|
|
609
|
+
<span
|
|
610
|
+
|
|
611
|
+
v-if="hasAddonBefore"
|
|
612
|
+
|
|
613
|
+
class="rs-input-addon rs-input-addon--before"
|
|
614
|
+
|
|
615
|
+
>
|
|
616
|
+
|
|
617
|
+
<slot name="addonBefore">{{ addonBefore }}</slot>
|
|
618
|
+
|
|
619
|
+
</span>
|
|
620
|
+
|
|
519
621
|
<div
|
|
520
622
|
|
|
521
623
|
class="rs-input-group"
|
|
@@ -534,6 +636,10 @@ defineExpose({
|
|
|
534
636
|
|
|
535
637
|
'rs-input-group--has-suffix': hasSuffix,
|
|
536
638
|
|
|
639
|
+
'rs-input-group--combined-before': hasAddonBefore,
|
|
640
|
+
|
|
641
|
+
'rs-input-group--combined-after': hasAddonAfter,
|
|
642
|
+
|
|
537
643
|
}"
|
|
538
644
|
|
|
539
645
|
>
|
|
@@ -576,10 +682,14 @@ defineExpose({
|
|
|
576
682
|
|
|
577
683
|
:aria-invalid="isInvalid || undefined"
|
|
578
684
|
|
|
579
|
-
:aria-describedby="
|
|
685
|
+
:aria-describedby="describedBy"
|
|
580
686
|
|
|
581
687
|
@input="onNativeInput"
|
|
582
688
|
|
|
689
|
+
@compositionstart="onCompositionStart"
|
|
690
|
+
|
|
691
|
+
@compositionend="onCompositionEnd"
|
|
692
|
+
|
|
583
693
|
@blur="onBlur"
|
|
584
694
|
|
|
585
695
|
@focus="onFocus"
|
|
@@ -596,8 +706,6 @@ defineExpose({
|
|
|
596
706
|
|
|
597
707
|
>
|
|
598
708
|
|
|
599
|
-
<slot name="suffix">{{ suffix }}</slot>
|
|
600
|
-
|
|
601
709
|
<span
|
|
602
710
|
|
|
603
711
|
v-if="showCountDisplay"
|
|
@@ -654,6 +762,54 @@ defineExpose({
|
|
|
654
762
|
|
|
655
763
|
</button>
|
|
656
764
|
|
|
765
|
+
<span v-if="hasCustomSuffix" class="rs-input-group__custom-suffix">
|
|
766
|
+
|
|
767
|
+
<slot name="suffix">{{ suffix }}</slot>
|
|
768
|
+
|
|
769
|
+
</span>
|
|
770
|
+
|
|
771
|
+
</span>
|
|
772
|
+
|
|
773
|
+
</div>
|
|
774
|
+
|
|
775
|
+
<span
|
|
776
|
+
|
|
777
|
+
v-if="hasAddonAfter"
|
|
778
|
+
|
|
779
|
+
class="rs-input-addon rs-input-addon--after"
|
|
780
|
+
|
|
781
|
+
:class="{ 'rs-input-addon--icon': showAddonAfterIcon }"
|
|
782
|
+
|
|
783
|
+
>
|
|
784
|
+
|
|
785
|
+
<template v-if="hasAddonAfterContent">
|
|
786
|
+
|
|
787
|
+
<slot name="addonAfter">{{ addonAfter }}</slot>
|
|
788
|
+
|
|
789
|
+
</template>
|
|
790
|
+
|
|
791
|
+
<button
|
|
792
|
+
|
|
793
|
+
v-else-if="showAddonAfterIcon"
|
|
794
|
+
|
|
795
|
+
type="button"
|
|
796
|
+
|
|
797
|
+
class="rs-input-addon__button"
|
|
798
|
+
|
|
799
|
+
:aria-label="addonAfterIconLabel || undefined"
|
|
800
|
+
|
|
801
|
+
:title="addonAfterIconLabel || undefined"
|
|
802
|
+
|
|
803
|
+
:disabled="resolvedDisabled || resolvedReadonly"
|
|
804
|
+
|
|
805
|
+
@click="onAddonAfterIconClick"
|
|
806
|
+
|
|
807
|
+
>
|
|
808
|
+
|
|
809
|
+
<RsIcon :name="addonAfterIcon!" :size="14" />
|
|
810
|
+
|
|
811
|
+
</button>
|
|
812
|
+
|
|
657
813
|
</span>
|
|
658
814
|
|
|
659
815
|
</div>
|
|
@@ -695,6 +851,243 @@ defineExpose({
|
|
|
695
851
|
|
|
696
852
|
}
|
|
697
853
|
|
|
854
|
+
.rs-input-shell {
|
|
855
|
+
|
|
856
|
+
width: 100%;
|
|
857
|
+
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/* 连体外壳统一外边框,避免 input/addon 各自圆角造成拼缝 */
|
|
861
|
+
.rs-input-shell--combined {
|
|
862
|
+
|
|
863
|
+
display: flex;
|
|
864
|
+
|
|
865
|
+
align-items: stretch;
|
|
866
|
+
|
|
867
|
+
box-sizing: border-box;
|
|
868
|
+
|
|
869
|
+
width: 100%;
|
|
870
|
+
|
|
871
|
+
border: 1px solid var(--rs-input-border, var(--rs-border));
|
|
872
|
+
|
|
873
|
+
border-radius: var(--rs-input-radius, var(--rs-radius-sm));
|
|
874
|
+
|
|
875
|
+
background: var(--rs-input-bg);
|
|
876
|
+
|
|
877
|
+
box-shadow: var(--rs-input-shadow, none);
|
|
878
|
+
|
|
879
|
+
overflow: hidden;
|
|
880
|
+
|
|
881
|
+
transition:
|
|
882
|
+
|
|
883
|
+
border-color var(--rs-transition-fast),
|
|
884
|
+
|
|
885
|
+
box-shadow var(--rs-transition-fast),
|
|
886
|
+
|
|
887
|
+
background var(--rs-transition-fast);
|
|
888
|
+
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
.rs-input-shell--combined:hover:not(:focus-within):not(:has(.rs-input-group--disabled)):not(
|
|
892
|
+
:has(.rs-input-group--readonly)
|
|
893
|
+
) {
|
|
894
|
+
|
|
895
|
+
border-color: var(--rs-input-border-hover, var(--rs-border));
|
|
896
|
+
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
.rs-input-shell--combined:focus-within {
|
|
900
|
+
|
|
901
|
+
border-color: var(--rs-focus-border, var(--rs-primary));
|
|
902
|
+
|
|
903
|
+
box-shadow:
|
|
904
|
+
|
|
905
|
+
var(--rs-input-shadow, none),
|
|
906
|
+
|
|
907
|
+
0 0 0 var(--rs-focus-ring-width, 2px) var(--rs-focus-ring);
|
|
908
|
+
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.rs-input-shell--combined:has(.rs-input-group--invalid) {
|
|
912
|
+
|
|
913
|
+
border-color: var(--rs-danger);
|
|
914
|
+
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.rs-input-shell--combined:has(.rs-input-group--invalid):focus-within {
|
|
918
|
+
|
|
919
|
+
border-color: var(--rs-danger);
|
|
920
|
+
|
|
921
|
+
box-shadow:
|
|
922
|
+
|
|
923
|
+
var(--rs-input-shadow, none),
|
|
924
|
+
|
|
925
|
+
0 0 0 var(--rs-focus-ring-width, 2px)
|
|
926
|
+
|
|
927
|
+
color-mix(in srgb, var(--rs-danger) 14%, transparent);
|
|
928
|
+
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.rs-input-shell--combined .rs-input-group {
|
|
932
|
+
|
|
933
|
+
flex: 1;
|
|
934
|
+
|
|
935
|
+
min-width: 0;
|
|
936
|
+
|
|
937
|
+
width: auto;
|
|
938
|
+
|
|
939
|
+
border: none;
|
|
940
|
+
|
|
941
|
+
border-radius: 0;
|
|
942
|
+
|
|
943
|
+
box-shadow: none;
|
|
944
|
+
|
|
945
|
+
background: transparent;
|
|
946
|
+
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.rs-input-shell--combined .rs-input-group:hover:not(.rs-input-group--disabled):not(
|
|
950
|
+
.rs-input-group--readonly
|
|
951
|
+
):not(:focus-within),
|
|
952
|
+
.rs-input-shell--combined .rs-input-group:focus-within,
|
|
953
|
+
.rs-input-shell--combined .rs-input-group--invalid,
|
|
954
|
+
.rs-input-shell--combined .rs-input-group--invalid:focus-within {
|
|
955
|
+
|
|
956
|
+
border-color: transparent;
|
|
957
|
+
|
|
958
|
+
box-shadow: none;
|
|
959
|
+
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.rs-input-addon {
|
|
963
|
+
|
|
964
|
+
display: inline-flex;
|
|
965
|
+
|
|
966
|
+
align-items: center;
|
|
967
|
+
|
|
968
|
+
justify-content: center;
|
|
969
|
+
|
|
970
|
+
box-sizing: border-box;
|
|
971
|
+
|
|
972
|
+
flex-shrink: 0;
|
|
973
|
+
|
|
974
|
+
min-width: var(--rs-control-height-md);
|
|
975
|
+
|
|
976
|
+
padding: 0 var(--rs-space-sm);
|
|
977
|
+
|
|
978
|
+
border: none;
|
|
979
|
+
|
|
980
|
+
border-left: 1px solid var(--rs-input-border, var(--rs-border));
|
|
981
|
+
|
|
982
|
+
border-radius: 0;
|
|
983
|
+
|
|
984
|
+
background: var(--rs-surface-hover);
|
|
985
|
+
|
|
986
|
+
color: var(--rs-muted);
|
|
987
|
+
|
|
988
|
+
font-size: var(--rs-font-size-sm);
|
|
989
|
+
|
|
990
|
+
white-space: nowrap;
|
|
991
|
+
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.rs-input-addon--before {
|
|
995
|
+
|
|
996
|
+
border-left: none;
|
|
997
|
+
|
|
998
|
+
border-right: 1px solid var(--rs-input-border, var(--rs-border));
|
|
999
|
+
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.rs-input-shell--ssm .rs-input-addon {
|
|
1003
|
+
|
|
1004
|
+
min-width: var(--rs-control-height-ssm);
|
|
1005
|
+
|
|
1006
|
+
padding: 0 var(--rs-space-xs);
|
|
1007
|
+
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.rs-input-shell--sm .rs-input-addon {
|
|
1011
|
+
|
|
1012
|
+
min-width: var(--rs-control-height-sm);
|
|
1013
|
+
|
|
1014
|
+
padding: 0 var(--rs-space-xs);
|
|
1015
|
+
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
.rs-input-shell--lg .rs-input-addon {
|
|
1019
|
+
|
|
1020
|
+
min-width: var(--rs-control-height-lg);
|
|
1021
|
+
|
|
1022
|
+
padding: 0 var(--rs-space-md);
|
|
1023
|
+
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
.rs-input-addon--icon {
|
|
1027
|
+
padding: 0;
|
|
1028
|
+
/* 图标选择器与输入区同底,整块一致,避免灰底 + 按钮再叠一层 hover */
|
|
1029
|
+
background: transparent;
|
|
1030
|
+
color: var(--rs-muted);
|
|
1031
|
+
transition: color var(--rs-transition-fast), background var(--rs-transition-fast);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
.rs-input-addon--icon:hover {
|
|
1035
|
+
color: var(--rs-primary);
|
|
1036
|
+
background: color-mix(in srgb, var(--rs-primary) 8%, transparent);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
.rs-input-addon__button {
|
|
1040
|
+
display: inline-flex;
|
|
1041
|
+
align-items: center;
|
|
1042
|
+
justify-content: center;
|
|
1043
|
+
box-sizing: border-box;
|
|
1044
|
+
width: 100%;
|
|
1045
|
+
height: 100%;
|
|
1046
|
+
min-width: inherit;
|
|
1047
|
+
margin: 0;
|
|
1048
|
+
padding: 0;
|
|
1049
|
+
border: none;
|
|
1050
|
+
background: transparent;
|
|
1051
|
+
color: inherit;
|
|
1052
|
+
cursor: pointer;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.rs-input-addon__button:hover:not(:disabled) {
|
|
1056
|
+
/* 悬浮样式交给整块 .rs-input-addon--icon,按钮不再单独变色/铺底 */
|
|
1057
|
+
color: inherit;
|
|
1058
|
+
background: transparent;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.rs-input-addon__button:focus-visible {
|
|
1062
|
+
outline: none;
|
|
1063
|
+
color: var(--rs-primary);
|
|
1064
|
+
box-shadow: inset 0 0 0 var(--rs-focus-ring-width, 2px) var(--rs-focus-ring);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
.rs-input-addon__button:disabled {
|
|
1068
|
+
cursor: not-allowed;
|
|
1069
|
+
opacity: 0.38;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.rs-input-shell--disabled .rs-input-addon,
|
|
1073
|
+
.rs-input-shell--combined:has(.rs-input-group--disabled) .rs-input-addon {
|
|
1074
|
+
|
|
1075
|
+
opacity: 0.38;
|
|
1076
|
+
|
|
1077
|
+
cursor: not-allowed;
|
|
1078
|
+
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
.rs-input-group__custom-suffix {
|
|
1082
|
+
|
|
1083
|
+
display: inline-flex;
|
|
1084
|
+
|
|
1085
|
+
align-items: center;
|
|
1086
|
+
|
|
1087
|
+
gap: var(--rs-space-xs);
|
|
1088
|
+
|
|
1089
|
+
}
|
|
1090
|
+
|
|
698
1091
|
.rs-field--label-left .rs-input-field {
|
|
699
1092
|
|
|
700
1093
|
grid-column: 2;
|
|
@@ -13,8 +13,10 @@ import { useRsI18n } from '../composables/useRsI18n'
|
|
|
13
13
|
import { useResolvedRsComponentSize } from './resolve-size'
|
|
14
14
|
import { rsRadiusCss, useResolvedRsRadius } from './resolve-radius'
|
|
15
15
|
import {
|
|
16
|
+
isRsFormItemBoundControl,
|
|
16
17
|
useRsFormContext,
|
|
17
18
|
useRsFormField,
|
|
19
|
+
useRsFormItemContext,
|
|
18
20
|
type RsFormLabelPosition,
|
|
19
21
|
} from './form-utils'
|
|
20
22
|
import {
|
|
@@ -101,6 +103,10 @@ const emit = defineEmits<{
|
|
|
101
103
|
}>()
|
|
102
104
|
|
|
103
105
|
const formContext = useRsFormContext()
|
|
106
|
+
const formItem = useRsFormItemContext()
|
|
107
|
+
const boundToItem = computed(() =>
|
|
108
|
+
isRsFormItemBoundControl(formItem, { id: props.id, name: props.name }),
|
|
109
|
+
)
|
|
104
110
|
const autoId = useId()
|
|
105
111
|
const resolvedId = computed(() => props.id || `rs-input-number-${autoId}`)
|
|
106
112
|
const resolvedSize = useResolvedRsComponentSize(() => props.size)
|
|
@@ -131,8 +137,11 @@ const fieldStyle = computed(() => {
|
|
|
131
137
|
})
|
|
132
138
|
|
|
133
139
|
const errorId = computed(() => `${resolvedId.value}-error`)
|
|
134
|
-
const isInvalid = computed(() =>
|
|
135
|
-
|
|
140
|
+
const isInvalid = computed(() => {
|
|
141
|
+
if (boundToItem.value) return Boolean(formItem?.invalid.value || props.invalid)
|
|
142
|
+
return Boolean(props.invalid || props.errorMessage)
|
|
143
|
+
})
|
|
144
|
+
const displayMessage = computed(() => (boundToItem.value ? '' : props.errorMessage || ''))
|
|
136
145
|
|
|
137
146
|
/** 输入框文本草稿(允许中间态) */
|
|
138
147
|
const draft = ref('')
|
|
@@ -366,7 +375,7 @@ defineExpose({
|
|
|
366
375
|
@focus="onFocus"
|
|
367
376
|
@blur="onBlur"
|
|
368
377
|
@keydown="onKeydown"
|
|
369
|
-
|
|
378
|
+
v-bind="changeOnWheel ? { onWheel } : {}"
|
|
370
379
|
/>
|
|
371
380
|
|
|
372
381
|
<div v-if="controls" class="rs-input-number__handlers" aria-hidden="true">
|
|
@@ -566,7 +575,7 @@ defineExpose({
|
|
|
566
575
|
|
|
567
576
|
.rs-input-number-field__error {
|
|
568
577
|
margin: 4px 0 0;
|
|
569
|
-
font-size:
|
|
578
|
+
font-size: var(--rs-font-size-xs);
|
|
570
579
|
color: var(--rs-danger);
|
|
571
580
|
line-height: 1.4;
|
|
572
581
|
}
|
|
@@ -62,7 +62,7 @@ function onClick(event: MouseEvent) {
|
|
|
62
62
|
.rs-link {
|
|
63
63
|
color: var(--rs-primary);
|
|
64
64
|
font-size: var(--rs-font-size-sm);
|
|
65
|
-
font-weight:
|
|
65
|
+
font-weight: var(--rs-font-weight-medium);
|
|
66
66
|
line-height: var(--rs-line-height-tight);
|
|
67
67
|
text-underline-offset: 0.18em;
|
|
68
68
|
text-decoration-thickness: 1px;
|
|
@@ -213,7 +213,7 @@ function setMode(next: RsMarkdownMode): void {
|
|
|
213
213
|
.rs-markdown__prose h3,
|
|
214
214
|
.rs-markdown__prose h4 {
|
|
215
215
|
margin: 1.25em 0 0.5em;
|
|
216
|
-
font-weight:
|
|
216
|
+
font-weight: var(--rs-font-weight-semibold);
|
|
217
217
|
line-height: 1.3;
|
|
218
218
|
letter-spacing: -0.01em;
|
|
219
219
|
color: var(--rs-text);
|
|
@@ -252,7 +252,7 @@ function setMode(next: RsMarkdownMode): void {
|
|
|
252
252
|
opacity: 0.85;
|
|
253
253
|
}
|
|
254
254
|
.rs-markdown__prose strong {
|
|
255
|
-
font-weight:
|
|
255
|
+
font-weight: var(--rs-font-weight-semibold);
|
|
256
256
|
}
|
|
257
257
|
.rs-markdown__prose blockquote {
|
|
258
258
|
padding: 0.15em 0 0.15em 0.9em;
|
|
@@ -268,7 +268,7 @@ function setMode(next: RsMarkdownMode): void {
|
|
|
268
268
|
padding: 0.1em 0.35em;
|
|
269
269
|
border-radius: calc(var(--rs-radius) * 0.6);
|
|
270
270
|
background: color-mix(in srgb, var(--rs-muted-bg, var(--rs-border)) 55%, transparent);
|
|
271
|
-
font-family: var(--rs-font-mono
|
|
271
|
+
font-family: var(--rs-font-mono);
|
|
272
272
|
font-size: 0.9em;
|
|
273
273
|
}
|
|
274
274
|
.rs-markdown__codeblock {
|
|
@@ -289,7 +289,7 @@ function setMode(next: RsMarkdownMode): void {
|
|
|
289
289
|
margin: 0;
|
|
290
290
|
padding: 0.75rem 0.9rem;
|
|
291
291
|
overflow: auto;
|
|
292
|
-
font-family: var(--rs-font-mono
|
|
292
|
+
font-family: var(--rs-font-mono);
|
|
293
293
|
font-size: 0.85em;
|
|
294
294
|
line-height: 1.55;
|
|
295
295
|
}
|
|
@@ -315,7 +315,7 @@ function setMode(next: RsMarkdownMode): void {
|
|
|
315
315
|
}
|
|
316
316
|
.rs-markdown__prose th {
|
|
317
317
|
background: color-mix(in srgb, var(--rs-muted-bg, var(--rs-border)) 40%, transparent);
|
|
318
|
-
font-weight:
|
|
318
|
+
font-weight: var(--rs-font-weight-semibold);
|
|
319
319
|
}
|
|
320
320
|
.rs-markdown__img {
|
|
321
321
|
max-width: 100%;
|
|
@@ -134,7 +134,7 @@ watch(
|
|
|
134
134
|
display: block;
|
|
135
135
|
padding: 0.5rem 0.5rem 0.25rem;
|
|
136
136
|
font-size: var(--rs-font-size-xs);
|
|
137
|
-
font-weight:
|
|
137
|
+
font-weight: var(--rs-font-weight-semibold);
|
|
138
138
|
color: var(--rs-muted);
|
|
139
139
|
letter-spacing: 0.04em;
|
|
140
140
|
line-height: var(--rs-line-height-tight);
|
|
@@ -215,7 +215,7 @@ watch(
|
|
|
215
215
|
margin-bottom: 0.125rem;
|
|
216
216
|
border-bottom: 1px solid var(--rs-border);
|
|
217
217
|
font-size: var(--rs-font-size-xs);
|
|
218
|
-
font-weight:
|
|
218
|
+
font-weight: var(--rs-font-weight-semibold);
|
|
219
219
|
color: var(--rs-muted);
|
|
220
220
|
letter-spacing: 0.04em;
|
|
221
221
|
line-height: var(--rs-line-height-tight);
|