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.
Files changed (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/button/Button.cjs +524 -0
  4. package/dist/button/Button.cjs.map +1 -0
  5. package/dist/button/Button.js +453 -0
  6. package/dist/button/Button.js.map +1 -0
  7. package/dist/composite/composite.cjs +56 -0
  8. package/dist/composite/composite.cjs.map +1 -0
  9. package/dist/composite/composite.js +21 -0
  10. package/dist/composite/composite.js.map +1 -0
  11. package/dist/control/FieldControl.cjs +576 -0
  12. package/dist/control/FieldControl.cjs.map +1 -0
  13. package/dist/control/FieldControl.js +511 -0
  14. package/dist/control/FieldControl.js.map +1 -0
  15. package/dist/control/FieldControlDataAttributes.cjs +42 -0
  16. package/dist/control/FieldControlDataAttributes.cjs.map +1 -0
  17. package/dist/control/FieldControlDataAttributes.js +36 -0
  18. package/dist/control/FieldControlDataAttributes.js.map +1 -0
  19. package/dist/description/FieldDescription.cjs +86 -0
  20. package/dist/description/FieldDescription.cjs.map +1 -0
  21. package/dist/description/FieldDescription.js +81 -0
  22. package/dist/description/FieldDescription.js.map +1 -0
  23. package/dist/direction-provider/DirectionContext.cjs +26 -0
  24. package/dist/direction-provider/DirectionContext.cjs.map +1 -0
  25. package/dist/direction-provider/DirectionContext.js +15 -0
  26. package/dist/direction-provider/DirectionContext.js.map +1 -0
  27. package/dist/direction-provider/DirectionProvider.cjs +37 -0
  28. package/dist/direction-provider/DirectionProvider.cjs.map +1 -0
  29. package/dist/direction-provider/DirectionProvider.js +32 -0
  30. package/dist/direction-provider/DirectionProvider.js.map +1 -0
  31. package/dist/error/FieldError.cjs +414 -0
  32. package/dist/error/FieldError.cjs.map +1 -0
  33. package/dist/error/FieldError.js +373 -0
  34. package/dist/error/FieldError.js.map +1 -0
  35. package/dist/fallback/AvatarFallback.cjs +165 -0
  36. package/dist/fallback/AvatarFallback.cjs.map +1 -0
  37. package/dist/fallback/AvatarFallback.js +136 -0
  38. package/dist/fallback/AvatarFallback.js.map +1 -0
  39. package/dist/form/Form.cjs +159 -0
  40. package/dist/form/Form.cjs.map +1 -0
  41. package/dist/form/Form.js +154 -0
  42. package/dist/form/Form.js.map +1 -0
  43. package/dist/header/AccordionHeader.cjs +189 -0
  44. package/dist/header/AccordionHeader.cjs.map +1 -0
  45. package/dist/header/AccordionHeader.js +148 -0
  46. package/dist/header/AccordionHeader.js.map +1 -0
  47. package/dist/image/AvatarImage.cjs +150 -0
  48. package/dist/image/AvatarImage.cjs.map +1 -0
  49. package/dist/image/AvatarImage.js +145 -0
  50. package/dist/image/AvatarImage.js.map +1 -0
  51. package/dist/image/AvatarImageDataAttributes.cjs +26 -0
  52. package/dist/image/AvatarImageDataAttributes.cjs.map +1 -0
  53. package/dist/image/AvatarImageDataAttributes.js +20 -0
  54. package/dist/image/AvatarImageDataAttributes.js.map +1 -0
  55. package/dist/index.cjs +64 -0
  56. package/dist/index.d.cts +1501 -0
  57. package/dist/index.d.cts.map +1 -0
  58. package/dist/index.d.ts +1501 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +15 -0
  61. package/dist/index2.cjs +2767 -0
  62. package/dist/index2.cjs.map +1 -0
  63. package/dist/index2.js +2618 -0
  64. package/dist/index2.js.map +1 -0
  65. package/package.json +77 -0
  66. package/src/accordion/accordion.types.ts +126 -0
  67. package/src/accordion/header/AccordionHeader.vue +36 -0
  68. package/src/accordion/index.ts +10 -0
  69. package/src/accordion/item/AccordionItem.vue +124 -0
  70. package/src/accordion/item/AccordionItemContext.ts +24 -0
  71. package/src/accordion/item/AccordionItemDataAttributes.ts +15 -0
  72. package/src/accordion/item/stateAttributesMapping.ts +14 -0
  73. package/src/accordion/panel/AccordionPanel.vue +156 -0
  74. package/src/accordion/panel/AccordionPanelCssVars.ts +12 -0
  75. package/src/accordion/root/AccordionRoot.vue +130 -0
  76. package/src/accordion/root/AccordionRootContext.ts +37 -0
  77. package/src/accordion/root/AccordionRootDataAttributes.ts +10 -0
  78. package/src/accordion/root/stateAttributesMapping.ts +6 -0
  79. package/src/accordion/trigger/AccordionTrigger.vue +186 -0
  80. package/src/avatar/fallback/AvatarFallback.vue +75 -0
  81. package/src/avatar/image/AvatarImage.vue +103 -0
  82. package/src/avatar/image/AvatarImageDataAttributes.ts +14 -0
  83. package/src/avatar/image/useImageLoadingStatus.ts +58 -0
  84. package/src/avatar/index.ts +19 -0
  85. package/src/avatar/root/AvatarRoot.vue +62 -0
  86. package/src/avatar/root/AvatarRootContext.ts +22 -0
  87. package/src/avatar/root/stateAttributesMapping.ts +7 -0
  88. package/src/button/Button.vue +59 -0
  89. package/src/button/ButtonDataAttributes.ts +6 -0
  90. package/src/button/button.types.ts +22 -0
  91. package/src/button/index.ts +2 -0
  92. package/src/collapsible/collapsible.types.ts +64 -0
  93. package/src/collapsible/index.ts +6 -0
  94. package/src/collapsible/panel/CollapsiblePanel.vue +145 -0
  95. package/src/collapsible/panel/CollapsiblePanelCssVars.ts +12 -0
  96. package/src/collapsible/panel/CollapsiblePanelDataAttributes.ts +18 -0
  97. package/src/collapsible/panel/useCollapsiblePanel.ts +489 -0
  98. package/src/collapsible/root/CollapsibleRoot.vue +60 -0
  99. package/src/collapsible/root/CollapsibleRootContext.ts +18 -0
  100. package/src/collapsible/root/stateAttributesMapping.ts +9 -0
  101. package/src/collapsible/root/useCollapsibleRoot.ts +252 -0
  102. package/src/collapsible/trigger/CollapsibleTrigger.vue +63 -0
  103. package/src/collapsible/trigger/CollapsibleTriggerDataAttributes.ts +6 -0
  104. package/src/composite/composite.ts +232 -0
  105. package/src/composite/constants.ts +1 -0
  106. package/src/composite/item/CompositeItem.vue +75 -0
  107. package/src/composite/item/useCompositeItem.ts +63 -0
  108. package/src/composite/list/CompositeList.vue +168 -0
  109. package/src/composite/list/CompositeListContext.ts +21 -0
  110. package/src/composite/list/useCompositeListItem.ts +130 -0
  111. package/src/composite/root/CompositeRoot.vue +106 -0
  112. package/src/composite/root/CompositeRootContext.ts +36 -0
  113. package/src/composite/root/index.ts +7 -0
  114. package/src/composite/root/useCompositeRoot.ts +418 -0
  115. package/src/direction-provider/DirectionContext.ts +29 -0
  116. package/src/direction-provider/DirectionProvider.vue +31 -0
  117. package/src/direction-provider/index.ts +8 -0
  118. package/src/field/control/FieldControl.vue +211 -0
  119. package/src/field/control/FieldControlDataAttributes.ts +30 -0
  120. package/src/field/description/FieldDescription.vue +62 -0
  121. package/src/field/description/FieldDescriptionDataAttributes.ts +30 -0
  122. package/src/field/error/FieldError.vue +159 -0
  123. package/src/field/error/FieldErrorDataAttributes.ts +38 -0
  124. package/src/field/index.ts +27 -0
  125. package/src/field/item/FieldItem.vue +63 -0
  126. package/src/field/item/FieldItemContext.ts +16 -0
  127. package/src/field/label/FieldLabel.vue +102 -0
  128. package/src/field/label/FieldLabelDataAttributes.ts +30 -0
  129. package/src/field/root/FieldRoot.vue +262 -0
  130. package/src/field/root/FieldRootContext.ts +97 -0
  131. package/src/field/root/FieldRootDataAttributes.ts +30 -0
  132. package/src/field/root/useFieldRootState.ts +81 -0
  133. package/src/field/root/useFieldValidation.ts +298 -0
  134. package/src/field/root/useFieldValidity.ts +30 -0
  135. package/src/field/useField.ts +73 -0
  136. package/src/field/utils/constants.ts +45 -0
  137. package/src/field/utils/getCombinedFieldValidityData.ts +18 -0
  138. package/src/field/validity/FieldValidity.vue +36 -0
  139. package/src/fieldset/index.ts +8 -0
  140. package/src/fieldset/legend/FieldsetLegend.vue +72 -0
  141. package/src/fieldset/root/FieldsetRoot.vue +74 -0
  142. package/src/fieldset/root/FieldsetRootContext.ts +26 -0
  143. package/src/floating-ui-vue/types.ts +4 -0
  144. package/src/floating-ui-vue/utils/composite.ts +475 -0
  145. package/src/floating-ui-vue/utils/constants.ts +4 -0
  146. package/src/floating-ui-vue/utils/event.ts +4 -0
  147. package/src/floating-ui-vue/utils.ts +2 -0
  148. package/src/form/Form.vue +188 -0
  149. package/src/form/FormContext.ts +59 -0
  150. package/src/form/index.ts +10 -0
  151. package/src/index.ts +14 -0
  152. package/src/labelable-provider/LabelableContext.ts +33 -0
  153. package/src/labelable-provider/LabelableProvider.vue +55 -0
  154. package/src/labelable-provider/index.ts +6 -0
  155. package/src/labelable-provider/useAriaLabelledBy.ts +100 -0
  156. package/src/labelable-provider/useLabelableId.ts +30 -0
  157. package/src/merge-props/index.ts +1 -0
  158. package/src/merge-props/mergeProps.ts +192 -0
  159. package/src/test/index.ts +1 -0
  160. package/src/test/utils.ts +9 -0
  161. package/src/types/index.ts +10 -0
  162. package/src/use-button/index.ts +1 -0
  163. package/src/use-button/useButton.ts +231 -0
  164. package/src/use-render/index.ts +1 -0
  165. package/src/use-render/useRender.spec.ts +90 -0
  166. package/src/use-render/useRender.ts +152 -0
  167. package/src/utils/collapsibleOpenStateMapping.ts +33 -0
  168. package/src/utils/constants.ts +1 -0
  169. package/src/utils/createBaseUIEventDetails.ts +127 -0
  170. package/src/utils/empty.ts +5 -0
  171. package/src/utils/error.ts +19 -0
  172. package/src/utils/getStateAttributesProps.ts +31 -0
  173. package/src/utils/isElementDisabled.ts +7 -0
  174. package/src/utils/noop.ts +1 -0
  175. package/src/utils/reasons.ts +69 -0
  176. package/src/utils/resolveRef.ts +9 -0
  177. package/src/utils/slot.ts +6 -0
  178. package/src/utils/stateAttributesMapping.ts +28 -0
  179. package/src/utils/transitionStatusMapping.ts +22 -0
  180. package/src/utils/types.ts +47 -0
  181. package/src/utils/useAnimationFrame.ts +130 -0
  182. package/src/utils/useAnimationsFinished.ts +101 -0
  183. package/src/utils/useBaseUiId.ts +9 -0
  184. package/src/utils/useControllableState.ts +44 -0
  185. package/src/utils/useFocusableWhenDisabled.ts +85 -0
  186. package/src/utils/useId.ts +26 -0
  187. package/src/utils/useMergedRefs.ts +91 -0
  188. package/src/utils/useOpenChangeComplete.ts +52 -0
  189. package/src/utils/useRenderElement.ts +162 -0
  190. package/src/utils/useTimeout.ts +48 -0
  191. package/src/utils/useTransitionStatus.ts +104 -0
  192. 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,6 @@
1
+ export enum ButtonDataAttributes {
2
+ /**
3
+ * Present when the button is disabled.
4
+ */
5
+ disabled = 'data-disabled',
6
+ }
@@ -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,2 @@
1
+ export type * from './button.types'
2
+ export { default as Button } from './Button.vue'
@@ -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
+ }