@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
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
.tree-control-btn
|
|
3
|
-
display
|
|
3
|
+
display inline-flex
|
|
4
|
+
align-items center
|
|
5
|
+
justify-content center
|
|
4
6
|
margin-left 3px
|
|
5
7
|
width 16px
|
|
6
8
|
line-height 30px
|
|
@@ -27,11 +29,6 @@
|
|
|
27
29
|
background #000
|
|
28
30
|
color #eee
|
|
29
31
|
cursor pointer
|
|
30
|
-
&:hover
|
|
31
|
-
.tree-control-btn
|
|
32
|
-
display inline-block
|
|
33
|
-
vertical-align middle
|
|
34
|
-
line-height 26px
|
|
35
32
|
&.item-dragover-top
|
|
36
33
|
border 2px solid var(--primary)
|
|
37
34
|
.tree-item-title
|
|
@@ -102,3 +99,49 @@
|
|
|
102
99
|
flex 1
|
|
103
100
|
overflow-y auto
|
|
104
101
|
min-height 0
|
|
102
|
+
position relative
|
|
103
|
+
|
|
104
|
+
.tree-list-virtual-spacer
|
|
105
|
+
position relative
|
|
106
|
+
width 100%
|
|
107
|
+
|
|
108
|
+
.tree-list-virtual-row
|
|
109
|
+
position absolute
|
|
110
|
+
left 0
|
|
111
|
+
right 0
|
|
112
|
+
|
|
113
|
+
.tree-list-row
|
|
114
|
+
box-sizing border-box
|
|
115
|
+
width 100%
|
|
116
|
+
position relative
|
|
117
|
+
&.is-hidden
|
|
118
|
+
visibility hidden
|
|
119
|
+
&:hover
|
|
120
|
+
.tree-item-title
|
|
121
|
+
padding-right 120px
|
|
122
|
+
.tree-item-op-wrap
|
|
123
|
+
opacity 1
|
|
124
|
+
pointer-events auto
|
|
125
|
+
|
|
126
|
+
.tree-list-row-group
|
|
127
|
+
position relative
|
|
128
|
+
padding-left 12px
|
|
129
|
+
|
|
130
|
+
.tree-list-editor-overlay
|
|
131
|
+
position absolute
|
|
132
|
+
right 4px
|
|
133
|
+
z-index 4
|
|
134
|
+
|
|
135
|
+
.tree-item-op-wrap
|
|
136
|
+
position absolute
|
|
137
|
+
top 0
|
|
138
|
+
bottom 0
|
|
139
|
+
right 4px
|
|
140
|
+
display flex
|
|
141
|
+
align-items center
|
|
142
|
+
z-index 3
|
|
143
|
+
opacity 0
|
|
144
|
+
pointer-events none
|
|
145
|
+
&.is-active
|
|
146
|
+
opacity 1
|
|
147
|
+
pointer-events auto
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
function getMetrics (container, rowHeight) {
|
|
4
|
+
return {
|
|
5
|
+
scrollTop: container?.scrollTop || 0,
|
|
6
|
+
viewportHeight: container?.clientHeight || rowHeight * 12
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function VirtualTreeList (props) {
|
|
11
|
+
const {
|
|
12
|
+
items,
|
|
13
|
+
rowHeight,
|
|
14
|
+
containerRef,
|
|
15
|
+
overscan = 8,
|
|
16
|
+
renderItem,
|
|
17
|
+
insertionGap
|
|
18
|
+
} = props
|
|
19
|
+
const frameRef = useRef(0)
|
|
20
|
+
const [metrics, setMetrics] = useState(() => {
|
|
21
|
+
return getMetrics(containerRef.current, rowHeight)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const container = containerRef.current
|
|
26
|
+
if (!container) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const update = () => {
|
|
31
|
+
setMetrics(prev => {
|
|
32
|
+
const next = getMetrics(container, rowHeight)
|
|
33
|
+
return prev.scrollTop === next.scrollTop &&
|
|
34
|
+
prev.viewportHeight === next.viewportHeight
|
|
35
|
+
? prev
|
|
36
|
+
: next
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const scheduleUpdate = () => {
|
|
41
|
+
window.cancelAnimationFrame(frameRef.current)
|
|
42
|
+
frameRef.current = window.requestAnimationFrame(update)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
update()
|
|
46
|
+
container.addEventListener('scroll', scheduleUpdate, { passive: true })
|
|
47
|
+
|
|
48
|
+
let resizeObserver
|
|
49
|
+
if (window.ResizeObserver) {
|
|
50
|
+
resizeObserver = new window.ResizeObserver(scheduleUpdate)
|
|
51
|
+
resizeObserver.observe(container)
|
|
52
|
+
}
|
|
53
|
+
window.addEventListener('resize', scheduleUpdate)
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
window.cancelAnimationFrame(frameRef.current)
|
|
57
|
+
container.removeEventListener('scroll', scheduleUpdate)
|
|
58
|
+
resizeObserver?.disconnect()
|
|
59
|
+
window.removeEventListener('resize', scheduleUpdate)
|
|
60
|
+
}
|
|
61
|
+
}, [containerRef, rowHeight])
|
|
62
|
+
|
|
63
|
+
const gapIndex = insertionGap?.index ?? Number.POSITIVE_INFINITY
|
|
64
|
+
const gapHeight = insertionGap?.height || 0
|
|
65
|
+
const gapTop = Number.isFinite(gapIndex)
|
|
66
|
+
? gapIndex * rowHeight
|
|
67
|
+
: Number.POSITIVE_INFINITY
|
|
68
|
+
const { scrollTop, viewportHeight } = metrics
|
|
69
|
+
const adjustedScrollTop = scrollTop > gapTop
|
|
70
|
+
? scrollTop - gapHeight
|
|
71
|
+
: scrollTop
|
|
72
|
+
const adjustedViewportBottom = scrollTop + viewportHeight > gapTop
|
|
73
|
+
? scrollTop + viewportHeight - gapHeight
|
|
74
|
+
: scrollTop + viewportHeight
|
|
75
|
+
const startIndex = Math.max(0, Math.floor(adjustedScrollTop / rowHeight) - overscan)
|
|
76
|
+
const endIndex = Math.min(
|
|
77
|
+
items.length,
|
|
78
|
+
Math.ceil(adjustedViewportBottom / rowHeight) + overscan
|
|
79
|
+
)
|
|
80
|
+
const visibleItems = items.slice(startIndex, endIndex)
|
|
81
|
+
const totalHeight = items.length * rowHeight + gapHeight
|
|
82
|
+
|
|
83
|
+
if (!items.length && !gapHeight) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
className='tree-list-virtual-spacer'
|
|
90
|
+
style={{ height: totalHeight }}
|
|
91
|
+
>
|
|
92
|
+
{
|
|
93
|
+
visibleItems.map((item, offset) => {
|
|
94
|
+
const index = startIndex + offset
|
|
95
|
+
const top = index * rowHeight + (index >= gapIndex ? gapHeight : 0)
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
key={item.key}
|
|
99
|
+
className='tree-list-virtual-row'
|
|
100
|
+
style={{
|
|
101
|
+
top,
|
|
102
|
+
height: rowHeight
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{renderItem(item, index)}
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|