renamed key to 'index'; all virtual components have a 'key' attribute
This commit is contained in:
parent
5838271f8b
commit
91d7ba25ea
5 changed files with 37 additions and 41 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
26
src/vdom.nim
26
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..<n.kids.len: yield n.kids[i]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue