@electerm/electerm-react 1.39.5 → 1.39.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 (45) hide show
  1. package/client/common/constants.js +7 -3
  2. package/client/common/create-title.jsx +12 -2
  3. package/client/common/default-setting.js +5 -0
  4. package/client/common/init-setting-item.js +6 -0
  5. package/client/components/bookmark-form/form-ssh-common.jsx +1 -1
  6. package/client/components/bookmark-form/index.jsx +6 -2
  7. package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
  8. package/client/components/bookmark-form/rdp-form.jsx +1 -1
  9. package/client/components/bookmark-form/render-auth-ssh.jsx +27 -1
  10. package/client/components/bookmark-form/render-ssh-tunnel.jsx +60 -31
  11. package/client/components/bookmark-form/use-ui.jsx +12 -0
  12. package/client/components/bookmark-form/vnc-form-ui.jsx +179 -0
  13. package/client/components/bookmark-form/vnc-form.jsx +16 -0
  14. package/client/components/footer/footer-entry.jsx +13 -6
  15. package/client/components/main/term-fullscreen.styl +4 -1
  16. package/client/components/profile/profile-form-elem.jsx +87 -0
  17. package/client/components/profile/profile-form.jsx +33 -0
  18. package/client/components/profile/profile-list.jsx +79 -0
  19. package/client/components/profile/profile-transport-mod.jsx +5 -0
  20. package/client/components/profile/profile-transport.jsx +12 -0
  21. package/client/components/quick-commands/quick-command-transport-mod.jsx +13 -14
  22. package/client/components/quick-commands/quick-commands-list-form.jsx +54 -3
  23. package/client/components/rdp/rdp-session.jsx +53 -36
  24. package/client/components/session/session.jsx +14 -3
  25. package/client/components/session/session.styl +7 -2
  26. package/client/components/setting-panel/setting-modal.jsx +23 -6
  27. package/client/components/setting-panel/setting-terminal.jsx +1 -1
  28. package/client/components/setting-panel/setting-wrap.jsx +5 -1
  29. package/client/components/setting-panel/setting-wrap.styl +5 -3
  30. package/client/components/setting-panel/tab-profiles.jsx +38 -0
  31. package/client/components/sftp/list-table-ui.jsx +99 -46
  32. package/client/components/sftp/sftp-entry.jsx +1 -0
  33. package/client/components/sftp/sftp.styl +4 -1
  34. package/client/components/sftp/transfer-common.js +1 -1
  35. package/client/components/tabs/index.jsx +1 -1
  36. package/client/components/tabs/tab.jsx +9 -4
  37. package/client/components/terminal/index.jsx +5 -5
  38. package/client/components/vnc/vnc-form.jsx +66 -0
  39. package/client/components/vnc/vnc-session.jsx +297 -0
  40. package/client/store/common.js +21 -0
  41. package/client/store/index.js +1 -0
  42. package/client/store/init-state.js +4 -23
  43. package/client/store/load-data.js +9 -2
  44. package/client/store/sync.js +7 -3
  45. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ import SettingCol from './col'
2
+ import ProfileForm from '../profile/profile-form'
3
+ import ProfileList from '../profile/profile-list'
4
+ import {
5
+ settingMap
6
+ } from '../../common/constants'
7
+
8
+ export default function TabProfiles (props) {
9
+ const {
10
+ settingTab
11
+ } = props
12
+ if (settingTab !== settingMap.profiles) {
13
+ return null
14
+ }
15
+ const {
16
+ settingItem,
17
+ listProps,
18
+ formProps,
19
+ store
20
+ } = props
21
+ return (
22
+ <div
23
+ className='setting-tabs-profile'
24
+ >
25
+ <SettingCol>
26
+ <ProfileList
27
+ {...listProps}
28
+ quickCommandId={store.quickCommandId}
29
+ />
30
+ <ProfileForm
31
+ {...formProps}
32
+ quickCommandTags={store.quickCommandTags}
33
+ key={settingItem.id}
34
+ />
35
+ </SettingCol>
36
+ </div>
37
+ )
38
+ }
@@ -6,7 +6,7 @@
6
6
  * - click header to sort
7
7
  */
8
8
 
9
- import React from 'react'
9
+ import { Component } from '../common/react-subx'
10
10
  import classnames from 'classnames'
11
11
  import { isEqual, pick, find, isNull, isArray, isUndefined } from 'lodash-es'
12
12
  import generate from '../../common/uid'
@@ -17,24 +17,32 @@ import {
17
17
  maxDragMove,
18
18
  sftpControlHeight,
19
19
  eventTypes,
20
- paneMap,
21
- commonActions
20
+ paneMap
22
21
  } from '../../common/constants'
23
22
  import copy from 'json-deep-copy'
24
23
  import FileSection from './file-item'
25
24
  import PagedList from './paged-list'
26
25
  import {
27
26
  DownOutlined,
28
- UpOutlined
27
+ UpOutlined,
28
+ CheckOutlined
29
29
  } from '@ant-design/icons'
30
+ import IconHolder from '../context-menu/icon-holder'
30
31
 
31
32
  const { prefix } = window
32
33
  const e = prefix('sftp')
33
34
 
34
- export default class FileListTable extends React.Component {
35
+ export default class FileListTable extends Component {
35
36
  constructor (props) {
36
37
  super(props)
37
- this.state = this.initFromProps()
38
+ this.state = {
39
+ ...this.initFromProps(),
40
+ showContextMenu: false,
41
+ contextMenuPos: {
42
+ left: 0,
43
+ top: 0
44
+ }
45
+ }
38
46
  }
39
47
 
40
48
  componentDidMount () {
@@ -61,10 +69,37 @@ export default class FileListTable extends React.Component {
61
69
  }
62
70
 
63
71
  componentWillUnmount () {
64
- window.removeEventListener('message', this.onContextAction)
65
72
  window.removeEventListener('message', this.onMsg)
66
73
  }
67
74
 
75
+ setOnCloseEvent = () => {
76
+ const dom = document
77
+ .querySelector('.ant-drawer')
78
+ if (dom) {
79
+ dom.addEventListener('click', this.onTriggerClose)
80
+ }
81
+ document
82
+ .getElementById('outside-context')
83
+ .addEventListener('click', this.onTriggerClose)
84
+ }
85
+
86
+ onTriggerClose = (e) => {
87
+ if (e.target.closest('.context-menu')) {
88
+ return null
89
+ }
90
+ this.setState({
91
+ showContextMenu: false
92
+ })
93
+ const dom = document
94
+ .querySelector('.ant-drawer')
95
+ if (dom) {
96
+ dom.removeEventListener('click', this.onTriggerClose)
97
+ }
98
+ document
99
+ .getElementById('outside-context')
100
+ .removeEventListener('click', this.onTriggerClose)
101
+ }
102
+
68
103
  toVisible = (prevProps, props) => {
69
104
  return (
70
105
  prevProps.pane === paneMap.ssh ||
@@ -128,7 +163,7 @@ export default class FileListTable extends React.Component {
128
163
  }
129
164
 
130
165
  getPropsDefault = () => {
131
- return [
166
+ return this.props.store.config.filePropsEnabled || [
132
167
  'name',
133
168
  'size',
134
169
  'modifyTime'
@@ -229,10 +264,7 @@ export default class FileListTable extends React.Component {
229
264
  }
230
265
 
231
266
  computePos = (e, height) => {
232
- return {
233
- left: e.clientX,
234
- top: e.clientY
235
- }
267
+ return e.target.getBoundingClientRect()
236
268
  }
237
269
 
238
270
  onToggleProp = name => {
@@ -244,41 +276,22 @@ export default class FileListTable extends React.Component {
244
276
  : [...names, name]
245
277
  const props = all.filter(g => newProps.includes(g))
246
278
  const update = this.initFromProps(props)
279
+ this.props.store.setConfig({
280
+ filePropsEnabled: props
281
+ })
247
282
  this.setState(update)
248
283
  }
249
284
 
250
- onContextAction = e => {
251
- const {
252
- action,
253
- id,
254
- args = [],
255
- func
256
- } = e.data || {}
257
- if (
258
- action !== commonActions.clickContextMenu ||
259
- id !== this.uid ||
260
- !this[func]
261
- ) {
262
- return false
263
- }
264
- window.removeEventListener('message', this.onContextAction)
265
- this[func](...args)
266
- }
267
-
268
285
  handleContextMenu = e => {
269
286
  e && e.preventDefault()
270
- const items = this.renderContext()
271
287
  const pos = e
272
288
  ? this.computePos(e)
273
289
  : this.pos
274
- this.pos = pos
275
- this.uid = generate()
276
- window.store.openContextMenu({
277
- id: this.uid,
278
- items,
279
- pos
290
+ this.setState({
291
+ contextMenuPos: pos,
292
+ showContextMenu: true
280
293
  })
281
- window.addEventListener('message', this.onContextAction)
294
+ this.setOnCloseEvent()
282
295
  }
283
296
 
284
297
  onClickName = (e) => {
@@ -318,18 +331,23 @@ export default class FileListTable extends React.Component {
318
331
  const selected = selectedNames.includes(p)
319
332
  const disabled = !i
320
333
  const cls = classnames(
334
+ 'context-item',
321
335
  { selected },
322
336
  { unselected: !selected }
323
337
  )
324
- return {
325
- func: 'onToggleProp',
326
- icon: disabled || selected ? 'CheckOutlined' : 'IconHolder',
327
- text: e(p),
328
- disabled,
329
- args: [p],
330
- noCloseMenu: true,
331
- className: cls
338
+ const icon = disabled || selected ? <CheckOutlined /> : <IconHolder />
339
+ const obj = {
340
+ className: cls,
341
+ onClick: () => this.onToggleProp(p)
332
342
  }
343
+ return (
344
+ <div
345
+ {...obj}
346
+ key={p}
347
+ >
348
+ {icon} {e(p)}
349
+ </div>
350
+ )
333
351
  })
334
352
  }
335
353
 
@@ -519,6 +537,40 @@ export default class FileListTable extends React.Component {
519
537
  )
520
538
  }
521
539
 
540
+ renderContextMenu = () => {
541
+ const {
542
+ showContextMenu
543
+ } = this.state
544
+ if (!showContextMenu) {
545
+ return null
546
+ }
547
+ const {
548
+ left,
549
+ top
550
+ } = this.state.contextMenuPos
551
+ const outerProps = {
552
+ className: 'context-menu file-header-context-menu',
553
+ style: {
554
+ left: left + 'px',
555
+ top: top + 'px'
556
+ }
557
+ }
558
+ const innerProps = {
559
+ className: 'context-menu-inner'
560
+ }
561
+ return (
562
+ <div
563
+ {...outerProps}
564
+ >
565
+ <div
566
+ {...innerProps}
567
+ >
568
+ {this.renderContext()}
569
+ </div>
570
+ </div>
571
+ )
572
+ }
573
+
522
574
  render () {
523
575
  const { fileList, height, type } = this.props
524
576
  const tableHeaderHeight = 30
@@ -539,6 +591,7 @@ export default class FileListTable extends React.Component {
539
591
  return (
540
592
  <div className={cls}>
541
593
  {this.renderTableHeader()}
594
+ {this.renderContextMenu()}
542
595
  <div
543
596
  {...props}
544
597
  >
@@ -990,6 +990,7 @@ export default class Sftp extends Component {
990
990
  const loading = this.state[`${type}Loading`]
991
991
  const { host, username } = this.props.tab
992
992
  const listProps = {
993
+ store: window.store,
993
994
  id,
994
995
  type,
995
996
  ...this.props,
@@ -188,4 +188,7 @@
188
188
 
189
189
  .pager-wrap
190
190
  z-index 4
191
- position relative
191
+ position relative
192
+ .file-header-context-menu
193
+ position fixed
194
+ z-index 999
@@ -2,7 +2,7 @@ import createTitle from '../../common/create-title'
2
2
 
3
3
  export function createTransferProps (props) {
4
4
  return {
5
- title: createTitle(props.tab),
5
+ title: createTitle(props.tab, false),
6
6
  tabId: props.tab.id,
7
7
  sessionId: props.sessionId
8
8
  }
@@ -258,7 +258,7 @@ export default class Tabs extends React.Component {
258
258
  : tabsWidthAll
259
259
  const w1 = isMacJs ? 30 : windowControlWidth
260
260
  const style = {
261
- width: width - w1 - 136
261
+ width: width - w1 - 166
262
262
  }
263
263
  return (
264
264
  <div
@@ -358,9 +358,14 @@ class Tab extends Component {
358
358
  sshTunnelLocalHost = '127.0.0.1',
359
359
  name
360
360
  } = obj
361
- let tunnel = sshTunnel === 'forwardRemoteToLocal'
362
- ? `-> ${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort} -> ${sshTunnelLocalHost}:${sshTunnelLocalPort}`
363
- : `-> ${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort} -> ${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
361
+ let tunnel
362
+ if (sshTunnel === 'dynamicForward') {
363
+ tunnel = `sock5://${sshTunnelLocalHost}:${sshTunnelLocalPort}`
364
+ } else {
365
+ tunnel = sshTunnel === 'forwardRemoteToLocal'
366
+ ? `-> ${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort} -> ${sshTunnelLocalHost}:${sshTunnelLocalPort}`
367
+ : `-> ${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort} -> ${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
368
+ }
364
369
  if (error) {
365
370
  tunnel = `error: ${tunnel}`
366
371
  }
@@ -371,7 +376,7 @@ class Tab extends Component {
371
376
  })
372
377
  return (
373
378
  <div>
374
- <div>${title}</div>
379
+ <div>{title}</div>
375
380
  {list}
376
381
  </div>
377
382
  )
@@ -1025,8 +1025,9 @@ class Term extends Component {
1025
1025
  const cmd = `ssh ${title.split(/\s/g)[0]}\r`
1026
1026
  return this.attachAddon._sendData(cmd)
1027
1027
  }
1028
- if (startDirectory) {
1029
- const cmd = `cd ${startDirectory}\r`
1028
+ const startFolder = startDirectory || window.initFolder
1029
+ if (startFolder) {
1030
+ const cmd = `cd "${startFolder}"\r`
1030
1031
  this.attachAddon._sendData(cmd)
1031
1032
  }
1032
1033
  if (runScripts && runScripts.length) {
@@ -1048,8 +1049,6 @@ class Term extends Component {
1048
1049
  }
1049
1050
  }
1050
1051
 
1051
- count = 0
1052
-
1053
1052
  setStatus = status => {
1054
1053
  const id = this.props.tab?.id
1055
1054
  this.props.editTab(id, {
@@ -1093,7 +1092,7 @@ class Term extends Component {
1093
1092
  server = ''
1094
1093
  } = config
1095
1094
  const { sessionId, terminalIndex, id, logName } = this.props
1096
- const tab = deepCopy(this.props.tab || {})
1095
+ const tab = window.store.applyProfile(deepCopy(this.props.tab || {}))
1097
1096
  const {
1098
1097
  srcId, from = 'bookmarks',
1099
1098
  type,
@@ -1126,6 +1125,7 @@ class Term extends Component {
1126
1125
  'execLinuxArgs',
1127
1126
  'debug'
1128
1127
  ]),
1128
+ keepaliveInterval: tab.keepaliveInterval === undefined ? config.keepaliveInterval : tab.keepaliveInterval,
1129
1129
  sessionId,
1130
1130
  tabId: id,
1131
1131
  srcTabId: tab.id,
@@ -0,0 +1,66 @@
1
+ /**
2
+ * web form
3
+ */
4
+
5
+ import {
6
+ Input,
7
+ Form,
8
+ Button
9
+ } from 'antd'
10
+ import { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
11
+
12
+ const FormItem = Form.Item
13
+ const { prefix } = window
14
+ const e = prefix('form')
15
+ const s = prefix('sftp')
16
+
17
+ export default function VncForm (props) {
18
+ const [form] = Form.useForm()
19
+
20
+ const initialValues = props.types.reduce((acc, cur) => {
21
+ acc[cur] = ''
22
+ return acc
23
+ }, {})
24
+ function renderCommon () {
25
+ const {
26
+ types
27
+ } = props
28
+ return types.map((type, index) => {
29
+ const Elem = type === 'password' ? Input.Password : Input
30
+ return (
31
+ <FormItem
32
+ {...formItemLayout}
33
+ label={e(type)}
34
+ name={type}
35
+ key={type}
36
+ autoFocus={index === 0}
37
+ >
38
+ <Elem />
39
+ </FormItem>
40
+ )
41
+ })
42
+ }
43
+
44
+ return (
45
+ <Form
46
+ form={form}
47
+ onFinish={props.handleFinish}
48
+ initialValues={initialValues}
49
+ name='vnc-form'
50
+ >
51
+ <div className='pd3t pd1b'>
52
+ {renderCommon()}
53
+ <FormItem
54
+ {...tailFormItemLayout}
55
+ >
56
+ <Button
57
+ type='primary'
58
+ htmlType='submit'
59
+ >
60
+ {s('submit')}
61
+ </Button>
62
+ </FormItem>
63
+ </div>
64
+ </Form>
65
+ )
66
+ }