@m3e/tooltip 1.0.0-rc.1 → 1.0.0-rc.2
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 +1 -2
- package/dist/custom-elements.json +2644 -9
- package/dist/html-custom-data.json +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +18 -18
- package/dist/index.min.js.map +1 -1
- package/package.json +4 -4
- package/cem.config.mjs +0 -16
- package/demo/index.html +0 -71
- package/dist/src/TooltipElement.d.ts +0 -104
- package/dist/src/TooltipElement.d.ts.map +0 -1
- package/dist/src/TooltipPosition.d.ts +0 -3
- package/dist/src/TooltipPosition.d.ts.map +0 -1
- package/dist/src/TooltipTouchGestures.d.ts +0 -3
- package/dist/src/TooltipTouchGestures.d.ts.map +0 -1
- package/dist/src/index.d.ts +0 -4
- package/dist/src/index.d.ts.map +0 -1
- package/eslint.config.mjs +0 -13
- package/rollup.config.js +0 -32
- package/src/TooltipElement.ts +0 -374
- package/src/TooltipPosition.ts +0 -2
- package/src/TooltipTouchGestures.ts +0 -2
- package/src/index.ts +0 -3
- package/tsconfig.json +0 -9
package/src/TooltipElement.ts
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import { css, CSSResultGroup, html, LitElement, PropertyValues, unsafeCSS } from "lit";
|
|
2
|
-
import { customElement, property, query } from "lit/decorators.js";
|
|
3
|
-
|
|
4
|
-
import { M3eAriaDescriber } from "@m3e/core/a11y";
|
|
5
|
-
import { M3ePlatform } from "@m3e/core/platform";
|
|
6
|
-
import { positionAnchor } from "@m3e/core/anchoring";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
AttachInternals,
|
|
10
|
-
DesignToken,
|
|
11
|
-
HoverController,
|
|
12
|
-
HtmlFor,
|
|
13
|
-
isDisabledMixin,
|
|
14
|
-
LongPressController,
|
|
15
|
-
Role,
|
|
16
|
-
} from "@m3e/core";
|
|
17
|
-
|
|
18
|
-
import { TooltipPosition } from "./TooltipPosition";
|
|
19
|
-
import { TooltipTouchGestures } from "./TooltipTouchGestures";
|
|
20
|
-
|
|
21
|
-
/** The space, in pixels, between the tooltip and anchor. */
|
|
22
|
-
const TOOLTIP_OFFSET = 4;
|
|
23
|
-
|
|
24
|
-
/** The default time, in milliseconds, before hiding a tooltip. */
|
|
25
|
-
const TOOLTIP_HIDE_DELAY = 200;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @summary
|
|
29
|
-
* Adds additional context to a button or other UI element.
|
|
30
|
-
*
|
|
31
|
-
* @description
|
|
32
|
-
* The `m3e-tooltip` component provides contextual information in response to user interaction, enhancing comprehension
|
|
33
|
-
* and reducing ambiguity. Tooltips are positioned relative to a target element and support configurable delays for
|
|
34
|
-
* show and hide behavior. The component is designed to reinforce accessibility and usability, especially in dense or
|
|
35
|
-
* icon-driven interfaces. Use the `for` attribute to designate the element for which to provide a tooltip.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* The following example illustrates connecting a tooltip to a button using the `for` attribute.
|
|
39
|
-
* ```html
|
|
40
|
-
* <m3e-icon-button id="button" aria-label="Back">
|
|
41
|
-
* <m3e-icon name="arrow_back"></m3e-icon>
|
|
42
|
-
* </m3e-icon-button>
|
|
43
|
-
* <m3e-tooltip for="button">Go Back</m3e-tooltip>
|
|
44
|
-
* ```
|
|
45
|
-
*
|
|
46
|
-
* @tag m3e-tooltip
|
|
47
|
-
*
|
|
48
|
-
* @slot - Renders the content of the tooltip.
|
|
49
|
-
*
|
|
50
|
-
* @attr disabled - Whether the element is disabled.
|
|
51
|
-
* @attr for - The query selector used to specify the element related to this element.
|
|
52
|
-
* @attr hide-delay - The amount of time, in milliseconds, before hiding the tooltip.
|
|
53
|
-
* @attr position - The position of the tooltip.
|
|
54
|
-
* @attr show-delay - The amount of time, in milliseconds, before showing the tooltip.
|
|
55
|
-
*
|
|
56
|
-
* @cssprop --m3e-tooltip-padding - Internal spacing of the tooltip container.
|
|
57
|
-
* @cssprop --m3e-tooltip-min-width - Minimum width of the tooltip.
|
|
58
|
-
* @cssprop --m3e-tooltip-max-width - Maximum width of the tooltip.
|
|
59
|
-
* @cssprop --m3e-tooltip-min-height - Minimum height of the tooltip container.
|
|
60
|
-
* @cssprop --m3e-tooltip-max-height - Maximum height of the tooltip.
|
|
61
|
-
* @cssprop --m3e-tooltip-shape - Border radius of the tooltip container.
|
|
62
|
-
* @cssprop --m3e-tooltip-container-color - Background color of the tooltip.
|
|
63
|
-
* @cssprop --m3e-tooltip-supporting-text-color - Text color of supporting text.
|
|
64
|
-
* @cssprop --m3e-tooltip-supporting-text-font-size - Font size of supporting text.
|
|
65
|
-
* @cssprop --m3e-tooltip-supporting-text-font-weight - Font weight of supporting text.
|
|
66
|
-
* @cssprop --m3e-tooltip-supporting-text-line-height - Line height of supporting text.
|
|
67
|
-
* @cssprop --m3e-tooltip-supporting-text-tracking - Letter spacing of supporting text.
|
|
68
|
-
*/
|
|
69
|
-
@customElement("m3e-tooltip")
|
|
70
|
-
export class M3eTooltipElement extends HtmlFor(AttachInternals(Role(LitElement, "none"))) {
|
|
71
|
-
/** The styles of the element. */
|
|
72
|
-
static override styles: CSSResultGroup = css`
|
|
73
|
-
:host {
|
|
74
|
-
display: contents;
|
|
75
|
-
}
|
|
76
|
-
.base {
|
|
77
|
-
position: absolute;
|
|
78
|
-
pointer-events: none;
|
|
79
|
-
margin: unset;
|
|
80
|
-
border: unset;
|
|
81
|
-
word-break: normal;
|
|
82
|
-
overflow-wrap: anywhere;
|
|
83
|
-
padding: var(--m3e-tooltip-padding, 0.25rem 0.5rem);
|
|
84
|
-
min-width: var(--m3e-tooltip-min-width, 2.5rem);
|
|
85
|
-
max-width: var(--m3e-tooltip-max-width, 12.5rem);
|
|
86
|
-
min-height: var(--m3e-tooltip-min-height, 1.5rem);
|
|
87
|
-
max-height: var(--m3e-tooltip-max-height, 40vh);
|
|
88
|
-
box-sizing: border-box;
|
|
89
|
-
overflow: hidden;
|
|
90
|
-
text-align: center;
|
|
91
|
-
border-radius: var(--m3e-tooltip-shape, ${DesignToken.shape.corner.extraSmall});
|
|
92
|
-
background-color: var(--m3e-tooltip-container-color, ${DesignToken.color.inverseSurface});
|
|
93
|
-
color: var(--m3e-tooltip-supporting-text-color, ${DesignToken.color.inverseOnSurface});
|
|
94
|
-
font-size: var(--m3e-tooltip-supporting-text-font-size, ${DesignToken.typescale.standard.body.small.fontSize});
|
|
95
|
-
font-weight: var(
|
|
96
|
-
--m3e-tooltip-supporting-text-font-weight,
|
|
97
|
-
${DesignToken.typescale.standard.body.small.fontWeight}
|
|
98
|
-
);
|
|
99
|
-
line-height: var(
|
|
100
|
-
--m3e-tooltip-supporting-text-line-height,
|
|
101
|
-
${DesignToken.typescale.standard.body.small.lineHeight}
|
|
102
|
-
);
|
|
103
|
-
letter-spacing: var(
|
|
104
|
-
--m3e-tooltip-supporting-text-tracking,
|
|
105
|
-
${DesignToken.typescale.standard.body.small.tracking}
|
|
106
|
-
);
|
|
107
|
-
visibility: hidden;
|
|
108
|
-
opacity: 0;
|
|
109
|
-
transform: scale(0.8);
|
|
110
|
-
transition: ${unsafeCSS(
|
|
111
|
-
`opacity ${DesignToken.motion.duration.short3} ${DesignToken.motion.easing.standard},
|
|
112
|
-
transform ${DesignToken.motion.duration.short3} ${DesignToken.motion.easing.standard},
|
|
113
|
-
overlay ${DesignToken.motion.duration.short3} ${DesignToken.motion.easing.standard} allow-discrete,
|
|
114
|
-
visibility ${DesignToken.motion.duration.short3} ${DesignToken.motion.easing.standard} allow-discrete`
|
|
115
|
-
)};
|
|
116
|
-
}
|
|
117
|
-
:host(.-multiline) .base {
|
|
118
|
-
text-align: start;
|
|
119
|
-
}
|
|
120
|
-
.base::backdrop {
|
|
121
|
-
background-color: transparent;
|
|
122
|
-
}
|
|
123
|
-
.base:not(:popover-open) {
|
|
124
|
-
visibility: hidden;
|
|
125
|
-
opacity: 0;
|
|
126
|
-
transform: scale(0.8);
|
|
127
|
-
}
|
|
128
|
-
.base:popover-open {
|
|
129
|
-
visibility: visible;
|
|
130
|
-
opacity: 1;
|
|
131
|
-
transform: scale(1);
|
|
132
|
-
}
|
|
133
|
-
@starting-style {
|
|
134
|
-
.base:popover-open {
|
|
135
|
-
opacity: 0;
|
|
136
|
-
transform: scale(0.8);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
@media (prefers-reduced-motion) {
|
|
140
|
-
.base {
|
|
141
|
-
transition: none;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
@media (forced-colors: active) {
|
|
145
|
-
.base {
|
|
146
|
-
background-color: Canvas;
|
|
147
|
-
color: CanvasText;
|
|
148
|
-
box-sizing: border-box;
|
|
149
|
-
border: 1px solid CanvasText;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
`;
|
|
153
|
-
|
|
154
|
-
/** @private */ private static readonly __openTooltips = new Array<M3eTooltipElement>();
|
|
155
|
-
|
|
156
|
-
/** @private */ @query(".base") private readonly _base!: HTMLElement;
|
|
157
|
-
/** @private */ #message?: string | null;
|
|
158
|
-
/** @private */ #for: HTMLElement | null = null;
|
|
159
|
-
/** @private */ #anchorCleanup?: () => void;
|
|
160
|
-
|
|
161
|
-
/** @private */ readonly #clickHandler = () => this.hide();
|
|
162
|
-
|
|
163
|
-
/** @private */
|
|
164
|
-
readonly #hoverController = new HoverController(this, {
|
|
165
|
-
target: null,
|
|
166
|
-
endDelay: TOOLTIP_HIDE_DELAY,
|
|
167
|
-
callback: (hovering) => {
|
|
168
|
-
if (hovering) {
|
|
169
|
-
this.show();
|
|
170
|
-
} else {
|
|
171
|
-
this.hide();
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
/** @private */
|
|
177
|
-
readonly #longPressController = new LongPressController(this, {
|
|
178
|
-
target: null,
|
|
179
|
-
callback: (pressed) => {
|
|
180
|
-
if (pressed) {
|
|
181
|
-
this.show();
|
|
182
|
-
} else {
|
|
183
|
-
this.hide();
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Whether the element is disabled.
|
|
190
|
-
* @default false
|
|
191
|
-
*/
|
|
192
|
-
@property({ type: Boolean, reflect: true }) disabled = false;
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* The position of the tooltip.
|
|
196
|
-
* @default "below"
|
|
197
|
-
*/
|
|
198
|
-
@property() position: TooltipPosition = "below";
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* The amount of time, in milliseconds, before showing the tooltip.
|
|
202
|
-
* @default 0
|
|
203
|
-
*/
|
|
204
|
-
@property({ attribute: "show-delay", type: Number }) get showDelay(): number {
|
|
205
|
-
return this.#hoverController.startDelay;
|
|
206
|
-
}
|
|
207
|
-
set showDelay(value: number) {
|
|
208
|
-
this.#hoverController.startDelay = value;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* The amount of time, in milliseconds, before hiding the tooltip.
|
|
213
|
-
* @default 200
|
|
214
|
-
*/
|
|
215
|
-
@property({ attribute: "hide-delay", type: Number }) get hideDelay(): number {
|
|
216
|
-
return this.#hoverController.endDelay;
|
|
217
|
-
}
|
|
218
|
-
set hideDelay(value: number) {
|
|
219
|
-
this.#hoverController.endDelay = value;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* The mode in which to handle touch gestures.
|
|
224
|
-
* @default "auto"
|
|
225
|
-
*/
|
|
226
|
-
@property({ attribute: "touch-gestures" }) touchGestures: TooltipTouchGestures = "auto";
|
|
227
|
-
|
|
228
|
-
/** @inheritdoc */
|
|
229
|
-
override attach(control: HTMLElement): void {
|
|
230
|
-
super.attach(control);
|
|
231
|
-
|
|
232
|
-
if (this.#message) {
|
|
233
|
-
M3eAriaDescriber.describe(control, this.#message);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (M3ePlatform.iOS || M3ePlatform.Android) {
|
|
237
|
-
this.#longPressController.observe(control);
|
|
238
|
-
this.#disableNativeGesturesIfNecessary();
|
|
239
|
-
} else {
|
|
240
|
-
this.#hoverController.observe(control);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
control.addEventListener("click", this.#clickHandler);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/** @inheritdoc */
|
|
247
|
-
override detach(): void {
|
|
248
|
-
if (this.control) {
|
|
249
|
-
if (this.#message) {
|
|
250
|
-
M3eAriaDescriber.removeDescription(this.control, this.#message);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
this.#hoverController.unobserve(this.control);
|
|
254
|
-
this.#longPressController.observe(this.control);
|
|
255
|
-
this.control.removeEventListener("click", this.#clickHandler);
|
|
256
|
-
this.hide();
|
|
257
|
-
}
|
|
258
|
-
super.detach();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** @inheritdoc */
|
|
262
|
-
override connectedCallback(): void {
|
|
263
|
-
super.connectedCallback();
|
|
264
|
-
this.ariaHidden = "true";
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** @inheritdoc */
|
|
268
|
-
protected override update(changedProperties: PropertyValues<this>): void {
|
|
269
|
-
super.update(changedProperties);
|
|
270
|
-
|
|
271
|
-
if (changedProperties.has("disabled") && this.disabled) {
|
|
272
|
-
this.hide();
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/** @inheritdoc */
|
|
277
|
-
protected override render(): unknown {
|
|
278
|
-
return html`<div class="base" popover="manual" @toggle="${this.#handleToggle}">
|
|
279
|
-
<slot @slotchange="${this.#handleSlotChange}"></slot>
|
|
280
|
-
</div>`;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Manually shows the tooltip.
|
|
285
|
-
* @returns {Promise<void>} A `Promise` that resolves when the tooltip is shown.
|
|
286
|
-
*/
|
|
287
|
-
async show(): Promise<void> {
|
|
288
|
-
if (!this.control || this.disabled || (isDisabledMixin(this.control) && this.control.disabled)) return;
|
|
289
|
-
|
|
290
|
-
M3eTooltipElement.__openTooltips.filter((x) => x !== this).forEach((x) => x.hide());
|
|
291
|
-
|
|
292
|
-
this._base.showPopover();
|
|
293
|
-
this.#anchorCleanup = await positionAnchor(this._base, this.control, {
|
|
294
|
-
position:
|
|
295
|
-
this.position === "above"
|
|
296
|
-
? "top"
|
|
297
|
-
: this.position === "below"
|
|
298
|
-
? "bottom"
|
|
299
|
-
: this.position === "before"
|
|
300
|
-
? "left"
|
|
301
|
-
: "right",
|
|
302
|
-
inline: true,
|
|
303
|
-
flip: true,
|
|
304
|
-
shift: true,
|
|
305
|
-
offset: TOOLTIP_OFFSET,
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
if (!M3eTooltipElement.__openTooltips.includes(this)) {
|
|
309
|
-
M3eTooltipElement.__openTooltips.push(this);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/** Manually hides the tooltip. */
|
|
314
|
-
hide(): void {
|
|
315
|
-
this._base.hidePopover();
|
|
316
|
-
this.#anchorCleanup?.();
|
|
317
|
-
this.#anchorCleanup = undefined;
|
|
318
|
-
this.#hoverController.clearDelays();
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** @private */
|
|
322
|
-
#handleSlotChange(): void {
|
|
323
|
-
if (this.isConnected && this.control) {
|
|
324
|
-
if (this.#message) {
|
|
325
|
-
M3eAriaDescriber.removeDescription(this.control, this.#message);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
this.#message = this.textContent;
|
|
329
|
-
|
|
330
|
-
if (this.#message) {
|
|
331
|
-
M3eAriaDescriber.describe(this.control, this.#message);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/** @private */
|
|
337
|
-
#handleToggle(e: ToggleEvent): void {
|
|
338
|
-
if (e.newState === "open") {
|
|
339
|
-
const multiline = this._base.getBoundingClientRect().height > parseFloat(getComputedStyle(this._base).minHeight);
|
|
340
|
-
this.classList.toggle("-multiline", multiline);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/** @private */
|
|
345
|
-
#disableNativeGesturesIfNecessary() {
|
|
346
|
-
if (this.touchGestures !== "off" && this.#for) {
|
|
347
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
348
|
-
const style: any = this.#for.style;
|
|
349
|
-
|
|
350
|
-
// If gestures are set to `auto`, we don't disable text selection on inputs and
|
|
351
|
-
// textareas, because it prevents the user from typing into them on iOS Safari.
|
|
352
|
-
|
|
353
|
-
if (this.touchGestures === "on" || (this.#for.nodeName !== "INPUT" && this.#for.nodeName !== "TEXTAREA")) {
|
|
354
|
-
style.userSelect = style.msUserSelect = style.webkitUserSelect = style.MozUserSelect = "none";
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// If we have `auto` gestures and the element uses native HTML dragging,
|
|
358
|
-
// we don't set `-webkit-user-drag` because it prevents the native behavior.
|
|
359
|
-
|
|
360
|
-
if (this.touchGestures === "on" || !this.#for.draggable) {
|
|
361
|
-
style.webkitUserDrag = "none";
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
style.touchAction = "none";
|
|
365
|
-
style.webkitTapHighlightColor = "transparent";
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
declare global {
|
|
371
|
-
interface HTMLElementTagNameMap {
|
|
372
|
-
"m3e-tooltip": M3eTooltipElement;
|
|
373
|
-
}
|
|
374
|
-
}
|
package/src/TooltipPosition.ts
DELETED
package/src/index.ts
DELETED