/*! ratio-ui · design system for knowledge sharing · https://github.com/losol/ratio-ui · (c) 2026 Losol AS · MPL-2.0 */
"use client";
import { useEffect, useRef } from "react";
import { jsx } from "react/jsx-runtime";
import { DropIndicator, useDragAndDrop } from "react-aria-components";
import { useTreeData } from "react-stately";
//#region src/tree/useSortableTree.tsx
/** Internal drag payload — carries the dragged node's key. */
var DRAG_TYPE = "application/x-ratio-tree-node";
/** Rebuild the nested `T[]` from React Stately's tree nodes. */
function serialize(nodes) {
	return nodes.map((node) => {
		const children = node.children ?? [];
		return Array.isArray(node.value.children) || children.length > 0 ? {
			...node.value,
			children: serialize(children)
		} : node.value;
	});
}
/**
* State + drag-and-drop wiring for {@link Tree}. Wraps React Stately's
* `useTreeData` (reorder/reparent operations) and React Aria's
* `useDragAndDrop` (pointer + keyboard DnD). Kept separate so the tree can be
* driven headlessly or rendered by a different shell.
*
* @beta Prop shape may change before release.
*/
function useSortableTree(options) {
	const { items, onChange, canHaveChildren, getLabel } = options;
	const tree = useTreeData({
		initialItems: items,
		getKey: (item) => item.id,
		getChildren: (item) => item.children ?? []
	});
	const onChangeRef = useRef(onChange);
	useEffect(() => {
		onChangeRef.current = onChange;
	});
	const mounted = useRef(false);
	useEffect(() => {
		if (!mounted.current) {
			mounted.current = true;
			return;
		}
		onChangeRef.current?.(serialize(tree.items));
	}, [tree.items]);
	const { dragAndDropHooks } = useDragAndDrop({
		getItems: (keys) => [...keys].map((key) => {
			const node = tree.getItem(key);
			const label = node && getLabel ? getLabel(node.value) : String(key);
			return {
				[DRAG_TYPE]: String(key),
				"text/plain": label
			};
		}),
		renderDropIndicator: (target) => /* @__PURE__ */ jsx(DropIndicator, {
			target,
			className: "tree__drop-indicator"
		}),
		shouldAcceptItemDrop: (target) => {
			if (target.dropPosition !== "on" || !canHaveChildren) return true;
			const node = tree.getItem(target.key);
			return node ? canHaveChildren(node.value) : false;
		},
		onReorder(e) {
			if (e.target.dropPosition === "before") tree.moveBefore(e.target.key, e.keys);
			else if (e.target.dropPosition === "after") tree.moveAfter(e.target.key, e.keys);
		},
		async onItemDrop(e) {
			const keys = await Promise.all(e.items.filter((item) => item.kind === "text" && item.types.has(DRAG_TYPE)).map((item) => item.getText(DRAG_TYPE)));
			let index = 0;
			for (const key of keys) if (key) tree.move(key, e.target.key, index++);
		}
	});
	return {
		tree,
		dragAndDropHooks
	};
}
//#endregion
export { useSortableTree };

//# sourceMappingURL=useSortableTree.js.map