@vnejs/plugins.views.scenario.interface 0.1.12
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/const/events.js +31 -0
- package/const/params.js +37 -0
- package/index.js +10 -0
- package/modules/controller.js +74 -0
- package/modules/interface.js +87 -0
- package/modules/view.js +14 -0
- package/package.json +17 -0
- package/view/components/InterfaceBackdrop.jsx +22 -0
- package/view/components/InterfaceViewDialog.jsx +42 -0
- package/view/components/InterfaceViewLine.jsx +25 -0
- package/view/components/InterfaceViewWall.jsx +65 -0
- package/view/components/index.js +4 -0
- package/view/components/tokens.hook.jsx +34 -0
- package/view/index.jsx +62 -0
package/const/events.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const SUBSCRIBE_EVENTS = {
|
|
2
|
+
SHOW: "vne:interface:show",
|
|
3
|
+
HIDE: "vne:interface:hide",
|
|
4
|
+
|
|
5
|
+
SHOW_CHANGED: "vne:interface:show_changed",
|
|
6
|
+
|
|
7
|
+
VIEW_SHOW: "vne:interface:view_show",
|
|
8
|
+
VIEW_HIDE: "vne:interface:view_hide",
|
|
9
|
+
VIEW_UPDATE: "vne:interface:view_update",
|
|
10
|
+
|
|
11
|
+
TEXT: "vne:interface:text",
|
|
12
|
+
TEXT_HIDE: "vne:interface:text_hide",
|
|
13
|
+
TEXT_CHANGED: "vne:interface:text_changed",
|
|
14
|
+
|
|
15
|
+
SPEAKER_CHANGED: "vne:interface:speaker_changed",
|
|
16
|
+
|
|
17
|
+
VISIBLE: "vne:interface:visible",
|
|
18
|
+
VISIBLE_CHANGED: "vne:interface:visible_changed",
|
|
19
|
+
|
|
20
|
+
VIEW: "vne:interface:view",
|
|
21
|
+
VIEW_CHANGED: "vne:interface:view_changed",
|
|
22
|
+
|
|
23
|
+
HISTORY: "vne:interface:history",
|
|
24
|
+
HISTORY_BACK: "vne:interface:history_back",
|
|
25
|
+
|
|
26
|
+
GAMEMENU: "vne:interface:gamemenu",
|
|
27
|
+
|
|
28
|
+
INTERACT: "vne:interface:interact",
|
|
29
|
+
|
|
30
|
+
STATE_CHANGED: "vne:interface:state_changed",
|
|
31
|
+
};
|
package/const/params.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getVneLength } from "@vnejs/uis.utils";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_VIEW = "line";
|
|
4
|
+
|
|
5
|
+
export const LOC_LABEL = "interface";
|
|
6
|
+
|
|
7
|
+
export const TRANSITION = 500;
|
|
8
|
+
export const ZINDEX = 1000;
|
|
9
|
+
|
|
10
|
+
const createBackdropProps = (opacity) => ({ type: "bottom", size: 600, transition: TRANSITION, opacity });
|
|
11
|
+
const createFlatBackdropPosition = (margin, opacity) => ({ top: margin, left: margin, right: margin, bottom: margin, transition: TRANSITION, opacity });
|
|
12
|
+
const createFlatBackdropStyle = (borderRadius) => ({ background: "rgba(0, 0, 0, 0.6)", borderRadius, transition: `${TRANSITION}ms` });
|
|
13
|
+
|
|
14
|
+
export const VIEW_PROPS = {
|
|
15
|
+
adv: {
|
|
16
|
+
backdrop: { props: createBackdropProps(1), flatPosition: createFlatBackdropPosition(0, 0), flatStyle: createFlatBackdropStyle(0) },
|
|
17
|
+
position: { bottom: 144, left: 600, right: 600 },
|
|
18
|
+
flex: { direction: "column", gap: 24 },
|
|
19
|
+
speaker: { size: 72, weight: "bold" },
|
|
20
|
+
text: { size: 60, height: 300 },
|
|
21
|
+
},
|
|
22
|
+
wall: {
|
|
23
|
+
backdrop: { props: createBackdropProps(0), flatPosition: createFlatBackdropPosition(120, 1), flatStyle: createFlatBackdropStyle(getVneLength(48)) },
|
|
24
|
+
position: { top: 180, left: 180, right: 180, bottom: 264 },
|
|
25
|
+
flex: { direction: "column", gap: 24 },
|
|
26
|
+
scrollBar: { height: 1680, scrollToEnd: true, hideScrolbars: true, disableScroll: true },
|
|
27
|
+
speaker: { size: 72, weight: "bold" },
|
|
28
|
+
text: { size: 60 },
|
|
29
|
+
},
|
|
30
|
+
line: {
|
|
31
|
+
backdrop: { props: createBackdropProps(0), flatPosition: createFlatBackdropPosition(0, 1), flatStyle: createFlatBackdropStyle(0) },
|
|
32
|
+
position: { isCentered: true },
|
|
33
|
+
flex: { direction: "column", gap: 60 },
|
|
34
|
+
speaker: { size: 72, weight: "bold" },
|
|
35
|
+
text: { isCentered: true, size: 60, width: 3120 },
|
|
36
|
+
},
|
|
37
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { regPlugin } from "@vnejs/shared";
|
|
2
|
+
|
|
3
|
+
import { SUBSCRIBE_EVENTS } from "./const/events";
|
|
4
|
+
import * as params from "./const/params";
|
|
5
|
+
|
|
6
|
+
import { InterfaceController } from "./modules/controller";
|
|
7
|
+
import { Interface } from "./modules/interface";
|
|
8
|
+
import { InterfaceView } from "./modules/view";
|
|
9
|
+
|
|
10
|
+
regPlugin("INTERFACE", { events: SUBSCRIBE_EVENTS, params }, [InterfaceController, Interface, InterfaceView]);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
+
|
|
3
|
+
export class InterfaceController extends ModuleController {
|
|
4
|
+
name = "interface.controller";
|
|
5
|
+
|
|
6
|
+
emitInteract = () => this.emit(this.EVENTS.INTERFACE.INTERACT);
|
|
7
|
+
|
|
8
|
+
updateEvent = this.EVENTS.INTERFACE.VIEW_UPDATE;
|
|
9
|
+
controls = {
|
|
10
|
+
[this.CONST.CONTROLS.BUTTONS.INTERACT]: this.emitInteract,
|
|
11
|
+
[this.CONST.CONTROLS.BUTTONS.ACCEPT]: this.emitInteract,
|
|
12
|
+
};
|
|
13
|
+
controlsUnvisible = {};
|
|
14
|
+
controlsIndex = this.PARAMS.INTERFACE.ZINDEX;
|
|
15
|
+
|
|
16
|
+
subscribe = () => {
|
|
17
|
+
this.on(this.EVENTS.INTERFACE.VIEW_SHOW, this.onShow);
|
|
18
|
+
this.on(this.EVENTS.INTERFACE.VIEW_HIDE, this.onHide);
|
|
19
|
+
|
|
20
|
+
this.on(this.EVENTS.INTERFACE.SHOW_CHANGED, this.onShowChanged);
|
|
21
|
+
this.on(this.EVENTS.INTERFACE.VIEW_CHANGED, this.onViewChanged);
|
|
22
|
+
this.on(this.EVENTS.INTERFACE.TEXT_CHANGED, this.onTextChanged);
|
|
23
|
+
this.on(this.EVENTS.INTERFACE.SPEAKER_CHANGED, this.onSpeakerChanged);
|
|
24
|
+
this.on(this.EVENTS.INTERFACE.STATE_CHANGED, this.onStateChanged);
|
|
25
|
+
this.on(this.EVENTS.INTERFACE.VISIBLE_CHANGED, this.onVisibleChanged);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
init = () => this.emit(this.EVENTS.INTERFACE.VIEW, { view: this.PARAMS.INTERFACE.DEFAULT_VIEW, isForce: true });
|
|
29
|
+
|
|
30
|
+
onShowChanged = ({ isForce = false } = {}) => {
|
|
31
|
+
const { isShow = false } = this.globalState.interface;
|
|
32
|
+
const event = isShow ? this.EVENTS.INTERFACE.VIEW_SHOW : this.EVENTS.INTERFACE.VIEW_HIDE;
|
|
33
|
+
|
|
34
|
+
if (this.state.isShow !== isShow) return this.emit(event, { isForce });
|
|
35
|
+
};
|
|
36
|
+
onViewChanged = ({ isForce = false } = {}) => {
|
|
37
|
+
const { view = "" } = this.globalState.interface;
|
|
38
|
+
|
|
39
|
+
if (this.state.view !== view) return this.updateStateAndView({ view }, isForce, !isForce);
|
|
40
|
+
};
|
|
41
|
+
onTextChanged = async () => {
|
|
42
|
+
const { tokens = [], wall = [], uid = "", tokensVisible = 0 } = this.globalState.interface;
|
|
43
|
+
|
|
44
|
+
while (this.isStateChangeInProcess) await this.waitRerender();
|
|
45
|
+
|
|
46
|
+
return this.updateStateAndViewFast({ tokens, wall, uid, tokensVisible });
|
|
47
|
+
};
|
|
48
|
+
onSpeakerChanged = async () => {
|
|
49
|
+
const { speaker = "", meet = 0 } = this.globalState.interface;
|
|
50
|
+
|
|
51
|
+
while (this.isStateChangeInProcess) await this.waitRerender();
|
|
52
|
+
|
|
53
|
+
return this.updateStateAndViewFast({ speaker, meet });
|
|
54
|
+
};
|
|
55
|
+
onVisibleChanged = async ({ isForce = false } = {}) => {
|
|
56
|
+
const { isVisible = false } = this.globalState.interface;
|
|
57
|
+
|
|
58
|
+
if (this.state.isVisible === isVisible) return;
|
|
59
|
+
|
|
60
|
+
const args = { key: `${this.name}-unvisible`, controls: this.controlsUnvisible, index: this.controlsIndex + 500 };
|
|
61
|
+
await this.emit(isVisible ? this.EVENTS.CONTROLS.POP : this.EVENTS.CONTROLS.PUSH, args);
|
|
62
|
+
await this.updateStateAndView({ isVisible }, isForce, !isForce);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
onStateChanged = async () => {
|
|
66
|
+
const { view = "", isShow = false, isVisible = false } = this.globalState.interface;
|
|
67
|
+
|
|
68
|
+
this.isStateChangeInProcess = true;
|
|
69
|
+
await this.updateStateAndViewForce({ view, isShow, isVisible });
|
|
70
|
+
this.isStateChangeInProcess = false;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
getDefaultState = () => ({ tokens: [], wall: [], uid: "", tokensVisible: 0, speaker: "", meet: 0, isShow: false, isVisible: false });
|
|
74
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
import { tokenizeExecLine } from "@vnejs/helpers";
|
|
4
|
+
|
|
5
|
+
export class Interface extends Module {
|
|
6
|
+
name = "interface";
|
|
7
|
+
|
|
8
|
+
subscribe = () => {
|
|
9
|
+
this.on(this.EVENTS.INTERFACE.INTERACT, this.onInteract);
|
|
10
|
+
this.on(this.EVENTS.INTERFACE.SHOW, this.onShow);
|
|
11
|
+
this.on(this.EVENTS.INTERFACE.HIDE, this.onHide);
|
|
12
|
+
this.on(this.EVENTS.INTERFACE.VISIBLE, this.onVisible);
|
|
13
|
+
this.on(this.EVENTS.INTERFACE.TEXT_HIDE, this.onTextHide);
|
|
14
|
+
this.on(this.EVENTS.INTERFACE.VIEW, this.onView);
|
|
15
|
+
|
|
16
|
+
this.on(this.EVENTS.TEXT.CHANGED, this.onTextChanged);
|
|
17
|
+
this.on(this.EVENTS.TEXT.SPEAKER_CHANGED, this.onTextSpeakerChanged);
|
|
18
|
+
this.on(this.EVENTS.TEXT.EMIT_BEFORE, this.onTextEmitBefore);
|
|
19
|
+
|
|
20
|
+
this.on(this.EVENTS.STATE.SET, this.onStateSet);
|
|
21
|
+
this.on(this.EVENTS.STATE.CLEAR, this.onStateClear);
|
|
22
|
+
};
|
|
23
|
+
init = () => this.emit(this.EVENTS.SCENARIO.LINE_EXEC_REG, { module: this.name, handler: this.onLineExec });
|
|
24
|
+
|
|
25
|
+
onLineExec = async ({ line = "" } = {}) => {
|
|
26
|
+
const isForce = Boolean(this.shared.viewForceAnimationSources.length);
|
|
27
|
+
|
|
28
|
+
const [action = "", execArg = ""] = tokenizeExecLine(line);
|
|
29
|
+
|
|
30
|
+
if (action === "show") await this.emit(this.EVENTS.INTERFACE.SHOW, { isForce });
|
|
31
|
+
if (action === "hide") await this.emit(this.EVENTS.INTERFACE.HIDE, { isForce });
|
|
32
|
+
if (action === "view") await this.emit(this.EVENTS.INTERFACE.VIEW, { view: execArg, isForce });
|
|
33
|
+
|
|
34
|
+
this.emit(this.EVENTS.SCENARIO.NEXT, { module: this.name });
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
onInteract = () => this.emit(this.state.isVisible ? this.EVENTS.INTERACT.EMIT : this.EVENTS.INTERFACE.VISIBLE);
|
|
38
|
+
onShow = ({ isForce = false } = {}) => !this.state.isShow && this.setShow(true, isForce);
|
|
39
|
+
onHide = ({ isForce = false } = {}) => this.state.isShow && this.setShow(false, isForce);
|
|
40
|
+
onVisible = ({ isForce = false } = {}) => this.setVisible(!this.state.isVisible, isForce);
|
|
41
|
+
onTextChanged = ({ tokens, wall, uid, tokensVisible } = {}) => this.setText(tokens, wall, uid, tokensVisible);
|
|
42
|
+
onTextSpeakerChanged = ({ speaker, meet } = {}) => this.setSpeaker(speaker, meet);
|
|
43
|
+
onTextEmitBefore = () => this.emit(this.EVENTS.INTERFACE.SHOW);
|
|
44
|
+
onTextHide = () => this.setText([], [], "", 0);
|
|
45
|
+
onView = ({ view, isForce = false } = {}) => this.setView(view, isForce);
|
|
46
|
+
onStateSet = ({ [this.name]: state } = {}) => this.setNewState(state.view, state.isVisible, state.isShow);
|
|
47
|
+
onStateClear = () => this.setNewState(this.PARAMS.INTERFACE.DEFAULT_VIEW, false, false);
|
|
48
|
+
|
|
49
|
+
setView = (view, isForce = false) => {
|
|
50
|
+
this.state.view = view;
|
|
51
|
+
|
|
52
|
+
return this.emit(this.EVENTS.INTERFACE.VIEW_CHANGED, { isForce });
|
|
53
|
+
};
|
|
54
|
+
setShow = async (isShow, isForce = false) => {
|
|
55
|
+
this.state.isShow = isShow;
|
|
56
|
+
|
|
57
|
+
if (isShow && !this.state.isVisible) await this.setVisible(true, isForce);
|
|
58
|
+
|
|
59
|
+
return this.emit(this.EVENTS.INTERFACE.SHOW_CHANGED, { isForce });
|
|
60
|
+
};
|
|
61
|
+
setVisible = (isVisible, isForce = false) => {
|
|
62
|
+
this.state.isVisible = isVisible;
|
|
63
|
+
|
|
64
|
+
return this.emit(this.EVENTS.INTERFACE.VISIBLE_CHANGED, { isForce });
|
|
65
|
+
};
|
|
66
|
+
setText = (tokens = [], wall = [], uid = "", tokensVisible) => {
|
|
67
|
+
Object.assign(this.state, { tokens, wall, uid, tokensVisible });
|
|
68
|
+
|
|
69
|
+
return this.emit(this.EVENTS.INTERFACE.TEXT_CHANGED);
|
|
70
|
+
};
|
|
71
|
+
setSpeaker = (speaker = "", meet = 0) => {
|
|
72
|
+
Object.assign(this.state, { speaker, meet });
|
|
73
|
+
|
|
74
|
+
return this.emit(this.EVENTS.INTERFACE.SPEAKER_CHANGED);
|
|
75
|
+
};
|
|
76
|
+
setNewState = (view, isVisible, isShow) => {
|
|
77
|
+
Object.assign(this.state, { isShow, view, isVisible });
|
|
78
|
+
|
|
79
|
+
return this.emit(this.EVENTS.INTERFACE.STATE_CHANGED);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
getDefaultState = () => {
|
|
83
|
+
const result = { tokens: [], view: this.PARAMS.INTERFACE.DEFAULT_VIEW, uid: "", speaker: "" };
|
|
84
|
+
|
|
85
|
+
return { ...result, meet: 0, tokensVisible: 0, isShow: false, isVisible: false };
|
|
86
|
+
};
|
|
87
|
+
}
|
package/modules/view.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ModuleView } from "@vnejs/module.components";
|
|
2
|
+
|
|
3
|
+
import { render } from "../view";
|
|
4
|
+
|
|
5
|
+
export class InterfaceView extends ModuleView {
|
|
6
|
+
name = "interface.view";
|
|
7
|
+
|
|
8
|
+
locLabel = this.PARAMS.INTERFACE.LOC_LABEL;
|
|
9
|
+
animationTime = this.PARAMS.INTERFACE.TRANSITION;
|
|
10
|
+
updateEvent = this.EVENTS.INTERFACE.VIEW_UPDATE;
|
|
11
|
+
|
|
12
|
+
renderFunc = render;
|
|
13
|
+
updateHandler = this.onUpdateStoreComponent;
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vnejs/plugins.views.scenario.interface",
|
|
3
|
+
"version": "0.1.12",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
7
|
+
"publish:major:plugin": "npm run publish:major",
|
|
8
|
+
"publish:minor:plugin": "npm run publish:minor",
|
|
9
|
+
"publish:patch:plugin": "npm run publish:patch",
|
|
10
|
+
"publish:major": "npm version major && npm publish --access public",
|
|
11
|
+
"publish:minor": "npm version minor && npm publish --access public",
|
|
12
|
+
"publish:patch": "npm version patch && npm publish --access public"
|
|
13
|
+
},
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"description": ""
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { Backdrop, PositionBox } from "@vnejs/uis.react";
|
|
4
|
+
|
|
5
|
+
export const InterfaceBackdrop = ({ view = "", transition = 0, ...props }) => {
|
|
6
|
+
const propsByView = useMemo(() => props.PARAMS.INTERFACE.VIEW_PROPS[view]?.backdrop || {}, [view]);
|
|
7
|
+
|
|
8
|
+
const propsBackdrop = useMemo(() => ({ ...propsByView.props, transition }), [propsByView, transition]);
|
|
9
|
+
const propsPosition = useMemo(() => ({ ...propsByView.flatPosition, transition }), [propsByView, transition]);
|
|
10
|
+
|
|
11
|
+
const styleFlatBackdrop = useMemo(() => ({ ...propsByView.flatStyle, width: "100%", height: "100%", transition: `${transition}ms` }), [propsByView]);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
<Backdrop {...propsBackdrop} />
|
|
16
|
+
|
|
17
|
+
<PositionBox {...propsPosition}>
|
|
18
|
+
<div style={styleFlatBackdrop} />
|
|
19
|
+
</PositionBox>
|
|
20
|
+
</>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { Flex, PositionBox, SmoothText, Text } from "@vnejs/uis.react";
|
|
4
|
+
import { getVneLength } from "@vnejs/uis.utils";
|
|
5
|
+
import { useTokensHook } from "./tokens.hook";
|
|
6
|
+
|
|
7
|
+
export const InterfaceViewDialog = (props = {}) => {
|
|
8
|
+
const { isShow = false, transition, tokensVisible = 0, tokens, speakerText, speakerName, speakerColor, opacity, color, uid = "" } = props;
|
|
9
|
+
|
|
10
|
+
const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.adv;
|
|
11
|
+
|
|
12
|
+
const propsPosition = useMemo(() => ({ ...propsByView.position, opacity: isShow ? 1 : 0, transition }), [isShow, transition, propsByView.position]);
|
|
13
|
+
|
|
14
|
+
const speakerOpacity = speakerName ? 1 : 0;
|
|
15
|
+
const speakerTransition = speakerName ? transition : 0;
|
|
16
|
+
|
|
17
|
+
const backgroundSep = useMemo(() => `linear-gradient(to right, ${speakerColor} 0%, transparent 100%)`, [speakerColor]);
|
|
18
|
+
const styleSepBg = useMemo(() => ({ height: getVneLength(6), width: "calc(200% / 3)", background: backgroundSep }), [backgroundSep]);
|
|
19
|
+
const styleSepColor = useMemo(() => ({ opacity: speakerOpacity, transition: `${speakerTransition}ms` || "none" }), [speakerOpacity, speakerTransition]);
|
|
20
|
+
const styleSep = useMemo(() => ({ ...styleSepBg, ...styleSepColor }), [styleSepBg, styleSepColor]);
|
|
21
|
+
|
|
22
|
+
const { realTokens, realTokensVisible } = useTokensHook(isShow, tokens, tokensVisible, uid);
|
|
23
|
+
|
|
24
|
+
const propsSpeaker = useMemo(
|
|
25
|
+
() => ({ ...propsByView.speaker, text: speakerText, color: speakerColor, opacity: speakerOpacity, transition: speakerTransition }),
|
|
26
|
+
[speakerText, speakerColor, speakerOpacity, speakerTransition],
|
|
27
|
+
);
|
|
28
|
+
const propsText = useMemo(
|
|
29
|
+
() => ({ ...propsByView.text, opacity, color, tokens: realTokens, tokensVisibleForce: realTokensVisible, transition }),
|
|
30
|
+
[opacity, color, realTokens, realTokensVisible, transition],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<PositionBox {...propsPosition}>
|
|
35
|
+
<Flex {...propsByView.flex}>
|
|
36
|
+
<Text {...propsSpeaker} />
|
|
37
|
+
<div style={styleSep} />
|
|
38
|
+
<SmoothText {...propsText} />
|
|
39
|
+
</Flex>
|
|
40
|
+
</PositionBox>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { Flex, PositionBox, SmoothText } from "@vnejs/uis.react";
|
|
4
|
+
|
|
5
|
+
import { useTokensHook } from "./tokens.hook";
|
|
6
|
+
|
|
7
|
+
export const InterfaceViewLine = (props = {}) => {
|
|
8
|
+
const { isShow = false, transition = 0, tokensVisible = 0, tokens = [], opacity, color, uid = "" } = props;
|
|
9
|
+
|
|
10
|
+
const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.line;
|
|
11
|
+
|
|
12
|
+
const { realTokens, realTokensVisible } = useTokensHook(isShow, tokens, tokensVisible, uid);
|
|
13
|
+
|
|
14
|
+
const propsTextTokens = useMemo(() => ({ tokens: realTokens, tokensVisibleForce: realTokensVisible }), [realTokens, realTokensVisible]);
|
|
15
|
+
const propsText = useMemo(() => ({ ...propsByView.text, opacity, color, transition, ...propsTextTokens }), [opacity, color, transition, propsTextTokens]);
|
|
16
|
+
const propsPosition = useMemo(() => ({ ...propsByView.position, opacity: isShow ? 1 : 0, transition }), [isShow, transition]);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<PositionBox {...propsPosition}>
|
|
20
|
+
<Flex {...propsByView.flex}>
|
|
21
|
+
<SmoothText {...propsText} />
|
|
22
|
+
</Flex>
|
|
23
|
+
</PositionBox>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { Flex, PositionBox, Scrollbar, ScrollContent, SmoothText, Text } from "@vnejs/uis.react";
|
|
4
|
+
|
|
5
|
+
import { useTokensHook } from "./tokens.hook";
|
|
6
|
+
|
|
7
|
+
const renderOneText = (wallElement) => (
|
|
8
|
+
<div key={wallElement.uid}>
|
|
9
|
+
{wallElement.speakerName && (
|
|
10
|
+
<Text
|
|
11
|
+
{...wallElement.propsByView.text}
|
|
12
|
+
isInlineBlock={true}
|
|
13
|
+
color={wallElement.speakerColor}
|
|
14
|
+
text={`${wallElement.speakerName}:`}
|
|
15
|
+
marginRight={12}
|
|
16
|
+
/>
|
|
17
|
+
)}
|
|
18
|
+
<SmoothText
|
|
19
|
+
{...wallElement.propsByView.text}
|
|
20
|
+
isNoScrollBar={true}
|
|
21
|
+
color={wallElement.color}
|
|
22
|
+
opacity={wallElement.opacity}
|
|
23
|
+
transition={wallElement.transition}
|
|
24
|
+
tokens={wallElement.tokens}
|
|
25
|
+
tokensVisibleForce={wallElement.tokensVisible}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const mapWall = ({ uid = "", tokens = [], speaker = "", meet = 0, transition = 0, props } = {}) => {
|
|
31
|
+
const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.wall;
|
|
32
|
+
const speakerInfo = props.PARAMS.TEXT.SPEAKERS_INFO[speaker] || props.PARAMS.TEXT.SPEAKERS_INFO.default || {};
|
|
33
|
+
const { color, opacity = 1, names = {}, speakerColor = "white" } = speakerInfo;
|
|
34
|
+
|
|
35
|
+
const langNames = names[props.shared.lang] || names.default || [];
|
|
36
|
+
const speakerName = langNames[meet] || langNames[langNames.length - 1];
|
|
37
|
+
|
|
38
|
+
return { speakerName, speakerColor, uid, color, opacity, tokens, tokensVisible: tokens.length, transition, propsByView };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const InterfaceViewWall = (props = {}) => {
|
|
42
|
+
const { isShow = false, transition = 0, tokensVisible = 0, tokens = [], wall = [], uid = "", speakerName, speakerColor, color, opacity } = props;
|
|
43
|
+
|
|
44
|
+
const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.wall;
|
|
45
|
+
|
|
46
|
+
const propsPosition = useMemo(() => ({ ...propsByView.position, opacity: isShow ? 1 : 0, transition }), [isShow, transition]);
|
|
47
|
+
const wallWithProps = useMemo(() => wall.map((item) => ({ ...item, transition, props })), [wall, transition]);
|
|
48
|
+
|
|
49
|
+
const { realTokens, realTokensVisible } = useTokensHook(isShow, tokens, tokensVisible, uid);
|
|
50
|
+
|
|
51
|
+
const wallTexts = useMemo(() => wallWithProps.map(mapWall), [wallWithProps]);
|
|
52
|
+
const curText = useMemo(
|
|
53
|
+
() => ({ speakerName, speakerColor, uid, color, opacity, tokens: realTokens, tokensVisible: realTokensVisible, transition, propsByView }),
|
|
54
|
+
[uid, realTokens, realTokensVisible, transition],
|
|
55
|
+
);
|
|
56
|
+
const texts = useMemo(() => [...wallTexts, curText], [wallTexts, curText]);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<PositionBox {...propsPosition}>
|
|
60
|
+
<Scrollbar {...propsByView.scrollBar}>
|
|
61
|
+
<Flex {...propsByView.flex}>{texts.map(renderOneText)}</Flex>
|
|
62
|
+
</Scrollbar>
|
|
63
|
+
</PositionBox>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { flushSync } from "react-dom";
|
|
3
|
+
|
|
4
|
+
const EMPTY_ARR = [];
|
|
5
|
+
|
|
6
|
+
export const useTokensHook = (isShow, tokens, tokensVisible, uid) => {
|
|
7
|
+
const [savedUid, setSavedUid] = useState("");
|
|
8
|
+
const [tokensSync, setTokensSync] = useState(EMPTY_ARR);
|
|
9
|
+
const [tokensWrong, setTokensWrong] = useState(EMPTY_ARR);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (isShow) setTokensWrong(tokens);
|
|
13
|
+
}, [isShow]);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setImmediate(() => {
|
|
16
|
+
if (savedUid === uid) {
|
|
17
|
+
if (!isShow) setTokensWrong(tokens);
|
|
18
|
+
setTokensSync(tokens);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
flushSync(() => {
|
|
22
|
+
setSavedUid(uid);
|
|
23
|
+
if (!isShow) setTokensWrong(tokens);
|
|
24
|
+
setTokensSync(tokens);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}, [tokens]);
|
|
28
|
+
|
|
29
|
+
const isShowTokens = useMemo(() => tokensSync !== tokensWrong, [tokensSync, tokensWrong]);
|
|
30
|
+
const realTokensVisible = useMemo(() => (isShowTokens ? tokensVisible : 0), [isShowTokens, tokensVisible]);
|
|
31
|
+
const realTokens = useMemo(() => (isShowTokens ? tokensSync : EMPTY_ARR), [isShowTokens, tokensSync]);
|
|
32
|
+
|
|
33
|
+
return { isShowTokens, realTokensVisible, realTokens };
|
|
34
|
+
};
|
package/view/index.jsx
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
|
|
4
|
+
import { Screen, useIsForceHook } from "@vnejs/uis.react";
|
|
5
|
+
|
|
6
|
+
import { InterfaceBackdrop, InterfaceViewDialog, InterfaceViewLine, InterfaceViewWall } from "./components";
|
|
7
|
+
|
|
8
|
+
const EMPTY_ARRAY = [];
|
|
9
|
+
|
|
10
|
+
const Interface = ({ store, onMount, ...props } = {}) => {
|
|
11
|
+
const [state, setState] = useState({});
|
|
12
|
+
|
|
13
|
+
const { isShow = false, isVisible = false, isForce = false, tokensVisible = 0, view = "", locs = {} } = state;
|
|
14
|
+
const { speaker, meet = 0, tokens = EMPTY_ARRAY, wall = [], postfix = "", prefix = "", uid = "" } = state;
|
|
15
|
+
|
|
16
|
+
const onClick = useCallback(() => props.emit(props.EVENTS.INTERFACE.INTERACT), []);
|
|
17
|
+
|
|
18
|
+
useEffect(() => store.subscribe(setState), []);
|
|
19
|
+
useEffect(() => void onMount(), []);
|
|
20
|
+
|
|
21
|
+
const isRealForce = useIsForceHook(isForce);
|
|
22
|
+
|
|
23
|
+
const speakerName = locs[`speaker.${speaker}.${meet ?? 0}`];
|
|
24
|
+
const speakerText = speakerName ? `${`${locs[`prefix.${prefix}`] ?? ""} `}${speakerName}${` ${locs[`postfix.${postfix}`] ?? ""}`}` : ".";
|
|
25
|
+
const transition = isRealForce ? 0 : props.PARAMS.INTERFACE.TRANSITION;
|
|
26
|
+
const speakerInfo = props.PARAMS.TEXT.SPEAKERS_INFO[speaker] || props.PARAMS.TEXT.SPEAKERS_INFO[props.PARAMS.TEXT.DEFAULT_SPEAKER_NAME];
|
|
27
|
+
const { color, opacity = 1, speakerColor = "white" } = speakerInfo;
|
|
28
|
+
|
|
29
|
+
const propsCommon = useMemo(
|
|
30
|
+
() => ({ isForce: isRealForce, transition, tokensVisible, tokens, wall, uid }),
|
|
31
|
+
[isRealForce, transition, tokensVisible, tokens, wall, uid],
|
|
32
|
+
);
|
|
33
|
+
const propsSpeaker = useMemo(() => ({ speakerText, speakerName, speakerColor }), [speakerText, speakerName, speakerColor]);
|
|
34
|
+
const propsText = useMemo(() => ({ opacity, color }), [opacity, color]);
|
|
35
|
+
const propsView = useMemo(() => ({ ...propsCommon, ...propsSpeaker, ...propsText }), [propsCommon, propsSpeaker, propsText]);
|
|
36
|
+
|
|
37
|
+
const propsBackdrop = useMemo(() => ({ view, transition, ...props }), [view, transition]);
|
|
38
|
+
const propsDialog = useMemo(() => ({ isShow: view === "adv", ...propsView, ...props }), [view, propsView]);
|
|
39
|
+
const propsLine = useMemo(() => ({ isShow: view === "line", ...propsView, ...props }), [view, propsView]);
|
|
40
|
+
const propsWall = useMemo(() => ({ isShow: view === "wall", ...propsView, ...props }), [view, propsView]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Screen
|
|
44
|
+
isShow={isShow}
|
|
45
|
+
isForce={isRealForce}
|
|
46
|
+
isHidden={!isVisible}
|
|
47
|
+
isDisableAutoread={false}
|
|
48
|
+
isAllowAutoread={Boolean(isShow && isVisible)}
|
|
49
|
+
isIgnoreOnScreenshot={false}
|
|
50
|
+
transition={transition}
|
|
51
|
+
zIndex={props.PARAMS.INTERFACE.ZINDEX}
|
|
52
|
+
onClick={onClick}
|
|
53
|
+
>
|
|
54
|
+
<InterfaceBackdrop {...propsBackdrop} />
|
|
55
|
+
<InterfaceViewDialog {...propsDialog} />
|
|
56
|
+
<InterfaceViewLine {...propsLine} />
|
|
57
|
+
<InterfaceViewWall {...propsWall} />
|
|
58
|
+
</Screen>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const render = (props, root) => createRoot(root).render(<Interface {...props} />);
|