@kudzujs/core 0.6.27 → 0.7.0
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.
- package/GOAL_A.md +1 -1
- package/README.md +72 -38
- package/RELEASES.md +44 -0
- package/framework/README.md +9 -3
- package/framework/binding-runtime.js +4 -2
- package/framework/build.mjs +417 -156
- package/framework/collection-selector.js +63 -0
- package/framework/core.d.ts +6 -1
- package/framework/core.mjs +73 -38
- package/framework/list-runtime.js +321 -95
- package/framework/native-runtime.js +13 -2
- package/framework/shared-runtime.js +18 -5
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { browserState, mountDom, notifyListItem, registerCommitter, registerMountHook, registerUnmountHook, unmountDom } from "./shared-runtime.js"
|
|
2
|
+
import { selectCollection } from "./collection-selector.js"
|
|
2
3
|
|
|
3
4
|
const listTargets = new Map()
|
|
4
5
|
const listRegistrations = new WeakMap()
|
|
@@ -7,6 +8,9 @@ const imports = __KUDZU_LIST_ASYNC_PARTS__ ? new Map() : undefined
|
|
|
7
8
|
const revisions = __KUDZU_LIST_ASYNC_PARTS__ ? new WeakMap() : undefined
|
|
8
9
|
const itemParts = new WeakMap()
|
|
9
10
|
const listItems = new WeakMap()
|
|
11
|
+
const listIndexes = __KUDZU_LIST_INDEXES__ ? new WeakMap() : undefined
|
|
12
|
+
const ownershipPaths = __KUDZU_LIST_ROW_HOOKS__ ? new WeakMap() : undefined
|
|
13
|
+
const rowReplacements = __KUDZU_LIST_ROW_HOOKS__ ? new WeakMap() : undefined
|
|
10
14
|
const ownedLists = __KUDZU_NESTED_LISTS__ ? new WeakMap() : undefined
|
|
11
15
|
const conditionOwners = __KUDZU_LIST_CONDITIONS__ ? new WeakMap() : undefined
|
|
12
16
|
const conditionTemplates = __KUDZU_LIST_CONDITIONS__ ? new WeakMap() : undefined
|
|
@@ -32,7 +36,7 @@ if (typeof document !== "undefined") mountDom(document)
|
|
|
32
36
|
function mountLists(root) {
|
|
33
37
|
let owner = root.nodeType === Node.ELEMENT_NODE ? root : root.parentElement
|
|
34
38
|
while (owner && !listItems.has(owner)) owner = owner.parentElement
|
|
35
|
-
if (owner) fillListParts(root, listItemParts(root), listItems.get(owner), 0)
|
|
39
|
+
if (owner) fillListParts(root, listItemParts(root), listItems.get(owner), 0, __KUDZU_LIST_INDEXES__ ? listIndexes.get(owner) ?? 0 : 0)
|
|
36
40
|
for (const start of matching(root, "template[data-k-list]")) {
|
|
37
41
|
if (mountedLists.has(start)) continue
|
|
38
42
|
mountedLists.add(start)
|
|
@@ -41,7 +45,7 @@ function mountLists(root) {
|
|
|
41
45
|
const roots = listRoots(start, end)
|
|
42
46
|
const nested = __KUDZU_NESTED_LISTS__ ? mountNestedPrototype(start, descriptor, roots) : undefined
|
|
43
47
|
const templateRoot = __KUDZU_NESTED_LISTS__ ? nested.templateRoot : start.content.firstElementChild
|
|
44
|
-
if (
|
|
48
|
+
if (__KUDZU_LIST_ROW_HOOKS__) for (let index = 0; index < roots.length; index++) initializeGeneralRowHooks(descriptor, descriptor.keys[index], roots[index], nested?.owner)
|
|
45
49
|
const parts = listItemPartPlan(templateRoot, descriptor.nested)
|
|
46
50
|
for (const root of roots) {
|
|
47
51
|
if (__KUDZU_LIST_CONDITIONS__ && descriptor.conditions) {
|
|
@@ -55,10 +59,11 @@ function mountLists(root) {
|
|
|
55
59
|
const list = {
|
|
56
60
|
start,
|
|
57
61
|
descriptor,
|
|
58
|
-
...(__KUDZU_NESTED_LISTS__ ? { templateRoot, ...(nested.
|
|
62
|
+
...(__KUDZU_NESTED_LISTS__ ? { templateRoot, ...(nested.childPrototypes?.size ? { childPrototypes: nested.childPrototypes } : {}) } : {}),
|
|
59
63
|
parts,
|
|
60
64
|
seedFields: __KUDZU_LIST_SEEDS__ && descriptor.seed && Object.keys(descriptor.seed),
|
|
61
65
|
roots: new Map(roots.map((node, index) => [keyToken(descriptor.keys[index]), node])),
|
|
66
|
+
...(__KUDZU_LIST_STABLE_FAST_PATHS__ ? { orderedRoots: roots } : {}),
|
|
62
67
|
values: new Map(),
|
|
63
68
|
items: undefined,
|
|
64
69
|
container: roots[0]?.parentNode,
|
|
@@ -67,8 +72,10 @@ function mountLists(root) {
|
|
|
67
72
|
}
|
|
68
73
|
if (__KUDZU_NESTED_LISTS__ && descriptor.ownerField) {
|
|
69
74
|
if (!list.owner) throw new Error("Nested keyed list has no parent row")
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
const lists = ownedLists.get(list.owner) ?? new Map()
|
|
76
|
+
if (lists.has(descriptor.id)) throw new Error(`Duplicate nested keyed list ID: ${descriptor.id}`)
|
|
77
|
+
lists.set(descriptor.id, list)
|
|
78
|
+
ownedLists.set(list.owner, lists)
|
|
72
79
|
listRegistrations.set(start, { list, owner: list.owner })
|
|
73
80
|
} else {
|
|
74
81
|
register(listTargets, descriptor.state, list)
|
|
@@ -86,41 +93,50 @@ function unregisterList(start) {
|
|
|
86
93
|
const registration = listRegistrations.get(start)
|
|
87
94
|
if (registration) {
|
|
88
95
|
if (__KUDZU_NESTED_LISTS__ && registration.owner) {
|
|
89
|
-
|
|
96
|
+
const lists = ownedLists.get(registration.owner)
|
|
97
|
+
if (lists?.get(registration.list.descriptor.id) === registration.list) lists.delete(registration.list.descriptor.id)
|
|
98
|
+
if (!lists?.size) ownedLists.delete(registration.owner)
|
|
90
99
|
} else {
|
|
91
100
|
const lists = listTargets.get(registration.state)
|
|
92
101
|
lists?.delete(registration.list)
|
|
93
102
|
if (!lists?.size) listTargets.delete(registration.state)
|
|
94
103
|
}
|
|
95
|
-
if (
|
|
104
|
+
if (__KUDZU_LIST_ROW_HOOKS__ && registration.list.descriptor.rowStates) for (const node of registration.list.roots.values()) deleteRowStates(registration.list.descriptor, ownershipPaths.get(node))
|
|
96
105
|
}
|
|
97
106
|
listRegistrations.delete(start)
|
|
98
107
|
mountedLists.delete(start)
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
function updateList(list) {
|
|
102
|
-
|
|
111
|
+
let items = __KUDZU_NESTED_LISTS__ && list.descriptor.ownerField
|
|
103
112
|
? listItems.get(list.owner)?.[list.descriptor.ownerField]
|
|
104
113
|
: browserState.get(list.descriptor.state)
|
|
114
|
+
if (__KUDZU_NESTED_LISTS__ && list.descriptor.ownerField && items == null) items = []
|
|
115
|
+
if (__KUDZU_COLLECTION_SELECTORS__) items = selectCollection(items, list.descriptor.selector)
|
|
105
116
|
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")
|
|
106
|
-
if (__KUDZU_NESTED_LISTS__ && (list.descriptor.
|
|
107
|
-
if (__KUDZU_NESTED_LISTS__ && list.descriptor.
|
|
108
|
-
if (list.descriptor.reducer && list.items && updateReducerList(list, items)) return
|
|
117
|
+
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
|
+
if (__KUDZU_NESTED_LISTS__ && list.descriptor.children) validateChildLists(items, list.descriptor.children)
|
|
119
|
+
if (list.descriptor.reducer && !list.descriptor.selector && list.descriptor.key !== null && list.items && updateReducerList(list, items)) return
|
|
120
|
+
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
|
|
109
121
|
const entries = []
|
|
110
122
|
const keys = new Set()
|
|
111
123
|
const seen = new Set()
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
const referenceOnly = usesItemReferences(list)
|
|
125
|
+
for (const [index, item] of items.entries()) {
|
|
126
|
+
const key = list.descriptor.key === null ? index : item?.[list.descriptor.key]
|
|
114
127
|
if (!validListKey(key)) throw new Error(`Keyed list key "${list.descriptor.key}" must be a string or finite number`)
|
|
115
128
|
assertListItem(item)
|
|
116
|
-
const seededValue = __KUDZU_LIST_SEEDS__ ? list.seedFields && seededListValue(item, list.seedFields, list.descriptor.seed) : undefined
|
|
129
|
+
const seededValue = __KUDZU_LIST_SEEDS__ ? list.seedFields && seededListValue(item, list.seedFields, list.descriptor.seed, !referenceOnly) : undefined
|
|
117
130
|
if (seededValue === undefined) assertListValue(item, seen, true)
|
|
118
131
|
const token = keyToken(key)
|
|
119
132
|
if (keys.has(token)) throw new Error(`Duplicate keyed list key: ${String(key)}`)
|
|
120
133
|
keys.add(token)
|
|
121
|
-
entries.push({ item, key, token, value: seededValue === undefined ? JSON.stringify(item) : seededValue })
|
|
134
|
+
entries.push({ item, index, key, token, value: referenceOnly ? item : seededValue === undefined ? JSON.stringify(item) : seededValue })
|
|
122
135
|
}
|
|
123
|
-
if (list.
|
|
136
|
+
if (list.descriptor.key !== null && !list.descriptor.indexed && !list.descriptor.selector && (referenceOnly || list.values.size) && entries.every(entry => {
|
|
137
|
+
const root = list.roots.get(entry.token)
|
|
138
|
+
return !root || (referenceOnly ? listItems.get(root) === entry.item : list.values.get(entry.token) === entry.value)
|
|
139
|
+
})) {
|
|
124
140
|
const currentTokens = [...list.roots.keys()]
|
|
125
141
|
const nextTokens = entries.map(entry => entry.token)
|
|
126
142
|
if (nextTokens.length === currentTokens.length && nextTokens.every((token, index) => token === currentTokens[currentTokens.length - index - 1])) {
|
|
@@ -129,6 +145,7 @@ function updateList(list) {
|
|
|
129
145
|
reordered.append(...nextTokens.map(token => list.roots.get(token)))
|
|
130
146
|
parent.insertBefore(reordered, list.boundary)
|
|
131
147
|
list.roots = new Map(nextTokens.map(token => [token, list.roots.get(token)]))
|
|
148
|
+
if (__KUDZU_LIST_STABLE_FAST_PATHS__) list.orderedRoots = nextTokens.map(token => list.roots.get(token))
|
|
132
149
|
list.container ??= parent
|
|
133
150
|
return
|
|
134
151
|
}
|
|
@@ -138,12 +155,14 @@ function updateList(list) {
|
|
|
138
155
|
if (removed && nextTokens.every((token, index) => token === currentTokens[index >= removedIndex ? index + 1 : index])) {
|
|
139
156
|
removeListRoot(list, removed)
|
|
140
157
|
list.roots = new Map(nextTokens.map(token => [token, list.roots.get(token)]))
|
|
158
|
+
if (__KUDZU_LIST_STABLE_FAST_PATHS__) list.orderedRoots = nextTokens.map(token => list.roots.get(token))
|
|
141
159
|
return
|
|
142
160
|
}
|
|
143
161
|
}
|
|
144
162
|
if (nextTokens.length === currentTokens.length + 1 && currentTokens.every((token, index) => token === nextTokens[index])) {
|
|
145
163
|
const entry = entries.at(-1)
|
|
146
164
|
addListRoot(list, entry)
|
|
165
|
+
list.items = items
|
|
147
166
|
return
|
|
148
167
|
}
|
|
149
168
|
}
|
|
@@ -152,25 +171,25 @@ function updateList(list) {
|
|
|
152
171
|
const parent = list.container ?? list.start.parentNode
|
|
153
172
|
const additions = parent.ownerDocument.createDocumentFragment()
|
|
154
173
|
let added = false
|
|
155
|
-
for (const { item, key, token, value } of entries) {
|
|
174
|
+
for (const { item, index, key, token, value } of entries) {
|
|
156
175
|
let node = list.roots.get(token)
|
|
157
176
|
if (!node) {
|
|
158
177
|
node = (__KUDZU_NESTED_LISTS__ ? list.templateRoot : list.start.content.firstElementChild)?.cloneNode(true)
|
|
159
178
|
if (node?.dataset.kListRoot !== list.descriptor.id) node = undefined
|
|
160
179
|
if (!node) throw new Error("Keyed list template has no root element")
|
|
161
180
|
node.removeAttribute("data-k-list-root")
|
|
162
|
-
if (__KUDZU_NESTED_LISTS__ && list.
|
|
163
|
-
if (
|
|
181
|
+
if (__KUDZU_NESTED_LISTS__ && list.childPrototypes) childPrototypes.set(node, list.childPrototypes)
|
|
182
|
+
if (__KUDZU_LIST_ROW_HOOKS__) initializeGeneralRowHooks(list.descriptor, key, node, list.owner)
|
|
164
183
|
mapListItemParts(list.parts, node, list.descriptor.nested)
|
|
165
|
-
fillListItem(node, item, list.descriptor.nested)
|
|
184
|
+
fillListItem(node, item, list.descriptor.nested, index)
|
|
166
185
|
additions.append(node)
|
|
167
186
|
added = true
|
|
168
|
-
} else if (list.values.get(token) !== value) {
|
|
169
|
-
fillListItem(node, item, list.descriptor.nested)
|
|
187
|
+
} else if ((referenceOnly ? listItems.get(node) !== item : list.values.get(token) !== value) || list.descriptor.indexed || list.descriptor.key === null) {
|
|
188
|
+
fillListItem(node, item, list.descriptor.nested, index)
|
|
170
189
|
if (__KUDZU_LIST_ITEM_HOOKS__) notifyListItem(list.descriptor.state, node)
|
|
171
190
|
}
|
|
172
191
|
next.push([token, node])
|
|
173
|
-
values.set(token, value)
|
|
192
|
+
if (!referenceOnly) values.set(token, value)
|
|
174
193
|
}
|
|
175
194
|
for (const [token, node] of list.roots) {
|
|
176
195
|
if (keys.has(token)) continue
|
|
@@ -178,7 +197,7 @@ function updateList(list) {
|
|
|
178
197
|
unmountDom(node)
|
|
179
198
|
node.remove()
|
|
180
199
|
} else node.remove()
|
|
181
|
-
if (
|
|
200
|
+
if (__KUDZU_LIST_ROW_HOOKS__ && list.descriptor.rowStates) deleteRowStates(list.descriptor, ownershipPaths.get(node))
|
|
182
201
|
}
|
|
183
202
|
if (added) {
|
|
184
203
|
if (__KUDZU_LIST_MOUNTS__ && list.descriptor.mount) {
|
|
@@ -208,10 +227,102 @@ function updateList(list) {
|
|
|
208
227
|
}
|
|
209
228
|
}
|
|
210
229
|
list.roots = new Map(next)
|
|
230
|
+
if (__KUDZU_LIST_STABLE_FAST_PATHS__) list.orderedRoots = next.map(([, node]) => node)
|
|
211
231
|
list.values = values
|
|
212
232
|
list.items = items
|
|
213
233
|
}
|
|
214
234
|
|
|
235
|
+
/* stable-list-fast-path */
|
|
236
|
+
function updateStableList(list, items) {
|
|
237
|
+
const previous = list.items
|
|
238
|
+
const referenceOnly = usesItemReferences(list)
|
|
239
|
+
if (items.length < previous.length) return false
|
|
240
|
+
const appending = items.length > previous.length
|
|
241
|
+
const keys = new Set()
|
|
242
|
+
const seen = new Set()
|
|
243
|
+
const keyField = list.descriptor.key
|
|
244
|
+
const fragment = list.start.ownerDocument.createDocumentFragment()
|
|
245
|
+
let stable = true
|
|
246
|
+
for (let index = 0; index < items.length; index++) {
|
|
247
|
+
const item = items[index]
|
|
248
|
+
assertListItem(item)
|
|
249
|
+
const seededValue = __KUDZU_LIST_SEEDS__ ? list.seedFields && seededListValue(item, list.seedFields, list.descriptor.seed, !referenceOnly) : undefined
|
|
250
|
+
if (seededValue === undefined) assertListValue(item, seen, true)
|
|
251
|
+
const key = item[keyField]
|
|
252
|
+
if (!validListKey(key)) throw new Error(`Keyed list key "${keyField}" must be a string or finite number`)
|
|
253
|
+
const token = keyToken(key)
|
|
254
|
+
if (keys.has(token)) throw new Error(`Duplicate keyed list key: ${String(key)}`)
|
|
255
|
+
keys.add(token)
|
|
256
|
+
if (index < previous.length && (key !== previous[index][keyField] || appending && item !== previous[index])) stable = false
|
|
257
|
+
if (!stable || items.length === previous.length + 1 || index < previous.length) continue
|
|
258
|
+
let node = (__KUDZU_NESTED_LISTS__ ? list.templateRoot : list.start.content.firstElementChild)?.cloneNode(true)
|
|
259
|
+
if (node?.dataset.kListRoot !== list.descriptor.id) node = undefined
|
|
260
|
+
if (!node) throw new Error("Keyed list template has no root element")
|
|
261
|
+
node.removeAttribute("data-k-list-root")
|
|
262
|
+
if (__KUDZU_LIST_ROW_HOOKS__) initializeGeneralRowHooks(list.descriptor, key, node, list.owner)
|
|
263
|
+
if (list.parts.directFill) fillStructuralListParts(list.parts, node, item)
|
|
264
|
+
else fillListItem(node, item, list.descriptor.nested, index, mapListItemParts(list.parts, node, list.descriptor.nested))
|
|
265
|
+
fragment.append(node)
|
|
266
|
+
}
|
|
267
|
+
if (!stable) return false
|
|
268
|
+
if (appending) {
|
|
269
|
+
if (items.length === previous.length + 1) {
|
|
270
|
+
const item = items.at(-1)
|
|
271
|
+
const key = item[keyField]
|
|
272
|
+
const token = keyToken(key)
|
|
273
|
+
const seededValue = __KUDZU_LIST_SEEDS__ ? list.seedFields && seededListValue(item, list.seedFields, list.descriptor.seed, !referenceOnly) : undefined
|
|
274
|
+
addListRoot(list, { item, index: previous.length, key, token, value: referenceOnly ? item : seededValue === undefined ? JSON.stringify(item) : seededValue })
|
|
275
|
+
if (referenceOnly) list.values.clear()
|
|
276
|
+
list.items = items
|
|
277
|
+
return true
|
|
278
|
+
}
|
|
279
|
+
const parent = list.container ?? list.start.parentNode
|
|
280
|
+
let node = fragment.firstChild
|
|
281
|
+
for (let index = previous.length; node; index++) {
|
|
282
|
+
const item = items[index]
|
|
283
|
+
const token = keyToken(item[keyField])
|
|
284
|
+
list.roots.set(token, node)
|
|
285
|
+
list.orderedRoots.push(node)
|
|
286
|
+
if (!referenceOnly) {
|
|
287
|
+
const seededValue = __KUDZU_LIST_SEEDS__ ? list.seedFields && seededListValue(item, list.seedFields, list.descriptor.seed) : undefined
|
|
288
|
+
list.values.set(token, seededValue === undefined ? JSON.stringify(item) : seededValue)
|
|
289
|
+
}
|
|
290
|
+
node = node.nextSibling
|
|
291
|
+
}
|
|
292
|
+
const firstAdded = fragment.firstChild
|
|
293
|
+
parent.insertBefore(fragment, list.boundary)
|
|
294
|
+
if (referenceOnly) list.values.clear()
|
|
295
|
+
if (__KUDZU_LIST_MOUNTS__ && list.descriptor.mount) for (let node = firstAdded; node !== list.boundary; node = node.nextSibling) mountDom(node)
|
|
296
|
+
list.container ??= parent
|
|
297
|
+
list.items = items
|
|
298
|
+
return true
|
|
299
|
+
}
|
|
300
|
+
if (referenceOnly) {
|
|
301
|
+
for (let index = 0; index < items.length; index++) {
|
|
302
|
+
const item = items[index]
|
|
303
|
+
if (item !== previous[index]) fillListItem(list.orderedRoots[index], item, list.descriptor.nested, index)
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
for (let index = 0; index < items.length; index++) {
|
|
307
|
+
const item = items[index]
|
|
308
|
+
if (item === previous[index]) continue
|
|
309
|
+
const token = keyToken(item[keyField])
|
|
310
|
+
const seededValue = __KUDZU_LIST_SEEDS__ ? list.seedFields && seededListValue(item, list.seedFields, list.descriptor.seed) : undefined
|
|
311
|
+
const value = seededValue === undefined ? JSON.stringify(item) : seededValue
|
|
312
|
+
if (list.values.get(token) !== value) {
|
|
313
|
+
const node = list.orderedRoots[index]
|
|
314
|
+
fillListItem(node, item, list.descriptor.nested, index)
|
|
315
|
+
if (__KUDZU_LIST_ITEM_HOOKS__) notifyListItem(list.descriptor.state, node)
|
|
316
|
+
list.values.set(token, value)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (referenceOnly) list.values.clear()
|
|
321
|
+
list.items = items
|
|
322
|
+
return true
|
|
323
|
+
}
|
|
324
|
+
/* stable-list-fast-path-end */
|
|
325
|
+
|
|
215
326
|
function updateNestedList(list, items) {
|
|
216
327
|
const previous = list.items
|
|
217
328
|
if (items === previous) return false
|
|
@@ -276,7 +387,7 @@ function nestedListEntry(list, item) {
|
|
|
276
387
|
const key = item?.[list.descriptor.key]
|
|
277
388
|
if (!validListKey(key)) throw new Error(`Keyed list key "${list.descriptor.key}" must be a string or finite number`)
|
|
278
389
|
assertListItem(item)
|
|
279
|
-
if (list.descriptor.
|
|
390
|
+
if (list.descriptor.children) validateChildLists([item], list.descriptor.children)
|
|
280
391
|
assertListValue(item, new Set(), true)
|
|
281
392
|
return { item, key, token: keyToken(key), value: JSON.stringify(item) }
|
|
282
393
|
}
|
|
@@ -328,20 +439,21 @@ function updateReducerList(list, items) {
|
|
|
328
439
|
return false
|
|
329
440
|
}
|
|
330
441
|
|
|
331
|
-
function addListRoot(list, { item, key, token, value }) {
|
|
442
|
+
function addListRoot(list, { item, index = list.roots.size, key, token, value }) {
|
|
332
443
|
let node = (__KUDZU_NESTED_LISTS__ ? list.templateRoot : list.start.content.firstElementChild)?.cloneNode(true)
|
|
333
444
|
if (node?.dataset.kListRoot !== list.descriptor.id) node = undefined
|
|
334
445
|
if (!node) throw new Error("Keyed list template has no root element")
|
|
335
446
|
node.removeAttribute("data-k-list-root")
|
|
336
|
-
if (__KUDZU_NESTED_LISTS__ && list.
|
|
337
|
-
if (
|
|
447
|
+
if (__KUDZU_NESTED_LISTS__ && list.childPrototypes) childPrototypes.set(node, list.childPrototypes)
|
|
448
|
+
if (__KUDZU_LIST_ROW_HOOKS__) initializeGeneralRowHooks(list.descriptor, key, node, list.owner)
|
|
338
449
|
mapListItemParts(list.parts, node, list.descriptor.nested)
|
|
339
|
-
fillListItem(node, item, list.descriptor.nested)
|
|
450
|
+
fillListItem(node, item, list.descriptor.nested, index)
|
|
340
451
|
const parent = list.container ?? list.start.parentNode
|
|
341
452
|
parent.insertBefore(node, list.boundary)
|
|
342
453
|
if (__KUDZU_LIST_MOUNTS__ && list.descriptor.mount) mountDom(node)
|
|
343
454
|
list.roots.set(token, node)
|
|
344
|
-
list.
|
|
455
|
+
if (__KUDZU_LIST_STABLE_FAST_PATHS__) list.orderedRoots.push(node)
|
|
456
|
+
if (!usesItemReferences(list)) list.values.set(token, value)
|
|
345
457
|
list.container ??= parent
|
|
346
458
|
}
|
|
347
459
|
|
|
@@ -349,24 +461,25 @@ function removeListRoot(list, token) {
|
|
|
349
461
|
const node = list.roots.get(token)
|
|
350
462
|
if (__KUDZU_LIST_MOUNTS__ && list.descriptor.mount) unmountDom(node)
|
|
351
463
|
node.remove()
|
|
352
|
-
if (
|
|
464
|
+
if (__KUDZU_LIST_ROW_HOOKS__ && list.descriptor.rowStates) deleteRowStates(list.descriptor, ownershipPaths.get(node))
|
|
353
465
|
list.roots.delete(token)
|
|
354
466
|
list.values.delete(token)
|
|
355
467
|
}
|
|
356
468
|
|
|
357
|
-
function fillListItem(root, item, nested = false) {
|
|
469
|
+
function fillListItem(root, item, nested = false, index = 0, parts = listItemParts(root, nested)) {
|
|
358
470
|
listItems.set(root, item)
|
|
471
|
+
if (__KUDZU_LIST_INDEXES__) listIndexes.set(root, index)
|
|
359
472
|
const revision = __KUDZU_LIST_ASYNC_PARTS__ ? (revisions.get(root) ?? 0) + 1 : 0
|
|
360
473
|
if (__KUDZU_LIST_ASYNC_PARTS__) revisions.set(root, revision)
|
|
361
|
-
|
|
362
|
-
fillListParts(root, parts, item, revision)
|
|
474
|
+
fillListParts(root, parts, item, revision, index)
|
|
363
475
|
if (__KUDZU_NESTED_LISTS__) {
|
|
364
|
-
const
|
|
365
|
-
if (child) updateList(child)
|
|
476
|
+
const children = ownedLists.get(root)
|
|
477
|
+
if (children) for (const child of children.values()) updateList(child)
|
|
366
478
|
}
|
|
479
|
+
if (__KUDZU_LIST_ROW_HOOKS__) replaceRowIds(root, rowReplacements.get(root))
|
|
367
480
|
}
|
|
368
481
|
|
|
369
|
-
function fillListParts(root, parts, item, revision) {
|
|
482
|
+
function fillListParts(root, parts, item, revision, index = 0) {
|
|
370
483
|
if (__KUDZU_LIST_EFFECTS__) for (const node of parts.effects) node.dataset.kEffectItem = JSON.stringify(item)
|
|
371
484
|
for (const [node, field] of parts.directTexts) {
|
|
372
485
|
const text = item?.[field]
|
|
@@ -389,31 +502,31 @@ function fillListParts(root, parts, item, revision) {
|
|
|
389
502
|
if (__KUDZU_LIST_EVENTS__) {
|
|
390
503
|
for (const [node, events] of parts.events) {
|
|
391
504
|
for (const [event, native] of JSON.parse(events)) {
|
|
392
|
-
native.scope = Object.fromEntries(Object.entries(native.scope).map(([name, value]) => [name, value?.type === "list-item" ? serializeItem(item) : value]))
|
|
505
|
+
native.scope = Object.fromEntries(Object.entries(native.scope).map(([name, value]) => [name, value?.type === "list-item" ? serializeItem(item) : value?.type === "list-index" ? index : value]))
|
|
393
506
|
node.dataset[`kNative${capitalize(event)}`] = JSON.stringify(native)
|
|
394
507
|
}
|
|
395
508
|
}
|
|
396
509
|
}
|
|
397
510
|
if (__KUDZU_LIST_EXPRESSIONS__) {
|
|
398
511
|
for (const [marker, descriptor] of parts.expressions) {
|
|
399
|
-
evaluate(descriptor, item).then(value => {
|
|
400
|
-
if (revisions.get(root) === revision &&
|
|
512
|
+
evaluate(descriptor, item, index).then(value => {
|
|
513
|
+
if (revisions.get(root) === revision && marker.isConnected) patchListText(marker, "template[data-k-list-expression-end]", value)
|
|
401
514
|
}).catch(error => console.error(error))
|
|
402
515
|
}
|
|
403
516
|
}
|
|
404
517
|
if (__KUDZU_LIST_EXPRESSION_ATTRIBUTES__) {
|
|
405
518
|
for (const [node, attributes] of parts.expressionAttributes) {
|
|
406
519
|
for (const [target, module, handler] of attributes) {
|
|
407
|
-
evaluate({ module, handler }, item).then(value => {
|
|
408
|
-
if (revisions.get(root) === revision &&
|
|
520
|
+
evaluate({ module, handler }, item, index).then(value => {
|
|
521
|
+
if (revisions.get(root) === revision && node.isConnected) patchBinding(node, target, value)
|
|
409
522
|
}).catch(error => console.error(error))
|
|
410
523
|
}
|
|
411
524
|
}
|
|
412
525
|
}
|
|
413
526
|
if (__KUDZU_LIST_CONDITIONS__) {
|
|
414
527
|
for (const [marker, descriptor] of parts.conditions) {
|
|
415
|
-
evaluate(descriptor, item).then(value => {
|
|
416
|
-
if (revisions.get(root) === revision &&
|
|
528
|
+
evaluate(descriptor, item, index).then(value => {
|
|
529
|
+
if (revisions.get(root) === revision && marker.isConnected) updateListCondition(marker, descriptor.kind, value, item, index)
|
|
417
530
|
}).catch(error => console.error(error))
|
|
418
531
|
}
|
|
419
532
|
}
|
|
@@ -462,21 +575,45 @@ function listItemPartPlan(template, nested = false) {
|
|
|
462
575
|
const source = nested ? ownedElements(template) : [template, ...template.querySelectorAll("*")]
|
|
463
576
|
const indexes = new Map(source.map((node, index) => [node, index]))
|
|
464
577
|
const parts = listItemParts(template, nested)
|
|
578
|
+
const structural = !nested && !parts.texts.length && !parts.expressions.length && !parts.expressionAttributes.length && !parts.conditions.length && !parts.effects.length
|
|
579
|
+
const location = structural ? node => elementPath(template, node) : node => indexes.get(node)
|
|
465
580
|
return {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
581
|
+
structural,
|
|
582
|
+
directFill: structural && !parts.events.length && !__KUDZU_LIST_ASYNC_PARTS__ && !__KUDZU_NESTED_LISTS__ && !__KUDZU_LIST_INDEXES__ && !__KUDZU_LIST_ROW_HOOKS__,
|
|
583
|
+
directTexts: parts.directTexts.map(([node, field]) => [location(node), field]),
|
|
584
|
+
texts: __KUDZU_LIST_TEXT_RANGES__ ? parts.texts.map(([node, field]) => [location(node), field]) : [],
|
|
585
|
+
attributes: __KUDZU_LIST_ATTRIBUTES__ ? parts.attributes.map(([node, attributes]) => [location(node), attributes]) : [],
|
|
586
|
+
events: __KUDZU_LIST_EVENTS__ ? parts.events.map(([node, events]) => [location(node), events]) : [],
|
|
587
|
+
expressions: __KUDZU_LIST_EXPRESSIONS__ ? parts.expressions.map(([node, descriptor]) => [location(node), descriptor]) : [],
|
|
588
|
+
expressionAttributes: __KUDZU_LIST_EXPRESSION_ATTRIBUTES__ ? parts.expressionAttributes.map(([node, attributes]) => [location(node), attributes]) : [],
|
|
589
|
+
conditions: __KUDZU_LIST_CONDITIONS__ ? parts.conditions.map(([node, descriptor]) => [location(node), descriptor, node]) : [],
|
|
590
|
+
effects: __KUDZU_LIST_EFFECTS__ ? parts.effects.map(location) : []
|
|
474
591
|
}
|
|
475
592
|
}
|
|
476
593
|
|
|
477
594
|
function mapListItemParts(parts, root, nested = false) {
|
|
595
|
+
if (parts.structural) {
|
|
596
|
+
const target = path => {
|
|
597
|
+
if (!path.length) return root
|
|
598
|
+
let node = root.children[path[0]]
|
|
599
|
+
for (let index = 1; index < path.length; index++) node = node.children[path[index]]
|
|
600
|
+
return node
|
|
601
|
+
}
|
|
602
|
+
const mapped = {
|
|
603
|
+
directTexts: parts.directTexts.map(([path, field]) => [target(path), field]),
|
|
604
|
+
texts: [],
|
|
605
|
+
attributes: __KUDZU_LIST_ATTRIBUTES__ ? parts.attributes.map(([path, attributes]) => [target(path), attributes]) : [],
|
|
606
|
+
events: __KUDZU_LIST_EVENTS__ ? parts.events.map(([path, events]) => [target(path), events]) : [],
|
|
607
|
+
expressions: [],
|
|
608
|
+
expressionAttributes: [],
|
|
609
|
+
conditions: [],
|
|
610
|
+
effects: []
|
|
611
|
+
}
|
|
612
|
+
itemParts.set(root, mapped)
|
|
613
|
+
return mapped
|
|
614
|
+
}
|
|
478
615
|
const target = nested ? ownedElements(root) : [root, ...root.querySelectorAll("*")]
|
|
479
|
-
|
|
616
|
+
const mapped = {
|
|
480
617
|
directTexts: parts.directTexts.map(([index, field]) => [target[index], field]),
|
|
481
618
|
texts: __KUDZU_LIST_TEXT_RANGES__ ? parts.texts.map(([index, field]) => [target[index], field]) : [],
|
|
482
619
|
attributes: __KUDZU_LIST_ATTRIBUTES__ ? parts.attributes.map(([index, attributes]) => [target[index], attributes]) : [],
|
|
@@ -488,19 +625,73 @@ function mapListItemParts(parts, root, nested = false) {
|
|
|
488
625
|
return [target[index], descriptor]
|
|
489
626
|
}) : [],
|
|
490
627
|
effects: __KUDZU_LIST_EFFECTS__ ? parts.effects.map(index => target[index]) : []
|
|
491
|
-
}
|
|
628
|
+
}
|
|
629
|
+
itemParts.set(root, mapped)
|
|
630
|
+
return mapped
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function fillStructuralListParts(parts, root, item) {
|
|
634
|
+
for (let partIndex = 0; partIndex < parts.directTexts.length; partIndex++) {
|
|
635
|
+
const part = parts.directTexts[partIndex]
|
|
636
|
+
const path = part[0]
|
|
637
|
+
let node = root
|
|
638
|
+
for (let pathIndex = 0; pathIndex < path.length; pathIndex++) node = node.children[path[pathIndex]]
|
|
639
|
+
node.textContent = item[part[1]] ?? ""
|
|
640
|
+
}
|
|
641
|
+
if (__KUDZU_LIST_ATTRIBUTES__) for (let partIndex = 0; partIndex < parts.attributes.length; partIndex++) {
|
|
642
|
+
const part = parts.attributes[partIndex]
|
|
643
|
+
const path = part[0]
|
|
644
|
+
let node = root
|
|
645
|
+
for (let pathIndex = 0; pathIndex < path.length; pathIndex++) node = node.children[path[pathIndex]]
|
|
646
|
+
const attributes = part[1]
|
|
647
|
+
for (let index = 0; index < attributes.length; index++) patchBinding(node, attributes[index][0], item[attributes[index][1]])
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function elementPath(root, node) {
|
|
652
|
+
const path = []
|
|
653
|
+
while (node !== root) {
|
|
654
|
+
const parent = node.parentElement
|
|
655
|
+
path.push(Array.prototype.indexOf.call(parent.children, node))
|
|
656
|
+
node = parent
|
|
657
|
+
}
|
|
658
|
+
return path.reverse()
|
|
492
659
|
}
|
|
493
660
|
|
|
494
661
|
function mapListConditionTemplates(planned, initial) {
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
662
|
+
if (!__KUDZU_DEEP_LIST_CONDITIONS__) {
|
|
663
|
+
if (planned.length !== initial.length) throw new Error("Keyed list condition markers do not match its template")
|
|
664
|
+
for (let index = 0; index < initial.length; index++) {
|
|
665
|
+
const marker = initial[index][0]
|
|
666
|
+
initial[index][1] ??= planned[index][1]
|
|
667
|
+
if (!marker.content.querySelector("template[data-k-list-true],template[data-k-list-false]")) conditionTemplates.set(marker, planned[index][2])
|
|
668
|
+
}
|
|
669
|
+
return
|
|
670
|
+
}
|
|
671
|
+
const templates = new Map()
|
|
672
|
+
const collect = root => {
|
|
673
|
+
for (const marker of root.querySelectorAll("template")) {
|
|
674
|
+
if (marker.hasAttribute("data-k-list-condition")) {
|
|
675
|
+
const descriptor = JSON.parse(marker.dataset.kListCondition)
|
|
676
|
+
templates.set(descriptor.handler, [descriptor, marker])
|
|
677
|
+
}
|
|
678
|
+
collect(marker.content)
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
for (const entry of planned) {
|
|
682
|
+
templates.set(entry[1].handler, [entry[1], entry[2]])
|
|
683
|
+
collect(entry[2].content)
|
|
684
|
+
}
|
|
685
|
+
for (const entry of initial) {
|
|
686
|
+
const marker = entry[0]
|
|
687
|
+
const template = templates.get(entry[1]?.handler ?? marker.dataset.kListConditionHandler)
|
|
688
|
+
if (!template) throw new Error("Keyed list condition marker has no matching template")
|
|
689
|
+
entry[1] ??= template[0]
|
|
690
|
+
if (!marker.content.querySelector("template[data-k-list-true],template[data-k-list-false]")) conditionTemplates.set(marker, template[1])
|
|
500
691
|
}
|
|
501
692
|
}
|
|
502
693
|
|
|
503
|
-
function updateListCondition(marker, kind, value, item) {
|
|
694
|
+
function updateListCondition(marker, kind, value, item, index) {
|
|
504
695
|
const current = listConditionKey(kind, value)
|
|
505
696
|
if (marker.dataset.kListCurrent === current) return
|
|
506
697
|
let end = marker.nextSibling
|
|
@@ -523,7 +714,7 @@ function updateListCondition(marker, kind, value, item) {
|
|
|
523
714
|
const nodes = [...fragment.childNodes]
|
|
524
715
|
const revision = (revisions.get(marker) ?? 0) + 1
|
|
525
716
|
revisions.set(marker, revision)
|
|
526
|
-
fillListParts(marker, listItemParts(fragment), item, revision)
|
|
717
|
+
fillListParts(marker, listItemParts(fragment), item, revision, index)
|
|
527
718
|
end.parentNode.insertBefore(fragment, end)
|
|
528
719
|
marker.dataset.kListCurrent = current
|
|
529
720
|
const owner = conditionOwners.get(marker)
|
|
@@ -539,19 +730,20 @@ function renderFalsy(value) {
|
|
|
539
730
|
return value === false || value == null || value === true ? "" : String(value)
|
|
540
731
|
}
|
|
541
732
|
|
|
542
|
-
function validateChildLists(items,
|
|
733
|
+
function validateChildLists(items, children) {
|
|
543
734
|
for (const item of items) {
|
|
544
|
-
const children
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
735
|
+
for (const child of children) {
|
|
736
|
+
const values = __KUDZU_COLLECTION_SELECTORS__ ? selectCollection(item?.[child.field], child.selector) : item?.[child.field] ?? []
|
|
737
|
+
const keys = new Set()
|
|
738
|
+
for (const entry of values) {
|
|
739
|
+
const key = child.key === null ? keys.size : entry?.[child.key]
|
|
740
|
+
if (!validListKey(key)) throw new Error(`Keyed list key "${child.key}" must be a string or finite number`)
|
|
741
|
+
assertListItem(entry)
|
|
742
|
+
assertListValue(entry, new Set(), true)
|
|
743
|
+
const token = keyToken(key)
|
|
744
|
+
if (keys.has(token)) throw new Error(`Duplicate keyed list key: ${String(key)}`)
|
|
745
|
+
keys.add(token)
|
|
746
|
+
}
|
|
555
747
|
}
|
|
556
748
|
}
|
|
557
749
|
}
|
|
@@ -564,24 +756,24 @@ function listOwner(start) {
|
|
|
564
756
|
|
|
565
757
|
function mountNestedPrototype(start, descriptor, roots) {
|
|
566
758
|
const owner = descriptor.ownerField ? listOwner(start) : undefined
|
|
567
|
-
const prototypeStart = owner ? childPrototypes.get(owner) : undefined
|
|
759
|
+
const prototypeStart = owner ? childPrototypes.get(owner)?.get(descriptor.id) : undefined
|
|
568
760
|
const templateRoot = prototypeStart?.content.firstElementChild ?? start.content.firstElementChild
|
|
569
761
|
if (!templateRoot) throw new Error("Nested keyed list has no shared row prototype")
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
572
|
-
if (
|
|
762
|
+
const prototypes = descriptor.children && new Map(descriptor.children.map(child => [child.id, findChildPrototype(templateRoot, child.id)]))
|
|
763
|
+
if (prototypes && [...prototypes.values()].some(prototype => !prototype)) throw new Error("Keyed list template has no nested row prototype")
|
|
764
|
+
if (prototypes?.size) for (const root of roots) childPrototypes.set(root, prototypes)
|
|
573
765
|
if (prototypeStart && start !== prototypeStart) start.content.replaceChildren()
|
|
574
|
-
return { owner,
|
|
766
|
+
return { owner, childPrototypes: prototypes, templateRoot }
|
|
575
767
|
}
|
|
576
768
|
|
|
577
|
-
function findChildPrototype(root,
|
|
769
|
+
function findChildPrototype(root, id) {
|
|
578
770
|
for (const element of root.children) {
|
|
579
771
|
if (element.matches("template[data-k-list]")) {
|
|
580
|
-
if (JSON.parse(element.dataset.kList).
|
|
581
|
-
const nested = findChildPrototype(element.content,
|
|
772
|
+
if (JSON.parse(element.dataset.kList).id === id) return element
|
|
773
|
+
const nested = findChildPrototype(element.content, id)
|
|
582
774
|
if (nested) return nested
|
|
583
775
|
} else {
|
|
584
|
-
const nested = findChildPrototype(element,
|
|
776
|
+
const nested = findChildPrototype(element, id)
|
|
585
777
|
if (nested) return nested
|
|
586
778
|
}
|
|
587
779
|
}
|
|
@@ -653,14 +845,14 @@ function patchListText(marker, endSelector, value) {
|
|
|
653
845
|
end.before(marker.ownerDocument.createTextNode(text))
|
|
654
846
|
}
|
|
655
847
|
|
|
656
|
-
function evaluate(descriptor, item) {
|
|
848
|
+
function evaluate(descriptor, item, index) {
|
|
657
849
|
let module = imports.get(descriptor.module)
|
|
658
850
|
if (!module) {
|
|
659
851
|
module = import(descriptor.module)
|
|
660
852
|
imports.set(descriptor.module, module)
|
|
661
853
|
}
|
|
662
854
|
return module.then(exports => {
|
|
663
|
-
const value = exports[descriptor.handler](item)
|
|
855
|
+
const value = exports[descriptor.handler](item, index)
|
|
664
856
|
if (value && typeof value.then === "function") throw new Error("Derived keyed list item expressions must return synchronous values")
|
|
665
857
|
return value
|
|
666
858
|
})
|
|
@@ -688,7 +880,8 @@ function patchBinding(node, target, value) {
|
|
|
688
880
|
} else if (value == null || (value === false && !isStringBooleanAttribute(target))) {
|
|
689
881
|
node.removeAttribute(target)
|
|
690
882
|
} else {
|
|
691
|
-
|
|
883
|
+
const next = value === true && !isStringBooleanAttribute(target) ? "" : String(value)
|
|
884
|
+
if (node.getAttribute(target) !== next) node.setAttribute(target, next)
|
|
692
885
|
}
|
|
693
886
|
}
|
|
694
887
|
|
|
@@ -696,27 +889,56 @@ function keyToken(key) {
|
|
|
696
889
|
return `${typeof key}:${key}`
|
|
697
890
|
}
|
|
698
891
|
|
|
892
|
+
/* general-row-hooks */
|
|
893
|
+
function initializeGeneralRowHooks(descriptor, key, root, owner) {
|
|
894
|
+
const token = keyToken(key)
|
|
895
|
+
const path = [...(ownershipPaths.get(owner) ?? []), `${descriptor.id}=${token}`]
|
|
896
|
+
ownershipPaths.set(root, path)
|
|
897
|
+
const statePath = descriptor.ownerField ? path : [token]
|
|
898
|
+
root.dataset.kRowPath = encodeURIComponent(statePath.join("/"))
|
|
899
|
+
const replacements = new Map()
|
|
900
|
+
for (const state of descriptor.rowStates ?? []) {
|
|
901
|
+
const id = rowStateId(state.id, statePath)
|
|
902
|
+
if (!browserState.has(id)) browserState.set(id, __KUDZU_COMPLEX_LIST_ROW_STATE__ && state.initialValue !== null && typeof state.initialValue === "object" ? structuredClone(state.initialValue) : state.initialValue)
|
|
903
|
+
replacements.set(state.id, id)
|
|
904
|
+
}
|
|
905
|
+
if (__KUDZU_LIST_ROW_REFS__) for (const ref of descriptor.rowRefs ?? []) replacements.set(ref, rowStateId(ref, statePath))
|
|
906
|
+
for (const marker of descriptor.rowConditions ?? []) replacements.set(marker, rowStateId(marker, statePath))
|
|
907
|
+
rowReplacements.set(root, replacements)
|
|
908
|
+
replaceRowIds(root, replacements)
|
|
909
|
+
}
|
|
910
|
+
/* general-row-hooks-end */
|
|
911
|
+
|
|
699
912
|
function initializeRowStates(descriptor, key, root) {
|
|
700
913
|
const token = keyToken(key)
|
|
701
914
|
const replacements = new Map()
|
|
702
915
|
for (const state of descriptor.rowStates) {
|
|
703
|
-
const id =
|
|
916
|
+
const id = flatRowStateId(state.id, token)
|
|
704
917
|
if (!browserState.has(id)) browserState.set(id, state.initialValue)
|
|
705
918
|
replacements.set(state.id, id)
|
|
706
919
|
}
|
|
707
|
-
for (const marker of descriptor.rowConditions ?? []) replacements.set(marker,
|
|
920
|
+
for (const marker of descriptor.rowConditions ?? []) replacements.set(marker, flatRowStateId(marker, token))
|
|
708
921
|
if (root) replaceRowIds(root, replacements)
|
|
709
922
|
}
|
|
710
923
|
|
|
711
|
-
function
|
|
712
|
-
for (const state of descriptor.rowStates) browserState.delete(
|
|
924
|
+
function deleteFlatRowStates(descriptor, token) {
|
|
925
|
+
for (const state of descriptor.rowStates) browserState.delete(flatRowStateId(state.id, token))
|
|
713
926
|
}
|
|
714
927
|
|
|
715
|
-
function
|
|
928
|
+
function flatRowStateId(id, token) {
|
|
716
929
|
return id.replace("$k", encodeURIComponent(token))
|
|
717
930
|
}
|
|
718
931
|
|
|
932
|
+
function deleteRowStates(descriptor, path) {
|
|
933
|
+
const statePath = descriptor.ownerField ? path : [path.at(-1).slice(path.at(-1).indexOf("=") + 1)]
|
|
934
|
+
for (const state of descriptor.rowStates) browserState.delete(rowStateId(state.id, statePath))
|
|
935
|
+
}
|
|
936
|
+
function rowStateId(id, path) {
|
|
937
|
+
return id.replace("$k", encodeURIComponent(path.join("/")))
|
|
938
|
+
}
|
|
939
|
+
|
|
719
940
|
function replaceRowIds(root, replacements) {
|
|
941
|
+
if (!replacements) return
|
|
720
942
|
const replace = node => {
|
|
721
943
|
for (const attribute of [...node.attributes]) {
|
|
722
944
|
if (!attribute.name.startsWith("data-k-")) continue
|
|
@@ -739,7 +961,7 @@ function assertListItem(item) {
|
|
|
739
961
|
if (!item || Array.isArray(item) || prototype !== Object.prototype) throw new Error("Keyed list items must be ordinary plain objects")
|
|
740
962
|
}
|
|
741
963
|
|
|
742
|
-
function seededListValue(item, fields, seed) {
|
|
964
|
+
function seededListValue(item, fields, seed, serialize = true) {
|
|
743
965
|
const keys = Reflect.ownKeys(item)
|
|
744
966
|
if (fields.length === 1) {
|
|
745
967
|
const field = fields[0]
|
|
@@ -749,10 +971,10 @@ function seededListValue(item, fields, seed) {
|
|
|
749
971
|
if (!("value" in descriptor)) throw new Error("Keyed list items must not contain accessors")
|
|
750
972
|
const value = descriptor.value
|
|
751
973
|
const type = value === null ? "null" : typeof value
|
|
752
|
-
return type === seed[field] && !(type === "number" && (!Number.isFinite(value) || Object.is(value, -0))) ? value : undefined
|
|
974
|
+
return type === seed[field] && !(type === "number" && (!Number.isFinite(value) || Object.is(value, -0))) ? serialize ? value : true : undefined
|
|
753
975
|
}
|
|
754
976
|
if (keys.length !== fields.length || keys.some(key => typeof key !== "string" || !fields.includes(key))) return undefined
|
|
755
|
-
const values = []
|
|
977
|
+
const values = serialize ? [] : undefined
|
|
756
978
|
for (const field of fields) {
|
|
757
979
|
const descriptor = Object.getOwnPropertyDescriptor(item, field)
|
|
758
980
|
if (!descriptor.enumerable) throw new Error("Keyed list items must not contain non-enumerable properties")
|
|
@@ -760,9 +982,13 @@ function seededListValue(item, fields, seed) {
|
|
|
760
982
|
const value = descriptor.value
|
|
761
983
|
const type = value === null ? "null" : typeof value
|
|
762
984
|
if (type !== seed[field] || type === "number" && (!Number.isFinite(value) || Object.is(value, -0))) return undefined
|
|
763
|
-
values.push(value)
|
|
985
|
+
if (serialize) values.push(value)
|
|
764
986
|
}
|
|
765
|
-
return values.length === 1 ? values[0] : JSON.stringify(values)
|
|
987
|
+
return serialize ? values.length === 1 ? values[0] : JSON.stringify(values) : true
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function usesItemReferences(list) {
|
|
991
|
+
return !list.descriptor.mount && !list.descriptor.effects && !list.descriptor.events && !list.descriptor.expressions && !list.descriptor.expressionAttributes && !list.descriptor.conditions
|
|
766
992
|
}
|
|
767
993
|
|
|
768
994
|
function assertListValue(value, seen, root = false) {
|