@jungherz-de/notionkit-elements 1.18.0 → 1.19.0

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 CHANGED
@@ -4,6 +4,20 @@ All notable changes to NotionKit Elements are documented here. The format follow
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.19.0] – 2026-09-25
8
+
9
+ Built against NotionKit CSS 1.19.1 (peer `>= 1.19.1`). Auxdesk's wish 24 and what SupaGantt's mock-ups found – 14, 15 and 16 – together with a toast that never hid.
10
+
11
+ ### Added
12
+ - **`<nk-toast>` with an action.** `toast.show('Moved to trash', { action: { label: 'Undo', value: 'undo' } })`, or a `<button slot="action">`, gives the toast a button and an ×, as Notion's "Moved to trash · Undo". Such a toast stays until one of them is used, unless `show()` names a duration, and waits while the pointer or the focus is on it. The action fires `nk-action { action, value, label }` and, unless cancelled, closes the toast – a handler that shows the next message, "Restored", keeps it open; Escape closes it while the focus is inside, and the focus goes back to where it came from (Auxdesk's wish 24, which showed "A new version of Auxdesk · Reload" as a banner under the topbar). The demo's trash toast offers Undo.
13
+
14
+ ### Fixed
15
+ - **A toast without a `duration` attribute hides by itself again.** `show()` read the missing attribute as 0 – "stay" – so every such toast stayed on screen since 1.0.0, the demo's too; the default of 2200ms applies again, and a set attribute or `show()`'s `duration` still win.
16
+ - **Escape closes the layer that opened last.** A floating date picker or menu opened from a `<nk-dialog>` and the dialog both listened for Escape on the document in the capture phase, where `stopPropagation()` does not part them, so one Escape closed both. The overlays – modal, sheet, dialog, side peek, palette, popover, floating menu and date picker – now join one stack while they are open, and Escape goes to its top only; a second one closes the dialog (SupaGantt's finding 15).
17
+ - **The focus goes back into a shadow root.** `<nk-menu>` checked `document.activeElement`, `<nk-modal>` and `<nk-cmdk>` remembered it – both stop at the host of a view that holds the trigger, so the focus ended on `<body>`. They look behind shadow hosts now, like the dialog and the sheet (SupaGantt's finding 14).
18
+ - **A long floating menu opens at its top again** – or with its first checked item in view. It kept the place it had been scrolled to, its heading out of sight (SupaGantt's finding 16).
19
+ - **A toast whose message is empty takes no place**, so static text in its slot no longer stands a second gap away from the icon.
20
+
7
21
  ## [1.18.0] – 2026-09-25
8
22
 
9
23
  Built against NotionKit CSS 1.18.0 (peer `>= 1.18.0`). What LearnHub and Auxdesk found after their move to 1.17.0: LearnHub's finding 17 and Auxdesk's 26, 27 and 28.
@@ -732,6 +746,7 @@ built and tested against NotionKit CSS 1.1.1 (peer range `>= 1.0.0`).
732
746
  import, never in the core bundle – shadow-less, adding `nk-block-host` to
733
747
  itself so the foundation's editor adapter rules apply.
734
748
 
749
+ [1.19.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.19.0
735
750
  [1.18.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.18.0
736
751
  [1.17.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.17.0
737
752
  [1.16.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.16.0
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  <h1 align="center">🧩 NotionKit Elements</h1>
2
2
 
3
3
  <p align="center">
4
- <a href="https://www.npmjs.com/package/@jungherz-de/notionkit-elements"><img src="https://img.shields.io/badge/version-1.18.0-2383e2?style=flat-square" alt="Version"></a>
4
+ <a href="https://www.npmjs.com/package/@jungherz-de/notionkit-elements"><img src="https://img.shields.io/badge/version-1.19.0-2383e2?style=flat-square" alt="Version"></a>
5
5
  <a href="#"><img src="https://img.shields.io/badge/vanilla_JS-no_dependencies-448361?style=flat-square" alt="Vanilla JS"></a>
6
6
  <a href="#"><img src="https://img.shields.io/badge/elements-88-529cca?style=flat-square" alt="88 Elements"></a>
7
7
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-9065b0?style=flat-square" alt="MIT License"></a>
8
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.18.0-lightgrey?style=flat-square" alt="Changelog"></a>
8
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.19.0-lightgrey?style=flat-square" alt="Changelog"></a>
9
9
  <a href="https://www.npmjs.com/package/@jungherz-de/notionkit-elements"><img src="https://img.shields.io/badge/npm-%40jungherz--de%2Fnotionkit--elements-cb3837?style=flat-square&logo=npm" alt="npm"></a>
10
10
  </p>
11
11
 
@@ -59,7 +59,7 @@ It is the **app layer** of the NotionKit family – three layers, one design lan
59
59
  | 🧩 **88 Elements** | Forms with copy field, image picker and date picker, content blocks with bookmarks, app shell & tree with the ☰ drawer and the collapsing sidebar, page shell with properties, panels, avatars and steps, overlays (modal, sheet, dialog, resizable side peek, tooltip, ⌘K palette, menus that float in and become sheets on a phone, popover, emoji picker, toast), database table, board, list, calendar & gallery with toolbar and filter pills, comments, AI thread |
60
60
  | 🎛️ **Form participation** | Input, textarea, select, switch, check, radio, slider, segmented and model card work natively with `<form>` via `ElementInternals`; radios group across shadow roots |
61
61
  | 🌗 **Theme sync** | One observer mirrors `data-theme` on `<html>` into every element |
62
- | 🪶 **Lightweight** | 288 KB minified / 58 KB gzipped (IIFE, foundation sheet included), no external dependencies |
62
+ | 🪶 **Lightweight** | 292 KB minified / 59 KB gzipped (IIFE, foundation sheet included), no external dependencies |
63
63
  | 📦 **Five bundle shapes** | IIFE, minified IIFE, ESM, minified ESM with a source map (`esm.min.js`), and per-component ESM entries on a stable `base.js`, which import NotionKit's sheet instead of carrying a copy |
64
64
  | 🤖 **AI-ready** | `SKILL.md` with copy-paste markup, six app skeletons and the rules – an agent given only that file built a working app in the release test |
65
65
 
@@ -71,8 +71,8 @@ It is the **app layer** of the NotionKit family – three layers, one design lan
71
71
  <!DOCTYPE html>
72
72
  <html lang="en" data-theme="light">
73
73
  <head>
74
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
75
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
74
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
75
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
76
76
  </head>
77
77
  <body class="nk-body">
78
78
  <nk-field label="Display name" desc="Shown next to your comments">
@@ -104,7 +104,7 @@ Without a build step, an import map can point at the minified module instead: `"
104
104
 
105
105
  Do not mix the full bundle with the per-component files: each brings its own `NkElement`. With the bundle, take `componentsSheet` and `NkElement` from the bundle (`NotionKitElements.componentsSheet` from the `<script>` build).
106
106
 
107
- Two prerequisites, always: `notionkit.css` on the document (the peer dependency, `>= 1.18.0`; 1.18.0 brings a bar and its label in one row, stacked properties that keep to their card on a phone and icons in the copy field, 1.17.0 brings a step's label and a gallery card as links, 1.16.0 brings `--nk-block-space` and `flush` for blocks in a column, keeps a wide progress bar and a stretched button's label in shape and lays floating menus above dialogs, 1.15.0 brings table text with a tone and a second line, 1.14.0 brings the gallery view and a banner action that stands at the right edge again, 1.13.0 keeps fields inside narrow panels, leaves a page without icon its padding under the cover and makes cover heights tokens, 1.12.0 brings a tag or button beside a panel's title, lists and grids without outer margin, text values, steps with states in any order and in a row, scrolling tabs, a cap for full pages and buttons per table row, 1.11.0 keeps a floating menu inside an open modal or dialog in place and inside the window and brings the banner's action as a button, fields that share a row and tooltips of several lines, 1.10.1 lets a disabled button show its tooltip, 1.10.0 brings the collapsing sidebar, the resizable side peek, the dialog and the tooltip, 1.9.0 the date picker and the calendar view, 1.8.0 the side peek, the bookmark, the copy field and pictures on the profile row, 1.7.0 the sheet, menus that float in and become sheets on a phone, the sidebar drawer, the database toolbar with filter pills and the steps, 1.7.1 minified files that carry all of it, 1.6.0 page properties, page options, list view, panels, prose and the avatar, 1.5.1 the safe areas, 1.5.0 the Notion-2025 tokens, the nine tag colours and the filled inputs; from 1.5.0 on, Elements and the foundation share one version number – the elements are built and tested against the same release) and `class="nk-body"` on `<body>`. The elements ship no visual CSS of their own – every rule comes from the foundation. The bundle injects the design tokens once as `@layer notionkit-defaults`, so any plain `:root { --nk-accent: … }` of yours re-brands every element in both themes.
107
+ Two prerequisites, always: `notionkit.css` on the document (the peer dependency, `>= 1.19.1`; 1.19.1 keeps a toast with an × at its height, 1.19.0 brings the toast with an action, 1.18.0 brings a bar and its label in one row, stacked properties that keep to their card on a phone and icons in the copy field, 1.17.0 brings a step's label and a gallery card as links, 1.16.0 brings `--nk-block-space` and `flush` for blocks in a column, keeps a wide progress bar and a stretched button's label in shape and lays floating menus above dialogs, 1.15.0 brings table text with a tone and a second line, 1.14.0 brings the gallery view and a banner action that stands at the right edge again, 1.13.0 keeps fields inside narrow panels, leaves a page without icon its padding under the cover and makes cover heights tokens, 1.12.0 brings a tag or button beside a panel's title, lists and grids without outer margin, text values, steps with states in any order and in a row, scrolling tabs, a cap for full pages and buttons per table row, 1.11.0 keeps a floating menu inside an open modal or dialog in place and inside the window and brings the banner's action as a button, fields that share a row and tooltips of several lines, 1.10.1 lets a disabled button show its tooltip, 1.10.0 brings the collapsing sidebar, the resizable side peek, the dialog and the tooltip, 1.9.0 the date picker and the calendar view, 1.8.0 the side peek, the bookmark, the copy field and pictures on the profile row, 1.7.0 the sheet, menus that float in and become sheets on a phone, the sidebar drawer, the database toolbar with filter pills and the steps, 1.7.1 minified files that carry all of it, 1.6.0 page properties, page options, list view, panels, prose and the avatar, 1.5.1 the safe areas, 1.5.0 the Notion-2025 tokens, the nine tag colours and the filled inputs; from 1.5.0 on, Elements and the foundation share one version number – the elements are built and tested against the same release) and `class="nk-body"` on `<body>`. The elements ship no visual CSS of their own – every rule comes from the foundation. The bundle injects the design tokens once as `@layer notionkit-defaults`, so any plain `:root { --nk-accent: … }` of yours re-brands every element in both themes.
108
108
 
109
109
  ## Elements
110
110
 
@@ -184,7 +184,7 @@ A step of `<nk-steps>` with `href` and a card of `<nk-gallery-view>` whose row h
184
184
  - Form controls are form-associated custom elements: `FormData`, `reset`, `required` and `<fieldset disabled>` just work. `<nk-radio>`s with the same `name` form a real group across shadow roots, with one tab stop and arrow keys.
185
185
  - Elements that copy light-DOM children (`nk-select` options, breadcrumb crumbs) watch them; `element.refresh()` is the escape hatch. Listeners are re-armed on every connect, so a moved element keeps working.
186
186
  - `::slotted()` matches only the assigned node: pass icons directly (`<span slot="icon">📁</span>`), never wrapped. For the same reason there is no `<nk-prose>`: rendered Markdown goes into a light-DOM `<div class="nk-prose">`, which reads the same rules as the editor adapter.
187
- - Overlays (`nk-modal`, `nk-sheet`, `nk-peek`, `nk-cmdk`, `nk-toast`, a floating `nk-menu`) go directly under `<body>`. The modal, the sheet and the palette lock scroll and make the rest of the page `inert` – the side peek only as a sheet on a phone, beside the page it leaves it usable; all of them return focus to the control that had it.
187
+ - Overlays (`nk-modal`, `nk-sheet`, `nk-peek`, `nk-cmdk`, `nk-toast`, a floating `nk-menu`) go directly under `<body>`. The modal, the sheet and the palette lock scroll and make the rest of the page `inert` – the side peek only as a sheet on a phone, beside the page it leaves it usable; all of them return focus to the control that had it, inside a shadow root too. Escape closes the layer that opened last: a date picker or menu opened from a dialog first, then the dialog.
188
188
  - The editor stays an adapter: `nk-block-host` is the shell, `docs-editor.js` the TipTap recipe (used in the demo app). `<nk-editor>` follows in v1.1 as an optional import, never in the core bundle.
189
189
 
190
190
  ## Development
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: notionkit-elements
3
- description: NotionKit Elements is a vanilla-JS Web Components library (v1.18.0) wrapping NotionKit CSS v1.18.0 – the calm, document-centric design system in the Notion idiom. 88 custom elements with the `nk-` prefix, Shadow DOM, automatic light/dark sync via data-theme on <html>, and form-associated controls. Use this reference whenever generating HTML that uses <nk-*> tags to get attributes, slots, events and composition right.
3
+ description: NotionKit Elements is a vanilla-JS Web Components library (v1.19.0) wrapping NotionKit CSS v1.19.1 – the calm, document-centric design system in the Notion idiom. 88 custom elements with the `nk-` prefix, Shadow DOM, automatic light/dark sync via data-theme on <html>, and form-associated controls. Use this reference whenever generating HTML that uses <nk-*> tags to get attributes, slots, events and composition right.
4
4
  ---
5
5
 
6
6
  # NotionKit Elements – AI Component Reference
@@ -21,8 +21,8 @@ description: NotionKit Elements is a vanilla-JS Web Components library (v1.18.0)
21
21
  <head>
22
22
  <meta charset="UTF-8">
23
23
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
24
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
25
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
24
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
25
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
26
26
  </head>
27
27
  <body class="nk-body">
28
28
  <nk-btn variant="primary">Save</nk-btn>
@@ -2531,29 +2531,29 @@ Search field, 8-column grid, category strip. Ships with a built-in set (names fo
2531
2531
 
2532
2532
  ### 3.75 `<nk-toast>` – Toast
2533
2533
 
2534
- One inverted pill at the bottom centre, above every overlay; over a tab bar at the bottom of the screen it rises 12px above the bar. `toast.show("Saved")` shows it and hides it after `duration` ms; `open` is the state.
2534
+ One inverted pill at the bottom centre, above every overlay; over a tab bar at the bottom of the screen it rises 12px above the bar. `toast.show("Saved")` shows it and hides it after `duration` ms; `open` is the state. With an action – `show("Moved to trash", { action: { label: "Undo", value: "undo" } })` or a `<button slot="action">` – it is Notion’s toast with a button and an ×: it stays until one of them is used, unless `show()` names a duration, and waits while the pointer or the focus is on it. The action fires `nk-action` and, unless cancelled, closes it; Escape closes it while the focus is inside.
2535
2535
 
2536
2536
  ```html
2537
- <nk-toast open duration="0">Settings saved</nk-toast>
2537
+ <nk-toast open duration="0" icon="🗑️">Moved to trash<button slot="action" value="undo">Undo</button></nk-toast>
2538
2538
  ```
2539
2539
 
2540
2540
  | Attribute | Type | Default | Description |
2541
2541
  |---|---|---|---|
2542
2542
  | `open` | boolean | – | Visible. |
2543
- | `duration` | ms | `2200` | Auto-hide delay (0 = stay). |
2543
+ | `duration` | ms | `2200` | Auto-hide delay (0 = stay); a toast with an action stays unless show() names one. |
2544
2544
  | `icon` | string | `✓` | Leading glyph. |
2545
2545
 
2546
- **Slots:** `(default)` – Static content (when `show()` gets no message).
2546
+ **Slots:** `(default)` – Static content (when `show()` gets no message). · `action` – A button for the action; its `value` goes out with `nk-action`.
2547
2547
 
2548
- **Events:** `nk-toggle` `{ open }` – Shown / hidden.
2548
+ **Events:** `nk-toggle` `{ open }` – Shown / hidden. · `nk-action` `{ action, value, label }` – The action used; cancel it to keep the toast.
2549
2549
 
2550
- **Properties:** `open`, `message` · **Methods:** `show(message?, { duration })`, `close()`
2550
+ **Properties:** `open`, `message` · **Methods:** `show(message?, { duration, action: { label, value } })`, `close()`
2551
2551
 
2552
- **Replaces:** `.nk-toast`, `.show`
2552
+ **Replaces:** `.nk-toast`, `.show`, `.t-action`, `.t-close`
2553
2553
 
2554
2554
  ```html
2555
2555
  <!-- equivalent class markup -->
2556
- <div class="nk-toast show">✓ <span>Settings saved</span></div>
2556
+ <div class="nk-toast show">🗑️ <span>Moved to trash</span><button class="t-action" type="button">Undo</button><button class="t-close" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button></div>
2557
2557
  ```
2558
2558
 
2559
2559
  **Small screens:** Unchanged.
@@ -3150,8 +3150,8 @@ Eight skeletons, one per app shape, mirroring the NotionKit CSS SKILL.md. Copy o
3150
3150
  <html lang="en" data-theme="light">
3151
3151
  <head>
3152
3152
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3153
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3154
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3153
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3154
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3155
3155
  </head>
3156
3156
  <body class="nk-body">
3157
3157
  <nk-app>
@@ -3232,8 +3232,8 @@ Rules of the shell: `nk-sidebar`, `nk-topbar` and `nk-page` are `display: conten
3232
3232
  <html lang="en" data-theme="light">
3233
3233
  <head>
3234
3234
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3235
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3236
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3235
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3236
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3237
3237
  </head>
3238
3238
  <body class="nk-body">
3239
3239
  <nk-app>
@@ -3340,8 +3340,8 @@ Data contract: `columns` describe the properties (`type`: text | select | multi-
3340
3340
  <html lang="en" data-theme="light">
3341
3341
  <head>
3342
3342
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3343
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3344
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3343
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3344
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3345
3345
  </head>
3346
3346
  <body class="nk-body">
3347
3347
  <nk-app>
@@ -3400,8 +3400,8 @@ Data contract: `columns` describe the properties (`type`: text | select | multi-
3400
3400
  <html lang="en" data-theme="light">
3401
3401
  <head>
3402
3402
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3403
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3404
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3403
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3404
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3405
3405
  </head>
3406
3406
  <body class="nk-body">
3407
3407
  <!-- your app -->
@@ -3478,8 +3478,8 @@ The open/close contract is one attribute: `settings.open = true`, `settings.show
3478
3478
  <html lang="en" data-theme="light">
3479
3479
  <head>
3480
3480
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3481
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3482
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3481
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3482
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3483
3483
  </head>
3484
3484
  <body class="nk-body">
3485
3485
  <div class="nk-page" style="padding-top:48px">
@@ -3525,8 +3525,8 @@ The open/close contract is one attribute: `settings.open = true`, `settings.show
3525
3525
  <html lang="en" data-theme="light">
3526
3526
  <head>
3527
3527
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3528
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3529
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3528
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3529
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3530
3530
  </head>
3531
3531
  <body class="nk-body">
3532
3532
  <nk-page narrow icon="📘" cover>
@@ -3570,8 +3570,8 @@ Note `narrow`: the page is the document, so there is no inner scroll wrapper –
3570
3570
  <html lang="en" data-theme="light">
3571
3571
  <head>
3572
3572
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3573
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3574
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3573
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3574
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3575
3575
  </head>
3576
3576
  <body class="nk-body">
3577
3577
  <nk-app>
@@ -3625,8 +3625,8 @@ Note `narrow`: the page is the document, so there is no inner scroll wrapper –
3625
3625
  <html lang="en" data-theme="light">
3626
3626
  <head>
3627
3627
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
3628
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.18.0/notionkit.min.css">
3629
- <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.18.0/dist/notionkit-elements.min.js"></script>
3628
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.19.1/notionkit.min.css">
3629
+ <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.19.0/dist/notionkit-elements.min.js"></script>
3630
3630
  </head>
3631
3631
  <body class="nk-body">
3632
3632
  <!-- A narrow column, centred: layout is yours, so it is inline. -->
@@ -3720,6 +3720,9 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
3720
3720
  | A `<div style="display:flex">` around `<nk-progress wide label>` in a panel so the label stays beside the bar | Not needed: with a `label` the element sets bar and label in one `.nk-progress-row` – beside each other in a flex column too |
3721
3721
  | `copy-label="⧉"` or other glyphs to save room in `<nk-copy-field>` on a phone | Nothing: on a phone the buttons show icons by themselves, the words stay their names; `icons` does it at every width |
3722
3722
  | `<nk-tab-bar>` kept on the last tab while a page from the drawer is open | Set `value` to that page's own value: a value no item has marks the `drawer` item ("More"), as on iOS |
3723
+ | A banner under the topbar for "New version · Reload", or a toast plus a separate Undo button | `toast.show('Moved to trash', { action: { label: 'Undo', value: 'undo' } })` – a button and an ×, it stays until used; `nk-action` names the action |
3724
+ | `stopImmediatePropagation()` or a check of your own so that Escape in a dialog closes only its date picker | Nothing: Escape closes the layer that opened last – the floating date picker or menu first, then the dialog |
3725
+ | A `focusBack()` helper after a menu, the modal or the palette opened from a view's shadow root | Nothing: the overlays find the focused control behind shadow hosts and give the focus back to it |
3723
3726
 
3724
3727
 
3725
3728
  # 7. Quick Reference
@@ -3800,7 +3803,7 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
3800
3803
  | `<nk-menu-item>` | overlays | `icon`, `shortcut`, `value`, `danger` | `(default)`, `icon` | `nk-select`, `nk-change` |
3801
3804
  | `<nk-pop>` | overlays | `open`, `placement`, `bare` | `trigger`, `(default)` | `nk-toggle` |
3802
3805
  | `<nk-emoji-picker>` | overlays | `placeholder`, `value` | – | `nk-select` |
3803
- | `<nk-toast>` | overlays | `open`, `duration`, `icon` | `(default)` | `nk-toggle` |
3806
+ | `<nk-toast>` | overlays | `open`, `duration`, `icon` | `(default)`, `action` | `nk-toggle`, `nk-action` |
3804
3807
  | `<nk-tooltip>` | overlays | `for`, `shortcut`, `delay`, `placement` | `(default)` | – |
3805
3808
  | `<nk-database>` | data | `view`, `add-view` | `(default)`, `tools`, `filters` | `nk-view-change`, `nk-action`, `nk-select / nk-change / nk-action` |
3806
3809
  | `<nk-table-view>` | data | `name`, `label`, `badge`, `count` | – | `nk-select`, `nk-change`, `nk-action` |
@@ -3834,10 +3837,10 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
3834
3837
  | Texts | `src/util/strings.js` – the English and German dictionary and `setStrings()`; `NkElement#str(key)` reads it in the element's language, `onStringsChanged()` runs after `setStrings()` and when `<html lang>` changes |
3835
3838
  | Components | `src/components/{forms,content,shell,page,overlays,data}/nk-*.js`, one tag per file, `customElements.define` at the bottom |
3836
3839
  | Build | Rollup: IIFE, minified IIFE, ESM, and per-component ESM entries on a stable `dist/components/base.js` that import NotionKit's sheet (`@jungherz-de/notionkit/notionkit-styles.js`) instead of inlining it; the full bundles inline it and export `componentsSheet` |
3837
- | Peer | `@jungherz-de/notionkit >= 1.18.0` – from 1.5.0 on the elements and the foundation share one version number; the bundle embeds that release's stylesheet, so keep them in step |
3840
+ | Peer | `@jungherz-de/notionkit >= 1.19.0` – from 1.5.0 on the elements and the foundation share one version number; the bundle embeds that release's stylesheet, so keep them in step |
3838
3841
 
3839
3842
  Lifecycle: construct (attach shadow, adopt sheets) → first connect (wrapper + `render()`) → every connect (`setupEvents()`, theme registration, light-DOM observer) → `attributeChangedCallback` → `onAttributeChanged` → disconnect (`teardownEvents()`, unregister).
3840
3843
 
3841
3844
 
3842
3845
  ---
3843
- *NotionKit Elements v1.18.0 · wrapping NotionKit CSS v1.18.0 · MIT · Jungherz GmbH*
3846
+ *NotionKit Elements v1.19.0 · wrapping NotionKit CSS v1.19.1 · MIT · Jungherz GmbH*
@@ -1,7 +1,8 @@
1
1
  import { NkFormElement } from './base.js';
2
2
  import { p as parseDay, i as isoOf, C as CHEVRONS, r as resolveLocale, f as firstWeekday, m as monthGrid, w as weekLabel, a as weekdayNames, b as isoWeek, s as stepMonth, c as addDays } from './shared/dates-a32DcW1l.js';
3
3
  import { p as placeUnder } from './shared/floating-DOK73b_D.js';
4
- import { d as deepActiveElement } from './shared/focus-C4tbSNND.js';
4
+ import { d as deepActiveElement } from './shared/focus-D55JGjRA.js';
5
+ import { c as closeLayer, o as openLayer } from './shared/layers-D9YYYA5g.js';
5
6
  import '@jungherz-de/notionkit/notionkit-styles.js';
6
7
 
7
8
  // <nk-calendar name="due" value="2026-06-02" weeks weekend="6,0"></nk-calendar>
@@ -272,14 +273,13 @@ class NkCalendar extends NkFormElement {
272
273
  this._draw();
273
274
  this.emit('nk-change', { value: '', start: null, end: null, time: null });
274
275
  };
275
- // Floating: a tap outside closes it and reaches nothing else; Escape closes (captured, before a modal around it).
276
+ // Floating: a tap outside closes it and reaches nothing else; Escape closes it, before a dialog around it (util/layers.js).
276
277
  this._onOutside = (e) => {
277
278
  const path = e.composedPath();
278
279
  if (path.includes(this) || (this._anchor && path.includes(this._anchor))) return;
279
280
  e.preventDefault(); e.stopPropagation();
280
281
  this.close();
281
282
  };
282
- this._onEscape = (e) => { if (e.key === 'Escape' && this.getBoolAttr('floating') && this.getBoolAttr('open')) { e.stopPropagation(); this.close(); } };
283
283
  this._grid.addEventListener('click', this._onGrid);
284
284
  this._grid.addEventListener('keydown', this._onKey);
285
285
  this._todayBtn.addEventListener('click', this._onToday);
@@ -287,7 +287,6 @@ class NkCalendar extends NkFormElement {
287
287
  this._next.addEventListener('click', this._onNext);
288
288
  this._timeInput.addEventListener('change', this._onTime);
289
289
  this._clear.addEventListener('click', this._onClear);
290
- document.addEventListener('keydown', this._onEscape, true);
291
290
  if (this.getBoolAttr('open')) document.addEventListener('click', this._onOutside, true);
292
291
  }
293
292
 
@@ -299,7 +298,7 @@ class NkCalendar extends NkFormElement {
299
298
  this._next?.removeEventListener('click', this._onNext);
300
299
  this._timeInput?.removeEventListener('change', this._onTime);
301
300
  this._clear?.removeEventListener('click', this._onClear);
302
- document.removeEventListener('keydown', this._onEscape, true);
301
+ closeLayer(this);
303
302
  document.removeEventListener('click', this._onOutside, true);
304
303
  }
305
304
 
@@ -323,7 +322,9 @@ class NkCalendar extends NkFormElement {
323
322
  this._returnFocus = deepActiveElement();
324
323
  if (this._returnFocus?.matches(':focus-visible')) requestAnimationFrame(() => this.focusDay());
325
324
  document.addEventListener('click', this._onOutside, true);
325
+ if (this.getBoolAttr('floating')) openLayer(this, () => this.close());
326
326
  } else {
327
+ closeLayer(this);
327
328
  document.removeEventListener('click', this._onOutside, true);
328
329
  const back = this._returnFocus;
329
330
  this._returnFocus = null;
@@ -1,5 +1,6 @@
1
1
  import { NkElement } from './base.js';
2
- import { l as lockScroll, i as inertOutside, u as unlockScroll } from './shared/focus-C4tbSNND.js';
2
+ import { d as deepActiveElement, l as lockScroll, i as inertOutside, u as unlockScroll } from './shared/focus-D55JGjRA.js';
3
+ import { o as openLayer, c as closeLayer } from './shared/layers-D9YYYA5g.js';
3
4
  import '@jungherz-de/notionkit/notionkit-styles.js';
4
5
 
5
6
  // Subsequence scoring for the command palette: every query character must
@@ -153,14 +154,16 @@ class NkCmdk extends NkElement {
153
154
  if (open === this._wasOpen) return;
154
155
  this._wasOpen = open;
155
156
  if (open) {
156
- this._returnFocus = document.activeElement;
157
+ this._returnFocus = deepActiveElement();
157
158
  this._input.value = '';
158
159
  this._index = 0;
159
160
  this._renderList();
160
161
  lockScroll();
161
162
  this._undoInert = inertOutside(this);
163
+ openLayer(this, () => this.close());
162
164
  requestAnimationFrame(() => this._input.focus({ preventScroll: true }));
163
165
  } else {
166
+ closeLayer(this);
164
167
  unlockScroll();
165
168
  this._undoInert?.(); this._undoInert = null;
166
169
  const back = this._returnFocus;
@@ -171,8 +174,7 @@ class NkCmdk extends NkElement {
171
174
 
172
175
  setupEvents() {
173
176
  this._onDocKey = (e) => {
174
- if (this._matchesHotkey(e)) { e.preventDefault(); this.toggle(); return; }
175
- if (e.key === 'Escape' && this.getBoolAttr('open')) { e.stopPropagation(); this.close(); }
177
+ if (this._matchesHotkey(e)) { e.preventDefault(); this.toggle(); }
176
178
  };
177
179
  this._onInput = () => { this._index = 0; this._renderList(); };
178
180
  this._onInputKey = (e) => {
@@ -201,7 +203,7 @@ class NkCmdk extends NkElement {
201
203
  this._list?.removeEventListener('mousemove', this._onListMove);
202
204
  this._list?.removeEventListener('mousedown', this._onListDown);
203
205
  this._backdrop?.removeEventListener('click', this._onBackdrop);
204
- if (this._wasOpen) { unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
206
+ if (this._wasOpen) { closeLayer(this); unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
205
207
  }
206
208
 
207
209
  onAttributeChanged(name) {
@@ -1,5 +1,6 @@
1
1
  import { NkElement } from './base.js';
2
- import { d as deepActiveElement, l as lockScroll, i as inertOutside, f as firstFocusable, u as unlockScroll } from './shared/focus-C4tbSNND.js';
2
+ import { d as deepActiveElement, l as lockScroll, i as inertOutside, f as firstFocusable, u as unlockScroll } from './shared/focus-D55JGjRA.js';
3
+ import { o as openLayer, c as closeLayer } from './shared/layers-D9YYYA5g.js';
3
4
  import '@jungherz-de/notionkit/notionkit-styles.js';
4
5
 
5
6
  // <nk-dialog id="trash" title="Move “Roadmap” to trash?" alert>
@@ -80,8 +81,10 @@ class NkDialog extends NkElement {
80
81
  this.inert = false;
81
82
  lockScroll();
82
83
  this._undoInert = inertOutside(this);
84
+ openLayer(this, () => this.close(''));
83
85
  requestAnimationFrame(() => (this.querySelector('[autofocus]') || firstFocusable(this) || this._box).focus({ preventScroll: true }));
84
86
  } else {
87
+ closeLayer(this);
85
88
  unlockScroll();
86
89
  this._undoInert?.(); this._undoInert = null;
87
90
  this.inert = this._wasInert;
@@ -93,7 +96,6 @@ class NkDialog extends NkElement {
93
96
 
94
97
  setupEvents() {
95
98
  this._onBackdrop = (e) => { if (e.target === this._backdrop) this.close(''); };
96
- this._onKey = (e) => { if (e.key === 'Escape' && this.getBoolAttr('open')) { e.stopPropagation(); this.close(''); } };
97
99
  // A button in the actions with a value closes it, and one with data-close
98
100
  // anywhere inside; a submit button is the form's.
99
101
  this._onClick = (e) => {
@@ -113,7 +115,6 @@ class NkDialog extends NkElement {
113
115
  this.addEventListener('click', this._onClick);
114
116
  this.addEventListener('submit', this._onSubmit);
115
117
  this._actionsSlot.addEventListener('slotchange', this._onSlot);
116
- document.addEventListener('keydown', this._onKey, true);
117
118
  this._onSlot();
118
119
  }
119
120
 
@@ -122,8 +123,7 @@ class NkDialog extends NkElement {
122
123
  this.removeEventListener('click', this._onClick);
123
124
  this.removeEventListener('submit', this._onSubmit);
124
125
  this._actionsSlot?.removeEventListener('slotchange', this._onSlot);
125
- document.removeEventListener('keydown', this._onKey, true);
126
- if (this._wasOpen) { unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
126
+ if (this._wasOpen) { closeLayer(this); unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
127
127
  }
128
128
 
129
129
  onAttributeChanged(name, oldValue, value) {
@@ -1,5 +1,6 @@
1
1
  import { NkElement } from './base.js';
2
- import { d as deepActiveElement } from './shared/focus-C4tbSNND.js';
2
+ import { d as deepActiveElement, c as containsDeep } from './shared/focus-D55JGjRA.js';
3
+ import { o as openLayer, c as closeLayer } from './shared/layers-D9YYYA5g.js';
3
4
  import { p as placeUnder } from './shared/floating-DOK73b_D.js';
4
5
  import '@jungherz-de/notionkit/notionkit-styles.js';
5
6
 
@@ -49,15 +50,30 @@ class NkMenu extends NkElement {
49
50
  this._returnFocus = deepActiveElement();
50
51
  if (this._returnFocus?.matches(':focus-visible')) requestAnimationFrame(() => this.focusFirst());
51
52
  document.addEventListener('click', this._onOutside, true);
53
+ if (this.getBoolAttr('floating')) { openLayer(this, () => this.close()); this._scrollToStart(); }
52
54
  } else {
55
+ closeLayer(this);
53
56
  document.removeEventListener('click', this._onOutside, true);
54
57
  const back = this._returnFocus;
55
58
  this._returnFocus = null;
56
- if (back?.isConnected && this.contains(document.activeElement)) back.focus({ preventScroll: true });
59
+ // The focus behind shadow hosts: document.activeElement stops at the
60
+ // host of a view that holds the menu (1.19.0).
61
+ const active = deepActiveElement();
62
+ if (back?.isConnected && active && (containsDeep(this, active) || active === document.body)) back.focus({ preventScroll: true });
57
63
  }
58
64
  this.emit('nk-toggle', { open });
59
65
  }
60
66
 
67
+ // A long menu opens at its top again, or at its first checked item (1.19.0):
68
+ // it kept where it had been scrolled to, its heading out of sight.
69
+ _scrollToStart() {
70
+ this._box.scrollTop = 0;
71
+ const item = this.items.find(i => i.hasAttribute('checked'))?.shadowRoot?.querySelector('.nk-menu-item');
72
+ if (!item) return;
73
+ const box = this._box.getBoundingClientRect(), r = item.getBoundingClientRect();
74
+ if (r.bottom > box.bottom) this._box.scrollTop += r.top - box.top - (box.height - r.height) / 2;
75
+ }
76
+
61
77
  /** Opens under `anchor` (any element, display: contents hosts included). */
62
78
  show(anchor) {
63
79
  if (anchor) {
@@ -95,17 +111,14 @@ class NkMenu extends NkElement {
95
111
  e.preventDefault(); e.stopPropagation();
96
112
  this.close();
97
113
  };
98
- // Captured, so a menu inside a modal or sheet closes before they do.
99
- this._onEscape = (e) => { if (e.key === 'Escape' && this.getBoolAttr('floating') && this.getBoolAttr('open')) { e.stopPropagation(); this.close(); } };
100
114
  this._onSelect = () => { if (this.getBoolAttr('floating')) this.close(); };
101
- document.addEventListener('keydown', this._onEscape, true);
102
115
  this.addEventListener('nk-select', this._onSelect);
103
116
  if (this.getBoolAttr('open')) document.addEventListener('click', this._onOutside, true);
104
117
  }
105
118
 
106
119
  teardownEvents() {
107
120
  this.removeEventListener('keydown', this._onKey);
108
- document.removeEventListener('keydown', this._onEscape, true);
121
+ closeLayer(this);
109
122
  this.removeEventListener('nk-select', this._onSelect);
110
123
  document.removeEventListener('click', this._onOutside, true);
111
124
  }
@@ -1,5 +1,6 @@
1
1
  import { NkElement } from './base.js';
2
- import { l as lockScroll, i as inertOutside, f as firstFocusable, u as unlockScroll } from './shared/focus-C4tbSNND.js';
2
+ import { d as deepActiveElement, l as lockScroll, i as inertOutside, f as firstFocusable, u as unlockScroll } from './shared/focus-D55JGjRA.js';
3
+ import { o as openLayer, c as closeLayer } from './shared/layers-D9YYYA5g.js';
3
4
  import '@jungherz-de/notionkit/notionkit-styles.js';
4
5
 
5
6
  // <nk-modal id="settings">
@@ -94,14 +95,17 @@ class NkModal extends NkElement {
94
95
  if (open === this._wasOpen) return;
95
96
  this._wasOpen = open;
96
97
  if (open) {
97
- this._returnFocus = document.activeElement;
98
+ // Behind shadow hosts too: a trigger in a view's shadow root gets the focus back.
99
+ this._returnFocus = deepActiveElement();
98
100
  lockScroll();
99
101
  this._undoInert = inertOutside(this);
102
+ openLayer(this, () => this.close());
100
103
  requestAnimationFrame(() => {
101
104
  const target = this._rows.querySelector('.nk-tree-item.active') || firstFocusable(this) || this._box;
102
105
  target.focus({ preventScroll: true });
103
106
  });
104
107
  } else {
108
+ closeLayer(this);
105
109
  unlockScroll();
106
110
  this._undoInert?.(); this._undoInert = null;
107
111
  const back = this._returnFocus;
@@ -112,7 +116,6 @@ class NkModal extends NkElement {
112
116
 
113
117
  setupEvents() {
114
118
  this._onBackdrop = (e) => { if (e.target === this._backdrop) this.close(); };
115
- this._onKey = (e) => { if (e.key === 'Escape' && this.getBoolAttr('open')) { e.stopPropagation(); this.close(); } };
116
119
  this._onNavClick = (e) => { const row = e.target.closest('[data-pane]'); if (row) { row.focus(); this.pane = row.dataset.pane; } };
117
120
  this._onNavKey = (e) => {
118
121
  const row = e.target.closest?.('[data-pane]');
@@ -126,7 +129,6 @@ class NkModal extends NkElement {
126
129
  };
127
130
  this._onSlot = () => this.requestNav();
128
131
  this._backdrop.addEventListener('click', this._onBackdrop);
129
- document.addEventListener('keydown', this._onKey);
130
132
  this._rows.addEventListener('click', this._onNavClick);
131
133
  this._rows.addEventListener('keydown', this._onNavKey);
132
134
  this._slot.addEventListener('slotchange', this._onSlot);
@@ -134,11 +136,10 @@ class NkModal extends NkElement {
134
136
 
135
137
  teardownEvents() {
136
138
  this._backdrop?.removeEventListener('click', this._onBackdrop);
137
- document.removeEventListener('keydown', this._onKey);
138
139
  this._rows?.removeEventListener('click', this._onNavClick);
139
140
  this._rows?.removeEventListener('keydown', this._onNavKey);
140
141
  this._slot?.removeEventListener('slotchange', this._onSlot);
141
- if (this._wasOpen) { unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
142
+ if (this._wasOpen) { closeLayer(this); unlockScroll(); this._undoInert?.(); this._undoInert = null; this._wasOpen = false; }
142
143
  }
143
144
 
144
145
  onStringsChanged() { this._nav.setAttribute('aria-label', this.str('settings')); }
@@ -1,5 +1,6 @@
1
1
  import { NkElement } from './base.js';
2
- import { d as deepActiveElement, l as lockScroll, i as inertOutside, u as unlockScroll } from './shared/focus-C4tbSNND.js';
2
+ import { d as deepActiveElement, l as lockScroll, i as inertOutside, u as unlockScroll } from './shared/focus-D55JGjRA.js';
3
+ import { o as openLayer, c as closeLayer } from './shared/layers-D9YYYA5g.js';
3
4
  import '@jungherz-de/notionkit/notionkit-styles.js';
4
5
 
5
6
  // <nk-peek id="peek" label="Database Table-View">
@@ -109,8 +110,10 @@ class NkPeek extends NkElement {
109
110
  lockScroll();
110
111
  this._undoInert = inertOutside(this);
111
112
  }
113
+ openLayer(this, () => this.close());
112
114
  requestAnimationFrame(() => this._box.focus({ preventScroll: true }));
113
115
  } else {
116
+ closeLayer(this);
114
117
  if (this._modal) { this._modal = false; this._box.removeAttribute('aria-modal'); unlockScroll(); this._undoInert?.(); this._undoInert = null; }
115
118
  const active = deepActiveElement();
116
119
  const inside = active && (this.contains(active) || this._shadow.contains(active) || active === document.body);
@@ -123,7 +126,6 @@ class NkPeek extends NkElement {
123
126
  setupEvents() {
124
127
  this._onClose = () => this.close();
125
128
  this._onBackdrop = (e) => { if (e.target === this._backdrop) this.close(); };
126
- this._onKey = (e) => { if (e.key === 'Escape' && this.getBoolAttr('open')) this.close(); };
127
129
  // Bubble phase, after the page's own handlers: a click that showed the
128
130
  // peek again (another row) must not close it.
129
131
  this._onOutside = (e) => {
@@ -157,7 +159,6 @@ class NkPeek extends NkElement {
157
159
  this._handle.addEventListener('pointermove', this._onResizeMove);
158
160
  this._handle.addEventListener('lostpointercapture', this._onResizeEnd);
159
161
  this._handle.addEventListener('keydown', this._onResizeKey);
160
- document.addEventListener('keydown', this._onKey);
161
162
  document.addEventListener('click', this._onOutside);
162
163
  addEventListener('resize', this._onViewport);
163
164
  }
@@ -169,7 +170,7 @@ class NkPeek extends NkElement {
169
170
  this._handle?.removeEventListener('pointermove', this._onResizeMove);
170
171
  this._handle?.removeEventListener('lostpointercapture', this._onResizeEnd);
171
172
  this._handle?.removeEventListener('keydown', this._onResizeKey);
172
- document.removeEventListener('keydown', this._onKey);
173
+ closeLayer(this);
173
174
  document.removeEventListener('click', this._onOutside);
174
175
  removeEventListener('resize', this._onViewport);
175
176
  if (this._insetApp) { this._app()?.removeAttribute('peek-inset'); this._insetApp = false; }
@@ -1,4 +1,5 @@
1
1
  import { NkElement } from './base.js';
2
+ import { c as closeLayer, o as openLayer } from './shared/layers-D9YYYA5g.js';
2
3
  import '@jungherz-de/notionkit/notionkit-styles.js';
3
4
 
4
5
  // <nk-pop placement="bottom-start">
@@ -49,12 +50,10 @@ class NkPop extends NkElement {
49
50
  this.toggle();
50
51
  };
51
52
  this._onDocClick = (e) => { if (this.getBoolAttr('open') && !e.composedPath().includes(this)) this.close(); };
52
- this._onKey = (e) => { if (e.key === 'Escape' && this.getBoolAttr('open')) this.close(); };
53
53
  this._onSelect = (e) => { if (e.target !== this) this.close(); };
54
54
  this._onSlot = () => this._syncSurface();
55
55
  this.addEventListener('click', this._onTrigger);
56
56
  document.addEventListener('click', this._onDocClick);
57
- document.addEventListener('keydown', this._onKey);
58
57
  this._float.addEventListener('nk-select', this._onSelect);
59
58
  this._slot.addEventListener('slotchange', this._onSlot);
60
59
  }
@@ -62,7 +61,7 @@ class NkPop extends NkElement {
62
61
  teardownEvents() {
63
62
  this.removeEventListener('click', this._onTrigger);
64
63
  document.removeEventListener('click', this._onDocClick);
65
- document.removeEventListener('keydown', this._onKey);
64
+ closeLayer(this);
66
65
  this._float?.removeEventListener('nk-select', this._onSelect);
67
66
  this._slot?.removeEventListener('slotchange', this._onSlot);
68
67
  }
@@ -71,6 +70,7 @@ class NkPop extends NkElement {
71
70
  if (name === 'bare') this._syncSurface();
72
71
  if (name === 'open') {
73
72
  const open = this.getBoolAttr('open');
73
+ if (open) openLayer(this, () => this.close()); else closeLayer(this);
74
74
  for (const el of this._triggerSlot.assignedElements()) el.setAttribute('aria-expanded', open ? 'true' : 'false');
75
75
  this.emit('nk-toggle', { open });
76
76
  }