@rxdrag/website-lib 0.0.145 → 0.0.146

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,306 @@
1
+ ---
2
+ const rootId = crypto.randomUUID();
3
+
4
+ export interface Props {
5
+ class?: string;
6
+ className?: string;
7
+ /** 自动播放间隔(毫秒),设为 0 禁用自动播放 */
8
+ autoplay?: number;
9
+ /** 是否循环播放 */
10
+ loop?: boolean;
11
+ /** 初始显示的 slide 索引 */
12
+ initialSlide?: number;
13
+ /** 切换动画时长(毫秒) */
14
+ transitionDuration?: number;
15
+ /** 是否启用触摸滑动 */
16
+ touchEnabled?: boolean;
17
+ /** 鼠标悬停时是否暂停自动播放 */
18
+ pauseOnHover?: boolean;
19
+ }
20
+
21
+ const {
22
+ class: className,
23
+ className: className2,
24
+ autoplay = 3500,
25
+ loop = true,
26
+ initialSlide = 0,
27
+ transitionDuration = 500,
28
+ touchEnabled = true,
29
+ pauseOnHover = true,
30
+ ...rest
31
+ } = Astro.props;
32
+ ---
33
+
34
+ <div
35
+ data-carousel
36
+ data-carousel-root
37
+ id={rootId}
38
+ data-autoplay={autoplay}
39
+ data-loop={loop}
40
+ data-initial-slide={initialSlide}
41
+ data-transition-duration={transitionDuration}
42
+ data-touch-enabled={touchEnabled}
43
+ data-pause-on-hover={pauseOnHover}
44
+ class:list={["carousel relative", className, className2]}
45
+ role="region"
46
+ aria-roledescription="carousel"
47
+ aria-label="Image carousel"
48
+ {...rest}
49
+ >
50
+ <slot />
51
+ </div>
52
+
53
+ <script is:inline define:vars={{ rootId }} data-astro-rerun>
54
+ const getState = () => {
55
+ const w = window;
56
+ const all = (w.__carousels__ ||= {});
57
+ return (all[rootId] ||= { cleanup: null, instance: null, globalCleanup: null });
58
+ };
59
+
60
+ const initCarousel = () => {
61
+ const root = document.getElementById(rootId);
62
+ if (!root) return () => {};
63
+
64
+ const viewport = root.querySelector(".carousel-viewport");
65
+ const track = root.querySelector(".carousel-track");
66
+ if (!track) return () => {};
67
+
68
+ const slides = Array.from(track.children);
69
+ const slideCount = slides.length;
70
+ if (slideCount === 0) return () => {};
71
+
72
+ const config = {
73
+ autoplay: parseInt(root.dataset.autoplay || "3500", 10),
74
+ loop: root.dataset.loop !== "false",
75
+ initialSlide: parseInt(root.dataset.initialSlide || "0", 10),
76
+ transitionDuration: parseInt(root.dataset.transitionDuration || "500", 10),
77
+ touchEnabled: root.dataset.touchEnabled !== "false",
78
+ pauseOnHover: root.dataset.pauseOnHover !== "false",
79
+ paginationType: root.dataset.paginationType || "bullets",
80
+ };
81
+
82
+ let currentIndex = Math.max(0, Math.min(config.initialSlide, slideCount - 1));
83
+ let autoplayTimer;
84
+ let touchStartX = 0;
85
+ let touchEndX = 0;
86
+ let isDragging = false;
87
+
88
+ const ac = new AbortController();
89
+ const { signal } = ac;
90
+
91
+ slides.forEach((slide, i) => {
92
+ slide.style.minWidth = "100%";
93
+ slide.style.flexShrink = "0";
94
+ slide.setAttribute("role", "group");
95
+ slide.setAttribute("aria-roledescription", "slide");
96
+ slide.setAttribute("aria-label", `Slide ${i + 1} of ${slideCount}`);
97
+ });
98
+
99
+ const paginationEl = root.querySelector(".carousel-pagination");
100
+ const progressBar = root.querySelector(".carousel-progress-bar");
101
+ const fractionCurrent = root.querySelector(".carousel-fraction-current");
102
+ const fractionTotal = root.querySelector(".carousel-fraction-total");
103
+ let bullets = [];
104
+
105
+ const updateUI = () => {
106
+ track.style.transform = `translateX(-${currentIndex * 100}%)`;
107
+
108
+ bullets.forEach((b, i) => {
109
+ const isActive = i === currentIndex;
110
+ b.classList.toggle("carousel-bullet-active", isActive);
111
+ b.setAttribute("aria-current", isActive ? "true" : "false");
112
+ b.setAttribute("aria-selected", isActive ? "true" : "false");
113
+ });
114
+
115
+ if (progressBar) {
116
+ const progress = ((currentIndex + 1) / slideCount) * 100;
117
+ progressBar.style.width = `${progress}%`;
118
+ }
119
+
120
+ if (fractionCurrent) fractionCurrent.textContent = String(currentIndex + 1);
121
+ if (fractionTotal) fractionTotal.textContent = String(slideCount);
122
+
123
+ slides.forEach((slide, i) => {
124
+ slide.setAttribute("aria-hidden", i !== currentIndex ? "true" : "false");
125
+ slide.inert = i !== currentIndex;
126
+ });
127
+ };
128
+
129
+ const goto = (index, animate = true) => {
130
+ if (!config.loop) {
131
+ index = Math.max(0, Math.min(index, slideCount - 1));
132
+ } else {
133
+ if (index < 0) index = slideCount - 1;
134
+ if (index >= slideCount) index = 0;
135
+ }
136
+
137
+ if (!animate) {
138
+ track.style.transitionDuration = "0ms";
139
+ }
140
+
141
+ currentIndex = index;
142
+ updateUI();
143
+
144
+ if (!animate) {
145
+ requestAnimationFrame(() => {
146
+ track.style.transitionDuration = `${config.transitionDuration}ms`;
147
+ });
148
+ }
149
+
150
+ root.dispatchEvent(new CustomEvent("carousel:change", {
151
+ detail: { index: currentIndex, slideCount },
152
+ bubbles: true,
153
+ }));
154
+ };
155
+
156
+ const next = () => goto(currentIndex + 1);
157
+ const prev = () => goto(currentIndex - 1);
158
+
159
+ const createBullets = () => {
160
+ if (!paginationEl || config.paginationType !== "bullets") return;
161
+ paginationEl.innerHTML = "";
162
+ bullets = [];
163
+
164
+ for (let i = 0; i < slideCount; i++) {
165
+ const bullet = document.createElement("button");
166
+ bullet.className = "carousel-bullet";
167
+ bullet.type = "button";
168
+ bullet.setAttribute("role", "tab");
169
+ bullet.setAttribute("aria-label", `Go to slide ${i + 1}`);
170
+ bullet.addEventListener("click", () => goto(i), { signal });
171
+ paginationEl.appendChild(bullet);
172
+ bullets.push(bullet);
173
+ }
174
+ };
175
+
176
+ const prevBtn = root.querySelector(".carousel-prev");
177
+ const nextBtn = root.querySelector(".carousel-next");
178
+
179
+ prevBtn?.addEventListener("click", prev, { signal });
180
+ nextBtn?.addEventListener("click", next, { signal });
181
+
182
+ const stopAutoplay = () => {
183
+ if (autoplayTimer) {
184
+ clearInterval(autoplayTimer);
185
+ autoplayTimer = undefined;
186
+ }
187
+ };
188
+
189
+ const startAutoplay = () => {
190
+ if (config.autoplay <= 0) return;
191
+ stopAutoplay();
192
+ autoplayTimer = setInterval(next, config.autoplay);
193
+ };
194
+
195
+ if (config.pauseOnHover) {
196
+ root.addEventListener("mouseenter", stopAutoplay, { signal });
197
+ root.addEventListener("mouseleave", startAutoplay, { signal });
198
+ root.addEventListener("focusin", stopAutoplay, { signal });
199
+ root.addEventListener("focusout", startAutoplay, { signal });
200
+ }
201
+
202
+ if (config.touchEnabled) {
203
+ const handleTouchStart = (e) => {
204
+ touchStartX = e.touches ? e.touches[0].clientX : e.clientX;
205
+ isDragging = true;
206
+ stopAutoplay();
207
+ };
208
+
209
+ const handleTouchMove = (e) => {
210
+ if (!isDragging) return;
211
+ touchEndX = e.touches ? e.touches[0].clientX : e.clientX;
212
+ };
213
+
214
+ const handleTouchEnd = () => {
215
+ if (!isDragging) return;
216
+ isDragging = false;
217
+ const diff = touchStartX - touchEndX;
218
+ const threshold = 50;
219
+
220
+ if (Math.abs(diff) > threshold) {
221
+ if (diff > 0) next();
222
+ else prev();
223
+ }
224
+
225
+ startAutoplay();
226
+ };
227
+
228
+ viewport?.addEventListener("touchstart", handleTouchStart, { signal, passive: true });
229
+ viewport?.addEventListener("touchmove", handleTouchMove, { signal, passive: true });
230
+ viewport?.addEventListener("touchend", handleTouchEnd, { signal });
231
+ viewport?.addEventListener("mousedown", handleTouchStart, { signal });
232
+ viewport?.addEventListener("mousemove", handleTouchMove, { signal });
233
+ viewport?.addEventListener("mouseup", handleTouchEnd, { signal });
234
+ viewport?.addEventListener("mouseleave", handleTouchEnd, { signal });
235
+ }
236
+
237
+ root.addEventListener("keydown", (e) => {
238
+ if (e.key === "ArrowLeft") { prev(); e.preventDefault(); }
239
+ if (e.key === "ArrowRight") { next(); e.preventDefault(); }
240
+ }, { signal });
241
+
242
+ document.addEventListener("visibilitychange", () => {
243
+ if (document.hidden) stopAutoplay();
244
+ else startAutoplay();
245
+ }, { signal });
246
+
247
+ createBullets();
248
+ goto(currentIndex, false);
249
+ startAutoplay();
250
+
251
+ const instance = { goto, next, prev, currentIndex: () => currentIndex, slideCount };
252
+ getState().instance = instance;
253
+
254
+ return () => {
255
+ stopAutoplay();
256
+ ac.abort();
257
+ getState().instance = null;
258
+ };
259
+ };
260
+
261
+ const boot = () => {
262
+ const state = getState();
263
+ state.cleanup?.();
264
+ state.cleanup = initCarousel();
265
+ };
266
+
267
+ const setupGlobalListeners = () => {
268
+ const onPageLoad = () => boot();
269
+ const onAfterSwap = () => boot();
270
+ const onBeforeSwap = () => getState().cleanup?.();
271
+
272
+ document.addEventListener("astro:page-load", onPageLoad);
273
+ document.addEventListener("astro:after-swap", onAfterSwap);
274
+ window.addEventListener("astro:before-swap", onBeforeSwap);
275
+
276
+ return () => {
277
+ document.removeEventListener("astro:page-load", onPageLoad);
278
+ document.removeEventListener("astro:after-swap", onAfterSwap);
279
+ window.removeEventListener("astro:before-swap", onBeforeSwap);
280
+ };
281
+ };
282
+
283
+ if (document.readyState === "loading") {
284
+ document.addEventListener("DOMContentLoaded", boot, { once: true });
285
+ } else {
286
+ boot();
287
+ }
288
+
289
+ const state = getState();
290
+ state.globalCleanup?.();
291
+ state.globalCleanup = setupGlobalListeners();
292
+ </script>
293
+
294
+ <style>
295
+ .carousel {
296
+ --carousel-nav-color: #fff;
297
+ --carousel-nav-bg: rgba(0, 0, 0, 0.3);
298
+ --carousel-nav-bg-hover: rgba(0, 0, 0, 0.5);
299
+ --carousel-nav-size: 44px;
300
+ --carousel-nav-icon-size: 24px;
301
+ --carousel-bullet-size: 10px;
302
+ --carousel-bullet-gap: 8px;
303
+ --carousel-bullet-bg: rgba(255, 255, 255, 0.4);
304
+ --carousel-bullet-bg-active: #fff;
305
+ }
306
+ </style>
@@ -0,0 +1,77 @@
1
+ ---
2
+ export interface Props {
3
+ class?: string;
4
+ direction: "prev" | "next";
5
+ /** 按钮尺寸 */
6
+ size?: "sm" | "md" | "lg";
7
+ /** 样式变体 */
8
+ variant?: "circle" | "square" | "minimal";
9
+ /** 自定义 aria-label */
10
+ ariaLabel?: string;
11
+ }
12
+
13
+ const {
14
+ class: className,
15
+ direction,
16
+ size = "md",
17
+ variant = "circle",
18
+ ariaLabel,
19
+ ...rest
20
+ } = Astro.props;
21
+
22
+ const sizeClasses = {
23
+ sm: "w-8 h-8",
24
+ md: "w-11 h-11",
25
+ lg: "w-14 h-14",
26
+ };
27
+
28
+ const iconSizes = {
29
+ sm: "w-4 h-4",
30
+ md: "w-6 h-6",
31
+ lg: "w-8 h-8",
32
+ };
33
+
34
+ const variantClasses = {
35
+ circle: "rounded-full",
36
+ square: "rounded-lg",
37
+ minimal: "rounded-lg bg-transparent hover:bg-white/10",
38
+ };
39
+
40
+ const defaultLabels = {
41
+ prev: "上一张",
42
+ next: "下一张",
43
+ };
44
+
45
+ const points = direction === "prev" ? "15 18 9 12 15 6" : "9 18 15 12 9 6";
46
+ const baseClass = direction === "prev" ? "carousel-prev" : "carousel-next";
47
+ ---
48
+
49
+ <button
50
+ type="button"
51
+ class:list={[
52
+ "carousel-nav-btn inline-flex items-center justify-center border-0 pointer-events-auto text-white bg-black/30 transition duration-200 ease-out opacity-80 hover:bg-black/50 hover:opacity-100 active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white",
53
+ baseClass,
54
+ sizeClasses[size],
55
+ variantClasses[variant],
56
+ className,
57
+ ]}
58
+ aria-label={ariaLabel ?? defaultLabels[direction]}
59
+ {...rest}
60
+ >
61
+ {Astro.slots.has("default") ? (
62
+ <slot />
63
+ ) : (
64
+ <svg
65
+ class:list={[iconSizes[size]]}
66
+ xmlns="http://www.w3.org/2000/svg"
67
+ viewBox="0 0 24 24"
68
+ fill="none"
69
+ stroke="currentColor"
70
+ stroke-width="2"
71
+ stroke-linecap="round"
72
+ stroke-linejoin="round"
73
+ >
74
+ <polyline points={points}></polyline>
75
+ </svg>
76
+ )}
77
+ </button>
@@ -0,0 +1,148 @@
1
+ ---
2
+ export interface Props {
3
+ class?: string;
4
+ /** 分页类型 */
5
+ type?: "bullets" | "fraction" | "progressbar";
6
+ /** 主体形式 */
7
+ variant?: "dots" | "numbers" | "lines";
8
+ }
9
+
10
+ const {
11
+ class: className,
12
+ type = "bullets",
13
+ variant = "dots",
14
+ } = Astro.props;
15
+ ---
16
+
17
+ <div
18
+ class:list={[
19
+ "carousel-pagination-wrapper",
20
+ `carousel-pagination--${variant}`,
21
+ className,
22
+ ]}
23
+ data-carousel-pagination
24
+ >
25
+ {type === "bullets" && (
26
+ <div class="carousel-pagination" role="tablist" aria-label="Slide navigation"></div>
27
+ )}
28
+
29
+ {type === "fraction" && (
30
+ <div class="carousel-fraction">
31
+ <span class="carousel-fraction-current">1</span>
32
+ <span class="carousel-fraction-separator">/</span>
33
+ <span class="carousel-fraction-total">1</span>
34
+ </div>
35
+ )}
36
+
37
+ {type === "progressbar" && (
38
+ <div class="carousel-progressbar">
39
+ <div class="carousel-progress-bar"></div>
40
+ </div>
41
+ )}
42
+ </div>
43
+
44
+ <style>
45
+ .carousel-pagination-wrapper {
46
+ display: flex;
47
+ align-items: center;
48
+ gap: var(--pagination-gap, 8px);
49
+ pointer-events: auto;
50
+ }
51
+
52
+ .carousel-pagination {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: var(--pagination-gap, 8px);
56
+ counter-reset: carouselPage;
57
+ }
58
+
59
+ .carousel-pagination :global(.carousel-bullet) {
60
+ width: var(--bullet-size, 10px);
61
+ height: var(--bullet-size, 10px);
62
+ min-width: var(--bullet-min-width, auto);
63
+ padding: var(--bullet-padding, 0);
64
+ border-radius: var(--bullet-radius, 9999px);
65
+ border: none;
66
+ cursor: pointer;
67
+ background: var(--bullet-bg, rgba(255, 255, 255, 0.4));
68
+ color: var(--bullet-color, rgba(255, 255, 255, 0.6));
69
+ font-size: var(--bullet-font-size, 0.75rem);
70
+ font-weight: var(--bullet-font-weight, 500);
71
+ letter-spacing: var(--bullet-letter-spacing, 0);
72
+ display: inline-flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ transition: var(--bullet-transition, background 0.2s, color 0.2s, transform 0.2s);
76
+ counter-increment: carouselPage;
77
+ }
78
+
79
+ .carousel-pagination :global(.carousel-bullet:hover) {
80
+ color: var(--bullet-color-hover, #ffffff);
81
+ transform: var(--bullet-hover-transform, scale(1.1));
82
+ }
83
+
84
+ .carousel-pagination :global(.carousel-bullet:focus-visible) {
85
+ outline: 2px solid var(--bullet-bg-active, #fff);
86
+ outline-offset: 2px;
87
+ }
88
+
89
+ .carousel-pagination :global(.carousel-bullet-active) {
90
+ background: var(--bullet-bg-active, #fff);
91
+ color: var(--bullet-color-active, #fff);
92
+ }
93
+
94
+ /* Variant: dots (default) */
95
+ .carousel-pagination--dots .carousel-pagination :global(.carousel-bullet) {
96
+ width: var(--bullet-size, 10px);
97
+ height: var(--bullet-size, 10px);
98
+ }
99
+
100
+ /* Variant: lines */
101
+ .carousel-pagination--lines .carousel-pagination :global(.carousel-bullet) {
102
+ width: 24px;
103
+ height: 4px;
104
+ border-radius: 2px;
105
+ }
106
+ .carousel-pagination--lines .carousel-pagination :global(.carousel-bullet-active) {
107
+ width: 32px;
108
+ }
109
+
110
+ /* Variant: numbers */
111
+ .carousel-pagination--numbers .carousel-pagination :global(.carousel-bullet) {
112
+ width: auto;
113
+ min-width: 2rem;
114
+ height: 2rem;
115
+ padding: 0 0.5rem;
116
+ }
117
+ .carousel-pagination--numbers .carousel-pagination :global(.carousel-bullet::before) {
118
+ content: counter(carouselPage, decimal-leading-zero);
119
+ }
120
+
121
+ /* Fraction */
122
+ .carousel-fraction {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 0.25rem;
126
+ font-size: 0.875rem;
127
+ font-weight: 500;
128
+ color: #fff;
129
+ }
130
+ .carousel-fraction-separator {
131
+ opacity: 0.5;
132
+ }
133
+
134
+ /* Progressbar */
135
+ .carousel-progressbar {
136
+ width: 100px;
137
+ height: 4px;
138
+ background: rgba(255, 255, 255, 0.2);
139
+ border-radius: 2px;
140
+ overflow: hidden;
141
+ }
142
+ .carousel-progress-bar {
143
+ height: 100%;
144
+ background: var(--bullet-bg-active, #fff);
145
+ transition: width 0.3s ease;
146
+ width: 0;
147
+ }
148
+ </style>
@@ -0,0 +1,69 @@
1
+ ---
2
+ import type { BackgroundConfig } from "@rxdrag/website-lib-core";
3
+ import Box from "@rxdrag/website-lib/components/Box.astro";
4
+ import Container from "@rxdrag/website-lib/components/Container.astro";
5
+
6
+ export interface Props {
7
+ class?: string;
8
+ className?: string;
9
+ /** 背景配置 */
10
+ backgrounds?: BackgroundConfig[];
11
+ /** 是否使用 Container 包裹内容 */
12
+ useContainer?: boolean;
13
+ /** 内容垂直对齐:start | center | end */
14
+ align?: "start" | "center" | "end";
15
+ /** 内容水平对齐:start | center | end */
16
+ justify?: "start" | "center" | "end";
17
+ }
18
+
19
+ const {
20
+ class: className,
21
+ className: className2,
22
+ backgrounds,
23
+ useContainer = true,
24
+ align = "center",
25
+ justify = "start",
26
+ } = Astro.props;
27
+
28
+ const alignClasses = {
29
+ start: "items-start",
30
+ center: "items-center",
31
+ end: "items-end",
32
+ };
33
+
34
+ const justifyClasses = {
35
+ start: "justify-start",
36
+ center: "justify-center",
37
+ end: "justify-end",
38
+ };
39
+ ---
40
+
41
+ <div class="carousel-slide h-full" data-carousel-slide>
42
+ <Box
43
+ class:list={[
44
+ "w-full h-full flex flex-col",
45
+ justifyClasses[justify],
46
+ alignClasses[align],
47
+ ]}
48
+ backgrounds={backgrounds}
49
+ >
50
+ {
51
+ useContainer ? (
52
+ <Container class:list={["h-full w-full slider-container relative overflow-hidden", className, className2]}>
53
+ <slot />
54
+ </Container>
55
+ ) : (
56
+ <div class:list={["w-full h-full slider-div relative overflow-hidden", className, className2]}>
57
+ <slot />
58
+ </div>
59
+ )
60
+ }
61
+ </Box>
62
+ </div>
63
+
64
+ <style>
65
+ .carousel-slide {
66
+ flex-shrink: 0;
67
+ min-width: 100%;
68
+ }
69
+ </style>
@@ -0,0 +1,26 @@
1
+ ---
2
+ export interface Props {
3
+ class?: string;
4
+ className?: string;
5
+ }
6
+
7
+ const { class: className, className: className2 } = Astro.props;
8
+ ---
9
+
10
+ <div class="carousel-viewport h-full relative overflow-hidden">
11
+ <div
12
+ class:list={["carousel-track h-full flex", className, className2]}
13
+ aria-live="polite"
14
+ data-carousel-track
15
+ >
16
+ <slot />
17
+ </div>
18
+ </div>
19
+
20
+ <style>
21
+ .carousel-track {
22
+ will-change: transform;
23
+ transition-property: transform;
24
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
25
+ }
26
+ </style>
@@ -7,35 +7,41 @@ import "./CookieConsent.css";
7
7
  import { run } from "vanilla-cookieconsent";
8
8
  import { config } from "./CookieConsentConfig";
9
9
 
10
- // 异步初始化Cookie同意逻辑
11
- async function initializeCookieConsent() {
12
- try {
13
- // 始终显示Cookie同意UI
14
- document.body.classList.add("cc--elegant-black");
15
- run(config);
10
+ // 设计模式下跳过执行(sandbox 环境 document.cookie 不可用)
11
+ const isDesignMode = typeof window !== 'undefined' && window.top !== window && (window as any).__ASTRO_SANDBOX_MODULES__;
12
+ if (isDesignMode) {
13
+ //console.log('[CookieConsent] skip in design mode');
14
+ } else {
15
+ // 异步初始化Cookie同意逻辑
16
+ async function initializeCookieConsent() {
17
+ try {
18
+ // 始终显示Cookie同意UI
19
+ document.body.classList.add("cc--elegant-black");
20
+ run(config);
16
21
 
17
- // 显示偏好设置按钮
18
- const preferencesBtn = document.getElementById("show-preferences-btn");
19
- if (preferencesBtn) {
20
- preferencesBtn.style.display = "block";
21
- }
22
- } catch (error) {
23
- console.error("Cookie同意初始化失败:", error);
24
- // 出错时默认显示同意UI(安全策略)
25
- document.body.classList.add("cc--elegant-black");
26
- run(config);
22
+ // 显示偏好设置按钮
23
+ const preferencesBtn = document.getElementById("show-preferences-btn");
24
+ if (preferencesBtn) {
25
+ preferencesBtn.style.display = "block";
26
+ }
27
+ } catch (error) {
28
+ console.error("Cookie同意初始化失败:", error);
29
+ // 出错时默认显示同意UI(安全策略)
30
+ document.body.classList.add("cc--elegant-black");
31
+ run(config);
27
32
 
28
- const preferencesBtn = document.getElementById("show-preferences-btn");
29
- if (preferencesBtn) {
30
- preferencesBtn.style.display = "block";
33
+ const preferencesBtn = document.getElementById("show-preferences-btn");
34
+ if (preferencesBtn) {
35
+ preferencesBtn.style.display = "block";
36
+ }
31
37
  }
32
38
  }
33
- }
34
39
 
35
- // 页面加载完成后初始化
36
- if (document.readyState === "loading") {
37
- document.addEventListener("DOMContentLoaded", initializeCookieConsent);
38
- } else {
39
- initializeCookieConsent();
40
+ // 页面加载完成后初始化
41
+ if (document.readyState === "loading") {
42
+ document.addEventListener("DOMContentLoaded", initializeCookieConsent);
43
+ } else {
44
+ initializeCookieConsent();
45
+ }
40
46
  }
41
47
  </script>
@@ -179,8 +179,6 @@ const resolvedExitTransform =
179
179
  const w = window;
180
180
  if (!w[globalKey]) w[globalKey] = new Set();
181
181
 
182
- const logPrefix = `[Dialog:${openableKey}]`;
183
-
184
182
  const backdropClass = backdropClassString || "";
185
183
 
186
184
  const isDialogEl = (el) => {
@@ -191,7 +189,6 @@ const resolvedExitTransform =
191
189
  const getDialog = () => {
192
190
  const el = document.getElementById(openableKey);
193
191
  if (!isDialogEl(el)) {
194
- console.log(logPrefix, "getDialog: dialog not found", openableKey);
195
192
  return null;
196
193
  }
197
194
  return el;
@@ -213,12 +210,6 @@ const resolvedExitTransform =
213
210
  ? `blur(${backdropBlurPx}px)`
214
211
  : "";
215
212
  backdrop.style.animation = `genericBackdropIn ${openAnimMs}ms ease-out`;
216
- console.log(logPrefix, "ensureBackdrop: create", {
217
- backdropClass,
218
- backdropOpacity,
219
- backdropBlurPx,
220
- openAnimMs,
221
- });
222
213
  backdrop.addEventListener("click", () =>
223
214
  requestClose(dialog, "backdrop")
224
215
  );
@@ -234,13 +225,11 @@ const resolvedExitTransform =
234
225
  const removeBackdrop = (dialog) => {
235
226
  const next = dialog.nextElementSibling;
236
227
  if (next && next.classList?.contains("backdrop")) {
237
- console.log(logPrefix, "removeBackdrop");
238
228
  next.remove();
239
229
  }
240
230
  };
241
231
 
242
232
  const closeNow = (dialog) => {
243
- console.log(logPrefix, "closeNow");
244
233
  if (typeof dialog.close === "function") {
245
234
  dialog.close();
246
235
  } else {
@@ -252,11 +241,6 @@ const resolvedExitTransform =
252
241
  };
253
242
 
254
243
  const requestClose = (dialog, reason = "unknown") => {
255
- console.log(logPrefix, "requestClose", {
256
- reason,
257
- open: isOpen(dialog),
258
- state: dialog.getAttribute("data-dialog-state"),
259
- });
260
244
  if (!isOpen(dialog)) return;
261
245
  if (dialog.getAttribute("data-dialog-state") === "closing") return;
262
246
  dialog.setAttribute("data-dialog-state", "closing");
@@ -275,10 +259,6 @@ const resolvedExitTransform =
275
259
  if (dialog.dataset.genericDialogBound) return;
276
260
  dialog.dataset.genericDialogBound = "1";
277
261
 
278
- console.log(logPrefix, "ensureDialogBound", {
279
- closeSelector,
280
- });
281
-
282
262
  dialog.addEventListener("click", (e) => {
283
263
  if (e.target === dialog) requestClose(dialog, "dialog_blank_area");
284
264
  });
@@ -304,11 +284,6 @@ const resolvedExitTransform =
304
284
  if (isOpen(dialog)) return;
305
285
 
306
286
  try {
307
- console.log(logPrefix, "open: start", {
308
- forceFallback,
309
- __force_dialog_fallback__: window.__force_dialog_fallback__,
310
- hasShowModal: typeof dialog.showModal === "function",
311
- });
312
287
  const { scrollX, scrollY } = window;
313
288
  dialog.removeAttribute("data-dialog-state");
314
289
 
@@ -316,10 +291,8 @@ const resolvedExitTransform =
316
291
  forceFallback === true || window.__force_dialog_fallback__ === true;
317
292
 
318
293
  if (!effectiveForceFallback && typeof dialog.showModal === "function") {
319
- console.log(logPrefix, "open: native showModal");
320
294
  dialog.showModal();
321
295
  } else {
322
- console.log(logPrefix, "open: fallback open attr + backdrop + esc");
323
296
  ensureBackdrop(dialog);
324
297
  dialog.setAttribute("open", "");
325
298
  dialog.__genericDialogEscHandler = (e) => {
@@ -338,17 +311,12 @@ const resolvedExitTransform =
338
311
  } catch {
339
312
  dialog.focus();
340
313
  }
341
- console.log(logPrefix, "open: focus done");
342
314
  });
343
315
  } catch (err) {
344
- console.log(logPrefix, "open failed", err);
345
316
  }
346
317
  };
347
318
 
348
319
  const init = () => {
349
- console.log(logPrefix, "init", {
350
- alreadyBound: w[globalKey].has(openableKey),
351
- });
352
320
  if (!w[globalKey].has(openableKey)) {
353
321
  w[globalKey].add(openableKey);
354
322
  document.addEventListener("click", (e) => {
@@ -358,10 +326,6 @@ const resolvedExitTransform =
358
326
  if (!opener) return;
359
327
  const cta = opener.getAttribute("data-call-to-action") || "";
360
328
  window.lastCta = cta;
361
- console.log(logPrefix, "opener click -> open", {
362
- cta,
363
- openerTag: opener.tagName,
364
- });
365
329
  open();
366
330
  });
367
331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rxdrag/website-lib",
3
- "version": "0.0.145",
3
+ "version": "0.0.146",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts",
@@ -27,13 +27,12 @@
27
27
  "astro": "^5.16.6",
28
28
  "eslint": "^9.39.2",
29
29
  "gsap": "^3.12.7",
30
- "swiper": "^12.0.3",
31
30
  "typescript": "^5",
32
- "@rxdrag/rxcms-models": "0.3.106",
33
- "@rxdrag/tiptap-preview": "0.0.3",
34
31
  "@rxdrag/entify-hooks": "0.2.79",
32
+ "@rxdrag/eslint-config-custom": "0.2.13",
33
+ "@rxdrag/tiptap-preview": "0.0.3",
35
34
  "@rxdrag/tsconfig": "0.2.1",
36
- "@rxdrag/eslint-config-custom": "0.2.13"
35
+ "@rxdrag/rxcms-models": "0.3.106"
37
36
  },
38
37
  "dependencies": {
39
38
  "aos": "3.0.0-beta.6",
@@ -41,12 +40,11 @@
41
40
  "react": "^19.1.0",
42
41
  "react-dom": "^19.1.0",
43
42
  "vanilla-cookieconsent": "3.1.0",
44
- "@rxdrag/rxcms-models": "0.3.106",
45
- "@rxdrag/website-lib-core": "0.0.125"
43
+ "@rxdrag/website-lib-core": "0.0.125",
44
+ "@rxdrag/rxcms-models": "0.3.106"
46
45
  },
47
46
  "peerDependencies": {
48
- "astro": "^5.16.6",
49
- "swiper": "^12.0.3"
47
+ "astro": "^5.16.6"
50
48
  },
51
49
  "scripts": {
52
50
  "lint": "eslint . --ext .ts,tsx",
@@ -1,204 +0,0 @@
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>
@@ -1,24 +0,0 @@
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>