@kudzujs/core 0.6.27 → 0.6.28
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 +53 -37
- package/framework/README.md +7 -3
- package/framework/binding-runtime.js +4 -2
- package/framework/build.mjs +397 -155
- package/framework/collection-selector.js +63 -0
- package/framework/core.d.ts +2 -1
- package/framework/core.mjs +70 -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 +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export function selectCollection(anchor, selector = []) {
|
|
2
|
+
let values = anchor == null ? [] : anchor
|
|
3
|
+
for (const operation of selector) {
|
|
4
|
+
if (operation[0] === "from") values = Array.from(values, operation[1] ? (item, index) => evaluateCollectionExpression(operation[1], item, index) : undefined)
|
|
5
|
+
else {
|
|
6
|
+
if (!Array.isArray(values)) throw new Error("Rendered collection source must remain an array")
|
|
7
|
+
if (operation[0] === "filter") values = values.filter((item, index) => evaluateCollectionExpression(operation[1], item, index))
|
|
8
|
+
else if (operation[0] === "flatMap") values = values.flatMap(item => item?.[operation[1]] ?? [])
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (!Array.isArray(values)) throw new Error("Rendered collection source must remain an array")
|
|
12
|
+
return values
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function evaluateCollectionExpression(expression, item, index) {
|
|
16
|
+
const [kind, ...parts] = expression
|
|
17
|
+
if (kind === "value") return parts[0]
|
|
18
|
+
if (kind === "undefined") return undefined
|
|
19
|
+
if (kind === "item") return item
|
|
20
|
+
if (kind === "index") return index
|
|
21
|
+
if (kind === "get") {
|
|
22
|
+
const object = evaluateCollectionExpression(parts[0], item, index)
|
|
23
|
+
return object == null && parts[2] ? undefined : object[parts[1]]
|
|
24
|
+
}
|
|
25
|
+
if (kind === "unary") {
|
|
26
|
+
const value = evaluateCollectionExpression(parts[1], item, index)
|
|
27
|
+
if (parts[0] === "!") return !value
|
|
28
|
+
if (parts[0] === "+") return +value
|
|
29
|
+
if (parts[0] === "-") return -value
|
|
30
|
+
if (parts[0] === "typeof") return typeof value
|
|
31
|
+
}
|
|
32
|
+
if (kind === "binary") {
|
|
33
|
+
const left = evaluateCollectionExpression(parts[1], item, index)
|
|
34
|
+
if (parts[0] === "&&") return left && evaluateCollectionExpression(parts[2], item, index)
|
|
35
|
+
if (parts[0] === "||") return left || evaluateCollectionExpression(parts[2], item, index)
|
|
36
|
+
if (parts[0] === "??") return left ?? evaluateCollectionExpression(parts[2], item, index)
|
|
37
|
+
const right = evaluateCollectionExpression(parts[2], item, index)
|
|
38
|
+
if (parts[0] === "===") return left === right
|
|
39
|
+
if (parts[0] === "!==") return left !== right
|
|
40
|
+
if (parts[0] === "==") return left == right
|
|
41
|
+
if (parts[0] === "!=") return left != right
|
|
42
|
+
if (parts[0] === "<") return left < right
|
|
43
|
+
if (parts[0] === "<=") return left <= right
|
|
44
|
+
if (parts[0] === ">") return left > right
|
|
45
|
+
if (parts[0] === ">=") return left >= right
|
|
46
|
+
if (parts[0] === "+") return left + right
|
|
47
|
+
if (parts[0] === "-") return left - right
|
|
48
|
+
if (parts[0] === "*") return left * right
|
|
49
|
+
if (parts[0] === "/") return left / right
|
|
50
|
+
if (parts[0] === "%") return left % right
|
|
51
|
+
}
|
|
52
|
+
if (kind === "conditional") return evaluateCollectionExpression(parts[0], item, index) ? evaluateCollectionExpression(parts[1], item, index) : evaluateCollectionExpression(parts[2], item, index)
|
|
53
|
+
if (kind === "array") return parts.map(value => evaluateCollectionExpression(value, item, index))
|
|
54
|
+
if (kind === "object") return Object.fromEntries(parts.map(([key, value]) => [key, evaluateCollectionExpression(value, item, index)]))
|
|
55
|
+
if (kind === "template") return parts[0].map((text, offset) => text + (offset < parts[1].length ? evaluateCollectionExpression(parts[1][offset], item, index) : "")).join("")
|
|
56
|
+
if (kind === "call") {
|
|
57
|
+
const receiver = evaluateCollectionExpression(parts[0], item, index)
|
|
58
|
+
return receiver[parts[1]](...parts.slice(2).map(value => evaluateCollectionExpression(value, item, index)))
|
|
59
|
+
}
|
|
60
|
+
if (kind === "global") return globalThis[parts[0]](...parts.slice(1).map(value => evaluateCollectionExpression(value, item, index)))
|
|
61
|
+
if (kind === "math") return Math[parts[0]](...parts.slice(1).map(value => evaluateCollectionExpression(value, item, index)))
|
|
62
|
+
throw new Error(`Unsupported rendered collection expression: ${String(kind)}`)
|
|
63
|
+
}
|
package/framework/core.d.ts
CHANGED
|
@@ -27,10 +27,11 @@ export function nativeBehavior(module: string, handler: string, states: Array<[s
|
|
|
27
27
|
export function binding(value: unknown, module: string, handler: string, states: Array<[string, unknown]>, scope: Array<[string, unknown]>): unknown
|
|
28
28
|
export function bindingValue(value: unknown): unknown
|
|
29
29
|
export function conditional(kind: "and" | "ternary", value: unknown, truthy: () => unknown, falsy: () => unknown, module: string, handler: string, states: Array<[string, unknown]>, scope: Array<[string, unknown]>): unknown
|
|
30
|
-
export function list(items: unknown, keyField: string, render: (item: unknown) => unknown, ownerField?: string): unknown
|
|
30
|
+
export function list(items: unknown, keyField: string | null, render: (item: unknown, index: number) => unknown, ownerField?: string, selector?: unknown[], indexed?: boolean): unknown
|
|
31
31
|
export function listField(read: () => unknown, field: string): unknown
|
|
32
32
|
export function listExpression(read: () => unknown, module: string, handler: string): unknown
|
|
33
33
|
export function listItem(): unknown
|
|
34
|
+
export function listIndex(): unknown
|
|
34
35
|
export function listConditional(kind: "and" | "ternary", read: () => unknown, truthy: () => unknown, falsy: () => unknown, module: string, handler: string): unknown
|
|
35
36
|
|
|
36
37
|
export type PageMetadata = {
|
package/framework/core.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { serializeStyle } from "./style.js"
|
|
2
|
+
import { selectCollection } from "./collection-selector.js"
|
|
2
3
|
|
|
3
4
|
const signalMarker = Symbol("kudzu.signal")
|
|
4
5
|
const setterMarker = Symbol("kudzu.setter")
|
|
@@ -12,6 +13,7 @@ const listMarker = Symbol("kudzu.list")
|
|
|
12
13
|
const listFieldMarker = Symbol("kudzu.listField")
|
|
13
14
|
const listExpressionMarker = Symbol("kudzu.listExpression")
|
|
14
15
|
const listItemMarker = Symbol("kudzu.listItem")
|
|
16
|
+
const listIndexMarker = Symbol("kudzu.listIndex")
|
|
15
17
|
const listConditionalMarker = Symbol("kudzu.listConditional")
|
|
16
18
|
const refMarker = Symbol("kudzu.ref")
|
|
17
19
|
const contextMarker = Symbol("kudzu.context")
|
|
@@ -116,9 +118,10 @@ export function useEffect(callback, dependencies, module, handler, states, scope
|
|
|
116
118
|
if (!effects) throw new Error(`${source} useEffect() inside keyed lists must belong to the direct row component`)
|
|
117
119
|
const index = effects.length
|
|
118
120
|
if (renderContext.listTemplate) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
const existing = renderContext.listEffectOwners[index]
|
|
122
|
+
owner = existing ?? nextRenderId("e")
|
|
123
|
+
renderContext.listEffectOwners[index] = owner
|
|
124
|
+
list = !existing
|
|
122
125
|
} else {
|
|
123
126
|
owner = renderContext.listEffectOwners[index]
|
|
124
127
|
if (!owner) throw new Error(`${source} Keyed row effects must have the same hook order for every item`)
|
|
@@ -146,7 +149,8 @@ function validEffectDependency(value) {
|
|
|
146
149
|
export function useRef(initialValue) {
|
|
147
150
|
if (!renderContext) throw new Error("useRef() can only run while rendering a Kudzu component")
|
|
148
151
|
if (initialValue !== null) throw new Error("Kudzu DOM refs must initialize with null")
|
|
149
|
-
|
|
152
|
+
const row = Boolean(renderContext.listRoot || renderContext.listRowRoot)
|
|
153
|
+
return { [refMarker]: true, id: row ? nextRowRenderId("r") : nextRenderId("r"), current: null, row }
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
export function createContext(defaultValue) {
|
|
@@ -214,7 +218,7 @@ export function stateConditional(kind, state, truthy, falsy) {
|
|
|
214
218
|
return { [conditionalMarker]: true, kind, value: state.value, truthy, falsy, state: state.id }
|
|
215
219
|
}
|
|
216
220
|
|
|
217
|
-
export function list(items, keyField, render, ownerField) {
|
|
221
|
+
export function list(items, keyField, render, ownerField, selector = [], indexed = false) {
|
|
218
222
|
if (!items?.[signalMarker] || !Array.isArray(items.value)) throw new Error("A keyed list must use local array state")
|
|
219
223
|
let values = items.value
|
|
220
224
|
if (ownerField) {
|
|
@@ -222,11 +226,12 @@ export function list(items, keyField, render, ownerField) {
|
|
|
222
226
|
if (!owner) throw new Error("A nested keyed list must be rendered inside a keyed row")
|
|
223
227
|
renderContext.listFields?.add(ownerField)
|
|
224
228
|
values = renderContext.listTemplate ? [] : owner.item?.[ownerField]
|
|
225
|
-
if (!Array.isArray(values)) throw new Error(`Nested keyed list property "${ownerField}" must remain an array`)
|
|
229
|
+
if (!Array.isArray(values) && values != null) throw new Error(`Nested keyed list property "${ownerField}" must remain an array`)
|
|
226
230
|
}
|
|
231
|
+
values = selectCollection(values, selector)
|
|
227
232
|
const keys = new Set()
|
|
228
|
-
for (const item of values) {
|
|
229
|
-
const key = item?.[keyField]
|
|
233
|
+
for (const [index, item] of values.entries()) {
|
|
234
|
+
const key = keyField === null ? index : item?.[keyField]
|
|
230
235
|
if (!validListKey(key)) throw new Error(`Keyed list key "${keyField}" must be a string or finite number`)
|
|
231
236
|
assertListItem(item)
|
|
232
237
|
assertListValue(item, new Set())
|
|
@@ -234,7 +239,7 @@ export function list(items, keyField, render, ownerField) {
|
|
|
234
239
|
if (keys.has(token)) throw new Error(`Duplicate keyed list key: ${String(key)}`)
|
|
235
240
|
keys.add(token)
|
|
236
241
|
}
|
|
237
|
-
return { [listMarker]: true, items, values, keyField, render, ownerField }
|
|
242
|
+
return { [listMarker]: true, items, values, keyField, render, ownerField, selector, indexed }
|
|
238
243
|
}
|
|
239
244
|
|
|
240
245
|
export function listField(read, field) {
|
|
@@ -252,6 +257,10 @@ export function listItem() {
|
|
|
252
257
|
return { [listItemMarker]: true }
|
|
253
258
|
}
|
|
254
259
|
|
|
260
|
+
export function listIndex() {
|
|
261
|
+
return { [listIndexMarker]: true }
|
|
262
|
+
}
|
|
263
|
+
|
|
255
264
|
export function listConditional(kind, read, truthy, falsy, module, handler) {
|
|
256
265
|
renderContext?.handlerModules.add(module)
|
|
257
266
|
return { [listConditionalMarker]: true, kind, value: renderContext?.listTemplate ? undefined : read(), truthy, falsy, module, handler }
|
|
@@ -320,6 +329,7 @@ function bindingDescriptor(value) {
|
|
|
320
329
|
|
|
321
330
|
function serializeCapture(name, value, seen) {
|
|
322
331
|
if (value?.[listItemMarker]) return { type: "list-item" }
|
|
332
|
+
if (value?.[listIndexMarker]) return { type: "list-index" }
|
|
323
333
|
if (value?.[refMarker]) return { type: "ref", id: value.id }
|
|
324
334
|
if (value?.[signalMarker]) return { type: "state", id: value.id }
|
|
325
335
|
if (typeof value === "function" && value[reducerDispatchMarker]) throw new Error(`Native capture "${name}" cannot contain a reducer dispatch`)
|
|
@@ -355,7 +365,7 @@ function serializeCapture(name, value, seen) {
|
|
|
355
365
|
}
|
|
356
366
|
|
|
357
367
|
export async function renderPage(component, metadata = {}, props = {}, layout) {
|
|
358
|
-
renderContext = { scoped: Boolean(layout), renderScope: layout ? "layout" : "route", counters: { layout: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0 }, route: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0 } }, nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, nextEffect: 0, nextParam: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listRowRoot: undefined, listTemplate: false, listInitialMarkers: false, listConditionalBranch: false, listFields: undefined, listEffectOwners: [], listRowStates: [], listRowConditions: [], listRowLists: [], effectOwners: [], contexts: [], states: {}, textStates: new Set(), conditionStates: new Set(), events: [], effects: [], bindings: [], textBindings: [], conditions: [], lists: [], handlerModules: new Set(), runtimeParamNames: metadata.runtimeParams, paramEntries: [], params: undefined, hasBehaviors: false, hasNativeBehaviors: false, hasEffects: false, hasParams: false, hasBindings: false, hasLists: false, hasListStyles: false }
|
|
368
|
+
renderContext = { scoped: Boolean(layout), renderScope: layout ? "layout" : "route", counters: { layout: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0 }, route: { s: 0, r: 0, c: 0, l: 0, e: 0, p: 0 } }, nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, nextEffect: 0, nextParam: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listRowRoot: undefined, listTemplate: false, listInitialMarkers: false, listConditionalBranch: false, listFields: undefined, listEffectOwners: [], listRowStates: [], listRowRefs: [], listRowConditions: [], listRowLists: [], effectOwners: [], contexts: [], states: {}, textStates: new Set(), conditionStates: new Set(), events: [], effects: [], bindings: [], textBindings: [], conditions: [], lists: [], handlerModules: new Set(), runtimeParamNames: metadata.runtimeParams, paramEntries: [], params: undefined, hasBehaviors: false, hasNativeBehaviors: false, hasEffects: false, hasParams: false, hasBindings: false, hasLists: false, hasListStyles: false }
|
|
359
369
|
|
|
360
370
|
try {
|
|
361
371
|
const page = { [routeScopeMarker]: true, component, props }
|
|
@@ -573,6 +583,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
573
583
|
const owner = renderContext.listRoot ?? renderContext.listRowRoot
|
|
574
584
|
if (owner) owner.conditions = true
|
|
575
585
|
const previousBranch = renderContext.listConditionalBranch
|
|
586
|
+
if (owner && previousBranch) owner.nestedConditions = true
|
|
576
587
|
renderContext.listConditionalBranch = true
|
|
577
588
|
let truthy
|
|
578
589
|
let falsy
|
|
@@ -587,8 +598,10 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
587
598
|
? ""
|
|
588
599
|
: node.kind === "and" && !node.value ? escapeHtml(renderFalsy(node.value)) : node.value ? truthy : falsy
|
|
589
600
|
const initial = renderContext.listTemplate ? "" : ` data-k-list-current="${escapeAttribute(key)}"`
|
|
590
|
-
const shared = renderContext.listInitialMarkers && !renderContext.listTemplate
|
|
591
|
-
const condition = shared
|
|
601
|
+
const shared = renderContext.listInitialMarkers && !renderContext.listTemplate
|
|
602
|
+
const condition = shared
|
|
603
|
+
? ` data-k-list-condition${owner.descriptor.conditionHandlers ? ` data-k-list-condition-handler="${escapeAttribute(node.handler)}"` : ""}`
|
|
604
|
+
: ` data-k-list-condition='${escapeJsonAttribute(descriptor)}'`
|
|
592
605
|
const branches = shared ? "" : `<template data-k-list-true>${truthy}</template><template data-k-list-false>${falsy}</template>`
|
|
593
606
|
return `<template${condition}${initial}>${branches}</template>${current}<template data-k-list-condition-end></template>`
|
|
594
607
|
}
|
|
@@ -649,7 +662,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
649
662
|
if (rawName === "children" || rawName === "key") continue
|
|
650
663
|
if (rawName === "ref") {
|
|
651
664
|
if (!value?.[refMarker]) throw new Error("ref must be created by useRef(null)")
|
|
652
|
-
if (renderContext.listDepth) throw new Error("Refs
|
|
665
|
+
if (renderContext.listDepth && !value.row) throw new Error("Refs in keyed lists must be declared by the keyed row component")
|
|
653
666
|
attributes += ` data-k-ref="${value.id}"`
|
|
654
667
|
continue
|
|
655
668
|
}
|
|
@@ -681,7 +694,7 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
681
694
|
const native = template
|
|
682
695
|
attributes += ` data-k-native-${event}='${escapeJsonAttribute(native)}'`
|
|
683
696
|
renderContext.events.push({ event, native })
|
|
684
|
-
if (renderContext.listDepth && Object.values(template.scope).some(entry => entry?.type === "list-item")) listEvents.push([event, template])
|
|
697
|
+
if (renderContext.listDepth && Object.values(template.scope).some(entry => entry?.type === "list-item" || entry?.type === "list-index")) listEvents.push([event, template])
|
|
685
698
|
renderContext.hasNativeBehaviors = true
|
|
686
699
|
} else {
|
|
687
700
|
throw new Error(`${rawName} must reference a compilable event handler`)
|
|
@@ -757,9 +770,14 @@ async function renderList(node, namespace, selectValue) {
|
|
|
757
770
|
if (namespace) throw new Error(`Reactive keyed lists are not supported inside ${namespace}`)
|
|
758
771
|
const ownerRoot = node.ownerField ? renderContext.listRoot ?? renderContext.listRowRoot : undefined
|
|
759
772
|
const ownerTemplate = Boolean(node.ownerField && renderContext.listTemplate)
|
|
760
|
-
const
|
|
761
|
-
const
|
|
762
|
-
|
|
773
|
+
const rowList = node.ownerField ? nextRowList() : undefined
|
|
774
|
+
const id = rowList?.id ?? nextRenderId("l")
|
|
775
|
+
const descriptor = { id, state: node.items.id, key: node.keyField, keys: node.values.map((item, index) => node.keyField === null ? index : item[node.keyField]), ...(node.ownerField ? { ownerField: node.ownerField } : {}), ...(node.selector.length ? { selector: node.selector } : {}), ...(node.indexed ? { indexed: true } : {}), ...(!node.selector.length && node.keyField !== null && node.items[reducerStateMarker] ? { reducer: true } : {}) }
|
|
776
|
+
if (ownerTemplate) {
|
|
777
|
+
ownerRoot.descriptor.children ??= []
|
|
778
|
+
ownerRoot.descriptor.children.push({ id, field: node.ownerField, key: node.keyField, ...(node.selector.length ? { selector: node.selector } : {}) })
|
|
779
|
+
Object.assign(ownerRoot.descriptor, { mount: true, nested: true })
|
|
780
|
+
}
|
|
763
781
|
renderContext.listDepth++
|
|
764
782
|
const previousListRoot = renderContext.listRoot
|
|
765
783
|
const previousListRowRoot = renderContext.listRowRoot
|
|
@@ -768,23 +786,26 @@ async function renderList(node, namespace, selectValue) {
|
|
|
768
786
|
const previousListFields = renderContext.listFields
|
|
769
787
|
const previousListEffectOwners = renderContext.listEffectOwners
|
|
770
788
|
const previousListRowStates = renderContext.listRowStates
|
|
789
|
+
const previousListRowRefs = renderContext.listRowRefs
|
|
771
790
|
const previousListRowConditions = renderContext.listRowConditions
|
|
772
791
|
const previousListRowLists = renderContext.listRowLists
|
|
773
792
|
try {
|
|
774
793
|
renderContext.listTemplate = true
|
|
775
|
-
renderContext.listEffectOwners = []
|
|
776
|
-
renderContext.listRowStates = []
|
|
777
|
-
renderContext.
|
|
778
|
-
renderContext.
|
|
794
|
+
renderContext.listEffectOwners = rowList?.effectOwners ?? []
|
|
795
|
+
renderContext.listRowStates = rowList?.rowStates ?? []
|
|
796
|
+
renderContext.listRowRefs = rowList?.rowRefs ?? []
|
|
797
|
+
renderContext.listRowConditions = rowList?.rowConditions ?? []
|
|
798
|
+
renderContext.listRowLists = rowList?.rowLists ?? []
|
|
779
799
|
renderContext.listFields = new Set([node.keyField])
|
|
780
|
-
const templateRoot = { id, state: node.items.id, descriptor, template: true, effects: [], item: {}, rowIndexes: { s: 0, c: 0, l: 0 } }
|
|
800
|
+
const templateRoot = { id, state: node.items.id, descriptor, template: true, effects: [], item: {}, path: ownerRoot?.path ?? [], rowIndexes: { s: 0, r: 0, c: 0, l: 0 } }
|
|
781
801
|
renderContext.listRoot = templateRoot
|
|
782
802
|
renderContext.listRowRoot = templateRoot
|
|
783
|
-
const template = await renderNode(node.render({}), namespace, selectValue)
|
|
803
|
+
const template = await renderNode(node.render({}, 0), namespace, selectValue)
|
|
784
804
|
if (template.includes("data-k-native-") || template.includes("data-k-effects=") || template.includes("data-k-list=")) descriptor.mount = true
|
|
785
805
|
if (template.includes("data-k-list=")) descriptor.nested = true
|
|
786
806
|
if (template.includes("data-k-effects=")) descriptor.effects = true
|
|
787
807
|
if (templateRoot.conditions) descriptor.conditions = true
|
|
808
|
+
if (templateRoot.nestedConditions) descriptor.conditionHandlers = true
|
|
788
809
|
if (template.includes("data-k-list-text-end")) descriptor.textRanges = true
|
|
789
810
|
if (template.includes("data-k-list-attrs")) descriptor.attributes = true
|
|
790
811
|
if (template.includes("data-k-list-events")) descriptor.events = true
|
|
@@ -795,17 +816,25 @@ async function renderList(node, namespace, selectValue) {
|
|
|
795
816
|
if (renderContext.listRowConditions.length) descriptor.rowConditions = renderContext.listRowConditions.map(({ id }) => id)
|
|
796
817
|
descriptor.mount = true
|
|
797
818
|
}
|
|
798
|
-
|
|
819
|
+
if (renderContext.listRowRefs.length) {
|
|
820
|
+
descriptor.rowRefs = renderContext.listRowRefs.map(({ id }) => id)
|
|
821
|
+
descriptor.mount = true
|
|
822
|
+
}
|
|
823
|
+
const seed = node.ownerField || node.selector.length || node.keyField === null ? undefined : listSeed(node.values, renderContext.listFields)
|
|
799
824
|
if (seed) descriptor.seed = seed
|
|
800
825
|
let current = ""
|
|
801
826
|
renderContext.listTemplate = false
|
|
802
827
|
renderContext.listInitialMarkers = Boolean(descriptor.conditions)
|
|
803
|
-
for (const item of node.values) {
|
|
804
|
-
|
|
828
|
+
for (const [index, item] of node.values.entries()) {
|
|
829
|
+
const key = node.keyField === null ? index : item[node.keyField]
|
|
830
|
+
renderContext.listRoot = { id, state: node.items.id, descriptor, key, template: false, effects: [], item, path: [...(ownerRoot?.path ?? []), `${id}=${typeof key}:${key}`], rowIndexes: { s: 0, r: 0, c: 0, l: 0 } }
|
|
805
831
|
renderContext.listRowRoot = renderContext.listRoot
|
|
806
|
-
current += await renderNode(node.render(item), namespace, selectValue)
|
|
832
|
+
current += await renderNode(node.render(item, index), namespace, selectValue)
|
|
833
|
+
}
|
|
834
|
+
if (!node.ownerField || ownerTemplate && !rowList.planned) {
|
|
835
|
+
renderContext.lists.push(descriptor)
|
|
836
|
+
if (rowList) rowList.planned = true
|
|
807
837
|
}
|
|
808
|
-
if (!node.ownerField || ownerTemplate) renderContext.lists.push(descriptor)
|
|
809
838
|
renderContext.hasBehaviors = true
|
|
810
839
|
renderContext.hasLists = true
|
|
811
840
|
const prototype = node.ownerField && !ownerTemplate ? "" : template
|
|
@@ -818,23 +847,24 @@ async function renderList(node, namespace, selectValue) {
|
|
|
818
847
|
renderContext.listFields = previousListFields
|
|
819
848
|
renderContext.listEffectOwners = previousListEffectOwners
|
|
820
849
|
renderContext.listRowStates = previousListRowStates
|
|
850
|
+
renderContext.listRowRefs = previousListRowRefs
|
|
821
851
|
renderContext.listRowConditions = previousListRowConditions
|
|
822
852
|
renderContext.listRowLists = previousListRowLists
|
|
823
853
|
renderContext.listDepth--
|
|
824
854
|
}
|
|
825
855
|
}
|
|
826
856
|
|
|
827
|
-
function
|
|
857
|
+
function nextRowList() {
|
|
828
858
|
const root = renderContext.listRoot ?? renderContext.listRowRoot
|
|
829
859
|
const index = root.rowIndexes.l++
|
|
830
860
|
if (renderContext.listTemplate) {
|
|
831
|
-
const
|
|
832
|
-
renderContext.listRowLists[index] =
|
|
833
|
-
return
|
|
861
|
+
const entry = renderContext.listRowLists[index] ?? { id: nextRenderId("l"), rowLists: [], rowStates: [], rowRefs: [], rowConditions: [], effectOwners: [], planned: false }
|
|
862
|
+
renderContext.listRowLists[index] = entry
|
|
863
|
+
return entry
|
|
834
864
|
}
|
|
835
865
|
const entry = renderContext.listRowLists[index]
|
|
836
866
|
if (!entry) throw new Error("Nested keyed lists must have the same order for every parent item")
|
|
837
|
-
return entry
|
|
867
|
+
return entry
|
|
838
868
|
}
|
|
839
869
|
|
|
840
870
|
function nextRenderId(kind) {
|
|
@@ -846,19 +876,21 @@ function nextRenderId(kind) {
|
|
|
846
876
|
function nextRowRenderId(kind, initialValue) {
|
|
847
877
|
const root = renderContext.listRoot ?? renderContext.listRowRoot
|
|
848
878
|
const index = root.rowIndexes[kind]++
|
|
849
|
-
const entries = kind === "s" ? renderContext.listRowStates : renderContext.listRowConditions
|
|
879
|
+
const entries = kind === "s" ? renderContext.listRowStates : kind === "r" ? renderContext.listRowRefs : renderContext.listRowConditions
|
|
850
880
|
if (renderContext.listTemplate) {
|
|
881
|
+
const entry = entries[index]
|
|
882
|
+
if (entry) return entry.id
|
|
851
883
|
const id = `${nextRenderId(kind)}:$k`
|
|
852
884
|
entries[index] = kind === "s" ? { id, initialValue } : { id }
|
|
853
885
|
return id
|
|
854
886
|
}
|
|
855
887
|
const entry = entries[index]
|
|
856
|
-
if (!entry) throw new Error(`Keyed row ${kind === "s" ? "state hooks" : "conditionals"} must have the same order for every item`)
|
|
857
|
-
return rowRenderId(entry.id, root.key)
|
|
888
|
+
if (!entry) throw new Error(`Keyed row ${kind === "s" ? "state hooks" : kind === "r" ? "ref hooks" : "conditionals"} must have the same order for every item`)
|
|
889
|
+
return rowRenderId(entry.id, root.descriptor.ownerField ? root.path : [`${typeof root.key}:${root.key}`])
|
|
858
890
|
}
|
|
859
891
|
|
|
860
|
-
function rowRenderId(id,
|
|
861
|
-
return id.replace("$k", encodeURIComponent(
|
|
892
|
+
function rowRenderId(id, path) {
|
|
893
|
+
return id.replace("$k", encodeURIComponent(path.join("/")))
|
|
862
894
|
}
|
|
863
895
|
|
|
864
896
|
function optionValue(props) {
|