UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

130 lines (129 loc) • 14 kB
/*! 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 };