( seen: Map<string, string>, instanceId: InstanceId, vnode: VNode, )
| 35 | } |
| 36 | |
| 37 | export function ensureInteractiveId( |
| 38 | seen: Map<string, string>, |
| 39 | instanceId: InstanceId, |
| 40 | vnode: VNode, |
| 41 | ): CommitFatal | null { |
| 42 | if (!isInteractiveVNode(vnode)) return null; |
| 43 | |
| 44 | const id = (vnode as { props: { id?: unknown } }).props.id; |
| 45 | if (typeof id !== "string" || id.length === 0) { |
| 46 | if (!kindRequiresId(vnode.kind)) return null; |
| 47 | return { |
| 48 | code: "ZRUI_INVALID_PROPS", |
| 49 | detail: `interactive node missing required id (kind=${vnode.kind}, instanceId=${String(instanceId)})`, |
| 50 | }; |
| 51 | } |
| 52 | if (id.trim().length === 0) { |
| 53 | return { |
| 54 | code: "ZRUI_INVALID_PROPS", |
| 55 | detail: `interactive node id must contain non-whitespace characters (kind=${vnode.kind}, instanceId=${String(instanceId)})`, |
| 56 | }; |
| 57 | } |
| 58 | |
| 59 | const existing = seen.get(id); |
| 60 | if (existing !== undefined) { |
| 61 | return { |
| 62 | code: "ZRUI_DUPLICATE_ID", |
| 63 | detail: `Duplicate interactive widget id "${id}". First: <${existing}>, second: <${vnode.kind}>. Hint: Use ctx.id() inside defineWidget to generate unique IDs for list items.`, |
| 64 | }; |
| 65 | } |
| 66 | if (DEV_MODE && id.length > MAX_INTERACTIVE_ID_LENGTH) { |
| 67 | warnDev( |
| 68 | `[rezi][commit] interactive widget id exceeds ${String(MAX_INTERACTIVE_ID_LENGTH)} chars (kind=${vnode.kind}, id length=${String(id.length)}). Consider using shorter IDs.`, |
| 69 | ); |
| 70 | } |
| 71 | seen.set(id, vnode.kind); |
| 72 | return null; |
| 73 | } |
| 74 | |
| 75 | export function isFocusContainerVNode(vnode: VNode): vnode is VNode & { kind: FocusContainerKind } { |
| 76 | return vnode.kind === "focusZone" || vnode.kind === "focusTrap" || vnode.kind === "modal"; |
no test coverage detected