try to make components aware of the previous state

This commit is contained in:
Araq 2017-07-02 09:34:58 +02:00
commit d538b7bc93
3 changed files with 50 additions and 33 deletions

View file

@ -132,7 +132,8 @@ proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node =
assert x.renderImpl != nil
if x.expanded == nil:
x.expanded = x.renderImpl(x)
x.updatedImpl(x)
# x.updatedImpl(x, nil)
assert x.expanded != nil
result = vnodeToDom(x.expanded, kxi)
attach n
return result
@ -197,7 +198,7 @@ proc eq(a, b: VNode): EqResult =
if a.text != b.text: return different
let x = VComponent(b)
assert x.changedImpl != nil
return if x.changedImpl(x): changed else: identical
return if x.changedImpl(x, VComponent(a)): changed else: identical
if not sameAttrs(a, b): return different
if a.class != b.class: return different
if not eq(a.style, b.style): return similar
@ -282,6 +283,7 @@ proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): E
if newNode.events.len != 0 or oldNode.events.len != 0:
mergeEvents(newNode, oldNode, kxi)
if oldNode.kind == VNodeKind.input or oldNode.kind == VNodeKind.textarea:
echo "comparing ", oldNode.text != newNode.text
if oldNode.text != newNode.text:
oldNode.text = newNode.text
oldNode.dom.value = newNode.text
@ -359,9 +361,11 @@ proc diff(newNode, oldNode: VNode; parent, current: Node; kxi: KaraxInstance): E
of changed:
assert oldNode.kind == VNodeKind.component
let x = VComponent(oldNode)
x.updatedImpl(x, VComponent newNode)
let oldExpanded = x.expanded
x.expanded = x.renderImpl(x)
x.updatedImpl(x)
x.version = VComponent(newNode).version
#x.updatedImpl(x)
if oldExpanded == nil:
detach(oldNode)
kxi.addPatch(pkReplace, parent, current, x.expanded)

View file

@ -124,14 +124,13 @@ type
VComponent* = ref object of VNode ## The abstract class for every karax component.
renderImpl*: proc(self: VComponent): VNode
changedImpl*: proc(self: VComponent): bool
updatedImpl*: proc(self: VComponent)
changedImpl*: proc(self, newInstance: VComponent): bool
updatedImpl*: proc(self, newInstance: VComponent)
onAttachImpl*: proc(self: VComponent)
onDetachImpl*: proc(self: VComponent)
version*: int ## Update this to trigger a redraw by karax. Usually you
## should call 'markDirty' instead which is an alias for
## 'inc version'.
renderedVersion*: int ## Do not touch. Used by karax.
expanded*: VNode ## Do not touch. Used by karax. The VDOM the component
## expanded to.
@ -153,19 +152,19 @@ proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
VNode(kind: VNodeKind.dthunk, text: name, key: -1, kids: @args)
proc defaultChangedImpl*(v: VComponent): bool =
proc defaultChangedImpl*(v, newInstance: VComponent): bool =
## The default implementation of 'changed'.
result = v.version != v.renderedVersion
result = v.version != newInstance.version
proc defaultUpdatedImpl*(v: VComponent) =
v.renderedVersion = v.version
proc defaultUpdatedImpl*(v, newInstance: VComponent) =
discard
template newComponent*[T](t: typeDesc[T];
render: (proc(self: VComponent): VNode) not nil,
onAttach: proc(self: VComponent) = nil,
onDetach: proc(self: VComponent) = nil,
changed: (proc(self: VComponent): bool) = defaultChangedImpl,
updated: proc(self: VComponent) = defaultUpdatedImpl): T =
changed: (proc(self, newInstance: VComponent): bool) = defaultChangedImpl,
updated: proc(self, newInstance: VComponent) = defaultUpdatedImpl): T =
## Use this template to create new components.
T(kind: VNodeKind.component, key: -1,
text: cstring(astToStr(t)), renderImpl: render,

View file

@ -41,18 +41,33 @@ proc render(x: VComponent): VNode =
proc onchanged(ev: Event; n: VNode) =
if self.onchange != nil and self.value != n.value:
self.onchange n.value
self.value = n.value
#self.value = n.value
result = buildHtml(tdiv(style=style)):
input(style=inputStyle, value=self.value, onblur=flip, onfocus=flip, onkeyup=onchanged)
proc setValue(x: TextInput; value: cstring) =
x.value = value
markDirty(x)
#proc setValue(x: TextInput; value: cstring) =
# x.value = value
# markDirty(x)
proc update(current, next: VComponent) =
let current = TextInput(current)
let next = TextInput(next)
if not current.isActive:
current.value = next.value
echo "updated! ", current.value
else:
echo "not updated! ", current.value
#markDirty(current)
proc changed(current, next: VComponent): bool =
let current = TextInput(current)
let next = TextInput(next)
result = next.value != current.value or true
proc newTextInput*(style: VStyle = VStyle(); value: cstring = cstring"",
onchange: proc(v: cstring) = nil): TextInput =
result = newComponent(TextInput, render)
result = newComponent(TextInput, render, changed=changed, updated=update)
result.style = style
result.value = value
result.onchange = onchange
@ -76,32 +91,25 @@ proc renderComb(self: VComponent): VNode =
button(onclick=bu):
text "reset"
proc changed(self: VComponent): bool =
let self = Combined(self)
result = self.a.changedImpl(self.a) or self.b.changedImpl(self.b)
when false:
proc changed(self: VComponent): bool =
let self = Combined(self)
result = self.a.changedImpl(self.a) or self.b.changedImpl(self.b)
proc newCombined*(style: VStyle = VStyle()): Combined =
result = newComponent(Combined, renderComb, changed=changed)
result.a = newTextInput(style, "AAA")
result.b = newTextInput(style, "BBB")
proc newCombined*(style: VStyle = VStyle()): Combined =
result = newComponent(Combined, renderComb, changed=changed)
result.a = newTextInput(style, "AAA")
result.b = newTextInput(style, "BBB")
var
persons: seq[cstring] = @[cstring"Karax", "Abathur", "Fenix"]
selected = -1
errmsg = cstring""
ti = newTextInput(VStyle(), "", proc (v: cstring) =
if v.len > 0:
if selected >= 0: persons[selected] = v
errmsg = ""
else:
errmsg = "name must not be empty"
)
proc renderPerson(text: cstring, index: int): VNode =
proc select(ev: Event, n: VNode) =
selected = index
ti.setValue(persons[selected])
result = buildHtml():
tdiv(onClick=select):
@ -113,7 +121,13 @@ proc createDom(): VNode =
for index, text in persons.pairs:
renderPerson(text, index)
tdiv:
ti
newTextInput(VStyle(), if selected >= 0: persons[selected] else: "", proc (v: cstring) =
if v.len > 0:
if selected >= 0: persons[selected] = v
errmsg = ""
else:
errmsg = "name must not be empty"
)
tdiv:
text errmsg