@data-slot/tooltip 0.2.86 → 0.2.87
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 +58 -64
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +18 -8
- package/dist/index.d.ts +18 -8
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -59,11 +59,16 @@ const tooltip = createTooltip(element, {
|
|
|
59
59
|
|--------|------|---------|-------------|
|
|
60
60
|
| `delay` | `number` | `300` | Delay before showing tooltip (ms) |
|
|
61
61
|
| `skipDelayDuration` | `number` | `300` | Duration to skip delay after closing (ms). Set to `0` to disable warm-up. |
|
|
62
|
-
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` |
|
|
63
|
-
| `align` | `"start" \| "center" \| "end"` | `"center"` |
|
|
62
|
+
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` | Preferred side relative to trigger |
|
|
63
|
+
| `align` | `"start" \| "center" \| "end"` | `"center"` | Preferred alignment |
|
|
64
|
+
| `sideOffset` | `number` | `4` | Distance from trigger in pixels |
|
|
65
|
+
| `alignOffset` | `number` | `0` | Offset from alignment edge in pixels |
|
|
66
|
+
| `avoidCollisions` | `boolean` | `true` | Flip/shift to stay in viewport |
|
|
67
|
+
| `collisionPadding` | `number` | `8` | Viewport edge padding in pixels |
|
|
68
|
+
| `portal` | `boolean` | `true` | Portal content to `document.body` while open |
|
|
64
69
|
| `onOpenChange` | `(open: boolean) => void` | `undefined` | Callback when visibility changes |
|
|
65
70
|
|
|
66
|
-
**Note:** `side` and `align` are resolved
|
|
71
|
+
**Note:** `side` and `align` are preferred placement inputs resolved at bind time. With collision handling enabled, computed `data-side` can differ at runtime.
|
|
67
72
|
|
|
68
73
|
### Data Attributes
|
|
69
74
|
|
|
@@ -75,6 +80,11 @@ Options can also be set via data attributes. JS options take precedence.
|
|
|
75
80
|
| `data-skip-delay-duration` | number | `300` | Duration to skip delay after closing (ms) |
|
|
76
81
|
| `data-side` | string | `"top"` | Side relative to trigger (checked on content first, then root) |
|
|
77
82
|
| `data-align` | string | `"center"` | Alignment along the side (checked on content first, then root) |
|
|
83
|
+
| `data-side-offset` | number | `4` | Distance from trigger (px) |
|
|
84
|
+
| `data-align-offset` | number | `0` | Alignment edge offset (px) |
|
|
85
|
+
| `data-avoid-collisions` | boolean | `true` | Collision handling |
|
|
86
|
+
| `data-collision-padding` | number | `8` | Viewport edge padding (px) |
|
|
87
|
+
| `data-portal` | boolean | `true` | Portal while open |
|
|
78
88
|
|
|
79
89
|
```html
|
|
80
90
|
<!-- Tooltip with faster response -->
|
|
@@ -87,8 +97,14 @@ Options can also be set via data attributes. JS options take precedence.
|
|
|
87
97
|
...
|
|
88
98
|
</div>
|
|
89
99
|
|
|
90
|
-
<!-- Side/align on content element -->
|
|
91
|
-
<div
|
|
100
|
+
<!-- Side/align and offsets on content element -->
|
|
101
|
+
<div
|
|
102
|
+
data-slot="tooltip-content"
|
|
103
|
+
data-side="bottom"
|
|
104
|
+
data-align="start"
|
|
105
|
+
data-side-offset="8"
|
|
106
|
+
data-align-offset="4"
|
|
107
|
+
>
|
|
92
108
|
...
|
|
93
109
|
</div>
|
|
94
110
|
```
|
|
@@ -111,7 +127,28 @@ Options can also be set via data attributes. JS options take precedence.
|
|
|
111
127
|
</div>
|
|
112
128
|
```
|
|
113
129
|
|
|
114
|
-
|
|
130
|
+
### Required Slots
|
|
131
|
+
|
|
132
|
+
- `tooltip-trigger`
|
|
133
|
+
- `tooltip-content`
|
|
134
|
+
|
|
135
|
+
### Optional Slots
|
|
136
|
+
|
|
137
|
+
- `tooltip-positioner` - Optional authored positioning wrapper
|
|
138
|
+
- `tooltip-portal` - Optional authored portal wrapper that can contain `tooltip-positioner`
|
|
139
|
+
|
|
140
|
+
### Composed Portal Markup (Optional)
|
|
141
|
+
|
|
142
|
+
```html
|
|
143
|
+
<div data-slot="tooltip">
|
|
144
|
+
<button data-slot="tooltip-trigger">Trigger</button>
|
|
145
|
+
<div data-slot="tooltip-portal">
|
|
146
|
+
<div data-slot="tooltip-positioner">
|
|
147
|
+
<div data-slot="tooltip-content">Content</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
```
|
|
115
152
|
|
|
116
153
|
### Output Attributes
|
|
117
154
|
|
|
@@ -120,7 +157,9 @@ The component sets these attributes automatically:
|
|
|
120
157
|
| Element | Attribute | Values |
|
|
121
158
|
|---------|-----------|--------|
|
|
122
159
|
| Root | `data-state` | `"open"` \| `"closed"` |
|
|
160
|
+
| Root | `data-open` / `data-closed` | Present when matching state |
|
|
123
161
|
| Content | `data-state` | `"open"` \| `"closed"` |
|
|
162
|
+
| Content | `data-open` / `data-closed` | Present when matching state |
|
|
124
163
|
| Content | `data-side` | `"top"` \| `"right"` \| `"bottom"` \| `"left"` |
|
|
125
164
|
| Content | `data-align` | `"start"` \| `"center"` \| `"end"` |
|
|
126
165
|
| Content | `role` | `"tooltip"` |
|
|
@@ -129,98 +168,53 @@ The component sets these attributes automatically:
|
|
|
129
168
|
|
|
130
169
|
## Styling
|
|
131
170
|
|
|
132
|
-
|
|
171
|
+
Position is computed in JavaScript and applied to the positioner as `position: absolute` + `transform: translate3d(...)`.
|
|
172
|
+
By default, content is portaled to `document.body` while open.
|
|
173
|
+
Use `data-open` / `data-closed`, `data-side`, and `data-align` for styling and animations.
|
|
133
174
|
|
|
134
175
|
### Recommended CSS
|
|
135
176
|
|
|
136
177
|
The visibility transition trick keeps the tooltip visible during fade-out, then becomes non-focusable after the transition completes—no JS timers needed.
|
|
137
178
|
|
|
138
179
|
```css
|
|
139
|
-
/* Positioning */
|
|
140
|
-
[data-slot="tooltip"] {
|
|
141
|
-
position: relative;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
180
|
[data-slot="tooltip-content"] {
|
|
145
|
-
position: absolute;
|
|
146
181
|
white-space: nowrap;
|
|
147
|
-
/* Hidden by default */
|
|
148
182
|
opacity: 0;
|
|
149
183
|
visibility: hidden;
|
|
150
184
|
pointer-events: none;
|
|
151
|
-
|
|
152
|
-
transition: opacity 0.15s, visibility 0s linear 0.15s;
|
|
185
|
+
transform-origin: center;
|
|
186
|
+
transition: opacity 0.15s ease, visibility 0s linear 0.15s;
|
|
153
187
|
}
|
|
154
188
|
|
|
155
|
-
|
|
156
|
-
[data-slot="tooltip"][data-state="open"] [data-slot="tooltip-content"] {
|
|
189
|
+
[data-slot="tooltip-content"][data-open] {
|
|
157
190
|
opacity: 1;
|
|
158
191
|
visibility: visible;
|
|
159
192
|
pointer-events: auto;
|
|
160
193
|
transition-delay: 0s;
|
|
161
194
|
}
|
|
162
195
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
bottom: 100%;
|
|
166
|
-
left: 50%;
|
|
167
|
-
transform: translateX(-50%);
|
|
168
|
-
margin-bottom: 8px;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
[data-slot="tooltip-content"][data-side="bottom"] {
|
|
172
|
-
top: 100%;
|
|
173
|
-
left: 50%;
|
|
174
|
-
transform: translateX(-50%);
|
|
175
|
-
margin-top: 8px;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
[data-slot="tooltip-content"][data-side="left"] {
|
|
179
|
-
right: 100%;
|
|
180
|
-
top: 50%;
|
|
181
|
-
transform: translateY(-50%);
|
|
182
|
-
margin-right: 8px;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
[data-slot="tooltip-content"][data-side="right"] {
|
|
186
|
-
left: 100%;
|
|
187
|
-
top: 50%;
|
|
188
|
-
transform: translateY(-50%);
|
|
189
|
-
margin-left: 8px;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/* Alignment (example for top/bottom sides) */
|
|
193
|
-
[data-slot="tooltip-content"][data-side="top"][data-align="start"],
|
|
194
|
-
[data-slot="tooltip-content"][data-side="bottom"][data-align="start"] {
|
|
195
|
-
left: 0;
|
|
196
|
-
transform: none;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
[data-slot="tooltip-content"][data-side="top"][data-align="end"],
|
|
200
|
-
[data-slot="tooltip-content"][data-side="bottom"][data-align="end"] {
|
|
201
|
-
left: auto;
|
|
202
|
-
right: 0;
|
|
203
|
-
transform: none;
|
|
196
|
+
[data-slot="tooltip-content"][data-closed] {
|
|
197
|
+
pointer-events: none;
|
|
204
198
|
}
|
|
205
199
|
```
|
|
206
200
|
|
|
207
201
|
### Tailwind Example
|
|
208
202
|
|
|
209
|
-
Use
|
|
203
|
+
Use root/content data attributes for open-state styling:
|
|
210
204
|
|
|
211
205
|
```html
|
|
212
|
-
<div data-slot="tooltip"
|
|
206
|
+
<div data-slot="tooltip">
|
|
213
207
|
<button data-slot="tooltip-trigger">
|
|
214
208
|
Hover me
|
|
215
209
|
</button>
|
|
216
210
|
<div
|
|
217
211
|
data-slot="tooltip-content"
|
|
218
212
|
data-side="top"
|
|
219
|
-
class="
|
|
213
|
+
class="px-2 py-1
|
|
220
214
|
bg-gray-900 text-white text-sm rounded
|
|
221
215
|
opacity-0 pointer-events-none transition-opacity duration-150
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
data-[open]:opacity-100
|
|
217
|
+
data-[open]:pointer-events-auto"
|
|
224
218
|
>
|
|
225
219
|
Tooltip text
|
|
226
220
|
</div>
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@data-slot/core`),t=0;const n=[`top`,`right`,`bottom`,`left`],r=[`start`,`center`,`end`];function i(i,a={}){let o=(0,e.getPart)(i,`tooltip-trigger`),s=(0,e.getPart)(i,`tooltip-content`);if(!o||!s)throw Error(`Tooltip requires trigger and content slots`);let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@data-slot/core`),t=0;const n=[`top`,`right`,`bottom`,`left`],r=[`start`,`center`,`end`];function i(i,a={}){let o=(0,e.getPart)(i,`tooltip-trigger`),s=(0,e.getPart)(i,`tooltip-content`),c=(0,e.getPart)(i,`tooltip-positioner`),l=c&&s&&c.contains(s)?c:null,u=(0,e.getPart)(i,`tooltip-portal`),d=u&&l&&u.contains(l)?u:null;if(!o||!s)throw Error(`Tooltip requires trigger and content slots`);let f=a.delay??(0,e.getDataNumber)(i,`delay`)??300,p=a.skipDelayDuration??(0,e.getDataNumber)(i,`skipDelayDuration`)??300,m=a.onOpenChange,h=a.portal??(0,e.getDataBool)(s,`portal`)??(0,e.getDataBool)(i,`portal`)??!0,g=a.side??(0,e.getDataEnum)(s,`side`,n)??(0,e.getDataEnum)(i,`side`,n)??`top`,_=a.align??(0,e.getDataEnum)(s,`align`,r)??(0,e.getDataEnum)(i,`align`,r)??`center`,v=a.sideOffset??(0,e.getDataNumber)(s,`sideOffset`)??(0,e.getDataNumber)(i,`sideOffset`)??4,y=a.alignOffset??(0,e.getDataNumber)(s,`alignOffset`)??(0,e.getDataNumber)(i,`alignOffset`)??0,b=a.avoidCollisions??(0,e.getDataBool)(s,`avoidCollisions`)??(0,e.getDataBool)(i,`avoidCollisions`)??!0,x=a.collisionPadding??(0,e.getDataNumber)(s,`collisionPadding`)??(0,e.getDataNumber)(i,`collisionPadding`)??8,S=!1,C=!1,w=!1,T=null,E=[],D=(0,e.createPortalLifecycle)({content:s,root:i,enabled:h,wrapperSlot:l?void 0:`tooltip-positioner`,container:l??void 0,mountTarget:l?d??l:void 0}),O=(0,e.ensureId)(s,`tooltip-content`);s.setAttribute(`role`,`tooltip`),s.setAttribute(`data-side`,g),s.setAttribute(`data-align`,_);let k=e=>{let t=D.container;if(i.setAttribute(`data-state`,e),s.setAttribute(`data-state`,e),t!==s&&t.setAttribute(`data-state`,e),e===`open`){i.setAttribute(`data-open`,``),s.setAttribute(`data-open`,``),t!==s&&t.setAttribute(`data-open`,``),i.removeAttribute(`data-closed`),s.removeAttribute(`data-closed`),t!==s&&t.removeAttribute(`data-closed`);return}i.setAttribute(`data-closed`,``),s.setAttribute(`data-closed`,``),t!==s&&t.setAttribute(`data-closed`,``),i.removeAttribute(`data-open`),s.removeAttribute(`data-open`),t!==s&&t.removeAttribute(`data-open`)},A=()=>{let t=D.container,n=i.ownerDocument.defaultView??window,r=(0,e.computeFloatingPosition)({anchorRect:o.getBoundingClientRect(),contentRect:s.getBoundingClientRect(),side:g,align:_,sideOffset:v,alignOffset:y,avoidCollisions:b,collisionPadding:x});t.style.position=`absolute`,t.style.top=`0px`,t.style.left=`0px`,t.style.transform=`translate3d(${r.x+n.scrollX}px, ${r.y+n.scrollY}px, 0)`,t.style.willChange=`transform`,t.style.margin=`0`,s.setAttribute(`data-side`,r.side),s.setAttribute(`data-align`,r.align),t!==s&&(t.setAttribute(`data-side`,r.side),t.setAttribute(`data-align`,r.align))},j=(0,e.createPresenceLifecycle)({element:s,onExitComplete:()=>{w||(D.restore(),s.hidden=!0)}}),M=(0,e.createPositionSync)({observedElements:[o,s],isActive:()=>S,ancestorScroll:!1,onUpdate:A}),N=()=>o.hasAttribute(`disabled`)||o.getAttribute(`aria-disabled`)===`true`,P=(n,r)=>{S!==n&&(!n&&S&&p>0&&(t=Date.now()+p),S=n,k(S?`open`:`closed`),S?(o.setAttribute(`aria-describedby`,O),s.setAttribute(`aria-hidden`,`false`),D.mount(),s.hidden=!1,j.enter(),A(),M.start(),M.update()):(o.removeAttribute(`aria-describedby`),s.setAttribute(`aria-hidden`,`true`),j.exit(),M.stop()),(0,e.emit)(i,`tooltip:change`,{open:S,trigger:o,content:s,reason:r}),m?.(S))},F=e=>{if(T&&=(clearTimeout(T),null),Date.now()<t){P(!0,e);return}T=setTimeout(()=>{P(!0,e),T=null},f)},I=e=>{T&&=(clearTimeout(T),null),P(!1,e)};return s.hidden=!0,s.setAttribute(`aria-hidden`,`true`),k(`closed`),E.push((0,e.on)(o,`pointerenter`,e=>{e.pointerType!==`touch`&&(N()||F(`pointer`))}),(0,e.on)(o,`pointerleave`,e=>{if(e.pointerType===`touch`||C)return;let t=e.relatedTarget;t&&s.contains(t)||I(`pointer`)}),(0,e.on)(o,`focus`,()=>{C=!0,!N()&&F(`focus`)}),(0,e.on)(o,`blur`,()=>{C=!1,I(`blur`)})),E.push((0,e.on)(s,`pointerleave`,e=>{if(e.pointerType===`touch`||C)return;let t=e.relatedTarget;t&&o.contains(t)||I(`pointer`)})),E.push((0,e.on)(i,`tooltip:set`,e=>{let t=e.detail,n;if(t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`)if(n){if(N())return;T&&=(clearTimeout(T),null),P(!0,`api`)}else I(`api`)})),E.push((0,e.createDismissLayer)({root:i,isOpen:()=>S,onDismiss:()=>I(`escape`),closeOnClickOutside:!1,closeOnEscape:!0,preventEscapeDefault:!1})),{show:()=>{N()||(T&&=(clearTimeout(T),null),P(!0,`api`))},hide:()=>I(`api`),get isOpen(){return S},destroy:()=>{w=!0,T&&clearTimeout(T),M.stop(),j.cleanup(),D.cleanup(),E.forEach(e=>e()),E.length=0}}}const a=new WeakSet;function o(t=document){let n=[];for(let r of(0,e.getRoots)(t,`tooltip`))a.has(r)||(a.add(r),n.push(i(r)));return n}exports.create=o,exports.createTooltip=i;
|
package/dist/index.d.cts
CHANGED
|
@@ -7,10 +7,20 @@ interface TooltipOptions {
|
|
|
7
7
|
delay?: number;
|
|
8
8
|
/** Duration to skip delay after closing (ms). Set to 0 to disable warm-up. Default: 300 */
|
|
9
9
|
skipDelayDuration?: number;
|
|
10
|
-
/**
|
|
10
|
+
/** Preferred side of tooltip relative to trigger. Default: 'top'. */
|
|
11
11
|
side?: TooltipSide;
|
|
12
|
-
/**
|
|
12
|
+
/** Preferred alignment along the side. Default: 'center'. */
|
|
13
13
|
align?: TooltipAlign;
|
|
14
|
+
/** Distance from trigger in pixels. Default: 4 */
|
|
15
|
+
sideOffset?: number;
|
|
16
|
+
/** Alignment-axis offset in pixels. Default: 0 */
|
|
17
|
+
alignOffset?: number;
|
|
18
|
+
/** Enable collision handling. Default: true */
|
|
19
|
+
avoidCollisions?: boolean;
|
|
20
|
+
/** Viewport edge padding used by collision handling. Default: 8 */
|
|
21
|
+
collisionPadding?: number;
|
|
22
|
+
/** Portal content to body while open. Default: true */
|
|
23
|
+
portal?: boolean;
|
|
14
24
|
/** Callback when visibility changes */
|
|
15
25
|
onOpenChange?: (open: boolean) => void;
|
|
16
26
|
}
|
|
@@ -38,15 +48,15 @@ interface TooltipController {
|
|
|
38
48
|
* ```
|
|
39
49
|
*
|
|
40
50
|
* Data attributes (checked on content first, then root):
|
|
41
|
-
* - `data-side`: 'top' | 'right' | 'bottom' | 'left' (bind-time
|
|
42
|
-
* - `data-align`: 'start' | 'center' | 'end' (bind-time
|
|
51
|
+
* - `data-side`: 'top' | 'right' | 'bottom' | 'left' (bind-time preferred side)
|
|
52
|
+
* - `data-align`: 'start' | 'center' | 'end' (bind-time preferred align)
|
|
53
|
+
* - `data-side-offset`: number (px)
|
|
54
|
+
* - `data-align-offset`: number (px)
|
|
55
|
+
* - `data-avoid-collisions`: boolean
|
|
56
|
+
* - `data-collision-padding`: number (px)
|
|
43
57
|
* - `data-delay`: number (ms)
|
|
44
58
|
* - `data-skip-delay-duration`: number (ms)
|
|
45
59
|
*
|
|
46
|
-
* Note: side and align are resolved once at bind time. To change placement,
|
|
47
|
-
* destroy and recreate the tooltip with new options.
|
|
48
|
-
*
|
|
49
|
-
* This tooltip uses simple CSS positioning (not collision-aware).
|
|
50
60
|
* Opens on hover (non-touch) and focus. Touch devices: focus-only.
|
|
51
61
|
* Content is hoverable: moving pointer from trigger to content keeps it open.
|
|
52
62
|
* Tooltip stays open while trigger has focus, even if pointer leaves.
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,20 @@ interface TooltipOptions {
|
|
|
7
7
|
delay?: number;
|
|
8
8
|
/** Duration to skip delay after closing (ms). Set to 0 to disable warm-up. Default: 300 */
|
|
9
9
|
skipDelayDuration?: number;
|
|
10
|
-
/**
|
|
10
|
+
/** Preferred side of tooltip relative to trigger. Default: 'top'. */
|
|
11
11
|
side?: TooltipSide;
|
|
12
|
-
/**
|
|
12
|
+
/** Preferred alignment along the side. Default: 'center'. */
|
|
13
13
|
align?: TooltipAlign;
|
|
14
|
+
/** Distance from trigger in pixels. Default: 4 */
|
|
15
|
+
sideOffset?: number;
|
|
16
|
+
/** Alignment-axis offset in pixels. Default: 0 */
|
|
17
|
+
alignOffset?: number;
|
|
18
|
+
/** Enable collision handling. Default: true */
|
|
19
|
+
avoidCollisions?: boolean;
|
|
20
|
+
/** Viewport edge padding used by collision handling. Default: 8 */
|
|
21
|
+
collisionPadding?: number;
|
|
22
|
+
/** Portal content to body while open. Default: true */
|
|
23
|
+
portal?: boolean;
|
|
14
24
|
/** Callback when visibility changes */
|
|
15
25
|
onOpenChange?: (open: boolean) => void;
|
|
16
26
|
}
|
|
@@ -38,15 +48,15 @@ interface TooltipController {
|
|
|
38
48
|
* ```
|
|
39
49
|
*
|
|
40
50
|
* Data attributes (checked on content first, then root):
|
|
41
|
-
* - `data-side`: 'top' | 'right' | 'bottom' | 'left' (bind-time
|
|
42
|
-
* - `data-align`: 'start' | 'center' | 'end' (bind-time
|
|
51
|
+
* - `data-side`: 'top' | 'right' | 'bottom' | 'left' (bind-time preferred side)
|
|
52
|
+
* - `data-align`: 'start' | 'center' | 'end' (bind-time preferred align)
|
|
53
|
+
* - `data-side-offset`: number (px)
|
|
54
|
+
* - `data-align-offset`: number (px)
|
|
55
|
+
* - `data-avoid-collisions`: boolean
|
|
56
|
+
* - `data-collision-padding`: number (px)
|
|
43
57
|
* - `data-delay`: number (ms)
|
|
44
58
|
* - `data-skip-delay-duration`: number (ms)
|
|
45
59
|
*
|
|
46
|
-
* Note: side and align are resolved once at bind time. To change placement,
|
|
47
|
-
* destroy and recreate the tooltip with new options.
|
|
48
|
-
*
|
|
49
|
-
* This tooltip uses simple CSS positioning (not collision-aware).
|
|
50
60
|
* Opens on hover (non-touch) and focus. Touch devices: focus-only.
|
|
51
61
|
* Content is hoverable: moving pointer from trigger to content keeps it open.
|
|
52
62
|
* Tooltip stays open while trigger has focus, even if pointer leaves.
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{computeFloatingPosition as e,createDismissLayer as t,createPortalLifecycle as n,createPositionSync as r,createPresenceLifecycle as i,emit as a,ensureId as o,getDataBool as s,getDataEnum as c,getDataNumber as l,getPart as u,getRoots as d,on as f}from"@data-slot/core";let p=0;const m=[`top`,`right`,`bottom`,`left`],h=[`start`,`center`,`end`];function g(d,g={}){let _=u(d,`tooltip-trigger`),v=u(d,`tooltip-content`),y=u(d,`tooltip-positioner`),b=y&&v&&y.contains(v)?y:null,x=u(d,`tooltip-portal`),S=x&&b&&x.contains(b)?x:null;if(!_||!v)throw Error(`Tooltip requires trigger and content slots`);let C=g.delay??l(d,`delay`)??300,w=g.skipDelayDuration??l(d,`skipDelayDuration`)??300,T=g.onOpenChange,E=g.portal??s(v,`portal`)??s(d,`portal`)??!0,D=g.side??c(v,`side`,m)??c(d,`side`,m)??`top`,O=g.align??c(v,`align`,h)??c(d,`align`,h)??`center`,k=g.sideOffset??l(v,`sideOffset`)??l(d,`sideOffset`)??4,A=g.alignOffset??l(v,`alignOffset`)??l(d,`alignOffset`)??0,j=g.avoidCollisions??s(v,`avoidCollisions`)??s(d,`avoidCollisions`)??!0,M=g.collisionPadding??l(v,`collisionPadding`)??l(d,`collisionPadding`)??8,N=!1,P=!1,F=!1,I=null,L=[],R=n({content:v,root:d,enabled:E,wrapperSlot:b?void 0:`tooltip-positioner`,container:b??void 0,mountTarget:b?S??b:void 0}),z=o(v,`tooltip-content`);v.setAttribute(`role`,`tooltip`),v.setAttribute(`data-side`,D),v.setAttribute(`data-align`,O);let B=e=>{let t=R.container;if(d.setAttribute(`data-state`,e),v.setAttribute(`data-state`,e),t!==v&&t.setAttribute(`data-state`,e),e===`open`){d.setAttribute(`data-open`,``),v.setAttribute(`data-open`,``),t!==v&&t.setAttribute(`data-open`,``),d.removeAttribute(`data-closed`),v.removeAttribute(`data-closed`),t!==v&&t.removeAttribute(`data-closed`);return}d.setAttribute(`data-closed`,``),v.setAttribute(`data-closed`,``),t!==v&&t.setAttribute(`data-closed`,``),d.removeAttribute(`data-open`),v.removeAttribute(`data-open`),t!==v&&t.removeAttribute(`data-open`)},V=()=>{let t=R.container,n=d.ownerDocument.defaultView??window,r=e({anchorRect:_.getBoundingClientRect(),contentRect:v.getBoundingClientRect(),side:D,align:O,sideOffset:k,alignOffset:A,avoidCollisions:j,collisionPadding:M});t.style.position=`absolute`,t.style.top=`0px`,t.style.left=`0px`,t.style.transform=`translate3d(${r.x+n.scrollX}px, ${r.y+n.scrollY}px, 0)`,t.style.willChange=`transform`,t.style.margin=`0`,v.setAttribute(`data-side`,r.side),v.setAttribute(`data-align`,r.align),t!==v&&(t.setAttribute(`data-side`,r.side),t.setAttribute(`data-align`,r.align))},H=i({element:v,onExitComplete:()=>{F||(R.restore(),v.hidden=!0)}}),U=r({observedElements:[_,v],isActive:()=>N,ancestorScroll:!1,onUpdate:V}),W=()=>_.hasAttribute(`disabled`)||_.getAttribute(`aria-disabled`)===`true`,G=(e,t)=>{N!==e&&(!e&&N&&w>0&&(p=Date.now()+w),N=e,B(N?`open`:`closed`),N?(_.setAttribute(`aria-describedby`,z),v.setAttribute(`aria-hidden`,`false`),R.mount(),v.hidden=!1,H.enter(),V(),U.start(),U.update()):(_.removeAttribute(`aria-describedby`),v.setAttribute(`aria-hidden`,`true`),H.exit(),U.stop()),a(d,`tooltip:change`,{open:N,trigger:_,content:v,reason:t}),T?.(N))},K=e=>{if(I&&=(clearTimeout(I),null),Date.now()<p){G(!0,e);return}I=setTimeout(()=>{G(!0,e),I=null},C)},q=e=>{I&&=(clearTimeout(I),null),G(!1,e)};return v.hidden=!0,v.setAttribute(`aria-hidden`,`true`),B(`closed`),L.push(f(_,`pointerenter`,e=>{e.pointerType!==`touch`&&(W()||K(`pointer`))}),f(_,`pointerleave`,e=>{if(e.pointerType===`touch`||P)return;let t=e.relatedTarget;t&&v.contains(t)||q(`pointer`)}),f(_,`focus`,()=>{P=!0,!W()&&K(`focus`)}),f(_,`blur`,()=>{P=!1,q(`blur`)})),L.push(f(v,`pointerleave`,e=>{if(e.pointerType===`touch`||P)return;let t=e.relatedTarget;t&&_.contains(t)||q(`pointer`)})),L.push(f(d,`tooltip:set`,e=>{let t=e.detail,n;if(t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`)if(n){if(W())return;I&&=(clearTimeout(I),null),G(!0,`api`)}else q(`api`)})),L.push(t({root:d,isOpen:()=>N,onDismiss:()=>q(`escape`),closeOnClickOutside:!1,closeOnEscape:!0,preventEscapeDefault:!1})),{show:()=>{W()||(I&&=(clearTimeout(I),null),G(!0,`api`))},hide:()=>q(`api`),get isOpen(){return N},destroy:()=>{F=!0,I&&clearTimeout(I),U.stop(),H.cleanup(),R.cleanup(),L.forEach(e=>e()),L.length=0}}}const _=new WeakSet;function v(e=document){let t=[];for(let n of d(e,`tooltip`))_.has(n)||(_.add(n),t.push(g(n)));return t}export{v as create,g as createTooltip};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-slot/tooltip",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.87",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -38,6 +38,6 @@
|
|
|
38
38
|
],
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@data-slot/core": "0.2.
|
|
41
|
+
"@data-slot/core": "0.2.87"
|
|
42
42
|
}
|
|
43
43
|
}
|