new component example works
This commit is contained in:
parent
d1e0e1367b
commit
4801b8d8c1
4 changed files with 100 additions and 64 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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..<a.len:
|
||||
if eq(a[i], b[i], deep) == different: return different
|
||||
elif b.kind == VNodeKind.component:
|
||||
# let x = VComponent(b)
|
||||
# assert x.changed != nil
|
||||
# return if x.changed(x): different else: identical
|
||||
return different
|
||||
elif deep:
|
||||
if a.len != b.len: return different
|
||||
for i in 0..<a.len:
|
||||
|
|
@ -204,13 +210,23 @@ proc updateStyles(newNode, oldNode: VNode; deep: bool) =
|
|||
|
||||
proc updateDom(newNode, oldNode: VNode) =
|
||||
newNode.dom = oldNode.dom
|
||||
newNode.nref = oldNode.nref
|
||||
assert newNode.len == oldNode.len
|
||||
for i in 0 ..< newNode.len:
|
||||
updateDom(newNode[i], oldNode[i])
|
||||
|
||||
proc updateElement(parent, current: Node, newNode, oldNode: VNode;
|
||||
kxi: KaraxInstance) =
|
||||
kxi: KaraxInstance): bool =
|
||||
if oldNode.kind == newNode.kind and oldNode.kind == VNodeKind.component:
|
||||
let x = VComponent(oldNode)
|
||||
assert x.changed != nil
|
||||
if x.changed(x):
|
||||
let n = vnodeToDom(x.render(x), kxi)
|
||||
if parent == nil:
|
||||
replaceById(kxi.rootId, n)
|
||||
else:
|
||||
parent.replaceChild(n, current)
|
||||
return true
|
||||
|
||||
let res = eq(newNode, oldNode, deep=false)
|
||||
if res == different:
|
||||
detach(oldNode)
|
||||
|
|
@ -241,32 +257,35 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
|
|||
else:
|
||||
var commonPrefix = 0
|
||||
|
||||
template eqAndUpdate(a, b: VNode; action: untyped) =
|
||||
let r = eq(a, b, true)
|
||||
template eqAndUpdate(a: VNode; i: int; b: VNode; action: untyped) =
|
||||
let r = eq(a[i], b, true)
|
||||
case r
|
||||
of identical:
|
||||
updateDom(a, b)
|
||||
a[i] = b
|
||||
#updateDom(a, b)
|
||||
action
|
||||
of different: break
|
||||
of similar:
|
||||
updateDom(a, b)
|
||||
updateStyles(a, b, true)
|
||||
#updateDom(a, b)
|
||||
a[i] = b
|
||||
#updateStyles(a, b, true)
|
||||
action
|
||||
|
||||
while commonPrefix < minLength:
|
||||
eqAndUpdate(newNode[commonPrefix], oldNode[commonPrefix]):
|
||||
eqAndUpdate(newNode, commonPrefix, oldNode[commonPrefix]):
|
||||
inc commonPrefix
|
||||
|
||||
var oldPos = oldLength - 1
|
||||
var newPos = newLength - 1
|
||||
while oldPos >= 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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
22
src/vdom.nim
22
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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue