This commit is contained in:
Andreas Rumpf 2017-06-22 21:21:54 +02:00
commit 9aa3ba545d
5 changed files with 118 additions and 13 deletions

View file

@ -106,7 +106,10 @@ proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node =
let x = VComponent(n)
if x.onAttachImpl != nil: x.onAttachImpl(x)
assert x.renderImpl != nil
result = vnodeToDom(x.renderImpl(x), kxi)
if x.expanded == nil:
x.expanded = x.renderImpl(x)
x.updatedImpl(x)
result = vnodeToDom(x.expanded, kxi)
attach n
return result
else:
@ -133,7 +136,9 @@ proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node =
if not n.style.isNil: applyStyle(result, n.style)
proc same(n: VNode, e: Node): bool =
if toTag[n.kind] == e.nodename:
if n.kind == VNodeKind.component:
result = same(VComponent(n).expanded, e)
elif toTag[n.kind] == e.nodename:
result = true
if n.kind != VNodeKind.text:
if e.len != n.len: return false
@ -177,7 +182,9 @@ proc eq(a, b: VNode; deep: bool): EqResult =
result = similar
if not sameAttrs(a, b): return different
if a.class != b.class: return different
if a.style != b.style: return similar
if not eq(a.style, b.style):
kout cstring"yes, styles differ"
return similar
# Do not test event listeners here!
return result
@ -218,11 +225,22 @@ proc updateDom(newNode, oldNode: VNode) =
for i in 0 ..< newNode.len:
updateDom(newNode[i], oldNode[i])
proc printV(n: VNode; depth: cstring = "") =
kout depth, cstring($n.kind), n.myid, cstring"key ", n.key
#for k, v in pairs(n.style):
# kout depth, "style: ", k, v
if n.kind == VNodeKind.component:
let nn = VComponent(n)
if nn.expanded != nil: printV(nn.expanded, ">>" & depth)
for i in 0 ..< n.len:
printV(n[i], depth & " ")
proc updateElement(parent, current: Node, newNode, oldNode: VNode;
kxi: KaraxInstance): EqResult =
result = eq(newNode, oldNode, deep=false)
if result <= different:
var n: Node
var state = 0
if result == changed:
assert oldNode.kind == VNodeKind.component
let x = VComponent(oldNode)
@ -231,7 +249,11 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
x.updatedImpl(x)
if oldExpanded == nil:
n = vnodeToDom(x.expanded, kxi)
state = 1
else:
kout cstring"now comparing components"
printV(oldExpanded)
printV(x.expanded)
if updateElement(parent, current, x.expanded, oldExpanded, kxi) >= similar:
x.expanded = oldExpanded
n = oldExpanded.dom
@ -239,12 +261,16 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
else:
n = x.expanded.dom
doAssert n != nil, "expanded.dom is nil"
state = 2
return
else:
detach(oldNode)
n = vnodeToDom(newNode, kxi)
state = 3
if parent == nil:
replaceById(kxi.rootId, n)
else:
kout cstring"state ", state, parent, current
parent.replaceChild(n, current)
elif result == similar:
updateStyles(newNode, oldNode, false)
@ -295,8 +321,9 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1:
if updateElement(current, current.childNodes[i],
newNode[i], oldNode[i], kxi) >= similar:
let res = updateElement(current, current.childNodes[i],
newNode[i], oldNode[i], kxi)
if res != different:
newNode[i] = oldNode[i]
var nextChildPos = oldPos + 1
@ -323,6 +350,9 @@ proc dodraw(kxi: KaraxInstance) =
let newtree = kxi.renderer()
newtree.id = kxi.rootId
kxi.toFocus = nil
#if kxi.currentTree != nil:
# kout cstring"same? ", same(kxi.currentTree, document.getElementById(kxi.rootId))
kout cstring"dodraw -----------------------------"
if kxi.currentTree == nil:
kxi.currentTree = newtree
let asdom = vnodeToDom(kxi.currentTree, kxi)
@ -330,8 +360,8 @@ proc dodraw(kxi: KaraxInstance) =
else:
let olddom = document.getElementById(kxi.rootId)
discard updateElement(nil, olddom, newtree, kxi.currentTree, kxi)
#assert same(newtree, document.getElementById("ROOT"))
kxi.currentTree = newtree
#kout cstring"same? ", same(kxi.currentTree, document.getElementById(kxi.rootId))
if not kxi.postRenderCallback.isNil:
kxi.postRenderCallback()
@ -348,10 +378,10 @@ proc redraw*(kxi: KaraxInstance = kxi) =
if drawTimeout != nil:
clearTimeout(drawTimeout)
drawTimeout = setTimeout(dodraw, 30)
elif true:
elif false:
reqFrame(proc () = kxi.dodraw)
else:
dodraw()
dodraw(kxi)
proc init(ev: Event) =
reqFrame(proc () = kxi.dodraw)

View file

@ -119,6 +119,7 @@ type
style*: VStyle ## the style that should be applied to the virtual node.
dom*: Node ## the attached real DOM node. Can be 'nil' if the virtual node
## is not part of the virtual DOM anymore.
myid*: int
VComponent* = ref object of VNode ## The abstract class for every karax component.
renderImpl*: proc(self: VComponent): VNode

View file

@ -213,7 +213,9 @@ type
attrs: array[StyleAttr, cstring]
mask: set[StyleAttr]
proc `==`*(a, b: VStyle): bool =
proc kout[T](x: T) {.importc: "console.log", varargs.}
proc eq*(a, b: VStyle): bool =
if a.isNil:
if b.isNil: return true
else: return false
@ -247,9 +249,10 @@ proc setStyle(d: Style; key, val: cstring) {.importcpp: "#[#] = #".}
proc merge*(a, b: VStyle): VStyle =
## merges two styles. ``b`` takes precedence over ``a``.
result = VStyle(mask: a.mask + b.mask)
result = VStyle(mask: {})
for i in low(StyleAttr)..high(StyleAttr):
result.attrs[i] = if b.attrs[i].isNil: a.attrs[i] else: b.attrs[i]
if result.attrs[i] != nil: incl(result.mask, i)
proc applyStyle*(n: Node; s: VStyle) =
## apply the style to the real DOM node ``n``.
@ -257,6 +260,11 @@ proc applyStyle*(n: Node; s: VStyle) =
for x in s.mask:
n.style.setStyle(toStyleAttrName[x], s.attrs[x])
iterator pairs*(v: VStyle): (cstring, cstring) =
if v != nil:
for x in v.mask:
yield (toStyleAttrName[x], v.attrs[x])
import jstrutils
proc rgb*(r, g, b: range[0..255]): cstring =

58
tests/blur.nim Normal file
View file

@ -0,0 +1,58 @@
import vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils
type TextInput* = ref object of VComponent
value: cstring
isActive: bool
var renderId: int
proc render(x: VComponent): VNode =
let self = TextInput(x)
let style = style(
(StyleAttr.position, cstring"relative"),
(StyleAttr.paddingLeft, cstring"10px"),
(StyleAttr.paddingRight, cstring"5px"),
(StyleAttr.height, cstring"30px"),
(StyleAttr.lineHeight, cstring"30px"),
(StyleAttr.border, cstring"solid 1px " & (if self.isActive: cstring"red" else: cstring"black")),
(StyleAttr.fontSize, cstring"12px"),
(StyleAttr.fontWeight, cstring"600")
).merge(self.style)
let inputStyle = style.merge(style(
(StyleAttr.color, cstring"inherit"),
(StyleAttr.fontSize, cstring"inherit"),
(StyleAttr.fontWeight, cstring"inherit"),
(StyleAttr.fontFamily, cstring"inherit"),
(StyleAttr.position, cstring"absolute"),
(StyleAttr.top, cstring"0"),
(StyleAttr.left, cstring"0"),
(StyleAttr.height, cstring"100%"),
(StyleAttr.width, cstring"100%"),
(StyleAttr.border, cstring"none"),
(StyleAttr.backgroundColor, cstring"transparent"),
))
proc flip(ev: Event; n: VNode) =
self.isActive = not self.isActive
markDirty(self)
kout cstring"rendering ", self.myid
inc renderId
result = buildHtml(tdiv(style=style, key=renderId)):
input(style=inputStyle, value=self.value, onblur=flip, onfocus=flip,
key=renderId)
var gid = 0
proc newTextInput*(style: VStyle = VStyle(); value: cstring = cstring""): TextInput =
result = newComponent(TextInput, render)
result.style = style
result.value = value
inc gid
result.myid = gid
proc createDom(): VNode =
result = buildHtml(tdiv):
newTextInput(value=cstring"test")
setRenderer createDom

View file

@ -21,12 +21,20 @@ proc exec(cmd: string) =
proc main =
var op = initOptParser()
let rest = op.cmdLineRest
var rest = op.cmdLineRest
var file = ""
var run = false
while true:
op.next()
case op.kind
of cmdLongOption, cmdShortOption: discard
of cmdLongOption:
if op.key == "run":
run = true
rest = rest.replace("--run ")
of cmdShortOption:
if op.key == "r":
run = true
rest = rest.replace("-r ")
of cmdArgument: file = op.key
of cmdEnd: break
@ -36,6 +44,6 @@ proc main =
exec("nim js --out:nimcache/" & name & ".js " & rest)
let dest = "nimcache" / name & ".html"
writeFile(dest, html % name)
openDefaultBrowser(dest)
if run: openDefaultBrowser(dest)
main()