base-ui-vue 0.2.0 → 0.4.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 (109) hide show
  1. package/dist/button/ToolbarButton.cjs +6 -0
  2. package/dist/button/ToolbarButton.js +1 -1
  3. package/dist/content/ScrollAreaContent.cjs +168 -0
  4. package/dist/content/ScrollAreaContent.cjs.map +1 -0
  5. package/dist/content/ScrollAreaContent.js +133 -0
  6. package/dist/content/ScrollAreaContent.js.map +1 -0
  7. package/dist/control/SliderControl.js +2 -2
  8. package/dist/corner/ScrollAreaCorner.cjs +77 -0
  9. package/dist/corner/ScrollAreaCorner.cjs.map +1 -0
  10. package/dist/corner/ScrollAreaCorner.js +72 -0
  11. package/dist/corner/ScrollAreaCorner.js.map +1 -0
  12. package/dist/decrement/NumberFieldDecrement.cjs +861 -0
  13. package/dist/decrement/NumberFieldDecrement.cjs.map +1 -0
  14. package/dist/decrement/NumberFieldDecrement.js +700 -0
  15. package/dist/decrement/NumberFieldDecrement.js.map +1 -0
  16. package/dist/fallback/AvatarFallback.cjs +2 -46
  17. package/dist/fallback/AvatarFallback.cjs.map +1 -1
  18. package/dist/fallback/AvatarFallback.js +3 -41
  19. package/dist/fallback/AvatarFallback.js.map +1 -1
  20. package/dist/group/NumberFieldGroup.cjs +72 -0
  21. package/dist/group/NumberFieldGroup.cjs.map +1 -0
  22. package/dist/group/NumberFieldGroup.js +67 -0
  23. package/dist/group/NumberFieldGroup.js.map +1 -0
  24. package/dist/increment/NumberFieldIncrement.cjs +112 -0
  25. package/dist/increment/NumberFieldIncrement.cjs.map +1 -0
  26. package/dist/increment/NumberFieldIncrement.js +107 -0
  27. package/dist/increment/NumberFieldIncrement.js.map +1 -0
  28. package/dist/index.cjs +52 -0
  29. package/dist/index.d.cts +1761 -430
  30. package/dist/index.d.cts.map +1 -1
  31. package/dist/index.d.ts +1761 -430
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +7 -2
  34. package/dist/index2.cjs +4065 -60
  35. package/dist/index2.cjs.map +1 -1
  36. package/dist/index2.js +3955 -184
  37. package/dist/index2.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/index.ts +6 -0
  40. package/src/input/Input.vue +37 -0
  41. package/src/input/InputDataAttributes.ts +30 -0
  42. package/src/input/index.ts +4 -0
  43. package/src/meter/index.ts +16 -0
  44. package/src/meter/indicator/MeterIndicator.vue +65 -0
  45. package/src/meter/label/MeterLabel.vue +63 -0
  46. package/src/meter/root/MeterRoot.vue +131 -0
  47. package/src/meter/root/MeterRootContext.ts +41 -0
  48. package/src/meter/track/MeterTrack.vue +46 -0
  49. package/src/meter/value/MeterValue.vue +85 -0
  50. package/src/number-field/decrement/NumberFieldDecrement.vue +109 -0
  51. package/src/number-field/group/NumberFieldGroup.vue +47 -0
  52. package/src/number-field/increment/NumberFieldIncrement.vue +109 -0
  53. package/src/number-field/index.ts +42 -0
  54. package/src/number-field/input/NumberFieldInput.vue +455 -0
  55. package/src/number-field/root/NumberFieldRoot.vue +626 -0
  56. package/src/number-field/root/NumberFieldRootContext.ts +94 -0
  57. package/src/number-field/root/useNumberFieldButton.ts +171 -0
  58. package/src/number-field/scrub-area/NumberFieldScrubArea.vue +359 -0
  59. package/src/number-field/scrub-area/NumberFieldScrubAreaContext.ts +26 -0
  60. package/src/number-field/scrub-area-cursor/NumberFieldScrubAreaCursor.vue +75 -0
  61. package/src/number-field/utils/constants.ts +4 -0
  62. package/src/number-field/utils/getViewportRect.ts +34 -0
  63. package/src/number-field/utils/parse.ts +248 -0
  64. package/src/number-field/utils/stateAttributesMapping.ts +9 -0
  65. package/src/number-field/utils/subscribeToVisualViewportResize.ts +27 -0
  66. package/src/number-field/utils/types.ts +24 -0
  67. package/src/number-field/utils/validate.ts +120 -0
  68. package/src/otp-field/index.ts +22 -0
  69. package/src/otp-field/input/OtpFieldInput.vue +336 -0
  70. package/src/otp-field/root/OtpFieldRoot.vue +583 -0
  71. package/src/otp-field/root/OtpFieldRootContext.ts +81 -0
  72. package/src/otp-field/utils/otp.ts +135 -0
  73. package/src/otp-field/utils/stateAttributesMapping.ts +16 -0
  74. package/src/progress/index.ts +23 -0
  75. package/src/progress/indicator/ProgressIndicator.vue +74 -0
  76. package/src/progress/label/ProgressLabel.vue +63 -0
  77. package/src/progress/root/ProgressRoot.vue +160 -0
  78. package/src/progress/root/ProgressRootContext.ts +51 -0
  79. package/src/progress/root/ProgressRootDataAttributes.ts +14 -0
  80. package/src/progress/root/stateAttributesMapping.ts +18 -0
  81. package/src/progress/track/ProgressTrack.vue +48 -0
  82. package/src/progress/value/ProgressValue.vue +92 -0
  83. package/src/scroll-area/constants.ts +2 -0
  84. package/src/scroll-area/content/ScrollAreaContent.vue +87 -0
  85. package/src/scroll-area/corner/ScrollAreaCorner.vue +64 -0
  86. package/src/scroll-area/index.ts +25 -0
  87. package/src/scroll-area/root/ScrollAreaRoot.vue +297 -0
  88. package/src/scroll-area/root/ScrollAreaRootContext.ts +89 -0
  89. package/src/scroll-area/root/ScrollAreaRootCssVars.ts +4 -0
  90. package/src/scroll-area/root/ScrollAreaRootDataAttributes.ts +9 -0
  91. package/src/scroll-area/root/stateAttributes.ts +14 -0
  92. package/src/scroll-area/scrollbar/ScrollAreaScrollbar.vue +263 -0
  93. package/src/scroll-area/scrollbar/ScrollAreaScrollbarContext.ts +20 -0
  94. package/src/scroll-area/scrollbar/ScrollAreaScrollbarCssVars.ts +4 -0
  95. package/src/scroll-area/scrollbar/ScrollAreaScrollbarDataAttributes.ts +11 -0
  96. package/src/scroll-area/thumb/ScrollAreaThumb.vue +120 -0
  97. package/src/scroll-area/thumb/ScrollAreaThumbDataAttributes.ts +3 -0
  98. package/src/scroll-area/utils/getOffset.ts +34 -0
  99. package/src/scroll-area/viewport/ScrollAreaViewport.vue +379 -0
  100. package/src/scroll-area/viewport/ScrollAreaViewportContext.ts +20 -0
  101. package/src/scroll-area/viewport/ScrollAreaViewportCssVars.ts +6 -0
  102. package/src/scroll-area/viewport/ScrollAreaViewportDataAttributes.ts +9 -0
  103. package/src/utils/detectBrowser.ts +15 -0
  104. package/src/utils/formatNumber.ts +60 -2
  105. package/src/utils/scrollEdges.ts +33 -0
  106. package/src/utils/styles.ts +28 -0
  107. package/src/utils/useInterval.ts +45 -0
  108. package/src/utils/usePressAndHold.ts +260 -0
  109. package/src/utils/useValueChanged.ts +21 -0
@@ -0,0 +1,336 @@
1
+ <script setup lang="ts">
2
+ import type { ComponentPublicInstance } from 'vue'
3
+ import type { BaseUIComponentProps } from '../../utils/types'
4
+ import type { OtpFieldRootState } from '../root/OtpFieldRoot.vue'
5
+ import { computed, ref, useAttrs } from 'vue'
6
+ import { IndexGuessBehavior, useCompositeListItem } from '../../composite/list/useCompositeListItem'
7
+ import { useDirection } from '../../direction-provider/DirectionContext'
8
+ import { stopEvent } from '../../floating-ui-vue/utils'
9
+ import { mergeProps } from '../../merge-props/mergeProps'
10
+ import { createChangeEventDetails, createGenericEventDetails } from '../../utils/createBaseUIEventDetails'
11
+ import { REASONS } from '../../utils/reasons'
12
+ import { useRenderElement } from '../../utils/useRenderElement'
13
+ import { getOtpFieldInputState, useOtpFieldRootContext } from '../root/OtpFieldRootContext'
14
+ import { getOTPCharacter, getOTPValueLength, normalizeOTPValueWithDetails, removeOTPCharacter, replaceOTPValue } from '../utils/otp'
15
+ import { inputStateAttributesMapping } from '../utils/stateAttributesMapping'
16
+
17
+ export interface OtpFieldInputState extends Omit<OtpFieldRootState, 'filled' | 'value'> {
18
+ /**
19
+ * Whether this input contains a character.
20
+ */
21
+ filled: boolean
22
+ /**
23
+ * The input index.
24
+ */
25
+ index: number
26
+ /**
27
+ * The character rendered in this slot.
28
+ */
29
+ value: string
30
+ }
31
+
32
+ export interface OtpFieldInputProps extends BaseUIComponentProps<OtpFieldInputState> {}
33
+
34
+ defineOptions({
35
+ name: 'OtpFieldInput',
36
+ inheritAttrs: false,
37
+ })
38
+
39
+ const props = defineProps<OtpFieldInputProps>()
40
+
41
+ const attrs = useAttrs()
42
+ const attrsObject = attrs as Record<string, any>
43
+
44
+ const {
45
+ activeIndex,
46
+ autoComplete,
47
+ disabled,
48
+ form,
49
+ focusInput,
50
+ queueFocusInput,
51
+ getInputId,
52
+ handleInputBlur,
53
+ handleInputFocus,
54
+ inputMode,
55
+ inputAriaLabelledBy,
56
+ invalid,
57
+ length,
58
+ mask,
59
+ pattern,
60
+ reportValueInvalid,
61
+ readOnly,
62
+ required,
63
+ normalizeValue,
64
+ setValue,
65
+ state,
66
+ validationType,
67
+ value,
68
+ } = useOtpFieldRootContext()
69
+
70
+ const { ref: listItemRef, index } = useCompositeListItem({
71
+ indexGuessBehavior: () => IndexGuessBehavior.GuessFromOrder,
72
+ })
73
+ const inputRef = ref<HTMLInputElement | null>(null)
74
+ const direction = useDirection()
75
+
76
+ const slotValue = computed(() => getOTPCharacter(value.value, index.value))
77
+ const inputState = computed(() => getOtpFieldInputState(state.value, slotValue.value, index.value))
78
+
79
+ const externalAriaLabel = computed(() => attrs['aria-label'] as string | undefined)
80
+ const externalAriaLabelledBy = computed(() => attrs['aria-labelledby'] as string | undefined)
81
+ const inheritedLabel = computed(() => externalAriaLabelledBy.value ?? inputAriaLabelledBy.value)
82
+ const ariaLabel = computed(() => (index.value === 0 ? undefined : externalAriaLabel.value))
83
+
84
+ function setInputRef(el: Element | ComponentPublicInstance | null) {
85
+ listItemRef(el as HTMLElement | null)
86
+ inputRef.value = el as HTMLInputElement | null
87
+ }
88
+
89
+ function onMousedown(event: MouseEvent) {
90
+ if (event.defaultPrevented || disabled.value) {
91
+ return
92
+ }
93
+
94
+ event.preventDefault()
95
+ focusInput(index.value)
96
+ }
97
+
98
+ function onFocus(event: FocusEvent) {
99
+ if (event.defaultPrevented || disabled.value) {
100
+ return
101
+ }
102
+
103
+ handleInputFocus(index.value, event)
104
+ }
105
+
106
+ function onBlur(event: FocusEvent) {
107
+ if (event.defaultPrevented) {
108
+ return
109
+ }
110
+
111
+ handleInputBlur(event)
112
+ }
113
+
114
+ function onInput(event: Event) {
115
+ const target = event.target as HTMLInputElement
116
+ if (event.defaultPrevented || disabled.value || readOnly.value) {
117
+ return
118
+ }
119
+
120
+ const rawValue = target.value
121
+ const [nextDigits, didRejectCharacters] = normalizeOTPValueWithDetails(
122
+ rawValue,
123
+ length.value,
124
+ validationType.value,
125
+ normalizeValue.value,
126
+ )
127
+
128
+ if (didRejectCharacters) {
129
+ reportValueInvalid(rawValue, createGenericEventDetails(REASONS.inputChange, event))
130
+ }
131
+
132
+ if (nextDigits === '') {
133
+ if (rawValue === '') {
134
+ setValue(
135
+ removeOTPCharacter(value.value, index.value),
136
+ createChangeEventDetails(REASONS.inputClear, event),
137
+ )
138
+ }
139
+ target.value = slotValue.value
140
+
141
+ if (slotValue.value !== '') {
142
+ target.select()
143
+ }
144
+ return
145
+ }
146
+
147
+ const nextValue = replaceOTPValue(
148
+ value.value,
149
+ index.value,
150
+ nextDigits,
151
+ length.value,
152
+ validationType.value,
153
+ normalizeValue.value,
154
+ )
155
+
156
+ const committedValue = setValue(nextValue, createChangeEventDetails(REASONS.inputChange, event))
157
+
158
+ if (committedValue != null) {
159
+ const nextInput = Math.min(index.value + getOTPValueLength(nextDigits), length.value - 1)
160
+ queueFocusInput(nextInput, committedValue)
161
+ }
162
+ }
163
+
164
+ function onKeydown(event: KeyboardEvent) {
165
+ if (event.defaultPrevented || disabled.value) {
166
+ return
167
+ }
168
+
169
+ const firstIndex = 0
170
+ const lastIndex = Math.max(length.value - 1, firstIndex)
171
+ const endTargetIndex = Math.min(getOTPValueLength(value.value), lastIndex)
172
+ const hasBoundaryModifier = (event.ctrlKey || event.metaKey) && !event.altKey
173
+ const isRtl = direction.value === 'rtl'
174
+ const previousKey = isRtl ? 'ArrowRight' : 'ArrowLeft'
175
+ const nextKey = isRtl ? 'ArrowLeft' : 'ArrowRight'
176
+
177
+ if (event.key === previousKey) {
178
+ stopEvent(event)
179
+ focusInput(hasBoundaryModifier ? firstIndex : Math.max(firstIndex, index.value - 1))
180
+ return
181
+ }
182
+
183
+ if (event.key === nextKey) {
184
+ stopEvent(event)
185
+ focusInput(hasBoundaryModifier ? endTargetIndex : Math.min(lastIndex, index.value + 1))
186
+ return
187
+ }
188
+
189
+ if (event.key === 'Home' || event.key === 'ArrowUp') {
190
+ stopEvent(event)
191
+ focusInput(firstIndex)
192
+ return
193
+ }
194
+
195
+ if (event.key === 'End' || event.key === 'ArrowDown') {
196
+ stopEvent(event)
197
+ focusInput(endTargetIndex)
198
+ return
199
+ }
200
+
201
+ if (readOnly.value) {
202
+ return
203
+ }
204
+
205
+ function setKeyboardValue(nextValue: string, targetIndex: number) {
206
+ const committedValue = setValue(nextValue, createChangeEventDetails(REASONS.keyboard, event))
207
+
208
+ if (committedValue != null) {
209
+ queueFocusInput(targetIndex, committedValue)
210
+ }
211
+ }
212
+
213
+ if (event.key === 'Backspace' && hasBoundaryModifier) {
214
+ stopEvent(event)
215
+ setKeyboardValue('', firstIndex)
216
+ return
217
+ }
218
+
219
+ if (event.key === 'Delete') {
220
+ stopEvent(event)
221
+ setKeyboardValue(removeOTPCharacter(value.value, index.value), index.value)
222
+ return
223
+ }
224
+
225
+ const target = event.currentTarget as HTMLInputElement
226
+ const inputValue = target.value
227
+ const fullSelection = target.selectionStart === 0 && target.selectionEnd === inputValue.length
228
+
229
+ if (getOTPValueLength(event.key) === 1 && fullSelection && slotValue.value === event.key) {
230
+ stopEvent(event)
231
+ if (index.value < length.value - 1) {
232
+ focusInput(index.value + 1)
233
+ }
234
+ return
235
+ }
236
+
237
+ if (event.key === 'Backspace') {
238
+ stopEvent(event)
239
+ const targetIndex = Math.max(firstIndex, index.value - 1)
240
+ const deleteIndex = slotValue.value === '' ? targetIndex : index.value
241
+ setKeyboardValue(removeOTPCharacter(value.value, deleteIndex), targetIndex)
242
+ }
243
+ }
244
+
245
+ function onPaste(event: ClipboardEvent) {
246
+ if (event.defaultPrevented || disabled.value || readOnly.value) {
247
+ return
248
+ }
249
+
250
+ let rawValue = ''
251
+
252
+ try {
253
+ rawValue = event.clipboardData?.getData('text/plain') ?? ''
254
+ }
255
+ catch {
256
+ return
257
+ }
258
+
259
+ event.preventDefault()
260
+
261
+ const [nextDigits, didRejectCharacters] = normalizeOTPValueWithDetails(
262
+ rawValue,
263
+ length.value,
264
+ validationType.value,
265
+ normalizeValue.value,
266
+ )
267
+
268
+ if (didRejectCharacters) {
269
+ reportValueInvalid(rawValue, createGenericEventDetails(REASONS.inputPaste, event))
270
+ }
271
+
272
+ if (nextDigits === '') {
273
+ return
274
+ }
275
+
276
+ const committedValue = setValue(
277
+ replaceOTPValue(value.value, index.value, nextDigits, length.value, validationType.value, normalizeValue.value),
278
+ createChangeEventDetails(REASONS.inputPaste, event),
279
+ )
280
+
281
+ if (committedValue != null) {
282
+ const nextInput = Math.min(index.value + getOTPValueLength(nextDigits), length.value - 1)
283
+ queueFocusInput(nextInput, committedValue)
284
+ }
285
+ }
286
+
287
+ const inputProps = computed(() => mergeProps(
288
+ attrsObject,
289
+ {
290
+ 'id': getInputId(index.value),
291
+ 'value': slotValue.value,
292
+ 'type': attrsObject.type ?? (mask.value ? 'password' : 'text'),
293
+ 'inputmode': inputMode.value,
294
+ 'autocomplete': index.value === 0 ? autoComplete.value : 'off',
295
+ 'autocorrect': 'off',
296
+ 'spellcheck': 'false',
297
+ 'enterkeyhint': index.value === length.value - 1 ? 'done' : 'next',
298
+ // Only the first slot has a max length to avoid password manager bubbles appearing after later inputs.
299
+ 'maxlength': index.value === 0 ? length.value : undefined,
300
+ 'tabindex': activeIndex.value === index.value ? 0 : -1,
301
+ 'disabled': disabled.value,
302
+ 'form': form.value,
303
+ 'pattern': pattern.value,
304
+ 'readonly': readOnly.value,
305
+ 'required': required.value,
306
+ 'aria-labelledby': ariaLabel.value == null ? inheritedLabel.value : undefined,
307
+ 'aria-invalid': !disabled.value && invalid.value ? true : undefined,
308
+ 'aria-label': ariaLabel.value,
309
+ 'onMousedown': onMousedown,
310
+ 'onFocus': onFocus,
311
+ 'onBlur': onBlur,
312
+ 'onInput': onInput,
313
+ 'onKeydown': onKeydown,
314
+ 'onPaste': onPaste,
315
+ },
316
+ ))
317
+
318
+ const {
319
+ tag,
320
+ mergedProps,
321
+ renderless,
322
+ ref: renderRef,
323
+ } = useRenderElement({
324
+ componentProps: props,
325
+ state: inputState,
326
+ props: inputProps,
327
+ stateAttributesMapping: inputStateAttributesMapping,
328
+ defaultTagName: 'input',
329
+ ref: setInputRef,
330
+ })
331
+ </script>
332
+
333
+ <template>
334
+ <slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="inputState" />
335
+ <component :is="tag" v-else :ref="renderRef" v-bind="mergedProps" />
336
+ </template>