Update DOM node properties and listeners on renderer update (#117)
* Update DOM properties and listeners on renderer update * Fix listeners not passed in EmptyView init
This commit is contained in:
parent
b7d7b125b2
commit
9bed8e0cb8
|
@ -20,10 +20,31 @@ import TokamakCore
|
|||
|
||||
public final class DOMNode: Target {
|
||||
let ref: JSObjectRef
|
||||
private var listeners: [String: JSClosure]
|
||||
|
||||
init<V: View>(_ view: V, _ ref: JSObjectRef) {
|
||||
init<V: View>(_ view: V, _ ref: JSObjectRef, _ listeners: [String: Listener] = [:]) {
|
||||
self.ref = ref
|
||||
self.listeners = [:]
|
||||
super.init(view)
|
||||
reinstall(listeners)
|
||||
}
|
||||
|
||||
/// Removes all existing event listeners on this DOM node and install new ones from
|
||||
/// the `listeners` argument
|
||||
func reinstall(_ listeners: [String: Listener]) {
|
||||
for (event, jsClosure) in self.listeners {
|
||||
_ = ref.removeEventListener!(event, jsClosure)
|
||||
}
|
||||
self.listeners = [:]
|
||||
|
||||
for (event, listener) in listeners {
|
||||
let jsClosure = JSClosure {
|
||||
listener($0[0].object!)
|
||||
return .undefined
|
||||
}
|
||||
_ = ref.addEventListener!(event, jsClosure)
|
||||
self.listeners[event] = jsClosure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +57,11 @@ public final class DOMRenderer: Renderer {
|
|||
|
||||
public init<V: View>(_ view: V, _ ref: JSObjectRef) {
|
||||
rootRef = ref
|
||||
reconciler = StackReconciler(view: view, target: DOMNode(view, ref), renderer: self) { closure in
|
||||
reconciler = StackReconciler(
|
||||
view: view,
|
||||
target: DOMNode(view, ref),
|
||||
renderer: self
|
||||
) { closure in
|
||||
let fn = JSClosure { _ in
|
||||
closure()
|
||||
return .undefined
|
||||
|
@ -67,14 +92,7 @@ public final class DOMRenderer: Renderer {
|
|||
let lastChild = children[Int(length) - 1].object
|
||||
else { return nil }
|
||||
|
||||
for (event, listener) in listeners {
|
||||
_ = lastChild.addEventListener!(event, JSClosure {
|
||||
listener($0[0].object!)
|
||||
return .undefined
|
||||
})
|
||||
}
|
||||
|
||||
return DOMNode(host.view, lastChild)
|
||||
return DOMNode(host.view, lastChild, listeners)
|
||||
}
|
||||
|
||||
public func update(target: DOMNode, with host: MountedHost) {
|
||||
|
@ -83,7 +101,7 @@ public final class DOMRenderer: Renderer {
|
|||
transform: { (html: AnyHTML) in html }
|
||||
) else { return }
|
||||
|
||||
html.update(dom: target.ref)
|
||||
html.update(dom: target)
|
||||
}
|
||||
|
||||
public func unmount(
|
||||
|
|
|
@ -42,10 +42,21 @@ extension AnyHTML {
|
|||
"""
|
||||
}
|
||||
|
||||
func update(dom: JSObjectRef) {
|
||||
// FIXME: handle attributes and listeners here
|
||||
func update(dom: DOMNode) {
|
||||
// FIXME: is there a sensible way to diff attributes and listeners to avoid
|
||||
// crossing the JavaScript bridge and touching DOM if not needed?
|
||||
|
||||
// @carson-katri: For diffing, could you build a Set from the keys and values of the dictionary,
|
||||
// then use the standard lib to get the difference?
|
||||
|
||||
for (attribute, value) in attributes {
|
||||
_ = dom.ref[dynamicMember: attribute] = JSValue(stringLiteral: value)
|
||||
}
|
||||
|
||||
dom.reinstall(listeners)
|
||||
|
||||
guard let innerHTML = innerHTML else { return }
|
||||
dom.innerHTML = .string(innerHTML)
|
||||
dom.ref.innerHTML = .string(innerHTML)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,10 +91,7 @@ extension HTML where Content == EmptyView {
|
|||
_ attributes: [String: String] = [:],
|
||||
listeners: [String: Listener] = [:]
|
||||
) {
|
||||
self.tag = tag
|
||||
self.attributes = attributes
|
||||
self.listeners = listeners
|
||||
content = EmptyView()
|
||||
self = HTML(tag, attributes, listeners: listeners) { EmptyView() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue