<template>
	<div class="tooltip-container">
		<div
			ref="trigger"
			class="trigger noselect"
			@click.stop="toggle"
		>
			<slot />
		</div>

		<teleport to="body">
			<div
				v-if="opened"
				ref="tooltip"
				class="tooltip-wrapper"
				:style="{ transform: `translate3d(${tooltipX}px, ${tooltipY}px, 0)` }"
			>
				<div class="tooltip-inner">
					<div class="tooltip">
						<slot name="tooltip" />
					</div>

					<div
						class="tooltip-arrow"
						:style="{ transform: `translate3d(${arrowOffset}px, 0, 0)` }"
					/>
				</div>
			</div>
		</teleport>
	</div>
</template>

<script>
import { nextTick } from 'vue';

function getX(triggerBoundary, tooltipBoundary) {
	const idealPosition = triggerBoundary.left + (triggerBoundary.width / 2) - (tooltipBoundary.width / 2);
	const rightEdgeOverflow = Math.max((idealPosition + tooltipBoundary.width) - window.innerWidth, 0);

	// don't overflow left edge
	if (idealPosition < 0) {
		return {
			tooltipX: 0,
			arrowOffset: idealPosition,
		};
	}

	// don't overflow right edge
	if (rightEdgeOverflow > 0) {
		return {
			tooltipX: window.innerWidth - tooltipBoundary.width,
			arrowOffset: rightEdgeOverflow,
		};
	}

	// position at the center of trigger
	return {
		tooltipX: idealPosition,
		arrowOffset: 0,
	};
}

async function calculate() {
	if (!this.opened) {
		return;
	}

	const triggerBoundary = this.$refs.trigger.getBoundingClientRect();
	const tooltipBoundary = this.$refs.tooltip.getBoundingClientRect();

	const { tooltipX, arrowOffset } = this.getX(triggerBoundary, tooltipBoundary);

	this.tooltipY = triggerBoundary.top + triggerBoundary.height;
	this.tooltipX = tooltipX;
	this.arrowOffset = arrowOffset;
}

async function open() {
	this.events.emit('blur');

	await nextTick();

	this.opened = true;
	await nextTick();

	this.calculate();
	this.$emit('open');
}

function close() {
	this.opened = false;

	this.tooltipY = 0;
	this.tooltipX = 0;
	this.arrowOffset = 0;

	this.$emit('close');
}

function toggle() {
	if (this.opened) {
		this.close();
		return;
	}

	this.open();
}

function mounted() {
	this.events.on('blur', () => {
		this.close();
	});

	this.events.on('resize', () => {
		this.calculate();
	});
}

export default {
	data() {
		return {
			opened: false,
			tooltipX: 0,
			tooltipY: 0,
			arrowOffset: 0,
		};
	},
	emits: ['open', 'close'],
	mounted,
	methods: {
		calculate,
		getX,
		open,
		close,
		toggle,
	},
};
</script>

<style lang="scss" scoped>
.tooltip-wrapper {
	display: flex;
	top: 0;
	left: 0;
	flex-direction: column;
	justify-content: center;
	position: absolute;
	z-index: 10;
}

.tooltip-inner {
	position: relative;
	box-shadow: 0 0 3px var(--darken-weak);
}

.tooltip {
	position: relative;
	background: var(--background-light);
}

.tooltip-arrow {
	content: '';
	width: 0;
	height: 0;
	position: absolute;
	top: -.5rem;
	left: calc(50% - .5rem);
	border-left: .5rem solid transparent;
	border-right: .5rem solid transparent;
	border-bottom: .5rem solid var(--background-light);
	margin: 0 auto;
	filter: drop-shadow(0 0 3px var(--darken-weak));
}
</style>