@radix-ng/primitives 1.0.9 → 1.0.10

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.
Files changed (37) hide show
  1. package/fesm2022/radix-ng-primitives-autocomplete.mjs +11 -3
  2. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-combobox.mjs +11 -3
  4. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-core.mjs +9 -6
  6. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-date-field.mjs +18 -8
  8. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  9. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +9 -0
  10. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -1
  11. package/fesm2022/radix-ng-primitives-focus-scope.mjs +241 -95
  12. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-menu.mjs +97 -9
  14. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +17 -3
  16. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-popper.mjs +148 -23
  18. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-portal.mjs +16 -5
  20. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  21. package/fesm2022/radix-ng-primitives-select.mjs +14 -6
  22. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-time-field.mjs +12 -4
  24. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  25. package/package.json +1 -5
  26. package/types/radix-ng-primitives-core.d.ts +15 -12
  27. package/types/radix-ng-primitives-date-field.d.ts +14 -4
  28. package/types/radix-ng-primitives-floating-focus-manager.d.ts +7 -0
  29. package/types/radix-ng-primitives-focus-scope.d.ts +61 -44
  30. package/types/radix-ng-primitives-menu.d.ts +14 -3
  31. package/types/radix-ng-primitives-popper.d.ts +136 -43
  32. package/types/radix-ng-primitives-portal.d.ts +18 -8
  33. package/types/radix-ng-primitives-time-field.d.ts +12 -4
  34. package/fesm2022/radix-ng-primitives-focus-guards.mjs +0 -53
  35. package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +0 -1
  36. package/focus-guards/README.md +0 -1
  37. package/types/radix-ng-primitives-focus-guards.d.ts +0 -15
@@ -1 +1 @@
1
- {"version":3,"file":"radix-ng-primitives-focus-scope.mjs","sources":["../../../packages/primitives/focus-scope/src/utils.ts","../../../packages/primitives/focus-scope/src/focus-guards.ts","../../../packages/primitives/focus-scope/src/focus-scope.config.ts","../../../packages/primitives/focus-scope/src/stack.ts","../../../packages/primitives/focus-scope/src/focus-scope.ts","../../../packages/primitives/focus-scope/radix-ng-primitives-focus-scope.ts"],"sourcesContent":["import { getActiveElement } from '@radix-ng/primitives/core';\n\nexport const AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount';\nexport const AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount';\nexport const EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype FocusableTarget = HTMLElement | { focus: () => void };\n\n/**\n * The real target of a (possibly retargeted) event, piercing shadow boundaries via `composedPath()`.\n * Falls back to `event.target` when `composedPath` is unavailable.\n */\nexport function getEventTarget(event: Event): EventTarget | null {\n return event.composedPath?.()[0] ?? event.target;\n}\n\n/**\n * Shadow-DOM-aware containment: whether `node` is `container` or lives inside it, crossing shadow roots\n * via their `host` (unlike `Node.contains`, which stops at a shadow boundary).\n */\nexport function composedContains(container: Node, node: Node | null): boolean {\n let current: Node | null = node;\n while (current) {\n if (current === container) {\n return true;\n }\n current = current instanceof ShadowRoot ? current.host : current.parentNode;\n }\n return false;\n}\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n */\nexport function focusFirst(candidates: HTMLElement[], { select = false } = {}) {\n const previouslyFocusedElement = getActiveElement();\n for (const candidate of candidates) {\n focus(candidate, { select });\n if (getActiveElement() !== previouslyFocusedElement) return true;\n }\n\n return;\n}\n\n/**\n * Returns a list of potential tabbable candidates.\n *\n * NOTE: This is only a close approximation. For example it doesn't take into account cases like when\n * elements are not visible. This cannot be worked out easily by just reading a property, but rather\n * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker\n * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1\n */\nexport function getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = container.ownerDocument.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: any) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden';\n if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;\n // `.tabIndex` is not the same as the `tabindex` attribute. It works on the\n // runtime's understanding of tabbability, so this automatically accounts\n // for any kind of element that could be tabbed to.\n return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n }\n });\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement);\n // we do not take into account the order of nodes with positive `tabIndex` as it\n // hinders accessibility to have tab order different from visual order.\n return nodes;\n}\n\nexport function isHidden(node: HTMLElement, { upTo }: { upTo?: HTMLElement }) {\n const view = node.ownerDocument.defaultView;\n if (!view) {\n return false; // no view (detached / SSR) — cannot resolve computed styles, treat as visible\n }\n if (view.getComputedStyle(node).visibility === 'hidden') return true;\n while (node) {\n // we stop at `upTo` (excluding it)\n if (upTo !== undefined && node === upTo) return false;\n if (view.getComputedStyle(node).display === 'none') return true;\n node = node.parentElement as HTMLElement;\n }\n return false;\n}\n\n/**\n * Returns the first visible element in a list.\n * NOTE: Only checks visibility up to the `container`.\n */\nexport function findVisible(elements: HTMLElement[], container: HTMLElement): HTMLElement | undefined {\n for (const element of elements) {\n // we stop checking if it's hidden at the `container` level (excluding)\n if (!isHidden(element, { upTo: container })) return element;\n }\n return undefined;\n}\n\n/**\n * Returns the first and last tabbable elements inside a container.\n */\nexport function getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container);\n const first = findVisible(candidates, container);\n const last = findVisible(candidates.reverse(), container);\n return [first, last] as const;\n}\n\n/** Visible tabbable elements of `root` in document order (the basis for tab-order navigation). */\nfunction visibleTabbablesIn(root: HTMLElement): HTMLElement[] {\n return getTabbableCandidates(root).filter((el) => !isHidden(el, { upTo: root }));\n}\n\n/** The tabbable one step (`dir`) from the document's active element, within `container`. */\nfunction getTabbableIn(container: HTMLElement, dir: 1 | -1): HTMLElement | undefined {\n const list = visibleTabbablesIn(container);\n if (list.length === 0) {\n return undefined;\n }\n const active = getActiveElement(container.ownerDocument) as HTMLElement | null;\n const index = active ? list.indexOf(active) : -1;\n const nextIndex = index === -1 ? (dir === 1 ? 0 : list.length - 1) : index + dir;\n return list[nextIndex];\n}\n\n/**\n * The next tabbable in the document after the current focus (Base UI `getNextTabbable`) — used by the\n * portal-focus bridge's trailing guard to step focus past the popup. Falls back to `reference`.\n */\nexport function getNextTabbable(reference: Element | null): HTMLElement | null {\n const body = (reference?.ownerDocument ?? document).body;\n return getTabbableIn(body, 1) ?? (reference as HTMLElement | null);\n}\n\n/** The previous tabbable in the document before the current focus (Base UI `getPreviousTabbable`). */\nexport function getPreviousTabbable(reference: Element | null): HTMLElement | null {\n const body = (reference?.ownerDocument ?? document).body;\n return getTabbableIn(body, -1) ?? (reference as HTMLElement | null);\n}\n\n/** The tabbable `dir` steps from `reference` in the document, wrapping around. */\nfunction getTabbableNearElement(reference: Element | null, dir: 1 | -1): HTMLElement | null {\n if (!reference) {\n return null;\n }\n const list = visibleTabbablesIn(reference.ownerDocument.body);\n const index = list.indexOf(reference as HTMLElement);\n if (list.length === 0 || index === -1) {\n return null;\n }\n return list[(index + dir + list.length) % list.length];\n}\n\n/** The tabbable immediately after `reference` in the document, wrapping (Base UI `getTabbableAfterElement`). */\nexport function getTabbableAfterElement(reference: Element | null): HTMLElement | null {\n return getTabbableNearElement(reference, 1);\n}\n\n/** The tabbable immediately before `reference` in the document, wrapping (Base UI `getTabbableBeforeElement`). */\nexport function getTabbableBeforeElement(reference: Element | null): HTMLElement | null {\n return getTabbableNearElement(reference, -1);\n}\n\nexport function isSelectableInput(element: any): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element;\n}\n\nexport function focus(element?: FocusableTarget | null, { select = false } = {}) {\n // only focus if that element is focusable\n if (element && element.focus) {\n const previouslyFocusedElement = getActiveElement();\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true });\n // only select if its not the same element, it supports selection and we need to select\n if (element !== previouslyFocusedElement && isSelectableInput(element) && select) {\n element.select();\n }\n }\n}\n","import { effect } from '@angular/core';\nimport { composedContains, getTabbableCandidates } from './utils';\n\n/** Marks the leading / trailing focus-guard spans (Base UI `data-base-ui-focus-guard`). */\nexport const FOCUS_GUARD_ATTR = 'data-rdx-focus-guard';\n\n/** Saved-tabindex marker used by {@link disableFocusInside} / {@link enableFocusInside}. */\nconst SAVED_TABINDEX_ATTR = 'data-rdx-tabindex';\n\n/** Visually-hidden, off-flow style for a focus guard / `aria-owns` anchor (Base UI `visuallyHidden`). */\nexport const FOCUS_GUARD_STYLE: Partial<CSSStyleDeclaration> = {\n position: 'fixed',\n top: '0',\n left: '0',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clipPath: 'inset(50%)',\n whiteSpace: 'nowrap',\n border: '0'\n};\n\n/**\n * Creates a visually-hidden, **tabbable** focus-guard `<span>` — the Angular counterpart of Base UI's\n * `FocusGuard`. The portal-focus bridge places one before and one after the portal content so a Tab into\n * (or out of) the portal lands on a guard, which then redirects focus to the right boundary.\n */\nexport function createFocusGuard(ownerDocument: Document): HTMLSpanElement {\n const guard = ownerDocument.createElement('span');\n guard.setAttribute('tabindex', '0');\n guard.setAttribute('aria-hidden', 'true');\n guard.setAttribute(FOCUS_GUARD_ATTR, '');\n Object.assign(guard.style, FOCUS_GUARD_STYLE);\n return guard;\n}\n\n/**\n * Creates a visually-hidden `<span aria-owns=\"…\">` that links the portal node into the trigger's tab /\n * AT order (Base UI's single `aria-owns` anchor). The manager places it next to the trigger.\n */\nexport function createAriaOwnsAnchor(ownerDocument: Document, portalId: string): HTMLSpanElement {\n const anchor = ownerDocument.createElement('span');\n anchor.setAttribute('aria-owns', portalId);\n Object.assign(anchor.style, FOCUS_GUARD_STYLE);\n return anchor;\n}\n\n/**\n * Makes every tabbable descendant of `container` **non-tabbable** (`tabindex=\"-1\"`), saving each one's\n * original tabindex so {@link enableFocusInside} can restore it. Base UI `disableFocusInside`: a\n * non-modal portal keeps its content untabbable until focus is actually inside it, so a Tab from the\n * trigger steps onto the guard instead of jumping into the content.\n */\nexport function disableFocusInside(container: HTMLElement): void {\n for (const element of getTabbableCandidates(container)) {\n element.setAttribute(SAVED_TABINDEX_ATTR, element.getAttribute('tabindex') ?? '');\n element.setAttribute('tabindex', '-1');\n }\n}\n\n/** Restores the tabbability that {@link disableFocusInside} suspended. Base UI `enableFocusInside`. */\nexport function enableFocusInside(container: HTMLElement): void {\n container.querySelectorAll<HTMLElement>(`[${SAVED_TABINDEX_ATTR}]`).forEach((element) => {\n const original = element.getAttribute(SAVED_TABINDEX_ATTR);\n element.removeAttribute(SAVED_TABINDEX_ATTR);\n if (original) {\n element.setAttribute('tabindex', original);\n } else {\n element.removeAttribute('tabindex');\n }\n });\n}\n\n/**\n * Whether a focus event crossed the `container` boundary — its `relatedTarget` (the other side of the\n * focus move) is `null` or outside `container` (Base UI `isOutsideEvent`). Shadow-DOM-aware via\n * {@link composedContains}.\n */\nexport function isOutsideEvent(event: FocusEvent, container: Element): boolean {\n const relatedTarget = event.relatedTarget as Node | null;\n return !relatedTarget || !composedContains(container, relatedTarget);\n}\n\n/**\n * The portal-focus bridge's **tabbability toggle** (Base UI `FloatingPortal` capture-phase `onFocus`).\n * While `portalNode` is mounted (and `enabled`), it makes the portal content tabbable **only when focus\n * is inside it**: focus entering from outside re-enables tabbability, focus leaving to outside disables\n * it again. Listens on the **capture** phase so it settles before the focus manager's guards react.\n *\n * Must be called in an injection context. The initial disable-on-mount and the guard-span placement are\n * the manager's responsibility (Phase 1b); this owns only the dynamic in/out toggle.\n */\nexport function useFocusGuardsTabbability(\n portalNode: () => HTMLElement | null,\n options: { enabled?: () => boolean } = {}\n): void {\n const enabled = options.enabled ?? (() => true);\n let focusInsideDisabled = false;\n\n effect((onCleanup) => {\n const node = portalNode();\n if (!node || !enabled()) {\n return;\n }\n const ownerDocument = node.ownerDocument;\n\n const onFocus = (event: FocusEvent): void => {\n // Only react to focus actually crossing the portal boundary.\n if (!event.relatedTarget || !isOutsideEvent(event, node)) {\n return;\n }\n if (event.type === 'focusin') {\n if (focusInsideDisabled) {\n enableFocusInside(node);\n focusInsideDisabled = false;\n }\n } else {\n disableFocusInside(node);\n focusInsideDisabled = true;\n }\n };\n\n if (isOutsideEvent(new FocusEvent('focusin', { relatedTarget: ownerDocument.activeElement }), node)) {\n disableFocusInside(node);\n focusInsideDisabled = true;\n }\n\n node.addEventListener('focusin', onFocus, true);\n node.addEventListener('focusout', onFocus, true);\n onCleanup(() => {\n node.removeEventListener('focusin', onFocus, true);\n node.removeEventListener('focusout', onFocus, true);\n if (focusInsideDisabled) {\n enableFocusInside(node);\n focusInsideDisabled = false;\n }\n });\n });\n}\n","import { InjectionToken, Provider, Signal, signal } from '@angular/core';\n\nexport type RdxFocusScopeConfig = {\n trapped: Signal<boolean>;\n\n /**\n * Optional return-focus policy override (ADR 0017 `returnFocus`), resolved by an enclosing\n * {@link RdxFloatingFocusManager} at **unmount time**. The focus scope owns the *timing* (its queued\n * post-unmount focus); this lets the manager own the *target*:\n * - `false` → do **not** return focus;\n * - an `HTMLElement` → return focus there **explicitly** (bypasses the moved-focus guard, matching\n * Base UI's `hasExplicitReturnFocus`);\n * - `undefined` → default behavior (return to the element focused before mount, with the guard).\n */\n returnFocus?: () => HTMLElement | false | undefined;\n};\n\nexport const RdxFocusScopeConfigToken = new InjectionToken<RdxFocusScopeConfig>('RdxFocusScopeConfig', {\n factory: () => ({\n trapped: signal(false)\n })\n});\n\nexport function provideRdxFocusScopeConfig(factory: () => RdxFocusScopeConfig): Provider {\n return { provide: RdxFocusScopeConfigToken, useFactory: factory };\n}\n","import { signal, WritableSignal } from '@angular/core';\n\nexport interface FocusScopeAPI {\n paused: WritableSignal<boolean>;\n pause(): void;\n resume(): void;\n}\n\n/**\n * The active-scope stack pauses/resumes scopes, so it **is** cross-document coordination state — keyed\n * per owner `Document` (a `WeakMap`) rather than process-global (ADR 0017 Phase 1a): opening a scope in\n * document B must not pause document A's scope.\n */\nconst stacksByDocument = new WeakMap<Document, WritableSignal<FocusScopeAPI[]>>();\n\nfunction getFocusStackState(document: Document): WritableSignal<FocusScopeAPI[]> {\n let state = stacksByDocument.get(document);\n if (!state) {\n state = signal<FocusScopeAPI[]>([]);\n stacksByDocument.set(document, state);\n }\n return state;\n}\n\nexport function createFocusScopesStack(document: Document) {\n /** A stack of focus scopes for this document, with the active one at the top */\n const stack = getFocusStackState(document);\n\n return {\n add(focusScope: FocusScopeAPI) {\n const current = stack();\n const active = current[0];\n if (focusScope !== active) {\n active?.pause();\n }\n const updated = arrayRemove(current, focusScope);\n updated.unshift(focusScope);\n stack.set(updated);\n },\n\n remove(focusScope: FocusScopeAPI) {\n const current = stack();\n const updated = arrayRemove(current, focusScope);\n stack.set(updated);\n // после удаления «возобновляем» новый верхний\n stack()[0]?.resume();\n }\n };\n}\n\nexport function arrayRemove<T>(array: T[], item: T): T[] {\n const copy = [...array];\n const idx = copy.indexOf(item);\n if (idx !== -1) {\n copy.splice(idx, 1);\n }\n return copy;\n}\n\nexport function removeLinks(items: HTMLElement[]): HTMLElement[] {\n return items.filter((el) => el.tagName !== 'A');\n}\n","import {\n afterNextRender,\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n Injector,\n input,\n output,\n Signal,\n signal\n} from '@angular/core';\nimport { BooleanInput, createContext, getActiveElement } from '@radix-ng/primitives/core';\nimport { RdxFocusScopeConfigToken } from './focus-scope.config';\nimport { createFocusScopesStack, FocusScopeAPI, removeLinks } from './stack';\nimport {\n AUTOFOCUS_ON_MOUNT,\n AUTOFOCUS_ON_UNMOUNT,\n composedContains,\n EVENT_OPTIONS,\n focus,\n focusFirst,\n getEventTarget,\n getTabbableCandidates,\n getTabbableEdges\n} from './utils';\n\nexport interface FocusScopeContext {\n loop?: Signal<boolean>;\n\n trapped?: Signal<boolean>;\n}\n\nexport const [injectFocusScopeContext, provideFocusScopeContext] = createContext<FocusScopeContext>(\n 'FocusScope Context',\n 'utils/focus-scope'\n);\n\nconst rootContext = (): FocusScopeContext => {\n const context = inject(RdxFocusScope);\n\n return {\n loop: context.loop,\n trapped: context.isTrapped\n };\n};\n\n/**\n * @group Components\n */\n@Directive({\n selector: '[rdxFocusScope]',\n providers: [provideFocusScopeContext(rootContext)],\n host: {\n tabindex: '-1',\n '(keydown)': 'handleKeyDown($event)'\n }\n})\nexport class RdxFocusScope {\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly config = inject(RdxFocusScopeConfigToken);\n\n /** The host's owner `Document` — all focus listeners / reads are scoped here, never global `document`. */\n private readonly ownerDocument = this.elementRef.nativeElement.ownerDocument ?? document;\n\n /**\n * When `true`, tabbing from last item will focus first tabbable\n * and shift+tab from first item will focus last tababble.\n *\n * @group Props\n * @defaultValue false\n */\n readonly loop = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * When `true`, focus cannot escape the focus scope via keyboard,\n * pointer, or a programmatic focus.\n *\n * @group Props\n * @defaultValue false\n */\n readonly trapped = input<boolean | undefined, BooleanInput | undefined>(undefined, {\n transform: (value) => (value === undefined ? undefined : booleanAttribute(value))\n });\n\n readonly isTrapped = computed(() => this.trapped() ?? this.config.trapped());\n\n /**\n * Event handler called when auto-focusing on mount.\n * Can be prevented.\n *\n * @group Emits\n */\n readonly mountAutoFocus = output<Event>();\n\n /**\n * Event handler called when auto-focusing on unmount.\n * Can be prevented.\n *\n * @group Emits\n */\n readonly unmountAutoFocus = output<Event>();\n\n readonly lastFocusedElement = signal<HTMLElement | null>(null);\n\n private readonly focusScopesStack = createFocusScopesStack(this.ownerDocument);\n\n readonly focusScope: FocusScopeAPI = {\n paused: signal(false),\n pause: () => this.focusScope.paused.set(true),\n resume: () => this.focusScope.paused.set(false)\n };\n\n private alive = true;\n\n constructor() {\n this.destroyRef.onDestroy(() => {\n this.alive = false;\n });\n\n afterNextRender(() => {\n effect(\n (onCleanup) => {\n const container = this.elementRef.nativeElement;\n\n if (this.isTrapped()) {\n const handleFocusIn = (event: FocusEvent) => {\n if (this.focusScope.paused() || !container) {\n return;\n }\n\n const target = getEventTarget(event) as HTMLElement | null;\n if (composedContains(container, target)) {\n this.lastFocusedElement.set(target);\n } else {\n focus(this.lastFocusedElement(), { select: true });\n }\n };\n\n const handleFocusOut = (event: FocusEvent) => {\n if (this.focusScope.paused() || !container) {\n return;\n }\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // A `focusout` event with a `null` `relatedTarget` will happen in at least two cases:\n //\n // 1. When the user switches app/tabs/windows/the browser itself loses focus.\n // 2. In Google Chrome, when the focused element is removed from the DOM.\n //\n // We let the browser do its thing here because:\n //\n // 1. The browser already keeps a memory of what's focused for when the page gets refocused.\n // 2. In Google Chrome, if we try to focus the deleted focused element (as per below), it\n // throws the CPU to 100%, so we avoid doing anything for this reason here too.\n if (relatedTarget === null) return;\n\n // If the focus has moved to an actual legitimate element (`relatedTarget !== null`)\n // that is outside the container, we move focus to the last valid focused element inside.\n if (!composedContains(container, relatedTarget)) {\n focus(this.lastFocusedElement(), { select: true });\n }\n };\n\n const handleMutations = () => {\n const isLastFocusedElementExist = composedContains(container, this.lastFocusedElement());\n\n if (!isLastFocusedElementExist) {\n focus(container);\n }\n };\n\n const mutationObserver = new MutationObserver(handleMutations);\n if (container) {\n mutationObserver.observe(container, { childList: true, subtree: true });\n }\n\n this.ownerDocument.addEventListener('focusin', handleFocusIn);\n this.ownerDocument.addEventListener('focusout', handleFocusOut);\n\n onCleanup(() => {\n this.ownerDocument.removeEventListener('focusin', handleFocusIn);\n this.ownerDocument.removeEventListener('focusout', handleFocusOut);\n mutationObserver.disconnect();\n });\n }\n },\n { injector: this.injector }\n );\n\n effect(\n async (onCleanup) => {\n const container = this.elementRef.nativeElement;\n\n await Promise.resolve();\n if (!container || !this.alive) {\n return;\n }\n\n this.focusScopesStack.add(this.focusScope);\n\n const previouslyFocusedElement = getActiveElement(this.ownerDocument) as HTMLElement | null;\n const hasFocusedCandidate = composedContains(container, previouslyFocusedElement);\n const mountEventHandler = (ev: Event) => {\n if (this.alive) this.mountAutoFocus.emit(ev);\n };\n\n if (!hasFocusedCandidate) {\n const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_MOUNT, mountEventHandler);\n container.dispatchEvent(mountEvent);\n\n if (!mountEvent.defaultPrevented) {\n focusFirst(removeLinks(getTabbableCandidates(container)), {\n select: true\n });\n if (getActiveElement(this.ownerDocument) === previouslyFocusedElement) focus(container);\n }\n }\n\n const unmountEventHandler = (ev: Event) => {\n if (this.alive) this.unmountAutoFocus.emit(ev);\n };\n container.addEventListener(AUTOFOCUS_ON_UNMOUNT, unmountEventHandler);\n\n onCleanup(() => {\n container.removeEventListener(AUTOFOCUS_ON_MOUNT, mountEventHandler);\n\n const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);\n container.dispatchEvent(unmountEvent);\n\n // Queue the return-focus on the owner window's animation frame (not `setTimeout`),\n // so it runs after the unmounting paint settles (ADR 0017 Phase 1a queued focus).\n const view = this.ownerDocument.defaultView ?? globalThis;\n view.requestAnimationFrame(() => {\n // An enclosing focus manager can override the return target (ADR 0017\n // `returnFocus`): `false` suppresses it, an element returns there explicitly\n // (bypassing the moved-focus guard), `undefined` keeps the default behavior.\n const override = this.config.returnFocus?.();\n if (override !== false && !unmountEvent.defaultPrevented) {\n if (override) {\n focus(override, { select: true });\n } else if (!this.shouldPreserveMovedFocus()) {\n focus(previouslyFocusedElement ?? this.ownerDocument.body, { select: true });\n }\n }\n\n // we need to remove the listener after we `dispatchEvent`\n container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, unmountEventHandler);\n\n this.focusScopesStack.remove(this.focusScope);\n });\n });\n },\n { injector: this.injector }\n );\n });\n }\n\n /**\n * Whether the interaction that unmounted this scope already moved focus to a legitimate element\n * **outside** it — e.g. an outside press onto an interactive control in a non-modal layer (ADR 0017\n * §2, finding #3). Returning focus to the previously-focused element would then *steal* it back from\n * what the user just acted on. Focus that fell to `<body>` / `null` (a backdrop press, Escape, or the\n * focused element being removed) is **not** \"moved\" — return focus normally so keyboard users land\n * back on the trigger. The page never scroll-jumps either way: {@link focus} uses `preventScroll`.\n */\n private shouldPreserveMovedFocus(): boolean {\n const active = getActiveElement(this.ownerDocument) as HTMLElement | null;\n return (\n !!active && active !== this.ownerDocument.body && !composedContains(this.elementRef.nativeElement, active)\n );\n }\n\n handleKeyDown(event: KeyboardEvent) {\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n\n const focusedElement = getActiveElement(this.ownerDocument) as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) event.preventDefault();\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n if (this.loop()) {\n focus(first, { select: true });\n }\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n if (this.loop()) {\n focus(last, { select: true });\n }\n }\n }\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"radix-ng-primitives-focus-scope.mjs","sources":["../../../packages/primitives/focus-scope/src/tabbable.ts","../../../packages/primitives/focus-scope/src/utils.ts","../../../packages/primitives/focus-scope/src/focus-guards.ts","../../../packages/primitives/focus-scope/src/focus-scope.config.ts","../../../packages/primitives/focus-scope/src/stack.ts","../../../packages/primitives/focus-scope/src/focus-scope.ts","../../../packages/primitives/focus-scope/radix-ng-primitives-focus-scope.ts"],"sourcesContent":["import { getActiveElement } from '@radix-ng/primitives/core';\n\n/**\n * Tabbable / focusable detection ported from Base UI (`floating-ui-react/utils/tabbable.ts`). Unlike the\n * old `TreeWalker` approximation this:\n * - **pierces shadow DOM and `<slot>`** via composed traversal (`getComposedChildren`), so guards / portal\n * tab-order see candidates inside web components and projected content;\n * - honours **`inert`** ancestors, **`hidden`**, and computed-style visibility (`display`/`visibility`/\n * `checkVisibility()` content-visibility);\n * - treats **`<details>`/`<summary>`**, **`contenteditable`**, and **`audio`/`video[controls]`** as\n * focusable with the right implicit tab index;\n * - applies **radio-group** semantics (only the checked radio of a named group — or the first when none is\n * checked — is tabbable).\n *\n * `composedContains` is the shadow-aware `Node.contains`, kept here as the low-level primitive so this\n * module stays a leaf (only `@radix-ng/primitives/core`); `utils.ts` re-exports it for back-compat.\n */\n\nexport type FocusableElement = HTMLElement | SVGElement;\n\nconst CANDIDATE_SELECTOR =\n 'a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable=\"false\"]),audio[controls],video[controls]';\n\nconst getNodeName = (node: Node): string => node.nodeName.toLowerCase();\n\nconst isNode = (value: unknown): value is Node => value != null && typeof (value as Node).nodeType === 'number';\n\n/**\n * Owner-window-safe `HTMLElement` check. A raw `value instanceof HTMLElement` is realm-sensitive — it\n * returns `false` for an element from another document (iframe / popup window), whose realm has its own\n * `HTMLElement` constructor. Resolve the constructor from the node's own `defaultView` (the same pattern\n * as `dismissable-layer`'s `dismiss.ts`; Base UI `isHTMLElement`).\n */\nconst isHTMLElement = (value: unknown): value is HTMLElement => {\n if (!isNode(value)) {\n return false;\n }\n const view = (value as Node).ownerDocument?.defaultView;\n return view ? value instanceof view.HTMLElement : value instanceof HTMLElement;\n};\n\n/** A shadow root, detected structurally (`DOCUMENT_FRAGMENT_NODE` with a `host`) so it is realm- and SSR-safe. */\nconst isShadowRoot = (node: Node | null): node is ShadowRoot => node !== null && node.nodeType === 11 && 'host' in node;\n\nconst getComputedStyleOf = (element: Element): CSSStyleDeclaration | null => {\n const view = element.ownerDocument.defaultView;\n return view ? view.getComputedStyle(element) : null;\n};\n\n/**\n * Shadow-DOM-aware containment: whether `node` is `container` or lives inside it, crossing shadow roots\n * via their `host` (unlike `Node.contains`, which stops at a shadow boundary). Base UI `contains`.\n */\nexport function composedContains(container: Node, node: Node | null): boolean {\n let current: Node | null = node;\n while (current) {\n if (current === container) {\n return true;\n }\n current = isShadowRoot(current) ? current.host : current.parentNode;\n }\n return false;\n}\n\nfunction isHiddenByStyles(styles: CSSStyleDeclaration): boolean {\n return styles.visibility === 'hidden' || styles.visibility === 'collapse';\n}\n\nfunction isElementVisible(element: Element, styles: CSSStyleDeclaration | null): boolean {\n if (!element.isConnected || !styles || isHiddenByStyles(styles)) {\n return false;\n }\n if (typeof element.checkVisibility === 'function') {\n return element.checkVisibility();\n }\n return styles.display !== 'none' && styles.display !== 'contents';\n}\n\n/** The composed parent: an assigned `<slot>`, the element's parent, or the shadow host at a boundary. */\nfunction getParentElement(element: Element): Element | null {\n const assignedSlot = (element as Element & { assignedSlot?: HTMLSlotElement | null }).assignedSlot;\n if (assignedSlot) {\n return assignedSlot;\n }\n if (element.parentElement) {\n return element.parentElement;\n }\n const rootNode = element.getRootNode();\n return isShadowRoot(rootNode) ? rootNode.host : null;\n}\n\nfunction getDetailsSummary(details: Element): Element | null {\n for (const child of Array.from(details.children)) {\n if (getNodeName(child) === 'summary') {\n return child;\n }\n }\n return null;\n}\n\nfunction isWithinOpenDetailsSummary(element: Element, details: Element): boolean {\n const summary = getDetailsSummary(details);\n return !!summary && (element === summary || composedContains(summary, element));\n}\n\nfunction isFocusableCandidate(element: Element | null): element is FocusableElement {\n const nodeName = element ? getNodeName(element) : '';\n return (\n element != null &&\n element.matches(CANDIDATE_SELECTOR) &&\n // a `<summary>` is only focusable as the (first) summary of a `<details>`\n (nodeName !== 'summary' ||\n (element.parentElement != null &&\n getNodeName(element.parentElement) === 'details' &&\n getDetailsSummary(element.parentElement) === element)) &&\n // a `<details>` with its own summary is not focusable (the summary is)\n (nodeName !== 'details' || getDetailsSummary(element) == null) &&\n (nodeName !== 'input' || (element as HTMLInputElement).type !== 'hidden')\n );\n}\n\nfunction isVisibleInTabbableTree(element: Element, isAncestor: boolean): boolean {\n const styles = getComputedStyleOf(element);\n if (!isAncestor) {\n return isElementVisible(element, styles);\n }\n return !!styles && styles.display !== 'none';\n}\n\nfunction isFocusableElement(element: Element | null): element is FocusableElement {\n if (!isFocusableCandidate(element) || !element.isConnected || element.matches(':disabled')) {\n return false;\n }\n\n for (let current: Element | null = element; current; current = getParentElement(current)) {\n const isAncestor = current !== element;\n const isSlot = getNodeName(current) === 'slot';\n\n if (current.hasAttribute('inert')) {\n return false;\n }\n\n if (\n (isAncestor &&\n getNodeName(current) === 'details' &&\n !(current as HTMLDetailsElement).open &&\n !isWithinOpenDetailsSummary(element, current)) ||\n current.hasAttribute('hidden') ||\n (!isSlot && !isVisibleInTabbableTree(current, isAncestor))\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n/** The effective tab index, giving `<details>`/media/`contenteditable` their implicit `0`. */\nfunction getTabIndex(element: FocusableElement): number {\n const tabIndex = element.tabIndex;\n if (tabIndex < 0) {\n const nodeName = getNodeName(element);\n if (\n nodeName === 'details' ||\n nodeName === 'audio' ||\n nodeName === 'video' ||\n (isHTMLElement(element) && element.isContentEditable)\n ) {\n return 0;\n }\n }\n return tabIndex;\n}\n\nfunction getNamedRadioInput(element: FocusableElement): HTMLInputElement | null {\n if (getNodeName(element) !== 'input') {\n return null;\n }\n const input = element as HTMLInputElement;\n return input.type === 'radio' && input.name !== '' ? input : null;\n}\n\n/** Within a named radio group only the checked radio (or the first, if none is checked) is tabbable. */\nfunction isTabbableRadio(element: FocusableElement, candidates: FocusableElement[]): boolean {\n const input = getNamedRadioInput(element);\n if (!input) {\n return true;\n }\n\n const checkedRadio = candidates.find((candidate) => {\n const radio = getNamedRadioInput(candidate);\n return radio?.name === input.name && radio.form === input.form && radio.checked;\n });\n\n if (checkedRadio) {\n return checkedRadio === input;\n }\n\n return (\n candidates.find((candidate) => {\n const radio = getNamedRadioInput(candidate);\n return radio?.name === input.name && radio.form === input.form;\n }) === input\n );\n}\n\n/** The composed children of a node: `<slot>` assigned elements, then shadow-root children, else children. */\nfunction getComposedChildren(container: ParentNode): Element[] {\n if (isHTMLElement(container) && getNodeName(container) === 'slot') {\n const assignedElements = (container as HTMLSlotElement).assignedElements({ flatten: true });\n if (assignedElements.length > 0) {\n return assignedElements;\n }\n }\n if (isHTMLElement(container) && container.shadowRoot) {\n return Array.from(container.shadowRoot.children);\n }\n return Array.from(container.children);\n}\n\nfunction appendCandidates(container: ParentNode, list: FocusableElement[]): void {\n getComposedChildren(container).forEach((child) => {\n if (isFocusableCandidate(child)) {\n list.push(child);\n }\n appendCandidates(child, list);\n });\n}\n\n/** Collects elements matching `selector` across the composed tree (shadow / slot aware). */\nexport function queryComposedAll(container: ParentNode, selector: string): HTMLElement[] {\n const list: HTMLElement[] = [];\n const walk = (node: ParentNode): void => {\n getComposedChildren(node).forEach((child) => {\n if (isHTMLElement(child) && child.matches(selector)) {\n list.push(child);\n }\n walk(child);\n });\n };\n walk(container);\n return list;\n}\n\n/** Whether `element` is reachable by Tab right now (Base UI `isTabbable`). */\nexport function isTabbable(element: Element | null): boolean {\n return isFocusableElement(element) && getTabIndex(element) >= 0;\n}\n\n/** All programmatically focusable elements inside `container`, in composed document order. */\nexport function focusable(container: Element): FocusableElement[] {\n const candidates: FocusableElement[] = [];\n appendCandidates(container, candidates);\n return candidates.filter(isFocusableElement);\n}\n\n/** All keyboard-tabbable elements inside `container`, in composed document order (Base UI `tabbable`). */\nexport function tabbable(container: Element): FocusableElement[] {\n const candidates = focusable(container);\n return candidates.filter((element) => getTabIndex(element) >= 0 && isTabbableRadio(element, candidates));\n}\n\n/**\n * Back-compat alias for {@link tabbable} — historically returned a looser `TreeWalker` approximation;\n * now the full Base UI tabbable set. Kept so existing imports (`getTabbableCandidates`) keep working.\n */\nexport function getTabbableCandidates(container: Element): FocusableElement[] {\n return tabbable(container);\n}\n\n/** The first and last tabbable elements inside `container`. */\nexport function getTabbableEdges(\n container: Element\n): readonly [FocusableElement | undefined, FocusableElement | undefined] {\n const list = tabbable(container);\n return [list[0], list[list.length - 1]] as const;\n}\n\nfunction getTabbableIn(container: HTMLElement, dir: 1 | -1): FocusableElement | undefined {\n const list = tabbable(container);\n if (list.length === 0) {\n return undefined;\n }\n const active = getActiveElement(container.ownerDocument) as FocusableElement | null;\n const index = active ? list.indexOf(active) : -1;\n const nextIndex = index === -1 ? (dir === 1 ? 0 : list.length - 1) : index + dir;\n return list[nextIndex];\n}\n\n/**\n * The next tabbable in the document after the current focus (Base UI `getNextTabbable`) — used by the\n * portal-focus bridge's trailing guard to step focus past the popup. Falls back to `reference`.\n */\nexport function getNextTabbable(reference: Element | null): FocusableElement | null {\n const body = (reference?.ownerDocument ?? document).body;\n return getTabbableIn(body, 1) ?? (reference as FocusableElement | null);\n}\n\n/** The previous tabbable in the document before the current focus (Base UI `getPreviousTabbable`). */\nexport function getPreviousTabbable(reference: Element | null): FocusableElement | null {\n const body = (reference?.ownerDocument ?? document).body;\n return getTabbableIn(body, -1) ?? (reference as FocusableElement | null);\n}\n\nfunction getTabbableNearElement(reference: Element | null, dir: 1 | -1): FocusableElement | null {\n if (!reference) {\n return null;\n }\n const list = tabbable(reference.ownerDocument.body);\n const index = list.indexOf(reference as FocusableElement);\n if (list.length === 0 || index === -1) {\n return null;\n }\n return list[(index + dir + list.length) % list.length];\n}\n\n/** The tabbable immediately after `reference` in the document, wrapping (Base UI `getTabbableAfterElement`). */\nexport function getTabbableAfterElement(reference: Element | null): FocusableElement | null {\n return getTabbableNearElement(reference, 1);\n}\n\n/** The tabbable immediately before `reference` in the document, wrapping (Base UI `getTabbableBeforeElement`). */\nexport function getTabbableBeforeElement(reference: Element | null): FocusableElement | null {\n return getTabbableNearElement(reference, -1);\n}\n","import { getActiveElement } from '@radix-ng/primitives/core';\n\n// Tabbable / focusable detection lives in `./tabbable` (Base UI port). Re-exported here so existing\n// `./utils` imports — and the `@radix-ng/primitives/focus-scope` barrel — keep their public surface.\nexport {\n composedContains,\n focusable,\n getNextTabbable,\n getPreviousTabbable,\n getTabbableAfterElement,\n getTabbableBeforeElement,\n getTabbableCandidates,\n getTabbableEdges,\n isTabbable,\n queryComposedAll,\n tabbable\n} from './tabbable';\nexport type { FocusableElement } from './tabbable';\n\nimport type { FocusableElement } from './tabbable';\n\nexport const AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount';\nexport const AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount';\nexport const EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype FocusableTarget = HTMLElement | { focus: () => void };\n\n/**\n * The real target of a (possibly retargeted) event, piercing shadow boundaries via `composedPath()`.\n * Falls back to `event.target` when `composedPath` is unavailable.\n */\nexport function getEventTarget(event: Event): EventTarget | null {\n return event.composedPath?.()[0] ?? event.target;\n}\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n *\n * Reads the active element from `root` (a focus scope passes its host's `ownerDocument`); falling back to\n * the global `document` would, in an iframe / multi-document scope, never detect that focus moved inside\n * the inner document and so walk past every candidate, leaving focus on the last one instead of the first.\n */\nexport function focusFirst(\n candidates: FocusableElement[],\n { select = false, root }: { select?: boolean; root?: DocumentOrShadowRoot } = {}\n) {\n const ownerRoot = root ?? candidates[0]?.ownerDocument ?? document;\n const previouslyFocusedElement = getActiveElement(ownerRoot);\n for (const candidate of candidates) {\n focus(candidate, { select });\n if (getActiveElement(ownerRoot) !== previouslyFocusedElement) return true;\n }\n\n return;\n}\n\nexport function isSelectableInput(element: any): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element;\n}\n\nexport function focus(element?: FocusableTarget | null, { select = false } = {}) {\n // only focus if that element is focusable\n if (element && element.focus) {\n // Read the active element from the element's own document so the same-element select() check stays\n // correct across iframes / multi-document scopes (consistent with this module's ownerDocument focus).\n const root = 'ownerDocument' in element ? element.ownerDocument : undefined;\n const previouslyFocusedElement = getActiveElement(root ?? document);\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true });\n // only select if its not the same element, it supports selection and we need to select\n if (element !== previouslyFocusedElement && isSelectableInput(element) && select) {\n element.select();\n }\n }\n}\n","import { effect } from '@angular/core';\nimport { composedContains, queryComposedAll, tabbable } from './utils';\n\n/** Marks the leading / trailing focus-guard spans (Base UI `data-base-ui-focus-guard`). */\nexport const FOCUS_GUARD_ATTR = 'data-rdx-focus-guard';\n\n/** Saved-tabindex marker used by {@link disableFocusInside} / {@link enableFocusInside}. */\nconst SAVED_TABINDEX_ATTR = 'data-rdx-tabindex';\n\n/** Visually-hidden, off-flow style for a focus guard / `aria-owns` anchor (Base UI `visuallyHidden`). */\nexport const FOCUS_GUARD_STYLE: Partial<CSSStyleDeclaration> = {\n position: 'fixed',\n top: '0',\n left: '0',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clipPath: 'inset(50%)',\n whiteSpace: 'nowrap',\n border: '0'\n};\n\n/**\n * Creates a visually-hidden, **tabbable** focus-guard `<span>` — the Angular counterpart of Base UI's\n * `FocusGuard`. The portal-focus bridge places one before and one after the portal content so a Tab into\n * (or out of) the portal lands on a guard, which then redirects focus to the right boundary.\n */\nexport function createFocusGuard(ownerDocument: Document): HTMLSpanElement {\n const guard = ownerDocument.createElement('span');\n guard.setAttribute('tabindex', '0');\n guard.setAttribute('aria-hidden', 'true');\n guard.setAttribute(FOCUS_GUARD_ATTR, '');\n Object.assign(guard.style, FOCUS_GUARD_STYLE);\n return guard;\n}\n\n/**\n * Creates a visually-hidden `<span aria-owns=\"…\">` that links the portal node into the trigger's tab /\n * AT order (Base UI's single `aria-owns` anchor). The manager places it next to the trigger.\n */\nexport function createAriaOwnsAnchor(ownerDocument: Document, portalId: string): HTMLSpanElement {\n const anchor = ownerDocument.createElement('span');\n anchor.setAttribute('aria-owns', portalId);\n Object.assign(anchor.style, FOCUS_GUARD_STYLE);\n return anchor;\n}\n\n/**\n * Makes every tabbable descendant of `container` **non-tabbable** (`tabindex=\"-1\"`), saving each one's\n * original tabindex so {@link enableFocusInside} can restore it. Base UI `disableFocusInside`: a\n * non-modal portal keeps its content untabbable until focus is actually inside it, so a Tab from the\n * trigger steps onto the guard instead of jumping into the content.\n */\nexport function disableFocusInside(container: HTMLElement): void {\n for (const element of tabbable(container)) {\n element.setAttribute(SAVED_TABINDEX_ATTR, element.getAttribute('tabindex') ?? '');\n element.setAttribute('tabindex', '-1');\n }\n}\n\n/**\n * Restores the tabbability that {@link disableFocusInside} suspended. Base UI `enableFocusInside`.\n * Collects markers across the composed tree so guards inside shadow roots / slots are restored too.\n */\nexport function enableFocusInside(container: HTMLElement): void {\n queryComposedAll(container, `[${SAVED_TABINDEX_ATTR}]`).forEach((element) => {\n const original = element.getAttribute(SAVED_TABINDEX_ATTR);\n element.removeAttribute(SAVED_TABINDEX_ATTR);\n if (original) {\n element.setAttribute('tabindex', original);\n } else {\n element.removeAttribute('tabindex');\n }\n });\n}\n\n/**\n * Whether a focus event crossed the `container` boundary — its `relatedTarget` (the other side of the\n * focus move) is `null` or outside `container` (Base UI `isOutsideEvent`). Shadow-DOM-aware via\n * {@link composedContains}.\n */\nexport function isOutsideEvent(event: FocusEvent, container: Element): boolean {\n const relatedTarget = event.relatedTarget as Node | null;\n return !relatedTarget || !composedContains(container, relatedTarget);\n}\n\n/**\n * The portal-focus bridge's **tabbability toggle** (Base UI `FloatingPortal` capture-phase `onFocus`).\n * While `portalNode` is mounted (and `enabled`), it makes the portal content tabbable **only when focus\n * is inside it**: focus entering from outside re-enables tabbability, focus leaving to outside disables\n * it again. Listens on the **capture** phase so it settles before the focus manager's guards react.\n *\n * Must be called in an injection context. The initial disable-on-mount and the guard-span placement are\n * the manager's responsibility (Phase 1b); this owns only the dynamic in/out toggle.\n */\nexport function useFocusGuardsTabbability(\n portalNode: () => HTMLElement | null,\n options: { enabled?: () => boolean } = {}\n): void {\n const enabled = options.enabled ?? (() => true);\n let focusInsideDisabled = false;\n\n effect((onCleanup) => {\n const node = portalNode();\n if (!node || !enabled()) {\n return;\n }\n const ownerDocument = node.ownerDocument;\n\n const onFocus = (event: FocusEvent): void => {\n // Only react to focus actually crossing the portal boundary.\n if (!event.relatedTarget || !isOutsideEvent(event, node)) {\n return;\n }\n if (event.type === 'focusin') {\n if (focusInsideDisabled) {\n enableFocusInside(node);\n focusInsideDisabled = false;\n }\n } else {\n disableFocusInside(node);\n focusInsideDisabled = true;\n }\n };\n\n if (isOutsideEvent(new FocusEvent('focusin', { relatedTarget: ownerDocument.activeElement }), node)) {\n disableFocusInside(node);\n focusInsideDisabled = true;\n }\n\n node.addEventListener('focusin', onFocus, true);\n node.addEventListener('focusout', onFocus, true);\n onCleanup(() => {\n node.removeEventListener('focusin', onFocus, true);\n node.removeEventListener('focusout', onFocus, true);\n if (focusInsideDisabled) {\n enableFocusInside(node);\n focusInsideDisabled = false;\n }\n });\n });\n}\n","import { InjectionToken, Provider, Signal, signal } from '@angular/core';\n\nexport type RdxFocusScopeConfig = {\n trapped: Signal<boolean>;\n\n /**\n * Optional return-focus policy override (ADR 0017 `returnFocus`), resolved by an enclosing\n * {@link RdxFloatingFocusManager} at **unmount time**. The focus scope owns the *timing* (its queued\n * post-unmount focus); this lets the manager own the *target*:\n * - `false` → do **not** return focus;\n * - an `HTMLElement` → return focus there **explicitly** (bypasses the moved-focus guard, matching\n * Base UI's `hasExplicitReturnFocus`);\n * - `undefined` → default behavior (return to the element focused before mount, with the guard).\n */\n returnFocus?: () => HTMLElement | false | undefined;\n};\n\nexport const RdxFocusScopeConfigToken = new InjectionToken<RdxFocusScopeConfig>('RdxFocusScopeConfig', {\n factory: () => ({\n trapped: signal(false)\n })\n});\n\nexport function provideRdxFocusScopeConfig(factory: () => RdxFocusScopeConfig): Provider {\n return { provide: RdxFocusScopeConfigToken, useFactory: factory };\n}\n","import { signal, WritableSignal } from '@angular/core';\n\nexport interface FocusScopeAPI {\n paused: WritableSignal<boolean>;\n pause(): void;\n resume(): void;\n}\n\n/**\n * The active-scope stack pauses/resumes scopes, so it **is** cross-document coordination state — keyed\n * per owner `Document` (a `WeakMap`) rather than process-global (ADR 0017 Phase 1a): opening a scope in\n * document B must not pause document A's scope.\n */\nconst stacksByDocument = new WeakMap<Document, WritableSignal<FocusScopeAPI[]>>();\n\nfunction getFocusStackState(document: Document): WritableSignal<FocusScopeAPI[]> {\n let state = stacksByDocument.get(document);\n if (!state) {\n state = signal<FocusScopeAPI[]>([]);\n stacksByDocument.set(document, state);\n }\n return state;\n}\n\nexport function createFocusScopesStack(document: Document) {\n /** A stack of focus scopes for this document, with the active one at the top */\n const stack = getFocusStackState(document);\n\n return {\n add(focusScope: FocusScopeAPI) {\n const current = stack();\n const active = current[0];\n if (focusScope !== active) {\n active?.pause();\n }\n const updated = arrayRemove(current, focusScope);\n updated.unshift(focusScope);\n stack.set(updated);\n },\n\n remove(focusScope: FocusScopeAPI) {\n const current = stack();\n const updated = arrayRemove(current, focusScope);\n stack.set(updated);\n // после удаления «возобновляем» новый верхний\n stack()[0]?.resume();\n }\n };\n}\n\nexport function arrayRemove<T>(array: T[], item: T): T[] {\n const copy = [...array];\n const idx = copy.indexOf(item);\n if (idx !== -1) {\n copy.splice(idx, 1);\n }\n return copy;\n}\n","import {\n afterNextRender,\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n Injector,\n input,\n output,\n Signal,\n signal\n} from '@angular/core';\nimport { BooleanInput, createContext, getActiveElement } from '@radix-ng/primitives/core';\nimport { RdxFocusScopeConfigToken } from './focus-scope.config';\nimport { createFocusScopesStack, FocusScopeAPI } from './stack';\nimport {\n AUTOFOCUS_ON_MOUNT,\n AUTOFOCUS_ON_UNMOUNT,\n composedContains,\n EVENT_OPTIONS,\n focus,\n focusFirst,\n getEventTarget,\n getTabbableCandidates,\n getTabbableEdges\n} from './utils';\n\nexport interface FocusScopeContext {\n loop?: Signal<boolean>;\n\n trapped?: Signal<boolean>;\n}\n\nexport const [injectFocusScopeContext, provideFocusScopeContext] = createContext<FocusScopeContext>(\n 'FocusScope Context',\n 'utils/focus-scope'\n);\n\nconst rootContext = (): FocusScopeContext => {\n const context = inject(RdxFocusScope);\n\n return {\n loop: context.loop,\n trapped: context.isTrapped\n };\n};\n\n/**\n * @group Components\n */\n@Directive({\n selector: '[rdxFocusScope]',\n providers: [provideFocusScopeContext(rootContext)],\n host: {\n tabindex: '-1',\n '(keydown)': 'handleKeyDown($event)'\n }\n})\nexport class RdxFocusScope {\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly config = inject(RdxFocusScopeConfigToken);\n\n /** The host's owner `Document` — all focus listeners / reads are scoped here, never global `document`. */\n private readonly ownerDocument = this.elementRef.nativeElement.ownerDocument ?? document;\n\n /**\n * When `true`, tabbing from last item will focus first tabbable\n * and shift+tab from first item will focus last tababble.\n *\n * @group Props\n * @defaultValue false\n */\n readonly loop = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * When `true`, focus cannot escape the focus scope via keyboard,\n * pointer, or a programmatic focus.\n *\n * @group Props\n * @defaultValue false\n */\n readonly trapped = input<boolean | undefined, BooleanInput | undefined>(undefined, {\n transform: (value) => (value === undefined ? undefined : booleanAttribute(value))\n });\n\n readonly isTrapped = computed(() => this.trapped() ?? this.config.trapped());\n\n /**\n * Event handler called when auto-focusing on mount.\n * Can be prevented.\n *\n * @group Emits\n */\n readonly mountAutoFocus = output<Event>();\n\n /**\n * Event handler called when auto-focusing on unmount.\n * Can be prevented.\n *\n * @group Emits\n */\n readonly unmountAutoFocus = output<Event>();\n\n readonly lastFocusedElement = signal<HTMLElement | null>(null);\n\n private readonly focusScopesStack = createFocusScopesStack(this.ownerDocument);\n\n readonly focusScope: FocusScopeAPI = {\n paused: signal(false),\n pause: () => this.focusScope.paused.set(true),\n resume: () => this.focusScope.paused.set(false)\n };\n\n private alive = true;\n\n constructor() {\n this.destroyRef.onDestroy(() => {\n this.alive = false;\n });\n\n afterNextRender(() => {\n effect(\n (onCleanup) => {\n const container = this.elementRef.nativeElement;\n\n if (this.isTrapped()) {\n const handleFocusIn = (event: FocusEvent) => {\n if (this.focusScope.paused() || !container) {\n return;\n }\n\n const target = getEventTarget(event) as HTMLElement | null;\n if (composedContains(container, target)) {\n this.lastFocusedElement.set(target);\n } else {\n focus(this.lastFocusedElement(), { select: true });\n }\n };\n\n const handleFocusOut = (event: FocusEvent) => {\n if (this.focusScope.paused() || !container) {\n return;\n }\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // A `focusout` event with a `null` `relatedTarget` will happen in at least two cases:\n //\n // 1. When the user switches app/tabs/windows/the browser itself loses focus.\n // 2. In Google Chrome, when the focused element is removed from the DOM.\n //\n // We let the browser do its thing here because:\n //\n // 1. The browser already keeps a memory of what's focused for when the page gets refocused.\n // 2. In Google Chrome, if we try to focus the deleted focused element (as per below), it\n // throws the CPU to 100%, so we avoid doing anything for this reason here too.\n if (relatedTarget === null) return;\n\n // If the focus has moved to an actual legitimate element (`relatedTarget !== null`)\n // that is outside the container, we move focus to the last valid focused element inside.\n if (!composedContains(container, relatedTarget)) {\n focus(this.lastFocusedElement(), { select: true });\n }\n };\n\n const handleMutations = () => {\n const isLastFocusedElementExist = composedContains(container, this.lastFocusedElement());\n\n if (!isLastFocusedElementExist) {\n focus(container);\n }\n };\n\n const mutationObserver = new MutationObserver(handleMutations);\n if (container) {\n mutationObserver.observe(container, { childList: true, subtree: true });\n }\n\n this.ownerDocument.addEventListener('focusin', handleFocusIn);\n this.ownerDocument.addEventListener('focusout', handleFocusOut);\n\n onCleanup(() => {\n this.ownerDocument.removeEventListener('focusin', handleFocusIn);\n this.ownerDocument.removeEventListener('focusout', handleFocusOut);\n mutationObserver.disconnect();\n });\n }\n },\n { injector: this.injector }\n );\n\n effect(\n async (onCleanup) => {\n const container = this.elementRef.nativeElement;\n\n await Promise.resolve();\n if (!container || !this.alive) {\n return;\n }\n\n this.focusScopesStack.add(this.focusScope);\n\n const previouslyFocusedElement = getActiveElement(this.ownerDocument) as HTMLElement | null;\n const hasFocusedCandidate = composedContains(container, previouslyFocusedElement);\n const mountEventHandler = (ev: Event) => {\n if (this.alive) this.mountAutoFocus.emit(ev);\n };\n\n if (!hasFocusedCandidate) {\n const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_MOUNT, mountEventHandler);\n container.dispatchEvent(mountEvent);\n\n if (!mountEvent.defaultPrevented) {\n focusFirst(getTabbableCandidates(container), {\n select: true,\n root: this.ownerDocument\n });\n if (getActiveElement(this.ownerDocument) === previouslyFocusedElement) focus(container);\n }\n }\n\n const unmountEventHandler = (ev: Event) => {\n if (this.alive) this.unmountAutoFocus.emit(ev);\n };\n container.addEventListener(AUTOFOCUS_ON_UNMOUNT, unmountEventHandler);\n\n onCleanup(() => {\n container.removeEventListener(AUTOFOCUS_ON_MOUNT, mountEventHandler);\n\n const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);\n container.dispatchEvent(unmountEvent);\n\n // Queue the return-focus on the owner window's animation frame (not `setTimeout`),\n // so it runs after the unmounting paint settles (ADR 0017 Phase 1a queued focus).\n const view = this.ownerDocument.defaultView ?? globalThis;\n view.requestAnimationFrame(() => {\n // An enclosing focus manager can override the return target (ADR 0017\n // `returnFocus`): `false` suppresses it, an element returns there explicitly\n // (bypassing the moved-focus guard), `undefined` keeps the default behavior.\n const override = this.config.returnFocus?.();\n if (override !== false && !unmountEvent.defaultPrevented) {\n if (override) {\n focus(override, { select: true });\n } else if (!this.shouldPreserveMovedFocus()) {\n focus(previouslyFocusedElement ?? this.ownerDocument.body, { select: true });\n }\n }\n\n // we need to remove the listener after we `dispatchEvent`\n container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, unmountEventHandler);\n\n this.focusScopesStack.remove(this.focusScope);\n });\n });\n },\n { injector: this.injector }\n );\n });\n }\n\n /**\n * Whether the interaction that unmounted this scope already moved focus to a legitimate element\n * **outside** it — e.g. an outside press onto an interactive control in a non-modal layer (ADR 0017\n * §2, finding #3). Returning focus to the previously-focused element would then *steal* it back from\n * what the user just acted on. Focus that fell to `<body>` / `null` (a backdrop press, Escape, or the\n * focused element being removed) is **not** \"moved\" — return focus normally so keyboard users land\n * back on the trigger. The page never scroll-jumps either way: {@link focus} uses `preventScroll`.\n */\n private shouldPreserveMovedFocus(): boolean {\n const active = getActiveElement(this.ownerDocument) as HTMLElement | null;\n return (\n !!active && active !== this.ownerDocument.body && !composedContains(this.elementRef.nativeElement, active)\n );\n }\n\n handleKeyDown(event: KeyboardEvent) {\n // Only a looping or trapped scope polices Tab at its edges. A plain non-modal scope must let Tab\n // through so the browser can reach whatever follows the host — e.g. the hidden outer focus guards\n // `RdxFloatingFocusManager` places around it (calling `preventDefault()` here would fire first and\n // strand focus at the boundary, defeating the guard). A paused scope (a nested scope took over)\n // also stands down so it can't double-handle a bubbled Tab. Mirrors Base UI / Radix `FocusScope`:\n // `if ((!loop && !trapped) || focusScope.paused) return`.\n if ((!this.loop() && !this.isTrapped()) || this.focusScope.paused()) {\n return;\n }\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n\n const focusedElement = getActiveElement(this.ownerDocument) as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) event.preventDefault();\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n if (this.loop()) {\n focus(first, { select: true });\n }\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n if (this.loop()) {\n focus(last, { select: true });\n }\n }\n }\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, ElementRef, model, input, booleanAttribute, output, signal, computed, effect, untracked, Directive, DestroyRef, isDevMode, Injector, afterNextRender, numberAttribute, PLATFORM_ID, NgModule } from '@angular/core';
3
3
  import * as i1 from '@radix-ng/primitives/popper';
4
- import { RdxPopper, RdxPopperContentWrapper, RdxPopperArrow, RdxPopperContent, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
4
+ import { RdxPopper, RdxPopperContentWrapper, RdxPopperArrow, RdxPopperContent, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, DROPDOWN_COLLISION_AVOIDANCE, RdxPopperAnchor } from '@radix-ng/primitives/popper';
5
5
  import * as i2 from '@radix-ng/primitives/core';
6
6
  import { createContext, createFloatingRootContext, useTransitionStatus, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, injectId, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useAnchoredScrollLock, RdxFloatingNodeRegistration, rdxDevError, setupInternalBackdrop, getMaxTransitionDuration } from '@radix-ng/primitives/core';
7
7
  import { injectDirection } from '@radix-ng/primitives/direction-provider';
@@ -1176,6 +1176,43 @@ class RdxMenuPopup {
1176
1176
  this.rootContext.close('focus-out', event);
1177
1177
  }
1178
1178
  });
1179
+ // keepMounted return-focus: the composed focus scope returns focus in its *unmount* cleanup,
1180
+ // which never runs while a `keepMounted` popup stays in the DOM (so Escape/close would strand
1181
+ // focus on the now-hidden popup — a keyboard trap). Mirror it on close: when the menu settles
1182
+ // closed but the popup is still mounted, apply the focus manager's resolved return target
1183
+ // (honoring `finalFocus`), falling back to the trigger. The `isConnected` guard scopes this to
1184
+ // the keep-mounted case — a normally-unmounting popup is gone by the deferred frame, its scope
1185
+ // having already returned focus.
1186
+ const settledClosed = computed(() => !this.rootContext.isOpen() && this.rootContext.transitionStatus() !== 'ending', /* @ts-ignore */
1187
+ ...(ngDevMode ? [{ debugName: "settledClosed" }] : /* istanbul ignore next */ []));
1188
+ let wasSettledClosed = settledClosed();
1189
+ effect(() => {
1190
+ const closed = settledClosed();
1191
+ const justSettled = closed && !wasSettledClosed;
1192
+ wasSettledClosed = closed;
1193
+ if (!justSettled) {
1194
+ return;
1195
+ }
1196
+ const popup = this.elementRef.nativeElement;
1197
+ const view = popup.ownerDocument.defaultView ?? globalThis;
1198
+ view.requestAnimationFrame(() => {
1199
+ if (!popup.isConnected) {
1200
+ return;
1201
+ }
1202
+ // Return focus only if it was inside the popup, or the browser dumped it to <body>
1203
+ // because the positioner became `hidden`. If the user moved focus to another real
1204
+ // element, leave it.
1205
+ const active = popup.ownerDocument.activeElement;
1206
+ if (active !== popup.ownerDocument.body && !popup.contains(active)) {
1207
+ return;
1208
+ }
1209
+ const target = this.focusManager.resolveReturnTarget();
1210
+ if (target === false) {
1211
+ return; // finalFocus={false} suppresses return
1212
+ }
1213
+ (target ?? this.rootContext.trigger())?.focus({ preventScroll: true });
1214
+ });
1215
+ });
1179
1216
  }
1180
1217
  handleCloseParent(event) {
1181
1218
  event.stopPropagation();
@@ -1387,14 +1424,21 @@ class RdxMenuPopup {
1387
1424
  closeInteractionType: () => rootContext.closeInteractionType()
1388
1425
  };
1389
1426
  })
1390
- ], exportAs: ["rdxMenuPopup"], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFloatingNodeRegistration }, { directive: i3.RdxFloatingFocusManager }, { directive: i1$1.RdxCompositeList }], ngImport: i0 }); }
1427
+ ], exportAs: ["rdxMenuPopup"], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFloatingNodeRegistration }, { directive: i3.RdxFloatingFocusManager, inputs: ["returnFocus", "finalFocus"] }, { directive: i1$1.RdxCompositeList }], ngImport: i0 }); }
1391
1428
  }
1392
1429
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxMenuPopup, decorators: [{
1393
1430
  type: Directive,
1394
1431
  args: [{
1395
1432
  selector: '[rdxMenuPopup]',
1396
1433
  exportAs: 'rdxMenuPopup',
1397
- hostDirectives: [RdxPopperContent, RdxFloatingNodeRegistration, RdxFloatingFocusManager, RdxCompositeList],
1434
+ hostDirectives: [
1435
+ RdxPopperContent,
1436
+ RdxFloatingNodeRegistration,
1437
+ // `finalFocus` maps to the focus manager's `returnFocus`, overriding the trigger-return policy
1438
+ // below when set (Base UI `Menu.Popup finalFocus`); same wiring as Popover/Select.
1439
+ { directive: RdxFloatingFocusManager, inputs: ['returnFocus: finalFocus'] },
1440
+ RdxCompositeList
1441
+ ],
1398
1442
  providers: [
1399
1443
  provideFloatingFocusManagerConfig(() => {
1400
1444
  const rootContext = injectRdxMenuRootContext();
@@ -1469,18 +1513,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1469
1513
  * This replaces the consumer-owned `@if (root.open())` mount: it adds both teleporting *and*
1470
1514
  * exit-animation support. Apply it with the `*` microsyntax on the positioner —
1471
1515
  * `<div *rdxMenuPortal rdxMenuPositioner>` — or as an explicit `<ng-template rdxMenuPortal>`. For a
1472
- * custom container, or a backdrop alongside the positioner (multi-root), use the explicit form.
1516
+ * custom container, a backdrop alongside the positioner (multi-root), or `[keepMounted]`, use the
1517
+ * explicit form.
1518
+ *
1519
+ * Set `[keepMounted]="true"` to keep the popup mounted while the menu is closed (the positioner's
1520
+ * `data-closed` state hides it) — inherited from {@link RdxPortalPresence}.
1473
1521
  */
1474
1522
  class RdxMenuPortal {
1475
1523
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxMenuPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1476
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxMenuPortal, isStandalone: true, selector: "ng-template[rdxMenuPortal]", providers: [provideRdxPresenceContext(() => ({ present: injectRdxMenuRootContext().present }))], exportAs: ["rdxMenuPortal"], hostDirectives: [{ directive: i1$2.RdxPortalPresence, inputs: ["container", "container"] }], ngImport: i0 }); }
1524
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxMenuPortal, isStandalone: true, selector: "ng-template[rdxMenuPortal]", providers: [provideRdxPresenceContext(() => ({ present: injectRdxMenuRootContext().present }))], exportAs: ["rdxMenuPortal"], hostDirectives: [{ directive: i1$2.RdxPortalPresence, inputs: ["container", "container", "keepMounted", "keepMounted"] }], ngImport: i0 }); }
1477
1525
  }
1478
1526
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxMenuPortal, decorators: [{
1479
1527
  type: Directive,
1480
1528
  args: [{
1481
1529
  selector: 'ng-template[rdxMenuPortal]',
1482
1530
  exportAs: 'rdxMenuPortal',
1483
- hostDirectives: [{ directive: RdxPortalPresence, inputs: ['container'] }],
1531
+ hostDirectives: [{ directive: RdxPortalPresence, inputs: ['container', 'keepMounted'] }],
1484
1532
  providers: [provideRdxPresenceContext(() => ({ present: injectRdxMenuRootContext().present }))]
1485
1533
  }]
1486
1534
  }] });
@@ -1531,6 +1579,16 @@ function setupMenuInternalBackdrop(positioner, rootContext, injector) {
1531
1579
  isOpen: () => rootContext.isOpen(),
1532
1580
  cutout: () => cutoutElement(rootContext),
1533
1581
  shouldRender: () => {
1582
+ // The backdrop must exist only while the menu is present (open or running its exit
1583
+ // animation), never while idle-closed. This matters for a `keepMounted` menu: its
1584
+ // positioner stays in the DOM while closed, so without this gate the shared backdrop
1585
+ // helper — which creates the element on `shouldRender()` alone — would build it at page
1586
+ // load and capture a stale cutout rect (the trigger's off-screen position), then never
1587
+ // refresh it. On open the stale backdrop would cover the trigger, and the trigger
1588
+ // `mouseup` would land on the backdrop and immediately close the menu (`cancel-open`).
1589
+ if (!rootContext.present()) {
1590
+ return false;
1591
+ }
1534
1592
  const type = rootContext.parentType();
1535
1593
  if (type === 'menu' || !rootContext.modal()) {
1536
1594
  return false; // submenus and non-modal menus get no backdrop
@@ -1556,6 +1614,17 @@ class RdxMenuPositioner extends RdxPopperContentWrapper {
1556
1614
  super();
1557
1615
  this.rootContext = injectRdxMenuRootContext();
1558
1616
  this.legacyVars = legacyPopperVars('menu');
1617
+ /**
1618
+ * Whether the popup is shown: open, or still running its closing transition. Drives both the native
1619
+ * `hidden` (finding: a closed keep-mounted popup must hide itself) and — via `positioningActive` —
1620
+ * whether the inherited wrapper keeps tracking the anchor, so a closed keep-mounted positioner stops
1621
+ * its `autoUpdate` `requestAnimationFrame` loop (finding: endless rAF while closed).
1622
+ */
1623
+ this.visible = computed(() => this.rootContext.isOpen() || this.rootContext.transitionStatus() === 'ending', /* @ts-ignore */
1624
+ ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
1625
+ // Gate the inherited positioning on visibility (see `visible`) — pauses `autoUpdate` while the
1626
+ // menu is closed but kept mounted.
1627
+ this.positioningActive = this.visible;
1559
1628
  const injector = inject(Injector);
1560
1629
  const host = inject(ElementRef).nativeElement;
1561
1630
  // After the structural portal has relocated this positioner into the portal container, set up the
@@ -1563,9 +1632,16 @@ class RdxMenuPositioner extends RdxPopperContentWrapper {
1563
1632
  afterNextRender(() => setupMenuInternalBackdrop(host, this.rootContext, injector));
1564
1633
  }
1565
1634
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxMenuPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1566
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxMenuPositioner, isStandalone: true, selector: "[rdxMenuPositioner]", host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "style": "legacyVars" } }, providers: [
1635
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxMenuPositioner, isStandalone: true, selector: "[rdxMenuPositioner]", host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "hidden": "!visible()", "style": "legacyVars" } }, providers: [
1567
1636
  ...provideRdxPopperContentWrapper(RdxMenuPositioner),
1568
- provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
1637
+ // Base UI applies the DROPDOWN preset to a root menu (and POPUP to submenus, which we don't
1638
+ // differentiate yet — logical-side submenu placement is a separate epic).
1639
+ provideRdxPopperContentConfig({
1640
+ arrowPadding: 5,
1641
+ collisionPadding: 5,
1642
+ collisionAvoidance: DROPDOWN_COLLISION_AVOIDANCE,
1643
+ updatePositionStrategy: 'always'
1644
+ })
1569
1645
  ], exportAs: ["rdxMenuPositioner"], usesInheritance: true, ngImport: i0 }); }
1570
1646
  }
1571
1647
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxMenuPositioner, decorators: [{
@@ -1575,11 +1651,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1575
1651
  exportAs: 'rdxMenuPositioner',
1576
1652
  providers: [
1577
1653
  ...provideRdxPopperContentWrapper(RdxMenuPositioner),
1578
- provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
1654
+ // Base UI applies the DROPDOWN preset to a root menu (and POPUP to submenus, which we don't
1655
+ // differentiate yet — logical-side submenu placement is a separate epic).
1656
+ provideRdxPopperContentConfig({
1657
+ arrowPadding: 5,
1658
+ collisionPadding: 5,
1659
+ collisionAvoidance: DROPDOWN_COLLISION_AVOIDANCE,
1660
+ updatePositionStrategy: 'always'
1661
+ })
1579
1662
  ],
1580
1663
  host: {
1581
1664
  '[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
1582
1665
  '[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
1666
+ // Base UI puts a native `hidden` on a closed positioner (`hidden: !mounted`). It matters for a
1667
+ // `keepMounted` menu: while closed-and-settled the popup stays in the DOM but must be removed from
1668
+ // layout / a11y / tab order. Gated on `visible` (not `present`/`isOpen`) so an exit animation
1669
+ // still plays — the popup only hides once the closing transition ends.
1670
+ '[hidden]': '!visible()',
1583
1671
  // `data-side`/`data-align`/`data-anchor-hidden` and the unified vars come from the inherited
1584
1672
  // wrapper (ADR 0012); only the deprecated `--radix-menu-*` aliases remain, for back-compat.
1585
1673
  '[style]': 'legacyVars'