@electerm/electerm-react 2.4.38 → 2.5.9

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 (37) hide show
  1. package/client/components/auth/login.jsx +1 -1
  2. package/client/components/bookmark-form/common/fields.jsx +1 -1
  3. package/client/components/bookmark-form/common/hex-input.jsx +6 -1
  4. package/client/components/bookmark-form/common/run-scripts.jsx +10 -8
  5. package/client/components/bookmark-form/common/ssh-host-selector.jsx +1 -1
  6. package/client/components/bookmark-form/common/ssh-tunnels.jsx +0 -1
  7. package/client/components/common/input-confirm-common.jsx +7 -5
  8. package/client/components/common/input-confirm.jsx +1 -3
  9. package/client/components/common/input-number-confirm.jsx +1 -3
  10. package/client/components/common/password.jsx +0 -4
  11. package/client/components/common/ref.js +2 -0
  12. package/client/components/main/main.jsx +1 -4
  13. package/client/components/quick-commands/quick-commands-list-form.jsx +24 -21
  14. package/client/components/setting-panel/keywords-form.jsx +9 -8
  15. package/client/components/setting-panel/setting-common.jsx +1 -1
  16. package/client/components/setting-panel/setting-terminal.jsx +8 -5
  17. package/client/components/setting-panel/terminal-bg-config.jsx +1 -1
  18. package/client/components/sftp/address-bar.jsx +3 -2
  19. package/client/components/sftp/address-bookmark.jsx +1 -1
  20. package/client/components/sftp/file-item.jsx +32 -46
  21. package/client/components/sftp/keyword-filter.jsx +2 -2
  22. package/client/components/sftp/list-table-ui.jsx +95 -83
  23. package/client/components/sftp/sftp-entry.jsx +26 -11
  24. package/client/components/shortcuts/shortcut-editor.jsx +1 -1
  25. package/client/components/sidebar/sidebar.styl +2 -1
  26. package/client/components/sys-menu/zoom.jsx +6 -4
  27. package/client/components/tabs/app-drag.jsx +27 -13
  28. package/client/components/tabs/tabs.styl +8 -0
  29. package/client/components/terminal/term-search.jsx +1 -2
  30. package/client/components/terminal/terminal.jsx +10 -1
  31. package/client/components/text-editor/simple-editor.jsx +1 -1
  32. package/client/components/tree-list/tree-list.jsx +4 -4
  33. package/client/components/tree-list/tree-list.styl +1 -0
  34. package/client/components/web/address-bar.jsx +2 -2
  35. package/client/store/event.js +6 -2
  36. package/client/store/store.js +8 -1
  37. package/package.json +1 -1
@@ -86,7 +86,7 @@ export default function Login () {
86
86
  readOnly={loading}
87
87
  onChange={handlePassChange}
88
88
  placeholder={e('password')}
89
- addonAfter={renderAfter()}
89
+ suffix={renderAfter()}
90
90
  onPressEnter={handleSubmit}
91
91
  />
92
92
  </div>
@@ -90,7 +90,7 @@ export function renderFormItem (item, formItemLayout, form, ctxProps, index) {
90
90
  )
91
91
  break
92
92
  case 'colorTitle':
93
- control = <InputAutoFocus addonBefore={<ColorPickerItem />} {...item.props} />
93
+ control = <InputAutoFocus prefix={<ColorPickerItem />} {...item.props} />
94
94
  break
95
95
  case 'password':
96
96
  control = <Password {...item.props} />
@@ -17,6 +17,11 @@ export const HexInput = (props) => {
17
17
  return <CheckOutlined className='pointer' onClick={submit} />
18
18
  }
19
19
  return (
20
- <Input addonBefore='#' {...props} value={v} onChange={handleChange} addonAfter={renderAfter()} />
20
+ <Input
21
+ prefix='#' {...props}
22
+ value={v}
23
+ onChange={handleChange}
24
+ suffix={renderAfter()}
25
+ />
21
26
  )
22
27
  }
@@ -25,14 +25,16 @@ export default function renderRunScripts () {
25
25
  name={[field.name, 'delay']}
26
26
  required
27
27
  >
28
- <InputNumber
29
- min={1}
30
- step={1}
31
- max={65535}
32
- addonBefore={e('loginScriptDelay')}
33
- rules={[{ required: true, message: e('loginScriptDelay') + ' required' }]}
34
- className='compact-input'
35
- />
28
+ <Space.Compact>
29
+ <Space.Addon>{e('loginScriptDelay')}</Space.Addon>
30
+ <InputNumber
31
+ min={1}
32
+ step={1}
33
+ max={65535}
34
+ rules={[{ required: true, message: e('loginScriptDelay') + ' required' }]}
35
+ className='compact-input'
36
+ />
37
+ </Space.Compact>
36
38
  </FormItem>
37
39
  <FormItem
38
40
  label=''
@@ -53,7 +53,7 @@ export default function SshHostSelector ({ ips = [], useIp, form, onBlur, onPast
53
53
  name='host'
54
54
  onBlur={props.onBlur}
55
55
  onPaste={e => onPaste(e, form)}
56
- addonBefore={<ColorPickerItem />}
56
+ prefix={<ColorPickerItem />}
57
57
  />
58
58
  </FormItem>
59
59
  </FormItem>
@@ -268,7 +268,6 @@ export default function renderSshTunnels (props) {
268
268
  <InputNumber
269
269
  min={1}
270
270
  max={65535}
271
- // addonBefore={e('localPort')}
272
271
  placeholder={e('port')}
273
272
  />
274
273
  </FormItem>
@@ -7,8 +7,8 @@ export default function InputConfirmCommon ({
7
7
  value,
8
8
  onChange,
9
9
  inputComponent: InputComponent,
10
- iconPlacement = 'addonAfter', // 'addonAfter', 'suffix', or 'below'
11
- extraAddonAfter,
10
+ addonBefore,
11
+ addonAfter,
12
12
  ...rest
13
13
  }) {
14
14
  const [localValue, setLocalValue] = useState(value)
@@ -50,10 +50,11 @@ export default function InputConfirmCommon ({
50
50
  : null
51
51
  const { className, ...restProps } = rest
52
52
  const cls = className ? `${className} input-confirm` : 'input-confirm'
53
- const finalAddon = extraAddonAfter || icons
53
+ const beforeAddon = addonBefore || null
54
+ const afterAddon = addonAfter || icons
54
55
  ? (
55
56
  <Space.Addon>
56
- {extraAddonAfter}
57
+ {addonAfter}
57
58
  {icons}
58
59
  </Space.Addon>
59
60
  )
@@ -70,8 +71,9 @@ export default function InputConfirmCommon ({
70
71
  return (
71
72
  <div>
72
73
  <Space.Compact className={cls}>
74
+ {beforeAddon}
73
75
  {inputElement}
74
- {finalAddon}
76
+ {afterAddon}
75
77
  </Space.Compact>
76
78
  </div>
77
79
  )
@@ -2,12 +2,10 @@ import { Input } from 'antd'
2
2
  import InputConfirmCommon from './input-confirm-common'
3
3
 
4
4
  export default function InputConfirm (props) {
5
- const { extraAddonAfter, ...rest } = props
6
5
  return (
7
6
  <InputConfirmCommon
8
- {...rest}
7
+ {...props}
9
8
  inputComponent={Input}
10
- extraAddonAfter={extraAddonAfter}
11
9
  />
12
10
  )
13
11
  }
@@ -2,12 +2,10 @@ import { InputNumber } from 'antd'
2
2
  import InputConfirmCommon from './input-confirm-common'
3
3
 
4
4
  export default function InputNumberConfirm (props) {
5
- const { addonAfter, ...rest } = props
6
5
  return (
7
6
  <InputConfirmCommon
8
- {...rest}
7
+ {...props}
9
8
  inputComponent={InputNumber}
10
- extraAddonAfter={addonAfter}
11
9
  />
12
10
  )
13
11
  }
@@ -49,9 +49,6 @@ export default forwardRef(function Password (props, ref) {
49
49
  }
50
50
  }, [props.onBlur])
51
51
 
52
- // Keep addonBefore as-is from props; must be null when caps lock is off
53
- const addonBefore = props.addonBefore ?? null
54
-
55
52
  // Show caps lock indicator inside prefix to avoid remounting the input wrapper
56
53
  let capsPrefix = null
57
54
  if (isCapsLockOn) {
@@ -75,7 +72,6 @@ export default forwardRef(function Password (props, ref) {
75
72
  <Input.Password
76
73
  {...props}
77
74
  ref={ref}
78
- addonBefore={addonBefore}
79
75
  prefix={prefix}
80
76
  onKeyDown={handleKeyEvent}
81
77
  onKeyUp={handleKeyEvent}
@@ -3,6 +3,7 @@ window.refs = new Map()
3
3
  window.refsStatic = new Map()
4
4
  window.refsTransfers = new Map()
5
5
  window.refsTabs = new Map()
6
+ window.filesRef = new Map()
6
7
 
7
8
  class Ref {
8
9
  constructor (key) {
@@ -59,3 +60,4 @@ export const refs = new Ref('refs')
59
60
  export const refsTransfers = new Ref('refsTransfers')
60
61
  export const refsStatic = new Ref('refsStatic')
61
62
  export const refsTabs = new TabsRef('refsTabs')
63
+ export const filesRef = new Ref('filesRef')
@@ -37,12 +37,8 @@ import { pick } from 'lodash-es'
37
37
  import deepCopy from 'json-deep-copy'
38
38
  import './wrapper.styl'
39
39
 
40
- function setupGlobalMessageDismiss () {
41
- }
42
-
43
40
  export default auto(function Index (props) {
44
41
  useEffect(() => {
45
- setupGlobalMessageDismiss()
46
42
  const { store } = props
47
43
  window.addEventListener('resize', store.onResize)
48
44
  setTimeout(store.triggerResize, 200)
@@ -107,6 +103,7 @@ export default auto(function Index (props) {
107
103
  'not-mac': !isMac,
108
104
  'is-win': isWin,
109
105
  pinned,
106
+ 'not-win': !isWin,
110
107
  'qm-pinned': pinnedQuickCommandBar,
111
108
  'term-fullscreen': terminalFullScreen,
112
109
  'is-main': !isSecondInstance
@@ -29,14 +29,16 @@ export default function renderQm () {
29
29
  name={[field.name, 'delay']}
30
30
  required
31
31
  >
32
- <InputNumber
33
- min={1}
34
- step={1}
35
- max={65535}
36
- addonBefore={e('delay')}
37
- placeholder={100}
38
- className='compact-input'
39
- />
32
+ <Space.Compact>
33
+ <Space.Addon>{e('delay')}</Space.Addon>
34
+ <InputNumber
35
+ min={1}
36
+ step={1}
37
+ max={65535}
38
+ placeholder={100}
39
+ className='compact-input'
40
+ />
41
+ </Space.Compact>
40
42
  </FormItem>
41
43
  <FormItem
42
44
  label=''
@@ -44,20 +46,21 @@ export default function renderQm () {
44
46
  required
45
47
  className='mg2x'
46
48
  >
47
- <Input.TextArea
48
- autoSize={{ minRows: 1 }}
49
- placeholder={e('quickCommand')}
50
- className='compact-input qm-input'
51
- onFocus={() => {
52
- focused.current = i
53
- }}
54
- />
49
+ <Space.Compact>
50
+ <Input.TextArea
51
+ autoSize={{ minRows: 1 }}
52
+ placeholder={e('quickCommand')}
53
+ className='compact-input qm-input'
54
+ onFocus={() => {
55
+ focused.current = i
56
+ }}
57
+ />
58
+ <Button
59
+ icon={<MinusCircleOutlined />}
60
+ onClick={() => remove(field.name)}
61
+ />
62
+ </Space.Compact>
55
63
  </FormItem>
56
- <Button
57
- icon={<MinusCircleOutlined />}
58
- onClick={() => remove(field.name)}
59
- className='mg24b'
60
- />
61
64
  </Space>
62
65
  )
63
66
  }
@@ -55,16 +55,17 @@ export default function KeywordForm (props) {
55
55
  name={[field.name, 'keyword']}
56
56
  rules={[{ validator: checker }]}
57
57
  >
58
- <InputConfirm
59
- addonBefore={renderBefore(field.name)}
60
- />
58
+ <Space.Compact>
59
+ <InputConfirm
60
+ addonBefore={renderBefore(field.name)}
61
+ />
62
+ <Button
63
+ icon={<MinusCircleOutlined />}
64
+ onClick={() => remove(field.name)}
65
+ />
66
+ </Space.Compact>
61
67
  </FormItem>
62
68
  </FormItem>
63
- <Button
64
- icon={<MinusCircleOutlined />}
65
- onClick={() => remove(field.name)}
66
- className='mg24b'
67
- />
68
69
  </Space>
69
70
  )
70
71
  }
@@ -442,7 +442,7 @@ export default class SettingCommon extends Component {
442
442
  onFocus: this.handleLoginPassFocus,
443
443
  onBlur: this.handleLoginPassBlur,
444
444
  onChange: this.handleChangeLoginPass,
445
- addonAfter: this.renderLoginPassAfter(),
445
+ suffix: this.renderLoginPassAfter(),
446
446
  placeholder: placeholderLogin
447
447
  }
448
448
  return (
@@ -11,7 +11,8 @@ import {
11
11
  Button,
12
12
  AutoComplete,
13
13
  Tooltip,
14
- Flex
14
+ Flex,
15
+ Space
15
16
  } from 'antd'
16
17
  import deepCopy from 'json-deep-copy'
17
18
  import {
@@ -176,23 +177,25 @@ export default class SettingTerminal extends Component {
176
177
  <Button
177
178
  onClick={this.handleChooseFolder}
178
179
  className='mg1r'
180
+ type='text'
179
181
  size='small'
180
182
  >
181
183
  {e('chooseFolder')}
182
184
  </Button>
183
185
  <Button
184
186
  size='small'
187
+ type='text'
185
188
  onClick={() => this.handleLogChange('')}
186
189
  >
187
190
  {e('reset')}
188
191
  </Button>
189
192
  </>
190
193
  ),
194
+ prefix: e('terminalLogPath'),
191
195
  addonBefore: (
192
- <>
193
- <span className='mg1r'>{e('terminalLogPath')}</span>
196
+ <Space.Addon>
194
197
  <ShowItem to={path} />
195
- </>
198
+ </Space.Addon>
196
199
  )
197
200
  }
198
201
  return (
@@ -340,7 +343,7 @@ export default class SettingTerminal extends Component {
340
343
  options={dataSource.map(this.renderBgOption)}
341
344
  >
342
345
  <InputConfirm
343
- extraAddonAfter={after}
346
+ addonAfter={after}
344
347
  />
345
348
  </AutoComplete>
346
349
  </Tooltip>
@@ -235,7 +235,7 @@ export default function TerminalBackgroundConfig ({
235
235
  options={dataSource.map(renderBgOption)}
236
236
  >
237
237
  <Input
238
- addonAfter={after}
238
+ suffix={after}
239
239
  />
240
240
  </AutoComplete>
241
241
  </Tooltip>
@@ -66,6 +66,7 @@ function renderAddonBefore (props, realPath) {
66
66
  realPath={realPath}
67
67
  host={host}
68
68
  type={type}
69
+ className='mg1r'
69
70
  onClickHistory={props.onClickHistory}
70
71
  />
71
72
  </>
@@ -134,11 +135,11 @@ export default function AddressBar (props) {
134
135
  value={path}
135
136
  onChange={e => props.onChange(e, n)}
136
137
  onPressEnter={e => props.onGoto(type, e)}
137
- addonBefore={renderAddonBefore(props, realPath)}
138
+ prefix={renderAddonBefore(props, realPath)}
138
139
  onFocus={() => props.onInputFocus(type)}
139
140
  onBlur={() => props.onInputBlur(type)}
140
141
  disabled={loadingSftp}
141
- addonAfter={
142
+ suffix={
142
143
  renderAddonAfter(isLoadingRemote, onGoto, GoIcon, type)
143
144
  }
144
145
  />
@@ -101,7 +101,7 @@ export default auto(function AddrBookmark (props) {
101
101
  placement='bottom'
102
102
  trigger='click'
103
103
  >
104
- <StarOutlined />
104
+ <StarOutlined className={props.className || ''} />
105
105
  </Popover>
106
106
  )
107
107
  })
@@ -36,11 +36,8 @@ import time from '../../common/time'
36
36
  import { filesize } from 'filesize'
37
37
  import { createTransferProps } from './transfer-common'
38
38
  import generate from '../../common/uid'
39
- import { refsStatic, refs } from '../common/ref'
39
+ import { refsStatic, refs, filesRef } from '../common/ref'
40
40
  import iconsMap from '../sys-menu/icons-map'
41
- import {
42
- Dropdown
43
- } from 'antd'
44
41
 
45
42
  const e = window.translate
46
43
 
@@ -63,6 +60,7 @@ export default class FileSection extends React.Component {
63
60
  }
64
61
 
65
62
  componentDidMount () {
63
+ filesRef.add(this.id, this)
66
64
  this.applyStyle()
67
65
  }
68
66
 
@@ -76,6 +74,7 @@ export default class FileSection extends React.Component {
76
74
  }
77
75
 
78
76
  componentWillUnmount () {
77
+ filesRef.remove(this.id)
79
78
  clearTimeout(this.timer)
80
79
  this.timer = null
81
80
  this.domRef = null
@@ -91,11 +90,11 @@ export default class FileSection extends React.Component {
91
90
  return refsStatic.get('text-editor')
92
91
  }
93
92
 
94
- handleDropdownOpenChange = (open) => {
95
- if (open) {
96
- this.forceUpdate()
97
- }
98
- }
93
+ // handleDropdownOpenChange = (open) => {
94
+ // if (open) {
95
+ // this.forceUpdate()
96
+ // }
97
+ // }
99
98
 
100
99
  applyStyle = () => {
101
100
  if (!this.domRef) {
@@ -447,20 +446,18 @@ export default class FileSection extends React.Component {
447
446
  const {
448
447
  id,
449
448
  type,
450
- isParent
449
+ isParent,
450
+ isEmpty
451
451
  } = file
452
- if (isParent) {
453
- return
452
+ if (isEmpty || isParent) {
453
+ return this.props.modifier({
454
+ selectedFiles: new Set()
455
+ })
454
456
  }
455
457
  this.props.modifier({
456
458
  lastClickedFile: file
457
459
  })
458
460
  this.onDragEnd(e)
459
- if (!id) {
460
- return this.props.modifier({
461
- selectedFiles: new Set()
462
- })
463
- }
464
461
  const selectedFilesOld = this.props.getSelectedFiles()
465
462
  const isSameSide = selectedFilesOld.length &&
466
463
  type === selectedFilesOld[0].type
@@ -519,7 +516,7 @@ export default class FileSection extends React.Component {
519
516
 
520
517
  handleBlur = () => {
521
518
  const file = copy(this.state.file)
522
- const { nameTemp, name, id, type } = this.state.file
519
+ const { nameTemp, name, type, id } = this.state.file
523
520
  if (name === nameTemp) {
524
521
  if (!id) {
525
522
  return this.cancelNew(type)
@@ -854,8 +851,8 @@ export default class FileSection extends React.Component {
854
851
  return this.props.renderDelConfirmTitle(files, true)
855
852
  }
856
853
 
857
- showModeEdit (type, id) {
858
- if (!id) {
854
+ showModeEdit (type, isRealFile) {
855
+ if (!isRealFile) {
859
856
  return false
860
857
  }
861
858
  if (type === typeMap.remote) {
@@ -865,6 +862,7 @@ export default class FileSection extends React.Component {
865
862
  }
866
863
 
867
864
  handleContextMenuCapture = (e) => {
865
+ this.props.setClickFileId(this.id)
868
866
  if (!this.isSelected(this.state.file.id)) {
869
867
  this.onClick(e)
870
868
  }
@@ -925,12 +923,13 @@ export default class FileSection extends React.Component {
925
923
  isDirectory,
926
924
  size,
927
925
  id,
926
+ isEmpty,
928
927
  isParent
929
928
  },
930
929
  selectedFiles,
931
930
  tab
932
931
  } = this.props
933
- const isRealFile = id && !isParent
932
+ const isRealFile = !isEmpty && !isParent
934
933
  const hasHost = !!tab.host
935
934
  const { enableSsh } = tab
936
935
  const isLocal = type === typeMap.local
@@ -1113,7 +1112,7 @@ export default class FileSection extends React.Component {
1113
1112
  <div className='sftp-item'>
1114
1113
  <Input
1115
1114
  value={nameTemp}
1116
- addonBefore={pre}
1115
+ prefix={pre}
1117
1116
  onChange={this.handleChange}
1118
1117
  onBlur={this.handleBlur}
1119
1118
  onPressEnter={this.handleBlur}
@@ -1202,9 +1201,7 @@ export default class FileSection extends React.Component {
1202
1201
  const props = {
1203
1202
  className,
1204
1203
  draggable: draggable && !isParent,
1205
- onDoubleClick: this.transferOrEnterDirectory,
1206
1204
  ...pick(this, [
1207
- 'onClick',
1208
1205
  'onDrag',
1209
1206
  'onDragEnter',
1210
1207
  'onDragExit',
@@ -1216,33 +1213,22 @@ export default class FileSection extends React.Component {
1216
1213
  onDragStart: onDragStart || this.onDragStart,
1217
1214
  'data-id': id,
1218
1215
  id: this.id,
1219
- 'data-is-parent': isParent ? 'y' : 'n',
1220
1216
  'data-type': type,
1221
1217
  title: file.name
1222
1218
  }
1223
- const ddProps = {
1224
- menu: {
1225
- items: this.renderContextMenu(),
1226
- onClick: this.onContextMenu
1227
- },
1228
- trigger: ['contextMenu'],
1229
- onOpenChange: this.handleDropdownOpenChange
1230
- }
1231
1219
  return (
1232
- <Dropdown {...ddProps}>
1233
- <div
1234
- ref={this.domRef}
1235
- {...props}
1236
- onContextMenu={this.handleContextMenuCapture}
1237
- >
1238
- <div className='file-bg' />
1239
- <div className='file-props-div'>
1240
- {
1241
- properties.map(this.renderProp)
1242
- }
1243
- </div>
1220
+ <div
1221
+ ref={this.domRef}
1222
+ {...props}
1223
+ onContextMenu={this.handleContextMenuCapture}
1224
+ >
1225
+ <div className='file-bg' />
1226
+ <div className='file-props-div'>
1227
+ {
1228
+ properties.map(this.renderProp)
1229
+ }
1244
1230
  </div>
1245
- </Dropdown>
1231
+ </div>
1246
1232
  )
1247
1233
  }
1248
1234
  }
@@ -37,11 +37,11 @@ export default function KeywordFilter ({ keyword, type, updateKeyword }) {
37
37
  const inputProps = {
38
38
  value: text,
39
39
  onChange: handleInputChange,
40
- addonBefore: <FilterOutlined />,
40
+ prefix: <FilterOutlined />,
41
41
  onKeyPress: handleKeyPress,
42
42
  placeholder: e('keyword'),
43
43
  className: 'keyword-filter-input',
44
- addonAfter: <CheckOutlined onClick={applyFilter} />
44
+ suffix: <CheckOutlined onClick={applyFilter} />
45
45
  }
46
46
 
47
47
  const tooltipContent = (
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { Component } from 'react'
6
+ import { Dropdown } from 'antd'
6
7
  import classnames from 'classnames'
7
8
  import FileSection from './file-item'
8
9
  import PagedList from './paged-list'
@@ -11,6 +12,7 @@ import {
11
12
  CheckOutlined
12
13
  } from '@ant-design/icons'
13
14
  import IconHolder from '../sys-menu/icon-holder'
15
+ import { filesRef } from '../common/ref'
14
16
 
15
17
  const e = window.translate
16
18
 
@@ -168,57 +170,6 @@ export default class FileListTable extends Component {
168
170
  'left'
169
171
  ]
170
172
 
171
- // saveOldStyle = () => {
172
- // const { properties } = this.state
173
- // const ids = [
174
- // ...properties,
175
- // ...splitHandles
176
- // ]
177
- // const { type, id } = this.props
178
- // const parentWidth = document.querySelector(
179
- // `#id-${id} .tw-${type} .sftp-table`
180
- // ).clientWidth
181
- // this.oldStyles = ids.reduce((prev, { id, name }) => {
182
- // const sel = `.session-current .tw-${type} .sftp-file-table-header .shi-${name || id}`
183
- // return {
184
- // ...prev,
185
- // [name || id]: {
186
- // style: pick(
187
- // document.querySelector(sel)?.style || {},
188
- // this.positionProps
189
- // ),
190
- // parentWidth
191
- // }
192
- // }
193
- // }, {})
194
- // }
195
-
196
- // changePosition = (
197
- // dom,
198
- // xDiff,
199
- // type,
200
- // style
201
- // ) => {
202
- // const realWidth = style.width
203
- // const realLeft = style.left
204
- // if (type === 'prev') {
205
- // dom.forEach(d => {
206
- // d.style.width = (realWidth + xDiff) + 'px'
207
- // })
208
- // } else if (type === 'dom') {
209
- // dom.style.left = (realLeft + xDiff) + 'px'
210
- // } else {
211
- // dom.forEach(d => {
212
- // d.style.width = (realWidth - xDiff) + 'px'
213
- // d.style.left = (realLeft + xDiff) + 'px'
214
- // })
215
- // }
216
- // }
217
-
218
- // onDragEnd = () => {}
219
-
220
- // onDoubleClick = () => this.resetWidth()
221
-
222
173
  hasPager = () => {
223
174
  const {
224
175
  pageSize
@@ -230,36 +181,29 @@ export default class FileListTable extends Component {
230
181
  return len > pageSize
231
182
  }
232
183
 
233
- // rebuildStyle = (name) => {
234
- // let { style, parentWidth } = this.oldStyles[name]
235
- // style = copy(style)
236
- // const {
237
- // type,
238
- // id
239
- // } = this.props
240
- // const currentParentWidth = document.querySelector(
241
- // `#id-${id} .tw-${type} .sftp-table`
242
- // ).clientWidth
243
- // style.width = (parseFloat(style.width) * currentParentWidth / parentWidth) + 'px'
244
- // style.left = (parseFloat(style.left) * currentParentWidth / parentWidth) + 'px'
245
- // return style
246
- // }
247
-
248
184
  // reset
249
185
  resetWidth = () => {
250
186
  this.setState(this.initFromProps())
251
187
  }
252
188
 
189
+ setClickFileId = (id) => {
190
+ this.currentFileId = id
191
+ }
192
+
253
193
  renderItem = (item) => {
254
194
  const { type } = this.props
255
195
  const cls = item.isParent ? 'parent-file-item' : 'real-file-item'
256
196
  const key = item.id
197
+ const fileProps = {
198
+ ...this.props.getFileProps(item, type),
199
+ cls,
200
+ properties: this.state.properties,
201
+ setClickFileId: this.setClickFileId
202
+ }
257
203
  return (
258
204
  <FileSection
259
- {...this.props.getFileProps(item, type)}
205
+ {...fileProps}
260
206
  key={key}
261
- cls={cls}
262
- properties={this.state.properties}
263
207
  />
264
208
  )
265
209
  }
@@ -268,9 +212,59 @@ export default class FileListTable extends Component {
268
212
  this.onToggleProp(key)
269
213
  }
270
214
 
215
+ handleClick = (e) => {
216
+ const target = e.target.closest('[data-id]')
217
+ if (target) {
218
+ const id = target.getAttribute('data-id')
219
+ const refKey = 'file-' + id
220
+ const ref = filesRef.get(refKey)
221
+ if (ref) {
222
+ ref.onClick(e)
223
+ }
224
+ }
225
+ }
226
+
227
+ handleDoubleClick = (e) => {
228
+ const target = e.target.closest('[data-id]')
229
+ if (target) {
230
+ const id = target.getAttribute('data-id')
231
+ const ref = filesRef.get('file-' + id)
232
+ if (ref) {
233
+ ref.transferOrEnterDirectory(e)
234
+ }
235
+ }
236
+ }
237
+
238
+ getClickedFile = () => {
239
+ const refKey = this.currentFileId
240
+ return filesRef.get(refKey)
241
+ }
242
+
243
+ handleDropdownOpenChange = (open) => {
244
+ if (open) {
245
+ this.forceUpdate()
246
+ }
247
+ }
248
+
249
+ onContextMenuFile = ({ key }) => {
250
+ if (key !== 'more-submenu') {
251
+ const inst = this.getClickedFile()
252
+ if (inst) {
253
+ inst[key]()
254
+ }
255
+ }
256
+ }
257
+
258
+ renderContextMenuFile = () => {
259
+ const fileInst = this.getClickedFile()
260
+ return fileInst ? fileInst.renderContextMenu() : []
261
+ }
262
+
271
263
  renderParent = (type) => {
272
264
  const { parentItem } = this.props
273
- return parentItem ? this.renderItem(parentItem) : null
265
+ return parentItem
266
+ ? this.renderItem(parentItem)
267
+ : null
274
268
  }
275
269
 
276
270
  render () {
@@ -282,7 +276,9 @@ export default class FileListTable extends Component {
282
276
  style: {
283
277
  height: height - 42 - 30 - 32 - 90
284
278
  },
285
- draggable: false
279
+ draggable: false,
280
+ onClick: this.handleClick,
281
+ onDoubleClick: this.handleDoubleClick
286
282
  }
287
283
  const hasPager = this.hasPager()
288
284
  const cls = classnames(
@@ -291,21 +287,37 @@ export default class FileListTable extends Component {
291
287
  'sftp-has-pager': hasPager
292
288
  }
293
289
  )
294
-
290
+ const ddProps = {
291
+ menu: {
292
+ items: this.renderContextMenuFile(),
293
+ onClick: this.onContextMenuFile
294
+ },
295
+ trigger: ['contextMenu'],
296
+ onOpenChange: this.handleDropdownOpenChange
297
+ }
295
298
  return (
296
299
  <div className={cls}>
297
300
  {this.renderTableHeader()}
298
- <div
299
- {...props}
300
- >
301
- {this.props.renderEmptyFile(type)}
302
- {this.renderParent(type)}
303
- <PagedList
304
- list={fileList}
305
- renderItem={this.renderItem}
306
- hasPager={hasPager}
307
- />
308
- </div>
301
+ <Dropdown {...ddProps}>
302
+ <div
303
+ {...props}
304
+ >
305
+ {
306
+ this.props.renderEmptyFile(
307
+ type,
308
+ {
309
+ setClickFileId: this.setClickFileId
310
+ }
311
+ )
312
+ }
313
+ {this.renderParent(type)}
314
+ <PagedList
315
+ list={fileList}
316
+ renderItem={this.renderItem}
317
+ hasPager={hasPager}
318
+ />
319
+ </div>
320
+ </Dropdown>
309
321
  </div>
310
322
  )
311
323
  }
@@ -1054,29 +1054,45 @@ export default class Sftp extends Component {
1054
1054
  }
1055
1055
  }
1056
1056
 
1057
- renderEmptyFile = (type) => {
1057
+ renderEmptyFile = (type, extra = {}) => {
1058
+ const uniqueId = this.getPathUid(type, 'empty')
1058
1059
  const item = {
1059
1060
  type,
1060
1061
  name: '',
1061
- isDirectory: true
1062
+ isDirectory: true,
1063
+ id: uniqueId,
1064
+ isEmpty: true
1065
+ }
1066
+ const allProps = {
1067
+ ...this.getFileProps(item, type),
1068
+ ...extra,
1069
+ cls: 'virtual-file-unit',
1070
+ key: 'empty' + type,
1071
+ isEmpty: true,
1072
+ draggable: false,
1073
+ ref: ref => {
1074
+ this[type + 'Dom'] = ref
1075
+ }
1062
1076
  }
1063
1077
  return (
1064
1078
  <div
1065
1079
  className={`virtual-file virtual-file-${type}`}
1066
1080
  >
1067
1081
  <FileSection
1068
- {...this.getFileProps(item, type)}
1069
- ref={ref => {
1070
- this[type + 'Dom'] = ref
1071
- }}
1072
- draggable={false}
1073
- cls='virtual-file-unit'
1074
- key={'empty' + type}
1082
+ {...allProps}
1083
+ key={uniqueId}
1075
1084
  />
1076
1085
  </div>
1077
1086
  )
1078
1087
  }
1079
1088
 
1089
+ getPathUid = (type, type1) => {
1090
+ const currentPath = this.state[`${type}Path`]
1091
+ const parentPath = resolve(currentPath, '..')
1092
+ const { id } = this.props.tab
1093
+ return `${type1}-${parentPath}-${id}-${type}`
1094
+ }
1095
+
1080
1096
  renderParentItem = (type) => {
1081
1097
  const currentPath = this.state[`${type}Path`]
1082
1098
  const parentPath = resolve(currentPath, '..')
@@ -1085,8 +1101,7 @@ export default class Sftp extends Component {
1085
1101
  return null
1086
1102
  }
1087
1103
 
1088
- const { id } = this.props.tab
1089
- const uniqueId = `parent-${parentPath}-${id}-${type}`
1104
+ const uniqueId = this.getPathUid(type, 'parent')
1090
1105
 
1091
1106
  return {
1092
1107
  type,
@@ -201,7 +201,7 @@ export default class ShortcutEdit extends PureComponent {
201
201
  return (
202
202
  <div className={this.getCls()}>
203
203
  <Input
204
- addonAfter={this.renderAfter()}
204
+ suffix={this.renderAfter()}
205
205
  value={shortcut}
206
206
  className='shortcut-input'
207
207
  />
@@ -13,7 +13,8 @@
13
13
  .item-list-unit:hover
14
14
  .list-item-remove
15
15
  display none !important
16
-
16
+ .sidebar-panel
17
+ top 36px
17
18
  .sidebar
18
19
  position absolute
19
20
  left 0
@@ -1,5 +1,7 @@
1
1
  import InputNumberConfirm from '../common/input-number-confirm'
2
-
2
+ import {
3
+ Space
4
+ } from 'antd'
3
5
  import {
4
6
  MinusCircleOutlined,
5
7
  PlusCircleOutlined
@@ -18,9 +20,9 @@ export default function ZoomMenu (props) {
18
20
  step={1}
19
21
  min={25}
20
22
  max={500}
21
- addonAfter='%'
23
+ suffix='%'
22
24
  addonBefore={
23
- <>
25
+ <Space.Addon>
24
26
  <PlusCircleOutlined
25
27
  onClick={() => store.zoom(0.25, true)}
26
28
  className='mg1r pointer font16'
@@ -29,7 +31,7 @@ export default function ZoomMenu (props) {
29
31
  onClick={() => store.zoom(-0.25, true)}
30
32
  className='pointer font16'
31
33
  />
32
- </>
34
+ </Space.Addon>
33
35
  }
34
36
  />
35
37
  )
@@ -48,23 +48,37 @@ export default function AppDrag (props) {
48
48
  window.pre.runGlobalAsync('maximize')
49
49
  }
50
50
  }
51
+ if (!window.store.shouldSendWindowMove) {
52
+ useEffect(() => {
53
+ // Listen for mouseup at document level to catch mouseup outside window
54
+ document.addEventListener('mouseup', onMouseUp)
55
+ window.addEventListener('contextmenu', onMouseUp)
51
56
 
52
- useEffect(() => {
53
- // Listen for mouseup at document level to catch mouseup outside window
54
- document.addEventListener('mouseup', onMouseUp)
55
- window.addEventListener('contextmenu', onMouseUp)
56
-
57
- return () => {
58
- document.removeEventListener('mouseup', onMouseUp)
59
- window.removeEventListener('contextmenu', onMouseUp)
57
+ return () => {
58
+ document.removeEventListener('mouseup', onMouseUp)
59
+ window.removeEventListener('contextmenu', onMouseUp)
60
+ }
61
+ }, [])
62
+ }
63
+ const props0 = {
64
+ className: 'app-drag',
65
+ onDoubleClick
66
+ }
67
+ if (
68
+ window.store.shouldSendWindowMove
69
+ ) {
70
+ Object.assign(props0, {
71
+ onMouseDown,
72
+ onMouseUp
73
+ })
74
+ } else {
75
+ props0.style = {
76
+ WebkitAppRegion: 'drag'
60
77
  }
61
- }, [])
78
+ }
62
79
  return (
63
80
  <div
64
- className='app-drag'
65
- onMouseDown={onMouseDown}
66
- onMouseUp={onMouseUp}
67
- onDoubleClick={onDoubleClick}
81
+ {...props0}
68
82
  >
69
83
  {props.children}
70
84
  </div>
@@ -308,3 +308,11 @@
308
308
  text-overflow ellipsis
309
309
  white-space nowrap
310
310
 
311
+ .not-webapp.not-win
312
+ .tab
313
+ .tabs-add-btn
314
+ .window-control-box
315
+ .tab-close
316
+ .tab-reload
317
+ .tabs-extra
318
+ -webkit-app-region no-drag
@@ -206,9 +206,8 @@ export default class TermSearch extends PureComponent {
206
206
  value: termSearch,
207
207
  className: 'iblock',
208
208
  onChange: this.handleChange,
209
- suffix: this.renderSuffix(),
209
+ suffix: <>{this.renderSuffix()} {this.renderAfter()}</>,
210
210
  onPressEnter: this.next,
211
- addonAfter: this.renderAfter(),
212
211
  selectall: true
213
212
  }
214
213
  return (
@@ -239,12 +239,21 @@ class Term extends Component {
239
239
  type: 'shell_integration',
240
240
  execute: async () => {
241
241
  await this.injectShellIntegration()
242
+ if (currSftpFollow) {
243
+ this.attachAddon._sendData('\r')
244
+ }
242
245
  }
243
246
  })
244
247
  } else {
245
248
  // No active queue, inject directly
246
- this.injectShellIntegration()
249
+ this.injectShellIntegration().then(() => {
250
+ if (currSftpFollow) {
251
+ this.attachAddon._sendData('\r')
252
+ }
253
+ })
247
254
  }
255
+ } else if (this.shellInjected && currSftpFollow) {
256
+ this.getCwd()
248
257
  }
249
258
  }
250
259
  if (
@@ -165,7 +165,7 @@ export default function SimpleEditor (props) {
165
165
  enterButton={<SearchOutlined />}
166
166
  onSearch={handleSearch}
167
167
  onPressEnter={handleSearch}
168
- addonAfter={renderAfter()}
168
+ suffix={renderAfter()}
169
169
  style={{ width: 'auto' }}
170
170
  />
171
171
  <Button
@@ -570,8 +570,8 @@ export default class ItemListTree extends Component {
570
570
  value={categoryTitle}
571
571
  onChange={this.handleChangeEdit}
572
572
  onPressEnter={this.handleSubmitEdit}
573
- addonBefore={colorPicker}
574
- addonAfter={confirm}
573
+ prefix={colorPicker}
574
+ suffix={confirm}
575
575
  />
576
576
  )
577
577
  }
@@ -757,8 +757,8 @@ export default class ItemListTree extends Component {
757
757
  value={bookmarkGroupTitle}
758
758
  onPressEnter={this.handleSubmit}
759
759
  onChange={this.handleChangeBookmarkGroupTitle}
760
- addonBefore={colorPicker}
761
- addonAfter={confirm}
760
+ prefix={colorPicker}
761
+ suffix={confirm}
762
762
  onBlur={this.handleBlurBookmarkGroupTitle}
763
763
  />
764
764
  </div>
@@ -28,6 +28,7 @@
28
28
  .tree-control-btn
29
29
  display inline-block
30
30
  vertical-align middle
31
+ line-height 26px
31
32
  &.item-dragover-top
32
33
  border-top 1px solid #18d551
33
34
  .tree-item-title
@@ -33,12 +33,12 @@ export default function AddressBar (props) {
33
33
  <Input
34
34
  value={url}
35
35
  onClick={handleClick}
36
- addonBefore={
36
+ prefix={
37
37
  <ReloadOutlined
38
38
  onClick={onReload}
39
39
  />
40
40
  }
41
- addonAfter={
41
+ suffix={
42
42
  <GlobalOutlined
43
43
  onClick={onOpen}
44
44
  />
@@ -12,13 +12,17 @@ export default Store => {
12
12
 
13
13
  Store.prototype.blur = function () {
14
14
  window.focused = false
15
- window.pre.runSync('windowMove', false)
15
+ if (window.store.shouldSendWindowMove) {
16
+ window.pre.runSync('windowMove', false)
17
+ }
16
18
  refs.get('term-' + window.store.activeTabId)?.term?.blur()
17
19
  }
18
20
 
19
21
  Store.prototype.onBlur = function () {
20
22
  window.focused = false
21
- window.pre.runSync('windowMove', false)
23
+ if (window.store.shouldSendWindowMove) {
24
+ window.pre.runSync('windowMove', false)
25
+ }
22
26
  }
23
27
 
24
28
  Store.prototype.selectall = function () {
@@ -36,7 +36,8 @@ import {
36
36
  settingMap,
37
37
  terminalSshConfigType,
38
38
  paneMap,
39
- staticNewItemTabs
39
+ staticNewItemTabs,
40
+ isWin
40
41
  } from '../common/constants'
41
42
  import getInitItem from '../common/init-setting-item'
42
43
  import createTitle from '../common/create-title'
@@ -89,6 +90,12 @@ class Store {
89
90
  return tab.props.tab
90
91
  }
91
92
 
93
+ get shouldSendWindowMove () {
94
+ return isWin &&
95
+ !window.et.isWebApp &&
96
+ !window.store.config.useSystemTitleBar
97
+ }
98
+
92
99
  get batchInputSelectedTabIds () {
93
100
  return Array.from(window.store._batchInputSelectedTabIds)
94
101
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "2.4.38",
3
+ "version": "2.5.9",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",