@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
130 lines (129 loc) • 14 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 { html } from "lit-html";
import { keyed } from "lit-html/directives/keyed.js";
import { createRef, ref } from "lit-html/directives/ref.js";
import { LitElement, safeClassMap, nothing } from "@arcgis/lumina";
import { g as guid } from "../../chunks/guid.js";
import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js";
import { c as componentFocusable, g as getIconScale } from "../../chunks/component.js";
import { c as createObserver } from "../../chunks/observers.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { css } from "@lit/reactive-element/css-tag.js";
const CSS = {
button: "button",
buttonTextVisible: "button--text-visible",
buttonCompact: "button--compact",
indicatorText: "indicator-text",
iconContainer: "icon-container",
slotContainer: "slot-container",
slotContainerHidden: "slot-container--hidden",
textContainer: "text-container",
textContainerVisible: "text-container--visible",
indicatorWithIcon: "indicator-with-icon",
indicatorWithoutIcon: "indicator-without-icon"
};
const SLOTS = {
tooltip: "tooltip"
};
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{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)) var(--calcite-action-corner-radius-start-end, var(--calcite-corner-radius)) var(--calcite-action-corner-radius-end-end, var(--calcite-corner-radius)) var(--calcite-action-corner-radius-end-start, var(--calcite-corner-radius)))}.interaction-container{border-radius:inherit}: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{border-radius:inherit;position:relative;margin:0;display:flex;inline-size:auto;align-items:center;justify-content:flex-start;border-style:none;font-family:var(--calcite-font-family);font-size:var(--calcite-font-size--2);line-height:1rem;font-weight:var(--calcite-font-weight-medium);outline-color:transparent;background-color:var(--calcite-action-background-color, var(--calcite-color-foreground-1));color:var(--calcite-action-text-color, var(--calcite-internal-action-text-color));text-align:unset;flex:1 0 auto;cursor:inherit}.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: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))))}.button:active{background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-foreground-3)))}.icon-container{pointer-events:none;margin:0;display:flex;align-items:center;justify-content:center;min-inline-size:1rem;min-block-size:1.5rem}.text-container{margin:0;inline-size:0px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.5rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-property:margin;transition-property:inline-size}.text-container--visible{inline-size:auto;flex:1 1 auto;opacity:1}:host([active]) .button,:host([active]) .button:hover,:host([active]) .button:focus{color:var(--calcite-action-text-color-press, var(--calcite-action-text-color-pressed, var(--calcite-color-text-1)));background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-foreground-3)))}:host([active]) .button:active{background-color:var(--calcite-action-background-color, var(--calcite-color-foreground-1))}:host([loading]) .button:hover,:host([loading]) .button:focus{background-color:var(--calcite-action-background-color, var(--calcite-color-foreground-1))}:host([loading]) .text-container{opacity:var(--calcite-opacity-disabled)}:host([loading]) calcite-loader[inline]{margin-inline-end:0px}:host([appearance=transparent]):host([active]) .button{background-color:var(--calcite-color-transparent-press)}:host([appearance=transparent]) .button{background-color:transparent;transition-property:box-shadow;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}:host([appearance=transparent]) .button:hover{background-color:var(--calcite-color-transparent-hover)}:host([appearance=transparent]) .button:active{background-color:var(--calcite-color-transparent-press)}:host([data-active]) .button{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))))}:host([scale=s]) .button{font-size:var(--calcite-font-size--2);line-height:1rem;font-weight:var(--calcite-font-weight-normal);padding-inline:var(--calcite-internal-action-padding-inline, .5rem);padding-block:var(--calcite-internal-action-padding-block, var(--calcite-spacing-xxs))}:host([scale=s]) .button--text-visible .icon-container{margin-inline-end:.5rem}:host([scale=m]) .button{font-size:var(--calcite-font-size--1);line-height:1rem;font-weight:var(--calcite-font-weight-normal);padding-inline:var(--calcite-internal-action-padding-inline, 1rem);padding-block:var(--calcite-internal-action-padding-block, var(--calcite-spacing-md))}:host([scale=m]) .button--text-visible .icon-container{margin-inline-end:.75rem}:host([scale=l]) .button{font-size:var(--calcite-font-size-0);line-height:1.25rem;font-weight:var(--calcite-font-weight-normal);padding-inline:var(--calcite-internal-action-padding-inline, 1.25rem);padding-block:var(--calcite-internal-action-padding-block, var(--calcite-spacing-xl))}:host([scale=l]) .button--text-visible .icon-container{margin-inline-end:1rem}: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}.button--text-visible{inline-size:100%}.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))}.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]) .button,:host([disabled]) .button:hover,:host([disabled]) .button:focus{cursor:default;opacity:var(--calcite-opacity-disabled);background-color:var(--calcite-action-background-color, var(--calcite-action-background-color, var(--calcite-color-foreground-1)))}:host([disabled]):host([active]) .button,:host([disabled]):host([active]) .button:hover,:host([disabled]):host([active]) .button:focus{opacity:var(--calcite-opacity-disabled);background-color:var(--calcite-action-background-color-press, var(--calcite-action-background-color-press, var(--calcite-action-background-color-pressed, var(--calcite-color-foreground-3))))}: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 = `calcite-action-${guid()}`;
this.buttonEl = createRef();
this.buttonId = `${this.guid}-button`;
this.indicatorId = `${this.guid}-indicator`;
this.mutationObserver = createObserver("mutation", () => this.requestUpdate());
this.messages = useT9n({ blocking: true });
this.active = false;
this.appearance = "solid";
this.compact = false;
this.disabled = false;
this.dragHandle = false;
this.iconFlipRtl = false;
this.indicator = false;
this.loading = false;
this.scale = "m";
this.textEnabled = false;
}
static {
this.properties = { active: [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 }], icon: [3, {}, { 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 }], text: 1, textEnabled: [7, {}, { reflect: true, type: Boolean }] };
}
static {
this.styles = styles;
}
async setFocus() {
await componentFocusable(this);
this.buttonEl.value?.focus();
}
connectedCallback() {
super.connectedCallback();
this.mutationObserver?.observe(this.el, { childList: true, subtree: true });
}
updated() {
updateHostInteraction(this);
}
disconnectedCallback() {
super.disconnectedCallback();
this.mutationObserver?.disconnect();
}
handleTooltipSlotChange(event) {
const tooltips = event.target.assignedElements({
flatten: true
}).filter((el) => el?.matches("calcite-tooltip"));
const tooltip = tooltips[0];
if (tooltip) {
tooltip.referenceElement = this.buttonEl.value;
}
}
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, indicatorId, buttonId } = this;
return html`<div aria-labelledby=${buttonId ?? nothing} aria-live=polite class=${safeClassMap(CSS.indicatorText)} id=${indicatorId ?? nothing} role=region>${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 { active, compact, disabled, icon, loading, textEnabled, label, text, indicator, indicatorId, 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>`) || ""}`;
if (this.dragHandle) {
return html`<span aria-controls=${(indicator ? indicatorId : null) ?? nothing} .ariaBusy=${loading} .ariaDisabled=${this.disabled ? this.disabled : null} .ariaLabel=${ariaLabel} .ariaPressed=${active} class=${safeClassMap(buttonClasses)} id=${buttonId ?? nothing} role=button tabindex=${(this.disabled ? null : 0) ?? nothing} ${ref(this.buttonEl)}>${buttonContent}</span>`;
}
return html`<button aria-controls=${(indicator ? indicatorId : null) ?? nothing} .ariaBusy=${loading} .ariaLabel=${ariaLabel} .ariaPressed=${active} class=${safeClassMap(buttonClasses)} .disabled=${disabled} id=${buttonId ?? nothing} tabindex=${(disabled ? null : 0) ?? nothing} ${ref(this.buttonEl)}>${buttonContent}</button>`;
}
render() {
return InteractiveContainer({ disabled: this.disabled, children: html`${this.renderButton()}<slot name=${SLOTS.tooltip} @slotchange=${this.handleTooltipSlotChange}></slot>${this.renderIndicatorText()}` });
}
}
customElement("calcite-action", Action);
export {
Action
};