Skip to content

Commit

Permalink
refactor(plugin): Refactor Web App to use Typescript
Browse files Browse the repository at this point in the history
Dumb translation, using tricks to bypass the type-system, or
the linter.

E.g. `var anyWindow = (window as any)` to register any values
or function on the Window object. Or adding `// @ts-ignore`
where I'm not sure what to do.
  • Loading branch information
bric3 committed Jul 1, 2021
1 parent 72c4130 commit c37a3fa
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 79 deletions.
14 changes: 12 additions & 2 deletions excalidraw-assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@
"license": "MIT",
"dependencies": {
"@excalidraw/excalidraw": "^0.8.0",
"@types/jest": "^26.0.23",
"@types/node": "^15.12.5",
"@types/react": "^17.0.11",
"@types/react-dom": "^17.0.8",
"awesome-debounce-promise": "^2.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3"
"react-scripts": "4.0.3",
"typescript": "^4.3.4"
},
"devDependencies": {
"cross-env": "^7.0.2",
"gulp": "^4.0.2",
"gulp-inline-source-html": "^1.0.3",
"gulp-replace": "^1.0.0"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"scripts": {
"start": "BROWSER=none cross-env NODE_ENV=development react-scripts start",
"build": "BUILD_PATH=./build/react-build/ react-scripts build && npx gulp",
Expand All @@ -36,4 +46,4 @@
"last 1 safari version"
]
}
}
}
2 changes: 1 addition & 1 deletion excalidraw-assets/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<!--<script type="text/javascript" src="../src/index.jsx"></script>-->
<!--<script type="text/javascript" src="../src/index.tsx"></script>-->
</body>
</html>
128 changes: 54 additions & 74 deletions excalidraw-assets/src/App.jsx → excalidraw-assets/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ import React from "react";
import Excalidraw, {exportToBlob, exportToSvg, getSceneVersion, serializeAsJSON,} from "@excalidraw/excalidraw";
import AwesomeDebouncePromise from 'awesome-debounce-promise';

// hack to access the non typed window object (any) to add old school javscript
var anyWindow = (window as any);

// placeholder for functions
// window.loadBlob = null; // not supported yet
window.updateApp = null;
window.updateAppState = null;
window.saveAsJson = null;
window.saveAsSvg = null;
window.saveAsPng = null;
anyWindow.updateApp = null;
anyWindow.updateAppState = null;
anyWindow.saveAsJson = null;
anyWindow.saveAsSvg = null;
anyWindow.saveAsPng = null;

const defaultInitialData = {
gridMode: false,
zenMode: false,
theme: "light",
debounceAutoSaveInMs: 300
}
const initialData = window.initialData ? window.initialData : defaultInitialData;
const initialData = anyWindow.initialData ?? defaultInitialData;

let currentSceneVersion = getSceneVersion([]); // scene elements are empty on load

Expand All @@ -30,41 +33,41 @@ window.addEventListener("message", (e) => {
let updateSceneVersion = getSceneVersion(elements);
if (currentSceneVersion !== updateSceneVersion) {
currentSceneVersion = updateSceneVersion;
updateApp({
anyWindow.updateApp({
elements: elements
});
}
break;
}

case "toggle-read-only": {
window.setViewModeEnabled(message.readOnly);
anyWindow.setViewModeEnabled(message.readOnly);
break;
}

case "toggle-scene-modes": {
let modes = message.sceneModes ?? {};
if ("gridMode" in modes) window.setGridModeEnabled(modes.gridMode);
if ("zenMode" in modes) window.setZenModeEnabled(modes.zenMode);
if ("gridMode" in modes) anyWindow.setGridModeEnabled(modes.gridMode);
if ("zenMode" in modes) anyWindow.setZenModeEnabled(modes.zenMode);
break;
}

case "theme-change": {
window.setTheme(message.theme);
anyWindow.setTheme(message.theme);
break;
}

case "save-as-json": {
dispatchToPlugin({
type: "json-content",
json: saveAsJson(),
json: anyWindow.saveAsJson(),
});
break;
}

case "save-as-svg": {
let exportConfig = message.exportConfig ?? {};
let svg = saveAsSvg(exportConfig);
let svg = anyWindow.saveAsSvg(exportConfig);
dispatchToPlugin({
type: "svg-content",
svg: svg.outerHTML,
Expand All @@ -75,7 +78,7 @@ window.addEventListener("message", (e) => {

case "save-as-png": {
let exportConfig = message.exportConfig ?? {};
saveAsPng(exportConfig).then((blob) => {
anyWindow.saveAsPng(exportConfig).then((blob:any) => {
let reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
Expand All @@ -93,14 +96,15 @@ window.addEventListener("message", (e) => {
});


window.continuousSaving = (elements, appState) => {
anyWindow.continuousSaving = (elements:object[], appState:object) => {
console.debug("debounced scene changed")
// @ts-ignore
let newSceneVersion = getSceneVersion(elements);
// maybe check appState
if (newSceneVersion != currentSceneVersion) {
if (newSceneVersion !== currentSceneVersion) {
currentSceneVersion = newSceneVersion;

let jsonContent = saveAsJson();
let jsonContent = anyWindow.saveAsJson();

dispatchToPlugin({
type: "continuous-update",
Expand All @@ -110,74 +114,54 @@ window.continuousSaving = (elements, appState) => {
}

const debouncedContinuousSaving = AwesomeDebouncePromise(
continuousSaving,
anyWindow.continuousSaving,
initialData.debounceAutoSaveInMs
);


const resolvablePromise = () => {
let resolve;
let reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};


export default function App() {
const excalidrawRef = React.useMemo(
() => ({
current: {
readyPromise: resolvablePromise()
}
}),
[]
);
const excalidrawApiRef = React.useRef(null);
const excalidrawRef = React.useCallback((excalidrawApi) => {
excalidrawApiRef.current = excalidrawApi;
dispatchToPlugin({ type: "ready" })
}, []);

React.useEffect(() => {
excalidrawRef.current.readyPromise.then((api) => {
dispatchToPlugin({type: "ready"});
});
}, [excalidrawRef]);


const [theme, setTheme] = React.useState(initialData.theme);
window.setTheme = setTheme;
anyWindow.setTheme = setTheme;
const [viewModeEnabled, setViewModeEnabled] = React.useState(false);
window.setViewModeEnabled = setViewModeEnabled;
anyWindow.setViewModeEnabled = setViewModeEnabled;
const [gridModeEnabled, setGridModeEnabled] = React.useState(initialData.gridMode);
window.setGridModeEnabled = setGridModeEnabled;
anyWindow.setGridModeEnabled = setGridModeEnabled;
const [zenModeEnabled, setZenModeEnabled] = React.useState(initialData.zenMode);
window.setZenModeEnabled = setZenModeEnabled;
anyWindow.setZenModeEnabled = setZenModeEnabled;
// const [exportWithDarkMode, setExportWithDarkMode] = React.useState(false);
// see https://codesandbox.io/s/excalidraw-forked-xsw0k?file=/src/App.js


window.updateApp = ({elements, appState}) => {
excalidrawRef.current.updateScene({
// @ts-ignore
anyWindow.updateApp = ({elements, appState}) => {
(excalidrawApiRef.current as any).updateScene({
elements: elements,
appState: appState,
});
};

window.updateAppState = (appState) => {
excalidrawRef.current.updateScene({
elements: excalidrawRef.current.getSceneElements(),
anyWindow.updateAppState = (appState:object) => {
(excalidrawApiRef.current as any).updateScene({
elements: (excalidrawApiRef.current as any).getSceneElements(),
appState: {
...excalidrawRef.current.getAppState(),
...(excalidrawApiRef.current as any).getAppState(),
...appState
},
});
};

window.saveAsJson = () => {
anyWindow.saveAsJson = () => {
return serializeAsJSON(
excalidrawRef.current.getSceneElements(),
excalidrawRef.current.getAppState()
(excalidrawApiRef.current as any).getSceneElements(),
(excalidrawApiRef.current as any).getAppState()
)
}

Expand All @@ -187,33 +171,33 @@ export default function App() {
// viewBackgroundColor: string (#fff) The default background color
// exportWithDarkMode: boolean (false) Indicates whether to export with dark mode

window.saveAsSvg = (exportParams) => {
anyWindow.saveAsSvg = (exportParams:object) => {
console.debug("saveAsSvg export config", exportParams);
return exportToSvg({
elements: excalidrawRef.current.getSceneElements(),
elements: (excalidrawApiRef.current as any).getSceneElements(),
appState: {
...excalidrawRef.current.getAppState(),
...(excalidrawApiRef.current as any).getAppState(),
...exportParams
},
});
};

window.saveAsPng = (exportParams) => {
anyWindow.saveAsPng = (exportParams:object) => {
console.debug("saveAsSvg export config", exportParams);
return exportToBlob({
elements: excalidrawRef.current.getSceneElements(),
elements: (excalidrawApiRef.current as any).getSceneElements(),
appState: {
...excalidrawRef.current.getAppState(),
...(excalidrawApiRef.current as any).getAppState(),
...exportParams
},
});
};

let onDrawingChange = async (elements, state) => {
let onDrawingChange = async (elements:any, state:object) => {
await debouncedContinuousSaving(elements, state);
};


return (
<div className="excalidraw-wrapper">
<Excalidraw
Expand All @@ -234,13 +218,9 @@ export default function App() {
onCollabButtonClick={() =>
window.alert("Not supported")
}
readyPromise={() => {
console.debug("excalidraw ready")
}}
viewModeEnabled={viewModeEnabled}
zenModeEnabled={zenModeEnabled}
gridModeEnabled={gridModeEnabled}
exportEmbedScene={true}
theme={theme}
// UIOptions={{ canvasActions: { clearCanvas: false, export: false, loadScene: false, saveScene: false } }}
UIOptions={{canvasActions: {loadScene: false}}}
Expand All @@ -249,17 +229,17 @@ export default function App() {
);
}

function dispatchToPlugin(message) {
function dispatchToPlugin(message:object) {
console.debug("dispatchToPlugin: ", message);
// noinspection JSUnresolvedVariable
if (window.cefQuery) {
window.cefQuery({
if (anyWindow.cefQuery) {
anyWindow.cefQuery({
request: JSON.stringify(message),
persistent: false,
onSuccess: function (response) {
onSuccess: function (response:any) {
console.debug("success for message", message, ", response", response);
},
onFailure: function (error_code, error_message) {
onFailure: function (error_code:any, error_message:any) {
console.debug("failure for message", message, ", error_code", error_code, ", error_message", error_message);
}
});
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions excalidraw-assets/src/react-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="react-scripts" />
26 changes: 26 additions & 0 deletions excalidraw-assets/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
Loading

0 comments on commit c37a3fa

Please sign in to comment.