@dialpad/dialtone-vue
Version:
Vue component library for Dialpad's design system Dialtone
366 lines (365 loc) • 12.1 kB
JavaScript
import ComboboxLoadingList from "../../../components/combobox/combobox_loading-list.vue.js";
import ComboboxEmptyList from "../../../components/combobox/combobox_empty-list.vue.js";
import { getUniqueString } from "../../../common/utils.js";
import SrOnlyCloseButtonMixin from "../../../common/mixins/sr_only_close_button.js";
import normalizeComponent from "../../../_virtual/_plugin-vue2_normalizer.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 = {
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()
};
},
computed: {
comboboxListeners() {
return {
...this.$listeners,
select: this.onSelect,
escape: this.onEscape,
highlight: 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();
}
}
};
var _sfc_render = function render() {
var _vm = this, _c = _vm._self._c;
return _c("dt-combobox", _vm._g({ ref: "combobox", attrs: { "loading": _vm.loading, "label": _vm.label, "label-visible": _vm.labelVisible, "size": _vm.size, "description": _vm.description, "empty-list": _vm.emptyList, "empty-state-message": _vm.emptyStateMessage, "show-list": _vm.isListShown, "on-beginning-of-list": _vm.onBeginningOfList, "on-end-of-list": _vm.onEndOfList, "list-rendered-outside": true, "list-id": _vm.listId, "data-qa": "dt-combobox" }, scopedSlots: _vm._u([{ key: "input", fn: function({ inputProps }) {
return [_c("div", { ref: "input", attrs: { "id": _vm.externalAnchor }, on: { "focusin": _vm.onFocusIn, "keydown": [function($event) {
if (!$event.type.indexOf("key") && _vm._k($event.keyCode, "up", 38, $event.key, ["Up", "ArrowUp"])) return null;
return _vm.openOnArrowKeyPress($event);
}, function($event) {
if (!$event.type.indexOf("key") && _vm._k($event.keyCode, "down", 40, $event.key, ["Down", "ArrowDown"])) return null;
return _vm.openOnArrowKeyPress($event);
}] } }, [_vm._t("input", null, { "inputProps": inputProps, "onInput": _vm.handleDisplayList })], 2)];
} }, { key: "list", fn: function({ opened, listProps, clearHighlightIndex }) {
return [_c("dt-popover", { ref: "popover", attrs: { "open": _vm.isListShown, "hide-on-click": false, "max-height": _vm.maxHeight, "max-width": _vm.maxWidth, "offset": _vm.popoverOffset, "sticky": _vm.popoverSticky, "placement": "bottom-start", "initial-focus-element": "none", "padding": "none", "role": "listbox", "external-anchor": _vm.externalAnchor, "content-width": _vm.contentWidth, "content-appear": true, "content-tabindex": null, "modal": false, "auto-focus": false, "append-to": _vm.appendTo, "transition": _vm.transition, "visually-hidden-close-label": _vm.visuallyHiddenCloseLabel, "visually-hidden-close": _vm.visuallyHiddenClose }, on: { "update:open": function($event) {
_vm.isListShown = $event;
}, "opened": function($event) {
return opened($event, arguments[1]);
} }, scopedSlots: _vm._u([_vm.$slots.header ? { key: "headerContent", fn: function() {
return [_c("div", { ref: "header" }, [_vm._t("header")], 2)];
}, proxy: true } : null, { key: "content", fn: function() {
return [_c("div", { ref: "listWrapper", class: [
"d-recipe-combobox-with-popover__list",
_vm.DROPDOWN_PADDING_CLASSES[_vm.padding],
_vm.listClass
], on: { "mouseleave": clearHighlightIndex, "focusout": clearHighlightIndex } }, [_vm.loading ? _c("combobox-loading-list", _vm._b({}, "combobox-loading-list", listProps, false)) : _vm.emptyList && _vm.emptyStateMessage ? _c("combobox-empty-list", _vm._b({ attrs: { "message": _vm.emptyStateMessage } }, "combobox-empty-list", listProps, false)) : _vm._t("list", null, { "listProps": listProps })], 2)];
}, proxy: true }, _vm.$slots.footer ? { key: "footerContent", fn: function() {
return [_c("div", { ref: "footer" }, [_vm._t("footer")], 2)];
}, proxy: true } : null], null, true) })];
} }], null, true) }, _vm.comboboxListeners));
};
var _sfc_staticRenderFns = [];
var __component__ = /* @__PURE__ */ normalizeComponent(
_sfc_main,
_sfc_render,
_sfc_staticRenderFns
);
const DtRecipeComboboxWithPopover = __component__.exports;
export {
DtRecipeComboboxWithPopover as default
};
//# sourceMappingURL=combobox_with_popover.vue.js.map