/**
 * Copyright IBM Corp. 2026
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { selectorTabbable } from "../../globals/settings.js";
import { carbonElement } from "../../globals/decorators/carbon-element.js";
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.js";
import iconLoader from "../../globals/internal/icon-loader.js";
import HostListener from "../../globals/decorators/host-listener.js";
import HostListenerMixin from "../../globals/mixins/host-listener.js";
import "../button/button-set-base.js";
import "../button/index.js";
import "../icon-button/index.js";
import "../layer/index.js";
import { SIDE_PANEL_PLACEMENT, SIDE_PANEL_SIZE } from "./defs.js";
import { LitElement, html } from "lit";
import { property, query, queryAssignedElements, state } from "lit/decorators.js";
import Close20 from "@carbon/icons/es/close/20.js";
import ArrowLeft16 from "@carbon/icons/es/arrow--left/16.js";
import { moderate02 } from "@carbon/motion";
//#region src/components/side-panel/side-panel.ts
/**
* Copyright IBM Corp. 2023, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const PRECEDING = Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS;
const FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY;
const blockClass = `cds-custom--side-panel`;
const blockClassActionSet = `cds-custom--action-set`;
/**
* Observes resize of the given element with the given resize observer.
*
* @param observer The resize observer.
* @param elem The element to observe the resize.
*/
const observeResize = (observer, elem) => {
	if (!elem) return null;
	observer.observe(elem);
	return { release() {
		observer.unobserve(elem);
		return null;
	} };
};
/**
* Tries to focus on the given elements and bails out if one of them is successful.
*
* @param elems The elements.
* @param reverse `true` to go through the list in reverse order.
* @returns `true` if one of the attempts is successful, `false` otherwise.
*/
function tryFocusElems(elems, reverse) {
	if (!reverse) for (let i = 0; i < elems.length; ++i) {
		const elem = elems[i];
		elem.focus();
		if (elem.ownerDocument.activeElement === elem) return true;
	}
	else for (let i = elems.length - 1; i >= 0; --i) {
		const elem = elems[i];
		elem.focus();
		if (elem.ownerDocument.activeElement === elem) return true;
	}
	return false;
}
/**
* SidePanel.
*
* @deprecated Use Carbon for IBM Products `side-panel` component.
*   This component has been deprecated in `@carbon/web-components` and will instead be maintained
*   in the Carbon for IBM Products library:
*   https://github.com/carbon-design-system/ibm-products/tree/main/packages/ibm-products-web-components
* @element cds-custom-side-panel
* @csspart dialog The dialog.
* @fires cds-custom-side-panel-beingclosed
*   The custom event fired before this side-panel is being closed upon a user gesture.
*   Cancellation of this event stops the user-initiated action of closing this side-panel.
* @fires cds-custom-side-panel-closed - The custom event fired after this side-panel is closed upon a user gesture.
* @fires cds-custom-side-panel-navigate-back - custom event fired when clicking navigate back (available when step > 0)
*/
let CDSSidePanel = class CDSSidePanel extends HostListenerMixin(LitElement) {
	constructor(..._args) {
		super(..._args);
		this._hObserveResize = null;
		this._launcher = null;
		this._doAnimateTitle = true;
		this._isOpen = false;
		this._containerScrollTop = -16;
		this._hasSubtitle = false;
		this._hasSlug = false;
		this._hasActionToolbar = false;
		this._actionsCount = 0;
		this._slugCloseSize = "sm";
		this._handleBlur = async ({ target, relatedTarget }) => {
			const { open, _startSentinelNode: startSentinelNode, _endSentinelNode: endSentinelNode } = this;
			const oldContains = target !== this && this.contains(target);
			const currentContains = relatedTarget !== this && (this.contains(relatedTarget) || this.shadowRoot?.contains(relatedTarget) && relatedTarget !== startSentinelNode && relatedTarget !== endSentinelNode);
			const { selectorTabbable: selectorTabbableForSidePanel } = this.constructor;
			if (open && relatedTarget && oldContains && !currentContains) {
				const comparisonResult = target.compareDocumentPosition(relatedTarget);
				if (relatedTarget === startSentinelNode || comparisonResult & PRECEDING) {
					await this.constructor._delay();
					if (!tryFocusElems(this.querySelectorAll(selectorTabbableForSidePanel), true) && relatedTarget !== this) this.focus();
				} else if (relatedTarget === endSentinelNode || comparisonResult & FOLLOWING) {
					await this.constructor._delay();
					if (!tryFocusElems(this.querySelectorAll(selectorTabbableForSidePanel), true)) this.focus();
				}
			}
		};
		this._handleKeydown = ({ key, target }) => {
			if (key === "Esc" || key === "Escape") this._handleUserInitiatedClose(target);
		};
		this._reducedMotion = typeof window !== "undefined" && window?.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)") : { matches: true };
		this._adjustPageContent = () => {
			if (this.selectorPageContent) {
				const pageContentEl = document.querySelector(this.selectorPageContent);
				if (pageContentEl) {
					const newValues = {
						marginInlineStart: "",
						marginInlineEnd: "",
						inlineSize: "",
						transition: this._reducedMotion.matches ? "none" : `all ${moderate02}`,
						transitionProperty: "margin-inline-start, margin-inline-end"
					};
					if (this.open) {
						newValues.inlineSize = "auto";
						if (this.placement === "left") newValues.marginInlineStart = `${this?._sidePanel?.offsetWidth}px`;
						else newValues.marginInlineEnd = `${this?._sidePanel?.offsetWidth}px`;
					}
					Object.keys(newValues).forEach((key) => {
						pageContentEl.style[key] = newValues[key];
					});
				}
			}
		};
		this._checkSetOpen = () => {
			const { _sidePanel: sidePanel } = this;
			if (sidePanel && this._isOpen) if (this._reducedMotion) this._isOpen = false;
			else sidePanel.addEventListener("transitionend", () => {
				this._isOpen = false;
			});
			else this._isOpen = this.open;
		};
		this._checkUpdateIconButtonSizes = () => {
			const slug = this.querySelector(`cds-custom-slug`);
			const otherButtons = this?.shadowRoot?.querySelectorAll("#nav-back-button, #close-button");
			let iconButtonSize = "sm";
			if (slug || otherButtons?.length) {
				if ((this?.querySelectorAll?.(`cds-custom-button[slot='actions']`))?.length && /l/.test(this.size)) iconButtonSize = "md";
			}
			if (slug) slug?.setAttribute("size", iconButtonSize);
			if (otherButtons) [...otherButtons].forEach((btn) => {
				btn.setAttribute("size", iconButtonSize);
			});
		};
		this._checkUpdateActionSizes = () => {
			if (this._actions) for (let i = 0; i < this._actions.length; i++) this._actions[i].setAttribute("size", this.condensedActions ? "lg" : "xl");
		};
		this._maxActions = 3;
		this._checkSetDoAnimateTitle = () => {
			let canDoAnimateTitle = false;
			if (this._sidePanel && this.open && this.animateTitle && this?.title?.length && !this._reducedMotion.matches) {
				const scrollAnimationDistance = this._getScrollAnimationDistance();
				this?._sidePanel?.style?.setProperty(`--${blockClass}--scroll-animation-distance`, `${scrollAnimationDistance}`);
				let scrollEl = this._animateScrollWrapper;
				if (!scrollEl && this.animateTitle && !this._doAnimateTitle) scrollEl = this._innerContent;
				if (scrollEl) {
					const innerComputed = window?.getComputedStyle(this._innerContent);
					const innerPaddingHeight = innerComputed ? parseFloat(innerComputed?.paddingTop) + parseFloat(innerComputed?.paddingBottom) : 0;
					canDoAnimateTitle = (!!this.labelText || !!this._hasActionToolbar || this._hasSubtitle) && scrollEl.scrollHeight - scrollEl.clientHeight >= scrollAnimationDistance + innerPaddingHeight;
				}
			}
			this._doAnimateTitle = canDoAnimateTitle;
		};
		this._resizeObserver = new ResizeObserver(() => {
			if (this._sidePanel) this._checkSetDoAnimateTitle();
		});
		this._getScrollAnimationDistance = () => {
			const labelHeight = this?._label?.offsetHeight ?? 0;
			const subtitleHeight = this?._subtitle?.offsetHeight ?? 0;
			const titleVerticalBorder = this._hasActionToolbar ? this._title.offsetHeight - this._title.clientHeight : 0;
			return labelHeight + subtitleHeight + titleVerticalBorder;
		};
		this._scrollObserver = () => {
			const scrollTop = this._animateScrollWrapper?.scrollTop ?? 0;
			const scrollAnimationDistance = this._getScrollAnimationDistance();
			this?._sidePanel?.style?.setProperty(`--${blockClass}--scroll-animation-progress`, `${Math.min(scrollTop, scrollAnimationDistance) / scrollAnimationDistance}`);
		};
		this._handleCurrentStepUpdate = () => {
			const scrollable = this._animateScrollWrapper ?? this._innerContent;
			if (scrollable) scrollable.scrollTop = 0;
		};
		this.animateTitle = true;
		this.closeIconDescription = "Close";
		this.condensedActions = false;
		this.includeOverlay = false;
		this.navigationBackIconDescription = "Back";
		this.open = false;
		this.placement = "right";
		this.preventCloseOnClickOutside = false;
		this.selectorPageContent = "";
		this.size = "md";
		this.slideIn = false;
	}
	/**
	* Handles `click` event on the side-panel container.
	*
	* @param event The event.
	*/
	_handleClickOnOverlay(event) {
		if (!this.preventCloseOnClickOutside) this._handleUserInitiatedClose(event.target);
	}
	/**
	* Handles `click` event on the side-panel container.
	*
	* @param event The event.
	*/
	_handleCloseClick(event) {
		this._handleUserInitiatedClose(event.target);
	}
	/**
	* Handles user-initiated close request of this side-panel.
	*
	* @param triggeredBy The element that triggered this close request.
	*/
	_handleUserInitiatedClose(triggeredBy) {
		if (this.open) {
			const init = {
				bubbles: true,
				cancelable: true,
				composed: true,
				detail: { triggeredBy }
			};
			if (this.dispatchEvent(new CustomEvent(this.constructor.eventBeforeClose, init))) {
				this.open = false;
				this.dispatchEvent(new CustomEvent(this.constructor.eventClose, init));
			}
		}
	}
	_handleNavigateBack(triggeredBy) {
		this.dispatchEvent(new CustomEvent(this.constructor.eventNavigateBack, {
			composed: true,
			detail: { triggeredBy }
		}));
	}
	_handleSlugChange(e) {
		this._checkUpdateIconButtonSizes();
		const childItems = e.target.assignedElements();
		this._hasSlug = childItems.length > 0;
	}
	_handleSubtitleChange(e) {
		const subtitle = e.target?.assignedElements();
		this._hasSubtitle = subtitle.length > 0;
	}
	_handleActionToolbarChange(e) {
		const toolbarActions = e.target?.assignedElements();
		this._hasActionToolbar = toolbarActions && toolbarActions.length > 0;
		if (this._hasActionToolbar) for (const toolbarAction of toolbarActions) toolbarAction.setAttribute("size", "sm");
	}
	_handleActionsChange(e) {
		const actions = e.target?.assignedElements();
		this._checkUpdateIconButtonSizes();
		const actionsCount = actions?.length ?? 0;
		if (actionsCount > this._maxActions) {
			this._actionsCount = this._maxActions;
			console.error(`Too many side-panel actions, max ${this._maxActions}.`);
		} else this._actionsCount = actionsCount;
		for (let i = 0; i < actions?.length; i++) if (i + 1 > this._maxActions) {
			actions[i].setAttribute("hidden", "true");
			actions[i].setAttribute(`data-actions-limit-${this._maxActions}-exceeded`, `${actions.length}`);
		} else actions[i].classList.add(`${blockClassActionSet}__action-button`);
		this._checkUpdateActionSizes();
	}
	async connectObservers() {
		await this.updateComplete;
		this._hObserveResize = observeResize(this._resizeObserver, this._sidePanel);
	}
	disconnectObservers() {
		if (this._hObserveResize) this._hObserveResize = this._hObserveResize.release();
	}
	connectedCallback() {
		super.connectedCallback();
		this.disconnectObservers();
		this.connectObservers();
	}
	disconnectedCallback() {
		super.disconnectedCallback();
		this.disconnectObservers();
	}
	render() {
		const { closeIconDescription, condensedActions, currentStep, includeOverlay, labelText, navigationBackIconDescription, open, placement, size, slideIn, title } = this;
		if (!open && !this._isOpen) return html``;
		const actionsMultiple = [
			"",
			"single",
			"double",
			"triple"
		][this._actionsCount];
		const titleTemplate = html`<div
      class=${`${blockClass}__title`}
      ?no-label=${!!labelText}>
      <h2 class=${title ? `${blockClass}__title-text` : ""} title=${title}>
        ${title}
      </h2>

      ${this._doAnimateTitle ? html`<h2
            class=${`${blockClass}__collapsed-title-text`}
            title=${title}
            aria-hidden="true">
            ${title}
          </h2>` : ""}
    </div>`;
		const headerTemplate = html`
      <div
        class=${`${blockClass}__header${this.title ? ` ${blockClass}__header--has-title ` : ""}`}
        ?detail-step=${currentStep > 0}
        ?no-title-animation=${!this._doAnimateTitle}
        ?reduced-motion=${this._reducedMotion.matches}>
        <!-- render back button -->
        ${currentStep > 0 ? html`<cds-custom-icon-button
              align="bottom-left"
              aria-label=${navigationBackIconDescription}
              kind="ghost"
              size="sm"
              class=${`cds-custom--btn ${blockClass}__navigation-back-button`}
              @click=${this._handleNavigateBack}>
              ${iconLoader(ArrowLeft16, { slot: "icon" })}
              <span slot="tooltip-content">
                ${navigationBackIconDescription}
              </span>
            </cds-custom-icon-button>` : ""}

        <!-- render title label -->
        ${title?.length && labelText?.length ? html` <p class=${`${blockClass}__label-text`}>${labelText}</p>` : ""}

        <!-- title -->
        ${title ? titleTemplate : ""}

        <!-- render slug and close button area -->
        <div class=${`${blockClass}__slug-and-close`}>
          <slot name="slug" @slotchange=${this._handleSlugChange}></slot>
          <!-- {normalizedSlug} -->
          <cds-custom-icon-button
            align="bottom-right"
            aria-label=${closeIconDescription}
            kind="ghost"
            size="sm"
            class=${`${blockClass}__close-button`}
            @click=${this._handleCloseClick}>
            ${iconLoader(Close20, { slot: "icon" })}
            <span slot="tooltip-content"> ${closeIconDescription} </span>
          </cds-custom-icon-button>
        </div>

        <!-- render sub title -->
        <p
          class=${this._hasSubtitle ? `${blockClass}__subtitle-text` : ""}
          ?hidden=${!this._hasSubtitle}
          ?no-title-animation=${!this._doAnimateTitle}
          ?no-action-toolbar=${!this._hasActionToolbar}
          ?no-title=${!title}>
          <slot
            name="subtitle"
            @slotchange=${this._handleSubtitleChange}></slot>
        </p>

        <div
          class=${this._hasActionToolbar ? `${blockClass}__action-toolbar` : ""}
          ?hidden=${!this._hasActionToolbar}
          ?no-title-animation=${!this._doAnimateTitle}>
          <slot
            name="action-toolbar"
            @slotchange=${this._handleActionToolbarChange}></slot>
        </div>
      </div>
    `;
		const mainTemplate = html`<div
      class=${`${blockClass}__inner-content`}
      ?scrolls=${!this._doAnimateTitle}>
      <cds-custom-layer level="1">
        <slot></slot>
      </cds-custom-layer>
    </div> `;
		return html`
      <div
        class=${`${blockClass}${this._doAnimateTitle ? ` ${blockClass}--animated-title` : ""}`}
        part="dialog"
        role="complementary"
        placement="${placement}"
        ?has-slug=${this._hasSlug}
        ?open=${this._isOpen}
        ?opening=${open && !this._isOpen}
        ?closing=${!open && this._isOpen}
        ?condensed-actions=${condensedActions}
        ?overlay=${includeOverlay || slideIn}
        ?slide-in=${slideIn}
        size=${size}>
        <a
          id="start-sentinel"
          class="sentinel"
          hidden
          href="javascript:void 0"
          role="navigation"></a>

        ${this._doAnimateTitle ? html`<div class=${`${blockClass}__animated-scroll-wrapper`} scrolls>
              ${headerTemplate} ${mainTemplate}
            </div>` : html` ${headerTemplate} ${mainTemplate}`}

        <cds-custom-button-set-base
          class=${`${blockClass}__actions-container`}
          ?hidden=${this._actionsCount === 0}
          ?condensed=${condensedActions}
          actions-multiple=${actionsMultiple}
          size=${size}>
          <slot name="actions" @slotchange=${this._handleActionsChange}></slot>
        </cds-custom-button-set-base>

        <a
          id="end-sentinel"
          class="sentinel"
          hidden
          href="javascript:void 0"
          role="navigation"></a>
      </div>

      ${includeOverlay ? html`<div
            ?slide-in=${slideIn}
            class=${`${blockClass}__overlay`}
            ?open=${this.open}
            ?opening=${open && !this._isOpen}
            ?closing=${!open && this._isOpen}
            tabindex="-1"
            @click=${this._handleClickOnOverlay}></div>` : ""}
    `;
	}
	async updated(changedProperties) {
		if (changedProperties.has("condensedActions")) this._checkUpdateActionSizes();
		if (changedProperties.has("currentStep")) this._handleCurrentStepUpdate();
		if (changedProperties.has("_doAnimateTitle")) {
			this?._animateScrollWrapper?.removeEventListener("scroll", this._scrollObserver);
			if (this._doAnimateTitle) this?._animateScrollWrapper?.addEventListener("scroll", this._scrollObserver);
			else this?._sidePanel?.style?.setProperty(`--${blockClass}--scroll-animation-progress`, "0");
		}
		if (changedProperties.has("_isOpen") || changedProperties.has("animateTitle")) this._checkSetDoAnimateTitle();
		if (changedProperties.has("slideIn") || changedProperties.has("open") || changedProperties.has("includeOverlay")) this._adjustPageContent();
		if (changedProperties.has("open")) {
			this._checkSetOpen();
			this.disconnectObservers();
			if (this.open) {
				this.connectObservers();
				this._launcher = this.ownerDocument.activeElement;
				const focusNode = this.selectorInitialFocus && this.querySelector(this.selectorInitialFocus);
				await this.constructor._delay();
				if (focusNode) focusNode.focus();
				else if (!tryFocusElems(this.querySelectorAll(this.constructor.selectorTabbable), true)) this.focus();
			} else if (this._launcher && typeof this._launcher.focus === "function") {
				this._launcher.focus();
				this._launcher = null;
			}
		}
	}
	/**
	* @param ms The number of milliseconds.
	* @returns A promise that is resolves after the given milliseconds.
	*/
	static _delay(ms = 0) {
		return new Promise((resolve) => {
			setTimeout(resolve, ms);
		});
	}
	/**
	* A selector selecting tabbable nodes.
	*/
	static get selectorTabbable() {
		return selectorTabbable;
	}
	/**
	* The name of the custom event fired before this side-panel is being closed upon a user gesture.
	* Cancellation of this event stops the user-initiated action of closing this side-panel.
	*/
	static get eventBeforeClose() {
		return `cds-custom-side-panel-beingclosed`;
	}
	/**
	* The name of the custom event fired after this side-panel is closed upon a user gesture.
	*/
	static get eventClose() {
		return `cds-custom-side-panel-closed`;
	}
	/**
	* The name of the custom event fired on clicking the navigate back button
	*/
	static get eventNavigateBack() {
		return `cds-custom-side-panel-navigate-back`;
	}
};
__decorate([query("#start-sentinel")], CDSSidePanel.prototype, "_startSentinelNode", void 0);
__decorate([query("#end-sentinel")], CDSSidePanel.prototype, "_endSentinelNode", void 0);
__decorate([query(`.${blockClass}`)], CDSSidePanel.prototype, "_sidePanel", void 0);
__decorate([query(`.${blockClass}__animated-scroll-wrapper`)], CDSSidePanel.prototype, "_animateScrollWrapper", void 0);
__decorate([query(`.${blockClass}__label-text`)], CDSSidePanel.prototype, "_label", void 0);
__decorate([query(`.${blockClass}__title-text`)], CDSSidePanel.prototype, "_title", void 0);
__decorate([query(`.${blockClass}__subtitle-text`)], CDSSidePanel.prototype, "_subtitle", void 0);
__decorate([query(`.${blockClass}__inner-content`)], CDSSidePanel.prototype, "_innerContent", void 0);
__decorate([queryAssignedElements({
	slot: "actions",
	selector: `cds-custom-button`
})], CDSSidePanel.prototype, "_actions", void 0);
__decorate([state()], CDSSidePanel.prototype, "_doAnimateTitle", void 0);
__decorate([state()], CDSSidePanel.prototype, "_isOpen", void 0);
__decorate([state()], CDSSidePanel.prototype, "_containerScrollTop", void 0);
__decorate([state()], CDSSidePanel.prototype, "_hasSubtitle", void 0);
__decorate([state()], CDSSidePanel.prototype, "_hasSlug", void 0);
__decorate([state()], CDSSidePanel.prototype, "_hasActionToolbar", void 0);
__decorate([state()], CDSSidePanel.prototype, "_actionsCount", void 0);
__decorate([state()], CDSSidePanel.prototype, "_slugCloseSize", void 0);
__decorate([HostListener("shadowRoot:focusout")], CDSSidePanel.prototype, "_handleBlur", void 0);
__decorate([HostListener("document:keydown")], CDSSidePanel.prototype, "_handleKeydown", void 0);
__decorate([property({
	reflect: true,
	attribute: "animate-title",
	type: Boolean
})], CDSSidePanel.prototype, "animateTitle", void 0);
__decorate([property({
	reflect: true,
	attribute: "close-icon-description"
})], CDSSidePanel.prototype, "closeIconDescription", void 0);
__decorate([property({
	type: Boolean,
	reflect: true,
	attribute: "condensed-actions"
})], CDSSidePanel.prototype, "condensedActions", void 0);
__decorate([property({
	reflect: true,
	attribute: "current-step",
	type: Number
})], CDSSidePanel.prototype, "currentStep", void 0);
__decorate([property({
	attribute: "include-overlay",
	type: Boolean,
	reflect: true
})], CDSSidePanel.prototype, "includeOverlay", void 0);
__decorate([property({
	reflect: true,
	attribute: "label-text"
})], CDSSidePanel.prototype, "labelText", void 0);
__decorate([property({
	reflect: true,
	attribute: "navigation-back-icon-description"
})], CDSSidePanel.prototype, "navigationBackIconDescription", void 0);
__decorate([property({
	type: Boolean,
	reflect: true
})], CDSSidePanel.prototype, "open", void 0);
__decorate([property({
	reflect: true,
	type: String
})], CDSSidePanel.prototype, "placement", void 0);
__decorate([property({
	type: Boolean,
	attribute: "prevent-close-on-click-outside"
})], CDSSidePanel.prototype, "preventCloseOnClickOutside", void 0);
__decorate([property({
	reflect: true,
	attribute: "selector-initial-focus",
	type: String
})], CDSSidePanel.prototype, "selectorInitialFocus", void 0);
__decorate([property({
	reflect: true,
	attribute: "selector-page-content"
})], CDSSidePanel.prototype, "selectorPageContent", void 0);
__decorate([property({
	reflect: true,
	type: String
})], CDSSidePanel.prototype, "size", void 0);
__decorate([property({
	attribute: "slide-in",
	type: Boolean,
	reflect: true
})], CDSSidePanel.prototype, "slideIn", void 0);
__decorate([property({
	reflect: false,
	type: String
})], CDSSidePanel.prototype, "title", void 0);
CDSSidePanel = __decorate([carbonElement(`cds-custom-side-panel`)], CDSSidePanel);
var side_panel_default = CDSSidePanel;
//#endregion
export { SIDE_PANEL_PLACEMENT, SIDE_PANEL_SIZE, side_panel_default as default };

//# sourceMappingURL=side-panel.js.map