added first somewhat working version of scrollapp.nim
This commit is contained in:
parent
fec9de14af
commit
b4991009a8
3 changed files with 249 additions and 29 deletions
113
karax.nim
113
karax.nim
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
import dom, vdom, jstrutils
|
import dom, vdom, jstrutils
|
||||||
|
|
||||||
export dom.Element, dom.Event, dom.cloneNode, dom
|
export dom.Event, dom.cloneNode, dom
|
||||||
|
|
||||||
|
proc len(x: Node): int {.importcpp: "#.childNodes.length".}
|
||||||
|
proc `[]`(x: Node; idx: int): Element {.importcpp: "#.childNodes[#]".}
|
||||||
|
|
||||||
proc kout*[T](x: T) {.importc: "console.log", varargs.}
|
proc kout*[T](x: T) {.importc: "console.log", varargs.}
|
||||||
## the preferred way of debugging karax applications.
|
## the preferred way of debugging karax applications.
|
||||||
|
|
@ -12,28 +15,52 @@ proc `id=`*(e: Node; x: cstring) {.importcpp: "#.id = #", nodecl.}
|
||||||
proc class*(e: Node): cstring {.importcpp: "#.className", nodecl.}
|
proc class*(e: Node): cstring {.importcpp: "#.className", nodecl.}
|
||||||
proc `class=`*(e: Node; v: cstring) {.importcpp: "#.className = #", nodecl.}
|
proc `class=`*(e: Node; v: cstring) {.importcpp: "#.className = #", nodecl.}
|
||||||
|
|
||||||
proc value*(e: Element): cstring {.importcpp: "#.value", nodecl.}
|
proc value*(e: Node): cstring {.importcpp: "#.value", nodecl.}
|
||||||
proc `value=`*(e: Element; v: cstring) {.importcpp: "#.value = #", nodecl.}
|
proc `value=`*(e: Node; v: cstring) {.importcpp: "#.value = #", nodecl.}
|
||||||
|
|
||||||
proc `disabled=`*(e: Element; v: bool) {.importcpp: "#.disabled = #", nodecl.}
|
proc `disabled=`*(e: Node; v: bool) {.importcpp: "#.disabled = #", nodecl.}
|
||||||
|
|
||||||
proc getElementsByClass*(e: Element; name: cstring): seq[Element] {.
|
proc getElementsByClass*(e: Node; name: cstring): seq[Node] {.
|
||||||
importcpp: "#.getElementsByClassName(#)", nodecl.}
|
importcpp: "#.getElementsByClassName(#)", nodecl.}
|
||||||
|
|
||||||
|
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
|
||||||
|
proc `key=`*(e: Node; x: VKey) {.importcpp: "#.karaxKey = #", nodecl.}
|
||||||
|
|
||||||
|
type
|
||||||
|
BoundingRect {.importc.} = object
|
||||||
|
top, bottom, left, right: int
|
||||||
|
|
||||||
|
proc getBoundingClientRect(e: Node): BoundingRect {.
|
||||||
|
importcpp: "getBoundingClientRect", nodecl.}
|
||||||
|
proc clientHeight(): int {.
|
||||||
|
importcpp: "(window.innerHeight || document.documentElement.clientHeight)@", nodecl}
|
||||||
|
proc clientWidth(): int {.
|
||||||
|
importcpp: "(window.innerWidth || document.documentElement.clientWidth)@", nodecl}
|
||||||
|
|
||||||
type
|
type
|
||||||
Timeout* = ref object
|
Timeout* = ref object
|
||||||
|
|
||||||
var
|
var
|
||||||
document* {.importc.}: Document
|
document* {.importc.}: Document
|
||||||
toFocus: Element
|
toFocus: Node
|
||||||
toFocusV: VNode
|
toFocusV: VNode
|
||||||
|
|
||||||
proc setFocus*(n: VNode) =
|
proc setFocus*(n: VNode) =
|
||||||
toFocusV = n
|
toFocusV = n
|
||||||
|
|
||||||
proc vnodeToDom(n: VNode): Element =
|
proc isElementInViewport(el: Node; h: var int): bool =
|
||||||
|
let rect = el.getBoundingClientRect()
|
||||||
|
h = rect.bottom - rect.top
|
||||||
|
result = rect.top >= 0 and rect.left >= 0 and
|
||||||
|
rect.bottom <= clientHeight() and
|
||||||
|
rect.right <= clientWidth()
|
||||||
|
|
||||||
|
proc vnodeToDom(n: VNode): Node =
|
||||||
if n.kind == VNodeKind.text:
|
if n.kind == VNodeKind.text:
|
||||||
result = cast[Element](document.createTextNode(n.text))
|
result = document.createTextNode(n.text)
|
||||||
else:
|
else:
|
||||||
result = document.createElement(toTag[n.kind])
|
result = document.createElement(toTag[n.kind])
|
||||||
for k in n:
|
for k in n:
|
||||||
|
|
@ -45,6 +72,8 @@ proc vnodeToDom(n: VNode): Element =
|
||||||
result.id = n.id
|
result.id = n.id
|
||||||
if n.class != nil:
|
if n.class != nil:
|
||||||
result.class = n.class
|
result.class = n.class
|
||||||
|
if n.key != 0:
|
||||||
|
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.setAttribute(k, v)
|
||||||
|
|
@ -59,13 +88,13 @@ proc vnodeToDom(n: VNode): Element =
|
||||||
if n == toFocusV and toFocus.isNil:
|
if n == toFocusV and toFocus.isNil:
|
||||||
toFocus = result
|
toFocus = result
|
||||||
|
|
||||||
proc same(n: VNode, e: Element): bool =
|
proc same(n: VNode, e: Node): bool =
|
||||||
if toTag[n.kind] == e.nodename:
|
if toTag[n.kind] == e.nodename:
|
||||||
result = true
|
result = true
|
||||||
if n.kind != VNodeKind.text:
|
if n.kind != VNodeKind.text:
|
||||||
if e.childNodes.len != n.len: return false
|
if e.len != n.len: return false
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
if not same(n[i], cast[Element](e.childNodes[i])): return false
|
if not same(n[i], e[i]): return false
|
||||||
|
|
||||||
var
|
var
|
||||||
dorender: proc (): VNode {.closure.}
|
dorender: proc (): VNode {.closure.}
|
||||||
|
|
@ -84,7 +113,7 @@ proc clearTimeout*(t: Timeout) {.importc, nodecl.}
|
||||||
#proc getElementsByClassName*(cls: cstring): seq[Element] {.importc:
|
#proc getElementsByClassName*(cls: cstring): seq[Element] {.importc:
|
||||||
# "document.getElementsByClassName", nodecl.}
|
# "document.getElementsByClassName", nodecl.}
|
||||||
|
|
||||||
proc textContent(e: Element): cstring {.
|
proc textContent(e: Node): cstring {.
|
||||||
importcpp: "#.textContent", nodecl.}
|
importcpp: "#.textContent", nodecl.}
|
||||||
|
|
||||||
proc replaceById(id: cstring; newTree: Node) =
|
proc replaceById(id: cstring; newTree: Node) =
|
||||||
|
|
@ -95,6 +124,7 @@ proc replaceById(id: cstring; newTree: Node) =
|
||||||
proc equals(a, b: VNode): bool =
|
proc equals(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.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
|
||||||
if not sameAttrs(a, b): return false
|
if not sameAttrs(a, b): return false
|
||||||
|
|
@ -126,9 +156,7 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
||||||
kout toTag[oldNode.kind]
|
kout toTag[oldNode.kind]
|
||||||
assert false
|
assert false
|
||||||
for i in 0..min(newLength, oldLength)-1:
|
for i in 0..min(newLength, oldLength)-1:
|
||||||
updateElement(current, current.childNodes[i],
|
updateElement(current, current[i], newNode[i], oldNode[i])
|
||||||
newNode[i],
|
|
||||||
oldNode[i])
|
|
||||||
if newLength > oldLength:
|
if newLength > oldLength:
|
||||||
for i in oldLength..newLength-1:
|
for i in oldLength..newLength-1:
|
||||||
current.appendChild(vnodeToDom(newNode[i]))
|
current.appendChild(vnodeToDom(newNode[i]))
|
||||||
|
|
@ -153,6 +181,20 @@ proc dodraw() =
|
||||||
if toFocus != nil:
|
if toFocus != nil:
|
||||||
toFocus.focus()
|
toFocus.focus()
|
||||||
|
|
||||||
|
proc visibleKeys(e: Node; a, b: var VKey; h, count: var int) =
|
||||||
|
# we only care about nodes that have a key:
|
||||||
|
var hh = 0
|
||||||
|
# do not recurse if there is a 'key' field already:
|
||||||
|
if e.key != 0:
|
||||||
|
if isElementInViewport(e, hh):
|
||||||
|
inc count
|
||||||
|
inc h, hh
|
||||||
|
a = min(a, e.key)
|
||||||
|
b = max(b, e.key)
|
||||||
|
else:
|
||||||
|
for i in 0..<e.len:
|
||||||
|
visibleKeys(e[i], a, b, h, count)
|
||||||
|
|
||||||
proc reqFrame(callback: proc()) {.importc: "window.requestAnimationFrame".}
|
proc reqFrame(callback: proc()) {.importc: "window.requestAnimationFrame".}
|
||||||
|
|
||||||
proc redraw*() =
|
proc redraw*() =
|
||||||
|
|
@ -173,14 +215,6 @@ proc init*() =
|
||||||
# parent.insertBefore(kid, parent.firstChild)
|
# parent.insertBefore(kid, parent.firstChild)
|
||||||
# prependChild(parent, kid)
|
# prependChild(parent, kid)
|
||||||
|
|
||||||
proc len(x: Element): int {.importcpp: "#.childNodes.length".}
|
|
||||||
proc `[]`(x: Element; idx: int): Element {.importcpp: "#.childNodes[#]".}
|
|
||||||
|
|
||||||
proc isInt*(s: cstring): bool {.asmNoStackFrame.} =
|
|
||||||
asm """
|
|
||||||
return s.match(/^[0-9]+$/);
|
|
||||||
"""
|
|
||||||
|
|
||||||
proc suffix*(s, prefix: cstring): cstring =
|
proc suffix*(s, prefix: cstring): cstring =
|
||||||
if s.startsWith(prefix):
|
if s.startsWith(prefix):
|
||||||
result = s.substr(prefix.len)
|
result = s.substr(prefix.len)
|
||||||
|
|
@ -189,9 +223,9 @@ proc suffix*(s, prefix: cstring): cstring =
|
||||||
|
|
||||||
proc suffixAsInt*(s, prefix: cstring): int = parseInt(suffix(s, prefix))
|
proc suffixAsInt*(s, prefix: cstring): int = parseInt(suffix(s, prefix))
|
||||||
|
|
||||||
proc scrollTop*(e: Element): int {.importcpp: "#.scrollTop", nodecl.}
|
proc scrollTop*(e: Node): int {.importcpp: "#.scrollTop", nodecl.}
|
||||||
proc offsetHeight*(e: Element): int {.importcpp: "#.offsetHeight", nodecl.}
|
proc offsetHeight*(e: Node): int {.importcpp: "#.offsetHeight", nodecl.}
|
||||||
proc offsetTop*(e: Element): int {.importcpp: "#.offsetTop", nodecl.}
|
proc offsetTop*(e: Node): int {.importcpp: "#.offsetTop", nodecl.}
|
||||||
|
|
||||||
template onImpl(s) {.dirty.} =
|
template onImpl(s) {.dirty.} =
|
||||||
proc wrapper(ev: Event; n: VNode) =
|
proc wrapper(ev: Event; n: VNode) =
|
||||||
|
|
@ -203,7 +237,7 @@ proc setOnclick*(e: VNode; action: EventHandler) =
|
||||||
onImpl EventKind.onclick
|
onImpl EventKind.onclick
|
||||||
|
|
||||||
proc setOnDblclick*(e: VNode; action: EventHandler) =
|
proc setOnDblclick*(e: VNode; action: EventHandler) =
|
||||||
onImpl EventKind.onclick
|
onImpl EventKind.ondblclick
|
||||||
|
|
||||||
proc setOnfocuslost*(e: VNode; action: EventHandler) =
|
proc setOnfocuslost*(e: VNode; action: EventHandler) =
|
||||||
onImpl EventKind.onblur
|
onImpl EventKind.onblur
|
||||||
|
|
@ -214,6 +248,27 @@ 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)) =
|
||||||
|
var oldY = window.pageYOffset
|
||||||
|
|
||||||
|
proc wrapper(ev: Event) =
|
||||||
|
let dir = window.pageYOffset - oldY
|
||||||
|
if dir == 0: return
|
||||||
|
|
||||||
|
var a, b: VKey
|
||||||
|
var h, count: int
|
||||||
|
document.visibleKeys(a, b, h, count)
|
||||||
|
let avgh = h / count
|
||||||
|
let diff = toInt(dir.float / avgh)
|
||||||
|
if diff != 0:
|
||||||
|
oldY = window.pageYOffset
|
||||||
|
action(a, b, diff)
|
||||||
|
redraw()
|
||||||
|
|
||||||
|
document.addEventListener("scroll", wrapper)
|
||||||
|
|
||||||
proc setOnHashChange*(action: proc (hashPart: cstring)) =
|
proc setOnHashChange*(action: proc (hashPart: cstring)) =
|
||||||
var onhashChange {.importc: "window.onhashchange".}: proc()
|
var onhashChange {.importc: "window.onhashchange".}: proc()
|
||||||
var hashPart {.importc: "window.location.hash".}: cstring
|
var hashPart {.importc: "window.location.hash".}: cstring
|
||||||
|
|
@ -283,7 +338,7 @@ proc tag*(kind: VNodeKind; id=cstring(nil), class=cstring(nil)): VNode =
|
||||||
proc tdiv*(id=cstring(nil), class=cstring(nil)): VNode = tag(VNodeKind.tdiv, id, 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 span*(id=cstring(nil), class=cstring(nil)): VNode = tag(VNodeKind.span, id, class)
|
||||||
|
|
||||||
proc valueAsInt*(e: Element): int = parseInt(e.value)
|
proc valueAsInt*(e: Node): int = parseInt(e.value)
|
||||||
|
|
||||||
proc th*(s: cstring): VNode =
|
proc th*(s: cstring): VNode =
|
||||||
result = newVNode(VNodeKind.th)
|
result = newVNode(VNodeKind.th)
|
||||||
|
|
@ -314,7 +369,7 @@ proc tr*(kids: varargs[VNode]): VNode =
|
||||||
else:
|
else:
|
||||||
result.add td(k)
|
result.add td(k)
|
||||||
|
|
||||||
proc getAttr(e: Element; key: cstring): cstring {.
|
proc getAttr(e: Node; key: cstring): cstring {.
|
||||||
importcpp: "#.getAttribute(#)", nodecl.}
|
importcpp: "#.getAttribute(#)", nodecl.}
|
||||||
|
|
||||||
template nativeValue(ev): cstring = cast[Element](ev.target).value
|
template nativeValue(ev): cstring = cast[Element](ev.target).value
|
||||||
|
|
|
||||||
19
scrollapp.html
Normal file
19
scrollapp.html
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Todo app</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="body">
|
||||||
|
|
||||||
|
<div id="ROOT" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="nimcache/scrollapp.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
onload(window.location.pathname.split('/')[1])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
146
scrollapp.nim
Normal file
146
scrollapp.nim
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
|
||||||
|
import vdom, karax, karaxdsl, jdict, jstrutils
|
||||||
|
|
||||||
|
type
|
||||||
|
Filter = enum
|
||||||
|
all, active, completed
|
||||||
|
|
||||||
|
const
|
||||||
|
WindowSize = 50 # overestimating it does not harm
|
||||||
|
|
||||||
|
var
|
||||||
|
entries: seq[(cstring, bool)]
|
||||||
|
visibleA = 0
|
||||||
|
visibleB = WindowSize-1
|
||||||
|
middle = WindowSize div 2
|
||||||
|
selectedEntry = -1
|
||||||
|
filter: Filter
|
||||||
|
|
||||||
|
proc onTodoEnter(ev: Event; n: VNode) =
|
||||||
|
entries.add((n.value, false))
|
||||||
|
n.value = ""
|
||||||
|
|
||||||
|
proc removeHandler(ev: Event; n: VNode) =
|
||||||
|
let id = suffixAsInt(n.id, "remove:")
|
||||||
|
entries[id] = (cstring(nil), false)
|
||||||
|
|
||||||
|
proc editHandler(ev: Event; n: VNode) =
|
||||||
|
let id = suffixAsInt(n.id, "edit:")
|
||||||
|
selectedEntry = id
|
||||||
|
|
||||||
|
proc focusLost(ev: Event; n: VNode) = selectedEntry = -1
|
||||||
|
|
||||||
|
proc editEntry(ev: Event; n: VNode) =
|
||||||
|
let id = suffixAsInt(n.id, "todo-edit:")
|
||||||
|
entries[id][0] = n.value
|
||||||
|
selectedEntry = -1
|
||||||
|
|
||||||
|
proc toggleEntry(ev: Event; n: VNode) =
|
||||||
|
let id = suffixAsInt(n.id, "toggle:")
|
||||||
|
entries[id][1] = not entries[id][1]
|
||||||
|
|
||||||
|
proc onAllDone(ev: Event; n: VNode) =
|
||||||
|
entries = @[]
|
||||||
|
selectedEntry = -1
|
||||||
|
|
||||||
|
proc clearCompleted(ev: Event, n: VNode) =
|
||||||
|
for i in 0..<entries.len:
|
||||||
|
if entries[i][1]: entries[i][0] = nil
|
||||||
|
|
||||||
|
proc toClass(completed: bool): cstring =
|
||||||
|
(if completed: cstring"completed" else: cstring(nil))
|
||||||
|
|
||||||
|
proc toChecked(checked: bool): cstring =
|
||||||
|
(if checked: cstring"checked" else: cstring(nil))
|
||||||
|
|
||||||
|
proc selected(v: Filter): cstring =
|
||||||
|
(if filter == v: cstring"selected" else: cstring(nil))
|
||||||
|
|
||||||
|
proc createEntry(id: int; d: cstring; completed, selected: bool): VNode =
|
||||||
|
result = buildHtml(tr):
|
||||||
|
li(class=toClass(completed), key = id):
|
||||||
|
if not selected:
|
||||||
|
tdiv(class = "view"):
|
||||||
|
input(class = "toggle", `type` = "checkbox", checked = toChecked(completed),
|
||||||
|
onclick=toggleEntry, id="toggle:" & &id)
|
||||||
|
label(onDblClick=editHandler, id="edit:" & &id):
|
||||||
|
text d
|
||||||
|
button(class = "destroy", id="remove:" & $id, onclick=removeHandler)
|
||||||
|
else:
|
||||||
|
input(class = "edit", name = "title", id = "todo-edit:" & &id,
|
||||||
|
onfocusLost = focusLost,
|
||||||
|
onenter = editEntry, value = d, setFocus)
|
||||||
|
|
||||||
|
proc createDom(): VNode =
|
||||||
|
result = buildHtml(tdiv(class="todomvc-wrapper")):
|
||||||
|
section(class = "todoapp"):
|
||||||
|
header(class = "header"):
|
||||||
|
h1:
|
||||||
|
text "todos"
|
||||||
|
input(class = "new-todo", placeholder="What needs to be done?", name = "newTodo",
|
||||||
|
onenter = onTodoEnter, setFocus)
|
||||||
|
section(class = "main"):
|
||||||
|
input(class = "toggle-all", `type` = "checkbox", name = "toggle")
|
||||||
|
label(`for` = "toggle-all", onclick = onAllDone):
|
||||||
|
text "Mark all as complete"
|
||||||
|
var entriesCount = 0
|
||||||
|
var completedCount = 0
|
||||||
|
ul(class = "todo-list"):
|
||||||
|
for i in visibleA..visibleB:
|
||||||
|
let d = entries[i]
|
||||||
|
if d[0] != nil:
|
||||||
|
let b = case filter
|
||||||
|
of all: true
|
||||||
|
of active: not d[1]
|
||||||
|
of completed: d[1]
|
||||||
|
if b:
|
||||||
|
createEntry(i, d[0], d[1], i == selectedEntry)
|
||||||
|
inc completedCount, ord(d[1])
|
||||||
|
inc entriesCount
|
||||||
|
footer(class = "footer"):
|
||||||
|
span(class = "todo-count"):
|
||||||
|
strong:
|
||||||
|
text(&entriesCount)
|
||||||
|
text cstring" item" & &(if entriesCount != 1: "s left" else: " left")
|
||||||
|
ul(class = "filters"):
|
||||||
|
li:
|
||||||
|
anchor(class = selected(all), href = "#/"):
|
||||||
|
text "All"
|
||||||
|
li:
|
||||||
|
anchor(class = selected(active), href = "#/active"):
|
||||||
|
text "Active"
|
||||||
|
li:
|
||||||
|
anchor(class = selected(completed), href = "#/completed"):
|
||||||
|
text "Completed"
|
||||||
|
button(class = "clear-completed", onclick = clearCompleted):
|
||||||
|
text "Clear completed (" & &completedCount & ")"
|
||||||
|
|
||||||
|
|
||||||
|
setOnscroll(proc(a, b: VKey; diff: int) =
|
||||||
|
const overshoot = 10
|
||||||
|
# scroll downwards:
|
||||||
|
if diff > 0:
|
||||||
|
#visibleA = a #max(a-diff, 0)
|
||||||
|
visibleB = min(b+diff+overshoot, entries.len-1)
|
||||||
|
visibleA = max(a-overshoot, 0)
|
||||||
|
else:
|
||||||
|
visibleA = max(a-diff-overshoot, 0)
|
||||||
|
visibleB = min(b+overshoot, entries.len-1)
|
||||||
|
# visibleB = min(entries.len-1, visibleB + diff)
|
||||||
|
# visibleA = max(0, visibleB - WindowSize*2)
|
||||||
|
#else:
|
||||||
|
# visibleA = max(0, visibleA + diff)
|
||||||
|
# visibleB = min(entries.len-1, visibleA + WindowSize - 1)
|
||||||
|
kout cstring"scrolling ", a, b, diff, visibleA, visibleB
|
||||||
|
)
|
||||||
|
setOnHashChange(proc(hash: cstring) =
|
||||||
|
if hash == cstring"#/": filter = all
|
||||||
|
elif hash == cstring"#/completed": filter = completed
|
||||||
|
elif hash == cstring"#/active": filter = active
|
||||||
|
)
|
||||||
|
setRenderer createDom
|
||||||
|
|
||||||
|
proc onload(session: cstring) {.exportc.} =
|
||||||
|
for i in 0..1000:
|
||||||
|
entries.add((cstring"Entry " & &i, false))
|
||||||
|
init()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue