<template>
  <div
    v-if="active"
    class="l-modal is-active"
    :class="[
      closePosition == 'inside' ? 'close-is-inside' : 'close-is-outside',
    ]"
    :style="{
      '--overlay-bg': overlayBG,
      '--modal-controls': modalControls,
      '--modal-controls-hover': modalControlsHover,
      '--modal-position': modalPosition,
      '--modal-fill': fullscreen ? 'fixed' : 'absolute',
      '--modal-layer': fullscreen ? 'var(--layer-top)' : 'var(--layer-5)',
      '--modal-overflow': allowOverflow ? 'visible' : 'auto',
    }"
  >
    <div class="l-modal-background" @click="cancel('outside')" />
    <div
      class="animation-content"
      :class="[
        'l-modal-content',
        position == 'left' || position == 'right'
          ? 'l-modal-content--flyout'
          : null,
        'l-modal-content-type--' + position,
      ]"
      :style="{ maxWidth: newWidth }"
    >
      <component
        :is="component"
        v-if="component"
        v-bind="props"
        v-on="events"
        @close="close"
      />
      <div v-else-if="content" v-html="content" />
      <slot v-else />

      <span
        v-if="showX && closePosition == 'inside'"
        class="l-modal-close l-modal-close--inside"
        @click="cancel('x')"
      >
        <l-icon size="5"><icon-x-alt /></l-icon>
      </span>
    </div>
    <span
      v-if="showX && position == 'center' && closePosition == 'outside'"
      class="l-modal-close"
      @click="cancel('x')"
    >
      <l-icon size="5"><icon-x-alt /></l-icon>
    </span>
  </div>
</template>

<script>
import LButton from "../atoms/LButton.vue";
import LIcon from "../atoms/LIcon.vue";
import IconXAlt from "../../icons/icon-x-alt.vue";
/**
 * Modal allows you to overlay any content above the main content.
 *
 * **Modal should not disrupt the user, only show a modal when it's expected UI behaviour.**
 *
 */
export default {
  name: "LModal",
  components: {
    LButton,
    LIcon,
    IconXAlt,
  },
  props: {
    /**
     * Whether modal is active or not
     */
    active: Boolean,
    /**
     * Component to be injected, used to open a component modal programmatically.
     * Close modal within the component by emitting a 'close' event — `this.$emit('close')`
     */
    component: [Object, Function],
    /**
     *  HTML content
     */
    content: String,
    /**
     *
     */
    props: Object,
    /**
     * Events to be binded to the injected component
     */
    events: Object,
    /**
     * The type of the modal.
     */
    type: {
      type: String,
      default: "neutral",
      validator: function (value) {
        return (
          [
            "primary",
            "secondary",
            "tertiary",
            "success",
            "warning",
            "danger",
            "neutral",
            "white",
            "black",
            "admin"
          ].indexOf(value) !== -1
        );
      },
    },
    /**
     * The position of the modal.
     *
     * If position is set to `center`, the modal will be centered.
     * If position is set to `left` or `right`, the modal will be positioned to the defined side and act as a flyout section.
     */
    position: {
      type: String,
      default: "center",
      validator: function (value) {
        return ["left", "center", "right"].indexOf(value) !== -1;
      },
    },
    /**
     * If `fullscreen` is set to `true`, the modal will be fullscreen and overlay all other content.
     *
     * If `fullscreen` is set to `false`, the modal will only fill the parent container (or whichever element has `position: relative` set).
     */
    fullscreen: {
      type: Boolean,
    },
    /**
     * Width of the Modal
     */
    width: {
      type: [String, Number],
      default: 960,
    },
    /**
     * Allow overflow of the modal content.
     */
    allowOverflow: {
      type: Boolean,
      default: false,
    },
    /**
     * Can close Modal by clicking 'X', pressing escape or clicking outside
     */
    canClose: {
      type: [Array, Boolean],
      default: () => {
        return ["escape", "x", "outside", "button"];
      },
    },
    /**
     * The position of the modal close button.
     *
     */
    closePosition: {
      type: String,
      default: "outside",
      validator: function (value) {
        return ["outside", "inside"].indexOf(value) !== -1;
      },
    },
    /**
     * Callback function to call after user canceled (clicked 'X' / pressed escape / clicked outside)
     */
    onCancel: {
      type: Function,
      default: () => {},
    },
    /**
     * clip to remove the <body> scrollbar, keep to have a non scrollable scrollbar to
     * avoid shifting background, but will set <body> to position fixed, might break some layouts
     */
    scroll: {
      type: String,
      default: () => {
        return "clip";
      },
      validator: (value) => {
        return ["clip", "keep"].indexOf(value) >= 0;
      },
    },
  },
  data() {
    return {
      savedScrollTop: null,
      newWidth: typeof this.width === "number" ? this.width + "px" : this.width,
    };
  },
  computed: {
    cancelOptions() {
      return typeof this.canClose === "boolean"
        ? this.canClose
          ? ["escape", "x", "outside", "button"]
          : []
        : this.canClose;
    },
    showX() {
      return this.cancelOptions.indexOf("x") >= 0;
    },
    overlayBG() {
      if (this.type === "white" || this.type === "black") {
        return `var(--${this.type})`;
      }
      return `var(--${this.type}-100)`;
    },
    modalControls() {
      if (this.type === "white") {
        return `var(--black)`;
      }
      if (this.type === "black") {
        return `var(--white)`;
      }
      return `var(--${this.type}-500)`;
    },
    modalControlsHover() {
      if (this.type === "white") {
        return `var(--primary-500)`;
      }
      if (this.type === "black") {
        return `var(--primary-400)`;
      }
      return `var(--${this.type}-600)`;
    },
    modalPosition() {
      if (this.position === "left") {
        return "flex-start";
      }
      if (this.position === "right") {
        return "flex-end";
      }
      return "center";
    },
  },
  watch: {
    width() {
      this.newWidth =
        typeof this.width === "number" ? this.width + "px" : this.width;
    },
  },
  created() {
    if (typeof window !== "undefined") {
      document.addEventListener("keyup", this.keyPress);
    }
  },
  mounted() {
    if (this.active) this.handleScroll();
  },
  beforeDestroy() {
    if (typeof window !== "undefined") {
      document.removeEventListener("keyup", this.keyPress);
      // reset scroll
      document.documentElement.classList.remove("is-clipped");
      document.documentElement.style.overflowY = "scroll";
      const savedScrollTop = !this.savedScrollTop
        ? document.documentElement.scrollTop
        : this.savedScrollTop;
      document.body.classList.remove("is-noscroll");
      document.documentElement.scrollTop = savedScrollTop;
      document.body.style.top = null;
    }
  },
  methods: {
    handleScroll() {
      if (typeof window === "undefined") return;
      if (this.scroll === "clip") {
        if (this.active && this.fullscreen) {
          document.documentElement.style.overflowY = "hidden";
        } else {
          document.documentElement.style.overflowY = "scroll";
        }
        return;
      }
      this.savedScrollTop = !this.savedScrollTop
        ? document.documentElement.scrollTop
        : this.savedScrollTop;
      if (this.active) {
        document.body.style.top = `-${this.savedScrollTop}px`;
        return;
      }
      document.documentElement.scrollTop = this.savedScrollTop;
      document.body.style.top = null;
      this.savedScrollTop = null;
    },
    removeElement(el) {
      if (typeof el.remove !== "undefined") {
        el.remove();
      } else if (typeof el.parentNode !== "undefined") {
        el.parentNode.removeChild(el);
      }
    },
    /**
     * Close the Modal if canClose and call the onCancel prop (function).
     */
    cancel(method) {
      if (this.cancelOptions.indexOf(method) < 0) return;
      this.onCancel.apply(null, arguments);
      this.close();
    },
    /** Call the onCancel prop (function).
     * Emit events
     * @event close
     * @type {Event}
     */
    close() {
      this.$emit("close");
      this.$emit("update:active", false);
    },
    /**
     * Keypress event that is bound to the document.
     */
    keyPress(event) {
      // Esc key
      if (this.active && event.keyCode === 27) this.cancel("escape");
    },
  },
};
</script>

<style>
.l-modal {
  align-items: var(--modal-position);
  display: none;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  position: var(--modal-fill);
  z-index: var(--modal-layer);
  bottom: 0;
  left: 0;
  right: 0;
  top: 0;
  padding: var(--spacing-4);
  height: 100%;
}
.l-modal.is-active {
  display: flex;
}
.l-modal-background {
  background-color: var(--neutral-400);
  opacity: 0.5;
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  animation: overlay var(--duration-150);
}
.l-modal-close {
  position: absolute;
  top: var(--spacing-7);
  right: var(--spacing-7);
  color: var(--modal-controls);
  cursor: pointer;
  transition: color 0.2s ease;
}
.l-modal-close:hover {
  color: var(--modal-controls-hover);
  transform: scale(1.1);
}
.l-modal-close.l-modal-close--inside {
  top: var(--spacing-4);
  right: var(--spacing-4);
}
.l-modal-content {
  background-color: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2xl);
  /* padding: var(--spacing-4) var(--spacing-6); */
  overflow: var(--modal-overflow);
  position: relative;
  width: 100%;
  animation: fadeInUp 0.45s;
  animation-delay: 0.23s;
  animation-fill-mode: backwards;

  /* @media (max-width: $breakpoint-l) {
      margin: 0 auto;
      max-height: calc(100vh - 80px);
      width: 80%;
    } */
}
@media screen and (min-width: 768px) {
  .l-modal-content {
    width: 80%;
  }
}
.l-modal-content--flyout {
  min-height: 100%;
}
/* Animations */
.l-modal-content-type--left {
  animation: modal-left var(--duration-500);
  animation-delay: var(--duration-150);
  animation-fill-mode: backwards;
}
.l-modal-content-type--right {
  animation: modal-right var(--duration-500);
  animation-delay: var(--duration-150);
  animation-fill-mode: backwards;
}
.l-modal-content-type--center {
  animation: modal-center var(--duration-500);
  animation-delay: var(--duration-150);
  animation-fill-mode: backwards;
}

.l-modal.close-is-inside .l-card-header {
  padding-right: var(--spacing-10);
}

@keyframes overlay {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 0.75;
  }
}

@keyframes modal-left {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}
@keyframes modal-right {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}
@keyframes modal-center {
  from {
    transform: scale(0);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}
</style>
