base-ui-vue 0.1.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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/button/Button.cjs +524 -0
- package/dist/button/Button.cjs.map +1 -0
- package/dist/button/Button.js +453 -0
- package/dist/button/Button.js.map +1 -0
- package/dist/composite/composite.cjs +56 -0
- package/dist/composite/composite.cjs.map +1 -0
- package/dist/composite/composite.js +21 -0
- package/dist/composite/composite.js.map +1 -0
- package/dist/control/FieldControl.cjs +576 -0
- package/dist/control/FieldControl.cjs.map +1 -0
- package/dist/control/FieldControl.js +511 -0
- package/dist/control/FieldControl.js.map +1 -0
- package/dist/control/FieldControlDataAttributes.cjs +42 -0
- package/dist/control/FieldControlDataAttributes.cjs.map +1 -0
- package/dist/control/FieldControlDataAttributes.js +36 -0
- package/dist/control/FieldControlDataAttributes.js.map +1 -0
- package/dist/description/FieldDescription.cjs +86 -0
- package/dist/description/FieldDescription.cjs.map +1 -0
- package/dist/description/FieldDescription.js +81 -0
- package/dist/description/FieldDescription.js.map +1 -0
- package/dist/direction-provider/DirectionContext.cjs +26 -0
- package/dist/direction-provider/DirectionContext.cjs.map +1 -0
- package/dist/direction-provider/DirectionContext.js +15 -0
- package/dist/direction-provider/DirectionContext.js.map +1 -0
- package/dist/direction-provider/DirectionProvider.cjs +37 -0
- package/dist/direction-provider/DirectionProvider.cjs.map +1 -0
- package/dist/direction-provider/DirectionProvider.js +32 -0
- package/dist/direction-provider/DirectionProvider.js.map +1 -0
- package/dist/error/FieldError.cjs +414 -0
- package/dist/error/FieldError.cjs.map +1 -0
- package/dist/error/FieldError.js +373 -0
- package/dist/error/FieldError.js.map +1 -0
- package/dist/fallback/AvatarFallback.cjs +165 -0
- package/dist/fallback/AvatarFallback.cjs.map +1 -0
- package/dist/fallback/AvatarFallback.js +136 -0
- package/dist/fallback/AvatarFallback.js.map +1 -0
- package/dist/form/Form.cjs +159 -0
- package/dist/form/Form.cjs.map +1 -0
- package/dist/form/Form.js +154 -0
- package/dist/form/Form.js.map +1 -0
- package/dist/header/AccordionHeader.cjs +189 -0
- package/dist/header/AccordionHeader.cjs.map +1 -0
- package/dist/header/AccordionHeader.js +148 -0
- package/dist/header/AccordionHeader.js.map +1 -0
- package/dist/image/AvatarImage.cjs +150 -0
- package/dist/image/AvatarImage.cjs.map +1 -0
- package/dist/image/AvatarImage.js +145 -0
- package/dist/image/AvatarImage.js.map +1 -0
- package/dist/image/AvatarImageDataAttributes.cjs +26 -0
- package/dist/image/AvatarImageDataAttributes.cjs.map +1 -0
- package/dist/image/AvatarImageDataAttributes.js +20 -0
- package/dist/image/AvatarImageDataAttributes.js.map +1 -0
- package/dist/index.cjs +64 -0
- package/dist/index.d.cts +1501 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1501 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index2.cjs +2767 -0
- package/dist/index2.cjs.map +1 -0
- package/dist/index2.js +2618 -0
- package/dist/index2.js.map +1 -0
- package/package.json +77 -0
- package/src/accordion/accordion.types.ts +126 -0
- package/src/accordion/header/AccordionHeader.vue +36 -0
- package/src/accordion/index.ts +10 -0
- package/src/accordion/item/AccordionItem.vue +124 -0
- package/src/accordion/item/AccordionItemContext.ts +24 -0
- package/src/accordion/item/AccordionItemDataAttributes.ts +15 -0
- package/src/accordion/item/stateAttributesMapping.ts +14 -0
- package/src/accordion/panel/AccordionPanel.vue +156 -0
- package/src/accordion/panel/AccordionPanelCssVars.ts +12 -0
- package/src/accordion/root/AccordionRoot.vue +130 -0
- package/src/accordion/root/AccordionRootContext.ts +37 -0
- package/src/accordion/root/AccordionRootDataAttributes.ts +10 -0
- package/src/accordion/root/stateAttributesMapping.ts +6 -0
- package/src/accordion/trigger/AccordionTrigger.vue +186 -0
- package/src/avatar/fallback/AvatarFallback.vue +75 -0
- package/src/avatar/image/AvatarImage.vue +103 -0
- package/src/avatar/image/AvatarImageDataAttributes.ts +14 -0
- package/src/avatar/image/useImageLoadingStatus.ts +58 -0
- package/src/avatar/index.ts +19 -0
- package/src/avatar/root/AvatarRoot.vue +62 -0
- package/src/avatar/root/AvatarRootContext.ts +22 -0
- package/src/avatar/root/stateAttributesMapping.ts +7 -0
- package/src/button/Button.vue +59 -0
- package/src/button/ButtonDataAttributes.ts +6 -0
- package/src/button/button.types.ts +22 -0
- package/src/button/index.ts +2 -0
- package/src/collapsible/collapsible.types.ts +64 -0
- package/src/collapsible/index.ts +6 -0
- package/src/collapsible/panel/CollapsiblePanel.vue +145 -0
- package/src/collapsible/panel/CollapsiblePanelCssVars.ts +12 -0
- package/src/collapsible/panel/CollapsiblePanelDataAttributes.ts +18 -0
- package/src/collapsible/panel/useCollapsiblePanel.ts +489 -0
- package/src/collapsible/root/CollapsibleRoot.vue +60 -0
- package/src/collapsible/root/CollapsibleRootContext.ts +18 -0
- package/src/collapsible/root/stateAttributesMapping.ts +9 -0
- package/src/collapsible/root/useCollapsibleRoot.ts +252 -0
- package/src/collapsible/trigger/CollapsibleTrigger.vue +63 -0
- package/src/collapsible/trigger/CollapsibleTriggerDataAttributes.ts +6 -0
- package/src/composite/composite.ts +232 -0
- package/src/composite/constants.ts +1 -0
- package/src/composite/item/CompositeItem.vue +75 -0
- package/src/composite/item/useCompositeItem.ts +63 -0
- package/src/composite/list/CompositeList.vue +168 -0
- package/src/composite/list/CompositeListContext.ts +21 -0
- package/src/composite/list/useCompositeListItem.ts +130 -0
- package/src/composite/root/CompositeRoot.vue +106 -0
- package/src/composite/root/CompositeRootContext.ts +36 -0
- package/src/composite/root/index.ts +7 -0
- package/src/composite/root/useCompositeRoot.ts +418 -0
- package/src/direction-provider/DirectionContext.ts +29 -0
- package/src/direction-provider/DirectionProvider.vue +31 -0
- package/src/direction-provider/index.ts +8 -0
- package/src/field/control/FieldControl.vue +211 -0
- package/src/field/control/FieldControlDataAttributes.ts +30 -0
- package/src/field/description/FieldDescription.vue +62 -0
- package/src/field/description/FieldDescriptionDataAttributes.ts +30 -0
- package/src/field/error/FieldError.vue +159 -0
- package/src/field/error/FieldErrorDataAttributes.ts +38 -0
- package/src/field/index.ts +27 -0
- package/src/field/item/FieldItem.vue +63 -0
- package/src/field/item/FieldItemContext.ts +16 -0
- package/src/field/label/FieldLabel.vue +102 -0
- package/src/field/label/FieldLabelDataAttributes.ts +30 -0
- package/src/field/root/FieldRoot.vue +262 -0
- package/src/field/root/FieldRootContext.ts +97 -0
- package/src/field/root/FieldRootDataAttributes.ts +30 -0
- package/src/field/root/useFieldRootState.ts +81 -0
- package/src/field/root/useFieldValidation.ts +298 -0
- package/src/field/root/useFieldValidity.ts +30 -0
- package/src/field/useField.ts +73 -0
- package/src/field/utils/constants.ts +45 -0
- package/src/field/utils/getCombinedFieldValidityData.ts +18 -0
- package/src/field/validity/FieldValidity.vue +36 -0
- package/src/fieldset/index.ts +8 -0
- package/src/fieldset/legend/FieldsetLegend.vue +72 -0
- package/src/fieldset/root/FieldsetRoot.vue +74 -0
- package/src/fieldset/root/FieldsetRootContext.ts +26 -0
- package/src/floating-ui-vue/types.ts +4 -0
- package/src/floating-ui-vue/utils/composite.ts +475 -0
- package/src/floating-ui-vue/utils/constants.ts +4 -0
- package/src/floating-ui-vue/utils/event.ts +4 -0
- package/src/floating-ui-vue/utils.ts +2 -0
- package/src/form/Form.vue +188 -0
- package/src/form/FormContext.ts +59 -0
- package/src/form/index.ts +10 -0
- package/src/index.ts +14 -0
- package/src/labelable-provider/LabelableContext.ts +33 -0
- package/src/labelable-provider/LabelableProvider.vue +55 -0
- package/src/labelable-provider/index.ts +6 -0
- package/src/labelable-provider/useAriaLabelledBy.ts +100 -0
- package/src/labelable-provider/useLabelableId.ts +30 -0
- package/src/merge-props/index.ts +1 -0
- package/src/merge-props/mergeProps.ts +192 -0
- package/src/test/index.ts +1 -0
- package/src/test/utils.ts +9 -0
- package/src/types/index.ts +10 -0
- package/src/use-button/index.ts +1 -0
- package/src/use-button/useButton.ts +231 -0
- package/src/use-render/index.ts +1 -0
- package/src/use-render/useRender.spec.ts +90 -0
- package/src/use-render/useRender.ts +152 -0
- package/src/utils/collapsibleOpenStateMapping.ts +33 -0
- package/src/utils/constants.ts +1 -0
- package/src/utils/createBaseUIEventDetails.ts +127 -0
- package/src/utils/empty.ts +5 -0
- package/src/utils/error.ts +19 -0
- package/src/utils/getStateAttributesProps.ts +31 -0
- package/src/utils/isElementDisabled.ts +7 -0
- package/src/utils/noop.ts +1 -0
- package/src/utils/reasons.ts +69 -0
- package/src/utils/resolveRef.ts +9 -0
- package/src/utils/slot.ts +6 -0
- package/src/utils/stateAttributesMapping.ts +28 -0
- package/src/utils/transitionStatusMapping.ts +22 -0
- package/src/utils/types.ts +47 -0
- package/src/utils/useAnimationFrame.ts +130 -0
- package/src/utils/useAnimationsFinished.ts +101 -0
- package/src/utils/useBaseUiId.ts +9 -0
- package/src/utils/useControllableState.ts +44 -0
- package/src/utils/useFocusableWhenDisabled.ts +85 -0
- package/src/utils/useId.ts +26 -0
- package/src/utils/useMergedRefs.ts +91 -0
- package/src/utils/useOpenChangeComplete.ts +52 -0
- package/src/utils/useRenderElement.ts +162 -0
- package/src/utils/useTimeout.ts +48 -0
- package/src/utils/useTransitionStatus.ts +104 -0
- package/src/utils/warn.ts +15 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { BaseUIComponentProps } from '../../utils/types'
|
|
3
|
+
import { computed, provide, ref, useAttrs } from 'vue'
|
|
4
|
+
import { useRenderElement } from '../../utils/useRenderElement'
|
|
5
|
+
import { AvatarRootContextKey } from './AvatarRootContext'
|
|
6
|
+
import { avatarStateAttributesMapping } from './stateAttributesMapping'
|
|
7
|
+
|
|
8
|
+
export type ImageLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error'
|
|
9
|
+
|
|
10
|
+
export interface AvatarRootState {
|
|
11
|
+
imageLoadingStatus: ImageLoadingStatus
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AvatarRootProps extends BaseUIComponentProps<AvatarRootState> { }
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Displays a user's profile picture, initials, or fallback icon.
|
|
18
|
+
* Renders a \`<span>\` element.
|
|
19
|
+
*
|
|
20
|
+
* Documentation: [Base UI Avatar](https://base-ui-vue.com/components/avatar)
|
|
21
|
+
*/
|
|
22
|
+
defineOptions({
|
|
23
|
+
name: 'AvatarRoot',
|
|
24
|
+
inheritAttrs: false,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const props = withDefaults(defineProps<AvatarRootProps>(), {
|
|
28
|
+
as: 'span',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const attrs = useAttrs()
|
|
32
|
+
|
|
33
|
+
const imageLoadingStatus = ref<ImageLoadingStatus>('idle')
|
|
34
|
+
|
|
35
|
+
provide(AvatarRootContextKey, {
|
|
36
|
+
imageLoadingStatus,
|
|
37
|
+
setImageLoadingStatus: (status: ImageLoadingStatus) => {
|
|
38
|
+
imageLoadingStatus.value = status
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const state = computed<AvatarRootState>(() => ({
|
|
43
|
+
imageLoadingStatus: imageLoadingStatus.value,
|
|
44
|
+
}))
|
|
45
|
+
|
|
46
|
+
const { tag, mergedProps, renderless } = useRenderElement({
|
|
47
|
+
componentProps: props,
|
|
48
|
+
state,
|
|
49
|
+
props: computed(() => ({
|
|
50
|
+
...attrs,
|
|
51
|
+
})),
|
|
52
|
+
stateAttributesMapping: avatarStateAttributesMapping,
|
|
53
|
+
defaultTagName: 'span',
|
|
54
|
+
})
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<slot v-if="renderless" :props="mergedProps" :state="state" />
|
|
59
|
+
<component :is="tag" v-else v-bind="mergedProps">
|
|
60
|
+
<slot :state="state" />
|
|
61
|
+
</component>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from 'vue'
|
|
2
|
+
import type { ImageLoadingStatus } from './AvatarRoot.vue'
|
|
3
|
+
import { inject } from 'vue'
|
|
4
|
+
|
|
5
|
+
export interface AvatarRootContext {
|
|
6
|
+
imageLoadingStatus: Ref<ImageLoadingStatus>
|
|
7
|
+
setImageLoadingStatus: (status: ImageLoadingStatus) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const AvatarRootContextKey: InjectionKey<AvatarRootContext>
|
|
11
|
+
= Symbol('AvatarRootContext')
|
|
12
|
+
|
|
13
|
+
export function useAvatarRootContext() {
|
|
14
|
+
const context = inject(AvatarRootContextKey, undefined)
|
|
15
|
+
if (context === undefined) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'Base UI Vue: AvatarRootContext is missing. Avatar parts must be placed within <AvatarRoot>.',
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return context
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StateAttributesMapping } from '../../utils/getStateAttributesProps'
|
|
2
|
+
import type { AvatarRootState } from './AvatarRoot.vue'
|
|
3
|
+
|
|
4
|
+
export const avatarStateAttributesMapping: StateAttributesMapping<AvatarRootState>
|
|
5
|
+
= {
|
|
6
|
+
imageLoadingStatus: () => null,
|
|
7
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ButtonProps } from './button.types'
|
|
3
|
+
import { computed, useAttrs } from 'vue'
|
|
4
|
+
import { useButton } from '../use-button'
|
|
5
|
+
import { useRenderElement } from '../utils/useRenderElement'
|
|
6
|
+
|
|
7
|
+
defineOptions({
|
|
8
|
+
name: 'BaseUIButton',
|
|
9
|
+
inheritAttrs: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<ButtonProps>(), {
|
|
13
|
+
as: 'button',
|
|
14
|
+
disabled: false,
|
|
15
|
+
focusableWhenDisabled: false,
|
|
16
|
+
nativeButton: undefined,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const attrs = useAttrs()
|
|
20
|
+
|
|
21
|
+
const isNativeButton = computed(() => {
|
|
22
|
+
if (props.nativeButton !== undefined) {
|
|
23
|
+
return props.nativeButton
|
|
24
|
+
}
|
|
25
|
+
return typeof props.as === 'string' && props.as.toLowerCase() === 'button'
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const { getButtonProps, buttonRef } = useButton({
|
|
29
|
+
disabled: () => props.disabled,
|
|
30
|
+
focusableWhenDisabled: () => props.focusableWhenDisabled,
|
|
31
|
+
native: isNativeButton,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const state = computed(() => ({
|
|
35
|
+
disabled: props.disabled,
|
|
36
|
+
}))
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
tag,
|
|
40
|
+
mergedProps,
|
|
41
|
+
renderless,
|
|
42
|
+
ref: renderRef,
|
|
43
|
+
} = useRenderElement({
|
|
44
|
+
componentProps: props,
|
|
45
|
+
state,
|
|
46
|
+
props: computed(() => getButtonProps({
|
|
47
|
+
...attrs,
|
|
48
|
+
})),
|
|
49
|
+
defaultTagName: 'button',
|
|
50
|
+
ref: buttonRef,
|
|
51
|
+
})
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<template>
|
|
55
|
+
<slot v-if="renderless" :ref="renderRef" :props="mergedProps" :state="state" />
|
|
56
|
+
<component :is="tag" v-else :ref="renderRef" v-bind="mergedProps">
|
|
57
|
+
<slot :state="state" />
|
|
58
|
+
</component>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BaseUIComponentProps, NativeButtonProps } from '../utils/types'
|
|
2
|
+
|
|
3
|
+
export interface ButtonState {
|
|
4
|
+
/**
|
|
5
|
+
* Whether the button should ignore user interaction.
|
|
6
|
+
*/
|
|
7
|
+
disabled: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ButtonProps
|
|
11
|
+
extends NativeButtonProps, BaseUIComponentProps<ButtonState> {
|
|
12
|
+
/**
|
|
13
|
+
* Whether the button should ignore user interaction.
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
/**
|
|
18
|
+
* Whether the button should be focusable when disabled.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
focusableWhenDisabled?: boolean
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { BaseUIChangeEventDetails } from '../utils/createBaseUIEventDetails'
|
|
2
|
+
import type { REASONS } from '../utils/reasons'
|
|
3
|
+
import type { BaseUIComponentProps, NativeButtonProps } from '../utils/types'
|
|
4
|
+
import type { TransitionStatus } from '../utils/useTransitionStatus'
|
|
5
|
+
|
|
6
|
+
export type { TransitionStatus }
|
|
7
|
+
|
|
8
|
+
export interface CollapsibleRootState {
|
|
9
|
+
open: boolean
|
|
10
|
+
disabled: boolean
|
|
11
|
+
transitionStatus: TransitionStatus
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type CollapsibleChangeEventReason = typeof REASONS.triggerPress | typeof REASONS.none
|
|
15
|
+
export type CollapsibleChangeEventDetails = BaseUIChangeEventDetails<CollapsibleChangeEventReason>
|
|
16
|
+
|
|
17
|
+
export interface CollapsibleRootProps extends BaseUIComponentProps<CollapsibleRootState> {
|
|
18
|
+
/**
|
|
19
|
+
* Whether the collapsible panel is currently open (controlled).
|
|
20
|
+
*/
|
|
21
|
+
open?: boolean
|
|
22
|
+
/**
|
|
23
|
+
* Whether the collapsible panel is initially open (uncontrolled).
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
defaultOpen?: boolean
|
|
27
|
+
/**
|
|
28
|
+
* Whether the component should ignore user interaction.
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
disabled?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CollapsibleTriggerProps
|
|
35
|
+
extends NativeButtonProps, BaseUIComponentProps<CollapsibleRootState> {
|
|
36
|
+
/**
|
|
37
|
+
* Whether the trigger should ignore user interaction.
|
|
38
|
+
* When undefined, inherits from CollapsibleRoot.
|
|
39
|
+
*/
|
|
40
|
+
disabled?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CollapsiblePanelState extends CollapsibleRootState {
|
|
44
|
+
transitionStatus: TransitionStatus
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface CollapsiblePanelProps extends BaseUIComponentProps<CollapsiblePanelState> {
|
|
48
|
+
/**
|
|
49
|
+
* The `id` attribute of the panel element.
|
|
50
|
+
* When set, overrides the auto-generated panel id.
|
|
51
|
+
*/
|
|
52
|
+
id?: string
|
|
53
|
+
/**
|
|
54
|
+
* Whether to keep the element in the DOM while the panel is hidden.
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
keepMounted?: boolean
|
|
58
|
+
/**
|
|
59
|
+
* Allows the browser's built-in page search to find and expand the panel contents.
|
|
60
|
+
* Overrides the `keepMounted` prop and uses `hidden="until-found"`.
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
hiddenUntilFound?: boolean
|
|
64
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type * from './collapsible.types'
|
|
2
|
+
export { default as CollapsiblePanel } from './panel/CollapsiblePanel.vue'
|
|
3
|
+
export { default as CollapsibleRoot } from './root/CollapsibleRoot.vue'
|
|
4
|
+
export { collapsibleRootContextKey, useCollapsibleRootContext } from './root/CollapsibleRootContext'
|
|
5
|
+
export type { CollapsibleRootContext } from './root/CollapsibleRootContext'
|
|
6
|
+
export { default as CollapsibleTrigger } from './trigger/CollapsibleTrigger.vue'
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CollapsiblePanelProps, CollapsiblePanelState } from '../collapsible.types'
|
|
3
|
+
import { computed, useAttrs, watch } from 'vue'
|
|
4
|
+
import { useOpenChangeComplete } from '../../utils/useOpenChangeComplete'
|
|
5
|
+
import { useRenderElement } from '../../utils/useRenderElement'
|
|
6
|
+
import { warn } from '../../utils/warn'
|
|
7
|
+
import { useCollapsibleRootContext } from '../root/CollapsibleRootContext'
|
|
8
|
+
import { collapsibleStateAttributesMapping } from '../root/stateAttributesMapping'
|
|
9
|
+
import { CollapsiblePanelCssVars } from './CollapsiblePanelCssVars'
|
|
10
|
+
import { useCollapsiblePanel } from './useCollapsiblePanel'
|
|
11
|
+
|
|
12
|
+
defineOptions({
|
|
13
|
+
name: 'CollapsiblePanel',
|
|
14
|
+
inheritAttrs: false,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(defineProps<CollapsiblePanelProps>(), {
|
|
18
|
+
as: 'div',
|
|
19
|
+
keepMounted: false,
|
|
20
|
+
hiddenUntilFound: false,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const attrs = useAttrs()
|
|
24
|
+
|
|
25
|
+
const ctx = useCollapsibleRootContext()
|
|
26
|
+
|
|
27
|
+
watch(
|
|
28
|
+
() => props.hiddenUntilFound,
|
|
29
|
+
val => ctx.setHiddenUntilFound(val),
|
|
30
|
+
{ immediate: true },
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
watch(
|
|
34
|
+
() => props.keepMounted,
|
|
35
|
+
val => ctx.setKeepMounted(val),
|
|
36
|
+
{ immediate: true },
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
watch(
|
|
40
|
+
() => props.id,
|
|
41
|
+
id => ctx.setPanelIdState(id),
|
|
42
|
+
{ immediate: true, flush: 'sync' },
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
46
|
+
watch(
|
|
47
|
+
[() => props.hiddenUntilFound, () => props.keepMounted] as const,
|
|
48
|
+
([hiddenUntilFound, keepMounted]) => {
|
|
49
|
+
if (hiddenUntilFound && keepMounted === false) {
|
|
50
|
+
warn(
|
|
51
|
+
'The `:keep-mounted="false"` prop on a Collapsible will be ignored when using `hidden-until-found` since it requires the Panel to remain mounted even when closed.',
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{ immediate: true },
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { hidden, panelRef } = useCollapsiblePanel({
|
|
60
|
+
abortControllerRef: ctx.abortControllerRef,
|
|
61
|
+
animationTypeRef: ctx.animationTypeRef,
|
|
62
|
+
height: ctx.height,
|
|
63
|
+
hiddenUntilFound: props.hiddenUntilFound,
|
|
64
|
+
id: ctx.panelId,
|
|
65
|
+
keepMounted: props.keepMounted,
|
|
66
|
+
mounted: ctx.mounted,
|
|
67
|
+
onOpenChange: ctx.onOpenChange,
|
|
68
|
+
open: ctx.open,
|
|
69
|
+
panelRef: ctx.panelRef,
|
|
70
|
+
runOnceAnimationsFinish: ctx.runOnceAnimationsFinish,
|
|
71
|
+
setDimensions: ctx.setDimensions,
|
|
72
|
+
setMounted: ctx.setMounted,
|
|
73
|
+
setOpen: ctx.setOpen,
|
|
74
|
+
setVisible: ctx.setVisible,
|
|
75
|
+
transitionDimensionRef: ctx.transitionDimensionRef,
|
|
76
|
+
visible: ctx.visible,
|
|
77
|
+
width: ctx.width,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
useOpenChangeComplete({
|
|
81
|
+
open: computed(() => ctx.open.value && ctx.transitionStatus.value === 'idle'),
|
|
82
|
+
ref: ctx.panelRef,
|
|
83
|
+
onComplete() {
|
|
84
|
+
if (!ctx.open.value) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
ctx.setDimensions({ height: undefined, width: undefined })
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const panelState = computed<CollapsiblePanelState>(() => ({
|
|
92
|
+
...ctx.state.value,
|
|
93
|
+
transitionStatus: ctx.transitionStatus.value,
|
|
94
|
+
}))
|
|
95
|
+
|
|
96
|
+
const shouldRender = computed(() =>
|
|
97
|
+
props.keepMounted || props.hiddenUntilFound || ctx.mounted.value,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const panelProps = computed(() => {
|
|
101
|
+
const heightVal = ctx.height.value
|
|
102
|
+
const widthVal = ctx.width.value
|
|
103
|
+
const resolvedStyle
|
|
104
|
+
= typeof props.style === 'function'
|
|
105
|
+
? props.style(panelState.value)
|
|
106
|
+
: props.style
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...attrs,
|
|
110
|
+
id: ctx.panelId.value,
|
|
111
|
+
hidden: props.hiddenUntilFound ? undefined : (hidden.value ? true : undefined),
|
|
112
|
+
style: [
|
|
113
|
+
resolvedStyle,
|
|
114
|
+
{
|
|
115
|
+
[CollapsiblePanelCssVars.collapsiblePanelHeight]: heightVal === undefined ? 'auto' : `${heightVal}px`,
|
|
116
|
+
[CollapsiblePanelCssVars.collapsiblePanelWidth]: widthVal === undefined ? 'auto' : `${widthVal}px`,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const {
|
|
123
|
+
tag,
|
|
124
|
+
mergedProps,
|
|
125
|
+
renderless,
|
|
126
|
+
ref: renderRef,
|
|
127
|
+
} = useRenderElement({
|
|
128
|
+
componentProps: {
|
|
129
|
+
as: props.as,
|
|
130
|
+
class: props.class,
|
|
131
|
+
},
|
|
132
|
+
state: panelState,
|
|
133
|
+
props: panelProps,
|
|
134
|
+
stateAttributesMapping: collapsibleStateAttributesMapping,
|
|
135
|
+
defaultTagName: 'div',
|
|
136
|
+
ref: panelRef,
|
|
137
|
+
})
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<template>
|
|
141
|
+
<slot v-if="renderless && shouldRender" :ref="renderRef" :props="mergedProps" :state="panelState" />
|
|
142
|
+
<component :is="tag" v-else-if="shouldRender" :ref="renderRef" v-bind="mergedProps">
|
|
143
|
+
<slot :state="panelState" />
|
|
144
|
+
</component>
|
|
145
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export enum CollapsiblePanelCssVars {
|
|
2
|
+
/**
|
|
3
|
+
* The collapsible panel's height.
|
|
4
|
+
* @type {number}
|
|
5
|
+
*/
|
|
6
|
+
collapsiblePanelHeight = '--collapsible-panel-height',
|
|
7
|
+
/**
|
|
8
|
+
* The collapsible panel's width.
|
|
9
|
+
* @type {number}
|
|
10
|
+
*/
|
|
11
|
+
collapsiblePanelWidth = '--collapsible-panel-width',
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export enum CollapsiblePanelDataAttributes {
|
|
2
|
+
/**
|
|
3
|
+
* Present when the collapsible panel is open.
|
|
4
|
+
*/
|
|
5
|
+
open = 'data-open',
|
|
6
|
+
/**
|
|
7
|
+
* Present when the collapsible panel is closed.
|
|
8
|
+
*/
|
|
9
|
+
closed = 'data-closed',
|
|
10
|
+
/**
|
|
11
|
+
* Present when the panel is animating in.
|
|
12
|
+
*/
|
|
13
|
+
startingStyle = 'data-starting-style',
|
|
14
|
+
/**
|
|
15
|
+
* Present when the panel is animating out.
|
|
16
|
+
*/
|
|
17
|
+
endingStyle = 'data-ending-style',
|
|
18
|
+
}
|