@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
214 lines (213 loc) • 13 kB
JavaScript
/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://github.com/Esri/calcite-design-system/blob/dev/LICENSE.md for details.
v3.2.1 */
import { c as customElement } from "../../chunks/runtime.js";
import { ref } from "lit-html/directives/ref.js";
import { html } from "lit";
import { LitElement, createEvent, stringOrBoolean, safeClassMap } from "@arcgis/lumina";
import { b as focusElement } from "../../chunks/dom.js";
import { c as connectForm, a as afterConnectDefaultValueSet, d as disconnectForm, H as HiddenFormInputSlot } from "../../chunks/form.js";
import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js";
import { c as connectLabel, d as disconnectLabel, g as getLabelText } from "../../chunks/label.js";
import { c as componentFocusable, g as getIconScale } from "../../chunks/component.js";
import { c as createObserver } from "../../chunks/observers.js";
import { V as Validation } from "../../chunks/Validation.js";
import { css } from "@lit/reactive-element/css-tag.js";
const styles = css`:host{--calcite-icon-size: 1rem;--calcite-spacing-eighth: .125rem;--calcite-spacing-quarter: .25rem;--calcite-spacing-half: .5rem;--calcite-spacing-three-quarters: .75rem;--calcite-spacing: 1rem;--calcite-spacing-plus-quarter: 1.25rem;--calcite-spacing-plus-half: 1.5rem;--calcite-spacing-double: 2rem;--calcite-menu-min-width: 10rem;--calcite-header-min-height: 3rem;--calcite-footer-min-height: 3rem}:host([disabled]){cursor:default;-webkit-user-select:none;user-select:none;opacity:var(--calcite-opacity-disabled)}:host([disabled]) *,:host([disabled]) ::slotted(*){pointer-events:none}:host{display:flex;flex-direction:column;font-size:var(--calcite-select-font-size);font-weight:var(--calcite-internal-select-font-weight, var(--calcite-font-weight-regular))}.wrapper{position:relative;display:flex;align-items:stretch;inline-size:var(--select-width);block-size:var(--calcite-internal-select-block-size, 32px)}.wrapper:focus-within .icon,.wrapper:active .icon,.wrapper:hover .icon{color:var(--calcite-select-icon-color-hover, var(--calcite-color-text-1))}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}:host([scale=s]){--calcite-internal-select-font-size: var(--calcite-font-size--2);--calcite-select-spacing-inline: .5rem 2rem;--calcite-internal-select-icon-container-padding-inline: var(--calcite-spacing-sm);--calcite-internal-select-block-size: 24px}:host([scale=m]){--calcite-internal-select-font-size: var(--calcite-font-size--1);--calcite-select-spacing-inline: .75rem 2.5rem;--calcite-internal-select-icon-container-padding-inline: var(--calcite-spacing-md)}:host([scale=l]){--calcite-internal-select-font-size: var(--calcite-font-size-0);--calcite-select-spacing-inline: 1rem 3rem;--calcite-internal-select-icon-container-padding-inline: var(--calcite-spacing-lg);--calcite-internal-select-block-size: 44px}:host([width=auto]){inline-size:auto}:host([width=half]){inline-size:50%}:host([width=full]){inline-size:100%}.select{margin:0;box-sizing:border-box;inline-size:100%;cursor:pointer;appearance:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:0;border-style:solid;background-color:var(--calcite-color-foreground-1);font-family:inherit;outline-color:transparent;font-weight:inherit;font-size:var(--calcite-select-font-size, var(--calcite-internal-select-font-size));color:var(--calcite-select-text-color, var(--calcite-color-text-2));border-color:var(--calcite-select-border-color, var(--calcite-color-border-input));border-width:var(--calcite-select-internal-border-width, var(--calcite-border-width-sm));padding-inline:var(--calcite-select-spacing-inline);padding-block:var(--calcite-internal-select-spacing-block);border-inline-end-width:0;line-height:var(--calcite-internal-select-line-height, normal)}.select:focus{outline:2px solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}select:disabled{border-color:var(--calcite-color-border-input);--tw-bg-opacity: 1}.icon-container{pointer-events:none;position:absolute;inset-block:0px;display:flex;align-items:center;border-width:0px;border-style:solid;border-color:var(--calcite-color-border-input);background-color:transparent;color:var(--calcite-color-text-2);inset-inline-end:0px;border-color:var(--calcite-select-border-color, var(--calcite-color-border-input));border-inline-width:0px var(--calcite-select-internal-icon-border-inline-end-width, 1px);padding-inline:var(--calcite-internal-select-icon-container-padding-inline)}.icon-container .icon{color:var(--calcite-select-icon-color, var(--calcite-color-text-3))}:host([status=invalid]) select,:host([status=invalid]) .icon-container{border-color:var(--calcite-color-status-danger)}:host([status=invalid]) select:focus,:host([status=invalid]) .icon-container:focus{outline:2px solid var(--calcite-color-status-danger);outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}.select:focus~.icon-container{border-color:transparent}.validation-container{display:flex;flex-direction:column;align-items:flex-start;align-self:stretch}:host([scale=m]) .validation-container,:host([scale=l]) .validation-container{padding-block-start:.5rem}:host([scale=s]) .validation-container{padding-block-start:.25rem}::slotted(input[slot=hidden-form-input]){margin:0 ;opacity:0 ;outline:none ;padding:0 ;position:absolute ;inset:0 ;transform:none ;-webkit-appearance:none ;z-index:-1 }:host([hidden]){display:none}[hidden]{display:none}`;
const CSS = {
icon: "icon",
iconContainer: "icon-container",
select: "select",
wrapper: "wrapper"
};
const IDS = {
validationMessage: "selectValidationMessage"
};
function isOption(optionOrGroup) {
return optionOrGroup.tagName === "CALCITE-OPTION";
}
function isOptionGroup(optionOrGroup) {
return optionOrGroup.tagName === "CALCITE-OPTION-GROUP";
}
class Select extends LitElement {
constructor() {
super();
this.componentToNativeEl = /* @__PURE__ */ new Map();
this.mutationObserver = createObserver("mutation", () => this.populateInternalSelect());
this.disabled = false;
this.required = false;
this.scale = "m";
this.status = "idle";
this.validity = {
valid: false,
badInput: false,
customError: false,
patternMismatch: false,
rangeOverflow: false,
rangeUnderflow: false,
stepMismatch: false,
tooLong: false,
tooShort: false,
typeMismatch: false,
valueMissing: false
};
this.value = null;
this.width = "auto";
this.calciteSelectChange = createEvent({ cancelable: false });
this.listen("calciteInternalOptionChange", this.handleOptionOrGroupChange);
this.listen("calciteInternalOptionGroupChange", this.handleOptionOrGroupChange);
}
static {
this.properties = { disabled: [7, {}, { reflect: true, type: Boolean }], form: [3, {}, { reflect: true }], label: 1, name: [3, {}, { reflect: true }], required: [7, {}, { reflect: true, type: Boolean }], scale: [3, {}, { reflect: true }], selectedOption: [0, {}, { attribute: false }], status: [3, {}, { reflect: true }], validationIcon: [3, { converter: stringOrBoolean }, { reflect: true }], validationMessage: 1, validity: [0, {}, { attribute: false }], value: 1, width: [3, {}, { reflect: true }] };
}
static {
this.styles = styles;
}
async setFocus() {
await componentFocusable(this);
focusElement(this.selectEl);
}
connectedCallback() {
super.connectedCallback();
const { el } = this;
this.mutationObserver?.observe(el, {
subtree: true,
childList: true
});
connectLabel(this);
connectForm(this);
}
willUpdate(changes) {
if (changes.has("value") && (this.hasUpdated || this.value !== null)) {
this.updateItemsFromValue(this.value);
}
if (changes.has("selectedOption")) {
this.value = this.selectedOption?.value;
}
}
updated() {
updateHostInteraction(this);
}
loaded() {
if (typeof this.value === "string") {
this.updateItemsFromValue(this.value);
}
this.populateInternalSelect();
const selected = this.selectEl.selectedOptions[0];
this.selectFromNativeOption(selected);
afterConnectDefaultValueSet(this, this.selectedOption?.value ?? "");
}
disconnectedCallback() {
super.disconnectedCallback();
this.mutationObserver?.disconnect();
disconnectLabel(this);
disconnectForm(this);
}
handleInternalSelectChange() {
const selected = this.selectEl.selectedOptions[0];
this.selectFromNativeOption(selected);
requestAnimationFrame(() => this.emitChangeEvent());
}
handleOptionOrGroupChange(event) {
event.stopPropagation();
const optionOrGroup = event.target;
const nativeEl = this.componentToNativeEl.get(optionOrGroup);
if (!nativeEl) {
return;
}
this.updateNativeElement(optionOrGroup, nativeEl);
if (isOption(optionOrGroup) && optionOrGroup.selected) {
this.deselectAllExcept(optionOrGroup);
this.selectedOption = optionOrGroup;
}
}
onLabelClick() {
this.setFocus();
}
updateItemsFromValue(value) {
this.el.querySelectorAll("calcite-option").forEach((item) => item.selected = item.value === value);
}
updateNativeElement(optionOrGroup, nativeOptionOrGroup) {
nativeOptionOrGroup.disabled = optionOrGroup.disabled;
nativeOptionOrGroup.label = optionOrGroup.label;
if (isOption(optionOrGroup)) {
const option = nativeOptionOrGroup;
option.selected = optionOrGroup.selected;
option.value = optionOrGroup.value;
option.innerText = optionOrGroup.label;
}
}
populateInternalSelect() {
const optionsAndGroups = Array.from(this.el.children).filter((child) => child.tagName === "CALCITE-OPTION" || child.tagName === "CALCITE-OPTION-GROUP");
this.clearInternalSelect();
optionsAndGroups.forEach((optionOrGroup) => this.selectEl?.append(this.toNativeElement(optionOrGroup)));
}
clearInternalSelect() {
this.componentToNativeEl.forEach((value) => value.remove());
this.componentToNativeEl.clear();
}
storeSelectRef(el) {
if (!el) {
return;
}
this.selectEl = el;
}
selectFromNativeOption(nativeOption) {
if (!nativeOption) {
return;
}
let futureSelected;
this.componentToNativeEl.forEach((nativeOptionOrGroup, optionOrGroup) => {
if (isOption(optionOrGroup) && nativeOptionOrGroup === nativeOption) {
optionOrGroup.selected = true;
futureSelected = optionOrGroup;
this.deselectAllExcept(optionOrGroup);
}
});
if (futureSelected) {
this.selectedOption = futureSelected;
}
}
toNativeElement(optionOrGroup) {
if (isOption(optionOrGroup)) {
const option = document.createElement("option");
this.updateNativeElement(optionOrGroup, option);
this.componentToNativeEl.set(optionOrGroup, option);
return option;
}
if (isOptionGroup(optionOrGroup)) {
const group = document.createElement("optgroup");
this.updateNativeElement(optionOrGroup, group);
Array.from(optionOrGroup.children).forEach((option) => {
const nativeOption = this.toNativeElement(option);
group.append(nativeOption);
this.componentToNativeEl.set(optionOrGroup, nativeOption);
});
this.componentToNativeEl.set(optionOrGroup, group);
return group;
}
throw new Error("unsupported element child provided");
}
deselectAllExcept(except) {
this.el.querySelectorAll("calcite-option").forEach((option) => {
if (option === except) {
return;
}
option.selected = false;
});
}
emitChangeEvent() {
this.calciteSelectChange.emit();
}
renderChevron() {
return html`<div class=${safeClassMap(CSS.iconContainer)}><calcite-icon class=${safeClassMap(CSS.icon)} icon=chevron-down .scale=${getIconScale(this.scale)}></calcite-icon></div>`;
}
render() {
const { disabled } = this;
return InteractiveContainer({ disabled, children: html`<div class=${safeClassMap(CSS.wrapper)}><select aria-errormessage=${IDS.validationMessage} .ariaInvalid=${this.status === "invalid"} .ariaLabel=${getLabelText(this)} class=${safeClassMap(CSS.select)} .disabled=${disabled} @change=${this.handleInternalSelectChange} ${ref(this.storeSelectRef)}><slot></slot></select>${this.renderChevron()}${HiddenFormInputSlot({ component: this })}</div>${this.validationMessage && this.status === "invalid" ? Validation({ icon: this.validationIcon, id: IDS.validationMessage, message: this.validationMessage, scale: this.scale, status: this.status }) : null}` });
}
}
customElement("calcite-select", Select);
export {
Select
};