Skip to content

Commit 6bf7d61

Browse files
authored
style: Refresh design of app store list (#2094)
1 parent 8ac2cce commit 6bf7d61

File tree

13 files changed

+185
-311
lines changed

13 files changed

+185
-311
lines changed

packages/server-admin-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"react-copy-to-clipboard": "^5.0.3",
4141
"react-dom": "^16.13.1",
4242
"react-html-parser": "^2.0.2",
43+
"react-infinite-scroll-component": "^6.1.0",
4344
"react-json-tree": "^0.20.0",
4445
"react-jsonschema-form-bs4": "^1.7.1",
4546
"react-redux": "^5.1.2",

packages/server-admin-ui/scss/_bootstrap-variables.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ $colors: (
4545

4646
$theme-colors: (
4747
primary: $blue,
48-
secondary: $gray-300,
48+
secondary: $gray-700,
4949
success: $green,
5050
info: $cyan,
5151
warning: $yellow,
5252
danger: $red,
53-
light: $gray-100,
53+
light: $gray-200,
5454
dark: $gray-800
5555
);
5656

@@ -59,7 +59,7 @@ $theme-colors: (
5959
// Quickly modify global styling by enabling or disabling optional features.
6060

6161
$enable-transitions: true;
62-
$enable-rounded: false;
62+
$enable-rounded: true;
6363

6464
// Body
6565
//

packages/server-admin-ui/scss/_custom.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ form.rjsf div.row.array-item {
2828
margin: 0px;
2929
}
3030
}
31+
32+
/** Utility class to attempt to wrap text in a more balanced way. **/
33+
.text-pretty {
34+
text-wrap: balance; /* Fallback for older browsers */
35+
text-wrap: pretty; /* Future spec */
36+
}

packages/server-admin-ui/scss/core/_dropdown.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Links, buttons, and more within the dropdown menu
22
.dropdown-item {
33
position: relative;
4-
padding: 10px 20px;
4+
padding: 0.375rem 0.75rem;
55
border-bottom: 1px solid $dropdown-border-color;
66

77
&:last-child {

packages/server-admin-ui/src/views/Webapps/Webapp.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ const propTypes = {
1616
children: PropTypes.node
1717
}
1818

19+
export function urlToWebapp(webAppInfo) {
20+
return webAppInfo.keywords.includes('signalk-embeddable-webapp')
21+
? `/admin/#/e/${toSafeModuleId(webAppInfo.name)}`
22+
: `/${webAppInfo.name}`
23+
}
24+
1925
class Webapp extends Component {
2026
render() {
2127
const { webAppInfo, ...attributes } = this.props
@@ -36,9 +42,7 @@ class Webapp extends Component {
3642
'text-capitalize'
3743
)
3844
const header = webAppInfo?.signalk?.displayName || webAppInfo.name
39-
const url = webAppInfo.keywords.includes('signalk-embeddable-webapp')
40-
? `/admin/#/e/${toSafeModuleId(webAppInfo.name)}`
41-
: `/${webAppInfo.name}`
45+
const url = urlToWebapp(webAppInfo)
4246

4347
const blockIcon = function (icon, appIcon = null) {
4448
const classes = classNames(

packages/server-admin-ui/src/views/appstore/Apps/Apps.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const Apps = function (props) {
5858
}
5959
}
6060

61-
/*
61+
/*
6262
Show different warning message
6363
whether the store is available or if an app was installed or removed
6464
*/
@@ -103,19 +103,19 @@ const Apps = function (props) {
103103
<CardTitle>Apps & Plugins</CardTitle>
104104
<div className="button-wrapper">
105105
<Button
106-
color={view === 'All' ? 'primary' : 'secondary'}
106+
color={view === 'All' ? 'secondary' : 'light'}
107107
onClick={() => setSelectedView('All')}
108108
>
109109
All
110110
</Button>
111111
<Button
112-
color={view === 'Installed' ? 'primary' : 'secondary'}
112+
color={view === 'Installed' ? 'secondary' : 'light'}
113113
onClick={() => setSelectedView('Installed')}
114114
>
115115
Installed
116116
</Button>
117117
<Button
118-
color={view === 'Updates' ? 'primary' : 'secondary'}
118+
color={view === 'Updates' ? 'secondary' : 'light'}
119119
onClick={() => setSelectedView('Updates')}
120120
>
121121
Updates
@@ -128,7 +128,7 @@ const Apps = function (props) {
128128
{props.appStore.installing.length > 0 && (
129129
<>
130130
<Button
131-
color={view === 'Installing' ? 'primary' : 'secondary'}
131+
color={view === 'Installing' ? 'secondary' : 'light'}
132132
onClick={() => setSelectedView('Installing')}
133133
>
134134
Installs & Removes
@@ -174,7 +174,7 @@ const Apps = function (props) {
174174
{props.appStore.categories?.map((item) => (
175175
<Button
176176
key={item}
177-
color="primary"
177+
color="secondary"
178178
className={category === item ? 'active' : undefined}
179179
outline
180180
onClick={() => setSelectedCategory(item)}
Lines changed: 59 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,65 @@
1-
import React, { Component } from 'react'
2-
3-
import { Table } from 'reactstrap'
4-
import NameCellRenderer from './Grid/cell-renderers/NameCellRenderer'
5-
import TypeCellRenderer from './Grid/cell-renderers/TypeCellRenderer'
1+
import React, { useEffect, useState } from 'react'
2+
import InfiniteScroll from 'react-infinite-scroll-component'
3+
import { ListGroup, ListGroupItem } from 'reactstrap'
64
import ActionCellRenderer from './Grid/cell-renderers/ActionCellRenderer'
7-
import VersionCellRenderer from './Grid/cell-renderers/VersionCellRenderer'
8-
9-
const XL_WIDTH = 1200
10-
const L_WIDTH = 992
11-
const M_WIDTH = 768
12-
const S_WIDTH = 576
135

14-
class AppsList extends Component {
15-
render() {
16-
return (
17-
<Table>
18-
<thead>
19-
<tr>
20-
<th>Name</th>
21-
<th
22-
className={
23-
'text-center ' + (window.innerWidth < S_WIDTH ? 'd-none' : '')
24-
}
25-
>
26-
Version
27-
</th>
28-
<th className={window.innerWidth < L_WIDTH ? 'd-none' : ''}>
29-
Description
30-
</th>
31-
<th className={window.innerWidth < XL_WIDTH ? 'd-none' : ''}>
32-
Author
33-
</th>
34-
<th
35-
className={
36-
'text-center ' + (window.innerWidth < M_WIDTH ? 'd-none' : '')
37-
}
38-
>
39-
<div>Type</div>
40-
</th>
6+
export function AppListItem(app) {
7+
return (
8+
<ListGroupItem className="p-3">
9+
<div className="d-md-flex align-items-center flex-grow-1">
10+
<div className="flex-grow-1 mr-3">
11+
<h5 className="text-dark mb-0">{app.name}</h5>
12+
<div className="text-muted">
13+
<span className="font-weight-bolder">
14+
v{app.installedVersion || app.version}{' '}
15+
</span>
16+
{app.newVersion && (
17+
<>
18+
<span className="text-secondary"></span>
19+
<span className="font-weight-bolder text-success font-italic">
20+
v{app.newVersion}
21+
</span>{' '}
22+
</>
23+
)}
24+
released by
25+
<span className="text-nowrap font-weight-bolder">
26+
{' '}
27+
{app.author}
28+
</span>{' '}
29+
on
30+
<span className="text-nowrap"> {app.updated.substring(0, 10)}</span>
31+
</div>
32+
<p className="text-pretty mb-0">{app.description}</p>
33+
</div>
34+
<div className="mt-3 mt-md-0">
35+
<ActionCellRenderer data={app} />
36+
</div>
37+
</div>
38+
</ListGroupItem>
39+
)
40+
}
4141

42-
<th className="text-center">Action</th>
43-
</tr>
44-
</thead>
45-
<tbody>
46-
{this.props.apps.map((app) => (
47-
<tr key={app.name}>
48-
<td>
49-
<NameCellRenderer data={app} value={app.name} />
50-
</td>
51-
<td className={window.innerWidth < S_WIDTH ? 'd-none' : ''}>
52-
<VersionCellRenderer data={app} />
53-
</td>
54-
<td className={window.innerWidth < L_WIDTH ? 'd-none' : ''}>
55-
{app.description}
56-
</td>
57-
<td className={window.innerWidth < XL_WIDTH ? 'd-none' : ''}>
58-
{app.author}
59-
</td>
60-
<td className={window.innerWidth < M_WIDTH ? 'd-none' : ''}>
61-
<TypeCellRenderer data={app} />
62-
</td>
42+
export default function AppList(props) {
43+
const [apps, setApps] = useState([])
6344

64-
<td>
65-
<ActionCellRenderer data={app} />
66-
</td>
67-
</tr>
68-
))}
69-
</tbody>
70-
</Table>
71-
)
45+
function loadMore() {
46+
setApps(props.apps.slice(0, apps.length + 20))
7247
}
73-
}
7448

75-
export default AppsList
49+
// Load initial list of apps
50+
useEffect(loadMore, [props.apps])
51+
52+
return (
53+
<ListGroup>
54+
<InfiniteScroll
55+
dataLength={apps.length}
56+
next={loadMore}
57+
hasMore={apps.length != props.apps.length}
58+
>
59+
{apps.map((app) => (
60+
<AppListItem key={app.name} {...app} />
61+
))}
62+
</InfiniteScroll>
63+
</ListGroup>
64+
)
65+
}

0 commit comments

Comments
 (0)