<template>
	<div class="c2-modal" :style="cssVars">
		<button v-if="!hideOpenBtn" :class="openBtnClasses" @click="openModal">
			<slot name="open-btn" />
		</button>

		<transition name="fade">
			<div v-if="open" class="overlay" :class="{ open: open }"></div>
		</transition>
		<transition :name="transitionType">
			<div v-if="open" class="modal-container">
				<div
					v-if="open"
					ref="modalWrapper"
					class="modal"
					:class="[{ open: open }, wrapperClasses, transitionType]"
					:aria-hidden="[!open]"
				>
					<div :class="headerClasses">
						<slot name="header" />

						<button
							ref="closeModalBtn"
							:class="closeBtnClasses"
							:tabindex="tabIndex"
							@click="closeModal"
						>
							<slot name="close-btn" />
						</button>
					</div>
					<slot name="body" />
				</div>
			</div>
		</transition>
	</div>
</template>

<script>
export default {
	name: "C2Modal",
	props: {
		backgroundOverlayColor: {
			type: String,
			default: "rgba(0,0,0,0.7)",
		},
		backgroundColor: {
			type: String,
			default: "#fff",
		},
		closeBtnClasses: {
			type: String,
			default: "",
		},
		openBtnClasses: {
			type: String,
			default: "",
		},
		wrapperClasses: {
			type: String,
			default: "",
		},
		transitionType: {
			type: String,
			default: "zoom",
		},
		headerClasses: {
			type: String,
			default: "",
		},
		maxWidth: {
			type: Number,
			default: 1024,
		},
		hideOpenBtn: {
			type: Boolean,
			default: false,
		},
	},
	data() {
		return {
			open: false,
			tabIndex: -1,
		};
	},
	computed: {
		cssVars() {
			return {
				"--c2ModalBackgroundOverlayColor": this.backgroundOverlayColor,
				"--c2ModalBackgroundColor": this.backgroundColor,
				"--c2ModalMaxWidth": `${Math.abs(this.maxWidth)}px`,
			};
		},
	},
	mounted() {
		document.addEventListener("DOMContentLoaded", this.trapFocus());
	},
	watch: {
		open() {
			if (this.open) {
				document
					.querySelector("body")
					.classList.add("overflow-hidden", "max-h-screen");
			} else {
				document
					.querySelector("body")
					.classList.remove("overflow-hidden", "max-h-screen");
			}
		},
	},
	methods: {
		trapFocus() {
			// add all the elements inside modal which you want to make focusable
			const self = this;

			document.addEventListener("keydown", function (e) {
				if (self.open) {
					const isTabPressed = e.key === "Tab" || e.keyCode === 9;
					const isEsc = e.keyCode === 27;

					if (isEsc) {
						self.closeModal();
						return;
					}

					if (!isTabPressed) {
						return;
					}

					if (
						e.shiftKey &&
						document.activeElement === self.$refs.closeModalBtn
					) {
						self.$refs.closeModalBtn.focus();
						e.preventDefault();
					}

					if (
						!self.$refs.modalWrapper.contains(
							document.activeElement
						)
					) {
						self.$refs.closeModalBtn.focus();
						e.preventDefault();
					}
				}
			});
		},
		openModal() {
			this.open = true;
			this.tabIndex = 0;
			this.$emit("modalOpened");
			this.$nextTick(() => {
				if (this.$refs.closeModalBtn) {
					this.$refs.closeModalBtn.focus();
				}
			});
		},
		closeModal() {
			this.open = false;
			this.tabIndex = -1;
			this.$emit("modalClosed");
		},
	},
};
</script>

<style lang="scss">
.c2-modal {
	position: relative;
	height: 0;
	overflow: hidden;

	.overlay {
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		background-color: var(--c2ModalBackgroundOverlayColor);
		z-index: 9998;
	}

	.modal-container {
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		z-index: 9999;
	}

	.modal {
		position: fixed;
		top: 50%;
		left: 50%;
		max-width: var(--c2ModalMaxWidth);
		background-color: var(--c2ModalBackgroundColor);
		width: 100%;
		height: auto;
		max-height: 100vh;
		transform: translate(-50%, -50%);
		overflow: hidden;
	}

	.fade-enter-active,
	.fade-leave-active {
		transition: opacity 0.3s ease-in-out;
	}

	.fade-enter-from,
	.fade-leave-to {
		opacity: 0;
	}

	.zoom-enter-active,
	.zoom-leave-active {
		transition: transform 0.3s ease-in-out;
	}

	.zoom-enter-from,
	.zoom-leave-to {
		transform: scale(0);
	}
}
</style>
