@electerm/electerm-react 2.3.18 → 2.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/client/common/has-active-input.js +10 -0
- package/client/components/file-transfer/transfer.styl +2 -1
- package/client/components/sftp/file-item.jsx +6 -6
- package/client/components/sftp/sftp-entry.jsx +18 -4
- package/client/components/sftp/sftp.styl +4 -3
- package/client/components/sidebar/index.jsx +6 -0
- package/client/components/sidebar/sidebar.styl +2 -2
- package/client/components/sys-menu/sub-tab-menu.jsx +2 -2
- package/client/components/sys-menu/sys-menu.styl +4 -2
- package/client/components/tabs/add-btn.jsx +258 -0
- package/client/components/tabs/add-btn.styl +12 -0
- package/client/components/tabs/index.jsx +8 -56
- package/client/components/tabs/tabs.styl +5 -4
- package/client/components/tree-list/tree-expander.jsx +4 -2
- package/client/css/basic.styl +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default function hasActiveInput (className = 'ant-input-search') {
|
|
2
|
+
const activeElement = document.activeElement
|
|
3
|
+
const isInput = activeElement && (
|
|
4
|
+
activeElement.tagName === 'INPUT' ||
|
|
5
|
+
activeElement.tagName === 'TEXTAREA'
|
|
6
|
+
)
|
|
7
|
+
const hasClass = className ? activeElement.classList.contains(className) : true
|
|
8
|
+
const hasInputDropDown = document.querySelector('.ant-dropdown:not(.ant-dropdown-hidden)')
|
|
9
|
+
return (isInput && hasClass) || hasInputDropDown
|
|
10
|
+
}
|
|
@@ -119,7 +119,7 @@ export default class FileSection extends React.Component {
|
|
|
119
119
|
|
|
120
120
|
onCopy = (targetFiles, isCut) => {
|
|
121
121
|
const { file } = this.state
|
|
122
|
-
const selected = this.isSelected(file)
|
|
122
|
+
const selected = this.isSelected(file.id)
|
|
123
123
|
const files = targetFiles ||
|
|
124
124
|
(
|
|
125
125
|
selected
|
|
@@ -138,7 +138,7 @@ export default class FileSection extends React.Component {
|
|
|
138
138
|
|
|
139
139
|
onCopyPath = (targetFiles) => {
|
|
140
140
|
const { file } = this.state
|
|
141
|
-
const selected = this.isSelected(file)
|
|
141
|
+
const selected = this.isSelected(file.id)
|
|
142
142
|
const files = targetFiles ||
|
|
143
143
|
(
|
|
144
144
|
selected
|
|
@@ -339,7 +339,7 @@ export default class FileSection extends React.Component {
|
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
transferDrop = (fromFiles, toFile, operation) => {
|
|
342
|
-
const files = this.isSelected(fromFiles[0])
|
|
342
|
+
const files = this.isSelected(fromFiles[0]?.id)
|
|
343
343
|
? this.props.getSelectedFiles()
|
|
344
344
|
: fromFiles
|
|
345
345
|
return this.doTransferSelected(
|
|
@@ -351,8 +351,8 @@ export default class FileSection extends React.Component {
|
|
|
351
351
|
)
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
isSelected =
|
|
355
|
-
return this.props.selectedFiles.has(
|
|
354
|
+
isSelected = (fileId = '') => {
|
|
355
|
+
return this.props.selectedFiles.has(fileId)
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
doRename = () => {
|
|
@@ -872,7 +872,7 @@ export default class FileSection extends React.Component {
|
|
|
872
872
|
}
|
|
873
873
|
|
|
874
874
|
handleContextMenuCapture = (e) => {
|
|
875
|
-
if (!this.isSelected(this.state.file)) {
|
|
875
|
+
if (!this.isSelected(this.state.file.id)) {
|
|
876
876
|
this.onClick(e)
|
|
877
877
|
}
|
|
878
878
|
this.contextMenuPosition = {
|
|
@@ -27,7 +27,7 @@ import fs from '../../common/fs'
|
|
|
27
27
|
import ListTable from './list-table-ui'
|
|
28
28
|
import deepCopy from 'json-deep-copy'
|
|
29
29
|
import isValidPath from '../../common/is-valid-path'
|
|
30
|
-
|
|
30
|
+
import { LoadingOutlined } from '@ant-design/icons'
|
|
31
31
|
import * as owner from './owner-list'
|
|
32
32
|
import AddressBar from './address-bar'
|
|
33
33
|
import getProxy from '../../common/get-proxy'
|
|
@@ -47,7 +47,8 @@ export default class Sftp extends Component {
|
|
|
47
47
|
onEditFile: false,
|
|
48
48
|
...this.defaultState(),
|
|
49
49
|
loadingSftp: false,
|
|
50
|
-
inited: false
|
|
50
|
+
inited: false,
|
|
51
|
+
ready: false
|
|
51
52
|
}
|
|
52
53
|
this.retryCount = 0
|
|
53
54
|
}
|
|
@@ -58,6 +59,11 @@ export default class Sftp extends Component {
|
|
|
58
59
|
if (this.props.isFtp) {
|
|
59
60
|
this.initFtpData()
|
|
60
61
|
}
|
|
62
|
+
this.timer = setTimeout(() => {
|
|
63
|
+
this.setState({
|
|
64
|
+
ready: true
|
|
65
|
+
})
|
|
66
|
+
}, 100)
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
componentDidUpdate (prevProps, prevState) {
|
|
@@ -1261,10 +1267,18 @@ export default class Sftp extends Component {
|
|
|
1261
1267
|
}
|
|
1262
1268
|
|
|
1263
1269
|
render () {
|
|
1264
|
-
const { height } = this.props
|
|
1265
1270
|
const {
|
|
1266
|
-
id
|
|
1271
|
+
id,
|
|
1272
|
+
ready
|
|
1267
1273
|
} = this.state
|
|
1274
|
+
if (!ready) {
|
|
1275
|
+
return (
|
|
1276
|
+
<div className='pd3 aligncenter'>
|
|
1277
|
+
<LoadingOutlined />
|
|
1278
|
+
</div>
|
|
1279
|
+
)
|
|
1280
|
+
}
|
|
1281
|
+
const { height } = this.props
|
|
1268
1282
|
const all = {
|
|
1269
1283
|
className: 'sftp-wrap overhide relative',
|
|
1270
1284
|
id: `id-${id}`,
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
box-shadow none
|
|
34
34
|
border none
|
|
35
35
|
height 0
|
|
36
|
-
background main
|
|
36
|
+
background var(--main)
|
|
37
37
|
overflow hidden
|
|
38
38
|
border-radius 3px
|
|
39
39
|
&.focused
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
cursor pointer
|
|
47
47
|
border-bottom 1px solid var(--main-darker)
|
|
48
48
|
&:hover
|
|
49
|
-
background var(--
|
|
49
|
+
background var(--primary)
|
|
50
|
+
color var(--primary-contrast)
|
|
50
51
|
|
|
51
52
|
.sftp-item-title
|
|
52
53
|
width 100%
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
height 28px
|
|
116
117
|
line-height 25px
|
|
117
118
|
padding 0 5px
|
|
118
|
-
background main
|
|
119
|
+
background var(--main)
|
|
119
120
|
overflow hidden
|
|
120
121
|
white-space nowrap
|
|
121
122
|
text-overflow ellipsis
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from '../../common/constants'
|
|
21
21
|
import SideIcon from './side-icon'
|
|
22
22
|
import SidePanel from './side-panel'
|
|
23
|
+
import hasActiveInput from '../../common/has-active-input'
|
|
23
24
|
import './sidebar.styl'
|
|
24
25
|
|
|
25
26
|
const e = window.translate
|
|
@@ -50,6 +51,11 @@ export default memo(function Sidebar (props) {
|
|
|
50
51
|
if (pinned) {
|
|
51
52
|
return false
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
if (hasActiveInput()) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
53
59
|
handler.current = setTimeout(
|
|
54
60
|
() => store.setOpenedSideBar(''),
|
|
55
61
|
400
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
width 36px
|
|
22
22
|
z-index 100
|
|
23
23
|
bottom 0
|
|
24
|
-
background var(--main-
|
|
24
|
+
background var(--main-dark)
|
|
25
25
|
|
|
26
26
|
.item-list
|
|
27
27
|
padding-right 0
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
color var(--success)
|
|
87
87
|
//btns
|
|
88
88
|
.btns
|
|
89
|
-
background var(--main-
|
|
89
|
+
background var(--main-dark)
|
|
90
90
|
border-color var(--primary)
|
|
91
91
|
.open-about-icon
|
|
92
92
|
color var(--text-dark)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PureComponent } from 'react'
|
|
2
|
-
import
|
|
2
|
+
import createTitle from '../../common/create-title'
|
|
3
3
|
|
|
4
4
|
export default class TabsSubMenuChild extends PureComponent {
|
|
5
5
|
handleClick = () => {
|
|
@@ -8,7 +8,7 @@ export default class TabsSubMenuChild extends PureComponent {
|
|
|
8
8
|
|
|
9
9
|
render () {
|
|
10
10
|
const { item } = this.props
|
|
11
|
-
const title =
|
|
11
|
+
const title = createTitle(item)
|
|
12
12
|
return (
|
|
13
13
|
<div
|
|
14
14
|
className='sub-context-menu-item'
|
|
@@ -42,16 +42,18 @@
|
|
|
42
42
|
left 100%
|
|
43
43
|
top 0
|
|
44
44
|
background var(--main)
|
|
45
|
-
box-shadow 0px 0px 3px 3px var(--main-
|
|
45
|
+
box-shadow 0px 0px 3px 3px var(--main-darker)
|
|
46
46
|
max-height 300px
|
|
47
47
|
overflow-y scroll
|
|
48
|
+
color var(--text)
|
|
48
49
|
.with-sub-menu
|
|
49
50
|
&:hover
|
|
50
51
|
.sub-context-menu
|
|
51
52
|
display block
|
|
53
|
+
|
|
52
54
|
.sub-context-menu-item
|
|
53
55
|
cursor pointer
|
|
54
|
-
padding
|
|
56
|
+
padding 5px 16px
|
|
55
57
|
overflow hidden
|
|
56
58
|
white-space nowrap
|
|
57
59
|
text-overflow ellipsis
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add button component for tabs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { Component } from 'react'
|
|
6
|
+
import { createPortal } from 'react-dom'
|
|
7
|
+
import {
|
|
8
|
+
CodeFilled,
|
|
9
|
+
PlusOutlined,
|
|
10
|
+
RightSquareFilled
|
|
11
|
+
} from '@ant-design/icons'
|
|
12
|
+
import BookmarksList from '../sidebar/bookmark-select'
|
|
13
|
+
import classNames from 'classnames'
|
|
14
|
+
import hasActiveInput from '../../common/has-active-input'
|
|
15
|
+
import './add-btn.styl'
|
|
16
|
+
|
|
17
|
+
const e = window.translate
|
|
18
|
+
|
|
19
|
+
export default class AddBtn extends Component {
|
|
20
|
+
constructor (props) {
|
|
21
|
+
super(props)
|
|
22
|
+
this.state = {
|
|
23
|
+
open: false,
|
|
24
|
+
menuPosition: 'right',
|
|
25
|
+
menuTop: 0,
|
|
26
|
+
menuLeft: 0
|
|
27
|
+
}
|
|
28
|
+
this.addBtnRef = React.createRef()
|
|
29
|
+
this.menuRef = React.createRef()
|
|
30
|
+
this.hideTimeout = null
|
|
31
|
+
this.portalContainer = null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getPortalContainer = () => {
|
|
35
|
+
if (!this.portalContainer) {
|
|
36
|
+
this.portalContainer = document.createElement('div')
|
|
37
|
+
this.portalContainer.className = 'add-btn-menu-portal'
|
|
38
|
+
document.body.appendChild(this.portalContainer)
|
|
39
|
+
}
|
|
40
|
+
return this.portalContainer
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
componentDidMount () {
|
|
44
|
+
document.addEventListener('click', this.handleDocumentClick)
|
|
45
|
+
// Listen for dropdown events to prevent menu closing
|
|
46
|
+
document.addEventListener('ant-dropdown-show', this.handleDropdownShow)
|
|
47
|
+
document.addEventListener('ant-dropdown-hide', this.handleDropdownHide)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
componentWillUnmount () {
|
|
51
|
+
document.removeEventListener('click', this.handleDocumentClick)
|
|
52
|
+
document.removeEventListener('ant-dropdown-show', this.handleDropdownShow)
|
|
53
|
+
document.removeEventListener('ant-dropdown-hide', this.handleDropdownHide)
|
|
54
|
+
if (this.hideTimeout) {
|
|
55
|
+
clearTimeout(this.hideTimeout)
|
|
56
|
+
}
|
|
57
|
+
// Clean up portal container
|
|
58
|
+
if (this.portalContainer) {
|
|
59
|
+
document.body.removeChild(this.portalContainer)
|
|
60
|
+
this.portalContainer = null
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleDropdownShow = () => {
|
|
65
|
+
// Cancel any pending hide timeout when dropdown shows
|
|
66
|
+
if (this.hideTimeout) {
|
|
67
|
+
clearTimeout(this.hideTimeout)
|
|
68
|
+
this.hideTimeout = null
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
handleDropdownHide = () => {
|
|
73
|
+
// Small delay after dropdown hides before allowing menu to close
|
|
74
|
+
// This prevents flicker when dropdown closes
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
handleDocumentClick = (e) => {
|
|
78
|
+
// Don't close menu when clicking inside menu or add button
|
|
79
|
+
if (this.menuRef.current && this.menuRef.current.contains(e.target)) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
if (this.addBtnRef.current && this.addBtnRef.current.contains(e.target)) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Don't close if clicking on dropdown or active input elements
|
|
87
|
+
if (hasActiveInput()) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.setState({ open: false })
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
handleMouseEnter = () => {
|
|
95
|
+
if (this.hideTimeout) {
|
|
96
|
+
clearTimeout(this.hideTimeout)
|
|
97
|
+
this.hideTimeout = null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Calculate menu position
|
|
101
|
+
if (this.addBtnRef.current) {
|
|
102
|
+
const rect = this.addBtnRef.current.getBoundingClientRect()
|
|
103
|
+
const windowWidth = window.innerWidth
|
|
104
|
+
const windowHeight = window.innerHeight
|
|
105
|
+
|
|
106
|
+
// Estimate menu width and height
|
|
107
|
+
const estimatedMenuWidth = Math.min(300, windowWidth - 40) // Responsive width
|
|
108
|
+
const estimatedMenuHeight = 400 // Rough estimate
|
|
109
|
+
|
|
110
|
+
// Calculate fixed position coordinates
|
|
111
|
+
let menuTop = rect.bottom + 4 // 4px margin
|
|
112
|
+
let menuLeft = rect.left
|
|
113
|
+
let menuPosition = 'right'
|
|
114
|
+
|
|
115
|
+
// Check if menu would overflow bottom of screen
|
|
116
|
+
if (menuTop + estimatedMenuHeight > windowHeight - 20) {
|
|
117
|
+
menuTop = rect.top - estimatedMenuHeight - 4 // Show above button
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// If aligning right would cause overflow, align left instead
|
|
121
|
+
if (rect.left + estimatedMenuWidth > windowWidth - 20) {
|
|
122
|
+
menuPosition = 'left'
|
|
123
|
+
menuLeft = rect.right - estimatedMenuWidth
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// If aligning left would cause overflow (negative position), force right
|
|
127
|
+
if (menuLeft < 20) {
|
|
128
|
+
menuPosition = 'right'
|
|
129
|
+
menuLeft = Math.max(20, rect.left) // Ensure at least 20px from edge
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Final check to ensure menu doesn't go off screen
|
|
133
|
+
if (menuLeft + estimatedMenuWidth > windowWidth - 20) {
|
|
134
|
+
menuLeft = windowWidth - estimatedMenuWidth - 20
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.setState({
|
|
138
|
+
open: true,
|
|
139
|
+
menuPosition,
|
|
140
|
+
menuTop,
|
|
141
|
+
menuLeft
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
if (!this.state.open) {
|
|
145
|
+
window.openTabBatch = this.props.batch
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
handleMouseLeave = () => {
|
|
151
|
+
this.hideTimeout = setTimeout(() => {
|
|
152
|
+
this.setState({ open: false })
|
|
153
|
+
}, 200)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
handleMenuMouseEnter = () => {
|
|
157
|
+
if (this.hideTimeout) {
|
|
158
|
+
clearTimeout(this.hideTimeout)
|
|
159
|
+
this.hideTimeout = null
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
handleMenuMouseLeave = () => {
|
|
164
|
+
// Don't close if there's an active input or dropdown
|
|
165
|
+
if (hasActiveInput()) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.hideTimeout = setTimeout(() => {
|
|
170
|
+
this.setState({ open: false })
|
|
171
|
+
}, 200)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
handleMenuScroll = (e) => {
|
|
175
|
+
// Prevent scroll events from bubbling up
|
|
176
|
+
e.stopPropagation()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
handleTabAdd = () => {
|
|
180
|
+
if (!window.store.hasNodePty) {
|
|
181
|
+
window.store.onNewSsh()
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
window.store.addTab(
|
|
185
|
+
undefined, undefined,
|
|
186
|
+
this.props.batch
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
renderMenus = () => {
|
|
191
|
+
const { onNewSsh } = window.store
|
|
192
|
+
const cls = 'pd2x pd1y context-item pointer'
|
|
193
|
+
const addTabBtn = window.store.hasNodePty
|
|
194
|
+
? (
|
|
195
|
+
<div
|
|
196
|
+
className={cls}
|
|
197
|
+
onClick={this.handleTabAdd}
|
|
198
|
+
>
|
|
199
|
+
<RightSquareFilled /> {e('newTab')}
|
|
200
|
+
</div>
|
|
201
|
+
)
|
|
202
|
+
: null
|
|
203
|
+
|
|
204
|
+
const { menuPosition, menuTop, menuLeft } = this.state
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<div
|
|
208
|
+
ref={this.menuRef}
|
|
209
|
+
className={`add-menu-wrap add-menu-${menuPosition}`}
|
|
210
|
+
style={{
|
|
211
|
+
maxHeight: window.innerHeight - menuTop - 50,
|
|
212
|
+
top: menuTop,
|
|
213
|
+
left: menuLeft
|
|
214
|
+
}}
|
|
215
|
+
onMouseEnter={this.handleMenuMouseEnter}
|
|
216
|
+
onMouseLeave={this.handleMenuMouseLeave}
|
|
217
|
+
onScroll={this.handleMenuScroll}
|
|
218
|
+
>
|
|
219
|
+
<div
|
|
220
|
+
className={cls}
|
|
221
|
+
onClick={onNewSsh}
|
|
222
|
+
>
|
|
223
|
+
<CodeFilled /> {e('newBookmark')}
|
|
224
|
+
</div>
|
|
225
|
+
{addTabBtn}
|
|
226
|
+
<BookmarksList
|
|
227
|
+
store={window.store}
|
|
228
|
+
/>
|
|
229
|
+
</div>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
render () {
|
|
234
|
+
const { empty, className } = this.props
|
|
235
|
+
const { open } = this.state
|
|
236
|
+
const cls = classNames(
|
|
237
|
+
'tabs-add-btn pointer',
|
|
238
|
+
className,
|
|
239
|
+
{
|
|
240
|
+
empty
|
|
241
|
+
}
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<>
|
|
246
|
+
<PlusOutlined
|
|
247
|
+
title={e('openNewTerm')}
|
|
248
|
+
className={cls}
|
|
249
|
+
onClick={this.handleTabAdd}
|
|
250
|
+
onMouseEnter={this.handleMouseEnter}
|
|
251
|
+
onMouseLeave={this.handleMouseLeave}
|
|
252
|
+
ref={this.addBtnRef}
|
|
253
|
+
/>
|
|
254
|
+
{open && createPortal(this.renderMenus(), this.getPortalContainer())}
|
|
255
|
+
</>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -8,12 +8,9 @@ import runIdle from '../../common/run-idle'
|
|
|
8
8
|
import { throttle } from 'lodash-es'
|
|
9
9
|
import TabTitle from './tab-title'
|
|
10
10
|
import {
|
|
11
|
-
CodeFilled,
|
|
12
11
|
DownOutlined,
|
|
13
12
|
LeftOutlined,
|
|
14
|
-
|
|
15
|
-
RightOutlined,
|
|
16
|
-
RightSquareFilled
|
|
13
|
+
RightOutlined
|
|
17
14
|
} from '@ant-design/icons'
|
|
18
15
|
import {
|
|
19
16
|
SingleIcon,
|
|
@@ -25,7 +22,7 @@ import {
|
|
|
25
22
|
TwoRowsRightIcon,
|
|
26
23
|
TwoColumnsBottomIcon
|
|
27
24
|
} from '../icons/split-icons'
|
|
28
|
-
import { Dropdown
|
|
25
|
+
import { Dropdown } from 'antd'
|
|
29
26
|
import Tab from './tab'
|
|
30
27
|
import './tabs.styl'
|
|
31
28
|
import {
|
|
@@ -37,7 +34,7 @@ import {
|
|
|
37
34
|
splitMapDesc
|
|
38
35
|
} from '../../common/constants'
|
|
39
36
|
import WindowControl from './window-control'
|
|
40
|
-
import
|
|
37
|
+
import AddBtn from './add-btn'
|
|
41
38
|
import AppDrag from './app-drag'
|
|
42
39
|
import NoSession from './no-session'
|
|
43
40
|
import classNames from 'classnames'
|
|
@@ -190,45 +187,6 @@ export default class Tabs extends Component {
|
|
|
190
187
|
window.store.setLayout(key)
|
|
191
188
|
}
|
|
192
189
|
|
|
193
|
-
handleOpenChange = (open) => {
|
|
194
|
-
if (open) {
|
|
195
|
-
window.openTabBatch = this.props.batch
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
renderMenus () {
|
|
200
|
-
const { onNewSsh } = window.store
|
|
201
|
-
const cls = 'pd2x pd1y context-item pointer'
|
|
202
|
-
const addTabBtn = window.store.hasNodePty
|
|
203
|
-
? (
|
|
204
|
-
<div
|
|
205
|
-
className={cls}
|
|
206
|
-
onClick={this.handleTabAdd}
|
|
207
|
-
>
|
|
208
|
-
<RightSquareFilled /> {e('newTab')}
|
|
209
|
-
</div>
|
|
210
|
-
)
|
|
211
|
-
: null
|
|
212
|
-
return (
|
|
213
|
-
<div
|
|
214
|
-
className='add-menu-wrap' style={{
|
|
215
|
-
maxHeight: window.innerHeight - 200
|
|
216
|
-
}}
|
|
217
|
-
>
|
|
218
|
-
<div
|
|
219
|
-
className={cls}
|
|
220
|
-
onClick={onNewSsh}
|
|
221
|
-
>
|
|
222
|
-
<CodeFilled /> {e('newBookmark')}
|
|
223
|
-
</div>
|
|
224
|
-
{addTabBtn}
|
|
225
|
-
<BookmarksList
|
|
226
|
-
store={window.store}
|
|
227
|
-
/>
|
|
228
|
-
</div>
|
|
229
|
-
)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
190
|
renderAddBtn = () => {
|
|
233
191
|
const cls = classNames(
|
|
234
192
|
'pointer tabs-add-btn font16',
|
|
@@ -237,17 +195,11 @@ export default class Tabs extends Component {
|
|
|
237
195
|
}
|
|
238
196
|
)
|
|
239
197
|
return (
|
|
240
|
-
<
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
<PlusOutlined
|
|
246
|
-
title={e('openNewTerm')}
|
|
247
|
-
className={cls}
|
|
248
|
-
onClick={this.handleTabAdd}
|
|
249
|
-
/>
|
|
250
|
-
</Popover>
|
|
198
|
+
<AddBtn
|
|
199
|
+
className={cls}
|
|
200
|
+
empty={!this.props.tabs?.length}
|
|
201
|
+
batch={this.props.batch}
|
|
202
|
+
/>
|
|
251
203
|
)
|
|
252
204
|
}
|
|
253
205
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
line-height 36px
|
|
42
42
|
margin 0 1px 0 0
|
|
43
43
|
border-radius 3px 3px 0 0
|
|
44
|
-
background var(--main-
|
|
44
|
+
background var(--main-dark)
|
|
45
45
|
text-align center
|
|
46
46
|
color var(--text-dark)
|
|
47
47
|
&.tab-last
|
|
@@ -121,8 +121,8 @@
|
|
|
121
121
|
right 5px
|
|
122
122
|
cursor pointer
|
|
123
123
|
top 50%
|
|
124
|
+
background var(--main)
|
|
124
125
|
margin-top -8px
|
|
125
|
-
background var(--text-dark)
|
|
126
126
|
border-radius 100%
|
|
127
127
|
color var(--text)
|
|
128
128
|
height 16px
|
|
@@ -131,7 +131,8 @@
|
|
|
131
131
|
line-height 16px
|
|
132
132
|
font-size 10px
|
|
133
133
|
&:hover
|
|
134
|
-
|
|
134
|
+
background var(--primary)
|
|
135
|
+
color var(--primary-contast)
|
|
135
136
|
|
|
136
137
|
.tabs-add-btn
|
|
137
138
|
display inline-block
|
|
@@ -206,7 +207,7 @@
|
|
|
206
207
|
.window-control-minimize
|
|
207
208
|
.window-control-maximize
|
|
208
209
|
&:hover
|
|
209
|
-
background var(--main-
|
|
210
|
+
background var(--main-dark)
|
|
210
211
|
.window-control-close:hover
|
|
211
212
|
background var(--error)
|
|
212
213
|
|
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
} from '@ant-design/icons'
|
|
5
5
|
|
|
6
6
|
export default function TreeExpander (props) {
|
|
7
|
-
function onExpand () {
|
|
7
|
+
function onExpand (e) {
|
|
8
|
+
e.stopPropagation()
|
|
8
9
|
props.onExpand(group)
|
|
9
10
|
}
|
|
10
|
-
function onUnExpand () {
|
|
11
|
+
function onUnExpand (e) {
|
|
12
|
+
e.stopPropagation()
|
|
11
13
|
props.onUnExpand(group)
|
|
12
14
|
}
|
|
13
15
|
const { group } = props
|
package/client/css/basic.styl
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
body
|
|
5
5
|
overflow hidden
|
|
6
|
-
background main
|
|
6
|
+
background var(--main)
|
|
7
7
|
font-size 12px
|
|
8
8
|
line-height 1.5715
|
|
9
9
|
font-family -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'
|