vue-search-select
Version:
A Vue.js search select form component
614 lines (613 loc) • 18.3 kB
JavaScript
import { openBlock as m, createElementBlock as c, normalizeClass as f, createElementVNode as p, withModifiers as a, withKeys as u, toDisplayString as x, normalizeStyle as O, Fragment as I, renderList as v, renderSlot as y, createTextVNode as g, h as S, createCommentVNode as M, withDirectives as A, vModelText as C } from "vue";
const l = {
// cursor on input
openOptions(e) {
e.$refs.input.focus(), e.showMenu = !0, e.mousedownState = !1;
},
blurInput(e) {
e.mousedownState || (e.searchText = "", e.closeOptions()), e.$emit("blur");
},
closeOptions(e) {
e.$refs.input.blur(), e.showMenu = !1;
},
/**
* up arrow key
* 上の移動するときには新しいscroll位置を毎回セットする
* Always scroll move, when "up arrow key" entered
*/
prevItem(e) {
const t = e.pointer - 1, n = e.$el.offsetHeight * t;
t >= 0 && (e.pointer = t), e.$refs.menu.scrollTop = n;
},
/**
*
* down arrow key
* ページsizeを計算してずれたらmove
* calculate page size. If different between itemPage and currentPage move scroll
*/
nextItem(e) {
const t = e.pointer + 1, n = e.$el.offsetHeight * t;
t <= e.filteredOptions.length - 1 && (e.pointer = t);
const d = e.$refs.menu.offsetHeight, r = Math.ceil((e.$refs.menu.scrollTop + e.$el.offsetHeight) / d), s = Math.ceil(n / d);
r !== s && (e.$refs.menu.scrollTop = (s - 1) * e.$refs.menu.offsetHeight);
},
// down enter key
enterItem(e) {
const t = e.filteredOptions[e.pointer], n = t.disabled;
t && !n && e.selectItem(t);
},
// mouse enter event on item
pointerSet(e, t) {
e.pointer = t;
},
pointerAdjust(e) {
e.pointer >= e.filteredOptions.length - 1 && (e.pointer = e.filteredOptions.length ? e.filteredOptions.length - 1 : 0);
},
mousedownItem(e) {
e.mousedownState = !0;
}
};
function E(e) {
return new RegExp(e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
}
const b = {
props: {
id: {
default: null
},
name: {
type: String,
default: ""
},
isError: {
type: Boolean,
default: !1
},
customAttr: {
type: Function,
default: () => ""
},
isDisabled: {
type: Boolean,
default: !1
},
placeholder: {
type: String,
default: ""
},
filterPredicate: {
type: Function,
default: (e, t) => e.match(E(t))
}
}
}, V = (e, t) => {
const n = e.__vccOpts || e;
for (const [d, r] of t)
n[d] = r;
return n;
}, k = {
name: "ModelSelect",
mixins: [b],
emits: ["blur", "searchchange", "update:modelValue"],
props: {
modelValue: {
type: [String, Number, Object, Boolean]
},
customAttr: {
type: Function,
default: () => ""
},
options: {
type: Array
}
},
data() {
return {
showMenu: !1,
searchText: "",
mousedownState: !1,
// mousedown on option menu
pointer: -1
};
},
watch: {
value(e) {
this.pointer = this.filteredOptions.findIndex((t) => t.value === this.optionValue(e));
},
filteredOptions() {
this.pointerAdjust();
},
searchText() {
this.$emit("searchchange", this.searchText);
}
},
computed: {
searchTextCustomAttr() {
return this.selectedOption && this.selectedOption.value ? this.customAttr(this.selectedOption) : "";
},
inputText() {
if (this.searchText)
return "";
{
let e = this.placeholder;
return this.selectedOption && (e = this.selectedOption.text), e;
}
},
customAttrs() {
try {
if (Array.isArray(this.options))
return this.options.map((e) => this.customAttr(e));
} catch {
}
return [];
},
textClass() {
return !this.selectedOption && this.placeholder ? "default" : "";
},
menuClass() {
return {
visible: this.showMenu,
hidden: !this.showMenu
};
},
menuStyle() {
return {
display: this.showMenu ? "block" : "none"
};
},
filteredOptions() {
return this.searchText ? this.options.filter((e) => {
try {
return this.filterPredicate(e.text, this.searchText);
} catch {
return !0;
}
}) : this.options;
},
selectedOption() {
return this.options.find((e) => e.value === this.optionValue(this.modelValue));
}
},
methods: {
deleteTextOrItem() {
!this.searchText && this.modelValue && (this.selectItem({}), this.openOptions());
},
openOptions() {
l.openOptions(this);
},
blurInput() {
l.blurInput(this);
},
closeOptions() {
l.closeOptions(this);
},
prevItem() {
l.prevItem(this);
},
nextItem() {
l.nextItem(this);
},
enterItem() {
l.enterItem(this);
},
pointerSet(e) {
l.pointerSet(this, e);
},
pointerAdjust() {
l.pointerAdjust(this);
},
mousedownItem() {
l.mousedownItem(this);
},
selectItem(e) {
this.searchText = "", this.closeOptions(), typeof this.modelValue == "object" && this.modelValue ? this.$emit("update:modelValue", e) : (this.$emit("update:modelValue", e.value), e.value !== void 0 && e.value === e.text && (this.searchText = e.value));
},
optionValue(e) {
return typeof e == "object" && e !== null ? e.value : e;
}
}
}, D = /* @__PURE__ */ p("i", { class: "dropdown icon" }, null, -1), j = ["disabled", "tabindex", "id", "name", "value"], R = ["data-vss-custom-attr"], B = ["data-vss-custom-attr", "onClick", "onMouseenter"];
function F(e, t, n, d, r, s) {
return m(), c("div", {
class: f(["ui fluid search selection dropdown", { "active visible": r.showMenu, error: e.isError, disabled: e.isDisabled }]),
onClick: t[11] || (t[11] = (...i) => s.openOptions && s.openOptions(...i)),
onFocus: t[12] || (t[12] = (...i) => s.openOptions && s.openOptions(...i))
}, [
D,
p("input", {
class: "search",
autocomplete: "off",
disabled: e.isDisabled,
tabindex: e.isDisabled ? -1 : 0,
id: e.id,
name: e.name,
value: r.searchText,
onInput: t[0] || (t[0] = (i) => r.searchText = i.target.value),
ref: "input",
onFocus: t[1] || (t[1] = a((...i) => s.openOptions && s.openOptions(...i), ["prevent"])),
onKeyup: [
t[2] || (t[2] = u((...i) => s.closeOptions && s.closeOptions(...i), ["esc"])),
t[7] || (t[7] = u(a((...i) => s.enterItem && s.enterItem(...i), ["prevent"]), ["enter"]))
],
onBlur: t[3] || (t[3] = (...i) => s.blurInput && s.blurInput(...i)),
onKeydown: [
t[4] || (t[4] = u((...i) => s.prevItem && s.prevItem(...i), ["up"])),
t[5] || (t[5] = u((...i) => s.nextItem && s.nextItem(...i), ["down"])),
t[6] || (t[6] = u(a(() => {
}, ["prevent"]), ["enter"])),
t[8] || (t[8] = u((...i) => s.deleteTextOrItem && s.deleteTextOrItem(...i), ["delete"]))
]
}, null, 40, j),
p("div", {
class: f(["text", s.textClass]),
"data-vss-custom-attr": s.searchTextCustomAttr
}, x(s.inputText), 11, R),
p("div", {
class: f(["menu", s.menuClass]),
ref: "menu",
onMousedown: t[10] || (t[10] = a(() => {
}, ["prevent"])),
style: O(s.menuStyle),
tabindex: "-1"
}, [
(m(!0), c(I, null, v(s.filteredOptions, (i, o) => (m(), c("div", {
key: o,
class: f(["item", { selected: i.selected || r.pointer === o, disabled: i.disabled }]),
"data-vss-custom-attr": s.customAttrs[o] ? s.customAttrs[o] : "",
onClick: a((h) => s.selectItem(i), ["stop"]),
onMousedown: t[9] || (t[9] = (...h) => s.mousedownItem && s.mousedownItem(...h)),
onMouseenter: (h) => s.pointerSet(o)
}, [
y(e.$slots, "default", {
option: i,
idx: o
}, () => [
g(x(i.text), 1)
])
], 42, B))), 128))
], 38)
], 34);
}
const T = /* @__PURE__ */ V(k, [["render", F]]), q = {
name: "ModelListSelect",
mixins: [b],
emits: ["blur", "searchchange", "update:modelValue"],
render: function() {
return S(T, {
id: this.id,
name: this.name,
options: this.options,
modelValue: this.innerValue,
isError: this.isError,
isDisabled: this.isDisabled,
placeholder: this.placeholder,
filterPredicate: this.filterPredicate,
onBlur: () => this.$emit("blur"),
"onUpdate:modelValue": this.onInput,
onSearchchange: (e) => this.$emit("searchchange", e)
});
},
props: {
modelValue: {
type: [String, Number, Object, Boolean]
},
list: {
type: Array
},
optionValue: {
type: String
},
optionText: {
type: String
},
customText: {
type: Function
},
optionDisabled: {
type: String
}
},
computed: {
options() {
return this.list.map((e) => ({ value: e[this.optionValue], text: this.buildText(e), disabled: !!e[this.optionDisabled] }));
},
innerValue() {
return this.modelValue ? typeof this.modelValue == "object" ? this.modelValue ? {
value: this.modelValue[this.optionValue],
text: this.buildText(this.modelValue),
disabled: !!this.modelValue[this.optionDisabled]
} : { value: "", text: "", disabled: !1 } : this.modelValue : this.modelValue;
}
},
methods: {
buildText(e) {
return e[this.optionValue] !== void 0 ? this.customText ? this.customText(e) : e[this.optionText] : "";
},
onInput(e) {
if (e === void 0)
return this.$emit("update:modelValue", "");
if (Object.keys(e).length === 0 && e.constructor === Object)
this.$emit("update:modelValue", e);
else if (typeof e == "object") {
const t = this.list.find((n) => n[this.optionValue] === e.value);
this.$emit("update:modelValue", t);
} else
this.$emit("update:modelValue", e);
}
},
components: {
ModelSelect: T
}
}, P = {
name: "MultiSelect",
mixins: [b],
emits: ["blur", "searchchange", "select"],
props: {
customAttr: {
type: Function,
default: () => ""
},
options: {
type: Array
},
selectedOptions: {
type: Array
},
cleanSearch: {
type: Boolean,
default: !0
},
hideSelectedOptions: {
type: Boolean,
default: !1
}
},
data() {
return {
showMenu: !1,
searchText: "",
mousedownState: !1,
// mousedown on option menu
pointer: -1
};
},
watch: {
selectedOptions() {
this.pointer = -1;
},
filteredOptions() {
this.pointerAdjust();
},
searchText() {
this.$emit("searchchange", this.searchText);
}
},
computed: {
inputText() {
return this.searchText ? "" : this.placeholder;
},
textClass() {
return this.placeholder ? "default" : "";
},
inputWidth() {
return {
width: (this.searchText.length + 1) * 8 + 20 + "px"
};
},
menuClass() {
return {
visible: this.showMenu,
hidden: !this.showMenu
};
},
menuStyle() {
return {
display: this.showMenu ? "block" : "none"
};
},
nonSelectOptions() {
return this.options.filter((e) => this.selectedOptions.findIndex((t) => t.value === e.value) === -1);
},
filteredOptions() {
return this.searchText ? this.nonSelectOptions.filter((e) => {
try {
return this.cleanSearch ? this.filterPredicate(this.accentsTidy(e.text), this.searchText) : this.filterPredicate(e.text, this.searchText);
} catch {
return !0;
}
}) : this.nonSelectOptions;
}
},
methods: {
deleteTextOrLastItem() {
!this.searchText && this.selectedOptions.length > 0 && this.deleteItem(this.selectedOptions[this.selectedOptions.length - 1]);
},
openOptions() {
l.openOptions(this);
},
blurInput() {
l.blurInput(this);
},
closeOptions() {
l.closeOptions(this);
},
prevItem() {
l.prevItem(this), this.openOptions();
},
nextItem() {
l.nextItem(this), this.openOptions();
},
enterItem() {
l.enterItem(this);
},
pointerSet(e) {
l.pointerSet(this, e);
},
pointerAdjust() {
l.pointerAdjust(this);
},
mousedownItem() {
l.mousedownItem(this);
},
selectItem(e) {
const t = this.selectedOptions.concat(e), n = t.filter((d, r) => t.indexOf(d) === r);
this.closeOptions(), this.searchText = "", this.$emit("select", n, e, "insert");
},
deleteItem(e) {
const t = this.selectedOptions.filter((n) => n.value !== e.value);
this.$emit("select", t, e, "delete");
},
accentsTidy(e) {
let t = e.toString().toLowerCase();
return t = t.replace(new RegExp("[àáâãäå]", "g"), "a"), t = t.replace(new RegExp("æ", "g"), "ae"), t = t.replace(new RegExp("ç", "g"), "c"), t = t.replace(new RegExp("[èéêë]", "g"), "e"), t = t.replace(new RegExp("[ìíîï]", "g"), "i"), t = t.replace(new RegExp("ñ", "g"), "n"), t = t.replace(new RegExp("[òóôõö]", "g"), "o"), t = t.replace(new RegExp("œ", "g"), "oe"), t = t.replace(new RegExp("[ùúûü]", "g"), "u"), t = t.replace(new RegExp("[ýÿ]", "g"), "y"), t;
}
}
}, L = /* @__PURE__ */ p("i", { class: "dropdown icon" }, null, -1), H = ["data-vss-custom-attr"], K = ["onClick"], N = ["disabled", "tabindex", "id", "name"], z = ["data-vss-custom-attr", "onClick", "onMouseenter"];
function U(e, t, n, d, r, s) {
return m(), c("div", {
class: f(["ui fluid search dropdown selection multiple", { "active visible": r.showMenu, error: e.isError, disabled: e.isDisabled }]),
onClick: t[11] || (t[11] = (...i) => s.openOptions && s.openOptions(...i)),
onFocus: t[12] || (t[12] = (...i) => s.openOptions && s.openOptions(...i))
}, [
L,
n.hideSelectedOptions ? M("", !0) : (m(!0), c(I, { key: 0 }, v(n.selectedOptions, (i, o) => (m(), c("a", {
key: o,
class: "ui label transition visible",
style: { display: "inline-block !important" },
"data-vss-custom-attr": n.customAttr(i)
}, [
y(e.$slots, "selected", {
option: i,
idx: o
}, () => [
g(x(i.text), 1),
p("i", {
class: "delete icon",
onClick: (h) => s.deleteItem(i)
}, null, 8, K)
])
], 8, H))), 128)),
A(p("input", {
class: "search",
autocomplete: "off",
disabled: e.isDisabled,
tabindex: e.isDisabled ? -1 : 0,
id: e.id,
name: e.name,
"onUpdate:modelValue": t[0] || (t[0] = (i) => r.searchText = i),
ref: "input",
style: O(s.inputWidth),
onFocus: t[1] || (t[1] = a((...i) => s.openOptions && s.openOptions(...i), ["prevent"])),
onKeyup: [
t[2] || (t[2] = u((...i) => s.closeOptions && s.closeOptions(...i), ["esc"])),
t[7] || (t[7] = u(a((...i) => s.enterItem && s.enterItem(...i), ["prevent"]), ["enter"]))
],
onBlur: t[3] || (t[3] = (...i) => s.blurInput && s.blurInput(...i)),
onKeydown: [
t[4] || (t[4] = u((...i) => s.prevItem && s.prevItem(...i), ["up"])),
t[5] || (t[5] = u((...i) => s.nextItem && s.nextItem(...i), ["down"])),
t[6] || (t[6] = u(a(() => {
}, ["prevent"]), ["enter"])),
t[8] || (t[8] = u((...i) => s.deleteTextOrLastItem && s.deleteTextOrLastItem(...i), ["delete"]))
]
}, null, 44, N), [
[C, r.searchText]
]),
p("div", {
class: f(["text", s.textClass])
}, x(s.inputText), 3),
p("div", {
class: f(["menu", s.menuClass]),
ref: "menu",
onMousedown: t[10] || (t[10] = a(() => {
}, ["prevent"])),
style: O(s.menuStyle),
tabindex: "-1"
}, [
(m(!0), c(I, null, v(s.filteredOptions, (i, o) => (m(), c("div", {
key: o,
class: f(["item", { selected: i.selected || r.pointer === o, disabled: i.disabled }]),
"data-vss-custom-attr": n.customAttr(i),
onClick: a((h) => s.selectItem(i), ["stop"]),
onMousedown: t[9] || (t[9] = (...h) => s.mousedownItem && s.mousedownItem(...h)),
onMouseenter: (h) => s.pointerSet(o)
}, [
y(e.$slots, "default", {
option: i,
idx: o
}, () => [
g(x(i.text), 1)
])
], 42, z))), 128))
], 38)
], 34);
}
const w = /* @__PURE__ */ V(P, [["render", U]]), G = {
name: "MultiListSelect",
mixins: [b],
emits: ["blur", "searchchange", "select"],
render: function() {
return S(w, {
id: this.id,
name: this.name,
options: this.options,
selectedOptions: this.items,
isError: this.isError,
isDisabled: this.isDisabled,
placeholder: this.placeholder,
filterPredicate: this.filterPredicate,
onSelect: this.onSelect,
onSearchchange: (e) => this.$emit("searchchange", e)
});
},
props: {
list: {
type: Array
},
optionValue: {
type: String
},
optionText: {
type: String
},
customText: {
type: Function
},
selectedItems: {
type: Array
},
optionDisabled: {
type: String
}
},
computed: {
options() {
return this.list.map((e) => ({ value: e[this.optionValue], text: this.buildText(e), disabled: !!e[this.optionDisabled] }));
},
items() {
return this.selectedItems.map((e) => ({ value: e[this.optionValue], text: this.buildText(e), disabled: !!e[this.optionDisabled] }));
}
},
methods: {
buildText(e) {
return e[this.optionValue] !== void 0 ? this.customText ? this.customText(e) : e[this.optionText] : "";
},
onSelect(e, t) {
if (Object.keys(t).length === 0 && t.constructor === Object)
this.$emit("select", e, t);
else {
const n = this.list.filter((r, s) => e.find((i, o) => r[this.optionValue] === i.value)), d = this.list.find((r) => r[this.optionValue] === t.value);
this.$emit("select", n, d);
}
}
},
components: {
MultiSelect: w
}
};
export {
q as ModelListSelect,
T as ModelSelect,
G as MultiListSelect,
w as MultiSelect
};