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.
Files changed (85) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/package.json +1 -1
  3. package/src/__tests__/RsDatePicker.spec.ts +21 -0
  4. package/src/__tests__/RsDialog.spec.ts +61 -0
  5. package/src/__tests__/RsForm.spec.ts +5 -5
  6. package/src/__tests__/RsFormItem.spec.ts +478 -0
  7. package/src/__tests__/RsFormList.spec.ts +109 -0
  8. package/src/__tests__/RsInput.spec.ts +93 -0
  9. package/src/__tests__/RsInputNumber.spec.ts +33 -0
  10. package/src/__tests__/RsSelect.spec.ts +423 -1
  11. package/src/__tests__/RsSwitch.spec.ts +30 -0
  12. package/src/__tests__/RsTimePicker.spec.ts +9 -0
  13. package/src/__tests__/RsTooltip.spec.ts +43 -0
  14. package/src/__tests__/css-token.spec.ts +51 -0
  15. package/src/__tests__/date-picker-utils.spec.ts +49 -0
  16. package/src/__tests__/dialog-utils.spec.ts +43 -0
  17. package/src/__tests__/form-path.spec.ts +46 -0
  18. package/src/__tests__/form-rules.spec.ts +63 -0
  19. package/src/__tests__/theme.spec.ts +2 -0
  20. package/src/components/RsAlert.vue +1 -1
  21. package/src/components/RsAvatar.vue +1 -1
  22. package/src/components/RsBadge.vue +1 -1
  23. package/src/components/RsBreadcrumb.vue +1 -1
  24. package/src/components/RsButton.vue +4 -4
  25. package/src/components/RsCalendarGrid.vue +3 -3
  26. package/src/components/RsCard.vue +4 -4
  27. package/src/components/RsCodeBlock.vue +4 -4
  28. package/src/components/RsCodeEditor.vue +8 -8
  29. package/src/components/RsConfirmDialog.vue +2 -2
  30. package/src/components/RsContextMenu.vue +6 -4
  31. package/src/components/RsDatePicker.vue +75 -31
  32. package/src/components/RsDateTimePicker.vue +9 -1
  33. package/src/components/RsDescriptions.vue +2 -2
  34. package/src/components/RsDialog.vue +26 -16
  35. package/src/components/RsDrawer.vue +1 -1
  36. package/src/components/RsDropdown.vue +1 -1
  37. package/src/components/RsDynamicTags.vue +18 -4
  38. package/src/components/RsEmpty.vue +1 -1
  39. package/src/components/RsForm.vue +68 -1
  40. package/src/components/RsFormItem.vue +423 -0
  41. package/src/components/RsFormItemControl.vue +21 -0
  42. package/src/components/RsFormList.vue +117 -0
  43. package/src/components/RsInput.vue +411 -18
  44. package/src/components/RsInputNumber.vue +13 -4
  45. package/src/components/RsLink.vue +1 -1
  46. package/src/components/RsMarkdown.vue +5 -5
  47. package/src/components/RsMenu.vue +2 -2
  48. package/src/components/RsMonacoEditor.vue +9 -3
  49. package/src/components/RsPagination.vue +1 -1
  50. package/src/components/RsProseEditor.vue +3 -3
  51. package/src/components/RsSelect.vue +332 -611
  52. package/src/components/RsSidebarGroup.vue +1 -1
  53. package/src/components/RsSplitPane.vue +22 -1
  54. package/src/components/RsStatCard.vue +2 -2
  55. package/src/components/RsSteps.vue +2 -2
  56. package/src/components/RsSwitch.vue +20 -7
  57. package/src/components/RsTabs.vue +9 -9
  58. package/src/components/RsTag.vue +1 -1
  59. package/src/components/RsTerminal.vue +58 -19
  60. package/src/components/RsTimePicker.vue +28 -4
  61. package/src/components/RsTimePickerColumns.vue +4 -4
  62. package/src/components/RsToaster.vue +7 -7
  63. package/src/components/RsTooltip.vue +10 -1
  64. package/src/components/RsTooltipProvider.vue +11 -1
  65. package/src/components/RsTree.vue +6 -6
  66. package/src/components/RsUpload.vue +2 -2
  67. package/src/components/date-picker-utils.ts +113 -2
  68. package/src/components/dialog-utils.ts +37 -2
  69. package/src/components/form-item-control.ts +55 -0
  70. package/src/components/form-path.ts +92 -0
  71. package/src/components/form-rules.ts +64 -10
  72. package/src/components/form-utils.ts +94 -2
  73. package/src/components/select-utils.ts +272 -12
  74. package/src/components/table/RsTableHeaderFilter.vue +1 -1
  75. package/src/components/table/rs-table.css +1 -1
  76. package/src/components/use-rs-select.ts +436 -0
  77. package/src/composables/useRsConfig.ts +10 -3
  78. package/src/composables/useRsI18n.ts +16 -5
  79. package/src/index.ts +69 -1
  80. package/src/locale/interpolate.ts +32 -0
  81. package/src/locale/messages.ts +10 -0
  82. package/src/styles.css +68 -12
  83. package/src/theme/brand.example.css +4 -1
  84. package/src/theme/css-token.ts +107 -0
  85. 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
- const isInvalid = computed(() => props.invalid || autoInvalid.value)
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
- props.suffix ||
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 (event.key === 'Enter') {
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="displayMessage ? errorId : undefined"
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(() => Boolean(props.invalid || props.errorMessage))
135
- const displayMessage = computed(() => props.errorMessage || '')
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
- @wheel="onWheel"
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: 12px;
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: 500;
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: 600;
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: 600;
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, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
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, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
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: 600;
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: 600;
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: 600;
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);