refactor: type rendering
This commit is contained in:
parent
c8ddacd5dd
commit
0c8ec0b1d2
|
@ -1,10 +1,23 @@
|
||||||
import {hot} from 'react-hot-loader';
|
import {hot} from 'react-hot-loader';
|
||||||
import React from 'react';
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
|
import TypeRenderersContext from '~/client/context/typeRenderers';
|
||||||
|
import {all as allTypeRenderers} from '~/client/type-renderers.scwp';
|
||||||
|
|
||||||
import Entity from './entity';
|
import Entity from './entity';
|
||||||
|
|
||||||
const Persea = () => (
|
const typeRenderMap = () => Object.values(allTypeRenderers()).reduce((r, M) => {
|
||||||
<Entity />
|
const {default: {type, Component}} = M;
|
||||||
);
|
return {...r, [type]: Component};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const Persea = () => {
|
||||||
|
const [typeRenderers] = useState(typeRenderMap());
|
||||||
|
return (
|
||||||
|
<TypeRenderersContext.Provider value={typeRenderers}>
|
||||||
|
<Entity />
|
||||||
|
</TypeRenderersContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default hot(module)(Persea);
|
export default hot(module)(Persea);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import {createSelector} from '@reduxjs/toolkit';
|
import {createSelector} from '@reduxjs/toolkit';
|
||||||
import {deregisterHooks, registerHooks} from 'scwp';
|
import {deregisterHooks, registerHooks} from 'scwp';
|
||||||
|
|
||||||
import {all as allTypeRenderers} from './types/type-renderers.scwp';
|
import Value from './types/value.type-renderer';
|
||||||
|
|
||||||
const SCROLL_MAG = 80;
|
const SCROLL_MAG = 80;
|
||||||
|
|
||||||
|
@ -43,20 +43,10 @@ const ensureTraitComponents = () => {
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let TypeRenderers;
|
|
||||||
const ensureTypeRenderers = () => {
|
|
||||||
if (!TypeRenderers) {
|
|
||||||
TypeRenderers = Object.values(allTypeRenderers()).reduce((r, M) => {
|
|
||||||
const {default: {type, Component}} = M;
|
|
||||||
return {...r, [type]: Component};
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeTabSelector = (context, type) => createSelector(
|
const makeTabSelector = (context, type) => createSelector(
|
||||||
(_) => _,
|
(_) => _,
|
||||||
(trait) => {
|
(trait) => {
|
||||||
ensureTypeRenderers();
|
|
||||||
ensureTraitComponents();
|
ensureTraitComponents();
|
||||||
const {params: paramsRaw, state: stateRaw} = trait;
|
const {params: paramsRaw, state: stateRaw} = trait;
|
||||||
const {[type]: TraitComponent} = TraitComponents;
|
const {[type]: TraitComponent} = TraitComponents;
|
||||||
|
@ -68,27 +58,19 @@ const makeTabSelector = (context, type) => createSelector(
|
||||||
const renderComponents = (description, values) => (
|
const renderComponents = (description, values) => (
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
Object.values(mapObject(description, (description, key) => {
|
Object.values(mapObject(description, (description, key) => {
|
||||||
const {label, options, type: componentType} = description;
|
const {label, options} = description;
|
||||||
const Component = TypeRenderers[componentType];
|
|
||||||
return (
|
return (
|
||||||
<label>
|
<label key={key}>
|
||||||
<span className="text">
|
<span className="text">
|
||||||
{label}
|
{label}
|
||||||
:
|
:
|
||||||
</span>
|
</span>
|
||||||
<div className="invisible-separator" />
|
<div className="invisible-separator" />
|
||||||
{
|
<Value.Component
|
||||||
Component
|
context={context}
|
||||||
? (
|
options={options}
|
||||||
<Component
|
value={values[key]}
|
||||||
context={context}
|
/>
|
||||||
key={key}
|
|
||||||
options={options}
|
|
||||||
value={values[key]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: <div className="unrenderable" title="No renderer found!">?</div>
|
|
||||||
}
|
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}))
|
}))
|
||||||
|
@ -187,6 +169,7 @@ const Traits = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
Traits.propTypes = {
|
Traits.propTypes = {
|
||||||
|
context: PropTypes.shape({}).isRequired,
|
||||||
traits: PropTypes.shape({}).isRequired,
|
traits: PropTypes.shape({}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,10 @@ const Actions = ({
|
||||||
{
|
{
|
||||||
value && value.traversals.length > 0 && (
|
value && value.traversals.length > 0 && (
|
||||||
value.traversals.map(
|
value.traversals.map(
|
||||||
(traversal) => (
|
(traversal, i) => (
|
||||||
<li>
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
<Value.Component
|
<li key={i}>
|
||||||
context={context}
|
<Value.Component context={context} value={traversal} />
|
||||||
type="any"
|
|
||||||
value={traversal}
|
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,6 @@
|
||||||
text-align-last: center;
|
text-align-last: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.operand .steps {
|
.operand .traversal {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ import React from 'react';
|
||||||
import propertyPropTypes from './property-prop-types';
|
import propertyPropTypes from './property-prop-types';
|
||||||
import Value from './value.type-renderer';
|
import Value from './value.type-renderer';
|
||||||
|
|
||||||
const renderOperand = (context, operand, type) => (
|
const renderOperand = (context, operand) => (
|
||||||
<span className="operand">
|
<span className="operand">
|
||||||
<Value.Component context={context} type={type} value={operand} />
|
<Value.Component context={context} value={operand} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ const Condition = ({
|
||||||
case '>=':
|
case '>=':
|
||||||
case '<':
|
case '<':
|
||||||
case '<=': {
|
case '<=': {
|
||||||
const type = -1 !== ['is', 'isnt'].indexOf(value.operator) ? 'any' : 'number';
|
|
||||||
return value.operands.slice(1).reduce(
|
return value.operands.slice(1).reduce(
|
||||||
(r, operand) => (
|
(r, operand) => (
|
||||||
<>
|
<>
|
||||||
|
@ -47,10 +46,10 @@ const Condition = ({
|
||||||
<select className="operator" readOnly value={value.operator}>
|
<select className="operator" readOnly value={value.operator}>
|
||||||
{binaryOps.map(([k, v]) => <option value={k}>{v}</option>)}
|
{binaryOps.map(([k, v]) => <option value={k}>{v}</option>)}
|
||||||
</select>
|
</select>
|
||||||
{renderOperand(context, operand, type)}
|
{renderOperand(context, operand)}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
renderOperand(context, value.operands[0], type),
|
renderOperand(context, value.operands[0]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
9
src/client/components/types/literal.raw.scss
Normal file
9
src/client/components/types/literal.raw.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
:scope {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
}
|
58
src/client/components/types/literal.type-renderer.jsx
Normal file
58
src/client/components/types/literal.type-renderer.jsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import {compose} from '@avocado/core';
|
||||||
|
import contempo from 'contempo';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import useTypeRenderers from '~/client/hooks/useTypeRenderers';
|
||||||
|
|
||||||
|
import propertyPropTypes from './property-prop-types';
|
||||||
|
import {contextStepsList} from './steps-lists';
|
||||||
|
import {typeFromLiteral} from './typing';
|
||||||
|
|
||||||
|
const decorate = compose(
|
||||||
|
contempo(require('./literal.raw.scss')),
|
||||||
|
);
|
||||||
|
|
||||||
|
const Literal = ({
|
||||||
|
context,
|
||||||
|
value,
|
||||||
|
}) => {
|
||||||
|
const typeRenderers = useTypeRenderers();
|
||||||
|
const type = typeFromLiteral(value.value);
|
||||||
|
const stepsList = contextStepsList(context, type);
|
||||||
|
const tierOptions = Object.keys(stepsList.reduce((r, optionSteps) => {
|
||||||
|
if (!optionSteps[0] || !optionSteps[0].key) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return {...r, [optionSteps[0].key]: true};
|
||||||
|
}, {}));
|
||||||
|
tierOptions.splice(tierOptions.indexOf('context'), 1);
|
||||||
|
tierOptions.push('<literal>');
|
||||||
|
return (
|
||||||
|
<div className="literal">
|
||||||
|
<select readOnly value="<literal>">
|
||||||
|
{
|
||||||
|
tierOptions
|
||||||
|
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
|
||||||
|
.map((tierOption) => <option key={tierOption}>{tierOption}</option>)
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
{
|
||||||
|
(() => {
|
||||||
|
const Component = typeRenderers[type];
|
||||||
|
return Component
|
||||||
|
? <Component value={value.value} />
|
||||||
|
: null;
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Literal.propTypes = {
|
||||||
|
...propertyPropTypes,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
type: 'literal',
|
||||||
|
Component: decorate(Literal),
|
||||||
|
};
|
|
@ -1,5 +1,7 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
context: PropTypes.shape({}),
|
||||||
options: PropTypes.shape({}),
|
options: PropTypes.shape({}),
|
||||||
|
value: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ const Routines = ({
|
||||||
entries.length > 0 && (
|
entries.length > 0 && (
|
||||||
entries.map(
|
entries.map(
|
||||||
([name, routine]) => (
|
([name, routine]) => (
|
||||||
<li className="routine">
|
<li className="routine" key={name}>
|
||||||
<label>
|
<label>
|
||||||
<span className="text">{name}</span>
|
<span className="text">{name}</span>
|
||||||
<Actions.Component context={context} value={routine.routine} />
|
<Actions.Component context={context} value={routine.routine} />
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
import {compose} from '@avocado/core';
|
|
||||||
import contempo from 'contempo';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import {contextStepsList} from './steps-lists';
|
|
||||||
import {typeFromSteps, typeFromLiteral} from './typing';
|
|
||||||
import Value from './value.type-renderer';
|
|
||||||
|
|
||||||
const decorate = compose(
|
|
||||||
contempo(require('./steps.raw.scss')),
|
|
||||||
);
|
|
||||||
|
|
||||||
const Steps = (props) => {
|
|
||||||
const {
|
|
||||||
context,
|
|
||||||
steps,
|
|
||||||
type,
|
|
||||||
value,
|
|
||||||
} = props;
|
|
||||||
let stepsType;
|
|
||||||
if ('undefined' === typeof value) {
|
|
||||||
stepsType = type;
|
|
||||||
}
|
|
||||||
else if (value.steps) {
|
|
||||||
stepsType = typeFromSteps(context, value.steps);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
stepsType = typeFromLiteral(value);
|
|
||||||
}
|
|
||||||
const stepsList = contextStepsList(context, stepsType);
|
|
||||||
return (
|
|
||||||
<div className="steps">
|
|
||||||
{steps.map((step, i) => {
|
|
||||||
switch (step.type) {
|
|
||||||
case 'key': {
|
|
||||||
const tierOptions = Object.keys(stepsList.reduce((r, optionSteps) => {
|
|
||||||
if (!optionSteps[i] || !optionSteps[i].key) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
for (let j = 0; j < i; ++j) {
|
|
||||||
if (steps[j].key !== optionSteps[j].key) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {...r, [optionSteps[i].key]: true};
|
|
||||||
}, {}));
|
|
||||||
if (0 === i) {
|
|
||||||
tierOptions.push('<literal>');
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span className="key">
|
|
||||||
<select readOnly value={steps[i].key}>
|
|
||||||
{
|
|
||||||
tierOptions
|
|
||||||
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
|
|
||||||
.map((tierOption) => <option>{tierOption}</option>)
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'invoke':
|
|
||||||
return (
|
|
||||||
<div className="invoke">
|
|
||||||
<span className="paren open">(</span>
|
|
||||||
{step.args.map((arg) => (
|
|
||||||
<div className="arg">
|
|
||||||
<label>
|
|
||||||
{arg.label}
|
|
||||||
<Value.Component
|
|
||||||
context={context}
|
|
||||||
type={
|
|
||||||
arg.steps
|
|
||||||
? typeFromSteps(context, arg.steps)
|
|
||||||
: typeFromLiteral(arg)
|
|
||||||
}
|
|
||||||
value={arg}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<span className="paren close">)</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
{
|
|
||||||
value && (
|
|
||||||
<span className="assign">
|
|
||||||
<span className="op">=</span>
|
|
||||||
<Value.Component
|
|
||||||
context={context}
|
|
||||||
type={typeFromSteps(context, steps)}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default decorate(Steps);
|
|
|
@ -37,7 +37,7 @@
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.arg .steps {
|
.arg .traversal {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
96
src/client/components/types/traversal.type-renderer.jsx
Normal file
96
src/client/components/types/traversal.type-renderer.jsx
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import {compose} from '@avocado/core';
|
||||||
|
import contempo from 'contempo';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import propertyPropTypes from './property-prop-types';
|
||||||
|
import {contextStepsList} from './steps-lists';
|
||||||
|
import {typeFromSteps} from './typing';
|
||||||
|
import Value from './value.type-renderer';
|
||||||
|
|
||||||
|
const decorate = compose(
|
||||||
|
contempo(require('./traversal.raw.scss')),
|
||||||
|
);
|
||||||
|
|
||||||
|
const Traversal = (props) => {
|
||||||
|
const {
|
||||||
|
context,
|
||||||
|
value: {steps, value},
|
||||||
|
} = props;
|
||||||
|
const stepsList = contextStepsList(context, typeFromSteps(context, steps));
|
||||||
|
return (
|
||||||
|
<div className="traversal">
|
||||||
|
{steps.map((step, i) => (
|
||||||
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
|
<span className="step" key={i}>
|
||||||
|
{
|
||||||
|
(() => {
|
||||||
|
switch (step.type) {
|
||||||
|
case 'key': {
|
||||||
|
const tierOptions = Object.keys(stepsList.reduce((r, optionSteps) => {
|
||||||
|
if (!optionSteps[i] || !optionSteps[i].key) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
for (let j = 0; j < i; ++j) {
|
||||||
|
if (steps[j].key !== optionSteps[j].key) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {...r, [optionSteps[i].key]: true};
|
||||||
|
}, {}));
|
||||||
|
if (0 === i) {
|
||||||
|
tierOptions.push('<literal>');
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span className="key">
|
||||||
|
<select readOnly value={steps[i].key}>
|
||||||
|
{
|
||||||
|
tierOptions
|
||||||
|
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
|
||||||
|
.map((tierOption) => <option key={tierOption}>{tierOption}</option>)
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case 'invoke':
|
||||||
|
return (
|
||||||
|
<div className="invoke">
|
||||||
|
<span className="paren open">(</span>
|
||||||
|
{step.args.map((arg) => (
|
||||||
|
<div className="arg" key={JSON.stringify(arg)}>
|
||||||
|
<label>
|
||||||
|
{arg.label}
|
||||||
|
<Value.Component context={context} value={arg} />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<span className="paren close">)</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
{
|
||||||
|
value && (
|
||||||
|
<span className="assign">
|
||||||
|
<span className="op">=</span>
|
||||||
|
<Value.Component context={context} value={value} />
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Traversal.propTypes = {
|
||||||
|
...propertyPropTypes,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
type: 'traversal',
|
||||||
|
Component: decorate(Traversal),
|
||||||
|
};
|
|
@ -1,33 +1,45 @@
|
||||||
import {Context} from '@avocado/behavior';
|
import {Context} from '@avocado/behavior';
|
||||||
|
|
||||||
export function typeFromLiteral(v) {
|
export function typeFromLiteral(value) {
|
||||||
if ('literal' === v.type) {
|
if ('undefined' === typeof value) {
|
||||||
const {value} = v;
|
return 'undefined';
|
||||||
if ('undefined' === typeof value) {
|
|
||||||
return 'undefined';
|
|
||||||
}
|
|
||||||
if (null === value) {
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
if ('number' === typeof value) {
|
|
||||||
return 'number';
|
|
||||||
}
|
|
||||||
if ('string' === typeof value) {
|
|
||||||
return 'string';
|
|
||||||
}
|
|
||||||
if ('boolean' === typeof value) {
|
|
||||||
return 'bool';
|
|
||||||
}
|
|
||||||
if (value.length && 2 === value.length && value instanceof Array) {
|
|
||||||
return 'vector';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ('actions' === v.type && v.traversals) {
|
if (null === value) {
|
||||||
return 'actions';
|
return 'null';
|
||||||
|
}
|
||||||
|
if ('number' === typeof value) {
|
||||||
|
return 'number';
|
||||||
|
}
|
||||||
|
if ('string' === typeof value) {
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
if ('boolean' === typeof value) {
|
||||||
|
return 'bool';
|
||||||
|
}
|
||||||
|
if (value.length && 2 === value.length && value instanceof Array) {
|
||||||
|
return 'vector';
|
||||||
}
|
}
|
||||||
return 'object';
|
return 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function typeFromValue(v) {
|
||||||
|
if ('object' === typeof v) {
|
||||||
|
if ('routines' === v.type) {
|
||||||
|
return 'routines';
|
||||||
|
}
|
||||||
|
if ('traversal' === v.type) {
|
||||||
|
return 'traversal';
|
||||||
|
}
|
||||||
|
if ('actions' === v.type && v.traversals) {
|
||||||
|
return 'actions';
|
||||||
|
}
|
||||||
|
if ('literal' === v.type) {
|
||||||
|
return 'literal';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export function typeFromSteps(context, steps) {
|
export function typeFromSteps(context, steps) {
|
||||||
if (!steps || 0 === steps.length) {
|
if (!steps || 0 === steps.length) {
|
||||||
return 'undefined';
|
return 'undefined';
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
.fn {
|
.fn {
|
||||||
text-align-last: right;
|
text-align-last: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.literal {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.literal select {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,66 +3,10 @@ import contempo from 'contempo';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import useTypeRenderers from '~/client/hooks/useTypeRenderers';
|
||||||
|
import {typeFromLiteral, typeFromValue} from '~/client/components/types/typing';
|
||||||
|
|
||||||
import propertyPropTypes from './property-prop-types';
|
import propertyPropTypes from './property-prop-types';
|
||||||
import Steps from './steps';
|
|
||||||
import {contextStepsList} from './steps-lists';
|
|
||||||
import {typeFromLiteral} from './typing';
|
|
||||||
|
|
||||||
let TypeRenderers;
|
|
||||||
const ensureTypeRenderers = () => {
|
|
||||||
if (!TypeRenderers) {
|
|
||||||
const {all: allTypeRenderers} = require('./type-renderers.scwp');
|
|
||||||
TypeRenderers = Object.values(allTypeRenderers()).reduce((r, M) => {
|
|
||||||
const {default: {type, Component}} = M;
|
|
||||||
return {...r, [type]: Component};
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderValue = (context, type, value) => {
|
|
||||||
ensureTypeRenderers();
|
|
||||||
switch (value.type) {
|
|
||||||
case 'traversal': {
|
|
||||||
return <Steps context={context} steps={value.steps} type={type} value={value.value} />;
|
|
||||||
}
|
|
||||||
case 'actions': {
|
|
||||||
const Component = TypeRenderers.actions;
|
|
||||||
return <Component context={context} value={value} />;
|
|
||||||
}
|
|
||||||
case 'literal': {
|
|
||||||
const stepsList = contextStepsList(context, type);
|
|
||||||
const tierOptions = Object.keys(stepsList.reduce((r, optionSteps) => {
|
|
||||||
if (!optionSteps[0] || !optionSteps[0].key) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
return {...r, [optionSteps[0].key]: true};
|
|
||||||
}, {}));
|
|
||||||
tierOptions.splice(tierOptions.indexOf('context'), 1);
|
|
||||||
tierOptions.push('<literal>');
|
|
||||||
return (
|
|
||||||
<div className="literal">
|
|
||||||
<select readOnly value="<literal>">
|
|
||||||
{
|
|
||||||
tierOptions
|
|
||||||
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
|
|
||||||
.map((tierOption) => <option>{tierOption}</option>)
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
{
|
|
||||||
(() => {
|
|
||||||
const Component = TypeRenderers['any' === type ? typeFromLiteral(value) : type];
|
|
||||||
return Component
|
|
||||||
? <Component value={value.value} />
|
|
||||||
: null;
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
contempo(require('./value.raw.scss')),
|
contempo(require('./value.raw.scss')),
|
||||||
|
@ -70,9 +14,23 @@ const decorate = compose(
|
||||||
|
|
||||||
const Value = ({
|
const Value = ({
|
||||||
context,
|
context,
|
||||||
type,
|
options,
|
||||||
value,
|
value,
|
||||||
}) => <span className="value">{renderValue(context, type, value)}</span>;
|
}) => {
|
||||||
|
const typeRenderers = useTypeRenderers();
|
||||||
|
const fromValue = typeFromValue(value);
|
||||||
|
const type = 'undefined' !== typeof fromValue ? fromValue : typeFromLiteral(value);
|
||||||
|
const Component = typeRenderers[type];
|
||||||
|
return (
|
||||||
|
<span className="value">
|
||||||
|
{
|
||||||
|
Component
|
||||||
|
? <Component context={context} options={options} value={value} />
|
||||||
|
: <div className="unrenderable" title={`No renderer for '${type}' found!`}>?</div>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Value.propTypes = {
|
Value.propTypes = {
|
||||||
...propertyPropTypes,
|
...propertyPropTypes,
|
||||||
|
|
3
src/client/context/typeRenderers.js
Normal file
3
src/client/context/typeRenderers.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import {createContext} from 'react';
|
||||||
|
|
||||||
|
export default createContext({});
|
5
src/client/hooks/useTypeRenderers.js
Normal file
5
src/client/hooks/useTypeRenderers.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import {useContext} from 'react';
|
||||||
|
|
||||||
|
import TypeRenderersContext from '~/client/context/typeRenderers';
|
||||||
|
|
||||||
|
export default () => useContext(TypeRenderersContext);
|
Loading…
Reference in New Issue
Block a user