@video-lab/react-config 4.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Video Lab Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @video-lab/react-config
2
+
3
+ React inline 与 React Frame 共用的 `PlayerProvider` 上下文。业务通常从 `@video-lab/react` 或 `@video-lab/react-frame` 导入 `PlayerProvider`、`usePlayerI18n`,无需直接安装本包。
4
+
5
+ 公开 API 与示例:[全局播放器配置与语言切换](../../docs/guides/PLAYER-GLOBAL-CONFIG.md)。
6
+
7
+ ```tsx
8
+ import { PlayerProvider, usePlayerI18n } from '@video-lab/react-config'
9
+
10
+ function LanguageLabel() {
11
+ const { locale } = usePlayerI18n()
12
+ return <span>{locale}</span>
13
+ }
14
+
15
+ export function App() {
16
+ return <PlayerProvider defaultLocale="zh-CN"><LanguageLabel /></PlayerProvider>
17
+ }
18
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,97 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _video_lab_protocol = require("@video-lab/protocol");
3
+ let react = require("react");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ //#region src/index.tsx
6
+ const Context = (0, react.createContext)(null);
7
+ function PlayerProvider(props) {
8
+ const parent = (0, react.useContext)(Context);
9
+ const [internalLocale, setInternalLocale] = (0, react.useState)(() => (0, _video_lab_protocol.normalizeLocaleTag)(props.defaultLocale ?? parent?.locale ?? "zh-CN"));
10
+ const inheritsLocale = props.locale === void 0 && props.defaultLocale === void 0 && parent !== null;
11
+ const value = {
12
+ locale: inheritsLocale ? parent.locale : (0, _video_lab_protocol.normalizeLocaleTag)(props.locale ?? internalLocale),
13
+ resources: (0, _video_lab_protocol.mergeMessageResources)(parent?.resources, props.resources),
14
+ messageOverrides: (0, _video_lab_protocol.mergeMessageResources)(parent?.messageOverrides, props.messageOverrides),
15
+ fallbackLocales: props.fallbackLocales ?? parent?.fallbackLocales ?? [],
16
+ defaults: (0, _video_lab_protocol.mergePlayerDefaults)(parent?.defaults, props.defaults),
17
+ frameDefaults: (0, _video_lab_protocol.mergeFrameDefaults)(parent?.frameDefaults, props.frameDefaults),
18
+ changeLanguage(next) {
19
+ const normalized = (0, _video_lab_protocol.normalizeLocaleTag)(next);
20
+ if (inheritsLocale) return parent.changeLanguage(normalized);
21
+ if (props.locale !== void 0) {
22
+ if (!props.onLocaleChange) return false;
23
+ props.onLocaleChange(normalized);
24
+ return true;
25
+ }
26
+ setInternalLocale(normalized);
27
+ props.onLocaleChange?.(normalized);
28
+ return true;
29
+ }
30
+ };
31
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Context.Provider, {
32
+ value,
33
+ children: props.children
34
+ });
35
+ }
36
+ /** 播放器组件使用的可选配置;脱离 Provider 时保留原有默认行为。 */
37
+ function useOptionalPlayerConfig() {
38
+ return (0, react.useContext)(Context);
39
+ }
40
+ const LIVE_DEFAULTS = [
41
+ "muted",
42
+ "interactive",
43
+ "showErrorOverlay",
44
+ "showDefaultLoadingOverlay",
45
+ "showLoadingText",
46
+ "errorButtonColors"
47
+ ];
48
+ /** 构造期默认值固定于首次渲染;语言及已声明的实时字段跟随 Provider。 */
49
+ function useResolvedPlayerProps(props, frame = false) {
50
+ const configProps = props;
51
+ const context = useOptionalPlayerConfig();
52
+ const initialDefaults = (0, react.useRef)(null);
53
+ const initialFrameDefaults = (0, react.useRef)(null);
54
+ const localeCache = (0, react.useRef)(null);
55
+ if (!context) return props;
56
+ if (!initialDefaults.current) initialDefaults.current = context.defaults;
57
+ if (!initialFrameDefaults.current) initialFrameDefaults.current = context.frameDefaults;
58
+ const defaults = { ...initialDefaults.current };
59
+ for (const key of LIVE_DEFAULTS) defaults[key] = context.defaults[key];
60
+ const resolved = (0, _video_lab_protocol.applyPlayerDefaults)(configProps, defaults);
61
+ if (frame) {
62
+ for (const key of [
63
+ "origin",
64
+ "iframeVersion",
65
+ "debug"
66
+ ]) if (resolved[key] === void 0) resolved[key] = initialFrameDefaults.current[key];
67
+ }
68
+ const key = JSON.stringify([
69
+ configProps.locale,
70
+ context.locale,
71
+ context.resources,
72
+ context.messageOverrides,
73
+ context.fallbackLocales
74
+ ]);
75
+ if (localeCache.current?.key !== key) localeCache.current = {
76
+ key,
77
+ value: (0, _video_lab_protocol.applyProviderLocale)(configProps.locale, context.locale, context.resources, context.messageOverrides, context.fallbackLocales)
78
+ };
79
+ resolved.locale = localeCache.current.value;
80
+ return resolved;
81
+ }
82
+ /** 业务子组件读取翻译并请求切换语言。 */
83
+ function usePlayerI18n() {
84
+ const context = (0, react.useContext)(Context);
85
+ if (!context) throw new Error("usePlayerI18n 必须在 PlayerProvider 内使用");
86
+ const messages = (0, _video_lab_protocol.resolveLocaleMessages)((0, _video_lab_protocol.applyProviderLocale)(void 0, context.locale, context.resources, context.messageOverrides, context.fallbackLocales)).messages;
87
+ return {
88
+ locale: context.locale,
89
+ t: (key) => messages[key] ?? key,
90
+ changeLanguage: context.changeLanguage
91
+ };
92
+ }
93
+ //#endregion
94
+ exports.PlayerProvider = PlayerProvider;
95
+ exports.useOptionalPlayerConfig = useOptionalPlayerConfig;
96
+ exports.usePlayerI18n = usePlayerI18n;
97
+ exports.useResolvedPlayerProps = useResolvedPlayerProps;
@@ -0,0 +1,39 @@
1
+ import { FrameDefaults, LocaleConfig, MessageResources, PlayerDefaults } from "@video-lab/protocol";
2
+ import { ReactNode } from "react";
3
+ //#region src/index.d.ts
4
+ interface PlayerProviderProps {
5
+ children?: ReactNode;
6
+ locale?: string;
7
+ defaultLocale?: string;
8
+ onLocaleChange?: (locale: string) => void;
9
+ resources?: MessageResources;
10
+ messageOverrides?: MessageResources;
11
+ fallbackLocales?: string[];
12
+ defaults?: PlayerDefaults;
13
+ frameDefaults?: FrameDefaults;
14
+ }
15
+ interface PlayerConfigContext {
16
+ locale: string;
17
+ resources: MessageResources;
18
+ messageOverrides: MessageResources;
19
+ fallbackLocales: string[];
20
+ defaults: PlayerDefaults;
21
+ frameDefaults: FrameDefaults;
22
+ changeLanguage(next: string): boolean;
23
+ }
24
+ declare function PlayerProvider(props: PlayerProviderProps): ReactNode;
25
+ /** 播放器组件使用的可选配置;脱离 Provider 时保留原有默认行为。 */
26
+ declare function useOptionalPlayerConfig(): PlayerConfigContext | null;
27
+ /** 构造期默认值固定于首次渲染;语言及已声明的实时字段跟随 Provider。 */
28
+ declare function useResolvedPlayerProps<T extends object>(props: T, frame?: boolean): T & {
29
+ locale?: LocaleConfig;
30
+ };
31
+ /** 业务子组件读取翻译并请求切换语言。 */
32
+ declare function usePlayerI18n(): {
33
+ locale: string;
34
+ t(key: string): string;
35
+ changeLanguage(next: string): boolean;
36
+ };
37
+ //#endregion
38
+ export { PlayerConfigContext, PlayerProvider, PlayerProviderProps, useOptionalPlayerConfig, usePlayerI18n, useResolvedPlayerProps };
39
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1,39 @@
1
+ import { FrameDefaults, LocaleConfig, MessageResources, PlayerDefaults } from "@video-lab/protocol";
2
+ import { ReactNode } from "react";
3
+ //#region src/index.d.ts
4
+ interface PlayerProviderProps {
5
+ children?: ReactNode;
6
+ locale?: string;
7
+ defaultLocale?: string;
8
+ onLocaleChange?: (locale: string) => void;
9
+ resources?: MessageResources;
10
+ messageOverrides?: MessageResources;
11
+ fallbackLocales?: string[];
12
+ defaults?: PlayerDefaults;
13
+ frameDefaults?: FrameDefaults;
14
+ }
15
+ interface PlayerConfigContext {
16
+ locale: string;
17
+ resources: MessageResources;
18
+ messageOverrides: MessageResources;
19
+ fallbackLocales: string[];
20
+ defaults: PlayerDefaults;
21
+ frameDefaults: FrameDefaults;
22
+ changeLanguage(next: string): boolean;
23
+ }
24
+ declare function PlayerProvider(props: PlayerProviderProps): ReactNode;
25
+ /** 播放器组件使用的可选配置;脱离 Provider 时保留原有默认行为。 */
26
+ declare function useOptionalPlayerConfig(): PlayerConfigContext | null;
27
+ /** 构造期默认值固定于首次渲染;语言及已声明的实时字段跟随 Provider。 */
28
+ declare function useResolvedPlayerProps<T extends object>(props: T, frame?: boolean): T & {
29
+ locale?: LocaleConfig;
30
+ };
31
+ /** 业务子组件读取翻译并请求切换语言。 */
32
+ declare function usePlayerI18n(): {
33
+ locale: string;
34
+ t(key: string): string;
35
+ changeLanguage(next: string): boolean;
36
+ };
37
+ //#endregion
38
+ export { PlayerConfigContext, PlayerProvider, PlayerProviderProps, useOptionalPlayerConfig, usePlayerI18n, useResolvedPlayerProps };
39
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,95 @@
1
+ import { applyPlayerDefaults, applyProviderLocale, mergeFrameDefaults, mergeMessageResources, mergePlayerDefaults, normalizeLocaleTag, resolveLocaleMessages } from "@video-lab/protocol";
2
+ import { createContext, useContext, useRef, useState } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/index.tsx
5
+ const Context = createContext(null);
6
+ function PlayerProvider(props) {
7
+ const parent = useContext(Context);
8
+ const [internalLocale, setInternalLocale] = useState(() => normalizeLocaleTag(props.defaultLocale ?? parent?.locale ?? "zh-CN"));
9
+ const inheritsLocale = props.locale === void 0 && props.defaultLocale === void 0 && parent !== null;
10
+ const value = {
11
+ locale: inheritsLocale ? parent.locale : normalizeLocaleTag(props.locale ?? internalLocale),
12
+ resources: mergeMessageResources(parent?.resources, props.resources),
13
+ messageOverrides: mergeMessageResources(parent?.messageOverrides, props.messageOverrides),
14
+ fallbackLocales: props.fallbackLocales ?? parent?.fallbackLocales ?? [],
15
+ defaults: mergePlayerDefaults(parent?.defaults, props.defaults),
16
+ frameDefaults: mergeFrameDefaults(parent?.frameDefaults, props.frameDefaults),
17
+ changeLanguage(next) {
18
+ const normalized = normalizeLocaleTag(next);
19
+ if (inheritsLocale) return parent.changeLanguage(normalized);
20
+ if (props.locale !== void 0) {
21
+ if (!props.onLocaleChange) return false;
22
+ props.onLocaleChange(normalized);
23
+ return true;
24
+ }
25
+ setInternalLocale(normalized);
26
+ props.onLocaleChange?.(normalized);
27
+ return true;
28
+ }
29
+ };
30
+ return /* @__PURE__ */ jsx(Context.Provider, {
31
+ value,
32
+ children: props.children
33
+ });
34
+ }
35
+ /** 播放器组件使用的可选配置;脱离 Provider 时保留原有默认行为。 */
36
+ function useOptionalPlayerConfig() {
37
+ return useContext(Context);
38
+ }
39
+ const LIVE_DEFAULTS = [
40
+ "muted",
41
+ "interactive",
42
+ "showErrorOverlay",
43
+ "showDefaultLoadingOverlay",
44
+ "showLoadingText",
45
+ "errorButtonColors"
46
+ ];
47
+ /** 构造期默认值固定于首次渲染;语言及已声明的实时字段跟随 Provider。 */
48
+ function useResolvedPlayerProps(props, frame = false) {
49
+ const configProps = props;
50
+ const context = useOptionalPlayerConfig();
51
+ const initialDefaults = useRef(null);
52
+ const initialFrameDefaults = useRef(null);
53
+ const localeCache = useRef(null);
54
+ if (!context) return props;
55
+ if (!initialDefaults.current) initialDefaults.current = context.defaults;
56
+ if (!initialFrameDefaults.current) initialFrameDefaults.current = context.frameDefaults;
57
+ const defaults = { ...initialDefaults.current };
58
+ for (const key of LIVE_DEFAULTS) defaults[key] = context.defaults[key];
59
+ const resolved = applyPlayerDefaults(configProps, defaults);
60
+ if (frame) {
61
+ for (const key of [
62
+ "origin",
63
+ "iframeVersion",
64
+ "debug"
65
+ ]) if (resolved[key] === void 0) resolved[key] = initialFrameDefaults.current[key];
66
+ }
67
+ const key = JSON.stringify([
68
+ configProps.locale,
69
+ context.locale,
70
+ context.resources,
71
+ context.messageOverrides,
72
+ context.fallbackLocales
73
+ ]);
74
+ if (localeCache.current?.key !== key) localeCache.current = {
75
+ key,
76
+ value: applyProviderLocale(configProps.locale, context.locale, context.resources, context.messageOverrides, context.fallbackLocales)
77
+ };
78
+ resolved.locale = localeCache.current.value;
79
+ return resolved;
80
+ }
81
+ /** 业务子组件读取翻译并请求切换语言。 */
82
+ function usePlayerI18n() {
83
+ const context = useContext(Context);
84
+ if (!context) throw new Error("usePlayerI18n 必须在 PlayerProvider 内使用");
85
+ const messages = resolveLocaleMessages(applyProviderLocale(void 0, context.locale, context.resources, context.messageOverrides, context.fallbackLocales)).messages;
86
+ return {
87
+ locale: context.locale,
88
+ t: (key) => messages[key] ?? key,
89
+ changeLanguage: context.changeLanguage
90
+ };
91
+ }
92
+ //#endregion
93
+ export { PlayerProvider, useOptionalPlayerConfig, usePlayerI18n, useResolvedPlayerProps };
94
+
95
+ //# sourceMappingURL=index.mjs.map
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@video-lab/react-config",
3
+ "version": "4.0.0",
4
+ "license": "MIT",
5
+ "description": "Video Lab Player 的 React 全局配置与语言上下文",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.cts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.mts",
14
+ "default": "./dist/index.mjs"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ }
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "sideEffects": false,
26
+ "peerDependencies": {
27
+ "react": ">=19.0.0"
28
+ },
29
+ "dependencies": {
30
+ "@video-lab/protocol": "4.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@testing-library/react": "^16.0.0",
34
+ "@types/react": "^19.2.17",
35
+ "jsdom": "^24.0.0",
36
+ "react": "^19.2.7",
37
+ "react-dom": "^19.2.7",
38
+ "tsdown": "^0.22.9",
39
+ "typescript": "~5.9.0",
40
+ "vitest": "^4.1.10"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "build": "tsdown src/index.tsx --format esm,cjs --dts --clean",
47
+ "test": "vitest run",
48
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit"
49
+ }
50
+ }