xxf_react 0.6.3 → 0.6.5
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 +35 -0
- package/dist/flow/{promise_ext.d.ts → PromiseExt.d.ts} +1 -1
- package/dist/flow/PromiseExt.d.ts.map +1 -0
- package/dist/flow/PromiseLifecycle.d.ts +33 -0
- package/dist/flow/PromiseLifecycle.d.ts.map +1 -0
- package/dist/flow/PromiseLifecycle.js +37 -0
- package/dist/flow/PromiseLoading.d.ts +25 -0
- package/dist/flow/PromiseLoading.d.ts.map +1 -0
- package/dist/flow/PromiseLoading.js +30 -0
- package/dist/flow/index.d.ts +3 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +3 -1
- package/dist/index.js +1 -1
- package/dist/media/video-play-safe.d.ts +26 -0
- package/dist/media/video-play-safe.d.ts.map +1 -0
- package/dist/media/video-play-safe.js +56 -0
- package/package.json +1 -1
- package/dist/flow/promise_ext.d.ts.map +0 -1
- /package/dist/flow/{promise_ext.js → PromiseExt.js} +0 -0
package/README.md
CHANGED
|
@@ -35,3 +35,38 @@ This project was created using `bun init` in bun v1.3.2. [Bun](https://bun.com)
|
|
|
35
35
|
## 详细文档
|
|
36
36
|
|
|
37
37
|
- [PlaybackQueue 播放队列状态管理](./src/media/playback-queue-store.md)
|
|
38
|
+
|
|
39
|
+
### VideoPlaySafe 视频安全播放
|
|
40
|
+
|
|
41
|
+
处理移动端自动播放限制,自动降级为静音播放。
|
|
42
|
+
|
|
43
|
+
#### 功能特性
|
|
44
|
+
|
|
45
|
+
- 按当前 `video.muted` 状态尝试播放
|
|
46
|
+
- 如果非静音播放失败(`NotAllowedError` / `AbortError`),自动降级为静音重试
|
|
47
|
+
- 支持扩展 `HTMLVideoElement` 原型,直接调用 `video.playSafe()`
|
|
48
|
+
|
|
49
|
+
#### 使用方式
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { playSafe, extendVideoElement } from '@xxf/react/media/video-play-safe';
|
|
53
|
+
|
|
54
|
+
// 方式一:直接调用函数
|
|
55
|
+
await playSafe(videoElement, {
|
|
56
|
+
mutedRetry: true, // 是否在非静音播放失败时降级为静音重试,默认 true
|
|
57
|
+
onMutedChange: () => { // 从有声降级为静音时回调
|
|
58
|
+
console.log('已降级为静音播放');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 方式二:扩展原型后调用
|
|
63
|
+
extendVideoElement();
|
|
64
|
+
await videoElement.playSafe({ mutedRetry: true });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### API
|
|
68
|
+
|
|
69
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
70
|
+
|------|------|--------|------|
|
|
71
|
+
| `mutedRetry` | `boolean` | `true` | 是否在非静音播放失败时降级为静音重试 |
|
|
72
|
+
| `onMutedChange` | `() => void` | - | 从有声降级为静音时的回调函数 |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromiseExt.d.ts","sourceRoot":"","sources":["../../src/flow/PromiseExt.ts"],"names":[],"mappings":"AAIA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC,CAAC;QACf,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACjE;CACJ;AAGD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAM1D;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAgDhD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface PromiseLifecycle<T> {
|
|
2
|
+
/** 开始执行时(同步调用) */
|
|
3
|
+
doOnStart?: () => void;
|
|
4
|
+
/** 成功时 */
|
|
5
|
+
doOnSuccess?: (value: T) => void;
|
|
6
|
+
/** 失败时 */
|
|
7
|
+
doOnError?: (error: unknown) => void;
|
|
8
|
+
/** 无论成功失败都会调用(类似 doFinally) */
|
|
9
|
+
doFinally?: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 为 Promise 添加生命周期钩子
|
|
13
|
+
*
|
|
14
|
+
* @param promiseFn - 返回 Promise 的函数(使用回调形式以确保 doOnStart 在 Promise 执行前调用)
|
|
15
|
+
* @param lifecycle - 生命周期回调配置
|
|
16
|
+
* @returns 原始 Promise 的结果
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // 基本用法:管理 loading 状态
|
|
21
|
+
* withLifecycle(
|
|
22
|
+
* () => fetchUserData(userId),
|
|
23
|
+
* {
|
|
24
|
+
* doOnStart: () => setLoading(true),
|
|
25
|
+
* doOnSuccess: (user) => setUser(user),
|
|
26
|
+
* doOnError: (err) => setError(err.message),
|
|
27
|
+
* doFinally: () => setLoading(false)
|
|
28
|
+
* }
|
|
29
|
+
* );
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function withLifecycle<T>(promiseFn: () => Promise<T>, lifecycle: PromiseLifecycle<T>): Promise<T>;
|
|
33
|
+
//# sourceMappingURL=PromiseLifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromiseLifecycle.d.ts","sourceRoot":"","sources":["../../src/flow/PromiseLifecycle.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAC/B,kBAAkB;IAClB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,UAAU;IACV,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACjC,UAAU;IACV,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC3B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 为 Promise 添加生命周期钩子
|
|
3
|
+
*
|
|
4
|
+
* @param promiseFn - 返回 Promise 的函数(使用回调形式以确保 doOnStart 在 Promise 执行前调用)
|
|
5
|
+
* @param lifecycle - 生命周期回调配置
|
|
6
|
+
* @returns 原始 Promise 的结果
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // 基本用法:管理 loading 状态
|
|
11
|
+
* withLifecycle(
|
|
12
|
+
* () => fetchUserData(userId),
|
|
13
|
+
* {
|
|
14
|
+
* doOnStart: () => setLoading(true),
|
|
15
|
+
* doOnSuccess: (user) => setUser(user),
|
|
16
|
+
* doOnError: (err) => setError(err.message),
|
|
17
|
+
* doFinally: () => setLoading(false)
|
|
18
|
+
* }
|
|
19
|
+
* );
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function withLifecycle(promiseFn, lifecycle) {
|
|
23
|
+
const { doOnStart, doOnSuccess, doOnError, doFinally } = lifecycle;
|
|
24
|
+
doOnStart === null || doOnStart === void 0 ? void 0 : doOnStart();
|
|
25
|
+
return promiseFn()
|
|
26
|
+
.then((value) => {
|
|
27
|
+
doOnSuccess === null || doOnSuccess === void 0 ? void 0 : doOnSuccess(value);
|
|
28
|
+
return value;
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
doOnError === null || doOnError === void 0 ? void 0 : doOnError(error);
|
|
32
|
+
throw error; // 继续抛出,不吞掉错误
|
|
33
|
+
})
|
|
34
|
+
.finally(() => {
|
|
35
|
+
doFinally === null || doFinally === void 0 ? void 0 : doFinally();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 为 Promise 自动绑定 loading 状态
|
|
3
|
+
*
|
|
4
|
+
* @param promiseFn - 返回 Promise 的函数
|
|
5
|
+
* @param setLoading - React 的 setState 函数,用于设置 loading 状态
|
|
6
|
+
* @returns 原始 Promise 的结果
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const [loading, setLoading] = useState(false);
|
|
11
|
+
*
|
|
12
|
+
* // 自动管理 loading 状态
|
|
13
|
+
* const handleSubmit = () => {
|
|
14
|
+
* withLoading(() => submitForm(data), setLoading);
|
|
15
|
+
* };
|
|
16
|
+
*
|
|
17
|
+
* // 等价于
|
|
18
|
+
* withLifecycle(() => submitForm(data), {
|
|
19
|
+
* doOnStart: () => setLoading(true),
|
|
20
|
+
* doFinally: () => setLoading(false)
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function withLoading<T>(promiseFn: () => Promise<T>, setLoading: (loading: boolean) => void): Promise<T>;
|
|
25
|
+
//# sourceMappingURL=PromiseLoading.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromiseLoading.d.ts","sourceRoot":"","sources":["../../src/flow/PromiseLoading.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EACzB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GACvC,OAAO,CAAC,CAAC,CAAC,CAKZ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { withLifecycle } from "./PromiseLifecycle";
|
|
2
|
+
/**
|
|
3
|
+
* 为 Promise 自动绑定 loading 状态
|
|
4
|
+
*
|
|
5
|
+
* @param promiseFn - 返回 Promise 的函数
|
|
6
|
+
* @param setLoading - React 的 setState 函数,用于设置 loading 状态
|
|
7
|
+
* @returns 原始 Promise 的结果
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const [loading, setLoading] = useState(false);
|
|
12
|
+
*
|
|
13
|
+
* // 自动管理 loading 状态
|
|
14
|
+
* const handleSubmit = () => {
|
|
15
|
+
* withLoading(() => submitForm(data), setLoading);
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* // 等价于
|
|
19
|
+
* withLifecycle(() => submitForm(data), {
|
|
20
|
+
* doOnStart: () => setLoading(true),
|
|
21
|
+
* doFinally: () => setLoading(false)
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function withLoading(promiseFn, setLoading) {
|
|
26
|
+
return withLifecycle(promiseFn, {
|
|
27
|
+
doOnStart: () => setLoading(true),
|
|
28
|
+
doFinally: () => setLoading(false)
|
|
29
|
+
});
|
|
30
|
+
}
|
package/dist/flow/index.d.ts
CHANGED
package/dist/flow/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/flow/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/flow/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
package/dist/flow/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video 安全播放扩展
|
|
3
|
+
* 处理移动端自动播放限制,自动降级为静音播放
|
|
4
|
+
*/
|
|
5
|
+
export interface PlaySafeOptions {
|
|
6
|
+
/** 是否在非静音播放失败时降级为静音重试,默认 true */
|
|
7
|
+
mutedRetry?: boolean;
|
|
8
|
+
/** 从有声降级为静音时回调 */
|
|
9
|
+
onMutedChange?: () => void;
|
|
10
|
+
}
|
|
11
|
+
declare global {
|
|
12
|
+
interface HTMLVideoElement {
|
|
13
|
+
playSafe(options?: PlaySafeOptions): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 安全播放视频
|
|
18
|
+
* 1. 按当前 video.muted 状态尝试播放
|
|
19
|
+
* 2. 如果非静音播放失败,降级为静音再试
|
|
20
|
+
*/
|
|
21
|
+
export declare function playSafe(video: HTMLVideoElement, options?: PlaySafeOptions): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* 扩展 HTMLVideoElement 原型
|
|
24
|
+
*/
|
|
25
|
+
export declare function extendVideoElement(): void;
|
|
26
|
+
//# sourceMappingURL=video-play-safe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-play-safe.d.ts","sourceRoot":"","sources":["../../src/media/video-play-safe.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC5B,iCAAiC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB;IAClB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAGD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,gBAAgB;QACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtD;CACJ;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC1B,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE,eAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAYzC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video 安全播放扩展
|
|
3
|
+
* 处理移动端自动播放限制,自动降级为静音播放
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 安全播放视频
|
|
7
|
+
* 1. 按当前 video.muted 状态尝试播放
|
|
8
|
+
* 2. 如果非静音播放失败,降级为静音再试
|
|
9
|
+
*/
|
|
10
|
+
export async function playSafe(video, options = {}) {
|
|
11
|
+
const { mutedRetry = true, onMutedChange } = options;
|
|
12
|
+
if (!video || !video.parentElement || !document.contains(video)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const wasMuted = video.muted;
|
|
16
|
+
try {
|
|
17
|
+
await video.play();
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
// 处理自动播放被阻止的错误
|
|
21
|
+
// - NotAllowedError: Chrome/Firefox 自动播放策略阻止
|
|
22
|
+
// - AbortError: Safari/iOS 自动播放策略阻止(包括低电量模式)
|
|
23
|
+
const isAutoplayBlocked = error instanceof Error &&
|
|
24
|
+
(error.name === 'NotAllowedError' || error.name === 'AbortError');
|
|
25
|
+
if (!isAutoplayBlocked) {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
// 不启用静音重试,直接抛出
|
|
29
|
+
if (!mutedRetry)
|
|
30
|
+
throw error;
|
|
31
|
+
// 已经是静音还失败,直接抛出
|
|
32
|
+
if (wasMuted)
|
|
33
|
+
throw error;
|
|
34
|
+
// 降级为静音播放
|
|
35
|
+
video.muted = true;
|
|
36
|
+
onMutedChange === null || onMutedChange === void 0 ? void 0 : onMutedChange();
|
|
37
|
+
await video.play();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 扩展 HTMLVideoElement 原型
|
|
42
|
+
*/
|
|
43
|
+
export function extendVideoElement() {
|
|
44
|
+
if (typeof HTMLVideoElement === 'undefined')
|
|
45
|
+
return;
|
|
46
|
+
if ('playSafe' in HTMLVideoElement.prototype)
|
|
47
|
+
return;
|
|
48
|
+
Object.defineProperty(HTMLVideoElement.prototype, 'playSafe', {
|
|
49
|
+
value: function (options = {}) {
|
|
50
|
+
return playSafe(this, options);
|
|
51
|
+
},
|
|
52
|
+
writable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
enumerable: false,
|
|
55
|
+
});
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"promise_ext.d.ts","sourceRoot":"","sources":["../../src/flow/promise_ext.ts"],"names":[],"mappings":"AAIA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC,CAAC;QACf,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACjE;CACJ;AAGD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAM1D;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAgDhD"}
|
|
File without changes
|