diff --git a/experiments/refs/demo.html b/experiments/refs/demo.html new file mode 100644 index 0000000..e1fe419 --- /dev/null +++ b/experiments/refs/demo.html @@ -0,0 +1,13 @@ + + + + + + Refs + + + +
+ + + diff --git a/experiments/refs/demo.nim b/experiments/refs/demo.nim new file mode 100644 index 0000000..6ecc7db --- /dev/null +++ b/experiments/refs/demo.nim @@ -0,0 +1,56 @@ +include karaxprelude +import future, sequtils + +type + CustomRef = ref object of VNodeRef + +method onAttach(r: CustomRef) = + kout(cstring"custom ref attached") + +method onDetach(r: CustomRef) = + kout(cstring"custom ref detached") + +var + modelData = @[5, 2, 4] + + refA = VNodeRef() + refB = VNodeRef() + refC = CustomRef() + refSeq = newSeq[VNodeRef]() + +proc onClick(ev: Event, n: VNode) = + modelData.add(0) + +proc showRefs() = + kout(refA) + kout(refB) + kout(refC) + kout(refSeq.map(nref => nref.vnode)) + +proc secureRefSlot(i: int): VNodeRef = + while refSeq.len <= i: + refSeq.add(VNodeRef()) + result = refSeq[i] + +proc view(): VNode = + result = buildHtml(): + tdiv: + button(onclick=onClick): + text "click me" + # storing refs to single elements is straightforward now + if modelData.len mod 2 == 0: + tdiv(nref=refA): + text "A" + tdiv(nref=refB): + text "B" + tdiv(nref=refC): + text "C" + # It's a bit more tricky when containers are involved: + for i, x in modelData.pairs: + tdiv(nref=secureRefSlot(i)): + text "List item: " & $x + +proc renderer(): VNode = + view() + +setRenderer(renderer, showRefs) diff --git a/experiments/refs/demo.nims b/experiments/refs/demo.nims new file mode 100644 index 0000000..9028ff1 --- /dev/null +++ b/experiments/refs/demo.nims @@ -0,0 +1 @@ +--path: "../../src" diff --git a/src/karax.nim b/src/karax.nim index d9f5773..77cebdf 100644 --- a/src/karax.nim +++ b/src/karax.nim @@ -79,8 +79,16 @@ proc wrapEvent(d: Node; n: VNode; k: EventKind; action: EventHandler) = # --------------------- DOM diff ----------------------------------------- -template detach(n: VNode) = n.dom = nil -template attach(n: Vnode) = n.dom = result +template detach(n: VNode) = + if not n.nref.isNil(): + n.nref.vnode = nil + n.nref.onDetach() + 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 = if n.kind == VNodeKind.text: diff --git a/src/karaxdsl.nim b/src/karaxdsl.nim index a3558c8..07a336f 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"] + SpecialAttrs = ["id", "class", "value", "key", "style", "nref"] proc getName(n: NimNode): string = case n.kind diff --git a/src/vdom.nim b/src/vdom.nim index d55e7b4..9f9c642 100644 --- a/src/vdom.nim +++ b/src/vdom.nim @@ -101,6 +101,9 @@ macro buildLookupTables(): untyped = buildLookupTables() type + VNodeRef* = ref object of RootObj + vnode*: VNode + EventHandler* = proc (ev: Event; target: VNode) {.closure.} VKey* = int @@ -108,6 +111,7 @@ type kind*: VNodeKind key*: VKey id*, class*, text*: cstring + nref*: VNodeRef kids: seq[VNode] # even index: key, odd index: value; done this way for memory efficiency: attrs: seq[cstring] @@ -118,6 +122,10 @@ 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: VNodeRef) {.base.} = discard +method onDetach*(v: VNodeRef) {.base.} = discard +method changed*(v: VNodeRef): bool {.base.} = false + proc value*(n: VNode): cstring = n.text proc `value=`*(n: VNode; v: cstring) = n.text = v