-
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New: Children rendering improvements. Fixes #120. Implements Renderab…
…le from #96. * Resilience against externally removed child elements * Support for moving child elements from one inserter to another * onMountInsert gracefully handling the above * Text nodes are now updated, not re-created * New Renderable typeclass to expand text-to-node and child.text capabilities * Laminar no longer clears the DOM of `{child,children,child.*} <-- stream` when the element is unmounted and re-mounted, it now retains the previously added elements in the DOM
- Loading branch information
Showing
29 changed files
with
1,521 additions
and
295 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 53 additions & 17 deletions
70
src/main/scala/com/raquo/laminar/modifiers/ChildInserter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,65 @@ | ||
package com.raquo.laminar.modifiers | ||
|
||
import com.raquo.airstream.core.Observable | ||
import com.raquo.laminar.lifecycle.{InsertContext, MountContext} | ||
import com.raquo.laminar.nodes.{ChildNode, ParentNode, ReactiveElement} | ||
|
||
import scala.scalajs.js | ||
|
||
object ChildInserter { | ||
|
||
def apply[El <: ReactiveElement.Base] ( | ||
$child: MountContext[El] => Observable[ChildNode.Base] | ||
): Inserter[El] = new Inserter[El]( | ||
insertFn = (ctx, owner) => { | ||
val mountContext = new MountContext[El]( | ||
thisNode = ctx.parentNode, | ||
owner = owner | ||
) | ||
var lastSeenChild: js.UndefOr[ChildNode.Base] = js.undefined | ||
$child(mountContext).foreach { newChildNode => | ||
if (!lastSeenChild.exists(_ eq newChildNode)) { // #Note: auto-distinction | ||
lastSeenChild = newChildNode | ||
ParentNode.replaceChild(parent = ctx.parentNode, oldChild = ctx.sentinelNode, newChild = newChildNode) | ||
ctx.sentinelNode = newChildNode | ||
$child: Observable[ChildNode.Base] | ||
): Inserter[El] = { | ||
new Inserter[El]( | ||
preferStrictMode = true, | ||
insertFn = (ctx, owner) => { | ||
if (!ctx.strictMode) { | ||
ctx.forceSetStrictMode() | ||
} | ||
}(owner) | ||
} | ||
) | ||
|
||
var maybeLastSeenChild: js.UndefOr[ChildNode.Base] = js.undefined | ||
|
||
$child.foreach { newChildNode => | ||
var remainingOldExtraNodeCount = ctx.extraNodeCount | ||
|
||
maybeLastSeenChild | ||
.filter(_.ref == ctx.sentinelNode.ref.nextSibling) // Assert that the prev child node was not moved. Note: nextSibling could be null | ||
.fold { | ||
// Inserting the child for the first time, OR after the previous child was externally moved / removed. | ||
val sentinelNodeIndex = ParentNode.indexOfChild(ctx.parentNode, ctx.sentinelNode) | ||
ParentNode.insertChild(parent = ctx.parentNode, newChildNode, atIndex = sentinelNodeIndex + 1) | ||
() | ||
} { lastSeenChild => | ||
// We found the existing child in the right place in the DOM | ||
// Just need to check that the new child is actually different from the old one | ||
// Replace the child with new one. | ||
// #Note: auto-distinction inside (lastSeenChild != newChildNode filter) | ||
val replaced = ParentNode.replaceChild( | ||
parent = ctx.parentNode, | ||
oldChild = lastSeenChild, | ||
newChild = newChildNode | ||
) | ||
if (replaced || lastSeenChild == newChildNode) { // #TODO[Performance,Integrity] Not liking this redundant auto-distinction | ||
// The only time we DON'T decrement this is when replacing fails for unexpected reasons. | ||
// - If lastSeenChild == newChildNode, then it's not an "old" node anymore, so we decrement | ||
// - If replaced == true, then lastSeenChild was removed from the DOM, so we decrement | ||
remainingOldExtraNodeCount -= 1 | ||
} | ||
() | ||
} | ||
|
||
// We've just inserted newChildNode after the sentinel, or replaced the first old node with newChildNode, | ||
// so any remaining old child nodes must be directly under it. | ||
ctx.removeOldChildNodesFromDOM(after = newChildNode) | ||
|
||
ctx.extraNodesMap.clear() | ||
ctx.extraNodesMap.set(newChildNode.ref, newChildNode) | ||
ctx.extraNodes = newChildNode :: Nil | ||
ctx.extraNodeCount = 1 | ||
|
||
maybeLastSeenChild = newChildNode | ||
}(owner) | ||
} | ||
) | ||
} | ||
} |
Oops, something went wrong.