@rxdrag/website-lib 0.0.5 → 0.0.8

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.
@@ -0,0 +1,66 @@
1
+ ---
2
+ import { PageMeta } from "@rxdrag/rxcms-models";
3
+
4
+ interface Props {
5
+ content?: PageMeta;
6
+ title?: string;
7
+ }
8
+
9
+ const { content, title: propTitle } = Astro.props;
10
+
11
+ // 从content中提取SEO相关属性
12
+ const {
13
+ // SeoMeta属性
14
+ seoTitle,
15
+ seoKeywords,
16
+ seoDescription,
17
+ seoAuthor,
18
+ publisher,
19
+ seoRobots,
20
+ // OgMeta属性
21
+ ogTitle,
22
+ ogDescription,
23
+ ogUrl,
24
+ ogSiteName,
25
+ ogType,
26
+ xCard,
27
+ xSite,
28
+ xTitle,
29
+ xDescription,
30
+ xUrl,
31
+ // 其他属性
32
+ ogImage,
33
+ } = content || {};
34
+
35
+ const title = propTitle || seoTitle || "YiZhanFei";
36
+
37
+ const canonicalURL = Astro.url.href;
38
+ const imageUrl = ogImage?.file?.url || "";
39
+ ---
40
+
41
+ <title>{seoTitle || title}</title>
42
+ <meta name="description" content={seoDescription} />
43
+ <meta name="keywords" content={seoKeywords} />
44
+ <meta name="author" content={seoAuthor} />
45
+ <meta name="publisher" content={publisher} />
46
+ <meta name="robots" content={seoRobots || "index, follow"} />
47
+ <link rel="canonical" href={canonicalURL} />
48
+
49
+ <!-- Open Graph / Facebook -->
50
+ <meta property="og:type" content={ogType || "website"} />
51
+ <meta property="og:url" content={ogUrl || canonicalURL} />
52
+ <meta property="og:title" content={ogTitle || title} />
53
+ <meta property="og:description" content={ogDescription || seoDescription} />
54
+ <meta property="og:image" content={imageUrl} />
55
+ {ogSiteName && <meta property="og:site_name" content={ogSiteName} />}
56
+
57
+ <!-- Twitter -->
58
+ <meta property="twitter:card" content={xCard || "summary_large_image"} />
59
+ <meta property="twitter:url" content={xUrl || ogUrl || canonicalURL} />
60
+ <meta property="twitter:title" content={xTitle || ogTitle || title} />
61
+ <meta
62
+ property="twitter:description"
63
+ content={xDescription || ogDescription || seoDescription}
64
+ />
65
+ <meta property="twitter:image" content={imageUrl} />
66
+ {xSite && <meta property="twitter:site" content={xSite} />}
@@ -0,0 +1,69 @@
1
+ ---
2
+ import type { CSSProperties } from "react";
3
+ import Popup from "./Popup.astro";
4
+ import { DATA_POPUP_ROLE, PopupRole } from "@rxdrag/website-lib-core";
5
+ import type { IMotionProps } from "./MotionTypes";
6
+
7
+ interface Props extends IMotionProps {
8
+ popupKey: string;
9
+ class?: string;
10
+ style?: string | CSSProperties;
11
+ }
12
+
13
+ const roleProps = {
14
+ [DATA_POPUP_ROLE]: PopupRole.ModalContainer,
15
+ };
16
+ ---
17
+
18
+ <Popup {...roleProps} {...Astro.props}>
19
+ <slot />
20
+ </Popup>
21
+
22
+ <script>
23
+ import {
24
+ initModal,
25
+ initModalCloser,
26
+ onEverySwap,
27
+ onPageLoaded,
28
+ } from "@rxdrag/website-lib-core";
29
+ import {
30
+ DATA_POPUP,
31
+ DATA_POPUP_ROLE,
32
+ PopupRole,
33
+ } from "@rxdrag/website-lib-core";
34
+
35
+ const initModals = () => {
36
+ // 获取所有的 Modal 实例
37
+ const popups = document.querySelectorAll(`[${DATA_POPUP}]`);
38
+ popups.forEach((popup) => {
39
+ const popupKey = popup.getAttribute(DATA_POPUP);
40
+ //处理鼠标交互事件
41
+ if (popupKey && popup) {
42
+ if (popup.getAttribute(DATA_POPUP_ROLE) === PopupRole.ModalTrigger) {
43
+ initModal(popupKey, popup as HTMLElement);
44
+ }
45
+ }
46
+ //获取所有 ModalCloser 实例,并处理点击事件
47
+ //TODO:未处理嵌套跟Modal外部关闭的情况
48
+ const closers = popup.querySelectorAll(
49
+ `[${DATA_POPUP_ROLE}="${PopupRole.ModalCloser}"]`
50
+ );
51
+
52
+ closers.forEach((closer) => {
53
+ // 使用initModalCloser函数处理关闭按钮事件
54
+ if (popupKey) {
55
+ initModalCloser(popupKey, closer as HTMLElement);
56
+ }
57
+ });
58
+ });
59
+ };
60
+
61
+ // 在页面加载和每次页面转场后都初始化Modal
62
+ onPageLoaded(() => {
63
+ initModals();
64
+ });
65
+
66
+ onEverySwap(() => {
67
+ initModals();
68
+ });
69
+ </script>
@@ -0,0 +1,22 @@
1
+ ---
2
+ import { CSSProperties } from "react";
3
+ import { DATA_POPUP_ROLE, PopupRole } from "@rxdrag/website-lib-core";
4
+ import Popup from "./Popup.astro";
5
+ import { IMotionProps } from "./MotionTypes";
6
+
7
+ interface Props extends IMotionProps {
8
+ popupKey?: string;
9
+ class?: string;
10
+ style?: string | CSSProperties;
11
+ }
12
+
13
+ const roleProps = {
14
+ [DATA_POPUP_ROLE]: PopupRole.ModalCloser,
15
+ };
16
+ ---
17
+
18
+ <Popup {...roleProps} {...Astro.props}>
19
+ <slot />
20
+ </Popup>
21
+
22
+ <script></script>
@@ -0,0 +1,22 @@
1
+ ---
2
+ import { CSSProperties } from "react";
3
+ import { DATA_POPUP_ROLE, PopupRole } from "@rxdrag/website-lib-core";
4
+ import Popup from "./Popup.astro";
5
+ import { IMotionProps } from "./MotionTypes";
6
+
7
+ interface Props extends IMotionProps {
8
+ popupKey?: string;
9
+ class?: string;
10
+ style?: string | CSSProperties;
11
+ }
12
+
13
+ const roleProps = {
14
+ [DATA_POPUP_ROLE]: PopupRole.ModalPanel,
15
+ };
16
+ ---
17
+
18
+ <Popup {...roleProps} {...Astro.props}>
19
+ <slot />
20
+ </Popup>
21
+
22
+ <script></script>
@@ -0,0 +1,30 @@
1
+ ---
2
+ import { CSSProperties } from "react";
3
+ import {
4
+ DATA_POPUP_CTA,
5
+ DATA_POPUP_ROLE,
6
+ PopupRole,
7
+ } from "@rxdrag/website-lib-core";
8
+ import Popup from "./Popup.astro";
9
+ import { HTMLElementsWithChildren, IMotionProps } from "./MotionTypes";
10
+
11
+ interface Props extends IMotionProps {
12
+ popupKey: string;
13
+ callToAction?: string;
14
+ as?: HTMLElementsWithChildren;
15
+ class?: string;
16
+ style?: string | CSSProperties;
17
+ }
18
+
19
+ const { callToAction, ...rest } = Astro.props;
20
+ const roleProps = {
21
+ [DATA_POPUP_CTA]: callToAction,
22
+ [DATA_POPUP_ROLE]: PopupRole.ModalTrigger,
23
+ };
24
+ ---
25
+
26
+ <Popup {...roleProps} {...rest}>
27
+ <slot />
28
+ </Popup>
29
+
30
+ <script></script>
@@ -0,0 +1,333 @@
1
+ ---
2
+ import {
3
+ DATA_MOTION_ANIMATE,
4
+ DATA_MOTION_CLOSE,
5
+ DATA_MOTION_HOVER,
6
+ DATA_MOTION_INVIEW,
7
+ DATA_MOTION_OPEN,
8
+ DATA_MOTION_SCROLL,
9
+ DATA_MOTION_TAP,
10
+ } from "@rxdrag/website-lib-core";
11
+ import { cssPropertyUnits } from "./MotionTypes";
12
+ import type { HTMLElementsWithChildren, IMotionProps } from "./MotionTypes";
13
+ import { DATA_MOTION_FLIP, DATA_MOTION_SELECTION } from "./TabsLogic";
14
+
15
+ interface Props extends IMotionProps {
16
+ as?: HTMLElementsWithChildren;
17
+ class?: string;
18
+ style?: string | Record<string, string | number>;
19
+ [key: string]: any;
20
+ }
21
+
22
+ const {
23
+ as = "div",
24
+ animate,
25
+ whileHover,
26
+ whileInView,
27
+ whileTap,
28
+ whileScroll,
29
+ whileOpen,
30
+ whileClose,
31
+ whileSelection,
32
+ flipTransition,
33
+ class: className,
34
+ style,
35
+ ...rest
36
+ } = Astro.props;
37
+
38
+ const Element = as;
39
+ const dataWhileHover = JSON.stringify(whileHover);
40
+ const dataWhileInView = JSON.stringify(whileInView);
41
+ const dataWhileTap = JSON.stringify(whileTap);
42
+ const dataWhileScroll = JSON.stringify(whileScroll);
43
+ const dataWhileOpen = JSON.stringify(whileOpen);
44
+ const dataWhileClose = JSON.stringify(whileClose);
45
+ const dataWhileSelection = JSON.stringify(whileSelection);
46
+ const motions: Record<string, string> = {
47
+ [DATA_MOTION_ANIMATE]: JSON.stringify(animate),
48
+ [DATA_MOTION_HOVER]: dataWhileHover,
49
+ [DATA_MOTION_INVIEW]: dataWhileInView,
50
+ [DATA_MOTION_TAP]: dataWhileTap,
51
+ [DATA_MOTION_SCROLL]: dataWhileScroll,
52
+ [DATA_MOTION_OPEN]: dataWhileOpen,
53
+ [DATA_MOTION_CLOSE]: dataWhileClose,
54
+ [DATA_MOTION_SELECTION]: dataWhileSelection,
55
+ [DATA_MOTION_FLIP]: JSON.stringify(flipTransition),
56
+ };
57
+
58
+ const props = { ...motions, ...rest };
59
+ const mergedStyle = { ...style };
60
+
61
+ if (animate?.keyframes) {
62
+ // 从keyframes中提取初始值
63
+ const initialValues = Object.entries(animate.keyframes).reduce(
64
+ (acc, [key, value]) => {
65
+ // 如果值是数组,取第一个值作为初始值
66
+ const initialValue = Array.isArray(value) ? value[0] : value;
67
+
68
+ if (key in cssPropertyUnits) {
69
+ const unit = cssPropertyUnits[key as keyof typeof cssPropertyUnits];
70
+ if (unit === "transform") {
71
+ acc.transform = (acc.transform || "") as string;
72
+ acc.transform += ` translate${key.toUpperCase()}(${typeof initialValue === "number" ? `${initialValue}px` : initialValue})`;
73
+ } else if (typeof initialValue === "number") {
74
+ acc[key] = `${initialValue}${unit}`;
75
+ } else {
76
+ acc[key] = initialValue;
77
+ }
78
+ } else {
79
+ // 对于未定义的属性,保持原样
80
+ acc[key] = initialValue.toString();
81
+ }
82
+ return acc;
83
+ },
84
+ {} as Record<string, string>
85
+ );
86
+
87
+ Object.assign(mergedStyle, initialValues);
88
+ }
89
+
90
+ // 将 styleProp 的定义移到条件块外部,确保它始终被定义
91
+ const styleProp =
92
+ Object.keys(mergedStyle).length > 0
93
+ ? {
94
+ style: mergedStyle,
95
+ }
96
+ : undefined;
97
+ ---
98
+
99
+ <Element class={className} {...props} {...styleProp}>
100
+ <slot />
101
+ </Element>
102
+
103
+ <script>
104
+ import { animate, inView } from "motion";
105
+
106
+ import {
107
+ DATA_MOTION_ANIMATE,
108
+ DATA_MOTION_CLOSE,
109
+ DATA_MOTION_INVIEW,
110
+ DATA_MOTION_OPEN,
111
+ DATA_MOTION_TAP,
112
+ onEverySwap,
113
+ onPageLoaded,
114
+ onSelected,
115
+ onUnSelected,
116
+ } from "@rxdrag/website-lib-core";
117
+ import type {
118
+ DualAnimation,
119
+ SelectionEvent,
120
+ SimpleAnimation,
121
+ ViewportAnimation,
122
+ } from "@rxdrag/website-lib-core";
123
+ import {
124
+ getPopupContainer,
125
+ normalizeSimpleAnimation,
126
+ normalizeDualAnimation,
127
+ onPopupOpen,
128
+ } from "@rxdrag/website-lib-core";
129
+ import { DATA_MOTION_SELECTION, DATA_TABS } from "./TabsLogic";
130
+
131
+ // 初始化动画
132
+ function initAnimation() {
133
+ // 查找所有带有 data-motion-animate 属性的元素
134
+ const elements = document.querySelectorAll(`[${DATA_MOTION_ANIMATE}]`);
135
+ elements.forEach((element) => {
136
+ if (element instanceof HTMLElement) {
137
+ if (element.dataset.motionAnimate) {
138
+ const animation = normalizeSimpleAnimation(
139
+ JSON.parse(element.dataset.motionAnimate) as
140
+ | SimpleAnimation
141
+ | undefined
142
+ );
143
+ if (animation?.keyframes) {
144
+ animate(element, animation.keyframes, animation.transition);
145
+ }
146
+ }
147
+ } else {
148
+ console.log("initAnimation: Not an HTMLElement somehow?");
149
+ }
150
+ });
151
+ }
152
+
153
+ function initPopups() {
154
+ onPopupOpen((event) => {
155
+ const openResponseElements = document.querySelectorAll(
156
+ `[${DATA_MOTION_OPEN}]`
157
+ );
158
+ const containerElement = getPopupContainer(event.key);
159
+
160
+ //响应open事件
161
+ openResponseElements.forEach((element) => {
162
+ if (
163
+ element instanceof HTMLElement &&
164
+ containerElement?.contains(element)
165
+ ) {
166
+ if (element.dataset.motionOpen) {
167
+ const animation = normalizeSimpleAnimation(
168
+ JSON.parse(element.dataset.motionOpen) as
169
+ | SimpleAnimation
170
+ | undefined
171
+ );
172
+ if (animation?.keyframes) {
173
+ animate(element, animation.keyframes, animation.transition);
174
+ }
175
+ }
176
+ }
177
+ });
178
+
179
+ return () => {
180
+ const closeResponseElements = document.querySelectorAll(
181
+ `[${DATA_MOTION_CLOSE}]`
182
+ );
183
+
184
+ //响应close事件
185
+ closeResponseElements.forEach((element) => {
186
+ if (element instanceof HTMLElement) {
187
+ if (
188
+ element.dataset.motionClose &&
189
+ containerElement?.contains(element)
190
+ ) {
191
+ const animation = normalizeSimpleAnimation(
192
+ JSON.parse(element.dataset.motionClose) as
193
+ | SimpleAnimation
194
+ | undefined
195
+ );
196
+ if (animation && animation.keyframes) {
197
+ animate(element, animation.keyframes, animation.transition);
198
+ }
199
+ }
200
+ } else {
201
+ console.log("===Not an HTMLElement somehow?");
202
+ }
203
+ });
204
+ };
205
+ });
206
+ }
207
+
208
+ // 初始化AOS(Animate On Scroll)动画
209
+ function initAos() {
210
+ //查找所有的data-motion-inview元素
211
+ const inviewElements = document.querySelectorAll(`[${DATA_MOTION_INVIEW}]`);
212
+ inviewElements.forEach((element) => {
213
+ if (element instanceof HTMLElement && element.dataset.motionInview) {
214
+ const animation = normalizeDualAnimation(
215
+ JSON.parse(element.dataset.motionInview) as
216
+ | ViewportAnimation
217
+ | undefined
218
+ ) as ViewportAnimation;
219
+
220
+ inView(element, (target) => {
221
+ const newElement = target as HTMLElement;
222
+ const needAction =
223
+ (!newElement.dataset.motionViewportEnter && animation.once) ||
224
+ !animation.once;
225
+
226
+ if (animation?.in?.keyframes && needAction) {
227
+ newElement.dataset.motionViewportEnter = "true";
228
+ animate(target, animation.in.keyframes, animation.in.transition);
229
+ }
230
+ // 返回清理函数
231
+ return () => {
232
+ // 如果需要在元素离开视图时执行某些操作
233
+ if (animation?.out?.keyframes && needAction && !animation.once) {
234
+ animate(
235
+ target,
236
+ animation.out.keyframes,
237
+ animation.out.transition
238
+ );
239
+ }
240
+ };
241
+ });
242
+ }
243
+ });
244
+ }
245
+
246
+ //TODO 初始化Taps
247
+ function initTaps() {
248
+ //查找所有的data-motion-tap元素
249
+ const tapElements = document.querySelectorAll(`[${DATA_MOTION_TAP}]`);
250
+ }
251
+
252
+ //初始化选项卡Selection动画
253
+ function initTabsMotion() {
254
+ onSelected((event: SelectionEvent) => {
255
+ //查找对应的data-motion-selection元素
256
+ const tabsElement = document.querySelector(`[${DATA_TABS}=${event.key}]`);
257
+
258
+ if (tabsElement instanceof HTMLElement) {
259
+ //该容器下,所有带有选中动画的元素
260
+ tabsElement
261
+ .querySelectorAll(`[${DATA_MOTION_SELECTION}]`)
262
+ .forEach((element) => {
263
+ const motions = (element as HTMLElement).dataset.motionSelection;
264
+ if (motions) {
265
+ const animation = normalizeDualAnimation(
266
+ JSON.parse(motions) as DualAnimation | undefined
267
+ );
268
+ if (animation?.in?.keyframes) {
269
+ animate(
270
+ element,
271
+ animation.in.keyframes,
272
+ animation.in.transition
273
+ );
274
+ }
275
+ }
276
+ });
277
+ }
278
+ });
279
+
280
+ onUnSelected((event: SelectionEvent) => {
281
+ //查找对应的data-motion-selection元素
282
+ const tabsElement = document.querySelector(`[${DATA_TABS}=${event.key}]`);
283
+
284
+ if (tabsElement instanceof HTMLElement) {
285
+ //该容器下,所有带有选中动画的元素
286
+ tabsElement
287
+ .querySelectorAll(`[${DATA_MOTION_SELECTION}]`)
288
+ .forEach((element) => {
289
+ const motions = (element as HTMLElement).dataset.motionSelection;
290
+ if (motions) {
291
+ const animation = normalizeDualAnimation(
292
+ JSON.parse(motions) as DualAnimation | undefined
293
+ );
294
+ if (animation?.out?.keyframes) {
295
+ animate(
296
+ element,
297
+ animation.out.keyframes,
298
+ animation.out.transition
299
+ );
300
+ }
301
+ }
302
+ });
303
+ }
304
+ });
305
+ }
306
+
307
+ const initMotion = () => {
308
+ initAnimation();
309
+ initPopups();
310
+ initAos();
311
+ initTaps();
312
+ initTabsMotion();
313
+ };
314
+
315
+ onEverySwap(() => {
316
+ initMotion();
317
+ });
318
+ onPageLoaded(() => {
319
+ initMotion();
320
+ });
321
+
322
+ // if (!(window as any).__motionInitialized) {
323
+ // (window as any).__motionInitialized = true;
324
+ // // 当DOM加载完成时初始化动画
325
+ // document.addEventListener("astro:after-swap", () => {
326
+ // initAnimation();
327
+ // initPopups();
328
+ // initAos();
329
+ // initTaps();
330
+ // initTabsMotion();
331
+ // });
332
+ // }
333
+ </script>
@@ -0,0 +1,71 @@
1
+ import type { SimpleAnimation, HoverAnimation, ViewportAnimation, TapAnimation, ScrollAnimation, SelectionAnimation } from "@rxdrag/website-lib-core";
2
+ import type { AnimationOptions } from "motion";
3
+
4
+ // 排除不能包含子元素的标签
5
+ export type ExcludedElements =
6
+ | "img"
7
+ | "input"
8
+ | "br"
9
+ | "hr"
10
+ | "area"
11
+ | "base"
12
+ | "col"
13
+ | "embed"
14
+ | "link"
15
+ | "meta"
16
+ | "param"
17
+ | "source"
18
+ | "track"
19
+ | "wbr";
20
+
21
+ export type HTMLElementsWithChildren = Exclude<
22
+ keyof HTMLElementTagNameMap,
23
+ ExcludedElements
24
+ >;
25
+
26
+ // CSS 属性的单位处理映射
27
+ export const cssPropertyUnits = {
28
+ // 无单位的属性
29
+ opacity: "",
30
+ scale: "",
31
+ fontWeight: "",
32
+ lineHeight: "",
33
+ zIndex: "",
34
+ flex: "",
35
+ flexGrow: "",
36
+ flexShrink: "",
37
+ order: "",
38
+ // 使用px的属性
39
+ width: "px",
40
+ height: "px",
41
+ minWidth: "px",
42
+ minHeight: "px",
43
+ maxWidth: "px",
44
+ maxHeight: "px",
45
+ padding: "px",
46
+ margin: "px",
47
+ top: "px",
48
+ right: "px",
49
+ bottom: "px",
50
+ left: "px",
51
+ gap: "px",
52
+ columnGap: "px",
53
+ rowGap: "px",
54
+ // transform 相关的属性
55
+ x: "transform",
56
+ y: "transform",
57
+ z: "transform",
58
+ } as const;
59
+
60
+ export interface IMotionProps {
61
+ animate?: SimpleAnimation;
62
+ whileHover?: HoverAnimation;
63
+ whileInView?: ViewportAnimation;
64
+ whileTap?: TapAnimation;
65
+ whileScroll?: ScrollAnimation;
66
+ whileOpen?: SimpleAnimation;
67
+ whileClose?: SimpleAnimation;
68
+ whileSelection?: SelectionAnimation;
69
+ // 在flip的父元素中使用
70
+ flipTransition?: AnimationOptions;
71
+ }
@@ -0,0 +1,57 @@
1
+ ---
2
+ import { CSSProperties } from "react";
3
+ import { DATA_POPUP_ROLE, PopupRole } from "@rxdrag/website-lib-core";
4
+ import Popup from "./Popup.astro";
5
+ import { IMotionProps } from "./MotionTypes";
6
+
7
+ interface Props extends IMotionProps {
8
+ popupKey: string;
9
+ class?: string;
10
+ style?: string | CSSProperties;
11
+ }
12
+
13
+ const roleProps = {
14
+ [DATA_POPUP_ROLE]: PopupRole.PopoverContainer,
15
+ };
16
+ ---
17
+
18
+ <Popup {...roleProps} {...Astro.props}>
19
+ <slot />
20
+ </Popup>
21
+
22
+ <script>
23
+ import {
24
+ initPopover,
25
+ onEverySwap,
26
+ onPageLoaded,
27
+ } from "@rxdrag/website-lib-core";
28
+ import {
29
+ DATA_POPUP,
30
+ DATA_POPUP_ROLE,
31
+ PopupRole,
32
+ } from "@rxdrag/website-lib-core";
33
+
34
+ const initPopoverActions = () => {
35
+ // 获取所有的 Popover 实例
36
+ const popups = document.querySelectorAll(`[${DATA_POPUP}]`);
37
+
38
+ popups.forEach((popup) => {
39
+ const popupKey = popup.getAttribute(DATA_POPUP);
40
+ //处理鼠标交互事件
41
+ if (popupKey && popup) {
42
+ if (
43
+ popup.getAttribute(DATA_POPUP_ROLE) === PopupRole.PopoverContainer
44
+ ) {
45
+ initPopover(popupKey, popup as HTMLElement);
46
+ }
47
+ }
48
+ });
49
+ };
50
+
51
+ onPageLoaded(() => {
52
+ initPopoverActions();
53
+ });
54
+ onEverySwap(() => {
55
+ initPopoverActions();
56
+ });
57
+ </script>
@@ -0,0 +1,19 @@
1
+ ---
2
+ import { DATA_POPUP } from "@rxdrag/website-lib-core";
3
+ import Motion from "./Motion.astro";
4
+ import { IMotionProps } from "./MotionTypes";
5
+
6
+ interface Props extends IMotionProps {
7
+ popupKey?: string;
8
+ }
9
+ const { popupKey, ...rest } = Astro.props;
10
+ const popupProps = {
11
+ [DATA_POPUP]: popupKey,
12
+ };
13
+ ---
14
+
15
+ <Motion {...popupProps} {...rest}>
16
+ <slot />
17
+ </Motion>
18
+
19
+ <script></script>