@limetech/lime-elements
Version:
428 lines (424 loc) • 14 kB
JavaScript
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