@electerm/electerm-react 1.40.20 → 1.50.31

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 (104) 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 +94 -0
  18. package/client/components/layout/layout.jsx +167 -0
  19. package/client/components/layout/layout.styl +8 -0
  20. package/client/components/layout/layouts.jsx +72 -0
  21. package/client/components/layout/session-size-alg.js +31 -0
  22. package/client/components/main/custom-css.jsx +1 -1
  23. package/client/components/main/main.jsx +184 -110
  24. package/client/components/main/ui-theme.jsx +1 -1
  25. package/client/components/main/wrapper.styl +2 -4
  26. package/client/components/profile/profile-list.jsx +1 -3
  27. package/client/components/profile/profile-transport-mod.jsx +1 -1
  28. package/client/components/profile/profile-transport.jsx +6 -9
  29. package/client/components/quick-commands/quick-command-transport.jsx +6 -9
  30. package/client/components/quick-commands/quick-commands-box.jsx +144 -153
  31. package/client/components/quick-commands/quick-commands-select.jsx +10 -3
  32. package/client/components/rdp/rdp-session.jsx +3 -23
  33. package/client/components/rdp/resolution-edit.jsx +40 -42
  34. package/client/components/session/session.jsx +63 -328
  35. package/client/components/session/session.styl +1 -5
  36. package/client/components/session/sessions.jsx +140 -111
  37. package/client/components/setting-panel/bookmark-tree-list.jsx +1 -1
  38. package/client/components/setting-panel/setting-common.jsx +6 -4
  39. package/client/components/setting-panel/setting-modal.jsx +31 -31
  40. package/client/components/setting-panel/start-session-select.jsx +4 -4
  41. package/client/components/setting-panel/tab-settings.jsx +27 -5
  42. package/client/components/setting-sync/data-import.jsx +36 -39
  43. package/client/components/setting-sync/setting-sync-form.jsx +10 -10
  44. package/client/components/setting-sync/setting-sync.jsx +50 -52
  45. package/client/components/sftp/address-bookmark.jsx +57 -58
  46. package/client/components/sftp/confirm-modal-store.jsx +34 -40
  47. package/client/components/sftp/file-item.jsx +14 -3
  48. package/client/components/sftp/file-mode-modal.jsx +3 -0
  49. package/client/components/sftp/list-table-ui.jsx +4 -4
  50. package/client/components/sftp/sftp-entry.jsx +2 -2
  51. package/client/components/sftp/transfer-conflict-store.jsx +13 -17
  52. package/client/components/sftp/transport-action-store.jsx +38 -31
  53. package/client/components/sftp/transports-action-store.jsx +3 -3
  54. package/client/components/sftp/transports-ui-store.jsx +18 -23
  55. package/client/components/shortcuts/shortcut-handler.js +1 -0
  56. package/client/components/shortcuts/shortcuts-defaults.js +5 -5
  57. package/client/components/shortcuts/shortcuts.jsx +9 -12
  58. package/client/components/side-panel-r/right-side-panel.styl +40 -0
  59. package/client/components/side-panel-r/side-panel-r.jsx +102 -0
  60. package/client/components/sidebar/bookmark-select.jsx +40 -40
  61. package/client/components/sidebar/bookmark.jsx +63 -65
  62. package/client/components/sidebar/history.jsx +53 -50
  63. package/client/components/sidebar/index.jsx +195 -184
  64. package/client/components/sidebar/info-modal.jsx +202 -202
  65. package/client/components/sidebar/sidebar.styl +8 -2
  66. package/client/components/sidebar/transfer-history-modal.jsx +95 -100
  67. package/client/components/sidebar/transfer-list-control.jsx +2 -2
  68. package/client/components/sidebar/transfer-list.jsx +45 -42
  69. package/client/components/sidebar/transfer-modal.jsx +49 -52
  70. package/client/components/sidebar/transport-ui.jsx +1 -1
  71. package/client/components/tabs/index.jsx +261 -49
  72. package/client/components/tabs/tab.jsx +48 -66
  73. package/client/components/tabs/tabs.styl +6 -1
  74. package/client/components/tabs/window-control.jsx +46 -48
  75. package/client/components/terminal/attach-addon-custom.js +1 -1
  76. package/client/components/terminal/index.jsx +111 -113
  77. package/client/components/terminal/term-search.jsx +26 -24
  78. package/client/components/terminal-info/run-cmd.jsx +0 -25
  79. package/client/components/terminal-info/terminal-info.jsx +60 -0
  80. package/client/components/terminal-info/terminal-info.styl +1 -1
  81. package/client/components/tree-list/bookmark-transport.jsx +8 -9
  82. package/client/components/tree-list/tree-list.jsx +36 -26
  83. package/client/components/vnc/vnc-session.jsx +1 -6
  84. package/client/components/web/address-bar.jsx +50 -0
  85. package/client/components/web/web-session.jsx +32 -10
  86. package/client/entry/index.jsx +5 -6
  87. package/client/store/common.js +1 -1
  88. package/client/store/db-upgrade.js +1 -1
  89. package/client/store/event.js +2 -2
  90. package/client/store/index.js +21 -32
  91. package/client/store/init-state.js +15 -3
  92. package/client/store/load-data.js +1 -1
  93. package/client/store/quick-command.js +4 -4
  94. package/client/store/session.js +1 -1
  95. package/client/store/setting.js +10 -6
  96. package/client/store/system-menu.js +1 -10
  97. package/client/store/tab.js +91 -1
  98. package/client/store/transfer-list.js +5 -6
  99. package/client/store/watch.js +11 -6
  100. package/package.json +1 -1
  101. package/client/components/common/react-subx.jsx +0 -1
  102. package/client/components/common/resize-wrap.jsx +0 -222
  103. package/client/components/common/resize-wrap.styl +0 -9
  104. 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,6 @@ import {
9
9
  Loading3QuartersOutlined,
10
10
  BorderlessTableOutlined
11
11
  } from '@ant-design/icons'
12
- import generate from '../../common/uid'
13
12
  import { Tooltip, message } from 'antd'
14
13
  import classnames from 'classnames'
15
14
  import copy from 'json-deep-copy'
@@ -18,8 +17,7 @@ import Input from '../common/input-auto-focus'
18
17
  import createName from '../../common/create-title'
19
18
  import { addClass, removeClass } from '../../common/class'
20
19
  import {
21
- terminalSshConfigType,
22
- commonActions
20
+ terminalSshConfigType
23
21
  } from '../../common/constants'
24
22
  import { shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
25
23
 
@@ -31,13 +29,12 @@ class Tab extends Component {
31
29
  constructor (props) {
32
30
  super(props)
33
31
  this.state = {
34
- terminalOnData: false,
35
32
  tab: copy(props.tab)
36
33
  }
37
34
  }
38
35
 
39
36
  componentDidMount () {
40
- this.dom = document.getElementById('id' + this.state.tab.id)
37
+ this.dom = document.getElementById('tab-' + this.state.tab.id)
41
38
  window.addEventListener('message', this.onEvent)
42
39
  }
43
40
 
@@ -47,12 +44,16 @@ class Tab extends Component {
47
44
  tab: copy(this.props.tab)
48
45
  })
49
46
  }
47
+ if (this.props.openContextMenu && !prevProps.openContextMenu) {
48
+ this.handleContextMenu()
49
+ }
50
+ if (this.props.contextFunc && !prevProps.contextFunc) {
51
+ this[this.props.contextFunc](...this.props.contextArgs)
52
+ }
50
53
  }
51
54
 
52
55
  componentWillUnmount () {
53
- window.removeEventListener('message', this.onEvent)
54
- window.removeEventListener('message', this.onContextAction)
55
- clearTimeout(this.handler)
56
+ this.dom = null
56
57
  }
57
58
 
58
59
  shouldComponentUpdate (nextProps, nextState) {
@@ -70,10 +71,11 @@ class Tab extends Component {
70
71
  'height',
71
72
  'isLast',
72
73
  'isMaximized',
73
- 'activeTerminalId',
74
74
  'config',
75
75
  'tab',
76
- 'width'
76
+ 'width',
77
+ 'openContextMenu',
78
+ 'contextFunc'
77
79
  ]
78
80
 
79
81
  // compare only the relevant props
@@ -84,28 +86,6 @@ class Tab extends Component {
84
86
  runIdle(() => this.setState(...args))
85
87
  }
86
88
 
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
89
  clearCls = () => {
110
90
  document.querySelectorAll('.' + onDragOverCls).forEach((d) => {
111
91
  removeClass(d, onDragOverCls)
@@ -173,12 +153,24 @@ class Tab extends Component {
173
153
  }
174
154
  const { id } = fromTab
175
155
  const tabs = copy(this.props.tabs)
176
- const indexFrom = findIndex(tabs, t => t.id === id)
177
156
  let indexDrop = findIndex(tabs, t => t.id === dropId)
178
- if (indexDrop > indexFrom) {
179
- indexDrop = indexDrop - 1
157
+ const dropTab = tabs[indexDrop]
158
+ const indexFrom = findIndex(tabs, t => t.id === id)
159
+ if (indexFrom === -1) {
160
+ const fromBatch = fromTab.batch
161
+ setTimeout(() => {
162
+ const closeBtn = document.querySelector(`.v${fromBatch + 1} .tab-${id} .tab-close .anticon`)
163
+ if (closeBtn) {
164
+ closeBtn.click()
165
+ }
166
+ }, 10)
167
+ fromTab.batch = dropTab.batch
168
+ } else {
169
+ if (indexDrop > indexFrom) {
170
+ indexDrop = indexDrop - 1
171
+ }
172
+ tabs.splice(indexFrom, 1)
180
173
  }
181
- tabs.splice(indexFrom, 1)
182
174
  tabs.splice(indexDrop, 0, fromTab)
183
175
  this.props.setTabs(
184
176
  tabs
@@ -206,6 +198,10 @@ class Tab extends Component {
206
198
  )
207
199
  }
208
200
 
201
+ cloneToNextLayout = () => {
202
+ window.store.cloneToNextLayout()
203
+ }
204
+
209
205
  newTab = () => {
210
206
  this.props.addTab()
211
207
  }
@@ -276,10 +272,13 @@ class Tab extends Component {
276
272
  const isSshConfig = tab.type === terminalSshConfigType
277
273
  const res = []
278
274
  const reloadShortcut = this.getShortcut('app_reloadCurrentTab')
275
+ const closeShortcut = this.getShortcut('app_closeCurrentTab')
276
+ const cloneToNextShortcut = this.getShortcut('app_cloneToNextLayout')
279
277
  res.push({
280
278
  func: 'handleClose',
281
279
  icon: 'CloseOutlined',
282
- text: e('close')
280
+ text: e('close'),
281
+ subText: closeShortcut
283
282
  })
284
283
  res.push({
285
284
  func: 'closeOther',
@@ -303,6 +302,12 @@ class Tab extends Component {
303
302
  icon: 'CopyOutlined',
304
303
  text: e('duplicate')
305
304
  })
305
+ res.push({
306
+ func: 'cloneToNextLayout',
307
+ icon: 'CopyOutlined',
308
+ text: e('cloneToNextLayout'),
309
+ subText: cloneToNextShortcut
310
+ })
306
311
  res.push({
307
312
  disabled: isSshConfig,
308
313
  func: 'doRename',
@@ -318,39 +323,17 @@ class Tab extends Component {
318
323
  return res
319
324
  }
320
325
 
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()
326
+ handleContextMenu = () => {
327
+ const rect = this.dom.getBoundingClientRect()
343
328
  const items = this.renderContext()
344
- this.uid = generate()
345
329
  window.store.openContextMenu({
346
330
  items,
347
331
  pos: {
348
332
  left: rect.left,
349
333
  top: rect.top + 20
350
334
  },
351
- id: this.uid
335
+ id: this.state.tab.id
352
336
  })
353
- window.addEventListener('message', this.onContextAction)
354
337
  }
355
338
 
356
339
  renderEditting (tab, cls) {
@@ -416,8 +399,8 @@ class Tab extends Component {
416
399
  const {
417
400
  currentTabId
418
401
  } = this.props
419
- const { isLast } = this.props
420
- const { tab, terminalOnData } = this.state
402
+ const { isLast, terminalOnData } = this.props
403
+ const { tab } = this.state
421
404
  const {
422
405
  id,
423
406
  isEditting,
@@ -461,7 +444,7 @@ class Tab extends Component {
461
444
  <div
462
445
  className={cls}
463
446
  draggable
464
- id={'id' + id}
447
+ id={'tab-' + id}
465
448
  data-id={id}
466
449
  {...pick(this, [
467
450
  'onDrag',
@@ -479,7 +462,6 @@ class Tab extends Component {
479
462
  onClick={this.handleClick}
480
463
  onDoubleClick={this.handleDup}
481
464
  style={styleTag}
482
- onContextMenu={this.handleContextMenu}
483
465
  >
484
466
  <Loading3QuartersOutlined
485
467
  className='pointer tab-reload mg1r'