@wzyjs/uis 0.3.28 → 0.3.30
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.
- package/dist/advanced/Com2Canvas/index.d.ts +8 -0
- package/dist/advanced/Com2Canvas/index.js +39 -0
- package/dist/advanced/Crud/components/CardList/index.d.ts +2 -0
- package/dist/advanced/Crud/components/CardList/index.js +90 -0
- package/dist/advanced/Crud/components/CreateUpdate/index.d.ts +2 -0
- package/dist/advanced/Crud/components/CreateUpdate/index.js +78 -0
- package/dist/advanced/Crud/components/ListTabs/index.d.ts +8 -0
- package/dist/advanced/Crud/components/ListTabs/index.js +7 -0
- package/dist/advanced/Crud/components/Provider/index.d.ts +7 -0
- package/dist/advanced/Crud/components/Provider/index.js +42 -0
- package/dist/advanced/Crud/components/QuickFilters/index.d.ts +8 -0
- package/dist/advanced/Crud/components/QuickFilters/index.js +20 -0
- package/dist/advanced/Crud/components/Remove/index.d.ts +2 -0
- package/dist/advanced/Crud/components/Remove/index.js +18 -0
- package/dist/advanced/Crud/components/index.d.ts +6 -0
- package/dist/advanced/Crud/components/index.js +6 -0
- package/dist/advanced/Crud/hooks/index.d.ts +5 -0
- package/dist/advanced/Crud/hooks/index.js +5 -0
- package/dist/advanced/Crud/hooks/useColumns.d.ts +11 -0
- package/dist/advanced/Crud/hooks/useColumns.js +111 -0
- package/dist/advanced/Crud/hooks/useList.d.ts +12 -0
- package/dist/advanced/Crud/hooks/useList.js +53 -0
- package/dist/advanced/Crud/hooks/useListFilters.d.ts +11 -0
- package/dist/advanced/Crud/hooks/useListFilters.js +159 -0
- package/dist/advanced/Crud/hooks/useOrderable.d.ts +15 -0
- package/dist/advanced/Crud/hooks/useOrderable.js +75 -0
- package/dist/advanced/Crud/hooks/useRequest.d.ts +13 -0
- package/dist/advanced/Crud/hooks/useRequest.js +27 -0
- package/dist/advanced/Crud/index.d.ts +3 -0
- package/dist/advanced/Crud/index.js +46 -0
- package/dist/advanced/Crud/types/index.d.ts +176 -0
- package/dist/advanced/Crud/types/index.js +1 -0
- package/dist/advanced/Crud/utils/index.d.ts +7 -0
- package/dist/advanced/Crud/utils/index.js +80 -0
- package/dist/advanced/Crud/utils/query.d.ts +3 -0
- package/dist/advanced/Crud/utils/query.js +34 -0
- package/dist/advanced/MindMap/context.d.ts +12 -0
- package/dist/advanced/MindMap/context.js +12 -0
- package/dist/advanced/MindMap/hooks/useAlignmentSnap.d.ts +15 -0
- package/dist/advanced/MindMap/hooks/useAlignmentSnap.js +164 -0
- package/dist/advanced/MindMap/hooks/useCopyPaste.d.ts +11 -0
- package/dist/advanced/MindMap/hooks/useCopyPaste.js +209 -0
- package/dist/advanced/MindMap/hooks/useDropToReparent.d.ts +21 -0
- package/dist/advanced/MindMap/hooks/useDropToReparent.js +216 -0
- package/dist/advanced/MindMap/hooks/useExpandCollapse.d.ts +18 -0
- package/dist/advanced/MindMap/hooks/useExpandCollapse.js +108 -0
- package/dist/advanced/MindMap/hooks/useMoveDescendants.d.ts +12 -0
- package/dist/advanced/MindMap/hooks/useMoveDescendants.js +98 -0
- package/dist/advanced/MindMap/hooks/useUndoRedo.d.ts +14 -0
- package/dist/advanced/MindMap/hooks/useUndoRedo.js +181 -0
- package/dist/advanced/MindMap/index.d.ts +29 -0
- package/dist/advanced/MindMap/index.js +52 -0
- package/dist/advanced/index.d.ts +5 -0
- package/dist/advanced/index.js +5 -0
- package/dist/antd/index.d.ts +6 -0
- package/dist/antd/index.js +5 -0
- package/dist/buttons/ButtonGroup/index.d.ts +8 -0
- package/dist/buttons/ButtonGroup/index.js +13 -0
- package/dist/buttons/ConfirmButton/index.d.ts +5 -0
- package/dist/buttons/ConfirmButton/index.js +9 -0
- package/dist/buttons/CopyButton/index.d.ts +6 -0
- package/dist/buttons/CopyButton/index.js +26 -0
- package/dist/buttons/DrawerButton/index.d.ts +6 -0
- package/dist/buttons/DrawerButton/index.js +13 -0
- package/dist/buttons/ProgressButton/index.css +63 -0
- package/dist/buttons/ProgressButton/index.d.ts +17 -0
- package/dist/buttons/ProgressButton/index.js +31 -0
- package/dist/buttons/SectorButton/index.d.ts +20 -0
- package/dist/buttons/SectorButton/index.js +130 -0
- package/dist/buttons/index.d.ts +6 -0
- package/dist/buttons/index.js +6 -0
- package/dist/display/CodeView/index.d.ts +26 -0
- package/dist/display/CodeView/index.js +60 -0
- package/dist/display/EnumTag/index.d.ts +12 -0
- package/dist/display/EnumTag/index.js +10 -0
- package/dist/display/HtmlDataRenderer/index.d.ts +6 -0
- package/dist/display/HtmlDataRenderer/index.js +15 -0
- package/dist/display/HtmlView/index.d.ts +6 -0
- package/dist/display/HtmlView/index.js +6 -0
- package/dist/display/IframePro/index.d.ts +8 -0
- package/dist/display/IframePro/index.js +24 -0
- package/dist/display/JsonSchemaRenderer/index.d.ts +11 -0
- package/dist/display/JsonSchemaRenderer/index.js +62 -0
- package/dist/display/JsonView/index.d.ts +3 -0
- package/dist/display/JsonView/index.js +7 -0
- package/dist/display/MarkdownView/index.d.ts +7 -0
- package/dist/display/MarkdownView/index.js +80 -0
- package/dist/display/MarkdownView/style.d.ts +1 -0
- package/{src/components/Markdown/style.ts → dist/display/MarkdownView/style.js} +1 -1
- package/dist/display/VideoPro/index.d.ts +9 -0
- package/dist/display/VideoPro/index.js +15 -0
- package/dist/display/index.d.ts +9 -0
- package/dist/display/index.js +9 -0
- package/dist/inputs/CheckboxButton/index.css +22 -0
- package/dist/inputs/CheckboxButton/index.d.ts +12 -0
- package/dist/inputs/CheckboxButton/index.js +9 -0
- package/dist/inputs/DateSwitcher/index.css +10 -0
- package/dist/inputs/DateSwitcher/index.d.ts +8 -0
- package/dist/inputs/DateSwitcher/index.js +29 -0
- package/dist/inputs/FetchSelect/index.d.ts +3 -0
- package/dist/inputs/FetchSelect/index.js +121 -0
- package/dist/inputs/FetchSelect/types.d.ts +33 -0
- package/dist/inputs/FetchSelect/types.js +1 -0
- package/dist/inputs/FetchSelect/utils.d.ts +21 -0
- package/dist/inputs/FetchSelect/utils.js +67 -0
- package/dist/inputs/FileUploader/index.d.ts +22 -0
- package/dist/inputs/FileUploader/index.js +79 -0
- package/dist/inputs/IconSelect/index.d.ts +89 -0
- package/dist/inputs/IconSelect/index.js +54 -0
- package/dist/inputs/ImageUploader/index.d.ts +12 -0
- package/dist/inputs/ImageUploader/index.js +192 -0
- package/dist/inputs/RadioButton/index.d.ts +15 -0
- package/dist/inputs/RadioButton/index.js +11 -0
- package/dist/inputs/RangeInput/index.d.ts +8 -0
- package/dist/inputs/RangeInput/index.js +17 -0
- package/dist/inputs/TextInput/index.d.ts +6 -0
- package/dist/inputs/TextInput/index.js +30 -0
- package/dist/inputs/index.d.ts +9 -0
- package/dist/inputs/index.js +9 -0
- package/dist/layout/DragSort/index.d.ts +16 -0
- package/dist/layout/DragSort/index.js +12 -0
- package/dist/layout/FoldCard/index.d.ts +9 -0
- package/dist/layout/FoldCard/index.js +69 -0
- package/dist/layout/PageBase/index.d.ts +6 -0
- package/dist/layout/PageBase/index.js +6 -0
- package/dist/layout/ResizableGridLayout/index.d.ts +11 -0
- package/dist/layout/ResizableGridLayout/index.js +13 -0
- package/dist/layout/SideMenu/index.d.ts +27 -0
- package/dist/layout/SideMenu/index.js +40 -0
- package/dist/layout/TabsPro/index.d.ts +9 -0
- package/dist/layout/TabsPro/index.js +87 -0
- package/dist/layout/index.d.ts +6 -0
- package/dist/layout/index.js +6 -0
- package/dist/web.d.ts +6 -0
- package/dist/web.js +6 -0
- package/package.json +28 -12
- package/src/antd/form/CheckboxButton/index.module.scss +0 -24
- package/src/antd/form/CheckboxButton/index.tsx +0 -31
- package/src/antd/form/FileUploader/index.tsx +0 -163
- package/src/antd/form/RadioButton/index.tsx +0 -32
- package/src/antd/form/Upload/index.tsx +0 -65
- package/src/antd/form/UploadImage/index.tsx +0 -338
- package/src/antd/form/index.ts +0 -6
- package/src/antd/index.ts +0 -46
- package/src/antd/pro/Alert/index.tsx +0 -24
- package/src/antd/pro/Button/components/Confirm.tsx +0 -24
- package/src/antd/pro/Button/components/Copy.tsx +0 -47
- package/src/antd/pro/Button/components/Drawer.tsx +0 -37
- package/src/antd/pro/Button/components/Group.tsx +0 -26
- package/src/antd/pro/Button/index.tsx +0 -11
- package/src/antd/pro/Card/index.tsx +0 -92
- package/src/antd/pro/Collapse/components/Item.tsx +0 -30
- package/src/antd/pro/Collapse/index.tsx +0 -27
- package/src/antd/pro/Image/index.tsx +0 -17
- package/src/antd/pro/Input/components/Range.tsx +0 -46
- package/src/antd/pro/Input/index.tsx +0 -61
- package/src/antd/pro/Popconfirm/index.tsx +0 -16
- package/src/antd/pro/Radio/components/Cancel.tsx +0 -30
- package/src/antd/pro/Radio/index.tsx +0 -7
- package/src/antd/pro/Space/index.tsx +0 -15
- package/src/antd/pro/Tabs/index.tsx +0 -135
- package/src/antd/pro/Typography/components/String.tsx +0 -72
- package/src/antd/pro/Typography/index.tsx +0 -9
- package/src/antd/pro/index.ts +0 -11
- package/src/components/BottomBar/index.tsx +0 -28
- package/src/components/CodeView/index.tsx +0 -85
- package/src/components/Collapse/index.tsx +0 -26
- package/src/components/Com2Canvas/index.tsx +0 -60
- package/src/components/CompileHtml/index.tsx +0 -26
- package/src/components/Crud/components/CardList/index.tsx +0 -174
- package/src/components/Crud/components/CreateUpdate/index.tsx +0 -179
- package/src/components/Crud/components/Provider/index.tsx +0 -83
- package/src/components/Crud/components/Remove/index.tsx +0 -56
- package/src/components/Crud/components/index.ts +0 -4
- package/src/components/Crud/hooks/index.ts +0 -4
- package/src/components/Crud/hooks/useColumns.tsx +0 -169
- package/src/components/Crud/hooks/useList.ts +0 -65
- package/src/components/Crud/hooks/useOrderable.tsx +0 -107
- package/src/components/Crud/hooks/useRequest.ts +0 -41
- package/src/components/Crud/index.tsx +0 -91
- package/src/components/Crud/types/index.ts +0 -188
- package/src/components/Crud/utils/index.ts +0 -87
- package/src/components/DateSwitcher/index.module.scss +0 -10
- package/src/components/DateSwitcher/index.tsx +0 -75
- package/src/components/DownloadLink/index.tsx +0 -36
- package/src/components/DragSort/index.tsx +0 -77
- package/src/components/DynamicSelect/index.tsx +0 -74
- package/src/components/DynamicSelect/utils.ts +0 -45
- package/src/components/EnumTag/index.tsx +0 -24
- package/src/components/FetchSelect/index.tsx +0 -57
- package/src/components/Fold/index.tsx +0 -52
- package/src/components/FormPro/index.tsx +0 -28
- package/src/components/GroupLayout/index.tsx +0 -45
- package/src/components/HtmlPro/index.tsx +0 -18
- package/src/components/IframePro/index.tsx +0 -52
- package/src/components/JsonRenderer/index.tsx +0 -114
- package/src/components/JsonView/index.tsx +0 -21
- package/src/components/Markdown/index.tsx +0 -152
- package/src/components/MindMap/context.tsx +0 -29
- package/src/components/MindMap/hooks/useAlignmentSnap.ts +0 -220
- package/src/components/MindMap/hooks/useCopyPaste.ts +0 -272
- package/src/components/MindMap/hooks/useDropToReparent.ts +0 -288
- package/src/components/MindMap/hooks/useExpandCollapse.ts +0 -146
- package/src/components/MindMap/hooks/useMoveDescendants.ts +0 -136
- package/src/components/MindMap/hooks/useUndoRedo.ts +0 -232
- package/src/components/MindMap/index.tsx +0 -117
- package/src/components/MultiImageDisplay/index.tsx +0 -63
- package/src/components/ProgressButton/index.module.scss +0 -65
- package/src/components/ProgressButton/index.tsx +0 -96
- package/src/components/SectorButton/index.tsx +0 -247
- package/src/components/TextInput/index.tsx +0 -61
- package/src/components/TimelineBar/components/CurrentWeekHighlight/index.tsx +0 -64
- package/src/components/TimelineBar/components/Guides/index.tsx +0 -61
- package/src/components/TimelineBar/components/Ticks/index.tsx +0 -56
- package/src/components/TimelineBar/components/TodayIndicator/index.tsx +0 -54
- package/src/components/TimelineBar/components/index.ts +0 -4
- package/src/components/TimelineBar/const.ts +0 -3
- package/src/components/TimelineBar/hooks/index.ts +0 -5
- package/src/components/TimelineBar/hooks/useHighlightRange.ts +0 -21
- package/src/components/TimelineBar/hooks/useMonthGuides.ts +0 -40
- package/src/components/TimelineBar/hooks/useTickValues.ts +0 -18
- package/src/components/TimelineBar/hooks/useVisibleRange.ts +0 -43
- package/src/components/TimelineBar/hooks/useWeekGuides.ts +0 -39
- package/src/components/TimelineBar/index.tsx +0 -63
- package/src/components/TimelineBar/utils.ts +0 -27
- package/src/components/Video/index.tsx +0 -37
- package/src/components/index.ts +0 -27
- package/src/rn.ts +0 -1
- package/src/rns/index.ts +0 -0
- package/src/web.ts +0 -2
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useMemo } from 'react'
|
|
4
|
-
import ReactMarkdown from 'react-markdown'
|
|
5
|
-
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
6
|
-
import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
7
|
-
import remarkGfm from 'remark-gfm'
|
|
8
|
-
import rehypeSlug from 'rehype-slug'
|
|
9
|
-
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
|
|
10
|
-
|
|
11
|
-
import 'github-markdown-css/github-markdown-light.css'
|
|
12
|
-
import { style } from './style'
|
|
13
|
-
|
|
14
|
-
interface MarkdownProps {
|
|
15
|
-
content: string
|
|
16
|
-
showToc?: boolean // 是否显示目录
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface TocItem {
|
|
20
|
-
id: string
|
|
21
|
-
text: string
|
|
22
|
-
level: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const Markdown = (props: MarkdownProps) => {
|
|
26
|
-
const { content = '', showToc = true } = props
|
|
27
|
-
|
|
28
|
-
// 提取标题生成目录
|
|
29
|
-
const tocItems = useMemo(() => {
|
|
30
|
-
if (!showToc) {
|
|
31
|
-
return []
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const lines = content.split('\n')
|
|
35
|
-
const items: TocItem[] = []
|
|
36
|
-
|
|
37
|
-
lines.forEach((line) => {
|
|
38
|
-
const match = line.match(/^(#{2,4})\s+(.+)$/)
|
|
39
|
-
if (match && match[1] && match[2]) {
|
|
40
|
-
const level = match[1].length
|
|
41
|
-
const text = match[2].trim()
|
|
42
|
-
const id = text
|
|
43
|
-
.toLowerCase()
|
|
44
|
-
.replace(/[^\w\u4e00-\u9fa5\s-]/g, '') // 保留中文、英文、数字、空格和连字符
|
|
45
|
-
.replace(/\s+/g, '-') // 空格替换为连字符
|
|
46
|
-
|
|
47
|
-
items.push({ id, text, level })
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
return items
|
|
52
|
-
}, [content, showToc])
|
|
53
|
-
|
|
54
|
-
// 渲染目录
|
|
55
|
-
const renderToc = () => {
|
|
56
|
-
if (!showToc || tocItems.length === 0) {
|
|
57
|
-
return null
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<div
|
|
62
|
-
className='markdown-toc-container'
|
|
63
|
-
style={{
|
|
64
|
-
padding: '10px 20px 10px 0',
|
|
65
|
-
backgroundColor: '#f8f9fa',
|
|
66
|
-
borderLeft: '4px solid #1890ff',
|
|
67
|
-
borderRadius: '4px',
|
|
68
|
-
overflowY: 'auto',
|
|
69
|
-
height: '100%'
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
<ul style={{ margin: 0, padding: 0 }}>
|
|
73
|
-
{tocItems.map((item, index) => (
|
|
74
|
-
<li
|
|
75
|
-
key={index}
|
|
76
|
-
style={{
|
|
77
|
-
marginBottom: '4px',
|
|
78
|
-
marginLeft: `${(item.level - 1) * 16}px`,
|
|
79
|
-
listStyle: 'none',
|
|
80
|
-
position: 'relative',
|
|
81
|
-
}}
|
|
82
|
-
>
|
|
83
|
-
<a
|
|
84
|
-
href={`#${item.id}`}
|
|
85
|
-
style={{
|
|
86
|
-
textDecoration: 'none',
|
|
87
|
-
color: '#1890ff',
|
|
88
|
-
fontSize: item.level <= 2 ? '14px' : '13px',
|
|
89
|
-
fontWeight: item.level === 1 ? 600 : 400,
|
|
90
|
-
}}
|
|
91
|
-
onClick={(e) => {
|
|
92
|
-
e.preventDefault()
|
|
93
|
-
const element = document.getElementById(item.id)
|
|
94
|
-
if (element) {
|
|
95
|
-
element.scrollIntoView({ behavior: 'smooth' })
|
|
96
|
-
}
|
|
97
|
-
}}
|
|
98
|
-
>
|
|
99
|
-
{item.text}
|
|
100
|
-
</a>
|
|
101
|
-
</li>
|
|
102
|
-
))}
|
|
103
|
-
</ul>
|
|
104
|
-
</div>
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div style={{ height: '100%' }}>
|
|
110
|
-
<style>{style}</style>
|
|
111
|
-
<div className='markdown-body markdown-toc' style={{ height: '100%', display: 'flex' }}>
|
|
112
|
-
<div style={{ marginRight: 14 }}>
|
|
113
|
-
{renderToc()}
|
|
114
|
-
</div>
|
|
115
|
-
<div style={{ overflow: 'auto', flex: 1 }}>
|
|
116
|
-
<ReactMarkdown
|
|
117
|
-
remarkPlugins={[remarkGfm]}
|
|
118
|
-
rehypePlugins={[
|
|
119
|
-
rehypeSlug,
|
|
120
|
-
[rehypeAutolinkHeadings, {
|
|
121
|
-
behavior: 'wrap',
|
|
122
|
-
properties: {
|
|
123
|
-
className: ['anchor'],
|
|
124
|
-
},
|
|
125
|
-
}],
|
|
126
|
-
]}
|
|
127
|
-
components={{
|
|
128
|
-
code(props) {
|
|
129
|
-
const { children, className } = props
|
|
130
|
-
const match = /language-(\w+)/.exec(className || '')
|
|
131
|
-
return match ? (
|
|
132
|
-
<SyntaxHighlighter
|
|
133
|
-
PreTag='div'
|
|
134
|
-
children={String(children).replace(/\n$/, '')}
|
|
135
|
-
language={match[1]}
|
|
136
|
-
style={oneLight}
|
|
137
|
-
/>
|
|
138
|
-
) : (
|
|
139
|
-
<code className={className}>
|
|
140
|
-
{children}
|
|
141
|
-
</code>
|
|
142
|
-
)
|
|
143
|
-
},
|
|
144
|
-
}}
|
|
145
|
-
>
|
|
146
|
-
{content}
|
|
147
|
-
</ReactMarkdown>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
)
|
|
152
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext, type ReactNode } from 'react'
|
|
4
|
-
|
|
5
|
-
export interface MindMapDndState {
|
|
6
|
-
draggingNodeId: string | null
|
|
7
|
-
dropTargetNodeId: string | null
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const MindMapDndContext = createContext<MindMapDndState | null>(null)
|
|
11
|
-
|
|
12
|
-
interface MindMapDndProviderProps {
|
|
13
|
-
value: MindMapDndState
|
|
14
|
-
children: ReactNode
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const MindMapDndProvider = (props: MindMapDndProviderProps) => {
|
|
18
|
-
const { value, children } = props
|
|
19
|
-
return (
|
|
20
|
-
<MindMapDndContext.Provider value={value}>
|
|
21
|
-
{children}
|
|
22
|
-
</MindMapDndContext.Provider>
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const useMindMapDndState = (): MindMapDndState => {
|
|
27
|
-
const value = useContext(MindMapDndContext)
|
|
28
|
-
return value ?? { draggingNodeId: null, dropTargetNodeId: null }
|
|
29
|
-
}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { Fragment, createElement, useCallback, useMemo, useState, type ReactElement } from 'react'
|
|
4
|
-
|
|
5
|
-
import type { NodeChange, Node, Viewport } from '@xyflow/react'
|
|
6
|
-
|
|
7
|
-
interface HelperLines {
|
|
8
|
-
x: number | null
|
|
9
|
-
y: number | null
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface UseAlignmentSnapParams<TNodeData extends Record<string, unknown>> {
|
|
13
|
-
// 开关:关闭时只透传回调,不做任何吸附与对齐线计算
|
|
14
|
-
enabled?: boolean
|
|
15
|
-
// 当前节点列表(用于计算对齐点)
|
|
16
|
-
nodes: Array<Node<TNodeData>>
|
|
17
|
-
// 透传给 ReactFlow 的 onNodesChange(通常来自 useNodesState)
|
|
18
|
-
onNodesChange?: (changes: Array<NodeChange<Node<TNodeData>>>) => void
|
|
19
|
-
// 吸附阈值(单位:画布坐标 px)
|
|
20
|
-
snapThreshold?: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface UseAlignmentSnapResult<TNodeData extends Record<string, unknown>> {
|
|
24
|
-
// hook 直接产出的对齐线元素,主组件可直接渲染
|
|
25
|
-
helperLines: ReactElement | null
|
|
26
|
-
// 用于同步 viewport,保证对齐线在屏幕坐标下位置正确
|
|
27
|
-
onMove: (_event: unknown, viewport: Viewport) => void
|
|
28
|
-
// 包装后的 onNodesChange:在 position change 上做吸附与对齐线更新
|
|
29
|
-
onNodesChange: (changes: Array<NodeChange<Node<TNodeData>>>) => void
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const getNodeSize = <TNodeData extends Record<string, unknown>,>(node: Node<TNodeData>): { width: number, height: number } => {
|
|
33
|
-
const width = node.measured?.width ?? node.width ?? 0
|
|
34
|
-
const height = node.measured?.height ?? node.height ?? 0
|
|
35
|
-
return { width, height }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const useAlignmentSnap = <TNodeData extends Record<string, unknown>,>(params: UseAlignmentSnapParams<TNodeData>): UseAlignmentSnapResult<TNodeData> => {
|
|
39
|
-
const { enabled = false, nodes, onNodesChange: onNodesChangeExternal, snapThreshold = 6 } = params
|
|
40
|
-
|
|
41
|
-
const [viewport, setViewport] = useState<Viewport>({ x: 0, y: 0, zoom: 1 })
|
|
42
|
-
// 保存对齐线在画布坐标下的位置(未做 viewport 变换)
|
|
43
|
-
const [helperLines, setHelperLines] = useState<HelperLines>({ x: null, y: null })
|
|
44
|
-
|
|
45
|
-
const onMove = useCallback((_event: unknown, nextViewport: Viewport): void => {
|
|
46
|
-
setViewport(nextViewport)
|
|
47
|
-
}, [])
|
|
48
|
-
|
|
49
|
-
const onNodesChange = useCallback((changes: Array<NodeChange<Node<TNodeData>>>): void => {
|
|
50
|
-
if (!onNodesChangeExternal) {
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!enabled) {
|
|
55
|
-
setHelperLines({ x: null, y: null })
|
|
56
|
-
onNodesChangeExternal(changes)
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 只在 position change 上参与吸附,避免对 selection 等变更产生副作用
|
|
61
|
-
const positionChange = changes.find(change => change.type === 'position')
|
|
62
|
-
if (positionChange?.type !== 'position' || !positionChange.position) {
|
|
63
|
-
onNodesChangeExternal(changes)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const dragging = positionChange.dragging === true
|
|
68
|
-
if (!dragging) {
|
|
69
|
-
setHelperLines({ x: null, y: null })
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const baseNode = nodes.find(item => item.id === positionChange.id)
|
|
73
|
-
if (!baseNode) {
|
|
74
|
-
onNodesChangeExternal(changes)
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const selfSize = getNodeSize(baseNode)
|
|
79
|
-
if (!selfSize.width || !selfSize.height) {
|
|
80
|
-
onNodesChangeExternal(changes)
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// 以“边/中/边”作为候选锚点,计算与其他节点锚点的最小距离
|
|
85
|
-
const selfX = positionChange.position.x
|
|
86
|
-
const selfY = positionChange.position.y
|
|
87
|
-
const selfXAnchors: Array<{ point: number, shift: number }> = [
|
|
88
|
-
{ point: selfX, shift: 0 },
|
|
89
|
-
{ point: selfX + selfSize.width / 2, shift: selfSize.width / 2 },
|
|
90
|
-
{ point: selfX + selfSize.width, shift: selfSize.width },
|
|
91
|
-
]
|
|
92
|
-
const selfYAnchors: Array<{ point: number, shift: number }> = [
|
|
93
|
-
{ point: selfY, shift: 0 },
|
|
94
|
-
{ point: selfY + selfSize.height / 2, shift: selfSize.height / 2 },
|
|
95
|
-
{ point: selfY + selfSize.height, shift: selfSize.height },
|
|
96
|
-
]
|
|
97
|
-
|
|
98
|
-
let bestXDiff: number = Number.POSITIVE_INFINITY
|
|
99
|
-
let bestYDiff: number = Number.POSITIVE_INFINITY
|
|
100
|
-
let bestXLine: number | null = null
|
|
101
|
-
let bestYLine: number | null = null
|
|
102
|
-
let bestXOffset: number = 0
|
|
103
|
-
let bestYOffset: number = 0
|
|
104
|
-
|
|
105
|
-
nodes.forEach(other => {
|
|
106
|
-
if (other.id === positionChange.id) {
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
const otherSize = getNodeSize(other)
|
|
110
|
-
if (!otherSize.width || !otherSize.height) {
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const otherX = other.position.x
|
|
115
|
-
const otherY = other.position.y
|
|
116
|
-
const otherXAnchors: Array<number> = [otherX, otherX + otherSize.width / 2, otherX + otherSize.width]
|
|
117
|
-
const otherYAnchors: Array<number> = [otherY, otherY + otherSize.height / 2, otherY + otherSize.height]
|
|
118
|
-
|
|
119
|
-
for (const selfAnchor of selfXAnchors) {
|
|
120
|
-
for (const otherAnchor of otherXAnchors) {
|
|
121
|
-
const diffX = Math.abs(selfAnchor.point - otherAnchor)
|
|
122
|
-
if (diffX <= snapThreshold && diffX < bestXDiff) {
|
|
123
|
-
bestXDiff = diffX
|
|
124
|
-
bestXLine = otherAnchor
|
|
125
|
-
bestXOffset = otherAnchor - selfAnchor.point
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
for (const selfAnchor of selfYAnchors) {
|
|
131
|
-
for (const otherAnchor of otherYAnchors) {
|
|
132
|
-
const diffY = Math.abs(selfAnchor.point - otherAnchor)
|
|
133
|
-
if (diffY <= snapThreshold && diffY < bestYDiff) {
|
|
134
|
-
bestYDiff = diffY
|
|
135
|
-
bestYLine = otherAnchor
|
|
136
|
-
bestYOffset = otherAnchor - selfAnchor.point
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
if (dragging) {
|
|
143
|
-
setHelperLines({ x: bestXLine, y: bestYLine })
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 没命中阈值则完全透传,避免抖动
|
|
147
|
-
if (bestXLine === null && bestYLine === null) {
|
|
148
|
-
onNodesChangeExternal(changes)
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const nextX = bestXLine === null ? selfX : selfX + bestXOffset
|
|
153
|
-
const nextY = bestYLine === null ? selfY : selfY + bestYOffset
|
|
154
|
-
|
|
155
|
-
// 通过“改写 position change”的方式实现吸附,避免与外部受控状态冲突
|
|
156
|
-
const nextChanges: Array<NodeChange<Node<TNodeData>>> = changes.map(change => {
|
|
157
|
-
if (change.type !== 'position' || change.id !== positionChange.id || !change.position) {
|
|
158
|
-
return change
|
|
159
|
-
}
|
|
160
|
-
if (change.position.x === nextX && change.position.y === nextY) {
|
|
161
|
-
return change
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
...change,
|
|
165
|
-
position: { x: nextX, y: nextY },
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
onNodesChangeExternal(nextChanges)
|
|
170
|
-
}, [enabled, nodes, onNodesChangeExternal, snapThreshold])
|
|
171
|
-
|
|
172
|
-
// 将画布坐标转换为屏幕坐标,用于 absolute 定位对齐线
|
|
173
|
-
const helperLineX = useMemo((): number | null => {
|
|
174
|
-
if (helperLines.x === null) {
|
|
175
|
-
return null
|
|
176
|
-
}
|
|
177
|
-
return helperLines.x * viewport.zoom + viewport.x
|
|
178
|
-
}, [helperLines.x, viewport.x, viewport.zoom])
|
|
179
|
-
|
|
180
|
-
const helperLineY = useMemo((): number | null => {
|
|
181
|
-
if (helperLines.y === null) {
|
|
182
|
-
return null
|
|
183
|
-
}
|
|
184
|
-
return helperLines.y * viewport.zoom + viewport.y
|
|
185
|
-
}, [helperLines.y, viewport.y, viewport.zoom])
|
|
186
|
-
|
|
187
|
-
const helperLinesElement = useMemo((): ReactElement | null => {
|
|
188
|
-
if (!enabled) {
|
|
189
|
-
return null
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const elements: ReactElement[] = []
|
|
193
|
-
if (helperLineX !== null) {
|
|
194
|
-
elements.push(createElement('div', {
|
|
195
|
-
key: 'helper-line-x',
|
|
196
|
-
className: 'pointer-events-none absolute top-0 h-full w-px bg-sky-500',
|
|
197
|
-
style: { left: helperLineX },
|
|
198
|
-
}))
|
|
199
|
-
}
|
|
200
|
-
if (helperLineY !== null) {
|
|
201
|
-
elements.push(createElement('div', {
|
|
202
|
-
key: 'helper-line-y',
|
|
203
|
-
className: 'pointer-events-none absolute left-0 w-full h-px bg-sky-500',
|
|
204
|
-
style: { top: helperLineY },
|
|
205
|
-
}))
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (elements.length === 0) {
|
|
209
|
-
return null
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return createElement(Fragment, null, ...elements)
|
|
213
|
-
}, [enabled, helperLineX, helperLineY])
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
helperLines: helperLinesElement,
|
|
217
|
-
onMove,
|
|
218
|
-
onNodesChange,
|
|
219
|
-
}
|
|
220
|
-
}
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
4
|
-
import type { Edge, EdgeChange, Node, NodeChange } from '@xyflow/react'
|
|
5
|
-
|
|
6
|
-
interface MindMapCopyPayload<TNodeData extends Record<string, unknown>> {
|
|
7
|
-
nodes: Array<Node<TNodeData>>
|
|
8
|
-
edges: Edge[]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface MindMapClipboardData<TNodeData extends Record<string, unknown>> {
|
|
12
|
-
t: 'mindmap'
|
|
13
|
-
v: 1
|
|
14
|
-
payload: MindMapCopyPayload<TNodeData>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface UseCopyPasteParams<TNodeData extends Record<string, unknown>> {
|
|
18
|
-
enabled?: boolean
|
|
19
|
-
nodes: Array<Node<TNodeData>>
|
|
20
|
-
edges: Edge[]
|
|
21
|
-
onNodesChange?: (changes: Array<NodeChange<Node<TNodeData>>>) => void
|
|
22
|
-
onEdgesChange?: (changes: Array<EdgeChange<Edge>>) => void
|
|
23
|
-
pasteOffset?: number
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const CLIPBOARD_MIME: string = 'application/x-mindmap+json'
|
|
27
|
-
|
|
28
|
-
const getDescendantIdSet = (edges: Edge[], rootIds: string[]): Set<string> => {
|
|
29
|
-
const sourceToTargets = new Map<string, string[]>()
|
|
30
|
-
edges.forEach(edge => {
|
|
31
|
-
const prev = sourceToTargets.get(edge.source) ?? []
|
|
32
|
-
sourceToTargets.set(edge.source, [...prev, edge.target])
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const visited = new Set<string>()
|
|
36
|
-
const result = new Set<string>()
|
|
37
|
-
const queue: string[] = [...rootIds]
|
|
38
|
-
|
|
39
|
-
rootIds.forEach(id => {
|
|
40
|
-
visited.add(id)
|
|
41
|
-
result.add(id)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
while (queue.length > 0) {
|
|
45
|
-
const id = queue.shift()
|
|
46
|
-
if (!id) {
|
|
47
|
-
continue
|
|
48
|
-
}
|
|
49
|
-
const targets = sourceToTargets.get(id) ?? []
|
|
50
|
-
for (const targetId of targets) {
|
|
51
|
-
if (visited.has(targetId)) {
|
|
52
|
-
continue
|
|
53
|
-
}
|
|
54
|
-
visited.add(targetId)
|
|
55
|
-
result.add(targetId)
|
|
56
|
-
queue.push(targetId)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return result
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const getCrypto = (): Crypto | undefined => {
|
|
64
|
-
const anyGlobal = globalThis as unknown as { crypto?: Crypto }
|
|
65
|
-
return anyGlobal.crypto
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const createId = (): string => {
|
|
69
|
-
const crypto = getCrypto()
|
|
70
|
-
if (crypto?.randomUUID) {
|
|
71
|
-
return crypto.randomUUID()
|
|
72
|
-
}
|
|
73
|
-
return `${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const clone = <T,>(value: T): T => {
|
|
77
|
-
const anyGlobal = globalThis as unknown as { structuredClone?: <V>(v: V) => V }
|
|
78
|
-
if (typeof anyGlobal.structuredClone === 'function') {
|
|
79
|
-
return anyGlobal.structuredClone(value)
|
|
80
|
-
}
|
|
81
|
-
return JSON.parse(JSON.stringify(value)) as T
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const safeParseClipboard = <TNodeData extends Record<string, unknown>,>(text: string): MindMapCopyPayload<TNodeData> | null => {
|
|
85
|
-
try {
|
|
86
|
-
const parsed = JSON.parse(text) as MindMapClipboardData<TNodeData>
|
|
87
|
-
if (parsed?.t !== 'mindmap' || parsed?.v !== 1) {
|
|
88
|
-
return null
|
|
89
|
-
}
|
|
90
|
-
const payload = parsed.payload
|
|
91
|
-
if (!payload?.nodes?.length) {
|
|
92
|
-
return null
|
|
93
|
-
}
|
|
94
|
-
return payload
|
|
95
|
-
} catch {
|
|
96
|
-
return null
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export const useCopyPaste = <TNodeData extends Record<string, unknown>,>(params: UseCopyPasteParams<TNodeData>): void => {
|
|
101
|
-
const {
|
|
102
|
-
enabled = false,
|
|
103
|
-
nodes,
|
|
104
|
-
edges,
|
|
105
|
-
onNodesChange,
|
|
106
|
-
onEdgesChange,
|
|
107
|
-
pasteOffset = 24,
|
|
108
|
-
} = params
|
|
109
|
-
|
|
110
|
-
const selectedNodes = useMemo((): Array<Node<TNodeData>> => {
|
|
111
|
-
return nodes.filter(node => node.selected)
|
|
112
|
-
}, [nodes])
|
|
113
|
-
|
|
114
|
-
const copyIdSet = useMemo((): Set<string> => {
|
|
115
|
-
if (selectedNodes.length === 0) {
|
|
116
|
-
return new Set<string>()
|
|
117
|
-
}
|
|
118
|
-
const rootIds = selectedNodes.map(n => n.id)
|
|
119
|
-
return getDescendantIdSet(edges, rootIds)
|
|
120
|
-
}, [edges, selectedNodes])
|
|
121
|
-
|
|
122
|
-
const nodesToCopy = useMemo((): Array<Node<TNodeData>> => {
|
|
123
|
-
if (copyIdSet.size === 0) {
|
|
124
|
-
return []
|
|
125
|
-
}
|
|
126
|
-
return nodes.filter(node => copyIdSet.has(node.id))
|
|
127
|
-
}, [copyIdSet, nodes])
|
|
128
|
-
|
|
129
|
-
const edgesToCopy = useMemo((): Edge[] => {
|
|
130
|
-
if (copyIdSet.size === 0) {
|
|
131
|
-
return []
|
|
132
|
-
}
|
|
133
|
-
return edges.filter(edge => copyIdSet.has(edge.source) && copyIdSet.has(edge.target))
|
|
134
|
-
}, [copyIdSet, edges])
|
|
135
|
-
|
|
136
|
-
const pasteIndexRef = useRef<number>(0)
|
|
137
|
-
|
|
138
|
-
const isEditableTarget = useCallback((target: EventTarget | null): boolean => {
|
|
139
|
-
const el = target as HTMLElement | null
|
|
140
|
-
if (!el) {
|
|
141
|
-
return false
|
|
142
|
-
}
|
|
143
|
-
const tag = el.tagName?.toLowerCase()
|
|
144
|
-
if (tag === 'input' || tag === 'textarea') {
|
|
145
|
-
return true
|
|
146
|
-
}
|
|
147
|
-
return el.isContentEditable
|
|
148
|
-
}, [])
|
|
149
|
-
|
|
150
|
-
const handleCopy = useCallback((event: ClipboardEvent): void => {
|
|
151
|
-
if (!enabled) {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
if (isEditableTarget(event.target)) {
|
|
155
|
-
return
|
|
156
|
-
}
|
|
157
|
-
if (nodesToCopy.length === 0) {
|
|
158
|
-
return
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const payload: MindMapCopyPayload<TNodeData> = {
|
|
162
|
-
nodes: clone(nodesToCopy),
|
|
163
|
-
edges: clone(edgesToCopy),
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const clipboardData: MindMapClipboardData<TNodeData> = {
|
|
167
|
-
t: 'mindmap',
|
|
168
|
-
v: 1,
|
|
169
|
-
payload,
|
|
170
|
-
}
|
|
171
|
-
const text = JSON.stringify(clipboardData)
|
|
172
|
-
|
|
173
|
-
if (!event.clipboardData) {
|
|
174
|
-
return
|
|
175
|
-
}
|
|
176
|
-
event.preventDefault()
|
|
177
|
-
event.clipboardData.setData(CLIPBOARD_MIME, text)
|
|
178
|
-
event.clipboardData.setData('text/plain', text)
|
|
179
|
-
}, [enabled, edgesToCopy, isEditableTarget, nodesToCopy])
|
|
180
|
-
|
|
181
|
-
const pastePayload = useCallback((payload: MindMapCopyPayload<TNodeData>): void => {
|
|
182
|
-
if (!onNodesChange) {
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
pasteIndexRef.current += 1
|
|
187
|
-
const shift = pasteOffset * pasteIndexRef.current
|
|
188
|
-
|
|
189
|
-
const idMap = new Map<string, string>()
|
|
190
|
-
const nextNodes = payload.nodes.map(node => {
|
|
191
|
-
const nextId = createId()
|
|
192
|
-
idMap.set(node.id, nextId)
|
|
193
|
-
|
|
194
|
-
const nextNode = clone(node)
|
|
195
|
-
nextNode.id = nextId
|
|
196
|
-
nextNode.position = {
|
|
197
|
-
x: nextNode.position.x + shift,
|
|
198
|
-
y: nextNode.position.y + shift,
|
|
199
|
-
}
|
|
200
|
-
nextNode.selected = true
|
|
201
|
-
delete (nextNode as unknown as { positionAbsolute?: unknown }).positionAbsolute
|
|
202
|
-
delete (nextNode as unknown as { measured?: unknown }).measured
|
|
203
|
-
return nextNode
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
const nodeChanges: Array<NodeChange<Node<TNodeData>>> = nextNodes.map(item => ({
|
|
207
|
-
type: 'add',
|
|
208
|
-
item,
|
|
209
|
-
}))
|
|
210
|
-
onNodesChange(nodeChanges)
|
|
211
|
-
|
|
212
|
-
if (!onEdgesChange || payload.edges.length === 0) {
|
|
213
|
-
return
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const nextEdges: Edge[] = payload.edges
|
|
217
|
-
.map(edge => {
|
|
218
|
-
const nextSource = idMap.get(edge.source)
|
|
219
|
-
const nextTarget = idMap.get(edge.target)
|
|
220
|
-
if (!nextSource || !nextTarget) {
|
|
221
|
-
return null
|
|
222
|
-
}
|
|
223
|
-
const nextEdge = clone(edge)
|
|
224
|
-
nextEdge.id = createId()
|
|
225
|
-
nextEdge.source = nextSource
|
|
226
|
-
nextEdge.target = nextTarget
|
|
227
|
-
return nextEdge
|
|
228
|
-
})
|
|
229
|
-
.filter((e): e is Edge => !!e)
|
|
230
|
-
|
|
231
|
-
if (nextEdges.length === 0) {
|
|
232
|
-
return
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const edgeChanges: Array<EdgeChange<Edge>> = nextEdges.map(item => ({
|
|
236
|
-
type: 'add',
|
|
237
|
-
item,
|
|
238
|
-
}))
|
|
239
|
-
onEdgesChange(edgeChanges)
|
|
240
|
-
}, [onEdgesChange, onNodesChange, pasteOffset])
|
|
241
|
-
|
|
242
|
-
const handlePaste = useCallback((event: ClipboardEvent): void => {
|
|
243
|
-
if (!enabled) {
|
|
244
|
-
return
|
|
245
|
-
}
|
|
246
|
-
if (isEditableTarget(event.target)) {
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const text = event.clipboardData?.getData(CLIPBOARD_MIME) || event.clipboardData?.getData('text/plain') || ''
|
|
251
|
-
const payloadFromClipboard = text ? safeParseClipboard<TNodeData>(text) : null
|
|
252
|
-
if (!payloadFromClipboard) {
|
|
253
|
-
return
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
event.preventDefault()
|
|
257
|
-
pastePayload(payloadFromClipboard)
|
|
258
|
-
}, [enabled, isEditableTarget, pastePayload])
|
|
259
|
-
|
|
260
|
-
useEffect(() => {
|
|
261
|
-
if (!enabled) {
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
document.addEventListener('copy', handleCopy)
|
|
266
|
-
document.addEventListener('paste', handlePaste)
|
|
267
|
-
return () => {
|
|
268
|
-
document.removeEventListener('copy', handleCopy)
|
|
269
|
-
document.removeEventListener('paste', handlePaste)
|
|
270
|
-
}
|
|
271
|
-
}, [enabled, handleCopy, handlePaste])
|
|
272
|
-
}
|