Starting to add SVG components

This commit is contained in:
Jeff Avallone 2019-01-26 11:02:45 -05:00
parent 57dbea8c40
commit 3378c68aed
6 changed files with 180 additions and 14 deletions

View File

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import PlaceholderIcon from 'react-feather/dist/icons/file-text'; import SVG from 'rendering/SVG';
import Text from 'rendering/Text';
import style from './style.module.css'; import style from './style.module.css';
@ -13,7 +14,7 @@ class Render extends React.PureComponent {
svgContainer = React.createRef() svgContainer = React.createRef()
provideSVGData() { provideSVGData = () => {
if (!this.svgContainer.current) { if (!this.svgContainer.current) {
return; return;
} }
@ -26,23 +27,14 @@ class Render extends React.PureComponent {
}); });
} }
componentDidMount() {
this.provideSVGData();
}
componentDidUpdate() {
this.provideSVGData();
}
render() { render() {
const { expr } = this.props; const { expr } = this.props;
// eslint-disable-next-line no-console
console.log('Render:', this.constructor.name, expr);
// Demo rendering for now // Demo rendering for now
return <div className={ style.render } ref={ this.svgContainer }> return <div className={ style.render } ref={ this.svgContainer }>
<PlaceholderIcon /> <SVG onReflow={ this.provideSVGData }>
<Text>{ this.constructor.name } =&gt; { expr }</Text>
</SVG>
</div>; </div>;
} }
} }

View File

@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import * as style from './style';
const namespaceProps = {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:cc': 'http://creativecommons.org/ns#',
'xmlns:rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
};
/* eslint-disable max-len */
const metadata = `<rdf:rdf>
<cc:license rdf:about="http://creativecommons.org/licenses/by/3.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"></cc:permits>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"></cc:permits>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"></cc:requires>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"></cc:requires>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"></cc:permits>
</cc:license>
</rdf:rdf>`;
/* eslint-enable max-len */
class SVG extends React.PureComponent {
static propTypes = {
onReflow: PropTypes.func,
children: PropTypes.node,
padding: PropTypes.number
}
static defaultProps = {
padding: 10
}
state = {
width: 0,
height: 0
}
handleReflow = box => {
const { padding } = this.props;
this.setState({
width: Math.round(box.width + 2 * padding),
height: Math.round(box.height + 2 * padding)
}, () => this.props.onReflow(this));
}
render() {
const { width, height } = this.state;
const { padding, children } = this.props;
const svgProps = {
width,
height,
viewBox: [0, 0, width, height].join(' '),
style: style.image,
...namespaceProps
};
return <svg { ...svgProps }>
<metadata dangerouslySetInnerHTML={{ __html: metadata }}></metadata>
<g transform={ `translate(${ padding } ${ padding })` }>
{ React.cloneElement(React.Children.only(children), {
onReflow: this.handleReflow
}) }
</g>
</svg>;
}
}
export default SVG;

View File

@ -0,0 +1,5 @@
import { white } from 'rendering/style';
export const image = {
backgroundColor: white
};

View File

@ -0,0 +1,70 @@
import React from 'react';
import PropTypes from 'prop-types';
import * as style from './style';
class Text extends React.PureComponent {
static propTypes = {
quoted: PropTypes.bool,
onReflow: PropTypes.func,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired
}
state = {
transform: ''
}
textRef = React.createRef()
componentDidMount() {
this.reflow();
}
componentDidUpdate() {
this.reflow();
}
reflow() {
const box = this.textRef.current.getBBox();
const transform = `translate(${ -box.x } ${ -box.y })`;
if (transform === this.state.transform) {
return; // No update required
}
this.setState({ transform }, () => this.props.onReflow(box));
}
renderContent() {
const { children, quoted } = this.props;
if (!quoted) {
return children;
}
return <>
<tspan style={ style.quote }>&ldquo;</tspan>
<tspan>{ children }</tspan>
<tspan style={ style.quote }>&rdquo;</tspan>
</>;
}
render() {
const { transform } = this.state;
const textProps = {
style: style.text,
transform,
ref: this.textRef
};
return <text { ...textProps }>
{ this.renderContent() }
</text>;
}
}
export default Text;

View File

@ -0,0 +1,8 @@
import { fontSize, fontFamily, grey } from 'rendering/style';
export const text = {
fontSize, fontFamily
};
export const quote = {
fill: grey
};

20
src/rendering/style.js Normal file
View File

@ -0,0 +1,20 @@
// Styles are in JS instead of CSS so they will be inlined as attributes
// instead of served as a CSS file. This is so styles are included in
// downloaded SVG files.
export const green = '#bada55';
export const brown = '#6b6659';
export const tan = '#cbcbba';
export const black = '#000';
export const grey = '#908c83';
export const white = '#fff';
export const blue = '#dae9e5';
export const fontFamily = 'Arial';
export const fontSize = '16px';
export const fontSizeSmall = '12px';
export const strokeBase = {
strokeWidth: '2px',
stroke: black
};