From 8893967cb8583098bbe0de1be7422813718347a1 Mon Sep 17 00:00:00 2001 From: Matei Adriel Date: Sun, 28 Jul 2019 12:16:49 +0300 Subject: [PATCH] the base logicGates page --- package-lock.json | 136 ++++++++++-------- package.json | 3 +- src/index.html | 5 +- src/main.tsx | 15 +- src/modules/core/components/App.scss | 8 +- src/modules/core/components/App.tsx | 27 ++-- .../core/components/BackToSimulation.tsx | 26 ++++ src/modules/core/components/Canvas.tsx | 24 ++-- src/modules/core/components/FluidCanvas.tsx | 1 + src/modules/core/components/Head.tsx | 7 +- src/modules/core/components/LogicGates.tsx | 27 ++-- src/modules/core/components/Root.tsx | 16 +++ src/modules/core/components/Scrollbars.scss | 19 +++ src/modules/core/components/Sidebar.tsx | 22 ++- src/modules/core/styles/colors.scss | 1 + .../core/styles/mixins/full-screen.scss | 5 + src/modules/core/subjects/loadedSubject.ts | 4 +- .../internalisation/translations/english.ts | 6 +- .../translations/nederlands.ts | 3 +- .../internalisation/translations/romanian.ts | 6 +- .../types/TranslationInterface.ts | 2 + .../helpers/initialiseKeyBindings.ts | 2 +- .../logic-gates/components/AddGate.tsx | 22 +++ .../logic-gates/components/GateInfo.tsx | 25 ++++ .../logic-gates/components/GateSettings.tsx | 19 +++ .../logic-gates/components/LogicGate.scss | 31 ++++ .../logic-gates/components/LogicGate.tsx | 46 ++++++ .../components/LogicGateModal.scss | 70 --------- .../logic-gates/components/LogicGateModal.tsx | 90 ------------ .../components/LogicGatesPage.scss | 34 +++++ .../logic-gates/components/LogicGatesPage.tsx | 38 +++++ .../helpers/addGateFromTemplate.ts | 12 ++ src/modules/modals/helpers/modalIsOpen.ts | 7 +- src/modules/simulation/helpers/addGate.ts | 11 ++ .../classes/SimulationRenderer.ts | 9 +- .../helpers/initRenderer.ts | 16 +++ src/public/robots.txt | 4 + src/server.ts | 2 +- webpack.config.js | 11 +- 39 files changed, 519 insertions(+), 293 deletions(-) create mode 100644 src/modules/core/components/BackToSimulation.tsx create mode 100644 src/modules/core/components/Root.tsx create mode 100644 src/modules/core/components/Scrollbars.scss create mode 100644 src/modules/logic-gates/components/AddGate.tsx create mode 100644 src/modules/logic-gates/components/GateInfo.tsx create mode 100644 src/modules/logic-gates/components/GateSettings.tsx create mode 100644 src/modules/logic-gates/components/LogicGate.scss create mode 100644 src/modules/logic-gates/components/LogicGate.tsx delete mode 100644 src/modules/logic-gates/components/LogicGateModal.scss delete mode 100644 src/modules/logic-gates/components/LogicGateModal.tsx create mode 100644 src/modules/logic-gates/components/LogicGatesPage.scss create mode 100644 src/modules/logic-gates/components/LogicGatesPage.tsx create mode 100644 src/modules/logic-gates/helpers/addGateFromTemplate.ts create mode 100644 src/modules/simulationRenderer/helpers/initRenderer.ts create mode 100644 src/public/robots.txt diff --git a/package-lock.json b/package-lock.json index a8ef658..ffebdf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1956,11 +1956,6 @@ "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", "dev": true }, - "add-px-to-style": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz", - "integrity": "sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo=" - }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", @@ -3173,6 +3168,48 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "copy-webpack-plugin": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz", + "integrity": "sha512-YBuYGpSzoCHSSDGyHy6VJ7SHojKp6WHT4D7ItcQFNAYx2hrwkMe56e97xfVR0/ovDuMTrMffXUiltvQljtAGeg==", + "dev": true, + "requires": { + "cacache": "^11.3.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.7.0", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "core-js": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", @@ -3739,6 +3776,32 @@ "randombytes": "^2.0.0" } }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -3773,16 +3836,6 @@ "utila": "~0.4" } }, - "dom-css": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz", - "integrity": "sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI=", - "requires": { - "add-px-to-style": "1.0.0", - "prefix-style": "2.0.1", - "to-camel-case": "1.0.0" - } - }, "dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -5880,6 +5933,12 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", @@ -7722,7 +7781,8 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "pify": { "version": "2.3.0", @@ -8385,11 +8445,6 @@ "integrity": "sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==", "dev": true }, - "prefix-style": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz", - "integrity": "sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY=" - }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -8560,14 +8615,6 @@ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, - "raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "requires": { - "performance-now": "^2.1.0" - } - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8621,16 +8668,6 @@ "scheduler": "^0.13.6" } }, - "react-custom-scrollbars": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz", - "integrity": "sha1-gw/ZUCkn6X6KeMIIaBOJmyqLZts=", - "requires": { - "dom-css": "^2.0.0", - "prop-types": "^15.5.10", - "raf": "^3.1.0" - } - }, "react-dom": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", @@ -10243,25 +10280,12 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, - "to-camel-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", - "integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=", - "requires": { - "to-space-case": "^1.0.0" - } - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-no-case": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -10304,14 +10328,6 @@ "repeat-string": "^1.6.1" } }, - "to-space-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", - "requires": { - "to-no-case": "^1.0.0" - } - }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", diff --git a/package.json b/package.json index 727d748..93a515d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "dev": "webpack-dev-server --open --mode development", "build": "cross-env NODE_ENV=production webpack", "build:server": "cross-env NODE_ENV=server webpack", - "deploy": "ts-node deploy", "show": "gource -f --start-date \"2019-07-01 12:00\" --key --hide dirnames,filenames,bloom -s 0.3", "start": "node ./dist/server" }, @@ -27,6 +26,7 @@ "babel-loader": "^8.0.6", "babel-plugin-transform-runtime": "^6.23.0", "babel-regenerator-runtime": "^6.5.0", + "copy-webpack-plugin": "^5.0.4", "cross-env": "^5.2.0", "css-loader": "^3.0.0", "file-loader": "^4.1.0", @@ -53,7 +53,6 @@ "keycode": "^2.2.0", "mainloop.js": "^1.0.4", "react": "^16.8.6", - "react-custom-scrollbars": "^4.2.1", "react-dom": "^16.8.6", "react-helmet": "^5.2.1", "react-router-dom": "^5.0.1", diff --git a/src/index.html b/src/index.html index 2bd0c85..112bb03 100644 --- a/src/index.html +++ b/src/index.html @@ -1,6 +1,9 @@ - + + + + { - console.clear() - - const result = loadSubject.pipe(take(1)).toPromise() + const result = loadSubject + .pipe( + filter(a => a), + take(1) + ) + .toPromise() handleErrors() + initRenderer() initKeyBindings() initBaseTemplates() logWelcome() + updateLogicGateList() render(, document.getElementById('app')) diff --git a/src/modules/core/components/App.scss b/src/modules/core/components/App.scss index 82dcb78..835675a 100644 --- a/src/modules/core/components/App.scss +++ b/src/modules/core/components/App.scss @@ -1,4 +1,6 @@ @import '../styles/global-styles/global-styles.scss'; +@import '../../core/styles/mixins/full-screen.scss'; +@import '../styles/colors.scss'; html, body { @@ -8,7 +10,7 @@ body { overflow: hidden; } -canvas { - background-color: #222222; - z-index: -1; +.page { + @include page-width(); + background-color: $bg; } diff --git a/src/modules/core/components/App.tsx b/src/modules/core/components/App.tsx index a9588d0..7d672f6 100644 --- a/src/modules/core/components/App.tsx +++ b/src/modules/core/components/App.tsx @@ -1,33 +1,38 @@ import '../styles/reset' import './App.scss' +import './Scrollbars.scss' import 'react-toastify/dist/ReactToastify.css' import { ToastContainer } from 'react-toastify' import { theme as muiTheme } from '../constants' +import { BrowserRouter as Router, Route, Link } from 'react-router-dom' -import React from 'react' -import Canvas from './Canvas' +import React, { useEffect } from 'react' import CssBaseline from '@material-ui/core/CssBaseline' import Theme from '@material-ui/styles/ThemeProvider' import Sidebar from './Sidebar' -import CreateSimulation from '../../create-simulation/components/CreateSimulation' -import Input from '../../input/components/Input' -import LogicGateModal from '../../logic-gates/components/LogicGateModal' import Head from './Head' +import Root from './Root' +import LogicGatePage from '../../logic-gates/components/LogicGatesPage' +import { loadSubject } from '../subjects/loadedSubject' const App = () => { + useEffect(() => { + loadSubject.next(true) + }) + return ( <> - - - - - - + + + + + + { + const translation = useTranslation() + + return ( + + + + arrow_back_ios + + + {translation.sidebar.backToSimulation} + + + + ) +} + +export default BackToSimulation diff --git a/src/modules/core/components/Canvas.tsx b/src/modules/core/components/Canvas.tsx index 90ec616..c9d07e9 100644 --- a/src/modules/core/components/Canvas.tsx +++ b/src/modules/core/components/Canvas.tsx @@ -1,34 +1,30 @@ -import React, { Component, createRef, Ref, RefObject } from 'react' +import React, { Component, createRef, RefObject } from 'react' import FluidCanvas from './FluidCanvas' import loop from 'mainloop.js' -import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer' import { renderSimulation } from '../../simulationRenderer/helpers/renderSimulation' +import { getRendererSafely } from '../../logic-gates/helpers/getRendererSafely' +import { Subscription } from 'rxjs' import { rendererSubject } from '../subjects/rendererSubject' -import { loadSubject } from '../subjects/loadedSubject' +import { filter } from 'rxjs/operators' class Canvas extends Component { private canvasRef: RefObject = createRef() private renderingContext: CanvasRenderingContext2D | null - private renderer = new SimulationRenderer(this.canvasRef) public constructor(props: {}) { super(props) - rendererSubject.next(this.renderer) - loop.setDraw(() => { if (this.renderingContext) { - renderSimulation(this.renderingContext, this.renderer) + renderSimulation(this.renderingContext, getRendererSafely()) } }) } public componentDidMount() { - loadSubject.next(true) - if (this.canvasRef.current) { this.renderingContext = this.canvasRef.current.getContext('2d') - this.renderer.updateWheelListener() + getRendererSafely().updateWheelListener(this.canvasRef) } loop.start() @@ -39,12 +35,14 @@ class Canvas extends Component { } public render() { + const renderer = getRendererSafely() + return ( ) } diff --git a/src/modules/core/components/FluidCanvas.tsx b/src/modules/core/components/FluidCanvas.tsx index b04f90a..7a142a5 100644 --- a/src/modules/core/components/FluidCanvas.tsx +++ b/src/modules/core/components/FluidCanvas.tsx @@ -33,6 +33,7 @@ const FluidCanvas = forwardRef( return ( { return ( @@ -34,6 +34,11 @@ const Head = () => { + + ) diff --git a/src/modules/core/components/LogicGates.tsx b/src/modules/core/components/LogicGates.tsx index 7af8256..2dd8574 100644 --- a/src/modules/core/components/LogicGates.tsx +++ b/src/modules/core/components/LogicGates.tsx @@ -4,8 +4,8 @@ import ListItemIcon from '@material-ui/core/ListItemIcon' import ListItemText from '@material-ui/core/ListItemText' import Icon from '@material-ui/core/Icon' import { useTranslation } from '../../internalisation/helpers/useLanguage' -import { open } from '../../logic-gates/components/LogicGateModal' import { updateLogicGateList } from '../../logic-gates/subjects/LogicGateList' +import { Link } from 'react-router-dom' /** * Component wich contains the sidebar 'Open simulation' button @@ -16,18 +16,19 @@ const LogicGates = () => { const translation = useTranslation() return ( - { - updateLogicGateList() - open.next(true) - }} - > - - memory - - {translation.sidebar.logicGates} - + + { + updateLogicGateList() + }} + > + + memory + + {translation.sidebar.logicGates} + + ) } diff --git a/src/modules/core/components/Root.tsx b/src/modules/core/components/Root.tsx new file mode 100644 index 0000000..f997169 --- /dev/null +++ b/src/modules/core/components/Root.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import Canvas from './Canvas' +import CreateSimulation from '../../create-simulation/components/CreateSimulation' +import Input from '../../input/components/Input' + +const Root = () => { + return ( + <> + + + + > + ) +} + +export default Root diff --git a/src/modules/core/components/Scrollbars.scss b/src/modules/core/components/Scrollbars.scss new file mode 100644 index 0000000..a4ebbad --- /dev/null +++ b/src/modules/core/components/Scrollbars.scss @@ -0,0 +1,19 @@ +/* width */ +::-webkit-scrollbar { + width: 10px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: transparent; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: #888; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: #555; +} diff --git a/src/modules/core/components/Sidebar.tsx b/src/modules/core/components/Sidebar.tsx index a917fcc..21d91b2 100644 --- a/src/modules/core/components/Sidebar.tsx +++ b/src/modules/core/components/Sidebar.tsx @@ -7,6 +7,8 @@ import LogicGates from './LogicGates' import { makeStyles, createStyles } from '@material-ui/core/styles' import Language from './Language' import SimulationActions from '../../simulation-actions/components/SimulationActions' +import { Route, Switch } from 'react-router' +import BackToSimulation from './BackToSimulation' /** * The width of the sidebar */ @@ -56,11 +58,21 @@ const useStyles = makeStyles( const Sidebar = () => { const classes = useStyles() + const rootSidebarContent = () => { + return ( + <> + + + + + > + ) + } return ( { }} > - - - - + + + + diff --git a/src/modules/core/styles/colors.scss b/src/modules/core/styles/colors.scss index aef8a79..e972476 100644 --- a/src/modules/core/styles/colors.scss +++ b/src/modules/core/styles/colors.scss @@ -1,3 +1,4 @@ $modal-bg-color: rgba(0, 0, 0, 0.7); $primary: #673ab7; $grey: #444444; +$bg: #222222; diff --git a/src/modules/core/styles/mixins/full-screen.scss b/src/modules/core/styles/mixins/full-screen.scss index 5c67538..757d8d9 100644 --- a/src/modules/core/styles/mixins/full-screen.scss +++ b/src/modules/core/styles/mixins/full-screen.scss @@ -5,3 +5,8 @@ top: 0; left: 0; } + +@mixin page-width { + @include full-screen(); + width: calc(100% - 240px); +} diff --git a/src/modules/core/subjects/loadedSubject.ts b/src/modules/core/subjects/loadedSubject.ts index 8105421..f1891ed 100644 --- a/src/modules/core/subjects/loadedSubject.ts +++ b/src/modules/core/subjects/loadedSubject.ts @@ -1,3 +1,3 @@ -import { Subject } from 'rxjs' +import { BehaviorSubject } from 'rxjs' -export const loadSubject = new Subject() +export const loadSubject = new BehaviorSubject(false) diff --git a/src/modules/internalisation/translations/english.ts b/src/modules/internalisation/translations/english.ts index 50f00ab..72abb80 100644 --- a/src/modules/internalisation/translations/english.ts +++ b/src/modules/internalisation/translations/english.ts @@ -10,7 +10,8 @@ export const EnglishTranslation: Translation = { logicGates: 'Logic gates', openSimulation: 'Open simulations', simulation: 'Simulation', - language: 'Language' + language: 'Language', + backToSimulation: 'Back to simulation' }, createSimulation: { mode: { @@ -42,6 +43,7 @@ export const EnglishTranslation: Translation = { cleaned: name => `Succesfully cleaned simulation '${name}'`, refreshed: name => `Succesfully refreshed simulation '${name}'`, undone: name => `Succesfully undone simulation '${name}'`, - deletedSimulation: name => `Succesfully deleted simulation '${name}'` + deletedSimulation: name => `Succesfully deleted simulation '${name}'`, + addedGate: name => `Succesfully added gate '${name}'` } } diff --git a/src/modules/internalisation/translations/nederlands.ts b/src/modules/internalisation/translations/nederlands.ts index 649857a..9995ae6 100644 --- a/src/modules/internalisation/translations/nederlands.ts +++ b/src/modules/internalisation/translations/nederlands.ts @@ -10,7 +10,8 @@ export const DutchTranslation: Translation = { logicGates: 'Logische poorten', openSimulation: 'Open simulatie', simulation: 'Todo', - language: 'Taal' + language: 'Taal', + backToSimulation: 'Todo' }, actions: { 'delete selection': 'Todo', diff --git a/src/modules/internalisation/translations/romanian.ts b/src/modules/internalisation/translations/romanian.ts index 41e6935..b258c9e 100644 --- a/src/modules/internalisation/translations/romanian.ts +++ b/src/modules/internalisation/translations/romanian.ts @@ -10,7 +10,8 @@ export const RomanianTranslation: Translation = { openSimulation: 'Deschide o simulație', logicGates: 'Porți logice', simulation: 'Simulație', - language: 'Limba' + language: 'Limba', + backToSimulation: 'Înapoi la simulație' }, createSimulation: { mode: { @@ -44,6 +45,7 @@ export const RomanianTranslation: Translation = { refreshed: name => `Simulația '${name}' a fost reîncărcată cu succes`, undone: name => `Acțiunea a fost întoarsă`, deletedSimulation: name => - `Simulația '${name}' a fost ștearsă cu succes` + `Simulația '${name}' a fost ștearsă cu succes`, + addedGate: name => `Componentul '${name}' a fost adăugat cu succes` } } diff --git a/src/modules/internalisation/types/TranslationInterface.ts b/src/modules/internalisation/types/TranslationInterface.ts index eb50c4e..5d7bc31 100644 --- a/src/modules/internalisation/types/TranslationInterface.ts +++ b/src/modules/internalisation/types/TranslationInterface.ts @@ -16,6 +16,7 @@ export interface Translation { logicGates: string simulation: string language: string + backToSimulation: string } createSimulation: { mode: { @@ -35,6 +36,7 @@ export interface Translation { cleaned: NameSentence undone: NameSentence deletedSimulation: NameSentence + addedGate: NameSentence } actions: Record } diff --git a/src/modules/keybindings/helpers/initialiseKeyBindings.ts b/src/modules/keybindings/helpers/initialiseKeyBindings.ts index f2673e3..bb1bdde 100644 --- a/src/modules/keybindings/helpers/initialiseKeyBindings.ts +++ b/src/modules/keybindings/helpers/initialiseKeyBindings.ts @@ -31,7 +31,7 @@ export const initKeyBindings = (bindings: KeyBindingMap = keyBindings) => { } window.addEventListener('keydown', e => { - if (!modalIsOpen()) { + if (!modalIsOpen() && location.pathname === '/') { const current: { keys: string[] callback: Function diff --git a/src/modules/logic-gates/components/AddGate.tsx b/src/modules/logic-gates/components/AddGate.tsx new file mode 100644 index 0000000..38560e8 --- /dev/null +++ b/src/modules/logic-gates/components/AddGate.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import Icon from '@material-ui/core/Icon' +import IconButton from '@material-ui/core/IconButton' +import { LogicGateProps } from './LogicGate' +import { addGateFromTemplate } from '../helpers/addGateFromTemplate' + +const AddGate = ({ template }: LogicGateProps) => { + return ( + + { + addGateFromTemplate(template) + }} + > + add + + + ) +} + +export default AddGate diff --git a/src/modules/logic-gates/components/GateInfo.tsx b/src/modules/logic-gates/components/GateInfo.tsx new file mode 100644 index 0000000..b582871 --- /dev/null +++ b/src/modules/logic-gates/components/GateInfo.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import Icon from '@material-ui/core/Icon' +import IconButton from '@material-ui/core/IconButton' +import { LogicGateProps } from './LogicGate' +import { randomItem } from '../../internalisation/helpers/randomItem' + +const GateInfo = ({ template }: LogicGateProps) => { + const info = template.info + + if (info.length === 0) { + return <>> + } else { + return ( + + + + info + + + + ) + } +} + +export default GateInfo diff --git a/src/modules/logic-gates/components/GateSettings.tsx b/src/modules/logic-gates/components/GateSettings.tsx new file mode 100644 index 0000000..685382c --- /dev/null +++ b/src/modules/logic-gates/components/GateSettings.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import Icon from '@material-ui/core/Icon' +import { LogicGateProps } from './LogicGate' + +const GateSettings = ({ template }: LogicGateProps) => { + const tags = template.tags + + if (tags.includes('base')) { + return <>> + } else { + return ( + + settings + + ) + } +} + +export default GateSettings diff --git a/src/modules/logic-gates/components/LogicGate.scss b/src/modules/logic-gates/components/LogicGate.scss new file mode 100644 index 0000000..dac8ff6 --- /dev/null +++ b/src/modules/logic-gates/components/LogicGate.scss @@ -0,0 +1,31 @@ +@import '../../core/styles/mixins/flex.scss'; + +$gate-margin: 1em; + +.gate > section > .gate-preview > * { + width: 100%; +} + +.gate > section > .gate-preview > * { + display: block; + height: 10em; + width: 10em; + + margin: $gate-margin; +} + +.gate > section > .gate-name { + width: 100%; + text-align: center; +} + +.gate > section > .gate-icons { + @include flex(); + + width: calc(100% - 2 * #{$gate-margin}); + height: 100%; + margin: $gate-margin; + + flex-direction: row; + justify-content: space-evenly; +} diff --git a/src/modules/logic-gates/components/LogicGate.tsx b/src/modules/logic-gates/components/LogicGate.tsx new file mode 100644 index 0000000..9ae0dd8 --- /dev/null +++ b/src/modules/logic-gates/components/LogicGate.tsx @@ -0,0 +1,46 @@ +import './LogicGate.scss' +import React from 'react' +import { GateTemplate } from '../../simulation/types/GateTemplate' +import GateInfo from './GateInfo' +import GateSettings from './GateSettings' +import AddGate from './AddGate' + +export interface LogicGateProps { + template: GateTemplate +} + +const LogicGate = ({ template }: LogicGateProps) => { + const gatePreview = + template.material.type === 'image' ? ( + + ) : ( + + ) + + const rawName = template.metadata.name + const name = `${rawName[0].toUpperCase()}${rawName.substr(1)}` + + return ( + + + {gatePreview} + + + {name} + + + + + + + + + + ) +} + +export default LogicGate diff --git a/src/modules/logic-gates/components/LogicGateModal.scss b/src/modules/logic-gates/components/LogicGateModal.scss deleted file mode 100644 index 5b1772d..0000000 --- a/src/modules/logic-gates/components/LogicGateModal.scss +++ /dev/null @@ -1,70 +0,0 @@ -@import '../../core/styles/mixins/flex.scss'; -@import '../../core/styles/mixins/visibility.scss'; -@import '../../modals/styles/mixins/modal-container.scss'; -@import '../../modals/styles/mixins/modal-title.scss'; -@import '../../core/styles/colors.scss'; -@import '../../core/styles/mixins/glow.scss'; - -// Opacities -$normal-opacity: 0.6; -$hover-opacity: 0.9; - -// Colors -$item-color: $grey; - -#logic-gate-modal-container { - @include modal-container(); - - justify-content: center; - overflow-y: auto; - height: 100%; -} - -.visible#logic-gate-modal-container { - @include visible(); -} - -#logic-gate-modal-container > .logic-gate-item { - @include flex(); - - justify-content: left; - flex-direction: row; - - width: 80%; - height: 4em; - - border: 3px solid white; - margin: 0.5em; - - background-color: rgba($item-color, $normal-opacity); -} - -#logic-gate-modal-container > .logic-gate-item:hover { - background-color: rgba($item-color, $hover-opacity); -} - -#logic-gate-modal-container > .logic-gate-item > * { - font-size: 2.5em; -} - -#logic-gate-modal-container > .logic-gate-item > .logic-gate-item-type { - margin: 0.5em; -} - -#logic-gate-modal-container > .logic-gate-item > *:last-child { - margin-right: 0.5em; -} - -.lgi-icon:hover:not(.logic-gate-item-type) { - border: 1px solid white; -} - -#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 deleted file mode 100644 index 4504989..0000000 --- a/src/modules/logic-gates/components/LogicGateModal.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import './LogicGateModal.scss' -import React from 'react' -import { BehaviorSubject } from 'rxjs' -import { useObservable } from 'rxjs-hooks' -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 { randomItem } from '../../internalisation/helpers/randomItem' -import { gateIcons } from '../constants' -import { getTemplateSafely } from '../helpers/getTemplateSafely' -import { getRendererSafely } from '../helpers/getRendererSafely' - -/** - * Subject containing the open state of the modal - */ -export const open = new BehaviorSubject(false) - -/** - * helper to close the modal - */ -export const handleClose = () => { - open.next(false) -} - -/** - * The component containing the info / actions about all logic gates - */ -const LogicGateModal = () => { - const openSnapshot = useObservable(() => open, false) - const gates = useObservable(() => LogicGateList, []) - const renderer = getRendererSafely() - - return ( - - --- - {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, name) - - e.stopPropagation() - }} - > - - {gateIcons[template.tags[0]]} - - - {name} - - {template.info.length ? ( - { - e.stopPropagation() - // e.preventDefault() - }} - > - info - - ) : ( - '' - )} - - ) - })} - - ) -} - -export default LogicGateModal diff --git a/src/modules/logic-gates/components/LogicGatesPage.scss b/src/modules/logic-gates/components/LogicGatesPage.scss new file mode 100644 index 0000000..d351b1a --- /dev/null +++ b/src/modules/logic-gates/components/LogicGatesPage.scss @@ -0,0 +1,34 @@ +@import '../../core/styles/colors.scss'; +@import '../../core/styles/mixins/flex.scss'; + +#gates-page { + color: white; +} + +.gate-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); + grid-auto-rows: 1fr; +} + +.gate-grid::before { + content: ''; + width: 0; + padding-bottom: 100%; + grid-row: 1 / 1; + grid-column: 1 / 1; +} + +.gate-grid > *:first-child { + grid-row: 1 / 1; + grid-column: 1 / 1; +} + +.gate-grid > * { + @include flex(); + + justify-content: start; + margin: 0.3em; + background-color: $grey; + height: 20em; +} diff --git a/src/modules/logic-gates/components/LogicGatesPage.tsx b/src/modules/logic-gates/components/LogicGatesPage.tsx new file mode 100644 index 0000000..8e63db2 --- /dev/null +++ b/src/modules/logic-gates/components/LogicGatesPage.tsx @@ -0,0 +1,38 @@ +import './LogicGatesPage.scss' +import React from 'react' +import { useObservable } from 'rxjs-hooks' +import { LogicGateList } from '../subjects/LogicGateList' +import { getTemplateSafely } from '../helpers/getTemplateSafely' +import { getRendererSafely } from '../helpers/getRendererSafely' +import LogicGate from './LogicGate' + +/** + * The component containing the info / actions about all logic gates + */ +const LogicGatePage = () => { + const gates = useObservable(() => LogicGateList, []) + const renderer = getRendererSafely() + + return ( + + + + {gates + .map(getTemplateSafely) + .filter(template => { + return ( + renderer.simulation.mode === 'project' || + template.metadata.name !== + renderer.simulation.name + ) + }) + .map((template, index) => { + return + })} + + + + ) +} + +export default LogicGatePage diff --git a/src/modules/logic-gates/helpers/addGateFromTemplate.ts b/src/modules/logic-gates/helpers/addGateFromTemplate.ts new file mode 100644 index 0000000..5a47314 --- /dev/null +++ b/src/modules/logic-gates/helpers/addGateFromTemplate.ts @@ -0,0 +1,12 @@ +import { GateTemplate } from '../../simulation/types/GateTemplate' +import { addGate } from '../../simulation/helpers/addGate' +import { getRendererSafely } from './getRendererSafely' + +/** + * Adds a gate to the current simulation from its template + * + * @param template The template to create + */ +export const addGateFromTemplate = (template: GateTemplate) => { + addGate(getRendererSafely(), template.metadata.name) +} diff --git a/src/modules/modals/helpers/modalIsOpen.ts b/src/modules/modals/helpers/modalIsOpen.ts index 02c4342..10a6d23 100644 --- a/src/modules/modals/helpers/modalIsOpen.ts +++ b/src/modules/modals/helpers/modalIsOpen.ts @@ -1,11 +1,6 @@ import { InputStore } from '../../input/stores/InputStore' -import { open as logicGateModalIsOpen } from '../../logic-gates/components/LogicGateModal' import { CreateSimulationStore } from '../../create-simulation/stores/CreateSimulationStore' export const modalIsOpen = () => { - return ( - InputStore.data.open.value || - logicGateModalIsOpen.value || - CreateSimulationStore.data.open.value - ) + return InputStore.data.open.value || CreateSimulationStore.data.open.value } diff --git a/src/modules/simulation/helpers/addGate.ts b/src/modules/simulation/helpers/addGate.ts index 90833a4..732688a 100644 --- a/src/modules/simulation/helpers/addGate.ts +++ b/src/modules/simulation/helpers/addGate.ts @@ -6,9 +6,13 @@ import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationR import { DefaultGateTemplate } from '../constants' import { vector2 } from '../../../common/math/classes/Transform' import { Screen } from '../../screen/helpers/Screen' +import { toast } from 'react-toastify' +import { createToastArguments } from '../../toasts/helpers/createToastArguments' +import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' export const addGate = (renderer: SimulationRenderer, templateName: string) => { const template = templateStore.get(templateName) + const translation = CurrentLanguage.getTranslation() if (!template) throw new SimulationError(`Cannot find template ${templateName}`) @@ -32,4 +36,11 @@ export const addGate = (renderer: SimulationRenderer, templateName: string) => { renderer.simulation.push(gate) renderer.spawnCount++ + + toast( + ...createToastArguments( + translation.messages.addedGate(templateName), + 'add_circle_outline' + ) + ) } diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts index f41b92c..fa906db 100644 --- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts +++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts @@ -67,7 +67,6 @@ export class SimulationRenderer { } public constructor( - public ref: RefObject, options: Partial = {}, public simulation = new Simulation('project', 'default') ) { @@ -83,6 +82,8 @@ export class SimulationRenderer { this.lastMousePosition = worldPosition + console.log('click') + // We need to iterate from the last to the first // because if we have 2 overlapping gates, // we want to select the one on top @@ -290,9 +291,9 @@ export class SimulationRenderer { this.reloadSave() } - public updateWheelListener() { - if (this.ref.current) { - this.ref.current.addEventListener('wheel', event => { + public updateWheelListener(ref: RefObject) { + if (ref.current) { + ref.current.addEventListener('wheel', event => { if (!modalIsOpen()) { event.preventDefault() diff --git a/src/modules/simulationRenderer/helpers/initRenderer.ts b/src/modules/simulationRenderer/helpers/initRenderer.ts new file mode 100644 index 0000000..0b0822a --- /dev/null +++ b/src/modules/simulationRenderer/helpers/initRenderer.ts @@ -0,0 +1,16 @@ +import { rendererSubject } from '../../core/subjects/rendererSubject' +import { SimulationError } from '../../errors/classes/SimulationError' +import { SimulationRenderer } from '../classes/SimulationRenderer' + +/** + * Helper to create the simulationRenderer + * + * @throws SimulationError if the renderer already exists + */ +export const initRenderer = () => { + if (rendererSubject.value) { + throw new SimulationError('Renderer already inited') + } else { + rendererSubject.next(new SimulationRenderer()) + } +} diff --git a/src/public/robots.txt b/src/public/robots.txt new file mode 100644 index 0000000..3914151 --- /dev/null +++ b/src/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: /server.js +Disallow: /js +Disallow: /css \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 83dd1ab..2b341ff 100644 --- a/src/server.ts +++ b/src/server.ts @@ -5,7 +5,7 @@ const app = express() app.use(_static(__dirname)) -app.get('/', (rex, res) => { +app.get('*', (rex, res) => { res.sendFile(resolve(__dirname, 'index.html')) }) diff --git a/webpack.config.js b/webpack.config.js index 9d5b380..4b359f2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const webpackMerge = require('webpack-merge') const nodeExternals = require('webpack-node-externals') +const CopyPlugin = require('copy-webpack-plugin') const isProduction = process.env.NODE_ENV === 'production' @@ -71,7 +72,15 @@ const serverConfig = { }, node: { __dirname: false - } + }, + plugins: [ + new CopyPlugin([ + { + from: resolve(sourceFolder, 'public'), + to: buildFolder + } + ]) + ] } const baseConfig = {