@electerm/electerm-react 3.15.153 → 3.15.155

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.
@@ -16,6 +16,15 @@
16
16
  .number-input
17
17
  min-width 210px
18
18
 
19
+ // On mobile, stack loginScriptDelay and loginScript vertically instead of
20
+ // horizontally (the antd Space renders them side by side by default).
21
+ .is-mobile
22
+ .run-scripts-space.ant-space
23
+ flex-direction column
24
+ align-items stretch
25
+ .ant-space-item
26
+ width 100%
27
+
19
28
  .tree-select-wrapper
20
29
  display flex
21
30
  flex-direction column
@@ -18,7 +18,7 @@ export default function renderRunScripts () {
18
18
  <>
19
19
  <Space
20
20
  align='baseline'
21
- className='width-100'
21
+ className='width-100 run-scripts-space'
22
22
  key={field.key}
23
23
  >
24
24
  <FormItem
@@ -78,4 +78,27 @@
78
78
  max-width none
79
79
  width 100%
80
80
  .qm-label-select
81
- width 100%
81
+ width 100%
82
+
83
+ // On mobile, stack the quick-command row (name / delay / command / remove)
84
+ // vertically instead of side by side — the horizontal Space.Compact layout
85
+ // is too cramped on narrow screens.
86
+ .is-mobile
87
+ .qm-cmd-row.ant-space-compact
88
+ display flex
89
+ flex-direction column
90
+ align-items stretch
91
+ gap 8px
92
+ .ant-space-compact-item
93
+ width 100%
94
+ // reset the constrained widths so inputs fill the row when stacked
95
+ .qm-name-input
96
+ max-width none
97
+ width 100%
98
+ .ant-input-number
99
+ width 100% !important
100
+ // keep the drag handle + remove button on a tidy line of their own
101
+ > .drag
102
+ align-self flex-start
103
+ > button:last-child
104
+ align-self flex-end
@@ -67,7 +67,7 @@ export default function renderQm (form) {
67
67
  return (
68
68
  <Space.Compact
69
69
  align='center'
70
- className='width-100 mg2b'
70
+ className='width-100 mg2b qm-cmd-row'
71
71
  key={field.key}
72
72
  draggable
73
73
  onDragStart={(e) => handleDragStart(e, i)}
@@ -656,7 +656,8 @@ export default class SessionWrapper extends Component {
656
656
  const simpleMapper = {
657
657
  [paneMap.terminal]: 'T',
658
658
  [paneMap.fileManager]: 'F',
659
- [paneMap.ssh]: 'T'
659
+ [paneMap.ssh]: 'T',
660
+ [paneMap.sftp]: 'S'
660
661
  }
661
662
  return (
662
663
  <div className='term-sftp-tabs fleft'>
@@ -110,7 +110,7 @@ export default function DeepLinkControl () {
110
110
  <Tooltip
111
111
  title={renderTooltipContent()}
112
112
  >
113
- <Space>
113
+ <div className='deep-link-btns'>
114
114
  {
115
115
  !isAllRegistered && (
116
116
  <Button
@@ -134,7 +134,7 @@ export default function DeepLinkControl () {
134
134
  </Button>
135
135
  )
136
136
  }
137
- </Space>
137
+ </div>
138
138
  </Tooltip>
139
139
  </div>
140
140
  )
@@ -11,9 +11,9 @@ import {
11
11
  } from '@ant-design/icons'
12
12
  import {
13
13
  Button,
14
+ Pagination,
14
15
  Modal,
15
16
  Space,
16
- Table,
17
17
  Tag,
18
18
  Tooltip,
19
19
  Typography,
@@ -126,8 +126,10 @@ export default class SettingPasswords extends Component {
126
126
  })
127
127
  }
128
128
 
129
- handleTableChange = (pagination) => {
130
- this.setState({ pagination })
129
+ handlePageChange = (page, pageSize) => {
130
+ this.setState({
131
+ pagination: { current: page, pageSize }
132
+ })
131
133
  }
132
134
 
133
135
  getFilteredData = () => {
@@ -143,88 +145,48 @@ export default class SettingPasswords extends Component {
143
145
  })
144
146
  }
145
147
 
146
- getColumns = () => {
147
- const columns = [
148
- {
149
- title: e('password'),
150
- dataIndex: 'password',
151
- key: 'password',
152
- render: () => {
153
- const props0 = {
154
- children: [
155
- <KeyOutlined key='icon' />,
156
- <TextAnt keyboard key='text'>********</TextAnt>
157
- ]
158
- }
159
- return <Space>{props0.children}</Space>
160
- }
161
- },
162
- {
163
- title: e('count'),
164
- dataIndex: 'count',
165
- key: 'count',
166
- width: 80,
167
- render: (count) => {
168
- const props0 = {
169
- color: 'blue',
170
- children: count
171
- }
172
- return <Tag {...props0} />
173
- }
174
- },
175
- {
176
- title: e('host'),
177
- dataIndex: 'titles',
178
- key: 'host',
179
- render: (titles) => {
180
- const display = titles.length > 2
181
- ? `${titles.slice(0, 2).join(', ')}... (+${titles.length - 2})`
182
- : titles.join(', ')
183
- const props0 = {
184
- title: titles.join('\n'),
185
- children: <span>{display}</span>
186
- }
187
- return <Tooltip {...props0} />
188
- }
189
- },
190
- {
191
- title: e('actions'),
192
- key: 'actions',
193
- width: 80,
194
- render: (_, record) => {
195
- const copyProps0 = {
196
- type: 'text',
197
- icon: <CopyOutlined />,
198
- onClick: () => this.handleCopyPassword(record.password)
199
- }
200
- const editProps0 = {
201
- type: 'text',
202
- icon: <EditOutlined />,
203
- onClick: () => this.showEditModal(record)
204
- }
205
- const copyTooltipProps = {
206
- title: e('copy'),
207
- children: <Button {...copyProps0} />
208
- }
209
- const editTooltipProps = {
210
- title: e('changePassword'),
211
- children: <Button {...editProps0} />
212
- }
213
- const spaceProps0 = {
214
- children: [
215
- <Tooltip key='copy' {...copyTooltipProps} />,
216
- <Tooltip key='edit' {...editTooltipProps} />
217
- ]
218
- }
219
- return <Space>{spaceProps0.children}</Space>
220
- }
221
- }
222
- ]
223
- return columns
148
+ renderItem = (record) => {
149
+ const { password, count, titles } = record
150
+ const display = titles.length > 2
151
+ ? `${titles.slice(0, 2).join(', ')}... (+${titles.length - 2})`
152
+ : titles.join(', ')
153
+ const copyProps = {
154
+ type: 'text',
155
+ icon: <CopyOutlined />,
156
+ onClick: () => this.handleCopyPassword(password)
157
+ }
158
+ const editProps = {
159
+ type: 'text',
160
+ icon: <EditOutlined />,
161
+ onClick: () => this.showEditModal(record)
162
+ }
163
+ return (
164
+ <div className='setting-passwords-item' key={password}>
165
+ <div className='setting-passwords-item-inner'>
166
+ <Space className='setting-passwords-pwd-cell'>
167
+ <KeyOutlined />
168
+ <TextAnt keyboard>********</TextAnt>
169
+ </Space>
170
+ <Tag color='blue' className='setting-passwords-count-cell'>{count}</Tag>
171
+ <Tooltip title={titles.join('\n')}>
172
+ <span className='setting-passwords-host-cell'>{display}</span>
173
+ </Tooltip>
174
+ <Space className='setting-passwords-actions-cell'>
175
+ <Tooltip title={e('copy')}>
176
+ <Button {...copyProps} />
177
+ </Tooltip>
178
+ <Tooltip title={e('changePassword')}>
179
+ <Button {...editProps} />
180
+ </Tooltip>
181
+ </Space>
182
+ </div>
183
+ </div>
184
+ )
224
185
  }
225
186
 
226
187
  renderContent () {
227
188
  const { search, pagination } = this.state
189
+ const { current, pageSize } = pagination
228
190
  const data = this.getFilteredData()
229
191
 
230
192
  if (data.length === 0) {
@@ -241,19 +203,29 @@ export default class SettingPasswords extends Component {
241
203
  onChange: this.handleSearchChange,
242
204
  placeholder: e('search')
243
205
  }
244
- const tableProps0 = {
245
- dataSource: data,
246
- columns: this.getColumns(),
247
- rowKey: 'password',
248
- pagination: { ...pagination, showSizeChanger: true },
249
- onChange: this.handleTableChange,
206
+
207
+ // clamp current page when filtering shrinks the data set
208
+ const pageCount = Math.ceil(data.length / pageSize)
209
+ const safePage = Math.min(current, Math.max(pageCount, 1))
210
+ const pagedData = data.slice((safePage - 1) * pageSize, safePage * pageSize)
211
+
212
+ const paginationProps0 = {
213
+ total: data.length,
214
+ current: safePage,
215
+ pageSize,
216
+ showSizeChanger: true,
217
+ onChange: this.handlePageChange,
218
+ onShowSizeChange: this.handlePageChange,
250
219
  size: 'small'
251
220
  }
252
221
 
253
222
  return (
254
223
  <div>
255
224
  <Search {...searchProps0} />
256
- <Table {...tableProps0} />
225
+ <div className='setting-passwords-list'>
226
+ {pagedData.map(item => this.renderItem(item))}
227
+ </div>
228
+ <Pagination {...paginationProps0} />
257
229
  </div>
258
230
  )
259
231
  }
@@ -10,4 +10,40 @@
10
10
  left 43px
11
11
  height 60px
12
12
  right 0
13
- z-index 888
13
+ z-index 888
14
+ .deep-link-btns
15
+ display flex
16
+ flex-wrap wrap
17
+ gap 8px
18
+
19
+ // passwords list — replaces the old table so content wraps
20
+ // instead of forcing horizontal scroll on narrow/mobile screens
21
+ .setting-passwords-list
22
+ border 1px solid var(--main-light)
23
+ border-radius 6px
24
+ overflow hidden
25
+ margin 8px 0
26
+ .setting-passwords-item
27
+ padding 8px 12px
28
+ &:not(:last-child)
29
+ border-bottom 1px solid var(--main-light)
30
+ .setting-passwords-item
31
+ .setting-passwords-item-inner
32
+ display flex
33
+ flex-wrap wrap
34
+ align-items center
35
+ gap 8px
36
+ width 100%
37
+ .setting-passwords-pwd-cell
38
+ flex-shrink 0
39
+ .setting-passwords-count-cell
40
+ flex-shrink 0
41
+ margin 0
42
+ .setting-passwords-host-cell
43
+ flex 1 1 120px
44
+ min-width 120px
45
+ word-break break-all
46
+ line-height 1.5
47
+ .setting-passwords-actions-cell
48
+ flex-shrink 0
49
+ margin-left auto
@@ -65,7 +65,7 @@ export default function DataTransport (props) {
65
65
  }
66
66
 
67
67
  return (
68
- <div className='pd2 fix'>
68
+ <div className='pd2 fix sync-data-transport'>
69
69
  <div className='fleft'>
70
70
  <Button
71
71
  icon={<ExportOutlined />}
@@ -9,6 +9,7 @@ import { Tabs, Tooltip } from 'antd'
9
9
  import MultiSelectModal from '../common/multi-select-modal'
10
10
  import {
11
11
  ArrowsAltOutlined,
12
+ CloseOutlined,
12
13
  EditOutlined,
13
14
  PlusCircleOutlined,
14
15
  ShrinkOutlined,
@@ -105,6 +106,10 @@ export default memo(function SidebarPanel (props) {
105
106
  onClick={store.handlePin}
106
107
  />
107
108
  </Tooltip>
109
+ <CloseOutlined
110
+ {...prps}
111
+ onClick={store.handleCloseSidebar}
112
+ />
108
113
  </div>
109
114
  <div className='pd1y pd2x'>
110
115
  <Tabs {...tabsProps} />
@@ -100,3 +100,35 @@
100
100
  margin-top 0
101
101
  min-height 100vh
102
102
  border-radius 0
103
+
104
+ // ---- 5. Sync panel data transport: stack export/import and auto-sync
105
+ // vertically with a gap. On narrow screens the right-floated
106
+ // auto-sync block wraps under the import button with no vertical
107
+ // gap — left-align it and add breathing room.
108
+ .sync-data-transport
109
+ .fleft,
110
+ .fright
111
+ float none
112
+ .fright
113
+ margin-top 8px
114
+ // the switch carries mg3l (32px left margin) to separate it from the
115
+ // import button on desktop — reset it when stacked so it left-aligns
116
+ .ant-switch
117
+ margin-left 0
118
+
119
+ // ---- 6. Solid Radio.Button groups (e.g. SSH auth type selector, session
120
+ // type selector): on narrow screens the connected button row wraps,
121
+ // but antd styles it as a single horizontal strip — adjacent buttons
122
+ // share borders via -1px margin and border-radius is only applied to
123
+ // the first/last child. When the row wraps, radius/borders break and
124
+ // the UI looks messed up. Convert each button to a standalone pill so
125
+ // wrapping stays tidy.
126
+ .ant-radio-group.ant-radio-group-solid
127
+ display flex
128
+ flex-wrap wrap
129
+ gap 6px
130
+ .ant-radio-button-wrapper
131
+ border-radius 3px
132
+ margin-inline-end 0
133
+ &::before
134
+ display none
@@ -29,6 +29,15 @@ export default Store => {
29
29
  }
30
30
  }
31
31
 
32
+ Store.prototype.handleCloseSidebar = function () {
33
+ const { store } = window
34
+ if (store.pinned) {
35
+ ls.setItem(sidebarPinnedKey, 'false')
36
+ store.pinned = false
37
+ }
38
+ store.setOpenedSideBar('')
39
+ }
40
+
32
41
  Store.prototype.handleSidebarPanelTab = function (tab) {
33
42
  window.store.sidebarPanelTab = tab
34
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "3.15.153",
3
+ "version": "3.15.155",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",