diff --git a/packages/hortensia/CHANGELOG.md b/packages/hortensia/CHANGELOG.md new file mode 100644 index 00000000000..eedf4c7513b --- /dev/null +++ b/packages/hortensia/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change log for: @freesewing/hortensia + + + +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/hortensia/README.md b/packages/hortensia/README.md new file mode 100644 index 00000000000..2410f733c83 --- /dev/null +++ b/packages/hortensia/README.md @@ -0,0 +1,100 @@ +![FreeSewing](https://freesewing.org/banner.jpg) +

@freesewing/hortensia on NPM + License: MIT + Code quality on DeepScan + Open issues tagged pkg:hortensia +

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

+ +## What am I looking at? 🤔 + +This repository is our *monorepo* holding [all our NPM packages](https://www.npmjs.com/search?q=keywords:freesewing). +This folder holds **@freesewing/hortensia** + +A FreeSewing pattern for a handbag + + + +## About FreeSewing 💀 + +Where the world of makers and developers collide, that's where you'll find FreeSewing. + +Our [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox +for parametric design of sewing patterns. It's a modular system (check our list +of [plugins](https://freesewing.dev/reference/plugins/) and getting started is as simple as: + +```bash +npm init freesewing-pattern +``` + +The [getting started](https://freesewing.dev/guides/getting-started/) section on [freesewing.dev](https://freesewing.dev/) is a good +entrypoint to our documentation, but you'll find a lot more there, including +our [API reference](https://freesewing.dev/reference/api/), +as well as [our turorial](https://freesewing.dev/tutorials/pattern-design/), +and [howtos](https://freesewing.dev/howtos/). + +If you're a maker, checkout [freesewing.org](https://freesewing/) where you can generate +our sewing patterns adapted to your measurements. + +## 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, you too +should [become a patron](https://freesewing.org/patrons/join). + +## Links 👩‍💻 + + - 💻 Makers website: [freesewing.org](https://freesewing.org) + - 💻 Developers website: [freesewing.dev](https://freesewing.dev) + - 💬 Chat: On Discord via [chat.freesewing.org](https://chat.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). diff --git a/packages/hortensia/config/index.js b/packages/hortensia/config/index.js new file mode 100644 index 00000000000..c98ccc0eee4 --- /dev/null +++ b/packages/hortensia/config/index.js @@ -0,0 +1,44 @@ +import { version } from "../package.json"; + +// ?? 🤔 ?? --> https://en.freesewing.dev/packages/core/config + +export default { + name: "hortensia", + version, + design: "Stoffsuchti/WouterVdub", + code: "Stoffsuchti/WouterVdub", + department: "accessories", + type: "pattern", + difficulty: 3, + tags: [ + "freesewing", + "design", + "diy", + "fashion", + "made to measure", + "parametric design", + "pattern", + "sewing", + "sewing pattern" + ], + optionGroups: { + options: ["size", "zipperSize","strapLength","handleWidth"] + }, + measurements: [], + dependencies: {}, + inject: {}, + hide: [], + parts: ["sidepanel","frontpanel","bottompanel","zipperpanel","sidepanelreinforcement","strap"], + options: { + width: 230, + height: 330, + minHandleSpaceWidth: 80, + maxHandleSpaceWidth: 250, + pctHandleSpace: 50, + pctHandleVert: 42, + strapLength: { pct: 160, min: 75, max: 250 }, + handleWidth: { mm: 20, min: 7, max: 30 }, + size: { pct: 50, min: 20, max: 200 }, + zipperSize: { dflt: '#5', list: ['#3','#4','#4.5','#5','#6','#8','#10','Invisible']} + } +}; diff --git a/packages/hortensia/example/.babelrc b/packages/hortensia/example/.babelrc new file mode 100644 index 00000000000..6e3090a4956 --- /dev/null +++ b/packages/hortensia/example/.babelrc @@ -0,0 +1,10 @@ +{ + "plugins": [ + ["prismjs", { + "languages": ["javascript", "css", "markup"], + "plugins": ["line-numbers"], + "theme": "twilight", + "css": true + }] + ] +} diff --git a/packages/hortensia/example/README.md b/packages/hortensia/example/README.md new file mode 100644 index 00000000000..4c9c311b4b9 --- /dev/null +++ b/packages/hortensia/example/README.md @@ -0,0 +1,96 @@ +

+Freesewing logo +
+FreeSewing v2 +

+

A JavaScript library for made-to-measure sewing patterns

+

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

+ +# hortensia example + +This project was bootstrapped with [Create Freesewing Pattern](https://en.freesewing.dev/create-freesewing-pattern): + +```js +npm init freesewing-pattern +``` + +This example folder is part of the local development environment. +It is **not** part of the pattern's source code. + +To run this example, follow these steps: + + - In the folder above this one, run: `yarn start` (or `npm start`) + - Then, in new terminal, run the same command in this folder: `yarn start` (or `npm start`) + +This will spin up the development environment, similar to [our online demo](https://hortensia.freesewing.dev/). + +## About FreeSewing 🤔 + +Where the world of makers and developers collide, that's where you'll find FreeSewing. + +Our [core library](https://freesewing.dev/en/freesewing) is a *batteries-included* toolbox +for parametric design of sewing patterns. It's a modular system (check our list +of [plugins](https://freesewing.dev/en/plugins) and getting started is as simple as: + +```bash +npm init freesewing-pattern +``` + +The [getting started] section on [freesewing.dev](https://freesewing.dev/) is a good +entrypoint to our documentation, but you'll find a lot more there, including +our [API documentation](https://freesewing.dev/en/freesewing/api), +as well as [examples](https://freesewing.dev/en/freesewing/examples), +and [best practices](https://freesewing.dev/en/do). + +If you're a maker, checkout [freesewing.org](https://freesewing/) where you can generate +our sewing patterns adapted to your measurements. + +## 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, you too +should [become a patron](https://freesewing.org/patrons/join). + +## Links 👩‍💻 + + - 💻 Makers website: [freesewing.org](https://freesewing.org) + - 💻 Developers website: [freesewing.dev](https://freesewing.org) + - 💬 Chat: [gitter.im/freesewing](https://gitter.im/freesewing/freesewing) + - 🐦 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 [chatroom on Gitter](https://gitter.im) is 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). + diff --git a/packages/hortensia/example/netlify.toml b/packages/hortensia/example/netlify.toml new file mode 100644 index 00000000000..725041f5ef4 --- /dev/null +++ b/packages/hortensia/example/netlify.toml @@ -0,0 +1,9 @@ +[build] + base = "packages/hortensia/example" + publish = "build" + command = "npm run build" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 diff --git a/packages/hortensia/example/package.json b/packages/hortensia/example/package.json new file mode 100644 index 00000000000..3897a04ac73 --- /dev/null +++ b/packages/hortensia/example/package.json @@ -0,0 +1,51 @@ +{ + "name": "hortensia", + "homepage": "https://hortensia.freesewing.dev/", + "version": "", + "private": true, + "dependencies": { + "@fontsource/permanent-marker": "^4.1.0", + "@fontsource/roboto-mono": "^4.1.0", + "@fontsource/ubuntu": "^4.1.0", + "@freesewing/components": "latest", + "@freesewing/core": "latest", + "@freesewing/css-theme": "latest", + "@freesewing/i18n": "latest", + "@freesewing/models": "latest", + "@freesewing/mui-theme": "latest", + "@freesewing/pattern-info": "latest", + "@freesewing/plugin-bundle": "latest", + "@freesewing/plugin-theme": "latest", + "@freesewing/plugin-i18n": "latest", + "@freesewing/plugin-svgattr": "latest", + "@freesewing/utils": "latest", + "@material-ui/core": "^4.11.2", + "@material-ui/icons": "^4.11.2", + "@material-ui/lab": "^v4.0.0-alpha.57", + "pattern": "link:..", + "prismjs": "1.22.0", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "react-scripts": "^3.4.4", + "file-saver": "^2.0.5", + "react-markdown": "5.0.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencies": { + "babel-plugin-prismjs": "2.0.1" + } +} diff --git a/packages/hortensia/example/public/favicon.ico b/packages/hortensia/example/public/favicon.ico new file mode 100644 index 00000000000..95061a260f1 Binary files /dev/null and b/packages/hortensia/example/public/favicon.ico differ diff --git a/packages/hortensia/example/public/index.html b/packages/hortensia/example/public/index.html new file mode 100644 index 00000000000..c1105bd8ff0 --- /dev/null +++ b/packages/hortensia/example/public/index.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + hortensia + + + +
+ + + diff --git a/packages/hortensia/example/public/layout.css b/packages/hortensia/example/public/layout.css new file mode 100644 index 00000000000..c62502f9791 --- /dev/null +++ b/packages/hortensia/example/public/layout.css @@ -0,0 +1 @@ +div.layout-wrapper{width:100%;margin:0;padding:0;background-color:red;background:#f8f9fa;background:linear-gradient(90deg, #f1f3f5 0%, #f1f3f5 25%, #f8f9fa 26%, #f8f9fa 100%)}div.layout-wrapper div.layout{display:flex;max-width:1600px;margin:auto;padding:0;flex-direction:row;flex-wrap:nowrap;justify-content:space-between;background-color:#f8f9fa;min-height:calc(100vh - 64px)}div.layout-wrapper div.layout>aside{width:33%;background:#f1f3f5;border-right:2px solid #dee2e6}div.layout-wrapper div.layout>section{margin:0;padding:1rem}div.layout-wrapper div.layout>section>div.content{max-width:66ch;min-width:340px}div.layout-wrapper div.layout>section>div.content.wide{max-width:100%;margin:auto}.theme-wrapper.dark header{background-color:#1a1d21}.theme-wrapper.dark div.layout-wrapper{background:#f8f9fa;background:linear-gradient(90deg, #1a1d21 0%, #1a1d21 25%, #212529 26%, #212529 100%)}.theme-wrapper.dark div.layout-wrapper div.layout{background-color:#212529}.theme-wrapper.dark div.layout-wrapper div.layout>aside{background-color:#1a1d21;border-right:2px solid #343a40}header a svg{color:#ced4da}header a:first-of-type svg{color:#f8f9fa}header a:hover svg{color:#b197fc}header a span,header button span{color:#ced4da}header a span svg,header button span svg{color:#dee2e6}header a:hover span,header button:hover span{color:#f8f9fa}header a:hover span svg,header button:hover span svg{color:#b197fc}header a,header button{padding:0 1vw !important}@media (min-width: 1200px){div.layout>section{width:63%}}@media (max-width: 1199px) and (min-width: 960px){div.layout>aside{width:298px}div.layout>section{width:calc(100% - 300px - 4rem);max-width:none;margin:0 1rem 0 3rem}}@media (max-width: 959px){div.layout>aside{width:218px}div.layout>section{width:calc(100% - 220px - 4rem);max-width:none;margin:0;padding:0 2rem}div.layout>section div.content{min-width:inherit}}@media (max-width: 599px){div.layout>aside{display:none}div.layout>section{width:calc(100%);margin:0 auto;padding:0 1.5rem;max-width:none}}div.gatsby-highlight{margin-bottom:1rem}@media (max-width: 599px){#mobile-menu{position:fixed;top:0;left:0;width:100%;height:100vh;padding:0 0 1rem;max-width:600px;z-index:-10;transition:opacity 0.25s ease 0s;opacity:0;overflow:scroll}#mobile-menu>ul,#mobile-menu>div{transform:translate(0px, 10px);transition:transform 0.25s ease 0s}.theme-wrapper.show-menu #mobile-menu{opacity:1;z-index:10}.theme-wrapper.show-menu #mobile-menu>div{transform:translate(0px, 0px)}}.theme-wrapper.light div.draft-ui-menu,.theme-wrapper.light div.menu{background:#f1f3f5}.theme-wrapper.dark div.draft-ui-menu,.theme-wrapper.dark div.menu{background:#343a40}.theme-wrapper.show-menu div.menu{opacity:1;z-index:10}.theme-wrapper.show-menu div.menu>div{transform:translate(0px, 0px)}div.spaced-buttons>button{margin:0 0.5rem 0.5rem 0}div.spaced>*{margin:0 0.5rem 0.5rem 0}ul#pre-main-menu{margin:0;padding:0}.boldish{font-weight:500}.freesewing.draft{padding:1rem}li.action{clear:both}li.action span.MuiSwitch-root{float:right}.theme-wrapper.light ul#draft-config li.action.toggle.off,.theme-wrapper.dark ul#draft-config li.action.toggle.off{color:#868e96}.theme-wrapper.light ul#draft-config li.action.toggle.off>span svg,.theme-wrapper.dark ul#draft-config li.action.toggle.off>span svg{color:#868e96}footer{background-color:#1a1d21;color:#adb5bd;padding:3rem 0 6rem}footer a{color:#dee2e6 !important;font-weight:400}footer a:hover{color:#d0bfff !important}footer div.cols{display:flex;flex-direction:row;justify-content:space-between;max-width:1600px;margin:auto;padding:0 1.5rem}footer div.cols>div{min-width:150px;max-width:calc(20% - 4rem);padding:0 2rem 0 0;width:100%}footer ul{text-align:left;font-size:1.1rem;margin:0;padding:0;width:100%}footer ul li:first-of-type{padding:0.35rem 0.75rem}footer ul li{display:block}footer ul li a:hover{text-decoration:none !important}footer ul li.heading{font-weight:bold;border-bottom:3px solid #adb5bd;margin-bottom:0.5rem}@media (min-width: 1200px){footer div.cols>div:last-of-type{min-width:350px}}@media (min-width: 600px) and (max-width: 959px){footer div.cols{flex-wrap:wrap}footer div.cols>div{width:calc(30% - 4rem);padding:0 1rem}}@media (max-width: 599px){footer div.cols{display:block}footer div.cols>div{margin:2rem auto 0;max-width:calc(100% - 4rem)}footer div.cols>div:first-of-type{margin-top:0}} diff --git a/packages/hortensia/example/public/manifest.json b/packages/hortensia/example/public/manifest.json new file mode 100644 index 00000000000..3b564518b99 --- /dev/null +++ b/packages/hortensia/example/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "hortensia", + "name": "hortensia", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/packages/hortensia/example/src/App.js b/packages/hortensia/example/src/App.js new file mode 100644 index 00000000000..79b7751e342 --- /dev/null +++ b/packages/hortensia/example/src/App.js @@ -0,0 +1,35 @@ +import React from 'react' +import freesewing from '@freesewing/core' +import Workbench from '@freesewing/components/Workbench' +import '@freesewing/css-theme' + +import Pattern from 'pattern' + +const App = (props) => { + // You can use this to add transations + /* + let translations = { + JSON: 'JSON', + someOtherString: 'Some other string that needs translation' + } + */ + + // Adds support for loading an external pattern configuration + let recreate = false + if (window) recreate = window.location.pathname.substr(1).split('/') + if (recreate.length === 3 && recreate[0] === 'recreate') + recreate = { from: recreate[1], id: recreate[2] } + else recreate = false + + return ( + + ) +} + +export default App diff --git a/packages/hortensia/example/src/index.js b/packages/hortensia/example/src/index.js new file mode 100644 index 00000000000..9dd7ba788d4 --- /dev/null +++ b/packages/hortensia/example/src/index.js @@ -0,0 +1,11 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import App from './App' +import * as serviceWorker from './serviceWorker' + +ReactDOM.render(, document.getElementById('root')) + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: http://bit.ly/CRA-PWA +serviceWorker.unregister() diff --git a/packages/hortensia/example/src/serviceWorker.js b/packages/hortensia/example/src/serviceWorker.js new file mode 100644 index 00000000000..44e1b1b2f8c --- /dev/null +++ b/packages/hortensia/example/src/serviceWorker.js @@ -0,0 +1,123 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) +) + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location) + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config) + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://goo.gl/SC7cgQ' + ) + }) + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl, config) + } + }) + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.') + + // Execute callback + if (config.onUpdate) { + config.onUpdate(registration) + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.') + + // Execute callback + if (config.onSuccess) { + config.onSuccess(registration) + } + } + } + } + } + }) + .catch(error => { + console.error('Error during service worker registration:', error) + }) +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload() + }) + }) + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config) + } + }) + .catch(() => { + console.log('No internet connection found. App is running in offline mode.') + }) +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister() + }) + } +} diff --git a/packages/hortensia/package.json b/packages/hortensia/package.json new file mode 100644 index 00000000000..63ae81fc47e --- /dev/null +++ b/packages/hortensia/package.json @@ -0,0 +1,95 @@ +{ + "name": "@freesewing/hortensia", + "version": "2.12.1", + "description": "A FreeSewing pattern for a handbag", + "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" + }, + "keywords": [ + "freesewing", + "design", + "diy", + "fashion", + "made to measure", + "parametric design", + "pattern", + "sewing", + "sewing pattern" + ], + "main": "dist/index.js", + "module": "dist/index.mjs", + "scripts": { + "clean": "rimraf dist", + "build": "rollup -c", + "test": "BABEL_ENV=production ../../node_modules/.bin/_mocha tests/*.test.js --require @babel/register", + "pubtest": "npm publish --registry http://localhost:6662", + "pubforce": "npm publish", + "symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -", + "start": "rollup -c -w", + "netlify": "echo \"Not configured yet\"", + "testci": "BABEL_ENV=production ./node_modules/.bin/_mocha tests/*.test.js --require @babel/register" + }, + "peerDependencies": { + "@freesewing/core": "^2.12.1", + "@freesewing/plugin-bundle": "^2.12.1" + }, + "dependencies": {}, + "devDependencies": { + "react": "^16.13.1", + "react-dom": "^16.13.1", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "babel-eslint": "^10.1.0", + "eslint": "^7.6.0", + "babel-jest": "^26.2.2", + "jest": "26.2.2", + "@freesewing/components": "^2.12.1", + "@freesewing/css-theme": "^2.12.1", + "@freesewing/i18n": "^2.12.1", + "@freesewing/mui-theme": "^2.12.1", + "@freesewing/plugin-bust": "^2.12.1", + "@freesewing/plugin-buttons": "^2.12.1", + "@freesewing/plugin-flip": "^2.12.1", + "@freesewing/utils": "^2.12.1", + "@svgr/rollup": "^2.4.1", + "cross-env": "^7.0.2", + "react-scripts": "^3.4.1", + "webpack": "^4.44.1", + "rollup": "^2.23.0", + "@rollup/plugin-babel": "^5.1.0", + "rollup-plugin-terser": "^6.1.0", + "@rollup/plugin-commonjs": "^14.0.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^8.4.0", + "rollup-plugin-peer-deps-external": "^2.2.3", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "4.9.1", + "@material-ui/lab": "^v4.0.0-alpha.56", + "axios": "0.21.1", + "react-intl": "^5.4.5", + "prop-types": "^15.7.2", + "mocha": "^8.1.0", + "chai": "^4.2.0", + "chai-string": "^1.5.0", + "@babel/register": "^7.10.5" + }, + "files": [ + "dist/*", + "README.md", + "package.json" + ], + "publishConfig": { + "access": "public", + "tag": "latest" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6" + }, + "rollup": { + "exports": "default" + } +} diff --git a/packages/hortensia/rollup.config.js b/packages/hortensia/rollup.config.js new file mode 100644 index 00000000000..a885f4cb4ec --- /dev/null +++ b/packages/hortensia/rollup.config.js @@ -0,0 +1,37 @@ +import resolve from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' +import json from '@rollup/plugin-json' +import { terser } from 'rollup-plugin-terser' +import peerDepsExternal from 'rollup-plugin-peer-deps-external' +import { name, version, description, author, license, main, module, rollup } from './package.json' + +const output = [ + { + file: main, + format: 'cjs', + sourcemap: true, + exports: rollup.exports + } +] +if (typeof module !== 'undefined') + output.push({ + file: module, + format: 'es', + sourcemap: true + }) + +export default { + input: 'src/index.js', + output, + plugins: [ + peerDepsExternal(), + resolve({ modulesOnly: true }), + commonjs(), + json(), + terser({ + output: { + preamble: `/**\n * ${name} | v${version}\n * ${description}\n * (c) ${new Date().getFullYear()} ${author}\n * @license ${license}\n */` + } + }) + ] +} diff --git a/packages/hortensia/src/bottompanel.js b/packages/hortensia/src/bottompanel.js new file mode 100644 index 00000000000..b7ee21142fa --- /dev/null +++ b/packages/hortensia/src/bottompanel.js @@ -0,0 +1,83 @@ +export default function(part) { + let { + store, + options, + Point, + Path, + points, + paths, + Snippet, + snippets, + complete, + sa, + paperless, + macro + } = part.shorthand() + + let w = store.get( 'bottomPanelLength' ); + let h = store.get( 'depth' ); + + points.topLeft = new Point(0, 0) + points.topRight = new Point(w, 0) + points.bottomLeft = new Point(0, h) + points.bottomRight = new Point(w, h) + + paths.seam = new Path() + .move(points.topLeft) + .line(points.bottomLeft) + .line(points.bottomRight) + .line(points.topRight) + .line(points.topLeft) + .close() + .attr('class', 'fabric') + + // Complete? + if (complete) { + points.logo = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) + snippets.logo = new Snippet('logo', points.logo) + points.title = points.logo + .shift(-90, 50) + .attr("data-text-class", "center") + + macro("title", { + at: points.title, + nr: 3, + title: "BottomPanel" + }); + points.__titleNr.attr("data-text-class", "center"); + points.__titleName.attr("data-text-class", "center"); + points.__titlePattern.attr("data-text-class", "center"); + + let scaleBoxMove = 180 *options.size; + console.log('scaleBoxMove: ' +scaleBoxMove); + console.log('w: ' +w); + console.log('h: ' +h); + + if( scaleBoxMove > 50 && w > 100 ) { + points.scaleBox = points.logo.shift(90, scaleBoxMove); + macro("scalebox", { + at: points.scaleBox + }); + } + + if (sa) { + paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa') + } + } + + // Paperless? + if (paperless) { + macro('hd', { + from: points.bottomLeft, + to: points.bottomRight, + y: points.bottomLeft.y + sa + 15 + }) + macro('vd', { + from: points.bottomRight, + to: points.topRight, + x: points.topRight.x + sa + 15 + }) + } + + return part +} diff --git a/packages/hortensia/src/frontpanel.js b/packages/hortensia/src/frontpanel.js new file mode 100644 index 00000000000..ac8949cd0fd --- /dev/null +++ b/packages/hortensia/src/frontpanel.js @@ -0,0 +1,146 @@ +export default function (part) { + let { + store, + options, + Point, + Path, + points, + paths, + Snippet, + snippets, + complete, + sa, + paperless, + macro, + } = part.shorthand(); + + let w = store.get("frontPanelLength"); + let h = store.get("depth"); + + points.topLeft = new Point(0, 0); + points.topRight = new Point(w, 0); + points.bottomLeft = new Point(0, h); + points.bottomRight = new Point(w, h); + + paths.bottom = new Path() + .move(points.topLeft) + .line(points.bottomLeft) + .attr('data-text', 'Bottom') + .attr("data-text-class", "center text-xs") + + paths.top = new Path() + .move(points.bottomRight) + .line(points.topRight) + .attr('data-text', 'Top') + .attr("data-text-class", "center text-xs") + + //paths.seam = new Path() + paths.seam = paths.bottom + .line(points.bottomRight) + .join(paths.top) + .line(points.topLeft) + .close() + .attr("class", "fabric"); + + let pctHandleVert = options.pctHandleVert; + let handleWidth = options.handleWidth; + let handleSpace = (h - handleWidth * 2) * (options.pctHandleSpace / 100); + if (handleSpace > options.maxHandleSpaceWidth) { + handleSpace = options.maxHandleSpaceWidth; + } else if (handleSpace < options.minHandleSpaceWidth) { + handleSpace = options.minHandleSpaceWidth; + if (handleSpace < h - handleWidth * 2) { + handleSpace = h - handleWidth * 2; + } + } + let handleVertPos = w * (pctHandleVert / 100); + if (handleVertPos + handleWidth * 2 > w) { + handleVertPos = w - handleWidth * 2; + } + + points.attachPoint1TL = new Point(handleVertPos, 0 + h / 2 - handleSpace / 2); + points.attachPoint2TL = new Point( + handleVertPos, + h - h / 2 + handleSpace / 2 - handleWidth + ); + points.attachPoint2TLtemp = new Point( + handleVertPos, + h - h / 2 + handleSpace / 2 + ); + points.attachPoint1BR = new Point( + handleVertPos + handleWidth * 2, + 0 + h / 2 - handleSpace / 2 + handleWidth + ); + points.attachPoint2BR = new Point( + handleVertPos + handleWidth * 2, + h - h / 2 + handleSpace / 2 + ); + + macro("crossBox", { + from: points.attachPoint1TL, + to: points.attachPoint1BR, + text: "attachment", + }); + + macro("crossBox", { + from: points.attachPoint2TL, + to: points.attachPoint2BR, + text: "attachment", + }); + + // Complete? + if (complete) { + points.logo = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5); + snippets.logo = new Snippet("logo", points.logo); + points.title = points.logo.shift(-90, 50).attr("data-text-class", "center"); + + macro("title", { + at: points.title, + nr: 2, + title: "FrontBackPanel", + }); + points.__titleNr.attr("data-text-class", "center"); + points.__titleName.attr("data-text-class", "center"); + points.__titlePattern.attr("data-text-class", "center"); + + if (sa) { + paths.sa = paths.seam.offset(sa).attr("class", "fabric sa"); + } + } + + // Paperless? + if (paperless) { + macro("hd", { + from: points.bottomLeft, + to: points.bottomRight, + y: points.bottomLeft.y + sa + 15, + }); + macro("hd", { + from: points.topLeft, + to: points.attachPoint1TL, + y: points.attachPoint1TL.y, + }); + macro("hd", { + from: points.topLeft, + to: points.attachPoint2TLtemp, + y: points.attachPoint2TLtemp.y, + }); + macro("vd", { + from: points.bottomRight, + to: points.topRight, + x: points.topRight.x + sa + 15, + }); + macro("vd", { + from: points.topLeft, + to: points.attachPoint1TL, + x: points.attachPoint1TL.x, + }); + macro("vd", { + from: points.attachPoint2TLtemp, + to: points.bottomLeft, + x: points.attachPoint2TLtemp.x, + }); + } + + return part; +} diff --git a/packages/hortensia/src/index.js b/packages/hortensia/src/index.js new file mode 100644 index 00000000000..5bd8368ffc8 --- /dev/null +++ b/packages/hortensia/src/index.js @@ -0,0 +1,24 @@ + +import freesewing from '@freesewing/core' +import plugins from '@freesewing/plugin-bundle' +// import theme from '@freesewing/plugin-theme' +import config from '../config' +import draftSidepanel from './sidepanel' +import draftFrontpanel from './frontpanel' +import draftBottompanel from './bottompanel' +import draftZipperpanel from './zipperpanel' +import draftSidepanelreinforcement from './sidepanelreinforcement' +import draftStrap from './strap' + +// Create new design +const Pattern = new freesewing.Design(config, plugins ) + +// Attach the draft methods to the prototype +Pattern.prototype.draftSidepanel = draftSidepanel +Pattern.prototype.draftStrap = draftStrap +Pattern.prototype.draftBottompanel = draftBottompanel +Pattern.prototype.draftFrontpanel = draftFrontpanel +Pattern.prototype.draftZipperpanel = draftZipperpanel +Pattern.prototype.draftSidepanelreinforcement = draftSidepanelreinforcement + +export default Pattern diff --git a/packages/hortensia/src/sidepanel.js b/packages/hortensia/src/sidepanel.js new file mode 100644 index 00000000000..cec2f1995a1 --- /dev/null +++ b/packages/hortensia/src/sidepanel.js @@ -0,0 +1,218 @@ + +import bottomsidepanel from './bottomsidepanel' + +export default function (part) { + let { + store, + options, + Point, + Path, + points, + paths, + Snippet, + snippets, + complete, + sa, + paperless, + macro + } = part.shorthand() + + const c = 0.551915024494; // circle constant + const phi = 1.6180339887; + + const zWidth = new Map([['Invisible',0],['#3',4.8],['#4',5.4],['#4.5',5.9],['#5',6.2],['#6',7],['#8',8],['#10',10.6]]); + + const w = options.width *options.size; + const sizeRatio = (w / 230); + + const h = options.height * sizeRatio; + const d = h * phi; + + const sideLength = h * 0.44 //options.sideFactor; + const shoulderCP = 50 * sizeRatio; + const topCP = 30 * sizeRatio; + + const topRadius = 60 * sizeRatio; + + const sidePanelReinforcementHeight = h / phi / phi / phi / phi; + const zipperWidth = zWidth.get( options.zipperSize ); + const zipperPanelWidth = sidePanelReinforcementHeight / phi; + // console.log( '---' ); + // console.log( options.zipperSize ); + // console.log( zipperWidth ); + // console.log( '---' ); + + + store.set( 'width', w ); + store.set( 'depth', d ); + store.set( 'sizeRatio', sizeRatio ); + store.set( 'sideLenght', sideLength ); + store.set( 'shoulderCP', shoulderCP ); + store.set( 'topCP', topCP ); + store.set( 'topRadius', topRadius ); + store.set( 'sidePanelReinforcementHeight', sidePanelReinforcementHeight ); + store.set( 'zipperWidth', zipperWidth ); + store.set( 'zipperPanelWidth', zipperPanelWidth ); + + console.log( 'zipperWidth: ' +zipperWidth ); + console.log( 'zipperPanelWidth: ' +zipperPanelWidth ); + + + points.topCenter = new Point(0, 0); + points.topCircleLeft = points.topCenter.shift(135, topRadius); + points.topCircleRight = points.topCenter.shift(45, topRadius); + points.topCircleLeftCPu = points.topCircleLeft.shift(45, topRadius * c); + points.topCircleRightCPu = points.topCircleRight.shift(135, topRadius * c); + + points.topMiddle = points.topCenter.shift(90, topRadius); + points.topLeft = points.topMiddle.shift(180, w / 2); + points.topRight = points.topMiddle.shift(0, w / 2); + points.topMiddleCPL = points.topMiddle.shift(180, topCP); + points.topMiddleCPR = points.topMiddle.shift(0, topCP * 1.1); + + bottomsidepanel(points, points.topMiddle, w, h, sizeRatio); + + points.shoulderLeft = points.bottomLeft.shift(90, sideLength); + points.shoulderLeftCP = points.shoulderLeft.shift(90, shoulderCP); + points.shoulderRight = points.bottomRight.shift(90, sideLength); + points.shoulderRightCP = points.shoulderRight.shift(90, shoulderCP); + + // points.topCircleLeftCPd = points.topCircleLeft.shiftTowards( points.shoulderLeft, topCP ); + points.topCircleLeftCPd = points.topCircleLeft.shift(225, topCP); + points.topCircleRightCPd = points.topCircleRight.shift(315, topCP); + + points.bottomSeamLeft = points.bottomLeft.shift(90, sidePanelReinforcementHeight ); + points.bottomSeamRight = points.bottomRight.shift(90, sidePanelReinforcementHeight ); + points.bottomMiddle = points.bottomLeft.shift(0, w/2); + + let pBottom = new Path() + .move(points.bottomLeftU) + .curve(points.bottomLeftUcp, points.bottomLeftRcp, points.bottomLeftR) + .line(points.bottomRightL) + .curve(points.bottomRightLcp, points.bottomRightUcp, points.bottomRightU) + .line(points.bottomRightU) + + let pBottomPanel = new Path() + .move(points.bottomSeamLeft) + .join(pBottom) + .line(points.bottomSeamRight) + + let pTop = new Path() + .move(points.topCircleRight) + .curve(points.topCircleRightCPu, points.topCircleLeftCPu, points.topCircleLeft) + + let topCircleLength = pTop.length(); + + points.topZipperRight = pTop.shiftAlong( (topCircleLength/2) -(zipperWidth/2)) + points.topZipperLeft = pTop.shiftAlong( (topCircleLength/2) +(zipperWidth/2)) + points.topZipperPanelRight = pTop.shiftAlong( (topCircleLength/2) -(zipperPanelWidth/2)) + points.topZipperPanelLeft = pTop.shiftAlong( (topCircleLength/2) +(zipperPanelWidth/2)) + + store.set( 'bottomPanelLength', pBottomPanel.length() ); + console.log( 'bottomPanelLength: ' +pBottomPanel.length() ); + + let pSidesAndTop = new Path() + .move(points.bottomSeamRight) + .line(points.shoulderRight) + .curve(points.shoulderRightCP, points.topCircleRightCPd, points.topCircleRight) + .join( pTop ) + .curve(points.topCircleLeftCPd, points.shoulderLeftCP, points.shoulderLeft) + .line(points.bottomSeamLeft) + + let frontPanelLength = (pSidesAndTop.length() -zipperPanelWidth) /2; + + store.set( 'frontPanelLength', frontPanelLength ); + console.log( 'frontPanelLength: ' +frontPanelLength ); + + paths.seam = new Path() + .move(points.bottomRightU) + .join( pSidesAndTop ) + .join(pBottom) + .close() + .attr('class', 'fabric') + + + // Complete? + if (complete) { + if( options.size > .4 ) { + points.logo = points.topMiddle.shiftFractionTowards(points.bottomMiddle, 0.30) + snippets.logo = new Snippet('logo', points.logo) + } + + points.title = points.topMiddle.shiftFractionTowards(points.bottomMiddle, 0.60) + .attr("data-text-class", "center") + + macro("title", { + at: points.title, + nr: 1, + title: "SidePanel" + }); + + points.__titleNr.attr("data-text-class", "center"); + points.__titleName.attr("data-text-class", "center"); + points.__titlePattern.attr("data-text-class", "center"); + // points.__titleFor.attr("data-text-class", "center"); + + snippets.topNotch = new Snippet('notch', points.topMiddle); + snippets.zipperLeft = new Snippet('notch', points.topZipperLeft); + snippets.zipperRight = new Snippet('notch', points.topZipperRight); + snippets.zipperPanelLeft = new Snippet('notch', points.topZipperPanelLeft); + snippets.zipperPanelRight = new Snippet('notch', points.topZipperPanelRight); + snippets.bottomLeft = new Snippet('notch', points.bottomSeamLeft); + snippets.bottomRight = new Snippet('notch', points.bottomSeamRight); + snippets.bottomMiddle = new Snippet('notch', points.bottomMiddle); + + if (sa) { + paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa') + } + } + + // Paperless? + if (paperless) { + macro('hd', { + from: points.bottomLeftU, + to: points.bottomRightU, + y: points.bottomLeft.y + sa + 15 + }) + macro('hd', { + from: points.topZipperPanelLeft, + to: points.topZipperPanelRight, + y: points.topZipperPanelRight.y + 15 + }) + macro('vd', { + from: points.bottomRightL, + to: points.topMiddle, + x: points.topRight.x + sa + 15 + }) + macro('vd', { + from: points.bottomRightL, + to: points.shoulderRight, + x: points.bottomRightL.x + }) + macro('ld', { + from: points.topCenter, + to: points.topCircleLeft, + noStartMarker: true + }) + macro('ld', { + from: points.topCenter, + to: points.topCircleRight, + noStartMarker: true + }) + macro('ld', { + from: points.topCenter, + to: points.topMiddle, + noStartMarker: true + }) + // macro('pd', { + // path: pSidesAndTop, + // d: -20 + // }) + // macro('pd', { + // path: pTop, + // d: -30 + // }) + } + + return part +} diff --git a/packages/hortensia/src/sidepanelreinforcement.js b/packages/hortensia/src/sidepanelreinforcement.js new file mode 100644 index 00000000000..7949ffa994e --- /dev/null +++ b/packages/hortensia/src/sidepanelreinforcement.js @@ -0,0 +1,76 @@ + +import bottomsidepanel from './bottomsidepanel' + +export default function (part) { + let { + store, + options, + Point, + Path, + points, + paths, + Snippet, + snippets, + complete, + sa, + paperless, + macro + } = part.shorthand() + + const w = store.get('width'); + const h = store.get('sidePanelReinforcementHeight'); + const sizeRatio = store.get( 'sizeRatio' ); + + points.topMiddle = new Point(0, 0); + points.topLeft = points.topMiddle.shift(180, w / 2); + points.topRight = points.topMiddle.shift(0, w / 2); + + bottomsidepanel(points, points.topMiddle, w, h, sizeRatio); + + paths.seam = new Path() + .move(points.topMiddle) + .line(points.topLeft) + .line(points.bottomLeftU) + .curve(points.bottomLeftUcp, points.bottomLeftRcp, points.bottomLeftR) + .line(points.bottomRightL) + .curve(points.bottomRightLcp, points.bottomRightUcp, points.bottomRightU) + .line(points.topRight) + .line(points.topMiddle) + .close() + .attr('class', 'fabric') + + // Complete? + if (complete) { + points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) + .attr('data-text-class', 'center') + macro("title", { + at: points.title, + nr: 4, + title: "SidePanelReinforcement", + scale: 0.25 + }); + points.__titleNr.attr("data-text-class", "center"); + points.__titleName.attr("data-text-class", "center"); + points.__titlePattern.attr("data-text-class", "center"); + + if (sa) { + paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa') + } + } + + // Paperless? + if (paperless) { + macro('hd', { + from: points.bottomLeftU, + to: points.bottomRightU, + y: points.bottomLeft.y + sa + 15 + }) + macro('vd', { + from: points.bottomRightL, + to: points.topRight, + x: points.topRight.x + sa + 15 + }) + } + + return part +} diff --git a/packages/hortensia/src/strap.js b/packages/hortensia/src/strap.js new file mode 100644 index 00000000000..d233eaaf997 --- /dev/null +++ b/packages/hortensia/src/strap.js @@ -0,0 +1,83 @@ +export default function(part) { + let { + store, + options, + Point, + Path, + points, + paths, + Snippet, + snippets, + complete, + sa, + paperless, + macro + } = part.shorthand(); + + let w = options.handleWidth; + let h = store.get( 'depth' ) * options.strapLength; + if( sa > w *.80 ) { + sa = w *.80; + } + console.log( w ); + console.log( h ); + console.log( sa ); + + points.topLeft = new Point(-w, 0) + points.topMiddle = new Point(0, 0) + points.topRight = new Point(w, 0) + points.bottomLeft = new Point(-w, h) + points.bottomMiddle = new Point(0, h) + points.bottomRight = new Point(w, h) + + paths.seam = new Path() + .move(points.topLeft) + .line(points.bottomLeft) + .line(points.bottomRight) + .line(points.topRight) + .line(points.topLeft) + .close() + .attr('class', 'fabric') + + paths.fold = new Path() + .move(points.topMiddle) + .line(points.bottomMiddle) + .attr('data-text', 'FoldLine') + .attr("data-text-class", "center text-xs") + .attr('class', 'lining dashed') + + // Complete? + if (complete) { + points.title = points.topMiddle.shiftFractionTowards(points.bottomMiddle, 0.25) + macro("title", { + at: points.title, + nr: 5, + title: "BottomPanel", + rotation: 90, + scale: 0.25 + }); + points.__titleNr.attr("data-text-class", "center"); + points.__titleName.attr("data-text-class", "center"); + points.__titlePattern.attr("data-text-class", "center"); + + if (sa) { + paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa') + } + } + + // Paperless? + if (paperless) { + macro('hd', { + from: points.bottomLeft, + to: points.bottomRight, + y: points.bottomLeft.y + sa + 15 + }) + macro('vd', { + from: points.bottomRight, + to: points.topRight, + x: points.topRight.x + sa + 15 + }) + } + + return part +} diff --git a/packages/hortensia/src/zipperpanel.js b/packages/hortensia/src/zipperpanel.js new file mode 100644 index 00000000000..a859c6783b5 --- /dev/null +++ b/packages/hortensia/src/zipperpanel.js @@ -0,0 +1,66 @@ +export default function(part) { + let { + store, + options, + Point, + Path, + points, + paths, + Snippet, + snippets, + complete, + sa, + paperless, + macro + } = part.shorthand(); + + let z = store.get( 'zipperWidth' ); + let w = (store.get( 'zipperPanelWidth' ) -z) /2; + let h = store.get( 'depth' ); + console.log( z ); + console.log( w ); + console.log( h ); + + points.topLeft = new Point(0, 0) + points.topRight = new Point(w, 0) + points.bottomLeft = new Point(0, h) + points.bottomRight = new Point(w, h) + + paths.seam = new Path() + .move(points.topLeft) + .line(points.bottomLeft) + .line(points.bottomRight) + .line(points.topRight) + .line(points.topLeft) + .close() + .attr('class', 'fabric') + + // Complete? + if (complete) { + paths.text = new Path() + .move(points.topLeft) + .line(points.bottomLeft) + .attr('data-text', 'ZipperPanel') + .attr('data-text-class', 'center text-xs') + + if (sa) { + paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa') + } + } + + // Paperless? + if (paperless) { + macro('hd', { + from: points.bottomLeft, + to: points.bottomRight, + y: points.bottomLeft.y + sa + 15 + }) + macro('vd', { + from: points.bottomRight, + to: points.topRight, + x: points.topRight.x + sa + 15 + }) + } + + return part +} diff --git a/packages/hortensia/tests/shared.test.js b/packages/hortensia/tests/shared.test.js new file mode 100644 index 00000000000..1f4a5be0c59 --- /dev/null +++ b/packages/hortensia/tests/shared.test.js @@ -0,0 +1,39 @@ +// This file is auto-generated. +// Changes you make will be overwritten. +const expect = require("chai").expect; +const models = require("@freesewing/models") +const patterns = require("@freesewing/pattern-info") + +const Hortensia = require('../dist') + +// Shared tests +const testPatternConfig = require('../../../tests/patterns/config') +const testPatternDrafting = require('../../../tests/patterns/drafting') +const testPatternSampling = require('../../../tests/patterns/sampling') + +// Test config +testPatternConfig( + 'hortensia', + new Hortensia(), + expect, + models, + patterns +) + +// Test drafting +testPatternDrafting( + 'hortensia', + Hortensia, + expect, + models, + patterns +) + +// Test sampling +testPatternSampling( + 'hortensia', + Hortensia, + expect, + models, + patterns +)