/** @license MIT | @cube-dev/ui-kit v0.142.7 | Cube Dev Team */
import { extractStyles } from "../../../utils/styles.js";
import { mergeProps as mergeProps$1 } from "../../../utils/react/mergeProps.js";
import { useEvent } from "../../../_internal/hooks/use-event.js";
import { useAutoTooltip } from "../use-auto-tooltip.js";
import { BLOCK_STYLES, COLOR_STYLES, OUTER_STYLES, filterBaseProps, tasty } from "@tenphi/tasty";
import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { FocusScope, useFocusRing, useFocusWithin } from "react-aria";
import { useControlledState } from "@react-stately/utils";

//#region src/components/content/InlineInput/InlineInput.tsx
const InlineInputRoot = tasty({
	as: "span",
	styles: {
		display: "inline-flex",
		alignItems: {
			"": "baseline",
			styled: "center"
		},
		verticalAlign: {
			"": "baseline",
			styled: "middle"
		},
		position: "relative",
		maxWidth: "100%",
		boxSizing: "border-box",
		color: {
			"": "inherit",
			styled: "#dark-02",
			"styled & disabled": "#dark.30"
		},
		preset: {
			"": "inherit",
			styled: "t3"
		},
		cursor: {
			"": "inherit",
			"editable & !editing": "text"
		},
		fill: {
			"": "transparent",
			styled: "#surface",
			"styled & disabled": "#disabled-surface"
		},
		border: {
			"": false,
			styled: true,
			"styled & focused": "#primary-text",
			"styled & disabled": true
		},
		outline: {
			"": "1bw #primary.0",
			"focused & !styled": "1bw #primary"
		},
		outlineOffset: 1,
		radius: {
			"": 0,
			focused: true,
			styled: true
		},
		padding: {
			"": 0,
			styled: "(.75x - 1bw) (1x - 1bw)"
		},
		height: {
			"": "auto",
			styled: "$size-md"
		},
		transition: "theme",
		Display: {
			display: "block",
			whiteSpace: "nowrap",
			overflow: "hidden",
			textOverflow: "ellipsis",
			maxWidth: "100%",
			preset: "inherit",
			color: "inherit"
		},
		Input: {
			recipe: "reset input / input-autofill",
			preset: "inherit",
			color: "inherit",
			fill: "transparent",
			textAlign: "left",
			width: {
				"": "initial $input-width 100%",
				styled: "100%"
			},
			minWidth: {
				"": "1em",
				styled: 0
			},
			"&::placeholder": { recipe: "input-placeholder" }
		},
		Measure: {
			position: "absolute",
			visibility: "hidden",
			pointerEvents: "none",
			whiteSpace: "pre",
			preset: "inherit",
			height: 0,
			overflow: "hidden"
		},
		Placeholder: {
			recipe: "input-placeholder",
			preset: "inherit"
		}
	}
});
const STYLE_PROPS = [
	...BLOCK_STYLES,
	...OUTER_STYLES,
	...COLOR_STYLES
];
const PROGRAMMATIC_EDIT_BLUR_GRACE_MS = 500;
/**
* Inline editable text. Renders the value as inline content by default and
* swaps to an auto-sizing text input when entering edit mode.
*
* Designed to drop into any text context (tab title, heading, paragraph,
* table cell) without style customization: typography, color, and font family
* are all inherited from the parent.
*
* Value and `isEditing` can each be controlled or uncontrolled independently.
* Programmatic entry via the imperative ref (`startEditing`/`stopEditing`)
* works regardless of `editTrigger`.
*/
const InlineInput = forwardRef(function InlineInput(allProps, ref) {
	const { value: valueProp, defaultValue, onChange, isEditing: isEditingProp, defaultIsEditing, onEditingChange, onSubmit, onCancel, editTrigger = "dblclick", keyboardActivation = true, submitOnBlur = true, trimOnSubmit = true, allowEmpty = false, isDisabled = false, isReadOnly = false, isStyled = false, placeholder, renderDisplay, tooltip = true, tooltipPlacement = "top", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, qa, qaVal, styles: stylesProp, inputStyles, mods: customMods, tokens: customTokens, ...otherProps } = allProps;
	const isControlled = valueProp !== void 0;
	const inputRef = useRef(null);
	const measureRef = useRef(null);
	const rootRef = useRef(null);
	const [value, setValue] = useControlledState(valueProp, defaultValue ?? "", onChange);
	const [isEditing, setIsEditing] = useControlledState(isEditingProp, defaultIsEditing ?? false, onEditingChange);
	const [optimisticValue, setOptimisticValue] = useState(null);
	const displayedValue = optimisticValue ?? value;
	const [draft, setDraft] = useState(value);
	const [inputWidth, setInputWidth] = useState(null);
	const [isFocusWithin, setIsFocusWithin] = useState(false);
	const [ringSuppressed, setRingSuppressed] = useState(false);
	const submitTokenRef = useRef(0);
	const programmaticEditStartRef = useRef(null);
	const pendingRestoreFocusRef = useRef(false);
	const isEditingRef = useRef(isEditing);
	useLayoutEffect(() => {
		isEditingRef.current = isEditing;
	}, [isEditing]);
	useEffect(() => {
		setOptimisticValue(null);
	}, [value]);
	const enterEditing = useEvent(() => {
		if (isDisabled || isReadOnly || isEditing) return;
		submitTokenRef.current += 1;
		isEditingRef.current = true;
		setOptimisticValue(null);
		setDraft(value);
		setIsEditing(true);
	});
	const canRestoreFocus = () => keyboardActivation && editTrigger !== "none" && !isDisabled && !isReadOnly;
	const commit = useEvent((rawValue, restoreFocus = true) => {
		if (!isEditingRef.current) return;
		const next = trimOnSubmit ? rawValue.trim() : rawValue;
		if (restoreFocus && canRestoreFocus()) pendingRestoreFocusRef.current = true;
		if (!next && !allowEmpty) {
			isEditingRef.current = false;
			programmaticEditStartRef.current = null;
			setIsEditing(false);
			onCancel?.();
			return;
		}
		const token = ++submitTokenRef.current;
		if (isControlled) setOptimisticValue(next);
		isEditingRef.current = false;
		programmaticEditStartRef.current = null;
		setValue(next);
		setIsEditing(false);
		const result = onSubmit?.(next);
		if (result != null && typeof result.then === "function") result.then(() => {}, () => {
			if (submitTokenRef.current === token && isControlled) setOptimisticValue(null);
		});
	});
	const cancel = useEvent((restoreFocus = true) => {
		if (!isEditingRef.current) return;
		if (restoreFocus && canRestoreFocus()) pendingRestoreFocusRef.current = true;
		isEditingRef.current = false;
		programmaticEditStartRef.current = null;
		setIsEditing(false);
		onCancel?.();
	});
	useEffect(() => {
		if (!isEditing) setDraft(value);
	}, [value, isEditing]);
	useLayoutEffect(() => {
		if (!isEditing) return;
		inputRef.current?.select();
	}, [isEditing]);
	useLayoutEffect(() => {
		if (isEditing || !pendingRestoreFocusRef.current) return;
		pendingRestoreFocusRef.current = false;
		const root = rootRef.current;
		if (!root) return;
		const active = root.ownerDocument.activeElement;
		if (!(!active || active === root.ownerDocument.body) && !root.contains(active)) return;
		setRingSuppressed(true);
		root.focus();
	}, [isEditing]);
	useLayoutEffect(() => {
		if (!isEditing) return;
		const measure = measureRef.current;
		if (!measure) return;
		setInputWidth(measure.scrollWidth);
	}, [
		isEditing,
		draft,
		placeholder
	]);
	const handleKeyDown = useEvent((e) => {
		programmaticEditStartRef.current = null;
		if (e.key === "Enter") {
			e.preventDefault();
			commit(draft);
			return;
		}
		if (e.key === "Escape") {
			e.preventDefault();
			cancel();
			return;
		}
		if (e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === " " || e.key === "Delete" || e.key === "Backspace") e.stopPropagation();
	});
	const handleInputChange = useEvent((e) => {
		programmaticEditStartRef.current = null;
		setDraft(e.target.value);
	});
	const { isFocusVisible, focusProps: focusRingProps } = useFocusRing();
	const { focusWithinProps } = useFocusWithin({
		onFocusWithinChange: (within) => {
			setIsFocusWithin(within);
			if (!within) setRingSuppressed(false);
		},
		onBlurWithin: () => {
			if (!isEditingRef.current) return;
			if (!submitOnBlur) return;
			const active = rootRef.current?.ownerDocument.activeElement;
			if (active && rootRef.current?.contains(active)) return;
			const startedAt = programmaticEditStartRef.current;
			if (startedAt != null && Date.now() - startedAt < PROGRAMMATIC_EDIT_BLUR_GRACE_MS && isEditingRef.current) {
				requestAnimationFrame(() => {
					if (isEditingRef.current) {
						inputRef.current?.focus();
						inputRef.current?.select();
					}
				});
				return;
			}
			commit(draft, false);
		}
	});
	const handleDblClick = useEvent((e) => {
		e.preventDefault();
		e.stopPropagation();
		enterEditing();
	});
	const handleClick = useEvent((e) => {
		e.preventDefault();
		e.stopPropagation();
		enterEditing();
	});
	const handleRootKeyDown = useEvent((e) => {
		if (e.key === "Enter" || e.key === "F2" || e.key === " ") {
			e.preventDefault();
			e.stopPropagation();
			enterEditing();
		}
	});
	useImperativeHandle(ref, () => ({
		startEditing: () => {
			const wasEditing = isEditingRef.current;
			enterEditing();
			if (!wasEditing && isEditingRef.current) programmaticEditStartRef.current = Date.now();
		},
		stopEditing: (submit = true) => {
			if (!isEditingRef.current) return;
			if (submit) commit(draft);
			else cancel();
		},
		focus: () => {
			if (isEditingRef.current) inputRef.current?.focus();
			else rootRef.current?.focus();
		},
		getValue: () => displayedValue
	}), [
		enterEditing,
		commit,
		cancel,
		draft,
		displayedValue
	]);
	const isEditable = editTrigger !== "none" && !isDisabled && !isReadOnly;
	const showFocusRing = keyboardActivation && (isEditing || isFocusVisible && isFocusWithin && isEditable && !ringSuppressed);
	const mods = useMemo(() => ({
		editing: isEditing,
		editable: isEditable,
		focused: showFocusRing,
		disabled: isDisabled,
		"read-only": isReadOnly,
		styled: isStyled,
		empty: !displayedValue,
		...customMods
	}), [
		isEditing,
		isEditable,
		showFocusRing,
		isDisabled,
		isReadOnly,
		isStyled,
		displayedValue,
		customMods
	]);
	const extractedStyles = extractStyles(otherProps, STYLE_PROPS, useMemo(() => {
		if (!stylesProp && !inputStyles) return void 0;
		if (!inputStyles) return stylesProp;
		const existingInput = stylesProp?.Input ?? void 0;
		return {
			...stylesProp,
			Input: existingInput ? {
				...existingInput,
				...inputStyles
			} : inputStyles
		};
	}, [stylesProp, inputStyles]));
	const tokens = useMemo(() => ({
		...customTokens,
		"$input-width": inputWidth != null ? `${inputWidth}px` : "auto"
	}), [customTokens, inputWidth]);
	const displayContent = renderDisplay ? renderDisplay(displayedValue) : displayedValue || (placeholder ? /* @__PURE__ */ jsx("span", {
		"data-element": "Placeholder",
		children: placeholder
	}) : "");
	const baseProps = filterBaseProps(otherProps, { eventProps: true });
	const { labelRef: tooltipLabelRef, renderWithTooltip } = useAutoTooltip({
		tooltip: isEditing || renderDisplay ? false : tooltip,
		children: displayedValue
	});
	const wantsClick = !isEditing && isEditable && editTrigger === "click";
	const wantsDblClick = !isEditing && isEditable && editTrigger === "dblclick";
	const wantsKeyboard = !isEditing && isEditable && keyboardActivation;
	const renderRoot = (triggerProps, tooltipRef) => {
		const handleRootRef = (element) => {
			rootRef.current = element;
			if (tooltipRef) tooltipRef.current = element;
		};
		const handleDisplayRef = (element) => {
			tooltipLabelRef(element);
		};
		const a11yProps = {};
		if (wantsKeyboard) {
			a11yProps.tabIndex = 0;
			a11yProps.role = "button";
			a11yProps["aria-roledescription"] = "editable text";
			if (ariaLabel) a11yProps["aria-label"] = ariaLabel;
			if (ariaLabelledby) a11yProps["aria-labelledby"] = ariaLabelledby;
		}
		if (isDisabled) a11yProps["aria-disabled"] = true;
		if (isReadOnly) a11yProps["aria-readonly"] = true;
		return /* @__PURE__ */ jsx(InlineInputRoot, {
			ref: handleRootRef,
			qa: qa ?? "InlineInput",
			qaVal,
			mods,
			tokens,
			styles: extractedStyles,
			...mergeProps$1(baseProps, focusWithinProps, focusRingProps, triggerProps ?? {}, {
				onDoubleClick: wantsDblClick ? handleDblClick : void 0,
				onClick: wantsClick ? handleClick : void 0,
				onKeyDown: wantsKeyboard ? handleRootKeyDown : void 0,
				...a11yProps
			}),
			children: isEditing ? /* @__PURE__ */ jsxs(FocusScope, {
				autoFocus: true,
				restoreFocus: false,
				contain: false,
				children: [/* @__PURE__ */ jsx("span", {
					ref: measureRef,
					"data-element": "Measure",
					"aria-hidden": "true",
					children: draft || placeholder || " "
				}), /* @__PURE__ */ jsx("input", {
					ref: inputRef,
					"data-element": "Input",
					type: "text",
					value: draft,
					placeholder,
					disabled: isDisabled,
					readOnly: isReadOnly,
					"aria-label": ariaLabel,
					"aria-labelledby": ariaLabelledby,
					onChange: handleInputChange,
					onKeyDown: handleKeyDown
				})]
			}) : /* @__PURE__ */ jsx("span", {
				ref: handleDisplayRef,
				"data-element": "Display",
				children: displayContent
			})
		});
	};
	return renderWithTooltip(renderRoot, tooltipPlacement);
});

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