Add local storage for todoapp
This commit is contained in:
parent
2c0dad5094
commit
3238422412
1 changed files with 80 additions and 38 deletions
|
|
@ -1,21 +1,52 @@
|
||||||
|
|
||||||
import vdom, karax, karaxdsl, jstrutils
|
import vdom, karax, karaxdsl, jstrutils, components
|
||||||
|
|
||||||
type
|
type
|
||||||
Filter = enum
|
Filter = enum
|
||||||
all, active, completed
|
all, active, completed
|
||||||
|
|
||||||
var
|
var
|
||||||
entries: seq[(cstring, bool)]
|
|
||||||
selectedEntry = -1
|
selectedEntry = -1
|
||||||
filter: Filter
|
filter: Filter
|
||||||
|
entriesSize: int
|
||||||
|
|
||||||
|
proc getItem(key : cstring ): cstring {.importc: "localStorage.getItem"}
|
||||||
|
proc setItem(key, value : cstring ) {.importc: "localStorage.setItem"}
|
||||||
|
proc clearEntries() {.importc: "localStorage.clear"}
|
||||||
|
|
||||||
|
proc getEntryCstring(pos : int): cstring =
|
||||||
|
result = getItem(cstring($pos & "cstring"))
|
||||||
|
if result == cstring"null":
|
||||||
|
result = nil
|
||||||
|
|
||||||
|
proc getEntryBool(pos : int): bool =
|
||||||
|
var value = getItem(cstring($pos & "bool"))
|
||||||
|
result = value == cstring"true"
|
||||||
|
|
||||||
|
proc setEntryCstring(pos : int, value : cstring) =
|
||||||
|
setItem(cstring($pos & "cstring"), value)
|
||||||
|
|
||||||
|
proc setEntryBool(pos : int, value : bool) =
|
||||||
|
var val = cstring"true"
|
||||||
|
if not value:
|
||||||
|
val = cstring"false"
|
||||||
|
setItem(cstring($pos & "bool"), val)
|
||||||
|
|
||||||
|
proc addEntry(str : cstring, bval : bool) =
|
||||||
|
setEntryCstring(entriesSize, str)
|
||||||
|
setEntryBool(entriesSize, bval)
|
||||||
|
inc entriesSize
|
||||||
|
|
||||||
|
proc setEntry(pos : int, str : cstring, bval : bool) =
|
||||||
|
setEntryCstring(pos, str)
|
||||||
|
setEntryBool(pos, bval)
|
||||||
|
|
||||||
proc onTodoEnter(ev: Event; n: VNode) =
|
proc onTodoEnter(ev: Event; n: VNode) =
|
||||||
entries.add((n.value, false))
|
addEntry(n.value, false)
|
||||||
n.value = ""
|
n.value = ""
|
||||||
|
|
||||||
proc removeHandler(ev: Event; n: VNode) =
|
proc removeHandler(ev: Event; n: VNode) =
|
||||||
entries[n.key] = (cstring(nil), false)
|
setEntry(n.key, cstring(nil), false)
|
||||||
|
|
||||||
proc editHandler(ev: Event; n: VNode) =
|
proc editHandler(ev: Event; n: VNode) =
|
||||||
selectedEntry = n.key
|
selectedEntry = n.key
|
||||||
|
|
@ -23,20 +54,20 @@ proc editHandler(ev: Event; n: VNode) =
|
||||||
proc focusLost(ev: Event; n: VNode) = selectedEntry = -1
|
proc focusLost(ev: Event; n: VNode) = selectedEntry = -1
|
||||||
|
|
||||||
proc editEntry(ev: Event; n: VNode) =
|
proc editEntry(ev: Event; n: VNode) =
|
||||||
entries[n.key][0] = n.value
|
setEntryCstring(n.key, n.value)
|
||||||
selectedEntry = -1
|
selectedEntry = -1
|
||||||
|
|
||||||
proc toggleEntry(ev: Event; n: VNode) =
|
proc toggleEntry(ev: Event; n: VNode) =
|
||||||
let id = n.key
|
let id = n.key
|
||||||
entries[id][1] = not entries[id][1]
|
setEntryBool(id, not getEntryBool(id))
|
||||||
|
|
||||||
proc onAllDone(ev: Event; n: VNode) =
|
proc onAllDone(ev: Event; n: VNode) =
|
||||||
entries = @[]
|
clearEntries()
|
||||||
selectedEntry = -1
|
selectedEntry = -1
|
||||||
|
|
||||||
proc clearCompleted(ev: Event, n: VNode) =
|
proc clearCompleted(ev: Event, n: VNode) =
|
||||||
for i in 0..<entries.len:
|
for i in 0..<entriesSize:
|
||||||
if entries[i][1]: entries[i][0] = nil
|
if getEntryBool(i): setEntryCstring(i, nil)
|
||||||
|
|
||||||
proc toClass(completed: bool): cstring =
|
proc toClass(completed: bool): cstring =
|
||||||
(if completed: cstring"completed" else: cstring(nil))
|
(if completed: cstring"completed" else: cstring(nil))
|
||||||
|
|
@ -47,7 +78,7 @@ 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))
|
||||||
|
|
||||||
proc createEntry(id: int; d: cstring; completed, selected: bool): VNode =
|
proc createEntry(id: int; d: cstring; completed, selected: bool): VNode {.component.} =
|
||||||
result = buildHtml(tr):
|
result = buildHtml(tr):
|
||||||
li(class=toClass(completed)):
|
li(class=toClass(completed)):
|
||||||
if not selected:
|
if not selected:
|
||||||
|
|
@ -62,32 +93,8 @@ proc createEntry(id: int; d: cstring; completed, selected: bool): VNode =
|
||||||
onblur = focusLost,
|
onblur = focusLost,
|
||||||
onkeyupenter = editEntry, value = d, setFocus)
|
onkeyupenter = editEntry, value = d, setFocus)
|
||||||
|
|
||||||
proc createDom(): VNode =
|
proc makeFooter(entriesCount, completedCount: int): VNode {.component.} =
|
||||||
result = buildHtml(tdiv(class="todomvc-wrapper")):
|
result = buildHtml(footer(class = "footer")):
|
||||||
section(class = "todoapp"):
|
|
||||||
header(class = "header"):
|
|
||||||
h1:
|
|
||||||
text "todos"
|
|
||||||
input(class = "new-todo", placeholder="What needs to be done?", name = "newTodo",
|
|
||||||
onkeyupenter = 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, d in pairs(entries):
|
|
||||||
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"):
|
span(class = "todo-count"):
|
||||||
strong:
|
strong:
|
||||||
text(&entriesCount)
|
text(&entriesCount)
|
||||||
|
|
@ -105,9 +112,44 @@ proc createDom(): VNode =
|
||||||
button(class = "clear-completed", onclick = clearCompleted):
|
button(class = "clear-completed", onclick = clearCompleted):
|
||||||
text "Clear completed (" & &completedCount & ")"
|
text "Clear completed (" & &completedCount & ")"
|
||||||
|
|
||||||
|
proc makeHeader(): VNode {.component.} =
|
||||||
|
result = buildHtml(header(class = "header")):
|
||||||
|
h1:
|
||||||
|
text "todos"
|
||||||
|
input(class = "new-todo", placeholder="What needs to be done?", name = "newTodo",
|
||||||
|
onkeyupenter = onTodoEnter, setFocus)
|
||||||
|
|
||||||
|
proc createDom(): VNode =
|
||||||
|
result = buildHtml(tdiv(class="todomvc-wrapper")):
|
||||||
|
section(class = "todoapp"):
|
||||||
|
makeHeader()
|
||||||
|
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, d in pairs(entries):
|
||||||
|
for i in 0..entriesSize-1:
|
||||||
|
|
||||||
|
var d0 = getEntryCstring(i)
|
||||||
|
var d1 = getEntryBool(i)
|
||||||
|
if d0 != nil:
|
||||||
|
let b = case filter
|
||||||
|
of all: true
|
||||||
|
of active: not d1
|
||||||
|
of completed: d1
|
||||||
|
if b:
|
||||||
|
createEntry(i, d0, d1, i == selectedEntry)
|
||||||
|
inc completedCount, ord(d1)
|
||||||
|
inc entriesCount
|
||||||
|
makeFooter(entriesCount, completedCount)
|
||||||
|
|
||||||
setOnHashChange(proc(hash: cstring) =
|
setOnHashChange(proc(hash: cstring) =
|
||||||
if hash == "#/": filter = all
|
if hash == "#/": filter = all
|
||||||
elif hash == "#/completed": filter = completed
|
elif hash == "#/completed": filter = completed
|
||||||
elif hash == "#/active": filter = active
|
elif hash == "#/active": filter = active
|
||||||
)
|
)
|
||||||
|
entriesSize = 0
|
||||||
setRenderer createDom
|
setRenderer createDom
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue