refactor: Variant
This commit is contained in:
parent
a296b57c02
commit
9ecf2d082b
|
@ -1,7 +1,11 @@
|
||||||
import {join, relative} from 'path';
|
import {join, relative} from 'path';
|
||||||
|
|
||||||
import {isInvocation, isKey} from '@avocado/behavior';
|
import {compile, isInvocation, isKey} from '@avocado/behavior';
|
||||||
import {PropTypes, React} from '@latus/react';
|
import {
|
||||||
|
PropTypes,
|
||||||
|
React,
|
||||||
|
useLatus,
|
||||||
|
} from '@latus/react';
|
||||||
import {useJsonPatcher} from '@persea/json';
|
import {useJsonPatcher} from '@persea/json';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
@ -10,18 +14,18 @@ import Key from './expression/key';
|
||||||
|
|
||||||
const Expression = ({
|
const Expression = ({
|
||||||
context,
|
context,
|
||||||
expression,
|
value,
|
||||||
path,
|
path,
|
||||||
type,
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
|
const latus = useLatus();
|
||||||
const patch = useJsonPatcher();
|
const patch = useJsonPatcher();
|
||||||
let i = 0;
|
let i = 0;
|
||||||
const {ops} = expression;
|
const {ops} = value;
|
||||||
const Renderables = [];
|
const Renderables = [];
|
||||||
let opsCount = 0;
|
let opsCount = 0;
|
||||||
let walk = context;
|
|
||||||
// eslint-disable-next-line react/destructuring-assignment
|
// 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 onChange = (event, value, localPath) => {
|
||||||
const patches = [];
|
const patches = [];
|
||||||
const j = parseInt(relative(path, localPath).split('/')[1], 10);
|
const j = parseInt(relative(path, localPath).split('/')[1], 10);
|
||||||
|
@ -100,8 +104,8 @@ const Expression = ({
|
||||||
onChange(event, value, localPath);
|
onChange(event, value, localPath);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
op={op}
|
|
||||||
path={opPath}
|
path={opPath}
|
||||||
|
value={op.key}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
opsCount += 1;
|
opsCount += 1;
|
||||||
|
@ -136,15 +140,10 @@ const Expression = ({
|
||||||
opsCount += 1;
|
opsCount += 1;
|
||||||
}
|
}
|
||||||
if (isKey(op)) {
|
if (isKey(op)) {
|
||||||
if (context === walk) {
|
const current = compile({type: 'expression', ops: ops.slice(0, i + 1)}, latus)(context);
|
||||||
[walk] = context.get(op.key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
walk = walk[op.key];
|
|
||||||
}
|
|
||||||
description = {
|
description = {
|
||||||
...context.constructor.descriptionFor(walk),
|
...context.constructor.descriptionFor(current),
|
||||||
...description.children[op.key],
|
...description?.children?.[op.key] || {},
|
||||||
};
|
};
|
||||||
if (description.args && (!nextOp || !isInvocation(nextOp))) {
|
if (description.args && (!nextOp || !isInvocation(nextOp))) {
|
||||||
Renderables.push(
|
Renderables.push(
|
||||||
|
@ -154,17 +153,16 @@ const Expression = ({
|
||||||
onClick={((description, opPath) => () => {
|
onClick={((description, opPath) => () => {
|
||||||
const parts = opPath.split('/');
|
const parts = opPath.split('/');
|
||||||
parts.push(parseInt(parts.pop(), 10) + 1);
|
parts.push(parseInt(parts.pop(), 10) + 1);
|
||||||
const invocation = {
|
|
||||||
type: 'invoke',
|
|
||||||
args: description.args.map(() => ({
|
|
||||||
type: 'literal',
|
|
||||||
value: null,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
patch({
|
patch({
|
||||||
op: 'add',
|
op: 'add',
|
||||||
path: parts.join('/'),
|
path: parts.join('/'),
|
||||||
value: invocation,
|
value: {
|
||||||
|
type: 'invoke',
|
||||||
|
args: description.args.map(() => ({
|
||||||
|
type: 'literal',
|
||||||
|
value: null,
|
||||||
|
})),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})(description, opPath)}
|
})(description, opPath)}
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -218,7 +216,7 @@ Expression.propTypes = {
|
||||||
describe: PropTypes.func,
|
describe: PropTypes.func,
|
||||||
get: PropTypes.func,
|
get: PropTypes.func,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
expression: PropTypes.shape({
|
value: PropTypes.shape({
|
||||||
ops: PropTypes.arrayOf(
|
ops: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
key: PropTypes.string,
|
key: PropTypes.string,
|
||||||
|
|
|
@ -1,22 +1,33 @@
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
import {PropTypes, React} from '@latus/react';
|
import {PropTypes, React} from '@latus/react';
|
||||||
import {useJsonPatcher} from '@persea/json';
|
|
||||||
|
|
||||||
import Literal from '../literal';
|
import Variant from '../variant';
|
||||||
import Key from './key';
|
|
||||||
|
|
||||||
const Invocation = ({
|
const Invocation = ({
|
||||||
context,
|
context,
|
||||||
description,
|
description,
|
||||||
Expression,
|
|
||||||
onChange,
|
onChange,
|
||||||
op,
|
op,
|
||||||
path,
|
path,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line react/destructuring-assignment
|
// eslint-disable-next-line react/destructuring-assignment
|
||||||
const contextDescription = context.describe();
|
const argComponent = (arg, i) => {
|
||||||
const patch = useJsonPatcher();
|
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 (
|
return (
|
||||||
<div className="invocation">
|
<div className="invocation">
|
||||||
(
|
(
|
||||||
|
@ -26,55 +37,7 @@ const Invocation = ({
|
||||||
key={join(path, 'args', i.toString())}
|
key={join(path, 'args', i.toString())}
|
||||||
className="invocation__arg"
|
className="invocation__arg"
|
||||||
>
|
>
|
||||||
{
|
{argComponent(arg, i)}
|
||||||
'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}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{i < op.args.length - 1 && <span className="invocation__arg-sep">, </span>}
|
{i < op.args.length - 1 && <span className="invocation__arg-sep">, </span>}
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
@ -85,9 +48,7 @@ const Invocation = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
Invocation.propTypes = {
|
Invocation.propTypes = {
|
||||||
context: PropTypes.shape({
|
context: PropTypes.shape({}).isRequired,
|
||||||
describe: PropTypes.func,
|
|
||||||
}).isRequired,
|
|
||||||
description: PropTypes.shape({
|
description: PropTypes.shape({
|
||||||
args: PropTypes.arrayOf(
|
args: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
|
@ -96,7 +57,6 @@ Invocation.propTypes = {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
Expression: PropTypes.func.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
op: PropTypes.shape({
|
op: PropTypes.shape({
|
||||||
args: PropTypes.arrayOf(PropTypes.any),
|
args: PropTypes.arrayOf(PropTypes.any),
|
||||||
|
|
|
@ -5,8 +5,8 @@ import {PropTypes, React} from '@latus/react';
|
||||||
const Key = ({
|
const Key = ({
|
||||||
childrenDescription,
|
childrenDescription,
|
||||||
onChange,
|
onChange,
|
||||||
op,
|
|
||||||
path,
|
path,
|
||||||
|
value,
|
||||||
}) => {
|
}) => {
|
||||||
const options = Object.entries(childrenDescription)
|
const options = Object.entries(childrenDescription)
|
||||||
.map(([key]) => (
|
.map(([key]) => (
|
||||||
|
@ -18,7 +18,7 @@ const Key = ({
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
onChange(event, event.target.value, join(path, 'key'));
|
onChange(event, event.target.value, join(path, 'key'));
|
||||||
}}
|
}}
|
||||||
value={op.key}
|
value={value}
|
||||||
>
|
>
|
||||||
{options}
|
{options}
|
||||||
</select>
|
</select>
|
||||||
|
@ -29,10 +29,8 @@ const Key = ({
|
||||||
Key.propTypes = {
|
Key.propTypes = {
|
||||||
childrenDescription: PropTypes.shape({}).isRequired,
|
childrenDescription: PropTypes.shape({}).isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
op: PropTypes.shape({
|
|
||||||
key: PropTypes.string,
|
|
||||||
}).isRequired,
|
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Key;
|
export default Key;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import Expression from './expression';
|
||||||
|
|
||||||
const Expressions = ({
|
const Expressions = ({
|
||||||
context,
|
context,
|
||||||
expressions: {expressions},
|
value: {expressions},
|
||||||
path,
|
path,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="expressions">
|
<div className="expressions">
|
||||||
|
@ -14,7 +14,7 @@ const Expressions = ({
|
||||||
expressions.map((expression, i) => (
|
expressions.map((expression, i) => (
|
||||||
<Expression
|
<Expression
|
||||||
context={context}
|
context={context}
|
||||||
expression={expression}
|
value={expression}
|
||||||
key={join(path, 'expressions', i.toString())}
|
key={join(path, 'expressions', i.toString())}
|
||||||
path={join(path, 'expressions', i.toString())}
|
path={join(path, 'expressions', i.toString())}
|
||||||
type="void"
|
type="void"
|
||||||
|
@ -26,7 +26,7 @@ const Expressions = ({
|
||||||
|
|
||||||
Expressions.propTypes = {
|
Expressions.propTypes = {
|
||||||
context: PropTypes.shape({}).isRequired,
|
context: PropTypes.shape({}).isRequired,
|
||||||
expressions: PropTypes.shape({
|
value: PropTypes.shape({
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
expressions: PropTypes.arrayOf(
|
expressions: PropTypes.arrayOf(
|
||||||
PropTypes.shape({}),
|
PropTypes.shape({}),
|
||||||
|
|
|
@ -1,17 +1,29 @@
|
||||||
|
import {join} from 'path';
|
||||||
|
|
||||||
import {PropTypes, React} from '@latus/react';
|
import {PropTypes, React} from '@latus/react';
|
||||||
import {
|
import {
|
||||||
Number,
|
Number,
|
||||||
} from '@persea/core';
|
} from '@persea/core';
|
||||||
import {JsonComponent} from '@persea/json';
|
import {
|
||||||
|
JsonComponent,
|
||||||
|
useJsonPatcher,
|
||||||
|
} from '@persea/json';
|
||||||
|
|
||||||
|
import Key from './expression/key';
|
||||||
|
|
||||||
const Literal = ({
|
const Literal = ({
|
||||||
|
context,
|
||||||
onChange,
|
onChange,
|
||||||
path,
|
path,
|
||||||
type,
|
type,
|
||||||
|
op,
|
||||||
options,
|
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) {
|
switch (type) {
|
||||||
case 'number':
|
case 'number':
|
||||||
return (
|
return (
|
||||||
|
@ -19,7 +31,7 @@ const Literal = ({
|
||||||
onChange={(event, value) => {
|
onChange={(event, value) => {
|
||||||
onChange(event, value, path);
|
onChange(event, value, path);
|
||||||
}}
|
}}
|
||||||
value={value}
|
value={null === value ? 0 : value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'string':
|
case 'string':
|
||||||
|
@ -29,7 +41,7 @@ const Literal = ({
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
onChange(event, value, path);
|
onChange(event, value, path);
|
||||||
}}
|
}}
|
||||||
value={value || ''}
|
value={null === value ? '' : value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'object':
|
case 'object':
|
||||||
|
@ -38,7 +50,7 @@ const Literal = ({
|
||||||
onChange={(event, value) => {
|
onChange={(event, value) => {
|
||||||
onChange(event, value, path);
|
onChange(event, value, path);
|
||||||
}}
|
}}
|
||||||
json={value}
|
json={null === value ? {} : value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
|
@ -46,32 +58,58 @@ const Literal = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="literal">
|
<>
|
||||||
{
|
<Key
|
||||||
options.length > 0
|
childrenDescription={{
|
||||||
? (
|
'<literal>': {
|
||||||
<select
|
label: '',
|
||||||
onChange={(event) => {
|
type: value.type,
|
||||||
onChange(event, value, path);
|
},
|
||||||
}}
|
...contextDescription.children,
|
||||||
value={options.findIndex(({value: optionValue}) => value === optionValue)}
|
}}
|
||||||
>
|
onChange={(event, value) => {
|
||||||
{
|
patch({
|
||||||
Object.entries(options)
|
op: 'replace',
|
||||||
.map(([key, {label}]) => (
|
path,
|
||||||
<option
|
value: {
|
||||||
key={label}
|
type: 'expression',
|
||||||
value={key}
|
ops: [
|
||||||
>
|
{
|
||||||
{label}
|
type: 'key',
|
||||||
</option>
|
key: value,
|
||||||
))
|
},
|
||||||
}
|
],
|
||||||
</select>
|
},
|
||||||
)
|
});
|
||||||
: valueComponent(path, type, value)
|
}}
|
||||||
}
|
op={op}
|
||||||
</div>
|
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 = {
|
Literal.propTypes = {
|
||||||
|
context: PropTypes.shape({
|
||||||
|
describe: PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
op: PropTypes.shape({
|
||||||
|
key: PropTypes.string,
|
||||||
|
}).isRequired,
|
||||||
options: PropTypes.arrayOf(PropTypes.any),
|
options: PropTypes.arrayOf(PropTypes.any),
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
// eslint-disable-next-line react/forbid-prop-types
|
||||||
|
|
30
packages/entity/src/behavior-components/variant.jsx
Normal file
30
packages/entity/src/behavior-components/variant.jsx
Normal 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;
|
|
@ -2,6 +2,9 @@ import {basename, extname} from 'path';
|
||||||
|
|
||||||
import {camelCase} from '@latus/core';
|
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';
|
import EntityResourceController from './resource-controllers/entity';
|
||||||
|
|
||||||
export {EntityResourceController};
|
export {EntityResourceController};
|
||||||
|
@ -11,6 +14,11 @@ export default {
|
||||||
'@latus/core/starting': async (latus) => {
|
'@latus/core/starting': async (latus) => {
|
||||||
const TraitRenderers = latus.invokeReduce('@persea/entity/trait-components');
|
const TraitRenderers = latus.invokeReduce('@persea/entity/trait-components');
|
||||||
latus.set('%trait-components', TraitRenderers);
|
latus.set('%trait-components', TraitRenderers);
|
||||||
|
latus.set('%behavior-controllers', {
|
||||||
|
expression: Expression,
|
||||||
|
expressions: Expressions,
|
||||||
|
literal: Literal,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
'@persea/core/resource-controllers': () => [
|
'@persea/core/resource-controllers': () => [
|
||||||
EntityResourceController,
|
EntityResourceController,
|
||||||
|
|
|
@ -14,7 +14,7 @@ const Alive = ({
|
||||||
Death actions
|
Death actions
|
||||||
<Expressions
|
<Expressions
|
||||||
context={entity.context}
|
context={entity.context}
|
||||||
expressions={json.params.deathActions}
|
value={json.params.deathActions}
|
||||||
path={join(path, 'params/deathActions')}
|
path={join(path, 'params/deathActions')}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user