@electerm/electerm-react 2.5.9 → 2.6.0

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 (27) hide show
  1. package/client/common/constants.js +1 -1
  2. package/client/components/bookmark-form/bookmark-form.styl +2 -0
  3. package/client/components/bookmark-form/common/encodes.js +1 -0
  4. package/client/components/bookmark-form/common/run-scripts.jsx +29 -27
  5. package/client/components/bookmark-form/config/common-fields.js +29 -2
  6. package/client/components/bookmark-form/{tree-delete.jsx → tree-select.jsx} +27 -13
  7. package/client/components/common/input-confirm-common.jsx +13 -8
  8. package/client/components/common/modal.styl +1 -0
  9. package/client/components/common/multi-select-modal.jsx +35 -0
  10. package/client/components/common/notification.styl +1 -1
  11. package/client/components/quick-commands/quick-commands-box.jsx +9 -9
  12. package/client/components/quick-commands/quick-commands-list-form.jsx +34 -35
  13. package/client/components/setting-panel/bookmark-tree-list.jsx +2 -2
  14. package/client/components/setting-panel/keywords-form.jsx +14 -20
  15. package/client/components/setting-panel/setting-common.jsx +18 -39
  16. package/client/components/setting-panel/setting-terminal.jsx +1 -1
  17. package/client/components/setting-panel/setting-wrap.styl +8 -2
  18. package/client/components/sidebar/sidebar-panel.jsx +15 -2
  19. package/client/components/sidebar/sidebar.styl +9 -3
  20. package/client/components/tabs/add-btn-menu.jsx +13 -9
  21. package/client/components/tabs/add-btn.styl +11 -1
  22. package/client/components/tabs/tab.jsx +7 -1
  23. package/client/components/tabs/tabs.styl +4 -1
  24. package/client/components/tree-list/tree-list.jsx +10 -8
  25. package/client/components/tree-list/tree-list.styl +14 -0
  26. package/client/store/bookmark.js +7 -0
  27. package/package.json +1 -1
@@ -233,7 +233,7 @@ export const downloadUpgradeTimeout = 20000
233
233
  export const expandedKeysLsKey = 'expanded-keys'
234
234
  export const resolutionsLsKey = 'custom-resolution-key'
235
235
  export const checkedKeysLsKey = 'checked-keys'
236
- export const quickCommandLabelsLsKey = 'quick-command-labels'
236
+ export const quickCommandLabelsLsKey = 'quick-command-label'
237
237
  export const localAddrBookmarkLsKey = 'local-addr-bookmark-keys'
238
238
  export const dismissDelKeyTipLsKey = 'dismiss-del-key-tip'
239
239
  export const sshTunnelHelpLink = 'https://github.com/electerm/electerm/wiki/How-to-use-ssh-tunnel'
@@ -13,3 +13,5 @@
13
13
  .ip-item:hover
14
14
  .item-item-use
15
15
  display inline-block
16
+ .number-input
17
+ min-width 210px
@@ -1,6 +1,7 @@
1
1
  export default [
2
2
  'utf-8',
3
3
  'gbk',
4
+ 'gb2312',
4
5
  'gb18030',
5
6
  'hz-gb-2312',
6
7
  'big5',
@@ -17,42 +17,44 @@ export default function renderRunScripts () {
17
17
  return (
18
18
  <>
19
19
  <Space
20
- align='center'
20
+ align='baseline'
21
+ block
21
22
  key={field.key}
22
23
  >
23
24
  <FormItem
24
25
  label=''
25
26
  name={[field.name, 'delay']}
26
27
  required
28
+ noStyle
27
29
  >
28
- <Space.Compact>
29
- <Space.Addon>{e('loginScriptDelay')}</Space.Addon>
30
- <InputNumber
31
- min={1}
32
- step={1}
33
- max={65535}
34
- rules={[{ required: true, message: e('loginScriptDelay') + ' required' }]}
35
- className='compact-input'
36
- />
37
- </Space.Compact>
38
- </FormItem>
39
- <FormItem
40
- label=''
41
- name={[field.name, 'script']}
42
- required
43
- className='mg2x'
44
- >
45
- <Input.TextArea
46
- autoSize={{ minRows: 1 }}
47
- placeholder={e('loginScript')}
48
- className='compact-input'
30
+ <InputNumber
31
+ min={1}
32
+ step={1}
33
+ prefix={e('loginScriptDelay')}
34
+ suffix='ms'
35
+ max={65535}
36
+ className='number-input'
37
+ rules={[{ required: true, message: e('loginScriptDelay') + ' required' }]}
49
38
  />
50
39
  </FormItem>
51
- <Button
52
- icon={<MinusCircleOutlined />}
53
- onClick={() => remove(field.name)}
54
- className='mg24b'
55
- />
40
+ <Space.Compact>
41
+ <FormItem
42
+ label=''
43
+ name={[field.name, 'script']}
44
+ required
45
+ noStyle
46
+ >
47
+ <Input.TextArea
48
+ autoSize={{ minRows: 1 }}
49
+ placeholder={e('loginScript')}
50
+ />
51
+ </FormItem>
52
+ <Button
53
+ icon={<MinusCircleOutlined />}
54
+ onClick={() => remove(field.name)}
55
+ className='mg24b'
56
+ />
57
+ </Space.Compact>
56
58
  </Space>
57
59
  </>
58
60
  )
@@ -9,6 +9,32 @@ import { isEmpty } from 'lodash-es'
9
9
 
10
10
  const e = window.translate
11
11
 
12
+ const commonLangOptions = [
13
+ 'zh_CN.GBK',
14
+ 'C',
15
+ 'POSIX',
16
+ 'C.UTF-8',
17
+ 'en_US.UTF-8',
18
+ 'en_US',
19
+ 'en_GB.UTF-8',
20
+ 'fr_FR.UTF-8',
21
+ 'de_DE.UTF-8',
22
+ 'es_ES.UTF-8',
23
+ 'it_IT.UTF-8',
24
+ 'pt_BR.UTF-8',
25
+ 'pt_PT.UTF-8',
26
+ 'zh_CN.UTF-8',
27
+ 'zh_CN.GB2312',
28
+ 'zh_TW.UTF-8',
29
+ 'zh_HK.UTF-8',
30
+ 'zh_SG.UTF-8',
31
+ 'ja_JP.UTF-8',
32
+ 'ja_JP.eucJP',
33
+ 'ko_KR.UTF-8',
34
+ 'ru_RU.UTF-8',
35
+ 'ar_SA.UTF-8'
36
+ ].map(l => ({ label: l, value: l }))
37
+
12
38
  // Common individual fields
13
39
  export const commonFields = {
14
40
  // Basic connection fields
@@ -118,10 +144,11 @@ export const commonFields = {
118
144
 
119
145
  // Terminal UI settings
120
146
  envLang: {
121
- type: 'input',
147
+ type: 'autocomplete',
122
148
  name: 'envLang',
123
149
  label: 'ENV:LANG',
124
150
  rules: [{ max: 130, message: '130 chars max' }],
151
+ options: commonLangOptions,
125
152
  props: { placeholder: 'en_US.UTF-8' }
126
153
  },
127
154
 
@@ -217,7 +244,6 @@ export const commonFields = {
217
244
 
218
245
  // Common field groups for settings tabs
219
246
  export const terminalSettings = [
220
- commonFields.envLang,
221
247
  commonFields.terminalType,
222
248
  commonFields.proxy,
223
249
  commonFields.displayRaw,
@@ -281,6 +307,7 @@ export const sshAuthFields = [
281
307
  commonFields.startDirectoryLocal,
282
308
  commonFields.startDirectory,
283
309
  commonFields.interactiveValues,
310
+ commonFields.envLang,
284
311
  commonFields.encode,
285
312
  commonFields.type
286
313
  ]
@@ -71,28 +71,42 @@ function buildData (bookmarks, bookmarkGroups) {
71
71
  return level1
72
72
  }
73
73
 
74
- export default function BookmarkTreeDelete (props) {
75
- const { expandedKeys, checkedKeys, bookmarks, bookmarkGroups } = props
74
+ export default function BookmarkTreeSelect (props) {
75
+ const { expandedKeys: propExpandedKeys, checkedKeys: propCheckedKeys, bookmarks, bookmarkGroups, type = 'delete', onCheck: propOnCheck, onExpand: propOnExpand } = props
76
76
 
77
- const onExpand = (expandedKeys) => {
77
+ const expandedKeys = propExpandedKeys !== undefined ? propExpandedKeys : window.store.expandedKeys
78
+ const checkedKeys = propCheckedKeys !== undefined ? propCheckedKeys : window.store.checkedKeys
79
+
80
+ const onExpand = propOnExpand || ((expandedKeys) => {
78
81
  window.store.expandedKeys = deepCopy(expandedKeys)
79
- }
82
+ })
80
83
 
81
- const onCheck = (checkedKeys) => {
84
+ const onCheck = propOnCheck || ((checkedKeys) => {
82
85
  window.store.checkedKeys = deepCopy(checkedKeys)
83
- }
86
+ })
84
87
 
85
- const handleDel = () => {
88
+ const handleOperation = () => {
86
89
  const { store } = window
87
90
  const arr = checkedKeys.filter(d => d !== defaultBookmarkGroupId)
88
- store.delItems(arr, settingMap.bookmarks)
89
- store.delItems(arr, settingMap.bookmarkGroups)
91
+ if (type === 'delete') {
92
+ store.delItems(arr, settingMap.bookmarks)
93
+ store.delItems(arr, settingMap.bookmarkGroups)
94
+ } else {
95
+ store.openBookmarks(arr)
96
+ if (props.onClose) {
97
+ props.onClose()
98
+ }
99
+ }
90
100
  store.checkedKeys = []
91
101
  }
92
102
 
93
103
  const handleCancel = () => {
94
104
  const { store } = window
95
- store.bookmarkSelectMode = false
105
+ if (props.onClose) {
106
+ props.onClose()
107
+ } else {
108
+ store.bookmarkSelectMode = false
109
+ }
96
110
  store.checkedKeys = []
97
111
  }
98
112
 
@@ -109,13 +123,13 @@ export default function BookmarkTreeDelete (props) {
109
123
  return (
110
124
  <div>
111
125
  <div className='pd2'>
112
- <Space.Compact>
126
+ <Space.Compact className='mg2b'>
113
127
  <Button
114
128
  type='primary'
115
129
  disabled={!len}
116
- onClick={handleDel}
130
+ onClick={handleOperation}
117
131
  >
118
- {e('delSelected')} ({len})
132
+ {type === 'delete' ? e('delSelected') : e('open')} ({len})
119
133
  </Button>
120
134
  <Button
121
135
  onClick={handleCancel}
@@ -34,6 +34,12 @@ export default function InputConfirmCommon ({
34
34
  setIsEditing(false)
35
35
  }
36
36
 
37
+ function handleBlur () {
38
+ if (isEditing) {
39
+ handleConfirm()
40
+ }
41
+ }
42
+
37
43
  const icons = isEditing
38
44
  ? (
39
45
  <>
@@ -63,18 +69,17 @@ export default function InputConfirmCommon ({
63
69
  const childProps = {
64
70
  ...restProps,
65
71
  value: localValue,
66
- onChange: handleChange
72
+ onChange: handleChange,
73
+ onBlur: handleBlur
67
74
  }
68
75
 
69
76
  const inputElement = <InputComponent {...childProps} />
70
77
 
71
78
  return (
72
- <div>
73
- <Space.Compact className={cls}>
74
- {beforeAddon}
75
- {inputElement}
76
- {afterAddon}
77
- </Space.Compact>
78
- </div>
79
+ <Space.Compact className={cls}>
80
+ {beforeAddon}
81
+ {inputElement}
82
+ {afterAddon}
83
+ </Space.Compact>
79
84
  )
80
85
  }
@@ -22,6 +22,7 @@
22
22
  justify-content center
23
23
  min-height 100%
24
24
  padding 24px
25
+ word-break break-word
25
26
 
26
27
  .custom-modal-content
27
28
  position relative
@@ -0,0 +1,35 @@
1
+ import { useState } from 'react'
2
+ import Modal from './modal'
3
+ import BookmarkTreeSelect from '../bookmark-form/tree-select'
4
+
5
+ const e = window.translate
6
+
7
+ const MultiSelectModal = ({ onClose, open }) => {
8
+ const [checkedKeys, setCheckedKeys] = useState([])
9
+ const [expandedKeys, setExpandedKeys] = useState([])
10
+ if (!open) {
11
+ return null
12
+ }
13
+ const { store } = window
14
+ return (
15
+ <Modal
16
+ title={e('open') + ' ' + e('bookmarks')}
17
+ open={open}
18
+ onCancel={onClose}
19
+ footer={null}
20
+ >
21
+ <BookmarkTreeSelect
22
+ type='open'
23
+ onClose={onClose}
24
+ bookmarks={store.bookmarks}
25
+ bookmarkGroups={store.bookmarkGroups}
26
+ expandedKeys={expandedKeys}
27
+ checkedKeys={checkedKeys}
28
+ onExpand={setExpandedKeys}
29
+ onCheck={setCheckedKeys}
30
+ />
31
+ </Modal>
32
+ )
33
+ }
34
+
35
+ export default MultiSelectModal
@@ -19,7 +19,7 @@
19
19
 
20
20
  .notification-message
21
21
  font-weight bold
22
- margin-bottom 10px
22
+
23
23
  .notification-title
24
24
  overflow hidden
25
25
  text-overflow ellipsis
@@ -23,7 +23,7 @@ const { Option } = Select
23
23
 
24
24
  export default function QuickCommandsFooterBox (props) {
25
25
  const [keyword, setKeyword] = useState('')
26
- const [labels, setLabels] = useState(ls.getItemJSON(quickCommandLabelsLsKey, []))
26
+ const [label, setLabel] = useState(ls.getItem(quickCommandLabelsLsKey, ''))
27
27
  const timer = useRef(null)
28
28
 
29
29
  function handleMouseLeave () {
@@ -68,8 +68,8 @@ export default function QuickCommandsFooterBox (props) {
68
68
  }
69
69
 
70
70
  function handleChangeLabels (v) {
71
- ls.setItemJSON(quickCommandLabelsLsKey, v)
72
- setLabels(v)
71
+ ls.setItem(quickCommandLabelsLsKey, v || '')
72
+ setLabel(v)
73
73
  }
74
74
 
75
75
  // function filterFunc (v, opt) {
@@ -132,10 +132,10 @@ export default function QuickCommandsFooterBox (props) {
132
132
  )
133
133
  }
134
134
 
135
- function filterArray (array, keyword, labels) {
135
+ function filterArray (array, keyword, label) {
136
136
  return array.filter(obj => {
137
137
  const nameMatches = !keyword || obj.name.toLowerCase().includes(keyword)
138
- const labelMatches = !labels.length || labels.some((label) => (obj.labels || []).includes(label))
138
+ const labelMatches = !label || (obj.labels || []).includes(label)
139
139
  return nameMatches && labelMatches
140
140
  })
141
141
  }
@@ -156,16 +156,16 @@ export default function QuickCommandsFooterBox (props) {
156
156
  return renderNoCmd()
157
157
  }
158
158
  const keyword0 = keyword.toLowerCase()
159
- const filtered = filterArray(all, keyword0, labels)
159
+ const filtered = filterArray(all, keyword0, label)
160
160
  const sorted = qmSortByFrequency
161
161
  ? sortBy(filtered, (obj) => -(obj.clickCount || 0))
162
162
  : filtered
163
163
  const sprops = {
164
- value: labels,
165
- mode: 'multiple',
164
+ value: label,
166
165
  onChange: handleChangeLabels,
167
166
  placeholder: e('labels'),
168
- className: 'qm-label-select'
167
+ className: 'qm-label-select',
168
+ allowClear: true
169
169
  }
170
170
  const tp = pinnedQuickCommandBar
171
171
  ? 'primary'
@@ -3,11 +3,9 @@ import {
3
3
  InputNumber,
4
4
  Space,
5
5
  Button,
6
- Input,
7
- Tag
6
+ Input
8
7
  } from 'antd'
9
8
  import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'
10
- import { formItemLayout } from '../../common/form-layout'
11
9
  import HelpIcon from '../common/help-icon'
12
10
  import { copy } from '../../common/clipboard'
13
11
  import { useRef } from 'react'
@@ -20,48 +18,49 @@ export default function renderQm () {
20
18
  const focused = useRef(0)
21
19
  function renderItem (field, i, add, remove) {
22
20
  return (
23
- <Space
21
+ <Space.Compact
24
22
  align='center'
23
+ block
25
24
  key={field.key}
25
+ className='mg2b'
26
26
  >
27
+ <Space.Addon>{e('delay')}</Space.Addon>
27
28
  <FormItem
28
29
  label=''
29
30
  name={[field.name, 'delay']}
30
31
  required
32
+ noStyle
31
33
  >
32
- <Space.Compact>
33
- <Space.Addon>{e('delay')}</Space.Addon>
34
- <InputNumber
35
- min={1}
36
- step={1}
37
- max={65535}
38
- placeholder={100}
39
- className='compact-input'
40
- />
41
- </Space.Compact>
34
+ <InputNumber
35
+ min={1}
36
+ step={1}
37
+ max={65535}
38
+ placeholder={100}
39
+ className='compact-input'
40
+ suffix='ms'
41
+ />
42
42
  </FormItem>
43
43
  <FormItem
44
44
  label=''
45
45
  name={[field.name, 'command']}
46
46
  required
47
47
  className='mg2x'
48
+ noStyle
48
49
  >
49
- <Space.Compact>
50
- <Input.TextArea
51
- autoSize={{ minRows: 1 }}
52
- placeholder={e('quickCommand')}
53
- className='compact-input qm-input'
54
- onFocus={() => {
55
- focused.current = i
56
- }}
57
- />
58
- <Button
59
- icon={<MinusCircleOutlined />}
60
- onClick={() => remove(field.name)}
61
- />
62
- </Space.Compact>
50
+ <Input.TextArea
51
+ autoSize={{ minRows: 1 }}
52
+ placeholder={e('quickCommand')}
53
+ className='compact-input qm-input'
54
+ onFocus={() => {
55
+ focused.current = i
56
+ }}
57
+ />
63
58
  </FormItem>
64
- </Space>
59
+ <Button
60
+ icon={<MinusCircleOutlined />}
61
+ onClick={() => remove(field.name)}
62
+ />
63
+ </Space.Compact>
65
64
  )
66
65
  }
67
66
  const commonCmds = [
@@ -89,28 +88,29 @@ export default function renderQm () {
89
88
 
90
89
  const cmds = commonCmds.map(c => {
91
90
  return (
92
- <Tag
91
+ <Button
93
92
  title={c.desc}
94
- variant='solid'
93
+ type='text'
95
94
  key={c.cmd}
95
+ size='small'
96
96
  onClick={() => {
97
97
  copy(c.cmd)
98
98
  }}
99
99
  >
100
100
  <b className='pointer'>{c.cmd}</b>
101
- </Tag>
101
+ </Button>
102
102
  )
103
103
  })
104
104
  const label = (
105
105
  <div>
106
- {e('commonCommands')}
106
+ {e('quickCommands')}
107
107
  <HelpIcon
108
108
  title={cmds}
109
109
  />
110
110
  </div>
111
111
  )
112
112
  return (
113
- <FormItem {...formItemLayout} label={label}>
113
+ <FormItem label={label}>
114
114
  <FormList
115
115
  name='commands'
116
116
  >
@@ -127,7 +127,6 @@ export default function renderQm () {
127
127
  <Button
128
128
  type='dashed'
129
129
  onClick={() => add()}
130
- block
131
130
  icon={<PlusOutlined />}
132
131
  >
133
132
  {e('quickCommand')}
@@ -1,10 +1,10 @@
1
1
  import TreeList from '../tree-list/tree-list'
2
- import BookmarkTreeDelete from '../bookmark-form/tree-delete'
2
+ import BookmarkTreeSelect from '../bookmark-form/tree-select'
3
3
 
4
4
  export default function BookmarkTreeList (props) {
5
5
  return props.bookmarkSelectMode
6
6
  ? (
7
- <BookmarkTreeDelete
7
+ <BookmarkTreeSelect
8
8
  {...props}
9
9
  />
10
10
  )
@@ -41,32 +41,26 @@ export default function KeywordForm (props) {
41
41
 
42
42
  function renderItem (field, i, add, remove) {
43
43
  return (
44
- <Space
44
+ <Space.Compact
45
45
  align='center'
46
46
  key={field.key}
47
- className='mg3r'
47
+ className='mg3r mg2b'
48
48
  >
49
49
  <FormItem
50
- hasFeedback
50
+ noStyle
51
+ required
52
+ name={[field.name, 'keyword']}
53
+ rules={[{ validator: checker }]}
51
54
  >
52
- <FormItem
53
- noStyle
54
- required
55
- name={[field.name, 'keyword']}
56
- rules={[{ validator: checker }]}
57
- >
58
- <Space.Compact>
59
- <InputConfirm
60
- addonBefore={renderBefore(field.name)}
61
- />
62
- <Button
63
- icon={<MinusCircleOutlined />}
64
- onClick={() => remove(field.name)}
65
- />
66
- </Space.Compact>
67
- </FormItem>
55
+ <InputConfirm
56
+ addonBefore={renderBefore(field.name)}
57
+ />
68
58
  </FormItem>
69
- </Space>
59
+ <Button
60
+ icon={<MinusCircleOutlined />}
61
+ onClick={() => remove(field.name)}
62
+ />
63
+ </Space.Compact>
70
64
  )
71
65
  }
72
66
 
@@ -6,10 +6,10 @@ import {
6
6
  MoonOutlined
7
7
  } from '@ant-design/icons'
8
8
  import message from '../common/message'
9
+ import { notification } from '../common/notification'
9
10
  import {
10
11
  Select,
11
12
  Switch,
12
- Alert,
13
13
  Button,
14
14
  Table,
15
15
  Space,
@@ -42,8 +42,6 @@ const e = window.translate
42
42
  export default class SettingCommon extends Component {
43
43
  state = {
44
44
  ready: false,
45
- languageChanged: false,
46
- passwordChanged: false,
47
45
  submittingPass: false,
48
46
  passInputFocused: false,
49
47
  placeholderLogin: window.pre.requireAuth ? '********' : e('notSet'),
@@ -82,7 +80,6 @@ export default class SettingCommon extends Component {
82
80
  this.setState({
83
81
  loginPass: pass ? '********' : '',
84
82
  submittingPass: false,
85
- passwordChanged: true,
86
83
  placeholderLogin: pass ? '********' : e('notSet')
87
84
  }, () => {
88
85
  this.submitting = false
@@ -121,10 +118,6 @@ export default class SettingCommon extends Component {
121
118
  })
122
119
  }
123
120
 
124
- handleRestart = () => {
125
- window.location.reload()
126
- }
127
-
128
121
  handleResetAll = () => {
129
122
  this.saveConfig(
130
123
  deepCopy(defaultSettings)
@@ -137,13 +130,24 @@ export default class SettingCommon extends Component {
137
130
  })
138
131
  }
139
132
 
140
- handleChangeLang = language => {
141
- this.setState({
142
- languageChanged: true
143
- })
144
- return this.saveConfig({
133
+ handleChangeLang = async language => {
134
+ await this.saveConfig({
145
135
  language
146
136
  })
137
+ notification.info({
138
+ message: (
139
+ <div>
140
+ {e('saveLang')}
141
+ <Button
142
+ onClick={() => window.location.reload()}
143
+ className='mg1l'
144
+ size='small'
145
+ >
146
+ {e('restartNow')}
147
+ </Button>
148
+ </div>
149
+ )
150
+ })
147
151
  }
148
152
 
149
153
  handleChangeTerminalTheme = id => {
@@ -199,30 +203,6 @@ export default class SettingCommon extends Component {
199
203
  )
200
204
  }
201
205
 
202
- renderRestart = (name) => {
203
- if (!this.state[name]) {
204
- return null
205
- }
206
- return (
207
- <div className='pd1t'>
208
- <Alert
209
- message={
210
- <div>
211
- {e('saveLang')}
212
- <Button
213
- onClick={this.handleRestart}
214
- className='mg1l'
215
- >
216
- {e('restartNow')}
217
- </Button>
218
- </div>
219
- }
220
- type='success'
221
- />
222
- </div>
223
- )
224
- }
225
-
226
206
  renderNumber = (name, options, title = '') => {
227
207
  let value = this.props.config[name]
228
208
  if (options.valueParser) {
@@ -296,7 +276,7 @@ export default class SettingCommon extends Component {
296
276
  }
297
277
  return (
298
278
  <div className='pd2b'>
299
- <Space.Compact block>
279
+ <Space.Compact className='width-100'>
300
280
  <InputConfirm
301
281
  value={value}
302
282
  onChange={onChange}
@@ -592,7 +572,6 @@ export default class SettingCommon extends Component {
592
572
  </Select>
593
573
  <Link className='mg1l' to={createEditLangLink(language)}>{e('edit')}</Link>
594
574
  </div>
595
- {this.renderRestart('languageChanged')}
596
575
  <div className='pd1b'>{e('default')} {e('execWindows')}</div>
597
576
  {
598
577
  this.renderTextExec('execWindows')
@@ -540,7 +540,7 @@ export default class SettingTerminal extends Component {
540
540
  <Flex flex='auto'>{this.renderFontFamily()}</Flex>
541
541
  </Flex>
542
542
  </div>
543
- <div className='pd2b'>
543
+ <div>
544
544
  <div className='pd1b'>
545
545
  <span className='inline-title mg1r'>{e('keywordsHighlight')}</span>
546
546
  <HelpIcon
@@ -24,8 +24,14 @@
24
24
  width 340px
25
25
  bottom 0
26
26
  padding 20px 0 20px 20px
27
- .ant-tree
28
- background none
27
+ display flex
28
+ flex-direction column
29
+ .model-bookmark-tree-wrap
30
+ flex 1
31
+ display flex
32
+ flex-direction column
33
+ overflow hidden
34
+ min-height 0
29
35
  .setting-row-right
30
36
  left 340px
31
37
  right 0
@@ -2,17 +2,19 @@
2
2
  * bookmark select
3
3
  */
4
4
 
5
- import { memo } from 'react'
5
+ import { memo, useState } from 'react'
6
6
  import BookmarkWrap from './bookmark'
7
7
  import History from './history'
8
8
  import { Tabs, Tooltip } from 'antd'
9
+ import MultiSelectModal from '../common/multi-select-modal'
9
10
  import {
10
11
  ArrowsAltOutlined,
11
12
  EditOutlined,
12
13
  PlusCircleOutlined,
13
14
  ShrinkOutlined,
14
15
  PushpinOutlined,
15
- UnorderedListOutlined
16
+ UnorderedListOutlined,
17
+ SelectOutlined
16
18
  } from '@ant-design/icons'
17
19
 
18
20
  const e = window.translate
@@ -20,6 +22,7 @@ const e = window.translate
20
22
  export default memo(function SidebarPanel (props) {
21
23
  const { sidebarPanelTab, pinned } = props
22
24
  const { store } = window
25
+ const [openSelectModal, setOpenSelectModal] = useState(false)
23
26
  const prps = {
24
27
  className: 'font16 mg1x mg2l pointer iblock control-icon'
25
28
  }
@@ -81,6 +84,12 @@ export default memo(function SidebarPanel (props) {
81
84
  <ShrinkOutlined
82
85
  {...pop3}
83
86
  />
87
+ </Tooltip>,
88
+ <Tooltip title={e('open') + ' ' + e('bookmarks')} key='multi'>
89
+ <SelectOutlined
90
+ {...prps}
91
+ onClick={() => setOpenSelectModal(true)}
92
+ />
84
93
  </Tooltip>
85
94
  ]
86
95
  }
@@ -119,6 +128,10 @@ export default memo(function SidebarPanel (props) {
119
128
  ? <BookmarkWrap {...props} />
120
129
  : <History store={store} />
121
130
  }
131
+ <MultiSelectModal
132
+ open={openSelectModal}
133
+ onClose={() => setOpenSelectModal(false)}
134
+ />
122
135
  </div>
123
136
  )
124
137
  })
@@ -39,9 +39,15 @@
39
39
  right 0
40
40
  top 112px
41
41
  bottom 0
42
- overflow-y scroll
43
- .item-list-wrap
44
- overflow-y hidden
42
+ overflow hidden
43
+ display flex
44
+ flex-direction column
45
+ .sidebar-inner
46
+ flex 1
47
+ display flex
48
+ flex-direction column
49
+ overflow hidden
50
+ min-height 0
45
51
  .not-system-ui.is-mac
46
52
  .sidebar-bar
47
53
  margin-top 20px
@@ -72,16 +72,20 @@ export default function AddBtnMenu ({
72
72
  <DragHandle
73
73
  {...dragProps}
74
74
  />
75
- <div
76
- className={cls}
77
- onClick={onNewSsh}
78
- >
79
- <CodeFilled /> {e('newBookmark')}
75
+ <div className='add-menu-header'>
76
+ <div
77
+ className={cls}
78
+ onClick={onNewSsh}
79
+ >
80
+ <CodeFilled /> {e('newBookmark')}
81
+ </div>
82
+ {addTabBtn}
83
+ </div>
84
+ <div className='add-menu-list'>
85
+ <BookmarksList
86
+ store={window.store}
87
+ />
80
88
  </div>
81
- {addTabBtn}
82
- <BookmarksList
83
- store={window.store}
84
- />
85
89
  </div>
86
90
  )
87
91
  }
@@ -8,10 +8,20 @@
8
8
  box-shadow 0 4px 12px rgba(0, 0, 0, 0.3), 0 0 0 1px var(--main-darker)
9
9
  min-width 300px
10
10
  max-width 600px
11
- overflow-y auto
11
+ overflow hidden
12
12
  margin-top 4px
13
13
  padding 4px 15px
14
+ display flex
15
+ flex-direction column
14
16
  .drag-handle
15
17
  right 3px
16
18
  display block
19
+ .add-menu-header
20
+ flex-shrink 0
21
+ .add-menu-list
22
+ flex 1
23
+ overflow hidden
24
+ display flex
25
+ flex-direction column
26
+ min-height 0
17
27
 
@@ -399,7 +399,11 @@ class Tab extends Component {
399
399
  }
400
400
 
401
401
  render () {
402
- const { isLast, tab, currentBatchTabId } = this.props
402
+ const {
403
+ isLast,
404
+ tab,
405
+ currentBatchTabId
406
+ } = this.props
403
407
  const {
404
408
  id,
405
409
  isEditting,
@@ -411,10 +415,12 @@ class Tab extends Component {
411
415
  terminalOnData
412
416
  } = this.state
413
417
  const active = id === currentBatchTabId
418
+ const activeAll = id === window.store.activeTabId
414
419
  const cls = classnames(
415
420
  `tab-${id}`,
416
421
  'tab',
417
422
  { active },
423
+ { 'active-all': activeAll },
418
424
  {
419
425
  'tab-last': isLast
420
426
  },
@@ -52,8 +52,11 @@
52
52
  &.active
53
53
  color var(--text)
54
54
  background var(--main)
55
+ &.active-all
56
+ font-weight bold
55
57
  .tab-count
56
- opacity 1
58
+ border-radius 15px
59
+ font-weight bold
57
60
  &:hover
58
61
  .tab-close
59
62
  display block
@@ -849,14 +849,16 @@ export default class ItemListTree extends Component {
849
849
  : []
850
850
  return (
851
851
  <div className={`tree-list item-type-${type}`}>
852
- {
853
- staticList
854
- ? null
855
- : this.renderNewButtons()
856
- }
857
- {
858
- this.renderSearch()
859
- }
852
+ <div className='tree-list-header'>
853
+ {
854
+ staticList
855
+ ? null
856
+ : this.renderNewButtons()
857
+ }
858
+ {
859
+ this.renderSearch()
860
+ }
861
+ </div>
860
862
  <div className='item-list-wrap' style={listStyle}>
861
863
  {this.renderNewCat({ id: '' })}
862
864
  {level1Bookgroups.map(this.renderGroup)}
@@ -85,3 +85,17 @@
85
85
  max-height 300px
86
86
  overflow-y auto
87
87
  padding 8px
88
+
89
+ .tree-list
90
+ display flex
91
+ flex-direction column
92
+ height 100%
93
+ overflow hidden
94
+
95
+ .tree-list-header
96
+ flex-shrink 0
97
+
98
+ .item-list-wrap
99
+ flex 1
100
+ overflow-y auto
101
+ min-height 0
@@ -19,6 +19,13 @@ export default Store => {
19
19
  return window.store.setItems('bookmarks', items)
20
20
  }
21
21
 
22
+ Store.prototype.openBookmarks = function (ids) {
23
+ const { store } = window
24
+ ids.forEach(id => {
25
+ store.onSelectBookmark(id)
26
+ })
27
+ }
28
+
22
29
  Store.prototype.addSshConfigs = function (items) {
23
30
  const { store } = window
24
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "2.5.9",
3
+ "version": "2.6.0",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",