base-ui-vue 0.1.0 → 0.2.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 (204) hide show
  1. package/README.md +41 -1
  2. package/dist/button/Button.cjs +53 -12
  3. package/dist/button/Button.cjs.map +1 -1
  4. package/dist/button/Button.js +26 -15
  5. package/dist/button/Button.js.map +1 -1
  6. package/dist/button/ToolbarButton.cjs +367 -0
  7. package/dist/button/ToolbarButton.cjs.map +1 -0
  8. package/dist/button/ToolbarButton.js +320 -0
  9. package/dist/button/ToolbarButton.js.map +1 -0
  10. package/dist/button/ToolbarButtonDataAttributes.cjs +27 -0
  11. package/dist/button/ToolbarButtonDataAttributes.cjs.map +1 -0
  12. package/dist/button/ToolbarButtonDataAttributes.js +21 -0
  13. package/dist/button/ToolbarButtonDataAttributes.js.map +1 -0
  14. package/dist/checkbox/index.cjs +1173 -0
  15. package/dist/checkbox/index.cjs.map +1 -0
  16. package/dist/checkbox/index.js +1048 -0
  17. package/dist/checkbox/index.js.map +1 -0
  18. package/dist/checkbox-group/CheckboxGroup.cjs +629 -0
  19. package/dist/checkbox-group/CheckboxGroup.cjs.map +1 -0
  20. package/dist/checkbox-group/CheckboxGroup.js +540 -0
  21. package/dist/checkbox-group/CheckboxGroup.js.map +1 -0
  22. package/dist/checkbox-group/CheckboxGroupDataAttributes.cjs +18 -0
  23. package/dist/checkbox-group/CheckboxGroupDataAttributes.cjs.map +1 -0
  24. package/dist/checkbox-group/CheckboxGroupDataAttributes.js +12 -0
  25. package/dist/checkbox-group/CheckboxGroupDataAttributes.js.map +1 -0
  26. package/dist/composite/composite.cjs +167 -0
  27. package/dist/composite/composite.cjs.map +1 -1
  28. package/dist/composite/composite.js +96 -1
  29. package/dist/composite/composite.js.map +1 -1
  30. package/dist/composite/constants.cjs +12 -0
  31. package/dist/composite/constants.cjs.map +1 -0
  32. package/dist/composite/constants.js +6 -0
  33. package/dist/composite/constants.js.map +1 -0
  34. package/dist/control/FieldControl.cjs +18 -343
  35. package/dist/control/FieldControl.cjs.map +1 -1
  36. package/dist/control/FieldControl.js +14 -285
  37. package/dist/control/FieldControl.js.map +1 -1
  38. package/dist/control/SliderControl.cjs +636 -0
  39. package/dist/control/SliderControl.cjs.map +1 -0
  40. package/dist/control/SliderControl.js +553 -0
  41. package/dist/control/SliderControl.js.map +1 -0
  42. package/dist/control/SliderControlDataAttributes.cjs +47 -0
  43. package/dist/control/SliderControlDataAttributes.cjs.map +1 -0
  44. package/dist/control/SliderControlDataAttributes.js +41 -0
  45. package/dist/control/SliderControlDataAttributes.js.map +1 -0
  46. package/dist/csp-provider/CSPContext.cjs +32 -0
  47. package/dist/csp-provider/CSPContext.cjs.map +1 -0
  48. package/dist/csp-provider/CSPContext.js +21 -0
  49. package/dist/csp-provider/CSPContext.js.map +1 -0
  50. package/dist/csp-provider/CSPProvider.cjs +46 -0
  51. package/dist/csp-provider/CSPProvider.cjs.map +1 -0
  52. package/dist/csp-provider/CSPProvider.js +41 -0
  53. package/dist/csp-provider/CSPProvider.js.map +1 -0
  54. package/dist/description/FieldDescription.cjs +5 -5
  55. package/dist/description/FieldDescription.cjs.map +1 -1
  56. package/dist/description/FieldDescription.js +1 -1
  57. package/dist/direction-provider/DirectionProvider.cjs +2 -2
  58. package/dist/direction-provider/DirectionProvider.cjs.map +1 -1
  59. package/dist/direction-provider/DirectionProvider.js +1 -1
  60. package/dist/error/FieldError.cjs +10 -288
  61. package/dist/error/FieldError.cjs.map +1 -1
  62. package/dist/error/FieldError.js +4 -246
  63. package/dist/error/FieldError.js.map +1 -1
  64. package/dist/form/Form.cjs +5 -4
  65. package/dist/form/Form.cjs.map +1 -1
  66. package/dist/form/Form.js +5 -4
  67. package/dist/form/Form.js.map +1 -1
  68. package/dist/group/ToolbarGroup.cjs +92 -0
  69. package/dist/group/ToolbarGroup.cjs.map +1 -0
  70. package/dist/group/ToolbarGroup.js +87 -0
  71. package/dist/group/ToolbarGroup.js.map +1 -0
  72. package/dist/group/ToolbarGroupDataAttributes.cjs +23 -0
  73. package/dist/group/ToolbarGroupDataAttributes.cjs.map +1 -0
  74. package/dist/group/ToolbarGroupDataAttributes.js +17 -0
  75. package/dist/group/ToolbarGroupDataAttributes.js.map +1 -0
  76. package/dist/header/AccordionHeader.cjs +2 -2
  77. package/dist/header/AccordionHeader.js +1 -1
  78. package/dist/image/AvatarImage.cjs +4 -4
  79. package/dist/image/AvatarImage.cjs.map +1 -1
  80. package/dist/image/AvatarImage.js +1 -1
  81. package/dist/index.cjs +80 -10
  82. package/dist/index.d.cts +2751 -612
  83. package/dist/index.d.cts.map +1 -1
  84. package/dist/index.d.ts +2751 -612
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +15 -5
  87. package/dist/index2.cjs +3651 -370
  88. package/dist/index2.cjs.map +1 -1
  89. package/dist/index2.js +3365 -270
  90. package/dist/index2.js.map +1 -1
  91. package/package.json +8 -4
  92. package/src/accordion/root/AccordionRoot.vue +2 -1
  93. package/src/checkbox/index.ts +23 -0
  94. package/src/checkbox/indicator/CheckboxIndicator.vue +102 -0
  95. package/src/checkbox/indicator/CheckboxIndicatorDataAttributes.ts +61 -0
  96. package/src/checkbox/root/CheckboxRoot.vue +632 -0
  97. package/src/checkbox/root/CheckboxRootContext.ts +22 -0
  98. package/src/checkbox/root/CheckboxRootDataAttributes.ts +54 -0
  99. package/src/checkbox/utils/useStateAttributesMapping.ts +30 -0
  100. package/src/checkbox-group/CheckboxGroup.vue +241 -0
  101. package/src/checkbox-group/CheckboxGroupContext.ts +39 -0
  102. package/src/checkbox-group/CheckboxGroupDataAttributes.ts +6 -0
  103. package/src/checkbox-group/index.ts +11 -0
  104. package/src/checkbox-group/useCheckboxGroupParent.ts +173 -0
  105. package/src/collapsible/panel/useCollapsiblePanel.ts +2 -1
  106. package/src/collapsible/root/useCollapsibleRoot.ts +3 -1
  107. package/src/composite/composite.ts +2 -0
  108. package/src/composite/item/CompositeItem.vue +7 -8
  109. package/src/composite/root/CompositeRoot.vue +12 -1
  110. package/src/csp-provider/CSPContext.ts +26 -0
  111. package/src/csp-provider/CSPProvider.vue +40 -0
  112. package/src/csp-provider/index.ts +5 -0
  113. package/src/field/item/FieldItem.vue +6 -1
  114. package/src/field/label/FieldLabel.vue +10 -51
  115. package/src/field/root/FieldRoot.vue +16 -3
  116. package/src/floating-ui-vue/types.ts +1 -4
  117. package/src/floating-ui-vue/utils/element.ts +12 -0
  118. package/src/floating-ui-vue/utils/shadowDom.ts +44 -0
  119. package/src/floating-ui-vue/utils.ts +3 -0
  120. package/src/form/Form.vue +5 -3
  121. package/src/index.ts +9 -0
  122. package/src/labelable-provider/LabelableContext.ts +2 -2
  123. package/src/labelable-provider/LabelableProvider.vue +21 -4
  124. package/src/labelable-provider/index.ts +2 -0
  125. package/src/labelable-provider/useAriaLabelledBy.ts +9 -9
  126. package/src/labelable-provider/useLabel.ts +115 -0
  127. package/src/labelable-provider/useLabelableId.ts +12 -10
  128. package/src/separator/Separator.vue +65 -0
  129. package/src/separator/SeparatorDataAttributes.ts +7 -0
  130. package/src/separator/index.ts +3 -0
  131. package/src/slider/control/SliderControl.vue +497 -0
  132. package/src/slider/control/SliderControlDataAttributes.ts +35 -0
  133. package/src/slider/index.ts +35 -0
  134. package/src/slider/indicator/SliderIndicator.vue +144 -0
  135. package/src/slider/indicator/SliderIndicatorDataAttributes.ts +35 -0
  136. package/src/slider/label/SliderLabel.vue +75 -0
  137. package/src/slider/root/SliderRoot.vue +557 -0
  138. package/src/slider/root/SliderRootContext.ts +126 -0
  139. package/src/slider/root/SliderRootDataAttributes.ts +35 -0
  140. package/src/slider/root/stateAttributesMapping.ts +13 -0
  141. package/src/slider/thumb/SliderThumb.vue +601 -0
  142. package/src/slider/thumb/SliderThumbDataAttributes.ts +39 -0
  143. package/src/slider/thumb/prehydrationScript.min.ts +5 -0
  144. package/src/slider/thumb/prehydrationScript.template.js +69 -0
  145. package/src/slider/track/SliderTrack.vue +48 -0
  146. package/src/slider/track/SliderTrackDataAttributes.ts +10 -0
  147. package/src/slider/utils/asc.ts +3 -0
  148. package/src/slider/utils/getMidpoint.ts +9 -0
  149. package/src/slider/utils/getPushedThumbValues.ts +68 -0
  150. package/src/slider/utils/getSliderValue.ts +25 -0
  151. package/src/slider/utils/replaceArrayItemAtIndex.ts +15 -0
  152. package/src/slider/utils/resolveThumbCollision.ts +177 -0
  153. package/src/slider/utils/roundValueToStep.ts +19 -0
  154. package/src/slider/utils/test-utils.ts +25 -0
  155. package/src/slider/utils/validateMinimumDistance.ts +20 -0
  156. package/src/slider/utils/valueArrayToPercentages.ts +10 -0
  157. package/src/slider/value/SliderValue.vue +90 -0
  158. package/src/slider/value/SliderValueDataAttributes.ts +35 -0
  159. package/src/switch/index.ts +14 -0
  160. package/src/switch/root/SwitchRoot.vue +448 -0
  161. package/src/switch/root/SwitchRootContext.ts +22 -0
  162. package/src/switch/root/SwitchRootDataAttributes.ts +46 -0
  163. package/src/switch/stateAttributesMapping.ts +23 -0
  164. package/src/switch/thumb/SwitchThumb.vue +59 -0
  165. package/src/switch/thumb/SwitchThumbDataAttributes.ts +46 -0
  166. package/src/toggle/Toggle.vue +211 -0
  167. package/src/toggle/ToggleDataAttributes.ts +6 -0
  168. package/src/toggle/index.ts +3 -0
  169. package/src/toggle-group/ToggleGroup.vue +224 -0
  170. package/src/toggle-group/ToggleGroupContext.ts +45 -0
  171. package/src/toggle-group/ToggleGroupDataAttributes.ts +15 -0
  172. package/src/toggle-group/index.ts +5 -0
  173. package/src/toolbar/button/ToolbarButton.vue +99 -0
  174. package/src/toolbar/button/ToolbarButtonDataAttributes.ts +15 -0
  175. package/src/toolbar/group/ToolbarGroup.vue +70 -0
  176. package/src/toolbar/group/ToolbarGroupContext.ts +23 -0
  177. package/src/toolbar/group/ToolbarGroupDataAttributes.ts +11 -0
  178. package/src/toolbar/index.ts +27 -0
  179. package/src/toolbar/input/ToolbarInput.vue +114 -0
  180. package/src/toolbar/input/ToolbarInputDataAttributes.ts +15 -0
  181. package/src/toolbar/link/ToolbarLink.vue +54 -0
  182. package/src/toolbar/link/ToolbarLinkDataAttributes.ts +7 -0
  183. package/src/toolbar/root/ToolbarRoot.vue +144 -0
  184. package/src/toolbar/root/ToolbarRootContext.ts +29 -0
  185. package/src/toolbar/root/ToolbarRootDataAttributes.ts +11 -0
  186. package/src/toolbar/separator/ToolbarSeparator.vue +41 -0
  187. package/src/toolbar/separator/ToolbarSeparatorDataAttributes.ts +7 -0
  188. package/src/use-button/useButton.ts +2 -1
  189. package/src/utils/areArraysEqual.ts +12 -0
  190. package/src/utils/clamp.ts +7 -0
  191. package/src/utils/createBaseUIEventDetails.ts +9 -0
  192. package/src/utils/formatNumber.ts +7 -0
  193. package/src/utils/owner.ts +5 -0
  194. package/src/utils/resolveAriaLabelledBy.ts +10 -0
  195. package/src/utils/useControllableState.ts +78 -14
  196. package/src/utils/useFocusableWhenDisabled.ts +6 -1
  197. package/src/utils/useMergedRefs.ts +26 -2
  198. package/src/utils/useRegisteredLabelId.ts +21 -0
  199. package/src/utils/valueToPercent.ts +7 -0
  200. package/src/utils/visuallyHidden.ts +24 -0
  201. package/dist/direction-provider/DirectionContext.cjs +0 -26
  202. package/dist/direction-provider/DirectionContext.cjs.map +0 -1
  203. package/dist/direction-provider/DirectionContext.js +0 -15
  204. package/dist/direction-provider/DirectionContext.js.map +0 -1
@@ -0,0 +1,3 @@
1
+ export { default as Separator } from './Separator.vue'
2
+ export type { SeparatorProps, SeparatorState } from './Separator.vue'
3
+ export { SeparatorDataAttributes } from './SeparatorDataAttributes'
@@ -0,0 +1,497 @@
1
+ <script setup lang="ts">
2
+ import type { ComponentPublicInstance } from 'vue'
3
+ import type { Coords } from '../../floating-ui-vue/types'
4
+ import type { BaseUIComponentProps } from '../../utils/types'
5
+ import type { SliderRootState } from '../root/SliderRoot.vue'
6
+ import { isElement } from '@floating-ui/utils/dom'
7
+ import { computed, onBeforeUnmount, onMounted, ref, useAttrs, watchEffect } from 'vue'
8
+ import { useDirection } from '../../direction-provider'
9
+ import { activeElement, contains, getTarget } from '../../floating-ui-vue/utils'
10
+ import { mergeProps } from '../../merge-props/mergeProps'
11
+ import { clamp } from '../../utils/clamp'
12
+ import { createChangeEventDetails, createGenericEventDetails } from '../../utils/createBaseUIEventDetails'
13
+ import { ownerDocument } from '../../utils/owner'
14
+ import { REASONS } from '../../utils/reasons'
15
+ import { useAnimationFrame } from '../../utils/useAnimationFrame'
16
+ import { useRenderElement } from '../../utils/useRenderElement'
17
+ import { useSliderRootContext } from '../root/SliderRootContext'
18
+ import { sliderStateAttributesMapping } from '../root/stateAttributesMapping'
19
+ import { getMidpoint } from '../utils/getMidpoint'
20
+ import { resolveThumbCollision } from '../utils/resolveThumbCollision'
21
+ import { roundValueToStep } from '../utils/roundValueToStep'
22
+ import { validateMinimumDistance } from '../utils/validateMinimumDistance'
23
+
24
+ /**
25
+ * The clickable, interactive part of the slider.
26
+ * Renders a `<div>` element.
27
+ *
28
+ * Documentation: [Base UI Vue Slider](https://baseui-vue.com/docs/components/slider)
29
+ */
30
+ defineOptions({
31
+ name: 'SliderControl',
32
+ inheritAttrs: false,
33
+ })
34
+
35
+ const props = withDefaults(defineProps<SliderControlProps>(), {
36
+ as: 'div',
37
+ })
38
+
39
+ const INTENTIONAL_DRAG_COUNT_THRESHOLD = 2
40
+
41
+ function getControlOffset(styles: CSSStyleDeclaration | null, vertical: boolean) {
42
+ if (!styles) {
43
+ return { start: 0, end: 0 }
44
+ }
45
+
46
+ function parseSize(value: string | null | undefined) {
47
+ const parsed = value != null ? Number.parseFloat(value) : 0
48
+ return Number.isNaN(parsed) ? 0 : parsed
49
+ }
50
+
51
+ const start = !vertical ? 'InlineStart' : 'Top'
52
+ const end = !vertical ? 'InlineEnd' : 'Bottom'
53
+
54
+ return {
55
+ start: parseSize(styles[`border${start}Width` as keyof CSSStyleDeclaration] as string)
56
+ + parseSize(styles[`padding${start}` as keyof CSSStyleDeclaration] as string),
57
+ end: parseSize(styles[`border${end}Width` as keyof CSSStyleDeclaration] as string)
58
+ + parseSize(styles[`padding${end}` as keyof CSSStyleDeclaration] as string),
59
+ }
60
+ }
61
+
62
+ function getFingerCoords(
63
+ event: TouchEvent | PointerEvent,
64
+ touchIdRef: { value: number | null },
65
+ ): Coords | null {
66
+ if (touchIdRef.value != null && 'changedTouches' in event) {
67
+ for (let i = 0; i < event.changedTouches.length; i += 1) {
68
+ const touch = event.changedTouches[i]
69
+ if (touch.identifier === touchIdRef.value) {
70
+ return { x: touch.clientX, y: touch.clientY }
71
+ }
72
+ }
73
+
74
+ return null
75
+ }
76
+
77
+ return {
78
+ x: (event as PointerEvent).clientX,
79
+ y: (event as PointerEvent).clientY,
80
+ }
81
+ }
82
+
83
+ interface FingerState {
84
+ value: number | number[]
85
+ thumbIndex: number
86
+ didSwap: boolean
87
+ }
88
+
89
+ export interface SliderControlState extends SliderRootState {}
90
+ export interface SliderControlProps extends BaseUIComponentProps<SliderControlState> {}
91
+
92
+ const attrs = useAttrs()
93
+ const rootContext = useSliderRootContext()
94
+ const direction = useDirection()
95
+ const focusFrame = useAnimationFrame()
96
+
97
+ const range = computed(() => rootContext.values.value.length > 1)
98
+ const vertical = computed(() => rootContext.orientation.value === 'vertical')
99
+
100
+ const controlRef = ref<HTMLElement | null>(null)
101
+ const stylesRef = ref<CSSStyleDeclaration | null>(null)
102
+ function setStylesRef(element: HTMLElement | null) {
103
+ if (element && stylesRef.value == null) {
104
+ stylesRef.value = getComputedStyle(element)
105
+ }
106
+ }
107
+
108
+ const touchIdRef = ref<number | null>(null)
109
+ const moveCountRef = ref(0)
110
+ const insetThumbOffsetRef = ref(0)
111
+ const latestValuesRef = ref(rootContext.values.value)
112
+
113
+ function updatePressedThumb(nextIndex: number) {
114
+ if (rootContext.pressedThumbIndexRef.value !== nextIndex) {
115
+ rootContext.pressedThumbIndexRef.value = nextIndex
116
+ }
117
+
118
+ const thumbElement = rootContext.thumbRefs.value[nextIndex]
119
+ if (!thumbElement) {
120
+ rootContext.pressedThumbCenterOffsetRef.value = null
121
+ rootContext.pressedInputRef.value = null
122
+ return
123
+ }
124
+
125
+ rootContext.pressedInputRef.value = thumbElement.querySelector<HTMLInputElement>('input[type="range"]')
126
+ }
127
+
128
+ function getFingerState(fingerCoords: Coords): FingerState | null {
129
+ const control = controlRef.value
130
+ if (!control) {
131
+ return null
132
+ }
133
+
134
+ const { width, height, bottom, left, right } = control.getBoundingClientRect()
135
+ const controlOffset = getControlOffset(stylesRef.value, vertical.value)
136
+ const insetThumbOffset = insetThumbOffsetRef.value
137
+ const controlSize
138
+ = (vertical.value ? height : width)
139
+ - controlOffset.start
140
+ - controlOffset.end
141
+ - insetThumbOffset * 2
142
+ const thumbCenterOffset = rootContext.pressedThumbCenterOffsetRef.value ?? 0
143
+ const fingerX = fingerCoords.x - thumbCenterOffset
144
+ const fingerY = fingerCoords.y - thumbCenterOffset
145
+
146
+ const valueSize = vertical.value
147
+ ? bottom - fingerY - controlOffset.end
148
+ : ((direction.value === 'rtl' ? right - fingerX : fingerX - left) - controlOffset.start)
149
+
150
+ const valueRescaled = clamp((valueSize - insetThumbOffset) / controlSize, 0, 1)
151
+
152
+ let newValue = (rootContext.max.value - rootContext.min.value) * valueRescaled + rootContext.min.value
153
+ newValue = roundValueToStep(newValue, rootContext.step.value, rootContext.min.value)
154
+ newValue = clamp(newValue, rootContext.min.value, rootContext.max.value)
155
+
156
+ if (!range.value) {
157
+ return {
158
+ value: newValue,
159
+ thumbIndex: 0,
160
+ didSwap: false,
161
+ }
162
+ }
163
+
164
+ const thumbIndex = rootContext.pressedThumbIndexRef.value
165
+ if (thumbIndex < 0) {
166
+ return null
167
+ }
168
+
169
+ const collisionResult = resolveThumbCollision({
170
+ behavior: rootContext.thumbCollisionBehavior.value,
171
+ values: rootContext.values.value,
172
+ currentValues: latestValuesRef.value ?? rootContext.values.value,
173
+ initialValues: rootContext.pressedValuesRef.value,
174
+ pressedIndex: thumbIndex,
175
+ nextValue: newValue,
176
+ min: rootContext.min.value,
177
+ max: rootContext.max.value,
178
+ step: rootContext.step.value,
179
+ minStepsBetweenValues: rootContext.minStepsBetweenValues.value,
180
+ })
181
+
182
+ if (rootContext.thumbCollisionBehavior.value === 'swap' && collisionResult.didSwap) {
183
+ updatePressedThumb(collisionResult.thumbIndex)
184
+ }
185
+ else {
186
+ rootContext.pressedThumbIndexRef.value = collisionResult.thumbIndex
187
+ }
188
+
189
+ return collisionResult
190
+ }
191
+
192
+ function startPressing(fingerCoords: Coords) {
193
+ rootContext.pressedValuesRef.value = range.value ? rootContext.values.value.slice() : null
194
+ latestValuesRef.value = rootContext.values.value
195
+
196
+ const pressedThumbIndex = rootContext.pressedThumbIndexRef.value
197
+ let closestThumbIndex = pressedThumbIndex
198
+
199
+ if (pressedThumbIndex > -1 && pressedThumbIndex < rootContext.values.value.length) {
200
+ if (rootContext.values.value[pressedThumbIndex] === rootContext.max.value) {
201
+ let candidateIndex = pressedThumbIndex
202
+
203
+ while (candidateIndex > 0 && rootContext.values.value[candidateIndex - 1] === rootContext.max.value) {
204
+ candidateIndex -= 1
205
+ }
206
+
207
+ closestThumbIndex = candidateIndex
208
+ }
209
+ }
210
+ else {
211
+ const axis = !vertical.value ? 'x' : 'y'
212
+ let minDistance: number | undefined
213
+
214
+ closestThumbIndex = -1
215
+
216
+ for (let i = 0; i < rootContext.thumbRefs.value.length; i += 1) {
217
+ const thumbEl = rootContext.thumbRefs.value[i]
218
+ if (isElement(thumbEl)) {
219
+ const midpoint = getMidpoint(thumbEl)
220
+ const distance = Math.abs(fingerCoords[axis] - midpoint[axis])
221
+
222
+ if (minDistance === undefined || distance <= minDistance) {
223
+ closestThumbIndex = i
224
+ minDistance = distance
225
+ }
226
+ }
227
+ }
228
+ }
229
+
230
+ if (closestThumbIndex > -1 && closestThumbIndex !== pressedThumbIndex) {
231
+ updatePressedThumb(closestThumbIndex)
232
+ }
233
+
234
+ if (rootContext.inset.value) {
235
+ const thumbEl = rootContext.thumbRefs.value[closestThumbIndex]
236
+ if (isElement(thumbEl)) {
237
+ const thumbRect = thumbEl.getBoundingClientRect()
238
+ insetThumbOffsetRef.value = thumbRect[vertical.value ? 'height' : 'width'] / 2
239
+ }
240
+ }
241
+ }
242
+
243
+ function focusThumb(thumbIndex: number) {
244
+ const input = rootContext.thumbRefs.value?.[thumbIndex]?.querySelector<HTMLInputElement>('input[type="range"]')
245
+ if (!input) {
246
+ return
247
+ }
248
+
249
+ input.focus({ preventScroll: true })
250
+ }
251
+
252
+ function stopListening() {
253
+ const doc = ownerDocument(controlRef.value)
254
+ if (!doc) {
255
+ return
256
+ }
257
+ doc.removeEventListener('pointermove', handleTouchMove)
258
+ doc.removeEventListener('pointerup', handleTouchEnd)
259
+ doc.removeEventListener('touchmove', handleTouchMove)
260
+ doc.removeEventListener('touchend', handleTouchEnd)
261
+ rootContext.pressedValuesRef.value = null
262
+ }
263
+
264
+ function handleTouchMove(nativeEvent: TouchEvent | PointerEvent) {
265
+ const fingerCoords = getFingerCoords(nativeEvent, touchIdRef)
266
+ if (fingerCoords == null) {
267
+ return
268
+ }
269
+
270
+ moveCountRef.value += 1
271
+
272
+ if (nativeEvent.type === 'pointermove' && (nativeEvent as PointerEvent).buttons === 0) {
273
+ handleTouchEnd(nativeEvent)
274
+ return
275
+ }
276
+
277
+ const finger = getFingerState(fingerCoords)
278
+ if (finger == null) {
279
+ return
280
+ }
281
+
282
+ if (validateMinimumDistance(finger.value, rootContext.step.value, rootContext.minStepsBetweenValues.value)) {
283
+ if (!rootContext.dragging.value && moveCountRef.value > INTENTIONAL_DRAG_COUNT_THRESHOLD) {
284
+ rootContext.setDragging(true)
285
+ }
286
+
287
+ rootContext.setValue(
288
+ finger.value,
289
+ createChangeEventDetails(REASONS.drag, nativeEvent, undefined, {
290
+ activeThumbIndex: finger.thumbIndex,
291
+ }),
292
+ )
293
+
294
+ latestValuesRef.value = Array.isArray(finger.value) ? finger.value : [finger.value]
295
+
296
+ if (finger.didSwap) {
297
+ focusThumb(finger.thumbIndex)
298
+ }
299
+ }
300
+ }
301
+
302
+ function handleTouchEnd(nativeEvent: TouchEvent | PointerEvent) {
303
+ rootContext.setActive(-1)
304
+ rootContext.setDragging(false)
305
+ rootContext.pressedInputRef.value = null
306
+ rootContext.pressedThumbCenterOffsetRef.value = null
307
+
308
+ const fingerCoords = getFingerCoords(nativeEvent, touchIdRef)
309
+ const finger = fingerCoords != null ? getFingerState(fingerCoords) : null
310
+
311
+ if (finger != null) {
312
+ const commitReason = rootContext.lastChangeReasonRef.value
313
+ rootContext.onValueCommitted(
314
+ rootContext.lastChangedValueRef.value ?? finger.value,
315
+ createGenericEventDetails(commitReason, nativeEvent),
316
+ )
317
+ }
318
+
319
+ if (
320
+ 'pointerId' in nativeEvent
321
+ && typeof controlRef.value?.hasPointerCapture === 'function'
322
+ && controlRef.value.hasPointerCapture(nativeEvent.pointerId)
323
+ && typeof controlRef.value.releasePointerCapture === 'function'
324
+ ) {
325
+ controlRef.value.releasePointerCapture(nativeEvent.pointerId)
326
+ }
327
+
328
+ rootContext.pressedThumbIndexRef.value = -1
329
+ touchIdRef.value = null
330
+ rootContext.pressedValuesRef.value = null
331
+ stopListening()
332
+ }
333
+
334
+ function handleTouchStart(nativeEvent: TouchEvent) {
335
+ if (rootContext.disabled.value) {
336
+ return
337
+ }
338
+
339
+ const touch = nativeEvent.changedTouches[0]
340
+ if (touch != null) {
341
+ touchIdRef.value = touch.identifier
342
+ }
343
+
344
+ const fingerCoords = getFingerCoords(nativeEvent, touchIdRef)
345
+ if (fingerCoords != null) {
346
+ startPressing(fingerCoords)
347
+
348
+ const finger = getFingerState(fingerCoords)
349
+ if (finger == null) {
350
+ return
351
+ }
352
+
353
+ focusThumb(finger.thumbIndex)
354
+ rootContext.setValue(
355
+ finger.value,
356
+ createChangeEventDetails(REASONS.trackPress, nativeEvent, undefined, {
357
+ activeThumbIndex: finger.thumbIndex,
358
+ }),
359
+ )
360
+
361
+ latestValuesRef.value = Array.isArray(finger.value) ? finger.value : [finger.value]
362
+
363
+ if (finger.didSwap) {
364
+ focusThumb(finger.thumbIndex)
365
+ }
366
+ }
367
+
368
+ moveCountRef.value = 0
369
+ const doc = ownerDocument(controlRef.value)
370
+ if (!doc) {
371
+ return
372
+ }
373
+ doc.addEventListener('touchmove', handleTouchMove, { passive: true })
374
+ doc.addEventListener('touchend', handleTouchEnd, { passive: true })
375
+ }
376
+
377
+ onMounted(() => {
378
+ const control = controlRef.value
379
+ if (!control) {
380
+ return
381
+ }
382
+
383
+ control.addEventListener('touchstart', handleTouchStart, { passive: true })
384
+ })
385
+
386
+ onBeforeUnmount(() => {
387
+ controlRef.value?.removeEventListener('touchstart', handleTouchStart)
388
+ focusFrame.cancel()
389
+ stopListening()
390
+ })
391
+
392
+ function mergedRef(node: Element | ComponentPublicInstance | null) {
393
+ const element = node instanceof HTMLElement ? node : null
394
+ controlRef.value = element
395
+ rootContext.registerFieldControlRef(element)
396
+ setStylesRef(element)
397
+ }
398
+
399
+ watchEffect(() => {
400
+ if (rootContext.disabled.value) {
401
+ stopListening()
402
+ }
403
+ })
404
+
405
+ const controlProps = computed(() => mergeProps(
406
+ attrs as Record<string, unknown>,
407
+ {
408
+ 'data-base-ui-slider-control': rootContext.renderBeforeHydration.value ? '' : undefined,
409
+ onPointerdown(event: PointerEvent) {
410
+ const control = controlRef.value
411
+ const target = getTarget(event)
412
+
413
+ if (
414
+ !control
415
+ || rootContext.disabled.value
416
+ || event.defaultPrevented
417
+ || !isElement(target)
418
+ || event.button !== 0
419
+ ) {
420
+ return
421
+ }
422
+
423
+ const fingerCoords = getFingerCoords(event, touchIdRef)
424
+
425
+ if (fingerCoords != null) {
426
+ startPressing(fingerCoords)
427
+ const finger = getFingerState(fingerCoords)
428
+
429
+ if (finger == null) {
430
+ return
431
+ }
432
+
433
+ const focusedElement = ownerDocument(control) ? activeElement(ownerDocument(control)!) : null
434
+ const pressedOnFocusedThumb = contains(
435
+ rootContext.thumbRefs.value[finger.thumbIndex],
436
+ focusedElement as Element | null,
437
+ )
438
+
439
+ if (pressedOnFocusedThumb) {
440
+ event.preventDefault()
441
+ }
442
+ else {
443
+ focusFrame.request(() => {
444
+ focusThumb(finger.thumbIndex)
445
+ })
446
+ }
447
+
448
+ rootContext.setDragging(true)
449
+
450
+ const pressedOnAnyThumb = rootContext.pressedThumbCenterOffsetRef.value != null
451
+ if (!pressedOnAnyThumb) {
452
+ rootContext.setValue(
453
+ finger.value,
454
+ createChangeEventDetails(REASONS.trackPress, event, undefined, {
455
+ activeThumbIndex: finger.thumbIndex,
456
+ }),
457
+ )
458
+
459
+ latestValuesRef.value = Array.isArray(finger.value) ? finger.value : [finger.value]
460
+
461
+ if (finger.didSwap) {
462
+ focusThumb(finger.thumbIndex)
463
+ }
464
+ }
465
+ }
466
+
467
+ if (event.pointerId && typeof control.setPointerCapture === 'function') {
468
+ control.setPointerCapture(event.pointerId)
469
+ }
470
+
471
+ moveCountRef.value = 0
472
+ const doc = ownerDocument(control)
473
+ if (!doc) {
474
+ return
475
+ }
476
+ doc.addEventListener('pointermove', handleTouchMove, { passive: true })
477
+ doc.addEventListener('pointerup', handleTouchEnd, { once: true })
478
+ },
479
+ },
480
+ ))
481
+
482
+ const { tag, mergedProps, renderless, ref: renderRef } = useRenderElement({
483
+ componentProps: props,
484
+ state: rootContext.state,
485
+ props: controlProps,
486
+ defaultTagName: 'div',
487
+ ref: mergedRef,
488
+ stateAttributesMapping: sliderStateAttributesMapping,
489
+ })
490
+ </script>
491
+
492
+ <template>
493
+ <slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="rootContext.state" />
494
+ <component :is="tag" v-else :ref="renderRef" v-bind="mergedProps">
495
+ <slot />
496
+ </component>
497
+ </template>
@@ -0,0 +1,35 @@
1
+ export enum SliderControlDataAttributes {
2
+ /**
3
+ * Present while the user is dragging.
4
+ */
5
+ dragging = 'data-dragging',
6
+ /**
7
+ * Indicates the orientation of the slider.
8
+ * @type {'horizontal' | 'vertical'}
9
+ */
10
+ orientation = 'data-orientation',
11
+ /**
12
+ * Present when the slider is disabled.
13
+ */
14
+ disabled = 'data-disabled',
15
+ /**
16
+ * Present when the slider is in valid state (when wrapped in Field.Root).
17
+ */
18
+ valid = 'data-valid',
19
+ /**
20
+ * Present when the slider is in invalid state (when wrapped in Field.Root).
21
+ */
22
+ invalid = 'data-invalid',
23
+ /**
24
+ * Present when the slider has been touched (when wrapped in Field.Root).
25
+ */
26
+ touched = 'data-touched',
27
+ /**
28
+ * Present when the slider's value has changed (when wrapped in Field.Root).
29
+ */
30
+ dirty = 'data-dirty',
31
+ /**
32
+ * Present when the slider is focused (when wrapped in Field.Root).
33
+ */
34
+ focused = 'data-focused',
35
+ }
@@ -0,0 +1,35 @@
1
+ export { default as SliderControl } from './control/SliderControl.vue'
2
+ export type { SliderControlProps, SliderControlState } from './control/SliderControl.vue'
3
+ export { SliderControlDataAttributes } from './control/SliderControlDataAttributes'
4
+ export { default as SliderIndicator } from './indicator/SliderIndicator.vue'
5
+ export type { SliderIndicatorProps, SliderIndicatorState } from './indicator/SliderIndicator.vue'
6
+
7
+ export { SliderIndicatorDataAttributes } from './indicator/SliderIndicatorDataAttributes'
8
+ export { default as SliderLabel } from './label/SliderLabel.vue'
9
+ export type { SliderLabelProps, SliderLabelState } from './label/SliderLabel.vue'
10
+
11
+ export { default as SliderRoot } from './root/SliderRoot.vue'
12
+ export type {
13
+ SliderRootChangeEventDetails,
14
+ SliderRootChangeEventReason,
15
+ SliderRootCommitEventDetails,
16
+ SliderRootCommitEventReason,
17
+ SliderRootProps,
18
+ SliderRootState,
19
+ } from './root/SliderRoot.vue'
20
+ export { sliderRootContextKey, useSliderRootContext } from './root/SliderRootContext'
21
+
22
+ export type { SliderRootContext } from './root/SliderRootContext'
23
+ export { SliderRootDataAttributes } from './root/SliderRootDataAttributes'
24
+ export { default as SliderThumb } from './thumb/SliderThumb.vue'
25
+
26
+ export type { SliderThumbProps, SliderThumbState, ThumbMetadata } from './thumb/SliderThumb.vue'
27
+ export { SliderThumbDataAttributes } from './thumb/SliderThumbDataAttributes'
28
+ export { default as SliderTrack } from './track/SliderTrack.vue'
29
+
30
+ export type { SliderTrackProps, SliderTrackState } from './track/SliderTrack.vue'
31
+ export { SliderTrackDataAttributes } from './track/SliderTrackDataAttributes'
32
+ export { default as SliderValue } from './value/SliderValue.vue'
33
+
34
+ export type { SliderValueProps, SliderValueState } from './value/SliderValue.vue'
35
+ export { SliderValueDataAttributes } from './value/SliderValueDataAttributes'