UNPKG

@dialpad/dialtone-vue

Version:

Vue component library for Dialpad's design system Dialtone

515 lines (514 loc) 16.9 kB
import p from "../combobox-with-popover/combobox-with-popover.js"; import l from "../input/input.js"; import h from "../chip/chip.js"; import u from "../validation-messages/validation-messages.js"; import { validationMessageValidator as c } from "../../common/validators/index.js"; import { POPOVER_APPEND_TO_VALUES as d } from "../popover/popover-constants.js"; import { CHIP_TOP_POSITION as f, CHIP_SIZES as g, MULTI_SELECT_SIZES as m } from "./combobox-multi-select-constants.js"; import { getUniqueString as b } from "../../common/utils/index.js"; import { n as y } from "../../_plugin-vue2_normalizer-DSLOjnn3.js"; const C = { name: "DtRecipeComboboxMultiSelect", components: { DtRecipeComboboxWithPopover: p, DtInput: l, DtChip: h, DtValidationMessages: u }, props: { /** * String to use for the input label. */ label: { type: String, required: !0 }, /** * Determines visibility of input label. * @values true, false */ labelVisible: { type: Boolean, default: !0 }, /** * Description for the input */ description: { type: String, default: "" }, /** * Input placeholder */ placeholder: { type: String, default: "Select one or start typing" }, /** * Input validation messages */ inputMessages: { type: Array, default: () => [], validator: (t) => c(t) }, /** * Show input validation message */ showInputMessages: { type: Boolean, default: !0 }, // @TODO: https://dialpad.atlassian.net/browse/DP-52324 // type: { // type: String, // values: ['input', 'select'], // default: 'select', // }, /** * Determines if the list is loading */ loading: { type: Boolean, default: !1 }, /** * The message when the list is loading */ loadingMessage: { type: String, default: "loading..." }, /** * Determines when to show the list element and also controls the aria-expanded attribute. * Leaving this null will have the combobox trigger on input focus by default. * If you set this value, the default trigger behavior will be disabled and you can * control it as you need. */ showList: { type: Boolean, default: null }, /** * Determines maximum height for the popover before overflow. * Possible units rem|px|em */ listMaxHeight: { type: String, default: "300px" }, /** * The selected items */ selectedItems: { type: Array, default: function() { return []; } }, /** * Would be the maximum number of selections you can make. 0 is unlimited */ maxSelected: { type: Number, default: 0 }, /** * Max select message when the max selections is exceeded with the structure: * `[{"message": string, "type": VALIDATION_MESSAGE_TYPES }]` */ maxSelectedMessage: { type: Array, default: function() { return []; } }, /** * Displays the list when the combobox is focused, before the user has typed anything. * When this is enabled the list will not close after selection. */ hasSuggestionList: { type: Boolean, default: !0 }, /** * Size of the chip, one of `xs`, `sm`, `md` */ size: { type: String, default: "md", validator: (t) => Object.values(m).includes(t) }, /** * Sets the element to which the popover is going to append to. * 'body' will append to the nearest body (supports shadow DOM). * @values 'body', 'parent', HTMLElement, */ appendTo: { type: [HTMLElement, String], default: "body", validator: (t) => d.includes(t) || t instanceof HTMLElement }, /** * Named transition when the content display is toggled. * @see DtLazyShow */ transition: { type: String, default: "fade" }, /** * Determines whether the combobox should collapse to a single when losing focus. * @type {boolean} */ collapseOnFocusOut: { type: Boolean, default: !1 }, /** * Determines maximum width for the popover before overflow. * Possible units rem|px|em */ listMaxWidth: { type: String, default: "" }, /** * Amount of reserved space (in px) on the right side of the input * before the chips and the input caret jump to the next line. * default is 64 */ reservedRightSpace: { type: Number, default: 64 }, /** * Determines the maximum width of a single chip. If the text within this chip exceeds the value * it will be truncated with ellipses. * Possible units rem|px|em */ chipMaxWidth: { type: String, default: "" }, /** * Additional class name for the input element. * Can accept String, Object, and Array, i.e. has the * same API as Vue's built-in handling of the class attribute. */ inputClass: { type: [String, Object, Array], default: "" }, /** * Additional class name for the input wrapper element. * Can accept all of String, Object, and Array, i.e. has the * same api as Vue's built-in handling of the class attribute. */ inputWrapperClass: { type: [String, Object, Array], default: "" }, /** * When true, disables the underlying input. */ disabled: { type: Boolean, default: !1 } }, emits: [ /** * Native input event * * @event input * @type {String } */ "input", /** * Event fired when item selected * * @event select * @type {Number} */ "select", /** * Event fired when item removed * * @event remove * @type {String} */ "remove", /** * Event fired when max selected items limit is reached * * @event max-selected * @type {Object} */ "max-selected", /** * Native keyup event * * @event keyup * @type {KeyboardEvent} */ "keyup", /** * Native keydown event * * @event keydown * @type {KeyboardEvent} */ "keydown", /** * Event fired when combobox item is highlighted * * @event combobox-highlight * @type {Object} */ "combobox-highlight" ], data() { return { value: "", popoverOffset: [0, 4], showValidationMessages: !1, resizeWindowObserver: null, initialInputHeight: null, CHIP_SIZES: g, inputFocused: !1, hideInputText: !1 }; }, computed: { inputPlaceHolder() { var t; return ((t = this.selectedItems) == null ? void 0 : t.length) > 0 ? "" : this.placeholder; }, chipListeners() { return { ...this.$listeners, keydown: (t) => { this.onChipKeyDown(t), this.$emit("keydown", t); } }; }, inputListeners() { return { ...this.$listeners, input: (t) => { this.$emit("input", t), this.hasSuggestionList && this.showComboboxList(); }, keydown: (t) => { this.onInputKeyDown(t); }, keyup: (t) => { this.$emit("keyup", t); }, click: () => { this.hasSuggestionList && this.showComboboxList(); } }; }, selectedItemsWithKeys() { return this.selectedItems.map((t) => ({ item: t, key: b(t) })); }, chipWrapperClass() { return { [`d-recipe-combobox-multi-select__chip-wrapper-${this.size}--collapsed`]: !this.inputFocused && this.collapseOnFocusOut }; } }, watch: { selectedItems: { async handler() { this.initSelectedItems(); } }, chipMaxWidth: { async handler() { this.initSelectedItems(); } }, async label() { await this.$nextTick(), this.setChipsTopPosition(); }, async description() { await this.$nextTick(), this.setChipsTopPosition(); }, size: { async handler() { await this.$nextTick(); const t = this.getInput(); this.revertInputPadding(t), this.initialInputHeight = t.getBoundingClientRect().height, this.setInputPadding(), this.setChipsTopPosition(); } } }, mounted() { this.setInitialInputHeight(), this.resizeWindowObserver = new ResizeObserver(async () => { this.setChipsTopPosition(), this.setInputPadding(); }), this.resizeWindowObserver.observe(document.body), this.initSelectedItems(); }, beforeDestroy() { var t; (t = this.resizeWindowObserver) == null || t.unobserve(document.body); }, methods: { comboboxHighlight(t) { this.$emit("combobox-highlight", t); }, async initSelectedItems() { await this.$nextTick(), this.setInputPadding(), this.setChipsTopPosition(), this.setInputMinWidth(), this.checkMaxSelected(); }, onChipRemove(t) { var e; this.$emit("remove", t), (e = this.$refs.input) == null || e.focus(); }, onComboboxSelect(t) { this.loading || (this.value = "", this.$emit("select", t)); }, showComboboxList() { var t; this.showList == null && ((t = this.$refs.comboboxWithPopover) == null || t.showComboboxList()); }, closeComboboxList() { var t; this.showList == null && ((t = this.$refs.comboboxWithPopover) == null || t.closeComboboxList()); }, getChipButtons() { return this.$refs.chips && this.$refs.chips.map((t) => t.$el.querySelector("button")); }, getChips() { return this.$refs.chips && this.$refs.chips.map((t) => t.$el); }, getLastChipButton() { return this.$refs.chips && this.getChipButtons()[this.getChipButtons().length - 1]; }, getLastChip() { return this.$refs.chips && this.getChips()[this.getChips().length - 1]; }, getFirstChip() { return this.$refs.chips && this.getChips()[0]; }, getInput() { var t; return (t = this.$refs.input) == null ? void 0 : t.$refs.input; }, onChipKeyDown(t) { var i; const e = (i = t.code) == null ? void 0 : i.toLowerCase(); e === "arrowleft" ? this.navigateBetweenChips(t.target, !0) : e === "arrowright" && (t.target.id === this.getLastChipButton().id ? this.moveFromChipToInput() : this.navigateBetweenChips(t.target, !1)); }, onInputKeyDown(t) { var i; const e = (i = t.code) == null ? void 0 : i.toLowerCase(); if (this.selectedItems.length > 0 && t.target.selectionStart === 0) { if (t.target.selectionEnd !== t.target.selectionStart) return; (e === "backspace" || e === "arrowleft") && this.moveFromInputToChip(); } }, moveFromInputToChip() { var t; this.getLastChipButton().focus(), (t = this.$refs.input) == null || t.blur(), this.closeComboboxList(); }, moveFromChipToInput() { var t; this.getLastChipButton().blur(), (t = this.$refs.input) == null || t.focus(), this.showComboboxList(); }, navigateBetweenChips(t, e) { var s; const i = this.getChipButtons().indexOf(t), o = e ? i - 1 : i + 1; o < 0 || o >= ((s = this.$refs.chips) == null ? void 0 : s.length) || (this.getChipButtons()[i].blur(), this.getChipButtons()[o].focus(), this.closeComboboxList()); }, setChipsTopPosition() { const t = this.getInput(); if (!t) return; const e = this.$refs.inputSlotWrapper, i = t.getBoundingClientRect().top - e.getBoundingClientRect().top, o = this.$refs.chipsWrapper; o.style.top = i - f[this.size] + "px"; }, setInputPadding() { const t = this.getLastChip(), e = this.getInput(), i = this.$refs.chipsWrapper; if (!e || (this.revertInputPadding(e), this.popoverOffset = [0, 4], !t) || this.collapseOnFocusOut && !this.inputFocused) return; const o = t.offsetLeft + this.getFullWidth(t), s = e.getBoundingClientRect().width - o; s > this.reservedRightSpace ? e.style.paddingLeft = o + "px" : e.style.paddingLeft = "4px"; const r = i.getBoundingClientRect().height - 4, n = t.getBoundingClientRect().height - 4, a = s > this.reservedRightSpace ? t.offsetTop + 2 : r + n - 9; e.style.paddingTop = `${a}px`; }, revertInputPadding(t) { t.style.paddingLeft = "", t.style.paddingTop = "", t.style.paddingBottom = ""; }, getFullWidth(t) { const e = window.getComputedStyle(t); return t.offsetWidth + parseInt(e.marginLeft) + parseInt(e.marginRight); }, setInputMinWidth() { const t = this.getFirstChip(), e = this.getInput(); e && (t ? e.style.minWidth = this.getFullWidth(t) + 4 + "px" : e.style.minWidth = ""); }, checkMaxSelected() { this.maxSelected !== 0 && (this.selectedItems.length > this.maxSelected ? (this.showValidationMessages = !0, this.$emit("max-selected")) : this.showValidationMessages = !1); }, setInitialInputHeight() { const t = this.getInput(); t && (this.initialInputHeight = t.getBoundingClientRect().height); }, async handleInputFocusIn() { this.inputFocused = !0, this.collapseOnFocusOut && (this.hideInputText = !1, await this.$nextTick(), this.setInputPadding()); }, async handleInputFocusOut() { if (this.inputFocused = !1, this.collapseOnFocusOut) { this.hideInputText = !0; const t = this.getInput(); if (!t || !t.style.paddingTop) return; this.revertInputPadding(t); } } } }; var x = function() { var e = this, i = e._self._c; return i("dt-recipe-combobox-with-popover", { ref: "comboboxWithPopover", attrs: { label: e.label, "show-list": e.showList, "max-height": e.listMaxHeight, "max-width": e.listMaxWidth, "popover-offset": e.popoverOffset, "has-suggestion-list": e.hasSuggestionList, "content-width": "anchor", "append-to": e.appendTo, transition: e.transition }, on: { select: e.onComboboxSelect, highlight: e.comboboxHighlight }, scopedSlots: e._u([{ key: "input", fn: function({ onInput: o }) { return [i("span", { ref: "inputSlotWrapper", staticClass: "d-recipe-combobox-multi-select__input-wrapper", on: { focusin: e.handleInputFocusIn, focusout: e.handleInputFocusOut } }, [i("span", { ref: "chipsWrapper", class: ["d-recipe-combobox-multi-select__chip-wrapper", e.chipWrapperClass] }, e._l(e.selectedItemsWithKeys, function({ item: s, key: r }) { return i("dt-chip", e._g({ key: r, ref: "chips", refInFor: !0, class: [ "d-recipe-combobox-multi-select__chip", { "d-recipe-combobox-multi-select__chip--truncate": !!e.chipMaxWidth } ], style: { maxWidth: e.chipMaxWidth }, attrs: { "label-class": ["d-chip__label"], size: e.CHIP_SIZES[e.size], disabled: e.disabled }, on: { keydown: function(n) { return !n.type.indexOf("key") && e._k(n.keyCode, "backspace", void 0, n.key, void 0) ? null : e.onChipRemove(s); }, close: function(n) { return e.onChipRemove(s); } } }, e.chipListeners), [e._v(" " + e._s(s) + " ")]); }), 1), i("dt-input", e._g({ ref: "input", staticClass: "d-recipe-combobox-multi-select__input", attrs: { "input-class": [ e.inputClass, { "d-recipe-combobox-multi-select__input--hidden": e.hideInputText } ], "input-wrapper-class": e.inputWrapperClass, disabled: e.disabled, "aria-label": e.label, label: e.labelVisible ? e.label : "", description: e.description, placeholder: e.inputPlaceHolder, "show-messages": e.showInputMessages, messages: e.inputMessages, size: e.size }, on: { input: o, select: function(s) { s.stopPropagation(); } }, model: { value: e.value, callback: function(s) { e.value = s; }, expression: "value" } }, e.inputListeners)), i("dt-validation-messages", { attrs: { "validation-messages": e.maxSelectedMessage, "show-messages": e.showValidationMessages } })], 1)]; } }, e.$slots.header ? { key: "header", fn: function() { return [i("div", { ref: "header" }, [e._t("header")], 2)]; }, proxy: !0 } : null, { key: "list", fn: function() { return [i("div", { ref: "list", staticClass: "d-recipe-combobox-multi-select__list", on: { mousedown: function(o) { o.preventDefault(); } } }, [e.loading ? i("div", { staticClass: "d-recipe-combobox-multi-select__list--loading" }, [e._v(" " + e._s(e.loadingMessage) + " ")]) : e._t("list")], 2)]; }, proxy: !0 }, e.$slots.footer ? { key: "footer", fn: function() { return [i("div", { ref: "footer" }, [e._t("footer")], 2)]; }, proxy: !0 } : null], null, !0) }); }, I = [], w = /* @__PURE__ */ y( C, x, I ); const T = w.exports; export { T as default }; //# sourceMappingURL=combobox-multi-select.js.map