diff --git a/package.json b/package.json index d9821b911..7dff397fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webgal-terre", - "version": "4.4.10", + "version": "4.4.11", "private": true, "scripts": { "dev": "concurrently \"yarn dev:terre\" \"yarn dev:origine\" \"yarn dev:start-dev-server\"", diff --git a/packages/origine2/package.json b/packages/origine2/package.json index 8da4b1661..bbde44928 100644 --- a/packages/origine2/package.json +++ b/packages/origine2/package.json @@ -1,7 +1,7 @@ { "name": "webgal-origine-2", "private": true, - "version": "4.4.10", + "version": "4.4.11", "license": "MPL-2.0", "scripts": { "dev": "vite --host", @@ -19,6 +19,7 @@ "@icon-park/react": "^1.4.2", "@monaco-editor/react": "^4.4.5", "@reduxjs/toolkit": "^1.8.1", + "@uiw/react-json-view": "^2.0.0-alpha.12", "axios": "^1.6.0", "cloudlogjs": "^1.0.11", "i18next": "^22.4.15", diff --git a/packages/origine2/public/fonts/JetBrainsMono-Regular.ttf b/packages/origine2/public/fonts/JetBrainsMono-Regular.ttf new file mode 100644 index 000000000..dff66cc50 Binary files /dev/null and b/packages/origine2/public/fonts/JetBrainsMono-Regular.ttf differ diff --git a/packages/origine2/src/App.css b/packages/origine2/src/App.css index b5c090842..dc77a040f 100644 --- a/packages/origine2/src/App.css +++ b/packages/origine2/src/App.css @@ -2,7 +2,6 @@ background-color: var(--bg-root); width: 100%; height: 100%; - text-align: center; } .App-logo { diff --git a/packages/origine2/src/App.tsx b/packages/origine2/src/App.tsx index 6ce7071af..db31f8285 100644 --- a/packages/origine2/src/App.tsx +++ b/packages/origine2/src/App.tsx @@ -13,6 +13,7 @@ import Translation from "./components/translation/Translation"; import {lspSceneName} from "@/runtime/WG_ORIGINE_RUNTIME"; import './config/themes/theme.css'; import {PersistGate} from 'redux-persist/integration/react'; +import './assets/font-family.css'; function App() { useEffect(() => { diff --git a/packages/origine2/src/assets/font-family.css b/packages/origine2/src/assets/font-family.css new file mode 100644 index 000000000..00c5365a5 --- /dev/null +++ b/packages/origine2/src/assets/font-family.css @@ -0,0 +1,5 @@ +@font-face { + font-family: 'JetBrains Mono'; /* 自定义的字体名称 */ + /*noinspection CssUnknownTarget*/ + src: url('fonts/JetBrainsMono-Regular.ttf') format('truetype'); /* 字体文件的路径和格式 */ +} diff --git a/packages/origine2/src/components/terreToggle/TerreToggle.tsx b/packages/origine2/src/components/terreToggle/TerreToggle.tsx index 9c9ff0336..3e5819ac3 100644 --- a/packages/origine2/src/components/terreToggle/TerreToggle.tsx +++ b/packages/origine2/src/components/terreToggle/TerreToggle.tsx @@ -1,4 +1,4 @@ -import { Toggle } from "@fluentui/react"; +import { Switch } from "@fluentui/react-components"; interface ITerreToggle{ title:string, @@ -9,8 +9,13 @@ interface ITerreToggle{ } export default function TerreToggle(props:ITerreToggle){ - return props.onChange(checked ?? false)} />; + return ( +
+ props.onChange(data.checked ?? false)} + /> + {props.isChecked ? props.onText : props.offText} +
+ ); } diff --git a/packages/origine2/src/config/info.ts b/packages/origine2/src/config/info.ts index fcd92bc57..7aad81afb 100644 --- a/packages/origine2/src/config/info.ts +++ b/packages/origine2/src/config/info.ts @@ -4,6 +4,6 @@ export interface Info { } export const __INFO: Info = { - version: '4.4.10', + version: '4.4.11', buildTime: '2023-12-30T03:22:49.651Z', // 编译时会通过 version-sync.js 自动更新 }; diff --git a/packages/origine2/src/pages/dashboard/About.tsx b/packages/origine2/src/pages/dashboard/About.tsx index 2f5faeaa8..e754b8b26 100644 --- a/packages/origine2/src/pages/dashboard/About.tsx +++ b/packages/origine2/src/pages/dashboard/About.tsx @@ -92,7 +92,7 @@ const About: React.FunctionComponent = () => {


- Powered by WebGAL Framework + Powered by WebGAL Framework
Made with ❤ by Mahiru diff --git a/packages/origine2/src/pages/editor/ChooseFile/ChooseFile.tsx b/packages/origine2/src/pages/editor/ChooseFile/ChooseFile.tsx index 96a700aa5..a8938dd82 100644 --- a/packages/origine2/src/pages/editor/ChooseFile/ChooseFile.tsx +++ b/packages/origine2/src/pages/editor/ChooseFile/ChooseFile.tsx @@ -3,11 +3,10 @@ import { useEffect, useMemo } from "react"; import axios from "axios"; import { useSelector } from "react-redux"; import { RootState } from "../../../store/origineStore"; -import { useId } from "@fluentui/react-hooks"; -import { Callout, DefaultButton, TextField } from "@fluentui/react"; import styles from "./chooseFile.module.scss"; import { FolderOpen, FolderWithdrawal, Notes } from "@icon-park/react"; import useTrans from "@/hooks/useTrans"; +import { Button, Input, Popover, PopoverSurface, PopoverTrigger } from "@fluentui/react-components"; export interface IChooseFile { sourceBase: string; @@ -47,7 +46,6 @@ export default function ChooseFile(props: IChooseFile) { }, [currentDirName]); const isShowChooseFileCallout = useValue(false); - const buttonId = useId("choosefile-callout"); function toggleIsCalloutVisible() { updateFileList(); @@ -91,27 +89,28 @@ export default function ChooseFile(props: IChooseFile) { props.onChange(null); } - return <> - - {isShowChooseFileCallout.value && ( - -
-
+ return ( + + + + + +
+
{t('choose')}
- fileSearch.set(newValue || '')}/> + fileSearch.set(data.value || '')} + style={{width: '100%'}} + />
{currentChildDir.value.length > 0 && ( @@ -123,9 +122,9 @@ export default function ChooseFile(props: IChooseFile) { {fileSelectButtonList}
- - )} - ; +
+
+ ); } /** diff --git a/packages/origine2/src/pages/editor/ChooseFile/chooseFile.module.scss b/packages/origine2/src/pages/editor/ChooseFile/chooseFile.module.scss index 508ee2fbc..3697c57f7 100644 --- a/packages/origine2/src/pages/editor/ChooseFile/chooseFile.module.scss +++ b/packages/origine2/src/pages/editor/ChooseFile/chooseFile.module.scss @@ -1,11 +1,4 @@ -.callout{ - padding: 10px 12px; - width: 320px; - max-width: 90%; - height: 30vh; -} - -.chooseFileCalloutTitle{ +.chooseFileTitle{ font-weight: bold; font-size: large; padding: 2px 0 2px 4px; @@ -21,10 +14,12 @@ border-bottom: var(--border-md); } -.chooseFileCalloutContentWarpper{ +.chooseFileContentWarpper{ display: flex; flex-flow: column; height: 100%; + max-height: 40vh; + gap: 8px; } .chooseFileFileListWarpper{ diff --git a/packages/origine2/src/pages/editor/EditorSidebar/EditorSidebar.tsx b/packages/origine2/src/pages/editor/EditorSidebar/EditorSidebar.tsx index 05f183b97..54c03c60e 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/EditorSidebar.tsx +++ b/packages/origine2/src/pages/editor/EditorSidebar/EditorSidebar.tsx @@ -6,8 +6,9 @@ import Assets from "./SidebarTags/Assets/Assets"; import Scenes from "./SidebarTags/Scenes/Scenes"; import React, { useEffect, useRef } from "react"; import useTrans from "@/hooks/useTrans"; -import { IconButton } from "@fluentui/react"; import {eventBus} from "@/utils/eventBus"; +import { ArrowClockwise24Filled, ArrowClockwise24Regular, bundleIcon, Open24Filled, Open24Regular } from "@fluentui/react-icons"; +import { Button } from "@fluentui/react-components"; let startX = 0; let prevXvalue = 0; @@ -15,6 +16,10 @@ let isMouseDown = false; export default function EditorSideBar() { const t = useTrans("editor.sideBar."); + + const ArrowClockwiseIcon = bundleIcon(ArrowClockwise24Filled, ArrowClockwise24Regular); + const OpenIcon = bundleIcon(Open24Filled, Open24Regular); + const state = useSelector((state: RootState) => state.status.editor); const ifRef = useRef(null); useEffect(() => { @@ -112,13 +117,15 @@ export default function EditorSideBar() { src={`/games/${state.currentEditingGame}`} />
- } title={t("preview.refresh")} onClick={refreshGame} /> - } title={t("preview.previewInNewTab")} onClick={() => window.open(`/games/${state.currentEditingGame}`, "_blank")} /> diff --git a/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/Assets.tsx b/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/Assets.tsx index f09baf1d9..3de2c2802 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/Assets.tsx +++ b/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/Assets.tsx @@ -1,4 +1,3 @@ -import styles from "../sidebarTags.module.scss"; import assetsStyles from "./assets.module.scss"; import axios from "axios"; import { origineStore, RootState } from "../../../../../store/origineStore"; @@ -8,8 +7,6 @@ import React, { useEffect, useState } from "react"; import { getFileList, IFileDescription } from "../../../ChooseFile/ChooseFile"; import { dirnameToDisplayNameMap, dirNameToExtNameMap } from "../../../ChooseFile/chooseFileConfig"; import { DeleteOne, Editor, FolderOpen, FolderPlus, LeftSmall, Upload } from "@icon-park/react"; -import { useId } from "@fluentui/react-hooks"; -import { Callout, PrimaryButton, Text, TextField } from "@fluentui/react"; import { ITag, statusActions } from "../../../../../store/statusReducer"; import { extractPathAfterPublic } from "../../../ResourceDisplay/ResourceDisplay"; import useTrans from "@/hooks/useTrans"; @@ -18,6 +15,7 @@ import { getDirIcon, getFileIcon } from "@/utils/getFileIcon"; import TagTitleWrapper from "@/components/TagTitleWrapper/TagTitleWrapper"; import { api } from "@/api"; import { RequestParams } from "@/api/Api"; +import { Button, Input, Popover, PopoverSurface, PopoverTrigger, Text } from "@fluentui/react-components"; export default function Assets() { const t = useTrans("editor.sideBar.assets."); @@ -27,7 +25,6 @@ export default function Assets() { */ const isShowUploadCallout = useValue(false); - const buttonId = useId("upload-button"); /** * 当前目录,以及包含文件 @@ -49,7 +46,6 @@ export default function Assets() { * 新建文件夹 */ const isShowMkdirCallout = useValue(false); - const mkdirButtonId = useId("mkdir-button"); const newDirName = useValue(""); const handleCreatNewDir = () => { axios.post("/api/manageGame/mkdir", { @@ -154,7 +150,6 @@ export default function Assets() { }); } - return (
{/* @@ -172,59 +167,57 @@ export default function Assets() {
{currentDirName !== "" && <> -
isShowUploadCallout.set(!isShowUploadCallout.value)}> - -
-
isShowMkdirCallout.set(!isShowMkdirCallout.value)}> - -
+ isShowUploadCallout.set(!isShowUploadCallout.value)} + > + +
+ +
+
+ + + {t("buttons.uploadAsset")} + + { + isShowUploadCallout.set(false); + refreshCurrentDir(); + }} targetDirectory={`public/games/${gameName}/game${currentDirName}`} + uploadUrl="/api/manageGame/uploadFiles" /> + +
+ + { + isShowMkdirCallout.set(!isShowMkdirCallout.value); + newDirName.set(""); + }} + > + +
+ +
+
+ + + {t("buttons.createNewFolder")} + +
+ { + newDirName.set(data.value ?? ""); + }} /> +
+ +
+
+
} - {isShowUploadCallout.value && ( - isShowUploadCallout.set(false)} - setInitialFocus - > - - {t("buttons.uploadAsset")} - - { - isShowUploadCallout.set(false); - refreshCurrentDir(); - }} targetDirectory={`public/games/${gameName}/game${currentDirName}`} - uploadUrl="/api/manageGame/uploadFiles" /> - - )} - {isShowMkdirCallout.value && ( - { - isShowMkdirCallout.set(false); - newDirName.set(""); - }} - setInitialFocus - > - - {t("buttons.createNewFolder")} - -
- { - newDirName.set(val ?? ""); - }} /> -
- {t("$common.create")} -
-
- )} +
open_assets()}> @@ -259,8 +252,6 @@ function CommonFileButton(props: IFileDescription & { const showConformDeleteCallout = useValue(false); const showRenameCallout = useValue(false); const newFileName = useValue(""); - const renameButtonId = useId("renameBtn"); - const deleteButtonId = useId("deleteBtn"); return
props.onClick()}> {!props.isDir && } @@ -268,70 +259,68 @@ function CommonFileButton(props: IFileDescription & {
{props.name}
- {props.showOptions && <> -
{ - e.stopPropagation(); - showRenameCallout.set(!showRenameCallout.value); - newFileName.set(props.name); - }} id={renameButtonId} className={assetsStyles.deleteButton} style={{ - display: showRenameCallout.value ? "block" : undefined - }}> - -
-
{ - e.stopPropagation(); - showConformDeleteCallout.set(!showConformDeleteCallout.value); - }} id={deleteButtonId} className={assetsStyles.deleteButton} style={{ - display: showRenameCallout.value ? "block" : undefined - }}> - -
- {showRenameCallout.value && { - showRenameCallout.set(false); + {props.showOptions && + <> + { newFileName.set(""); }} - setInitialFocus > - - {t("buttons.rename")} - -
- { - newFileName.set(val ?? ""); - }} /> -
- { - props.onRename(newFileName.value); - showRenameCallout.set(false); - }}>{t("buttons.rename")} -
-
} - {showConformDeleteCallout.value && { - showConformDeleteCallout.set(false); - }} - setInitialFocus + +
{ + e.stopPropagation(); + newFileName.set(props.name); + }} + className={assetsStyles.deleteButton} + style={{ display: showRenameCallout.value ? "block" : undefined }} + > + +
+
+ e.stopPropagation()}> + + {t("buttons.rename")} + +
+ { + newFileName.set(data.value ?? ""); + }} /> +
+ +
+
+ + - - {t("$common.delete")} - -
- { - props.onDelete(); - showConformDeleteCallout.set(false); - }}>{t("buttons.deleteSure")} -
-
} - } + +
e.stopPropagation()} + className={assetsStyles.deleteButton} + style={{ display: showRenameCallout.value ? "block" : undefined}} + > + +
+
+ e.stopPropagation()}> + + {t("$common.delete")} + +
+ +
+
+ + + }
; } diff --git a/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/assets.module.scss b/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/assets.module.scss index 5dea89ca9..b898d1f0b 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/assets.module.scss +++ b/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Assets/assets.module.scss @@ -78,11 +78,11 @@ margin: 0 4px 0 0; border-radius: var(--radius-md); transition: all 0.33s; - display: none; + visibility: hidden; } .commonFileButton:hover .deleteButton { - display: block; + visibility: visible; } .deleteButton:hover { diff --git a/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Scenes/Scenes.tsx b/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Scenes/Scenes.tsx index 654682ecd..49e32f47d 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Scenes/Scenes.tsx +++ b/packages/origine2/src/pages/editor/EditorSidebar/SidebarTags/Scenes/Scenes.tsx @@ -1,4 +1,3 @@ -import styles from "../sidebarTags.module.scss"; import {useValue} from "../../../../../hooks/useValue"; import {useEffect, useState} from "react"; import {useDispatch, useSelector} from "react-redux"; @@ -6,12 +5,12 @@ import {RootState} from "../../../../../store/origineStore"; import axios from "axios"; import {IFileInfo} from "webgal-terre-2/dist/Modules/webgal-fs/webgal-fs.service"; import FileElement from "../../sidebarComponents/FileElement"; -import {Callout, PrimaryButton, Text, TextField} from "@fluentui/react"; import {ITag, statusActions} from "../../../../../store/statusReducer"; import useTrans from "@/hooks/useTrans"; import TagTitleWrapper from "@/components/TagTitleWrapper/TagTitleWrapper"; import {Newlybuild} from "@icon-park/react"; import s from './sceneTab.module.scss'; +import { Button, Input, Popover, PopoverSurface, PopoverTrigger, Text } from "@fluentui/react-components"; export default function Scenes() { const t = useTrans('editor.sideBar.scenes.'); @@ -27,26 +26,25 @@ export default function Scenes() { // 处理新建场景的问题 const showCreateSceneCallout = useValue(false); const newSceneName = useValue(""); - const updateNewSceneName = (event: any) => { - const newValue = event.target.value; - newSceneName.set(newValue); - }; + const createNewScene = async () => { - const gameName = state.currentEditingGame; - const params = new URLSearchParams(); + if (newSceneName.value && newSceneName.value.length !==0){ + const gameName = state.currentEditingGame; + const params = new URLSearchParams(); - params.append("gameName", gameName); - params.append("sceneName", newSceneName.value); + params.append("gameName", gameName); + params.append("sceneName", newSceneName.value); - axios.post("/api/manageGame/createNewScene/", params) - .then(() => { - showCreateSceneCallout.set(false); - updateSceneListView(); - newSceneName.set(""); - }) - .catch(() => { - setErrorMessage(t('dialogs.create.sceneExisted')); - }); + axios.post("/api/manageGame/createNewScene/", params) + .then(() => { + showCreateSceneCallout.set(false); + updateSceneListView(); + newSceneName.set(""); + }) + .catch(() => { + setErrorMessage(t('dialogs.create.sceneExisted')); + }); + } }; // 请求场景文件的函数 @@ -110,48 +108,48 @@ export default function Scenes() { return (
- -
showCreateSceneCallout.set(!showCreateSceneCallout.value)} - > - - {t('dialogs.create.button')} -
- {showCreateSceneCallout.value && ( - { - showCreateSceneCallout.set(false); - }} - setInitialFocus - style={{width: "300px", padding: "5px 10px 5px 10px"}} + showCreateSceneCallout.set(!showCreateSceneCallout.value)} > - - {t('dialogs.create.title')} - - -
- -
-
- -
-
- )}}/> + +
+ + {t('dialogs.create.button')} +
+
+ + + {t('dialogs.create.title')} + +
+ newSceneName.set(e.target.value)} + placeholder={t('dialogs.create.text')} + onKeyDown={(event) => (event.key === "Enter") && createNewScene()} + /> +
+
+ +
+
+ + } + />
{showSceneList}
); diff --git a/packages/origine2/src/pages/editor/EditorSidebar/editorSidebar.module.scss b/packages/origine2/src/pages/editor/EditorSidebar/editorSidebar.module.scss index dc7d0ec9d..5fdfb133f 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/editorSidebar.module.scss +++ b/packages/origine2/src/pages/editor/EditorSidebar/editorSidebar.module.scss @@ -58,10 +58,6 @@ display: block; } -.gamePreviewButons>button:hover { - background: var(--bg-card-hover); -} - .preview_top_title_container { display: flex; align-items: center; diff --git a/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/FileElement.tsx b/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/FileElement.tsx index 7acc1f0fa..d67ff7715 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/FileElement.tsx +++ b/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/FileElement.tsx @@ -2,11 +2,10 @@ import { DeleteOne, Editor } from "@icon-park/react"; import { ReactElement } from "react"; import styles from "./sidebarComponents.module.scss"; import { useValue } from "../../../../hooks/useValue"; -import { Callout, DefaultButton, PrimaryButton, Text, TextField } from "@fluentui/react"; -import { useId } from "@fluentui/react-hooks"; import useTrans from "@/hooks/useTrans"; import documentLogo from "material-icon-theme/icons/document.svg"; import IconWrapper from "@/components/iconWrapper/IconWrapper"; +import { Button, Input, Popover, PopoverSurface, PopoverTrigger, Text } from "@fluentui/react-components"; export interface IFileElementProps { name: string; @@ -58,72 +57,64 @@ export default function FileElement(props: IFileElementProps) { const showDeleteCalllout = useValue(false); - const editNameButtonId = useId(`editNameButton`); - const deleteButtonId = useId("deleteButton"); - - // @ts-ignore - return
-
{icon}
-
{props.name}
-
{ - e.stopPropagation(); - switchEditNameCallout(); - }}> - - {showEditNameCallout.value && +
{icon}
+
{props.name}
+ - - {t("editName.title")} - -
- -
-
- -
-
} -
-
{ - e.stopPropagation(); - showDeleteCalllout.set(!showDeleteCalllout.value); - }}> - {!props?.undeletable && } - {!props?.undeletable && showDeleteCalllout.value && { - showDeleteCalllout.set(false); - }} - setInitialFocus - style={{ width: "300px", padding: "5px 10px 5px 10px" }} + +
e.stopPropagation()} + > + +
+
+ e.stopPropagation()}> + + {t("editName.title")} + +
+ +
+
+ +
+
+ + showDeleteCalllout.set(!showDeleteCalllout.value)} > - - {t({ key: "delete.text", format: { name: props.name } })} - -
- - { - showDeleteCalllout.set(false); - }} allowDisabledFocus /> -
-
} + + { + !props?.undeletable + ?
e.stopPropagation()} + > + +
+ :
+ } + + e.stopPropagation()}> + + {t({ key: "delete.text", format: { name: props.name } })} + +
+ + +
+
+
-
; + ); } diff --git a/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/sidebarComponents.module.scss b/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/sidebarComponents.module.scss index 8693b3033..25277a21f 100644 --- a/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/sidebarComponents.module.scss +++ b/packages/origine2/src/pages/editor/EditorSidebar/sidebarComponents/sidebarComponents.module.scss @@ -37,11 +37,11 @@ border-radius: var(--radius-md); transition: all 0.33s; cursor: pointer; - display: none; + visibility: hidden; } .fileElement:hover .fileElement_interactable_icon { - display: block; + visibility: visible; } .fileElement_interactable_icon:hover { diff --git a/packages/origine2/src/pages/editor/GraphicalEditor/GraphicalEditor.tsx b/packages/origine2/src/pages/editor/GraphicalEditor/GraphicalEditor.tsx index 5fee4f337..ce1f062e8 100644 --- a/packages/origine2/src/pages/editor/GraphicalEditor/GraphicalEditor.tsx +++ b/packages/origine2/src/pages/editor/GraphicalEditor/GraphicalEditor.tsx @@ -146,7 +146,7 @@ export default function GraphicalEditor(props: IGraphicalEditorProps) { const parsedScene = (sceneText.value === "" ? {sentenceList: []} : parseScene(sceneText.value)); return
-
+
{(provided, snapshot) => ( @@ -200,7 +200,7 @@ export default function GraphicalEditor(props: IGraphicalEditorProps) {
deleteOneSentence(i)}> -
{t("delete")} @@ -208,7 +208,7 @@ export default function GraphicalEditor(props: IGraphicalEditorProps) {
syncToIndex(i)}> -
{t("$执行到此句")} diff --git a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeBg.tsx b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeBg.tsx index 0b62ff13e..221ef3de0 100644 --- a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeBg.tsx +++ b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeBg.tsx @@ -8,9 +8,9 @@ import TerreToggle from "../../../../components/terreToggle/TerreToggle"; import useTrans from "@/hooks/useTrans"; import CommonTips from "@/pages/editor/GraphicalEditor/components/CommonTips"; import {EffectEditor} from "@/pages/editor/GraphicalEditor/components/EffectEditor"; -import {DefaultButton, PrimaryButton, TextField} from "@fluentui/react"; import {TerrePanel} from "@/pages/editor/GraphicalEditor/components/TerrePanel"; import {useExpand} from "@/hooks/useExpand"; +import { Button, Input } from "@fluentui/react-components"; export default function ChangeBg(props: ISentenceEditorProps) { const t = useTrans('editor.graphical.sentences.changeBg.'); @@ -74,9 +74,9 @@ export default function ChangeBg(props: ISentenceEditorProps) { /> } - { +
@@ -88,14 +88,18 @@ export default function ChangeBg(props: ISentenceEditorProps) { }}/>
- { - const newDuration = Number(newValue); - if (isNaN(newDuration) || newValue === '') + { + const newDuration = Number(data.value); + if (isNaN(newDuration) || data.value === '') duration.set(""); else duration.set(newDuration); - }} onBlur={submit}/> + }} + onBlur={submit} + />
diff --git a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeFigure.tsx b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeFigure.tsx index f398a342e..00ee16c29 100644 --- a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeFigure.tsx +++ b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/ChangeFigure.tsx @@ -1,32 +1,35 @@ import CommonOptions from "../components/CommonOption"; -import {ISentenceEditorProps} from "./index"; +import { ISentenceEditorProps } from "./index"; import styles from "./sentenceEditor.module.scss"; import ChooseFile from "../../ChooseFile/ChooseFile"; -import {useValue} from "../../../../hooks/useValue"; -import {getArgByKey} from "../utils/getArgByKey"; +import { useValue } from "../../../../hooks/useValue"; +import { getArgByKey } from "../utils/getArgByKey"; import TerreToggle from "../../../../components/terreToggle/TerreToggle"; -import {useEffect, useState} from "react"; -import {ActionButton, DefaultButton, Dropdown, Panel, PanelType, PrimaryButton, TextField} from "@fluentui/react"; +import { useEffect, useState } from "react"; import useTrans from "@/hooks/useTrans"; -import {EffectEditor} from "@/pages/editor/GraphicalEditor/components/EffectEditor"; +import { EffectEditor } from "@/pages/editor/GraphicalEditor/components/EffectEditor"; import CommonTips from "@/pages/editor/GraphicalEditor/components/CommonTips"; import axios from "axios"; -import {useSelector} from "react-redux"; -import {RootState} from "@/store/origineStore"; -import {TerrePanel} from "@/pages/editor/GraphicalEditor/components/TerrePanel"; -import {useExpand} from "@/hooks/useExpand"; +import { useSelector } from "react-redux"; +import { RootState } from "@/store/origineStore"; +import { TerrePanel } from "@/pages/editor/GraphicalEditor/components/TerrePanel"; +import { useExpand } from "@/hooks/useExpand"; +import { Button, Dropdown, Input, Option } from "@fluentui/react-components"; + +type FigurePosition = "" | "left" | "right"; +type AnimationFlag = "" | "on"; export default function ChangeFigure(props: ISentenceEditorProps) { const gameName = useSelector((state: RootState) => state.status.editor.currentEditingGame); const t = useTrans('editor.graphical.sentences.changeFigure.'); const isGoNext = useValue(!!getArgByKey(props.sentence, "next")); const figureFile = useValue(props.sentence.content); - const figurePosition = useValue<"left" | "" | "right">(""); + const figurePosition = useValue(""); const isNoFile = props.sentence.content === ""; const id = useValue(getArgByKey(props.sentence, "id").toString() ?? ""); const json = useValue(getArgByKey(props.sentence, 'transform') as string); const duration = useValue(getArgByKey(props.sentence, 'duration') as number); - const {updateExpandIndex} = useExpand(); + const { updateExpandIndex } = useExpand(); const mouthOpen = useValue(getArgByKey(props.sentence, "mouthOpen").toString() ?? ""); const mouthHalfOpen = useValue(getArgByKey(props.sentence, "mouthHalfOpen").toString() ?? ""); const mouthClose = useValue(getArgByKey(props.sentence, "mouthClose").toString() ?? ""); @@ -42,13 +45,24 @@ export default function ChangeFigure(props: ISentenceEditorProps) { getArgByKey(props.sentence, "expression").toString() ?? "" ); + const figurePositions = new Map([ + ["", t('options.position.options.center')], + ["left", t('options.position.options.left')], + ["right", t('options.position.options.right')] + ]); + + const animationFlags = new Map([ + ["", "OFF"], + ["on", "ON"], + ]); + useEffect(() => { if (figureFile.value.includes('json')) { console.log('loading JSON file to get motion and expression'); axios.get(`/games/${gameName}/game/figure/${figureFile.value}`).then(resp => { const data = resp.data; - if(data?.motions){ + if (data?.motions) { // 处理 motions const motions = Object.keys(data.motions); setL2dMotionsList(motions.sort((a, b) => a.localeCompare(b))); @@ -61,12 +75,12 @@ export default function ChangeFigure(props: ISentenceEditorProps) { } // 处理 v3 版本的 model - if(data?.['FileReferences']?.['Motions']){ + if (data?.['FileReferences']?.['Motions']) { const motions = Object.keys(data['FileReferences']['Motions']); setL2dMotionsList(motions.sort((a, b) => a.localeCompare(b))); } - if(data?.['FileReferences']?.['Expressions']){ + if (data?.['FileReferences']?.['Expressions']) { const expressions: string[] = data['FileReferences']['Expressions'].map((exp: { Name: string }) => exp.Name); setL2dExpressionsList(expressions.sort((a, b) => a.localeCompare(b))); } @@ -132,7 +146,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { } else figureFile.set("none"); submit(); - }} onText={t("options.hide.on")} offText={t("options.hide.off")} isChecked={isNoFile}/> + }} onText={t("options.hide.on")} offText={t("options.hide.off")} isChecked={isNoFile} /> {!isNoFile && @@ -142,7 +156,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { figureFile.set(fileDesc?.name ?? ""); submit(); }} - extName={[".png", ".jpg", ".webp", ".json"]}/> + extName={[".png", ".jpg", ".webp", ".json"]} /> } @@ -150,51 +164,52 @@ export default function ChangeFigure(props: ISentenceEditorProps) { isGoNext.set(newValue); submit(); }} onText={t('$editor.graphical.sentences.common.options.goNext.on')} - offText={t('$editor.graphical.sentences.common.options.goNext.off')} isChecked={isGoNext.value}/> + offText={t('$editor.graphical.sentences.common.options.goNext.off')} isChecked={isGoNext.value} /> {figureFile.value.includes('.json') && ( <> ({key: e, text: e}))} - onChange={(ev, newValue: any) => { - currentMotion.set(newValue.key); + value={currentMotion.value} + selectedOptions={[currentMotion.value]} + onOptionSelect={(ev, data) => { + data.optionValue && currentMotion.set(data.optionValue); submit(); }} - /> + style={{ minWidth: 0 }} + > + {l2dMotionsList.map(e => ())} + ({key: e, text: e}))} - onChange={(ev, newValue: any) => { - currentExpression.set(newValue.key); + value={currentExpression.value} + selectedOptions={[currentExpression.value]} + onOptionSelect={(ev, data) => { + data.optionValue && currentExpression.set(data.optionValue); submit(); }} - /> + style={{ minWidth: 0 }} + > + {l2dExpressionsList.map(e => ())} + )} { - figurePosition.set(newValue?.key?.toString() ?? ""); + value={figurePositions.get(figurePosition.value) ?? figurePosition.value} + selectedOptions={[figurePosition.value]} + onOptionSelect={(ev, data) => { + figurePosition.set(data.optionValue?.toString() as FigurePosition ?? ""); submit(); }} - /> + style={{ minWidth: 0 }} + > + {Array.from(figurePositions.entries()).map(([key, value]) => )} + - { +
+ text={t("$效果提示")} /> { json.set(newJson); submit(); - }}/> + }} />
- { - const newDuration = Number(newValue); - if (isNaN(newDuration) || newValue === '') + { + const newDuration = Number(data.value); + if (isNaN(newDuration) || data.value === '') duration.set(""); else duration.set(newDuration); - }} onBlur={submit}/> + }} onBlur={submit} />
@@ -244,16 +259,16 @@ export default function ChangeFigure(props: ISentenceEditorProps) { }}> { - animationFlag.set(newValue?.key?.toString() ?? ""); + value={animationFlags.get(animationFlag.value as AnimationFlag)} + selectedOptions={[animationFlag.value]} + onOptionSelect={(ev, data) => { + animationFlag.set(data.optionValue?.toString() ?? ""); submit(); }} - /> + style={{ minWidth: 0 }} + > + {Array.from(animationFlags.entries()).map(([key, value]) => )} + {animationFlag.value === "on" && @@ -263,7 +278,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { mouthOpen.set(fileDesc?.name ?? ""); submit(); }} - extName={[".png", ".jpg", ".webp"]}/> + extName={[".png", ".jpg", ".webp"]} /> } {animationFlag.value === "on" && @@ -274,7 +289,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { mouthHalfOpen.set(fileDesc?.name ?? ""); submit(); }} - extName={[".png", ".jpg", ".webp"]}/> + extName={[".png", ".jpg", ".webp"]} /> } {animationFlag.value === "on" && @@ -285,7 +300,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { mouthClose.set(fileDesc?.name ?? ""); submit(); }} - extName={[".png", ".jpg", ".webp"]}/> + extName={[".png", ".jpg", ".webp"]} /> } {animationFlag.value === "on" && @@ -295,7 +310,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { eyesOpen.set(fileDesc?.name ?? ""); submit(); }} - extName={[".png", ".jpg", ".webp"]}/> + extName={[".png", ".jpg", ".webp"]} /> } {animationFlag.value === "on" && @@ -305,7 +320,7 @@ export default function ChangeFigure(props: ISentenceEditorProps) { eyesClose.set(fileDesc?.name ?? ""); submit(); }} - extName={[".png", ".jpg", ".webp"]}/> + extName={[".png", ".jpg", ".webp"]} /> }
diff --git a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Choose.tsx b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Choose.tsx index fc07fd67b..5323e27f6 100644 --- a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Choose.tsx +++ b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Choose.tsx @@ -3,8 +3,8 @@ import styles from "./sentenceEditor.module.scss"; import { useValue } from "../../../../hooks/useValue"; import { cloneDeep } from "lodash"; import ChooseFile from "../../ChooseFile/ChooseFile"; -import { DefaultButton } from "@fluentui/react"; import useTrans from "@/hooks/useTrans"; +import { Button } from "@fluentui/react-components"; export default function Choose(props: ISentenceEditorProps) { const t = useTrans('editor.graphical.sentences.choose.'); @@ -18,12 +18,16 @@ export default function Choose(props: ISentenceEditorProps) { const chooseList = chooseItems.value.map((item, i) => { return
- { - const newList = cloneDeep(chooseItems.value); - newList.splice(i,1); - chooseItems.set(newList); - submit(); - }}>{t('delete')} + { const newValue = ev.target.value; @@ -50,11 +54,14 @@ export default function Choose(props: ISentenceEditorProps) { }); return
{chooseList} - { - const newList = cloneDeep(chooseItems.value); - newList.push(t('option.option', 'option.chooseFile')); - chooseItems.set(newList); - submit(); - }}>{t('add')} +
; } diff --git a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Intro.tsx b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Intro.tsx index c04beae32..c2cae9eeb 100644 --- a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Intro.tsx +++ b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/Intro.tsx @@ -1,16 +1,21 @@ import CommonOptions from "../components/CommonOption"; -import {ISentenceEditorProps} from "./index"; +import { ISentenceEditorProps } from "./index"; import styles from "./sentenceEditor.module.scss"; -import {useValue} from "../../../../hooks/useValue"; -import {cloneDeep} from "lodash"; -import {DefaultButton, PrimaryButton, Dropdown, ColorPicker, IColor, Toggle} from "@fluentui/react"; +import { useValue } from "../../../../hooks/useValue"; +import { cloneDeep } from "lodash"; +import { ColorPicker, IColor } from "@fluentui/react"; import useTrans from "@/hooks/useTrans"; -import {getArgByKey} from "../utils/getArgByKey"; -import {useState} from "react"; +import { getArgByKey } from "../utils/getArgByKey"; +import { useState } from "react"; import React from 'react'; -import {logger} from "@/utils/logger"; -import {TerrePanel} from "../components/TerrePanel"; -import {useExpand} from "@/hooks/useExpand"; +import { logger } from "@/utils/logger"; +import { TerrePanel } from "../components/TerrePanel"; +import { useExpand } from "@/hooks/useExpand"; +import { Button, Dropdown, Option, Switch } from "@fluentui/react-components"; + +type FontSize = "small" | "medium" | "large"; +type Animation = "fadeIn" | "slideIn" | "typingEffect" | "pixelateEffect" | "revealAnimation"; +type DelayTime = 1500 | 2000 | 2500 | 3000 | 3500 | 4000 | 4500 | 5000; export default function Intro(props: ISentenceEditorProps) { const t = useTrans('editor.graphical.sentences.intro.options.'); @@ -88,62 +93,60 @@ export default function Intro(props: ISentenceEditorProps) { return initialFontColor; }; + const fontSizes = new Map([ + ["small", "small"], + ["medium", "medium"], + ["large", "large"], + ]); + + const animations = new Map([ + ["fadeIn", "fadeIn"], + ["slideIn", "slideIn"], + ["typingEffect", "typingEffect"], + ["pixelateEffect", "pixelateEffect"], + ["revealAnimation", "revealAnimation"], + ]); + + const delayTimes = new Map([ + [1500, "1.5"], + [2000, "2"], + [2500, "2.5"], + [3000, "3"], + [3500, "3.5"], + [4000, "4"], + [4500, "4.5"], + [5000, "5"], + ]); + const getInitialFontSize = (): string => { const fontSizeValue = getArgByKey(props.sentence, "fontSize"); - if (typeof fontSizeValue === 'string' && ["small", "medium", "large"].includes(fontSizeValue)) { + if (typeof fontSizeValue === 'string' && Array.from(fontSizes.keys()).includes(fontSizeValue as FontSize)) { return fontSizeValue; } return "medium"; }; - const getInitialAnimation = (): string => { const animationValue = getArgByKey(props.sentence, "animation"); - if (typeof animationValue === 'string' && ["fadeIn", "slideIn", "typingEffect", "pixelateEffect", "revealAnimation"].includes(animationValue)) { + if (typeof animationValue === 'string' && Array.from(animations.keys()).includes(animationValue as Animation)) { return animationValue; } return "fadeIn"; }; - const getInitialDelayTime = (): string => { const delayTimeValue = getArgByKey(props.sentence, "delayTime"); - if (typeof delayTimeValue === 'number' && [1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000].includes(delayTimeValue)) { + if (typeof delayTimeValue === 'number' && Array.from(delayTimes.keys()).includes(delayTimeValue as DelayTime)) { return delayTimeValue.toString(); } return "1500"; }; - const fontSizes = [ - {key: "small", text: "small"}, - {key: "medium", text: "medium"}, - {key: "large", text: "large"}, - ]; - - const animations = [ - {key: "fadeIn", text: "fadeIn"}, - {key: "slideIn", text: "slideIn"}, - {key: "typingEffect", text: "typingEffect"}, - {key: "pixelateEffect", text: "pixelateEffect"}, - {key: "revealAnimation", text: "revealAnimation"}, - ]; - - const delayTimes = [ - {key: "1500", text: "1.5"}, - {key: "2000", text: "2"}, - {key: "2500", text: "2.5"}, - {key: "3000", text: "3"}, - {key: "3500", text: "3.5"}, - {key: "4000", text: "4"}, - {key: "4500", text: "4.5"}, - {key: "5000", text: "5"}, - ]; - const backgroundColor = useValue(getBackgroundColor()); const fontColor = useValue(getFontColor()); const fontSize = useValue(getInitialFontSize()); @@ -151,13 +154,7 @@ export default function Intro(props: ISentenceEditorProps) { const delayTime = useValue(getInitialDelayTime()); const [localBackgroundColor, setLocalBackgroundColor] = useState(backgroundColor.value); const [localFontColor, setLocalFontColor] = useState(fontColor.value); - const {updateExpandIndex} = useExpand(); - const optionButtonStyles = { - root: { - // margin: '6px 0 0 0', - display: 'flex' - }, - }; + const { updateExpandIndex } = useExpand(); const handleLocalBackgroundColorChange = (ev: React.SyntheticEvent, newColor: IColor) => { setLocalBackgroundColor(newColor); @@ -185,7 +182,18 @@ export default function Intro(props: ISentenceEditorProps) { }; const introCompList = introTextList.value.map((text, index) => { - return
+ return
+ +
{ const newValue = ev.target.value; @@ -196,75 +204,77 @@ export default function Intro(props: ISentenceEditorProps) { onBlur={submit} className={styles.sayInput} placeholder={t('value.placeholder')} - style={{width: "100%"}} + style={{ width: "100%" }} /> -
- { - const newList = cloneDeep(introTextList.value); - newList.splice(index, 1); - introTextList.set(newList); - submit(); - }}>{t('$common.delete')}
; }); - return
-
+ return
+
{introCompList}
- { + + -
-
+
+
({key: f.key, text: f.text}))} - selectedKey={fontSize.value} - onChange={(event, item) => { - item && fontSize.set(item.key as string); + value={fontSizes.get(fontSize.value as FontSize) ?? fontSize.value} + selectedOptions={[fontSize.value]} + onOptionSelect={(event, data) => { + data.optionValue && fontSize.set(data.optionValue as string); submit(); }} - /> + style={{ minWidth: 0 }} + > + {Array.from(fontSizes.entries()).map(([key, value]) => )} + ({key: f.key, text: f.text}))} - onChange={(ev, newValue: any) => { - animation.set(newValue?.key?.toString() ?? ""); + value={animations.get(animation.value as Animation) ?? animation.value} + selectedOptions={[animation.value]} + onOptionSelect={(ev, data) => { + data.optionValue && animation.set(data.optionValue.toString() ?? ""); submit(); }} - /> + style={{ minWidth: 0 }} + > + {Array.from(animations.entries()).map(([key, value]) => )} + ({key: f.key, text: f.text}))} - onChange={(ev, newValue: any) => { - delayTime.set(newValue?.key?.toString() ?? ""); + value={delayTimes.get(Number(delayTime.value) as DelayTime) ?? delayTime.value} + selectedOptions={[delayTime.value]} + onOptionSelect={(ev, data) => { + data.optionValue && delayTime.set(data.optionValue.toString() ?? ""); submit(); }} - /> + style={{ minWidth: 0 }} + > + {Array.from(delayTimes.entries()).map(([key, value]) => )} + - { - isHold.set(newValue ?? false); + onChange={(ev, data) => { + isHold.set(data.checked ?? false); submit(); }} />
-
+
- {t('colorPicker.submit')} +
; diff --git a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/PixiPerform.tsx b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/PixiPerform.tsx index 0f45b1929..3f36281df 100644 --- a/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/PixiPerform.tsx +++ b/packages/origine2/src/pages/editor/GraphicalEditor/SentenceEditor/PixiPerform.tsx @@ -3,17 +3,22 @@ import { ISentenceEditorProps } from "./index"; import styles from "./sentenceEditor.module.scss"; import { useValue } from "../../../../hooks/useValue"; import TerreToggle from "../../../../components/terreToggle/TerreToggle"; -import { Dropdown } from "@fluentui/react"; import { commandType } from "webgal-parser/src/interface/sceneInterface"; import useTrans from "@/hooks/useTrans"; +import { Dropdown, Option } from "@fluentui/react-components"; export default function PixiPerform(props: ISentenceEditorProps) { const t = useTrans('editor.graphical.sentences.specialEffect.options.'); + const effects = new Map([ + ["snow", t('usePrepared.effects.snow')], + ["rain", t('usePrepared.effects.rain')], + ["cherryBlossoms", t('usePrepared.effects.cherryBlossoms')], + ]); + const isSetEffectsOff = useValue(props.sentence.command === commandType.pixiInit); const effectName = useValue(props.sentence.content); - const isUsePreset = useValue(["snow", "rain", "cherryBlossoms"].includes(effectName.value)); - + const isUsePreset = useValue(Array.from(effects.keys()).includes(effectName.value)); const submit = () => { if (isSetEffectsOff.value) { @@ -43,14 +48,16 @@ export default function PixiPerform(props: ISentenceEditorProps) { } {isUsePreset.value && { - effectName.set(newValue?.key?.toString() ?? ""); + value={effects.get(effectName.value) ?? effectName.value} + selectedOptions={[effectName.value]} + onOptionSelect={(ev, data) => { + effectName.set(data.optionValue?.toString() ?? ""); submit(); }} - styles={{ dropdown: { width: "160px" } }} - /> + style={{ minWidth: 0}} + > + { Array.from(effects.entries()).map(([key, value]) => ) } + } {!isUsePreset.value && !isSetEffectsOff.value && < CommonOptions title={t('useUser.title')} key="3"> (""); const figureId = useValue(getArgByKey(props.sentence, "figureId").toString() ?? ""); + const figurePosition = useValue(""); + const figurePositions = new Map([ + [ "", t('position.options.none') ] , + [ "left", t('position.options.left') ], + [ "center", t('position.options.center') ], + [ "right", t('position.options.right') ], + [ "id", t('position.options.id') ], + ]); + useEffect(() => { /** * 初始化立绘位置 @@ -38,24 +49,23 @@ export default function Say(props: ISentenceEditorProps) { } }, []); - const getInitialFontSize = (): string => { + const fontSizes = new Map([ + [ "default", t('font.options.default')], + [ "small", t('font.options.small') ], + [ "medium", t('font.options.medium') ], + [ "large", t('font.options.large') ], + ]); + const getInitialFontSize = (): FontSize => { const fontSizeValue = getArgByKey(props.sentence, "fontSize"); - if (typeof fontSizeValue === 'string' && ["default", "small", "medium", "large"].includes(fontSizeValue)) { - return fontSizeValue; + if (typeof fontSizeValue === 'string' && Array.from(fontSizes.keys()).includes(fontSizeValue as FontSize)) { + return fontSizeValue as FontSize; } return "default"; }; const fontSize = useValue(getInitialFontSize()); - const fontSizes = [ - { key: "default", text: t('font.options.default')}, - { key: "small", text: t('font.options.small') }, - { key: "medium", text: t('font.options.medium') }, - { key: "large", text: t('font.options.large') }, - ]; - const submit = () => { const selectedFontSize = fontSize.value; const pos = figurePosition.value !== "" ? ` -${figurePosition.value}` : ""; @@ -70,7 +80,7 @@ export default function Say(props: ISentenceEditorProps) { return
-
+
{ const newValue = ev.target.value; @@ -83,7 +93,7 @@ export default function Say(props: ISentenceEditorProps) { />
{currentValue.value.map((text, index) => ( -
+