feat: internal checkbox for prop gates
This commit is contained in:
parent
e8331b3d7a
commit
8168d60ed9
11
src/common/lang/record/map.ts
Normal file
11
src/common/lang/record/map.ts
Normal 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)
|
||||
])
|
||||
)
|
|
@ -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()
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}`
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue