From 4801b8d8c115fec6301a4307cd18a791290fedcc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 19 Jun 2017 21:25:34 +0200 Subject: [PATCH] new component example works --- experiments/example.nim | 79 +++++++++++++++++++++++------------------ src/karax.nim | 61 ++++++++++++++++++++----------- src/karaxdsl.nim | 2 +- src/vdom.nim | 22 ++++++++---- 4 files changed, 100 insertions(+), 64 deletions(-) diff --git a/experiments/example.nim b/experiments/example.nim index d48143d..5653e22 100644 --- a/experiments/example.nim +++ b/experiments/example.nim @@ -2,59 +2,68 @@ import vdom, vstyles, karax, karaxdsl, jdict, jstrutils, kdom type - Car = ref object of VComponent + Carousel = ref object of VComponent counter: int - cntdown: int + cntdown, myid: int timer: TimeOut list: seq[cstring] change: bool - onclick: proc (ev: Event; n: VNode) const ticksUntilChange = 5 -proc car*(images: seq[cstring]): Car = - new(result) - result.list = images - result.cntdown = ticksUntilChange - - proc docount() = - dec result.cntdown - if result.cntdown == 0: - result.counter = (result.counter + 1) mod result.list.len - result.cntdown = ticksUntilChange - else: - result.timer = setTimeout(docount, 30) - result.change = true - redraw() - - result.onclick = proc (ev: Event; n: VNode) = - result.change = false - if result.timer != nil: - clearTimeout(result.timer) - result.timer = setTimeout(docount, 30) - -method changed(c: Car): bool = true - var images: seq[cstring] = @[cstring"a", "b", "c", "d"] -proc carousel*(): VNode = - let c = car(images) +proc render(x: VComponent): VNode = + let self = Carousel(x) + + proc docount() = + dec self.cntdown + if self.cntdown == 0: + self.counter = (self.counter + 1) mod self.list.len + self.cntdown = ticksUntilChange + else: + self.timer = setTimeout(docount, 30) + self.change = true + redraw() + + proc onclick(ev: Event; n: VNode) = + self.change = false + if self.timer != nil: + clearTimeout(self.timer) + self.timer = setTimeout(docount, 30) + let col = - case c.counter + case self.counter of 0: cstring"#4d4d4d" of 1: cstring"#ff00ff" of 2: cstring"#00ffff" of 3: cstring"#ffff00" else: cstring"red" - result = buildHtml(tdiv(nref = c)): - text images[c.counter] - button(onclick = c.onclick): + result = buildHtml(tdiv()): + text self.list[self.counter] + button(onclick = onclick): text "Next" tdiv(style = style(StyleAttr.color, col)): - text "This changes its color." - if c.cntdown != ticksUntilChange: - text &c.cntdown + text "This changes its color." & &self.myid + if self.cntdown != ticksUntilChange: + text &self.cntdown + +var gid: int + +proc carousel(): Carousel = + result = Carousel(kind: VNodeKind.component, key: -1) + result.render = render + result.list = images + result.changed = proc (c: VComponent): bool = + let x = Carousel(c) + result = x.change + result.cntdown = ticksUntilChange + result.myid = gid + inc gid + #result.onAttach = proc (_: VComponent) = + # result.cntdown = ticksUntilChange + # kout cstring"attached!" proc createDom(): VNode = result = buildHtml(table): diff --git a/src/karax.nim b/src/karax.nim index 56357ef..f15326d 100644 --- a/src/karax.nim +++ b/src/karax.nim @@ -80,14 +80,11 @@ proc wrapEvent(d: Node; n: VNode; k: EventKind; action: EventHandler) = # --------------------- DOM diff ----------------------------------------- template detach(n: VNode) = - if not n.nref.isNil(): - n.nref.vnode = nil - n.nref.onDetach() + if n.kind == VNodeKind.component: + let x = VComponent(n) + if x.onDetach != nil: x.onDetach(x) n.dom = nil template attach(n: VNode) = - if not n.nref.isNil(): - n.nref.vnode = n - n.nref.onAttach() n.dom = result proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node = @@ -105,6 +102,13 @@ proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node = #n.key = result.key attach n return result + elif n.kind == VNodeKind.component: + let x = VComponent(n) + if x.onAttach != nil: x.onAttach(x) + assert x.render != nil + result = vnodeToDom(x.render(x), kxi) + attach n + return result else: result = document.createElement(toTag[n.kind]) attach n @@ -150,9 +154,6 @@ proc eq(a, b: VNode; deep: bool): EqResult = if a.id != b.id: return different result = identical if a.key != b.key: return different - if a.nref != nil and changed(a.nref): - kout cstring"yes, change!" - return different if a.kind == VNodeKind.text: if a.text != b.text: return different elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk: @@ -160,6 +161,11 @@ proc eq(a, b: VNode; deep: bool): EqResult = if a.len != b.len: return different for i in 0..= commonPrefix and newPos >= commonPrefix: - eqAndUpdate(newNode[newPos], oldNode[oldPos]): + eqAndUpdate(newNode, newPos, oldNode[oldPos]): dec oldPos dec newPos var pos = min(oldPos, newPos) + 1 for i in commonPrefix..pos-1: - updateElement(current, current.childNodes[i], newNode[i], oldNode[i], kxi) + if updateElement(current, current.childNodes[i], newNode[i], oldNode[i], kxi): + newNode[i] = oldNode[i] var nextChildPos = oldPos + 1 while pos <= newPos: @@ -297,7 +316,7 @@ proc dodraw(kxi: KaraxInstance) = replaceById(kxi.rootId, asdom) else: let olddom = document.getElementById(kxi.rootId) - updateElement(nil, olddom, newtree, kxi.currentTree, kxi) + discard updateElement(nil, olddom, newtree, kxi.currentTree, kxi) #assert same(newtree, document.getElementById("ROOT")) if someDirty: updateDirtyElements(nil, olddom, newtree, kxi) @@ -363,7 +382,7 @@ proc setupErrorHandler*() = ## the error message is put into the console, otherwise `alert` ## is called. proc stackTraceAsCstring(): cstring = cstring(getStackTrace()) - var onerror {.importc: "window.onerror".} = + var onerror {.importc: "window.onerror", used.} = proc (msg, url: cstring, line, col: int, error: cstring): bool = var x = cstring"Error: " & msg & "\n" & stackTraceAsCstring() kout(x) diff --git a/src/karaxdsl.nim b/src/karaxdsl.nim index 07a336f..a3558c8 100644 --- a/src/karaxdsl.nim +++ b/src/karaxdsl.nim @@ -4,7 +4,7 @@ from strutils import startsWith, toLowerAscii const StmtContext = ["kout", "inc", "echo", "dec", "!"] - SpecialAttrs = ["id", "class", "value", "key", "style", "nref"] + SpecialAttrs = ["id", "class", "value", "key", "style"] proc getName(n: NimNode): string = case n.kind diff --git a/src/vdom.nim b/src/vdom.nim index e478da1..f25b1a7 100644 --- a/src/vdom.nim +++ b/src/vdom.nim @@ -8,6 +8,7 @@ type VNodeKind* {.pure.} = enum text = "#text", int = "#int", bool = "#bool", vthunk = "#vthunk", dthunk = "#dthunk", + component = "#component", html, head, title, base, link, meta, style, script, noscript, @@ -101,16 +102,13 @@ macro buildLookupTables(): untyped = buildLookupTables() type - VComponent* = ref object of RootObj - EventHandler* = proc (ev: Event; target: VNode) {.closure.} VKey* = int - VNode* = ref object + VNode* = ref object of RootObj kind*: VNodeKind key*: VKey id*, class*, text*: cstring - nref*: VComponent kids: seq[VNode] # even index: key, odd index: value; done this way for memory efficiency: attrs: seq[cstring] @@ -121,9 +119,11 @@ type dom*: Node ## the attached real DOM node. Can be 'nil' if the virtual node ## is not part of the virtual DOM anymore. -method onAttach*(v: VComponent) {.base.} = discard -method onDetach*(v: VComponent) {.base.} = discard -method changed*(v: VComponent): bool {.base.} = false + VComponent* = ref object of VNode + render*: proc(self: VComponent): VNode + changed*: proc(self: VComponent): bool + onAttach*: proc(self: VComponent) + onDetach*: proc(self: VComponent) proc value*(n: VNode): cstring = n.text proc `value=`*(n: VNode; v: cstring) = n.text = v @@ -143,6 +143,13 @@ proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode = proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode = VNode(kind: VNodeKind.dthunk, text: name, key: -1, kids: @args) +proc vcomponent*(render: (proc(self: VComponent): VNode) not nil, + changed: (proc(self: VComponent): bool) not nil, + onAttach: proc(self: VComponent) = nil, + onDetach: proc(self: VComponent) = nil): VNode = + result = VComponent(kind: VNodeKind.component, key: -1, render: render, + changed: changed, onAttach: onAttach, onDetach: onDetach) + proc setAttr*(n: VNode; key: cstring; val: cstring = "") = if n.attrs.isNil: n.attrs = @[key, val] @@ -160,6 +167,7 @@ proc getAttr*(n: VNode; key: cstring): cstring = proc len*(x: VNode): int = x.kids.len proc `[]`*(x: VNode; idx: int): VNode = x.kids[idx] +proc `[]=`*(x: VNode; idx: int; y: VNode) = x.kids[idx] = y proc add*(parent, kid: VNode) = parent.kids.add kid proc newVNode*(kind: VNodeKind): VNode = VNode(kind: kind, key: -1)