mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 07:17:18 +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 { FlashAnother } from '../flash-another/flash-another';
|
||||
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 {
|
||||
|
@ -46,7 +46,7 @@ const {
|
||||
Modal
|
||||
} = require('rendition')
|
||||
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')
|
||||
|
||||
// TODO move these styles to rendition
|
||||
|
@ -20,7 +20,8 @@ const React = require('react')
|
||||
const propTypes = require('prop-types')
|
||||
const styled = require('styled-components').default
|
||||
const { color } = require('styled-system')
|
||||
const SvgIcon = require('../svg-icon/svg-icon.jsx')
|
||||
|
||||
const { SVGIcon } = require('../svg-icon/svg-icon')
|
||||
|
||||
const Div = styled.div `
|
||||
position: absolute;
|
||||
@ -57,13 +58,13 @@ const ReducedFlashingInfos = (props) => {
|
||||
return (props.shouldShow) ? (
|
||||
<Div>
|
||||
<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 color='#7e8085'>{ props.imageSize }</Span>
|
||||
</Span>
|
||||
|
||||
<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>
|
||||
</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 DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.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 settings from '../../models/settings';
|
||||
import { observe, store } from '../../models/store';
|
||||
@ -105,7 +105,7 @@ export const DriveSelector = ({
|
||||
)}
|
||||
|
||||
<div className="center-block">
|
||||
<SvgIcon paths={['../../assets/drive.svg']} disabled={disabled} />
|
||||
<SVGIcon paths={['../../assets/drive.svg']} disabled={disabled} />
|
||||
</div>
|
||||
|
||||
<div className="space-vertical-large">
|
||||
|
@ -22,7 +22,7 @@ import * as constraints from '../../../../shared/drive-constraints';
|
||||
import * as messages from '../../../../shared/messages';
|
||||
import * as DriveSelectorModal from '../../components/drive-selector/DriveSelectorModal.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 flashState from '../../models/flash-state';
|
||||
import * as selection from '../../models/selection-state';
|
||||
@ -222,7 +222,7 @@ export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => {
|
||||
<React.Fragment>
|
||||
<div className="box text-center">
|
||||
<div className="center-block">
|
||||
<SvgIcon
|
||||
<SVGIcon
|
||||
paths={['../../assets/flash.svg']}
|
||||
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 SafeWebview from '../../components/safe-webview/safe-webview';
|
||||
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 selectionState from '../../models/selection-state';
|
||||
import * as settings from '../../models/settings';
|
||||
@ -139,11 +139,11 @@ export class MainPage extends React.Component<
|
||||
}
|
||||
tabIndex={100}
|
||||
>
|
||||
<SvgIcon
|
||||
<SVGIcon
|
||||
paths={['../../assets/etcher.svg']}
|
||||
width="123px"
|
||||
height="22px"
|
||||
></SvgIcon>
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span
|
||||
|
Loading…
x
Reference in New Issue
Block a user