Moving app state management code into App context
This commit is contained in:
parent
1f5da0c690
commit
786cd06cd9
@ -1,5 +1,149 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import URLSearchParams from '@ungap/url-search-params';
|
||||||
|
|
||||||
const AppContext = React.createContext();
|
const AppContext = React.createContext();
|
||||||
|
|
||||||
|
const toUrl = params => new URLSearchParams(params).toString();
|
||||||
|
|
||||||
|
const readURLHash = () => {
|
||||||
|
const query = document.location.hash.slice(1);
|
||||||
|
const params = new URLSearchParams(query);
|
||||||
|
|
||||||
|
if (params.get('syntax')) {
|
||||||
|
return {
|
||||||
|
syntax: params.get('syntax'),
|
||||||
|
expr: params.get('expr')
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Assuming old-style URL
|
||||||
|
return {
|
||||||
|
syntax: 'js',
|
||||||
|
expr: query || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSvgLink = async ({ svg }) => {
|
||||||
|
try {
|
||||||
|
const type = 'image/svg+xml';
|
||||||
|
const blob = new Blob([svg], { type });
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: URL.createObjectURL(blob),
|
||||||
|
label: 'Download SVG',
|
||||||
|
filename: 'image.svg',
|
||||||
|
type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createPngLink = async ({ svg, width, height }) => {
|
||||||
|
try {
|
||||||
|
const type = 'image/png';
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
const loader = new Image();
|
||||||
|
|
||||||
|
loader.width = canvas.width = Number(width) * 2;
|
||||||
|
loader.height = canvas.height = Number(height) * 2;
|
||||||
|
|
||||||
|
await new Promise(resolve => {
|
||||||
|
loader.onload = resolve;
|
||||||
|
loader.src = 'data:image/svg+xml,' + encodeURIComponent(svg);
|
||||||
|
});
|
||||||
|
|
||||||
|
context.drawImage(loader, 0, 0, loader.width, loader.height);
|
||||||
|
|
||||||
|
const blob = await new Promise(resolve => canvas.toBlob(resolve, type));
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: URL.createObjectURL(blob),
|
||||||
|
label: 'Download PNG',
|
||||||
|
filename: 'image.png',
|
||||||
|
type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppContextProvider extends React.PureComponent {
|
||||||
|
state = {}
|
||||||
|
|
||||||
|
mutations = {
|
||||||
|
svgData: async ({ svg, width, height }) => {
|
||||||
|
if (svg !== this.state.svg) {
|
||||||
|
this.setState({
|
||||||
|
svg,
|
||||||
|
svgLink: await createSvgLink({ svg }),
|
||||||
|
pngLink: await createPngLink({ svg, width, height })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
renderExpr: ({ syntax, expr }) => {
|
||||||
|
if (expr) {
|
||||||
|
document.location.hash = toUrl({ syntax, expr });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// Gatsby doesn't have document.location, so readURLHash can't be called
|
||||||
|
// until here
|
||||||
|
this.setState(readURLHash());
|
||||||
|
|
||||||
|
window.addEventListener('hashchange', this.handleHashChange);
|
||||||
|
this.handleHashChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('hashchange', this.handleHashChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHashChange = () => {
|
||||||
|
const { expr, syntax } = readURLHash();
|
||||||
|
|
||||||
|
if (!expr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
syntax,
|
||||||
|
expr,
|
||||||
|
permalinkUrl: document.location.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.props.onExpressionChange) {
|
||||||
|
this.props.onExpressionChange({ syntax, expr });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children } = this.props;
|
||||||
|
const context = {
|
||||||
|
...this.state,
|
||||||
|
...this.mutations
|
||||||
|
};
|
||||||
|
|
||||||
|
return <AppContext.Provider value={ context }>
|
||||||
|
{ children }
|
||||||
|
</AppContext.Provider>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppContextProvider.propTypes = {
|
||||||
|
onExpressionChange: PropTypes.func,
|
||||||
|
children: PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
|
PropTypes.node
|
||||||
|
]).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export { AppContextProvider };
|
||||||
export default AppContext;
|
export default AppContext;
|
||||||
|
@ -1,151 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import URLSearchParams from '@ungap/url-search-params';
|
|
||||||
|
|
||||||
import AppContext from 'components/App/context';
|
import { AppContextProvider } from 'components/App/context';
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import Message from 'components/Message';
|
import Message from 'components/Message';
|
||||||
import SVG from 'components/SVG';
|
import SVG from 'components/SVG';
|
||||||
|
|
||||||
const toUrl = params => new URLSearchParams(params).toString();
|
|
||||||
|
|
||||||
const readURLHash = () => {
|
|
||||||
const query = document.location.hash.slice(1);
|
|
||||||
const params = new URLSearchParams(query);
|
|
||||||
|
|
||||||
if (params.get('syntax')) {
|
|
||||||
return {
|
|
||||||
syntax: params.get('syntax'),
|
|
||||||
expr: params.get('expr')
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Assuming old-style URL
|
|
||||||
return {
|
|
||||||
syntax: 'js',
|
|
||||||
expr: query || ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createSvgLink = async ({ svg }) => {
|
|
||||||
try {
|
|
||||||
const type = 'image/svg+xml';
|
|
||||||
const blob = new Blob([svg], { type });
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: URL.createObjectURL(blob),
|
|
||||||
label: 'Download SVG',
|
|
||||||
filename: 'image.svg',
|
|
||||||
type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.error(e); // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createPngLink = async ({ svg, width, height }) => {
|
|
||||||
try {
|
|
||||||
const type = 'image/png';
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const context = canvas.getContext('2d');
|
|
||||||
const loader = new Image();
|
|
||||||
|
|
||||||
loader.width = canvas.width = Number(width) * 2;
|
|
||||||
loader.height = canvas.height = Number(height) * 2;
|
|
||||||
|
|
||||||
await new Promise(resolve => {
|
|
||||||
loader.onload = resolve;
|
|
||||||
loader.src = 'data:image/svg+xml,' + encodeURIComponent(svg);
|
|
||||||
});
|
|
||||||
|
|
||||||
context.drawImage(loader, 0, 0, loader.width, loader.height);
|
|
||||||
|
|
||||||
const blob = await new Promise(resolve => canvas.toBlob(resolve, type));
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: URL.createObjectURL(blob),
|
|
||||||
label: 'Download PNG',
|
|
||||||
filename: 'image.png',
|
|
||||||
type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.error(e); // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class App extends React.PureComponent {
|
class App extends React.PureComponent {
|
||||||
state={}
|
state={}
|
||||||
|
|
||||||
componentDidMount() {
|
handleRender = ({ syntax, expr }) => {
|
||||||
// Gatsby doesn't have document.location, so readURLHash can't be called
|
|
||||||
// until here
|
|
||||||
this.setState(readURLHash());
|
|
||||||
|
|
||||||
window.addEventListener('hashchange', this.handleHashChange);
|
|
||||||
this.handleHashChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
window.removeEventListener('hashchange', this.handleHashChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleHashChange = () => {
|
|
||||||
const { expr, syntax } = readURLHash();
|
|
||||||
|
|
||||||
if (!expr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
syntax,
|
|
||||||
expr,
|
|
||||||
permalinkUrl: document.location.toString()
|
|
||||||
});
|
|
||||||
console.log('Render:', syntax, expr); // eslint-disable-line no-console
|
console.log('Render:', syntax, expr); // eslint-disable-line no-console
|
||||||
|
this.setState({ syntax, expr });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRetry = event => {
|
handleRetry = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.handleHashChange();
|
this.handleRender(this.state);
|
||||||
}
|
|
||||||
|
|
||||||
renderExpr = ({ expr, syntax }) => {
|
|
||||||
if (expr) {
|
|
||||||
document.location.hash = toUrl({ syntax, expr });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svgData = async ({ svg, width, height }) => {
|
|
||||||
if (svg !== this.state.svg) {
|
|
||||||
this.setState({
|
|
||||||
svg,
|
|
||||||
svgLink: await createSvgLink({ svg }),
|
|
||||||
pngLink: await createPngLink({ svg, width, height })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
return <AppContextProvider onExpressionChange={ this.handleRender }>
|
||||||
svgLink,
|
|
||||||
pngLink,
|
|
||||||
permalinkUrl,
|
|
||||||
expr,
|
|
||||||
syntax
|
|
||||||
} = this.state;
|
|
||||||
const context = {
|
|
||||||
svgLink,
|
|
||||||
pngLink,
|
|
||||||
permalinkUrl,
|
|
||||||
expr,
|
|
||||||
syntax,
|
|
||||||
renderExpr: this.renderExpr,
|
|
||||||
svgData: this.svgData
|
|
||||||
};
|
|
||||||
|
|
||||||
return <AppContext.Provider value={ context }>
|
|
||||||
<Form />
|
<Form />
|
||||||
|
|
||||||
<Loader />
|
<Loader />
|
||||||
@ -156,7 +31,7 @@ class App extends React.PureComponent {
|
|||||||
</Message>
|
</Message>
|
||||||
|
|
||||||
<SVG />
|
<SVG />
|
||||||
</AppContext.Provider>;
|
</AppContextProvider>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user