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,263 @@
1
+ <script setup lang="ts">
2
+ import type { ComponentPublicInstance } from 'vue'
3
+ import type { BaseUIComponentProps } from '../../utils/types'
4
+ import type { ScrollAreaRootState } from '../root/ScrollAreaRootContext'
5
+ import { computed, onBeforeUnmount, provide, shallowRef, useAttrs, watch } from 'vue'
6
+ import { useDirection } from '../../direction-provider/DirectionContext'
7
+ import { contains, getTarget } from '../../floating-ui-vue/utils/shadowDom'
8
+ import { mergeProps } from '../../merge-props/mergeProps'
9
+ import { useRenderElement } from '../../utils/useRenderElement'
10
+ import { useScrollAreaRootContext } from '../root/ScrollAreaRootContext'
11
+ import { ScrollAreaRootCssVars } from '../root/ScrollAreaRootCssVars'
12
+ import { scrollAreaStateAttributesMapping } from '../root/stateAttributes'
13
+ import { getOffset } from '../utils/getOffset'
14
+ import { scrollAreaScrollbarContextKey } from './ScrollAreaScrollbarContext'
15
+ import { ScrollAreaScrollbarCssVars } from './ScrollAreaScrollbarCssVars'
16
+
17
+ export interface ScrollAreaScrollbarState extends ScrollAreaRootState {
18
+ hovering: boolean
19
+ scrolling: boolean
20
+ orientation: 'vertical' | 'horizontal'
21
+ }
22
+
23
+ export interface ScrollAreaScrollbarProps extends BaseUIComponentProps<ScrollAreaScrollbarState> {
24
+ orientation?: 'vertical' | 'horizontal'
25
+ keepMounted?: boolean
26
+ }
27
+
28
+ defineOptions({
29
+ name: 'ScrollAreaScrollbar',
30
+ inheritAttrs: false,
31
+ })
32
+
33
+ const props = withDefaults(defineProps<ScrollAreaScrollbarProps>(), {
34
+ as: 'div',
35
+ orientation: 'vertical',
36
+ keepMounted: false,
37
+ })
38
+
39
+ const attrs = useAttrs()
40
+
41
+ const {
42
+ hovering,
43
+ scrollingX,
44
+ scrollingY,
45
+ hiddenState,
46
+ overflowEdges,
47
+ scrollbarYRef,
48
+ scrollbarXRef,
49
+ viewportRef,
50
+ thumbYRef,
51
+ thumbXRef,
52
+ handlePointerDown,
53
+ handlePointerUp,
54
+ rootId,
55
+ thumbSize,
56
+ hasMeasuredScrollbar,
57
+ } = useScrollAreaRootContext()
58
+
59
+ const direction = useDirection()
60
+
61
+ let wheelCleanup: (() => void) | undefined
62
+ const scrollbarElementRef = shallowRef<HTMLDivElement | null>(null)
63
+
64
+ watch(
65
+ [scrollbarElementRef, () => props.orientation],
66
+ ([element, orientation], [previousElement, previousOrientation]) => {
67
+ if (previousElement) {
68
+ const previousRef = previousOrientation === 'vertical' ? scrollbarYRef : scrollbarXRef
69
+
70
+ if (previousRef.value === previousElement) {
71
+ previousRef.value = null
72
+ }
73
+ }
74
+
75
+ if (!element) {
76
+ return
77
+ }
78
+
79
+ const nextRef = orientation === 'vertical' ? scrollbarYRef : scrollbarXRef
80
+ nextRef.value = element
81
+ },
82
+ )
83
+
84
+ watch(
85
+ [scrollbarElementRef, viewportRef, () => props.orientation],
86
+ () => {
87
+ wheelCleanup?.()
88
+ wheelCleanup = setupWheelHandler()
89
+ },
90
+ { flush: 'post' },
91
+ )
92
+
93
+ onBeforeUnmount(() => {
94
+ wheelCleanup?.()
95
+ })
96
+
97
+ function setupWheelHandler() {
98
+ const viewportEl = viewportRef.value
99
+ const scrollbarEl = scrollbarElementRef.value
100
+
101
+ if (!scrollbarEl)
102
+ return undefined
103
+
104
+ function handleWheel(event: WheelEvent) {
105
+ if (!viewportEl || !scrollbarEl || event.ctrlKey)
106
+ return
107
+
108
+ if (props.orientation === 'vertical') {
109
+ if (viewportEl.scrollTop === 0 && event.deltaY < 0)
110
+ return
111
+ if (viewportEl.scrollTop === viewportEl.scrollHeight - viewportEl.clientHeight && event.deltaY > 0)
112
+ return
113
+ event.preventDefault()
114
+ viewportEl.scrollTop += event.deltaY
115
+ }
116
+ else {
117
+ if (viewportEl.scrollLeft === 0 && event.deltaX < 0)
118
+ return
119
+ if (viewportEl.scrollLeft === viewportEl.scrollWidth - viewportEl.clientWidth && event.deltaX > 0)
120
+ return
121
+ event.preventDefault()
122
+ viewportEl.scrollLeft += event.deltaX
123
+ }
124
+ }
125
+
126
+ scrollbarEl.addEventListener('wheel', handleWheel, { passive: false })
127
+ return () => scrollbarEl.removeEventListener('wheel', handleWheel)
128
+ }
129
+
130
+ function setScrollbarElement(element: Element | ComponentPublicInstance | null) {
131
+ scrollbarElementRef.value = element as HTMLDivElement | null
132
+ }
133
+
134
+ const state = computed<ScrollAreaScrollbarState>(() => ({
135
+ hovering: hovering.value,
136
+ scrolling: props.orientation === 'horizontal' ? scrollingX.value : scrollingY.value,
137
+ orientation: props.orientation,
138
+ hasOverflowX: !hiddenState.value.x,
139
+ hasOverflowY: !hiddenState.value.y,
140
+ overflowXStart: overflowEdges.value.xStart,
141
+ overflowXEnd: overflowEdges.value.xEnd,
142
+ overflowYStart: overflowEdges.value.yStart,
143
+ overflowYEnd: overflowEdges.value.yEnd,
144
+ cornerHidden: hiddenState.value.corner,
145
+ }))
146
+
147
+ const hideTrackUntilMeasured = computed(() => !hasMeasuredScrollbar.value && !props.keepMounted)
148
+
149
+ function onPointerDown(event: PointerEvent) {
150
+ if (event.button !== 0)
151
+ return
152
+
153
+ const target = getTarget(event) as Element | null
154
+ const thumb = props.orientation === 'vertical' ? thumbYRef.value : thumbXRef.value
155
+
156
+ if (thumb && contains(thumb, target))
157
+ return
158
+ if (!viewportRef.value)
159
+ return
160
+
161
+ if (thumbYRef.value && scrollbarYRef.value && props.orientation === 'vertical') {
162
+ const thumbYOffset = getOffset(thumbYRef.value, 'margin', 'y')
163
+ const scrollbarYOffset = getOffset(scrollbarYRef.value, 'padding', 'y')
164
+ const thumbHeight = thumbYRef.value.offsetHeight
165
+ const trackRectY = scrollbarYRef.value.getBoundingClientRect()
166
+ const clickY = event.clientY - trackRectY.top - thumbHeight / 2 - scrollbarYOffset + thumbYOffset / 2
167
+ const scrollableContentHeight = viewportRef.value.scrollHeight
168
+ const viewportHeight = viewportRef.value.clientHeight
169
+ const maxThumbOffsetY = scrollbarYRef.value.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset
170
+ const scrollRatioY = clickY / maxThumbOffsetY
171
+ viewportRef.value.scrollTop = scrollRatioY * (scrollableContentHeight - viewportHeight)
172
+ }
173
+
174
+ if (thumbXRef.value && scrollbarXRef.value && props.orientation === 'horizontal') {
175
+ const thumbXOffset = getOffset(thumbXRef.value, 'margin', 'x')
176
+ const scrollbarXOffset = getOffset(scrollbarXRef.value, 'padding', 'x')
177
+ const thumbWidth = thumbXRef.value.offsetWidth
178
+ const trackRectX = scrollbarXRef.value.getBoundingClientRect()
179
+ const clickX = event.clientX - trackRectX.left - thumbWidth / 2 - scrollbarXOffset + thumbXOffset / 2
180
+ const scrollableContentWidth = viewportRef.value.scrollWidth
181
+ const viewportWidth = viewportRef.value.clientWidth
182
+ const maxThumbOffsetX = scrollbarXRef.value.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset
183
+ const scrollRatioX = clickX / maxThumbOffsetX
184
+
185
+ let newScrollLeft: number
186
+ if (direction.value === 'rtl') {
187
+ newScrollLeft = (1 - scrollRatioX) * (scrollableContentWidth - viewportWidth)
188
+ if (viewportRef.value.scrollLeft <= 0) {
189
+ newScrollLeft = -newScrollLeft
190
+ }
191
+ }
192
+ else {
193
+ newScrollLeft = scrollRatioX * (scrollableContentWidth - viewportWidth)
194
+ }
195
+ viewportRef.value.scrollLeft = newScrollLeft
196
+ }
197
+
198
+ handlePointerDown(event)
199
+ }
200
+
201
+ const elementProps = computed(() => mergeProps(
202
+ attrs as Record<string, any>,
203
+ {
204
+ ...(rootId ? { 'data-id': `${rootId}-scrollbar` } : {}),
205
+ onPointerdown: onPointerDown,
206
+ onPointerup: handlePointerUp,
207
+ style: {
208
+ position: 'absolute',
209
+ touchAction: 'none',
210
+ WebkitUserSelect: 'none',
211
+ userSelect: 'none',
212
+ visibility: hideTrackUntilMeasured.value ? 'hidden' : undefined,
213
+ ...(props.orientation === 'vertical' && {
214
+ top: 0,
215
+ bottom: `var(${ScrollAreaRootCssVars.scrollAreaCornerHeight})`,
216
+ insetInlineEnd: 0,
217
+ [ScrollAreaScrollbarCssVars.scrollAreaThumbHeight]: `${thumbSize.value.height}px`,
218
+ }),
219
+ ...(props.orientation === 'horizontal' && {
220
+ insetInlineStart: 0,
221
+ insetInlineEnd: `var(${ScrollAreaRootCssVars.scrollAreaCornerWidth})`,
222
+ bottom: 0,
223
+ [ScrollAreaScrollbarCssVars.scrollAreaThumbWidth]: `${thumbSize.value.width}px`,
224
+ }),
225
+ },
226
+ },
227
+ ))
228
+
229
+ const {
230
+ tag,
231
+ mergedProps,
232
+ renderless,
233
+ ref: renderRef,
234
+ } = useRenderElement({
235
+ componentProps: props,
236
+ state,
237
+ props: elementProps,
238
+ stateAttributesMapping: scrollAreaStateAttributesMapping,
239
+ defaultTagName: 'div',
240
+ ref: setScrollbarElement,
241
+ })
242
+
243
+ const isHidden = computed(() => props.orientation === 'vertical' ? hiddenState.value.y : hiddenState.value.x)
244
+ const shouldRender = computed(() => props.keepMounted || !isHidden.value)
245
+
246
+ const orientation = computed(() => props.orientation)
247
+
248
+ provide(scrollAreaScrollbarContextKey, { orientation })
249
+ </script>
250
+
251
+ <template>
252
+ <template v-if="shouldRender">
253
+ <slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="state" />
254
+ <component
255
+ :is="tag"
256
+ v-else
257
+ :ref="renderRef"
258
+ v-bind="mergedProps"
259
+ >
260
+ <slot />
261
+ </component>
262
+ </template>
263
+ </template>
@@ -0,0 +1,20 @@
1
+ import type { ComputedRef, InjectionKey } from 'vue'
2
+ import { inject } from 'vue'
3
+
4
+ export interface ScrollAreaScrollbarContext {
5
+ orientation: ComputedRef<'horizontal' | 'vertical'>
6
+ }
7
+
8
+ export const scrollAreaScrollbarContextKey: InjectionKey<ScrollAreaScrollbarContext> = Symbol(
9
+ 'ScrollAreaScrollbarContext',
10
+ )
11
+
12
+ export function useScrollAreaScrollbarContext(): ScrollAreaScrollbarContext {
13
+ const context = inject(scrollAreaScrollbarContextKey)
14
+ if (context === undefined) {
15
+ throw new Error(
16
+ 'Base UI: ScrollAreaScrollbarContext is missing. ScrollArea.Thumb must be placed within <ScrollAreaScrollbar>.',
17
+ )
18
+ }
19
+ return context
20
+ }
@@ -0,0 +1,4 @@
1
+ export enum ScrollAreaScrollbarCssVars {
2
+ scrollAreaThumbHeight = '--scroll-area-thumb-height',
3
+ scrollAreaThumbWidth = '--scroll-area-thumb-width',
4
+ }
@@ -0,0 +1,11 @@
1
+ export enum ScrollAreaScrollbarDataAttributes {
2
+ orientation = 'data-orientation',
3
+ hovering = 'data-hovering',
4
+ scrolling = 'data-scrolling',
5
+ hasOverflowX = 'data-has-overflow-x',
6
+ hasOverflowY = 'data-has-overflow-y',
7
+ overflowXStart = 'data-overflow-x-start',
8
+ overflowXEnd = 'data-overflow-x-end',
9
+ overflowYStart = 'data-overflow-y-start',
10
+ overflowYEnd = 'data-overflow-y-end',
11
+ }
@@ -0,0 +1,120 @@
1
+ <script setup lang="ts">
2
+ import type { ComponentPublicInstance } from 'vue'
3
+ import type { BaseUIComponentProps } from '../../utils/types'
4
+ import { computed, shallowRef, useAttrs, watch } from 'vue'
5
+ import { mergeProps } from '../../merge-props/mergeProps'
6
+ import { useRenderElement } from '../../utils/useRenderElement'
7
+ import { useScrollAreaRootContext } from '../root/ScrollAreaRootContext'
8
+ import { useScrollAreaScrollbarContext } from '../scrollbar/ScrollAreaScrollbarContext'
9
+ import { ScrollAreaScrollbarCssVars } from '../scrollbar/ScrollAreaScrollbarCssVars'
10
+
11
+ export interface ScrollAreaThumbState {
12
+ orientation?: 'horizontal' | 'vertical'
13
+ }
14
+
15
+ export interface ScrollAreaThumbProps extends BaseUIComponentProps<ScrollAreaThumbState> {}
16
+
17
+ defineOptions({
18
+ name: 'ScrollAreaThumb',
19
+ inheritAttrs: false,
20
+ })
21
+
22
+ const props = withDefaults(defineProps<ScrollAreaThumbProps>(), {
23
+ as: 'div',
24
+ })
25
+
26
+ const attrs = useAttrs()
27
+
28
+ const {
29
+ thumbYRef,
30
+ thumbXRef,
31
+ handlePointerDown,
32
+ handlePointerMove,
33
+ handlePointerUp,
34
+ setScrollingX,
35
+ setScrollingY,
36
+ hasMeasuredScrollbar,
37
+ } = useScrollAreaRootContext()
38
+
39
+ const { orientation } = useScrollAreaScrollbarContext()
40
+ const thumbElementRef = shallowRef<HTMLDivElement | null>(null)
41
+
42
+ watch(
43
+ [thumbElementRef, orientation],
44
+ ([element, currentOrientation], [previousElement, previousOrientation]) => {
45
+ if (previousElement) {
46
+ const previousRef = previousOrientation === 'vertical' ? thumbYRef : thumbXRef
47
+
48
+ if (previousRef.value === previousElement) {
49
+ previousRef.value = null
50
+ }
51
+ }
52
+
53
+ if (!element) {
54
+ return
55
+ }
56
+
57
+ const nextRef = currentOrientation === 'vertical' ? thumbYRef : thumbXRef
58
+ nextRef.value = element
59
+ },
60
+ )
61
+
62
+ const state = computed<ScrollAreaThumbState>(() => ({
63
+ orientation: orientation.value,
64
+ }))
65
+
66
+ function onPointerUp(event: PointerEvent) {
67
+ if (orientation.value === 'vertical') {
68
+ setScrollingY(false)
69
+ }
70
+ if (orientation.value === 'horizontal') {
71
+ setScrollingX(false)
72
+ }
73
+ handlePointerUp(event)
74
+ }
75
+
76
+ function setThumbElement(element: Element | ComponentPublicInstance | null) {
77
+ thumbElementRef.value = element as HTMLDivElement | null
78
+ }
79
+
80
+ const elementProps = computed(() => mergeProps(
81
+ attrs,
82
+ {
83
+ onPointerdown: handlePointerDown,
84
+ onPointermove: handlePointerMove,
85
+ onPointerup: onPointerUp,
86
+ style: {
87
+ visibility: hasMeasuredScrollbar.value ? undefined : 'hidden',
88
+ ...(orientation.value === 'vertical' && {
89
+ height: `var(${ScrollAreaScrollbarCssVars.scrollAreaThumbHeight})`,
90
+ }),
91
+ ...(orientation.value === 'horizontal' && {
92
+ width: `var(${ScrollAreaScrollbarCssVars.scrollAreaThumbWidth})`,
93
+ }),
94
+ },
95
+ },
96
+ ))
97
+
98
+ const {
99
+ tag,
100
+ mergedProps,
101
+ renderless,
102
+ ref: renderRef,
103
+ } = useRenderElement({
104
+ componentProps: props,
105
+ state,
106
+ props: elementProps,
107
+ defaultTagName: 'div',
108
+ ref: setThumbElement,
109
+ })
110
+ </script>
111
+
112
+ <template>
113
+ <slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="state" />
114
+ <component
115
+ :is="tag"
116
+ v-else
117
+ :ref="renderRef"
118
+ v-bind="mergedProps"
119
+ />
120
+ </template>
@@ -0,0 +1,3 @@
1
+ export enum ScrollAreaThumbDataAttributes {
2
+ orientation = 'data-orientation',
3
+ }
@@ -0,0 +1,34 @@
1
+ export function getOffset(
2
+ element: Element | null,
3
+ prop: 'margin' | 'padding',
4
+ axis: 'x' | 'y',
5
+ ): number {
6
+ if (!element) {
7
+ return 0
8
+ }
9
+
10
+ const styles = getComputedStyle(element)
11
+ const propAxis = axis === 'x' ? 'Inline' : 'Block'
12
+ const start = getStyleNumber(styles, `${prop}${propAxis}Start`)
13
+ const end = getStyleNumber(styles, `${prop}${propAxis}End`)
14
+
15
+ // Safari misreports `marginInlineEnd` in RTL.
16
+ // We have to assume the start/end values are symmetrical, which is likely.
17
+ if (axis === 'x' && prop === 'margin' && styles.direction === 'rtl' && isSafari()) {
18
+ return start * 2
19
+ }
20
+
21
+ return start + end
22
+ }
23
+
24
+ function getStyleNumber(styles: CSSStyleDeclaration, prop: string) {
25
+ return Number.parseFloat(styles[prop as any]) || 0
26
+ }
27
+
28
+ function isSafari() {
29
+ if (typeof navigator === 'undefined') {
30
+ return false
31
+ }
32
+
33
+ return /AppleWebKit/.test(navigator.userAgent) && !/Chrome|Chromium|Edg\//.test(navigator.userAgent)
34
+ }