import { SlTag } from "./chunk.YPYTROFE.js"; import { select_styles_default } from "./chunk.ZKZJ333J.js"; import { o } from "./chunk.EPJHAO2T.js"; import { scrollIntoView } from "./chunk.RK73WSZS.js"; import { defaultValue } from "./chunk.RQ7JZ4R7.js"; import { SlPopup } from "./chunk.SJLA5ROP.js"; import { FormControlController } from "./chunk.NMS6LWK4.js"; import { getAnimation, setDefaultAnimation } from "./chunk.RCZVQXWP.js"; import { waitForEvent } from "./chunk.B4BZKR24.js"; import { animateTo, stopAnimations } from "./chunk.S7GYYU7Z.js"; import { LocalizeController } from "./chunk.NH3SRVOC.js"; import { HasSlotController } from "./chunk.NYIIDP5N.js"; import { e } from "./chunk.UZVKBFXH.js"; import { SlIcon } from "./chunk.UZYAV5H6.js"; import { watch } from "./chunk.FA5RT4K4.js"; import { ShoelaceElement, e as e2, n, r } from "./chunk.SEXBCYCU.js"; import { x } from "./chunk.CXZZ2LVK.js"; import { __decorateClass } from "./chunk.KIILAQWQ.js"; // src/components/select/select.component.ts var SlSelect = class extends ShoelaceElement { constructor() { super(...arguments); this.formControlController = new FormControlController(this, { assumeInteractionOn: ["sl-blur", "sl-input"] }); this.hasSlotController = new HasSlotController(this, "help-text", "label"); this.localize = new LocalizeController(this); this.typeToSelectString = ""; this.hasFocus = false; this.displayLabel = ""; this.selectedOptions = []; this.name = ""; this.value = ""; this.defaultValue = ""; this.size = "medium"; this.placeholder = ""; this.multiple = false; this.maxOptionsVisible = 3; this.disabled = false; this.clearable = false; this.open = false; this.hoist = false; this.filled = false; this.pill = false; this.label = ""; this.placement = "bottom"; this.helpText = ""; this.form = ""; this.required = false; this.getTag = (option) => { return x` this.handleTagRemove(event, option)} > ${option.getTextLabel()} `; }; this.handleDocumentFocusIn = (event) => { const path = event.composedPath(); if (this && !path.includes(this)) { this.hide(); } }; this.handleDocumentKeyDown = (event) => { const target = event.target; const isClearButton = target.closest(".select__clear") !== null; const isIconButton = target.closest("sl-icon-button") !== null; if (isClearButton || isIconButton) { return; } if (event.key === "Escape" && this.open && !this.closeWatcher) { event.preventDefault(); event.stopPropagation(); this.hide(); this.displayInput.focus({ preventScroll: true }); } if (event.key === "Enter" || event.key === " " && this.typeToSelectString === "") { event.preventDefault(); event.stopImmediatePropagation(); if (!this.open) { this.show(); return; } if (this.currentOption && !this.currentOption.disabled) { if (this.multiple) { this.toggleOptionSelection(this.currentOption); } else { this.setSelectedOptions(this.currentOption); } this.updateComplete.then(() => { this.emit("sl-input"); this.emit("sl-change"); }); if (!this.multiple) { this.hide(); this.displayInput.focus({ preventScroll: true }); } } return; } if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) { const allOptions = this.getAllOptions(); const currentIndex = allOptions.indexOf(this.currentOption); let newIndex = Math.max(0, currentIndex); event.preventDefault(); if (!this.open) { this.show(); if (this.currentOption) { return; } } if (event.key === "ArrowDown") { newIndex = currentIndex + 1; if (newIndex > allOptions.length - 1) newIndex = 0; } else if (event.key === "ArrowUp") { newIndex = currentIndex - 1; if (newIndex < 0) newIndex = allOptions.length - 1; } else if (event.key === "Home") { newIndex = 0; } else if (event.key === "End") { newIndex = allOptions.length - 1; } this.setCurrentOption(allOptions[newIndex]); } if (event.key.length === 1 || event.key === "Backspace") { const allOptions = this.getAllOptions(); if (event.metaKey || event.ctrlKey || event.altKey) { return; } if (!this.open) { if (event.key === "Backspace") { return; } this.show(); } event.stopPropagation(); event.preventDefault(); clearTimeout(this.typeToSelectTimeout); this.typeToSelectTimeout = window.setTimeout(() => this.typeToSelectString = "", 1e3); if (event.key === "Backspace") { this.typeToSelectString = this.typeToSelectString.slice(0, -1); } else { this.typeToSelectString += event.key.toLowerCase(); } for (const option of allOptions) { const label = option.getTextLabel().toLowerCase(); if (label.startsWith(this.typeToSelectString)) { this.setCurrentOption(option); break; } } } }; this.handleDocumentMouseDown = (event) => { const path = event.composedPath(); if (this && !path.includes(this)) { this.hide(); } }; } /** Gets the validity state object */ get validity() { return this.valueInput.validity; } /** Gets the validation message */ get validationMessage() { return this.valueInput.validationMessage; } connectedCallback() { super.connectedCallback(); this.open = false; } addOpenListeners() { var _a; const root = this.getRootNode(); if ("CloseWatcher" in window) { (_a = this.closeWatcher) == null ? void 0 : _a.destroy(); this.closeWatcher = new CloseWatcher(); this.closeWatcher.onclose = () => { if (this.open) { this.hide(); this.displayInput.focus({ preventScroll: true }); } }; } root.addEventListener("focusin", this.handleDocumentFocusIn); root.addEventListener("keydown", this.handleDocumentKeyDown); root.addEventListener("mousedown", this.handleDocumentMouseDown); } removeOpenListeners() { var _a; const root = this.getRootNode(); root.removeEventListener("focusin", this.handleDocumentFocusIn); root.removeEventListener("keydown", this.handleDocumentKeyDown); root.removeEventListener("mousedown", this.handleDocumentMouseDown); (_a = this.closeWatcher) == null ? void 0 : _a.destroy(); } handleFocus() { this.hasFocus = true; this.displayInput.setSelectionRange(0, 0); this.emit("sl-focus"); } handleBlur() { this.hasFocus = false; this.emit("sl-blur"); } handleLabelClick() { this.displayInput.focus(); } handleComboboxMouseDown(event) { const path = event.composedPath(); const isIconButton = path.some((el) => el instanceof Element && el.tagName.toLowerCase() === "sl-icon-button"); if (this.disabled || isIconButton) { return; } event.preventDefault(); this.displayInput.focus({ preventScroll: true }); this.open = !this.open; } handleComboboxKeyDown(event) { if (event.key === "Tab") { return; } event.stopPropagation(); this.handleDocumentKeyDown(event); } handleClearClick(event) { event.stopPropagation(); if (this.value !== "") { this.setSelectedOptions([]); this.displayInput.focus({ preventScroll: true }); this.updateComplete.then(() => { this.emit("sl-clear"); this.emit("sl-input"); this.emit("sl-change"); }); } } handleClearMouseDown(event) { event.stopPropagation(); event.preventDefault(); } handleOptionClick(event) { const target = event.target; const option = target.closest("sl-option"); const oldValue = this.value; if (option && !option.disabled) { if (this.multiple) { this.toggleOptionSelection(option); } else { this.setSelectedOptions(option); } this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true })); if (this.value !== oldValue) { this.updateComplete.then(() => { this.emit("sl-input"); this.emit("sl-change"); }); } if (!this.multiple) { this.hide(); this.displayInput.focus({ preventScroll: true }); } } } handleDefaultSlotChange() { const allOptions = this.getAllOptions(); const value = Array.isArray(this.value) ? this.value : [this.value]; const values = []; if (customElements.get("sl-option")) { allOptions.forEach((option) => values.push(option.value)); this.setSelectedOptions(allOptions.filter((el) => value.includes(el.value))); } else { customElements.whenDefined("sl-option").then(() => this.handleDefaultSlotChange()); } } handleTagRemove(event, option) { event.stopPropagation(); if (!this.disabled) { this.toggleOptionSelection(option, false); this.updateComplete.then(() => { this.emit("sl-input"); this.emit("sl-change"); }); } } // Gets an array of all elements getAllOptions() { return [...this.querySelectorAll("sl-option")]; } // Gets the first element getFirstOption() { return this.querySelector("sl-option"); } // Sets the current option, which is the option the user is currently interacting with (e.g. via keyboard). Only one // option may be "current" at a time. setCurrentOption(option) { const allOptions = this.getAllOptions(); allOptions.forEach((el) => { el.current = false; el.tabIndex = -1; }); if (option) { this.currentOption = option; option.current = true; option.tabIndex = 0; option.focus(); } } // Sets the selected option(s) setSelectedOptions(option) { const allOptions = this.getAllOptions(); const newSelectedOptions = Array.isArray(option) ? option : [option]; allOptions.forEach((el) => el.selected = false); if (newSelectedOptions.length) { newSelectedOptions.forEach((el) => el.selected = true); } this.selectionChanged(); } // Toggles an option's selected state toggleOptionSelection(option, force) { if (force === true || force === false) { option.selected = force; } else { option.selected = !option.selected; } this.selectionChanged(); } // This method must be called whenever the selection changes. It will update the selected options cache, the current // value, and the display value selectionChanged() { var _a, _b, _c, _d; this.selectedOptions = this.getAllOptions().filter((el) => el.selected); if (this.multiple) { this.value = this.selectedOptions.map((el) => el.value); if (this.placeholder && this.value.length === 0) { this.displayLabel = ""; } else { this.displayLabel = this.localize.term("numOptionsSelected", this.selectedOptions.length); } } else { this.value = (_b = (_a = this.selectedOptions[0]) == null ? void 0 : _a.value) != null ? _b : ""; this.displayLabel = (_d = (_c = this.selectedOptions[0]) == null ? void 0 : _c.getTextLabel()) != null ? _d : ""; } this.updateComplete.then(() => { this.formControlController.updateValidity(); }); } get tags() { return this.selectedOptions.map((option, index) => { if (index < this.maxOptionsVisible || this.maxOptionsVisible <= 0) { const tag = this.getTag(option, index); return x` this.handleTagRemove(e3, option)}> ${typeof tag === "string" ? o(tag) : tag} `; } else if (index === this.maxOptionsVisible) { return x`+${this.selectedOptions.length - index}`; } return x``; }); } handleInvalid(event) { this.formControlController.setValidity(false); this.formControlController.emitInvalidEvent(event); } handleDisabledChange() { if (this.disabled) { this.open = false; this.handleOpenChange(); } } handleValueChange() { const allOptions = this.getAllOptions(); const value = Array.isArray(this.value) ? this.value : [this.value]; this.setSelectedOptions(allOptions.filter((el) => value.includes(el.value))); } async handleOpenChange() { if (this.open && !this.disabled) { this.setCurrentOption(this.selectedOptions[0] || this.getFirstOption()); this.emit("sl-show"); this.addOpenListeners(); await stopAnimations(this); this.listbox.hidden = false; this.popup.active = true; requestAnimationFrame(() => { this.setCurrentOption(this.currentOption); }); const { keyframes, options } = getAnimation(this, "select.show", { dir: this.localize.dir() }); await animateTo(this.popup.popup, keyframes, options); if (this.currentOption) { scrollIntoView(this.currentOption, this.listbox, "vertical", "auto"); } this.emit("sl-after-show"); } else { this.emit("sl-hide"); this.removeOpenListeners(); await stopAnimations(this); const { keyframes, options } = getAnimation(this, "select.hide", { dir: this.localize.dir() }); await animateTo(this.popup.popup, keyframes, options); this.listbox.hidden = true; this.popup.active = false; this.emit("sl-after-hide"); } } /** Shows the listbox. */ async show() { if (this.open || this.disabled) { this.open = false; return void 0; } this.open = true; return waitForEvent(this, "sl-after-show"); } /** Hides the listbox. */ async hide() { if (!this.open || this.disabled) { this.open = false; return void 0; } this.open = false; return waitForEvent(this, "sl-after-hide"); } /** Checks for validity but does not show a validation message. Returns `true` when valid and `false` when invalid. */ checkValidity() { return this.valueInput.checkValidity(); } /** Gets the associated form, if one exists. */ getForm() { return this.formControlController.getForm(); } /** Checks for validity and shows the browser's validation message if the control is invalid. */ reportValidity() { return this.valueInput.reportValidity(); } /** Sets a custom validation message. Pass an empty string to restore validity. */ setCustomValidity(message) { this.valueInput.setCustomValidity(message); this.formControlController.updateValidity(); } /** Sets focus on the control. */ focus(options) { this.displayInput.focus(options); } /** Removes focus from the control. */ blur() { this.displayInput.blur(); } render() { const hasLabelSlot = this.hasSlotController.test("label"); const hasHelpTextSlot = this.hasSlotController.test("help-text"); const hasLabel = this.label ? true : !!hasLabelSlot; const hasHelpText = this.helpText ? true : !!hasHelpTextSlot; const hasClearIcon = this.clearable && !this.disabled && this.value.length > 0; const isPlaceholderVisible = this.placeholder && this.value.length === 0; return x` ${this.label} ${this.multiple ? x`${this.tags}` : ""} this.focus()} @invalid=${this.handleInvalid} /> ${hasClearIcon ? x` ` : ""} ${this.helpText} `; } }; SlSelect.styles = select_styles_default; SlSelect.dependencies = { "sl-icon": SlIcon, "sl-popup": SlPopup, "sl-tag": SlTag }; __decorateClass([ e2(".select") ], SlSelect.prototype, "popup", 2); __decorateClass([ e2(".select__combobox") ], SlSelect.prototype, "combobox", 2); __decorateClass([ e2(".select__display-input") ], SlSelect.prototype, "displayInput", 2); __decorateClass([ e2(".select__value-input") ], SlSelect.prototype, "valueInput", 2); __decorateClass([ e2(".select__listbox") ], SlSelect.prototype, "listbox", 2); __decorateClass([ r() ], SlSelect.prototype, "hasFocus", 2); __decorateClass([ r() ], SlSelect.prototype, "displayLabel", 2); __decorateClass([ r() ], SlSelect.prototype, "currentOption", 2); __decorateClass([ r() ], SlSelect.prototype, "selectedOptions", 2); __decorateClass([ n() ], SlSelect.prototype, "name", 2); __decorateClass([ n({ converter: { fromAttribute: (value) => value.split(" "), toAttribute: (value) => value.join(" ") } }) ], SlSelect.prototype, "value", 2); __decorateClass([ defaultValue() ], SlSelect.prototype, "defaultValue", 2); __decorateClass([ n({ reflect: true }) ], SlSelect.prototype, "size", 2); __decorateClass([ n() ], SlSelect.prototype, "placeholder", 2); __decorateClass([ n({ type: Boolean, reflect: true }) ], SlSelect.prototype, "multiple", 2); __decorateClass([ n({ attribute: "max-options-visible", type: Number }) ], SlSelect.prototype, "maxOptionsVisible", 2); __decorateClass([ n({ type: Boolean, reflect: true }) ], SlSelect.prototype, "disabled", 2); __decorateClass([ n({ type: Boolean }) ], SlSelect.prototype, "clearable", 2); __decorateClass([ n({ type: Boolean, reflect: true }) ], SlSelect.prototype, "open", 2); __decorateClass([ n({ type: Boolean }) ], SlSelect.prototype, "hoist", 2); __decorateClass([ n({ type: Boolean, reflect: true }) ], SlSelect.prototype, "filled", 2); __decorateClass([ n({ type: Boolean, reflect: true }) ], SlSelect.prototype, "pill", 2); __decorateClass([ n() ], SlSelect.prototype, "label", 2); __decorateClass([ n({ reflect: true }) ], SlSelect.prototype, "placement", 2); __decorateClass([ n({ attribute: "help-text" }) ], SlSelect.prototype, "helpText", 2); __decorateClass([ n({ reflect: true }) ], SlSelect.prototype, "form", 2); __decorateClass([ n({ type: Boolean, reflect: true }) ], SlSelect.prototype, "required", 2); __decorateClass([ n() ], SlSelect.prototype, "getTag", 2); __decorateClass([ watch("disabled", { waitUntilFirstUpdate: true }) ], SlSelect.prototype, "handleDisabledChange", 1); __decorateClass([ watch("value", { waitUntilFirstUpdate: true }) ], SlSelect.prototype, "handleValueChange", 1); __decorateClass([ watch("open", { waitUntilFirstUpdate: true }) ], SlSelect.prototype, "handleOpenChange", 1); setDefaultAnimation("select.show", { keyframes: [ { opacity: 0, scale: 0.9 }, { opacity: 1, scale: 1 } ], options: { duration: 100, easing: "ease" } }); setDefaultAnimation("select.hide", { keyframes: [ { opacity: 1, scale: 1 }, { opacity: 0, scale: 0.9 } ], options: { duration: 100, easing: "ease" } }); export { SlSelect };