import { c as _c } from "react/compiler-runtime"; import React from 'react'; import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; import { Box } from '../../ink.js'; import { type OptionWithDescription, Select } from '../CustomSelect/select.js'; export type TreeNode = { id: string | number; value: T; label: string; description?: string; dimDescription?: boolean; children?: TreeNode[]; metadata?: Record; }; type FlattenedNode = { node: TreeNode; depth: number; isExpanded: boolean; hasChildren: boolean; parentId?: string | number; }; export type TreeSelectProps = { /** * Tree nodes to display. */ readonly nodes: TreeNode[]; /** * Callback when a node is selected. */ readonly onSelect: (node: TreeNode) => void; /** * Callback when cancel is pressed. */ readonly onCancel?: () => void; /** * Callback when focused node changes. */ readonly onFocus?: (node: TreeNode) => void; /** * Node to focus by ID. */ readonly focusNodeId?: string | number; /** * Number of visible options. */ readonly visibleOptionCount?: number; /** * Layout of the options. */ readonly layout?: 'compact' | 'expanded' | 'compact-vertical'; /** * When disabled, user input is ignored. */ readonly isDisabled?: boolean; /** * When true, hides the numeric indexes next to each option. */ readonly hideIndexes?: boolean; /** * Function to determine if a node should be initially expanded. * If not provided, all nodes start collapsed. */ readonly isNodeExpanded?: (nodeId: string | number) => boolean; /** * Callback when a node is expanded. */ readonly onExpand?: (nodeId: string | number) => void; /** * Callback when a node is collapsed. */ readonly onCollapse?: (nodeId: string | number) => void; /** * Custom prefix function for parent nodes * @param isExpanded - Whether the parent node is currently expanded * @returns The prefix string to display (default: '▼ ' when expanded, '▶ ' when collapsed) */ readonly getParentPrefix?: (isExpanded: boolean) => string; /** * Custom prefix function for child nodes * @param depth - The depth of the child node in the tree (0-indexed from parent) * @returns The prefix string to display (default: ' ▸ ') */ readonly getChildPrefix?: (depth: number) => string; /** * Callback when user presses up from the first item. * If provided, navigation will not wrap to the last item. */ readonly onUpFromFirstItem?: () => void; }; /** * TreeSelect is a generic component for selecting items from a hierarchical tree structure. * It handles expand/collapse state, keyboard navigation, and renders the tree as a flat list * using the Select component. */ export function TreeSelect(t0) { const $ = _c(48); const { nodes, onSelect, onCancel, onFocus, focusNodeId, visibleOptionCount, layout: t1, isDisabled: t2, hideIndexes: t3, isNodeExpanded, onExpand, onCollapse, getParentPrefix, getChildPrefix, onUpFromFirstItem } = t0; const layout = t1 === undefined ? "expanded" : t1; const isDisabled = t2 === undefined ? false : t2; const hideIndexes = t3 === undefined ? false : t3; let t4; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t4 = new Set(); $[0] = t4; } else { t4 = $[0]; } const [internalExpandedIds, setInternalExpandedIds] = React.useState(t4); const isProgrammaticFocusRef = React.useRef(false); const lastFocusedIdRef = React.useRef(null); let t5; if ($[1] !== internalExpandedIds || $[2] !== isNodeExpanded) { t5 = nodeId => { if (isNodeExpanded) { return isNodeExpanded(nodeId); } return internalExpandedIds.has(nodeId); }; $[1] = internalExpandedIds; $[2] = isNodeExpanded; $[3] = t5; } else { t5 = $[3]; } const isExpanded = t5; let result; if ($[4] !== isExpanded || $[5] !== nodes) { result = []; function traverse(node, depth, parentId) { const hasChildren = !!node.children && node.children.length > 0; const nodeIsExpanded = isExpanded(node.id); result.push({ node, depth, isExpanded: nodeIsExpanded, hasChildren, parentId }); if (hasChildren && nodeIsExpanded && node.children) { for (const child of node.children) { traverse(child, depth + 1, node.id); } } } for (const node_0 of nodes) { traverse(node_0, 0); } $[4] = isExpanded; $[5] = nodes; $[6] = result; } else { result = $[6]; } const flattenedNodes = result; const defaultGetParentPrefix = _temp; const defaultGetChildPrefix = _temp2; const parentPrefixFn = getParentPrefix ?? defaultGetParentPrefix; const childPrefixFn = getChildPrefix ?? defaultGetChildPrefix; let t6; if ($[7] !== childPrefixFn || $[8] !== parentPrefixFn) { t6 = flatNode => { let prefix = ""; if (flatNode.hasChildren) { prefix = parentPrefixFn(flatNode.isExpanded); } else { if (flatNode.depth > 0) { prefix = childPrefixFn(flatNode.depth); } } return prefix + flatNode.node.label; }; $[7] = childPrefixFn; $[8] = parentPrefixFn; $[9] = t6; } else { t6 = $[9]; } const buildLabel = t6; let t7; if ($[10] !== buildLabel || $[11] !== flattenedNodes) { t7 = flattenedNodes.map(flatNode_0 => ({ label: buildLabel(flatNode_0), description: flatNode_0.node.description, dimDescription: flatNode_0.node.dimDescription ?? true, value: flatNode_0.node.id })); $[10] = buildLabel; $[11] = flattenedNodes; $[12] = t7; } else { t7 = $[12]; } const options = t7; let map; if ($[13] !== flattenedNodes) { map = new Map(); flattenedNodes.forEach(fn => map.set(fn.node.id, fn.node)); $[13] = flattenedNodes; $[14] = map; } else { map = $[14]; } const nodeMap = map; let t8; if ($[15] !== flattenedNodes) { t8 = nodeId_0 => flattenedNodes.find(fn_0 => fn_0.node.id === nodeId_0); $[15] = flattenedNodes; $[16] = t8; } else { t8 = $[16]; } const findFlattenedNode = t8; let t9; if ($[17] !== findFlattenedNode || $[18] !== onCollapse || $[19] !== onExpand) { t9 = (nodeId_1, shouldExpand) => { const flatNode_1 = findFlattenedNode(nodeId_1); if (!flatNode_1 || !flatNode_1.hasChildren) { return; } if (shouldExpand) { if (onExpand) { onExpand(nodeId_1); } else { setInternalExpandedIds(prev => new Set(prev).add(nodeId_1)); } } else { if (onCollapse) { onCollapse(nodeId_1); } else { setInternalExpandedIds(prev_0 => { const newSet = new Set(prev_0); newSet.delete(nodeId_1); return newSet; }); } } }; $[17] = findFlattenedNode; $[18] = onCollapse; $[19] = onExpand; $[20] = t9; } else { t9 = $[20]; } const toggleExpand = t9; let t10; if ($[21] !== findFlattenedNode || $[22] !== focusNodeId || $[23] !== isDisabled || $[24] !== nodeMap || $[25] !== onFocus || $[26] !== toggleExpand) { t10 = e => { if (!focusNodeId || isDisabled) { return; } const flatNode_2 = findFlattenedNode(focusNodeId); if (!flatNode_2) { return; } if (e.key === "right" && flatNode_2.hasChildren) { e.preventDefault(); toggleExpand(focusNodeId, true); } else { if (e.key === "left") { if (flatNode_2.hasChildren && flatNode_2.isExpanded) { e.preventDefault(); toggleExpand(focusNodeId, false); } else { if (flatNode_2.parentId !== undefined) { e.preventDefault(); isProgrammaticFocusRef.current = true; toggleExpand(flatNode_2.parentId, false); if (onFocus) { const parentNode = nodeMap.get(flatNode_2.parentId); if (parentNode) { onFocus(parentNode); } } } } } } }; $[21] = findFlattenedNode; $[22] = focusNodeId; $[23] = isDisabled; $[24] = nodeMap; $[25] = onFocus; $[26] = toggleExpand; $[27] = t10; } else { t10 = $[27]; } const handleKeyDown = t10; let t11; if ($[28] !== nodeMap || $[29] !== onSelect) { t11 = nodeId_2 => { const node_1 = nodeMap.get(nodeId_2); if (!node_1) { return; } onSelect(node_1); }; $[28] = nodeMap; $[29] = onSelect; $[30] = t11; } else { t11 = $[30]; } const handleChange = t11; let t12; if ($[31] !== nodeMap || $[32] !== onFocus) { t12 = nodeId_3 => { if (isProgrammaticFocusRef.current) { isProgrammaticFocusRef.current = false; return; } if (lastFocusedIdRef.current === nodeId_3) { return; } lastFocusedIdRef.current = nodeId_3; if (onFocus) { const node_2 = nodeMap.get(nodeId_3); if (node_2) { onFocus(node_2); } } }; $[31] = nodeMap; $[32] = onFocus; $[33] = t12; } else { t12 = $[33]; } const handleFocus = t12; let t13; if ($[34] !== focusNodeId || $[35] !== handleChange || $[36] !== handleFocus || $[37] !== hideIndexes || $[38] !== isDisabled || $[39] !== layout || $[40] !== onCancel || $[41] !== onUpFromFirstItem || $[42] !== options || $[43] !== visibleOptionCount) { t13 =