@electerm/electerm-react 1.34.30

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 (273) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/client/common/auto-complete-data-mapper.js +6 -0
  4. package/client/common/byte-format.js +14 -0
  5. package/client/common/class.js +52 -0
  6. package/client/common/clipboard.js +49 -0
  7. package/client/common/constants.js +308 -0
  8. package/client/common/create-lang-edit-link.js +7 -0
  9. package/client/common/create-title.js +29 -0
  10. package/client/common/db-fix.js +24 -0
  11. package/client/common/db.js +155 -0
  12. package/client/common/download-mirrors.js +10 -0
  13. package/client/common/download.js +16 -0
  14. package/client/common/error-handler.jsx +27 -0
  15. package/client/common/fetch-from-server.js +47 -0
  16. package/client/common/fetch.jsx +71 -0
  17. package/client/common/find-bookmark-group-id.js +15 -0
  18. package/client/common/find-parent.js +30 -0
  19. package/client/common/form-layout.js +27 -0
  20. package/client/common/fs.js +21 -0
  21. package/client/common/get-proxy.js +8 -0
  22. package/client/common/id-with-stamp.js +10 -0
  23. package/client/common/index-sorter.js +4 -0
  24. package/client/common/init-setting-item.js +32 -0
  25. package/client/common/is-absolute-path.js +3 -0
  26. package/client/common/is-ip.js +16 -0
  27. package/client/common/is-valid-path.js +7 -0
  28. package/client/common/key-control-pressed.js +13 -0
  29. package/client/common/key-pressed.js +13 -0
  30. package/client/common/key-shift-pressed.js +7 -0
  31. package/client/common/mode2permission.js +81 -0
  32. package/client/common/new-terminal.js +31 -0
  33. package/client/common/parse-int10.js +3 -0
  34. package/client/common/parse-json-safe.js +11 -0
  35. package/client/common/pass-enc.js +25 -0
  36. package/client/common/post-msg.js +3 -0
  37. package/client/common/pre.js +156 -0
  38. package/client/common/promise-timeout.js +27 -0
  39. package/client/common/resolve.js +31 -0
  40. package/client/common/run-idle.js +1 -0
  41. package/client/common/safe-local-storage.js +35 -0
  42. package/client/common/safe-name.js +19 -0
  43. package/client/common/sftp.js +74 -0
  44. package/client/common/terminal-theme.js +158 -0
  45. package/client/common/test-connection.js +12 -0
  46. package/client/common/time.js +30 -0
  47. package/client/common/to-simple-obj.js +5 -0
  48. package/client/common/track.js +7 -0
  49. package/client/common/transfer.js +76 -0
  50. package/client/common/trzsz.js +62 -0
  51. package/client/common/ui-theme.js +44 -0
  52. package/client/common/uid.js +5 -0
  53. package/client/common/update-check.js +79 -0
  54. package/client/common/upgrade.js +68 -0
  55. package/client/common/wait.js +8 -0
  56. package/client/common/ws.js +161 -0
  57. package/client/components/batch-op/batch-op.jsx +650 -0
  58. package/client/components/bookmark-form/bookmark-form.styl +8 -0
  59. package/client/components/bookmark-form/bookmark-group-tree-format.js +39 -0
  60. package/client/components/bookmark-form/encodes.js +44 -0
  61. package/client/components/bookmark-form/form-ssh-common.jsx +208 -0
  62. package/client/components/bookmark-form/form-tabs.jsx +69 -0
  63. package/client/components/bookmark-form/index.jsx +159 -0
  64. package/client/components/bookmark-form/local-form-ui.jsx +152 -0
  65. package/client/components/bookmark-form/local-form.jsx +16 -0
  66. package/client/components/bookmark-form/proxy.jsx +49 -0
  67. package/client/components/bookmark-form/quick-command-list.jsx +31 -0
  68. package/client/components/bookmark-form/quick-command.jsx +228 -0
  69. package/client/components/bookmark-form/render-auth-ssh.jsx +104 -0
  70. package/client/components/bookmark-form/render-connection-hopping.jsx +229 -0
  71. package/client/components/bookmark-form/render-delayed-scripts.jsx +88 -0
  72. package/client/components/bookmark-form/render-ssh-tunnel.jsx +116 -0
  73. package/client/components/bookmark-form/serial-form-ui.jsx +311 -0
  74. package/client/components/bookmark-form/serial-form.jsx +20 -0
  75. package/client/components/bookmark-form/sftp-enable.jsx +33 -0
  76. package/client/components/bookmark-form/ssh-form-ui.jsx +100 -0
  77. package/client/components/bookmark-form/ssh-form.jsx +348 -0
  78. package/client/components/bookmark-form/telnet-form-ui.jsx +154 -0
  79. package/client/components/bookmark-form/telnet-form.jsx +16 -0
  80. package/client/components/bookmark-form/tree-delete.jsx +87 -0
  81. package/client/components/bookmark-form/use-form-funcs.jsx +50 -0
  82. package/client/components/bookmark-form/use-quick-commands.jsx +83 -0
  83. package/client/components/bookmark-form/use-submit.jsx +77 -0
  84. package/client/components/bookmark-form/use-ui.jsx +82 -0
  85. package/client/components/bookmark-form/x11.jsx +23 -0
  86. package/client/components/common/animate-text.jsx +37 -0
  87. package/client/components/common/animate-text.styl +54 -0
  88. package/client/components/common/external-link.jsx +28 -0
  89. package/client/components/common/help-icon.jsx +25 -0
  90. package/client/components/common/highlight.jsx +23 -0
  91. package/client/components/common/highlight.styl +3 -0
  92. package/client/components/common/input-auto-focus.jsx +68 -0
  93. package/client/components/common/input-confirm.jsx +66 -0
  94. package/client/components/common/logo-elem.jsx +22 -0
  95. package/client/components/common/markdown.jsx +27 -0
  96. package/client/components/common/react-subx.jsx +1 -0
  97. package/client/components/common/resize-wrap.jsx +222 -0
  98. package/client/components/common/resize-wrap.styl +9 -0
  99. package/client/components/common/search.jsx +9 -0
  100. package/client/components/common/show-item.jsx +27 -0
  101. package/client/components/context-menu/boomarks.jsx +15 -0
  102. package/client/components/context-menu/context-menu.jsx +340 -0
  103. package/client/components/context-menu/context-menu.styl +90 -0
  104. package/client/components/context-menu/history.jsx +27 -0
  105. package/client/components/context-menu/icon-holder.jsx +5 -0
  106. package/client/components/context-menu/menu-btn.jsx +224 -0
  107. package/client/components/context-menu/sub-tab-menu.jsx +23 -0
  108. package/client/components/context-menu/tabs.jsx +22 -0
  109. package/client/components/context-menu/zoom.jsx +40 -0
  110. package/client/components/footer/batch-input.jsx +177 -0
  111. package/client/components/footer/footer-entry.jsx +141 -0
  112. package/client/components/footer/footer.styl +47 -0
  113. package/client/components/icons/match-case.jsx +10 -0
  114. package/client/components/icons/match-whole-word.jsx +13 -0
  115. package/client/components/icons/regular-exp.jsx +10 -0
  116. package/client/components/main/css-overwrite.jsx +92 -0
  117. package/client/components/main/error-wrapper.jsx +59 -0
  118. package/client/components/main/index.jsx +11 -0
  119. package/client/components/main/loading.jsx +25 -0
  120. package/client/components/main/main.jsx +149 -0
  121. package/client/components/main/term-fullscreen-control.jsx +21 -0
  122. package/client/components/main/term-fullscreen.styl +27 -0
  123. package/client/components/main/ui-theme.jsx +31 -0
  124. package/client/components/main/upgrade.jsx +351 -0
  125. package/client/components/main/upgrade.styl +27 -0
  126. package/client/components/main/wrapper.styl +41 -0
  127. package/client/components/quick-commands/qm.styl +29 -0
  128. package/client/components/quick-commands/quick-command-item.jsx +36 -0
  129. package/client/components/quick-commands/quick-command-transport-mod.jsx +54 -0
  130. package/client/components/quick-commands/quick-command-transport.jsx +12 -0
  131. package/client/components/quick-commands/quick-commands-box.jsx +233 -0
  132. package/client/components/quick-commands/quick-commands-form-elem.jsx +119 -0
  133. package/client/components/quick-commands/quick-commands-form.jsx +33 -0
  134. package/client/components/quick-commands/quick-commands-list.jsx +128 -0
  135. package/client/components/quick-commands/quick-commands-select.jsx +38 -0
  136. package/client/components/session/session.jsx +533 -0
  137. package/client/components/session/session.styl +53 -0
  138. package/client/components/session/sessions.jsx +445 -0
  139. package/client/components/setting-panel/bookmark-transport.jsx +148 -0
  140. package/client/components/setting-panel/bookmark-tree-list.jsx +14 -0
  141. package/client/components/setting-panel/col.jsx +18 -0
  142. package/client/components/setting-panel/list.jsx +186 -0
  143. package/client/components/setting-panel/list.styl +33 -0
  144. package/client/components/setting-panel/on-tree-drop.js +222 -0
  145. package/client/components/setting-panel/setting-modal.jsx +163 -0
  146. package/client/components/setting-panel/setting-wrap.jsx +37 -0
  147. package/client/components/setting-panel/setting-wrap.styl +52 -0
  148. package/client/components/setting-panel/setting.jsx +858 -0
  149. package/client/components/setting-panel/setting.styl +4 -0
  150. package/client/components/setting-panel/start-session-select.jsx +91 -0
  151. package/client/components/setting-panel/tab-bookmarks.jsx +37 -0
  152. package/client/components/setting-panel/tab-history.jsx +44 -0
  153. package/client/components/setting-panel/tab-quick-commands.jsx +38 -0
  154. package/client/components/setting-panel/tab-settings.jsx +42 -0
  155. package/client/components/setting-panel/tab-themes.jsx +34 -0
  156. package/client/components/setting-panel/tree-list.jsx +978 -0
  157. package/client/components/setting-panel/tree-list.styl +57 -0
  158. package/client/components/setting-sync/data-import.jsx +65 -0
  159. package/client/components/setting-sync/setting-sync-form.jsx +271 -0
  160. package/client/components/setting-sync/setting-sync.jsx +81 -0
  161. package/client/components/setting-sync/sync.styl +7 -0
  162. package/client/components/sftp/address-bar.jsx +139 -0
  163. package/client/components/sftp/address-bookmark-item.jsx +47 -0
  164. package/client/components/sftp/address-bookmark.jsx +81 -0
  165. package/client/components/sftp/address-bookmark.styl +8 -0
  166. package/client/components/sftp/confirm-modal.jsx +184 -0
  167. package/client/components/sftp/file-icon.jsx +22 -0
  168. package/client/components/sftp/file-item.jsx +1226 -0
  169. package/client/components/sftp/file-mode-modal.jsx +205 -0
  170. package/client/components/sftp/file-props-modal.jsx +211 -0
  171. package/client/components/sftp/file-read.js +81 -0
  172. package/client/components/sftp/list-table-ui.jsx +547 -0
  173. package/client/components/sftp/owner-list.js +97 -0
  174. package/client/components/sftp/paged-list.jsx +60 -0
  175. package/client/components/sftp/permission-render.jsx +42 -0
  176. package/client/components/sftp/sftp-entry.jsx +1069 -0
  177. package/client/components/sftp/sftp.styl +217 -0
  178. package/client/components/sftp/transfer-common.js +9 -0
  179. package/client/components/sftp/transfer-conflict.jsx +315 -0
  180. package/client/components/sftp/transfer-speed-format.js +60 -0
  181. package/client/components/sftp/transfer-tag.jsx +40 -0
  182. package/client/components/sftp/transfer-tag.styl +11 -0
  183. package/client/components/sftp/transfer.styl +55 -0
  184. package/client/components/sftp/transport-action.jsx +410 -0
  185. package/client/components/sftp/transport-entry.jsx +108 -0
  186. package/client/components/sftp/transport-types.js +8 -0
  187. package/client/components/sftp/transports-action.jsx +111 -0
  188. package/client/components/sftp/transports-ui.jsx +93 -0
  189. package/client/components/sftp/zip.js +42 -0
  190. package/client/components/sidebar/bookmark-select.jsx +48 -0
  191. package/client/components/sidebar/bookmark.jsx +82 -0
  192. package/client/components/sidebar/history.jsx +66 -0
  193. package/client/components/sidebar/index.jsx +230 -0
  194. package/client/components/sidebar/info-modal.jsx +250 -0
  195. package/client/components/sidebar/info.styl +27 -0
  196. package/client/components/sidebar/side-icon.jsx +25 -0
  197. package/client/components/sidebar/sidebar.styl +128 -0
  198. package/client/components/sidebar/transfer-history-modal.jsx +110 -0
  199. package/client/components/sidebar/transfer-history.styl +3 -0
  200. package/client/components/sidebar/transfer-list-control.jsx +205 -0
  201. package/client/components/sidebar/transfer-list.jsx +55 -0
  202. package/client/components/sidebar/transfer-modal.jsx +76 -0
  203. package/client/components/sidebar/transfer.styl +8 -0
  204. package/client/components/sidebar/transport-ui.jsx +109 -0
  205. package/client/components/tabs/index.jsx +320 -0
  206. package/client/components/tabs/tab.jsx +427 -0
  207. package/client/components/tabs/tabs.styl +220 -0
  208. package/client/components/tabs/window-control.jsx +55 -0
  209. package/client/components/terminal/attach-addon-custom.js +70 -0
  210. package/client/components/terminal/build-ls-term-id.js +5 -0
  211. package/client/components/terminal/index.jsx +1358 -0
  212. package/client/components/terminal/normal-buffer.jsx +33 -0
  213. package/client/components/terminal/term-search.jsx +224 -0
  214. package/client/components/terminal/term-search.styl +15 -0
  215. package/client/components/terminal/terminal-apis.js +31 -0
  216. package/client/components/terminal/terminal-interactive.jsx +148 -0
  217. package/client/components/terminal/terminal.styl +96 -0
  218. package/client/components/terminal/xterm-zmodem.js +48 -0
  219. package/client/components/terminal/zmodem-transfer.jsx +98 -0
  220. package/client/components/terminal/zmodem.styl +14 -0
  221. package/client/components/terminal-info/activity.jsx +54 -0
  222. package/client/components/terminal-info/base.jsx +25 -0
  223. package/client/components/terminal-info/content.jsx +101 -0
  224. package/client/components/terminal-info/data-cols-parser.jsx +50 -0
  225. package/client/components/terminal-info/disk.jsx +29 -0
  226. package/client/components/terminal-info/index.jsx +25 -0
  227. package/client/components/terminal-info/network.jsx +114 -0
  228. package/client/components/terminal-info/resource.jsx +80 -0
  229. package/client/components/terminal-info/run-cmd.jsx +273 -0
  230. package/client/components/terminal-info/terminal-info.styl +29 -0
  231. package/client/components/terminal-info/up.jsx +15 -0
  232. package/client/components/terminal-theme/index.jsx +264 -0
  233. package/client/components/terminal-theme/terminal-theme-list.styl +3 -0
  234. package/client/components/terminal-theme/theme-list.jsx +146 -0
  235. package/client/components/text-editor/text-editor-form.jsx +97 -0
  236. package/client/components/text-editor/text-editor.jsx +182 -0
  237. package/client/css/antd-overwrite.styl +14 -0
  238. package/client/css/basic.styl +38 -0
  239. package/client/css/includes/box.styl +154 -0
  240. package/client/css/includes/font-size.styl +6 -0
  241. package/client/css/includes/index.styl +3 -0
  242. package/client/css/includes/text.styl +31 -0
  243. package/client/css/includes/theme-default.styl +20 -0
  244. package/client/entry/basic.js +58 -0
  245. package/client/entry/index.jsx +15 -0
  246. package/client/entry/worker.js +137 -0
  247. package/client/store/address-bookmark.js +25 -0
  248. package/client/store/app-upgrade.js +23 -0
  249. package/client/store/batch-input-history.js +26 -0
  250. package/client/store/bookmark-group.js +128 -0
  251. package/client/store/bookmark.js +22 -0
  252. package/client/store/common.js +140 -0
  253. package/client/store/context-menu.js +23 -0
  254. package/client/store/db-upgrade.js +43 -0
  255. package/client/store/event.js +70 -0
  256. package/client/store/index.js +335 -0
  257. package/client/store/init-state.js +191 -0
  258. package/client/store/item.js +120 -0
  259. package/client/store/load-data.js +198 -0
  260. package/client/store/quick-command.js +43 -0
  261. package/client/store/session.js +54 -0
  262. package/client/store/setting.js +208 -0
  263. package/client/store/sidebar.js +48 -0
  264. package/client/store/sync.js +390 -0
  265. package/client/store/system-menu.js +120 -0
  266. package/client/store/tab.js +74 -0
  267. package/client/store/terminal-theme.js +116 -0
  268. package/client/store/transfer-history.js +27 -0
  269. package/client/store/transfer-list.js +20 -0
  270. package/client/store/ui-theme.js +71 -0
  271. package/client/store/watch.js +116 -0
  272. package/client/views/index.pug +58 -0
  273. package/package.json +34 -0
@@ -0,0 +1,1226 @@
1
+ /**
2
+ * file section
3
+ */
4
+
5
+ import React from 'react'
6
+ import ExtIcon from './file-icon'
7
+ import {
8
+ FolderOutlined,
9
+ FileOutlined
10
+ } from '@ant-design/icons'
11
+ import classnames from 'classnames'
12
+ import copy from 'json-deep-copy'
13
+ import { pick, some } from 'lodash-es'
14
+ import Input from '../common/input-auto-focus'
15
+ import resolve from '../../common/resolve'
16
+ import { addClass, removeClass } from '../../common/class'
17
+ import {
18
+ mode2permission,
19
+ permission2mode
20
+ } from '../../common/mode2permission'
21
+ import wait from '../../common/wait'
22
+ import postMessage from '../../common/post-msg'
23
+ import {
24
+ fileOperationsMap,
25
+ isWin, transferTypeMap, typeMap,
26
+ isMac, maxEditFileSize, ctrlOrCmd,
27
+ commonActions
28
+ } from '../../common/constants'
29
+ import findParent from '../../common/find-parent'
30
+ import sorter from '../../common/index-sorter'
31
+ import { getFolderFromFilePath } from './file-read'
32
+ import { readClipboard, copy as copyToClipboard, hasFileInClipboardText } from '../../common/clipboard'
33
+ import fs from '../../common/fs'
34
+ import time from '../../../app/common/time'
35
+ import { filesize } from 'filesize'
36
+ import { createTransferProps } from './transfer-common'
37
+ import generate from '../../common/uid'
38
+
39
+ const { prefix } = window
40
+ const e = prefix('sftp')
41
+ const m = prefix('menu')
42
+
43
+ const computePos = (e) => {
44
+ return {
45
+ left: e.clientX,
46
+ top: e.clientY
47
+ }
48
+ }
49
+
50
+ const fileItemCls = 'sftp-item'
51
+ const onDragCls = 'sftp-ondrag'
52
+ const onDragOverCls = 'sftp-dragover'
53
+ const onMultiDragCls = 'sftp-dragover-multi'
54
+
55
+ export default class FileSection extends React.Component {
56
+ constructor (props) {
57
+ super(props)
58
+ this.state = {
59
+ file: copy(props.file),
60
+ overwriteStrategy: ''
61
+ }
62
+ this.id = 'FileSection-' + (props.file?.id || generate())
63
+ }
64
+
65
+ componentDidMount () {
66
+ this.dom = document.getElementById(this.id)
67
+ this.applyStyle()
68
+ }
69
+
70
+ componentDidUpdate (prevProps, prevState) {
71
+ if (
72
+ !prevState.file.id &&
73
+ this.state.file.id
74
+ ) {
75
+ this.applyStyle()
76
+ }
77
+ }
78
+
79
+ componentWillUnmount () {
80
+ clearTimeout(this.timer)
81
+ this.removeFileEditEvent()
82
+ window.removeEventListener('message', this.changeFileMode)
83
+ window.removeEventListener('message', this.onContextAction)
84
+ }
85
+
86
+ applyStyle = () => {
87
+ if (!this.dom) {
88
+ return
89
+ }
90
+ const {
91
+ id,
92
+ type
93
+ } = this.props
94
+ const headers = document.querySelectorAll(
95
+ `#id-${id} .${type} .sftp-file-table-header .sftp-header-box`
96
+ )
97
+ this.dom.querySelectorAll('.sftp-file-prop').forEach((n, i) => {
98
+ const h = headers[i]
99
+ if (h) {
100
+ const s = pick(h.style, ['width', 'left'])
101
+ Object.assign(n.style, s)
102
+ }
103
+ })
104
+ }
105
+
106
+ onCopy = (targetFiles, isCut) => {
107
+ const { file } = this.state
108
+ const selected = this.isSelected(file)
109
+ const files = targetFiles ||
110
+ (
111
+ selected
112
+ ? this.props.selectedFiles
113
+ : [file]
114
+ )
115
+ const prefix = file.type === typeMap.remote
116
+ ? 'remote:'
117
+ : ''
118
+ const textToCopy = files.map(f => {
119
+ return prefix + resolve(f.path, f.name)
120
+ }).join('\n')
121
+ copyToClipboard(textToCopy)
122
+ window.store.fileOperation = isCut ? fileOperationsMap.mv : fileOperationsMap.cp
123
+ }
124
+
125
+ onCopyPath = (targetFiles) => {
126
+ const { file } = this.state
127
+ const selected = this.isSelected(file)
128
+ const files = targetFiles ||
129
+ (
130
+ selected
131
+ ? this.props.selectedFiles
132
+ : [file]
133
+ )
134
+ const textToCopy = files.map(f => {
135
+ return resolve(f.path, f.name)
136
+ }).join('\n')
137
+ copyToClipboard(textToCopy)
138
+ }
139
+
140
+ onCut = (targetFiles) => {
141
+ this.onCopy(targetFiles, true)
142
+ }
143
+
144
+ getTransferType = fileType => {
145
+ return fileType !== typeMap.local
146
+ ? transferTypeMap.upload
147
+ : transferTypeMap.download
148
+ }
149
+
150
+ onPaste = async () => {
151
+ const { type } = this.state.file
152
+ const path = this.props[type + 'Path']
153
+ const clickBoardText = readClipboard()
154
+ const fileNames = clickBoardText.split('\n')
155
+ const res = []
156
+ const operation = this.props.fileOperation || fileOperationsMap.cp
157
+ for (let i = 0, len = fileNames.length; i < len; i++) {
158
+ const item = fileNames[i]
159
+ const isRemote = item.startsWith('remote:')
160
+ const fromPath = isRemote
161
+ ? item.replace(/^remote:/, '')
162
+ : item
163
+ const { name } = getFolderFromFilePath(fromPath, isRemote)
164
+ const toPath = resolve(path, name)
165
+ res.push({
166
+ typeFrom: isRemote ? typeMap.remote : typeMap.local,
167
+ typeTo: type,
168
+ fromPath,
169
+ toPath,
170
+ id: generate(),
171
+ ...createTransferProps(this.props),
172
+ operation
173
+ })
174
+ }
175
+ this.props.addTransferList(res)
176
+ }
177
+
178
+ onDrag = () => {}
179
+
180
+ onDragEnter = e => {
181
+ let { target } = e
182
+ target = findParent(target, '.' + fileItemCls)
183
+ if (!target) {
184
+ return e.preventDefault()
185
+ }
186
+ this.dropTarget = target
187
+ target.classList.add(onDragOverCls)
188
+ }
189
+
190
+ onDragExit = () => {}
191
+
192
+ onDragLeave = e => {
193
+ let { target } = e
194
+ target = findParent(target, '.' + fileItemCls)
195
+ if (!target) {
196
+ return e.preventDefault()
197
+ }
198
+ target.classList.remove(onDragOverCls)
199
+ }
200
+
201
+ onDragOver = e => {
202
+ e.preventDefault()
203
+ }
204
+
205
+ onDragStart = e => {
206
+ this.props.modifier({
207
+ onDrag: true
208
+ })
209
+ const cls = this.props.selectedFiles.length > 1
210
+ ? onDragCls + ' ' + onMultiDragCls
211
+ : onDragCls
212
+ addClass(this.dom, cls)
213
+ e.dataTransfer.setData('fromFile', JSON.stringify(this.props.file))
214
+ }
215
+
216
+ getDropFileList = data => {
217
+ const fromFile = data.getData('fromFile')
218
+ if (fromFile) {
219
+ return [JSON.parse(fromFile)]
220
+ }
221
+ const { files } = data
222
+ const res = []
223
+ for (let i = 0, len = files.length; i < len; i++) {
224
+ const item = files[i]
225
+ if (!item) {
226
+ continue
227
+ }
228
+ // let file = item.getAsFile()
229
+ const isRemote = false
230
+ const fileObj = getFolderFromFilePath(item.path, isRemote)
231
+ res.push({
232
+ ...fileObj,
233
+ type: typeMap.local
234
+ })
235
+ }
236
+ return res
237
+ }
238
+
239
+ onDrop = async e => {
240
+ e.preventDefault()
241
+ const fromFileManager = !!e?.dataTransfer?.files?.length
242
+ let { target } = e
243
+ if (!target) {
244
+ return
245
+ }
246
+ const fromFiles = this.getDropFileList(e.dataTransfer)
247
+ if (!fromFiles) {
248
+ return
249
+ }
250
+ while (!target.className.includes(fileItemCls)) {
251
+ target = target.parentNode
252
+ }
253
+ const id = target.getAttribute('data-id')
254
+ const type = target.getAttribute('data-type')
255
+ if (!type) {
256
+ return
257
+ }
258
+ let toFile = this.props[type + 'FileTree'][id] || {}
259
+ if (!toFile.id || !toFile.isDirectory) {
260
+ toFile = {
261
+ type,
262
+ ...getFolderFromFilePath(this.props[type + 'Path']),
263
+ isDirectory: false
264
+ }
265
+ }
266
+ this.onDropFile(fromFiles, toFile, fromFileManager)
267
+ }
268
+
269
+ onDragEnd = e => {
270
+ this.props.modifier({
271
+ onDrag: false
272
+ })
273
+ removeClass(this.dom, onDragCls, onMultiDragCls)
274
+ document.querySelectorAll('.' + onDragOverCls).forEach((d) => {
275
+ removeClass(d, onDragOverCls)
276
+ })
277
+ if (e && e.dataTransfer) {
278
+ const dt = e.dataTransfer
279
+ if (dt.items) {
280
+ // Use DataTransferItemList interface to remove the drag data
281
+ for (let i = 0, len = dt.items.length; i < len; i++) {
282
+ dt.items.remove(i)
283
+ }
284
+ }
285
+ dt.clearData()
286
+ }
287
+ }
288
+
289
+ onDropFile = (fromFiles, toFile, fromFileManager) => {
290
+ const { type: fromType } = fromFiles[0]
291
+ const {
292
+ id,
293
+ type: toType,
294
+ isDirectory: isDirectoryTo
295
+ } = toFile
296
+
297
+ let operation = ''
298
+
299
+ // same side and drop to file = drop to folder
300
+ if (!fromFileManager && fromType === toType && !isDirectoryTo) {
301
+ return
302
+ }
303
+
304
+ // drop from file manager
305
+ if (fromFileManager && toType === typeMap.local) {
306
+ operation = fileOperationsMap.cp
307
+ if (id) {
308
+ toFile = {
309
+ ...toFile,
310
+ ...getFolderFromFilePath(
311
+ resolve(toFile.path, toFile.name)
312
+ ),
313
+ id: undefined
314
+ }
315
+ }
316
+ }
317
+
318
+ // same side and drop to folder, do mv
319
+ if (fromType === toType && isDirectoryTo && !fromFileManager) {
320
+ operation = fileOperationsMap.mv
321
+ }
322
+
323
+ // other side, do transfer
324
+ this.transferDrop(fromFiles, toFile, operation)
325
+ }
326
+
327
+ transferDrop = (fromFiles, toFile, operation) => {
328
+ const files = this.isSelected(fromFiles[0])
329
+ ? this.props.selectedFiles
330
+ : fromFiles
331
+ return this.doTransferSelected(
332
+ null,
333
+ files,
334
+ resolve(toFile.path, toFile.name),
335
+ toFile.type,
336
+ operation
337
+ )
338
+ }
339
+
340
+ isSelected = file => {
341
+ return some(
342
+ this.props.selectedFiles,
343
+ f => f.id === file.id
344
+ )
345
+ }
346
+
347
+ doRename = () => {
348
+ const file = copy(this.state.file)
349
+ file.nameTemp = file.name
350
+ file.isEditing = true
351
+ this.props.modifier({
352
+ onEditFile: true
353
+ })
354
+ this.setState({
355
+ file
356
+ })
357
+ }
358
+
359
+ editPermission = () => {
360
+ this.openFileModeModal(this.state.file)
361
+ }
362
+
363
+ showInfo = () => {
364
+ const { type } = this.props
365
+ window.store.openFileInfoModal({
366
+ file: this.state.file,
367
+ tab: this.props.tab,
368
+ visible: true,
369
+ pid: this.props.pid,
370
+ sessionId: this.props.sessionId,
371
+ uidTree: this.props[`${type}UidTree`],
372
+ gidTree: this.props[`${type}GidTree`]
373
+ })
374
+ }
375
+
376
+ cancelNew = (type) => {
377
+ let list = this.props[type]
378
+ list = list.filter(p => p.id)
379
+ this.props.modifier({
380
+ [type]: list
381
+ })
382
+ }
383
+
384
+ localCreateNew = async file => {
385
+ const { nameTemp, isDirectory } = file
386
+ const { localPath } = this.props
387
+ const p = resolve(localPath, nameTemp)
388
+ const func = isDirectory
389
+ ? fs.mkdir
390
+ : fs.touch
391
+ const res = await func(p)
392
+ .then(() => true)
393
+ .catch(window.store.onError)
394
+ if (res) {
395
+ this.props.localList()
396
+ }
397
+ }
398
+
399
+ remoteCreateNew = async file => {
400
+ const { nameTemp, isDirectory } = file
401
+ const { remotePath, sftp } = this.props
402
+ const p = resolve(remotePath, nameTemp)
403
+ const func = isDirectory
404
+ ? sftp.mkdir
405
+ : sftp.touch
406
+ const res = await func(p)
407
+ .then(() => true)
408
+ .catch(window.store.onError)
409
+ if (res) {
410
+ await wait(500)
411
+ await this.props.remoteList()
412
+ }
413
+ }
414
+
415
+ selectAll = (e) => {
416
+ const { type } = this.props.file
417
+ this.props.selectAll(type, e)
418
+ }
419
+
420
+ createNew = file => {
421
+ const { type } = file
422
+ return this[`${type}CreateNew`](file)
423
+ }
424
+
425
+ getShiftSelected (file, type) {
426
+ const indexs = this.props.selectedFiles.map(
427
+ this.props.getIndex
428
+ )
429
+ const i = this.props.getIndex(file)
430
+ const lastI = this.props.getIndex(this.props.lastClickedFile)
431
+ const arr = [...indexs, i].sort(sorter)
432
+ const last = arr.length - 1
433
+ const from = arr[0]
434
+ const to = arr[last]
435
+ let [start, end] = [from, to]
436
+ if (indexs.includes(i)) {
437
+ const other = lastI > i ? from : to
438
+ ;[start, end] = [other, i].sort(sorter)
439
+ }
440
+ return this.props.getFileList(type).slice(start, end + 1)
441
+ }
442
+
443
+ onClick = e => {
444
+ const { file } = this.state
445
+ const {
446
+ id,
447
+ type
448
+ } = file
449
+ this.props.modifier({
450
+ lastClickedFile: file
451
+ })
452
+ this.onDragEnd(e)
453
+ if (!id) {
454
+ return this.props.modifier({
455
+ selectedFiles: []
456
+ })
457
+ }
458
+ const selectedFilesOld = copy(
459
+ this.props.selectedFiles
460
+ )
461
+ const isSameSide = selectedFilesOld.length &&
462
+ type === selectedFilesOld[0].type
463
+ let selectedFiles = [file]
464
+ if (isSameSide) {
465
+ if (
466
+ (e.ctrlKey && !isMac) ||
467
+ (e.metaKey && isMac)
468
+ ) {
469
+ const isSelected = some(
470
+ selectedFilesOld,
471
+ s => s.id === id
472
+ )
473
+ selectedFiles = isSelected
474
+ ? selectedFilesOld.filter(s => s.id !== id)
475
+ : [
476
+ ...copy(selectedFilesOld),
477
+ file
478
+ ]
479
+ } else if (e.shiftKey) {
480
+ selectedFiles = this.getShiftSelected(file, type)
481
+ }
482
+ }
483
+ this.props.modifier({
484
+ selectedFiles,
485
+ lastClickedFile: file
486
+ })
487
+ }
488
+
489
+ changeFileMode = async e => {
490
+ const {
491
+ file = {},
492
+ action
493
+ } = e.data || {}
494
+ if (
495
+ action !== commonActions.submitFileModeEdit ||
496
+ file.id !== this.state.file.id
497
+ ) {
498
+ return false
499
+ }
500
+ const { permission, type, path, name } = file
501
+ const func = type === typeMap.local
502
+ ? fs.chmod
503
+ : this.props.sftp.chmod
504
+ const p = resolve(path, name)
505
+ await func(p, permission).catch(window.store.onError)
506
+ window.removeEventListener('message', this.changeFileMode)
507
+ this.props[type + 'List']()
508
+ }
509
+
510
+ openFileModeModal = () => {
511
+ const { type } = this.props
512
+ window.addEventListener(
513
+ 'message', this.changeFileMode
514
+ )
515
+ window.store.openFileModeModal({
516
+ tab: this.props.tab,
517
+ visible: true,
518
+ uidTree: this.props[`${type}UidTree`],
519
+ gidTree: this.props[`${type}GidTree`]
520
+ }, this.state.file)
521
+ }
522
+
523
+ handleBlur = () => {
524
+ const file = copy(this.state.file)
525
+ const { nameTemp, name, id, type } = this.state.file
526
+ if (name === nameTemp) {
527
+ if (!id) {
528
+ return this.cancelNew(type)
529
+ }
530
+ delete file.nameTemp
531
+ delete file.isEditing
532
+ return this.setState({
533
+ file
534
+ })
535
+ }
536
+ if (!id) {
537
+ return this.createNew(file)
538
+ }
539
+ this.rename(name, nameTemp)
540
+ }
541
+
542
+ rename = (oldname, newname) => {
543
+ const { type } = this.props.file
544
+ return this[`${type}Rename`](oldname, newname)
545
+ }
546
+
547
+ localRename = async (oldname, newname) => {
548
+ const { localPath } = this.props
549
+ const p1 = resolve(localPath, oldname)
550
+ const p2 = resolve(localPath, newname)
551
+ await fs.rename(p1, p2).catch(window.store.onError)
552
+ this.props.localList()
553
+ }
554
+
555
+ remoteRename = async (oldname, newname) => {
556
+ const { remotePath, sftp } = this.props
557
+ const p1 = resolve(remotePath, oldname)
558
+ const p2 = resolve(remotePath, newname)
559
+ const res = await sftp.rename(p1, p2)
560
+ .catch(window.store.onError)
561
+ .then(() => true)
562
+ if (res) {
563
+ this.props.remoteList()
564
+ }
565
+ }
566
+
567
+ handleChange = e => {
568
+ const nameTemp = e.target.value
569
+ const file = copy(this.state.file)
570
+ file.nameTemp = nameTemp
571
+ this.setState({
572
+ file
573
+ })
574
+ }
575
+
576
+ enterDirectory = (e, file) => {
577
+ e && e.stopPropagation && e.stopPropagation()
578
+ const { type, name } = file || this.state.file
579
+ const n = `${type}Path`
580
+ const path = this.props[n]
581
+ const np = resolve(path, name)
582
+ const op = this.props[type + 'Path']
583
+ this.props.modifier({
584
+ [n]: np,
585
+ [n + 'Temp']: np
586
+ }, () => this.props[`${type}List`](
587
+ undefined,
588
+ undefined,
589
+ op
590
+ ))
591
+ }
592
+
593
+ openFile = file => {
594
+ const filePath = resolve(file.path, file.name)
595
+ fs.openFile(filePath)
596
+ .catch(window.store.onError)
597
+ }
598
+
599
+ removeFileEditEvent = () => {
600
+ delete this.eid
601
+ if (this.watchingFile) {
602
+ window.pre.ipcOffEvent('file-change', this.onFileChange)
603
+ window.pre.runGlobalAsync('unwatchFile', this.watchingFile)
604
+ delete this.watchingFile
605
+ }
606
+ window.removeEventListener('message', this.onFileEditEvent)
607
+ }
608
+
609
+ onFileEditEvent = e => {
610
+ const {
611
+ action,
612
+ id,
613
+ text,
614
+ path,
615
+ mode,
616
+ type,
617
+ noClose
618
+ } = e.data || {}
619
+ if (id !== this.eid) {
620
+ return false
621
+ }
622
+ if (
623
+ action === commonActions.fetchTextEditorText
624
+ ) {
625
+ this.fetchEditorText(path, type)
626
+ } else if (action === commonActions.submitTextEditorText) {
627
+ this.onSubmitEditFile(mode, type, path, text, noClose)
628
+ } else if (action === commonActions.onCloseTextEditor) {
629
+ this.removeFileEditEvent()
630
+ } else if (action === commonActions.editWithSystemEditor) {
631
+ this.editWithSystemEditor(text)
632
+ }
633
+ }
634
+
635
+ editWithSystemEditor = async (text) => {
636
+ const {
637
+ path,
638
+ name,
639
+ type
640
+ } = this.state.file
641
+ let tempPath = ''
642
+ if (type === typeMap.local) {
643
+ tempPath = window.pre.resolve(path, name)
644
+ } else {
645
+ const id = generate()
646
+ tempPath = window.pre.resolve(
647
+ window.pre.tempDir, `temp-${id}-${name}`
648
+ )
649
+ await fs.writeFile(tempPath, text)
650
+ }
651
+ this.watchingFile = tempPath
652
+ this.watchFile(tempPath)
653
+ }
654
+
655
+ onFileChange = (e, text) => {
656
+ postMessage({
657
+ action: commonActions.editWithSystemEditorDone,
658
+ data: {
659
+ id: this.eid,
660
+ text
661
+ }
662
+ })
663
+ }
664
+
665
+ watchFile = async (tempPath) => {
666
+ window.pre.runGlobalAsync('watchFile', tempPath)
667
+ fs.openFile(tempPath)
668
+ .catch(window.store.onError)
669
+ window.pre.showItemInFolder(tempPath)
670
+ window.pre.ipcOnEvent('file-change', this.onFileChange)
671
+ }
672
+
673
+ gotoFolderInTerminal = () => {
674
+ const {
675
+ path, name
676
+ } = this.state.file
677
+ const rp = resolve(path, name)
678
+ ;(
679
+ document.querySelector('.session-current .term-sftp-tabs .type-tab.terminal') ||
680
+ document.querySelector('.session-current .term-sftp-tabs .type-tab.ssh')
681
+ ).click()
682
+ this.timer = setTimeout(() => {
683
+ window.store.runQuickCommand(
684
+ `cd "${rp}"`
685
+ )
686
+ }, 500)
687
+ }
688
+
689
+ fetchEditorText = async (path, type) => {
690
+ // const sftp = sftpFunc()
691
+ const text = typeMap.remote === type
692
+ ? await this.props.sftp.readFile(path)
693
+ : await fs.readFile(path)
694
+ postMessage({
695
+ action: commonActions.loadTextEditorText,
696
+ data: {
697
+ text,
698
+ loading: false
699
+ }
700
+ })
701
+ }
702
+
703
+ onSubmitEditFile = async (mode, type, path, text, noClose) => {
704
+ const r = typeMap.remote === type
705
+ ? await this.props.sftp.writeFile(
706
+ path,
707
+ text,
708
+ mode
709
+ )
710
+ : await fs.writeFile(
711
+ path,
712
+ text,
713
+ mode
714
+ )
715
+ const data = {
716
+ loading: false
717
+ }
718
+ if (r && !noClose) {
719
+ data.id = ''
720
+ data.file = null
721
+ data.text = ''
722
+ }
723
+ postMessage({
724
+ action: commonActions.openTextEditor,
725
+ data
726
+ })
727
+ if (r && !noClose) {
728
+ this.props[`${type}List`]()
729
+ }
730
+ }
731
+
732
+ editFile = () => {
733
+ this.eid = generate()
734
+ postMessage({
735
+ action: commonActions.openTextEditor,
736
+ data: {
737
+ id: this.eid,
738
+ file: this.state.file
739
+ }
740
+ })
741
+ window.addEventListener('message', this.onFileEditEvent)
742
+ }
743
+
744
+ transferOrEnterDirectory = async (e, edit) => {
745
+ const { file } = this.state
746
+ const { isDirectory, type, size } = file
747
+ if (isDirectory) {
748
+ return this.enterDirectory(e)
749
+ }
750
+ if (!edit && type === typeMap.local) {
751
+ return this.openFile(this.state.file)
752
+ }
753
+ const remoteEdit = !edit && type === typeMap.remote && size < maxEditFileSize
754
+ if (
755
+ edit === true || remoteEdit
756
+ ) {
757
+ return this.editFile()
758
+ }
759
+ if (
760
+ this.props.tab?.host
761
+ ) {
762
+ this.transfer()
763
+ }
764
+ }
765
+
766
+ getTransferList = (
767
+ file,
768
+ toPathBase,
769
+ _typeTo,
770
+ operation
771
+ ) => {
772
+ const { name, path, type, isDirectory } = file
773
+ let typeTo = type === typeMap.local
774
+ ? typeMap.remote
775
+ : typeMap.local
776
+ if (_typeTo) {
777
+ typeTo = _typeTo
778
+ }
779
+ let toPath = type === typeMap.local
780
+ ? this.props[typeMap.remote + 'Path']
781
+ : this.props[typeMap.local + 'Path']
782
+ if (toPathBase) {
783
+ toPath = toPathBase
784
+ }
785
+ toPath = resolve(toPath, name)
786
+ const obj = {
787
+ typeFrom: type,
788
+ typeTo,
789
+ fromPath: resolve(path, name),
790
+ toPath,
791
+ id: generate(),
792
+ ...createTransferProps(this.props),
793
+ operation
794
+ }
795
+ if (isDirectory) {
796
+ Object.assign(obj, {
797
+ zip: true,
798
+ skipExpand: true
799
+ })
800
+ }
801
+ return [obj]
802
+ }
803
+
804
+ doTransferSelected = (
805
+ e,
806
+ selectedFiles = this.props.selectedFiles,
807
+ toPathBase,
808
+ typeTo,
809
+ operation
810
+ ) => {
811
+ let all = []
812
+ for (const f of selectedFiles) {
813
+ const arr = this.getTransferList(f, toPathBase, typeTo, operation)
814
+ all = [
815
+ ...all,
816
+ ...arr
817
+ ]
818
+ }
819
+ this.props.addTransferList(all)
820
+ }
821
+
822
+ transfer = async () => {
823
+ const { file } = this.state
824
+ const arr = this.getTransferList(file)
825
+ this.props.addTransferList(arr)
826
+ }
827
+
828
+ zipTransferDirectory = () => {
829
+ const { file } = this.state
830
+ const arr = this.getTransferList(file)
831
+ this.props.addTransferList(arr)
832
+ }
833
+
834
+ doEnterDirectory = (e) => {
835
+ this.enterDirectory(e)
836
+ }
837
+
838
+ refresh = () => {
839
+ this.props.onGoto(this.props.file.type)
840
+ }
841
+
842
+ del = async (delSelected) => {
843
+ const { file, selectedFiles } = this.props
844
+ const { type } = file
845
+ const files = delSelected
846
+ ? selectedFiles
847
+ : [file]
848
+ postMessage({
849
+ type: commonActions.closeContextMenu
850
+ })
851
+ await this.props.delFiles(type, files)
852
+ }
853
+
854
+ doTransfer = () => {
855
+ this.transfer()
856
+ }
857
+
858
+ newFile = () => {
859
+ return this.newItem(false)
860
+ }
861
+
862
+ newDirectory = () => {
863
+ return this.newItem(true)
864
+ }
865
+
866
+ showInDefaultFileManager = () => {
867
+ const { path, name } = this.state.file
868
+ const p = resolve(path, name)
869
+ window.pre.showItemInFolder(p)
870
+ }
871
+
872
+ newItem = (isDirectory) => {
873
+ const { type } = this.state.file
874
+ const list = this.props[type]
875
+ list.unshift({
876
+ name: '',
877
+ nameTemp: '',
878
+ isDirectory,
879
+ isEditing: true,
880
+ type
881
+ })
882
+ this.props.modifier({
883
+ [type]: list,
884
+ onEditFile: true
885
+ })
886
+ }
887
+
888
+ renderDelConfirmTitle (shouldShowSelectedMenu) {
889
+ const { file, selectedFiles } = this.props
890
+ const files = shouldShowSelectedMenu
891
+ ? selectedFiles
892
+ : [file]
893
+ return this.props.renderDelConfirmTitle(files, true)
894
+ }
895
+
896
+ showModeEdit (type, id) {
897
+ if (!id) {
898
+ return false
899
+ }
900
+ if (type === typeMap.remote) {
901
+ return true
902
+ }
903
+ return !isWin
904
+ }
905
+
906
+ renderContextItems () {
907
+ const {
908
+ file: {
909
+ type,
910
+ isDirectory,
911
+ size,
912
+ id
913
+ },
914
+ selectedFiles,
915
+ tab
916
+ } = this.props
917
+ const hasHost = !!tab.host
918
+ const { enableSsh } = tab
919
+ const transferText = type === typeMap.local
920
+ ? e(transferTypeMap.upload)
921
+ : e(transferTypeMap.download)
922
+ const iconType = type === typeMap.local
923
+ ? 'CloudUploadOutlined'
924
+ : 'CloudDownloadOutlined'
925
+ const len = selectedFiles.length
926
+ const shouldShowSelectedMenu = id &&
927
+ len > 1 &&
928
+ some(selectedFiles, d => d.id === id)
929
+ const delTxt = shouldShowSelectedMenu ? `${e('deleteAll')}(${len})` : m('del')
930
+ const canPaste = hasFileInClipboardText()
931
+ const showEdit = !isDirectory && id &&
932
+ size < maxEditFileSize
933
+ const res = []
934
+ if (isDirectory && id) {
935
+ res.push({
936
+ func: 'doEnterDirectory',
937
+ icon: 'EnterOutlined',
938
+ text: e('enter')
939
+ })
940
+ }
941
+ if (shouldShowSelectedMenu && hasHost) {
942
+ res.push({
943
+ func: 'doTransferSelected',
944
+ icon: iconType,
945
+ text: `${e('selected')}(${len})`
946
+ })
947
+ }
948
+ if (
949
+ isDirectory && id &&
950
+ (
951
+ (hasHost && enableSsh && type === typeMap.remote) ||
952
+ (type === typeMap.local && !hasHost)
953
+ )
954
+ ) {
955
+ res.push({
956
+ func: 'gotoFolderInTerminal',
957
+ icon: 'CodeOutlined',
958
+ text: e('gotoFolderInTerminal')
959
+ })
960
+ }
961
+ if (!(!id || !hasHost || shouldShowSelectedMenu)) {
962
+ res.push({
963
+ func: 'doTransfer',
964
+ icon: iconType,
965
+ text: transferText
966
+ })
967
+ }
968
+ if (!isDirectory && id && type === typeMap.local) {
969
+ res.push({
970
+ func: 'transferOrEnterDirectory',
971
+ icon: 'ArrowRightOutlined',
972
+ text: e('open')
973
+ })
974
+ }
975
+ if (id && type === typeMap.local) {
976
+ res.push({
977
+ func: 'showInDefaultFileManager',
978
+ icon: 'ContainerOutlined',
979
+ text: e('showInDefaultFileMananger')
980
+ })
981
+ }
982
+ if (showEdit) {
983
+ res.push({
984
+ func: 'editFile',
985
+ icon: 'EditOutlined',
986
+ text: e('edit')
987
+ })
988
+ }
989
+ if (id) {
990
+ res.push({
991
+ func: 'del',
992
+ icon: 'CloseCircleOutlined',
993
+ text: delTxt,
994
+ noAutoClose: true,
995
+ requireConfirm: true,
996
+ confirmTitle: this.renderDelConfirmTitle(shouldShowSelectedMenu),
997
+ args: [shouldShowSelectedMenu]
998
+ })
999
+ res.push({
1000
+ func: 'onCopy',
1001
+ icon: 'CopyOutlined',
1002
+ text: m('copy'),
1003
+ subText: `${ctrlOrCmd}+c`
1004
+ })
1005
+ res.push({
1006
+ func: 'onCut',
1007
+ icon: 'FileExcelOutlined',
1008
+ text: m('cut'),
1009
+ subText: `${ctrlOrCmd}+x`
1010
+ })
1011
+ }
1012
+ res.push({
1013
+ func: 'onPaste',
1014
+ icon: 'CopyOutlined',
1015
+ text: m('paste'),
1016
+ disabled: !canPaste,
1017
+ subText: `${ctrlOrCmd}+v`
1018
+ })
1019
+ if (id) {
1020
+ res.push({
1021
+ func: 'doRename',
1022
+ icon: 'EditOutlined',
1023
+ text: e('rename')
1024
+ })
1025
+ res.push({
1026
+ func: 'onCopyPath',
1027
+ icon: 'CopyOutlined',
1028
+ text: m('copyFilePath')
1029
+ })
1030
+ }
1031
+ res.push({
1032
+ func: 'newFile',
1033
+ icon: 'FileAddOutlined',
1034
+ text: e('newFile'),
1035
+ subText: `${ctrlOrCmd}+v`
1036
+ })
1037
+ res.push({
1038
+ func: 'newDirectory',
1039
+ icon: 'FolderAddOutlined',
1040
+ text: e('newFolder'),
1041
+ subText: `${ctrlOrCmd}+v`
1042
+ })
1043
+ res.push({
1044
+ func: 'selectAll',
1045
+ icon: 'CheckSquareOutlined',
1046
+ text: e('selectAll'),
1047
+ subText: `${ctrlOrCmd}+a`
1048
+ })
1049
+ res.push({
1050
+ func: 'refresh',
1051
+ icon: 'ReloadOutlined',
1052
+ text: e('refresh')
1053
+ })
1054
+ if (this.showModeEdit(type, id)) {
1055
+ res.push({
1056
+ func: 'editPermission',
1057
+ icon: 'LockOutlined',
1058
+ text: e('editPermission')
1059
+ })
1060
+ }
1061
+ if (id) {
1062
+ res.push({
1063
+ func: 'showInfo',
1064
+ icon: 'InfoCircleOutlined',
1065
+ text: e('info')
1066
+ })
1067
+ }
1068
+ return res
1069
+ }
1070
+
1071
+ onContextAction = e => {
1072
+ const {
1073
+ action,
1074
+ id,
1075
+ args = [],
1076
+ func
1077
+ } = e.data || {}
1078
+ if (
1079
+ action !== commonActions.clickContextMenu ||
1080
+ id !== this.uid ||
1081
+ !this[func]
1082
+ ) {
1083
+ return false
1084
+ }
1085
+ window.removeEventListener('message', this.onContextAction)
1086
+ this[func](...args)
1087
+ }
1088
+
1089
+ onContextMenu = e => {
1090
+ e.preventDefault()
1091
+ const { file } = this.state
1092
+ const selected = this.isSelected(file)
1093
+ if (!selected) {
1094
+ this.onClick(e)
1095
+ }
1096
+ this.props.modifier({
1097
+ lastClickedFile: file
1098
+ })
1099
+ const items = this.renderContextItems()
1100
+ this.uid = generate()
1101
+ window.store.openContextMenu({
1102
+ items,
1103
+ id: this.uid,
1104
+ pos: computePos(e)
1105
+ })
1106
+ window.addEventListener('message', this.onContextAction)
1107
+ }
1108
+
1109
+ renderEditing (file) {
1110
+ const {
1111
+ nameTemp,
1112
+ isDirectory
1113
+ } = file
1114
+ const Icon = isDirectory ? FolderOutlined : FileOutlined
1115
+ const pre = <Icon />
1116
+ return (
1117
+ <div className='sftp-item'>
1118
+ <Input
1119
+ value={nameTemp}
1120
+ addonBefore={pre}
1121
+ onChange={this.handleChange}
1122
+ onBlur={this.handleBlur}
1123
+ onPressEnter={this.handleBlur}
1124
+ />
1125
+ </div>
1126
+ )
1127
+ }
1128
+
1129
+ renderProp = ({ name, style }) => {
1130
+ const { file } = this.state
1131
+ let value = file[name]
1132
+ let typeIcon = null
1133
+ let symbolicLinkText = null
1134
+ const {
1135
+ isDirectory,
1136
+ isSymbolicLink
1137
+ } = file
1138
+ if (isDirectory && name === 'size') {
1139
+ value = null
1140
+ } else if (!isDirectory && name === 'size') {
1141
+ value = filesize(value)
1142
+ } else if (name === 'owner') {
1143
+ const { type } = this.props
1144
+ value = this.props[`${type}UidTree`]['' + value] || value
1145
+ } else if (name === 'group') {
1146
+ const { type } = this.props
1147
+ value = this.props[`${type}GidTree`]['' + value] || value
1148
+ }
1149
+ if (name === 'name') {
1150
+ // const Icon = isDirectory
1151
+ // ? FolderOutlined
1152
+ // : FileOutlined
1153
+ typeIcon = <ExtIcon file={file} className='mg1r' />
1154
+ symbolicLinkText = isSymbolicLink
1155
+ ? <sup className='color-blue symbolic-link-icon'>*</sup>
1156
+ : null
1157
+ } else if (name === 'mode') {
1158
+ value = permission2mode(mode2permission(value))
1159
+ } else if (name.toLowerCase().includes('time')) {
1160
+ value = time(value)
1161
+ }
1162
+ return (
1163
+ <div
1164
+ key={name}
1165
+ title={value}
1166
+ className={`sftp-file-prop noise shi-${name}`}
1167
+ style={style}
1168
+ >
1169
+ {typeIcon}
1170
+ {symbolicLinkText}
1171
+ {value}
1172
+ </div>
1173
+ )
1174
+ }
1175
+
1176
+ render () {
1177
+ const { type, selectedFiles, draggable = true, properties = [], onDragStart, cls = '' } = this.props
1178
+ const { file } = this.state
1179
+ const {
1180
+ isDirectory,
1181
+ id,
1182
+ isEditing
1183
+ } = file
1184
+ if (isEditing) {
1185
+ return this.renderEditing(file)
1186
+ }
1187
+ const selected = some(selectedFiles.filter(d => d), s => s.id === id)
1188
+ const className = classnames('sftp-item', cls, type, {
1189
+ directory: isDirectory,
1190
+ selected
1191
+ })
1192
+ const props = {
1193
+ className,
1194
+ draggable,
1195
+ onDoubleClick: this.transferOrEnterDirectory,
1196
+ ...pick(this, [
1197
+ 'onContextMenu',
1198
+ 'onClick',
1199
+ 'onDrag',
1200
+ 'onDragEnter',
1201
+ 'onDragExit',
1202
+ 'onDragLeave',
1203
+ 'onDragOver',
1204
+ 'onDrop',
1205
+ 'onDragEnd'
1206
+ ]),
1207
+ onDragStart: onDragStart || this.onDragStart
1208
+ }
1209
+ return (
1210
+ <div
1211
+ {...props}
1212
+ data-id={id}
1213
+ id={this.id}
1214
+ data-type={type}
1215
+ title={file.name}
1216
+ >
1217
+ <div className='file-bg' />
1218
+ <div className='file-props'>
1219
+ {
1220
+ properties.map(this.renderProp)
1221
+ }
1222
+ </div>
1223
+ </div>
1224
+ )
1225
+ }
1226
+ }