🚧 [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:
|
||||
"react": "^16.4.1"
|
||||
"prop-types": "15.7.2"
|
||||
"@freesewing/patterns": "^{{version}}"
|
||||
"@freesewing/i18n": "^{{version}}"
|
||||
"react-intl": "^2.8.0"
|
||||
core:
|
||||
_:
|
||||
"bezier-js": "^2.2.13"
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import React from "react";
|
||||
import { configure } from "@storybook/react";
|
||||
import { addParameters } from "@storybook/react";
|
||||
import "../../../dist/css-theme/theme.css";
|
||||
import { addParameters, addDecorator } from "@storybook/react";
|
||||
import { IntlProvider } from "react-intl";
|
||||
import { strings } from "@freesewing/i18n";
|
||||
|
||||
function loadStories() {
|
||||
// Load all 'stories.js' files under src
|
||||
|
@ -8,6 +11,12 @@ function loadStories() {
|
|||
req.keys().forEach(filename => req(filename));
|
||||
}
|
||||
|
||||
addDecorator(story => (
|
||||
<IntlProvider locale="en" messages={strings.en}>
|
||||
{story()}
|
||||
</IntlProvider>
|
||||
));
|
||||
|
||||
//addParameters({
|
||||
// backgrounds: [
|
||||
// { 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": {
|
||||
"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": [
|
||||
"../../dist/packages/components/*",
|
||||
|
@ -39,5 +42,9 @@
|
|||
"engines": {
|
||||
"node": ">=8.0.0",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@freesewing/i18n": "0.11.3",
|
||||
"@freesewing/patterns": "0.18.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
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 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";
|
||||
|
||||
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" },
|
||||
thumb: { width: "16px", height: "16px" }
|
||||
})(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 ResetIcon from "@material-ui/icons/SettingsBackupRestore";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
import { injectIntl } from "react-intl";
|
||||
|
||||
const OptionPreamble = props => {
|
||||
const styles = {
|
||||
|
@ -18,6 +19,15 @@ const OptionPreamble = props => {
|
|||
right: { margin: "0 0.5rem" }
|
||||
};
|
||||
|
||||
const resetLabel = props.intl.formatMessage({
|
||||
id: "app.restoreDefaults",
|
||||
defaultMessage: " ♻️ "
|
||||
});
|
||||
const docsLabel = props.intl.formatMessage({
|
||||
id: "app.docs",
|
||||
defaultMessage: " 🤔 "
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={styles.container}>
|
||||
|
@ -36,8 +46,8 @@ const OptionPreamble = props => {
|
|||
</div>
|
||||
<div style={styles.right}>
|
||||
<IconButton
|
||||
title={props.resetLabel}
|
||||
aria-label={props.resetLabel}
|
||||
title={resetLabel}
|
||||
aria-label={resetLabel}
|
||||
color="primary"
|
||||
disabled={props.value === props.dflt ? true : false}
|
||||
onClick={props.reset}
|
||||
|
@ -45,8 +55,8 @@ const OptionPreamble = props => {
|
|||
<ResetIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title={props.docsLabel}
|
||||
aria-label={props.docsLabel}
|
||||
title={docsLabel}
|
||||
aria-label={docsLabel}
|
||||
color="primary"
|
||||
onClick={props.showHelp}
|
||||
>
|
||||
|
@ -67,17 +77,10 @@ OptionPreamble.propTypes = {
|
|||
PropTypes.number.isRequired,
|
||||
PropTypes.string.isRequired
|
||||
]),
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired,
|
||||
resetLabel: PropTypes.string,
|
||||
docsLabel: PropTypes.string,
|
||||
title: PropTypes.node.isRequired,
|
||||
desc: PropTypes.node.isRequired,
|
||||
reset: PropTypes.func.isRequired,
|
||||
showHelp: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
OptionPreamble.defaultProps = {
|
||||
resetLabel: "♻️",
|
||||
docsLabel: "🤔"
|
||||
};
|
||||
|
||||
export default OptionPreamble;
|
||||
export default injectIntl(OptionPreamble);
|
||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from "prop-types";
|
|||
import FormFieldBool from "../FormFieldBool";
|
||||
import OptionPreamble from "../OptionPreamble";
|
||||
|
||||
const DraftSettingBool = props => {
|
||||
const PatternOptionBool = props => {
|
||||
const [value, setValue] = useState(props.dflt);
|
||||
|
||||
const update = (name, newValue, evt) => {
|
||||
|
@ -17,7 +17,7 @@ const DraftSettingBool = props => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={"pattern-option list"}>
|
||||
<div className={"pattern-option bool"}>
|
||||
<OptionPreamble
|
||||
dflt={props.dflt}
|
||||
value={value}
|
||||
|
@ -46,23 +46,17 @@ const DraftSettingBool = props => {
|
|||
);
|
||||
};
|
||||
|
||||
DraftSettingBool.propTypes = {
|
||||
PatternOptionBool.propTypes = {
|
||||
triggerAction: PropTypes.func.isRequired,
|
||||
updateValue: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
dflt: PropTypes.oneOfType([
|
||||
PropTypes.number.isRequired,
|
||||
PropTypes.string.isRequired
|
||||
]),
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired,
|
||||
resetLabel: PropTypes.string,
|
||||
docsLabel: PropTypes.string,
|
||||
list: PropTypes.object.isRequired
|
||||
title: PropTypes.node.isRequired,
|
||||
desc: PropTypes.node.isRequired,
|
||||
labels: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
DraftSettingBool.defaultProps = {
|
||||
title: false,
|
||||
desc: false
|
||||
};
|
||||
|
||||
export default DraftSettingBool;
|
||||
export default PatternOptionBool;
|
|
@ -7,15 +7,15 @@ const props = {
|
|||
console.log(`Action of type ${type} triggered, data passed is`, data),
|
||||
updateValue: (name, value) =>
|
||||
console.log(`Updated pct/deg/count option ${name}, value is now: ${value}`),
|
||||
name: "exampleDraftSettingBool",
|
||||
name: "examplePatternOptionBool",
|
||||
dflt: false,
|
||||
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:
|
||||
"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"]
|
||||
};
|
||||
|
||||
storiesOf("DraftSettingBool", module)
|
||||
storiesOf("PatternOptionBool", module)
|
||||
.add("Basic", () => <Bool {...props} />)
|
||||
.add("Yes as default", () => <Bool {...props} dflt={true} />);
|
|
@ -29,6 +29,15 @@ const PatternOptionList = props => {
|
|||
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 (
|
||||
<div className={"pattern-option list"}>
|
||||
<OptionPreamble
|
||||
|
@ -37,7 +46,7 @@ const PatternOptionList = props => {
|
|||
desc={props.desc}
|
||||
title={props.title}
|
||||
id={"po-list-" + props.name}
|
||||
displayValue={props.list[value]}
|
||||
displayValue={list[value]}
|
||||
reset={reset}
|
||||
showHelp={() =>
|
||||
props.triggerAction("showHelp", {
|
||||
|
@ -53,7 +62,7 @@ const PatternOptionList = props => {
|
|||
onChange={update}
|
||||
label={"po-list-" + props.name}
|
||||
updateValue={update}
|
||||
list={props.list}
|
||||
list={list}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -66,16 +75,9 @@ PatternOptionList.propTypes = {
|
|||
PropTypes.number.isRequired,
|
||||
PropTypes.string.isRequired
|
||||
]),
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired,
|
||||
resetLabel: PropTypes.string,
|
||||
docsLabel: PropTypes.string,
|
||||
list: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
PatternOptionList.defaultProps = {
|
||||
title: false,
|
||||
desc: false
|
||||
title: PropTypes.node.isRequired,
|
||||
desc: PropTypes.node.isRequired,
|
||||
list: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
export default PatternOptionList;
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
sliderStep,
|
||||
roundMm,
|
||||
roundMmUp,
|
||||
roundMmDown,
|
||||
formatMm
|
||||
} from "../utils";
|
||||
import FormFieldSlider from "../FormFieldSlider";
|
||||
import OptionPreamble from "../OptionPreamble";
|
||||
|
||||
|
@ -7,26 +14,8 @@ const PatternOptionMillimeter = props => {
|
|||
const [value, setValue] = 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) => {
|
||||
newValue = round(newValue);
|
||||
newValue = roundMm(newValue, props.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
|
||||
|
@ -44,51 +33,6 @@ const PatternOptionMillimeter = props => {
|
|||
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 = {
|
||||
container: {
|
||||
display: "flex",
|
||||
|
@ -110,7 +54,7 @@ const PatternOptionMillimeter = props => {
|
|||
desc={props.desc}
|
||||
title={props.title}
|
||||
id={"po-mm-" + props.name}
|
||||
displayValue={formatValue(value)}
|
||||
displayValue={formatMm(value, props.units)}
|
||||
reset={reset}
|
||||
showHelp={() =>
|
||||
props.triggerAction("showHelp", {
|
||||
|
@ -122,9 +66,9 @@ const PatternOptionMillimeter = props => {
|
|||
<FormFieldSlider
|
||||
name={props.name}
|
||||
value={value}
|
||||
min={roundUp(props.min)}
|
||||
max={roundDown(props.max)}
|
||||
step={props.units === "imperial" ? smallestImperialStep : 0.1}
|
||||
min={roundMmUp(props.min, props.units)}
|
||||
max={roundMmDown(props.max, props.units)}
|
||||
step={sliderStep[props.units]}
|
||||
onChange={update}
|
||||
label={"po-mm-" + props.name}
|
||||
updateValue={update}
|
||||
|
@ -139,10 +83,8 @@ PatternOptionMillimeter.propTypes = {
|
|||
updateValue: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
dflt: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired,
|
||||
resetLabel: PropTypes.string,
|
||||
docsLabel: PropTypes.string,
|
||||
title: PropTypes.node.isRequired,
|
||||
desc: PropTypes.node.isRequired,
|
||||
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -82,10 +82,8 @@ PatternOptionPctDegCount.propTypes = {
|
|||
updateValue: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
dflt: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired,
|
||||
resetLabel: PropTypes.string,
|
||||
docsLabel: PropTypes.string,
|
||||
title: PropTypes.node.isRequired,
|
||||
desc: PropTypes.node.isRequired,
|
||||
type: PropTypes.oneOf(["pct", "deg", "count"])
|
||||
};
|
||||
|
||||
|
@ -93,8 +91,6 @@ PatternOptionPctDegCount.defaultProps = {
|
|||
min: 0,
|
||||
max: 100,
|
||||
step: 0.1,
|
||||
title: false,
|
||||
desc: false,
|
||||
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;
|
||||
|
||||
export default Simon;
|
||||
export { config };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue