@kudzujs/core 0.7.6 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,6 +47,7 @@ function mountLists(root) {
47
47
  const templateRoot = __KUDZU_NESTED_LISTS__ ? nested.templateRoot : start.content.firstElementChild
48
48
  if (__KUDZU_LIST_ROW_HOOKS__) for (let index = 0; index < roots.length; index++) initializeGeneralRowHooks(descriptor, descriptor.keys[index], roots[index], nested?.owner)
49
49
  const parts = listItemPartPlan(templateRoot, descriptor.nested)
50
+ const staticRows = __KUDZU_STATIC_COLLECTIONS__ && descriptor.static && parts.directFill ? new Map() : undefined
50
51
  for (const root of roots) {
51
52
  if (__KUDZU_LIST_CONDITIONS__ && descriptor.conditions) {
52
53
  if (__KUDZU_NESTED_LISTS__ && descriptor.ownerField) itemPartPlans.set(root, parts)
@@ -56,11 +57,16 @@ function mountLists(root) {
56
57
  }
57
58
  if (__KUDZU_LIST_SEEDS__ && descriptor.seed && !browserState.has(descriptor.state)) browserState.set(descriptor.state, roots.map((root, index) => seedListItem(root, descriptor, index)))
58
59
  const items = browserState.get(descriptor.state)
60
+ const staticEntries = __KUDZU_STATIC_COLLECTIONS__ && descriptor.static && descriptor.key !== null && !descriptor.indexed && descriptor.selector?.every(operation => operation[0] === "filter") && parts.directFill
61
+ ? cacheStaticListEntries(descriptor, items)
62
+ : undefined
59
63
  const list = {
60
64
  start,
61
65
  descriptor,
62
66
  ...(__KUDZU_NESTED_LISTS__ ? { templateRoot, ...(nested.childPrototypes?.size ? { childPrototypes: nested.childPrototypes } : {}) } : {}),
63
67
  parts,
68
+ ...(__KUDZU_STATIC_COLLECTIONS__ && staticRows ? { staticRows } : {}),
69
+ ...(__KUDZU_STATIC_COLLECTIONS__ && staticEntries ? { staticEntries, staticPositions: staticEntries.positions } : {}),
64
70
  seedFields: __KUDZU_LIST_SEEDS__ && descriptor.seed && Object.keys(descriptor.seed),
65
71
  roots: new Map(roots.map((node, index) => [keyToken(descriptor.keys[index]), node])),
66
72
  ...(__KUDZU_LIST_STABLE_FAST_PATHS__ ? { orderedRoots: roots } : {}),
@@ -78,8 +84,14 @@ function mountLists(root) {
78
84
  ownedLists.set(list.owner, lists)
79
85
  listRegistrations.set(start, { list, owner: list.owner })
80
86
  } else {
81
- register(listTargets, descriptor.state, list)
82
- listRegistrations.set(start, { state: descriptor.state, list })
87
+ if (__KUDZU_COLLECTION_SELECTORS__ && descriptor.selectorStates) {
88
+ const states = [...new Set([descriptor.state, ...Object.values(descriptor.selectorStates)])]
89
+ for (const state of states) register(listTargets, state, list)
90
+ listRegistrations.set(start, { states, list })
91
+ } else {
92
+ register(listTargets, descriptor.state, list)
93
+ listRegistrations.set(start, { state: descriptor.state, list })
94
+ }
83
95
  }
84
96
  updateList(list)
85
97
  }
@@ -97,9 +109,17 @@ function unregisterList(start) {
97
109
  if (lists?.get(registration.list.descriptor.id) === registration.list) lists.delete(registration.list.descriptor.id)
98
110
  if (!lists?.size) ownedLists.delete(registration.owner)
99
111
  } else {
100
- const lists = listTargets.get(registration.state)
101
- lists?.delete(registration.list)
102
- if (!lists?.size) listTargets.delete(registration.state)
112
+ if (__KUDZU_COLLECTION_SELECTORS__ && registration.states) {
113
+ for (const state of registration.states) {
114
+ const lists = listTargets.get(state)
115
+ lists?.delete(registration.list)
116
+ if (!lists?.size) listTargets.delete(state)
117
+ }
118
+ } else {
119
+ const lists = listTargets.get(registration.state)
120
+ lists?.delete(registration.list)
121
+ if (!lists?.size) listTargets.delete(registration.state)
122
+ }
103
123
  }
104
124
  if (__KUDZU_LIST_ROW_HOOKS__ && registration.list.descriptor.rowStates) for (const node of registration.list.roots.values()) deleteRowStates(registration.list.descriptor, ownershipPaths.get(node))
105
125
  }
@@ -112,12 +132,13 @@ function updateList(list) {
112
132
  ? listItems.get(list.owner)?.[list.descriptor.ownerField]
113
133
  : browserState.get(list.descriptor.state)
114
134
  if (__KUDZU_NESTED_LISTS__ && list.descriptor.ownerField && items == null) items = []
115
- if (__KUDZU_COLLECTION_SELECTORS__) items = selectCollection(items, list.descriptor.selector)
135
+ if (__KUDZU_COLLECTION_SELECTORS__) items = selectCollection(items, list.descriptor.selector, name => browserState.get(list.descriptor.selectorStates?.[name]))
116
136
  if (!Array.isArray(items)) throw new Error(list.descriptor.ownerField ? `Nested keyed list property "${list.descriptor.ownerField}" must remain an array` : "Keyed list state must remain an array")
117
137
  if (__KUDZU_NESTED_LISTS__ && (list.descriptor.children || list.descriptor.ownerField) && !list.descriptor.indexed && !list.descriptor.selector && list.descriptor.key !== null && list.items && updateNestedList(list, items)) return
118
138
  if (__KUDZU_NESTED_LISTS__ && list.descriptor.children) validateChildLists(items, list.descriptor.children)
119
139
  if (list.descriptor.reducer && !list.descriptor.selector && list.descriptor.key !== null && list.items && updateReducerList(list, items)) return
120
140
  if (__KUDZU_LIST_STABLE_FAST_PATHS__ && list.descriptor.key !== null && !list.descriptor.indexed && !list.descriptor.selector && !list.descriptor.reducer && !list.descriptor.children && !list.descriptor.ownerField && list.items && updateStableList(list, items)) return
141
+ if (__KUDZU_STATIC_COLLECTIONS__ && list.staticEntries && updateStaticFilterList(list, items)) return
121
142
  const entries = []
122
143
  const keys = new Set()
123
144
  const seen = new Set()
@@ -174,14 +195,18 @@ function updateList(list) {
174
195
  for (const { item, index, key, token, value } of entries) {
175
196
  let node = list.roots.get(token)
176
197
  if (!node) {
177
- node = (__KUDZU_NESTED_LISTS__ ? list.templateRoot : list.start.content.firstElementChild)?.cloneNode(true)
178
- if (node?.dataset.kListRoot !== list.descriptor.id) node = undefined
198
+ const staticRoot = __KUDZU_STATIC_COLLECTIONS__ && list.staticRows?.get(token)?.cloneNode(true)
199
+ node = staticRoot ?? (__KUDZU_NESTED_LISTS__ ? list.templateRoot : list.start.content.firstElementChild)?.cloneNode(true)
200
+ if (!staticRoot && node?.dataset.kListRoot !== list.descriptor.id) node = undefined
179
201
  if (!node) throw new Error("Keyed list template has no root element")
180
202
  node.removeAttribute("data-k-list-root")
181
203
  if (__KUDZU_NESTED_LISTS__ && list.childPrototypes) childPrototypes.set(node, list.childPrototypes)
182
204
  if (__KUDZU_LIST_ROW_HOOKS__) initializeGeneralRowHooks(list.descriptor, key, node, list.owner)
183
- mapListItemParts(list.parts, node, list.descriptor.nested)
184
- fillListItem(node, item, list.descriptor.nested, index)
205
+ if (staticRoot) listItems.set(node, item)
206
+ else if (list.parts.directFill) {
207
+ listItems.set(node, item)
208
+ fillStructuralListParts(list.parts, node, item)
209
+ } else fillListItem(node, item, list.descriptor.nested, index, mapListItemParts(list.parts, node, list.descriptor.nested))
185
210
  additions.append(node)
186
211
  added = true
187
212
  } else if ((referenceOnly ? listItems.get(node) !== item : list.values.get(token) !== value) || list.descriptor.indexed || list.descriptor.key === null) {
@@ -199,20 +224,38 @@ function updateList(list) {
199
224
  } else node.remove()
200
225
  if (__KUDZU_LIST_ROW_HOOKS__ && list.descriptor.rowStates) deleteRowStates(list.descriptor, ownershipPaths.get(node))
201
226
  }
227
+ let ordered = false
202
228
  if (added) {
203
- if (__KUDZU_LIST_MOUNTS__ && list.descriptor.mount) {
204
- const addedNodes = [...additions.childNodes]
205
- parent.insertBefore(additions, list.boundary)
206
- for (const node of addedNodes) mountDom(node)
229
+ const addedNodes = __KUDZU_LIST_MOUNTS__ && list.descriptor.mount ? [...additions.childNodes] : undefined
230
+ let seenAddition = false
231
+ let interleaved = false
232
+ for (const [, node] of next) {
233
+ if (node.parentNode === additions) seenAddition = true
234
+ else if (seenAddition) {
235
+ interleaved = true
236
+ break
237
+ }
238
+ }
239
+ if (interleaved) {
240
+ const run = parent.ownerDocument.createDocumentFragment()
241
+ for (const [, node] of next) {
242
+ if (node.parentNode === additions) run.append(node)
243
+ else if (run.firstChild) parent.insertBefore(run, node)
244
+ }
245
+ if (run.firstChild) parent.insertBefore(run, list.boundary)
246
+ ordered = true
207
247
  } else parent.insertBefore(additions, list.boundary)
248
+ if (addedNodes) for (const node of addedNodes) mountDom(node)
208
249
  list.container ??= parent
209
250
  }
210
251
  let anchor = list.boundary
211
252
  let misplaced = 0
212
- for (let index = next.length - 1; index >= 0; index--) {
213
- const node = next[index][1]
214
- if (node.nextSibling !== anchor) misplaced++
215
- anchor = node
253
+ if (!ordered) {
254
+ for (let index = next.length - 1; index >= 0; index--) {
255
+ const node = next[index][1]
256
+ if (node.nextSibling !== anchor) misplaced++
257
+ anchor = node
258
+ }
216
259
  }
217
260
  if (misplaced > next.length / 2) {
218
261
  const reordered = parent.ownerDocument.createDocumentFragment()
@@ -232,6 +275,68 @@ function updateList(list) {
232
275
  list.items = items
233
276
  }
234
277
 
278
+ function updateStaticFilterList(list, items) {
279
+ const entries = new Array(items.length)
280
+ for (let index = 0; index < items.length; index++) {
281
+ const entry = list.staticEntries.get(items[index])
282
+ if (!entry || !list.roots.has(entry.token) && !list.staticRows.has(entry.token)) return false
283
+ entries[index] = entry
284
+ }
285
+ let selectedIndex = 0
286
+ for (const [token, node] of list.roots) {
287
+ const position = list.staticPositions.get(token)
288
+ while (entries[selectedIndex]?.position < position) selectedIndex++
289
+ if (entries[selectedIndex]?.token === token) {
290
+ selectedIndex++
291
+ continue
292
+ }
293
+ list.staticRows.set(token, node)
294
+ node.remove()
295
+ }
296
+ const parent = list.container ?? list.start.parentNode
297
+ const run = parent.ownerDocument.createDocumentFragment()
298
+ const next = new Array(entries.length)
299
+ for (let index = 0; index < entries.length; index++) {
300
+ const entry = entries[index]
301
+ let node = list.roots.get(entry.token)
302
+ if (!node) {
303
+ node = list.staticRows.get(entry.token).cloneNode(true)
304
+ listItems.set(node, entry.item)
305
+ run.append(node)
306
+ } else if (run.firstChild) parent.insertBefore(run, node)
307
+ next[index] = [entry.token, node]
308
+ }
309
+ if (run.firstChild) parent.insertBefore(run, list.boundary)
310
+ list.roots = new Map(next)
311
+ if (__KUDZU_LIST_STABLE_FAST_PATHS__) list.orderedRoots = next.map(([, node]) => node)
312
+ list.values.clear()
313
+ list.items = items
314
+ list.container ??= parent
315
+ return true
316
+ }
317
+
318
+ function cacheStaticListEntries(descriptor, items) {
319
+ if (!Array.isArray(items)) return undefined
320
+ const entries = new WeakMap()
321
+ const positions = new Map()
322
+ const keys = new Set()
323
+ const seen = new Set()
324
+ for (let position = 0; position < items.length; position++) {
325
+ const item = items[position]
326
+ assertListItem(item)
327
+ assertListValue(item, seen, true)
328
+ const key = item[descriptor.key]
329
+ if (!validListKey(key)) throw new Error(`Keyed list key "${descriptor.key}" must be a string or finite number`)
330
+ const token = keyToken(key)
331
+ if (keys.has(token)) throw new Error(`Duplicate keyed list key: ${String(key)}`)
332
+ keys.add(token)
333
+ entries.set(item, { item, key, token, value: item, position })
334
+ positions.set(token, position)
335
+ }
336
+ entries.positions = positions
337
+ return entries
338
+ }
339
+
235
340
  /* stable-list-fast-path */
236
341
  function updateStableList(list, items) {
237
342
  const previous = list.items
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kudzujs/core",
3
- "version": "0.7.6",
3
+ "version": "0.7.9",
4
4
  "description": "HTML-first TSX framework with synchronous state semantics and no virtual DOM",
5
5
  "type": "module",
6
6
  "license": "MIT",