@electerm/electerm-react 3.7.18 → 3.8.8
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/client/components/file-transfer/transfer.jsx +17 -6
- package/client/components/setting-panel/setting-modal.jsx +1 -0
- package/client/components/sftp/file-info-modal.jsx +50 -0
- package/client/components/sftp/file-item.jsx +7 -7
- package/client/components/tree-list/tree-expander.jsx +9 -20
- package/client/components/tree-list/tree-item-op.jsx +156 -0
- package/client/components/tree-list/tree-list-editor-overlay.jsx +47 -0
- package/client/components/tree-list/tree-list-item.jsx +6 -170
- package/client/components/tree-list/tree-list-layout.js +3 -0
- package/client/components/tree-list/tree-list-row.jsx +111 -0
- package/client/components/tree-list/tree-list-rows.js +107 -0
- package/client/components/tree-list/tree-list.jsx +226 -314
- package/client/components/tree-list/tree-list.styl +49 -6
- package/client/components/tree-list/virtual-tree-list.jsx +112 -0
- package/package.json +1 -1
|
@@ -155,7 +155,7 @@ export default class TransportAction extends Component {
|
|
|
155
155
|
const finishTime = Date.now()
|
|
156
156
|
if (!config.disableTransferHistory) {
|
|
157
157
|
const fromFile = transfer.fromFile || this.fromFile
|
|
158
|
-
const size = update.size
|
|
158
|
+
const size = update.size ?? update.transferred ?? fromFile.size
|
|
159
159
|
const r = copy(transfer)
|
|
160
160
|
assign(r, {
|
|
161
161
|
finishTime,
|
|
@@ -182,20 +182,27 @@ export default class TransportAction extends Component {
|
|
|
182
182
|
return
|
|
183
183
|
}
|
|
184
184
|
const { transfer } = this.props
|
|
185
|
+
const fromFile = transfer.fromFile || this.fromFile || {}
|
|
186
|
+
const transferredValue = typeof transferred === 'object' && transferred !== null
|
|
187
|
+
? transferred.transferred
|
|
188
|
+
: transferred
|
|
189
|
+
const total = typeof transferred === 'object' && transferred !== null
|
|
190
|
+
? (transferred.total || fromFile.size || 0)
|
|
191
|
+
: (fromFile.size || 0)
|
|
185
192
|
const up = {}
|
|
186
|
-
const total = transfer.fromFile.size
|
|
187
193
|
let percent = total === 0
|
|
188
194
|
? 100
|
|
189
|
-
: Math.floor(100 *
|
|
195
|
+
: Math.floor(100 * transferredValue / total)
|
|
190
196
|
percent = percent >= 100 ? 100 : percent
|
|
197
|
+
this.total = total
|
|
191
198
|
up.percent = percent
|
|
192
199
|
up.status = 'active'
|
|
193
|
-
up.transferred =
|
|
200
|
+
up.transferred = transferredValue
|
|
194
201
|
up.startTime = this.startTime
|
|
195
|
-
up.speed = format(
|
|
202
|
+
up.speed = format(transferredValue, up.startTime)
|
|
196
203
|
assign(
|
|
197
204
|
up,
|
|
198
|
-
computeLeftTime(
|
|
205
|
+
computeLeftTime(transferredValue, total, up.startTime)
|
|
199
206
|
)
|
|
200
207
|
up.passedTime = computePassedTime(up.startTime)
|
|
201
208
|
this.update(up)
|
|
@@ -290,6 +297,7 @@ export default class TransportAction extends Component {
|
|
|
290
297
|
this.transport = await sftp[transferType]({
|
|
291
298
|
remotePath,
|
|
292
299
|
localPath,
|
|
300
|
+
isDirectory: !!fromFile.isDirectory,
|
|
293
301
|
options: { mode },
|
|
294
302
|
onData: this.onData,
|
|
295
303
|
onError: this.onError,
|
|
@@ -528,6 +536,9 @@ export default class TransportAction extends Component {
|
|
|
528
536
|
}
|
|
529
537
|
if (zip) {
|
|
530
538
|
return this.zipTransferFolder()
|
|
539
|
+
}
|
|
540
|
+
if (!this.isFtp) {
|
|
541
|
+
return this.transferFile()
|
|
531
542
|
} else {
|
|
532
543
|
await this.transferFolderRecursive()
|
|
533
544
|
}
|
|
@@ -46,6 +46,11 @@ export default class FileMode extends React.PureComponent {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
showFileInfoModal (data) {
|
|
49
|
+
if (data.pid !== this.remoteExecPid) {
|
|
50
|
+
this.remoteExecPid = data.pid
|
|
51
|
+
this.remoteExecPlatform = null
|
|
52
|
+
this.remoteExecPlatformPromise = null
|
|
53
|
+
}
|
|
49
54
|
this.setStateProxy({
|
|
50
55
|
...data,
|
|
51
56
|
size: 0,
|
|
@@ -115,6 +120,47 @@ export default class FileMode extends React.PureComponent {
|
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
122
|
|
|
123
|
+
escapePowerShellPath = (value) => {
|
|
124
|
+
return String(value).replace(/'/g, "''")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
normalizeRemoteWindowsPath = (value) => {
|
|
128
|
+
return String(value).replace(/^\/([a-zA-Z]:)/, '$1')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getRemoteExecPlatform = async () => {
|
|
132
|
+
if (this.remoteExecPid === this.state.pid && this.remoteExecPlatform) {
|
|
133
|
+
return this.remoteExecPlatform
|
|
134
|
+
}
|
|
135
|
+
if (this.remoteExecPid !== this.state.pid) {
|
|
136
|
+
this.remoteExecPid = this.state.pid
|
|
137
|
+
this.remoteExecPlatform = null
|
|
138
|
+
this.remoteExecPlatformPromise = null
|
|
139
|
+
}
|
|
140
|
+
if (!this.remoteExecPlatformPromise) {
|
|
141
|
+
this.remoteExecPlatformPromise = runCmd(this.state.pid, 'cmd.exe /d /s /c ver')
|
|
142
|
+
.then(output => {
|
|
143
|
+
return String(output).toLowerCase().includes('windows')
|
|
144
|
+
? 'windows'
|
|
145
|
+
: 'posix'
|
|
146
|
+
})
|
|
147
|
+
.catch(() => 'posix')
|
|
148
|
+
.then(platform => {
|
|
149
|
+
this.remoteExecPlatform = platform
|
|
150
|
+
return platform
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
return this.remoteExecPlatformPromise
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
calcRemoteWin = async (folder) => {
|
|
157
|
+
const winFolder = this.normalizeRemoteWindowsPath(folder)
|
|
158
|
+
const escapedFolder = this.escapePowerShellPath(winFolder)
|
|
159
|
+
const cmd = `powershell.exe -NoLogo -NonInteractive -NoProfile -Command "$result = Get-ChildItem -LiteralPath '${escapedFolder}' -Recurse -File | Measure-Object -Property Length -Sum; if ($null -eq $result.Sum) { 0 } else { [int64]$result.Sum }"`
|
|
160
|
+
const result = await runCmd(this.state.pid, cmd).catch(window.store.onError)
|
|
161
|
+
return filesize(parseInt(String(result || '').trim(), 10) || 0)
|
|
162
|
+
}
|
|
163
|
+
|
|
118
164
|
calcLocal = async (folder) => {
|
|
119
165
|
const cmd = isWin
|
|
120
166
|
? `Get-ChildItem -Recurse '${folder}' | Measure-Object -Property Length -Sum`
|
|
@@ -125,6 +171,10 @@ export default class FileMode extends React.PureComponent {
|
|
|
125
171
|
}
|
|
126
172
|
|
|
127
173
|
calcRemote = async (folder) => {
|
|
174
|
+
const platform = await this.getRemoteExecPlatform()
|
|
175
|
+
if (platform === 'windows') {
|
|
176
|
+
return this.calcRemoteWin(folder)
|
|
177
|
+
}
|
|
128
178
|
const cmd = `du -sh '${folder}'`
|
|
129
179
|
const r = await runCmd(
|
|
130
180
|
this.state.pid,
|
|
@@ -1037,13 +1037,13 @@ export default class FileSection extends React.Component {
|
|
|
1037
1037
|
icon: iconType,
|
|
1038
1038
|
text: transferText
|
|
1039
1039
|
})
|
|
1040
|
-
if (isDirectory && !this.props.isFtp) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1040
|
+
// if (isDirectory && !this.props.isFtp) {
|
|
1041
|
+
// res.push({
|
|
1042
|
+
// func: 'zipAndTransfer',
|
|
1043
|
+
// icon: 'FileZipOutlined',
|
|
1044
|
+
// text: e('compressAndTransfer')
|
|
1045
|
+
// })
|
|
1046
|
+
// }
|
|
1047
1047
|
}
|
|
1048
1048
|
if (!isDirectory && isRealFile && isLocal) {
|
|
1049
1049
|
res.push({
|
|
@@ -5,41 +5,30 @@ import {
|
|
|
5
5
|
CaretRightOutlined
|
|
6
6
|
} from '@ant-design/icons'
|
|
7
7
|
|
|
8
|
-
function hasChildren (group) {
|
|
9
|
-
return Boolean(
|
|
10
|
-
group?.bookmarkIds?.length ||
|
|
11
|
-
group?.bookmarkGroupIds?.length
|
|
12
|
-
)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function isOpen (props) {
|
|
16
|
-
return Boolean(props.keyword) || props.expandedKeys.includes(props.group.id)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
8
|
function areEqual (prevProps, nextProps) {
|
|
20
9
|
return prevProps.group?.id === nextProps.group?.id &&
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
isOpen(prevProps) === isOpen(nextProps)
|
|
10
|
+
prevProps.hasChildren === nextProps.hasChildren &&
|
|
11
|
+
prevProps.shouldOpen === nextProps.shouldOpen
|
|
24
12
|
}
|
|
25
13
|
|
|
26
14
|
function TreeExpander (props) {
|
|
15
|
+
const { group } = props
|
|
16
|
+
|
|
27
17
|
function onExpand (e) {
|
|
28
18
|
e.stopPropagation()
|
|
29
19
|
props.onExpand(group)
|
|
30
20
|
}
|
|
21
|
+
|
|
31
22
|
function onUnExpand (e) {
|
|
32
23
|
e.stopPropagation()
|
|
33
24
|
props.onUnExpand(group)
|
|
34
25
|
}
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
!group?.bookmarkIds?.length &&
|
|
38
|
-
!group?.bookmarkGroupIds?.length
|
|
39
|
-
) {
|
|
26
|
+
|
|
27
|
+
if (!props.hasChildren) {
|
|
40
28
|
return null
|
|
41
29
|
}
|
|
42
|
-
|
|
30
|
+
|
|
31
|
+
const shouldOpen = props.shouldOpen
|
|
43
32
|
const Icon = shouldOpen
|
|
44
33
|
? CaretDownOutlined
|
|
45
34
|
: CaretRightOutlined
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloseOutlined,
|
|
3
|
+
CopyOutlined,
|
|
4
|
+
EditOutlined,
|
|
5
|
+
FolderAddOutlined,
|
|
6
|
+
FolderOpenOutlined,
|
|
7
|
+
RightSquareOutlined
|
|
8
|
+
} from '@ant-design/icons'
|
|
9
|
+
import {
|
|
10
|
+
Popconfirm,
|
|
11
|
+
Tooltip
|
|
12
|
+
} from 'antd'
|
|
13
|
+
import { useState } from 'react'
|
|
14
|
+
import {
|
|
15
|
+
defaultBookmarkGroupId
|
|
16
|
+
} from '../../common/constants'
|
|
17
|
+
|
|
18
|
+
const e = window.translate
|
|
19
|
+
|
|
20
|
+
export default function TreeItemOp (props) {
|
|
21
|
+
const [pendingDeleteItem, setPendingDeleteItem] = useState(null)
|
|
22
|
+
const {
|
|
23
|
+
item,
|
|
24
|
+
isGroup,
|
|
25
|
+
staticList,
|
|
26
|
+
del,
|
|
27
|
+
openAll,
|
|
28
|
+
openMoveModal,
|
|
29
|
+
editItem,
|
|
30
|
+
addSubCat,
|
|
31
|
+
duplicateItem
|
|
32
|
+
} = props
|
|
33
|
+
|
|
34
|
+
if (!item) {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isDefaultGroup = item.id === defaultBookmarkGroupId
|
|
39
|
+
const canShowSharedOps = !staticList && !isDefaultGroup
|
|
40
|
+
|
|
41
|
+
const handleDel = (event) => {
|
|
42
|
+
del(pendingDeleteItem || item, event)
|
|
43
|
+
setPendingDeleteItem(null)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const handleDeleteOpenChange = (open) => {
|
|
47
|
+
setPendingDeleteItem(open ? item : null)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleOpenMoveModal = (event) => {
|
|
51
|
+
openMoveModal(event, item, isGroup)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const handleEditItem = (event) => {
|
|
55
|
+
editItem(event, item, isGroup)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const handleAddSubCat = (event) => {
|
|
59
|
+
addSubCat(event, item)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const handleDuplicateItem = (event) => {
|
|
63
|
+
duplicateItem(event, item)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const handleOpenAll = () => {
|
|
67
|
+
openAll(item)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const buttons = []
|
|
71
|
+
|
|
72
|
+
if (isGroup && !staticList) {
|
|
73
|
+
buttons.push(
|
|
74
|
+
<FolderAddOutlined
|
|
75
|
+
key='new-tree'
|
|
76
|
+
title={`${e('new')} ${e('bookmarkCategory')}`}
|
|
77
|
+
onClick={handleAddSubCat}
|
|
78
|
+
className='pointer tree-control-btn'
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isGroup && staticList) {
|
|
84
|
+
buttons.push(
|
|
85
|
+
<Tooltip title={e('openAll')} key='open-all-tooltip'>
|
|
86
|
+
<FolderOpenOutlined
|
|
87
|
+
key='open-all-tree'
|
|
88
|
+
onClick={handleOpenAll}
|
|
89
|
+
className='pointer open-all-icon tree-control-btn'
|
|
90
|
+
/>
|
|
91
|
+
</Tooltip>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!isGroup && !staticList && item.id) {
|
|
96
|
+
buttons.push(
|
|
97
|
+
<CopyOutlined
|
|
98
|
+
key='duplicate-tree'
|
|
99
|
+
title={e('duplicate')}
|
|
100
|
+
className='pointer tree-control-btn'
|
|
101
|
+
onClick={handleDuplicateItem}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (canShowSharedOps) {
|
|
107
|
+
buttons.push(
|
|
108
|
+
<RightSquareOutlined
|
|
109
|
+
key='move-tree'
|
|
110
|
+
className='pointer tree-control-btn'
|
|
111
|
+
onClick={handleOpenMoveModal}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!isDefaultGroup && !staticList) {
|
|
117
|
+
buttons.push(
|
|
118
|
+
<Popconfirm
|
|
119
|
+
key='delete-tree'
|
|
120
|
+
title={e('del') + '?'}
|
|
121
|
+
onConfirm={handleDel}
|
|
122
|
+
onCancel={() => setPendingDeleteItem(null)}
|
|
123
|
+
onOpenChange={handleDeleteOpenChange}
|
|
124
|
+
okText={e('del')}
|
|
125
|
+
cancelText={e('cancel')}
|
|
126
|
+
placement='top'
|
|
127
|
+
>
|
|
128
|
+
<CloseOutlined title={e('del')} className='pointer tree-control-btn' />
|
|
129
|
+
</Popconfirm>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const shouldShowEdit = !((staticList && isGroup) || (!staticList && !isGroup))
|
|
134
|
+
if (shouldShowEdit) {
|
|
135
|
+
buttons.push(
|
|
136
|
+
<EditOutlined
|
|
137
|
+
title={e('edit')}
|
|
138
|
+
key='edit-tree'
|
|
139
|
+
onClick={handleEditItem}
|
|
140
|
+
className='pointer edit-icon tree-control-btn'
|
|
141
|
+
/>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!buttons.length) {
|
|
146
|
+
return null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<div
|
|
151
|
+
className={`tree-item-op-wrap${pendingDeleteItem ? ' is-active' : ''}`}
|
|
152
|
+
>
|
|
153
|
+
{buttons}
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckOutlined,
|
|
3
|
+
CloseOutlined
|
|
4
|
+
} from '@ant-design/icons'
|
|
5
|
+
import InputAutoFocus from '../common/input-auto-focus'
|
|
6
|
+
import { CategoryColorPicker } from './category-color-picker.jsx'
|
|
7
|
+
import { getRandomDefaultColor } from '../../common/rand-hex-color.js'
|
|
8
|
+
import { treeEditorRowHeight } from './tree-list-layout'
|
|
9
|
+
|
|
10
|
+
export default function TreeListEditorOverlay ({ editor }) {
|
|
11
|
+
if (!editor) {
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const confirm = (
|
|
16
|
+
<span>
|
|
17
|
+
<CheckOutlined className='pointer' onClick={editor.handleSubmit} />
|
|
18
|
+
<CloseOutlined className='mg1l pointer' onClick={editor.handleCancel} />
|
|
19
|
+
</span>
|
|
20
|
+
)
|
|
21
|
+
const colorPicker = (
|
|
22
|
+
<CategoryColorPicker
|
|
23
|
+
value={editor.color || getRandomDefaultColor()}
|
|
24
|
+
onChange={editor.handleColorChange}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
className='tree-list-editor-overlay'
|
|
31
|
+
style={{
|
|
32
|
+
top: editor.top,
|
|
33
|
+
left: editor.left,
|
|
34
|
+
height: treeEditorRowHeight
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<InputAutoFocus
|
|
38
|
+
value={editor.title}
|
|
39
|
+
onChange={editor.handleTitleChange}
|
|
40
|
+
onPressEnter={editor.handleSubmit}
|
|
41
|
+
prefix={colorPicker}
|
|
42
|
+
suffix={confirm}
|
|
43
|
+
selectall={editor.selectall}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -2,30 +2,12 @@
|
|
|
2
2
|
* tree list for bookmarks
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { memo
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
CloseOutlined,
|
|
9
|
-
CopyOutlined,
|
|
10
|
-
EditOutlined,
|
|
11
|
-
FolderAddOutlined,
|
|
12
|
-
FolderOpenOutlined,
|
|
13
|
-
RightSquareOutlined
|
|
14
|
-
} from '@ant-design/icons'
|
|
15
|
-
import {
|
|
16
|
-
Popconfirm,
|
|
17
|
-
Tooltip
|
|
18
|
-
} from 'antd'
|
|
5
|
+
import { memo } from 'react'
|
|
19
6
|
import createName, { createTitleTag } from '../../common/create-title'
|
|
20
7
|
import classnames from 'classnames'
|
|
21
|
-
import {
|
|
22
|
-
defaultBookmarkGroupId
|
|
23
|
-
} from '../../common/constants'
|
|
24
8
|
import highlight from '../common/highlight'
|
|
25
9
|
import uid from '../../common/uid'
|
|
26
10
|
|
|
27
|
-
const e = window.translate
|
|
28
|
-
|
|
29
11
|
function getItemLabel (item, isGroup) {
|
|
30
12
|
return isGroup
|
|
31
13
|
? item?.title || ''
|
|
@@ -35,12 +17,15 @@ function getItemLabel (item, isGroup) {
|
|
|
35
17
|
function areEqual (prevProps, nextProps) {
|
|
36
18
|
const prevSelected = prevProps.selectedItemId === prevProps.item.id
|
|
37
19
|
const nextSelected = nextProps.selectedItemId === nextProps.item.id
|
|
20
|
+
const prevSearchSelected = Boolean(prevProps.searchSelected)
|
|
21
|
+
const nextSearchSelected = Boolean(nextProps.searchSelected)
|
|
38
22
|
|
|
39
23
|
return prevProps.isGroup === nextProps.isGroup &&
|
|
40
24
|
prevProps.parentId === nextProps.parentId &&
|
|
41
25
|
prevProps.staticList === nextProps.staticList &&
|
|
42
26
|
prevProps.keyword === nextProps.keyword &&
|
|
43
27
|
prevSelected === nextSelected &&
|
|
28
|
+
prevSearchSelected === nextSearchSelected &&
|
|
44
29
|
prevProps.item.id === nextProps.item.id &&
|
|
45
30
|
prevProps.item.level === nextProps.item.level &&
|
|
46
31
|
prevProps.item.color === nextProps.item.color &&
|
|
@@ -49,145 +34,10 @@ function areEqual (prevProps, nextProps) {
|
|
|
49
34
|
}
|
|
50
35
|
|
|
51
36
|
function TreeListItem (props) {
|
|
52
|
-
const [hovered, setHovered] = useState(false)
|
|
53
|
-
|
|
54
|
-
const handleDel = (e) => {
|
|
55
|
-
props.del(props.item, e)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const renderDelBtn = (item) => {
|
|
59
|
-
if (props.item.id === defaultBookmarkGroupId || props.staticList) {
|
|
60
|
-
return null
|
|
61
|
-
}
|
|
62
|
-
return (
|
|
63
|
-
<Popconfirm
|
|
64
|
-
title={e('del') + '?'}
|
|
65
|
-
onConfirm={handleDel}
|
|
66
|
-
okText={e('del')}
|
|
67
|
-
cancelText={e('cancel')}
|
|
68
|
-
placement='top'
|
|
69
|
-
>
|
|
70
|
-
<CloseOutlined title={e('del')} className='pointer tree-control-btn' />
|
|
71
|
-
</Popconfirm>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const renderOperationBtn = (item, isGroup) => {
|
|
76
|
-
if (props.staticList || props.item.id === defaultBookmarkGroupId) {
|
|
77
|
-
return null
|
|
78
|
-
}
|
|
79
|
-
return (
|
|
80
|
-
<RightSquareOutlined
|
|
81
|
-
className='pointer tree-control-btn'
|
|
82
|
-
onClick={openMoveModal}
|
|
83
|
-
/>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const handleOpenAll = () => {
|
|
88
|
-
props.openAll(props.item)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const openMoveModal = (e) => {
|
|
92
|
-
props.openMoveModal(e, props.item, props.isGroup)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const handleEditItem = (e) => {
|
|
96
|
-
props.editItem(e, props.item, props.isGroup)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const handleAddSubCat = (e) => {
|
|
100
|
-
props.addSubCat(e, props.item)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const renderAddNewSubGroupBtn = () => {
|
|
104
|
-
if (props.staticList) {
|
|
105
|
-
return null
|
|
106
|
-
}
|
|
107
|
-
return (
|
|
108
|
-
<FolderAddOutlined
|
|
109
|
-
key='new-tree'
|
|
110
|
-
title={`${e('new')} ${e('bookmarkCategory')}`}
|
|
111
|
-
onClick={handleAddSubCat}
|
|
112
|
-
className='pointer tree-control-btn'
|
|
113
|
-
/>
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const renderEditBtn = () => {
|
|
118
|
-
const {
|
|
119
|
-
isGroup, staticList
|
|
120
|
-
} = props
|
|
121
|
-
if (
|
|
122
|
-
(staticList && isGroup) ||
|
|
123
|
-
(!staticList && !isGroup)
|
|
124
|
-
) {
|
|
125
|
-
return null
|
|
126
|
-
}
|
|
127
|
-
return (
|
|
128
|
-
<EditOutlined
|
|
129
|
-
title={e('edit')}
|
|
130
|
-
key='edit-tree'
|
|
131
|
-
onClick={handleEditItem}
|
|
132
|
-
className='pointer edit-icon tree-control-btn'
|
|
133
|
-
/>
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
37
|
const onSelect = (e) => {
|
|
138
38
|
props.onSelect(e)
|
|
139
39
|
}
|
|
140
40
|
|
|
141
|
-
const renderOpenAll = () => {
|
|
142
|
-
const {
|
|
143
|
-
staticList,
|
|
144
|
-
isGroup
|
|
145
|
-
} = props
|
|
146
|
-
if (
|
|
147
|
-
(staticList && !isGroup) ||
|
|
148
|
-
!staticList
|
|
149
|
-
) {
|
|
150
|
-
return null
|
|
151
|
-
}
|
|
152
|
-
return (
|
|
153
|
-
<Tooltip title={e('openAll')}>
|
|
154
|
-
<FolderOpenOutlined
|
|
155
|
-
key='open-all-tree'
|
|
156
|
-
onClick={handleOpenAll}
|
|
157
|
-
className='pointer open-all-icon tree-control-btn'
|
|
158
|
-
/>
|
|
159
|
-
</Tooltip>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const handleDuplicateItem = (e) => {
|
|
164
|
-
props.duplicateItem(e, props.item)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const renderDuplicateBtn = () => {
|
|
168
|
-
const {
|
|
169
|
-
item,
|
|
170
|
-
staticList
|
|
171
|
-
} = props
|
|
172
|
-
if (!item.id || staticList) {
|
|
173
|
-
return null
|
|
174
|
-
}
|
|
175
|
-
return (
|
|
176
|
-
<CopyOutlined
|
|
177
|
-
title={e('duplicate')}
|
|
178
|
-
className='pointer tree-control-btn'
|
|
179
|
-
onClick={handleDuplicateItem}
|
|
180
|
-
/>
|
|
181
|
-
)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const renderGroupBtns = () => {
|
|
185
|
-
return [
|
|
186
|
-
renderAddNewSubGroupBtn(),
|
|
187
|
-
renderOpenAll()
|
|
188
|
-
]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
41
|
const onDragOver = e => {
|
|
192
42
|
props.onDragOver(e)
|
|
193
43
|
}
|
|
@@ -215,7 +65,8 @@ function TreeListItem (props) {
|
|
|
215
65
|
} = props
|
|
216
66
|
const cls = classnames(
|
|
217
67
|
{
|
|
218
|
-
selected: selectedItemId === item.id
|
|
68
|
+
selected: selectedItemId === item.id,
|
|
69
|
+
'search-selected': props.searchSelected
|
|
219
70
|
},
|
|
220
71
|
'tree-item',
|
|
221
72
|
{
|
|
@@ -251,8 +102,6 @@ function TreeListItem (props) {
|
|
|
251
102
|
'data-item-id': item.id,
|
|
252
103
|
'data-parent-id': props.parentId,
|
|
253
104
|
'data-is-group': isGroup ? 'true' : 'false',
|
|
254
|
-
onMouseEnter: () => setHovered(true),
|
|
255
|
-
onMouseLeave: () => setHovered(false),
|
|
256
105
|
onDragOver,
|
|
257
106
|
onDragStart,
|
|
258
107
|
onDragEnter,
|
|
@@ -277,19 +126,6 @@ function TreeListItem (props) {
|
|
|
277
126
|
>
|
|
278
127
|
{colorTag}{tag}{titleHighlight}
|
|
279
128
|
</div>
|
|
280
|
-
{
|
|
281
|
-
hovered && isGroup
|
|
282
|
-
? renderGroupBtns()
|
|
283
|
-
: null
|
|
284
|
-
}
|
|
285
|
-
{
|
|
286
|
-
hovered && !isGroup
|
|
287
|
-
? renderDuplicateBtn()
|
|
288
|
-
: null
|
|
289
|
-
}
|
|
290
|
-
{hovered ? renderOperationBtn() : null}
|
|
291
|
-
{hovered ? renderDelBtn() : null}
|
|
292
|
-
{hovered ? renderEditBtn() : null}
|
|
293
129
|
</div>
|
|
294
130
|
)
|
|
295
131
|
}
|