<template>
    <hy-dropdown class="hy-select-dropdown" v-model="open" :title="displayValue" v-bind="dropdownProps">
        <template #icon>
            <slot name="icon" />
        </template>

        <template #content>
            <div class="searchBar" v-if="showSearch">
                <hy-input v-model="search" ref="searchBar" :placeholder="searchPlaceholder" size="sm"> </hy-input>

                <hy-button @click="search = ''" :disabled="search == ''" size="sm">
                    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1000 1000">
                        <path
                            d="M150.062+74.4688C130.868+74.4544+111.656+81.7726+97+96.4062C67.6887+125.674+67.6701+173.158+96.9375+202.469L394.031+500L96.9375+797.531C67.6701+826.843+67.6887+874.326+97+903.594C126.311+932.861+173.795+932.843+203.062+903.531L500+606.156L796.938+903.531C826.205+932.843+873.689+932.862+903+903.594C932.311+874.326+932.33+826.842+903.062+797.531L605.969+500L903.062+202.469C932.33+173.157+932.311+125.674+903+96.4062C888.344+81.7726+869.132+74.4544+849.938+74.4688C830.743+74.4831+811.571+81.8131+796.938+96.4688L500+393.844L203.062+96.4688C248.429+81.8131+169.257+74.4831+150.062+74.4688Z"
                        />
                    </svg>
                </hy-button>
            </div>

            <div class="optionsContainer" :class="{ 'hy-hide-scrollbars': maxHeight == null }" :style="{ maxHeight }" ref="options">
                <p
                    v-for="option in searchedOptions"
                    class="option"
                    :class="{ selected: option.value == modelValue }"
                    @click="updateValue(option)"
                    :key="option.value"
                >
                    {{ option.displayValue }}
                </p>
            </div>

            <p class="noSearchResults" v-if="searchedOptions.length == 0">{{ searchNoResults }}</p>
        </template>
    </hy-dropdown>
</template>

<script>
import HyButton from "./Button.vue";
import HyInput from "./Input.vue";
import HyDropdown from "./Dropdown.vue";

export default {
    name: "HySelectDropdown",
    props: {
        // SELECT SPECIFIC OPTIONS
        modelValue: null,

        showSearch: {
            type: Boolean,
            default: true,
        },
        searchPlaceholder: {
            type: String,
            default: "Search...",
        },
        searchNoResults: {
            type: String,
            default: "No search results found...",
        },

        // DROPDOWN OPTIONS
        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 {
            open: false,
            search: "",
            _buttonZIndex: "revert",
        };
    },
    computed: {
        allOptions() {
            let options = this.$slots.default?.() ?? this.$slots.options?.() ?? [];

            // Recusively map children
            let mapper = (el) => {
                // Dynamically rendered elements (like with v-for)
                if (typeof el.children == "object") {
                    return el.children.map(mapper);
                }

                // Static elements
                if (!el || !el.props || !el.props["data-value"]) return undefined;

                return {
                    value: el.props["data-value"],
                    displayValue: el.children,
                };
            };

            options = options.map(mapper).flat(Infinity);
            options = options.filter((el) => el != undefined);

            return options;
        },
        searchedOptions() {
            let search = this.search.toLowerCase();

            return this.allOptions.filter((el) => {
                if (el.value.toLowerCase().includes(search)) return true;
                if (el.displayValue.toLowerCase().includes(search)) return true;
                return false;
            });
        },

        displayValue() {
            return this.allOptions.find((el) => el.value == this.modelValue)?.displayValue ?? this.modelValue;
        },

        dropdownProps() {
            return {
                showBorder: this.showBorder,
                loading: this.loading,
                progress: this.progress,
                progressWidth: this.progressWidth,
                disabled: this.disabled,
                invalid: this.invalid,
                invalidText: this.invalidText,
                size: this.size,
                variant: this.variant,
                dropdownDirection: this.dropdownDirection,
                maxHeight: this.maxHeight,
                zIndex: this.zIndex,
                hideOnClickOutside: this.hideOnClickOutside,
            };
        },
    },
    watch: {
        modelValue() {
            let option = this.allOptions.find((el) => el.value == this.modelValue);

            this.updateValue(option);
        },
        open(newValue) {
            // Focus search bar on open (if on device with a keyboard)
            if (newValue == true) {
                this.$nextTick(() => {
                    if ("ontouchstart" in document.documentElement == true) return;

                    this.$refs.searchBar?.$el?.querySelector("input").focus();
                    this.$refs.searchBar?.$el?.querySelector("input").select();
                });
            }
        },
    },
    methods: {
        updateValue(option) {
            if (!option) return;

            this.$emit("update:modelValue", option.value);

            setTimeout(() => {
                this.open = false;
            }, 300);
        },
    },
    components: {
        HyButton,
        HyInput,
        HyDropdown,
    },
};
</script>

<style lang="scss">
.hy-select-dropdown {
    &.hy-dropdown .header button {
        font-weight: normal !important;
    }

    .optionsContainer {
        overflow-y: scroll;

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

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

        & > p.option {
            padding: var(--hy-element-padding-md);
            padding-left: 40px;
            border-radius: var(--hy-element-border-radius);
            margin: 5px 0;
            position: relative;
            cursor: pointer;
            transition: var(--hy-element-transition);
            transition-property: background-color, color;
            overflow: hidden;
            text-overflow: ellipsis;

            &::before {
                content: "";
                position: absolute;
                top: 50%;
                left: var(--hy-element-padding-md);
                transform: translateY(-50%);
                width: 15px;
                height: 15px;
                border-radius: 999px;
                border: var(--hy-element-border-secondary);
                transition: var(--hy-element-transition);
                transition-property: background-color, border-color;
            }

            &::after {
                content: "";
                position: absolute;
                top: 50%;
                left: calc(var(--hy-element-padding-md) + 7px);
                transform: translateY(-50%);
                width: 5px;
                height: 5px;
                border-radius: 999px;
                background-color: var(--hy-variant-primary-font-color);
                opacity: 0;
                transition: var(--hy-element-transition);
                transition-property: opacity;
            }

            &:hover {
                background-color: var(--hy-variant-ghost-hover-color);
            }

            &:not(.selected) {
                color: var(--hy-font-color-gray);
            }

            &.selected {
                &::before {
                    background-color: var(--hy-accent-color);
                    border-color: transparent;
                }

                &::after {
                    opacity: 1;
                }
            }
        }
    }

    .searchBar {
        display: flex;
        gap: var(--hy-flex-gap);
        align-items: stretch;
        width: 100%;
        margin-bottom: var(--hy-element-margin-value);

        * {
            margin: 0;
        }

        & > .hy-input {
            flex: 1;
            min-width: 0;
        }

        & > .hy-button {
            display: grid;
            align-items: stretch;

            button {
                display: grid;
                place-items: center;

                svg {
                    height: 20px;
                    width: 20px;
                    fill: var(--hy-font-color-contrast);
                }
            }
        }
    }

    .noSearchResults {
        background-color: var(--hy-color-gray-3);
        border: var(--hy-element-border-secondary);
        border-style: dashed;
        border-radius: var(--hy-element-border-radius);
        padding: var(--hy-element-padding);
        color: var(--hy-font-color-gray);
    }
}
</style>
