diff --git a/lib/gui/app/components/target-selector/target-selector-modal.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx
similarity index 77%
rename from lib/gui/app/components/target-selector/target-selector-modal.tsx
rename to lib/gui/app/components/drive-selector/drive-selector.tsx
index 58b7d004..5523c96b 100644
--- a/lib/gui/app/components/target-selector/target-selector-modal.tsx
+++ b/lib/gui/app/components/drive-selector/drive-selector.tsx
@@ -33,7 +33,7 @@ import {
getDriveImageCompatibilityStatuses,
hasListDriveImageCompatibilityStatus,
isDriveValid,
- TargetStatus,
+ DriveStatus,
Image,
} from '../../../../shared/drive-constraints';
import { compatibility } from '../../../../shared/messages';
@@ -49,7 +49,7 @@ import { logEvent, logException } from '../../modules/analytics';
import { open as openExternal } from '../../os/open-external/services/open-external';
import { Modal, ScrollableFlex } from '../../styled-components';
-import TargetSVGIcon from '../../../assets/tgt.svg';
+import DriveSVGIcon from '../../../assets/tgt.svg';
interface UsbbootDrive extends sourceDestination.UsbbootDrive {
progress: number;
@@ -64,26 +64,26 @@ interface DriverlessDrive {
linkCTA: string;
}
-type Target = scanner.adapters.DrivelistDrive | DriverlessDrive | UsbbootDrive;
+type Drive = scanner.adapters.DrivelistDrive | DriverlessDrive | UsbbootDrive;
-function isUsbbootDrive(drive: Target): drive is UsbbootDrive {
+function isUsbbootDrive(drive: Drive): drive is UsbbootDrive {
return (drive as UsbbootDrive).progress !== undefined;
}
-function isDriverlessDrive(drive: Target): drive is DriverlessDrive {
+function isDriverlessDrive(drive: Drive): drive is DriverlessDrive {
return (drive as DriverlessDrive).link !== undefined;
}
function isDrivelistDrive(
- drive: Target,
+ drive: Drive,
): drive is scanner.adapters.DrivelistDrive {
return typeof (drive as scanner.adapters.DrivelistDrive).size === 'number';
}
-const TargetsTable = styled(({ refFn, ...props }) => {
+const DrivesTable = styled(({ refFn, ...props }) => {
return (
-
ref={refFn} {...props} />
+ ref={refFn} {...props} />
);
})`
@@ -145,30 +145,37 @@ const InitProgress = styled(
}
`;
-interface TargetSelectorModalProps extends Omit {
- done: (targets: scanner.adapters.DrivelistDrive[]) => void;
+export interface DriveSelectorProps
+ extends Omit {
+ multipleSelection?: boolean;
+ cancel: () => void;
+ done: (drives: scanner.adapters.DrivelistDrive[]) => void;
+ titleLabel: string;
+ emptyListLabel: string;
}
-interface TargetSelectorModalState {
- drives: Target[];
+interface DriveSelectorState {
+ drives: Drive[];
image: Image;
missingDriversModal: { drive?: DriverlessDrive };
selectedList: scanner.adapters.DrivelistDrive[];
showSystemDrives: boolean;
}
-export class TargetSelectorModal extends React.Component<
- TargetSelectorModalProps,
- TargetSelectorModalState
+export class DriveSelector extends React.Component<
+ DriveSelectorProps,
+ DriveSelectorState
> {
private unsubscribe: (() => void) | undefined;
- tableColumns: Array>;
+ multipleSelection: boolean;
+ tableColumns: Array>;
- constructor(props: TargetSelectorModalProps) {
+ constructor(props: DriveSelectorProps) {
super(props);
const defaultMissingDriversModalState: { drive?: DriverlessDrive } = {};
const selectedList = getSelectedDrives();
+ this.multipleSelection = !!this.props.multipleSelection;
this.state = {
drives: getDrives(),
@@ -182,7 +189,7 @@ export class TargetSelectorModal extends React.Component<
{
field: 'description',
label: 'Name',
- render: (description: string, drive: Target) => {
+ render: (description: string, drive: Drive) => {
return isDrivelistDrive(drive) && drive.isSystem ? (
@@ -197,7 +204,7 @@ export class TargetSelectorModal extends React.Component<
field: 'description',
key: 'size',
label: 'Size',
- render: (_description: string, drive: Target) => {
+ render: (_description: string, drive: Drive) => {
if (isDrivelistDrive(drive) && drive.size !== null) {
return bytesToClosestUnit(drive.size);
}
@@ -207,7 +214,7 @@ export class TargetSelectorModal extends React.Component<
field: 'description',
key: 'link',
label: 'Location',
- render: (_description: string, drive: Target) => {
+ render: (_description: string, drive: Drive) => {
return (
{drive.displayName}
@@ -231,7 +238,7 @@ export class TargetSelectorModal extends React.Component<
key: 'extra',
// Space as empty string would use the field name as label
label: ' ',
- render: (_description: string, drive: Target) => {
+ render: (_description: string, drive: Drive) => {
if (isUsbbootDrive(drive)) {
return this.renderProgress(drive.progress);
} else if (isDrivelistDrive(drive)) {
@@ -244,7 +251,7 @@ export class TargetSelectorModal extends React.Component<
];
}
- private driveShouldBeDisabled(drive: Target, image: any) {
+ private driveShouldBeDisabled(drive: Drive, image: any) {
return (
isUsbbootDrive(drive) ||
isDriverlessDrive(drive) ||
@@ -252,8 +259,8 @@ export class TargetSelectorModal extends React.Component<
);
}
- private getDisplayedTargets(targets: Target[]): Target[] {
- return targets.filter((drive) => {
+ private getDisplayedDrives(drives: Drive[]): Drive[] {
+ return drives.filter((drive) => {
return (
isUsbbootDrive(drive) ||
isDriverlessDrive(drive) ||
@@ -264,7 +271,7 @@ export class TargetSelectorModal extends React.Component<
});
}
- private getDisabledTargets(drives: Target[], image: any): string[] {
+ private getDisabledDrives(drives: Drive[], image: any): string[] {
return drives
.filter((drive) => this.driveShouldBeDisabled(drive, image))
.map((drive) => drive.displayName);
@@ -279,7 +286,7 @@ export class TargetSelectorModal extends React.Component<
);
}
- private renderStatuses(statuses: TargetStatus[]) {
+ private renderStatuses(statuses: DriveStatus[]) {
return (
// the column render fn expects a single Element
<>
@@ -324,12 +331,12 @@ export class TargetSelectorModal extends React.Component<
const { cancel, done, ...props } = this.props;
const { selectedList, drives, image, missingDriversModal } = this.state;
- const displayedTargets = this.getDisplayedTargets(drives);
- const disabledTargets = this.getDisabledTargets(drives, image);
+ const displayedDrives = this.getDisplayedDrives(drives);
+ const disabledDrives = this.getDisabledDrives(drives, image);
const numberOfSystemDrives = drives.filter(
(drive) => isDrivelistDrive(drive) && drive.isSystem,
).length;
- const numberOfDisplayedSystemDrives = displayedTargets.filter(
+ const numberOfDisplayedSystemDrives = displayedDrives.filter(
(drive) => isDrivelistDrive(drive) && drive.isSystem,
).length;
const numberOfHiddenSystemDrives =
@@ -341,7 +348,7 @@ export class TargetSelectorModal extends React.Component<
titleElement={
- Select target
+ {this.props.titleLabel}
-
- Plug a target drive
+
+ {this.props.emptyListLabel}
) : (
-
- ) => {
+
+ ) => {
if (t !== null) {
t.setRowSelection(selectedList);
}
}}
columns={this.tableColumns}
- data={displayedTargets}
- disabledRows={disabledTargets}
+ data={displayedDrives}
+ disabledRows={disabledDrives}
rowKey="displayName"
- onCheck={(rows: Target[]) => {
+ onCheck={(rows: Drive[]) => {
+ const newSelection = rows.filter(isDrivelistDrive);
+ if (this.multipleSelection) {
+ this.setState({
+ selectedList: newSelection,
+ });
+ return;
+ }
this.setState({
- selectedList: rows.filter(isDrivelistDrive),
+ selectedList: newSelection.slice(newSelection.length - 1),
});
}}
- onRowClick={(row: Target) => {
+ onRowClick={(row: Drive) => {
if (
!isDrivelistDrive(row) ||
this.driveShouldBeDisabled(row, image)
) {
return;
}
- const newList = [...selectedList];
- const selectedIndex = selectedList.findIndex(
- (target) => target.device === row.device,
- );
- if (selectedIndex === -1) {
- newList.push(row);
- } else {
- // Deselect if selected
- newList.splice(selectedIndex, 1);
+ if (this.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: newList,
+ selectedList: [row],
});
}}
/>
diff --git a/lib/gui/app/components/target-selector/target-selector-button.tsx b/lib/gui/app/components/target-selector/target-selector-button.tsx
index e6bd6424..f7abd371 100644
--- a/lib/gui/app/components/target-selector/target-selector-button.tsx
+++ b/lib/gui/app/components/target-selector/target-selector-button.tsx
@@ -67,7 +67,7 @@ function DriveCompatibilityWarning({
);
}
-export function TargetSelector(props: TargetSelectorProps) {
+export function TargetSelectorButton(props: TargetSelectorProps) {
const targets = getSelectedDrives();
if (targets.length === 1) {
diff --git a/lib/gui/app/pages/main/DriveSelector.tsx b/lib/gui/app/components/target-selector/target-selector.tsx
similarity index 89%
rename from lib/gui/app/pages/main/DriveSelector.tsx
rename to lib/gui/app/components/target-selector/target-selector.tsx
index f89e4d98..958e99f9 100644
--- a/lib/gui/app/pages/main/DriveSelector.tsx
+++ b/lib/gui/app/components/target-selector/target-selector.tsx
@@ -19,6 +19,10 @@ import * as React from 'react';
import { Flex } from 'rendition';
import { TargetSelector } from '../../components/target-selector/target-selector-button';
import { TargetSelectorModal } from '../../components/target-selector/target-selector-modal';
+import {
+ DriveSelector,
+ DriveSelectorProps,
+} from '../drive-selector/drive-selector';
import {
isDriveSelected,
getImage,
@@ -50,6 +54,16 @@ const getDriveSelectionStateSlice = () => ({
image: getImage(),
});
+export const TargetSelectorModal = (
+ props: Omit,
+) => (
+
+);
+
export const selectAllTargets = (
modalTargets: scanner.adapters.DrivelistDrive[],
) => {
@@ -79,17 +93,17 @@ export const selectAllTargets = (
});
};
-interface DriveSelectorProps {
+interface TargetSelectorProps {
disabled: boolean;
hasDrive: boolean;
flashing: boolean;
}
-export const DriveSelector = ({
+export const TargetSelector = ({
disabled,
hasDrive,
flashing,
-}: DriveSelectorProps) => {
+}: TargetSelectorProps) => {
// TODO: inject these from redux-connector
const [
{ showDrivesButton, driveListLabel, targets, image },
@@ -115,7 +129,7 @@ export const DriveSelector = ({
}}
/>
-
+ />
)}
);
diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx
index 24c6e9c6..b61853d4 100644
--- a/lib/gui/app/pages/main/Flash.tsx
+++ b/lib/gui/app/pages/main/Flash.tsx
@@ -24,7 +24,6 @@ import * as constraints from '../../../../shared/drive-constraints';
import * as messages from '../../../../shared/messages';
import { ProgressButton } from '../../components/progress-button/progress-button';
import { SourceOptions } from '../../components/source-selector/source-selector';
-import { TargetSelectorModal } from '../../components/target-selector/target-selector-modal';
import * as availableDrives from '../../models/available-drives';
import * as flashState from '../../models/flash-state';
import * as selection from '../../models/selection-state';
@@ -32,7 +31,10 @@ import * as analytics from '../../modules/analytics';
import { scanner as driveScanner } from '../../modules/drive-scanner';
import * as imageWriter from '../../modules/image-writer';
import * as notification from '../../os/notification';
-import { selectAllTargets } from './DriveSelector';
+import {
+ selectAllTargets,
+ TargetSelectorModal,
+} from '../../components/target-selector/target-selector';
import FlashSvg from '../../../assets/flash.svg';
@@ -333,7 +335,7 @@ export class FlashStep extends React.PureComponent<
selectAllTargets(modalTargets);
this.setState({ showDriveSelectorModal: false });
}}
- >
+ />
)}
>
);
diff --git a/lib/gui/app/pages/main/MainPage.tsx b/lib/gui/app/pages/main/MainPage.tsx
index d4577175..e2306540 100644
--- a/lib/gui/app/pages/main/MainPage.tsx
+++ b/lib/gui/app/pages/main/MainPage.tsx
@@ -44,7 +44,10 @@ import {
import { bytesToClosestUnit } from '../../../../shared/units';
-import { DriveSelector, getDriveListLabel } from './DriveSelector';
+import {
+ TargetSelector,
+ getDriveListLabel,
+} from '../../components/target-selector/target-selector';
import { FlashStep } from './Flash';
import EtcherSvg from '../../../assets/etcher.svg';
@@ -252,7 +255,7 @@ export class MainPage extends React.Component<
-