@electerm/electerm-react 3.15.178 → 3.15.187

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 (31) hide show
  1. package/client/common/bookmark-schemas.js +2 -0
  2. package/client/common/default-setting.js +2 -1
  3. package/client/common/drop-position.js +59 -0
  4. package/client/components/bookmark-form/bookmark-schema.js +2 -0
  5. package/client/components/bookmark-form/common/connection-hopping.jsx +33 -7
  6. package/client/components/bookmark-form/common/ssh-tunnel-form.jsx +25 -7
  7. package/client/components/bookmark-form/common/ssh-tunnels.jsx +1 -1
  8. package/client/components/bookmark-form/config/serial.js +14 -0
  9. package/client/components/quick-commands/on-drop.js +6 -1
  10. package/client/components/quick-commands/quick-commands-box.jsx +18 -2
  11. package/client/components/quick-commands/quick-commands-list-form.jsx +15 -4
  12. package/client/components/quick-commands/quick-commands-list.jsx +18 -2
  13. package/client/components/session/session-control.jsx +30 -12
  14. package/client/components/session/session.jsx +5 -0
  15. package/client/components/setting-panel/setting-common.jsx +1 -0
  16. package/client/components/sftp/address-bookmark-item.jsx +15 -2
  17. package/client/components/sidebar/transport-ui.jsx +21 -14
  18. package/client/components/tabs/index.jsx +18 -3
  19. package/client/components/tabs/tab.jsx +45 -9
  20. package/client/components/tabs/tabs.styl +8 -0
  21. package/client/components/terminal/attach-addon-custom.js +147 -36
  22. package/client/components/terminal/highlight-addon.js +26 -8
  23. package/client/components/terminal/term-search.jsx +1 -1
  24. package/client/components/terminal/terminal.jsx +75 -1
  25. package/client/components/terminal-info/format-disks.js +45 -0
  26. package/client/components/terminal-info/run-cmd.jsx +2 -26
  27. package/client/components/tree-list/tree-list.jsx +57 -39
  28. package/client/css/basic.styl +14 -0
  29. package/client/css/mobile.styl +18 -0
  30. package/client/store/item.js +10 -6
  31. package/package.json +1 -1
@@ -0,0 +1,45 @@
1
+ /**
2
+ * parse `df` output into disk list
3
+ */
4
+
5
+ export default function formatDisks (str) {
6
+ if (!str) {
7
+ return {
8
+ disks: []
9
+ }
10
+ }
11
+ const r = str.split('\n')
12
+ .slice(1)
13
+ .reduce((acc, s) => {
14
+ if (!s.trim()) {
15
+ return acc
16
+ }
17
+ const arr = s.trim().split(/ +/)
18
+ // df wraps long filesystem names (e.g. nfs "host:/very/long/export/path")
19
+ // onto a second line that starts with spaces and only holds the numeric
20
+ // columns. Merge such continuation lines back into the previous entry, so
21
+ // those disks (commonly nfs) are listed instead of dropped.
22
+ if (/^\s/.test(s) && acc.length) {
23
+ const prev = acc[acc.length - 1]
24
+ prev.size = arr[0]
25
+ prev.used = arr[1]
26
+ prev.avail = arr[2]
27
+ prev.usedPercent = arr[3]
28
+ prev.mount = arr[4]
29
+ return acc
30
+ }
31
+ acc.push({
32
+ filesystem: arr[0],
33
+ size: arr[1],
34
+ used: arr[2],
35
+ avail: arr[3],
36
+ usedPercent: arr[4],
37
+ mount: arr[5]
38
+ })
39
+ return acc
40
+ }, [])
41
+ .filter(d => d.filesystem && d.mount)
42
+ return {
43
+ disks: r
44
+ }
45
+ }
@@ -6,6 +6,7 @@ import { runCmd } from '../terminal/terminal-apis'
6
6
  import { useEffect } from 'react'
7
7
  import wait from '../../common/wait'
8
8
  import parseInt10 from '../../common/parse-int10'
9
+ import formatDisks from './format-disks'
9
10
 
10
11
  function formatActivities (str) {
11
12
  if (!str) {
@@ -31,31 +32,6 @@ function formatActivities (str) {
31
32
  }
32
33
  }
33
34
 
34
- function formatDisks (str) {
35
- if (!str) {
36
- return {
37
- disks: []
38
- }
39
- }
40
- const r = str.split('\n')
41
- .slice(1)
42
- .map(s => {
43
- const arr = s.split(/ +/)
44
- return {
45
- filesystem: arr[0],
46
- size: arr[1],
47
- used: arr[2],
48
- avail: arr[3],
49
- usedPercent: arr[4],
50
- mount: arr[5]
51
- }
52
- })
53
- .filter(d => d.filesystem)
54
- return {
55
- disks: r
56
- }
57
- }
58
-
59
35
  function formatCpu (str) {
60
36
  if (!str) {
61
37
  return {
@@ -206,7 +182,7 @@ export default (props) => {
206
182
  },
207
183
  {
208
184
  name: 'disks',
209
- cmd: 'df -h',
185
+ cmd: 'df -hP',
210
186
  interval: 10000,
211
187
  delay: 1600,
212
188
  formatter: formatDisks
@@ -14,6 +14,7 @@ import {
14
14
  } from '../../common/constants'
15
15
  import { getItemJSON, setItemJSON } from '../../common/safe-local-storage'
16
16
  import findParentBySel from '../../common/find-parent'
17
+ import { isDropAfterHalf, setDropIndicator, clearDropIndicator } from '../../common/drop-position'
17
18
  import copy from 'json-deep-copy'
18
19
  import NewButtonsGroup from './bookmark-toolbar'
19
20
  import findBookmarkGroupId from '../../common/find-bookmark-group-id'
@@ -479,8 +480,10 @@ export default class ItemListTree extends Component {
479
480
  )
480
481
  }
481
482
 
482
- onDragEnter = e => {
483
- e.preventDefault()
483
+ // resolve the .tree-item under the pointer and show a directional
484
+ // drop indicator (top edge = before, bottom edge = after).
485
+ // NOTE: preventDefault on dragover is required for onDrop to fire.
486
+ updateDropIndicator = e => {
484
487
  let {
485
488
  target
486
489
  } = e
@@ -488,7 +491,12 @@ export default class ItemListTree extends Component {
488
491
  if (tar) {
489
492
  target = tar
490
493
  }
491
- target.classList.add('item-dragover-top')
494
+ setDropIndicator(target, isDropAfterHalf(e, target))
495
+ }
496
+
497
+ onDragEnter = e => {
498
+ e.preventDefault()
499
+ this.updateDropIndicator(e)
492
500
  }
493
501
 
494
502
  onDragLeave = e => {
@@ -500,25 +508,21 @@ export default class ItemListTree extends Component {
500
508
  if (tar) {
501
509
  target = tar
502
510
  }
503
- target.classList.remove('item-dragover-top')
511
+ clearDropIndicator(target)
504
512
  }
505
513
 
506
514
  onDragOver = e => {
507
- let {
508
- target
509
- } = e
510
- const tar = findParentBySel(target, '.tree-item')
511
- if (tar) {
512
- target = tar
513
- }
514
- target.classList.add('item-dragover-top')
515
+ e.preventDefault()
516
+ this.updateDropIndicator(e)
515
517
  }
516
518
 
517
519
  onDrop = action(e => {
518
520
  e.preventDefault()
519
- const elems = document.querySelectorAll('.tree-item.item-dragover-top')
521
+ const elems = document.querySelectorAll(
522
+ '.tree-item.dnd-before, .tree-item.dnd-after'
523
+ )
520
524
  elems.forEach(elem => {
521
- elem.classList.remove('item-dragover-top')
525
+ clearDropIndicator(elem)
522
526
  })
523
527
  let {
524
528
  target
@@ -537,6 +541,9 @@ export default class ItemListTree extends Component {
537
541
  const pidDrops = target.getAttribute('data-parent-id') || ''
538
542
  const pidDropsArr = pidDrops.split('#')
539
543
  const pidDrop = pidDropsArr[pidDropsArr.length - 1]
544
+ // bottom half of the target row => insert after it, so the last
545
+ // item/group in a list can receive a drop (append to the end).
546
+ const insertAfter = isDropAfterHalf(e, target)
540
547
  // can not drag item to its own children
541
548
  if (
542
549
  (idDragged === 'default' &&
@@ -566,8 +573,9 @@ export default class ItemListTree extends Component {
566
573
  if (indexDrop < 0) {
567
574
  return
568
575
  }
576
+ const insertIndex = insertAfter ? indexDrop + 1 : indexDrop
569
577
  bookmarkGroups.splice(
570
- indexDrop,
578
+ insertIndex,
571
579
  0,
572
580
  dragItem
573
581
  )
@@ -580,9 +588,13 @@ export default class ItemListTree extends Component {
580
588
  )
581
589
  : false
582
590
  if (parentDrag) {
583
- parentDrag.bookmarkGroupIds = (parentDrag.bookmarkGroupIds || []).filter(
584
- id => id !== idDragged
585
- )
591
+ // remove from old parent IN PLACE: manate only reacts to in-place
592
+ // array mutations (the delete inside splice), not reassignment.
593
+ const fromArr = parentDrag.bookmarkGroupIds || []
594
+ const fromIdx = fromArr.indexOf(idDragged)
595
+ if (fromIdx >= 0) {
596
+ fromArr.splice(fromIdx, 1)
597
+ }
586
598
  }
587
599
  const parentDrop = pidDrop
588
600
  ? bookmarkGroups.find(
@@ -594,20 +606,18 @@ export default class ItemListTree extends Component {
594
606
  if (!parentDrop) {
595
607
  return
596
608
  }
609
+ const dropArr = parentDrop.bookmarkGroupIds || []
597
610
  if (!pidDrop) {
598
- parentDrop.bookmarkGroupIds = uniq(
599
- [
600
- ...(parentDrop.bookmarkGroupIds || []),
601
- idDragged
602
- ]
603
- )
611
+ if (!dropArr.includes(idDragged)) {
612
+ dropArr.push(idDragged)
613
+ }
604
614
  } else {
605
- const arr = parentDrop.bookmarkGroupIds || []
606
- let index = arr.findIndex(item => item === idDrop)
615
+ let index = dropArr.findIndex(item => item === idDrop)
607
616
  if (index < 0) {
608
617
  index = 0
609
618
  }
610
- arr.splice(index, 0, idDragged)
619
+ const insertIndex = insertAfter ? index + 1 : index
620
+ dropArr.splice(insertIndex, 0, idDragged)
611
621
  }
612
622
  } else {
613
623
  const parentDrag = bookmarkGroups.find(
@@ -616,9 +626,13 @@ export default class ItemListTree extends Component {
616
626
  if (!parentDrag) {
617
627
  return
618
628
  }
619
- parentDrag.bookmarkIds = (parentDrag.bookmarkIds || []).filter(
620
- id => id !== idDragged
621
- )
629
+ // remove from old parent IN PLACE: manate only reacts to in-place
630
+ // array mutations (the delete inside splice), not reassignment.
631
+ const fromArr = parentDrag.bookmarkIds || []
632
+ const fromIdx = fromArr.indexOf(idDragged)
633
+ if (fromIdx >= 0) {
634
+ fromArr.splice(fromIdx, 1)
635
+ }
622
636
  const parentDrop = isGroupDrop
623
637
  ? bookmarkGroups.find(
624
638
  item => item.id === idDrop
@@ -629,21 +643,25 @@ export default class ItemListTree extends Component {
629
643
  if (!parentDrop) {
630
644
  return
631
645
  }
646
+ const dropArr = parentDrop.bookmarkIds || []
632
647
  if (isGroupDrop) {
633
- parentDrop.bookmarkIds = uniq(
634
- [
635
- ...(parentDrop.bookmarkIds || []),
636
- idDragged
637
- ]
638
- )
648
+ if (!dropArr.includes(idDragged)) {
649
+ dropArr.push(idDragged)
650
+ }
639
651
  } else {
640
- const arr = parentDrop.bookmarkIds || []
641
- let index = arr.findIndex(item => item === idDrop)
652
+ let index = dropArr.findIndex(item => item === idDrop)
642
653
  if (index < 0) {
643
654
  index = 0
644
655
  }
645
- arr.splice(index, 0, idDragged)
656
+ const insertIndex = insertAfter ? index + 1 : index
657
+ dropArr.splice(insertIndex, 0, idDragged)
646
658
  }
659
+ // a manual bookmark reorder overrides any active sort, otherwise
660
+ // buildVisibleTreeRows would re-sort bookmarks and hide this order.
661
+ setItemJSON(treeSortLsKey, null)
662
+ this.setState({
663
+ sort: null
664
+ })
647
665
  }
648
666
  if (
649
667
  isGroupDrag &&
@@ -42,6 +42,20 @@ a
42
42
  .cap
43
43
  text-transform capitalize
44
44
 
45
+ // shared drag-and-drop reorder indicators (vertical lists)
46
+ // first half of the target => insert before => top edge
47
+ // second half of the target => insert after => bottom edge
48
+ // (tabs use their own horizontal left/right line indicators)
49
+ .dnd-before
50
+ box-shadow inset 0 2px 0 0 var(--primary)
51
+ .dnd-after
52
+ box-shadow inset 0 -2px 0 0 var(--primary)
53
+ // antd table rows: box-shadow on <tr> is not rendered, apply on cells
54
+ tr.dnd-before > td
55
+ box-shadow inset 0 2px 0 0 var(--primary)
56
+ tr.dnd-after > td
57
+ box-shadow inset 0 -2px 0 0 var(--primary)
58
+
45
59
  .sync-data-compare
46
60
  .sync-suggest-action
47
61
  color var(--success)
@@ -29,6 +29,24 @@
29
29
  // close icon always visible on mobile
30
30
  .tab-close
31
31
  display block
32
+ // keep a left drag grip for the custom title bar (not webapp, not system-ui),
33
+ // so the empty area left of the tab is draggable. mac needs extra room for
34
+ // the traffic-light window controls in the top-left.
35
+ &.not-system-ui.not-webapp
36
+ .mobile-tabs-inner
37
+ margin-left 20px !important
38
+ &.is-mac
39
+ .mobile-tabs-inner
40
+ margin-left 72px !important
41
+ // AppDrag wraps .mobile-tabs-inner under a custom title bar; make it the flex
42
+ // item so the tab layout is unchanged and the inner's left drag grip stays
43
+ // inside the draggable .app-drag box.
44
+ .tabs.mobile-tabs > .app-drag
45
+ flex 1
46
+ min-width 0
47
+ display flex
48
+ align-items center
49
+ overflow hidden
32
50
  .mobile-tabs-extra
33
51
  flex-shrink 0
34
52
  display flex
@@ -82,18 +82,22 @@ export default Store => {
82
82
  window.store[type] = arr
83
83
  }
84
84
 
85
- Store.prototype.adjustOrder = function (type, fromId, toId) {
85
+ Store.prototype.adjustOrder = function (type, fromId, toId, insertAfter = false) {
86
86
  const { store } = window
87
87
  const items = store.getItems(type)
88
88
  const fromIndex = items.findIndex(t => t.id === fromId)
89
- let toIndex = items.findIndex(t => t.id === toId)
89
+ const toIndex = items.findIndex(t => t.id === toId)
90
90
  if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex) {
91
91
  return
92
92
  }
93
- if (fromIndex < toIndex) {
94
- toIndex = toIndex - 1
95
- }
93
+ // insertAfter lets the item land after the target, so the last
94
+ // item can receive a drop (insertIndex can reach items.length).
95
+ let insertIndex = insertAfter ? toIndex + 1 : toIndex
96
96
  const [removed] = items.splice(fromIndex, 1)
97
- items.splice(toIndex, 0, removed)
97
+ // account for the index shift caused by the removal above
98
+ if (fromIndex < insertIndex) {
99
+ insertIndex = insertIndex - 1
100
+ }
101
+ items.splice(insertIndex, 0, removed)
98
102
  }
99
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "3.15.178",
3
+ "version": "3.15.187",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",