<script setup lang="ts">
import { ref, onMounted } from "vue";
import { debounce } from "@/helpers";

const activeImage = ref(0);
const images = ref<Array<Element>>([]);
const element = document.createElement("div");
const galleryViewport = ref<HTMLElement>(element);
const galleryItemWidth = ref<number>(1);

const resizeObserverCallback = (entries: Array<ResizeObserverEntry>): void => {
  for (const entry of entries) {
    galleryItemWidth.value = entry.contentRect.width;
  }
};

const resizeObserver = new ResizeObserver(
  debounce(resizeObserverCallback, 1000)
);

const setActiveImage = (index: number): void => {
  activeImage.value = index;
  galleryViewport.value.scrollTo({
    left: galleryItemWidth.value * index,
    behavior: "smooth",
  });
};

const getActiveImage = (gallery: HTMLElement, itemWidth: number): number => {
  return gallery.scrollLeft / itemWidth;
};

const prev = (): void => {
  if (activeImage.value > 0) {
    galleryViewport.value.scrollBy({
      left: galleryItemWidth.value * -1,
      behavior: "smooth",
    });
  }
};

const next = (): void => {
  if (activeImage.value < images.value.length - 1) {
    galleryViewport.value.scrollBy({
      left: galleryItemWidth.value,
      behavior: "smooth",
    });
  }
};

const onScroll = (): void => {
  const newImg = getActiveImage(galleryViewport.value, galleryItemWidth.value);
  setActiveImage(newImg);
};

onMounted(() => {
  resizeObserver.observe(galleryViewport.value);
  images.value = Array.from(galleryViewport.value.children);
  galleryItemWidth.value =
    galleryViewport.value.scrollWidth / images.value.length;
  galleryViewport.value.addEventListener("scroll", debounce(onScroll, 100));
});
</script>

<template>
  <div class="gallery">
    <div class="gallery__images">
      <a
        href="#"
        class="gallery__prev"
        @click.prevent="prev()"
        v-show="activeImage > 0"
      ></a>
      <a
        href="#"
        class="gallery__next"
        @click.prevent="next()"
        v-show="activeImage < images.length - 1"
      ></a>

      <div class="gallery__viewport" ref="galleryViewport">
        <slot></slot>
      </div>
    </div>

    <nav class="gallery__navigation">
      <ul>
        <li v-for="(image, idx) in images" :key="idx">
          <a
            href="#"
            :class="{ active: activeImage === idx }"
            @click.prevent="setActiveImage(idx)"
          ></a>
        </li>
      </ul>
    </nav>
  </div>
</template>

<style lang="scss">
.gallery {
  &__prev,
  &__next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);

    width: var(--gallery-size);
    height: var(--gallery-size);

    z-index: 1;

    border: {
      top: 0.125rem solid #fff;
      right: 0.125rem solid #fff;
    }
  }

  &__prev {
    left: var(--gallery-arrow-position);
    transform: rotate(-135deg);
  }

  &__next {
    right: var(--gallery-arrow-position);
    transform: rotate(45deg);
  }

  &__images {
    position: relative;
    overflow: hidden;
  }

  &__viewport {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;

    width: 100%;
    height: 100%;

    overflow: auto;
    scroll-snap-type: x mandatory;

    scrollbar-width: none;

    transition: 0.3s all ease-in-out;

    &::-webkit-scrollbar {
      display: none;
    }
  }

  .figure {
    margin: 0;
    padding: var(--gallery-image-padding);
    flex: 1 0 100%;

    scroll-snap-align: center;

    &__meta {
      text-align: center;

      p {
        margin: 0.5rem auto;
      }
    }
  }

  &__navigation {
    display: flex;
    flex-flow: row wrap;
    justify-content: center;

    ul {
      flex: 0 1 auto;

      display: flex;
      flex-flow: row wrap;
      justify-content: center;

      list-style: none;
      margin: 1rem 0;
      padding: 0;
    }

    li {
      flex: 0 1 auto;
      padding: 0.375em;

      a {
        display: block;
        width: 0.5em;
        height: 0.5em;
        border-radius: 1em;
        background: rgba(#fff, 0.5);

        &.active {
          background: #fff;
        }
      }
    }
  }
}
</style>
