@electerm/electerm-react 1.40.20 → 1.50.21

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.
Files changed (96) hide show
  1. package/client/common/constants.js +57 -7
  2. package/client/common/new-terminal.js +2 -2
  3. package/client/components/auth/login.jsx +34 -57
  4. package/client/components/batch-op/batch-op.jsx +12 -11
  5. package/client/components/bookmark-form/index.jsx +2 -2
  6. package/client/components/bookmark-form/ssh-form.jsx +4 -1
  7. package/client/components/bookmark-form/tree-delete.jsx +5 -5
  8. package/client/components/context-menu/boomarks.jsx +8 -12
  9. package/client/components/context-menu/context-menu.jsx +10 -10
  10. package/client/components/context-menu/history.jsx +21 -24
  11. package/client/components/context-menu/menu-btn.jsx +11 -11
  12. package/client/components/context-menu/tabs.jsx +15 -19
  13. package/client/components/context-menu/zoom.jsx +25 -29
  14. package/client/components/footer/footer-entry.jsx +56 -56
  15. package/client/components/icons/split-icons.jsx +77 -0
  16. package/client/components/layout/layout-alg.js +260 -0
  17. package/client/components/layout/layout-item.jsx +26 -0
  18. package/client/components/layout/layout.jsx +167 -0
  19. package/client/components/layout/layout.styl +5 -0
  20. package/client/components/layout/layouts.jsx +71 -0
  21. package/client/components/layout/session-size-alg.js +31 -0
  22. package/client/components/main/main.jsx +183 -109
  23. package/client/components/main/wrapper.styl +2 -4
  24. package/client/components/profile/profile-list.jsx +1 -3
  25. package/client/components/profile/profile-transport-mod.jsx +1 -1
  26. package/client/components/profile/profile-transport.jsx +6 -9
  27. package/client/components/quick-commands/quick-command-transport.jsx +6 -9
  28. package/client/components/quick-commands/quick-commands-box.jsx +144 -153
  29. package/client/components/quick-commands/quick-commands-select.jsx +10 -3
  30. package/client/components/rdp/rdp-session.jsx +3 -23
  31. package/client/components/rdp/resolution-edit.jsx +40 -42
  32. package/client/components/session/session.jsx +62 -317
  33. package/client/components/session/session.styl +1 -5
  34. package/client/components/session/sessions.jsx +99 -105
  35. package/client/components/setting-panel/bookmark-tree-list.jsx +1 -1
  36. package/client/components/setting-panel/setting-common.jsx +6 -4
  37. package/client/components/setting-panel/setting-modal.jsx +31 -31
  38. package/client/components/setting-panel/start-session-select.jsx +4 -4
  39. package/client/components/setting-panel/tab-settings.jsx +27 -5
  40. package/client/components/setting-sync/data-import.jsx +36 -39
  41. package/client/components/setting-sync/setting-sync-form.jsx +9 -9
  42. package/client/components/setting-sync/setting-sync.jsx +50 -52
  43. package/client/components/sftp/address-bookmark.jsx +57 -58
  44. package/client/components/sftp/confirm-modal-store.jsx +34 -40
  45. package/client/components/sftp/file-item.jsx +14 -3
  46. package/client/components/sftp/file-mode-modal.jsx +3 -0
  47. package/client/components/sftp/list-table-ui.jsx +4 -4
  48. package/client/components/sftp/sftp-entry.jsx +2 -2
  49. package/client/components/sftp/transfer-conflict-store.jsx +13 -17
  50. package/client/components/sftp/transport-action-store.jsx +38 -31
  51. package/client/components/sftp/transports-action-store.jsx +3 -3
  52. package/client/components/sftp/transports-ui-store.jsx +18 -23
  53. package/client/components/shortcuts/shortcut-handler.js +1 -0
  54. package/client/components/shortcuts/shortcuts.jsx +9 -12
  55. package/client/components/side-panel-r/right-side-panel.styl +40 -0
  56. package/client/components/side-panel-r/side-panel-r.jsx +102 -0
  57. package/client/components/sidebar/bookmark-select.jsx +40 -40
  58. package/client/components/sidebar/bookmark.jsx +63 -65
  59. package/client/components/sidebar/history.jsx +53 -50
  60. package/client/components/sidebar/index.jsx +195 -184
  61. package/client/components/sidebar/info-modal.jsx +202 -202
  62. package/client/components/sidebar/sidebar.styl +8 -2
  63. package/client/components/sidebar/transfer-history-modal.jsx +95 -100
  64. package/client/components/sidebar/transfer-list-control.jsx +2 -2
  65. package/client/components/sidebar/transfer-list.jsx +45 -42
  66. package/client/components/sidebar/transfer-modal.jsx +49 -52
  67. package/client/components/sidebar/transport-ui.jsx +1 -1
  68. package/client/components/tabs/index.jsx +261 -49
  69. package/client/components/tabs/tab.jsx +60 -65
  70. package/client/components/tabs/tabs.styl +6 -1
  71. package/client/components/tabs/window-control.jsx +46 -48
  72. package/client/components/terminal/index.jsx +101 -104
  73. package/client/components/terminal/term-search.jsx +26 -24
  74. package/client/components/terminal-info/run-cmd.jsx +0 -25
  75. package/client/components/terminal-info/terminal-info.jsx +60 -0
  76. package/client/components/terminal-info/terminal-info.styl +1 -1
  77. package/client/components/tree-list/bookmark-transport.jsx +8 -9
  78. package/client/components/tree-list/tree-list.jsx +36 -26
  79. package/client/components/vnc/vnc-session.jsx +1 -6
  80. package/client/store/common.js +1 -1
  81. package/client/store/event.js +2 -2
  82. package/client/store/index.js +21 -32
  83. package/client/store/init-state.js +15 -3
  84. package/client/store/load-data.js +1 -1
  85. package/client/store/quick-command.js +4 -4
  86. package/client/store/session.js +1 -1
  87. package/client/store/setting.js +9 -5
  88. package/client/store/system-menu.js +1 -10
  89. package/client/store/tab.js +66 -1
  90. package/client/store/transfer-list.js +5 -6
  91. package/client/store/watch.js +11 -6
  92. package/package.json +1 -1
  93. package/client/components/common/react-subx.jsx +0 -1
  94. package/client/components/common/resize-wrap.jsx +0 -222
  95. package/client/components/common/resize-wrap.styl +0 -9
  96. package/client/components/terminal-info/content.jsx +0 -152
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * session tabs component
3
- * @param {array} props.tabs {id, title}
4
3
  */
5
4
 
6
5
  import React from 'react'
7
- import { findIndex } from 'lodash-es'
6
+ import runIdle from '../../common/run-idle'
7
+ import { findIndex, debounce } from 'lodash-es'
8
8
  import TabTitle from './tab-title'
9
9
  import {
10
10
  CodeFilled,
@@ -14,8 +14,17 @@ import {
14
14
  RightOutlined,
15
15
  RightSquareFilled
16
16
  } from '@ant-design/icons'
17
-
18
- import { Dropdown, Menu, Popover } from 'antd'
17
+ import {
18
+ SingleIcon,
19
+ TwoColumnsIcon,
20
+ ThreeColumnsIcon,
21
+ TwoRowsIcon,
22
+ ThreeRowsIcon,
23
+ Grid2x2Icon,
24
+ TwoRowsRightIcon,
25
+ TwoColumnsBottomIcon
26
+ } from '../icons/split-icons'
27
+ import { Dropdown, Popover } from 'antd'
19
28
  import Tab from './tab'
20
29
  import './tabs.styl'
21
30
  import {
@@ -23,7 +32,9 @@ import {
23
32
  tabMargin,
24
33
  extraTabWidth,
25
34
  windowControlWidth,
26
- isMacJs
35
+ isMacJs,
36
+ splitMapDesc,
37
+ commonActions
27
38
  } from '../../common/constants'
28
39
  import findParentBySel from '../../common/find-parent'
29
40
  import WindowControl from './window-control'
@@ -32,22 +43,28 @@ import AppDrag from './app-drag'
32
43
  import classNames from 'classnames'
33
44
 
34
45
  const e = window.translate
35
- const MenuItem = Menu.Item
36
46
 
37
47
  export default class Tabs extends React.Component {
38
48
  constructor (props) {
39
49
  super(props)
40
50
  this.tabsRef = React.createRef()
41
51
  this.state = {
42
- overflow: false
52
+ overflow: false,
53
+ receiveDataTabId: '',
54
+ onContextMenuTabId: '',
55
+ contextFuncTabId: '',
56
+ contextFunc: '',
57
+ contextArgs: []
43
58
  }
44
59
  }
45
60
 
46
61
  componentDidMount () {
47
- this.dom = document.querySelector('.tabs-inner')
62
+ const { batch } = this.props
63
+ this.dom = document.querySelector(`.v${batch + 1} .tabs-inner`)
48
64
  const {
49
65
  tabsRef
50
66
  } = this
67
+ window.addEventListener('message', this.onEvent)
51
68
  tabsRef.current.addEventListener('mousedown', this.handleClickEvent)
52
69
  tabsRef.current.addEventListener('mousewheel', this.handleWheelEvent)
53
70
  }
@@ -56,15 +73,102 @@ export default class Tabs extends React.Component {
56
73
  if (
57
74
  prevProps.currentTabId !== this.props.currentTabId ||
58
75
  prevProps.width !== this.props.width ||
59
- prevProps.tabs.length !== this.props.tabs.length
76
+ (prevProps.tabs || []).length !== (this.props.tabs || []).length
60
77
  ) {
61
78
  this.adjustScroll()
62
79
  }
63
80
  }
64
81
 
82
+ componentWillUnmount () {
83
+ window.removeEventListener('message', this.onEvent)
84
+ this.offTimer()
85
+ }
86
+
87
+ modifier = (...args) => {
88
+ runIdle(() => this.setState(...args))
89
+ }
90
+
91
+ onEvent = (e) => {
92
+ const {
93
+ action,
94
+ tabId
95
+ } = e.data || {}
96
+ if (
97
+ action === commonActions.closeContextMenuAfter
98
+ ) {
99
+ this.offContextMenu()
100
+ } else if (
101
+ action === 'terminal-receive-data' &&
102
+ tabId
103
+ ) {
104
+ this.modifier({
105
+ receiveDataTabId: tabId
106
+ })
107
+ this.timer = setTimeout(this.clearReceiveData, 4000)
108
+ }
109
+ }
110
+
111
+ clearReceiveData = () => {
112
+ this.modifier({
113
+ receiveDataTabId: ''
114
+ })
115
+ }
116
+
117
+ offTimer = () => {
118
+ clearTimeout(this.timer)
119
+ this.timer = null
120
+ }
121
+
122
+ offContextMenu = () => {
123
+ window.removeEventListener('message', this.onContextAction)
124
+ this.setState({
125
+ onContextMenuTabId: ''
126
+ })
127
+ }
128
+
129
+ handleContextMenu = e => {
130
+ e.preventDefault()
131
+ const { target } = e
132
+ const tabElem = findParentBySel(target, '.tab')
133
+ if (!tabElem) {
134
+ return
135
+ }
136
+ this.setState({
137
+ contextFuncTabId: '',
138
+ onContextMenuTabId: tabElem.dataset.id
139
+ })
140
+ window.addEventListener('message', this.onContextAction)
141
+ }
142
+
143
+ onContextAction = e => {
144
+ const {
145
+ action,
146
+ id,
147
+ args = [],
148
+ func
149
+ } = e.data || {}
150
+ if (
151
+ action !== commonActions.clickContextMenu ||
152
+ id !== this.state.onContextMenuTabId
153
+ ) {
154
+ return false
155
+ }
156
+ this.offContextMenu()
157
+ this.setContextFunc(id, func, args)
158
+ }
159
+
160
+ setContextFunc = (id, func, args) => {
161
+ this.setState({
162
+ contextFuncTabId: id,
163
+ contextFunc: func,
164
+ contextArgs: args
165
+ })
166
+ }
167
+
65
168
  tabsWidth = () => {
169
+ const { batch } = this.props
66
170
  return Array.from(
67
- document.querySelectorAll('.tab')
171
+ document.querySelectorAll(`.v${batch + 1} .tab`)
68
172
  ).reduce((prev, c) => {
69
173
  return prev + c.clientWidth
70
174
  }, 0)
@@ -80,7 +184,9 @@ export default class Tabs extends React.Component {
80
184
  }
81
185
 
82
186
  getInnerWidth = () => {
83
- const inner = document.querySelector('.tabs-inner')
187
+ const { batch } = this.props
188
+ const cls = `.v${batch + 1} .tabs-inner`
189
+ const inner = document.querySelector(cls)
84
190
  return inner ? inner.clientWidth : 0
85
191
  }
86
192
 
@@ -101,11 +207,15 @@ export default class Tabs extends React.Component {
101
207
  this.props.addTab()
102
208
  }
103
209
 
210
+ handleTabAdd = () => {
211
+ this.props.addTab()
212
+ }
213
+
104
214
  adjustScroll = () => {
105
- const { tabs, currentTabId } = this.props
215
+ const { tabs, currentTabId, batch } = this.props
106
216
  const index = findIndex(tabs, t => t.id === currentTabId)
107
217
  const tabsDomWith = Array.from(
108
- document.querySelectorAll('.tab')
218
+ document.querySelectorAll(`.v${batch + 1} .tab`)
109
219
  ).slice(0, index + 2).reduce((prev, c) => {
110
220
  return prev + c.clientWidth
111
221
  }, 0)
@@ -138,7 +248,7 @@ export default class Tabs extends React.Component {
138
248
  this.dom.scrollLeft = scrollLeft
139
249
  }
140
250
 
141
- handleWheelEvent = (e) => {
251
+ handleWheelEvent = debounce((e) => {
142
252
  if (this.isOverflow()) {
143
253
  if (e.deltaY < 0) {
144
254
  this.handleScrollLeft()
@@ -146,34 +256,24 @@ export default class Tabs extends React.Component {
146
256
  this.handleScrollRight()
147
257
  }
148
258
  }
149
- }
259
+ }, 100)
150
260
 
151
261
  handleClickMenu = ({ key }) => {
152
262
  const id = key.split('##')[1]
153
263
  this.props.onChangeTabId(id)
154
264
  }
155
265
 
156
- renderList = () => {
157
- const { tabs = [] } = this.props
158
- return (
159
- <Menu onClick={this.handleClickMenu}>
160
- {
161
- tabs.map((t, i) => {
162
- return (
163
- <MenuItem
164
- key={i + '##' + t.id}
165
- >
166
- <TabTitle tab={t} />
167
- </MenuItem>
168
- )
169
- })
170
- }
171
- </Menu>
172
- )
266
+ handleChangeLayout = ({ key }) => {
267
+ window.store.setLayout(key)
268
+ }
269
+
270
+ handleOpenChange = (open) => {
271
+ if (open) {
272
+ window.openTabBatch = this.props.batch
273
+ }
173
274
  }
174
275
 
175
276
  renderMenus () {
176
- const { addTab } = this.props
177
277
  const { onNewSsh } = window.store
178
278
  const cls = 'pd2x pd1y context-item pointer'
179
279
  return (
@@ -190,7 +290,7 @@ export default class Tabs extends React.Component {
190
290
  </div>
191
291
  <div
192
292
  className={cls}
193
- onClick={() => addTab()}
293
+ onClick={this.handleTabAdd}
194
294
  >
195
295
  <RightSquareFilled /> {e('newTab')}
196
296
  </div>
@@ -205,23 +305,48 @@ export default class Tabs extends React.Component {
205
305
  const cls = classNames(
206
306
  'pointer tabs-add-btn font16',
207
307
  {
208
- empty: !this.props.tabs.length
308
+ empty: !this.props.tabs?.length
209
309
  }
210
310
  )
211
311
  return (
212
312
  <Popover
213
313
  content={this.renderMenus()}
314
+ onOpenChange={this.handleOpenChange}
214
315
  >
215
316
  <PlusOutlined
216
317
  title={e('openNewTerm')}
217
318
  className={cls}
218
- onClick={() => this.props.addTab()}
319
+ onClick={this.handleTabAdd}
219
320
  />
220
321
  </Popover>
221
322
  )
222
323
  }
223
324
 
325
+ renderNoExtra () {
326
+ return (
327
+ <div className='tabs-extra pd1x'>
328
+ {this.renderLayoutMenu()}
329
+ </div>
330
+ )
331
+ }
332
+
224
333
  renderExtra () {
334
+ const items = this.props.tabs.map((t, i) => {
335
+ return {
336
+ key: i + '##' + t.id,
337
+ label: (
338
+ <span><TabTitle tab={t} /></span>
339
+ ),
340
+ onClick: () => this.handleClickMenu({ key: i + '##' + t.id })
341
+ }
342
+ })
343
+ const dropProps = {
344
+ className: 'tabs-add-btn font16',
345
+ menu: {
346
+ items
347
+ },
348
+ placement: 'bottomRight'
349
+ }
225
350
  return (
226
351
  <div className='tabs-extra pd1x'>
227
352
  {this.renderAddBtn()}
@@ -234,12 +359,13 @@ export default class Tabs extends React.Component {
234
359
  onClick={this.handleScrollRight}
235
360
  />
236
361
  <Dropdown
237
- className='iblock'
238
- placement='bottomRight'
239
- overlay={this.renderList()}
362
+ {...dropProps}
240
363
  >
241
364
  <DownOutlined className='tabs-dd-icon' />
242
365
  </Dropdown>
366
+ {
367
+ this.renderLayoutMenu()
368
+ }
243
369
  </div>
244
370
  )
245
371
  }
@@ -260,13 +386,13 @@ export default class Tabs extends React.Component {
260
386
  const { tabs = [], width, config } = this.props
261
387
  const len = tabs.length
262
388
  const tabsWidthAll = tabMargin * len + 10 + this.tabsWidth()
263
- const { overflow } = this.state
389
+ const { overflow, receiveDataTabId, onContextMenuTabId } = this.state
264
390
  const left = overflow
265
391
  ? '100%'
266
392
  : tabsWidthAll
267
393
  const w1 = isMacJs && (config.useSystemTitleBar || window.et.isWebApp)
268
394
  ? 30
269
- : windowControlWidth
395
+ : this.getExtraTabWidth()
270
396
  const style = {
271
397
  width: width - w1 - 166
272
398
  }
@@ -290,11 +416,20 @@ export default class Tabs extends React.Component {
290
416
  {
291
417
  tabs.map((tab, i) => {
292
418
  const isLast = i === len - 1
419
+ const tabProps = {
420
+ ...this.props,
421
+ tab,
422
+ isLast,
423
+ receiveData: receiveDataTabId === tab.id,
424
+ openContextMenu: onContextMenuTabId === tab.id
425
+ }
426
+ if (this.state.contextFuncTabId === tab.id) {
427
+ tabProps.contextFunc = this.state.contextFunc
428
+ tabProps.contextArgs = this.state.contextArgs
429
+ }
293
430
  return (
294
431
  <Tab
295
- {...this.props}
296
- tab={tab}
297
- isLast={isLast}
432
+ {...tabProps}
298
433
  key={tab.id}
299
434
  />
300
435
  )
@@ -310,18 +445,95 @@ export default class Tabs extends React.Component {
310
445
  )
311
446
  }
312
447
 
313
- render () {
314
- const { overflow } = this.state
448
+ getLayoutIcon = (layout) => {
449
+ const iconMaps = {
450
+ single: SingleIcon,
451
+ twoColumns: TwoColumnsIcon,
452
+ threeColumns: ThreeColumnsIcon,
453
+ twoRows: TwoRowsIcon,
454
+ threeRows: ThreeRowsIcon,
455
+ grid2x2: Grid2x2Icon,
456
+ twoRowsRight: TwoRowsRightIcon,
457
+ twoColumnsBottom: TwoColumnsBottomIcon
458
+ }
459
+ return iconMaps[layout]
460
+ }
461
+
462
+ renderLayoutMenu = () => {
463
+ if (!this.shouldRenderWindowControl()) {
464
+ return null
465
+ }
466
+ const items = Object.keys(splitMapDesc).map((t) => {
467
+ const v = splitMapDesc[t]
468
+ const Icon = this.getLayoutIcon(v)
469
+ return {
470
+ key: t,
471
+ label: (
472
+ <span>
473
+ <Icon /> {e(v)}
474
+ </span>
475
+ ),
476
+ onClick: () => this.handleChangeLayout({ key: t })
477
+ }
478
+ })
479
+ const v = splitMapDesc[this.props.layout]
480
+ const Icon = this.getLayoutIcon(v)
315
481
  return (
316
- <div className='tabs' ref={this.tabsRef}>
317
- {this.renderContent()}
482
+ <Dropdown
483
+ menu={{ items }}
484
+ placement='bottomRight'
485
+ >
486
+ <span className='tabs-dd-icon mg1l'>
487
+ <Icon /> <DownOutlined />
488
+ </span>
489
+ </Dropdown>
490
+ )
491
+ }
492
+
493
+ shouldRenderWindowControl = () => {
494
+ const { layout, batch } = this.props
495
+ const batchToRender = {
496
+ c1: 0,
497
+ c2: 1,
498
+ c3: 2,
499
+ r2: 0,
500
+ r3: 0,
501
+ c2x2: 1,
502
+ c1r2: 1,
503
+ r1c2: 0
504
+ }
505
+ return batch === batchToRender[layout]
506
+ }
507
+
508
+ getExtraTabWidth = () => {
509
+ return this.shouldRenderWindowControl()
510
+ ? windowControlWidth
511
+ : 0
512
+ }
513
+
514
+ renderWindowControl = () => {
515
+ if (this.shouldRenderWindowControl()) {
516
+ return (
318
517
  <WindowControl
319
518
  store={window.store}
320
519
  />
520
+ )
521
+ }
522
+ return null
523
+ }
524
+
525
+ render () {
526
+ const { overflow } = this.state
527
+ return (
528
+ <div className='tabs' ref={this.tabsRef} onContextMenu={this.handleContextMenu}>
529
+ {this.renderContent()}
530
+ {
531
+ this.renderWindowControl()
532
+ }
321
533
  {
322
534
  overflow
323
535
  ? this.renderExtra()
324
- : null
536
+ : this.renderNoExtra()
325
537
  }
326
538
  </div>
327
539
  )
@@ -9,7 +9,7 @@ import {
9
9
  Loading3QuartersOutlined,
10
10
  BorderlessTableOutlined
11
11
  } from '@ant-design/icons'
12
- import generate from '../../common/uid'
12
+ import generate from '../../common/id-with-stamp'
13
13
  import { Tooltip, message } from 'antd'
14
14
  import classnames from 'classnames'
15
15
  import copy from 'json-deep-copy'
@@ -19,7 +19,9 @@ import createName from '../../common/create-title'
19
19
  import { addClass, removeClass } from '../../common/class'
20
20
  import {
21
21
  terminalSshConfigType,
22
- commonActions
22
+ splitConfig,
23
+ paneMap,
24
+ statusMap
23
25
  } from '../../common/constants'
24
26
  import { shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
25
27
 
@@ -31,13 +33,12 @@ class Tab extends Component {
31
33
  constructor (props) {
32
34
  super(props)
33
35
  this.state = {
34
- terminalOnData: false,
35
36
  tab: copy(props.tab)
36
37
  }
37
38
  }
38
39
 
39
40
  componentDidMount () {
40
- this.dom = document.getElementById('id' + this.state.tab.id)
41
+ this.dom = document.getElementById('tab-' + this.state.tab.id)
41
42
  window.addEventListener('message', this.onEvent)
42
43
  }
43
44
 
@@ -47,12 +48,16 @@ class Tab extends Component {
47
48
  tab: copy(this.props.tab)
48
49
  })
49
50
  }
51
+ if (this.props.openContextMenu && !prevProps.openContextMenu) {
52
+ this.handleContextMenu()
53
+ }
54
+ if (this.props.contextFunc && !prevProps.contextFunc) {
55
+ this[this.props.contextFunc](...this.props.contextArgs)
56
+ }
50
57
  }
51
58
 
52
59
  componentWillUnmount () {
53
- window.removeEventListener('message', this.onEvent)
54
- window.removeEventListener('message', this.onContextAction)
55
- clearTimeout(this.handler)
60
+ this.dom = null
56
61
  }
57
62
 
58
63
  shouldComponentUpdate (nextProps, nextState) {
@@ -70,10 +75,11 @@ class Tab extends Component {
70
75
  'height',
71
76
  'isLast',
72
77
  'isMaximized',
73
- 'activeTerminalId',
74
78
  'config',
75
79
  'tab',
76
- 'width'
80
+ 'width',
81
+ 'openContextMenu',
82
+ 'contextFunc'
77
83
  ]
78
84
 
79
85
  // compare only the relevant props
@@ -84,28 +90,6 @@ class Tab extends Component {
84
90
  runIdle(() => this.setState(...args))
85
91
  }
86
92
 
87
- onEvent = (e) => {
88
- if (
89
- e.data &&
90
- e.data.action === 'terminal-receive-data' &&
91
- e.data.tabId === this.state.tab.id
92
- ) {
93
- this.modifier({
94
- terminalOnData: true
95
- })
96
- if (this.handler) {
97
- clearTimeout(this.handler)
98
- }
99
- this.handler = setTimeout(this.offTerminalOnData, 4000)
100
- }
101
- }
102
-
103
- offTerminalOnData = () => {
104
- this.modifier({
105
- terminalOnData: false
106
- })
107
- }
108
-
109
93
  clearCls = () => {
110
94
  document.querySelectorAll('.' + onDragOverCls).forEach((d) => {
111
95
  removeClass(d, onDragOverCls)
@@ -173,12 +157,24 @@ class Tab extends Component {
173
157
  }
174
158
  const { id } = fromTab
175
159
  const tabs = copy(this.props.tabs)
176
- const indexFrom = findIndex(tabs, t => t.id === id)
177
160
  let indexDrop = findIndex(tabs, t => t.id === dropId)
178
- if (indexDrop > indexFrom) {
179
- indexDrop = indexDrop - 1
161
+ const dropTab = tabs[indexDrop]
162
+ const indexFrom = findIndex(tabs, t => t.id === id)
163
+ if (indexFrom === -1) {
164
+ const fromBatch = fromTab.batch
165
+ setTimeout(() => {
166
+ const closeBtn = document.querySelector(`.v${fromBatch + 1} .tab-${id} .tab-close .anticon`)
167
+ if (closeBtn) {
168
+ closeBtn.click()
169
+ }
170
+ }, 10)
171
+ fromTab.batch = dropTab.batch
172
+ } else {
173
+ if (indexDrop > indexFrom) {
174
+ indexDrop = indexDrop - 1
175
+ }
176
+ tabs.splice(indexFrom, 1)
180
177
  }
181
- tabs.splice(indexFrom, 1)
182
178
  tabs.splice(indexDrop, 0, fromTab)
183
179
  this.props.setTabs(
184
180
  tabs
@@ -206,6 +202,21 @@ class Tab extends Component {
206
202
  )
207
203
  }
208
204
 
205
+ cloneToNextLayout = () => {
206
+ const defaultStatus = statusMap.processing
207
+ const { batch, layout } = this.props
208
+ const ntb = copy(this.state.tab)
209
+ Object.assign(ntb, {
210
+ id: generate(),
211
+ status: defaultStatus,
212
+ isTransporting: undefined,
213
+ pane: paneMap.terminal
214
+ })
215
+ const maxBatch = splitConfig[layout].children
216
+ ntb.batch = (batch + 1) % maxBatch
217
+ window.store.addTab(ntb)
218
+ }
219
+
209
220
  newTab = () => {
210
221
  this.props.addTab()
211
222
  }
@@ -269,7 +280,7 @@ class Tab extends Component {
269
280
  }
270
281
 
271
282
  renderContext = () => {
272
- const { tabs, tab } = this.props
283
+ const { tabs, tab, layout } = this.props
273
284
  const len = tabs.length
274
285
  const index = findIndex(tabs, t => t.id === tab.id)
275
286
  const noRight = index >= len - 1
@@ -303,6 +314,13 @@ class Tab extends Component {
303
314
  icon: 'CopyOutlined',
304
315
  text: e('duplicate')
305
316
  })
317
+ if (layout !== 'c1') {
318
+ res.push({
319
+ func: 'cloneToNextLayout',
320
+ icon: 'CopyOutlined',
321
+ text: e('cloneToNextLayout')
322
+ })
323
+ }
306
324
  res.push({
307
325
  disabled: isSshConfig,
308
326
  func: 'doRename',
@@ -318,39 +336,17 @@ class Tab extends Component {
318
336
  return res
319
337
  }
320
338
 
321
- onContextAction = e => {
322
- const {
323
- action,
324
- id,
325
- args = [],
326
- func
327
- } = e.data || {}
328
- if (
329
- action !== commonActions.clickContextMenu ||
330
- id !== this.uid ||
331
- !this[func]
332
- ) {
333
- return false
334
- }
335
- window.removeEventListener('message', this.onContextAction)
336
- this[func](...args)
337
- }
338
-
339
- handleContextMenu = e => {
340
- e.preventDefault()
341
- const { target } = e
342
- const rect = target.getBoundingClientRect()
339
+ handleContextMenu = () => {
340
+ const rect = this.dom.getBoundingClientRect()
343
341
  const items = this.renderContext()
344
- this.uid = generate()
345
342
  window.store.openContextMenu({
346
343
  items,
347
344
  pos: {
348
345
  left: rect.left,
349
346
  top: rect.top + 20
350
347
  },
351
- id: this.uid
348
+ id: this.state.tab.id
352
349
  })
353
- window.addEventListener('message', this.onContextAction)
354
350
  }
355
351
 
356
352
  renderEditting (tab, cls) {
@@ -416,8 +412,8 @@ class Tab extends Component {
416
412
  const {
417
413
  currentTabId
418
414
  } = this.props
419
- const { isLast } = this.props
420
- const { tab, terminalOnData } = this.state
415
+ const { isLast, terminalOnData } = this.props
416
+ const { tab } = this.state
421
417
  const {
422
418
  id,
423
419
  isEditting,
@@ -461,7 +457,7 @@ class Tab extends Component {
461
457
  <div
462
458
  className={cls}
463
459
  draggable
464
- id={'id' + id}
460
+ id={'tab-' + id}
465
461
  data-id={id}
466
462
  {...pick(this, [
467
463
  'onDrag',
@@ -479,7 +475,6 @@ class Tab extends Component {
479
475
  onClick={this.handleClick}
480
476
  onDoubleClick={this.handleDup}
481
477
  style={styleTag}
482
- onContextMenu={this.handleContextMenu}
483
478
  >
484
479
  <Loading3QuartersOutlined
485
480
  className='pointer tab-reload mg1r'