refactor: Variant

This commit is contained in:
cha0s 2021-01-30 14:14:05 -06:00
parent a296b57c02
commit 9ecf2d082b
8 changed files with 162 additions and 124 deletions

View File

@ -1,7 +1,11 @@
import {join, relative} from 'path';
import {isInvocation, isKey} from '@avocado/behavior';
import {PropTypes, React} from '@latus/react';
import {compile, isInvocation, isKey} from '@avocado/behavior';
import {
PropTypes,
React,
useLatus,
} from '@latus/react';
import {useJsonPatcher} from '@persea/json';
import classnames from 'classnames';
@ -10,18 +14,18 @@ import Key from './expression/key';
const Expression = ({
context,
expression,
value,
path,
type,
}) => {
const latus = useLatus();
const patch = useJsonPatcher();
let i = 0;
const {ops} = expression;
const {ops} = value;
const Renderables = [];
let opsCount = 0;
let walk = context;
// eslint-disable-next-line react/destructuring-assignment
let description = context.constructor.descriptionFor(walk);
let description = context.constructor.descriptionFor(context);
const onChange = (event, value, localPath) => {
const patches = [];
const j = parseInt(relative(path, localPath).split('/')[1], 10);
@ -100,8 +104,8 @@ const Expression = ({
onChange(event, value, localPath);
}
}}
op={op}
path={opPath}
value={op.key}
/>,
);
opsCount += 1;
@ -136,15 +140,10 @@ const Expression = ({
opsCount += 1;
}
if (isKey(op)) {
if (context === walk) {
[walk] = context.get(op.key);
}
else {
walk = walk[op.key];
}
const current = compile({type: 'expression', ops: ops.slice(0, i + 1)}, latus)(context);
description = {
...context.constructor.descriptionFor(walk),
...description.children[op.key],
...context.constructor.descriptionFor(current),
...description?.children?.[op.key] || {},
};
if (description.args && (!nextOp || !isInvocation(nextOp))) {
Renderables.push(
@ -154,17 +153,16 @@ const Expression = ({
onClick={((description, opPath) => () => {
const parts = opPath.split('/');
parts.push(parseInt(parts.pop(), 10) + 1);
const invocation = {
type: 'invoke',
args: description.args.map(() => ({
type: 'literal',
value: null,
})),
};
patch({
op: 'add',
path: parts.join('/'),
value: invocation,
value: {
type: 'invoke',
args: description.args.map(() => ({
type: 'literal',
value: null,
})),
},
});
})(description, opPath)}
type="button"
@ -218,7 +216,7 @@ Expression.propTypes = {
describe: PropTypes.func,
get: PropTypes.func,
}).isRequired,
expression: PropTypes.shape({
value: PropTypes.shape({
ops: PropTypes.arrayOf(
PropTypes.shape({
key: PropTypes.string,

View File

@ -1,22 +1,33 @@
import {join} from 'path';
import {PropTypes, React} from '@latus/react';
import {useJsonPatcher} from '@persea/json';
import Literal from '../literal';
import Key from './key';
import Variant from '../variant';
const Invocation = ({
context,
description,
Expression,
onChange,
op,
path,
}) => {
// eslint-disable-next-line react/destructuring-assignment
const contextDescription = context.describe();
const patch = useJsonPatcher();
const argComponent = (arg, i) => {
const argPath = join(path, 'args', i.toString());
const options = description?.args?.[i].options;
const type = description?.args?.[i].type || 'undefined';
return (
<Variant
context={context}
onChange={onChange}
op={op}
options={options}
path={argPath}
type={type}
value={arg}
/>
);
};
return (
<div className="invocation">
(
@ -26,55 +37,7 @@ const Invocation = ({
key={join(path, 'args', i.toString())}
className="invocation__arg"
>
{
'literal' === arg.type
? (
<>
<Key
childrenDescription={{
'<literal>': {
label: '',
type: arg.type,
},
...contextDescription.children,
}}
onChange={(event, value) => {
patch({
op: 'replace',
path: join(path, 'args', i.toString()),
value: {
type: 'expression',
ops: [
{
type: 'key',
key: value,
},
],
},
});
}}
op={op}
path={path}
/>
<Literal
onChange={onChange}
options={description.args[i].options}
path={join(path, 'args', i.toString(), 'value')}
type={description.args[i].type}
value={arg}
/>
</>
)
: (
<Expression
context={context}
expression={arg}
onChange={onChange}
path={join(path, 'args', i.toString())}
type={description.args[i].type}
/>
)
}
{argComponent(arg, i)}
{i < op.args.length - 1 && <span className="invocation__arg-sep">, </span>}
</div>
))
@ -85,9 +48,7 @@ const Invocation = ({
};
Invocation.propTypes = {
context: PropTypes.shape({
describe: PropTypes.func,
}).isRequired,
context: PropTypes.shape({}).isRequired,
description: PropTypes.shape({
args: PropTypes.arrayOf(
PropTypes.shape({
@ -96,7 +57,6 @@ Invocation.propTypes = {
}),
),
}).isRequired,
Expression: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
op: PropTypes.shape({
args: PropTypes.arrayOf(PropTypes.any),

View File

@ -5,8 +5,8 @@ import {PropTypes, React} from '@latus/react';
const Key = ({
childrenDescription,
onChange,
op,
path,
value,
}) => {
const options = Object.entries(childrenDescription)
.map(([key]) => (
@ -18,7 +18,7 @@ const Key = ({
onChange={(event) => {
onChange(event, event.target.value, join(path, 'key'));
}}
value={op.key}
value={value}
>
{options}
</select>
@ -29,10 +29,8 @@ const Key = ({
Key.propTypes = {
childrenDescription: PropTypes.shape({}).isRequired,
onChange: PropTypes.func.isRequired,
op: PropTypes.shape({
key: PropTypes.string,
}).isRequired,
path: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
};
export default Key;

View File

@ -6,7 +6,7 @@ import Expression from './expression';
const Expressions = ({
context,
expressions: {expressions},
value: {expressions},
path,
}) => (
<div className="expressions">
@ -14,7 +14,7 @@ const Expressions = ({
expressions.map((expression, i) => (
<Expression
context={context}
expression={expression}
value={expression}
key={join(path, 'expressions', i.toString())}
path={join(path, 'expressions', i.toString())}
type="void"
@ -26,7 +26,7 @@ const Expressions = ({
Expressions.propTypes = {
context: PropTypes.shape({}).isRequired,
expressions: PropTypes.shape({
value: PropTypes.shape({
type: PropTypes.string,
expressions: PropTypes.arrayOf(
PropTypes.shape({}),

View File

@ -1,17 +1,29 @@
import {join} from 'path';
import {PropTypes, React} from '@latus/react';
import {
Number,
} from '@persea/core';
import {JsonComponent} from '@persea/json';
import {
JsonComponent,
useJsonPatcher,
} from '@persea/json';
import Key from './expression/key';
const Literal = ({
context,
onChange,
path,
type,
op,
options,
value: {value},
value,
}) => {
const valueComponent = (path, type, value) => {
// eslint-disable-next-line react/destructuring-assignment
const contextDescription = context.describe();
const patch = useJsonPatcher();
const valueComponent = (path, type, {value}) => {
switch (type) {
case 'number':
return (
@ -19,7 +31,7 @@ const Literal = ({
onChange={(event, value) => {
onChange(event, value, path);
}}
value={value}
value={null === value ? 0 : value}
/>
);
case 'string':
@ -29,7 +41,7 @@ const Literal = ({
onChange={(event) => {
onChange(event, value, path);
}}
value={value || ''}
value={null === value ? '' : value}
/>
);
case 'object':
@ -38,7 +50,7 @@ const Literal = ({
onChange={(event, value) => {
onChange(event, value, path);
}}
json={value}
json={null === value ? {} : value}
/>
);
default:
@ -46,32 +58,58 @@ const Literal = ({
}
};
return (
<div className="literal">
{
options.length > 0
? (
<select
onChange={(event) => {
onChange(event, value, path);
}}
value={options.findIndex(({value: optionValue}) => value === optionValue)}
>
{
Object.entries(options)
.map(([key, {label}]) => (
<option
key={label}
value={key}
>
{label}
</option>
))
}
</select>
)
: valueComponent(path, type, value)
}
</div>
<>
<Key
childrenDescription={{
'<literal>': {
label: '',
type: value.type,
},
...contextDescription.children,
}}
onChange={(event, value) => {
patch({
op: 'replace',
path,
value: {
type: 'expression',
ops: [
{
type: 'key',
key: value,
},
],
},
});
}}
op={op}
path={path}
/>
<div className="literal">
{
options.length > 0
? (
<select
onChange={onChange}
value={options.findIndex(({value: optionValue}) => value.value === optionValue)}
>
{
Object.entries(options)
.map(([key, {label}]) => (
<option
key={label}
value={key}
>
{label}
</option>
))
}
</select>
)
: valueComponent(join(path, 'value'), type, value)
}
</div>
</>
);
};
@ -80,8 +118,14 @@ Literal.defaultProps = {
};
Literal.propTypes = {
context: PropTypes.shape({
describe: PropTypes.func,
}).isRequired,
type: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
op: PropTypes.shape({
key: PropTypes.string,
}).isRequired,
options: PropTypes.arrayOf(PropTypes.any),
path: PropTypes.string.isRequired,
// eslint-disable-next-line react/forbid-prop-types

View File

@ -0,0 +1,30 @@
import {
PropTypes,
React,
useLatus,
} from '@latus/react';
const Variant = (props) => {
const {value} = props;
const {type} = value;
const latus = useLatus();
const BehaviorControllers = latus.get('%behavior-controllers');
const Controller = BehaviorControllers[type];
return (
<Controller
value={value}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);
};
Variant.propTypes = {
onChange: PropTypes.func.isRequired,
path: PropTypes.string.isRequired,
value: PropTypes.shape({
type: PropTypes.string,
}).isRequired,
};
export default Variant;

View File

@ -2,6 +2,9 @@ import {basename, extname} from 'path';
import {camelCase} from '@latus/core';
import Expression from './behavior-components/expression';
import Expressions from './behavior-components/expressions';
import Literal from './behavior-components/literal';
import EntityResourceController from './resource-controllers/entity';
export {EntityResourceController};
@ -11,6 +14,11 @@ export default {
'@latus/core/starting': async (latus) => {
const TraitRenderers = latus.invokeReduce('@persea/entity/trait-components');
latus.set('%trait-components', TraitRenderers);
latus.set('%behavior-controllers', {
expression: Expression,
expressions: Expressions,
literal: Literal,
});
},
'@persea/core/resource-controllers': () => [
EntityResourceController,

View File

@ -14,7 +14,7 @@ const Alive = ({
Death actions
<Expressions
context={entity.context}
expressions={json.params.deathActions}
value={json.params.deathActions}
path={join(path, 'params/deathActions')}
/>
</label>