dishui 0.0.64 → 0.0.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/components/Badge/index.js +12 -9
  2. package/dist/components/Badge/index.js.map +1 -1
  3. package/dist/components/Button/index.js +43 -40
  4. package/dist/components/Button/index.js.map +1 -1
  5. package/dist/components/Card/index.d.ts +11 -0
  6. package/dist/components/Card/index.js +62 -37
  7. package/dist/components/Card/index.js.map +1 -1
  8. package/dist/components/Chart/Chart.js.map +1 -1
  9. package/dist/components/Checkbox/index.js +32 -30
  10. package/dist/components/Checkbox/index.js.map +1 -1
  11. package/dist/components/Demo/index.js.map +1 -1
  12. package/dist/components/EmptyState/index.d.ts +4 -1
  13. package/dist/components/EmptyState/index.js +3 -3
  14. package/dist/components/EmptyState/index.js.map +1 -1
  15. package/dist/components/FileUploader/DropZone.js.map +1 -1
  16. package/dist/components/FileUploader/FileItem.js.map +1 -1
  17. package/dist/components/FileUploader/FileUploader.js.map +1 -1
  18. package/dist/components/FileUploader/UploadQueue.js.map +1 -1
  19. package/dist/components/Input/index.js +30 -30
  20. package/dist/components/Input/index.js.map +1 -1
  21. package/dist/components/Label/index.js +41 -40
  22. package/dist/components/Label/index.js.map +1 -1
  23. package/dist/components/List/index.js +47 -32
  24. package/dist/components/List/index.js.map +1 -1
  25. package/dist/components/Markdown/Markdown.js +61 -57
  26. package/dist/components/Markdown/Markdown.js.map +1 -1
  27. package/dist/components/Markdown/markdownComponents.js +49 -49
  28. package/dist/components/Markdown/markdownComponents.js.map +1 -1
  29. package/dist/components/Modal/index.d.ts +7 -3
  30. package/dist/components/Modal/index.js +77 -65
  31. package/dist/components/Modal/index.js.map +1 -1
  32. package/dist/components/Page/index.d.ts +58 -0
  33. package/dist/components/Page/index.js +74 -0
  34. package/dist/components/Page/index.js.map +1 -0
  35. package/dist/components/PageHeader/index.js.map +1 -1
  36. package/dist/components/Select/index.js +0 -3
  37. package/dist/components/Select/index.js.map +1 -1
  38. package/dist/components/SidebarToggleButton/index.js.map +1 -1
  39. package/dist/components/StatCard/index.d.ts +3 -1
  40. package/dist/components/StatCard/index.js +39 -11
  41. package/dist/components/StatCard/index.js.map +1 -1
  42. package/dist/components/Table/index.js +103 -85
  43. package/dist/components/Table/index.js.map +1 -1
  44. package/dist/components/Tabs/index.d.ts +19 -5
  45. package/dist/components/Tabs/index.js +32 -13
  46. package/dist/components/Tabs/index.js.map +1 -1
  47. package/dist/components/Tree/Tree.js.map +1 -1
  48. package/dist/components/pro/ArticleContextMenu/index.js.map +1 -1
  49. package/dist/components/pro/ArticleControl/ArticleControl2.js.map +1 -1
  50. package/dist/components/pro/CodeEditor/editors/CodeEditor.js +141 -134
  51. package/dist/components/pro/CodeEditor/editors/CodeEditor.js.map +1 -1
  52. package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js +81 -78
  53. package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
  54. package/dist/components/pro/CodeEditor/panels/FootnotePanel.js.map +1 -1
  55. package/dist/components/pro/CodeEditor/panels/HistoryPanel.js.map +1 -1
  56. package/dist/components/pro/CodeEditor/panels/OutlinePanel.js.map +1 -1
  57. package/dist/components/pro/CodeEditor/panels/TableOfContentsPanel.js.map +1 -1
  58. package/dist/components/pro/CodeEditor/panels/VariablePanel.js.map +1 -1
  59. package/dist/components/pro/CodeEditor/themes.js +59 -59
  60. package/dist/components/pro/CodeEditor/themes.js.map +1 -1
  61. package/dist/components/pro/DataTable/DataTable.js +49 -48
  62. package/dist/components/pro/DataTable/DataTable.js.map +1 -1
  63. package/dist/components/pro/DataTable/Filters.js +42 -42
  64. package/dist/components/pro/DataTable/Filters.js.map +1 -1
  65. package/dist/components/pro/DataTable/PivotPanel.js.map +1 -1
  66. package/dist/components/pro/DataTable/PivotView.js +35 -32
  67. package/dist/components/pro/DataTable/PivotView.js.map +1 -1
  68. package/dist/components/pro/DataTable/components/BulkActions.js.map +1 -1
  69. package/dist/components/pro/DataTable/components/ColumnSelector.js.map +1 -1
  70. package/dist/components/pro/DataTable/components/FilterBar.js.map +1 -1
  71. package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
  72. package/dist/components/pro/DataTable/components/TableHeader.js.map +1 -1
  73. package/dist/components/pro/DataTable/components/TablePagination.js.map +1 -1
  74. package/dist/components/pro/DataTable/components/TableToolbar.js.map +1 -1
  75. package/dist/components/pro/DataTable/pivotExtension.js.map +1 -1
  76. package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
  77. package/dist/components/pro/DataTable/tanstack-table.js +37 -37
  78. package/dist/components/pro/DataTable/tanstack-table.js.map +1 -1
  79. package/dist/components/pro/FileExplorer/Breadcrumb.js +53 -53
  80. package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
  81. package/dist/components/pro/FileExplorer/CreateDialog.js.map +1 -1
  82. package/dist/components/pro/FileExplorer/FileExplorer.js.map +1 -1
  83. package/dist/components/pro/FileExplorer/FileIcon.js +22 -22
  84. package/dist/components/pro/FileExplorer/FileIcon.js.map +1 -1
  85. package/dist/components/pro/FileExplorer/GridView.js.map +1 -1
  86. package/dist/components/pro/FileExplorer/ListView.js.map +1 -1
  87. package/dist/components/pro/FileExplorer/Toolbar.js.map +1 -1
  88. package/dist/components/pro/Flow/Flow.js +496 -494
  89. package/dist/components/pro/Flow/Flow.js.map +1 -1
  90. package/dist/components/pro/Flow/FlowCanvas.js +916 -917
  91. package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
  92. package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
  93. package/dist/components/pro/Flow/edges/EdgeRenderer.js +59 -59
  94. package/dist/components/pro/Flow/edges/EdgeRenderer.js.map +1 -1
  95. package/dist/components/pro/Flow/hooks/useClipboard.js.map +1 -1
  96. package/dist/components/pro/Flow/hooks/useDeleteSelection.js.map +1 -1
  97. package/dist/components/pro/Flow/hooks/useEdgeMutations.js.map +1 -1
  98. package/dist/components/pro/Flow/hooks/useLayoutActions.js +9 -11
  99. package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
  100. package/dist/components/pro/Flow/hooks/useLineMutations.js.map +1 -1
  101. package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
  102. package/dist/components/pro/Flow/hooks/useNodeMutations.js.map +1 -1
  103. package/dist/components/pro/Flow/lines/FreehandTool.js +9 -9
  104. package/dist/components/pro/Flow/lines/FreehandTool.js.map +1 -1
  105. package/dist/components/pro/Flow/lines/LineRenderer.js +5 -5
  106. package/dist/components/pro/Flow/lines/LineRenderer.js.map +1 -1
  107. package/dist/components/pro/Flow/nodes/DataNodeView.js +113 -93
  108. package/dist/components/pro/Flow/nodes/DataNodeView.js.map +1 -1
  109. package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
  110. package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
  111. package/dist/components/pro/Flow/nodes/Model3DView.js +1 -1
  112. package/dist/components/pro/Flow/nodes/Model3DView.js.map +1 -1
  113. package/dist/components/pro/Flow/nodes/NodeRenderer.js +118 -118
  114. package/dist/components/pro/Flow/nodes/NodeRenderer.js.map +1 -1
  115. package/dist/components/pro/Flow/nodes/dataUtils.js.map +1 -1
  116. package/dist/components/pro/Flow/nodes/shapes.js +1 -3
  117. package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
  118. package/dist/components/pro/Flow/panels/AIPanel.js.map +1 -1
  119. package/dist/components/pro/Flow/panels/Breadcrumb.js.map +1 -1
  120. package/dist/components/pro/Flow/panels/ContextMenu.js.map +1 -1
  121. package/dist/components/pro/Flow/panels/DataInspectorSection.js.map +1 -1
  122. package/dist/components/pro/Flow/panels/ExpandableTextarea.js.map +1 -1
  123. package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
  124. package/dist/components/pro/Flow/panels/FlowToolbar.js +97 -97
  125. package/dist/components/pro/Flow/panels/FlowToolbar.js.map +1 -1
  126. package/dist/components/pro/Flow/panels/InspectorPanel.js +57 -57
  127. package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
  128. package/dist/components/pro/Flow/panels/OutlinePanel.js +26 -22
  129. package/dist/components/pro/Flow/panels/OutlinePanel.js.map +1 -1
  130. package/dist/components/pro/Flow/panels/PresentationMode.js +18 -16
  131. package/dist/components/pro/Flow/panels/PresentationMode.js.map +1 -1
  132. package/dist/components/pro/Flow/panels/ProjectTitle.js.map +1 -1
  133. package/dist/components/pro/Flow/panels/ShapePalette.js +109 -104
  134. package/dist/components/pro/Flow/panels/ShapePalette.js.map +1 -1
  135. package/dist/components/pro/Flow/panels/toolbarIcons.js.map +1 -1
  136. package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
  137. package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
  138. package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
  139. package/dist/components/pro/RichTextEditor/CommentDialog.js.map +1 -1
  140. package/dist/components/pro/RichTextEditor/CommentPanel.js.map +1 -1
  141. package/dist/components/pro/RichTextEditor/CommentPopover.js.map +1 -1
  142. package/dist/components/pro/RichTextEditor/ContextMenu.js.map +1 -1
  143. package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
  144. package/dist/components/pro/RichTextEditor/SlashMenu.js.map +1 -1
  145. package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
  146. package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
  147. package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
  148. package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
  149. package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
  150. package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
  151. package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
  152. package/dist/components/pro/RichTextEditor/extensions/FloatingDialog.js.map +1 -1
  153. package/dist/components/pro/RichTextEditor/extensions/MarkdownBlock.js.map +1 -1
  154. package/dist/contexts/AppContext.js.map +1 -1
  155. package/dist/dishui.css +1 -1
  156. package/dist/hooks/useTableColumns.js +7 -8
  157. package/dist/hooks/useTableColumns.js.map +1 -1
  158. package/dist/index.d.ts +2 -0
  159. package/dist/index.js +227 -221
  160. package/dist/layouts/MainLayout.js +22 -22
  161. package/dist/layouts/MainLayout.js.map +1 -1
  162. package/dist/layouts/Navbar.js.map +1 -1
  163. package/dist/layouts/Sidebar.js +94 -73
  164. package/dist/layouts/Sidebar.js.map +1 -1
  165. package/dist/layouts/headbar/Headbar.js.map +1 -1
  166. package/dist/layouts/headbar/HeadbarLangSwitcher.js.map +1 -1
  167. package/dist/layouts/headbar/HeadbarSearch.js.map +1 -1
  168. package/dist/layouts/headbar/HeadbarThemeSwitcher.js +11 -11
  169. package/dist/layouts/headbar/HeadbarThemeSwitcher.js.map +1 -1
  170. package/dist/layouts/headbar/HeadbarTitle.js.map +1 -1
  171. package/dist/utils/i18n.js +10 -10
  172. package/dist/utils/i18n.js.map +1 -1
  173. package/dist/utils/menu.js.map +1 -1
  174. package/dist/utils/routerBuilder.js +17 -15
  175. package/dist/utils/routerBuilder.js.map +1 -1
  176. package/dist/utils/themes.js.map +1 -1
  177. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"Tree.js","names":[],"sources":["../../../src/components/Tree/Tree.tsx"],"sourcesContent":["import React, { useState, useCallback, useMemo } from 'react';\nimport { generateTreeActiveStyles } from '../../styles/themeStyles';\n\n// ============= 类型定义 =============\n\nexport interface TreeNode {\n id: string | number;\n label: string;\n children?: TreeNode[];\n icon?: React.ReactNode;\n disabled?: boolean;\n meta?: Record<string, any>;\n}\n\nexport interface TreeProps {\n /** 树形数据 */\n data: TreeNode[];\n\n /** 节点点击回调 */\n onNodeClick?: (node: TreeNode, path: (string | number)[]) => void;\n\n /** 节点双击回调 */\n onNodeDoubleClick?: (node: TreeNode, path: (string | number)[]) => void;\n\n /** 节点展开/折叠回调 */\n onNodeToggle?: (nodeId: string | number, expanded: boolean) => void;\n\n /** 当前激活的节点 ID */\n activeNodeId?: string | number;\n\n /** 默认展开的节点 ID 列表 */\n defaultExpandedIds?: (string | number)[];\n\n /** 是否显示搜索框 */\n showSearch?: boolean;\n\n /** 搜索占位符 */\n searchPlaceholder?: string;\n\n /** 是否显示展开/折叠全部按钮 */\n showExpandAll?: boolean;\n\n /** 展开全部按钮文本 */\n expandAllText?: string;\n\n /** 折叠全部按钮文本 */\n collapseAllText?: string;\n\n /** 是否显示节点图标 */\n showIcon?: boolean;\n\n /** 默认图标 */\n defaultIcon?: React.ReactNode;\n\n /** 展开图标 */\n expandIcon?: React.ReactNode;\n\n /** 折叠图标 */\n collapseIcon?: React.ReactNode;\n\n /** 是否显示连接线 */\n showLine?: boolean;\n\n /** 最大高度 */\n maxHeight?: string;\n\n /** 自定义类名 */\n className?: string;\n\n /** 标题 */\n title?: string;\n\n /** 标题图标 */\n titleIcon?: React.ReactNode;\n\n /** 是否显示节点数量 */\n showCount?: boolean;\n\n /** 空状态文本 */\n emptyText?: string;\n\n /** 搜索无结果文本 */\n noResultsText?: string;\n\n /** 缩进宽度(像素) */\n indent?: number;\n\n /** 展开按钮位置:left(左侧)或 right(右侧) */\n togglePosition?: 'left' | 'right';\n\n /** 是否可拖拽(暂不实现) */\n draggable?: boolean;\n\n /** 是否可选择(暂不实现) */\n checkable?: boolean;\n\n /** 自定义渲染节点 */\n renderNode?: (node: TreeNode, defaultRender: React.ReactNode) => React.ReactNode;\n}\n\n// ============= Tree 组件 =============\n\nconst Tree: React.FC<TreeProps> = ({\n data,\n onNodeClick,\n onNodeDoubleClick,\n onNodeToggle,\n activeNodeId,\n defaultExpandedIds = [],\n showSearch = true,\n searchPlaceholder = 'Search...',\n showExpandAll = true,\n expandAllText = 'Expand All',\n collapseAllText = 'Collapse All',\n showIcon = true,\n defaultIcon,\n expandIcon,\n collapseIcon,\n showLine = false,\n maxHeight = '600px',\n className = '',\n title,\n titleIcon,\n showCount = true,\n emptyText = 'No data',\n noResultsText = 'No results found',\n indent = 16,\n togglePosition = 'left',\n renderNode,\n}) => {\n const [searchQuery, setSearchQuery] = useState('');\n const [expandedIds, setExpandedIds] = useState<Set<string | number>>(\n new Set(defaultExpandedIds)\n );\n\n // 当 defaultExpandedIds 变化时,同步更新 expandedIds(用于异步加载的数据)\n React.useEffect(() => {\n if (defaultExpandedIds.length > 0) {\n setExpandedIds(prev => {\n const newSet = new Set(prev);\n defaultExpandedIds.forEach(id => newSet.add(id));\n return newSet;\n });\n }\n }, [defaultExpandedIds]);\n\n // 默认展开/折叠图标\n const defaultExpandIconEl = expandIcon || (\n <svg className=\"w-3 h-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n );\n\n const defaultCollapseIconEl = collapseIcon || (\n <svg className=\"w-3 h-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n );\n\n // 默认节点图标\n const defaultIconEl = defaultIcon || (\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n );\n\n // 统计节点总数\n const countNodes = useCallback((nodes: TreeNode[]): number => {\n return nodes.reduce((count, node) => {\n return count + 1 + (node.children ? countNodes(node.children) : 0);\n }, 0);\n }, []);\n\n const totalCount = useMemo(() => countNodes(data), [data, countNodes]);\n\n // 过滤节点(支持搜索)\n const filterNodes = useCallback((nodes: TreeNode[], query: string): TreeNode[] => {\n if (!query) return nodes;\n\n const lowerQuery = query.toLowerCase();\n const filtered: TreeNode[] = [];\n\n for (const node of nodes) {\n const matches = node.label.toLowerCase().includes(lowerQuery);\n const filteredChildren = node.children ? filterNodes(node.children, query) : [];\n\n if (matches || filteredChildren.length > 0) {\n filtered.push({\n ...node,\n children: filteredChildren.length > 0 ? filteredChildren : node.children,\n });\n }\n }\n\n return filtered;\n }, []);\n\n const filteredData = useMemo(\n () => filterNodes(data, searchQuery),\n [data, searchQuery, filterNodes]\n );\n\n // 切换节点展开/折叠\n const toggleNode = useCallback(\n (nodeId: string | number) => {\n const newExpanded = new Set(expandedIds);\n if (newExpanded.has(nodeId)) {\n newExpanded.delete(nodeId);\n onNodeToggle?.(nodeId, false);\n } else {\n newExpanded.add(nodeId);\n onNodeToggle?.(nodeId, true);\n }\n setExpandedIds(newExpanded);\n },\n [expandedIds, onNodeToggle]\n );\n\n // 展开所有节点\n const expandAll = useCallback(() => {\n const allIds = new Set<string | number>();\n const collectIds = (nodes: TreeNode[]) => {\n nodes.forEach((node) => {\n if (node.children && node.children.length > 0) {\n allIds.add(node.id);\n collectIds(node.children);\n }\n });\n };\n collectIds(data);\n setExpandedIds(allIds);\n }, [data]);\n\n // 折叠所有节点\n const collapseAll = useCallback(() => {\n setExpandedIds(new Set());\n }, []);\n\n // 渲染单个节点\n const renderTreeNode = useCallback(\n (node: TreeNode, depth: number = 0, path: (string | number)[] = []) => {\n const hasChildren = node.children && node.children.length > 0;\n const isExpanded = expandedIds.has(node.id);\n const isActive = activeNodeId === node.id;\n const currentPath = [...path, node.id];\n\n const nodeContent = (\n <div\n key={node.id}\n data-node-id={node.id}\n className={`tree-node ${isActive ? 'tree-node-active' : ''} ${node.disabled ? 'tree-node-disabled' : ''\n }`}\n style={{\n paddingLeft: `${depth * indent}px`,\n }}\n >\n <div\n className={`tree-node-content flex items-center gap-2 px-2 py-1.5 rounded cursor-pointer transition-colors ${isActive ? 'tree-node-content-active' : 'tree-node-content-inactive'\n } ${node.disabled ? 'opacity-50 cursor-not-allowed' : ''}`}\n onClick={() => {\n if (!node.disabled) {\n onNodeClick?.(node, currentPath);\n }\n }}\n onDoubleClick={() => {\n if (!node.disabled) {\n onNodeDoubleClick?.(node, currentPath);\n }\n }}\n >\n {/* 左侧展开按钮 */}\n {togglePosition === 'left' && (\n <>\n {hasChildren ? (\n <button\n className={`tree-node-toggle btn btn-xs btn-ghost btn-circle ${isExpanded ? 'tree-node-toggle-expanded' : ''\n }`}\n onClick={(e) => {\n e.stopPropagation();\n if (!node.disabled) {\n toggleNode(node.id);\n }\n }}\n disabled={node.disabled}\n >\n {isExpanded ? defaultExpandIconEl : defaultCollapseIconEl}\n </button>\n ) : (\n <span className=\"tree-node-toggle-placeholder\" style={{ width: '1.5rem', height: '1.5rem', flexShrink: 0 }}></span>\n )}\n </>\n )}\n\n {/* 节点图标 */}\n {showIcon && (\n <span className=\"tree-node-icon\">{node.icon || defaultIconEl}</span>\n )}\n\n {/* 节点标签 */}\n <span className=\"tree-node-label text-sm truncate flex-1\" title={node.label}>\n {node.label}\n </span>\n\n {/* 子节点数量 */}\n {hasChildren && showCount && (\n <span className=\"tree-node-count text-xs opacity-60\">\n ({node.children!.length})\n </span>\n )}\n\n {/* 右侧展开按钮 */}\n {togglePosition === 'right' && hasChildren && (\n <button\n className={`tree-node-toggle tree-node-toggle-right btn btn-xs btn-ghost ${isExpanded ? 'tree-node-toggle-expanded' : ''\n }`}\n onClick={(e) => {\n e.stopPropagation();\n if (!node.disabled) {\n toggleNode(node.id);\n }\n }}\n disabled={node.disabled}\n >\n {isExpanded ? defaultExpandIconEl : defaultCollapseIconEl}\n </button>\n )}\n </div>\n\n {/* 子节点 */}\n {hasChildren && (\n <div\n className={`tree-node-children ${isExpanded ? 'tree-node-children-expanded' : 'tree-node-children-collapsed'}`}\n >\n {node.children!.map((child) => renderTreeNode(child, depth + 1, currentPath))}\n </div>\n )}\n </div>\n );\n\n // 自定义渲染\n if (renderNode) {\n return renderNode(node, nodeContent);\n }\n\n return nodeContent;\n },\n [\n expandedIds,\n activeNodeId,\n indent,\n showIcon,\n showCount,\n togglePosition,\n defaultExpandIconEl,\n defaultCollapseIconEl,\n defaultIconEl,\n onNodeClick,\n onNodeDoubleClick,\n toggleNode,\n renderNode,\n ]\n );\n\n return (\n <div className={`tree-component bg-base-100 border border-base-300 rounded-lg p-2 ${className}`}>\n {/* 标题 */}\n {title && (\n <div className=\"tree-header flex items-center gap-2 px-2 py-1 mb-2 border-b border-base-300\">\n {titleIcon}\n <span className=\"font-semibold\">{title}</span>\n {showCount && (\n <span className=\"text-xs opacity-60 ml-auto\">({totalCount})</span>\n )}\n </div>\n )}\n\n {/* 搜索框 */}\n {showSearch && (\n <input\n type=\"text\"\n placeholder={searchPlaceholder}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"tree-search input input-sm input-bordered w-full mb-2\"\n />\n )}\n\n {/* 操作按钮 */}\n {showExpandAll && (\n <div className=\"tree-actions flex gap-1 mb-2\">\n <button className=\"btn btn-xs btn-ghost\" onClick={expandAll}>\n {expandAllText}\n </button>\n <button className=\"btn btn-xs btn-ghost\" onClick={collapseAll}>\n {collapseAllText}\n </button>\n </div>\n )}\n\n {/* 树形列表 */}\n <div\n className=\"tree-content overflow-y-auto\"\n style={{ maxHeight }}\n >\n {filteredData.length === 0 ? (\n <div className=\"tree-empty text-center text-sm opacity-60 py-4\">\n {searchQuery ? noResultsText : emptyText}\n </div>\n ) : (\n <div className={`tree-nodes ${showLine ? 'tree-show-line' : ''}`}>\n {filteredData.map((node) => renderTreeNode(node))}\n </div>\n )}\n </div>\n\n <style>{`\n .tree-node {\n user-select: none;\n }\n \n /* 子节点容器基础样式 */\n .tree-node-children {\n margin-top: 2px;\n overflow: hidden;\n transform-origin: top;\n box-sizing: border-box;\n }\n \n /* 折叠状态 - 初始隐藏 */\n .tree-node-children-collapsed {\n max-height: 0;\n opacity: 0;\n transform: scaleY(0.8);\n border-left: none !important;\n }\n \n /* 展开状态 - 显示内容 */\n .tree-node-children-expanded {\n max-height: 5000px;\n opacity: 1;\n transform: scaleY(1);\n transition: \n max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),\n transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n /* 不对 border 做动画 */\n box-sizing: border-box;\n }\n \n /* 连接线样式 - 在展开状态显示,使用多重选择器提高优先级 */\n .tree-component .tree-show-line .tree-node-children-expanded,\n .tree-show-line .tree-node-children.tree-node-children-expanded {\n border-left: 1px solid var(--fallback-bc, oklch(var(--bc) / 0.2)) !important;\n margin-left: 8px !important;\n padding-left: 8px !important;\n position: relative;\n }\n \n /* 左侧连接线圆点装饰 */\n .tree-show-line .tree-node-children-expanded::before {\n content: '';\n position: absolute;\n left: -3px;\n top: 0;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background-color: var(--fallback-bc, oklch(var(--bc) / 0.3));\n }\n \n /* 折叠过渡 */\n .tree-node-children-collapsed {\n transition: \n max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),\n transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n /* 展开/折叠按钮图标旋转动画 */\n .tree-node-toggle {\n z-index: 2;\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n .tree-node-toggle svg {\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n /* 折叠状态的图标 - 向右 */\n .tree-node-toggle:not(.tree-node-toggle-expanded) svg {\n transform: rotate(0deg);\n }\n \n /* 展开状态的图标 - 向下(已经是向下的,所以不需要额外旋转) */\n .tree-node-toggle-expanded svg {\n transform: rotate(0deg);\n }\n \n .tree-component {\n position: relative;\n z-index: 1;\n }\n \n .tree-node-disabled {\n cursor: not-allowed;\n }\n \n .tree-node-disabled .tree-node-content {\n pointer-events: none;\n }\n \n /* 节点内容悬停效果 */\n .tree-node-content {\n transition: background-color 0.15s ease;\n }\n \n /* 优化性能 - 告诉浏览器这些属性会变化 */\n .tree-node-children {\n will-change: max-height, opacity, transform;\n }\n \n /* 动画完成后移除 will-change 以节省资源 */\n .tree-node-children-expanded,\n .tree-node-children-collapsed {\n animation: remove-will-change 0.3s forwards;\n }\n \n @keyframes remove-will-change {\n to {\n will-change: auto;\n }\n }\n \n /* 活跃节点基础样式 */\n .tree-node-content-active {\n font-weight: 600 !important;\n padding-left: calc(0.5rem - 4px) !important;\n border-radius: 0.375rem !important;\n }\n \n /* 主题活跃状态样式 - 从共用配置生成 */\n ${generateTreeActiveStyles()}\n \n /* 非活跃节点的悬停状态 */\n .tree-node-content-inactive:hover {\n background-color: oklch(var(--b2));\n }\n `}</style>\n </div>\n );\n};\n\nexport default Tree;\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tree.js","names":[],"sources":["../../../src/components/Tree/Tree.tsx"],"sourcesContent":["import React, { useState, useCallback, useMemo } from 'react';\nimport { generateTreeActiveStyles } from '../../styles/themeStyles';\n\n// ============= 类型定义 =============\n\nexport interface TreeNode {\n id: string | number;\n label: string;\n children?: TreeNode[];\n icon?: React.ReactNode;\n disabled?: boolean;\n meta?: Record<string, any>;\n}\n\nexport interface TreeProps {\n /** 树形数据 */\n data: TreeNode[];\n\n /** 节点点击回调 */\n onNodeClick?: (node: TreeNode, path: (string | number)[]) => void;\n\n /** 节点双击回调 */\n onNodeDoubleClick?: (node: TreeNode, path: (string | number)[]) => void;\n\n /** 节点展开/折叠回调 */\n onNodeToggle?: (nodeId: string | number, expanded: boolean) => void;\n\n /** 当前激活的节点 ID */\n activeNodeId?: string | number;\n\n /** 默认展开的节点 ID 列表 */\n defaultExpandedIds?: (string | number)[];\n\n /** 是否显示搜索框 */\n showSearch?: boolean;\n\n /** 搜索占位符 */\n searchPlaceholder?: string;\n\n /** 是否显示展开/折叠全部按钮 */\n showExpandAll?: boolean;\n\n /** 展开全部按钮文本 */\n expandAllText?: string;\n\n /** 折叠全部按钮文本 */\n collapseAllText?: string;\n\n /** 是否显示节点图标 */\n showIcon?: boolean;\n\n /** 默认图标 */\n defaultIcon?: React.ReactNode;\n\n /** 展开图标 */\n expandIcon?: React.ReactNode;\n\n /** 折叠图标 */\n collapseIcon?: React.ReactNode;\n\n /** 是否显示连接线 */\n showLine?: boolean;\n\n /** 最大高度 */\n maxHeight?: string;\n\n /** 自定义类名 */\n className?: string;\n\n /** 标题 */\n title?: string;\n\n /** 标题图标 */\n titleIcon?: React.ReactNode;\n\n /** 是否显示节点数量 */\n showCount?: boolean;\n\n /** 空状态文本 */\n emptyText?: string;\n\n /** 搜索无结果文本 */\n noResultsText?: string;\n\n /** 缩进宽度(像素) */\n indent?: number;\n\n /** 展开按钮位置:left(左侧)或 right(右侧) */\n togglePosition?: 'left' | 'right';\n\n /** 是否可拖拽(暂不实现) */\n draggable?: boolean;\n\n /** 是否可选择(暂不实现) */\n checkable?: boolean;\n\n /** 自定义渲染节点 */\n renderNode?: (node: TreeNode, defaultRender: React.ReactNode) => React.ReactNode;\n}\n\n// ============= Tree 组件 =============\n\nconst Tree: React.FC<TreeProps> = ({\n data,\n onNodeClick,\n onNodeDoubleClick,\n onNodeToggle,\n activeNodeId,\n defaultExpandedIds = [],\n showSearch = true,\n searchPlaceholder = 'Search...',\n showExpandAll = true,\n expandAllText = 'Expand All',\n collapseAllText = 'Collapse All',\n showIcon = true,\n defaultIcon,\n expandIcon,\n collapseIcon,\n showLine = false,\n maxHeight = '600px',\n className = '',\n title,\n titleIcon,\n showCount = true,\n emptyText = 'No data',\n noResultsText = 'No results found',\n indent = 16,\n togglePosition = 'left',\n renderNode,\n}) => {\n const [searchQuery, setSearchQuery] = useState('');\n const [expandedIds, setExpandedIds] = useState<Set<string | number>>(\n new Set(defaultExpandedIds)\n );\n\n // 当 defaultExpandedIds 变化时,同步更新 expandedIds(用于异步加载的数据)\n React.useEffect(() => {\n if (defaultExpandedIds.length > 0) {\n setExpandedIds(prev => {\n const newSet = new Set(prev);\n defaultExpandedIds.forEach(id => newSet.add(id));\n return newSet;\n });\n }\n }, [defaultExpandedIds]);\n\n // 默认展开/折叠图标\n const defaultExpandIconEl = expandIcon || (\n <svg className=\"w-3 h-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n );\n\n const defaultCollapseIconEl = collapseIcon || (\n <svg className=\"w-3 h-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n );\n\n // 默认节点图标\n const defaultIconEl = defaultIcon || (\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n );\n\n // 统计节点总数\n const countNodes = useCallback((nodes: TreeNode[]): number => {\n return nodes.reduce((count, node) => {\n return count + 1 + (node.children ? countNodes(node.children) : 0);\n }, 0);\n }, []);\n\n const totalCount = useMemo(() => countNodes(data), [data, countNodes]);\n\n // 过滤节点(支持搜索)\n const filterNodes = useCallback((nodes: TreeNode[], query: string): TreeNode[] => {\n if (!query) return nodes;\n\n const lowerQuery = query.toLowerCase();\n const filtered: TreeNode[] = [];\n\n for (const node of nodes) {\n const matches = node.label.toLowerCase().includes(lowerQuery);\n const filteredChildren = node.children ? filterNodes(node.children, query) : [];\n\n if (matches || filteredChildren.length > 0) {\n filtered.push({\n ...node,\n children: filteredChildren.length > 0 ? filteredChildren : node.children,\n });\n }\n }\n\n return filtered;\n }, []);\n\n const filteredData = useMemo(\n () => filterNodes(data, searchQuery),\n [data, searchQuery, filterNodes]\n );\n\n // 切换节点展开/折叠\n const toggleNode = useCallback(\n (nodeId: string | number) => {\n const newExpanded = new Set(expandedIds);\n if (newExpanded.has(nodeId)) {\n newExpanded.delete(nodeId);\n onNodeToggle?.(nodeId, false);\n } else {\n newExpanded.add(nodeId);\n onNodeToggle?.(nodeId, true);\n }\n setExpandedIds(newExpanded);\n },\n [expandedIds, onNodeToggle]\n );\n\n // 展开所有节点\n const expandAll = useCallback(() => {\n const allIds = new Set<string | number>();\n const collectIds = (nodes: TreeNode[]) => {\n nodes.forEach((node) => {\n if (node.children && node.children.length > 0) {\n allIds.add(node.id);\n collectIds(node.children);\n }\n });\n };\n collectIds(data);\n setExpandedIds(allIds);\n }, [data]);\n\n // 折叠所有节点\n const collapseAll = useCallback(() => {\n setExpandedIds(new Set());\n }, []);\n\n // 渲染单个节点\n const renderTreeNode = useCallback(\n (node: TreeNode, depth: number = 0, path: (string | number)[] = []) => {\n const hasChildren = node.children && node.children.length > 0;\n const isExpanded = expandedIds.has(node.id);\n const isActive = activeNodeId === node.id;\n const currentPath = [...path, node.id];\n\n const nodeContent = (\n <div\n key={node.id}\n data-node-id={node.id}\n className={`tree-node ${isActive ? 'tree-node-active' : ''} ${node.disabled ? 'tree-node-disabled' : ''\n }`}\n style={{\n paddingLeft: `${depth * indent}px`,\n }}\n >\n <div\n className={`tree-node-content flex items-center gap-2 px-2 py-1.5 rounded cursor-pointer transition-colors ${isActive ? 'tree-node-content-active' : 'tree-node-content-inactive'\n } ${node.disabled ? 'opacity-50 cursor-not-allowed' : ''}`}\n onClick={() => {\n if (!node.disabled) {\n onNodeClick?.(node, currentPath);\n }\n }}\n onDoubleClick={() => {\n if (!node.disabled) {\n onNodeDoubleClick?.(node, currentPath);\n }\n }}\n >\n {/* 左侧展开按钮 */}\n {togglePosition === 'left' && (\n <>\n {hasChildren ? (\n <button\n className={`tree-node-toggle btn btn-xs btn-ghost btn-circle ${isExpanded ? 'tree-node-toggle-expanded' : ''\n }`}\n onClick={(e) => {\n e.stopPropagation();\n if (!node.disabled) {\n toggleNode(node.id);\n }\n }}\n disabled={node.disabled}\n >\n {isExpanded ? defaultExpandIconEl : defaultCollapseIconEl}\n </button>\n ) : (\n <span className=\"tree-node-toggle-placeholder\" style={{ width: '1.5rem', height: '1.5rem', flexShrink: 0 }}></span>\n )}\n </>\n )}\n\n {/* 节点图标 */}\n {showIcon && (\n <span className=\"tree-node-icon\">{node.icon || defaultIconEl}</span>\n )}\n\n {/* 节点标签 */}\n <span className=\"tree-node-label text-sm truncate flex-1\" title={node.label}>\n {node.label}\n </span>\n\n {/* 子节点数量 */}\n {hasChildren && showCount && (\n <span className=\"tree-node-count text-xs opacity-60\">\n ({node.children!.length})\n </span>\n )}\n\n {/* 右侧展开按钮 */}\n {togglePosition === 'right' && hasChildren && (\n <button\n className={`tree-node-toggle tree-node-toggle-right btn btn-xs btn-ghost ${isExpanded ? 'tree-node-toggle-expanded' : ''\n }`}\n onClick={(e) => {\n e.stopPropagation();\n if (!node.disabled) {\n toggleNode(node.id);\n }\n }}\n disabled={node.disabled}\n >\n {isExpanded ? defaultExpandIconEl : defaultCollapseIconEl}\n </button>\n )}\n </div>\n\n {/* 子节点 */}\n {hasChildren && (\n <div\n className={`tree-node-children ${isExpanded ? 'tree-node-children-expanded' : 'tree-node-children-collapsed'}`}\n >\n {node.children!.map((child) => renderTreeNode(child, depth + 1, currentPath))}\n </div>\n )}\n </div>\n );\n\n // 自定义渲染\n if (renderNode) {\n return renderNode(node, nodeContent);\n }\n\n return nodeContent;\n },\n [\n expandedIds,\n activeNodeId,\n indent,\n showIcon,\n showCount,\n togglePosition,\n defaultExpandIconEl,\n defaultCollapseIconEl,\n defaultIconEl,\n onNodeClick,\n onNodeDoubleClick,\n toggleNode,\n renderNode,\n ]\n );\n\n return (\n <div className={`tree-component bg-base-100 border border-base-300 rounded-lg p-2 ${className}`}>\n {/* 标题 */}\n {title && (\n <div className=\"tree-header flex items-center gap-2 px-2 py-1 mb-2 border-b border-base-300\">\n {titleIcon}\n <span className=\"font-semibold\">{title}</span>\n {showCount && (\n <span className=\"text-xs opacity-60 ml-auto\">({totalCount})</span>\n )}\n </div>\n )}\n\n {/* 搜索框 */}\n {showSearch && (\n <input\n type=\"text\"\n placeholder={searchPlaceholder}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"tree-search input input-sm input-bordered w-full mb-2\"\n />\n )}\n\n {/* 操作按钮 */}\n {showExpandAll && (\n <div className=\"tree-actions flex gap-1 mb-2\">\n <button className=\"btn btn-xs btn-ghost\" onClick={expandAll}>\n {expandAllText}\n </button>\n <button className=\"btn btn-xs btn-ghost\" onClick={collapseAll}>\n {collapseAllText}\n </button>\n </div>\n )}\n\n {/* 树形列表 */}\n <div\n className=\"tree-content overflow-y-auto\"\n style={{ maxHeight }}\n >\n {filteredData.length === 0 ? (\n <div className=\"tree-empty text-center text-sm opacity-60 py-4\">\n {searchQuery ? noResultsText : emptyText}\n </div>\n ) : (\n <div className={`tree-nodes ${showLine ? 'tree-show-line' : ''}`}>\n {filteredData.map((node) => renderTreeNode(node))}\n </div>\n )}\n </div>\n\n <style>{`\n .tree-node {\n user-select: none;\n }\n \n /* 子节点容器基础样式 */\n .tree-node-children {\n margin-top: 2px;\n overflow: hidden;\n transform-origin: top;\n box-sizing: border-box;\n }\n \n /* 折叠状态 - 初始隐藏 */\n .tree-node-children-collapsed {\n max-height: 0;\n opacity: 0;\n transform: scaleY(0.8);\n border-left: none !important;\n }\n \n /* 展开状态 - 显示内容 */\n .tree-node-children-expanded {\n max-height: 5000px;\n opacity: 1;\n transform: scaleY(1);\n transition: \n max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),\n transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n /* 不对 border 做动画 */\n box-sizing: border-box;\n }\n \n /* 连接线样式 - 在展开状态显示,使用多重选择器提高优先级 */\n .tree-component .tree-show-line .tree-node-children-expanded,\n .tree-show-line .tree-node-children.tree-node-children-expanded {\n border-left: 1px solid var(--fallback-bc, oklch(var(--bc) / 0.2)) !important;\n margin-left: 8px !important;\n padding-left: 8px !important;\n position: relative;\n }\n \n /* 左侧连接线圆点装饰 */\n .tree-show-line .tree-node-children-expanded::before {\n content: '';\n position: absolute;\n left: -3px;\n top: 0;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background-color: var(--fallback-bc, oklch(var(--bc) / 0.3));\n }\n \n /* 折叠过渡 */\n .tree-node-children-collapsed {\n transition: \n max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),\n transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n /* 展开/折叠按钮图标旋转动画 */\n .tree-node-toggle {\n z-index: 2;\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n .tree-node-toggle svg {\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n /* 折叠状态的图标 - 向右 */\n .tree-node-toggle:not(.tree-node-toggle-expanded) svg {\n transform: rotate(0deg);\n }\n \n /* 展开状态的图标 - 向下(已经是向下的,所以不需要额外旋转) */\n .tree-node-toggle-expanded svg {\n transform: rotate(0deg);\n }\n \n .tree-component {\n position: relative;\n z-index: 1;\n }\n \n .tree-node-disabled {\n cursor: not-allowed;\n }\n \n .tree-node-disabled .tree-node-content {\n pointer-events: none;\n }\n \n /* 节点内容悬停效果 */\n .tree-node-content {\n transition: background-color 0.15s ease;\n }\n \n /* 优化性能 - 告诉浏览器这些属性会变化 */\n .tree-node-children {\n will-change: max-height, opacity, transform;\n }\n \n /* 动画完成后移除 will-change 以节省资源 */\n .tree-node-children-expanded,\n .tree-node-children-collapsed {\n animation: remove-will-change 0.3s forwards;\n }\n \n @keyframes remove-will-change {\n to {\n will-change: auto;\n }\n }\n \n /* 活跃节点基础样式 */\n .tree-node-content-active {\n font-weight: 600 !important;\n padding-left: calc(0.5rem - 4px) !important;\n border-radius: 0.375rem !important;\n }\n \n /* 主题活跃状态样式 - 从共用配置生成 */\n ${generateTreeActiveStyles()}\n \n /* 非活跃节点的悬停状态 */\n .tree-node-content-inactive:hover {\n background-color: oklch(var(--b2));\n }\n `}</style>\n </div>\n );\n};\n\nexport default Tree;\n\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/pro/ArticleContextMenu/index.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { useClickOutside } from '../../../hooks/useClickOutside';\nimport { useContainerElement } from '../../../hooks/useContainerElement';\nimport { SelectionInfo, getSelectionInfo, getMarkdownContent } from '../../../utils/selection';\n\n// 菜单项配置\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon?: React.ReactNode;\n description?: string;\n action: (content: string, selection?: SelectionInfo) => void;\n disabled?: boolean;\n}\n\nexport type { SelectionInfo } from '../../../utils/selection';\n\nexport interface ArticleContextMenuProps {\n /**\n * 绑定的容器 ID 或 ref\n */\n targetId?: string;\n targetRef?: React.RefObject<HTMLElement | HTMLDivElement | null>;\n\n /**\n * 菜单项列表\n */\n menuItems: ContextMenuItem[];\n\n /**\n * 自定义样式类名\n */\n className?: string;\n\n /**\n * 是否启用\n * @default true\n */\n enabled?: boolean;\n}\n\nconst ArticleContextMenu: React.FC<ArticleContextMenuProps> = ({\n targetId,\n targetRef,\n menuItems,\n className = '',\n enabled = true,\n}) => {\n const [visible, setVisible] = useState(false);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [hasSelection, setHasSelection] = useState(false);\n const menuRef = useRef<HTMLDivElement>(null);\n const container = useContainerElement(targetId, targetRef);\n\n // 点击外部关闭菜单\n useClickOutside(menuRef, useCallback(() => setVisible(false), []), visible);\n\n\n\n // 处理右键点击\n const handleContextMenu = useCallback((e: MouseEvent) => {\n if (!enabled || !container) return;\n\n // 检查点击位置是否在容器内\n let target = e.target as Node | null;\n let isInContainer = false;\n while (target) {\n if (target === container) {\n isInContainer = true;\n break;\n }\n target = target.parentNode;\n }\n\n if (!isInContainer) return;\n\n e.preventDefault();\n\n // 检查是否有选中文本\n const selection = window.getSelection();\n const hasText = selection && selection.toString().trim() !== '';\n setHasSelection(!!hasText);\n\n // 计算菜单位置(显示在鼠标上方)\n const menuHeight = 200; // 预估菜单高度\n const menuWidth = 200; // 预估菜单宽度\n\n let x = e.clientX;\n let y = e.clientY - menuHeight - 10; // 显示在鼠标上方\n\n // 边界检查\n if (y < 0) {\n y = e.clientY + 10; // 如果上方空间不够,显示在下方\n }\n if (x + menuWidth > window.innerWidth) {\n x = window.innerWidth - menuWidth - 10;\n }\n\n setPosition({ x, y });\n setVisible(true);\n }, [enabled, container]);\n\n // 处理菜单项点击\n const handleMenuItemClick = useCallback((item: ContextMenuItem) => {\n if (item.disabled) return;\n\n const content = getMarkdownContent(container);\n const selection = getSelectionInfo(container);\n\n item.action(content, selection);\n setVisible(false);\n }, [container]);\n\n // 滚动时关闭菜单\n useEffect(() => {\n if (!visible) return;\n\n const handleScroll = () => setVisible(false);\n document.addEventListener('scroll', handleScroll, true);\n\n return () => {\n document.removeEventListener('scroll', handleScroll, true);\n };\n }, [visible]);\n\n // 监听右键事件\n useEffect(() => {\n document.addEventListener('contextmenu', handleContextMenu);\n\n return () => {\n document.removeEventListener('contextmenu', handleContextMenu);\n };\n }, [handleContextMenu]);\n\n // ESC 键关闭菜单\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setVisible(false);\n }\n };\n\n if (visible) {\n document.addEventListener('keydown', handleKeyDown);\n }\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [visible]);\n\n if (!visible) return null;\n\n return (\n <div\n ref={menuRef}\n className={`fixed z-50 bg-base-100 rounded-lg shadow-2xl border border-base-300 py-2 min-w-[200px] ${className}`}\n style={{\n left: `${position.x}px`,\n top: `${position.y}px`,\n }}\n >\n {menuItems.map((item) => {\n const isDisabled = item.disabled || (!hasSelection && item.id !== 'copy' && item.id !== 'stats');\n\n return (\n <button\n key={item.id}\n onClick={() => handleMenuItemClick(item)}\n disabled={isDisabled}\n className={`w-full px-4 py-2 text-left flex items-center gap-3 hover:bg-base-200 transition-colors ${isDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'\n }`}\n title={item.description}\n >\n {item.icon && (\n <span className=\"flex-shrink-0 w-5 h-5 flex items-center justify-center\">\n {item.icon}\n </span>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium\">{item.label}</div>\n {item.description && (\n <div className=\"text-xs text-base-content/60 truncate\">{item.description}</div>\n )}\n </div>\n </button>\n );\n })}\n\n {menuItems.length === 0 && (\n <div className=\"px-4 py-2 text-sm text-base-content/60 text-center\">\n 暂无可用操作\n </div>\n )}\n </div>\n );\n};\n\nexport default ArticleContextMenu;\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/pro/ArticleContextMenu/index.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { useClickOutside } from '../../../hooks/useClickOutside';\nimport { useContainerElement } from '../../../hooks/useContainerElement';\nimport { SelectionInfo, getSelectionInfo, getMarkdownContent } from '../../../utils/selection';\n\n// 菜单项配置\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon?: React.ReactNode;\n description?: string;\n action: (content: string, selection?: SelectionInfo) => void;\n disabled?: boolean;\n}\n\nexport type { SelectionInfo } from '../../../utils/selection';\n\nexport interface ArticleContextMenuProps {\n /**\n * 绑定的容器 ID 或 ref\n */\n targetId?: string;\n targetRef?: React.RefObject<HTMLElement | HTMLDivElement | null>;\n\n /**\n * 菜单项列表\n */\n menuItems: ContextMenuItem[];\n\n /**\n * 自定义样式类名\n */\n className?: string;\n\n /**\n * 是否启用\n * @default true\n */\n enabled?: boolean;\n}\n\nconst ArticleContextMenu: React.FC<ArticleContextMenuProps> = ({\n targetId,\n targetRef,\n menuItems,\n className = '',\n enabled = true,\n}) => {\n const [visible, setVisible] = useState(false);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [hasSelection, setHasSelection] = useState(false);\n const menuRef = useRef<HTMLDivElement>(null);\n const container = useContainerElement(targetId, targetRef);\n\n // 点击外部关闭菜单\n useClickOutside(menuRef, useCallback(() => setVisible(false), []), visible);\n\n\n\n // 处理右键点击\n const handleContextMenu = useCallback((e: MouseEvent) => {\n if (!enabled || !container) return;\n\n // 检查点击位置是否在容器内\n let target = e.target as Node | null;\n let isInContainer = false;\n while (target) {\n if (target === container) {\n isInContainer = true;\n break;\n }\n target = target.parentNode;\n }\n\n if (!isInContainer) return;\n\n e.preventDefault();\n\n // 检查是否有选中文本\n const selection = window.getSelection();\n const hasText = selection && selection.toString().trim() !== '';\n setHasSelection(!!hasText);\n\n // 计算菜单位置(显示在鼠标上方)\n const menuHeight = 200; // 预估菜单高度\n const menuWidth = 200; // 预估菜单宽度\n\n let x = e.clientX;\n let y = e.clientY - menuHeight - 10; // 显示在鼠标上方\n\n // 边界检查\n if (y < 0) {\n y = e.clientY + 10; // 如果上方空间不够,显示在下方\n }\n if (x + menuWidth > window.innerWidth) {\n x = window.innerWidth - menuWidth - 10;\n }\n\n setPosition({ x, y });\n setVisible(true);\n }, [enabled, container]);\n\n // 处理菜单项点击\n const handleMenuItemClick = useCallback((item: ContextMenuItem) => {\n if (item.disabled) return;\n\n const content = getMarkdownContent(container);\n const selection = getSelectionInfo(container);\n\n item.action(content, selection);\n setVisible(false);\n }, [container]);\n\n // 滚动时关闭菜单\n useEffect(() => {\n if (!visible) return;\n\n const handleScroll = () => setVisible(false);\n document.addEventListener('scroll', handleScroll, true);\n\n return () => {\n document.removeEventListener('scroll', handleScroll, true);\n };\n }, [visible]);\n\n // 监听右键事件\n useEffect(() => {\n document.addEventListener('contextmenu', handleContextMenu);\n\n return () => {\n document.removeEventListener('contextmenu', handleContextMenu);\n };\n }, [handleContextMenu]);\n\n // ESC 键关闭菜单\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setVisible(false);\n }\n };\n\n if (visible) {\n document.addEventListener('keydown', handleKeyDown);\n }\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [visible]);\n\n if (!visible) return null;\n\n return (\n <div\n ref={menuRef}\n className={`fixed z-50 bg-base-100 rounded-lg shadow-2xl border border-base-300 py-2 min-w-[200px] ${className}`}\n style={{\n left: `${position.x}px`,\n top: `${position.y}px`,\n }}\n >\n {menuItems.map((item) => {\n const isDisabled = item.disabled || (!hasSelection && item.id !== 'copy' && item.id !== 'stats');\n\n return (\n <button\n key={item.id}\n onClick={() => handleMenuItemClick(item)}\n disabled={isDisabled}\n className={`w-full px-4 py-2 text-left flex items-center gap-3 hover:bg-base-200 transition-colors ${isDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'\n }`}\n title={item.description}\n >\n {item.icon && (\n <span className=\"flex-shrink-0 w-5 h-5 flex items-center justify-center\">\n {item.icon}\n </span>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium\">{item.label}</div>\n {item.description && (\n <div className=\"text-xs text-base-content/60 truncate\">{item.description}</div>\n )}\n </div>\n </button>\n );\n })}\n\n {menuItems.length === 0 && (\n <div className=\"px-4 py-2 text-sm text-base-content/60 text-center\">\n 暂无可用操作\n </div>\n )}\n </div>\n );\n};\n\nexport default ArticleContextMenu;\n\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ArticleControl2.js","names":[],"sources":["../../../../src/components/pro/ArticleControl/ArticleControl.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { Tree } from '../../Tree';\nimport type { TreeNode } from '../../Tree';\nimport { useDraggable } from '../../../hooks/useDraggable';\nimport { useContainerElement } from '../../../hooks/useContainerElement';\nimport { getSelectionInfo, getMarkdownContent } from '../../../utils/selection';\nimport type { ArticleControlProps, ArticleAction, HeadingItem } from './types';\nimport { useArticleToc } from './useArticleToc';\nimport { buildHeadingTree, getAllNodeIds } from './buildHeadingTree';\nimport './ArticleControl.css';\n\nconst ArticleControl: React.FC<ArticleControlProps> = ({\n targetId,\n targetRef,\n initialPosition = { x: window.innerWidth - 320, y: 100 },\n initialExpanded = true,\n ballSize = 56,\n panelWidth = 280,\n panelMaxHeight = 400,\n className = '',\n onHeadingClick,\n actions = [],\n}) => {\n const [isExpanded, setIsExpanded] = useState(initialExpanded);\n const controlRef = useRef<HTMLDivElement>(null);\n const tocContainerRef = useRef<HTMLDivElement>(null);\n const [hoveredAction, setHoveredAction] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n\n const { position, isDragging, handleMouseDown, handleTouchStart } = useDraggable({\n initialPosition,\n boundarySize: {\n width: isExpanded ? panelWidth : ballSize,\n height: isExpanded ? 60 : ballSize,\n },\n });\n\n const container = useContainerElement(targetId, targetRef);\n const { headings, activeId, prevActiveId, scrollToHeading } = useArticleToc(container);\n\n useEffect(() => {\n if (!activeId || !tocContainerRef.current || !isExpanded) return;\n requestAnimationFrame(() => {\n const activeElement = tocContainerRef.current?.querySelector(`[data-node-id=\"${activeId}\"]`);\n if (activeElement) {\n const cont = tocContainerRef.current;\n if (!cont) return;\n const elementRect = activeElement.getBoundingClientRect();\n const containerRect = cont.getBoundingClientRect();\n if (elementRect.top < containerRect.top || elementRect.bottom > containerRect.bottom) {\n activeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n }\n });\n }, [activeId, isExpanded]);\n\n const filteredHeadings = useMemo(() => {\n if (!searchQuery.trim()) return headings;\n const query = searchQuery.toLowerCase();\n return headings.filter(h => h.text.toLowerCase().includes(query));\n }, [headings, searchQuery]);\n\n const treeData = useMemo(() => buildHeadingTree(filteredHeadings), [filteredHeadings]);\n const defaultExpandedIds = useMemo(() => getAllNodeIds(treeData), [treeData]);\n\n const getContent = useCallback(() => getMarkdownContent(container, true), [container]);\n const getSelection = useCallback(() => getSelectionInfo(container), [container]);\n\n const handleActionClick = (action: ArticleAction) => {\n action.action(getContent(), getSelection());\n };\n\n const handleTreeNodeClick = (node: TreeNode) => {\n const heading = node.meta?.heading as HeadingItem;\n if (heading) {\n scrollToHeading(heading);\n onHeadingClick?.(heading);\n }\n };\n\n if (headings.length === 0) return null;\n\n return (\n <div\n ref={controlRef}\n className={`fixed z-50 ${className}`}\n style={{\n left: `${position.x}px`,\n top: `${position.y}px`,\n cursor: isDragging ? 'grabbing' : 'grab',\n touchAction: 'none',\n }}\n onMouseDown={handleMouseDown}\n onTouchStart={handleTouchStart}\n >\n {isExpanded ? (\n <div\n className=\"bg-base-100 rounded-lg shadow-2xl border border-base-300\"\n style={{ width: `${panelWidth}px` }}\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-base-300 bg-base-200 rounded-t-lg\">\n <div className=\"flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5 text-primary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 12h16M4 18h7\" />\n </svg>\n <span className=\"font-semibold text-sm\">目录</span>\n <span className=\"text-xs text-base-content/60\">({filteredHeadings.length}/{headings.length})</span>\n </div>\n <button onClick={() => setIsExpanded(false)} className=\"btn btn-ghost btn-xs btn-circle\" title=\"收起\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className=\"px-3 py-2 border-b border-base-300 toc-search\">\n <div className=\"relative\">\n <input\n type=\"text\"\n placeholder=\"筛选目录...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"input input-sm input-bordered w-full pl-8 text-sm\"\n onClick={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n />\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4 absolute left-2.5 top-1/2 -translate-y-1/2 opacity-50\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n {searchQuery && (\n <button\n onClick={(e) => { e.stopPropagation(); setSearchQuery(''); }}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 btn btn-ghost btn-xs btn-circle\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n\n {actions.length > 0 && (\n <div className=\"px-2 py-2 border-b border-base-300 bg-base-100\">\n <div className=\"flex flex-wrap gap-1\">\n {actions.map((action) => (\n <div\n key={action.id}\n className=\"relative group\"\n onMouseEnter={() => setHoveredAction(action.id)}\n onMouseLeave={() => setHoveredAction(null)}\n >\n <button\n onClick={(e) => { e.stopPropagation(); handleActionClick(action); }}\n className=\"btn btn-ghost btn-xs btn-square\"\n title={action.label}\n >\n {action.icon || (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n )}\n </button>\n {hoveredAction === action.id && (action.label || action.description) && (\n <div className=\"absolute left-full ml-2 top-0 z-50 bg-base-content text-base-100 px-3 py-2 rounded shadow-lg whitespace-nowrap min-w-max\">\n {action.label && <div className=\"font-semibold text-sm\">{action.label}</div>}\n {action.description && <div className=\"text-xs opacity-80 mt-1\">{action.description}</div>}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n <div ref={tocContainerRef} className=\"overflow-y-auto p-2 scroll-smooth\" style={{ maxHeight: `${panelMaxHeight}px` }}>\n <Tree\n data={treeData}\n activeNodeId={activeId}\n defaultExpandedIds={defaultExpandedIds}\n showSearch={false}\n showExpandAll={false}\n showIcon={true}\n showCount={false}\n showLine={true}\n indent={8}\n togglePosition=\"right\"\n maxHeight={`${panelMaxHeight}px`}\n className={`article-toc-tree ${prevActiveId !== activeId ? 'toc-transitioning' : ''}`}\n onNodeClick={handleTreeNodeClick}\n />\n </div>\n </div>\n ) : (\n <button\n onClick={() => setIsExpanded(true)}\n className=\"btn btn-circle btn-primary shadow-2xl hover:scale-110 transition-transform\"\n style={{ width: `${ballSize}px`, height: `${ballSize}px` }}\n title={`目录 (${headings.length})`}\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 12h16M4 18h7\" />\n </svg>\n </button>\n )}\n\n {isDragging && (\n <div className=\"absolute -top-8 left-1/2 -translate-x-1/2 bg-base-content text-base-100 px-3 py-1 rounded text-xs whitespace-nowrap\">\n 拖动中...\n </div>\n )}\n </div>\n );\n};\n\nexport default ArticleControl;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ArticleControl2.js","names":[],"sources":["../../../../src/components/pro/ArticleControl/ArticleControl.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { Tree } from '../../Tree';\nimport type { TreeNode } from '../../Tree';\nimport { useDraggable } from '../../../hooks/useDraggable';\nimport { useContainerElement } from '../../../hooks/useContainerElement';\nimport { getSelectionInfo, getMarkdownContent } from '../../../utils/selection';\nimport type { ArticleControlProps, ArticleAction, HeadingItem } from './types';\nimport { useArticleToc } from './useArticleToc';\nimport { buildHeadingTree, getAllNodeIds } from './buildHeadingTree';\nimport './ArticleControl.css';\n\nconst ArticleControl: React.FC<ArticleControlProps> = ({\n targetId,\n targetRef,\n initialPosition = { x: window.innerWidth - 320, y: 100 },\n initialExpanded = true,\n ballSize = 56,\n panelWidth = 280,\n panelMaxHeight = 400,\n className = '',\n onHeadingClick,\n actions = [],\n}) => {\n const [isExpanded, setIsExpanded] = useState(initialExpanded);\n const controlRef = useRef<HTMLDivElement>(null);\n const tocContainerRef = useRef<HTMLDivElement>(null);\n const [hoveredAction, setHoveredAction] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n\n const { position, isDragging, handleMouseDown, handleTouchStart } = useDraggable({\n initialPosition,\n boundarySize: {\n width: isExpanded ? panelWidth : ballSize,\n height: isExpanded ? 60 : ballSize,\n },\n });\n\n const container = useContainerElement(targetId, targetRef);\n const { headings, activeId, prevActiveId, scrollToHeading } = useArticleToc(container);\n\n useEffect(() => {\n if (!activeId || !tocContainerRef.current || !isExpanded) return;\n requestAnimationFrame(() => {\n const activeElement = tocContainerRef.current?.querySelector(`[data-node-id=\"${activeId}\"]`);\n if (activeElement) {\n const cont = tocContainerRef.current;\n if (!cont) return;\n const elementRect = activeElement.getBoundingClientRect();\n const containerRect = cont.getBoundingClientRect();\n if (elementRect.top < containerRect.top || elementRect.bottom > containerRect.bottom) {\n activeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n }\n });\n }, [activeId, isExpanded]);\n\n const filteredHeadings = useMemo(() => {\n if (!searchQuery.trim()) return headings;\n const query = searchQuery.toLowerCase();\n return headings.filter(h => h.text.toLowerCase().includes(query));\n }, [headings, searchQuery]);\n\n const treeData = useMemo(() => buildHeadingTree(filteredHeadings), [filteredHeadings]);\n const defaultExpandedIds = useMemo(() => getAllNodeIds(treeData), [treeData]);\n\n const getContent = useCallback(() => getMarkdownContent(container, true), [container]);\n const getSelection = useCallback(() => getSelectionInfo(container), [container]);\n\n const handleActionClick = (action: ArticleAction) => {\n action.action(getContent(), getSelection());\n };\n\n const handleTreeNodeClick = (node: TreeNode) => {\n const heading = node.meta?.heading as HeadingItem;\n if (heading) {\n scrollToHeading(heading);\n onHeadingClick?.(heading);\n }\n };\n\n if (headings.length === 0) return null;\n\n return (\n <div\n ref={controlRef}\n className={`fixed z-50 ${className}`}\n style={{\n left: `${position.x}px`,\n top: `${position.y}px`,\n cursor: isDragging ? 'grabbing' : 'grab',\n touchAction: 'none',\n }}\n onMouseDown={handleMouseDown}\n onTouchStart={handleTouchStart}\n >\n {isExpanded ? (\n <div\n className=\"bg-base-100 rounded-lg shadow-2xl border border-base-300\"\n style={{ width: `${panelWidth}px` }}\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-base-300 bg-base-200 rounded-t-lg\">\n <div className=\"flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5 text-primary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 12h16M4 18h7\" />\n </svg>\n <span className=\"font-semibold text-sm\">目录</span>\n <span className=\"text-xs text-base-content/60\">({filteredHeadings.length}/{headings.length})</span>\n </div>\n <button onClick={() => setIsExpanded(false)} className=\"btn btn-ghost btn-xs btn-circle\" title=\"收起\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className=\"px-3 py-2 border-b border-base-300 toc-search\">\n <div className=\"relative\">\n <input\n type=\"text\"\n placeholder=\"筛选目录...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"input input-sm input-bordered w-full pl-8 text-sm\"\n onClick={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n onTouchStart={(e) => e.stopPropagation()}\n />\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4 absolute left-2.5 top-1/2 -translate-y-1/2 opacity-50\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n {searchQuery && (\n <button\n onClick={(e) => { e.stopPropagation(); setSearchQuery(''); }}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 btn btn-ghost btn-xs btn-circle\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n\n {actions.length > 0 && (\n <div className=\"px-2 py-2 border-b border-base-300 bg-base-100\">\n <div className=\"flex flex-wrap gap-1\">\n {actions.map((action) => (\n <div\n key={action.id}\n className=\"relative group\"\n onMouseEnter={() => setHoveredAction(action.id)}\n onMouseLeave={() => setHoveredAction(null)}\n >\n <button\n onClick={(e) => { e.stopPropagation(); handleActionClick(action); }}\n className=\"btn btn-ghost btn-xs btn-square\"\n title={action.label}\n >\n {action.icon || (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n )}\n </button>\n {hoveredAction === action.id && (action.label || action.description) && (\n <div className=\"absolute left-full ml-2 top-0 z-50 bg-base-content text-base-100 px-3 py-2 rounded shadow-lg whitespace-nowrap min-w-max\">\n {action.label && <div className=\"font-semibold text-sm\">{action.label}</div>}\n {action.description && <div className=\"text-xs opacity-80 mt-1\">{action.description}</div>}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n <div ref={tocContainerRef} className=\"overflow-y-auto p-2 scroll-smooth\" style={{ maxHeight: `${panelMaxHeight}px` }}>\n <Tree\n data={treeData}\n activeNodeId={activeId}\n defaultExpandedIds={defaultExpandedIds}\n showSearch={false}\n showExpandAll={false}\n showIcon={true}\n showCount={false}\n showLine={true}\n indent={8}\n togglePosition=\"right\"\n maxHeight={`${panelMaxHeight}px`}\n className={`article-toc-tree ${prevActiveId !== activeId ? 'toc-transitioning' : ''}`}\n onNodeClick={handleTreeNodeClick}\n />\n </div>\n </div>\n ) : (\n <button\n onClick={() => setIsExpanded(true)}\n className=\"btn btn-circle btn-primary shadow-2xl hover:scale-110 transition-transform\"\n style={{ width: `${ballSize}px`, height: `${ballSize}px` }}\n title={`目录 (${headings.length})`}\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 12h16M4 18h7\" />\n </svg>\n </button>\n )}\n\n {isDragging && (\n <div className=\"absolute -top-8 left-1/2 -translate-x-1/2 bg-base-content text-base-100 px-3 py-1 rounded text-xs whitespace-nowrap\">\n 拖动中...\n </div>\n )}\n </div>\n );\n};\n\nexport default ArticleControl;\n"],"mappings":"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"}