feat: params, actions, etc

This commit is contained in:
cha0s 2020-06-18 02:15:49 -05:00
parent a6aeb66809
commit c88c5e5c02
10 changed files with 240 additions and 22 deletions

View File

@ -0,0 +1,84 @@
import {compose} from '@avocado/core';
import contempo from 'contempo';
import PropTypes from 'prop-types';
import React from 'react';
import propertyPropTypes from './property-prop-types';
const reduceStep = (r, step) => {
switch (step.type) {
case 'key':
return `${r}.${step.key}`;
case 'invoke':
return (
<span className="step">
<span className="invocation">{r}</span>
<span className="paren open">(</span>
<div className="args">
{step.args.map(renderValue).map((element) => <div className="arg">{element}</div>)}
</div>
<span className="paren close">)</span>
</span>
);
default:
return undefined;
}
};
const renderValue = (value) => {
switch (value.type) {
case 'traversal': {
const {steps} = value;
return (
<span>
{steps.length > 0 && (
steps.slice(1).reduce(
reduceStep,
steps[0].key,
)
)}
</span>
);
}
case 'literal':
return <input value={value.value} />;
default:
return null;
}
};
const decorate = compose(
contempo(require('./actions.raw.scss')),
);
const Actions = ({
name,
label,
options,
value,
}) => (
<label>
<span className="text">
{label}
:
</span>
<div className="invisible-separator" />
<div className="actions">
<ol>
{
value.traversals.length > 0 && (
value.traversals.map(renderValue).map((o) => <li>{o}</li>)
)
}
</ol>
</div>
</label>
);
Actions.propTypes = {
...propertyPropTypes,
value: PropTypes.shape({}).isRequired,
};
export default decorate(Actions);

View File

@ -0,0 +1,3 @@
.args {
margin-left: 2em;
}

View File

@ -9,7 +9,10 @@ const Bool = ({
value, value,
}) => ( }) => (
<label> <label>
<span className="text">{label}</span> <span className="text">
{label}
:
</span>
<div className="invisible-separator" /> <div className="invisible-separator" />
<input name={name} type="checkbox" checked={value} /> <input name={name} type="checkbox" checked={value} />
</label> </label>

View File

@ -0,0 +1,94 @@
import {compose} from '@avocado/core';
import contempo from 'contempo';
import PropTypes from 'prop-types';
import React from 'react';
import propertyPropTypes from './property-prop-types';
const renderValue = (value) => {
switch (value.type) {
case 'traversal': {
const {steps} = value;
return (
<span>
{steps.length > 0 && (
steps.slice(1).reduce((r, step) => `${r}.${step.key}`, steps[0].key)
)}
</span>
);
}
case 'literal':
return <input value={value.value} />;
default:
return null;
}
};
const renderOperand = (operand) => (
<span className="operand">
{renderValue(operand)}
</span>
);
const binaryOps = [
['is', 'is equal to'],
['isnt', "isn't equal to"],
['>', 'is greater than'],
['>=', 'is greater than or equal to'],
['<', 'is less than'],
['<=', 'is less than or equal to'],
];
const decorate = compose(
contempo(require('./condition.raw.scss')),
);
const Condition = ({
name,
label,
options,
value,
}) => (
<label>
<span className="text">
{label}
:
</span>
<div className="invisible-separator" />
<div className="condition">
{
value.operands.length > 0 && (() => {
switch (value.operator) {
case 'is':
case 'isnt':
case '>':
case '>=':
case '<':
case '<=':
return value.operands.slice(1).reduce(
(r, operand) => (
<>
{r}
<select className="operator" value={value.operator}>
{binaryOps.map(([k, v]) => <option value={k}>{v}</option>)}
</select>
{renderOperand(operand)}
</>
),
renderOperand(value.operands[0]),
);
default:
return undefined;
}
})()
}
</div>
</label>
);
Condition.propTypes = {
...propertyPropTypes,
value: PropTypes.shape({}).isRequired,
};
export default decorate(Condition);

View File

@ -0,0 +1,14 @@
.condition {
// flex-grow: 1;
display: flex;
justify-content: flex-end;
align-items: center;
border: 1px solid rgba(0, 0, 0, 0.2);
padding: 0.25em;
}
.operator {
margin: 0 0.5em;
text-align: center;
text-align-last: center;
}

View File

@ -10,7 +10,10 @@ const Number = ({
value, value,
}) => ( }) => (
<label> <label>
<span className="text">{label}</span> <span className="text">
{label}
:
</span>
<div className="invisible-separator" /> <div className="invisible-separator" />
{ {
options options
@ -21,7 +24,7 @@ const Number = ({
))} ))}
</select> </select>
) )
: <input name={name} size={5} type="text" value={value} /> : <input name={name} type="text" value={value} />
} }
</label> </label>
); );

View File

@ -1,11 +1,15 @@
import Actions from './actions';
import Bool from './bool'; import Bool from './bool';
import Condition from './condition';
import Number from './number'; import Number from './number';
import String from './string'; import String from './string';
// eslint-disable-next-line import/prefer-default-export // eslint-disable-next-line import/prefer-default-export
export function propertyComponents() { export function propertyComponents() {
return { return {
actions: Actions,
bool: Bool, bool: Bool,
condition: Condition,
number: Number, number: Number,
string: String, string: String,
}; };

View File

@ -10,7 +10,10 @@ const String = ({
value, value,
}) => ( }) => (
<label> <label>
<span className="text">{label}</span> <span className="text">
{label}
:
</span>
<div className="invisible-separator" /> <div className="invisible-separator" />
{ {
options options
@ -21,7 +24,7 @@ const String = ({
))} ))}
</select> </select>
) )
: <input name={name} size={5} type="text" value={value} /> : <input name={name} type="text" value={value} />
} }
</label> </label>
); );

View File

@ -59,8 +59,11 @@ const makeTabSelector = (type) => createSelector(
const Trait = lookupTrait(type); const Trait = lookupTrait(type);
const params = Trait ? {...Trait.defaultParams(), ...paramsRaw} : paramsRaw; const params = Trait ? {...Trait.defaultParams(), ...paramsRaw} : paramsRaw;
const state = Trait ? {...Trait.defaultState(), ...stateRaw} : stateRaw; const state = Trait ? {...Trait.defaultState(), ...stateRaw} : stateRaw;
const paramsDescription = Trait?.describeParams() || {};
const stateDescription = Trait?.describeState() || {}; const stateDescription = Trait?.describeState() || {};
const Components = Object.values(mapObject(stateDescription, (description, key) => { const renderComponents = (description, values) => (
// eslint-disable-next-line no-shadow
Object.values(mapObject(description, (description, key) => {
const {label, options, type: componentType} = description; const {label, options, type: componentType} = description;
const Component = PropertyComponents[componentType]; const Component = PropertyComponents[componentType];
return Component return Component
@ -70,14 +73,20 @@ const makeTabSelector = (type) => createSelector(
label={label} label={label}
name={key} name={key}
options={options} options={options}
value={state[key]} value={values[key]}
/> />
) )
: null; : null;
})); }))
);
return TraitComponent return TraitComponent
? <TraitComponent params={params} state={state} /> ? <TraitComponent params={params} state={state} />
: <form>{Components}</form>; : (
<form>
{renderComponents(paramsDescription, params)}
{renderComponents(stateDescription, state)}
</form>
);
}, },
); );

View File

@ -19,7 +19,8 @@ form {
} }
.invisible-separator { .invisible-separator {
margin: 0.25em 0; flex-grow: 1;
margin: 1em;
} }
.text { .text {