@nysds/nys-tooltip 1.11.4 → 1.13.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/dist/nys-tooltip.js +193 -130
- package/dist/nys-tooltip.js.map +1 -1
- package/package.json +1 -1
package/dist/nys-tooltip.js
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
|
-
import { LitElement as
|
|
2
|
-
import { property as
|
|
3
|
-
const
|
|
4
|
-
var
|
|
5
|
-
for (var
|
|
6
|
-
(s =
|
|
7
|
-
return
|
|
1
|
+
import { LitElement as m, unsafeCSS as y, html as u } from "lit";
|
|
2
|
+
import { property as p, state as g } from "lit/decorators.js";
|
|
3
|
+
const v = `:host{--_nys-tooltip-color: var(--nys-color-text-reverse, #ffffff);--_nys-tooltip-background-color: var(--nys-color-ink, #1b1b1b);--_nys-tooltip-border-radius: var(--nys-radius-md, 4px);--_nys-tooltip-font-family: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif ) );--_nys-tooltip-font-size: var(--nys-type-size-ui-sm, 14px);--_nys-tooltip-letter-spacing: var(--nys-font-letterspacing-ui-sm, .044px);--_nys-tooltip-line-height: var(--nys-font-lineheight-ui-sm, 24px)}.nys-tooltip__content{position:fixed;top:0;left:0;max-width:400px;width:max-content;max-height:120px;padding:var(--nys-space-50, 4px) var(--nys-space-100, 8px);background-color:var(--_nys-tooltip-background-color);border-radius:var(--_nys-tooltip-border-radius);cursor:auto;z-index:1}.nys-tooltip__inner{display:block;color:var(--_nys-tooltip-color);font-family:var(--_nys-tooltip-font-family);font-size:var(--_nys-tooltip-font-size);font-weight:400;line-height:var(--_nys-tooltip-line-height);letter-spacing:var(--_nys-tooltip-letter-spacing);white-space:normal;word-break:break-word;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:4;line-clamp:4;-webkit-box-orient:vertical}.nys-tooltip__arrow{position:absolute;width:14px;height:6px;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="15" height="6" viewBox="0 0 15 6" fill="none"><path d="M8.15079 5.44218C7.7763 5.76317 7.2237 5.76317 6.84921 5.44218L0.5 0H14.5L8.15079 5.44218Z" fill="%231B1B1B"/></svg>') no-repeat center}.nys-tooltip__content[active]{display:block}.fade-out{opacity:0;transition:opacity .2s ease-out}:host([position=top]) .nys-tooltip__arrow{top:100%;left:var(--arrow-offset-x, 50%);transform:translate(-50%)}:host([position=bottom]) .nys-tooltip__arrow{bottom:100%;left:var(--arrow-offset-x, 50%);transform:translate(-50%) rotate(180deg)}:host([position=left]) .nys-tooltip__arrow{left:100%;top:50%;transform:translateY(-50%) rotate(-90deg);margin-left:-4px}:host([position=right]) .nys-tooltip__arrow{right:100%;top:50%;transform:translateY(-50%) rotate(90deg);margin-right:-4px}:host([inverted]) .nys-tooltip__content{--_nys-tooltip-color: var(--nys-color-text, #1b1b1b);--_nys-tooltip-background-color: var(--nys-color-ink-reverse, #fff)}:host([inverted]) .nys-tooltip__arrow{background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="15" height="6" viewBox="0 0 15 6" fill="none"><path d="M8.15079 5.44218C7.7763 5.76317 7.2237 5.76317 6.84921 5.44218L0.5 0H14.5L8.15079 5.44218Z" fill="white"/></svg>') no-repeat center}@media(max-width:400px){.nys-tooltip__content{max-width:calc(100vw - 2rem)}}`;
|
|
4
|
+
var w = Object.defineProperty, b = Object.getOwnPropertyDescriptor, c = (f, t, e, n) => {
|
|
5
|
+
for (var o = n > 1 ? void 0 : n ? b(t, e) : t, i = f.length - 1, s; i >= 0; i--)
|
|
6
|
+
(s = f[i]) && (o = (n ? s(t, e, o) : s(o)) || o);
|
|
7
|
+
return n && o && w(t, e, o), o;
|
|
8
8
|
};
|
|
9
9
|
let x = 0;
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const d = class d extends m {
|
|
11
|
+
/**
|
|
12
|
+
* Lifecycle Methods
|
|
13
|
+
* --------------------------------------------------------------------------
|
|
14
|
+
*/
|
|
12
15
|
constructor() {
|
|
13
|
-
super(), this.id = "", this.text = "", this.inverted = !1, this.
|
|
16
|
+
super(), this.id = "", this.text = "", this.inverted = !1, this.for = "", this._active = !1, this._userHasSetPosition = !1, this._originalUserPosition = null, this._internallyUpdatingPosition = !1, this._hideTimeout = null, this._position = null, this._showTooltip = () => {
|
|
14
17
|
if (this._active = !0, this._addScrollListeners(), this._userHasSetPosition && this._originalUserPosition && this._doesPositionFit(this._originalUserPosition)) {
|
|
15
18
|
this.position = this._originalUserPosition, this.updateComplete.then(() => {
|
|
16
|
-
|
|
17
|
-
".nys-tooltip__content"
|
|
18
|
-
);
|
|
19
|
-
t && this._shiftTooltipIntoViewport(t);
|
|
19
|
+
this._userPositionTooltip();
|
|
20
20
|
});
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
-
this.
|
|
23
|
+
this._autoPositionTooltip();
|
|
24
24
|
}, this._handleBlurOrMouseLeave = () => {
|
|
25
|
-
this.
|
|
26
|
-
const t = this.shadowRoot?.querySelector(
|
|
25
|
+
const t = this._getReferenceElement(), e = this.shadowRoot?.querySelector(
|
|
27
26
|
".nys-tooltip__content"
|
|
28
27
|
);
|
|
29
|
-
t && this.
|
|
28
|
+
t !== document.activeElement && (!t || !e || this._triggerFadeOut(e));
|
|
29
|
+
}, this._cancelFadeOut = () => {
|
|
30
|
+
const t = this.shadowRoot?.querySelector(
|
|
31
|
+
".nys-tooltip__content"
|
|
32
|
+
), e = this._getReferenceElement();
|
|
33
|
+
if (!t || !e) return;
|
|
34
|
+
const n = t.matches(":hover"), o = e.matches(":hover"), i = document.activeElement === e;
|
|
35
|
+
!n && !o && !i || (this._hideTimeout && (clearTimeout(this._hideTimeout), this._hideTimeout = null), t.classList.remove("fade-out"), this._active = !0);
|
|
30
36
|
}, this._handleScrollOrResize = () => {
|
|
31
|
-
this._active
|
|
32
|
-
const t = this.shadowRoot?.querySelector(
|
|
33
|
-
".nys-tooltip__content"
|
|
34
|
-
);
|
|
35
|
-
t && this._shiftTooltipIntoViewport(t);
|
|
36
|
-
})) : this.autoPositionTooltip() : this.autoPositionTooltip());
|
|
37
|
+
!this._active || this._hideTimeout || this._showTooltip();
|
|
37
38
|
}, this._handleEscapeKey = (t) => {
|
|
38
39
|
if (t.key === "Escape" && this._active) {
|
|
39
40
|
this._active = !1, this._removeScrollListeners();
|
|
40
|
-
const
|
|
41
|
+
const e = this.shadowRoot?.querySelector(
|
|
41
42
|
".nys-tooltip__content"
|
|
42
43
|
);
|
|
43
|
-
|
|
44
|
+
e && this._resetTooltipPositioningStyles(e);
|
|
44
45
|
}
|
|
45
46
|
};
|
|
46
47
|
}
|
|
@@ -48,29 +49,31 @@ const f = class f extends g {
|
|
|
48
49
|
return this._position;
|
|
49
50
|
}
|
|
50
51
|
set position(t) {
|
|
51
|
-
const
|
|
52
|
-
this._position = t, this.requestUpdate("position",
|
|
52
|
+
const e = this._position;
|
|
53
|
+
this._position = t, this.requestUpdate("position", e), this._internallyUpdatingPosition || (this._userHasSetPosition = t !== null, this._originalUserPosition = t);
|
|
53
54
|
}
|
|
54
55
|
connectedCallback() {
|
|
55
56
|
super.connectedCallback(), this.id || (this.id = `nys-tooltip-${Date.now()}-${x++}`), window.addEventListener("keydown", this._handleEscapeKey);
|
|
56
57
|
}
|
|
57
58
|
disconnectedCallback() {
|
|
58
|
-
super.disconnectedCallback()
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return this.shadowRoot?.querySelector("slot")?.assignedElements({ flatten: !0 })[0];
|
|
59
|
+
super.disconnectedCallback();
|
|
60
|
+
const t = this._getReferenceElement(), e = this.shadowRoot?.querySelector(".nys-tooltip__content");
|
|
61
|
+
t && e && (t.removeEventListener("mouseenter", this._showTooltip), t.removeEventListener("mouseenter", this._cancelFadeOut), t.removeEventListener("mouseleave", this._handleBlurOrMouseLeave), t.removeEventListener("focusin", this._showTooltip), t.removeEventListener("focusout", this._handleBlurOrMouseLeave), e.removeEventListener("mouseenter", this._cancelFadeOut), e.removeEventListener("mouseleave", this._handleBlurOrMouseLeave)), window.removeEventListener("keydown", this._handleEscapeKey);
|
|
62
62
|
}
|
|
63
|
-
firstUpdated() {
|
|
64
|
-
|
|
65
|
-
t
|
|
66
|
-
|
|
67
|
-
o && this._applyFocusBehavior(o);
|
|
68
|
-
});
|
|
63
|
+
async firstUpdated() {
|
|
64
|
+
await this.updateComplete;
|
|
65
|
+
const t = this._getReferenceElement(), e = this.shadowRoot?.querySelector(".nys-tooltip__content");
|
|
66
|
+
!t || !e || (this.applyInverseTransform(), this._applyTooltipPropToFormComponent(t), (t.tagName.toLowerCase() === "nys-button" || t.tagName.toLowerCase() === "nys-icon") && (this._applyFocusBehavior(t), t.addEventListener("mouseenter", this._showTooltip), t.addEventListener("mouseenter", this._cancelFadeOut), t.addEventListener("mouseleave", this._handleBlurOrMouseLeave), t.addEventListener("focusin", this._showTooltip), t.addEventListener("focusout", this._handleBlurOrMouseLeave), e.addEventListener("mouseenter", this._cancelFadeOut), e.addEventListener("mouseleave", this._handleBlurOrMouseLeave)));
|
|
69
67
|
}
|
|
70
68
|
updated(t) {
|
|
71
69
|
super.updated(t);
|
|
72
|
-
const
|
|
73
|
-
|
|
70
|
+
const e = this._getReferenceElement();
|
|
71
|
+
e && (this._positionStartingBase(), t.has("text") && this._applyTooltipPropToFormComponent(e));
|
|
72
|
+
}
|
|
73
|
+
_triggerFadeOut(t) {
|
|
74
|
+
!t || this._hideTimeout || (t.classList.add("fade-out"), this._hideTimeout = window.setTimeout(() => {
|
|
75
|
+
this._active = !1, this._removeScrollListeners(), this._positionStartingBase(), this._resetTooltipPositioningStyles(t), t.classList.remove("fade-out"), this._hideTimeout = null;
|
|
76
|
+
}, 200));
|
|
74
77
|
}
|
|
75
78
|
// Listen to window scroll so a focus tooltip can auto position even when user move across the page
|
|
76
79
|
_addScrollListeners() {
|
|
@@ -79,57 +82,93 @@ const f = class f extends g {
|
|
|
79
82
|
_removeScrollListeners() {
|
|
80
83
|
window.removeEventListener("scroll", this._handleScrollOrResize, !0), window.removeEventListener("resize", this._handleScrollOrResize);
|
|
81
84
|
}
|
|
82
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Functions
|
|
87
|
+
* --------------------------------------------------------------------------
|
|
88
|
+
*/
|
|
89
|
+
_getReferenceElement() {
|
|
90
|
+
const t = this.for;
|
|
91
|
+
if (!t) return null;
|
|
92
|
+
let e = document.getElementById(t);
|
|
93
|
+
if (e) return e;
|
|
94
|
+
const n = (o) => {
|
|
95
|
+
for (const i of Array.from(o.querySelectorAll("*"))) {
|
|
96
|
+
const s = i.shadowRoot;
|
|
97
|
+
if (s) {
|
|
98
|
+
const r = s.getElementById(t);
|
|
99
|
+
if (r) return r;
|
|
100
|
+
const l = n(s);
|
|
101
|
+
if (l) return l;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
return n(document);
|
|
107
|
+
}
|
|
83
108
|
// We need to pass `ariaLabel` or `ariaDescription` to the nys-components so they can announce both their label and the tooltip's text
|
|
84
|
-
_passAria(t) {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
async _passAria(t) {
|
|
110
|
+
const e = t.tagName.toLowerCase();
|
|
111
|
+
e === "nys-icon" ? t.setAttribute("ariaLabel", `Hint: ${this.text}`) : e === "nys-button" && t.setAttribute("ariaDescription", `, Hint: ${this.text}`);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* In React, the reference element found is often the native HTML element within the nys-component.
|
|
115
|
+
* Therefore, this function accounts for the closest NYS component ancestor that supports a tooltip prop.
|
|
116
|
+
*/
|
|
117
|
+
_applyTooltipPropToFormComponent(t) {
|
|
118
|
+
const e = t.tagName.toLowerCase();
|
|
119
|
+
if (e.startsWith("nys-")) {
|
|
120
|
+
if (e === "nys-button" || e === "nys-icon") {
|
|
121
|
+
this._applyFocusBehavior(t), this._passAria(t);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
"tooltip" in t && (t.tooltip = this.text);
|
|
125
|
+
}
|
|
92
126
|
}
|
|
93
127
|
// Applies focus behavior to an otherwise non focus element (i.e. nys-icon is non focusable by default)
|
|
94
128
|
async _applyFocusBehavior(t) {
|
|
95
|
-
if (
|
|
96
|
-
if (t.tagName.toLowerCase() === "nys-icon") {
|
|
129
|
+
if (t.style.cursor = "pointer", t.tagName.toLowerCase() === "nys-icon") {
|
|
97
130
|
"updateComplete" in t && await t.updateComplete;
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
t.setAttribute("tabindex", "0");
|
|
131
|
+
const n = t.shadowRoot?.querySelector("svg");
|
|
132
|
+
n && n.setAttribute("tabindex", "0");
|
|
133
|
+
}
|
|
102
134
|
}
|
|
103
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Checks if the tooltip fits inside the viewport on the given side of the trigger.
|
|
137
|
+
* Used for auto-positioning. Ignores text overflow for now.
|
|
138
|
+
*/
|
|
104
139
|
_doesPositionFit(t) {
|
|
105
|
-
const
|
|
106
|
-
if (!
|
|
107
|
-
const
|
|
108
|
-
top:
|
|
109
|
-
left:
|
|
110
|
-
bottom: window.innerHeight -
|
|
111
|
-
right: window.innerWidth -
|
|
140
|
+
const e = this._getReferenceElement(), n = this.shadowRoot?.querySelector(".nys-tooltip__content");
|
|
141
|
+
if (!e || !n || t == null) return;
|
|
142
|
+
const o = e.getBoundingClientRect(), i = n.getBoundingClientRect(), s = 8, r = {
|
|
143
|
+
top: o.top - s,
|
|
144
|
+
left: o.left - s,
|
|
145
|
+
bottom: window.innerHeight - o.bottom - s,
|
|
146
|
+
right: window.innerWidth - o.right - s
|
|
112
147
|
};
|
|
113
148
|
return {
|
|
114
|
-
top:
|
|
115
|
-
bottom:
|
|
116
|
-
left:
|
|
117
|
-
right:
|
|
149
|
+
top: r.top >= i.height,
|
|
150
|
+
bottom: r.bottom >= i.height,
|
|
151
|
+
left: r.left >= i.width,
|
|
152
|
+
right: r.right >= i.width
|
|
118
153
|
}[t];
|
|
119
154
|
}
|
|
120
|
-
|
|
121
|
-
async autoPositionTooltip() {
|
|
155
|
+
_userPositionTooltip() {
|
|
122
156
|
const t = this.shadowRoot?.querySelector(
|
|
123
|
-
".nys-
|
|
124
|
-
),
|
|
157
|
+
".nys-tooltip__content"
|
|
158
|
+
), e = this._getReferenceElement();
|
|
159
|
+
t && e && (this._positionTooltipElement(e, t, this.position), this._shiftTooltipIntoViewport(t));
|
|
160
|
+
}
|
|
161
|
+
// Calculates the best placement based on available space (flips placement if it doesn't fit)
|
|
162
|
+
async _autoPositionTooltip() {
|
|
163
|
+
const t = this._getReferenceElement(), e = this.shadowRoot?.querySelector(
|
|
125
164
|
".nys-tooltip__content"
|
|
126
165
|
);
|
|
127
|
-
if (!t || !
|
|
128
|
-
const
|
|
129
|
-
top:
|
|
130
|
-
left:
|
|
131
|
-
bottom: window.innerHeight -
|
|
132
|
-
right: window.innerWidth -
|
|
166
|
+
if (!t || !e) return;
|
|
167
|
+
const n = t.getBoundingClientRect(), o = 8, i = {
|
|
168
|
+
top: n.top - o,
|
|
169
|
+
left: n.left - o,
|
|
170
|
+
bottom: window.innerHeight - n.bottom - o,
|
|
171
|
+
right: window.innerWidth - n.right - o
|
|
133
172
|
};
|
|
134
173
|
let s = [
|
|
135
174
|
"top",
|
|
@@ -138,56 +177,80 @@ const f = class f extends g {
|
|
|
138
177
|
"left"
|
|
139
178
|
];
|
|
140
179
|
if (this._userHasSetPosition && this._originalUserPosition) {
|
|
141
|
-
const
|
|
142
|
-
|
|
180
|
+
const a = this._originalUserPosition;
|
|
181
|
+
a === "left" ? s = ["left", "right", "top", "bottom"] : a === "right" ? s = ["right", "left", "top", "bottom"] : a === "top" ? s = ["top", "bottom", "right", "left"] : a === "bottom" && (s = ["bottom", "top", "right", "left"]);
|
|
143
182
|
}
|
|
144
|
-
for (const
|
|
145
|
-
if (this._doesPositionFit(
|
|
146
|
-
this._setInternalPosition(
|
|
183
|
+
for (const a of s)
|
|
184
|
+
if (this._doesPositionFit(a)) {
|
|
185
|
+
this._setInternalPosition(a), await this.updateComplete, this._positionTooltipElement(t, e, a), this._shiftTooltipIntoViewport(e);
|
|
147
186
|
return;
|
|
148
187
|
}
|
|
149
|
-
let
|
|
150
|
-
for (const
|
|
151
|
-
|
|
152
|
-
this._setInternalPosition(
|
|
188
|
+
let r = "top", l = i.top;
|
|
189
|
+
for (const a of s)
|
|
190
|
+
i[a] > l && (l = i[a], r = a);
|
|
191
|
+
this._setInternalPosition(r), await this.updateComplete, this._positionTooltipElement(t, e, r), this._shiftTooltipIntoViewport(e);
|
|
192
|
+
}
|
|
193
|
+
_positionStartingBase() {
|
|
194
|
+
const t = this.shadowRoot?.querySelector(
|
|
195
|
+
".nys-tooltip__content"
|
|
196
|
+
);
|
|
197
|
+
t && (t.style.top = "0px", t.style.left = "0px");
|
|
198
|
+
}
|
|
199
|
+
_positionTooltipElement(t, e, n) {
|
|
200
|
+
const o = t.getBoundingClientRect(), i = e.getBoundingClientRect(), s = 8;
|
|
201
|
+
let r = 0, l = 0;
|
|
202
|
+
switch (n) {
|
|
203
|
+
case "top":
|
|
204
|
+
r = o.top - i.height - s, l = o.left + o.width / 2 - i.width / 2;
|
|
205
|
+
break;
|
|
206
|
+
case "bottom":
|
|
207
|
+
r = o.bottom + s, l = o.left + o.width / 2 - i.width / 2;
|
|
208
|
+
break;
|
|
209
|
+
case "left":
|
|
210
|
+
r = o.top + o.height / 2 - i.height / 2, l = o.left - i.width - s;
|
|
211
|
+
break;
|
|
212
|
+
case "right":
|
|
213
|
+
r = o.top + o.height / 2 - i.height / 2, l = o.right + s;
|
|
214
|
+
break;
|
|
215
|
+
default:
|
|
216
|
+
r = o.top - i.height - s, l = o.left + o.width / 2 - i.width / 2;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
e.style.top = `${r}px`, e.style.left = `${l}px`;
|
|
220
|
+
}
|
|
221
|
+
// Like storybook, some user's parent container may contain transform styling, which sets a new coordinate system.
|
|
222
|
+
// This function reverse any container of that scale to not interfere with tooltip calculation.
|
|
223
|
+
applyInverseTransform() {
|
|
224
|
+
document.querySelectorAll('div[scale="1"]').forEach((t) => {
|
|
225
|
+
t.style.transform = "none";
|
|
226
|
+
});
|
|
153
227
|
}
|
|
154
|
-
// Sets flag to distinguish to position's setter that we are updating "position" prop internally
|
|
155
228
|
_setInternalPosition(t) {
|
|
156
229
|
this._internallyUpdatingPosition = !0, this.position = t, this._internallyUpdatingPosition = !1;
|
|
157
230
|
}
|
|
158
231
|
// Determines if text of tooltip over-extends outside of viewport edge and adjust tooltip for horizontal overflow
|
|
159
232
|
_shiftTooltipIntoViewport(t) {
|
|
160
|
-
const e =
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
t.style.setProperty("--arrow-offset-x", `${
|
|
233
|
+
const e = this._getReferenceElement();
|
|
234
|
+
if (!e) return;
|
|
235
|
+
const n = e.getBoundingClientRect(), o = t.getBoundingClientRect(), i = n.left + n.width / 2, s = o.left < 0, r = o.right > window.innerWidth;
|
|
236
|
+
s ? (t.style.left = "10px", t.style.transform = "none") : r && (t.style.right = "0px", t.style.left = "auto", t.style.transform = "none");
|
|
237
|
+
const l = t.getBoundingClientRect(), a = (i - l.left) / l.width, _ = Math.max(0, Math.min(1, a)) * 100;
|
|
238
|
+
t.style.setProperty("--arrow-offset-x", `${_}%`);
|
|
166
239
|
}
|
|
167
240
|
// Reposition tooltip back to original set position (e.g. top, left, bottom, right) to avoid positioning issue base on last position
|
|
168
241
|
_resetTooltipPositioningStyles(t) {
|
|
169
|
-
t.style.left = "", t.style.right = "", t.style.transform = "";
|
|
242
|
+
t.style.left = "", t.style.right = "", t.style.top = "", t.style.transform = "", t.style.removeProperty("--arrow-offset-x");
|
|
170
243
|
}
|
|
171
244
|
render() {
|
|
172
|
-
return
|
|
245
|
+
return u`
|
|
173
246
|
<div class="nys-tooltip__main">
|
|
174
|
-
|
|
175
|
-
class="nys-tooltip__wrapper"
|
|
176
|
-
@mouseenter=${this._handleTooltipEnter}
|
|
177
|
-
@mouseleave=${this._handleBlurOrMouseLeave}
|
|
178
|
-
@focusin=${this._handleTooltipEnter}
|
|
179
|
-
@focusout=${this._handleBlurOrMouseLeave}
|
|
180
|
-
>
|
|
181
|
-
<span class="nys-tooltip__trigger">
|
|
182
|
-
<slot></slot>
|
|
183
|
-
</span>
|
|
184
|
-
</div>
|
|
185
|
-
${this.text?.trim() ? _`<div
|
|
247
|
+
${this.text?.trim() ? u`<div
|
|
186
248
|
id=${this.id}
|
|
187
249
|
class="nys-tooltip__content"
|
|
188
250
|
role="tooltip"
|
|
189
|
-
aria-hidden=${!this.
|
|
251
|
+
aria-hidden=${this._active && !this._hideTimeout ? "false" : "true"}
|
|
190
252
|
?active=${this._active}
|
|
253
|
+
style="visibility: ${this._active ? "visible" : "hidden"}; "
|
|
191
254
|
>
|
|
192
255
|
<div class="nys-tooltip__inner">${this.text}</div>
|
|
193
256
|
<span class="nys-tooltip__arrow"></span>
|
|
@@ -196,28 +259,28 @@ const f = class f extends g {
|
|
|
196
259
|
`;
|
|
197
260
|
}
|
|
198
261
|
};
|
|
199
|
-
|
|
200
|
-
let
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
],
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
],
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
],
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
],
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
],
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
],
|
|
219
|
-
customElements.get("nys-tooltip") || customElements.define("nys-tooltip",
|
|
262
|
+
d.styles = y(v);
|
|
263
|
+
let h = d;
|
|
264
|
+
c([
|
|
265
|
+
p({ type: String, reflect: !0 })
|
|
266
|
+
], h.prototype, "id", 2);
|
|
267
|
+
c([
|
|
268
|
+
p({ type: String })
|
|
269
|
+
], h.prototype, "text", 2);
|
|
270
|
+
c([
|
|
271
|
+
p({ type: Boolean, reflect: !0 })
|
|
272
|
+
], h.prototype, "inverted", 2);
|
|
273
|
+
c([
|
|
274
|
+
p({ type: String })
|
|
275
|
+
], h.prototype, "for", 2);
|
|
276
|
+
c([
|
|
277
|
+
g()
|
|
278
|
+
], h.prototype, "_active", 2);
|
|
279
|
+
c([
|
|
280
|
+
p({ type: String, reflect: !0 })
|
|
281
|
+
], h.prototype, "position", 1);
|
|
282
|
+
customElements.get("nys-tooltip") || customElements.define("nys-tooltip", h);
|
|
220
283
|
export {
|
|
221
|
-
|
|
284
|
+
h as NysTooltip
|
|
222
285
|
};
|
|
223
286
|
//# sourceMappingURL=nys-tooltip.js.map
|
package/dist/nys-tooltip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nys-tooltip.js","sources":["../src/nys-tooltip.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-tooltip.scss?inline\";\n\nlet tooltipIdCounter = 0; // Counter for generating unique IDs\n\nexport class NysTooltip extends LitElement {\n static styles = unsafeCSS(styles);\n\n @property({ type: String, reflect: true }) id = \"\";\n @property({ type: String }) text = \"\";\n @property({ type: Boolean, reflect: true }) inverted = false;\n @property({ type: Boolean, reflect: true }) focusable = false;\n\n // Track if tooltip is active (hovered or focused)\n @state()\n private _active = false;\n\n // Track if user set position is set explicitly\n private _userHasSetPosition = false;\n private _originalUserPosition: typeof this._position | null = null;\n // Internal flag to prevent dynamic positioning when not needed\n private _internallyUpdatingPosition = false;\n\n /********************* Position Logic *********************/\n private _position: \"top\" | \"bottom\" | \"left\" | \"right\" | null = null;\n\n @property({ type: String, reflect: true })\n get position() {\n return this._position;\n }\n\n set position(value) {\n const oldVal = this._position;\n this._position = value;\n this.requestUpdate(\"position\", oldVal);\n\n // The \"_internallyUpdatingPosition\" flag allows user's set position to take preference\n if (!this._internallyUpdatingPosition) {\n this._userHasSetPosition = value !== null;\n this._originalUserPosition = value;\n }\n }\n\n /**************** Lifecycle Methods ****************/\n constructor() {\n super();\n }\n\n connectedCallback() {\n super.connectedCallback();\n\n // Generate a unique ID if not provided\n if (!this.id) {\n this.id = `nys-tooltip-${Date.now()}-${tooltipIdCounter++}`;\n }\n\n window.addEventListener(\"keydown\", this._handleEscapeKey);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n window.removeEventListener(\"keydown\", this._handleEscapeKey);\n }\n\n private get _firstAssignedEl(): HTMLElement | undefined {\n const slot = this.shadowRoot?.querySelector(\"slot\");\n return slot?.assignedElements({ flatten: true })[0] as\n | HTMLElement\n | undefined;\n }\n\n firstUpdated() {\n const slot = this.shadowRoot?.querySelector(\"slot\");\n if (slot) {\n slot.addEventListener(\"slotchange\", () => {\n const firstEl = this._firstAssignedEl;\n if (firstEl) {\n this._applyFocusBehavior(firstEl);\n }\n });\n }\n }\n\n updated(changedProps: Map<string, unknown>) {\n super.updated(changedProps);\n\n const firstEl = this._firstAssignedEl;\n if (!firstEl) return;\n\n // Accounts for tooltip's text change (for code editor changes & VO)\n if (changedProps.has(\"text\")) {\n this._passAria(firstEl);\n }\n if (changedProps.has(\"focusable\")) {\n this._applyFocusBehavior(firstEl);\n }\n }\n\n /******************** Event Handlers ********************/\n // When toggling tooltip, check if user has set position to give it preference it space allows. Otherwise dynamically position tooltip.\n private _handleTooltipEnter = () => {\n this._active = true;\n this._addScrollListeners();\n\n // Try to honor user's original preference first\n if (this._userHasSetPosition && this._originalUserPosition) {\n if (this._doesPositionFit(this._originalUserPosition)) {\n this.position = this._originalUserPosition;\n // Check if current tooltip position overflows to edge of screen\n this.updateComplete.then(() => {\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n if (tooltip) this._shiftTooltipIntoViewport(tooltip);\n });\n return;\n }\n }\n\n // Otherwise fall back to auto logic\n this.autoPositionTooltip();\n };\n\n private _handleBlurOrMouseLeave = () => {\n this._active = false;\n this._removeScrollListeners();\n\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n\n if (tooltip) {\n this._resetTooltipPositioningStyles(tooltip);\n }\n };\n\n // Listen to window scroll so a focus tooltip can auto position even when user move across the page\n private _addScrollListeners() {\n window.addEventListener(\"scroll\", this._handleScrollOrResize, true);\n window.addEventListener(\"resize\", this._handleScrollOrResize);\n }\n\n private _removeScrollListeners() {\n window.removeEventListener(\"scroll\", this._handleScrollOrResize, true);\n window.removeEventListener(\"resize\", this._handleScrollOrResize);\n }\n\n private _handleScrollOrResize = () => {\n if (!this._active) return;\n\n if (this._userHasSetPosition && this._originalUserPosition) {\n if (this._doesPositionFit(this._originalUserPosition)) {\n this._setInternalPosition(this._originalUserPosition);\n\n // Check if current tooltip position overflows to edge of screen\n this.updateComplete.then(() => {\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n if (tooltip) this._shiftTooltipIntoViewport(tooltip);\n });\n } else {\n this.autoPositionTooltip();\n }\n } else {\n this.autoPositionTooltip();\n }\n };\n\n private _handleEscapeKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && this._active) {\n this._active = false;\n this._removeScrollListeners();\n\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n if (tooltip) {\n this._resetTooltipPositioningStyles(tooltip);\n }\n }\n };\n\n /******************** Functions ********************/\n // We need to pass `ariaLabel` or `ariaDescription` to the nys-components so they can announce both their label and the tooltip's text\n private _passAria(el: HTMLElement) {\n const tagName = el.tagName.toLowerCase();\n\n if (tagName.startsWith(\"nys-\")) {\n if (tagName === \"nys-icon\") {\n // For nys-icon, use ariaLabel instead\n const existingLabel = el.getAttribute(\"ariaLabel\") || \"\";\n const mergedLabel = existingLabel\n ? `${existingLabel}, ${this.text}`\n : this.text;\n\n el.setAttribute(\"ariaLabel\", mergedLabel);\n } else {\n // For other components like nys-button, use ariaDescription\n el.setAttribute(\"ariaDescription\", this.text);\n }\n }\n }\n\n // Applies focus behavior to an otherwise non focus element (i.e. nys-icon is non focusable by default)\n private async _applyFocusBehavior(el: HTMLElement) {\n if (!this.focusable) return;\n\n const tagName = el.tagName.toLowerCase();\n if (tagName === \"nys-icon\") {\n if (\"updateComplete\" in el) {\n await (el as any).updateComplete;\n }\n const svg = el.shadowRoot?.querySelector(\"svg\");\n if (svg) {\n svg.setAttribute(\"tabindex\", \"0\");\n }\n } else {\n el.setAttribute(\"tabindex\", \"0\");\n }\n }\n\n // Checks if user's set position fit with current viewport (Does not account for overflow texts at this moment)\n private _doesPositionFit(position: typeof this._position) {\n const wrapper = this.shadowRoot?.querySelector(\".nys-tooltip__wrapper\");\n const tooltip = this.shadowRoot?.querySelector(\".nys-tooltip__content\");\n\n if (!wrapper || !tooltip || position == null) return;\n\n const triggerRect = wrapper.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n\n // Define some margin buffer between tooltip and trigger (to avoid touching the edges too tightly)\n const margin = 8;\n\n // Available space on each side relative to trigger rect and viewport\n const spaceAvailable = {\n top: triggerRect.top - margin,\n left: triggerRect.left - margin,\n bottom: window.innerHeight - triggerRect.bottom - margin,\n right: window.innerWidth - triggerRect.right - margin,\n };\n\n // Check if tooltip fits on each side (compare available space vs tooltip size)\n const fits = {\n top: spaceAvailable.top >= tooltipRect.height,\n bottom: spaceAvailable.bottom >= tooltipRect.height,\n left: spaceAvailable.left >= tooltipRect.width,\n right: spaceAvailable.right >= tooltipRect.width,\n };\n\n return fits[position];\n }\n\n // Calculates the best placement based on available space (flips placement if it doesn't fit)\n private async autoPositionTooltip() {\n const wrapper = this.shadowRoot?.querySelector(\n \".nys-tooltip__wrapper\",\n ) as HTMLElement;\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n\n if (!wrapper || !tooltip) return;\n\n const triggerRect = wrapper.getBoundingClientRect();\n const margin = 8;\n\n const spaceAvailable = {\n top: triggerRect.top - margin,\n left: triggerRect.left - margin,\n bottom: window.innerHeight - triggerRect.bottom - margin,\n right: window.innerWidth - triggerRect.right - margin,\n };\n\n // Default tryOrder for auto mode\n let tryOrder: Array<keyof typeof spaceAvailable> = [\n \"top\",\n \"bottom\",\n \"right\",\n \"left\",\n ];\n\n // If user explicitly set a preferred position (even if it didn’t fit),\n // favor it first before trying others\n if (this._userHasSetPosition && this._originalUserPosition) {\n const userPosition = this._originalUserPosition;\n if (userPosition === \"left\") {\n tryOrder = [\"left\", \"right\", \"top\", \"bottom\"];\n } else if (userPosition === \"right\") {\n tryOrder = [\"right\", \"left\", \"top\", \"bottom\"];\n } else if (userPosition === \"top\") {\n tryOrder = [\"top\", \"bottom\", \"right\", \"left\"];\n } else if (userPosition === \"bottom\") {\n tryOrder = [\"bottom\", \"top\", \"right\", \"left\"];\n }\n }\n\n // 1) Try to find the first side that fits\n for (const pos of tryOrder) {\n if (this._doesPositionFit(pos)) {\n this._setInternalPosition(pos);\n await this.updateComplete;\n this._shiftTooltipIntoViewport(tooltip);\n return;\n }\n }\n\n // 2) Fallback: pick the side with the most space\n let bestPosition: keyof typeof spaceAvailable = \"top\";\n let maxSpace = spaceAvailable.top;\n\n for (const pos of tryOrder) {\n if (spaceAvailable[pos] > maxSpace) {\n maxSpace = spaceAvailable[pos];\n bestPosition = pos;\n }\n }\n this._setInternalPosition(bestPosition);\n await this.updateComplete;\n this._shiftTooltipIntoViewport(tooltip);\n }\n\n // Sets flag to distinguish to position's setter that we are updating \"position\" prop internally\n private _setInternalPosition(bestPosition: typeof this._position) {\n this._internallyUpdatingPosition = true;\n this.position = bestPosition;\n this._internallyUpdatingPosition = false;\n }\n\n // Determines if text of tooltip over-extends outside of viewport edge and adjust tooltip for horizontal overflow\n private _shiftTooltipIntoViewport(tooltip: HTMLElement) {\n const wrapper = this.shadowRoot?.querySelector(\n \".nys-tooltip__wrapper\",\n ) as HTMLElement;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n\n const wrapperCenter = wrapperRect.left + wrapperRect.width / 2;\n\n const overflowLeft = tooltipRect.left < 0;\n const overflowRight = tooltipRect.right > window.innerWidth;\n\n this._resetTooltipPositioningStyles(tooltip);\n\n // Tooltip is past the viewport edge, shift it inwards\n if (overflowLeft) {\n tooltip.style.left = \"0px\";\n tooltip.style.transform = \"none\";\n } else if (overflowRight) {\n tooltip.style.right = \"0px\";\n tooltip.style.left = \"auto\";\n tooltip.style.transform = \"none\";\n }\n\n // Recalculate tooltip rect after any shift\n const newTooltipRect = tooltip.getBoundingClientRect();\n\n // Arrow offset relative to tooltip width to maintain accuracy on zoom and out-of-bounds\n const arrowOffsetRatio =\n (wrapperCenter - newTooltipRect.left) / newTooltipRect.width;\n const arrowOffsetPercent = Math.max(0, Math.min(1, arrowOffsetRatio)) * 100;\n\n tooltip.style.setProperty(\"--arrow-offset-x\", `${arrowOffsetPercent}%`);\n }\n\n // Reposition tooltip back to original set position (e.g. top, left, bottom, right) to avoid positioning issue base on last position\n private _resetTooltipPositioningStyles(tooltip: HTMLElement) {\n tooltip.style.left = \"\";\n tooltip.style.right = \"\";\n tooltip.style.transform = \"\";\n }\n\n render() {\n return html`\n <div class=\"nys-tooltip__main\">\n <div\n class=\"nys-tooltip__wrapper\"\n @mouseenter=${this._handleTooltipEnter}\n @mouseleave=${this._handleBlurOrMouseLeave}\n @focusin=${this._handleTooltipEnter}\n @focusout=${this._handleBlurOrMouseLeave}\n >\n <span class=\"nys-tooltip__trigger\">\n <slot></slot>\n </span>\n </div>\n ${this.text?.trim()\n ? html`<div\n id=${this.id}\n class=\"nys-tooltip__content\"\n role=\"tooltip\"\n aria-hidden=${!this._active}\n ?active=${this._active}\n >\n <div class=\"nys-tooltip__inner\">${this.text}</div>\n <span class=\"nys-tooltip__arrow\"></span>\n </div>`\n : \"\"}\n </div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-tooltip\")) {\n customElements.define(\"nys-tooltip\", NysTooltip);\n}\n"],"names":["tooltipIdCounter","_NysTooltip","LitElement","tooltip","e","value","oldVal","slot","firstEl","changedProps","el","tagName","existingLabel","mergedLabel","svg","position","wrapper","triggerRect","tooltipRect","margin","spaceAvailable","tryOrder","userPosition","pos","bestPosition","maxSpace","wrapperRect","wrapperCenter","overflowLeft","overflowRight","newTooltipRect","arrowOffsetRatio","arrowOffsetPercent","html","unsafeCSS","styles","NysTooltip","__decorateClass","property","state"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nys-tooltip.js","sources":["../src/nys-tooltip.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-tooltip.scss?inline\";\n\nlet tooltipIdCounter = 0;\n\n/**\n * `<nys-tooltip>` is a custom tooltip component for NYS design system elements.\n * It supports dynamic positioning, screen-reader accessibility, keyboard interaction,\n * and viewport overflow handling.\n *\n * The tooltip automatically positions itself relative to a target element specified\n * via the `for` attribute, but can also respect a user-defined position.\n *\n * @fires nys-focus - Dispatched when the reference element receives focus (via keyboard or programmatically).\n * @fires nys-blur - Dispatched when the reference element loses focus or mouse leaves the tooltip.\n *\n * Notes:\n * - Tooltip visibility is automatically managed on hover/focus of the reference element.\n * - The component adjusts position dynamically to prevent overflow off-screen.\n * - Supports keyboard dismissal with the Escape key.\n */\n\nexport class NysTooltip extends LitElement {\n static styles = unsafeCSS(styles);\n\n @property({ type: String, reflect: true }) id = \"\";\n @property({ type: String }) text = \"\";\n @property({ type: Boolean, reflect: true }) inverted = false;\n @property({ type: String }) for = \"\";\n\n // Track if tooltip is active (hovered or focused)\n @state()\n private _active = false;\n\n // Track if user set position is set explicitly\n private _userHasSetPosition = false;\n private _originalUserPosition: typeof this._position | null = null;\n // Internal flag to prevent dynamic positioning when not needed\n private _internallyUpdatingPosition = false;\n // Flag for hiding the timeout\n private _hideTimeout: number | null = null;\n\n // Position Logic\n private _position: \"top\" | \"bottom\" | \"left\" | \"right\" | null = null;\n\n @property({ type: String, reflect: true })\n get position() {\n return this._position;\n }\n\n set position(value) {\n const oldVal = this._position;\n this._position = value;\n this.requestUpdate(\"position\", oldVal);\n\n // The \"_internallyUpdatingPosition\" flag allows user's set position to take preference\n if (!this._internallyUpdatingPosition) {\n this._userHasSetPosition = value !== null;\n this._originalUserPosition = value;\n }\n }\n\n /**\n * Lifecycle Methods\n * --------------------------------------------------------------------------\n */\n\n constructor() {\n super();\n }\n\n connectedCallback() {\n super.connectedCallback();\n\n // Generate a unique ID if not provided\n if (!this.id) {\n this.id = `nys-tooltip-${Date.now()}-${tooltipIdCounter++}`;\n }\n\n window.addEventListener(\"keydown\", this._handleEscapeKey);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n const ref = this._getReferenceElement();\n const tooltip = this.shadowRoot?.querySelector(\".nys-tooltip__content\");\n\n if (ref && tooltip) {\n ref.removeEventListener(\"mouseenter\", this._showTooltip);\n ref.removeEventListener(\"mouseenter\", this._cancelFadeOut);\n ref.removeEventListener(\"mouseleave\", this._handleBlurOrMouseLeave);\n ref.removeEventListener(\"focusin\", this._showTooltip);\n ref.removeEventListener(\"focusout\", this._handleBlurOrMouseLeave);\n tooltip.removeEventListener(\"mouseenter\", this._cancelFadeOut);\n tooltip.removeEventListener(\"mouseleave\", this._handleBlurOrMouseLeave);\n }\n window.removeEventListener(\"keydown\", this._handleEscapeKey);\n }\n\n async firstUpdated() {\n await this.updateComplete;\n const ref = this._getReferenceElement();\n const tooltip = this.shadowRoot?.querySelector(\".nys-tooltip__content\");\n\n if (!ref || !tooltip) return;\n\n this.applyInverseTransform();\n this._applyTooltipPropToFormComponent(ref);\n\n if (\n ref.tagName.toLowerCase() === \"nys-button\" ||\n ref.tagName.toLowerCase() === \"nys-icon\"\n ) {\n this._applyFocusBehavior(ref);\n ref.addEventListener(\"mouseenter\", this._showTooltip);\n ref.addEventListener(\"mouseenter\", this._cancelFadeOut);\n ref.addEventListener(\"mouseleave\", this._handleBlurOrMouseLeave);\n ref.addEventListener(\"focusin\", this._showTooltip);\n ref.addEventListener(\"focusout\", this._handleBlurOrMouseLeave);\n tooltip.addEventListener(\"mouseenter\", this._cancelFadeOut);\n tooltip.addEventListener(\"mouseleave\", this._handleBlurOrMouseLeave);\n }\n }\n\n updated(changedProps: Map<string, unknown>) {\n super.updated(changedProps);\n\n const ref = this._getReferenceElement();\n if (!ref) return;\n\n this._positionStartingBase();\n\n // Accounts for tooltip's text change (for code editor changes & VO)\n if (changedProps.has(\"text\")) {\n this._applyTooltipPropToFormComponent(ref);\n }\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n // When toggling tooltip, check if user has set position to give it preference it space allows. Otherwise dynamically position tooltip.\n private _showTooltip = () => {\n this._active = true;\n this._addScrollListeners();\n\n // Try to honor user's original preference first\n if (this._userHasSetPosition && this._originalUserPosition) {\n if (this._doesPositionFit(this._originalUserPosition)) {\n this.position = this._originalUserPosition;\n // Check if current tooltip position overflows to edge of screen\n this.updateComplete.then(() => {\n this._userPositionTooltip();\n });\n return;\n }\n }\n\n // Otherwise fall back to auto logic\n this._autoPositionTooltip();\n };\n\n private _handleBlurOrMouseLeave = () => {\n const ref = this._getReferenceElement();\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n\n if (ref === document.activeElement) return;\n if (!ref || !tooltip) return;\n\n this._triggerFadeOut(tooltip);\n };\n\n private _triggerFadeOut(tooltip: HTMLElement) {\n if (!tooltip || this._hideTimeout) return;\n\n tooltip.classList.add(\"fade-out\");\n\n this._hideTimeout = window.setTimeout(() => {\n this._active = false;\n this._removeScrollListeners();\n this._positionStartingBase();\n this._resetTooltipPositioningStyles(tooltip);\n\n tooltip.classList.remove(\"fade-out\");\n this._hideTimeout = null;\n }, 200);\n }\n\n private _cancelFadeOut = () => {\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n const ref = this._getReferenceElement();\n\n if (!tooltip || !ref) return;\n\n const isPointerInsideTooltip = tooltip.matches(\":hover\");\n const isPointerInsideRef = ref.matches(\":hover\");\n\n const isTouched = document.activeElement === ref;\n\n if (!isPointerInsideTooltip && !isPointerInsideRef && !isTouched) {\n return;\n }\n\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n\n tooltip.classList.remove(\"fade-out\");\n this._active = true;\n };\n\n // Listen to window scroll so a focus tooltip can auto position even when user move across the page\n private _addScrollListeners() {\n window.addEventListener(\"scroll\", this._handleScrollOrResize, true);\n window.addEventListener(\"resize\", this._handleScrollOrResize);\n }\n\n private _removeScrollListeners() {\n window.removeEventListener(\"scroll\", this._handleScrollOrResize, true);\n window.removeEventListener(\"resize\", this._handleScrollOrResize);\n }\n\n private _handleScrollOrResize = () => {\n if (!this._active || this._hideTimeout) return;\n\n this._showTooltip();\n };\n\n private _handleEscapeKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && this._active) {\n this._active = false;\n this._removeScrollListeners();\n\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n if (tooltip) {\n this._resetTooltipPositioningStyles(tooltip);\n }\n }\n };\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n private _getReferenceElement() {\n const targetId = this.for;\n if (!targetId) return null;\n\n // lightDOM search\n let htmlElement = document.getElementById(targetId);\n if (htmlElement) return htmlElement;\n\n // Search recursively through shadow DOMs (e.g. for nys-label within other component's shadowDOM)\n const findInShadows = (root: ParentNode): HTMLElement | null => {\n for (const el of Array.from(root.querySelectorAll(\"*\"))) {\n const shadowElement = el.shadowRoot;\n if (shadowElement) {\n const found = shadowElement.getElementById(targetId);\n if (found) return found;\n\n const deeper = findInShadows(shadowElement);\n if (deeper) return deeper;\n }\n }\n return null;\n };\n\n return findInShadows(document);\n }\n\n // We need to pass `ariaLabel` or `ariaDescription` to the nys-components so they can announce both their label and the tooltip's text\n private async _passAria(el: HTMLElement) {\n const tagName = el.tagName.toLowerCase();\n\n if (tagName === \"nys-icon\") {\n // For nys-icon, use ariaLabel instead\n el.setAttribute(\"ariaLabel\", `Hint: ${this.text}`);\n } else if (tagName === \"nys-button\") {\n // For other components like nys-button, use ariaDescription\n el.setAttribute(\"ariaDescription\", `, Hint: ${this.text}`);\n }\n }\n\n /**\n * In React, the reference element found is often the native HTML element within the nys-component.\n * Therefore, this function accounts for the closest NYS component ancestor that supports a tooltip prop.\n */\n private _applyTooltipPropToFormComponent(ref: HTMLElement) {\n const tagName = ref.tagName.toLowerCase();\n if (!tagName.startsWith(\"nys-\")) return;\n\n if (tagName === \"nys-button\" || tagName === \"nys-icon\") {\n // Already handled elsewhere in this component; we just ensure we attach listeners\n this._applyFocusBehavior(ref);\n this._passAria(ref);\n return;\n }\n\n if (\"tooltip\" in ref) {\n ref.tooltip = this.text;\n }\n }\n\n // Applies focus behavior to an otherwise non focus element (i.e. nys-icon is non focusable by default)\n private async _applyFocusBehavior(el: HTMLElement) {\n el.style.cursor = \"pointer\";\n const tagName = el.tagName.toLowerCase();\n\n if (tagName === \"nys-icon\") {\n if (\"updateComplete\" in el) {\n await (el as any).updateComplete;\n }\n const svg = el.shadowRoot?.querySelector(\"svg\");\n if (svg) {\n svg.setAttribute(\"tabindex\", \"0\");\n }\n }\n }\n\n /**\n * Checks if the tooltip fits inside the viewport on the given side of the trigger.\n * Used for auto-positioning. Ignores text overflow for now.\n */\n private _doesPositionFit(position: typeof this._position) {\n const ref = this._getReferenceElement();\n const tooltip = this.shadowRoot?.querySelector(\".nys-tooltip__content\");\n\n if (!ref || !tooltip || position == null) return;\n\n const refRect = ref.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n\n // Define some margin buffer between tooltip and trigger (to avoid touching the edges too tightly)\n const margin = 8;\n\n // Available space on each side relative to trigger rect and viewport\n const spaceAvailable = {\n top: refRect.top - margin,\n left: refRect.left - margin,\n bottom: window.innerHeight - refRect.bottom - margin,\n right: window.innerWidth - refRect.right - margin,\n };\n\n // Check if tooltip fits on each side (compare available space vs tooltip size)\n const fits = {\n top: spaceAvailable.top >= tooltipRect.height,\n bottom: spaceAvailable.bottom >= tooltipRect.height,\n left: spaceAvailable.left >= tooltipRect.width,\n right: spaceAvailable.right >= tooltipRect.width,\n };\n\n return fits[position];\n }\n\n private _userPositionTooltip() {\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n const ref = this._getReferenceElement();\n if (tooltip && ref) {\n this._positionTooltipElement(ref, tooltip, this.position);\n this._shiftTooltipIntoViewport(tooltip);\n }\n }\n\n // Calculates the best placement based on available space (flips placement if it doesn't fit)\n private async _autoPositionTooltip() {\n const ref = this._getReferenceElement();\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n\n if (!ref || !tooltip) return;\n\n const refRect = ref.getBoundingClientRect();\n const margin = 8;\n\n const spaceAvailable = {\n top: refRect.top - margin,\n left: refRect.left - margin,\n bottom: window.innerHeight - refRect.bottom - margin,\n right: window.innerWidth - refRect.right - margin,\n };\n\n // Default tryOrder for auto mode\n let tryOrder: Array<keyof typeof spaceAvailable> = [\n \"top\",\n \"bottom\",\n \"right\",\n \"left\",\n ];\n\n // If user explicitly set a preferred position (even if it didn’t fit),\n // favor it first before trying others\n if (this._userHasSetPosition && this._originalUserPosition) {\n const userPosition = this._originalUserPosition;\n if (userPosition === \"left\") {\n tryOrder = [\"left\", \"right\", \"top\", \"bottom\"];\n } else if (userPosition === \"right\") {\n tryOrder = [\"right\", \"left\", \"top\", \"bottom\"];\n } else if (userPosition === \"top\") {\n tryOrder = [\"top\", \"bottom\", \"right\", \"left\"];\n } else if (userPosition === \"bottom\") {\n tryOrder = [\"bottom\", \"top\", \"right\", \"left\"];\n }\n }\n\n // 1) Try to find the first side that fits\n for (const pos of tryOrder) {\n if (this._doesPositionFit(pos)) {\n this._setInternalPosition(pos);\n await this.updateComplete;\n this._positionTooltipElement(ref, tooltip, pos);\n this._shiftTooltipIntoViewport(tooltip);\n return;\n }\n }\n\n // 2) Fallback: pick the side with the most space\n let bestPosition: keyof typeof spaceAvailable = \"top\";\n let maxSpace = spaceAvailable.top;\n\n for (const pos of tryOrder) {\n if (spaceAvailable[pos] > maxSpace) {\n maxSpace = spaceAvailable[pos];\n bestPosition = pos;\n }\n }\n this._setInternalPosition(bestPosition);\n await this.updateComplete;\n this._positionTooltipElement(ref, tooltip, bestPosition);\n this._shiftTooltipIntoViewport(tooltip);\n }\n\n private _positionStartingBase() {\n const tooltip = this.shadowRoot?.querySelector(\n \".nys-tooltip__content\",\n ) as HTMLElement;\n\n if (!tooltip) return;\n\n tooltip.style.top = \"0px\";\n tooltip.style.left = \"0px\";\n }\n\n private _positionTooltipElement(\n ref: HTMLElement,\n tooltip: HTMLElement,\n bestPosition: typeof this._position,\n ) {\n const refRect = ref.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const margin = 8;\n\n let top = 0;\n let left = 0;\n\n switch (bestPosition) {\n case \"top\":\n top = refRect.top - tooltipRect.height - margin;\n left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;\n break;\n case \"bottom\":\n top = refRect.bottom + margin;\n left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;\n break;\n case \"left\":\n top = refRect.top + refRect.height / 2 - tooltipRect.height / 2;\n left = refRect.left - tooltipRect.width - margin;\n break;\n case \"right\":\n top = refRect.top + refRect.height / 2 - tooltipRect.height / 2;\n left = refRect.right + margin;\n break;\n default:\n // Default to top\n top = refRect.top - tooltipRect.height - margin;\n left = refRect.left + refRect.width / 2 - tooltipRect.width / 2;\n break;\n }\n\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${left}px`;\n }\n\n // Like storybook, some user's parent container may contain transform styling, which sets a new coordinate system.\n // This function reverse any container of that scale to not interfere with tooltip calculation.\n private applyInverseTransform() {\n document.querySelectorAll('div[scale=\"1\"]').forEach((el) => {\n (el as HTMLElement).style.transform = \"none\";\n });\n }\n\n private _setInternalPosition(bestPosition: typeof this._position) {\n this._internallyUpdatingPosition = true;\n this.position = bestPosition;\n this._internallyUpdatingPosition = false;\n }\n\n // Determines if text of tooltip over-extends outside of viewport edge and adjust tooltip for horizontal overflow\n private _shiftTooltipIntoViewport(tooltip: HTMLElement) {\n const ref = this._getReferenceElement();\n if (!ref) return;\n\n const refRect = ref.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n\n const refCenter = refRect.left + refRect.width / 2;\n\n const overflowLeft = tooltipRect.left < 0;\n const overflowRight = tooltipRect.right > window.innerWidth;\n\n // Tooltip is past the viewport edge, shift it inwards\n if (overflowLeft) {\n tooltip.style.left = \"10px\";\n tooltip.style.transform = \"none\";\n } else if (overflowRight) {\n tooltip.style.right = \"0px\";\n tooltip.style.left = \"auto\";\n tooltip.style.transform = \"none\";\n }\n\n // Recalculate tooltip rect after any shift\n const newTooltipRect = tooltip.getBoundingClientRect();\n\n // Arrow offset relative to tooltip width to maintain accuracy on zoom and out-of-bounds\n const arrowOffsetRatio =\n (refCenter - newTooltipRect.left) / newTooltipRect.width;\n const arrowOffsetPercent = Math.max(0, Math.min(1, arrowOffsetRatio)) * 100;\n\n tooltip.style.setProperty(\"--arrow-offset-x\", `${arrowOffsetPercent}%`);\n }\n\n // Reposition tooltip back to original set position (e.g. top, left, bottom, right) to avoid positioning issue base on last position\n private _resetTooltipPositioningStyles(tooltip: HTMLElement) {\n tooltip.style.left = \"\";\n tooltip.style.right = \"\";\n tooltip.style.top = \"\";\n tooltip.style.transform = \"\";\n tooltip.style.removeProperty(\"--arrow-offset-x\");\n }\n\n render() {\n return html`\n <div class=\"nys-tooltip__main\">\n ${this.text?.trim()\n ? html`<div\n id=${this.id}\n class=\"nys-tooltip__content\"\n role=\"tooltip\"\n aria-hidden=${this._active && !this._hideTimeout\n ? \"false\"\n : \"true\"}\n ?active=${this._active}\n style=\"visibility: ${this._active ? \"visible\" : \"hidden\"}; \"\n >\n <div class=\"nys-tooltip__inner\">${this.text}</div>\n <span class=\"nys-tooltip__arrow\"></span>\n </div>`\n : \"\"}\n </div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-tooltip\")) {\n customElements.define(\"nys-tooltip\", NysTooltip);\n}\n"],"names":["tooltipIdCounter","_NysTooltip","LitElement","ref","tooltip","isPointerInsideTooltip","isPointerInsideRef","isTouched","e","value","oldVal","changedProps","targetId","htmlElement","findInShadows","root","el","shadowElement","found","deeper","tagName","svg","position","refRect","tooltipRect","margin","spaceAvailable","tryOrder","userPosition","pos","bestPosition","maxSpace","top","left","refCenter","overflowLeft","overflowRight","newTooltipRect","arrowOffsetRatio","arrowOffsetPercent","html","unsafeCSS","styles","NysTooltip","__decorateClass","property","state"],"mappings":"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"}
|