<template>
    <div
        class="hy-dropdown"
        :class="[{ icon: $slots.icon, menuOpen: modelValue == true, showBorder }, `direction_${dropdownDirection}`]"
        v-click-outside="clickOutsideHandler"
    >
        <!-- Header -->
        <hy-button
            class="header"
            :class="{ invalid, transitionsEnabled }"
            @click="toggleMenu()"
            :loading="loading"
            :progress="progress"
            :progressWidth="progressWidth"
            :disabled="disabled"
            :size="size"
            :variant="variant"
            ref="header"
        >
            <span class="iconSlot" :class="[_size]" v-if="$slots.icon">
                <slot name="icon" />
            </span>

            <slot v-if="$slots.title" name="title" />
            {{ title }}

            <span class="chevron" :class="[_size]">
                <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1000 1000" width="30" height="30">
                    <path
                        d="M102.406+249.281C80.8052+248.6+59.077+257.194+43.6875+274.688C16.3284+305.787+19.3688+353.203+50.4688+380.562L450.062+732.062C478.366+756.962+520.734+756.995+549.062+732.125L949.469+380.594C980.596+353.266+983.703+305.878+956.375+274.75C929.047+243.622+881.659+240.547+850.531+267.875L499.656+575.906L149.531+267.938C135.925+255.968+119.207+249.811+102.406+249.281Z"
                    />
                </svg>
            </span>
        </hy-button>

        <!-- Menu -->
        <transition :name="dropdownDirection == 'bottom' ? 'fade-direction-bottom' : 'fade-direction-top'">
            <!-- tabindex necessary to be able to focus on this div => Use for click outside detection -->
            <div v-if="modelValue == true" class="menu hy-hide-scrollbars" :style="{ zIndex }" ref="menu" tabindex="-1">
                <slot />
                <slot name="content" />
            </div>
        </transition>

        <!-- Invalid Text -->
        <p class="invalidText" ref="invalidText" v-show="invalid && invalidText" :class="{ muted: disabled || loading }">
            {{ invalidText }}
        </p>
    </div>
</template>

<script>
import HyButton from "./Button.vue";
import vClickOutside from "click-outside-vue3";
import { getBorderRadius } from "../utils/getBorderRadius.js";

export default {
    name: "HyDropdown",
    props: {
        modelValue: {
            type: Boolean,
            required: true,
        },
        title: {
            type: String,
            required: false,
        },
        showBorder: {
            type: Boolean,
            default: false,
        },
        loading: {
            type: Boolean,
            default: false,
        },
        progress: {
            type: Number,
            default: null,
        },
        progressWidth: {
            type: String,
            default: "min(calc(100% - 2 * var(--hy-element-padding)), 200px)",
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        invalid: {
            type: Boolean,
            default: false,
        },
        invalidText: {
            type: String,
            default: null,
        },
        size: {
            type: String,
            default: null,
        },
        variant: {
            type: String,
            default: null,
        },
        maxHeight: {
            type: String,
            default: null,
        },
        zIndex: {
            type: Number,
            default: 10,
        },
        hideOnClickOutside: {
            type: Boolean,
            default: true,
        },
        dropdownDirection: {
            type: String,
            default: "bottom",
        },
    },
    data() {
        return {
            _buttonZIndex: "revert",
            _headerHeight: `0px`,
            _borderRadius: "10",
            transitionsEnabled: false,
        };
    },
    watch: {
        size() {
            this.updateHeaderHeight();
        },
    },
    computed: {
        _size() {
            let size = this.size ?? this.hy_defaults.size;
            let allowed = ["lg", "md", "sm"];

            if (allowed.includes(size)) return `size-${size}`;
            else return "size-lg";
        },
        _invalidTextLeftClipPath() {
            return `path("M 0 0 a ${this._borderRadius} ${this._borderRadius} 0 0 0 ${this._borderRadius} ${this._borderRadius} L 0 ${this._borderRadius}")`;
        },
        _invalidTextRightClipPath() {
            return `path("M 0 ${this._borderRadius} a ${this._borderRadius} ${this._borderRadius} 0 0 0 ${this._borderRadius} -${this._borderRadius} L ${this._borderRadius} ${this._borderRadius}")`;
        },
        _chevronColor() {
            let variant = (this.variant ?? this.hy_defaults.variant).replace("-bg", ""); // "ghost" and "ghost-bg" variants share the same variables => trim the "-bg" part
            return `var(--hy-variant-${variant}-font-color)`;
        },
    },
    methods: {
        toggleMenu() {
            this.modelValue ? this.hide() : this.show();
        },
        show() {
            this.$emit("update:modelValue", true);
            this._buttonZIndex = this.zIndex + 1;
            this.updateHeaderHeight();
        },
        hide() {
            this.$emit("update:modelValue", false);

            // Revert z-index to default, but only if the dropdown has not been repopened since initatiating the hide
            setTimeout(() => {
                if (this.modelValue == false) this._buttonZIndex = "revert";
            }, 200);
        },
        clickOutsideHandler() {
            if (this.hideOnClickOutside == false) return;

            this.hide();
        },
        updateHeaderHeight() {
            this._headerHeight = `${this.$refs.header.$el.offsetHeight}px`;
        },
    },
    mounted() {
        this._borderRadius = getBorderRadius();

        requestAnimationFrame(() => (this.transitionsEnabled = true));
    },
    directives: {
        clickOutside: vClickOutside.directive,
    },
    components: {
        HyButton,
    },
};
</script>

<style lang="scss">
.hy-dropdown {
    margin: var(--hy-element-margin);
    position: relative;
    min-width: max-content;
    border-radius: var(--hy-element-border-radius);
    transition: var(--hy-element-transition);
    transition-property: box-shadow;

    & > .header {
        z-index: v-bind(_buttonZIndex);
        margin: 0;

        &.transitionsEnabled {
            & > button {
                transition: var(--hy-element-transition);
                transition-property: border-radius, border-color, background-color, z-index, opacity, filter, color;
            }

            .chevron {
                transition: var(--hy-element-transition);
                transition-property: transform, filter;
            }

            & > .iconSlot {
                transition: var(--hy-element-transition);
                transition-property: background-color, color, border, filter;
            }
        }

        & > button {
            text-align: left;
            padding-left: var(
                --hy-element-padding
            ) !important; // Override default padding when using smaller sizes => Prevents misalignment of icon compared to hy-select or hy-input
        }

        .chevron {
            position: absolute;
            width: 16px;
            height: 16px;
            right: 15px;
            display: flex;
            align-items: center;
            margin: 0;
            pointer-events: none;
            transform: rotate(-90deg);

            &.size-lg {
                top: calc(var(--hy-element-padding) + 2px + 4px); // 2px border-width + 4px arbatrary value for centering
            }

            &.size-md {
                top: calc(var(--hy-element-padding-md) + 4px); // 2px border-width + 4px arbatrary value for centering
            }

            &.size-sm {
                top: calc(var(--hy-element-padding-sm) + 4px); // 2px border-width + 4px arbatrary value for centering
            }

            svg {
                transition: var(--hy-element-transition);
                transition-property: fill;
                fill: v-bind("_chevronColor");
            }
        }

        & > .iconSlot {
            pointer-events: none;
            position: absolute;
            margin: 0;
            left: 2px;
            padding: 0 var(--hy-element-padding);
            border-radius: var(--hy-element-border-radius);

            &.size-lg {
                top: calc(var(--hy-element-padding) + 2px); // 2px border-width
            }

            &.size-md {
                top: calc(var(--hy-element-padding-md) + 2px); // 2px border-width
            }

            &.size-sm {
                top: calc(var(--hy-element-padding-sm) + 2px); // 2px border-width
            }
        }
    }

    &.menuOpen {
        box-shadow: var(--hy-popover-shadow);

        .chevron svg {
            fill: var(--hy-font-color-contrast);
        }

        &.direction_top > .header {
            button {
                border-top-left-radius: 0;
                border-top-right-radius: 0;
                box-shadow: var(--hy-dropdown-top-shadow);
            }

            .chevron {
                transform: rotate(0deg);
            }
        }

        &.direction_bottom > .header {
            button {
                border-bottom-left-radius: 0;
                border-bottom-right-radius: 0;
                box-shadow: var(--hy-dropdown-bottom-shadow);
            }

            .chevron {
                transform: rotate(-180deg);
            }
        }

        & > .header {
            button {
                color: var(--hy-font-color-contrast) !important;
                background-color: var(--hy-variant-secondary-bg-color) !important;
            }
        }
    }

    &:not(.menuOpen) > .header.invalid > button {
        border: var(--hy-element-border-error) !important;
    }

    &:not(.showBorder).menuOpen > .header button {
        border-color: transparent;
    }

    &.showBorder.menuOpen > .header button {
        border-color: var(--hy-element-border-secondary-color);
    }

    &.showBorder {
        & > .menu {
            border: var(--hy-element-border-secondary);
        }
    }

    &.direction_top > .menu {
        bottom: v-bind(_headerHeight);
        transform: translateY(2px);
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        border-bottom-color: transparent;
    }

    &.direction_bottom > .menu {
        top: v-bind(_headerHeight);
        transform: translateY(-2px);
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-top-color: transparent;
    }

    & > .menu {
        position: absolute;
        width: 100%;
        border-radius: var(--hy-element-border-radius);
        /* border: var(--hy-element-border-secondary); */
        padding: var(--hy-element-padding);
        margin: 0;
        background-color: var(--hy-popover-bg-color-match);
        color: var(--hy-font-color-contrast);
        box-shadow: var(--hy-popover-shadow);
        overflow-y: scroll;

        & > *:first-child {
            margin-top: 0 !important;
        }

        & > *:last-child {
            margin-bottom: 0 !important;
        }
    }

    .invalidText {
        font-size: var(--hy-font-size-small);
        padding: var(--hy-element-padding-sm);
        margin: 0;
        background-color: var(--hy-invalid-bg-color);
        border-radius: var(--hy-element-border-radius);
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        position: relative;
        transition: var(--hy-element-transition);
        transition-property: filter;

        &::before,
        &::after {
            content: "";
            height: var(--hy-element-border-radius);
            width: var(--hy-element-border-radius);
            top: calc(var(--hy-element-border-radius) * -1);
            position: absolute;
            background-color: var(--hy-invalid-bg-color);
        }

        &::before {
            left: 0;
            clip-path: v-bind(_invalidTextLeftClipPath);
        }

        &::after {
            right: 0;
            clip-path: v-bind(_invalidTextRightClipPath);
        }

        &.muted {
            filter: opacity(50%);
        }
    }
}

.fade-direction-bottom-enter-active,
.fade-direction-bottom-leave-active,
.fade-direction-top-enter-active,
.fade-direction-top-leave-active {
    transition: var(--hy-element-transition);
    transition-property: opacity, transform;
}

.fade-direction-top-enter-from,
.fade-direction-top-leave-to {
    opacity: 0;
    transform: translateY(10px) !important;
}

.fade-direction-bottom-enter-from,
.fade-direction-bottom-leave-to {
    opacity: 0;
    transform: translateY(-10px) !important;
}
</style>
