UNPKG

vue3-simple-typeahead-stdunn

Version:

Forked from original until PR is merged. A simple and lightweight Vue3 typeahead component that show a suggested list of elements while the user types in.

378 lines (343 loc) 13.1 kB
'use strict';var vue=require('vue');function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }var script = vue.defineComponent({ name: 'Vue3SimpleTypeahead', emits: ['onInput', 'onFocus', 'onBlur', 'selectItem'], inheritAttrs: false, props: { id: { type: String, default: '' }, placeholder: { type: String, default: '' }, items: { type: Array, required: true }, defaultItem: { default: null }, itemProjection: { type: Function, default: function _default(item) { return item; } }, minInputLength: { type: Number, default: 2, validator: function validator(val) { return val >= 0; } }, minItemLength: { type: Number, default: 0, validator: function validator(val) { return val >= 0; } }, selectOnTab: { type: Boolean, default: true }, tokenizedMatches: { type: Boolean, default: false } }, data: function data() { return { inputId: this.id || "simple_typeahead_".concat((Math.random() * 1000).toFixed()), input: '', isInputFocused: false, currentSelectionIndex: 0 }; }, mounted: function mounted() { if (this.defaultItem !== undefined && this.defaultItem !== null) { this.selectItem(this.defaultItem); } }, methods: { onInput: function onInput() { if (this.isListVisible && this.currentSelectionIndex >= this.filteredItems.length) { this.currentSelectionIndex = Math.max(this.filteredItems.length - 1, 0); } this.$emit('onInput', { input: this.input, items: this.filteredItems }); }, onFocus: function onFocus() { this.isInputFocused = true; this.$emit('onFocus', { input: this.input, items: this.filteredItems }); }, onBlur: function onBlur() { this.isInputFocused = false; this.$emit('onBlur', { input: this.input, items: this.filteredItems }); }, onArrowDown: function onArrowDown() { if (this.isListVisible && this.currentSelectionIndex < this.filteredItems.length - 1) { this.currentSelectionIndex++; } this.scrollSelectionIntoView(); }, onArrowUp: function onArrowUp() { if (this.isListVisible && this.currentSelectionIndex > 0) { this.currentSelectionIndex--; } this.scrollSelectionIntoView(); }, scrollSelectionIntoView: function scrollSelectionIntoView() { var _this = this; this.$nextTick(function () { var listNode = document.querySelector("#".concat(_this.wrapperId, " .simple-typeahead-list")); var activeNode = document.querySelector("#".concat(_this.wrapperId, " .simple-typeahead-list-item.simple-typeahead-list-item-active")); if (!listNode || !activeNode) return; if (!(activeNode.offsetTop >= listNode.scrollTop && activeNode.offsetTop + activeNode.offsetHeight < listNode.scrollTop + listNode.offsetHeight)) { var scrollTo = 0; if (activeNode.offsetTop > listNode.scrollTop) { scrollTo = activeNode.offsetTop + activeNode.offsetHeight - listNode.offsetHeight; } else if (activeNode.offsetTop < listNode.scrollTop) { scrollTo = activeNode.offsetTop; } listNode.scrollTo(0, scrollTo); } }); }, selectCurrentSelection: function selectCurrentSelection() { if (this.currentSelection) { this.selectItem(this.currentSelection); } }, selectCurrentSelectionTab: function selectCurrentSelectionTab() { if (this.selectOnTab) { this.selectCurrentSelection(); } else { this.$refs.inputRef.blur(); } }, selectItem: function selectItem(item) { this.input = this.itemProjection(item); this.currentSelectionIndex = 0; this.$refs.inputRef.blur(); this.$emit('selectItem', item); }, escapeRegExp: function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }, boldMatchText: function boldMatchText(text) { var _this2 = this; // Split the user input by whitespace and highlight each found token. var tokens = this.input.trim().split(/\s+/); var highlighted = text; tokens.forEach(function (token) { if (!token) return; var re = new RegExp("(".concat(_this2.escapeRegExp(token), ")"), 'ig'); highlighted = highlighted.replace(re, '<strong>$1</strong>'); }); return highlighted; }, clearInput: function clearInput() { this.input = ''; }, getInput: function getInput() { return this.$refs.inputRef; }, focusInput: function focusInput() { this.$refs.inputRef.focus(); this.onFocus(); }, blurInput: function blurInput() { this.$refs.inputRef.blur(); this.onBlur(); } }, computed: { wrapperId: function wrapperId() { return "".concat(this.inputId, "_wrapper"); }, filteredItems: function filteredItems() { var _this3 = this; var userInput = this.input.trim(); if (!userInput) { return this.items; } if (!this.tokenizedMatches) { var regexp = new RegExp(this.escapeRegExp(this.input), 'i'); return this.items.filter(function (item) { return _this3.itemProjection(item).match(regexp); }); } // Split user input into tokens and lowercase them var tokens = userInput.split(/\s+/).map(function (t) { return t.toLowerCase(); }); return this.items.filter(function (item) { // Lowercase the projected text var text = _this3.itemProjection(item).toLowerCase(); // Keep if at least one token matches return tokens.some(function (token) { return text.includes(token); }); }); }, isListVisible: function isListVisible() { return this.isInputFocused && this.input.length >= this.minInputLength && this.filteredItems.length > this.minItemLength; }, currentSelection: function currentSelection() { return this.isListVisible && this.currentSelectionIndex < this.filteredItems.length ? this.filteredItems[this.currentSelectionIndex] : undefined; } } });vue.pushScopeId("data-v-99a07096"); var _hoisted_1 = ["id"]; var _hoisted_2 = ["id", "placeholder"]; var _hoisted_3 = { key: 0, class: "simple-typeahead-list" }; var _hoisted_4 = { key: 0, class: "simple-typeahead-list-header" }; var _hoisted_5 = ["onClick", "onMouseenter"]; var _hoisted_6 = ["data-text"]; var _hoisted_7 = ["data-text", "innerHTML"]; var _hoisted_8 = { key: 1, class: "simple-typeahead-list-footer" }; vue.popScopeId(); function render(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("div", { id: _ctx.wrapperId, class: "simple-typeahead" }, [vue.withDirectives(vue.createElementVNode("input", vue.mergeProps({ ref: "inputRef", id: _ctx.inputId, class: "simple-typeahead-input", type: "text", placeholder: _ctx.placeholder, "onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { return _ctx.input = $event; }), onInput: _cache[1] || (_cache[1] = function () { return _ctx.onInput && _ctx.onInput.apply(_ctx, arguments); }), onFocus: _cache[2] || (_cache[2] = function () { return _ctx.onFocus && _ctx.onFocus.apply(_ctx, arguments); }), onBlur: _cache[3] || (_cache[3] = function () { return _ctx.onBlur && _ctx.onBlur.apply(_ctx, arguments); }), onKeydown: [_cache[4] || (_cache[4] = vue.withKeys(vue.withModifiers(function () { return _ctx.onArrowDown && _ctx.onArrowDown.apply(_ctx, arguments); }, ["prevent"]), ["down"])), _cache[5] || (_cache[5] = vue.withKeys(vue.withModifiers(function () { return _ctx.onArrowUp && _ctx.onArrowUp.apply(_ctx, arguments); }, ["prevent"]), ["up"])), _cache[6] || (_cache[6] = vue.withKeys(vue.withModifiers(function () { return _ctx.selectCurrentSelection && _ctx.selectCurrentSelection.apply(_ctx, arguments); }, ["prevent"]), ["enter"])), _cache[7] || (_cache[7] = vue.withKeys(vue.withModifiers(function () { return _ctx.selectCurrentSelectionTab && _ctx.selectCurrentSelectionTab.apply(_ctx, arguments); }, ["prevent"]), ["tab"]))], autocomplete: "off" }, _ctx.$attrs), null, 16, _hoisted_2), [[vue.vModelText, _ctx.input]]), _ctx.isListVisible ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [_ctx.$slots['list-header'] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4, [vue.renderSlot(_ctx.$slots, "list-header")])) : vue.createCommentVNode("", true), (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(_ctx.filteredItems, function (item, index) { return vue.openBlock(), vue.createElementBlock("div", { class: vue.normalizeClass(["simple-typeahead-list-item", { 'simple-typeahead-list-item-active': _ctx.currentSelectionIndex == index }]), key: index, onMousedown: _cache[8] || (_cache[8] = vue.withModifiers(function () {}, ["prevent"])), onClick: function onClick($event) { return _ctx.selectItem(item); }, onMouseenter: function onMouseenter($event) { return _ctx.currentSelectionIndex = index; } }, [_ctx.$slots['list-item-text'] ? (vue.openBlock(), vue.createElementBlock("span", { key: 0, class: "simple-typeahead-list-item-text", "data-text": _ctx.itemProjection(item) }, [vue.renderSlot(_ctx.$slots, "list-item-text", { item: item, itemProjection: _ctx.itemProjection, boldMatchText: _ctx.boldMatchText })], 8, _hoisted_6)) : (vue.openBlock(), vue.createElementBlock("span", { key: 1, class: "simple-typeahead-list-item-text", "data-text": _ctx.itemProjection(item), innerHTML: _ctx.boldMatchText(_ctx.itemProjection(item)) }, null, 8, _hoisted_7))], 42, _hoisted_5); }), 128)), _ctx.$slots['list-footer'] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [vue.renderSlot(_ctx.$slots, "list-footer")])) : vue.createCommentVNode("", true)])) : vue.createCommentVNode("", true)], 8, _hoisted_1); }script.render = render; script.__scopeId = "data-v-99a07096";// Import vue component // IIFE injects install function into component, allowing component // to be registered via Vue.use() as well as Vue.component(), var component = /*#__PURE__*/(function () { // Get component instance var installable = script; // Attach install function executed by Vue.use() installable.install = function (app) { app.component('Vue3SimpleTypeahead', installable); }; return installable; })(); // It's possible to expose named exports when writing components that can // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; // export const RollupDemoDirective = directive; var namedExports=/*#__PURE__*/Object.freeze({__proto__:null,'default':component});// only expose one global var, with named exports exposed as properties of // that global var (eg. plugin.namedExport) Object.entries(namedExports).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), exportName = _ref2[0], exported = _ref2[1]; if (exportName !== 'default') component[exportName] = exported; });module.exports=component;