import gsap from "gsap"; const MODAL_MAX_Y_OFFSET = 106; const BUTTONS_MAX_Y_OFFSET = 20; const BUTTONS_TIME = 0.167; const MODAL_TIME = 0.25; export const useConfirmationModal = defineStore("confirmationModal", { state: () => ({ isOpen: false, text: "" as string | (() => string), aLabel: null as string | null, bLabel: null as string | null, onActivateA: null as (() => void) | null, onActivateB: null as (() => void) | null, onClosed: null as ((choice: "A" | "B") => void) | null, keepButtonsDown: null as boolean | ((choice: "A" | "B") => boolean) | null, offsetY: MODAL_MAX_Y_OFFSET, buttonsYOffset: 0, modalButtonsYOffset: BUTTONS_MAX_Y_OFFSET, isVisible: false, isClosing: false, }), actions: { open(options: { text: string | (() => string); aLabel?: string; bLabel?: string; onActivateA?: () => void; onActivateB?: () => void; onClosed?: (choice: "A" | "B") => void; keepButtonsDown?: boolean | ((choice: "A" | "B") => boolean); timeout?: number; }) { gsap.killTweensOf(this); this.text = options.text; this.aLabel = options.aLabel ?? null; this.bLabel = options.bLabel ?? null; this.onActivateA = options.onActivateA ?? null; this.onActivateB = options.onActivateB ?? null; this.onClosed = options.onClosed ?? null; this.keepButtonsDown = options.keepButtonsDown ?? null; this.isVisible = true; this.isClosing = false; this.isOpen = true; const { assets } = useAssets(); if ( options.aLabel !== undefined || options.bLabel !== undefined || options.onActivateA !== undefined || options.onActivateB !== undefined ) { assets.audio.menuOpen.play(); } else { assets.audio.menuConfirmed.play(); } gsap .timeline() // standard buttons down .fromTo( this, { buttonsYOffset: 0 }, { buttonsYOffset: BUTTONS_MAX_Y_OFFSET, duration: BUTTONS_TIME, ease: "none", }, ) // modal up .fromTo( this, { offsetY: MODAL_MAX_Y_OFFSET }, { offsetY: 0, duration: MODAL_TIME, ease: "none" }, ) // modal buttons up .fromTo( this, { modalButtonsYOffset: BUTTONS_MAX_Y_OFFSET }, { modalButtonsYOffset: 0, duration: BUTTONS_TIME, ease: "none" }, ) .call(() => { if (options.timeout) { setTimeout(() => { this.close("B"); }, options.timeout); } }); }, close(choice: "A" | "B") { if (!this.isVisible || this.isClosing) return; this.isClosing = true; if ( this.aLabel !== null || this.bLabel !== null || this.onActivateA !== null || this.onActivateB !== null ) { const { assets } = useAssets(); if (choice === "A") { assets.audio.menuConfirmed.play(); } else { assets.audio.menuError.play(); } } const keepButtonsDown = typeof this.keepButtonsDown === "function" ? this.keepButtonsDown(choice) : !!this.keepButtonsDown; const tl = gsap .timeline() // modal buttons down .to(this, { modalButtonsYOffset: BUTTONS_MAX_Y_OFFSET, duration: BUTTONS_TIME, ease: "none", }) // modal down .to(this, { offsetY: MODAL_MAX_Y_OFFSET, duration: MODAL_TIME, ease: "none", }); if (!keepButtonsDown) { // standard buttons up tl.to(this, { buttonsYOffset: 0, duration: BUTTONS_TIME, ease: "none", }); } tl.call(() => { const closedCallback = this.onClosed; this.$reset(); if (keepButtonsDown) { this.buttonsYOffset = BUTTONS_MAX_Y_OFFSET; } closedCallback?.(choice); }); }, }, });