feat(components): Added pan and zoom to Workbench. Closes #368
This commit is contained in:
parent
b53615aea0
commit
054b7565e6
9 changed files with 397 additions and 91 deletions
|
@ -44,6 +44,7 @@
|
||||||
"@material-ui/core": "^4.0.1",
|
"@material-ui/core": "^4.0.1",
|
||||||
"@material-ui/icons": "^4.0.1",
|
"@material-ui/icons": "^4.0.1",
|
||||||
"@material-ui/lab": "^v4.0.0-alpha.14",
|
"@material-ui/lab": "^v4.0.0-alpha.14",
|
||||||
|
"react-svg-pan-zoom": "^3.8.0",
|
||||||
"prismjs": "1.16.0",
|
"prismjs": "1.16.0",
|
||||||
"file-saver": "^2.0.2"
|
"file-saver": "^2.0.2"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import babel from "rollup-plugin-babel";
|
import babel from 'rollup-plugin-babel'
|
||||||
import resolve from "rollup-plugin-node-resolve";
|
import resolve from 'rollup-plugin-node-resolve'
|
||||||
import json from "rollup-plugin-json";
|
import json from 'rollup-plugin-json'
|
||||||
import minify from "rollup-plugin-babel-minify";
|
import minify from 'rollup-plugin-babel-minify'
|
||||||
import peerDepsExternal from "rollup-plugin-peer-deps-external";
|
import peerDepsExternal from 'rollup-plugin-peer-deps-external'
|
||||||
import { name, version, description, author, license } from "./package.json";
|
import { name, version, description, author, license } from './package.json'
|
||||||
import components from "./src/index.js";
|
import components from './src/index.js'
|
||||||
|
|
||||||
const createConfig = (component, module) => {
|
const createConfig = (component, module) => {
|
||||||
return {
|
return {
|
||||||
input: `./src/${component + "/"}index.js`,
|
input: `./src/${component + '/'}index.js`,
|
||||||
output: {
|
output: {
|
||||||
file: `./${component}/index` + (module ? ".mjs" : ".js"),
|
file: `./${component}/index` + (module ? '.mjs' : '.js'),
|
||||||
format: module ? "es" : "cjs",
|
format: module ? 'es' : 'cjs',
|
||||||
sourcemap: true
|
sourcemap: true
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -19,8 +19,8 @@ const createConfig = (component, module) => {
|
||||||
resolve({ modulesOnly: true }),
|
resolve({ modulesOnly: true }),
|
||||||
json(),
|
json(),
|
||||||
babel({
|
babel({
|
||||||
exclude: "node_modules/**",
|
exclude: 'node_modules/**',
|
||||||
plugins: ["@babel/plugin-proposal-object-rest-spread"]
|
plugins: ['@babel/plugin-proposal-object-rest-spread']
|
||||||
}),
|
}),
|
||||||
minify({
|
minify({
|
||||||
comments: false,
|
comments: false,
|
||||||
|
@ -28,13 +28,16 @@ const createConfig = (component, module) => {
|
||||||
banner: `/**\n * ${name}/${component} | v${version}\n * ${description}\n * (c) ${new Date().getFullYear()} ${author}\n * @license ${license}\n */`
|
banner: `/**\n * ${name}/${component} | v${version}\n * ${description}\n * (c) ${new Date().getFullYear()} ${author}\n * @license ${license}\n */`
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const config = [];
|
const config = []
|
||||||
|
// When developing, you can use this to only rebuild the components you're working on
|
||||||
|
let dev = false
|
||||||
|
let only = ['Workbench']
|
||||||
for (let component of components) {
|
for (let component of components) {
|
||||||
config.push(createConfig(component, false));
|
if (!dev || only.indexOf(component) !== -1) config.push(createConfig(component, false))
|
||||||
// Webpack doesn't handle .mjs very well
|
// Webpack doesn't handle .mjs very well
|
||||||
//config.push(createConfig(component, true));
|
//config.push(createConfig(component, true));
|
||||||
}
|
}
|
||||||
export default config;
|
export default config
|
||||||
|
|
|
@ -7,7 +7,7 @@ const Svg = (props) => {
|
||||||
'xmlns:svg': 'http://www.w3.org/2000/svg',
|
'xmlns:svg': 'http://www.w3.org/2000/svg',
|
||||||
xmlnsXlink: 'http://www.w3.org/1999/xlink',
|
xmlnsXlink: 'http://www.w3.org/1999/xlink',
|
||||||
xmlLang: props.language,
|
xmlLang: props.language,
|
||||||
viewBox: `0 0 ${props.width} ${props.height}`,
|
viewBox: props.viewBox || `0 0 ${props.width} ${props.height}`,
|
||||||
className: props.className,
|
className: props.className,
|
||||||
style: props.style
|
style: props.style
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,8 @@ Svg.defaultProps = {
|
||||||
design: false,
|
design: false,
|
||||||
language: 'en',
|
language: 'en',
|
||||||
className: 'freesewing draft',
|
className: 'freesewing draft',
|
||||||
style: {}
|
style: {},
|
||||||
|
viewBox: false
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Svg
|
export default Svg
|
||||||
|
|
|
@ -13,6 +13,8 @@ const Draft = (props) => (
|
||||||
id={props.settings.idPrefix + 'svg'}
|
id={props.settings.idPrefix + 'svg'}
|
||||||
design={props.design}
|
design={props.design}
|
||||||
style={props.style}
|
style={props.style}
|
||||||
|
viewBox={props.viewBox}
|
||||||
|
className={props.className || 'freesewing draft'}
|
||||||
>
|
>
|
||||||
<Defs
|
<Defs
|
||||||
units={props.settings.units}
|
units={props.settings.units}
|
||||||
|
|
|
@ -1,16 +1,28 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Draft from '../../Draft'
|
import Draft from '../../Draft'
|
||||||
|
import Zoombox from '../Zoombox'
|
||||||
import Design from '../Design'
|
import Design from '../Design'
|
||||||
import DraftConfigurator from '../../DraftConfigurator'
|
import DraftConfigurator from '../../DraftConfigurator'
|
||||||
import { FormattedMessage } from 'react-intl'
|
import { FormattedMessage } from 'react-intl'
|
||||||
import Prism from 'prismjs'
|
import Prism from 'prismjs'
|
||||||
import fileSaver from 'file-saver'
|
import fileSaver from 'file-saver'
|
||||||
import theme from '@freesewing/plugin-theme'
|
import theme from '@freesewing/plugin-theme'
|
||||||
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
|
import DesignIcon from '@material-ui/icons/Fingerprint'
|
||||||
|
import DumpIcon from '@material-ui/icons/LocalSee'
|
||||||
|
import ClearIcon from '@material-ui/icons/HighlightOff'
|
||||||
|
import AdvancedIcon from '@material-ui/icons/Policy'
|
||||||
|
import PaperlessIcon from '@material-ui/icons/Nature'
|
||||||
|
import CompleteIcon from '@material-ui/icons/Style'
|
||||||
|
import UnhideIcon from '@material-ui/icons/ChevronLeft'
|
||||||
|
import HideIcon from '@material-ui/icons/ChevronRight'
|
||||||
|
|
||||||
const DraftPattern = props => {
|
const DraftPattern = (props) => {
|
||||||
const [design, setDesign] = useState(true)
|
const [design, setDesign] = useState(true)
|
||||||
const [focus, setFocus] = useState(null)
|
const [focus, setFocus] = useState(null)
|
||||||
|
const [viewBox, setViewBox] = useState(false)
|
||||||
|
const [hideAside, setHideAside] = useState(false)
|
||||||
|
|
||||||
const raiseEvent = (type, data) => {
|
const raiseEvent = (type, data) => {
|
||||||
if (type === 'clearFocusAll') {
|
if (type === 'clearFocusAll') {
|
||||||
|
@ -31,7 +43,7 @@ const DraftPattern = props => {
|
||||||
setFocus(f)
|
setFocus(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
const svgToFile = svg => {
|
const svgToFile = (svg) => {
|
||||||
const blob = new Blob([svg], {
|
const blob = new Blob([svg], {
|
||||||
type: 'image/svg+xml;charset=utf-8'
|
type: 'image/svg+xml;charset=utf-8'
|
||||||
})
|
})
|
||||||
|
@ -54,6 +66,27 @@ const DraftPattern = props => {
|
||||||
const styles = {
|
const styles = {
|
||||||
paragraph: {
|
paragraph: {
|
||||||
padding: '0 1rem'
|
padding: '0 1rem'
|
||||||
|
},
|
||||||
|
aside: {
|
||||||
|
maxWidth: '350px',
|
||||||
|
background: 'transparent',
|
||||||
|
border: 0,
|
||||||
|
fontSize: '90%',
|
||||||
|
boxShadow: '0 0 1px #cccc',
|
||||||
|
display: hideAside ? 'none' : 'block'
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
margin: '0 0.25rem'
|
||||||
|
},
|
||||||
|
unhide: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '76px',
|
||||||
|
right: 0,
|
||||||
|
background: props.theme === 'dark' ? '#f8f9fa' : '#212529',
|
||||||
|
borderTopLeftRadius: '50%',
|
||||||
|
borderBottomLeftRadius: '50%',
|
||||||
|
width: '26px',
|
||||||
|
height: '30px'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let pattern = new props.Pattern(props.gist.settings)
|
let pattern = new props.Pattern(props.gist.settings)
|
||||||
|
@ -73,57 +106,116 @@ const DraftPattern = props => {
|
||||||
Prism.languages.javascript,
|
Prism.languages.javascript,
|
||||||
'javascript'
|
'javascript'
|
||||||
)
|
)
|
||||||
|
let iconProps = {
|
||||||
|
size: 'small',
|
||||||
|
style: styles.icon,
|
||||||
|
color: 'inherit'
|
||||||
|
}
|
||||||
|
const color = (check) => (check ? '#40c057' : '#fa5252')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fs-sa">
|
<div className="fs-sa">
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
<Draft
|
||||||
<FormattedMessage id="app.pattern" />
|
{...patternProps}
|
||||||
</h2>
|
design={design}
|
||||||
<Draft {...patternProps} design={design} focus={focus} raiseEvent={raiseEvent} />
|
focus={focus}
|
||||||
<h2>gist</h2>
|
raiseEvent={raiseEvent}
|
||||||
<div className="gatsby-highlight">
|
viewBox={viewBox}
|
||||||
<pre className="language-json" dangerouslySetInnerHTML={{ __html: gist }} />
|
className="freesewing draft shadow"
|
||||||
|
/>
|
||||||
|
{hideAside && (
|
||||||
|
<div style={styles.unhide}>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => setHideAside(false)}
|
||||||
|
title="Show sidebar"
|
||||||
|
{...iconProps}
|
||||||
|
style={{ margin: 0 }}
|
||||||
|
>
|
||||||
|
<span style={{ color: props.theme === 'dark' ? '#212529' : '#f8f9fa' }}>
|
||||||
|
<UnhideIcon />
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<aside>
|
<aside style={styles.aside}>
|
||||||
<div className="sticky">
|
<div className="sticky">
|
||||||
{design ? (
|
<div style={{ padding: '5px' }}>
|
||||||
<React.Fragment>
|
<Zoombox patternProps={patternProps} setViewBox={setViewBox} />
|
||||||
<p style={styles.paragraph}>
|
</div>
|
||||||
<FormattedMessage id="cfp.designModeIsOn" />
|
<div style={{ margin: '1rem auto 0', textAlign: 'center' }}>
|
||||||
(
|
<IconButton
|
||||||
<a href="#logo" onClick={() => setDesign(false)}>
|
onClick={() => setDesign(!design)}
|
||||||
<FormattedMessage id="cfp.turnOff" />
|
title="Toggle design mode"
|
||||||
</a>
|
{...iconProps}
|
||||||
)
|
>
|
||||||
{focusCount > 0 ? (
|
<span style={{ color: color(design) }}>
|
||||||
<React.Fragment>
|
<DesignIcon />
|
||||||
 (
|
</span>
|
||||||
<a href="#logo" onClick={() => raiseEvent('clearFocusAll', null)}>
|
</IconButton>
|
||||||
<FormattedMessage id="app.reset" />
|
{design && (
|
||||||
</a>
|
<IconButton
|
||||||
)
|
onClick={() => raiseEvent('clearFocusAll', null)}
|
||||||
</React.Fragment>
|
title="Clear design mode"
|
||||||
) : null}
|
{...iconProps}
|
||||||
</p>
|
>
|
||||||
|
<ClearIcon color="primary" />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
onClick={() => console.log(pattern)}
|
||||||
|
title="console.log(pattern)"
|
||||||
|
{...iconProps}
|
||||||
|
>
|
||||||
|
<DumpIcon color="primary" />
|
||||||
|
</IconButton>
|
||||||
|
|
|
||||||
|
<IconButton
|
||||||
|
onClick={() =>
|
||||||
|
props.updateGist(!props.gist.settings.advanced, 'settings', 'advanced')
|
||||||
|
}
|
||||||
|
title="Toggle advanced settings"
|
||||||
|
{...iconProps}
|
||||||
|
>
|
||||||
|
<span style={{ color: color(props.gist.settings.advanced) }}>
|
||||||
|
<AdvancedIcon />
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
onClick={() =>
|
||||||
|
props.updateGist(!props.gist.settings.paperless, 'settings', 'paperless')
|
||||||
|
}
|
||||||
|
title="Toggle paperless"
|
||||||
|
{...iconProps}
|
||||||
|
>
|
||||||
|
<span style={{ color: color(props.gist.settings.paperless) }}>
|
||||||
|
<PaperlessIcon />
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
onClick={() =>
|
||||||
|
props.updateGist(!props.gist.settings.complete, 'settings', 'complete')
|
||||||
|
}
|
||||||
|
title="Toggle complete"
|
||||||
|
{...iconProps}
|
||||||
|
>
|
||||||
|
<span style={{ color: color(props.gist.settings.complete) }}>
|
||||||
|
<CompleteIcon />
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton onClick={() => setHideAside(true)} title="Hide sidebar" {...iconProps}>
|
||||||
|
<HideIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
{design && (
|
||||||
<Design
|
<Design
|
||||||
focus={focus}
|
focus={focus}
|
||||||
design={design}
|
design={design}
|
||||||
raiseEvent={raiseEvent}
|
raiseEvent={raiseEvent}
|
||||||
parts={patternProps.parts}
|
parts={patternProps.parts}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<p style={styles.paragraph}>
|
|
||||||
<FormattedMessage id="cfp.designModeIsOff" />
|
|
||||||
(
|
|
||||||
<a href="#logo" onClick={() => setDesign(true)}>
|
|
||||||
<FormattedMessage id="cfp.turnOn" />
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
<DraftConfigurator
|
<DraftConfigurator
|
||||||
noDocs
|
noDocs
|
||||||
|
|
20
packages/components/src/Workbench/Json/index.js
Normal file
20
packages/components/src/Workbench/Json/index.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import Prism from 'prismjs'
|
||||||
|
|
||||||
|
const PatternJson = (props) => {
|
||||||
|
let gist = Prism.highlight(
|
||||||
|
JSON.stringify(props.gist, null, 2),
|
||||||
|
Prism.languages.javascript,
|
||||||
|
'javascript'
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '1rem' }}>
|
||||||
|
<div className="gatsby-highlight">
|
||||||
|
<pre className="language-json" dangerouslySetInnerHTML={{ __html: gist }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PatternJson
|
|
@ -1,27 +1,30 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from 'prop-types'
|
||||||
import SampleConfigurator from "../../SampleConfigurator";
|
import SampleConfigurator from '../../SampleConfigurator'
|
||||||
import svgattrPlugin from "@freesewing/plugin-svgattr";
|
import svgattrPlugin from '@freesewing/plugin-svgattr'
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from 'react-intl'
|
||||||
|
|
||||||
const SamplePattern = props => {
|
const SamplePattern = (props) => {
|
||||||
let pattern = new props.Pattern(props.gist.settings).use(svgattrPlugin, {
|
let pattern = new props.Pattern(props.gist.settings).use(svgattrPlugin, {
|
||||||
class: "freesewing draft"
|
class: 'freesewing draft'
|
||||||
});
|
})
|
||||||
try {
|
try {
|
||||||
pattern.sample();
|
pattern.sample()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="fs-sa">
|
<div className="fs-sa">
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
|
||||||
<FormattedMessage id="app.pattern" />
|
|
||||||
</h2>
|
|
||||||
<div dangerouslySetInnerHTML={{ __html: pattern.render() }} />
|
<div dangerouslySetInnerHTML={{ __html: pattern.render() }} />
|
||||||
<h2>gist</h2>
|
<div style={{ padding: '1rem' }}>
|
||||||
<pre>{JSON.stringify(props.gist, null, 2)}</pre>
|
<div className="gatsby-highlight">
|
||||||
|
<pre
|
||||||
|
className="language-json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(props.gist, null, 2) }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<aside>
|
<aside>
|
||||||
|
@ -37,8 +40,8 @@ const SamplePattern = props => {
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
SamplePattern.propTypes = {
|
SamplePattern.propTypes = {
|
||||||
gist: PropTypes.object.isRequired,
|
gist: PropTypes.object.isRequired,
|
||||||
|
@ -46,12 +49,12 @@ SamplePattern.propTypes = {
|
||||||
config: PropTypes.object.isRequired,
|
config: PropTypes.object.isRequired,
|
||||||
raiseEvent: PropTypes.func.isRequired,
|
raiseEvent: PropTypes.func.isRequired,
|
||||||
Pattern: PropTypes.func.isRequired,
|
Pattern: PropTypes.func.isRequired,
|
||||||
units: PropTypes.oneOf(["metric", "imperial"])
|
units: PropTypes.oneOf(['metric', 'imperial'])
|
||||||
};
|
}
|
||||||
|
|
||||||
SamplePattern.defaultProps = {
|
SamplePattern.defaultProps = {
|
||||||
units: "metric",
|
units: 'metric',
|
||||||
pointInfo: null
|
pointInfo: null
|
||||||
};
|
}
|
||||||
|
|
||||||
export default SamplePattern;
|
export default SamplePattern
|
||||||
|
|
158
packages/components/src/Workbench/Zoombox/index.js
Normal file
158
packages/components/src/Workbench/Zoombox/index.js
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import React, { useState, useRef, useEffect } from 'react'
|
||||||
|
import Draft from '../../Draft'
|
||||||
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
|
import ZoomIcon from '@material-ui/icons/Cancel'
|
||||||
|
|
||||||
|
const Zoombox = (props) => {
|
||||||
|
const [from, setFrom] = useState(false)
|
||||||
|
const [to, setTo] = useState(false)
|
||||||
|
const [dragging, setDragging] = useState(false)
|
||||||
|
const [factor, setFactor] = useState(1)
|
||||||
|
const [box, setBox] = useState(false)
|
||||||
|
const [panning, setPanning] = useState(false)
|
||||||
|
const [falseAlarm, setFalseAlarm] = useState(false)
|
||||||
|
const [panFrom, setPanFrom] = useState(false)
|
||||||
|
const ref = useRef(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let box = ref.current.getBoundingClientRect()
|
||||||
|
setBox(box)
|
||||||
|
setFactor(props.patternProps.width / box.width)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const resetZoom = (evt) => {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
setFrom(false)
|
||||||
|
setTo(false)
|
||||||
|
setDragging(false)
|
||||||
|
props.setViewBox(false)
|
||||||
|
}
|
||||||
|
const startPan = (evt) => {
|
||||||
|
if (!dragging && !panning) {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
setPanning(true)
|
||||||
|
setPanFrom([evt.clientX, evt.clientY])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const endPan = (evt) => {
|
||||||
|
if (!dragging && panning) {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
setPanning(false)
|
||||||
|
setPanFrom(false)
|
||||||
|
updateViewBox(evt)
|
||||||
|
//props.setViewBox(`${from[0] * factor} ${from[1] * factor} ${to[0] * factor} ${to[1] * factor}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handlePan = (evt) => {
|
||||||
|
if (!dragging && panning) {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
let x, y
|
||||||
|
if (from[0] + (evt.clientX - panFrom[0]) <= -5) {
|
||||||
|
// Bump into left
|
||||||
|
} else if (from[1] + (evt.clientY - panFrom[1]) <= -5) {
|
||||||
|
// Bump into top
|
||||||
|
} else if (to[0] + (evt.clientX - panFrom[0]) >= box.width - 11) {
|
||||||
|
// Bump into right
|
||||||
|
} else if (to[1] + (evt.clientY - panFrom[1]) >= box.height - 11) {
|
||||||
|
// Bump into bottom
|
||||||
|
} else {
|
||||||
|
setPanFrom([evt.clientX, evt.clientY])
|
||||||
|
setFrom([from[0] + (evt.clientX - panFrom[0]), from[1] + (evt.clientY - panFrom[1])])
|
||||||
|
setTo([to[0] + (evt.clientX - panFrom[0]), to[1] + (evt.clientY - panFrom[1])])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleMouseDown = (evt) => {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
setFrom([evt.clientX - box.x, evt.clientY - box.y])
|
||||||
|
setTo([evt.clientX - box.x, evt.clientY - box.y])
|
||||||
|
setDragging(1)
|
||||||
|
setPanning(false)
|
||||||
|
}
|
||||||
|
const handleMouseUp = (evt) => {
|
||||||
|
if (dragging == 2) {
|
||||||
|
updateViewBox(evt)
|
||||||
|
if (falseAlarm) setFalseAlarm(false)
|
||||||
|
} else setFalseAlarm(true)
|
||||||
|
setDragging(false)
|
||||||
|
setPanning(false)
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
}
|
||||||
|
const handleMouseMove = (evt) => {
|
||||||
|
if (dragging) {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
if (dragging === 1) setDragging(2)
|
||||||
|
if (falseAlarm) setFalseAlarm(false)
|
||||||
|
setTo([evt.clientX - box.x, evt.clientY - box.y])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleMouseOver = (evt) => {
|
||||||
|
evt.stopPropagation()
|
||||||
|
evt.preventDefault()
|
||||||
|
setFactor(props.patternProps.width / box.width)
|
||||||
|
}
|
||||||
|
const updateViewBox = (evt) => {
|
||||||
|
props.setViewBox(
|
||||||
|
from[0] * factor +
|
||||||
|
' ' +
|
||||||
|
from[1] * factor +
|
||||||
|
' ' +
|
||||||
|
(evt.clientX - box.x - from[0]) * factor +
|
||||||
|
' ' +
|
||||||
|
(evt.clientY - box.y - from[1]) * factor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
onMouseUp={handleMouseUp}
|
||||||
|
onMouseOver={handleMouseOver}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
className="zoombox"
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<Draft {...props.patternProps} />
|
||||||
|
<div className="mask" />
|
||||||
|
{box && from && to && dragging !== 1 && !falseAlarm && (
|
||||||
|
<div
|
||||||
|
className={'box' + (dragging ? ' active' : ' inactive')}
|
||||||
|
style={{
|
||||||
|
// Remove 16px because of the close icon
|
||||||
|
width: to[0] - from[0] - 16 + 'px',
|
||||||
|
height: to[1] - from[1] - 16 + 'px',
|
||||||
|
left: from[0] + 'px',
|
||||||
|
top: from[1] + 'px'
|
||||||
|
}}
|
||||||
|
onMouseDown={startPan}
|
||||||
|
onMouseUp={endPan}
|
||||||
|
onMouseMove={handlePan}
|
||||||
|
>
|
||||||
|
{!dragging && (
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
className="close"
|
||||||
|
onMouseDown={resetZoom}
|
||||||
|
onMouseUp={resetZoom}
|
||||||
|
>
|
||||||
|
<ZoomIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<pre>{false && JSON.stringify({ from, to, panFrom }, null, 2)}</pre>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Zoombox
|
|
@ -11,12 +11,13 @@ import LanguageIcon from '@material-ui/icons/Translate'
|
||||||
import DarkModeIcon from '@material-ui/icons/Brightness3'
|
import DarkModeIcon from '@material-ui/icons/Brightness3'
|
||||||
import LanguageChooser from './LanguageChooser'
|
import LanguageChooser from './LanguageChooser'
|
||||||
import DraftPattern from './DraftPattern'
|
import DraftPattern from './DraftPattern'
|
||||||
|
import Json from './Json'
|
||||||
import SamplePattern from './SamplePattern'
|
import SamplePattern from './SamplePattern'
|
||||||
import Welcome from './Welcome'
|
import Welcome from './Welcome'
|
||||||
import Footer from '../Footer'
|
import Footer from '../Footer'
|
||||||
import Measurements from './Measurements'
|
import Measurements from './Measurements'
|
||||||
|
|
||||||
const Workbench = props => {
|
const Workbench = (props) => {
|
||||||
const [display, setDisplay] = useState(null)
|
const [display, setDisplay] = useState(null)
|
||||||
const [pattern, setPattern] = useState(false)
|
const [pattern, setPattern] = useState(false)
|
||||||
const [theme, setTheme] = useState('light')
|
const [theme, setTheme] = useState('light')
|
||||||
|
@ -39,12 +40,12 @@ const Workbench = props => {
|
||||||
}, [props.language])
|
}, [props.language])
|
||||||
|
|
||||||
const getDisplay = () => storage.get(props.config.name + '-display')
|
const getDisplay = () => storage.get(props.config.name + '-display')
|
||||||
const saveDisplay = d => {
|
const saveDisplay = (d) => {
|
||||||
setDisplay(d)
|
setDisplay(d)
|
||||||
storage.set(props.config.name + '-display', d)
|
storage.set(props.config.name + '-display', d)
|
||||||
}
|
}
|
||||||
const getMeasurements = () => storage.get(props.config.name + '-measurements')
|
const getMeasurements = () => storage.get(props.config.name + '-measurements')
|
||||||
const saveMeasurements = data => {
|
const saveMeasurements = (data) => {
|
||||||
storage.set(props.config.name + '-measurements', data)
|
storage.set(props.config.name + '-measurements', data)
|
||||||
props.updateGist(data, 'settings', 'measurements')
|
props.updateGist(data, 'settings', 'measurements')
|
||||||
}
|
}
|
||||||
|
@ -54,7 +55,7 @@ const Workbench = props => {
|
||||||
setMeasurements(updatedMeasurements)
|
setMeasurements(updatedMeasurements)
|
||||||
saveMeasurements(updatedMeasurements)
|
saveMeasurements(updatedMeasurements)
|
||||||
}
|
}
|
||||||
const preloadMeasurements = model => {
|
const preloadMeasurements = (model) => {
|
||||||
let updatedMeasurements = {
|
let updatedMeasurements = {
|
||||||
...measurements,
|
...measurements,
|
||||||
...model
|
...model
|
||||||
|
@ -97,6 +98,12 @@ const Workbench = props => {
|
||||||
onClick: () => saveDisplay('measurements'),
|
onClick: () => saveDisplay('measurements'),
|
||||||
text: 'app.measurements',
|
text: 'app.measurements',
|
||||||
active: display === 'measurements' ? true : false
|
active: display === 'measurements' ? true : false
|
||||||
|
},
|
||||||
|
json: {
|
||||||
|
type: 'button',
|
||||||
|
onClick: () => saveDisplay('json'),
|
||||||
|
text: 'JSON',
|
||||||
|
active: display === 'json' ? true : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
right: {
|
right: {
|
||||||
|
@ -148,6 +155,7 @@ const Workbench = props => {
|
||||||
units={props.units}
|
units={props.units}
|
||||||
svgExport={svgExport}
|
svgExport={svgExport}
|
||||||
setSvgExport={setSvgExport}
|
setSvgExport={setSvgExport}
|
||||||
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
@ -177,6 +185,24 @@ const Workbench = props => {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
case 'json':
|
||||||
|
main = <Json gist={props.gist} />
|
||||||
|
break
|
||||||
|
case 'inspect':
|
||||||
|
main = (
|
||||||
|
<InspectPattern
|
||||||
|
freesewing={props.freesewing}
|
||||||
|
Pattern={props.Pattern}
|
||||||
|
config={props.config}
|
||||||
|
gist={props.gist}
|
||||||
|
updateGist={props.updateGist}
|
||||||
|
raiseEvent={raiseEvent}
|
||||||
|
units={props.units}
|
||||||
|
svgExport={svgExport}
|
||||||
|
setSvgExport={setSvgExport}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
main = <Welcome language={props.language} setDisplay={saveDisplay} />
|
main = <Welcome language={props.language} setDisplay={saveDisplay} />
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue