UNPKG

@exmg/exmg-searchbar

Version:

An element to search the needle in a haystack.

311 lines 12.5 kB
import { __decorate } from "tslib"; import { LitElement, html } from 'lit'; import { query, property, queryAll } from 'lit/decorators'; import '@material/mwc-textfield/mwc-textfield.js'; import '@polymer/paper-listbox/paper-listbox.js'; import '@polymer/paper-item/paper-item.js'; import '@polymer/paper-item/paper-icon-item.js'; import '@polymer/paper-spinner/paper-spinner-lite.js'; import styles from './styles/exmg-searchbar-styles'; /** * A search bar based on mwc-textfield element. * * __Properties__ * filterKeys: string[] = undefined => Set of which keys of `data` should be checked to filter data by default. * data: any[] = undefined => Data to be passed into exmg-searchbar. * searchQuery: String = '' => Current query of search bar. * suggestionLabelKey: String = undefined => Defines which property of each data item should be placed into suggestions' labels. * suggestions: ExmgSearchSuggestion[] = [] => List of suggestions to be passed into and displayed. * ExmgSearchSuggestion = {icon?: string, text: string, value: any} * keepSuggestionsOnSelect: Boolean = false => Option to whether keep suggestions visible or not on suggestion selection. * suggestionsLoading: Boolean = false => If true and there are no suggestions passed to element, a loading indicator should be shown. * notifyOnQueryChange: Boolean = true => If true, dispatches `query-change` event with the current query on every change of search input. * submitOnKeyPress: Boolean = true => If true, dispatches `query-submit` event with the current query upon receiving key press event with specific keys passed to `keys` string array. * submitKeys: String[] = ['ENTER'] => Determines which keys should trigger submitting search query. * keepFocus: Boolean = false => If true, keeps focus on search bar after `query-submit` is fired. * * __Methods__ * setSuggestions(suggestions: ExmgSearchSuggestion[]) => Sets `suggestions`. * clearSuggestions() => Clears suggestions. * showSuggestionsLoading() => Shows the loading indicator if there are no suggestions available. * hideSuggestionsLoading() => Hides the loading indicator. * setQuery(query: string) => Sets query. * search() => Fires `query-submit` event. * * __Events__ * @suggestion-select => Fired on suggestion selection. Payload: {value: any, index: number} * @query-change => Fired on query change. Payload: {value: string} * @query-submit => Fired on query submit. Payload: {value: string} * * __Styling__ * --exmg-searchbar-primary-color: #0071dc; => Primary theme color of search bar. * --exmg-searchbar-error-color: #b00020; => Error color of search bar if needed. * --exmg-searchbar-text-color: rgba(0, 0, 0, 0.87); => Text color of search bar. * --exmg-searchbar-hint-color: rgba(0, 0, 0, 0.6); => Hint text color of search bar. * --exmg-searchbar-width: 100%; => Width of search bar. * --exmg-searchbar-suggestions-spinner-color: #0071dc; => Suggestions loading indicator color. * --exmg-searchbar-suggestions-spinner-width: 3px; => Suggestions loading indicator spinner width. * --exmg-searchbar-suggestions-min-height: 40px; => Suggestion item row height. * --exmg-searchbar-suggestions-text-color: rgba(0, 0, 0, 0.6); => Suggestion item text color. * --exmg-searchbar-suggestions-background-color: #ffffff; => Suggestions list background color. * * __Info__ * More information about styling and the element itself can be found on the link below: * https://github.com/material-components/material-components-web-components/tree/master/packages/textfield */ const KEY_ENTER = 'ENTER'; const KEY_ARROW_UP = 'ArrowUp'; const KEY_ARROW_DOWN = 'ArrowDown'; const KEY_ESC = 'Escape'; const KEY_BACKSPACE = 'Backspace'; export class ExmgSearchBarBase extends LitElement { constructor() { super(...arguments); /** * Current query of search bar. * Default: '' */ this.searchQuery = ''; /** * Option to whether keep suggestions visible or not on * suggestion selection. * Default: false */ this.keepSuggestionsOnSelect = false; /** * If true and there are no suggestions passed to element, * a loading indicator should be shown. * Default: false */ this.suggestionsLoading = false; /** * If true, dispatches `query-change` event with the current * query on every change of search input. * Default: true */ this.notifyOnQueryChange = true; /** * If true, dispatches `query-submit` event with the current * query upon receiving key press event with specific keys * passed to `keys` string array. * Default: true */ this.submitOnKeyPress = true; /** * Determines which keys should trigger submitting * search query. * Default: ['ENTER'] */ this.submitKeys = [KEY_ENTER]; /** * If true, keeps focus on search bar after * `query-submit` is fired. * Default: false */ this.keepFocus = false; this._focusIndex = 0; } /** * Sets `suggestions` property. * @param suggestions List of ExmgSearchSuggestion[]. */ setSuggestions(suggestions) { this._focusIndex = 0; this.suggestions = suggestions; } /** * Clears suggestions. */ clearSuggestions() { this._focusIndex = 0; this.suggestions = []; } /** * Shows the loading indicator if there are * no suggestions available. */ showSuggestionsLoading() { this.suggestionsLoading = true; } /** * Hides the loading indicator. */ hideSuggestionsLoading() { this.suggestionsLoading = false; } /** * Sets query * @param _query String query */ setQuery(_query) { this.searchQuery = _query; } /** * The function which fires query-submit event. * This function exists to fire the query-submit event through not only * key press but other actions which is up to developer. */ search() { const _query = this._mwcTextField.value; this.searchQuery = _query; this.dispatchEvent(new CustomEvent('query-submit', { bubbles: true, composed: true, detail: { value: this.searchQuery } })); if (!this.keepFocus) { //Lose focus for mobile devices so that keyboard automatically gets hidden. this._mwcTextField.blur(); this._focusIndex = 0; } } firstUpdated() { //Transfer all the properties of host element to mwc-textfield. this._tryTransferProperties(); } connectedCallback() { super.connectedCallback(); this._listener = (event) => { if (event.key === KEY_ESC || event.key === KEY_ARROW_DOWN || event.key === KEY_ARROW_UP || event.key === KEY_BACKSPACE) { this._handleArrowKeyEvents(event); } this._handleKeyEvent(event); }; this.addEventListener('keydown', this._listener); } disconnectedCallback() { if (this._listener) { this.removeEventListener('keydown', this._listener); } super.disconnectedCallback(); } _handleClickSuggestion(value, index) { this._focusIndex = 0; this.dispatchEvent(new CustomEvent('suggestion-select', { bubbles: true, composed: true, detail: { index: index, value: value } })); if (!this.keepSuggestionsOnSelect) { this.clearSuggestions(); } } _handleArrowKeyEvents(event) { if (event.key === KEY_ARROW_UP && this._focusIndex > 0) { event.preventDefault(); this._focusIndex -= 1; this._focusableFields[this._focusIndex].focus(); } else if (event.key === KEY_ARROW_DOWN && this._focusIndex < this._focusableFields.length - 1) { event.preventDefault(); this._focusIndex += 1; this._focusableFields[this._focusIndex].focus(); } else if (event.key === KEY_ESC) { event.preventDefault(); this.clearSuggestions(); this.setQuery(''); this._focusIndex = 0; this._focusableFields[this._focusIndex].focus(); } else if (event.key === KEY_BACKSPACE) { if (this._focusIndex > 0) { this._focusIndex = 0; this._focusableFields[this._focusIndex].focus(); } } } _handleInputChange() { this._focusIndex = 0; if (this._mwcTextField) { const _query = this._mwcTextField.value; this.searchQuery = _query; if (this.data && this.data.length > 0 && this.filterKeys && this.filterKeys.length > 0) { this.suggestions = this.filter(this.data, this.filterKeys, this.searchQuery); } if (this.notifyOnQueryChange) { this.dispatchEvent(new CustomEvent('query-change', { bubbles: true, composed: true, detail: { value: this.searchQuery } })); } } } _handleKeyEvent(event) { if (!event || !event.key) { return; } console.log('Pressed Key!!!' + event.key); const pressedKeyCode = event.key.toUpperCase(); if (this.submitKeys.includes(pressedKeyCode) && this.submitOnKeyPress) { if (this.suggestions && this.suggestions.length === 1) { this._handleClickSuggestion(this.suggestions[0].value, 0); } else { this.clearSuggestions(); } this.search(); } } _tryTransferProperties() { if (this._mwcTextField) { const props = this.attributes; const size = props.length; for (let i = 0; i < size; i++) { const prop = props.item(i); this._mwcTextField.setAttribute(prop.name, prop.value); } } } render() { return html ` <mwc-textfield @input=${this._handleInputChange} type="search" icon="search" placeholder="Search" value="${this.searchQuery}" aria-owns="exmg-searchbar-suggestions" aria-controls="exmg-searchbar-suggestions" focusable ></mwc-textfield> <div id="exmg-searchbar-suggestions" class="exmg-searchbar-suggestions"> ${this.suggestions && this.suggestions.length > 0 ? this.renderSuggestions(this.suggestions) : this.suggestionsLoading ? this.renderSuggestionsLoading() : html ``} </div> `; } } ExmgSearchBarBase.styles = styles; __decorate([ query('mwc-textfield') ], ExmgSearchBarBase.prototype, "_mwcTextField", void 0); __decorate([ queryAll('[focusable]') ], ExmgSearchBarBase.prototype, "_focusableFields", void 0); __decorate([ property({ type: String, attribute: 'search-query' }) ], ExmgSearchBarBase.prototype, "searchQuery", void 0); __decorate([ property({ type: Array }) ], ExmgSearchBarBase.prototype, "suggestions", void 0); __decorate([ property({ type: Array }) ], ExmgSearchBarBase.prototype, "data", void 0); __decorate([ property({ type: Array }) ], ExmgSearchBarBase.prototype, "filterKeys", void 0); __decorate([ property({ type: String, attribute: 'suggestion-label-key' }) ], ExmgSearchBarBase.prototype, "suggestionLabelKey", void 0); __decorate([ property({ type: Boolean, attribute: 'keep-suggestions-on-select' }) ], ExmgSearchBarBase.prototype, "keepSuggestionsOnSelect", void 0); __decorate([ property({ type: Boolean, attribute: 'show-suggestions-loading' }) ], ExmgSearchBarBase.prototype, "suggestionsLoading", void 0); __decorate([ property({ type: Boolean, attribute: 'notify-on-query-change' }) ], ExmgSearchBarBase.prototype, "notifyOnQueryChange", void 0); __decorate([ property({ type: Boolean, attribute: 'submit-on-key' }) ], ExmgSearchBarBase.prototype, "submitOnKeyPress", void 0); __decorate([ property({ type: Array, attribute: 'submit-keys' }) ], ExmgSearchBarBase.prototype, "submitKeys", void 0); __decorate([ property({ type: Boolean, attribute: 'keep-focus' }) ], ExmgSearchBarBase.prototype, "keepFocus", void 0); //# sourceMappingURL=exmg-searchbar-base.js.map