kopia lustrzana https://github.com/shoelace-style/shoelace
update tooltip
rodzic
3fce846a8d
commit
d720121044
|
@ -16,10 +16,13 @@ The most elegant solution I found was to use the [Web Animations API](https://de
|
||||||
|
|
||||||
- 🚨 BREAKING: changed `left` and `right` placements to `start` and `end` in `sl-drawer`
|
- 🚨 BREAKING: changed `left` and `right` placements to `start` and `end` in `sl-drawer`
|
||||||
- 🚨 BREAKING: changed `left` and `right` placements to `start` and `end` in `sl-tab-group`
|
- 🚨 BREAKING: changed `left` and `right` placements to `start` and `end` in `sl-tab-group`
|
||||||
|
- 🚨 BREAKING: removed `--hide-duration`, `--hide-timing-function`, `--show-duration`, and `--show-timing-function` custom properties from `sl-tooltip` (use the Animation Registry instead)
|
||||||
- Added the Animation Registry
|
- Added the Animation Registry
|
||||||
- Fixed a bug where removing `sl-dropdown` from the DOM and adding it back destroyed the popover reference [#443](https://github.com/shoelace-style/shoelace/issues/443)
|
- Fixed a bug where removing `sl-dropdown` from the DOM and adding it back destroyed the popover reference [#443](https://github.com/shoelace-style/shoelace/issues/443)
|
||||||
- Updated animations for `sl-alert`, `sl-dialog`, `sl-drawer`, and `sl-dropdown` to use the Animation Registry instead of CSS transitions
|
- Updated animations for `sl-alert`, `sl-dialog`, `sl-drawer`, `sl-dropdown`, and `sl-tooltip` to use the Animation Registry instead of CSS transitions
|
||||||
- Improved a11y by respecting `prefers-reduced-motion` for all show/hide animations
|
- Improved a11y by respecting `prefers-reduced-motion` for all show/hide animations
|
||||||
|
- Improved `--show-delay` and `--hide-delay` behavior in `sl-tooltip` so they only apply on hover
|
||||||
|
- Removed the internal popover utility
|
||||||
|
|
||||||
## 2.0.0-beta.40
|
## 2.0.0-beta.40
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,8 @@
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
--max-width: 20rem;
|
--max-width: 20rem;
|
||||||
--hide-delay: 0s;
|
--hide-delay: 0ms;
|
||||||
--hide-duration: 0.125s;
|
--show-delay: 150ms;
|
||||||
--hide-timing-function: ease;
|
|
||||||
--show-delay: 0.125s;
|
|
||||||
--show-duration: 0.125s;
|
|
||||||
--show-timing-function: ease;
|
|
||||||
|
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
@ -27,14 +23,7 @@
|
||||||
font-weight: var(--sl-tooltip-font-weight);
|
font-weight: var(--sl-tooltip-font-weight);
|
||||||
line-height: var(--sl-tooltip-line-height);
|
line-height: var(--sl-tooltip-line-height);
|
||||||
color: var(--sl-tooltip-color);
|
color: var(--sl-tooltip-color);
|
||||||
opacity: 0;
|
|
||||||
padding: var(--sl-tooltip-padding);
|
padding: var(--sl-tooltip-padding);
|
||||||
transform: scale(0.8);
|
|
||||||
transform-origin: bottom;
|
|
||||||
transition-property: opacity, transform;
|
|
||||||
transition-delay: var(--hide-delay);
|
|
||||||
transition-duration: var(--hide-duration);
|
|
||||||
transition-timing-function: var(--hide-timing-function);
|
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -60,14 +49,6 @@
|
||||||
&[data-popper-placement^='right'] .tooltip {
|
&[data-popper-placement^='right'] .tooltip {
|
||||||
transform-origin: left;
|
transform-origin: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.popover-visible .tooltip {
|
|
||||||
opacity: 1;
|
|
||||||
transform: none;
|
|
||||||
transition-delay: var(--show-delay);
|
|
||||||
transition-duration: var(--show-duration);
|
|
||||||
transition-timing-function: var(--show-timing-function);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrow + bottom
|
// Arrow + bottom
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { LitElement, html, unsafeCSS } from 'lit';
|
import { LitElement, html, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, query } from 'lit/decorators';
|
import { customElement, property, query } from 'lit/decorators';
|
||||||
import { classMap } from 'lit-html/directives/class-map';
|
import { classMap } from 'lit-html/directives/class-map';
|
||||||
|
import { animateTo, parseDuration, stopAnimations } from '../../internal/animate';
|
||||||
|
import { Instance as PopperInstance, createPopper } from '@popperjs/core/dist/esm';
|
||||||
import { event, EventEmitter, watch } from '../../internal/decorators';
|
import { event, EventEmitter, watch } from '../../internal/decorators';
|
||||||
import Popover from '../../internal/popover';
|
import { setDefaultAnimation, getAnimation } from '../../utilities/animation-registry';
|
||||||
import styles from 'sass:./tooltip.scss';
|
import styles from 'sass:./tooltip.scss';
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
@ -16,13 +18,12 @@ let id = 0;
|
||||||
*
|
*
|
||||||
* @part base - The component's base wrapper.
|
* @part base - The component's base wrapper.
|
||||||
*
|
*
|
||||||
* @customProperty --hide-delay - The amount of time to wait before hiding the tooltip.
|
|
||||||
* @customProperty --hide-duration - The amount of time the hide transition takes to complete.
|
|
||||||
* @customProperty --hide-timing-function - The timing function (easing) to use for the hide transition.
|
|
||||||
* @customProperty --max-width - The maximum width of the tooltip.
|
* @customProperty --max-width - The maximum width of the tooltip.
|
||||||
* @customProperty --show-delay - The amount of time to wait before showing the tooltip.
|
* @customProperty --hide-delay - The amount of time to wait before hiding the tooltip (hover only).
|
||||||
* @customProperty --show-duration - The amount of time the show transition takes to complete.
|
* @customProperty --show-delay - The amount of time to wait before showing the tooltip (hover only).
|
||||||
* @customProperty --show-timing-function - The timing function (easing) to use for the show transition.
|
*
|
||||||
|
* @animation tooltip.show - The animation to use when showing the tooltip.
|
||||||
|
* @animation tooltip.hide - The animation to use when hiding the tooltip.
|
||||||
*/
|
*/
|
||||||
@customElement('sl-tooltip')
|
@customElement('sl-tooltip')
|
||||||
export default class SlTooltip extends LitElement {
|
export default class SlTooltip extends LitElement {
|
||||||
|
@ -32,10 +33,10 @@ export default class SlTooltip extends LitElement {
|
||||||
@query('.tooltip') tooltip: HTMLElement;
|
@query('.tooltip') tooltip: HTMLElement;
|
||||||
|
|
||||||
private componentId = `tooltip-${++id}`;
|
private componentId = `tooltip-${++id}`;
|
||||||
|
private hasInitialized = false;
|
||||||
private target: HTMLElement;
|
private target: HTMLElement;
|
||||||
private popover: Popover;
|
private popover: PopperInstance;
|
||||||
|
private hoverTimeout: any;
|
||||||
private isVisible = false;
|
|
||||||
|
|
||||||
/** The tooltip's content. Alternatively, you can use the content slot. */
|
/** The tooltip's content. Alternatively, you can use the content slot. */
|
||||||
@property() content = '';
|
@property() content = '';
|
||||||
|
@ -100,9 +101,8 @@ export default class SlTooltip extends LitElement {
|
||||||
this.handleMouseOut = this.handleMouseOut.bind(this);
|
this.handleMouseOut = this.handleMouseOut.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
async firstUpdated() {
|
||||||
this.target = this.getTarget();
|
this.target = this.getTarget();
|
||||||
this.popover = new Popover(this.target, this.positioner);
|
|
||||||
this.syncOptions();
|
this.syncOptions();
|
||||||
|
|
||||||
this.addEventListener('blur', this.handleBlur, true);
|
this.addEventListener('blur', this.handleBlur, true);
|
||||||
|
@ -112,16 +112,20 @@ export default class SlTooltip extends LitElement {
|
||||||
this.addEventListener('mouseover', this.handleMouseOver);
|
this.addEventListener('mouseover', this.handleMouseOver);
|
||||||
this.addEventListener('mouseout', this.handleMouseOut);
|
this.addEventListener('mouseout', this.handleMouseOut);
|
||||||
|
|
||||||
// Show on init if open
|
// Set initial visibility
|
||||||
this.positioner.hidden = !this.open;
|
this.tooltip.hidden = !this.open;
|
||||||
if (this.open) {
|
|
||||||
this.show();
|
// Set the initialized flag after the first update is complete
|
||||||
}
|
await this.updateComplete;
|
||||||
|
this.hasInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
|
|
||||||
|
if (this.popover) {
|
||||||
this.popover.destroy();
|
this.popover.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
this.removeEventListener('blur', this.handleBlur, true);
|
this.removeEventListener('blur', this.handleBlur, true);
|
||||||
this.removeEventListener('focus', this.handleFocus, true);
|
this.removeEventListener('focus', this.handleFocus, true);
|
||||||
|
@ -132,27 +136,56 @@ export default class SlTooltip extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shows the tooltip. */
|
/** Shows the tooltip. */
|
||||||
show() {
|
async show() {
|
||||||
// Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
|
// Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
|
||||||
if (this.isVisible || this.disabled) {
|
if (!this.hasInitialized || this.open) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.popover) {
|
||||||
|
this.popover.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
const slShow = this.slShow.emit();
|
const slShow = this.slShow.emit();
|
||||||
if (slShow.defaultPrevented) {
|
if (slShow.defaultPrevented) {
|
||||||
this.open = false;
|
this.open = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isVisible = true;
|
|
||||||
this.open = true;
|
this.open = true;
|
||||||
this.popover.show();
|
|
||||||
|
await stopAnimations(this.tooltip);
|
||||||
|
|
||||||
|
this.popover = createPopper(this.target, this.positioner, {
|
||||||
|
placement: this.placement,
|
||||||
|
strategy: 'absolute',
|
||||||
|
modifiers: [
|
||||||
|
{
|
||||||
|
name: 'flip',
|
||||||
|
options: {
|
||||||
|
boundary: 'viewport'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'offset',
|
||||||
|
options: {
|
||||||
|
offset: [this.skidding, this.distance]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tooltip.hidden = false;
|
||||||
|
const { keyframes, options } = getAnimation(this, 'tooltip.show');
|
||||||
|
await animateTo(this.tooltip, keyframes, options);
|
||||||
|
|
||||||
|
this.slAfterShow.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shows the tooltip. */
|
/** Shows the tooltip. */
|
||||||
hide() {
|
async hide() {
|
||||||
// Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
|
// Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
|
||||||
if (!this.isVisible) {
|
if (!this.hasInitialized || !this.open) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,9 +195,14 @@ export default class SlTooltip extends LitElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isVisible = false;
|
|
||||||
this.open = false;
|
this.open = false;
|
||||||
this.popover.hide();
|
|
||||||
|
await stopAnimations(this.tooltip);
|
||||||
|
const { keyframes, options } = getAnimation(this, 'tooltip.hide');
|
||||||
|
await animateTo(this.tooltip, keyframes, options);
|
||||||
|
this.tooltip.hidden = true;
|
||||||
|
|
||||||
|
this.slAfterHide.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
getTarget() {
|
getTarget() {
|
||||||
|
@ -208,13 +246,17 @@ export default class SlTooltip extends LitElement {
|
||||||
|
|
||||||
handleMouseOver() {
|
handleMouseOver() {
|
||||||
if (this.hasTrigger('hover')) {
|
if (this.hasTrigger('hover')) {
|
||||||
this.show();
|
const delay = parseDuration(getComputedStyle(this).getPropertyValue('--show-delay'));
|
||||||
|
clearTimeout(this.hoverTimeout);
|
||||||
|
this.hoverTimeout = setTimeout(() => this.show(), delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseOut() {
|
handleMouseOut() {
|
||||||
if (this.hasTrigger('hover')) {
|
if (this.hasTrigger('hover')) {
|
||||||
this.hide();
|
const delay = parseDuration(getComputedStyle(this).getPropertyValue('--hide-delay'));
|
||||||
|
clearTimeout(this.hoverTimeout);
|
||||||
|
this.hoverTimeout = setTimeout(() => this.hide(), delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,11 +300,21 @@ export default class SlTooltip extends LitElement {
|
||||||
if (this.popover) {
|
if (this.popover) {
|
||||||
this.popover.setOptions({
|
this.popover.setOptions({
|
||||||
placement: this.placement,
|
placement: this.placement,
|
||||||
distance: this.distance,
|
strategy: 'absolute',
|
||||||
skidding: this.skidding,
|
modifiers: [
|
||||||
transitionElement: this.tooltip,
|
{
|
||||||
onAfterHide: () => this.slAfterHide.emit(),
|
name: 'flip',
|
||||||
onAfterShow: () => this.slAfterShow.emit()
|
options: {
|
||||||
|
boundary: 'viewport'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'offset',
|
||||||
|
options: {
|
||||||
|
offset: [this.skidding, this.distance]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,6 +341,19 @@ export default class SlTooltip extends LitElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDefaultAnimation('tooltip.show', {
|
||||||
|
keyframes: [{ opacity: 0 }, { opacity: 0, transform: 'scale(0.8)' }, { opacity: 1, transform: 'scale(1)' }],
|
||||||
|
options: { duration: 150, easing: 'ease' }
|
||||||
|
});
|
||||||
|
|
||||||
|
setDefaultAnimation('tooltip.hide', {
|
||||||
|
keyframes: [
|
||||||
|
{ opacity: 1, transform: 'scale(1)' },
|
||||||
|
{ opacity: 0, transform: 'scale(0.8)' }
|
||||||
|
],
|
||||||
|
options: { duration: 150, easing: 'ease' }
|
||||||
|
});
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
'sl-tooltip': SlTooltip;
|
'sl-tooltip': SlTooltip;
|
||||||
|
|
Ładowanie…
Reference in New Issue