@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 12.7 kB
Source Map (JSON)
{"version":3,"file":"Tree.mjs","names":["useControlledState"],"sources":["../../../src/base-ui/Tree/Tree.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { memo, useId, useMemo, useRef, useState } from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { FocusScope, focusScopeItem, useScopeArrowNav } from '@/base-ui/FocusScope';\n\nimport { TreeContext, type TreeContextValue } from './context';\nimport { styles } from './style';\nimport TreeNode from './TreeNode';\nimport type { TreeDataNode, TreeProps } from './type';\nimport { conductCheck, flattenVisible, getAllKeys, getAncestorKeys, getSubtreeKeys } from './utils';\n\nconst EMPTY: string[] = [];\n\nconst Tree = memo<TreeProps>(\n ({\n blockNode = false,\n checkStrictly = false,\n checkable = false,\n checkedKeys: checkedKeysProp,\n className,\n classNames = {},\n defaultCheckedKeys = EMPTY,\n defaultExpandAll = false,\n defaultExpandedKeys = EMPTY,\n defaultSelectedKeys = EMPTY,\n disabled = false,\n expandedKeys: expandedKeysProp,\n indent = 20,\n multiple = false,\n onCheck,\n onExpand,\n onRightClick,\n onSelect,\n scopeId: scopeIdProp,\n selectedKeys: selectedKeysProp,\n showIcon = false,\n showLine = false,\n size = 'middle',\n style,\n styles: customStyles = {},\n switcherIcon,\n titleRender,\n treeData,\n vimKeys = false,\n }) => {\n const generatedId = useId();\n const scopeId = scopeIdProp ?? generatedId;\n const [expandedKeys, setExpandedKeys] = useControlledState<string[]>(\n defaultExpandAll ? getAllKeys(treeData) : defaultExpandedKeys,\n { value: expandedKeysProp },\n );\n const [selectedKeys, setSelectedKeys] = useControlledState<string[]>(defaultSelectedKeys, {\n value: selectedKeysProp,\n });\n const [checkedKeys, setCheckedKeys] = useControlledState<string[]>(defaultCheckedKeys, {\n value: checkedKeysProp,\n });\n\n const expanded = useMemo(() => new Set(expandedKeys), [expandedKeys]);\n const selected = useMemo(() => new Set(selectedKeys), [selectedKeys]);\n const flat = useMemo(() => flattenVisible(treeData, expanded), [treeData, expanded]);\n const { checked, halfChecked } = useMemo(\n () =>\n checkStrictly\n ? { checked: new Set(checkedKeys), halfChecked: new Set<string>() }\n : conductCheck(treeData, checkedKeys),\n [checkStrictly, treeData, checkedKeys],\n );\n\n const [activeKeyState, setActiveKeyState] = useState<string | null>(null);\n const setActiveKey = setActiveKeyState;\n const activeKey =\n activeKeyState && flat.some((row) => row.node.key === activeKeyState)\n ? activeKeyState\n : (selectedKeys.find((key) => flat.some((row) => row.node.key === key)) ??\n flat[0]?.node.key ??\n null);\n\n const anchorRef = useRef<string | null>(null);\n\n const rowOf = (key: string) =>\n document.querySelector<HTMLElement>(`[data-focus-scope=\"${scopeId}\"] [data-id=\"${key}\"]`);\n const focusRow = (key: string) => {\n setActiveKey(key);\n const row = rowOf(key);\n if (row) focusScopeItem(scopeId, row);\n };\n\n const isDisabled = (node: TreeDataNode) => disabled || !!node.disabled;\n\n const toggleExpand = (node: TreeDataNode) => {\n const willExpand = !expanded.has(node.key);\n const next = willExpand\n ? [...expandedKeys, node.key]\n : expandedKeys.filter((key) => key !== node.key);\n setExpandedKeys(next);\n onExpand?.(next, { expanded: willExpand, node });\n };\n\n const toggleSelect: TreeContextValue['toggleSelect'] = (node, event) => {\n if (isDisabled(node) || node.selectable === false) return;\n let next: string[];\n if (multiple && event?.shiftKey && anchorRef.current) {\n const a = flat.findIndex((row) => row.node.key === anchorRef.current);\n const b = flat.findIndex((row) => row.node.key === node.key);\n next = flat\n .slice(Math.min(a, b), Math.max(a, b) + 1)\n .filter((row) => !isDisabled(row.node) && row.node.selectable !== false)\n .map((row) => row.node.key);\n } else if (multiple && (event?.metaKey || event?.ctrlKey)) {\n next = selected.has(node.key)\n ? selectedKeys.filter((key) => key !== node.key)\n : [...selectedKeys, node.key];\n anchorRef.current = node.key;\n } else {\n next = [node.key];\n anchorRef.current = node.key;\n }\n setSelectedKeys(next);\n setActiveKey(node.key);\n onSelect?.(next, { node, selected: next.includes(node.key) });\n };\n\n const toggleCheck = (node: TreeDataNode) => {\n if (isDisabled(node) || node.checkable === false) return;\n const willCheck = !checked.has(node.key);\n const keys = checkStrictly ? [node.key] : getSubtreeKeys(node);\n const base = new Set(checkStrictly ? checkedKeys : checked);\n keys.forEach((key) => (willCheck ? base.add(key) : base.delete(key)));\n if (!willCheck && !checkStrictly) getAncestorKeys(treeData, node.key).forEach((key) => base.delete(key));\n const next = checkStrictly ? [...base] : [...conductCheck(treeData, base).checked];\n setCheckedKeys(next);\n setActiveKey(node.key);\n onCheck?.(next, { checked: willCheck, node });\n };\n\n const currentRow = () => {\n const focused = document.activeElement?.closest<HTMLElement>('[role=\"treeitem\"]');\n const key = focused?.dataset.id ?? activeKey;\n return flat.find((row) => row.node.key === key);\n };\n\n const withRow = (fn: (row: (typeof flat)[number], event: KeyboardEvent) => void) =>\n (event: KeyboardEvent) => {\n const row = currentRow();\n if (row) fn(row, event);\n };\n\n useScopeArrowNav({\n extra: {\n ' ': withRow((row, event) => {\n if (checkable && row.node.checkable !== false) toggleCheck(row.node);\n else toggleSelect(row.node, event);\n }),\n '*': withRow((row) => {\n const siblings = flat.filter(\n (r) => r.parentKey === row.parentKey && r.hasChildren && !expanded.has(r.node.key),\n );\n if (siblings.length === 0) return;\n const next = [...expandedKeys, ...siblings.map((r) => r.node.key)];\n setExpandedKeys(next);\n onExpand?.(next, { expanded: true, node: row.node });\n }),\n ArrowLeft: withRow((row) => {\n if (row.hasChildren && expanded.has(row.node.key)) toggleExpand(row.node);\n else if (row.parentKey) focusRow(row.parentKey);\n }),\n ArrowRight: withRow((row) => {\n if (!row.hasChildren) return;\n if (expanded.has(row.node.key)) focusRow(flat[row.index + 1].node.key);\n else toggleExpand(row.node);\n }),\n Enter: withRow((row, event) => toggleSelect(row.node, event)),\n },\n itemSelector: '[role=\"treeitem\"]',\n onItemFocus: (el) => el.dataset.id && setActiveKey(el.dataset.id),\n scopeId,\n vimKeys,\n });\n\n const ctx: TreeContextValue = {\n activeKey,\n blockNode,\n checkable,\n checked,\n classNames,\n disabled,\n expanded,\n halfChecked,\n indent,\n onRightClick: (event, node) => onRightClick?.({ event, node }),\n selected,\n setActiveKey,\n showIcon,\n showLine,\n size,\n styles: customStyles,\n switcherIcon,\n titleRender,\n toggleCheck,\n toggleExpand,\n toggleSelect,\n };\n\n return (\n <TreeContext value={ctx}>\n <FocusScope\n className={cx(styles.root, classNames.root, className)}\n id={scopeId}\n role=\"tree\"\n style={{ ...customStyles.root, ...style }}\n >\n {treeData.map((node, i) => (\n <TreeNode\n depth={0}\n isLast={i === treeData.length - 1}\n key={node.key}\n node={node}\n trail={[]}\n />\n ))}\n </FocusScope>\n </TreeContext>\n );\n },\n);\n\nTree.displayName = 'Tree';\n\nexport default Tree;\n"],"mappings":"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"}