@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
269 lines (268 loc) • 21.2 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.0/LICENSE.txt */
import { c as customElement } from "../../chunks/runtime.js";
import { keyed } from "lit/directives/keyed.js";
import { ref } from "lit/directives/ref.js";
import { css, html, nothing } from "lit";
import { LitElement, createEvent, safeClassMap } from "@arcgis/lumina";
import { s as slotChangeHasAssignedElement, b as slotChangeGetAssignedElements } from "../../chunks/dom.js";
import { H as Heading } from "../../chunks/Heading.js";
import { g as getIconScale } from "../../chunks/component.js";
import { t as toggleOpenClose } from "../../chunks/openCloseComponent.js";
import { b as defaultEndMenuPlacement } from "../../chunks/floating-ui.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { l as logger } from "../../chunks/logger.js";
import { u as useSetFocus } from "../../chunks/useSetFocus.js";
import { s as styles$2 } from "../../chunks/sortable.js";
import { s as styles$1 } from "../../chunks/header.js";
import { u as useInteractive } from "../../chunks/useInteractive.js";
const IDS = {
content: "content",
toggle: "toggle",
header: "header"
};
const CSS = {
actionsEnd: "actions-end",
container: "container",
content: "content",
contentEnd: "content-end",
contentStart: "content-start",
description: "description",
header: "header",
headerContainer: "header-container",
headerHasContent: "header--has-content",
headerDraggable: "header--draggable",
heading: "heading",
icon: "icon",
iconStart: "icon--start",
iconEnd: "icon--end",
iconEndContainer: "icon-end-container",
invalid: "invalid",
statusIcon: "status-icon",
title: "title",
toggle: "toggle",
toggleIcon: "toggle-icon",
valid: "valid"
};
const SLOTS = {
actionsEnd: "actions-end",
contentEnd: "content-end",
contentStart: "content-start",
headerMenuActions: "header-menu-actions"
};
const ICONS = {
expanded: "chevron-up",
collapsed: "chevron-down",
valid: "check-circle",
invalid: "exclamation-mark-triangle"
};
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([scale=s]) .header{gap:var(--calcite-spacing-sm)}:host([scale=s]) .icon-end-container{gap:var(--calcite-spacing-sm);padding-inline-end:var(--calcite-spacing-sm)}:host([scale=s]) .heading{font-size:var(--calcite-font-size-sm)}:host([scale=s]) .description{font-size:var(--calcite-font-size-xs)}:host([scale=s]){--calcite-internal-block-actions-spacing: var(--calcite-spacing-xxs);--calcite-internal-block-header-content-padding: var(--calcite-spacing-sm);--calcite-internal-block-padding-block: var( --calcite-block-content-space, var(--calcite-block-padding, var(--calcite-spacing-xxs)) );--calcite-internal-block-padding-inline: var( --calcite-block-content-space, var(--calcite-block-padding, var(--calcite-spacing-sm)) )}:host([scale=m]) .header{gap:var(--calcite-spacing-md)}:host([scale=m]) .icon-end-container{gap:var(--calcite-spacing-md);padding-inline-end:var(--calcite-spacing-md)}:host([scale=m]) .heading{font-size:var(--calcite-font-size)}:host([scale=m]) .description{font-size:var(--calcite-font-size-sm)}:host([scale=m]){--calcite-internal-block-actions-spacing: var(--calcite-spacing-xxs);--calcite-internal-block-header-content-padding: var(--calcite-spacing-md);--calcite-internal-block-padding-block: var( --calcite-block-content-space, var(--calcite-block-padding, var(--calcite-spacing-sm)) );--calcite-internal-block-padding-inline: var( --calcite-block-content-space, var(--calcite-block-padding, var(--calcite-spacing-md)) )}:host([scale=l]) .header{gap:var(--calcite-spacing-lg)}:host([scale=l]) .icon-end-container{gap:var(--calcite-spacing-lg);padding-inline-end:var(--calcite-spacing-lg)}:host([scale=l]) .heading{font-size:var(--calcite-font-size-md)}:host([scale=l]) .description{font-size:var(--calcite-font-size)}:host([scale=l]){--calcite-internal-block-actions-spacing: var(--calcite-spacing-xs);--calcite-internal-block-header-content-padding: var(--calcite-spacing-lg);--calcite-internal-block-padding-block: var( --calcite-block-content-space, var(--calcite-block-padding, var(--calcite-spacing-md)) );--calcite-internal-block-padding-inline: var( --calcite-block-content-space, var(--calcite-block-padding, var(--calcite-spacing-lg)) )}:host{display:flex;flex-shrink:0;flex-grow:0;flex-direction:column;border-width:0px;border-block-end-width:1px;border-style:solid;padding:0;transition-property:margin;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--calcite-animation-timing);transition-timing-function:cubic-bezier(.215,.44,.42,.88);flex-basis:auto;border-color:var(--calcite-block-border-color, var(--calcite-color-border-3))}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}.header{justify-content:flex-start}.header--has-content{padding:var(--calcite-internal-block-header-content-padding)}.header--draggable{padding-inline-start:var(--calcite-spacing-xxs)}.header,.toggle{grid-area:header}.header-container{display:grid;align-items:stretch;grid-template:auto/auto 1fr auto auto auto auto;grid-template-areas:"handle header icon-end menu actions-end"}.icon--start,.icon--end{color:var(--calcite-block-icon-color, var(--calcite-color-text-3))}.actions-end{align-items:center;display:flex;gap:var(--calcite-internal-block-actions-spacing);grid-area:actions-end;padding-block:var(--calcite-internal-block-actions-spacing);padding-inline-end:var(--calcite-internal-block-actions-spacing)}.toggle{margin:0;display:flex;cursor:pointer;flex-wrap:nowrap;align-items:center;justify-content:space-between;border-style:none;padding:0;outline-color:transparent;font-family:inherit;text-align:initial;background-color:var(--calcite-block-header-background-color, transparent)}.toggle:hover{background-color:var(--calcite-block-header-background-color-hover, var(--calcite-color-foreground-2))}.toggle:focus{outline:var(--calcite-border-width-md) solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(calc(-1 * var(--calcite-spacing-base)) * calc(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}.toggle:active{background-color:var(--calcite-block-header-background-color-press, var(--calcite-color-foreground-3))}calcite-loader[inline]{align-self:center}calcite-sort-handle{align-self:center;grid-area:handle;margin-block:var(--calcite-internal-block-actions-spacing);padding-inline-start:var(--calcite-spacing-xxs)}.title{display:flex;flex-direction:column}.header .title .heading{padding:0;word-wrap:break-word;word-break:break-word;color:var(--calcite-block-heading-text-color, var(--calcite-color-text-1));font-weight:var(--calcite-font-weight-normal);line-height:var(--calcite-font-line-height-relative-snug)}.description{padding:0;word-wrap:break-word;word-break:break-word;color:var(--calcite-block-description-text-color, var(--calcite-color-text-3));font-weight:var(--calcite-font-weight-regular);line-height:var(--calcite-font-line-height-relative-snug)}.icon{display:flex}.status-icon.valid{color:var(--calcite-color-status-success)}.status-icon.invalid{color:var(--calcite-color-status-danger)}@keyframes spin{0%{transform:rotate(0)}50%{transform:rotate(180deg)}to{transform:rotate(360deg)}}.icon-end-container{display:flex;align-items:center;grid-area:icon-end}.toggle-icon{align-self:center;justify-self:end;transition-property:color;transition-duration:var(--calcite-animation-timing);transition-timing-function:cubic-bezier(.4,0,.2,1);color:var(--calcite-block-icon-color, var(--calcite-color-text-3))}.toggle:hover .toggle-icon{color:var(--calcite-block-icon-color-hover, var(--calcite-color-text-1))}.container{position:relative;display:flex;block-size:100%;flex-direction:column}.content{position:relative;min-block-size:0px;flex:1 1 0%}@keyframes in{0%{opacity:0}to{opacity:1}}.content{animation:in var(--calcite-internal-animation-timing-slow) ease-in-out;padding-block:var(--calcite-internal-block-padding-block);padding-inline:var(--calcite-internal-block-padding-inline)}.content-end,.content-start{display:flex;align-items:center;color:var(--calcite-block-text-color, var(--calcite-color-text-3))}calcite-action-menu{align-self:center;grid-area:menu;margin-inline-end:var(--calcite-internal-block-actions-spacing)}:host([expanded]){margin-block:.5rem}:host([expanded]) .header .title .heading{color:var(--calcite-block-heading-text-color, var(--calcite-block-heading-text-color-press, var(--calcite-color-text-1)));font-weight:var(--calcite-font-weight-medium)}:host([expanded]) .description{color:var(--calcite-block-description-text-color, var(--calcite-color-text-2))}:host([expanded]) .icon--start,:host([expanded]) .icon--end{color:var(--calcite-block-icon-color, var(--calcite-color-text-1))}:host([expanded][scale=s]){margin-block:var(--calcite-spacing-xxs)}:host([expanded][scale=l]){margin-block:var(--calcite-spacing-md)}:host([hidden]){display:none}[hidden]{display:none}`;
class Block extends LitElement {
constructor() {
super(...arguments);
this.transitionProp = "margin-top";
this.blockSectionChildren = [];
this.messages = useT9n();
this.focusSetter = useSetFocus()(this);
this.interactiveContainer = useInteractive(this);
this.hasContentEnd = false;
this.hasContentStart = false;
this.hasEndActions = false;
this.hasMenuActions = false;
this.collapsible = false;
this.disabled = false;
this.dragDisabled = false;
this.dragHandle = false;
this.expanded = false;
this.loading = false;
this.menuPlacement = defaultEndMenuPlacement;
this.addToItems = [];
this.moveToItems = [];
this.sortDisabled = false;
this.overlayPositioning = "absolute";
this.scale = "m";
this.setPosition = null;
this.setSize = null;
this.sortHandleOpen = false;
this.topLayerDisabled = false;
this.calciteBlockBeforeClose = createEvent({ cancelable: false });
this.calciteBlockBeforeOpen = createEvent({ cancelable: false });
this.calciteBlockClose = createEvent({ cancelable: false });
this.calciteBlockCollapse = createEvent({ cancelable: false });
this.calciteBlockExpand = createEvent({ cancelable: false });
this.calciteBlockOpen = createEvent({ cancelable: false });
this.calciteBlockSortHandleBeforeClose = createEvent({ cancelable: false });
this.calciteBlockSortHandleBeforeOpen = createEvent({ cancelable: false });
this.calciteBlockSortHandleClose = createEvent({ cancelable: false });
this.calciteBlockSortHandleOpen = createEvent({ cancelable: false });
this.calciteBlockToggle = createEvent({ cancelable: false });
this.calciteInternalBlockUpdateSortMenuItems = createEvent({ cancelable: false });
}
static {
this.properties = { hasContentEnd: [16, {}, { state: true }], hasContentStart: [16, {}, { state: true }], hasEndActions: [16, {}, { state: true }], hasMenuActions: [16, {}, { state: true }], collapsible: [7, {}, { reflect: true, type: Boolean }], description: 1, disabled: [7, {}, { reflect: true, type: Boolean }], dragDisabled: [7, {}, { reflect: true, type: Boolean }], dragHandle: [7, {}, { reflect: true, type: Boolean }], expanded: [7, {}, { reflect: true, type: Boolean }], heading: 1, headingLevel: [11, {}, { type: Number, reflect: true }], iconEnd: [3, { type: String }, { reflect: true }], iconFlipRtl: [3, {}, { reflect: true }], iconStart: [3, { type: String }, { reflect: true }], loading: [7, {}, { reflect: true, type: Boolean }], label: 1, menuFlipPlacements: [0, {}, { attribute: false }], menuPlacement: [3, {}, { reflect: true }], messageOverrides: [0, {}, { attribute: false }], addToItems: [0, {}, { attribute: false }], moveToItems: [0, {}, { attribute: false }], sortDisabled: [5, {}, { type: Boolean }], open: [7, {}, { reflect: true, type: Boolean }], overlayPositioning: [3, {}, { reflect: true }], scale: [3, {}, { reflect: true }], setPosition: [9, {}, { type: Number }], setSize: [9, {}, { type: Number }], sortHandleOpen: [7, {}, { reflect: true, type: Boolean }], status: [3, {}, { reflect: true }], topLayerDisabled: [7, {}, { reflect: true, type: Boolean }] };
}
static {
this.styles = [styles$1, styles, styles$2];
}
get open() {
return this.expanded;
}
set open(value) {
logger.deprecated("property", {
component: this,
name: "open",
removalVersion: 5,
suggested: "expanded"
});
this.expanded = value;
}
async setFocus(options) {
return this.focusSetter(() => this.el, options);
}
connectedCallback() {
super.connectedCallback();
this.transitionEl = this.el;
}
load() {
if (!this.heading && !this.label) {
logger.warn(`${this.el.tagName} is missing both heading & label. Please provide a heading or label for the component to be accessible.`);
}
}
willUpdate(changes) {
if (changes.has("expanded") && (this.hasUpdated || this.expanded !== false)) {
toggleOpenClose(this);
}
if (changes.has("sortHandleOpen") && (this.hasUpdated || this.sortHandleOpen !== false)) {
this.sortHandleOpenHandler();
}
if (changes.has("expanded") && this.hasUpdated) {
if (this.expanded) {
this.calciteBlockExpand.emit();
} else {
this.calciteBlockCollapse.emit();
}
}
if (changes.has("scale") && this.hasUpdated) {
this.updateBlockSectionScale();
}
}
onBeforeOpen() {
this.calciteBlockBeforeOpen.emit();
}
onOpen() {
this.calciteBlockOpen.emit();
}
onBeforeClose() {
this.calciteBlockBeforeClose.emit();
}
onClose() {
this.calciteBlockClose.emit();
}
sortHandleOpenHandler() {
if (!this.sortHandleEl) {
return;
}
this.sortHandleEl.open = this.sortHandleOpen;
}
setSortHandleEl(el) {
this.sortHandleEl = el;
this.sortHandleOpenHandler();
}
handleSortHandleBeforeOpen(event) {
event.stopPropagation();
this.calciteBlockSortHandleBeforeOpen.emit();
}
handleSortHandleBeforeClose(event) {
event.stopPropagation();
this.calciteBlockSortHandleBeforeClose.emit();
}
handleSortHandleClose(event) {
event.stopPropagation();
this.sortHandleOpen = false;
this.calciteBlockSortHandleClose.emit();
}
handleSortHandleOpen(event) {
event.stopPropagation();
this.sortHandleOpen = true;
this.calciteBlockSortHandleOpen.emit();
}
onHeaderClick() {
this.expanded = !this.expanded;
this.calciteBlockToggle.emit();
}
menuActionsSlotChangeHandler(event) {
this.hasMenuActions = slotChangeHasAssignedElement(event);
}
actionsEndSlotChangeHandler(event) {
this.hasEndActions = slotChangeHasAssignedElement(event);
}
handleContentEndSlotChange(event) {
this.hasContentEnd = slotChangeHasAssignedElement(event);
}
handleContentStartSlotChange(event) {
this.hasContentStart = slotChangeHasAssignedElement(event);
}
handleDefaultSlotChange(event) {
this.blockSectionChildren = slotChangeGetAssignedElements(event, "calcite-block-section");
this.updateBlockSectionScale();
}
updateBlockSectionScale() {
this.blockSectionChildren.forEach((el) => {
el.scale = this.scale;
});
}
renderScrim() {
const { loading } = this;
const defaultSlot = html`<slot @slotchange=${this.handleDefaultSlotChange}></slot>`;
return [loading ? html`<calcite-scrim .loading=${loading}></calcite-scrim>` : null, defaultSlot];
}
renderLoaderStatusIcon() {
const { loading, messages, status: status2 } = this;
return loading ? keyed("loader", html`<div class=${safeClassMap(CSS.icon)}><calcite-loader inline .label=${messages.loading} .scale=${this.scale}></calcite-loader></div>`) : status2 ? keyed("status-icon", html`<div class=${safeClassMap(CSS.icon)}><calcite-icon class=${safeClassMap({
[CSS.statusIcon]: true,
[CSS.valid]: status2 == "valid",
[CSS.invalid]: status2 == "invalid"
})} .icon=${ICONS[status2]} .scale=${getIconScale(this.scale)}></calcite-icon></div>`) : null;
}
renderActionsEnd() {
return html`<div class=${safeClassMap(CSS.actionsEnd)} .hidden=${!this.hasEndActions}><slot name=${SLOTS.actionsEnd} @slotchange=${this.actionsEndSlotChangeHandler}></slot></div>`;
}
renderContentEnd() {
return html`<div class=${safeClassMap({ [CSS.iconEndContainer]: !this.iconEnd && !this.collapsible })} .hidden=${!this.hasContentEnd}><div class=${safeClassMap(CSS.contentEnd)}><slot name=${SLOTS.contentEnd} @slotchange=${this.handleContentEndSlotChange}></slot></div></div>`;
}
renderContentStart() {
return html`<div class=${safeClassMap(CSS.contentStart)} .hidden=${!this.hasContentStart}><slot name=${SLOTS.contentStart} @slotchange=${this.handleContentStartSlotChange}></slot></div>`;
}
renderTitle() {
const { heading, headingLevel, description } = this;
return heading || description ? html`<div class=${safeClassMap(CSS.title)}>${Heading({ class: CSS.heading, level: headingLevel, children: heading })}${description ? html`<div class=${safeClassMap(CSS.description)}>${description}</div>` : null}</div>` : null;
}
renderIcon(position) {
const { iconFlipRtl } = this;
const flipRtl = iconFlipRtl === "both" || position === "start" ? iconFlipRtl === "start" : iconFlipRtl === "end";
const iconValue = position === "start" ? this.iconStart : this.iconEnd;
const iconClass = position === "start" ? CSS.iconStart : CSS.iconEnd;
if (!iconValue) {
return void 0;
}
return keyed(iconValue, html`<calcite-icon class=${safeClassMap(iconClass)} .flipRtl=${flipRtl} .icon=${iconValue} .scale=${getIconScale(this.scale)}></calcite-icon>`);
}
render() {
const { collapsible, loading, expanded, label, heading, messages, description, menuFlipPlacements, menuPlacement, moveToItems, addToItems, setPosition, setSize, dragDisabled, sortDisabled, iconEnd, hasContentEnd, hasContentStart, iconStart } = this;
const toggleLabel = expanded ? messages.collapse : messages.expand;
const headerHasContent = !!(heading || description || hasContentEnd || hasContentStart || iconStart || loading || status);
const headerContent = html`<header class=${safeClassMap({
[CSS.header]: true,
[CSS.headerHasContent]: headerHasContent,
[CSS.headerDraggable]: this.dragHandle
})} id=${IDS.header}>${this.renderIcon("start")}${this.renderContentStart()}${this.renderLoaderStatusIcon()}${this.renderTitle()}</header>`;
const collapseIcon = expanded ? ICONS.expanded : ICONS.collapsed;
const headerNode = html`<div class=${safeClassMap(CSS.headerContainer)}>${this.dragHandle ? html`<calcite-sort-handle .addToItems=${addToItems} .disabled=${dragDisabled} .label=${heading || label} .moveToItems=${moveToItems} @calciteSortHandleBeforeClose=${this.handleSortHandleBeforeClose} @calciteSortHandleBeforeOpen=${this.handleSortHandleBeforeOpen} @calciteSortHandleClose=${this.handleSortHandleClose} @calciteSortHandleOpen=${this.handleSortHandleOpen} overlay-positioning=fixed .scale=${this.scale} .setPosition=${setPosition} .setSize=${setSize} .sortDisabled=${sortDisabled} .topLayerDisabled=${this.topLayerDisabled} ${ref(this.setSortHandleEl)}></calcite-sort-handle>` : null}${collapsible ? html`<button aria-controls=${IDS.content} aria-describedby=${IDS.header} .ariaExpanded=${collapsible ? expanded : null} class=${safeClassMap(CSS.toggle)} id=${IDS.toggle} @click=${this.onHeaderClick} title=${toggleLabel ?? nothing}>${headerContent}<div class=${safeClassMap(CSS.iconEndContainer)}>${this.renderContentEnd()}${this.renderIcon("end")}<calcite-icon class=${safeClassMap(CSS.toggleIcon)} .icon=${collapseIcon} .scale=${getIconScale(this.scale)}></calcite-icon></div></button>` : headerContent}${iconEnd && !collapsible ? html`<div class=${safeClassMap(CSS.iconEndContainer)}>${this.renderContentEnd()}${this.renderIcon("end")}</div>` : !iconEnd && !collapsible ? this.renderContentEnd() : null}<calcite-action-menu .flipPlacements=${menuFlipPlacements ?? ["top", "bottom"]} .hidden=${!this.hasMenuActions} .label=${messages.options} .overlayPositioning=${this.overlayPositioning} .placement=${menuPlacement} .scale=${this.scale} .topLayerDisabled=${this.topLayerDisabled}><slot name=${SLOTS.headerMenuActions} @slotchange=${this.menuActionsSlotChangeHandler}></slot></calcite-action-menu>${this.renderActionsEnd()}</div>`;
return this.interactiveContainer({ disabled: this.disabled, children: html`<article aria-label=${label ?? nothing} .ariaBusy=${loading} class=${safeClassMap({
[CSS.container]: true
})}>${headerNode}<section aria-labelledby=${IDS.toggle} class=${safeClassMap(CSS.content)} .hidden=${!expanded} id=${IDS.content}>${this.renderScrim()}</section></article>` });
}
}
customElement("calcite-block", Block);
export {
Block
};