diff --git a/config/exceptions.yaml b/config/exceptions.yaml index 106d3201095..07d1c992dc5 100644 --- a/config/exceptions.yaml +++ b/config/exceptions.yaml @@ -6,6 +6,7 @@ customBuild: - new-design - prettier-config - plugin-bundle + - react-components - rehype-jargon - rehype-highlight-lines skipTests: diff --git a/packages/react-components/CHANGELOG.md b/packages/react-components/CHANGELOG.md new file mode 100644 index 00000000000..5f230c77d5b --- /dev/null +++ b/packages/react-components/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change log for: @freesewing/react-components + + + +This is the **initial release**, and the start of this change log. + +> Prior to version 2, FreeSewing was not a JavaScript project. +> As such, that history is out of scope for this change log. + diff --git a/packages/react-components/README.md b/packages/react-components/README.md new file mode 100644 index 00000000000..350e3999662 --- /dev/null +++ b/packages/react-components/README.md @@ -0,0 +1,301 @@ +![FreeSewing](https://static.freesewing.org/banner.png) +

@freesewing/react-components on NPM + License: MIT + Code quality on DeepScan + Open issues tagged pkg:react-components + All Contributors +

Follow @freesewing_org on Twitter + Chat with us on Discord + Become a FreeSewing Patron + Follow @freesewing_org on Twitter +

+ +# @freesewing/react-components + +React components by/for FreeSewing + + + + +> #### Note: Version 3 is a work in progress +> +> We are working on a new major version (v3) but it is not ready for prime-time. +> For production use, please refer to our v2 packages (the `latest` on NPM) +> or [the `v2` branch in our monorepo](https://github.com/freesewing/freesewing/tree/v2). +> +> We the `main` branch and `next` packages on NPM holds v3 code. But it's alpha for now. + +## What am I looking at? πŸ€” + +This repository is our *monorepo* holding all our NPM designs, plugins, other NPM packages, and (web)sites. + +This folder holds: @freesewing/react-components + +If you're not entirely sure what to do or how to start, type this command: + +``` +npm run tips +``` + +> If you don't want to set up a dev environment, you can run it in your browser: +> +> [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/freesewing/freesewing) +> +> We recommend that you fork our repository and then +> put `gitpod.io/# to start up a browser-based dev environment of your own. + +## About FreeSewing πŸ’€ + +Where the world of makers and developers collide, that's where you'll find FreeSewing. + +If you're a maker, checkout [freesewing.org](https://freesewing.org/) where you can generate +our sewing patterns adapted to your measurements. + +If you're a developer, our documentation is on [freesewing.dev](https://freesewing.dev/). +Our [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox +for parametric design of sewing patterns. But we also provide a range +of [plugins](https://freesewing.dev/reference/plugins/) that further extend the +functionality of the platform. + +If you have NodeJS installed, you can try it right now by running: + +```bash +npx create-freesewing-pattern +``` + +Or, consult our getting started guides +for [Linux](https://freesewing.dev/tutorials/getting-started-linux/), +[MacOS](https://freesewing.dev/tutorials/getting-started-mac/), +or [Windows](https://freesewing.dev/tutorials/getting-started-windows/). + +We also have a [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) that +walks you through your first parametric design, +and [a friendly community](https://freesewing.org/community/where/) with +people who can help you when you get stuck. + +## Support FreeSewing: Become a patron πŸ₯° + +FreeSewing is an open source project run by a community, +and financially supported by our patrons. + +If you feel what we do is worthwhile, and you can spend a few coind without +hardship, then you should [join us and become a patron](https://freesewing.org/community/join). + +## Links πŸ‘©β€πŸ’» + + - πŸ’» Makers website: [freesewing.org](https://freesewing.org) + - πŸ’» Developers website: [freesewing.dev](https://freesewing.dev) + - πŸ’¬ Chat: On Discord via [discord.freesewing.org](https://discord.freesewing.org/) + - βœ… Todo list/Kanban board: On Github via [todo.freesewing.org](https://todo.freesewing.org/) + - 🐦 Twitter: [@freesewing_org](https://twitter.com/freesewing_org) + - πŸ“· Instagram: [@freesewing_org](https://instagram.com/freesewing_org) + +## License: MIT πŸ€“ + +Β© [Joost De Cock](https://github.com/joostdecock). +See [the license file](https://github.com/freesewing/freesewing/blob/develop/LICENSE) for details. + +## Where to get help 🀯 + +Our [chatrooms on Discord](https://chat.freesewing.org/) are the best place to ask questions, +share your feedback, or just hang out. + +If you want to report a problem, please [create an issue](https://github.com/freesewing/freesewing/issues/new). + + + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Adam Tomkins
Adam Tomkins

πŸ“–
Alexandre Ignjatovic
Alexandre Ignjatovic

πŸ’»
AlfaLyr
AlfaLyr

πŸ’» πŸ”Œ 🎨
Andrew James
Andrew James

πŸ“–
Anneke
Anneke

πŸ“– 🌍
Annie Kao
Annie Kao

πŸ“–
Anternative
Anternative

πŸ“–
Anthony
Anthony

πŸ’¬
Ari Grayzel-student
Ari Grayzel-student

πŸ’»
Bart
Bart

πŸ“–
BenJamesBen
BenJamesBen

πŸ’» πŸ“– πŸ›
Cameron Dubas
Cameron Dubas

πŸ“–
Carsten Biebricher
Carsten Biebricher

πŸ“–
Cathy Zoller
Cathy Zoller

πŸ“–
Chantal Lapointe
Chantal Lapointe

🌍
Damien PIQUET
Damien PIQUET

πŸ’»
Darigov Research
Darigov Research

πŸ“– πŸ€”
David Clegg
David Clegg

🎨 πŸ’»
Elena FdR
Elena FdR

πŸ“– πŸ“
Emmanuel Nyachoke
Emmanuel Nyachoke

πŸ’» πŸ“–
Enoch Riese
Enoch Riese

πŸ’»
EvEkSwed
EvEkSwed

🌍
Fantastik-Maman
Fantastik-Maman

🌍
Forrest O.
Forrest O.

πŸ“–
FrΓ©dΓ©ric
FrΓ©dΓ©ric

🌍
Glenn Matthews
Glenn Matthews

πŸ“–
Greg Sadetsky
Greg Sadetsky

πŸ“–
Igor Couto
Igor Couto

πŸ›
Ikko Ashimine
Ikko Ashimine

πŸ“–
Irapeke
Irapeke

🌍
Jacek Sawoszczuk
Jacek Sawoszczuk

πŸ“–
Jason Williams
Jason Williams

πŸ“–
Jeremy Jackson
Jeremy Jackson

πŸ’»
Jeroen Hoek
Jeroen Hoek

πŸ“–
Joe Schofield
Joe Schofield

πŸ“–
Joebidido
Joebidido

🌍
Joost De Cock
Joost De Cock

🚧
Josh Essman
Josh Essman

πŸ“–
Kake
Kake

πŸ“–
Kapunahele Wong
Kapunahele Wong

πŸ“–
Karen
Karen

πŸ“– πŸ“‹
Katie McGinley
Katie McGinley

πŸ“–
Kieran Klaassen
Kieran Klaassen

πŸ’»
Kittycatou
Kittycatou

🌍
Kris
Kris

πŸ“–
Kristin Ruben
Kristin Ruben

πŸ’»
Loudepeuter
Loudepeuter

🌍
Lucian
Lucian

πŸ“‹
Luiz Saggioro
Luiz Saggioro

πŸ’»
MA-TATAS
MA-TATAS

πŸ“–
Marcus
Marcus

🌍
Martin Tribo
Martin Tribo

πŸ“–
Nadege Michel
Nadege Michel

⚠️ πŸ“–
Natalia
Natalia

πŸ’» 🎨 πŸ“
Nathan Yergler
Nathan Yergler

πŸ“–
Nick Dower
Nick Dower

πŸ“– πŸ’» πŸ›
Nikhil Chelliah
Nikhil Chelliah

πŸ“–
OysteinHoiby
OysteinHoiby

πŸ’»
Patrick Forringer
Patrick Forringer

πŸ”Œ
Paul
Paul

πŸ“– πŸ“ 🌍
Phillip Thelen
Phillip Thelen

πŸ’»
Pixieish
Pixieish

πŸ“–
Prof. dr. Sorcha NΓ­ Dhubhghaill
Prof. dr. Sorcha NΓ­ Dhubhghaill

πŸ“–
Quentin FELIX
Quentin FELIX

πŸ’» 🎨
Rik Hekker
Rik Hekker

πŸ›
Sam Livingston-Gray
Sam Livingston-Gray

πŸ“–
Sanne
Sanne

πŸ’» πŸ“–
Sara Latorre
Sara Latorre

🌍
SeaZeeZee
SeaZeeZee

πŸ“– πŸ’»
SimonbJohnson
SimonbJohnson

πŸ›
SirCharlotte
SirCharlotte

🌍
Slylele
Slylele

πŸ“– 🌍
Soazillon
Soazillon

🌍
SoneaTheBest
SoneaTheBest

🌍
Stefan Sydow
Stefan Sydow

🌍 πŸ“– πŸ’»
TrΓ­ona
TrΓ­ona

πŸ“–
Unmutual
Unmutual

πŸ“–
Wouter van Wageningen
Wouter van Wageningen

πŸ’» 🎨 πŸ”§
amysews
amysews

πŸ“–
anna-puk
anna-puk

πŸ’»
beautifulsummermoon
beautifulsummermoon

🌍
berce
berce

πŸ“–
biou
biou

πŸ’»
bobgeorgethe3rd
bobgeorgethe3rd

πŸ’» πŸ“– 🎨
brmlyklr
brmlyklr

πŸ“–
chri5b
chri5b

πŸ’» ⚠️
dingcycle
dingcycle

🌍
drowned-in-books
drowned-in-books

πŸ’¬
econo202
econo202

πŸ“–
ericamattos
ericamattos

🌍
fightingrabbit
fightingrabbit

πŸ’»
gaylyndie
gaylyndie

πŸ“–
grimlokason
grimlokason

πŸ’»
hellgy
hellgy

🎨
jackseye
jackseye

πŸ“–
marckiesel
marckiesel

🌍
marpants
marpants

πŸ’»
mergerg
mergerg

πŸ“–
mesil
mesil

πŸ›
starfetch
starfetch

πŸ’» πŸ“– 🌍 🎨
timorl
timorl

πŸ’»
ttimearl
ttimearl

πŸ–‹
tuesgloomsday
tuesgloomsday

πŸ“–
valadaptive
valadaptive

πŸ’»
viocky
viocky

🌍
woolishboy
woolishboy

πŸ’»
yc
yc

🌍
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! + diff --git a/packages/react-components/build.mjs b/packages/react-components/build.mjs new file mode 100644 index 00000000000..30420e8d453 --- /dev/null +++ b/packages/react-components/build.mjs @@ -0,0 +1,36 @@ +/* This script will build the package with esbuild */ +import esbuild from 'esbuild' +import pkg from './package.json' assert { type: 'json' } + +// Create banner based on package info +const banner = `/** + * ${pkg.name} | v${pkg.version} + * ${pkg.description} + * (c) ${new Date().getFullYear()} ${pkg.author} + * @license ${pkg.license} + */` + +// Shared esbuild options +const options = { + banner: { js: banner }, + bundle: true, + entryPoints: ['src/index.mjs'], + format: 'esm', + outfile: 'dist/index.mjs', + external: ['@freesewing', 'react'], + metafile: process.env.VERBOSE ? true : false, + minify: process.env.NO_MINIFY ? false : true, + sourcemap: true, + loader: { '.mjs': 'jsx' }, +} + +// Let esbuild generate the build +const build = async () => { + const result = await esbuild.build(options).catch(() => process.exit(1)) + + if (process.env.VERBOSE) { + const info = await esbuild.analyzeMetafile(result.metafile) + console.log(info) + } +} +build() diff --git a/packages/react-components/data.mjs b/packages/react-components/data.mjs new file mode 100644 index 00000000000..0bc5ffa900d --- /dev/null +++ b/packages/react-components/data.mjs @@ -0,0 +1,4 @@ +// This file is auto-generated | All changes you make will be overwritten. +export const name = '@freesewing/react-components' +export const version = '3.0.0-alpha.10' +export const data = { name, version } diff --git a/packages/react-components/package.json b/packages/react-components/package.json new file mode 100644 index 00000000000..255d5702022 --- /dev/null +++ b/packages/react-components/package.json @@ -0,0 +1,54 @@ +{ + "name": "@freesewing/react-components", + "version": "3.0.0-alpha.10", + "description": "React components by/for FreeSewing", + "author": "Joost De Cock (https://github.com/joostdecock)", + "homepage": "https://freesewing.org/", + "repository": "github:freesewing/freesewing", + "license": "MIT", + "bugs": { + "url": "https://github.com/freesewing/freesewing/issues" + }, + "funding": { + "type": "individual", + "url": "https://freesewing.org/patrons/join" + }, + "keywords": [ + "freesewing", + "freesewing" + ], + "type": "module", + "module": "dist/index.mjs", + "exports": { + ".": "./dist/index.mjs" + }, + "scripts": { + "build": "node build.mjs", + "clean": "rimraf dist", + "mbuild": "NO_MINIFY=1 node build.mjs", + "symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -", + "test": "echo \"react-components: No tests configured. Perhaps you could write some?\" && exit 0", + "vbuild": "VERBOSE=1 node build.mjs", + "lab": "cd ../../sites/lab && yarn start", + "tips": "node ../../scripts/help.mjs", + "lint": "npx eslint 'src/**' 'tests/*.mjs'", + "cibuild_step6": "node build.mjs", + "wbuild": "node build.mjs", + "wcibuild_step6": "node build.mjs" + }, + "peerDependencies": {}, + "dependencies": {}, + "devDependencies": {}, + "files": [ + "dist/*", + "README.md" + ], + "publishConfig": { + "access": "public", + "tag": "next" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8" + } +} diff --git a/packages/react-components/src/index.mjs b/packages/react-components/src/index.mjs new file mode 100644 index 00000000000..80ab7b1c173 --- /dev/null +++ b/packages/react-components/src/index.mjs @@ -0,0 +1,27 @@ +import { Pattern as PatternComponent } from './pattern/index.mjs' +import { Svg as SvgComponent } from './pattern/svg.mjs' +import { Defs as DefsComponent } from './pattern/defs.mjs' +import { Group as GroupComponent } from './pattern/group.mjs' +import { Stack as StackComponent } from './pattern/stack.mjs' +import { Part as PartComponent } from './pattern/part.mjs' +import { Point as PointComponent } from './pattern/point.mjs' +import { Snippet as SnippetComponent } from './pattern/snippet.mjs' +import { Path as PathComponent } from './pattern/path.mjs' +import { Grid as GridComponent } from './pattern/grid.mjs' +import { Text as TextComponent, TextOnPath as TextOnPathComponent } from './pattern/text.mjs' + +/* + * Export all components as named exports + */ +export const Pattern = PatternComponent +export const Svg = SvgComponent +export const Defs = DefsComponent +export const Group = GroupComponent +export const Stack = StackComponent +export const Part = DefsComponent +export const Point = PointComponent +export const Path = PathComponent +export const Snippet = SnippetComponent +export const Grid = GridComponent +export const Text = TextComponent +export const TextOnPath = TextOnPathComponent diff --git a/packages/react-components/src/pattern/circle.mjs b/packages/react-components/src/pattern/circle.mjs new file mode 100644 index 00000000000..86609f251e7 --- /dev/null +++ b/packages/react-components/src/pattern/circle.mjs @@ -0,0 +1,14 @@ +import React from 'react' + +export const Circle = ({ point }) => + point.attributes.list['data-circle'].map((r, i) => { + const circleProps = point.attributes.circleProps + const extraProps = {} + for (const prop in circleProps) { + const val = point.attributes.list[`data-circle-${prop === 'className' ? 'class' : prop}`] + if (val.length >= i) extraProps[prop] = val[i] + else extraProps[prop] = val.join(' ') + } + + return + }) diff --git a/packages/react-components/src/pattern/defs.mjs b/packages/react-components/src/pattern/defs.mjs new file mode 100644 index 00000000000..e134cf49bb0 --- /dev/null +++ b/packages/react-components/src/pattern/defs.mjs @@ -0,0 +1,25 @@ +import React from 'react' + +const style = ` style="fill: none; stroke: currentColor;" ` +const grids = { + imperial: ``, + metric: ``, +} + +export const Defs = (props) => { + let defs = props.svg.defs.render() + if (props.settings[0].paperless) { + defs += grids[props.settings[0].units || 'metric'] + for (let p in props.parts[0]) { + let anchor = { x: 0, y: 0 } + if (typeof props.parts[0][p].points.gridAnchor !== 'undefined') + anchor = props.parts[0][p].points.gridAnchor + else if (typeof props.parts[0][p].points.anchor !== 'undefined') + anchor = props.parts[0][p].points.anchor + + defs += `` + } + } + + return +} diff --git a/packages/react-components/src/pattern/grid.mjs b/packages/react-components/src/pattern/grid.mjs new file mode 100644 index 00000000000..084963cc3e2 --- /dev/null +++ b/packages/react-components/src/pattern/grid.mjs @@ -0,0 +1,12 @@ +import React from 'react' + +export const Grid = ({ part, partName, settings }) => ( + +) diff --git a/packages/react-components/src/pattern/group.mjs b/packages/react-components/src/pattern/group.mjs new file mode 100644 index 00000000000..92d04df3de2 --- /dev/null +++ b/packages/react-components/src/pattern/group.mjs @@ -0,0 +1,3 @@ +import React from 'react' + +export const Group = (props) => {props.children} diff --git a/packages/react-components/src/pattern/index.mjs b/packages/react-components/src/pattern/index.mjs new file mode 100644 index 00000000000..ce185800aa1 --- /dev/null +++ b/packages/react-components/src/pattern/index.mjs @@ -0,0 +1,80 @@ +import React from 'react' +// Components that can be swizzled +import { Svg as DefaultSvg } from './svg.mjs' +import { Defs as DefaultDefs } from './defs.mjs' +import { Group as DefaultGroup } from './group.mjs' +import { Stack as DefaultStack } from './stack.mjs' +import { Part as DefaultPart } from './part.mjs' +import { Point as DefaultPoint } from './point.mjs' +import { Snippet as DefaultSnippet } from './snippet.mjs' +import { Path as DefaultPath } from './path.mjs' +import { Grid as DefaultGrid } from './grid.mjs' +import { Text as DefaultText, TextOnPath as DefaultTextOnPath } from './text.mjs' + +/* + * Allow people to swizzle these components + */ +const defaultComponents = { + Svg: DefaultSvg, + Defs: DefaultDefs, + Group: DefaultGroup, + Stack: DefaultStack, + Part: DefaultPart, + Point: DefaultPoint, + Path: DefaultPath, + Snippet: DefaultSnippet, + Grid: DefaultGrid, + Text: DefaultText, + TextOnPath: DefaultTextOnPath, +} + +export const Pattern = ({ + renderProps = false, + t = (string) => string, + components = {}, + children = false, + className = 'freesewing pattern', + ref = false, +}) => { + if (!renderProps) return null + + // Merge default and swizzled components + components = { + ...defaultComponents, + ...components, + } + + const { Svg, Defs, Stack, Group } = components + + const optionalProps = {} + if (ref) optionalProps.ref = ref + if (className) optionalProps.className = className + + return ( + + + + + {children + ? children + : Object.keys(renderProps.stacks).map((stackName) => ( + + ))} + + + ) +} diff --git a/packages/react-components/src/pattern/part.mjs b/packages/react-components/src/pattern/part.mjs new file mode 100644 index 00000000000..a415171e23c --- /dev/null +++ b/packages/react-components/src/pattern/part.mjs @@ -0,0 +1,50 @@ +import React, { forwardRef } from 'react' +import { getId, getProps } from './utils.mjs' + +export const PartInner = forwardRef( + ({ stackName, partName, part, settings, components, t }, ref) => { + const { Group, Grid, Path, Point, Snippet } = components + + return ( + + {settings.paperless ? : null} + {Object.keys(part.paths).map((pathName) => ( + + ))} + {Object.keys(part.points).map((pointName) => ( + + ))} + {Object.keys(part.snippets).map((snippetName) => ( + + ))} + + ) + } +) + +export const Part = ({ stackName, partName, part, settings, components, t }) => { + const { Group } = components + + return ( + + + + ) +} diff --git a/packages/react-components/src/pattern/path.mjs b/packages/react-components/src/pattern/path.mjs new file mode 100644 index 00000000000..16e4b71b2cd --- /dev/null +++ b/packages/react-components/src/pattern/path.mjs @@ -0,0 +1,22 @@ +import React from 'react' +import { getId, getProps } from './utils.mjs' + +export const Path = ({ stackName, pathName, path, partName, part, settings, components, t }) => { + // Don't render hidden paths + if (path.hidden) return null + + // Get potentially swizzled components + const { TextOnPath } = components + + const output = [] + const pathId = getId({ settings, stackName, partName, pathName }) + + return ( + <> + + {path.attributes.text.length > 0 ? : null} + + ) + + return output +} diff --git a/packages/react-components/src/pattern/point.mjs b/packages/react-components/src/pattern/point.mjs new file mode 100644 index 00000000000..e956c368fcd --- /dev/null +++ b/packages/react-components/src/pattern/point.mjs @@ -0,0 +1,17 @@ +import React from 'react' +import { withinPartBounds } from './utils.mjs' + +export const Point = ({ stackName, partName, pointName, part, point, settings, components, t }) => { + // Don't include points outside the part bounding box + if (!withinPartBounds(point, part)) return null + + // Get potentially swizzled components + const { Circle, Text } = components + + return point.attributes ? ( + <> + {point.attributes.text ? : null} + {point.attributes.circle ? : null} + + ) : null +} diff --git a/packages/react-components/src/pattern/snippet.mjs b/packages/react-components/src/pattern/snippet.mjs new file mode 100644 index 00000000000..43f859dcfd4 --- /dev/null +++ b/packages/react-components/src/pattern/snippet.mjs @@ -0,0 +1,26 @@ +import React from 'react' +import { getProps } from './utils.mjs' + +export const Snippet = ({ snippet }) => { + if (!snippet?.anchor || !snippet.def) return null + const snippetProps = { + xlinkHref: '#' + snippet.def, + x: snippet.anchor.x, + y: snippet.anchor.y, + } + const scale = snippet.attributes.list['data-scale']?.[0] || false + const rotate = snippet.attributes.list['data-rotate']?.[0] || false + 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 +} diff --git a/packages/react-components/src/pattern/stack.mjs b/packages/react-components/src/pattern/stack.mjs new file mode 100644 index 00000000000..7dc7da6956a --- /dev/null +++ b/packages/react-components/src/pattern/stack.mjs @@ -0,0 +1,18 @@ +import React from 'react' +import { getProps } from './utils.mjs' + +export const Stack = ({ stackName, stack, settings, components, t }) => { + const { Group, Part } = components + + return ( + + {[...stack.parts].map((part) => ( + + ))} + + ) +} diff --git a/packages/react-components/src/pattern/svg.mjs b/packages/react-components/src/pattern/svg.mjs new file mode 100644 index 00000000000..50cd0f58846 --- /dev/null +++ b/packages/react-components/src/pattern/svg.mjs @@ -0,0 +1,41 @@ +import React from 'react' +import { forwardRef } from 'react' + +export const Svg = forwardRef( + ( + { + embed = true, + locale = 'en', + className = 'freesewing pattern', + style = {}, + viewBox = false, + width, + height, + children, + }, + ref + ) => { + if (width < 1) width = 1000 + if (height < 1) height = 1000 + let attributes = { + xmlns: 'http://www.w3.org/2000/svg', + 'xmlns:svg': 'http://www.w3.org/2000/svg', + xmlnsXlink: 'http://www.w3.org/1999/xlink', + xmlLang: locale, + viewBox: viewBox || `0 0 ${width} ${height}`, + className, + style, + } + + if (!embed) { + attributes.width = width + 'mm' + attributes.height = height + 'mm' + } + + return ( + + {children} + + ) + } +) diff --git a/packages/react-components/src/pattern/text.mjs b/packages/react-components/src/pattern/text.mjs new file mode 100644 index 00000000000..d9cdbe4b8e5 --- /dev/null +++ b/packages/react-components/src/pattern/text.mjs @@ -0,0 +1,52 @@ +import React from 'react' +import { translateStrings } from './utils.mjs' + +export const TextSpans = ({ point, t }) => { + const translated = translateStrings(t, point.attributes.list['data-text']) + const text = [] + if (translated.indexOf('\n') !== -1) { + // Handle muti-line text + let key = 0 + let lines = translated.split('\n') + text.push({lines.shift()}) + for (let line of lines) { + key++ + text.push( + + {line.toString().replace(/"/g, '"')} + + ) + } + } else text.push({translated}) + + return text +} + +export const Text = ({ point, t }) => ( + + + +) + +export const TextOnPath = ({ path, pathId, t }) => { + const textPathProps = { + xlinkHref: '#' + pathId, + startOffset: '0%', + } + const translated = translateStrings(t, path.attributes.text) + const align = path.attributes.list['data-text-class'].join(' ') + if (align && align.indexOf('center') > -1) textPathProps.startOffset = '50%' + else if (align && align.indexOf('right') > -1) textPathProps.startOffset = '100%' + + return ( + + + + + + ) +} diff --git a/packages/react-components/src/pattern/utils.mjs b/packages/react-components/src/pattern/utils.mjs new file mode 100644 index 00000000000..b2d724d438f --- /dev/null +++ b/packages/react-components/src/pattern/utils.mjs @@ -0,0 +1,76 @@ +import React from 'react' + +export const getProps = (obj) => { + /** I can't believe it but there seems to be no method on NPM todo this */ + const cssKey = (key) => { + let chunks = key.split('-') + if (chunks.length > 1) { + key = chunks.shift() + for (let s of chunks) key += s.charAt(0).toUpperCase() + s.slice(1) + } + + return key + } + + const convert = (css) => { + let style = {} + let rules = css.split(';') + for (let rule of rules) { + let chunks = rule.split(':') + if (chunks.length === 2) style[cssKey(chunks[0].trim())] = chunks[1].trim() + } + return style + } + + let rename = { + class: 'className', + 'marker-start': 'markerStart', + 'marker-end': 'markerEnd', + } + let props = {} + for (let key in obj.attributes.list) { + if (key === 'style') props[key] = convert(obj.attributes.list[key].join(' ')) + if (Object.keys(rename).indexOf(key) !== -1) + props[rename[key]] = obj.attributes.list[key].join(' ') + else if (key !== 'style') props[key] = obj.attributes.list[key].join(' ') + } + + return props +} + +export const withinPartBounds = (point, part) => + point.x >= part.topLeft.x && + point.x <= part.bottomRight.x && + point.y >= part.topLeft.y && + point.y <= part.bottomRight.y + ? true + : false + +export const getId = ({ + settings, + stackName = false, + partName = false, + pathName = false, + pointName = false, + snippetName = false, + name = false, +}) => { + let id = settings.idPrefix || '' + if (stackName) id += `${stackName}-` + if (partName) id += `${partName}-` + if (pathName) id += `${pathName}-` + if (pointName) id += `${pointName}-` + if (snippetName) id += `${snippetName}-` + if (name) id += name + + return id +} + +export const translateStrings = (t, list) => { + let translated = '' + for (const string of list) { + if (string) translated += t(string.toString()).replace(/"/g, '"') + ' ' + } + + return translated +}