@rxdrag/website-lib 0.0.143 → 0.0.145

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.
@@ -1,8 +1,10 @@
1
1
  ---
2
+ import "aos/dist/aos.css";
2
3
  import { PageType, type PageMeta } from "@rxdrag/rxcms-models";
3
4
  import type { Locals } from "@rxdrag/website-lib-core";
4
5
  import Meta from "@rxdrag/website-lib/components/Meta.astro";
5
6
  import { Entify } from "@rxdrag/website-lib-core";
7
+ import { ClientRouter } from "astro:transitions";
6
8
 
7
9
  const { env, imageSizes } = Astro.locals as Locals;
8
10
 
@@ -31,6 +33,15 @@ const {
31
33
  ...rest
32
34
  } = Astro.props;
33
35
 
36
+ const bodyAttrs = {
37
+ "data-aos-easing": "ease-out-cubic",
38
+ "data-aos-duration": "800",
39
+ "data-aos-delay": "0",
40
+ "data-aos-offset": "50",
41
+ "data-aos-once": "true",
42
+ ...rest,
43
+ };
44
+
34
45
  //TODO: 添加meta获取逻辑,有可能需要重构后端数据结构,现在看来,只有动态内容需要获取meta,其它页面可以传入
35
46
 
36
47
  let resolvedMeta: PageMeta | undefined = meta;
@@ -40,8 +51,10 @@ let resolvedTitle: string | undefined = title;
40
51
  if (!resolvedMeta || !resolvedTitle) {
41
52
  try {
42
53
  const pathname = Astro.url?.pathname || "";
43
- const slug = typeof Astro.params?.slug === "string" ? Astro.params.slug : undefined;
44
- const id = typeof Astro.params?.id === "string" ? Astro.params.id : undefined;
54
+ const slug =
55
+ typeof Astro.params?.slug === "string" ? Astro.params.slug : undefined;
56
+ const id =
57
+ typeof Astro.params?.id === "string" ? Astro.params.id : undefined;
45
58
 
46
59
  if (slug && pathname.startsWith("/posts/categories/")) {
47
60
  const category = await rx.getPostCategoryBySlug(slug);
@@ -63,7 +76,10 @@ if (!resolvedMeta || !resolvedTitle) {
63
76
  resolvedTitle = resolvedTitle ?? post?.title;
64
77
  resolvedMeta = resolvedMeta ?? post?.meta;
65
78
  } else if (slug && pathname.startsWith("/products/")) {
66
- const product = await rx.getProductBySlug(slug, { width: 800, height: 800 });
79
+ const product = await rx.getProductBySlug(slug, {
80
+ width: 800,
81
+ height: 800,
82
+ });
67
83
  resolvedTitle = resolvedTitle ?? product?.title;
68
84
  resolvedMeta = resolvedMeta ?? product?.meta;
69
85
  }
@@ -86,6 +102,99 @@ if (!resolvedMeta || !resolvedTitle) {
86
102
  <link rel="sitemap" href="/sitemap-index.xml" />
87
103
  <link rel="icon" type="image/png" href="/favicon.png" />
88
104
  <!-- 给上层 Layout/页面注入自定义 head 内容(预加载、脚本、样式等) -->
105
+ <!-- 预连接 -->
106
+ <link
107
+ rel="preconnect"
108
+ href="https://customer-amj1dt4tnge8w6ji.cloudflarestream.com"
109
+ crossorigin
110
+ />
111
+ <script>
112
+ const w = window as any;
113
+ const state =
114
+ w.__aos_state__ ||
115
+ (w.__aos_state__ = { inited: false, loading: null, aos: null });
116
+
117
+ const el = document.documentElement;
118
+ const raf = () =>
119
+ new Promise((resolve) =>
120
+ requestAnimationFrame(() => resolve(undefined)),
121
+ );
122
+
123
+ const ensureAos = async () => {
124
+ if (state.aos) return state.aos;
125
+ if (!state.loading) {
126
+ state.loading = import("aos").then((m) => {
127
+ state.aos = m.default;
128
+ return state.aos;
129
+ });
130
+ }
131
+ return state.loading;
132
+ };
133
+
134
+ const bootOrRefresh = async () => {
135
+ if (
136
+ window.matchMedia &&
137
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
138
+ ) {
139
+ el.classList.add("aos-ready");
140
+ el.classList.remove("aos-preload");
141
+ return;
142
+ }
143
+
144
+ if (!document.querySelector("[data-aos]")) {
145
+ el.classList.add("aos-ready");
146
+ el.classList.remove("aos-preload");
147
+ return;
148
+ }
149
+
150
+ el.classList.add("aos-preload");
151
+ el.classList.remove("aos-ready");
152
+
153
+ const AOS = await ensureAos();
154
+ if (!state.inited) {
155
+ state.inited = true;
156
+ AOS.init({
157
+ duration: 800,
158
+ easing: "ease-out-cubic",
159
+ once: true,
160
+ offset: 50,
161
+ });
162
+ }
163
+
164
+ await raf();
165
+ await raf();
166
+ AOS.refresh();
167
+
168
+ el.classList.add("aos-ready");
169
+ el.classList.remove("aos-preload");
170
+ };
171
+
172
+ const boot = () => {
173
+ bootOrRefresh();
174
+ };
175
+
176
+ if (document.readyState === "loading") {
177
+ document.addEventListener("DOMContentLoaded", boot, { once: true });
178
+ } else {
179
+ boot();
180
+ }
181
+
182
+ document.addEventListener("astro:page-load", boot);
183
+ document.addEventListener("astro:after-swap", boot);
184
+ </script>
185
+ <ClientRouter />
186
+ <!-- Consent default (Google) -->
187
+ <script is:inline>
188
+ window.dataLayer = window.dataLayer || [];
189
+ function gtag() {
190
+ dataLayer.push(arguments);
191
+ }
192
+ gtag("js", new Date());
193
+ gtag("consent", "default", {
194
+ ad_storage: "denied",
195
+ analytics_storage: "denied",
196
+ });
197
+ </script>
89
198
  <slot name="head" />
90
199
  <Meta title={resolvedTitle} content={resolvedMeta} />
91
200
  <style>
@@ -97,7 +206,7 @@ if (!resolvedMeta || !resolvedTitle) {
97
206
  </style>
98
207
  </head>
99
208
  <!-- rest 会被展开到 body 上,方便上层控制 data-* 等属性 -->
100
- <body class:list={[className, className2]} {...rest}>
209
+ <body class:list={[className, className2]} {...bodyAttrs}>
101
210
  <slot />
102
211
  </body>
103
212
  </html>
@@ -0,0 +1,204 @@
1
+ ---
2
+ import Section from "./Section.astro";
3
+ import clsx from "clsx";
4
+ import "swiper/css";
5
+ import "swiper/css/pagination";
6
+ import "swiper/css/navigation";
7
+
8
+ interface Props {
9
+ class?: string;
10
+ }
11
+
12
+ const { class: className, ...rest } = Astro.props;
13
+ ---
14
+
15
+ <Section
16
+ className={clsx(
17
+ "w-full h-[300px] sm:h-[400px] md:h-screen flex flex-col mt-0 bg-black relative overflow-hidden z-10 py-0",
18
+ className,
19
+ )}
20
+ disableContainer
21
+ {...rest}
22
+ >
23
+ <div class="hero-carousel w-full h-full relative" data-hero-carousel>
24
+ <div class="swiper h-full relative">
25
+ <div class="swiper-wrapper h-full">
26
+ <slot />
27
+ </div>
28
+ <slot name="navigation" />
29
+ <slot name="pagination" />
30
+ </div>
31
+ </div>
32
+ </Section>
33
+
34
+ <script>
35
+ type SwiperRootEl = HTMLElement & { __swiperInstance?: any };
36
+ type SwiperType = typeof import("swiper").default;
37
+ type SwiperModulesType = typeof import("swiper/modules");
38
+
39
+ type BootWindow = Window & {
40
+ __heroCarouselBootstrapped?: boolean;
41
+ };
42
+
43
+ const destroyRoot = (root: SwiperRootEl) => {
44
+ const existing = root.__swiperInstance;
45
+ if (existing && typeof existing.destroy === "function") {
46
+ existing.destroy(true, true);
47
+ root.__swiperInstance = null;
48
+ }
49
+ root.dataset.heroCarouselInited = "";
50
+ };
51
+
52
+ const loadSwiperResources = async () => {
53
+ const [{ default: Swiper }, { Autoplay, Navigation, Pagination }] =
54
+ await Promise.all([import("swiper"), import("swiper/modules")]);
55
+ return { Swiper, Autoplay, Navigation, Pagination };
56
+ };
57
+
58
+ let swiperCache: {
59
+ Swiper: SwiperType;
60
+ Autoplay: SwiperModulesType["Autoplay"];
61
+ Navigation: SwiperModulesType["Navigation"];
62
+ Pagination: SwiperModulesType["Pagination"];
63
+ } | null = null;
64
+
65
+ const getSwiperModules = async () => {
66
+ if (!swiperCache) {
67
+ swiperCache = await loadSwiperResources();
68
+ }
69
+ return swiperCache;
70
+ };
71
+
72
+ const initRoot = async (root: SwiperRootEl) => {
73
+ if (root.dataset.heroCarouselInited === "1") {
74
+ return;
75
+ }
76
+
77
+ const el = root.querySelector(".swiper") as HTMLElement | null;
78
+ if (!el) return;
79
+
80
+ const paginationEl = root.querySelector(
81
+ ".swiper-pagination",
82
+ ) as HTMLElement | null;
83
+ const prevEl = root.querySelector(
84
+ ".swiper-button-prev",
85
+ ) as HTMLElement | null;
86
+ const nextEl = root.querySelector(
87
+ ".swiper-button-next",
88
+ ) as HTMLElement | null;
89
+
90
+ const wantPagination = !!paginationEl;
91
+ const wantNavigation = !!prevEl && !!nextEl;
92
+
93
+ destroyRoot(root);
94
+
95
+ const { Swiper, Autoplay, Navigation, Pagination } =
96
+ await getSwiperModules();
97
+
98
+ const modules = [Autoplay] as any[];
99
+ if (wantPagination) {
100
+ modules.push(Pagination);
101
+ }
102
+ if (wantNavigation) {
103
+ modules.push(Navigation);
104
+ }
105
+
106
+ const instance = new Swiper(el, {
107
+ modules,
108
+ slidesPerView: 1,
109
+ spaceBetween: 0,
110
+ rewind: true,
111
+ observer: true,
112
+ observeParents: true,
113
+ autoplay: {
114
+ delay: 3500,
115
+ disableOnInteraction: false,
116
+ pauseOnMouseEnter: false,
117
+ },
118
+ ...(wantPagination && paginationEl
119
+ ? {
120
+ pagination: {
121
+ el: paginationEl,
122
+ clickable: true,
123
+ type: "bullets",
124
+ bulletActiveClass: "swiper-pagination-bullet-active",
125
+ },
126
+ }
127
+ : {}),
128
+ ...(wantNavigation && prevEl && nextEl
129
+ ? {
130
+ navigation: {
131
+ prevEl,
132
+ nextEl,
133
+ },
134
+ }
135
+ : {}),
136
+ });
137
+
138
+ try {
139
+ instance.autoplay?.start?.();
140
+ } catch {}
141
+
142
+ root.__swiperInstance = instance;
143
+ root.dataset.heroCarouselInited = "1";
144
+ };
145
+
146
+ const initAll = async () => {
147
+ const roots = document.querySelectorAll(
148
+ "[data-hero-carousel]",
149
+ ) as NodeListOf<SwiperRootEl>;
150
+ if (roots.length === 0) return;
151
+ await Promise.all(Array.from(roots).map((root) => initRoot(root)));
152
+ };
153
+
154
+ const destroyAll = () => {
155
+ const roots = document.querySelectorAll(
156
+ "[data-hero-carousel]",
157
+ ) as NodeListOf<SwiperRootEl>;
158
+ roots.forEach((root) => destroyRoot(root));
159
+ };
160
+
161
+ const ensureAutoplayAll = () => {
162
+ const roots = document.querySelectorAll(
163
+ "[data-hero-carousel]",
164
+ ) as NodeListOf<SwiperRootEl>;
165
+ roots.forEach((root) => {
166
+ const inst = root.__swiperInstance;
167
+ try {
168
+ inst?.autoplay?.start?.();
169
+ } catch {}
170
+ });
171
+ };
172
+
173
+ const boot = async () => {
174
+ try {
175
+ await initAll();
176
+ } catch (e) {
177
+ console.log("HeroCarousel init error:", e);
178
+ }
179
+ };
180
+
181
+ const w = window as BootWindow;
182
+ if (!w.__heroCarouselBootstrapped) {
183
+ w.__heroCarouselBootstrapped = true;
184
+
185
+ if (document.readyState === "loading") {
186
+ document.addEventListener("DOMContentLoaded", boot);
187
+ } else {
188
+ boot();
189
+ }
190
+
191
+ document.addEventListener("astro:page-load", boot);
192
+ document.addEventListener("astro:after-swap", () => {
193
+ boot();
194
+ ensureAutoplayAll();
195
+ });
196
+ window.addEventListener("astro:before-swap", destroyAll);
197
+
198
+ document.addEventListener("visibilitychange", () => {
199
+ if (!document.hidden) {
200
+ ensureAutoplayAll();
201
+ }
202
+ });
203
+ }
204
+ </script>
@@ -0,0 +1,24 @@
1
+ ---
2
+ import Box from "./Box.astro";
3
+ import Container from "./Container.astro";
4
+ import clsx from "clsx";
5
+ import type { BackgroundConfig } from "@rxdrag/website-lib-core";
6
+
7
+ interface Props {
8
+ class?: string;
9
+ backgrounds?: BackgroundConfig[];
10
+ }
11
+
12
+ const { class: className, backgrounds } = Astro.props;
13
+ ---
14
+
15
+ <div class={clsx("swiper-slide h-full")}>
16
+ <Box
17
+ class={clsx("w-full h-full flex flex-col justify-center")}
18
+ backgrounds={backgrounds}
19
+ >
20
+ <Container class={className}>
21
+ <slot />
22
+ </Container>
23
+ </Box>
24
+ </div>
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@rxdrag/website-lib",
3
- "version": "0.0.143",
3
+ "version": "0.0.145",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts",
7
+ "./components/HeroCarousel.astro": "./components/HeroCarousel.astro",
8
+ "./components/HeroCarouselPanel.astro": "./components/HeroCarouselPanel.astro",
7
9
  "./components/*.astro": "./components/*.astro"
8
10
  },
9
11
  "files": [
@@ -25,12 +27,13 @@
25
27
  "astro": "^5.16.6",
26
28
  "eslint": "^9.39.2",
27
29
  "gsap": "^3.12.7",
30
+ "swiper": "^12.0.3",
28
31
  "typescript": "^5",
29
- "@rxdrag/eslint-config-custom": "0.2.13",
30
- "@rxdrag/entify-hooks": "0.2.79",
31
32
  "@rxdrag/rxcms-models": "0.3.106",
32
33
  "@rxdrag/tiptap-preview": "0.0.3",
33
- "@rxdrag/tsconfig": "0.2.1"
34
+ "@rxdrag/entify-hooks": "0.2.79",
35
+ "@rxdrag/tsconfig": "0.2.1",
36
+ "@rxdrag/eslint-config-custom": "0.2.13"
34
37
  },
35
38
  "dependencies": {
36
39
  "aos": "3.0.0-beta.6",
@@ -42,7 +45,8 @@
42
45
  "@rxdrag/website-lib-core": "0.0.125"
43
46
  },
44
47
  "peerDependencies": {
45
- "astro": "^5.16.6"
48
+ "astro": "^5.16.6",
49
+ "swiper": "^12.0.3"
46
50
  },
47
51
  "scripts": {
48
52
  "lint": "eslint . --ext .ts,tsx",