@xiping/react-components 1.0.57 → 1.0.59
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/README.md +92 -6
- package/dist/cjs/components/comic-text/ComicText.css +1 -1
- package/dist/cjs/components/dock/Duck.css +1 -1
- package/dist/cjs/components/gradient-text/index.css +1 -1
- package/dist/cjs/components/gradient-text/index.js +1 -1
- package/dist/cjs/components/image-compare/ImageCompare.css +1 -1
- package/dist/cjs/components/image-viewer/ImageThumbnails.css +1 -1
- package/dist/cjs/components/image-viewer/ImageViewer.css +1 -1
- package/dist/cjs/components/message/Message.js +1 -1
- package/dist/cjs/components/scratch-to-reveal/ScratchToReveal.css +1 -1
- package/dist/cjs/components/subtitle-player/LyricsMode.css +1 -1
- package/dist/cjs/components/subtitle-player/SubtitlePlayer.js +1 -1
- package/dist/cjs/components/txt-reader/TxtReader.js +1 -1
- package/dist/cjs/components/typing-animation/index.js +1 -1
- package/dist/cjs/components/variable-proximity/index.css +1 -1
- package/dist/cjs/components/variable-proximity/index.js +1 -1
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayer.css +1 -1
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayer.d.ts +3 -3
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayer.js +1 -1
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayer.mobile.css +1 -0
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayer.pc.css +1 -0
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayerMobile.d.ts +20 -0
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayerMobile.js +1 -0
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayerPC.d.ts +23 -0
- package/dist/cjs/components/video-subtitle-player/VideoSubtitlePlayerPC.js +1 -0
- package/dist/cjs/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutMobile.d.ts +8 -0
- package/dist/cjs/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutMobile.js +1 -0
- package/dist/cjs/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutPC.d.ts +6 -0
- package/dist/cjs/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutPC.js +1 -0
- package/dist/cjs/components/video-subtitle-player/layouts/index.d.ts +3 -0
- package/dist/cjs/components/video-subtitle-player/layouts/types.d.ts +17 -0
- package/dist/cjs/components/video-subtitle-player/useXGPlayer.d.ts +10 -0
- package/dist/cjs/components/video-subtitle-player/useXGPlayer.js +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/node_modules/.pnpm/xgplayer@3.0.23_core-js@3.47.0/node_modules/xgplayer/dist/index.min.css +1 -1
- package/dist/cjs/react-components.css +114 -0
- package/dist/cjs/utils/utils.d.ts +1 -0
- package/dist/es/components/comic-text/ComicText.css +1 -1
- package/dist/es/components/confetti-button/index.js +1 -1
- package/dist/es/components/dock/Duck.css +1 -1
- package/dist/es/components/gradient-text/index.css +1 -1
- package/dist/es/components/gradient-text/index.js +6 -7
- package/dist/es/components/image-compare/ImageCompare.css +1 -1
- package/dist/es/components/image-viewer/ImageThumbnails.css +1 -1
- package/dist/es/components/image-viewer/ImageViewer.css +1 -1
- package/dist/es/components/message/Message.js +15 -16
- package/dist/es/components/scratch-to-reveal/ScratchToReveal.css +1 -1
- package/dist/es/components/subtitle-player/LyricsMode.css +1 -1
- package/dist/es/components/subtitle-player/SubtitlePlayer.js +9 -10
- package/dist/es/components/txt-reader/TxtReader.js +9 -10
- package/dist/es/components/typing-animation/index.js +17 -18
- package/dist/es/components/variable-proximity/index.css +1 -1
- package/dist/es/components/variable-proximity/index.js +28 -29
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayer.css +1 -1
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayer.d.ts +3 -3
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayer.js +65 -126
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayer.mobile.css +1 -0
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayer.pc.css +1 -0
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayerMobile.d.ts +20 -0
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayerMobile.js +47 -0
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayerPC.d.ts +23 -0
- package/dist/es/components/video-subtitle-player/VideoSubtitlePlayerPC.js +75 -0
- package/dist/es/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutMobile.d.ts +8 -0
- package/dist/es/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutMobile.js +21 -0
- package/dist/es/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutPC.d.ts +6 -0
- package/dist/es/components/video-subtitle-player/layouts/VideoSubtitlePlayerLayoutPC.js +50 -0
- package/dist/es/components/video-subtitle-player/layouts/index.d.ts +3 -0
- package/dist/es/components/video-subtitle-player/layouts/types.d.ts +17 -0
- package/dist/es/components/video-subtitle-player/useXGPlayer.d.ts +10 -0
- package/dist/es/components/video-subtitle-player/useXGPlayer.js +36 -0
- package/dist/es/index.js +51 -51
- package/dist/es/node_modules/.pnpm/xgplayer@3.0.23_core-js@3.47.0/node_modules/xgplayer/dist/index.min.css +1 -1
- package/dist/es/react-components.css +114 -0
- package/dist/es/utils/utils.d.ts +1 -0
- package/package.json +29 -30
|
@@ -7,10 +7,10 @@ const R = 16, S = 1.5, A = "normal", O = ({
|
|
|
7
7
|
content: a,
|
|
8
8
|
lineHeight: m = S,
|
|
9
9
|
fontSize: p = R,
|
|
10
|
-
fontWeight:
|
|
11
|
-
className:
|
|
12
|
-
style:
|
|
13
|
-
onProgressChange:
|
|
10
|
+
fontWeight: g = A,
|
|
11
|
+
className: d = "",
|
|
12
|
+
style: x = {},
|
|
13
|
+
onProgressChange: f,
|
|
14
14
|
initialScrollPosition: D,
|
|
15
15
|
cacheKey: H,
|
|
16
16
|
showTopProgress: u = !0,
|
|
@@ -23,7 +23,7 @@ const R = 16, S = 1.5, A = "normal", O = ({
|
|
|
23
23
|
target: n
|
|
24
24
|
}), i = I(() => (s.get() * 100).toFixed(2).replace(/\.0*$/, "")), F = L`${i}%`;
|
|
25
25
|
return i.on("change", (_) => {
|
|
26
|
-
|
|
26
|
+
f?.(Number(_));
|
|
27
27
|
}), /* @__PURE__ */ h(v, { children: [
|
|
28
28
|
u && /* @__PURE__ */ t(
|
|
29
29
|
l.div,
|
|
@@ -45,12 +45,12 @@ const R = 16, S = 1.5, A = "normal", O = ({
|
|
|
45
45
|
"div",
|
|
46
46
|
{
|
|
47
47
|
ref: n,
|
|
48
|
-
className: r(e["xiping-content"],
|
|
48
|
+
className: r(e["xiping-content"], d),
|
|
49
49
|
style: {
|
|
50
50
|
fontSize: `${p}px`,
|
|
51
51
|
lineHeight: m,
|
|
52
|
-
fontWeight:
|
|
53
|
-
...
|
|
52
|
+
fontWeight: g,
|
|
53
|
+
...x
|
|
54
54
|
},
|
|
55
55
|
children: a
|
|
56
56
|
}
|
|
@@ -71,6 +71,5 @@ const R = 16, S = 1.5, A = "normal", O = ({
|
|
|
71
71
|
] });
|
|
72
72
|
};
|
|
73
73
|
export {
|
|
74
|
-
O as TxtReader
|
|
75
|
-
O as default
|
|
74
|
+
O as TxtReader
|
|
76
75
|
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as
|
|
2
|
+
import { jsx as d } from "react/jsx-runtime";
|
|
3
3
|
import { motion as x } from "motion/react";
|
|
4
|
-
import { useState as u, useRef as I, useEffect as
|
|
4
|
+
import { useState as u, useRef as I, useEffect as m } from "react";
|
|
5
5
|
import b from "clsx";
|
|
6
6
|
import './index.css';/* empty css */
|
|
7
7
|
function S({
|
|
8
8
|
children: r,
|
|
9
|
-
className:
|
|
10
|
-
duration:
|
|
9
|
+
className: a,
|
|
10
|
+
duration: s = 100,
|
|
11
11
|
delay: n = 0,
|
|
12
12
|
as: l = "div",
|
|
13
13
|
startOnView: i = !1,
|
|
14
14
|
...p
|
|
15
15
|
}) {
|
|
16
|
-
const
|
|
16
|
+
const g = x.create(l, {
|
|
17
17
|
forwardMotionProps: !0
|
|
18
|
-
}), [
|
|
19
|
-
return
|
|
18
|
+
}), [v, T] = u(""), [c, f] = u(!1), o = I(null);
|
|
19
|
+
return m(() => {
|
|
20
20
|
if (!i) {
|
|
21
21
|
const t = setTimeout(() => {
|
|
22
22
|
f(!0);
|
|
@@ -31,27 +31,26 @@ function S({
|
|
|
31
31
|
},
|
|
32
32
|
{ threshold: 0.1 }
|
|
33
33
|
);
|
|
34
|
-
return
|
|
35
|
-
}, [n, i]),
|
|
34
|
+
return o.current && e.observe(o.current), () => e.disconnect();
|
|
35
|
+
}, [n, i]), m(() => {
|
|
36
36
|
if (!c) return;
|
|
37
37
|
let e = 0;
|
|
38
38
|
const t = setInterval(() => {
|
|
39
|
-
e < r.length ? (
|
|
40
|
-
},
|
|
39
|
+
e < r.length ? (T(r.substring(0, e + 1)), e++) : clearInterval(t);
|
|
40
|
+
}, s);
|
|
41
41
|
return () => {
|
|
42
42
|
clearInterval(t);
|
|
43
43
|
};
|
|
44
|
-
}, [r,
|
|
45
|
-
|
|
44
|
+
}, [r, s, c]), /* @__PURE__ */ d(
|
|
45
|
+
g,
|
|
46
46
|
{
|
|
47
|
-
ref:
|
|
48
|
-
className: b("xiping-typing-animation",
|
|
47
|
+
ref: o,
|
|
48
|
+
className: b("xiping-typing-animation", a),
|
|
49
49
|
...p,
|
|
50
|
-
children:
|
|
50
|
+
children: v
|
|
51
51
|
}
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
export {
|
|
55
|
-
S as TypingAnimation
|
|
56
|
-
S as default
|
|
55
|
+
S as TypingAnimation
|
|
57
56
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.xiping-variable-proximity{font-family:Roboto Flex,Noto Sans SC,PingFang SC,Microsoft YaHei,SimHei,sans-serif}.
|
|
1
|
+
.xiping-variable-proximity{font-family:Roboto Flex,Noto Sans SC,PingFang SC,Microsoft YaHei,SimHei,sans-serif}.xiping-variable-proximity__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
import { motion as
|
|
4
|
-
import
|
|
1
|
+
import { jsxs as U, jsx as k } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as G, useRef as y, useMemo as N, useEffect as w } from "react";
|
|
3
|
+
import { motion as H } from "motion/react";
|
|
4
|
+
import J from "clsx";
|
|
5
5
|
import './index.css';/* empty css */
|
|
6
6
|
const l = {
|
|
7
7
|
robotoFlex: !1,
|
|
@@ -16,7 +16,7 @@ function V(n) {
|
|
|
16
16
|
return !0;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function K() {
|
|
20
20
|
if (l.loading || l.robotoFlex && l.notoSansSC) return;
|
|
21
21
|
l.loading = !0;
|
|
22
22
|
const n = V("Roboto Flex"), r = V("Noto Sans SC");
|
|
@@ -62,12 +62,12 @@ function O() {
|
|
|
62
62
|
};
|
|
63
63
|
d(0);
|
|
64
64
|
}
|
|
65
|
-
function
|
|
65
|
+
function O(n = !0) {
|
|
66
66
|
w(() => {
|
|
67
|
-
n && typeof document < "u" &&
|
|
67
|
+
n && typeof document < "u" && K();
|
|
68
68
|
}, [n]);
|
|
69
69
|
}
|
|
70
|
-
function
|
|
70
|
+
function Q(n) {
|
|
71
71
|
w(() => {
|
|
72
72
|
let r;
|
|
73
73
|
const i = () => {
|
|
@@ -76,7 +76,7 @@ function W(n) {
|
|
|
76
76
|
return r = requestAnimationFrame(i), () => cancelAnimationFrame(r);
|
|
77
77
|
}, [n]);
|
|
78
78
|
}
|
|
79
|
-
function
|
|
79
|
+
function W(n) {
|
|
80
80
|
const r = y({ x: 0, y: 0 });
|
|
81
81
|
return w(() => {
|
|
82
82
|
const i = (t, s) => {
|
|
@@ -94,7 +94,7 @@ function Z(n) {
|
|
|
94
94
|
};
|
|
95
95
|
}, [n]), r;
|
|
96
96
|
}
|
|
97
|
-
const
|
|
97
|
+
const Z = G(
|
|
98
98
|
(n, r) => {
|
|
99
99
|
const {
|
|
100
100
|
label: i,
|
|
@@ -106,19 +106,19 @@ const _ = H(
|
|
|
106
106
|
className: c = "",
|
|
107
107
|
onClick: E,
|
|
108
108
|
style: C,
|
|
109
|
-
fontFamily:
|
|
109
|
+
fontFamily: h,
|
|
110
110
|
autoLoadFonts: P = !0,
|
|
111
111
|
...A
|
|
112
112
|
} = n;
|
|
113
|
-
|
|
114
|
-
const b = y([]), v = y([]), S =
|
|
113
|
+
O(P && !h);
|
|
114
|
+
const b = y([]), v = y([]), S = W(t), x = y({
|
|
115
115
|
x: null,
|
|
116
116
|
y: null
|
|
117
117
|
}), q = N(() => {
|
|
118
118
|
const e = (o) => new Map(
|
|
119
119
|
o.split(",").map((p) => p.trim()).map((p) => {
|
|
120
|
-
const [
|
|
121
|
-
return [
|
|
120
|
+
const [g, F] = p.split(" ");
|
|
121
|
+
return [g.replace(/['"]/g, ""), parseFloat(F)];
|
|
122
122
|
})
|
|
123
123
|
), a = e(f), u = e(d);
|
|
124
124
|
return Array.from(a.entries()).map(([o, p]) => ({
|
|
@@ -137,7 +137,7 @@ const _ = H(
|
|
|
137
137
|
return a;
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
-
|
|
140
|
+
Q(() => {
|
|
141
141
|
if (!t?.current) return;
|
|
142
142
|
const { x: e, y: a } = S.current;
|
|
143
143
|
if (x.current.x === e && x.current.y === a)
|
|
@@ -146,7 +146,7 @@ const _ = H(
|
|
|
146
146
|
const u = t.current.getBoundingClientRect();
|
|
147
147
|
b.current.forEach((o, p) => {
|
|
148
148
|
if (!o) return;
|
|
149
|
-
const
|
|
149
|
+
const g = o.getBoundingClientRect(), F = g.left + g.width / 2 - u.left, Y = g.top + g.height / 2 - u.top, L = I(
|
|
150
150
|
S.current.x,
|
|
151
151
|
S.current.y,
|
|
152
152
|
F,
|
|
@@ -156,9 +156,9 @@ const _ = H(
|
|
|
156
156
|
o.style.fontVariationSettings = f;
|
|
157
157
|
return;
|
|
158
158
|
}
|
|
159
|
-
const z = $(L), M = q.map(({ axis: T, fromValue: R, toValue:
|
|
160
|
-
const
|
|
161
|
-
return `'${T}' ${
|
|
159
|
+
const z = $(L), M = q.map(({ axis: T, fromValue: R, toValue: _ }) => {
|
|
160
|
+
const D = R + (_ - R) * z;
|
|
161
|
+
return `'${T}' ${D}`;
|
|
162
162
|
}).join(", ");
|
|
163
163
|
v.current[p] = M, o.style.fontVariationSettings = M;
|
|
164
164
|
});
|
|
@@ -170,13 +170,13 @@ const _ = H(
|
|
|
170
170
|
let B = 0;
|
|
171
171
|
const X = N(() => {
|
|
172
172
|
const e = { display: "inline", ...C };
|
|
173
|
-
return
|
|
174
|
-
}, [
|
|
175
|
-
return /* @__PURE__ */
|
|
173
|
+
return h && (e.fontFamily = h), e;
|
|
174
|
+
}, [h, C]);
|
|
175
|
+
return /* @__PURE__ */ U(
|
|
176
176
|
"span",
|
|
177
177
|
{
|
|
178
178
|
ref: r,
|
|
179
|
-
className:
|
|
179
|
+
className: J("xiping-variable-proximity", c),
|
|
180
180
|
onClick: E,
|
|
181
181
|
style: X,
|
|
182
182
|
...A,
|
|
@@ -184,7 +184,7 @@ const _ = H(
|
|
|
184
184
|
j.map((e, a) => {
|
|
185
185
|
const u = B++;
|
|
186
186
|
return /* @__PURE__ */ k(
|
|
187
|
-
|
|
187
|
+
H.span,
|
|
188
188
|
{
|
|
189
189
|
ref: (o) => {
|
|
190
190
|
b.current[u] = o;
|
|
@@ -199,14 +199,13 @@ const _ = H(
|
|
|
199
199
|
a
|
|
200
200
|
);
|
|
201
201
|
}),
|
|
202
|
-
/* @__PURE__ */ k("span", { className: "
|
|
202
|
+
/* @__PURE__ */ k("span", { className: "xiping-variable-proximity__sr-only", children: i })
|
|
203
203
|
]
|
|
204
204
|
}
|
|
205
205
|
);
|
|
206
206
|
}
|
|
207
207
|
);
|
|
208
|
-
|
|
208
|
+
Z.displayName = "VariableProximity";
|
|
209
209
|
export {
|
|
210
|
-
|
|
211
|
-
_ as default
|
|
210
|
+
Z as VariableProximity
|
|
212
211
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.xiping-video-subtitle-player{width:100%;height:100%;background:#1a1a1a;border-radius:8px;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.xiping-video-subtitle-player__video-wrapper{width:100%;height:100%;background:#000;overflow:hidden}.xiping-video-subtitle-player__video-container{position:relative;width:100%;height:100%;background:#000;overflow:hidden}.xiping-video-subtitle-player__video{position:absolute;top:0;left:0;width:100%;height:100%;padding:0}.xiping-video-subtitle-player__video-container .xgplayer{width:100%!important;height:100%!important}.xiping-video-subtitle-player__video-container .xgplayer video{
|
|
1
|
+
.xiping-video-subtitle-player{width:100%;height:100%;background:#1a1a1a;border-radius:8px;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.xiping-video-subtitle-player__video-wrapper{width:100%;height:100%;background:#000;overflow:hidden}.xiping-video-subtitle-player__video-container{position:relative;width:100%;height:100%;background:#000;overflow:hidden}.xiping-video-subtitle-player__video{position:absolute;top:0;left:0;width:100%;height:100%;padding:0}.xiping-video-subtitle-player__video-container .xgplayer{width:100%!important;height:100%!important}.xiping-video-subtitle-player__video-container .xgplayer video{object-fit:fill!important}.xiping-video-subtitle-player__subtitle-container{width:100%;height:100%;padding:20px;overflow-y:auto;background:#0000004d}.xiping-video-subtitle-player__right{width:100%;height:100%;background:#00000080;display:flex;flex-direction:column}.xiping-video-subtitle-player__detail{flex:1;padding:24px;overflow-y:auto;color:#fff}.xiping-video-subtitle-player__detail-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px;color:#ffffffb3}.xiping-video-subtitle-player__detail-loading-spinner{width:40px;height:40px;border:3px solid rgba(255,255,255,.1);border-top-color:#fbbf24;border-radius:50%;animation:xiping-vsp-spin 1s linear infinite}@keyframes xiping-vsp-spin{to{transform:rotate(360deg)}}.xiping-video-subtitle-player__detail-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px;color:#ffffff80;text-align:center}.xiping-video-subtitle-player__detail-empty-icon{font-size:48px;opacity:.5}.xiping-video-subtitle-player__detail-empty-text{font-size:14px;line-height:1.6}.xiping-video-subtitle-player__detail-content{display:flex;flex-direction:column;gap:20px}.xiping-video-subtitle-player__detail-word{font-size:32px;font-weight:700;color:#fbbf24;line-height:1.2;margin-bottom:8px}.xiping-video-subtitle-player__detail-pronunciation{font-size:18px;color:#ffffffb3;font-style:italic;margin-bottom:4px}.xiping-video-subtitle-player__detail-pos{display:inline-block;padding:4px 12px;background:#fbbf2433;color:#fbbf24;border-radius:4px;font-size:12px;font-weight:600;text-transform:uppercase;width:fit-content;margin-bottom:8px}.xiping-video-subtitle-player__detail-label{font-size:12px;font-weight:600;color:#ffffff80;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}.xiping-video-subtitle-player__detail-text{font-size:16px;line-height:1.6;color:#ffffffe6}.xiping-video-subtitle-player__detail-translation,.xiping-video-subtitle-player__detail-definition{padding-bottom:16px;border-bottom:1px solid rgba(255,255,255,.1)}.xiping-video-subtitle-player__detail-definition{border-bottom:none}.xiping-video-subtitle-player__detail-examples{display:flex;flex-direction:column;gap:16px}.xiping-video-subtitle-player__detail-example{padding:16px;background:#ffffff0d;border-radius:8px;border-left:3px solid #fbbf24}.xiping-video-subtitle-player__detail-example-en{font-size:15px;line-height:1.6;color:#fffffff2;margin-bottom:8px}.xiping-video-subtitle-player__detail-example-zh{font-size:14px;line-height:1.6;color:#ffffffb3;padding-left:12px;border-left:2px solid rgba(255,255,255,.2)}.xiping-video-subtitle-player__subtitle-container::-webkit-scrollbar,.xiping-video-subtitle-player__detail::-webkit-scrollbar{width:8px}.xiping-video-subtitle-player__subtitle-container::-webkit-scrollbar-track,.xiping-video-subtitle-player__detail::-webkit-scrollbar-track{background:#ffffff0d;border-radius:4px}.xiping-video-subtitle-player__subtitle-container::-webkit-scrollbar-thumb,.xiping-video-subtitle-player__detail::-webkit-scrollbar-thumb{background:#fff3;border-radius:4px}.xiping-video-subtitle-player__subtitle-container::-webkit-scrollbar-thumb:hover,.xiping-video-subtitle-player__detail::-webkit-scrollbar-thumb:hover{background:#ffffff4d}
|
|
@@ -13,11 +13,11 @@ export interface VideoSubtitlePlayerProps {
|
|
|
13
13
|
style?: React.CSSProperties;
|
|
14
14
|
/** 自定义详情获取函数,用于调用大模型API */
|
|
15
15
|
onFetchDetail?: (target: HoverTarget | null) => Promise<void>;
|
|
16
|
+
/** 强制使用布局:传入则不再做设备检测,直接使用对应布局 */
|
|
17
|
+
forceLayout?: "mobile" | "pc";
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
|
-
*
|
|
19
|
-
* 左侧:上部分视频播放器(填充模式),下部分字幕显示
|
|
20
|
-
* 右侧:字幕详情(单词/短语的发音、解释等)
|
|
20
|
+
* 视频字幕播放器:根据设备或 forceLayout 选择 PC 或移动端视图,二者完全分离
|
|
21
21
|
*/
|
|
22
22
|
export declare const VideoSubtitlePlayer: React.FC<VideoSubtitlePlayerProps>;
|
|
23
23
|
export default VideoSubtitlePlayer;
|
|
@@ -1,135 +1,74 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useRef as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { jsx as u } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as v, useEffect as w, useCallback as d } from "react";
|
|
3
|
+
import { isMobile as V, isTablet as g } from "react-device-detect";
|
|
4
|
+
import { useVideoSubtitleStore as k } from "./useVideoSubtitleStore.js";
|
|
5
|
+
import { useXGPlayer as H } from "./useXGPlayer.js";
|
|
6
|
+
import { VideoSubtitlePlayerPC as M } from "./VideoSubtitlePlayerPC.js";
|
|
7
|
+
import { VideoSubtitlePlayerMobile as T } from "./VideoSubtitlePlayerMobile.js";
|
|
8
|
+
import './VideoSubtitlePlayer.css';/* empty css */
|
|
9
|
+
const X = ({
|
|
10
|
+
videoUrl: p,
|
|
11
|
+
subtitles: r,
|
|
12
|
+
poster: b,
|
|
13
|
+
className: l = "",
|
|
14
|
+
style: n,
|
|
15
|
+
onFetchDetail: e,
|
|
16
|
+
forceLayout: m
|
|
17
17
|
}) => {
|
|
18
|
-
const
|
|
19
|
-
currentTime:
|
|
20
|
-
setCurrentTime:
|
|
21
|
-
setIsPlaying:
|
|
22
|
-
setSubtitles:
|
|
23
|
-
currentDetail:
|
|
24
|
-
isLoadingDetail:
|
|
25
|
-
fetchDetail:
|
|
26
|
-
} =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}, [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const x = () => {
|
|
45
|
-
const I = i.currentTime || 0;
|
|
46
|
-
_(I);
|
|
47
|
-
}, f = () => {
|
|
48
|
-
d(!0);
|
|
49
|
-
}, b = () => {
|
|
50
|
-
d(!1);
|
|
51
|
-
}, g = () => {
|
|
52
|
-
d(!1);
|
|
53
|
-
};
|
|
54
|
-
return i.on("timeupdate", x), i.on("play", f), i.on("pause", b), i.on("ended", g), () => {
|
|
55
|
-
i.off("timeupdate", x), i.off("play", f), i.off("pause", b), i.off("ended", g), i.destroy(), n.current = null;
|
|
56
|
-
};
|
|
57
|
-
}, [m, v, _, d]), u(() => {
|
|
58
|
-
if (n.current) {
|
|
59
|
-
const a = n.current;
|
|
60
|
-
Math.abs(a.currentTime - r) > 0.5 && (a.currentTime = r);
|
|
61
|
-
}
|
|
62
|
-
}, [r]);
|
|
63
|
-
const C = R(
|
|
64
|
-
async (a) => {
|
|
65
|
-
p ? await p(a) : await y(a);
|
|
18
|
+
const t = v(null), c = m !== void 0 ? m === "mobile" : V || g, {
|
|
19
|
+
currentTime: a,
|
|
20
|
+
setCurrentTime: C,
|
|
21
|
+
setIsPlaying: y,
|
|
22
|
+
setSubtitles: s,
|
|
23
|
+
currentDetail: P,
|
|
24
|
+
isLoadingDetail: S,
|
|
25
|
+
fetchDetail: i
|
|
26
|
+
} = k();
|
|
27
|
+
w(() => {
|
|
28
|
+
s(r);
|
|
29
|
+
}, [r, s]), H(
|
|
30
|
+
t,
|
|
31
|
+
p,
|
|
32
|
+
b,
|
|
33
|
+
{ setCurrentTime: C, setIsPlaying: y },
|
|
34
|
+
a
|
|
35
|
+
);
|
|
36
|
+
const f = d(
|
|
37
|
+
async (o) => {
|
|
38
|
+
e ? await e(o) : await i(o);
|
|
39
|
+
},
|
|
40
|
+
[e, i]
|
|
41
|
+
), W = d(
|
|
42
|
+
async (o) => {
|
|
43
|
+
e ? await e(o) : await i(o);
|
|
66
44
|
},
|
|
67
|
-
[
|
|
45
|
+
[e, i]
|
|
68
46
|
);
|
|
69
|
-
return /* @__PURE__ */
|
|
70
|
-
|
|
47
|
+
return c ? /* @__PURE__ */ u(
|
|
48
|
+
T,
|
|
49
|
+
{
|
|
50
|
+
playerRef: t,
|
|
51
|
+
subtitles: r,
|
|
52
|
+
currentTime: a,
|
|
53
|
+
onWordHoverChange: f,
|
|
54
|
+
onWordClick: W,
|
|
55
|
+
className: l,
|
|
56
|
+
style: n
|
|
57
|
+
}
|
|
58
|
+
) : /* @__PURE__ */ u(
|
|
59
|
+
M,
|
|
71
60
|
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
className: "xiping-video-subtitle-player__video"
|
|
81
|
-
}
|
|
82
|
-
) }) }) }),
|
|
83
|
-
/* @__PURE__ */ e(z, { className: "xiping-video-subtitle-player__resize-handle xiping-video-subtitle-player__resize-handle--vertical" }),
|
|
84
|
-
/* @__PURE__ */ e(s, { defaultSize: 40, minSize: 20, children: /* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__subtitle-container", children: /* @__PURE__ */ e(
|
|
85
|
-
E,
|
|
86
|
-
{
|
|
87
|
-
subtitles: o,
|
|
88
|
-
currentTime: r,
|
|
89
|
-
mode: "current",
|
|
90
|
-
onWordHoverChange: C
|
|
91
|
-
}
|
|
92
|
-
) }) })
|
|
93
|
-
] }) }),
|
|
94
|
-
/* @__PURE__ */ e(z, { className: "xiping-video-subtitle-player__resize-handle xiping-video-subtitle-player__resize-handle--horizontal" }),
|
|
95
|
-
/* @__PURE__ */ e(s, { defaultSize: 35, minSize: 25, maxSize: 50, children: /* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__right", children: /* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail", children: T ? /* @__PURE__ */ t("div", { className: "xiping-video-subtitle-player__detail-loading", children: [
|
|
96
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-loading-spinner" }),
|
|
97
|
-
/* @__PURE__ */ e("span", { children: "加载中..." })
|
|
98
|
-
] }) : l ? /* @__PURE__ */ t("div", { className: "xiping-video-subtitle-player__detail-content", children: [
|
|
99
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-word", children: l.word }),
|
|
100
|
-
l.pronunciation && /* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-pronunciation", children: l.pronunciation }),
|
|
101
|
-
l.partOfSpeech && /* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-pos", children: l.partOfSpeech }),
|
|
102
|
-
l.translation && /* @__PURE__ */ t("div", { className: "xiping-video-subtitle-player__detail-translation", children: [
|
|
103
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-label", children: "中文翻译" }),
|
|
104
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-text", children: l.translation })
|
|
105
|
-
] }),
|
|
106
|
-
l.definition && /* @__PURE__ */ t("div", { className: "xiping-video-subtitle-player__detail-definition", children: [
|
|
107
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-label", children: "英文解释" }),
|
|
108
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-text", children: l.definition })
|
|
109
|
-
] }),
|
|
110
|
-
l.examples && l.examples.length > 0 && /* @__PURE__ */ t("div", { className: "xiping-video-subtitle-player__detail-examples", children: [
|
|
111
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-label", children: "例句" }),
|
|
112
|
-
l.examples.map((a, i) => /* @__PURE__ */ t(
|
|
113
|
-
"div",
|
|
114
|
-
{
|
|
115
|
-
className: "xiping-video-subtitle-player__detail-example",
|
|
116
|
-
children: [
|
|
117
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-example-en", children: a.sentence }),
|
|
118
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-example-zh", children: a.translation })
|
|
119
|
-
]
|
|
120
|
-
},
|
|
121
|
-
i
|
|
122
|
-
))
|
|
123
|
-
] })
|
|
124
|
-
] }) : /* @__PURE__ */ t("div", { className: "xiping-video-subtitle-player__detail-empty", children: [
|
|
125
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-empty-icon", children: "📖" }),
|
|
126
|
-
/* @__PURE__ */ e("div", { className: "xiping-video-subtitle-player__detail-empty-text", children: "将鼠标悬停在字幕单词上查看详情" })
|
|
127
|
-
] }) }) }) })
|
|
128
|
-
] })
|
|
61
|
+
playerRef: t,
|
|
62
|
+
subtitles: r,
|
|
63
|
+
currentTime: a,
|
|
64
|
+
currentDetail: P,
|
|
65
|
+
isLoadingDetail: S,
|
|
66
|
+
onWordHoverChange: f,
|
|
67
|
+
className: l,
|
|
68
|
+
style: n
|
|
129
69
|
}
|
|
130
70
|
);
|
|
131
71
|
};
|
|
132
72
|
export {
|
|
133
|
-
|
|
134
|
-
A as default
|
|
73
|
+
X as VideoSubtitlePlayer
|
|
135
74
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.xiping-video-subtitle-player--mobile{display:flex;flex-direction:column;min-height:0;height:100%}.xiping-video-subtitle-player__mobile-video{flex:0 0 auto;width:100%;aspect-ratio:16 / 9;background:#000}.xiping-video-subtitle-player__mobile-video-wrapper{width:100%;height:100%;background:#000;overflow:hidden}.xiping-video-subtitle-player__mobile-video-container{position:relative;width:100%;height:100%;background-color:#a62c2c;overflow:hidden}.xiping-video-subtitle-player__mobile-video-container .xgplayer{width:100%!important;height:100%!important}.xiping-video-subtitle-player__mobile-video-container .xgplayer video{object-fit:fill!important}.xiping-video-subtitle-player__mobile-subtitle{flex:1;min-height:0;padding:12px 16px;overflow-y:auto;background:#0000004d;-webkit-overflow-scrolling:touch}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.xiping-video-subtitle-player--pc .xiping-video-subtitle-player__panel-group,.xiping-video-subtitle-player--pc .xiping-video-subtitle-player__panel-group-inner{width:100%;height:100%}.xiping-video-subtitle-player--pc .xiping-video-subtitle-player__right{border-left:1px solid rgba(255,255,255,.1)}.xiping-video-subtitle-player__resize-handle--horizontal{position:relative;width:2px;background:#ffffff1a;cursor:col-resize;transition:background .2s ease;flex-shrink:0}.xiping-video-subtitle-player__resize-handle--horizontal:hover{background:#fbbf2480}.xiping-video-subtitle-player__resize-handle--horizontal:active{background:#fbbf24cc}.xiping-video-subtitle-player__resize-handle--horizontal:before{content:"";position:absolute;inset:0 -4px;cursor:col-resize}.xiping-video-subtitle-player__resize-handle--vertical{position:relative;height:2px;background:#ffffff1a;cursor:row-resize;transition:background .2s ease;flex-shrink:0}.xiping-video-subtitle-player__resize-handle--vertical:hover{background:#fbbf2480}.xiping-video-subtitle-player__resize-handle--vertical:active{background:#fbbf24cc}.xiping-video-subtitle-player__resize-handle--vertical:before{content:"";position:absolute;inset:-4px 0;cursor:row-resize}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { default as React, RefObject } from 'react';
|
|
2
|
+
import { SubtitleData, HoverTarget, WordClickTarget } from '../subtitle-player/SubtitlePlayer';
|
|
3
|
+
export interface VideoSubtitlePlayerMobileProps {
|
|
4
|
+
/** 播放器挂载的容器 ref */
|
|
5
|
+
playerRef: RefObject<HTMLDivElement | null>;
|
|
6
|
+
/** 字幕数据 */
|
|
7
|
+
subtitles: SubtitleData[];
|
|
8
|
+
/** 当前播放时间(秒) */
|
|
9
|
+
currentTime: number;
|
|
10
|
+
/** 单词 hover 变化时拉取/展示详情(可为弹窗等) */
|
|
11
|
+
onWordHoverChange: (target: HoverTarget | null) => void | Promise<void>;
|
|
12
|
+
/** 单词点击时拉取/展示详情(移动端主要交互) */
|
|
13
|
+
onWordClick: (target: WordClickTarget) => void | Promise<void>;
|
|
14
|
+
className?: string;
|
|
15
|
+
style?: React.CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 移动端视图:上下两栏(视频 + 字幕),无右侧详情区,详情后续以弹窗等形式实现
|
|
19
|
+
*/
|
|
20
|
+
export declare const VideoSubtitlePlayerMobile: React.FC<VideoSubtitlePlayerMobileProps>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback as l } from "react";
|
|
3
|
+
import u from "clsx";
|
|
4
|
+
import { SubtitlePlayer as y } from "../subtitle-player/SubtitlePlayer.js";
|
|
5
|
+
import { VideoSubtitlePlayerLayoutMobile as f } from "./layouts/VideoSubtitlePlayerLayoutMobile.js";
|
|
6
|
+
const P = ({
|
|
7
|
+
playerRef: r,
|
|
8
|
+
subtitles: a,
|
|
9
|
+
currentTime: s,
|
|
10
|
+
onWordHoverChange: t,
|
|
11
|
+
onWordClick: i,
|
|
12
|
+
className: c = "",
|
|
13
|
+
style: m
|
|
14
|
+
}) => {
|
|
15
|
+
const n = l(
|
|
16
|
+
async (o) => {
|
|
17
|
+
await t(o);
|
|
18
|
+
},
|
|
19
|
+
[t]
|
|
20
|
+
), d = l(
|
|
21
|
+
async (o) => {
|
|
22
|
+
await i(o);
|
|
23
|
+
},
|
|
24
|
+
[i]
|
|
25
|
+
), b = /* @__PURE__ */ e("div", { ref: r, className: "xiping-video-subtitle-player__video" }), p = /* @__PURE__ */ e(
|
|
26
|
+
y,
|
|
27
|
+
{
|
|
28
|
+
subtitles: a,
|
|
29
|
+
currentTime: s,
|
|
30
|
+
mode: "lyrics",
|
|
31
|
+
onWordHoverChange: n,
|
|
32
|
+
onWordClick: d
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
return /* @__PURE__ */ e(
|
|
36
|
+
f,
|
|
37
|
+
{
|
|
38
|
+
className: u(c),
|
|
39
|
+
style: m,
|
|
40
|
+
videoSlot: b,
|
|
41
|
+
subtitleSlot: p
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
export {
|
|
46
|
+
P as VideoSubtitlePlayerMobile
|
|
47
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { default as React, RefObject } from 'react';
|
|
2
|
+
import { SubtitleData, HoverTarget } from '../subtitle-player/SubtitlePlayer';
|
|
3
|
+
import { SubtitleDetail } from './useVideoSubtitleStore';
|
|
4
|
+
export interface VideoSubtitlePlayerPCProps {
|
|
5
|
+
/** 播放器挂载的容器 ref */
|
|
6
|
+
playerRef: RefObject<HTMLDivElement | null>;
|
|
7
|
+
/** 字幕数据 */
|
|
8
|
+
subtitles: SubtitleData[];
|
|
9
|
+
/** 当前播放时间(秒) */
|
|
10
|
+
currentTime: number;
|
|
11
|
+
/** 当前悬停单词的详情 */
|
|
12
|
+
currentDetail: SubtitleDetail | null;
|
|
13
|
+
/** 是否正在加载详情 */
|
|
14
|
+
isLoadingDetail: boolean;
|
|
15
|
+
/** 单词 hover 变化时拉取/展示详情 */
|
|
16
|
+
onWordHoverChange: (target: HoverTarget | null) => void | Promise<void>;
|
|
17
|
+
className?: string;
|
|
18
|
+
style?: React.CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* PC 端视图:可拖拽分栏 + 右侧详情区,所有 PC 专属 UI 集中在此
|
|
22
|
+
*/
|
|
23
|
+
export declare const VideoSubtitlePlayerPC: React.FC<VideoSubtitlePlayerPCProps>;
|