UNPKG

@synergy-design-system/components

Version:

This package provides the base of the Synergy Design System as native web components. It uses [lit](https://www.lit.dev) and parts of [shoelace](https://shoelace.style/). Synergy officially supports the latest two versions of all major browsers (as define

419 lines (414 loc) 14.2 kB
import { popup_styles_default } from "./chunk.AZ3N5IOO.js"; import { LocalizeController } from "./chunk.OAQRCZOO.js"; import { component_styles_default } from "./chunk.NLYVOJGK.js"; import { SynergyElement } from "./chunk.3AZFEB6D.js"; import { __decorateClass, __spreadProps, __spreadValues } from "./chunk.Z4XV3SMG.js"; // src/components/popup/popup.component.ts import { arrow, autoUpdate, computePosition, flip, offset, platform, shift, size } from "@floating-ui/dom"; import { classMap } from "lit/directives/class-map.js"; import { html } from "lit"; import { offsetParent } from "composed-offset-position"; import { property, query } from "lit/decorators.js"; function isVirtualElement(e) { return e !== null && typeof e === "object" && "getBoundingClientRect" in e && ("contextElement" in e ? e.contextElement instanceof Element : true); } var SynPopup = class extends SynergyElement { constructor() { super(...arguments); this.localize = new LocalizeController(this); this.active = false; this.placement = "top"; this.strategy = "absolute"; this.distance = 0; this.skidding = 0; this.arrow = false; this.arrowPlacement = "anchor"; this.arrowPadding = 10; this.flip = false; this.flipFallbackPlacements = ""; this.flipFallbackStrategy = "best-fit"; this.flipPadding = 0; this.shift = false; this.shiftPadding = 0; this.autoSizePadding = 0; this.hoverBridge = false; this.updateHoverBridge = () => { if (this.hoverBridge && this.anchorEl) { const anchorRect = this.anchorEl.getBoundingClientRect(); const popupRect = this.popup.getBoundingClientRect(); const isVertical = this.placement.includes("top") || this.placement.includes("bottom"); let topLeftX = 0; let topLeftY = 0; let topRightX = 0; let topRightY = 0; let bottomLeftX = 0; let bottomLeftY = 0; let bottomRightX = 0; let bottomRightY = 0; if (isVertical) { if (anchorRect.top < popupRect.top) { topLeftX = anchorRect.left; topLeftY = anchorRect.bottom; topRightX = anchorRect.right; topRightY = anchorRect.bottom; bottomLeftX = popupRect.left; bottomLeftY = popupRect.top; bottomRightX = popupRect.right; bottomRightY = popupRect.top; } else { topLeftX = popupRect.left; topLeftY = popupRect.bottom; topRightX = popupRect.right; topRightY = popupRect.bottom; bottomLeftX = anchorRect.left; bottomLeftY = anchorRect.top; bottomRightX = anchorRect.right; bottomRightY = anchorRect.top; } } else { if (anchorRect.left < popupRect.left) { topLeftX = anchorRect.right; topLeftY = anchorRect.top; topRightX = popupRect.left; topRightY = popupRect.top; bottomLeftX = anchorRect.right; bottomLeftY = anchorRect.bottom; bottomRightX = popupRect.left; bottomRightY = popupRect.bottom; } else { topLeftX = popupRect.right; topLeftY = popupRect.top; topRightX = anchorRect.left; topRightY = anchorRect.top; bottomLeftX = popupRect.right; bottomLeftY = popupRect.bottom; bottomRightX = anchorRect.left; bottomRightY = anchorRect.bottom; } } this.style.setProperty("--hover-bridge-top-left-x", `${topLeftX}px`); this.style.setProperty("--hover-bridge-top-left-y", `${topLeftY}px`); this.style.setProperty("--hover-bridge-top-right-x", `${topRightX}px`); this.style.setProperty("--hover-bridge-top-right-y", `${topRightY}px`); this.style.setProperty("--hover-bridge-bottom-left-x", `${bottomLeftX}px`); this.style.setProperty("--hover-bridge-bottom-left-y", `${bottomLeftY}px`); this.style.setProperty("--hover-bridge-bottom-right-x", `${bottomRightX}px`); this.style.setProperty("--hover-bridge-bottom-right-y", `${bottomRightY}px`); } }; } async connectedCallback() { super.connectedCallback(); await this.updateComplete; this.start(); } disconnectedCallback() { super.disconnectedCallback(); this.stop(); } async updated(changedProps) { super.updated(changedProps); if (changedProps.has("active")) { if (this.active) { this.start(); } else { this.stop(); } } if (changedProps.has("anchor")) { this.handleAnchorChange(); } if (this.active) { await this.updateComplete; this.reposition(); } } async handleAnchorChange() { await this.stop(); if (this.anchor && typeof this.anchor === "string") { const root = this.getRootNode(); this.anchorEl = root.getElementById(this.anchor); } else if (this.anchor instanceof Element || isVirtualElement(this.anchor)) { this.anchorEl = this.anchor; } else { this.anchorEl = this.querySelector('[slot="anchor"]'); } if (this.anchorEl instanceof HTMLSlotElement) { this.anchorEl = this.anchorEl.assignedElements({ flatten: true })[0]; } if (this.anchorEl && this.active) { this.start(); } } start() { if (!this.anchorEl || !this.active) { return; } this.cleanup = autoUpdate(this.anchorEl, this.popup, () => { this.reposition(); }); } async stop() { return new Promise((resolve) => { if (this.cleanup) { this.cleanup(); this.cleanup = void 0; this.removeAttribute("data-current-placement"); this.style.removeProperty("--auto-size-available-width"); this.style.removeProperty("--auto-size-available-height"); requestAnimationFrame(() => resolve()); } else { resolve(); } }); } /** Forces the popup to recalculate and reposition itself. */ reposition() { if (!this.active || !this.anchorEl) { return; } const middleware = [ // The offset middleware goes first offset({ mainAxis: this.distance, crossAxis: this.skidding }) ]; if (this.sync) { middleware.push( size({ apply: ({ rects }) => { const syncWidth = this.sync === "width" || this.sync === "both"; const syncHeight = this.sync === "height" || this.sync === "both"; this.popup.style.width = syncWidth ? `${rects.reference.width}px` : ""; this.popup.style.height = syncHeight ? `${rects.reference.height}px` : ""; } }) ); } else { this.popup.style.width = ""; this.popup.style.height = ""; } if (this.flip) { middleware.push( flip({ boundary: this.flipBoundary, // @ts-expect-error - We're converting a string attribute to an array here fallbackPlacements: this.flipFallbackPlacements, fallbackStrategy: this.flipFallbackStrategy === "best-fit" ? "bestFit" : "initialPlacement", padding: this.flipPadding }) ); } if (this.shift) { middleware.push( shift({ boundary: this.shiftBoundary, padding: this.shiftPadding }) ); } if (this.autoSize) { middleware.push( size({ boundary: this.autoSizeBoundary, padding: this.autoSizePadding, apply: ({ availableWidth, availableHeight }) => { if (this.autoSize === "vertical" || this.autoSize === "both") { this.style.setProperty("--auto-size-available-height", `${availableHeight}px`); } else { this.style.removeProperty("--auto-size-available-height"); } if (this.autoSize === "horizontal" || this.autoSize === "both") { this.style.setProperty("--auto-size-available-width", `${availableWidth}px`); } else { this.style.removeProperty("--auto-size-available-width"); } } }) ); } else { this.style.removeProperty("--auto-size-available-width"); this.style.removeProperty("--auto-size-available-height"); } if (this.arrow) { middleware.push( arrow({ element: this.arrowEl, padding: this.arrowPadding }) ); } const getOffsetParent = this.strategy === "absolute" ? (element) => platform.getOffsetParent(element, offsetParent) : platform.getOffsetParent; computePosition(this.anchorEl, this.popup, { placement: this.placement, middleware, strategy: this.strategy, platform: __spreadProps(__spreadValues({}, platform), { getOffsetParent }) }).then(({ x, y, middlewareData, placement }) => { const isRtl = this.localize.dir() === "rtl"; const staticSide = { top: "bottom", right: "left", bottom: "top", left: "right" }[placement.split("-")[0]]; this.setAttribute("data-current-placement", placement); Object.assign(this.popup.style, { left: `${x}px`, top: `${y}px` }); if (this.arrow) { const arrowX = middlewareData.arrow.x; const arrowY = middlewareData.arrow.y; let top = ""; let right = ""; let bottom = ""; let left = ""; if (this.arrowPlacement === "start") { const value = typeof arrowX === "number" ? `calc(${this.arrowPadding}px - var(--arrow-padding-offset))` : ""; top = typeof arrowY === "number" ? `calc(${this.arrowPadding}px - var(--arrow-padding-offset))` : ""; right = isRtl ? value : ""; left = isRtl ? "" : value; } else if (this.arrowPlacement === "end") { const value = typeof arrowX === "number" ? `calc(${this.arrowPadding}px - var(--arrow-padding-offset))` : ""; right = isRtl ? "" : value; left = isRtl ? value : ""; bottom = typeof arrowY === "number" ? `calc(${this.arrowPadding}px - var(--arrow-padding-offset))` : ""; } else if (this.arrowPlacement === "center") { left = typeof arrowX === "number" ? `calc(50% - var(--arrow-size-diagonal))` : ""; top = typeof arrowY === "number" ? `calc(50% - var(--arrow-size-diagonal))` : ""; } else { left = typeof arrowX === "number" ? `${arrowX}px` : ""; top = typeof arrowY === "number" ? `${arrowY}px` : ""; } Object.assign(this.arrowEl.style, { top, right, bottom, left, [staticSide]: "calc(var(--arrow-size-diagonal) * -1)" }); } }); requestAnimationFrame(() => this.updateHoverBridge()); this.emit("syn-reposition"); } render() { return html` <slot name="anchor" @slotchange=${this.handleAnchorChange}></slot> <span part="hover-bridge" class=${classMap({ "popup-hover-bridge": true, "popup-hover-bridge--visible": this.hoverBridge && this.active })} ></span> <div part="popup" class=${classMap({ popup: true, "popup--active": this.active, "popup--fixed": this.strategy === "fixed", "popup--has-arrow": this.arrow })} > <slot></slot> ${this.arrow ? html`<div part="arrow" class="popup__arrow" role="presentation"></div>` : ""} </div> `; } }; SynPopup.styles = [component_styles_default, popup_styles_default]; __decorateClass([ query(".popup") ], SynPopup.prototype, "popup", 2); __decorateClass([ query(".popup__arrow") ], SynPopup.prototype, "arrowEl", 2); __decorateClass([ property() ], SynPopup.prototype, "anchor", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], SynPopup.prototype, "active", 2); __decorateClass([ property({ reflect: true }) ], SynPopup.prototype, "placement", 2); __decorateClass([ property({ reflect: true }) ], SynPopup.prototype, "strategy", 2); __decorateClass([ property({ type: Number }) ], SynPopup.prototype, "distance", 2); __decorateClass([ property({ type: Number }) ], SynPopup.prototype, "skidding", 2); __decorateClass([ property({ type: Boolean }) ], SynPopup.prototype, "arrow", 2); __decorateClass([ property({ attribute: "arrow-placement" }) ], SynPopup.prototype, "arrowPlacement", 2); __decorateClass([ property({ attribute: "arrow-padding", type: Number }) ], SynPopup.prototype, "arrowPadding", 2); __decorateClass([ property({ type: Boolean }) ], SynPopup.prototype, "flip", 2); __decorateClass([ property({ attribute: "flip-fallback-placements", converter: { fromAttribute: (value) => { return value.split(" ").map((p) => p.trim()).filter((p) => p !== ""); }, toAttribute: (value) => { return value.join(" "); } } }) ], SynPopup.prototype, "flipFallbackPlacements", 2); __decorateClass([ property({ attribute: "flip-fallback-strategy" }) ], SynPopup.prototype, "flipFallbackStrategy", 2); __decorateClass([ property({ type: Object }) ], SynPopup.prototype, "flipBoundary", 2); __decorateClass([ property({ attribute: "flip-padding", type: Number }) ], SynPopup.prototype, "flipPadding", 2); __decorateClass([ property({ type: Boolean }) ], SynPopup.prototype, "shift", 2); __decorateClass([ property({ type: Object }) ], SynPopup.prototype, "shiftBoundary", 2); __decorateClass([ property({ attribute: "shift-padding", type: Number }) ], SynPopup.prototype, "shiftPadding", 2); __decorateClass([ property({ attribute: "auto-size" }) ], SynPopup.prototype, "autoSize", 2); __decorateClass([ property() ], SynPopup.prototype, "sync", 2); __decorateClass([ property({ type: Object }) ], SynPopup.prototype, "autoSizeBoundary", 2); __decorateClass([ property({ attribute: "auto-size-padding", type: Number }) ], SynPopup.prototype, "autoSizePadding", 2); __decorateClass([ property({ attribute: "hover-bridge", type: Boolean }) ], SynPopup.prototype, "hoverBridge", 2); export { SynPopup }; //# sourceMappingURL=chunk.6CC5CH2P.js.map