Archived
Template
1
Fork 0
This repository has been archived on 2024-09-04. You can view files and clone it, but cannot push or open issues or pull requests.
Contour/public/@shoelace-style/shoelace/cdn/chunks/chunk.J4OW3AZY.js

776 lines
24 KiB
JavaScript
Raw Permalink Normal View History

2024-01-30 10:59:28 -05:00
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`
<sl-tag
part="tag"
exportparts="
base:tag__base,
content:tag__content,
remove-button:tag__remove-button,
remove-button__base:tag__remove-button__base
"
?pill=${this.pill}
size=${this.size}
removable
@sl-remove=${(event) => this.handleTagRemove(event, option)}
>
${option.getTextLabel()}
</sl-tag>
`;
};
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 <sl-option> elements
getAllOptions() {
return [...this.querySelectorAll("sl-option")];
}
// Gets the first <sl-option> 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`<div @sl-remove=${(e3) => this.handleTagRemove(e3, option)}>
${typeof tag === "string" ? o(tag) : tag}
</div>`;
} else if (index === this.maxOptionsVisible) {
return x`<sl-tag>+${this.selectedOptions.length - index}</sl-tag>`;
}
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`
<div
part="form-control"
class=${e({
"form-control": true,
"form-control--small": this.size === "small",
"form-control--medium": this.size === "medium",
"form-control--large": this.size === "large",
"form-control--has-label": hasLabel,
"form-control--has-help-text": hasHelpText
})}
>
<label
id="label"
part="form-control-label"
class="form-control__label"
aria-hidden=${hasLabel ? "false" : "true"}
@click=${this.handleLabelClick}
>
<slot name="label">${this.label}</slot>
</label>
<div part="form-control-input" class="form-control-input">
<sl-popup
class=${e({
select: true,
"select--standard": true,
"select--filled": this.filled,
"select--pill": this.pill,
"select--open": this.open,
"select--disabled": this.disabled,
"select--multiple": this.multiple,
"select--focused": this.hasFocus,
"select--placeholder-visible": isPlaceholderVisible,
"select--top": this.placement === "top",
"select--bottom": this.placement === "bottom",
"select--small": this.size === "small",
"select--medium": this.size === "medium",
"select--large": this.size === "large"
})}
placement=${this.placement}
strategy=${this.hoist ? "fixed" : "absolute"}
flip
shift
sync="width"
auto-size="vertical"
auto-size-padding="10"
>
<div
part="combobox"
class="select__combobox"
slot="anchor"
@keydown=${this.handleComboboxKeyDown}
@mousedown=${this.handleComboboxMouseDown}
>
<slot part="prefix" name="prefix" class="select__prefix"></slot>
<input
part="display-input"
class="select__display-input"
type="text"
placeholder=${this.placeholder}
.disabled=${this.disabled}
.value=${this.displayLabel}
autocomplete="off"
spellcheck="false"
autocapitalize="off"
readonly
aria-controls="listbox"
aria-expanded=${this.open ? "true" : "false"}
aria-haspopup="listbox"
aria-labelledby="label"
aria-disabled=${this.disabled ? "true" : "false"}
aria-describedby="help-text"
role="combobox"
tabindex="0"
@focus=${this.handleFocus}
@blur=${this.handleBlur}
/>
${this.multiple ? x`<div part="tags" class="select__tags">${this.tags}</div>` : ""}
<input
class="select__value-input"
type="text"
?disabled=${this.disabled}
?required=${this.required}
.value=${Array.isArray(this.value) ? this.value.join(", ") : this.value}
tabindex="-1"
aria-hidden="true"
@focus=${() => this.focus()}
@invalid=${this.handleInvalid}
/>
${hasClearIcon ? x`
<button
part="clear-button"
class="select__clear"
type="button"
aria-label=${this.localize.term("clearEntry")}
@mousedown=${this.handleClearMouseDown}
@click=${this.handleClearClick}
tabindex="-1"
>
<slot name="clear-icon">
<sl-icon name="x-circle-fill" library="system"></sl-icon>
</slot>
</button>
` : ""}
<slot name="expand-icon" part="expand-icon" class="select__expand-icon">
<sl-icon library="system" name="chevron-down"></sl-icon>
</slot>
</div>
<div
id="listbox"
role="listbox"
aria-expanded=${this.open ? "true" : "false"}
aria-multiselectable=${this.multiple ? "true" : "false"}
aria-labelledby="label"
part="listbox"
class="select__listbox"
tabindex="-1"
@mouseup=${this.handleOptionClick}
@slotchange=${this.handleDefaultSlotChange}
>
<slot></slot>
</div>
</sl-popup>
</div>
<div
part="form-control-help-text"
id="help-text"
class="form-control__help-text"
aria-hidden=${hasHelpText ? "false" : "true"}
>
<slot name="help-text">${this.helpText}</slot>
</div>
</div>
`;
}
};
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
};