refactor: Variant
This commit is contained in:
parent
a296b57c02
commit
9ecf2d082b
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({}),
|
||||
|
|
|
@ -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
|
||||
|
|
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 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,
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user