@electerm/electerm-react 2.3.136 → 2.3.166
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.
- package/client/common/constants.js +4 -2
- package/client/common/db.js +2 -1
- package/client/common/init-setting-item.js +7 -0
- package/client/components/batch-op/batch-op.jsx +3 -8
- package/client/components/bookmark-form/common/color-picker.jsx +16 -5
- package/client/components/bookmark-form/common/color-picker.styl +1 -2
- package/client/components/bookmark-form/common/connection-hopping.jsx +1 -0
- package/client/components/bookmark-form/config/common-fields.js +1 -0
- package/client/components/common/drawer.jsx +62 -0
- package/client/components/common/drawer.styl +34 -0
- package/client/components/common/modal.jsx +89 -0
- package/client/components/common/modal.styl +77 -0
- package/client/components/common/notification-with-details.jsx +34 -0
- package/client/components/file-transfer/conflict-resolve.jsx +2 -1
- package/client/components/file-transfer/transfer-speed-format.js +6 -0
- package/client/components/file-transfer/transfer.jsx +5 -2
- package/client/components/file-transfer/transports-action-store.jsx +14 -1
- package/client/components/main/main.jsx +2 -0
- package/client/components/profile/profile-form.jsx +1 -1
- package/client/components/quick-commands/qm.styl +2 -1
- package/client/components/quick-commands/quick-commands-form.jsx +1 -1
- package/client/components/setting-panel/list.jsx +1 -1
- package/client/components/setting-panel/setting-common.jsx +5 -4
- package/client/components/setting-panel/setting-terminal.jsx +1 -1
- package/client/components/setting-panel/setting-wrap.jsx +4 -10
- package/client/components/setting-panel/setting-wrap.styl +8 -6
- package/client/components/setting-panel/start-session-select.jsx +146 -21
- package/client/components/setting-panel/text-bg-modal.jsx +15 -4
- package/client/components/sftp/file-info-modal.jsx +2 -1
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sftp/paged-list.jsx +2 -1
- package/client/components/sftp/sftp-entry.jsx +1 -1
- package/client/components/sftp/sftp.styl +13 -0
- package/client/components/sidebar/info-modal.jsx +53 -34
- package/client/components/sidebar/info.styl +0 -7
- package/client/components/tabs/index.jsx +6 -58
- package/client/components/tabs/layout-menu.jsx +75 -0
- package/client/components/tabs/layout-select.jsx +60 -0
- package/client/components/tabs/tabs.styl +64 -0
- package/client/components/tabs/workspace-save-modal.jsx +117 -0
- package/client/components/tabs/workspace-select.jsx +79 -0
- package/client/components/terminal/attach-addon-custom.js +7 -1
- package/client/components/terminal/terminal-interactive.jsx +2 -1
- package/client/components/terminal/terminal.jsx +0 -1
- package/client/components/text-editor/text-editor.jsx +2 -1
- package/client/components/tree-list/move-item-modal.jsx +115 -30
- package/client/components/tree-list/tree-list.jsx +1 -1
- package/client/components/tree-list/tree-list.styl +6 -1
- package/client/components/vnc/vnc-session.jsx +2 -2
- package/client/components/widgets/widget-control.jsx +4 -5
- package/client/components/widgets/widget-form.jsx +3 -8
- package/client/components/widgets/widget-instance.jsx +44 -9
- package/client/components/widgets/widget-notification-with-details.jsx +34 -0
- package/client/css/basic.styl +3 -1
- package/client/store/init-state.js +4 -0
- package/client/store/load-data.js +15 -6
- package/client/store/store.js +2 -0
- package/client/store/workspace.js +108 -0
- package/package.json +1 -1
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
.close-setting-wrap-icon
|
|
2
|
+
|
|
3
|
+
.close-setting-wrap
|
|
5
4
|
position absolute
|
|
6
5
|
top 70px
|
|
7
6
|
font-size 16px
|
|
8
7
|
cursor pointer
|
|
9
8
|
z-index 889
|
|
9
|
+
right 20px
|
|
10
10
|
&:hover
|
|
11
11
|
color var(--success)
|
|
12
12
|
.alt-close-setting-wrap
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
left 20px
|
|
14
|
+
right auto
|
|
15
|
+
.batch-op-wrap .close-setting-wrap
|
|
16
|
+
top 40px
|
|
17
|
+
right 30px
|
|
16
18
|
.setting-row
|
|
17
19
|
position absolute
|
|
18
20
|
top 127px
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { TreeSelect } from 'antd'
|
|
2
|
-
import {
|
|
1
|
+
import { TreeSelect, Tabs, Select, Empty } from 'antd'
|
|
2
|
+
import { useState } from 'react'
|
|
3
3
|
import copy from 'json-deep-copy'
|
|
4
4
|
import { createTitleWithTag } from '../../common/create-title'
|
|
5
|
+
import {
|
|
6
|
+
AppstoreOutlined,
|
|
7
|
+
BookOutlined
|
|
8
|
+
} from '@ant-design/icons'
|
|
9
|
+
import HelpIcon from '../common/help-icon'
|
|
5
10
|
|
|
6
11
|
const e = window.translate
|
|
7
12
|
const { SHOW_CHILD } = TreeSelect
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
function BookmarkSelect (props) {
|
|
15
|
+
const {
|
|
16
|
+
bookmarks,
|
|
17
|
+
bookmarkGroups,
|
|
18
|
+
onStartSessions,
|
|
19
|
+
onChangeStartSessions
|
|
20
|
+
} = props
|
|
21
|
+
|
|
22
|
+
const buildData = () => {
|
|
23
|
+
const cats = bookmarkGroups
|
|
24
|
+
const tree = bookmarks
|
|
13
25
|
.reduce((p, k) => {
|
|
14
26
|
return {
|
|
15
27
|
...p,
|
|
@@ -64,28 +76,141 @@ export default class StartSessionSelect extends PureComponent {
|
|
|
64
76
|
...(d.bookmarkIds || []).map(buildLeaf)
|
|
65
77
|
].filter(d => d)
|
|
66
78
|
}
|
|
67
|
-
// if (!r.children.length) {
|
|
68
|
-
// return ''
|
|
69
|
-
// }
|
|
70
79
|
return r
|
|
71
80
|
}).filter(d => d)
|
|
72
81
|
return level1
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// onStartSessions is array for bookmarks
|
|
85
|
+
const value = Array.isArray(onStartSessions) ? onStartSessions : []
|
|
86
|
+
|
|
87
|
+
const rProps = {
|
|
88
|
+
treeData: buildData(),
|
|
89
|
+
value: copy(value),
|
|
90
|
+
onChange: onChangeStartSessions,
|
|
91
|
+
treeCheckable: true,
|
|
92
|
+
showCheckedStrategy: SHOW_CHILD,
|
|
93
|
+
placeholder: e('pleaseSelect'),
|
|
94
|
+
style: {
|
|
95
|
+
width: '100%'
|
|
86
96
|
}
|
|
97
|
+
}
|
|
98
|
+
return (
|
|
99
|
+
<TreeSelect {...rProps} />
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function WorkspaceSelect (props) {
|
|
104
|
+
const {
|
|
105
|
+
workspaces,
|
|
106
|
+
onStartSessions,
|
|
107
|
+
onChangeStartSessions
|
|
108
|
+
} = props
|
|
109
|
+
|
|
110
|
+
if (!workspaces.length) {
|
|
87
111
|
return (
|
|
88
|
-
<
|
|
112
|
+
<Empty
|
|
113
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
114
|
+
description={e('noWorkspaces')}
|
|
115
|
+
/>
|
|
89
116
|
)
|
|
90
117
|
}
|
|
118
|
+
|
|
119
|
+
// onStartSessions is string for workspace
|
|
120
|
+
const value = typeof onStartSessions === 'string' ? onStartSessions : undefined
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Select
|
|
124
|
+
value={value}
|
|
125
|
+
onChange={onChangeStartSessions}
|
|
126
|
+
placeholder={e('workspaces')}
|
|
127
|
+
style={{ width: '100%' }}
|
|
128
|
+
allowClear
|
|
129
|
+
>
|
|
130
|
+
{workspaces.map(w => (
|
|
131
|
+
<Select.Option key={w.id} value={w.id}>
|
|
132
|
+
{w.name}
|
|
133
|
+
</Select.Option>
|
|
134
|
+
))}
|
|
135
|
+
</Select>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default function StartSessionSelect (props) {
|
|
140
|
+
const {
|
|
141
|
+
onStartSessions,
|
|
142
|
+
bookmarks,
|
|
143
|
+
bookmarkGroups,
|
|
144
|
+
workspaces,
|
|
145
|
+
onChangeStartSessions
|
|
146
|
+
} = props
|
|
147
|
+
|
|
148
|
+
// Determine initial tab based on what's configured
|
|
149
|
+
// string = workspace, array = bookmarks
|
|
150
|
+
const getInitialTab = () => {
|
|
151
|
+
if (typeof onStartSessions === 'string' && onStartSessions) {
|
|
152
|
+
return 'workspaces'
|
|
153
|
+
}
|
|
154
|
+
return 'bookmarks'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const [activeTab, setActiveTab] = useState(getInitialTab)
|
|
158
|
+
|
|
159
|
+
// When switching tabs, clear the value if needed
|
|
160
|
+
const handleTabChange = (key) => {
|
|
161
|
+
setActiveTab(key)
|
|
162
|
+
// Reset to appropriate default when switching
|
|
163
|
+
if (key === 'bookmarks' && typeof onStartSessions === 'string') {
|
|
164
|
+
onChangeStartSessions([])
|
|
165
|
+
} else if (key === 'workspaces' && Array.isArray(onStartSessions)) {
|
|
166
|
+
onChangeStartSessions(undefined)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const tabItems = [
|
|
171
|
+
{
|
|
172
|
+
key: 'bookmarks',
|
|
173
|
+
label: (
|
|
174
|
+
<span>
|
|
175
|
+
<BookOutlined /> {e('bookmarks')}
|
|
176
|
+
</span>
|
|
177
|
+
)
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
key: 'workspaces',
|
|
181
|
+
label: (
|
|
182
|
+
<span>
|
|
183
|
+
<AppstoreOutlined /> {e('workspaces')}
|
|
184
|
+
<HelpIcon link='https://github.com/electerm/electerm/wiki/Workspace-Feature' />
|
|
185
|
+
</span>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div>
|
|
192
|
+
<Tabs
|
|
193
|
+
items={tabItems}
|
|
194
|
+
size='small'
|
|
195
|
+
activeKey={activeTab}
|
|
196
|
+
onChange={handleTabChange}
|
|
197
|
+
/>
|
|
198
|
+
{activeTab === 'bookmarks'
|
|
199
|
+
? (
|
|
200
|
+
<BookmarkSelect
|
|
201
|
+
bookmarks={bookmarks}
|
|
202
|
+
bookmarkGroups={bookmarkGroups}
|
|
203
|
+
onStartSessions={onStartSessions}
|
|
204
|
+
onChangeStartSessions={onChangeStartSessions}
|
|
205
|
+
/>
|
|
206
|
+
)
|
|
207
|
+
: (
|
|
208
|
+
<WorkspaceSelect
|
|
209
|
+
workspaces={workspaces}
|
|
210
|
+
onStartSessions={onStartSessions}
|
|
211
|
+
onChangeStartSessions={onChangeStartSessions}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
)
|
|
91
216
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import {
|
|
3
|
-
Modal,
|
|
4
3
|
Input,
|
|
5
4
|
InputNumber,
|
|
6
5
|
Space,
|
|
7
6
|
Typography,
|
|
8
|
-
Select
|
|
7
|
+
Select,
|
|
8
|
+
Button
|
|
9
9
|
} from 'antd'
|
|
10
|
+
import Modal from '../common/modal'
|
|
10
11
|
import { ColorPicker } from '../bookmark-form/common/color-picker.jsx'
|
|
11
12
|
|
|
12
13
|
const { TextArea } = Input
|
|
@@ -47,14 +48,24 @@ export default function TextBgModal ({
|
|
|
47
48
|
setFontFamily(initialFontFamily)
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
const footer = (
|
|
52
|
+
<>
|
|
53
|
+
<Button onClick={handleCancel}>
|
|
54
|
+
{e('cancel')}
|
|
55
|
+
</Button>
|
|
56
|
+
<Button type='primary' onClick={handleOk} className='mg1l'>
|
|
57
|
+
{e('ok')}
|
|
58
|
+
</Button>
|
|
59
|
+
</>
|
|
60
|
+
)
|
|
61
|
+
|
|
50
62
|
return (
|
|
51
63
|
<Modal
|
|
52
64
|
title={e('terminalBackgroundText')}
|
|
53
65
|
open={visible}
|
|
54
|
-
onOk={handleOk}
|
|
55
66
|
onCancel={handleCancel}
|
|
56
67
|
width={500}
|
|
57
|
-
|
|
68
|
+
footer={footer}
|
|
58
69
|
>
|
|
59
70
|
<div className='pd1'>
|
|
60
71
|
<Space direction='vertical' size='large' style={{ width: '100%' }}>
|
|
@@ -183,6 +183,7 @@ export default class FileSection extends React.Component {
|
|
|
183
183
|
toPath,
|
|
184
184
|
id: generate(),
|
|
185
185
|
host: this.props.tab?.host,
|
|
186
|
+
tabType: this.props.tab?.type,
|
|
186
187
|
...createTransferProps(this.props),
|
|
187
188
|
operation
|
|
188
189
|
})
|
|
@@ -736,6 +737,7 @@ export default class FileSection extends React.Component {
|
|
|
736
737
|
toPath = resolve(toPath, name)
|
|
737
738
|
const obj = {
|
|
738
739
|
host: this.props.tab?.host,
|
|
740
|
+
tabType: this.props.tab?.type,
|
|
739
741
|
typeFrom: type,
|
|
740
742
|
typeTo,
|
|
741
743
|
fromPath: resolve(path, name),
|
|
@@ -37,8 +37,9 @@ export default class ScrollFiles extends Component {
|
|
|
37
37
|
current: this.state.page,
|
|
38
38
|
pageSize: this.state.pageSize,
|
|
39
39
|
total: this.props.list.length,
|
|
40
|
+
showLessItems: true,
|
|
40
41
|
showSizeChanger: false,
|
|
41
|
-
simple:
|
|
42
|
+
simple: false,
|
|
42
43
|
onChange: this.onChange
|
|
43
44
|
}
|
|
44
45
|
return (
|
|
@@ -164,6 +164,19 @@
|
|
|
164
164
|
.pager-wrap
|
|
165
165
|
z-index 4
|
|
166
166
|
position relative
|
|
167
|
+
|
|
168
|
+
.sftp-has-pager
|
|
169
|
+
.sftp-table-content
|
|
170
|
+
position static
|
|
171
|
+
padding-bottom 42px
|
|
172
|
+
.pager-wrap
|
|
173
|
+
position absolute
|
|
174
|
+
bottom 0
|
|
175
|
+
left 0
|
|
176
|
+
width 100%
|
|
177
|
+
background var(--main)
|
|
178
|
+
border-top 1px solid var(--main-darker)
|
|
179
|
+
|
|
167
180
|
.file-header-context-menu
|
|
168
181
|
position fixed
|
|
169
182
|
z-index 999
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
InfoCircleOutlined,
|
|
9
9
|
AlignLeftOutlined,
|
|
10
10
|
BugOutlined,
|
|
11
|
-
HeartOutlined
|
|
12
|
-
JavaScriptOutlined
|
|
11
|
+
HeartOutlined
|
|
13
12
|
} from '@ant-design/icons'
|
|
14
|
-
import {
|
|
13
|
+
import { Tabs, Button } from 'antd'
|
|
14
|
+
import Modal from '../common/modal'
|
|
15
15
|
import Link from '../common/external-link'
|
|
16
16
|
import LogoElem from '../common/logo-elem'
|
|
17
17
|
import RunningTime from './app-running-time'
|
|
@@ -55,8 +55,48 @@ export default auto(function InfoModal (props) {
|
|
|
55
55
|
)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
|
|
58
|
+
const renderParsed = (obj, depth = 0) => {
|
|
59
|
+
if (Array.isArray(obj)) {
|
|
60
|
+
return (
|
|
61
|
+
<ul className='pd2l'>
|
|
62
|
+
{obj.map((item, i) => (
|
|
63
|
+
<li key={i}>{renderParsed(item, depth + 1)}</li>
|
|
64
|
+
))}
|
|
65
|
+
</ul>
|
|
66
|
+
)
|
|
67
|
+
} else if (typeof obj === 'object' && obj !== null) {
|
|
68
|
+
return (
|
|
69
|
+
<div className={depth > 0 ? 'pd2l' : ''}>
|
|
70
|
+
{Object.entries(obj).map(([k, v]) => (
|
|
71
|
+
<div key={k} className='pd1b'>
|
|
72
|
+
<b>{k}:</b> {renderParsed(v, depth + 1)}
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
} else {
|
|
78
|
+
return <span>{String(obj)}</span>
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const renderValue = (v) => {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(v)
|
|
85
|
+
return renderParsed(parsed)
|
|
86
|
+
} catch {
|
|
87
|
+
return <span>{v}</span>
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const renderOSInfo = () => {
|
|
92
|
+
return window.pre.osInfo().map(({ k, v }, i) => (
|
|
93
|
+
<div className='pd1b' key={i + '_os_' + k}>
|
|
94
|
+
<b className='bold'>{k}:</b>
|
|
95
|
+
<span className='mg1l'>
|
|
96
|
+
{renderValue(v)}
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
))
|
|
60
100
|
}
|
|
61
101
|
|
|
62
102
|
const { infoModalTab, commandLineHelp } = props
|
|
@@ -100,7 +140,7 @@ export default auto(function InfoModal (props) {
|
|
|
100
140
|
...env
|
|
101
141
|
}
|
|
102
142
|
const title = (
|
|
103
|
-
<div className='
|
|
143
|
+
<div className='custom-modal-close-confirm-title font16'>
|
|
104
144
|
<InfoCircleOutlined className='font20 mg1r' /> {e('about')} {name}
|
|
105
145
|
</div>
|
|
106
146
|
)
|
|
@@ -108,9 +148,7 @@ export default auto(function InfoModal (props) {
|
|
|
108
148
|
title,
|
|
109
149
|
width: window.innerWidth - 100,
|
|
110
150
|
maskClosable: true,
|
|
111
|
-
okText: e('ok'),
|
|
112
151
|
onCancel: onCloseAbout,
|
|
113
|
-
footer: null,
|
|
114
152
|
open: true,
|
|
115
153
|
wrapClassName: 'info-modal'
|
|
116
154
|
}
|
|
@@ -123,18 +161,18 @@ export default auto(function InfoModal (props) {
|
|
|
123
161
|
<LogoElem />
|
|
124
162
|
<p className='mg2b'>{e('desc')}</p>
|
|
125
163
|
<RunningTime />
|
|
126
|
-
<p className='mg1b'>
|
|
127
|
-
<UserOutlined /> <b className='mg1r'>{e('author')} ➾</b>
|
|
128
|
-
<Link to={authorUrl} className='mg1l'>
|
|
129
|
-
{authorName} ({email})
|
|
130
|
-
</Link>
|
|
131
|
-
</p>
|
|
132
164
|
<p className='mg1b'>
|
|
133
165
|
<HomeOutlined /> <b>{e('homepage')}/{e('download')} ➾</b>
|
|
134
166
|
<Link to={homepage} className='mg1l'>
|
|
135
167
|
{homepage}
|
|
136
168
|
</Link>
|
|
137
169
|
</p>
|
|
170
|
+
<p className='mg1b'>
|
|
171
|
+
<UserOutlined /> <b className='mg1r'>{e('author')} ➾</b>
|
|
172
|
+
<Link to={authorUrl} className='mg1l'>
|
|
173
|
+
{authorName} ({email})
|
|
174
|
+
</Link>
|
|
175
|
+
</p>
|
|
138
176
|
<p className='mg1b'>
|
|
139
177
|
<GithubOutlined /> <b className='mg1r'>github ➾</b>
|
|
140
178
|
<Link to={link} className='mg1l'>
|
|
@@ -180,12 +218,6 @@ export default auto(function InfoModal (props) {
|
|
|
180
218
|
<p className='mg1b'>
|
|
181
219
|
<InfoCircleOutlined /> <b className='mg1r'>{window.store.installSrc}</b>
|
|
182
220
|
</p>
|
|
183
|
-
<p className='mg1b'>
|
|
184
|
-
<JavaScriptOutlined /> <b className='mg1r'>Powered by</b>
|
|
185
|
-
<Link to='https://github.com/tylerlong/manate'>
|
|
186
|
-
manate
|
|
187
|
-
</Link>
|
|
188
|
-
</p>
|
|
189
221
|
{renderCheckUpdate()}
|
|
190
222
|
</>
|
|
191
223
|
)
|
|
@@ -223,20 +255,7 @@ export default auto(function InfoModal (props) {
|
|
|
223
255
|
{
|
|
224
256
|
key: infoTabs.os,
|
|
225
257
|
label: e('os'),
|
|
226
|
-
children:
|
|
227
|
-
return (
|
|
228
|
-
<div className='pd1b' key={i + '_os_' + k}>
|
|
229
|
-
<b className='bold'>{k}</b>:
|
|
230
|
-
<span className='mg1l'>
|
|
231
|
-
{
|
|
232
|
-
v.length > 30
|
|
233
|
-
? <pre>{formatJSON(v)}</pre>
|
|
234
|
-
: v
|
|
235
|
-
}
|
|
236
|
-
</span>
|
|
237
|
-
</div>
|
|
238
|
-
)
|
|
239
|
-
})
|
|
258
|
+
children: <div>{renderOSInfo()}</div>
|
|
240
259
|
}
|
|
241
260
|
]
|
|
242
261
|
|
|
@@ -12,26 +12,16 @@ import {
|
|
|
12
12
|
LeftOutlined,
|
|
13
13
|
RightOutlined
|
|
14
14
|
} from '@ant-design/icons'
|
|
15
|
-
import {
|
|
16
|
-
SingleIcon,
|
|
17
|
-
TwoColumnsIcon,
|
|
18
|
-
ThreeColumnsIcon,
|
|
19
|
-
TwoRowsIcon,
|
|
20
|
-
ThreeRowsIcon,
|
|
21
|
-
Grid2x2Icon,
|
|
22
|
-
TwoRowsRightIcon,
|
|
23
|
-
TwoColumnsBottomIcon
|
|
24
|
-
} from '../icons/split-icons'
|
|
25
15
|
import { Dropdown } from 'antd'
|
|
26
16
|
import Tab from './tab'
|
|
17
|
+
import LayoutMenu from './layout-menu'
|
|
27
18
|
import './tabs.styl'
|
|
28
19
|
import {
|
|
29
20
|
tabWidth,
|
|
30
21
|
tabMargin,
|
|
31
22
|
extraTabWidth,
|
|
32
23
|
windowControlWidth,
|
|
33
|
-
isMacJs
|
|
34
|
-
splitMapDesc
|
|
24
|
+
isMacJs
|
|
35
25
|
} from '../../common/constants'
|
|
36
26
|
import WindowControl from './window-control'
|
|
37
27
|
import AddBtn from './add-btn'
|
|
@@ -39,8 +29,6 @@ import AppDrag from './app-drag'
|
|
|
39
29
|
import NoSession from './no-session'
|
|
40
30
|
import classNames from 'classnames'
|
|
41
31
|
|
|
42
|
-
const e = window.translate
|
|
43
|
-
|
|
44
32
|
export default class Tabs extends Component {
|
|
45
33
|
constructor (props) {
|
|
46
34
|
super(props)
|
|
@@ -183,10 +171,6 @@ export default class Tabs extends Component {
|
|
|
183
171
|
window.store['activeTabId' + this.props.batch] = id
|
|
184
172
|
}
|
|
185
173
|
|
|
186
|
-
handleChangeLayout = ({ key }) => {
|
|
187
|
-
window.store.setLayout(key)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
174
|
renderAddBtn = () => {
|
|
191
175
|
const cls = classNames(
|
|
192
176
|
'pointer tabs-add-btn font16',
|
|
@@ -324,48 +308,12 @@ export default class Tabs extends Component {
|
|
|
324
308
|
)
|
|
325
309
|
}
|
|
326
310
|
|
|
327
|
-
getLayoutIcon = (layout) => {
|
|
328
|
-
const iconMaps = {
|
|
329
|
-
single: SingleIcon,
|
|
330
|
-
twoColumns: TwoColumnsIcon,
|
|
331
|
-
threeColumns: ThreeColumnsIcon,
|
|
332
|
-
twoRows: TwoRowsIcon,
|
|
333
|
-
threeRows: ThreeRowsIcon,
|
|
334
|
-
grid2x2: Grid2x2Icon,
|
|
335
|
-
twoRowsRight: TwoRowsRightIcon,
|
|
336
|
-
twoColumnsBottom: TwoColumnsBottomIcon
|
|
337
|
-
}
|
|
338
|
-
return iconMaps[layout]
|
|
339
|
-
}
|
|
340
|
-
|
|
341
311
|
renderLayoutMenu = () => {
|
|
342
|
-
if (!this.shouldRenderWindowControl()) {
|
|
343
|
-
return null
|
|
344
|
-
}
|
|
345
|
-
const items = Object.keys(splitMapDesc).map((t) => {
|
|
346
|
-
const v = splitMapDesc[t]
|
|
347
|
-
const Icon = this.getLayoutIcon(v)
|
|
348
|
-
return {
|
|
349
|
-
key: t,
|
|
350
|
-
label: (
|
|
351
|
-
<span>
|
|
352
|
-
<Icon /> {e(v)}
|
|
353
|
-
</span>
|
|
354
|
-
),
|
|
355
|
-
onClick: () => this.handleChangeLayout({ key: t })
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
const v = splitMapDesc[this.props.layout]
|
|
359
|
-
const Icon = this.getLayoutIcon(v)
|
|
360
312
|
return (
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
<span className='tabs-dd-icon layout-dd-icon mg1l'>
|
|
366
|
-
<Icon /> <DownOutlined />
|
|
367
|
-
</span>
|
|
368
|
-
</Dropdown>
|
|
313
|
+
<LayoutMenu
|
|
314
|
+
layout={this.props.layout}
|
|
315
|
+
visible={this.shouldRenderWindowControl()}
|
|
316
|
+
/>
|
|
369
317
|
)
|
|
370
318
|
}
|
|
371
319
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout and Workspace menu dropdown component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { useState } from 'react'
|
|
6
|
+
import { Dropdown, Tabs } from 'antd'
|
|
7
|
+
import {
|
|
8
|
+
DownOutlined,
|
|
9
|
+
AppstoreOutlined,
|
|
10
|
+
LayoutOutlined
|
|
11
|
+
} from '@ant-design/icons'
|
|
12
|
+
import { splitMapDesc } from '../../common/constants'
|
|
13
|
+
import LayoutSelect, { getLayoutIcon } from './layout-select'
|
|
14
|
+
import WorkspaceSelect from './workspace-select'
|
|
15
|
+
import HelpIcon from '../common/help-icon'
|
|
16
|
+
|
|
17
|
+
const e = window.translate
|
|
18
|
+
|
|
19
|
+
export default function LayoutMenu (props) {
|
|
20
|
+
const { layout, visible } = props
|
|
21
|
+
const [activeTab, setActiveTab] = useState('layout')
|
|
22
|
+
|
|
23
|
+
if (!visible) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const tabItems = [
|
|
28
|
+
{
|
|
29
|
+
key: 'layout',
|
|
30
|
+
label: (
|
|
31
|
+
<span>
|
|
32
|
+
<LayoutOutlined /> {e('layout')}
|
|
33
|
+
</span>
|
|
34
|
+
)
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: 'workspaces',
|
|
38
|
+
label: (
|
|
39
|
+
<span>
|
|
40
|
+
<AppstoreOutlined /> {e('workspaces')}
|
|
41
|
+
<HelpIcon link='https://github.com/electerm/electerm/wiki/Workspace-Feature' />
|
|
42
|
+
</span>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const v = splitMapDesc[layout]
|
|
48
|
+
const Icon = getLayoutIcon(v)
|
|
49
|
+
|
|
50
|
+
const dropdownContent = (
|
|
51
|
+
<div className='layout-workspace-dropdown'>
|
|
52
|
+
<Tabs
|
|
53
|
+
items={tabItems}
|
|
54
|
+
size='small'
|
|
55
|
+
activeKey={activeTab}
|
|
56
|
+
onChange={setActiveTab}
|
|
57
|
+
/>
|
|
58
|
+
{activeTab === 'layout'
|
|
59
|
+
? <LayoutSelect layout={layout} />
|
|
60
|
+
: <WorkspaceSelect store={window.store} />}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Dropdown
|
|
66
|
+
popupRender={() => dropdownContent}
|
|
67
|
+
placement='bottomRight'
|
|
68
|
+
trigger={['click']}
|
|
69
|
+
>
|
|
70
|
+
<span className='tabs-dd-icon layout-dd-icon mg1l'>
|
|
71
|
+
<Icon /> <DownOutlined />
|
|
72
|
+
</span>
|
|
73
|
+
</Dropdown>
|
|
74
|
+
)
|
|
75
|
+
}
|