1
0
Fork 0

refactor(components): Removed prop-types dependency

This commit is contained in:
Joost De Cock 2020-04-25 19:09:02 +02:00
parent 8cf8424437
commit cda9b03713
44 changed files with 559 additions and 1026 deletions

View file

@ -1,49 +1,36 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import Radio from '@material-ui/core/Radio'
import RadioGroup from '@material-ui/core/RadioGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
const Bool = props => {
const [value, setValue] = useState(props.dflt)
const Bool = ({ dflt = false, labels = ['false', 'true'], value, name, updateValue }) => {
const [val, setVal] = useState(dflt)
useEffect(() => {
if (props.value !== value) setValue(props.value)
}, [props.value])
if (value !== val) setVal(value)
}, [value])
const toggle = () => {
props.updateValue(props.name, !value)
setValue(!value)
updateValue(name, !val)
setVal(!val)
}
return (
<RadioGroup onChange={toggle} value={JSON.stringify(value)}>
<RadioGroup onChange={toggle} value={JSON.stringify(val)}>
<FormControlLabel
control={<Radio color="primary" />}
value="false"
checked={value === 'true' || value === true || value === 1 ? false : true}
label={props.labels[0]}
checked={val === 'true' || val === true || val === 1 ? false : true}
label={labels[0]}
className="po-list-item"
/>
<FormControlLabel
control={<Radio color="primary" />}
value="true"
checked={value === 'true' || value === true || value === 1 ? true : false}
label={props.labels[1]}
checked={val === 'true' || val === true || val === 1 ? true : false}
label={labels[1]}
className="po-list-item"
/>
</RadioGroup>
)
}
Bool.propTypes = {
dflt: PropTypes.bool,
labels: PropTypes.array,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired
}
Bool.defaultProps = {
dflt: false,
labels: ['false', 'true']
}
export default Bool

View file

@ -1,24 +1,23 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import FormGroup from "@material-ui/core/FormGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Checkbox from "@material-ui/core/Checkbox";
import React, { useState } from 'react'
import FormGroup from '@material-ui/core/FormGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import Checkbox from '@material-ui/core/Checkbox'
const FormFieldChecks = props => {
const [value, setValue] = useState(props.dflt ? props.dflt : []);
const FormFieldChecks = (props) => {
const [value, setValue] = useState(props.dflt ? props.dflt : [])
const toggle = part => {
let parts = value.slice(0);
let index = parts.indexOf(part);
if (index === -1) parts.push(part);
else parts.splice(index, 1);
setValue(parts);
props.updateValue(props.name, parts);
};
const toggle = (part) => {
let parts = value.slice(0)
let index = parts.indexOf(part)
if (index === -1) parts.push(part)
else parts.splice(index, 1)
setValue(parts)
props.updateValue(props.name, parts)
}
return (
<FormGroup>
{Object.keys(props.checks).map(i => {
{Object.keys(props.checks).map((i) => {
return (
<FormControlLabel
control={
@ -32,17 +31,10 @@ const FormFieldChecks = props => {
key={i}
className="po-list-item"
/>
);
)
})}
</FormGroup>
);
};
)
}
FormFieldChecks.propTypes = {
dflt: PropTypes.array,
checks: PropTypes.object,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired
};
export default FormFieldChecks;
export default FormFieldChecks

View file

@ -1,15 +1,14 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import Radio from '@material-ui/core/Radio'
import RadioGroup from '@material-ui/core/RadioGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
const FormFieldList = props => {
const FormFieldList = (props) => {
const [value, setValue] = useState(props.dflt)
useEffect(() => {
if (props.value !== value) setValue(props.value)
}, [props.value])
const update = evt => {
const update = (evt) => {
props.updateValue(props.name, evt.target.value)
setValue(evt.target.value)
}
@ -30,15 +29,4 @@ const FormFieldList = props => {
)
}
FormFieldList.propTypes = {
dflt: PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired,
PropTypes.bool.isRequired
]),
list: PropTypes.object,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired
}
export default FormFieldList

View file

@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import TextField from '@material-ui/core/TextField'
import IconButton from '@material-ui/core/IconButton'
import InvalidIcon from '@material-ui/icons/Warning'
@ -47,13 +46,4 @@ const FormFieldMeasurement = (props) => {
)
}
FormFieldMeasurement.propTypes = {
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
units: PropTypes.oneOf(['metric', 'imperial'])
}
FormFieldMeasurement.defaultProps = {}
export default injectIntl(FormFieldMeasurement)

View file

@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import Slider from '@material-ui/core/Slider'
import { withStyles } from '@material-ui/core/styles'
@ -11,48 +10,32 @@ const PaddedSlider = withStyles({
thumb: { width: '16px', height: '16px' }
})(Slider)
const FormFieldSlider = props => {
const [value, setValue] = useState(props.value)
const FormFieldSlider = ({ min = 0, max = 100, step = 0.1, label = false, updateValue, name }) => {
const [val, setVal] = useState(value)
useEffect(() => {
if (props.value !== value) setValue(props.value)
}, [props.value])
if (value !== val) setVal(value)
}, [value])
const update = (evt, newValue) => {
props.updateValue(props.name, newValue, evt)
setValue(newValue)
updateValue(name, newValue, evt)
setVal(newValue)
}
return (
<PaddedSlider
value={value}
min={props.min}
max={props.max}
step={props.step}
value={val}
min={min}
max={max}
step={step}
onChange={update}
onChangeCommitted={update}
classes={{
track: 'slider-track',
thumb: 'slider-thumb'
}}
aria-labelledby={props.label}
aria-labelledby={label}
/>
)
}
FormFieldSlider.propTypes = {
min: PropTypes.number,
max: PropTypes.number,
step: PropTypes.number,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.oneOf([false])])
}
FormFieldSlider.defaultProps = {
min: 0,
max: 100,
step: 0.1,
label: false
}
export default FormFieldSlider

View file

@ -1,26 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import Icon from '../Icon'
const Blockquote = props => {
const Blockquote = (props) => {
const attr = Object.assign({}, props)
delete attr.type
delete attr.children
return (
<blockquote className={props.type} {...attr}>
{props.children}
{props.type === 'fixme' ? null : <Icon icon={props.type} className={'icon ' + props.type} />}
<blockquote className={props.type || 'note'} {...attr}>
{props.children || null}
{props.type !== 'fixme' && <Icon icon={props.type} className={'icon ' + props.type} />}
</blockquote>
)
}
Blockquote.propTypes = {
type: PropTypes.string.isRequired,
children: PropTypes.node
}
Blockquote.defaultProps = {
type: 'note',
children: null
}
export default Blockquote

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
const Circle = (props) => (
<circle
@ -10,8 +9,4 @@ const Circle = (props) => (
/>
)
Circle.propTypes = {
point: PropTypes.object.isRequired
}
export default Circle

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import Path from '../Path'
import Point from '../Point'
import Snippet from '../Snippet'
@ -136,12 +135,4 @@ const Part = (props) => {
)
}
Part.propTypes = {
part: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
language: PropTypes.string.isRequired,
paperless: PropTypes.bool.isRequired,
design: PropTypes.bool.isRequired
}
export default Part

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import TextOnPath from '../TextOnPath'
import DesignPath from '../DesignPath'
import { getProps } from '../utils'
@ -18,10 +17,4 @@ const Path = (props) => {
return output
}
Path.propTypes = {
path: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
language: PropTypes.string.isRequired
}
export default Path

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import DesignPoint from '../DesignPoint'
import Text from '../Text'
import Circle from '../Circle'
@ -12,14 +11,8 @@ const Point = (props) => {
output.push(<Text {...props} key={'point-' + props.name} />)
if (props.point.attributes.get('data-circle'))
output.push(<Circle point={props.point} key={'circle-' + props.name} />)
if (output.length < 1) return null
else return output
}
Point.propTypes = {
point: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
language: PropTypes.string.isRequired
return output.length < 1 ? null : output
}
export default Point

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import { getProps } from '../utils'
const Snippet = (props) => {
@ -25,8 +24,4 @@ const Snippet = (props) => {
return <use {...snippetProps} {...getProps(props.snippet)} />
}
Snippet.propTypes = {
snippet: PropTypes.object.isRequired
}
export default Snippet

View file

@ -1,40 +1,33 @@
import React from 'react'
import PropTypes from 'prop-types'
const Svg = (props) => {
const Svg = ({
embed = true,
design = false,
language = 'en',
className = 'freesewing draft',
style = {},
viewBox = false,
width,
height,
children
}) => {
let attributes = {
xmlns: 'http://www.w3.org/2000/svg',
'xmlns:svg': 'http://www.w3.org/2000/svg',
xmlnsXlink: 'http://www.w3.org/1999/xlink',
xmlLang: props.language,
viewBox: props.viewBox || `0 0 ${props.width} ${props.height}`,
className: props.className,
style: props.style
xmlLang: language,
viewBox: viewBox || `0 0 ${width} ${height}`,
className,
style
}
if (!props.embed) {
attributes.width = props.width + 'mm'
attributes.height = props.height + 'mm'
if (!embed) {
attributes.width = width + 'mm'
attributes.height = height + 'mm'
}
if (props.design) attributes.className += ' design'
if (design) attributes.className += ' design'
return <svg {...attributes}>{props.children}</svg>
}
Svg.propTypes = {
embed: PropTypes.bool,
className: PropTypes.string,
language: PropTypes.string,
design: PropTypes.bool
}
Svg.defaultProps = {
embed: true,
design: false,
language: 'en',
className: 'freesewing draft',
style: {},
viewBox: false
return <svg {...attributes}>{children}</svg>
}
export default Svg

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import { strings } from '@freesewing/i18n'
const Text = (props) => {
@ -30,6 +29,7 @@ const Text = (props) => {
)
}
} else text.push(<tspan key="tspan-1">{translated}</tspan>)
return (
<text
x={props.point.x}
@ -41,9 +41,4 @@ const Text = (props) => {
)
}
Text.propTypes = {
point: PropTypes.object.isRequired,
language: PropTypes.string.isRequired
}
export default Text

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import { strings } from '@freesewing/i18n'
const TextOnPath = (props) => {
@ -29,9 +28,4 @@ const TextOnPath = (props) => {
)
}
TextOnPath.propTypes = {
path: PropTypes.object.isRequired,
language: PropTypes.string.isRequired
}
export default TextOnPath

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import Svg from './Svg'
import Defs from './Defs'
import Part from './Part'
@ -11,8 +10,8 @@ const Draft = (props) => (
height={props.height}
language={props.settings.locale}
id={props.settings.idPrefix + 'svg'}
design={props.design}
style={props.style}
design={props.design || false}
style={props.style || {}}
viewBox={props.viewBox}
className={props.className || 'freesewing draft'}
>
@ -20,7 +19,7 @@ const Draft = (props) => (
units={props.settings.units}
parts={props.parts}
paperless={props.settings.paperless}
design={props.design}
design={props.design || false}
/>
<g>
{Object.keys(props.parts).map((name) => (
@ -31,8 +30,8 @@ const Draft = (props) => (
units={props.settings.units}
key={name}
name={name}
focus={props.focus}
design={props.design}
focus={props.focus || false}
design={props.design || false}
raiseEvent={props.raiseEvent}
/>
))}
@ -40,16 +39,4 @@ const Draft = (props) => (
</Svg>
)
Draft.propTypes = {
parts: PropTypes.object.isRequired,
settings: PropTypes.object.isRequired,
design: PropTypes.bool
}
Draft.defaultProps = {
design: false,
focus: false,
style: {}
}
export default Draft

View file

@ -1,11 +1,10 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import FormFieldList from '../../.form/FormFieldList'
import OptionPreamble from '../OptionPreamble'
import { injectIntl } from 'react-intl'
import { languages } from '@freesewing/i18n'
const DraftSettingLanguage = props => {
const DraftSettingLanguage = (props) => {
const [value, setValue] = useState(props.value === null ? props.intl.locale : props.value)
const [expanded, setExpanded] = useState(false)
@ -64,12 +63,4 @@ const DraftSettingLanguage = props => {
)
}
DraftSettingLanguage.propTypes = {
raiseEvent: PropTypes.func.isRequired,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired
}
export default injectIntl(DraftSettingLanguage)

View file

@ -1,12 +1,11 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import FormFieldSlider from '../../.form/FormFieldSlider'
import formatMm from '@freesewing/utils/formatMm'
import roundMm from '@freesewing/utils/roundMm'
import sliderStep from '@freesewing/utils/sliderStep'
import OptionPreamble from '../OptionPreamble'
const DraftSettingMargin = props => {
const DraftSettingMargin = (props) => {
const [value, setValue] = useState(props.value === null ? props.dflt : props.value)
const [expanded, setExpanded] = useState(false)
@ -77,16 +76,4 @@ const DraftSettingMargin = props => {
)
}
DraftSettingMargin.propTypes = {
raiseEvent: PropTypes.func.isRequired,
updateValue: PropTypes.func.isRequired,
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
units: PropTypes.oneOf(['metric', 'imperial']).isRequired
}
DraftSettingMargin.defaultProps = {
// FIXME
}
export default DraftSettingMargin

View file

@ -1,5 +1,4 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import FormFieldList from '../../.form/FormFieldList'
import FormFieldSlider from '../../.form/FormFieldSlider'
import formatMm from '@freesewing/utils/formatMm'
@ -8,7 +7,7 @@ import defaultSa from '@freesewing/utils/defaultSa'
import sliderStep from '@freesewing/utils/sliderStep'
import OptionPreamble from '../OptionPreamble'
const DraftSettingSa = props => {
const DraftSettingSa = (props) => {
const [value, setValue] = useState(
props.value === defaultSa[props.units] ? 'dflt' : props.value === 0 ? 'none' : 'custom'
)
@ -131,16 +130,4 @@ const DraftSettingSa = props => {
)
}
DraftSettingSa.propTypes = {
updateValue: PropTypes.func.isRequired,
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
units: PropTypes.oneOf(['metric', 'imperial']).isRequired,
labels: PropTypes.object
}
DraftSettingSa.defaultProps = {
// FIXME
}
export default DraftSettingSa

View file

@ -1,5 +1,4 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import DraftSettingSa from '../DraftSettingSa'
import DraftSettingMargin from '../DraftSettingMargin'
@ -11,9 +10,41 @@ import DraftSettingLanguage from '../DraftSettingLanguage'
import DraftSettingOnly from '../DraftSettingOnly'
import RightIcon from '@material-ui/icons/KeyboardArrowRight'
const DraftSettings = props => {
const DraftSettings = ({
units = 'metric',
raiseEvent,
updateValue,
noDocs,
pattern,
config,
data = { settings: {} }
}) => {
// State
const [expanded, setExpanded] = useState([])
const toggleGroup = group => {
// Building blocks
const noyes = [<FormattedMessage id="app.no" />, <FormattedMessage id="app.yes" />]
const hideshow = [<FormattedMessage id="app.hide" />, <FormattedMessage id="app.show" />]
const metricimperial = {
metric: <FormattedMessage id="app.metricUnits" />,
imperial: <FormattedMessage id="app.imperialUnits" />
}
const labels = {
sa: {
none: <FormattedMessage id="app.noSeamAllowance" />,
dflt: <FormattedMessage id="app.standardSeamAllowance" />,
custom: <FormattedMessage id="app.customSeamAllowance" />
},
only: {
dflt: <FormattedMessage id="app.default" />,
custom: <FormattedMessage id="app.custom" />
},
paperless: noyes,
advanced: hideshow,
complete: hideshow
}
// Methods
const toggleGroup = (group) => {
let shown = expanded.slice(0)
let index = shown.indexOf(group)
if (index === -1) shown.push(group)
@ -33,42 +64,21 @@ const DraftSettings = props => {
case 'margin':
return 2
case 'units':
return props.units
return units
default:
return false
}
}
let noyes = [<FormattedMessage id="app.no" />, <FormattedMessage id="app.yes" />]
let hideshow = [<FormattedMessage id="app.hide" />, <FormattedMessage id="app.show" />]
let units = {
metric: <FormattedMessage id="app.metricUnits" />,
imperial: <FormattedMessage id="app.imperialUnits" />
}
const addProps = setting => {
const labels = {
sa: {
none: <FormattedMessage id="app.noSeamAllowance" />,
dflt: <FormattedMessage id="app.standardSeamAllowance" />,
custom: <FormattedMessage id="app.customSeamAllowance" />
},
only: {
dflt: <FormattedMessage id="app.default" />,
custom: <FormattedMessage id="app.custom" />
},
paperless: noyes,
advanced: hideshow,
complete: hideshow
}
const addProps = (setting) => {
let childProps = {
raiseEvent: props.raiseEvent,
updateValue: props.updateValue,
units: props.units,
raiseEvent,
updateValue,
units,
key: setting,
name: setting,
labels: labels[setting],
noDocs: props.noDocs,
dflt: getDefault(setting, props.pattern),
noDocs,
dflt: getDefault(setting, pattern),
designDflt: getDefault(setting)
}
childProps.title = <FormattedMessage id={'settings.' + setting + '.title'} />
@ -76,26 +86,21 @@ const DraftSettings = props => {
if (setting === 'only') {
childProps.customDflt = []
childProps.parts = {}
if (props.config.draftOrder) {
for (let part of props.config.draftOrder)
if (config.draftOrder) {
for (let part of config.draftOrder)
childProps.parts[part] = <FormattedMessage id={'parts.' + part} />
}
}
if (
typeof props.data !== 'undefined' &&
typeof props.data.settings !== 'undefined' &&
typeof props.data.settings[setting] !== 'undefined'
)
childProps.value = props.data.settings[setting]
if (typeof data.settings[setting] !== 'undefined') childProps.value = data.settings[setting]
else childProps.value = null
return childProps
}
let groups = {
const groups = {
advanced: [
<DraftSettingLanguage {...addProps('locale')} />,
<DraftSettingUnits {...addProps('units')} list={units} />,
<DraftSettingUnits {...addProps('units')} list={metricimperial} />,
<DraftSettingComplete {...addProps('complete')} />,
<DraftSettingMargin {...addProps('margin')} />,
<DraftSettingOnly {...addProps('only')} />
@ -103,19 +108,19 @@ const DraftSettings = props => {
}
return (
<React.Fragment>
<>
<ul className="config l2 nogroups">
<DraftSettingSa {...addProps('sa')} />
<DraftSettingPaperless {...addProps('paperless')} />
<DraftSettingAdvanced {...addProps('advanced')} />
</ul>
{props.data.settings.advanced ? (
{data.settings.advanced && (
<ul className="config l2">
{Object.keys(groups).map(group => {
{Object.keys(groups).map((group) => {
let open = true
if (expanded.indexOf(group) === -1) open = false
let children = null
if (open) children = groups[group].map(component => component)
if (open) children = groups[group].map((component) => component)
return (
<React.Fragment key={group}>
<li className={open ? 'expanded' : 'collapsed'} key={group + '-ghead'}>
@ -129,15 +134,9 @@ const DraftSettings = props => {
)
})}
</ul>
) : null}
</React.Fragment>
)}
</>
)
}
DraftSettings.propTypes = {
config: PropTypes.object.isRequired
}
DraftSettings.defaultProps = {}
export default DraftSettings

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import Pct from '../PatternOptionPercentage'
import Deg from '../PatternOptionDegree'
import Mm from '../PatternOptionMillimeter'
@ -12,7 +11,7 @@ import { FormattedMessage } from 'react-intl'
import { injectIntl } from 'react-intl'
import RightIcon from '@material-ui/icons/KeyboardArrowRight'
const OptionGroup = props => {
const OptionGroup = (props) => {
const renderOption = (name, sub = false) => {
let option = props.config.options[name]
let type = optionType(option)
@ -66,12 +65,10 @@ const OptionGroup = props => {
}
return (
<React.Fragment>
{props.options.map(name => {
//let key = name;
<>
{props.options.map((name) => {
let output = []
if (typeof name === 'object') {
//key = Object.keys(name).pop();
// Subgroup
for (let subGroup of Object.keys(name)) {
let children = []
@ -90,16 +87,8 @@ const OptionGroup = props => {
return output
})}
</React.Fragment>
</>
)
}
OptionGroup.propTypes = {
config: PropTypes.object.isRequired,
options: PropTypes.array.isRequired,
units: PropTypes.oneOf(['metric', 'imperial']).isRequired
}
OptionGroup.defaultProps = {}
export default injectIntl(OptionGroup)

View file

@ -1,11 +1,25 @@
import React from 'react'
import PropTypes from 'prop-types'
import IconButton from '@material-ui/core/IconButton'
import RightIcon from '@material-ui/icons/KeyboardArrowRight'
import ResetIcon from '@material-ui/icons/SettingsBackupRestore'
import { injectIntl } from 'react-intl'
const OptionPreamble = props => {
const OptionPreamble = ({
intl,
title,
desc,
dflt,
designDflt,
option,
value,
displayValue,
displayFormat = 'node',
sameButDifferent,
expanded,
toggleExpanded,
reset,
designReset
}) => {
const styles = {
container: {
display: 'flex',
@ -22,43 +36,41 @@ const OptionPreamble = props => {
}
}
const resetLabel = props.intl.formatMessage({
const resetLabel = intl.formatMessage({
id: 'app.restoreDefaults',
defaultMessage: ' ♻️ '
})
const resetDesignLabel = props.intl.formatMessage({
const resetDesignLabel = intl.formatMessage({
id: 'app.restoreDesignDefaults',
defaultMessage: ' ♻️ '
})
const resetPatternLabel = props.intl.formatMessage({
const resetPatternLabel = intl.formatMessage({
id: 'app.restorePatternDefaults',
defaultMessage: ' ♻️ '
})
let pattern = false
if (props.dflt !== props.designDflt) pattern = true
let displayClass = props.value === props.dflt ? 'dflt' : 'custom'
if (pattern && props.value === props.designDflt) displayClass = 'p-dflt'
else if (pattern && props.sameButDifferent) displayClass = 'custom'
let displayValue = <span className={displayClass}>{props.displayValue}</span>
if (dflt !== designDflt) pattern = true
let displayClass = value === dflt ? 'dflt' : 'custom'
if (pattern && value === designDflt) displayClass = 'p-dflt'
else if (pattern && sameButDifferent) displayClass = 'custom'
let dspValue = <span className={displayClass}>{displayValue}</span>
if (props.displayFormat === 'html')
displayValue = (
<span className={displayClass} dangerouslySetInnerHTML={{ __html: props.displayValue }} />
)
if (displayFormat === 'html')
dspValue = <span className={displayClass} dangerouslySetInnerHTML={{ __html: displayValue }} />
return (
<React.Fragment>
<div onClick={props.toggleExpanded} style={styles.container}>
<div onClick={toggleExpanded} style={styles.container}>
<div style={styles.left}>
<RightIcon className={'icon-col-exp ' + (props.expanded ? 'expanded' : 'collapsed')} />
{props.title}
<RightIcon className={'icon-col-exp ' + (expanded ? 'expanded' : 'collapsed')} />
{title}
</div>
<div style={styles.right}>{displayValue}</div>
<div style={styles.right}>{dspValue}</div>
</div>
<div className={props.expanded ? 'col-exp expanded' : 'col-exp collapsed'}>
<div className={expanded ? 'col-exp expanded' : 'col-exp collapsed'}>
<div style={styles.container}>
<div style={styles.left}>
<p>{props.desc}</p>
<p>{desc}</p>
</div>
<div style={styles.right}>
{pattern ? (
@ -66,8 +78,8 @@ const OptionPreamble = props => {
title={resetDesignLabel}
aria-label={resetDesignLabel}
color="primary"
disabled={props.value === props.designDflt ? true : false}
onClick={props.designReset}
disabled={value === designDflt ? true : false}
onClick={designReset}
className="mini-icon-btn pattern"
>
<ResetIcon />
@ -77,40 +89,18 @@ const OptionPreamble = props => {
title={pattern ? resetPatternLabel : resetLabel}
aria-label={pattern ? resetPatternLabel : resetLabel}
color="primary"
disabled={props.value === props.dflt && !props.sameButDifferent ? true : false}
onClick={props.reset}
disabled={value === dflt && !sameButDifferent ? true : false}
onClick={reset}
className={'mini-icon-btn' + (pattern ? ' pattern' : '')}
>
<ResetIcon />
</IconButton>
</div>
</div>
{props.option}
{option}
</div>
</React.Fragment>
)
}
OptionPreamble.propTypes = {
dflt: PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired,
PropTypes.bool.isRequired
]),
value: PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired,
PropTypes.bool.isRequired
]),
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
reset: PropTypes.func.isRequired,
expanded: PropTypes.bool,
displayFormat: PropTypes.string
}
OptionPreamble.defaultProps = {
displayFormat: 'node'
}
export default injectIntl(OptionPreamble)

View file

@ -1,9 +1,8 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import FormFieldBool from '../../.form/FormFieldBool'
import OptionPreamble from '../OptionPreamble'
const PatternOptionBool = props => {
const PatternOptionBool = (props) => {
const [value, setValue] = useState(props.value === null ? props.dflt : props.value)
const [expanded, setExpanded] = useState(false)
@ -62,18 +61,4 @@ const PatternOptionBool = props => {
)
}
PatternOptionBool.propTypes = {
raiseEvent: PropTypes.func.isRequired,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
dflt: PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired,
PropTypes.bool.isRequired
]),
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
labels: PropTypes.array.isRequired
}
export default PatternOptionBool

View file

@ -1,9 +1,8 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import FormFieldList from '../../.form/FormFieldList'
import OptionPreamble from '../OptionPreamble'
const PatternOptionList = props => {
const PatternOptionList = (props) => {
const [value, setValue] = useState(props.dflt)
const [expanded, setExpanded] = useState(false)
@ -70,13 +69,4 @@ const PatternOptionList = props => {
)
}
PatternOptionList.propTypes = {
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
dflt: PropTypes.oneOfType([PropTypes.number.isRequired, PropTypes.string.isRequired]),
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
list: PropTypes.array.isRequired
}
export default PatternOptionList

View file

@ -1,94 +1,85 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import sliderStep from "@freesewing/utils/sliderStep";
import roundMm from "@freesewing/utils/roundMm";
import roundMmUp from "@freesewing/utils/roundMmUp";
import roundMmDown from "@freesewing/utils/roundMmDown";
import formatMm from "@freesewing/utils/formatMm";
import FormFieldSlider from "../../.form/FormFieldSlider";
import OptionPreamble from "../OptionPreamble";
import React, { useState } from 'react'
import sliderStep from '@freesewing/utils/sliderStep'
import roundMm from '@freesewing/utils/roundMm'
import roundMmUp from '@freesewing/utils/roundMmUp'
import roundMmDown from '@freesewing/utils/roundMmDown'
import formatMm from '@freesewing/utils/formatMm'
import FormFieldSlider from '../../.form/FormFieldSlider'
import OptionPreamble from '../OptionPreamble'
const PatternOptionMillimeter = props => {
const [value, setValue] = useState(props.dflt);
const [previousValue, setPreviousValue] = useState(props.dflt);
const [expanded, setExpanded] = useState(false);
const PatternOptionMillimeter = ({
title = false,
desc = false,
units = 'metric',
min = 0,
max = 100,
updateValue,
name,
dflt,
noDocs
}) => {
const [val, setVal] = useState(dflt)
const [previousValue, setPreviousValue] = useState(dflt)
const [expanded, setExpanded] = useState(false)
const update = (name, newValue, evt) => {
newValue = roundMm(newValue, props.units);
newValue = roundMm(newValue, units)
// Sometimes, when sliding, the rapid succession of updates
// causes a weird timing issue to result in a value that is NaN.
// If that's the case, just ignore this update and keep the
// previous one instead
if (!isNaN(newValue)) {
setValue(newValue);
if (evt.type !== "mousemove") props.updateValue(props.name, newValue);
setVal(newValue)
if (evt.type !== 'mousemove') updateValue(name, newValue)
} else {
if (evt.type !== "mousemove") props.updateValue(props.name, value);
if (evt.type !== 'mousemove') updateValue(name, val)
}
};
}
const reset = () => {
setValue(props.dflt);
props.updateValue(props.name, props.dflt);
};
setVal(dflt)
updateValue(name, dflt)
}
const toggleExpanded = () => setExpanded(!expanded);
const toggleExpanded = () => setExpanded(!expanded)
let option = (
<FormFieldSlider
name={props.name}
value={value}
min={roundMmUp(props.min, props.units)}
max={roundMmDown(props.max, props.units)}
step={sliderStep[props.units]}
name={name}
value={val}
min={roundMmUp(min, units)}
max={roundMmDown(units)}
step={sliderStep[units]}
onChange={update}
label={"po-mm-" + props.name}
label={'po-mm-' + name}
updateValue={update}
/>
);
)
return (
<li>
<OptionPreamble
dflt={props.dflt}
value={value}
desc={props.desc}
title={props.title}
id={"po-mm-" + props.name}
displayValue={formatMm(value, props.units)}
dflt={dflt}
value={val}
desc={desc}
title={title}
id={'po-mm-' + name}
displayValue={formatMm(val, units)}
displayFormat="html"
reset={reset}
toggleExpanded={toggleExpanded}
expanded={expanded}
showHelp={() =>
props.raiseEvent("showHelp", {
type: "patternOption",
value: props.name
raiseEvent('showHelp', {
type: 'patternOption',
value: name
})
}
option={option}
noDocs={props.noDocs}
noDocs={noDocs}
/>
</li>
);
};
)
}
PatternOptionMillimeter.propTypes = {
min: PropTypes.number,
max: PropTypes.number,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
dflt: PropTypes.number.isRequired,
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
};
PatternOptionMillimeter.defaultProps = {
min: 0,
max: 100,
title: false,
desc: false
};
export default PatternOptionMillimeter;
export default PatternOptionMillimeter

View file

@ -1,18 +1,27 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import FormFieldSlider from '../../.form/FormFieldSlider'
import OptionPreamble from '../OptionPreamble'
const PatternOptionPctDegCount = props => {
const PatternOptionPctDegCount = ({
min = 0,
max = 100,
step = 0.1,
type = 'pct',
updateValue,
name,
dflt,
designDflt,
title,
desc,
value,
raiseEvent,
noDocs
}) => {
let factor = 1
if (props.type === 'pct') factor = 100
const round = val => Math.round(val * 10) / 10
const [value, setValue] = useState(
props.value === null ? props.dflt : round(props.value * factor)
)
const [previousValue, setPreviousValue] = useState(
props.value === null ? props.dflt : round(props.value * factor)
)
if (type === 'pct') factor = 100
const round = (val) => Math.round(val * 10) / 10
const [val, setVal] = useState(value === null ? dflt : round(value * factor))
const [previousValue, setPreviousValue] = useState(value === null ? dflt : round(value * factor))
const [expanded, setExpanded] = useState(false)
const update = (name, newValue, evt) => {
@ -22,38 +31,38 @@ const PatternOptionPctDegCount = props => {
// If that's the case, just ignore this update and keep the
// previous one instead
if (!isNaN(newValue)) {
setValue(newValue)
if (evt.type !== 'mousemove') props.updateValue(props.name, newValue / factor)
setVal(newValue)
if (evt.type !== 'mousemove') updateValue(name, newValue / factor)
} else {
if (evt.type !== 'mousemove') props.updateValue(props.name, value / factor)
if (evt.type !== 'mousemove') updateValue(name, value / factor)
}
}
const reset = () => {
setValue(props.dflt)
props.updateValue(props.name, props.dflt / factor)
setVal(dflt)
updateValue(name, dflt / factor)
}
const patternReset = () => {
setValue(props.designDflt)
props.updateValue(props.name, props.designDflt / factor)
setVal(designDflt)
updateValue(name, designDflt / factor)
}
const toggleExpanded = () => setExpanded(!expanded)
let unit = ''
if (props.type === 'pct') unit = '%'
if (props.type === 'deg') unit = '°'
if (type === 'pct') unit = '%'
if (type === 'deg') unit = '°'
let option = (
<FormFieldSlider
name={props.name}
value={value}
min={props.min}
max={props.max}
step={props.type === 'count' ? 1 : props.step}
name={name}
value={val}
min={min}
max={max}
step={type === 'count' ? 1 : step}
onChange={update}
label={'po-' + props.type + '-' + props.name}
label={'po-' + type + '-' + name}
updateValue={update}
/>
)
@ -61,47 +70,28 @@ const PatternOptionPctDegCount = props => {
return (
<li>
<OptionPreamble
dflt={props.dflt}
designDflt={props.designDflt}
value={value}
desc={props.desc}
title={props.title}
id={'po-' + props.type + '-' + props.name}
displayValue={value + unit}
dflt={dflt}
designDflt={designDflt}
value={val}
desc={desc}
title={title}
id={'po-' + type + '-' + name}
displayValue={val + unit}
reset={reset}
patternReset={patternReset}
toggleExpanded={toggleExpanded}
expanded={expanded}
showHelp={() =>
props.raiseEvent('showHelp', {
raiseEvent('showHelp', {
type: 'patternOption',
value: props.name
value: name
})
}
option={option}
noDocs={props.noDocs}
noDocs={noDocs}
/>
</li>
)
}
PatternOptionPctDegCount.propTypes = {
min: PropTypes.number,
max: PropTypes.number,
step: PropTypes.number,
updateValue: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
dflt: PropTypes.number.isRequired,
title: PropTypes.node.isRequired,
desc: PropTypes.node.isRequired,
type: PropTypes.oneOf(['pct', 'deg', 'count'])
}
PatternOptionPctDegCount.defaultProps = {
min: 0,
max: 100,
step: 0.1,
type: 'pct'
}
export default PatternOptionPctDegCount

View file

@ -1,12 +1,11 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import OptionGroup from '../OptionGroup'
import { FormattedMessage } from 'react-intl'
import RightIcon from '@material-ui/icons/KeyboardArrowRight'
const PatternOptions = props => {
const PatternOptions = (props) => {
const [expanded, setExpanded] = useState([])
const toggleGroup = group => {
const toggleGroup = (group) => {
let shown = expanded.slice(0)
let index = shown.indexOf(group)
if (index === -1) shown.push(group)
@ -14,7 +13,7 @@ const PatternOptions = props => {
setExpanded(shown)
}
const renderGroup = group => {
const renderGroup = (group) => {
let open = true
if (expanded.indexOf(group) === -1) open = false
let output = []
@ -58,11 +57,4 @@ const PatternOptions = props => {
return <ul className="config l2">{children}</ul>
}
PatternOptions.propTypes = {
config: PropTypes.object.isRequired,
raiseEvent: PropTypes.func
}
PatternOptions.defaultProps = {}
export default PatternOptions

View file

@ -1,11 +1,25 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import { FormattedMessage } from 'react-intl'
import PatternOptions from './PatternOptions'
import DraftSettings from './DraftSettings'
const DraftConfigurator = props => {
const [expanded, setExpanded] = useState([])
const DraftConfigurator = ({
noDocs = false,
units = 'metric',
config = {},
data = {},
pattern,
updatePatternData,
raiseEvent
}) => {
let childProps = {
noDocs,
units,
config,
data,
pattern,
raiseEvent
}
return (
<ul className="config l1">
<li>
@ -13,13 +27,8 @@ const DraftConfigurator = props => {
<FormattedMessage id="app.designOptions" />
</span>
<PatternOptions
noDocs={props.noDocs}
config={props.config}
data={props.data}
pattern={props.pattern}
updateValue={(name, value) => props.updatePatternData(value, 'settings', 'options', name)}
raiseEvent={props.raiseEvent}
units={props.units}
{...childProps}
updateValue={(name, value) => updatePatternData(value, 'settings', 'options', name)}
/>
</li>
<li>
@ -27,25 +36,12 @@ const DraftConfigurator = props => {
<FormattedMessage id="app.patternOptions" />
</span>
<DraftSettings
noDocs={props.noDocs}
config={props.config}
data={props.data}
pattern={props.pattern}
updateValue={(name, value) => props.updatePatternData(value, 'settings', name)}
raiseEvent={props.raiseEvent}
units={props.units}
{...childProps}
updateValue={(name, value) => updatePatternData(value, 'settings', name)}
/>
</li>
</ul>
)
}
DraftConfigurator.propTypes = {
units: PropTypes.oneOf(['metric', 'imperial']).isRequired
}
DraftConfigurator.defaultProps = {
noDocs: false
}
export default DraftConfigurator

View file

@ -1,21 +1,10 @@
import React from "react";
import PropTypes from "prop-types";
import React from 'react'
const Emblem = props => (
const Emblem = (props) => (
<React.Fragment>
<span className="emb">{props.t1}</span>
<span className="lem">{props.t2}</span>
<span className="emb">{props.t1 || 'Free'}</span>
<span className="lem">{props.t2 || 'Sewing'}</span>
</React.Fragment>
);
)
Emblem.propTypes = {
t1: PropTypes.string,
t2: PropTypes.string
};
Emblem.defaultProps = {
t1: "Free",
t2: "Sewing"
};
export default Emblem;
export default Emblem

View file

@ -1,41 +1,47 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import examples from "@freesewing/examples";
import rendertest from "@freesewing/rendertest";
import tutorial from "@freesewing/tutorial";
import Draft from "../Draft";
import Design from "../Workbench/Design";
import IconButton from "@material-ui/core/IconButton";
import ResetIcon from "@material-ui/icons/SettingsBackupRestore";
import Switch from "@material-ui/core/Switch";
import React, { useState } from 'react'
import examples from '@freesewing/examples'
import rendertest from '@freesewing/rendertest'
import tutorial from '@freesewing/tutorial'
import Draft from '../Draft'
import Design from '../Workbench/Design'
import IconButton from '@material-ui/core/IconButton'
import ResetIcon from '@material-ui/icons/SettingsBackupRestore'
import Switch from '@material-ui/core/Switch'
const Example = props => {
const [design, setDesign] = useState(false);
const [focus, setFocus] = useState(null);
const Example = ({
pattern = 'examples',
design = true,
caption = '',
options = {},
settings,
part = '',
sample
}) => {
const [designMode, setDesignMode] = useState(false)
const [focus, setFocus] = useState(null)
const raiseEvent = (type, data) => {
if (type === "clearFocusAll") return setFocus(null);
let f = {};
if (focus !== null) f = { ...focus };
if (typeof f[data.part] === "undefined")
f[data.part] = { paths: [], points: [], coords: [] };
if (type === "point") f[data.part].points.push(data.name);
else if (type === "path") f[data.part].paths.push(data.name);
else if (type === "coords") f[data.part].coords.push(data.coords);
else if (type === "clearFocus") {
let i = focus[data.part][data.type].indexOf(data.name);
f[data.part][data.type].splice(i, 1);
if (type === 'clearFocusAll') return setFocus(null)
let f = {}
if (focus !== null) f = { ...focus }
if (typeof f[data.part] === 'undefined') f[data.part] = { paths: [], points: [], coords: [] }
if (type === 'point') f[data.part].points.push(data.name)
else if (type === 'path') f[data.part].paths.push(data.name)
else if (type === 'coords') f[data.part].coords.push(data.coords)
else if (type === 'clearFocus') {
let i = focus[data.part][data.type].indexOf(data.name)
f[data.part][data.type].splice(i, 1)
}
setFocus(f);
};
setFocus(f)
}
let focusCount = 0;
let focusCount = 0
if (focus !== null) {
for (let p of Object.keys(focus)) {
for (let i in focus[p].points) focusCount++;
for (let i in focus[p].paths) focusCount++;
for (let i in focus[p].coords) focusCount++;
for (let i in focus[p].points) focusCount++
for (let i in focus[p].paths) focusCount++
for (let i in focus[p].coords) focusCount++
}
}
@ -43,75 +49,49 @@ const Example = props => {
examples,
rendertest,
tutorial
};
const settings = {
options: { ...props.options },
}
settings = {
options: { ...options },
measurements: { headCircumference: 390 },
...props.settings
};
if (props.part !== "") settings.only = [props.part];
const pattern = new patterns[props.pattern](settings);
...settings
}
if (part !== '') settings.only = [part]
const patternInstance = new patterns[pattern](settings)
if (props.sample) pattern.sample();
else pattern.draft();
const patternProps = pattern.getRenderProps();
if (sample) patternInstance.sample()
else patternInstance.draft()
const patternProps = patternInstance.getRenderProps()
return (
<figure className={design ? "design example" : "example"}>
<figure className={designMode ? 'design example' : 'example'}>
<div className="example">
{props.design ? (
{designMode ? (
<div className="actions">
{design ? (
<IconButton
color="primary"
onClick={() => raiseEvent("clearFocusAll", null)}
>
<ResetIcon />
</IconButton>
) : null}
<IconButton color="primary" onClick={() => raiseEvent('clearFocusAll', null)}>
<ResetIcon />
</IconButton>
<Switch
checked={design}
onChange={() => setDesign(!design)}
value={design}
checked={designMode}
onChange={() => setDesignMode(!designMode)}
value={designMode}
color="primary"
/>
</div>
) : null}
<Draft
{...patternProps}
design={design}
focus={focus}
raiseEvent={raiseEvent}
/>
<Draft {...patternProps} design={design} focus={focus} raiseEvent={raiseEvent} />
</div>
<figcaption>{props.caption}</figcaption>
{design ? (
<figcaption>{caption}</figcaption>
{designMode && (
<div className="design">
<Design
focus={focus}
design={design}
design={designMode}
raiseEvent={raiseEvent}
parts={patternProps.parts}
/>
</div>
) : null}
)}
</figure>
);
};
)
}
Example.propTypes = {
pattern: PropTypes.string,
design: PropTypes.bool,
caption: PropTypes.string,
part: PropTypes.string,
options: PropTypes.obj
};
Example.defaultProps = {
pattern: "examples",
design: true,
caption: "",
options: {},
part: ""
};
export default Example;
export default Example

View file

@ -1,40 +1,24 @@
import React from "react";
import PropTypes from "prop-types";
import icons from "./icons";
import React from 'react'
import icons from './icons'
const Icon = props => {
return (
<svg
style={props.style}
className={props.className}
xmlns="http://www.w3.org/2000/svg"
width={props.size}
height={props.size}
viewBox={props.viewBox}
>
<path
stroke="none"
fill={props.color ? props.color : "currentColor"}
d={icons[props.icon]}
/>
</svg>
);
};
const Icon = ({
size = 24,
viewBox = '0 0 24 24',
className = '',
icon = 'github',
color = false,
style = {}
}) => (
<svg
style={style}
className={className}
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox={viewBox}
>
<path stroke="none" fill={color ? color : 'currentColor'} d={icons[icon]} />
</svg>
)
Icon.propTypes = {
size: PropTypes.number,
viewBox: PropTypes.string,
icon: PropTypes.string,
style: PropTypes.object
};
Icon.defaultProps = {
size: 24,
viewBox: "0 0 24 24",
className: "",
icon: "github",
color: false,
style: {}
};
export default Icon;
export default Icon

View file

@ -1,11 +1,10 @@
import React from 'react'
import PropTypes from 'prop-types'
import patterns from './patterns'
const LineDrawing = props => {
const LineDrawing = (props) => {
const attr = {
style: props.style,
className: 'fs linedrawing ' + props.className,
style: props.style || {},
className: 'fs linedrawing ' + (props.className || ''),
xmlns: 'http://www.w3.org/2000/svg',
viewBox: '0 0 270 270'
}
@ -13,20 +12,7 @@ const LineDrawing = props => {
attr.width = props.size
attr.height = props.size
}
return <svg {...attr}>{patterns[props.pattern] || null}</svg>
}
LineDrawing.propTypes = {
size: PropTypes.number,
pattern: PropTypes.string,
style: PropTypes.object
}
LineDrawing.defaultProps = {
size: 0,
className: '',
pattern: 'aaron',
style: {}
return <svg {...attr}>{patterns[props.pattern || 'aaron'] || null}</svg>
}
export default LineDrawing

File diff suppressed because one or more lines are too long

View file

@ -1,11 +1,15 @@
import React from 'react'
import PropTypes from 'prop-types'
import Logo from '../Logo'
import Emblem from '../Emblem'
import { FormattedMessage } from 'react-intl'
import useMediaQuery from '@material-ui/core/useMediaQuery'
const Navbar = props => {
const Navbar = ({
home = 'https://freesewing.org/',
navs = { left: [], right: [], mleft: {}, mright: {} },
logo = <Logo embed color="#e9ecef" />,
emblem = <Emblem />
}) => {
const mobile = useMediaQuery('(max-width:599px)')
if (mobile) return null
@ -47,49 +51,36 @@ const Navbar = props => {
let homeProps = {
href: '#home'
}
if (typeof props.home === 'function') homeProps.onClick = props.home
else homeProps.href = props.home
if (typeof home === 'function') homeProps.onClick = home
else homeProps.href = home
let logo = (
let logoDiv = (
<div className="logo">
<a id="home" {...homeProps} data-test="navbar-home">
{props.logo}
{logo}
</a>
</div>
)
let emblem = (
let emblemDiv = (
<div className="emblem">
<a {...homeProps}>{props.emblem}</a>
<a {...homeProps}>{emblem}</a>
</div>
)
return (
<header className="navbar">
<div>
<div style={{ display: 'flex' }}>
{logo}
{emblem}
{Object.keys(props.navs.left).map(key => renderNav(key, props.navs.left[key]))}
{logoDiv}
{emblemDiv}
{Object.keys(navs.left).map((key) => renderNav(key, navs.left[key]))}
</div>
<div className="spread" />
<div style={{ display: 'flex' }}>
{Object.keys(props.navs.right).map(key => renderNav(key, props.navs.right[key]))}
{Object.keys(navs.right).map((key) => renderNav(key, navs.right[key]))}
</div>
</div>
</header>
)
}
Navbar.propTypes = {
navs: PropTypes.object,
logo: PropTypes.node,
emblem: PropTypes.node,
home: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
}
Navbar.defaultProps = {
home: 'https://freesewing.org/',
navs: { left: [], right: [], mleft: {}, mright: {} },
logo: <Logo embed color="#e9ecef" />,
emblem: <Emblem />
}
export default Navbar

File diff suppressed because one or more lines are too long

View file

@ -1,38 +1,23 @@
import React from "react";
import PropTypes from "prop-types";
import poses from "./poses";
import React from 'react'
import poses from './poses'
const Robot = props => {
return (
<svg
className={props.className}
xmlns="http://www.w3.org/2000/svg"
width={props.embed ? "" : props.size}
height={props.embed ? "" : props.size}
viewBox={props.viewBox}
>
<path
stroke="none"
fill={props.color ? props.color : "currentColor"}
d={poses[props.pose]}
/>
</svg>
);
};
const Robot = ({
size = 124,
viewBox = '0 0 500 500',
className = '',
pose = 'yay',
color = false,
embed = false
}) => (
<svg
className={className || ''}
xmlns="http://www.w3.org/2000/svg"
width={embed ? '' : size || 124}
height={embed ? '' : size || 124}
viewBox={viewBox || '0 0 500 500'}
>
<path stroke="none" fill={color ? color : 'currentColor'} d={poses[pose]} />
</svg>
)
Robot.propTypes = {
size: PropTypes.number,
viewBox: PropTypes.string,
pose: PropTypes.string,
embed: PropTypes.bool
};
Robot.defaultProps = {
size: 124,
viewBox: "0 0 500 500",
className: "",
pose: "yay",
color: false
};
export default Robot;
export default Robot

View file

@ -1,12 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import { injectIntl } from 'react-intl'
const OptionGroup = props => {
const OptionGroup = (props) => {
return (
<React.Fragment>
{props.options.map(name => {
{props.options.map((name) => {
let output = []
if (typeof name === 'object') {
// Subgroup
@ -44,11 +43,4 @@ const OptionGroup = props => {
)
}
OptionGroup.propTypes = {
config: PropTypes.object.isRequired,
options: PropTypes.array.isRequired
}
OptionGroup.defaultProps = {}
export default injectIntl(OptionGroup)

View file

@ -1,46 +1,38 @@
import React from "react";
import PropTypes from "prop-types";
import OptionGroup from "../OptionGroup";
import { FormattedMessage } from "react-intl";
import React from 'react'
import OptionGroup from '../OptionGroup'
import { FormattedMessage } from 'react-intl'
const PatternOptions = props => {
const renderGroup = group => {
let output = [];
const PatternOptions = (props) => {
const renderGroup = (group) => {
let output = []
let children = (
<ul className="links">
<OptionGroup
key={group + "-group"}
key={group + '-group'}
units={props.units}
config={props.config}
options={props.config.optionGroups[group]}
sampleOption={props.sampleOption}
/>
</ul>
);
)
output.push(
<li key={group + "-ghead"} className="nodot">
<li key={group + '-ghead'} className="nodot">
<h3>
<FormattedMessage id={"optiongroups." + group} />
<FormattedMessage id={'optiongroups.' + group} />
</h3>
{children}
</li>
);
)
return output;
};
return output
}
return (
<ul className="links">
{Object.keys(props.config.optionGroups).map(group => renderGroup(group))}
{Object.keys(props.config.optionGroups).map((group) => renderGroup(group))}
</ul>
);
};
)
}
PatternOptions.propTypes = {
config: PropTypes.object.isRequired,
sampleOption: PropTypes.func.isRequired
};
PatternOptions.defaultProps = {};
export default PatternOptions;
export default PatternOptions

View file

@ -1,12 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
const Spinner = props => {
const Spinner = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={props.embed ? '' : props.size}
height={props.embed ? '' : props.size}
width={props.embed ? '' : props.size || 200}
height={props.embed ? '' : props.size || 200}
viewBox="-28 -28 108 108"
className={'spinner ' + props.className}
>
@ -65,14 +64,4 @@ const Spinner = props => {
)
}
Spinner.propTypes = {
size: PropTypes.number,
embed: PropTypes.bool
}
Spinner.defaultProps = {
size: 200,
embed: false
}
export default Spinner

View file

@ -1,5 +1,4 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import Draft from '../../Draft'
import Zoombox from '../Zoombox'
import Design from '../Design'
@ -115,7 +114,7 @@ const DraftPattern = (props) => {
return (
<div className="fs-sa">
<section>
<section style={{ margin: '1rem' }}>
<Draft
{...patternProps}
design={design}
@ -224,7 +223,7 @@ const DraftPattern = (props) => {
updatePatternData={props.updateGist}
raiseEvent={props.raiseEvent}
freesewing={props.freesewing}
units={props.units}
units={props.units || 'metric'}
/>
</div>
</aside>
@ -232,18 +231,4 @@ const DraftPattern = (props) => {
)
}
DraftPattern.propTypes = {
gist: PropTypes.object.isRequired,
updateGist: PropTypes.func.isRequired,
config: PropTypes.object.isRequired,
raiseEvent: PropTypes.func.isRequired,
Pattern: PropTypes.func.isRequired,
units: PropTypes.oneOf(['metric', 'imperial'])
}
DraftPattern.defaultProps = {
units: 'metric',
pointInfo: null
}
export default DraftPattern

View file

@ -1,31 +1,30 @@
import React from "react";
import PropTypes from "prop-types";
import Button from "@material-ui/core/Button";
import { FormattedMessage, FormattedHTMLMessage } from "react-intl";
import FormFieldMeasurement from "../../.form/FormFieldMeasurement";
import { withBreasts, withoutBreasts } from "@freesewing/models";
import React from 'react'
import Button from '@material-ui/core/Button'
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl'
import FormFieldMeasurement from '../../.form/FormFieldMeasurement'
import { withBreasts, withoutBreasts } from '@freesewing/models'
const Measurements = props => {
const Measurements = (props) => {
const styles = {
container: {
display: "flex",
flexDirection: "row",
width: "100%",
minHeight: "70vh"
display: 'flex',
flexDirection: 'row',
width: '100%',
minHeight: '70vh'
},
chooser: {
width: "100%",
maxWidth: "500px",
margin: "auto",
alignSelf: "center"
width: '100%',
maxWidth: '500px',
margin: 'auto',
alignSelf: 'center'
}
};
}
const getValue = m => {
if (props.measurements === null) return "";
if (typeof props.measurements[m] === "undefined") return "";
return props.measurements[m];
};
const getValue = (m) => {
if (props.measurements === null) return ''
if (typeof props.measurements[m] === 'undefined') return ''
return props.measurements[m]
}
if (props.required.length < 1)
return (
@ -43,18 +42,14 @@ const Measurements = props => {
<p>
<FormattedMessage id="cfp.seeDocsAt" />
&nbsp;
<a
href={
"https://" + props.language + "/.freesewing.dev/core/config"
}
>
<a href={'https://' + props.language + '/.freesewing.dev/core/config'}>
{props.language}
.freesewing.dev/core/config
</a>
</p>
</div>
</div>
);
)
return (
<div style={styles.container}>
<div style={styles.chooser}>
@ -79,13 +74,13 @@ const Measurements = props => {
<h3 id="manual">
<FormattedMessage id="cfp.enterMeasurements" />
</h3>
{props.required.map(m => (
{props.required.map((m) => (
<FormFieldMeasurement
key={m}
name={m}
units={props.units}
value={getValue(m)}
label={"measurements." + m}
label={'measurements.' + m}
updateValue={props.updateMeasurement}
/>
))}
@ -96,11 +91,9 @@ const Measurements = props => {
<FormattedMessage id="app.withoutBreasts" />
</h4>
<ul>
{Object.keys(withoutBreasts).map(m => (
{Object.keys(withoutBreasts).map((m) => (
<li key={m}>
<Button
onClick={() => props.preloadMeasurements(withoutBreasts[m])}
>
<Button onClick={() => props.preloadMeasurements(withoutBreasts[m])}>
<FormattedMessage id="cfp.size" />
&nbsp;
{m.slice(-2)}
@ -112,7 +105,7 @@ const Measurements = props => {
<FormattedMessage id="app.withBreasts" />
</h4>
<ul>
{Object.keys(withBreasts).map(m => (
{Object.keys(withBreasts).map((m) => (
<li key={m}>
<Button onClick={() => props.preloadMeasurements(withBreasts[m])}>
<FormattedMessage id="cfp.size" />
@ -124,15 +117,7 @@ const Measurements = props => {
</ul>
</div>
</div>
);
};
)
}
Measurements.propTypes = {
measurements: PropTypes.object.isRequired,
required: PropTypes.array.isRequired,
units: PropTypes.oneOf(["metric", "imperial"]),
updateMeasurement: PropTypes.func.isRequired,
preloadMeasurements: PropTypes.func.isRequired
};
export default Measurements;
export default Measurements

View file

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import SampleConfigurator from '../../SampleConfigurator'
import svgattrPlugin from '@freesewing/plugin-svgattr'
import { FormattedMessage } from 'react-intl'
@ -35,7 +34,7 @@ const SamplePattern = (props) => {
updateGist={props.updateGist}
raiseEvent={props.raiseEvent}
freesewing={props.freesewing}
units={props.units}
units={props.units || 'metric'}
/>
</div>
</aside>
@ -43,18 +42,4 @@ const SamplePattern = (props) => {
)
}
SamplePattern.propTypes = {
gist: PropTypes.object.isRequired,
updateGist: PropTypes.func.isRequired,
config: PropTypes.object.isRequired,
raiseEvent: PropTypes.func.isRequired,
Pattern: PropTypes.func.isRequired,
units: PropTypes.oneOf(['metric', 'imperial'])
}
SamplePattern.defaultProps = {
units: 'metric',
pointInfo: null
}
export default SamplePattern

View file

@ -1,30 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import Logo from "../../Logo";
import FormattedMessage from "react-intl";
import Button from "@material-ui/core/Button";
const Welcome = props => {
const style = {
textAlign: "center"
}
return (
<div className="fs-sa">
<section style={{style}}>
<div><Logo size={250} /></div>
<h1><FormattedMessage id="app.welcome" /></h1>
<p><FormattedMessage id="cfp.devDocsAvailableAt" /> <a href="https://freesewing.dev/">freesewing.dev</a></p>
<p><FormattedMessage id="cfp.talkToUs" />: <a href="https://gitter.im/freesewing/freesewing/">gitter.im/freesewing</a></p>
<Button
variant="outlined"
size="large"
onClick={() => props.setDisplay('pattern')}
><FormattedMessage id="app.docs" /></Button>
</section>
</div>
);
};
export default Welcome;

View file

@ -43,7 +43,6 @@ const Zoombox = (props) => {
setPanning(false)
setPanFrom(false)
updateViewBox(evt)
//props.setViewBox(`${from[0] * factor} ${from[1] * factor} ${to[0] * factor} ${to[1] * factor}`)
}
}
const handlePan = (evt) => {
@ -55,9 +54,9 @@ const Zoombox = (props) => {
// 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) {
} else if (to[0] + (evt.clientX - panFrom[0]) >= box.width + 5) {
// Bump into right
} else if (to[1] + (evt.clientY - panFrom[1]) >= box.height - 11) {
} else if (to[1] + (evt.clientY - panFrom[1]) >= box.height + 5) {
// Bump into bottom
} else {
setPanFrom([evt.clientX, evt.clientY])
@ -78,7 +77,12 @@ const Zoombox = (props) => {
if (dragging == 2) {
updateViewBox(evt)
if (falseAlarm) setFalseAlarm(false)
} else setFalseAlarm(true)
} else {
setFalseAlarm(true)
let box = ref.current.getBoundingClientRect()
setBox(box)
setFactor(props.patternProps.width / box.width)
}
setDragging(false)
setPanning(false)
evt.stopPropagation()
@ -93,20 +97,15 @@ const Zoombox = (props) => {
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 +
(to[0] - from[0]) * factor +
' ' +
(evt.clientY - box.y - from[1]) * factor
(to[1] - from[1]) * factor
)
}
@ -115,7 +114,6 @@ const Zoombox = (props) => {
<div
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseOver={handleMouseOver}
onMouseMove={handleMouseMove}
className="zoombox"
ref={ref}

View file

@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import withGist from '../withGist'
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
import Navbar from '../Navbar'
@ -17,7 +16,18 @@ import Welcome from './Welcome'
import Footer from '../Footer'
import Measurements from './Measurements'
const Workbench = (props) => {
const Workbench = ({
updateGist,
setLanguage,
userLanguage = 'en',
language = 'en',
gist,
importGist,
config,
freesewing,
Pattern,
units = 'metric'
}) => {
const [display, setDisplay] = useState(null)
const [pattern, setPattern] = useState(false)
const [theme, setTheme] = useState('light')
@ -27,27 +37,23 @@ const Workbench = (props) => {
useEffect(() => {
let m = getMeasurements()
setMeasurements(m)
props.updateGist(m, 'settings', 'measurements')
updateGist(m, 'settings', 'measurements')
setDisplay(getDisplay())
props.setLanguage(props.userLanguage || 'en')
setLanguage(userLanguage)
}, [])
useEffect(() => {
if (props.from) props.importGist(props.from)
}, [props.from])
useEffect(() => {
if (props.language !== props.gist.settings.locale)
props.updateGist(props.language, 'settings', 'locale')
}, [props.language])
if (language !== gist.settings.locale) updateGist(language, 'settings', 'locale')
}, [language])
const getDisplay = () => storage.get(props.config.name + '-display')
const getDisplay = () => storage.get(config.name + '-display')
const saveDisplay = (d) => {
setDisplay(d)
storage.set(props.config.name + '-display', d)
storage.set(config.name + '-display', d)
}
const getMeasurements = () => storage.get(props.config.name + '-measurements')
const getMeasurements = () => storage.get(config.name + '-measurements')
const saveMeasurements = (data) => {
storage.set(props.config.name + '-measurements', data)
props.updateGist(data, 'settings', 'measurements')
storage.set(config.name + '-measurements', data)
updateGist(data, 'settings', 'measurements')
}
const updateMeasurement = (name, val) => {
let updatedMeasurements = { ...measurements }
@ -64,7 +70,7 @@ const Workbench = (props) => {
saveMeasurements(updatedMeasurements)
}
const measurementsMissing = () => {
let required = props.config.measurements
let required = config.measurements
if (required.length < 1) return false
if (measurements === null) return true
for (let m of required) {
@ -110,7 +116,7 @@ const Workbench = (props) => {
version: {
type: 'link',
href: 'https://github.com/freesewing/freesewing/releases',
text: 'v' + props.freesewing.version
text: 'v' + freesewing.version
},
language: {
type: 'button',
@ -140,19 +146,19 @@ const Workbench = (props) => {
let main = null
switch (display) {
case 'languages':
main = <LanguageChooser setLanguage={props.setLanguage} setDisplay={saveDisplay} />
main = <LanguageChooser setLanguage={setLanguage} setDisplay={saveDisplay} />
break
case 'draft':
if (measurementsMissing()) saveDisplay('measurements')
main = (
<DraftPattern
freesewing={props.freesewing}
Pattern={props.Pattern}
config={props.config}
gist={props.gist}
updateGist={props.updateGist}
freesewing={freesewing}
Pattern={Pattern}
config={config}
gist={gist}
updateGist={updateGist}
raiseEvent={raiseEvent}
units={props.units}
units={units}
svgExport={svgExport}
setSvgExport={setSvgExport}
theme={theme}
@ -163,13 +169,13 @@ const Workbench = (props) => {
if (measurementsMissing()) saveDisplay('measurements')
main = (
<SamplePattern
freesewing={props.freesewing}
Pattern={props.Pattern}
config={props.config}
gist={props.gist}
updateGist={props.updateGist}
freesewing={freesewing}
Pattern={Pattern}
config={config}
gist={gist}
updateGist={updateGist}
raiseEvent={raiseEvent}
units={props.units}
units={units}
/>
)
break
@ -177,59 +183,49 @@ const Workbench = (props) => {
main = (
<Measurements
measurements={measurements}
required={props.config.measurements}
units={props.units}
required={config.measurements}
units={units}
updateMeasurement={updateMeasurement}
preloadMeasurements={preloadMeasurements}
language={props.language}
language={language}
/>
)
break
case 'json':
main = <Json gist={props.gist} />
main = <Json gist={gist} />
break
case 'inspect':
main = (
<InspectPattern
freesewing={props.freesewing}
Pattern={props.Pattern}
config={props.config}
gist={props.gist}
updateGist={props.updateGist}
freesewing={freesewing}
Pattern={Pattern}
config={config}
gist={gist}
updateGist={updateGist}
raiseEvent={raiseEvent}
units={props.units}
units={units}
svgExport={svgExport}
setSvgExport={setSvgExport}
/>
)
break
default:
main = <Welcome language={props.language} setDisplay={saveDisplay} />
main = <Welcome language={language} setDisplay={saveDisplay} />
}
const themes = { dark, light }
return (
<MuiThemeProvider theme={createMuiTheme(themes[theme])}>
<div className={theme === 'light' ? 'theme-wrapper light' : 'theme-wrapper dark'}>
{display !== 'welcome' ? <Navbar navs={navs} home={() => saveDisplay('welcome')} /> : null}
{main}
{display !== 'welcome' ? <Footer language={props.language} /> : null}
{display !== 'welcome' ? <Footer language={language} /> : null}
</div>
</MuiThemeProvider>
)
}
Workbench.propTypes = {
freesewing: PropTypes.object.isRequired,
Pattern: PropTypes.func.isRequired,
config: PropTypes.object.isRequired,
from: PropTypes.object
}
Workbench.defaultProps = {
from: { settings: { embed: true } }
}
export default withLanguage(
withGist(Workbench, {
gist: defaultGist,