tolltop 2.0.0 → 3.0.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/README.md +50 -48
- package/package.json +13 -16
- package/tolltop.css +52 -23
- package/tolltop.js +219 -0
- package/index.d.ts +0 -1
- package/index.js +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# tolltop
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tiny, dependency-free tooltips with smart edge-aware positioning. One attribute to add a
|
|
4
|
+
tooltip, one call to configure them all, no build step, and it works in all modern
|
|
5
|
+
browsers.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -10,65 +12,65 @@ npm install tolltop
|
|
|
10
12
|
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
|
-
### CDN (recommended)
|
|
14
|
-
|
|
15
15
|
```html
|
|
16
|
-
<
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### CSS import
|
|
20
|
-
|
|
21
|
-
```css
|
|
22
|
-
@import 'tolltop/tolltop.css';
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### JS bundler
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
import 'tolltop'
|
|
29
|
-
```
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/tolltop/tolltop.js"></script>
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### Then just add the attribute
|
|
34
|
-
|
|
35
|
-
```html
|
|
18
|
+
<!-- mark any element with the attribute -->
|
|
36
19
|
<button data-tooltip="Save your work">Save</button>
|
|
37
20
|
```
|
|
38
21
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- `::after` pseudo-element with `content: attr(data-tooltip)`
|
|
44
|
-
- CSS Anchor Positioning (`position-area: top` + `position-try-fallbacks: flip-block`) for viewport flipping
|
|
45
|
-
- `light-dark()` for automatic color inversion based on `color-scheme`
|
|
46
|
-
- `:hover` for show/hide (works as tap on mobile)
|
|
22
|
+
Tooltips show on hover and on keyboard focus, and hide on `Esc`. The script injects its own
|
|
23
|
+
styles, so loading `tolltop.css` is optional. Link it (before the script) only if you want
|
|
24
|
+
to edit the CSS directly:
|
|
47
25
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```css
|
|
53
|
-
:root {
|
|
54
|
-
--tt-bg: #1e3a5f;
|
|
55
|
-
--tt-color: #fff;
|
|
56
|
-
--tt-radius: 8px;
|
|
57
|
-
--tt-font-size: 14px;
|
|
58
|
-
--tt-line-height: 1.5;
|
|
59
|
-
}
|
|
26
|
+
```html
|
|
27
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tolltop/tolltop.css">
|
|
28
|
+
<script src="https://cdn.jsdelivr.net/npm/tolltop/tolltop.js"></script>
|
|
60
29
|
```
|
|
61
30
|
|
|
62
|
-
##
|
|
31
|
+
## Configuration
|
|
63
32
|
|
|
64
|
-
|
|
33
|
+
Style and behavior are global. Call `tolltop()` once with any subset of options (it merges
|
|
34
|
+
into the current config and applies immediately):
|
|
65
35
|
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
|
|
36
|
+
```js
|
|
37
|
+
tolltop({
|
|
38
|
+
bg: '#18181b',
|
|
39
|
+
color: '#e4e4e7',
|
|
40
|
+
radius: 6, // number = px, or any CSS length string
|
|
41
|
+
fontSize: 12, // same
|
|
42
|
+
padding: '6px 9px',
|
|
43
|
+
maxWidth: 240, // px
|
|
44
|
+
placement: 'auto', // 'auto' | 'top' | 'bottom'
|
|
45
|
+
gap: 10, // px between the trigger and the tooltip
|
|
46
|
+
edge: 24, // px min gap from each viewport side
|
|
47
|
+
});
|
|
69
48
|
```
|
|
70
49
|
|
|
71
|
-
|
|
50
|
+
| Option | Example | Default |
|
|
51
|
+
| ------------ | ---------------- | ---------- |
|
|
52
|
+
| `bg` | `'#1e3a5f'` | `#18181b` |
|
|
53
|
+
| `color` | `'#fff'` | `#e4e4e7` |
|
|
54
|
+
| `radius` | `10` or `'10px'` | `6` |
|
|
55
|
+
| `fontSize` | `14` or `'.9rem'`| `12` |
|
|
56
|
+
| `padding` | `'8px 12px'` | `6px 9px` |
|
|
57
|
+
| `maxWidth` | `320` | `240` |
|
|
58
|
+
| `placement` | `'top'` | `'auto'` |
|
|
59
|
+
| `gap` | `12` | `10` |
|
|
60
|
+
| `edge` | `16` | `24` |
|
|
61
|
+
|
|
62
|
+
Calling `tolltop()` with no arguments returns the current config. The only per-element
|
|
63
|
+
attribute is `data-tooltip="text"`, which marks an element and supplies its text.
|
|
64
|
+
|
|
65
|
+
## Positioning
|
|
66
|
+
|
|
67
|
+
- Shows above the trigger by default.
|
|
68
|
+
- Flips below if there isn't room above. Set `placement` to force a side (it still flips if
|
|
69
|
+
the forced side won't fit).
|
|
70
|
+
- Centers horizontally on the trigger, then shifts left or right just enough to stay on
|
|
71
|
+
screen, leaving `edge` px of room on each side (the vertical scrollbar is excluded).
|
|
72
|
+
- Long text is capped at the available width and wraps instead of running off screen.
|
|
73
|
+
- The arrow stays centered on the trigger even after the box shifts, and points toward it.
|
|
72
74
|
|
|
73
75
|
## License
|
|
74
76
|
|
package/package.json
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tolltop",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Tiny dependency-free tooltips with smart edge-aware positioning. One attribute, no build, works in all modern browsers.",
|
|
5
|
+
"main": "tolltop.js",
|
|
6
6
|
"style": "tolltop.css",
|
|
7
|
-
"
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "node build.js",
|
|
9
|
+
"prepublishOnly": "node build.js"
|
|
10
|
+
},
|
|
8
11
|
"files": [
|
|
9
|
-
"
|
|
10
|
-
"index.d.ts",
|
|
12
|
+
"tolltop.js",
|
|
11
13
|
"tolltop.css"
|
|
12
14
|
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "node build.js"
|
|
15
|
-
},
|
|
16
15
|
"keywords": [
|
|
17
16
|
"tooltip",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
17
|
+
"tooltips",
|
|
18
|
+
"vanilla",
|
|
19
|
+
"no-dependencies",
|
|
20
|
+
"positioning",
|
|
21
21
|
"lightweight"
|
|
22
22
|
],
|
|
23
23
|
"author": "David Miranda",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/
|
|
28
|
-
},
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"esbuild": "^0.27.4"
|
|
27
|
+
"url": "git+https://github.com/panphora/tolltop.git"
|
|
31
28
|
}
|
|
32
29
|
}
|
package/tolltop.css
CHANGED
|
@@ -1,31 +1,60 @@
|
|
|
1
|
-
|
|
1
|
+
.tolltop {
|
|
2
|
+
--tt-bg: #18181b;
|
|
3
|
+
--tt-color: #e4e4e7;
|
|
4
|
+
--tt-radius: 6px;
|
|
5
|
+
--tt-font-size: 12px;
|
|
6
|
+
--tt-padding: 6px 9px;
|
|
7
|
+
--tt-arrow: 6px;
|
|
8
|
+
--tt-arrow-x: 50%;
|
|
2
9
|
|
|
3
|
-
[data-tooltip]::after {
|
|
4
|
-
content: attr(data-tooltip);
|
|
5
10
|
position: fixed;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
margin: 0
|
|
11
|
-
padding: 5px 9px;
|
|
12
|
-
font-size: var(--tt-font-size, 12px);
|
|
13
|
-
font-weight: 500;
|
|
14
|
-
line-height: var(--tt-line-height, 1.4);
|
|
15
|
-
font-family: ui-monospace, monospace;
|
|
16
|
-
border-radius: var(--tt-radius, 5px);
|
|
17
|
-
max-width: 240px;
|
|
11
|
+
top: 0;
|
|
12
|
+
left: 0;
|
|
13
|
+
z-index: 2147483647;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
margin: 0;
|
|
18
16
|
width: max-content;
|
|
17
|
+
max-width: 240px;
|
|
18
|
+
padding: var(--tt-padding);
|
|
19
|
+
border-radius: var(--tt-radius);
|
|
20
|
+
background: var(--tt-bg);
|
|
21
|
+
color: var(--tt-color);
|
|
22
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
23
|
+
font-size: var(--tt-font-size);
|
|
24
|
+
font-weight: 500;
|
|
25
|
+
line-height: 1.4;
|
|
19
26
|
text-align: center;
|
|
27
|
+
white-space: normal;
|
|
28
|
+
overflow-wrap: break-word;
|
|
20
29
|
pointer-events: none;
|
|
21
|
-
|
|
22
|
-
background: var(--tt-bg, light-dark(#18181b, #fafafa));
|
|
23
|
-
color: var(--tt-color, light-dark(#e4e4e7, #18181b));
|
|
24
|
-
box-shadow:
|
|
25
|
-
0 2px 10px light-dark(rgba(0,0,0,.45), rgba(0,0,0,.12)),
|
|
26
|
-
0 0 0 1px light-dark(rgba(255,255,255,.06), rgba(0,0,0,.06));
|
|
30
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
|
|
27
31
|
opacity: 0;
|
|
28
|
-
|
|
32
|
+
visibility: hidden;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
[data-
|
|
35
|
+
.tolltop[data-show] {
|
|
36
|
+
opacity: 1;
|
|
37
|
+
visibility: visible;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.tolltop::after {
|
|
41
|
+
content: "";
|
|
42
|
+
position: absolute;
|
|
43
|
+
left: var(--tt-arrow-x);
|
|
44
|
+
transform: translateX(-50%);
|
|
45
|
+
width: 0;
|
|
46
|
+
height: 0;
|
|
47
|
+
border: var(--tt-arrow) solid transparent;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tolltop[data-placement="top"]::after {
|
|
51
|
+
top: 100%;
|
|
52
|
+
border-bottom-width: 0;
|
|
53
|
+
border-top-color: var(--tt-bg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tolltop[data-placement="bottom"]::after {
|
|
57
|
+
bottom: 100%;
|
|
58
|
+
border-top-width: 0;
|
|
59
|
+
border-bottom-color: var(--tt-bg);
|
|
60
|
+
}
|
package/tolltop.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* tolltop v3.0.1
|
|
3
|
+
* Tiny edge-aware tooltips with smart positioning. One attribute, one config call.
|
|
4
|
+
* MIT License · https://github.com/panphora/tolltop
|
|
5
|
+
*/
|
|
6
|
+
(() => {
|
|
7
|
+
'use strict';
|
|
8
|
+
if (window.__tolltop) return;
|
|
9
|
+
window.__tolltop = true;
|
|
10
|
+
|
|
11
|
+
const VEDGE = 8;
|
|
12
|
+
const TIP_ID = 'tolltop-tip';
|
|
13
|
+
|
|
14
|
+
// Baseline styles, injected only if tolltop.css isn't already on the page.
|
|
15
|
+
// Generated from tolltop.css by build.js; do not edit by hand. Run `npm run build`.
|
|
16
|
+
const CSS = `.tolltop{--tt-bg:#18181b;--tt-color:#e4e4e7;--tt-radius:6px;--tt-font-size:12px;--tt-padding:6px 9px;--tt-arrow:6px;--tt-arrow-x:50%;position:fixed;top:0;left:0;z-index:2147483647;box-sizing:border-box;margin:0;width:max-content;max-width:240px;padding:var(--tt-padding);border-radius:var(--tt-radius);background:var(--tt-bg);color:var(--tt-color);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:var(--tt-font-size);font-weight:500;line-height:1.4;text-align:center;white-space:normal;overflow-wrap:break-word;pointer-events:none;box-shadow:0 2px 12px rgba(0,0,0,0.3);opacity:0;visibility:hidden;}.tolltop[data-show]{opacity:1;visibility:visible;}.tolltop::after{content:"";position:absolute;left:var(--tt-arrow-x);transform:translateX(-50%);width:0;height:0;border:var(--tt-arrow) solid transparent;}.tolltop[data-placement="top"]::after{top:100%;border-bottom-width:0;border-top-color:var(--tt-bg);}.tolltop[data-placement="bottom"]::after{bottom:100%;border-top-width:0;border-bottom-color:var(--tt-bg);}`;
|
|
17
|
+
|
|
18
|
+
const cfg = {
|
|
19
|
+
bg: null,
|
|
20
|
+
color: null,
|
|
21
|
+
radius: null,
|
|
22
|
+
fontSize: null,
|
|
23
|
+
padding: null,
|
|
24
|
+
maxWidth: 240,
|
|
25
|
+
placement: 'auto',
|
|
26
|
+
gap: 10,
|
|
27
|
+
edge: 24,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let tip = null;
|
|
31
|
+
let active = null;
|
|
32
|
+
let activePrevAria = null;
|
|
33
|
+
|
|
34
|
+
const clamp = (n, lo, hi) => Math.min(Math.max(n, lo), hi);
|
|
35
|
+
const isNumStr = (v) => /^-?\d*\.?\d+$/.test(String(v).trim());
|
|
36
|
+
const toLen = (v) =>
|
|
37
|
+
v == null || v === '' ? null : typeof v === 'number' ? v + 'px' : isNumStr(v) ? v.trim() + 'px' : v.trim();
|
|
38
|
+
const numOr = (v, fallback) => (isFinite(Number(v)) ? Number(v) : fallback);
|
|
39
|
+
|
|
40
|
+
function setVar(name, value) {
|
|
41
|
+
if (value == null || value === '') tip.style.removeProperty(name);
|
|
42
|
+
else tip.style.setProperty(name, value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function applyConfig() {
|
|
46
|
+
if (!tip) return;
|
|
47
|
+
setVar('--tt-bg', cfg.bg);
|
|
48
|
+
setVar('--tt-color', cfg.color);
|
|
49
|
+
setVar('--tt-radius', toLen(cfg.radius));
|
|
50
|
+
setVar('--tt-font-size', toLen(cfg.fontSize));
|
|
51
|
+
setVar('--tt-padding', cfg.padding);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ensureTip() {
|
|
55
|
+
if (tip) return tip;
|
|
56
|
+
tip = document.createElement('div');
|
|
57
|
+
tip.className = 'tolltop';
|
|
58
|
+
tip.id = TIP_ID;
|
|
59
|
+
tip.setAttribute('role', 'tooltip');
|
|
60
|
+
document.body.appendChild(tip);
|
|
61
|
+
if (getComputedStyle(tip).position !== 'fixed') {
|
|
62
|
+
const style = document.createElement('style');
|
|
63
|
+
style.setAttribute('data-tolltop', '');
|
|
64
|
+
style.textContent = CSS;
|
|
65
|
+
document.head.insertBefore(style, document.head.firstChild);
|
|
66
|
+
}
|
|
67
|
+
applyConfig();
|
|
68
|
+
return tip;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function position(el) {
|
|
72
|
+
const rect = el.getBoundingClientRect();
|
|
73
|
+
// clientWidth/Height exclude the scrollbar; innerWidth would not.
|
|
74
|
+
const vw = document.documentElement.clientWidth;
|
|
75
|
+
const vh = document.documentElement.clientHeight;
|
|
76
|
+
const gap = Math.max(0, numOr(cfg.gap, 10));
|
|
77
|
+
const edge = Math.max(0, numOr(cfg.edge, 24));
|
|
78
|
+
|
|
79
|
+
const maxW = Math.min(numOr(cfg.maxWidth, 240), vw - edge * 2);
|
|
80
|
+
tip.style.maxWidth = maxW + 'px';
|
|
81
|
+
|
|
82
|
+
const t = tip.getBoundingClientRect();
|
|
83
|
+
|
|
84
|
+
const pref = cfg.placement;
|
|
85
|
+
const topY = rect.top - t.height - gap;
|
|
86
|
+
const botY = rect.bottom + gap;
|
|
87
|
+
const topFits = topY >= VEDGE;
|
|
88
|
+
const botFits = botY + t.height <= vh - VEDGE;
|
|
89
|
+
let placement;
|
|
90
|
+
if (pref === 'bottom') placement = botFits || !topFits ? 'bottom' : 'top';
|
|
91
|
+
else if (pref === 'top') placement = topFits || !botFits ? 'top' : 'bottom';
|
|
92
|
+
else placement = topFits ? 'top' : 'bottom';
|
|
93
|
+
// When the tip is taller than the viewport, pin to the top edge so its top stays visible.
|
|
94
|
+
const y =
|
|
95
|
+
t.height >= vh - VEDGE * 2 ? VEDGE : clamp(placement === 'top' ? topY : botY, VEDGE, vh - t.height - VEDGE);
|
|
96
|
+
|
|
97
|
+
const centerX = rect.left + rect.width / 2;
|
|
98
|
+
const x = clamp(centerX - t.width / 2, edge, vw - t.width - edge);
|
|
99
|
+
|
|
100
|
+
const radius = parseFloat(getComputedStyle(tip).borderTopLeftRadius) || 6;
|
|
101
|
+
const arrow = parseFloat(getComputedStyle(tip).getPropertyValue('--tt-arrow')) || 6;
|
|
102
|
+
const arrowX = clamp(centerX - x, radius + arrow, t.width - radius - arrow);
|
|
103
|
+
|
|
104
|
+
tip.style.left = x + 'px';
|
|
105
|
+
tip.style.top = y + 'px';
|
|
106
|
+
tip.style.setProperty('--tt-arrow-x', arrowX + 'px');
|
|
107
|
+
tip.setAttribute('data-placement', placement);
|
|
108
|
+
tip.setAttribute('data-show', '');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function restoreAria() {
|
|
112
|
+
if (!active) return;
|
|
113
|
+
if (activePrevAria == null) active.removeAttribute('aria-describedby');
|
|
114
|
+
else active.setAttribute('aria-describedby', activePrevAria);
|
|
115
|
+
activePrevAria = null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function show(el) {
|
|
119
|
+
const text = el.getAttribute('data-tooltip');
|
|
120
|
+
if (!text) return;
|
|
121
|
+
ensureTip();
|
|
122
|
+
if (active !== el) {
|
|
123
|
+
if (active) restoreAria();
|
|
124
|
+
active = el;
|
|
125
|
+
activePrevAria = el.getAttribute('aria-describedby');
|
|
126
|
+
const ids = activePrevAria ? activePrevAria.split(/\s+/) : [];
|
|
127
|
+
if (ids.indexOf(TIP_ID) === -1) ids.push(TIP_ID);
|
|
128
|
+
el.setAttribute('aria-describedby', ids.join(' '));
|
|
129
|
+
}
|
|
130
|
+
tip.textContent = text;
|
|
131
|
+
position(el);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function hide() {
|
|
135
|
+
if (!active) return;
|
|
136
|
+
restoreAria();
|
|
137
|
+
active = null;
|
|
138
|
+
if (tip) tip.removeAttribute('data-show');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// True when a scrollable/overflow ancestor fully clips the element out of its box.
|
|
142
|
+
function clippedOut(el) {
|
|
143
|
+
const pos = getComputedStyle(el).position;
|
|
144
|
+
if (pos === 'fixed') return false; // viewport-positioned, not clipped by scroll ancestors
|
|
145
|
+
const r = el.getBoundingClientRect();
|
|
146
|
+
// An absolute element isn't clipped by overflow ancestors below its containing block.
|
|
147
|
+
const cb = pos === 'absolute' ? el.offsetParent : null;
|
|
148
|
+
let node = el.parentElement;
|
|
149
|
+
let clips = !cb;
|
|
150
|
+
while (node && node !== document.documentElement) {
|
|
151
|
+
if (!clips && node === cb) clips = true;
|
|
152
|
+
// Skip non-rendered/zero-box ancestors (e.g. display:contents) that don't actually clip.
|
|
153
|
+
if (clips && (node.clientWidth || node.clientHeight)) {
|
|
154
|
+
const o = getComputedStyle(node);
|
|
155
|
+
if (/auto|scroll|hidden|clip/.test(o.overflow + o.overflowX + o.overflowY)) {
|
|
156
|
+
const c = node.getBoundingClientRect();
|
|
157
|
+
const left = c.left + node.clientLeft; // padding box: where overflow actually clips
|
|
158
|
+
const top = c.top + node.clientTop;
|
|
159
|
+
if (r.bottom <= top || r.top >= top + node.clientHeight || r.right <= left || r.left >= left + node.clientWidth) return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
node = node.parentElement;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function reposition() {
|
|
168
|
+
if (!active) return;
|
|
169
|
+
// A fixed tip isn't clipped by an ancestor's overflow, so hide it ourselves when the
|
|
170
|
+
// trigger is removed, scrolled out of the viewport, or clipped out of a scroll container.
|
|
171
|
+
if (!active.isConnected) {
|
|
172
|
+
hide();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const r = active.getBoundingClientRect();
|
|
176
|
+
const vw = document.documentElement.clientWidth;
|
|
177
|
+
const vh = document.documentElement.clientHeight;
|
|
178
|
+
if (r.bottom < 0 || r.top > vh || r.right < 0 || r.left > vw || clippedOut(active)) {
|
|
179
|
+
hide();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
position(active);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function trigger(node) {
|
|
186
|
+
return node && node.closest ? node.closest('[data-tooltip]') : null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
document.addEventListener('pointerover', (e) => {
|
|
190
|
+
const el = trigger(e.target);
|
|
191
|
+
if (el && el !== active) show(el);
|
|
192
|
+
});
|
|
193
|
+
document.addEventListener('pointerout', (e) => {
|
|
194
|
+
if (active && (!e.relatedTarget || !active.contains(e.relatedTarget))) hide();
|
|
195
|
+
});
|
|
196
|
+
document.addEventListener('focusin', (e) => {
|
|
197
|
+
const el = trigger(e.target);
|
|
198
|
+
if (el) show(el);
|
|
199
|
+
});
|
|
200
|
+
document.addEventListener('focusout', hide);
|
|
201
|
+
document.addEventListener('keydown', (e) => {
|
|
202
|
+
if (e.key === 'Escape') hide();
|
|
203
|
+
});
|
|
204
|
+
window.addEventListener('scroll', reposition, true);
|
|
205
|
+
window.addEventListener('resize', reposition);
|
|
206
|
+
|
|
207
|
+
window.tolltop = function (opts) {
|
|
208
|
+
if (opts && typeof opts === 'object') {
|
|
209
|
+
for (const k in opts) {
|
|
210
|
+
if (Object.prototype.hasOwnProperty.call(cfg, k)) cfg[k] = opts[k];
|
|
211
|
+
}
|
|
212
|
+
applyConfig();
|
|
213
|
+
if (active) position(active);
|
|
214
|
+
}
|
|
215
|
+
const out = {};
|
|
216
|
+
for (const k in cfg) out[k] = cfg[k];
|
|
217
|
+
return out;
|
|
218
|
+
};
|
|
219
|
+
})();
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
declare module 'tolltop' {}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(()=>{(()=>{let t=document.createElement("style");t.textContent="[data-tooltip]:hover{anchor-name:--tt}[data-tooltip]:after{content:attr(data-tooltip);position:fixed;position-anchor:--tt;position-area:top;position-try-fallbacks:flip-block;inset:auto;margin:0 0 8px;padding:5px 9px;font-size:var(--tt-font-size, 12px);font-weight:500;line-height:var(--tt-line-height, 1.4);font-family:ui-monospace,monospace;border-radius:var(--tt-radius, 5px);max-width:240px;width:max-content;text-align:center;pointer-events:none;z-index:2147483647;background:var(--tt-bg, light-dark(#18181b, #fafafa));color:var(--tt-color, light-dark(#e4e4e7, #18181b));box-shadow:0 2px 10px light-dark(rgba(0,0,0,.45),rgba(0,0,0,.12)),0 0 0 1px light-dark(rgba(255,255,255,.06),rgba(0,0,0,.06));opacity:0;transition:opacity .1s ease}[data-tooltip]:hover:after{opacity:1}",document.head.appendChild(t)})();})();
|