@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
153 lines (152 loc) • 13.8 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 { ref } from "lit-html/directives/ref.js";
import { LitElement, createEvent, safeClassMap, nothing } from "@arcgis/lumina";
import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js";
import { a as slotChangeHasAssignedElement } from "../../chunks/dom.js";
import { c as componentFocusable } from "../../chunks/component.js";
import { css } from "@lit/reactive-element/css-tag.js";
const CSS = {
container: "container",
contentContainer: "content-container",
contentContainerHasContent: "content-container--has-content",
contentContainerHasOnlyContentTopAndBottom: "content-container--has-only-content-top-and-bottom",
textContentContainer: "text-content-container",
description: "description",
heading: "heading",
icon: "icon",
interactive: "interactive",
largeVisualDeprecated: "large-visual-deprecated",
row: "row",
selected: "selected",
selectionIcon: "selection-icon",
textContent: "text-content"
};
const ICONS = {
selectedMultiple: "check-square-f",
selectedSingle: "circle-f",
unselectedMultiple: "square",
unselectedSingle: "circle"
};
const SLOTS = {
contentBottom: "content-bottom",
contentEnd: "content-end",
contentStart: "content-start",
contentTop: "content-top"
};
const styles = css`: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{box-shadow:var(--calcite-tile-shadow, var(--calcite-shadow-none));box-sizing:border-box;display:inline-block}calcite-link{--calcite-link-text-color: var(--calcite-tile-link-text-color)}.container{background-color:var(--calcite-tile-background-color, var(--calcite-color-foreground-1));block-size:var(--calcite-container-size-content-fluid);border-radius:var(--calcite-tile-corner-radius, var(--calcite-corner-radius));box-sizing:border-box;color:var(--calcite-tile-text-color, var(--calcite-color-text-3));inline-size:var(--calcite-container-size-content-fluid);outline:var(--calcite-border-width-sm, 1px) solid var(--calcite-tile-border-color, var(--calcite-color-border-2));padding:var(--calcite-internal-tile-spacing);position:relative;-webkit-user-select:none;user-select:none}.container .selection-icon{color:var(--calcite-tile-text-color, var(--calcite-color-text-3))}.container.interactive{cursor:pointer}.container.interactive:hover,.container.interactive:focus,.container.interactive.selected{outline-color:var(--calcite-tile-accent-color-press, var(--calcite-color-brand));z-index:var(--calcite-z-index)}.container.interactive:hover .selection-icon,.container.interactive:focus .selection-icon,.container.interactive.selected .selection-icon{color:var(--calcite-tile-accent-color-press, var(--calcite-color-brand))}.container.interactive:focus{box-shadow:inset 0 0 0 1px var(--calcite-tile-accent-color-press, var(--calcite-color-brand));z-index:calc(var(--calcite-z-index) + 1)}.content-container,.row{align-items:flex-start;display:flex}.content-container{flex-direction:column;word-wrap:break-word;word-break:break-word;inline-size:var(--calcite-container-size-content-fluid)}.text-content-container{inline-size:100%;outline-color:transparent;padding:0}.text-content{display:flex;flex-direction:column}.heading{color:var(--calcite-tile-heading-text-color, var(--calcite-color-text-2));font-weight:var(--calcite-font-weight-medium);line-height:1.20313rem;overflow-wrap:break-word}.description{font-weight:var(--calcite-font-weight-regular);overflow-wrap:break-word}.large-visual-deprecated{align-items:center;justify-content:center;min-block-size:12rem;text-align:center}.large-visual-deprecated .icon{align-self:center;block-size:64px;inline-size:64px}.large-visual-deprecated .selection-icon{position:absolute;inset-inline-start:var(--calcite-internal-tile-spacing);inset-block-start:var(--calcite-internal-tile-spacing);z-index:var(--calcite-z-index)}.large-visual-deprecated .text-content-container{justify-content:center}:host([alignment=center]) .icon{align-self:center}:host([alignment=center]) .text-content{text-align:center}:host([alignment=center]) slot[name=content-start]::slotted(*),:host([alignment=center]) slot[name=content-end]::slotted(*){align-self:center}:host([scale=s]){--calcite-internal-tile-spacing: var(--calcite-spacing-sm);max-inline-size:400px;min-inline-size:100px}:host([scale=s]) .heading{font-size:var(--calcite-font-size--2);line-height:1.03125rem}:host([scale=s]) .description{font-size:var(--calcite-font-size--3);line-height:.85938rem}:host([scale=m]){--calcite-internal-tile-spacing: var(--calcite-spacing-md);max-inline-size:460px;min-inline-size:140px}:host([scale=m]) .heading{font-size:var(--calcite-font-size--1);line-height:1.20313rem}:host([scale=m]) .description{font-size:var(--calcite-font-size--2);line-height:1.03125rem}:host([scale=l]){--calcite-internal-tile-spacing: var(--calcite-spacing-lg);max-inline-size:520px;min-inline-size:160px}:host([scale=l]) .heading{font-size:var(--calcite-font-size-0);line-height:1.375rem}:host([scale=l]) .description{font-size:var(--calcite-font-size--1);line-height:1.20313rem}.content-container--has-content,.row{gap:var(--calcite-internal-tile-spacing)}.content-container--has-only-content-top-and-bottom slot[name=content-top]::slotted(*){margin-block-end:var(--calcite-internal-tile-spacing)}:host([selection-appearance=border][layout=horizontal]) .container.selected:focus:before,:host([selection-appearance=border][layout=vertical]) .container.selected:focus:before{block-size:100%;box-shadow:inset 0 0 0 1px var(--calcite-tile-accent-color-press, var(--calcite-color-brand));content:"";display:block;inline-size:100%;inset-block-start:0;inset-inline-start:0;position:absolute}:host([selection-appearance=border][layout=horizontal]) .container.selected{box-shadow:inset 0 -4px 0 0 var(--calcite-tile-accent-color-press, var(--calcite-color-brand))}:host([selection-appearance=border][layout=vertical]) .container.selected{box-shadow:inset 4px 0 0 0 var(--calcite-tile-accent-color-press, var(--calcite-color-brand))}:host(:hover:not([disabled])) .heading,:host([active]:not([disabled])) .heading{color:var(--calcite-tile-heading-text-color, var(--calcite-color-text-1))}:host(:hover:not([disabled])) .description,:host([active]:not([disabled])) .description{color:var(--calcite-tile-text-color, var(--calcite-color-text-2))}:host([href]:focus:not([disabled])) .container,:host([href]:hover:not([disabled])) .container{outline-color:var(--calcite-tile-link-color, var(--calcite-color-text-link));z-index:var(--calcite-z-index)}:host([href]:focus:not([disabled])) .icon,:host([href]:hover:not([disabled])) .icon{color:var(--calcite-tile-link-color, var(--calcite-color-text-link))}:host([href]:focus:not([disabled])) .heading,:host([href]:hover:not([disabled])) .heading{color:var(--calcite-tile-link-color, var(--calcite-color-text-link))}:host([href]:active:not([disabled])) .container{box-shadow:inset 0 0 0 1px var(--calcite-tile-link-color, var(--calcite-color-text-link));outline-color:var(--calcite-tile-link-color, var(--calcite-color-text-link))}:host([embed]) .container{padding:0}:host([selection-mode=none]) .container:hover,:host([selection-mode=none]) .container.selected{outline-color:var(--calcite-tile-border-color, var(--calcite-color-border-2))}:host([selection-mode=none]) .container:focus{outline-color:var(--calcite-tile-accent-color-press, var(--calcite-color-brand))}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}:host([hidden]){display:none}[hidden]{display:none}::slotted(*){max-inline-size:100%}`;
class Tile extends LitElement {
constructor() {
super();
this.hasContentBottom = false;
this.hasContentEnd = false;
this.hasContentStart = false;
this.hasContentTop = false;
this.active = false;
this.alignment = "start";
this.disabled = false;
this.embed = false;
this.iconFlipRtl = false;
this.interactive = false;
this.layout = "horizontal";
this.scale = "m";
this.selected = false;
this.selectionAppearance = "icon";
this.selectionMode = "none";
this.calciteInternalTileKeyEvent = createEvent({ cancelable: false });
this.calciteTileSelect = createEvent();
this.listen("keydown", this.keyDownHandler);
}
static {
this.properties = { hasContentBottom: [16, {}, { state: true }], hasContentEnd: [16, {}, { state: true }], hasContentStart: [16, {}, { state: true }], hasContentTop: [16, {}, { state: true }], active: [7, {}, { reflect: true, type: Boolean }], alignment: [3, {}, { reflect: true }], description: [3, {}, { reflect: true }], disabled: [7, {}, { reflect: true, type: Boolean }], embed: [7, {}, { reflect: true, type: Boolean }], heading: [3, {}, { reflect: true }], href: [3, {}, { reflect: true }], icon: [3, {}, { reflect: true }], iconFlipRtl: [7, {}, { reflect: true, type: Boolean }], interactive: [5, {}, { type: Boolean }], label: 1, layout: [3, {}, { reflect: true }], scale: [3, {}, { reflect: true }], selected: [7, {}, { reflect: true, type: Boolean }], selectionAppearance: [3, {}, { reflect: true }], selectionMode: [3, {}, { reflect: true }] };
}
static {
this.styles = styles;
}
async setFocus() {
await componentFocusable(this);
if (!this.disabled && this.interactive) {
this.containerEl?.focus();
}
}
updated() {
updateHostInteraction(this);
}
clickHandler() {
if (this.interactive) {
this.setFocus();
this.handleSelectEvent();
}
}
handleSelectEvent() {
if (this.disabled || !this.interactive || this.selectionMode === "single-persist" && this.selected === true) {
return;
}
this.calciteTileSelect.emit();
}
handleSlotChange(event) {
const slotName = event.target.dataset.name;
this[`has${slotName}`] = slotChangeHasAssignedElement(event);
}
setContainerEl(el) {
this.containerEl = el;
}
keyDownHandler(event) {
if (event.target === this.el) {
switch (event.key) {
case " ":
case "Enter":
this.handleSelectEvent();
event.preventDefault();
break;
case "ArrowDown":
case "ArrowLeft":
case "ArrowRight":
case "ArrowUp":
case "Home":
case "End":
this.calciteInternalTileKeyEvent.emit(event);
event.preventDefault();
break;
}
}
}
renderSelectionIcon() {
const { selected, selectionAppearance, selectionMode } = this;
if (selectionAppearance === "icon" && selectionMode !== "none") {
return html`<calcite-icon class=${safeClassMap(CSS.selectionIcon)} .icon=${selected ? selectionMode === "multiple" ? ICONS.selectedMultiple : ICONS.selectedSingle : selectionMode === "multiple" ? ICONS.unselectedMultiple : ICONS.unselectedSingle} scale=s></calcite-icon>`;
}
return;
}
renderTile() {
const { description, disabled, hasContentBottom, hasContentEnd, hasContentStart, hasContentTop, heading, icon, iconFlipRtl, interactive, selectionMode } = this;
const isLargeVisual = heading && icon && !description;
const disableInteraction = Boolean(this.href) || !interactive;
const role = selectionMode === "multiple" && interactive ? "checkbox" : selectionMode !== "none" && interactive ? "radio" : interactive ? "button" : void 0;
const hasContent = !!(description || hasContentEnd || hasContentStart || heading || icon);
const hasOnlyContentTopAndBottom = !hasContent && hasContentTop && hasContentBottom;
return html`<div .ariaChecked=${selectionMode !== "none" && interactive ? this.selected : void 0} .ariaDisabled=${disableInteraction ? disabled : void 0} .ariaLabel=${role && this.label} class=${safeClassMap({
[CSS.container]: true,
[CSS.interactive]: interactive,
// [Deprecated] Use the content-top slot for rendering icon with alignment="center" instead
[CSS.largeVisualDeprecated]: isLargeVisual,
[CSS.row]: true,
[CSS.selected]: this.selected
})} @click=${this.clickHandler} .role=${role} tabindex=${(disableInteraction ? void 0 : 0) ?? nothing} ${ref(this.setContainerEl)}>${this.renderSelectionIcon()}<div class=${safeClassMap({
[CSS.contentContainer]: true,
[CSS.contentContainerHasContent]: hasContent,
[CSS.contentContainerHasOnlyContentTopAndBottom]: hasOnlyContentTopAndBottom
})}><slot name=${SLOTS.contentTop} @slotchange=${this.handleSlotChange}></slot>${icon && html`<calcite-icon class=${safeClassMap(CSS.icon)} .flipRtl=${iconFlipRtl} .icon=${icon} scale=l></calcite-icon>` || ""}<div class=${safeClassMap({ [CSS.textContentContainer]: true, [CSS.row]: true })}><slot name=${SLOTS.contentStart} @slotchange=${this.handleSlotChange}></slot><div class=${safeClassMap(CSS.textContent)}>${heading && html`<div class=${safeClassMap(CSS.heading)}>${heading}</div>` || ""}${description && html`<div class=${safeClassMap(CSS.description)}>${description}</div>` || ""}</div><slot name=${SLOTS.contentEnd} @slotchange=${this.handleSlotChange}></slot></div><slot name=${SLOTS.contentBottom} @slotchange=${this.handleSlotChange}></slot></div></div>`;
}
render() {
const { disabled } = this;
return InteractiveContainer({ disabled, children: this.href ? html`<calcite-link .disabled=${disabled} .href=${this.href}>${this.renderTile()}</calcite-link>` : this.renderTile() });
}
}
customElement("calcite-tile", Tile);
export {
Tile
};