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.
- package/README.md +41 -1
- package/dist/button/Button.cjs +53 -12
- package/dist/button/Button.cjs.map +1 -1
- package/dist/button/Button.js +26 -15
- package/dist/button/Button.js.map +1 -1
- package/dist/button/ToolbarButton.cjs +367 -0
- package/dist/button/ToolbarButton.cjs.map +1 -0
- package/dist/button/ToolbarButton.js +320 -0
- package/dist/button/ToolbarButton.js.map +1 -0
- package/dist/button/ToolbarButtonDataAttributes.cjs +27 -0
- package/dist/button/ToolbarButtonDataAttributes.cjs.map +1 -0
- package/dist/button/ToolbarButtonDataAttributes.js +21 -0
- package/dist/button/ToolbarButtonDataAttributes.js.map +1 -0
- package/dist/checkbox/index.cjs +1173 -0
- package/dist/checkbox/index.cjs.map +1 -0
- package/dist/checkbox/index.js +1048 -0
- package/dist/checkbox/index.js.map +1 -0
- package/dist/checkbox-group/CheckboxGroup.cjs +629 -0
- package/dist/checkbox-group/CheckboxGroup.cjs.map +1 -0
- package/dist/checkbox-group/CheckboxGroup.js +540 -0
- package/dist/checkbox-group/CheckboxGroup.js.map +1 -0
- package/dist/checkbox-group/CheckboxGroupDataAttributes.cjs +18 -0
- package/dist/checkbox-group/CheckboxGroupDataAttributes.cjs.map +1 -0
- package/dist/checkbox-group/CheckboxGroupDataAttributes.js +12 -0
- package/dist/checkbox-group/CheckboxGroupDataAttributes.js.map +1 -0
- package/dist/composite/composite.cjs +167 -0
- package/dist/composite/composite.cjs.map +1 -1
- package/dist/composite/composite.js +96 -1
- package/dist/composite/composite.js.map +1 -1
- package/dist/composite/constants.cjs +12 -0
- package/dist/composite/constants.cjs.map +1 -0
- package/dist/composite/constants.js +6 -0
- package/dist/composite/constants.js.map +1 -0
- package/dist/control/FieldControl.cjs +18 -343
- package/dist/control/FieldControl.cjs.map +1 -1
- package/dist/control/FieldControl.js +14 -285
- package/dist/control/FieldControl.js.map +1 -1
- package/dist/control/SliderControl.cjs +636 -0
- package/dist/control/SliderControl.cjs.map +1 -0
- package/dist/control/SliderControl.js +553 -0
- package/dist/control/SliderControl.js.map +1 -0
- package/dist/control/SliderControlDataAttributes.cjs +47 -0
- package/dist/control/SliderControlDataAttributes.cjs.map +1 -0
- package/dist/control/SliderControlDataAttributes.js +41 -0
- package/dist/control/SliderControlDataAttributes.js.map +1 -0
- package/dist/csp-provider/CSPContext.cjs +32 -0
- package/dist/csp-provider/CSPContext.cjs.map +1 -0
- package/dist/csp-provider/CSPContext.js +21 -0
- package/dist/csp-provider/CSPContext.js.map +1 -0
- package/dist/csp-provider/CSPProvider.cjs +46 -0
- package/dist/csp-provider/CSPProvider.cjs.map +1 -0
- package/dist/csp-provider/CSPProvider.js +41 -0
- package/dist/csp-provider/CSPProvider.js.map +1 -0
- package/dist/description/FieldDescription.cjs +5 -5
- package/dist/description/FieldDescription.cjs.map +1 -1
- package/dist/description/FieldDescription.js +1 -1
- package/dist/direction-provider/DirectionProvider.cjs +2 -2
- package/dist/direction-provider/DirectionProvider.cjs.map +1 -1
- package/dist/direction-provider/DirectionProvider.js +1 -1
- package/dist/error/FieldError.cjs +10 -288
- package/dist/error/FieldError.cjs.map +1 -1
- package/dist/error/FieldError.js +4 -246
- package/dist/error/FieldError.js.map +1 -1
- package/dist/form/Form.cjs +5 -4
- package/dist/form/Form.cjs.map +1 -1
- package/dist/form/Form.js +5 -4
- package/dist/form/Form.js.map +1 -1
- package/dist/group/ToolbarGroup.cjs +92 -0
- package/dist/group/ToolbarGroup.cjs.map +1 -0
- package/dist/group/ToolbarGroup.js +87 -0
- package/dist/group/ToolbarGroup.js.map +1 -0
- package/dist/group/ToolbarGroupDataAttributes.cjs +23 -0
- package/dist/group/ToolbarGroupDataAttributes.cjs.map +1 -0
- package/dist/group/ToolbarGroupDataAttributes.js +17 -0
- package/dist/group/ToolbarGroupDataAttributes.js.map +1 -0
- package/dist/header/AccordionHeader.cjs +2 -2
- package/dist/header/AccordionHeader.js +1 -1
- package/dist/image/AvatarImage.cjs +4 -4
- package/dist/image/AvatarImage.cjs.map +1 -1
- package/dist/image/AvatarImage.js +1 -1
- package/dist/index.cjs +80 -10
- package/dist/index.d.cts +2751 -612
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +2751 -612
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index2.cjs +3651 -370
- package/dist/index2.cjs.map +1 -1
- package/dist/index2.js +3365 -270
- package/dist/index2.js.map +1 -1
- package/package.json +8 -4
- package/src/accordion/root/AccordionRoot.vue +2 -1
- package/src/checkbox/index.ts +23 -0
- package/src/checkbox/indicator/CheckboxIndicator.vue +102 -0
- package/src/checkbox/indicator/CheckboxIndicatorDataAttributes.ts +61 -0
- package/src/checkbox/root/CheckboxRoot.vue +632 -0
- package/src/checkbox/root/CheckboxRootContext.ts +22 -0
- package/src/checkbox/root/CheckboxRootDataAttributes.ts +54 -0
- package/src/checkbox/utils/useStateAttributesMapping.ts +30 -0
- package/src/checkbox-group/CheckboxGroup.vue +241 -0
- package/src/checkbox-group/CheckboxGroupContext.ts +39 -0
- package/src/checkbox-group/CheckboxGroupDataAttributes.ts +6 -0
- package/src/checkbox-group/index.ts +11 -0
- package/src/checkbox-group/useCheckboxGroupParent.ts +173 -0
- package/src/collapsible/panel/useCollapsiblePanel.ts +2 -1
- package/src/collapsible/root/useCollapsibleRoot.ts +3 -1
- package/src/composite/composite.ts +2 -0
- package/src/composite/item/CompositeItem.vue +7 -8
- package/src/composite/root/CompositeRoot.vue +12 -1
- package/src/csp-provider/CSPContext.ts +26 -0
- package/src/csp-provider/CSPProvider.vue +40 -0
- package/src/csp-provider/index.ts +5 -0
- package/src/field/item/FieldItem.vue +6 -1
- package/src/field/label/FieldLabel.vue +10 -51
- package/src/field/root/FieldRoot.vue +16 -3
- package/src/floating-ui-vue/types.ts +1 -4
- package/src/floating-ui-vue/utils/element.ts +12 -0
- package/src/floating-ui-vue/utils/shadowDom.ts +44 -0
- package/src/floating-ui-vue/utils.ts +3 -0
- package/src/form/Form.vue +5 -3
- package/src/index.ts +9 -0
- package/src/labelable-provider/LabelableContext.ts +2 -2
- package/src/labelable-provider/LabelableProvider.vue +21 -4
- package/src/labelable-provider/index.ts +2 -0
- package/src/labelable-provider/useAriaLabelledBy.ts +9 -9
- package/src/labelable-provider/useLabel.ts +115 -0
- package/src/labelable-provider/useLabelableId.ts +12 -10
- package/src/separator/Separator.vue +65 -0
- package/src/separator/SeparatorDataAttributes.ts +7 -0
- package/src/separator/index.ts +3 -0
- package/src/slider/control/SliderControl.vue +497 -0
- package/src/slider/control/SliderControlDataAttributes.ts +35 -0
- package/src/slider/index.ts +35 -0
- package/src/slider/indicator/SliderIndicator.vue +144 -0
- package/src/slider/indicator/SliderIndicatorDataAttributes.ts +35 -0
- package/src/slider/label/SliderLabel.vue +75 -0
- package/src/slider/root/SliderRoot.vue +557 -0
- package/src/slider/root/SliderRootContext.ts +126 -0
- package/src/slider/root/SliderRootDataAttributes.ts +35 -0
- package/src/slider/root/stateAttributesMapping.ts +13 -0
- package/src/slider/thumb/SliderThumb.vue +601 -0
- package/src/slider/thumb/SliderThumbDataAttributes.ts +39 -0
- package/src/slider/thumb/prehydrationScript.min.ts +5 -0
- package/src/slider/thumb/prehydrationScript.template.js +69 -0
- package/src/slider/track/SliderTrack.vue +48 -0
- package/src/slider/track/SliderTrackDataAttributes.ts +10 -0
- package/src/slider/utils/asc.ts +3 -0
- package/src/slider/utils/getMidpoint.ts +9 -0
- package/src/slider/utils/getPushedThumbValues.ts +68 -0
- package/src/slider/utils/getSliderValue.ts +25 -0
- package/src/slider/utils/replaceArrayItemAtIndex.ts +15 -0
- package/src/slider/utils/resolveThumbCollision.ts +177 -0
- package/src/slider/utils/roundValueToStep.ts +19 -0
- package/src/slider/utils/test-utils.ts +25 -0
- package/src/slider/utils/validateMinimumDistance.ts +20 -0
- package/src/slider/utils/valueArrayToPercentages.ts +10 -0
- package/src/slider/value/SliderValue.vue +90 -0
- package/src/slider/value/SliderValueDataAttributes.ts +35 -0
- package/src/switch/index.ts +14 -0
- package/src/switch/root/SwitchRoot.vue +448 -0
- package/src/switch/root/SwitchRootContext.ts +22 -0
- package/src/switch/root/SwitchRootDataAttributes.ts +46 -0
- package/src/switch/stateAttributesMapping.ts +23 -0
- package/src/switch/thumb/SwitchThumb.vue +59 -0
- package/src/switch/thumb/SwitchThumbDataAttributes.ts +46 -0
- package/src/toggle/Toggle.vue +211 -0
- package/src/toggle/ToggleDataAttributes.ts +6 -0
- package/src/toggle/index.ts +3 -0
- package/src/toggle-group/ToggleGroup.vue +224 -0
- package/src/toggle-group/ToggleGroupContext.ts +45 -0
- package/src/toggle-group/ToggleGroupDataAttributes.ts +15 -0
- package/src/toggle-group/index.ts +5 -0
- package/src/toolbar/button/ToolbarButton.vue +99 -0
- package/src/toolbar/button/ToolbarButtonDataAttributes.ts +15 -0
- package/src/toolbar/group/ToolbarGroup.vue +70 -0
- package/src/toolbar/group/ToolbarGroupContext.ts +23 -0
- package/src/toolbar/group/ToolbarGroupDataAttributes.ts +11 -0
- package/src/toolbar/index.ts +27 -0
- package/src/toolbar/input/ToolbarInput.vue +114 -0
- package/src/toolbar/input/ToolbarInputDataAttributes.ts +15 -0
- package/src/toolbar/link/ToolbarLink.vue +54 -0
- package/src/toolbar/link/ToolbarLinkDataAttributes.ts +7 -0
- package/src/toolbar/root/ToolbarRoot.vue +144 -0
- package/src/toolbar/root/ToolbarRootContext.ts +29 -0
- package/src/toolbar/root/ToolbarRootDataAttributes.ts +11 -0
- package/src/toolbar/separator/ToolbarSeparator.vue +41 -0
- package/src/toolbar/separator/ToolbarSeparatorDataAttributes.ts +7 -0
- package/src/use-button/useButton.ts +2 -1
- package/src/utils/areArraysEqual.ts +12 -0
- package/src/utils/clamp.ts +7 -0
- package/src/utils/createBaseUIEventDetails.ts +9 -0
- package/src/utils/formatNumber.ts +7 -0
- package/src/utils/owner.ts +5 -0
- package/src/utils/resolveAriaLabelledBy.ts +10 -0
- package/src/utils/useControllableState.ts +78 -14
- package/src/utils/useFocusableWhenDisabled.ts +6 -1
- package/src/utils/useMergedRefs.ts +26 -2
- package/src/utils/useRegisteredLabelId.ts +21 -0
- package/src/utils/valueToPercent.ts +7 -0
- package/src/utils/visuallyHidden.ts +24 -0
- package/dist/direction-provider/DirectionContext.cjs +0 -26
- package/dist/direction-provider/DirectionContext.cjs.map +0 -1
- package/dist/direction-provider/DirectionContext.js +0 -15
- package/dist/direction-provider/DirectionContext.js.map +0 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { StateAttributesMapping } from '../../utils/getStateAttributesProps'
|
|
2
|
+
import type { CheckboxRootState } from '../root/CheckboxRoot.vue'
|
|
3
|
+
import { fieldValidityMapping } from '../../field/utils/constants'
|
|
4
|
+
import { CheckboxRootDataAttributes } from '../root/CheckboxRootDataAttributes'
|
|
5
|
+
|
|
6
|
+
const CHECKED_ATTRS: Record<string, string> = {
|
|
7
|
+
[CheckboxRootDataAttributes.checked]: '',
|
|
8
|
+
}
|
|
9
|
+
const UNCHECKED_ATTRS: Record<string, string> = {
|
|
10
|
+
[CheckboxRootDataAttributes.unchecked]: '',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useCheckboxStateAttributesMapping(
|
|
14
|
+
state: Readonly<{ value: CheckboxRootState }>,
|
|
15
|
+
): StateAttributesMapping<CheckboxRootState> {
|
|
16
|
+
return {
|
|
17
|
+
checked(value) {
|
|
18
|
+
if (state.value.indeterminate) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (value) {
|
|
23
|
+
return CHECKED_ATTRS
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return UNCHECKED_ATTRS
|
|
27
|
+
},
|
|
28
|
+
...fieldValidityMapping,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { FieldRootState } from '../field/root/FieldRoot.vue'
|
|
3
|
+
import type { BaseUIChangeEventDetails } from '../utils/createBaseUIEventDetails'
|
|
4
|
+
import type { REASONS } from '../utils/reasons'
|
|
5
|
+
import type { BaseUIComponentProps } from '../utils/types'
|
|
6
|
+
import { computed, provide, ref, useAttrs, watch, watchEffect } from 'vue'
|
|
7
|
+
import { useFieldRootContext } from '../field/root/FieldRootContext'
|
|
8
|
+
import { useField } from '../field/useField'
|
|
9
|
+
import { fieldValidityMapping } from '../field/utils/constants'
|
|
10
|
+
import { useFormContext } from '../form/FormContext'
|
|
11
|
+
import { useLabelableContext } from '../labelable-provider/LabelableContext'
|
|
12
|
+
import { mergeProps } from '../merge-props/mergeProps'
|
|
13
|
+
import { EMPTY_ARRAY } from '../utils/constants'
|
|
14
|
+
import { useBaseUiId } from '../utils/useBaseUiId'
|
|
15
|
+
import { useControllableState } from '../utils/useControllableState'
|
|
16
|
+
import { useRenderElement } from '../utils/useRenderElement'
|
|
17
|
+
import { checkboxGroupContextKey } from './CheckboxGroupContext'
|
|
18
|
+
import { useCheckboxGroupParent } from './useCheckboxGroupParent'
|
|
19
|
+
|
|
20
|
+
export interface CheckboxGroupState extends FieldRootState {
|
|
21
|
+
/**
|
|
22
|
+
* Whether the component should ignore user interaction.
|
|
23
|
+
*/
|
|
24
|
+
disabled: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CheckboxGroupProps extends BaseUIComponentProps<CheckboxGroupState> {
|
|
28
|
+
/**
|
|
29
|
+
* Names of the checkboxes in the group that should be ticked.
|
|
30
|
+
*
|
|
31
|
+
* To render an uncontrolled checkbox group, use the `defaultValue` prop instead.
|
|
32
|
+
*/
|
|
33
|
+
value?: string[]
|
|
34
|
+
/**
|
|
35
|
+
* Names of the checkboxes in the group that should be initially ticked.
|
|
36
|
+
*
|
|
37
|
+
* To render a controlled checkbox group, use the `value` prop instead.
|
|
38
|
+
*/
|
|
39
|
+
defaultValue?: string[]
|
|
40
|
+
/**
|
|
41
|
+
* Names of all checkboxes in the group. Use this when creating a parent checkbox.
|
|
42
|
+
*/
|
|
43
|
+
allValues?: string[]
|
|
44
|
+
/**
|
|
45
|
+
* Whether the component should ignore user interaction.
|
|
46
|
+
* @default false
|
|
47
|
+
*/
|
|
48
|
+
disabled?: boolean
|
|
49
|
+
id?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Provides a shared state to a series of checkboxes.
|
|
54
|
+
*
|
|
55
|
+
* Documentation: [Base UI Vue Checkbox Group](https://baseui-vue.com/docs/components/checkbox-group)
|
|
56
|
+
*/
|
|
57
|
+
defineOptions({
|
|
58
|
+
name: 'CheckboxGroup',
|
|
59
|
+
inheritAttrs: false,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const props = withDefaults(defineProps<CheckboxGroupProps>(), {
|
|
63
|
+
as: 'div',
|
|
64
|
+
disabled: false,
|
|
65
|
+
defaultValue: () => [],
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const emit = defineEmits<{
|
|
69
|
+
/**
|
|
70
|
+
* Event handler called when a checkbox in the group is ticked or unticked.
|
|
71
|
+
* Provides the new value as an argument.
|
|
72
|
+
*/
|
|
73
|
+
valueChange: [
|
|
74
|
+
value: string[],
|
|
75
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
76
|
+
]
|
|
77
|
+
}>()
|
|
78
|
+
|
|
79
|
+
const attrs = useAttrs()
|
|
80
|
+
const attrsObject = attrs as Record<string, any>
|
|
81
|
+
|
|
82
|
+
const {
|
|
83
|
+
disabled: fieldDisabled,
|
|
84
|
+
name: fieldName,
|
|
85
|
+
state: fieldState,
|
|
86
|
+
validation,
|
|
87
|
+
setFilled,
|
|
88
|
+
setDirty,
|
|
89
|
+
shouldValidateOnChange,
|
|
90
|
+
validityData,
|
|
91
|
+
} = useFieldRootContext()
|
|
92
|
+
const labelableContext = useLabelableContext()
|
|
93
|
+
const { labelId, getDescriptionProps } = labelableContext
|
|
94
|
+
const { clearErrors } = useFormContext()
|
|
95
|
+
const controlSource = Symbol('checkbox-group-control')
|
|
96
|
+
|
|
97
|
+
const disabled = computed(() => fieldDisabled.value || props.disabled)
|
|
98
|
+
|
|
99
|
+
const { value, setValue: setValueUnwrapped } = useControllableState<string[]>({
|
|
100
|
+
controlled: () => props.value,
|
|
101
|
+
default: () => props.defaultValue,
|
|
102
|
+
name: 'CheckboxGroup',
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const generatedId = useBaseUiId()
|
|
106
|
+
const defaultValue = computed(() => props.defaultValue ?? [])
|
|
107
|
+
const allValues = computed(() => props.allValues)
|
|
108
|
+
const id = computed(() => props.id ?? generatedId)
|
|
109
|
+
|
|
110
|
+
watchEffect((onCleanup) => {
|
|
111
|
+
labelableContext.registerControlId(controlSource, id.value)
|
|
112
|
+
|
|
113
|
+
onCleanup(() => {
|
|
114
|
+
labelableContext.registerControlId(controlSource, undefined)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
function setValue(
|
|
119
|
+
nextValue: string[],
|
|
120
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
121
|
+
) {
|
|
122
|
+
emit('valueChange', nextValue, eventDetails)
|
|
123
|
+
|
|
124
|
+
if (eventDetails.isCanceled) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
setValueUnwrapped(nextValue)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const parent = useCheckboxGroupParent({
|
|
132
|
+
allValues,
|
|
133
|
+
value,
|
|
134
|
+
onValueChange: setValue,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const controlRef = ref<HTMLElement | null>(null)
|
|
138
|
+
|
|
139
|
+
function registerControlRef(element: HTMLElement | null) {
|
|
140
|
+
if (controlRef.value == null && element != null && !element.hasAttribute('data-parent')) {
|
|
141
|
+
controlRef.value = element
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
useField({
|
|
146
|
+
enabled: computed(() => Boolean(fieldName.value)),
|
|
147
|
+
id,
|
|
148
|
+
commit: (value: unknown) => validation.commit(value),
|
|
149
|
+
value,
|
|
150
|
+
controlRef,
|
|
151
|
+
name: fieldName,
|
|
152
|
+
getValue: () => value.value,
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
watch(
|
|
156
|
+
() => value.value.slice(),
|
|
157
|
+
(nextValue) => {
|
|
158
|
+
if (fieldName.value) {
|
|
159
|
+
clearErrors(fieldName.value)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const initialValue = Array.isArray(validityData.value.initialValue)
|
|
163
|
+
? validityData.value.initialValue as string[]
|
|
164
|
+
: EMPTY_ARRAY as string[]
|
|
165
|
+
const normalizedNextValue = normalizeSelection(nextValue)
|
|
166
|
+
const normalizedInitialValue = normalizeSelection(initialValue)
|
|
167
|
+
|
|
168
|
+
setFilled(nextValue.length > 0)
|
|
169
|
+
setDirty(!areArraysEqual(normalizedNextValue, normalizedInitialValue))
|
|
170
|
+
|
|
171
|
+
if (shouldValidateOnChange()) {
|
|
172
|
+
void validation.commit(nextValue)
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
void validation.commit(nextValue, true)
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{ flush: 'sync' },
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
const state = computed<CheckboxGroupState>(() => ({
|
|
182
|
+
...fieldState.value,
|
|
183
|
+
disabled: disabled.value,
|
|
184
|
+
}))
|
|
185
|
+
|
|
186
|
+
provide(checkboxGroupContextKey, {
|
|
187
|
+
value,
|
|
188
|
+
defaultValue,
|
|
189
|
+
allValues,
|
|
190
|
+
disabled,
|
|
191
|
+
validation,
|
|
192
|
+
parent,
|
|
193
|
+
setValue,
|
|
194
|
+
registerControlRef,
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const rootProps = computed(() => mergeProps(
|
|
198
|
+
attrsObject,
|
|
199
|
+
getDescriptionProps(),
|
|
200
|
+
{
|
|
201
|
+
'role': 'group',
|
|
202
|
+
'aria-labelledby': labelId.value,
|
|
203
|
+
},
|
|
204
|
+
))
|
|
205
|
+
|
|
206
|
+
const {
|
|
207
|
+
tag,
|
|
208
|
+
mergedProps,
|
|
209
|
+
renderless,
|
|
210
|
+
ref: renderRef,
|
|
211
|
+
} = useRenderElement({
|
|
212
|
+
componentProps: props,
|
|
213
|
+
state,
|
|
214
|
+
props: rootProps,
|
|
215
|
+
stateAttributesMapping: fieldValidityMapping,
|
|
216
|
+
defaultTagName: 'div',
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
function areArraysEqual(a: readonly string[], b: readonly string[]) {
|
|
220
|
+
if (a === b) {
|
|
221
|
+
return true
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (a.length !== b.length) {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return a.every((item, index) => item === b[index])
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function normalizeSelection(values: readonly string[]) {
|
|
232
|
+
return values.slice().sort()
|
|
233
|
+
}
|
|
234
|
+
</script>
|
|
235
|
+
|
|
236
|
+
<template>
|
|
237
|
+
<slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="state" />
|
|
238
|
+
<component :is="tag" v-else :ref="renderRef" v-bind="mergedProps">
|
|
239
|
+
<slot :state="state" />
|
|
240
|
+
</component>
|
|
241
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from 'vue'
|
|
2
|
+
import type { UseFieldValidationReturnValue } from '../field/root/useFieldValidation'
|
|
3
|
+
import type { BaseUIChangeEventDetails } from '../utils/createBaseUIEventDetails'
|
|
4
|
+
import type { REASONS } from '../utils/reasons'
|
|
5
|
+
import type { UseCheckboxGroupParentReturnValue } from './useCheckboxGroupParent'
|
|
6
|
+
import { inject } from 'vue'
|
|
7
|
+
|
|
8
|
+
export interface CheckboxGroupContext {
|
|
9
|
+
value: Readonly<Ref<string[]>>
|
|
10
|
+
defaultValue: Readonly<Ref<string[]>>
|
|
11
|
+
allValues: Readonly<Ref<string[] | undefined>>
|
|
12
|
+
disabled: Readonly<Ref<boolean>>
|
|
13
|
+
validation: UseFieldValidationReturnValue
|
|
14
|
+
parent: UseCheckboxGroupParentReturnValue
|
|
15
|
+
setValue: (
|
|
16
|
+
value: string[],
|
|
17
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
18
|
+
) => void
|
|
19
|
+
registerControlRef: (element: HTMLElement | null) => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const checkboxGroupContextKey: InjectionKey<CheckboxGroupContext> = Symbol(
|
|
23
|
+
'CheckboxGroupContext',
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
export function useCheckboxGroupContext(
|
|
27
|
+
optional: true,
|
|
28
|
+
): CheckboxGroupContext | undefined
|
|
29
|
+
export function useCheckboxGroupContext(optional?: false): CheckboxGroupContext
|
|
30
|
+
export function useCheckboxGroupContext(optional = false) {
|
|
31
|
+
const context = inject(checkboxGroupContextKey, undefined)
|
|
32
|
+
if (!context && !optional) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
'Base UI Vue: CheckboxGroupContext is missing. CheckboxGroup parts must be placed within <CheckboxGroup>.',
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return context
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as CheckboxGroup } from './CheckboxGroup.vue'
|
|
2
|
+
export type { CheckboxGroupProps, CheckboxGroupState } from './CheckboxGroup.vue'
|
|
3
|
+
export { checkboxGroupContextKey, useCheckboxGroupContext } from './CheckboxGroupContext'
|
|
4
|
+
|
|
5
|
+
export type { CheckboxGroupContext } from './CheckboxGroupContext'
|
|
6
|
+
export { CheckboxGroupDataAttributes } from './CheckboxGroupDataAttributes'
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
UseCheckboxGroupParentParameters,
|
|
10
|
+
UseCheckboxGroupParentReturnValue,
|
|
11
|
+
} from './useCheckboxGroupParent'
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import type { BaseUIChangeEventDetails } from '../utils/createBaseUIEventDetails'
|
|
3
|
+
import type { REASONS } from '../utils/reasons'
|
|
4
|
+
import { computed, ref, watch } from 'vue'
|
|
5
|
+
import { useBaseUiId } from '../utils/useBaseUiId'
|
|
6
|
+
|
|
7
|
+
export interface UseCheckboxGroupParentParameters {
|
|
8
|
+
allValues: Readonly<Ref<string[] | undefined>>
|
|
9
|
+
value: Readonly<Ref<string[]>>
|
|
10
|
+
onValueChange: (
|
|
11
|
+
value: string[],
|
|
12
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
13
|
+
) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UseCheckboxGroupParentReturnValue {
|
|
17
|
+
id: string | undefined
|
|
18
|
+
indeterminate: Readonly<Ref<boolean>>
|
|
19
|
+
disabledStatesRef: Ref<Map<string, boolean>>
|
|
20
|
+
registeredControlIdsRef: Ref<Map<string, string>>
|
|
21
|
+
getParentProps: () => {
|
|
22
|
+
'id': string | undefined
|
|
23
|
+
'indeterminate': boolean
|
|
24
|
+
'checked': boolean
|
|
25
|
+
'aria-controls': string
|
|
26
|
+
'onCheckedChange': (
|
|
27
|
+
checked: boolean,
|
|
28
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
29
|
+
) => void
|
|
30
|
+
}
|
|
31
|
+
getChildProps: (value: string) => {
|
|
32
|
+
checked: boolean
|
|
33
|
+
onCheckedChange: (
|
|
34
|
+
checked: boolean,
|
|
35
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
36
|
+
) => void
|
|
37
|
+
}
|
|
38
|
+
registerChildControlId: (value: string, id: string | undefined) => void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function useCheckboxGroupParent(
|
|
42
|
+
params: UseCheckboxGroupParentParameters,
|
|
43
|
+
): UseCheckboxGroupParentReturnValue {
|
|
44
|
+
const { allValues, value, onValueChange } = params
|
|
45
|
+
const resolvedAllValues = computed(() => allValues.value ?? [])
|
|
46
|
+
|
|
47
|
+
const uncontrolledStateRef = ref<string[]>(value.value)
|
|
48
|
+
const disabledStatesRef = ref(new Map<string, boolean>())
|
|
49
|
+
const registeredControlIdsRef = ref(new Map<string, string>())
|
|
50
|
+
const status = ref<'on' | 'off' | 'mixed'>('mixed')
|
|
51
|
+
|
|
52
|
+
const id = useBaseUiId()
|
|
53
|
+
const checked = computed(() => value.value.length === resolvedAllValues.value.length)
|
|
54
|
+
const indeterminate = computed(() =>
|
|
55
|
+
value.value.length !== resolvedAllValues.value.length && value.value.length > 0,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
watch(
|
|
59
|
+
() => value.value.slice(),
|
|
60
|
+
(nextValue) => {
|
|
61
|
+
if (nextValue.length > 0 && nextValue.length < resolvedAllValues.value.length) {
|
|
62
|
+
uncontrolledStateRef.value = nextValue
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{ flush: 'sync' },
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
function getParentProps() {
|
|
69
|
+
return {
|
|
70
|
+
id,
|
|
71
|
+
'indeterminate': indeterminate.value,
|
|
72
|
+
'checked': checked.value,
|
|
73
|
+
'aria-controls': resolvedAllValues.value
|
|
74
|
+
.map(item => registeredControlIdsRef.value.get(item))
|
|
75
|
+
.filter((item): item is string => Boolean(item))
|
|
76
|
+
.join(' '),
|
|
77
|
+
onCheckedChange(
|
|
78
|
+
_nextChecked: boolean,
|
|
79
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
80
|
+
) {
|
|
81
|
+
const uncontrolledState = uncontrolledStateRef.value
|
|
82
|
+
|
|
83
|
+
const none = resolvedAllValues.value.filter(
|
|
84
|
+
(item: string) =>
|
|
85
|
+
disabledStatesRef.value.get(item) && uncontrolledState.includes(item),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const all = resolvedAllValues.value.filter(
|
|
89
|
+
(item: string) =>
|
|
90
|
+
!disabledStatesRef.value.get(item)
|
|
91
|
+
|| (disabledStatesRef.value.get(item) && uncontrolledState.includes(item)),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
const allOnOrOff
|
|
95
|
+
= uncontrolledState.length === all.length || uncontrolledState.length === 0
|
|
96
|
+
|
|
97
|
+
if (allOnOrOff) {
|
|
98
|
+
if (value.value.length === all.length) {
|
|
99
|
+
onValueChange(none, eventDetails)
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
onValueChange(all, eventDetails)
|
|
103
|
+
}
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (status.value === 'mixed') {
|
|
108
|
+
onValueChange(all, eventDetails)
|
|
109
|
+
status.value = 'on'
|
|
110
|
+
}
|
|
111
|
+
else if (status.value === 'on') {
|
|
112
|
+
onValueChange(none, eventDetails)
|
|
113
|
+
status.value = 'off'
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
onValueChange(uncontrolledState, eventDetails)
|
|
117
|
+
status.value = 'mixed'
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getChildProps(childValue: string) {
|
|
124
|
+
return {
|
|
125
|
+
checked: value.value.includes(childValue),
|
|
126
|
+
onCheckedChange(
|
|
127
|
+
nextChecked: boolean,
|
|
128
|
+
eventDetails: BaseUIChangeEventDetails<typeof REASONS.none>,
|
|
129
|
+
) {
|
|
130
|
+
const newValue = value.value.slice()
|
|
131
|
+
|
|
132
|
+
if (nextChecked) {
|
|
133
|
+
if (!newValue.includes(childValue)) {
|
|
134
|
+
newValue.push(childValue)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const index = newValue.indexOf(childValue)
|
|
139
|
+
if (index !== -1) {
|
|
140
|
+
newValue.splice(index, 1)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
uncontrolledStateRef.value = newValue
|
|
145
|
+
onValueChange(newValue, eventDetails)
|
|
146
|
+
status.value = 'mixed'
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function registerChildControlId(value: string, id: string | undefined) {
|
|
152
|
+
const nextRegisteredControlIds = new Map(registeredControlIdsRef.value)
|
|
153
|
+
|
|
154
|
+
if (id === undefined) {
|
|
155
|
+
nextRegisteredControlIds.delete(value)
|
|
156
|
+
registeredControlIdsRef.value = nextRegisteredControlIds
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
nextRegisteredControlIds.set(value, id)
|
|
161
|
+
registeredControlIdsRef.value = nextRegisteredControlIds
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
id,
|
|
166
|
+
indeterminate,
|
|
167
|
+
disabledStatesRef,
|
|
168
|
+
registeredControlIdsRef,
|
|
169
|
+
getParentProps,
|
|
170
|
+
getChildProps,
|
|
171
|
+
registerChildControlId,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { Ref } from 'vue'
|
|
|
3
3
|
import type { CollapsibleChangeEventDetails } from '../collapsible.types'
|
|
4
4
|
import type { AnimationType, Dimensions } from '../root/useCollapsibleRoot'
|
|
5
5
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
6
|
+
import { AccordionRootDataAttributes } from '../../accordion/root/AccordionRootDataAttributes'
|
|
6
7
|
import { createChangeEventDetails } from '../../utils/createBaseUIEventDetails'
|
|
7
8
|
import { REASONS } from '../../utils/reasons'
|
|
8
9
|
import {
|
|
@@ -167,7 +168,7 @@ export function useCollapsiblePanel(
|
|
|
167
168
|
* Setting both to `0px` will break layout.
|
|
168
169
|
*/
|
|
169
170
|
if (
|
|
170
|
-
element.getAttribute(
|
|
171
|
+
element.getAttribute(AccordionRootDataAttributes.orientation) === 'horizontal'
|
|
171
172
|
|| panelStyles.transitionProperty.includes('width')
|
|
172
173
|
) {
|
|
173
174
|
transitionDimensionRef.value = 'width'
|
|
@@ -109,7 +109,9 @@ export function useCollapsibleRoot(
|
|
|
109
109
|
|
|
110
110
|
const { value: open, setValue: setOpen } = useControllableState<boolean>({
|
|
111
111
|
controlled: () => (isControlled.value ? openParam?.() : undefined),
|
|
112
|
-
default: defaultOpen,
|
|
112
|
+
default: () => defaultOpen,
|
|
113
|
+
name: 'CollapsibleRoot',
|
|
114
|
+
state: 'open',
|
|
113
115
|
})
|
|
114
116
|
|
|
115
117
|
const { mounted, setMounted, transitionStatus } = useTransitionStatus(
|
|
@@ -25,6 +25,8 @@ export const ARROW_LEFT = 'ArrowLeft'
|
|
|
25
25
|
export const ARROW_RIGHT = 'ArrowRight'
|
|
26
26
|
export const HOME = 'Home'
|
|
27
27
|
export const END = 'End'
|
|
28
|
+
export const PAGE_UP = 'PageUp'
|
|
29
|
+
export const PAGE_DOWN = 'PageDown'
|
|
28
30
|
|
|
29
31
|
export const HORIZONTAL_KEYS = new Set([ARROW_LEFT, ARROW_RIGHT])
|
|
30
32
|
export const HORIZONTAL_KEYS_WITH_EXTRA_KEYS = new Set([
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts" generic="Metadata, State extends Record<string, any> = Record<string, any>">
|
|
2
2
|
import type { StateAttributesMapping } from '../../utils/getStateAttributesProps'
|
|
3
3
|
import type { BaseUIComponentProps } from '../../utils/types'
|
|
4
|
+
import type { RenderRef } from '../../utils/useRenderElement'
|
|
4
5
|
import { computed, useAttrs } from 'vue'
|
|
6
|
+
import { mergeProps } from '../../merge-props/mergeProps'
|
|
5
7
|
import { EMPTY_OBJECT } from '../../utils/constants'
|
|
8
|
+
import { useMergedRefs } from '../../utils/useMergedRefs'
|
|
6
9
|
import { useRenderElement } from '../../utils/useRenderElement'
|
|
7
10
|
import { useCompositeItem } from './useCompositeItem'
|
|
8
11
|
|
|
@@ -13,7 +16,7 @@ export interface CompositeItemProps<Metadata, State extends Record<string, any>>
|
|
|
13
16
|
as?: string | any
|
|
14
17
|
style?: BaseUIComponentProps<State>['style']
|
|
15
18
|
metadata?: Metadata
|
|
16
|
-
refs?:
|
|
19
|
+
refs?: RenderRef[]
|
|
17
20
|
props?: Array<Record<string, any> | (() => Record<string, any>)>
|
|
18
21
|
stateAttributesMapping?: StateAttributesMapping<State>
|
|
19
22
|
state?: State
|
|
@@ -41,15 +44,11 @@ const externalProps = computed(() => {
|
|
|
41
44
|
if (props.props) {
|
|
42
45
|
props.props.forEach((prop) => {
|
|
43
46
|
const p = typeof prop === 'function' ? prop() : prop
|
|
44
|
-
externalProps =
|
|
47
|
+
externalProps = mergeProps(externalProps, p)
|
|
45
48
|
})
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
return
|
|
49
|
-
...attrs,
|
|
50
|
-
...externalProps,
|
|
51
|
-
...compositeProps.value,
|
|
52
|
-
}
|
|
51
|
+
return mergeProps(attrs, externalProps, compositeProps.value)
|
|
53
52
|
})
|
|
54
53
|
|
|
55
54
|
const {
|
|
@@ -63,7 +62,7 @@ const {
|
|
|
63
62
|
props: externalProps,
|
|
64
63
|
stateAttributesMapping: props.stateAttributesMapping,
|
|
65
64
|
defaultTagName: 'div',
|
|
66
|
-
ref: compositeRef,
|
|
65
|
+
ref: props.refs?.length ? useMergedRefs(...props.refs, compositeRef) : compositeRef,
|
|
67
66
|
})
|
|
68
67
|
</script>
|
|
69
68
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { StateAttributesMapping } from '../../utils/getStateAttributesProps'
|
|
2
3
|
import type { BaseUIComponentProps } from '../../utils/types'
|
|
3
4
|
import type { Dimensions, ModifierKey } from '../composite'
|
|
4
5
|
import type { CompositeMetadata } from '../list/CompositeList.vue'
|
|
@@ -10,6 +11,8 @@ import { compositeRootContextKey } from './CompositeRootContext'
|
|
|
10
11
|
import { useCompositeRoot } from './useCompositeRoot'
|
|
11
12
|
|
|
12
13
|
export interface CompositeRootProps extends BaseUIComponentProps<any> {
|
|
14
|
+
state?: Record<string, any>
|
|
15
|
+
stateAttributesMapping?: StateAttributesMapping<any>
|
|
13
16
|
orientation?: 'horizontal' | 'vertical' | 'both'
|
|
14
17
|
cols?: number
|
|
15
18
|
loopFocus?: boolean
|
|
@@ -46,6 +49,13 @@ const props = withDefaults(defineProps<CompositeRootProps>(), {
|
|
|
46
49
|
})
|
|
47
50
|
|
|
48
51
|
const emit = defineEmits<CompositeRootEmits>()
|
|
52
|
+
defineSlots<{
|
|
53
|
+
default?: (props: {
|
|
54
|
+
ref?: ((el: Element | null) => void)
|
|
55
|
+
props?: Record<string, any>
|
|
56
|
+
state: Record<string, any>
|
|
57
|
+
}) => any
|
|
58
|
+
}>()
|
|
49
59
|
const attrs = useAttrs()
|
|
50
60
|
const direction = useDirection()
|
|
51
61
|
|
|
@@ -77,7 +87,7 @@ provide(
|
|
|
77
87
|
}),
|
|
78
88
|
)
|
|
79
89
|
|
|
80
|
-
const state = computed(() =>
|
|
90
|
+
const state = computed(() => props.state ?? {})
|
|
81
91
|
|
|
82
92
|
const {
|
|
83
93
|
tag,
|
|
@@ -88,6 +98,7 @@ const {
|
|
|
88
98
|
componentProps: props,
|
|
89
99
|
state,
|
|
90
100
|
props: computed(() => root.getRootProps(attrs)),
|
|
101
|
+
stateAttributesMapping: props.stateAttributesMapping,
|
|
91
102
|
defaultTagName: 'div',
|
|
92
103
|
ref: root.mergedRef,
|
|
93
104
|
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from 'vue'
|
|
2
|
+
import { computed, inject } from 'vue'
|
|
3
|
+
|
|
4
|
+
export interface CSPContextValue {
|
|
5
|
+
nonce: Readonly<Ref<string | undefined>>
|
|
6
|
+
disableStyleElements: Readonly<Ref<boolean>>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export const cspContextKey = Symbol(
|
|
13
|
+
'CSPContext',
|
|
14
|
+
) as InjectionKey<CSPContextValue>
|
|
15
|
+
|
|
16
|
+
const DEFAULT_CSP_CONTEXT_VALUE: CSPContextValue = {
|
|
17
|
+
nonce: computed(() => undefined),
|
|
18
|
+
disableStyleElements: computed(() => false),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export function useCSPContext(): CSPContextValue {
|
|
25
|
+
return inject(cspContextKey, DEFAULT_CSP_CONTEXT_VALUE)
|
|
26
|
+
}
|