UNPKG

@limetech/lime-elements

Version:
428 lines (424 loc) • 14 kB
import { r as registerInstance, c as createEvent, h, g as getElement } from './index-2714248e.js'; import { i as isDescendant } from './dom-0b0170a0.js'; import { T as TAB, E as ESCAPE, a as ENTER, A as ARROW_UP, b as ARROW_DOWN } from './keycodes-e2e44b7e.js'; import { c as createRandomString } from './random-string-355331d3.js'; import { g as getIconName, d as getIconFillColor } from './get-icon-props-37514418.js'; import { d as debounce } from './debounce-9a05c91c.js'; import './isObject-c74e273c.js'; import './toNumber-a6ed64f0.js'; import './isSymbol-5bf20921.js'; import './isObjectLike-38996507.js'; const pickerCss = ":host{position:relative;display:block}:host([hidden]){display:none}"; const SEARCH_DEBOUNCE = 300; const CHIP_SET_TAG_NAME = 'limel-chip-set'; const DEFAULT_SEARCHER_MAX_RESULTS = 20; const Picker = class { constructor(hostRef) { registerInstance(this, hostRef); this.change = createEvent(this, "change", 7); this.interact = createEvent(this, "interact", 7); this.action = createEvent(this, "action", 7); // Should NOT be decorated with State(), since this // should not trigger a re-render by itself. this.chipSetEditMode = false; this.getValueId = (item) => { const value = item.value; if (!!value && typeof value === 'object') { return value.id; } return value; }; this.createChips = (value) => { if (!value) { return []; } if (this.multiple) { const listItems = value; return listItems.map(this.createChip); } const listItem = value; return [this.createChip(listItem)]; }; this.createChip = (listItem) => { const name = getIconName(listItem.icon); const color = getIconFillColor(listItem.icon, listItem.iconColor); const valueId = this.getValueId(listItem); return { id: `${valueId}`, text: listItem.text, removable: true, icon: name ? { name: name, color: color } : undefined, image: listItem.image, value: listItem, menuItems: listItem.actions, }; }; this.search = async (query) => { const timeoutId = setTimeout(() => { this.loading = true; }); const searcher = this.searcher || this.defaultSearcher; const result = (await searcher(this.textValue)); // If the search function resolves immediately, // the loading spinner will not be shown. clearTimeout(timeoutId); this.handleSearchResult(query, result); }; this.defaultSearcher = async (query) => { if (query === '') { return this.allItems.slice(0, DEFAULT_SEARCHER_MAX_RESULTS); } const filteredItems = this.allItems.filter((item) => { let searchText = item.text.toLowerCase(); if (item.secondaryText) { searchText = searchText + ' ' + item.secondaryText.toLowerCase(); } return searchText.includes(query.toLowerCase()); }); return filteredItems.slice(0, DEFAULT_SEARCHER_MAX_RESULTS); }; this.disabled = false; this.readonly = false; this.label = undefined; this.searchLabel = undefined; this.helperText = undefined; this.leadingIcon = undefined; this.emptyResultMessage = undefined; this.required = false; this.invalid = false; this.value = undefined; this.searcher = undefined; this.allItems = []; this.multiple = false; this.delimiter = null; this.actions = []; this.actionPosition = 'bottom'; this.actionScrollBehavior = 'sticky'; this.badgeIcons = false; this.items = undefined; this.textValue = ''; this.loading = false; this.chips = []; this.handleTextInput = this.handleTextInput.bind(this); this.handleInputKeyDown = this.handleInputKeyDown.bind(this); this.handleDropdownKeyDown = this.handleDropdownKeyDown.bind(this); this.handleInputFieldFocus = this.handleInputFieldFocus.bind(this); this.handleChange = this.handleChange.bind(this); this.handleInteract = this.handleInteract.bind(this); this.handleListChange = this.handleListChange.bind(this); this.handleActionListChange = this.handleActionListChange.bind(this); this.handleStopEditAndBlur = this.handleStopEditAndBlur.bind(this); this.handleCloseMenu = this.handleCloseMenu.bind(this); this.onListKeyDown = this.onListKeyDown.bind(this); this.portalId = createRandomString(); this.debouncedSearch = debounce(this.search, SEARCH_DEBOUNCE); } componentWillLoad() { this.chips = this.createChips(this.value); } componentDidLoad() { this.chipSet = this.host.shadowRoot.querySelector(CHIP_SET_TAG_NAME); } disconnectedCallback() { this.debouncedSearch.cancel(); } async componentWillUpdate() { this.chipSetEditMode = false; if (this.chipSet) { this.chipSetEditMode = await this.chipSet.getEditMode(); } } render() { const props = {}; if (!this.multiple) { props.maxItems = 1; } return [ h("limel-chip-set", Object.assign({ type: "input", inputType: "search", label: this.label, helperText: this.helperText, leadingIcon: this.leadingIcon, value: this.chips, disabled: this.disabled, invalid: this.invalid, delimiter: this.renderDelimiter(), readonly: this.readonly, required: this.required, searchLabel: this.searchLabel, onInput: this.handleTextInput, onKeyDown: this.handleInputKeyDown, onChange: this.handleChange, onInteract: this.handleInteract, onStartEdit: this.handleInputFieldFocus, onStopEdit: this.handleStopEditAndBlur, emptyInputOnBlur: false, clearAllButton: this.multiple && !this.chipSetEditMode }, props)), this.renderDropdown(), ]; } onChangeValue() { this.chips = this.createChips(this.value); } renderDelimiter() { if (this.multiple) { return this.delimiter; } return null; } /** * Renders the dropdown with the items to pick from, or a spinner if the picker * is waiting for items to be received * * @returns picker dropdown */ renderDropdown() { const dropDownContent = this.getDropdownContent(); const content = []; if (this.shouldShowDropDownContent()) { const actionContent = this.getActionContent(); if (this.actionPosition === 'top') { content.push(actionContent); } if (dropDownContent) { content.push(dropDownContent); } if (this.actionPosition === 'bottom') { content.push(actionContent); } } return this.renderPortal(content); } getActionContent() { var _a, _b; const actionCount = (_b = (_a = this.actions) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0; if (actionCount === 0) { return null; } return [ h("limel-list", { class: { 'static-actions-list': true, 'is-on-top': this.actionPosition === 'top', 'is-at-bottom': this.actionPosition === 'bottom', 'has-position-sticky': this.actionScrollBehavior === 'sticky', }, badgeIcons: true, type: 'selectable', onChange: this.handleActionListChange, items: this.actions.map(this.removeUnusedPropertiesOnAction) }), ]; } removeUnusedPropertiesOnAction(action) { return Object.assign(Object.assign({}, action), { actions: [] }); } shouldShowDropDownContent() { if (this.isFull()) { return false; } return !!this.chipSetEditMode; } getDropdownContent() { var _a; if (!this.shouldShowDropDownContent()) { return; } if (this.loading) { return this.renderSpinner(); } if (!((_a = this.items) === null || _a === void 0 ? void 0 : _a.length)) { return this.renderEmptyMessage(); } return this.renderListResult(); } /** * Returns true if the picker is "full" * The picker is considered to be full if it has a value and only one is allowed * * @returns true if the picker is full */ isFull() { return !this.multiple && !!this.value; } renderSpinner() { return (h("div", { style: { width: '100%', display: 'flex', 'align-items': 'center', 'justify-content': 'center', padding: '1rem 0', } }, h("limel-spinner", { limeBranded: false }))); } renderEmptyMessage() { if (!this.emptyResultMessage) { return; } const style = { color: 'rgb(var(--contrast-1100))', 'text-align': 'center', margin: '0.5rem 1rem', }; return h("p", { style: style }, this.emptyResultMessage); } renderListResult() { return (h("limel-list", { badgeIcons: this.badgeIcons, onChange: this.handleListChange, onKeyDown: this.onListKeyDown, type: "selectable", items: this.items })); } onListKeyDown(event) { const keyFound = [TAB, ESCAPE, ENTER].includes(event.key); if (keyFound) { this.chipSet.setFocus(); } } renderPortal(content = []) { const dropdownZIndex = getComputedStyle(this.host).getPropertyValue('--dropdown-z-index'); return (h("limel-portal", { visible: content.length > 0, containerId: this.portalId, inheritParentWidth: true, containerStyle: { 'z-index': dropdownZIndex } }, h("limel-menu-surface", { open: content.length > 0, allowClicksElement: this.host, style: { '--menu-surface-width': '100%', 'max-height': 'inherit', display: 'flex', }, onDismiss: this.handleCloseMenu }, content))); } /** * Check if a descendant still has focus. If not, reset text value and search result. */ handleStopEditAndBlur() { // In browsers where shadow DOM is not supported activeElement on shadowRoot will return null // However, document.activeElement will return the actual focused element instead of the outermost shadow host const element = this.host.shadowRoot.activeElement || document.activeElement; const portalElement = document.querySelector(`#${this.portalId}`); if (isDescendant(element, this.host) || isDescendant(element, portalElement)) { return; } this.clearInputField(); } /** * Input handler for the input field * * @param event - event */ async handleTextInput(event) { event.stopPropagation(); const query = event.detail; this.textValue = query; this.debouncedSearch(query); // If the search-query is an empty string, bypass debouncing. if (query === '') { this.debouncedSearch.flush(); } } /** * Change handler for the list * * @param event - event */ handleListChange(event) { var _a; event.stopPropagation(); if (!this.value || this.value !== event.detail) { let newValue = event.detail; if (this.multiple) { newValue = [ ...this.value, event.detail, ]; } this.change.emit(newValue); this.items = []; } if (this.multiple) { this.textValue = ''; (_a = this.chipSet) === null || _a === void 0 ? void 0 : _a.setFocus(true); } } /** * Change handler for the list * * @param event - event */ handleActionListChange(event) { event.stopPropagation(); if (!event.detail) { return; } this.action.emit(event.detail.value); this.items = []; } /** * Focus handler for the chip set * Prevent focus if the picker has a value and does not support multiple values */ handleInputFieldFocus() { const query = this.textValue; this.debouncedSearch(query); } handleChange(event) { event.stopPropagation(); let newValue = null; if (this.multiple) { const chips = event.detail; newValue = chips.map((chip) => { return this.value.find((item) => { const valueId = this.getValueId(item); return `${valueId}` === chip.id; }); }); } this.change.emit(newValue); } handleInteract(event) { event.stopPropagation(); this.interact.emit(event.detail ? event.detail.value : event.detail); } /** * Key handler for the input field * Will change focus to the first/last item in the dropdown list to enable selection with the keyboard * * @param event - event */ handleInputKeyDown(event) { const isForwardTab = event.key === TAB && !event.altKey && !event.metaKey && !event.shiftKey; const isUp = event.key === ARROW_UP; const isDown = event.key === ARROW_DOWN; if (!isForwardTab && !isUp && !isDown) { return; } const list = document.querySelector(` #${this.portalId} limel-list`); if (!list) { return; } event.preventDefault(); if (isForwardTab || isDown) { const listElement = list.shadowRoot.querySelector('.mdc-deprecated-list-item:first-child'); listElement.focus(); return; } if (isUp) { const listElement = list.shadowRoot.querySelector('.mdc-deprecated-list-item:last-child'); listElement.focus(); } } /** * Key handler for the dropdown * * @param event - event */ handleDropdownKeyDown(event) { const isEscape = event.key === ESCAPE; if (isEscape) { event.preventDefault(); this.textValue = ''; this.chipSet.setFocus(true); } } handleSearchResult(query, result) { if (query === this.textValue) { this.items = result; if (this.multiple) { const values = this.value; this.items = result.filter((item) => { return !values.includes(item); }); } this.loading = false; } } handleCloseMenu() { if (this.items.length > 0) { return; } this.clearInputField(); } clearInputField() { this.chipSet.emptyInput(); this.textValue = ''; this.handleSearchResult('', []); this.debouncedSearch.cancel(); } get host() { return getElement(this); } static get watchers() { return { "value": ["onChangeValue"] }; } }; Picker.style = pickerCss; export { Picker as limel_picker }; //# sourceMappingURL=limel-picker.entry.js.map