( seen: Map<string, FocusContainerKind>, instanceId: InstanceId, vnode: VNode, )
| 77 | } |
| 78 | |
| 79 | export function ensureFocusContainerId( |
| 80 | seen: Map<string, FocusContainerKind>, |
| 81 | instanceId: InstanceId, |
| 82 | vnode: VNode, |
| 83 | ): CommitFatal | null { |
| 84 | if (!isFocusContainerVNode(vnode)) return null; |
| 85 | |
| 86 | const id = (vnode as { props: { id?: unknown } }).props.id; |
| 87 | if (typeof id !== "string" || id.length === 0) { |
| 88 | return { |
| 89 | code: "ZRUI_INVALID_PROPS", |
| 90 | detail: `focus container missing required id (kind=${vnode.kind}, instanceId=${String(instanceId)})`, |
| 91 | }; |
| 92 | } |
| 93 | if (id.trim().length === 0) { |
| 94 | return { |
| 95 | code: "ZRUI_INVALID_PROPS", |
| 96 | detail: `focus container id must contain non-whitespace characters (kind=${vnode.kind}, instanceId=${String(instanceId)})`, |
| 97 | }; |
| 98 | } |
| 99 | |
| 100 | const existing = seen.get(id); |
| 101 | if (existing !== undefined) { |
| 102 | return { |
| 103 | code: "ZRUI_DUPLICATE_ID", |
| 104 | detail: `Duplicate focus container id "${id}". First: <${existing}>, second: <${vnode.kind}>. Hint: focusZone, focusTrap, and modal ids must be unique across the tree.`, |
| 105 | }; |
| 106 | } |
| 107 | |
| 108 | seen.set(id, vnode.kind); |
| 109 | return null; |
| 110 | } |
| 111 | |
| 112 | export function isVNode(v: unknown): v is VNode { |
| 113 | return typeof v === "object" && v !== null && "kind" in v; |
no test coverage detected