added .component macro to karax
This commit is contained in:
parent
1c242332da
commit
a799413f7f
6 changed files with 169 additions and 186 deletions
|
|
@ -1,17 +1,14 @@
|
||||||
|
|
||||||
import vdom, karax, karaxdsl, jdict, jstrutils
|
import vdom, karax, karaxdsl, jdict, jstrutils, components
|
||||||
|
|
||||||
type
|
type
|
||||||
Filter = enum
|
Filter = enum
|
||||||
all, active, completed
|
all, active, completed
|
||||||
|
|
||||||
const
|
|
||||||
WindowSize = 50 # overestimating it does not harm
|
|
||||||
|
|
||||||
var
|
var
|
||||||
entries: seq[(cstring, bool)]
|
entries: seq[(cstring, bool)]
|
||||||
visibleA = 0
|
visibleA = 0
|
||||||
visibleB = 10_000
|
visibleB = 10
|
||||||
selectedEntry = -1
|
selectedEntry = -1
|
||||||
filter: Filter
|
filter: Filter
|
||||||
|
|
||||||
|
|
@ -52,34 +49,15 @@ proc toChecked(checked: bool): cstring =
|
||||||
proc selected(v: Filter): cstring =
|
proc selected(v: Filter): cstring =
|
||||||
(if filter == v: cstring"selected" else: cstring(nil))
|
(if filter == v: cstring"selected" else: cstring(nil))
|
||||||
|
|
||||||
when false:
|
proc createEntry(id: int; d: cstring;
|
||||||
proc entry(id: int, d: cstring; completed, selected: bool): proc (): VNode =
|
completed: bool, selected: bool): VNode {.component.} =
|
||||||
var lastid: type(id)
|
|
||||||
var lastd: type(d)
|
|
||||||
var lastCompleted: type(completed)
|
|
||||||
var lastSelected: type(selected)
|
|
||||||
proc inner(): VNode =
|
|
||||||
if lastid != id or lastd != d or lastCompleted != completed or lastSelected != selected:
|
|
||||||
result = logic(id, d, completed, selected)
|
|
||||||
lastid = id
|
|
||||||
lastd = d
|
|
||||||
lastCompleted = completed
|
|
||||||
lastSelected = selected
|
|
||||||
|
|
||||||
result = inner
|
|
||||||
|
|
||||||
proc lazyEntry(args: seq[VNode]): VNode =
|
|
||||||
let id = args[0].intValue
|
|
||||||
let d = args[1].text
|
|
||||||
let completed = args[2].intValue != 0
|
|
||||||
let selected = args[3].intValue != 0
|
|
||||||
result = buildHtml(tr):
|
result = buildHtml(tr):
|
||||||
li(class=toClass(completed), key = id):
|
li(class=toClass(completed), key = id):
|
||||||
if not selected:
|
if not selected:
|
||||||
tdiv(class = "view"):
|
tdiv(class = "view"):
|
||||||
input(class = "toggle", `type` = "checkbox", checked = toChecked(completed),
|
input(class = "toggle", `type` = "checkbox", checked = toChecked(completed),
|
||||||
onclick=toggleEntry, key = id)
|
onclick=toggleEntry, key = id)
|
||||||
label(onDblClick=editHandler, key = id):
|
label(onclick=editHandler, key = id):
|
||||||
text d
|
text d
|
||||||
button(class = "destroy", key = id, onclick = removeHandler)
|
button(class = "destroy", key = id, onclick = removeHandler)
|
||||||
else:
|
else:
|
||||||
|
|
@ -87,9 +65,6 @@ proc lazyEntry(args: seq[VNode]): VNode =
|
||||||
onfocusLost = focusLost,
|
onfocusLost = focusLost,
|
||||||
onenter = editEntry, value = d, setFocus)
|
onenter = editEntry, value = d, setFocus)
|
||||||
|
|
||||||
proc createEntry(id: int; d: cstring; completed, selected: bool): VNode =
|
|
||||||
result = thunk(lazyEntry, id, d, completed, selected)
|
|
||||||
|
|
||||||
proc myscroll(ev: Event; n: VNode) =
|
proc myscroll(ev: Event; n: VNode) =
|
||||||
kout cstring"scrolling event here"
|
kout cstring"scrolling event here"
|
||||||
|
|
||||||
|
|
@ -105,10 +80,6 @@ proc createDom(): VNode =
|
||||||
input(class = "toggle-all", `type` = "checkbox", name = "toggle")
|
input(class = "toggle-all", `type` = "checkbox", name = "toggle")
|
||||||
label(`for` = "toggle-all", onclick = onAllDone):
|
label(`for` = "toggle-all", onclick = onAllDone):
|
||||||
text "Mark all as complete"
|
text "Mark all as complete"
|
||||||
tdiv(onscroll = myscroll, style = "overflow: visible; width: 0px; display: block; max-height: 1em"):
|
|
||||||
for i in 0..4:
|
|
||||||
tdiv:
|
|
||||||
text entries[i][0]
|
|
||||||
|
|
||||||
var entriesCount = 0
|
var entriesCount = 0
|
||||||
var completedCount = 0
|
var completedCount = 0
|
||||||
|
|
|
||||||
103
src/components.nim
Normal file
103
src/components.nim
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
## Components in Karax are built by the ``.component`` macro annotation.
|
||||||
|
|
||||||
|
import macros, jdict, dom, vdom, tables, strutils
|
||||||
|
|
||||||
|
var
|
||||||
|
vcomponents* = newJDict[cstring, proc(args: seq[VNode]): VNode]()
|
||||||
|
dcomponents* = newJDict[cstring, proc(args: seq[VNode]): Node]()
|
||||||
|
|
||||||
|
type
|
||||||
|
ComponentKind* {.pure.} = enum
|
||||||
|
None,
|
||||||
|
VNode,
|
||||||
|
Node
|
||||||
|
|
||||||
|
var
|
||||||
|
allcomponents {.compileTime.} = initTable[string, ComponentKind]()
|
||||||
|
|
||||||
|
proc isComponent*(x: string): ComponentKind {.compileTime.} =
|
||||||
|
allcomponents.getOrDefault(x)
|
||||||
|
|
||||||
|
proc unpack(symbolicType: NimNode; index: int): NimNode {.compileTime.} =
|
||||||
|
#let t = symbolicType.getTypeImpl
|
||||||
|
let t = repr(symbolicType)
|
||||||
|
case t
|
||||||
|
of "cstring":
|
||||||
|
result = quote do:
|
||||||
|
args[`index`].text
|
||||||
|
of "int", "VKey":
|
||||||
|
result = quote do:
|
||||||
|
args[`index`].intValue
|
||||||
|
of "bool":
|
||||||
|
result = quote do:
|
||||||
|
args[`index`].intValue != 0
|
||||||
|
elif t.endsWith"Kind":
|
||||||
|
result = quote do:
|
||||||
|
`symbolicType`(args[`index`].intValue)
|
||||||
|
else:
|
||||||
|
# just pass it along, maybe there is some conversion for it:
|
||||||
|
result = quote do:
|
||||||
|
args[`index`]
|
||||||
|
|
||||||
|
proc newname*(n: NimNode): NimNode =
|
||||||
|
if n.kind == nnkPostfix:
|
||||||
|
n[1] = newname(n[1])
|
||||||
|
result = n
|
||||||
|
elif n.kind == nnkSym:
|
||||||
|
result = ident($n.symbol)
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
||||||
|
macro component*(prc: untyped): untyped =
|
||||||
|
## A component takes an proc body and registers it as a component to the
|
||||||
|
## virtual dom.
|
||||||
|
var n = prc.copyNimNode
|
||||||
|
for i in 0..6: n.add prc[i].copyNimTree
|
||||||
|
expectKind(n, nnkProcDef)
|
||||||
|
if n[0].kind == nnkEmpty:
|
||||||
|
error("please pass a non anonymous proc")
|
||||||
|
let name = n[0]
|
||||||
|
let params = params(n)
|
||||||
|
let rettype = repr params[0]
|
||||||
|
var isvirtual = ComponentKind.None
|
||||||
|
if rettype == "VNode":
|
||||||
|
isvirtual = ComponentKind.VNode
|
||||||
|
elif rettype == "Node":
|
||||||
|
isvirtual = ComponentKind.Node
|
||||||
|
else:
|
||||||
|
error "component must return VNode or Node"
|
||||||
|
n[0] = ident("inner" & $name)
|
||||||
|
var unpackCall = newCall(n[0])
|
||||||
|
var counter = 0
|
||||||
|
for i in 1.. <params.len:
|
||||||
|
let param = params[i]
|
||||||
|
let L = param.len
|
||||||
|
let typ = param[L-2]
|
||||||
|
for j in 0 .. L-3:
|
||||||
|
unpackCall.add unpack(typ, counter)
|
||||||
|
inc counter
|
||||||
|
|
||||||
|
template vwrapper(wname, nameStrLit, unpackCall) {.dirty.} =
|
||||||
|
proc wname*(args: seq[VNode]): VNode =
|
||||||
|
unpackCall
|
||||||
|
vcomponents[cstring(nameStrLit)] = wname
|
||||||
|
|
||||||
|
template dwrapper(wname, nameStrLit, unpackCall) {.dirty.} =
|
||||||
|
proc wname(args: seq[VNode]): Node =
|
||||||
|
unpackCall
|
||||||
|
dcomponents[cstring(nameStrLit)] = wname
|
||||||
|
|
||||||
|
result = newTree(nnkStmtList, n)
|
||||||
|
if isvirtual == ComponentKind.VNode:
|
||||||
|
result.add getAst(vwrapper(newname name, newLit($name), unpackCall))
|
||||||
|
else:
|
||||||
|
result.add getAst(dwrapper(newname name, newLit($name), unpackCall))
|
||||||
|
allcomponents[$name] = isvirtual
|
||||||
|
when defined(debugKaraxDsl):
|
||||||
|
echo repr result
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
proc foo(x, y: int, b: bool; s: cstring): VNode {.component.} =
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -520,6 +520,8 @@ proc item*(list: TouchList, i: int): Touch
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
proc setAttr*(n: Node; key, val: cstring) {.importcpp: "#.setAttribute(@)".}
|
||||||
|
|
||||||
var
|
var
|
||||||
window* {.importc, nodecl.}: Window
|
window* {.importc, nodecl.}: Window
|
||||||
document* {.importc, nodecl.}: Document
|
document* {.importc, nodecl.}: Document
|
||||||
|
|
|
||||||
170
src/karax.nim
170
src/karax.nim
|
|
@ -1,6 +1,6 @@
|
||||||
# Simple lib to write JS UIs
|
# Simple lib to write JS UIs
|
||||||
|
|
||||||
import dom, vdom, jstrutils
|
import dom, vdom, jstrutils, components, jdict
|
||||||
|
|
||||||
export dom.Event, dom.cloneNode, dom
|
export dom.Event, dom.cloneNode, dom
|
||||||
|
|
||||||
|
|
@ -68,8 +68,12 @@ proc isElementInViewport(el: Node; h: var int): bool =
|
||||||
proc vnodeToDom(n: VNode): Node =
|
proc vnodeToDom(n: VNode): Node =
|
||||||
if n.kind == VNodeKind.text:
|
if n.kind == VNodeKind.text:
|
||||||
result = document.createTextNode(n.text)
|
result = document.createTextNode(n.text)
|
||||||
elif n.kind == VNodeKind.thunk:
|
elif n.kind == VNodeKind.vthunk:
|
||||||
return vnodeToDom(n.callThunk)
|
let x = callThunk(vcomponents[n.text], n)
|
||||||
|
return vnodeToDom(x)
|
||||||
|
elif n.kind == VNodeKind.dthunk:
|
||||||
|
let x = callThunk(dcomponents[n.text], n)
|
||||||
|
return x
|
||||||
else:
|
else:
|
||||||
result = document.createElement(toTag[n.kind])
|
result = document.createElement(toTag[n.kind])
|
||||||
for k in n:
|
for k in n:
|
||||||
|
|
@ -85,7 +89,7 @@ proc vnodeToDom(n: VNode): Node =
|
||||||
result.key = n.key
|
result.key = n.key
|
||||||
for k, v in attrs(n):
|
for k, v in attrs(n):
|
||||||
if v != nil:
|
if v != nil:
|
||||||
result.setAttribute(k, v)
|
result.setAttr(k, v)
|
||||||
let myn = n
|
let myn = n
|
||||||
for e, h in items(n.events):
|
for e, h in items(n.events):
|
||||||
proc wrapper(): proc (ev: Event) =
|
proc wrapper(): proc (ev: Event) =
|
||||||
|
|
@ -136,23 +140,26 @@ proc equals(a, b: VNode): bool =
|
||||||
if a.key != b.key: return false
|
if a.key != b.key: return false
|
||||||
if a.kind == VNodeKind.text:
|
if a.kind == VNodeKind.text:
|
||||||
if a.text != b.text: return false
|
if a.text != b.text: return false
|
||||||
elif a.kind == VNodeKind.thunk:
|
elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk:
|
||||||
if a.thunk != b.thunk: return false
|
if a.text != b.text: return false
|
||||||
if a.len != b.len: return false
|
if a.len != b.len: return false
|
||||||
for i in 0..<a.len:
|
for i in 0..<a.len:
|
||||||
if not equals(a[i], b[i]): return false
|
if not equals(a[i], b[i]): return false
|
||||||
if not sameAttrs(a, b): return false
|
if not sameAttrs(a, b): return false
|
||||||
if a.class != b.class: return false
|
if a.class != b.class: return false
|
||||||
# XXX test event listeners here?
|
# XXX test event listeners here?
|
||||||
# --> maybe give nodes a hash?
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc equalsTree(a, b: VNode): bool =
|
proc equalsTree(a, b: VNode): bool =
|
||||||
if not a.validHash:
|
when false:
|
||||||
a.calcHash()
|
# hashing is too fragile now with component support:
|
||||||
if not b.validHash:
|
if not a.validHash:
|
||||||
b.calcHash()
|
a.calcHash()
|
||||||
return a.hash == b.hash
|
if not b.validHash:
|
||||||
|
b.calcHash()
|
||||||
|
return a.hash == b.hash
|
||||||
|
else:
|
||||||
|
return eq(a, b)
|
||||||
|
|
||||||
proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
||||||
if not equals(newNode, oldNode):
|
if not equals(newNode, oldNode):
|
||||||
|
|
@ -177,12 +184,14 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
||||||
current.removeChild(current.lastChild)
|
current.removeChild(current.lastChild)
|
||||||
else:
|
else:
|
||||||
var commonPrefix = 0
|
var commonPrefix = 0
|
||||||
while commonPrefix < minLength and equalsTree(newNode[commonPrefix], oldNode[commonPrefix]):
|
while commonPrefix < minLength and
|
||||||
|
equalsTree(newNode[commonPrefix], oldNode[commonPrefix]):
|
||||||
inc commonPrefix
|
inc commonPrefix
|
||||||
|
|
||||||
var oldPos = oldLength - 1
|
var oldPos = oldLength - 1
|
||||||
var newPos = newLength - 1
|
var newPos = newLength - 1
|
||||||
while oldPos >= commonPrefix and newPos >= commonPrefix and equalsTree(newNode[newPos], oldNode[oldPos]):
|
while oldPos >= commonPrefix and newPos >= commonPrefix and
|
||||||
|
equalsTree(newNode[newPos], oldNode[oldPos]):
|
||||||
dec oldPos
|
dec oldPos
|
||||||
dec newPos
|
dec newPos
|
||||||
|
|
||||||
|
|
@ -254,18 +263,6 @@ proc redraw*() =
|
||||||
proc init*() =
|
proc init*() =
|
||||||
reqFrame(dodraw)
|
reqFrame(dodraw)
|
||||||
|
|
||||||
#proc prepend*(parent, kid: Element) =
|
|
||||||
# parent.insertBefore(kid, parent.firstChild)
|
|
||||||
# prependChild(parent, kid)
|
|
||||||
|
|
||||||
proc suffix*(s, prefix: cstring): cstring =
|
|
||||||
if s.startsWith(prefix):
|
|
||||||
result = s.substr(prefix.len)
|
|
||||||
else:
|
|
||||||
kout(cstring"bug! " & s & cstring" does not start with " & prefix)
|
|
||||||
|
|
||||||
proc suffixAsInt*(s, prefix: cstring): int = parseInt(suffix(s, prefix))
|
|
||||||
|
|
||||||
proc scrollTop*(e: Node): int {.importcpp: "#.scrollTop", nodecl.}
|
proc scrollTop*(e: Node): int {.importcpp: "#.scrollTop", nodecl.}
|
||||||
proc offsetHeight*(e: Node): int {.importcpp: "#.offsetHeight", nodecl.}
|
proc offsetHeight*(e: Node): int {.importcpp: "#.offsetHeight", nodecl.}
|
||||||
proc offsetTop*(e: Node): int {.importcpp: "#.offsetTop", nodecl.}
|
proc offsetTop*(e: Node): int {.importcpp: "#.offsetTop", nodecl.}
|
||||||
|
|
@ -291,8 +288,6 @@ proc setOnchanged*(e: VNode; action: EventHandler) =
|
||||||
proc setOnscroll*(e: VNode; action: EventHandler) =
|
proc setOnscroll*(e: VNode; action: EventHandler) =
|
||||||
onImpl EventKind.onscroll
|
onImpl EventKind.onscroll
|
||||||
|
|
||||||
#proc ceil(f: float): int {.importc: "Math.ceil", nodecl.}
|
|
||||||
|
|
||||||
proc setOnscroll*(action: proc(min, max: VKey; diff: int)) =
|
proc setOnscroll*(action: proc(min, max: VKey; diff: int)) =
|
||||||
var oldY = window.pageYOffset
|
var oldY = window.pageYOffset
|
||||||
|
|
||||||
|
|
@ -321,98 +316,6 @@ proc setOnHashChange*(action: proc (hashPart: cstring)) =
|
||||||
redraw()
|
redraw()
|
||||||
onhashchange = wrapper
|
onhashchange = wrapper
|
||||||
|
|
||||||
var
|
|
||||||
linkCounter: int
|
|
||||||
|
|
||||||
proc link*(id: int): VNode =
|
|
||||||
result = newVNode(VNodeKind.anchor)
|
|
||||||
result.setAttr("href", "#")
|
|
||||||
inc linkCounter
|
|
||||||
result.setAttr("id", $linkCounter & ":" & $id)
|
|
||||||
|
|
||||||
proc link*(action: EventHandler): VNode =
|
|
||||||
result = newVNode(VNodeKind.anchor)
|
|
||||||
result.setAttr("href", "#")
|
|
||||||
result.setOnclick action
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc button*(caption: cstring; action: EventHandler; disabled=false): VNode =
|
|
||||||
result = newVNode(VNodeKind.button)
|
|
||||||
result.add text(caption)
|
|
||||||
if action != nil:
|
|
||||||
result.setOnClick action
|
|
||||||
if disabled:
|
|
||||||
result.setAttr("disabled", "true")
|
|
||||||
|
|
||||||
proc select*(choices: openarray[cstring]): VNode =
|
|
||||||
result = newVNode(VNodeKind.select)
|
|
||||||
var i = 0
|
|
||||||
for c in choices:
|
|
||||||
result.add tree(VNodeKind.option, [(cstring"value", toCstr(i))], text(c))
|
|
||||||
inc i
|
|
||||||
|
|
||||||
proc select*(choices: openarray[(int, cstring)]): VNode =
|
|
||||||
result = newVNode(VNodeKind.select)
|
|
||||||
for c in choices:
|
|
||||||
result.add tree(VNodeKind.option, [(cstring"value", toCstr(c[0]))], text(c[1]))
|
|
||||||
|
|
||||||
var radioCounter: int
|
|
||||||
|
|
||||||
proc radio*(choices: openarray[(int, cstring)]): VNode =
|
|
||||||
result = newVNode(VNodeKind.fieldset)
|
|
||||||
var i = 0
|
|
||||||
inc radioCounter
|
|
||||||
for c in choices:
|
|
||||||
let id = cstring"radio_" & c[1] & toCstr(i)
|
|
||||||
var kid = tree(VNodeKind.input, [(cstring"type", cstring"radio"),
|
|
||||||
(cstring"id", id), (cstring"name", cstring"radio" & toCStr(radioCounter)),
|
|
||||||
(cstring"value", toCStr(c[0]))])
|
|
||||||
if i == 0:
|
|
||||||
kid.setAttr(cstring"checked", cstring"checked")
|
|
||||||
var lab = tree(VNodeKind.label, [(cstring"for", id)], text(c[1]))
|
|
||||||
kid.add lab
|
|
||||||
result.add kid
|
|
||||||
inc i
|
|
||||||
|
|
||||||
proc tag*(kind: VNodeKind; id=cstring(nil), class=cstring(nil)): VNode =
|
|
||||||
result = newVNode(kind)
|
|
||||||
result.id = id
|
|
||||||
result.class = class
|
|
||||||
|
|
||||||
proc tdiv*(id=cstring(nil), class=cstring(nil)): VNode = tag(VNodeKind.tdiv, id, class)
|
|
||||||
proc span*(id=cstring(nil), class=cstring(nil)): VNode = tag(VNodeKind.span, id, class)
|
|
||||||
|
|
||||||
proc valueAsInt*(e: Node): int = parseInt(e.value)
|
|
||||||
|
|
||||||
proc th*(s: cstring): VNode =
|
|
||||||
result = newVNode(VNodeKind.th)
|
|
||||||
result.add text(s)
|
|
||||||
|
|
||||||
proc td*(s: string): VNode =
|
|
||||||
result = newVNode(VNodeKind.td)
|
|
||||||
result.add text(s)
|
|
||||||
|
|
||||||
proc td*(s: VNode): VNode =
|
|
||||||
result = newVNode(VNodeKind.td)
|
|
||||||
result.add s
|
|
||||||
|
|
||||||
proc td*(class: cstring; s: VNode): VNode =
|
|
||||||
result = newVNode(VNodeKind.td)
|
|
||||||
result.add s
|
|
||||||
result.class = class
|
|
||||||
|
|
||||||
proc table*(class=cstring(nil), kids: varargs[VNode]): VNode =
|
|
||||||
result = tag(VNodeKind.table, nil, class)
|
|
||||||
for k in kids: result.add k
|
|
||||||
|
|
||||||
proc tr*(kids: varargs[VNode]): VNode =
|
|
||||||
result = newVNode(VNodeKind.tr)
|
|
||||||
for k in kids:
|
|
||||||
if k.kind in {VNodeKind.td, VNodeKind.th}:
|
|
||||||
result.add k
|
|
||||||
else:
|
|
||||||
result.add td(k)
|
|
||||||
|
|
||||||
proc getAttr(e: Node; key: cstring): cstring {.
|
proc getAttr(e: Node; key: cstring): cstring {.
|
||||||
importcpp: "#.getAttribute(#)", nodecl.}
|
importcpp: "#.getAttribute(#)", nodecl.}
|
||||||
|
|
||||||
|
|
@ -426,9 +329,6 @@ template keyeventBody() =
|
||||||
redraw()
|
redraw()
|
||||||
|
|
||||||
proc realtimeInput*(val: cstring; action: EventHandler): VNode =
|
proc realtimeInput*(val: cstring; action: EventHandler): VNode =
|
||||||
#let oldElem = getElementById(id)
|
|
||||||
#if oldElem != nil: return oldElem
|
|
||||||
#let newVal = if oldElem.isNil: val else: $oldElem.value
|
|
||||||
var timer: Timeout
|
var timer: Timeout
|
||||||
proc onkeyup(ev: Event; n: VNode) =
|
proc onkeyup(ev: Event; n: VNode) =
|
||||||
proc wrapper() = keyeventBody()
|
proc wrapper() = keyeventBody()
|
||||||
|
|
@ -440,9 +340,6 @@ proc realtimeInput*(val: cstring; action: EventHandler): VNode =
|
||||||
result.addEventListener(EventKind.onkeyup, onkeyup)
|
result.addEventListener(EventKind.onkeyup, onkeyup)
|
||||||
|
|
||||||
proc enterInput*(id, val: cstring; action: EventHandler): VNode =
|
proc enterInput*(id, val: cstring; action: EventHandler): VNode =
|
||||||
#let oldElem = getElementById(id)
|
|
||||||
#if oldElem != nil: return oldElem
|
|
||||||
#let newVal = if oldElem.isNil: val else: $oldElem.value
|
|
||||||
proc onkeyup(ev: Event; n: VNode) =
|
proc onkeyup(ev: Event; n: VNode) =
|
||||||
if ev.keyCode == 13: keyeventBody()
|
if ev.keyCode == 13: keyeventBody()
|
||||||
|
|
||||||
|
|
@ -508,16 +405,13 @@ proc setupErrorHandler*(useAlert=false) =
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
when false:
|
proc prepend*(parent, kid: Element) =
|
||||||
var plugins {.exportc.}: seq[(string, proc())] = @[]
|
parent.insertBefore(kid, parent.firstChild)
|
||||||
|
|
||||||
proc onInput(val: cstring) =
|
|
||||||
kout val
|
|
||||||
if val == "dyn":
|
|
||||||
let body = getElementById("body")
|
|
||||||
body.prepend(tree("script", [("type", "text/javascript"), ("src", "nimcache/dyn.js")]))
|
|
||||||
redraw()
|
|
||||||
kout(plugins.len)
|
|
||||||
if plugins.len > 0:
|
|
||||||
plugins[0][1]()
|
|
||||||
|
|
||||||
|
proc loadScript*(jsfilename: cstring) =
|
||||||
|
let body = getElementById("body")
|
||||||
|
let s = document.createElement("script")
|
||||||
|
s.setAttr "type", "text/javascript"
|
||||||
|
s.setAttr "src", jsfilename
|
||||||
|
body.prepend(s)
|
||||||
|
redraw()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
import macros, karax, vdom
|
import macros, karax, vdom, components
|
||||||
from strutils import startsWith, toLowerAscii
|
from strutils import startsWith, toLowerAscii
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -61,11 +61,19 @@ proc tcall2(n, tmpContext: NimNode): NimNode =
|
||||||
result = n
|
result = n
|
||||||
of nnkCallKinds:
|
of nnkCallKinds:
|
||||||
let op = getName(n[0])
|
let op = getName(n[0])
|
||||||
if isTag(op):
|
let ck = isComponent(op)
|
||||||
|
let isTag = isTag(op)
|
||||||
|
if isTag or ck != ComponentKind.None:
|
||||||
let tmp = genSym(nskLet, "tmp")
|
let tmp = genSym(nskLet, "tmp")
|
||||||
|
let call = if isTag:
|
||||||
|
newCall(bindSym"tree", newDotExpr(bindSym"VNodeKind", n[0]))
|
||||||
|
elif ck == ComponentKind.VNode:
|
||||||
|
newCall(bindSym"vthunk", newLit(op))
|
||||||
|
else:
|
||||||
|
newCall(bindSym"dthunk", newLit(op))
|
||||||
result = newTree(
|
result = newTree(
|
||||||
if tmpContext == nil: nnkStmtListExpr else: nnkStmtList,
|
if tmpContext == nil: nnkStmtListExpr else: nnkStmtList,
|
||||||
newLetStmt(tmp, newCall(bindSym"tree", newDotExpr(bindSym"VNodeKind", n[0]))))
|
newLetStmt(tmp, call))
|
||||||
for i in 1 ..< n.len:
|
for i in 1 ..< n.len:
|
||||||
# named parameters are transformed into attributes or events:
|
# named parameters are transformed into attributes or events:
|
||||||
let x = n[i]
|
let x = n[i]
|
||||||
|
|
@ -77,7 +85,9 @@ proc tcall2(n, tmpContext: NimNode): NimNode =
|
||||||
result.add newDotAsgn(tmp, key, x[1])
|
result.add newDotAsgn(tmp, key, x[1])
|
||||||
else:
|
else:
|
||||||
result.add newCall(bindSym"setAttr", tmp, newLit(key), x[1])
|
result.add newCall(bindSym"setAttr", tmp, newLit(key), x[1])
|
||||||
elif x.kind == nnkIdent:
|
elif not isTag:
|
||||||
|
call.add x
|
||||||
|
elif eqIdent(x, "setFocus"):
|
||||||
result.add newCall(x, tmp)
|
result.add newCall(x, tmp)
|
||||||
else:
|
else:
|
||||||
result.add tcall2(x, tmp)
|
result.add tcall2(x, tmp)
|
||||||
|
|
|
||||||
23
src/vdom.nim
23
src/vdom.nim
|
|
@ -5,7 +5,7 @@ import shash
|
||||||
|
|
||||||
type
|
type
|
||||||
VNodeKind* {.pure.} = enum
|
VNodeKind* {.pure.} = enum
|
||||||
text, int, bool, thunk,
|
text, int, bool, vthunk, dthunk,
|
||||||
anchor,
|
anchor,
|
||||||
tdiv,
|
tdiv,
|
||||||
table, tr, td, th, thead, tbody,
|
table, tr, td, th, thead, tbody,
|
||||||
|
|
@ -15,7 +15,8 @@ type
|
||||||
|
|
||||||
const
|
const
|
||||||
toTag*: array[VNodeKind, cstring] = [
|
toTag*: array[VNodeKind, cstring] = [
|
||||||
cstring"#text", "#int", "#bool", "#thunk", "A", "DIV", "TABLE", "TR", "TD",
|
cstring"#text", "#int", "#bool", "#vthunk", "#dthunk",
|
||||||
|
"A", "DIV", "TABLE", "TR", "TD",
|
||||||
"TH", "THEAD",
|
"TH", "THEAD",
|
||||||
"TBODY", "LINK",
|
"TBODY", "LINK",
|
||||||
"SPAN", "LABEL", "BR", "SELECT", "OPTION", "FIELDSET", "INPUT", "BUTTON", "UL",
|
"SPAN", "LABEL", "BR", "SELECT", "OPTION", "FIELDSET", "INPUT", "BUTTON", "UL",
|
||||||
|
|
@ -43,7 +44,6 @@ type
|
||||||
# even index: key, odd index: value; done this way for memory efficiency:
|
# even index: key, odd index: value; done this way for memory efficiency:
|
||||||
attrs: seq[cstring]
|
attrs: seq[cstring]
|
||||||
events*: seq[(EventKind, EventHandler)]
|
events*: seq[(EventKind, EventHandler)]
|
||||||
thunk*: proc (args: seq[VNode]): VNode
|
|
||||||
hash*: Hash
|
hash*: Hash
|
||||||
validHash*: bool
|
validHash*: bool
|
||||||
|
|
||||||
|
|
@ -55,23 +55,26 @@ 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*(b: bool): VNode = VNode(kind: VNodeKind.int, key: ord(b))
|
||||||
proc vn*(x: cstring): VNode = VNode(kind: VNodeKind.text, key: -1, text: x)
|
proc vn*(x: cstring): VNode = VNode(kind: VNodeKind.text, key: -1, text: x)
|
||||||
|
|
||||||
proc callThunk*(n: VNode): VNode =
|
template callThunk*(fn: typed; n: VNode): untyped =
|
||||||
assert n.kind == VNodeKind.thunk
|
## for internal usage only.
|
||||||
result = n.thunk(n.kids)
|
fn(n.kids)
|
||||||
|
|
||||||
proc thunk*(cb: proc(args: seq[VNode]): VNode; args: varargs[VNode, vn]): VNode =
|
proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
|
||||||
VNode(kind: VNodeKind.thunk, thunk: cb, kids: @args)
|
VNode(kind: VNodeKind.vthunk, text: name, kids: @args)
|
||||||
|
|
||||||
|
proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
|
||||||
|
VNode(kind: VNodeKind.dthunk, text: name, kids: @args)
|
||||||
|
|
||||||
proc eq*(a, b: VNode): bool =
|
proc eq*(a, b: VNode): bool =
|
||||||
if a.kind != b.kind: return false
|
if a.kind != b.kind: return false
|
||||||
if a.id != b.id: return false
|
if a.id != b.id: return false
|
||||||
if a.class != b.class: return false
|
if a.class != b.class: return false
|
||||||
|
if a.key != b.key: return false
|
||||||
if a.kind != VNodeKind.text:
|
if a.kind != VNodeKind.text:
|
||||||
if a.kids.len != b.kids.len: return false
|
if a.kids.len != b.kids.len: return false
|
||||||
for i in 0..<a.kids.len:
|
for i in 0..<a.kids.len:
|
||||||
if not eq(a.kids[i], b.kids[i]): return false
|
if not eq(a.kids[i], b.kids[i]): return false
|
||||||
else:
|
if a.text != b.text: return false
|
||||||
if a.text != b.text: return false
|
|
||||||
if a.attrs.len != b.attrs.len: return false
|
if a.attrs.len != b.attrs.len: return false
|
||||||
for i in 0..<a.attrs.len:
|
for i in 0..<a.attrs.len:
|
||||||
if a.attrs[i] != b.attrs[i]: return false
|
if a.attrs[i] != b.attrs[i]: return false
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue