mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-29 22:26:32 +00:00
Resize modal to show content appropriately
Change-type: patch Changelog-entry: Resize modal to show content appropriately Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
This commit is contained in:
parent
5c5273bd6c
commit
630f6c691c
@ -120,10 +120,6 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
|||||||
</Txt>
|
</Txt>
|
||||||
}
|
}
|
||||||
done={() => toggleModal(false)}
|
done={() => toggleModal(false)}
|
||||||
style={{
|
|
||||||
width: 780,
|
|
||||||
height: 420,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column">
|
||||||
{_.map(settingsList, (setting: Setting, i: number) => {
|
{_.map(settingsList, (setting: Setting, i: number) => {
|
||||||
|
@ -52,6 +52,7 @@ import {
|
|||||||
Modal,
|
Modal,
|
||||||
StepButton,
|
StepButton,
|
||||||
StepNameButton,
|
StepNameButton,
|
||||||
|
ScrollableFlex,
|
||||||
} from '../../styled-components';
|
} from '../../styled-components';
|
||||||
import { colors } from '../../theme';
|
import { colors } from '../../theme';
|
||||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
||||||
@ -61,19 +62,24 @@ import ImageSvg from '../../../assets/image.svg';
|
|||||||
|
|
||||||
const recentUrlImagesKey = 'recentUrlImages';
|
const recentUrlImagesKey = 'recentUrlImages';
|
||||||
|
|
||||||
function normalizeRecentUrlImages(urls: any): string[] {
|
function normalizeRecentUrlImages(urls: any[]): URL[] {
|
||||||
if (!Array.isArray(urls)) {
|
if (!Array.isArray(urls)) {
|
||||||
urls = [];
|
urls = [];
|
||||||
}
|
}
|
||||||
return _.chain(urls)
|
urls = urls
|
||||||
.filter(_.isString)
|
.map((url) => {
|
||||||
.reject(_.isEmpty)
|
try {
|
||||||
.uniq()
|
return new URL(url);
|
||||||
.takeRight(5)
|
} catch (error) {
|
||||||
.value();
|
// Invalid URL, skip
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((url) => url !== undefined);
|
||||||
|
urls = _.uniqBy(urls, (url) => url.href);
|
||||||
|
return urls.slice(urls.length - 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecentUrlImages(): string[] {
|
function getRecentUrlImages(): URL[] {
|
||||||
let urls = [];
|
let urls = [];
|
||||||
try {
|
try {
|
||||||
urls = JSON.parse(localStorage.getItem(recentUrlImagesKey) || '[]');
|
urls = JSON.parse(localStorage.getItem(recentUrlImagesKey) || '[]');
|
||||||
@ -83,11 +89,9 @@ function getRecentUrlImages(): string[] {
|
|||||||
return normalizeRecentUrlImages(urls);
|
return normalizeRecentUrlImages(urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRecentUrlImages(urls: string[]) {
|
function setRecentUrlImages(urls: URL[]) {
|
||||||
localStorage.setItem(
|
const normalized = normalizeRecentUrlImages(urls.map((url: URL) => url.href));
|
||||||
recentUrlImagesKey,
|
localStorage.setItem(recentUrlImagesKey, JSON.stringify(normalized));
|
||||||
JSON.stringify(normalizeRecentUrlImages(urls)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Card = styled(BaseCard)`
|
const Card = styled(BaseCard)`
|
||||||
@ -124,13 +128,13 @@ const URLSelector = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [imageURL, setImageURL] = React.useState('');
|
const [imageURL, setImageURL] = React.useState('');
|
||||||
const [recentImages, setRecentImages]: [
|
const [recentImages, setRecentImages]: [
|
||||||
string[],
|
URL[],
|
||||||
(value: React.SetStateAction<string[]>) => void,
|
(value: React.SetStateAction<URL[]>) => void,
|
||||||
] = React.useState([]);
|
] = React.useState([]);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const fetchRecentUrlImages = async () => {
|
const fetchRecentUrlImages = async () => {
|
||||||
const recentUrlImages: string[] = await getRecentUrlImages();
|
const recentUrlImages: URL[] = await getRecentUrlImages();
|
||||||
setRecentImages(recentUrlImages);
|
setRecentImages(recentUrlImages);
|
||||||
};
|
};
|
||||||
fetchRecentUrlImages();
|
fetchRecentUrlImages();
|
||||||
@ -139,15 +143,16 @@ const URLSelector = ({
|
|||||||
<Modal
|
<Modal
|
||||||
cancel={cancel}
|
cancel={cancel}
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
disabled: loading || !imageURL,
|
className: loading || !imageURL ? 'disabled' : '',
|
||||||
}}
|
}}
|
||||||
done={async () => {
|
done={async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const sanitizedRecentUrls = normalizeRecentUrlImages([
|
const urlStrings = recentImages.map((url: URL) => url.href);
|
||||||
...recentImages,
|
const normalizedRecentUrls = normalizeRecentUrlImages([
|
||||||
|
...urlStrings,
|
||||||
imageURL,
|
imageURL,
|
||||||
]);
|
]);
|
||||||
setRecentUrlImages(sanitizedRecentUrls);
|
setRecentUrlImages(normalizedRecentUrls);
|
||||||
await done(imageURL);
|
await done(imageURL);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -164,24 +169,29 @@ const URLSelector = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
{!_.isEmpty(recentImages) && (
|
{recentImages.length > 0 && (
|
||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column" height="78.6%">
|
||||||
<Txt fontSize={18}>Recent</Txt>
|
<Txt fontSize={18}>Recent</Txt>
|
||||||
<Card
|
<ScrollableFlex flexDirection="column">
|
||||||
style={{ padding: '10px 15px' }}
|
<Card
|
||||||
rows={_.map(recentImages, (recent) => (
|
p="10px 15px"
|
||||||
<Txt
|
rows={recentImages
|
||||||
key={recent}
|
.map((recent) => (
|
||||||
onClick={() => {
|
<Txt
|
||||||
setImageURL(recent);
|
key={recent.href}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setImageURL(recent.href);
|
||||||
<span>
|
}}
|
||||||
{_.last(_.split(recent, '/'))} - {recent}
|
style={{
|
||||||
</span>
|
overflowWrap: 'break-word',
|
||||||
</Txt>
|
}}
|
||||||
))}
|
>
|
||||||
/>
|
{recent.pathname.split('/').pop()} - {recent.href}
|
||||||
|
</Txt>
|
||||||
|
))
|
||||||
|
.reverse()}
|
||||||
|
/>
|
||||||
|
</ScrollableFlex>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
@ -280,7 +290,7 @@ export class SourceSelector extends React.Component<
|
|||||||
|
|
||||||
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
||||||
const isURL =
|
const isURL =
|
||||||
_.startsWith(imagePath, 'https://') || _.startsWith(imagePath, 'http://');
|
imagePath.startsWith('https://') || imagePath.startsWith('http://');
|
||||||
await this.selectImageByPath({
|
await this.selectImageByPath({
|
||||||
imagePath,
|
imagePath,
|
||||||
SourceType: isURL ? sourceDestination.Http : sourceDestination.File,
|
SourceType: isURL ? sourceDestination.Http : sourceDestination.File,
|
||||||
@ -354,8 +364,8 @@ export class SourceSelector extends React.Component<
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
!_.startsWith(imagePath, 'https://') &&
|
!imagePath.startsWith('https://') &&
|
||||||
!_.startsWith(imagePath, 'http://')
|
!imagePath.startsWith('http://')
|
||||||
) {
|
) {
|
||||||
const invalidImageError = errors.createUserError({
|
const invalidImageError = errors.createUserError({
|
||||||
title: 'Unsupported protocol',
|
title: 'Unsupported protocol',
|
||||||
|
@ -50,7 +50,7 @@ import {
|
|||||||
import { store } from '../../models/store';
|
import { store } from '../../models/store';
|
||||||
import { logEvent, logException } from '../../modules/analytics';
|
import { logEvent, logException } from '../../modules/analytics';
|
||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||||
import { Modal } from '../../styled-components';
|
import { Modal, ScrollableFlex } from '../../styled-components';
|
||||||
|
|
||||||
import TargetSVGIcon from '../../../assets/tgt.svg';
|
import TargetSVGIcon from '../../../assets/tgt.svg';
|
||||||
|
|
||||||
@ -83,19 +83,6 @@ function isDrivelistDrive(
|
|||||||
return typeof (drive as scanner.adapters.DrivelistDrive).size === 'number';
|
return typeof (drive as scanner.adapters.DrivelistDrive).size === 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScrollableFlex = styled(Flex)`
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div > div {
|
|
||||||
/* This is required for the sticky table header in TargetsTable */
|
|
||||||
overflow-x: visible;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TargetsTable = styled(({ refFn, ...props }) => {
|
const TargetsTable = styled(({ refFn, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -376,10 +363,6 @@ export class TargetSelectorModal extends React.Component<
|
|||||||
cancel={cancel}
|
cancel={cancel}
|
||||||
done={() => done(selectedList)}
|
done={() => done(selectedList)}
|
||||||
action={`Select (${selectedList.length})`}
|
action={`Select (${selectedList.length})`}
|
||||||
style={{
|
|
||||||
width: '780px',
|
|
||||||
height: '420px',
|
|
||||||
}}
|
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
primary: !hasStatus,
|
primary: !hasStatus,
|
||||||
warning: hasStatus,
|
warning: hasStatus,
|
||||||
@ -387,7 +370,7 @@ export class TargetSelectorModal extends React.Component<
|
|||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Flex width="100%" height="100%">
|
<Flex width="100%" height="90%">
|
||||||
{!hasAvailableDrives() ? (
|
{!hasAvailableDrives() ? (
|
||||||
<Flex
|
<Flex
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@ -399,11 +382,7 @@ export class TargetSelectorModal extends React.Component<
|
|||||||
<b>Plug a target drive</b>
|
<b>Plug a target drive</b>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<ScrollableFlex
|
<ScrollableFlex flexDirection="column" width="100%">
|
||||||
flexDirection="column"
|
|
||||||
width="100%"
|
|
||||||
height="calc(100% - 15px)"
|
|
||||||
>
|
|
||||||
<TargetsTable
|
<TargetsTable
|
||||||
refFn={(t: Table<Target>) => {
|
refFn={(t: Table<Target>) => {
|
||||||
if (t !== null) {
|
if (t !== null) {
|
||||||
|
@ -63,5 +63,5 @@ button:focus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
opacity: 0.2;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,7 @@ export const DetailsText = (props: FlexProps) => (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Modal = styled((props) => {
|
export const Modal = styled(({ style, ...props }) => {
|
||||||
const { style = { height: 420 } } = props;
|
|
||||||
return (
|
return (
|
||||||
<Provider
|
<Provider
|
||||||
theme={{
|
theme={{
|
||||||
@ -143,23 +142,29 @@ export const Modal = styled((props) => {
|
|||||||
>
|
>
|
||||||
<ModalBase
|
<ModalBase
|
||||||
position="top"
|
position="top"
|
||||||
width={780}
|
width="96vw"
|
||||||
cancelButtonProps={{
|
cancelButtonProps={{
|
||||||
style: {
|
style: {
|
||||||
marginRight: '20px',
|
marginRight: '20px',
|
||||||
border: 'solid 1px #2a506f',
|
border: 'solid 1px #2a506f',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
style={style}
|
style={{
|
||||||
|
height: '86.5vh',
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
})`
|
})`
|
||||||
> div {
|
> div {
|
||||||
padding: 30px;
|
padding: 24px 30px;
|
||||||
height: calc(100% - 80px);
|
height: calc(100% - 80px);
|
||||||
overflow-y: auto;
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
> h3 {
|
> h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -178,3 +183,16 @@ export const Modal = styled((props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ScrollableFlex = styled(Flex)`
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div > div {
|
||||||
|
/* This is required for the sticky table header in TargetsTable */
|
||||||
|
overflow-x: visible;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user