mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-09-20 15:26:15 +08:00
refactor: improve animated modal code
allow global definition of custom show and hide animations these can still be overridden at each show/hide call store global default animation duration
This commit is contained in:
parent
1d7fde25a3
commit
a2bf62edd4
|
@ -1,32 +1,44 @@
|
|||
import { isPopupVisible } from "../utils/misc";
|
||||
import * as Skeleton from "./skeleton";
|
||||
|
||||
type CustomAnimation = {
|
||||
from: Record<string, string>;
|
||||
to: Record<string, string>;
|
||||
easing?: string;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
type CustomWrapperAndModalAnimations = {
|
||||
wrapper?: CustomAnimation;
|
||||
modal?: CustomAnimation;
|
||||
};
|
||||
|
||||
type ConstructorCustomAnimations = {
|
||||
show?: CustomWrapperAndModalAnimations;
|
||||
hide?: CustomWrapperAndModalAnimations;
|
||||
};
|
||||
|
||||
type ShowHideOptions = {
|
||||
animationMode?: "none" | "both" | "modalOnly";
|
||||
animationDurationMs?: number;
|
||||
customAnimation?: {
|
||||
wrapper?: {
|
||||
from: Record<string, string>;
|
||||
to: Record<string, string>;
|
||||
easing?: string;
|
||||
};
|
||||
modal?: {
|
||||
from: Record<string, string>;
|
||||
to: Record<string, string>;
|
||||
easing?: string;
|
||||
};
|
||||
};
|
||||
customAnimation?: CustomWrapperAndModalAnimations;
|
||||
beforeAnimation?: (modal: HTMLElement) => Promise<void>;
|
||||
afterAnimation?: (modal: HTMLElement) => Promise<void>;
|
||||
};
|
||||
|
||||
export class AnimatedModal {
|
||||
const DEFAULT_ANIMATION_DURATION = 125;
|
||||
|
||||
export default class AnimatedModal {
|
||||
private wrapperEl: HTMLDialogElement;
|
||||
private modalEl: HTMLElement;
|
||||
private wrapperId: string;
|
||||
private open = false;
|
||||
private customShowAnimations: CustomWrapperAndModalAnimations | undefined;
|
||||
private customHideAnimations: CustomWrapperAndModalAnimations | undefined;
|
||||
|
||||
constructor(wrapperId: string) {
|
||||
constructor(
|
||||
wrapperId: string,
|
||||
customAnimations?: ConstructorCustomAnimations
|
||||
) {
|
||||
if (wrapperId.startsWith("#")) {
|
||||
wrapperId = wrapperId.slice(1);
|
||||
}
|
||||
|
@ -57,6 +69,8 @@ export class AnimatedModal {
|
|||
this.wrapperId = wrapperId;
|
||||
this.wrapperEl = dialogElement;
|
||||
this.modalEl = modalElement;
|
||||
this.customShowAnimations = customAnimations?.show;
|
||||
this.customHideAnimations = customAnimations?.hide;
|
||||
|
||||
this.wrapperEl.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && isPopupVisible(this.wrapperId)) {
|
||||
|
@ -96,34 +110,48 @@ export class AnimatedModal {
|
|||
|
||||
await options?.beforeAnimation?.(this.modalEl);
|
||||
|
||||
const modalAnimation =
|
||||
options?.customAnimation?.modal ?? this.customShowAnimations?.modal;
|
||||
const modalAnimationDuration =
|
||||
options?.customAnimation?.modal?.durationMs ??
|
||||
this.customShowAnimations?.modal?.durationMs ??
|
||||
DEFAULT_ANIMATION_DURATION;
|
||||
const wrapperAnimation = options?.customAnimation?.wrapper ??
|
||||
this.customShowAnimations?.wrapper ?? {
|
||||
from: { opacity: "0" },
|
||||
to: { opacity: "1" },
|
||||
easing: "swing",
|
||||
};
|
||||
const wrapperAnimationDuration =
|
||||
options?.customAnimation?.wrapper?.durationMs ??
|
||||
this.customShowAnimations?.wrapper?.durationMs ??
|
||||
DEFAULT_ANIMATION_DURATION;
|
||||
|
||||
const animationMode = options?.animationMode ?? "both";
|
||||
const animationDuration = options?.animationDurationMs ?? 125;
|
||||
|
||||
$(this.modalEl).stop(true, false);
|
||||
$(this.wrapperEl).stop(true, false);
|
||||
|
||||
if (animationMode === "both" || animationMode === "none") {
|
||||
if (options?.customAnimation?.modal?.from) {
|
||||
$(this.modalEl).css(options.customAnimation.modal.from);
|
||||
if (modalAnimation?.from) {
|
||||
$(this.modalEl).css(modalAnimation.from);
|
||||
$(this.modalEl).animate(
|
||||
options.customAnimation.modal.to,
|
||||
animationMode === "none" ? 0 : animationDuration,
|
||||
options.customAnimation.modal.easing ?? "swing"
|
||||
modalAnimation.to,
|
||||
animationMode === "none" ? 0 : modalAnimationDuration,
|
||||
modalAnimation.easing ?? "swing"
|
||||
);
|
||||
} else {
|
||||
$(this.modalEl).css("opacity", "1");
|
||||
}
|
||||
|
||||
if (options?.customAnimation?.wrapper?.from) {
|
||||
$(this.wrapperEl).css(options.customAnimation.wrapper.from);
|
||||
}
|
||||
$(this.wrapperEl).css(wrapperAnimation.from);
|
||||
$(this.wrapperEl)
|
||||
.removeClass("hidden")
|
||||
.css("opacity", "0")
|
||||
.animate(
|
||||
options?.customAnimation?.wrapper?.to ?? { opacity: 1 },
|
||||
animationMode === "none" ? 0 : animationDuration,
|
||||
options?.customAnimation?.wrapper?.easing ?? "swing",
|
||||
wrapperAnimation.to ?? { opacity: 1 },
|
||||
animationMode === "none" ? 0 : wrapperAnimationDuration,
|
||||
wrapperAnimation.easing ?? "swing",
|
||||
async () => {
|
||||
this.wrapperEl.focus();
|
||||
await options?.afterAnimation?.(this.modalEl);
|
||||
|
@ -133,15 +161,15 @@ export class AnimatedModal {
|
|||
} else if (animationMode === "modalOnly") {
|
||||
$(this.wrapperEl).removeClass("hidden").css("opacity", "1");
|
||||
|
||||
if (options?.customAnimation?.modal?.from) {
|
||||
$(this.modalEl).css(options.customAnimation.modal.from);
|
||||
if (modalAnimation?.from) {
|
||||
$(this.modalEl).css(modalAnimation.from);
|
||||
} else {
|
||||
$(this.modalEl).css("opacity", "0");
|
||||
}
|
||||
$(this.modalEl).animate(
|
||||
options?.customAnimation?.modal?.to ?? { opacity: 1 },
|
||||
animationDuration,
|
||||
options?.customAnimation?.modal?.easing ?? "swing",
|
||||
modalAnimation?.to ?? { opacity: 1 },
|
||||
modalAnimationDuration,
|
||||
modalAnimation?.easing ?? "swing",
|
||||
async () => {
|
||||
this.wrapperEl.focus();
|
||||
await options?.afterAnimation?.(this.modalEl);
|
||||
|
@ -159,33 +187,46 @@ export class AnimatedModal {
|
|||
|
||||
await options?.beforeAnimation?.(this.modalEl);
|
||||
|
||||
const modalAnimation =
|
||||
options?.customAnimation?.modal ?? this.customHideAnimations?.modal;
|
||||
const modalAnimationDuration =
|
||||
options?.customAnimation?.modal?.durationMs ??
|
||||
this.customHideAnimations?.modal?.durationMs ??
|
||||
DEFAULT_ANIMATION_DURATION;
|
||||
const wrapperAnimation = options?.customAnimation?.wrapper ??
|
||||
this.customHideAnimations?.wrapper ?? {
|
||||
from: { opacity: "1" },
|
||||
to: { opacity: "0" },
|
||||
easing: "swing",
|
||||
};
|
||||
const wrapperAnimationDuration =
|
||||
options?.customAnimation?.wrapper?.durationMs ??
|
||||
this.customHideAnimations?.wrapper?.durationMs ??
|
||||
DEFAULT_ANIMATION_DURATION;
|
||||
const animationMode = options?.animationMode ?? "both";
|
||||
const animationDuration = options?.animationDurationMs ?? 125;
|
||||
|
||||
$(this.modalEl).stop(true, false);
|
||||
$(this.wrapperEl).stop(true, false);
|
||||
|
||||
if (animationMode === "both" || animationMode === "none") {
|
||||
if (options?.customAnimation?.modal?.from) {
|
||||
$(this.modalEl).css(options.customAnimation.modal.from);
|
||||
if (modalAnimation?.from) {
|
||||
$(this.modalEl).css(modalAnimation.from);
|
||||
$(this.modalEl).animate(
|
||||
options.customAnimation.modal.to,
|
||||
animationMode === "none" ? 0 : animationDuration,
|
||||
options.customAnimation.modal.easing ?? "swing"
|
||||
modalAnimation.to,
|
||||
animationMode === "none" ? 0 : modalAnimationDuration,
|
||||
modalAnimation.easing ?? "swing"
|
||||
);
|
||||
} else {
|
||||
$(this.modalEl).css("opacity", "0");
|
||||
$(this.modalEl).css("opacity", "1");
|
||||
}
|
||||
|
||||
if (options?.customAnimation?.wrapper?.from) {
|
||||
$(this.wrapperEl).css(options.customAnimation.wrapper.from);
|
||||
}
|
||||
$(this.wrapperEl).css(wrapperAnimation.from);
|
||||
$(this.wrapperEl)
|
||||
.css("opacity", "1")
|
||||
.animate(
|
||||
options?.customAnimation?.wrapper?.to ?? { opacity: 0 },
|
||||
animationMode === "none" ? 0 : animationDuration,
|
||||
options?.customAnimation?.wrapper?.easing ?? "swing",
|
||||
wrapperAnimation?.to ?? { opacity: 0 },
|
||||
animationMode === "none" ? 0 : wrapperAnimationDuration,
|
||||
wrapperAnimation?.easing ?? "swing",
|
||||
async () => {
|
||||
this.wrapperEl.close();
|
||||
this.wrapperEl.classList.add("hidden");
|
||||
|
@ -198,15 +239,15 @@ export class AnimatedModal {
|
|||
} else if (animationMode === "modalOnly") {
|
||||
$(this.wrapperEl).removeClass("hidden").css("opacity", "1");
|
||||
|
||||
if (options?.customAnimation?.modal?.from) {
|
||||
$(this.modalEl).css(options.customAnimation.modal.from);
|
||||
if (modalAnimation?.from) {
|
||||
$(this.modalEl).css(modalAnimation.from);
|
||||
} else {
|
||||
$(this.modalEl).css("opacity", "1");
|
||||
}
|
||||
$(this.modalEl).animate(
|
||||
options?.customAnimation?.modal?.to ?? { opacity: 0 },
|
||||
animationDuration,
|
||||
options?.customAnimation?.modal?.easing ?? "swing",
|
||||
modalAnimation?.to ?? { opacity: 0 },
|
||||
modalAnimationDuration,
|
||||
modalAnimation?.easing ?? "swing",
|
||||
async () => {
|
||||
this.wrapperEl.close();
|
||||
$(this.wrapperEl).addClass("hidden").css("opacity", "0");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as UpdateConfig from "../config";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import { AnimatedModal } from "./animated-modal";
|
||||
import AnimatedModal from "./animated-modal";
|
||||
|
||||
type State = {
|
||||
mode: "import" | "export";
|
||||
|
|
Loading…
Reference in a new issue