From a9c9ba0b5f067926c74db9a8c965fc4395103677 Mon Sep 17 00:00:00 2001 From: Matei Adriel Date: Mon, 13 Apr 2020 23:39:00 +0300 Subject: [PATCH] feat: configurable ics:) --- .vscode/settings.json | 3 +- babel.config.js | 3 +- package-lock.json | 37 ++++- package.json | 3 +- src/common/lang/record/types/ValueOf.ts | 1 + src/common/lazy/classes/Lazy.ts | 17 ++ .../integrated-circuits/helpers/compileIc.ts | 29 +++- .../components/GateProperties.scss | 21 ++- .../components/GatePropertiesModal.tsx | 94 +++++++---- src/modules/saving/types/SimulationSave.ts | 6 +- src/modules/screen/helpers/Screen.ts | 9 +- src/modules/simulation/classes/Gate.ts | 155 ++++++++++++++---- src/modules/simulation/constants.ts | 21 +-- src/modules/simulation/types/GateTemplate.ts | 23 ++- .../classes/SimulationRenderer.ts | 4 +- tsconfig.json | 12 +- 16 files changed, 327 insertions(+), 111 deletions(-) create mode 100644 src/common/lang/record/types/ValueOf.ts create mode 100644 src/common/lazy/classes/Lazy.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index d0015c5..c05778d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "editor.formatOnSave": true, "prettier.eslintIntegration": true, - "explorer.autoReveal": false + "explorer.autoReveal": false, + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/babel.config.js b/babel.config.js index e8639d3..6955daf 100644 --- a/babel.config.js +++ b/babel.config.js @@ -5,6 +5,7 @@ module.exports = { '@babel/preset-typescript' ], plugins: [ + '@babel/plugin-proposal-optional-chaining', '@babel/plugin-syntax-dynamic-import', ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: true }] @@ -14,4 +15,4 @@ module.exports = { presets: [['@babel/preset-env', { targets: { node: 'current' } }]] } } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index a9f0889..7209dd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -672,6 +672,22 @@ "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" } }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", + "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" + } + } + }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", @@ -746,6 +762,21 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" + } + } + }, "@babel/plugin-syntax-typescript": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz", @@ -10461,9 +10492,9 @@ "dev": true }, "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index 08a2080..06b9fd0 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "optimize-css-assets-webpack-plugin": "^5.0.3", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", - "typescript": "^3.5.2", + "typescript": "^3.8.3", "webpack": "^4.36.1", "webpack-cli": "^3.3.6", "webpack-dev-server": "^3.7.2", @@ -46,6 +46,7 @@ "webpack-node-externals": "^1.7.2" }, "dependencies": { + "@babel/plugin-proposal-optional-chaining": "^7.9.0", "@eix-js/utils": "0.0.6", "@material-ui/core": "^4.2.1", "deepmerge": "^4.0.0", diff --git a/src/common/lang/record/types/ValueOf.ts b/src/common/lang/record/types/ValueOf.ts new file mode 100644 index 0000000..834a4ab --- /dev/null +++ b/src/common/lang/record/types/ValueOf.ts @@ -0,0 +1 @@ +export type ValueOf = T[keyof T] diff --git a/src/common/lazy/classes/Lazy.ts b/src/common/lazy/classes/Lazy.ts new file mode 100644 index 0000000..ac4230b --- /dev/null +++ b/src/common/lazy/classes/Lazy.ts @@ -0,0 +1,17 @@ +export class Lazy { + private value: T | null = null + + /** + * SImple encoding of lazy values + * @param getter a function o get the lazy value + */ + public constructor(private getter: () => T) {} + + public get(value) { + if (this.value === null) { + this.value = this.getter() + } + + return this.value + } +} diff --git a/src/modules/integrated-circuits/helpers/compileIc.ts b/src/modules/integrated-circuits/helpers/compileIc.ts index 8566a62..69c0a0c 100644 --- a/src/modules/integrated-circuits/helpers/compileIc.ts +++ b/src/modules/integrated-circuits/helpers/compileIc.ts @@ -1,6 +1,11 @@ import { SimulationState } from '../../saving/types/SimulationSave' import { SimulationError } from '../../errors/classes/SimulationError' -import { GateTemplate } from '../../simulation/types/GateTemplate' +import { + GateTemplate, + Property, + PropGroup, + isGroup +} from '../../simulation/types/GateTemplate' import { simulationInputCount, simulationOutputCount @@ -13,6 +18,8 @@ import { fromSimulationState } from '../../saving/helpers/fromState' import { cleanSimulation } from '../../simulation-actions/helpers/clean' import { getSimulationState } from '../../saving/helpers/getState' import { categories } from '../../saving/data/categories' +import { getTemplateSafely } from '../../logic-gates/helpers/getTemplateSafely' +import { reservedPropNames } from '../../simulation/constants' /** * Compiles a simulation into a logicGate @@ -35,6 +42,8 @@ export const compileIc = (state: SimulationState) => { const inputCount = simulationInputCount(cleanState.gates) const outputCount = simulationOutputCount(cleanState.gates) + const props = cleanState.gates.filter(({ props }) => props.external) + const result: DeepPartial = { metadata: { name @@ -52,6 +61,24 @@ export const compileIc = (state: SimulationState) => { material: { type: 'image', fill: require('../../../assets/ic') + }, + properties: { + enabled: !!props.length, + data: props.map((gate) => { + const template = getTemplateSafely(gate.template) + return { + groupName: gate.props.label, + props: template.properties.data.map((prop) => { + if (isGroup(prop)) { + return prop + } + return { + ...prop, + base: gate.props[prop.name] + } + }) + } as PropGroup + }) } } diff --git a/src/modules/logic-gates/components/GateProperties.scss b/src/modules/logic-gates/components/GateProperties.scss index b6fd5c3..22cb619 100644 --- a/src/modules/logic-gates/components/GateProperties.scss +++ b/src/modules/logic-gates/components/GateProperties.scss @@ -17,21 +17,24 @@ $gate-props-margin: 1rem; } div #gate-properties-container { - @include flex; + display: flex; + flex-direction: column; background-color: $grey; - padding: $gate-props-margin * 4; border-radius: 1em; -} -#gate-props-divider { - margin-top: $gate-props-margin; - margin-bottom: $gate-props-margin; + padding: $gate-props-margin * 4; + box-sizing: border-box; + + max-height: 80vh; + overflow: auto; } div #gate-props-title { color: white; font-size: 3em; + + margin-bottom: 2 * $gate-props-margin; } div .gate-prop-container { @@ -45,6 +48,12 @@ div .gate-prop-container { } } +div .gate-prop-group-container { + @include flex; + + margin-left: 1rem; +} + div #save-props { width: 50%; margin: $gate-props-margin * 2; diff --git a/src/modules/logic-gates/components/GatePropertiesModal.tsx b/src/modules/logic-gates/components/GatePropertiesModal.tsx index dc67450..5e0d252 100644 --- a/src/modules/logic-gates/components/GatePropertiesModal.tsx +++ b/src/modules/logic-gates/components/GatePropertiesModal.tsx @@ -1,45 +1,83 @@ import './GateProperties.scss' -import React, { ChangeEvent, MouseEvent } from 'react' +import React, { ChangeEvent } from 'react' import { getRendererSafely } from '../helpers/getRendererSafely' -import { Property } from '../../simulation/types/GateTemplate' +import { Property, RawProp, isGroup } from '../../simulation/types/GateTemplate' import { useObservable } from 'rxjs-hooks' import Divider from '@material-ui/core/Divider' import TextField from '@material-ui/core/TextField' import CheckBox from '@material-ui/core/Checkbox' import { open, id } from '../subjects/LogicGatePropsSubjects' -import { Gate } from '../../simulation/classes/Gate' -import { mapRecord } from '../../../common/lang/record/map' -import { BehaviorSubject } from 'rxjs' +import { Gate, GateProps } from '../../simulation/classes/Gate' import { map } from 'rxjs/operators' - -export interface GatePropertyProps { - raw: Property +import { BehaviorSubject } from 'rxjs' +import ExpansionPanel from '@material-ui/core/ExpansionPanel' +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary' +import Typography from '@material-ui/core/Typography' +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails' +import Icon from '@material-ui/core/Icon' +interface GatePropertyProps { + raw: T gate: Gate + props: GateProps } const emptyInput = <> +const GateProperty = ({ raw, props, gate }: GatePropertyProps) => { + if (isGroup(raw)) { + return ( + + expand_more} + aria-controls={raw.groupName} + id={raw.groupName} + > + {raw.groupName} + + +
+ {raw.props.map((propTemplate, index) => ( + + ))} +
+
+
+ ) + } + + return +} + /** * Renders a single props of the gate * * @param param0 The props passed to the component */ -export const GatePropery = ({ raw, gate }: GatePropertyProps) => { +const GateRawProperty = ({ + props, + raw, + gate +}: GatePropertyProps & { raw: RawProp }) => { const { name } = raw - const prop = gate.props[name] + const prop = props[raw.name] as BehaviorSubject const outputSnapshot = useObservable(() => prop, '') - // rerender when the internal checkbox changes - const internal = useObservable( + // rerender when the external checkbox changes + const external = useObservable( () => - gate.props.internal.pipe( - map((value) => value && name !== 'internal') + gate.props.external.pipe( + map((value) => value && name !== 'external') ), false ) const displayableName = `${name[0].toUpperCase()}${name.slice(1)} ${ - internal ? '(default value)' : '' + external && name !== 'label' ? '(default value)' : '' }:` const handleChange = (e: ChangeEvent) => { @@ -55,19 +93,19 @@ export const GatePropery = ({ raw, gate }: GatePropertyProps) => { if (raw.type !== 'boolean') { prop.next(value) } - - if (raw.needsUpdate === true) { - gate.update() - } } let input = (() => { - if ( - raw.show && - !raw.show( - mapRecord(gate.props, (subject) => subject.value), - gate + const displayExternal = () => + getRendererSafely().simulation.mode === 'ic' && + gate.env === 'global' && + !gate.template.properties.data.some( + (prop) => (prop as RawProp).needsUpdate ) + + if ( + (raw.name === 'external' && !displayExternal()) || + (raw.name === 'label' && !external) ) { return emptyInput } @@ -147,12 +185,12 @@ const GateProperties = () => { }} >
Gate properties
- - {gate.template.properties.data.map((raw, index) => { + {gate.template.properties.data.map((prop, index) => { return ( - diff --git a/src/modules/saving/types/SimulationSave.ts b/src/modules/saving/types/SimulationSave.ts index d607667..218dfd0 100644 --- a/src/modules/saving/types/SimulationSave.ts +++ b/src/modules/saving/types/SimulationSave.ts @@ -8,11 +8,15 @@ export interface TransformState { rotation: number } +export type PropsSave = { + [K in string]: string | number | boolean | PropsSave +} + export interface GateState { transform: TransformState id: number template: string - props: Record + props: PropsSave } export interface CameraState { diff --git a/src/modules/screen/helpers/Screen.ts b/src/modules/screen/helpers/Screen.ts index 1dd17f4..9700478 100644 --- a/src/modules/screen/helpers/Screen.ts +++ b/src/modules/screen/helpers/Screen.ts @@ -2,26 +2,25 @@ import { Transform } from '../../../common/math/classes/Transform' import { BehaviorSubject, fromEvent } from 'rxjs' import { map } from 'rxjs/operators' import { getWidth } from '../helpers/getWidth' -import { sidebarWidth } from '../../core/constants' const width = new BehaviorSubject(getWidth()) const height = new BehaviorSubject(window.innerHeight) const resize = fromEvent(window, 'resize') -resize.pipe(map(getWidth)).subscribe(val => width.next(val)) -resize.pipe(map(() => window.innerHeight)).subscribe(val => height.next(val)) +resize.pipe(map(getWidth)).subscribe((val) => width.next(val)) +resize.pipe(map(() => window.innerHeight)).subscribe((val) => height.next(val)) /** * The main screen transform */ const Screen = new Transform() -width.subscribe(currentWidth => { +width.subscribe((currentWidth) => { Screen.width = currentWidth }) -height.subscribe(currentHeight => { +height.subscribe((currentHeight) => { Screen.height = currentHeight }) diff --git a/src/modules/simulation/classes/Gate.ts b/src/modules/simulation/classes/Gate.ts index a9561f0..50e2850 100644 --- a/src/modules/simulation/classes/Gate.ts +++ b/src/modules/simulation/classes/Gate.ts @@ -1,10 +1,20 @@ import { Transform } from '../../../common/math/classes/Transform' import { Pin } from './Pin' -import { GateTemplate, PinCount } from '../types/GateTemplate' +import { + GateTemplate, + PinCount, + isGroup, + Property +} from '../types/GateTemplate' import { idStore } from '../stores/idStore' import { Context, InitialisationContext } from '../../activation/types/Context' import { toFunction } from '../../activation/helpers/toFunction' -import { Subscription, BehaviorSubject, asapScheduler, animationFrameScheduler } from 'rxjs' +import { + Subscription, + BehaviorSubject, + asapScheduler, + animationFrameScheduler +} from 'rxjs' import { SimulationError } from '../../errors/classes/SimulationError' import { getGateTimePipes } from '../helpers/getGateTimePipes' import { ImageStore } from '../../simulationRenderer/stores/imageStore' @@ -16,6 +26,8 @@ import { Wire } from './Wire' import { cleanSimulation } from '../../simulation-actions/helpers/clean' import { ExecutionQueue } from '../../activation/classes/ExecutionQueue' import { tap, observeOn } from 'rxjs/operators' +import { PropsSave } from '../../saving/types/SimulationSave' +import { ValueOf } from '../../../common/lang/record/types/ValueOf' /** * The interface for the pins of a gate @@ -47,6 +59,13 @@ export interface GateFunctions { onClick: GateFunction } +export type GateProps = { + [K in keyof PropsSave]: BehaviorSubject | GateProps +} & { + external: BehaviorSubject + label: BehaviorSubject +} + export class Gate { /** * The transform of the gate @@ -126,10 +145,7 @@ export class Gate { /** * The props used by the activation function (the same as memory but presists) */ - public props: Record< - string, - BehaviorSubject - > = {} + public props: GateProps = {} as GateProps /** * The main logic gate class @@ -140,10 +156,9 @@ export class Gate { public constructor( template: DeepPartial = {}, id?: number, - props: Record = {} + props: PropsSave = {} ) { this.template = completeTemplate(template) - this.transform.scale = this.template.shape.scale if (this.template.material.type === 'color') { @@ -204,9 +219,7 @@ export class Gate { if (!state) { throw new SimulationError( - `Cannot run ic ${ - this.template.metadata.name - } - save not found` + `Cannot run ic ${this.template.metadata.name} - save not found` ) } @@ -219,30 +232,26 @@ export class Gate { const gates = Array.from(this.ghostSimulation.gates) const inputs = gates - .filter(gate => gate.template.integration.input) + .filter((gate) => gate.template.integration.input) .sort(sortByPosition) - .map(gate => gate.wrapPins(gate._pins.outputs)) + .map((gate) => gate.wrapPins(gate._pins.outputs)) .flat() const outputs = gates - .filter(gate => gate.template.integration.output) + .filter((gate) => gate.template.integration.output) .sort(sortByPosition) - .map(gate => gate.wrapPins(gate._pins.inputs)) + .map((gate) => gate.wrapPins(gate._pins.inputs)) .flat() if (inputs.length !== this._pins.inputs.length) { throw new SimulationError( - `Input count needs to match with the container gate: ${ - inputs.length - } !== ${this._pins.inputs.length}` + `Input count needs to match with the container gate: ${inputs.length} !== ${this._pins.inputs.length}` ) } if (outputs.length !== this._pins.outputs.length) { throw new SimulationError( - `Output count needs to match with the container gate: ${ - outputs.length - } !== ${this._pins.outputs.length}` + `Output count needs to match with the container gate: ${outputs.length} !== ${this._pins.outputs.length}` ) } @@ -267,28 +276,95 @@ export class Gate { this.assignProps(props) } + private updateNestedProp( + path: string[], + value: ValueOf, + gate: Gate = this + ) { + if (!path.length) { + return + } + + if (path.length === 1) { + const subject = gate.props[path[0]] + + if (subject instanceof BehaviorSubject) { + subject.next(value) + } + + return + } + + const nextGates = [...gate.ghostSimulation.gates].filter( + (gate) => gate.props?.label?.value === path[0] + ) + + for (const nextGate of nextGates) { + this.updateNestedProp(path.slice(1), value, nextGate) + } + } + /** * Assign the props passed to the gate and mere them with the base ones */ - private assignProps(props: Record) { + private assignProps( + source: PropsSave, + props: Property[] = this.template.properties.data, + target: GateProps = this.props, + path: string[] = [] + ) { let shouldUpdate = false if (this.template.properties.enabled) { - for (const { base, name, needsUpdate } of this.template.properties - .data) { - this.props[name] = new BehaviorSubject( - props.hasOwnProperty(name) ? props[name] : base + for (const prop of props) { + if (isGroup(prop)) { + const { groupName } = prop + target[groupName] = {} as GateProps + const needsUpdate = this.assignProps( + typeof source[groupName] === 'object' + ? (source[groupName] as PropsSave) + : {}, + prop.props, + target[groupName] as GateProps, + [...path, groupName] + ) + + if (needsUpdate) { + shouldUpdate = true + } + + continue + } + + const { name, base, needsUpdate } = prop + + const subject = new BehaviorSubject( + source.hasOwnProperty(name) ? source[name] : base ) - if (!shouldUpdate && needsUpdate) { + target[name] = subject + + this.subscriptions.push( + subject.subscribe((value) => { + if (needsUpdate && path.length === 0) { + return this.update() + } + + if (path.length === 0) { + return + } + + this.updateNestedProp([...path, name], value) + }) + ) + + if (needsUpdate) { shouldUpdate = true } } } - if (shouldUpdate) { - this.update() - } + return shouldUpdate } /** @@ -315,11 +391,15 @@ export class Gate { /** * Used to get the props as an object */ - public getProps() { - const props: Record = {} + public getProps(target = this.props) { + const props: PropsSave = {} - for (const key in this.props) { - props[key] = this.props[key].value + for (const [key, value] of Object.entries(target)) { + if (value instanceof BehaviorSubject) { + props[key] = value.value + } else { + props[key] = this.getProps(value) + } } return props @@ -361,7 +441,7 @@ export class Gate { */ public getContext(): Context { const maxLength = Math.max( - ...this._pins.inputs.map(pin => pin.state.value.length) + ...this._pins.inputs.map((pin) => pin.state.value.length) ) const toLength = ( @@ -412,7 +492,10 @@ export class Gate { return this.props[name].value }, setProperty: (name: string, value: string | number | boolean) => { - this.props[name].next(value) + const subject = this.props[name] + if (subject instanceof BehaviorSubject) { + subject.next(value) + } }, innerText: (value: string) => { this.text.inner.next(value) diff --git a/src/modules/simulation/constants.ts b/src/modules/simulation/constants.ts index b8e90ad..bf9b8ff 100644 --- a/src/modules/simulation/constants.ts +++ b/src/modules/simulation/constants.ts @@ -1,5 +1,6 @@ -import { GateTemplate } from './types/GateTemplate' +import { GateTemplate, Property, RawProp } from './types/GateTemplate' import { categories } from '../saving/data/categories' +import { getRendererSafely } from '../logic-gates/helpers/getRendererSafely' export const DefaultGateTemplate: GateTemplate = { metadata: { @@ -49,7 +50,6 @@ export const DefaultGateTemplate: GateTemplate = { input: false, output: false }, - info: [], tags: ['base'], properties: { enabled: false, @@ -57,18 +57,12 @@ export const DefaultGateTemplate: GateTemplate = { { type: 'boolean', base: false, - name: 'internal', - show: (_, gate) => - gate.env === 'global' && - !gate.template.properties.data.some( - (prop) => prop.needsUpdate - ) + name: 'external' }, { type: 'string', base: 'my-logic-gate', - name: 'label', - show: ({ internal }: { internal: boolean }) => internal + name: 'label' } ] }, @@ -78,3 +72,10 @@ export const DefaultGateTemplate: GateTemplate = { }, category: categories.basic } + +/** + * Prop names which need to not be overriten + */ +export const reservedPropNames = DefaultGateTemplate.properties.data.map( + ({ name }: RawProp) => name +) diff --git a/src/modules/simulation/types/GateTemplate.ts b/src/modules/simulation/types/GateTemplate.ts index 3a38357..aca59ba 100644 --- a/src/modules/simulation/types/GateTemplate.ts +++ b/src/modules/simulation/types/GateTemplate.ts @@ -1,23 +1,31 @@ import { vector2 } from '../../../common/math/types/vector2' -import { Gate } from '../classes/Gate' export interface PinCount { variable: boolean count: number } -export interface Property< +export type PropGroup< T extends boolean | number | string = boolean | number | string -> { +> = { + groupName: string + props: Property[] +} + +export const isGroup = (prop: Property): prop is PropGroup => + (prop as PropGroup).groupName !== undefined + +export type RawProp< + T extends boolean | number | string = boolean | number | string +> = { type: 'number' | 'string' | 'text' | 'boolean' base: T name: string needsUpdate?: boolean - show?: ( - obj: Record, - gate: Gate - ) => boolean } +export type Property< + T extends boolean | number | string = boolean | number | string +> = PropGroup | RawProp export interface Material { type: 'color' | 'image' @@ -74,7 +82,6 @@ export interface GateTemplate { input: boolean output: boolean } - info: string[] tags: GateTag[] properties: { enabled: boolean diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts index 45561ff..6c71238 100644 --- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts +++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts @@ -80,7 +80,7 @@ export class SimulationRenderer { public updateWheelListener(ref: RefObject) { if (ref.current) { - ref.current.addEventListener('wheel', event => { + ref.current.addEventListener('wheel', (event) => { if (!modalIsOpen() && location.pathname === '/') { event.preventDefault() @@ -119,7 +119,7 @@ export class SimulationRenderer { * @throws SimulationError if the id doesnt have a data prop */ public getSelected(): Gate[] { - return setToArray(this.allSelectedIds()).map(id => { + return setToArray(this.allSelectedIds()).map((id) => { const gate = this.simulation.gates.get(id) if (!gate) { diff --git a/tsconfig.json b/tsconfig.json index 7838367..0c9f867 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,15 +4,11 @@ "esModuleInterop": true, "jsx": "preserve", "experimentalDecorators": true, - "target": "esnext", + "target": "ESNext", "downlevelIteration": true, "strictNullChecks": true, "module": "esnext" }, - "exclude": [ - "node_modules" - ], - "include": [ - "src" - ] -} \ No newline at end of file + "exclude": ["node_modules"], + "include": ["src"] +}