UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

494 lines (493 loc) • 15.2 kB
/*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. * v1.5.0-next.4 */ import { Build, forceUpdate, h, Host } from "@stencil/core"; import { toAriaBoolean } from "../../utils/dom"; import { guid } from "../../utils/guid"; import { connectInteractive, disconnectInteractive, updateHostInteraction } from "../../utils/interactive"; import { componentLoaded, setComponentLoaded, setUpLoadableComponent } from "../../utils/loadable"; import { connectLocalized, disconnectLocalized } from "../../utils/locale"; import { createObserver } from "../../utils/observers"; import { connectMessages, disconnectMessages, setUpMessages, updateMessages } from "../../utils/t9n"; import { CSS, SLOTS } from "./resources"; /** * @slot - A slot for adding a `calcite-icon`. */ export class Action { constructor() { this.mutationObserver = createObserver("mutation", () => forceUpdate(this)); this.guid = `calcite-action-${guid()}`; this.indicatorId = `${this.guid}-indicator`; this.buttonId = `${this.guid}-button`; // -------------------------------------------------------------------------- // // Private Methods // // -------------------------------------------------------------------------- this.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; } }; this.active = false; this.alignment = undefined; this.appearance = "solid"; this.compact = false; this.disabled = false; this.icon = undefined; this.iconFlipRtl = false; this.indicator = false; this.label = undefined; this.loading = false; this.scale = "m"; this.text = undefined; this.textEnabled = false; this.messages = undefined; this.messageOverrides = undefined; this.effectiveLocale = ""; this.defaultMessages = undefined; } onMessagesChange() { /* wired up by t9n util */ } effectiveLocaleChange() { updateMessages(this, this.effectiveLocale); } // -------------------------------------------------------------------------- // // Lifecycle // // -------------------------------------------------------------------------- connectedCallback() { connectInteractive(this); connectLocalized(this); connectMessages(this); this.mutationObserver?.observe(this.el, { childList: true, subtree: true }); } async componentWillLoad() { setUpLoadableComponent(this); if (Build.isBrowser) { await setUpMessages(this); } } componentDidLoad() { setComponentLoaded(this); } disconnectedCallback() { disconnectInteractive(this); disconnectLocalized(this); disconnectMessages(this); this.mutationObserver?.disconnect(); } componentDidRender() { updateHostInteraction(this); } // -------------------------------------------------------------------------- // // Methods // // -------------------------------------------------------------------------- /** Sets focus on the component. */ async setFocus() { await componentLoaded(this); this.buttonEl?.focus(); } // -------------------------------------------------------------------------- // // Render Methods // // -------------------------------------------------------------------------- renderTextContainer() { const { text, textEnabled } = this; const textContainerClasses = { [CSS.textContainer]: true, [CSS.textContainerVisible]: textEnabled }; return text ? (h("div", { class: textContainerClasses, key: "text-container" }, text)) : null; } renderIndicatorText() { const { indicator, messages, indicatorId, buttonId } = this; return (h("div", { "aria-labelledby": buttonId, "aria-live": "polite", class: CSS.indicatorText, id: indicatorId, role: "region" }, indicator ? messages.indicator : null)); } renderIconContainer() { const { loading, icon, scale, el, iconFlipRtl } = this; const iconScale = scale === "l" ? "m" : "s"; const loaderScale = scale === "l" ? "l" : "m"; const calciteLoaderNode = loading ? (h("calcite-loader", { inline: true, label: this.messages.loading, scale: loaderScale })) : null; const calciteIconNode = icon ? (h("calcite-icon", { flipRtl: iconFlipRtl, icon: icon, scale: iconScale })) : null; const iconNode = calciteLoaderNode || calciteIconNode; const hasIconToDisplay = iconNode || el.children?.length; const slotContainerNode = (h("div", { class: { [CSS.slotContainer]: true, [CSS.slotContainerHidden]: loading } }, h("slot", null))); return hasIconToDisplay ? (h("div", { "aria-hidden": "true", class: CSS.iconContainer, key: "icon-container" }, iconNode, slotContainerNode)) : null; } render() { const { active, compact, disabled, loading, textEnabled, label, text, indicator, indicatorId, buttonId, messages } = this; const ariaLabel = `${label || text}${indicator ? ` (${messages.indicator})` : ""}`; const buttonClasses = { [CSS.button]: true, [CSS.buttonTextVisible]: textEnabled, [CSS.buttonCompact]: compact }; return (h(Host, null, h("button", { "aria-busy": toAriaBoolean(loading), "aria-controls": indicator ? indicatorId : null, "aria-disabled": toAriaBoolean(disabled), "aria-label": ariaLabel, "aria-pressed": toAriaBoolean(active), class: buttonClasses, disabled: disabled, id: buttonId, // eslint-disable-next-line react/jsx-sort-props ref: (buttonEl) => (this.buttonEl = buttonEl) }, this.renderIconContainer(), this.renderTextContainer()), h("slot", { name: SLOTS.tooltip, onSlotchange: this.handleTooltipSlotChange }), this.renderIndicatorText())); } static get is() { return "calcite-action"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["action.scss"] }; } static get styleUrls() { return { "$": ["action.css"] }; } static get assetsDirs() { return ["assets"]; } static get properties() { return { "active": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, the component is highlighted." }, "attribute": "active", "reflect": true, "defaultValue": "false" }, "alignment": { "type": "string", "mutable": false, "complexType": { "original": "Alignment", "resolved": "\"center\" | \"end\" | \"start\"", "references": { "Alignment": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies the horizontal alignment of button elements with text content." }, "attribute": "alignment", "reflect": true }, "appearance": { "type": "string", "mutable": false, "complexType": { "original": "Extract<\"solid\" | \"transparent\", Appearance>", "resolved": "\"solid\" | \"transparent\"", "references": { "Extract": { "location": "global" }, "Appearance": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies the appearance of the component." }, "attribute": "appearance", "reflect": true, "defaultValue": "\"solid\"" }, "compact": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, the side padding of the component is reduced. Compact mode is used internally by components, e.g. `calcite-block-section`." }, "attribute": "compact", "reflect": true, "defaultValue": "false" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, interaction is prevented and the component is displayed with lower opacity." }, "attribute": "disabled", "reflect": true, "defaultValue": "false" }, "icon": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies an icon to display." }, "attribute": "icon", "reflect": false }, "iconFlipRtl": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, the icon will be flipped when the element direction is right-to-left (`\"rtl\"`)." }, "attribute": "icon-flip-rtl", "reflect": true, "defaultValue": "false" }, "indicator": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, displays a visual indicator." }, "attribute": "indicator", "reflect": true, "defaultValue": "false" }, "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies the label of the component. If no label is provided, the label inherits what's provided for the `text` prop." }, "attribute": "label", "reflect": false }, "loading": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, a busy indicator is displayed." }, "attribute": "loading", "reflect": true, "defaultValue": "false" }, "scale": { "type": "string", "mutable": false, "complexType": { "original": "Scale", "resolved": "\"l\" | \"m\" | \"s\"", "references": { "Scale": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Specifies the size of the component." }, "attribute": "scale", "reflect": true, "defaultValue": "\"m\"" }, "text": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "Specifies text that accompanies the icon." }, "attribute": "text", "reflect": false }, "textEnabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Indicates whether the text is displayed." }, "attribute": "text-enabled", "reflect": true, "defaultValue": "false" }, "messages": { "type": "unknown", "mutable": true, "complexType": { "original": "ActionMessages", "resolved": "{ loading: string; indicator: string; }", "references": { "ActionMessages": { "location": "import", "path": "./assets/action/t9n" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "internal", "text": undefined }], "text": "Made into a prop for testing purposes only" } }, "messageOverrides": { "type": "unknown", "mutable": true, "complexType": { "original": "Partial<ActionMessages>", "resolved": "{ loading?: string; indicator?: string; }", "references": { "Partial": { "location": "global" }, "ActionMessages": { "location": "import", "path": "./assets/action/t9n" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Use this property to override individual strings used by the component." } } }; } static get states() { return { "effectiveLocale": {}, "defaultMessages": {} }; } static get methods() { return { "setFocus": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Sets focus on the component.", "tags": [] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "messageOverrides", "methodName": "onMessagesChange" }, { "propName": "effectiveLocale", "methodName": "effectiveLocaleChange" }]; } }