@rxdrag/website-lib 0.0.102 → 0.0.103

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/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@rxdrag/website-lib",
3
- "version": "0.0.102",
3
+ "version": "0.0.103",
4
4
  "type": "module",
5
5
  "exports": {
6
- ".": "./index.ts"
6
+ ".": "./index.ts",
7
+ "./components/*": "./src/components/*.astro"
7
8
  },
8
9
  "files": [
9
10
  "src",
@@ -18,6 +19,7 @@
18
19
  "devDependencies": {
19
20
  "@astrojs/react": "^3.0.0",
20
21
  "@babel/types": "^7.26.9",
22
+ "@types/aos": "^3.0.7",
21
23
  "@types/react": "^19.1.0",
22
24
  "@types/react-dom": "^19.1.0",
23
25
  "astro": "^4.0.0",
@@ -25,17 +27,18 @@
25
27
  "gsap": "^3.12.7",
26
28
  "typescript": "^5",
27
29
  "@rxdrag/rxcms-models": "0.3.96",
28
- "@rxdrag/eslint-config-custom": "0.2.12",
29
30
  "@rxdrag/entify-hooks": "0.2.77",
31
+ "@rxdrag/eslint-config-custom": "0.2.12",
30
32
  "@rxdrag/slate-preview": "1.2.63",
31
33
  "@rxdrag/tsconfig": "0.2.0"
32
34
  },
33
35
  "dependencies": {
36
+ "aos": "3.0.0-beta.6",
34
37
  "clsx": "^2.1.0",
35
38
  "react": "^19.1.0",
36
39
  "react-dom": "^19.1.0",
37
- "@rxdrag/rxcms-models": "0.3.96",
38
- "@rxdrag/website-lib-core": "0.0.103"
40
+ "@rxdrag/website-lib-core": "0.0.104",
41
+ "@rxdrag/rxcms-models": "0.3.96"
39
42
  },
40
43
  "peerDependencies": {
41
44
  "astro": "^4.0.0 || ^5.0.0"
@@ -1,15 +1,19 @@
1
1
  ---
2
- import {
3
- getAnimationNumberDataAttrs,
4
- type AnimationNumberProps,
5
- } from "@rxdrag/website-lib-core";
6
-
7
- interface Props extends AnimationNumberProps {}
2
+ interface Props {
3
+ start?: number;
4
+ end: number;
5
+ duration?: number;
6
+ class?: string;
7
+ sign?: string;
8
+ once?: boolean;
9
+ type?: "int" | "float";
10
+ fractionDigits?: number;
11
+ }
8
12
 
9
13
  const {
10
14
  start = 0,
11
- end = 100,
12
- transition = { duration: 2 },
15
+ end,
16
+ duration = 1200,
13
17
  class: className,
14
18
  sign = "+",
15
19
  once = false,
@@ -17,45 +21,198 @@ const {
17
21
  fractionDigits = 1,
18
22
  } = Astro.props;
19
23
 
20
- const attrs = getAnimationNumberDataAttrs({
21
- start,
22
- end,
23
- transition,
24
- type,
25
- fractionDigits,
26
- once,
27
- });
24
+ const id = crypto.randomUUID();
28
25
  ---
29
26
 
30
- <div {...attrs} class={className}>
31
- <span class="motion-safe:animate-number">{start}</span>
32
- {sign && <span class="motion-safe:animate-sign hidden">{sign}</span>}
33
- </div>
27
+ <span
28
+ class={className}
29
+ data-light-number
30
+ data-id={id}
31
+ data-start={start}
32
+ data-end={end}
33
+ data-duration={duration}
34
+ data-once={once ? "1" : "0"}
35
+ data-type={type}
36
+ data-fraction-digits={fractionDigits}
37
+ >
38
+ <span data-light-number-value>{start}</span>
39
+ {
40
+ sign && (
41
+ <span data-light-number-sign style="display:none">
42
+ {sign}
43
+ </span>
44
+ )
45
+ }
46
+ </span>
47
+
48
+ <script is:inline>
49
+ (() => {
50
+ const bindAstroLifecycle = (key, fn) => {
51
+ const w = window;
52
+ const bound = (w.__astro_lifecycle_bound__ ||= Object.create(null));
53
+ if (bound[key]) return;
54
+ bound[key] = true;
34
55
 
35
- <script>
36
- import { pageLoader } from "@rxdrag/website-lib-core";
37
- import { numberController } from "@rxdrag/website-lib-core";
56
+ const boot = () => fn();
38
57
 
39
- // 页面加载后重新初始化动画
40
- //TODO 第三方引入时, pageloader有bug
41
- // pageLoader.onLoaded(() => {
42
- // numberController.mount();
43
- // });
58
+ if (document.readyState === "loading") {
59
+ document.addEventListener("DOMContentLoaded", boot, { once: true });
60
+ } else {
61
+ boot();
62
+ }
44
63
 
45
- function mount() {
46
- numberController.mount();
47
- }
64
+ document.addEventListener("astro:page-load", boot);
65
+ document.addEventListener("astro:after-swap", boot);
66
+ };
48
67
 
49
- try {
50
- if (typeof queueMicrotask === "function") {
51
- queueMicrotask(mount);
52
- } else {
53
- requestAnimationFrame(() => mount());
54
- }
55
- } catch {
56
- // ignore
57
- }
68
+ const globalKey = "__light_number_state__";
69
+ const w = window;
70
+ const state = (w[globalKey] ||= {
71
+ docBound: false,
72
+ moBound: false,
73
+ scheduled: false,
74
+ });
75
+
76
+ const scheduleInit = () => {
77
+ if (state.scheduled) return;
78
+ state.scheduled = true;
79
+ requestAnimationFrame(() => {
80
+ state.scheduled = false;
81
+ init();
82
+ });
83
+ };
84
+
85
+ const init = () => {
86
+ const nodes = document.querySelectorAll("[data-light-number]");
87
+ nodes.forEach((root) => {
88
+ if (!(root instanceof HTMLElement)) return;
89
+ if (root.dataset.bound === "1") return;
90
+ root.dataset.bound = "1";
91
+
92
+ const valueEl = root.querySelector("[data-light-number-value]");
93
+ const signEl = root.querySelector("[data-light-number-sign]");
94
+ if (!(valueEl instanceof HTMLElement)) return;
95
+
96
+ const start = Number(root.dataset.start ?? "0");
97
+ const end = Number(root.dataset.end ?? "0");
98
+ const duration = Number(root.dataset.duration ?? "1200");
99
+ const once = root.dataset.once === "1";
100
+ const type = root.dataset.type === "float" ? "float" : "int";
101
+ const fractionDigits = Number(root.dataset.fractionDigits ?? "1");
102
+
103
+ let running = false;
104
+ let played = false;
105
+ let rafId = 0;
106
+
107
+ const format = (n) => {
108
+ if (type === "float") {
109
+ return n.toFixed(
110
+ Number.isFinite(fractionDigits) ? fractionDigits : 1
111
+ );
112
+ }
113
+ return String(Math.round(n));
114
+ };
115
+
116
+ const setValue = (n) => {
117
+ valueEl.textContent = format(n);
118
+ };
58
119
 
59
- document.addEventListener("astro:page-load", mount);
60
- document.addEventListener("astro:after-swap", mount);
61
- </script>
120
+ const showSign = () => {
121
+ if (signEl instanceof HTMLElement) {
122
+ signEl.style.display = "";
123
+ }
124
+ };
125
+
126
+ const animate = () => {
127
+ if (running) return;
128
+ if (once && played) return;
129
+
130
+ running = true;
131
+ played = true;
132
+ showSign();
133
+
134
+ const from = start;
135
+ const to = end;
136
+ const t0 = performance.now();
137
+
138
+ const step = (t) => {
139
+ const p = Math.min(1, (t - t0) / Math.max(16, duration));
140
+ const eased = 1 - Math.pow(1 - p, 3);
141
+ const v = from + (to - from) * eased;
142
+ setValue(v);
143
+ if (p < 1) {
144
+ rafId = requestAnimationFrame(step);
145
+ } else {
146
+ running = false;
147
+ }
148
+ };
149
+
150
+ cancelAnimationFrame(rafId);
151
+ rafId = requestAnimationFrame(step);
152
+ };
153
+
154
+ const isInViewport = () => {
155
+ const rect = root.getBoundingClientRect();
156
+ const vh = window.innerHeight || document.documentElement.clientHeight;
157
+ return rect.bottom > 0 && rect.top < vh;
158
+ };
159
+
160
+ if (typeof IntersectionObserver === "undefined") {
161
+ if (isInViewport()) animate();
162
+ else window.addEventListener("scroll", animate, { passive: true, once: true });
163
+ return;
164
+ }
165
+
166
+ const io = new IntersectionObserver(
167
+ (entries) => {
168
+ entries.forEach((entry) => {
169
+ if (!entry.isIntersecting) return;
170
+ animate();
171
+ if (once) io.disconnect();
172
+ });
173
+ },
174
+ { threshold: 0.25 }
175
+ );
176
+
177
+ io.observe(root);
178
+
179
+ if (isInViewport()) animate();
180
+ });
181
+ };
182
+
183
+ const boot = () => {
184
+ init();
185
+ };
186
+
187
+ bindAstroLifecycle("light-number:boot", boot);
188
+
189
+ if (!state.moBound) {
190
+ state.moBound = true;
191
+ const mo = new MutationObserver((mutations) => {
192
+ for (const m of mutations) {
193
+ if (!m.addedNodes || m.addedNodes.length === 0) continue;
194
+ for (const n of m.addedNodes) {
195
+ if (!(n instanceof Element)) continue;
196
+ if (n.matches && n.matches("[data-light-number]")) {
197
+ scheduleInit();
198
+ return;
199
+ }
200
+ if (n.querySelector && n.querySelector("[data-light-number]")) {
201
+ scheduleInit();
202
+ return;
203
+ }
204
+ }
205
+ }
206
+ });
207
+ if (document.body) {
208
+ mo.observe(document.body, { childList: true, subtree: true });
209
+ } else {
210
+ document.addEventListener(
211
+ "DOMContentLoaded",
212
+ () => mo.observe(document.body, { childList: true, subtree: true }),
213
+ { once: true }
214
+ );
215
+ }
216
+ }
217
+ })();
218
+ </script>
@@ -1,20 +1,125 @@
1
1
  ---
2
- import {
3
- getCollapseDataAttrs,
4
- type IMotionProps,
5
- } from "@rxdrag/website-lib-core";
6
-
7
- import Motion from "./Motion.astro";
8
-
9
- interface Props extends IMotionProps {
10
- openableKey?: string;
2
+ interface Props {
3
+ class?: string;
4
+ triggerClass?: string;
5
+ panelClass?: string;
6
+ initialOpen?: boolean;
11
7
  }
12
8
 
13
- const { openableKey, ...rest } = Astro.props;
9
+ const {
10
+ class: className,
11
+ triggerClass,
12
+ panelClass,
13
+ initialOpen = false,
14
+ } = Astro.props;
14
15
 
15
- const dataAttrs = getCollapseDataAttrs(openableKey);
16
+ const collapseId = crypto.randomUUID();
17
+ const panelId = `collapse-panel-${collapseId}`;
18
+ const rootId = `collapse-${collapseId}`;
16
19
  ---
17
20
 
18
- <Motion {...dataAttrs} {...rest}>
19
- <slot />
20
- </Motion>
21
+ <div
22
+ class:list={["n-collapse", className]}
23
+ id={rootId}
24
+ data-open={initialOpen ? "true" : "false"}
25
+ >
26
+ <button
27
+ type="button"
28
+ class:list={["n-collapse__trigger", triggerClass]}
29
+ aria-expanded={initialOpen ? "true" : "false"}
30
+ aria-controls={panelId}
31
+ >
32
+ <slot name="trigger" />
33
+ </button>
34
+
35
+ <div
36
+ id={panelId}
37
+ class:list={["n-collapse__panel", panelClass]}
38
+ style={`height: ${initialOpen ? "auto" : "0px"};`}
39
+ aria-hidden={initialOpen ? "false" : "true"}
40
+ >
41
+ <slot />
42
+ </div>
43
+ </div>
44
+
45
+ <style>
46
+ .n-collapse__panel {
47
+ transition: height 0.35s ease;
48
+ }
49
+
50
+ .n-collapse__indicator {
51
+ transition: transform 0.25s ease;
52
+ }
53
+
54
+ .n-collapse[data-open="true"] .n-collapse__indicator {
55
+ transform: rotate(180deg);
56
+ }
57
+ </style>
58
+
59
+ <script is:inline>
60
+ // This component can appear multiple times per page.
61
+ // We install a single global initializer so it keeps working after Astro SPA navigation.
62
+ if (!window.__nCollapse) {
63
+ window.__nCollapse = {
64
+ initCollapseRoot(root) {
65
+ if (!root || root.dataset.nCollapseBound === "true") return;
66
+ root.dataset.nCollapseBound = "true";
67
+
68
+ const trigger = root.querySelector(":scope > .n-collapse__trigger");
69
+ const panel = root.querySelector(":scope > .n-collapse__panel");
70
+ if (!trigger || !panel) {
71
+ console.log("Collapse: trigger/panel not found", root);
72
+ return;
73
+ }
74
+
75
+ let open = root.dataset.open === "true";
76
+ if (open) {
77
+ panel.style.height = "auto";
78
+ trigger.setAttribute("aria-expanded", "true");
79
+ panel.setAttribute("aria-hidden", "false");
80
+ }
81
+
82
+ const setOpen = (next) => {
83
+ open = next;
84
+ root.dataset.open = open ? "true" : "false";
85
+ trigger.setAttribute("aria-expanded", open ? "true" : "false");
86
+ panel.setAttribute("aria-hidden", open ? "false" : "true");
87
+
88
+ if (open) {
89
+ panel.style.height = "auto";
90
+ const h = panel.scrollHeight;
91
+ panel.style.height = "0px";
92
+ panel.offsetHeight;
93
+ panel.style.height = h + "px";
94
+ } else {
95
+ const h = panel.scrollHeight;
96
+ panel.style.height = h + "px";
97
+ panel.offsetHeight;
98
+ panel.style.height = "0px";
99
+ }
100
+ };
101
+
102
+ const onTransitionEnd = (e) => {
103
+ if (e.target !== panel || e.propertyName !== "height") return;
104
+ if (open) panel.style.height = "auto";
105
+ };
106
+
107
+ trigger.addEventListener("click", () => setOpen(!open));
108
+ panel.addEventListener("transitionend", onTransitionEnd);
109
+ },
110
+ initAll() {
111
+ document.querySelectorAll(".n-collapse").forEach((el) => window.__nCollapse.initCollapseRoot(el));
112
+ },
113
+ listenersBound: false,
114
+ };
115
+ }
116
+
117
+ // Bind listeners once to support ClientRouter/ViewTransitions.
118
+ if (!window.__nCollapse.listenersBound) {
119
+ window.__nCollapse.listenersBound = true;
120
+ document.addEventListener("astro:page-load", () => window.__nCollapse.initAll());
121
+ document.addEventListener("astro:after-swap", () => window.__nCollapse.initAll());
122
+ }
123
+
124
+ window.__nCollapse.initAll();
125
+ </script>
@@ -0,0 +1,20 @@
1
+ ---
2
+ interface Props {
3
+ class?: string;
4
+ }
5
+
6
+ const { class: className } = Astro.props;
7
+ ---
8
+
9
+ <span class:list={["n-collapse__indicator", "h-5", "w-5", "flex-none", "text-foreground", className]} aria-hidden="true">
10
+ <svg
11
+ aria-hidden="true"
12
+ fill="currentColor"
13
+ focusable="false"
14
+ height="1.25rem"
15
+ viewBox="0 0 24 24"
16
+ width="1.25rem"
17
+ >
18
+ <path d="M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z"></path>
19
+ </svg>
20
+ </span>