UNPKG

@dialpad/dialtone-vue

Version:

Vue component library for Dialpad's design system Dialtone

352 lines (351 loc) 10.7 kB
import a from "../../common/mixins/keyboard-list-navigation.js"; import { DROPDOWN_PADDING_CLASSES as o } from "./dropdown-constants.js"; import { getUniqueString as l } from "../../common/utils/index.js"; import { EVENT_KEYNAMES as i } from "../../common/constants/index.js"; import { n as h } from "../../_plugin-vue2_normalizer-DSLOjnn3.js"; import { LIST_ITEM_NAVIGATION_TYPES as r } from "../list-item/list-item-constants.js"; import d from "../popover/popover.js"; import { POPOVER_APPEND_TO_VALUES as p } from "../popover/popover-constants.js"; const u = { name: "DtDropdown", components: { DtPopover: d }, mixins: [ a({ indexKey: "highlightIndex", idKey: "highlightId", listElementKey: "getListElement", listItemRole: "menuitem", afterHighlightMethod: "afterHighlight", beginningOfListMethod: "beginningOfListMethod", endOfListMethod: "endOfListMethod", activeItemKey: "activeItemEl", focusOnKeyboardNavigation: !0 }) ], props: { /** * Controls whether the dropdown is shown. Leaving this null will have the dropdown trigger on click by default. * If you set this value, the default trigger behavior will be disabled and you can control it as you need. * Supports .sync modifier */ open: { type: Boolean, default: null }, /** * Opens the dropdown on right click (context menu). If you set this value to `true`, * the default trigger behavior will be disabled. */ openOnContext: { type: Boolean, default: !1 }, /** * Vertical padding size around the list element. * @values none, small, large */ padding: { type: String, default: "small", validator: (e) => Object.keys(o).some((t) => t === e) }, /** * Determines modal state, dropdown has a modal overlay preventing interaction with elements * below it, but it is invisible. */ modal: { type: Boolean, default: !0 }, /** * Width configuration for the popover content. When its value is 'anchor', * the popover content will have the same width as the anchor. * @values null, anchor */ contentWidth: { type: String, 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: "" }, /** * 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 l(); } }, /** * The type of navigation that this component should support. * - "arrow-keys" for items that are navigated with UP/DOWN keys. * - "tab" for items that are navigated using the TAB key. * - "none" for static items that are not interactive. * @values arrow-keys, tab, none */ navigationType: { type: String, default: r.ARROW_KEYS, validator: (e) => Object.values(r).includes(e) }, /** * If the dropdown does not fit in the direction described by "placement", * it will attempt to change it's direction to the "fallbackPlacements". * * @values top, top-start, top-end, * right, right-start, right-end, * left, left-start, left-end, * bottom, bottom-start, bottom-end, * auto, auto-start, auto-end * */ fallbackPlacements: { type: Array, default: () => ["auto"] }, /** * The direction the dropdown displays relative to the anchor. */ placement: { type: String, default: "bottom" }, /** * 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 }, /** * Additional class for the wrapper list element. */ listClass: { type: [String, Array, Object], 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: (e) => p.includes(e) || e instanceof HTMLElement }, /** * If set to false the dialog will display over top of the anchor when there is insufficient space. * If set to true it will never move from its position relative to the anchor and will clip instead. * <a * class="d-link" * href="https://popper.js.org/docs/v2/modifiers/prevent-overflow/#tether" * target="_blank" * > * Popper.js docs * </a> * @values true, false */ tether: { type: Boolean, default: !0 }, /** * Named transition when the content display is toggled. * @see DtLazyShow */ transition: { type: String, default: "fade" } }, emits: [ /** * Event fired when the highlight changes * * @event highlight * @type {Number} */ "highlight", /** * Event fired when dropdown is shown or hidden * * @event opened * @type {Boolean | Array} */ "opened", /** * Event fired to sync the open prop with the parent component * @event update:open */ "update:open" ], data() { return { LIST_ITEM_NAVIGATION_TYPES: r, DROPDOWN_PADDING_CLASSES: o, EVENT_KEYNAMES: i, openedWithKeyboard: !1, isOpen: null }; }, computed: { dropdownListeners() { return { ...this.$listeners, opened: (e) => { this.updateInitialHighlightIndex(e); }, keydown: (e) => { switch (e.code) { case i.up: case i.arrowup: this.onUpKeyPress(e), e.stopPropagation(), e.preventDefault(); break; case i.down: case i.arrowdown: this.onDownKeyPress(e), e.stopPropagation(), e.preventDefault(); break; case i.space: case i.spacebar: this.onSpaceKey(); break; case i.enter: this.onEnterKey(); break; case i.home: this.onHomeKeyPress(e), e.stopPropagation(), e.preventDefault(); break; case i.end: this.onEndKeyPress(e), e.stopPropagation(), e.preventDefault(); break; default: this.onKeyPress(e); break; } this.$emit("keydown", e); } }; }, beginningOfListMethod() { return this.onBeginningOfList || this.jumpToEnd; }, endOfListMethod() { return this.onEndOfList || this.jumpToBeginning; }, activeItemEl() { return this.getListElement().querySelector("#" + this.highlightId); }, isArrowKeyNav() { return this.navigationType === this.LIST_ITEM_NAVIGATION_TYPES.ARROW_KEYS; }, listClasses() { return [ "d-dropdown-list", o[this.padding], this.listClass, { "d-context-menu-list": this.openOnContext } ]; }, shouldOpenWithArrowKeys() { return !this.openOnContext; } }, methods: { onMouseHighlight(e) { const t = e.target.closest("li"); t && t.role && this.highlightId !== t.id && (this.setHighlightId(t.id), t.focus()); }, getListElement() { return this.$refs.listWrapper; }, clearHighlightIndex() { this.setHighlightIndex(-1); }, afterHighlight() { this.highlightIndex !== this._itemsLength() - 1 && this.$emit("highlight", this.highlightIndex); }, updateInitialHighlightIndex(e) { this.isOpen = e, e ? (this.openedWithKeyboard && this.isArrowKeyNav && this.setHighlightIndex(0), this.$emit("opened", !0)) : (this.clearHighlightIndex(), this.openedWithKeyboard = !1, this.$emit("opened", !1)); }, onSpaceKey() { this.open || (this.openedWithKeyboard = !0); }, onEnterKey() { this.open || (this.openedWithKeyboard = !0); }, onUpKeyPress() { if (!this.isOpen) { this.openedWithKeyboard = !0; return; } if (this.isArrowKeyNav) return this.onUpKey(); }, onDownKeyPress() { if (!this.isOpen) { this.openedWithKeyboard = !0; return; } if (this.isArrowKeyNav) return this.onDownKey(); }, onHomeKeyPress() { if (!(!this.isOpen || !this.isArrowKeyNav)) return this.onHomeKey(); }, onEndKeyPress() { if (!(!this.isOpen || !this.isArrowKeyNav)) return this.onEndKey(); }, onKeyPress(e) { if (!(!this.isOpen || !this.isArrowKeyNav || !this.isValidLetter(e.key))) return e.stopPropagation(), e.preventDefault(), this.onNavigationKey(e.key); } } }; var g = function() { var t = this, s = t._self._c; return s("dt-popover", t._g({ ref: "popover", attrs: { "content-width": t.contentWidth, open: t.open, placement: t.placement, "initial-focus-element": t.openedWithKeyboard ? "first" : "dialog", "fallback-placements": t.fallbackPlacements, padding: "none", role: "menu", "append-to": t.appendTo, modal: t.modal, "max-height": t.maxHeight, "max-width": t.maxWidth, "open-with-arrow-keys": t.shouldOpenWithArrowKeys, "open-on-context": t.openOnContext, tether: t.tether, transition: t.transition }, scopedSlots: t._u([{ key: "anchor", fn: function({ attrs: n }) { return [t._t("anchor", null, null, n)]; } }, { key: "content", fn: function({ close: n }) { return [s("ul", { ref: "listWrapper", class: t.listClasses, attrs: { id: t.listId, "data-qa": "dt-dropdown-list-wrapper" }, on: { mouseleave: t.clearHighlightIndex, "!mousemove": function(y) { return t.onMouseHighlight.apply(null, arguments); } } }, [t._t("list", null, { close: n })], 2)]; } }, { key: "footerContent", fn: function({ close: n }) { return [t._t("footer", null, { close: n })]; } }], null, !0) }, t.dropdownListeners)); }, f = [], m = /* @__PURE__ */ h( u, g, f ); const x = m.exports; export { x as default }; //# sourceMappingURL=dropdown.js.map