UNPKG

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 26.5 kB
{"version":3,"sources":["components/dropdown/dropdown.ts"],"names":[],"mappings":"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","file":"dropdown.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 settings from 'carbon-components/es/globals/js/settings';\nimport { classMap } from 'lit-html/directives/class-map';\nimport { TemplateResult } from 'lit-html';\nimport { ifDefined } from 'lit-html/directives/if-defined';\nimport { html, property, query, customElement, LitElement } from 'lit-element';\nimport ChevronDown16 from '@carbon/icons/lib/chevron--down/16';\nimport WarningFilled16 from '@carbon/icons/lib/warning--filled/16';\nimport { FORM_ELEMENT_COLOR_SCHEME } from '../../globals/shared-enums';\nimport FocusMixin from '../../globals/mixins/focus';\nimport HostListenerMixin from '../../globals/mixins/host-listener';\nimport ValidityMixin from '../../globals/mixins/validity';\nimport HostListener from '../../globals/decorators/host-listener';\nimport { find, forEach, indexOf } from '../../globals/internal/collection-helpers';\nimport BXDropdownItem from './dropdown-item';\nimport styles from './dropdown.scss';\n\nexport { FORM_ELEMENT_COLOR_SCHEME as DROPDOWN_COLOR_SCHEME } from '../../globals/shared-enums';\n\nconst { prefix } = settings;\n\n/**\n * Navigation direction, associated with key symbols.\n */\nexport const NAVIGATION_DIRECTION = {\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 DROPDOWN_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 * Dropdown size.\n */\nexport enum DROPDOWN_SIZE {\n /**\n * Regular size.\n */\n REGULAR = '',\n\n /**\n * Small size.\n */\n SMALL = 'sm',\n\n /**\n * Extra large size.\n */\n EXTRA_LARGE = 'xl',\n}\n\n/**\n * Dropdown types.\n */\nexport enum DROPDOWN_TYPE {\n /**\n * Regular type.\n */\n REGULAR = '',\n\n /**\n * Inline type.\n */\n INLINE = 'inline',\n}\n\n/**\n * Dropdown.\n * @element bx-dropdown\n * @csspart label-text The label text.\n * @csspart helper-text The helper text.\n * @csspart trigger-button The trigger button.\n * @csspart menu-body The menu body.\n * @csspart validity-message The validity message.\n * @fires bx-dropdown-beingselected\n * The custom event fired before a dropdown item is selected upon a user gesture.\n * Cancellation of this event stops changing the user-initiated selection.\n * @fires bx-dropdown-selected - The custom event fired after a a dropdown item is selected upon a user gesture.\n */\n@customElement(`${prefix}-dropdown`)\nclass BXDropdown extends ValidityMixin(HostListenerMixin(FocusMixin(LitElement))) {\n /**\n * The latest status of this dropdown, for screen reader to accounce.\n */\n protected _assistiveStatusText?: string;\n\n /**\n * The content of the selected item.\n */\n protected _selectedItemContent: DocumentFragment | null = null;\n\n /**\n * `true` if the trigger button should be focusable.\n * Derived class can set `false` to this if the trigger button contains another primary focusable element (e.g. `<input>`).\n */\n protected _shouldTriggerBeFocusable = true;\n\n /**\n * The `<slot>` element for the helper text in the shadow DOM.\n */\n @query('slot[name=\"helper-text\"]')\n protected _slotHelperTextNode!: HTMLSlotElement;\n\n /**\n * The `<slot>` element for the label text in the shadow DOM.\n */\n @query('slot[name=\"label-text\"]')\n protected _slotLabelTextNode!: HTMLSlotElement;\n\n /**\n * @param itemToSelect A dropdown item. Absense of this argument means clearing selection.\n * @returns `true` if the selection of this dropdown should change if the given item is selected upon user interaction.\n */\n protected _selectionShouldChange(itemToSelect?: BXDropdownItem) {\n return !itemToSelect || itemToSelect.value !== this.value;\n }\n\n /**\n * A callback that runs after change in dropdown selection upon user interaction is confirmed.\n * @param itemToSelect\n * A dropdown item.\n * Absense of this argument means clearing selection, which may be handled by a derived class.\n */\n protected _selectionDidChange(itemToSelect?: BXDropdownItem) {\n if (itemToSelect) {\n this.value = itemToSelect.value;\n forEach(this.querySelectorAll((this.constructor as typeof BXDropdown).selectorItemSelected), item => {\n (item as BXDropdownItem).selected = false;\n });\n itemToSelect.selected = true;\n this._assistiveStatusText = this.selectedItemAssistiveText;\n this._handleUserInitiatedToggle(false);\n }\n }\n\n /**\n * Handles `click` event on the top-level element in the shadow DOM.\n * @param event The event.\n */\n protected _handleClickInner(event: MouseEvent) {\n if (this.shadowRoot!.contains(event.target as Node)) {\n this._handleUserInitiatedToggle();\n } else {\n const item = (event.target as Element).closest((this.constructor as typeof BXDropdown).selectorItem) as BXDropdownItem;\n if (this.contains(item)) {\n this._handleUserInitiatedSelectItem(item);\n }\n }\n }\n\n /**\n * Handler for the `keydown` event on the top-level element in the shadow DOM.\n */\n protected _handleKeydownInner(event: KeyboardEvent) {\n const { key } = event;\n const action = (this.constructor as typeof BXDropdown).getAction(key);\n if (!this.open) {\n switch (action) {\n case DROPDOWN_KEYBOARD_ACTION.NAVIGATING:\n this._handleUserInitiatedToggle(true);\n // If this menu gets open with an arrow key, reset the highlight\n this._clearHighlight();\n break;\n default:\n break;\n }\n } else {\n switch (action) {\n case DROPDOWN_KEYBOARD_ACTION.CLOSING:\n this._handleUserInitiatedToggle(false);\n break;\n case DROPDOWN_KEYBOARD_ACTION.NAVIGATING:\n this._navigate(NAVIGATION_DIRECTION[key]);\n break;\n default:\n break;\n }\n }\n }\n\n /**\n * Handler for the `keypress` event on the top-level element in the shadow DOM.\n */\n protected _handleKeypressInner(event: KeyboardEvent) {\n const { key } = event;\n const action = (this.constructor as typeof BXDropdown).getAction(key);\n if (!this.open) {\n switch (action) {\n case DROPDOWN_KEYBOARD_ACTION.TRIGGERING:\n this._handleUserInitiatedToggle(true);\n break;\n default:\n break;\n }\n } else {\n switch (action) {\n case DROPDOWN_KEYBOARD_ACTION.TRIGGERING:\n {\n const constructor = this.constructor as typeof BXDropdown;\n const highlightedItem = this.querySelector(constructor.selectorItemHighlighted) as BXDropdownItem;\n if (highlightedItem) {\n this._handleUserInitiatedSelectItem(highlightedItem);\n } else {\n this._handleUserInitiatedToggle(false);\n }\n }\n break;\n default:\n break;\n }\n }\n }\n\n /**\n * Handles `blur` event handler on the document this element is in.\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 protected _handleFocusOut(event: FocusEvent) {\n if (!this.contains(event.relatedTarget as Node)) {\n this._handleUserInitiatedToggle(false);\n }\n }\n\n /**\n * Handles `slotchange` event for the `<slot>` for helper text.\n */\n protected _handleSlotchangeHelperText() {\n this.requestUpdate();\n }\n\n /**\n * Handles `slotchange` event for the `<slot>` for label text.\n */\n protected _handleSlotchangeLabelText() {\n this.requestUpdate();\n }\n\n /**\n * Handles user-initiated selection of a dropdown item.\n * @param [item] The dropdown item user wants to select. Absense of this argument means clearing selection.\n */\n protected _handleUserInitiatedSelectItem(item?: BXDropdownItem) {\n if (this._selectionShouldChange(item)) {\n const init = {\n bubbles: true,\n composed: true,\n detail: {\n item,\n },\n };\n const constructor = this.constructor as typeof BXDropdown;\n const beforeSelectEvent = new CustomEvent(constructor.eventBeforeSelect, {\n ...init,\n cancelable: true,\n });\n if (this.dispatchEvent(beforeSelectEvent)) {\n this._selectionDidChange(item);\n const afterSelectEvent = new CustomEvent(constructor.eventSelect, init);\n this.dispatchEvent(afterSelectEvent);\n }\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 protected _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 forEach(this.querySelectorAll((this.constructor as typeof BXDropdown).selectorItemHighlighted), item => {\n (item as BXDropdownItem).highlighted = false;\n });\n }\n this.requestUpdate();\n }\n\n /**\n * Clears the selection of dropdown items.\n */\n protected _clearHighlight() {\n forEach(this.querySelectorAll((this.constructor as typeof BXDropdown).selectorItem), item => {\n (item as BXDropdownItem).highlighted = false;\n });\n }\n\n /**\n * Navigate through dropdown items.\n * @param direction `-1` to navigate backward, `1` to navigate forward.\n */\n protected _navigate(direction: number) {\n const constructor = this.constructor as typeof BXDropdown;\n const items = this.querySelectorAll(constructor.selectorItem);\n const highlightedItem = this.querySelector(constructor.selectorItemHighlighted);\n const highlightedIndex = indexOf(items, highlightedItem!);\n let nextIndex = highlightedIndex + direction;\n if (nextIndex < 0) {\n nextIndex = items.length - 1;\n }\n if (nextIndex >= items.length) {\n nextIndex = 0;\n }\n forEach(items, (item, i) => {\n (item as BXDropdownItem).highlighted = i === nextIndex;\n });\n\n const nextItem = items[nextIndex];\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 /* eslint-disable class-methods-use-this */\n /**\n * @returns The content preceding the trigger button.\n */\n protected _renderPrecedingTriggerContent(): TemplateResult | void {\n return undefined;\n }\n /* eslint-enable class-methods-use-this */\n\n /**\n * @returns The main content of the trigger button.\n */\n protected _renderTriggerContent(): TemplateResult {\n const { triggerContent, _selectedItemContent: selectedItemContent } = this;\n return html`\n <span id=\"trigger-label\" class=\"${prefix}--list-box__label\">${selectedItemContent || triggerContent}</span>\n `;\n }\n\n /* eslint-disable class-methods-use-this */\n /**\n * @returns The content following the trigger button.\n */\n protected _renderFollowingTriggerContent(): TemplateResult | void {\n return undefined;\n }\n /* eslint-enable class-methods-use-this */\n\n /**\n * The color scheme.\n */\n @property({ attribute: 'color-scheme', reflect: true })\n colorScheme = FORM_ELEMENT_COLOR_SCHEME.REGULAR;\n\n /**\n * `true` if this dropdown should be disabled.\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The helper text.\n */\n @property({ attribute: 'helper-text' })\n helperText = '';\n\n /**\n * `true` to show the UI of the invalid state.\n */\n @property({ type: Boolean, reflect: true })\n invalid = false;\n\n /**\n * The label text.\n */\n @property({ attribute: 'label-text' })\n labelText = '';\n\n /**\n * `true` if this dropdown should be open.\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * `true` if the value is required.\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * The special validity message for `required`.\n */\n @property({ attribute: 'required-validity-message' })\n requiredValidityMessage = 'Please fill out this field.';\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 * Dropdown size.\n */\n @property({ reflect: true })\n size = DROPDOWN_SIZE.REGULAR;\n\n /**\n * The `aria-label` attribute for the UI indicating the closed state.\n */\n @property({ attribute: 'toggle-label-closed' })\n toggleLabelClosed = '';\n\n /**\n * The `aria-label` attribute for the UI indicating the open state.\n */\n @property({ attribute: 'toggle-label-open' })\n toggleLabelOpen = '';\n\n /**\n * The content of the trigger button.\n */\n @property({ attribute: 'trigger-content' })\n triggerContent = '';\n\n /**\n * `true` if this dropdown should use the inline UI variant.\n */\n @property({ reflect: true })\n type = DROPDOWN_TYPE.REGULAR;\n\n /**\n * The validity message.\n */\n @property({ attribute: 'validity-message' })\n validityMessage = '';\n\n /**\n * The value of the selected item.\n */\n @property({ reflect: true })\n value = '';\n\n createRenderRoot() {\n return this.attachShadow({ mode: 'open', delegatesFocus: true });\n }\n\n shouldUpdate(changedProperties) {\n const { selectorItem } = this.constructor as typeof BXDropdown;\n if (changedProperties.has('size')) {\n forEach(this.querySelectorAll(selectorItem), elem => {\n (elem as BXDropdownItem).size = this.size;\n });\n }\n if (changedProperties.has('value')) {\n // `<bx-multi-select>` updates selection beforehand\n // because our rendering logic for `<bx-multi-select>` looks for selected items via `qSA()`\n forEach(this.querySelectorAll(selectorItem), elem => {\n (elem as BXDropdownItem).selected = (elem as BXDropdownItem).value === this.value;\n });\n const item = find(this.querySelectorAll(selectorItem), elem => (elem as BXDropdownItem).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 updated(changedProperties) {\n const { helperText, type } = this;\n const inline = type === DROPDOWN_TYPE.INLINE;\n const { selectorItem } = this.constructor as typeof BXDropdown;\n if (changedProperties.has('disabled')) {\n const { disabled } = this;\n // Propagate `disabled` attribute to descendants until `:host-context()` gets supported in all major browsers\n forEach(this.querySelectorAll(selectorItem), elem => {\n (elem as BXDropdownItem).disabled = disabled;\n });\n }\n if ((changedProperties.has('helperText') || changedProperties.has('type')) && helperText && inline) {\n // eslint-disable-next-line no-console\n console.warn('Found `helperText` property/attribute usage in inline mode, that is not supported, at:', this);\n }\n }\n\n render() {\n const {\n colorScheme,\n disabled,\n helperText,\n invalid,\n labelText,\n open,\n toggleLabelClosed,\n toggleLabelOpen,\n size,\n type,\n validityMessage,\n _assistiveStatusText: assistiveStatusText,\n _shouldTriggerBeFocusable: shouldTriggerBeFocusable,\n _handleClickInner: handleClickInner,\n _handleKeydownInner: handleKeydownInner,\n _handleKeypressInner: handleKeypressInner,\n _handleSlotchangeHelperText: handleSlotchangeHelperText,\n _handleSlotchangeLabelText: handleSlotchangeLabelText,\n _slotHelperTextNode: slotHelperTextNode,\n _slotLabelTextNode: slotLabelTextNode,\n } = this;\n const inline = type === DROPDOWN_TYPE.INLINE;\n const selectedItemsCount = this.querySelectorAll((this.constructor as typeof BXDropdown).selectorItemSelected).length;\n const classes = classMap({\n [`${prefix}--dropdown`]: true,\n [`${prefix}--list-box`]: true,\n [`${prefix}--list-box--${colorScheme}`]: colorScheme,\n [`${prefix}--list-box--disabled`]: disabled,\n [`${prefix}--list-box--inline`]: inline,\n [`${prefix}--list-box--expanded`]: open,\n [`${prefix}--list-box--${size}`]: size,\n [`${prefix}--dropdown--invalid`]: invalid,\n [`${prefix}--dropdown--inline`]: inline,\n [`${prefix}--dropdown--selected`]: selectedItemsCount > 0,\n });\n const labelClasses = classMap({\n [`${prefix}--label`]: true,\n [`${prefix}--label--disabled`]: disabled,\n });\n const helperClasses = classMap({\n [`${prefix}--form__helper-text`]: true,\n [`${prefix}--form__helper-text--disabled`]: disabled,\n });\n const iconContainerClasses = classMap({\n [`${prefix}--list-box__menu-icon`]: true,\n [`${prefix}--list-box__menu-icon--open`]: open,\n });\n const toggleLabel = (open ? toggleLabelOpen : toggleLabelClosed) || undefined;\n const hasHelperText = helperText || (slotHelperTextNode && slotHelperTextNode.assignedNodes().length > 0);\n const hasLabelText = labelText || (slotLabelTextNode && slotLabelTextNode.assignedNodes().length > 0);\n const validity = !invalid\n ? undefined\n : html`\n <div part=\"validity-message\" class=${`${prefix}--form-requirement`}>\n <slot name=\"validity-message\">${validityMessage}</slot>\n </div>\n `;\n const validityIcon = !invalid\n ? undefined\n : WarningFilled16({ class: `${prefix}--list-box__invalid-icon`, 'aria-label': toggleLabel });\n const menuBody = !open\n ? undefined\n : html`\n <div id=\"menu-body\" part=\"menu-body\" class=\"${prefix}--list-box__menu\" role=\"listbox\" tabindex=\"-1\">\n <slot></slot>\n </div>\n `;\n return html`\n <label part=\"label-text\" class=\"${labelClasses}\" ?hidden=\"${!hasLabelText}\">\n <slot name=\"label-text\" @slotchange=\"${handleSlotchangeLabelText}\">${labelText}</slot>\n </label>\n <div part=\"helper-text\" class=\"${helperClasses}\" ?hidden=\"${inline || !hasHelperText}\">\n <slot name=\"helper-text\" @slotchange=\"${handleSlotchangeHelperText}\">${helperText}</slot>\n </div>\n <div\n role=\"listbox\"\n class=\"${classes}\"\n ?data-invalid=${invalid}\n @click=${handleClickInner}\n @keydown=${handleKeydownInner}\n @keypress=${handleKeypressInner}\n >\n ${validityIcon}\n <div\n part=\"trigger-button\"\n role=\"${ifDefined(!shouldTriggerBeFocusable ? undefined : 'button')}\"\n class=\"${prefix}--list-box__field\"\n tabindex=\"${ifDefined(!shouldTriggerBeFocusable ? undefined : '0')}\"\n aria-labelledby=\"trigger-label\"\n aria-expanded=\"${String(open)}\"\n aria-haspopup=\"listbox\"\n aria-owns=\"menu-body\"\n aria-controls=\"menu-body\"\n >\n ${this._renderPrecedingTriggerContent()}${this._renderTriggerContent()}${this._renderFollowingTriggerContent()}\n <div class=\"${iconContainerClasses}\">\n ${ChevronDown16({ 'aria-label': toggleLabel })}\n </div>\n </div>\n ${menuBody}\n </div>\n ${validity}\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 highlighted items.\n */\n static get selectorItemHighlighted() {\n return `${prefix}-dropdown-item[highlighted]`;\n }\n\n /**\n * A selector that will return dropdown items.\n */\n static get selectorItem() {\n return `${prefix}-dropdown-item`;\n }\n\n /**\n * A selector that will return selected items.\n */\n static get selectorItemSelected() {\n return `${prefix}-dropdown-item[selected]`;\n }\n\n /**\n * The name of the custom event fired before a dropdown item 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}-dropdown-beingselected`;\n }\n\n /**\n * The name of the custom event fired after a a dropdown item is selected upon a user gesture.\n */\n static get eventSelect() {\n return `${prefix}-dropdown-selected`;\n }\n\n static styles = styles;\n\n /**\n * @returns A action for dropdown for the given key symbol.\n */\n static getAction(key: string) {\n if (key === 'Escape') {\n return DROPDOWN_KEYBOARD_ACTION.CLOSING;\n }\n if (key in NAVIGATION_DIRECTION) {\n return DROPDOWN_KEYBOARD_ACTION.NAVIGATING;\n }\n if (this.TRIGGER_KEYS.has(key)) {\n return DROPDOWN_KEYBOARD_ACTION.TRIGGERING;\n }\n return DROPDOWN_KEYBOARD_ACTION.NONE;\n }\n}\n\nexport default BXDropdown;\n"]}