MCPcopy Create free account
hub / github.com/formkit/formkit / syncListNodes

Function syncListNodes

packages/core/src/node.ts:2078–2161  ·  view source on GitHub ↗

* Hydrate a list node and its children. There are some assumptions about the * child nodes that are made here: * 1. The child nodes are either: * - Are scalars and their values can be exchanged. * - Are groups and should maintain node identity. * 2. The value of the list will be a 1-1 rep

(node: FormKitNode, context: FormKitContext)

Source from the content-addressed store, hash-verified

2076 * @param node - A {@link FormKitNode | FormKitNode}
2077 */
2078function syncListNodes(node: FormKitNode, context: FormKitContext) {
2079 const _value = node._value
2080 if (!Array.isArray(_value)) return
2081
2082 const newChildren: Array<FormKitNode | FormKitPlaceholderNode | null> = []
2083 const unused = new Set(context.children)
2084 const placeholderValues = new Map<unknown, number[]>()
2085
2086 // 1. Iterate over the values and if the values at the same index are equal
2087 // then we can reuse the node. Otherwise we add a `null` placeholder.
2088 _value.forEach((value, i) => {
2089 if (context.children[i] && context.children[i]._value === value) {
2090 newChildren.push(context.children[i])
2091 unused.delete(context.children[i])
2092 } else {
2093 newChildren.push(null)
2094
2095 const indexes = placeholderValues.get(value) || []
2096 indexes.push(i)
2097 placeholderValues.set(value, indexes)
2098 }
2099 })
2100
2101 // 2. If there are unused nodes, and there are null nodes in the new children
2102 // then we attempt to match those irregardless of their index.
2103 if (unused.size && placeholderValues.size) {
2104 unused.forEach((child) => {
2105 if (placeholderValues.has(child._value)) {
2106 /* eslint-disable @typescript-eslint/no-non-null-assertion */
2107 const indexes = placeholderValues.get(child._value)!
2108 const index = indexes.shift()!
2109 /* eslint-enable @typescript-eslint/no-non-null-assertion */
2110 newChildren[index] = child
2111 unused.delete(child)
2112 if (!indexes.length) placeholderValues.delete(child._value)
2113 }
2114 })
2115 }
2116
2117 // 3. If there are still unused nodes, and unused placeholders, we assign the
2118 // unused nodes to the unused placeholders in order.
2119 // IMPORTANT: Collect empty indexes in sequential order (0, 1, 2, ...) rather
2120 // than iterating placeholderValues Map which groups indexes by value.
2121 const emptyIndexes: number[] = []
2122 for (let i = 0; i < newChildren.length; i++) {
2123 if (newChildren[i] === null) {
2124 emptyIndexes.push(i)
2125 }
2126 }
2127
2128 while (unused.size && emptyIndexes.length) {
2129 const child = unused.values().next().value
2130 const index = emptyIndexes.shift()
2131 if (index === undefined || child === undefined) break
2132 newChildren[index] = child
2133 unused.delete(child)
2134 }
2135

Callers 1

hydrateFunction · 0.85

Calls 4

createPlaceholderFunction · 0.85
isPlaceholderFunction · 0.85
destroyFunction · 0.85
nextMethod · 0.80

Tested by

no test coverage detected