@rxdrag/website-lib 0.0.177 → 1.0.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.
@@ -1,132 +1,140 @@
1
- ---
2
- import type { BackgroundConfig } from "@rxdrag/website-lib-core";
3
- import type { Locals } from "@rxdrag/website-lib-core/server";
4
- import { Entify } from "@rxdrag/website-lib-core/server";
5
- import { BackgroundVideoPlayer } from "@rxdrag/website-lib-react/ui";
6
- import { BackgroundHlsVideoPlayer } from "@rxdrag/website-lib-react/video";
7
- import Image from "./Image.astro";
8
- import GlassBorder from "./GlassBorder.astro";
9
- import GradientBorder from "./GradientBorder.astro";
10
- import GlassRefraction from "./GlassRefraction.astro";
11
-
12
- export interface Props {
13
- background: BackgroundConfig;
14
- index?: number;
15
- }
16
-
17
- const { env, imageSizes } = Astro.locals as Locals;
18
-
19
- const { background } = Astro.props;
20
-
21
- const baseFill = "absolute inset-0 w-full h-full";
22
- const defaultFill = `${baseFill} object-cover`;
23
- const rawClass = (background.class || "").trim();
24
- const hasLayoutOverride = /(?:^|\s)(?:inset(?:-[xy])?-|top-|bottom-|left-|right-|w-|h-)/.test(
25
- rawClass,
26
- );
27
- const className = rawClass
28
- ? `${hasLayoutOverride ? "absolute" : defaultFill} ${rawClass}`
29
- : defaultFill;
30
-
31
- const mediaRef = background.type === "video" ? background.mediaRef : undefined;
32
-
33
- const rx = Entify.getInstance(env, imageSizes);
34
-
35
- const media = rx ? await rx.getMedia(mediaRef) : undefined;
36
-
37
- const videoUrl =
38
- background.type === "video" ? media?.file?.original : undefined;
39
-
40
- let posterUrl: string | undefined;
41
- if (background.type === "video" && background.poster) {
42
- if (typeof background.poster === "string") {
43
- posterUrl = background.poster;
44
- } else if (typeof background.poster === "object") {
45
- const poster = background.poster as Partial<ImageMetadata>;
46
- if (
47
- typeof poster.src === "string" &&
48
- typeof poster.width === "number" &&
49
- typeof poster.height === "number"
50
- ) {
51
- posterUrl = poster.src;
52
- } else {
53
- console.log(
54
- "[Background] invalid video poster, expect ImageMetadata:",
55
- background.poster,
56
- );
57
- }
58
- }
59
- }
60
-
61
- const isHls =
62
- background.type === "video" &&
63
- (media?.storageType === "cloudflare_stream" ||
64
- videoUrl?.endsWith(".m3u8") ||
65
- videoUrl?.includes("cloudflarestream.com"));
66
-
67
- const splineUrl = background.type === "spline" ? background.url : undefined;
68
- ---
69
-
70
- {background.type === "color" && <div class={className} />}
71
-
72
- {background.type === "blur" && <div class={className} />}
73
-
74
- {
75
- background.type === "glass" && (
76
- <GlassRefraction class={className} />
77
- )
78
- }
79
-
80
- {
81
- background.type === "glass-border" && (
82
- <GlassBorder {...background} class={className} />
83
- )
84
- }
85
-
86
- {
87
- background.type === "gradient-border" && (
88
- <GradientBorder {...background} class={className} />
89
- )
90
- }
91
-
92
- {
93
- background.type === "image" && background.src && (
94
- <Image
95
- class={className}
96
- src={background.src}
97
- layout={background.layout || "full-width"}
98
- alt=""
99
- />
100
- )
101
- }
102
-
103
- {
104
- background.type === "svg" && background.code && (
105
- <div class={className} set:html={background.code} />
106
- )
107
- }
108
-
109
- {
110
- background.type === "video" &&
111
- (isHls ? (
112
- <BackgroundHlsVideoPlayer
113
- className={className}
114
- src={videoUrl}
115
- poster={posterUrl}
116
- client:only="react"
117
- />
118
- ) : (
119
- <BackgroundVideoPlayer
120
- className={className}
121
- src={videoUrl}
122
- poster={posterUrl}
123
- client:only="react"
124
- />
125
- ))
126
- }
127
-
128
- {
129
- background.type === "spline" && (
130
- <iframe class={className} src={splineUrl} title="Spline 3D Scene" />
131
- )
132
- }
1
+ ---
2
+ import type { BackgroundConfig, Locals } from "@rxdrag/website-lib-core";
3
+ import {
4
+ BackgroundVideoPlayer,
5
+ BackgroundHlsVideoPlayer,
6
+ Entify,
7
+ } from "@rxdrag/website-lib-core";
8
+ import Image from "./Image.astro";
9
+ import GlassBorder from "./GlassBorder.astro";
10
+ import GradientBorder from "./GradientBorder.astro";
11
+ import GlassRefraction from "./GlassRefraction.astro";
12
+
13
+ export interface Props {
14
+ background: BackgroundConfig;
15
+ index?: number;
16
+ }
17
+
18
+ const { env, imageSizes } = Astro.locals as Locals;
19
+
20
+ const { background } = Astro.props;
21
+
22
+ const baseFill = "absolute inset-0 w-full h-full";
23
+ const defaultFill = `${baseFill} object-cover`;
24
+ const rawClass = (background.class || "").trim();
25
+ const hasLayoutOverride = /(?:^|\s)(?:inset(?:-[xy])?-|top-|bottom-|left-|right-|w-|h-)/.test(
26
+ rawClass,
27
+ );
28
+ const className = rawClass
29
+ ? `${hasLayoutOverride ? "absolute" : defaultFill} ${rawClass}`
30
+ : defaultFill;
31
+
32
+ // 安全地提取 mediaRef 和 posterRef(只有部分背景类型有这些属性)
33
+ const mediaRef = background.type === "video" ? background.mediaRef : undefined;
34
+
35
+ const rx = Entify.getInstance(env, imageSizes);
36
+
37
+ // 获取媒体数据
38
+ const media = rx ? await rx.getMedia(mediaRef) : undefined;
39
+
40
+ // 预计算视频相关数据
41
+ const videoUrl =
42
+ background.type === "video" ? media?.file?.original : undefined;
43
+
44
+ // 处理视频封面
45
+ let posterUrl: string | undefined;
46
+ if (background.type === "video" && background.poster) {
47
+ if (typeof background.poster === "string") {
48
+ posterUrl = background.poster;
49
+ } else if (typeof background.poster === "object") {
50
+ const poster = background.poster as Partial<ImageMetadata>;
51
+ if (
52
+ typeof poster.src === "string" &&
53
+ typeof poster.width === "number" &&
54
+ typeof poster.height === "number"
55
+ ) {
56
+ posterUrl = poster.src;
57
+ } else {
58
+ console.log(
59
+ "[Background] invalid video poster, expect ImageMetadata:",
60
+ background.poster,
61
+ );
62
+ }
63
+ }
64
+ }
65
+
66
+ const isHls =
67
+ background.type === "video" &&
68
+ (media?.storageType === "cloudflare_stream" ||
69
+ videoUrl?.endsWith(".m3u8") ||
70
+ videoUrl?.includes("cloudflarestream.com"));
71
+
72
+ // 预计算 Spline URL
73
+ const splineUrl = background.type === "spline" ? background.url : undefined;
74
+
75
+ ---
76
+
77
+ {background.type === "color" && <div class={className} />}
78
+
79
+ {background.type === "blur" && <div class={className} />}
80
+
81
+ {
82
+ background.type === "glass" && (
83
+ <GlassRefraction class={className} />
84
+ )
85
+ }
86
+
87
+ {
88
+ background.type === "glass-border" && (
89
+ <GlassBorder {...background} class={className} />
90
+ )
91
+ }
92
+
93
+ {
94
+ background.type === "gradient-border" && (
95
+ <GradientBorder {...background} class={className} />
96
+ )
97
+ }
98
+
99
+ {
100
+ background.type === "image" && background.src && (
101
+ <Image
102
+ class={className}
103
+ src={background.src}
104
+ layout={background.layout || "full-width"}
105
+ alt=""
106
+ />
107
+ )
108
+ }
109
+
110
+ {
111
+ background.type === "svg" && background.code && (
112
+ <div class={className} set:html={background.code} />
113
+ )
114
+ }
115
+
116
+ {
117
+ background.type === "video" &&
118
+ (isHls ? (
119
+ <BackgroundHlsVideoPlayer
120
+ className={className}
121
+ src={videoUrl}
122
+ poster={posterUrl}
123
+ client:load
124
+ />
125
+ ) : (
126
+ <BackgroundVideoPlayer
127
+ className={className}
128
+ src={videoUrl}
129
+ poster={posterUrl}
130
+ client:load
131
+ />
132
+ ))
133
+ }
134
+
135
+ {
136
+ background.type === "spline" && (
137
+ <iframe class={className} src={splineUrl} title="Spline 3D Scene" />
138
+ )
139
+ }
140
+
@@ -1,256 +1,256 @@
1
- ---
2
- import "aos/dist/aos.css";
3
- import type { PageMeta } from "@rxdrag/rxcms-models";
4
- import type { Locals } from "@rxdrag/website-lib-core/server";
5
- import Meta from "./Meta.astro";
6
- import { Entify } from "@rxdrag/website-lib-core/server";
7
- import { ClientRouter } from "astro:transitions";
8
-
9
- const { env, imageSizes, currentHtmlLang, langs } = Astro.locals as Locals;
10
-
11
- // 获取 Entify 实例
12
- const rx = Entify.getInstance(env, imageSizes);
13
-
14
- // 使用中间件下发的语言数据(如果中间件失败,提供回退逻辑)
15
- const defaultLang = currentHtmlLang || "en";
16
-
17
- // 获取当前语言的 dir 属性(用于 RTL 语言如阿拉伯语、希伯来语等)
18
- const currentLangAbbr = (Astro.locals as any).currentLangAbbr;
19
- const currentLangObj = langs?.find((l) => l.abbr === currentLangAbbr) || langs?.[0];
20
- const defaultDir = currentLangObj?.dir || "ltr";
21
-
22
- // 注意:langAbbr 已由中间件设置到 Entify 实例,无需再次调用 setLangAbbr
23
-
24
- // 计算支持的语言列表(用于路径解析)
25
- const supportedLangs = langs?.map((l) => l.htmlLang) || [];
26
- const firstSegment = Astro.url.pathname.split("/")[1];
27
-
28
- // 文档壳:只负责 <html>/<head>/<body> 结构,不包含 Header/Footer 等站点布局。
29
- interface Props {
30
- // html 的 lang 属性(可选,传入则覆盖默认动态获取的语言)
31
- lang?: string;
32
- // SEO Meta(由上层 Layout/页面传入)
33
- meta?: PageMeta;
34
- // 页面标题(由 Meta 组件消费)
35
- title?: string;
36
- // JSON-LD 结构化数据,支持单个或数组
37
- jsonLd?: Record<string, unknown> | Record<string, unknown>[];
38
- // 兼容 class / className 两种写法,最终合并到 body 上
39
- class?: string;
40
- className?: string;
41
- }
42
-
43
- const {
44
- lang,
45
- meta,
46
- title,
47
- jsonLd,
48
- class: className = "scroll-smooth",
49
- className: className2,
50
- // 其余 props 透传到 body(例如 data-* 属性等)
51
- ...rest
52
- } = Astro.props;
53
-
54
- const jsonLdItems = jsonLd ? (Array.isArray(jsonLd) ? jsonLd : [jsonLd]) : [];
55
-
56
- const bodyAttrs = {
57
- "data-aos-easing": "ease-out-cubic",
58
- "data-aos-duration": "800",
59
- "data-aos-delay": "0",
60
- "data-aos-offset": "50",
61
- "data-aos-once": "true",
62
- ...rest,
63
- };
64
-
65
- let resolvedMeta: PageMeta | undefined = meta;
66
- let resolvedTitle: string | undefined = title;
67
-
68
- // 仅在未传入 meta/title 时,针对动态详情页兜底拉取
69
- if (!resolvedMeta || !resolvedTitle) {
70
- try {
71
- const pathname = Astro.url?.pathname || "";
72
- const slug =
73
- typeof Astro.params?.slug === "string" ? Astro.params.slug : undefined;
74
- const id =
75
- typeof Astro.params?.id === "string" ? Astro.params.id : undefined;
76
-
77
- // 去掉语言前缀,支持多语言路径
78
- // 例如: /en/posts/slug -> /posts/slug
79
- const pathWithoutLang = supportedLangs.includes(firstSegment)
80
- ? pathname.slice(firstSegment.length + 1) // +1 去掉开头的 /
81
- : pathname;
82
-
83
- if (slug && pathWithoutLang.startsWith("/posts/categories/")) {
84
- const category = await rx.getPostCategoryBySlug(slug);
85
- resolvedTitle = resolvedTitle ?? category?.name ?? "Blog";
86
- } else if (slug && pathWithoutLang.startsWith("/products/categories/")) {
87
- const category = await rx.getProductCategoryBySlug(slug);
88
- resolvedTitle = resolvedTitle ?? category?.name ?? "Products";
89
- } else if (id && pathWithoutLang.startsWith("/profiles/")) {
90
- const user = await rx.getOneUser(id);
91
- resolvedTitle = resolvedTitle ?? user?.name ?? "Profile";
92
- } else if (slug && pathWithoutLang.startsWith("/posts/")) {
93
- const post = await rx.getPostBySlug(slug, { width: 1200, height: 600 });
94
- resolvedTitle = resolvedTitle ?? post?.title;
95
- resolvedMeta = resolvedMeta ?? post?.meta;
96
- } else if (slug && pathWithoutLang.startsWith("/products/")) {
97
- const product = await rx.getProductBySlug(slug, {
98
- width: 800,
99
- height: 800,
100
- });
101
- resolvedTitle = resolvedTitle ?? product?.title;
102
- resolvedMeta = resolvedMeta ?? product?.meta;
103
- }
104
- } catch (e) {
105
- console.log("[Document] resolve meta failed:", e);
106
- }
107
- }
108
- ---
109
-
110
- <!doctype html>
111
- <html lang={lang || defaultLang} dir={defaultDir} class="aos-preload">
112
- <head>
113
- <meta charset="UTF-8" />
114
- <meta
115
- name="viewport"
116
- content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=0.5, user-scalable=yes"
117
- />
118
-
119
- <meta name="generator" content={Astro.generator} />
120
- <link rel="sitemap" href="/sitemap-index.xml" />
121
- <!-- 给上层 Layout/页面注入自定义 head 内容(预加载、脚本、样式等) -->
122
- <!-- 预连接 -->
123
- <link
124
- rel="preconnect"
125
- href="https://customer-amj1dt4tnge8w6ji.cloudflarestream.com"
126
- crossorigin
127
- />
128
- <script>
129
- const w = window as any;
130
- const state =
131
- w.__aos_state__ ||
132
- (w.__aos_state__ = { inited: false, loading: null, aos: null });
133
-
134
- const el = document.documentElement;
135
- const raf = () =>
136
- new Promise((resolve) =>
137
- requestAnimationFrame(() => resolve(undefined)),
138
- );
139
-
140
- const ensureAos = async () => {
141
- if (state.aos) return state.aos;
142
- if (!state.loading) {
143
- state.loading = import("aos").then((m) => {
144
- state.aos = m.default;
145
- return state.aos;
146
- });
147
- }
148
- return state.loading;
149
- };
150
-
151
- const bootOrRefresh = async () => {
152
- if (
153
- window.matchMedia &&
154
- window.matchMedia("(prefers-reduced-motion: reduce)").matches
155
- ) {
156
- el.classList.add("aos-ready");
157
- el.classList.remove("aos-preload");
158
- return;
159
- }
160
-
161
- if (!document.querySelector("[data-aos]")) {
162
- el.classList.add("aos-ready");
163
- el.classList.remove("aos-preload");
164
- return;
165
- }
166
-
167
- el.classList.add("aos-preload");
168
- el.classList.remove("aos-ready");
169
-
170
- const AOS = await ensureAos();
171
- if (!state.inited) {
172
- state.inited = true;
173
- AOS.init({
174
- duration: 800,
175
- easing: "ease-out-cubic",
176
- once: true,
177
- offset: 50,
178
- });
179
- }
180
-
181
- await raf();
182
- await raf();
183
- AOS.refresh();
184
-
185
- el.classList.add("aos-ready");
186
- el.classList.remove("aos-preload");
187
- };
188
-
189
- const boot = () => {
190
- bootOrRefresh();
191
- };
192
-
193
- if (document.readyState === "loading") {
194
- document.addEventListener("DOMContentLoaded", boot, { once: true });
195
- } else {
196
- boot();
197
- }
198
-
199
- document.addEventListener("astro:page-load", boot);
200
- document.addEventListener("astro:after-swap", boot);
201
- </script>
202
- <ClientRouter />
203
- <!-- Consent default (Google) -->
204
- <script is:inline>
205
- window.dataLayer = window.dataLayer || [];
206
- function gtag() {
207
- dataLayer.push(arguments);
208
- }
209
- gtag("js", new Date());
210
- gtag("consent", "default", {
211
- ad_storage: "denied",
212
- analytics_storage: "denied",
213
- });
214
- </script>
215
- <slot name="head" />
216
- <Meta title={resolvedTitle} content={resolvedMeta} />
217
- {
218
- jsonLdItems.map((item) => (
219
- <script
220
- is:inline
221
- type="application/ld+json"
222
- set:html={JSON.stringify(item)}
223
- />
224
- ))
225
- }
226
- <style>
227
- html.aos-preload [data-aos] {
228
- opacity: 1 !important;
229
- transform: none !important;
230
- transition: none !important;
231
- }
232
- </style>
233
- </head>
234
- <!-- rest 会被展开到 body 上,方便上层控制 data-* 等属性 -->
235
- <body class:list={[className, className2]} {...bodyAttrs}>
236
- <slot />
237
- </body>
238
- </html>
239
-
240
- <style>
241
- html,
242
- body {
243
- margin: 0;
244
- width: 100%;
245
- height: 100%;
246
- }
247
-
248
- html {
249
- scroll-behavior: smooth;
250
- scrollbar-gutter: stable;
251
- }
252
-
253
- body {
254
- scrollbar-gutter: stable;
255
- }
256
- </style>
1
+ ---
2
+ import "aos/dist/aos.css";
3
+ import type { PageMeta } from "@rxdrag/rxcms-models";
4
+ import type { Locals } from "@rxdrag/website-lib-core";
5
+ import Meta from "@rxdrag/website-lib/components/Meta.astro";
6
+ import { Entify } from "@rxdrag/website-lib-core";
7
+ import { ClientRouter } from "astro:transitions";
8
+
9
+ const { env, imageSizes, currentHtmlLang, langs } = Astro.locals as Locals;
10
+
11
+ // 获取 Entify 实例
12
+ const rx = Entify.getInstance(env, imageSizes);
13
+
14
+ // 使用中间件下发的语言数据(如果中间件失败,提供回退逻辑)
15
+ const defaultLang = currentHtmlLang || "en";
16
+
17
+ // 获取当前语言的 dir 属性(用于 RTL 语言如阿拉伯语、希伯来语等)
18
+ const currentLangAbbr = (Astro.locals as any).currentLangAbbr;
19
+ const currentLangObj = langs?.find((l) => l.abbr === currentLangAbbr) || langs?.[0];
20
+ const defaultDir = currentLangObj?.dir || "ltr";
21
+
22
+ // 注意:langAbbr 已由中间件设置到 Entify 实例,无需再次调用 setLangAbbr
23
+
24
+ // 计算支持的语言列表(用于路径解析)
25
+ const supportedLangs = langs?.map((l) => l.htmlLang) || [];
26
+ const firstSegment = Astro.url.pathname.split("/")[1];
27
+
28
+ // 文档壳:只负责 <html>/<head>/<body> 结构,不包含 Header/Footer 等站点布局。
29
+ interface Props {
30
+ // html 的 lang 属性(可选,传入则覆盖默认动态获取的语言)
31
+ lang?: string;
32
+ // SEO Meta(由上层 Layout/页面传入)
33
+ meta?: PageMeta;
34
+ // 页面标题(由 Meta 组件消费)
35
+ title?: string;
36
+ // JSON-LD 结构化数据,支持单个或数组
37
+ jsonLd?: Record<string, unknown> | Record<string, unknown>[];
38
+ // 兼容 class / className 两种写法,最终合并到 body 上
39
+ class?: string;
40
+ className?: string;
41
+ }
42
+
43
+ const {
44
+ lang,
45
+ meta,
46
+ title,
47
+ jsonLd,
48
+ class: className = "scroll-smooth",
49
+ className: className2,
50
+ // 其余 props 透传到 body(例如 data-* 属性等)
51
+ ...rest
52
+ } = Astro.props;
53
+
54
+ const jsonLdItems = jsonLd ? (Array.isArray(jsonLd) ? jsonLd : [jsonLd]) : [];
55
+
56
+ const bodyAttrs = {
57
+ "data-aos-easing": "ease-out-cubic",
58
+ "data-aos-duration": "800",
59
+ "data-aos-delay": "0",
60
+ "data-aos-offset": "50",
61
+ "data-aos-once": "true",
62
+ ...rest,
63
+ };
64
+
65
+ let resolvedMeta: PageMeta | undefined = meta;
66
+ let resolvedTitle: string | undefined = title;
67
+
68
+ // 仅在未传入 meta/title 时,针对动态详情页兜底拉取
69
+ if (!resolvedMeta || !resolvedTitle) {
70
+ try {
71
+ const pathname = Astro.url?.pathname || "";
72
+ const slug =
73
+ typeof Astro.params?.slug === "string" ? Astro.params.slug : undefined;
74
+ const id =
75
+ typeof Astro.params?.id === "string" ? Astro.params.id : undefined;
76
+
77
+ // 去掉语言前缀,支持多语言路径
78
+ // 例如: /en/posts/slug -> /posts/slug
79
+ const pathWithoutLang = supportedLangs.includes(firstSegment)
80
+ ? pathname.slice(firstSegment.length + 1) // +1 去掉开头的 /
81
+ : pathname;
82
+
83
+ if (slug && pathWithoutLang.startsWith("/posts/categories/")) {
84
+ const category = await rx.getPostCategoryBySlug(slug);
85
+ resolvedTitle = resolvedTitle ?? category?.name ?? "Blog";
86
+ } else if (slug && pathWithoutLang.startsWith("/products/categories/")) {
87
+ const category = await rx.getProductCategoryBySlug(slug);
88
+ resolvedTitle = resolvedTitle ?? category?.name ?? "Products";
89
+ } else if (id && pathWithoutLang.startsWith("/profiles/")) {
90
+ const user = await rx.getOneUser(id);
91
+ resolvedTitle = resolvedTitle ?? user?.name ?? "Profile";
92
+ } else if (slug && pathWithoutLang.startsWith("/posts/")) {
93
+ const post = await rx.getPostBySlug(slug, { width: 1200, height: 600 });
94
+ resolvedTitle = resolvedTitle ?? post?.title;
95
+ resolvedMeta = resolvedMeta ?? post?.meta;
96
+ } else if (slug && pathWithoutLang.startsWith("/products/")) {
97
+ const product = await rx.getProductBySlug(slug, {
98
+ width: 800,
99
+ height: 800,
100
+ });
101
+ resolvedTitle = resolvedTitle ?? product?.title;
102
+ resolvedMeta = resolvedMeta ?? product?.meta;
103
+ }
104
+ } catch (e) {
105
+ console.log("[Document] resolve meta failed:", e);
106
+ }
107
+ }
108
+ ---
109
+
110
+ <!doctype html>
111
+ <html lang={lang || defaultLang} dir={defaultDir} class="aos-preload">
112
+ <head>
113
+ <meta charset="UTF-8" />
114
+ <meta
115
+ name="viewport"
116
+ content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=0.5, user-scalable=yes"
117
+ />
118
+
119
+ <meta name="generator" content={Astro.generator} />
120
+ <link rel="sitemap" href="/sitemap-index.xml" />
121
+ <!-- 给上层 Layout/页面注入自定义 head 内容(预加载、脚本、样式等) -->
122
+ <!-- 预连接 -->
123
+ <link
124
+ rel="preconnect"
125
+ href="https://customer-amj1dt4tnge8w6ji.cloudflarestream.com"
126
+ crossorigin
127
+ />
128
+ <script>
129
+ const w = window as any;
130
+ const state =
131
+ w.__aos_state__ ||
132
+ (w.__aos_state__ = { inited: false, loading: null, aos: null });
133
+
134
+ const el = document.documentElement;
135
+ const raf = () =>
136
+ new Promise((resolve) =>
137
+ requestAnimationFrame(() => resolve(undefined)),
138
+ );
139
+
140
+ const ensureAos = async () => {
141
+ if (state.aos) return state.aos;
142
+ if (!state.loading) {
143
+ state.loading = import("aos").then((m) => {
144
+ state.aos = m.default;
145
+ return state.aos;
146
+ });
147
+ }
148
+ return state.loading;
149
+ };
150
+
151
+ const bootOrRefresh = async () => {
152
+ if (
153
+ window.matchMedia &&
154
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
155
+ ) {
156
+ el.classList.add("aos-ready");
157
+ el.classList.remove("aos-preload");
158
+ return;
159
+ }
160
+
161
+ if (!document.querySelector("[data-aos]")) {
162
+ el.classList.add("aos-ready");
163
+ el.classList.remove("aos-preload");
164
+ return;
165
+ }
166
+
167
+ el.classList.add("aos-preload");
168
+ el.classList.remove("aos-ready");
169
+
170
+ const AOS = await ensureAos();
171
+ if (!state.inited) {
172
+ state.inited = true;
173
+ AOS.init({
174
+ duration: 800,
175
+ easing: "ease-out-cubic",
176
+ once: true,
177
+ offset: 50,
178
+ });
179
+ }
180
+
181
+ await raf();
182
+ await raf();
183
+ AOS.refresh();
184
+
185
+ el.classList.add("aos-ready");
186
+ el.classList.remove("aos-preload");
187
+ };
188
+
189
+ const boot = () => {
190
+ bootOrRefresh();
191
+ };
192
+
193
+ if (document.readyState === "loading") {
194
+ document.addEventListener("DOMContentLoaded", boot, { once: true });
195
+ } else {
196
+ boot();
197
+ }
198
+
199
+ document.addEventListener("astro:page-load", boot);
200
+ document.addEventListener("astro:after-swap", boot);
201
+ </script>
202
+ <ClientRouter />
203
+ <!-- Consent default (Google) -->
204
+ <script is:inline>
205
+ window.dataLayer = window.dataLayer || [];
206
+ function gtag() {
207
+ dataLayer.push(arguments);
208
+ }
209
+ gtag("js", new Date());
210
+ gtag("consent", "default", {
211
+ ad_storage: "denied",
212
+ analytics_storage: "denied",
213
+ });
214
+ </script>
215
+ <slot name="head" />
216
+ <Meta title={resolvedTitle} content={resolvedMeta} />
217
+ {
218
+ jsonLdItems.map((item) => (
219
+ <script
220
+ is:inline
221
+ type="application/ld+json"
222
+ set:html={JSON.stringify(item)}
223
+ />
224
+ ))
225
+ }
226
+ <style>
227
+ html.aos-preload [data-aos] {
228
+ opacity: 1 !important;
229
+ transform: none !important;
230
+ transition: none !important;
231
+ }
232
+ </style>
233
+ </head>
234
+ <!-- rest 会被展开到 body 上,方便上层控制 data-* 等属性 -->
235
+ <body class:list={[className, className2]} {...bodyAttrs}>
236
+ <slot />
237
+ </body>
238
+ </html>
239
+
240
+ <style>
241
+ html,
242
+ body {
243
+ margin: 0;
244
+ width: 100%;
245
+ height: 100%;
246
+ }
247
+
248
+ html {
249
+ scroll-behavior: smooth;
250
+ scrollbar-gutter: stable;
251
+ }
252
+
253
+ body {
254
+ scrollbar-gutter: stable;
255
+ }
256
+ </style>
@@ -1,60 +1,68 @@
1
- ---
2
- import { Entify, type Locals } from "@rxdrag/website-lib-core/server";
3
- import { ReactVideoPlayer } from "@rxdrag/website-lib-react/video";
4
-
5
- const { env, imageSizes } = Astro.locals as Locals;
6
-
7
- interface Props {
8
- videoRef?: string;
9
- poster?: string | ImageMetadata;
10
- endTitle?: string;
11
- eagerLoad?: boolean;
12
- classNames?: {
13
- container?: string;
14
- aspect?: string;
15
- video?: string;
16
- playButton?: string;
17
- playButtonOuter?: string;
18
- playButtonInner?: string;
19
- playIcon?: string;
20
- overlay?: string;
21
- overlayTitle?: string;
22
- ctaButton?: string;
23
- overlayReplayButton?: string;
24
- };
25
- }
26
-
27
- const {
28
- videoRef,
29
- poster,
30
- eagerLoad,
31
- endTitle = "Contact Us Now",
32
- classNames = {},
33
- } = Astro.props;
34
-
35
- const rx = Entify.getInstance(env, imageSizes);
36
-
37
- const media = rx ? await rx.getMedia(videoRef) : undefined;
38
-
39
- let posterUrl: string | undefined;
40
- if (poster) {
41
- if (typeof poster === "string") {
42
- posterUrl = poster;
43
- } else {
44
- posterUrl = poster.src;
45
- }
46
- }
47
- ---
48
-
49
- {
50
- <ReactVideoPlayer
51
- client:only="react"
52
- media={media}
53
- endTitle={endTitle}
54
- posterUrl={posterUrl}
55
- eagerLoad={eagerLoad}
56
- classNames={classNames}
57
- >
58
- <slot />
59
- </ReactVideoPlayer>
60
- }
1
+ ---
2
+ import {
3
+ Entify,
4
+ ReactVideoPlayer,
5
+ type Locals,
6
+ } from "@rxdrag/website-lib-core";
7
+
8
+ const { env, imageSizes } = Astro.locals as Locals;
9
+
10
+ interface Props {
11
+ videoRef?: string;
12
+ poster?: string | ImageMetadata;
13
+ endTitle?: string;
14
+ eagerLoad?: boolean;
15
+ classNames?: {
16
+ container?: string;
17
+ aspect?: string;
18
+ video?: string;
19
+ playButton?: string;
20
+ playButtonOuter?: string;
21
+ playButtonInner?: string;
22
+ playIcon?: string;
23
+ overlay?: string;
24
+ overlayTitle?: string;
25
+ ctaButton?: string;
26
+ overlayReplayButton?: string;
27
+ };
28
+ }
29
+
30
+ const {
31
+ videoRef,
32
+ poster,
33
+ eagerLoad,
34
+ endTitle = "Contact Us Now",
35
+ classNames = {},
36
+ } = Astro.props;
37
+
38
+ const rx = Entify.getInstance(env, imageSizes);
39
+
40
+ // 获取媒体数据(增加 rx 为空时的保护)
41
+ const media = rx ? await rx.getMedia(videoRef) : undefined;
42
+
43
+ // 处理封面图
44
+ let posterUrl: string | undefined;
45
+ if (poster) {
46
+ if (typeof poster === "string") {
47
+ posterUrl = poster;
48
+ } else {
49
+ //const posterImage = await getImage({ src: poster });
50
+ posterUrl = poster.src; //;posterImage.src;
51
+ }
52
+ }
53
+ ---
54
+
55
+ {
56
+
57
+ <ReactVideoPlayer
58
+ client:load
59
+ media={media}
60
+ endTitle={endTitle}
61
+ posterUrl={posterUrl}
62
+ eagerLoad={eagerLoad}
63
+ classNames={classNames}
64
+ >
65
+ <slot />
66
+ </ReactVideoPlayer>
67
+
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rxdrag/website-lib",
3
- "version": "0.0.177",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts",
@@ -26,12 +26,12 @@
26
26
  "eslint": "^9.39.2",
27
27
  "gsap": "^3.12.7",
28
28
  "typescript": "^5",
29
- "@rxdrag/eslint-config-custom": "0.2.13",
30
- "@rxdrag/tiptap-preview": "0.0.3",
31
- "@rxdrag/website-lib-core": "0.0.142",
32
- "@rxdrag/rxcms-models": "0.3.115",
33
- "@rxdrag/entify-hooks": "0.2.83",
34
- "@rxdrag/tsconfig": "0.2.1"
29
+ "@rxdrag/entify-hooks": "0.3.0",
30
+ "@rxdrag/rxcms-models": "0.4.0",
31
+ "@rxdrag/eslint-config-custom": "0.3.0",
32
+ "@rxdrag/website-lib-core": "0.1.0",
33
+ "@rxdrag/tsconfig": "0.3.0",
34
+ "@rxdrag/tiptap-preview": "0.1.0"
35
35
  },
36
36
  "dependencies": {
37
37
  "aos": "3.0.0-beta.6",
@@ -39,8 +39,7 @@
39
39
  "react": "^19.1.0",
40
40
  "react-dom": "^19.1.0",
41
41
  "vanilla-cookieconsent": "3.1.0",
42
- "@rxdrag/rxcms-models": "0.3.115",
43
- "@rxdrag/website-lib-react": "0.0.8"
42
+ "@rxdrag/rxcms-models": "0.4.0"
44
43
  },
45
44
  "peerDependencies": {
46
45
  "astro": "^6.1.1",