UNPKG

@dialpad/dialtone

Version:

Dialpad's Dialtone design system monorepo

359 lines (358 loc) 11.7 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); const keyboard_list_navigation = require("../../common/mixins/keyboard_list_navigation.cjs"); const common_utils = require("../../common/utils.cjs"); const combobox_loadingList = require("./combobox_loading-list.vue.cjs"); const combobox_emptyList = require("./combobox_empty-list.vue.cjs"); const combobox_constants = require("./combobox_constants.cjs"); const vue = require("vue"); const _pluginVue_exportHelper = require("../../_virtual/_plugin-vue_export-helper.cjs"); const _sfc_main = { compatConfig: { MODE: 3 }, name: "DtCombobox", components: { ComboboxLoadingList: combobox_loadingList.default, ComboboxEmptyList: combobox_emptyList.default }, mixins: [ keyboard_list_navigation.default({ indexKey: "highlightIndex", idKey: "highlightId", listElementKey: "getListElement", afterHighlightMethod: "afterHighlight", beginningOfListMethod: "beginningOfListMethod", endOfListMethod: "endOfListMethod", activeItemKey: "activeItemEl" }) ], props: { /** * String to use for the input label. */ label: { type: String, required: true }, /** * Determines visibility of input label. * @values true, false */ labelVisible: { type: Boolean, default: true }, /** * Size of the input, one of `xs`, `sm`, `md`, `lg`, `xl` * @values null, xs, sm, md, lg, xl */ size: { type: String, default: null, validator: (t) => Object.values(combobox_constants.LABEL_SIZES).includes(t) }, /** * Description for the input */ description: { type: String, default: "" }, /** * Sets an ID on the list element of the component. Used by several aria attributes * as well as when deriving the IDs for each item. */ listId: { type: String, default() { return common_utils.getUniqueString(); } }, /** * A method that will be called when the selection goes past the beginning of the list. */ onBeginningOfList: { type: Function, default: null }, /** * A method that will be called when the selection goes past the end of the list. */ onEndOfList: { type: Function, default: null }, /** * Determines when to show the list element and also controls the aria-expanded attribute. * @values true, false */ showList: { type: Boolean, default: false }, /** * If the list is rendered outside the component, like when using popover as the list wrapper. * @values true, false */ listRenderedOutside: { type: Boolean, default: false }, /** * Determines when to show the skeletons and also controls aria-busy attribute. * @values true, false */ loading: { type: Boolean, default: false }, /** * Sets the list to an empty state, and displays the message from prop `emptyStateMessage`. * @values true, false */ emptyList: { type: Boolean, default: false }, /** * Message to show when the list is empty */ emptyStateMessage: { type: String, default: "" }, /** * Additional class name for the empty list 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. */ emptyStateClass: { type: [String, Object, Array], default: "" }, /** * Programmatically click on the active list item element when a selection * comes from keyboard navigation, i.e. pressing the "Enter" key. * @values true, false */ clickOnSelect: { type: Boolean, default: false } }, emits: [ /** * Event fired when item selected * * @event select * @type {Number} */ "select", /** * Event fired when pressing escape * * @event escape */ "escape", /** * Event fired when the highlight changes * * @event highlight * @type {Number} */ "highlight", /** * Event fired when list is shown or hidden * * @event opened * @type {Boolean} */ "opened" ], data() { return { // If the list is rendered at the root, rather than as a child // of this component, this is the ref to that dom element. Set // by the onOpen method. outsideRenderedListRef: null, hasSlotContent: common_utils.hasSlotContent }; }, computed: { inputProps() { return { label: this.label, labelVisible: this.labelVisible, size: this.size, description: this.description, role: "combobox", "aria-label": this.label, "aria-expanded": this.showList.toString(), "aria-owns": this.listId, "aria-haspopup": "listbox", "aria-activedescendant": this.activeItemId, "aria-controls": this.listId }; }, listProps() { return { role: "listbox", id: this.listId, // The list has to be positioned relatively so that the auto-scroll can // calculate the correct offset for the list items. class: "d-ps-relative", "aria-label": this.label }; }, beginningOfListMethod() { return this.onBeginningOfList || this.jumpToEnd; }, endOfListMethod() { return this.onEndOfList || this.jumpToBeginning; }, activeItemId() { if (!this.showList || this.highlightIndex < 0 || this.loading) { return; } return this.highlightId; }, activeItemEl() { if (!this.highlightId) return ""; return this.getListElement().querySelector("#" + this.highlightId); } }, watch: { showList(showList) { if (!this.listRenderedOutside) { this.setInitialHighlightIndex(); this.$emit("opened", showList); } if (!showList && this.outsideRenderedListRef) { this.outsideRenderedListRef.removeEventListener("mousemove", this.onMouseHighlight); this.outsideRenderedListRef = null; } }, loading(loading) { this.$nextTick(() => { this.setInitialHighlightIndex(); }); }, $props: { deep: true, immediate: true, handler() { this.validateEmptyListProps(); } } }, created() { this.validateEmptyListProps(); }, methods: { onMouseHighlight(e) { if (this.loading) return; const liElement = e.target.closest("li"); if (liElement && this.highlightId !== liElement.id) { this.setHighlightId(liElement.id); } }, getListElement() { var _a; return this.outsideRenderedListRef ?? ((_a = this.$refs.listWrapper) == null ? void 0 : _a.querySelector(`#${this.listId}`)); }, clearHighlightIndex() { if (this.showList) { this.setHighlightIndex(-1); } }, afterHighlight() { if (this.loading) return; this.$emit("highlight", this.highlightIndex); }, onEnterKey() { var _a; if (this.loading || this.emptyList) return; if (this.highlightIndex >= 0) { this.$emit("select", this.highlightIndex); if (this.clickOnSelect) { (_a = this.activeItemEl) == null ? void 0 : _a.click(); } } }, onEscapeKey() { this.$emit("escape"); }, onOpen(open, contentRef) { var _a; this.outsideRenderedListRef = contentRef; (_a = this.outsideRenderedListRef) == null ? void 0 : _a.addEventListener("mousemove", this.onMouseHighlight); this.$emit("opened", open); if (open) { this.setInitialHighlightIndex(); } }, onKeyValidation(e, eventHandler) { if (!this.showList || !this.getListElement()) return; this[eventHandler](e); }, setInitialHighlightIndex() { if (!this.showList) return; this.$nextTick(() => { this.setHighlightIndex(this.loading ? -1 : 0); }); }, validateEmptyListProps() { if (this.$slots.emptyListItem) { return; } if (this.emptyList && !this.emptyStateMessage) { console.error(`Invalid props: you must pass both props emptyList and emptyStateMessage to show the empty message.`); } } } }; const _hoisted_1 = { "data-qa": "dt-combobox-input-wrapper" }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_combobox_loading_list = vue.resolveComponent("combobox-loading-list"); const _component_combobox_empty_list = vue.resolveComponent("combobox-empty-list"); return vue.openBlock(), vue.createElementBlock("div", { onKeydown: [ _cache[3] || (_cache[3] = vue.withKeys(vue.withModifiers(($event) => $options.onKeyValidation($event, "onEscapeKey"), ["stop"]), ["esc"])), _cache[4] || (_cache[4] = vue.withKeys(vue.withModifiers(($event) => $options.onKeyValidation($event, "onEnterKey"), ["exact"]), ["enter"])), _cache[5] || (_cache[5] = vue.withKeys(vue.withModifiers(($event) => $options.onKeyValidation($event, "onUpKey"), ["stop", "prevent"]), ["up"])), _cache[6] || (_cache[6] = vue.withKeys(vue.withModifiers(($event) => $options.onKeyValidation($event, "onDownKey"), ["stop", "prevent"]), ["down"])), _cache[7] || (_cache[7] = vue.withKeys(vue.withModifiers(($event) => $options.onKeyValidation($event, "onHomeKey"), ["stop", "prevent"]), ["home"])), _cache[8] || (_cache[8] = vue.withKeys(vue.withModifiers(($event) => $options.onKeyValidation($event, "onEndKey"), ["stop", "prevent"]), ["end"])) ] }, [ vue.createElementVNode("div", _hoisted_1, [ vue.renderSlot(_ctx.$slots, "input", { inputProps: $options.inputProps }) ]), $props.showList ? (vue.openBlock(), vue.createElementBlock("div", { key: 0, ref: "listWrapper", "data-qa": "dt-combobox-list-wrapper", onMouseleave: _cache[0] || (_cache[0] = (...args) => $options.clearHighlightIndex && $options.clearHighlightIndex(...args)), onFocusout: _cache[1] || (_cache[1] = (...args) => $options.clearHighlightIndex && $options.clearHighlightIndex(...args)), onMousemoveCapture: _cache[2] || (_cache[2] = (...args) => $options.onMouseHighlight && $options.onMouseHighlight(...args)) }, [ $props.loading && !$props.listRenderedOutside ? (vue.openBlock(), vue.createBlock(_component_combobox_loading_list, vue.normalizeProps(vue.mergeProps({ key: 0 }, $options.listProps)), null, 16)) : $props.emptyList && ($props.emptyStateMessage || $data.hasSlotContent(_ctx.$slots.emptyListItem)) && !$props.listRenderedOutside ? (vue.openBlock(), vue.createBlock(_component_combobox_empty_list, vue.mergeProps({ key: 1 }, $options.listProps, { message: $props.emptyStateMessage, "item-class": $props.emptyStateClass }), { default: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "emptyListItem") ]), _: 3 }, 16, ["message", "item-class"])) : vue.renderSlot(_ctx.$slots, "list", { key: 2, listProps: $options.listProps, opened: $options.onOpen, clearHighlightIndex: $options.clearHighlightIndex }) ], 544)) : vue.createCommentVNode("", true) ], 32); } const DtCombobox = /* @__PURE__ */ _pluginVue_exportHelper.default(_sfc_main, [["render", _sfc_render]]); exports.default = DtCombobox; //# sourceMappingURL=combobox.vue.cjs.map