refactor: type renderers

This commit is contained in:
cha0s 2020-06-18 04:29:29 -05:00
parent 3f1a94545b
commit c84caf601e
13 changed files with 148 additions and 142 deletions

View File

@ -1,16 +0,0 @@
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,
};
}

View File

@ -1,5 +1,5 @@
import {compose, mapObject} from '@avocado/core';
import {all} from '@avocado/entity/trait/trait-components.scwp';
import {all as allTraitComponents} from '@avocado/entity/trait/trait-components.scwp';
import {lookupTrait} from '@avocado/entity';
import contempo from 'contempo';
import {produce} from 'immer';
@ -15,6 +15,8 @@ import {
import {createSelector} from '@reduxjs/toolkit';
import {deregisterHooks, invokeHookFlat, registerHooks} from 'scwp';
import {all as allTypeRenderers} from './types/type-renderers.scwp';
const SCROLL_MAG = 80;
const decorate = compose(
@ -35,24 +37,26 @@ const decorate = compose(
let TraitComponents;
const ensureTraitComponents = () => {
if (!TraitComponents) {
TraitComponents = Object.values(all()).reduce((r, M) => {
TraitComponents = Object.values(allTraitComponents()).reduce((r, M) => {
const {default: TraitComponent} = M;
return {...r, [TraitComponent.type]: TraitComponent};
}, {});
}
};
let PropertyComponents;
const ensurePropertyComponents = () => {
if (!PropertyComponents) {
const results = invokeHookFlat('propertyComponents');
PropertyComponents = results.reduce((r, map) => ({...r, ...map}), {});
let TypeRenderers;
const ensureTypeRenderers = () => {
if (!TypeRenderers) {
TypeRenderers = Object.values(allTypeRenderers()).reduce((r, M) => {
const {default: {type, component}} = M;
return {...r, [type]: component};
}, {});
}
};
const makeTabSelector = (type) => createSelector(
(_) => _,
(trait) => {
ensurePropertyComponents();
ensureTypeRenderers();
ensureTraitComponents();
const {params: paramsRaw, state: stateRaw} = trait;
const {[type]: TraitComponent} = TraitComponents;
@ -65,16 +69,23 @@ const makeTabSelector = (type) => createSelector(
// eslint-disable-next-line no-shadow
Object.values(mapObject(description, (description, key) => {
const {label, options, type: componentType} = description;
const Component = PropertyComponents[componentType];
const Component = TypeRenderers[componentType];
return Component
? (
<Component
key={key}
label={label}
name={key}
options={options}
value={values[key]}
/>
<label>
<span className="text">
{label}
:
</span>
<div className="invisible-separator" />
<Component
key={key}
label={label}
name={key}
options={options}
value={values[key]}
/>
</label>
)
: null;
}))

View File

@ -0,0 +1,41 @@
import {compose} from '@avocado/core';
import classnames from 'classnames';
import contempo from 'contempo';
import PropTypes from 'prop-types';
import React from 'react';
import propertyPropTypes from './property-prop-types';
import Value from './value.type-renderer';
const decorate = compose(
contempo(require('./actions.raw.scss')),
);
const Actions = ({
name,
label,
options,
value,
}) => (
<div className="actions">
<ol>
{
value.traversals.length > 0 && (
value.traversals.map(
(traversal) => <li><Value.component value={traversal} /></li>,
)
)
}
</ol>
</div>
);
Actions.propTypes = {
...propertyPropTypes,
value: PropTypes.shape({}).isRequired,
};
export default {
type: 'actions',
component: decorate(Actions),
};

View File

@ -8,14 +8,7 @@ const Bool = ({
label,
value,
}) => (
<label>
<span className="text">
{label}
:
</span>
<div className="invisible-separator" />
<input name={name} type="checkbox" checked={value} />
</label>
<input name={name} type="checkbox" checked={value} />
);
Bool.propTypes = {
@ -23,4 +16,7 @@ Bool.propTypes = {
value: PropTypes.bool.isRequired,
};
export default Bool;
export default {
type: 'bool',
component: Bool,
};

View File

@ -49,41 +49,34 @@ const Condition = ({
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>
<span 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;
}
})()
}
</span>
);
Condition.propTypes = {
@ -91,4 +84,7 @@ Condition.propTypes = {
value: PropTypes.shape({}).isRequired,
};
export default decorate(Condition);
export default {
type: 'condition',
component: decorate(Condition),
};

View File

@ -9,12 +9,7 @@ const Number = ({
options,
value,
}) => (
<label>
<span className="text">
{label}
:
</span>
<div className="invisible-separator" />
<span className="number">
{
options
? (
@ -26,7 +21,7 @@ const Number = ({
)
: <input name={name} type="text" value={value} />
}
</label>
</span>
);
Number.propTypes = {
@ -34,4 +29,7 @@ Number.propTypes = {
value: PropTypes.number.isRequired,
};
export default Number;
export default {
type: 'number',
component: Number,
};

View File

@ -9,12 +9,7 @@ const String = ({
options,
value,
}) => (
<label>
<span className="text">
{label}
:
</span>
<div className="invisible-separator" />
<span className="string">
{
options
? (
@ -26,7 +21,7 @@ const String = ({
)
: <input name={name} type="text" value={value} />
}
</label>
</span>
);
String.propTypes = {
@ -34,4 +29,7 @@ String.propTypes = {
value: PropTypes.string.isRequired,
};
export default String;
export default {
type: 'string',
component: String,
};

View File

@ -0,0 +1,9 @@
function types(scwp) {
scwp.enterSelf();
scwp.loadSelf('scwp/autoreg', {
paths: scwp.paths,
root: scwp.root,
type: 'type-renderer',
});
}
module.exports = types;

View File

@ -13,7 +13,7 @@ const reduceStep = (r, step) => {
case 'invoke':
return (
<span className="step">
<span className="invocation"><input value={r} /></span>
<span className="fn"><input value={r} /></span>
<span className="paren open">(</span>
<div className="args">
{
@ -22,22 +22,12 @@ const reduceStep = (r, step) => {
(r2, element, i) => (
<>
{r2}
<div
className={classnames(
'arg',
step.args[i + 1].type,
)}
>
<div className={classnames('arg', step.args[i + 1].type)}>
{element}
</div>
</>
),
<div
className={classnames(
'arg',
step.args[0].type,
)}
>
<div className={classnames('arg', step.args[0].type)}>
{renderValue(step.args[0])}
</div>,
)
@ -76,56 +66,39 @@ const renderValue = (value) => {
return <span className="wrapper"><input value={value.value} /></span>;
}
return (
<>
<div className="object">
<span className="bracket open">{'{'}</span>
<pre contentEditable>
<code>
{' '}
{JSON.stringify(value.value, null, 2).slice(1).slice(0, -1).trim('\n')}
</code>
</pre>
<span className="bracket close">{'}'}</span>
</div>
</>
<div className="object">
<span className="bracket open">{'{'}</span>
<pre contentEditable>
<code>
{' '}
{JSON.stringify(value.value, null, 2).slice(1).slice(0, -1).trim('\n')}
</code>
</pre>
<span className="bracket close">{'}'}</span>
</div>
);
default:
return null;
}
};
}
const decorate = compose(
contempo(require('./actions.raw.scss')),
contempo(require('./value.raw.scss')),
);
const Actions = ({
const Value = ({
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>
);
}) => renderValue(value);
Actions.propTypes = {
Value.propTypes = {
...propertyPropTypes,
value: PropTypes.shape({}).isRequired,
};
export default decorate(Actions);
export default {
type: 'value',
component: decorate(Value),
};