@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
212 lines (211 loc) • 10.6 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 { D as DEBOUNCE, c as customElement } from "../../chunks/runtime.js";
import { debounce } from "lodash-es";
import { html } from "lit";
import { LitElement, createEvent, safeClassMap } from "@arcgis/lumina";
import { h as focusFirstTabbable, a as slotChangeHasAssignedElement, s as slotChangeGetAssignedElements } from "../../chunks/dom.js";
import { c as componentFocusable } from "../../chunks/component.js";
import { c as createObserver } from "../../chunks/observers.js";
import { o as overflowActions, q as queryActions, t as toggleChildActionText, E as ExpandToggle } from "../../chunks/ExpandToggle.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { css } from "@lit/reactive-element/css-tag.js";
const calculateMaxItems = ({
bufferSize = 0,
containerSize,
itemSizes
}) => {
const maxSize = containerSize - bufferSize;
let breakpoint = itemSizes.length;
let sizeSum = 0;
for (const [index, size] of itemSizes.entries()) {
sizeSum = sizeSum + size;
if (sizeSum > maxSize) {
breakpoint = index;
break;
} else {
continue;
}
}
return breakpoint;
};
const getOverflowCount = ({
bufferSize = 0,
containerSize,
itemSizes
}) => {
return Math.max(itemSizes.length - calculateMaxItems({ bufferSize, itemSizes, containerSize }), 0);
};
const CSS = {
container: "container",
actionGroupEnd: "action-group--end"
};
const SLOTS = {
actionsEnd: "actions-end",
bottomActions: "bottom-actions",
expandTooltip: "expand-tooltip"
};
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{display:inline-flex;align-self:stretch;background:transparent}.container{display:inline-flex;flex:1 1 auto;flex-direction:column;gap:var(--calcite-action-bar-items-space, 0);background-color:var(--calcite-action-bar-background-color, var(--calcite-color-foreground-1))}@keyframes in{0%{opacity:0}to{opacity:1}}:host([floating]) .container{animation:in var(--calcite-internal-animation-timing-slow) ease-in-out;overflow:hidden;border-radius:var(--calcite-action-bar-corner-radius, var(--calcite-corner-radius-round));--tw-shadow: 0 6px 20px -4px rgba(0, 0, 0, .1), 0 4px 12px -2px rgba(0, 0, 0, .08);--tw-shadow-colored: 0 6px 20px -4px var(--tw-shadow-color), 0 4px 12px -2px var(--tw-shadow-color);box-shadow:var(--calcite-action-bar-shadow, var(--tw-ring-offset-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-ring-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-shadow))}:host([layout=vertical]){flex-direction:column}:host([layout=vertical]):host([overflow-actions-disabled]) .container{overflow-y:auto}:host([layout=vertical]):host([expanded]) .container{max-inline-size:var(--calcite-action-bar-expanded-max-width, auto)}:host([layout=vertical]) .action-group--end{margin-block-start:auto}:host([layout=vertical]) ::slotted(calcite-action-group:not(:last-of-type)){border-block-end-width:var(--calcite-border-width-sm)}:host([layout=horizontal]){flex-direction:row}:host([layout=horizontal]) .container{flex-direction:row}:host([layout=horizontal]):host([overflow-actions-disabled]) .container{overflow-x:auto}:host([layout=horizontal]) .action-group--end{margin-inline-start:auto}:host([layout=horizontal]) ::slotted(calcite-action-group:not(:last-of-type)){border-inline-end-width:var(--calcite-border-width-sm)}.action-group--end{justify-content:flex-end}:host([hidden]){display:none}[hidden]{display:none}`;
class ActionBar extends LitElement {
constructor() {
super();
this.mutationObserver = createObserver("mutation", () => this.mutationObserverHandler());
this.resize = debounce(({ width, height }) => {
const { expanded, expandDisabled, layout, overflowActionsDisabled, actionGroups } = this;
if (overflowActionsDisabled || layout === "vertical" && !height || layout === "horizontal" && !width) {
return;
}
const itemSizes = this.getItemSizes();
this.updateGroups();
const groupCount = this.hasActionsEnd || this.hasBottomActions || !expandDisabled ? actionGroups.length + 1 : actionGroups.length;
const overflowCount = getOverflowCount({
bufferSize: groupCount,
// 1px border for each group
containerSize: layout === "horizontal" ? width : height,
itemSizes
});
overflowActions({
actionGroups,
expanded,
overflowCount
});
}, DEBOUNCE.resize);
this.resizeHandler = (entry) => {
const { width, height } = entry.contentRect;
this.resize({ width, height });
};
this.resizeObserver = createObserver("resize", (entries) => this.resizeHandlerEntries(entries));
this.toggleExpand = () => {
this.expanded = !this.expanded;
this.calciteActionBarToggle.emit();
};
this.messages = useT9n();
this.hasActionsEnd = false;
this.hasBottomActions = false;
this.floating = false;
this.expandDisabled = false;
this.expanded = false;
this.layout = "vertical";
this.overflowActionsDisabled = false;
this.overlayPositioning = "absolute";
this.scale = "m";
this.calciteActionBarToggle = createEvent({ cancelable: false });
this.listen("calciteActionMenuOpen", this.actionMenuOpenHandler);
}
static {
this.properties = { expandTooltip: [16, {}, { state: true }], hasActionsEnd: [16, {}, { state: true }], hasBottomActions: [16, {}, { state: true }], actionsEndGroupLabel: 1, floating: [7, {}, { reflect: true, type: Boolean }], expandDisabled: [7, {}, { reflect: true, type: Boolean }], expanded: [7, {}, { reflect: true, type: Boolean }], layout: [3, {}, { reflect: true }], messageOverrides: [0, {}, { attribute: false }], overflowActionsDisabled: [7, {}, { reflect: true, type: Boolean }], overlayPositioning: [3, {}, { reflect: true }], position: [3, {}, { reflect: true }], scale: [3, {}, { reflect: true }] };
}
static {
this.styles = styles;
}
async overflowActions() {
this.resize({ width: this.el.clientWidth, height: this.el.clientHeight });
}
async setFocus() {
await componentFocusable(this);
focusFirstTabbable(this.el);
}
connectedCallback() {
super.connectedCallback();
this.updateGroups();
this.overflowActions();
this.mutationObserver?.observe(this.el, { childList: true, subtree: true });
this.overflowActionsDisabledHandler(this.overflowActionsDisabled);
}
willUpdate(changes) {
if (changes.has("expandDisabled") && (this.hasUpdated || this.expandDisabled !== false)) {
this.overflowActions();
}
if (changes.has("expanded") && this.hasUpdated) {
this.expandedHandler();
}
if (changes.has("layout") && (this.hasUpdated || this.layout !== "vertical")) {
this.updateGroups();
}
if (changes.has("overflowActionsDisabled") && (this.hasUpdated || this.overflowActionsDisabled !== false)) {
this.overflowActionsDisabledHandler(this.overflowActionsDisabled);
}
}
loaded() {
this.overflowActions();
}
disconnectedCallback() {
super.disconnectedCallback();
this.mutationObserver?.disconnect();
this.resizeObserver?.disconnect();
}
getItemSizes() {
const { el, layout, expandToggleEl } = this;
const actions = queryActions(el);
if (expandToggleEl) {
actions.push(expandToggleEl);
}
const clientSize = layout === "horizontal" ? "clientWidth" : "clientHeight";
const fallbackSize = Math.max(...actions.map((action) => action[clientSize] || 0));
return actions.map((action) => action[clientSize] || fallbackSize);
}
expandedHandler() {
const { el, expanded } = this;
toggleChildActionText({ el, expanded });
this.overflowActions();
}
overflowActionsDisabledHandler(overflowActionsDisabled) {
if (overflowActionsDisabled) {
this.resizeObserver?.disconnect();
return;
}
this.resizeObserver?.observe(this.el);
this.overflowActions();
}
actionMenuOpenHandler(event) {
if (event.target.menuOpen) {
const composedPath = event.composedPath();
this.actionGroups?.forEach((group) => {
if (!composedPath.includes(group)) {
group.menuOpen = false;
}
});
}
}
mutationObserverHandler() {
this.updateGroups();
this.overflowActions();
}
resizeHandlerEntries(entries) {
entries.forEach(this.resizeHandler);
}
updateGroups() {
const groups = Array.from(this.el.querySelectorAll("calcite-action-group"));
this.actionGroups = groups;
groups.forEach((group) => {
group.layout = this.layout;
group.scale = this.scale;
});
}
handleDefaultSlotChange() {
this.updateGroups();
}
handleActionsEndSlotChange(event) {
this.hasActionsEnd = slotChangeHasAssignedElement(event);
}
handleBottomActionsSlotChange(event) {
this.hasBottomActions = slotChangeHasAssignedElement(event);
}
handleTooltipSlotChange(event) {
const tooltips = slotChangeGetAssignedElements(event).filter((el) => el?.matches("calcite-tooltip"));
this.expandTooltip = tooltips[0];
}
renderBottomActionGroup() {
const { expanded, expandDisabled, el, position, toggleExpand, scale, layout, messages, actionsEndGroupLabel, overlayPositioning } = this;
const expandToggleNode = !expandDisabled ? ExpandToggle({ collapseLabel: messages.collapseLabel, collapseText: messages.collapse, el, expandLabel: messages.expandLabel, expandText: messages.expand, expanded, position, ref: (el2) => this.expandToggleEl = el2, scale, toggle: toggleExpand, tooltip: this.expandTooltip }) : null;
return html`<calcite-action-group class=${safeClassMap(CSS.actionGroupEnd)} .hidden=${this.expandDisabled && !(this.hasActionsEnd || this.hasBottomActions)} .label=${actionsEndGroupLabel} .layout=${layout} .overlayPositioning=${overlayPositioning} .scale=${scale}><slot name=${SLOTS.actionsEnd} @slotchange=${this.handleActionsEndSlotChange}></slot><slot name=${SLOTS.bottomActions} @slotchange=${this.handleBottomActionsSlotChange}></slot><slot name=${SLOTS.expandTooltip} @slotchange=${this.handleTooltipSlotChange}></slot>${expandToggleNode}</calcite-action-group>`;
}
render() {
return html`<div class=${safeClassMap(CSS.container)}><slot @slotchange=${this.handleDefaultSlotChange}></slot>${this.renderBottomActionGroup()}</div>`;
}
}
customElement("calcite-action-bar", ActionBar);
export {
ActionBar
};