reactive sequence works now

This commit is contained in:
Andreas Rumpf 2017-08-19 14:54:10 +02:00
commit 56a50679de
4 changed files with 137 additions and 25 deletions

View file

@ -2,24 +2,61 @@
import vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils, reactive
type
User = ref object
User = ref object of ReactiveBase
firstname, lastname: cstring
var gu = @[newReactive(User(firstname: "Some", lastname: "Body")),
newReactive(User(firstname: "Some", lastname: "One")),
newReactive(User(firstname: "Some", lastname: "Two"))]
var gu = newRSeq(@[ (User(firstname: "Some", lastname: "Body")),
(User(firstname: "Some", lastname: "One")),
(User(firstname: "Some", lastname: "Two"))])
var clicks = 0
proc renderUser(u: Reactive[User]): VNode {.track.} =
result = buildHtml(button):
text u.now.firstname & " " & u.now.lastname
proc onclick(ev: Event; n: VNode) =
inc clicks
u <- User(firstname: "Another", lastname: &clicks)
discard """
# Text gets a *reactive* string in the first place!
# Text can register and knows how to update itself!
proc toReact(): RString =
observe(u):
u.firstname & u.lastname
proc main(): VNode =
result = buildHtml(tdiv):
for i in 0..high(gu):
renderUser(gu[i])
let t = text(u.firstname & " " & u.lastname)
observe(u, t.update(u.firstname & " " & u.lastname))
t
"""
setInitializer(main)
proc renderUser(u: User): VNode = #{.track.} =
proc inner(u: User): VNode =
result = buildHtml(tdiv):
text u.firstname & " " & u.lastname
button:
text "(X)"
proc onclick(ev: Event; n: VNode) =
#u.firstname = "kfdj"
#u.lastname = &clicks
#notifyObservers(u)
gu.deleteElem(u)
result = inner(u)
doTrack(u, result, inner(u))
template vmap(x: RSeq; elem, f: untyped): VNode =
let tmp = buildHtml(elem):
for i in 0..<len(x):
f(x[i])
doTrackResize(x, tmp, f(x[pos]))
tmp
proc main(gu: RSeq[User]): VNode = #{.track.} =
proc inner(gu: RSeq[User]): VNode =
result = buildHtml(tdiv):
vmap(gu, tdiv, renderUser)
tdiv:
button:
text "Add User"
proc onclick(ev: Event; n: VNode) =
inc clicks
gu.add User(firstname: "Added", lastname: &clicks)
result = inner(gu)
doTrack(gu, result, inner(gu))
proc init(): VNode = main(gu)
setInitializer(init)

View file

@ -514,6 +514,24 @@ when defined(stats):
result = m + 1
inc total
proc runDel*(kxi: KaraxInstance; parent: VNode; position: int) =
detach(parent[position])
let current = parent.dom
kxi.addPatch(pkRemove, current, current.childNodes[position], nil)
parent.delete(position)
applyPatch(kxi)
proc runIns*(kxi: KaraxInstance; parent, kid: VNode; position: int) =
let current = parent.dom
if position >= parent.len:
kxi.addPatch(pkAppend, current, nil, kid)
parent.add(kid)
else:
let before = current.childNodes[position]
kxi.addPatch(pkInsertBefore, current, before, kid)
parent.insert(kid, position)
applyPatch(kxi)
proc runDiff*(kxi: KaraxInstance; oldNode, newNode: VNode) =
let olddom = oldNode.dom
discard diff(newNode, oldNode, nil, olddom, kxi)

View file

@ -32,7 +32,7 @@ type
s: seq[T]
L: RInt
proc addSink[T](x: Reactive[T]; sink: proc(msg: Message; pos: int)) =
proc addSink(x: ReactiveBase; sink: proc(msg: Message; pos: int)) =
x.sinks.add sink
proc addSink(x: ReactiveBase; key: cstring; sink: proc(msg: Message; pos: int)) =
@ -88,7 +88,7 @@ proc `<-`*[T](x: Reactive[T], val: T) =
x.broadcast(Mark)
x.broadcast(Changed)
proc changed*(x: ReactiveBase) =
proc notifyObservers*(x: ReactiveBase) =
x.broadcast(Mark)
x.broadcast(Changed)
@ -125,10 +125,17 @@ proc newRSeq*[T](data: seq[T]): RSeq[T] =
for i in 0..high(data):
result.s[i] = data[i]
proc `[]=`[T](x: RSeq[T]; index: int; v: T) =
proc `[]=`*[T](x: RSeq[T]; index: int; v: T) =
x.s[index] = v
proc `[]`[T](x: RSeq[T]; index: int): T = x.s[index]
proc `[]`*[T](x: RSeq[T]; index: int): T = x.s[index]
proc len*[T](x: RSeq[T]): int = x.s.len
proc add*[T](x: RSeq[T]; y: T) =
let position = x.s.len
x.s.add(y)
x.broadcast(Mark)
x.broadcast(Inserted, position)
proc insert*[T](x: RSeq[T]; y: T; position = 0) =
x.s.insert(y, position)
@ -140,6 +147,16 @@ proc delete*[T](x: RSeq[T]; position = 0) =
x.broadcast(Mark)
x.broadcast(Deleted, position)
proc deleteElem*[T](x: RSeq[T]; y: T) =
var position = -1
for i in 0..<x.len:
if x[i] == y:
position = i; break
if position >= 0:
x.s.delete(position)
x.broadcast(Mark)
x.broadcast(Deleted, position)
proc map*[T, U](x: RSeq[T], f: proc(x: T): U): RSeq[U] =
let xl = x.L.value
let res = newRSeq[U](xl)
@ -159,24 +176,60 @@ proc map*[T, U](x: RSeq[T], f: proc(x: T): U): RSeq[U] =
import macros
template trackImpl(r: ReactiveBase; key: cstring; a, b: untyped) =
when r is Reactive:
when r is ReactiveBase:
addSink r, key, proc(m: Message; pos: int) =
if m == Changed:
runDiff(kxi, a, b)
template doTrack*(r: ReactiveBase; a, b: untyped) {.dirty.} =
bind addSink, Message, RSeq, Changed, Deleted, Inserted
addSink r, proc(m: Message; pos: int) =
#when r is RSeq:
#echo "Message: ", m, " ", pos
if m == Changed: karax.runDiff(kxi, a, b)
template doTrackResize*(r: ReactiveBase; a, b: untyped) {.dirty.} =
bind addSink, Message, RSeq, Changed, Deleted, Inserted
addSink r, proc(m: Message; pos: int) =
#when r is RSeq:
#echo "Message: ", m, " ", pos
case m
of Deleted: karax.runDel(kxi, a, pos)
of Inserted:
karax.runIns(kxi, a, b, pos)
else: discard
template notifyImpl(r: untyped) =
when r is ReactiveBase:
notifyObservers(r)
proc root(n: NimNode): NimNode =
result = n
while result.kind in {nnkDotExpr, nnkBracketExpr}: result = result[0]
proc analyse(n: NimNode; paramList: seq[NimNode]): NimNode =
result = n
#if n.kind in {nnkAsgn, nnkFastAsgn}:
#
# n[0]
#else:
# recurse()
macro track*(procDef: untyped): untyped =
let params = params(procDef)
let key = lineInfo(procDef)
var call = newCall(procDef.name)
var trackings = newStmtList()
for b in procDef.body: trackings.add b
var paramList: seq[NimNode] = @[]
for j in 1..<params.len:
let x = params[j]
expectKind x, nnkIdentDefs
for i in 0..x.len-3:
let param = x[i]
call.add param
trackings.add getAst(trackImpl(param, key, ident"result", call))
for i in 0..x.len-3: paramList.add(x[i])
for b in procDef.body: trackings.add analyse(b, paramList)
for param in paramList:
call.add param
trackings.add getAst(trackImpl(param, key, ident"result", call))
result = copyNimTree(procDef)
result.body = trackings

View file

@ -224,6 +224,10 @@ 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 delete*(parent: VNode; position: int) =
parent.kids.delete(position)
proc insert*(parent, kid: VNode; position: int) =
parent.kids.insert(kid, position)
proc newVNode*(kind: VNodeKind): VNode = VNode(kind: kind, index: -1)
proc tree*(kind: VNodeKind; kids: varargs[VNode]): VNode =