From a8fabd555b3ece6b2dd97c523d4b1ee26abe477b Mon Sep 17 00:00:00 2001 From: Misode Date: Fri, 29 May 2020 01:36:52 +0200 Subject: [PATCH] Support styling --- public/index.html | 1 + public/styles/nodes.css | 35 ++++++++++++++++++++++ src/app/Sandbox.ts | 2 +- src/minecraft/nodes/RangeNode.ts | 4 +++ src/minecraft/nodes/ResourceNode.ts | 4 +++ src/minecraft/schemas/Predicates.ts | 20 ++++++------- src/nodes/AbstractNode.ts | 7 ++++- src/nodes/BooleanNode.ts | 4 +++ src/nodes/EnumNode.ts | 5 ++++ src/nodes/ListNode.ts | 8 +++-- src/nodes/MapNode.ts | 8 +++-- src/nodes/NumberNode.ts | 4 +++ src/nodes/ObjectNode.ts | 46 ++++++++++++++++++++++++----- src/nodes/ReferenceNode.ts | 18 +++++++++-- src/nodes/StringNode.ts | 4 +++ 15 files changed, 143 insertions(+), 27 deletions(-) create mode 100644 public/styles/nodes.css diff --git a/public/index.html b/public/index.html index 6fc1b4de..ea138dde 100644 --- a/public/index.html +++ b/public/index.html @@ -4,6 +4,7 @@ Document + diff --git a/public/styles/nodes.css b/public/styles/nodes.css new file mode 100644 index 00000000..d06b7c65 --- /dev/null +++ b/public/styles/nodes.css @@ -0,0 +1,35 @@ + +.node { + font-size: 18px; + font-family: Arial, Helvetica, sans-serif +} + +.list-entry:not(:last-child), +.map-entry:not(:last-child), +.object-node:not(:last-child) > .object-fields { + margin-bottom: 0.3rem; +} + +.list-entry, +.map-entry, +.object-fields { + padding-left: 0.7rem; +} + +.list-entry { + border-left: 2px solid #0b552a; +} + +.map-fields { + border-left: 2px solid #066fb4; +} + +.object-fields { + border-left: 2px solid #b9b9b9; +} +.object-node > label { + user-select: none; +} +.object-node > label.collapse { + cursor: pointer; +} diff --git a/src/app/Sandbox.ts b/src/app/Sandbox.ts index 84e9686c..7364f0f1 100644 --- a/src/app/Sandbox.ts +++ b/src/app/Sandbox.ts @@ -51,5 +51,5 @@ export const SandboxSchema = new ObjectNode({ nbt: 'hi' } }), - transform: (v) => v?.condition === 'foo' ? ({...v, test: 'hello'}) : v + transform: (v: any) => v?.condition === 'foo' ? ({...v, test: 'hello'}) : v }); diff --git a/src/minecraft/nodes/RangeNode.ts b/src/minecraft/nodes/RangeNode.ts index 7ccd20d2..af077c48 100644 --- a/src/minecraft/nodes/RangeNode.ts +++ b/src/minecraft/nodes/RangeNode.ts @@ -87,6 +87,10 @@ export class RangeNode extends AbstractNode implements StateNode ${input}` } + getClassName() { + return 'range-node' + } + static isExact(v?: IRange) { return v === undefined || typeof v === 'number' } diff --git a/src/minecraft/nodes/ResourceNode.ts b/src/minecraft/nodes/ResourceNode.ts index 4c7d8677..cab5a314 100644 --- a/src/minecraft/nodes/ResourceNode.ts +++ b/src/minecraft/nodes/ResourceNode.ts @@ -37,4 +37,8 @@ export class ResourceNode extends EnumNode { return super.renderRaw(path, value, view, options) } } + + getClassName() { + return 'enum-node' + } } diff --git a/src/minecraft/schemas/Predicates.ts b/src/minecraft/schemas/Predicates.ts index c54b4c7f..faf42b1f 100644 --- a/src/minecraft/schemas/Predicates.ts +++ b/src/minecraft/schemas/Predicates.ts @@ -53,7 +53,7 @@ SCHEMAS.register('location-predicate', new ObjectNode({ x: new RangeNode(), y: new RangeNode(), z: new RangeNode() - }), + }, {collapse: true}), biome: new ResourceNode(COLLECTIONS.get('biomes')), feature: new EnumNode(COLLECTIONS.get('structures')), dimension: new ResourceNode(COLLECTIONS.get('dimensions'), {additional: true}), @@ -61,8 +61,8 @@ SCHEMAS.register('location-predicate', new ObjectNode({ light: new RangeNode() }), smokey: new BooleanNode(), - block: new ReferenceNode('block-predicate'), - fluid: new ReferenceNode('fluid-predicate') + block: new ReferenceNode('block-predicate', {collapse: true}), + fluid: new ReferenceNode('fluid-predicate', {collapse: true}) })) SCHEMAS.register('statistic-predicate', new ObjectNode({ @@ -87,7 +87,7 @@ SCHEMAS.register('player-predicate', new ObjectNode({ ) })) -SCHEMAS.register('status-effect', new ObjectNode({ +SCHEMAS.register('status-effect-predicate', new ObjectNode({ amplifier: new RangeNode(), duration: new RangeNode(), ambient: new BooleanNode(), @@ -106,22 +106,22 @@ SCHEMAS.register('entity-predicate', new ObjectNode({ type: new StringNode(), nbt: new StringNode(), team: new StringNode(), - location: new ReferenceNode('location-predicate'), - distance: new ReferenceNode('distance-predicate'), + location: new ReferenceNode('location-predicate', {collapse: true}), + distance: new ReferenceNode('distance-predicate', {collapse: true}), flags: new ObjectNode({ is_on_fire: new BooleanNode(), is_sneaking: new BooleanNode(), is_sprinting: new BooleanNode(), is_swimming: new BooleanNode(), is_baby: new BooleanNode() - }), + }, {collapse: true}), equipment: new MapNode( new EnumNode(COLLECTIONS.get('slots')), new ReferenceNode('item-predicate') ), - // vehicle: new ReferenceNode('entity-predicate'), - // targeted_entity: new ReferenceNode('entity-predicate'), - player: new ReferenceNode('player-predicate'), + vehicle: new ReferenceNode('entity-predicate', {collapse: true}), + targeted_entity: new ReferenceNode('entity-predicate', {collapse: true}), + player: new ReferenceNode('player-predicate', {collapse: true}), fishing_hook: new ObjectNode({ in_open_water: new BooleanNode() }), diff --git a/src/nodes/AbstractNode.ts b/src/nodes/AbstractNode.ts index 938dafc1..85b63ed2 100644 --- a/src/nodes/AbstractNode.ts +++ b/src/nodes/AbstractNode.ts @@ -17,6 +17,7 @@ export interface StateNode extends INode { export type RenderOptions = { hideLabel?: boolean syncModel?: boolean + collapse?: boolean } export type NodeChildren = { @@ -82,8 +83,12 @@ export abstract class AbstractNode implements INode { const id = view.register(el => { this.mounted(el, path, view) }) - return `
${this.renderRaw(path, value, view, options)}
` + return `
+ ${this.renderRaw(path, value, view, options)} +
` } abstract renderRaw(path: Path, value: T, view: TreeView, options?: RenderOptions): string + + abstract getClassName(): string } diff --git a/src/nodes/BooleanNode.ts b/src/nodes/BooleanNode.ts index 81ccfc7e..3ff30c66 100644 --- a/src/nodes/BooleanNode.ts +++ b/src/nodes/BooleanNode.ts @@ -21,4 +21,8 @@ export class BooleanNode extends AbstractNode { False True` } + + getClassName() { + return 'boolean-node' + } } diff --git a/src/nodes/EnumNode.ts b/src/nodes/EnumNode.ts index 4f9655f2..86d2e0f6 100644 --- a/src/nodes/EnumNode.ts +++ b/src/nodes/EnumNode.ts @@ -5,6 +5,7 @@ import { Path } from '../model/Path' export class EnumNode extends AbstractNode implements StateNode { protected options: string[] + static className = 'enum-node' constructor(options: string[], mods?: NodeMods | string) { super(typeof mods === 'string' ? { @@ -29,4 +30,8 @@ export class EnumNode extends AbstractNode implements StateNode ${this.options.map(o => ``).join('')} ` } + + getClassName() { + return 'enum-node' + } } diff --git a/src/nodes/ListNode.ts b/src/nodes/ListNode.ts index 5e6874aa..2cfe7690 100644 --- a/src/nodes/ListNode.ts +++ b/src/nodes/ListNode.ts @@ -33,7 +33,7 @@ export class ListNode extends AbstractNode { }) return ` -
+
${value.map((obj, index) => { return this.renderEntry(path.push(index), obj, view) }).join('')} @@ -44,8 +44,12 @@ export class ListNode extends AbstractNode { const button = view.registerClick(el => { view.model.set(path, undefined) }) - return `
+ return `
${this.children.render(path, value, view, {hideLabel: true})}
` } + + getClassName() { + return 'list-node' + } } diff --git a/src/nodes/MapNode.ts b/src/nodes/MapNode.ts index 6db920fb..78219abd 100644 --- a/src/nodes/MapNode.ts +++ b/src/nodes/MapNode.ts @@ -37,7 +37,7 @@ export class MapNode extends AbstractNode { return ` ${this.keys.renderRaw(path, '', view, {hideLabel: true, syncModel: false})} -
+
${Object.keys(value).map(key => { return this.renderEntry(path.push(key), value[key], view) }).join('')} @@ -48,8 +48,12 @@ export class MapNode extends AbstractNode { const button = view.registerClick(el => { view.model.set(path, undefined) }) - return `
+ return `
${this.values.render(path, value, view)}
` } + + getClassName() { + return 'map-node' + } } diff --git a/src/nodes/NumberNode.ts b/src/nodes/NumberNode.ts index 5282cc50..18565a16 100644 --- a/src/nodes/NumberNode.ts +++ b/src/nodes/NumberNode.ts @@ -39,4 +39,8 @@ export class NumberNode extends AbstractNode implements StateNode${path.last()}`} ` } + + getClassName() { + return 'number-node' + } } diff --git a/src/nodes/ObjectNode.ts b/src/nodes/ObjectNode.ts index c7a19422..e2952177 100644 --- a/src/nodes/ObjectNode.ts +++ b/src/nodes/ObjectNode.ts @@ -19,15 +19,21 @@ export type FilteredChildren = { [Case]?: NestedNodeChildren } +export interface ObjectNodeMods extends NodeMods { + collapse?: boolean +} + export class ObjectNode extends AbstractNode { fields: NodeChildren cases: NestedNodeChildren filter?: string + collapse?: boolean - constructor(fields: FilteredChildren, mods?: NodeMods) { + constructor(fields: FilteredChildren, mods?: ObjectNodeMods) { super({ default: () => ({}), ...mods}) + this.collapse = mods?.collapse ?? false const {[Switch]: _switch, [Case]: _case, ..._fields} = fields this.fields = _fields this.cases = _case ?? {} @@ -46,14 +52,38 @@ export class ObjectNode extends AbstractNode { } renderRaw(path: Path, value: IObject, view: TreeView, options?: RenderOptions) { - value = value ?? {} const activeCase = this.filter ? this.cases[value[this.filter]] : {}; const activeFields = {...this.fields, ...activeCase} - return `${options?.hideLabel ? `` : ` -
`} - ${Object.keys(activeFields).map(f => { - return activeFields[f].render(path.push(f), value[f], view) - }).join('')} - ${options?.hideLabel ? `` : `
`}` + if (options?.hideLabel) { + value = value ?? {} + return this.renderFields(path, value, view, activeFields) + } else if (this.collapse || options?.collapse) { + if (value === undefined) { + const id = view.registerClick(() => view.model.set(path, this.default())) + return `` + } else { + const id = view.registerClick(() => view.model.set(path, undefined)) + return ` +
+ ${this.renderFields(path, value, view, activeFields)} +
` + } + } else { + value = value ?? {} + return ` +
+ ${this.renderFields(path, value, view, activeFields)} +
` + } + } + + renderFields(path: Path, value: IObject, view: TreeView, activeFields: NodeChildren) { + return Object.keys(activeFields).map(f => { + return activeFields[f].render(path.push(f), value[f], view) + }).join('') + } + + getClassName() { + return 'object-node' } } diff --git a/src/nodes/ReferenceNode.ts b/src/nodes/ReferenceNode.ts index eef02fcf..e0c33b31 100644 --- a/src/nodes/ReferenceNode.ts +++ b/src/nodes/ReferenceNode.ts @@ -3,11 +3,19 @@ import { TreeView } from '../view/TreeView' import { Path } from '../model/Path' import { SCHEMAS } from '../Registries' +export interface AnyNodeMods extends NodeMods { + [name: string]: any +} + export class ReferenceNode extends AbstractNode { protected reference: () => INode + options: RenderOptions - constructor(id: string, mods?: NodeMods) { + constructor(id: string, mods?: AnyNodeMods) { super(mods) + this.options = { + collapse: mods?.collapse + } this.reference = () => SCHEMAS.get(id) } @@ -20,10 +28,14 @@ export class ReferenceNode extends AbstractNode { } render(path: Path, value: any, view: TreeView, options?: RenderOptions) { - return this.reference()?.render(path, value, view, options) + return this.reference()?.render(path, value, view, {...this.options, ...options}) } renderRaw(path: Path, value: any, view: TreeView, options?: RenderOptions) { - return this.reference()?.renderRaw(path, value, view, options) + return this.reference()?.renderRaw(path, value, view, {...this.options, ...options}) + } + + getClassName() { + return '' } } diff --git a/src/nodes/StringNode.ts b/src/nodes/StringNode.ts index a501bb3e..52e3c762 100644 --- a/src/nodes/StringNode.ts +++ b/src/nodes/StringNode.ts @@ -28,4 +28,8 @@ export class StringNode extends AbstractNode implements StateNode${path.last()}`} ` } + + getClassName() { + return 'string-node' + } }