mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 07:10:41 +00:00
Update model and add invalidated cycle
This commit is contained in:
@@ -21,8 +21,8 @@ const predicateTree = new RootNode('predicate', {
|
||||
});
|
||||
|
||||
const model = new DataModel(predicateTree)
|
||||
const treeView = new TreeView(model)
|
||||
const sourceView = new SourceView(model)
|
||||
|
||||
treeView.render(document!.getElementById('view')!)
|
||||
sourceView.render(document!.getElementById('source')!)
|
||||
new TreeView(model, document!.getElementById('view')!)
|
||||
new SourceView(model, document!.getElementById('source')!)
|
||||
|
||||
model.invalidate()
|
||||
|
||||
@@ -1,12 +1,39 @@
|
||||
import { RootNode } from "../nodes/RootNode"
|
||||
import { Path } from "./Path"
|
||||
|
||||
export interface ModelListener {
|
||||
invalidated(model: DataModel): void
|
||||
}
|
||||
|
||||
export class DataModel {
|
||||
data: any
|
||||
schema: RootNode
|
||||
listeners: ModelListener[]
|
||||
|
||||
constructor(schema: RootNode) {
|
||||
this.schema = schema
|
||||
this.data = schema.default
|
||||
this.listeners = []
|
||||
}
|
||||
|
||||
addListener(listener: ModelListener) {
|
||||
this.listeners.push(listener)
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this.listeners.forEach(listener => listener.invalidated(this))
|
||||
}
|
||||
|
||||
set(path: Path, value: any) {
|
||||
let node = this.data;
|
||||
for (let index of path.pop()) {
|
||||
if (node[index] === undefined) {
|
||||
node[index] = {}
|
||||
}
|
||||
node = node[index]
|
||||
}
|
||||
node[path.last()] = value
|
||||
|
||||
this.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
type PathElement = (string | number)
|
||||
|
||||
export class Path {
|
||||
export class Path implements Iterable<PathElement> {
|
||||
private arr: PathElement[]
|
||||
|
||||
constructor(arr?: PathElement[]) {
|
||||
@@ -23,4 +23,14 @@ export class Path {
|
||||
copy(): Path {
|
||||
return new Path([...this.arr])
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `[${this.arr.map(e => e.toString()).join(', ')}]`
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
for (const e of this.arr) {
|
||||
yield e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ export abstract class AbstractNode<T> implements INode<T> {
|
||||
|
||||
wrap(path: Path, view: TreeView, renderResult: string): string {
|
||||
const id = view.register(el => {
|
||||
console.log(`Callback ${id} -> ${path.last()}`)
|
||||
this.mounted(el, path, view)
|
||||
})
|
||||
return `<div data-id="${id}">${renderResult}</div>`
|
||||
|
||||
@@ -12,12 +12,14 @@ export class EnumNode extends AbstractNode<string> {
|
||||
}
|
||||
|
||||
updateModel(el: Element, path: Path, model: DataModel) {
|
||||
model.set(path, el.querySelector('select')?.value)
|
||||
}
|
||||
|
||||
render(path: Path, value: string, view: TreeView) {
|
||||
const id = view.register(el => (el as HTMLInputElement).value = value)
|
||||
return this.wrap(path, view, `<span>${path.last()}</span>
|
||||
<select value="${value}">
|
||||
${this.options.map(o => `<option>${o}</option>`)}
|
||||
<select data-id=${id}>
|
||||
${this.options.map(o => `<option value="${o}">${o}</option>`).join('')}
|
||||
</select>`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export class StringNode extends AbstractNode<string> {
|
||||
}
|
||||
|
||||
updateModel(el: Element, path: Path, model: DataModel) {
|
||||
model.set(path, el.querySelector('input')?.value)
|
||||
}
|
||||
|
||||
render(path: Path, value: string, view: TreeView) {
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { DataModel } from "../model/DataModel"
|
||||
import { Path } from "../model/Path"
|
||||
import { DataModel, ModelListener } from "../model/DataModel"
|
||||
|
||||
export class SourceView {
|
||||
export class SourceView implements ModelListener {
|
||||
model: DataModel
|
||||
target: HTMLElement
|
||||
|
||||
constructor(model: DataModel) {
|
||||
constructor(model: DataModel, target: HTMLElement) {
|
||||
this.model = model
|
||||
this.target = target
|
||||
model.addListener(this)
|
||||
}
|
||||
|
||||
render(target: HTMLElement) {
|
||||
target.textContent = this.model.schema.transform(this.model.data)
|
||||
render() {
|
||||
this.target.textContent = this.model.schema.transform(this.model.data)
|
||||
}
|
||||
|
||||
invalidated() {
|
||||
this.render()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DataModel } from "../model/DataModel"
|
||||
import { DataModel, ModelListener } from "../model/DataModel"
|
||||
import { Path } from "../model/Path"
|
||||
|
||||
type Registry = {
|
||||
@@ -14,12 +14,15 @@ export function getId() {
|
||||
return Array.from(arr, dec2hex).join('')
|
||||
}
|
||||
|
||||
export class TreeView {
|
||||
export class TreeView implements ModelListener {
|
||||
model: DataModel
|
||||
target: HTMLElement
|
||||
registry: Registry = {}
|
||||
|
||||
constructor(model: DataModel) {
|
||||
constructor(model: DataModel, target: HTMLElement) {
|
||||
this.model = model
|
||||
this.target = target
|
||||
model.addListener(this)
|
||||
}
|
||||
|
||||
register(callback: (el: Element) => void): string {
|
||||
@@ -28,11 +31,15 @@ export class TreeView {
|
||||
return id
|
||||
}
|
||||
|
||||
render(target: HTMLElement) {
|
||||
target.innerHTML = this.model.schema.render(new Path(), this.model.data, this)
|
||||
render() {
|
||||
this.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}"]`)
|
||||
const element = this.target.querySelector(`[data-id="${id}"]`)
|
||||
if (element !== null) this.registry[id](element)
|
||||
}
|
||||
}
|
||||
|
||||
invalidated(model: DataModel) {
|
||||
this.render()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"downlevelIteration": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user