@electerm/electerm-react 1.60.16 → 1.60.29

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/clipboard.js +1 -14
  2. package/client/common/constants.js +0 -43
  3. package/client/common/data-compare.js +55 -0
  4. package/client/common/default-setting.js +2 -10
  5. package/client/common/resolve.js +18 -22
  6. package/client/common/sftp.js +0 -3
  7. package/client/components/ai/ai-chat.jsx +30 -6
  8. package/client/components/ai/ai-config.jsx +17 -6
  9. package/client/components/batch-op/batch-op.jsx +3 -24
  10. package/client/components/bookmark-form/bookmark-group-tree-format.js +7 -9
  11. package/client/components/bookmark-form/form-ssh-common.jsx +0 -2
  12. package/client/components/bookmark-form/ssh-form.jsx +8 -41
  13. package/client/components/bookmark-form/tree-delete.jsx +6 -15
  14. package/client/components/common/animate-text.jsx +3 -4
  15. package/client/components/common/drag-handle.jsx +59 -45
  16. package/client/components/common/drag-handle.styl +2 -1
  17. package/client/components/common/input-auto-focus.jsx +29 -63
  18. package/client/components/common/ref.js +24 -0
  19. package/client/components/footer/batch-input.jsx +1 -6
  20. package/client/components/footer/footer-entry.jsx +13 -16
  21. package/client/components/footer/footer.styl +0 -5
  22. package/client/components/icons/ai-icon.jsx +17 -0
  23. package/client/components/icons/ai-icon.styl +3 -0
  24. package/client/components/layout/layout-item.jsx +14 -0
  25. package/client/components/main/main.jsx +8 -19
  26. package/client/components/main/upgrade.jsx +13 -25
  27. package/client/components/profile/profile-form-elem.jsx +1 -2
  28. package/client/components/quick-commands/on-drop.js +1 -12
  29. package/client/components/quick-commands/quick-command-transport-mod.jsx +3 -13
  30. package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -2
  31. package/client/components/rdp/rdp-session.jsx +4 -4
  32. package/client/components/session/session.jsx +9 -11
  33. package/client/components/setting-panel/on-tree-drop.js +4 -35
  34. package/client/components/setting-panel/setting-common.jsx +4 -1
  35. package/client/components/setting-panel/setting-modal.jsx +7 -5
  36. package/client/components/setting-panel/tab-settings.jsx +0 -1
  37. package/client/components/setting-sync/setting-sync.jsx +0 -1
  38. package/client/components/sftp/address-bookmark-item.jsx +1 -15
  39. package/client/components/sftp/confirm-modal-store.jsx +2 -2
  40. package/client/components/sftp/{file-mode-modal.jsx → file-info-modal.jsx} +137 -37
  41. package/client/components/sftp/file-item.jsx +156 -192
  42. package/client/components/sftp/file-table-header.jsx +98 -0
  43. package/client/components/sftp/list-table-ui.jsx +125 -416
  44. package/client/components/sftp/sftp-entry.jsx +102 -128
  45. package/client/components/sftp/sftp.styl +6 -22
  46. package/client/components/sftp/transfer-conflict-store.jsx +8 -12
  47. package/client/components/sftp/transport-action-store.jsx +7 -15
  48. package/client/components/shortcuts/shortcut-control.jsx +72 -3
  49. package/client/components/shortcuts/shortcut-handler.js +0 -1
  50. package/client/components/side-panel-r/side-panel-r.jsx +7 -4
  51. package/client/components/sidebar/history.jsx +3 -0
  52. package/client/components/sidebar/index.jsx +1 -1
  53. package/client/components/sidebar/info-modal.jsx +3 -0
  54. package/client/components/sidebar/side-panel.jsx +7 -4
  55. package/client/components/sidebar/sidebar-panel.jsx +1 -1
  56. package/client/components/sidebar/sidebar.styl +3 -3
  57. package/client/components/sys-menu/icons-map.jsx +52 -0
  58. package/client/components/{context-menu → sys-menu}/menu-btn.jsx +33 -45
  59. package/client/components/sys-menu/sys-menu.jsx +163 -0
  60. package/client/components/{context-menu/context-menu.styl → sys-menu/sys-menu.styl} +2 -11
  61. package/client/components/tabs/index.jsx +5 -97
  62. package/client/components/tabs/tab.jsx +121 -73
  63. package/client/components/tabs/tabs.styl +4 -1
  64. package/client/components/terminal/term-search.jsx +16 -28
  65. package/client/components/terminal/terminal-interactive.jsx +0 -2
  66. package/client/components/terminal/{index.jsx → terminal.jsx} +126 -248
  67. package/client/components/terminal-info/base.jsx +21 -46
  68. package/client/components/terminal-info/terminal-info.jsx +3 -0
  69. package/client/components/text-editor/text-editor.jsx +38 -53
  70. package/client/components/theme/theme-form.jsx +0 -2
  71. package/client/components/tree-list/bookmark-toolbar.jsx +23 -47
  72. package/client/components/tree-list/bookmark-transport.jsx +2 -90
  73. package/client/components/tree-list/move-item-modal.jsx +101 -0
  74. package/client/components/tree-list/tree-list-item.jsx +6 -8
  75. package/client/components/tree-list/tree-list.jsx +48 -273
  76. package/client/components/vnc/vnc-session.jsx +5 -3
  77. package/client/store/app-upgrade.js +2 -5
  78. package/client/store/bookmark-group.js +74 -28
  79. package/client/store/common.js +36 -54
  80. package/client/store/event.js +4 -37
  81. package/client/store/init-state.js +9 -12
  82. package/client/store/item.js +34 -39
  83. package/client/store/load-data.js +5 -1
  84. package/client/store/quick-command.js +2 -12
  85. package/client/store/session.js +6 -7
  86. package/client/store/setting.js +3 -7
  87. package/client/store/sidebar.js +2 -8
  88. package/client/store/store.js +0 -20
  89. package/client/store/system-menu.js +1 -2
  90. package/client/store/tab.js +29 -1
  91. package/client/store/terminal-theme.js +0 -4
  92. package/client/store/watch.js +26 -4
  93. package/package.json +1 -1
  94. package/client/common/post-msg.js +0 -3
  95. package/client/components/common/native-input.jsx +0 -30
  96. package/client/components/context-menu/context-menu.jsx +0 -339
  97. package/client/components/sftp/file-props-modal.jsx +0 -210
  98. package/client/store/context-menu.js +0 -23
  99. /package/client/components/{context-menu → sys-menu}/boomarks.jsx +0 -0
  100. /package/client/components/{context-menu → sys-menu}/history.jsx +0 -0
  101. /package/client/components/{context-menu → sys-menu}/icon-holder.jsx +0 -0
  102. /package/client/components/{context-menu → sys-menu}/sub-tab-menu.jsx +0 -0
  103. /package/client/components/{context-menu → sys-menu}/tabs.jsx +0 -0
  104. /package/client/components/{context-menu → sys-menu}/zoom.jsx +0 -0
@@ -19,12 +19,10 @@ import {
19
19
  permission2mode
20
20
  } from '../../common/mode2permission'
21
21
  import wait from '../../common/wait'
22
- import postMessage from '../../common/post-msg'
23
22
  import {
24
23
  fileOperationsMap,
25
24
  isWin, transferTypeMap, typeMap,
26
- isMac, maxEditFileSize, ctrlOrCmd,
27
- commonActions
25
+ isMac, maxEditFileSize, ctrlOrCmd
28
26
  } from '../../common/constants'
29
27
  import findParent from '../../common/find-parent'
30
28
  import sorter from '../../common/index-sorter'
@@ -35,16 +33,14 @@ import time from '../../common/time'
35
33
  import { filesize } from 'filesize'
36
34
  import { createTransferProps } from './transfer-common'
37
35
  import generate from '../../common/uid'
36
+ import { refsStatic, refs } from '../common/ref'
37
+ import iconsMap from '../sys-menu/icons-map'
38
+ import {
39
+ Dropdown
40
+ } from 'antd'
38
41
 
39
42
  const e = window.translate
40
43
 
41
- const computePos = (e) => {
42
- return {
43
- left: e.clientX,
44
- top: e.clientY
45
- }
46
- }
47
-
48
44
  const fileItemCls = 'sftp-item'
49
45
  const onDragCls = 'sftp-ondrag'
50
46
  const onDragOverCls = 'sftp-dragover'
@@ -57,11 +53,13 @@ export default class FileSection extends React.Component {
57
53
  file: copy(props.file),
58
54
  overwriteStrategy: ''
59
55
  }
60
- this.id = 'FileSection-' + (props.file?.id || generate())
56
+ // Create ref
57
+ this.domRef = React.createRef()
61
58
  }
62
59
 
63
60
  componentDidMount () {
64
- this.dom = document.getElementById(this.id)
61
+ this.id = 'file-' + (this.props.file?.id || generate())
62
+ refs.add(this.id, this)
65
63
  this.applyStyle()
66
64
  }
67
65
 
@@ -75,17 +73,20 @@ export default class FileSection extends React.Component {
75
73
  }
76
74
 
77
75
  componentWillUnmount () {
76
+ refsStatic.remove(this.id)
78
77
  clearTimeout(this.timer)
79
78
  this.timer = null
80
- this.dom = null
79
+ this.domRef = null
81
80
  this.dropTarget = null
82
81
  this.removeFileEditEvent()
83
- window.removeEventListener('message', this.changeFileMode)
84
- window.removeEventListener('message', this.onContextAction)
82
+ }
83
+
84
+ get editor () {
85
+ return refsStatic.get('text-editor')
85
86
  }
86
87
 
87
88
  applyStyle = () => {
88
- if (!this.dom) {
89
+ if (!this.domRef) {
89
90
  return
90
91
  }
91
92
  const {
@@ -95,7 +96,7 @@ export default class FileSection extends React.Component {
95
96
  const headers = document.querySelectorAll(
96
97
  `#id-${id} .${type} .sftp-file-table-header .sftp-header-box`
97
98
  )
98
- this.dom.querySelectorAll('.sftp-file-prop').forEach((n, i) => {
99
+ this.domRef.current?.querySelectorAll('.sftp-file-prop').forEach((n, i) => {
99
100
  const h = headers[i]
100
101
  if (h) {
101
102
  const s = pick(h.style, ['width', 'left'])
@@ -211,7 +212,7 @@ export default class FileSection extends React.Component {
211
212
  const cls = this.props.selectedFiles.length > 1
212
213
  ? onDragCls + ' ' + onMultiDragCls
213
214
  : onDragCls
214
- addClass(this.dom, cls)
215
+ addClass(this.domRef.current, cls)
215
216
  e.dataTransfer.setData('fromFile', JSON.stringify(this.props.file))
216
217
  }
217
218
 
@@ -257,7 +258,7 @@ export default class FileSection extends React.Component {
257
258
  if (!type) {
258
259
  return
259
260
  }
260
- let toFile = this.props[type + 'FileTree'][id] || {}
261
+ let toFile = this.props[type + 'FileTree'].get(id) || {}
261
262
  if (!toFile.id || !toFile.isDirectory) {
262
263
  toFile = {
263
264
  type,
@@ -272,7 +273,7 @@ export default class FileSection extends React.Component {
272
273
  this.props.modifier({
273
274
  onDrag: false
274
275
  })
275
- removeClass(this.dom, onDragCls, onMultiDragCls)
276
+ removeClass(this.domRef.current, onDragCls, onMultiDragCls)
276
277
  document.querySelectorAll('.' + onDragOverCls).forEach((d) => {
277
278
  removeClass(d, onDragOverCls)
278
279
  })
@@ -381,7 +382,7 @@ export default class FileSection extends React.Component {
381
382
 
382
383
  showInfo = () => {
383
384
  const { type } = this.props
384
- window.store.openFileInfoModal({
385
+ refsStatic.get('file-modal')?.showFileInfoModal({
385
386
  file: this.state.file,
386
387
  tab: this.props.tab,
387
388
  visible: true,
@@ -463,8 +464,12 @@ export default class FileSection extends React.Component {
463
464
  const { file } = this.state
464
465
  const {
465
466
  id,
466
- type
467
+ type,
468
+ isParent
467
469
  } = file
470
+ if (isParent) {
471
+ return
472
+ }
468
473
  this.props.modifier({
469
474
  lastClickedFile: file
470
475
  })
@@ -505,42 +510,28 @@ export default class FileSection extends React.Component {
505
510
  })
506
511
  }
507
512
 
508
- changeFileMode = async e => {
509
- const {
510
- file = {},
511
- action
512
- } = e.data || {}
513
- if (action === commonActions.submitFileModeClose) {
514
- window.removeEventListener('message', this.changeFileMode)
515
- return false
516
- }
517
- if (
518
- action !== commonActions.submitFileModeEdit ||
519
- file.id !== this.state.file.id
520
- ) {
521
- return false
522
- }
513
+ changeFileMode = async (file) => {
523
514
  const { permission, type, path, name } = file
524
515
  const func = type === typeMap.local
525
516
  ? fs.chmod
526
517
  : this.props.sftp.chmod
527
518
  const p = resolve(path, name)
528
519
  await func(p, permission).catch(window.store.onError)
529
- window.removeEventListener('message', this.changeFileMode)
530
520
  this.props[type + 'List']()
531
521
  }
532
522
 
533
523
  openFileModeModal = () => {
534
524
  const { type } = this.props
535
- window.addEventListener(
536
- 'message', this.changeFileMode
525
+ refsStatic.get('file-modal')?.showFileModeModal(
526
+ {
527
+ tab: this.props.tab,
528
+ visible: true,
529
+ uidTree: this.props[`${type}UidTree`],
530
+ gidTree: this.props[`${type}GidTree`]
531
+ },
532
+ this.state.file,
533
+ this.id
537
534
  )
538
- window.store.openFileModeModal({
539
- tab: this.props.tab,
540
- visible: true,
541
- uidTree: this.props[`${type}UidTree`],
542
- gidTree: this.props[`${type}GidTree`]
543
- }, this.state.file)
544
535
  }
545
536
 
546
537
  handleBlur = () => {
@@ -596,11 +587,11 @@ export default class FileSection extends React.Component {
596
587
  })
597
588
  }
598
589
 
599
- enterDirectory = (e, file) => {
590
+ enterDirectory = (e, file = this.state.file) => {
600
591
  e && e.stopPropagation && e.stopPropagation()
601
- const { type, name } = file || this.state.file
592
+ const { type, name, isParent } = file
602
593
  const n = `${type}Path`
603
- const path = this.props[n]
594
+ const path = isParent ? file.path : this.props[n]
604
595
  const np = resolve(path, name)
605
596
  const op = this.props[type + 'Path']
606
597
  this.props.modifier({
@@ -620,39 +611,11 @@ export default class FileSection extends React.Component {
620
611
  }
621
612
 
622
613
  removeFileEditEvent = () => {
623
- delete this.eid
624
614
  if (this.watchingFile) {
625
615
  window.pre.ipcOffEvent('file-change', this.onFileChange)
626
616
  window.pre.runGlobalAsync('unwatchFile', this.watchingFile)
627
617
  delete this.watchingFile
628
618
  }
629
- window.removeEventListener('message', this.onFileEditEvent)
630
- }
631
-
632
- onFileEditEvent = e => {
633
- const {
634
- action,
635
- id,
636
- text,
637
- path,
638
- mode,
639
- type,
640
- noClose
641
- } = e.data || {}
642
- if (id !== this.eid) {
643
- return false
644
- }
645
- if (
646
- action === commonActions.fetchTextEditorText
647
- ) {
648
- this.fetchEditorText(path, type)
649
- } else if (action === commonActions.submitTextEditorText) {
650
- this.onSubmitEditFile(mode, type, path, text, noClose)
651
- } else if (action === commonActions.onCloseTextEditor) {
652
- this.removeFileEditEvent()
653
- } else if (action === commonActions.editWithSystemEditor) {
654
- this.editWithSystemEditor(text)
655
- }
656
619
  }
657
620
 
658
621
  editWithSystemEditor = async (text) => {
@@ -667,7 +630,7 @@ export default class FileSection extends React.Component {
667
630
  } else {
668
631
  const id = generate()
669
632
  tempPath = window.pre.resolve(
670
- window.pre.tempDir, `temp-${id}-${name}`
633
+ window.pre.tempDir, `electerm-temp-${id}-${name}`
671
634
  )
672
635
  await fs.writeFile(tempPath, text)
673
636
  }
@@ -676,12 +639,9 @@ export default class FileSection extends React.Component {
676
639
  }
677
640
 
678
641
  onFileChange = (e, text) => {
679
- postMessage({
680
- action: commonActions.editWithSystemEditorDone,
681
- data: {
682
- id: this.eid,
683
- text
684
- }
642
+ this.editor.editWithSystemEditorDone({
643
+ id: this.id,
644
+ text
685
645
  })
686
646
  }
687
647
 
@@ -714,13 +674,7 @@ export default class FileSection extends React.Component {
714
674
  const text = typeMap.remote === type
715
675
  ? await this.props.sftp.readFile(path)
716
676
  : await fs.readFile(path)
717
- postMessage({
718
- action: commonActions.loadTextEditorText,
719
- data: {
720
- text,
721
- loading: false
722
- }
723
- })
677
+ return text
724
678
  }
725
679
 
726
680
  onSubmitEditFile = async (mode, type, path, text, noClose) => {
@@ -743,25 +697,17 @@ export default class FileSection extends React.Component {
743
697
  data.file = null
744
698
  data.text = ''
745
699
  }
746
- postMessage({
747
- action: commonActions.openTextEditor,
748
- data
749
- })
700
+ this.editor?.setState(data)
750
701
  if (r && !noClose) {
751
702
  this.props[`${type}List`]()
752
703
  }
753
704
  }
754
705
 
755
706
  editFile = () => {
756
- this.eid = generate()
757
- postMessage({
758
- action: commonActions.openTextEditor,
759
- data: {
760
- id: this.eid,
761
- file: this.state.file
762
- }
707
+ this.editor?.openEditor({
708
+ id: this.id,
709
+ file: this.state.file
763
710
  })
764
- window.addEventListener('message', this.onFileEditEvent)
765
711
  }
766
712
 
767
713
  transferOrEnterDirectory = async (e, edit) => {
@@ -861,15 +807,25 @@ export default class FileSection extends React.Component {
861
807
  this.props.onGoto(this.props.file.type)
862
808
  }
863
809
 
864
- del = async (delSelected) => {
810
+ shouldShowSelectedMenu = () => {
811
+ const {
812
+ file: {
813
+ id
814
+ },
815
+ selectedFiles
816
+ } = this.props
817
+ return id &&
818
+ selectedFiles.length > 1 &&
819
+ some(selectedFiles, d => d.id === id)
820
+ }
821
+
822
+ del = async () => {
823
+ const delSelected = this.shouldShowSelectedMenu()
865
824
  const { file, selectedFiles } = this.props
866
825
  const { type } = file
867
826
  const files = delSelected
868
827
  ? selectedFiles
869
828
  : [file]
870
- postMessage({
871
- type: commonActions.closeContextMenu
872
- })
873
829
  await this.props.delFiles(type, files)
874
830
  }
875
831
 
@@ -925,17 +881,42 @@ export default class FileSection extends React.Component {
925
881
  return !isWin
926
882
  }
927
883
 
884
+ renderContextMenu = () => {
885
+ return this.renderContextItems()
886
+ .map(r => {
887
+ const {
888
+ func,
889
+ text,
890
+ disabled,
891
+ icon,
892
+ subText,
893
+ requireConfirm
894
+ } = r
895
+ const IconCom = iconsMap[icon]
896
+ return {
897
+ key: func,
898
+ label: text,
899
+ disabled,
900
+ icon: <IconCom />,
901
+ extra: subText,
902
+ danger: requireConfirm
903
+ }
904
+ })
905
+ }
906
+
928
907
  renderContextItems () {
929
908
  const {
930
909
  file: {
931
910
  type,
932
911
  isDirectory,
933
912
  size,
934
- id
913
+ id,
914
+ isParent
935
915
  },
936
916
  selectedFiles,
937
917
  tab
938
918
  } = this.props
919
+ const isRealFile = id && !isParent
939
920
  const hasHost = !!tab.host
940
921
  const { enableSsh } = tab
941
922
  const isLocal = type === typeMap.local
@@ -955,7 +936,7 @@ export default class FileSection extends React.Component {
955
936
  const showEdit = !isDirectory && id &&
956
937
  size < maxEditFileSize
957
938
  const res = []
958
- if (isDirectory && id) {
939
+ if (isDirectory && isRealFile) {
959
940
  res.push({
960
941
  func: 'doEnterDirectory',
961
942
  icon: 'EnterOutlined',
@@ -970,7 +951,7 @@ export default class FileSection extends React.Component {
970
951
  })
971
952
  }
972
953
  if (
973
- isDirectory && id &&
954
+ isDirectory && isRealFile &&
974
955
  (
975
956
  (hasHost && enableSsh !== false && isRemote) ||
976
957
  (isLocal && !hasHost)
@@ -982,21 +963,21 @@ export default class FileSection extends React.Component {
982
963
  text: e('gotoFolderInTerminal')
983
964
  })
984
965
  }
985
- if (!(!id || !hasHost || shouldShowSelectedMenu)) {
966
+ if (!(!isRealFile || !hasHost || shouldShowSelectedMenu)) {
986
967
  res.push({
987
968
  func: 'doTransfer',
988
969
  icon: iconType,
989
970
  text: transferText
990
971
  })
991
972
  }
992
- if (!isDirectory && id && isLocal) {
973
+ if (!isDirectory && isRealFile && isLocal) {
993
974
  res.push({
994
975
  func: 'transferOrEnterDirectory',
995
976
  icon: 'ArrowRightOutlined',
996
977
  text: e('open')
997
978
  })
998
979
  }
999
- if (id && isLocal) {
980
+ if (isRealFile && isLocal) {
1000
981
  res.push({
1001
982
  func: 'showInDefaultFileManager',
1002
983
  icon: 'ContainerOutlined',
@@ -1010,15 +991,12 @@ export default class FileSection extends React.Component {
1010
991
  text: e('edit')
1011
992
  })
1012
993
  }
1013
- if (id) {
994
+ if (isRealFile) {
1014
995
  res.push({
1015
996
  func: 'del',
1016
997
  icon: 'CloseCircleOutlined',
1017
998
  text: delTxt,
1018
- noAutoClose: true,
1019
- requireConfirm: true,
1020
- confirmTitle: this.renderDelConfirmTitle(shouldShowSelectedMenu),
1021
- args: [shouldShowSelectedMenu]
999
+ requireConfirm: true
1022
1000
  })
1023
1001
  res.push({
1024
1002
  func: 'onCopy',
@@ -1040,7 +1018,7 @@ export default class FileSection extends React.Component {
1040
1018
  disabled: !canPaste,
1041
1019
  subText: `${ctrlOrCmd}+v`
1042
1020
  })
1043
- if (id) {
1021
+ if (isRealFile) {
1044
1022
  res.push({
1045
1023
  func: 'doRename',
1046
1024
  icon: 'EditOutlined',
@@ -1075,14 +1053,14 @@ export default class FileSection extends React.Component {
1075
1053
  icon: 'ReloadOutlined',
1076
1054
  text: e('refresh')
1077
1055
  })
1078
- if (this.showModeEdit(type, id)) {
1056
+ if (this.showModeEdit(type, isRealFile)) {
1079
1057
  res.push({
1080
1058
  func: 'editPermission',
1081
1059
  icon: 'LockOutlined',
1082
1060
  text: e('editPermission')
1083
1061
  })
1084
1062
  }
1085
- if (id) {
1063
+ if (isRealFile) {
1086
1064
  res.push({
1087
1065
  func: 'showInfo',
1088
1066
  icon: 'InfoCircleOutlined',
@@ -1092,46 +1070,8 @@ export default class FileSection extends React.Component {
1092
1070
  return res
1093
1071
  }
1094
1072
 
1095
- onContextAction = e => {
1096
- const {
1097
- action,
1098
- id,
1099
- args = [],
1100
- func
1101
- } = e.data || {}
1102
- if (action === commonActions.closeContextMenuAfter) {
1103
- window.removeEventListener('message', this.onContextAction)
1104
- return false
1105
- }
1106
- if (
1107
- action !== commonActions.clickContextMenu ||
1108
- id !== this.uid ||
1109
- !this[func]
1110
- ) {
1111
- return false
1112
- }
1113
- window.removeEventListener('message', this.onContextAction)
1114
- this[func](...args)
1115
- }
1116
-
1117
- onContextMenu = e => {
1118
- e.preventDefault()
1119
- const { file } = this.state
1120
- const selected = this.isSelected(file)
1121
- if (!selected) {
1122
- this.onClick(e)
1123
- }
1124
- this.props.modifier({
1125
- lastClickedFile: file
1126
- })
1127
- const items = this.renderContextItems()
1128
- this.uid = generate()
1129
- window.store.openContextMenu({
1130
- items,
1131
- id: this.uid,
1132
- pos: computePos(e)
1133
- })
1134
- window.addEventListener('message', this.onContextAction)
1073
+ onContextMenu = ({ key }) => {
1074
+ this[key]()
1135
1075
  }
1136
1076
 
1137
1077
  renderEditing (file) {
@@ -1154,27 +1094,28 @@ export default class FileSection extends React.Component {
1154
1094
  )
1155
1095
  }
1156
1096
 
1157
- renderProp = ({ name, style }) => {
1097
+ renderProp = ({ id, size }) => {
1158
1098
  const { file } = this.state
1159
- let value = file[name]
1099
+ let value = file[id]
1160
1100
  let typeIcon = null
1161
1101
  let symbolicLinkText = null
1162
1102
  const {
1163
1103
  isDirectory,
1164
- isSymbolicLink
1104
+ isSymbolicLink,
1105
+ isParent
1165
1106
  } = file
1166
- if (isDirectory && name === 'size') {
1107
+ if (isDirectory && id === 'size') {
1167
1108
  value = null
1168
- } else if (!isDirectory && name === 'size') {
1109
+ } else if (!isDirectory && id === 'size') {
1169
1110
  value = filesize(value)
1170
- } else if (name === 'owner') {
1111
+ } else if (id === 'owner') {
1171
1112
  const { type } = this.props
1172
1113
  value = this.props[`${type}UidTree`]['' + value] || value
1173
- } else if (name === 'group') {
1114
+ } else if (id === 'group') {
1174
1115
  const { type } = this.props
1175
1116
  value = this.props[`${type}GidTree`]['' + value] || value
1176
1117
  }
1177
- if (name === 'name') {
1118
+ if (id === 'name') {
1178
1119
  // const Icon = isDirectory
1179
1120
  // ? FolderOutlined
1180
1121
  // : FileOutlined
@@ -1182,17 +1123,29 @@ export default class FileSection extends React.Component {
1182
1123
  symbolicLinkText = isSymbolicLink
1183
1124
  ? <sup className='color-blue symbolic-link-icon'>*</sup>
1184
1125
  : null
1185
- } else if (name === 'mode') {
1126
+ } else if (id === 'mode') {
1186
1127
  value = permission2mode(mode2permission(value))
1187
- } else if (name.toLowerCase().includes('time')) {
1128
+ } else if (id.toLowerCase().includes('time')) {
1188
1129
  value = time(value)
1189
1130
  }
1131
+ const divProps = {
1132
+ className: `sftp-file-prop noise shi-${id}`,
1133
+ style: {
1134
+ width: size + '%',
1135
+ flexBasis: `${size}%`
1136
+ },
1137
+ title: value
1138
+ }
1139
+ if (isParent && id !== 'name') {
1140
+ value = null
1141
+ divProps.title = ''
1142
+ } else if (isParent && id === 'name') {
1143
+ value = '..'
1144
+ }
1190
1145
  return (
1191
1146
  <div
1192
- key={name}
1193
- title={value}
1194
- className={`sftp-file-prop noise shi-${name}`}
1195
- style={style}
1147
+ {...divProps}
1148
+ key={id}
1196
1149
  >
1197
1150
  {typeIcon}
1198
1151
  {symbolicLinkText}
@@ -1207,7 +1160,8 @@ export default class FileSection extends React.Component {
1207
1160
  const {
1208
1161
  isDirectory,
1209
1162
  id,
1210
- isEditing
1163
+ isEditing,
1164
+ isParent
1211
1165
  } = file
1212
1166
  if (isEditing) {
1213
1167
  return this.renderEditing(file)
@@ -1219,10 +1173,9 @@ export default class FileSection extends React.Component {
1219
1173
  })
1220
1174
  const props = {
1221
1175
  className,
1222
- draggable,
1176
+ draggable: draggable && !isParent,
1223
1177
  onDoubleClick: this.transferOrEnterDirectory,
1224
1178
  ...pick(this, [
1225
- 'onContextMenu',
1226
1179
  'onClick',
1227
1180
  'onDrag',
1228
1181
  'onDragEnter',
@@ -1232,23 +1185,34 @@ export default class FileSection extends React.Component {
1232
1185
  'onDrop',
1233
1186
  'onDragEnd'
1234
1187
  ]),
1235
- onDragStart: onDragStart || this.onDragStart
1188
+ onDragStart: onDragStart || this.onDragStart,
1189
+ 'data-id': id,
1190
+ id: this.id,
1191
+ 'data-is-parent': isParent ? 'y' : 'n',
1192
+ 'data-type': type,
1193
+ title: file.name
1194
+ }
1195
+ const ddProps = {
1196
+ menu: {
1197
+ items: this.renderContextMenu(),
1198
+ onClick: this.onContextMenu
1199
+ },
1200
+ trigger: ['contextMenu']
1236
1201
  }
1237
1202
  return (
1238
- <div
1239
- {...props}
1240
- data-id={id}
1241
- id={this.id}
1242
- data-type={type}
1243
- title={file.name}
1244
- >
1245
- <div className='file-bg' />
1246
- <div className='file-props'>
1247
- {
1248
- properties.map(this.renderProp)
1249
- }
1203
+ <Dropdown {...ddProps}>
1204
+ <div
1205
+ ref={this.domRef}
1206
+ {...props}
1207
+ >
1208
+ <div className='file-bg' />
1209
+ <div className='file-props-div'>
1210
+ {
1211
+ properties.map(this.renderProp)
1212
+ }
1213
+ </div>
1250
1214
  </div>
1251
- </div>
1215
+ </Dropdown>
1252
1216
  )
1253
1217
  }
1254
1218
  }