@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
121 lines (120 loc) • 15.2 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.0/LICENSE.txt */
import { c as customElement } from "../../chunks/runtime.js";
import { css, html } from "lit";
import { keyed } from "lit/directives/keyed.js";
import { createRef, ref } from "lit/directives/ref.js";
import { LitElement, safeClassMap, nothing } from "@arcgis/lumina";
import { g as guid } from "../../chunks/guid.js";
import { c as createObserver } from "../../chunks/observers.js";
import { g as getIconScale } from "../../chunks/component.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { u as useSetFocus } from "../../chunks/useSetFocus.js";
import { f as findAssociatedForm, s as submitForm, r as resetForm } from "../../chunks/form.js";
import { u as useInteractive } from "../../chunks/useInteractive.js";
import { I as IDS, C as CSS } from "../../chunks/resources.js";
const styles = css`:host{box-sizing:border-box;background-color:var(--calcite-color-foreground-1);color:var(--calcite-color-text-2);font-size:var(--calcite-font-size--1)}:host *{box-sizing:border-box}: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([scale=s]){--calcite-internal-action-font-size: var(--calcite-font-size--2);--calcite-internal-action-height: var(--calcite-size-sm);--calcite-internal-action-line-height: 1rem;--calcite-internal-action-spacing: var(--calcite-spacing-xxs)}:host([scale=m]){--calcite-internal-action-font-size: var(--calcite-font-size--1);--calcite-internal-action-height: var(--calcite-size-md);--calcite-internal-action-line-height: 1rem;--calcite-internal-action-spacing: var(--calcite-spacing-sm)}:host([scale=l]){--calcite-internal-action-font-size: var(--calcite-font-size-0);--calcite-internal-action-height: var(--calcite-size-lg);--calcite-internal-action-line-height: 1.25rem;--calcite-internal-action-spacing: var(--calcite-spacing-sm-plus)}:host{display:flex;cursor:pointer;background-color:transparent;--calcite-internal-action-text-color: var(--calcite-color-text-3);border-radius:var(--calcite-action-corner-radius, var(--calcite-action-corner-radius-start-start, var(--calcite-corner-radius-xs)) var(--calcite-action-corner-radius-start-end, var(--calcite-corner-radius-xs)) var(--calcite-action-corner-radius-end-end, var(--calcite-corner-radius-xs)) var(--calcite-action-corner-radius-end-start, var(--calcite-corner-radius-xs)))}.interaction-container{border-radius:inherit}:host([width=full]){flex:1 0 auto}:host([width=full]) .button{justify-content:center}:host([width=full]) .button .text-container--visible{flex:none}:host([drag-handle]){cursor:move;--calcite-internal-action-text-color: var(--calcite-color-border-input);--calcite-internal-action-padding-inline: var(--calcite-spacing-xxs)}.button{position:relative;margin:0;display:flex;inline-size:auto;align-items:center;justify-content:flex-start;border-style:none;outline-color:transparent;background-color:var(--calcite-action-background-color, var(--calcite-color-foreground-1));border-radius:inherit;color:var(--calcite-action-text-color, var(--calcite-internal-action-text-color));cursor:inherit;flex:1 0 auto;font-family:inherit;font-size:var(--calcite-internal-action-font-size);font-weight:var(--calcite-font-weight-normal);line-height:var(--calcite-internal-action-line-height);min-block-size:var(--calcite-internal-action-height);padding-block:var(--calcite-internal-action-padding-block, var(--calcite-internal-action-spacing));padding-inline:var(--calcite-internal-action-padding-inline, var(--calcite-internal-action-spacing));text-align:start}.button:hover{background-color:var(--calcite-action-background-color-hover, var(--calcite-color-foreground-2));color:var(--calcite-action-text-color-press, var(--calcite-action-text-color-pressed, var(--calcite-color-text-1)))}.button:focus{outline:var(--calcite-border-width-md) solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(calc(-1 * var(--calcite-spacing-base)) * calc(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}.button:active{background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-foreground-3)))}.button--text-visible{gap:var(--calcite-internal-action-spacing);inline-size:100%}.icon-container{pointer-events:none;margin:0;display:flex;align-items:center;justify-content:center}.text-container{margin:0;inline-size:0px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:0;transition-property:opacity;transition-duration:var(--calcite-animation-timing);transition-timing-function:cubic-bezier(.4,0,.2,1);transition-property:inline-size}.text-container--visible{inline-size:auto;flex:1 1 auto;opacity:1}:host([active]) .button{background-color:var(--calcite-action-background-color, var(--calcite-color-foreground-3));color:var(--calcite-action-text-color-press, var(--calcite-action-text-color-pressed, var(--calcite-color-text-1)))}:host([active]) .button:hover{background-color:var(--calcite-action-background-color-hover, var(--calcite-color-foreground-3))}:host([active]) .button:active{background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-foreground-3)))}:host([loading]) .button:hover,:host([loading]) .button:focus{background-color:var(--calcite-action-background-color, var(--calcite-color-foreground-1))}:host([loading]) calcite-loader[inline]{margin-inline-end:0px}:host([appearance=transparent]):host([active]) .button{background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-transparent-press)))}:host([appearance=transparent]) .button{transition-property:box-shadow;transition-duration:var(--calcite-animation-timing);transition-timing-function:cubic-bezier(.4,0,.2,1);background-color:var(--calcite-action-background-color, var(--calcite-color-transparent))}:host([appearance=transparent]) .button:hover{background-color:var(--calcite-action-background-color-hover, var(--calcite-color-transparent-hover))}:host([appearance=transparent]) .button:active{background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-transparent-press)))}:host([selection-appearance=highlight]):host([active]) .button{background-color:var(--calcite-color-surface-highlight);color:var(--calcite-color-text-highlight)}:host([active-descendant]) .button{outline:var(--calcite-border-width-md) solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(calc(-1 * var(--calcite-spacing-base)) * calc(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}:host([alignment=center]) .button{justify-content:center}:host([alignment=end]) .button{justify-content:flex-end}:host([alignment=center]) .button .text-container--visible,:host([alignment=end]) .button .text-container--visible{flex:0 1 auto}:host([scale=s][compact]) .button,:host([scale=m][compact]) .button,:host([scale=l][compact]) .button{padding-inline:0px}.slot-container{display:flex}.slot-container--hidden{display:none}.indicator-with-icon{position:relative}.indicator-with-icon:after{content:"";position:absolute;block-size:.5rem;inline-size:.5rem;border-radius:9999px;inset-block-end:-.275rem;inset-inline-end:-.275rem;background-color:var(--calcite-action-indicator-color, var(--calcite-color-brand))}.indicator-without-icon{margin-inline:.25rem;inline-size:1rem;position:relative}.indicator-without-icon:after{content:"";position:absolute;block-size:.5rem;inline-size:.5rem;border-radius:9999px;inset-block-end:-.275rem;inset-inline-end:-.275rem;background-color:var(--calcite-action-indicator-color, var(--calcite-color-brand))}:host([scale=s]) .indicator-with-icon{position:relative}:host([scale=s]) .indicator-with-icon:after{content:"";position:absolute;block-size:.5rem;inline-size:.5rem;border-radius:9999px;inset-block-end:-.125rem;inset-inline-end:-.125rem;background-color:var(--calcite-action-indicator-color, var(--calcite-color-brand));block-size:.375rem;inline-size:.375rem}:host([scale=s]) .indicator-without-icon{position:relative}:host([scale=s]) .indicator-without-icon:after{content:"";position:absolute;block-size:.5rem;inline-size:.5rem;border-radius:9999px;inset-block-end:-.175rem;inset-inline-end:-.175rem;background-color:var(--calcite-action-indicator-color, var(--calcite-color-brand));block-size:.375rem;inline-size:.375rem}.indicator-text{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host([hidden]){display:none}[hidden]{display:none}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}`;
class Action extends LitElement {
constructor() {
super(...arguments);
this.guid = guid();
this.buttonRef = createRef();
this.buttonId = IDS.button(this.guid);
this.mutationObserver = createObserver("mutation", () => this.requestUpdate());
this.messages = useT9n({ blocking: true });
this.focusSetter = useSetFocus()(this);
this.indicatorRef = createRef();
this.interactiveContainer = useInteractive(this);
this.active = false;
this.activeDescendant = false;
this.appearance = "transparent";
this.compact = false;
this.disabled = false;
this.dragHandle = false;
this.iconFlipRtl = false;
this.indicator = false;
this.loading = false;
this.scale = "m";
this.width = "auto";
this.textEnabled = false;
this.type = "button";
}
static {
this.properties = { aria: [0, {}, { attribute: false }], active: [7, {}, { reflect: true, type: Boolean }], activeDescendant: [7, {}, { reflect: true, type: Boolean }], alignment: [3, {}, { reflect: true }], appearance: [3, {}, { reflect: true }], compact: [7, {}, { reflect: true, type: Boolean }], disabled: [7, {}, { reflect: true, type: Boolean }], dragHandle: [7, {}, { reflect: true, type: Boolean }], form: [3, {}, { reflect: true }], icon: [3, { type: String }, { reflect: true }], iconFlipRtl: [7, {}, { reflect: true, type: Boolean }], indicator: [7, {}, { reflect: true, type: Boolean }], label: 1, loading: [7, {}, { reflect: true, type: Boolean }], messageOverrides: [0, {}, { attribute: false }], scale: [3, {}, { reflect: true }], width: [3, {}, { reflect: true }], text: 1, textEnabled: [7, {}, { reflect: true, type: Boolean }], type: [3, {}, { reflect: true }], selectionAppearance: [3, {}, { reflect: true }] };
}
static {
this.styles = styles;
}
async setFocus(options) {
return this.focusSetter(() => this.buttonRef.value, options);
}
connectedCallback() {
super.connectedCallback();
this.formEl = findAssociatedForm(this);
this.mutationObserver?.observe(this.el, { childList: true, subtree: true });
}
disconnectedCallback() {
super.disconnectedCallback();
this.formEl = null;
this.mutationObserver?.disconnect();
}
handleClick() {
const { type } = this;
if (type === "submit") {
submitForm(this);
} else if (type === "reset") {
resetForm(this);
}
}
renderTextContainer() {
const { text, textEnabled } = this;
const textContainerClasses = {
[CSS.textContainer]: true,
[CSS.textContainerVisible]: textEnabled
};
return text ? keyed("text-container", html`<div class=${safeClassMap(textContainerClasses)}>${text}</div>`) : null;
}
renderIndicatorText() {
const { indicator, messages, buttonId } = this;
return html`<div aria-labelledby=${buttonId ?? nothing} aria-live=polite class=${safeClassMap(CSS.indicatorText)} role=region ${ref(this.indicatorRef)}>${indicator ? messages.indicator : null}</div>`;
}
renderIconContainer() {
const { loading, icon, scale, el, iconFlipRtl, indicator } = this;
const loaderScale = scale === "l" ? "l" : "m";
const calciteLoaderNode = loading ? html`<calcite-loader inline .label=${this.messages.loading} .scale=${loaderScale}></calcite-loader>` : null;
const calciteIconNode = icon ? html`<calcite-icon class=${safeClassMap({ [CSS.indicatorWithIcon]: indicator })} .flipRtl=${iconFlipRtl} .icon=${icon} .scale=${getIconScale(this.scale)}></calcite-icon>` : null;
const iconNode = calciteLoaderNode || calciteIconNode;
const hasIconToDisplay = iconNode || el.children?.length;
const slotContainerNode = html`<div class=${safeClassMap({
[CSS.slotContainer]: true,
[CSS.slotContainerHidden]: loading
})}><slot></slot></div>`;
return hasIconToDisplay ? keyed("icon-container", html`<div aria-hidden=true class=${safeClassMap(CSS.iconContainer)}>${iconNode}${slotContainerNode}</div>`) : null;
}
renderButton() {
const { compact, disabled, icon, loading, textEnabled, label, text, indicator, indicatorRef, buttonId, messages } = this;
const labelFallback = label || text || "";
const ariaLabel = indicator ? messages.indicatorLabel.replace("{label}", labelFallback) : labelFallback;
const buttonClasses = {
[CSS.button]: true,
[CSS.buttonTextVisible]: textEnabled,
[CSS.buttonCompact]: compact
};
const buttonContent = html`${this.renderIconContainer()}${this.renderTextContainer()}${!icon && indicator && keyed("indicator-no-icon", html`<div class=${safeClassMap(CSS.indicatorWithoutIcon)}></div>`) || ""}`;
const internalControlsElements = indicator && indicatorRef.value ? [indicatorRef.value] : [];
const ariaControlsElements = [
...this.aria?.controlsElements ?? [],
...internalControlsElements
];
if (this.dragHandle) {
return html`<span .ariaBusy=${loading} .ariaControlsElements=${ariaControlsElements} .ariaDescribedByElements=${this.aria?.describedByElements} .ariaExpanded=${this.aria?.expanded} .ariaHasPopup=${this.aria?.hasPopup} .ariaLabel=${ariaLabel} .ariaLabelledByElements=${this.aria?.labelledByElements} .ariaOwnsElements=${this.aria?.ownsElements} .ariaPressed=${this.aria?.pressed} class=${safeClassMap(buttonClasses)} id=${buttonId ?? nothing} role=button tabindex=${(this.disabled ? null : 0) ?? nothing} ${ref(this.buttonRef)}>${buttonContent}</span>`;
}
return html`<button .ariaBusy=${loading} .ariaChecked=${this.aria?.checked} .ariaControlsElements=${ariaControlsElements} .ariaDescribedByElements=${this.aria?.describedByElements} .ariaExpanded=${this.aria?.expanded} .ariaHasPopup=${this.aria?.hasPopup} .ariaLabel=${ariaLabel} .ariaLabelledByElements=${this.aria?.labelledByElements} .ariaOwnsElements=${this.aria?.ownsElements} .ariaPressed=${this.aria?.pressed} class=${safeClassMap(buttonClasses)} .disabled=${disabled} id=${buttonId ?? nothing} @click=${this.handleClick} .role=${this.aria?.role} ${ref(this.buttonRef)}>${buttonContent}</button>`;
}
render() {
return this.interactiveContainer({ disabled: this.disabled, children: html`${this.renderButton()}${this.renderIndicatorText()}` });
}
}
customElement("calcite-action", Action);
export {
Action
};