/
kaws
/
ui-kit-ce
Обзор
Документация
Войти
/
kaws
/
ui-kit-ce
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
packages/tree/src/userComponents/advanced/TreeNode/TreeNode.tsx
273 строки
7 KB
Урядышев Павел Александрович
refactor(tree): bug fix
10 июл 2025, 16:41
10 июл 2025, 16:41
71acdc5
Код
Авторство
О чём код?
'use client' import * as React from 'react' import { useTreeContext, useTreeNodeContext, useTreePropsContext, } from '../../../contexts' import { TreeNodeControl, TreeNodeChecker, TreeNodeContentContainer, TreeNodeExpander, TreeNodeIconContainer, TreeTrail, } from '../../../components' import { getDataIdProperty, getTreeNodeIndent, TreeSize, TreeSizeType, } from '../../../common' import { TreeItem, TreeSelectMode } from '../../../hooks' import { ComponentPropsWithRefFix } from '@v-uik/common' import { clsx } from '@v-uik/theme' import { LastParentsOnLevelMap, TreeBaseComponentProps } from '../../../types' import { useMergedRefs } from '@v-uik/hooks' export type TreeNodeClasses = { /** * Стиль, применяемый к узлу дерева. * * @inner */ node?: string } export type TreeNodeProps<TItem = TreeItem> = ComponentPropsWithRefFix<'span'> & { /** * Ключ узла дерева. * * @inner */ nodeKey: React.Key /** * Индекс элемента дерева в массиве данных. * * @inner */ nodeIndex: number /** * Карта соответствия узлов дерева, которые являются последними дочерними элементами в списке относительно * текущего узла дерева. * * @ignore */ lastParentsOnLevelMap?: LastParentsOnLevelMap /** * Объект данных узла дерева. * * @inner */ data: TItem /** * Размер узла дерева. * * @inner */ size?: TreeSizeType /** * CSS классы компонента. * * @inner */ classes?: TreeNodeClasses } const _TreeNode = React.forwardRef( (props: TreeNodeProps, ref: React.Ref<HTMLSpanElement>) => { const { nodeIndex, nodeKey, lastParentsOnLevelMap = {}, children: mainItemChildren, data, size = TreeSize.md, classes, } = props const { classesMap, getters: { getTreeItemKey, getTreeItemCheckDisabled, getTreeItemDisabled, getTreeItemLabel, getTreeItemLoadable, }, } = useTreeContext() const { disableExpand, toggleExpand, maxContentLines = 1, showIcons, showTrails, withHalfLevelSpacer, checkable, toggleCheck, getExpandButtonInnerProps, selectMode = TreeSelectMode.strict, levelSpacer, multipleSelect, disableFocusEffectOnCheckboxClick, disableFocusEffectOnClick, disableFocusEffectOnExpanderClick, getLabelCheckboxInnerProps, getTreeNodeData, getNodeInnerProps, getIsSelected, getIsIndeterminate, getIsChecked, disabled: disabledProp, getIsExpanded, getIsLoading, suffixControls, prefixControls, } = useTreePropsContext() const { getTreeNodeHandlersById } = useTreeNodeContext() const item = getTreeNodeData(nodeKey) const { $meta: { depth, closestChildKey }, children, } = item const nodesInParent = item.$meta?.parentKey ? getTreeNodeData(item.$meta.parentKey).children || [] : [] const key = getTreeItemKey(item as TreeItem) const nodeInnerProps = getNodeInnerProps?.(item) const nodeClassName = clsx( classesMap.node, nodeInnerProps?.className, classes?.node ) const mergedSpanRef = useMergedRefs([ref, nodeInnerProps?.ref ?? null]) if (!key) { return null } const selected = getIsSelected(key) const loadable = getTreeItemLoadable(item) const indeterminate = getIsIndeterminate(key) const disabled = disabledProp || getTreeItemDisabled(item as TreeItem) const isLastChild = nodeIndex === nodesInParent.length - 1 const checked = getIsChecked(key) const checkDisabled = getTreeItemCheckDisabled(item as TreeItem) const label = getTreeItemLabel(item as TreeItem) const expanded = getIsExpanded(key) const loading = getIsLoading(key) const canCheck = !disabled && checkable && !checkDisabled const baseProps: TreeBaseComponentProps<TreeItem> = { nodeKey: key, depth, size, data, disabled, expanded, selected, loading, } const { handleItemClick, handleMouseDown } = getTreeNodeHandlersById(key) return ( <span {...getDataIdProperty(key)} {...nodeInnerProps} ref={mergedSpanRef} className={nodeClassName} onMouseDown={handleMouseDown} onClick={handleItemClick} > {!multipleSelect && selectMode === TreeSelectMode.strict && selected && <div className={classesMap.selectedIndicator} />} {showTrails && ( <TreeTrail lastParentsOnLevelMap={lastParentsOnLevelMap} levelSpacer={levelSpacer} withHalfLevelSpacer={withHalfLevelSpacer} isLastChild={isLastChild} childrenCount={children?.length || 0} {...baseProps} /> )} {depth > 0 && ( <div style={{ marginLeft: depth * getTreeNodeIndent(withHalfLevelSpacer, levelSpacer), }} /> )} {!disableExpand && ( <TreeNodeExpander {...baseProps} {...getExpandButtonInnerProps?.(item)} disableFocusEffectOnExpanderClick={ disableFocusEffectOnExpanderClick } toggleExpand={toggleExpand} mock={!closestChildKey} /> )} {checkable && ( <TreeNodeChecker indeterminate={indeterminate} checked={checked} toggleCheck={toggleCheck} disableFocusEffectOnCheckboxClick={ disableFocusEffectOnCheckboxClick } disableFocusEffectOnClick={disableFocusEffectOnClick} labelProps={getLabelCheckboxInnerProps?.(item)} {...baseProps} disabled={!canCheck} /> )} {showIcons && ( <TreeNodeIconContainer canExpand={!!closestChildKey || loadable} {...baseProps} /> )} {prefixControls?.map(({ id, renderControl }) => ( <TreeNodeControl key={id}> {renderControl?.(baseProps)} </TreeNodeControl> ))} <TreeNodeContentContainer maxContentLines={maxContentLines} {...baseProps} > {mainItemChildren} {label} </TreeNodeContentContainer> {suffixControls?.map(({ id, renderControl }) => ( <TreeNodeControl key={id}> {renderControl?.(baseProps)} </TreeNodeControl> ))} </span> ) } ) export const TreeNode = _TreeNode as <TItem extends unknown = TreeItem>( props: Omit<React.PropsWithChildren<TreeNodeProps<TItem>>, 'ref'> & { ref?: React.Ref<HTMLSpanElement> | null } ) => React.ReactElement