@internetarchive/collection-browser 3.4.1-alpha-webdev7761.0 → 3.4.1-alpha-webdev7761.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ia-combo-box.js","sourceRoot":"","sources":["../../../src/combo-box/ia-combo-box.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n html,\r\n LitElement,\r\n nothing,\r\n TemplateResult,\r\n CSSResultGroup,\r\n css,\r\n PropertyValues,\r\n} from 'lit';\r\nimport { customElement, property, state, query } from 'lit/decorators.js';\r\nimport { classMap } from 'lit/directives/class-map.js';\r\nimport { ifDefined } from 'lit/directives/if-defined.js';\r\nimport { live } from 'lit/directives/live.js';\r\nimport { when } from 'lit/directives/when.js';\r\nimport { msg } from '@lit/localize';\r\n\r\nimport {\r\n hasAnyOf,\r\n type IAComboBoxBehavior,\r\n type IAComboBoxFilterFunction,\r\n type IAComboBoxFilterOption,\r\n type IAComboBoxFilterPreset,\r\n type IAComboBoxOption,\r\n} from './models';\r\n\r\nimport caretClosed from './caret-closed';\r\nimport caretOpen from './caret-open';\r\n\r\n/**\r\n * Tests whether the given `haystack` string has the given `needle` as a subsequence.\r\n * Returns `true` if the characters of `needle` appear in order within `haystack`,\r\n * regardless of whether they are contiguous. Returns `false` otherwise.\r\n *\r\n * E.g., `ace` is a subsequence of `archive` (but not a contiguous substring).\r\n *\r\n * Note: The empty string is a subsequence of any string, including itself.\r\n *\r\n * @param needle The potential subsequence to check for inside `haystack`.\r\n * @param haystack The string to be tested for containing the `needle` subsequence.\r\n * @returns Whether `haystack` has `needle` as a subsequence.\r\n */\r\nconst isSubsequence = (needle: string, haystack: string): boolean => {\r\n const needleLen = needle.length;\r\n const haystackLen = haystack.length;\r\n if (needleLen === 0) return true;\r\n\r\n let needleIdx = 0;\r\n let haystackIdx = 0;\r\n while (haystackIdx < haystackLen) {\r\n if (haystack[haystackIdx] === needle[needleIdx]) needleIdx += 1;\r\n if (needleIdx >= needleLen) return true;\r\n haystackIdx += 1;\r\n }\r\n return false;\r\n};\r\n\r\n/**\r\n * Map from filter preset keys to their associated filtering function.\r\n * @see {@linkcode IAComboBox.filter}\r\n */\r\nconst FILTER_PRESETS: Record<IAComboBoxFilterPreset, IAComboBoxFilterFunction> =\r\n {\r\n all: () => true,\r\n prefix: (filterText, optionText) => optionText.startsWith(filterText),\r\n suffix: (filterText, optionText) => optionText.endsWith(filterText),\r\n substring: (filterText, optionText) => optionText.includes(filterText),\r\n subsequence: isSubsequence,\r\n };\r\n\r\nconst DEFAULT_BEHAVIOR: IAComboBoxBehavior = 'list';\r\nconst DEFAULT_FILTER_PRESET: IAComboBoxFilterPreset = 'substring';\r\n\r\nconst STRING_IDENTITY_FN = (str: string): string => str;\r\nconst STRING_LOWER_CASE_FN = (str: string): string => str.toLocaleLowerCase();\r\n\r\n/**\r\n * A flexible component combining the features of a dropdown menu and a text input,\r\n * allowing users to either select from a predefined list of options or type\r\n * freeform text to filter down & find specific options.\r\n */\r\n@customElement('ia-combo-box')\r\nexport class IAComboBox extends LitElement {\r\n /**\r\n * Array of options representing values that this combo box can take.\r\n *\r\n * If this combo box's `behavior` property is `select-only` or `list`, these options\r\n * represent _all_ of the allowed values that the user may choose from.\r\n *\r\n * If this combo box's `behavior` property is `freeform`, this array simply represents\r\n * a non-exhaustive list of _suggested_ values, which the user may either choose from\r\n * or enter their own value.\r\n * @see {@linkcode IAComboBox.behavior}\r\n */\r\n @property({ type: Array }) options: IAComboBoxOption[] = [];\r\n\r\n /**\r\n * Optional placeholder text to display in the combo box when no option is selected.\r\n */\r\n @property({ type: String }) placeholder?: string;\r\n\r\n /**\r\n * What style of behavior to use for the combo box.\r\n *\r\n * Currently, the options are\r\n * - `select-only` (behaving similar to a `<select>`, disallowing text entry and only\r\n * allowing selection from the predefined, unfiltered options)\r\n * - `list` (the default, allowing text entry to filter the dropdown list, but still\r\n * requiring values to be set from the predefined options)\r\n * - `freeform` (allows text entry to filter the dropdown list, and also allows setting\r\n * custom values not included in the list)\r\n */\r\n @property({ type: String }) behavior: IAComboBoxBehavior = DEFAULT_BEHAVIOR;\r\n\r\n /**\r\n * Maximum number of options to include in the autocomplete menu when filtering.\r\n *\r\n * Default value is `Infinity`, presenting all matching options regardless of count.\r\n */\r\n @property({ type: Number, attribute: 'max-autocomplete-entries' })\r\n maxAutocompleteEntries = Number.POSITIVE_INFINITY;\r\n\r\n /**\r\n * Specifies how the options should be filtered when text is entered into the combo box.\r\n * Has no effect if `behavior` is `select-only`, as the component is not text-editable.\r\n *\r\n * Default is `substring`, showing only options whose `text` contains the value entered\r\n * as a substring at any position. The possible preset options are:\r\n * - `all`: Does not filter the dropdown entries. They will always all be shown (up to\r\n * the limit specified by `maxAutocompleteEntries`).\r\n * - `prefix`: Only includes options whose text _starts_ with the entered value.\r\n * - `suffix`: Only includes options whose text _ends_ with the entered value.\r\n * - `substring`: Only includes options whose text contains the entered value as a\r\n * contiguous substring.\r\n * - `subsequence`: Only shows options whose text has the characters of the entered\r\n * value in order, though they do not need to be contiguous.\r\n * E.g., `ace` is a subsequence of `archive` (but not a substring).\r\n *\r\n * If custom filtering outside of these presets is desired, an arbitrary filtering function\r\n * may instead be bound to this property directly.\r\n *\r\n * @see {@link IAComboBox.caseSensitive} to enable case-sensitive filtering.\r\n */\r\n @property({ type: String }) filter: IAComboBoxFilterOption =\r\n DEFAULT_FILTER_PRESET;\r\n\r\n /**\r\n * Whether filtering should be case-sensitive. Default is `false`, performing only\r\n * case-insensitive filtering.\r\n */\r\n @property({ type: Boolean, reflect: true, attribute: 'case-sensitive' })\r\n caseSensitive = false;\r\n\r\n /**\r\n * Whether the filtered options should be listed in lexicographically-sorted order.\r\n * Default is `false`, displaying them in the same order as the provided options array.\r\n */\r\n @property({ type: Boolean, reflect: true }) sort = false;\r\n\r\n /**\r\n * Whether the combo box allows multiple options to be selected at once.\r\n * Default is `false`, allowing only a single option to be selected.\r\n */\r\n // @property({ type: Boolean, reflect: true }) multiple = false; // TODO\r\n\r\n /**\r\n * Whether pressing the Up/Down arrow keys should wrap focus back to the text input box\r\n * while focus is already on the first/last option, respectively.\r\n *\r\n * Default is `false`, doing nothing upon such key presses.\r\n */\r\n @property({ type: Boolean, reflect: true, attribute: 'wrap-arrow-keys' })\r\n wrapArrowKeys = false;\r\n\r\n /**\r\n * Whether the options list should remain open after an option is selected.\r\n *\r\n * Default is `false`, closing the options list when a selection is made.\r\n */\r\n @property({ type: Boolean, reflect: true, attribute: 'stay-open' })\r\n stayOpen = false;\r\n\r\n /**\r\n * Whether the combo box's option menu is currently expanded. Default is `false`.\r\n */\r\n @property({ type: Boolean, reflect: true }) open = false;\r\n\r\n /**\r\n * Whether the combo box should be rendered in its disabled state, preventing all\r\n * interactions such as editing the text field or opening the options menu.\r\n * Default is `false`.\r\n */\r\n @property({ type: Boolean, reflect: true }) disabled = false;\r\n\r\n /**\r\n * If used within a form, whether this combo box is required to have a value selected\r\n * before the form may be submitted. Default is `false`.\r\n */\r\n @property({ type: Boolean, reflect: true }) required = false;\r\n\r\n /**\r\n * For `select-only` or `list` behavior, this value is the ID of the selected option,\r\n * or `null` if there is no selection.\r\n *\r\n * For `freeform` behavior, this may be any string entered into the text field, and\r\n * need not correspond to an option ID.\r\n */\r\n @property({ type: String }) value: string | null = null;\r\n\r\n /**\r\n * Whether any part of this component currently has focus.\r\n */\r\n @state() private hasFocus = false;\r\n\r\n /**\r\n * Which option in the dropdown menu is currently highlighted by the user. Not to be\r\n * confused with the _selected_ option that has been committed, this field only\r\n * represents the user's current navigation state within the menu, which they may\r\n * subsequently select by, e.g., hitting Enter.\r\n */\r\n @state() private highlightedOption: IAComboBoxOption | null = null;\r\n\r\n /**\r\n * The text than has been entered into the text input box.\r\n */\r\n @state() private enteredText: string = '';\r\n\r\n /**\r\n * The text that we are currently using the filter the dropdown menu options.\r\n */\r\n @state() private filterText: string = '';\r\n\r\n @query('#main-widget-row') private mainWidgetRow!: HTMLDivElement;\r\n\r\n @query('#text-input') private textInput!: HTMLInputElement;\r\n\r\n @query('#caret-button') private caretButton!: HTMLInputElement;\r\n\r\n @query('#options-list') private optionsList!: HTMLUListElement;\r\n\r\n static formAssociated = true;\r\n\r\n private internals: ElementInternals;\r\n\r\n /**\r\n * Set when part of the component blurs, and cleared when any part of it is focused.\r\n * If still set on the next tick after a blur event, this serves as a signal that focus\r\n * has moved away from the component as a whole and should be closed.\r\n */\r\n private losingFocus = false;\r\n\r\n /**\r\n * A cache of the mapping from option IDs to their corresponding options, so that\r\n * we can more efficiently look up options by ID.\r\n */\r\n private optionsByID: Map<string, IAComboBoxOption> = new Map();\r\n\r\n /**\r\n * A cache of the values against which each option should be filtered, to minimize\r\n * the work needed at filter time to handle case-insensitivity etc.\r\n */\r\n private optionFilteringValues: Map<IAComboBoxOption, string> = new Map();\r\n\r\n /**\r\n * A cache of the current set of options that is pre-sorted if the component's\r\n * `sort` flag is set, or as provided otherwise. Just ensures we don't have to\r\n * sort all the options again every time we filter them.\r\n */\r\n private optionsRespectingSortFlag: IAComboBoxOption[] = [];\r\n\r\n /**\r\n * A cache of the current set of filtered options, so that we don't have to\r\n * recalculate it unnecessarily whenever the component is opened/closed/etc.\r\n */\r\n private filteredOptions: IAComboBoxOption[] = [];\r\n\r\n constructor() {\r\n super();\r\n this.internals = this.attachInternals();\r\n }\r\n\r\n render(): TemplateResult | typeof nothing {\r\n return html`\r\n <div\r\n id=\"container\"\r\n class=${classMap({ focused: this.hasFocus })}\r\n part=\"container\"\r\n >\r\n ${this.labelTemplate}\r\n <div\r\n id=\"main-widget-row\"\r\n class=${classMap({ disabled: this.disabled })}\r\n part=\"combo-box\"\r\n >\r\n ${this.textInputTemplate} ${this.caretButtonTemplate}\r\n </div>\r\n ${this.optionsListTemplate}\r\n </div>\r\n `;\r\n }\r\n\r\n willUpdate(changed: PropertyValues): void {\r\n if (changed.has('options') || changed.has('caseSensitive')) {\r\n // Need to update the cached values against which our filters are matched\r\n this.rebuildOptionFilteringValues();\r\n }\r\n\r\n if (changed.has('options')) {\r\n // Need to update the cached mapping of IDs to options\r\n this.rebuildOptionIDMap();\r\n }\r\n\r\n if (changed.has('options') || changed.has('sort')) {\r\n // Sort the options upfront if needed\r\n this.rebuildSortedOptions();\r\n }\r\n\r\n if (\r\n hasAnyOf(changed, [\r\n 'options',\r\n 'behavior',\r\n 'maxAutocompleteEntries',\r\n 'filter',\r\n 'filterText',\r\n 'caseSensitive',\r\n 'sort',\r\n ])\r\n ) {\r\n // If anything about the options or how they are filtered has changed, we need to\r\n // update our cache of the filtered options\r\n this.rebuildFilteredOptions();\r\n }\r\n\r\n if (changed.has('value')) {\r\n this.handleValueChanged();\r\n }\r\n\r\n if (changed.has('open')) {\r\n if (this.open) {\r\n // Highlight selection on open, if possible\r\n if (this.value) this.setHighlightedOption(this.selectedOption);\r\n } else {\r\n // Clear highlight on close\r\n this.setHighlightedOption(null);\r\n }\r\n }\r\n\r\n if (changed.has('required')) {\r\n this.updateFormValidity();\r\n }\r\n }\r\n\r\n updated(changed: PropertyValues): void {\r\n if (changed.has('open')) {\r\n if (this.open) {\r\n this.positionOptionsMenu();\r\n this.optionsList.showPopover?.();\r\n this.optionsList.classList.add('visible');\r\n } else {\r\n this.optionsList.hidePopover?.();\r\n this.optionsList.classList.remove('visible');\r\n }\r\n }\r\n }\r\n\r\n //\r\n // TEMPLATES\r\n //\r\n\r\n /**\r\n * Template for the main label for the combo box.\r\n *\r\n * Uses the contents of the default (unnamed) slot as the label text.\r\n */\r\n private get labelTemplate(): TemplateResult {\r\n return html`<label id=\"label\" for=\"text-input\"><slot></slot></label>`;\r\n }\r\n\r\n /**\r\n * Template for the text input field that users can edit to filter the available\r\n * options or (if freeform behavior) to enter a custom value.\r\n */\r\n private get textInputTemplate(): TemplateResult {\r\n const textInputClasses = classMap({\r\n editable: this.behavior !== 'select-only',\r\n });\r\n\r\n return html`\r\n <input\r\n type=\"text\"\r\n id=\"text-input\"\r\n class=${textInputClasses}\r\n .value=${live(this.enteredText)}\r\n placeholder=${ifDefined(this.placeholder)}\r\n part=\"text-input\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n aria-autocomplete=\"list\"\r\n aria-controls=\"options-list\"\r\n aria-expanded=${this.open}\r\n aria-activedescendant=${ifDefined(this.highlightedOption?.id)}\r\n ?disabled=${this.disabled}\r\n ?required=${this.required}\r\n @click=${this.handleComboBoxClick}\r\n @keydown=${this.handleComboBoxKeyDown}\r\n @input=${this.handleTextBoxInput}\r\n @focus=${this.handleFocus}\r\n @blur=${this.handleBlur}\r\n />\r\n `;\r\n }\r\n\r\n /**\r\n * Template for the caret open/closed icons to show beside the text input.\r\n * The icons are wrapped in named slots to allow consumers to override them.\r\n */\r\n private get caretTemplate(): TemplateResult {\r\n return html`\r\n <slot name=\"caret-closed\" ?hidden=${this.open}> ${caretClosed} </slot>\r\n <slot name=\"caret-open\" ?hidden=${!this.open}> ${caretOpen} </slot>\r\n `;\r\n }\r\n\r\n /**\r\n * Template for the caret button to be shown beside the text input.\r\n */\r\n private get caretButtonTemplate(): TemplateResult {\r\n return html`\r\n <button\r\n type=\"button\"\r\n id=\"caret-button\"\r\n part=\"caret-button\"\r\n tabindex=\"-1\"\r\n aria-controls=\"options-list\"\r\n aria-expanded=${this.open}\r\n ?disabled=${this.disabled}\r\n @click=${this.handleComboBoxClick}\r\n @keydown=${this.handleComboBoxKeyDown}\r\n @focus=${this.handleFocus}\r\n @blur=${this.handleBlur}\r\n >\r\n ${this.caretTemplate}\r\n </button>\r\n `;\r\n }\r\n\r\n /**\r\n * Template for the options list that is displayed when the combo box is open.\r\n */\r\n private get optionsListTemplate(): TemplateResult {\r\n return html`\r\n <ul\r\n id=\"options-list\"\r\n part=\"options-list\"\r\n role=\"listbox\"\r\n popover\r\n ?hidden=${!this.open}\r\n @focus=${this.handleFocus}\r\n @blur=${this.handleBlur}\r\n >\r\n <slot name=\"options-list-top\"></slot>\r\n ${when(this.open, () => this.optionTemplates)}\r\n <slot name=\"options-list-bottom\"></slot>\r\n </ul>\r\n `;\r\n }\r\n\r\n /**\r\n * Array of templates for all of the filtered options to be shown in the options menu.\r\n */\r\n private get optionTemplates(): TemplateResult[] {\r\n // If there are no options matching the filter, just show a message saying as much.\r\n if (this.filteredOptions.length === 0 && this.maxAutocompleteEntries > 0) {\r\n return [this.emptyOptionsTemplate];\r\n }\r\n\r\n // Otherwise build a list item for each filtered option\r\n return this.filteredOptions.map((opt) => {\r\n const optionClasses = classMap({\r\n option: true,\r\n highlight: opt === this.highlightedOption,\r\n });\r\n\r\n return html`\r\n <li\r\n id=${opt.id}\r\n class=${optionClasses}\r\n part=\"option\"\r\n tabindex=\"-1\"\r\n @pointerenter=${this.handleOptionPointerEnter}\r\n @pointermove=${this.handleOptionPointerMove}\r\n @click=${this.handleOptionClick}\r\n @focus=${this.handleFocus}\r\n @blur=${this.handleBlur}\r\n >\r\n ${opt.content ?? opt.text}\r\n </li>\r\n `;\r\n });\r\n }\r\n\r\n /**\r\n * Info message shown in the listbox when no options match the entered text.\r\n * Renders within an `empty-options` named slot so that its content can be customized.\r\n */\r\n private get emptyOptionsTemplate(): TemplateResult {\r\n return html`\r\n <li id=\"empty-options\" part=\"empty-options\">\r\n <slot name=\"empty-options\">${msg('No matching options')}</slot>\r\n </li>\r\n `;\r\n }\r\n\r\n //\r\n // EVENT HANDLERS & MUTATORS\r\n //\r\n\r\n /**\r\n * Handler for when the pointer device enters an option element in the dropdown.\r\n */\r\n private handleOptionPointerEnter(e: PointerEvent): void {\r\n this.handleOptionPointerMove(e);\r\n }\r\n\r\n /**\r\n * Handler for when the pointer device is moved within an option in the dropdown.\r\n */\r\n private handleOptionPointerMove(e: PointerEvent): void {\r\n const target = e.target as HTMLLIElement;\r\n const option = this.getOptionFor(target.id);\r\n if (option) this.setHighlightedOption(option);\r\n }\r\n\r\n /**\r\n * Handler for when the user clicks on an option in the dropdown.\r\n */\r\n private handleOptionClick(e: PointerEvent): void {\r\n const target = e.target as HTMLLIElement;\r\n const option = this.getOptionFor(target.id);\r\n if (option) {\r\n this.setSelectedOption(option.id);\r\n this.closeOptionsMenu();\r\n }\r\n }\r\n\r\n /**\r\n * Handler for `keydown` events on various special keys.\r\n */\r\n private handleComboBoxKeyDown(e: KeyboardEvent): void {\r\n switch (e.key) {\r\n case 'Enter':\r\n this.handleEnterPressed();\r\n break;\r\n case 'Escape':\r\n this.handleEscapePressed();\r\n break;\r\n case 'ArrowUp':\r\n if (e.altKey) {\r\n this.handleAltUpArrowPressed();\r\n } else {\r\n this.handleUpArrowPressed();\r\n }\r\n break;\r\n case 'ArrowDown':\r\n if (e.altKey) {\r\n this.handleAltDownArrowPressed();\r\n } else {\r\n this.handleDownArrowPressed();\r\n }\r\n break;\r\n case 'Tab':\r\n this.textInput.focus();\r\n return;\r\n default:\r\n // Do nothing and allow propagation otherwise\r\n return;\r\n }\r\n\r\n e.stopPropagation();\r\n e.preventDefault();\r\n }\r\n\r\n /**\r\n * Handler for `input` events in the text input box.\r\n */\r\n private async handleTextBoxInput(): Promise<void> {\r\n this.enteredText = this.textInput.value;\r\n this.setFilterText(this.textInput.value);\r\n this.openOptionsMenu();\r\n\r\n await this.updateComplete;\r\n this.highlightFirstOption();\r\n }\r\n\r\n /**\r\n * Handler for when the Enter key is pressed\r\n */\r\n private handleEnterPressed(): void {\r\n // Just open the options menu if it's currently closed\r\n if (!this.open) {\r\n this.openOptionsMenu();\r\n return;\r\n }\r\n\r\n if (this.highlightedOption) {\r\n // If an option is highlighted, select it\r\n this.setSelectedOption(this.highlightedOption.id);\r\n } else if (this.behavior === 'freeform') {\r\n // Otherwise, in the freeform behavior we just accept the current value regardless\r\n this.setValue(this.enteredText);\r\n }\r\n\r\n // Close the options list if needed\r\n if (!this.stayOpen) this.open = false;\r\n }\r\n\r\n /**\r\n * Handler for when the Escape key is pressed\r\n */\r\n private handleEscapePressed(): void {\r\n // Close the options menu if it's currently open\r\n if (this.open) {\r\n this.closeOptionsMenu();\r\n return;\r\n }\r\n\r\n // Otherwise, deselect any selected option & clear any filter text\r\n this.clearSelectedOption();\r\n }\r\n\r\n /**\r\n * Handler for when the Up Arrow key is pressed (without modifiers).\r\n * @see {@linkcode handleAltUpArrowPressed()} for behavior under the Alt modifier.\r\n */\r\n private handleUpArrowPressed(): void {\r\n if (!this.open) this.openOptionsMenu();\r\n this.highlightPreviousOption();\r\n }\r\n\r\n /**\r\n * Handler for when the Down Arrow key is pressed (without modifiers).\r\n * @see {@linkcode handleAltDownArrowPressed()} for behavior under the Alt modifier.\r\n */\r\n private handleDownArrowPressed(): void {\r\n if (!this.open) this.openOptionsMenu();\r\n this.highlightNextOption();\r\n }\r\n\r\n /**\r\n * Handler for when the Alt + Up Arrow key combo is pressed\r\n */\r\n private handleAltUpArrowPressed(): void {\r\n this.closeOptionsMenu();\r\n }\r\n\r\n /**\r\n * Handler for when the Alt + Down Arrow key combo is pressed\r\n */\r\n private handleAltDownArrowPressed(): void {\r\n this.openOptionsMenu();\r\n }\r\n\r\n /**\r\n * Handler for clicks on the combo box input field or caret button.\r\n */\r\n private handleComboBoxClick(): void {\r\n this.toggleOptionsMenu();\r\n }\r\n\r\n /**\r\n * Handler for when any part of the combo box receives focus.\r\n */\r\n private handleFocus(): void {\r\n if (this.behavior === 'select-only') {\r\n this.caretButton.focus();\r\n } else {\r\n this.textInput.focus();\r\n }\r\n this.hasFocus = true;\r\n this.losingFocus = false;\r\n }\r\n\r\n /**\r\n * Handler for when any part of the combo box loses focus.\r\n */\r\n private handleBlur(): void {\r\n this.hasFocus = false;\r\n this.losingFocus = true;\r\n setTimeout(() => {\r\n if (this.losingFocus && !this.shadowRoot?.activeElement) {\r\n this.losingFocus = false;\r\n this.closeOptionsMenu();\r\n if (this.behavior === 'freeform') this.setValue(this.enteredText);\r\n }\r\n }, 0);\r\n }\r\n\r\n /**\r\n * Handler for when the `value` of this component is changed externally\r\n */\r\n private handleValueChanged(): void {\r\n if (this.behavior !== 'freeform' && this.value !== null) {\r\n // The value must correspond to a valid option or null\r\n if (!this.getOptionFor(this.value)) this.clearSelectedOption();\r\n }\r\n }\r\n\r\n /**\r\n * Highlights the first option shown in the filtered list.\r\n */\r\n private highlightFirstOption(): void {\r\n this.setHighlightedOption(this.firstFilteredOption);\r\n }\r\n\r\n /**\r\n * Highlights the last option shown in the filtered list.\r\n */\r\n private highlightLastOption(): void {\r\n this.setHighlightedOption(this.lastFilteredOption);\r\n }\r\n\r\n /**\r\n * Highlights the option before the currently highlighted one in the list, or the last one\r\n * if none is highlighted.\r\n */\r\n private highlightPreviousOption(): void {\r\n const { filteredOptions, lastFilteredIndex } = this;\r\n\r\n // If no option is highlighted yet, highlight the last one\r\n if (!this.highlightedOption) {\r\n this.highlightLastOption();\r\n return;\r\n }\r\n\r\n // Otherwise, move to the previous option (wrapping if needed)\r\n const { highlightedIndex } = this;\r\n const previousIndex =\r\n this.wrapArrowKeys && highlightedIndex === 0\r\n ? lastFilteredIndex // Wrap around to the end\r\n : Math.max(highlightedIndex - 1, 0);\r\n this.setHighlightedOption(filteredOptions[previousIndex]);\r\n }\r\n\r\n /**\r\n * Highlights the option after the currently highlighted one in the list, or the first one\r\n * if none is highlighted.\r\n */\r\n private highlightNextOption(): void {\r\n const { filteredOptions, lastFilteredIndex } = this;\r\n\r\n // If no option is highlighted yet, highlight the first one\r\n if (!this.highlightedOption) {\r\n this.highlightFirstOption();\r\n return;\r\n }\r\n\r\n // Otherwise, move to the next option (wrapping if needed)\r\n const { highlightedIndex } = this;\r\n const nextIndex =\r\n this.wrapArrowKeys && highlightedIndex === lastFilteredIndex\r\n ? 0 // Wrap back to the start\r\n : Math.min(highlightedIndex + 1, lastFilteredIndex);\r\n this.setHighlightedOption(filteredOptions[nextIndex]);\r\n }\r\n\r\n /**\r\n * Highlights the given option and scrolls it into view if necessary.\r\n * If `null` is provided, any current highlight will be cleared.\r\n */\r\n private async setHighlightedOption(\r\n option: IAComboBoxOption | null,\r\n ): Promise<void> {\r\n this.highlightedOption = option;\r\n await this.updateComplete;\r\n\r\n const { optionsList, highlightedElement } = this;\r\n if (!highlightedElement) return;\r\n\r\n // TODO: Not ideal as this will trigger a reflow...\r\n // But may not be an issue in practice given the highlight isn't changing in a hot loop.\r\n // If we have issues with this let's see if we can hook up an IntersectionObserver.\r\n const elmtRect = highlightedElement.getBoundingClientRect();\r\n const listRect = optionsList.getBoundingClientRect();\r\n if (elmtRect.top < listRect.top || elmtRect.bottom > listRect.bottom) {\r\n highlightedElement.scrollIntoView({ block: 'nearest' });\r\n }\r\n }\r\n\r\n /**\r\n * Changes this combo box's selected option to the one matching the specified ID.\r\n *\r\n * Throws a `RangeError` if given an ID that does not correspond to any option\r\n * held by this combo box.\r\n */\r\n setSelectedOption(id: string): void {\r\n const option = this.getOptionFor(id);\r\n if (!option) throw new RangeError('Unknown option ID');\r\n\r\n const prevValue = this.value;\r\n this.value = option.id;\r\n this.internals.setFormValue(this.value);\r\n this.setTextValue(option.text);\r\n if (this.value !== prevValue) this.emitChangeEvent();\r\n\r\n // Invoke the option's select callback if defined\r\n option.onSelected?.(option);\r\n }\r\n\r\n /**\r\n * Clears any currently selected option from this combo box, setting it to null\r\n * and clearing any value in the text box.\r\n */\r\n clearSelectedOption(): void {\r\n const prevValue = this.value;\r\n this.value = null;\r\n this.internals.setFormValue(this.value);\r\n this.setTextValue('');\r\n if (this.value !== prevValue) this.emitChangeEvent();\r\n }\r\n\r\n /**\r\n * Set this combo box's value to the given string if possible.\r\n *\r\n * In `freeform` behavior, this always succeeds.\r\n *\r\n * In other behavior modes, this method is identical to `setSelectedOption`,\r\n * interpreting the input as an option ID and throwing an error if no such\r\n * option exists.\r\n *\r\n * @see {@linkcode IAComboBox.setSelectedOption}\r\n */\r\n setValue(value: string): void {\r\n if (this.behavior === 'freeform') {\r\n const prevValue = this.value;\r\n this.value = value;\r\n this.internals.setFormValue(this.value);\r\n this.setTextValue(value);\r\n if (this.value !== prevValue) this.emitChangeEvent();\r\n } else {\r\n this.setSelectedOption(value);\r\n }\r\n }\r\n\r\n /**\r\n * Changes the value of the text input box, and updates the filter accordingly.\r\n */\r\n private setTextValue(value: string): void {\r\n this.textInput.value = value;\r\n this.enteredText = value;\r\n this.setFilterText(value);\r\n }\r\n\r\n openOptionsMenu(): void {\r\n this.open = true;\r\n this.emitToggleEvent();\r\n }\r\n\r\n closeOptionsMenu(): void {\r\n this.open = false;\r\n this.emitToggleEvent();\r\n }\r\n\r\n toggleOptionsMenu(): void {\r\n this.open = !this.open;\r\n this.emitToggleEvent();\r\n }\r\n\r\n private updateFormValidity(): void {\r\n if (this.required && !this.value) {\r\n this.internals.setValidity(\r\n { valueMissing: true },\r\n msg('A value is required'),\r\n );\r\n } else {\r\n // All good\r\n this.internals.setValidity({});\r\n }\r\n }\r\n\r\n private emitChangeEvent(): void {\r\n this.dispatchEvent(\r\n new CustomEvent<string | null>('change', {\r\n detail: this.value,\r\n }),\r\n );\r\n }\r\n\r\n private emitToggleEvent(): void {\r\n this.dispatchEvent(\r\n new CustomEvent<boolean>('toggle', {\r\n detail: this.open,\r\n }),\r\n );\r\n }\r\n\r\n //\r\n // HELPERS\r\n //\r\n\r\n /**\r\n * Sets the size and position of the options menu to match the size and position of\r\n * the combo box widget. Prefers to position below the main widget, but will flip\r\n * to the top if needed & if there's more room above.\r\n */\r\n private positionOptionsMenu(): void {\r\n const { mainWidgetRow, optionsList } = this;\r\n const mainWidgetRect = mainWidgetRow.getBoundingClientRect();\r\n\r\n const { innerHeight, scrollX, scrollY } = window;\r\n const usableHeightAbove = mainWidgetRect.top;\r\n const usableHeightBelow = innerHeight - mainWidgetRect.bottom;\r\n\r\n // We still want to respect any CSS var specified by the consumer\r\n const maxHeightVar = 'var(--comboBoxListMaxHeight, 250px)';\r\n\r\n const optionsListStyles: Record<string, string> = {\r\n top: `${mainWidgetRect.bottom + scrollY}px`,\r\n left: `${mainWidgetRect.left + scrollX}px`,\r\n width: `var(--comboBoxListWidth, ${mainWidgetRect.width}px)`,\r\n maxHeight: `min(${usableHeightBelow}px, ${maxHeightVar})`,\r\n };\r\n\r\n Object.assign(optionsList.style, optionsListStyles);\r\n\r\n // Wait a tick for it to appear, then check if we should flip it upwards instead\r\n setTimeout(() => {\r\n const listRect = optionsList.getBoundingClientRect();\r\n const overflowingViewport = listRect.bottom >= innerHeight;\r\n const moreSpaceAbove = usableHeightAbove > usableHeightBelow;\r\n if (overflowingViewport && moreSpaceAbove) {\r\n optionsList.style.top = 'auto';\r\n optionsList.style.bottom = `${innerHeight - mainWidgetRect.top - scrollY}px`;\r\n optionsList.style.maxHeight = `min(${usableHeightAbove}px, ${maxHeightVar})`;\r\n }\r\n }, 0);\r\n }\r\n\r\n /**\r\n * A function to transform option & filter text based on the combo box's\r\n * current case sensitivity.\r\n */\r\n private get caseTransform(): (text: string) => string {\r\n return this.caseSensitive ? STRING_IDENTITY_FN : STRING_LOWER_CASE_FN;\r\n }\r\n\r\n /**\r\n * Sets the current filter text based on the provided string. The resulting filter\r\n * text might not exactly match the provided value, depending on the current case\r\n * sensitivity.\r\n */\r\n private setFilterText(baseFilterText: string): void {\r\n const { caseTransform } = this;\r\n this.filterText = caseTransform(baseFilterText);\r\n }\r\n\r\n /**\r\n * Returns the combo box option having the given ID, or null if none exists.\r\n */\r\n private getOptionFor(id: string): IAComboBoxOption | null {\r\n return this.optionsByID.get(id) ?? null;\r\n }\r\n\r\n /**\r\n * Clears any previously-cached mapping of IDs to options, and rebuilds the\r\n * map based on the current set of options.\r\n */\r\n private rebuildOptionIDMap(): void {\r\n this.optionsByID.clear();\r\n for (const option of this.options) {\r\n this.optionsByID.set(option.id, option);\r\n }\r\n }\r\n\r\n /**\r\n * Applies any required sort to the options and caches the result to be used\r\n * at filter/display time.\r\n */\r\n private rebuildSortedOptions(): void {\r\n if (this.sort) {\r\n this.optionsRespectingSortFlag = this.options.sort((a, b) => {\r\n const aValue = this.optionFilteringValues.get(a) as string;\r\n const bValue = this.optionFilteringValues.get(b) as string;\r\n return aValue.localeCompare(bValue);\r\n });\r\n } else {\r\n this.optionsRespectingSortFlag = this.options;\r\n }\r\n }\r\n\r\n /**\r\n * Clears any previously-cached option filtering values, and rebuilds the\r\n * map based on the current component properties.\r\n */\r\n private rebuildOptionFilteringValues(): void {\r\n this.optionFilteringValues.clear();\r\n\r\n const { caseTransform } = this;\r\n for (const option of this.options) {\r\n const filteringValue = caseTransform(option.text);\r\n this.optionFilteringValues.set(option, filteringValue);\r\n }\r\n }\r\n\r\n /**\r\n * Returns the filtered array of options by applying the specified filter function\r\n * with the current filter text, up to the limit specified by `maxAutocompleteEntries`.\r\n */\r\n private rebuildFilteredOptions(): void {\r\n // We don't want to filter the results in select-only mode\r\n const resolvedFilterOption =\r\n this.behavior === 'select-only' ? 'all' : this.filter;\r\n\r\n const filterFn =\r\n typeof resolvedFilterOption === 'string'\r\n ? FILTER_PRESETS[resolvedFilterOption]\r\n : resolvedFilterOption;\r\n\r\n const filtered = this.optionsRespectingSortFlag\r\n .filter((opt) => {\r\n const optionFilteringValue = this.optionFilteringValues.get(opt);\r\n if (!optionFilteringValue) return false;\r\n\r\n return filterFn(this.filterText, optionFilteringValue, opt);\r\n })\r\n .slice(0, this.maxAutocompleteEntries);\r\n\r\n this.filteredOptions = filtered;\r\n }\r\n\r\n /**\r\n * The first option appearing in the filtered list, or null if there are none.\r\n */\r\n private get firstFilteredOption(): IAComboBoxOption | null {\r\n return this.filteredOptions[0] ?? null;\r\n }\r\n\r\n /**\r\n * The last option appearing in the filtered list, or null if there are none.\r\n */\r\n private get lastFilteredOption(): IAComboBoxOption | null {\r\n return this.filteredOptions[this.lastFilteredIndex] ?? null;\r\n }\r\n\r\n /**\r\n * The index of the last filtered option, or -1 if no options match the filter.\r\n */\r\n private get lastFilteredIndex(): number {\r\n return this.filteredOptions.length - 1;\r\n }\r\n\r\n /**\r\n * The IAComboBoxOption that is currently selected, or null if none is selected.\r\n */\r\n get selectedOption(): IAComboBoxOption | null {\r\n if (this.value == null) return null;\r\n return this.getOptionFor(this.value);\r\n }\r\n\r\n /**\r\n * The index of the currently highlighted option in the filtered list, or -1 if\r\n * no option is currently highlighted.\r\n */\r\n private get highlightedIndex(): number {\r\n if (!this.highlightedOption) return -1;\r\n return this.filteredOptions.indexOf(this.highlightedOption);\r\n }\r\n\r\n /**\r\n * The HTML element representing the currently-highlighted option, or null if\r\n * no option is highlighted.\r\n */\r\n private get highlightedElement(): HTMLElement | null {\r\n if (!this.highlightedOption) return null;\r\n return this.shadowRoot!.getElementById(this.highlightedOption.id);\r\n }\r\n\r\n static get styles(): CSSResultGroup {\r\n return css`\r\n #label {\r\n display: block;\r\n width: fit-content;\r\n }\r\n\r\n #main-widget-row {\r\n display: inline-flex;\r\n align-items: stretch;\r\n flex-wrap: nowrap;\r\n background: white;\r\n border: 1px solid black;\r\n }\r\n\r\n .focused #main-widget-row {\r\n outline: black auto 1px;\r\n outline-offset: 3px;\r\n }\r\n\r\n #text-input {\r\n appearance: none;\r\n background: transparent;\r\n border: none;\r\n padding: var(--comboBoxPadding, 5px);\r\n width: 100%;\r\n font-size: inherit;\r\n outline: none;\r\n }\r\n\r\n #text-input:not(.editable) {\r\n cursor: pointer;\r\n }\r\n\r\n #caret-button {\r\n display: inline-flex;\r\n align-items: center;\r\n appearance: none;\r\n background: transparent;\r\n border: none;\r\n padding: var(--comboBoxPadding, 5px);\r\n outline: none;\r\n cursor: pointer;\r\n }\r\n\r\n #options-list {\r\n position: absolute;\r\n list-style-type: none;\r\n margin: 1px 0 0;\r\n border: none;\r\n padding: 0;\r\n background: white;\r\n max-height: 400px;\r\n box-shadow: 0 0 1px 1px #ddd;\r\n opacity: 0;\r\n transition: opacity 0.125s ease;\r\n }\r\n\r\n #options-list.visible {\r\n opacity: 1;\r\n }\r\n\r\n #empty-options {\r\n padding: 5px;\r\n color: #606060;\r\n font-style: italic;\r\n text-align: center;\r\n }\r\n\r\n .caret {\r\n width: 14px;\r\n height: 14px;\r\n }\r\n\r\n .option {\r\n padding: 5px;\r\n cursor: pointer;\r\n }\r\n\r\n .highlight {\r\n background-color: #dbe0ff;\r\n }\r\n\r\n .disabled,\r\n .disabled * {\r\n cursor: not-allowed !important;\r\n }\r\n\r\n .sr-only {\r\n position: absolute !important;\r\n width: 1px !important;\r\n height: 1px !important;\r\n margin: -1px !important;\r\n padding: 0 !important;\r\n border: 0 !important;\r\n overflow: hidden !important;\r\n white-space: nowrap !important;\r\n clip: rect(1px, 1px, 1px, 1px) !important;\r\n -webkit-clip-path: inset(50%) !important;\r\n clip-path: inset(50%) !important;\r\n user-select: none !important;\r\n }\r\n `;\r\n }\r\n}\r\n"]}
@@ -0,0 +1,61 @@
1
+ import { TemplateResult } from 'lit';
2
+ /**
3
+ * Represents a single predefined option in a combo box.
4
+ */
5
+ export interface IAComboBoxOption {
6
+ /**
7
+ * A unique ID representing this option.
8
+ *
9
+ * This value is used both as the DOM `id` for the option element, and as the
10
+ * identifier indicating which option(s) are selected.
11
+ */
12
+ id: string;
13
+ /**
14
+ * The text to use for filtering autocomplete entries in the options menu, and for
15
+ * displaying the option in the menu if no separate `content` value is provided.
16
+ */
17
+ text: string;
18
+ /**
19
+ * Optionally, the text or template to render for this option in the dropdown menu.
20
+ *
21
+ * If provided, it will be displayed in the options list instead of the option's
22
+ * `text` value (though `text` will still be used for filtering the list). For this
23
+ * reason, it is strongly encouraged to ensure that this content still prominently
24
+ * includes the `text` value both visually and in accessible labels to avoid confusion.
25
+ *
26
+ * If not provided, then `text` assumes both display and filtering roles by default.
27
+ */
28
+ content?: string | TemplateResult;
29
+ /**
30
+ * Optionally, a callback to invoke when this option is selected in the combo box menu.
31
+ */
32
+ onSelected?: (option: this) => unknown;
33
+ }
34
+ /**
35
+ * Type union of the possible preset options for combo box behavior (defining editability,
36
+ * autocomplete style, etc).
37
+ * @see {@linkcode IAComboBox.behavior}
38
+ */
39
+ export type IAComboBoxBehavior = 'select-only' | 'list' | 'freeform';
40
+ /**
41
+ * Type union of the possible preset options for filtering combo box entries.
42
+ * @see {@linkcode IAComboBox.filter}
43
+ */
44
+ export type IAComboBoxFilterPreset = 'all' | 'prefix' | 'suffix' | 'substring' | 'subsequence';
45
+ /**
46
+ * Required shape of functions used to filter combo box entries.
47
+ * @see {@linkcode IAComboBox.filter}
48
+ */
49
+ export type IAComboBoxFilterFunction = (filterText: string, optionText: string, option: IAComboBoxOption) => boolean;
50
+ /**
51
+ * The `filter` property of a combo box may be specified either as a string
52
+ * identifying a preset function, or directly as a custom filtering function.
53
+ * @see {@linkcode IAComboBox.filter}
54
+ */
55
+ export type IAComboBoxFilterOption = IAComboBoxFilterPreset | IAComboBoxFilterFunction;
56
+ /**
57
+ * Helper function to check if a Map has any of a list of keys
58
+ * @param map The map to check
59
+ * @param keys The list of keys to check
60
+ */
61
+ export declare function hasAnyOf<T>(map: Map<T, unknown>, keys: T[]): boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Helper function to check if a Map has any of a list of keys
3
+ * @param map The map to check
4
+ * @param keys The list of keys to check
5
+ */
6
+ export function hasAnyOf(map, keys) {
7
+ return keys.some((prop) => map.has(prop));
8
+ }
9
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../../src/combo-box/models.ts"],"names":[],"mappings":"AA2EA;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAI,GAAoB,EAAE,IAAS;IACzD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import { TemplateResult } from 'lit';\n\n/**\n * Represents a single predefined option in a combo box.\n */\nexport interface IAComboBoxOption {\n /**\n * A unique ID representing this option.\n *\n * This value is used both as the DOM `id` for the option element, and as the\n * identifier indicating which option(s) are selected.\n */\n id: string;\n\n /**\n * The text to use for filtering autocomplete entries in the options menu, and for\n * displaying the option in the menu if no separate `content` value is provided.\n */\n text: string;\n\n /**\n * Optionally, the text or template to render for this option in the dropdown menu.\n *\n * If provided, it will be displayed in the options list instead of the option's\n * `text` value (though `text` will still be used for filtering the list). For this\n * reason, it is strongly encouraged to ensure that this content still prominently\n * includes the `text` value both visually and in accessible labels to avoid confusion.\n *\n * If not provided, then `text` assumes both display and filtering roles by default.\n */\n content?: string | TemplateResult;\n\n /**\n * Optionally, a callback to invoke when this option is selected in the combo box menu.\n */\n onSelected?: (option: this) => unknown;\n}\n\n/**\n * Type union of the possible preset options for combo box behavior (defining editability,\n * autocomplete style, etc).\n * @see {@linkcode IAComboBox.behavior}\n */\nexport type IAComboBoxBehavior = 'select-only' | 'list' | 'freeform';\n\n/**\n * Type union of the possible preset options for filtering combo box entries.\n * @see {@linkcode IAComboBox.filter}\n */\nexport type IAComboBoxFilterPreset =\n | 'all'\n | 'prefix'\n | 'suffix'\n | 'substring'\n | 'subsequence';\n\n/**\n * Required shape of functions used to filter combo box entries.\n * @see {@linkcode IAComboBox.filter}\n */\nexport type IAComboBoxFilterFunction = (\n filterText: string,\n optionText: string,\n option: IAComboBoxOption,\n) => boolean;\n\n/**\n * The `filter` property of a combo box may be specified either as a string\n * identifying a preset function, or directly as a custom filtering function.\n * @see {@linkcode IAComboBox.filter}\n */\nexport type IAComboBoxFilterOption =\n | IAComboBoxFilterPreset\n | IAComboBoxFilterFunction;\n\n/**\n * Helper function to check if a Map has any of a list of keys\n * @param map The map to check\n * @param keys The list of keys to check\n */\nexport function hasAnyOf<T>(map: Map<T, unknown>, keys: T[]): boolean {\n return keys.some((prop) => map.has(prop));\n}\n"]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "The Internet Archive Collection Browser.",
4
4
  "license": "AGPL-3.0-only",
5
5
  "author": "Internet Archive",
6
- "version": "3.4.1-alpha-webdev7761.0",
6
+ "version": "3.4.1-alpha-webdev7761.2",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
9
9
  "scripts": {
@@ -77,6 +77,7 @@ import { log } from './utils/log';
77
77
  import type { PlaceholderType } from './empty-placeholder';
78
78
  import type { ManageBar } from './manage/manage-bar';
79
79
  import type { SmartFacetBar } from './collection-facets/smart-facets/smart-facet-bar';
80
+ import { updateSelectedFacetBucket } from './utils/facet-utils';
80
81
 
81
82
  import './empty-placeholder';
82
83
  import './tiles/tile-dispatcher';
@@ -86,10 +87,8 @@ import './manage/manage-bar';
86
87
  import './collection-facets';
87
88
  import './circular-activity-indicator';
88
89
  import './collection-facets/smart-facets/smart-facet-bar';
89
- import {
90
- mergeSelectedFacets,
91
- updateSelectedFacetBucket,
92
- } from './utils/facet-utils';
90
+ import './combo-box/ia-combo-box';
91
+ import { IAComboBox } from './combo-box/ia-combo-box';
93
92
 
94
93
  @customElement('collection-browser')
95
94
  export class CollectionBrowser
@@ -547,11 +546,11 @@ export class CollectionBrowser
547
546
  this.selectedTVShow = undefined;
548
547
 
549
548
  if (this.tvNetworksDropdown) {
550
- this.tvNetworksDropdown.selectedIndex = 0;
549
+ this.tvNetworksDropdown.clearSelectedOption();
551
550
  }
552
551
 
553
552
  if (this.tvShowsDropdown) {
554
- this.tvShowsDropdown.selectedIndex = 0;
553
+ this.tvShowsDropdown.clearSelectedOption();
555
554
  }
556
555
  }
557
556
 
@@ -1269,32 +1268,33 @@ export class CollectionBrowser
1269
1268
 
1270
1269
  @state() private selectedTVNetwork?: string = undefined;
1271
1270
  @state() private selectedTVShow?: string = undefined;
1272
- @state() private shouldPopulateShows: boolean = false;
1271
+ @state() private tvMapsPopulated: boolean = false;
1272
+ @state() private loadingNetworks: boolean = false;
1273
+ @state() private loadingShows: boolean = false;
1273
1274
 
1274
- @query('#tv-networks') private tvNetworksDropdown?: HTMLSelectElement;
1275
- @query('#tv-shows') private tvShowsDropdown?: HTMLSelectElement;
1275
+ @query('#tv-networks') private tvNetworksDropdown?: IAComboBox;
1276
+ @query('#tv-shows') private tvShowsDropdown?: IAComboBox;
1276
1277
 
1277
- private async networksDropdownClicked(): Promise<void> {
1278
- this.tvNetworksDropdown?.classList.add('loading');
1278
+ private async networksDropdownToggled(): Promise<void> {
1279
+ if (this.tvMapsPopulated) return;
1280
+ this.loadingNetworks = true;
1279
1281
  await this.dataSource.populateTVChannelMaps();
1280
- this.tvNetworksDropdown?.classList.remove('loading');
1282
+ this.loadingNetworks = false;
1283
+ this.tvMapsPopulated = true;
1281
1284
  }
1282
1285
 
1283
- private async showsDropdownClicked(): Promise<void> {
1284
- this.tvShowsDropdown?.classList.add('loading');
1286
+ private async showsDropdownToggled(): Promise<void> {
1287
+ if (this.tvMapsPopulated) return;
1288
+ this.loadingShows = true;
1285
1289
  await this.dataSource.populateTVChannelMaps();
1286
- // Delay for a single tick so that the loading indicator will be rendered
1287
- // while the dropdown options are being added
1288
- await new Promise(res => setTimeout(res, 0));
1289
- this.shouldPopulateShows = true;
1290
- await this.updateComplete;
1291
- this.tvShowsDropdown?.classList.remove('loading');
1290
+ this.loadingShows = false;
1291
+ this.tvMapsPopulated = true;
1292
1292
  }
1293
1293
 
1294
1294
  private async networksDropdownChanged(): Promise<void> {
1295
1295
  const previousNetwork = this.selectedTVNetwork;
1296
- this.selectedTVNetwork =
1297
- this.tvNetworksDropdown!.selectedOptions[0].value || undefined;
1296
+ const newNetwork = this.tvNetworksDropdown!.selectedOption?.text;
1297
+ this.selectedTVNetwork = newNetwork ?? undefined;
1298
1298
 
1299
1299
  const entries = this.dataSource.tvChannelMaps.channelToNetwork!.entries();
1300
1300
  for (const [channel, network] of entries) {
@@ -1328,8 +1328,8 @@ export class CollectionBrowser
1328
1328
 
1329
1329
  private async showsDropdownChanged(): Promise<void> {
1330
1330
  const previousShow = this.selectedTVShow;
1331
- this.selectedTVShow =
1332
- this.tvShowsDropdown!.selectedOptions[0].value || undefined;
1331
+ const newShow = this.tvShowsDropdown!.selectedOption?.text;
1332
+ this.selectedTVShow = newShow ?? undefined;
1333
1333
 
1334
1334
  // Remove any previously-applied shows filter
1335
1335
  if (previousShow !== undefined) {
@@ -1383,51 +1383,45 @@ export class CollectionBrowser
1383
1383
  const shows = showEntries.map(([show]) => show);
1384
1384
  log('end filters template preprocess', Date.now());
1385
1385
 
1386
- const makeNetworkOption = (network: string) => html`
1387
- <option ?selected=${this.selectedTVNetwork === network}>
1388
- ${network}
1389
- </option>
1390
- `;
1391
-
1392
- const makeShowOption = (show: string) => html`
1393
- <option ?selected=${this.selectedTVShow === show}>${show}</option>
1394
- `;
1395
-
1396
1386
  const loadingIndicator = html`
1397
1387
  <img src="https://archive.org/images/loading.gif" />
1398
1388
  `;
1399
1389
 
1400
1390
  return html`
1401
1391
  <div id="tv-filters" slot="facets-top">
1402
- <div class="tv-filter-dropdown-wrap">
1403
- <select
1404
- id="tv-networks"
1405
- class="tv-filter-dropdown"
1406
- @click=${this.networksDropdownClicked}
1407
- @change=${this.networksDropdownChanged}
1408
- >
1409
- <option value="" ?selected=${!this.selectedTVNetwork}>
1410
- ${msg('Filter by Network')}
1411
- </option>
1412
- ${map(networks, makeNetworkOption)}
1413
- </select>
1414
- ${loadingIndicator}
1415
- </div>
1416
-
1417
- <div class="tv-filter-dropdown-wrap">
1418
- <select
1419
- id="tv-shows"
1420
- class="tv-filter-dropdown"
1421
- @click=${this.showsDropdownClicked}
1422
- @change=${this.showsDropdownChanged}
1423
- >
1424
- <option value="" ?selected=${!this.selectedTVShow}>
1425
- ${msg('Filter by Show')}
1426
- </option>
1427
- ${this.shouldPopulateShows ? map(shows, makeShowOption) : nothing}
1428
- </select>
1429
- ${loadingIndicator}
1430
- </div>
1392
+ <ia-combo-box
1393
+ id="tv-networks"
1394
+ class="tv-filter-dropdown"
1395
+ placeholder="Filter by Network"
1396
+ wrap-arrow-keys
1397
+ sort
1398
+ .options=${networks.map((n, i) => ({ id: `network-${i}`, text: n }))}
1399
+ @toggle=${this.networksDropdownToggled}
1400
+ @change=${this.networksDropdownChanged}
1401
+ >
1402
+ <span class="sr-only">${msg('Filter by Network')}</span>
1403
+ ${this.loadingNetworks
1404
+ ? html`<span slot="empty-options">${loadingIndicator}</span>`
1405
+ : nothing
1406
+ }
1407
+ </ia-combo-box>
1408
+ <ia-combo-box
1409
+ id="tv-shows"
1410
+ class="tv-filter-dropdown"
1411
+ placeholder="Filter by Show"
1412
+ max-autocomplete-entries="500"
1413
+ wrap-arrow-keys
1414
+ sort
1415
+ .options=${shows.map((s, i) => ({ id: `show-${i}`, text: s }))}
1416
+ @toggle=${this.showsDropdownToggled}
1417
+ @change=${this.showsDropdownChanged}
1418
+ >
1419
+ <span class="sr-only">${msg('Filter by Show')}</span>
1420
+ ${this.loadingShows
1421
+ ? html`<span slot="empty-options">${loadingIndicator}</span>`
1422
+ : nothing
1423
+ }
1424
+ </ia-combo-box>
1431
1425
  </div>
1432
1426
  `;
1433
1427
  }
@@ -2738,10 +2732,10 @@ export class CollectionBrowser
2738
2732
  #facets-bottom-fade {
2739
2733
  background: linear-gradient(
2740
2734
  to bottom,
2741
- #f5f5f700 0%,
2742
- #f5f5f7c0 50%,
2743
- #f5f5f7 80%,
2744
- #f5f5f7 100%
2735
+ #fbfbfd00 0%,
2736
+ #fbfbfdc0 50%,
2737
+ #fbfbfd 80%,
2738
+ #fbfbfd 100%
2745
2739
  );
2746
2740
  position: fixed;
2747
2741
  bottom: 0;
@@ -2848,25 +2842,24 @@ export class CollectionBrowser
2848
2842
  margin-bottom: 15px;
2849
2843
  }
2850
2844
 
2851
- .tv-filter-dropdown {
2852
- width: 100%;
2853
- padding: 3px;
2845
+ #tv-shows {
2846
+ --comboBoxListWidth: 300px;
2854
2847
  }
2855
2848
 
2856
- .tv-filter-dropdown-wrap {
2857
- display: flex;
2858
- align-items: center;
2859
- column-gap: 5px;
2849
+ .tv-filter-dropdown {
2850
+ display: block;
2851
+ font-size: 14px;
2852
+ margin-left: 1px;
2860
2853
  margin-bottom: 5px;
2861
2854
  }
2862
2855
 
2863
- .tv-filter-dropdown-wrap > img {
2864
- flex: 1;
2865
- visibility: hidden;
2856
+ .tv-filter-dropdown::part(combo-box) {
2857
+ outline-offset: 1px;
2866
2858
  }
2867
2859
 
2868
- .tv-filter-dropdown.loading + img {
2869
- visibility: visible;
2860
+ .tv-filter-dropdown::part(option) {
2861
+ line-height: 1.1;
2862
+ padding: 7px;
2870
2863
  }
2871
2864
 
2872
2865
  #facets-container {
@@ -923,7 +923,7 @@ export class CollectionFacets extends LitElement {
923
923
  transform: rotate(90deg);
924
924
  }
925
925
 
926
- .facet-group:not(:last-child) {
926
+ .facet-group {
927
927
  margin-bottom: 2rem;
928
928
  }
929
929
 
@@ -0,0 +1,13 @@
1
+ import { html } from 'lit';
2
+
3
+ export default html`
4
+ <svg
5
+ class="caret caret-closed"
6
+ viewBox="0 0 8 4"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ >
9
+ <path
10
+ d="m6.7226499.58397485c.22976435-.15317623.54019902-.09108929.69337525.13867505.13615665.20423498.10222882.47220947-.06836249.63681849l-.07031256.05655676-3.2773501 2.18490006-3.2773501-2.18490006c-.22976434-.15317623-.29185128-.4636109-.13867505-.69337525.13615665-.20423497.39656688-.27598409.61412572-.18182636l.07924953.04315131 2.7226499 1.81402515z"
11
+ ></path>
12
+ </svg>
13
+ `;
@@ -0,0 +1,13 @@
1
+ import { html } from 'lit';
2
+
3
+ export default html`
4
+ <svg
5
+ class="caret caret-open"
6
+ viewBox="0 0 8 4"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ >
9
+ <path
10
+ d="m6.7226499 3.51689722c.22976435.15317623.54019902.0910893.69337525-.13867505.13615665-.20423497.10222882-.47220946-.06836249-.63681849l-.07031256-.05655675-3.2773501-2.18490007-3.2773501 2.18490007c-.22976434.15317623-.29185128.4636109-.13867505.69337524.13615665.20423498.39656688.27598409.61412572.18182636l.07924953-.04315131 2.7226499-1.81402514z"
11
+ ></path>
12
+ </svg>
13
+ `;