base-ui-vue 0.1.0 → 0.3.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 (253) 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/content/ScrollAreaContent.cjs +168 -0
  35. package/dist/content/ScrollAreaContent.cjs.map +1 -0
  36. package/dist/content/ScrollAreaContent.js +133 -0
  37. package/dist/content/ScrollAreaContent.js.map +1 -0
  38. package/dist/control/FieldControl.cjs +18 -343
  39. package/dist/control/FieldControl.cjs.map +1 -1
  40. package/dist/control/FieldControl.js +14 -285
  41. package/dist/control/FieldControl.js.map +1 -1
  42. package/dist/control/SliderControl.cjs +636 -0
  43. package/dist/control/SliderControl.cjs.map +1 -0
  44. package/dist/control/SliderControl.js +553 -0
  45. package/dist/control/SliderControl.js.map +1 -0
  46. package/dist/control/SliderControlDataAttributes.cjs +47 -0
  47. package/dist/control/SliderControlDataAttributes.cjs.map +1 -0
  48. package/dist/control/SliderControlDataAttributes.js +41 -0
  49. package/dist/control/SliderControlDataAttributes.js.map +1 -0
  50. package/dist/corner/ScrollAreaCorner.cjs +77 -0
  51. package/dist/corner/ScrollAreaCorner.cjs.map +1 -0
  52. package/dist/corner/ScrollAreaCorner.js +72 -0
  53. package/dist/corner/ScrollAreaCorner.js.map +1 -0
  54. package/dist/csp-provider/CSPContext.cjs +32 -0
  55. package/dist/csp-provider/CSPContext.cjs.map +1 -0
  56. package/dist/csp-provider/CSPContext.js +21 -0
  57. package/dist/csp-provider/CSPContext.js.map +1 -0
  58. package/dist/csp-provider/CSPProvider.cjs +46 -0
  59. package/dist/csp-provider/CSPProvider.cjs.map +1 -0
  60. package/dist/csp-provider/CSPProvider.js +41 -0
  61. package/dist/csp-provider/CSPProvider.js.map +1 -0
  62. package/dist/description/FieldDescription.cjs +5 -5
  63. package/dist/description/FieldDescription.cjs.map +1 -1
  64. package/dist/description/FieldDescription.js +1 -1
  65. package/dist/direction-provider/DirectionProvider.cjs +2 -2
  66. package/dist/direction-provider/DirectionProvider.cjs.map +1 -1
  67. package/dist/direction-provider/DirectionProvider.js +1 -1
  68. package/dist/error/FieldError.cjs +10 -288
  69. package/dist/error/FieldError.cjs.map +1 -1
  70. package/dist/error/FieldError.js +4 -246
  71. package/dist/error/FieldError.js.map +1 -1
  72. package/dist/form/Form.cjs +5 -4
  73. package/dist/form/Form.cjs.map +1 -1
  74. package/dist/form/Form.js +5 -4
  75. package/dist/form/Form.js.map +1 -1
  76. package/dist/group/ToolbarGroup.cjs +92 -0
  77. package/dist/group/ToolbarGroup.cjs.map +1 -0
  78. package/dist/group/ToolbarGroup.js +87 -0
  79. package/dist/group/ToolbarGroup.js.map +1 -0
  80. package/dist/group/ToolbarGroupDataAttributes.cjs +23 -0
  81. package/dist/group/ToolbarGroupDataAttributes.cjs.map +1 -0
  82. package/dist/group/ToolbarGroupDataAttributes.js +17 -0
  83. package/dist/group/ToolbarGroupDataAttributes.js.map +1 -0
  84. package/dist/header/AccordionHeader.cjs +2 -2
  85. package/dist/header/AccordionHeader.js +1 -1
  86. package/dist/image/AvatarImage.cjs +4 -4
  87. package/dist/image/AvatarImage.cjs.map +1 -1
  88. package/dist/image/AvatarImage.js +1 -1
  89. package/dist/index.cjs +113 -10
  90. package/dist/index.d.cts +3457 -603
  91. package/dist/index.d.cts.map +1 -1
  92. package/dist/index.d.ts +3457 -603
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +17 -5
  95. package/dist/index2.cjs +5897 -424
  96. package/dist/index2.cjs.map +1 -1
  97. package/dist/index2.js +5389 -270
  98. package/dist/index2.js.map +1 -1
  99. package/package.json +8 -4
  100. package/src/accordion/root/AccordionRoot.vue +2 -1
  101. package/src/checkbox/index.ts +23 -0
  102. package/src/checkbox/indicator/CheckboxIndicator.vue +102 -0
  103. package/src/checkbox/indicator/CheckboxIndicatorDataAttributes.ts +61 -0
  104. package/src/checkbox/root/CheckboxRoot.vue +632 -0
  105. package/src/checkbox/root/CheckboxRootContext.ts +22 -0
  106. package/src/checkbox/root/CheckboxRootDataAttributes.ts +54 -0
  107. package/src/checkbox/utils/useStateAttributesMapping.ts +30 -0
  108. package/src/checkbox-group/CheckboxGroup.vue +241 -0
  109. package/src/checkbox-group/CheckboxGroupContext.ts +39 -0
  110. package/src/checkbox-group/CheckboxGroupDataAttributes.ts +6 -0
  111. package/src/checkbox-group/index.ts +11 -0
  112. package/src/checkbox-group/useCheckboxGroupParent.ts +173 -0
  113. package/src/collapsible/panel/useCollapsiblePanel.ts +2 -1
  114. package/src/collapsible/root/useCollapsibleRoot.ts +3 -1
  115. package/src/composite/composite.ts +2 -0
  116. package/src/composite/item/CompositeItem.vue +7 -8
  117. package/src/composite/root/CompositeRoot.vue +12 -1
  118. package/src/csp-provider/CSPContext.ts +26 -0
  119. package/src/csp-provider/CSPProvider.vue +40 -0
  120. package/src/csp-provider/index.ts +5 -0
  121. package/src/field/item/FieldItem.vue +6 -1
  122. package/src/field/label/FieldLabel.vue +10 -51
  123. package/src/field/root/FieldRoot.vue +16 -3
  124. package/src/floating-ui-vue/types.ts +1 -4
  125. package/src/floating-ui-vue/utils/element.ts +12 -0
  126. package/src/floating-ui-vue/utils/shadowDom.ts +44 -0
  127. package/src/floating-ui-vue/utils.ts +3 -0
  128. package/src/form/Form.vue +5 -3
  129. package/src/index.ts +13 -0
  130. package/src/input/Input.vue +37 -0
  131. package/src/input/InputDataAttributes.ts +30 -0
  132. package/src/input/index.ts +4 -0
  133. package/src/labelable-provider/LabelableContext.ts +2 -2
  134. package/src/labelable-provider/LabelableProvider.vue +21 -4
  135. package/src/labelable-provider/index.ts +2 -0
  136. package/src/labelable-provider/useAriaLabelledBy.ts +9 -9
  137. package/src/labelable-provider/useLabel.ts +115 -0
  138. package/src/labelable-provider/useLabelableId.ts +12 -10
  139. package/src/meter/index.ts +16 -0
  140. package/src/meter/indicator/MeterIndicator.vue +65 -0
  141. package/src/meter/label/MeterLabel.vue +63 -0
  142. package/src/meter/root/MeterRoot.vue +131 -0
  143. package/src/meter/root/MeterRootContext.ts +41 -0
  144. package/src/meter/track/MeterTrack.vue +46 -0
  145. package/src/meter/value/MeterValue.vue +85 -0
  146. package/src/progress/index.ts +23 -0
  147. package/src/progress/indicator/ProgressIndicator.vue +74 -0
  148. package/src/progress/label/ProgressLabel.vue +63 -0
  149. package/src/progress/root/ProgressRoot.vue +160 -0
  150. package/src/progress/root/ProgressRootContext.ts +51 -0
  151. package/src/progress/root/ProgressRootDataAttributes.ts +14 -0
  152. package/src/progress/root/stateAttributesMapping.ts +18 -0
  153. package/src/progress/track/ProgressTrack.vue +48 -0
  154. package/src/progress/value/ProgressValue.vue +92 -0
  155. package/src/scroll-area/constants.ts +2 -0
  156. package/src/scroll-area/content/ScrollAreaContent.vue +87 -0
  157. package/src/scroll-area/corner/ScrollAreaCorner.vue +64 -0
  158. package/src/scroll-area/index.ts +25 -0
  159. package/src/scroll-area/root/ScrollAreaRoot.vue +297 -0
  160. package/src/scroll-area/root/ScrollAreaRootContext.ts +89 -0
  161. package/src/scroll-area/root/ScrollAreaRootCssVars.ts +4 -0
  162. package/src/scroll-area/root/ScrollAreaRootDataAttributes.ts +9 -0
  163. package/src/scroll-area/root/stateAttributes.ts +14 -0
  164. package/src/scroll-area/scrollbar/ScrollAreaScrollbar.vue +263 -0
  165. package/src/scroll-area/scrollbar/ScrollAreaScrollbarContext.ts +20 -0
  166. package/src/scroll-area/scrollbar/ScrollAreaScrollbarCssVars.ts +4 -0
  167. package/src/scroll-area/scrollbar/ScrollAreaScrollbarDataAttributes.ts +11 -0
  168. package/src/scroll-area/thumb/ScrollAreaThumb.vue +120 -0
  169. package/src/scroll-area/thumb/ScrollAreaThumbDataAttributes.ts +3 -0
  170. package/src/scroll-area/utils/getOffset.ts +34 -0
  171. package/src/scroll-area/viewport/ScrollAreaViewport.vue +379 -0
  172. package/src/scroll-area/viewport/ScrollAreaViewportContext.ts +20 -0
  173. package/src/scroll-area/viewport/ScrollAreaViewportCssVars.ts +6 -0
  174. package/src/scroll-area/viewport/ScrollAreaViewportDataAttributes.ts +9 -0
  175. package/src/separator/Separator.vue +65 -0
  176. package/src/separator/SeparatorDataAttributes.ts +7 -0
  177. package/src/separator/index.ts +3 -0
  178. package/src/slider/control/SliderControl.vue +497 -0
  179. package/src/slider/control/SliderControlDataAttributes.ts +35 -0
  180. package/src/slider/index.ts +35 -0
  181. package/src/slider/indicator/SliderIndicator.vue +144 -0
  182. package/src/slider/indicator/SliderIndicatorDataAttributes.ts +35 -0
  183. package/src/slider/label/SliderLabel.vue +75 -0
  184. package/src/slider/root/SliderRoot.vue +557 -0
  185. package/src/slider/root/SliderRootContext.ts +126 -0
  186. package/src/slider/root/SliderRootDataAttributes.ts +35 -0
  187. package/src/slider/root/stateAttributesMapping.ts +13 -0
  188. package/src/slider/thumb/SliderThumb.vue +601 -0
  189. package/src/slider/thumb/SliderThumbDataAttributes.ts +39 -0
  190. package/src/slider/thumb/prehydrationScript.min.ts +5 -0
  191. package/src/slider/thumb/prehydrationScript.template.js +69 -0
  192. package/src/slider/track/SliderTrack.vue +48 -0
  193. package/src/slider/track/SliderTrackDataAttributes.ts +10 -0
  194. package/src/slider/utils/asc.ts +3 -0
  195. package/src/slider/utils/getMidpoint.ts +9 -0
  196. package/src/slider/utils/getPushedThumbValues.ts +68 -0
  197. package/src/slider/utils/getSliderValue.ts +25 -0
  198. package/src/slider/utils/replaceArrayItemAtIndex.ts +15 -0
  199. package/src/slider/utils/resolveThumbCollision.ts +177 -0
  200. package/src/slider/utils/roundValueToStep.ts +19 -0
  201. package/src/slider/utils/test-utils.ts +25 -0
  202. package/src/slider/utils/validateMinimumDistance.ts +20 -0
  203. package/src/slider/utils/valueArrayToPercentages.ts +10 -0
  204. package/src/slider/value/SliderValue.vue +90 -0
  205. package/src/slider/value/SliderValueDataAttributes.ts +35 -0
  206. package/src/switch/index.ts +14 -0
  207. package/src/switch/root/SwitchRoot.vue +448 -0
  208. package/src/switch/root/SwitchRootContext.ts +22 -0
  209. package/src/switch/root/SwitchRootDataAttributes.ts +46 -0
  210. package/src/switch/stateAttributesMapping.ts +23 -0
  211. package/src/switch/thumb/SwitchThumb.vue +59 -0
  212. package/src/switch/thumb/SwitchThumbDataAttributes.ts +46 -0
  213. package/src/toggle/Toggle.vue +211 -0
  214. package/src/toggle/ToggleDataAttributes.ts +6 -0
  215. package/src/toggle/index.ts +3 -0
  216. package/src/toggle-group/ToggleGroup.vue +224 -0
  217. package/src/toggle-group/ToggleGroupContext.ts +45 -0
  218. package/src/toggle-group/ToggleGroupDataAttributes.ts +15 -0
  219. package/src/toggle-group/index.ts +5 -0
  220. package/src/toolbar/button/ToolbarButton.vue +99 -0
  221. package/src/toolbar/button/ToolbarButtonDataAttributes.ts +15 -0
  222. package/src/toolbar/group/ToolbarGroup.vue +70 -0
  223. package/src/toolbar/group/ToolbarGroupContext.ts +23 -0
  224. package/src/toolbar/group/ToolbarGroupDataAttributes.ts +11 -0
  225. package/src/toolbar/index.ts +27 -0
  226. package/src/toolbar/input/ToolbarInput.vue +114 -0
  227. package/src/toolbar/input/ToolbarInputDataAttributes.ts +15 -0
  228. package/src/toolbar/link/ToolbarLink.vue +54 -0
  229. package/src/toolbar/link/ToolbarLinkDataAttributes.ts +7 -0
  230. package/src/toolbar/root/ToolbarRoot.vue +144 -0
  231. package/src/toolbar/root/ToolbarRootContext.ts +29 -0
  232. package/src/toolbar/root/ToolbarRootDataAttributes.ts +11 -0
  233. package/src/toolbar/separator/ToolbarSeparator.vue +41 -0
  234. package/src/toolbar/separator/ToolbarSeparatorDataAttributes.ts +7 -0
  235. package/src/use-button/useButton.ts +2 -1
  236. package/src/utils/areArraysEqual.ts +12 -0
  237. package/src/utils/clamp.ts +7 -0
  238. package/src/utils/createBaseUIEventDetails.ts +9 -0
  239. package/src/utils/formatNumber.ts +32 -0
  240. package/src/utils/owner.ts +5 -0
  241. package/src/utils/resolveAriaLabelledBy.ts +10 -0
  242. package/src/utils/scrollEdges.ts +33 -0
  243. package/src/utils/styles.ts +28 -0
  244. package/src/utils/useControllableState.ts +78 -14
  245. package/src/utils/useFocusableWhenDisabled.ts +6 -1
  246. package/src/utils/useMergedRefs.ts +26 -2
  247. package/src/utils/useRegisteredLabelId.ts +21 -0
  248. package/src/utils/valueToPercent.ts +7 -0
  249. package/src/utils/visuallyHidden.ts +24 -0
  250. package/dist/direction-provider/DirectionContext.cjs +0 -26
  251. package/dist/direction-provider/DirectionContext.cjs.map +0 -1
  252. package/dist/direction-provider/DirectionContext.js +0 -15
  253. package/dist/direction-provider/DirectionContext.js.map +0 -1
@@ -0,0 +1,23 @@
1
+ import type { InjectionKey, Ref } from 'vue'
2
+ import { inject } from 'vue'
3
+
4
+ export interface ToolbarGroupContext {
5
+ disabled: Ref<boolean>
6
+ }
7
+
8
+ export const toolbarGroupContextKey: InjectionKey<ToolbarGroupContext>
9
+ = Symbol('ToolbarGroupContext')
10
+
11
+ export function useToolbarGroupContext(): ToolbarGroupContext
12
+ export function useToolbarGroupContext(optional: true): ToolbarGroupContext | undefined
13
+ export function useToolbarGroupContext(optional = false) {
14
+ const context = inject(toolbarGroupContextKey, undefined)
15
+
16
+ if (context === undefined && !optional) {
17
+ throw new Error(
18
+ 'Base UI Vue: ToolbarGroupContext is missing. ToolbarGroup parts must be placed within <ToolbarGroup>.',
19
+ )
20
+ }
21
+
22
+ return context
23
+ }
@@ -0,0 +1,11 @@
1
+ export enum ToolbarGroupDataAttributes {
2
+ /**
3
+ * Present when the group is disabled.
4
+ */
5
+ disabled = 'data-disabled',
6
+ /**
7
+ * Indicates the orientation of the toolbar.
8
+ * @type {'horizontal' | 'vertical'}
9
+ */
10
+ orientation = 'data-orientation',
11
+ }
@@ -0,0 +1,27 @@
1
+ export { default as ToolbarButton } from './button/ToolbarButton.vue'
2
+ export type { ToolbarButtonProps, ToolbarButtonState } from './button/ToolbarButton.vue'
3
+ export { ToolbarButtonDataAttributes } from './button/ToolbarButtonDataAttributes'
4
+
5
+ export { default as ToolbarGroup } from './group/ToolbarGroup.vue'
6
+ export type { ToolbarGroupProps, ToolbarGroupState } from './group/ToolbarGroup.vue'
7
+ export { toolbarGroupContextKey, useToolbarGroupContext } from './group/ToolbarGroupContext'
8
+ export type { ToolbarGroupContext } from './group/ToolbarGroupContext'
9
+ export { ToolbarGroupDataAttributes } from './group/ToolbarGroupDataAttributes'
10
+
11
+ export { default as ToolbarInput } from './input/ToolbarInput.vue'
12
+ export type { ToolbarInputProps, ToolbarInputState } from './input/ToolbarInput.vue'
13
+ export { ToolbarInputDataAttributes } from './input/ToolbarInputDataAttributes'
14
+
15
+ export { default as ToolbarLink } from './link/ToolbarLink.vue'
16
+ export type { ToolbarLinkProps, ToolbarLinkState } from './link/ToolbarLink.vue'
17
+ export { ToolbarLinkDataAttributes } from './link/ToolbarLinkDataAttributes'
18
+
19
+ export { default as ToolbarRoot } from './root/ToolbarRoot.vue'
20
+ export type { ToolbarRootProps, ToolbarRootState } from './root/ToolbarRoot.vue'
21
+ export { toolbarRootContextKey, useToolbarRootContext } from './root/ToolbarRootContext'
22
+ export type { ToolbarRootContext, ToolbarRootItemMetadata } from './root/ToolbarRootContext'
23
+ export { ToolbarRootDataAttributes } from './root/ToolbarRootDataAttributes'
24
+
25
+ export { default as ToolbarSeparator } from './separator/ToolbarSeparator.vue'
26
+ export type { ToolbarSeparatorProps, ToolbarSeparatorState } from './separator/ToolbarSeparator.vue'
27
+ export { ToolbarSeparatorDataAttributes } from './separator/ToolbarSeparatorDataAttributes'
@@ -0,0 +1,114 @@
1
+ <script setup lang="ts">
2
+ import type { BaseUIComponentProps } from '../../utils/types'
3
+ import type { ToolbarRootState } from '../root/ToolbarRoot.vue'
4
+ import { computed, useAttrs } from 'vue'
5
+ import { ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, stopEvent } from '../../composite/composite'
6
+ import CompositeItem from '../../composite/item/CompositeItem.vue'
7
+ import { useFocusableWhenDisabled } from '../../utils/useFocusableWhenDisabled'
8
+ import { useToolbarGroupContext } from '../group/ToolbarGroupContext'
9
+ import { useToolbarRootContext } from '../root/ToolbarRootContext'
10
+
11
+ export interface ToolbarInputState extends ToolbarRootState {
12
+ /**
13
+ * Whether the component is disabled.
14
+ */
15
+ disabled: boolean
16
+ /**
17
+ * Whether the component remains focusable when disabled.
18
+ */
19
+ focusable: boolean
20
+ }
21
+
22
+ export interface ToolbarInputProps extends BaseUIComponentProps<ToolbarInputState> {
23
+ /**
24
+ * Whether the component is disabled.
25
+ * @default false
26
+ */
27
+ disabled?: boolean
28
+ /**
29
+ * Whether the component remains focusable when disabled.
30
+ * @default true
31
+ */
32
+ focusableWhenDisabled?: boolean
33
+ defaultValue?: string | number
34
+ }
35
+
36
+ /**
37
+ * A native input element that integrates with Toolbar keyboard navigation.
38
+ * Renders an `<input>` element.
39
+ *
40
+ * Documentation: [Base UI Vue Toolbar](https://baseui-vue.com/docs/components/toolbar)
41
+ */
42
+ defineOptions({
43
+ name: 'ToolbarInput',
44
+ inheritAttrs: false,
45
+ })
46
+
47
+ const props = withDefaults(defineProps<ToolbarInputProps>(), {
48
+ as: 'input',
49
+ disabled: false,
50
+ focusableWhenDisabled: true,
51
+ })
52
+
53
+ const attrs = useAttrs()
54
+ const toolbarRootContext = useToolbarRootContext()
55
+ const toolbarGroupContext = useToolbarGroupContext(true)
56
+
57
+ const disabled = computed(() =>
58
+ toolbarRootContext.disabled.value
59
+ || (toolbarGroupContext?.disabled.value ?? false)
60
+ || props.disabled,
61
+ )
62
+
63
+ const { props: focusableWhenDisabledProps } = useFocusableWhenDisabled({
64
+ composite: true,
65
+ disabled,
66
+ focusableWhenDisabled: () => props.focusableWhenDisabled,
67
+ isNativeButton: false,
68
+ })
69
+
70
+ const state = computed<ToolbarInputState>(() => ({
71
+ disabled: disabled.value,
72
+ orientation: toolbarRootContext.orientation.value,
73
+ focusable: props.focusableWhenDisabled,
74
+ }))
75
+
76
+ const allowedKeysWhenDisabled = computed(() =>
77
+ toolbarRootContext.orientation.value === 'vertical'
78
+ ? new Set([ARROW_UP, ARROW_DOWN])
79
+ : new Set([ARROW_LEFT, ARROW_RIGHT]),
80
+ )
81
+
82
+ const defaultProps = computed(() => ({
83
+ onClick(event: MouseEvent) {
84
+ if (disabled.value) {
85
+ event.preventDefault()
86
+ }
87
+ },
88
+ onKeydown(event: KeyboardEvent) {
89
+ if (disabled.value && !allowedKeysWhenDisabled.value.has(event.key)) {
90
+ stopEvent(event)
91
+ }
92
+ },
93
+ onPointerdown(event: PointerEvent) {
94
+ if (disabled.value) {
95
+ event.preventDefault()
96
+ }
97
+ },
98
+ }))
99
+ </script>
100
+
101
+ <template>
102
+ <CompositeItem
103
+ :as="as"
104
+ :class="props.class"
105
+ :style="props.style"
106
+ :metadata="{ focusableWhenDisabled }"
107
+ :state="state"
108
+ :props="[
109
+ defaultProps,
110
+ attrs as Record<string, any>,
111
+ () => focusableWhenDisabledProps,
112
+ ]"
113
+ />
114
+ </template>
@@ -0,0 +1,15 @@
1
+ export enum ToolbarInputDataAttributes {
2
+ /**
3
+ * Present when the input is disabled.
4
+ */
5
+ disabled = 'data-disabled',
6
+ /**
7
+ * Indicates the orientation of the toolbar.
8
+ * @type {'horizontal' | 'vertical'}
9
+ */
10
+ orientation = 'data-orientation',
11
+ /**
12
+ * Present when the input remains focusable when disabled.
13
+ */
14
+ focusable = 'data-focusable',
15
+ }
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ import type { BaseUIComponentProps } from '../../utils/types'
3
+ import { computed, useAttrs } from 'vue'
4
+ import CompositeItem from '../../composite/item/CompositeItem.vue'
5
+ import { useToolbarRootContext } from '../root/ToolbarRootContext'
6
+
7
+ /**
8
+ * A link component.
9
+ * Renders an `<a>` element.
10
+ *
11
+ * Documentation: [Base UI Vue Toolbar](https://baseui-vue.com/docs/components/toolbar)
12
+ */
13
+ defineOptions({
14
+ name: 'ToolbarLink',
15
+ inheritAttrs: false,
16
+ })
17
+
18
+ const props = withDefaults(defineProps<ToolbarLinkProps>(), {
19
+ as: 'a',
20
+ })
21
+
22
+ const TOOLBAR_LINK_METADATA = {
23
+ focusableWhenDisabled: true,
24
+ }
25
+
26
+ export interface ToolbarLinkState {
27
+ /**
28
+ * The component orientation.
29
+ */
30
+ orientation: 'horizontal' | 'vertical'
31
+ }
32
+
33
+ export interface ToolbarLinkProps extends BaseUIComponentProps<ToolbarLinkState> {}
34
+
35
+ const attrs = useAttrs()
36
+ const toolbarRootContext = useToolbarRootContext()
37
+
38
+ const state = computed<ToolbarLinkState>(() => ({
39
+ orientation: toolbarRootContext.orientation.value,
40
+ }))
41
+ </script>
42
+
43
+ <template>
44
+ <CompositeItem
45
+ :as="as"
46
+ :class="props.class"
47
+ :style="props.style"
48
+ :metadata="TOOLBAR_LINK_METADATA"
49
+ :state="state"
50
+ :props="[attrs]"
51
+ >
52
+ <slot :state="state" />
53
+ </CompositeItem>
54
+ </template>
@@ -0,0 +1,7 @@
1
+ export enum ToolbarLinkDataAttributes {
2
+ /**
3
+ * Indicates the orientation of the toolbar.
4
+ * @type {'horizontal' | 'vertical'}
5
+ */
6
+ orientation = 'data-orientation',
7
+ }
@@ -0,0 +1,144 @@
1
+ <script setup lang="ts">
2
+ import type { CompositeMetadata } from '../../composite/list/CompositeList.vue'
3
+ import type { BaseUIComponentProps, Orientation } from '../../utils/types'
4
+ import type { ToolbarRootItemMetadata } from './ToolbarRootContext'
5
+ import { computed, provide, reactive, ref, useAttrs } from 'vue'
6
+ import CompositeList from '../../composite/list/CompositeList.vue'
7
+ import { compositeRootContextKey } from '../../composite/root/CompositeRootContext'
8
+ import { useCompositeRoot } from '../../composite/root/useCompositeRoot'
9
+ import { useDirection } from '../../direction-provider/DirectionContext'
10
+ import { isElementDisabled } from '../../utils/isElementDisabled'
11
+ import { useRenderElement } from '../../utils/useRenderElement'
12
+ import { toolbarRootContextKey } from './ToolbarRootContext'
13
+
14
+ export interface ToolbarRootState {
15
+ /**
16
+ * Whether the component is disabled.
17
+ */
18
+ disabled: boolean
19
+ /**
20
+ * The component orientation.
21
+ */
22
+ orientation: Orientation
23
+ }
24
+
25
+ export interface ToolbarRootProps extends BaseUIComponentProps<ToolbarRootState> {
26
+ /**
27
+ * Whether the component is disabled.
28
+ * @default false
29
+ */
30
+ disabled?: boolean
31
+ /**
32
+ * The orientation of the toolbar.
33
+ * @default 'horizontal'
34
+ */
35
+ orientation?: Orientation
36
+ /**
37
+ * If `true`, using keyboard navigation will wrap focus to the other end of
38
+ * the toolbar once the end is reached.
39
+ * @default true
40
+ */
41
+ loopFocus?: boolean
42
+ }
43
+
44
+ /**
45
+ * A container for grouping a set of controls, such as buttons, toggle groups, or menus.
46
+ * Renders a `<div>` element.
47
+ *
48
+ * Documentation: [Base UI Vue Toolbar](https://baseui-vue.com/docs/components/toolbar)
49
+ */
50
+ defineOptions({
51
+ name: 'ToolbarRoot',
52
+ inheritAttrs: false,
53
+ })
54
+
55
+ const props = withDefaults(defineProps<ToolbarRootProps>(), {
56
+ as: 'div',
57
+ disabled: false,
58
+ orientation: 'horizontal',
59
+ loopFocus: true,
60
+ })
61
+
62
+ const attrs = useAttrs()
63
+ const direction = useDirection()
64
+
65
+ const itemMap = ref(new Map<Element, CompositeMetadata<ToolbarRootItemMetadata> | null>())
66
+
67
+ const disabledIndices = computed(() => {
68
+ const output: number[] = []
69
+
70
+ itemMap.value.forEach((itemMetadata, element) => {
71
+ if (
72
+ itemMetadata?.index != null
73
+ && !itemMetadata.focusableWhenDisabled
74
+ && isElementDisabled(element as HTMLElement)
75
+ ) {
76
+ output.push(itemMetadata.index)
77
+ }
78
+ })
79
+
80
+ return output
81
+ })
82
+
83
+ const state = computed<ToolbarRootState>(() => ({
84
+ disabled: props.disabled,
85
+ orientation: props.orientation,
86
+ }))
87
+
88
+ const root = useCompositeRoot({
89
+ orientation: () => props.orientation,
90
+ loopFocus: () => props.loopFocus,
91
+ direction: () => direction.value,
92
+ disabledIndices: () => disabledIndices.value,
93
+ })
94
+
95
+ provide(toolbarRootContextKey, {
96
+ disabled: computed(() => props.disabled),
97
+ orientation: computed(() => props.orientation),
98
+ })
99
+
100
+ provide(
101
+ compositeRootContextKey,
102
+ reactive({
103
+ highlightedIndex: root.highlightedIndex,
104
+ onHighlightedIndexChange: root.onHighlightedIndexChange,
105
+ highlightItemOnHover: computed(() => false),
106
+ relayKeyboardEvent: root.relayKeyboardEvent,
107
+ }),
108
+ )
109
+
110
+ const {
111
+ tag,
112
+ mergedProps,
113
+ renderless,
114
+ ref: renderRef,
115
+ } = useRenderElement({
116
+ componentProps: props,
117
+ state,
118
+ props: computed(() => ({
119
+ ...root.getRootProps(attrs),
120
+ 'role': 'toolbar',
121
+ 'aria-orientation': props.orientation,
122
+ })),
123
+ defaultTagName: 'div',
124
+ ref: root.mergedRef,
125
+ })
126
+
127
+ function handleMapChange(newMap: Map<Element, { index?: number | null | undefined } | null>) {
128
+ const typedMap = newMap as Map<Element, CompositeMetadata<ToolbarRootItemMetadata> | null>
129
+ itemMap.value = typedMap
130
+ root.onMapChange(typedMap)
131
+ }
132
+ </script>
133
+
134
+ <template>
135
+ <CompositeList
136
+ :elements-ref="root.elementsRef"
137
+ @map-change="handleMapChange"
138
+ >
139
+ <slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="state" />
140
+ <component :is="tag" v-else :ref="renderRef" v-bind="mergedProps">
141
+ <slot :state="state" />
142
+ </component>
143
+ </CompositeList>
144
+ </template>
@@ -0,0 +1,29 @@
1
+ import type { InjectionKey, Ref } from 'vue'
2
+ import type { Orientation } from '../../utils/types'
3
+ import { inject } from 'vue'
4
+
5
+ export interface ToolbarRootItemMetadata {
6
+ focusableWhenDisabled: boolean
7
+ }
8
+
9
+ export interface ToolbarRootContext {
10
+ disabled: Ref<boolean>
11
+ orientation: Ref<Orientation>
12
+ }
13
+
14
+ export const toolbarRootContextKey: InjectionKey<ToolbarRootContext>
15
+ = Symbol('ToolbarRootContext')
16
+
17
+ export function useToolbarRootContext(optional: true): ToolbarRootContext | undefined
18
+ export function useToolbarRootContext(optional?: false): ToolbarRootContext
19
+ export function useToolbarRootContext(optional = false) {
20
+ const context = inject(toolbarRootContextKey, undefined)
21
+
22
+ if (context === undefined && !optional) {
23
+ throw new Error(
24
+ 'Base UI Vue: ToolbarRootContext is missing. Toolbar parts must be placed within <ToolbarRoot>.',
25
+ )
26
+ }
27
+
28
+ return context
29
+ }
@@ -0,0 +1,11 @@
1
+ export enum ToolbarRootDataAttributes {
2
+ /**
3
+ * Present when the toolbar is disabled.
4
+ */
5
+ disabled = 'data-disabled',
6
+ /**
7
+ * Indicates the orientation of the toolbar.
8
+ * @type {'horizontal' | 'vertical'}
9
+ */
10
+ orientation = 'data-orientation',
11
+ }
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts">
2
+ import type { SeparatorProps, SeparatorState } from '../../separator/Separator.vue'
3
+ import { computed, useAttrs } from 'vue'
4
+ import Separator from '../../separator/Separator.vue'
5
+ import { useToolbarRootContext } from '../root/ToolbarRootContext'
6
+
7
+ export interface ToolbarSeparatorState extends SeparatorState {}
8
+
9
+ export interface ToolbarSeparatorProps extends SeparatorProps {}
10
+
11
+ /**
12
+ * A separator element accessible to screen readers.
13
+ * Renders a `<div>` element.
14
+ *
15
+ * Documentation: [Base UI Vue Toolbar](https://baseui-vue.com/docs/components/toolbar)
16
+ */
17
+ defineOptions({
18
+ name: 'ToolbarSeparator',
19
+ inheritAttrs: false,
20
+ })
21
+
22
+ const props = defineProps<ToolbarSeparatorProps>()
23
+ const attrs = useAttrs()
24
+ const toolbarRootContext = useToolbarRootContext()
25
+
26
+ const orientation = computed(() => props.orientation
27
+ ?? (toolbarRootContext.orientation.value === 'vertical' ? 'horizontal' : 'vertical'))
28
+ </script>
29
+
30
+ <template>
31
+ <Separator
32
+ v-slot="slotProps"
33
+ :as="props.as"
34
+ :class="props.class"
35
+ :style="props.style"
36
+ :orientation="orientation"
37
+ v-bind="attrs"
38
+ >
39
+ <slot v-bind="slotProps" />
40
+ </Separator>
41
+ </template>
@@ -0,0 +1,7 @@
1
+ export enum ToolbarSeparatorDataAttributes {
2
+ /**
3
+ * Indicates the orientation of the toolbar.
4
+ * @type {'horizontal' | 'vertical'}
5
+ */
6
+ orientation = 'data-orientation',
7
+ }
@@ -61,11 +61,13 @@ export function useButton(
61
61
  parameters: UseButtonParameters = {},
62
62
  ): UseButtonReturnValue {
63
63
  const buttonRef = ref<HTMLElement | null>(null)
64
+ const isCompositeItem = useCompositeRootContext(true) !== undefined
64
65
 
65
66
  const { props: focusableWhenDisabledProps } = useFocusableWhenDisabled({
66
67
  focusableWhenDisabled: () =>
67
68
  toValue(parameters.focusableWhenDisabled) ?? false,
68
69
  disabled: () => toValue(parameters.disabled) ?? false,
70
+ composite: () => isCompositeItem,
69
71
  isNativeButton: () => toValue(parameters.native) ?? true,
70
72
  tabIndex: () => toValue(parameters.tabIndex) ?? 0,
71
73
  })
@@ -105,7 +107,6 @@ export function useButton(
105
107
  // <Toolbar.Button disabled as={<Menu.Trigger />} />
106
108
  // the `disabled` prop needs to pass through 2 `useButton`s then finally
107
109
  // delete the `disabled` attribute from DOM
108
- const isCompositeItem = useCompositeRootContext(true) !== undefined
109
110
  const updateDisabled = () => {
110
111
  const element = buttonRef.value
111
112
  if (!isButtonElement(element))
@@ -0,0 +1,12 @@
1
+ type ItemComparer<Item> = (a: Item, b: Item) => boolean
2
+
3
+ export function areArraysEqual<Item>(
4
+ array1: readonly Item[],
5
+ array2: readonly Item[],
6
+ itemComparer: ItemComparer<Item> = (a, b) => a === b,
7
+ ): boolean {
8
+ return (
9
+ array1.length === array2.length
10
+ && array1.every((value, index) => itemComparer(value, array2[index]))
11
+ )
12
+ }
@@ -0,0 +1,7 @@
1
+ export function clamp(
2
+ value: number,
3
+ min: number = Number.MIN_SAFE_INTEGER,
4
+ max: number = Number.MAX_SAFE_INTEGER,
5
+ ): number {
6
+ return Math.min(Math.max(value, min), max)
7
+ }
@@ -86,6 +86,7 @@ type BaseUIChangeEventDetail<Reason extends string, CustomProperties extends obj
86
86
  } & CustomProperties
87
87
 
88
88
  export type BaseUIChangeEventDetails<Reason extends string, CustomProperties extends object = Record<string, never>> = Reason extends string ? BaseUIChangeEventDetail<Reason, CustomProperties> : never
89
+ export type BaseUIGenericEventDetails<Reason extends string> = BaseUIChangeEventDetails<Reason>
89
90
 
90
91
  /**
91
92
  * Creates a Base UI event details object with the given reason and utilities
@@ -125,3 +126,11 @@ export function createChangeEventDetails<
125
126
 
126
127
  return details
127
128
  }
129
+
130
+ export function createGenericEventDetails<Reason extends string>(
131
+ reason: Reason,
132
+ event?: ReasonToEvent<Reason>,
133
+ trigger?: HTMLElement,
134
+ ): BaseUIGenericEventDetails<Reason> {
135
+ return createChangeEventDetails(reason, event, trigger)
136
+ }
@@ -0,0 +1,32 @@
1
+ export function formatNumber(
2
+ value: number,
3
+ locale?: Intl.LocalesArgument,
4
+ options?: Intl.NumberFormatOptions,
5
+ ) {
6
+ return new Intl.NumberFormat(locale, options).format(value)
7
+ }
8
+
9
+ /**
10
+ * Formats a numeric value for display inside Base UI Vue components.
11
+ *
12
+ * When no `format` is provided, the value is interpreted as a percentage
13
+ * in the 0-100 range (matching React's Base UI semantics for
14
+ * `<Meter.Root>` / `<Progress.Root>`).
15
+ *
16
+ * Returns an empty string when the value is `null`.
17
+ */
18
+ export function formatNumberValue(
19
+ value: number | null,
20
+ locale?: Intl.LocalesArgument,
21
+ format?: Intl.NumberFormatOptions,
22
+ ): string {
23
+ if (value == null) {
24
+ return ''
25
+ }
26
+
27
+ if (!format) {
28
+ return formatNumber(value / 100, locale, { style: 'percent' })
29
+ }
30
+
31
+ return formatNumber(value, locale, format)
32
+ }
@@ -0,0 +1,5 @@
1
+ export { getWindow as ownerWindow } from '@floating-ui/utils/dom'
2
+
3
+ export function ownerDocument(node: Element | null): Document | null {
4
+ return node?.ownerDocument || (typeof document !== 'undefined' ? document : null)
5
+ }
@@ -0,0 +1,10 @@
1
+ export function getDefaultLabelId(id: string | null | undefined) {
2
+ return id == null ? undefined : `${id}-label`
3
+ }
4
+
5
+ export function resolveAriaLabelledBy(
6
+ fieldLabelId: string | undefined,
7
+ localLabelId: string | undefined,
8
+ ) {
9
+ return fieldLabelId ?? localLabelId
10
+ }
@@ -0,0 +1,33 @@
1
+ import { clamp } from './clamp'
2
+
3
+ export const SCROLL_EDGE_TOLERANCE_PX = 1
4
+
5
+ export function getMaxScrollOffset(scrollSize: number, clientSize: number) {
6
+ return Math.max(0, scrollSize - clientSize)
7
+ }
8
+
9
+ export function normalizeScrollOffset(value: number, max: number) {
10
+ if (max <= 0) {
11
+ return 0
12
+ }
13
+
14
+ const clamped = clamp(value, 0, max)
15
+ const startDistance = clamped
16
+ const endDistance = max - clamped
17
+ const withinStartTolerance = startDistance <= SCROLL_EDGE_TOLERANCE_PX
18
+ const withinEndTolerance = endDistance <= SCROLL_EDGE_TOLERANCE_PX
19
+
20
+ if (withinStartTolerance && withinEndTolerance) {
21
+ return startDistance <= endDistance ? 0 : max
22
+ }
23
+
24
+ if (withinStartTolerance) {
25
+ return 0
26
+ }
27
+
28
+ if (withinEndTolerance) {
29
+ return max
30
+ }
31
+
32
+ return clamped
33
+ }
@@ -0,0 +1,28 @@
1
+ const DISABLE_SCROLLBAR_CLASS_NAME = 'base-ui-disable-scrollbar'
2
+ const STYLE_ELEMENT_ID = `style-${DISABLE_SCROLLBAR_CLASS_NAME}`
3
+
4
+ export const styleDisableScrollbar = {
5
+ className: DISABLE_SCROLLBAR_CLASS_NAME,
6
+
7
+ inject(nonce?: string, disableStyleElements?: boolean) {
8
+ if (disableStyleElements) {
9
+ return
10
+ }
11
+
12
+ if (typeof document === 'undefined') {
13
+ return
14
+ }
15
+
16
+ if (document.getElementById(STYLE_ELEMENT_ID)) {
17
+ return
18
+ }
19
+
20
+ const style = document.createElement('style')
21
+ style.id = STYLE_ELEMENT_ID
22
+ if (nonce) {
23
+ style.nonce = nonce
24
+ }
25
+ style.textContent = `.${DISABLE_SCROLLBAR_CLASS_NAME}{scrollbar-width:none}.${DISABLE_SCROLLBAR_CLASS_NAME}::-webkit-scrollbar{display:none}`
26
+ document.head.appendChild(style)
27
+ },
28
+ }