/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-e694c50 | Cube Dev Team */
import { extractStyles } from "../../../utils/styles.js";
import { mergeProps as mergeProps$1 } from "../../../utils/react/mergeProps.js";
import { useCombinedRefs } from "../../../utils/react/useCombinedRefs.js";
import { useLocalStorage } from "../../../utils/react/useLocalStorage.js";
import { DisplayTransition } from "../../helpers/DisplayTransition/DisplayTransition.js";
import { useDebouncedValue } from "../../../_internal/hooks/use-debounced-value.js";
import { DialogContainer } from "../../overlays/Dialog/DialogContainer.js";
import { Dialog } from "../../overlays/Dialog/Dialog.js";
import { LayoutContextReset, LayoutPanelContext, useLayoutActionsContext, useLayoutRefsContext, useLayoutStateContext } from "./LayoutContext.js";
import { clampSize, getOppositeSide, resolveCssSize } from "./utils.js";
import { CONTAINER_STYLES, filterBaseProps, mergeStyles, tasty } from "@tenphi/tasty";
import { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { useFocusRing, useHover, useMove } from "react-aria";
import { createPortal } from "react-dom";

//#region src/components/content/Layout/LayoutPanel.tsx
const HANDLER_WIDTH = 9;
const HANDLER_OFFSET = 4;
const RESIZABLE_INSET_OFFSET = 2;
const PanelElement = tasty({
	as: "div",
	qa: "LayoutPanel",
	styles: {
		container: "panel / inline-size",
		position: "absolute",
		display: "flex",
		flow: "column",
		overflow: "hidden",
		boxSizing: "border-box",
		"$content-padding": "1x",
		"$layout-border-size": "1bw",
		top: {
			"": 0,
			"side=bottom": "initial"
		},
		right: {
			"": 0,
			"side=left": "initial"
		},
		bottom: {
			"": 0,
			"side=top": "initial"
		},
		left: {
			"": 0,
			"side=right": "initial"
		},
		width: {
			"": "$min-size $panel-size $max-size",
			"side=top | side=bottom": "100%"
		},
		height: {
			"": "100%",
			"side=top | side=bottom": "$min-size $panel-size $max-size"
		},
		border: {
			"": false,
			"side=left": "right",
			"side=right": "left",
			"side=top": "bottom",
			"side=bottom": "top"
		},
		fill: "#white",
		transform: {
			"": "translateX(0) translateY(0)",
			"offscreen & side=left": "translateX(-100%)",
			"offscreen & side=right": "translateX(100%)",
			"offscreen & side=top": "translateY(-100%)",
			"offscreen & side=bottom": "translateY(100%)"
		},
		transition: {
			"": "none",
			"has-transition": "transform $transition ease-out"
		}
	}
});
const ResizeHandlerElement = tasty({
	qa: "PanelResizeHandler",
	styles: {
		position: "absolute",
		width: {
			"": "100%",
			horizontal: `${HANDLER_WIDTH}px`,
			"disabled & horizontal": "1bw"
		},
		height: {
			"": `${HANDLER_WIDTH}px`,
			horizontal: "100%",
			"disabled & !horizontal": "1bw"
		},
		top: {
			"": 0,
			"side=top": `calc($panel-size - ${HANDLER_OFFSET}px)`,
			"side=bottom": "initial"
		},
		bottom: {
			"": 0,
			"side=bottom": `calc($panel-size - ${HANDLER_OFFSET}px)`,
			"side=top": "initial"
		},
		left: {
			"": 0,
			"side=left": `calc($panel-size - ${HANDLER_OFFSET}px)`,
			"side=right": "initial"
		},
		right: {
			"": 0,
			"side=right": `calc($panel-size - ${HANDLER_OFFSET}px)`,
			"side=left": "initial"
		},
		transform: {
			"": "translateX(0)",
			"offscreen & side=left": `translateX(calc(-1 * $panel-size - ${HANDLER_WIDTH - HANDLER_OFFSET}px))`,
			"offscreen & side=right": `translateX(calc($panel-size + ${HANDLER_WIDTH - HANDLER_OFFSET}px))`,
			"offscreen & side=top": `translateY(calc(-1 * $panel-size - ${HANDLER_WIDTH - HANDLER_OFFSET}px))`,
			"offscreen & side=bottom": `translateY(calc($panel-size + ${HANDLER_WIDTH - HANDLER_OFFSET}px))`
		},
		cursor: {
			"": "row-resize",
			horizontal: "col-resize",
			disabled: "not-allowed"
		},
		touchAction: {
			"": "none",
			disabled: "auto"
		},
		padding: 0,
		outline: 0,
		boxSizing: "border-box",
		transition: {
			"": "theme",
			"has-transition": "transform $transition ease-out, theme"
		},
		Track: {
			width: {
				"": "initial",
				horizontal: "3px",
				"disabled & horizontal": "1px"
			},
			height: {
				"": "3px",
				horizontal: "initial",
				"disabled & !horizontal": "1px"
			},
			position: "absolute",
			inset: {
				"": "3px 0",
				horizontal: "0 3px",
				disabled: "0 0"
			},
			fill: {
				"": "#border",
				"(hovered | drag | focused) & !disabled": "#purple-03"
			},
			border: 0,
			transition: "theme",
			outline: {
				"": "1bw #primary-text.0",
				"drag | focused": "1bw #primary-text"
			},
			outlineOffset: 1
		},
		Drag: {
			display: "grid",
			gap: "2bw",
			border: 0,
			flow: {
				"": "row",
				horizontal: "column"
			},
			gridColumns: {
				"": "3px 3px 3px 3px 3px",
				horizontal: "auto"
			},
			gridRows: {
				"": "auto",
				horizontal: "3px 3px 3px 3px 3px"
			},
			width: {
				"": "auto",
				horizontal: "1px"
			},
			height: {
				"": "1px",
				horizontal: "auto"
			},
			inset: {
				"": "4px 50% auto auto",
				horizontal: "50% 4px auto auto"
			},
			transform: {
				"": "translate(-50%, 0)",
				horizontal: "translate(0, -50%)"
			},
			position: "absolute",
			transition: "theme"
		},
		DragPart: {
			radius: true,
			fill: {
				"": "#dark-03",
				"hovered | drag | focused": "#primary-text",
				disabled: "#dark-04"
			}
		}
	}
});
const OverlayBackdrop = tasty({
	qa: "PanelOverlay",
	styles: {
		position: "absolute",
		inset: 0,
		backdropFilter: "invert(.15)",
		cursor: "pointer",
		opacity: {
			"": 0,
			visible: 1
		},
		pointerEvents: {
			"": "none",
			visible: "auto"
		},
		transition: "opacity .15s ease-out"
	}
});
function ResizeHandler(props) {
	const { side, isDisabled, mods, moveProps, style, onDoubleClick } = props;
	const { hoverProps, isHovered } = useHover({});
	const { focusProps, isFocusVisible } = useFocusRing();
	const isHorizontal = side === "left" || side === "right";
	return /* @__PURE__ */ jsxs(ResizeHandlerElement, {
		...mergeProps$1(hoverProps, focusProps, moveProps, {
			mods: {
				hovered: useDebouncedValue(isHovered, 150),
				horizontal: isHorizontal,
				disabled: isDisabled,
				focused: isFocusVisible,
				side,
				...mods
			},
			style,
			tabIndex: isDisabled ? void 0 : 0,
			role: "separator",
			"aria-orientation": isHorizontal ? "vertical" : "horizontal",
			"aria-label": `Resize ${side} panel`,
			onDoubleClick: isDisabled ? void 0 : onDoubleClick
		}),
		children: [/* @__PURE__ */ jsx("div", { "data-element": "Track" }), !isDisabled && /* @__PURE__ */ jsxs("div", {
			"data-element": "Drag",
			children: [
				/* @__PURE__ */ jsx("div", { "data-element": "DragPart" }),
				/* @__PURE__ */ jsx("div", { "data-element": "DragPart" }),
				/* @__PURE__ */ jsx("div", { "data-element": "DragPart" }),
				/* @__PURE__ */ jsx("div", { "data-element": "DragPart" }),
				/* @__PURE__ */ jsx("div", { "data-element": "DragPart" })
			]
		})]
	});
}
function LayoutPanel(props, ref) {
	const layoutActions = useLayoutActionsContext();
	const layoutState = useLayoutStateContext();
	const layoutRefs = useLayoutRefsContext();
	if (!layoutActions || !layoutState || !layoutRefs) throw new Error("Layout.Panel must be used within a Layout component. Ensure your panel is rendered inside a <Layout> parent.");
	const { side = "left", mode: modeProp, size: providedSize, defaultSize = 280, minSize = 200, maxSize, isResizable = false, onSizeChange, isOpen: providedIsOpen, defaultIsOpen = true, onOpenChange, hasTransition: hasTransitionProp, isDismissable = true, overlayStyles, isDialog = false, isDialogOpen: providedIsDialogOpen, defaultIsDialogOpen = false, onDialogOpenChange, dialogProps, contentPadding, sizeStorageKey, children, styles, mods, ...otherProps } = props;
	const mode = modeProp ?? (isDialog ? "dialog" : "default");
	const isDialogMode = mode === "dialog";
	const isOverlayMode = mode === "overlay";
	const hasTransition = hasTransitionProp ?? layoutActions.hasTransition;
	const combinedRef = useCombinedRefs(ref);
	const prevProvidedSizeRef = useRef(providedSize);
	const isHorizontal = side === "left" || side === "right";
	const { containerWidth, containerHeight } = layoutState;
	const { minContentSize } = layoutActions;
	const containerDimension = isHorizontal ? containerWidth : containerHeight;
	const oppositeSide = getOppositeSide(side);
	const oppositePanelSize = layoutState.panelSizes[oppositeSide];
	const ownInsetOffset = isResizable ? RESIZABLE_INSET_OFFSET : 0;
	const naturalMax = useMemo(() => containerDimension > 0 ? Math.max(0, containerDimension - oppositePanelSize - minContentSize - ownInsetOffset) : Infinity, [
		containerDimension,
		oppositePanelSize,
		minContentSize,
		ownInsetOffset
	]);
	const [internalIsOpen, setInternalIsOpen] = useState(defaultIsOpen);
	const isOpen = providedIsOpen ?? internalIsOpen;
	const [internalIsDialogOpen, setInternalIsDialogOpen] = useState(defaultIsDialogOpen);
	const dialogOpen = providedIsDialogOpen ?? internalIsDialogOpen;
	const [storedSize, setStoredSize] = useLocalStorage(sizeStorageKey ?? null, null);
	const [isDragging, setIsDragging] = useState(false);
	const [size, setSize] = useState(() => {
		return clampSize(typeof providedSize === "number" ? providedSize : storedSize != null ? storedSize : typeof defaultSize === "number" ? defaultSize : 280, minSize, maxSize);
	});
	const extractedStyles = extractStyles(otherProps, CONTAINER_STYLES);
	const finalStyles = useMemo(() => mergeStyles(styles, contentPadding != null ? { "$content-padding": contentPadding } : null, extractedStyles), [
		extractedStyles,
		contentPadding,
		styles
	]);
	const resolvedMax = useMemo(() => {
		if (typeof maxSize === "number") return maxSize;
		if (typeof maxSize === "string" && containerDimension > 0) return resolveCssSize(maxSize, containerDimension);
	}, [maxSize, containerDimension]);
	const effectiveMax = useMemo(() => {
		const values = [];
		if (resolvedMax != null) values.push(resolvedMax);
		if (Number.isFinite(naturalMax)) values.push(naturalMax);
		return values.length > 0 ? Math.min(...values) : void 0;
	}, [resolvedMax, naturalMax]);
	const clampValue = useCallback((value) => clampSize(value, minSize, void 0, effectiveMax), [minSize, effectiveMax]);
	const setContextDragging = layoutActions.setDragging;
	const { moveProps } = useMove({
		onMoveStart() {
			if (!isResizable) return;
			setIsDragging(true);
			setContextDragging(true);
		},
		onMove(e) {
			if (!isResizable) return;
			let delta;
			if (e.pointerType === "keyboard") {
				const step = e.shiftKey ? 50 : 10;
				const rawDelta = isHorizontal ? e.deltaX : e.deltaY;
				const direction = side === "right" || side === "bottom" ? -1 : 1;
				delta = rawDelta * step * direction;
			} else delta = isHorizontal ? e.deltaX * (side === "right" ? -1 : 1) : e.deltaY * (side === "bottom" ? -1 : 1);
			setSize((currentSize) => clampValue(currentSize + delta));
		},
		onMoveEnd() {
			setIsDragging(false);
			setContextDragging(false);
			setSize((currentSize) => {
				const finalSize = Math.round(clampValue(currentSize));
				onSizeChange?.(finalSize);
				setStoredSize(finalSize);
				return finalSize;
			});
		}
	});
	useEffect(() => {
		if (prevProvidedSizeRef.current !== providedSize) {
			prevProvidedSizeRef.current = providedSize;
			if (typeof providedSize === "number" && !isDragging) setSize(clampValue(providedSize));
		}
	}, [
		providedSize,
		isDragging,
		clampValue
	]);
	useEffect(() => {
		if (!isDragging && containerDimension > 0) setSize((prev) => {
			const clamped = clampValue(prev);
			if (clamped !== prev) {
				onSizeChange?.(Math.round(clamped));
				setStoredSize(Math.round(clamped));
			}
			return clamped;
		});
	}, [
		isDragging,
		clampValue,
		containerDimension
	]);
	const effectivePanelSize = isOpen && mode === "default" ? size : 0;
	const effectiveInsetSize = Math.round(effectivePanelSize + (isResizable && effectivePanelSize > 0 ? RESIZABLE_INSET_OFFSET : 0));
	const { registerPanel, unregisterPanel, updatePanelSize } = layoutActions;
	const { isReady } = layoutState;
	const lastSizeRef = useRef(effectiveInsetSize);
	useLayoutEffect(() => {
		registerPanel(side, lastSizeRef.current);
		return () => {
			unregisterPanel(side);
		};
	}, [
		side,
		registerPanel,
		unregisterPanel
	]);
	useLayoutEffect(() => {
		if (lastSizeRef.current !== effectiveInsetSize) {
			lastSizeRef.current = effectiveInsetSize;
			updatePanelSize(side, effectiveInsetSize);
		}
	}, [
		side,
		effectiveInsetSize,
		updatePanelSize
	]);
	const handleOpenChange = useCallback((newIsOpen) => {
		setInternalIsOpen(newIsOpen);
		onOpenChange?.(newIsOpen);
	}, [onOpenChange]);
	const handleDismiss = useCallback(() => {
		if (isDismissable) handleOpenChange(false);
	}, [isDismissable, handleOpenChange]);
	const { registerOverlayPanel } = layoutActions;
	useEffect(() => {
		if (isOverlayMode && isOpen && isDismissable) return registerOverlayPanel(() => handleOpenChange(false));
	}, [
		isOverlayMode,
		isOpen,
		isDismissable,
		registerOverlayPanel,
		handleOpenChange
	]);
	const handleDialogOpenChange = useCallback((newIsOpen) => {
		setInternalIsDialogOpen(newIsOpen);
		onDialogOpenChange?.(newIsOpen);
	}, [onDialogOpenChange]);
	const panelContextValue = useMemo(() => ({
		onOpenChange: handleOpenChange,
		isOpen
	}), [handleOpenChange, isOpen]);
	const dialogPanelContextValue = useMemo(() => ({
		onOpenChange: handleDialogOpenChange,
		isOpen: dialogOpen
	}), [handleDialogOpenChange, dialogOpen]);
	const panelMods = useMemo(() => ({
		side,
		drag: isDragging,
		horizontal: isHorizontal,
		"has-transition": hasTransition && isReady,
		...mods
	}), [
		side,
		isDragging,
		isHorizontal,
		hasTransition,
		isReady,
		mods
	]);
	const maxSizeCss = useMemo(() => {
		const parts = [];
		if (maxSize != null) parts.push(typeof maxSize === "number" ? `${maxSize}px` : maxSize);
		if (containerDimension > 0 && Number.isFinite(naturalMax)) parts.push(`${naturalMax}px`);
		if (parts.length === 0) return void 0;
		return parts.length === 1 ? parts[0] : `min(${parts.join(", ")})`;
	}, [
		maxSize,
		naturalMax,
		containerDimension
	]);
	const panelStyle = useMemo(() => ({
		"--panel-size": `${size}px`,
		"--min-size": typeof minSize === "number" ? `${minSize}px` : minSize,
		"--max-size": maxSizeCss
	}), [
		size,
		minSize,
		maxSizeCss
	]);
	const panelRefCallback = useCallback((node, transitionRef) => {
		combinedRef.current = node;
		transitionRef?.(node);
	}, [combinedRef]);
	const handleResetSize = useCallback(() => {
		const clampedSize = clampValue((typeof defaultSize === "number" ? defaultSize : parseInt(defaultSize, 10)) || 280);
		setSize(clampedSize);
		onSizeChange?.(clampedSize);
		setStoredSize(clampedSize);
	}, [
		defaultSize,
		clampValue,
		onSizeChange,
		setStoredSize
	]);
	const renderPanelContent = (offscreen = false, transitionRef) => {
		return /* @__PURE__ */ jsxs(Fragment$1, { children: [
			isOverlayMode && /* @__PURE__ */ jsx(OverlayBackdrop, {
				mods: { visible: isOverlayMode && !offscreen },
				styles: overlayStyles,
				"aria-hidden": "true",
				onClick: handleDismiss
			}),
			/* @__PURE__ */ jsx(PanelElement, {
				ref: (node) => panelRefCallback(node, transitionRef),
				...filterBaseProps(otherProps, { eventProps: true }),
				mods: {
					...panelMods,
					offscreen
				},
				styles: finalStyles,
				style: panelStyle,
				"data-side": side,
				children: /* @__PURE__ */ jsx(LayoutPanelContext.Provider, {
					value: panelContextValue,
					children: /* @__PURE__ */ jsx(LayoutContextReset, { children })
				})
			}),
			isResizable && /* @__PURE__ */ jsx(ResizeHandler, {
				side,
				isDisabled: !isResizable,
				mods: {
					drag: isDragging,
					offscreen,
					"has-transition": hasTransition && isReady
				},
				moveProps,
				style: panelStyle,
				onDoubleClick: handleResetSize
			})
		] });
	};
	if (isDialogMode) return /* @__PURE__ */ jsx(DialogContainer, {
		isOpen: dialogOpen,
		isDismissable,
		onDismiss: () => handleDialogOpenChange(false),
		...dialogProps,
		children: /* @__PURE__ */ jsx(Dialog, {
			isDismissable: false,
			children: /* @__PURE__ */ jsx(LayoutPanelContext.Provider, {
				value: dialogPanelContextValue,
				children: /* @__PURE__ */ jsx(LayoutContextReset, { children })
			})
		})
	});
	if (!layoutRefs.isPanelContainerReady) return null;
	const portalContainer = layoutRefs.panelContainerRef.current;
	if (hasTransition) return createPortal(/* @__PURE__ */ jsx(DisplayTransition, {
		isShown: isOpen,
		animateOnMount: false,
		children: ({ isShown, ref: transitionRef }) => renderPanelContent(!isShown, transitionRef)
	}), portalContainer);
	if (!isOpen) return null;
	return createPortal(renderPanelContent(false), portalContainer);
}
const _LayoutPanel = forwardRef(LayoutPanel);
_LayoutPanel.displayName = "Layout.Panel";

//#endregion
export { _LayoutPanel };
//# sourceMappingURL=LayoutPanel.js.map