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 {
|
public final class DOMNode: Target {
|
||||||
let ref: JSObjectRef
|
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.ref = ref
|
||||||
|
self.listeners = [:]
|
||||||
super.init(view)
|
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) {
|
public init<V: View>(_ view: V, _ ref: JSObjectRef) {
|
||||||
rootRef = ref
|
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
|
let fn = JSClosure { _ in
|
||||||
closure()
|
closure()
|
||||||
return .undefined
|
return .undefined
|
||||||
|
@ -67,14 +92,7 @@ public final class DOMRenderer: Renderer {
|
||||||
let lastChild = children[Int(length) - 1].object
|
let lastChild = children[Int(length) - 1].object
|
||||||
else { return nil }
|
else { return nil }
|
||||||
|
|
||||||
for (event, listener) in listeners {
|
return DOMNode(host.view, lastChild, listeners)
|
||||||
_ = lastChild.addEventListener!(event, JSClosure {
|
|
||||||
listener($0[0].object!)
|
|
||||||
return .undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return DOMNode(host.view, lastChild)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(target: DOMNode, with host: MountedHost) {
|
public func update(target: DOMNode, with host: MountedHost) {
|
||||||
|
@ -83,7 +101,7 @@ public final class DOMRenderer: Renderer {
|
||||||
transform: { (html: AnyHTML) in html }
|
transform: { (html: AnyHTML) in html }
|
||||||
) else { return }
|
) else { return }
|
||||||
|
|
||||||
html.update(dom: target.ref)
|
html.update(dom: target)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func unmount(
|
public func unmount(
|
||||||
|
|
|
@ -42,10 +42,21 @@ extension AnyHTML {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(dom: JSObjectRef) {
|
func update(dom: DOMNode) {
|
||||||
// FIXME: handle attributes and listeners here
|
// 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 }
|
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] = [:],
|
_ attributes: [String: String] = [:],
|
||||||
listeners: [String: Listener] = [:]
|
listeners: [String: Listener] = [:]
|
||||||
) {
|
) {
|
||||||
self.tag = tag
|
self = HTML(tag, attributes, listeners: listeners) { EmptyView() }
|
||||||
self.attributes = attributes
|
|
||||||
self.listeners = listeners
|
|
||||||
content = EmptyView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue