@jungherz-de/notionkit-elements 1.20.0 → 1.20.1
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 +29 -0
- package/README.md +3 -3
- package/SKILL.md +13 -13
- package/dist/components/nk-tooltip.js +61 -5
- package/dist/notionkit-elements.esm.js +61 -5
- package/dist/notionkit-elements.esm.min.js +1 -1
- package/dist/notionkit-elements.esm.min.js.map +1 -1
- package/dist/notionkit-elements.js +61 -5
- package/dist/notionkit-elements.min.js +1 -1
- package/package.json +1 -1
- package/src/components/overlays/nk-tooltip.js +61 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ 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.1] – 2026-10-14
|
|
8
|
+
|
|
9
|
+
Elements only; NotionKit CSS stays at 1.20.0 (peer `>= 1.20.0`).
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **`<nk-tooltip>` follows the pointer and the focus inside a shadow root
|
|
13
|
+
(#3).** Moving from one `[data-tooltip]` to the next in the same shadow
|
|
14
|
+
root never updated the tooltip – the first one stayed, or none appeared –
|
|
15
|
+
for the pointer and for Tab alike; `<nk-copy-field icons secret>` kept
|
|
16
|
+
saying "Show" over Copy, an app's toolbar or chart bars in one root did
|
|
17
|
+
the same. `pointerover`, `focusin` and `focusout` carry a `relatedTarget`,
|
|
18
|
+
and when target and `relatedTarget` share a shadow tree the DOM standard
|
|
19
|
+
ends the event path at its host, so the listeners on the document never
|
|
20
|
+
heard of the move. The tooltip now also reads `pointermove` (no
|
|
21
|
+
`relatedTarget`, it always reaches the document) and acts only when the
|
|
22
|
+
element under the pointer changes, so a hand resting on the page leaves a
|
|
23
|
+
tooltip the keyboard or `show(rect)` opened alone; and it catches a key
|
|
24
|
+
that moved the focus – Tab, Option+Tab in Safari, the arrow keys of a
|
|
25
|
+
roving tab stop that moves the focus in its own `keydown` – on `keyup`,
|
|
26
|
+
against the focus recorded before the key went down. Nested shadow roots
|
|
27
|
+
are followed too. A focus move by script alone inside one shadow root
|
|
28
|
+
stays unseen. Reported by Mailverse.
|
|
29
|
+
- **Leaving the window** over a `[data-tooltip]` left its tooltip standing,
|
|
30
|
+
and one still waiting appeared after the pointer had gone. It now goes
|
|
31
|
+
with the pointer (`pointerout` without a `relatedTarget`); a tooltip the
|
|
32
|
+
keyboard or `show(rect)` opened is not the pointer's and stays. Found
|
|
33
|
+
while testing #3.
|
|
34
|
+
|
|
7
35
|
## [1.20.0] – 2026-10-12
|
|
8
36
|
|
|
9
37
|
Built against NotionKit CSS 1.20.0 (peer `>= 1.20.0`).
|
|
@@ -778,6 +806,7 @@ built and tested against NotionKit CSS 1.1.1 (peer range `>= 1.0.0`).
|
|
|
778
806
|
import, never in the core bundle – shadow-less, adding `nk-block-host` to
|
|
779
807
|
itself so the foundation's editor adapter rules apply.
|
|
780
808
|
|
|
809
|
+
[1.20.1]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.20.1
|
|
781
810
|
[1.20.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.20.0
|
|
782
811
|
[1.19.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.19.0
|
|
783
812
|
[1.18.0]: https://github.com/JUNGHERZ/NotionKit-Elements/releases/tag/v1.18.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.20.
|
|
4
|
+
<a href="https://www.npmjs.com/package/@jungherz-de/notionkit-elements"><img src="https://img.shields.io/badge/version-1.20.1-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.20.
|
|
8
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.20.1-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
|
|
|
@@ -72,7 +72,7 @@ It is the **app layer** of the NotionKit family – three layers, one design lan
|
|
|
72
72
|
<html lang="en" data-theme="light">
|
|
73
73
|
<head>
|
|
74
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.
|
|
75
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/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">
|
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.20.
|
|
3
|
+
description: NotionKit Elements is a vanilla-JS Web Components library (v1.20.1) 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
|
|
@@ -22,7 +22,7 @@ description: NotionKit Elements is a vanilla-JS Web Components library (v1.20.0)
|
|
|
22
22
|
<meta charset="UTF-8">
|
|
23
23
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
24
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.
|
|
25
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
26
26
|
</head>
|
|
27
27
|
<body class="nk-body">
|
|
28
28
|
<nk-btn variant="primary">Save</nk-btn>
|
|
@@ -2562,7 +2562,7 @@ One inverted pill at the bottom centre, above every overlay; over a tab bar at t
|
|
|
2562
2562
|
|
|
2563
2563
|
### 3.76 `<nk-tooltip>` – Tooltip
|
|
2564
2564
|
|
|
2565
|
-
Notion’s hover hint in the toast’s colours, a shortcut muted beside it. One `<nk-tooltip>` without `for` serves every element with `data-tooltip` (and `data-tooltip-key`) on the page, inside shadow roots too – a whole toolbar in one element; with `for` it belongs to one element and shows its own content and `shortcut`. It appears after `delay` ms (400) under the pointer and at once on keyboard focus, never on touch; leaving
|
|
2565
|
+
Notion’s hover hint in the toast’s colours, a shortcut muted beside it. One `<nk-tooltip>` without `for` serves every element with `data-tooltip` (and `data-tooltip-key`) on the page, inside shadow roots too – a whole toolbar in one element, and it follows pointer and keyboard from one such element to the next within the same shadow root; with `for` it belongs to one element and shows its own content and `shortcut`. It appears after `delay` ms (400) under the pointer and at once on keyboard focus, never on touch; leaving – the window too –, a press, blur, scrolling and Escape hide it. It sits 6px below its target and centred, above it where the window ends, 8px inside the window – `placeNear` in `util/floating.js`. `show(target, text, shortcut)` takes an element or a rect, for what has no element of its own: the bars of a chart. The target’s `aria-describedby` names it while it shows. Hover the buttons. A tooltip shown with `show(rect, …)` stays until `hide()`, the pointer on another `[data-tooltip]`, a press or Escape; the wheel hides it too, also inside a shadow root. A line break in the text is kept.
|
|
2566
2566
|
|
|
2567
2567
|
```html
|
|
2568
2568
|
<div style="display:flex;gap:16px;align-items:center;padding:4px 0 44px">
|
|
@@ -3153,7 +3153,7 @@ Eight skeletons, one per app shape, mirroring the NotionKit CSS SKILL.md. Copy o
|
|
|
3153
3153
|
<head>
|
|
3154
3154
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3155
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.
|
|
3156
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3157
3157
|
</head>
|
|
3158
3158
|
<body class="nk-body">
|
|
3159
3159
|
<nk-app>
|
|
@@ -3235,7 +3235,7 @@ Rules of the shell: `nk-sidebar`, `nk-topbar` and `nk-page` are `display: conten
|
|
|
3235
3235
|
<head>
|
|
3236
3236
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3237
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.
|
|
3238
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3239
3239
|
</head>
|
|
3240
3240
|
<body class="nk-body">
|
|
3241
3241
|
<nk-app>
|
|
@@ -3343,7 +3343,7 @@ Data contract: `columns` describe the properties (`type`: text | select | multi-
|
|
|
3343
3343
|
<head>
|
|
3344
3344
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3345
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.
|
|
3346
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3347
3347
|
</head>
|
|
3348
3348
|
<body class="nk-body">
|
|
3349
3349
|
<nk-app>
|
|
@@ -3403,7 +3403,7 @@ Data contract: `columns` describe the properties (`type`: text | select | multi-
|
|
|
3403
3403
|
<head>
|
|
3404
3404
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3405
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.
|
|
3406
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3407
3407
|
</head>
|
|
3408
3408
|
<body class="nk-body">
|
|
3409
3409
|
<!-- your app -->
|
|
@@ -3481,7 +3481,7 @@ The open/close contract is one attribute: `settings.open = true`, `settings.show
|
|
|
3481
3481
|
<head>
|
|
3482
3482
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3483
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.
|
|
3484
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3485
3485
|
</head>
|
|
3486
3486
|
<body class="nk-body">
|
|
3487
3487
|
<div class="nk-page" style="padding-top:48px">
|
|
@@ -3528,7 +3528,7 @@ The open/close contract is one attribute: `settings.open = true`, `settings.show
|
|
|
3528
3528
|
<head>
|
|
3529
3529
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3530
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.
|
|
3531
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3532
3532
|
</head>
|
|
3533
3533
|
<body class="nk-body">
|
|
3534
3534
|
<nk-page narrow icon="📘" cover>
|
|
@@ -3573,7 +3573,7 @@ Note `narrow`: the page is the document, so there is no inner scroll wrapper –
|
|
|
3573
3573
|
<head>
|
|
3574
3574
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3575
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.
|
|
3576
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3577
3577
|
</head>
|
|
3578
3578
|
<body class="nk-body">
|
|
3579
3579
|
<nk-app>
|
|
@@ -3628,7 +3628,7 @@ Note `narrow`: the page is the document, so there is no inner scroll wrapper –
|
|
|
3628
3628
|
<head>
|
|
3629
3629
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3630
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.
|
|
3631
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/notionkit-elements@1.20.1/dist/notionkit-elements.min.js"></script>
|
|
3632
3632
|
</head>
|
|
3633
3633
|
<body class="nk-body">
|
|
3634
3634
|
<!-- A narrow column, centred: layout is yours, so it is inline. -->
|
|
@@ -3839,10 +3839,10 @@ Form controls additionally re-dispatch a native, bubbling `change` event, so `fo
|
|
|
3839
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 |
|
|
3840
3840
|
| Components | `src/components/{forms,content,shell,page,overlays,data}/nk-*.js`, one tag per file, `customElements.define` at the bottom |
|
|
3841
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` |
|
|
3842
|
-
| Peer | `@jungherz-de/notionkit >= 1.20.
|
|
3842
|
+
| Peer | `@jungherz-de/notionkit >= 1.20.1` – 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 |
|
|
3843
3843
|
|
|
3844
3844
|
Lifecycle: construct (attach shadow, adopt sheets) → first connect (wrapper + `render()`) → every connect (`setupEvents()`, theme registration, light-DOM observer) → `attributeChangedCallback` → `onAttributeChanged` → disconnect (`teardownEvents()`, unregister).
|
|
3845
3845
|
|
|
3846
3846
|
|
|
3847
3847
|
---
|
|
3848
|
-
*NotionKit Elements v1.20.
|
|
3848
|
+
*NotionKit Elements v1.20.1 · wrapping NotionKit CSS v1.20.0 · MIT · Jungherz GmbH*
|
|
@@ -24,6 +24,17 @@ import '@jungherz-de/notionkit/notionkit-styles.js';
|
|
|
24
24
|
// [data-tooltip], a press or Escape. A line break in the text is kept.
|
|
25
25
|
// While it shows, the target's aria-describedby names it (in the same
|
|
26
26
|
// document or shadow root).
|
|
27
|
+
//
|
|
28
|
+
// Following the pointer and the focus inside a shadow root (1.20.1):
|
|
29
|
+
// pointerover, focusin and focusout carry a relatedTarget, and when target
|
|
30
|
+
// and relatedTarget share a shadow tree the DOM standard ends the event path
|
|
31
|
+
// at its host – the document never hears of a move from one [data-tooltip]
|
|
32
|
+
// to the next in the same root. So the pointer is also read from
|
|
33
|
+
// pointermove, which has no relatedTarget, and acted on only when the
|
|
34
|
+
// element under it changes; and a key that moved the focus – Tab, the arrow
|
|
35
|
+
// keys of a group – is caught on keyup, comparing the focused element with
|
|
36
|
+
// the one before the key went down. A move by script alone inside one
|
|
37
|
+
// shadow root stays unseen.
|
|
27
38
|
const hostSheet = new CSSStyleSheet();
|
|
28
39
|
hostSheet.replaceSync(`:host { display: block; position: fixed; top: 0; left: 0; z-index: 130; pointer-events: none; }`);
|
|
29
40
|
let count = 0;
|
|
@@ -101,14 +112,50 @@ class NkTooltip extends NkElement {
|
|
|
101
112
|
this.show(el, own ? null : el.getAttribute('data-tooltip'), own ? this.getAttribute('shortcut') : el.getAttribute('data-tooltip-key'));
|
|
102
113
|
}
|
|
103
114
|
|
|
115
|
+
/**
|
|
116
|
+
* The pointer is over `el` now (null: no [data-tooltip]). pointerover acts
|
|
117
|
+
* on every boundary it crosses, as it always has; pointermove – the only
|
|
118
|
+
* one that reaches the document from inside a shadow root – only when the
|
|
119
|
+
* element under the pointer changed, so a hand resting on the page leaves
|
|
120
|
+
* a tooltip the keyboard or show(rect) opened alone.
|
|
121
|
+
*/
|
|
122
|
+
_pointer(e, move) {
|
|
123
|
+
const el = e.pointerType === 'touch' ? null : this._targetOf(e);
|
|
124
|
+
if (move && el === this._hover) return;
|
|
125
|
+
this._hover = el;
|
|
126
|
+
if (el === this._target) return;
|
|
127
|
+
this.hide();
|
|
128
|
+
if (!el) return;
|
|
129
|
+
this._target = el;
|
|
130
|
+
this._timer = setTimeout(() => this._showFor(el), this._delay);
|
|
131
|
+
}
|
|
132
|
+
|
|
104
133
|
setupEvents() {
|
|
105
|
-
this._onOver = (e) =>
|
|
106
|
-
|
|
107
|
-
|
|
134
|
+
this._onOver = (e) => this._pointer(e, false);
|
|
135
|
+
this._onMove = (e) => this._pointer(e, true);
|
|
136
|
+
// The pointer left the window (no relatedTarget): a tooltip – shown or
|
|
137
|
+
// still waiting – for what it was on goes with it. One the keyboard or
|
|
138
|
+
// show(rect) opened is not the pointer's and stays.
|
|
139
|
+
this._onOut = (e) => {
|
|
140
|
+
if (e.relatedTarget || e.pointerType === 'touch') return;
|
|
141
|
+
if (this._target && this._target === this._hover) this.hide();
|
|
142
|
+
this._hover = null;
|
|
143
|
+
};
|
|
144
|
+
// Capture, before an element's own keydown moves the focus (a roving tab
|
|
145
|
+
// stop does); not for a held key's repeats, so keyup compares with where
|
|
146
|
+
// the focus was before the key went down.
|
|
147
|
+
this._onKeyDown = (e) => { if (!e.repeat) this._focusBefore = deepActiveElement(); };
|
|
148
|
+
this._onKeyUp = (e) => {
|
|
149
|
+
const active = deepActiveElement();
|
|
150
|
+
if (active === this._focusBefore) return;
|
|
151
|
+
this._focusBefore = active;
|
|
152
|
+
// keyup goes to the focused element, so its path is the focus's path.
|
|
153
|
+
const el = this._targetOf(e);
|
|
154
|
+
if (el && el === this._target) return; // focusin got there first
|
|
108
155
|
this.hide();
|
|
109
|
-
if (!el) return;
|
|
156
|
+
if (!el || !active?.matches(':focus-visible')) return;
|
|
110
157
|
this._target = el;
|
|
111
|
-
this.
|
|
158
|
+
this._showFor(el);
|
|
112
159
|
};
|
|
113
160
|
this._onFocus = (e) => {
|
|
114
161
|
const el = this._targetOf(e);
|
|
@@ -120,7 +167,11 @@ class NkTooltip extends NkElement {
|
|
|
120
167
|
this._onHide = () => this.hide();
|
|
121
168
|
this._onKey = (e) => { if (e.key === 'Escape' && this.open) this.hide(); };
|
|
122
169
|
document.addEventListener('pointerover', this._onOver);
|
|
170
|
+
document.addEventListener('pointermove', this._onMove, { capture: true, passive: true });
|
|
171
|
+
document.addEventListener('pointerout', this._onOut);
|
|
123
172
|
document.addEventListener('focusin', this._onFocus);
|
|
173
|
+
document.addEventListener('keydown', this._onKeyDown, true);
|
|
174
|
+
document.addEventListener('keyup', this._onKeyUp, true);
|
|
124
175
|
document.addEventListener('focusout', this._onHide);
|
|
125
176
|
document.addEventListener('pointerdown', this._onHide, true);
|
|
126
177
|
document.addEventListener('scroll', this._onHide, true);
|
|
@@ -131,13 +182,18 @@ class NkTooltip extends NkElement {
|
|
|
131
182
|
|
|
132
183
|
teardownEvents() {
|
|
133
184
|
document.removeEventListener('pointerover', this._onOver);
|
|
185
|
+
document.removeEventListener('pointermove', this._onMove, { capture: true });
|
|
186
|
+
document.removeEventListener('pointerout', this._onOut);
|
|
134
187
|
document.removeEventListener('focusin', this._onFocus);
|
|
188
|
+
document.removeEventListener('keydown', this._onKeyDown, true);
|
|
189
|
+
document.removeEventListener('keyup', this._onKeyUp, true);
|
|
135
190
|
document.removeEventListener('focusout', this._onHide);
|
|
136
191
|
document.removeEventListener('pointerdown', this._onHide, true);
|
|
137
192
|
document.removeEventListener('scroll', this._onHide, true);
|
|
138
193
|
document.removeEventListener('wheel', this._onHide, { capture: true });
|
|
139
194
|
document.removeEventListener('keydown', this._onKey);
|
|
140
195
|
this.hide();
|
|
196
|
+
this._hover = this._focusBefore = null;
|
|
141
197
|
}
|
|
142
198
|
|
|
143
199
|
onAttributeChanged(name) { if (name === 'shortcut' && !this._text.data) this._fill(null); if (name === 'for') this.hide(); }
|
|
@@ -5636,6 +5636,17 @@ customElements.define('nk-dialog', NkDialog);
|
|
|
5636
5636
|
// [data-tooltip], a press or Escape. A line break in the text is kept.
|
|
5637
5637
|
// While it shows, the target's aria-describedby names it (in the same
|
|
5638
5638
|
// document or shadow root).
|
|
5639
|
+
//
|
|
5640
|
+
// Following the pointer and the focus inside a shadow root (1.20.1):
|
|
5641
|
+
// pointerover, focusin and focusout carry a relatedTarget, and when target
|
|
5642
|
+
// and relatedTarget share a shadow tree the DOM standard ends the event path
|
|
5643
|
+
// at its host – the document never hears of a move from one [data-tooltip]
|
|
5644
|
+
// to the next in the same root. So the pointer is also read from
|
|
5645
|
+
// pointermove, which has no relatedTarget, and acted on only when the
|
|
5646
|
+
// element under it changes; and a key that moved the focus – Tab, the arrow
|
|
5647
|
+
// keys of a group – is caught on keyup, comparing the focused element with
|
|
5648
|
+
// the one before the key went down. A move by script alone inside one
|
|
5649
|
+
// shadow root stays unseen.
|
|
5639
5650
|
const hostSheet = new CSSStyleSheet();
|
|
5640
5651
|
hostSheet.replaceSync(`:host { display: block; position: fixed; top: 0; left: 0; z-index: 130; pointer-events: none; }`);
|
|
5641
5652
|
let count = 0;
|
|
@@ -5713,14 +5724,50 @@ class NkTooltip extends NkElement {
|
|
|
5713
5724
|
this.show(el, own ? null : el.getAttribute('data-tooltip'), own ? this.getAttribute('shortcut') : el.getAttribute('data-tooltip-key'));
|
|
5714
5725
|
}
|
|
5715
5726
|
|
|
5727
|
+
/**
|
|
5728
|
+
* The pointer is over `el` now (null: no [data-tooltip]). pointerover acts
|
|
5729
|
+
* on every boundary it crosses, as it always has; pointermove – the only
|
|
5730
|
+
* one that reaches the document from inside a shadow root – only when the
|
|
5731
|
+
* element under the pointer changed, so a hand resting on the page leaves
|
|
5732
|
+
* a tooltip the keyboard or show(rect) opened alone.
|
|
5733
|
+
*/
|
|
5734
|
+
_pointer(e, move) {
|
|
5735
|
+
const el = e.pointerType === 'touch' ? null : this._targetOf(e);
|
|
5736
|
+
if (move && el === this._hover) return;
|
|
5737
|
+
this._hover = el;
|
|
5738
|
+
if (el === this._target) return;
|
|
5739
|
+
this.hide();
|
|
5740
|
+
if (!el) return;
|
|
5741
|
+
this._target = el;
|
|
5742
|
+
this._timer = setTimeout(() => this._showFor(el), this._delay);
|
|
5743
|
+
}
|
|
5744
|
+
|
|
5716
5745
|
setupEvents() {
|
|
5717
|
-
this._onOver = (e) =>
|
|
5718
|
-
|
|
5719
|
-
|
|
5746
|
+
this._onOver = (e) => this._pointer(e, false);
|
|
5747
|
+
this._onMove = (e) => this._pointer(e, true);
|
|
5748
|
+
// The pointer left the window (no relatedTarget): a tooltip – shown or
|
|
5749
|
+
// still waiting – for what it was on goes with it. One the keyboard or
|
|
5750
|
+
// show(rect) opened is not the pointer's and stays.
|
|
5751
|
+
this._onOut = (e) => {
|
|
5752
|
+
if (e.relatedTarget || e.pointerType === 'touch') return;
|
|
5753
|
+
if (this._target && this._target === this._hover) this.hide();
|
|
5754
|
+
this._hover = null;
|
|
5755
|
+
};
|
|
5756
|
+
// Capture, before an element's own keydown moves the focus (a roving tab
|
|
5757
|
+
// stop does); not for a held key's repeats, so keyup compares with where
|
|
5758
|
+
// the focus was before the key went down.
|
|
5759
|
+
this._onKeyDown = (e) => { if (!e.repeat) this._focusBefore = deepActiveElement(); };
|
|
5760
|
+
this._onKeyUp = (e) => {
|
|
5761
|
+
const active = deepActiveElement();
|
|
5762
|
+
if (active === this._focusBefore) return;
|
|
5763
|
+
this._focusBefore = active;
|
|
5764
|
+
// keyup goes to the focused element, so its path is the focus's path.
|
|
5765
|
+
const el = this._targetOf(e);
|
|
5766
|
+
if (el && el === this._target) return; // focusin got there first
|
|
5720
5767
|
this.hide();
|
|
5721
|
-
if (!el) return;
|
|
5768
|
+
if (!el || !active?.matches(':focus-visible')) return;
|
|
5722
5769
|
this._target = el;
|
|
5723
|
-
this.
|
|
5770
|
+
this._showFor(el);
|
|
5724
5771
|
};
|
|
5725
5772
|
this._onFocus = (e) => {
|
|
5726
5773
|
const el = this._targetOf(e);
|
|
@@ -5732,7 +5779,11 @@ class NkTooltip extends NkElement {
|
|
|
5732
5779
|
this._onHide = () => this.hide();
|
|
5733
5780
|
this._onKey = (e) => { if (e.key === 'Escape' && this.open) this.hide(); };
|
|
5734
5781
|
document.addEventListener('pointerover', this._onOver);
|
|
5782
|
+
document.addEventListener('pointermove', this._onMove, { capture: true, passive: true });
|
|
5783
|
+
document.addEventListener('pointerout', this._onOut);
|
|
5735
5784
|
document.addEventListener('focusin', this._onFocus);
|
|
5785
|
+
document.addEventListener('keydown', this._onKeyDown, true);
|
|
5786
|
+
document.addEventListener('keyup', this._onKeyUp, true);
|
|
5736
5787
|
document.addEventListener('focusout', this._onHide);
|
|
5737
5788
|
document.addEventListener('pointerdown', this._onHide, true);
|
|
5738
5789
|
document.addEventListener('scroll', this._onHide, true);
|
|
@@ -5743,13 +5794,18 @@ class NkTooltip extends NkElement {
|
|
|
5743
5794
|
|
|
5744
5795
|
teardownEvents() {
|
|
5745
5796
|
document.removeEventListener('pointerover', this._onOver);
|
|
5797
|
+
document.removeEventListener('pointermove', this._onMove, { capture: true });
|
|
5798
|
+
document.removeEventListener('pointerout', this._onOut);
|
|
5746
5799
|
document.removeEventListener('focusin', this._onFocus);
|
|
5800
|
+
document.removeEventListener('keydown', this._onKeyDown, true);
|
|
5801
|
+
document.removeEventListener('keyup', this._onKeyUp, true);
|
|
5747
5802
|
document.removeEventListener('focusout', this._onHide);
|
|
5748
5803
|
document.removeEventListener('pointerdown', this._onHide, true);
|
|
5749
5804
|
document.removeEventListener('scroll', this._onHide, true);
|
|
5750
5805
|
document.removeEventListener('wheel', this._onHide, { capture: true });
|
|
5751
5806
|
document.removeEventListener('keydown', this._onKey);
|
|
5752
5807
|
this.hide();
|
|
5808
|
+
this._hover = this._focusBefore = null;
|
|
5753
5809
|
}
|
|
5754
5810
|
|
|
5755
5811
|
onAttributeChanged(name) { if (name === 'shortcut' && !this._text.data) this._fill(null); if (name === 'for') this.hide(); }
|