feat: internal checkbox for prop gates

This commit is contained in:
Matei Adriel 2020-04-13 17:45:44 +03:00
parent e8331b3d7a
commit 8168d60ed9
6 changed files with 91 additions and 34 deletions

View file

@ -0,0 +1,11 @@
// Like Array.map but for records
export const mapRecord = <T extends keyof object, A, B>(
record: Record<T, A>,
mapper: (a: A) => B
): Record<T, B> =>
Object.fromEntries(
Object.entries(record).map(([key, value]: [T, A]) => [
key,
mapper(value)
])
)

View file

@ -8,12 +8,17 @@ 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 { map } from 'rxjs/operators'
export interface GatePropertyProps {
raw: Property
gate: Gate
}
const emptyInput = <></>
/**
* Renders a single props of the gate
*
@ -24,7 +29,18 @@ export const GatePropery = ({ raw, gate }: GatePropertyProps) => {
const prop = gate.props[name]
const outputSnapshot = useObservable(() => prop, '')
const displayableName = `${name[0].toUpperCase()}${name.slice(1)}:`
// rerender when the internal checkbox changes
const internal = useObservable(
() =>
gate.props.internal.pipe(
map((value) => value && name !== 'internal')
),
false
)
const displayableName = `${name[0].toUpperCase()}${name.slice(1)} ${
internal ? '(default value)' : ''
}:`
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const target = e.target as HTMLInputElement
@ -45,33 +61,47 @@ export const GatePropery = ({ raw, gate }: GatePropertyProps) => {
}
}
let input = <></>
let input = (() => {
if (
raw.show &&
!raw.show(
mapRecord(gate.props, (subject) => subject.value),
gate
)
) {
return emptyInput
}
if (raw.type === 'number' || raw.type === 'text' || raw.type === 'string') {
input = (
<TextField
onChange={handleChange}
label={displayableName}
value={outputSnapshot}
type={raw.type}
multiline={raw.type === 'string'}
rowsMax={7}
/>
)
} else if (raw.type === 'boolean') {
input = (
<>
<span className="checkbox-label">{displayableName}</span>
<CheckBox
onClick={() => {
prop.next(!outputSnapshot)
}}
if (
raw.type === 'number' ||
raw.type === 'text' ||
raw.type === 'string'
) {
return (input = (
<TextField
onChange={handleChange}
checked={!!outputSnapshot}
/>{' '}
</>
)
}
label={displayableName}
value={outputSnapshot}
type={raw.type}
multiline={raw.type === 'string'}
rowsMax={7}
/>
))
} else if (raw.type === 'boolean') {
return (input = (
<>
<span className="checkbox-label">{displayableName}</span>
<CheckBox
onClick={() => {
prop.next(!outputSnapshot)
}}
onChange={handleChange}
checked={!!outputSnapshot}
/>{' '}
</>
))
}
})()
return <div className="gate-prop-container">{input}</div>
}
@ -104,7 +134,7 @@ const GateProperties = () => {
>
<div
id="gate-properties-container"
onClick={e => {
onClick={(e) => {
e.stopPropagation()
}}
>

View file

@ -4,6 +4,6 @@ import { DefaultGateTemplate } from '../../simulation/constants'
export const completeTemplate = (template: DeepPartial<GateTemplate>) => {
return merge(DefaultGateTemplate, template, {
arrayMerge: (a: unknown[], b: unknown[]) => b
arrayMerge: (a: unknown[], b: unknown[]) => a.concat(b)
}) as GateTemplate
}

View file

@ -4,7 +4,7 @@ import { GateTemplate, PinCount } from '../types/GateTemplate'
import { idStore } from '../stores/idStore'
import { Context, InitialisationContext } from '../../activation/types/Context'
import { toFunction } from '../../activation/helpers/toFunction'
import { Subscription, BehaviorSubject } 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'
@ -15,7 +15,7 @@ import { saveStore } from '../../saving/stores/saveStore'
import { Wire } from './Wire'
import { cleanSimulation } from '../../simulation-actions/helpers/clean'
import { ExecutionQueue } from '../../activation/classes/ExecutionQueue'
import { tap } from 'rxjs/operators'
import { tap, observeOn } from 'rxjs/operators'
/**
* The interface for the pins of a gate
@ -205,7 +205,7 @@ export class Gate {
if (!state) {
throw new SimulationError(
`Cannot run ic ${
this.template.metadata.name
this.template.metadata.name
} - save not found`
)
}
@ -233,7 +233,7 @@ export class Gate {
if (inputs.length !== this._pins.inputs.length) {
throw new SimulationError(
`Input count needs to match with the container gate: ${
inputs.length
inputs.length
} !== ${this._pins.inputs.length}`
)
}
@ -241,7 +241,7 @@ export class Gate {
if (outputs.length !== this._pins.outputs.length) {
throw new SimulationError(
`Output count needs to match with the container gate: ${
outputs.length
outputs.length
} !== ${this._pins.outputs.length}`
)
}

View file

@ -53,7 +53,18 @@ export const DefaultGateTemplate: GateTemplate = {
tags: ['base'],
properties: {
enabled: false,
data: []
data: [
{
type: 'boolean',
base: false,
name: 'internal',
show: (_, gate) =>
gate.env === 'global' &&
!gate.template.properties.data.some(
(prop) => prop.needsUpdate
)
}
]
},
innerText: {
enabled: false,

View file

@ -1,4 +1,5 @@
import { vector2 } from '../../../common/math/types/vector2'
import { Gate } from '../classes/Gate'
export interface PinCount {
variable: boolean
@ -12,6 +13,10 @@ export interface Property<
base: T
name: string
needsUpdate?: boolean
show?: (
obj: Record<string, string | boolean | number>,
gate: Gate
) => boolean
}
export interface Material {