@electerm/electerm-react 1.51.3 → 1.51.8

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 (54) hide show
  1. package/client/common/constants.js +1 -2
  2. package/client/common/db.js +10 -9
  3. package/client/components/batch-op/batch-op.jsx +16 -5
  4. package/client/components/bookmark-form/index.jsx +1 -1
  5. package/client/components/bookmark-form/ssh-form.jsx +3 -23
  6. package/client/components/bookmark-form/use-submit.jsx +6 -15
  7. package/client/components/context-menu/context-menu.styl +5 -5
  8. package/client/components/context-menu/history.jsx +2 -11
  9. package/client/components/context-menu/sub-tab-menu.jsx +1 -1
  10. package/client/components/footer/footer-entry.jsx +1 -6
  11. package/client/components/layout/layout-item.jsx +2 -2
  12. package/client/components/main/main.jsx +9 -5
  13. package/client/components/session/session.jsx +15 -1
  14. package/client/components/session/session.styl +3 -2
  15. package/client/components/setting-panel/list.styl +0 -1
  16. package/client/components/setting-panel/on-tree-drop.js +5 -5
  17. package/client/components/setting-panel/setting-modal.jsx +0 -12
  18. package/client/components/sftp/confirm-modal-store.jsx +0 -7
  19. package/client/components/sftp/file-mode-modal.jsx +2 -2
  20. package/client/components/sftp/sftp-entry.jsx +2 -2
  21. package/client/components/sftp/transfer-conflict-store.jsx +69 -66
  22. package/client/components/sftp/transport-action-store.jsx +32 -50
  23. package/client/components/sftp/transports-action-store.jsx +15 -15
  24. package/client/components/sftp/transports-ui-store.jsx +9 -5
  25. package/client/components/sidebar/bookmark-select.jsx +1 -1
  26. package/client/components/sidebar/bookmark.jsx +4 -63
  27. package/client/components/sidebar/history-item.jsx +34 -0
  28. package/client/components/sidebar/history.jsx +17 -52
  29. package/client/components/sidebar/index.jsx +4 -34
  30. package/client/components/sidebar/sidebar-panel.jsx +107 -0
  31. package/client/components/sidebar/sidebar.styl +14 -0
  32. package/client/components/sidebar/transfer-list-control.jsx +1 -0
  33. package/client/components/sidebar/transfer.styl +1 -1
  34. package/client/components/sidebar/transport-ui.jsx +179 -37
  35. package/client/components/tabs/index.jsx +4 -4
  36. package/client/components/tabs/tab.jsx +19 -10
  37. package/client/components/tree-list/tree-list.jsx +8 -10
  38. package/client/entry/worker.js +5 -3
  39. package/client/store/bookmark-group.js +3 -5
  40. package/client/store/common.js +11 -1
  41. package/client/store/db-upgrade.js +0 -2
  42. package/client/store/index.js +0 -3
  43. package/client/store/init-state.js +4 -3
  44. package/client/store/item.js +0 -19
  45. package/client/store/load-data.js +2 -0
  46. package/client/store/setting.js +2 -51
  47. package/client/store/sidebar.js +7 -8
  48. package/client/store/sync.js +7 -7
  49. package/client/store/tab.js +72 -4
  50. package/client/store/transfer-history.js +3 -9
  51. package/client/store/transfer-list.js +75 -75
  52. package/client/store/watch.js +9 -1
  53. package/package.json +1 -1
  54. package/client/components/setting-panel/tab-history.jsx +0 -43
@@ -2,65 +2,30 @@
2
2
  * history select
3
3
  */
4
4
 
5
- import { memo } from 'react'
6
- import ItemList from '../setting-panel/list'
7
- import { pick } from 'lodash-es'
8
- import { EditOutlined, PushpinOutlined } from '@ant-design/icons'
9
- import { Tooltip } from 'antd'
5
+ import { auto } from 'manate/react'
6
+ import HistoryItem from './history-item'
10
7
 
11
- const e = window.translate
12
-
13
- export default memo(function HistoryPanel (props) {
8
+ export default auto(function HistoryPanel (props) {
14
9
  const { store } = window
15
10
  const {
16
- openedSideBar,
17
- pinned,
18
- history,
19
- activeItemId
20
- } = props
21
- if (openedSideBar !== 'history') {
22
- return null
23
- }
24
- const prps = {
25
- className: 'font16 mg1x mg2l pointer iblock control-icon'
26
- }
27
- const prps1 = {
28
- className: prps.className + (pinned ? ' pinned' : '')
29
- }
30
- function handleClickItem (item) {
31
- store.onSelectHistory(item.id)
32
- }
11
+ history
12
+ } = store
33
13
  return (
34
14
  <div
35
- className='sidebar-panel history-panel animate-fast'
36
- {...pick(props, ['onMouseEnter', 'onMouseLeave'])}
15
+ className='sidebar-panel-history'
37
16
  >
38
- <div className='pd1y pd2t pd2x'>
39
- <div className='fix'>
40
- <div className='fleft'>{e('history')}</div>
41
- <div className='fleft'>
42
- <Tooltip title={`${e('edit')} ${e('history')}`}>
43
- <EditOutlined
44
- className='font16 mg1x mg2l pointer iblock control-icon icon-do-edit'
45
- onClick={store.handleEditHistory}
46
- />
47
- </Tooltip>
48
- <Tooltip title={e('pin')}>
49
- <PushpinOutlined
50
- {...prps1}
51
- onClick={store.handlePin}
52
- />
53
- </Tooltip>
54
- </div>
55
- </div>
56
- </div>
57
17
  <div className='pd2x'>
58
- <ItemList
59
- type='history'
60
- list={history || []}
61
- onClickItem={handleClickItem}
62
- activeItemId={activeItemId}
63
- />
18
+ {
19
+ history.map((item, i) => {
20
+ return (
21
+ <HistoryItem
22
+ key={item.id}
23
+ index={i}
24
+ item={item}
25
+ />
26
+ )
27
+ })
28
+ }
64
29
  </div>
65
30
  </div>
66
31
  )
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  BookOutlined,
3
- ClockCircleOutlined,
4
3
  CloudSyncOutlined,
5
4
  InfoCircleOutlined,
6
5
  PictureOutlined,
@@ -11,8 +10,7 @@ import {
11
10
  } from '@ant-design/icons'
12
11
  import { useRef, memo } from 'react'
13
12
  import { Tooltip } from 'antd'
14
- import BookMarksWrap from './bookmark'
15
- import HistoryWrap from './history'
13
+ import SideBarPanel from './sidebar-panel'
16
14
  import TransferList from './transfer-list'
17
15
  import MenuBtn from '../context-menu/menu-btn'
18
16
  import {
@@ -43,7 +41,7 @@ export default memo(function Sidebar (props) {
43
41
  transferTab,
44
42
  showModal,
45
43
  showInfoModal,
46
- activeItemId
44
+ sidebarPanelTab
47
45
  } = props
48
46
 
49
47
  const { store } = window
@@ -66,14 +64,6 @@ export default memo(function Sidebar (props) {
66
64
  store.setOpenedSideBar('bookmarks')
67
65
  }
68
66
 
69
- const handleMouseEnterHistory = () => {
70
- if (pinned) {
71
- return false
72
- }
73
- clearTimeout(handler.current)
74
- store.setOpenedSideBar('history')
75
- }
76
-
77
67
  const handleShowUpgrade = () => {
78
68
  store.storeAssign({
79
69
  _upgradeInfo: JSON.stringify({
@@ -90,7 +80,6 @@ export default memo(function Sidebar (props) {
90
80
  openSettingSync,
91
81
  openTerminalThemes,
92
82
  onClickBookmark,
93
- onClickHistory,
94
83
  toggleBatchOp,
95
84
  setLeftSidePanelWidth
96
85
  } = store
@@ -105,7 +94,6 @@ export default memo(function Sidebar (props) {
105
94
  const settingActive = showSetting && settingTab === settingMap.setting && settingItem.id === 'setting-common'
106
95
  const syncActive = showSetting && settingTab === settingMap.setting && settingItem.id === 'setting-sync'
107
96
  const themeActive = showSetting && settingTab === settingMap.terminalThemes
108
- const historyActive = showSetting && settingTab === settingMap.history
109
97
  const bookmarksActive = showSetting && settingTab === settingMap.bookmarks
110
98
  const sideProps = openedSideBar
111
99
  ? {
@@ -154,17 +142,6 @@ export default memo(function Sidebar (props) {
154
142
  className='font20 iblock control-icon'
155
143
  />
156
144
  </SideIcon>
157
- <SideIcon
158
- title={e(settingMap.history)}
159
- active={historyActive}
160
- >
161
- <ClockCircleOutlined
162
- onMouseEnter={handleMouseEnterHistory}
163
- onMouseLeave={handleMouseLeave}
164
- onClick={onClickHistory}
165
- className='font20 iblock control-icon'
166
- />
167
- </SideIcon>
168
145
  <TransferList {...transferProps} />
169
146
  <SideIcon
170
147
  title={e(settingMap.terminalThemes)}
@@ -231,19 +208,12 @@ export default memo(function Sidebar (props) {
231
208
  setLeftSidePanelWidth={setLeftSidePanelWidth}
232
209
  leftSidebarWidth={leftSidebarWidth}
233
210
  >
234
- <BookMarksWrap
211
+ <SideBarPanel
235
212
  pinned={pinned}
213
+ sidebarPanelTab={sidebarPanelTab}
236
214
  onMouseEnter={handleMouseEnterBookmark}
237
215
  onMouseLeave={handleMouseLeave}
238
216
  />
239
- <HistoryWrap
240
- history={props.history}
241
- openedSideBar={openedSideBar}
242
- onMouseEnter={handleMouseEnterHistory}
243
- onMouseLeave={handleMouseLeave}
244
- activeItemId={activeItemId}
245
- pinned={pinned}
246
- />
247
217
  </SidePanel>
248
218
  </div>
249
219
  )
@@ -0,0 +1,107 @@
1
+ /**
2
+ * bookmark select
3
+ */
4
+
5
+ import { memo } from 'react'
6
+ import BookmarkWrap from './bookmark'
7
+ import History from './history'
8
+ import { pick } from 'lodash-es'
9
+ import { Tabs, Tooltip } from 'antd'
10
+ import { ArrowsAltOutlined, EditOutlined, PlusCircleOutlined, ShrinkOutlined, PushpinOutlined } from '@ant-design/icons'
11
+
12
+ const e = window.translate
13
+
14
+ export default memo(function SidebarPanel (props) {
15
+ const { sidebarPanelTab, pinned } = props
16
+ const { store } = window
17
+ const prps = {
18
+ className: 'font16 mg1x mg2l pointer iblock control-icon'
19
+ }
20
+ const prps1 = {
21
+ className: prps.className + (pinned ? ' pinned' : '')
22
+ }
23
+ const tabsProps = {
24
+ activeKey: sidebarPanelTab,
25
+ onChange: store.handleSidebarPanelTab,
26
+ items: [
27
+ {
28
+ key: 'bookmarks',
29
+ label: e('bookmarks'),
30
+ children: null
31
+ },
32
+ {
33
+ key: 'history',
34
+ label: e('history'),
35
+ children: null
36
+ }
37
+ ]
38
+ }
39
+ const pop1 = {
40
+ ...prps,
41
+ onClick: store.onNewSsh
42
+ }
43
+ const pop2 = {
44
+ ...prps,
45
+ onClick: store.expandBookmarks
46
+ }
47
+ const pop3 = {
48
+ ...prps,
49
+ onClick: store.collapseBookmarks
50
+ }
51
+
52
+ function renderExpandIcons () {
53
+ if (sidebarPanelTab !== 'bookmarks') {
54
+ return null
55
+ }
56
+ return [
57
+ <Tooltip title={e('expandAll')} key='expand'>
58
+ <ArrowsAltOutlined
59
+ {...pop2}
60
+ />
61
+ </Tooltip>,
62
+ <Tooltip title={e('collapseAll')} key='collapse'>
63
+ <ShrinkOutlined
64
+ {...pop3}
65
+ />
66
+ </Tooltip>
67
+ ]
68
+ }
69
+ return (
70
+ <div
71
+ className='sidebar-panel bookmarks-panel animate-fast'
72
+ {...pick(props, ['onMouseEnter', 'onMouseLeave'])}
73
+ >
74
+ <div className='sidebar-pin-top'>
75
+ <div className='pd1y pd2t pd2x sidebar-panel-control'>
76
+ <Tooltip title={e('newBookmark')}>
77
+ <PlusCircleOutlined
78
+ {...pop1}
79
+ />
80
+ </Tooltip>
81
+ <Tooltip title={`${e('edit')} ${e('bookmarks')}`}>
82
+ <EditOutlined
83
+ {...pop1}
84
+ />
85
+ </Tooltip>
86
+ {
87
+ renderExpandIcons()
88
+ }
89
+ <Tooltip title={e('pin')}>
90
+ <PushpinOutlined
91
+ {...prps1}
92
+ onClick={store.handlePin}
93
+ />
94
+ </Tooltip>
95
+ </div>
96
+ <div className='pd1y pd2x'>
97
+ <Tabs {...tabsProps} />
98
+ </div>
99
+ </div>
100
+ {
101
+ sidebarPanelTab === 'bookmarks'
102
+ ? <BookmarkWrap {...props} />
103
+ : <History store={store} />
104
+ }
105
+ </div>
106
+ )
107
+ })
@@ -26,6 +26,20 @@
26
26
  box-shadow 0px 0px 3px 3px alpha(main, .1)
27
27
  .item-list
28
28
  padding-right 0
29
+ .sidebar-pin-top
30
+ position absolute
31
+ left 0
32
+ right 0
33
+ top 36px
34
+ height 112px
35
+ .sidebar-panel-bookmarks
36
+ .sidebar-panel-history
37
+ position absolute
38
+ left 0
39
+ right 0
40
+ top 148px
41
+ bottom 0
42
+ overflow-y scroll
29
43
  .not-system-ui.is-mac
30
44
  .sidebar-bar
31
45
  margin-top 20px
@@ -117,6 +117,7 @@ export default class TransferModalUI extends Component {
117
117
  return (
118
118
  <Transport
119
119
  transfer={t}
120
+ index={i}
120
121
  key={id + ':tr:' + i}
121
122
  />
122
123
  )
@@ -4,4 +4,4 @@
4
4
  .ant-badge-multiple-words
5
5
  padding 0 3px
6
6
  .transfer-list-card
7
- max-width calc(100% - 38px)
7
+ width calc(100% - 48px)
@@ -1,18 +1,23 @@
1
1
  /**
2
2
  * transporter UI component
3
3
  */
4
- import React from 'react'
4
+ import { useRef } from 'react'
5
5
  import Tag from '../sftp/transfer-tag'
6
+ import { Flex } from 'antd'
6
7
  import {
7
8
  CloseCircleOutlined,
8
9
  PlayCircleOutlined,
9
- PauseCircleOutlined
10
+ PauseCircleOutlined,
11
+ VerticalAlignTopOutlined
10
12
  } from '@ant-design/icons'
13
+ import { action } from 'manate'
14
+ import { addClass, removeClass } from '../../common/class'
11
15
  import './transfer.styl'
12
16
 
13
17
  const e = window.translate
14
18
 
15
19
  export default function Transporter (props) {
20
+ const dom = useRef()
16
21
  const {
17
22
  fromPath,
18
23
  toPath,
@@ -29,12 +34,109 @@ export default function Transporter (props) {
29
34
  inited,
30
35
  id
31
36
  } = props.transfer
37
+ const { index } = props
38
+ const onDragCls = 'ondrag-tr'
39
+ const onDragOverCls = 'dragover-tr'
40
+ function moveToTop () {
41
+ action(function () {
42
+ const arr = window.store.fileTransfers
43
+ if (index > 0) {
44
+ const [item] = arr.splice(index, 1)
45
+ arr.unshift(item)
46
+ }
47
+ })()
48
+ }
32
49
  function cancel () {
33
50
  window.store.cancelTransfer(id)
34
51
  }
35
52
  function handlePauseOrResume () {
36
53
  window.store.toggleTransfer(id)
37
54
  }
55
+
56
+ function clearCls () {
57
+ document.querySelectorAll('.' + onDragOverCls).forEach((d) => {
58
+ removeClass(d, onDragOverCls)
59
+ })
60
+ }
61
+
62
+ function onDrag () {
63
+ addClass(dom.current, onDragCls)
64
+ }
65
+
66
+ function onDragEnter () {
67
+ clearCls()
68
+ addClass(dom.current, onDragOverCls)
69
+ }
70
+
71
+ function onDragExit () {
72
+ // debug('ondragexit')
73
+ // let {target} = e
74
+ // removeClass(target, 'sftp-dragover')
75
+ }
76
+
77
+ function onDragLeave (e) {
78
+ // debug('ondragleave')
79
+ const { target } = e
80
+ removeClass(target, onDragOverCls)
81
+ }
82
+
83
+ function onDragOver (e) {
84
+ // debug('ondragover')
85
+ // debug(e.target)
86
+ // removeClass(dom.current, 'sftp-dragover')
87
+ e.preventDefault()
88
+ }
89
+
90
+ function onDragStart (e) {
91
+ // debug('ondragstart')
92
+ // debug(e.target)
93
+ e.dataTransfer.setData('id', JSON.stringify(dom.current.getAttribute('data-id')))
94
+ // e.effectAllowed = 'copyMove'
95
+ }
96
+
97
+ function onDrop (e) {
98
+ e.preventDefault()
99
+ const { target } = e
100
+ if (!target) {
101
+ return
102
+ }
103
+ let onDropTab = target
104
+ while (onDropTab) {
105
+ if (onDropTab.classList && onDropTab.classList.contains('sftp-transport')) {
106
+ break
107
+ }
108
+ onDropTab = onDropTab.parentElement
109
+ }
110
+ const fromId = JSON.parse(e.dataTransfer.getData('id'))
111
+ if (!onDropTab || !fromId) {
112
+ return
113
+ }
114
+
115
+ const dropId = onDropTab.getAttribute('data-id')
116
+ if (!dropId || dropId === fromId) {
117
+ return
118
+ }
119
+
120
+ const arr = window.store.fileTransfers
121
+ const indexFrom = arr.findIndex(t => t.id === fromId)
122
+ let indexDrop = arr.findIndex(t => t.id === dropId)
123
+ if (indexFrom >= 0 && indexDrop >= 0) {
124
+ // Reorder tabs and update batch
125
+ action(function () {
126
+ const [tr] = arr.splice(indexFrom, 1)
127
+ if (indexFrom < indexDrop) {
128
+ indexDrop = indexDrop - 1
129
+ }
130
+ arr.splice(indexDrop, 0, tr)
131
+ })()
132
+ }
133
+ }
134
+
135
+ function onDragEnd (e) {
136
+ removeClass(dom.current, onDragCls)
137
+ clearCls()
138
+ e && e.dataTransfer && e.dataTransfer.clearData()
139
+ }
38
140
  const isTransfer = typeTo !== typeFrom
39
141
  const Icon = !pausing ? PauseCircleOutlined : PlayCircleOutlined
40
142
  const pauseTitle = pausing ? e('resume') : e('pause')
@@ -49,6 +151,14 @@ export default function Transporter (props) {
49
151
  title={e('cancel')}
50
152
  />
51
153
  )
154
+ const toTopIcon = index === 0
155
+ ? null
156
+ : (
157
+ <VerticalAlignTopOutlined
158
+ className='transfer-control-icon pointer hover-black font14'
159
+ onClick={moveToTop}
160
+ />
161
+ )
52
162
  const controlIcon = isTransfer
53
163
  ? (
54
164
  <Icon
@@ -58,41 +168,73 @@ export default function Transporter (props) {
58
168
  />
59
169
  )
60
170
  : null
171
+ const flexProps = {
172
+ className: cls,
173
+ gap: 3,
174
+ title,
175
+ ref: dom,
176
+ id: `transfer-unit-${id}`,
177
+ draggable: true,
178
+ 'data-id': id,
179
+ onDrag,
180
+ onDragEnter,
181
+ onDragExit,
182
+ onDragLeave,
183
+ onDragOver,
184
+ onDragStart,
185
+ onDrop,
186
+ onDragEnd
187
+ }
61
188
  return (
62
- <div className={cls} title={title} id={`transfer-unit-${id}`}>
63
- <Tag
64
- transfer={{
65
- typeTo,
66
- typeFrom,
67
- error,
68
- inited
69
- }}
70
- />
71
- <span
72
- className='flex-child sftp-file sftp-local-file elli'
73
- title={fromPath}
74
- >{fromPathReal || fromPath}
75
- </span>
76
- <span className='flex-child sftp-transfer-arrow'>
77
-
78
- </span>
79
- <span
80
- className='flex-child sftp-file sftp-remote-file elli'
81
- >{toPathReal || toPath}
82
- </span>
83
- <span
84
- className='flex-child sftp-file-percent'
85
- >
86
- {percent || 0}%
87
- {speed ? `(${speed})` : null}
88
- </span>
89
- <span
90
- className='flex-child sftp-file-percent'
91
- >
92
- {passedTime || '-'}|{leftTime || '-'}
93
- </span>
94
- {controlIcon}
95
- {cancelIcon}
96
- </div>
189
+ <Flex
190
+ {...flexProps}
191
+ >
192
+ <Flex>
193
+ <Tag
194
+ transfer={{
195
+ typeTo,
196
+ typeFrom,
197
+ error,
198
+ inited
199
+ }}
200
+ />
201
+ </Flex>
202
+ <Flex>
203
+ <span
204
+ className='sftp-file sftp-local-file elli'
205
+ title={fromPath}
206
+ >{fromPathReal || fromPath}
207
+ </span>
208
+ </Flex>
209
+ <Flex>
210
+ <span className='sftp-transfer-arrow'>
211
+
212
+ </span>
213
+ </Flex>
214
+ <Flex>
215
+ <span
216
+ className='sftp-file sftp-remote-file elli'
217
+ >{toPathReal || toPath}
218
+ </span>
219
+ </Flex>
220
+ <Flex>
221
+ <span
222
+ className='sftp-file-percent'
223
+ >
224
+ {percent || 0}%
225
+ {speed ? `(${speed})` : null}
226
+ </span>
227
+ </Flex>
228
+ <Flex>
229
+ <span
230
+ className='sftp-file-percent'
231
+ >
232
+ {passedTime || '-'}|{leftTime || '-'}
233
+ </span>
234
+ </Flex>
235
+ <Flex>{controlIcon}</Flex>
236
+ <Flex>{cancelIcon}</Flex>
237
+ <Flex>{toTopIcon}</Flex>
238
+ </Flex>
97
239
  )
98
240
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React from 'react'
6
6
  import runIdle from '../../common/run-idle'
7
- import { findIndex, debounce } from 'lodash-es'
7
+ import { debounce } from 'lodash-es'
8
8
  import TabTitle from './tab-title'
9
9
  import {
10
10
  CodeFilled,
@@ -227,13 +227,13 @@ export default class Tabs extends React.Component {
227
227
 
228
228
  adjustScroll = () => {
229
229
  const { tabs, currentBatchTabId, batch } = this.props
230
- const index = findIndex(tabs, t => t.id === currentBatchTabId)
230
+ const index = tabs.findIndex(t => t.id === currentBatchTabId)
231
231
  const tabsDomWith = Array.from(
232
232
  document.querySelectorAll(`.v${batch + 1} .tab`)
233
- ).slice(0, index + 2).reduce((prev, c) => {
233
+ ).slice(0, index + 1).reduce((prev, c) => {
234
234
  return prev + c.clientWidth
235
235
  }, 0)
236
- const w = (index + 1) * tabMargin + 5 + tabsDomWith
236
+ const w = (index + 1) * tabMargin + tabsDomWith
237
237
  const tabsInnerWidth = this.getInnerWidth()
238
238
  const scrollLeft = w > tabsInnerWidth
239
239
  ? w - tabsInnerWidth
@@ -10,13 +10,14 @@ import {
10
10
  } from '@ant-design/icons'
11
11
  import { Tooltip, message } from 'antd'
12
12
  import classnames from 'classnames'
13
- import { findIndex, pick } from 'lodash-es'
13
+ import { pick } from 'lodash-es'
14
14
  import Input from '../common/input-auto-focus'
15
15
  import createName from '../../common/create-title'
16
16
  import { addClass, removeClass } from '../../common/class'
17
17
  import {
18
18
  terminalSshConfigType
19
19
  } from '../../common/constants'
20
+ import { action } from 'manate'
20
21
  import { shortcutDescExtend } from '../shortcuts/shortcut-handler.js'
21
22
 
22
23
  const e = window.translate
@@ -117,7 +118,13 @@ class Tab extends Component {
117
118
  }
118
119
 
119
120
  const fromTab = JSON.parse(e.dataTransfer.getData('fromFile'))
120
- const onDropTab = document.querySelector('.' + onDragOverCls)
121
+ let onDropTab = target
122
+ while (onDropTab) {
123
+ if (onDropTab.classList && onDropTab.classList.contains('tab')) {
124
+ break
125
+ }
126
+ onDropTab = onDropTab.parentElement
127
+ }
121
128
  if (!onDropTab || !fromTab) {
122
129
  return
123
130
  }
@@ -129,8 +136,8 @@ class Tab extends Component {
129
136
 
130
137
  const { id } = fromTab
131
138
  const storeTabs = window.store.tabs
132
- const indexFrom = findIndex(storeTabs, t => t.id === id)
133
- let indexDrop = findIndex(storeTabs, t => t.id === dropId)
139
+ const indexFrom = storeTabs.findIndex(t => t.id === id)
140
+ let indexDrop = storeTabs.findIndex(t => t.id === dropId)
134
141
 
135
142
  if (indexFrom >= 0 && indexDrop >= 0) {
136
143
  const targetTab = storeTabs[indexDrop]
@@ -146,12 +153,14 @@ class Tab extends Component {
146
153
  }
147
154
 
148
155
  // Reorder tabs and update batch
149
- const [tab] = storeTabs.splice(indexFrom, 1)
150
- tab.batch = targetTab.batch // Update the batch to match target tab's batch
151
- if (indexFrom < indexDrop) {
152
- indexDrop = indexDrop - 1
153
- }
154
- storeTabs.splice(indexDrop, 0, tab)
156
+ action(function () {
157
+ const [tab] = storeTabs.splice(indexFrom, 1)
158
+ tab.batch = targetTab.batch // Update the batch to match target tab's batch
159
+ if (indexFrom < indexDrop) {
160
+ indexDrop = indexDrop - 1
161
+ }
162
+ storeTabs.splice(indexDrop, 0, tab)
163
+ })()
155
164
  window.store.focus()
156
165
  }
157
166
  }