🚧 Progress on workbench
This commit is contained in:
parent
158c19ae1d
commit
a888922968
31 changed files with 716 additions and 153 deletions
|
@ -1,17 +1,14 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const Circle = props => {
|
||||
return null;
|
||||
let foo = (
|
||||
<circle
|
||||
cx={props.point.x}
|
||||
cy={props.point.y}
|
||||
r={props.point.attributes.get("data-circle")}
|
||||
{...props.point.attributes.asPropsIfPrefixIs("data-circle-")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const Circle = props => (
|
||||
<circle
|
||||
cx={props.point.x}
|
||||
cy={props.point.y}
|
||||
r={props.point.attributes.get("data-circle")}
|
||||
{...props.point.attributes.asPropsIfPrefixIs("data-circle-")}
|
||||
/>
|
||||
);
|
||||
|
||||
Circle.propTypes = {
|
||||
point: PropTypes.object.isRequired
|
||||
|
|
84
packages/components/src/Draft/Defs/Grid/index.js
Normal file
84
packages/components/src/Draft/Defs/Grid/index.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
import React from "react";
|
||||
|
||||
const Grid = props => {
|
||||
let style = {
|
||||
style: {
|
||||
fill: "none",
|
||||
stroke: "currentColor"
|
||||
}
|
||||
};
|
||||
if (props.units === "imperial")
|
||||
return (
|
||||
<pattern
|
||||
id="grid"
|
||||
height="25.4"
|
||||
width="25.4"
|
||||
patternUnits="userSpaceOnUse"
|
||||
key="grid"
|
||||
>
|
||||
<path
|
||||
className="gridline lg imperial"
|
||||
d="M 0 0 L 0 25.4 L 25.4 25.4"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline lg imperial"
|
||||
d="M 12.7 0 L 12.7 25.4 M 0 12.7 L 25.4 12.7"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline sm imperial"
|
||||
d="M 3.175 0 L 3.175 25.4 M 6.32 0 L 6.35 25.4 M 9.525 0 L 9.525 25.4 M 15.875 0 L 15.875 25.4 M 19.05 0 L 19.05 25.4 M 22.225 0 L 22.225 25.4"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline sm imperial"
|
||||
d="M 0 3.175 L 25.4 3.175 M 0 6.32 L 25.4 6.35 M 0 9.525 L 25.4 9.525 M 0 15.875 L 25.4 15.875 M 0 19.05 L 25.4 19.05 M 0 22.225 L 25.4 22.225"
|
||||
{...style}
|
||||
/>
|
||||
</pattern>
|
||||
);
|
||||
else
|
||||
return (
|
||||
<pattern
|
||||
id="grid"
|
||||
height="100"
|
||||
width="100"
|
||||
patternUnits="userSpaceOnUse"
|
||||
key="grid"
|
||||
>
|
||||
<path
|
||||
className="gridline lg metric"
|
||||
d="M 0 0 L 0 100 L 100 100"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline metric"
|
||||
d="M 50 0 L 50 100 M 0 50 L 100 50"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline sm metric"
|
||||
d="M 10 0 L 10 100 M 20 0 L 20 100 M 30 0 L 30 100 M 40 0 L 40 100 M 60 0 L 60 100 M 70 0 L 70 100 M 80 0 L 80 100 M 90 0 L 90 100"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline sm metric"
|
||||
d="M 0 10 L 100 10 M 0 20 L 100 20 M 0 30 L 100 30 M 0 40 L 100 40 M 0 60 L 100 60 M 0 70 L 100 70 M 0 80 L 100 80 M 0 90 L 100 90"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline xs metric"
|
||||
d="M 5 0 L 5 100 M 15 0 L 15 100 M 25 0 L 25 100 M 35 0 L 35 100 M 45 0 L 45 100 M 55 0 L 55 100 M 65 0 L 65 100 M 75 0 L 75 100 M 85 0 L 85 100 M 95 0 L 95 100"
|
||||
{...style}
|
||||
/>
|
||||
<path
|
||||
className="gridline xs metric"
|
||||
d="M 0 5 L 100 5 M 0 15 L 100 15 M 0 25 L 100 25 M 0 35 L 100 35 M 0 45 L 100 45 M 0 55 L 100 55 M 0 65 L 100 65 M 0 75 L 100 75 M 0 85 L 100 85 M 0 95 L 100 95"
|
||||
{...style}
|
||||
/>
|
||||
</pattern>
|
||||
);
|
||||
};
|
||||
|
||||
export default Grid;
|
1
packages/components/src/Draft/Defs/Grid/logo-path.js
Normal file
1
packages/components/src/Draft/Defs/Grid/logo-path.js
Normal file
File diff suppressed because one or more lines are too long
34
packages/components/src/Draft/Defs/Markers/index.js
Normal file
34
packages/components/src/Draft/Defs/Markers/index.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import React from "react";
|
||||
|
||||
const Markers = props => {
|
||||
const markerProps = {
|
||||
orient: "auto",
|
||||
refX: "0.0",
|
||||
refY: "0.0",
|
||||
style: { overflow: "visible" }
|
||||
};
|
||||
const from = { d: "M 0,0 L 12,-4 C 10,-2 10,2 12, 4 z" };
|
||||
const to = { d: "M 0,0 L -12,-4 C -10,-2 -10,2 -12, 4 z" };
|
||||
const types = {
|
||||
grainline: "note",
|
||||
cutonfold: "note",
|
||||
dimension: "mark"
|
||||
};
|
||||
let output = [];
|
||||
for (let type in types) {
|
||||
output.push(
|
||||
<marker id={type + "From"} key={type + "-from"} {...markerProps}>
|
||||
<path className={types[type] + " fill-" + types[type]} {...from} />
|
||||
</marker>
|
||||
);
|
||||
output.push(
|
||||
<marker id={type + "To"} key={type + "-to"} {...markerProps}>
|
||||
<path className={types[type] + " fill-" + types[type]} {...to} />
|
||||
</marker>
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
export default Markers;
|
41
packages/components/src/Draft/Defs/Snippets/index.js
Normal file
41
packages/components/src/Draft/Defs/Snippets/index.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import logoPathString from "./logo-path";
|
||||
|
||||
const Snippets = props => {
|
||||
const fill = { fill: "currentColor", stroke: "none" };
|
||||
const stroke = { fill: "none", stroke: "currentColor" };
|
||||
return [
|
||||
<g id="notch" className="snippet notch" key="notch">
|
||||
<circle cy="0" cx="0" r="1.4" {...fill} />
|
||||
<circle cy="0" cx="0" r="2.8" {...stroke} />
|
||||
</g>,
|
||||
<g id="bnotch" className="snippet bnotch" key="bnotch">
|
||||
<path d="M -1.1 -1.1 L 1.1 1.1 M 1.1 -1.1 L -1.1 1.1" {...stroke} />
|
||||
<circle cy="0" cx="0" r="2.8" {...stroke} />
|
||||
</g>,
|
||||
<g id="button" className="snippet button" key="button">
|
||||
<circle cx="0" cy="0" r="3.4" {...stroke} /> />
|
||||
<circle cx="-1" cy="-1" r="0.5" {...fill} />
|
||||
<circle cx="1" cy="-1" r="0.5" {...fill} />
|
||||
<circle cx="1" cy="1" r="0.5" {...fill} />
|
||||
<circle cx="-1" cy="1" r="0.5" {...fill} />
|
||||
</g>,
|
||||
<g id="buttonhole" className="snippet buttonhole" key="buttonhole">
|
||||
<path d="M -1,-5 L 1,-5 L 1,5 L -1,5 z" {...stroke} />
|
||||
<path
|
||||
d="M -1,-5 L 1,-5 L 1,-4 L -1,-4 z M -1,5 L 1,5 L 1,4 L -1,4 z"
|
||||
{...fill}
|
||||
/>
|
||||
</g>,
|
||||
<g
|
||||
id="logo"
|
||||
className="snippet logo"
|
||||
transform="translate(-23 -36)"
|
||||
key="logo"
|
||||
>
|
||||
<path d={logoPathString} {...fill} />
|
||||
</g>
|
||||
];
|
||||
};
|
||||
|
||||
export default Snippets;
|
1
packages/components/src/Draft/Defs/Snippets/logo-path.js
Normal file
1
packages/components/src/Draft/Defs/Snippets/logo-path.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,37 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import Markers from "./Markers";
|
||||
import Snippets from "./Snippets";
|
||||
import Grid from "./Grid";
|
||||
|
||||
const Defs = props => <defs>{props.defs}</defs>;
|
||||
|
||||
Defs.propTypes = { defs: PropTypes.string };
|
||||
Defs.defaultProps = { defs: "" };
|
||||
const Defs = props => {
|
||||
let paperlessGrids = null;
|
||||
if (props.paperless) {
|
||||
paperlessGrids = [];
|
||||
for (let p in props.parts) {
|
||||
let anchor = { x: 0, y: 0 };
|
||||
if (typeof props.parts[p].points.gridAnchor !== "undefined")
|
||||
anchor = props.parts[p].points.gridAnchor;
|
||||
else if (typeof props.parts[p].points.anchor !== "undefined")
|
||||
anchor = props.parts[p].points.anchor;
|
||||
paperlessGrids.push(
|
||||
<pattern
|
||||
id={"grid-" + p}
|
||||
key={"grid-" + p}
|
||||
xlinkHref="#grid"
|
||||
x={anchor.x}
|
||||
y={anchor.y}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<defs>
|
||||
<Markers />
|
||||
<Snippets />
|
||||
<Grid units={props.units} />
|
||||
{paperlessGrids}
|
||||
</defs>
|
||||
);
|
||||
};
|
||||
|
||||
export default Defs;
|
||||
|
|
|
@ -2,21 +2,45 @@ import React, { useState } from "react";
|
|||
import PropTypes from "prop-types";
|
||||
import Path from "../Path";
|
||||
import Point from "../Point";
|
||||
import Snippet from "../Snippet";
|
||||
import { getProps } from "../utils";
|
||||
|
||||
const Part = props => {
|
||||
console.log(props.part);
|
||||
let grid = props.paperless ? (
|
||||
<rect
|
||||
x={props.part.topLeft.x}
|
||||
y={props.part.topLeft.y}
|
||||
width={props.part.width}
|
||||
height={props.part.height}
|
||||
className="grid"
|
||||
fill={"url(#grid-" + props.name + ")"}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<g {...getProps(props.part)}>
|
||||
{grid}
|
||||
{Object.keys(props.part.points).map(name => (
|
||||
<Point
|
||||
key={name}
|
||||
name={name}
|
||||
part={props.name}
|
||||
language={props.language}
|
||||
point={props.part.points[name]}
|
||||
/>
|
||||
))}
|
||||
{Object.keys(props.part.paths).map(name => (
|
||||
<Path key={name} name={name} path={props.part.paths[name]} />
|
||||
<Path
|
||||
key={name}
|
||||
name={name}
|
||||
part={props.name}
|
||||
language={props.language}
|
||||
path={props.part.paths[name]}
|
||||
/>
|
||||
))}
|
||||
{Object.keys(props.part.snippets).map(name => (
|
||||
<Snippet key={name} name={name} snippet={props.part.snippets[name]} />
|
||||
))}
|
||||
</g>
|
||||
);
|
||||
|
@ -25,7 +49,9 @@ const Part = props => {
|
|||
Part.propTypes = {
|
||||
part: PropTypes.object.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
language: PropTypes.string.isRequired
|
||||
language: PropTypes.string.isRequired,
|
||||
paperless: PropTypes.bool.isRequired,
|
||||
units: PropTypes.oneOf(["metric", "imperial"]).isRequired
|
||||
};
|
||||
|
||||
export default Part;
|
||||
|
|
|
@ -1,14 +1,36 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import TextOnPath from "../TextOnPath";
|
||||
import { getProps } from "../utils";
|
||||
|
||||
const Path = props => {
|
||||
if (!props.path.render) return null;
|
||||
return <path d={props.path.asPathstring()} {...getProps(props.path)} />;
|
||||
const output = [];
|
||||
const pathId = "path-" + props.part + "-" + props.name;
|
||||
output.push(
|
||||
<path
|
||||
id={pathId}
|
||||
key={"path-" + props.name}
|
||||
d={props.path.asPathstring()}
|
||||
{...getProps(props.path)}
|
||||
/>
|
||||
);
|
||||
if (props.path.attributes.get("data-text"))
|
||||
output.push(
|
||||
<TextOnPath
|
||||
key={"text-on-path-" + props.name}
|
||||
pathId={pathId}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
Path.propTypes = {
|
||||
path: PropTypes.object.isRequired
|
||||
path: PropTypes.object.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
language: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default Path;
|
||||
|
|
39
packages/components/src/Draft/Snippet/index.js
Normal file
39
packages/components/src/Draft/Snippet/index.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Text from "../Text";
|
||||
import Circle from "../Circle";
|
||||
|
||||
const Snippet = props => {
|
||||
const snippetProps = {
|
||||
xlinkHref: "#" + props.snippet.def,
|
||||
x: props.snippet.anchor.x,
|
||||
y: props.snippet.anchor.y
|
||||
};
|
||||
let scale = props.snippet.attributes.get("data-scale");
|
||||
let rotate = props.snippet.attributes.get("data-rotate");
|
||||
if (scale || rotate) {
|
||||
snippetProps.transform = "";
|
||||
if (scale) {
|
||||
snippetProps.transform += `translate(${snippetProps.x}, ${
|
||||
snippetProps.y
|
||||
}) `;
|
||||
snippetProps.transform += `scale(${scale}) `;
|
||||
snippetProps.transform += `translate(${snippetProps.x *
|
||||
-1}, ${snippetProps.y * -1}) `;
|
||||
}
|
||||
if (rotate) {
|
||||
snippetProps.transform += `rotate(${rotate}, ${snippetProps.x}, ${
|
||||
snippetProps.y
|
||||
}) `;
|
||||
}
|
||||
}
|
||||
|
||||
return <use {...snippetProps} />;
|
||||
};
|
||||
|
||||
Snippet.propTypes = {
|
||||
snippet: PropTypes.object.isRequired,
|
||||
name: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default Snippet;
|
|
@ -30,7 +30,6 @@ const Text = props => {
|
|||
);
|
||||
}
|
||||
} else text.push(<tspan key="tspan-1">{translated}</tspan>);
|
||||
return null;
|
||||
return (
|
||||
<text
|
||||
x={props.point.x}
|
||||
|
@ -43,7 +42,8 @@ const Text = props => {
|
|||
};
|
||||
|
||||
Text.propTypes = {
|
||||
point: PropTypes.object.isRequired
|
||||
point: PropTypes.object.isRequired,
|
||||
language: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default Text;
|
||||
|
|
40
packages/components/src/Draft/TextOnPath/index.js
Normal file
40
packages/components/src/Draft/TextOnPath/index.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { strings } from "@freesewing/i18n";
|
||||
|
||||
const TextOnPath = props => {
|
||||
let text = [];
|
||||
// Handle translation
|
||||
let translated = "";
|
||||
for (let string of props.path.attributes.getAsArray("data-text")) {
|
||||
if (strings[props.language]["plugin." + string])
|
||||
translated += strings[props.language]["plugin." + string];
|
||||
else translated += string;
|
||||
translated += " ";
|
||||
}
|
||||
let textPathProps = {
|
||||
xlinkHref: "#" + props.pathId,
|
||||
startOffset: "0%"
|
||||
};
|
||||
let align = props.path.attributes.get("data-text-class");
|
||||
if (align && align.indexOf("center") > -1) textPathProps.startOffset = "50%";
|
||||
else if (align && align.indexOf("right") > -1)
|
||||
textPathProps.startOffset = "100%";
|
||||
|
||||
return (
|
||||
<text>
|
||||
<textPath {...textPathProps}>
|
||||
<tspan {...props.path.attributes.asPropsIfPrefixIs("data-text-")}>
|
||||
{translated}
|
||||
</tspan>
|
||||
</textPath>
|
||||
</text>
|
||||
);
|
||||
};
|
||||
|
||||
TextOnPath.propTypes = {
|
||||
path: PropTypes.object.isRequired,
|
||||
language: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default TextOnPath;
|
|
@ -1,93 +0,0 @@
|
|||
svg.freesewing {
|
||||
/* Reset */
|
||||
path,circle,rect{fill:none;stroke:none}
|
||||
|
||||
/* Defaults */
|
||||
path,circle{
|
||||
stroke:#000;
|
||||
stroke-opacity:1;
|
||||
stroke-width:.3;
|
||||
stroke-linecap:round;
|
||||
stroke-linejoin:round;
|
||||
}
|
||||
|
||||
/* Stroke classes */
|
||||
.fabric{
|
||||
stroke-width:.6;
|
||||
stroke:#212121
|
||||
}
|
||||
.lining{
|
||||
stroke-width:.6;
|
||||
stroke:#ff5b77;
|
||||
}
|
||||
.interfacing{
|
||||
stroke-width:.6;
|
||||
stroke:#64b5f6;
|
||||
}
|
||||
.canvas{
|
||||
stroke-width:.6;
|
||||
stroke:#ff9000;
|
||||
}
|
||||
.various{
|
||||
stroke-width:.6;
|
||||
stroke:#4caf50;
|
||||
}
|
||||
.note{
|
||||
stroke-width:.4;
|
||||
stroke:#dd60dd;
|
||||
}
|
||||
.mark{
|
||||
stroke-width:.4;
|
||||
stroke:blue;
|
||||
}
|
||||
.contrast{
|
||||
stroke-width:.8;
|
||||
stroke:red;
|
||||
}
|
||||
.stroke-xs{ stroke-width:.1; }
|
||||
.stroke-sm{ stroke-width:.2; }
|
||||
.stroke-lg{ stroke-width:.6; }
|
||||
.stroke-xl{ stroke-width:1; }
|
||||
.stroke-xxl{ stroke-width:2; }
|
||||
|
||||
.sa { stroke-dasharray:0.4,0.8; }
|
||||
.help {
|
||||
stroke-width:.2;
|
||||
stroke-dasharray:15,1.5,1,1.5;
|
||||
}
|
||||
.dotted { stroke-dasharray:0.4,0.8; }
|
||||
.dashed { stroke-dasharray:1,1.5; }
|
||||
.lashed { stroke-dasharray:6,6; }
|
||||
.hidden {
|
||||
stroke:none;
|
||||
fill:none;
|
||||
}
|
||||
|
||||
/* Fill classes */
|
||||
.fill-fabric{ fill:#212121; }
|
||||
.fill-lining{ fill:#ff5b77; }
|
||||
.fill-interfacing{ fill:#64b5f6; }
|
||||
.fill-canvas{ fill:#ff9000; }
|
||||
.fill-various{ fill:#4caf50; }
|
||||
.fill-note{ fill:#dd69dd; }
|
||||
.fill-mark{ fill:blue; }
|
||||
.fill-contrast{ fill:red; }
|
||||
|
||||
/* Text */
|
||||
text{
|
||||
font-size:5px;
|
||||
font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
|
||||
fill:#000;
|
||||
text-anchor:start;
|
||||
font-weight:200;
|
||||
dominant-baseline:ideographic;
|
||||
}
|
||||
.text-xs { font-size:3px; }
|
||||
.text-sm { font-size:4px; }
|
||||
.text-lg { font-size:7px; }
|
||||
.text-xl { font-size:9px; }
|
||||
.text-xxl{ font-size:12px; }
|
||||
|
||||
.center{ text-anchor:middle; }
|
||||
.right{ text-anchor:end; }
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Svg from "./Svg";
|
||||
//import Style from "./Style";
|
||||
//import Defs from "./Defs";
|
||||
import Defs from "./Defs";
|
||||
import Part from "./Part";
|
||||
|
||||
const Draft = props => {
|
||||
|
@ -14,11 +13,18 @@ const Draft = props => {
|
|||
language={props.settings.locale}
|
||||
id={props.settings.idPrefix + "svg"}
|
||||
>
|
||||
<Defs
|
||||
units={props.settings.units}
|
||||
parts={props.parts}
|
||||
paperless={props.settings.paperless}
|
||||
/>
|
||||
<g>
|
||||
{Object.keys(props.parts).map(name => (
|
||||
<Part
|
||||
part={props.parts[name]}
|
||||
language={props.settings.locale}
|
||||
paperless={props.settings.paperless}
|
||||
units={props.settings.units}
|
||||
key={name}
|
||||
name={name}
|
||||
/>
|
||||
|
|
|
@ -51,7 +51,7 @@ const OptionGroup = props => {
|
|||
return <Count {...option} {...extraProps} />;
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupport option type: " + type);
|
||||
throw new Error("Unsupported option type: " + type);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ const Footer = props => {
|
|||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div key={l}>
|
||||
<h4>
|
||||
<FormattedMessage id={"app." + l} />
|
||||
</h4>
|
||||
|
|
|
@ -58,6 +58,7 @@ const Workbench = props => {
|
|||
};
|
||||
const measurementsMissing = () => {
|
||||
let required = props.config.measurements;
|
||||
if (required.length < 1) return false;
|
||||
if (measurements === null) return true;
|
||||
for (let m of required) {
|
||||
if (typeof measurements[m] === "undefined") return true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue