Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
feat: basic plugin editor (#184)
Browse files Browse the repository at this point in the history
* wip: plugin editor

* working text editor, refactoring, better example widget

* get WAS positions working

* clean up nav

* refactor

* add javascript syntax highlighting

* add reset functionality

* add filetype to default

* update infobox name

* Changed monaco package, moved into molecules

* remove old package from webpack

* Camelcase component folder/name

* wip: fix asset modal

* update to videoOnly props

* Revert "update to videoOnly props"

This reverts commit 4f18b68.

* Revert "wip: fix asset modal"

This reverts commit 5e6ba05.

* convert plugin editor into page

* changes based on PR review

* remove primitive option

* lazy import plugin editor

Co-authored-by: rot1024 <aayhrot@gmail.com>
  • Loading branch information
KaWaite and rot1024 committed Apr 5, 2022
1 parent 83a6bf0 commit 1c4e090
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -154,6 +154,7 @@
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@monaco-editor/react": "^4.3.1",
"@popperjs/core": "^2.11.2",
"@reach/router": "^1.3.4",
"@rot1024/use-transition": "^1.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app.tsx
Expand Up @@ -27,6 +27,7 @@ import { Provider as ThemeProvider, styled } from "./theme";
const EarthEditor = React.lazy(() => import("@reearth/components/pages/EarthEditor"));
const Dashboard = React.lazy(() => import("@reearth/components/pages/Dashboard"));
const GraphQLPlayground = React.lazy(() => import("@reearth/components/pages/GraphQLPlayground"));
const PluginEditor = React.lazy(() => import("./components/pages/PluginEditor"));

const enableWhyDidYouRender = false;

Expand Down Expand Up @@ -64,6 +65,7 @@ const App: React.FC = () => {
<PublicSettings path="/settings/project/:projectId/public" />
<DatasetSettings path="/settings/project/:projectId/dataset" />
<PluginSettings path="/settings/project/:projectId/plugins" />
<PluginEditor path="/plugin-editor" />
{process.env.NODE_ENV !== "production" && <GraphQLPlayground path="/graphql" />}
<NotFound default />
</StyledRouter>
Expand Down
167 changes: 167 additions & 0 deletions src/components/molecules/PluginEditor/hooks.ts
@@ -0,0 +1,167 @@
import { useState, useMemo, ChangeEvent, useEffect, useCallback } from "react";

import type {
Widget,
WidgetAlignSystem,
WidgetZone,
WidgetSection,
} from "@reearth/components/molecules/Visualizer";

export type Position = { section: string; area: string };

const originalSourceCode = `
reearth.ui.show(
\`<style>
body {
margin: 0;
}
#wrapper {
background: #232226;
height: 100%;
color: white;
border: 3px dotted red;
border-radius: 5px;
padding: 20px 0;
}
</style>
<div id="wrapper">
<h2 style="text-align: center; margin: 0;">Hello2 World</h2>
</div>
\`
, { visible: true });
`.trim();

const defaultPosition = {
section: "left",
area: "top",
};

export default () => {
const [mode, setMode] = useState("widget");
const [showInfobox, setShowInfobox] = useState(false);
const [infoboxSize, setInfoboxSize] = useState<"small" | "medium" | "large">("small");
const [showAlignSystem, setShowAlignSystem] = useState(false);
const [currentPosition, setCurrentPosition] = useState<Position>(defaultPosition);
const [alignSystem, setAlignSystem] = useState<WidgetAlignSystem | undefined>();
const [sourceCode, setSourceCode] = useState<{ fileName?: string; body?: string }>({
fileName: "untitled.js",
body: originalSourceCode,
});
const [hardSourceCode, setHardSourceCode] = useState<{ fileName?: string; body: string }>({
fileName: "untitled.js",
body: originalSourceCode,
});

const positions: { [key: string]: Position }[] = [
{
LeftTop: { section: "left", area: "top" },
LeftMiddle: { section: "left", area: "middle" },
LeftBottom: { section: "left", area: "bottom" },
},
{
CenterTop: { section: "center", area: "top" },
CenterBottom: { section: "center", area: "bottom" },
},
{
RightTop: { section: "right", area: "top" },
RightMiddle: { section: "right", area: "middle" },
RightBottom: { section: "right", area: "bottom" },
},
];

const handleAlignSystemUpdate = (widget: Widget, newLoc: Position) => {
setAlignSystem(() => {
const alignment =
newLoc.section === "center" || newLoc.area === "middle" ? "centered" : "start";
const newSection: WidgetSection = {
top: {
widgets: newLoc.area === "top" ? [widget] : undefined,
align: alignment,
},
middle: {
widgets: newLoc.area === "middle" ? [widget] : undefined,
align: alignment,
},
bottom: {
widgets: newLoc.area === "bottom" ? [widget] : undefined,
align: alignment,
},
};

const newZone: WidgetZone = {
left: {
...(newLoc.section === "left" ? newSection : undefined),
},
center: {
...(newLoc.section === "center" ? newSection : undefined),
},
right: {
...(newLoc.section === "right" ? newSection : undefined),
},
};
setCurrentPosition(newLoc);
return { outer: newZone };
});
};
const handleAlignSystemToggle = () => {
setShowAlignSystem(!showAlignSystem);
};

const handleInfoboxToggle = () => {
setShowInfobox(!showInfobox);
};

const openFile = async (e: ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
const file = e.currentTarget.files?.[0];
if (!file) return;
const reader = new FileReader();

reader.onload = async e2 => {
const body = e2?.target?.result;
if (typeof body != "string") return;
setSourceCode({ fileName: file.name, body });
setHardSourceCode({ fileName: file.name, body });
};
reader.readAsText(file);
};

const widget = useMemo(() => {
return {
id: "xxx",
// extended: true,
__REEARTH_SOURCECODE: sourceCode.body,
};
}, [sourceCode.body]);

const reset = useCallback(() => {
if (confirm("Are you sure you want to reset?")) {
setSourceCode(hardSourceCode);
handleAlignSystemUpdate(widget, defaultPosition);
}
}, [widget, hardSourceCode]);

useEffect(() => {
handleAlignSystemUpdate(widget, currentPosition);
}, [widget]); // eslint-disable-line react-hooks/exhaustive-deps

return {
sourceCode,
widget,
currentPosition,
positions,
mode,
alignSystem,
infoboxSize,
showAlignSystem,
showInfobox,
handleAlignSystemToggle,
handleInfoboxToggle,
openFile,
handleAlignSystemUpdate,
setSourceCode,
setMode,
setInfoboxSize,
reset,
};
};

0 comments on commit 1c4e090

Please sign in to comment.