diff --git a/config/descriptions.yaml b/config/descriptions.yaml index 94a76afadbf..73e17e0dbd1 100644 --- a/config/descriptions.yaml +++ b/config/descriptions.yaml @@ -22,6 +22,7 @@ examples: 'A FreeSewing pattern holding examples for our documentation' florent: 'A FreeSewing pattern for a flat cap' florence: 'A FreeSewing pattern for a face mask' freesewing.dev: 'FreeSewing website with documentation for contributors & developers' +freesewing.lab: 'FreeSewing website to test various patterns' freesewing.org: 'FreeSewing website' freesewing.shared: 'Shared code and React components for different websites' gatsby-remark-jargon: 'A gatsby-transformer-remark sub-plugin for jargon terms' diff --git a/packages/freesewing.dev/components/header.js b/packages/freesewing.dev/components/header.js index 54aa8dd9870..ddabd24548f 100644 --- a/packages/freesewing.dev/components/header.js +++ b/packages/freesewing.dev/components/header.js @@ -60,8 +60,23 @@ const Header = ({ app, setSearch }) => { `} onClick={app.togglePrimaryMenu}> {app.primaryMenu - ? <>swipe - : <>swipe + ? ( + <> + + + + swipe + + + ) : ( + <> + + + + swipe + + + ) }
@@ -100,7 +115,10 @@ const Header = ({ app, setSearch }) => {
-
+
) } diff --git a/packages/freesewing.dev/hooks/useApp.js b/packages/freesewing.dev/hooks/useApp.js index c9a4f8c5264..57575a32f5b 100644 --- a/packages/freesewing.dev/hooks/useApp.js +++ b/packages/freesewing.dev/hooks/useApp.js @@ -6,6 +6,8 @@ import useLocalStorage from 'shared/hooks/useLocalStorage.js' import prebuildNavigation from 'site/prebuild/navigation.js' function useApp(full = true) { + // No translation for freesewing.dev + const language = 'en' // User color scheme preference const prefersDarkMode = (typeof window !== 'undefined' && typeof window.matchMedia === 'function') @@ -15,7 +17,6 @@ function useApp(full = true) { // Persistent state const [account, setAccount] = useLocalStorage('account', { username: false }) const [theme, setTheme] = useLocalStorage('theme', prefersDarkMode ? 'dark' : 'light') - const [language, setLanguage] = useLocalStorage('language', 'en') // React State const [primaryMenu, setPrimaryMenu] = useState(false) @@ -41,9 +42,9 @@ function useApp(full = true) { return { // Static vars site: 'dev', + language, // State - language, loading, navigation, primaryMenu, @@ -51,7 +52,6 @@ function useApp(full = true) { theme, // State setters - setLanguage, setLoading, setNavigation, setPrimaryMenu, @@ -63,6 +63,10 @@ function useApp(full = true) { // State handlers togglePrimaryMenu, + + // Dummy translation method + t: s => s, + i18n: false, } } diff --git a/packages/freesewing.lab/.eslintrc.json b/packages/freesewing.lab/.eslintrc.json new file mode 100644 index 00000000000..bffb357a712 --- /dev/null +++ b/packages/freesewing.lab/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/freesewing.lab/components/about.js b/packages/freesewing.lab/components/about.js new file mode 100644 index 00000000000..8c29b068b25 --- /dev/null +++ b/packages/freesewing.lab/components/about.js @@ -0,0 +1,25 @@ +import Popout from 'shared/components/popout.js' + +const About = () => ( + +

What to expect at lab.freesewing.org

+

+ The FreeSewing lab is an online environment to road-test our various patterns/designs. +

+

+ This website runs the bleeding edge of our code base. + Some patterns here may be unreleased or at various states of being worked on. + As such, this website is intended for FreeSewing contributors or people interested + in what happens under the hood. +

+

+ If you want sewing patterns to actually start making something, + please visit freesewing.org, our flagship website for makers. +

+
+) + +export default About diff --git a/packages/freesewing.lab/components/footer.js b/packages/freesewing.lab/components/footer.js new file mode 100644 index 00000000000..7d5ee65047c --- /dev/null +++ b/packages/freesewing.lab/components/footer.js @@ -0,0 +1,166 @@ +import NextLink from 'next/link' +import Logo from 'shared/components/logos/freesewing.js' +import contributors from 'site/prebuild/allcontributors.js' +import patrons from 'site/prebuild/patrons.js' +import OsiLogo from 'shared/components/logos/osi.js' +import CreativeCommonsLogo from 'shared/components/logos/cc.js' +import CcByLogo from 'shared/components/logos/cc-by.js' + +const Link = ({ href, txt }) => ( + + {txt} + +) +const link = "text-secondary font-bold hover:pointer hover:underline px-1" + +const social = { + Discord: 'https://discord.freesewing.org/', + Instagram: 'https://instagram.com/freesewing_org', + Facebook: 'https://www.facebook.com/groups/627769821272714/', + Github: 'https://github.com/freesewing', + Reddit: 'https://www.reddit.com/r/freesewing/', + Twitter: 'https://twitter.com/freesewing_org', +} + +const Footer = ({ app }) => ( +
+
+
+
+
+
+
+ +
+

+ Content on freesewing.org is licensed under + a Creative + Commons Attribution 4.0 International license +

+
+
+
+ +
+

+ Our source code and markdown is available + on GitHub under the MIT license +

+
+
+ +
+
{app.t('whatIsThis')}
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ +
+
Where can I turn for help?
+
+

+ Our Discord server is + the best place to ask questions and get help. It's where our community hangs out + so you'll get the fastest response and might even make a few new friends along the way. +

+

+ You can also reach out on Twitter or create an issue on Github if Discord is not your jam. +

+
+ +
+
Social Media
+
+
    + {Object.keys(social).map(item =>
  • )} +
+
+ +
+ +
FreeSewing
+

+ Come for the sewing patterns +
+ Stay for the community +

+
+
+

+ FreeSewing is made by these wonderful contributors +

+
+ {contributors.map(person => ( + + {`Avatar + + ))} +
+ +

+ FreeSewing is supported by these generous patrons +

+
+ {patrons.map(person => ( + + {`Avatar + + ))} +
+ +

+ FreeSewing is hosted by these awesome companies +

+ + +
+) + +export default Footer + diff --git a/packages/freesewing.lab/components/header.js b/packages/freesewing.lab/components/header.js new file mode 100644 index 00000000000..81723b22e37 --- /dev/null +++ b/packages/freesewing.lab/components/header.js @@ -0,0 +1,94 @@ +import { useState, useEffect } from 'react' +import Logo from 'shared/components/logos/freesewing.js' +import Link from 'next/link' +import ThemePicker from 'shared/components/theme-picker.js' +import LanguagePicker from 'shared/components/language-picker.js' +import PatternPicker from 'site/components/pattern-picker.js' +import CloseIcon from 'shared/components/icons/close.js' +import MenuIcon from 'shared/components/icons/menu.js' + +const Right = props => ( + + + +) +const Left = props => ( + + + +) + +const Header = ({ app }) => { + + const [prevScrollPos, setPrevScrollPos] = useState(0) + const [show, setShow] = useState(true) + + useEffect(() => { + if (typeof window !== 'undefined') { + const handleScroll = () => { + const curScrollPos = (typeof window !== 'undefined') ? window.pageYOffset : 0 + if (curScrollPos >= prevScrollPos) { + if (show && curScrollPos > 20) setShow(false) + } + else setShow(true) + setPrevScrollPos(curScrollPos) + } + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + } + }, [prevScrollPos, show]) + + + return ( +
+
+
+ +
+ +
+ +
+ + +
+
+
+
+
+ ) +} + +export default Header diff --git a/packages/freesewing.lab/components/help-us.js b/packages/freesewing.lab/components/help-us.js new file mode 100644 index 00000000000..772a9c3c829 --- /dev/null +++ b/packages/freesewing.lab/components/help-us.js @@ -0,0 +1,49 @@ +import Popout from 'shared/components/popout.js' + +const HelpUs = ({ mdx=false, slug='/' }) => ( +
+ Click here to learn how you can help us improve this page + {mdx && ( + +
Found a mistake?
+ You can edit this page on Github and help us improve our documentation. +
+ )} + +
Does this look ok?
+ +

+ If it looks ok, great! But if not, please let me know about it. + Either by + reaching out on Discord + or feel free to create + an issue on Github. +

+
Why do you ask?
+

+ I recently added a backend endpoint to auto-generate pretty (I hope) Open Graph images. + They are those little preview images you see when you paste a link in Discord (for example). +

+

+ This idea is that it will auto-generate an image, but I am certain there are some edge cases + where that will not work. + There are hundreds of pages on this website and checking them all one by one is not something + I see myself doing. But since you are here on this page, perhaps you could see if the image + above looks ok. +

+

Thank you, I really appreciate your help with this.

+
+
+) + +export default HelpUs + diff --git a/packages/freesewing.lab/components/pattern-picker.js b/packages/freesewing.lab/components/pattern-picker.js new file mode 100644 index 00000000000..7d5a85ee970 --- /dev/null +++ b/packages/freesewing.lab/components/pattern-picker.js @@ -0,0 +1,43 @@ +import React from 'react' +import config from 'site/freesewing.config.js' +import DesignIcon from 'shared/components/icons/design.js' +import Link from 'next/link' + +const PatternPicker = ({ app }) => { + const { t } = app + return ( +
+
+ + Patterns +
+
    + {Object.keys(config.patterns).map(section => ( + +
  • + {t(config.navigation[section].__title)} +
  • + {config.patterns[section].map(pattern => ( +
  • + + + +
  • + ))} +
    + ))} +
+
+ ) +} + +export default PatternPicker diff --git a/packages/freesewing.lab/components/search.js b/packages/freesewing.lab/components/search.js new file mode 100644 index 00000000000..ad4e17b5767 --- /dev/null +++ b/packages/freesewing.lab/components/search.js @@ -0,0 +1 @@ +export default () => null diff --git a/packages/freesewing.lab/freesewing.config.js b/packages/freesewing.lab/freesewing.config.js new file mode 100644 index 00000000000..7c913fe76ae --- /dev/null +++ b/packages/freesewing.lab/freesewing.config.js @@ -0,0 +1,103 @@ +const patterns = { + accessories: [ + 'florence', + 'hortensia', + 'florent', + 'holmes', + ], + blocks: [ + 'bella', + 'bent', + 'brian', + 'titan', + ], + garments: [ + 'aaron', + 'albert', + 'bee', + 'benjamin', + 'breanna', + 'bruce', + 'carlita', + 'carlton', + 'cathrin', + 'charlie', + 'cornelius', + 'diana', + 'huey', + 'hugo', + 'jaeger', + 'lunetius', + 'paco', + 'penelope', + 'sandy', + 'shin', + 'simon', + 'simone', + 'sven', + 'tamiko', + 'teagan', + 'theo', + 'tiberius', + 'trayvon', + 'ursula', + 'wahid', + 'walburga', + 'waralee', + 'yuri', + ], + utilities: [ + 'examples', + 'legend', + 'plugintest', + 'rendertest', + 'tutorial', + ], +} + +const navigation = { + accessories: { + __title: 'accessoryPatterns', + __order: 'accessoryPatterns', + __linktitle: 'accessoryPatterns', + __slug: 'accessories', + }, + blocks: { + __title: 'blockPatterns', + __order: 'blockPatterns', + __linktitle: 'blockPatterns', + __slug: 'blocks', + }, + garments: { + __title: 'garmentPatterns', + __order: 'garmentPatterns', + __linktitle: 'GarmentPatterns', + __slug: 'garments', + }, + utilities: { + __title: 'utilityPatterns', + __order: 'utilityPatterns', + __linktitle: 'utilityPatterns', + __slug: 'utilities', + }, +} +for (const type in patterns) { + for (const design of patterns[type]) { + navigation[type][design] = { + __title: design, + __order: design, + __linktitle: design, + __slug: `${type}/${design}` + } + } +} + + +const config = { + monorepo: 'https://github.com/freesewing/freesewing', + navigation, + patterns, +} + +export default config + diff --git a/packages/freesewing.lab/hooks/useApp.js b/packages/freesewing.lab/hooks/useApp.js new file mode 100644 index 00000000000..332dbe7ee71 --- /dev/null +++ b/packages/freesewing.lab/hooks/useApp.js @@ -0,0 +1,102 @@ +import { useState } from 'react' +import set from 'lodash.set' +// Stores state in local storage +import useLocalStorage from 'shared/hooks/useLocalStorage.js' +// config +import config from 'site/freesewing.config.js' +// Languages +import { strings } from 'pkgs/i18n' + +const translateNavigation = (lang, t) => { + const newNav = {...config.navigation} + for (const key in newNav) { + const translated = t(newNav[key].__title, false, lang) + newNav[key].__title = translated + newNav[key].__linktitle = translated + newNav[key].__order = translated + } + + return newNav +} + + +function useApp(full = true) { + + // User color scheme preference + const prefersDarkMode = (typeof window !== 'undefined' && typeof window.matchMedia === 'function') + ? window.matchMedia(`(prefers-color-scheme: dark`).matches + : null + + // Persistent state + const [account, setAccount] = useLocalStorage('account', { username: false }) + const [theme, setTheme] = useLocalStorage('theme', prefersDarkMode ? 'dark' : 'light') + const [language, setLanguage] = useLocalStorage('language', 'en') + + // React State + const [primaryMenu, setPrimaryMenu] = useState(false) + const [navigation, setNavigation] = useState(config.navigation) + const [slug, setSlug] = useState('/') + const [pattern, setPattern] = useState(false) + const [loading, setLoading] = useState(false) + + // State methods + const togglePrimaryMenu = () => setPrimaryMenu(!primaryMenu) + const changeLanguage = lang => { + setLanguage(lang) + setNavigation(translateNavigation(lang, t)) + } + + /* + * Translation method + */ + const t = (key, props=false, toLanguage=false) => { + if (!toLanguage) toLanguage = language + if (!props) { // easy + if (strings[toLanguage][key]) return strings[toLanguage][key] + // app is the most common prefix, so we allow to skip it + if (strings[toLanguage][`app.${key}`]) return strings[toLanguage][`app.${key}`] + // Can we fall back to English? + if (toLanguage !== 'en') { + if (strings.en[key]) return strings.en[key] + if (strings.en[`app.${key}`]) return strings.en[`app.${key}`] + } + } + console.log('Missing translation key:', key) + + return key + } + + return { + // Static vars + site: 'lab', + + // State + language, + loading, + navigation, + pattern, + primaryMenu, + slug, + theme, + + // State setters + setLoading, + setNavigation, + setPattern, + setPrimaryMenu, + setSlug, + setTheme, + startLoading: () => { setLoading(true); setPrimaryMenu(false) }, // Always close menu when navigating + stopLoading: () => setLoading(false), + + // State handlers + togglePrimaryMenu, + changeLanguage, + + // Translation + t, + } +} + +export default useApp + diff --git a/packages/freesewing.lab/next.config.mjs b/packages/freesewing.lab/next.config.mjs new file mode 100644 index 00000000000..086bd6666a4 --- /dev/null +++ b/packages/freesewing.lab/next.config.mjs @@ -0,0 +1,3 @@ +import configBuilder from '../freesewing.shared/config/next.mjs' + +export default configBuilder('lab') diff --git a/packages/freesewing.lab/package.json b/packages/freesewing.lab/package.json new file mode 100644 index 00000000000..e005bb7806e --- /dev/null +++ b/packages/freesewing.lab/package.json @@ -0,0 +1,54 @@ +{ + "name": "freesewing.dev", + "version": "2.19.9", + "private": true, + "scripts": { + "dev": "next dev -p 3002", + "develop": "next dev -p 3002", + "prebuild": "SITE=dev node ../freesewing.shared/prebuild/index.mjs", + "build": "next build", + "export": "yarn prebuild && next build && next export", + "start": "next start -p 3002", + "lint": "next lint", + "testdeploy": "next build && next export && netlify-cli deploy", + "deploy": "next build && next export && netlify-cli deploy --prod", + "serve": "pm2 start npm --name 'freesewing.dev' -- run start" + }, + "dependencies": { + "@heroicons/react": "^1.0.5", + "@mdx-js/loader": "^2.0.0-rc.2", + "@mdx-js/mdx": "^2.0.0-rc.2", + "@mdx-js/react": "^2.0.0-rc.2", + "@mdx-js/runtime": "next", + "@tailwindcss/typography": "^0.5.0", + "algoliasearch": "^4.11.0", + "daisyui": "^1.16.2", + "lodash.get": "^4.4.2", + "lodash.orderby": "^4.6.0", + "lodash.set": "^4.3.2", + "netlify-cli": "^8.4.2", + "next": "latest", + "react-copy-to-clipboard": "^5.0.4", + "react-hotkeys-hook": "^3.4.4", + "react-instantsearch-dom": "^6.18.0", + "react-markdown": "^7.1.1", + "react-swipeable": "^6.2.0", + "react-timeago": "^6.2.1", + "rehype-highlight": "^5.0.1", + "rehype-sanitize": "^5.0.1", + "rehype-stringify": "^9.0.2", + "remark-copy-linked-files": "https://github.com/joostdecock/remark-copy-linked-files", + "remark-gfm": "^3.0.1", + "remark-jargon": "^2.19.9" + }, + "devDependencies": { + "autoprefixer": "^10.4.0", + "eslint-config-next": "12.0.7", + "js-yaml": "^4.1.0", + "postcss": "^8.4.4", + "tailwindcss": "^3.0.1" + }, + "engines": { + "node": ">=14.18.1" + } +} diff --git a/packages/freesewing.lab/page-templates/pattern-list.js b/packages/freesewing.lab/page-templates/pattern-list.js new file mode 100644 index 00000000000..b2854bc9855 --- /dev/null +++ b/packages/freesewing.lab/page-templates/pattern-list.js @@ -0,0 +1,51 @@ +import Page from 'shared/components/wrappers/page.js' +import useApp from 'site/hooks/useApp.js' +import Head from 'next/head' +import Link from 'next/link' +import config from 'site/freesewing.config.js' +import About from 'site/components/about.js' + +const links = (section, list) => list.map(design => ( +
  • + + {design} + +
  • +)) + +const ListPage = ({ sections=Object.keys(config.patterns) }) => { + const app = useApp() + return ( + + + + + + + + + + + + + + +
    + {Object.keys(config.navigation).map(section => { + if (sections.indexOf(section) !== -1) return ( +
    +

    {config.navigation[section].__title}

    +
      + {links(section, config.patterns[section])} +
    +
    + ) + else return null + })} + +
    +
    + ) +} + +export default ListPage diff --git a/packages/freesewing.lab/page-templates/workbench.js b/packages/freesewing.lab/page-templates/workbench.js new file mode 100644 index 00000000000..575eff69926 --- /dev/null +++ b/packages/freesewing.lab/page-templates/workbench.js @@ -0,0 +1,16 @@ +import Page from 'shared/components/wrappers/page.js' +import useApp from 'site/hooks/useApp.js' +import WorkbenchWrapper from 'shared/components/wrappers/workbench.js' + +const WorkbenchPage = ({ pattern }) => { + const app = useApp() + + return ( + + + + ) +} + +export default WorkbenchPage + diff --git a/packages/freesewing.lab/pages/_app.js b/packages/freesewing.lab/pages/_app.js new file mode 100644 index 00000000000..8d56d327f58 --- /dev/null +++ b/packages/freesewing.lab/pages/_app.js @@ -0,0 +1,5 @@ +import 'shared/styles/globals.css' + +const FreeSewingLab = ({ Component, pageProps }) => + +export default FreeSewingLab diff --git a/packages/freesewing.lab/pages/accessories/index.js b/packages/freesewing.lab/pages/accessories/index.js new file mode 100644 index 00000000000..341a7ccd6b1 --- /dev/null +++ b/packages/freesewing.lab/pages/accessories/index.js @@ -0,0 +1,5 @@ +import Template from 'site/page-templates/pattern-list.js' + +const Page = props =>