UNPKG

@ant-design/x

Version:

Craft AI-driven interfaces effortlessly

271 lines (250 loc) 9.83 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _util = require("@rc-component/util"); var _antd = require("antd"); var _clsx = require("clsx"); var _react = _interopRequireWildcard(require("react")); var _useProxyImperativeHandle = _interopRequireDefault(require("../_util/hooks/use-proxy-imperative-handle")); var _useXComponentConfig = _interopRequireDefault(require("../_util/hooks/use-x-component-config")); var _locale = require("../locale"); var _en_US = _interopRequireDefault(require("../locale/en_US")); var _xProvider = require("../x-provider"); var _DirectoryTree = _interopRequireDefault(require("./DirectoryTree")); var _FilePreview = _interopRequireDefault(require("./FilePreview")); var _style = _interopRequireDefault(require("./style")); // File content service interface // Folder properties // Ref interface type const ForwardFolder = /*#__PURE__*/_react.default.forwardRef((props, ref) => { const { prefixCls: customizePrefixCls, className, classNames, styles, style, treeData, directoryIcons, previewRender, directoryTitle, previewTitle, selectable = true, defaultSelectedFile, defaultExpandAll = true, selectedFile, onSelectedFileChange, directoryTreeWith = 278, emptyRender, defaultExpandedPaths, expandedPaths, onExpandedPathsChange, onFileClick, onFolderClick } = props; // ============================= Refs ============================= const containerRef = _react.default.useRef(null); (0, _useProxyImperativeHandle.default)(ref, () => { return { nativeElement: containerRef.current }; }); // ============================ State ============================ // Find node and validate path const findNodeAndValidate = (0, _react.useCallback)((path, validateAsFile = false) => { if (!path) return { node: undefined, isValid: false }; const segments = Array.isArray(path) ? path.filter(Boolean) : path.split('/').filter(Boolean); if (segments.length === 0) return { node: undefined, isValid: false }; const findNode = (nodes, index = 0) => { if (index >= segments.length) return undefined; const currentSegment = segments[index]; for (const node of nodes) { if (node.path === currentSegment) { return index === segments.length - 1 ? node : node.children ? findNode(node.children, index + 1) : undefined; } } return undefined; }; const node = findNode(treeData); const isValid = validateAsFile ? !!node && (!node?.children || node.children.length === 0) : !!node; return { node, isValid }; }, [treeData]); const [validSelectedFile, setValidSelectedFile] = (0, _react.useState)(false); const isValidSelectedFile = filePath => !!(filePath && filePath.length > 0 && findNodeAndValidate(filePath, true).isValid); const [expandedPathsState, setExpandedPaths] = (0, _util.useControlledState)(defaultExpandedPaths, expandedPaths); const [selectedFileState, setSelectedFileState] = (0, _util.useControlledState)(isValidSelectedFile(defaultSelectedFile || []) ? defaultSelectedFile || [] : [], selectedFile); (0, _react.useEffect)(() => { const isValid = isValidSelectedFile(selectedFile || defaultSelectedFile || []); setValidSelectedFile(isValid); }, [selectedFile, treeData, defaultSelectedFile]); const [fileContent, setFileContent] = (0, _react.useState)(''); const [loadingContent, setLoadingContent] = (0, _react.useState)(false); // ============================ Prefix ============================ const { getPrefixCls, direction } = (0, _xProvider.useXProviderContext)(); const prefixCls = getPrefixCls('folder', customizePrefixCls); const [hashId, cssVarCls] = (0, _style.default)(prefixCls); const contextConfig = (0, _useXComponentConfig.default)('folder'); const [locale] = (0, _locale.useLocale)('Folder', _en_US.default.Folder); // ============================ Style ============================ const mergedCls = (0, _clsx.clsx)(prefixCls, contextConfig.className, className, classNames?.root, hashId, cssVarCls, { [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-selectable`]: selectable }); // ============================ Event Handlers ============================ const handleSelect = (_keys, info) => { const keys = _keys; const nodes = Array.isArray(info.selectedNodes) ? info.selectedNodes : [info.selectedNodes]; // Check if a folder was clicked const isFolder = nodes.some(node => !node.isLeaf); if (isFolder) { // Click folder: don't update selectedFileState, only trigger folder click event if (nodes.length === 1) { const node = nodes[0]; onFolderClick?.(node.path); } return; } // Convert full path to array format const pathArray = keys[0]?.split('/').filter(Boolean) || []; // Avoid empty or invalid paths if (pathArray.length === 0) return; // Get selected file name and content (single file selection) const selectedNode = nodes[0]; const fileName = selectedNode?.title; const fileContent = selectedNode?.content; // Trigger selection change callback (main interaction method) onSelectedFileChange?.({ path: pathArray, title: fileName, content: fileContent }); // // Update internal state in uncontrolled mode const isControlled = selectedFile !== undefined; if (!isControlled) { setValidSelectedFile(true); setSelectedFileState(pathArray); } // Handle single file click event if (nodes.length === 1) { const node = nodes[0]; onFileClick?.(node.path, node.content); } }; const handleExpand = keys => { const newPaths = keys; setExpandedPaths(newPaths); onExpandedPathsChange?.(newPaths); }; // ============================ Effects ============================ (0, _react.useEffect)(() => { const loadFileContent = async () => { if (!validSelectedFile || selectedFileState.length === 0) { setFileContent(''); setLoadingContent(false); return; } const filePath = selectedFileState.join('/'); // First check if the node already has content const segments = filePath.split('/').filter(segment => segment !== ''); const { node } = findNodeAndValidate(segments); // If file content service is available, use it to load content if (props.fileContentService) { setLoadingContent(true); try { const content = await props.fileContentService.loadFileContent(filePath); setFileContent(content); } catch (error) { setFileContent(`// ${locale?.loadError}: ${error instanceof Error ? error.message : 'Unknown error'}`); } finally { setLoadingContent(false); } } else if (node?.content) { // If node already has content, use it directly setFileContent(node.content); setLoadingContent(false); return; } else { // No file content service, show prompt message setFileContent(`// ${locale.noService}`); setLoadingContent(false); } }; loadFileContent(); }, [validSelectedFile, selectedFileState, treeData, props.fileContentService, findNodeAndValidate]); // ============================ Style ============================ const mergedStyle = { ...contextConfig.style, ...styles?.root, ...style }; return /*#__PURE__*/_react.default.createElement("div", { ref: containerRef, className: mergedCls, style: mergedStyle }, /*#__PURE__*/_react.default.createElement(_antd.Flex, { className: `${prefixCls}-container` }, /*#__PURE__*/_react.default.createElement(_antd.Splitter, null, /*#__PURE__*/_react.default.createElement(_antd.Splitter.Panel, { defaultSize: directoryTreeWith }, /*#__PURE__*/_react.default.createElement("div", { className: (0, _clsx.clsx)(`${prefixCls}-directory-tree`, classNames?.directoryTree), style: { ...contextConfig.styles?.directoryTree, ...styles?.directoryTree } }, /*#__PURE__*/_react.default.createElement(_DirectoryTree.default, { directoryIcons: directoryIcons, prefixCls: customizePrefixCls, treeData: treeData, selectedKeys: selectable && selectedFileState && validSelectedFile ? [selectedFileState.join('/')] : [], classNames: classNames, styles: styles, expandedKeys: expandedPathsState, onSelect: handleSelect, onExpand: handleExpand, defaultExpandAll: defaultExpandAll, directoryTitle: directoryTitle }))), /*#__PURE__*/_react.default.createElement(_antd.Splitter.Panel, null, /*#__PURE__*/_react.default.createElement(_FilePreview.default, { emptyRender: emptyRender, prefixCls: customizePrefixCls, classNames: classNames, styles: styles, selectedFile: validSelectedFile ? selectedFileState : [], fileContent: fileContent, loading: loadingContent, previewTitle: previewTitle, previewRender: previewRender, getFileNode: path => { if (!path || path.length === 0) return undefined; const { node } = findNodeAndValidate(path); return node ? { title: node.title, path: node.path, content: node.content } : undefined; } }))))); }); const Folder = ForwardFolder; if (process.env.NODE_ENV !== 'production') { Folder.displayName = 'Folder'; } var _default = exports.default = Folder;