UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

269 lines (268 loc) • 21.2 kB
/* 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 };