diff --git a/examples/mediaplayer/mediaplayer.nim b/examples/mediaplayer/mediaplayer.nim index 3f970a8..7bc9c65 100644 --- a/examples/mediaplayer/mediaplayer.nim +++ b/examples/mediaplayer/mediaplayer.nim @@ -15,7 +15,7 @@ proc `width=`(n: Node, w: int) {.importcpp: "#.width = #".} proc mplayer*(id, resource: cstring): VNode {.compact.} = proc handler(ev: Event; n: VNode) = let myVideo = document.getElementById(id) - case n.key + case n.index of Play: if myVideo.paused: myVideo.play() @@ -27,13 +27,13 @@ proc mplayer*(id, resource: cstring): VNode {.compact.} = else: discard result = buildHtml(tdiv): - button(onclick=handler, key=Play): + button(onclick=handler, index=Play): text "Play/Pause" - button(onclick=handler, key=Big): + button(onclick=handler, index=Big): text "Big" - button(onclick=handler, key=Small): + button(onclick=handler, index=Small): text "Small" - button(onclick=handler, key=Normal): + button(onclick=handler, index=Normal): text "Normal" br() br() diff --git a/examples/todoapp/todoapp.nim b/examples/todoapp/todoapp.nim index 0caa70c..47ddeae 100644 --- a/examples/todoapp/todoapp.nim +++ b/examples/todoapp/todoapp.nim @@ -45,19 +45,19 @@ proc onTodoEnter(ev: Event; n: VNode) = n.value = "" proc removeHandler(ev: Event; n: VNode) = - updateEntry(n.key, cstring(nil), false) + updateEntry(n.index, cstring(nil), false) proc editHandler(ev: Event; n: VNode) = - selectedEntry = n.key + selectedEntry = n.index proc focusLost(ev: Event; n: VNode) = selectedEntry = -1 proc editEntry(ev: Event; n: VNode) = - setEntryContent(n.key, n.value) + setEntryContent(n.index, n.value) selectedEntry = -1 proc toggleEntry(ev: Event; n: VNode) = - let id = n.key + let id = n.index markAsCompleted(id, not isCompleted(id)) proc onAllDone(ev: Event; n: VNode) = @@ -83,12 +83,12 @@ proc createEntry(id: int; d: cstring; completed, selected: bool): VNode {.compac if not selected: tdiv(class = "view"): input(class = "toggle", `type` = "checkbox", checked = toChecked(completed), - onclick=toggleEntry, key=id) - label(onDblClick=editHandler, key=id): + onclick=toggleEntry, index=id) + label(onDblClick=editHandler, index=id): text d - button(class = "destroy", key=id, onclick=removeHandler) + button(class = "destroy", index=id, onclick=removeHandler) else: - input(class = "edit", name = "title", key=id, + input(class = "edit", name = "title", index=id, onblur = focusLost, onkeyupenter = editEntry, value = d, setFocus=true) diff --git a/src/karax.nim b/src/karax.nim index 22ceceb..c96901b 100644 --- a/src/karax.nim +++ b/src/karax.nim @@ -7,13 +7,6 @@ export kdom.Event proc kout*[T](x: T) {.importc: "console.log", varargs.} ## the preferred way of debugging karax applications. -proc hasProp(e: Node; prop: cstring): bool {.importcpp: "(#.hasOwnProperty(#))".} -proc rawkey(e: Node): VKey {.importcpp: "#.karaxKey", nodecl.} -proc key*(e: Node): VKey = - if e.hasProp"karaxKey": result = e.rawkey - else: result = -1 -proc `key=`*(e: Node; x: VKey) {.importcpp: "#.karaxKey = #", nodecl.} - type PatchKind = enum pkReplace, pkRemove, pkAppend, pkInsertBefore, pkDetach @@ -200,7 +193,7 @@ proc eq(a, b: VNode): EqResult = if a.kind != b.kind: return different if a.id != b.id: return different result = identical - if a.key != b.key: return different + if a.index != b.index: return different if a.kind == VNodeKind.text: if a.text != b.text: return different elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk: @@ -211,6 +204,7 @@ proc eq(a, b: VNode): EqResult = elif b.kind == VNodeKind.component: # different component names mean different components: if a.text != b.text: return different + if VComponent(a).key != VComponent(b).key: return different return componentsIdentical if a.class != b.class: return different if not eq(a.style, b.style) or not sameAttrs(a, b): return similar @@ -246,7 +240,7 @@ proc mergeEvents(newNode, oldNode: VNode; kxi: KaraxInstance) = applyEvents(oldNode, kxi) proc printV(n: VNode; depth: cstring = "") = - kout depth, cstring($n.kind), cstring"key ", n.key + kout depth, cstring($n.kind), cstring"key ", n.index #for k, v in pairs(n.style): # kout depth, "style: ", k, v if n.kind == VNodeKind.component: @@ -323,16 +317,16 @@ proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): E inc kxi.recursion result = eq(newNode, oldNode) case result - of identical, componentsIdentical, similar: + of componentsIdentical: + kxi.components.add ComponentPair(oldNode: VComponent(oldNode), + newNode: VComponent(newNode), + parent: parent, + current: current) + of identical, similar: newNode.dom = oldNode.dom if result == similar: updateStyles(newNode, oldNode) updateAttributes(newNode, oldNode) - elif result == componentsIdentical: - kxi.components.add ComponentPair(oldNode: VComponent(oldNode), - newNode: VComponent(newNode), - parent: parent, - current: current) if newNode.events.len != 0 or oldNode.events.len != 0: mergeEvents(newNode, oldNode, kxi) if oldNode.kind == VNodeKind.input or oldNode.kind == VNodeKind.textarea: diff --git a/src/karaxdsl.nim b/src/karaxdsl.nim index 15c6fc6..c17d725 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", "index", "style"] proc getName(n: NimNode): string = case n.kind diff --git a/src/vdom.nim b/src/vdom.nim index f751676..8e4f983 100644 --- a/src/vdom.nim +++ b/src/vdom.nim @@ -105,11 +105,11 @@ buildLookupTables() type EventHandler* = proc (ev: Event; target: VNode) {.closure.} NativeEventHandler* = proc (ev: Event) {.closure.} - VKey* = int + VKey* = cstring VNode* = ref object of RootObj kind*: VNodeKind - key*: VKey + index*: int ## a generally useful 'index' id*, class*, text*: cstring kids: seq[VNode] # even index: key, odd index: value; done this way for memory efficiency: @@ -123,6 +123,8 @@ type ## is not part of the virtual DOM anymore. VComponent* = ref object of VNode ## The abstract class for every karax component. + key*: VKey ## key that determines if two components are + ## identical. renderImpl*: proc(self: VComponent): VNode changedImpl*: proc(self, newInstance: VComponent): bool updatedImpl*: proc(self, newInstance: VComponent) @@ -140,20 +142,20 @@ type proc value*(n: VNode): cstring = n.text proc `value=`*(n: VNode; v: cstring) = n.text = v -proc intValue*(n: VNode): int = n.key -proc vn*(i: int): VNode = VNode(kind: VNodeKind.int, key: i) -proc vn*(b: bool): VNode = VNode(kind: VNodeKind.int, key: ord(b)) -proc vn*(x: cstring): VNode = VNode(kind: VNodeKind.text, key: -1, text: x) +proc intValue*(n: VNode): int = n.index +proc vn*(i: int): VNode = VNode(kind: VNodeKind.int, index: i) +proc vn*(b: bool): VNode = VNode(kind: VNodeKind.int, index: ord(b)) +proc vn*(x: cstring): VNode = VNode(kind: VNodeKind.text, index: -1, text: x) template callThunk*(fn: typed; n: VNode): untyped = ## for internal usage only. fn(n.kids) proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode = - VNode(kind: VNodeKind.vthunk, text: name, key: -1, kids: @args) + VNode(kind: VNodeKind.vthunk, text: name, index: -1, kids: @args) proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode = - VNode(kind: VNodeKind.dthunk, text: name, key: -1, kids: @args) + VNode(kind: VNodeKind.dthunk, text: name, index: -1, kids: @args) proc defaultChangedImpl*(v, newInstance: VComponent): bool = ## The default implementation of 'changed'. @@ -174,7 +176,7 @@ template newComponent*[T](t: typeDesc[T]; changed: (proc(self, newInstance: VComponent): bool) = defaultChangedImpl, updated: proc(self, newInstance: VComponent) = defaultUpdatedImpl): T = ## Use this template to create new components. - T(kind: VNodeKind.component, key: -1, + T(kind: VNodeKind.component, index: -1, text: cstring(astToStr(t)), renderImpl: render, changedImpl: changed, updatedImpl: updated, onAttachImpl: onAttach, onDetachImpl: onDetach, @@ -206,7 +208,7 @@ 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) +proc newVNode*(kind: VNodeKind): VNode = VNode(kind: kind, index: -1) proc tree*(kind: VNodeKind; kids: varargs[VNode]): VNode = result = newVNode(kind) @@ -217,8 +219,8 @@ proc tree*(kind: VNodeKind; attrs: openarray[(cstring, cstring)]; result = tree(kind, kids) for a in attrs: result.setAttr(a[0], a[1]) -proc text*(s: string): VNode = VNode(kind: VNodeKind.text, text: cstring(s), key: -1) -proc text*(s: cstring): VNode = VNode(kind: VNodeKind.text, text: s, key: -1) +proc text*(s: string): VNode = VNode(kind: VNodeKind.text, text: cstring(s), index: -1) +proc text*(s: cstring): VNode = VNode(kind: VNodeKind.text, text: s, index: -1) iterator items*(n: VNode): VNode = for i in 0..