@vue-interface/searchable-select-field 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/searchable-select-field.js +251 -0
- package/dist/searchable-select-field.js.map +1 -0
- package/dist/searchable-select-field.umd.cjs +2 -0
- package/dist/searchable-select-field.umd.cjs.map +1 -0
- package/dist/src/SearchableSelectField.vue.d.ts +100 -0
- package/dist/vite.config.d.ts +3 -0
- package/package.json +1 -1
- package/src/SearchableSelectField.vue +18 -2
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { openBlock as d, createElementBlock as v, createElementVNode as S, defineComponent as Z, useModel as _, computed as b, watchEffect as ee, ref as C, useTemplateRef as B, watch as $, nextTick as le, createVNode as x, unref as c, mergeProps as I, withKeys as k, withModifiers as m, createSlots as te, withCtx as R, renderSlot as z, normalizeProps as oe, guardReactiveProps as ne, createBlock as ae, createCommentVNode as O, normalizeClass as T, Fragment as ie, renderList as ue, toDisplayString as re, mergeModels as j } from "vue";
|
|
2
|
+
import { ActivityIndicator as se, Pulse as de } from "@vue-interface/activity-indicator";
|
|
3
|
+
import { useFormControl as ce } from "@vue-interface/form-control";
|
|
4
|
+
import { InputField as ve } from "@vue-interface/input-field";
|
|
5
|
+
import fe from "fuse.js";
|
|
6
|
+
function me(e, w) {
|
|
7
|
+
return d(), v("svg", {
|
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
9
|
+
fill: "none",
|
|
10
|
+
viewBox: "0 0 24 24",
|
|
11
|
+
"stroke-width": "1.5",
|
|
12
|
+
stroke: "currentColor",
|
|
13
|
+
"aria-hidden": "true",
|
|
14
|
+
"data-slot": "icon"
|
|
15
|
+
}, [
|
|
16
|
+
S("path", {
|
|
17
|
+
"stroke-linecap": "round",
|
|
18
|
+
"stroke-linejoin": "round",
|
|
19
|
+
d: "m19.5 8.25-7.5 7.5-7.5-7.5"
|
|
20
|
+
})
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
function ye(e, w) {
|
|
24
|
+
return d(), v("svg", {
|
|
25
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
26
|
+
fill: "none",
|
|
27
|
+
viewBox: "0 0 24 24",
|
|
28
|
+
"stroke-width": "1.5",
|
|
29
|
+
stroke: "currentColor",
|
|
30
|
+
"aria-hidden": "true",
|
|
31
|
+
"data-slot": "icon"
|
|
32
|
+
}, [
|
|
33
|
+
S("path", {
|
|
34
|
+
"stroke-linecap": "round",
|
|
35
|
+
"stroke-linejoin": "round",
|
|
36
|
+
d: "M6 18 18 6M6 6l12 12"
|
|
37
|
+
})
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
const pe = { class: "relative [&_.form-control]:pr-8" }, be = ["title", "onClick"], ke = { class: "truncate" }, $e = /* @__PURE__ */ Z({
|
|
41
|
+
__name: "SearchableSelectField",
|
|
42
|
+
props: /* @__PURE__ */ j({
|
|
43
|
+
activity: { type: Boolean },
|
|
44
|
+
disabled: { type: Boolean },
|
|
45
|
+
error: {},
|
|
46
|
+
errors: {},
|
|
47
|
+
feedback: {},
|
|
48
|
+
formControlClass: { default: "form-control" },
|
|
49
|
+
helpText: {},
|
|
50
|
+
id: {},
|
|
51
|
+
indicator: {},
|
|
52
|
+
indicatorSize: {},
|
|
53
|
+
invalid: { type: Boolean },
|
|
54
|
+
label: {},
|
|
55
|
+
labelClass: { default: "form-label" },
|
|
56
|
+
modelValue: {},
|
|
57
|
+
name: {},
|
|
58
|
+
plaintext: { type: Boolean },
|
|
59
|
+
size: { default: "form-control-md" },
|
|
60
|
+
color: {},
|
|
61
|
+
readonly: { type: Boolean },
|
|
62
|
+
valid: { type: Boolean },
|
|
63
|
+
value: {},
|
|
64
|
+
options: { default: () => [] },
|
|
65
|
+
fuseOptions: {},
|
|
66
|
+
display: {},
|
|
67
|
+
allowCustom: { type: Boolean },
|
|
68
|
+
clearable: { type: Boolean, default: !0 }
|
|
69
|
+
}, {
|
|
70
|
+
modelValue: {},
|
|
71
|
+
modelModifiers: {}
|
|
72
|
+
}),
|
|
73
|
+
emits: /* @__PURE__ */ j(["blur", "focus", "focusin", "focusout", "click", "doubleclick", "contextmenu", "mousedown", "mouseup", "mouseover", "mouseout", "mouseenter", "mouseleave", "mousemove", "keydown", "keyup", "keypress", "select", "selectionchange", "invalid", "submit", "reset", "scroll", "wheel", "copy", "cut", "paste", "touchstart", "touchend", "touchmove", "touchcancel", "change", "input", "beforeinput"], ["update:modelValue"]),
|
|
74
|
+
setup(e, { emit: w }) {
|
|
75
|
+
const t = e, u = _(e, "modelValue"), s = b(() => !t.disabled && !t.readonly), P = w, {
|
|
76
|
+
controlAttributes: A,
|
|
77
|
+
formGroupClasses: N,
|
|
78
|
+
listeners: D
|
|
79
|
+
} = ce({ model: u, props: t, emit: P });
|
|
80
|
+
ee(() => {
|
|
81
|
+
t.value !== void 0 && (u.value = t.value);
|
|
82
|
+
});
|
|
83
|
+
const i = C(), V = B("field"), a = C(!1), n = C(), p = B("buttons"), h = B("optionsEl"), G = b(() => typeof t.options == "object" && t.options?.[0] ? Object.keys(t.options?.[0]) : ["$"]);
|
|
84
|
+
let K = F();
|
|
85
|
+
function F() {
|
|
86
|
+
return new fe(t.options ?? [], t.fuseOptions ?? {
|
|
87
|
+
includeScore: !0,
|
|
88
|
+
threshold: 0.45,
|
|
89
|
+
keys: G.value
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const f = b(() => {
|
|
93
|
+
if (!i.value)
|
|
94
|
+
return t.options ?? [];
|
|
95
|
+
const l = K.search(i.value).map(({ item: o }) => o);
|
|
96
|
+
return t.allowCustom && !l.length ? t.options : l;
|
|
97
|
+
});
|
|
98
|
+
$(() => t.options, () => {
|
|
99
|
+
K = F();
|
|
100
|
+
});
|
|
101
|
+
function q(l) {
|
|
102
|
+
const o = h.value;
|
|
103
|
+
if (!o || !l)
|
|
104
|
+
return;
|
|
105
|
+
const r = o.getBoundingClientRect(), E = l.getBoundingClientRect().top - r.top + o.scrollTop;
|
|
106
|
+
o.scrollTop = E;
|
|
107
|
+
}
|
|
108
|
+
$([i, n], ([l, o]) => {
|
|
109
|
+
l ? p.value?.[0]?.scrollIntoView({
|
|
110
|
+
block: "nearest",
|
|
111
|
+
inline: "nearest"
|
|
112
|
+
}) : o !== void 0 && p.value?.[o]?.scrollIntoView({
|
|
113
|
+
block: "nearest",
|
|
114
|
+
inline: "nearest"
|
|
115
|
+
});
|
|
116
|
+
}), $(h, (l) => {
|
|
117
|
+
!l || n.value === void 0 || le(() => {
|
|
118
|
+
q(p.value?.[n.value]);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
function y(l) {
|
|
122
|
+
u.value = l, n.value = l && t.options.includes(l) ? t.options.indexOf(l) : void 0, i.value = void 0, a.value = !1;
|
|
123
|
+
}
|
|
124
|
+
function L(l) {
|
|
125
|
+
s.value && (a.value = !0, n.value = void 0, i.value = l.target?.value, !i.value && !t.allowCustom && (u.value = void 0), t.allowCustom && (u.value = i.value));
|
|
126
|
+
}
|
|
127
|
+
function U() {
|
|
128
|
+
if (s.value) {
|
|
129
|
+
if (!a.value) {
|
|
130
|
+
a.value = !0;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
t.allowCustom && n.value === void 0 ? y(i.value ?? u.value) : n.value === void 0 ? y(f.value[0]) : f.value[n.value] ? y(f.value[n.value]) : y(void 0);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function H() {
|
|
137
|
+
s.value && (a.value = !0, n.value ? n.value-- : n.value = f.value.length - 1);
|
|
138
|
+
}
|
|
139
|
+
function J() {
|
|
140
|
+
s.value && (a.value = !0, n.value === void 0 || n.value === f.value.length - 1 ? n.value = 0 : n.value++);
|
|
141
|
+
}
|
|
142
|
+
function Q() {
|
|
143
|
+
a.value = !1, i.value = void 0;
|
|
144
|
+
}
|
|
145
|
+
function W(l) {
|
|
146
|
+
s.value && y(l);
|
|
147
|
+
}
|
|
148
|
+
function X() {
|
|
149
|
+
s.value && (i.value = void 0, u.value = void 0);
|
|
150
|
+
}
|
|
151
|
+
function Y() {
|
|
152
|
+
s.value && (a.value ? a.value = !1 : (a.value = !0, V.value?.$el?.querySelector("input")?.focus()));
|
|
153
|
+
}
|
|
154
|
+
const M = b(() => t.clearable && (!!i.value || !!u.value) && s.value);
|
|
155
|
+
return (l, o) => (d(), v("div", pe, [
|
|
156
|
+
x(c(ve), I({
|
|
157
|
+
ref_key: "field",
|
|
158
|
+
ref: V,
|
|
159
|
+
class: ["searchable-select-field-input", { "has-clear-button": M.value, formGroupClasses: c(N) }],
|
|
160
|
+
size: e.size
|
|
161
|
+
}, { ...l.$attrs, controlAttributes: c(A), listeners: c(D) }, {
|
|
162
|
+
name: e.name,
|
|
163
|
+
label: e.label,
|
|
164
|
+
"model-value": i.value ?? (u.value && t?.display ? t?.display?.(u.value) : u.value),
|
|
165
|
+
disabled: e.disabled,
|
|
166
|
+
readonly: e.readonly,
|
|
167
|
+
color: e.color,
|
|
168
|
+
error: e.error,
|
|
169
|
+
errors: e.errors,
|
|
170
|
+
feedback: e.feedback,
|
|
171
|
+
valid: e.valid,
|
|
172
|
+
invalid: e.invalid,
|
|
173
|
+
onClick: o[0] || (o[0] = (r) => s.value && (a.value = !0)),
|
|
174
|
+
onFocus: o[1] || (o[1] = (r) => s.value && (a.value = !0)),
|
|
175
|
+
onBlur: Q,
|
|
176
|
+
onKeypress: k(m(U, ["prevent"]), ["enter"]),
|
|
177
|
+
onKeydown: [
|
|
178
|
+
k(m(H, ["prevent"]), ["up"]),
|
|
179
|
+
k(m(J, ["prevent"]), ["down"])
|
|
180
|
+
],
|
|
181
|
+
onKeyup: o[2] || (o[2] = k((r) => a.value = !1, ["escape"])),
|
|
182
|
+
onInput: L
|
|
183
|
+
}), te({
|
|
184
|
+
activity: R(() => [
|
|
185
|
+
z(l.$slots, "activity", oe(ne({ disabled: e.disabled, options: e.options, invalid: e.invalid, valid: e.valid })), () => [
|
|
186
|
+
!e.disabled && !e.options ? (d(), ae(c(se), {
|
|
187
|
+
key: 0,
|
|
188
|
+
type: c(de),
|
|
189
|
+
size: "activity-indicator-sm"
|
|
190
|
+
}, null, 8, ["type"])) : M.value ? (d(), v("button", {
|
|
191
|
+
key: 1,
|
|
192
|
+
type: "button",
|
|
193
|
+
class: "searchable-select-field-clear-button",
|
|
194
|
+
onClick: m(X, ["stop"])
|
|
195
|
+
}, [
|
|
196
|
+
x(c(ye), { class: "size-[1.25em]" })
|
|
197
|
+
])) : !e.invalid && !e.valid ? (d(), v("button", {
|
|
198
|
+
key: 2,
|
|
199
|
+
type: "button",
|
|
200
|
+
onClick: m(Y, ["stop"])
|
|
201
|
+
}, [
|
|
202
|
+
x(c(me), { class: "size-[1em]" })
|
|
203
|
+
])) : O("", !0)
|
|
204
|
+
])
|
|
205
|
+
]),
|
|
206
|
+
_: 2
|
|
207
|
+
}, [
|
|
208
|
+
l.$slots.icon ? {
|
|
209
|
+
name: "icon",
|
|
210
|
+
fn: R(() => [
|
|
211
|
+
z(l.$slots, "icon")
|
|
212
|
+
]),
|
|
213
|
+
key: "0"
|
|
214
|
+
} : void 0
|
|
215
|
+
]), 1040, ["class", "size", "name", "label", "model-value", "disabled", "readonly", "color", "error", "errors", "feedback", "valid", "invalid", "onKeypress", "onKeydown"]),
|
|
216
|
+
a.value && f.value.length ? (d(), v("div", {
|
|
217
|
+
key: 0,
|
|
218
|
+
ref_key: "optionsEl",
|
|
219
|
+
ref: h,
|
|
220
|
+
tabindex: "-1",
|
|
221
|
+
class: T(["searchable-select-field-dropdown", e.size]),
|
|
222
|
+
onMousedown: o[4] || (o[4] = m(() => {
|
|
223
|
+
}, ["prevent", "stop"]))
|
|
224
|
+
}, [
|
|
225
|
+
(d(!0), v(ie, null, ue(f.value, (r, g) => (d(), v("button", {
|
|
226
|
+
ref_for: !0,
|
|
227
|
+
ref_key: "buttons",
|
|
228
|
+
ref: p,
|
|
229
|
+
key: g,
|
|
230
|
+
type: "button",
|
|
231
|
+
tabindex: "-1",
|
|
232
|
+
title: e.display?.(r) ?? String(r),
|
|
233
|
+
class: T({
|
|
234
|
+
"bg-neutral-200 dark:bg-neutral-700": n.value === g
|
|
235
|
+
}),
|
|
236
|
+
onMousedown: o[3] || (o[3] = m(() => {
|
|
237
|
+
}, ["prevent"])),
|
|
238
|
+
onClick: (E) => W(r)
|
|
239
|
+
}, [
|
|
240
|
+
z(l.$slots, "default", I({ ref_for: !0 }, { option: r, display: e.display }), () => [
|
|
241
|
+
S("div", ke, re(e.display?.(r) ?? r), 1)
|
|
242
|
+
])
|
|
243
|
+
], 42, be))), 128))
|
|
244
|
+
], 34)) : O("", !0)
|
|
245
|
+
]));
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
export {
|
|
249
|
+
$e as SearchableSelectField
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=searchable-select-field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchable-select-field.js","sources":["../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/ChevronDownIcon.js","../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/XMarkIcon.js","../src/SearchableSelectField.vue"],"sourcesContent":["import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"m19.5 8.25-7.5 7.5-7.5-7.5\"\n })\n ]))\n}","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"M6 18 18 6M6 6l12 12\"\n })\n ]))\n}","<script setup lang=\"ts\" generic=\"T, Value\">\nimport { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';\nimport { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';\nimport type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';\nimport { useFormControl } from '@vue-interface/form-control';\nimport { InputField } from '@vue-interface/input-field';\nimport Fuse, { IFuseOptions } from 'fuse.js';\nimport { InputHTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';\n\nconst props = withDefaults(defineProps<SearchableSelectFieldProps<T,Value>>(), {\n formControlClass: 'form-control',\n labelClass: 'form-label',\n size: 'form-control-md',\n clearable: true,\n options: () => []\n});\n\nconst model = defineModel<T>();\nconst isInteractive = computed(() => !props.disabled && !props.readonly);\n\ndefineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,T> & {\n default(props: { option: T; display?: (option: T) => string }): any;\n}>();\n\nconst emit = defineEmits<FormControlEvents>();\n\nconst {\n controlAttributes,\n formGroupClasses,\n listeners\n} = useFormControl<InputHTMLAttributes, SearchableSelectFieldSizePrefix, T|undefined, T>({ model, props, emit });\n\nwatchEffect(() => {\n if(props.value !== undefined) {\n model.value = props.value;\n }\n});\n\nconst input = ref<string>();\nconst field = useTemplateRef<any>('field');\nconst showOptions = ref(false);\nconst active = ref<number>();\nconst buttons = useTemplateRef<HTMLButtonElement[]>('buttons');\nconst optionsEl = useTemplateRef<HTMLDivElement>('optionsEl');\n\nconst keys = computed(() => {\n return typeof props.options === 'object' && props.options?.[0]\n ? Object.keys(props.options?.[0])\n : ['$'];\n});\n\nlet fuse: Fuse<T> = createFuse();\n\nfunction createFuse() {\n return new Fuse(props.options ?? [], props.fuseOptions ?? {\n includeScore: true,\n threshold: .45,\n keys: keys.value\n });\n}\n\nconst filtered = computed<T[]>(() => {\n if(!input.value) {\n return props.options ?? [];\n }\n\n const matches = fuse.search(input.value).map(({ item }) => item);\n\n if(props.allowCustom && !matches.length) {\n return props.options;\n }\n\n return matches;\n});\n\nwatch(() => props.options, () => {\n fuse = createFuse();\n});\n\nfunction scrollIntoView(child?: HTMLElement) {\n const parent = optionsEl.value;\n\n if(!parent || !child) {\n return;\n }\n\n const parentRect = parent.getBoundingClientRect();\n const childRect = child.getBoundingClientRect();\n\n const childTop = childRect.top - parentRect.top + parent.scrollTop;\n\n parent.scrollTop = childTop;\n};\n\nwatch([input, active], ([input, active]) => {\n if(input) {\n buttons.value?.[0]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n else if(active !== undefined) {\n buttons.value?.[active]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n});\n\nwatch(optionsEl, (value) => {\n if(!value || active.value === undefined) {\n return;\n }\n\n nextTick(() => {\n scrollIntoView(buttons.value?.[active.value as number]);\n });\n});\n\nfunction select(option?: T) {\n model.value = option;\n active.value = option && props.options.includes(option)\n ? props.options.indexOf(option)\n : undefined;\n input.value = undefined;\n showOptions.value = false;\n}\n\nfunction onInput(e: Event) {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n active.value = undefined;\n input.value = (e.target as HTMLInputElement)?.value;\n\n if(!input.value && !props.allowCustom) {\n model.value = undefined;\n }\n\n if(props.allowCustom) {\n model.value = input.value as T;\n }\n}\n\nfunction onKeypressEnter() {\n if (!isInteractive.value) return;\n\n if(!showOptions.value) {\n showOptions.value = true;\n return;\n }\n\n if(props.allowCustom && active.value === undefined) {\n select(input.value as T ?? model.value);\n }\n else if(active.value === undefined) {\n select(filtered.value[0]);\n }\n else if(filtered.value[active.value]) {\n select(filtered.value[active.value]);\n }\n else {\n select(undefined);\n }\n}\n\nfunction onKeydownUp() {\n if (!isInteractive.value) return;\n \n showOptions.value = true;\n\n if(!active.value) {\n active.value = filtered.value.length - 1;\n }\n else {\n active.value--;\n }\n}\n\nfunction onKeydownDown() {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n\n if(active.value === undefined || active.value === filtered.value.length - 1) {\n active.value = 0;\n }\n else {\n active.value++;\n }\n}\n\nfunction onBlur() {\n showOptions.value = false;\n input.value = undefined;\n}\n\nfunction onClickOption(option: T) {\n if (!isInteractive.value) return;\n select(option);\n}\n\nfunction clear() {\n if (!isInteractive.value) return;\n input.value = undefined;\n model.value = undefined;\n}\n\nfunction toggle() {\n if (!isInteractive.value) return;\n \n if(showOptions.value ) {\n showOptions.value = false;\n }\n else {\n showOptions.value = true;\n field.value?.$el?.querySelector('input')?.focus();\n }\n}\n\nconst canClear = computed(() => {\n return props.clearable && (!!input.value || !!model.value) && isInteractive.value;\n});\n</script>\n<script lang=\"ts\">\nexport type SearchableSelectFieldSizePrefix = 'form-control';\n\nexport type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<\n InputHTMLAttributes, \n SearchableSelectFieldSizePrefix, \n ModelValue, \n Value\n> & {\n options?: ModelValue[];\n fuseOptions?: IFuseOptions<ModelValue>;\n display?: (option: ModelValue) => string;\n allowCustom?: boolean;\n clearable?: boolean;\n};\n</script>\n\n\n<template>\n <div class=\"relative [&_.form-control]:pr-8\">\n <InputField\n ref=\"field\"\n class=\"searchable-select-field-input\"\n :class=\"{ 'has-clear-button': canClear, formGroupClasses }\"\n :size=\"size\"\n v-bind=\"{ ...$attrs, controlAttributes, listeners }\"\n :name=\"name\"\n :label=\"label\"\n :model-value=\"input ?? (model && props?.display ? props?.display?.(model) : model)\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :color=\"color\"\n :error=\"error\"\n :errors=\"errors\"\n :feedback=\"feedback\"\n :valid=\"valid\"\n :invalid=\"invalid\"\n @click=\"isInteractive && (showOptions = true)\"\n @focus=\"isInteractive && (showOptions = true)\"\n @blur=\"onBlur\"\n @keypress.enter.prevent=\"onKeypressEnter\"\n @keydown.up.prevent=\"onKeydownUp\"\n @keydown.down.prevent=\"onKeydownDown\"\n @keyup.escape=\"showOptions = false\"\n @input=\"onInput\">\n <template #icon v-if=\"$slots.icon\">\n <slot name=\"icon\" />\n </template>\n <template #activity>\n <slot\n name=\"activity\"\n v-bind=\"{ disabled, options, invalid, valid }\">\n <ActivityIndicator\n v-if=\"!disabled && !options\"\n :type=\"Pulse\"\n size=\"activity-indicator-sm\" />\n <button\n v-else-if=\"canClear\"\n type=\"button\"\n class=\"searchable-select-field-clear-button\"\n @click.stop=\"clear\">\n <XMarkIcon class=\"size-[1.25em]\" />\n </button>\n <button\n v-else-if=\"!invalid && !valid\"\n type=\"button\"\n @click.stop=\"toggle\">\n <ChevronDownIcon class=\"size-[1em]\" />\n </button>\n </slot>\n </template>\n </InputField>\n <div\n v-if=\"showOptions && filtered.length\"\n ref=\"optionsEl\"\n tabindex=\"-1\"\n class=\"searchable-select-field-dropdown\"\n :class=\"size\"\n @mousedown.prevent.stop>\n <button\n v-for=\"(option, i) in filtered\"\n ref=\"buttons\"\n :key=\"i\"\n type=\"button\"\n tabindex=\"-1\"\n :title=\"display?.(option) ?? String(option)\"\n :class=\"{\n ['bg-neutral-200 dark:bg-neutral-700']: active === i\n }\"\n @mousedown.prevent\n @click=\"onClickOption(option)\">\n <slot v-bind=\"{ option, display }\">\n <div class=\"truncate\">\n {{ display?.(option) ?? option }}\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>"],"names":["render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","props","__props","model","_useModel","isInteractive","computed","emit","__emit","controlAttributes","formGroupClasses","listeners","useFormControl","watchEffect","input","ref","field","useTemplateRef","showOptions","active","buttons","optionsEl","keys","fuse","createFuse","Fuse","filtered","matches","item","watch","scrollIntoView","child","parent","parentRect","childTop","value","nextTick","select","option","onInput","e","onKeypressEnter","onKeydownUp","onKeydownDown","onBlur","onClickOption","clear","toggle","canClear","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","$event","_renderSlot","_normalizeProps","_guardReactiveProps","_createBlock","ActivityIndicator","Pulse","XMarkIcon","ChevronDownIcon","$slots","_normalizeClass","_Fragment","_renderList","i","_hoisted_3","_toDisplayString"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(s,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("@vue-interface/activity-indicator"),require("@vue-interface/form-control"),require("@vue-interface/input-field"),require("fuse.js")):typeof define=="function"&&define.amd?define(["exports","vue","@vue-interface/activity-indicator","@vue-interface/form-control","@vue-interface/input-field","fuse.js"],e):(s=typeof globalThis<"u"?globalThis:s||self,e(s.SearchableSelectField={},s.Vue,s.VueInterfaceActivityIndicator,s.VueInterfaceFormControl,s.VueInterfaceInputField,s.FuseJs))})(this,(function(s,e,w,V,S,E){"use strict";function M(t,p){return e.openBlock(),e.createElementBlock("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true","data-slot":"icon"},[e.createElementVNode("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"m19.5 8.25-7.5 7.5-7.5-7.5"})])}function x(t,p){return e.openBlock(),e.createElementBlock("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true","data-slot":"icon"},[e.createElementVNode("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M6 18 18 6M6 6l12 12"})])}const z={class:"relative [&_.form-control]:pr-8"},K=["title","onClick"],$={class:"truncate"},F=e.defineComponent({__name:"SearchableSelectField",props:e.mergeModels({activity:{type:Boolean},disabled:{type:Boolean},error:{},errors:{},feedback:{},formControlClass:{default:"form-control"},helpText:{},id:{},indicator:{},indicatorSize:{},invalid:{type:Boolean},label:{},labelClass:{default:"form-label"},modelValue:{},name:{},plaintext:{type:Boolean},size:{default:"form-control-md"},color:{},readonly:{type:Boolean},valid:{type:Boolean},value:{},options:{default:()=>[]},fuseOptions:{},display:{},allowCustom:{type:Boolean},clearable:{type:Boolean,default:!0}},{modelValue:{},modelModifiers:{}}),emits:e.mergeModels(["blur","focus","focusin","focusout","click","doubleclick","contextmenu","mousedown","mouseup","mouseover","mouseout","mouseenter","mouseleave","mousemove","keydown","keyup","keypress","select","selectionchange","invalid","submit","reset","scroll","wheel","copy","cut","paste","touchstart","touchend","touchmove","touchcancel","change","input","beforeinput"],["update:modelValue"]),setup(t,{emit:p}){const o=t,c=e.useModel(t,"modelValue"),d=e.computed(()=>!o.disabled&&!o.readonly),I=p,{controlAttributes:T,formGroupClasses:R,listeners:j}=V.useFormControl({model:c,props:o,emit:I});e.watchEffect(()=>{o.value!==void 0&&(c.value=o.value)});const r=e.ref(),h=e.useTemplateRef("field"),a=e.ref(!1),i=e.ref(),v=e.useTemplateRef("buttons"),y=e.useTemplateRef("optionsEl"),N=e.computed(()=>typeof o.options=="object"&&o.options?.[0]?Object.keys(o.options?.[0]):["$"]);let b=B();function B(){return new E(o.options??[],o.fuseOptions??{includeScore:!0,threshold:.45,keys:N.value})}const f=e.computed(()=>{if(!r.value)return o.options??[];const l=b.search(r.value).map(({item:n})=>n);return o.allowCustom&&!l.length?o.options:l});e.watch(()=>o.options,()=>{b=B()});function O(l){const n=y.value;if(!n||!l)return;const u=n.getBoundingClientRect(),g=l.getBoundingClientRect().top-u.top+n.scrollTop;n.scrollTop=g}e.watch([r,i],([l,n])=>{l?v.value?.[0]?.scrollIntoView({block:"nearest",inline:"nearest"}):n!==void 0&&v.value?.[n]?.scrollIntoView({block:"nearest",inline:"nearest"})}),e.watch(y,l=>{!l||i.value===void 0||e.nextTick(()=>{O(v.value?.[i.value])})});function m(l){c.value=l,i.value=l&&o.options.includes(l)?o.options.indexOf(l):void 0,r.value=void 0,a.value=!1}function q(l){d.value&&(a.value=!0,i.value=void 0,r.value=l.target?.value,!r.value&&!o.allowCustom&&(c.value=void 0),o.allowCustom&&(c.value=r.value))}function P(){if(d.value){if(!a.value){a.value=!0;return}o.allowCustom&&i.value===void 0?m(r.value??c.value):i.value===void 0?m(f.value[0]):f.value[i.value]?m(f.value[i.value]):m(void 0)}}function A(){d.value&&(a.value=!0,i.value?i.value--:i.value=f.value.length-1)}function D(){d.value&&(a.value=!0,i.value===void 0||i.value===f.value.length-1?i.value=0:i.value++)}function G(){a.value=!1,r.value=void 0}function J(l){d.value&&m(l)}function L(){d.value&&(r.value=void 0,c.value=void 0)}function U(){d.value&&(a.value?a.value=!1:(a.value=!0,h.value?.$el?.querySelector("input")?.focus()))}const C=e.computed(()=>o.clearable&&(!!r.value||!!c.value)&&d.value);return(l,n)=>(e.openBlock(),e.createElementBlock("div",z,[e.createVNode(e.unref(S.InputField),e.mergeProps({ref_key:"field",ref:h,class:["searchable-select-field-input",{"has-clear-button":C.value,formGroupClasses:e.unref(R)}],size:t.size},{...l.$attrs,controlAttributes:e.unref(T),listeners:e.unref(j)},{name:t.name,label:t.label,"model-value":r.value??(c.value&&o?.display?o?.display?.(c.value):c.value),disabled:t.disabled,readonly:t.readonly,color:t.color,error:t.error,errors:t.errors,feedback:t.feedback,valid:t.valid,invalid:t.invalid,onClick:n[0]||(n[0]=u=>d.value&&(a.value=!0)),onFocus:n[1]||(n[1]=u=>d.value&&(a.value=!0)),onBlur:G,onKeypress:e.withKeys(e.withModifiers(P,["prevent"]),["enter"]),onKeydown:[e.withKeys(e.withModifiers(A,["prevent"]),["up"]),e.withKeys(e.withModifiers(D,["prevent"]),["down"])],onKeyup:n[2]||(n[2]=e.withKeys(u=>a.value=!1,["escape"])),onInput:q}),e.createSlots({activity:e.withCtx(()=>[e.renderSlot(l.$slots,"activity",e.normalizeProps(e.guardReactiveProps({disabled:t.disabled,options:t.options,invalid:t.invalid,valid:t.valid})),()=>[!t.disabled&&!t.options?(e.openBlock(),e.createBlock(e.unref(w.ActivityIndicator),{key:0,type:e.unref(w.Pulse),size:"activity-indicator-sm"},null,8,["type"])):C.value?(e.openBlock(),e.createElementBlock("button",{key:1,type:"button",class:"searchable-select-field-clear-button",onClick:e.withModifiers(L,["stop"])},[e.createVNode(e.unref(x),{class:"size-[1.25em]"})])):!t.invalid&&!t.valid?(e.openBlock(),e.createElementBlock("button",{key:2,type:"button",onClick:e.withModifiers(U,["stop"])},[e.createVNode(e.unref(M),{class:"size-[1em]"})])):e.createCommentVNode("",!0)])]),_:2},[l.$slots.icon?{name:"icon",fn:e.withCtx(()=>[e.renderSlot(l.$slots,"icon")]),key:"0"}:void 0]),1040,["class","size","name","label","model-value","disabled","readonly","color","error","errors","feedback","valid","invalid","onKeypress","onKeydown"]),a.value&&f.value.length?(e.openBlock(),e.createElementBlock("div",{key:0,ref_key:"optionsEl",ref:y,tabindex:"-1",class:e.normalizeClass(["searchable-select-field-dropdown",t.size]),onMousedown:n[4]||(n[4]=e.withModifiers(()=>{},["prevent","stop"]))},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(f.value,(u,k)=>(e.openBlock(),e.createElementBlock("button",{ref_for:!0,ref_key:"buttons",ref:v,key:k,type:"button",tabindex:"-1",title:t.display?.(u)??String(u),class:e.normalizeClass({"bg-neutral-200 dark:bg-neutral-700":i.value===k}),onMousedown:n[3]||(n[3]=e.withModifiers(()=>{},["prevent"])),onClick:g=>J(u)},[e.renderSlot(l.$slots,"default",e.mergeProps({ref_for:!0},{option:u,display:t.display}),()=>[e.createElementVNode("div",$,e.toDisplayString(t.display?.(u)??u),1)])],42,K))),128))],34)):e.createCommentVNode("",!0)]))}});s.SearchableSelectField=F,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
|
|
2
|
+
//# sourceMappingURL=searchable-select-field.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchable-select-field.umd.cjs","sources":["../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/ChevronDownIcon.js","../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/XMarkIcon.js","../src/SearchableSelectField.vue"],"sourcesContent":["import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"m19.5 8.25-7.5 7.5-7.5-7.5\"\n })\n ]))\n}","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"M6 18 18 6M6 6l12 12\"\n })\n ]))\n}","<script setup lang=\"ts\" generic=\"T, Value\">\nimport { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';\nimport { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';\nimport type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';\nimport { useFormControl } from '@vue-interface/form-control';\nimport { InputField } from '@vue-interface/input-field';\nimport Fuse, { IFuseOptions } from 'fuse.js';\nimport { InputHTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';\n\nconst props = withDefaults(defineProps<SearchableSelectFieldProps<T,Value>>(), {\n formControlClass: 'form-control',\n labelClass: 'form-label',\n size: 'form-control-md',\n clearable: true,\n options: () => []\n});\n\nconst model = defineModel<T>();\nconst isInteractive = computed(() => !props.disabled && !props.readonly);\n\ndefineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,T> & {\n default(props: { option: T; display?: (option: T) => string }): any;\n}>();\n\nconst emit = defineEmits<FormControlEvents>();\n\nconst {\n controlAttributes,\n formGroupClasses,\n listeners\n} = useFormControl<InputHTMLAttributes, SearchableSelectFieldSizePrefix, T|undefined, T>({ model, props, emit });\n\nwatchEffect(() => {\n if(props.value !== undefined) {\n model.value = props.value;\n }\n});\n\nconst input = ref<string>();\nconst field = useTemplateRef<any>('field');\nconst showOptions = ref(false);\nconst active = ref<number>();\nconst buttons = useTemplateRef<HTMLButtonElement[]>('buttons');\nconst optionsEl = useTemplateRef<HTMLDivElement>('optionsEl');\n\nconst keys = computed(() => {\n return typeof props.options === 'object' && props.options?.[0]\n ? Object.keys(props.options?.[0])\n : ['$'];\n});\n\nlet fuse: Fuse<T> = createFuse();\n\nfunction createFuse() {\n return new Fuse(props.options ?? [], props.fuseOptions ?? {\n includeScore: true,\n threshold: .45,\n keys: keys.value\n });\n}\n\nconst filtered = computed<T[]>(() => {\n if(!input.value) {\n return props.options ?? [];\n }\n\n const matches = fuse.search(input.value).map(({ item }) => item);\n\n if(props.allowCustom && !matches.length) {\n return props.options;\n }\n\n return matches;\n});\n\nwatch(() => props.options, () => {\n fuse = createFuse();\n});\n\nfunction scrollIntoView(child?: HTMLElement) {\n const parent = optionsEl.value;\n\n if(!parent || !child) {\n return;\n }\n\n const parentRect = parent.getBoundingClientRect();\n const childRect = child.getBoundingClientRect();\n\n const childTop = childRect.top - parentRect.top + parent.scrollTop;\n\n parent.scrollTop = childTop;\n};\n\nwatch([input, active], ([input, active]) => {\n if(input) {\n buttons.value?.[0]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n else if(active !== undefined) {\n buttons.value?.[active]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n});\n\nwatch(optionsEl, (value) => {\n if(!value || active.value === undefined) {\n return;\n }\n\n nextTick(() => {\n scrollIntoView(buttons.value?.[active.value as number]);\n });\n});\n\nfunction select(option?: T) {\n model.value = option;\n active.value = option && props.options.includes(option)\n ? props.options.indexOf(option)\n : undefined;\n input.value = undefined;\n showOptions.value = false;\n}\n\nfunction onInput(e: Event) {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n active.value = undefined;\n input.value = (e.target as HTMLInputElement)?.value;\n\n if(!input.value && !props.allowCustom) {\n model.value = undefined;\n }\n\n if(props.allowCustom) {\n model.value = input.value as T;\n }\n}\n\nfunction onKeypressEnter() {\n if (!isInteractive.value) return;\n\n if(!showOptions.value) {\n showOptions.value = true;\n return;\n }\n\n if(props.allowCustom && active.value === undefined) {\n select(input.value as T ?? model.value);\n }\n else if(active.value === undefined) {\n select(filtered.value[0]);\n }\n else if(filtered.value[active.value]) {\n select(filtered.value[active.value]);\n }\n else {\n select(undefined);\n }\n}\n\nfunction onKeydownUp() {\n if (!isInteractive.value) return;\n \n showOptions.value = true;\n\n if(!active.value) {\n active.value = filtered.value.length - 1;\n }\n else {\n active.value--;\n }\n}\n\nfunction onKeydownDown() {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n\n if(active.value === undefined || active.value === filtered.value.length - 1) {\n active.value = 0;\n }\n else {\n active.value++;\n }\n}\n\nfunction onBlur() {\n showOptions.value = false;\n input.value = undefined;\n}\n\nfunction onClickOption(option: T) {\n if (!isInteractive.value) return;\n select(option);\n}\n\nfunction clear() {\n if (!isInteractive.value) return;\n input.value = undefined;\n model.value = undefined;\n}\n\nfunction toggle() {\n if (!isInteractive.value) return;\n \n if(showOptions.value ) {\n showOptions.value = false;\n }\n else {\n showOptions.value = true;\n field.value?.$el?.querySelector('input')?.focus();\n }\n}\n\nconst canClear = computed(() => {\n return props.clearable && (!!input.value || !!model.value) && isInteractive.value;\n});\n</script>\n<script lang=\"ts\">\nexport type SearchableSelectFieldSizePrefix = 'form-control';\n\nexport type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<\n InputHTMLAttributes, \n SearchableSelectFieldSizePrefix, \n ModelValue, \n Value\n> & {\n options?: ModelValue[];\n fuseOptions?: IFuseOptions<ModelValue>;\n display?: (option: ModelValue) => string;\n allowCustom?: boolean;\n clearable?: boolean;\n};\n</script>\n\n\n<template>\n <div class=\"relative [&_.form-control]:pr-8\">\n <InputField\n ref=\"field\"\n class=\"searchable-select-field-input\"\n :class=\"{ 'has-clear-button': canClear, formGroupClasses }\"\n :size=\"size\"\n v-bind=\"{ ...$attrs, controlAttributes, listeners }\"\n :name=\"name\"\n :label=\"label\"\n :model-value=\"input ?? (model && props?.display ? props?.display?.(model) : model)\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :color=\"color\"\n :error=\"error\"\n :errors=\"errors\"\n :feedback=\"feedback\"\n :valid=\"valid\"\n :invalid=\"invalid\"\n @click=\"isInteractive && (showOptions = true)\"\n @focus=\"isInteractive && (showOptions = true)\"\n @blur=\"onBlur\"\n @keypress.enter.prevent=\"onKeypressEnter\"\n @keydown.up.prevent=\"onKeydownUp\"\n @keydown.down.prevent=\"onKeydownDown\"\n @keyup.escape=\"showOptions = false\"\n @input=\"onInput\">\n <template #icon v-if=\"$slots.icon\">\n <slot name=\"icon\" />\n </template>\n <template #activity>\n <slot\n name=\"activity\"\n v-bind=\"{ disabled, options, invalid, valid }\">\n <ActivityIndicator\n v-if=\"!disabled && !options\"\n :type=\"Pulse\"\n size=\"activity-indicator-sm\" />\n <button\n v-else-if=\"canClear\"\n type=\"button\"\n class=\"searchable-select-field-clear-button\"\n @click.stop=\"clear\">\n <XMarkIcon class=\"size-[1.25em]\" />\n </button>\n <button\n v-else-if=\"!invalid && !valid\"\n type=\"button\"\n @click.stop=\"toggle\">\n <ChevronDownIcon class=\"size-[1em]\" />\n </button>\n </slot>\n </template>\n </InputField>\n <div\n v-if=\"showOptions && filtered.length\"\n ref=\"optionsEl\"\n tabindex=\"-1\"\n class=\"searchable-select-field-dropdown\"\n :class=\"size\"\n @mousedown.prevent.stop>\n <button\n v-for=\"(option, i) in filtered\"\n ref=\"buttons\"\n :key=\"i\"\n type=\"button\"\n tabindex=\"-1\"\n :title=\"display?.(option) ?? String(option)\"\n :class=\"{\n ['bg-neutral-200 dark:bg-neutral-700']: active === i\n }\"\n @mousedown.prevent\n @click=\"onClickOption(option)\">\n <slot v-bind=\"{ option, display }\">\n <div class=\"truncate\">\n {{ display?.(option) ?? option }}\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>"],"names":["render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","props","__props","model","_useModel","isInteractive","computed","emit","__emit","controlAttributes","formGroupClasses","listeners","useFormControl","watchEffect","input","ref","field","useTemplateRef","showOptions","active","buttons","optionsEl","keys","fuse","createFuse","Fuse","filtered","matches","item","watch","scrollIntoView","child","parent","parentRect","childTop","value","nextTick","select","option","onInput","e","onKeypressEnter","onKeydownUp","onKeydownDown","onBlur","onClickOption","clear","toggle","canClear","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","$event","_renderSlot","_normalizeProps","_guardReactiveProps","_createBlock","ActivityIndicator","Pulse","XMarkIcon","ChevronDownIcon","$slots","_normalizeClass","_Fragment","_renderList","i","_hoisted_3","_toDisplayString"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { FormControlProps, FormControlSlots } from '@vue-interface/form-control';
|
|
2
|
+
import { IFuseOptions } from 'fuse.js';
|
|
3
|
+
import { InputHTMLAttributes } from 'vue';
|
|
4
|
+
export type SearchableSelectFieldSizePrefix = 'form-control';
|
|
5
|
+
export type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<InputHTMLAttributes, SearchableSelectFieldSizePrefix, ModelValue, Value> & {
|
|
6
|
+
options?: ModelValue[];
|
|
7
|
+
fuseOptions?: IFuseOptions<ModelValue>;
|
|
8
|
+
display?: (option: ModelValue) => string;
|
|
9
|
+
allowCustom?: boolean;
|
|
10
|
+
clearable?: boolean;
|
|
11
|
+
};
|
|
12
|
+
declare const _default: <T, Value>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
13
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
14
|
+
readonly onInput?: ((event: Event) => any) | undefined;
|
|
15
|
+
readonly onSelect?: ((event: Event) => any) | undefined;
|
|
16
|
+
readonly onBlur?: ((event: FocusEvent) => any) | undefined;
|
|
17
|
+
readonly onFocus?: ((event: FocusEvent) => any) | undefined;
|
|
18
|
+
readonly onFocusin?: ((event: FocusEvent) => any) | undefined;
|
|
19
|
+
readonly onFocusout?: ((event: FocusEvent) => any) | undefined;
|
|
20
|
+
readonly onClick?: ((event: MouseEvent) => any) | undefined;
|
|
21
|
+
readonly onDoubleclick?: ((event: MouseEvent) => any) | undefined;
|
|
22
|
+
readonly onContextmenu?: ((event: MouseEvent) => any) | undefined;
|
|
23
|
+
readonly onMousedown?: ((event: MouseEvent) => any) | undefined;
|
|
24
|
+
readonly onMouseup?: ((event: MouseEvent) => any) | undefined;
|
|
25
|
+
readonly onMouseover?: ((event: MouseEvent) => any) | undefined;
|
|
26
|
+
readonly onMouseout?: ((event: MouseEvent) => any) | undefined;
|
|
27
|
+
readonly onMouseenter?: ((event: MouseEvent) => any) | undefined;
|
|
28
|
+
readonly onMouseleave?: ((event: MouseEvent) => any) | undefined;
|
|
29
|
+
readonly onMousemove?: ((event: MouseEvent) => any) | undefined;
|
|
30
|
+
readonly onKeydown?: ((event: KeyboardEvent) => any) | undefined;
|
|
31
|
+
readonly onKeyup?: ((event: KeyboardEvent) => any) | undefined;
|
|
32
|
+
readonly onKeypress?: ((event: KeyboardEvent) => any) | undefined;
|
|
33
|
+
readonly onSelectionchange?: ((event: Event) => any) | undefined;
|
|
34
|
+
readonly onInvalid?: ((event: Event) => any) | undefined;
|
|
35
|
+
readonly onSubmit?: ((event: Event) => any) | undefined;
|
|
36
|
+
readonly onReset?: ((event: Event) => any) | undefined;
|
|
37
|
+
readonly onScroll?: ((event: Event) => any) | undefined;
|
|
38
|
+
readonly onWheel?: ((event: WheelEvent) => any) | undefined;
|
|
39
|
+
readonly onCopy?: ((event: ClipboardEvent) => any) | undefined;
|
|
40
|
+
readonly onCut?: ((event: ClipboardEvent) => any) | undefined;
|
|
41
|
+
readonly onPaste?: ((event: ClipboardEvent) => any) | undefined;
|
|
42
|
+
readonly onTouchstart?: ((event: TouchEvent) => any) | undefined;
|
|
43
|
+
readonly onTouchend?: ((event: TouchEvent) => any) | undefined;
|
|
44
|
+
readonly onTouchmove?: ((event: TouchEvent) => any) | undefined;
|
|
45
|
+
readonly onTouchcancel?: ((event: TouchEvent) => any) | undefined;
|
|
46
|
+
readonly onChange?: ((event: Event) => any) | undefined;
|
|
47
|
+
readonly onBeforeinput?: ((event: Event) => any) | undefined;
|
|
48
|
+
readonly "onUpdate:modelValue"?: ((value: T) => any) | undefined;
|
|
49
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onCopy" | "onCut" | "onPaste" | "onFocus" | "onFocusin" | "onFocusout" | "onBlur" | "onChange" | "onBeforeinput" | "onInput" | "onReset" | "onSubmit" | "onInvalid" | "onKeydown" | "onKeypress" | "onKeyup" | "onMousedown" | "onMouseenter" | "onMouseleave" | "onMousemove" | "onMouseout" | "onMouseover" | "onMouseup" | "onSelect" | "onScroll" | "onTouchcancel" | "onTouchend" | "onTouchmove" | "onTouchstart" | "onClick" | "onContextmenu" | "onWheel" | "onDoubleclick" | "onSelectionchange" | "onUpdate:modelValue"> & ({
|
|
50
|
+
modelValue?: T;
|
|
51
|
+
} & {
|
|
52
|
+
activity?: boolean;
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
error?: import('@vue-interface/form-control').FormControlErrorProp;
|
|
55
|
+
errors?: import('@vue-interface/form-control').FormControlErrorPropArray | import('@vue-interface/form-control').FormControlErrorPropRecord;
|
|
56
|
+
feedback?: import('@vue-interface/form-control').FormControlFeedbackProp;
|
|
57
|
+
formControlClass?: string;
|
|
58
|
+
helpText?: string;
|
|
59
|
+
id?: string;
|
|
60
|
+
indicator?: import('vue').Component;
|
|
61
|
+
indicatorSize?: import('@vue-interface/sizeable').ComponentSize<import('@vue-interface/activity-indicator').ActivityIndicatorSizePrefix>;
|
|
62
|
+
invalid?: boolean;
|
|
63
|
+
label?: string;
|
|
64
|
+
labelClass?: string;
|
|
65
|
+
modelValue?: T | undefined;
|
|
66
|
+
name?: string;
|
|
67
|
+
plaintext?: boolean;
|
|
68
|
+
size?: import('@vue-interface/form-control').FormControlSize<"form-control"> | undefined;
|
|
69
|
+
color?: string;
|
|
70
|
+
readonly?: boolean;
|
|
71
|
+
valid?: boolean;
|
|
72
|
+
value?: Value | undefined;
|
|
73
|
+
} & Omit<InputHTMLAttributes, "size"> & {
|
|
74
|
+
options?: T[] | undefined;
|
|
75
|
+
fuseOptions?: IFuseOptions<T> | undefined;
|
|
76
|
+
display?: ((option: T) => string) | undefined;
|
|
77
|
+
allowCustom?: boolean;
|
|
78
|
+
clearable?: boolean;
|
|
79
|
+
}) & Partial<{}>> & import('vue').PublicProps;
|
|
80
|
+
expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
|
|
81
|
+
attrs: any;
|
|
82
|
+
slots: Readonly<FormControlSlots<"form-control", T> & {
|
|
83
|
+
default(props: {
|
|
84
|
+
option: T;
|
|
85
|
+
display?: (option: T) => string;
|
|
86
|
+
}): any;
|
|
87
|
+
}> & FormControlSlots<"form-control", T> & {
|
|
88
|
+
default(props: {
|
|
89
|
+
option: T;
|
|
90
|
+
display?: (option: T) => string;
|
|
91
|
+
}): any;
|
|
92
|
+
};
|
|
93
|
+
emit: (((evt: "input", event: Event) => void) & ((evt: "select", event: Event) => void) & ((evt: "blur", event: FocusEvent) => void) & ((evt: "focus", event: FocusEvent) => void) & ((evt: "focusin", event: FocusEvent) => void) & ((evt: "focusout", event: FocusEvent) => void) & ((evt: "click", event: MouseEvent) => void) & ((evt: "doubleclick", event: MouseEvent) => void) & ((evt: "contextmenu", event: MouseEvent) => void) & ((evt: "mousedown", event: MouseEvent) => void) & ((evt: "mouseup", event: MouseEvent) => void) & ((evt: "mouseover", event: MouseEvent) => void) & ((evt: "mouseout", event: MouseEvent) => void) & ((evt: "mouseenter", event: MouseEvent) => void) & ((evt: "mouseleave", event: MouseEvent) => void) & ((evt: "mousemove", event: MouseEvent) => void) & ((evt: "keydown", event: KeyboardEvent) => void) & ((evt: "keyup", event: KeyboardEvent) => void) & ((evt: "keypress", event: KeyboardEvent) => void) & ((evt: "selectionchange", event: Event) => void) & ((evt: "invalid", event: Event) => void) & ((evt: "submit", event: Event) => void) & ((evt: "reset", event: Event) => void) & ((evt: "scroll", event: Event) => void) & ((evt: "wheel", event: WheelEvent) => void) & ((evt: "copy", event: ClipboardEvent) => void) & ((evt: "cut", event: ClipboardEvent) => void) & ((evt: "paste", event: ClipboardEvent) => void) & ((evt: "touchstart", event: TouchEvent) => void) & ((evt: "touchend", event: TouchEvent) => void) & ((evt: "touchmove", event: TouchEvent) => void) & ((evt: "touchcancel", event: TouchEvent) => void) & ((evt: "change", event: Event) => void) & ((evt: "beforeinput", event: Event) => void)) & ((evt: "update:modelValue", value: T) => void);
|
|
94
|
+
}>) => import('vue').VNode & {
|
|
95
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
96
|
+
};
|
|
97
|
+
export default _default;
|
|
98
|
+
type __VLS_PrettifyLocal<T> = {
|
|
99
|
+
[K in keyof T]: T[K];
|
|
100
|
+
} & {};
|
package/package.json
CHANGED
|
@@ -37,6 +37,7 @@ watchEffect(() => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
const input = ref<string>();
|
|
40
|
+
const field = useTemplateRef<any>('field');
|
|
40
41
|
const showOptions = ref(false);
|
|
41
42
|
const active = ref<number>();
|
|
42
43
|
const buttons = useTemplateRef<HTMLButtonElement[]>('buttons');
|
|
@@ -205,6 +206,18 @@ function clear() {
|
|
|
205
206
|
model.value = undefined;
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
function toggle() {
|
|
210
|
+
if (!isInteractive.value) return;
|
|
211
|
+
|
|
212
|
+
if(showOptions.value ) {
|
|
213
|
+
showOptions.value = false;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
showOptions.value = true;
|
|
217
|
+
field.value?.$el?.querySelector('input')?.focus();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
208
221
|
const canClear = computed(() => {
|
|
209
222
|
return props.clearable && (!!input.value || !!model.value) && isInteractive.value;
|
|
210
223
|
});
|
|
@@ -272,9 +285,12 @@ export type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<
|
|
|
272
285
|
@click.stop="clear">
|
|
273
286
|
<XMarkIcon class="size-[1.25em]" />
|
|
274
287
|
</button>
|
|
275
|
-
<
|
|
288
|
+
<button
|
|
276
289
|
v-else-if="!invalid && !valid"
|
|
277
|
-
|
|
290
|
+
type="button"
|
|
291
|
+
@click.stop="toggle">
|
|
292
|
+
<ChevronDownIcon class="size-[1em]" />
|
|
293
|
+
</button>
|
|
278
294
|
</slot>
|
|
279
295
|
</template>
|
|
280
296
|
</InputField>
|