Skip to content

Commit

Permalink
change web code editor to use react-ace (#2060)
Browse files Browse the repository at this point in the history
- remove dependency on prismjs fork
  • Loading branch information
srliao authored Mar 15, 2024
1 parent 34f8d03 commit 59a644a
Show file tree
Hide file tree
Showing 6 changed files with 675 additions and 156 deletions.
3 changes: 2 additions & 1 deletion ui/packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@visx/text": "^3.0.0",
"@visx/threshold": "^3.0.0",
"@visx/tooltip": "^3.0.0",
"ace-builds": "^1.32.7",
"axios": "^1.2.1",
"classnames": "^2.3.2",
"fast-deep-equal": "^3.1.3",
Expand All @@ -37,9 +38,9 @@
"lodash-es": "^4.17.21",
"merge": "^2.1.1",
"pako": "^2.1.0",
"prismjs": "https://github.com/srliao/prism.git",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-helmet": "^6.1.0",
Expand Down
46 changes: 17 additions & 29 deletions ui/packages/ui/src/Pages/Simulator/Components/ActionList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import Editor from "react-simple-code-editor";
import "@fontsource/fira-mono";

//@ts-ignore
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-gcsim";

// import Prism from "prismjs";
import "prismjs/themes/prism-tomorrow.css";

// Prism.highlight("stuff",Prism.languages)
import AceEditor from "react-ace";
//THESE IMPORTS NEEDS TO BE AFTER IMPORTING AceEditor
import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/theme-tomorrow_night";
import "../../../util/mode-gcsim.js";

type Props = {
cfg: string;
Expand All @@ -21,27 +16,20 @@ export function ActionList(props: Props) {
// };
return (
<div className="p-1 md:p-2">
<Editor
<AceEditor
mode="gcsim"
theme="tomorrow_night"
width="100%"
onChange={props.onChange}
value={props.cfg}
onValueChange={(code) => props.onChange(code)}
textareaId="codeArea"
className="editor"
highlight={(code) =>
highlight(code, languages.gcsim)
.split("\n")
.map(
//@ts-ignore
(line, i) =>
`<span class='editorLineNumber'>${i + 1}</span>${line}`
)
.join("\n")
}
insertSpaces
padding={10}
style={{
fontFamily: '"Fira Mono", monospace',
name="UNIQUE_ID_OF_DIV"
editorProps={{
$blockScrolling: true,
}}
setOptions={{
maxLines: Infinity,
fontSize: 14,
backgroundColor: "rgb(45 45 45)",
tabSize: 2,
}}
/>
</div>
Expand Down
140 changes: 76 additions & 64 deletions ui/packages/ui/src/Pages/Simulator/Simulator.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import { Callout, Intent } from "@blueprintjs/core";
import { Executor, ExecutorSupplier } from "@gcsim/executors";
import { Character } from "@gcsim/types";
import { debounce } from "lodash-es";
import { useEffect, useRef, useState } from "react";
import { Viewport, SectionDivider } from "../../Components";
import { Trans, useTranslation } from "react-i18next";
import { SectionDivider, Viewport } from "../../Components";
import { appActions, defaultStats } from "../../Stores/appSlice";
import { RootState, useAppDispatch, useAppSelector } from "../../Stores/store";
import { ActionList } from "./Components";
import { OmnibarBlock } from "./Components/OmnibarBlock";
import { Team } from "./Team";
import { Trans, useTranslation } from "react-i18next";
import { Toolbox } from "./Toolbox";
import { ActionListTooltip, TeamBuilderTooltip } from "./Tooltips";
import { useAppSelector, RootState, useAppDispatch } from "../../Stores/store";
import { Executor, ExecutorSupplier } from "@gcsim/executors";
import { appActions, defaultStats } from "../../Stores/appSlice";
import { Character } from "@gcsim/types";
import { debounce } from "lodash-es";
import { OmnibarBlock } from "./Components/OmnibarBlock";

export function Simulator({ exec }: { exec: ExecutorSupplier<Executor> }) {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const { settings, cfg } = useAppSelector(
(state: RootState) => {
return {
cfg: state.app.cfg,
settings: state.user.data.settings,
};
}
);
const { settings, cfg } = useAppSelector((state: RootState) => {
return {
cfg: state.app.cfg,
settings: state.user.data.settings,
};
});

const onChange = (newCfg: string) => {
dispatch(appActions.setCfg({ cfg: newCfg, keepTeam: false }));
Expand All @@ -33,7 +31,9 @@ export function Simulator({ exec }: { exec: ExecutorSupplier<Executor> }) {
const [isReady, setReady] = useState<boolean | null>(null);
useEffect(() => {
const interval = setInterval(() => {
exec().ready().then(res => setReady(res))
exec()
.ready()
.then((res) => setReady(res));
}, 250);
return () => clearInterval(interval);
}, [exec]);
Expand Down Expand Up @@ -75,19 +75,26 @@ export function Simulator({ exec }: { exec: ExecutorSupplier<Executor> }) {

<ActionList cfg={cfg} onChange={onChange} />

<div className="sticky bottom-0 bg-bp4-dark-gray-100 flex flex-col gap-y-1">
<div className="sticky bottom-0 bg-bp4-dark-gray-100 flex flex-col gap-y-1 z-50">
{err !== "" && cfg !== "" ? (
<div className="pl-2 pr-2 pt-2 mt-1">
<Callout intent={Intent.DANGER} title={t<string>("viewer.error_encountered") + t<string>("viewer.config_invalid")}>
<Callout
intent={Intent.DANGER}
title={
t<string>("viewer.error_encountered") +
t<string>("viewer.config_invalid")
}
>
<pre className="whitespace-pre-wrap pl-5">{err}</pre>
</Callout>
</div>
) : null}
<Toolbox
exec={exec}
cfg={cfg}
isReady={isReady === true}
isValid={err === "" && validated} />
exec={exec}
cfg={cfg}
isReady={isReady === true}
isValid={err === "" && validated}
/>
</div>
</div>
</div>
Expand All @@ -96,8 +103,11 @@ export function Simulator({ exec }: { exec: ExecutorSupplier<Executor> }) {
}

export function useConfigValidateListener(
exec: ExecutorSupplier<Executor>, cfg: string, isReady: boolean | null,
setErr: (str: string) => void): boolean {
exec: ExecutorSupplier<Executor>,
cfg: string,
isReady: boolean | null,
setErr: (str: string) => void
): boolean {
const dispatch = useAppDispatch();
const [validated, setValidated] = useState(false);
const debounced = useRef(debounce((x: () => void) => x(), 200));
Expand All @@ -114,47 +124,49 @@ export function useConfigValidateListener(

setValidated(false);
debounced.current(() => {
exec().validate(cfg).then(
(res) => {
console.log("all is good");
setErr("");
//if successful then we're going to update the team based on the parsed results
let team: Character[] = [];
if (res.characters) {
team = res.characters.map((c) => {
return {
name: c.base.key,
level: c.base.level,
element: c.base.element,
max_level: c.base.max_level,
cons: c.base.cons,
weapon: c.weapon,
talents: c.talents,
stats: c.stats,
snapshot: defaultStats,
sets: c.sets,
};
});
exec()
.validate(cfg)
.then(
(res) => {
console.log("all is good");
setErr("");
//if successful then we're going to update the team based on the parsed results
let team: Character[] = [];
if (res.characters) {
team = res.characters.map((c) => {
return {
name: c.base.key,
level: c.base.level,
element: c.base.element,
max_level: c.base.max_level,
cons: c.base.cons,
weapon: c.weapon,
talents: c.talents,
stats: c.stats,
snapshot: defaultStats,
sets: c.sets,
};
});
}
//check if there are any warning msgs
if (res.errors) {
let msg = "";
res.errors.forEach((err) => {
msg += err + "\n";
});
setErr(msg);
}
dispatch(appActions.setTeam(team));
setValidated(true);
},
(err) => {
//set error state
setErr(err);
setValidated(false);
}
//check if there are any warning msgs
if (res.errors) {
let msg = "";
res.errors.forEach((err) => {
msg += err + "\n";
});
setErr(msg);
}
dispatch(appActions.setTeam(team));
setValidated(true);
},
(err) => {
//set error state
setErr(err);
setValidated(false);
}
);
);
});
}, [exec, cfg, dispatch, setErr, isReady]);

return validated;
}
}
Loading

0 comments on commit 59a644a

Please sign in to comment.