@jungherz-de/notionkit-elements 1.18.0 → 1.20.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 +48 -0
- package/README.md +11 -7
- package/SKILL.md +42 -37
- package/dist/components/base.js +2 -0
- package/dist/components/nk-calendar.js +6 -5
- package/dist/components/nk-cmdk.js +7 -5
- package/dist/components/nk-dialog.js +5 -5
- package/dist/components/nk-menu.js +19 -6
- package/dist/components/nk-modal.js +7 -6
- package/dist/components/nk-peek.js +5 -4
- package/dist/components/nk-pop.js +3 -3
- package/dist/components/nk-sheet.js +5 -5
- package/dist/components/nk-sidebar.js +1 -1
- package/dist/components/nk-theme-toggle.js +163 -22
- package/dist/components/nk-toast.js +107 -11
- package/dist/components/nk-tooltip.js +1 -1
- package/dist/components/shared/{focus-C4tbSNND.js → focus-D55JGjRA.js} +7 -1
- package/dist/components/shared/layers-D9YYYA5g.js +35 -0
- package/dist/notionkit-elements.esm.js +357 -69
- package/dist/notionkit-elements.esm.min.js +1 -1
- package/dist/notionkit-elements.esm.min.js.map +1 -1
- package/dist/notionkit-elements.js +357 -69
- package/dist/notionkit-elements.min.js +1 -1
- package/package.json +3 -3
- package/src/components/forms/nk-calendar.js +5 -4
- package/src/components/overlays/nk-cmdk.js +7 -5
- package/src/components/overlays/nk-dialog.js +4 -4
- package/src/components/overlays/nk-menu.js +19 -6
- package/src/components/overlays/nk-modal.js +7 -6
- package/src/components/overlays/nk-peek.js +4 -3
- package/src/components/overlays/nk-pop.js +3 -3
- package/src/components/overlays/nk-sheet.js +4 -4
- package/src/components/overlays/nk-toast.js +107 -11
- package/src/components/shell/nk-theme-toggle.js +163 -22
- package/src/util/focus.js +6 -0
- package/src/util/layers.js +33 -0
- package/src/util/strings.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,52 @@ 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.20.0] – 2026-10-12
|
|
8
|
+
|
|
9
|
+
Built against NotionKit CSS 1.20.0 (peer `>= 1.20.0`).
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`<nk-theme-toggle modes="auto light dark">` – a third state that follows
|
|
13
|
+
the system.** `auto` is what no stored value means, so the documented
|
|
14
|
+
inline script before first paint keeps working and a choice made without
|
|
15
|
+
`modes` still counts; while it is in force the element follows
|
|
16
|
+
`prefers-color-scheme` live, which the toggle never did. A click goes from
|
|
17
|
+
`auto` to the theme *not* on screen, then to the other one, then back, so
|
|
18
|
+
every click but the last changes something visibly. The glyph shows the
|
|
19
|
+
mode – `.nk-theme-glyph`, sun and moon diagonally for `auto` – instead of
|
|
20
|
+
the next theme, and the title names the mode and the next step (four new
|
|
21
|
+
texts: `appearance`, `themeAuto`, `themeLight`, `themeDark`). A choice made
|
|
22
|
+
in another tab arrives through the `storage` event. `value` returns
|
|
23
|
+
`auto | light | dark`, the new `theme` what is on screen, `nk-change`
|
|
24
|
+
reports both; `nextMode()` and `themeFor(mode)` are public.
|
|
25
|
+
**Two states stay the default**, where nothing about this element changes.
|
|
26
|
+
Requested by Mailverse (#2).
|
|
27
|
+
- `theme-color` keeps `<meta name="theme-color">` in step: while a theme is
|
|
28
|
+
fixed every bar carries that theme's colour, back on `auto` each one
|
|
29
|
+
carries its own again. The colours are the page's, never invented.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- The toggle added its system-preference listener to a throwaway
|
|
33
|
+
`matchMedia()` object and removed it from a second one, so it was never
|
|
34
|
+
released and could be collected with its query. One query per element now.
|
|
35
|
+
- A mode whose write failed (Safari's private mode refuses to store but
|
|
36
|
+
reads) is kept in the element until the page reloads, instead of falling
|
|
37
|
+
back to `auto` on the next read.
|
|
38
|
+
|
|
39
|
+
## [1.19.0] – 2026-09-25
|
|
40
|
+
|
|
41
|
+
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.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- **`<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.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
- **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.
|
|
48
|
+
- **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).
|
|
49
|
+
- **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).
|
|
50
|
+
- **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).
|
|
51
|
+
- **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.
|
|
52
|
+
|
|
7
53
|
## [1.18.0] – 2026-09-25
|
|
8
54
|
|
|
9
55
|
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 +778,8 @@ built and tested against NotionKit CSS 1.1.1 (peer range `>= 1.0.0`).
|
|
|
732
778
|
import, never in the core bundle – shadow-less, adding `nk-block-host` to
|
|
733
779
|
itself so the foundation's editor adapter rules apply.
|
|
734
780
|
|
|
781
|
+
[1.20.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.20.0
|
|
782
|
+
[1.19.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.19.0
|
|
735
783
|
[1.18.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.18.0
|
|
736
784
|
[1.17.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.17.0
|
|
737
785
|
[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.
|
|
4
|
+
<a href="https://www.npmjs.com/package/@jungherz-de/notionkit-elements"><img src="https://img.shields.io/badge/version-1.20.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.
|
|
8
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.20.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** |
|
|
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.
|
|
75
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
74
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
75
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.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.
|
|
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
|
|
|
@@ -161,6 +161,10 @@ The elements on the page take new texts at once, after `setStrings()` and when `
|
|
|
161
161
|
| `commandPlaceholder` | `nk-cmdk` | `placeholder` | Search or type a command … | Suchen oder Befehl eingeben … |
|
|
162
162
|
| `emojiPlaceholder` | `nk-emoji-picker` | `placeholder` | Search… | Suchen … |
|
|
163
163
|
| `toggleTheme` | `nk-theme-toggle` | `title` | Toggle light / dark | Hell / Dunkel |
|
|
164
|
+
| `appearance` | `nk-theme-toggle` (`modes` with `auto`) | `title` | Appearance: {mode} – click: {next} | Darstellung: {mode} – Klick: {next} |
|
|
165
|
+
| `themeAuto` | `nk-theme-toggle` (`modes` with `auto`) | – | automatic (follows the system) | automatisch (wie das System) |
|
|
166
|
+
| `themeLight` | `nk-theme-toggle` (`modes` with `auto`) | – | light | hell |
|
|
167
|
+
| `themeDark` | `nk-theme-toggle` (`modes` with `auto`) | – | dark | dunkel |
|
|
164
168
|
| `synced` | `nk-synced` | `badge` | ⟳ synced | ⟳ synchronisiert |
|
|
165
169
|
| `message` | `nk-ai-input-row` | – | Message | Nachricht |
|
|
166
170
|
| `comment` | `nk-comments` | – | Comment | Kommentar |
|
|
@@ -184,7 +188,7 @@ A step of `<nk-steps>` with `href` and a card of `<nk-gallery-view>` whose row h
|
|
|
184
188
|
- 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
189
|
- 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
190
|
- `::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.
|
|
191
|
+
- 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
192
|
- 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
193
|
|
|
190
194
|
## 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.
|
|
3
|
+
description: NotionKit Elements is a vanilla-JS Web Components library (v1.20.0) wrapping NotionKit CSS v1.20.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.
|
|
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.
|
|
25
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
24
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
25
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.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>
|
|
@@ -1177,7 +1177,7 @@ Give it plain `<span>` or `<a>` children; they are cloned into the bar with sepa
|
|
|
1177
1177
|
|
|
1178
1178
|
### 3.33 `<nk-theme-toggle>` – Theme toggle
|
|
1179
1179
|
|
|
1180
|
-
The ☀️/🌙 button. Flips `data-theme` on `<html>`, remembers the choice in `localStorage`, applies a stored or system preference on first connect when `<html>` has no theme yet, and accepts `postMessage({ nkTheme })` from a parent page. `apply(
|
|
1180
|
+
The ☀️/🌙 button. Flips `data-theme` on `<html>`, remembers the choice in `localStorage`, applies a stored or system preference on first connect when `<html>` has no theme yet, and accepts `postMessage({ nkTheme })` from a parent page. `apply(mode)` does everything a click does: sets, persists and fires `nk-change`.`modes="auto light dark"` adds a third state: auto follows `prefers-color-scheme`, live, and is what no stored value means – so the inline script before first paint keeps working and a choice made without `modes` still counts. A click then goes from auto to the theme not on screen, to the other one, back to auto, so every click but the last changes something. The glyph shows the mode (`nk-theme-glyph`, sun and moon diagonally for auto) instead of the next theme, the title names the mode and the next one, and another tab's choice arrives through the `storage` event. Two states stay the default, where nothing about this element changes.
|
|
1181
1181
|
|
|
1182
1182
|
```html
|
|
1183
1183
|
<nk-theme-toggle></nk-theme-toggle>
|
|
@@ -1185,14 +1185,16 @@ The ☀️/🌙 button. Flips `data-theme` on `<html>`, remembers the choice in
|
|
|
1185
1185
|
|
|
1186
1186
|
| Attribute | Type | Default | Description |
|
|
1187
1187
|
|---|---|---|---|
|
|
1188
|
-
| `
|
|
1189
|
-
| `
|
|
1188
|
+
| `modes` | string | `light dark` | The states offered, e.g. `auto light dark`; without `auto` among them nothing changes. |
|
|
1189
|
+
| `storage-key` | string | `nk-theme` | localStorage key; `auto` is no entry. |
|
|
1190
|
+
| `theme-color` | boolean | – | Keeps `<meta name="theme-color">` in step: while a theme is fixed every bar carries that theme's colour – the page's own, never invented. |
|
|
1191
|
+
| `title` | string | – | Tooltip; replaces the built-in text. |
|
|
1190
1192
|
|
|
1191
|
-
**Events:** `nk-change` `{ value: 'light' | 'dark' }` –
|
|
1193
|
+
**Events:** `nk-change` `{ value: 'auto' | 'light' | 'dark', theme: 'light' | 'dark' }` – Mode applied; `theme` is what is on screen (without `auto` both are the theme).
|
|
1192
1194
|
|
|
1193
|
-
**Properties:** `value` · **Methods:** `apply(
|
|
1195
|
+
**Properties:** `value`, `theme`, `mode`, `modes` · **Methods:** `apply(mode)`, `nextMode()`, `themeFor(mode)`
|
|
1194
1196
|
|
|
1195
|
-
**Replaces:** `.nk-theme-toggle`
|
|
1197
|
+
**Replaces:** `.nk-theme-toggle`, `.nk-theme-glyph`
|
|
1196
1198
|
|
|
1197
1199
|
```html
|
|
1198
1200
|
<!-- equivalent class markup -->
|
|
@@ -2531,29 +2533,29 @@ Search field, 8-column grid, category strip. Ships with a built-in set (names fo
|
|
|
2531
2533
|
|
|
2532
2534
|
### 3.75 `<nk-toast>` – Toast
|
|
2533
2535
|
|
|
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.
|
|
2536
|
+
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
2537
|
|
|
2536
2538
|
```html
|
|
2537
|
-
<nk-toast open duration="0">
|
|
2539
|
+
<nk-toast open duration="0" icon="🗑️">Moved to trash<button slot="action" value="undo">Undo</button></nk-toast>
|
|
2538
2540
|
```
|
|
2539
2541
|
|
|
2540
2542
|
| Attribute | Type | Default | Description |
|
|
2541
2543
|
|---|---|---|---|
|
|
2542
2544
|
| `open` | boolean | – | Visible. |
|
|
2543
|
-
| `duration` | ms | `2200` | Auto-hide delay (0 = stay). |
|
|
2545
|
+
| `duration` | ms | `2200` | Auto-hide delay (0 = stay); a toast with an action stays unless show() names one. |
|
|
2544
2546
|
| `icon` | string | `✓` | Leading glyph. |
|
|
2545
2547
|
|
|
2546
|
-
**Slots:** `(default)` – Static content (when `show()` gets no message).
|
|
2548
|
+
**Slots:** `(default)` – Static content (when `show()` gets no message). · `action` – A button for the action; its `value` goes out with `nk-action`.
|
|
2547
2549
|
|
|
2548
|
-
**Events:** `nk-toggle` `{ open }` – Shown / hidden.
|
|
2550
|
+
**Events:** `nk-toggle` `{ open }` – Shown / hidden. · `nk-action` `{ action, value, label }` – The action used; cancel it to keep the toast.
|
|
2549
2551
|
|
|
2550
|
-
**Properties:** `open`, `message` · **Methods:** `show(message?, { duration })`, `close()`
|
|
2552
|
+
**Properties:** `open`, `message` · **Methods:** `show(message?, { duration, action: { label, value } })`, `close()`
|
|
2551
2553
|
|
|
2552
|
-
**Replaces:** `.nk-toast`, `.show`
|
|
2554
|
+
**Replaces:** `.nk-toast`, `.show`, `.t-action`, `.t-close`
|
|
2553
2555
|
|
|
2554
2556
|
```html
|
|
2555
2557
|
<!-- equivalent class markup -->
|
|
2556
|
-
<div class="nk-toast show"
|
|
2558
|
+
<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
2559
|
```
|
|
2558
2560
|
|
|
2559
2561
|
**Small screens:** Unchanged.
|
|
@@ -3150,8 +3152,8 @@ Eight skeletons, one per app shape, mirroring the NotionKit CSS SKILL.md. Copy o
|
|
|
3150
3152
|
<html lang="en" data-theme="light">
|
|
3151
3153
|
<head>
|
|
3152
3154
|
<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.
|
|
3154
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3155
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3156
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3155
3157
|
</head>
|
|
3156
3158
|
<body class="nk-body">
|
|
3157
3159
|
<nk-app>
|
|
@@ -3232,8 +3234,8 @@ Rules of the shell: `nk-sidebar`, `nk-topbar` and `nk-page` are `display: conten
|
|
|
3232
3234
|
<html lang="en" data-theme="light">
|
|
3233
3235
|
<head>
|
|
3234
3236
|
<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.
|
|
3236
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3237
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3238
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3237
3239
|
</head>
|
|
3238
3240
|
<body class="nk-body">
|
|
3239
3241
|
<nk-app>
|
|
@@ -3340,8 +3342,8 @@ Data contract: `columns` describe the properties (`type`: text | select | multi-
|
|
|
3340
3342
|
<html lang="en" data-theme="light">
|
|
3341
3343
|
<head>
|
|
3342
3344
|
<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.
|
|
3344
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3345
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3346
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3345
3347
|
</head>
|
|
3346
3348
|
<body class="nk-body">
|
|
3347
3349
|
<nk-app>
|
|
@@ -3400,8 +3402,8 @@ Data contract: `columns` describe the properties (`type`: text | select | multi-
|
|
|
3400
3402
|
<html lang="en" data-theme="light">
|
|
3401
3403
|
<head>
|
|
3402
3404
|
<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.
|
|
3404
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3405
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3406
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3405
3407
|
</head>
|
|
3406
3408
|
<body class="nk-body">
|
|
3407
3409
|
<!-- your app -->
|
|
@@ -3478,8 +3480,8 @@ The open/close contract is one attribute: `settings.open = true`, `settings.show
|
|
|
3478
3480
|
<html lang="en" data-theme="light">
|
|
3479
3481
|
<head>
|
|
3480
3482
|
<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.
|
|
3482
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3483
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3484
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3483
3485
|
</head>
|
|
3484
3486
|
<body class="nk-body">
|
|
3485
3487
|
<div class="nk-page" style="padding-top:48px">
|
|
@@ -3525,8 +3527,8 @@ The open/close contract is one attribute: `settings.open = true`, `settings.show
|
|
|
3525
3527
|
<html lang="en" data-theme="light">
|
|
3526
3528
|
<head>
|
|
3527
3529
|
<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.
|
|
3529
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3530
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3531
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3530
3532
|
</head>
|
|
3531
3533
|
<body class="nk-body">
|
|
3532
3534
|
<nk-page narrow icon="📘" cover>
|
|
@@ -3570,8 +3572,8 @@ Note `narrow`: the page is the document, so there is no inner scroll wrapper –
|
|
|
3570
3572
|
<html lang="en" data-theme="light">
|
|
3571
3573
|
<head>
|
|
3572
3574
|
<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.
|
|
3574
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3575
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3576
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3575
3577
|
</head>
|
|
3576
3578
|
<body class="nk-body">
|
|
3577
3579
|
<nk-app>
|
|
@@ -3625,8 +3627,8 @@ Note `narrow`: the page is the document, so there is no inner scroll wrapper –
|
|
|
3625
3627
|
<html lang="en" data-theme="light">
|
|
3626
3628
|
<head>
|
|
3627
3629
|
<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.
|
|
3629
|
-
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.
|
|
3630
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit@1.20.0/notionkit.min.css">
|
|
3631
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.0/dist/notionkit-elements.min.js"></script>
|
|
3630
3632
|
</head>
|
|
3631
3633
|
<body class="nk-body">
|
|
3632
3634
|
<!-- A narrow column, centred: layout is yours, so it is inline. -->
|
|
@@ -3720,6 +3722,9 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
|
|
|
3720
3722
|
| 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
3723
|
| `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
3724
|
| `<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 |
|
|
3725
|
+
| 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 |
|
|
3726
|
+
| `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 |
|
|
3727
|
+
| 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
3728
|
|
|
3724
3729
|
|
|
3725
3730
|
# 7. Quick Reference
|
|
@@ -3758,7 +3763,7 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
|
|
|
3758
3763
|
| `<nk-tree-item>` | shell | `icon`, `label`, `value`, `href` | `(default)`, `icon`, `end` | `nk-select`, `nk-toggle`, `nk-action` |
|
|
3759
3764
|
| `<nk-topbar>` | shell | – | `(default)`, `actions` | – |
|
|
3760
3765
|
| `<nk-breadcrumb>` | shell | `separator` | `(default)` | `nk-select` |
|
|
3761
|
-
| `<nk-theme-toggle>` | shell | `storage-key`, `title` | – | `nk-change` |
|
|
3766
|
+
| `<nk-theme-toggle>` | shell | `modes`, `storage-key`, `theme-color`, `title` | – | `nk-change` |
|
|
3762
3767
|
| `<nk-tab-bar>` | shell | `value`, `always`, `fixed`, `floating` | `(default)` | `nk-change`, `nk-select` |
|
|
3763
3768
|
| `<nk-tab-bar-item>` | shell | `icon`, `value`, `label`, `href` | `(default)`, `icon` | `nk-select` |
|
|
3764
3769
|
| `<nk-page>` | page | `icon`, `full`, `small`, `cover` | `(default)`, `cover`, `icon` | `nk-action` |
|
|
@@ -3800,7 +3805,7 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
|
|
|
3800
3805
|
| `<nk-menu-item>` | overlays | `icon`, `shortcut`, `value`, `danger` | `(default)`, `icon` | `nk-select`, `nk-change` |
|
|
3801
3806
|
| `<nk-pop>` | overlays | `open`, `placement`, `bare` | `trigger`, `(default)` | `nk-toggle` |
|
|
3802
3807
|
| `<nk-emoji-picker>` | overlays | `placeholder`, `value` | – | `nk-select` |
|
|
3803
|
-
| `<nk-toast>` | overlays | `open`, `duration`, `icon` | `(default)` | `nk-toggle` |
|
|
3808
|
+
| `<nk-toast>` | overlays | `open`, `duration`, `icon` | `(default)`, `action` | `nk-toggle`, `nk-action` |
|
|
3804
3809
|
| `<nk-tooltip>` | overlays | `for`, `shortcut`, `delay`, `placement` | `(default)` | – |
|
|
3805
3810
|
| `<nk-database>` | data | `view`, `add-view` | `(default)`, `tools`, `filters` | `nk-view-change`, `nk-action`, `nk-select / nk-change / nk-action` |
|
|
3806
3811
|
| `<nk-table-view>` | data | `name`, `label`, `badge`, `count` | – | `nk-select`, `nk-change`, `nk-action` |
|
|
@@ -3834,10 +3839,10 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
|
|
|
3834
3839
|
| 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
3840
|
| Components | `src/components/{forms,content,shell,page,overlays,data}/nk-*.js`, one tag per file, `customElements.define` at the bottom |
|
|
3836
3841
|
| 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.
|
|
3842
|
+
| Peer | `@jungherz-de/notionkit >= 1.20.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
3843
|
|
|
3839
3844
|
Lifecycle: construct (attach shadow, adopt sheets) → first connect (wrapper + `render()`) → every connect (`setupEvents()`, theme registration, light-DOM observer) → `attributeChangedCallback` → `onAttributeChanged` → disconnect (`teardownEvents()`, unregister).
|
|
3840
3845
|
|
|
3841
3846
|
|
|
3842
3847
|
---
|
|
3843
|
-
*NotionKit Elements v1.
|
|
3848
|
+
*NotionKit Elements v1.20.0 · wrapping NotionKit CSS v1.20.0 · MIT · Jungherz GmbH*
|
package/dist/components/base.js
CHANGED
|
@@ -18,6 +18,7 @@ const BUILT_IN = {
|
|
|
18
18
|
commentPlaceholder: 'Comment …', send: 'Send', askPlaceholder: 'Ask something …',
|
|
19
19
|
commandPlaceholder: 'Search or type a command …', emojiPlaceholder: 'Search…',
|
|
20
20
|
toggleTheme: 'Toggle light / dark', synced: '⟳ synced',
|
|
21
|
+
appearance: 'Appearance: {mode} – click: {next}', themeAuto: 'automatic (follows the system)', themeLight: 'light', themeDark: 'dark',
|
|
21
22
|
message: 'Message', breadcrumb: 'Breadcrumb', commandPalette: 'Command palette', comment: 'Comment',
|
|
22
23
|
searchEmoji: 'Search emoji', settings: 'Settings', title: 'Title', changeIcon: 'Change icon',
|
|
23
24
|
add: 'Add', more: 'More',
|
|
@@ -32,6 +33,7 @@ const BUILT_IN = {
|
|
|
32
33
|
commentPlaceholder: 'Kommentieren …', send: 'Senden', askPlaceholder: 'Frag etwas …',
|
|
33
34
|
commandPlaceholder: 'Suchen oder Befehl eingeben …', emojiPlaceholder: 'Suchen …',
|
|
34
35
|
toggleTheme: 'Hell / Dunkel', synced: '⟳ synchronisiert',
|
|
36
|
+
appearance: 'Darstellung: {mode} – Klick: {next}', themeAuto: 'automatisch (wie das System)', themeLight: 'hell', themeDark: 'dunkel',
|
|
35
37
|
message: 'Nachricht', breadcrumb: 'Pfad', commandPalette: 'Befehlspalette', comment: 'Kommentar',
|
|
36
38
|
searchEmoji: 'Emoji suchen', settings: 'Einstellungen', title: 'Titel', changeIcon: 'Symbol ändern',
|
|
37
39
|
add: 'Hinzufügen', more: 'Mehr',
|
|
@@ -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-
|
|
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
|
|
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
|
-
|
|
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-
|
|
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 =
|
|
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();
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
121
|
+
closeLayer(this);
|
|
109
122
|
this.removeEventListener('nk-select', this._onSelect);
|
|
110
123
|
document.removeEventListener('click', this._onOutside, true);
|
|
111
124
|
}
|