diff --git a/.changeset/tooltip-no-base.md b/.changeset/tooltip-no-base.md new file mode 100644 index 0000000000..48e4fda014 --- /dev/null +++ b/.changeset/tooltip-no-base.md @@ -0,0 +1,4 @@ +--- +"@rhds/elements": patch +--- +``: remove dependency on `@patternfly/elements` diff --git a/elements/rh-tooltip/rh-tooltip.css b/elements/rh-tooltip/rh-tooltip.css index b87c14a8fc..c272697164 100644 --- a/elements/rh-tooltip/rh-tooltip.css +++ b/elements/rh-tooltip/rh-tooltip.css @@ -1,17 +1,80 @@ -/* WARNING: properties containing `__`are deprecated and will be removed */ - -:host([position="left"]), -:host([position="right"]) { - --_text-alignment: "start"; +:host { + display: inline; } -#rh-container { - display: contents; +#container { + display: inline-flex; + position: relative; + max-width: 100%; + /* WARNING: properties containing `__`are deprecated and will be removed */ --_floating-arrow-size: var(--rh-tooltip-arrow-size, var(--rh-tooltip__arrow--Width, 11px)); } +#tooltip, +#tooltip:after { + position: absolute; +} + +#tooltip { + display: block; + opacity: 0; + pointer-events: none; + z-index: 10000; + transition: opacity 300ms cubic-bezier(0.54, 1.5, 0.38, 1.11) 0s; + text-align: center; + word-break: break-word; + translate: var(--_floating-content-translate); + max-width: calc(100vw - 10px); + width: max-content; + top: 0; + left: 0; + will-change: opacity; +} + +#tooltip:after { + display: block; + content: ""; + rotate: 45deg; + width: var(--_floating-arrow-size); + height: var(--_floating-arrow-size); + will-change: left top right bottom; +} + +.open #tooltip { + opacity: 1; +} + +/* LEFT */ +.left #tooltip:after { right: calc(-0.5 * var(--_floating-arrow-size)); } +.left.center #tooltip:after { top: calc(50% - 0.5 * var(--_floating-arrow-size)); } +.left.start #tooltip:after { top: var(--_floating-arrow-size); } +.left.end #tooltip:after { bottom: var(--_floating-arrow-size); } + +/* TOP */ +.top #tooltip:after { top: calc(100% - 0.5 * var(--_floating-arrow-size)); } +.top.center #tooltip:after { right: calc(50% - 0.5 * var(--_floating-arrow-size)); } +.top.start #tooltip:after { left: var(--_floating-arrow-size); } +.top.end #tooltip:after { right: var(--_floating-arrow-size); } + +/* RIGHT */ +.right #tooltip:after { right: calc(100% - 0.5 * var(--_floating-arrow-size)); } +.right.center #tooltip:after { top: calc(50% - 0.5 * var(--_floating-arrow-size)); } +.right.start #tooltip:after { top: var(--_floating-arrow-size); } +.right.end #tooltip:after { bottom: var(--_floating-arrow-size); } + +/* BOTTOM */ +.bottom #tooltip:after { bottom: calc(100% - 0.5 * var(--_floating-arrow-size)); } +.bottom.center #tooltip:after { right: calc(50% - 0.5 * var(--_floating-arrow-size)); } +.bottom.start #tooltip:after { left: var(--_floating-arrow-size); } +.bottom.end #tooltip:after { right: var(--_floating-arrow-size); } + +:host([position="left"]), +:host([position="right"]) { + --_text-alignment: "start"; +} + .dark { --rh-tooltip-content-background-color: var(--rh-color-surface-lightest, #ffffff); --rh-tooltip-content-color: var(--rh-color-text-primary-on-light, #151515); @@ -22,6 +85,8 @@ max-width: var(--rh-tooltip-max-width, var(--rh-tooltip--MaxWidth, 18.75rem)); text-align: var(--_text-alignment, center); box-shadow: var(--rh-box-shadow-sm, 0 2px 4px 0 rgba(21, 21, 21, 0.2)); + + /* WARNING: properties containing `__`are deprecated and will be removed */ padding: var(--rh-tooltip-content-padding-block-start, var(--rh-tooltip__content--PaddingTop, var(--rh-space-lg, 16px))) @@ -31,8 +96,12 @@ var(--rh-tooltip__content--PaddingBottom, var(--rh-space-lg, 16px))) var(--rh-tooltip-content-padding-inline-start, var(--rh-tooltip__content--PaddingLeft, var(--rh-space-lg, 16px))); + + /* WARNING: properties containing `__`are deprecated and will be removed */ font-size: var(--rh-tooltip-content-font-size, var(--rh-tooltip__content--FontSize, var(--rh-font-size-body-text-sm, 0.875rem))); + + /* WARNING: properties containing `__`are deprecated and will be removed */ color: var(--rh-tooltip-content-color, var(--rh-tooltip__content--Color, var(--rh-color-text-primary-on-dark, #ffffff))); background-color: var(--rh-tooltip-content-background-color, @@ -40,6 +109,7 @@ } #tooltip:after { + /* WARNING: properties containing `__`are deprecated and will be removed */ background-color: var(--rh-tooltip-content-background-color, var(--rh-tooltip__content--BackgroundColor, var(--rh-color-surface-darkest, #151515))); } diff --git a/elements/rh-tooltip/rh-tooltip.ts b/elements/rh-tooltip/rh-tooltip.ts index e61cd3957c..563d54dfd6 100644 --- a/elements/rh-tooltip/rh-tooltip.ts +++ b/elements/rh-tooltip/rh-tooltip.ts @@ -1,42 +1,100 @@ -import type { Placement } from '@patternfly/pfe-core/controllers/floating-dom-controller.js'; - -import { html } from 'lit'; +import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; import { classMap } from 'lit/directives/class-map.js'; +import { styleMap } from 'lit/directives/style-map.js'; import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js'; -import { BaseTooltip } from '@patternfly/elements/pf-tooltip/BaseTooltip.js'; +import { + FloatingDOMController, + type Placement, +} from '@patternfly/pfe-core/controllers/floating-dom-controller.js'; import styles from './rh-tooltip.css'; +const ENTER_EVENTS = ['focusin', 'tap', 'click', 'mouseenter']; +const EXIT_EVENTS = ['focusout', 'blur', 'mouseleave']; + /** - * A tooltip is a floating text area that provides helpful or contextual information on hover, focus, or tap. + * A tooltip is a floating text area that provides helpful + * or contextual information on hover, focus, or tap. + * * @summary Reveals a small area of information on hover * - * @slot - Place element content here + * @slot - Place invoking element here, + * i.e. the element which when hovered the tooltip will display. + * Must be inline content. + * @slot content - Place tooltip content here. Overrides the `content` attribute. * + * @cssprop {} [--rh-tooltip-arrow-size=11px] + * @cssprop {} [--rh-tooltip-content-background-color=#ffffff] + * @cssprop {} [--rh-tooltip-content-color=#151515] + * @cssprop {} [--rh-tooltip-max-width=18.75rem] + * @cssprop {} [--rh-tooltip-content-padding-block-start=16px] + * @cssprop {} [--rh-tooltip-content-padding-inline-end=16px] + * @cssprop {} [--rh-tooltip-content-padding-block-end=16px] + * @cssprop {} [--rh-tooltip-content-padding-inline-start=16px] + * @cssprop { | | | } [--rh-tooltip-content-font-size=0.875rem] */ @customElement('rh-tooltip') -export class RhTooltip extends BaseTooltip { +export class RhTooltip extends LitElement { static readonly version = '{{version}}'; - static readonly styles = [...BaseTooltip.styles, styles]; - - @colorContextConsumer() private on?: ColorTheme; + static readonly styles = [styles]; + /** The position of the tooltip, relative to the invoking content */ @property() position: Placement = 'top'; + + /** Tooltip content. Overridden by the content slot */ @property() content?: string; + @colorContextConsumer() private on?: ColorTheme; + + #float = new FloatingDOMController(this, { + content: (): HTMLElement | undefined | null => this.shadowRoot?.querySelector('#tooltip'), + }); + + override connectedCallback(): void { + super.connectedCallback(); + ENTER_EVENTS.forEach(evt => this.addEventListener(evt, this.show)); + EXIT_EVENTS.forEach(evt => this.addEventListener(evt, this.hide)); + } + override render() { const { on = '' } = this; + const { alignment, anchor, open, styles } = this.#float; + const ariaHidden = String(!open) as 'true' | 'false'; + return html` -
- ${super.render()} +
+ + ${this.content}
`; } + + /** Show the tooltip */ + async show() { + await this.updateComplete; + const placement = this.position; + const offset = + !placement?.match(/top|bottom/) ? 15 + : { mainAxis: 15, alignmentAxis: -4 }; + await this.#float.show({ offset, placement }); + } + + /** Hide the tooltip */ + async hide() { + await this.#float.hide(); + } } declare global {