From d9648be60c21da7536359935f4daa802e8a3393d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 21 Jun 2017 17:20:31 +0200 Subject: [PATCH] sane markDirty() mechanism for components --- experiments/carousel.html | 15 ++++++++++++ experiments/{example.nim => carousel.nim} | 11 +++------ experiments/{example.nims => carousel.nims} | 0 experiments/example.html | 15 ------------ src/karax.nim | 1 + src/vdom.nim | 26 +++++++++++++++++---- 6 files changed, 41 insertions(+), 27 deletions(-) create mode 100644 experiments/carousel.html rename experiments/{example.nim => carousel.nim} (89%) rename experiments/{example.nims => carousel.nims} (100%) delete mode 100644 experiments/example.html diff --git a/experiments/carousel.html b/experiments/carousel.html new file mode 100644 index 0000000..979dd38 --- /dev/null +++ b/experiments/carousel.html @@ -0,0 +1,15 @@ + + + + + Carousel app + + + + +
+ + + + + diff --git a/experiments/example.nim b/experiments/carousel.nim similarity index 89% rename from experiments/example.nim rename to experiments/carousel.nim index fa1e739..a820be7 100644 --- a/experiments/example.nim +++ b/experiments/carousel.nim @@ -1,3 +1,4 @@ +## This demo shows how you can develop your own stateful components with Karax. import vdom, vstyles, karax, karaxdsl, jdict, jstrutils, kdom @@ -7,7 +8,6 @@ type cntdown, myid: int timer: TimeOut list: seq[cstring] - change: bool const ticksUntilChange = 5 @@ -24,11 +24,10 @@ proc render(x: VComponent): VNode = self.cntdown = ticksUntilChange else: self.timer = setTimeout(docount, 30) - self.change = true + markDirty(self) redraw() proc onclick(ev: Event; n: VNode) = - self.change = false if self.timer != nil: clearTimeout(self.timer) self.timer = setTimeout(docount, 30) @@ -51,12 +50,8 @@ proc render(x: VComponent): VNode = var gid: int -proc changed(c: VComponent): bool = - let x = Carousel(c) - result = x.change - proc carousel(): Carousel = - result = newComponent(Carousel, render, changed) + result = newComponent(Carousel, render) result.list = images result.cntdown = ticksUntilChange result.myid = gid diff --git a/experiments/example.nims b/experiments/carousel.nims similarity index 100% rename from experiments/example.nims rename to experiments/carousel.nims diff --git a/experiments/example.html b/experiments/example.html deleted file mode 100644 index 2303d95..0000000 --- a/experiments/example.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Todo app - - - - -
- - - - - diff --git a/src/karax.nim b/src/karax.nim index 42be93f..57c5162 100644 --- a/src/karax.nim +++ b/src/karax.nim @@ -225,6 +225,7 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode; assert oldNode.kind == VNodeKind.component let x = VComponent(oldNode) n = vnodeToDom(x.renderImpl(x), kxi) + x.updatedImpl(x) result = true else: detach(oldNode) diff --git a/src/vdom.nim b/src/vdom.nim index 8c19ea2..4edcd4e 100644 --- a/src/vdom.nim +++ b/src/vdom.nim @@ -119,11 +119,16 @@ type dom*: Node ## the attached real DOM node. Can be 'nil' if the virtual node ## is not part of the virtual DOM anymore. - VComponent* = ref object of VNode + VComponent* = ref object of VNode ## The abstract class for every karax component. renderImpl*: proc(self: VComponent): VNode changedImpl*: proc(self: VComponent): bool + updatedImpl*: proc(self: VComponent) onAttachImpl*: proc(self: VComponent) onDetachImpl*: proc(self: VComponent) + version*: int ## Update this to trigger a redraw by karax. Usually you + ## should call 'markDirty' instead which is an alias for + ## 'inc version'. + renderedVersion*: int ## Do not touch. Used by karax. proc value*(n: VNode): cstring = n.text proc `value=`*(n: VNode; v: cstring) = n.text = v @@ -143,15 +148,28 @@ 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 defaultChangedImpl*(v: VComponent): bool = + ## The default implementation of 'changed'. + result = v.version != v.renderedVersion + +proc defaultUpdatedImpl*(v: VComponent) = + v.renderedVersion = v.version + template newComponent*[T](t: typeDesc[T]; render: (proc(self: VComponent): VNode) not nil, - changed: (proc(self: VComponent): bool) not nil, onAttach: proc(self: VComponent) = nil, - onDetach: proc(self: VComponent) = nil): T = + onDetach: proc(self: VComponent) = nil, + changed: (proc(self: VComponent): bool) = defaultChangedImpl, + updated: proc(self: VComponent) = defaultUpdatedImpl): T = ## Use this template to create new components. T(kind: VNodeKind.component, key: -1, text: cstring(astToStr(t)), renderImpl: render, - changedImpl: changed, onAttachImpl: onAttach, onDetachImpl: onDetach) + changedImpl: changed, updatedImpl: updated, + onAttachImpl: onAttach, onDetachImpl: onDetach) + +template markDirty*(c: VComponent) = + ## mark the component as dirty so that it is re-rendered. + inc c.version proc setAttr*(n: VNode; key: cstring; val: cstring = "") = if n.attrs.isNil: