@rxdrag/website-lib-core 0.0.7 → 0.0.9
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/index.ts +1 -1
- package/package.json +11 -7
- package/src/component-logic/gsap.d.ts +4 -0
- package/src/component-logic/index.ts +8 -0
- package/src/component-logic/link-client.ts +33 -0
- package/src/component-logic/link.ts +50 -0
- package/src/component-logic/modal.ts +36 -0
- package/src/component-logic/motion.ts +272 -0
- package/src/component-logic/number.ts +45 -0
- package/src/component-logic/popover.ts +51 -0
- package/src/component-logic/tabs.ts +10 -0
- package/src/controller/AnimateController.ts +138 -0
- package/src/controller/AosController.ts +240 -0
- package/src/controller/FlipController.ts +339 -0
- package/src/controller/ModalController.ts +127 -0
- package/src/controller/NumberController.ts +161 -0
- package/src/controller/PageLoader.ts +153 -0
- package/src/controller/PopoverController.ts +116 -0
- package/src/controller/TabsController.ts +271 -0
- package/src/controller/applyAnimation.ts +86 -0
- package/src/controller/applyInitialState.ts +79 -0
- package/src/{scripts → controller}/consts.ts +0 -2
- package/src/controller/index.ts +9 -0
- package/src/controller/popup.ts +346 -0
- package/src/controller/utils.ts +48 -0
- package/src/entify/Entify.ts +354 -365
- package/src/entify/IEntify.ts +91 -0
- package/src/entify/index.ts +3 -2
- package/src/entify/lib/newQueryProductOptions.ts +2 -3
- package/src/entify/lib/newQueryProductsMediaOptions.ts +19 -18
- package/src/entify/lib/queryAllProducts.ts +11 -3
- package/src/entify/lib/queryFeaturedProducts.ts +3 -3
- package/src/entify/lib/queryLatestPosts.ts +2 -2
- package/src/entify/lib/queryOneTheme.ts +1 -1
- package/src/entify/lib/queryPostCategories.ts +3 -3
- package/src/entify/lib/queryPostSlugs.ts +2 -2
- package/src/entify/lib/queryPosts.ts +92 -92
- package/src/entify/lib/queryProductCategories.ts +3 -3
- package/src/entify/lib/queryProducts.ts +69 -69
- package/src/entify/lib/queryUserPosts.ts +2 -2
- package/src/entify/lib/searchProducts.ts +2 -2
- package/src/index.ts +3 -1
- package/src/lib/formatDate.ts +15 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/pagination.ts +114 -0
- package/src/lib/utils.ts +119 -0
- package/src/motion/consts.ts +428 -598
- package/src/motion/convertToGsapVars.ts +102 -0
- package/src/motion/index.ts +5 -1
- package/src/motion/normalizeAnimation.ts +28 -0
- package/src/motion/normalizeAosAnimation.ts +22 -0
- package/src/motion/normalizePopupAnimation.ts +24 -0
- package/src/motion/types.ts +133 -46
- package/src/react/components/AttachmentIcon/index.tsx +53 -0
- package/src/react/components/ContactForm/index.tsx +341 -0
- package/src/react/components/Icon/index.tsx +10 -0
- package/src/react/components/Medias/index.tsx +347 -347
- package/src/react/components/ProductCard/ProductCta/index.tsx +7 -5
- package/src/react/components/RichTextOutline/index.tsx +76 -76
- package/src/react/components/Scroller.tsx +5 -1
- package/src/react/components/SearchInput.tsx +36 -34
- package/src/react/components/ToTop.tsx +63 -28
- package/src/react/components/index.ts +3 -1
- package/src/react/hooks/useScroll.ts +16 -10
- package/src/react/components/EnquiryForm/index.tsx +0 -334
- package/src/scripts/actions.ts +0 -304
- package/src/scripts/events.ts +0 -33
- package/src/scripts/index.ts +0 -3
- /package/src/react/components/{EnquiryForm → ContactForm}/Input.tsx +0 -0
- /package/src/react/components/{EnquiryForm → ContactForm}/Submit.tsx +0 -0
- /package/src/react/components/{EnquiryForm → ContactForm}/Textarea.tsx +0 -0
package/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./src"
|
|
1
|
+
export * from "./src"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rxdrag/website-lib-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.ts"
|
|
@@ -17,21 +17,25 @@
|
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@babel/types": "^7.26.9",
|
|
20
|
+
"@iconify/react": "^5.0.2",
|
|
21
|
+
"@types/gsap": "^3.0.0",
|
|
22
|
+
"@types/lodash-es": "^4.14.191",
|
|
20
23
|
"@types/react": "^18.2.22",
|
|
21
24
|
"@types/react-dom": "^18.2.7",
|
|
22
25
|
"eslint": "^7.32.0",
|
|
23
26
|
"typescript": "^5",
|
|
24
|
-
"@rxdrag/entify-hooks": "0.2.
|
|
25
|
-
"@rxdrag/
|
|
26
|
-
"@rxdrag/
|
|
27
|
-
"@rxdrag/
|
|
27
|
+
"@rxdrag/entify-hooks": "0.2.40",
|
|
28
|
+
"@rxdrag/slate-preview": "1.2.55",
|
|
29
|
+
"@rxdrag/tsconfig": "0.2.0",
|
|
30
|
+
"@rxdrag/eslint-config-custom": "0.2.12"
|
|
28
31
|
},
|
|
29
32
|
"dependencies": {
|
|
30
33
|
"clsx": "^2.1.0",
|
|
31
|
-
"
|
|
34
|
+
"gsap": "^3.12.7",
|
|
35
|
+
"lodash-es": "^4.17.21",
|
|
32
36
|
"react": "^18.2.0",
|
|
33
37
|
"react-dom": "^18.2.0",
|
|
34
|
-
"@rxdrag/rxcms-models": "0.3.
|
|
38
|
+
"@rxdrag/rxcms-models": "0.3.46"
|
|
35
39
|
},
|
|
36
40
|
"peerDependencies": {
|
|
37
41
|
"astro": "^4.0.0 || ^5.0.0"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const initLinks = () => {
|
|
2
|
+
// 获取当前URL的路径部分(去除域名)
|
|
3
|
+
const currentPath = window.location.pathname;
|
|
4
|
+
|
|
5
|
+
// 获取所有导航链接
|
|
6
|
+
const links = document.querySelectorAll<HTMLAnchorElement>(
|
|
7
|
+
"[data-actived-path]"
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
links.forEach((anchorLink) => {
|
|
11
|
+
// 从链接URL中提取路径部分
|
|
12
|
+
try {
|
|
13
|
+
const linkUrl = new URL(anchorLink.href);
|
|
14
|
+
const linkPath =
|
|
15
|
+
anchorLink.dataset.activedPath === "true"
|
|
16
|
+
? linkUrl.pathname
|
|
17
|
+
: anchorLink.dataset.activedPath;
|
|
18
|
+
|
|
19
|
+
// 检查当前路径是否与链接路径匹配或是其子路径
|
|
20
|
+
if (
|
|
21
|
+
currentPath === linkPath ||
|
|
22
|
+
(linkPath !== "/" && linkPath && currentPath.startsWith(linkPath))
|
|
23
|
+
) {
|
|
24
|
+
anchorLink.classList.add("actived");
|
|
25
|
+
} else {
|
|
26
|
+
anchorLink.classList.remove("actived");
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// 处理无效URL的情况
|
|
30
|
+
console.warn("Invalid URL:", e, anchorLink);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type LinkType =
|
|
2
|
+
| "product"
|
|
3
|
+
| "post"
|
|
4
|
+
| "page"
|
|
5
|
+
| "user"
|
|
6
|
+
| "product-category"
|
|
7
|
+
| "post-category"
|
|
8
|
+
| "link"
|
|
9
|
+
| "products"
|
|
10
|
+
| "posts";
|
|
11
|
+
|
|
12
|
+
export function toHref(type: LinkType | string, to: string) {
|
|
13
|
+
if (type === "link" || !type) {
|
|
14
|
+
return to;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (type === "products") {
|
|
18
|
+
return "/products/page/1";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (type === "posts") {
|
|
22
|
+
return "/posts/page/1";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (type === "page") {
|
|
26
|
+
return `/${to}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (type === "post") {
|
|
30
|
+
return `/posts/${to}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (type === "product") {
|
|
34
|
+
return `/products/${to}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (type === "user") {
|
|
38
|
+
return `/profiles/${to}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (type === "product-category") {
|
|
42
|
+
return `/products/categories/${to}/1`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (type === "post-category") {
|
|
46
|
+
return `/posts/categories/${to}/1`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return `/${type}/${to}`;
|
|
50
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { DATA_POPUP, DATA_POPUP_ROLE, PopupRole } from "../controller";
|
|
2
|
+
import { HTMLElementsWithChildren, IMotionProps } from "./motion";
|
|
3
|
+
|
|
4
|
+
export type ModalTriggerProps = IMotionProps & {
|
|
5
|
+
popupKey: string;
|
|
6
|
+
callToAction?: string;
|
|
7
|
+
as?: HTMLElementsWithChildren;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function getModalDataAttrs(popupKey?: string) {
|
|
11
|
+
return {
|
|
12
|
+
[DATA_POPUP]: popupKey,
|
|
13
|
+
[DATA_POPUP_ROLE]: PopupRole.ModalContainer,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getModalCloserDataAttrs(popupKey?: string) {
|
|
18
|
+
return {
|
|
19
|
+
[DATA_POPUP]: popupKey,
|
|
20
|
+
[DATA_POPUP_ROLE]: PopupRole.ModalCloser,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getModalPanelDataAttrs(popupKey?: string) {
|
|
25
|
+
return {
|
|
26
|
+
[DATA_POPUP]: popupKey,
|
|
27
|
+
[DATA_POPUP_ROLE]: PopupRole.ModalPanel,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getModalTriggerDataAttrs(popupKey?: string) {
|
|
32
|
+
return {
|
|
33
|
+
[DATA_POPUP]: popupKey,
|
|
34
|
+
[DATA_POPUP_ROLE]: PopupRole.ModalTrigger,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeAnimation,
|
|
3
|
+
DATA_MOTION,
|
|
4
|
+
DATA_MOTION_HOVER,
|
|
5
|
+
DATA_MOTION_INVIEW,
|
|
6
|
+
DATA_MOTION_TAP,
|
|
7
|
+
AnimationConfig,
|
|
8
|
+
AosAnimationConfig,
|
|
9
|
+
HoverAnimationConfig,
|
|
10
|
+
PopupAnimationConfig,
|
|
11
|
+
DATA_MOTION_POPUP,
|
|
12
|
+
normalizePopupAnimation,
|
|
13
|
+
normalizeAosAnimation,
|
|
14
|
+
} from "../motion";
|
|
15
|
+
import { DATA_MOTION_SELECTION, DATA_MOTION_FLIP } from "./tabs";
|
|
16
|
+
import { CSSProperties } from "react";
|
|
17
|
+
|
|
18
|
+
// 排除不能包含子元素的标签
|
|
19
|
+
export type ExcludedElements =
|
|
20
|
+
| "img"
|
|
21
|
+
| "input"
|
|
22
|
+
| "br"
|
|
23
|
+
| "hr"
|
|
24
|
+
| "area"
|
|
25
|
+
| "base"
|
|
26
|
+
| "col"
|
|
27
|
+
| "embed"
|
|
28
|
+
| "link"
|
|
29
|
+
| "meta"
|
|
30
|
+
| "param"
|
|
31
|
+
| "source"
|
|
32
|
+
| "track"
|
|
33
|
+
| "wbr";
|
|
34
|
+
|
|
35
|
+
export type HTMLElementsWithChildren = Exclude<
|
|
36
|
+
keyof HTMLElementTagNameMap,
|
|
37
|
+
ExcludedElements
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
// CSS 属性的单位处理映射
|
|
41
|
+
export const cssPropertyUnits = {
|
|
42
|
+
// 无单位的属性
|
|
43
|
+
opacity: "",
|
|
44
|
+
scale: "",
|
|
45
|
+
fontWeight: "",
|
|
46
|
+
lineHeight: "",
|
|
47
|
+
zIndex: "",
|
|
48
|
+
flex: "",
|
|
49
|
+
flexGrow: "",
|
|
50
|
+
flexShrink: "",
|
|
51
|
+
order: "",
|
|
52
|
+
// 使用px的属性
|
|
53
|
+
width: "px",
|
|
54
|
+
height: "px",
|
|
55
|
+
minWidth: "px",
|
|
56
|
+
minHeight: "px",
|
|
57
|
+
maxWidth: "px",
|
|
58
|
+
maxHeight: "px",
|
|
59
|
+
padding: "px",
|
|
60
|
+
margin: "px",
|
|
61
|
+
top: "px",
|
|
62
|
+
right: "px",
|
|
63
|
+
bottom: "px",
|
|
64
|
+
left: "px",
|
|
65
|
+
gap: "px",
|
|
66
|
+
columnGap: "px",
|
|
67
|
+
rowGap: "px",
|
|
68
|
+
// transform 相关的属性
|
|
69
|
+
x: "transform",
|
|
70
|
+
y: "transform",
|
|
71
|
+
z: "transform",
|
|
72
|
+
} as const;
|
|
73
|
+
|
|
74
|
+
export interface IMotionProps {
|
|
75
|
+
animate?: AnimationConfig | string;
|
|
76
|
+
whileHover?: HoverAnimationConfig | string;
|
|
77
|
+
whileInView?: AosAnimationConfig | string;
|
|
78
|
+
whileTap?: AnimationConfig | string;
|
|
79
|
+
whilePopup?: PopupAnimationConfig | string;
|
|
80
|
+
whileSelection?: AnimationConfig | string;
|
|
81
|
+
// 在flip的父元素中使用
|
|
82
|
+
flipTransition?: AnimationConfig | string;
|
|
83
|
+
class?: string;
|
|
84
|
+
style?: CSSProperties;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface MotionAttributes {
|
|
88
|
+
className?: string;
|
|
89
|
+
style?: CSSProperties;
|
|
90
|
+
class?: string;
|
|
91
|
+
[DATA_MOTION]?: string;
|
|
92
|
+
[DATA_MOTION_HOVER]?: string;
|
|
93
|
+
[DATA_MOTION_INVIEW]?: string;
|
|
94
|
+
[DATA_MOTION_TAP]?: string;
|
|
95
|
+
[DATA_MOTION_POPUP]?: string;
|
|
96
|
+
[DATA_MOTION_SELECTION]?: string;
|
|
97
|
+
[DATA_MOTION_FLIP]?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getMotionProps(props: IMotionProps): MotionAttributes {
|
|
101
|
+
const {
|
|
102
|
+
animate,
|
|
103
|
+
whileHover,
|
|
104
|
+
whileInView,
|
|
105
|
+
whileTap,
|
|
106
|
+
whilePopup,
|
|
107
|
+
whileSelection,
|
|
108
|
+
flipTransition,
|
|
109
|
+
class: className,
|
|
110
|
+
style,
|
|
111
|
+
...rest
|
|
112
|
+
} = props;
|
|
113
|
+
const normalizedAnimate = normalizeAnimation(animate);
|
|
114
|
+
const normalizedWhileHover = normalizeAnimation(whileHover);
|
|
115
|
+
const normalizedWhileInView = normalizeAosAnimation(whileInView);
|
|
116
|
+
const normalizedWhileTap = normalizeAnimation(whileTap);
|
|
117
|
+
const normalizedWhilePopup = normalizePopupAnimation(whilePopup);
|
|
118
|
+
const normalizedWhileSelection = normalizeAnimation(whileSelection);
|
|
119
|
+
|
|
120
|
+
const dataWhileHover = JSON.stringify(normalizedWhileHover);
|
|
121
|
+
const dataWhileInView = JSON.stringify(normalizedWhileInView);
|
|
122
|
+
const dataWhileTap = JSON.stringify(normalizedWhileTap);
|
|
123
|
+
const dataWhilePopup = JSON.stringify(normalizedWhilePopup);
|
|
124
|
+
const dataWhileSelection = JSON.stringify(normalizedWhileSelection);
|
|
125
|
+
const motions: Record<string, string> = {
|
|
126
|
+
[DATA_MOTION_HOVER]: dataWhileHover,
|
|
127
|
+
[DATA_MOTION_INVIEW]: dataWhileInView,
|
|
128
|
+
[DATA_MOTION_TAP]: dataWhileTap,
|
|
129
|
+
[DATA_MOTION_POPUP]: dataWhilePopup,
|
|
130
|
+
[DATA_MOTION_SELECTION]: dataWhileSelection,
|
|
131
|
+
[DATA_MOTION_FLIP]: JSON.stringify(flipTransition),
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (Object.keys(normalizedAnimate || {}).length > 0) {
|
|
135
|
+
motions[DATA_MOTION] = JSON.stringify(normalizedAnimate);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const newClassName = className || "";
|
|
139
|
+
const newProps = { ...motions, ...rest };
|
|
140
|
+
const mergedStyle = { ...style };
|
|
141
|
+
|
|
142
|
+
if (normalizedAnimate?.initial) {
|
|
143
|
+
// 处理特殊的transform属性 (x, y, z, rotation, scale等)
|
|
144
|
+
// 这些属性需要转换为CSS的transform属性
|
|
145
|
+
|
|
146
|
+
// 处理标准CSS属性
|
|
147
|
+
if (normalizedAnimate.initial.opacity !== undefined)
|
|
148
|
+
mergedStyle.opacity = String(normalizedAnimate.initial.opacity);
|
|
149
|
+
if (normalizedAnimate.initial.backgroundColor !== undefined)
|
|
150
|
+
mergedStyle.backgroundColor = normalizedAnimate.initial.backgroundColor;
|
|
151
|
+
if (normalizedAnimate.initial.color !== undefined)
|
|
152
|
+
mergedStyle.color = normalizedAnimate.initial.color;
|
|
153
|
+
if (normalizedAnimate.initial.width !== undefined) {
|
|
154
|
+
mergedStyle.width =
|
|
155
|
+
typeof normalizedAnimate.initial.width === "number"
|
|
156
|
+
? `${normalizedAnimate.initial.width}px`
|
|
157
|
+
: normalizedAnimate.initial.width;
|
|
158
|
+
}
|
|
159
|
+
if (normalizedAnimate.initial.height !== undefined) {
|
|
160
|
+
mergedStyle.height =
|
|
161
|
+
typeof normalizedAnimate.initial.height === "number"
|
|
162
|
+
? `${normalizedAnimate.initial.height}px`
|
|
163
|
+
: normalizedAnimate.initial.height;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 处理transformOrigin
|
|
167
|
+
if (normalizedAnimate.initial.transformOrigin !== undefined) {
|
|
168
|
+
mergedStyle.transformOrigin = normalizedAnimate.initial.transformOrigin;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 处理transform属性
|
|
172
|
+
const transforms: string[] = [];
|
|
173
|
+
|
|
174
|
+
// 注意:对于x和y,我们使用translateX和translateY,但要确保单位是正确的
|
|
175
|
+
if (normalizedAnimate.initial.x !== undefined) {
|
|
176
|
+
const value =
|
|
177
|
+
typeof normalizedAnimate.initial.x === "number"
|
|
178
|
+
? `${normalizedAnimate.initial.x}px`
|
|
179
|
+
: normalizedAnimate.initial.x;
|
|
180
|
+
transforms.push(`translateX(${value})`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (normalizedAnimate.initial.y !== undefined) {
|
|
184
|
+
const value =
|
|
185
|
+
typeof normalizedAnimate.initial.y === "number"
|
|
186
|
+
? `${normalizedAnimate.initial.y}px`
|
|
187
|
+
: normalizedAnimate.initial.y;
|
|
188
|
+
transforms.push(`translateY(${value})`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (normalizedAnimate.initial.z !== undefined) {
|
|
192
|
+
const value =
|
|
193
|
+
typeof normalizedAnimate.initial.z === "number"
|
|
194
|
+
? `${normalizedAnimate.initial.z}px`
|
|
195
|
+
: normalizedAnimate.initial.z;
|
|
196
|
+
transforms.push(`translateZ(${value})`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 处理旋转
|
|
200
|
+
if (normalizedAnimate.initial.rotation !== undefined) {
|
|
201
|
+
transforms.push(`rotate(${normalizedAnimate.initial.rotation}deg)`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (normalizedAnimate.initial.rotationX !== undefined) {
|
|
205
|
+
transforms.push(`rotateX(${normalizedAnimate.initial.rotationX}deg)`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (normalizedAnimate.initial.rotationY !== undefined) {
|
|
209
|
+
transforms.push(`rotateY(${normalizedAnimate.initial.rotationY}deg)`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (normalizedAnimate.initial.rotationZ !== undefined) {
|
|
213
|
+
transforms.push(`rotateZ(${normalizedAnimate.initial.rotationZ}deg)`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 处理缩放
|
|
217
|
+
if (normalizedAnimate.initial.scale !== undefined) {
|
|
218
|
+
transforms.push(`scale(${normalizedAnimate.initial.scale})`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (normalizedAnimate.initial.scaleX !== undefined) {
|
|
222
|
+
transforms.push(`scaleX(${normalizedAnimate.initial.scaleX})`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (normalizedAnimate.initial.scaleY !== undefined) {
|
|
226
|
+
transforms.push(`scaleY(${normalizedAnimate.initial.scaleY})`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 如果有transform属性,合并它们
|
|
230
|
+
if (transforms.length > 0) {
|
|
231
|
+
mergedStyle.transform = transforms.join(" ");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 处理其他属性
|
|
235
|
+
const handledProps = [
|
|
236
|
+
"opacity",
|
|
237
|
+
"backgroundColor",
|
|
238
|
+
"color",
|
|
239
|
+
"width",
|
|
240
|
+
"height",
|
|
241
|
+
"transformOrigin",
|
|
242
|
+
"x",
|
|
243
|
+
"y",
|
|
244
|
+
"z",
|
|
245
|
+
"rotation",
|
|
246
|
+
"rotationX",
|
|
247
|
+
"rotationY",
|
|
248
|
+
"rotationZ",
|
|
249
|
+
"scale",
|
|
250
|
+
"scaleX",
|
|
251
|
+
"scaleY",
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
// 复制其他没有特殊处理的属性
|
|
255
|
+
for (const key in normalizedAnimate.initial) {
|
|
256
|
+
if (!handledProps.includes(key)) {
|
|
257
|
+
// @ts-expect-error - 允许动态属性赋值
|
|
258
|
+
mergedStyle[key] = normalizedAnimate.initial[key];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 将 styleProp 的定义移到条件块外部,确保它始终被定义
|
|
264
|
+
const styleProp =
|
|
265
|
+
Object.keys(mergedStyle).length > 0
|
|
266
|
+
? {
|
|
267
|
+
style: mergedStyle,
|
|
268
|
+
}
|
|
269
|
+
: undefined;
|
|
270
|
+
|
|
271
|
+
return { ...newProps, ...styleProp, class: newClassName };
|
|
272
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DATA_MOTION_NUMBER, DATA_MOTION_NUMBER_ONCE } from "../motion/consts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Animation options for GSAP animations
|
|
5
|
+
*/
|
|
6
|
+
export interface AnimationOptions {
|
|
7
|
+
/** Animation duration in seconds */
|
|
8
|
+
duration?: number;
|
|
9
|
+
/** GSAP easing function (e.g., "power1.out", "power2.in", "elastic.out") */
|
|
10
|
+
ease?: string;
|
|
11
|
+
/** Delay before animation starts in seconds */
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** Repeat count (-1 for infinite) */
|
|
14
|
+
repeat?: number;
|
|
15
|
+
/** Whether to yoyo the animation (play in reverse after completion) */
|
|
16
|
+
yoyo?: boolean;
|
|
17
|
+
/** Stagger value for multiple animations */
|
|
18
|
+
stagger?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AnimationNumberProps {
|
|
22
|
+
start?: number;
|
|
23
|
+
end?: number;
|
|
24
|
+
transition?: AnimationOptions;
|
|
25
|
+
class?: string;
|
|
26
|
+
sign?: "string";
|
|
27
|
+
once?: boolean;
|
|
28
|
+
type?: "int" | "float" | "percentage" | "currency";
|
|
29
|
+
fractionDigits?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getAnimationNumberDataAttrs(props: AnimationNumberProps) {
|
|
33
|
+
const { start, end, transition, type, fractionDigits } = props;
|
|
34
|
+
const motionValue = {
|
|
35
|
+
start,
|
|
36
|
+
end,
|
|
37
|
+
transition,
|
|
38
|
+
type,
|
|
39
|
+
fractionDigits,
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
[DATA_MOTION_NUMBER]: JSON.stringify(motionValue),
|
|
43
|
+
[DATA_MOTION_NUMBER_ONCE]: props.once,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DATA_POPUP, DATA_POPUP_ROLE, PopupRole } from "../controller";
|
|
2
|
+
import { AnimationConfig, PopupAnimationConfig } from "../motion";
|
|
3
|
+
|
|
4
|
+
export const defaultPopoverPanelOpen: AnimationConfig = {
|
|
5
|
+
fromTo: {
|
|
6
|
+
from: {
|
|
7
|
+
height: 0,
|
|
8
|
+
opacity: 0,
|
|
9
|
+
},
|
|
10
|
+
to: {
|
|
11
|
+
height: "auto",
|
|
12
|
+
opacity: 1,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
duration: 0.6,
|
|
16
|
+
ease: "spring",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const defaultPopoverPanelClose: AnimationConfig = {
|
|
20
|
+
fromTo: {
|
|
21
|
+
from: {
|
|
22
|
+
height: "auto",
|
|
23
|
+
opacity: 1,
|
|
24
|
+
},
|
|
25
|
+
to: {
|
|
26
|
+
height: 0,
|
|
27
|
+
opacity: 0,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
duration: 0.25,
|
|
31
|
+
ease: "cubic-bezier(0.4, 0.0, 0.2, 1)",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const defaultPopoverPanelAnimation: PopupAnimationConfig = {
|
|
35
|
+
open: defaultPopoverPanelOpen,
|
|
36
|
+
close: defaultPopoverPanelClose,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function getPopoverDataAttrs(popupKey?: string) {
|
|
40
|
+
return {
|
|
41
|
+
[DATA_POPUP]: popupKey,
|
|
42
|
+
[DATA_POPUP_ROLE]: PopupRole.PopoverContainer,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getPopoverPanelDataAttrs(popupKey?: string) {
|
|
47
|
+
return {
|
|
48
|
+
[DATA_POPUP]: popupKey || "",
|
|
49
|
+
[DATA_POPUP_ROLE]: PopupRole.PopoverPanel,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DATA_TABS = "data-tabs";
|
|
2
|
+
export const DATA_TABS_HEADER = "data-tabs-header";
|
|
3
|
+
export const DATA_TABS_PANEL = "data-tabs-panel";
|
|
4
|
+
export const DATA_TABS_TAB = "data-tabs-tab";
|
|
5
|
+
export const DATA_TABS_BODY = "data-tabs-body";
|
|
6
|
+
//当前选中的选项卡
|
|
7
|
+
export const DATA_TABS_SELECTION = "data-tabs-selection";
|
|
8
|
+
//选中动画,存动画数据
|
|
9
|
+
export const DATA_MOTION_SELECTION = "data-motion-selection";
|
|
10
|
+
export const DATA_MOTION_FLIP = "data-motion-flip";
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { AnimationConfig, DATA_MOTION } from "../motion";
|
|
2
|
+
import { applyAnimation } from "./applyAnimation";
|
|
3
|
+
import { applyInitialState } from "./applyInitialState";
|
|
4
|
+
|
|
5
|
+
export class AnimateController {
|
|
6
|
+
private static instance: Record<string, AnimateController> = {};
|
|
7
|
+
private animations: gsap.core.Tween[] = [];
|
|
8
|
+
private unmountHandlers: Array<() => void> = [];
|
|
9
|
+
private registeredElements: WeakSet<Element> = new WeakSet();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 私有构造函数,防止外部直接创建实例
|
|
13
|
+
* @param clientDoc - 可选的Document对象,在SSR环境中可能为undefined
|
|
14
|
+
*/
|
|
15
|
+
private constructor(private clientDoc?: Document) {}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 获取单例实例
|
|
19
|
+
* @param key - 实例的唯一标识,默认为"default"
|
|
20
|
+
* @param clientDoc - 可选的Document对象
|
|
21
|
+
* @returns AnimateController 实例
|
|
22
|
+
*/
|
|
23
|
+
public static getInstance(
|
|
24
|
+
key: string = "runtime",
|
|
25
|
+
clientDoc?: Document
|
|
26
|
+
): AnimateController {
|
|
27
|
+
if (!AnimateController.instance[key]) {
|
|
28
|
+
// 在服务器端渲染时,document 不存在,传入 undefined
|
|
29
|
+
const doc =
|
|
30
|
+
clientDoc || (typeof document !== "undefined" ? document : undefined);
|
|
31
|
+
AnimateController.instance[key] = new AnimateController(doc);
|
|
32
|
+
}
|
|
33
|
+
// 这个代码很有必要,需要替换掉旧doc,因为react不确定刷新的问题
|
|
34
|
+
if (clientDoc) {
|
|
35
|
+
AnimateController.instance[key].destroy();
|
|
36
|
+
AnimateController.instance[key].setClientDoc(clientDoc);
|
|
37
|
+
}
|
|
38
|
+
return AnimateController.instance[key];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 销毁指定的实例
|
|
43
|
+
* @param key - 实例的唯一标识
|
|
44
|
+
*/
|
|
45
|
+
public static destroyInstance(key: string = "runtime"): void {
|
|
46
|
+
if (AnimateController.instance[key]) {
|
|
47
|
+
AnimateController.instance[key].destroy();
|
|
48
|
+
delete AnimateController.instance[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 重置所有实例(主要用于测试)
|
|
54
|
+
*/
|
|
55
|
+
public static resetAll(): void {
|
|
56
|
+
Object.keys(AnimateController.instance).forEach((key) => {
|
|
57
|
+
AnimateController.instance[key].destroy();
|
|
58
|
+
});
|
|
59
|
+
AnimateController.instance = {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 设置客户端文档对象
|
|
64
|
+
* @param clientDoc - Document对象
|
|
65
|
+
*/
|
|
66
|
+
public setClientDoc(clientDoc: Document): void {
|
|
67
|
+
this.clientDoc = clientDoc;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 挂载动画控制器,初始化所有动画
|
|
72
|
+
* @param clientDoc - 可选的Document对象,如果未提供则使用构造函数中的文档
|
|
73
|
+
*/
|
|
74
|
+
public mount(clientDoc?: Document): void {
|
|
75
|
+
const doc = clientDoc || this.clientDoc;
|
|
76
|
+
if (!doc) return;
|
|
77
|
+
|
|
78
|
+
// 查找所有带有 data-motion 属性的元素
|
|
79
|
+
const elements = doc.querySelectorAll(`[${DATA_MOTION}]`);
|
|
80
|
+
// 初始化新的动画
|
|
81
|
+
elements.forEach((element) => {
|
|
82
|
+
// 检查元素是否已经被注册过
|
|
83
|
+
if (this.registeredElements.has(element)) {
|
|
84
|
+
return; // 如果已经注册过,则跳过
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
doc.defaultView &&
|
|
89
|
+
element instanceof doc.defaultView.HTMLElement &&
|
|
90
|
+
element.dataset.motion
|
|
91
|
+
) {
|
|
92
|
+
try {
|
|
93
|
+
const animation = JSON.parse(element.dataset.motion) as
|
|
94
|
+
| AnimationConfig
|
|
95
|
+
| undefined;
|
|
96
|
+
if (animation) {
|
|
97
|
+
if (animation.initial) {
|
|
98
|
+
applyInitialState(element, animation.initial);
|
|
99
|
+
}
|
|
100
|
+
// 执行动画
|
|
101
|
+
const tween = applyAnimation(element, animation);
|
|
102
|
+
if (tween) {
|
|
103
|
+
this.animations.push(tween);
|
|
104
|
+
// 将元素添加到已注册集合中
|
|
105
|
+
this.registeredElements.add(element);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("解析动画配置失败", error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 销毁动画控制器,清理所有动画和事件监听器
|
|
117
|
+
*/
|
|
118
|
+
public destroy(): void {
|
|
119
|
+
// 停止所有动画
|
|
120
|
+
this.animations.forEach((tween) => {
|
|
121
|
+
tween.kill();
|
|
122
|
+
});
|
|
123
|
+
this.animations = [];
|
|
124
|
+
|
|
125
|
+
// 执行所有卸载处理函数
|
|
126
|
+
this.unmountHandlers.forEach((handler) => handler());
|
|
127
|
+
this.unmountHandlers = [];
|
|
128
|
+
|
|
129
|
+
// 创建新的 WeakSet 来清理已注册元素的跟踪
|
|
130
|
+
this.registeredElements = new WeakSet();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 导出默认实例,方便直接使用
|
|
135
|
+
export const animate = AnimateController.getInstance(
|
|
136
|
+
"runtime",
|
|
137
|
+
typeof document !== "undefined" ? document : undefined
|
|
138
|
+
);
|