Add clip list component

This commit is contained in:
MrKBear 2022-04-30 13:59:22 +08:00
parent 5a3ec7d2af
commit 41fe51ec9c
10 changed files with 244 additions and 7 deletions

View File

@ -0,0 +1,131 @@
@import "../Theme/Theme.scss";
$clip-item-height: 45px;
div.clip-list-root {
margin: -5px;
display: flex;
flex-wrap: wrap;
div.clip-item {
margin: 5px;
height: $clip-item-height;
user-select: none;
border-radius: 3px;
overflow: hidden;
cursor: pointer;
display: flex;
div.clip-item-hole-view {
height: 100%;
width: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-items: center;
justify-content: space-between;
padding: 5px;
padding-right: 0;
div.clip-item-hole {
width: 5px;
height: 5px;
background-color: #000000;
flex-shrink: 0;
}
}
div.clip-icon-view {
width: $clip-item-height;
height: $clip-item-height;
display: flex;
justify-content: center;
align-items: center;
i.icon {
display: inline-block;
font-size: 25px;
}
i.delete {
display: none;
}
i.delete:hover {
color: $lt-red;
}
}
div.clip-item-content {
height: $clip-item-height;
display: flex;
flex-direction: column;
justify-content: center;
}
}
div.clip-item:hover {
div.clip-icon-view {
i.icon {
display: none;
}
i.delete {
display: inline-block;
}
}
}
div.add-button {
width: 26px;
height: 26px;
display: flex;
justify-content: center;
align-items: center;
}
}
div.dark.clip-list-root {
div.clip-item {
background-color: $lt-bg-color-lvl3-dark;
div.clip-item-hole-view div.clip-item-hole {
background-color: $lt-bg-color-lvl4-dark;
}
}
div.clip-item:hover {
color: $lt-font-color-lvl2-dark;
background-color: $lt-bg-color-lvl2-dark;
}
div.clip-item.focus {
color: $lt-font-color-lvl1-dark;
background-color: $lt-bg-color-lvl1-dark;
}
}
div.light.clip-list-root {
div.clip-item {
background-color: $lt-bg-color-lvl3-light;
div.clip-item-hole-view div.clip-item-hole {
background-color: $lt-bg-color-lvl4-light;
}
}
div.clip-item:hover {
color: $lt-font-color-lvl2-light;
background-color: $lt-bg-color-lvl2-light;
}
div.clip-item.focus {
color: $lt-font-color-lvl1-light;
background-color: $lt-bg-color-lvl1-light;
}
}

View File

@ -0,0 +1,61 @@
import { Theme } from "@Component/Theme/Theme";
import { Icon } from "@fluentui/react";
import { Clip } from "@Model/Clip";
import { Component, ReactNode } from "react";
import "./ClipList.scss";
interface IClipListProps {
clips: Clip[];
add?: () => any;
delete?: (clip: Clip) => any;
}
class ClipList extends Component<IClipListProps> {
private renderClip(clip: Clip) {
return <div
key={clip.id}
className="clip-item"
>
<div className="clip-item-hole-view">
{new Array(4).fill(0).map(() => {
return <div className="clip-item-hole"/>
})}
</div>
<div className="clip-icon-view">
<Icon iconName="MyMoviesTV" className="icon"/>
<Icon
iconName="Delete"
className="delete"
onClick={() => {
this.props.delete && this.props.delete(clip);
}}
/>
</div>
<div className="clip-item-content">
<div className="title">{clip.name}</div>
<div className="info">{clip.frames.length}</div>
</div>
</div>;
}
private renderAddButton(): ReactNode {
return <div
className="clip-item add-button"
onClick={this.props.add}
>
<Icon iconName="Add"/>
</div>
}
public render(): ReactNode {
return <Theme className="clip-list-root">
{ this.props.clips.map((clip => {
return this.renderClip(clip);
})) }
{ this.renderAddButton() }
</Theme>;
}
}
export { ClipList };

View File

@ -3,7 +3,7 @@
div.recorder-root { div.recorder-root {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
padding: 10px; padding: 10px 10px 0 10px;
div.recorder-slider { div.recorder-slider {
width: 100%; width: 100%;

View File

@ -62,7 +62,7 @@ class Recorder extends Component<IRecorderProps> {
if (this.props.running) { if (this.props.running) {
return "Stop"; return "Stop";
} else { } else {
return "CircleStop"; return "StatusCircleRing";
} }
} }
return "Play"; return "Play";

View File

@ -424,6 +424,22 @@ class Status extends Emitter<IStatusEvent> {
return label; return label;
} }
public newClip() {
let searchKey = I18N(this.setting.language, "Object.List.New.Clip", { id: "" });
let nextIndex = 1;
this.model.clipPool.forEach((obj) => {
nextIndex = Math.max(nextIndex, this.getNextNumber(
obj.name, searchKey
));
});
const clip = this.model.addClip(
I18N(this.setting.language, "Object.List.New.Clip", {
id: nextIndex.toString()
})
);
return clip;
}
public setMouseMod(mod: MouseMod) { public setMouseMod(mod: MouseMod) {
this.mouseMod = mod; this.mouseMod = mod;
if (this.renderer instanceof ClassicRenderer) { if (this.renderer instanceof ClassicRenderer) {

View File

@ -31,6 +31,7 @@ const EN_US = {
"Object.List.New.Group": "Group object {id}", "Object.List.New.Group": "Group object {id}",
"Object.List.New.Range": "Range object {id}", "Object.List.New.Range": "Range object {id}",
"Object.List.New.Label": "Label {id}", "Object.List.New.Label": "Label {id}",
"Object.List.New.Clip": "Clip {id}",
"Object.List.No.Data": "There are no objects in the model, click the button to create it", "Object.List.No.Data": "There are no objects in the model, click the button to create it",
"Object.Picker.List.No.Data": "There is no model in the model for this option", "Object.Picker.List.No.Data": "There is no model in the model for this option",
"Behavior.Picker.Add.Button": "Click here to assign behavior to this group", "Behavior.Picker.Add.Button": "Click here to assign behavior to this group",
@ -148,6 +149,7 @@ const EN_US = {
"Panel.Info.Behavior.Details.Parameter.Key.Vec.X": "{key} X", "Panel.Info.Behavior.Details.Parameter.Key.Vec.X": "{key} X",
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y",
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z",
"Panel.Info.Clip.List.Error.Nodata": "There is no clip, please click the record button to record, or click the plus sign to create",
"Info.Hint.Save.After.Close": "Any unsaved progress will be lost. Are you sure you want to continue?", "Info.Hint.Save.After.Close": "Any unsaved progress will be lost. Are you sure you want to continue?",
"Info.Hint.Load.File.Title": "Load save", "Info.Hint.Load.File.Title": "Load save",
"Info.Hint.Load.File.Intro": "Release to load the dragged save file", "Info.Hint.Load.File.Intro": "Release to load the dragged save file",

View File

@ -31,6 +31,7 @@ const ZH_CN = {
"Object.List.New.Group": "群对象 {id}", "Object.List.New.Group": "群对象 {id}",
"Object.List.New.Range": "范围对象 {id}", "Object.List.New.Range": "范围对象 {id}",
"Object.List.New.Label": "标签 {id}", "Object.List.New.Label": "标签 {id}",
"Object.List.New.Clip": "剪辑片段 {id}",
"Object.List.No.Data": "模型中没有任何对象,点击按钮以创建", "Object.List.No.Data": "模型中没有任何对象,点击按钮以创建",
"Object.Picker.List.No.Data": "模型中没有合适此选项的模型", "Object.Picker.List.No.Data": "模型中没有合适此选项的模型",
"Behavior.Picker.Add.Button": "点击此处以赋予行为到此群", "Behavior.Picker.Add.Button": "点击此处以赋予行为到此群",
@ -148,6 +149,7 @@ const ZH_CN = {
"Panel.Info.Behavior.Details.Parameter.Key.Vec.X": "{key} X 坐标", "Panel.Info.Behavior.Details.Parameter.Key.Vec.X": "{key} X 坐标",
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y 坐标", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y 坐标",
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标",
"Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建",
"Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?", "Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?",
"Info.Hint.Load.File.Title": "加载存档", "Info.Hint.Load.File.Title": "加载存档",
"Info.Hint.Load.File.Intro": "释放以加载拽入的存档", "Info.Hint.Load.File.Intro": "释放以加载拽入的存档",

View File

@ -0,0 +1,6 @@
div.Clip-player-clip-list-root {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 10px;
}

View File

@ -1,12 +1,31 @@
import { Component, ReactNode } from "react"; import { Component, ReactNode } from "react";
import { ClipList } from "@Component/ClipList/ClipList";
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
import { Theme } from "@Component/Theme/Theme";
import { Message } from "@Input/Message/Message";
import { Clip } from "@Model/Clip";
import "./ClipPlayer.scss"; import "./ClipPlayer.scss";
class ClipPlayer extends Component { @useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange")
class ClipPlayer extends Component<IMixinStatusProps> {
private renderMessage(): ReactNode {
return <Message i18nKey="Panel.Info.Clip.List.Error.Nodata"/>;
}
private renderClipList(clipList: Clip[]): ReactNode {
return <ClipList
clips={clipList}
/>;
}
public render(): ReactNode { public render(): ReactNode {
return <> const clipList = this.props.status?.model.clipPool ?? [];
</>; return <Theme className="Clip-player-clip-list-root">
{ clipList.length > 0 ? null : this.renderMessage() }
{ this.renderClipList(clipList) }
</Theme>;
} }
} }

View File

@ -37,7 +37,7 @@ class ClipRecorder extends Component<IMixinStatusProps> {
if (mod === "R" && !runner) { if (mod === "R" && !runner) {
// 获取新实例 // 获取新实例
let newClip = this.props.status?.model.addClip(); let newClip = this.props.status?.newClip();
// 开启录制时钟 // 开启录制时钟
this.props.status?.actuator.startRecord(newClip!); this.props.status?.actuator.startRecord(newClip!);