@@ -1198,8 +1198,9 @@ var Idiomorph = (function () {
11981198 if ( newContent . parentNode ) {
11991199 // we can't use the parent directly because newContent may have siblings
12001200 // that we don't want in the morph, and reparenting might be expensive (TODO is it?),
1201- // so we create a duck-typed parent node instead.
1202- return createDuckTypedParent ( newContent ) ;
1201+ // so instead we create a fake parent node that only sees a slice of its children.
1202+ /** @type {Element } */
1203+ return /** @type {any } */ ( new SlicedParentNode ( newContent ) ) ;
12031204 } else {
12041205 // a single node is added as a child to a dummy parent
12051206 const dummyParent = document . createElement ( "div" ) ;
@@ -1218,33 +1219,78 @@ var Idiomorph = (function () {
12181219 }
12191220
12201221 /**
1221- * Creates a fake duck-typed parent element to wrap a single node, without actually reparenting it.
1222+ * A fake duck-typed parent element to wrap a single node, without actually reparenting it.
1223+ * This is useful because the node may have siblings that we don't want in the morph, and it may also be moved
1224+ * or replaced with one or more elements during the morph. This class effectively allows us a window into
1225+ * a slice of a node's children.
12221226 * "If it walks like a duck, and quacks like a duck, then it must be a duck!" -- James Whitcomb Riley (1849–1916)
1223- *
1224- * @param {Node } newContent
1225- * @returns {Element }
12261227 */
1227- function createDuckTypedParent ( newContent ) {
1228- return /** @type {Element } */ (
1229- /** @type {unknown } */ ( {
1230- childNodes : [ newContent ] ,
1231- /** @ts -ignore - cover your eyes for a minute, tsc */
1232- querySelectorAll : ( s ) => {
1233- /** @ts -ignore */
1234- const elements = newContent . querySelectorAll ( s ) ;
1235- /** @ts -ignore */
1236- return newContent . matches ( s ) ? [ newContent , ...elements ] : elements ;
1237- } ,
1238- /** @ts -ignore */
1239- insertBefore : ( n , r ) => newContent . parentNode . insertBefore ( n , r ) ,
1240- /** @ts -ignore */
1241- moveBefore : ( n , r ) => newContent . parentNode . moveBefore ( n , r ) ,
1242- // for later use with populateIdMapWithTree to halt upwards iteration
1243- get __idiomorphRoot ( ) {
1244- return newContent ;
1245- } ,
1246- } )
1247- ) ;
1228+ class SlicedParentNode {
1229+ /** @param {Node } node */
1230+ constructor ( node ) {
1231+ this . originalNode = node ;
1232+ this . realParentNode = /** @type {Element } */ ( node . parentNode ) ;
1233+ this . previousSibling = node . previousSibling ;
1234+ this . nextSibling = node . nextSibling ;
1235+ }
1236+
1237+ /** @returns {Node[] } */
1238+ get childNodes ( ) {
1239+ // return slice of realParent's current childNodes, based on previousSibling and nextSibling
1240+ const nodes = [ ] ;
1241+ let cursor = this . previousSibling
1242+ ? this . previousSibling . nextSibling
1243+ : this . realParentNode . firstChild ;
1244+ while ( cursor && cursor != this . nextSibling ) {
1245+ nodes . push ( cursor ) ;
1246+ cursor = cursor . nextSibling ;
1247+ }
1248+ return nodes ;
1249+ }
1250+
1251+ /**
1252+ * @param {string } selector
1253+ * @returns {Element[] }
1254+ */
1255+ querySelectorAll ( selector ) {
1256+ return this . childNodes . reduce ( ( results , node ) => {
1257+ if ( node instanceof Element ) {
1258+ if ( node . matches ( selector ) ) results . push ( node ) ;
1259+ const nodeList = node . querySelectorAll ( selector ) ;
1260+ for ( let i = 0 ; i < nodeList . length ; i ++ ) {
1261+ results . push ( nodeList [ i ] ) ;
1262+ }
1263+ }
1264+ return results ;
1265+ } , /** @type {Element[] } */ ( [ ] ) ) ;
1266+ }
1267+
1268+ /**
1269+ * @param {Node } node
1270+ * @param {Node } referenceNode
1271+ * @returns {Node }
1272+ */
1273+ insertBefore ( node , referenceNode ) {
1274+ return this . realParentNode . insertBefore ( node , referenceNode ) ;
1275+ }
1276+
1277+ /**
1278+ * @param {Node } node
1279+ * @param {Node } referenceNode
1280+ * @returns {Node }
1281+ */
1282+ moveBefore ( node , referenceNode ) {
1283+ // @ts -ignore - use new moveBefore feature
1284+ return this . realParentNode . moveBefore ( node , referenceNode ) ;
1285+ }
1286+
1287+ /**
1288+ * for later use with populateIdMapWithTree to halt upwards iteration
1289+ * @returns {Node }
1290+ */
1291+ get __idiomorphRoot ( ) {
1292+ return this . originalNode ;
1293+ }
12481294 }
12491295
12501296 /**
0 commit comments