@rxdrag/website-lib 0.0.132 → 0.0.134

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,108 +1,114 @@
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 { getImage } from "astro:assets";
9
- import type { ImageMetadata } from "astro";
10
- import Image from "./Image.astro";
11
-
12
- export interface Props {
13
- background: BackgroundConfig;
14
- index?: number;
15
- defaultClass?: string;
16
- }
17
-
18
- const { env, imageSizes } = Astro.locals as Locals;
19
-
20
- const {
21
- background,
22
- defaultClass = "absolute top-0 left-0 w-full h-full object-cover",
23
- } = Astro.props;
24
-
25
- // 安全地提取 mediaRef 和 posterRef(只有部分背景类型有这些属性)
26
- const mediaRef = background.type === "video" ? background.mediaRef : undefined;
27
-
28
- const rx = Entify.getInstance(env, imageSizes);
29
-
30
- // 获取媒体数据
31
- const media = rx ? await rx.getMedia(mediaRef) : undefined;
32
-
33
- // 构建 className(同时支持 class 和 className)
34
- const className = [defaultClass, background.class, background.className]
35
- .filter(Boolean)
36
- .join(" ");
37
-
38
- // 预计算视频相关数据
39
- const videoUrl =
40
- background.type === "video" ? media?.file?.original : undefined;
41
-
42
- // 处理视频封面
43
- let posterUrl: string | undefined;
44
- if (background.type === "video" && background.poster) {
45
- if (typeof background.poster === "string") {
46
- posterUrl = background.poster;
47
- } else if (
48
- background.poster &&
49
- typeof background.poster === "object" &&
50
- "src" in background.poster
51
- ) {
52
- const posterImage = await getImage({
53
- src: background.poster as ImageMetadata,
54
- });
55
- posterUrl = posterImage.src;
56
- }
57
- }
58
-
59
- const isHls =
60
- background.type === "video" &&
61
- (media?.storageType === "cloudflare_stream" ||
62
- videoUrl?.endsWith(".m3u8") ||
63
- videoUrl?.includes("cloudflarestream.com"));
64
-
65
- // 预计算 Spline URL
66
- const splineUrl = background.type === "spline" ? background.url : undefined;
67
- ---
68
-
69
- {background.type === "color" && <div class={className} />}
70
-
71
- {background.type === "blur" && <div class={className} />}
72
-
73
- {
74
- background.type === "image" && background.src && (
75
- <Image class={className} src={background.src} alt="Background Image" />
76
- )
77
- }
78
-
79
- {
80
- background.type === "svg" && background.code && (
81
- <div class={className} set:html={background.code} />
82
- )
83
- }
84
-
85
- {
86
- background.type === "video" &&
87
- (isHls ? (
88
- <BackgroundHlsVideoPlayer
89
- className={className}
90
- src={videoUrl}
91
- poster={posterUrl}
92
- client:load
93
- />
94
- ) : (
95
- <BackgroundVideoPlayer
96
- className={className}
97
- src={videoUrl}
98
- poster={posterUrl}
99
- client:load
100
- />
101
- ))
102
- }
103
-
104
- {
105
- background.type === "spline" && (
106
- <iframe class={className} src={splineUrl} title="Spline 3D Scene" />
107
- )
108
- }
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
+
10
+ export interface Props {
11
+ background: BackgroundConfig;
12
+ index?: number;
13
+ defaultClass?: string;
14
+ }
15
+
16
+ const { env, imageSizes } = Astro.locals as Locals;
17
+
18
+ const {
19
+ background,
20
+ defaultClass = "absolute top-0 left-0 w-full h-full object-cover",
21
+ } = Astro.props;
22
+
23
+ // 安全地提取 mediaRef 和 posterRef(只有部分背景类型有这些属性)
24
+ const mediaRef = background.type === "video" ? background.mediaRef : undefined;
25
+
26
+ const rx = Entify.getInstance(env, imageSizes);
27
+
28
+ // 获取媒体数据
29
+ const media = rx ? await rx.getMedia(mediaRef) : undefined;
30
+
31
+ // 构建 className(同时支持 class className)
32
+ const className = [defaultClass, background.class].filter(Boolean).join(" ");
33
+
34
+ // 预计算视频相关数据
35
+ const videoUrl =
36
+ background.type === "video" ? media?.file?.original : undefined;
37
+
38
+ // 处理视频封面
39
+ let posterUrl: string | undefined;
40
+ if (background.type === "video" && background.poster) {
41
+ if (typeof background.poster === "string") {
42
+ posterUrl = background.poster;
43
+ } else if (typeof background.poster === "object") {
44
+ const poster = background.poster as Partial<ImageMetadata>;
45
+ if (
46
+ typeof poster.src === "string" &&
47
+ typeof poster.width === "number" &&
48
+ typeof poster.height === "number"
49
+ ) {
50
+ posterUrl = poster.src;
51
+ } else {
52
+ console.log(
53
+ "[Background] invalid video poster, expect ImageMetadata:",
54
+ background.poster,
55
+ );
56
+ }
57
+ }
58
+ }
59
+
60
+ const isHls =
61
+ background.type === "video" &&
62
+ (media?.storageType === "cloudflare_stream" ||
63
+ videoUrl?.endsWith(".m3u8") ||
64
+ videoUrl?.includes("cloudflarestream.com"));
65
+
66
+ // 预计算 Spline URL
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 === "image" && background.src && (
76
+ <Image
77
+ class={className}
78
+ src={background.src}
79
+ layout={background.layout || "full-width"}
80
+ alt=""
81
+ />
82
+ )
83
+ }
84
+
85
+ {
86
+ background.type === "svg" && background.code && (
87
+ <div class={className} set:html={background.code} />
88
+ )
89
+ }
90
+
91
+ {
92
+ background.type === "video" &&
93
+ (isHls ? (
94
+ <BackgroundHlsVideoPlayer
95
+ className={className}
96
+ src={videoUrl}
97
+ poster={posterUrl}
98
+ client:load
99
+ />
100
+ ) : (
101
+ <BackgroundVideoPlayer
102
+ className={className}
103
+ src={videoUrl}
104
+ poster={posterUrl}
105
+ client:load
106
+ />
107
+ ))
108
+ }
109
+
110
+ {
111
+ background.type === "spline" && (
112
+ <iframe class={className} src={splineUrl} title="Spline 3D Scene" />
113
+ )
114
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { Image as AstroImage, Picture as AstroPicture } from "astro:assets";
3
- import { type ImageProps, SIZES_MAP } from "./Image.type";
3
+ import { type ImageProps, SIZES_MAP } from "@rxdrag/website-lib-core";
4
4
 
5
5
  interface Props extends ImageProps {}
6
6
 
@@ -14,13 +14,15 @@ const {
14
14
  loading,
15
15
  priority,
16
16
  quality,
17
- sizes = "auto",
17
+ widths,
18
+ sizes,
19
+ sizeHint = "auto",
18
20
  class: className,
19
21
  ...restProps
20
- } = Astro.props;
22
+ } = Astro.props as Props;
21
23
 
22
- // 转换 sizes 预设为实际值
23
- const actualSizes = SIZES_MAP[sizes];
24
+ // 优先使用原生 sizes,否则用 sizeHint 映射
25
+ const actualSizes = sizes ?? SIZES_MAP[sizeHint];
24
26
 
25
27
  // 判断是否使用 Picture(有 formats 时使用)
26
28
  const usePicture = formats && formats.length > 0;
@@ -32,13 +34,13 @@ const isStringSrc = typeof src === "string";
32
34
  if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
33
35
  throw new Error(
34
36
  `Image src="${src}" 是本地资产路径,必须使用 import 导入。\n` +
35
- `正确用法:\n` +
36
- ` import myImage from "${src}";\n` +
37
- ` <Image src={myImage} />`
37
+ `正确用法:\n` +
38
+ ` import myImage from "${src}";\n` +
39
+ ` <Image src={myImage} />`
38
40
  );
39
41
  }
40
- ---
41
42
 
43
+ ---
42
44
  {
43
45
  isStringSrc ? (
44
46
  <img
@@ -46,7 +48,8 @@ if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
46
48
  alt={alt}
47
49
  width={width}
48
50
  height={height}
49
- loading={loading}
51
+ loading={priority ? "eager" : loading}
52
+ fetchpriority={priority ? "high" : undefined}
50
53
  class={className}
51
54
  {...restProps}
52
55
  />
@@ -59,9 +62,10 @@ if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
59
62
  formats={formats}
60
63
  quality={quality}
61
64
  loading={priority ? "eager" : loading}
62
- sizes={actualSizes}
65
+ sizes={layout === "none" ? undefined : actualSizes}
66
+ widths={layout === "none" ? undefined : widths}
63
67
  class={className}
64
- {...(layout ? { layout } : {})}
68
+ {...(layout && layout !== "none" ? { layout } : {})}
65
69
  {...restProps}
66
70
  />
67
71
  ) : (
@@ -71,11 +75,12 @@ if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
71
75
  width={width}
72
76
  height={height}
73
77
  quality={quality}
74
- loading={loading}
75
- sizes={actualSizes}
78
+ loading={priority ? "eager" : loading}
79
+ sizes={layout === "none" ? undefined : actualSizes}
80
+ widths={layout === "none" ? undefined : widths}
76
81
  class={className}
77
82
  {...(priority ? { priority } : {})}
78
- {...(layout ? { layout } : {})}
83
+ {...(layout && layout !== "none" ? { layout } : {})}
79
84
  {...restProps}
80
85
  />
81
86
  )
@@ -5,7 +5,6 @@ import {
5
5
  type Locals,
6
6
  } from "@rxdrag/website-lib-core";
7
7
  import { getImage } from "astro:assets";
8
- import type { ImageMetadata } from "astro";
9
8
 
10
9
  const { env, imageSizes } = Astro.locals as Locals;
11
10
 
@@ -47,8 +46,8 @@ if (poster) {
47
46
  if (typeof poster === "string") {
48
47
  posterUrl = poster;
49
48
  } else {
50
- const posterImage = await getImage({ src: poster });
51
- posterUrl = posterImage.src;
49
+ //const posterImage = await getImage({ src: poster });
50
+ posterUrl = poster.src; //;posterImage.src;
52
51
  }
53
52
  }
54
53
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rxdrag/website-lib",
3
- "version": "0.0.132",
3
+ "version": "0.0.134",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts",
@@ -22,15 +22,15 @@
22
22
  "@types/aos": "^3.0.7",
23
23
  "@types/react": "^19.1.0",
24
24
  "@types/react-dom": "^19.1.0",
25
- "astro": "^4.0.0",
25
+ "astro": "^5.16.6",
26
26
  "eslint": "^9.39.2",
27
27
  "gsap": "^3.12.7",
28
28
  "typescript": "^5",
29
+ "@rxdrag/entify-hooks": "0.2.79",
29
30
  "@rxdrag/eslint-config-custom": "0.2.13",
30
31
  "@rxdrag/rxcms-models": "0.3.98",
31
- "@rxdrag/entify-hooks": "0.2.79",
32
- "@rxdrag/tiptap-preview": "0.0.3",
33
- "@rxdrag/tsconfig": "0.2.1"
32
+ "@rxdrag/tsconfig": "0.2.1",
33
+ "@rxdrag/tiptap-preview": "0.0.3"
34
34
  },
35
35
  "dependencies": {
36
36
  "aos": "3.0.0-beta.6",
@@ -42,7 +42,7 @@
42
42
  "@rxdrag/rxcms-models": "0.3.98"
43
43
  },
44
44
  "peerDependencies": {
45
- "astro": "^4.0.0 || ^5.0.0"
45
+ "astro": "^5.16.6"
46
46
  },
47
47
  "scripts": {
48
48
  "lint": "eslint . --ext .ts,tsx",
@@ -1,242 +0,0 @@
1
- import type { HTMLAttributes } from "astro/types";
2
- import type { ImageMetadata } from "astro";
3
-
4
- /**
5
- * 响应式布局模式(Astro 原生值)
6
- * @see https://docs.astro.build/zh-cn/guides/images/
7
- */
8
- export type ImageLayout =
9
- | "responsive" // 跟随父容器,最大不超过原图(推荐)
10
- | "full-width" // 填满父容器(100% 宽度)
11
- | "fixed" // 固定尺寸,不响应
12
- | "none"; // 不使用响应式
13
-
14
- /**
15
- * 图片输出格式
16
- */
17
- export type ImageFormat = "webp" | "avif" | "png" | "jpg" | "gif" | "svg";
18
-
19
- /**
20
- * 图片在页面中的布局位置(用于优化下载尺寸)
21
- */
22
- export type ImageSizePreset =
23
- | "auto" // 自动处理(让 layout 决定)
24
- | "full" // 全宽内容
25
- | "half" // 两栏布局(约 50%)
26
- | "third" // 三栏布局(约 33%)
27
- | "quarter" // 四栏布局(约 25%)
28
- | "sidebar"; // 侧边栏内图片
29
-
30
- /**
31
- * 布局预设 → sizes 属性映射
32
- */
33
- export const SIZES_MAP: Record<ImageSizePreset, string | undefined> = {
34
- auto: undefined,
35
- full: "100vw",
36
- half: "(max-width: 768px) 100vw, 50vw",
37
- third: "(max-width: 768px) 100vw, 33vw",
38
- quarter: "(max-width: 768px) 100vw, 25vw",
39
- sidebar: "(max-width: 768px) 100vw, 300px",
40
- };
41
-
42
- /**
43
- * ============================================================
44
- * 图片组件属性
45
- * ============================================================
46
- *
47
- * 设计原则:
48
- * 1. Props 只处理图片核心功能(来源、加载、优化)
49
- * 2. 样式相关属性通过 Tailwind class 设置
50
- *
51
- * ============================================================
52
- * 样式设置指南(通过 class 属性使用 Tailwind)
53
- * ============================================================
54
- *
55
- * 【尺寸】
56
- * - 宽度:w-full, w-1/2, w-64, w-[300px]
57
- * - 高度:h-auto, h-64, h-screen, h-[200px]
58
- * - 最大宽度:max-w-md, max-w-lg, max-w-full
59
- *
60
- * 【宽高比】
61
- * - 正方形:aspect-square
62
- * - 视频:aspect-video (16:9)
63
- * - 自定义:aspect-[4/3], aspect-[3/2]
64
- *
65
- * 【填充模式】
66
- * - 裁剪填满:object-cover(推荐用于背景图、卡片图)
67
- * - 完整显示:object-contain(推荐用于 logo)
68
- * - 拉伸填满:object-fill
69
- * - 原始尺寸:object-none
70
- *
71
- * 【位置(配合 object-cover/contain 使用)】
72
- * - 居中:object-center(默认)
73
- * - 顶部:object-top
74
- * - 底部:object-bottom
75
- * - 左右:object-left, object-right
76
- *
77
- * 【圆角】
78
- * - 小:rounded-sm
79
- * - 中:rounded-md, rounded-lg
80
- * - 大:rounded-xl, rounded-2xl
81
- * - 圆形:rounded-full
82
- *
83
- * 【阴影】
84
- * - 小:shadow-sm
85
- * - 中:shadow, shadow-md
86
- * - 大:shadow-lg, shadow-xl
87
- *
88
- * 【边框】
89
- * - 细边框:border
90
- * - 边框颜色:border-gray-200, border-white
91
- *
92
- * 【滤镜效果】
93
- * - 灰度:grayscale
94
- * - 模糊:blur-sm, blur-md
95
- * - 亮度:brightness-50, brightness-150
96
- * - 悬停效果:hover:scale-105, hover:brightness-110
97
- *
98
- * 使用示例:
99
- * ```astro
100
- * <Image
101
- * src="/hero.jpg"
102
- * alt="英雄图"
103
- * class="w-full aspect-video object-cover rounded-lg shadow-lg"
104
- * />
105
- * ```
106
- */
107
- export interface ImageProps extends Omit<HTMLAttributes<"img">, "src" | "width" | "height"> {
108
- /**
109
- * 图片来源
110
- * - 本地图片:使用 import 导入
111
- * - 远程图片:使用完整 URL
112
- * - CMS 图片:使用媒体库路径
113
- */
114
- src: string | ImageMetadata;
115
-
116
- /**
117
- * 图片宽度
118
- * - 本地图片可自动推断,可不填
119
- * - 本地图片使用 layout="fixed" 时必须填写
120
- * - 远程图片不需要填写,用 Tailwind 样式代替
121
- */
122
- width?: number;
123
-
124
- /**
125
- * 图片高度
126
- * - 本地图片可自动推断,可不填
127
- * - 本地图片使用 layout="fixed" 时必须填写
128
- * - 远程图片不需要填写,用 Tailwind 样式代替
129
- */
130
- height?: number;
131
-
132
- /**
133
- * 图片描述(用于无障碍访问和 SEO)
134
- * - 内容图片:填写描述文字
135
- * - 装饰性图片:设为空字符串 alt="",屏幕阅读器会忽略
136
- */
137
- alt?: string;
138
-
139
- /**
140
- * 响应式布局模式(Astro 原生值)
141
- * - responsive: 跟随容器宽度,最大不超过原图尺寸(推荐)
142
- * - full-width: 填满父容器(100% 宽度)
143
- * - fixed: 固定尺寸,不响应屏幕变化
144
- * - none: 不使用 Astro 响应式处理
145
- */
146
- layout?: ImageLayout;
147
-
148
- /**
149
- * 输出格式(用于 Picture 组件)
150
- * - 浏览器会自动选择最优格式
151
- * - 推荐:['avif', 'webp'] 获得最佳压缩
152
- * @example ['avif', 'webp']
153
- */
154
- formats?: ImageFormat[];
155
-
156
- /**
157
- * 加载策略
158
- * - lazy: 延迟加载,首屏外的图片使用
159
- * - eager: 立即加载,首屏图片使用
160
- * @default "lazy"
161
- */
162
- loading?: "lazy" | "eager";
163
-
164
- /**
165
- * 是否为重要图片(LCP 优化,Astro 原生支持)
166
- * - 设为 true 会自动设置 loading="eager" 和 fetchpriority="high"
167
- * - 仅对首屏最大的图片使用
168
- */
169
- priority?: boolean;
170
-
171
- /**
172
- * 图片质量(1-100)
173
- * - 数值越高质量越好,文件越大
174
- * - 推荐 80-90 平衡质量和大小
175
- * 备注:这个可以先不提供可视化支持,小白理解不了
176
- * @default 80
177
- */
178
- quality?: number;
179
-
180
- /**
181
- * 图片在页面中的布局位置(用于优化下载尺寸)
182
- * - auto: 自动处理(推荐,让 layout 决定)
183
- * - full: 全宽内容
184
- * - half: 两栏布局
185
- * - third: 三栏布局
186
- * - quarter: 四栏布局
187
- * - sidebar: 侧边栏内图片
188
- * @default "auto"
189
- */
190
- sizes?: ImageSizePreset;
191
-
192
- /**
193
- * 样式类(使用 Tailwind)
194
- *
195
- * 【避免布局偏移】
196
- * 设置宽高比可预留空间,防止图片加载时页面跳动:
197
- * - aspect-square(1:1)
198
- * - aspect-video(16:9)
199
- * - aspect-[4/3]、aspect-[3/2] 等自定义比例
200
- *
201
- * 【常用宽高比】
202
- * - 1:1 - 正方形,电商产品、头像、社交卡片
203
- * - 4:3 - 传统屏幕,博客卡片、新闻缩略图
204
- * - 3:2 - 单反照片,摄影作品、画廊
205
- * - 16:9 - 宽屏视频,英雄图、视频封面
206
- * - 21:9 - 超宽屏,电影海报、横幅广告
207
- * - 4:5 - 竖版,Instagram、社交媒体
208
- * - 9:16 - 竖屏视频,Stories、短视频封面
209
- *
210
- * 【快捷模板】
211
- * - 英雄图:w-full aspect-video object-cover
212
- * - 博客卡片:w-full aspect-[16/9] object-cover rounded-lg
213
- * - 新闻卡片:w-full aspect-[4/3] object-cover rounded-lg
214
- * - 电商卡片:w-full aspect-square object-cover rounded-lg
215
- * - 社交卡片:w-full aspect-[4/5] object-cover rounded-lg
216
- * - 缩略图:w-32 aspect-square object-cover rounded
217
- * - 头像(小):w-8 h-8 object-cover rounded-full
218
- * - 头像(中):w-12 h-12 object-cover rounded-full
219
- * - 头像(大):w-16 h-16 object-cover rounded-full
220
- * - Logo:h-8 w-auto object-contain
221
- * - 产品图:w-full aspect-square object-contain bg-white
222
- * - 背景图:w-full h-full object-cover absolute inset-0
223
- * - 画廊图:w-full aspect-[3/2] object-cover hover:scale-105 transition
224
- * - 横幅广告:w-full aspect-[21/9] object-cover
225
- *
226
- * 【常用样式】
227
- * - 尺寸:w-full, w-1/2, w-32, h-64, max-w-lg, max-w-screen-xl
228
- * - 填充:object-cover(裁剪填满), object-contain(完整显示), object-fill(拉伸)
229
- * - 位置:object-center, object-top, object-bottom, object-left, object-right
230
- * - 圆角:rounded-sm, rounded, rounded-md, rounded-lg, rounded-xl, rounded-full
231
- * - 阴影:shadow-sm, shadow, shadow-md, shadow-lg, shadow-xl
232
- * - 边框:border, border-2, border-white, border-gray-200
233
- * - 滤镜:grayscale, blur-sm, brightness-75, contrast-125
234
- * - 过渡:transition, hover:scale-105, hover:brightness-110, hover:shadow-xl
235
- *
236
- * @example "w-full aspect-video object-cover rounded-lg"
237
- */
238
- class?: string;
239
- //兼容react组件
240
- className?: string;
241
- }
242
-
@@ -1,35 +0,0 @@
1
- ---
2
- import type { HTMLAttributes } from "astro/types";
3
-
4
- type PictureSource = {
5
- srcset: string;
6
- media?: string;
7
- type?: string;
8
- sizes?: string;
9
- };
10
-
11
- interface Props extends HTMLAttributes<"picture"> {
12
- className?: string;
13
- sources?: PictureSource[];
14
- imgProps: Omit<HTMLAttributes<"img">, "src"> & {
15
- src: string;
16
- };
17
- }
18
-
19
- const {
20
- className,
21
- sources,
22
- imgProps,
23
- class: originalClass,
24
- ...rest
25
- } = Astro.props;
26
-
27
- const sourceList = sources ?? [];
28
- ---
29
-
30
- <picture class:list={["picture-block", className, originalClass]} {...rest}>
31
- {sourceList.map((source) => (
32
- <source {...source} />
33
- ))}
34
- <img {...imgProps} />
35
- </picture>