mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
Add list node and register click events
This commit is contained in:
@@ -5,6 +5,7 @@ import { StringNode } from '../nodes/StringNode'
|
||||
import { DataModel } from '../model/DataModel'
|
||||
import { TreeView } from '../view/TreeView'
|
||||
import { SourceView } from '../view/SourceView'
|
||||
import { ListNode } from '../nodes/ListNode'
|
||||
|
||||
const EntityCollection = ['sheep', 'pig']
|
||||
|
||||
@@ -15,9 +16,24 @@ const predicateTree = new RootNode('predicate', {
|
||||
predicate: new ObjectNode({
|
||||
type: new EnumNode(EntityCollection),
|
||||
nbt: new StringNode()
|
||||
})
|
||||
}),
|
||||
effects: new ListNode(
|
||||
new ObjectNode({
|
||||
type: new EnumNode(EntityCollection),
|
||||
nbt: new StringNode()
|
||||
}, {
|
||||
default: () => ({
|
||||
type: 'sheep'
|
||||
})
|
||||
})
|
||||
)
|
||||
}, {
|
||||
default: () => ({ condition: 'foo', predicate: { nbt: 'hi' } })
|
||||
default: () => ({
|
||||
condition: 'foo',
|
||||
predicate: {
|
||||
nbt: 'hi'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const model = new DataModel(predicateTree)
|
||||
|
||||
@@ -12,7 +12,7 @@ export class DataModel {
|
||||
|
||||
constructor(schema: RootNode) {
|
||||
this.schema = schema
|
||||
this.data = schema.default
|
||||
this.data = schema.default()
|
||||
this.listeners = []
|
||||
}
|
||||
|
||||
@@ -32,7 +32,18 @@ export class DataModel {
|
||||
}
|
||||
node = node[index]
|
||||
}
|
||||
node[path.last()] = value
|
||||
|
||||
console.log('Set', path.toString(), JSON.stringify(value))
|
||||
|
||||
if (value === undefined) {
|
||||
if (typeof path.last() === 'number') {
|
||||
node.splice(path.last(), 1)
|
||||
} else {
|
||||
delete node[path.last()]
|
||||
}
|
||||
} else {
|
||||
node[path.last()] = value
|
||||
}
|
||||
|
||||
this.invalidate()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,13 @@ import { TreeView } from "../view/TreeView"
|
||||
|
||||
export interface INode<T> {
|
||||
setParent: (parent: INode<any>) => void
|
||||
default: () => T | null
|
||||
transform: (value: T) => any
|
||||
render: (path: Path, value: T, view: TreeView) => string
|
||||
render: (path: Path, value: T, view: TreeView, options?: RenderOptions) => string
|
||||
}
|
||||
|
||||
export type RenderOptions = {
|
||||
hideLabel?: boolean
|
||||
}
|
||||
|
||||
export type NodeChildren = {
|
||||
@@ -19,11 +24,11 @@ export type NodeMods<T> = {
|
||||
|
||||
export abstract class AbstractNode<T> implements INode<T> {
|
||||
parent?: INode<any>
|
||||
default?: T
|
||||
default: () => T | null = () => null
|
||||
transformMod = (v: T) => v
|
||||
|
||||
constructor(mods?: NodeMods<T>) {
|
||||
if (mods?.default) this.default = mods.default()
|
||||
if (mods?.default) this.default = mods.default
|
||||
if (mods?.transform) this.transformMod = mods.transform
|
||||
}
|
||||
|
||||
@@ -39,7 +44,10 @@ export abstract class AbstractNode<T> implements INode<T> {
|
||||
}
|
||||
|
||||
mounted(el: Element, path: Path, view: TreeView) {
|
||||
el.addEventListener('change', evt => this.updateModel(el, path, view.model))
|
||||
el.addEventListener('change', evt => {
|
||||
this.updateModel(el, path, view.model)
|
||||
evt.stopPropagation()
|
||||
})
|
||||
}
|
||||
|
||||
updateModel(el: Element, path: Path, model: DataModel) {}
|
||||
@@ -48,5 +56,5 @@ export abstract class AbstractNode<T> implements INode<T> {
|
||||
return this.transformMod(value)
|
||||
}
|
||||
|
||||
abstract render(path: Path, value: T, view: TreeView): string
|
||||
abstract render(path: Path, value: T, view: TreeView, options?: any): string
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractNode, NodeMods } from './AbstractNode'
|
||||
import { AbstractNode, NodeMods, RenderOptions } from './AbstractNode'
|
||||
import { DataModel } from '../model/DataModel'
|
||||
import { TreeView } from '../view/TreeView'
|
||||
import { Path } from '../model/Path'
|
||||
@@ -15,9 +15,10 @@ export class EnumNode extends AbstractNode<string> {
|
||||
model.set(path, el.querySelector('select')?.value)
|
||||
}
|
||||
|
||||
render(path: Path, value: string, view: TreeView) {
|
||||
render(path: Path, value: string, view: TreeView, options?: RenderOptions) {
|
||||
const id = view.register(el => (el as HTMLInputElement).value = value)
|
||||
return this.wrap(path, view, `<span>${path.last()}</span>
|
||||
return this.wrap(path, view, `
|
||||
${options?.hideLabel ? `` : `<label>${path.last()}</label>`}
|
||||
<select data-id=${id}>
|
||||
${this.options.map(o => `<option value="${o}">${o}</option>`).join('')}
|
||||
</select>`)
|
||||
|
||||
43
src/nodes/ListNode.ts
Normal file
43
src/nodes/ListNode.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { AbstractNode, NodeMods, INode } from './AbstractNode'
|
||||
import { DataModel } from '../model/DataModel'
|
||||
import { TreeView } from '../view/TreeView'
|
||||
import { Path } from '../model/Path'
|
||||
import { IObject } from './ObjectNode'
|
||||
|
||||
|
||||
|
||||
export class ListNode extends AbstractNode<IObject[]> {
|
||||
protected children: INode<any>
|
||||
|
||||
constructor(values: INode<any>, mods?: NodeMods<IObject[]>) {
|
||||
super(mods)
|
||||
this.children = values
|
||||
}
|
||||
|
||||
updateModel(el: Element, path: Path, model: DataModel) {
|
||||
model.set(path, el.querySelector('select')?.value)
|
||||
}
|
||||
|
||||
render(path: Path, value: IObject[], view: TreeView) {
|
||||
value = value || []
|
||||
const button = view.registerClick(el => {
|
||||
view.model.set(path, [...value, this.children.default()])
|
||||
})
|
||||
return this.wrap(path, view, `<label>${path.last()}:</label>
|
||||
<button data-id="${button}">Add</button>
|
||||
<div style="padding-left:8px">
|
||||
${value.map((obj, index) => {
|
||||
return this.renderEntry(path.push(index), obj, view)
|
||||
}).join('')}
|
||||
</div>`)
|
||||
}
|
||||
|
||||
private renderEntry(path: Path, value: IObject, view: TreeView) {
|
||||
const button = view.registerClick(el => {
|
||||
view.model.set(path, undefined)
|
||||
})
|
||||
return `<div><button data-id="${button}">Remove</button>
|
||||
${this.children.render(path, value, view, {hideLabel: true})}
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractNode, NodeChildren, NodeMods } from './AbstractNode'
|
||||
import { AbstractNode, NodeChildren, NodeMods, RenderOptions } from './AbstractNode'
|
||||
import { TreeView } from '../view/TreeView'
|
||||
import { Path } from '../model/Path'
|
||||
|
||||
@@ -27,12 +27,15 @@ export class ObjectNode extends AbstractNode<IObject> {
|
||||
return res;
|
||||
}
|
||||
|
||||
render(path: Path, value: IObject, view: TreeView) {
|
||||
render(path: Path, value: IObject, view: TreeView, options?: RenderOptions) {
|
||||
if (value === undefined) return ``
|
||||
return this.wrap(path, view, `<span>${path.last()}:</span><div style="padding-left:8px">
|
||||
return this.wrap(path, view, `
|
||||
${options?.hideLabel ? `` : `<label>${path.last()}:</label>
|
||||
<div style="padding-left:8px">
|
||||
`}
|
||||
${Object.keys(this.fields).map(f => {
|
||||
return this.fields[f].render(path.push(f), value[f], view)
|
||||
}).join('')}
|
||||
</div>`)
|
||||
${options?.hideLabel ? `` : `</div>`}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractNode, NodeMods } from './AbstractNode'
|
||||
import { AbstractNode, NodeMods, RenderOptions } from './AbstractNode'
|
||||
import { Path } from '../model/Path'
|
||||
import { DataModel } from '../model/DataModel'
|
||||
import { TreeView } from '../view/TreeView'
|
||||
@@ -12,8 +12,9 @@ export class StringNode extends AbstractNode<string> {
|
||||
model.set(path, el.querySelector('input')?.value)
|
||||
}
|
||||
|
||||
render(path: Path, value: string, view: TreeView) {
|
||||
return this.wrap(path, view,
|
||||
`<span>${path.last()}</span> <input value="${value || ''}"></input>`)
|
||||
render(path: Path, value: string, view: TreeView, options?: RenderOptions) {
|
||||
return this.wrap(path, view, `
|
||||
${options?.hideLabel ? `` : `<label>${path.last()}</label>`}
|
||||
<input value="${value || ''}"></input>`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ export class TreeView implements ModelListener {
|
||||
return id
|
||||
}
|
||||
|
||||
registerClick(callback: (el: Element) => void): string {
|
||||
return this.register(el => {
|
||||
el.addEventListener('click', _ => callback(el))
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
this.target.innerHTML = this.model.schema.render(new Path(), this.model.data, this)
|
||||
for (const id in this.registry) {
|
||||
|
||||
Reference in New Issue
Block a user