mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 11:16:39 +00:00
Convert svg-icon.jsx to typescript
Change-type: patch
This commit is contained in:
parent
c535543922
commit
b5f175d220
@ -27,7 +27,7 @@ import { updateLock } from '../../modules/update-lock';
|
|||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||||
import { FlashAnother } from '../flash-another/flash-another';
|
import { FlashAnother } from '../flash-another/flash-another';
|
||||||
import { FlashResults } from '../flash-results/flash-results';
|
import { FlashResults } from '../flash-results/flash-results';
|
||||||
import * as SVGIcon from '../svg-icon/svg-icon';
|
import { SVGIcon } from '../svg-icon/svg-icon';
|
||||||
|
|
||||||
const restart = (options: any, goToMain: () => void) => {
|
const restart = (options: any, goToMain: () => void) => {
|
||||||
const {
|
const {
|
||||||
|
@ -46,7 +46,7 @@ const {
|
|||||||
Modal
|
Modal
|
||||||
} = require('rendition')
|
} = require('rendition')
|
||||||
const { middleEllipsis } = require('../../utils/middle-ellipsis')
|
const { middleEllipsis } = require('../../utils/middle-ellipsis')
|
||||||
const SVGIcon = require('../svg-icon/svg-icon.jsx')
|
const { SVGIcon } = require('../svg-icon/svg-icon')
|
||||||
const { default: styled } = require('styled-components')
|
const { default: styled } = require('styled-components')
|
||||||
|
|
||||||
// TODO move these styles to rendition
|
// TODO move these styles to rendition
|
||||||
|
@ -20,7 +20,8 @@ const React = require('react')
|
|||||||
const propTypes = require('prop-types')
|
const propTypes = require('prop-types')
|
||||||
const styled = require('styled-components').default
|
const styled = require('styled-components').default
|
||||||
const { color } = require('styled-system')
|
const { color } = require('styled-system')
|
||||||
const SvgIcon = require('../svg-icon/svg-icon.jsx')
|
|
||||||
|
const { SVGIcon } = require('../svg-icon/svg-icon')
|
||||||
|
|
||||||
const Div = styled.div `
|
const Div = styled.div `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -57,13 +58,13 @@ const ReducedFlashingInfos = (props) => {
|
|||||||
return (props.shouldShow) ? (
|
return (props.shouldShow) ? (
|
||||||
<Div>
|
<Div>
|
||||||
<Span className="step-name">
|
<Span className="step-name">
|
||||||
<SvgIcon disabled contents={[ props.imageLogo ]} paths={[ '../../assets/image.svg' ]} width='20px'></SvgIcon>
|
<SVGIcon disabled contents={[ props.imageLogo ]} paths={[ '../../assets/image.svg' ]} width='20px'></SVGIcon>
|
||||||
<Span>{ props.imageName }</Span>
|
<Span>{ props.imageName }</Span>
|
||||||
<Span color='#7e8085'>{ props.imageSize }</Span>
|
<Span color='#7e8085'>{ props.imageSize }</Span>
|
||||||
</Span>
|
</Span>
|
||||||
|
|
||||||
<Span className="step-name">
|
<Span className="step-name">
|
||||||
<SvgIcon disabled paths={[ '../../assets/drive.svg' ]} width='20px'></SvgIcon>
|
<SVGIcon disabled paths={[ '../../assets/drive.svg' ]} width='20px'></SVGIcon>
|
||||||
<Span>{ props.driveTitle }</Span>
|
<Span>{ props.driveTitle }</Span>
|
||||||
</Span>
|
</Span>
|
||||||
</Div>
|
</Div>
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 balena.io
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module Etcher.Components.SVGIcon
|
|
||||||
*/
|
|
||||||
|
|
||||||
const _ = require('lodash')
|
|
||||||
const react = require('react')
|
|
||||||
const propTypes = require('prop-types')
|
|
||||||
const path = require('path')
|
|
||||||
const fs = require('fs')
|
|
||||||
const analytics = require('../../modules/analytics')
|
|
||||||
const domParser = new window.DOMParser()
|
|
||||||
|
|
||||||
const DEFAULT_SIZE = '40px'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Try to parse SVG contents and return it data encoded
|
|
||||||
*
|
|
||||||
* @param {String} contents - SVG XML contents
|
|
||||||
* @returns {String|null}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const encodedSVG = tryParseSVGContents('<svg><path></path></svg>')
|
|
||||||
*
|
|
||||||
* img.src = encodedSVG
|
|
||||||
*/
|
|
||||||
const tryParseSVGContents = (contents) => {
|
|
||||||
const doc = domParser.parseFromString(contents, 'image/svg+xml')
|
|
||||||
const parserError = doc.querySelector('parsererror')
|
|
||||||
const svg = doc.querySelector('svg')
|
|
||||||
|
|
||||||
if (!parserError && svg) {
|
|
||||||
return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-disable jsdoc/require-example */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary SVG element that takes both filepaths and file contents
|
|
||||||
* @type {Object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
class SVGIcon extends react.Component {
|
|
||||||
/**
|
|
||||||
* @summary Render the SVG
|
|
||||||
* @returns {react.Element}
|
|
||||||
*/
|
|
||||||
render () {
|
|
||||||
// __dirname behaves strangely inside a Webpack bundle,
|
|
||||||
// so we need to provide different base directories
|
|
||||||
// depending on whether __dirname is absolute or not,
|
|
||||||
// which helps detecting a Webpack bundle.
|
|
||||||
// We use global.__dirname inside a Webpack bundle since
|
|
||||||
// that's the only way to get the "real" __dirname.
|
|
||||||
const baseDirectory = path.isAbsolute(__dirname)
|
|
||||||
? path.join(__dirname, '..')
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
: global.__dirname
|
|
||||||
|
|
||||||
let svgData = ''
|
|
||||||
|
|
||||||
_.find(this.props.contents, (content) => {
|
|
||||||
const attempt = tryParseSVGContents(content)
|
|
||||||
|
|
||||||
if (attempt) {
|
|
||||||
svgData = attempt
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!svgData) {
|
|
||||||
_.find(this.props.paths, (relativePath) => {
|
|
||||||
// This means the path to the icon should be
|
|
||||||
// relative to *this directory*.
|
|
||||||
// TODO: There might be a way to compute the path
|
|
||||||
// relatively to the `index.html`.
|
|
||||||
const imagePath = path.join(baseDirectory, 'assets', relativePath)
|
|
||||||
|
|
||||||
const contents = _.attempt(() => {
|
|
||||||
return fs.readFileSync(imagePath, {
|
|
||||||
encoding: 'utf8'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if (_.isError(contents)) {
|
|
||||||
analytics.logException(contents)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsed = _.attempt(tryParseSVGContents, contents)
|
|
||||||
|
|
||||||
if (parsed) {
|
|
||||||
svgData = parsed
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = this.props.width || DEFAULT_SIZE
|
|
||||||
const height = this.props.height || DEFAULT_SIZE
|
|
||||||
|
|
||||||
return react.createElement('img', {
|
|
||||||
className: 'svg-icon',
|
|
||||||
style: {
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
},
|
|
||||||
src: svgData,
|
|
||||||
disabled: this.props.disabled
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SVGIcon.propTypes = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Paths to SVG files to be tried in succession if any fails
|
|
||||||
*/
|
|
||||||
paths: propTypes.array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary List of embedded SVG contents to be tried in succession if any fails
|
|
||||||
*/
|
|
||||||
contents: propTypes.array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary SVG image width unit
|
|
||||||
*/
|
|
||||||
width: propTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary SVG image height unit
|
|
||||||
*/
|
|
||||||
height: propTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Should the element visually appear grayed out and disabled?
|
|
||||||
*/
|
|
||||||
disabled: propTypes.bool
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = SVGIcon
|
|
142
lib/gui/app/components/svg-icon/svg-icon.tsx
Normal file
142
lib/gui/app/components/svg-icon/svg-icon.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 balena.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import * as analytics from '../../modules/analytics';
|
||||||
|
|
||||||
|
const domParser = new window.DOMParser();
|
||||||
|
|
||||||
|
const DEFAULT_SIZE = '40px';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Try to parse SVG contents and return it data encoded
|
||||||
|
*
|
||||||
|
* @param {String} contents - SVG XML contents
|
||||||
|
* @returns {String|null}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const encodedSVG = tryParseSVGContents('<svg><path></path></svg>')
|
||||||
|
*
|
||||||
|
* img.src = encodedSVG
|
||||||
|
*/
|
||||||
|
function tryParseSVGContents(contents: string) {
|
||||||
|
const doc = domParser.parseFromString(contents, 'image/svg+xml');
|
||||||
|
const parserError = doc.querySelector('parsererror');
|
||||||
|
const svg = doc.querySelector('svg');
|
||||||
|
|
||||||
|
if (!parserError && svg) {
|
||||||
|
return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SVGIconProps {
|
||||||
|
// Paths to SVG files to be tried in succession if any fails
|
||||||
|
paths: string[];
|
||||||
|
// List of embedded SVG contents to be tried in succession if any fails
|
||||||
|
contents?: string[];
|
||||||
|
// SVG image width unit
|
||||||
|
width?: string;
|
||||||
|
// SVG image height unit
|
||||||
|
height?: string;
|
||||||
|
// Should the element visually appear grayed out and disabled?
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary SVG element that takes both filepaths and file contents
|
||||||
|
*/
|
||||||
|
export class SVGIcon extends React.Component<SVGIconProps> {
|
||||||
|
public render() {
|
||||||
|
// __dirname behaves strangely inside a Webpack bundle,
|
||||||
|
// so we need to provide different base directories
|
||||||
|
// depending on whether __dirname is absolute or not,
|
||||||
|
// which helps detecting a Webpack bundle.
|
||||||
|
// We use global.__dirname inside a Webpack bundle since
|
||||||
|
// that's the only way to get the "real" __dirname.
|
||||||
|
let baseDirectory: string;
|
||||||
|
if (path.isAbsolute(__dirname)) {
|
||||||
|
baseDirectory = path.join(__dirname, '..');
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
baseDirectory = global.__dirname;
|
||||||
|
}
|
||||||
|
|
||||||
|
let svgData = '';
|
||||||
|
|
||||||
|
_.find(this.props.contents, content => {
|
||||||
|
const attempt = tryParseSVGContents(content);
|
||||||
|
|
||||||
|
if (attempt) {
|
||||||
|
svgData = attempt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!svgData) {
|
||||||
|
_.find(this.props.paths, relativePath => {
|
||||||
|
// This means the path to the icon should be
|
||||||
|
// relative to *this directory*.
|
||||||
|
// TODO: There might be a way to compute the path
|
||||||
|
// relatively to the `index.html`.
|
||||||
|
const imagePath = path.join(baseDirectory, 'assets', relativePath);
|
||||||
|
|
||||||
|
const contents = _.attempt(() => {
|
||||||
|
return fs.readFileSync(imagePath, {
|
||||||
|
encoding: 'utf8',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_.isError(contents)) {
|
||||||
|
analytics.logException(contents);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = tryParseSVGContents(contents);
|
||||||
|
|
||||||
|
if (parsed) {
|
||||||
|
svgData = parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const width = this.props.width || DEFAULT_SIZE;
|
||||||
|
const height = this.props.height || DEFAULT_SIZE;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className="svg-icon"
|
||||||
|
style={{
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}}
|
||||||
|
src={svgData}
|
||||||
|
// @ts-ignore
|
||||||
|
disabled={this.props.disabled}
|
||||||
|
></img>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ import styled from 'styled-components';
|
|||||||
import * as driveConstraints from '../../../../shared/drive-constraints';
|
import * as driveConstraints from '../../../../shared/drive-constraints';
|
||||||
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
|
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
|
||||||
import * as TargetSelector from '../../components/drive-selector/target-selector.jsx';
|
import * as TargetSelector from '../../components/drive-selector/target-selector.jsx';
|
||||||
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
import { SVGIcon } from '../../components/svg-icon/svg-icon';
|
||||||
import * as selectionState from '../../models/selection-state';
|
import * as selectionState from '../../models/selection-state';
|
||||||
import * as settings from '../../models/settings';
|
import * as settings from '../../models/settings';
|
||||||
import { observe, store } from '../../models/store';
|
import { observe, store } from '../../models/store';
|
||||||
@ -105,7 +105,7 @@ export const DriveSelector = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="center-block">
|
<div className="center-block">
|
||||||
<SvgIcon paths={['../../assets/drive.svg']} disabled={disabled} />
|
<SVGIcon paths={['../../assets/drive.svg']} disabled={disabled} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-vertical-large">
|
<div className="space-vertical-large">
|
||||||
|
@ -22,7 +22,7 @@ import * as constraints from '../../../../shared/drive-constraints';
|
|||||||
import * as messages from '../../../../shared/messages';
|
import * as messages from '../../../../shared/messages';
|
||||||
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
|
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.jsx';
|
||||||
import * as ProgressButton from '../../components/progress-button/progress-button.jsx';
|
import * as ProgressButton from '../../components/progress-button/progress-button.jsx';
|
||||||
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
import { SVGIcon } from '../../components/svg-icon/svg-icon';
|
||||||
import * as availableDrives from '../../models/available-drives';
|
import * as availableDrives from '../../models/available-drives';
|
||||||
import * as flashState from '../../models/flash-state';
|
import * as flashState from '../../models/flash-state';
|
||||||
import * as selection from '../../models/selection-state';
|
import * as selection from '../../models/selection-state';
|
||||||
@ -222,7 +222,7 @@ export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="box text-center">
|
<div className="box text-center">
|
||||||
<div className="center-block">
|
<div className="center-block">
|
||||||
<SvgIcon
|
<SVGIcon
|
||||||
paths={['../../assets/flash.svg']}
|
paths={['../../assets/flash.svg']}
|
||||||
disabled={shouldFlashStepBeDisabled}
|
disabled={shouldFlashStepBeDisabled}
|
||||||
/>
|
/>
|
||||||
|
@ -26,7 +26,7 @@ import * as ImageSelector from '../../components/image-selector/image-selector';
|
|||||||
import * as ReducedFlashingInfos from '../../components/reduced-flashing-infos/reduced-flashing-infos';
|
import * as ReducedFlashingInfos from '../../components/reduced-flashing-infos/reduced-flashing-infos';
|
||||||
import * as SafeWebview from '../../components/safe-webview/safe-webview';
|
import * as SafeWebview from '../../components/safe-webview/safe-webview';
|
||||||
import { SettingsModal } from '../../components/settings/settings';
|
import { SettingsModal } from '../../components/settings/settings';
|
||||||
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
import { SVGIcon } from '../../components/svg-icon/svg-icon';
|
||||||
import * as flashState from '../../models/flash-state';
|
import * as flashState from '../../models/flash-state';
|
||||||
import * as selectionState from '../../models/selection-state';
|
import * as selectionState from '../../models/selection-state';
|
||||||
import * as settings from '../../models/settings';
|
import * as settings from '../../models/settings';
|
||||||
@ -139,11 +139,11 @@ export class MainPage extends React.Component<
|
|||||||
}
|
}
|
||||||
tabIndex={100}
|
tabIndex={100}
|
||||||
>
|
>
|
||||||
<SvgIcon
|
<SVGIcon
|
||||||
paths={['../../assets/etcher.svg']}
|
paths={['../../assets/etcher.svg']}
|
||||||
width="123px"
|
width="123px"
|
||||||
height="22px"
|
height="22px"
|
||||||
></SvgIcon>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
|
Loading…
x
Reference in New Issue
Block a user