carbon-custom-elements
Version:
A Carbon Design System variant that's as easy to use as native HTML elements, with no framework tax, no framework silo.
1 lines • 15.9 kB
Source Map (JSON)
{"version":3,"sources":["components/tabs/tabs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,iBAAiB,EAAE,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC/F,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC;;GAEG;AACH,eAAO,MAAM,2BAA2B;;;;;CAKvC,CAAC;AAEF;;GAEG;AACH,oBAAY,oBAAoB;IAC9B;;OAEG;IACH,IAAI,SAAS;IAEb;;OAEG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,UAAU,eAAe;IAEzB;;OAEG;IACH,UAAU,eAAe;CAC1B;AAED;;GAEG;AACH,oBAAY,SAAS;IACnB;;OAEG;IACH,OAAO,KAAK;IAEZ;;OAEG;IACH,SAAS,cAAc;CACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAED;;;;;;;GAOG;AACH,cACM,MAAO,SAAQ,WAAoC;IACvD;;OAEG;IACH,OAAO,CAAC,oBAAoB,CAAC,CAAS;IAEtC;;OAEG;IACH,OAAO,CAAC,KAAK,CAAS;IAEtB;;OAEG;IACH,OAAO,CAAC,oBAAoB,CAAiC;IAE7D;;OAEG;IAEH,OAAO,CAAC,YAAY,CAAkB;IAEtC;;OAEG;IAGH,OAAO,CAAC,cAAc;IAOtB;;;OAGG;IAGH,OAAO,CAAC,eAAe;IAUvB;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAmBlC;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;;;;;;;;OASG;IACH,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO;IA+BlF,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,UAAU;IAexC,SAAS,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,EAAE,aAAa;IAuD/C,SAAS,CAAC,mBAAmB,CAAC,YAAY,EAAE,KAAK;IAMjD;;OAEG;IAEH,2BAA2B,SAA8D;IAEzF;;OAEG;IAEH,yBAAyB,SAAuB;IAEhD;;OAEG;IAEH,cAAc,SAAM;IAEpB;;OAEG;IAEH,IAAI,YAAqB;IAEzB,iBAAiB;IAOjB,YAAY,CAAC,iBAAiB,KAAA;IAqB9B,MAAM;IAwCN;;OAEG;IACH,MAAM,CAAC,YAAY,cAA2B;IAE9C;;OAEG;IACH,MAAM,KAAK,YAAY,WAEtB;IAED;;OAEG;IACH,MAAM,KAAK,mBAAmB,WAE7B;IAED;;OAEG;IACH,MAAM,KAAK,uBAAuB,WAEjC;IAED;;OAEG;IACH,MAAM,KAAK,oBAAoB,WAE9B;IAED;;;OAGG;IACH,MAAM,KAAK,iBAAiB,WAE3B;IAED;;OAEG;IACH,MAAM,KAAK,WAAW,WAErB;IAED,MAAM,CAAC,MAAM,MAAU;IAEvB;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE;CAYvE;AAED,eAAe,MAAM,CAAC","file":"tabs.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2019, 2020\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { classMap } from 'lit-html/directives/class-map';\nimport { html, property, query, customElement } from 'lit-element';\nimport ChevronDown16 from '@carbon/icons/lib/chevron--down/16';\nimport settings from 'carbon-components/es/globals/js/settings';\nimport HostListenerMixin from '../../globals/mixins/host-listener';\nimport HostListener from '../../globals/decorators/host-listener';\nimport { find, forEach } from '../../globals/internal/collection-helpers';\nimport BXContentSwitcher, { NAVIGATION_DIRECTION } from '../content-switcher/content-switcher';\nimport BXTab from './tab';\nimport styles from './tabs.scss';\n\nconst { prefix } = settings;\n\nexport { NAVIGATION_DIRECTION };\n\n/**\n * Navigation direction for narrow mode, associated with key symbols.\n */\nexport const NAVIGATION_DIRECTION_NARROW = {\n Up: -1,\n ArrowUp: -1,\n Down: 1,\n ArrowDown: 1,\n};\n\n/**\n * The keyboard action categories for dropdown.\n */\nexport enum TABS_KEYBOARD_ACTION {\n /**\n * Not doing any action.\n */\n NONE = 'none',\n\n /**\n * Keyboard action to close menu.\n */\n CLOSING = 'closing',\n\n /**\n * Keyboard action to navigate back/forward.\n */\n NAVIGATING = 'navigating',\n\n /**\n * Keyboard action to open/close menu on the trigger button or select/deselect a menu item.\n */\n TRIGGERING = 'triggering',\n}\n\n/**\n * Tabs types.\n */\nexport enum TABS_TYPE {\n /**\n * Regular tabs.\n */\n REGULAR = '',\n\n /**\n * Container type.\n */\n CONTAINER = 'container',\n}\n\n/**\n * Tabs.\n * @element bx-tabs\n * @fires bx-tabs-beingselected\n * The custom event fired before a tab is selected upon a user gesture.\n * Cancellation of this event stops changing the user-initiated selection.\n * @fires bx-tabs-selected - The custom event fired after a a tab is selected upon a user gesture.\n */\n@customElement(`${prefix}-tabs`)\nclass BXTabs extends HostListenerMixin(BXContentSwitcher) {\n /**\n * The latest status of this dropdown, for screen reader to accounce.\n */\n private _assistiveStatusText?: string;\n\n /**\n * `true` if the narrow mode dropdown should be open.\n */\n private _open = false;\n\n /**\n * The content of the selected item, used in the narrow mode.\n */\n private _selectedItemContent: DocumentFragment | null = null;\n\n /**\n * The DOM element for the trigger button in narrow mode.\n */\n @query('#trigger')\n private _triggerNode!: HTMLDivElement;\n\n /**\n * Handles `focus` event handler on this element.\n */\n @HostListener('focusin')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleFocusIn() {\n const { selectorItem } = this.constructor as typeof BXTabs;\n forEach(this.querySelectorAll(selectorItem), item => {\n (item as BXTab).inFocus = true;\n });\n }\n\n /**\n * Handles `blur` event handler on this element.\n * @param event The event.\n */\n @HostListener('focusout')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleFocusOut({ relatedTarget }: FocusEvent) {\n if (!this.contains(relatedTarget as Node)) {\n const { selectorItem } = this.constructor as typeof BXTabs;\n forEach(this.querySelectorAll(selectorItem), item => {\n (item as BXTab).inFocus = false;\n });\n this._handleUserInitiatedToggle(false);\n }\n }\n\n /**\n * Handles user-initiated toggling the open state.\n * @param [force] If specified, forces the open state to the given one.\n */\n private _handleUserInitiatedToggle(force: boolean = !this._open) {\n this._open = force;\n if (this._open) {\n this._assistiveStatusText = this.selectingItemsAssistiveText;\n } else {\n const {\n selectedItemAssistiveText,\n triggerContent,\n _assistiveStatusText: assistiveStatusText,\n _selectedItemContent: selectedItemContent,\n } = this;\n const selectedItemText = (selectedItemContent && selectedItemContent.textContent) || triggerContent;\n if (selectedItemText && assistiveStatusText !== selectedItemAssistiveText) {\n this._assistiveStatusText = selectedItemText;\n }\n }\n this.requestUpdate();\n }\n\n /**\n * Clears the selection of tabs.\n */\n private _clearHighlight() {\n forEach(this.querySelectorAll((this.constructor as typeof BXTabs).selectorItem), item => {\n (item as BXTab).highlighted = false;\n });\n }\n\n /**\n * Navigates through tabs.\n * @param direction `-1` to navigate backward, `1` to navigate forward.\n * @param [options] The options.\n * @param [options.immediate]\n * `true` to make it \"immediate selection change\" mode, which does:\n *\n * * Starts with the selected item\n * * Going prev/next item immediately changes the selection\n */\n protected _navigate(direction: number, { immediate }: { immediate?: boolean } = {}) {\n const { selectorItem, selectorItemHighlighted, selectorItemSelected } = this.constructor as typeof BXTabs;\n const nextItem = this._getNextItem(\n this.querySelector(immediate ? selectorItemSelected : selectorItemHighlighted) as BXTab,\n direction\n );\n if (!nextItem) {\n return;\n }\n\n if (immediate) {\n this._handleUserInitiatedSelectItem(nextItem as BXTab);\n } else {\n forEach(this.querySelectorAll(selectorItem), item => {\n (item as BXTab)[immediate ? 'selected' : 'highlighted'] = nextItem === item;\n });\n }\n\n // Using `{ block: 'nearest' }` to prevent scrolling unless scrolling is absolutely necessary.\n // `scrollIntoViewOptions` seems to work in latest Safari despite of MDN/caniuse table.\n // IE falls back to the old behavior.\n nextItem.scrollIntoView({ block: 'nearest' });\n\n const nextItemText = nextItem.textContent;\n if (nextItemText) {\n this._assistiveStatusText = nextItemText;\n }\n this.requestUpdate();\n }\n\n @HostListener('click')\n protected _handleClick(event: MouseEvent) {\n const { target } = event;\n if (this === target) {\n this._handleUserInitiatedToggle();\n } else if ((target as BXTab).value === this.value) {\n // Clicking on selected item, simply closes the narrow mode dropdown\n this._handleUserInitiatedToggle(false);\n } else {\n // Trying to select the item\n // If the custom event of the selection is canceled, we don't close the narrow mode dropdown\n super._handleClick(event);\n }\n }\n\n @HostListener('keydown')\n protected _handleKeydown({ key }: KeyboardEvent) {\n const { _open: open, _triggerNode: triggerNode } = this;\n const narrowMode = Boolean(triggerNode.offsetParent);\n const action = (this.constructor as typeof BXTabs).getAction(key, { narrowMode });\n if (!open && narrowMode) {\n // Menu closed in narrow mode\n switch (action) {\n case TABS_KEYBOARD_ACTION.NAVIGATING:\n this._handleUserInitiatedToggle(true);\n // If this menu gets open with an arrow key, resets the highlight\n this._clearHighlight();\n break;\n case TABS_KEYBOARD_ACTION.TRIGGERING:\n this._handleUserInitiatedToggle(true);\n break;\n default:\n break;\n }\n } else {\n switch (action) {\n case TABS_KEYBOARD_ACTION.CLOSING:\n this._handleUserInitiatedToggle(false);\n break;\n case TABS_KEYBOARD_ACTION.NAVIGATING:\n {\n const direction = narrowMode ? NAVIGATION_DIRECTION_NARROW[key] : NAVIGATION_DIRECTION[key];\n if (direction) {\n this._navigate(direction, { immediate: !narrowMode });\n }\n }\n break;\n case TABS_KEYBOARD_ACTION.TRIGGERING:\n {\n const { selectorItemHighlighted } = this.constructor as typeof BXTabs;\n const highlightedItem = this.querySelector(selectorItemHighlighted) as BXTab;\n if (highlightedItem) {\n if (highlightedItem.value === this.value) {\n // Selecting an already-selected item, simply closes the narrow mode dropdown\n this._handleUserInitiatedToggle(false);\n } else {\n // Trying to select the item\n // If the custom event of the selection is canceled, we don't close the narrow mode dropdown\n this._handleUserInitiatedSelectItem(highlightedItem);\n }\n } else {\n this._handleUserInitiatedToggle();\n }\n }\n break;\n default:\n break;\n }\n }\n }\n\n protected _selectionDidChange(itemToSelect: BXTab) {\n super._selectionDidChange(itemToSelect);\n this._assistiveStatusText = this.selectedItemAssistiveText;\n this._handleUserInitiatedToggle(false);\n }\n\n /**\n * An assistive text for screen reader to announce, telling the open state.\n */\n @property({ attribute: 'selecting-items-assistive-text' })\n selectingItemsAssistiveText = 'Selecting items. Use up and down arrow keys to navigate.';\n\n /**\n * An assistive text for screen reader to announce, telling that an item is selected.\n */\n @property({ attribute: 'selected-item-assistive-text' })\n selectedItemAssistiveText = 'Selected an item.';\n\n /**\n * The content of the trigger button for narrow mode.\n */\n @property({ attribute: 'trigger-content' })\n triggerContent = '';\n\n /**\n * Tabs type.\n */\n @property({ reflect: true })\n type = TABS_TYPE.REGULAR;\n\n connectedCallback() {\n if (!this.hasAttribute('tabindex')) {\n this.tabIndex = 0;\n }\n super.connectedCallback();\n }\n\n shouldUpdate(changedProperties) {\n super.shouldUpdate(changedProperties);\n const { selectorItem } = this.constructor as typeof BXTabs;\n if (changedProperties.has('type')) {\n forEach(this.querySelectorAll(selectorItem), elem => {\n (elem as BXTab).type = this.type;\n });\n }\n if (changedProperties.has('value')) {\n const item = find(this.querySelectorAll(selectorItem), elem => (elem as BXTab).value === this.value);\n if (item) {\n const range = this.ownerDocument!.createRange();\n range.selectNodeContents(item);\n this._selectedItemContent = range.cloneContents();\n } else {\n this._selectedItemContent = null;\n }\n }\n return true;\n }\n\n render() {\n const {\n triggerContent,\n _assistiveStatusText: assistiveStatusText,\n _open: open,\n _selectedItemContent: selectedItemContent,\n } = this;\n const triggerClasses = classMap({\n [`${prefix}--tabs-trigger`]: true,\n [`${prefix}--tabs-trigger--open`]: open,\n });\n const listClasses = classMap({\n [`${prefix}--tabs__nav`]: true,\n [`${prefix}--tabs__nav--hidden`]: !open,\n });\n return html`\n <div\n id=\"trigger\"\n role=\"button\"\n class=\"${triggerClasses}\"\n aria-labelledby=\"trigger-label\"\n aria-expanded=\"${String(open)}\"\n aria-haspopup=\"listbox\"\n aria-owns=\"tablist\"\n aria-controls=\"tablist\"\n >\n <span id=\"trigger-label\" class=\"${prefix}--tabs-trigger-text\">\n ${selectedItemContent || triggerContent}\n </span>\n ${ChevronDown16({ 'aria-hidden': 'true' })}\n </div>\n <ul id=\"tablist\" role=\"tablist\" class=\"${listClasses}\">\n <slot></slot>\n </ul>\n <div class=\"${prefix}--assistive-text\" role=\"status\" aria-live=\"assertive\" aria-relevant=\"additions text\">\n ${assistiveStatusText}\n </div>\n `;\n }\n\n /**\n * Symbols of keys that triggers opening/closing menu and selecting/deselecting menu item.\n */\n static TRIGGER_KEYS = new Set([' ', 'Enter']);\n\n /**\n * A selector that will return tabs.\n */\n static get selectorItem() {\n return `${prefix}-tab`;\n }\n\n /**\n * A selector that will return enabled tabs.\n */\n static get selectorItemEnabled() {\n return `${prefix}-tab:not([disabled])`;\n }\n\n /**\n * A selector that will return highlighted tabs.\n */\n static get selectorItemHighlighted() {\n return `${prefix}-tab[highlighted]`;\n }\n\n /**\n * A selector that will return selected tabs.\n */\n static get selectorItemSelected() {\n return `${prefix}-tab[selected]`;\n }\n\n /**\n * The name of the custom event fired before a tab is selected upon a user gesture.\n * Cancellation of this event stops changing the user-initiated selection.\n */\n static get eventBeforeSelect() {\n return `${prefix}-tabs-beingselected`;\n }\n\n /**\n * The name of the custom event fired after a a tab is selected upon a user gesture.\n */\n static get eventSelect() {\n return `${prefix}-tabs-selected`;\n }\n\n static styles = styles;\n\n /**\n * @param key The key symbol.\n * @param [options] The options.\n * @param [options.narrowMode] `true` to get the action for narrow mode.\n * @returns A action for dropdown for the given key symbol.\n */\n static getAction(key: string, { narrowMode }: { narrowMode?: boolean }) {\n if (key === 'Escape') {\n return TABS_KEYBOARD_ACTION.CLOSING;\n }\n if (key in (narrowMode ? NAVIGATION_DIRECTION_NARROW : NAVIGATION_DIRECTION)) {\n return TABS_KEYBOARD_ACTION.NAVIGATING;\n }\n if (narrowMode && this.TRIGGER_KEYS.has(key)) {\n return TABS_KEYBOARD_ACTION.TRIGGERING;\n }\n return TABS_KEYBOARD_ACTION.NONE;\n }\n}\n\nexport default BXTabs;\n"]}