Add view classes

This commit is contained in:
Misode
2020-05-25 13:22:36 +02:00
parent 1bef36a713
commit 79a5742dad
9 changed files with 83 additions and 53 deletions

View File

@@ -3,7 +3,8 @@ import { EnumNode } from '../nodes/EnumNode'
import { ObjectNode } from '../nodes/ObjectNode'
import { StringNode } from '../nodes/StringNode'
import { DataModel } from '../model/DataModel'
import { TreeView } from '../view/TreeView'
import { SourceView } from '../view/SourceView'
const EntityCollection = ['sheep', 'pig']
@@ -12,7 +13,7 @@ const predicateTree = new RootNode('predicate', {
transform: (s: string) => (s === 'foo') ? {test: 'baz'} : s
}),
predicate: new ObjectNode({
type: new EnumNode(EntityCollection, {}),
type: new EnumNode(EntityCollection),
nbt: new StringNode()
})
}, {
@@ -20,7 +21,8 @@ const predicateTree = new RootNode('predicate', {
});
const model = new DataModel(predicateTree)
const treeView = new TreeView(model)
const sourceView = new SourceView(model)
model.render(document!.getElementById('view')!)
// document!.getElementById('source')!.textContent = predicateTree.transform(dummyData)
treeView.render(document!.getElementById('view')!)
sourceView.render(document!.getElementById('source')!)

View File

@@ -1,38 +1,12 @@
import { RootNode } from "../nodes/RootNode"
import { Path } from "./Path"
type Registry = {
[id: string]: (el: Element) => void
}
const registryIdLength = 12
const dec2hex = (dec: number) => ('0' + dec.toString(16)).substr(-2)
export function getId() {
var arr = new Uint8Array((registryIdLength || 40) / 2)
window.crypto.getRandomValues(arr)
return Array.from(arr, dec2hex).join('')
}
export class DataModel {
private data: any
private schema: RootNode
private registry: Registry = {}
data: any
schema: RootNode
constructor(schema: RootNode) {
this.schema = schema
this.data = schema.default
}
register(id: string, callback: (el: Element) => void) {
this.registry[id] = callback
}
render(target: HTMLElement) {
target.innerHTML = this.schema.render(new Path(), this.data, this)
for (const id in this.registry) {
const element = target.querySelector(`[data-id="${id}"]`)
if (element !== null) this.registry[id](element)
}
}
}

View File

@@ -1,10 +1,11 @@
import { DataModel, getId } from "../model/DataModel"
import { DataModel } from "../model/DataModel"
import { Path } from "../model/Path"
import { TreeView } from "../view/TreeView"
export interface INode<T> {
setParent: (parent: INode<any>) => void
transform: (value: T) => any
render: (path: Path, value: T, model: DataModel) => string
render: (path: Path, value: T, view: TreeView) => string
}
export type NodeChildren = {
@@ -30,17 +31,16 @@ export abstract class AbstractNode<T> implements INode<T> {
this.parent = parent
}
wrap(path: Path, model: DataModel, renderResult: string): string {
const id = getId()
model.register(id, el => {
wrap(path: Path, view: TreeView, renderResult: string): string {
const id = view.register(el => {
console.log(`Callback ${id} -> ${path.last()}`)
this.mounted(el, path, model)
this.mounted(el, path, view)
})
return `<div data-id="${id}">${renderResult}</div>`
}
mounted(el: Element, path: Path, model: DataModel) {
el.addEventListener('change', evt => this.updateModel(el, path, model))
mounted(el: Element, path: Path, view: TreeView) {
el.addEventListener('change', evt => this.updateModel(el, path, view.model))
}
updateModel(el: Element, path: Path, model: DataModel) {}
@@ -49,5 +49,5 @@ export abstract class AbstractNode<T> implements INode<T> {
return this.transformMod(value)
}
abstract render(path: Path, value: T, model: DataModel): string
abstract render(path: Path, value: T, view: TreeView): string
}

View File

@@ -1,5 +1,6 @@
import { AbstractNode, NodeMods } from './AbstractNode'
import { DataModel } from '../model/DataModel'
import { TreeView } from '../view/TreeView'
import { Path } from '../model/Path'
export class EnumNode extends AbstractNode<string> {
@@ -13,8 +14,8 @@ export class EnumNode extends AbstractNode<string> {
updateModel(el: Element, path: Path, model: DataModel) {
}
render(path: Path, value: string, model: DataModel) {
return this.wrap(path, model, `<span>${path.last()}</span>
render(path: Path, value: string, view: TreeView) {
return this.wrap(path, view, `<span>${path.last()}</span>
<select value="${value}">
${this.options.map(o => `<option>${o}</option>`)}
</select>`)

View File

@@ -1,5 +1,5 @@
import { AbstractNode, NodeChildren, NodeMods } from './AbstractNode'
import { DataModel } from '../model/DataModel'
import { TreeView } from '../view/TreeView'
import { Path } from '../model/Path'
export interface IObject {
@@ -27,11 +27,11 @@ export class ObjectNode extends AbstractNode<IObject> {
return res;
}
render(path: Path, value: IObject, model: DataModel) {
render(path: Path, value: IObject, view: TreeView) {
if (value === undefined) return ``
return this.wrap(path, model, `<span>${path.last()}:</span><div style="padding-left:8px">
return this.wrap(path, view, `<span>${path.last()}:</span><div style="padding-left:8px">
${Object.keys(this.fields).map(f => {
return this.fields[f].render(path.push(f), value[f], model)
return this.fields[f].render(path.push(f), value[f], view)
}).join('')}
</div>`)
}

View File

@@ -1,7 +1,7 @@
import { NodeChildren, NodeMods } from './AbstractNode'
import { ObjectNode, IObject } from './ObjectNode'
import { Path } from '../model/Path'
import { DataModel } from '../model/DataModel'
import { TreeView } from '../view/TreeView'
export class RootNode extends ObjectNode {
id: string
@@ -15,11 +15,11 @@ export class RootNode extends ObjectNode {
return JSON.stringify(super.transform(value))
}
render(path: Path, value: IObject, model: DataModel) {
render(path: Path, value: IObject, view: TreeView) {
value = value || {}
return `<div>
${Object.keys(this.fields).map(f => {
return this.fields[f].render(path.push(f), value[f], model)
return this.fields[f].render(path.push(f), value[f], view)
}).join('')}
</div>`
}

View File

@@ -1,6 +1,7 @@
import { AbstractNode, NodeMods } from './AbstractNode'
import { Path } from '../model/Path'
import { DataModel } from '../model/DataModel'
import { TreeView } from '../view/TreeView'
export class StringNode extends AbstractNode<string> {
constructor(mods?: NodeMods<string>) {
@@ -10,8 +11,8 @@ export class StringNode extends AbstractNode<string> {
updateModel(el: Element, path: Path, model: DataModel) {
}
render(path: Path, value: string, model: DataModel) {
return this.wrap(path, model,
render(path: Path, value: string, view: TreeView) {
return this.wrap(path, view,
`<span>${path.last()}</span> <input value="${value || ''}"></input>`)
}
}

14
src/view/SourceView.ts Normal file
View File

@@ -0,0 +1,14 @@
import { DataModel } from "../model/DataModel"
import { Path } from "../model/Path"
export class SourceView {
model: DataModel
constructor(model: DataModel) {
this.model = model
}
render(target: HTMLElement) {
target.textContent = this.model.schema.transform(this.model.data)
}
}

38
src/view/TreeView.ts Normal file
View File

@@ -0,0 +1,38 @@
import { DataModel } from "../model/DataModel"
import { Path } from "../model/Path"
type Registry = {
[id: string]: (el: Element) => void
}
const registryIdLength = 12
const dec2hex = (dec: number) => ('0' + dec.toString(16)).substr(-2)
export function getId() {
var arr = new Uint8Array((registryIdLength || 40) / 2)
window.crypto.getRandomValues(arr)
return Array.from(arr, dec2hex).join('')
}
export class TreeView {
model: DataModel
registry: Registry = {}
constructor(model: DataModel) {
this.model = model
}
register(callback: (el: Element) => void): string {
const id = getId()
this.registry[id] = callback
return id
}
render(target: HTMLElement) {
target.innerHTML = this.model.schema.render(new Path(), this.model.data, this)
for (const id in this.registry) {
const element = target.querySelector(`[data-id="${id}"]`)
if (element !== null) this.registry[id](element)
}
}
}