🚧 [components] Work on draft/pattern settings/options
This commit is contained in:
parent
3ca3742953
commit
c0690d5496
30 changed files with 2195 additions and 128 deletions
|
@ -29,6 +29,9 @@ components:
|
||||||
peer:
|
peer:
|
||||||
"react": "^16.4.1"
|
"react": "^16.4.1"
|
||||||
"prop-types": "15.7.2"
|
"prop-types": "15.7.2"
|
||||||
|
"@freesewing/patterns": "^{{version}}"
|
||||||
|
"@freesewing/i18n": "^{{version}}"
|
||||||
|
"react-intl": "^2.8.0"
|
||||||
core:
|
core:
|
||||||
_:
|
_:
|
||||||
"bezier-js": "^2.2.13"
|
"bezier-js": "^2.2.13"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import React from "react";
|
||||||
import { configure } from "@storybook/react";
|
import { configure } from "@storybook/react";
|
||||||
import { addParameters } from "@storybook/react";
|
|
||||||
import "../../../dist/css-theme/theme.css";
|
import "../../../dist/css-theme/theme.css";
|
||||||
|
import { addParameters, addDecorator } from "@storybook/react";
|
||||||
|
import { IntlProvider } from "react-intl";
|
||||||
|
import { strings } from "@freesewing/i18n";
|
||||||
|
|
||||||
function loadStories() {
|
function loadStories() {
|
||||||
// Load all 'stories.js' files under src
|
// Load all 'stories.js' files under src
|
||||||
|
@ -8,6 +11,12 @@ function loadStories() {
|
||||||
req.keys().forEach(filename => req(filename));
|
req.keys().forEach(filename => req(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDecorator(story => (
|
||||||
|
<IntlProvider locale="en" messages={strings.en}>
|
||||||
|
{story()}
|
||||||
|
</IntlProvider>
|
||||||
|
));
|
||||||
|
|
||||||
//addParameters({
|
//addParameters({
|
||||||
// backgrounds: [
|
// backgrounds: [
|
||||||
// { name: 'Light', value: '#f8f9fa', default: true },
|
// { name: 'Light', value: '#f8f9fa', default: true },
|
||||||
|
|
1193
packages/components/package-lock.json
generated
Normal file
1193
packages/components/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -26,7 +26,10 @@
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.4.1",
|
"react": "^16.4.1",
|
||||||
"prop-types": "15.7.2"
|
"prop-types": "15.7.2",
|
||||||
|
"@freesewing/patterns": "^0.33.0",
|
||||||
|
"@freesewing/i18n": "^0.33.0",
|
||||||
|
"react-intl": "^2.8.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"../../dist/packages/components/*",
|
"../../dist/packages/components/*",
|
||||||
|
@ -39,5 +42,9 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0",
|
"node": ">=8.0.0",
|
||||||
"npm": ">=5"
|
"npm": ">=5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@freesewing/i18n": "0.11.3",
|
||||||
|
"@freesewing/patterns": "0.18.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import DraftSettingBool from "../DraftSettingBool";
|
import PatternOptionBool from "../PatternOptionBool";
|
||||||
|
|
||||||
export default props => <DraftSettingBool {...props} name="complete" />;
|
export default props => <PatternOptionBool {...props} name="complete" />;
|
||||||
|
|
62
packages/components/src/DraftSettingLanguage/index.js
Normal file
62
packages/components/src/DraftSettingLanguage/index.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import FormFieldList from "../FormFieldList";
|
||||||
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
|
const DraftSettingLanguage = props => {
|
||||||
|
const [value, setValue] = useState(props.dflt);
|
||||||
|
|
||||||
|
const update = (name, newValue, evt) => {
|
||||||
|
props.updateValue(props.name, newValue);
|
||||||
|
setValue(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setValue(props.dflt);
|
||||||
|
props.updateValue(props.name, props.dflt);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"pattern-option list"}>
|
||||||
|
<OptionPreamble
|
||||||
|
dflt={props.dflt}
|
||||||
|
value={value}
|
||||||
|
desc={props.desc}
|
||||||
|
title={props.title}
|
||||||
|
id={"po-list-" + props.name}
|
||||||
|
displayValue={props.languages[value]}
|
||||||
|
reset={reset}
|
||||||
|
showHelp={() =>
|
||||||
|
props.triggerAction("showHelp", {
|
||||||
|
type: "draftSetting",
|
||||||
|
value: props.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FormFieldList
|
||||||
|
name={props.name}
|
||||||
|
value={value}
|
||||||
|
dflt={props.dflt}
|
||||||
|
onChange={update}
|
||||||
|
label={"po-list-" + props.name}
|
||||||
|
updateValue={update}
|
||||||
|
list={props.languages}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DraftSettingLanguage.propTypes = {
|
||||||
|
triggerAction: PropTypes.func.isRequired,
|
||||||
|
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.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DraftSettingLanguage;
|
27
packages/components/src/DraftSettingLanguage/stories.js
Normal file
27
packages/components/src/DraftSettingLanguage/stories.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import Lang from ".";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
triggerAction: (type, data) =>
|
||||||
|
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||||
|
updateValue: (name, value) =>
|
||||||
|
console.log(`Updated setting ${name}, value is now: ${value}`),
|
||||||
|
name: "exampleDraftSettingLanguage",
|
||||||
|
dflt: "en",
|
||||||
|
title:
|
||||||
|
"I am a language draft setting. This is my title. I'm wrapped in an h4 tag",
|
||||||
|
desc:
|
||||||
|
"This is the description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it.",
|
||||||
|
languages: {
|
||||||
|
de: "German",
|
||||||
|
en: "English",
|
||||||
|
es: "Spanish",
|
||||||
|
fr: "French",
|
||||||
|
nl: "Dutch"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("DraftSettingLanguage", module).add("Basic", () => (
|
||||||
|
<Lang {...props} />
|
||||||
|
));
|
74
packages/components/src/DraftSettingMargin/index.js
Normal file
74
packages/components/src/DraftSettingMargin/index.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import FormFieldList from "../FormFieldList";
|
||||||
|
import FormFieldSlider from "../FormFieldSlider";
|
||||||
|
import { formatMm, roundMm, defaultSa, sliderStep } from "../utils";
|
||||||
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
|
const DraftSettingMargin = props => {
|
||||||
|
const [value, setValue] = useState(props.dflt);
|
||||||
|
|
||||||
|
const update = (name, newValue, evt) => {
|
||||||
|
newValue = roundMm(newValue);
|
||||||
|
// 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("margin", newValue);
|
||||||
|
} else {
|
||||||
|
if (evt.type !== "mousemove") props.updateValue("margin", newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setValue(props.dflt);
|
||||||
|
props.updateValue("margin", props.dflt);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"pattern-option list"}>
|
||||||
|
<OptionPreamble
|
||||||
|
dflt={props.dflt}
|
||||||
|
value={value}
|
||||||
|
desc={props.desc}
|
||||||
|
title={props.title}
|
||||||
|
id="po-slider-margin"
|
||||||
|
displayValue={formatMm(value, props.units)}
|
||||||
|
reset={reset}
|
||||||
|
showHelp={() =>
|
||||||
|
props.triggerAction("showHelp", {
|
||||||
|
type: "draftSetting",
|
||||||
|
value: "margin"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FormFieldSlider
|
||||||
|
name="customSa"
|
||||||
|
value={value}
|
||||||
|
dflt={props.dflt}
|
||||||
|
label="po-slider-margin"
|
||||||
|
updateValue={update}
|
||||||
|
min={0}
|
||||||
|
max={25.4}
|
||||||
|
step={sliderStep[props.units]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DraftSettingMargin.propTypes = {
|
||||||
|
triggerAction: PropTypes.func.isRequired,
|
||||||
|
updateValue: PropTypes.func.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
desc: PropTypes.string.isRequired,
|
||||||
|
units: PropTypes.oneOf(["metric", "imperial"]).isRequired,
|
||||||
|
labels: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
DraftSettingMargin.defaultProps = {
|
||||||
|
// FIXME
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DraftSettingMargin;
|
19
packages/components/src/DraftSettingMargin/stories.js
Normal file
19
packages/components/src/DraftSettingMargin/stories.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import Margin from ".";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
triggerAction: (type, data) =>
|
||||||
|
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||||
|
updateValue: (name, value) =>
|
||||||
|
console.log(`Updated setting ${name}, value is now: ${value}`),
|
||||||
|
name: "margin",
|
||||||
|
dflt: 2,
|
||||||
|
title: "Margin",
|
||||||
|
desc:
|
||||||
|
"This is the margin description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it."
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("DraftSettingMargin", module)
|
||||||
|
.add("Metric", () => <Margin {...props} units="metric" />)
|
||||||
|
.add("Imperial", () => <Margin {...props} units="imperial" />);
|
78
packages/components/src/DraftSettingOnly/index.js
Normal file
78
packages/components/src/DraftSettingOnly/index.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import FormFieldChecks from "../FormFieldChecks";
|
||||||
|
import FormFieldList from "../FormFieldList";
|
||||||
|
import FormFieldSlider from "../FormFieldSlider";
|
||||||
|
import { formatMm, roundMm, defaultSa, sliderStep } from "../utils";
|
||||||
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
|
const DraftSettingOnly = props => {
|
||||||
|
const [value, setValue] = useState(props.dflt);
|
||||||
|
const [parts, setParts] = useState([]);
|
||||||
|
|
||||||
|
const update = (name, newValue, evt) => {
|
||||||
|
setValue(newValue);
|
||||||
|
if (newValue === "dflt") props.updateValue("only", false);
|
||||||
|
else props.updateValue("only", parts);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setValue("dflt");
|
||||||
|
setParts([]);
|
||||||
|
props.updateValue("only", false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCustom = (name, newValue, evt) => {
|
||||||
|
props.updateValue("only", newValue);
|
||||||
|
setParts(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const list = {
|
||||||
|
dflt: props.labels.dflt,
|
||||||
|
custom: props.labels.custom
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"pattern-option list"}>
|
||||||
|
<OptionPreamble
|
||||||
|
dflt="dflt"
|
||||||
|
value={value}
|
||||||
|
desc={props.desc}
|
||||||
|
title={props.title}
|
||||||
|
id="po-list-only"
|
||||||
|
displayValue={props.labels[value]}
|
||||||
|
reset={reset}
|
||||||
|
showHelp={() =>
|
||||||
|
props.triggerAction("showHelp", {
|
||||||
|
type: "draftSetting",
|
||||||
|
value: "only"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FormFieldList
|
||||||
|
name="only"
|
||||||
|
value={value}
|
||||||
|
dflt={props.dflt}
|
||||||
|
onChange={update}
|
||||||
|
label="po-list-only"
|
||||||
|
updateValue={update}
|
||||||
|
list={list}
|
||||||
|
/>
|
||||||
|
{value === "custom" ? (
|
||||||
|
<FormFieldChecks
|
||||||
|
checks={props.parts}
|
||||||
|
name="parts"
|
||||||
|
value={value}
|
||||||
|
dflt={props.customDflt}
|
||||||
|
onChange={updateCustom}
|
||||||
|
label="po-list-only"
|
||||||
|
updateValue={updateCustom}
|
||||||
|
list={list}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DraftSettingOnly;
|
38
packages/components/src/DraftSettingOnly/stories.js
Normal file
38
packages/components/src/DraftSettingOnly/stories.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import Sa from ".";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
triggerAction: (type, data) =>
|
||||||
|
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||||
|
updateValue: (name, value) =>
|
||||||
|
console.log(`Updated setting ${name}, value is now: ${value}`),
|
||||||
|
title: "Only (known as contents on the website) title",
|
||||||
|
desc:
|
||||||
|
"This is the only description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it.",
|
||||||
|
labels: {
|
||||||
|
dflt: "Default",
|
||||||
|
custom: "Custom"
|
||||||
|
},
|
||||||
|
dflt: "dflt",
|
||||||
|
customDflt: [],
|
||||||
|
parts: {
|
||||||
|
front: "Front",
|
||||||
|
back: "Back",
|
||||||
|
sleeve: "Sleeve",
|
||||||
|
pocket: "Pocket"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("DraftSettingOnly", module)
|
||||||
|
.add("Default", () => <Sa {...props} />)
|
||||||
|
.add("Default, all parts preselected", () => (
|
||||||
|
<Sa {...props} customDflt={Object.keys(props.parts)} />
|
||||||
|
))
|
||||||
|
.add("Custom, some parts preselected", () => (
|
||||||
|
<Sa
|
||||||
|
{...props}
|
||||||
|
dflt="custom"
|
||||||
|
customDflt={Object.keys(props.parts).slice(2)}
|
||||||
|
/>
|
||||||
|
));
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import DraftSettingBool from "../DraftSettingBool";
|
import PatternOptionBool from "../PatternOptionBool";
|
||||||
|
|
||||||
export default props => <DraftSettingBool {...props} name="paperless" />;
|
export default props => <PatternOptionBool {...props} name="paperless" />;
|
||||||
|
|
116
packages/components/src/DraftSettingSa/index.js
Normal file
116
packages/components/src/DraftSettingSa/index.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import FormFieldList from "../FormFieldList";
|
||||||
|
import FormFieldSlider from "../FormFieldSlider";
|
||||||
|
import { formatMm, roundMm, defaultSa, sliderStep } from "../utils";
|
||||||
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
|
const DraftSettingSa = props => {
|
||||||
|
const [value, setValue] = useState("dflt");
|
||||||
|
const [saValue, setSaValue] = useState(defaultSa[props.units]);
|
||||||
|
const [customValue, setCustomValue] = useState(10);
|
||||||
|
|
||||||
|
const update = (name, newValue, evt) => {
|
||||||
|
switch (newValue) {
|
||||||
|
case "none":
|
||||||
|
props.updateValue("sa", 0);
|
||||||
|
setValue(newValue);
|
||||||
|
setSaValue(0);
|
||||||
|
break;
|
||||||
|
case "dflt":
|
||||||
|
props.updateValue("sa", defaultSa[props.units]);
|
||||||
|
setValue(newValue);
|
||||||
|
setSaValue(defaultSa[props.units]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
props.updateValue("sa", customValue);
|
||||||
|
setValue(newValue);
|
||||||
|
setSaValue(customValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setValue("dflt");
|
||||||
|
setSaValue(defaultSa[props.units]);
|
||||||
|
props.updateValue("sa", defaultSa[props.units]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCustom = (name, newValue, evt) => {
|
||||||
|
newValue = roundMm(newValue);
|
||||||
|
// 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)) {
|
||||||
|
setSaValue(newValue);
|
||||||
|
if (evt.type !== "mousemove") props.updateValue("sa", newValue);
|
||||||
|
} else {
|
||||||
|
if (evt.type !== "mousemove") props.updateValue("sa", newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const list = {
|
||||||
|
none: props.labels.none,
|
||||||
|
dflt: props.labels.dflt,
|
||||||
|
custom: props.labels.custom
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"pattern-option list"}>
|
||||||
|
<OptionPreamble
|
||||||
|
dflt={"dflt"}
|
||||||
|
value={value}
|
||||||
|
desc={props.desc}
|
||||||
|
title={props.title}
|
||||||
|
id="po-list-sa"
|
||||||
|
displayValue={formatMm(saValue, props.units)}
|
||||||
|
reset={reset}
|
||||||
|
showHelp={() =>
|
||||||
|
props.triggerAction("showHelp", {
|
||||||
|
type: "draftSetting",
|
||||||
|
value: "sa"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FormFieldList
|
||||||
|
name="sa"
|
||||||
|
value={value}
|
||||||
|
dflt={"dflt"}
|
||||||
|
onChange={update}
|
||||||
|
label="po-bool-sa"
|
||||||
|
updateValue={update}
|
||||||
|
list={list}
|
||||||
|
/>
|
||||||
|
{value === "custom" ? (
|
||||||
|
<FormFieldSlider
|
||||||
|
name="customSa"
|
||||||
|
value={saValue}
|
||||||
|
dflt={defaultSa[props.units]}
|
||||||
|
label="po-bool-sa"
|
||||||
|
updateValue={updateCustom}
|
||||||
|
min={0}
|
||||||
|
max={25.4}
|
||||||
|
step={sliderStep[props.units]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DraftSettingSa.propTypes = {
|
||||||
|
triggerAction: PropTypes.func.isRequired,
|
||||||
|
updateValue: PropTypes.func.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
desc: PropTypes.string.isRequired,
|
||||||
|
units: PropTypes.oneOf(["metric", "imperial"]).isRequired,
|
||||||
|
labels: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
DraftSettingSa.defaultProps = {
|
||||||
|
// FIXME
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DraftSettingSa;
|
24
packages/components/src/DraftSettingSa/stories.js
Normal file
24
packages/components/src/DraftSettingSa/stories.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import Sa from ".";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
triggerAction: (type, data) =>
|
||||||
|
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||||
|
updateValue: (name, value) =>
|
||||||
|
console.log(`Updated setting ${name}, value is now: ${value}`),
|
||||||
|
name: "sa",
|
||||||
|
dflt: "dflt",
|
||||||
|
title: "Seam allowance",
|
||||||
|
desc:
|
||||||
|
"This is the seam allowance description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it.",
|
||||||
|
labels: {
|
||||||
|
none: "No seam allowance",
|
||||||
|
dflt: "Standard seam allowance",
|
||||||
|
custom: "Custom seam allowance"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("DraftSettingSa", module)
|
||||||
|
.add("Metric", () => <Sa {...props} units="metric" />)
|
||||||
|
.add("Imperial", () => <Sa {...props} units="imperial" />);
|
48
packages/components/src/DraftSettingUnits/index.js
Normal file
48
packages/components/src/DraftSettingUnits/index.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import FormFieldList from "../FormFieldList";
|
||||||
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
|
const DraftSettingUnits = props => {
|
||||||
|
const [value, setValue] = useState(props.dflt);
|
||||||
|
|
||||||
|
const update = (name, newValue, evt) => {
|
||||||
|
props.updateValue(props.name, newValue);
|
||||||
|
setValue(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setValue(props.dflt);
|
||||||
|
props.updateValue(props.name, props.dflt);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"pattern-option list"}>
|
||||||
|
<OptionPreamble
|
||||||
|
dflt={props.dflt}
|
||||||
|
value={value}
|
||||||
|
desc={props.desc}
|
||||||
|
title={props.title}
|
||||||
|
id="po-list-units"
|
||||||
|
displayValue={props.list[value]}
|
||||||
|
reset={reset}
|
||||||
|
showHelp={() =>
|
||||||
|
props.triggerAction("showHelp", {
|
||||||
|
type: "draftSetting",
|
||||||
|
value: "units"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FormFieldList
|
||||||
|
name="units"
|
||||||
|
value={value}
|
||||||
|
dflt={props.dflt}
|
||||||
|
onChange={update}
|
||||||
|
label="po-bool-units"
|
||||||
|
updateValue={update}
|
||||||
|
list={props.list}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DraftSettingUnits;
|
21
packages/components/src/DraftSettingUnits/stories.js
Normal file
21
packages/components/src/DraftSettingUnits/stories.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import Units from ".";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
triggerAction: (type, data) =>
|
||||||
|
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||||
|
updateValue: (name, value) =>
|
||||||
|
console.log(`Updated setting ${name}, value is now: ${value}`),
|
||||||
|
name: "paperless",
|
||||||
|
dflt: "metric",
|
||||||
|
title: "Units title",
|
||||||
|
desc:
|
||||||
|
"This is the units description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it.",
|
||||||
|
list: {
|
||||||
|
metric: "Metric",
|
||||||
|
imperial: "Imperial"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("DraftSettingUnits", module).add("Basic", () => <Units {...props} />);
|
54
packages/components/src/FormFieldChecks/index.js
Normal file
54
packages/components/src/FormFieldChecks/index.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import FormControl from "@material-ui/core/FormControl";
|
||||||
|
import FormGroup from "@material-ui/core/FormGroup";
|
||||||
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
|
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||||
|
import Checkbox from "@material-ui/core/Checkbox";
|
||||||
|
|
||||||
|
const FormFieldChecks = props => {
|
||||||
|
const [value, setValue] = useState(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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Force state update when rerendering due to props change
|
||||||
|
// if (props.value !== value) setValue(props.value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup>
|
||||||
|
{Object.keys(props.checks).map(i => {
|
||||||
|
let check = props.checks[i];
|
||||||
|
return (
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={value.indexOf(i) === -1 ? false : true}
|
||||||
|
onChange={() => toggle(i)}
|
||||||
|
value={i}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={props.checks[i]}
|
||||||
|
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;
|
21
packages/components/src/FormFieldChecks/stories.js
Normal file
21
packages/components/src/FormFieldChecks/stories.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import FormFieldChecks from ".";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
updateValue: (name, value) =>
|
||||||
|
console.log(`Updated option ${name}, value is now: ${value}`),
|
||||||
|
name: "exampleChecksOption",
|
||||||
|
checks: {
|
||||||
|
apple: "Apple",
|
||||||
|
banana: "Banana",
|
||||||
|
cherry: "Cherry"
|
||||||
|
},
|
||||||
|
dflt: []
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("FormFieldChecks", module)
|
||||||
|
.add("Basic", () => <FormFieldChecks {...props} />)
|
||||||
|
.add("Apple", () => <FormFieldChecks {...props} dflt={["apple"]} />)
|
||||||
|
.add("Banana", () => <FormFieldChecks {...props} dflt={["banana"]} />)
|
||||||
|
.add("Cherry", () => <FormFieldChecks {...props} dflt={["cherry"]} />);
|
|
@ -4,7 +4,10 @@ import Slider from "@material-ui/lab/Slider";
|
||||||
import { withStyles } from "@material-ui/core/styles";
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
|
|
||||||
const PaddedSlider = withStyles({
|
const PaddedSlider = withStyles({
|
||||||
container: { padding: "25px 0" },
|
container: {
|
||||||
|
padding: "25px 0",
|
||||||
|
overflowX: "hidden" // See: https://github.com/mui-org/material-ui/issues/14234
|
||||||
|
},
|
||||||
track: { height: "4px" },
|
track: { height: "4px" },
|
||||||
thumb: { width: "16px", height: "16px" }
|
thumb: { width: "16px", height: "16px" }
|
||||||
})(Slider);
|
})(Slider);
|
||||||
|
|
59
packages/components/src/GistConfigurator/index.js
Normal file
59
packages/components/src/GistConfigurator/index.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Pct from "../PatternOptionPercentage";
|
||||||
|
import Deg from "../PatternOptionDegree";
|
||||||
|
import Mm from "../PatternOptionMillimeter";
|
||||||
|
import Bool from "../PatternOptionBool";
|
||||||
|
import OptionGroup from "../OptionGroup";
|
||||||
|
import { optionType, defaultGist, gistDefaults } from "../utils";
|
||||||
|
import { patternInfo, patternList } from "@freesewing/patterns";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
const GistConfigurator = props => {
|
||||||
|
const [gist, setGist] = useState(props.gist || defaultGist);
|
||||||
|
|
||||||
|
const update = (type, name, value) => {
|
||||||
|
console.log("updating", type, name, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
let pattern = patternInfo[props.pattern];
|
||||||
|
let dflts = gistDefaults(pattern.config, gist);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="gist-config">
|
||||||
|
<div className="gist-options">
|
||||||
|
<h2>
|
||||||
|
<FormattedMessage id="app.patternOptions" />
|
||||||
|
</h2>
|
||||||
|
{Object.keys(pattern.optionGroups).map(group => (
|
||||||
|
<React.Fragment key={group}>
|
||||||
|
<h3 key={group + "-title"}>
|
||||||
|
<FormattedMessage id={"optiongroups." + group} />
|
||||||
|
</h3>
|
||||||
|
<OptionGroup
|
||||||
|
key={group + "-group"}
|
||||||
|
units={props.units}
|
||||||
|
pattern={pattern}
|
||||||
|
dflts={dflts}
|
||||||
|
options={pattern.optionGroups[group]}
|
||||||
|
updateValue={update}
|
||||||
|
triggerAction={props.triggerAction}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
<h2>
|
||||||
|
<FormattedMessage id="app.draftSettings" />
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GistConfigurator.propTypes = {
|
||||||
|
pattern: PropTypes.oneOf(patternList),
|
||||||
|
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
GistConfigurator.defaultProps = {};
|
||||||
|
|
||||||
|
export default GistConfigurator;
|
18
packages/components/src/GistConfigurator/stories.js
Normal file
18
packages/components/src/GistConfigurator/stories.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import React from "react";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import GistConfigurator from ".";
|
||||||
|
//import { IntlProvider } from "react-intl";
|
||||||
|
//import { strings } from "@freesewing/i18n";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
triggerAction: (type, data) =>
|
||||||
|
console.log(`Action of type ${type} triggered, data passed is`, data)
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("GistConfigurator", module)
|
||||||
|
.add("Simon metric", () => (
|
||||||
|
<GistConfigurator pattern="simon" gist={false} units="metric" />
|
||||||
|
))
|
||||||
|
.add("Trayvon imperial", () => (
|
||||||
|
<GistConfigurator pattern="trayvon" gist={false} units="imperial" />
|
||||||
|
));
|
94
packages/components/src/OptionGroup/index.js
Normal file
94
packages/components/src/OptionGroup/index.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Pct from "../PatternOptionPercentage";
|
||||||
|
import Deg from "../PatternOptionDegree";
|
||||||
|
import Mm from "../PatternOptionMillimeter";
|
||||||
|
import Bool from "../PatternOptionBool";
|
||||||
|
import List from "../PatternOptionList";
|
||||||
|
import Count from "../PatternOptionCount";
|
||||||
|
import { optionType } from "../utils";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
import { injectIntl } from "react-intl";
|
||||||
|
|
||||||
|
const OptionGroup = props => {
|
||||||
|
const update = (name, value) => props.updateValue("option", name, value);
|
||||||
|
|
||||||
|
const renderOption = name => {
|
||||||
|
let option = props.pattern.config.options[name];
|
||||||
|
let type = optionType(option);
|
||||||
|
let stringKey = `options.${props.pattern.config.name}.${name}.`;
|
||||||
|
let extraProps = {
|
||||||
|
name,
|
||||||
|
dflt: props.dflts.options[name],
|
||||||
|
units: props.units,
|
||||||
|
updateValue: update,
|
||||||
|
triggerAction: props.triggerAction,
|
||||||
|
title: <FormattedMessage id={stringKey + "title"} />,
|
||||||
|
desc: <FormattedMessage id={stringKey + "description"} />,
|
||||||
|
intl: props.intl,
|
||||||
|
pattern: props.pattern.config.name
|
||||||
|
};
|
||||||
|
let noyes = [
|
||||||
|
<FormattedMessage id="app.no" />,
|
||||||
|
<FormattedMessage id="app.yes" />
|
||||||
|
];
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "pct":
|
||||||
|
return <Pct {...option} {...extraProps} key={name} />;
|
||||||
|
break;
|
||||||
|
case "deg":
|
||||||
|
return <Deg {...option} {...extraProps} key={name} />;
|
||||||
|
break;
|
||||||
|
case "mm":
|
||||||
|
return (
|
||||||
|
<Mm {...option} {...extraProps} key={name} units={props.units} />
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "bool":
|
||||||
|
return <Bool {...option} {...extraProps} key={name} labels={noyes} />;
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
return <List {...option} {...extraProps} key={name} />;
|
||||||
|
break;
|
||||||
|
case "count":
|
||||||
|
return <Count {...option} {...extraProps} key={name} />;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unsupport option type: " + type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="optiongroup">
|
||||||
|
{props.options.map(name => {
|
||||||
|
let output = [];
|
||||||
|
if (typeof name === "object") {
|
||||||
|
// Subgroup
|
||||||
|
for (let subGroup of Object.keys(name)) {
|
||||||
|
output.push(
|
||||||
|
<h4 key={subGroup + "-title"}>
|
||||||
|
<FormattedMessage id={"optiongroups." + subGroup} />
|
||||||
|
</h4>
|
||||||
|
);
|
||||||
|
for (let option of name[subGroup])
|
||||||
|
output.push(renderOption(option));
|
||||||
|
}
|
||||||
|
} else output.push(renderOption(name));
|
||||||
|
|
||||||
|
return output;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OptionGroup.propTypes = {
|
||||||
|
pattern: PropTypes.object.isRequired,
|
||||||
|
dflts: PropTypes.object.isRequired,
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
OptionGroup.defaultProps = {};
|
||||||
|
|
||||||
|
export default injectIntl(OptionGroup);
|
|
@ -3,6 +3,7 @@ import PropTypes from "prop-types";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import ResetIcon from "@material-ui/icons/SettingsBackupRestore";
|
import ResetIcon from "@material-ui/icons/SettingsBackupRestore";
|
||||||
import HelpIcon from "@material-ui/icons/Help";
|
import HelpIcon from "@material-ui/icons/Help";
|
||||||
|
import { injectIntl } from "react-intl";
|
||||||
|
|
||||||
const OptionPreamble = props => {
|
const OptionPreamble = props => {
|
||||||
const styles = {
|
const styles = {
|
||||||
|
@ -18,6 +19,15 @@ const OptionPreamble = props => {
|
||||||
right: { margin: "0 0.5rem" }
|
right: { margin: "0 0.5rem" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetLabel = props.intl.formatMessage({
|
||||||
|
id: "app.restoreDefaults",
|
||||||
|
defaultMessage: " ♻️ "
|
||||||
|
});
|
||||||
|
const docsLabel = props.intl.formatMessage({
|
||||||
|
id: "app.docs",
|
||||||
|
defaultMessage: " 🤔 "
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
|
@ -36,8 +46,8 @@ const OptionPreamble = props => {
|
||||||
</div>
|
</div>
|
||||||
<div style={styles.right}>
|
<div style={styles.right}>
|
||||||
<IconButton
|
<IconButton
|
||||||
title={props.resetLabel}
|
title={resetLabel}
|
||||||
aria-label={props.resetLabel}
|
aria-label={resetLabel}
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={props.value === props.dflt ? true : false}
|
disabled={props.value === props.dflt ? true : false}
|
||||||
onClick={props.reset}
|
onClick={props.reset}
|
||||||
|
@ -45,8 +55,8 @@ const OptionPreamble = props => {
|
||||||
<ResetIcon />
|
<ResetIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
title={props.docsLabel}
|
title={docsLabel}
|
||||||
aria-label={props.docsLabel}
|
aria-label={docsLabel}
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={props.showHelp}
|
onClick={props.showHelp}
|
||||||
>
|
>
|
||||||
|
@ -67,17 +77,10 @@ OptionPreamble.propTypes = {
|
||||||
PropTypes.number.isRequired,
|
PropTypes.number.isRequired,
|
||||||
PropTypes.string.isRequired
|
PropTypes.string.isRequired
|
||||||
]),
|
]),
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.node.isRequired,
|
||||||
desc: PropTypes.string.isRequired,
|
desc: PropTypes.node.isRequired,
|
||||||
resetLabel: PropTypes.string,
|
|
||||||
docsLabel: PropTypes.string,
|
|
||||||
reset: PropTypes.func.isRequired,
|
reset: PropTypes.func.isRequired,
|
||||||
showHelp: PropTypes.func.isRequired
|
showHelp: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
OptionPreamble.defaultProps = {
|
export default injectIntl(OptionPreamble);
|
||||||
resetLabel: "♻️",
|
|
||||||
docsLabel: "🤔"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OptionPreamble;
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from "prop-types";
|
||||||
import FormFieldBool from "../FormFieldBool";
|
import FormFieldBool from "../FormFieldBool";
|
||||||
import OptionPreamble from "../OptionPreamble";
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
const DraftSettingBool = props => {
|
const PatternOptionBool = props => {
|
||||||
const [value, setValue] = useState(props.dflt);
|
const [value, setValue] = useState(props.dflt);
|
||||||
|
|
||||||
const update = (name, newValue, evt) => {
|
const update = (name, newValue, evt) => {
|
||||||
|
@ -17,7 +17,7 @@ const DraftSettingBool = props => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"pattern-option list"}>
|
<div className={"pattern-option bool"}>
|
||||||
<OptionPreamble
|
<OptionPreamble
|
||||||
dflt={props.dflt}
|
dflt={props.dflt}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -46,23 +46,17 @@ const DraftSettingBool = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DraftSettingBool.propTypes = {
|
PatternOptionBool.propTypes = {
|
||||||
|
triggerAction: PropTypes.func.isRequired,
|
||||||
updateValue: PropTypes.func.isRequired,
|
updateValue: PropTypes.func.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
dflt: PropTypes.oneOfType([
|
dflt: PropTypes.oneOfType([
|
||||||
PropTypes.number.isRequired,
|
PropTypes.number.isRequired,
|
||||||
PropTypes.string.isRequired
|
PropTypes.string.isRequired
|
||||||
]),
|
]),
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.node.isRequired,
|
||||||
desc: PropTypes.string.isRequired,
|
desc: PropTypes.node.isRequired,
|
||||||
resetLabel: PropTypes.string,
|
labels: PropTypes.array.isRequired
|
||||||
docsLabel: PropTypes.string,
|
|
||||||
list: PropTypes.object.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DraftSettingBool.defaultProps = {
|
export default PatternOptionBool;
|
||||||
title: false,
|
|
||||||
desc: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DraftSettingBool;
|
|
|
@ -7,15 +7,15 @@ const props = {
|
||||||
console.log(`Action of type ${type} triggered, data passed is`, data),
|
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||||
updateValue: (name, value) =>
|
updateValue: (name, value) =>
|
||||||
console.log(`Updated pct/deg/count option ${name}, value is now: ${value}`),
|
console.log(`Updated pct/deg/count option ${name}, value is now: ${value}`),
|
||||||
name: "exampleDraftSettingBool",
|
name: "examplePatternOptionBool",
|
||||||
dflt: false,
|
dflt: false,
|
||||||
title:
|
title:
|
||||||
"I am a boolean draft setting. This is my title. I'm wrapped in an h4 tag",
|
"I am a boolean pattern option. This is my title. I'm wrapped in an h4 tag",
|
||||||
desc:
|
desc:
|
||||||
"This is the description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it.",
|
"This is the description. I'm wrapped in a p tag. This component only sets the CSS class on a non-default value. It's up to you to supply the CSS to style it.",
|
||||||
labels: ["No", "Yes"]
|
labels: ["No", "Yes"]
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("DraftSettingBool", module)
|
storiesOf("PatternOptionBool", module)
|
||||||
.add("Basic", () => <Bool {...props} />)
|
.add("Basic", () => <Bool {...props} />)
|
||||||
.add("Yes as default", () => <Bool {...props} dflt={true} />);
|
.add("Yes as default", () => <Bool {...props} dflt={true} />);
|
|
@ -29,6 +29,15 @@ const PatternOptionList = props => {
|
||||||
right: { margin: "0 0.5rem" }
|
right: { margin: "0 0.5rem" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add translations
|
||||||
|
let stringKey = `options.${props.pattern}.${props.name}.options.`;
|
||||||
|
let list = {};
|
||||||
|
for (let item of props.list)
|
||||||
|
list[item] = props.intl.formatMessage({
|
||||||
|
id: stringKey + item,
|
||||||
|
defaultMessage: item
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"pattern-option list"}>
|
<div className={"pattern-option list"}>
|
||||||
<OptionPreamble
|
<OptionPreamble
|
||||||
|
@ -37,7 +46,7 @@ const PatternOptionList = props => {
|
||||||
desc={props.desc}
|
desc={props.desc}
|
||||||
title={props.title}
|
title={props.title}
|
||||||
id={"po-list-" + props.name}
|
id={"po-list-" + props.name}
|
||||||
displayValue={props.list[value]}
|
displayValue={list[value]}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
showHelp={() =>
|
showHelp={() =>
|
||||||
props.triggerAction("showHelp", {
|
props.triggerAction("showHelp", {
|
||||||
|
@ -53,7 +62,7 @@ const PatternOptionList = props => {
|
||||||
onChange={update}
|
onChange={update}
|
||||||
label={"po-list-" + props.name}
|
label={"po-list-" + props.name}
|
||||||
updateValue={update}
|
updateValue={update}
|
||||||
list={props.list}
|
list={list}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -66,16 +75,9 @@ PatternOptionList.propTypes = {
|
||||||
PropTypes.number.isRequired,
|
PropTypes.number.isRequired,
|
||||||
PropTypes.string.isRequired
|
PropTypes.string.isRequired
|
||||||
]),
|
]),
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.node.isRequired,
|
||||||
desc: PropTypes.string.isRequired,
|
desc: PropTypes.node.isRequired,
|
||||||
resetLabel: PropTypes.string,
|
list: PropTypes.array.isRequired
|
||||||
docsLabel: PropTypes.string,
|
|
||||||
list: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
PatternOptionList.defaultProps = {
|
|
||||||
title: false,
|
|
||||||
desc: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PatternOptionList;
|
export default PatternOptionList;
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import {
|
||||||
|
sliderStep,
|
||||||
|
roundMm,
|
||||||
|
roundMmUp,
|
||||||
|
roundMmDown,
|
||||||
|
formatMm
|
||||||
|
} from "../utils";
|
||||||
import FormFieldSlider from "../FormFieldSlider";
|
import FormFieldSlider from "../FormFieldSlider";
|
||||||
import OptionPreamble from "../OptionPreamble";
|
import OptionPreamble from "../OptionPreamble";
|
||||||
|
|
||||||
|
@ -7,26 +14,8 @@ const PatternOptionMillimeter = props => {
|
||||||
const [value, setValue] = useState(props.dflt);
|
const [value, setValue] = useState(props.dflt);
|
||||||
const [previousValue, setPreviousValue] = useState(props.dflt);
|
const [previousValue, setPreviousValue] = useState(props.dflt);
|
||||||
|
|
||||||
const smallestImperialStep = 0.396875;
|
|
||||||
|
|
||||||
const round = val => {
|
|
||||||
if (props.units === "imperial") {
|
|
||||||
return Math.round(val * 1000000) / 1000000;
|
|
||||||
} else return Math.round(val * 10) / 10;
|
|
||||||
};
|
|
||||||
const roundDown = val => {
|
|
||||||
if (props.units === "imperial") {
|
|
||||||
return val - (val % smallestImperialStep);
|
|
||||||
} else return Math.floor(val * 10) / 10;
|
|
||||||
};
|
|
||||||
const roundUp = val => {
|
|
||||||
if (props.units === "imperial") {
|
|
||||||
return val - (val % 0.396875);
|
|
||||||
} else return Math.ceil(val * 10) / 10;
|
|
||||||
};
|
|
||||||
|
|
||||||
const update = (name, newValue, evt) => {
|
const update = (name, newValue, evt) => {
|
||||||
newValue = round(newValue);
|
newValue = roundMm(newValue, props.units);
|
||||||
// Sometimes, when sliding, the rapid succession of updates
|
// Sometimes, when sliding, the rapid succession of updates
|
||||||
// causes a weird timing issue to result in a value that is NaN.
|
// 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
|
// If that's the case, just ignore this update and keep the
|
||||||
|
@ -44,51 +33,6 @@ const PatternOptionMillimeter = props => {
|
||||||
props.updateValue(props.name, props.dflt);
|
props.updateValue(props.name, props.dflt);
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatValue = val => {
|
|
||||||
val = round(val);
|
|
||||||
if (props.units === "imperial") {
|
|
||||||
if (val == 0) return 0 + '"';
|
|
||||||
let negative = "";
|
|
||||||
let inches = "";
|
|
||||||
let rest = "";
|
|
||||||
let fraction = val / 25.4;
|
|
||||||
if (fraction < 0) {
|
|
||||||
fraction = fraction * -1;
|
|
||||||
negative = "-";
|
|
||||||
}
|
|
||||||
if (Math.abs(fraction) < 1) {
|
|
||||||
inches = "";
|
|
||||||
rest = fraction;
|
|
||||||
} else {
|
|
||||||
inches = Math.floor(fraction);
|
|
||||||
rest = fraction - inches;
|
|
||||||
}
|
|
||||||
inches += " ";
|
|
||||||
let fraction128 = Math.round(rest * 128);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 == 0) return negative + inches;
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 % 64 == 0)
|
|
||||||
return negative + inches + fraction128 / 64 + "/2";
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 % 32 == 0)
|
|
||||||
return negative + inches + fraction128 / 32 + "/2";
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 % 16 == 0)
|
|
||||||
return negative + inches + fraction128 / 16 + "/8";
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 % 8 == 0)
|
|
||||||
return negative + inches + fraction128 / 8 + "/16";
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 % 4 == 0)
|
|
||||||
return negative + inches + fraction128 / 4 + "/32";
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (fraction128 % 2 == 0)
|
|
||||||
return negative + inches + fraction128 / 2 + "/64";
|
|
||||||
else return negative + fraction;
|
|
||||||
} else return val + "cm";
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
container: {
|
container: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -110,7 +54,7 @@ const PatternOptionMillimeter = props => {
|
||||||
desc={props.desc}
|
desc={props.desc}
|
||||||
title={props.title}
|
title={props.title}
|
||||||
id={"po-mm-" + props.name}
|
id={"po-mm-" + props.name}
|
||||||
displayValue={formatValue(value)}
|
displayValue={formatMm(value, props.units)}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
showHelp={() =>
|
showHelp={() =>
|
||||||
props.triggerAction("showHelp", {
|
props.triggerAction("showHelp", {
|
||||||
|
@ -122,9 +66,9 @@ const PatternOptionMillimeter = props => {
|
||||||
<FormFieldSlider
|
<FormFieldSlider
|
||||||
name={props.name}
|
name={props.name}
|
||||||
value={value}
|
value={value}
|
||||||
min={roundUp(props.min)}
|
min={roundMmUp(props.min, props.units)}
|
||||||
max={roundDown(props.max)}
|
max={roundMmDown(props.max, props.units)}
|
||||||
step={props.units === "imperial" ? smallestImperialStep : 0.1}
|
step={sliderStep[props.units]}
|
||||||
onChange={update}
|
onChange={update}
|
||||||
label={"po-mm-" + props.name}
|
label={"po-mm-" + props.name}
|
||||||
updateValue={update}
|
updateValue={update}
|
||||||
|
@ -139,10 +83,8 @@ PatternOptionMillimeter.propTypes = {
|
||||||
updateValue: PropTypes.func.isRequired,
|
updateValue: PropTypes.func.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
dflt: PropTypes.number.isRequired,
|
dflt: PropTypes.number.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.node.isRequired,
|
||||||
desc: PropTypes.string.isRequired,
|
desc: PropTypes.node.isRequired,
|
||||||
resetLabel: PropTypes.string,
|
|
||||||
docsLabel: PropTypes.string,
|
|
||||||
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
|
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -82,10 +82,8 @@ PatternOptionPctDegCount.propTypes = {
|
||||||
updateValue: PropTypes.func.isRequired,
|
updateValue: PropTypes.func.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
dflt: PropTypes.number.isRequired,
|
dflt: PropTypes.number.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.node.isRequired,
|
||||||
desc: PropTypes.string.isRequired,
|
desc: PropTypes.node.isRequired,
|
||||||
resetLabel: PropTypes.string,
|
|
||||||
docsLabel: PropTypes.string,
|
|
||||||
type: PropTypes.oneOf(["pct", "deg", "count"])
|
type: PropTypes.oneOf(["pct", "deg", "count"])
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,8 +91,6 @@ PatternOptionPctDegCount.defaultProps = {
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
title: false,
|
|
||||||
desc: false,
|
|
||||||
type: "pct"
|
type: "pct"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
161
packages/components/src/utils/index.js
Normal file
161
packages/components/src/utils/index.js
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// Needed to use JSX
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export const smallestImperialStep = 0.396875;
|
||||||
|
|
||||||
|
export const roundMm = (val, units) => {
|
||||||
|
if (units === "imperial") return Math.round(val * 1000000) / 1000000;
|
||||||
|
else return Math.round(val * 10) / 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const roundMmDown = (val, units) => {
|
||||||
|
if (units === "imperial") return val - (val % smallestImperialStep);
|
||||||
|
else return Math.floor(val * 10) / 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const roundMmUp = (val, units) => {
|
||||||
|
if (units === "imperial") return val - (val % 0.396875);
|
||||||
|
else return Math.ceil(val * 10) / 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatImperial = (
|
||||||
|
neg,
|
||||||
|
inch,
|
||||||
|
numo = false,
|
||||||
|
deno = false,
|
||||||
|
format = "html"
|
||||||
|
) => {
|
||||||
|
if (format === "html") {
|
||||||
|
if (numo)
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{neg}
|
||||||
|
{inch} <sup>{numo}</sup>/<sub>{deno}</sub>"
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{neg}
|
||||||
|
{inch}"
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (numo) return neg + inch;
|
||||||
|
else return neg + inch + " " + numo + "/" + deno;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatMm = (val, units, format = "html") => {
|
||||||
|
val = roundMm(val);
|
||||||
|
let H = format === "html" ? true : false;
|
||||||
|
if (units === "imperial") {
|
||||||
|
if (val == 0) return formatImperial("", 0, false, false, format);
|
||||||
|
let negative = "";
|
||||||
|
let inches = "";
|
||||||
|
let rest = "";
|
||||||
|
let fraction = val / 25.4;
|
||||||
|
if (fraction < 0) {
|
||||||
|
fraction = fraction * -1;
|
||||||
|
negative = "-";
|
||||||
|
}
|
||||||
|
if (Math.abs(fraction) < 1) rest = fraction;
|
||||||
|
else {
|
||||||
|
inches = Math.floor(fraction);
|
||||||
|
rest = fraction - inches;
|
||||||
|
}
|
||||||
|
let fraction128 = Math.round(rest * 128);
|
||||||
|
if (fraction128 == 0) return formatImperial(negative, inches);
|
||||||
|
if (fraction128 % 64 == 0)
|
||||||
|
return formatImperial(negative, inches, fraction128 / 64, 2);
|
||||||
|
if (fraction128 % 32 == 0)
|
||||||
|
return formatImperial(negative, inches, fraction128 / 32, 4);
|
||||||
|
if (fraction128 % 16 == 0)
|
||||||
|
return formatImperial(negative, inches, fraction128 / 16, 8);
|
||||||
|
if (fraction128 % 8 == 0)
|
||||||
|
return formatImperial(negative, inches, fraction128 / 8, 16);
|
||||||
|
if (fraction128 % 4 == 0)
|
||||||
|
return formatImperial(negative, inches, fraction128 / 4, 32);
|
||||||
|
if (fraction128 % 2 == 0)
|
||||||
|
return formatImperial(negative, inches, fraction128 / 2, 64);
|
||||||
|
return negative + fraction;
|
||||||
|
} else {
|
||||||
|
if (format === "html") return roundMm(val / 10) + "cm";
|
||||||
|
else return roundMm(val / 10);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultSa = {
|
||||||
|
imperial: 15.875,
|
||||||
|
metric: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sliderStep = {
|
||||||
|
metric: 0.1,
|
||||||
|
imperial: 0.396875
|
||||||
|
};
|
||||||
|
|
||||||
|
export const optionType = option => {
|
||||||
|
if (typeof option === "object") {
|
||||||
|
if (typeof option.pct !== "undefined") return "pct";
|
||||||
|
if (typeof option.mm !== "undefined") return "mm";
|
||||||
|
if (typeof option.deg !== "undefined") return "deg";
|
||||||
|
if (typeof option.count !== "undefined") return "count";
|
||||||
|
if (typeof option.bool !== "undefined") return "bool";
|
||||||
|
if (typeof option.list !== "undefined") return "list";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "constant";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultGist = {
|
||||||
|
settings: {
|
||||||
|
embed: true,
|
||||||
|
sa: 0,
|
||||||
|
complete: true,
|
||||||
|
paperless: false,
|
||||||
|
locale: "en",
|
||||||
|
units: "metric",
|
||||||
|
margin: 2,
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const gistDefaults = (config, gist) => {
|
||||||
|
let options = {};
|
||||||
|
for (let option of Object.keys(config.options)) {
|
||||||
|
if (
|
||||||
|
typeof gist.options !== "undefined" &&
|
||||||
|
typeof gist.options[option] !== undefined
|
||||||
|
)
|
||||||
|
options[option] = gist.options[option];
|
||||||
|
else options[option] = optionDefault(config.options[option]);
|
||||||
|
}
|
||||||
|
delete gist.options;
|
||||||
|
let settings = JSON.parse(JSON.stringify(defaultGist.settings));
|
||||||
|
delete settings.locale;
|
||||||
|
delete settings.units;
|
||||||
|
for (let setting of Object.keys(settings)) {
|
||||||
|
if (typeof gist.settings[setting] !== undefined) {
|
||||||
|
settings[setting] = gist.settings[setting];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.options = options;
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const optionDefault = option => {
|
||||||
|
let type = optionType(option);
|
||||||
|
switch (optionType(option)) {
|
||||||
|
case "constant":
|
||||||
|
return option;
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
return option.dflt;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return option[type];
|
||||||
|
}
|
||||||
|
};
|
|
@ -55,3 +55,4 @@ Simon.prototype.draftSleevePlacketOverlap = draftSleevePlacketOverlap;
|
||||||
Simon.prototype.draftCuff = draftCuff;
|
Simon.prototype.draftCuff = draftCuff;
|
||||||
|
|
||||||
export default Simon;
|
export default Simon;
|
||||||
|
export { config };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue