feat: params, actions, etc
This commit is contained in:
parent
a6aeb66809
commit
c88c5e5c02
84
src/client/components/properties/actions.jsx
Normal file
84
src/client/components/properties/actions.jsx
Normal 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);
|
3
src/client/components/properties/actions.raw.scss
Normal file
3
src/client/components/properties/actions.raw.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.args {
|
||||
margin-left: 2em;
|
||||
}
|
|
@ -9,7 +9,10 @@ const Bool = ({
|
|||
value,
|
||||
}) => (
|
||||
<label>
|
||||
<span className="text">{label}</span>
|
||||
<span className="text">
|
||||
{label}
|
||||
:
|
||||
</span>
|
||||
<div className="invisible-separator" />
|
||||
<input name={name} type="checkbox" checked={value} />
|
||||
</label>
|
||||
|
|
94
src/client/components/properties/condition.jsx
Normal file
94
src/client/components/properties/condition.jsx
Normal 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);
|
14
src/client/components/properties/condition.raw.scss
Normal file
14
src/client/components/properties/condition.raw.scss
Normal 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;
|
||||
}
|
|
@ -10,7 +10,10 @@ const Number = ({
|
|||
value,
|
||||
}) => (
|
||||
<label>
|
||||
<span className="text">{label}</span>
|
||||
<span className="text">
|
||||
{label}
|
||||
:
|
||||
</span>
|
||||
<div className="invisible-separator" />
|
||||
{
|
||||
options
|
||||
|
@ -21,7 +24,7 @@ const Number = ({
|
|||
))}
|
||||
</select>
|
||||
)
|
||||
: <input name={name} size={5} type="text" value={value} />
|
||||
: <input name={name} type="text" value={value} />
|
||||
}
|
||||
</label>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import Actions from './actions';
|
||||
import Bool from './bool';
|
||||
import Condition from './condition';
|
||||
import Number from './number';
|
||||
import String from './string';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function propertyComponents() {
|
||||
return {
|
||||
actions: Actions,
|
||||
bool: Bool,
|
||||
condition: Condition,
|
||||
number: Number,
|
||||
string: String,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,10 @@ const String = ({
|
|||
value,
|
||||
}) => (
|
||||
<label>
|
||||
<span className="text">{label}</span>
|
||||
<span className="text">
|
||||
{label}
|
||||
:
|
||||
</span>
|
||||
<div className="invisible-separator" />
|
||||
{
|
||||
options
|
||||
|
@ -21,7 +24,7 @@ const String = ({
|
|||
))}
|
||||
</select>
|
||||
)
|
||||
: <input name={name} size={5} type="text" value={value} />
|
||||
: <input name={name} type="text" value={value} />
|
||||
}
|
||||
</label>
|
||||
);
|
||||
|
|
|
@ -59,25 +59,34 @@ const makeTabSelector = (type) => createSelector(
|
|||
const Trait = lookupTrait(type);
|
||||
const params = Trait ? {...Trait.defaultParams(), ...paramsRaw} : paramsRaw;
|
||||
const state = Trait ? {...Trait.defaultState(), ...stateRaw} : stateRaw;
|
||||
const paramsDescription = Trait?.describeParams() || {};
|
||||
const stateDescription = Trait?.describeState() || {};
|
||||
const Components = Object.values(mapObject(stateDescription, (description, key) => {
|
||||
const {label, options, type: componentType} = description;
|
||||
const Component = PropertyComponents[componentType];
|
||||
return Component
|
||||
? (
|
||||
<Component
|
||||
key={key}
|
||||
label={label}
|
||||
name={key}
|
||||
options={options}
|
||||
value={state[key]}
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
}));
|
||||
const renderComponents = (description, values) => (
|
||||
// eslint-disable-next-line no-shadow
|
||||
Object.values(mapObject(description, (description, key) => {
|
||||
const {label, options, type: componentType} = description;
|
||||
const Component = PropertyComponents[componentType];
|
||||
return Component
|
||||
? (
|
||||
<Component
|
||||
key={key}
|
||||
label={label}
|
||||
name={key}
|
||||
options={options}
|
||||
value={values[key]}
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
}))
|
||||
);
|
||||
return TraitComponent
|
||||
? <TraitComponent params={params} state={state} />
|
||||
: <form>{Components}</form>;
|
||||
: (
|
||||
<form>
|
||||
{renderComponents(paramsDescription, params)}
|
||||
{renderComponents(stateDescription, state)}
|
||||
</form>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ form {
|
|||
}
|
||||
|
||||
.invisible-separator {
|
||||
margin: 0.25em 0;
|
||||
flex-grow: 1;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.text {
|
||||
|
|
Loading…
Reference in New Issue
Block a user