@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
172 lines (171 loc) • 15.2 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 { createRef, ref } from "lit-html/directives/ref.js";
import { LitElement, createEvent, safeClassMap } from "@arcgis/lumina";
import { a as slotChangeHasAssignedElement } from "../../chunks/dom.js";
import { c as componentFocusable } from "../../chunks/component.js";
import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js";
import { i as isActivationKey } from "../../chunks/key.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { css } from "@lit/reactive-element/css-tag.js";
const CSS = {
container: "container",
contentWrapper: "content-wrapper",
header: "header",
footer: "footer",
checkboxWrapper: "checkbox-wrapper",
checkboxWrapperDeprecated: "checkbox-wrapper-deprecated",
thumbnailWrapper: "thumbnail-wrapper",
headerTextContainer: "header-text-container",
cardContent: "card-content",
hasSlottedContent: "has-slotted-content"
};
const SLOTS = {
thumbnail: "thumbnail",
heading: "heading",
description: "description",
footerStart: "footer-start",
footerEnd: "footer-end",
title: "title",
subtitle: "subtitle"
};
const ICONS = {
selected: "check-square-f",
unselected: "square",
selectedSingle: "circle-f",
unselectedSingle: "circle"
};
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{display:block;max-inline-size:100%}.content-wrapper{position:relative;display:flex;block-size:100%;flex-direction:column;justify-content:space-between;overflow:hidden;border:var(--calcite-border-width-sm) solid var(--calcite-card-border-color, var(--calcite-color-border-3));border-radius:var(--calcite-card-corner-radius, var(--calcite-corner-radius-sharp));background-color:var(--calcite-card-background-color, var(--calcite-color-foreground-1));box-shadow:var(--calcite-card-shadow, var(--calcite-shadow-none));pointer-events:none}::slotted(*){pointer-events:auto}:host(:not([selectable])) .content-wrapper:not(.non-interactive){outline-color:transparent}:host(:not([selectable])) .content-wrapper:not(.non-interactive):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))))}.container{position:relative;display:flex;flex:1 1 auto;flex-direction:column}:host([loading]) .content-wrapper *:not(calcite-loader):not(.calcite-card-loader-container){pointer-events:none;opacity:0}:host([loading]) .calcite-card-loader-container{position:absolute;inset:0;display:flex;align-items:center}.header{display:flex;flex-direction:row;align-items:flex-start}.footer{margin-block-start:auto;display:flex;flex-direction:row;align-content:space-between;justify-content:space-between;padding-inline:var(--calcite-spacing-md);padding-block-start:var(--calcite-spacing-xxs);padding-block-end:var(--calcite-spacing-md)}.header-text-container{display:flex;inline-size:100%;flex-direction:column;justify-content:center;padding-inline:.75rem;padding-block:.5rem}.header-text-container:not(:only-child){padding-inline-end:.125rem}.footer{margin-block-start:auto;flex-direction:row;align-content:space-between;justify-content:space-between;padding-inline:.75rem;padding-block:.25rem .75rem}.card-content{block-size:auto;font-size:var(--calcite-font-size--2);line-height:1.375}.has-slotted-content{padding:.75rem}:host([selected]) .content-wrapper{box-shadow:inset 0 -4px 0 0 var(--calcite-card-accent-color-selected, var(--calcite-color-brand))}:host([selectable]) .header{padding-inline-end:var(--calcite-spacing-xxxl)}slot[name=title]::slotted(*),*::slotted([slot=title]){margin:0;font-size:var(--calcite-font-size--1);line-height:1.375;font-weight:var(--calcite-font-weight-medium);color:var(--calcite-color-text-1)}slot[name=subtitle]::slotted(*),*::slotted([slot=subtitle]){margin:0;margin-block-start:.125rem;font-size:var(--calcite-font-size--2);line-height:1.375;font-weight:var(--calcite-font-weight-normal);color:var(--calcite-color-text-2)}slot[name=heading]::slotted(*),*::slotted([slot=heading]){margin:0;font-size:var(--calcite-font-size--1);line-height:1.375;font-weight:var(--calcite-font-weight-medium);color:var(--calcite-color-text-1)}slot[name=description]::slotted(*),*::slotted([slot=description]){margin:0;margin-block-start:.125rem;font-size:var(--calcite-font-size--2);line-height:1.375;font-weight:var(--calcite-font-weight-normal);color:var(--calcite-color-text-2)}slot[name=thumbnail]::slotted(img),img::slotted([slot=thumbnail]){min-inline-size:100%;max-inline-size:100%}slot[name=footer-start]::slotted(*),*::slotted([slot=footer-start]){align-self:center;font-size:var(--calcite-font-size--2);line-height:1.375;margin-inline-end:auto}slot[name=footer-end]::slotted(*),*::slotted([slot=footer-end]){align-self:center;font-size:var(--calcite-font-size--2);line-height:1.375}.checkbox-wrapper-deprecated{pointer-events:auto;position:absolute;inset-block-start:var(--calcite-spacing-sm);inset-inline-end:var(--calcite-spacing-sm);margin:0;padding:0;color:var(--calcite-card-selection-color, var(--calcite-color-text-3))}.checkbox-wrapper-deprecated:hover{background-color:var(--calcite-card-selection-background-color-hover, var(--calcite-color-foreground-2));color:var(--calcite-card-selection-color-hover, var(--calcite-card-selection-icon-color-hover, var(--calcite-color-text-2)))}.checkbox-wrapper-deprecated:active{background-color:var(--calcite-card-selection-background-color-press, var(--calcite-color-transparent-press))}.checkbox-wrapper{pointer-events:auto;margin:.5rem;cursor:pointer;padding:.5rem;outline-color:transparent;display:flex;align-items:center;justify-items:center;color:var(--calcite-card-selection-color, var(--calcite-color-text-3))}.checkbox-wrapper:hover{background-color:var(--calcite-card-selection-background-color-hover, var(--calcite-color-foreground-2));color:var(--calcite-card-selection-color-hover, var(--calcite-card-selection-icon-color-hover, var(--calcite-color-text-2)))}.checkbox-wrapper:active{background-color:var(--calcite-card-selection-background-color-press, var(--calcite-color-transparent-press))}.checkbox-wrapper calcite-icon{pointer-events:none}:host([selected]) .checkbox-wrapper-deprecated,:host([selected]) .checkbox-wrapper{color:var(--calcite-card-accent-color-selected, var(--calcite-card-selection-icon-color-selected, var(--calcite-color-brand)))}:host(:not([selectable])) .content-wrapper:not(.non-interactive):focus .checkbox-wrapper-deprecated,:host(:not([selectable])) .content-wrapper:not(.non-interactive):focus .checkbox-wrapper{background-color:var(--calcite-card-selection-background-color-hover, var(--calcite-color-foreground-2));color:var(--calcite-card-selection-color-hover, var(--calcite-card-selection-icon-color-hover, var(--calcite-color-text-2)))}:host([selected]:not([selectable])) .content-wrapper:not(.non-interactive):focus .checkbox-wrapper-deprecated,:host([selected]:not([selectable])) .content-wrapper:not(.non-interactive):focus .checkbox-wrapper{background-color:var(--calcite-card-selection-background-color-press, var(--calcite-color-transparent-press));color:var(--calcite-card-accent-color-selected, var(--calcite-card-selection-icon-color-selected, var(--calcite-color-brand)))}.thumbnail-wrapper{display:flex}.content-wrapper.inline{flex-direction:row}.content-wrapper.inline>.container{inline-size:60%}.content-wrapper.inline>.thumbnail-wrapper{inline-size:40%;align-items:flex-start}.content-wrapper.inline slot[name=thumbnail]::slotted(img),.content-wrapper.inline img::slotted([slot=thumbnail]){inline-size:100%}slot[name=footer-start]::slotted(*),slot[name=footer-end]::slotted(*){display:flex;gap:.25rem}: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 Card extends LitElement {
constructor() {
super(...arguments);
this.containerEl = createRef();
this.messages = useT9n();
this.hasContent = false;
this.hasDescription = false;
this.hasFooterEnd = false;
this.hasFooterStart = false;
this.hasHeading = false;
this.hasSubtitle = false;
this.hasThumbnail = false;
this.hasTitle = false;
this.disabled = false;
this.loading = false;
this.selectable = false;
this.selected = false;
this.selectionMode = "none";
this.thumbnailPosition = "block-start";
this.calciteCardSelect = createEvent({ cancelable: false });
this.calciteInternalCardKeyEvent = createEvent({ cancelable: false });
}
static {
this.properties = { hasContent: [16, {}, { state: true }], hasDescription: [16, {}, { state: true }], hasFooterEnd: [16, {}, { state: true }], hasFooterStart: [16, {}, { state: true }], hasHeading: [16, {}, { state: true }], hasSubtitle: [16, {}, { state: true }], hasThumbnail: [16, {}, { state: true }], hasTitle: [16, {}, { state: true }], disabled: [7, {}, { reflect: true, type: Boolean }], label: 1, loading: [7, {}, { reflect: true, type: Boolean }], messageOverrides: [0, {}, { attribute: false }], selectable: [7, {}, { reflect: true, type: Boolean }], selected: [7, {}, { reflect: true, type: Boolean }], selectionMode: 1, thumbnailPosition: [3, {}, { reflect: true }] };
}
static {
this.styles = styles;
}
async setFocus() {
await componentFocusable(this);
if (!this.disabled) {
this.containerEl.value?.focus();
}
}
updated() {
updateHostInteraction(this);
}
handleThumbnailSlotChange(event) {
this.hasThumbnail = slotChangeHasAssignedElement(event);
}
handleHeadingSlotChange(event) {
this.hasHeading = slotChangeHasAssignedElement(event);
}
handleDescriptionSlotChange(event) {
this.hasDescription = slotChangeHasAssignedElement(event);
}
handleTitleSlotChange(event) {
this.hasTitle = slotChangeHasAssignedElement(event);
}
handleSubtitleSlotChange(event) {
this.hasSubtitle = slotChangeHasAssignedElement(event);
}
handleFooterStartSlotChange(event) {
this.hasFooterStart = slotChangeHasAssignedElement(event);
}
handleFooterEndSlotChange(event) {
this.hasFooterEnd = slotChangeHasAssignedElement(event);
}
handleDefaultSlotChange(event) {
this.hasContent = slotChangeHasAssignedElement(event);
}
keyDownHandler(event) {
if (event.target === this.containerEl.value && !this.selectable && !this.disabled) {
if (isActivationKey(event.key) && this.selectionMode !== "none") {
this.calciteCardSelect.emit();
event.preventDefault();
} else {
switch (event.key) {
case "ArrowRight":
case "ArrowLeft":
case "Home":
case "End":
this.calciteInternalCardKeyEvent.emit(event);
event.preventDefault();
break;
}
}
}
}
cardBodyClickHandler(event) {
const isFromScreenReader = event.target === this.containerEl.value;
if (isFromScreenReader && !this.selectable && !this.disabled && this.selectionMode !== "none") {
this.calciteCardSelect.emit();
}
}
selectCardDeprecated(event) {
this.selected = event.currentTarget.checked;
this.calciteCardSelect.emit();
}
cardSelectClick(event) {
if (!this.disabled) {
event.preventDefault();
this.calciteCardSelect.emit();
this.setFocus();
}
}
renderCheckboxDeprecated() {
return html`<calcite-label class=${safeClassMap(CSS.checkboxWrapperDeprecated)}><calcite-checkbox .checked=${this.selected} .label=${this.messages.select} @calciteCheckboxChange=${this.selectCardDeprecated}></calcite-checkbox></calcite-label>`;
}
renderThumbnail() {
return html`<section class=${safeClassMap(CSS.thumbnailWrapper)} .hidden=${!this.hasThumbnail}><slot name=${SLOTS.thumbnail} @slotchange=${this.handleThumbnailSlotChange}></slot></section>`;
}
renderSelectionIcon() {
const icon = this.selectionMode === "multiple" && this.selected ? ICONS.selected : this.selectionMode === "multiple" ? ICONS.unselected : this.selected ? ICONS.selectedSingle : ICONS.unselectedSingle;
return html`<div class=${safeClassMap(CSS.checkboxWrapper)} @pointerdown=${this.cardSelectClick} tabindex=-1><calcite-icon .icon=${icon} scale=s></calcite-icon></div>`;
}
renderHeader() {
const hasHeader = this.hasHeading || this.hasDescription;
const hasDeprecatedHeader = this.hasSubtitle || this.hasTitle;
const showHeader = hasHeader || hasDeprecatedHeader;
return html`<header class=${safeClassMap(CSS.header)} .hidden=${!showHeader}>${this.selectable ? this.renderCheckboxDeprecated() : null}<div class=${safeClassMap(CSS.headerTextContainer)}><slot name=${SLOTS.heading} @slotchange=${this.handleHeadingSlotChange}></slot><slot name=${SLOTS.description} @slotchange=${this.handleDescriptionSlotChange}></slot><slot name=${SLOTS.title} @slotchange=${this.handleTitleSlotChange}></slot><slot name=${SLOTS.subtitle} @slotchange=${this.handleSubtitleSlotChange}></slot></div>${this.selectionMode !== "none" && this.renderSelectionIcon() || ""}</header>`;
}
renderFooter() {
const hasFooter = this.hasFooterStart || this.hasFooterEnd;
return html`<footer class=${safeClassMap(CSS.footer)} .hidden=${!hasFooter}><slot name=${SLOTS.footerStart} @slotchange=${this.handleFooterStartSlotChange}></slot><slot name=${SLOTS.footerEnd} @slotchange=${this.handleFooterEndSlotChange}></slot></footer>`;
}
render() {
const thumbnailInline = this.thumbnailPosition.startsWith("inline");
const thumbnailStart = this.thumbnailPosition.endsWith("start");
const role = this.selectionMode === "multiple" ? "checkbox" : this.selectionMode !== "none" ? "radio" : void 0;
return InteractiveContainer({ disabled: this.disabled, children: html`<div .ariaChecked=${this.selectionMode !== "none" ? this.selected : void 0} .ariaLabel=${this.label} class=${safeClassMap({ [CSS.contentWrapper]: true, inline: thumbnailInline })} @click=${this.cardBodyClickHandler} @keydown=${this.keyDownHandler} .role=${role} .tabIndex=${!this.selectable || this.disabled ? 0 : -1} ${ref(this.containerEl)}>${this.loading ? html`<div aria-live=polite class="calcite-card-loader-container"><calcite-loader .label=${this.messages.loading}></calcite-loader></div>` : null}${thumbnailStart && this.renderThumbnail() || ""}<section .ariaBusy=${this.loading} class=${safeClassMap({ [CSS.container]: true })}>${this.renderHeader()}<div class=${safeClassMap({
[CSS.cardContent]: true,
[CSS.hasSlottedContent]: this.hasContent
})}><slot @slotchange=${this.handleDefaultSlotChange}></slot></div>${this.renderFooter()}</section>${!thumbnailStart && this.renderThumbnail() || ""}</div>` });
}
}
customElement("calcite-card", Card);
export {
Card
};