* Adds a child to the node. * * @param node - A FormKitNode | FormKitNode * @param context - A parent FormKitContext | FormKitContext * @param child - A FormKitNode | FormKitNode * @param listIndex - A index number to be added at * * @internal
( parent: FormKitNode, parentContext: FormKitContext, child: FormKitNode, listIndex?: number )
| 2408 | * @internal |
| 2409 | */ |
| 2410 | function addChild( |
| 2411 | parent: FormKitNode, |
| 2412 | parentContext: FormKitContext, |
| 2413 | child: FormKitNode, |
| 2414 | listIndex?: number |
| 2415 | ) { |
| 2416 | if (parent.type === 'input') error(100, parent) |
| 2417 | if (child.parent && child.parent !== parent) { |
| 2418 | child.parent.remove(child) |
| 2419 | } |
| 2420 | // Synchronously set the initial value on the parent |
| 2421 | if (!parentContext.children.includes(child)) { |
| 2422 | if (listIndex !== undefined && parent.type === 'list') { |
| 2423 | // Inject the child: |
| 2424 | const existingNode = parentContext.children[listIndex] |
| 2425 | if (existingNode && '__FKP' in existingNode) { |
| 2426 | // The node index is populated by a placeholderNode so we need to |
| 2427 | // remove that replace it with the real node (the current child). |
| 2428 | child._c.uid = existingNode.uid |
| 2429 | parentContext.children.splice(listIndex, 1, child) |
| 2430 | } else if (existingNode && parent.sync) { |
| 2431 | // Synced lists always have a placeholder waiting for a mounting child, |
| 2432 | // so a real node sitting here means the framework re-mounted and put the |
| 2433 | // new child in before pulling the old one out. Take over its slot and |
| 2434 | // uid instead of inserting a duplicate, which would grow the value and |
| 2435 | // spin up an endless re-mount loop (#1758). |
| 2436 | child._c.uid = existingNode.uid |
| 2437 | parentContext.children.splice(listIndex, 1, child) |
| 2438 | } else { |
| 2439 | // Same re-mount, opposite order: the old child left first, so the slot's |
| 2440 | // empty. Reuse its uid if we stashed one this tick so the key holds. |
| 2441 | if (!existingNode && parent.sync) { |
| 2442 | const removedUids = recentlyRemovedSyncUids.get(parent) |
| 2443 | const reusableUid = removedUids?.get(listIndex) |
| 2444 | if (reusableUid) { |
| 2445 | child._c.uid = reusableUid |
| 2446 | removedUids?.delete(listIndex) |
| 2447 | } |
| 2448 | } |
| 2449 | parentContext.children.splice(listIndex, 0, child) |
| 2450 | } |
| 2451 | |
| 2452 | if ( |
| 2453 | Array.isArray(parent.value) && |
| 2454 | parent.value.length < parentContext.children.length |
| 2455 | ) { |
| 2456 | // When adding an node or value to a list it is absolutely critical to |
| 2457 | // know if, at the moment of injection, the parent’s value or the node |
| 2458 | // children are the source of truth. For example, if a user pushes or |
| 2459 | // splices a new value onto the lists’s array then we want to use that |
| 2460 | // value as the value of the new node, but if a user adds a node to the |
| 2461 | // list then we want the node’s value. In this specific case, we |
| 2462 | // assume (due to length) that a new node was injected into the list, so |
| 2463 | // we want that new node’s value injected into the parent list value. |
| 2464 | parent.disturb().calm({ |
| 2465 | name: listIndex, |
| 2466 | value: child.value, |
| 2467 | from: valueInserted, |