@electerm/electerm-react 1.51.8 → 1.51.18

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 (56) hide show
  1. package/client/common/constants.js +16 -1
  2. package/client/common/default-setting.js +0 -1
  3. package/client/common/is-color-dark.js +15 -11
  4. package/client/common/new-terminal.js +2 -5
  5. package/client/common/reverse-color.js +12 -0
  6. package/client/common/ws.js +4 -1
  7. package/client/components/bookmark-form/render-connection-hopping.jsx +25 -2
  8. package/client/components/bookmark-form/ssh-form.jsx +1 -2
  9. package/client/components/bookmark-form/tree-delete.jsx +5 -10
  10. package/client/components/bookmark-form/use-ui.jsx +1 -2
  11. package/client/components/common/connection-hopping-warning-text.jsx +36 -0
  12. package/client/components/common/drag-handle.jsx +60 -0
  13. package/client/components/common/drag-handle.styl +12 -0
  14. package/client/components/layout/layout.jsx +3 -2
  15. package/client/components/main/connection-hopping-warnning.jsx +45 -0
  16. package/client/components/main/error-wrapper.jsx +120 -5
  17. package/client/components/main/main.jsx +23 -3
  18. package/client/components/main/upgrade.jsx +4 -9
  19. package/client/components/main/wrapper.styl +2 -1
  20. package/client/components/profile/profile-form-ssh.jsx +1 -1
  21. package/client/components/rdp/resolution-edit.jsx +3 -5
  22. package/client/components/session/session.jsx +7 -2
  23. package/client/components/setting-panel/setting-common.jsx +28 -7
  24. package/client/components/setting-panel/setting-terminal.jsx +7 -4
  25. package/client/components/sftp/confirm-modal-store.jsx +3 -4
  26. package/client/components/sftp/sftp-entry.jsx +2 -5
  27. package/client/components/sftp/transfer-conflict-store.jsx +1 -3
  28. package/client/components/sftp/transport-action-store.jsx +21 -10
  29. package/client/components/side-panel-r/side-panel-r.jsx +13 -40
  30. package/client/components/sidebar/bookmark-select.jsx +0 -3
  31. package/client/components/sidebar/index.jsx +1 -6
  32. package/client/components/sidebar/side-panel.jsx +27 -51
  33. package/client/components/sidebar/sidebar.styl +0 -9
  34. package/client/components/ssh-config/load-ssh-configs.jsx +106 -0
  35. package/client/components/ssh-config/ssh-config-item.jsx +26 -0
  36. package/client/components/ssh-config/ssh-config-load-notify.jsx +60 -0
  37. package/client/components/tabs/tab.jsx +9 -10
  38. package/client/components/tabs/tabs.styl +6 -1
  39. package/client/components/terminal/index.jsx +4 -18
  40. package/client/components/tree-list/bookmark-toolbar.jsx +203 -0
  41. package/client/components/tree-list/bookmark-transport.jsx +2 -0
  42. package/client/components/tree-list/tree-list.jsx +25 -32
  43. package/client/store/bookmark-group.js +2 -13
  44. package/client/store/bookmark.js +43 -1
  45. package/client/store/common.js +2 -8
  46. package/client/store/index.js +45 -50
  47. package/client/store/init-state.js +16 -20
  48. package/client/store/load-data.js +5 -10
  49. package/client/store/setting.js +8 -15
  50. package/client/store/sync.js +0 -1
  51. package/client/store/tab.js +22 -11
  52. package/client/store/terminal-theme.js +1 -1
  53. package/client/store/transfer-list.js +0 -8
  54. package/client/store/ui-theme.js +0 -9
  55. package/client/store/watch.js +8 -8
  56. package/package.json +1 -1
@@ -63,12 +63,9 @@ export default class Upgrade extends PureComponent {
63
63
  }
64
64
 
65
65
  changeProps = (update) => {
66
- window.store.storeAssign({
67
- _upgradeInfo: JSON.stringify({
68
- ...this.props.upgradeInfo,
69
- ...update
70
- })
71
- })
66
+ Object.assign(
67
+ window.store.upgradeInfo, update
68
+ )
72
69
  }
73
70
 
74
71
  handleMinimize = () => {
@@ -79,9 +76,7 @@ export default class Upgrade extends PureComponent {
79
76
  }
80
77
 
81
78
  handleClose = () => {
82
- window.store.storeAssign({
83
- _upgradeInfo: '{}'
84
- })
79
+ window.store.upgradeInfo = {}
85
80
  }
86
81
 
87
82
  onData = (upgradePercent) => {
@@ -9,10 +9,11 @@
9
9
  // .pinned .sessions
10
10
  // margin-left 343px
11
11
  .error-wrapper
12
- background main-light
12
+ background main
13
13
  height 100%
14
14
  position fixed
15
15
  color text
16
+ overflow-y scroll
16
17
  .init-wrap
17
18
  .loading-data
18
19
  position fixed
@@ -9,7 +9,7 @@ const FormItem = Form.Item
9
9
  const e = window.translate
10
10
 
11
11
  export default function ProfileFormSsh (props) {
12
- const { form } = props.store
12
+ const { form } = props
13
13
  return (
14
14
  <>
15
15
  <FormItem
@@ -7,22 +7,20 @@ import uid from '../../common/uid'
7
7
 
8
8
  export default function Resolutions (props) {
9
9
  function remove (id) {
10
- const { resolutions } = props
10
+ const { resolutions } = window.store
11
11
  const index = resolutions.findIndex(d => d.id === id)
12
12
  if (index < 0) {
13
13
  return
14
14
  }
15
15
  resolutions.splice(index, 1)
16
- window.store.setState('resolutions', resolutions)
17
16
  }
18
17
 
19
18
  function submit (data) {
20
- const { resolutions } = props
19
+ const { resolutions } = window.store
21
20
  resolutions.push({
22
21
  ...data,
23
22
  id: uid()
24
23
  })
25
- window.store.setState('resolutions', resolutions)
26
24
  }
27
25
 
28
26
  const {
@@ -37,7 +35,7 @@ export default function Resolutions (props) {
37
35
  }
38
36
  const modalProps = {
39
37
  footer: null,
40
- visible: true,
38
+ open: true,
41
39
  onCancel: () => toggleResolutionEdit()
42
40
  }
43
41
  const resList = {
@@ -14,7 +14,8 @@ import {
14
14
  CloseOutlined
15
15
  } from '@ant-design/icons'
16
16
  import {
17
- Tooltip
17
+ Tooltip,
18
+ message
18
19
  } from 'antd'
19
20
  import { pick } from 'lodash-es'
20
21
  import generate from '../../common/uid'
@@ -150,8 +151,12 @@ export default class SessionWrapper extends Component {
150
151
  }
151
152
 
152
153
  toggleCheckSftpPathFollowSsh = () => {
154
+ const nv = !this.state.sftpPathFollowSsh
155
+ if (nv) {
156
+ message.warning(e('sftpPathFollowSshTip'), 8)
157
+ }
153
158
  this.setState({
154
- sftpPathFollowSsh: !this.state.sftpPathFollowSsh
159
+ sftpPathFollowSsh: nv
155
160
  })
156
161
  }
157
162
 
@@ -1,7 +1,9 @@
1
1
  import React, { Component } from 'react'
2
2
  import {
3
3
  ArrowRightOutlined,
4
- LoadingOutlined
4
+ LoadingOutlined,
5
+ SunOutlined,
6
+ MoonOutlined
5
7
  } from '@ant-design/icons'
6
8
  import {
7
9
  message,
@@ -12,7 +14,8 @@ import {
12
14
  Alert,
13
15
  Button,
14
16
  Table,
15
- Space
17
+ Space,
18
+ Tag
16
19
  } from 'antd'
17
20
  import deepCopy from 'json-deep-copy'
18
21
  import {
@@ -26,6 +29,7 @@ import createEditLangLink from '../../common/create-lang-edit-link'
26
29
  import StartSession from './start-session-select'
27
30
  import HelpIcon from '../common/help-icon'
28
31
  import delay from '../../common/wait.js'
32
+ import isColorDark from '../../common/is-color-dark'
29
33
  import './setting.styl'
30
34
 
31
35
  const { Option } = Select
@@ -501,8 +505,8 @@ export default class SettingCommon extends Component {
501
505
  customCss
502
506
  } = props.config
503
507
  const {
504
- langs
505
- } = this.props.store
508
+ langs = []
509
+ } = window.et
506
510
  const terminalThemes = props.store.getSidebarList(settingMap.terminalThemes)
507
511
  const [modifier, key] = hotkey.split('+')
508
512
  const pops = {
@@ -581,9 +585,27 @@ export default class SettingCommon extends Component {
581
585
  >
582
586
  {
583
587
  terminalThemes.map(l => {
584
- const { id, name } = l
588
+ const { id, name, uiThemeConfig } = l
589
+ const { main, text } = uiThemeConfig
590
+ const isDark = isColorDark(main)
591
+ const txt = isDark ? <MoonOutlined /> : <SunOutlined />
592
+ const tag = (
593
+ <Tag
594
+ color={main}
595
+ className='mg1l'
596
+ style={
597
+ {
598
+ color: text
599
+ }
600
+ }
601
+ >
602
+ {txt}
603
+ </Tag>
604
+ )
585
605
  return (
586
- <Option key={id} value={id}>{name}</Option>
606
+ <Option key={id} value={id}>
607
+ {tag} {name}
608
+ </Option>
587
609
  )
588
610
  })
589
611
  }
@@ -633,7 +655,6 @@ export default class SettingCommon extends Component {
633
655
  {
634
656
  [
635
657
  'autoRefreshWhenSwitchToSftp',
636
- 'hideSshConfig',
637
658
  'showHiddenFilesOnSftpStart',
638
659
  'screenReaderMode',
639
660
  'initDefaultTabOnStart',
@@ -18,7 +18,8 @@ import deepCopy from 'json-deep-copy'
18
18
  import {
19
19
  noTerminalBgValue,
20
20
  rendererTypes,
21
- regexHelpLink
21
+ regexHelpLink,
22
+ terminalTypes
22
23
  } from '../../common/constants'
23
24
  import defaultSettings from '../../common/default-setting'
24
25
  import ShowItem from '../common/show-item'
@@ -66,7 +67,9 @@ export default class SettingTerminal extends Component {
66
67
 
67
68
  onChangeValue = (value, name) => {
68
69
  if (name === 'useSystemTitleBar') {
69
- message.info(e('useSystemTitleBarTip'), 5)
70
+ message.info(e('useSystemTitleBarTip'), 8)
71
+ } else if (name === 'sftpPathFollowSsh' && value) {
72
+ message.warn(e('sftpPathFollowSshTip'), 8)
70
73
  }
71
74
  this.saveConfig({
72
75
  [name]: value
@@ -304,7 +307,7 @@ export default class SettingTerminal extends Component {
304
307
  }
305
308
 
306
309
  renderDefaultTerminalType = () => {
307
- const opts = this.props.config.terminalTypes.map(mapper)
310
+ const opts = terminalTypes.map(mapper)
308
311
  return (
309
312
  <AutoComplete
310
313
  options={opts}
@@ -364,7 +367,7 @@ export default class SettingTerminal extends Component {
364
367
  }
365
368
 
366
369
  renderFontFamily = () => {
367
- const { fonts } = this.props.store
370
+ const { fonts = [] } = window.et
368
371
  const { fontFamily } = this.props.config
369
372
  const props = {
370
373
  mode: 'multiple',
@@ -11,6 +11,7 @@ import { FolderOutlined, FileOutlined } from '@ant-design/icons'
11
11
  import {
12
12
  fileActions
13
13
  } from '../../common/constants'
14
+ import deepCopy from 'json-deep-copy'
14
15
  import postMessage from '../../common/post-msg'
15
16
 
16
17
  const e = window.translate
@@ -25,9 +26,7 @@ function formatTimeAuto (strOrDigit) {
25
26
  export default function ConfirmModalStore (props) {
26
27
  function act (action) {
27
28
  const { transferToConfirm } = props
28
- window.store.setState(
29
- 'transferToConfirm', {}
30
- )
29
+ window.store.transferToConfirm = {}
31
30
  const {
32
31
  fromFile: {
33
32
  id: fileId
@@ -39,7 +38,7 @@ export default function ConfirmModalStore (props) {
39
38
  transferGroupId,
40
39
  fileId,
41
40
  id,
42
- transfer: transferToConfirm,
41
+ transfer: deepCopy(transferToConfirm),
43
42
  action
44
43
  })
45
44
  }
@@ -14,7 +14,6 @@ import {
14
14
  typeMap, maxSftpHistory, paneMap,
15
15
  eventTypes,
16
16
  fileTypeMap,
17
- terminalSshConfigType,
18
17
  terminalSerialType,
19
18
  unexpectedPacketErrorDesc,
20
19
  sftpRetryInterval,
@@ -207,7 +206,7 @@ export default class Sftp extends Component {
207
206
  action === commonActions.sftpList &&
208
207
  sessionId === this.props.sessionId
209
208
  ) {
210
- this[type + 'List']()
209
+ this[type + 'ListDebounce']()
211
210
  }
212
211
  }
213
212
 
@@ -482,9 +481,7 @@ export default class Sftp extends Component {
482
481
 
483
482
  shouldRenderRemote = () => {
484
483
  const { props } = this
485
- return props.tab?.host &&
486
- props.tab?.type !== terminalSshConfigType &&
487
- props.tab?.type !== terminalSerialType
484
+ return props.tab?.host && props.tab?.type !== terminalSerialType
488
485
  }
489
486
 
490
487
  initLocalAll = () => {
@@ -128,9 +128,7 @@ export default class TransferConflictStore extends PureComponent {
128
128
  if (this.props.transferToConfirm.id) {
129
129
  return
130
130
  }
131
- window.store.setState(
132
- 'transferToConfirm', tr
133
- )
131
+ window.store.transferToConfirm = tr
134
132
  }
135
133
 
136
134
  onDecision = (event) => {
@@ -98,18 +98,11 @@ export default class TransportAction extends Component {
98
98
  typeTo,
99
99
  next
100
100
  } = transfer
101
- const cb = this[typeTo + 'List']
102
101
  const finishTime = Date.now()
103
- if (next) {
104
- setTimeout(() => {
105
- window.store.fileTransfers.splice(
106
- this.props.index, 0, copy(next)
107
- )
108
- }, 0)
109
- }
110
102
  if (!config.disableTransferHistory) {
103
+ const r = copy(transfer)
111
104
  delete transfer.next
112
- Object.assign(transfer, update, {
105
+ Object.assign(r, update, {
113
106
  finishTime,
114
107
  startTime: this.startTime,
115
108
  size: transfer.fromFile.size,
@@ -117,9 +110,27 @@ export default class TransportAction extends Component {
117
110
  speed: format(transfer.fromFile.size, this?.startTime)
118
111
  })
119
112
  window.store.addTransferHistory(
120
- transfer
113
+ r
121
114
  )
122
115
  }
116
+ const cbs = [
117
+ this[typeTo + 'List']
118
+ ]
119
+ if (next) {
120
+ cbs.push(() => {
121
+ setTimeout(
122
+ () => {
123
+ window.store.fileTransfers.splice(
124
+ this.props.index, 0, copy(next)
125
+ )
126
+ },
127
+ 100
128
+ )
129
+ })
130
+ }
131
+ const cb = () => {
132
+ cbs.forEach(cb => cb())
133
+ }
123
134
  this.cancel(cb)
124
135
  }
125
136
 
@@ -1,4 +1,5 @@
1
- import React, { useRef, memo } from 'react'
1
+ import React, { memo } from 'react'
2
+ import DragHandle from '../common/drag-handle'
2
3
  import './right-side-panel.styl'
3
4
  import {
4
5
  CloseCircleOutlined,
@@ -17,43 +18,12 @@ export default memo(function RightSidePanel (
17
18
  return null
18
19
  }
19
20
 
20
- const dragStart = useRef(false)
21
- const clientX = useRef(0)
22
-
23
- function handleMousedown (e) {
24
- dragStart.current = true
25
- clientX.current = e.clientX
26
- document.body.addEventListener('mouseup', handleMouseup)
27
- document.body.addEventListener('mousemove', handleMousemove)
28
- }
29
-
30
- function handleMouseup (e) {
31
- dragStart.current = false
32
- const {
33
- clientX: cx
34
- } = e
35
- let nw = clientX.current - cx + rightPanelWidth
36
- if (nw < 400) {
37
- nw = 400
38
- } else if (nw > 1000) {
39
- nw = 1000
40
- }
21
+ function onDragEnd (nw) {
41
22
  window.store.setRightSidePanelWidth(nw)
42
- document.body.removeEventListener('mouseup', handleMouseup)
43
- document.body.removeEventListener('mousemove', handleMousemove)
44
23
  }
45
24
 
46
- function handleMousemove (e) {
47
- const {
48
- clientX: cx
49
- } = e
25
+ function onDragMove (nw) {
50
26
  const el = document.getElementById('right-side-panel')
51
- let nw = clientX.current - cx + rightPanelWidth
52
- if (nw < 400) {
53
- nw = 400
54
- } else if (nw > 1000) {
55
- nw = 1000
56
- }
57
27
  el.style.width = nw + 'px'
58
28
  }
59
29
 
@@ -77,16 +47,19 @@ export default memo(function RightSidePanel (
77
47
  className: 'right-side-panel-pin right-side-panel-controls' + (rightPanelPinned ? ' pinned' : ''),
78
48
  onClick: togglePin
79
49
  }
80
-
50
+ const dragProps = {
51
+ min: 400,
52
+ max: 1000,
53
+ width: rightPanelWidth,
54
+ onDragEnd,
55
+ onDragMove,
56
+ left: false
57
+ }
81
58
  return (
82
59
  <div
83
60
  {...panelProps}
84
61
  >
85
- <div
86
- className='drag-handle'
87
- onMouseDown={handleMousedown}
88
- draggable={false}
89
- />
62
+ <DragHandle {...dragProps} />
90
63
  <CloseCircleOutlined
91
64
  className='right-side-panel-close right-side-panel-controls'
92
65
  onClick={onClose}
@@ -29,9 +29,6 @@ export default auto(function BookmarkSelect (props) {
29
29
  listStyle,
30
30
  staticList: true
31
31
  }
32
- if (!store.config.hideSshConfig) {
33
- base.bookmarks.push(...store.sshConfigItems)
34
- }
35
32
  const propsTree = {
36
33
  ...base,
37
34
  shouldConfirmDel: true,
@@ -65,12 +65,7 @@ export default memo(function Sidebar (props) {
65
65
  }
66
66
 
67
67
  const handleShowUpgrade = () => {
68
- store.storeAssign({
69
- _upgradeInfo: JSON.stringify({
70
- ...props.store.upgradeInfo,
71
- showUpgradeModal: true
72
- })
73
- })
68
+ window.store.upgradeInfo.showUpgradeModal = true
74
69
  }
75
70
 
76
71
  const {
@@ -1,62 +1,38 @@
1
- import { PureComponent } from 'react'
2
- export default class SidePanel extends PureComponent {
3
- handleMousedown = (e) => {
4
- e.stopPropagation()
5
- this.dragStart = true
6
- this.clientX = e.clientX
7
- window.addEventListener('mouseup', this.handleMouseup)
8
- window.addEventListener('mousemove', this.handleMousemove)
9
- }
1
+ import { useCallback } from 'react'
2
+ import DragHandle from '../common/drag-handle'
10
3
 
11
- handleMouseup = (e) => {
12
- this.dragStart = false
13
- const {
14
- clientX
15
- } = e
16
- let nw = clientX - this.clientX + this.props.leftSidebarWidth
17
- if (nw < 150) {
18
- nw = 150
19
- } else if (nw > 600) {
20
- nw = 600
21
- }
22
- this.props.setLeftSidePanelWidth(nw)
4
+ export default function SidePanel (props) {
5
+ const onDragEnd = useCallback((nw) => {
6
+ props.setLeftSidePanelWidth(nw)
23
7
  window.store.onResize()
24
- window.removeEventListener('mouseup', this.handleMouseup)
25
- window.removeEventListener('mousemove', this.handleMousemove)
26
- }
8
+ }, [props])
27
9
 
28
- handleMousemove = (e) => {
29
- const {
30
- clientX
31
- } = e
10
+ const onDragMove = useCallback((nw) => {
32
11
  const el = document.getElementById('side-panel')
33
- let nw = clientX - this.clientX + this.props.leftSidebarWidth
34
- if (nw < 343) {
35
- nw = 343
36
- } else if (nw > 600) {
37
- nw = 600
38
- }
39
12
  el.style.width = nw + 'px'
40
13
  const el1 = document.querySelector('.sessions')
41
14
  if (el1) {
42
15
  el1.style.left = (nw + 43) + 'px'
43
16
  }
17
+ }, [props.leftSidebarWidth])
18
+ const dragProps = {
19
+ min: 343,
20
+ max: 600,
21
+ width: props.leftSidebarWidth,
22
+ onDragEnd,
23
+ onDragMove,
24
+ left: true
44
25
  }
45
-
46
- render () {
47
- return (
48
- <div
49
- {...this.props.sideProps}
50
- id='side-panel'
51
- draggable={false}
52
- >
53
- <div
54
- className='drag-handle'
55
- onMouseDown={this.handleMousedown}
56
- draggable={false}
57
- />
58
- {this.props.children}
59
- </div>
60
- )
61
- }
26
+ return (
27
+ <div
28
+ {...props.sideProps}
29
+ id='side-panel'
30
+ draggable={false}
31
+ >
32
+ <DragHandle
33
+ {...dragProps}
34
+ />
35
+ {props.children}
36
+ </div>
37
+ )
62
38
  }
@@ -93,15 +93,6 @@
93
93
  &:hover
94
94
  color text-light
95
95
 
96
- .drag-handle
97
- position absolute
98
- right 0
99
- top 0
100
- width 3px
101
- bottom 0
102
- cursor ew-resize
103
- display none
104
- z-index 201
105
96
  // Hover.css (http://ianlunn.github.io/Hover/)
106
97
  // Version: 2.3.2
107
98
  // Author: Ian Lunn @IanLunn
@@ -0,0 +1,106 @@
1
+ import {
2
+ Modal,
3
+ Spin,
4
+ Button,
5
+ Empty
6
+ } from 'antd'
7
+ import { useState, useEffect } from 'react'
8
+ import SshConfigItem from './ssh-config-item'
9
+ import * as ls from '../../common/safe-local-storage'
10
+ import {
11
+ sshConfigLoadKey
12
+ } from '../../common/constants'
13
+ import { ReloadOutlined } from '@ant-design/icons'
14
+
15
+ const e = window.translate
16
+
17
+ export default function LoadSshConfigs (props) {
18
+ const [loading, setLoading] = useState(false)
19
+ const { sshConfigs } = props
20
+
21
+ const {
22
+ store
23
+ } = window
24
+ const {
25
+ showSshConfigModal
26
+ } = props
27
+ const handleCancel = function () {
28
+ store.showSshConfigModal = false
29
+ }
30
+ const loadSshConfig = async function () {
31
+ setLoading(true)
32
+ await store.fetchSshConfigItems()
33
+ setLoading(false)
34
+ }
35
+
36
+ const handleLoadSshConfig = function () {
37
+ store.showSshConfigModal = false
38
+ store.addSshConfigs(sshConfigs)
39
+ ls.setItem(sshConfigLoadKey, 'yes')
40
+ }
41
+
42
+ const renderList = function () {
43
+ if (!sshConfigs.length) {
44
+ return (
45
+ <Empty />
46
+ )
47
+ }
48
+ return sshConfigs.map((d, i) => {
49
+ return (
50
+ <SshConfigItem item={d} key={d.title} />
51
+ )
52
+ })
53
+ }
54
+
55
+ useEffect(() => {
56
+ if (sshConfigs.length && ls.getItem(sshConfigLoadKey) !== 'yes') {
57
+ loadSshConfig()
58
+ }
59
+ }, [sshConfigs.length])
60
+ if (!showSshConfigModal) {
61
+ return null
62
+ }
63
+ const modProps = {
64
+ title: e('loadSshConfigs'),
65
+ footer: null,
66
+ open: true,
67
+ onCancel: handleCancel,
68
+ width: '80%'
69
+ }
70
+ return (
71
+ <Modal {...modProps}>
72
+ <Spin spinning={loading}>
73
+ <div className='pd1y'>
74
+ <Button
75
+ onClick={loadSshConfig}
76
+ disabled={loading}
77
+ className='mg1b'
78
+ >
79
+ <ReloadOutlined /> {e('reload')}
80
+ </Button>
81
+ </div>
82
+ <div className='ssh-config-list'>
83
+ {
84
+ renderList()
85
+ }
86
+ </div>
87
+ <div className='pd1y'>
88
+ <Button
89
+ type='primary'
90
+ className='mg1r mg1b'
91
+ onClick={handleLoadSshConfig}
92
+ disabled={!sshConfigs.length || loading}
93
+ >
94
+ {e('import')}
95
+ </Button>
96
+ <Button
97
+ onClick={handleCancel}
98
+ className='mg1r mg1b'
99
+ >
100
+ {e('cancel')}
101
+ </Button>
102
+ </div>
103
+ </Spin>
104
+ </Modal>
105
+ )
106
+ }
@@ -0,0 +1,26 @@
1
+ import { Tooltip } from 'antd'
2
+
3
+ export default function SshConfigItem (props) {
4
+ const { item } = props
5
+
6
+ const generateTooltipContent = (item) => {
7
+ return Object.entries(item)
8
+ .filter(([key]) => key !== 'id')
9
+ .map(([key, value]) => (
10
+ <div key={key}>
11
+ <b className='mg1r'>{key}:</b>
12
+ <span>{value}</span>
13
+ </div>
14
+ ))
15
+ }
16
+
17
+ return (
18
+ <Tooltip title={generateTooltipContent(item)}>
19
+ <div>
20
+ <div className='elli pd1y pd2x'>
21
+ ssh {item.title}
22
+ </div>
23
+ </div>
24
+ </Tooltip>
25
+ )
26
+ }