Starting to add SVG components
This commit is contained in:
parent
57dbea8c40
commit
3378c68aed
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
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';
|
||||
|
||||
@ -13,7 +14,7 @@ class Render extends React.PureComponent {
|
||||
|
||||
svgContainer = React.createRef()
|
||||
|
||||
provideSVGData() {
|
||||
provideSVGData = () => {
|
||||
if (!this.svgContainer.current) {
|
||||
return;
|
||||
}
|
||||
@ -26,23 +27,14 @@ class Render extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.provideSVGData();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.provideSVGData();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { expr } = this.props;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Render:', this.constructor.name, expr);
|
||||
|
||||
// Demo rendering for now
|
||||
return <div className={ style.render } ref={ this.svgContainer }>
|
||||
<PlaceholderIcon />
|
||||
<SVG onReflow={ this.provideSVGData }>
|
||||
<Text>{ this.constructor.name } => { expr }</Text>
|
||||
</SVG>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
71
src/rendering/SVG/index.js
Normal file
71
src/rendering/SVG/index.js
Normal 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;
|
5
src/rendering/SVG/style.js
Normal file
5
src/rendering/SVG/style.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { white } from 'rendering/style';
|
||||
|
||||
export const image = {
|
||||
backgroundColor: white
|
||||
};
|
70
src/rendering/Text/index.js
Normal file
70
src/rendering/Text/index.js
Normal 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 }>“</tspan>
|
||||
<tspan>{ children }</tspan>
|
||||
<tspan style={ style.quote }>”</tspan>
|
||||
</>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { transform } = this.state;
|
||||
|
||||
const textProps = {
|
||||
style: style.text,
|
||||
transform,
|
||||
ref: this.textRef
|
||||
};
|
||||
|
||||
return <text { ...textProps }>
|
||||
{ this.renderContent() }
|
||||
</text>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Text;
|
8
src/rendering/Text/style.js
Normal file
8
src/rendering/Text/style.js
Normal 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
20
src/rendering/style.js
Normal 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
|
||||
};
|
Loading…
Reference in New Issue
Block a user