kerfjs 5.0.0-beta.97 → 5.0.0-beta.99
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/CHANGELOG.md +79 -0
- package/ai/manifest.json +1 -1
- package/dist/overlay.d.ts +43 -1
- package/dist/overlay.js +163 -15
- package/dist/overlay.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
8
8
|
|
|
9
9
|
### Fixed
|
|
10
10
|
|
|
11
|
+
- A dark-tone `AnchoredSurface` (`tone: 'dark'`) now follows a live color-scheme switch while it is open. It picked its inverse scheme once, on open, so an OS preference change or an app theme toggled on an ancestor left the popup on the old side until it was reopened. It now flips with the page. `openAnchoredSurfaceAt` also reads the dark tone's scheme from `point.context`, the element under the pointer, instead of from the body or dialog that holds its temporary anchor, so a region with its own forced `color-scheme` is honored.
|
|
12
|
+
- A keyboard move made as a long `PopupMenu` submenu (or menu) opens now leaves the chosen item in view, not just focused. When Web Awesome's late first-item focus landed after the move (say `End` pressed right after `ArrowRight`), it scrolled the menu back to the top, and kerf's restore refocused the chosen item without scrolling, so the focused last choice sat out of view below the clip. The restore now scrolls the item into view within its own menu, leaving the page alone.
|
|
13
|
+
- A default-tone `ToolbarControlGroup` in an overlay `Pane` header (`headerPresentation="overlay"`) or in a `DialogSurface` floating header's `header-actions` (`headerPresentation="floating"`) now keeps its controls at WCAG AA over content passing beneath the translucent chrome material. Its quiet control color over its own translucent fill measured about 3.0:1 (light) and 3.5:1 (dark) over the worst case; its controls now take the normal text color there, as they already did in overlay `NavStack` chrome.
|
|
14
|
+
- A dark `ToolbarControlGroup` (`tone="dark"`) holding one control no longer hides its icon on hover over a light page. A lone control's hover replaces the whole group's fill, and the dark tone's hover was a translucent surface wash meant to lie over that fill, so the white icon sat on a near-white circle (about 1.3:1). The wash now paints over the group's loud fill, as it does on one control of a multi-control dark group, and it is 14% instead of 28%: at 28% even that layered paint measured about 2.7:1. Hovered dark controls now keep about 3.4:1 over a light page and about 5.8:1 in dark.
|
|
15
|
+
- An unframed scrollable `List` now stops a row focused or scrolled into view with its whole focus ring inside the scroll clip. Only a framed List carried ring-room scroll padding, so in an unframed one the ring of a row brought into view ended flush against the clip and was cut off. Every `scrollable` List now has 6px of block scroll padding (the ring's 4px reach plus 2px of slack), which moves nothing at rest. An unframed List's first and last rows still meet the clip at the scroll extremes; `frame`'s 4px inset is the answer when they must show a whole ring.
|
|
16
|
+
- An overlay `Pane` header (`headerPresentation="overlay"`) now clears its forced-colors edge. In forced colors the header gains a 1px `CanvasText` bottom border, but `wireScrollDividers` observed the header's content box, so the border alone reported no resize and `--kui-scroll-clearance-block-start` stayed 1px short: the content's first item rested under the edge. The wiring now observes each overlay header's border box, the same box it publishes.
|
|
17
|
+
- `positionAnchored({ fitHeight: true })` (and so `autoReposition()`, `popover()`, `tooltip()`, and `@kerfjs/ui`'s `openAnchoredSurface()`) now keeps an element placed above its anchor growing upward. It set `top` from the height the element had at the reposition, so an element still growing afterwards — say an expand-from-0 `max-height` entrance placed above mid-expand — slid its bottom edge down over the anchor until the next scroll or resize. An above placement now pins the element's bottom edge at the gap above the anchor (`bottom`, with `top: auto`), so it grows and shrinks upward, away from the anchor, whatever height it goes on to lay out; a below placement sets `top` with `bottom: auto`. Placement without `fitHeight` is unchanged (`KF-MZ03RG`).
|
|
18
|
+
- An overlay `Pane` header (`headerPresentation="overlay"`) no longer covers the top of a classic, space-taking scrollbar (Windows and Linux, or macOS with "always show scrollbars"). Its thumb and arrow buttons ran beneath the translucent header. `wireScrollDividers` now also measures the content's vertical scrollbar and publishes a classic one's inline size on the Pane as `--kui-pane-scrollbar-inline-size`, and the header ends where the scrollbar's track begins. Overlay scrollbars take no space and still pass beneath the header like the content.
|
|
19
|
+
- A `ToolbarText` in an overlay `Pane` header (`headerPresentation="overlay"`) now keeps WCAG AA over the translucent chrome material. Over worst-case content passing beneath, its quiet `description` measured about 3.2:1 (light) and 4.1:1 (dark), and a default-size title about 3.8:1 (light). In that header an untoned `ToolbarText` now takes the normal text color for its title at every size and mixes its description halfway toward it (about 5.9:1 in both themes), so the description stays readable and still reads as secondary.
|
|
20
|
+
- A framed `List` (`frame`) now keeps a focused middle row's ring inside its scroll clip in Chromium. The frame's scroll padding equaled the ring's 4px reach, but Chromium's focus scroll stops 1px short of the padded target and a row's fractional position can cost another fraction of a pixel, so about 1px of the ring was cut. The scroll padding is now the ring reach plus 2px; the frame's 4px visual inset and its first- and last-row resting positions are unchanged.
|
|
21
|
+
- `positionAnchored({ fitHeight: true })` now measures an element whose `max-height` is mid-animation at the cap it is headed to. An app transition already running on `max-height` (say an expand-from-0 entrance to `max-height: 300px`) or a `@keyframes` animation of `max-height` made the cap read report the current frame, so a reposition mid-expand judged the element by a height it was about to outgrow and could open it on a side its settled cap does not fit. kerf now reads the cap from the running effect instead — its last `max-height` keyframe, or its first while the current iteration plays in reverse — and, for that case, never touches the element's style, so the app's animation keeps running uninterrupted. A finished animation that still fills is read from computed style as before, and a keyframe `var()` kerf cannot resolve outside the element counts as no cap (`KF-YW3PRD`).
|
|
22
|
+
- `eslint-plugin-kerfjs`'s `ui-css-values` rule now recognizes CSS-value helpers imported from any `@kerfjs/ui` subpath, including the documented `@kerfjs/ui/css-values`. It only knew helpers imported from the package root, so `iconTile={{ color: foregroundColor('#000') }}` with `foregroundColor` from `@kerfjs/ui/css-values` passed lint while `kerf-ui-analyze` reported `KUI-L014` (and likewise `KUI-L015` for a bare `plus()`). Lint now resolves a helper exactly as the analyzer does: by its imported name, from the component's package or any subpath of it, through a named, aliased, or namespace import. A same-named helper from another package is still ignored (`KF-HKGK2C`).
|
|
23
|
+
- `positionAnchored({ fitHeight: true })` now honors a relative `max-height` cap and is no longer fooled by a `max-height` transition. A cap that stays relative after computing — `max-height: 60%`, `calc(50% - 2rem)`, `min(300px, 60%)`, `clamp()` — was treated as no cap, so an element that fit the preferred side at that cap still opened on the roomier side. kerf now resolves it against the element's fixed-position containing block (normally the viewport) with an unpainted probe beside the element, so the element's scroll position and layout are untouched. And an app `transition` on `max-height` made the cap read report the transition's running value (the room kerf last published) and fire `transitionrun` / `transitionstart` events, so the cap was ignored again; kerf now holds off only the `max-height` transition for that read, and every other transition keeps running (`KF-53GEGN`).
|
|
24
|
+
- A `StateBanner`'s side action no longer squeezes or covers its copy on a narrow banner. The banner laid out as a three-track grid whose action track took the action's full width, so on a phone a pair of buttons (say Retry Save and Discard Changes in a wrapping `Row`) left the title about 65px, and a word wider than that ran underneath the buttons. The banner is now a wrapping row: the default `actionPlacement="trailing"` keeps the action beside the copy while the copy has at least 12rem, and otherwise moves it to its own end-aligned trailing row. The choice follows the banner's own width rather than a breakpoint, so it also works in a narrow sidebar on a wide screen and inside an intrinsically sized `CornerStatus`. `actionPlacement="below"` still always uses the trailing row (`KF-PXCKVZ`).
|
|
25
|
+
- `positionAnchored({ fitHeight: true })` (and so `autoReposition()`, `popover()`, `tooltip()`, and `@kerfjs/ui`'s `openAnchoredSurface()`) now honors the element's own `max-height` when choosing a side. It measured a scrolling element at its full content height, so an element the app caps below that height — say `max-height: min(300px, var(--kerf-anchor-available-height))` with 500px of content — fit neither side and opened on the roomier one, even when its 300px capped height fit the preferred side. kerf now reads that cap with its own published room swapped for an unbounded one, so it never mistakes its last cap for the app's, and the read is style-only, so the element's scroll position is kept (`KF-878W6T`).
|
|
26
|
+
- A keyboard move made just as a `PopupMenu` submenu finishes opening is no longer undone. Web Awesome hands focus to the submenu's first item from a timer it queues when the show animation settles, and a browser may dispatch a keypress before that timer runs. So pressing `End` (or an arrow key) at that moment focused the chosen item, and the late handoff then snapped focus back to the first item. PopupMenu now keeps the keyboard or pointer choice through that handoff; a deliberate `Home` to the first item still stands (`KF-XMDR2Q`).
|
|
27
|
+
- An open `PopupMenu` is now announced with a name. Web Awesome renders the `role="menu"` panel in a shadow root, and the trigger's name never reached it: a `context` menu's `label` sat only on its `aria-hidden` anchor, and no ID reference can cross into or out of the shadow roots involved. So a screen reader announced an unnamed menu, and `getByRole('menu', { name })` found nothing. The panel now takes the trigger's name (the `context` or icon-only `label`, or the visible `text`), and each submenu is named by its parent item's label (`KF-AB4ZVD`).
|
|
28
|
+
- A `TabBar` with `snapTabs` no longer grows its tab strip past its allocation. The snap mode reserves end scroll room so the last tab can settle at the strip's start, and it reserved that room as end padding on the strip. A flex item can't shrink below its own padding, so room measured at one width held the strip open after the bar narrowed. This happened, for example, when a drawer opened or lost space to a neighboring panel. The strip then pushed an adjacent `trailing` action and the `end` zone out of the bar, where they could not be clicked; a twelve-tab drawer strip measured 1584px against a 1088px edge. The room is now scrollable content after the last tab, inside the strip. It lengthens only the scroll range, so the strip shrinks to its allocation and the last tab can still snap to the start. Writing the room no longer resizes the strip, so `wireTabBars` now re-measures it on the next frame until the value holds. This replaces the follow-up measurement that a resize used to trigger (`KF-3Q98FJ`).
|
|
29
|
+
- In Safari, closing a `Select` inside a scrolling dialog body no longer leaves blank scroll space below the content. WebKit kept the closed listbox's last box in the body's scrollable overflow, so the body scrolled into an empty band the height of the listbox. The Select now relayouts its popup once after it closes, which keeps focus and the keyboard on the Select (`KF-C3523R`).
|
|
11
30
|
- In a divided list, `ListItem`, `ListItemLink`, and `ListActionRow` corners now follow the row's position. A corner is flat wherever a divider line meets it, whether the row draws that line or its neighbor does, so with lines between rows the first row rounds only its top, middle rows are flat, and the last row rounds only its bottom. Previously each row squared only the edges it drew itself, so in a `divider="after"` list every hovered or selected row was rounded on top and flat on the bottom, including the last. A single divided row is unchanged (`KF-AQN4H7`).
|
|
12
31
|
|
|
13
32
|
- A collapsible `TokenSearchField` in a `ToolbarControlGroup` with `content="search"` and `sizing="grow"` now opens and closes as one surface. The group snapped straight to its grown width (its `width: auto` cannot transition) while the field animated its own width inside it, so for about 250ms the pill showed an empty band and the query wrapped onto several lines. The group now animates its flex size, and the field fills it on every frame, laid out at least basis-wide while the group clips it. In a toolbar 480px wide or narrower, where the grown group takes its own row, and under reduced motion, both open in one frame (`KF-50K0JM`). A plain collapsible search group (no `sizing="grow"`) had the same defect in a milder form: its field ran a second width transition that lagged the group's by up to 90px, and the query wrapped onto as many as 19 lines for about 100ms. The group now owns that motion too. The field follows it on every frame and keeps the query on one line until the opening ends, then wraps normally again (`KF-CXQB30`).
|
|
@@ -82,6 +101,46 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
82
101
|
|
|
83
102
|
### Added
|
|
84
103
|
|
|
104
|
+
- `TabNavigator` takes `barPresentation: 'docked' | 'overlay'` (default `docked`). With `overlay`, the tab bar lies over the active scene on the translucent chrome material, so content scrolls visibly beneath it, as under an iOS tab bar. `wireTabNavigator` measures the live bar (bottom safe area included), and the scenes hand its height to their content as the block-end edge inset: a plain scene, a sole `Pane`, or a `NavStack` (whose own overlay bottom toolbar then sits just above the bar) rests its last item clear of it. Unselected and selected labels lift to keep AA over the material. The bar is opaque under reduced transparency, increased contrast, or without `backdrop-filter`, and paints `Canvas` with a `CanvasText` edge in forced colors. Unwired, the bar stays docked. Like the NavStack overlay bars, the tab bar ends where the active scene's classic (space-taking) scrollbar begins, so the thumb and buttons stay visible.
|
|
105
|
+
- `@kerfjs/ui/wire-list-focus-scroll`'s `wireListFocusScroll(root)` brings a keyboard-focused row of a `scrollable` List fully into view, focus ring included, in every engine. Firefox's `focus()` leaves a partly hidden row alone until about half of it is hidden and then centers it, and Safari's never scrolls it, so arrow-key or Tab navigation could rest on a row that was mostly clipped. The wiring calls `scrollIntoView({ block: 'nearest' })` only when a `:focus-visible` element's ring crosses the List's clip, honoring its ring-room scroll padding; pointer focus is left alone, so a click never scrolls its row from under the pointer.
|
|
106
|
+
- **`AnchoredSurface` dark tone for popups anchored to media chrome.** `openAnchoredSurface` and `openAnchoredSurfaceAt` take `tone: 'dark'`, the same loud, inverse tone as a `ToolbarControlGroup` or `ToolbarText` `tone="dark"`. A volume popup anchored to a dark media control group over a video preview is dark beside a light page and light beside a dark one, matching the group that opened it. The surface takes the opposite of its anchor's color scheme when it opens and is marked `data-tone="dark"`. Every semantic token is a `light-dark()` pair, so the content inherits the inverse text, border, and focus colors without tone props or overrides: a `ListItem` mute row and a `wa-slider` included. Measured on the catalog demo in Chromium, Firefox, and WebKit, the row label reads 12.8:1 and its focus ring 8.1:1 on a light page, and 16.8:1 and 4.0:1 on a dark one. The default tone is unchanged.
|
|
107
|
+
- A floating `DialogSurface` title bar (`headerPresentation="floating"`) now draws its divider as scroll state, like an overlay `Pane` header: a 1px line along its bottom edge while the body, or a filling child that reaches beneath it (a transcript `List` or `Pane` with block-start `scrollClearance`), is scrolled away from its top, and none at rest. In light mode the title bar over white bubbles had little separation. `@kerfjs/ui/surface-scaffold/register` follows those scrollers; a nested scroller that starts below the title bar never counts.
|
|
108
|
+
- **Analyzer and lint check row icon-tile colors.** Catalog `cssValueProps` paths now reach a member of an object-valued prop (`iconTile.color`) as well as each array item's member (`choices[].swatch`); a prop given a non-object value, such as a tone string `iconTile="brand"`, is skipped. `ListItem`, `ListItemLink`, and `ListActionRow` record `iconTile.color` as a color fill (`fillColor()`, `uiColor()`, `colorVar()`) and `iconTile.foreground` as a foreground (`uiColor()`, `foregroundColorVar()`, `foregroundColor()`), so the Kerf UI analyzer, `kerf-ui-doctor`, and the `kerfjs/ui-css-values` ESLint rule report `foregroundColor()` on a tile fill (`KUI-L014`) and keep accepting it as the glyph color. The catalog and composition schemas' `path` pattern accepts the same grammar, so consumer catalogs can publish object-member paths (`KF-R02RDK`).
|
|
109
|
+
- **Catalog frames can be a named anchor with a centered content column.** `CatalogExample`'s `viewport` takes `anchorName`, a CSS dashed-ident (`"--app-shell-main"`) that becomes the frame's `anchor-name`, so a top-layer specimen (a `popover="manual"` request popup, say) whose own CSS sets `position-anchor` to that name positions against the frame exactly as it does against its production anchor. An invalid name throws a `TypeError`. `minHeight` adds `"app"` (592px, a floor the frame can grow past), and `contentWidth` (`"compact"` 440px, `"medium"` 640px, or `"wide"` 864px, capped at the frame) with `contentAlign` (`"start"` or `"center"`) gives each in-flow child a centered or start-aligned reading column; `[popover]` children keep their own geometry. An app no longer needs a wrapper stage element to name the anchor and center the column. The catalog's Application Layout page shows a request popup docked to the bottom of its frame (`KF-D532S2`).
|
|
110
|
+
- **`fillColor()` for application fill colors.** `@kerfjs/ui/css-values` adds `fillColor(value)`, the fill twin of `foregroundColor()`: it accepts one application-chosen CSS color (`#3b82f6`, `rgb(20 40 60 / 0.8)`, `rebeccapurple`) with the same single-declaration check, but returns a plain `CssColor` rather than a `CssForegroundColor`, so a fill literal is rejected by props that paint text or an icon (`SelectChoice.color`, `LucideIcon.color`, `Text.color`, an `iconTile` `foreground`). Use it for a `ChoiceChips` `swatch` and a list row's `iconTile` `color`; every example, demo, and design template now does. A `CssForegroundColor` is still a `CssColor`, so an existing `foregroundColor()` fill keeps compiling, and the `ChoiceChips` catalog entry now records `choices[].swatch` as a color fill so the Kerf UI analyzer reports `foregroundColor()` there (`KUI-L014`) with `fillColor()` as the fix (`KF-7JA25S`).
|
|
111
|
+
- **Framed result pickers without app CSS.** `List` takes `frame`, `maxHeight`, and `minHeight` (typed `CssLength`s from `@kerfjs/ui/css-values`). `frame` draws a 1px neutral border with the `--kui-radius-m` control radius and a 4px inset on every side, replacing `inset`, `textInsets`, and `controlInsets`. That inset is exactly a row's focus-ring reach, and the frame adds it to its scroll padding, so the first and last rows' rings stay inside the clip. With `scrollable`, the height bounds work without a flex-column ancestor. `<List scrollable frame gap="2xs" minHeight={rem(4.25)} maxHeight={rem(14)}>` is a bounded list of `ListItem` candidates, such as Hot Sheet's duplicate-ticket picker. The catalog's List page shows it as "Result Picker" (`KF-SHW2J9`).
|
|
112
|
+
- **Transcripts scroll beneath a floating composer.** A `scrollable` `List` and a `Pane` take `scrollClearance` (`"block-start"`, `"block-end"`, or `"both"`; the `ScrollClearance` type). It clears chrome laid over the scroller's block edge inside its own scroll area: the scroller adds the inherited `--kui-scroll-clearance-block-start` / `--kui-scroll-clearance-block-end` context that the chrome publishes to its block padding and `scroll-padding`. Content then passes visibly beneath the chrome, while the first and last items, and anything focused or scrolled into view, still rest clear of it. A `DialogSurface` with `footerPresentation="floating"` publishes block-end clearance for a `fill` or `fill-last` child that opts in. That clearance is the footer's measured height plus its offset. The body stops reserving the footer below that child, so a chat transcript reaches the panel's end and scrolls beneath the transparent composer. Without the opt-in, the transcript still ends at the composer's top. Every dialog body clears clearance inherited from the page. An app that overlays its own translucent chrome on a scrolling region publishes the same context, so header clearance works today. The catalog's chat dialog now scrolls its transcript beneath the composer (`KF-ET0EX8`). `PaneConfig` carries `scrollClearance` too, so a composed Pane opts in through a Workbench `mainPane` or panel `pane`, a `CollapsiblePanel` `pane`, or a `NavStack` view's `pane`; the composites now forward every `PaneConfig` field through one exhaustively typed helper, so a new field can no longer be silently dropped (`KF-574HQV`).
|
|
113
|
+
- **Translucent overlay Pane headers.** `Pane` takes `headerPresentation` (`"docked"`, the default, or `"overlay"`; the `PaneHeaderPresentation` type, also in `PaneConfig`). An overlay header is laid over the top of the scrolling content on the new chrome material. The material is `--kui-chrome-material`, the surface at 80% opacity, behind `--kui-chrome-material-filter`, a blur and saturation backdrop filter, so content scrolls visibly beneath the header. `wireScrollDividers` measures each overlay header with a `ResizeObserver` and publishes its height on the Pane as `--kui-scroll-clearance-block-start`. The Pane's content clears that automatically, so the first item rests just below the header and anything scrolled into view stays clear of it. The clearance follows the header as its title wraps or its text grows. Until the wiring runs, and with `chromePlacement="auto"`, the header stays docked. The material turns opaque under `prefers-reduced-transparency: reduce` or `prefers-contrast: more`, and without `backdrop-filter`. In forced colors the header paints `Canvas` with a `CanvasText` edge. Normal text keeps WCAG AA over any content beneath the material. Quiet text does not, so overlay-header text uses the normal text color (`ui/docs/accessibility.md` "Chrome material"). The catalog's Pane page shows an inbox scrolling beneath its header (`KF-DT6C3F`).
|
|
114
|
+
- **Translucent dialog title bars.** `DialogSurface` takes `headerPresentation` (`"docked"`, the default, or `"floating"`; the `DialogSurfaceHeaderPresentation` type). A floating header lies over the body's block start on `--kui-chrome-material-raised`, a new token for chrome on a raised surface: the raised panel color at 80% opacity behind `--kui-chrome-material-filter`. The `surface-scaffold/register` import now measures the header as well as the footer, and the body reserves the header's height before its own inset, with matching scroll padding, so flowing content starts below the title bar and scrolls beneath it. Without the register, the header stays docked. When the body's only child fills it and opts into block-start `scrollClearance`, the body publishes the header's height as `--kui-scroll-clearance-block-start` instead. With a floating composer and `scrollClearance="both"`, a transcript then scrolls beneath both the title bar and the composer while its first and last messages rest clear of them. The material has the same opaque fallbacks as the Pane overlay header, and normal text keeps WCAG AA over any content beneath it. An untoned `ToolbarText` slotted as the dialog's label (or into its header actions) takes the same AA lift it gets in an overlay Pane header: a normal-color title and a description mixed halfway toward it. The catalog's surface scaffold page adds a "Floating Title Bar" thread dialog (`KF-REH2T2`).
|
|
115
|
+
- **Translucent NavStack top bar.** `NavStack` takes `chromePresentation` (`"docked"`, the default, or `"overlay"`; the `NavStackChromePresentation` type). An overlay top chrome lies over the active view on the same chrome material as an overlay Pane header, so content scrolls visibly beneath it, as under an iOS navigation bar. `wireNavStack` measures the live chrome (never a cross-fading copy) with a `ResizeObserver`, publishes its height on the stack root with a `data-chrome-overlay="measured"` gate, and restores both before paint if a re-render strips them. The measured height, top safe area included, becomes the viewport's block-start edge inset, so a plain view, a sole `Pane`, or a nested layout clears the bar automatically: the first item rests just below it, and focused or scrolled-to content stops clear of it. A classic, space-taking scrollbar in the active view stays uncovered: the chrome ends where its track begins. Unwired, or with `hideToolbar`, the chrome stays docked. The material keeps its opaque fallbacks (reduced transparency, increased contrast, no `backdrop-filter`) and its forced-colors edge, and an untoned `ToolbarText` in the chrome lifts its title and description to AA exactly as in an overlay Pane header. `SplitView` forwards the option through `compactStack`, and a Workbench navigation panel through `navStack`. The catalog's NavStack page adds an **Overlay Chrome** example; `ui/tests/browser/overlay-nav-stack.spec.ts` covers it in Chromium, Firefox, and WebKit.
|
|
116
|
+
- **Translucent NavStack bottom toolbar.** `NavStack` takes `bottomToolbarPresentation` (`"docked"`, the default, or `"overlay"`; the `NavStackBottomToolbarPresentation` type), a separate prop from `chromePresentation`, which keeps governing only the top chrome. An overlay bottom toolbar lies over the active view's bottom edge on the same chrome material, as an iOS toolbar does. `wireNavStack` measures the live toolbar (never a cross-fading copy), publishes its height with a `data-bottom-toolbar-overlay="measured"` gate, and the measured height, bottom safe area included, becomes the views' block-end edge inset: a plain view's or a sole `Pane`'s last item (or the Pane's footer) rests just above the toolbar while content scrolls visibly beneath it, and the toolbar's scroll divider keeps working. Unwired, or while no bottom toolbar is shown, it stays docked; inside a `TabNavigator` scene it sits just above the tab bar. It keeps the material's opaque fallbacks, and in forced colors its top border becomes a `CanvasText` edge. A default-tone `ToolbarControlGroup` in either overlaid bar now lifts its controls to the normal text color, so they keep AA over the content beneath (the top chrome's back control measured about 3.0:1 before). A non-scrolling `List` directly in a `Pane`'s content no longer shrinks below its rows, so the content's block-end padding (a bottom safe area or the toolbar's clearance) follows the last row. `SplitView` forwards the option through `compactStack`, and a Workbench navigation panel through `navStack`. The catalog's **Overlay Chrome** example now overlays both bars; `ui/tests/browser/overlay-nav-stack-bottom.spec.ts` covers it in Chromium, Firefox, and WebKit.
|
|
117
|
+
|
|
118
|
+
- **Anchored surfaces fit beside their anchor.** `positionAnchored()` / `autoReposition()` (and so `popover()` and `tooltip()`) take `viewportMargin`, a gutter in px that the horizontal clamp and the vertical fit test keep from the viewport edges (default `0`, the bare edge as before), and `fitHeight` for an element that scrolls its overflow: when neither side has room for its full height it opens on the roomier side, and every placement publishes that room as the `--kerf-anchor-available-height` custom property on the element, so CSS can cap it (`max-height: var(--kerf-anchor-available-height); overflow: auto`). Scrolling inside the element keeps its scroll position. Without the new options placement is unchanged. `@kerfjs/ui`'s `openAnchoredSurface()` uses both: the surface stays 8px inside the viewport (`viewportMargin` overrides it), caps its height to the room beside its anchor, scrolls its content, and exposes that room to its content as `--kui-anchored-surface-available-height`, refreshed on scroll and resize, so a tall help surface fits beside a field on a phone with no application media queries (`KF-K4GE6K`).
|
|
119
|
+
|
|
120
|
+
- **Stateful `TokenSearchField` trailing action.** `trailingAction` now takes `expanded` (a disclosure such as a help toggle; renders `aria-expanded`) or `pressed` (a toggle; renders `aria-pressed`), which are mutually exclusive at compile time, plus `controls` (`aria-controls`), `haspopup` (`aria-haspopup`), and `attributes` for domain `data-*` metadata (the field keeps `data-action`). An open or pressed button keeps its hover fill and reads at full text strength. In the `toolbar-group` presentation it now takes the clear button's control-highlight radius, so the compact 24px help glyph beside the clear button matches it in every presentation with no application CSS. The catalog's Structured Ticket Search help toggle opens syntax help in an `openAnchoredSurface` (`KF-R056AJ`).
|
|
121
|
+
|
|
122
|
+
- **Link `Chip` for downloadable files.** `Chip` takes an `href`, with an optional `download` (`true` or a suggested file name) and `external` (new tab, `rel="noopener noreferrer"`, and an "(opens in new tab)" accessible-name suffix, as on `ListItemLink`). It then renders a native `<a>` with the chip's geometry, icon slot, truncation, focus ring, and hover feedback, so an app no longer styles its own file-link chips. A link chip cannot also carry a remove button, and a disabled one drops its destination and leaves the tab order. The new `inherit` tone (`ChipTone`) follows the surrounding foreground, for a chip inside a `brand-loud` or `neutral` `ContentItem` bubble. It draws a currentColor border and no fill, so the label keeps the bubble's text contrast, and its focus ring follows the foreground too. The Chip catalog shows file chips in both bubble kinds (`KF-H658D6`).
|
|
123
|
+
|
|
124
|
+
- **Multi-select `ChoiceChips`.** Set `multiple` with a `readonly Value[]` `value` and the same wrapping pills, swatches, and icons become native checkboxes in a named `role="group"`: each pill is its own Tab stop, Space toggles it, assistive technology reads its native checked state, and every checked value submits under `name` (`FormData.getAll(name)`). The props are a discriminated union, so a multiple group rejects a single value (and a single group an array), and a multiple group takes no `required`. Every chip color is a theme token, so on dark inverse chrome, such as a media viewer's annotation intents, place the chips in a dark appearance scope (`.wa-dark`) rather than restyling them. The UX catalog adds Multiple Selection and On Dark Chrome examples, and the design templates add a Multiple Selection variant.
|
|
125
|
+
- `ListItem`, `ListItemLink`, and `ListActionRow` take an `iconTile` prop that presents the leading icon in a filled 32px rounded tile holding an 18px glyph, for settings and command rows identified by color. Pass a tone (`neutral`, `brand`, `success`, `warning`, or `danger`, the same quiet palette as `ToolbarControlGroup.tileTone`) or `{ color, foreground? }` with a typed `CssColor` fill (`uiColor()`, `colorVar()`, or `foregroundColor('#e5484d')`) and an optional `CssForegroundColor` glyph; without a foreground, the glyph is black or white, whichever contrasts with the fill. The tile keeps 6px clear of a standard 44px row, a compact row is at least 42px tall, the label moves to the tile's edge, and hover and selection leave the tile's colors alone.
|
|
126
|
+
|
|
127
|
+
- **A chat composer without app chrome.** The new `Composer` (`@kerfjs/ui/composer`) is a raised frame (the standard border, large radius, and `shadow-l`) whose border turns brand while focus is within. It holds a borderless textarea that grows with `field-sizing: content` between `minRows` (default 1) and `maxRows` (default 8) lines and then scrolls. Below the textarea sits an action row with an optional quiet `hint`, which describes the field when you pass an `id`, and the trailing `actions` (a Send button). Engines without `field-sizing` show a fixed three-line field that scrolls. Slot it into a `DialogSurface` floating footer to float it over a transcript; the catalog's chat dialog now does (`KF-S5EG5K`).
|
|
128
|
+
- **Enter-to-send for `Composer`.** Set `submitOnEnter` and call `wireComposers(root)` (`@kerfjs/ui/wire-composers`) once: Enter, or Cmd/Ctrl+Enter, in the field clicks the composer's first trailing action, so a keyboard send runs the same delegated `data-action` handler as a click. Shift+Enter still inserts a newline. An Enter that commits an IME composition (`isComposing`, keyCode 229, or between `compositionstart` and `compositionend`) is left to the input method, and an empty or whitespace-only message, an auto-repeating key, a disabled field, or a disabled or loading action sends nothing. The field announces the shortcut with `aria-keyshortcuts="Enter"`. The catalog's Composer and chat dialog examples use it (`KF-DGD5QS`).
|
|
129
|
+
- `PopoverDialogSurface` takes an optional `describedBy` id, rendered as `aria-describedby` on its `role="dialog"` root. Pair it with a title `ToolbarText`'s `descriptionId` so a non-modal dialog keeps its accessible description; `rootAttributes` accepts only `data-*` attributes, so there was no way to set it before. `DialogSurface` is unchanged, because it wraps a `wa-dialog` that the app authors and does not render the dialog root itself (`KF-SKVEQW`).
|
|
130
|
+
- A compact `ListActionRow` is 36px tall, as documented, instead of 44px. Its label kept the standard row's minimum height and its primary button kept the standard 8px block padding, so `density="compact"` had no effect on a single-line row. Compact rows now use 4px block padding like a compact `ListItem`, so the two row kinds stay level in a dense list; a compact row with an `iconTile` is exactly 42px. The ListActionRow catalog adds a Compact Rows example (`KF-4Q5MJ5`).
|
|
131
|
+
- `ListActionRow` leading icons default to 18px (they were 24px), the same size as `ListItem` icons, so a `List` that mixes the two row kinds starts their labels on one edge, not 6px apart. Both read a new shared `--kui-list-row-icon-size`: set it on a containing `List` to resize both row kinds' untiled icons together. `--kui-list-item-icon-size` still wins for `ListItem` rows that set it, and an `iconTile` keeps its 32px tile and 18px glyph. An app that wants the old 24px `ListActionRow` glyphs sets `--kui-list-row-icon-size: 24px` on the list (`KF-KWFMM1`).
|
|
132
|
+
- `ListItem` and `ListActionRow` share one leading inline inset. Set `--kui-list-row-padding-inline` on a containing `List` and both row kinds move their content and reorder insertion lines together; a `ListActionRow` adds the 1px a `ListItem` border takes, so their leading icons stay on one edge at any value. It defaults to `--kui-layout-item-padding`, so unset lists render as before. Previously only `ListItem` had an inline padding token, and overriding it moved `ListItem` icons away from `ListActionRow` icons in the same list. `--kui-list-item-padding-inline` still wins for `ListItem` rows that set it. The List catalog's new Shared Row Inset example shows mixed rows on a customized inset (`KF-N3TDV3`).
|
|
133
|
+
- `@kerfjs/ui` now has one capitalization rule for interface copy, taken from Apple's guidance and documented in the new `ui/docs/writing.md`. Text that names a command, control, view, or field is title style ("Save Draft", "Back to Inbox", "Workspace Settings"): buttons, menu items, segments, `Select` choices, tabs, titles, `ListHeader` labels, and field labels. Text that describes, explains, or reports is sentence style: descriptions, messages, tooltips, placeholders, option text, and status lines. The AI skill, `llms.txt`, and the design docs carry the rule. `kerf-ui-doctor` reports a lowercase word in a known title-style label as the warning `KUI-D040`, with the repaired label. The catalog demos, recipes, and design templates now follow the rule (`KF-6VWSV4`).
|
|
134
|
+
- **Chat-style dialogs without `::part()` rules.** `DialogSurface` gains `footerPresentation="floating"`, which lays the footer over the body's block end, a typed `footerOffset` from the panel's inline and block-end edges (default the 16px container inset), on a transparent background. The `surface-scaffold/register` import measures the footer, and the body reserves that height plus the offset, so a growing composer never hides the transcript's last message. `bodyLayout="fill-last"` (also on `PopoverDialogSurface`) keeps the body's leading children at their natural height and gives the last child the remaining height and its own scrolling: a notice above a scrolling transcript. The catalog's new chat dialog example composes both (`KF-913AT2`).
|
|
135
|
+
- `@kerfjs/ui`'s title-style rule now reaches the names the doctor could not see. `TokenSearchField`'s default `clearLabel` is `Clear Search` (it was `Clear search`); a test or app that addresses the clear control by its default accessible name needs the new spelling, and `clearLabel` still overrides it. The catalog shell (`Filter Catalog`, `Clear Filter`, `Reduce Motion`, `Vertical Stripes`, `Layout Guide`, `Related Entries`, `Capture Region`, the `Demo Source` / `Component Source` / `Design Template` / `Integration Guidance` resource links, and the `Used By` related group), the catalog entry names that render as page titles (`Foundation Tokens`, `Surface Scaffolds`, `List Workspace States`, …), and the demos' and recipes' accessible region, control, field, dialog, tab, and list-item names are title style. `kerf-ui-doctor`'s `KUI-D040` now also reads the accessible names of icon-only controls: `IconButton` / `IconToggleButton` / `PopupMenu` `label`, `SplitButton` `optionsLabel`, `TokenSearchField` `clearLabel`, and the `aria-label` of a `button` or `wa-button` whose only content is an icon (`KF-ZAD6EJ`).
|
|
136
|
+
- `@kerfjs/ui` catalog example and stack labels are headings, so they now use title style across every UX-demo page ("Heading Identity Stays Whole", "Icon Trigger in a Toolbar Group"); their notes stay sentence style. Demo content item titles ("Build Output", "Work Area") follow, with mirrored `ariaLabel`s. `kerf-ui-doctor`'s `KUI-D040` now reads `CatalogExample` and `CatalogExampleStack` `label`, and keeps a minor word inside a longer hyphenated compound lowercase ("Edge-to-Edge"). A test that addresses a demo specimen by its region name needs the new spelling (`KF-4TXTVV`).
|
|
137
|
+
- A labelled `CatalogExample` now names its `section` with its label, so each specimen is a named region that assistive technology and `getByRole('region', { name })` can address. An unlabeled example stays an ordinary grouping (`KF-ZQMXK1`).
|
|
138
|
+
- `CatalogExampleStack` takes a `width` option: `reading` (default) keeps the 736px reading measure, and `full` lets the stack span the stage so a catalog can show application-width compositions such as boards, app shells, drawers, and split views without clipping them (`KF-HKVT5S`).
|
|
139
|
+
- `CatalogExample` `viewport.width` takes `sidebar`: a 272px production-rail width, capped at the stage, so sidebar-pane specimens render at their real width without a wrapper (`KF-DH5MH8`).
|
|
140
|
+
- In a `PopupMenu` level (the top menu or one submenu) that holds a checkbox choice, plain commands now reserve the check column too, so check, icon, and label columns line up across checked, unchecked, and plain rows; a level without a choice keeps the compact start. Submenu checkbox rows drop an extra 1em label inset, so their icon-to-label gap matches the top level (`KF-31QHPE`).
|
|
141
|
+
- Hovering a selected (`aria-current`) or pressed `ListItem` likewise keeps its selected tint instead of the neutral hover colors (`KF-84AGE5`).
|
|
142
|
+
- Hovering a selected or pressed `ListActionRow` keeps its selected tint instead of switching to the neutral hover colors, so a freshly selected row no longer looks unselected while the pointer stays on it (`KF-Z737D0`).
|
|
143
|
+
- A `width="full"` `CatalogExampleStack` now spans the whole catalog stage on wide screens; the stage's 1120px canvas cap no longer stops it short of the right edge (`KF-WDSFWH`).
|
|
85
144
|
- **Chat-bubble `ContentItem`s.** `bubble="start" | "end"` lays a `ContentItem` out as a transcript bubble: it fits its message up to 80% of the row, sits at the start (other participants) or end (the person's own messages), and wraps long unbroken text. The new `appearance="brand-loud"` paints the solid brand fill with its on-loud foreground for the person's own messages (`KF-M3HJK8`).
|
|
86
145
|
|
|
87
146
|
- **`DialogSurface rootAttributes`.** `DialogSurface` takes safe `data-*` `rootAttributes`, such as a `data-key`, on its root; its own configuration attributes stay protected. Sibling dialogs that render conditionally then morph by identity rather than by position. Before, a re-render could reuse one open dialog's element for another, swapping its accessible name and suppressing `wa-after-hide`. Apps no longer wrap each dialog in a keyed `div` (`KF-D7H0WN`).
|
|
@@ -177,6 +236,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
177
236
|
|
|
178
237
|
### Changed
|
|
179
238
|
|
|
239
|
+
- A resizable `@kerfjs/ui` `Workbench` panel without a `label` now names its resize handle in title style, as `ui/docs/writing.md` asks for region names: "Resize Left Rail", "Resize Right Rail", and "Resize Bottom Drawer" (previously "Resize left rail" and so on). A test or locator that matched the old accessible name needs the new one (`KF-NWS9GB`).
|
|
240
|
+
|
|
241
|
+
- `kerf-ui-doctor`'s `KUI-D040` now also checks template literals passed to title-style props, judging only their static words: ``label={`Edit ${name} shortcut`}`` is reported with the suggestion `` `Edit ${name} Shortcut` ``. Interpolated values and words joined to an interpolation are never judged. The UX demos' dynamic control names follow it ("Edit … Shortcut", "Open Ticket …", "New Tab …"), and the NavStack demo's detail item is named by its visible title alone (`KF-8B6JES`).
|
|
242
|
+
|
|
243
|
+
- `kerf-ui-doctor`'s `KUI-D040` now also reads region and landmark names, which `ui/docs/writing.md` makes title style, and raw Web Awesome field labels. The new region names are:
|
|
244
|
+
- the `label` of `Pane`, `Workbench`, `Toolbar`, `ToolbarControlGroup`, `TabBar`, `NavStack`, `CollapsiblePanel`, `FloatingToolbar`, and `ResizableRegion`;
|
|
245
|
+
- the `ariaLabel` of `SunkenPanel` and `List`;
|
|
246
|
+
- a `Pane`'s `contentLabel`;
|
|
247
|
+
- the panel, toolbar, and pane labels in Workbench, CollapsiblePanel, and NavStack configuration;
|
|
248
|
+
- the `aria-label` of `section`, `nav`, `aside`, `main`, `header`, `footer`, `form`, `search`, or an element with a landmark role.
|
|
249
|
+
|
|
250
|
+
The field labels it reads are the `label` of a raw `wa-input`, `wa-select`, `wa-textarea`, or `wa-radio-group`. `wa-checkbox`, `wa-switch`, and `wa-radio` option text, and `LoadingSpinner` and `Skeleton` labels, stay unread. Expect new warnings on lowercase names such as `<Pane label="Workspace pages">` ("Workspace Pages"). The package's own fixtures and examples are fixed. The catalog shell's sidebar navigation is now named "Kerf Components" (it was "Kerf components") (`KF-8JR7EC`).
|
|
251
|
+
|
|
252
|
+
- A panel toolbar's `toggle: { action, name }` (Workbench panels, `CollapsiblePanel`) now documents `name` as the panel's title-style name. Pass `name: "Navigator"`, not `"navigator"`: `name` labels the toggle group and completes the default "Show …" / "Hide …" label, which kerf never recapitalizes. The docs, demos, and recipes passed lowercase names, which produced sentence-style accessible names such as "Show output". They now pass title-style names. The catalog shell names its sidebar "Kerf Catalog" and its settings rail toggle reads "Hide Settings" (they read "Kerf catalog" and "Hide settings"). `KUI-D040` now reports a lowercase `name` in a `toggle` object literal that also has an `action` (`KF-EJ32ZH`).
|
|
253
|
+
- A `NavStack` root view's title now defaults to the `xlarge` page-heading size, and steps down to `large` once a back control shares the bar. An explicit `titleSize` still wins. A Workbench panel's stack keeps `large` at every depth, so narrow rails stay quieter than the work-area title (`KF-2XWXFR`).
|
|
254
|
+
- The built-in close control of a `wa-dialog` or `wa-drawer` now matches a borderless single `ToolbarControlGroup` button, the close a toolbar-composed dialog uses: a 40px circle with no border and the neutral hover fill, instead of Web Awesome's 35px rounded square (`KF-5B5C4W`).
|
|
255
|
+
- A capped, scrolling `Select` listbox, `PopupMenu` menu, or phone-width `PopupMenu` submenu now shows that it scrolls: a soft shadow lines each edge with more choices beyond it. With overlay scrollbars (macOS, iOS, Android), a capped submenu whose bottom edge fell between rows looked like a complete short list. The cue is CSS only, the same on every popup in the shared popup contract, and absent when every choice fits (`KF-B6BTHW`).
|
|
256
|
+
- A centered `NavStack` title (and any lone `ToolbarText` in a `centerAlign="balanced"` center) now keeps the full-strength text color the root title has, instead of dimming to the quieter `large`-size color as it centers (`KF-DX3739`).
|
|
257
|
+
- Once a `NavStack` shows a back control, the view title centers on the toolbar like a platform navigation bar. It stays on the midpoint while the sides leave room, moves away from a wider side when they do not, and truncates with an ellipsis before it reaches the back control or a trailing group. At the root, and on a view that supplies `toolbar.center`, the title stays left-aligned. A lone `ToolbarText` in a `centerAlign="balanced"` toolbar gets this navigation-bar layout in any `Toolbar` (`KF-9YD9KV`).
|
|
258
|
+
- The `NavStack` back control is now a contained, bordered `ToolbarControlGroup` (the default pill), so it reads as a real control instead of a bare chevron beside the title (`KF-TBRP2P`).
|
|
180
259
|
- HTML and attribute escaping in the JSX runtime now runs one pass over a value instead of one `replace` pass per special character, about 2x faster on values that need escaping. Output is unchanged (`KF-3SHRTR`).
|
|
181
260
|
|
|
182
261
|
- Rename the internal TabNavigator implementation folder, files, and focused test files from `tab-scaffold` to `tab-navigator`; public imports and rendered DOM hooks stay stable.
|
package/ai/manifest.json
CHANGED
package/dist/overlay.d.ts
CHANGED
|
@@ -221,6 +221,36 @@ interface AnchorPositionOptions {
|
|
|
221
221
|
align?: 'start' | 'end';
|
|
222
222
|
/** Gap in px between the anchor and the element. Default `4`. */
|
|
223
223
|
gap?: number;
|
|
224
|
+
/**
|
|
225
|
+
* Gutter in px kept between the element and the viewport edges: the
|
|
226
|
+
* horizontal clamp stops this far inside the left and right edges, and a
|
|
227
|
+
* side fits only when the element ends this far inside the top or bottom
|
|
228
|
+
* edge. Default `0` (the bare viewport edge).
|
|
229
|
+
*/
|
|
230
|
+
viewportMargin?: number;
|
|
231
|
+
/**
|
|
232
|
+
* The element's height can shrink (it scrolls its overflow). When neither
|
|
233
|
+
* side has room for its full height, it takes the side with more room, and
|
|
234
|
+
* that room — from the gap to the viewport margin, in `el`'s CSS px — is
|
|
235
|
+
* published on `el` as the `--kerf-anchor-available-height` custom property
|
|
236
|
+
* on every reposition. Cap the element with it, for example
|
|
237
|
+
* `max-height: var(--kerf-anchor-available-height); overflow: auto`. The
|
|
238
|
+
* height measured for that choice honors the element's own `max-height`
|
|
239
|
+
* (read without the published property, so `max-height: min(300px,
|
|
240
|
+
* var(--kerf-anchor-available-height))` counts as a 300px cap), so an
|
|
241
|
+
* app-capped element that fits the preferred side stays there. A relative
|
|
242
|
+
* cap (`60%`, `calc()`, `min()`, `clamp()`) is resolved against the
|
|
243
|
+
* element's fixed-position containing block (normally the viewport), and a
|
|
244
|
+
* `max-height` transition on the element neither skews that read nor runs
|
|
245
|
+
* because of it. While an animation or transition is already driving
|
|
246
|
+
* `max-height` (an expand-from-0 entrance, a `@keyframes` animation), the
|
|
247
|
+
* cap is the value it is headed to, and it is left running. An above
|
|
248
|
+
* placement is pinned by its bottom edge (`bottom`, with `top: auto`), so
|
|
249
|
+
* the element grows and shrinks upward, away from the anchor, as its height
|
|
250
|
+
* changes between repositions; a below placement sets `top` with
|
|
251
|
+
* `bottom: auto`. Default `false`.
|
|
252
|
+
*/
|
|
253
|
+
fitHeight?: boolean;
|
|
224
254
|
}
|
|
225
255
|
/**
|
|
226
256
|
* One-shot: position `el` relative to `anchor` — below by default, flipping above
|
|
@@ -230,7 +260,10 @@ interface AnchorPositionOptions {
|
|
|
230
260
|
* ancestor makes itself the containing block for fixed descendants (a
|
|
231
261
|
* `transform`, `filter`, `perspective`, or `contain` on a wrapping `<dialog>`),
|
|
232
262
|
* `left`/`top` are translated (and unscaled) into that box's coordinates so `el`
|
|
233
|
-
* still lands next to the anchor.
|
|
263
|
+
* still lands next to the anchor. `viewportMargin` keeps a gutter from the
|
|
264
|
+
* viewport edges; `fitHeight` publishes the room on the chosen side so CSS can
|
|
265
|
+
* cap a scrolling element to it, and pins an above placement by `bottom`
|
|
266
|
+
* (with `top: auto`) so the element grows upward from the anchor. This is
|
|
234
267
|
* `popover()`'s placement core, usable on any element (an inline hint, a tooltip) —
|
|
235
268
|
* no overlay lifecycle. Pair with {@link autoReposition} to keep it glued while open.
|
|
236
269
|
*/
|
|
@@ -264,6 +297,15 @@ interface PopoverOptions {
|
|
|
264
297
|
align?: 'start' | 'end';
|
|
265
298
|
/** Gap in px between the anchor and the popover. Default `4`. */
|
|
266
299
|
gap?: number;
|
|
300
|
+
/** Gutter in px kept from the viewport edges. See `AnchorPositionOptions.viewportMargin`. Default `0`. */
|
|
301
|
+
viewportMargin?: number;
|
|
302
|
+
/**
|
|
303
|
+
* Fit a scrolling popover to the room beside the anchor: take the roomier
|
|
304
|
+
* side when neither fits and publish that room as
|
|
305
|
+
* `--kerf-anchor-available-height`. See `AnchorPositionOptions.fitHeight`.
|
|
306
|
+
* Default `false`.
|
|
307
|
+
*/
|
|
308
|
+
fitHeight?: boolean;
|
|
267
309
|
/**
|
|
268
310
|
* Which user actions dismiss the popover. Default `['outside']` (a click
|
|
269
311
|
* outside the popover, the anchor exempt). Pass `false` to close only via `close()`.
|
package/dist/overlay.js
CHANGED
|
@@ -782,36 +782,172 @@ function form(fields, options = {}) {
|
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
// src/overlay-position.ts
|
|
785
|
+
var AVAILABLE_HEIGHT = "--kerf-anchor-available-height";
|
|
785
786
|
function positionAnchored(el, anchor, options = {}) {
|
|
786
|
-
const {
|
|
787
|
+
const {
|
|
788
|
+
placement = "bottom",
|
|
789
|
+
align = "start",
|
|
790
|
+
gap = 4,
|
|
791
|
+
viewportMargin = 0,
|
|
792
|
+
fitHeight = false
|
|
793
|
+
} = options;
|
|
787
794
|
el.style.position = "fixed";
|
|
788
795
|
el.style.margin = "0";
|
|
789
796
|
const anchorRect = anchor.getBoundingClientRect();
|
|
790
797
|
const elementRect = el.getBoundingClientRect();
|
|
791
798
|
const viewportWidth = window.innerWidth;
|
|
792
799
|
const viewportHeight = window.innerHeight;
|
|
800
|
+
const height = fitHeight ? fullHeight(el, elementRect) : elementRect.height;
|
|
793
801
|
const belowTop = anchorRect.bottom + gap;
|
|
794
|
-
const
|
|
802
|
+
const roomBelow = viewportHeight - viewportMargin - belowTop;
|
|
803
|
+
const roomAbove = anchorRect.top - gap - viewportMargin;
|
|
795
804
|
let below = placement !== "top";
|
|
796
|
-
if (
|
|
797
|
-
below =
|
|
798
|
-
else if (
|
|
799
|
-
|
|
805
|
+
if (height > roomBelow && height > roomAbove) {
|
|
806
|
+
if (fitHeight) below = roomBelow >= roomAbove;
|
|
807
|
+
} else if (below && height > roomBelow) below = false;
|
|
808
|
+
else if (!below && height > roomAbove) below = true;
|
|
800
809
|
let left = align === "end" ? anchorRect.right - elementRect.width : anchorRect.left;
|
|
801
|
-
left = Math.max(
|
|
810
|
+
left = Math.max(
|
|
811
|
+
viewportMargin,
|
|
812
|
+
Math.min(left, viewportWidth - viewportMargin - elementRect.width)
|
|
813
|
+
);
|
|
802
814
|
const [originX, originY, scaleX, scaleY] = fixedOrigin(el);
|
|
803
815
|
el.style.left = `${(left - originX) / scaleX}px`;
|
|
804
|
-
|
|
816
|
+
if (fitHeight) {
|
|
817
|
+
const room = Math.max(0, below ? roomBelow : roomAbove);
|
|
818
|
+
el.style.setProperty(AVAILABLE_HEIGHT, `${room / scaleY}px`);
|
|
819
|
+
if (!below) {
|
|
820
|
+
const edge = anchorRect.top - gap;
|
|
821
|
+
el.style.top = "auto";
|
|
822
|
+
el.style.bottom = `${(fixedBottom(el, originY, scaleY) - edge) / scaleY}px`;
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
el.style.bottom = "auto";
|
|
826
|
+
}
|
|
827
|
+
const top = below ? belowTop : anchorRect.top - gap - height;
|
|
828
|
+
el.style.top = `${(top - originY) / scaleY}px`;
|
|
805
829
|
}
|
|
806
|
-
function
|
|
830
|
+
function fullHeight(el, rect) {
|
|
831
|
+
const scale = el.offsetHeight ? rect.height / el.offsetHeight : 1;
|
|
832
|
+
const full = rect.height + Math.max(0, el.scrollHeight - el.clientHeight) * scale;
|
|
833
|
+
return Math.min(full, ownMaxHeight(el) * scale);
|
|
834
|
+
}
|
|
835
|
+
var VERTICAL_EDGES = [
|
|
836
|
+
"paddingTop",
|
|
837
|
+
"paddingBottom",
|
|
838
|
+
"borderTopWidth",
|
|
839
|
+
"borderBottomWidth"
|
|
840
|
+
];
|
|
841
|
+
var UNBOUNDED_ROOM = "9999999px";
|
|
842
|
+
var SWAPPED = [
|
|
843
|
+
AVAILABLE_HEIGHT,
|
|
844
|
+
"transition-property",
|
|
845
|
+
"transition-duration",
|
|
846
|
+
"transition-delay"
|
|
847
|
+
];
|
|
848
|
+
function ownMaxHeight(el) {
|
|
849
|
+
const style = window.getComputedStyle(el);
|
|
850
|
+
const maxHeight = animatedMaxHeight(el) ?? swappedMaxHeight(el, style);
|
|
851
|
+
const inset = style.boxSizing === "border-box" ? 0 : VERTICAL_EDGES.reduce(
|
|
852
|
+
(sum, edge) => sum + (Number.parseFloat(style[edge]) || 0),
|
|
853
|
+
0
|
|
854
|
+
);
|
|
855
|
+
if (maxHeight.endsWith("px")) return Number.parseFloat(maxHeight) + inset;
|
|
856
|
+
if (/^[a-z-]+$|var\(/.test(maxHeight)) return Infinity;
|
|
857
|
+
return resolveHeight(el, maxHeight) + inset;
|
|
858
|
+
}
|
|
859
|
+
function swappedMaxHeight(el, style) {
|
|
860
|
+
const inline = el.style;
|
|
861
|
+
const saved = SWAPPED.map((name) => [
|
|
862
|
+
name,
|
|
863
|
+
inline.getPropertyValue(name),
|
|
864
|
+
inline.getPropertyPriority(name)
|
|
865
|
+
]);
|
|
866
|
+
const lists = SWAPPED.map((name) => style.getPropertyValue(name).split(","));
|
|
867
|
+
const transitions = lists[1];
|
|
868
|
+
if (transitions[0] && transitions[0] !== "none") {
|
|
869
|
+
for (let index = 1; index < 4; index++) {
|
|
870
|
+
const items = lists[index];
|
|
871
|
+
inline.setProperty(
|
|
872
|
+
SWAPPED[index],
|
|
873
|
+
[
|
|
874
|
+
...transitions.map((_, entry) => items[entry % items.length]),
|
|
875
|
+
index > 1 ? "0s" : "max-height"
|
|
876
|
+
].join(),
|
|
877
|
+
"important"
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
inline.setProperty(AVAILABLE_HEIGHT, UNBOUNDED_ROOM);
|
|
882
|
+
const maxHeight = style.maxHeight;
|
|
883
|
+
saved.forEach(([name, value, priority], index) => {
|
|
884
|
+
if (value) inline.setProperty(name, value, priority);
|
|
885
|
+
else inline.removeProperty(name);
|
|
886
|
+
if (!index) style.getPropertyValue("max-height");
|
|
887
|
+
});
|
|
888
|
+
return maxHeight;
|
|
889
|
+
}
|
|
890
|
+
function animatedMaxHeight(el) {
|
|
891
|
+
const animations = el.getAnimations?.() ?? [];
|
|
892
|
+
for (let index = animations.length - 1; index >= 0; index--) {
|
|
893
|
+
const animation = animations[index];
|
|
894
|
+
const effect = animation.effect;
|
|
895
|
+
const frames = (effect?.getKeyframes() ?? []).filter(
|
|
896
|
+
(frame) => frame["maxHeight"] != null
|
|
897
|
+
);
|
|
898
|
+
if (!frames.length) continue;
|
|
899
|
+
if (animation.playState === "finished") return void 0;
|
|
900
|
+
const { direction, currentIteration } = effect.getComputedTiming();
|
|
901
|
+
const odd = (currentIteration ?? 0) % 2 === 1;
|
|
902
|
+
const reversed = (direction === "reverse" || direction === "alternate" && odd || direction === "alternate-reverse" && !odd) !== animation.playbackRate < 0;
|
|
903
|
+
return String(frames[reversed ? 0 : frames.length - 1]["maxHeight"]);
|
|
904
|
+
}
|
|
905
|
+
return void 0;
|
|
906
|
+
}
|
|
907
|
+
function resolveHeight(el, height) {
|
|
908
|
+
return withFixedProbe(
|
|
909
|
+
el,
|
|
910
|
+
"width:0;box-sizing:content-box;min-height:0;max-height:none;visibility:hidden",
|
|
911
|
+
(probe) => {
|
|
912
|
+
probe.style.height = height;
|
|
913
|
+
if (!probe.style.height) return Infinity;
|
|
914
|
+
return Number.parseFloat(window.getComputedStyle(probe).height);
|
|
915
|
+
},
|
|
916
|
+
Infinity
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
function withFixedProbe(el, css, measure, detached) {
|
|
807
920
|
const parent = el.parentNode;
|
|
808
|
-
if (!parent) return
|
|
921
|
+
if (!parent) return detached;
|
|
809
922
|
const probe = el.ownerDocument.createElement("div");
|
|
810
|
-
probe.style.cssText =
|
|
923
|
+
probe.style.cssText = `position:fixed;left:0;top:0;display:block;margin:0;padding:0;border:0;${css}`;
|
|
811
924
|
parent.insertBefore(probe, el);
|
|
812
|
-
const
|
|
925
|
+
const result = measure(probe);
|
|
813
926
|
probe.remove();
|
|
814
|
-
return
|
|
927
|
+
return result;
|
|
928
|
+
}
|
|
929
|
+
function fixedOrigin(el) {
|
|
930
|
+
return withFixedProbe(
|
|
931
|
+
el,
|
|
932
|
+
"width:1px;height:1px",
|
|
933
|
+
(probe) => {
|
|
934
|
+
const rect = probe.getBoundingClientRect();
|
|
935
|
+
return [rect.left, rect.top, rect.width || 1, rect.height || 1];
|
|
936
|
+
},
|
|
937
|
+
[0, 0, 1, 1]
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
function fixedBottom(el, originY, scaleY) {
|
|
941
|
+
const fallback = originY + window.innerHeight * scaleY;
|
|
942
|
+
return withFixedProbe(
|
|
943
|
+
el,
|
|
944
|
+
"width:1px;height:1px;top:auto;bottom:0",
|
|
945
|
+
(probe) => {
|
|
946
|
+
const rect = probe.getBoundingClientRect();
|
|
947
|
+
return rect.height ? rect.bottom : fallback;
|
|
948
|
+
},
|
|
949
|
+
fallback
|
|
950
|
+
);
|
|
815
951
|
}
|
|
816
952
|
function autoReposition(el, anchor, options = {}) {
|
|
817
953
|
const reposition = () => positionAnchored(el, anchor, options);
|
|
@@ -832,6 +968,8 @@ function popover(anchor, content, options = {}) {
|
|
|
832
968
|
placement = "bottom",
|
|
833
969
|
align = "start",
|
|
834
970
|
gap = 4,
|
|
971
|
+
viewportMargin,
|
|
972
|
+
fitHeight,
|
|
835
973
|
dismiss = ["outside"],
|
|
836
974
|
initialFocus = false,
|
|
837
975
|
outsideIgnore,
|
|
@@ -854,7 +992,9 @@ function popover(anchor, content, options = {}) {
|
|
|
854
992
|
stopReposition = autoReposition(handle.el, anchor, {
|
|
855
993
|
placement,
|
|
856
994
|
align,
|
|
857
|
-
gap
|
|
995
|
+
gap,
|
|
996
|
+
viewportMargin,
|
|
997
|
+
fitHeight
|
|
858
998
|
});
|
|
859
999
|
} catch (error) {
|
|
860
1000
|
handle.close();
|
|
@@ -962,6 +1102,8 @@ function tooltip(anchor, content, options = {}) {
|
|
|
962
1102
|
placement = "top",
|
|
963
1103
|
align = "start",
|
|
964
1104
|
gap = 4,
|
|
1105
|
+
viewportMargin,
|
|
1106
|
+
fitHeight,
|
|
965
1107
|
native = false
|
|
966
1108
|
} = options;
|
|
967
1109
|
const body = typeof content === "function" ? content : typeof content === "string" ? jsx("span", { class: `${className}__text`, children: content }) : content;
|
|
@@ -981,7 +1123,13 @@ function tooltip(anchor, content, options = {}) {
|
|
|
981
1123
|
handle.el.setAttribute("role", role);
|
|
982
1124
|
let stop;
|
|
983
1125
|
try {
|
|
984
|
-
stop = autoReposition(handle.el, anchor, {
|
|
1126
|
+
stop = autoReposition(handle.el, anchor, {
|
|
1127
|
+
placement,
|
|
1128
|
+
align,
|
|
1129
|
+
gap,
|
|
1130
|
+
viewportMargin,
|
|
1131
|
+
fitHeight
|
|
1132
|
+
});
|
|
985
1133
|
} catch (error) {
|
|
986
1134
|
handle.close();
|
|
987
1135
|
throw error;
|
package/dist/overlay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/overlay-core.ts","../src/overlay-choice.ts","../src/overlay-confirm.ts","../src/overlay-prompt.ts","../src/overlay-form.ts","../src/overlay-position.ts","../src/overlay-popover.ts","../src/overlay-toast.ts","../src/overlay-tooltip.ts"],"names":["options"],"mappings":"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","file":"overlay.js","sourcesContent":["/**\n * Shared overlay lifecycle and document arbitration implementation.\n *\n * Every real kerf app hand-rolls this: `toElement → body.appendChild → mount →\n * wire dismissal → remove`, plus the fiddly parts (Escape, backdrop / outside\n * click, focus trap, restoring focus on close). `window.confirm` is a no-op in\n * Tauri WKWebViews, so a hand-built overlay is mandatory there. This subpath\n * blesses the pattern through `overlay()`, with anchored surfaces and dialog\n * helpers building on it. No per-instance framework state: each call owns its\n * DOM + listeners in a closure and returns a handle.\n *\n * import { overlay, confirm, toast } from 'kerfjs/overlay';\n *\n * const ok = await confirm('Delete this file?', { danger: true });\n * toast('Saved');\n * const dialog = overlay(<Settings />, { dismiss: ['escape', 'backdrop'] });\n * // …later: dialog.close(); or await dialog.result;\n *\n * Structural only — kerf ships no CSS. The wrapper gets your `className`; style\n * the backdrop / centering / animation yourself.\n */\nimport { attach } from './attach.js';\nimport { devHooks } from './dev-hooks.js';\nimport type { SafeHtml } from './jsx-runtime.js';\nimport { mount, type MountResult } from './mount.js';\nimport { OVERLAY_HOST_SELECTOR } from './utils/overlay-host.js';\n\n/** A user-initiated dismissal trigger. */\nexport type DismissTrigger = 'escape' | 'backdrop' | 'outside';\n\n/** Content for an overlay: static `SafeHtml`, or a render function `mount()` drives reactively. */\nexport type OverlayContent = SafeHtml | (() => MountResult);\n\n/** Options for {@link overlay}. */\nexport interface OverlayOptions {\n /**\n * Where to append the overlay wrapper. Default `document.body`. A container\n * inside an open modal `<dialog>` (including one slotted into a shadow-root\n * modal such as Web Awesome's `wa-dialog`) is already interactive, so the\n * surface is not lifted, and it closes when that dialog closes.\n */\n container?: Element;\n /** Class on the wrapper element (you style it — kerf ships no CSS). Default `'kerf-overlay'`. */\n className?: string;\n /**\n * Which user actions dismiss the overlay. Default `['escape', 'backdrop']`.\n * `'backdrop'` = a click on the wrapper itself (not its content); `'outside'`\n * = a click anywhere outside the wrapper (for anchored popovers). `false`\n * disables user dismissal (close it programmatically).\n */\n dismiss?: DismissTrigger | DismissTrigger[] | false;\n /**\n * Where focus lands on open: a selector, `true` (first focusable element, or\n * the wrapper if none), or `false` (leave focus alone). Default `true`.\n */\n initialFocus?: string | boolean;\n /**\n * Trap Tab / Shift+Tab within the overlay while open and mark it\n * `role=\"dialog\"` / `aria-modal=\"true\"`. Default `true`. Set `false` for a\n * non-modal popover.\n */\n trap?: boolean;\n /** ARIA role for the wrapper when `trap` is on. Default `'dialog'`. */\n role?: string;\n /** Called on any user-initiated dismissal (before `close()` runs). */\n onDismiss?: () => void;\n /** For `'outside'` dismissal: clicks on these elements do NOT count as outside (e.g. the trigger button). */\n outsideIgnore?: Element | readonly Element[];\n /**\n * Opt into the browser **top layer** (`docs/19-native-overlay-backing.md`).\n * When `true` and the engine supports it, a modal overlay (`trap: true`) is\n * hosted in a `<dialog>` opened with `.showModal()` — real inerting of the rest\n * of the document + guaranteed stacking above any `z-index` — and a non-modal\n * one (`trap: false`) uses the Popover API (`[popover]` + `showPopover()`).\n * Feature-detected; falls back to today's plain `<div>` where unsupported.\n *\n * The `render` slot + promise API are unchanged — kerf just hosts your markup\n * in a `<dialog>` / `[popover]` instead of a `<div>`. Two caveats: native\n * `<dialog>` / `[popover]` carry **UA default styles** (a `::backdrop`,\n * centering, border, padding) that kerf does not reset — style the element (and\n * its `::backdrop`) via `className`; and `container` is effectively a **no-op**\n * for visual position, since the top layer ignores where the element lives in\n * the DOM. Default `false`.\n */\n native?: boolean;\n}\n\n/** Handle returned by {@link overlay}. Holds no framework state — it's a closure. */\nexport interface OverlayHandle {\n /** The wrapper element (mounted into, appended to `container`). */\n el: HTMLElement;\n /** Tear down: dispose the mount, remove listeners + the node, restore focus, resolve `result`. Idempotent. */\n close(result?: unknown): void;\n /** Resolves with the value passed to `close()` (or `undefined` on user dismissal). */\n result: Promise<unknown>;\n}\n\nconst FOCUSABLE =\n 'a[href],area[href],button:not([disabled]),input:not([disabled]),' +\n 'select:not([disabled]),textarea:not([disabled]),iframe,' +\n '[tabindex]:not([tabindex=\"-1\"]),[contenteditable=\"true\"]';\n\nfunction focusable(root: Element): HTMLElement[] {\n const items = Array.from(\n root.querySelectorAll<HTMLElement>(FOCUSABLE),\n ).filter((el) => !el.hasAttribute('hidden') && el.tabIndex >= 0);\n // WebKit on macOS follows the system's keyboard-navigation preference and\n // may omit implicitly tabbable controls from sequential focus navigation.\n // A focus trap promises stable traversal through every eligible control, so\n // make those implicit stops explicit while preserving authored tabindexes.\n for (const el of items) if (!el.hasAttribute('tabindex')) el.tabIndex = 0;\n return items;\n}\n\n// Native top-layer feature detection (KF-526). Modal → `<dialog>.showModal()`;\n// non-modal → the Popover API. Each is independent — an engine may ship one\n// without the other — and both fall back to the plain `<div>` where absent.\nfunction supportsDialog(): boolean {\n return (\n typeof HTMLDialogElement !== 'undefined' &&\n typeof HTMLDialogElement.prototype.showModal === 'function'\n );\n}\nfunction supportsPopover(): boolean {\n return (\n typeof HTMLElement !== 'undefined' &&\n typeof HTMLElement.prototype.showPopover === 'function'\n );\n}\n\n// Stacked-surface arbitration (KF-CW7GJ8: a tooltip inside a modal used to\n// become \"topmost\" and disable the modal's Escape, backdrop, and focus trap).\n// Every open overlay — native or fallback — is recorded, in open order, on a\n// per-document stack. An input kind (Escape, a backdrop / outside click, Tab\n// for the trap) belongs to the topmost surface that HANDLES that kind; a\n// surface that doesn't handle it is transparent, except that a modal surface\n// is a barrier nothing beneath it sees past. A tooltip (or any overlay with no\n// dismiss trigger and no trap) handles nothing, so it never intercepts. The\n// `handled` set marks an event a surface already acted on, so one click that is\n// both \"outside\" a popover and on the modal backdrop closes only the popover.\nconst OVERLAY_ARBITRATION = Symbol('kerf.overlayArbitration');\n\n/** An arbitration input: a dismiss trigger, or Tab for the focus trap. */\ntype ArbitrationKind = DismissTrigger | 'trap';\n\ninterface OverlaySurface {\n el: HTMLElement;\n triggers: readonly DismissTrigger[];\n modal: boolean;\n /** Where focus returns on close; re-pointed when the surface holding it closes first. */\n restoreTo: Element | null;\n /** Non-modal only: move (or re-show) into the top layer above a newly opened modal `<dialog>`. */\n liftAbove?: () => void;\n}\n\ninterface OverlayArbitration {\n stack: OverlaySurface[];\n handled: WeakSet<Event>;\n /** Document capture listener, installed only while the stack is non-empty. */\n onToggle: (event: Event) => void;\n /** Open shadow roots carrying `onToggle` too (a shadow `toggle` never reaches the document). */\n shadowRoots: Set<ShadowRoot>;\n}\n\ntype ArbitratedDocument = Document & {\n [OVERLAY_ARBITRATION]?: OverlayArbitration;\n};\n\nfunction overlayArbitration(doc: Document): OverlayArbitration {\n const arbitrated = doc as ArbitratedDocument;\n return (arbitrated[OVERLAY_ARBITRATION] ??= {\n stack: [],\n handled: new WeakSet(),\n // KF-AHY6H4: an app-owned modal `<dialog>` opened outside kerf would leave\n // the kerf surfaces already showing inert beneath it. A dialog fires\n // `toggle` after it opens (it does not bubble, so listen in the capture\n // phase); re-host above it exactly as kerf does for its own dialogs, which\n // are skipped here because `overlay()` already re-hosted synchronously.\n onToggle: (event) => {\n const dialog = event.target as Element;\n const arbitration = arbitrated[OVERLAY_ARBITRATION] as OverlayArbitration;\n if (\n dialog.tagName === 'DIALOG' &&\n !arbitration.stack.some(({ el }) => el === dialog) &&\n isOpenModal(dialog, arbitration)\n )\n rehostAbove(arbitration, dialog);\n },\n shadowRoots: new Set(),\n });\n}\n\n// Every open shadow root in `root`, nested ones included (KF-1FGTX1: Web\n// Awesome's `wa-dialog` / `wa-drawer` keep their modal `<dialog>` in one, where\n// neither `querySelectorAll` nor a document `toggle` listener can see it). A\n// closed shadow root is invisible by design. One element walk per `overlay()`\n// open — a user-paced event — so the cost stays off every render path.\nfunction openShadowRoots(\n root: Document | ShadowRoot,\n found: ShadowRoot[] = [],\n): ShadowRoot[] {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {\n const shadow = (node as Element).shadowRoot;\n if (shadow !== null) {\n found.push(shadow);\n openShadowRoots(shadow, found);\n }\n }\n return found;\n}\n\n// Install the `toggle` watch on every open shadow root not yet watched, so an\n// app-owned modal `<dialog>` opening inside one re-hosts the surfaces showing.\nfunction watchShadowRoots(\n arbitration: OverlayArbitration,\n roots: readonly ShadowRoot[],\n): void {\n for (const root of roots)\n if (!arbitration.shadowRoots.has(root)) {\n arbitration.shadowRoots.add(root);\n root.addEventListener('toggle', arbitration.onToggle, true);\n }\n}\n\n// Is `el` inside `dialog` in the flat tree — the rendered tree, where content\n// slotted into a shadow host renders inside the host's shadow `<dialog>`?\nfunction renderedInside(el: Element, dialog: Element): boolean {\n for (let node: Element | null = el; node !== null;) {\n if (node === dialog) return true;\n const root = node.getRootNode();\n node =\n node.assignedSlot ??\n node.parentElement ??\n (root instanceof ShadowRoot ? root.host : null);\n }\n return false;\n}\n\n// Every non-modal kerf surface already open is blocked beneath a modal\n// `<dialog>` that just opened: lift (or re-show) it above (KF-FJ9VD8). One\n// rendered inside that dialog — including a slot-hosted one in a shadow\n// host's light DOM — is part of its subtree already.\nfunction rehostAbove(arbitration: OverlayArbitration, dialog: Element): void {\n for (const { el, liftAbove } of arbitration.stack)\n if (el !== dialog && el.isConnected && !renderedInside(el, dialog))\n liftAbove?.();\n}\n\n// Is `dialog` an open modal `<dialog>`? kerf's own are known from the stack;\n// an app-owned one is found through `:modal`, which an older engine may not\n// parse (then only kerf's own count).\nfunction isOpenModal(\n dialog: Element,\n arbitration: OverlayArbitration,\n): boolean {\n if (!dialog.isConnected || (dialog as HTMLDialogElement).open !== true)\n return false;\n if (arbitration.stack.some(({ el, modal }) => modal && el === dialog))\n return true;\n try {\n return dialog.matches(':modal');\n } catch {\n return false; // `:modal` unsupported\n }\n}\n\n// Is a modal `<dialog>` open (KF-0V9RTE: a plain surface opened in that state\n// is inert and painted beneath the dialog's top layer)?\n// The open shadow roots count too (KF-1FGTX1: a `wa-dialog`'s modal).\nfunction modalDialogOpen(\n doc: Document,\n arbitration: OverlayArbitration,\n shadowRoots: readonly ShadowRoot[],\n): boolean {\n return (\n arbitration.stack.some(({ el }) => isOpenModal(el, arbitration)) ||\n [doc, ...shadowRoots].some((root) =>\n Array.from(root.querySelectorAll('dialog[open]')).some((dialog) =>\n isOpenModal(dialog, arbitration),\n ),\n )\n );\n}\n\n// The modal context `el` renders in, resolved across shadow boundaries\n// (KF-F52T5D: Web Awesome's `wa-dialog` / `wa-drawer` keep their modal\n// `<dialog>` in a shadow root, so `closest('dialog')` from slotted light-DOM\n// content found nothing and the surface landed, inert, in `document.body`).\n// Walks the light-DOM ancestors, continuing from a shadow tree to its host.\n// The nearest `<dialog>` decides, as before; so does a shadow host whose slot\n// for the walked-from child sits inside a `<dialog>` in its shadow root. Only\n// an OPEN MODAL dialog yields a context: `dialog` is that element, `root` the\n// light-DOM element a host slot belongs in — the dialog itself, or the shadow\n// host (content appended to it is slotted into the dialog).\ninterface ModalContext {\n dialog: Element;\n root: Element;\n}\nfunction modalContext(\n el: Element,\n arbitration: OverlayArbitration,\n): ModalContext | undefined {\n let child: Element | undefined;\n for (let node: Element | null = el; node !== null;) {\n const dialog =\n node.localName === 'dialog'\n ? node\n : child?.assignedSlot?.closest('dialog');\n if (dialog != null)\n return isOpenModal(dialog, arbitration)\n ? { dialog, root: node }\n : undefined;\n child = node;\n const root = node.getRootNode();\n node =\n node.parentElement ?? (root instanceof ShadowRoot ? root.host : null);\n }\n return undefined;\n}\n\n// The in-dialog host slot (KF-FBHQEP: a surface lifted over a modal\n// `<dialog>` is visible but inert, because every engine inerts content outside\n// the modal). A `popover()` / `tooltip()` whose anchor renders inside an open\n// modal `<dialog>` (see `modalContext`) renders into that context's\n// `[data-kerf-overlay-host]` element instead, so it is part of the modal\n// subtree and fully interactive. An app-owned native dialog opts in by marking\n// an element; a dialog kerf opened itself, and a shadow-hosted modal (whose\n// author cannot reach inside the shadow root), get one on demand —\n// `display: contents` so it adds no layout box, and `data-morph-skip` +\n// `data-morph-preserve` so the content's own `mount()` neither re-renders nor\n// removes it (`utils/overlay-host.ts` makes the slot a nested-mount boundary).\nexport function overlayHost(anchor: Element): Element | undefined {\n const arbitration = overlayArbitration(document);\n const context = modalContext(anchor, arbitration);\n if (context === undefined) return undefined;\n const { dialog, root } = context;\n let host = Array.from(root.querySelectorAll(OVERLAY_HOST_SELECTOR)).find(\n (el) => modalContext(el, arbitration)?.root === root,\n );\n if (\n host === undefined &&\n (root !== dialog || arbitration.stack.some(({ el }) => el === dialog))\n ) {\n root.insertAdjacentHTML(\n 'beforeend',\n '<div data-kerf-overlay-host data-morph-skip data-morph-preserve style=\"display:contents\"></div>',\n );\n host = root.lastElementChild as Element;\n }\n return host;\n}\n\n// `self` is on the stack and handles `kind` (its callers are exactly the\n// handlers it installed for that kind), so it owns `kind` unless some surface\n// above it handles `kind` too or is modal.\nfunction ownsInput(\n stack: readonly OverlaySurface[],\n self: OverlaySurface,\n kind: ArbitrationKind,\n): boolean {\n for (let i = stack.indexOf(self) + 1; i < stack.length; i++) {\n const above = stack[i];\n if (above.modal || (kind !== 'trap' && above.triggers.includes(kind)))\n return false;\n }\n return true;\n}\n\n// Validate a string `initialFocus` BEFORE any DOM mutation: `querySelector`\n// throws a generic `SyntaxError` DOMException on a malformed selector, and the\n// focus pass runs last — after the wrapper, mount, listeners, and stack entry\n// already exist — so an unvalidated selector would strand all of them with no\n// handle to close. An inert fragment parses the selector without matching.\nfunction assertInitialFocusSelector(selector: string): void {\n try {\n document.createDocumentFragment().querySelector(selector);\n } catch (cause) {\n throw new Error(\n `overlay(): invalid initialFocus selector ${JSON.stringify(selector)}`,\n { cause },\n );\n }\n}\n\n/**\n * Open an overlay: append a wrapper to `container`, `mount()` `content` inside\n * it, wire the requested dismissals + (optionally) a focus trap, and return a\n * handle. Concurrent fallback overlays dismiss from the top down. See\n * {@link OverlayOptions}.\n *\n * Construction is transactional: a malformed `initialFocus` selector throws a\n * descriptive error before anything is touched, and if a later setup phase\n * throws (the content's first render, `showModal()` / `showPopover()`), every\n * piece already installed — wrapper node, mount, listeners, fallback-stack\n * entry, top-layer state, moved focus — is rolled back before the original\n * error is rethrown.\n */\nexport function overlay(\n content: OverlayContent,\n options: OverlayOptions = {},\n): OverlayHandle {\n const {\n container = document.body,\n className = 'kerf-overlay',\n dismiss = ['escape', 'backdrop'],\n initialFocus = true,\n trap = true,\n role = 'dialog',\n onDismiss,\n outsideIgnore,\n native = false,\n } = options;\n\n if (typeof initialFocus === 'string')\n assertInitialFocusSelector(initialFocus);\n\n const triggers: readonly DismissTrigger[] =\n dismiss === false ? [] : Array.isArray(dismiss) ? dismiss : [dismiss];\n\n // Native top-layer backing (KF-526), feature-detected: a modal overlay\n // (`trap`) → `<dialog>.showModal()`; a non-modal one → `[popover]`. Each falls\n // back to the plain `<div>` where the API is missing, so behavior is unchanged\n // on unsupporting engines. A surface opened while a modal `<dialog>` is open\n // is lifted the same way even without `native` (KF-0V9RTE), because a plain\n // `<div>` would be inert and painted beneath the dialog.\n const arbitration = overlayArbitration(document);\n // A surface placed inside an open modal `<dialog>` (its host slot, or an\n // explicit `container` there, including one slotted into a shadow-root\n // modal) is already part of the modal subtree: no lift.\n const containerContext = modalContext(container, arbitration);\n const shadowRoots = openShadowRoots(document);\n const liftOverModal =\n !native &&\n containerContext === undefined &&\n modalDialogOpen(document, arbitration, shadowRoots);\n const topLayer = native || liftOverModal;\n const useDialog = topLayer && trap && supportsDialog();\n const usePopover = topLayer && !trap && supportsPopover();\n\n const wrapper: HTMLElement = useDialog\n ? document.createElement('dialog')\n : document.createElement('div');\n wrapper.className = className;\n if (usePopover) wrapper.setAttribute('popover', 'manual');\n // `<dialog>.showModal()` conveys modality natively (role + inert), so the ARIA\n // attributes are only needed on the plain-`<div>` fallback.\n if (trap && !useDialog) {\n wrapper.setAttribute('role', role);\n wrapper.setAttribute('aria-modal', 'true');\n }\n const surface: OverlaySurface = {\n el: wrapper,\n triggers,\n modal: trap,\n restoreTo: document.activeElement,\n };\n\n // Does this surface own `kind` right now (and has no surface acted on `event`)?\n const owns = (kind: ArbitrationKind, event?: Event): boolean =>\n !(event !== undefined && arbitration.handled.has(event)) &&\n ownsInput(arbitration.stack, surface, kind);\n\n const removers: Array<() => void> = [];\n const resultBox: { resolve?: (value: unknown) => void } = {};\n const result = new Promise<unknown>((resolve) => {\n resultBox.resolve = resolve;\n });\n const state = { closed: false };\n let disposeMount: (() => void) | undefined;\n let nativeOpened = false;\n\n // KF-FJ9VD8: a modal `<dialog>` kerf opens later would leave this non-modal\n // surface inert beneath it, so the opener calls this to move it into the top\n // layer — or, if it is already a `[popover]`, re-show it so it stacks above\n // the new dialog (the top layer orders by most recent show).\n if (!trap)\n surface.liftAbove = () => {\n if (!supportsPopover()) {\n devHooks.overlayBlockedByModal?.('hidden');\n return;\n }\n const el = wrapper as HTMLElement & {\n showPopover(): void;\n hidePopover(): void;\n };\n if (nativeOpened) el.hidePopover();\n else {\n wrapper.setAttribute('popover', 'manual');\n wrapper.style.inset = 'auto';\n }\n el.showPopover();\n nativeOpened = true;\n if (wrapper.querySelector(FOCUSABLE) !== null)\n devHooks.overlayBlockedByModal?.('inert');\n };\n\n // Also the construction rollback (see the transaction below): every step\n // tolerates a phase that never ran — no mount yet, no stack entry, never\n // appended, never shown.\n function close(value?: unknown): void {\n if (state.closed) return;\n state.closed = true;\n const { stack } = arbitration;\n const stackIndex = stack.indexOf(surface);\n if (stackIndex !== -1) stack.splice(stackIndex, 1);\n if (stack.length === 0) {\n document.removeEventListener('toggle', arbitration.onToggle, true);\n for (const root of arbitration.shadowRoots)\n root.removeEventListener('toggle', arbitration.onToggle, true);\n arbitration.shadowRoots.clear();\n }\n // A surface opened from inside this one would restore focus into a node\n // that is about to be detached: hand it this surface's own restore target.\n for (const other of stack)\n if (other.restoreTo !== null && wrapper.contains(other.restoreTo))\n other.restoreTo = surface.restoreTo;\n const active = document.activeElement;\n for (const remove of removers) remove();\n disposeMount?.();\n // Exit the top layer before removing the node, so the native close steps run\n // (restore inert, fire the dialog's `close`). `<dialog>` also restores focus\n // itself; kerf's manual restore below stays as the fallback path's behavior.\n if (nativeOpened) {\n nativeOpened = false;\n if (useDialog) (wrapper as HTMLDialogElement).close();\n else (wrapper as HTMLElement & { hidePopover(): void }).hidePopover();\n }\n wrapper.remove();\n restoreFocus(active);\n resultBox.resolve?.(value);\n }\n\n // Closing a surface that doesn't hold focus (an out-of-order close beneath\n // another open surface) must not pull focus out of the surface that does;\n // otherwise focus returns to where it was when this surface opened.\n function restoreFocus(active: Element | null): void {\n if (\n active instanceof HTMLElement &&\n active.isConnected &&\n arbitration.stack.some((other) => other.el.contains(active))\n ) {\n if (document.activeElement !== active) active.focus();\n return;\n }\n const { restoreTo } = surface;\n if (restoreTo instanceof HTMLElement && restoreTo.isConnected)\n restoreTo.focus();\n }\n\n function userDismiss(): void {\n onDismiss?.();\n close();\n }\n\n // One transaction: if any phase below throws (the content's first render,\n // `showModal()` / `showPopover()`, …), roll back whatever the earlier phases\n // installed via `close()` and rethrow the original error. No handle is\n // returned on failure, so nothing may outlive the call.\n try {\n container.appendChild(wrapper);\n disposeMount = mount(\n wrapper,\n typeof content === 'function' ? content : () => content,\n );\n // Watch for app-owned modal dialogs while any kerf surface is open.\n // Shadow roots are refreshed on every open; one attached after the latest\n // open is not watched until the next.\n if (arbitration.stack.push(surface) === 1)\n document.addEventListener('toggle', arbitration.onToggle, true);\n watchShadowRoots(arbitration, shadowRoots);\n\n // Enter the top layer after the content is mounted + connected.\n // `showModal()` moves focus into the dialog by default; kerf's\n // `initialFocus` pass below runs afterward and wins.\n if (useDialog) {\n (wrapper as HTMLDialogElement).showModal();\n nativeOpened = true;\n rehostAbove(arbitration, wrapper);\n } else if (usePopover) {\n (wrapper as HTMLElement & { showPopover(): void }).showPopover();\n nativeOpened = true;\n // Neutralize the UA `[popover] { inset: 0; margin: auto }` anchoring so\n // `positionAnchored` (which sets `top`/`left`) controls placement —\n // otherwise the retained `right`/`bottom` insets would stretch/center it.\n wrapper.style.inset = 'auto';\n }\n\n installDismissal();\n if (container.closest(OVERLAY_HOST_SELECTOR) !== null) {\n makeTabReachable();\n // KF-WZ9KQM: a slot-hosted surface leaves with its dialog even when its\n // anchor (moved out of the dialog) stays connected, so the anchor watch\n // never fires. Watch the wrapper itself: close as cleanup (no\n // `onDismiss`) and leave the open-order stack.\n removers.push(attach(wrapper, () => close));\n }\n // KF-F52T5D: a surface inside a modal context leaves when that dialog\n // closes, as cleanup (no `onDismiss`). Closing an app-owned or\n // shadow-hosted dialog removes nothing, and a shadow dialog's `close`\n // never reaches a light-DOM listener, so a caller could not watch it.\n if (containerContext !== undefined) {\n const { dialog } = containerContext;\n const onDialogClose = (): void => close();\n dialog.addEventListener('close', onDialogClose);\n removers.push(() => dialog.removeEventListener('close', onDialogClose));\n }\n applyInitialFocus();\n } catch (error) {\n close();\n throw error;\n }\n\n // What the lift cannot repair: no top-layer API at all, or a lifted\n // non-modal surface whose controls the modal still inerts.\n if (liftOverModal) {\n if (!useDialog && !usePopover) devHooks.overlayBlockedByModal?.('hidden');\n else if (usePopover && wrapper.querySelector(FOCUSABLE) !== null)\n devHooks.overlayBlockedByModal?.('inert');\n }\n\n return { el: wrapper, close, result };\n\n function installDismissal(): void {\n const wantEscape = triggers.includes('escape');\n if (useDialog) {\n // A native modal `<dialog>` confines Tab focus itself and surfaces Escape as\n // a `cancel` event. Take that over: `preventDefault` so kerf owns teardown\n // (and so Escape is swallowed when it isn't a dismiss trigger), then dismiss.\n // A surface stacked above that handles Escape (or is modal) owns it; a\n // stray `cancel` then leaves this dialog open.\n const onCancel = (event: Event): void => {\n event.preventDefault();\n if (wantEscape && owns('escape')) userDismiss();\n };\n wrapper.addEventListener('cancel', onCancel);\n removers.push(() => wrapper.removeEventListener('cancel', onCancel));\n } else if (wantEscape || trap) {\n const onKeydown = (event: KeyboardEvent): void => {\n if (wantEscape && event.key === 'Escape') {\n if (!owns('escape', event)) return;\n // `preventDefault` also withholds the close request from a native\n // `<dialog>` beneath, so one Escape closes exactly one surface.\n event.stopPropagation();\n event.preventDefault();\n arbitration.handled.add(event);\n userDismiss();\n return;\n }\n if (trap && event.key === 'Tab' && owns('trap')) {\n const items = focusable(wrapper);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const active = document.activeElement;\n const outside = !wrapper.contains(active);\n if (event.shiftKey && (active === first || outside)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (active === last || outside)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n document.addEventListener('keydown', onKeydown, true);\n removers.push(() =>\n document.removeEventListener('keydown', onKeydown, true),\n );\n }\n\n if (triggers.includes('backdrop')) {\n const onClick = (event: Event): void => {\n if (event.target !== wrapper || !owns('backdrop', event)) return;\n arbitration.handled.add(event);\n userDismiss();\n };\n wrapper.addEventListener('click', onClick);\n removers.push(() => wrapper.removeEventListener('click', onClick));\n }\n\n if (triggers.includes('outside')) {\n const ignore =\n outsideIgnore === undefined\n ? []\n : Array.isArray(outsideIgnore)\n ? outsideIgnore\n : [outsideIgnore];\n // Capture phase: the click that opened this overlay already passed\n // document's capture phase, so this never fires for that opening click.\n const onDocClick = (event: Event): void => {\n const target = event.target as Node | null;\n if (target === null) return;\n if (wrapper.contains(target)) return;\n if (ignore.some((el) => el === target || el.contains(target))) return;\n if (!owns('outside', event)) return;\n arbitration.handled.add(event);\n userDismiss();\n };\n document.addEventListener('click', onDocClick, true);\n removers.push(() =>\n document.removeEventListener('click', onDocClick, true),\n );\n }\n }\n\n // KF-QZ9SFG (slot-hosted controls unreachable by Tab in macOS WebKit): a\n // surface in an overlay host slot sits in the modal dialog's own Tab order,\n // not behind a kerf trap, so WebKit's system keyboard-navigation preference\n // would skip its implicitly tabbable controls. Make those stops explicit, as\n // the trap does — now, and again before each Tab so a control a re-render\n // added (or whose tabindex the morph dropped) is reachable too.\n function makeTabReachable(): void {\n focusable(wrapper);\n const onTab = (event: KeyboardEvent): void => {\n if (event.key === 'Tab') focusable(wrapper);\n };\n document.addEventListener('keydown', onTab, true);\n removers.push(() => document.removeEventListener('keydown', onTab, true));\n }\n\n function applyInitialFocus(): void {\n if (initialFocus !== false) {\n if (typeof initialFocus === 'string') {\n wrapper.querySelector<HTMLElement>(initialFocus)?.focus();\n } else {\n const first = focusable(wrapper)[0];\n if (first !== undefined) {\n first.focus();\n } else {\n wrapper.tabIndex = -1;\n wrapper.focus();\n }\n }\n }\n }\n}\n\n/**\n * Internal: run a promise-dialog helper's post-open wiring (required-slot\n * lookups, `delegate()` handlers, key listeners) as the tail of `overlay()`'s\n * construction transaction. `wire` registers a disposer for every listener it\n * installs via `track`; if any step throws, those listeners are removed, the\n * just-opened overlay is closed (node, mount, dismissal listeners, stack\n * entry, top-layer state, focus), and the original error is rethrown — so a\n * failed helper call returns no promise and leaves no open overlay behind.\n *\n * After construction, `guard` wraps a user-action handler (OK click, Enter).\n * KF-B0CFQP (a throwing `validate` callback): a throw inside a guarded action\n * is a programming error, so it closes the dialog (full teardown, focus\n * restored) and the returned promise REJECTS with the original error, instead\n * of the error escaping the event handler and stranding an open dialog whose\n * OK can never succeed. Otherwise the returned promise settles with\n * `settle(handle.result's value)`. Not re-exported from `kerfjs/overlay`.\n */\nexport function wireDialog<T>(\n handle: OverlayHandle,\n wire: (\n track: (dispose: () => void) => void,\n guard: (action: () => void) => () => void,\n ) => void,\n settle: (value: unknown) => T,\n): Promise<T> {\n const disposers: Array<() => void> = [];\n // Holds a guarded action's error AND is the close() value that marks the\n // failure, so only a throw that actually settles the dialog rejects it.\n const failure: unknown[] = [];\n try {\n wire(\n (dispose) => void disposers.push(dispose),\n (action) => () => {\n try {\n action();\n } catch (error) {\n failure[0] = error;\n handle.close(failure);\n }\n },\n );\n } catch (error) {\n disposers.forEach((dispose) => dispose());\n handle.close();\n throw error;\n }\n // One hop, not a chain: `settle` maps the value in the same reaction, so a\n // helper's promise settles as soon after `close()` as it always has.\n return handle.result.then((value) => {\n if (value === failure) throw failure[0];\n return settle(value);\n });\n}\n","/** `choice()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\n\n/** One choosable action in a {@link choice} dialog. */\nexport interface ChoiceAction<R> {\n value: R;\n label: string;\n className?: string;\n}\n\n/** Wiring slots for a {@link ChoiceOptions.render}. */\nexport interface ChoiceRenderSlots {\n message: string;\n actions: Array<Record<string, string>>;\n}\n\n/** Options for {@link choice}. */\nexport interface ChoiceOptions<R> {\n container?: Element;\n className?: string;\n title?: string;\n defaultValue?: R;\n native?: boolean;\n render?: (slots: ChoiceRenderSlots) => OverlayContent;\n}\n\n/** The N-way sibling of {@link confirm}. */\nexport function choice<R>(\n message: string,\n actions: ReadonlyArray<ChoiceAction<R>>,\n options: ChoiceOptions<R> = {},\n): Promise<R | null> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n defaultValue,\n native = false,\n render,\n } = options;\n const hasDefault = 'defaultValue' in options;\n const actionAttrs = actions.map((_, i) => ({ 'data-choice': String(i) }));\n\n const body: OverlayContent =\n render !== undefined\n ? render({ message, actions: actionAttrs })\n : jsx('div', {\n class: 'kerf-choice',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-choice__title', children: title })\n : '',\n jsx('p', { class: 'kerf-choice__message', children: message }),\n jsx('div', {\n class: 'kerf-choice__actions',\n children: actions.map((action, i) =>\n jsx('button', {\n type: 'button',\n class:\n action.className !== undefined\n ? `kerf-choice__action ${action.className}`\n : 'kerf-choice__action',\n ...actionAttrs[i],\n children: action.label,\n }),\n ),\n }),\n ],\n });\n\n let resolveChoice!: (result: R | null) => void;\n const result = new Promise<R | null>((resolve) => {\n resolveChoice = resolve;\n });\n\n const handle = overlay(body, {\n container,\n className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-choice]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a throw closes the overlay. Any\n // close settles a still-pending choice as a dismissal (`null`).\n void wireDialog(\n handle,\n (track) => {\n track(\n delegate(handle.el, 'click', '[data-choice]', (_event, el) => {\n resolveChoice(actions[Number(el.getAttribute('data-choice'))].value);\n handle.close();\n }),\n );\n\n if (hasDefault) {\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key === 'Enter') {\n event.preventDefault();\n resolveChoice(defaultValue as R);\n handle.close();\n }\n };\n // Last wiring step: nothing after it can throw, so it needs no rollback.\n handle.el.addEventListener('keydown', onKeydown);\n }\n },\n () => resolveChoice(null),\n );\n return result;\n}\n","/** `confirm()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\n\n/** Wiring slots passed to a {@link ConfirmOptions.render}. */\nexport interface ConfirmRenderSlots {\n message: string;\n /** Spread onto the confirm control. */\n ok: Record<string, string>;\n /** Spread onto the cancel control. */\n cancel: Record<string, string>;\n}\n\n/** Options for {@link confirm}. */\nexport interface ConfirmOptions {\n /** Where to append the overlay. Default `document.body`. */\n container?: Element;\n /** Wrapper class. Default `'kerf-overlay'`. */\n className?: string;\n /** Optional heading above the message. */\n title?: string;\n /** Confirm button label. Default `'OK'`. */\n okText?: string;\n /** Cancel button label. Default `'Cancel'`. */\n cancelText?: string;\n /** Add a `kerf-confirm--danger` class to the wrapper for destructive actions. */\n danger?: boolean;\n /** Host the dialog in the browser top layer where supported. */\n native?: boolean;\n /** Bring your own body while retaining kerf's dialog wiring. */\n render?: (slots: ConfirmRenderSlots) => OverlayContent;\n}\n\n/** A promise-based `window.confirm` replacement. */\nexport function confirm(\n message: string,\n options: ConfirmOptions = {},\n): Promise<boolean> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n okText = 'OK',\n cancelText = 'Cancel',\n danger = false,\n native = false,\n render,\n } = options;\n\n const body: OverlayContent =\n render !== undefined\n ? render({\n message,\n ok: { 'data-confirm': 'ok' },\n cancel: { 'data-confirm': 'cancel' },\n })\n : jsx('div', {\n class: 'kerf-confirm',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-confirm__title', children: title })\n : '',\n jsx('p', { class: 'kerf-confirm__message', children: message }),\n jsx('div', {\n class: 'kerf-confirm__actions',\n children: [\n jsx('button', {\n type: 'button',\n 'data-confirm': 'cancel',\n children: cancelText,\n }),\n jsx('button', {\n type: 'button',\n 'data-confirm': 'ok',\n class: 'kerf-confirm__ok',\n children: okText,\n }),\n ],\n }),\n ],\n });\n\n const handle = overlay(body, {\n container,\n className: danger ? `${className} kerf-confirm--danger` : className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-confirm=\"ok\"]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a throw closes the overlay. The\n // click table is the only (and so the last) step, so it needs no rollback.\n return wireDialog(\n handle,\n () => {\n delegate(handle.el, 'click', '[data-confirm]', (_event, el) => {\n handle.close(el.getAttribute('data-confirm') === 'ok');\n });\n },\n (value) => value === true,\n );\n}\n","/** `prompt()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\n\n/**\n * Validate a single field's value, synchronously: return a non-empty message\n * to block OK, anything else to accept. A validator that throws is treated as\n * a programming error — the dialog closes and the returned promise rejects\n * with that error.\n */\nexport type FieldValidator = (\n value: string,\n) => string | null | undefined | void;\n\n/** Options for {@link prompt}. */\n/**\n * Run a {@link FieldValidator}: the message it reports, or `undefined` when the\n * value is valid. Validators are synchronous — a promise result is a\n * programming error that closes the dialog and rejects its promise (the stray\n * promise's own rejection is silenced so it is not reported twice).\n * Internal to the dialog helpers.\n */\nexport function runValidator(\n validate: FieldValidator | undefined,\n value: string,\n owner: string,\n): string | undefined {\n const result: unknown = validate?.(value);\n if (typeof (result as PromiseLike<unknown> | null)?.then === 'function') {\n (result as PromiseLike<unknown>).then(undefined, () => undefined);\n throw new TypeError(`${owner}: validate returned a promise.`);\n }\n return typeof result === 'string' && result.length > 0 ? result : undefined;\n}\n\nexport interface PromptOptions {\n container?: Element;\n className?: string;\n title?: string;\n defaultValue?: string;\n placeholder?: string;\n inputType?: string;\n okText?: string;\n cancelText?: string;\n validate?: FieldValidator;\n native?: boolean;\n render?: (slots: PromptRenderSlots) => OverlayContent;\n}\n\n/** Wiring slots for a {@link PromptOptions.render}. */\nexport interface PromptRenderSlots {\n message: string;\n input: Record<string, string>;\n error: Record<string, string>;\n ok: Record<string, string>;\n cancel: Record<string, string>;\n}\n\n/** A promise-based `window.prompt` replacement. */\nexport function prompt(\n message: string,\n options: PromptOptions = {},\n): Promise<string | null> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n defaultValue = '',\n placeholder,\n inputType = 'text',\n okText = 'OK',\n cancelText = 'Cancel',\n validate,\n native = false,\n render,\n } = options;\n\n const inputAttrs: Record<string, string> = {\n 'data-prompt-input': '',\n type: inputType,\n value: defaultValue,\n ...(placeholder !== undefined ? { placeholder } : {}),\n };\n\n const body: OverlayContent =\n render !== undefined\n ? render({\n message,\n input: inputAttrs,\n error: { 'data-prompt-error': '' },\n ok: { 'data-prompt': 'ok' },\n cancel: { 'data-prompt': 'cancel' },\n })\n : jsx('div', {\n class: 'kerf-prompt',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-prompt__title', children: title })\n : '',\n jsx('label', { class: 'kerf-prompt__message', children: message }),\n jsx('input', { class: 'kerf-prompt__input', ...inputAttrs }),\n jsx('p', {\n class: 'kerf-prompt__error',\n 'data-prompt-error': '',\n children: '',\n }),\n jsx('div', {\n class: 'kerf-prompt__actions',\n children: [\n jsx('button', {\n type: 'button',\n 'data-prompt': 'cancel',\n children: cancelText,\n }),\n jsx('button', {\n type: 'button',\n 'data-prompt': 'ok',\n class: 'kerf-prompt__ok',\n children: okText,\n }),\n ],\n }),\n ],\n });\n\n const handle = overlay(body, {\n container,\n className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-prompt-input]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a missing required input or any\n // other throw below removes what was wired, closes the overlay, and rethrows.\n return wireDialog(\n handle,\n (track, guard) => {\n // Looked up again at OK: a reactive re-render may have replaced the\n // input, and a detached node would report a stale value. Missing at\n // construction is a render bug (rolls back, throws); missing at OK\n // rejects the promise through `guard`.\n const inputFor = (problem = 'render missing'): HTMLInputElement => {\n const input = handle.el.querySelector<HTMLInputElement>(\n 'input[data-prompt-input]',\n );\n if (input !== null) return input;\n throw new Error(`prompt(): ${problem} <input data-prompt-input>.`);\n };\n inputFor();\n const errorEl = handle.el.querySelector<HTMLElement>(\n '[data-prompt-error]',\n );\n if (errorEl !== null) errorEl.hidden = true;\n\n // A throwing (or promise-returning) `validate` closes the dialog and\n // rejects the promise (guard).\n const attemptOk = guard((): void => {\n const input = inputFor('removed after open:');\n const value = input.value;\n const error = runValidator(validate, value, 'prompt()');\n if (error !== undefined) {\n if (errorEl !== null) {\n errorEl.textContent = error;\n errorEl.hidden = false;\n }\n input.focus();\n return;\n }\n handle.close(value);\n });\n\n track(\n delegate(handle.el, 'click', '[data-prompt]', (_event, el) => {\n if (el.getAttribute('data-prompt') === 'ok') attemptOk();\n else handle.close(null);\n }),\n );\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (\n event.key === 'Enter' &&\n (event.target as Element | null)?.matches('input[data-prompt-input]')\n ) {\n event.preventDefault();\n attemptOk();\n }\n };\n // Last wiring step: nothing after it can throw, so it needs no rollback.\n handle.el.addEventListener('keydown', onKeydown);\n },\n (value) => (typeof value === 'string' ? value : null),\n );\n}\n","/** `form()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\nimport { type FieldValidator, runValidator } from './overlay-prompt.js';\n\n/** A single field in a {@link form}. */\nexport interface FormField {\n name: string;\n label?: string;\n defaultValue?: string;\n placeholder?: string;\n type?: string;\n validate?: FieldValidator;\n}\n\n/** One field's wiring in a {@link FormRenderSlots}. */\nexport interface FormRenderField {\n name: string;\n label: string;\n input: Record<string, string>;\n error: Record<string, string>;\n}\n\n/** Wiring slots for a {@link FormOptions.render}. */\nexport interface FormRenderSlots {\n fields: FormRenderField[];\n ok: Record<string, string>;\n cancel: Record<string, string>;\n}\n\n/** Options for {@link form}. */\nexport interface FormOptions {\n container?: Element;\n className?: string;\n title?: string;\n okText?: string;\n cancelText?: string;\n native?: boolean;\n render?: (slots: FormRenderSlots) => OverlayContent;\n}\n\n/** A promise-based multi-field dialog. */\nexport function form(\n fields: readonly FormField[],\n options: FormOptions = {},\n): Promise<Record<string, string> | null> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n okText = 'OK',\n cancelText = 'Cancel',\n native = false,\n render,\n } = options;\n\n const fieldAttrs = (field: FormField): Record<string, string> => ({\n 'data-field': field.name,\n name: field.name,\n type: field.type ?? 'text',\n value: field.defaultValue ?? '',\n ...(field.placeholder !== undefined\n ? { placeholder: field.placeholder }\n : {}),\n });\n\n const body: OverlayContent =\n render !== undefined\n ? render({\n fields: fields.map((field) => ({\n name: field.name,\n label: field.label ?? field.name,\n input: fieldAttrs(field),\n error: { 'data-field-error': field.name },\n })),\n ok: { 'data-form': 'ok' },\n cancel: { 'data-form': 'cancel' },\n })\n : jsx('div', {\n class: 'kerf-form',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-form__title', children: title })\n : '',\n ...fields.map((field) =>\n jsx('div', {\n class: 'kerf-form__field',\n children: [\n jsx('label', {\n class: 'kerf-form__label',\n children: field.label ?? field.name,\n }),\n jsx('input', {\n class: 'kerf-form__input',\n ...fieldAttrs(field),\n }),\n jsx('p', {\n class: 'kerf-form__error',\n 'data-field-error': field.name,\n children: '',\n }),\n ],\n }),\n ),\n jsx('div', {\n class: 'kerf-form__actions',\n children: [\n jsx('button', {\n type: 'button',\n 'data-form': 'cancel',\n children: cancelText,\n }),\n jsx('button', {\n type: 'button',\n 'data-form': 'ok',\n class: 'kerf-form__ok',\n children: okText,\n }),\n ],\n }),\n ],\n });\n\n const handle = overlay(body, {\n container,\n className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-field]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a missing required field input or\n // any other throw below removes what was wired, closes the overlay, and\n // rethrows.\n return wireDialog(\n handle,\n (track, guard) => {\n const errorFor = (name: string): HTMLElement | null =>\n Array.from(\n handle.el.querySelectorAll<HTMLElement>('[data-field-error]'),\n ).find((el) => el.getAttribute('data-field-error') === name) ?? null;\n // KF-HRNJ4T (a field input removed after open by a reactive re-render):\n // at construction a missing input is a render bug that rolls back and\n // throws synchronously; at OK time it throws inside `guard`, so the\n // dialog closes and the promise rejects naming the field — never a\n // `null` that reads as a user Cancel.\n const inputFor = (\n name: string,\n problem = 'render missing',\n ): HTMLInputElement => {\n const input = Array.from(\n handle.el.querySelectorAll<HTMLInputElement>('input[data-field]'),\n ).find((el) => el.getAttribute('data-field') === name);\n if (input !== undefined) return input;\n throw new Error(`form(): ${problem} <input data-field=\"${name}\">.`);\n };\n\n for (const field of fields) {\n inputFor(field.name);\n const errorEl = errorFor(field.name);\n if (errorEl !== null) errorEl.hidden = true;\n }\n\n // A throwing `field.validate` closes the dialog and rejects the promise.\n const attemptOk = guard((): void => {\n const record: Record<string, string> = {};\n let firstInvalid: HTMLInputElement | null = null;\n for (const field of fields) {\n const el = inputFor(field.name, 'removed after open:');\n const value = el.value;\n record[field.name] = value;\n const error = runValidator(field.validate, value, 'form()');\n const errorEl = errorFor(field.name);\n if (error !== undefined) {\n if (errorEl !== null) {\n errorEl.textContent = error;\n errorEl.hidden = false;\n }\n if (firstInvalid === null) firstInvalid = el;\n } else if (errorEl !== null) {\n errorEl.hidden = true;\n }\n }\n if (firstInvalid !== null) {\n firstInvalid.focus();\n return;\n }\n handle.close(record);\n });\n\n track(\n delegate(handle.el, 'click', '[data-form]', (_event, el) => {\n if (el.getAttribute('data-form') === 'ok') attemptOk();\n else handle.close(null);\n }),\n );\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (\n event.key === 'Enter' &&\n (event.target as Element | null)?.matches('[data-field]')\n ) {\n event.preventDefault();\n attemptOk();\n }\n };\n // Last wiring step: nothing after it can throw, so it needs no rollback.\n handle.el.addEventListener('keydown', onKeydown);\n },\n (value) =>\n value !== null && typeof value === 'object'\n ? (value as Record<string, string>)\n : null,\n );\n}\n","/**\n * Anchored positioning primitives for `kerfjs/overlay` — position an element\n * relative to an anchor, with viewport flip + clamp. These are standalone: they\n * take any element and have no overlay lifecycle, so `popover()` / `tooltip()`\n * build on them but you can also position your own element (an inline hint, a\n * custom menu). Re-exported from `src/overlay.ts` so the public `kerfjs/overlay`\n * surface is unchanged (KF-511 split out of overlay.ts's dialog + toast code).\n */\n\n/** Vertical placement relative to an anchor (used by `popover`, {@link positionAnchored}, `tooltip`). */\nexport type PopoverPlacement = 'bottom' | 'top';\n\n/** Placement options for {@link positionAnchored} / {@link autoReposition}. */\nexport interface AnchorPositionOptions {\n /** Preferred side of the anchor; flips to the other side if it would overflow the viewport. Default `'bottom'`. */\n placement?: PopoverPlacement;\n /** Horizontal edge to line up with the anchor: `'start'` (left edges) or `'end'` (right edges). Default `'start'`. */\n align?: 'start' | 'end';\n /** Gap in px between the anchor and the element. Default `4`. */\n gap?: number;\n}\n\n/**\n * One-shot: position `el` relative to `anchor` — below by default, flipping above\n * if it would overflow the viewport, aligned to a horizontal edge and clamped into\n * view. Sets `el.style` `position: fixed`, `margin: 0`, `left`, and `top` (fixed so\n * `left`/`top` are viewport coordinates, matching `getBoundingClientRect`). When an\n * ancestor makes itself the containing block for fixed descendants (a\n * `transform`, `filter`, `perspective`, or `contain` on a wrapping `<dialog>`),\n * `left`/`top` are translated (and unscaled) into that box's coordinates so `el`\n * still lands next to the anchor. This is\n * `popover()`'s placement core, usable on any element (an inline hint, a tooltip) —\n * no overlay lifecycle. Pair with {@link autoReposition} to keep it glued while open.\n */\nexport function positionAnchored(\n el: HTMLElement,\n anchor: Element,\n options: AnchorPositionOptions = {},\n): void {\n const { placement = 'bottom', align = 'start', gap = 4 } = options;\n // Fix `el` FIRST, then measure it. Measuring a still-`display:block` wrapper\n // reports the full body-content width (≈ viewport minus body margins), which\n // collapses the horizontal clamp below and lands the element at the body's left\n // edge. As a fixed, shrink-to-fit box its `width`/`height` are its real size.\n el.style.position = 'fixed';\n el.style.margin = '0';\n const anchorRect = anchor.getBoundingClientRect();\n const elementRect = el.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n // Vertical: preferred side, flipped only if it overflows and the other side fits.\n const belowTop = anchorRect.bottom + gap;\n const aboveTop = anchorRect.top - gap - elementRect.height;\n let below = placement !== 'top';\n if (below && belowTop + elementRect.height > viewportHeight && aboveTop >= 0)\n below = false;\n else if (\n !below &&\n aboveTop < 0 &&\n belowTop + elementRect.height <= viewportHeight\n )\n below = true;\n\n // Horizontal: align to an anchor edge, then clamp into the viewport.\n let left =\n align === 'end' ? anchorRect.right - elementRect.width : anchorRect.left;\n left = Math.max(0, Math.min(left, viewportWidth - elementRect.width));\n\n // `left`/`top` so far are viewport coordinates. They are only the CSS values\n // too when `el`'s fixed-position containing block is the viewport; an\n // ancestor with a `transform`, `filter`, `perspective`, `contain`, … (say a\n // modal `<dialog>` holding an overlay host slot) captures fixed descendants,\n // so map the coordinates into that box's space.\n const [originX, originY, scaleX, scaleY] = fixedOrigin(el);\n el.style.left = `${(left - originX) / scaleX}px`;\n el.style.top = `${((below ? belowTop : aboveTop) - originY) / scaleY}px`;\n}\n\n/**\n * Where `left: 0; top: 0` actually lands for a fixed-position child of `el`'s\n * parent, plus that containing block's scale — `[x, y, scaleX, scaleY]`, which\n * is `[0, 0, 1, 1]` when the viewport is the containing block. Measured with a\n * 1px probe rather than `el` itself so `el`'s own transform or running entrance\n * animation cannot skew it. The probe is inserted and removed synchronously, so\n * it is never painted. Translation and scale are compensated; a rotated or\n * skewed containing block is not.\n */\nfunction fixedOrigin(el: HTMLElement): [number, number, number, number] {\n const parent = el.parentNode;\n if (!parent) return [0, 0, 1, 1];\n const probe = el.ownerDocument.createElement('div');\n probe.style.cssText =\n 'position:fixed;left:0;top:0;width:1px;height:1px;margin:0;padding:0;border:0';\n parent.insertBefore(probe, el);\n const rect = probe.getBoundingClientRect();\n probe.remove();\n // A zero size (a containing block mid `scale(0)` animation, or a DOM without\n // layout) would divide by zero; treat it as unscaled.\n return [rect.left, rect.top, rect.width || 1, rect.height || 1];\n}\n\n/**\n * Keep `el` positioned against `anchor` (via {@link positionAnchored}) as the page\n * scrolls or resizes. Positions once immediately, then re-runs on `scroll`\n * (capture phase — catches scrolls in any inner container, not just `window`) and\n * `resize`. Returns a disposer that removes the listeners.\n */\nexport function autoReposition(\n el: HTMLElement,\n anchor: Element,\n options: AnchorPositionOptions = {},\n): () => void {\n const reposition = (): void => positionAnchored(el, anchor, options);\n reposition();\n window.addEventListener('scroll', reposition, true);\n window.addEventListener('resize', reposition);\n return () => {\n window.removeEventListener('scroll', reposition, true);\n window.removeEventListener('resize', reposition);\n };\n}\n","/** Anchored popover lifecycle built on the shared overlay transaction. */\nimport { attach } from './attach.js';\nimport {\n type DismissTrigger,\n overlay,\n type OverlayContent,\n type OverlayHandle,\n overlayHost,\n} from './overlay-core.js';\nimport { autoReposition, type PopoverPlacement } from './overlay-position.js';\n\n/** Options for {@link popover}. */\nexport interface PopoverOptions {\n /**\n * Where to append the popover wrapper. Default: when `anchor` sits inside an\n * open modal `<dialog>`, that dialog's `[data-kerf-overlay-host]` element (a\n * dialog kerf opened gets one automatically), so the popover is part of the\n * modal subtree and stays interactive; otherwise `document.body`. A modal\n * `<dialog>` in a shadow root counts when `anchor` is slotted into it (Web\n * Awesome's `wa-dialog` / `wa-drawer`): the slot is created on demand in the\n * shadow host's light DOM. A popover inside a modal closes when that dialog\n * closes.\n */\n container?: Element;\n /** Class on the wrapper. Default `'kerf-popover'`. */\n className?: string;\n /** Preferred side of the anchor. Flips to the other side if it would overflow the viewport. Default `'bottom'`. */\n placement?: PopoverPlacement;\n /** Horizontal edge to line up with the anchor: `'start'` (left edges) or `'end'` (right edges). Default `'start'`. */\n align?: 'start' | 'end';\n /** Gap in px between the anchor and the popover. Default `4`. */\n gap?: number;\n /**\n * Which user actions dismiss the popover. Default `['outside']` (a click\n * outside the popover, the anchor exempt). Pass `false` to close only via `close()`.\n */\n dismiss?: DismissTrigger | DismissTrigger[] | false;\n /** Focus behavior on open. Default `false` (non-modal — leave focus alone). */\n initialFocus?: string | boolean;\n /** Extra elements (besides the anchor) whose clicks do NOT count as outside. */\n outsideIgnore?: Element | readonly Element[];\n /** Called on any user-initiated dismissal. */\n onDismiss?: () => void;\n /**\n * Host the popover in the browser top layer (the Popover API — `[popover]` +\n * `showPopover()`) where supported, so it stacks above any `z-index` without a\n * z-index war. Falls back to today's plain `<div>` where unsupported. kerf keeps\n * owning positioning + its own dismiss wiring; the popover is `popover=\"manual\"`.\n * See {@link OverlayOptions.native}. Default `false`.\n */\n native?: boolean;\n}\n\n/**\n * Anchored, non-modal overlay: positions `content` relative to `anchor` (below by\n * default, flipping above if it would overflow, and clamped horizontally to the\n * viewport) and repositions on scroll / resize while open. A thin wrapper over\n * {@link overlay} with non-modal defaults — `trap: false`, `dismiss: ['outside']`,\n * and the anchor added to `outsideIgnore` so the trigger click doesn't self-close.\n * Returns the same {@link OverlayHandle}; `close()` also drops the reposition\n * listeners. `position: fixed` is set inline (you style everything else).\n */\nexport function popover(\n anchor: Element,\n content: OverlayContent,\n options: PopoverOptions = {},\n): OverlayHandle {\n const {\n container,\n className = 'kerf-popover',\n placement = 'bottom',\n align = 'start',\n gap = 4,\n dismiss = ['outside'],\n initialFocus = false,\n outsideIgnore,\n onDismiss,\n native = false,\n } = options;\n\n const extraIgnore =\n outsideIgnore === undefined\n ? []\n : Array.isArray(outsideIgnore)\n ? [...outsideIgnore]\n : [outsideIgnore];\n\n const handle = overlay(content, {\n container: container ?? overlayHost(anchor),\n className,\n dismiss,\n trap: false,\n initialFocus,\n onDismiss,\n outsideIgnore: [anchor, ...extraIgnore],\n native,\n });\n\n // Position + keep it glued while open; drop the listeners on close. A throw\n // while positioning (e.g. an anchor whose geometry read fails) is still part\n // of construction: close the just-opened overlay before rethrowing.\n let stopReposition: () => void;\n try {\n stopReposition = autoReposition(handle.el, anchor, {\n placement,\n align,\n gap,\n });\n } catch (error) {\n handle.close();\n throw error;\n }\n void handle.result.then(stopReposition);\n\n // KF-BAVCEV: never outlive the anchor. When it leaves the document (e.g. the\n // modal holding it closes) close the popover as cleanup — not a user\n // dismissal, so no `onDismiss`. A move within the document keeps it open.\n void handle.result.then(attach(anchor, () => handle.close));\n\n return handle;\n}\n","/**\n * `toast()` for `kerfjs/overlay` — a non-modal, auto-dismissing notification that\n * stacks in a shared body-level region. Split out of `overlay.ts` (KF-513) since\n * it's a distinct transient-UI concern from the modal dialogs; re-exported from\n * `overlay.ts` so the public `kerfjs/overlay` surface is unchanged. Structural\n * only — kerf ships no CSS; you style the region / toast / animations.\n */\nimport { type SafeHtml } from './jsx-runtime.js';\nimport { mount, type MountResult } from './mount.js';\nimport { escapeHtml } from './utils/escapeHtml.js';\n\n/** Content for a {@link toast}: an escaped text string, trusted `SafeHtml`, or a render function. */\nexport type ToastContent = string | SafeHtml | (() => MountResult);\n\n/** Accent variant for a {@link toast} — mapped to a `${className}--${variant}` class. */\nexport type ToastVariant = 'info' | 'success' | 'warning';\n\n/** Options for {@link toast}. */\nexport interface ToastOptions {\n /** Where toasts stack. Default: a lazily-created `<div class=\"kerf-toasts\">` on `document.body`. */\n container?: Element;\n /** Class on the toast element. Default `'kerf-toast'`. */\n className?: string;\n /** Auto-dismiss after this many ms. `0` keeps it until dismissed by hand. Default `4000`. */\n duration?: number;\n /** ARIA role. Default `'status'`. */\n role?: string;\n /**\n * `'stack'` (default) shows toasts stacked in the region; `'replace'` dismisses\n * the region's current toast(s) first (collapse-to-latest for a rapid sequence).\n */\n mode?: 'stack' | 'replace';\n /**\n * How `mode: 'replace'` drops the prior toast(s): `'fade'` (default) runs their\n * full exit transition (nice for a STACKING region), or `'instant'` removes them\n * synchronously with no exit — what a single, exactly-centered toast slot wants,\n * so the outgoing and incoming messages never cross-fade in the same spot.\n */\n collapse?: 'fade' | 'instant';\n /** Accent variant — adds a `${className}--${variant}` class (kerf ships no CSS; you style it). */\n variant?: ToastVariant;\n /** Class added on the next animation frame after mount, so a CSS **entrance** transition can run. */\n enterClass?: string;\n /**\n * Class added when dismissing, so CSS owns the **exit**. On dismiss the\n * `enterClass` (if any) is also REMOVED, so `exitClass` doesn't have to\n * out-specify it — and a symmetric single-class fade (entrance = add\n * `enterClass`, exit = remove it) works by setting only `enterClass` +\n * `exitDuration`. The node is removed `exitDuration` ms later.\n */\n exitClass?: string;\n /** ms to wait before removing the node on dismiss — applies when `exitClass` is set OR when it's > 0 (to let a removed `enterClass` transition out). Default `0`. */\n exitDuration?: number;\n}\n\n/** Handle returned by {@link toast}. */\nexport interface ToastHandle {\n /** The toast element — inspect it, or run your own entrance/exit transitions. */\n el: HTMLElement;\n /**\n * Dismiss it early. Default runs the `exitClass` transition (removed after\n * `exitDuration`); pass `{ instant: true }` to remove it **synchronously** with\n * no exit — for an action button that immediately shows a replacement toast in a\n * single centered slot (no cross-fade). Idempotent.\n */\n dismiss(options?: { instant?: boolean }): void;\n}\n\n/** The region's active toasts live on the region element (in the DOM), not in module state. */\nconst TOAST_SET = Symbol('kerf.toasts');\n/** A dismiss function with an `.removeNow()` for instant (no-exit) teardown. */\ntype Dismisser = (() => void) & { removeNow: () => void };\ntype ToastCarrier = Element & { [TOAST_SET]?: Set<Dismisser> };\n\n/** The singleton toast region lives in the DOM (queried, not held in a module variable). */\nfunction toastRegion(container?: Element): Element {\n if (container !== undefined) return container;\n const existing = document.querySelector('.kerf-toasts');\n if (existing !== null) return existing;\n const region = document.createElement('div');\n region.className = 'kerf-toasts';\n region.setAttribute('aria-live', 'polite');\n document.body.appendChild(region);\n return region;\n}\n\n/**\n * Show a non-modal, auto-dismissing notification. Stacks in a shared body-level\n * region (or your `container`). Returns a {@link ToastHandle} (`{ el, dismiss }`)\n * so you can run entrance/exit transitions, wire an action button, or inspect the\n * node. `mode: 'replace'` collapses a rapid sequence to the latest; `variant`\n * adds an accent class; `enterClass`/`exitClass` let CSS own the animation.\n */\nexport function toast(\n content: ToastContent,\n options: ToastOptions = {},\n): ToastHandle {\n const {\n container,\n className = 'kerf-toast',\n duration = 4000,\n role = 'status',\n mode = 'stack',\n collapse = 'fade',\n variant,\n enterClass,\n exitClass,\n exitDuration = 0,\n } = options;\n\n const region = toastRegion(container) as ToastCarrier;\n const active = (region[TOAST_SET] ??= new Set<Dismisser>());\n // collapse-to-latest: 'instant' removes priors synchronously (no cross-fade in a\n // centered single slot); 'fade' runs their exit transition (nice for a stack).\n if (mode === 'replace') {\n for (const d of [...active]) {\n if (collapse === 'instant') d.removeNow();\n else d();\n }\n }\n\n const el = document.createElement('div');\n el.className = className;\n if (variant !== undefined) el.classList.add(`${className}--${variant}`);\n el.setAttribute('role', role);\n region.appendChild(el);\n\n const render =\n typeof content === 'function'\n ? content\n : () => (typeof content === 'string' ? escapeHtml(content) : content);\n const disposeMount = mount(el, render);\n const state: {\n dismissed: boolean;\n removed: boolean;\n timer: ReturnType<typeof setTimeout> | undefined;\n exitTimer: ReturnType<typeof setTimeout> | undefined;\n } = {\n dismissed: false,\n removed: false,\n timer: undefined,\n exitTimer: undefined,\n };\n\n if (enterClass !== undefined) {\n globalThis.requestAnimationFrame(() => {\n if (!state.dismissed) el.classList.add(enterClass);\n });\n }\n\n const remove = (): void => {\n if (state.removed) return;\n state.removed = true;\n state.dismissed = true; // once removed, a later dismiss() is a clean no-op\n if (state.exitTimer !== undefined) clearTimeout(state.exitTimer);\n disposeMount();\n el.remove();\n active.delete(dismiss as Dismisser);\n };\n\n // Force synchronous teardown NOW, with no exit transition — even if a fade is\n // already in flight (so `mode:'replace'` collapse can clean up a mid-exit toast,\n // and `dismiss({ instant: true })` drops it immediately).\n const removeNow = (): void => {\n if (state.timer !== undefined) clearTimeout(state.timer);\n remove();\n };\n\n const fadeOut = (): void => {\n if (state.dismissed) return;\n state.dismissed = true;\n if (state.timer !== undefined) clearTimeout(state.timer);\n // Reverse the entrance (drop enterClass so exitClass needn't out-specify it),\n // add exitClass, and delay removal for the transition — including the\n // single-class idiom (enterClass removed + exitDuration, no exitClass).\n if (enterClass !== undefined) el.classList.remove(enterClass);\n if (exitClass !== undefined) el.classList.add(exitClass);\n if (exitClass !== undefined || exitDuration > 0) {\n state.exitTimer = setTimeout(remove, exitDuration);\n } else {\n remove();\n }\n };\n\n function dismiss(options?: { instant?: boolean }): void {\n if (options?.instant === true) removeNow();\n else fadeOut();\n }\n (dismiss as Dismisser).removeNow = removeNow;\n\n active.add(dismiss as Dismisser);\n if (duration > 0) state.timer = setTimeout(fadeOut, duration);\n return { el, dismiss };\n}\n","/** Hover and focus tooltip lifecycle built on the shared overlay transaction. */\nimport { attach } from './attach.js';\nimport { jsx, type SafeHtml } from './jsx-runtime.js';\nimport type { MountResult } from './mount.js';\nimport {\n overlay,\n type OverlayContent,\n type OverlayHandle,\n overlayHost,\n} from './overlay-core.js';\nimport {\n type AnchorPositionOptions,\n autoReposition,\n} from './overlay-position.js';\n\n/** Content for a {@link tooltip}: text (auto-escaped), `SafeHtml`, or a render function. */\nexport type TooltipContent = string | SafeHtml | (() => MountResult);\n\n/** Options for {@link tooltip}. */\nexport interface TooltipOptions extends AnchorPositionOptions {\n /** Where to append the tooltip wrapper. Default: the anchor's modal-dialog host slot (see {@link PopoverOptions.container}), else `document.body`. */\n container?: Element;\n /** Class on the wrapper. Default `'kerf-tooltip'`. */\n className?: string;\n /** Delay in ms before showing after hover/focus enters. Default `400`. */\n delay?: number;\n /** Delay in ms before hiding after hover/focus leaves. Default `100`. */\n hideDelay?: number;\n /** ARIA role on the wrapper. Default `'tooltip'`. */\n role?: string;\n /** Host the tooltip in the browser top layer (the Popover API) where supported. See {@link OverlayOptions.native}. Default `false`. */\n native?: boolean;\n}\n\n/**\n * A hover/focus-triggered, non-modal, auto-hiding tooltip anchored to `anchor`.\n * Shows after `delay` on `pointerenter`/`focus`, hides after `hideDelay` on\n * `pointerleave`/`blur`, and positions itself with {@link autoReposition} (above\n * the anchor by default). Unlike {@link popover} there is no click-dismiss model —\n * it follows the pointer/focus. Returns a disposer that removes the anchor\n * listeners and hides any shown tooltip. Structural only (kerf ships no CSS).\n */\nexport function tooltip(\n anchor: Element,\n content: TooltipContent,\n options: TooltipOptions = {},\n): () => void {\n const {\n container,\n className = 'kerf-tooltip',\n delay = 400,\n hideDelay = 100,\n role = 'tooltip',\n placement = 'top',\n align = 'start',\n gap = 4,\n native = false,\n } = options;\n\n const body: OverlayContent =\n typeof content === 'function'\n ? content\n : typeof content === 'string'\n ? jsx('span', { class: `${className}__text`, children: content })\n : content;\n\n const timers: {\n show?: ReturnType<typeof setTimeout>;\n hide?: ReturnType<typeof setTimeout>;\n } = {};\n let current:\n | { handle: OverlayHandle; stop: () => void; unwatch: () => void }\n | undefined;\n let presence = 0;\n\n // Runs from the `delay` timer, so there is no caller to throw to. A failed\n // show (a throwing render fn, `native` `showPopover()`, or positioning) is\n // rolled back before `current` is set — nothing stays on screen and the\n // next pointerenter / focus schedules a fresh attempt — and the original\n // error then escapes the timer callback for the host to report as uncaught\n // (a window `error` event in browsers), the same way kerf's other deferred\n // callbacks (debounce / throttle timers, attach teardown) surface errors.\n function show(): void {\n // The anchor left the document while the show was pending (KF-BAVCEV).\n if (!anchor.isConnected) return;\n const handle = overlay(body, {\n container: container ?? overlayHost(anchor),\n className,\n dismiss: false,\n trap: false,\n initialFocus: false,\n native,\n });\n handle.el.setAttribute('role', role);\n let stop: () => void;\n try {\n stop = autoReposition(handle.el, anchor, { placement, align, gap });\n } catch (error) {\n // Not yet tracked in `current`, so `hide()` could never reach it.\n handle.close();\n throw error;\n }\n // KF-BAVCEV: an anchor removed from the document (e.g. with the modal that\n // held it) fires no pointerleave / blur, so hide when it leaves and forget\n // its presence; a move within the document keeps the tooltip shown.\n const unwatch = attach(anchor, () => () => {\n presence = 0;\n if (timers.hide !== undefined) clearTimeout(timers.hide);\n hide();\n });\n current = { handle, stop, unwatch };\n // A surface that closed itself (its host slot left the document with the\n // dialog) must not stay `current`, or the next hover would never show.\n void handle.result.then(() => {\n if (current?.handle === handle) hide();\n });\n }\n\n function hide(): void {\n const shown = current;\n if (shown === undefined) return;\n // Cleared first: `unwatch()` runs the anchor teardown, which re-enters here.\n current = undefined;\n shown.unwatch();\n shown.stop();\n shown.handle.close();\n }\n\n const onEnter = (event: Event): void => {\n presence |= event.type === 'focus' ? 2 : 1;\n if (timers.hide !== undefined) clearTimeout(timers.hide);\n if (current !== undefined) return;\n if (timers.show !== undefined) clearTimeout(timers.show); // debounce: one pending show at a time\n timers.show = setTimeout(show, delay);\n };\n const onLeave = (event: Event): void => {\n presence &= event.type === 'blur' ? ~2 : ~1;\n if (presence) return;\n if (timers.show !== undefined) clearTimeout(timers.show);\n if (current === undefined) return;\n timers.hide = setTimeout(hide, hideDelay);\n };\n\n anchor.addEventListener('pointerenter', onEnter);\n anchor.addEventListener('pointerleave', onLeave);\n anchor.addEventListener('focus', onEnter);\n anchor.addEventListener('blur', onLeave);\n\n return () => {\n anchor.removeEventListener('pointerenter', onEnter);\n anchor.removeEventListener('pointerleave', onLeave);\n anchor.removeEventListener('focus', onEnter);\n anchor.removeEventListener('blur', onLeave);\n if (timers.show !== undefined) clearTimeout(timers.show);\n if (timers.hide !== undefined) clearTimeout(timers.hide);\n hide();\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/overlay-core.ts","../src/overlay-choice.ts","../src/overlay-confirm.ts","../src/overlay-prompt.ts","../src/overlay-form.ts","../src/overlay-position.ts","../src/overlay-popover.ts","../src/overlay-toast.ts","../src/overlay-tooltip.ts"],"names":["options"],"mappings":"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","file":"overlay.js","sourcesContent":["/**\n * Shared overlay lifecycle and document arbitration implementation.\n *\n * Every real kerf app hand-rolls this: `toElement → body.appendChild → mount →\n * wire dismissal → remove`, plus the fiddly parts (Escape, backdrop / outside\n * click, focus trap, restoring focus on close). `window.confirm` is a no-op in\n * Tauri WKWebViews, so a hand-built overlay is mandatory there. This subpath\n * blesses the pattern through `overlay()`, with anchored surfaces and dialog\n * helpers building on it. No per-instance framework state: each call owns its\n * DOM + listeners in a closure and returns a handle.\n *\n * import { overlay, confirm, toast } from 'kerfjs/overlay';\n *\n * const ok = await confirm('Delete this file?', { danger: true });\n * toast('Saved');\n * const dialog = overlay(<Settings />, { dismiss: ['escape', 'backdrop'] });\n * // …later: dialog.close(); or await dialog.result;\n *\n * Structural only — kerf ships no CSS. The wrapper gets your `className`; style\n * the backdrop / centering / animation yourself.\n */\nimport { attach } from './attach.js';\nimport { devHooks } from './dev-hooks.js';\nimport type { SafeHtml } from './jsx-runtime.js';\nimport { mount, type MountResult } from './mount.js';\nimport { OVERLAY_HOST_SELECTOR } from './utils/overlay-host.js';\n\n/** A user-initiated dismissal trigger. */\nexport type DismissTrigger = 'escape' | 'backdrop' | 'outside';\n\n/** Content for an overlay: static `SafeHtml`, or a render function `mount()` drives reactively. */\nexport type OverlayContent = SafeHtml | (() => MountResult);\n\n/** Options for {@link overlay}. */\nexport interface OverlayOptions {\n /**\n * Where to append the overlay wrapper. Default `document.body`. A container\n * inside an open modal `<dialog>` (including one slotted into a shadow-root\n * modal such as Web Awesome's `wa-dialog`) is already interactive, so the\n * surface is not lifted, and it closes when that dialog closes.\n */\n container?: Element;\n /** Class on the wrapper element (you style it — kerf ships no CSS). Default `'kerf-overlay'`. */\n className?: string;\n /**\n * Which user actions dismiss the overlay. Default `['escape', 'backdrop']`.\n * `'backdrop'` = a click on the wrapper itself (not its content); `'outside'`\n * = a click anywhere outside the wrapper (for anchored popovers). `false`\n * disables user dismissal (close it programmatically).\n */\n dismiss?: DismissTrigger | DismissTrigger[] | false;\n /**\n * Where focus lands on open: a selector, `true` (first focusable element, or\n * the wrapper if none), or `false` (leave focus alone). Default `true`.\n */\n initialFocus?: string | boolean;\n /**\n * Trap Tab / Shift+Tab within the overlay while open and mark it\n * `role=\"dialog\"` / `aria-modal=\"true\"`. Default `true`. Set `false` for a\n * non-modal popover.\n */\n trap?: boolean;\n /** ARIA role for the wrapper when `trap` is on. Default `'dialog'`. */\n role?: string;\n /** Called on any user-initiated dismissal (before `close()` runs). */\n onDismiss?: () => void;\n /** For `'outside'` dismissal: clicks on these elements do NOT count as outside (e.g. the trigger button). */\n outsideIgnore?: Element | readonly Element[];\n /**\n * Opt into the browser **top layer** (`docs/19-native-overlay-backing.md`).\n * When `true` and the engine supports it, a modal overlay (`trap: true`) is\n * hosted in a `<dialog>` opened with `.showModal()` — real inerting of the rest\n * of the document + guaranteed stacking above any `z-index` — and a non-modal\n * one (`trap: false`) uses the Popover API (`[popover]` + `showPopover()`).\n * Feature-detected; falls back to today's plain `<div>` where unsupported.\n *\n * The `render` slot + promise API are unchanged — kerf just hosts your markup\n * in a `<dialog>` / `[popover]` instead of a `<div>`. Two caveats: native\n * `<dialog>` / `[popover]` carry **UA default styles** (a `::backdrop`,\n * centering, border, padding) that kerf does not reset — style the element (and\n * its `::backdrop`) via `className`; and `container` is effectively a **no-op**\n * for visual position, since the top layer ignores where the element lives in\n * the DOM. Default `false`.\n */\n native?: boolean;\n}\n\n/** Handle returned by {@link overlay}. Holds no framework state — it's a closure. */\nexport interface OverlayHandle {\n /** The wrapper element (mounted into, appended to `container`). */\n el: HTMLElement;\n /** Tear down: dispose the mount, remove listeners + the node, restore focus, resolve `result`. Idempotent. */\n close(result?: unknown): void;\n /** Resolves with the value passed to `close()` (or `undefined` on user dismissal). */\n result: Promise<unknown>;\n}\n\nconst FOCUSABLE =\n 'a[href],area[href],button:not([disabled]),input:not([disabled]),' +\n 'select:not([disabled]),textarea:not([disabled]),iframe,' +\n '[tabindex]:not([tabindex=\"-1\"]),[contenteditable=\"true\"]';\n\nfunction focusable(root: Element): HTMLElement[] {\n const items = Array.from(\n root.querySelectorAll<HTMLElement>(FOCUSABLE),\n ).filter((el) => !el.hasAttribute('hidden') && el.tabIndex >= 0);\n // WebKit on macOS follows the system's keyboard-navigation preference and\n // may omit implicitly tabbable controls from sequential focus navigation.\n // A focus trap promises stable traversal through every eligible control, so\n // make those implicit stops explicit while preserving authored tabindexes.\n for (const el of items) if (!el.hasAttribute('tabindex')) el.tabIndex = 0;\n return items;\n}\n\n// Native top-layer feature detection (KF-526). Modal → `<dialog>.showModal()`;\n// non-modal → the Popover API. Each is independent — an engine may ship one\n// without the other — and both fall back to the plain `<div>` where absent.\nfunction supportsDialog(): boolean {\n return (\n typeof HTMLDialogElement !== 'undefined' &&\n typeof HTMLDialogElement.prototype.showModal === 'function'\n );\n}\nfunction supportsPopover(): boolean {\n return (\n typeof HTMLElement !== 'undefined' &&\n typeof HTMLElement.prototype.showPopover === 'function'\n );\n}\n\n// Stacked-surface arbitration (KF-CW7GJ8: a tooltip inside a modal used to\n// become \"topmost\" and disable the modal's Escape, backdrop, and focus trap).\n// Every open overlay — native or fallback — is recorded, in open order, on a\n// per-document stack. An input kind (Escape, a backdrop / outside click, Tab\n// for the trap) belongs to the topmost surface that HANDLES that kind; a\n// surface that doesn't handle it is transparent, except that a modal surface\n// is a barrier nothing beneath it sees past. A tooltip (or any overlay with no\n// dismiss trigger and no trap) handles nothing, so it never intercepts. The\n// `handled` set marks an event a surface already acted on, so one click that is\n// both \"outside\" a popover and on the modal backdrop closes only the popover.\nconst OVERLAY_ARBITRATION = Symbol('kerf.overlayArbitration');\n\n/** An arbitration input: a dismiss trigger, or Tab for the focus trap. */\ntype ArbitrationKind = DismissTrigger | 'trap';\n\ninterface OverlaySurface {\n el: HTMLElement;\n triggers: readonly DismissTrigger[];\n modal: boolean;\n /** Where focus returns on close; re-pointed when the surface holding it closes first. */\n restoreTo: Element | null;\n /** Non-modal only: move (or re-show) into the top layer above a newly opened modal `<dialog>`. */\n liftAbove?: () => void;\n}\n\ninterface OverlayArbitration {\n stack: OverlaySurface[];\n handled: WeakSet<Event>;\n /** Document capture listener, installed only while the stack is non-empty. */\n onToggle: (event: Event) => void;\n /** Open shadow roots carrying `onToggle` too (a shadow `toggle` never reaches the document). */\n shadowRoots: Set<ShadowRoot>;\n}\n\ntype ArbitratedDocument = Document & {\n [OVERLAY_ARBITRATION]?: OverlayArbitration;\n};\n\nfunction overlayArbitration(doc: Document): OverlayArbitration {\n const arbitrated = doc as ArbitratedDocument;\n return (arbitrated[OVERLAY_ARBITRATION] ??= {\n stack: [],\n handled: new WeakSet(),\n // KF-AHY6H4: an app-owned modal `<dialog>` opened outside kerf would leave\n // the kerf surfaces already showing inert beneath it. A dialog fires\n // `toggle` after it opens (it does not bubble, so listen in the capture\n // phase); re-host above it exactly as kerf does for its own dialogs, which\n // are skipped here because `overlay()` already re-hosted synchronously.\n onToggle: (event) => {\n const dialog = event.target as Element;\n const arbitration = arbitrated[OVERLAY_ARBITRATION] as OverlayArbitration;\n if (\n dialog.tagName === 'DIALOG' &&\n !arbitration.stack.some(({ el }) => el === dialog) &&\n isOpenModal(dialog, arbitration)\n )\n rehostAbove(arbitration, dialog);\n },\n shadowRoots: new Set(),\n });\n}\n\n// Every open shadow root in `root`, nested ones included (KF-1FGTX1: Web\n// Awesome's `wa-dialog` / `wa-drawer` keep their modal `<dialog>` in one, where\n// neither `querySelectorAll` nor a document `toggle` listener can see it). A\n// closed shadow root is invisible by design. One element walk per `overlay()`\n// open — a user-paced event — so the cost stays off every render path.\nfunction openShadowRoots(\n root: Document | ShadowRoot,\n found: ShadowRoot[] = [],\n): ShadowRoot[] {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {\n const shadow = (node as Element).shadowRoot;\n if (shadow !== null) {\n found.push(shadow);\n openShadowRoots(shadow, found);\n }\n }\n return found;\n}\n\n// Install the `toggle` watch on every open shadow root not yet watched, so an\n// app-owned modal `<dialog>` opening inside one re-hosts the surfaces showing.\nfunction watchShadowRoots(\n arbitration: OverlayArbitration,\n roots: readonly ShadowRoot[],\n): void {\n for (const root of roots)\n if (!arbitration.shadowRoots.has(root)) {\n arbitration.shadowRoots.add(root);\n root.addEventListener('toggle', arbitration.onToggle, true);\n }\n}\n\n// Is `el` inside `dialog` in the flat tree — the rendered tree, where content\n// slotted into a shadow host renders inside the host's shadow `<dialog>`?\nfunction renderedInside(el: Element, dialog: Element): boolean {\n for (let node: Element | null = el; node !== null;) {\n if (node === dialog) return true;\n const root = node.getRootNode();\n node =\n node.assignedSlot ??\n node.parentElement ??\n (root instanceof ShadowRoot ? root.host : null);\n }\n return false;\n}\n\n// Every non-modal kerf surface already open is blocked beneath a modal\n// `<dialog>` that just opened: lift (or re-show) it above (KF-FJ9VD8). One\n// rendered inside that dialog — including a slot-hosted one in a shadow\n// host's light DOM — is part of its subtree already.\nfunction rehostAbove(arbitration: OverlayArbitration, dialog: Element): void {\n for (const { el, liftAbove } of arbitration.stack)\n if (el !== dialog && el.isConnected && !renderedInside(el, dialog))\n liftAbove?.();\n}\n\n// Is `dialog` an open modal `<dialog>`? kerf's own are known from the stack;\n// an app-owned one is found through `:modal`, which an older engine may not\n// parse (then only kerf's own count).\nfunction isOpenModal(\n dialog: Element,\n arbitration: OverlayArbitration,\n): boolean {\n if (!dialog.isConnected || (dialog as HTMLDialogElement).open !== true)\n return false;\n if (arbitration.stack.some(({ el, modal }) => modal && el === dialog))\n return true;\n try {\n return dialog.matches(':modal');\n } catch {\n return false; // `:modal` unsupported\n }\n}\n\n// Is a modal `<dialog>` open (KF-0V9RTE: a plain surface opened in that state\n// is inert and painted beneath the dialog's top layer)?\n// The open shadow roots count too (KF-1FGTX1: a `wa-dialog`'s modal).\nfunction modalDialogOpen(\n doc: Document,\n arbitration: OverlayArbitration,\n shadowRoots: readonly ShadowRoot[],\n): boolean {\n return (\n arbitration.stack.some(({ el }) => isOpenModal(el, arbitration)) ||\n [doc, ...shadowRoots].some((root) =>\n Array.from(root.querySelectorAll('dialog[open]')).some((dialog) =>\n isOpenModal(dialog, arbitration),\n ),\n )\n );\n}\n\n// The modal context `el` renders in, resolved across shadow boundaries\n// (KF-F52T5D: Web Awesome's `wa-dialog` / `wa-drawer` keep their modal\n// `<dialog>` in a shadow root, so `closest('dialog')` from slotted light-DOM\n// content found nothing and the surface landed, inert, in `document.body`).\n// Walks the light-DOM ancestors, continuing from a shadow tree to its host.\n// The nearest `<dialog>` decides, as before; so does a shadow host whose slot\n// for the walked-from child sits inside a `<dialog>` in its shadow root. Only\n// an OPEN MODAL dialog yields a context: `dialog` is that element, `root` the\n// light-DOM element a host slot belongs in — the dialog itself, or the shadow\n// host (content appended to it is slotted into the dialog).\ninterface ModalContext {\n dialog: Element;\n root: Element;\n}\nfunction modalContext(\n el: Element,\n arbitration: OverlayArbitration,\n): ModalContext | undefined {\n let child: Element | undefined;\n for (let node: Element | null = el; node !== null;) {\n const dialog =\n node.localName === 'dialog'\n ? node\n : child?.assignedSlot?.closest('dialog');\n if (dialog != null)\n return isOpenModal(dialog, arbitration)\n ? { dialog, root: node }\n : undefined;\n child = node;\n const root = node.getRootNode();\n node =\n node.parentElement ?? (root instanceof ShadowRoot ? root.host : null);\n }\n return undefined;\n}\n\n// The in-dialog host slot (KF-FBHQEP: a surface lifted over a modal\n// `<dialog>` is visible but inert, because every engine inerts content outside\n// the modal). A `popover()` / `tooltip()` whose anchor renders inside an open\n// modal `<dialog>` (see `modalContext`) renders into that context's\n// `[data-kerf-overlay-host]` element instead, so it is part of the modal\n// subtree and fully interactive. An app-owned native dialog opts in by marking\n// an element; a dialog kerf opened itself, and a shadow-hosted modal (whose\n// author cannot reach inside the shadow root), get one on demand —\n// `display: contents` so it adds no layout box, and `data-morph-skip` +\n// `data-morph-preserve` so the content's own `mount()` neither re-renders nor\n// removes it (`utils/overlay-host.ts` makes the slot a nested-mount boundary).\nexport function overlayHost(anchor: Element): Element | undefined {\n const arbitration = overlayArbitration(document);\n const context = modalContext(anchor, arbitration);\n if (context === undefined) return undefined;\n const { dialog, root } = context;\n let host = Array.from(root.querySelectorAll(OVERLAY_HOST_SELECTOR)).find(\n (el) => modalContext(el, arbitration)?.root === root,\n );\n if (\n host === undefined &&\n (root !== dialog || arbitration.stack.some(({ el }) => el === dialog))\n ) {\n root.insertAdjacentHTML(\n 'beforeend',\n '<div data-kerf-overlay-host data-morph-skip data-morph-preserve style=\"display:contents\"></div>',\n );\n host = root.lastElementChild as Element;\n }\n return host;\n}\n\n// `self` is on the stack and handles `kind` (its callers are exactly the\n// handlers it installed for that kind), so it owns `kind` unless some surface\n// above it handles `kind` too or is modal.\nfunction ownsInput(\n stack: readonly OverlaySurface[],\n self: OverlaySurface,\n kind: ArbitrationKind,\n): boolean {\n for (let i = stack.indexOf(self) + 1; i < stack.length; i++) {\n const above = stack[i];\n if (above.modal || (kind !== 'trap' && above.triggers.includes(kind)))\n return false;\n }\n return true;\n}\n\n// Validate a string `initialFocus` BEFORE any DOM mutation: `querySelector`\n// throws a generic `SyntaxError` DOMException on a malformed selector, and the\n// focus pass runs last — after the wrapper, mount, listeners, and stack entry\n// already exist — so an unvalidated selector would strand all of them with no\n// handle to close. An inert fragment parses the selector without matching.\nfunction assertInitialFocusSelector(selector: string): void {\n try {\n document.createDocumentFragment().querySelector(selector);\n } catch (cause) {\n throw new Error(\n `overlay(): invalid initialFocus selector ${JSON.stringify(selector)}`,\n { cause },\n );\n }\n}\n\n/**\n * Open an overlay: append a wrapper to `container`, `mount()` `content` inside\n * it, wire the requested dismissals + (optionally) a focus trap, and return a\n * handle. Concurrent fallback overlays dismiss from the top down. See\n * {@link OverlayOptions}.\n *\n * Construction is transactional: a malformed `initialFocus` selector throws a\n * descriptive error before anything is touched, and if a later setup phase\n * throws (the content's first render, `showModal()` / `showPopover()`), every\n * piece already installed — wrapper node, mount, listeners, fallback-stack\n * entry, top-layer state, moved focus — is rolled back before the original\n * error is rethrown.\n */\nexport function overlay(\n content: OverlayContent,\n options: OverlayOptions = {},\n): OverlayHandle {\n const {\n container = document.body,\n className = 'kerf-overlay',\n dismiss = ['escape', 'backdrop'],\n initialFocus = true,\n trap = true,\n role = 'dialog',\n onDismiss,\n outsideIgnore,\n native = false,\n } = options;\n\n if (typeof initialFocus === 'string')\n assertInitialFocusSelector(initialFocus);\n\n const triggers: readonly DismissTrigger[] =\n dismiss === false ? [] : Array.isArray(dismiss) ? dismiss : [dismiss];\n\n // Native top-layer backing (KF-526), feature-detected: a modal overlay\n // (`trap`) → `<dialog>.showModal()`; a non-modal one → `[popover]`. Each falls\n // back to the plain `<div>` where the API is missing, so behavior is unchanged\n // on unsupporting engines. A surface opened while a modal `<dialog>` is open\n // is lifted the same way even without `native` (KF-0V9RTE), because a plain\n // `<div>` would be inert and painted beneath the dialog.\n const arbitration = overlayArbitration(document);\n // A surface placed inside an open modal `<dialog>` (its host slot, or an\n // explicit `container` there, including one slotted into a shadow-root\n // modal) is already part of the modal subtree: no lift.\n const containerContext = modalContext(container, arbitration);\n const shadowRoots = openShadowRoots(document);\n const liftOverModal =\n !native &&\n containerContext === undefined &&\n modalDialogOpen(document, arbitration, shadowRoots);\n const topLayer = native || liftOverModal;\n const useDialog = topLayer && trap && supportsDialog();\n const usePopover = topLayer && !trap && supportsPopover();\n\n const wrapper: HTMLElement = useDialog\n ? document.createElement('dialog')\n : document.createElement('div');\n wrapper.className = className;\n if (usePopover) wrapper.setAttribute('popover', 'manual');\n // `<dialog>.showModal()` conveys modality natively (role + inert), so the ARIA\n // attributes are only needed on the plain-`<div>` fallback.\n if (trap && !useDialog) {\n wrapper.setAttribute('role', role);\n wrapper.setAttribute('aria-modal', 'true');\n }\n const surface: OverlaySurface = {\n el: wrapper,\n triggers,\n modal: trap,\n restoreTo: document.activeElement,\n };\n\n // Does this surface own `kind` right now (and has no surface acted on `event`)?\n const owns = (kind: ArbitrationKind, event?: Event): boolean =>\n !(event !== undefined && arbitration.handled.has(event)) &&\n ownsInput(arbitration.stack, surface, kind);\n\n const removers: Array<() => void> = [];\n const resultBox: { resolve?: (value: unknown) => void } = {};\n const result = new Promise<unknown>((resolve) => {\n resultBox.resolve = resolve;\n });\n const state = { closed: false };\n let disposeMount: (() => void) | undefined;\n let nativeOpened = false;\n\n // KF-FJ9VD8: a modal `<dialog>` kerf opens later would leave this non-modal\n // surface inert beneath it, so the opener calls this to move it into the top\n // layer — or, if it is already a `[popover]`, re-show it so it stacks above\n // the new dialog (the top layer orders by most recent show).\n if (!trap)\n surface.liftAbove = () => {\n if (!supportsPopover()) {\n devHooks.overlayBlockedByModal?.('hidden');\n return;\n }\n const el = wrapper as HTMLElement & {\n showPopover(): void;\n hidePopover(): void;\n };\n if (nativeOpened) el.hidePopover();\n else {\n wrapper.setAttribute('popover', 'manual');\n wrapper.style.inset = 'auto';\n }\n el.showPopover();\n nativeOpened = true;\n if (wrapper.querySelector(FOCUSABLE) !== null)\n devHooks.overlayBlockedByModal?.('inert');\n };\n\n // Also the construction rollback (see the transaction below): every step\n // tolerates a phase that never ran — no mount yet, no stack entry, never\n // appended, never shown.\n function close(value?: unknown): void {\n if (state.closed) return;\n state.closed = true;\n const { stack } = arbitration;\n const stackIndex = stack.indexOf(surface);\n if (stackIndex !== -1) stack.splice(stackIndex, 1);\n if (stack.length === 0) {\n document.removeEventListener('toggle', arbitration.onToggle, true);\n for (const root of arbitration.shadowRoots)\n root.removeEventListener('toggle', arbitration.onToggle, true);\n arbitration.shadowRoots.clear();\n }\n // A surface opened from inside this one would restore focus into a node\n // that is about to be detached: hand it this surface's own restore target.\n for (const other of stack)\n if (other.restoreTo !== null && wrapper.contains(other.restoreTo))\n other.restoreTo = surface.restoreTo;\n const active = document.activeElement;\n for (const remove of removers) remove();\n disposeMount?.();\n // Exit the top layer before removing the node, so the native close steps run\n // (restore inert, fire the dialog's `close`). `<dialog>` also restores focus\n // itself; kerf's manual restore below stays as the fallback path's behavior.\n if (nativeOpened) {\n nativeOpened = false;\n if (useDialog) (wrapper as HTMLDialogElement).close();\n else (wrapper as HTMLElement & { hidePopover(): void }).hidePopover();\n }\n wrapper.remove();\n restoreFocus(active);\n resultBox.resolve?.(value);\n }\n\n // Closing a surface that doesn't hold focus (an out-of-order close beneath\n // another open surface) must not pull focus out of the surface that does;\n // otherwise focus returns to where it was when this surface opened.\n function restoreFocus(active: Element | null): void {\n if (\n active instanceof HTMLElement &&\n active.isConnected &&\n arbitration.stack.some((other) => other.el.contains(active))\n ) {\n if (document.activeElement !== active) active.focus();\n return;\n }\n const { restoreTo } = surface;\n if (restoreTo instanceof HTMLElement && restoreTo.isConnected)\n restoreTo.focus();\n }\n\n function userDismiss(): void {\n onDismiss?.();\n close();\n }\n\n // One transaction: if any phase below throws (the content's first render,\n // `showModal()` / `showPopover()`, …), roll back whatever the earlier phases\n // installed via `close()` and rethrow the original error. No handle is\n // returned on failure, so nothing may outlive the call.\n try {\n container.appendChild(wrapper);\n disposeMount = mount(\n wrapper,\n typeof content === 'function' ? content : () => content,\n );\n // Watch for app-owned modal dialogs while any kerf surface is open.\n // Shadow roots are refreshed on every open; one attached after the latest\n // open is not watched until the next.\n if (arbitration.stack.push(surface) === 1)\n document.addEventListener('toggle', arbitration.onToggle, true);\n watchShadowRoots(arbitration, shadowRoots);\n\n // Enter the top layer after the content is mounted + connected.\n // `showModal()` moves focus into the dialog by default; kerf's\n // `initialFocus` pass below runs afterward and wins.\n if (useDialog) {\n (wrapper as HTMLDialogElement).showModal();\n nativeOpened = true;\n rehostAbove(arbitration, wrapper);\n } else if (usePopover) {\n (wrapper as HTMLElement & { showPopover(): void }).showPopover();\n nativeOpened = true;\n // Neutralize the UA `[popover] { inset: 0; margin: auto }` anchoring so\n // `positionAnchored` (which sets `top`/`left`) controls placement —\n // otherwise the retained `right`/`bottom` insets would stretch/center it.\n wrapper.style.inset = 'auto';\n }\n\n installDismissal();\n if (container.closest(OVERLAY_HOST_SELECTOR) !== null) {\n makeTabReachable();\n // KF-WZ9KQM: a slot-hosted surface leaves with its dialog even when its\n // anchor (moved out of the dialog) stays connected, so the anchor watch\n // never fires. Watch the wrapper itself: close as cleanup (no\n // `onDismiss`) and leave the open-order stack.\n removers.push(attach(wrapper, () => close));\n }\n // KF-F52T5D: a surface inside a modal context leaves when that dialog\n // closes, as cleanup (no `onDismiss`). Closing an app-owned or\n // shadow-hosted dialog removes nothing, and a shadow dialog's `close`\n // never reaches a light-DOM listener, so a caller could not watch it.\n if (containerContext !== undefined) {\n const { dialog } = containerContext;\n const onDialogClose = (): void => close();\n dialog.addEventListener('close', onDialogClose);\n removers.push(() => dialog.removeEventListener('close', onDialogClose));\n }\n applyInitialFocus();\n } catch (error) {\n close();\n throw error;\n }\n\n // What the lift cannot repair: no top-layer API at all, or a lifted\n // non-modal surface whose controls the modal still inerts.\n if (liftOverModal) {\n if (!useDialog && !usePopover) devHooks.overlayBlockedByModal?.('hidden');\n else if (usePopover && wrapper.querySelector(FOCUSABLE) !== null)\n devHooks.overlayBlockedByModal?.('inert');\n }\n\n return { el: wrapper, close, result };\n\n function installDismissal(): void {\n const wantEscape = triggers.includes('escape');\n if (useDialog) {\n // A native modal `<dialog>` confines Tab focus itself and surfaces Escape as\n // a `cancel` event. Take that over: `preventDefault` so kerf owns teardown\n // (and so Escape is swallowed when it isn't a dismiss trigger), then dismiss.\n // A surface stacked above that handles Escape (or is modal) owns it; a\n // stray `cancel` then leaves this dialog open.\n const onCancel = (event: Event): void => {\n event.preventDefault();\n if (wantEscape && owns('escape')) userDismiss();\n };\n wrapper.addEventListener('cancel', onCancel);\n removers.push(() => wrapper.removeEventListener('cancel', onCancel));\n } else if (wantEscape || trap) {\n const onKeydown = (event: KeyboardEvent): void => {\n if (wantEscape && event.key === 'Escape') {\n if (!owns('escape', event)) return;\n // `preventDefault` also withholds the close request from a native\n // `<dialog>` beneath, so one Escape closes exactly one surface.\n event.stopPropagation();\n event.preventDefault();\n arbitration.handled.add(event);\n userDismiss();\n return;\n }\n if (trap && event.key === 'Tab' && owns('trap')) {\n const items = focusable(wrapper);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const active = document.activeElement;\n const outside = !wrapper.contains(active);\n if (event.shiftKey && (active === first || outside)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (active === last || outside)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n document.addEventListener('keydown', onKeydown, true);\n removers.push(() =>\n document.removeEventListener('keydown', onKeydown, true),\n );\n }\n\n if (triggers.includes('backdrop')) {\n const onClick = (event: Event): void => {\n if (event.target !== wrapper || !owns('backdrop', event)) return;\n arbitration.handled.add(event);\n userDismiss();\n };\n wrapper.addEventListener('click', onClick);\n removers.push(() => wrapper.removeEventListener('click', onClick));\n }\n\n if (triggers.includes('outside')) {\n const ignore =\n outsideIgnore === undefined\n ? []\n : Array.isArray(outsideIgnore)\n ? outsideIgnore\n : [outsideIgnore];\n // Capture phase: the click that opened this overlay already passed\n // document's capture phase, so this never fires for that opening click.\n const onDocClick = (event: Event): void => {\n const target = event.target as Node | null;\n if (target === null) return;\n if (wrapper.contains(target)) return;\n if (ignore.some((el) => el === target || el.contains(target))) return;\n if (!owns('outside', event)) return;\n arbitration.handled.add(event);\n userDismiss();\n };\n document.addEventListener('click', onDocClick, true);\n removers.push(() =>\n document.removeEventListener('click', onDocClick, true),\n );\n }\n }\n\n // KF-QZ9SFG (slot-hosted controls unreachable by Tab in macOS WebKit): a\n // surface in an overlay host slot sits in the modal dialog's own Tab order,\n // not behind a kerf trap, so WebKit's system keyboard-navigation preference\n // would skip its implicitly tabbable controls. Make those stops explicit, as\n // the trap does — now, and again before each Tab so a control a re-render\n // added (or whose tabindex the morph dropped) is reachable too.\n function makeTabReachable(): void {\n focusable(wrapper);\n const onTab = (event: KeyboardEvent): void => {\n if (event.key === 'Tab') focusable(wrapper);\n };\n document.addEventListener('keydown', onTab, true);\n removers.push(() => document.removeEventListener('keydown', onTab, true));\n }\n\n function applyInitialFocus(): void {\n if (initialFocus !== false) {\n if (typeof initialFocus === 'string') {\n wrapper.querySelector<HTMLElement>(initialFocus)?.focus();\n } else {\n const first = focusable(wrapper)[0];\n if (first !== undefined) {\n first.focus();\n } else {\n wrapper.tabIndex = -1;\n wrapper.focus();\n }\n }\n }\n }\n}\n\n/**\n * Internal: run a promise-dialog helper's post-open wiring (required-slot\n * lookups, `delegate()` handlers, key listeners) as the tail of `overlay()`'s\n * construction transaction. `wire` registers a disposer for every listener it\n * installs via `track`; if any step throws, those listeners are removed, the\n * just-opened overlay is closed (node, mount, dismissal listeners, stack\n * entry, top-layer state, focus), and the original error is rethrown — so a\n * failed helper call returns no promise and leaves no open overlay behind.\n *\n * After construction, `guard` wraps a user-action handler (OK click, Enter).\n * KF-B0CFQP (a throwing `validate` callback): a throw inside a guarded action\n * is a programming error, so it closes the dialog (full teardown, focus\n * restored) and the returned promise REJECTS with the original error, instead\n * of the error escaping the event handler and stranding an open dialog whose\n * OK can never succeed. Otherwise the returned promise settles with\n * `settle(handle.result's value)`. Not re-exported from `kerfjs/overlay`.\n */\nexport function wireDialog<T>(\n handle: OverlayHandle,\n wire: (\n track: (dispose: () => void) => void,\n guard: (action: () => void) => () => void,\n ) => void,\n settle: (value: unknown) => T,\n): Promise<T> {\n const disposers: Array<() => void> = [];\n // Holds a guarded action's error AND is the close() value that marks the\n // failure, so only a throw that actually settles the dialog rejects it.\n const failure: unknown[] = [];\n try {\n wire(\n (dispose) => void disposers.push(dispose),\n (action) => () => {\n try {\n action();\n } catch (error) {\n failure[0] = error;\n handle.close(failure);\n }\n },\n );\n } catch (error) {\n disposers.forEach((dispose) => dispose());\n handle.close();\n throw error;\n }\n // One hop, not a chain: `settle` maps the value in the same reaction, so a\n // helper's promise settles as soon after `close()` as it always has.\n return handle.result.then((value) => {\n if (value === failure) throw failure[0];\n return settle(value);\n });\n}\n","/** `choice()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\n\n/** One choosable action in a {@link choice} dialog. */\nexport interface ChoiceAction<R> {\n value: R;\n label: string;\n className?: string;\n}\n\n/** Wiring slots for a {@link ChoiceOptions.render}. */\nexport interface ChoiceRenderSlots {\n message: string;\n actions: Array<Record<string, string>>;\n}\n\n/** Options for {@link choice}. */\nexport interface ChoiceOptions<R> {\n container?: Element;\n className?: string;\n title?: string;\n defaultValue?: R;\n native?: boolean;\n render?: (slots: ChoiceRenderSlots) => OverlayContent;\n}\n\n/** The N-way sibling of {@link confirm}. */\nexport function choice<R>(\n message: string,\n actions: ReadonlyArray<ChoiceAction<R>>,\n options: ChoiceOptions<R> = {},\n): Promise<R | null> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n defaultValue,\n native = false,\n render,\n } = options;\n const hasDefault = 'defaultValue' in options;\n const actionAttrs = actions.map((_, i) => ({ 'data-choice': String(i) }));\n\n const body: OverlayContent =\n render !== undefined\n ? render({ message, actions: actionAttrs })\n : jsx('div', {\n class: 'kerf-choice',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-choice__title', children: title })\n : '',\n jsx('p', { class: 'kerf-choice__message', children: message }),\n jsx('div', {\n class: 'kerf-choice__actions',\n children: actions.map((action, i) =>\n jsx('button', {\n type: 'button',\n class:\n action.className !== undefined\n ? `kerf-choice__action ${action.className}`\n : 'kerf-choice__action',\n ...actionAttrs[i],\n children: action.label,\n }),\n ),\n }),\n ],\n });\n\n let resolveChoice!: (result: R | null) => void;\n const result = new Promise<R | null>((resolve) => {\n resolveChoice = resolve;\n });\n\n const handle = overlay(body, {\n container,\n className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-choice]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a throw closes the overlay. Any\n // close settles a still-pending choice as a dismissal (`null`).\n void wireDialog(\n handle,\n (track) => {\n track(\n delegate(handle.el, 'click', '[data-choice]', (_event, el) => {\n resolveChoice(actions[Number(el.getAttribute('data-choice'))].value);\n handle.close();\n }),\n );\n\n if (hasDefault) {\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key === 'Enter') {\n event.preventDefault();\n resolveChoice(defaultValue as R);\n handle.close();\n }\n };\n // Last wiring step: nothing after it can throw, so it needs no rollback.\n handle.el.addEventListener('keydown', onKeydown);\n }\n },\n () => resolveChoice(null),\n );\n return result;\n}\n","/** `confirm()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\n\n/** Wiring slots passed to a {@link ConfirmOptions.render}. */\nexport interface ConfirmRenderSlots {\n message: string;\n /** Spread onto the confirm control. */\n ok: Record<string, string>;\n /** Spread onto the cancel control. */\n cancel: Record<string, string>;\n}\n\n/** Options for {@link confirm}. */\nexport interface ConfirmOptions {\n /** Where to append the overlay. Default `document.body`. */\n container?: Element;\n /** Wrapper class. Default `'kerf-overlay'`. */\n className?: string;\n /** Optional heading above the message. */\n title?: string;\n /** Confirm button label. Default `'OK'`. */\n okText?: string;\n /** Cancel button label. Default `'Cancel'`. */\n cancelText?: string;\n /** Add a `kerf-confirm--danger` class to the wrapper for destructive actions. */\n danger?: boolean;\n /** Host the dialog in the browser top layer where supported. */\n native?: boolean;\n /** Bring your own body while retaining kerf's dialog wiring. */\n render?: (slots: ConfirmRenderSlots) => OverlayContent;\n}\n\n/** A promise-based `window.confirm` replacement. */\nexport function confirm(\n message: string,\n options: ConfirmOptions = {},\n): Promise<boolean> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n okText = 'OK',\n cancelText = 'Cancel',\n danger = false,\n native = false,\n render,\n } = options;\n\n const body: OverlayContent =\n render !== undefined\n ? render({\n message,\n ok: { 'data-confirm': 'ok' },\n cancel: { 'data-confirm': 'cancel' },\n })\n : jsx('div', {\n class: 'kerf-confirm',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-confirm__title', children: title })\n : '',\n jsx('p', { class: 'kerf-confirm__message', children: message }),\n jsx('div', {\n class: 'kerf-confirm__actions',\n children: [\n jsx('button', {\n type: 'button',\n 'data-confirm': 'cancel',\n children: cancelText,\n }),\n jsx('button', {\n type: 'button',\n 'data-confirm': 'ok',\n class: 'kerf-confirm__ok',\n children: okText,\n }),\n ],\n }),\n ],\n });\n\n const handle = overlay(body, {\n container,\n className: danger ? `${className} kerf-confirm--danger` : className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-confirm=\"ok\"]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a throw closes the overlay. The\n // click table is the only (and so the last) step, so it needs no rollback.\n return wireDialog(\n handle,\n () => {\n delegate(handle.el, 'click', '[data-confirm]', (_event, el) => {\n handle.close(el.getAttribute('data-confirm') === 'ok');\n });\n },\n (value) => value === true,\n );\n}\n","/** `prompt()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\n\n/**\n * Validate a single field's value, synchronously: return a non-empty message\n * to block OK, anything else to accept. A validator that throws is treated as\n * a programming error — the dialog closes and the returned promise rejects\n * with that error.\n */\nexport type FieldValidator = (\n value: string,\n) => string | null | undefined | void;\n\n/** Options for {@link prompt}. */\n/**\n * Run a {@link FieldValidator}: the message it reports, or `undefined` when the\n * value is valid. Validators are synchronous — a promise result is a\n * programming error that closes the dialog and rejects its promise (the stray\n * promise's own rejection is silenced so it is not reported twice).\n * Internal to the dialog helpers.\n */\nexport function runValidator(\n validate: FieldValidator | undefined,\n value: string,\n owner: string,\n): string | undefined {\n const result: unknown = validate?.(value);\n if (typeof (result as PromiseLike<unknown> | null)?.then === 'function') {\n (result as PromiseLike<unknown>).then(undefined, () => undefined);\n throw new TypeError(`${owner}: validate returned a promise.`);\n }\n return typeof result === 'string' && result.length > 0 ? result : undefined;\n}\n\nexport interface PromptOptions {\n container?: Element;\n className?: string;\n title?: string;\n defaultValue?: string;\n placeholder?: string;\n inputType?: string;\n okText?: string;\n cancelText?: string;\n validate?: FieldValidator;\n native?: boolean;\n render?: (slots: PromptRenderSlots) => OverlayContent;\n}\n\n/** Wiring slots for a {@link PromptOptions.render}. */\nexport interface PromptRenderSlots {\n message: string;\n input: Record<string, string>;\n error: Record<string, string>;\n ok: Record<string, string>;\n cancel: Record<string, string>;\n}\n\n/** A promise-based `window.prompt` replacement. */\nexport function prompt(\n message: string,\n options: PromptOptions = {},\n): Promise<string | null> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n defaultValue = '',\n placeholder,\n inputType = 'text',\n okText = 'OK',\n cancelText = 'Cancel',\n validate,\n native = false,\n render,\n } = options;\n\n const inputAttrs: Record<string, string> = {\n 'data-prompt-input': '',\n type: inputType,\n value: defaultValue,\n ...(placeholder !== undefined ? { placeholder } : {}),\n };\n\n const body: OverlayContent =\n render !== undefined\n ? render({\n message,\n input: inputAttrs,\n error: { 'data-prompt-error': '' },\n ok: { 'data-prompt': 'ok' },\n cancel: { 'data-prompt': 'cancel' },\n })\n : jsx('div', {\n class: 'kerf-prompt',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-prompt__title', children: title })\n : '',\n jsx('label', { class: 'kerf-prompt__message', children: message }),\n jsx('input', { class: 'kerf-prompt__input', ...inputAttrs }),\n jsx('p', {\n class: 'kerf-prompt__error',\n 'data-prompt-error': '',\n children: '',\n }),\n jsx('div', {\n class: 'kerf-prompt__actions',\n children: [\n jsx('button', {\n type: 'button',\n 'data-prompt': 'cancel',\n children: cancelText,\n }),\n jsx('button', {\n type: 'button',\n 'data-prompt': 'ok',\n class: 'kerf-prompt__ok',\n children: okText,\n }),\n ],\n }),\n ],\n });\n\n const handle = overlay(body, {\n container,\n className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-prompt-input]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a missing required input or any\n // other throw below removes what was wired, closes the overlay, and rethrows.\n return wireDialog(\n handle,\n (track, guard) => {\n // Looked up again at OK: a reactive re-render may have replaced the\n // input, and a detached node would report a stale value. Missing at\n // construction is a render bug (rolls back, throws); missing at OK\n // rejects the promise through `guard`.\n const inputFor = (problem = 'render missing'): HTMLInputElement => {\n const input = handle.el.querySelector<HTMLInputElement>(\n 'input[data-prompt-input]',\n );\n if (input !== null) return input;\n throw new Error(`prompt(): ${problem} <input data-prompt-input>.`);\n };\n inputFor();\n const errorEl = handle.el.querySelector<HTMLElement>(\n '[data-prompt-error]',\n );\n if (errorEl !== null) errorEl.hidden = true;\n\n // A throwing (or promise-returning) `validate` closes the dialog and\n // rejects the promise (guard).\n const attemptOk = guard((): void => {\n const input = inputFor('removed after open:');\n const value = input.value;\n const error = runValidator(validate, value, 'prompt()');\n if (error !== undefined) {\n if (errorEl !== null) {\n errorEl.textContent = error;\n errorEl.hidden = false;\n }\n input.focus();\n return;\n }\n handle.close(value);\n });\n\n track(\n delegate(handle.el, 'click', '[data-prompt]', (_event, el) => {\n if (el.getAttribute('data-prompt') === 'ok') attemptOk();\n else handle.close(null);\n }),\n );\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (\n event.key === 'Enter' &&\n (event.target as Element | null)?.matches('input[data-prompt-input]')\n ) {\n event.preventDefault();\n attemptOk();\n }\n };\n // Last wiring step: nothing after it can throw, so it needs no rollback.\n handle.el.addEventListener('keydown', onKeydown);\n },\n (value) => (typeof value === 'string' ? value : null),\n );\n}\n","/** `form()` implementation and public option types. */\nimport { delegate } from './delegate.js';\nimport { jsx } from './jsx-runtime.js';\nimport { overlay, type OverlayContent, wireDialog } from './overlay-core.js';\nimport { type FieldValidator, runValidator } from './overlay-prompt.js';\n\n/** A single field in a {@link form}. */\nexport interface FormField {\n name: string;\n label?: string;\n defaultValue?: string;\n placeholder?: string;\n type?: string;\n validate?: FieldValidator;\n}\n\n/** One field's wiring in a {@link FormRenderSlots}. */\nexport interface FormRenderField {\n name: string;\n label: string;\n input: Record<string, string>;\n error: Record<string, string>;\n}\n\n/** Wiring slots for a {@link FormOptions.render}. */\nexport interface FormRenderSlots {\n fields: FormRenderField[];\n ok: Record<string, string>;\n cancel: Record<string, string>;\n}\n\n/** Options for {@link form}. */\nexport interface FormOptions {\n container?: Element;\n className?: string;\n title?: string;\n okText?: string;\n cancelText?: string;\n native?: boolean;\n render?: (slots: FormRenderSlots) => OverlayContent;\n}\n\n/** A promise-based multi-field dialog. */\nexport function form(\n fields: readonly FormField[],\n options: FormOptions = {},\n): Promise<Record<string, string> | null> {\n const {\n container,\n className = 'kerf-overlay',\n title,\n okText = 'OK',\n cancelText = 'Cancel',\n native = false,\n render,\n } = options;\n\n const fieldAttrs = (field: FormField): Record<string, string> => ({\n 'data-field': field.name,\n name: field.name,\n type: field.type ?? 'text',\n value: field.defaultValue ?? '',\n ...(field.placeholder !== undefined\n ? { placeholder: field.placeholder }\n : {}),\n });\n\n const body: OverlayContent =\n render !== undefined\n ? render({\n fields: fields.map((field) => ({\n name: field.name,\n label: field.label ?? field.name,\n input: fieldAttrs(field),\n error: { 'data-field-error': field.name },\n })),\n ok: { 'data-form': 'ok' },\n cancel: { 'data-form': 'cancel' },\n })\n : jsx('div', {\n class: 'kerf-form',\n children: [\n title !== undefined\n ? jsx('h2', { class: 'kerf-form__title', children: title })\n : '',\n ...fields.map((field) =>\n jsx('div', {\n class: 'kerf-form__field',\n children: [\n jsx('label', {\n class: 'kerf-form__label',\n children: field.label ?? field.name,\n }),\n jsx('input', {\n class: 'kerf-form__input',\n ...fieldAttrs(field),\n }),\n jsx('p', {\n class: 'kerf-form__error',\n 'data-field-error': field.name,\n children: '',\n }),\n ],\n }),\n ),\n jsx('div', {\n class: 'kerf-form__actions',\n children: [\n jsx('button', {\n type: 'button',\n 'data-form': 'cancel',\n children: cancelText,\n }),\n jsx('button', {\n type: 'button',\n 'data-form': 'ok',\n class: 'kerf-form__ok',\n children: okText,\n }),\n ],\n }),\n ],\n });\n\n const handle = overlay(body, {\n container,\n className,\n dismiss: ['escape', 'backdrop'],\n initialFocus: '[data-field]',\n trap: true,\n native,\n });\n\n // Post-open wiring is still construction: a missing required field input or\n // any other throw below removes what was wired, closes the overlay, and\n // rethrows.\n return wireDialog(\n handle,\n (track, guard) => {\n const errorFor = (name: string): HTMLElement | null =>\n Array.from(\n handle.el.querySelectorAll<HTMLElement>('[data-field-error]'),\n ).find((el) => el.getAttribute('data-field-error') === name) ?? null;\n // KF-HRNJ4T (a field input removed after open by a reactive re-render):\n // at construction a missing input is a render bug that rolls back and\n // throws synchronously; at OK time it throws inside `guard`, so the\n // dialog closes and the promise rejects naming the field — never a\n // `null` that reads as a user Cancel.\n const inputFor = (\n name: string,\n problem = 'render missing',\n ): HTMLInputElement => {\n const input = Array.from(\n handle.el.querySelectorAll<HTMLInputElement>('input[data-field]'),\n ).find((el) => el.getAttribute('data-field') === name);\n if (input !== undefined) return input;\n throw new Error(`form(): ${problem} <input data-field=\"${name}\">.`);\n };\n\n for (const field of fields) {\n inputFor(field.name);\n const errorEl = errorFor(field.name);\n if (errorEl !== null) errorEl.hidden = true;\n }\n\n // A throwing `field.validate` closes the dialog and rejects the promise.\n const attemptOk = guard((): void => {\n const record: Record<string, string> = {};\n let firstInvalid: HTMLInputElement | null = null;\n for (const field of fields) {\n const el = inputFor(field.name, 'removed after open:');\n const value = el.value;\n record[field.name] = value;\n const error = runValidator(field.validate, value, 'form()');\n const errorEl = errorFor(field.name);\n if (error !== undefined) {\n if (errorEl !== null) {\n errorEl.textContent = error;\n errorEl.hidden = false;\n }\n if (firstInvalid === null) firstInvalid = el;\n } else if (errorEl !== null) {\n errorEl.hidden = true;\n }\n }\n if (firstInvalid !== null) {\n firstInvalid.focus();\n return;\n }\n handle.close(record);\n });\n\n track(\n delegate(handle.el, 'click', '[data-form]', (_event, el) => {\n if (el.getAttribute('data-form') === 'ok') attemptOk();\n else handle.close(null);\n }),\n );\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (\n event.key === 'Enter' &&\n (event.target as Element | null)?.matches('[data-field]')\n ) {\n event.preventDefault();\n attemptOk();\n }\n };\n // Last wiring step: nothing after it can throw, so it needs no rollback.\n handle.el.addEventListener('keydown', onKeydown);\n },\n (value) =>\n value !== null && typeof value === 'object'\n ? (value as Record<string, string>)\n : null,\n );\n}\n","/**\n * Anchored positioning primitives for `kerfjs/overlay` — position an element\n * relative to an anchor, with viewport flip + clamp. These are standalone: they\n * take any element and have no overlay lifecycle, so `popover()` / `tooltip()`\n * build on them but you can also position your own element (an inline hint, a\n * custom menu). Re-exported from `src/overlay.ts` so the public `kerfjs/overlay`\n * surface is unchanged (KF-511 split out of overlay.ts's dialog + toast code).\n */\n\n/** Vertical placement relative to an anchor (used by `popover`, {@link positionAnchored}, `tooltip`). */\nexport type PopoverPlacement = 'bottom' | 'top';\n\n/** Placement options for {@link positionAnchored} / {@link autoReposition}. */\nexport interface AnchorPositionOptions {\n /** Preferred side of the anchor; flips to the other side if it would overflow the viewport. Default `'bottom'`. */\n placement?: PopoverPlacement;\n /** Horizontal edge to line up with the anchor: `'start'` (left edges) or `'end'` (right edges). Default `'start'`. */\n align?: 'start' | 'end';\n /** Gap in px between the anchor and the element. Default `4`. */\n gap?: number;\n /**\n * Gutter in px kept between the element and the viewport edges: the\n * horizontal clamp stops this far inside the left and right edges, and a\n * side fits only when the element ends this far inside the top or bottom\n * edge. Default `0` (the bare viewport edge).\n */\n viewportMargin?: number;\n /**\n * The element's height can shrink (it scrolls its overflow). When neither\n * side has room for its full height, it takes the side with more room, and\n * that room — from the gap to the viewport margin, in `el`'s CSS px — is\n * published on `el` as the `--kerf-anchor-available-height` custom property\n * on every reposition. Cap the element with it, for example\n * `max-height: var(--kerf-anchor-available-height); overflow: auto`. The\n * height measured for that choice honors the element's own `max-height`\n * (read without the published property, so `max-height: min(300px,\n * var(--kerf-anchor-available-height))` counts as a 300px cap), so an\n * app-capped element that fits the preferred side stays there. A relative\n * cap (`60%`, `calc()`, `min()`, `clamp()`) is resolved against the\n * element's fixed-position containing block (normally the viewport), and a\n * `max-height` transition on the element neither skews that read nor runs\n * because of it. While an animation or transition is already driving\n * `max-height` (an expand-from-0 entrance, a `@keyframes` animation), the\n * cap is the value it is headed to, and it is left running. An above\n * placement is pinned by its bottom edge (`bottom`, with `top: auto`), so\n * the element grows and shrinks upward, away from the anchor, as its height\n * changes between repositions; a below placement sets `top` with\n * `bottom: auto`. Default `false`.\n */\n fitHeight?: boolean;\n}\n\n/** The custom property `fitHeight` publishes on the positioned element. */\nconst AVAILABLE_HEIGHT = '--kerf-anchor-available-height';\n\n/**\n * One-shot: position `el` relative to `anchor` — below by default, flipping above\n * if it would overflow the viewport, aligned to a horizontal edge and clamped into\n * view. Sets `el.style` `position: fixed`, `margin: 0`, `left`, and `top` (fixed so\n * `left`/`top` are viewport coordinates, matching `getBoundingClientRect`). When an\n * ancestor makes itself the containing block for fixed descendants (a\n * `transform`, `filter`, `perspective`, or `contain` on a wrapping `<dialog>`),\n * `left`/`top` are translated (and unscaled) into that box's coordinates so `el`\n * still lands next to the anchor. `viewportMargin` keeps a gutter from the\n * viewport edges; `fitHeight` publishes the room on the chosen side so CSS can\n * cap a scrolling element to it, and pins an above placement by `bottom`\n * (with `top: auto`) so the element grows upward from the anchor. This is\n * `popover()`'s placement core, usable on any element (an inline hint, a tooltip) —\n * no overlay lifecycle. Pair with {@link autoReposition} to keep it glued while open.\n */\nexport function positionAnchored(\n el: HTMLElement,\n anchor: Element,\n options: AnchorPositionOptions = {},\n): void {\n const {\n placement = 'bottom',\n align = 'start',\n gap = 4,\n viewportMargin = 0,\n fitHeight = false,\n } = options;\n // Fix `el` FIRST, then measure it. Measuring a still-`display:block` wrapper\n // reports the full body-content width (≈ viewport minus body margins), which\n // collapses the horizontal clamp below and lands the element at the body's left\n // edge. As a fixed, shrink-to-fit box its `width`/`height` are its real size.\n el.style.position = 'fixed';\n el.style.margin = '0';\n const anchorRect = anchor.getBoundingClientRect();\n const elementRect = el.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n // Vertical: preferred side, flipped only if it overflows and the other side\n // fits. A height-flexible element that fits neither side takes the roomier one.\n const height = fitHeight ? fullHeight(el, elementRect) : elementRect.height;\n const belowTop = anchorRect.bottom + gap;\n const roomBelow = viewportHeight - viewportMargin - belowTop;\n const roomAbove = anchorRect.top - gap - viewportMargin;\n let below = placement !== 'top';\n if (height > roomBelow && height > roomAbove) {\n if (fitHeight) below = roomBelow >= roomAbove;\n } else if (below && height > roomBelow) below = false;\n else if (!below && height > roomAbove) below = true;\n\n // Horizontal: align to an anchor edge, then clamp inside the viewport gutter.\n let left =\n align === 'end' ? anchorRect.right - elementRect.width : anchorRect.left;\n left = Math.max(\n viewportMargin,\n Math.min(left, viewportWidth - viewportMargin - elementRect.width),\n );\n\n // `left`/`top` so far are viewport coordinates. They are only the CSS values\n // too when `el`'s fixed-position containing block is the viewport; an\n // ancestor with a `transform`, `filter`, `perspective`, `contain`, … (say a\n // modal `<dialog>` holding an overlay host slot) captures fixed descendants,\n // so map the coordinates into that box's space.\n const [originX, originY, scaleX, scaleY] = fixedOrigin(el);\n el.style.left = `${(left - originX) / scaleX}px`;\n if (fitHeight) {\n const room = Math.max(0, below ? roomBelow : roomAbove);\n el.style.setProperty(AVAILABLE_HEIGHT, `${room / scaleY}px`);\n if (!below) {\n // Above, a height-flexible element hangs from its bottom edge. Pin\n // `bottom` instead of deriving `top` from a height measured now, so\n // whatever height it goes on to lay out (the cap from the room just\n // published, a `max-height` expand still running, content that grows)\n // grows and shrinks upward, away from the anchor, never over it.\n const edge = anchorRect.top - gap;\n el.style.top = 'auto';\n el.style.bottom = `${(fixedBottom(el, originY, scaleY) - edge) / scaleY}px`;\n return;\n }\n el.style.bottom = 'auto';\n }\n const top = below ? belowTop : anchorRect.top - gap - height;\n el.style.top = `${(top - originY) / scaleY}px`;\n}\n\n/**\n * The height `el` would take without the cap a previous reposition published:\n * its laid-out height plus whatever it currently scrolls, but no more than its\n * own `max-height` (see {@link ownMaxHeight}). Read from scroll metrics rather\n * than by lifting the cap, which would reset the element's own scroll position\n * — and every scroll inside it repositions it. Scroll metrics and `max-height`\n * are CSS px, so they are scaled into the viewport px of `rect`.\n */\nfunction fullHeight(el: HTMLElement, rect: DOMRect): number {\n const scale = el.offsetHeight ? rect.height / el.offsetHeight : 1;\n const full =\n rect.height + Math.max(0, el.scrollHeight - el.clientHeight) * scale;\n return Math.min(full, ownMaxHeight(el) * scale);\n}\n\n/** The vertical padding and border a `content-box` `max-height` excludes. */\nconst VERTICAL_EDGES = [\n 'paddingTop',\n 'paddingBottom',\n 'borderTopWidth',\n 'borderBottomWidth',\n] as const;\n\n/**\n * The stand-in room published while {@link ownMaxHeight} reads the element's\n * own cap: large enough that it never binds, and a valid length so a cap such\n * as `min(300px, var(--kerf-anchor-available-height))` still resolves (removing\n * the property would make that declaration invalid, so `none`).\n */\nconst UNBOUNDED_ROOM = '9999999px';\n\n/**\n * The inline properties {@link ownMaxHeight} overrides around its read, in\n * restore order: the published room, then the transition longhands.\n */\nconst SWAPPED = [\n AVAILABLE_HEIGHT,\n 'transition-property',\n 'transition-duration',\n 'transition-delay',\n];\n\n/**\n * `el`'s own border-box `max-height` in CSS px, excluding the room kerf\n * itself published. The published property is swapped for an unbounded room\n * only around a computed-style read (a style recalc, no layout, so nothing\n * reflows or scrolls) and restored at once, so kerf never reads its own last\n * cap back as the element's.\n *\n * A `max-height` transition would report its running value for that read (the\n * published room, not the cap) and start a transition on the swap, so for the\n * read `max-height` is appended to the element's transition list with no\n * duration or delay (the last entry for a property wins); every other running\n * transition keeps running. The restored cap is settled before the app's\n * transitions return, so they see no change to animate.\n *\n * While an animation or transition on `el` drives `max-height`, the cap is\n * the value it is headed to ({@link animatedMaxHeight}) and nothing is\n * swapped.\n *\n * A cap that stays relative after computing (a percentage, `calc()`,\n * `min()`, `clamp()`) is resolved against `el`'s containing block by\n * {@link resolveHeight}; a keyword (`none`, `fit-content`) is no cap.\n */\nfunction ownMaxHeight(el: HTMLElement): number {\n const style = window.getComputedStyle(el);\n const maxHeight = animatedMaxHeight(el) ?? swappedMaxHeight(el, style);\n const inset =\n style.boxSizing === 'border-box'\n ? 0\n : VERTICAL_EDGES.reduce(\n (sum, edge) => sum + (Number.parseFloat(style[edge]) || 0),\n 0,\n );\n if (maxHeight.endsWith('px')) return Number.parseFloat(maxHeight) + inset;\n // A keyword is no cap, and so is a keyframe `var()` that only `el` could\n // resolve (the probe does not inherit `el`'s custom properties).\n if (/^[a-z-]+$|var\\(/.test(maxHeight)) return Infinity;\n return resolveHeight(el, maxHeight) + inset;\n}\n\n/**\n * `el`'s computed `max-height` with the published room swapped out, as\n * {@link ownMaxHeight} describes: the room and, when `el` has transitions,\n * the transition longhands are overridden for the read and restored exactly.\n */\nfunction swappedMaxHeight(el: HTMLElement, style: CSSStyleDeclaration): string {\n const inline = el.style;\n const saved = SWAPPED.map((name): [string, string, string] => [\n name,\n inline.getPropertyValue(name),\n inline.getPropertyPriority(name),\n ]);\n // The computed transition lists, read before any of them is overridden.\n const lists = SWAPPED.map((name) => style.getPropertyValue(name).split(','));\n const transitions = lists[1]!;\n if (transitions[0] && transitions[0] !== 'none') {\n // Repeat the duration and delay lists to the property list's length, as\n // CSS does, so appending an entry leaves every existing pairing intact.\n for (let index = 1; index < 4; index++) {\n const items = lists[index]!;\n inline.setProperty(\n SWAPPED[index]!,\n [\n ...transitions.map((_, entry) => items[entry % items.length]),\n index > 1 ? '0s' : 'max-height',\n ].join(),\n 'important',\n );\n }\n }\n inline.setProperty(AVAILABLE_HEIGHT, UNBOUNDED_ROOM);\n const maxHeight = style.maxHeight;\n saved.forEach(([name, value, priority], index) => {\n if (value) inline.setProperty(name, value, priority);\n else inline.removeProperty(name);\n // Settle the restored room before the transitions return.\n if (!index) style.getPropertyValue('max-height');\n });\n return maxHeight;\n}\n\n/**\n * The `max-height` an unfinished animation or transition on `el` is headed\n * to, or `undefined` when none drives it. While one runs (or is paused), the\n * computed value is its current frame — part-way through an app's expand\n * transition, or a `@keyframes` value — not the cap the element settles at,\n * and lifting the published room could retarget or cancel the app's\n * transition. So the cap is read from the effect instead: its last\n * `max-height` keyframe, or its first while the current iteration plays in\n * reverse. The topmost such effect in composite order wins; a finished one\n * that still fills already shows its final value in computed style.\n */\nfunction animatedMaxHeight(el: HTMLElement): string | undefined {\n // An engine (or DOM shim) without Web Animations has nothing animating.\n const animations = el.getAnimations?.() ?? [];\n for (let index = animations.length - 1; index >= 0; index--) {\n const animation = animations[index]!;\n const effect = animation.effect as KeyframeEffect | null;\n const frames = (effect?.getKeyframes() ?? []).filter(\n (frame) => frame['maxHeight'] != null,\n );\n if (!frames.length) continue;\n if (animation.playState === 'finished') return undefined;\n const { direction, currentIteration } = effect!.getComputedTiming();\n const odd = (currentIteration ?? 0) % 2 === 1;\n const reversed =\n (direction === 'reverse' ||\n (direction === 'alternate' && odd) ||\n (direction === 'alternate-reverse' && !odd)) !==\n animation.playbackRate < 0;\n return String(frames[reversed ? 0 : frames.length - 1]!['maxHeight']);\n }\n return undefined;\n}\n\n/**\n * Resolve a computed but still relative height (`60%`, `calc(50% - 1rem)`,\n * `min(300px, 60%)`) to CSS px against `el`'s fixed-position containing block\n * — the viewport, or a transformed ancestor: a fixed probe beside `el` takes it\n * as its `height`, and the probe's used height is read back. `el` itself is\n * never touched, so its scroll position and layout are unaffected. A height\n * the probe cannot take, or a detached `el`, is no cap.\n */\nfunction resolveHeight(el: HTMLElement, height: string): number {\n return withFixedProbe(\n el,\n 'width:0;box-sizing:content-box;min-height:0;max-height:none;visibility:hidden',\n (probe) => {\n probe.style.height = height;\n if (!probe.style.height) return Infinity;\n return Number.parseFloat(window.getComputedStyle(probe).height);\n },\n Infinity,\n );\n}\n\n/**\n * Run `measure` on a fixed-position probe at `left: 0; top: 0`, inserted just\n * before `el` so it shares `el`'s containing block, then remove it. The probe\n * is inserted and removed synchronously, so it is never painted. A detached\n * `el` has no containing block to share, so `detached` is returned instead.\n */\nfunction withFixedProbe<T>(\n el: HTMLElement,\n css: string,\n measure: (probe: HTMLElement) => T,\n detached: T,\n): T {\n const parent = el.parentNode;\n if (!parent) return detached;\n const probe = el.ownerDocument.createElement('div');\n probe.style.cssText = `position:fixed;left:0;top:0;display:block;margin:0;padding:0;border:0;${css}`;\n parent.insertBefore(probe, el);\n const result = measure(probe);\n probe.remove();\n return result;\n}\n\n/**\n * Where `left: 0; top: 0` actually lands for a fixed-position child of `el`'s\n * parent, plus that containing block's scale — `[x, y, scaleX, scaleY]`, which\n * is `[0, 0, 1, 1]` when the viewport is the containing block. Measured with a\n * 1px probe rather than `el` itself so `el`'s own transform or running entrance\n * animation cannot skew it. Translation and scale are compensated; a rotated\n * or skewed containing block is not.\n */\nfunction fixedOrigin(el: HTMLElement): [number, number, number, number] {\n return withFixedProbe(\n el,\n 'width:1px;height:1px',\n (probe): [number, number, number, number] => {\n const rect = probe.getBoundingClientRect();\n // A zero size (a containing block mid `scale(0)` animation, or a DOM\n // without layout) would divide by zero; treat it as unscaled.\n return [rect.left, rect.top, rect.width || 1, rect.height || 1];\n },\n [0, 0, 1, 1],\n );\n}\n\n/**\n * The viewport y of the bottom edge of `el`'s fixed-position containing block\n * (where `bottom: 0` lands), measured with a 1px probe pinned there. A\n * detached `el`, or a containing block with no layout (a DOM without layout,\n * a `scale(0)` animation), falls back to the viewport height below the origin\n * {@link fixedOrigin} measured, at its scale.\n */\nfunction fixedBottom(el: HTMLElement, originY: number, scaleY: number): number {\n const fallback = originY + window.innerHeight * scaleY;\n return withFixedProbe(\n el,\n 'width:1px;height:1px;top:auto;bottom:0',\n (probe) => {\n const rect = probe.getBoundingClientRect();\n return rect.height ? rect.bottom : fallback;\n },\n fallback,\n );\n}\n\n/**\n * Keep `el` positioned against `anchor` (via {@link positionAnchored}) as the page\n * scrolls or resizes. Positions once immediately, then re-runs on `scroll`\n * (capture phase — catches scrolls in any inner container, not just `window`) and\n * `resize`. Returns a disposer that removes the listeners.\n */\nexport function autoReposition(\n el: HTMLElement,\n anchor: Element,\n options: AnchorPositionOptions = {},\n): () => void {\n const reposition = (): void => positionAnchored(el, anchor, options);\n reposition();\n window.addEventListener('scroll', reposition, true);\n window.addEventListener('resize', reposition);\n return () => {\n window.removeEventListener('scroll', reposition, true);\n window.removeEventListener('resize', reposition);\n };\n}\n","/** Anchored popover lifecycle built on the shared overlay transaction. */\nimport { attach } from './attach.js';\nimport {\n type DismissTrigger,\n overlay,\n type OverlayContent,\n type OverlayHandle,\n overlayHost,\n} from './overlay-core.js';\nimport { autoReposition, type PopoverPlacement } from './overlay-position.js';\n\n/** Options for {@link popover}. */\nexport interface PopoverOptions {\n /**\n * Where to append the popover wrapper. Default: when `anchor` sits inside an\n * open modal `<dialog>`, that dialog's `[data-kerf-overlay-host]` element (a\n * dialog kerf opened gets one automatically), so the popover is part of the\n * modal subtree and stays interactive; otherwise `document.body`. A modal\n * `<dialog>` in a shadow root counts when `anchor` is slotted into it (Web\n * Awesome's `wa-dialog` / `wa-drawer`): the slot is created on demand in the\n * shadow host's light DOM. A popover inside a modal closes when that dialog\n * closes.\n */\n container?: Element;\n /** Class on the wrapper. Default `'kerf-popover'`. */\n className?: string;\n /** Preferred side of the anchor. Flips to the other side if it would overflow the viewport. Default `'bottom'`. */\n placement?: PopoverPlacement;\n /** Horizontal edge to line up with the anchor: `'start'` (left edges) or `'end'` (right edges). Default `'start'`. */\n align?: 'start' | 'end';\n /** Gap in px between the anchor and the popover. Default `4`. */\n gap?: number;\n /** Gutter in px kept from the viewport edges. See `AnchorPositionOptions.viewportMargin`. Default `0`. */\n viewportMargin?: number;\n /**\n * Fit a scrolling popover to the room beside the anchor: take the roomier\n * side when neither fits and publish that room as\n * `--kerf-anchor-available-height`. See `AnchorPositionOptions.fitHeight`.\n * Default `false`.\n */\n fitHeight?: boolean;\n /**\n * Which user actions dismiss the popover. Default `['outside']` (a click\n * outside the popover, the anchor exempt). Pass `false` to close only via `close()`.\n */\n dismiss?: DismissTrigger | DismissTrigger[] | false;\n /** Focus behavior on open. Default `false` (non-modal — leave focus alone). */\n initialFocus?: string | boolean;\n /** Extra elements (besides the anchor) whose clicks do NOT count as outside. */\n outsideIgnore?: Element | readonly Element[];\n /** Called on any user-initiated dismissal. */\n onDismiss?: () => void;\n /**\n * Host the popover in the browser top layer (the Popover API — `[popover]` +\n * `showPopover()`) where supported, so it stacks above any `z-index` without a\n * z-index war. Falls back to today's plain `<div>` where unsupported. kerf keeps\n * owning positioning + its own dismiss wiring; the popover is `popover=\"manual\"`.\n * See {@link OverlayOptions.native}. Default `false`.\n */\n native?: boolean;\n}\n\n/**\n * Anchored, non-modal overlay: positions `content` relative to `anchor` (below by\n * default, flipping above if it would overflow, and clamped horizontally to the\n * viewport) and repositions on scroll / resize while open. A thin wrapper over\n * {@link overlay} with non-modal defaults — `trap: false`, `dismiss: ['outside']`,\n * and the anchor added to `outsideIgnore` so the trigger click doesn't self-close.\n * Returns the same {@link OverlayHandle}; `close()` also drops the reposition\n * listeners. `position: fixed` is set inline (you style everything else).\n */\nexport function popover(\n anchor: Element,\n content: OverlayContent,\n options: PopoverOptions = {},\n): OverlayHandle {\n const {\n container,\n className = 'kerf-popover',\n placement = 'bottom',\n align = 'start',\n gap = 4,\n viewportMargin,\n fitHeight,\n dismiss = ['outside'],\n initialFocus = false,\n outsideIgnore,\n onDismiss,\n native = false,\n } = options;\n\n const extraIgnore =\n outsideIgnore === undefined\n ? []\n : Array.isArray(outsideIgnore)\n ? [...outsideIgnore]\n : [outsideIgnore];\n\n const handle = overlay(content, {\n container: container ?? overlayHost(anchor),\n className,\n dismiss,\n trap: false,\n initialFocus,\n onDismiss,\n outsideIgnore: [anchor, ...extraIgnore],\n native,\n });\n\n // Position + keep it glued while open; drop the listeners on close. A throw\n // while positioning (e.g. an anchor whose geometry read fails) is still part\n // of construction: close the just-opened overlay before rethrowing.\n let stopReposition: () => void;\n try {\n stopReposition = autoReposition(handle.el, anchor, {\n placement,\n align,\n gap,\n viewportMargin,\n fitHeight,\n });\n } catch (error) {\n handle.close();\n throw error;\n }\n void handle.result.then(stopReposition);\n\n // KF-BAVCEV: never outlive the anchor. When it leaves the document (e.g. the\n // modal holding it closes) close the popover as cleanup — not a user\n // dismissal, so no `onDismiss`. A move within the document keeps it open.\n void handle.result.then(attach(anchor, () => handle.close));\n\n return handle;\n}\n","/**\n * `toast()` for `kerfjs/overlay` — a non-modal, auto-dismissing notification that\n * stacks in a shared body-level region. Split out of `overlay.ts` (KF-513) since\n * it's a distinct transient-UI concern from the modal dialogs; re-exported from\n * `overlay.ts` so the public `kerfjs/overlay` surface is unchanged. Structural\n * only — kerf ships no CSS; you style the region / toast / animations.\n */\nimport { type SafeHtml } from './jsx-runtime.js';\nimport { mount, type MountResult } from './mount.js';\nimport { escapeHtml } from './utils/escapeHtml.js';\n\n/** Content for a {@link toast}: an escaped text string, trusted `SafeHtml`, or a render function. */\nexport type ToastContent = string | SafeHtml | (() => MountResult);\n\n/** Accent variant for a {@link toast} — mapped to a `${className}--${variant}` class. */\nexport type ToastVariant = 'info' | 'success' | 'warning';\n\n/** Options for {@link toast}. */\nexport interface ToastOptions {\n /** Where toasts stack. Default: a lazily-created `<div class=\"kerf-toasts\">` on `document.body`. */\n container?: Element;\n /** Class on the toast element. Default `'kerf-toast'`. */\n className?: string;\n /** Auto-dismiss after this many ms. `0` keeps it until dismissed by hand. Default `4000`. */\n duration?: number;\n /** ARIA role. Default `'status'`. */\n role?: string;\n /**\n * `'stack'` (default) shows toasts stacked in the region; `'replace'` dismisses\n * the region's current toast(s) first (collapse-to-latest for a rapid sequence).\n */\n mode?: 'stack' | 'replace';\n /**\n * How `mode: 'replace'` drops the prior toast(s): `'fade'` (default) runs their\n * full exit transition (nice for a STACKING region), or `'instant'` removes them\n * synchronously with no exit — what a single, exactly-centered toast slot wants,\n * so the outgoing and incoming messages never cross-fade in the same spot.\n */\n collapse?: 'fade' | 'instant';\n /** Accent variant — adds a `${className}--${variant}` class (kerf ships no CSS; you style it). */\n variant?: ToastVariant;\n /** Class added on the next animation frame after mount, so a CSS **entrance** transition can run. */\n enterClass?: string;\n /**\n * Class added when dismissing, so CSS owns the **exit**. On dismiss the\n * `enterClass` (if any) is also REMOVED, so `exitClass` doesn't have to\n * out-specify it — and a symmetric single-class fade (entrance = add\n * `enterClass`, exit = remove it) works by setting only `enterClass` +\n * `exitDuration`. The node is removed `exitDuration` ms later.\n */\n exitClass?: string;\n /** ms to wait before removing the node on dismiss — applies when `exitClass` is set OR when it's > 0 (to let a removed `enterClass` transition out). Default `0`. */\n exitDuration?: number;\n}\n\n/** Handle returned by {@link toast}. */\nexport interface ToastHandle {\n /** The toast element — inspect it, or run your own entrance/exit transitions. */\n el: HTMLElement;\n /**\n * Dismiss it early. Default runs the `exitClass` transition (removed after\n * `exitDuration`); pass `{ instant: true }` to remove it **synchronously** with\n * no exit — for an action button that immediately shows a replacement toast in a\n * single centered slot (no cross-fade). Idempotent.\n */\n dismiss(options?: { instant?: boolean }): void;\n}\n\n/** The region's active toasts live on the region element (in the DOM), not in module state. */\nconst TOAST_SET = Symbol('kerf.toasts');\n/** A dismiss function with an `.removeNow()` for instant (no-exit) teardown. */\ntype Dismisser = (() => void) & { removeNow: () => void };\ntype ToastCarrier = Element & { [TOAST_SET]?: Set<Dismisser> };\n\n/** The singleton toast region lives in the DOM (queried, not held in a module variable). */\nfunction toastRegion(container?: Element): Element {\n if (container !== undefined) return container;\n const existing = document.querySelector('.kerf-toasts');\n if (existing !== null) return existing;\n const region = document.createElement('div');\n region.className = 'kerf-toasts';\n region.setAttribute('aria-live', 'polite');\n document.body.appendChild(region);\n return region;\n}\n\n/**\n * Show a non-modal, auto-dismissing notification. Stacks in a shared body-level\n * region (or your `container`). Returns a {@link ToastHandle} (`{ el, dismiss }`)\n * so you can run entrance/exit transitions, wire an action button, or inspect the\n * node. `mode: 'replace'` collapses a rapid sequence to the latest; `variant`\n * adds an accent class; `enterClass`/`exitClass` let CSS own the animation.\n */\nexport function toast(\n content: ToastContent,\n options: ToastOptions = {},\n): ToastHandle {\n const {\n container,\n className = 'kerf-toast',\n duration = 4000,\n role = 'status',\n mode = 'stack',\n collapse = 'fade',\n variant,\n enterClass,\n exitClass,\n exitDuration = 0,\n } = options;\n\n const region = toastRegion(container) as ToastCarrier;\n const active = (region[TOAST_SET] ??= new Set<Dismisser>());\n // collapse-to-latest: 'instant' removes priors synchronously (no cross-fade in a\n // centered single slot); 'fade' runs their exit transition (nice for a stack).\n if (mode === 'replace') {\n for (const d of [...active]) {\n if (collapse === 'instant') d.removeNow();\n else d();\n }\n }\n\n const el = document.createElement('div');\n el.className = className;\n if (variant !== undefined) el.classList.add(`${className}--${variant}`);\n el.setAttribute('role', role);\n region.appendChild(el);\n\n const render =\n typeof content === 'function'\n ? content\n : () => (typeof content === 'string' ? escapeHtml(content) : content);\n const disposeMount = mount(el, render);\n const state: {\n dismissed: boolean;\n removed: boolean;\n timer: ReturnType<typeof setTimeout> | undefined;\n exitTimer: ReturnType<typeof setTimeout> | undefined;\n } = {\n dismissed: false,\n removed: false,\n timer: undefined,\n exitTimer: undefined,\n };\n\n if (enterClass !== undefined) {\n globalThis.requestAnimationFrame(() => {\n if (!state.dismissed) el.classList.add(enterClass);\n });\n }\n\n const remove = (): void => {\n if (state.removed) return;\n state.removed = true;\n state.dismissed = true; // once removed, a later dismiss() is a clean no-op\n if (state.exitTimer !== undefined) clearTimeout(state.exitTimer);\n disposeMount();\n el.remove();\n active.delete(dismiss as Dismisser);\n };\n\n // Force synchronous teardown NOW, with no exit transition — even if a fade is\n // already in flight (so `mode:'replace'` collapse can clean up a mid-exit toast,\n // and `dismiss({ instant: true })` drops it immediately).\n const removeNow = (): void => {\n if (state.timer !== undefined) clearTimeout(state.timer);\n remove();\n };\n\n const fadeOut = (): void => {\n if (state.dismissed) return;\n state.dismissed = true;\n if (state.timer !== undefined) clearTimeout(state.timer);\n // Reverse the entrance (drop enterClass so exitClass needn't out-specify it),\n // add exitClass, and delay removal for the transition — including the\n // single-class idiom (enterClass removed + exitDuration, no exitClass).\n if (enterClass !== undefined) el.classList.remove(enterClass);\n if (exitClass !== undefined) el.classList.add(exitClass);\n if (exitClass !== undefined || exitDuration > 0) {\n state.exitTimer = setTimeout(remove, exitDuration);\n } else {\n remove();\n }\n };\n\n function dismiss(options?: { instant?: boolean }): void {\n if (options?.instant === true) removeNow();\n else fadeOut();\n }\n (dismiss as Dismisser).removeNow = removeNow;\n\n active.add(dismiss as Dismisser);\n if (duration > 0) state.timer = setTimeout(fadeOut, duration);\n return { el, dismiss };\n}\n","/** Hover and focus tooltip lifecycle built on the shared overlay transaction. */\nimport { attach } from './attach.js';\nimport { jsx, type SafeHtml } from './jsx-runtime.js';\nimport type { MountResult } from './mount.js';\nimport {\n overlay,\n type OverlayContent,\n type OverlayHandle,\n overlayHost,\n} from './overlay-core.js';\nimport {\n type AnchorPositionOptions,\n autoReposition,\n} from './overlay-position.js';\n\n/** Content for a {@link tooltip}: text (auto-escaped), `SafeHtml`, or a render function. */\nexport type TooltipContent = string | SafeHtml | (() => MountResult);\n\n/** Options for {@link tooltip}. */\nexport interface TooltipOptions extends AnchorPositionOptions {\n /** Where to append the tooltip wrapper. Default: the anchor's modal-dialog host slot (see {@link PopoverOptions.container}), else `document.body`. */\n container?: Element;\n /** Class on the wrapper. Default `'kerf-tooltip'`. */\n className?: string;\n /** Delay in ms before showing after hover/focus enters. Default `400`. */\n delay?: number;\n /** Delay in ms before hiding after hover/focus leaves. Default `100`. */\n hideDelay?: number;\n /** ARIA role on the wrapper. Default `'tooltip'`. */\n role?: string;\n /** Host the tooltip in the browser top layer (the Popover API) where supported. See {@link OverlayOptions.native}. Default `false`. */\n native?: boolean;\n}\n\n/**\n * A hover/focus-triggered, non-modal, auto-hiding tooltip anchored to `anchor`.\n * Shows after `delay` on `pointerenter`/`focus`, hides after `hideDelay` on\n * `pointerleave`/`blur`, and positions itself with {@link autoReposition} (above\n * the anchor by default). Unlike {@link popover} there is no click-dismiss model —\n * it follows the pointer/focus. Returns a disposer that removes the anchor\n * listeners and hides any shown tooltip. Structural only (kerf ships no CSS).\n */\nexport function tooltip(\n anchor: Element,\n content: TooltipContent,\n options: TooltipOptions = {},\n): () => void {\n const {\n container,\n className = 'kerf-tooltip',\n delay = 400,\n hideDelay = 100,\n role = 'tooltip',\n placement = 'top',\n align = 'start',\n gap = 4,\n viewportMargin,\n fitHeight,\n native = false,\n } = options;\n\n const body: OverlayContent =\n typeof content === 'function'\n ? content\n : typeof content === 'string'\n ? jsx('span', { class: `${className}__text`, children: content })\n : content;\n\n const timers: {\n show?: ReturnType<typeof setTimeout>;\n hide?: ReturnType<typeof setTimeout>;\n } = {};\n let current:\n | { handle: OverlayHandle; stop: () => void; unwatch: () => void }\n | undefined;\n let presence = 0;\n\n // Runs from the `delay` timer, so there is no caller to throw to. A failed\n // show (a throwing render fn, `native` `showPopover()`, or positioning) is\n // rolled back before `current` is set — nothing stays on screen and the\n // next pointerenter / focus schedules a fresh attempt — and the original\n // error then escapes the timer callback for the host to report as uncaught\n // (a window `error` event in browsers), the same way kerf's other deferred\n // callbacks (debounce / throttle timers, attach teardown) surface errors.\n function show(): void {\n // The anchor left the document while the show was pending (KF-BAVCEV).\n if (!anchor.isConnected) return;\n const handle = overlay(body, {\n container: container ?? overlayHost(anchor),\n className,\n dismiss: false,\n trap: false,\n initialFocus: false,\n native,\n });\n handle.el.setAttribute('role', role);\n let stop: () => void;\n try {\n stop = autoReposition(handle.el, anchor, {\n placement,\n align,\n gap,\n viewportMargin,\n fitHeight,\n });\n } catch (error) {\n // Not yet tracked in `current`, so `hide()` could never reach it.\n handle.close();\n throw error;\n }\n // KF-BAVCEV: an anchor removed from the document (e.g. with the modal that\n // held it) fires no pointerleave / blur, so hide when it leaves and forget\n // its presence; a move within the document keeps the tooltip shown.\n const unwatch = attach(anchor, () => () => {\n presence = 0;\n if (timers.hide !== undefined) clearTimeout(timers.hide);\n hide();\n });\n current = { handle, stop, unwatch };\n // A surface that closed itself (its host slot left the document with the\n // dialog) must not stay `current`, or the next hover would never show.\n void handle.result.then(() => {\n if (current?.handle === handle) hide();\n });\n }\n\n function hide(): void {\n const shown = current;\n if (shown === undefined) return;\n // Cleared first: `unwatch()` runs the anchor teardown, which re-enters here.\n current = undefined;\n shown.unwatch();\n shown.stop();\n shown.handle.close();\n }\n\n const onEnter = (event: Event): void => {\n presence |= event.type === 'focus' ? 2 : 1;\n if (timers.hide !== undefined) clearTimeout(timers.hide);\n if (current !== undefined) return;\n if (timers.show !== undefined) clearTimeout(timers.show); // debounce: one pending show at a time\n timers.show = setTimeout(show, delay);\n };\n const onLeave = (event: Event): void => {\n presence &= event.type === 'blur' ? ~2 : ~1;\n if (presence) return;\n if (timers.show !== undefined) clearTimeout(timers.show);\n if (current === undefined) return;\n timers.hide = setTimeout(hide, hideDelay);\n };\n\n anchor.addEventListener('pointerenter', onEnter);\n anchor.addEventListener('pointerleave', onLeave);\n anchor.addEventListener('focus', onEnter);\n anchor.addEventListener('blur', onLeave);\n\n return () => {\n anchor.removeEventListener('pointerenter', onEnter);\n anchor.removeEventListener('pointerleave', onLeave);\n anchor.removeEventListener('focus', onEnter);\n anchor.removeEventListener('blur', onLeave);\n if (timers.show !== undefined) clearTimeout(timers.show);\n if (timers.hide !== undefined) clearTimeout(timers.hide);\n hide();\n };\n}\n"]}
|
package/package.json
CHANGED