UNPKG

wj-elements

Version:

WebJET Elements is a modern set of user interface tools harnessing the power of web components designed to simplify web application development.

1,183 lines 60.7 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import WJElement from "./wje-element.js"; import { event } from "./event.js"; const styles = ":host {\n display: block;\n z-index: var(--wje-sliding-container-z-index, 1000);\n position: relative;\n height: 100%;\n right: unset;\n left: unset;\n .close-button {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n }\n}\n\n:host([hide-button]) {\n .close-button {\n display: none;\n }\n}\n\n.sliding-container-backdrop {\n display: none;\n}\n\n.sliding-container-wrapper {\n height: 100%;\n position: relative;\n overflow: hidden;\n}\n\n.native-sliding-container {\n\n position: absolute;\n width: 0;\n height: 100%;\n}\n\n.native-sliding-container-inner {\n height: 100%;\n position: absolute;\n}\n\n.sliding-container-sheet-handle-area {\n display: none;\n}\n\n:host(.bottom-sheet) {\n inset: 0;\n pointer-events: none;\n z-index: var(--wje-sliding-container-bottom-sheet-z-index, var(--wje-sliding-container-z-index, 1000));\n}\n\n:host(.bottom-sheet.open) {\n overscroll-behavior: contain;\n}\n\n:host(.bottom-sheet) .sliding-container-backdrop {\n display: none;\n position: fixed;\n inset: 0;\n z-index: var(--wje-sliding-container-bottom-sheet-backdrop-z-index, 0);\n opacity: 0;\n pointer-events: none;\n touch-action: none;\n overscroll-behavior: none;\n background: var(--wje-sliding-container-backdrop-background, var(--wje-backdrop, rgba(0, 0, 0, .35)));\n}\n\n:host(.bottom-sheet.open) .sliding-container-backdrop {\n pointer-events: auto;\n}\n\n:host(.bottom-sheet) .sliding-container-wrapper {\n position: fixed;\n inset: 0;\n overflow: visible;\n pointer-events: none;\n z-index: var(--wje-sliding-container-bottom-sheet-panel-z-index, 1);\n}\n\n:host(.bottom-sheet) .sliding-container-transparent {\n display: none;\n}\n\n:host(.bottom-sheet) .native-sliding-container {\n position: fixed;\n top: auto;\n height: fit-content;\n overflow: hidden;\n pointer-events: auto;\n display: flex;\n flex-direction: column;\n overscroll-behavior: contain;\n background: var(--wje-sliding-container-background);\n box-shadow: var(--wje-sliding-container-box-shadow, var(--wje-sliding-container-shadow));\n border-radius: var(--wje-sliding-container-sheet-border-radius, var(--wje-sliding-container-border-radius) var(--wje-sliding-container-border-radius) 0 0);\n}\n\n:host(.bottom-sheet) .native-sliding-container-inner {\n position: relative;\n flex: 1 1 auto;\n min-height: 0;\n height: auto;\n max-height: inherit;\n overflow: auto;\n overscroll-behavior: contain;\n}\n\n:host(.bottom-sheet) .sliding-container-sheet-handle-area {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 var(--wje-sliding-container-sheet-handle-area-height, 24px);\n height: var(--wje-sliding-container-sheet-handle-area-height, 24px);\n cursor: grab;\n pointer-events: auto;\n touch-action: none;\n user-select: none;\n -webkit-user-select: none;\n}\n\n:host(.bottom-sheet) .sliding-container-sheet-handle-area:active {\n cursor: grabbing;\n}\n\n:host(.bottom-sheet) .sliding-container-sheet-handle {\n width: var(--wje-sliding-container-sheet-handle-width, 36px);\n height: var(--wje-sliding-container-sheet-handle-height, 2px);\n background: var(--wje-sliding-container-sheet-handle-background, var(--wje-border-color));\n border-radius: var(--wje-sliding-container-sheet-handle-radius, 999px);\n}\n"; class SlidingContainer extends WJElement { /** * Creates an instance of SlidingContainer. * @class */ constructor() { super(); __publicField(this, "className", "SlidingContainer"); /** * Handles backdrop click dismissal. * @param {MouseEvent} e The click event. */ __publicField(this, "handleBackdropClick", (e) => { if (this.isEventInsideSheet(e) || Date.now() < this._sheetIgnoreDismissUntil || Date.now() < this._sheetBackdropHandledUntil) { e.preventDefault(); e.stopPropagation(); return; } if (this.isBackdropElement(e.target)) { this.close(e); } }); /** * Stops a handle interaction from dismissing the bottom sheet. * @param {Event} e The interaction emitted by the resize handle. */ __publicField(this, "stopSheetHandleEvent", (e) => { var _a; if (!this.isBottomSheet()) return; this._sheetIgnoreDismissUntil = Date.now() + 500; e.preventDefault(); e.stopPropagation(); (_a = e.stopImmediatePropagation) == null ? void 0 : _a.call(e); }); /** * Prevents scroll events from reaching the page behind the bottom sheet. * @param {Event} e The scroll event. */ __publicField(this, "preventBottomSheetScroll", (e) => { if (!this.isBottomSheet() || !this.classList.contains("open")) return; e.preventDefault(); e.stopPropagation(); }); /** * Starts mobile bottom sheet resizing. * @param {PointerEvent} e The pointer event. */ __publicField(this, "handleSheetDragStart", (e) => { var _a, _b; if (!this.sheetResizable || !this.isBottomSheet()) return; e.preventDefault(); e.stopPropagation(); (_a = e.stopImmediatePropagation) == null ? void 0 : _a.call(e); this._sheetIgnoreDismissUntil = Date.now() + 500; if (this._sheetDrag) return; this.startSheetDrag(e.pointerId, e.clientY); try { (_b = this.sheetHandleArea) == null ? void 0 : _b.setPointerCapture(e.pointerId); } catch { } window.addEventListener("pointermove", this.handleSheetDragMove); window.addEventListener("pointerup", this.handleSheetDragEnd); window.addEventListener("pointercancel", this.handleSheetDragEnd); }); /** * Starts mobile bottom sheet resizing from a touch event. * @param {TouchEvent} e The touch event. */ __publicField(this, "handleSheetTouchStart", (e) => { var _a, _b; if (!this.sheetResizable || !this.isBottomSheet() || this._sheetDrag) return; const touch = (_a = e.touches) == null ? void 0 : _a[0]; if (!touch) return; e.preventDefault(); e.stopPropagation(); (_b = e.stopImmediatePropagation) == null ? void 0 : _b.call(e); this._sheetIgnoreDismissUntil = Date.now() + 500; this.startSheetDrag("touch", touch.clientY); window.addEventListener("touchmove", this.handleSheetTouchMove, { passive: false }); window.addEventListener("touchend", this.handleSheetTouchEnd, { passive: false }); window.addEventListener("touchcancel", this.handleSheetTouchEnd, { passive: false }); }); /** * Starts tracking a possible backdrop dismiss tap. * @param {PointerEvent} e The pointer event. */ __publicField(this, "handleBackdropPointerStart", (e) => { if (!this.isBottomSheet() || !this.isBackdropElement(e.target) || this.isEventInsideSheet(e) || Date.now() < this._sheetIgnoreDismissUntil) { this._sheetBackdropGesture = null; return; } this._sheetBackdropGesture = { pointerId: e.pointerId, startX: e.clientX, startY: e.clientY, hasMoved: false }; }); /** * Tracks movement during a possible backdrop dismiss tap. * @param {PointerEvent} e The pointer event. */ __publicField(this, "handleBackdropPointerMove", (e) => { if (!this._sheetBackdropGesture || e.pointerId !== this._sheetBackdropGesture.pointerId) return; const distanceX = Math.abs(e.clientX - this._sheetBackdropGesture.startX); const distanceY = Math.abs(e.clientY - this._sheetBackdropGesture.startY); if (distanceX > 8 || distanceY > 8) { this._sheetBackdropGesture.hasMoved = true; } }); /** * Finishes a possible backdrop dismiss tap. * @param {PointerEvent} e The pointer event. */ __publicField(this, "handleBackdropPointerEnd", (e) => { if (!this._sheetBackdropGesture || e.pointerId !== this._sheetBackdropGesture.pointerId) return; const shouldClose = !this._sheetBackdropGesture.hasMoved && this.isBackdropElement(e.target) && !this.isEventInsideSheet(e) && Date.now() >= this._sheetIgnoreDismissUntil; this._sheetBackdropGesture = null; this._sheetBackdropHandledUntil = Date.now() + 350; if (shouldClose) { e.preventDefault(); e.stopPropagation(); this.close(e); } }); /** * Cancels a possible backdrop dismiss tap. */ __publicField(this, "handleBackdropPointerCancel", () => { this._sheetBackdropGesture = null; this._sheetBackdropHandledUntil = Date.now() + 350; }); /** * Resizes the mobile bottom sheet during dragging. * @param {PointerEvent} e The pointer event. */ __publicField(this, "handleSheetDragMove", (e) => { var _a; if (!this._sheetDrag || e.pointerId !== this._sheetDrag.pointerId) return; e.preventDefault(); e.stopPropagation(); (_a = e.stopImmediatePropagation) == null ? void 0 : _a.call(e); this.updateSheetDrag(e.clientY); }); /** * Resizes the mobile bottom sheet during touch dragging. * @param {TouchEvent} e The touch event. */ __publicField(this, "handleSheetTouchMove", (e) => { var _a, _b; if (!this._sheetDrag || this._sheetDrag.pointerId !== "touch") return; const touch = (_a = e.touches) == null ? void 0 : _a[0]; if (!touch) return; e.preventDefault(); e.stopPropagation(); (_b = e.stopImmediatePropagation) == null ? void 0 : _b.call(e); this.updateSheetDrag(touch.clientY); }); /** * Ends mobile bottom sheet resizing. * @param {PointerEvent} e The pointer event. */ __publicField(this, "handleSheetDragEnd", (e) => { var _a, _b, _c; const hasMoved = (_a = this._sheetDrag) == null ? void 0 : _a.hasMoved; if (this._sheetDrag && (e == null ? void 0 : e.pointerId) === this._sheetDrag.pointerId) { e.preventDefault(); e.stopPropagation(); (_b = e.stopImmediatePropagation) == null ? void 0 : _b.call(e); try { (_c = this.sheetHandleArea) == null ? void 0 : _c.releasePointerCapture(e.pointerId); } catch { } } if (hasMoved) { this._sheetIgnoreDismissUntil = Date.now() + 350; } else { this._sheetIgnoreDismissUntil = Date.now() + 500; } this.endSheetDrag(); }); /** * Ends mobile bottom sheet resizing from a touch event. * @param {TouchEvent} e The touch event. */ __publicField(this, "handleSheetTouchEnd", (e) => { var _a; if (!this._sheetDrag || this._sheetDrag.pointerId !== "touch") return; const hasMoved = this._sheetDrag.hasMoved; if (e.type !== "touchcancel") { e.preventDefault(); } e.stopPropagation(); if (e.type !== "touchcancel") { (_a = e.stopImmediatePropagation) == null ? void 0 : _a.call(e); } this._sheetIgnoreDismissUntil = Date.now() + (hasMoved ? 350 : 700); this.endSheetDrag(); }); /** * Triggers the event based on the target element. * If the target element is different from the last caller, it refreshes the children by calling the `open` method. * If the target element is the same as the last caller, it toggles the state by calling the `toggle` method. * @param {Event} e The event object. */ __publicField(this, "triggerEvent", async (e) => { if (this._lastCaller && this._lastCaller !== e.composedPath()[0]) { await this.open(e); } else { await this.toggle(e); } this._lastCaller = e.composedPath()[0]; }); this._isOpen = false; this._lastCaller = null; this._sheetDrag = null; this._sheetIgnoreDismissUntil = 0; this._sheetBackdropGesture = null; this._sheetBackdropHandledUntil = 0; this._bottomSheetScopeFrame = null; this._resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { if (entry.contentBoxSize) { if (this.drawingStatus < 3) return; if (this.isBottomSheet()) { this.checkForVariant(this.variant); } else if (this.screenBreakPoint && window.innerWidth <= this.screenBreakPoint) { if (this.variant !== "over") { this.variant = "over"; } else { this.checkForVariant(this.variant); } } else { if (this.variant !== "in-place") { this.variant = "in-place"; } else { this.checkForVariant(this.variant); } } } } }); this._resizeObserver.observe(document.documentElement); } /** * Sets the maximum width of an element by updating the 'max-width' attribute. * @param {string} value The maximum width value to be set (e.g., '100px', '50%', etc.). */ set maxWidth(value) { this.setAttribute("max-width", value); } /** * Gets the maximum width value of the element. * Retrieves the value of the 'max-width' attribute. If the attribute is not set, it defaults to 'auto'. * @returns {string} The maximum width value of the element or 'auto' if the attribute is not defined. */ get maxWidth() { return this.getAttribute("max-width") ?? "auto"; } /** * Sets the maximum height for the element. * @param {string} value The maximum height value to be applied to the element. This can include units such as "px", "em", "%", etc. */ set maxHeight(value) { this.setAttribute("max-height", value); } /** * Retrieves the maximum height value of the element, or returns 'auto' if not set. * @returns {string} The maximum height value or 'auto' if the attribute is not specified. */ get maxHeight() { return this.getAttribute("max-height") ?? "auto"; } /** * Sets the 'trigger' attribute for the element. * @param {string} value The value to set for the 'trigger' attribute. */ set trigger(value) { this.setAttribute("trigger", value); } /** * Retrieves the value of the 'trigger' attribute. If the attribute is not set, it defaults to 'sliding-container'. * @returns {string} The value of the 'trigger' attribute or the default value 'sliding-container' if not defined. */ get trigger() { return this.getAttribute("trigger") ?? "sliding-container"; } /** * Sets the direction attribute for the element. * @param {string} value The direction value to be assigned. Possible values are typically 'ltr' (left-to-right), 'rtl' (right-to-left), or 'auto'. */ set direction(value) { this.setAttribute("direction", value); } /** * Retrieves the direction attribute of the instance. * If the direction attribute is not set, it defaults to 'right'. * @returns {string} The value of the direction attribute or 'right' if not set. */ get direction() { return this.getAttribute("direction") ?? "right"; } /** * Sets the value of the `remove-child-after-close` attribute. * This attribute determines if a child element should be removed after a close operation. * @param {boolean|string} value The value to set for the `remove-child-after-close` attribute. The value can be a boolean or a string representation of a boolean. */ set removeChildAfterClose(value) { this.setAttribute("remove-child-after-close", value); } /** * Gets the value indicating whether the child element should be removed after closing. * * This property checks the presence of the 'remove-child-after-close' attribute on the element. * Returns `false` if the attribute does not exist. * @returns {boolean} True if the 'remove-child-after-close' attribute is present, otherwise false. */ get removeChildAfterClose() { return this.hasAttribute("remove-child-after-close") ?? false; } /** * Sets the 'variant' attribute to the specified value. * @param {string} value The value to set for the 'variant' attribute. */ set variant(value) { this.setAttribute("variant", value); } /** * Retrieves the value of the "variant" attribute. If the attribute is not set, * it returns the default value 'in-place'. * @returns {string} The variant value or the default value 'in-place'. */ get variant() { return this.getAttribute("variant") ?? "in-place"; } /** * Retrieves the value of the 'screen-break-point' attribute. * @returns {string} The value of the 'screen-break-point' attribute. */ get screenBreakPoint() { return this.getAttribute("screen-break-point"); } /** * Sets the screen break point value to determine responsive behavior. * @param {string} value The value to set as the screen break point. */ set screenBreakPoint(value) { this.setAttribute("screen-break-point", value); } /** * Sets mobile presentation mode. * @param {string} value The mobile presentation mode. */ set mobilePresentation(value) { this.setAttribute("mobile-presentation", value); } /** * Gets mobile presentation mode. * @returns {string|null} The mobile presentation mode. */ get mobilePresentation() { return this.getAttribute("mobile-presentation"); } /** * Sets the breakpoint for mobile presentation. * @param {string} value The mobile breakpoint. */ set mobileBreakPoint(value) { this.setAttribute("mobile-break-point", value); } /** * Gets the breakpoint for mobile presentation. * @returns {string} The mobile breakpoint. */ get mobileBreakPoint() { return this.getAttribute("mobile-break-point") || "768"; } /** * Sets the duration of the animation by updating the `animation-duration` attribute. * @param {string} value The duration value for the animation, specified in a format * such as seconds (e.g., "2s") or milliseconds (e.g., "200ms"). */ set animationDuration(value) { this.setAttribute("animation-duration", value); } /** * Gets the animation duration for an element. * It retrieves the value of the 'animation-duration' attribute if present; otherwise, it defaults to '500'. * @returns {string} The value of the animation duration, either from the attribute or the default '500'. */ get animationDuration() { return this.getAttribute("animation-duration") ?? "500"; } /** * Sets the easing function for the animation. * @param {string} value The easing function to use for the animation. This can be any valid CSS timing function such as "ease", "linear", "ease-in", "ease-out", etc. */ set animationEasing(value) { this.setAttribute("animation-easing", value); } /** * Retrieves the easing function for the animation. * @returns {string} The value of the 'animation-easing' attribute if set, otherwise defaults to 'linear'. */ get animationEasing() { return this.getAttribute("animation-easing") ?? "linear"; } /** * Determines if the element has an 'has-opacity' attribute. * @returns {boolean} True if the element has the 'has-opacity' attribute, otherwise false. */ get hasOpacity() { return this.hasAttribute("has-opacity") ?? false; } /** * Determines if the bottom sheet should close when the backdrop is clicked. * @returns {boolean} True if backdrop dismiss is enabled. */ get backdropDismiss() { return this.hasAttribute("backdrop-dismiss") ?? false; } /** * Enables resizing for the mobile bottom sheet. * @returns {boolean} True if the sheet can be resized. */ get sheetResizable() { return this.hasAttribute("sheet-resizable") ?? false; } /** * Sets resizing for the mobile bottom sheet. * @param {boolean|string} value The value to set. */ set sheetResizable(value) { this.setAttribute("sheet-resizable", value); } /** * Gets the mobile bottom sheet panel scope. The backdrop always covers the viewport. * @returns {string} Either 'viewport', 'container', or 'parent'. */ get sheetScope() { return this.getAttribute("sheet-scope") || "viewport"; } /** * Sets the mobile bottom sheet panel scope. * @param {string} value The new boundary mode for the mobile sheet. */ set sheetScope(value) { this.setAttribute("sheet-scope", value); } /** * Gets the selector used for container-scoped mobile bottom sheet panels. * @returns {string|null} A CSS selector used to find the composed boundary ancestor. */ get sheetBoundary() { return this.getAttribute("sheet-boundary"); } /** * Sets the selector used for container-scoped mobile bottom sheet panels. * @param {string} value Selector for the boundary element. */ set sheetBoundary(value) { this.setAttribute("sheet-boundary", value); } /** * Gets the mobile bottom sheet opening height. * @returns {string|null} The opening height. */ get sheetHeight() { return this.getAttribute("sheet-height"); } /** * Sets the mobile bottom sheet opening height. * @param {string} value The opening height. */ set sheetHeight(value) { this.setAttribute("sheet-height", value); } /** * Gets the minimum mobile bottom sheet height while resizing. * @returns {string|null} The minimum height. */ get sheetMinHeight() { return this.getAttribute("sheet-min-height"); } /** * Sets the minimum mobile bottom sheet height while resizing. * @param {string} value The minimum height. */ set sheetMinHeight(value) { this.setAttribute("sheet-min-height", value); } /** * Gets the maximum mobile bottom sheet height while resizing. * @returns {string|null} The maximum height. */ get sheetMaxHeight() { return this.getAttribute("sheet-max-height"); } /** * Sets the maximum mobile bottom sheet height while resizing. * @param {string} value The maximum height. */ set sheetMaxHeight(value) { this.setAttribute("sheet-max-height", value); } /** * Sets the value of the 'add-to-height' attribute. * This attribute is used to modify or adjust the height dynamically. * @param {string} value The value to be assigned to the 'add-to-height' attribute. */ set addToHeight(value) { this.setAttribute("add-to-height", value); } /** * Retrieves the value of the 'add-to-height' attribute from the element. * If the attribute is not set, it defaults to '0'. * @returns {string} The value of the 'add-to-height' attribute or '0' if the attribute is not present. */ get addToHeight() { return this.getAttribute("add-to-height") ?? "0"; } /** * Determines whether the current state is open. * @returns {boolean} True if the state is open, otherwise false. */ get isOpen() { return this._isOpen; } /** * Returns the observed attributes for the component. * @returns {string[]} */ static get observedAttributes() { return ["max-width", "max-height", "trigger", "direction", "variant", "screen-break-point", "remove-child-after-close", "animation-duration", "animation-easing", "has-opacity", "mobile-presentation", "mobile-break-point", "backdrop-dismiss", "sheet-resizable", "sheet-scope", "sheet-boundary", "sheet-height", "sheet-min-height", "sheet-max-height"]; } /** * Returns the CSS styles for the component. * @static * @returns {CSSStyleSheet} */ static get cssStyleSheet() { return styles; } /** * Sets up the attributes for the component. */ setupAttributes() { this.isShadowRoot = "open"; this.syncAria(); } /** * Executes before drawing the element. */ beforeDraw() { var _a, _b; (_a = this.animation) == null ? void 0 : _a.cancel(); (_b = this.nativeAnimation) == null ? void 0 : _b.cancel(); this.endSheetDrag(); this.removePortalBackdrop(); document.removeEventListener(this.trigger, this.triggerEvent); } /** * Cleans up global listeners and portaled layers. */ afterDisconnect() { var _a, _b, _c; (_a = this.animation) == null ? void 0 : _a.cancel(); (_b = this.nativeAnimation) == null ? void 0 : _b.cancel(); this.endSheetDrag(); this.removePortalBackdrop(); (_c = this._resizeObserver) == null ? void 0 : _c.disconnect(); document.removeEventListener(this.trigger, this.triggerEvent); } /** * Draws the component. * @param {object} context The context for drawing. * @param {object} store The store for drawing. * @param {object} params The parameters for drawing. * @returns {DocumentFragment} */ draw(context, store, params) { let fragment = document.createDocumentFragment(); const isBottomSheet = this.isBottomSheet(); let backdrop = document.createElement("div"); backdrop.setAttribute("part", "backdrop"); backdrop.classList.add("sliding-container-backdrop"); if (this._isOpen && isBottomSheet) { backdrop.style.opacity = "var(--wje-sliding-container-backdrop-opacity, 1)"; } this.addBackdropListeners(backdrop); let wrapperDiv = document.createElement("div"); wrapperDiv.classList.add("sliding-container-wrapper"); let transparentDiv = document.createElement("div"); transparentDiv.classList.add("sliding-container-transparent"); if (this._isOpen && !isBottomSheet) { transparentDiv.style.width = this.maxWidth; } let native = document.createElement("div"); native.setAttribute("part", "sliding-container"); native.classList.add("native-sliding-container"); native.style.width = isBottomSheet ? "100%" : 0; if (this.hasOpacity) { native.style.opacity = 0; } if (this._isOpen) { native.style.width = isBottomSheet ? "100%" : this.maxWidth; if (isBottomSheet) { native.style.transform = "translateY(0)"; } if (this.hasOpacity) { native.style.opacity = 1; } } else if (isBottomSheet) { native.style.transform = "translateY(100%)"; } if (isBottomSheet) { native.style.left = 0; native.style.right = 0; native.style.bottom = 0; native.style.maxHeight = this.maxHeight; } else if (this.direction === "right") { native.style.right = 0; } else { native.style.left = 0; } const nativeInner = document.createElement("div"); nativeInner.classList.add("native-sliding-container-inner"); nativeInner.style.width = isBottomSheet ? "100%" : this.maxWidth; if (isBottomSheet) { nativeInner.style.maxHeight = this.maxHeight; } let slot = document.createElement("slot"); this.sheetHandleArea = null; nativeInner.append(slot); nativeInner.append(this.htmlCloseButton()); if (this.sheetResizable) { native.append(this.htmlSheetHandle()); } native.append(nativeInner); wrapperDiv.append(transparentDiv); wrapperDiv.append(native); fragment.append(backdrop); fragment.append(wrapperDiv); this.backdropElement = backdrop; this.transparentDiv = transparentDiv; this.wrapperDiv = wrapperDiv; this.nativeElement = native; this.nativeInner = nativeInner; return fragment; } /** * Performs actions after the element is drawn on the screen. * Attaches an event listener to the document based on the specified trigger. * Sets the variant to "over" if the document width is smaller than the screen break point. * Calls the checkForVariant method with the current variant. */ afterDraw() { this.syncAria(); document.addEventListener(this.trigger, this.triggerEvent); if (!this.isBottomSheet() && this.screenBreakPoint && window.innerWidth <= this.screenBreakPoint) { this.variant = "over"; } this.checkForVariant(this.variant); } /** * Returns whether the mobile bottom sheet presentation is active. * @returns {boolean} */ isBottomSheet() { return this.mobilePresentation === "bottom-sheet" && window.innerWidth <= +this.mobileBreakPoint; } /** * Adds interaction listeners to a backdrop element. * @param {HTMLElement} backdrop The backdrop element. */ addBackdropListeners(backdrop) { backdrop.addEventListener("wheel", this.preventBottomSheetScroll, { passive: false }); backdrop.addEventListener("touchmove", this.preventBottomSheetScroll, { passive: false }); if (!this.backdropDismiss) return; backdrop.addEventListener("pointerdown", this.handleBackdropPointerStart); backdrop.addEventListener("pointermove", this.handleBackdropPointerMove); backdrop.addEventListener("pointerup", this.handleBackdropPointerEnd); backdrop.addEventListener("pointercancel", this.handleBackdropPointerCancel); backdrop.addEventListener("click", this.handleBackdropClick); } /** * Removes interaction listeners from a backdrop element. * @param {HTMLElement} backdrop The backdrop element. */ removeBackdropListeners(backdrop) { backdrop.removeEventListener("wheel", this.preventBottomSheetScroll); backdrop.removeEventListener("touchmove", this.preventBottomSheetScroll); backdrop.removeEventListener("pointerdown", this.handleBackdropPointerStart); backdrop.removeEventListener("pointermove", this.handleBackdropPointerMove); backdrop.removeEventListener("pointerup", this.handleBackdropPointerEnd); backdrop.removeEventListener("pointercancel", this.handleBackdropPointerCancel); backdrop.removeEventListener("click", this.handleBackdropClick); } /** * Checks whether an element is one of this component's backdrop layers. * @param {EventTarget|null} element The event target. * @returns {boolean} */ isBackdropElement(element) { return element === this.backdropElement || element === this._portalBackdropElement; } /** * Gets the backdrop element that is active for the current presentation. * @param {object} options Options for resolving the backdrop. * @param {boolean} options.createPortal Creates the body-level mobile backdrop when needed. * @returns {HTMLElement|null} */ getBackdropElement({ createPortal = false } = {}) { if (!this.isBottomSheet()) return this.backdropElement; if (createPortal) return this.ensurePortalBackdrop(); return this._portalBackdropElement || this.backdropElement; } /** * Creates the body-level backdrop used by mobile bottom sheets. * @returns {HTMLElement} Element appended to document.body for page-level dimming. */ ensurePortalBackdrop() { if (!this._portalBackdropElement) { this._portalBackdropElement = document.createElement("div"); this._portalBackdropElement.setAttribute("data-wje-sliding-container-backdrop", ""); this._portalBackdropElement.classList.add("sliding-container-backdrop", "sliding-container-backdrop-portal"); this.addBackdropListeners(this._portalBackdropElement); document.body.append(this._portalBackdropElement); } this.syncPortalBackdropStyles(); return this._portalBackdropElement; } /** * Removes the body-level mobile backdrop. */ removePortalBackdrop() { if (!this._portalBackdropElement) return; this.removeBackdropListeners(this._portalBackdropElement); this._portalBackdropElement.remove(); this._portalBackdropElement = null; } /** * Gets the full-page z-index for the body-level mobile backdrop. * @returns {string} */ getPortalBackdropZIndex() { const hostStyle = getComputedStyle(this); const configuredZIndex = hostStyle.getPropertyValue("--wje-sliding-container-portal-backdrop-z-index").trim(); if (configuredZIndex) return configuredZIndex; const hostZIndex = Number.parseInt(hostStyle.zIndex, 10); if (Number.isFinite(hostZIndex)) return String(hostZIndex - 1); return "999"; } /** * Gets the target opacity for the active mobile backdrop. * @returns {string} */ getBackdropOpenOpacity() { return getComputedStyle(this).getPropertyValue("--wje-sliding-container-backdrop-opacity").trim() || "1"; } /** * Syncs visual styles from the component to the body-level backdrop. */ syncPortalBackdropStyles() { if (!this._portalBackdropElement) return; const backdropStyle = this.backdropElement ? getComputedStyle(this.backdropElement) : null; this._portalBackdropElement.style.position = "fixed"; this._portalBackdropElement.style.inset = "0"; this._portalBackdropElement.style.display = "block"; this._portalBackdropElement.style.opacity = this.classList.contains("open") ? this.getBackdropOpenOpacity() : "0"; this._portalBackdropElement.style.pointerEvents = this.classList.contains("open") ? "auto" : "none"; this._portalBackdropElement.style.touchAction = "none"; this._portalBackdropElement.style.overscrollBehavior = "none"; this._portalBackdropElement.style.background = (backdropStyle == null ? void 0 : backdropStyle.background) || "rgba(0, 0, 0, .35)"; this._portalBackdropElement.style.zIndex = this.getPortalBackdropZIndex(); } /** * Sync ARIA attributes on host. */ syncAria() { if (!this.hasAttribute("role")) { this.setAriaState({ role: "region" }); } this.setAriaState({ hidden: !this.isOpen }); const ariaLabel = this.getAttribute("aria-label"); const label = this.getAttribute("label"); if (!ariaLabel && label) { this.setAriaState({ label }); } } /** * Creates and returns a styled close button element with an icon, * including an event listener to trigger the close method. * @returns {HTMLElement} The close button element configured with styles, an icon, and event listener. */ htmlCloseButton() { let closeButton = document.createElement("wje-button"); closeButton.setAttribute("part", "close-button"); closeButton.classList.add("close-button"); let icon = document.createElement("wje-icon"); icon.setAttribute("slot", "icon-only"); icon.setAttribute("name", "x"); closeButton.append(icon); event.addListener(closeButton, "wje-button:click", null, (e) => { this.close(); }); return closeButton; } /** * Creates the mobile bottom sheet resize handle. * @returns {HTMLElement} The resize handle element. */ htmlSheetHandle() { const handleArea = document.createElement("div"); handleArea.setAttribute("part", "sheet-handle-area"); handleArea.setAttribute("role", "separator"); handleArea.setAttribute("aria-orientation", "horizontal"); handleArea.classList.add("sliding-container-sheet-handle-area"); const handle = document.createElement("div"); handle.setAttribute("part", "sheet-handle"); handle.classList.add("sliding-container-sheet-handle"); handleArea.append(handle); handleArea.addEventListener("pointerdown", this.handleSheetDragStart, { capture: true }); handleArea.addEventListener("touchstart", this.handleSheetTouchStart, { capture: true, passive: false }); handleArea.addEventListener("click", this.stopSheetHandleEvent, { capture: true }); handleArea.addEventListener("mousedown", this.stopSheetHandleEvent, { capture: true }); handleArea.addEventListener("mouseup", this.stopSheetHandleEvent, { capture: true }); this.sheetHandleArea = handleArea; return handleArea; } /** * Retrieves the parent element of the current element. * If the parent element is not found, it attempts to find the root host element. * @returns {Element|null} The parent element or the root host element if no parent exists. Returns null if neither is found. */ getParentElement() { let parentElement = this.parentElement; if (!parentElement) { parentElement = this.getRootNode().host; } return parentElement; } /** * Finds the closest element across shadow DOM boundaries. * @param {string} selector The selector to match. * @returns {Element|null} The matched composed ancestor. */ getClosestComposedElement(selector) { var _a; let currentElement = this; while (currentElement) { let matches = false; try { matches = currentElement instanceof Element && currentElement.matches(selector); } catch { return null; } if (matches) { return currentElement; } if (currentElement.parentElement) { currentElement = currentElement.parentElement; continue; } const root = (_a = currentElement.getRootNode) == null ? void 0 : _a.call(currentElement); currentElement = root instanceof ShadowRoot ? root.host : null; } return null; } /** * Returns the viewport frame. * @returns {{top: number, left: number, right: number, bottom: number, width: number, height: number}} */ getViewportFrame() { return { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight, width: window.innerWidth, height: window.innerHeight }; } /** * Returns the element that bounds the mobile bottom sheet panel. * @returns {Element|null} The scope element, or null for viewport scope. */ getBottomSheetScopeElement() { if (this.sheetScope === "viewport") return null; if (this.sheetScope === "parent") return this.getParentElement(); if (this.sheetBoundary) return this.getClosestComposedElement(this.sheetBoundary); return this.getParentElement(); } /** * Returns the frame used by the mobile bottom sheet panel. * @returns {{top: number, left: number, right: number, bottom: number, width: number, height: number}} */ getBottomSheetScopeFrame() { const scopeElement = this.getBottomSheetScopeElement(); if (!scopeElement) return this.getViewportFrame(); const rect = scopeElement.getBoundingClientRect(); const top = Math.max(rect.top, 0); const left = Math.max(rect.left, 0); const right = Math.min(rect.right, window.innerWidth); const bottom = Math.min(rect.bottom, window.innerHeight); const width = right - left; const height = bottom - top; if (width <= 0 || height <= 0) { return this.getViewportFrame(); } return { top, left, right, bottom, width, height }; } /** * Applies a fixed frame to a bottom sheet layer. * @param {HTMLElement|SlidingContainer} element The element to update. * @param {object} frame The frame to apply. */ setBottomSheetLayerFrame(element, frame) { element.style.top = frame.top + "px"; element.style.left = frame.left + "px"; element.style.right = "auto"; element.style.bottom = "auto"; element.style.width = frame.width + "px"; element.style.height = frame.height + "px"; } /** * Removes fixed frame styles from a bottom sheet layer. * @param {HTMLElement|SlidingContainer} element The element to reset. */ resetBottomSheetLayerFrame(element) { element == null ? void 0 : element.style.removeProperty("top"); element == null ? void 0 : element.style.removeProperty("left"); element == null ? void 0 : element.style.removeProperty("right"); element == null ? void 0 : element.style.removeProperty("bottom"); element == null ? void 0 : element.style.removeProperty("width"); element == null ? void 0 : element.style.removeProperty("height"); } /** * Adjusts the position and dimensions of the current element based on the specified variant. * * The method handles modifications to the element's positioning style, aligns it relative to its parent, * and manages alignment to its siblings based on the specified direction. * @param {string} variant The variant to determine how the element should be updated. For example, when set to 'over', specific adjustments to the position and size are performed. * @returns {void} No value is returned, the method modifies the element's style properties directly. */ checkForVariant(variant) { if (this.isBottomSheet()) { this.setBottomSheetVariant(); return; } this.resetBottomSheetVariant(); if (variant === "over") { this.style.position = "fixed"; let computentStyleOfParent = window.getComputedStyle(this.getParentElement()); let parentElementBoundingbox = this.getParentElement().getBoundingClientRect(); let heightOfParrentElement = parseFloat(computentStyleOfParent.height); let topOfParrentElement = parseFloat(computentStyleOfParent.top); this.style.height = heightOfParrentElement + +this.addToHeight + "px"; this.wrapperDiv.style.height = heightOfParrentElement + +this.addToHeight + "px"; this.style.top = topOfParrentElement + "px"; const leftSibling = this.previousElementSibling; const rightSibling = this.nextElementSibling; const leftSiblingBoundingbox = leftSibling == null ? void 0 : leftSibling.getBoundingClientRect(); const rightSiblingBoundingbox = rightSibling == null ? void 0 : rightSibling.getBoundingClientRect(); if (this.direction === "right") { if (leftSiblingBoundingbox) { this.style.left = leftSiblingBoundingbox.left + leftSiblingBoundingbox.width + "px"; } else { this.style.left = parentElementBoundingbox.left + "px"; } } else { if (rightSiblingBoundingbox) { this.style.right = window.innerWidth - rightSiblingBoundingbox.left + "px"; } else { this.style.right = window.innerWidth - (parentElementBoundingbox.left + parentElementBoundingbox.width) + "px"; } } } } /** * Applies the mobile bottom sheet layout. */ setBottomSheetVariant() { const scopeFrame = this.getBottomSheetScopeFrame(); const backdropFrame = this.getViewportFrame(); const backdrop = this.getBackdropElement({ createPortal: this.classList.contains("open") || this._isOpen }); this._bottomSheetScopeFrame = scopeFrame; this.classList.add("bottom-sheet"); this.style.position = "fixed"; this.setBottomSheetLayerFrame(this, scopeFrame); this.setBottomSheetLayerFrame(backdrop, backdropFrame); this.setBottomSheetLayerFrame(this.wrapperDiv, scopeFrame); this.syncPortalBackdropStyles(); this.wrapperDiv.style.height = "100%"; this.transparentDiv.style.width = 0; this.nativeElement.style.left = scopeFrame.left + "px"; this.nativeElement.style.right = "auto"; this.nativeElement.style.bottom = window.innerHeight - scopeFrame.bottom + "px"; this.nativeElement.style.top = "auto"; this.nativeElement.style.width = scopeFrame.width + "px"; if (this._sheetDrag) { this.nativeElement.style.maxHeight = this.getSheetMaxHeightInPixels() + "px"; } else { this.setSheetHeight(); } this.setBottomSheetVisualState(this.classList.contains("open")); this.nativeInner.style.width = "100%"; this.nativeInner.style.maxHeight = this.nativeElement.style.maxHeight; } /** * Removes layout styles managed by the mobile bottom sheet mode. */ resetBottomSheetVariant() { var _a, _b, _c, _d, _e, _f, _g, _h, _i; if (!this.classList.contains("bottom-sheet")) return; this.classList.remove("bottom-sheet"); this.style.removeProperty("position"); this.resetBottomSheetLayerFrame(this); this.resetBottomSheetLayerFrame(this.backdropElement); this.removePortalBackdrop(); this.resetBottomSheetLayerFrame(this.wrapperDiv); if (this.transparentDiv) { this.transparentDiv.style.width = this._isOpen ? this.maxWidth : 0; } (_a = this.nativeElement) == null ? void 0 : _a.style.removeProperty("left"); (_b = this.nativeElement) == null ? void 0 : _b.style.removeProperty("right"); (_c = this.nativeElement) == null ? void 0 : _c.style.removeProperty("bottom"); (_d = this.nativeElement) == null ? void 0 : _d.style.removeProperty("top"); (_e = this.nativeElement) == null ? void 0 : _e.style.removeProperty("height"); (_f = this.nativeElement) == null ? void 0 : _f.style.removeProperty("max-height"); (_g = this.nativeElement) == null ? void 0 : _g.style.removeProperty("transform"); if (this.nativeElement) { if (this.direction === "right") { this.nativeElement.style.right = 0; } else { this.nativeElement.style.left = 0; } this.nativeElement.style.width = this._isOpen ? this.maxWidth : 0; } (_h = this.nativeInner) == null ? void 0 : _h.style.removeProperty("max-height"); if (this.nativeInner) { this.nativeInner.style.width = this.maxWidth; } (_i = this.backdropElement) == null ? void 0 : _i.style.removeProperty("opacity"); this._bottomSheetScopeFrame = null; this.endSheetDrag(); } /** * Applies the current visual open or closed state to the mobile bottom sheet. * @param {boolean} isOpen True when the sheet should be visible. */ setBottomSheetVisualState(isOpen) { this.nativeElement.style.transform = isOpen ? "translateY(0)" : "translateY(100%)"; const backdrop = this.getBackdropElement({ createPortal: isOpen || Boolean(this._portalBackdropElement) }); if (this.hasOpacity) { this.nativeElement.style.opacity = isOpen ? 1 : 0; } if (backdrop) { backdrop.style.opacity = isOpen ? this.getBackdropOpenOpacity() : "0"; backdrop.style.pointerEvents = isOpen ? "auto" : "none"; } if (!isOpen) { this.removePortalBackdrop(); } } /** * Gets the CSS height limit used for the mobile bottom sheet. * @returns {string} The CSS max height. */ getSheetMaxHeight() { return this.sheetMaxHeight || this.maxHeight; } /** * Applies the configured bottom sheet opening height. */ setSheetHeight() { const maxHeightInPixels = this.getSheetMaxHeightInPixels(); this.nativeElement.style.maxHeight = maxHeightInPixels + "px"; if (!this.sheetHeight) { this.nativeElement.style.removeProperty("height"); return; } const minHeight = Math.min(this.getSheetMinHeightInPixels(), maxHeightInPixels); const height = this.resolveCssHeight(this.sheetHeight, maxHeightInPixels); const clampedHeight = this.clamp(height, minHeight, maxHeightInPixels); this.nativeElement.style.height = clampedHeight + "px"; } /** * Resolves the sheet minimum height in pixels. * @returns {number} The minimum height. */ getSheetMinHeightInPixels() { const availableHeight = this.getBottomSheetAvailableHeight(); return Math.min( this.resolveCssHeight(this.sheetMinHeight || "30vh", availableHeight * 0.3), availableHeight ); } /** * Resolves the sheet maximum height in pixels. * @returns {number} The maximum height. */ getSheetMaxHeightInPixels() { const availableHeight = this.getBottomSheetAvailableHeight(); return Math.min( this.resolveCssHeight(this.getSheetMaxHeight(), availableHeight * 0.9), availableHeight ); } /** * Returns available height for the bottom sheet scope. * @returns {number} Pixel height that can be used inside the current sheet frame. */ getBottomSheetAvailableHeight() { var _a; return ((_a = this._bottomSheetScopeFrame) == null ? void 0 : _a.height) || this.getBottomSheetScopeFrame().height || window.innerHeight; } /** * Resolves a CSS height value against the viewport. * @param {string} value The configured sheet height. * @param {number} fallback The fallback height in pixels. * @returns {number} The resolved height. */ resolveCssHeight(value, fallback) { if (!value || value === "auto") return fallback; const normalizedValue = `${value}`.trim(); const parsedValue = Number.parseFloat(normalizedValue); if (Number.isNaN(parsedValue)) return fallback; if (normalizedValue.endsWith("px")) return parsedValue; if (normalizedValue.endsWith("vh")) return window.innerHeight * parsedValue / 100; if (normalizedValue.endsWith("vw")) return window.innerWidth * parsedValue / 100; if (normalizedValue.endsWith("%")) return window.innerHeight * parsedValue / 100; if (/^-?\d+(\.\d+)?$/.test(normalizedValue)) return parsedValue; return fallback; } /** * Clamps a number between min and max. * @param {number} value The measured height. * @param {number} min The minimum. * @param {number} max The maximum. * @returns {number} The clamped value. */ clamp(value, min, max) { return Math.min(Math.max(value, min), max); } /** * Checks whether an event came from the resize handle. * @param {Event} e The event to check. * @returns {boolean} True when the event belongs to the resize handle. */ isSheetHandleEvent(e) { if (!(e == null ? void 0 : e.composedPath)) return false; return e.composedPath().includes(this.sheetHandleArea); } /** * Checks whether an event happened inside the visible bottom sheet. * @param {Event} e The event to check. * @returns {boolean} True when the event coordinates are inside the sheet. */ isEventInsideSheet(e) { if (!this.nativeElement || typeof (e == null ? void 0 : e.clientX) !== "number" || typeof (e == null ? void 0 : e.clientY) !== "number") return false; const rect = this.nativeElement.getBoundingClientRect(); return e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom; } /** * Stores the initial drag state for the mobile bottom sheet. * @param {number|string} pointerId The pointer identifier. * @param