1
0
Fork 0

🚧 Added sampling to workbench

This commit is contained in:
Joost De Cock 2019-05-07 16:57:41 +02:00
parent 3754680841
commit c2628a0a52
19 changed files with 463 additions and 33 deletions

View file

@ -0,0 +1,51 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { optionType } from "@freesewing/utils";
import { FormattedMessage } from "react-intl";
import { injectIntl } from "react-intl";
const OptionGroup = props => {
return (
<React.Fragment>
{props.options.map(name => {
let key = name;
let output = [];
if (typeof name === "object") {
key = Object.keys(name).pop();
// Subgroup
for (let subGroup of Object.keys(name)) {
output.push(
<h5 key={subGroup + "-title"} className="subheading">
<FormattedMessage id={"optiongroups." + subGroup} />
</h5>
);
let children = [];
for (let option of name[subGroup]) children.push(<p>{option}</p>);
output.push(<ul style={{ paddingLeft: "1rem" }}>{children}</ul>);
}
} else
output.push(
<li>
<a href="#logo" onClick={() => props.sampleOption(name)}>
<FormattedMessage
id={"options." + props.config.name + "." + name + ".title"}
/>
</a>
</li>
);
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

@ -0,0 +1,40 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import OptionGroup from ".";
const options = {
armholeDrop: { pct: 10, min: 1, max: 75 },
backlineBend: { pct: 50, min: 50, max: 100 },
chestEase: { pct: 8, min: 0, max: 20 },
hipsEase: { pct: 8, min: 0, max: 20 },
lengthBonus: { pct: 10, min: -20, max: 60 },
necklineBend: { pct: 100, min: 40, max: 100 },
necklineDrop: { pct: 20, min: 10, max: 35 },
stretchFactor: { pct: 5, min: 0, max: 15 },
shoulderStrapWidth: { pct: 15, min: 10, max: 40 },
shoulderStrapPlacement: { pct: 40, min: 20, max: 80 }
};
const props = {
raiseEvent: (type, data) =>
console.log(`Action of type ${type} triggered, data passed is`, data),
updateValue: (type, data) =>
console.log(`Update ${type} with new value`, data),
gist: {
settings: {
options: {}
}
},
pattern: {
config: {
name: "aaron",
options: options
}
},
dflts: { options: {} },
options: Object.keys(options)
};
storiesOf("Low level/OptionGroup", module).add("Simon metric", () => (
<OptionGroup pattern="simon" {...props} units="metric" />
));

View file

@ -0,0 +1,49 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import OptionGroup from "../OptionGroup";
import { optionType } from "@freesewing/utils";
import { FormattedMessage } from "react-intl";
import DownIcon from "@material-ui/icons/KeyboardArrowDown";
const PatternOptions = props => {
const renderGroup = group => {
let output = [];
let children = (
<ul className="links">
<OptionGroup
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">
<h3>
<FormattedMessage id={"optiongroups." + group} />
</h3>
{children}
</li>
);
return output;
};
return (
<ul className="links">
{Object.keys(props.config.optionGroups).map(group => renderGroup(group))}
</ul>
);
};
PatternOptions.propTypes = {
config: PropTypes.object.isRequired,
gist: PropTypes.object.isRequired,
sampleOption: PropTypes.func.isRequired
};
PatternOptions.defaultProps = {};
export default PatternOptions;

View file

@ -0,0 +1,28 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import PatternOptions from ".";
const props = {
raiseEvent: (type, data) =>
console.log(`Action of type ${type} triggered, data passed is`, data),
updateValue: (type, data) =>
console.log(`Update ${type} with new value`, data),
gist: {
settings: {
options: {}
}
}
};
storiesOf("Low level/PatternOptions", module)
.add("Simon metric", () => (
<PatternOptions pattern="simon" gist={false} units="metric" {...props} />
))
.add("Trayvon imperial", () => (
<PatternOptions
pattern="trayvon"
gist={false}
units="imperial"
{...props}
/>
));

View file

@ -0,0 +1,109 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { FormattedMessage } from "react-intl";
import PatternOptions from "./PatternOptions";
import models from "@freesewing/models";
const SampleConfigurator = props => {
const [expanded, setExpanded] = useState([]);
const toggleGroup = group => {
let shown = expanded.slice(0);
let index = shown.indexOf(group);
if (index === -1) shown.push(group);
else shown.splice(index, 1);
setExpanded(shown);
};
const sampleOption = option => {
props.updateGist(
{
type: "option",
option
},
"settings",
"sample"
);
};
const sampleMeasurement = measurement => {
props.updateGist(
{
type: "measurement",
measurement
},
"settings",
"sample"
);
};
const sampleModels = models => {
props.updateGist(
{
type: "models",
models
},
"settings",
"sample"
);
};
let antMan = {
ant: {},
man: models.manSize42
};
for (let m in models.manSize42) antMan.ant[m] = antMan.man[m] / 10;
return (
<ul className="links">
<li className="nodot">
<h2>
<FormattedMessage id="app.patternOptions" />
</h2>
<PatternOptions
config={props.config}
gist={props.gist}
sampleOption={sampleOption}
/>
</li>
<li className="nodot">
<h2>
<FormattedMessage id="app.measurements" />
</h2>
<ul style={{ paddingLeft: "1rem" }}>
{props.config.measurements.map(m => (
<li key={m}>
<a href="#logo" onClick={() => sampleMeasurement(m)}>
<FormattedMessage id={"measurements." + m} />
</a>
</li>
))}
</ul>
</li>
<li className="nodot">
<h2>
<FormattedMessage id="app.models" />
</h2>
<ul style={{ paddingLeft: "1rem" }}>
<li>
<a href="#logo" onClick={() => sampleModels(models)}>
<FormattedMessage id="app.withoutBreasts" />
</a>
</li>
<li>
<a href="#logo" onClick={() => sampleModels(antMan)}>
Antman
</a>
</li>
</ul>
</li>
</ul>
);
};
SampleConfigurator.propTypes = {
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
};
SampleConfigurator.defaultProps = {};
export default SampleConfigurator;

View file

@ -0,0 +1,20 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import GistConfigurator from ".";
//import { IntlProvider } from "react-intl";
//import { strings } from "@freesewing/i18n";
const props = {
raiseEvent: (type, data) =>
console.log(`Action of type ${type} triggered, data passed is`, data),
updateValue: (type, data) =>
console.log(`Update ${type} with new value`, data)
};
storiesOf("DraftConfigurator", module)
.add("Simon metric", () => (
<GistConfigurator pattern="simon" {...props} units="metric" />
))
.add("Trayvon imperial", () => (
<GistConfigurator pattern="trayvon" {...props} units="imperial" />
));

View file

@ -3,21 +3,12 @@ import PropTypes from "prop-types";
import { defaultGist } from "@freesewing/utils"; import { defaultGist } from "@freesewing/utils";
import Draft from "../../Draft"; import Draft from "../../Draft";
import DraftConfigurator from "../../DraftConfigurator"; import DraftConfigurator from "../../DraftConfigurator";
//import themePlugin from "@freesewing/plugin-theme";
//import svgattrPlugin from "@freesewing/plugin-svgattr";
//import i18nPlugin from "@freesewing/plugin-i18n";
//import validatePlugin from "@freesewing/plugin-validate";
import { strings } from "@freesewing/i18n"; import { strings } from "@freesewing/i18n";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
const DraftPattern = props => { const DraftPattern = props => {
let pattern = new props.Pattern(props.gist.settings); let pattern = new props.Pattern(props.gist.settings);
pattern.draft(); pattern.draft();
//.use(themePlugin)
//.use(svgattrPlugin, { class: "freesewing draft" })
//.use(i18nPlugin, { strings: strings })
//.use(validatePlugin)
//<pre>{pattern.render()}</pre>
return ( return (
<div className="fs-sa"> <div className="fs-sa">
<section> <section>

View file

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { storage } from "@freesewing/utils"; import { storage } from "@freesewing/utils";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import { FormattedMessage } from "react-intl"; import { FormattedMessage, FormattedHTMLMessage } from "react-intl";
import FormFieldMeasurement from "../../.form/FormFieldMeasurement"; import FormFieldMeasurement from "../../.form/FormFieldMeasurement";
import models from "@freesewing/models"; import models from "@freesewing/models";
@ -11,7 +11,8 @@ const Measurements = props => {
container: { container: {
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
width: "100%" width: "100%",
minHeight: "70vh"
}, },
chooser: { chooser: {
width: "100%", width: "100%",
@ -27,6 +28,33 @@ const Measurements = props => {
return props.measurements[m]; return props.measurements[m];
}; };
if (props.required.length < 1)
return (
<div style={styles.container}>
<div style={styles.chooser}>
<h2>
<FormattedMessage id="app.requiredMeasurements" />
</h2>
<h3>
<FormattedMessage id="cfp.noRequiredMeasurements" />
</h3>
<p>
<FormattedHTMLMessage id="cfp.howtoAddMeasurements" />
</p>
<p>
<FormattedMessage id="cfp.seeDocsAt" />
&nbsp;
<a
href={
"https://" + props.language + "/.freesewing.dev/core/config"
}
>
{props.language}.freesewing.dev/core/config
</a>
</p>
</div>
</div>
);
return ( return (
<div style={styles.container}> <div style={styles.container}>
<div style={styles.chooser}> <div style={styles.chooser}>

View file

@ -0,0 +1,60 @@
import React from "react";
import PropTypes from "prop-types";
import { defaultGist } from "@freesewing/utils";
import Draft from "../../Draft";
import SampleConfigurator from "../../SampleConfigurator";
import svgattrPlugin from "@freesewing/plugin-svgattr";
import { strings } from "@freesewing/i18n";
import { FormattedMessage } from "react-intl";
const SamplePattern = props => {
let pattern = new props.Pattern(props.gist.settings).use(svgattrPlugin, {
class: "freesewing draft"
});
try {
pattern.sample();
} catch (err) {
console.log(err);
}
return (
<div className="fs-sa">
<section>
<h2>
<FormattedMessage id="app.pattern" />
</h2>
<div dangerouslySetInnerHTML={{ __html: pattern.render() }} />
<h2>gist</h2>
<pre>{JSON.stringify(props.gist, null, 2)}</pre>
</section>
<aside>
<div className="sticky">
<SampleConfigurator
config={props.config}
gist={props.gist}
updateGist={props.updateGist}
raiseEvent={props.raiseEvent}
freesewing={props.freesewing}
units={props.units}
/>
</div>
</aside>
</div>
);
};
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

@ -14,12 +14,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 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("welcome"); 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");
const [measurements, setMeasurements] = useState(null); const [measurements, setMeasurements] = useState(null);
@ -27,6 +28,7 @@ const Workbench = props => {
let m = getMeasurements(); let m = getMeasurements();
setMeasurements(m); setMeasurements(m);
props.updateGist(m, "settings", "measurements"); props.updateGist(m, "settings", "measurements");
setDisplay(getDisplay());
}, []); }, []);
useEffect(() => { useEffect(() => {
if (props.from) props.importGist(props.from); if (props.from) props.importGist(props.from);
@ -36,6 +38,11 @@ const Workbench = props => {
props.updateGist(props.language, "settings", "locale"); props.updateGist(props.language, "settings", "locale");
}, [props.language]); }, [props.language]);
const getDisplay = () => storage.get(props.config.name + "-display");
const saveDisplay = d => {
setDisplay(d);
storage.set(props.config.name + "-display", d);
};
const getMeasurements = () => const getMeasurements = () =>
storage.get(props.config.name + "-measurements"); storage.get(props.config.name + "-measurements");
const saveMeasurements = data => { const saveMeasurements = data => {
@ -66,7 +73,7 @@ const Workbench = props => {
return false; return false;
}; };
const showLanguageChooser = () => setDisplay("language"); const showLanguageChooser = () => saveDisplay("language");
const updatePattern = p => { const updatePattern = p => {
setPattern(p); setPattern(p);
store.set("pattern", p); store.set("pattern", p);
@ -83,19 +90,19 @@ const Workbench = props => {
left: { left: {
draft: { draft: {
type: "button", type: "button",
onClick: () => setDisplay("draft"), onClick: () => saveDisplay("draft"),
text: "cfp.draftYourPattern", text: "cfp.draftYourPattern",
active: display === "draft" ? true : false active: display === "draft" ? true : false
}, },
sample: { sample: {
type: "button", type: "button",
onClick: () => setDisplay("sample"), onClick: () => saveDisplay("sample"),
text: "cfp.testYourPattern", text: "cfp.testYourPattern",
active: display === "sample" ? true : false active: display === "sample" ? true : false
}, },
measurements: { measurements: {
type: "button", type: "button",
onClick: () => setDisplay("measurements"), onClick: () => saveDisplay("measurements"),
text: "app.measurements", text: "app.measurements",
active: display === "measurements" ? true : false active: display === "measurements" ? true : false
} }
@ -108,7 +115,7 @@ const Workbench = props => {
}, },
language: { language: {
type: "button", type: "button",
onClick: () => setDisplay("languages"), onClick: () => saveDisplay("languages"),
text: <LanguageIcon className="nav-icon" />, text: <LanguageIcon className="nav-icon" />,
title: "Languages", title: "Languages",
active: display === "languages" ? true : false active: display === "languages" ? true : false
@ -128,12 +135,12 @@ const Workbench = props => {
main = ( main = (
<LanguageChooser <LanguageChooser
setLanguage={props.setLanguage} setLanguage={props.setLanguage}
setDisplay={setDisplay} setDisplay={saveDisplay}
/> />
); );
break; break;
case "draft": case "draft":
if (measurementsMissing()) setDisplay("measurements"); if (measurementsMissing()) saveDisplay("measurements");
main = ( main = (
<DraftPattern <DraftPattern
freesewing={props.freesewing} freesewing={props.freesewing}
@ -147,8 +154,18 @@ const Workbench = props => {
); );
break; break;
case "sample": case "sample":
if (measurementsMissing()) setDisplay("measurements"); if (measurementsMissing()) saveDisplay("measurements");
main = <p>Sample: TODO</p>; main = (
<SamplePattern
freesewing={props.freesewing}
Pattern={props.Pattern}
config={props.config}
gist={props.gist}
updateGist={props.updateGist}
raiseEvent={raiseEvent}
units={props.units}
/>
);
break; break;
case "measurements": case "measurements":
main = ( main = (
@ -158,11 +175,12 @@ const Workbench = props => {
units={props.units} units={props.units}
updateMeasurement={updateMeasurement} updateMeasurement={updateMeasurement}
preloadMeasurements={preloadMeasurements} preloadMeasurements={preloadMeasurements}
language={props.language}
/> />
); );
break; break;
default: default:
main = <Welcome language={props.language} setDisplay={setDisplay} />; main = <Welcome language={props.language} setDisplay={saveDisplay} />;
} }
const themes = { dark, light }; const themes = { dark, light };
@ -175,7 +193,7 @@ const Workbench = props => {
} }
> >
{display !== "welcome" ? ( {display !== "welcome" ? (
<Navbar navs={navs} home={() => setDisplay("welcome")} /> <Navbar navs={navs} home={() => saveDisplay("welcome")} />
) : null} ) : null}
{main} {main}
{display !== "welcome" ? <Footer language={props.language} /> : null} {display !== "welcome" ? <Footer language={props.language} /> : null}

View file

@ -1,2 +1,3 @@
@import "elements/a"; @import "elements/a";
@import "elements/footer"; @import "elements/footer";
@import "elements/ul";

View file

@ -1,11 +1,10 @@
ul.nav { padding-left: 0;}
ul.nav.l1 { overflow-x: hidden;} ul.nav.l1 { overflow-x: hidden;}
ul.nav.l1,
ul.nav.l2, ul.nav.l2,
ul.nav.l3, ul.nav.l3,
ul.nav.l4 { ul.nav.l4 {
margin: 0; margin: 0;
padding: 0; padding: 0;
@include title-font;
} }
ul.nav li {list-style-type: none;} ul.nav li {list-style-type: none;}
@ -15,21 +14,17 @@ ul.nav h2,
ul.nav h3, ul.nav h3,
ul.nav h4, ul.nav h4,
ul.nav h5 { ul.nav h5 {
font-weight: normal;
display: block; display: block;
margin: 0; margin: 0;
padding: 0.5rem 0.25rem; padding: 0.5rem 0.25rem 0.5rem 1rem;
} }
ul.nav h3:hover, ul.nav h3:hover,
ul.nav h4:hover { ul.nav h4:hover {
cursor: pointer; cursor: pointer;
background: $oc-gray-3; background: $oc-gray-3;
} }
ul.nav h2 { font-size: 1.2rem; margin-left: 5px;} ul.nav h5 { padding-left: 1.5rem; }
ul.nav h4 { font-size: 1rem; }
ul.nav h5 { font-size: 1rem; padding-left: 1.5rem; font-weight: bold;}
ul.nav h3 { ul.nav h3 {
font-size: 1.1rem;
position: sticky; position: sticky;
top: 0; top: 0;
background: $oc-gray-1; background: $oc-gray-1;

View file

@ -0,0 +1,24 @@
ul.links {
padding-left: 1rem;
li {
list-style-type: none;
}
li:before {
content: "\00a0";
background: $oc-gray-5;
border-radius: 3.5px;
display: inline-block;
height: 7px;
width: 7px;
padding: 0;
margin: 0 0.5rem 0 0;
font-size: 9px;
transition: background 0.15s ease-in;
}
li.nodot:before {
content: none;
}
li:hover:before {
background: $fc-link-light;
}
}

View file

@ -18,3 +18,7 @@ youCan: Sie können
enterMeasurements: Geben Sie die Maße von Hand ein enterMeasurements: Geben Sie die Maße von Hand ein
preloadMeasurements: Laden Sie eine Reihe von Messungen vor preloadMeasurements: Laden Sie eine Reihe von Messungen vor
size: Größe size: Größe
noRequiredMeasurements: Dieses Muster hat keine erforderlichen Messungen.
howtoAddMeasurements: Um Messungen anzufordern, fügen Sie sie dem <b>measurements</b> der Konfigurationsdatei des Musters hinzu.
seeDocsAt: Dokumentation zu diesem Thema finden Sie unter

View file

@ -17,3 +17,6 @@ youCan: You can
enterMeasurements: Enter measurements by hand enterMeasurements: Enter measurements by hand
preloadMeasurements: Preload a set of measurements preloadMeasurements: Preload a set of measurements
size: Size size: Size
noRequiredMeasurements: This pattern has no required measurements
howtoAddMeasurements: To require measurements, add them to the <b>measurements</b> section of the pattern's configuration file.
seeDocsAt: Documentation on this topic is available at

View file

@ -18,3 +18,6 @@ youCan: Usted puede
enterMeasurements: Introducir medidas a mano enterMeasurements: Introducir medidas a mano
preloadMeasurements: Precargar un conjunto de medidas preloadMeasurements: Precargar un conjunto de medidas
size: Tamaño size: Tamaño
noRequiredMeasurements: Este patrón no tiene medidas requeridas.
howtoAddMeasurements: Para requerir mediciones, agréguelas a la sección de <b>measurements</b> del archivo de configuración del patrón.
seeDocsAt: La documentación sobre este tema está disponible en

View file

@ -18,3 +18,6 @@ youCan: Vous pouvez
enterMeasurements: Entrer les mesures à la main enterMeasurements: Entrer les mesures à la main
preloadMeasurements: Précharger un ensemble de mesures preloadMeasurements: Précharger un ensemble de mesures
size: Taille size: Taille
noRequiredMeasurements: Ce patron n'a pas de mesures requises.
howtoAddMeasurements: Pour exiger des mesures, ajoutez-les à la section <b>measurements</b> du fichier de configuration du patron.
seeDocsAt: Documentation sur ce sujet est disponible à l'adresse

View file

@ -15,6 +15,9 @@ testYourPattern: Test je patroon
renderInBrowser: Klik hieronder om uw patroon te renderen in de browser. renderInBrowser: Klik hieronder om uw patroon te renderen in de browser.
weWillReRender: Telkens u wijzigingen aanbrengt renderen we opnieuw. weWillReRender: Telkens u wijzigingen aanbrengt renderen we opnieuw.
youCan: Je kan youCan: Je kan
enterMeasurements: Handmatig afmetingen invoeren enterMeasurements: Handmatig lichaamsmaten invoeren
preloadMeasurements: Een reeks afmetingen laden preloadMeasurements: Een reeks lichaamsmaten laden
size: Maat size: Maat
noRequiredMeasurements: Dit patroon vereist geen lichaamsmaten.
howtoAddMeasurements: Om lichaamsmaten te vereisen, voegt u ze toe aan de <b>measurements</b> sectie van het configuratiebestand van het patroon.
seeDocsAt: Documentatie over dit onderwerp is beschikbaar op