From fccc1922fbe02e05814faf260cc63c10a1bf111c Mon Sep 17 00:00:00 2001 From: Matei Adriel Date: Tue, 23 Jul 2019 22:53:59 +0300 Subject: [PATCH] loading --- package-lock.json | 36 ++++++-- package.json | 4 +- src/common/dom/helpers/querySelector.ts | 15 +++ .../lang/errors/helpers/getSafeErrorStack.ts | 21 +++++ src/index.html | 9 ++ src/index.ts | 27 ++++++ src/main.tsx | 18 +++- src/modules/activation/helpers/toFunction.ts | 12 ++- src/modules/core/components/Canvas.tsx | 3 + src/modules/core/subjects/loadedSubject.ts | 3 + .../integrated-circuits/helpers/compileIc.ts | 11 ++- .../internalisation/translations/english.ts | 3 +- .../translations/nederlands.ts | 3 +- .../internalisation/translations/romanian.ts | 3 +- .../types/TranslationInterface.ts | 1 + .../components/LogicGateModal.scss | 10 +- .../logic-gates/components/LogicGateModal.tsx | 92 +++++++++---------- .../logic-gates/helpers/getRendererSafely.ts | 17 ++++ .../logic-gates/helpers/getTemplateSafely.ts | 20 ++++ src/modules/saving/helpers/initSimulation.ts | 5 + src/modules/splash/classes/Spinner.scss | 32 +++++++ src/modules/splash/classes/Splash.scss | 61 ++++++++++++ src/modules/splash/classes/Splash.ts | 84 +++++++++++++++++ tsconfig.json | 3 +- webpack.config.js | 2 +- 25 files changed, 425 insertions(+), 70 deletions(-) create mode 100644 src/common/dom/helpers/querySelector.ts create mode 100644 src/common/lang/errors/helpers/getSafeErrorStack.ts create mode 100644 src/index.ts create mode 100644 src/modules/core/subjects/loadedSubject.ts create mode 100644 src/modules/logic-gates/helpers/getRendererSafely.ts create mode 100644 src/modules/logic-gates/helpers/getTemplateSafely.ts create mode 100644 src/modules/splash/classes/Spinner.scss create mode 100644 src/modules/splash/classes/Splash.scss create mode 100644 src/modules/splash/classes/Splash.ts diff --git a/package-lock.json b/package-lock.json index e6255f4..4c71943 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2550,8 +2550,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-indexof": { "version": "1.1.1", @@ -3202,6 +3201,31 @@ "sha.js": "^2.4.8" } }, + "cross-env": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", + "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5", + "is-windows": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, "cross-spawn": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", @@ -9469,8 +9493,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.2", @@ -9489,7 +9512,6 @@ "version": "0.5.12", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -9929,7 +9951,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/terser/-/terser-4.1.2.tgz", "integrity": "sha512-jvNoEQSPXJdssFwqPSgWjsOrb+ELoE+ILpHPKXC83tIxOlh2U75F1KuB2luLD/3a6/7K3Vw5pDn+hvu0C4AzSw==", - "dev": true, "requires": { "commander": "^2.20.0", "source-map": "~0.6.1", @@ -9939,8 +9960,7 @@ "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" } } }, diff --git a/package.json b/package.json index 69c2f93..846c36b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "babel-loader": "^8.0.6", "babel-plugin-transform-runtime": "^6.23.0", "babel-regenerator-runtime": "^6.5.0", + "cross-env": "^5.2.0", "css-loader": "^3.0.0", "file-loader": "^4.1.0", "html-webpack-inline-source-plugin": "0.0.10", @@ -50,6 +51,7 @@ "react-router-dom": "^5.0.1", "react-toastify": "^5.3.2", "rxjs": "^6.5.2", - "rxjs-hooks": "^0.5.1" + "rxjs-hooks": "^0.5.1", + "terser": "^4.1.2" } } diff --git a/src/common/dom/helpers/querySelector.ts b/src/common/dom/helpers/querySelector.ts new file mode 100644 index 0000000..6b46852 --- /dev/null +++ b/src/common/dom/helpers/querySelector.ts @@ -0,0 +1,15 @@ +/** + * A type-safe querySelector function, which throws if the given element was not found + * + * @credit https://gitlab.com/wavedistrict/web-client/blob/master/src/common/dom/helpers/querySelector.ts + */ +export function querySelector( + selector: string, + parent: Element = document.body +) { + const element = parent.querySelector(selector) + if (!element) { + throw `Could not find element with selector "${selector}"` + } + return element as E +} diff --git a/src/common/lang/errors/helpers/getSafeErrorStack.ts b/src/common/lang/errors/helpers/getSafeErrorStack.ts new file mode 100644 index 0000000..183aec7 --- /dev/null +++ b/src/common/lang/errors/helpers/getSafeErrorStack.ts @@ -0,0 +1,21 @@ +export const getSafeErrorStack = (error: any) => { + const errorString: string = error.toString() + const stackString: string = error.stack + + if (stackString) { + const safeStackString = + stackString.replace(errorString + '\n', '') || stackString + + const stackItems = safeStackString.split('\n') + const safeStackItems = stackItems + .map(item => item.replace(' at ', '')) + .filter(item => item !== '') + .map(item => ` at ${item}`) + + const safeStack = safeStackItems.join('\n') + + return `${errorString}\n${safeStack}` + } + + return errorString +} diff --git a/src/index.html b/src/index.html index 275ed07..481ede9 100644 --- a/src/index.html +++ b/src/index.html @@ -28,5 +28,14 @@ oncontextmenu="return false" >
+
+
+
+
+
+
+
+ +
diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4b257bc --- /dev/null +++ b/src/index.ts @@ -0,0 +1,27 @@ +import { Splash } from './modules/splash/classes/Splash' + +async function main() { + let splash: Splash | undefined = undefined + + try { + splash = new Splash() + } catch {} + + try { + const app = await import('./main') + + await app.start() + } catch (error) { + if (splash) splash.setError(error) + console.error(error.stack || error) + return + } + + if (splash) { + splash.fade() + } +} + +main().catch(error => { + console.error('Error loading app', error) +}) diff --git a/src/main.tsx b/src/main.tsx index cb7e5d9..41877b3 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,11 +5,19 @@ import { render } from 'react-dom' import { handleErrors } from './modules/errors/helpers/handleErrors' import { initKeyBindings } from './modules/keybindings/helpers/initialiseKeyBindings' import { initBaseTemplates } from './modules/saving/helpers/initBaseTemplates' +import { loadSubject } from './modules/core/subjects/loadedSubject' +import { take } from 'rxjs/operators' -console.clear() +export const start = async () => { + console.clear() -handleErrors() -initKeyBindings() -initBaseTemplates() + const result = loadSubject.pipe(take(1)).toPromise() -render(, document.getElementById('app')) + handleErrors() + initKeyBindings() + initBaseTemplates() + + render(, document.getElementById('app')) + + await result +} diff --git a/src/modules/activation/helpers/toFunction.ts b/src/modules/activation/helpers/toFunction.ts index 034cd70..cfd6816 100644 --- a/src/modules/activation/helpers/toFunction.ts +++ b/src/modules/activation/helpers/toFunction.ts @@ -1,8 +1,16 @@ +/** + * Transforms js code into a function + * + * @param source tThe js code + * @param args The name of arguments to pass to the function + */ export const toFunction = ( source: string, ...args: string[] ): ((...args: T) => void) => { - return new Function(`return (${args.join(',')}) => { + const raw = `return (${args.join(',')}) => { ${source} - }`)() + }` + + return new Function(raw)() } diff --git a/src/modules/core/components/Canvas.tsx b/src/modules/core/components/Canvas.tsx index e575fb4..a94ddfa 100644 --- a/src/modules/core/components/Canvas.tsx +++ b/src/modules/core/components/Canvas.tsx @@ -5,6 +5,7 @@ import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationR import { renderSimulation } from '../../simulationRenderer/helpers/renderSimulation' import { updateSimulation } from '../../simulationRenderer/helpers/updateSimulation' import { rendererSubject } from '../subjects/rendererSubject' +import { loadSubject } from '../subjects/loadedSubject' class Canvas extends Component { private canvasRef: RefObject = createRef() @@ -24,6 +25,8 @@ class Canvas extends Component { } public componentDidMount() { + loadSubject.next(true) + if (this.canvasRef.current) { this.renderingContext = this.canvasRef.current.getContext('2d') this.renderer.updateWheelListener() diff --git a/src/modules/core/subjects/loadedSubject.ts b/src/modules/core/subjects/loadedSubject.ts new file mode 100644 index 0000000..8105421 --- /dev/null +++ b/src/modules/core/subjects/loadedSubject.ts @@ -0,0 +1,3 @@ +import { Subject } from 'rxjs' + +export const loadSubject = new Subject() diff --git a/src/modules/integrated-circuits/helpers/compileIc.ts b/src/modules/integrated-circuits/helpers/compileIc.ts index 0211eb4..0492b2f 100644 --- a/src/modules/integrated-circuits/helpers/compileIc.ts +++ b/src/modules/integrated-circuits/helpers/compileIc.ts @@ -5,8 +5,10 @@ import { simulationInputCount, simulationOutputCount } from './simulationIoCount' -import { InitialisationContext } from '../../activation/types/Context' import { templateStore } from '../../saving/stores/templateStore' +import { toast } from 'react-toastify' +import { createToastArguments } from '../../toasts/helpers/createToastArguments' +import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' /** * Compiles a simulation into a logicGate @@ -18,6 +20,7 @@ export const compileIc = ({ mode, name, gates }: SimulationState) => { throw new SimulationError('Cannot compile project') } + const translation = CurrentLanguage.getTranslation() const inputCount = simulationInputCount(gates) const outputCount = simulationOutputCount(gates) @@ -37,4 +40,10 @@ export const compileIc = ({ mode, name, gates }: SimulationState) => { } templateStore.set(name, result) + toast( + ...createToastArguments( + translation.messages.compiledIc(name), + 'markunread_mailbox' + ) + ) } diff --git a/src/modules/internalisation/translations/english.ts b/src/modules/internalisation/translations/english.ts index e6881ed..c1e332d 100644 --- a/src/modules/internalisation/translations/english.ts +++ b/src/modules/internalisation/translations/english.ts @@ -27,6 +27,7 @@ export const EnglishTranslation: Translation = { createdSimulation: name => `Succesfully created simulation '${name}'`, switchedToSimulation: name => `Succesfully switched to simulation '${name}'`, - savedSimulation: name => `Succesfully saved simulation '${name}'` + savedSimulation: name => `Succesfully saved simulation '${name}'`, + compiledIc: name => `Succesfully compiled circuit '${name}'` } } diff --git a/src/modules/internalisation/translations/nederlands.ts b/src/modules/internalisation/translations/nederlands.ts index 67e862e..4d2bd77 100644 --- a/src/modules/internalisation/translations/nederlands.ts +++ b/src/modules/internalisation/translations/nederlands.ts @@ -28,6 +28,7 @@ export const DutchTranslation: Translation = { createdSimulation: name => `Simulatie '${name}' succesvol gecreerd`, switchedToSimulation: name => `Succesvol veranderd naar simulatie '${name}'`, - savedSimulation: name => `Simulatie succesvol opgeslagen '${name}'` + savedSimulation: name => `Simulatie succesvol opgeslagen '${name}'`, + compiledIc: name => `Todo: ${name}` } } diff --git a/src/modules/internalisation/translations/romanian.ts b/src/modules/internalisation/translations/romanian.ts index b2fa222..c2b4449 100644 --- a/src/modules/internalisation/translations/romanian.ts +++ b/src/modules/internalisation/translations/romanian.ts @@ -28,6 +28,7 @@ export const RomanianTranslation: Translation = { `Simulația '${name}' a fost creeată cu succes`, switchedToSimulation: name => `Simulația '${name}' a fost deschisă cu succes`, - savedSimulation: name => `Simulația '${name}' a fost salvată cu succes` + savedSimulation: name => `Simulația '${name}' a fost salvată cu succes`, + compiledIc: name => `Simulația '${name}' a fost compilată cu succes` } } diff --git a/src/modules/internalisation/types/TranslationInterface.ts b/src/modules/internalisation/types/TranslationInterface.ts index c84de13..fcba812 100644 --- a/src/modules/internalisation/types/TranslationInterface.ts +++ b/src/modules/internalisation/types/TranslationInterface.ts @@ -28,5 +28,6 @@ export interface Translation { createdSimulation: NameSentence switchedToSimulation: NameSentence savedSimulation: NameSentence + compiledIc: NameSentence } } diff --git a/src/modules/logic-gates/components/LogicGateModal.scss b/src/modules/logic-gates/components/LogicGateModal.scss index 7ef63cd..5b1772d 100644 --- a/src/modules/logic-gates/components/LogicGateModal.scss +++ b/src/modules/logic-gates/components/LogicGateModal.scss @@ -16,6 +16,8 @@ $item-color: $grey; @include modal-container(); justify-content: center; + overflow-y: auto; + height: 100%; } .visible#logic-gate-modal-container { @@ -42,7 +44,7 @@ $item-color: $grey; } #logic-gate-modal-container > .logic-gate-item > * { - font-size: 3em; + font-size: 2.5em; } #logic-gate-modal-container > .logic-gate-item > .logic-gate-item-type { @@ -60,3 +62,9 @@ $item-color: $grey; #logic-gate-modal-container > .logic-gate-item > .logic-gate-item-name { flex-grow: 1; } + +#logic-gate-modal-container > .logic-gate-item:first-child { + // height: 4em; + + opacity: 0; +} diff --git a/src/modules/logic-gates/components/LogicGateModal.tsx b/src/modules/logic-gates/components/LogicGateModal.tsx index 588787c..b43fd6e 100644 --- a/src/modules/logic-gates/components/LogicGateModal.tsx +++ b/src/modules/logic-gates/components/LogicGateModal.tsx @@ -6,12 +6,10 @@ import { LogicGateList } from '../subjects/LogicGateList' import Icon from '@material-ui/core/Icon' import Typography from '@material-ui/core/Typography' import { addGate } from '../../simulation/helpers/addGate' -import { rendererSubject } from '../../core/subjects/rendererSubject' -import { SimulationError } from '../../errors/classes/SimulationError' -import { templateStore } from '../../saving/stores/templateStore' import { randomItem } from '../../internalisation/helpers/randomItem' -import { completeTemplate } from '../helpers/completeTemplate' import { gateIcons } from '../constants' +import { getTemplateSafely } from '../helpers/getTemplateSafely' +import { getRendererSafely } from '../helpers/getRendererSafely' /** * Subject containing the open state of the modal @@ -31,6 +29,7 @@ export const handleClose = () => { const LogicGateModal = () => { const openSnapshot = useObservable(() => open, false) const gates = useObservable(() => LogicGateList, []) + const renderer = getRendererSafely() return (
{ id="logic-gate-modal-container" onClick={handleClose} > - {gates.map((gate, index) => { - const renderer = rendererSubject.value - - if (!renderer) { - throw new SimulationError(`Renderer not found`) - } - - const template = completeTemplate(templateStore.get(gate) || {}) - - if (!template) { - throw new SimulationError( - `Template ${gate} cannot be found` +
---
+ {gates + .map(getTemplateSafely) + .filter(template => { + return ( + renderer.simulation.mode === 'project' || + template.metadata.name !== renderer.simulation.name ) - } + }) + .map((template, index) => { + const { name } = template.metadata - return ( -
{ - addGate(renderer.simulation, gate) - }} - > - - {gateIcons[template.tags[0]]} - - - {gate} - - {template.info.length && ( - { - e.stopPropagation() - e.preventDefault() - }} - > - info - - )} -
- ) - })} + return ( +
{ + addGate(renderer.simulation, name) + }} + > + + {gateIcons[template.tags[0]]} + + + {name} + + {template.info.length ? ( + { + e.stopPropagation() + e.preventDefault() + }} + > + info + + ) : ( + '' + )} +
+ ) + })}
) } diff --git a/src/modules/logic-gates/helpers/getRendererSafely.ts b/src/modules/logic-gates/helpers/getRendererSafely.ts new file mode 100644 index 0000000..a9bdc4e --- /dev/null +++ b/src/modules/logic-gates/helpers/getRendererSafely.ts @@ -0,0 +1,17 @@ +import { rendererSubject } from '../../core/subjects/rendererSubject' +import { SimulationError } from '../../errors/classes/SimulationError' + +/** + * Gets the current simulation renderer + * + * @throws SimulationError no renderer was found + */ +export const getRendererSafely = () => { + const renderer = rendererSubject.value + + if (!renderer) { + throw new SimulationError(`Renderer not found`) + } + + return renderer +} diff --git a/src/modules/logic-gates/helpers/getTemplateSafely.ts b/src/modules/logic-gates/helpers/getTemplateSafely.ts new file mode 100644 index 0000000..6456b97 --- /dev/null +++ b/src/modules/logic-gates/helpers/getTemplateSafely.ts @@ -0,0 +1,20 @@ +import { templateStore } from '../../saving/stores/templateStore' +import { SimulationError } from '../../errors/classes/SimulationError' +import { completeTemplate } from './completeTemplate' + +/** + * Gets a gate template from localStorage + * + * @param name - The name of the template + * + * @throws SimulationError if the template cant be found + */ +export const getTemplateSafely = (name: string) => { + const template = completeTemplate(templateStore.get(name) || {}) + + if (!template) { + throw new SimulationError(`Template ${name} cannot be found`) + } + + return template +} diff --git a/src/modules/saving/helpers/initSimulation.ts b/src/modules/saving/helpers/initSimulation.ts index c423f97..4120e64 100644 --- a/src/modules/saving/helpers/initSimulation.ts +++ b/src/modules/saving/helpers/initSimulation.ts @@ -5,6 +5,7 @@ import { saveStore } from '../stores/saveStore' import { toast } from 'react-toastify' import { createToastArguments } from '../../toasts/helpers/createToastArguments' import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' +import { compileIc } from '../../integrated-circuits/helpers/compileIc' /** * Inits a simulation by: @@ -31,5 +32,9 @@ export const initSimulation = (name: string, mode: simulationMode) => { ) ) + if (mode === 'ic') { + compileIc(state.simulation) + } + return state } diff --git a/src/modules/splash/classes/Spinner.scss b/src/modules/splash/classes/Spinner.scss new file mode 100644 index 0000000..120662e --- /dev/null +++ b/src/modules/splash/classes/Spinner.scss @@ -0,0 +1,32 @@ +.lds-ripple { + display: inline-block; + position: relative; + width: 64px; + height: 64px; +} +.lds-ripple div { + position: absolute; + border: 4px solid #fff; + opacity: 1; + border-radius: 50%; + animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; +} +.lds-ripple div:nth-child(2) { + animation-delay: -0.5s; +} +@keyframes lds-ripple { + 0% { + top: 28px; + left: 28px; + width: 0; + height: 0; + opacity: 1; + } + 100% { + top: -1px; + left: -1px; + width: 58px; + height: 58px; + opacity: 0; + } +} diff --git a/src/modules/splash/classes/Splash.scss b/src/modules/splash/classes/Splash.scss new file mode 100644 index 0000000..86fd467 --- /dev/null +++ b/src/modules/splash/classes/Splash.scss @@ -0,0 +1,61 @@ +@import './Spinner.scss'; +@import '../../core/styles/colors.scss'; + +.Splash { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + + background-color: $grey; + padding: 32px; + + overflow-y: auto; + z-index: 999999999999999; +} + +.Splash > .loading { + width: 100%; + height: 100%; + + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.Splash > .error { + display: none; +} + +.Splash > .error > .title { + font-size: 24px; + margin-bottom: 16px; +} + +.Splash > .error > .details { + padding: 16px; + + background-color: shade(darker); + + border-radius: 3px; + margin-bottom: 16px; + + font-family: monospace; +} + +.Splash > .error > .description { + color: font-color(normal); + margin-bottom: 16px; + font-size: 14px; +} + +.Splash.-hasError { + > .error { + display: block; + } + > .loading { + display: none; + } +} diff --git a/src/modules/splash/classes/Splash.ts b/src/modules/splash/classes/Splash.ts new file mode 100644 index 0000000..e326ee1 --- /dev/null +++ b/src/modules/splash/classes/Splash.ts @@ -0,0 +1,84 @@ +import { querySelector } from '../../../common/dom/helpers/querySelector' +import { getSafeErrorStack } from '../../../common/lang/errors/helpers/getSafeErrorStack' +import './Splash.scss' + +export class Splash { + private element = querySelector('.Splash') + + public fade() { + this.element.style.transition = '0.3s' + this.element.style.opacity = '0' + this.element.style.visibility = 'hidden' + + setTimeout(() => { + this.element.remove() + }, 300) + } + + private createErrorDOM() { + const root = document.createElement('div') + root.className = 'error' + + const title = document.createElement('div') + title.className = 'title' + title.innerText = 'Oops! An error occurred.' + + const details = document.createElement('pre') + details.className = 'details' + + const description = document.createElement('div') + description.className = 'description' + description.innerHTML = ` +
+

Your browser might not be supported, or your data might be corrupt. + Press "Clear data" below to reset the simulator and try again.
+

+ We do not support the following browsers:

+
    +
  • Opera Mini
  • +
  • Internet Explorer
  • +
+
+ ` + + const actions = document.createElement('div') + actions.className = 'actions' + + root.appendChild(title) + root.appendChild(details) + root.appendChild(description) + root.appendChild(actions) + + this.element.appendChild(root) + } + + public setError(error: any) { + this.createErrorDOM() + + const details = querySelector( + '.Splash > .error > .details' + ) + const actions = querySelector( + '.Splash > .error > .actions' + ) + + details.innerText = getSafeErrorStack(error) + actions.appendChild(this.getClearButton()) + + this.element.classList.add('-hasError') + } + + private getClearButton() { + const clearButton = document.createElement('button') + + clearButton.classList.add('PrimaryButton') + clearButton.textContent = 'Clear data' + + clearButton.addEventListener('click', () => { + localStorage.clear() + location.reload() + }) + + return clearButton + } +} diff --git a/tsconfig.json b/tsconfig.json index 20fb5c4..0073ddb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "noImplicitAny": true, "experimentalDecorators": true, "target": "esnext", - "strictNullChecks": true + "strictNullChecks": true, + "module": "esnext" }, "exclude": ["node_modules"], "include": ["src"] diff --git a/webpack.config.js b/webpack.config.js index 15db204..033eb1a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -54,7 +54,7 @@ const sassRule = { const baseConfig = { mode: 'none', - entry: ['babel-regenerator-runtime', resolve(sourceFolder, 'main')], + entry: ['babel-regenerator-runtime', resolve(sourceFolder, 'index')], output: { filename: 'js/[name].js', path: buildFolder,