import { ArrowDropDown, ChevronRight, ExpandMore } from '@mui/icons-material'; import { CheckBox, CheckBoxOutlineBlank, IndeterminateCheckBox } from '@mui/icons-material'; import { Box, Checkbox, InputAdornment, Popover, TextField, Typography } from '@mui/material'; import { SimpleTreeView, TreeItem } from '@mui/x-tree-view'; import { memo, useMemo, useState } from 'react'; import { filterColumnTree, getColumnTreeLeafKeys } from '../RealtimeTable/utils/columnUtils'; const EMPTY_IDS = []; const CollapsibleTree = ({ columnTree, excludeColumnKeys = EMPTY_IDS, placeholder = 'Скрыть столбцы', placeholderSelect = 'Скрыто столбцов: ', selectedIds = EMPTY_IDS, onSelectNode, onSelectAllNodes, forStage = false, }) => { const [expandedItems, setExpandedItems] = useState([]); const [anchorEl, setAnchorEl] = useState(null); const selectedNodes = selectedIds; const visibleColumnTree = useMemo(() => filterColumnTree(columnTree, excludeColumnKeys), [columnTree, excludeColumnKeys]); const visibleLeafKeys = useMemo(() => getColumnTreeLeafKeys(visibleColumnTree), [visibleColumnTree]); const handleExpandedItemsChange = (_event, itemIds) => { setExpandedItems(itemIds); }; const handleSelectAll = (isSelected) => { if (onSelectAllNodes) { onSelectAllNodes(visibleLeafKeys, !isSelected); return; } for (const col of visibleColumnTree) { onSelectNode?.(col, !isSelected); } }; const getChildrenIds = (node) => { const getLeafColumn = (col) => { const hasColumns = col.columns && Array.isArray(col.columns); if (!hasColumns || col.columns.length === 0) { return [col]; } const allChildrenAreLeaves = col.columns.every( (child) => !child.columns || !Array.isArray(child.columns) || child.columns.length === 0, ); if (allChildrenAreLeaves) { return col.columns; } return col.columns.flatMap((childCol) => getLeafColumn(childCol)); }; const leafColumns = getLeafColumn(node); return leafColumns.map((c) => c.id || c.accessorKey); }; const isNodeChecked = (node) => { const childrenIds = getChildrenIds(node); const selectedChildren = childrenIds.filter((id) => selectedNodes.includes(id)); if (selectedChildren.length === 0) return false; if (selectedChildren.length === childrenIds.length) return true; return 'indeterminate'; }; const handleNodeCheck = (node, isSelected) => { onSelectNode?.(node, !isSelected); }; const getId = (column) => { const getFullColumnPath = (column) => { const ancestors = []; let current = column; while (current) { ancestors.unshift(current.id || current.accessorKey); current = current.parent; } return ancestors; }; return getFullColumnPath(column).join('_'); }; const renderTree = (nodes) => { if (!nodes || nodes.length === 0) return null; return nodes.map((node) => { const checkStatus = isNodeChecked(node); return ( e.stopPropagation()} checked={checkStatus === true} indeterminate={checkStatus === 'indeterminate'} onChange={() => handleNodeCheck(node, checkStatus === 'indeterminate' ? true : !checkStatus)} icon={} checkedIcon={} indeterminateIcon={} /> {node.columnDef?.header || node.name || node.header || 'Без названия'} }> {node.columns && node.columns.length > 0 && renderTree(node.columns)} ); }); }; const treeElements = useMemo(() => { return visibleColumnTree.length > 0 ? renderTree(visibleColumnTree) : null; }, [visibleColumnTree, selectedNodes]); const handleOpen = (event) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; const visibleSelectedCount = selectedNodes.filter((id) => visibleLeafKeys.includes(id)).length; const selectedCount = visibleSelectedCount; const isAllSelected = () => { if (!visibleColumnTree.length) return false; let isSelect = true; for (const col of visibleColumnTree) { const isChecked = isNodeChecked(col); isSelect = isChecked === 'indeterminate' ? false : isChecked && isSelect; } return isSelect; }; const open = Boolean(anchorEl); const id = open ? 'tree-popover' : undefined; const getDisplayValue = () => { if (selectedCount === 0) { return ''; } return `${placeholderSelect} ${selectedCount}`; }; return ( <> ), }, }} sx={{ cursor: 'pointer', height: '2.5rem', padding: 0, '& .MuiInputBase-root': { cursor: 'pointer', height: '2.5rem', }, '& input': { cursor: 'pointer', }, }} /> 0 && !isAllSelected()} onChange={(e) => handleSelectAll(e.target.checked)} icon={} checkedIcon={} indeterminateIcon={} /> Выбрать все {treeElements} ); }; export default memo(CollapsibleTree);