Skip to content

Commit

Permalink
Add tooltip animation props (#256)
Browse files Browse the repository at this point in the history
* Add delay to tooltip

* Reduce default delay

* Cleanup and documentation

* Cleanup and documentation

* Add default animation

* Documentation

* Update tooltip.ts
  • Loading branch information
jeffdaley authored Jul 17, 2023
1 parent 819bd18 commit ab2b7dc
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 24 deletions.
82 changes: 67 additions & 15 deletions web/app/modifiers/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { restartableTask, timeout } from "ember-concurrency";
import Ember from "ember";
import simpleTimeout from "hermes/utils/simple-timeout";

const DEFAULT_DELAY = Ember.testing ? 0 : 275;
const DEFAULT_DELAY = 0;
const DEFAULT_OPEN_DURATION = 200;

enum TooltipState {
Opening = "opening",
Expand All @@ -45,15 +46,19 @@ enum TooltipState {
* - Add animation logic
*/

interface TooltipModifierNamedArgs {
placement?: Placement;
stayOpenOnClick?: boolean;
delay?: number;
openDuration?: number;
_useTestDelay?: boolean;
}

interface TooltipModifierSignature {
Args: {
Element: HTMLElement;
Positional: [string];
Named: {
placement?: Placement;
delay?: number;
_useTestDelay?: boolean;
};
Named: TooltipModifierNamedArgs;
};
}

Expand Down Expand Up @@ -116,7 +121,8 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
@tracked _arrow: HTMLElement | null = null;

/**
* The tooltip element that is rendered in the DOM.
* The tooltip element that is rendered in the DOM and positioned
* relative to the reference element.
*/
@tracked tooltip: HTMLElement | null = null;

Expand All @@ -133,6 +139,18 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
*/
@tracked placement: Placement = "top";

/**
* The duration of the tooltip's open animation.
* Ignored in the testing environment.
*/
@tracked openDuration: number = DEFAULT_OPEN_DURATION;

/**
* The transform applied to the tooltip.
* Calculated based on placement; used for animations.
*/
@tracked transform: string = "none";

/**
* The delay before the tooltip is shown.
* Can be overridden with a `delay` argument.
Expand Down Expand Up @@ -208,7 +226,7 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>

this.updateState(TooltipState.Opening);

if (this.delay > 0) {
if (!Ember.testing && this.delay > 0) {
await timeout(this.delay);
}

Expand All @@ -221,7 +239,7 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
* Create the tooltip and set its attributes
*/
this.tooltip = document.createElement("div");
this.tooltip.classList.add("hermes-tooltip");
this.tooltip.classList.add("hermes-floating-ui-content", "hermes-tooltip");
this.tooltip.setAttribute("id", `tooltip-${this.id}`);
this.tooltip.setAttribute("role", "tooltip");

Expand Down Expand Up @@ -321,6 +339,16 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
});
}
});

if (this.placement.startsWith("top")) {
this.transform = "scale(.97) translateY(1px)";
} else if (this.placement.startsWith("bottom")) {
this.transform = "scale(.97) translateY(-1px)";
} else if (this.placement.startsWith("left")) {
this.transform = "scale(.97) translateX(1px)";
} else if (this.placement.startsWith("right")) {
this.transform = "scale(.97) translateX(-1px)";
}
};

this.floatingUICleanup = autoUpdate(
Expand All @@ -329,6 +357,31 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
updatePosition
);

if (!Ember.testing && this.openDuration) {
const fadeAnimation = this.tooltip.animate(
[{ opacity: 0 }, { opacity: 1 }],
{
duration: Ember.testing ? 0 : 50,
}
);
const transformAnimation = this.tooltip.animate(
[{ transform: this.transform }, { transform: "none" }],
{
duration: this.openDuration,
easing: "ease-in-out",
}
);
try {
await Promise.all([
fadeAnimation.finished,
transformAnimation.finished,
]);
} finally {
fadeAnimation.cancel();
transformAnimation.cancel();
}
}

this.updateState(TooltipState.Open);
});

Expand Down Expand Up @@ -409,12 +462,7 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
modify(
element: Element,
positional: [string],
named: {
placement?: Placement;
stayOpenOnClick?: boolean;
delay?: number;
_useTestDelay?: boolean;
}
named: TooltipModifierNamedArgs
) {
this._reference = element;
this._tooltipText = positional[0];
Expand All @@ -433,6 +481,10 @@ export default class TooltipModifier extends Modifier<TooltipModifierSignature>
this._useTestDelay = named._useTestDelay;
}

if (named.openDuration) {
this.openDuration = named.openDuration;
}

if (named.delay !== undefined) {
this.delay = named.delay;
} else {
Expand Down
10 changes: 1 addition & 9 deletions web/app/styles/components/tooltip.scss
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
.hermes-tooltip {
@apply absolute w-max bg-color-foreground-strong rounded py-2 px-2.5 text-color-foreground-high-contrast z-50;

/* These positioning styles are overwritten by FloatingUI,
* but they ensure that the tooltip isn't added to the bottom of the page,
* where it could cause a reflow. This is especially important because
* the Google Docs iframe responds to layout changes and might
* otherwise jitter when a tooltip opened.
*/
@apply top-0 left-0;
@apply z-50 w-max bg-color-foreground-strong rounded py-2 px-2.5 text-color-foreground-high-contrast;

.arrow {
@apply absolute bg-color-foreground-strong h-2 w-2 -z-10 pointer-events-none;
Expand Down

0 comments on commit ab2b7dc

Please sign in to comment.