mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-21 10:16:32 +00:00
Use drive-selector's table for flash errors table
Change-type: patch Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
This commit is contained in:
parent
e74dc9eb60
commit
31409c61ca
@ -18,15 +18,7 @@ import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exc
|
||||
import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg';
|
||||
import * as sourceDestination from 'etcher-sdk/build/source-destination/';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Flex,
|
||||
ModalProps,
|
||||
Txt,
|
||||
Badge,
|
||||
Link,
|
||||
Table,
|
||||
TableColumn,
|
||||
} from 'rendition';
|
||||
import { Flex, ModalProps, Txt, Badge, Link, TableColumn } from 'rendition';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
@ -43,7 +35,12 @@ import { getImage, isDriveSelected } from '../../models/selection-state';
|
||||
import { store } from '../../models/store';
|
||||
import { logEvent, logException } from '../../modules/analytics';
|
||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||
import { Alert, Modal, ScrollableFlex } from '../../styled-components';
|
||||
import {
|
||||
Alert,
|
||||
GenericTableProps,
|
||||
Modal,
|
||||
Table,
|
||||
} from '../../styled-components';
|
||||
|
||||
import DriveSVGIcon from '../../../assets/tgt.svg';
|
||||
import { SourceMetadata } from '../source-selector/source-selector';
|
||||
@ -75,74 +72,29 @@ function isDrivelistDrive(drive: Drive): drive is DrivelistDrive {
|
||||
return typeof (drive as DrivelistDrive).size === 'number';
|
||||
}
|
||||
|
||||
const DrivesTable = styled(({ refFn, ...props }) => (
|
||||
<div>
|
||||
<Table<Drive> ref={refFn} {...props} />
|
||||
</div>
|
||||
const DrivesTable = styled((props: GenericTableProps<Drive>) => (
|
||||
<Table<Drive> {...props} />
|
||||
))`
|
||||
[data-display='table-head']
|
||||
> [data-display='table-row']
|
||||
> [data-display='table-cell'] {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: ${(props) => props.theme.colors.quartenary.light};
|
||||
|
||||
input[type='checkbox'] + div {
|
||||
display: ${({ multipleSelection }) =>
|
||||
multipleSelection ? 'flex' : 'none'};
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 38%;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
width: 32%;
|
||||
}
|
||||
}
|
||||
|
||||
[data-display='table-body'] > [data-display='table-row'] {
|
||||
> [data-display='table-cell']:first-child {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
> [data-display='table-cell']:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&[data-highlight='true'] {
|
||||
&.system {
|
||||
background-color: ${(props) =>
|
||||
props.showWarnings ? '#fff5e6' : '#e8f5fc'};
|
||||
[data-display='table-head'],
|
||||
[data-display='table-body'] {
|
||||
> [data-display='table-row'] > [data-display='table-cell'] {
|
||||
&:nth-child(2) {
|
||||
width: 38%;
|
||||
}
|
||||
|
||||
> [data-display='table-cell']:first-child {
|
||||
box-shadow: none;
|
||||
&:nth-child(3) {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
width: 32%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&& [data-display='table-row'] > [data-display='table-cell'] {
|
||||
padding: 6px 8px;
|
||||
color: #2a506f;
|
||||
}
|
||||
|
||||
input[type='checkbox'] + div {
|
||||
border-radius: ${({ multipleSelection }) =>
|
||||
multipleSelection ? '4px' : '50%'};
|
||||
}
|
||||
`;
|
||||
|
||||
function badgeShadeFromStatus(status: string) {
|
||||
@ -453,95 +405,92 @@ export class DriveSelector extends React.Component<
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<Flex width="100%" height="90%">
|
||||
{!hasAvailableDrives() ? (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
width="100%"
|
||||
>
|
||||
<DriveSVGIcon width="40px" height="90px" />
|
||||
<b>{this.props.emptyListLabel}</b>
|
||||
</Flex>
|
||||
) : (
|
||||
<ScrollableFlex flexDirection="column" width="100%">
|
||||
<DrivesTable
|
||||
refFn={(t: Table<Drive>) => {
|
||||
if (t !== null) {
|
||||
t.setRowSelection(selectedList);
|
||||
}
|
||||
}}
|
||||
multipleSelection={this.props.multipleSelection}
|
||||
columns={this.tableColumns}
|
||||
data={displayedDrives}
|
||||
disabledRows={disabledDrives}
|
||||
getRowClass={(row: Drive) =>
|
||||
isDrivelistDrive(row) && row.isSystem ? ['system'] : []
|
||||
{!hasAvailableDrives() ? (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
width="100%"
|
||||
>
|
||||
<DriveSVGIcon width="40px" height="90px" />
|
||||
<b>{this.props.emptyListLabel}</b>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<DrivesTable
|
||||
refFn={(t) => {
|
||||
if (t !== null) {
|
||||
t.setRowSelection(selectedList);
|
||||
}
|
||||
rowKey="displayName"
|
||||
onCheck={(rows: Drive[]) => {
|
||||
const newSelection = rows.filter(isDrivelistDrive);
|
||||
if (this.props.multipleSelection) {
|
||||
this.setState({
|
||||
selectedList: newSelection,
|
||||
});
|
||||
return;
|
||||
}}
|
||||
multipleSelection={this.props.multipleSelection}
|
||||
columns={this.tableColumns}
|
||||
data={displayedDrives}
|
||||
disabledRows={disabledDrives}
|
||||
getRowClass={(row: Drive) =>
|
||||
isDrivelistDrive(row) && row.isSystem ? ['system'] : []
|
||||
}
|
||||
rowKey="displayName"
|
||||
onCheck={(rows: Drive[]) => {
|
||||
const newSelection = rows.filter(isDrivelistDrive);
|
||||
if (this.props.multipleSelection) {
|
||||
this.setState({
|
||||
selectedList: newSelection,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
selectedList: newSelection.slice(newSelection.length - 1),
|
||||
});
|
||||
}}
|
||||
onRowClick={(row: Drive) => {
|
||||
if (
|
||||
!isDrivelistDrive(row) ||
|
||||
this.driveShouldBeDisabled(row, image)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (this.props.multipleSelection) {
|
||||
const newList = [...selectedList];
|
||||
const selectedIndex = selectedList.findIndex(
|
||||
(drive) => drive.device === row.device,
|
||||
);
|
||||
if (selectedIndex === -1) {
|
||||
newList.push(row);
|
||||
} else {
|
||||
// Deselect if selected
|
||||
newList.splice(selectedIndex, 1);
|
||||
}
|
||||
this.setState({
|
||||
selectedList: newSelection.slice(newSelection.length - 1),
|
||||
selectedList: newList,
|
||||
});
|
||||
}}
|
||||
onRowClick={(row: Drive) => {
|
||||
if (
|
||||
!isDrivelistDrive(row) ||
|
||||
this.driveShouldBeDisabled(row, image)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (this.props.multipleSelection) {
|
||||
const newList = [...selectedList];
|
||||
const selectedIndex = selectedList.findIndex(
|
||||
(drive) => drive.device === row.device,
|
||||
);
|
||||
if (selectedIndex === -1) {
|
||||
newList.push(row);
|
||||
} else {
|
||||
// Deselect if selected
|
||||
newList.splice(selectedIndex, 1);
|
||||
}
|
||||
this.setState({
|
||||
selectedList: newList,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
selectedList: [row],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{numberOfHiddenSystemDrives > 0 && (
|
||||
<Link
|
||||
mt={15}
|
||||
mb={15}
|
||||
fontSize="14px"
|
||||
onClick={() => this.setState({ showSystemDrives: true })}
|
||||
>
|
||||
<Flex alignItems="center">
|
||||
<ChevronDownSvg height="1em" fill="currentColor" />
|
||||
<Txt ml={8}>Show {numberOfHiddenSystemDrives} hidden</Txt>
|
||||
</Flex>
|
||||
</Link>
|
||||
)}
|
||||
</ScrollableFlex>
|
||||
)}
|
||||
{this.props.showWarnings && hasSystemDrives ? (
|
||||
<Alert className="system-drive-alert" style={{ width: '67%' }}>
|
||||
Selecting your system drive is dangerous and will erase your
|
||||
drive!
|
||||
</Alert>
|
||||
) : null}
|
||||
</Flex>
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
selectedList: [row],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{numberOfHiddenSystemDrives > 0 && (
|
||||
<Link
|
||||
mt={15}
|
||||
mb={15}
|
||||
fontSize="14px"
|
||||
onClick={() => this.setState({ showSystemDrives: true })}
|
||||
>
|
||||
<Flex alignItems="center">
|
||||
<ChevronDownSvg height="1em" fill="currentColor" />
|
||||
<Txt ml={8}>Show {numberOfHiddenSystemDrives} hidden</Txt>
|
||||
</Flex>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{this.props.showWarnings && hasSystemDrives ? (
|
||||
<Alert className="system-drive-alert" style={{ width: '67%' }}>
|
||||
Selecting your system drive is dangerous and will erase your drive!
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{missingDriversModal.drive !== undefined && (
|
||||
<Modal
|
||||
|
@ -20,7 +20,7 @@ import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circl
|
||||
import * as _ from 'lodash';
|
||||
import outdent from 'outdent';
|
||||
import * as React from 'react';
|
||||
import { Flex, FlexProps, Link, Table, TableColumn, Txt } from 'rendition';
|
||||
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { progress } from '../../../../shared/messages';
|
||||
@ -31,7 +31,7 @@ import { getDrives } from '../../models/available-drives';
|
||||
import { resetState } from '../../models/flash-state';
|
||||
import * as selection from '../../models/selection-state';
|
||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
||||
import { Modal } from '../../styled-components';
|
||||
import { Modal, Table } from '../../styled-components';
|
||||
|
||||
const ErrorsTable = styled((props) => <Table<FlashError> {...props} />)`
|
||||
&&& [data-display='table-head'],
|
||||
@ -99,7 +99,7 @@ const columns: Array<TableColumn<FlashError>> = [
|
||||
field: 'message',
|
||||
label: 'Error',
|
||||
render: (message: string, { code }: FlashError) => {
|
||||
return message ? message : code;
|
||||
return message ?? code;
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -152,7 +152,7 @@ export function FlashResults({
|
||||
allFailed={allFailed}
|
||||
color={allFailed || someFailed ? '#c6c8c9' : '#1ac135'}
|
||||
/>
|
||||
<Txt>{middleEllipsis(image, 16)}</Txt>
|
||||
<Txt>{middleEllipsis(image, 24)}</Txt>
|
||||
</Flex>
|
||||
<Txt fontSize={24} color="#fff" mb="17px">
|
||||
Flash Complete!
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Alert as AlertBase,
|
||||
@ -23,27 +24,16 @@ import {
|
||||
ButtonProps,
|
||||
Modal as ModalBase,
|
||||
Provider,
|
||||
Table as BaseTable,
|
||||
TableProps as BaseTableProps,
|
||||
Txt,
|
||||
Theme as renditionTheme,
|
||||
} from 'rendition';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
import { colors, theme } from './theme';
|
||||
|
||||
const defaultTheme = {
|
||||
...renditionTheme,
|
||||
...theme,
|
||||
layer: {
|
||||
extend: () => `
|
||||
> div:first-child {
|
||||
background-color: transparent;
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
export const ThemedProvider = (props: any) => (
|
||||
<Provider theme={defaultTheme} {...props}></Provider>
|
||||
<Provider theme={theme} {...props}></Provider>
|
||||
);
|
||||
|
||||
export const BaseButton = styled(Button)`
|
||||
@ -134,41 +124,27 @@ const modalFooterShadowCss = css`
|
||||
background-attachment: local, local, scroll, scroll;
|
||||
`;
|
||||
|
||||
export const Modal = styled(({ style, ...props }) => {
|
||||
export const Modal = styled(({ style, children, ...props }) => {
|
||||
return (
|
||||
<Provider
|
||||
theme={{
|
||||
...defaultTheme,
|
||||
header: {
|
||||
height: '50px',
|
||||
},
|
||||
layer: {
|
||||
extend: () => `
|
||||
${defaultTheme.layer.extend()}
|
||||
|
||||
> div:last-child {
|
||||
top: 0;
|
||||
}
|
||||
`,
|
||||
<ModalBase
|
||||
position="top"
|
||||
width="97vw"
|
||||
cancelButtonProps={{
|
||||
style: {
|
||||
marginRight: '20px',
|
||||
border: 'solid 1px #2a506f',
|
||||
},
|
||||
}}
|
||||
style={{
|
||||
height: '87.5vh',
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ModalBase
|
||||
position="top"
|
||||
width="97vw"
|
||||
cancelButtonProps={{
|
||||
style: {
|
||||
marginRight: '20px',
|
||||
border: 'solid 1px #2a506f',
|
||||
},
|
||||
}}
|
||||
style={{
|
||||
height: '87.5vh',
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</Provider>
|
||||
<ScrollableFlex flexDirection="column" width="100%" height="90%">
|
||||
{...children}
|
||||
</ScrollableFlex>
|
||||
</ModalBase>
|
||||
);
|
||||
})`
|
||||
> div {
|
||||
@ -188,11 +164,8 @@ export const Modal = styled(({ style, ...props }) => {
|
||||
|
||||
> div:nth-child(2) {
|
||||
height: 61%;
|
||||
|
||||
> div:not(.system-drive-alert) {
|
||||
padding: 0 30px;
|
||||
${modalFooterShadowCss}
|
||||
}
|
||||
padding: 0 30px;
|
||||
${modalFooterShadowCss}
|
||||
}
|
||||
|
||||
> div:last-child {
|
||||
@ -249,3 +222,82 @@ export const Alert = styled((props) => (
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export interface GenericTableProps<T> extends BaseTableProps<T> {
|
||||
refFn: (t: BaseTable<T>) => void;
|
||||
multipleSelection: boolean;
|
||||
showWarnings?: boolean;
|
||||
}
|
||||
|
||||
const GenericTable: <T>(
|
||||
props: GenericTableProps<T>,
|
||||
) => React.ReactElement<GenericTableProps<T>> = <T extends {}>({
|
||||
refFn,
|
||||
...props
|
||||
}: GenericTableProps<T>) => (
|
||||
<div>
|
||||
<BaseTable<T> ref={refFn} {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
function StyledTable<T>() {
|
||||
return styled((props: GenericTableProps<T>) => (
|
||||
<GenericTable<T> {...props} />
|
||||
))`
|
||||
[data-display='table-head']
|
||||
> [data-display='table-row']
|
||||
> [data-display='table-cell'] {
|
||||
position: sticky;
|
||||
background-color: #f8f9fd;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
|
||||
input[type='checkbox'] + div {
|
||||
display: ${(props) => (props.multipleSelection ? 'flex' : 'none')};
|
||||
}
|
||||
}
|
||||
|
||||
[data-display='table-head'] > [data-display='table-row'],
|
||||
[data-display='table-body'] > [data-display='table-row'] {
|
||||
> [data-display='table-cell']:first-child {
|
||||
padding-left: 15px;
|
||||
width: 6%;
|
||||
}
|
||||
|
||||
> [data-display='table-cell']:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-display='table-body'] > [data-display='table-row'] {
|
||||
&:nth-of-type(2n) {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&[data-highlight='true'] {
|
||||
&.system {
|
||||
background-color: ${(props) =>
|
||||
props.showWarnings ? '#fff5e6' : '#e8f5fc'};
|
||||
}
|
||||
|
||||
> [data-display='table-cell']:first-child {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&& [data-display='table-row'] > [data-display='table-cell'] {
|
||||
padding: 6px 8px;
|
||||
color: #2a506f;
|
||||
}
|
||||
|
||||
input[type='checkbox'] + div {
|
||||
border-radius: ${(props) => (props.multipleSelection ? '4px' : '50%')};
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export const Table = <T extends {}>(props: GenericTableProps<T>) => {
|
||||
const TypedStyledFunctional = StyledTable<T>();
|
||||
return <TypedStyledFunctional {...props} />;
|
||||
};
|
||||
|
@ -115,4 +115,11 @@ export const theme = _.merge({}, Theme, {
|
||||
}
|
||||
`,
|
||||
},
|
||||
layer: {
|
||||
extend: () => `
|
||||
> div:first-child {
|
||||
background-color: transparent;
|
||||
}
|
||||
`,
|
||||
},
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user