UNPKG

@dialpad/dialtone

Version:

Dialpad's Dialtone design system monorepo

444 lines (443 loc) 13.8 kB
import ComboboxLoadingList from "../../../components/combobox/combobox_loading-list.vue.js"; import ComboboxEmptyList from "../../../components/combobox/combobox_empty-list.vue.js"; import { getUniqueString, hasSlotContent } from "../../../common/utils.js"; import SrOnlyCloseButtonMixin from "../../../common/mixins/sr_only_close_button.js"; import { resolveComponent, openBlock, createBlock, mergeProps, withCtx, createElementVNode, withKeys, renderSlot, createVNode, createSlots, normalizeClass, normalizeProps } from "vue"; import _export_sfc from "../../../_virtual/_plugin-vue_export-helper.js"; import DtCombobox from "../../../components/combobox/combobox.vue.js"; import DtPopover from "../../../components/popover/popover.vue.js"; import { LABEL_SIZES } from "../../../components/combobox/combobox_constants.js"; import { DROPDOWN_PADDING_CLASSES } from "../../../components/dropdown/dropdown_constants.js"; import { POPOVER_CONTENT_WIDTHS, POPOVER_APPEND_TO_VALUES } from "../../../components/popover/popover_constants.js"; const _sfc_main = { compatConfig: { MODE: 3 }, name: "DtRecipeComboboxWithPopover", components: { DtCombobox, DtPopover, ComboboxLoadingList, ComboboxEmptyList }, mixins: [SrOnlyCloseButtonMixin], 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(LABEL_SIZES).includes(t) }, /** * Description for the input */ description: { type: String, default: "" }, /** * 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 }, /** * 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 getUniqueString(); } }, /** * Additional class for the wrapper list element. */ listClass: { type: [String, Array, Object], default: "" }, /** * 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 maximum height for the popover before overflow. * Possible units rem|px|em */ maxHeight: { type: String, default: "" }, /** * Determines maximum width for the popover before overflow. * Possible units rem|px|%|em */ maxWidth: { type: String, default: "" }, /** * Vertical padding size around the list element. */ padding: { type: String, default: "small", validator: (padding) => { return Object.keys(DROPDOWN_PADDING_CLASSES).some((item) => item === padding); } }, /** * Width configuration for the popover content. When its value is 'anchor', * the popover content will have the same width as the anchor. */ contentWidth: { type: String, default: null, validator: (contentWidth) => POPOVER_CONTENT_WIDTHS.includes(contentWidth) }, /** * If the list should be shown by pressing up or down arrow key on the input element. * This can be set when not passing showList prop. */ openWithArrowKeys: { type: Boolean, default: false }, /** * Displaces the popover content box from its anchor element * by the specified number of pixels. */ popoverOffset: { type: Array, default: () => [0, 4] }, /** * If the popover sticks to the input. */ popoverSticky: { type: [Boolean, String], default: false }, /** * 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: true }, /** * Determines when to show the skeletons and also controls aria-busy attribute. */ loading: { type: Boolean, default: false }, /** * Sets the list to an empty state, and displays the message from prop `emptyStateMessage`. */ emptyList: { type: Boolean, default: false }, /** * Message to show when the list is empty */ emptyStateMessage: { type: String, default: "" }, /** * 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: (appendTo) => { return POPOVER_APPEND_TO_VALUES.includes(appendTo) || appendTo instanceof HTMLElement; } }, /** * Named transition when the content display is toggled. * @see DtLazyShow */ transition: { type: String, default: "fade" } }, emits: [ /** * Event fired when item selected * * @event select * @type {Number} */ "select", /** * Event fired when 'escape' key is pressed * * @event escape */ "escape", /** * Event fired when an item is highlighted * * @event highlight * @type {Number} */ "highlight", /** * Emitted when items are shown or hidden * * @event opened * @type {Boolean | Array} */ "opened" ], data() { return { DROPDOWN_PADDING_CLASSES, isListShown: false, isInputFocused: false, isListFocused: false, externalAnchor: getUniqueString(), hasSlotContent }; }, computed: { comboboxListeners() { return { ...this.$attrs, onSelect: this.onSelect, onEscape: this.onEscape, onHighlight: this.onHighlight }; } }, watch: { showList: { handler: function(show) { if (show !== null) { this.isListShown = show; } }, immediate: true }, isListShown(val) { if (val) { window.addEventListener("mousedown", this.onFocusOut); } else { window.removeEventListener("mousedown", this.onFocusOut); } this.onOpened(val); } }, methods: { handleDisplayList(value) { if (!this.hasSuggestionList && value) { this.showComboboxList(); } if (!this.hasSuggestionList && !value) { this.closeComboboxList(); } }, showComboboxList() { if (this.showList != null) { return; } this.isListShown = true; }, closeComboboxList() { if (this.showList != null) { return; } this.isListShown = false; }, onSelect(highlightIndex) { if (this.loading) return; this.$emit("select", highlightIndex); if (!this.hasSuggestionList) { this.closeComboboxList(); } }, onEscape() { this.$emit("escape"); this.closeComboboxList(); }, onHighlight(highlightIndex) { if (this.loading) return; this.$emit("highlight", highlightIndex); }, onOpened(opened) { this.$emit("opened", opened); }, onFocusIn(e) { var _a; if (this.hasSuggestionList && e && ((_a = this.$refs.input) == null ? void 0 : _a.querySelector("input")) === e.target) { this.showComboboxList(); } }, onFocusOut(e) { var _a, _b, _c; const popoverEl = (_b = (_a = this.$refs.popover) == null ? void 0 : _a.tip) == null ? void 0 : _b.popper; const comboboxEl = this.$refs.input; if ((_c = e.composedPath()) == null ? void 0 : _c.some((el) => [popoverEl, comboboxEl].includes(el))) return; this.closeComboboxList(); }, openOnArrowKeyPress() { if (this.showList !== null || this.isListShown || !this.openWithArrowKeys) { return; } this.showComboboxList(); } } }; const _hoisted_1 = ["id"]; const _hoisted_2 = { ref: "header" }; const _hoisted_3 = ["onMouseleave", "onFocusout"]; const _hoisted_4 = { ref: "footer" }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_combobox_loading_list = resolveComponent("combobox-loading-list"); const _component_combobox_empty_list = resolveComponent("combobox-empty-list"); const _component_dt_popover = resolveComponent("dt-popover"); const _component_dt_combobox = resolveComponent("dt-combobox"); return openBlock(), createBlock(_component_dt_combobox, mergeProps({ ref: "combobox", loading: $props.loading, label: $props.label, "label-visible": $props.labelVisible, size: $props.size, description: $props.description, "empty-list": $props.emptyList, "empty-state-message": $props.emptyStateMessage, "show-list": $data.isListShown, "on-beginning-of-list": $props.onBeginningOfList, "on-end-of-list": $props.onEndOfList, "list-rendered-outside": true, "list-id": $props.listId, "data-qa": "dt-combobox" }, $options.comboboxListeners), { input: withCtx(({ inputProps }) => [ createElementVNode("div", { id: $data.externalAnchor, ref: "input", onFocusin: _cache[0] || (_cache[0] = (...args) => $options.onFocusIn && $options.onFocusIn(...args)), onKeydown: [ _cache[1] || (_cache[1] = withKeys(($event) => $options.openOnArrowKeyPress($event), ["up"])), _cache[2] || (_cache[2] = withKeys(($event) => $options.openOnArrowKeyPress($event), ["down"])) ] }, [ renderSlot(_ctx.$slots, "input", { inputProps, onInput: $options.handleDisplayList }) ], 40, _hoisted_1) ]), list: withCtx(({ opened, listProps, clearHighlightIndex }) => [ createVNode(_component_dt_popover, { ref: "popover", open: $data.isListShown, "onUpdate:open": _cache[3] || (_cache[3] = ($event) => $data.isListShown = $event), "hide-on-click": false, "max-height": $props.maxHeight, "max-width": $props.maxWidth, offset: $props.popoverOffset, sticky: $props.popoverSticky, placement: "bottom-start", "initial-focus-element": "none", padding: "none", role: "listbox", "external-anchor": $data.externalAnchor, "content-width": $props.contentWidth, "content-appear": true, "content-tabindex": null, modal: false, "auto-focus": false, "append-to": $props.appendTo, transition: $props.transition, "visually-hidden-close-label": _ctx.visuallyHiddenCloseLabel, "visually-hidden-close": _ctx.visuallyHiddenClose, onOpened: opened }, createSlots({ content: withCtx(() => [ createElementVNode("div", { ref: "listWrapper", class: normalizeClass([ "d-recipe-combobox-with-popover__list", $data.DROPDOWN_PADDING_CLASSES[$props.padding], $props.listClass ]), onMouseleave: clearHighlightIndex, onFocusout: clearHighlightIndex }, [ $props.loading ? (openBlock(), createBlock(_component_combobox_loading_list, normalizeProps(mergeProps({ key: 0 }, listProps)), null, 16)) : $props.emptyList && $props.emptyStateMessage ? (openBlock(), createBlock(_component_combobox_empty_list, mergeProps({ key: 1 }, listProps, { message: $props.emptyStateMessage }), null, 16, ["message"])) : renderSlot(_ctx.$slots, "list", { key: 2, listProps }) ], 42, _hoisted_3) ]), _: 2 }, [ $data.hasSlotContent(_ctx.$slots.header) ? { name: "headerContent", fn: withCtx(() => [ createElementVNode("div", _hoisted_2, [ renderSlot(_ctx.$slots, "header") ], 512) ]), key: "0" } : void 0, $data.hasSlotContent(_ctx.$slots.footer) ? { name: "footerContent", fn: withCtx(() => [ createElementVNode("div", _hoisted_4, [ renderSlot(_ctx.$slots, "footer") ], 512) ]), key: "1" } : void 0 ]), 1032, ["open", "max-height", "max-width", "offset", "sticky", "external-anchor", "content-width", "append-to", "transition", "visually-hidden-close-label", "visually-hidden-close", "onOpened"]) ]), _: 3 }, 16, ["loading", "label", "label-visible", "size", "description", "empty-list", "empty-state-message", "show-list", "on-beginning-of-list", "on-end-of-list", "list-id"]); } const DtRecipeComboboxWithPopover = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]); export { DtRecipeComboboxWithPopover as default }; //# sourceMappingURL=combobox_with_popover.vue.js.map