diff --git a/lib/gui/app/components/drive-selector/drive-selector.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx index 8bd3daef..7cb5196a 100644 --- a/lib/gui/app/components/drive-selector/drive-selector.tsx +++ b/lib/gui/app/components/drive-selector/drive-selector.tsx @@ -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 }) => ( -
- ref={refFn} {...props} /> -
+const DrivesTable = styled((props: GenericTableProps) => ( + {...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} > - - {!hasAvailableDrives() ? ( - - - {this.props.emptyListLabel} - - ) : ( - - ) => { - 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() ? ( + + + {this.props.emptyListLabel} + + ) : ( + <> + { + 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 && ( - this.setState({ showSystemDrives: true })} - > - - - Show {numberOfHiddenSystemDrives} hidden - - - )} - - )} - {this.props.showWarnings && hasSystemDrives ? ( - - Selecting your system drive is dangerous and will erase your - drive! - - ) : null} - + return; + } + this.setState({ + selectedList: [row], + }); + }} + /> + {numberOfHiddenSystemDrives > 0 && ( + this.setState({ showSystemDrives: true })} + > + + + Show {numberOfHiddenSystemDrives} hidden + + + )} + + )} + {this.props.showWarnings && hasSystemDrives ? ( + + Selecting your system drive is dangerous and will erase your drive! + + ) : null} {missingDriversModal.drive !== undefined && ( {...props} />)` &&& [data-display='table-head'], @@ -99,7 +99,7 @@ const columns: Array> = [ 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'} /> - {middleEllipsis(image, 16)} + {middleEllipsis(image, 24)} Flash Complete! diff --git a/lib/gui/app/styled-components.tsx b/lib/gui/app/styled-components.tsx index 7ecd0487..2b5f8547 100644 --- a/lib/gui/app/styled-components.tsx +++ b/lib/gui/app/styled-components.tsx @@ -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) => ( - + ); 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 ( - ` - ${defaultTheme.layer.extend()} - - > div:last-child { - top: 0; - } - `, + - - + + {...children} + + ); })` > 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 extends BaseTableProps { + refFn: (t: BaseTable) => void; + multipleSelection: boolean; + showWarnings?: boolean; +} + +const GenericTable: ( + props: GenericTableProps, +) => React.ReactElement> = ({ + refFn, + ...props +}: GenericTableProps) => ( +
+ ref={refFn} {...props} /> +
+); + +function StyledTable() { + return styled((props: GenericTableProps) => ( + {...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 = (props: GenericTableProps) => { + const TypedStyledFunctional = StyledTable(); + return ; +}; diff --git a/lib/gui/app/theme.ts b/lib/gui/app/theme.ts index 6c30e43e..d45b45c9 100644 --- a/lib/gui/app/theme.ts +++ b/lib/gui/app/theme.ts @@ -115,4 +115,11 @@ export const theme = _.merge({}, Theme, { } `, }, + layer: { + extend: () => ` + > div:first-child { + background-color: transparent; + } + `, + }, });