the base logicGates page
This commit is contained in:
parent
c4883b9484
commit
8893967cb8
136
package-lock.json
generated
136
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head> </head>
|
||||
<head>
|
||||
<!-- This doesnt work inside react helmetv-->
|
||||
<meta name="pinterest" content="nopin" />
|
||||
</head>
|
||||
|
||||
<body
|
||||
ondragstart="return false;"
|
||||
|
|
15
src/main.tsx
15
src/main.tsx
|
@ -6,18 +6,25 @@ 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'
|
||||
import { take, filter } from 'rxjs/operators'
|
||||
import { logWelcome } from './modules/core/helpers/logWelcome'
|
||||
import { initRenderer } from './modules/simulationRenderer/helpers/initRenderer'
|
||||
import { updateLogicGateList } from './modules/logic-gates/subjects/LogicGateList'
|
||||
|
||||
export const start = async () => {
|
||||
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(<App />, document.getElementById('app'))
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<Head />
|
||||
<CssBaseline />
|
||||
|
||||
<Theme theme={muiTheme}>
|
||||
<CssBaseline />
|
||||
<Canvas />
|
||||
<Router>
|
||||
<Sidebar />
|
||||
<CreateSimulation />
|
||||
<Input />
|
||||
<LogicGateModal />
|
||||
|
||||
<Route path="/" component={Root} exact />
|
||||
<Route path="/gates" component={LogicGatePage} />
|
||||
</Router>
|
||||
</Theme>
|
||||
|
||||
<ToastContainer
|
||||
|
|
26
src/modules/core/components/BackToSimulation.tsx
Normal file
26
src/modules/core/components/BackToSimulation.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from 'react'
|
||||
import ListItem from '@material-ui/core/ListItem'
|
||||
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 { Link } from 'react-router-dom'
|
||||
|
||||
const BackToSimulation = () => {
|
||||
const translation = useTranslation()
|
||||
|
||||
return (
|
||||
<Link to="/">
|
||||
<ListItem button className="contained">
|
||||
<ListItemIcon>
|
||||
<Icon>arrow_back_ios</Icon>
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
{translation.sidebar.backToSimulation}
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default BackToSimulation
|
|
@ -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<HTMLCanvasElement> = 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 (
|
||||
<FluidCanvas
|
||||
ref={this.canvasRef}
|
||||
mouseDownOuput={this.renderer.mouseDownOutput}
|
||||
mouseUpOutput={this.renderer.mouseUpOutput}
|
||||
mouseMoveOutput={this.renderer.mouseMoveOutput}
|
||||
mouseDownOuput={renderer.mouseDownOutput}
|
||||
mouseUpOutput={renderer.mouseUpOutput}
|
||||
mouseMoveOutput={renderer.mouseMoveOutput}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ const FluidCanvas = forwardRef(
|
|||
|
||||
return (
|
||||
<canvas
|
||||
className="page"
|
||||
ref={ref}
|
||||
width={currentWidth}
|
||||
height={currentHeight}
|
||||
|
|
|
@ -4,7 +4,7 @@ import React from 'react'
|
|||
const title = 'Logic gate simulator'
|
||||
const description = 'A logic gate simulator made for infoeducatie 2019'
|
||||
const url = 'https://logic-gate-simulator.herokuapp.com/'
|
||||
const thumbail = require('../../../assets/thumbail.png')
|
||||
const thumbail = require('../../../assets/favicon.ico')
|
||||
|
||||
const Head = () => {
|
||||
return (
|
||||
|
@ -34,6 +34,11 @@ const Head = () => {
|
|||
<meta property="og:image" content={`${url}${thumbail}`} />
|
||||
<meta property="og:url" content={url} />
|
||||
|
||||
<meta
|
||||
name="Description"
|
||||
content="A logic gate simulator made for infoeducatie 2019"
|
||||
/>
|
||||
|
||||
<link rel="icon" href={require('../../../assets/favicon.ico')} />
|
||||
</Helmet>
|
||||
)
|
||||
|
|
|
@ -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,11 +16,11 @@ const LogicGates = () => {
|
|||
const translation = useTranslation()
|
||||
|
||||
return (
|
||||
<Link to="/gates">
|
||||
<ListItem
|
||||
button
|
||||
onClick={() => {
|
||||
updateLogicGateList()
|
||||
open.next(true)
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
|
@ -28,6 +28,7 @@ const LogicGates = () => {
|
|||
</ListItemIcon>
|
||||
<ListItemText>{translation.sidebar.logicGates}</ListItemText>
|
||||
</ListItem>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
16
src/modules/core/components/Root.tsx
Normal file
16
src/modules/core/components/Root.tsx
Normal file
|
@ -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 (
|
||||
<>
|
||||
<Canvas />
|
||||
<CreateSimulation />
|
||||
<Input />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Root
|
19
src/modules/core/components/Scrollbars.scss
Normal file
19
src/modules/core/components/Scrollbars.scss
Normal file
|
@ -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;
|
||||
}
|
|
@ -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 (
|
||||
<>
|
||||
<CreateSimulationButton />
|
||||
<OpenSimulation />
|
||||
<LogicGates />
|
||||
<SimulationActions />
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Drawer
|
||||
className={classes.drawer}
|
||||
variant="persistent"
|
||||
variant={'persistent'}
|
||||
anchor="right"
|
||||
open={true}
|
||||
classes={{
|
||||
|
@ -68,10 +80,10 @@ const Sidebar = () => {
|
|||
}}
|
||||
>
|
||||
<List component="nav" className={classes.list}>
|
||||
<CreateSimulationButton />
|
||||
<OpenSimulation />
|
||||
<LogicGates />
|
||||
<SimulationActions />
|
||||
<Switch>
|
||||
<Route path="/" exact component={rootSidebarContent} />
|
||||
<Route path="*" component={BackToSimulation} />
|
||||
</Switch>
|
||||
</List>
|
||||
|
||||
<Language />
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
$modal-bg-color: rgba(0, 0, 0, 0.7);
|
||||
$primary: #673ab7;
|
||||
$grey: #444444;
|
||||
$bg: #222222;
|
||||
|
|
|
@ -5,3 +5,8 @@
|
|||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@mixin page-width {
|
||||
@include full-screen();
|
||||
width: calc(100% - 240px);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { Subject } from 'rxjs'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
|
||||
export const loadSubject = new Subject<true>()
|
||||
export const loadSubject = new BehaviorSubject(false)
|
||||
|
|
|
@ -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}'`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<possibleAction, string>
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
22
src/modules/logic-gates/components/AddGate.tsx
Normal file
22
src/modules/logic-gates/components/AddGate.tsx
Normal file
|
@ -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 (
|
||||
<div className="gate-info-icon">
|
||||
<IconButton
|
||||
aria-label="add"
|
||||
onClick={() => {
|
||||
addGateFromTemplate(template)
|
||||
}}
|
||||
>
|
||||
<Icon>add</Icon>
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddGate
|
25
src/modules/logic-gates/components/GateInfo.tsx
Normal file
25
src/modules/logic-gates/components/GateInfo.tsx
Normal file
|
@ -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 (
|
||||
<div className="gate-info-icon">
|
||||
<a href={randomItem(info)} target="blank">
|
||||
<IconButton aria-label="info">
|
||||
<Icon>info</Icon>
|
||||
</IconButton>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GateInfo
|
19
src/modules/logic-gates/components/GateSettings.tsx
Normal file
19
src/modules/logic-gates/components/GateSettings.tsx
Normal file
|
@ -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 (
|
||||
<div className="gate-info-icon">
|
||||
<Icon>settings</Icon>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GateSettings
|
31
src/modules/logic-gates/components/LogicGate.scss
Normal file
31
src/modules/logic-gates/components/LogicGate.scss
Normal file
|
@ -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;
|
||||
}
|
46
src/modules/logic-gates/components/LogicGate.tsx
Normal file
46
src/modules/logic-gates/components/LogicGate.tsx
Normal file
|
@ -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' ? (
|
||||
<img src={template.material.fill} alt={template.metadata.name} />
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: template.material.fill
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
const rawName = template.metadata.name
|
||||
const name = `${rawName[0].toUpperCase()}${rawName.substr(1)}`
|
||||
|
||||
return (
|
||||
<div className="gate">
|
||||
<section>
|
||||
<div className="gate-preview">{gatePreview}</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="gate-name">{name}</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="gate-icons">
|
||||
<GateInfo template={template} />
|
||||
<GateSettings template={template} />
|
||||
<AddGate template={template} />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LogicGate
|
|
@ -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;
|
||||
}
|
|
@ -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 (
|
||||
<div
|
||||
className={openSnapshot ? 'visible' : ''}
|
||||
id="logic-gate-modal-container"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<div className="logic-gate-item">---</div>
|
||||
{gates
|
||||
.map(getTemplateSafely)
|
||||
.filter(template => {
|
||||
return (
|
||||
renderer.simulation.mode === 'project' ||
|
||||
template.metadata.name !== renderer.simulation.name
|
||||
)
|
||||
})
|
||||
.map((template, index) => {
|
||||
const { name } = template.metadata
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="logic-gate-item"
|
||||
onClick={e => {
|
||||
addGate(renderer, name)
|
||||
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<Icon className="lgi-icon logic-gate-item-type">
|
||||
{gateIcons[template.tags[0]]}
|
||||
</Icon>
|
||||
<Typography className="logic-gate-item-name">
|
||||
{name}
|
||||
</Typography>
|
||||
{template.info.length ? (
|
||||
<a
|
||||
target="_blank"
|
||||
className="logic-gate-item-info"
|
||||
href={randomItem(template.info)}
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
// e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<Icon className="lgi-icon">info</Icon>
|
||||
</a>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LogicGateModal
|
34
src/modules/logic-gates/components/LogicGatesPage.scss
Normal file
34
src/modules/logic-gates/components/LogicGatesPage.scss
Normal file
|
@ -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;
|
||||
}
|
38
src/modules/logic-gates/components/LogicGatesPage.tsx
Normal file
38
src/modules/logic-gates/components/LogicGatesPage.tsx
Normal file
|
@ -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 (
|
||||
<main>
|
||||
<div className="page" id="gates-page">
|
||||
<div className="gate-grid">
|
||||
{gates
|
||||
.map(getTemplateSafely)
|
||||
.filter(template => {
|
||||
return (
|
||||
renderer.simulation.mode === 'project' ||
|
||||
template.metadata.name !==
|
||||
renderer.simulation.name
|
||||
)
|
||||
})
|
||||
.map((template, index) => {
|
||||
return <LogicGate key={index} template={template} />
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default LogicGatePage
|
12
src/modules/logic-gates/helpers/addGateFromTemplate.ts
Normal file
12
src/modules/logic-gates/helpers/addGateFromTemplate.ts
Normal file
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ export class SimulationRenderer {
|
|||
}
|
||||
|
||||
public constructor(
|
||||
public ref: RefObject<HTMLCanvasElement>,
|
||||
options: Partial<SimulationRendererOptions> = {},
|
||||
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<HTMLCanvasElement>) {
|
||||
if (ref.current) {
|
||||
ref.current.addEventListener('wheel', event => {
|
||||
if (!modalIsOpen()) {
|
||||
event.preventDefault()
|
||||
|
||||
|
|
16
src/modules/simulationRenderer/helpers/initRenderer.ts
Normal file
16
src/modules/simulationRenderer/helpers/initRenderer.ts
Normal file
|
@ -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())
|
||||
}
|
||||
}
|
4
src/public/robots.txt
Normal file
4
src/public/robots.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
User-agent: *
|
||||
Disallow: /server.js
|
||||
Disallow: /js
|
||||
Disallow: /css
|
|
@ -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'))
|
||||
})
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in a new issue