<template>
  <div class="accordion-wrapper" ref="wrapper">
    <div
      class="sliding-container"
      ref="slide"
      :style="{ height: state.sliderHeight }"
    >
      <div class="content-wrapper" ref="content" :class="props.revealStyle">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, reactive, ref, watch } from "vue";
export default defineComponent({
  props: {
    active: {
      required: true,
      type: Boolean,
      default: true,
    },
    duration: {
      type: Number,
      default: 552,
    },
    easing: {
      type: String,
      default: "cubic-bezier(0.52, 0.16, 0.24, 1)",
    },
    revealStyle: {
      type: String,
      default: "",
    },
  },
  setup(props, { emit }) {
    const state = reactive<{
      sliderHeight: any;
      timeoutId: any;
      animation: any;
    }>({
      sliderHeight: 0,
      timeoutId: null,
      animation: null,
    });

    const animationOptions = reactive<{
      duration: number;
      easing: string;
    }>({
      duration: props.duration,
      easing: props.easing,
    });
    const wrapper = ref<HTMLElement | any>();
    const slide = ref<HTMLElement | any>();
    const content = ref<HTMLElement | any>();

    const getHeight = (): Promise<number> => {
      return new Promise<number>((resolve) => {
        setTimeout(() => {
          const contentHeight = props.active
            ? content.value.clientHeight
            : slide.value.clientHeight;

          const maxHeight = parseInt(getComputedStyle(wrapper.value).maxHeight);

          if (maxHeight && contentHeight > maxHeight) {
            return resolve(maxHeight);
          } else {
            return resolve(contentHeight);
          }
        });
      });
    };

    const checkBottomSpace = (contentHeight: number): void => {
      let windowHeight = window.innerHeight;
      let boundingRect = wrapper.value.getBoundingClientRect();
      let bottomDistance = windowHeight - boundingRect.top;
      emit("nospace", bottomDistance <= contentHeight);
    };

    onMounted((): void => {
      if (props.active) {
        slide.value.style.height = "auto";
        slide.value.style.overflow = "visible";
      }
    });

    watch(
      () => props.active,
      async (newVal: boolean): Promise<void> => {
        clearTimeout(state.timeoutId);

        const startHeight = slide.value.clientHeight;
        const height = await getHeight();

        if (state.animation) {
          state.animation.cancel();
        }
        if (newVal) {
          checkBottomSpace(height);

          state.animation = slide.value.animate(
            [{ height: startHeight + "px" }, { height: height + "px" }],
            animationOptions
          );

          state.animation.onfinish = () => {
            slide.value.style.height = "auto";
            slide.value.style.overflow = "visible";
            content.value.style.transform = "initial";
          };
        } else {
          content.value.style.transform = null;
          slide.value.style.overflow = null;

          state.animation = slide.value.animate(
            [{ height: startHeight + "px" }, { height: 0 }],
            animationOptions
          );

          state.animation.onfinish = () => {
            slide.value.style.height = "0";
          };
        }
      }
    );

    return {
      props,
      state,
      slide,
      content,
      wrapper,
    };
  },
});
</script>

<style lang="scss">
.accordion-wrapper {
  .sliding-container {
    position: relative;
    overflow: hidden;
    transition: background 0.56s cubic-bezier(0.32, 0.08, 0.24, 1),
      height 0.552s cubic-bezier(0.52, 0.16, 0.24, 1);

    .content-wrapper {
      &.center {
        position: relative;
        top: 50%;
        transform: translateY(-50%);
      }
    }
  }
}
</style>
