Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 6873032

Browse files
committed
feat: setup zag transition for popover
1 parent fd5c5f3 commit 6873032

File tree

12 files changed

+248
-34
lines changed

12 files changed

+248
-34
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
"@vueuse/core": "4.9.1",
142142
"@vueuse/head": "^0.7.4",
143143
"@vueuse/integrations": "^4.8.1",
144-
"@vueuse/motion": "^1.5.4",
144+
"@vueuse/motion": "^1.6.0",
145145
"@vueuse/shared": "^9.11.1",
146146
"aria-hidden": "^1.1.2",
147147
"axe-core": "^4.1.2",

packages/c-checkbox/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"@chakra-ui/vue-composables": "workspace:*",
3838
"@chakra-ui/vue-system": "workspace:*",
3939
"@chakra-ui/vue-utils": "workspace:*",
40-
"@vueuse/motion": "^1.5.4",
40+
"@vueuse/motion": "^1.6.0",
4141
"@zag-js/checkbox": "0.2.12",
4242
"@zag-js/vue": "0.2.10"
4343
},

packages/c-modal/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"@chakra-ui/vue-composables": "workspace:*",
4545
"@chakra-ui/vue-system": "workspace:*",
4646
"@chakra-ui/vue-utils": "workspace:*",
47-
"@vueuse/motion": "^1.5.4",
47+
"@vueuse/motion": "^1.6.0",
4848
"aria-hidden": "^1.1.2"
4949
},
5050
"devDependencies": {

packages/c-motion/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@chakra-ui/vue-composables": "workspace:*",
3939
"@chakra-ui/vue-system": "workspace:*",
4040
"@chakra-ui/vue-utils": "workspace:*",
41-
"@vueuse/motion": "^1.5.4",
41+
"@vueuse/motion": "^1.6.0",
4242
"@vueuse/shared": "^9.11.1"
4343
},
4444
"devDependencies": {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import { chakra, useColorModeValue } from "../../vue/src"
3+
import {
4+
CPopover,
5+
CPopoverTrigger,
6+
CPopoverContent,
7+
CPopoverArrow,
8+
CPopoverHeader,
9+
CPopoverBody,
10+
} from "../src"
11+
import { CButton } from "../../c-button/src"
12+
</script>
13+
14+
<template>
15+
<c-popover
16+
trigger="hover"
17+
:positioning="{
18+
placement: 'right',
19+
}"
20+
>
21+
<c-popover-trigger>
22+
<c-button> Over over me </c-button>
23+
</c-popover-trigger>
24+
<c-popover-content
25+
px="3"
26+
py="2"
27+
:bg="useColorModeValue('gray.100', 'gray.800').value"
28+
>
29+
<c-popover-arrow />
30+
<c-popover-header>Hover Popover</c-popover-header>
31+
<c-popover-body>
32+
<chakra.div> This is a popover </chakra.div>
33+
</c-popover-body>
34+
</c-popover-content>
35+
</c-popover>
36+
</template>

packages/c-popover/src/c-popover-content.tsx

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
import { defineComponent } from "vue"
1+
import { Transition, computed, defineComponent, watch, watchEffect } from "vue"
22
import { usePopoverContext } from "./popover.context"
33
import { HTMLChakraProps, chakra } from "@chakra-ui/vue-system"
44
import { CPopoverPositioner } from "./c-popover-positioner"
5+
import { CAnimatePresence, TransitionDefaults } from "@chakra-ui/c-motion"
6+
import { MotionDirective, useMotion, useMotions } from "@vueuse/motion"
7+
import { useId } from "@chakra-ui/vue-composables"
8+
import { withDirectives } from "vue"
9+
import { match } from "@chakra-ui/vue-utils"
510

611
export interface CPopoverContentProps extends HTMLChakraProps<"div"> {}
712
export const CPopoverContent = defineComponent({
@@ -10,9 +15,60 @@ export const CPopoverContent = defineComponent({
1015
setup(_, { slots, attrs }) {
1116
const api = usePopoverContext()
1217

18+
const popoverContentProps = computed(() => {
19+
const { ...rest } = { ...attrs, ...api.value.contentProps }
20+
return {
21+
...rest,
22+
...match(api.value.trigger, {
23+
hover: {
24+
async onPointerenter(e: MouseEvent) {
25+
const motions = useMotions()
26+
const instance = motions[api.value.transitionId]
27+
instance.stopTransitions()
28+
api.value.open()
29+
requestAnimationFrame(() => {
30+
api.value.enterTransition(() => null)
31+
})
32+
},
33+
async onPointerleave(e: MouseEvent) {
34+
requestAnimationFrame(() => {
35+
api.value.leaveTransition(() => api.value.close())
36+
})
37+
},
38+
},
39+
click: {},
40+
}),
41+
}
42+
})
43+
1344
return () => (
14-
<CPopoverPositioner {...attrs}>
15-
<chakra.div {...api.value.contentProps}>{slots.default?.()}</chakra.div>
45+
<CPopoverPositioner>
46+
<CAnimatePresence>
47+
{api.value.isOpen &&
48+
withDirectives(
49+
<chakra.div {...popoverContentProps.value}>
50+
{slots.default?.()}
51+
</chakra.div>,
52+
[
53+
[
54+
MotionDirective({
55+
initial: { scale: 0.95, opacity: 0 },
56+
enter: {
57+
scale: 1,
58+
transition: TransitionDefaults.enter,
59+
opacity: 1,
60+
},
61+
leave: {
62+
scale: 0.95,
63+
transition: TransitionDefaults.leave,
64+
opacity: 0,
65+
},
66+
}),
67+
api.value.transitionId,
68+
],
69+
]
70+
)}
71+
</CAnimatePresence>
1672
</CPopoverPositioner>
1773
)
1874
},
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { defineComponent } from "vue"
22
import { usePopoverContext } from "./popover.context"
33
import { HTMLChakraProps, chakra } from "@chakra-ui/vue-system"
4-
import { CMotion } from "@chakra-ui/c-motion"
54

65
export interface CPopoverPositionerProps extends HTMLChakraProps<"div"> {}
76
export const CPopoverPositioner = defineComponent({
87
name: "CPopoverPositioner",
8+
inheritAttrs: false,
99
setup(_, { slots, attrs }) {
1010
const api = usePopoverContext()
1111

12-
return () => (
13-
<chakra.div {...api.value.positionerProps}>
14-
<CMotion type={"scale"}>
15-
{api.value.isOpen && (
16-
<chakra.div {...attrs}>{slots.default?.()}</chakra.div>
17-
)}
18-
</CMotion>
19-
</chakra.div>
20-
)
12+
return () => {
13+
const { style, ...positionerProps } = api.value.positionerProps
14+
const { opacity, ...styleProps } = style
15+
return (
16+
<chakra.div {...positionerProps} style={styleProps}>
17+
<chakra.div {...attrs}>{slots.default?.()}</chakra.div>
18+
</chakra.div>
19+
)
20+
}
2121
},
2222
})

packages/c-popover/src/c-popover-trigger.tsx

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,49 @@
1-
import { defineComponent } from "vue"
1+
import { computed, defineComponent, watchEffect } from "vue"
22
import { usePopoverContext } from "./popover.context"
3-
import { withSingleton } from "@chakra-ui/vue-utils"
3+
import { match, withSingleton } from "@chakra-ui/vue-utils"
44

55
export const CPopoverTrigger = defineComponent({
66
name: "CPopoverTrigger",
77
setup(_, { slots, attrs }) {
88
const api = usePopoverContext()
99

10+
const popoverTriggerProps = computed(() => {
11+
const { onClick, ...rest } = api.value.triggerProps
12+
return {
13+
...rest,
14+
...match(api.value.trigger, {
15+
click: {
16+
async onClick(e: MouseEvent) {
17+
if (api.value.isOpen) {
18+
requestAnimationFrame(() => {
19+
api.value.leaveTransition(() => onClick(e))
20+
})
21+
await api.value.wait(300)
22+
} else {
23+
onClick(e)
24+
}
25+
},
26+
},
27+
hover: {
28+
async onPointerenter(e: MouseEvent) {
29+
api.value.open()
30+
requestAnimationFrame(() => {
31+
api.value.enterTransition(() => null)
32+
})
33+
},
34+
async onPointerleave(e: MouseEvent) {
35+
requestAnimationFrame(() => {
36+
api.value.leaveTransition(() => api.value.close())
37+
})
38+
},
39+
},
40+
}),
41+
}
42+
})
43+
1044
return () =>
1145
withSingleton(slots, "CPopoverTrigger", {
12-
...api.value.triggerProps,
46+
...popoverTriggerProps.value,
1347
...attrs,
1448
})
1549
},

packages/c-popover/src/c-popover.tsx

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@
1010

1111
import { computed, defineComponent, PropType } from "vue"
1212
import { PopoverProvider } from "./popover.context"
13-
import { usePopover, UsePopoverProps } from "./use-popover"
14-
import type * as Z from "@zag-js/types"
15-
import type * as PO from "@zag-js/popover"
16-
import type * as PP from "@zag-js/popper"
13+
import {
14+
useDeferredDisclosure,
15+
usePopover,
16+
UsePopoverProps,
17+
} from "./use-popover"
18+
import type * as ZP from "@zag-js/popper"
19+
import { useId } from "@chakra-ui/vue-composables"
20+
import { useMotions } from "@vueuse/motion"
1721

1822
type PopoverPropsContext = UsePopoverProps["context"]
1923

20-
export type CPopoverProps = PopoverPropsContext
24+
export interface CPopoverProps extends PopoverPropsContext {
25+
trigger: "click" | "hover"
26+
}
2127

2228
const VuePopoverProps = {
2329
autoFocus: {
@@ -56,6 +62,14 @@ const VuePopoverProps = {
5662
positioning: {
5763
type: Object as PropType<CPopoverProps["positioning"]>,
5864
},
65+
trigger: {
66+
type: String as PropType<CPopoverProps["trigger"]>,
67+
default: "click",
68+
},
69+
}
70+
71+
function wait(delay: number) {
72+
return new Promise((resolve) => setTimeout(resolve, delay))
5973
}
6074

6175
export const CPopover = defineComponent({
@@ -74,10 +88,42 @@ export const CPopover = defineComponent({
7488
emit,
7589
}))
7690

91+
const transitionId = useId(
92+
popoverProps.value.context.id,
93+
"transition:popover:"
94+
)
95+
96+
/** Handles exit transition */
97+
const leaveTransition = (done: VoidFunction) => {
98+
const motions = useMotions()
99+
const instance = motions[transitionId.value]
100+
instance?.leave(() => {
101+
done()
102+
})
103+
}
104+
105+
const enterTransition = async (done: VoidFunction) => {
106+
const motions = useMotions()
107+
const instance = motions[transitionId.value]
108+
await instance.apply("enter")
109+
done()
110+
}
111+
77112
const api = usePopover(popoverProps.value)
113+
const nativeIsOpen = computed(() => api.value.isOpen)
78114

79-
PopoverProvider(api)
115+
const { isOpenDeferred } = useDeferredDisclosure(nativeIsOpen)
116+
const popoverApi = computed(() => ({
117+
...api.value,
118+
deferredIsOpen: isOpenDeferred.value,
119+
leaveTransition,
120+
enterTransition,
121+
wait,
122+
transitionId: transitionId.value,
123+
trigger: props.trigger,
124+
}))
80125

126+
PopoverProvider(popoverApi)
81127
return () => slots.default?.()
82128
},
83129
})

packages/c-popover/src/popover.context.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import type { connect } from "@zag-js/popover"
22
import type { ComputedRef } from "vue"
3-
import { createContext } from "@chakra-ui/vue-utils"
3+
import { AnyFn, createContext } from "@chakra-ui/vue-utils"
44
import type { UsePopoverReturn } from "./use-popover"
55

66
export const [PopoverProvider, usePopoverContext] = createContext<
7-
ComputedRef<ReturnType<typeof connect>>
7+
ComputedRef<
8+
ReturnType<typeof connect> & {
9+
deferredIsOpen: boolean
10+
leaveTransition: AnyFn
11+
enterTransition: AnyFn
12+
wait: AnyFn
13+
transitionId: string
14+
trigger: "click" | "hover"
15+
}
16+
>
817
>({
918
name: "CPopoverContext",
1019
strict: true,

0 commit comments

Comments
 (0)