import { computed, defineComponent, h, nextTick, ref } from "vue";
import { cx, eventValue, iconNode } from "../utils.js";
export default defineComponent({
name: "GnCombobox",
inheritAttrs: false,
props: {
modelValue: { type: [String, Number], default: "" },
label: { type: String, default: "" },
icon: { type: String, default: "" },
options: { type: Array, default: () => [] },
placeholder: { type: String, default: "Search" },
notFoundText: { type: String, default: "Nothing found" },
state: { type: String, default: "" },
help: { type: String, default: "" }
},
emits: ["update:modelValue", "select"],
setup(props, { attrs, emit }) {
const open = ref(false);
const focused = ref(-1);
const inputRef = ref(null);
const normalized = computed(() => props.options.map(option => typeof option === "object" ? option : {
value: option,
label: option
}));
const query = computed(() => String(props.modelValue ?? "").toLowerCase());
const filtered = computed(() => normalized.value.filter(option => String(option.label).toLowerCase().includes(query.value)));
const select = option => {
if(!option) {
return;
}
emit("update:modelValue", option.label);
emit("select", option);
open.value = false;
focused.value = -1;
};
const move = direction => {
if(!filtered.value.length) {
return;
}
open.value = true;
focused.value = (focused.value + direction + filtered.value.length) % filtered.value.length;
nextTick(() => {
const container = inputRef.value?.closest(".form-group")?.querySelector(".advanced-select");
container?.querySelector(".option.focus")?.scrollIntoView({ block: "nearest" });
});
};
const onKeydown = event => {
if(event.key === "ArrowDown") {
event.preventDefault();
move(1);
} else if(event.key === "ArrowUp") {
event.preventDefault();
move(-1);
} else if(event.key === "Enter") {
event.preventDefault();
select(filtered.value[focused.value]);
} else if(event.key === "Escape") {
open.value = false;
focused.value = -1;
}
};
return () => h("div", { class: "form-group" }, [
h("label", { class: cx("label", props.state) }, [
props.label,
iconNode(props.icon),
h("input", {
...attrs,
ref: inputRef,
type: "text",
value: props.modelValue,
placeholder: props.placeholder,
autocomplete: "off",
class: cx("input", attrs.class),
onFocus: () => {
open.value = true;
},
onBlur: () => {
setTimeout(() => {
open.value = false;
}, 120);
},
onInput: event => {
focused.value = -1;
open.value = true;
emit("update:modelValue", eventValue(event));
},
onKeydown
})
]),
h("div", { class: "advanced-select-container" }, [
h("div", { class: cx("advanced-select", { "a-show": open.value }) }, [
h("div", { class: "popup-options-container" }, [
h("div", { class: cx("not-found", { show: !filtered.value.length }) }, props.notFoundText),
h("div", { class: cx("options", { show: filtered.value.length }) }, filtered.value.map((option, index) => h("div", {
class: cx("option", { focus: index === focused.value }),
"data-value": option.value,
"data-display-value": option.label,
onMousedown: event => {
event.preventDefault();
select(option);
}
}, option.label)))
])
])
]),
props.help && h("div", { class: cx("input-info", props.state === "error" && "error") }, props.help)
]);
}
});