diff --git a/src/app/app.ts b/src/app/app.ts index 9d68065e..750e642f 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -15,7 +15,6 @@ modelSelector.innerHTML = ` ` modelSelector.addEventListener('change', evt => { - console.log("hello?") if (modelSelector.value === 'sandbox') { model = sandboxModel } else { diff --git a/src/model/DataModel.ts b/src/model/DataModel.ts index 63f83562..e6cdabf0 100644 --- a/src/model/DataModel.ts +++ b/src/model/DataModel.ts @@ -1,4 +1,3 @@ -import { RootNode } from "../nodes/RootNode" import { Path } from "./Path" import { INode } from "../nodes/AbstractNode" @@ -25,6 +24,11 @@ export class DataModel { this.listeners.forEach(listener => listener.invalidated(this)) } + reset(value: any) { + this.data = value + this.invalidate() + } + get(path: Path) { let node = this.data; for (let index of path) { diff --git a/src/nodes/ListNode.ts b/src/nodes/ListNode.ts index d38b0067..5e6874aa 100644 --- a/src/nodes/ListNode.ts +++ b/src/nodes/ListNode.ts @@ -14,6 +14,14 @@ export class ListNode extends AbstractNode { this.children = values } + transform(path: Path, value: IObject[]) { + if (!(value instanceof Array)) return undefined + const res = value.map((obj, index) => + this.children.transform(path.push(index), obj) + ) + return this.transformMod(res) + } + updateModel(el: Element, path: Path, model: DataModel) { model.set(path, el.querySelector('select')?.value) } diff --git a/src/nodes/MapNode.ts b/src/nodes/MapNode.ts index 1bf1ac99..6db920fb 100644 --- a/src/nodes/MapNode.ts +++ b/src/nodes/MapNode.ts @@ -19,6 +19,15 @@ export class MapNode extends AbstractNode { this.values = values } + transform(path: Path, value: IMap) { + if (value === undefined) return undefined + let res: any = {} + Object.keys(value).forEach(f => + res[f] = this.values.transform(path.push(f), value[f]) + ) + return this.transformMod(res); + } + renderRaw(path: Path, value: IMap, view: TreeView) { value = value ?? [] const button = view.registerClick(el => { diff --git a/src/nodes/ObjectNode.ts b/src/nodes/ObjectNode.ts index c5e5e9bd..f738980c 100644 --- a/src/nodes/ObjectNode.ts +++ b/src/nodes/ObjectNode.ts @@ -36,14 +36,14 @@ export class ObjectNode extends AbstractNode { transform(path: Path, value: IObject) { if (value === undefined) return undefined - value = value ?? {} const activeCase = this.filter ? this.cases[value[this.filter]] : {}; const activeFields = {...this.fields, ...activeCase} let res: any = {} - Object.keys(activeFields).forEach(f => - res[f] = activeFields[f].transform(path.push(f), value[f]) - ) - return res; + Object.keys(activeFields).forEach(f => { + console.log(f) + return res[f] = activeFields[f].transform(path.push(f), value[f]) + }) + return this.transformMod(res); } renderRaw(path: Path, value: IObject, view: TreeView, options?: RenderOptions) { diff --git a/src/nodes/RootNode.ts b/src/nodes/RootNode.ts deleted file mode 100644 index d586f59f..00000000 --- a/src/nodes/RootNode.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NodeChildren, NodeMods } from './AbstractNode' -import { ObjectNode, IObject } from './ObjectNode' -import { Path } from '../model/Path' -import { TreeView } from '../view/TreeView' - -export class RootNode extends ObjectNode { - id: string - - constructor(id: string, fields: NodeChildren, mods?: NodeMods) { - super(fields, mods) - this.id = id - } - - render(path: Path, value: IObject, view: TreeView) { - value = value ?? {} - return `
- ${Object.keys(this.fields).map(f => { - return this.fields[f].render(path.push(f), value[f], view) - }).join('')} -
` - } -} diff --git a/src/nodes/custom/ResourceNode.ts b/src/nodes/custom/ResourceNode.ts index 1d6d3d74..1fab1ab1 100644 --- a/src/nodes/custom/ResourceNode.ts +++ b/src/nodes/custom/ResourceNode.ts @@ -17,7 +17,7 @@ export class ResourceNode extends EnumNode { const options = mods?.options ?? [] // TODO: Support registry using `github.com/Arcensoth/mcdata` super(options, { transform: (v) => { - if (v === undefined) return undefined + if (v === undefined || v.length === 0) return undefined return v.startsWith('minecraft:') ? v : 'minecraft:' + v }, ...mods}) this.additional = mods?.additional ?? false diff --git a/src/schemas/Sandbox.ts b/src/schemas/Sandbox.ts index 457e1eb8..803ac839 100644 --- a/src/schemas/Sandbox.ts +++ b/src/schemas/Sandbox.ts @@ -1,9 +1,7 @@ import { ObjectNode } from '../nodes/ObjectNode'; import { EnumNode } from '../nodes/EnumNode'; -import { ResourceNode } from '../nodes/custom/ResourceNode'; import { NumberNode } from '../nodes/NumberNode'; import { BooleanNode } from '../nodes/BooleanNode'; -import { RootNode } from '../nodes/RootNode'; import { RangeNode } from '../nodes/custom/RangeNode'; import { MapNode } from '../nodes/MapNode'; import { StringNode } from '../nodes/StringNode'; @@ -11,10 +9,9 @@ import { ListNode } from '../nodes/ListNode'; const EntityCollection = ['sheep', 'pig'] -export const SandboxSchema = new RootNode('predicate', { +export const SandboxSchema = new ObjectNode({ condition: new EnumNode(['foo', 'bar'], { - default: () => 'bar', - transform: (s: string) => (s === 'foo') ? {test: 'baz'} : s + default: () => 'bar' }), number: new NumberNode({integer: false, min: 0}), range: new RangeNode({ @@ -22,14 +19,18 @@ export const SandboxSchema = new RootNode('predicate', { }), predicate: new ObjectNode({ type: new EnumNode(EntityCollection), - nbt: new ResourceNode({ + nbt: new StringNode({ default: (v) => 'hahaha' }), test: new BooleanNode({force: () => true}), recipes: new MapNode( new StringNode(), new RangeNode({ - default: (v) => RangeNode.isExact(v) ? 2 : v + default: (v) => RangeNode.isExact(v) ? 2 : v, + transform: (v: any) => RangeNode.isRange(v) ? ({ + min: v?.min ?? -2147483648, + max: v?.max ?? 2147483647 + }) : v }) ) }), @@ -49,5 +50,6 @@ export const SandboxSchema = new RootNode('predicate', { predicate: { nbt: 'hi' } - }) + }), + transform: (v) => v?.condition === 'foo' ? ({...v, test: 'hello'}) : v }); diff --git a/src/view/SourceView.ts b/src/view/SourceView.ts index 19bfaa4d..70970e14 100644 --- a/src/view/SourceView.ts +++ b/src/view/SourceView.ts @@ -13,7 +13,16 @@ export class SourceView implements ModelListener { render() { const transformed = this.model.schema.transform(new Path([], this.model), this.model.data) - this.target.textContent = JSON.stringify(transformed) + const textarea = document.createElement('textarea') + textarea.style.width = 'calc(100% - 6px)' + textarea.rows = 10 + textarea.textContent = JSON.stringify(transformed) + textarea.addEventListener('change', evt => { + const parsed = JSON.parse(textarea.value) + this.model.reset(parsed) + }) + this.target.innerHTML = '' + this.target.appendChild(textarea) } invalidated() { diff --git a/src/view/TreeView.ts b/src/view/TreeView.ts index ac0aa372..915ed1cd 100644 --- a/src/view/TreeView.ts +++ b/src/view/TreeView.ts @@ -49,7 +49,8 @@ export class TreeView implements ModelListener { } render() { - this.target.innerHTML = this.model.schema.render(new Path(), this.model.data, this) + this.target.innerHTML = this.model.schema.render( + new Path(), this.model.data, this, {hideLabel: true}) for (const id in this.registry) { const element = this.target.querySelector(`[data-id="${id}"]`) if (element !== null) this.registry[id](element)