@electerm/electerm-react 3.7.16 → 3.8.6
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/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
|
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import TreeExpander from './tree-expander'
|
|
2
|
+
import TreeListItem from './tree-list-item'
|
|
3
|
+
import TreeItemOp from './tree-item-op'
|
|
4
|
+
import { treeLevelIndent } from './tree-list-layout'
|
|
5
|
+
|
|
6
|
+
export default function TreeListRow (props) {
|
|
7
|
+
const {
|
|
8
|
+
row,
|
|
9
|
+
keyword,
|
|
10
|
+
expandedKeys,
|
|
11
|
+
activeItemId,
|
|
12
|
+
searchSelectedRowKey,
|
|
13
|
+
staticList,
|
|
14
|
+
leftSidebarWidth,
|
|
15
|
+
handleExpand,
|
|
16
|
+
handleUnExpand,
|
|
17
|
+
del,
|
|
18
|
+
openAll,
|
|
19
|
+
openMoveModal,
|
|
20
|
+
editItem,
|
|
21
|
+
addSubCat,
|
|
22
|
+
onSelect,
|
|
23
|
+
duplicateItem,
|
|
24
|
+
onDragStart,
|
|
25
|
+
onDrop,
|
|
26
|
+
onDragEnter,
|
|
27
|
+
onDragLeave,
|
|
28
|
+
onDragOver,
|
|
29
|
+
isHidden
|
|
30
|
+
} = props
|
|
31
|
+
const { item, isGroup, parentId, depth } = row
|
|
32
|
+
const groupHasChildren = Boolean(
|
|
33
|
+
item?.bookmarkIds?.length ||
|
|
34
|
+
item?.bookmarkGroupIds?.length
|
|
35
|
+
)
|
|
36
|
+
const isGroupExpanded = Boolean(keyword) || expandedKeys.includes(item.id)
|
|
37
|
+
const itemProps = {
|
|
38
|
+
item,
|
|
39
|
+
isGroup,
|
|
40
|
+
parentId,
|
|
41
|
+
leftSidebarWidth,
|
|
42
|
+
staticList,
|
|
43
|
+
selectedItemId: activeItemId,
|
|
44
|
+
searchSelected: searchSelectedRowKey === row.key,
|
|
45
|
+
del,
|
|
46
|
+
openAll,
|
|
47
|
+
openMoveModal,
|
|
48
|
+
editItem,
|
|
49
|
+
addSubCat,
|
|
50
|
+
onSelect,
|
|
51
|
+
duplicateItem,
|
|
52
|
+
onDragStart,
|
|
53
|
+
onDrop,
|
|
54
|
+
onDragEnter,
|
|
55
|
+
onDragLeave,
|
|
56
|
+
onDragOver,
|
|
57
|
+
keyword
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!isGroup) {
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
className={`tree-list-row${isHidden ? ' is-hidden' : ''}`}
|
|
64
|
+
style={{ paddingLeft: depth * treeLevelIndent }}
|
|
65
|
+
>
|
|
66
|
+
<TreeListItem {...itemProps} />
|
|
67
|
+
<TreeItemOp
|
|
68
|
+
item={item}
|
|
69
|
+
isGroup={isGroup}
|
|
70
|
+
staticList={staticList}
|
|
71
|
+
del={del}
|
|
72
|
+
openAll={openAll}
|
|
73
|
+
openMoveModal={openMoveModal}
|
|
74
|
+
editItem={editItem}
|
|
75
|
+
addSubCat={addSubCat}
|
|
76
|
+
duplicateItem={duplicateItem}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
className={`tree-list-row${isHidden ? ' is-hidden' : ''}`}
|
|
85
|
+
style={{ paddingLeft: Math.max(0, (depth - 1) * treeLevelIndent) }}
|
|
86
|
+
>
|
|
87
|
+
<div className='tree-list-row-group'>
|
|
88
|
+
<TreeExpander
|
|
89
|
+
level={parentId}
|
|
90
|
+
group={item}
|
|
91
|
+
hasChildren={groupHasChildren}
|
|
92
|
+
shouldOpen={isGroupExpanded}
|
|
93
|
+
onExpand={handleExpand}
|
|
94
|
+
onUnExpand={handleUnExpand}
|
|
95
|
+
/>
|
|
96
|
+
<TreeListItem {...itemProps} />
|
|
97
|
+
<TreeItemOp
|
|
98
|
+
item={item}
|
|
99
|
+
isGroup={isGroup}
|
|
100
|
+
staticList={staticList}
|
|
101
|
+
del={del}
|
|
102
|
+
openAll={openAll}
|
|
103
|
+
openMoveModal={openMoveModal}
|
|
104
|
+
editItem={editItem}
|
|
105
|
+
addSubCat={addSubCat}
|
|
106
|
+
duplicateItem={duplicateItem}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|