diff --git a/.eslintrc.cjs b/.eslintrc.cjs index b6fe3292172..f91c6fb0f0d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -27,6 +27,7 @@ module.exports = { extends: 'eslint:recommended', env: { es2021: true, + node: true, }, // Required when using experimental EcmaScript features parser: '@babel/eslint-parser', diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 6278305e403..27419f4d7e5 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -62,7 +62,7 @@ core: 'lodash.unset': &_unset '4.5.2' 'lodash.clonedeep': '^4.5.0' dev: - 'eslint': &eslint '8.42.0' + 'eslint': &eslint '8.43.0' 'nyc': '15.1.0' 'mocha': *mocha 'chai': *chai @@ -192,12 +192,12 @@ yuri: backend: _: - '@aws-sdk/client-sesv2': '3.352.0' - '@prisma/client': &prisma '4.15.0' + '@aws-sdk/client-sesv2': '3.354.0' + '@prisma/client': &prisma '4.16.1' 'bcryptjs': '2.4.3' 'cors': '2.8.5' 'crypto': '1.0.1' - 'dotenv': '16.1.4' + 'dotenv': '16.3.1' 'express': '4.18.2' 'js-yaml': *jsyaml 'lodash.get': *_get @@ -208,12 +208,12 @@ backend: 'passport-jwt': '4.0.1' 'pino': '8.14.1' 'qrcode': '1.5.3' - 'swagger-ui-dist': '4.19.0' + 'swagger-ui-dist': '5.1.0' 'swagger-ui-express': '4.6.3' dev: 'chai': *chai 'chai-http': '4.4.0' - 'esbuild': '0.18.2' + 'esbuild': '0.18.8' 'mocha': *mocha 'mocha-steps': '1.3.0' 'nodemon': '2.0.22' @@ -225,10 +225,10 @@ dev: '@mdx-js/mdx': *mdx '@mdx-js/react': *mdx '@mdx-js/runtime': &mdxRuntime '2.0.0-next.9' - '@next/bundle-analyzer': &next '13.4.6' + '@next/bundle-analyzer': &next '13.4.7' '@tailwindcss/typography': &tailwindTypography '0.5.9' - 'algoliasearch': '4.17.2' - 'daisyui': &daisyui '3.1.0' + 'algoliasearch': '4.18.0' + 'daisyui': &daisyui '3.1.1' 'lodash.get': *_get 'lodash.orderby': &_orderby '4.6.0' 'lodash.set': *_set @@ -238,7 +238,7 @@ dev: 'react-dom': *react 'react-hotkeys-hook': &reactHotkeysHook '4.4.0' 'react-instantsearch-dom': &reactInstantsearchDom '6.40.0' - 'react-instantsearch-hooks-web': '6.44.2' + 'react-instantsearch-hooks-web': '6.45.0' 'react-markdown': &reactMarkdown '8.0.7' 'react-swipeable': &reactSwipeable '7.0.1' 'react-timeago': &reactTimeago '7.1.0' @@ -276,7 +276,7 @@ lab: '@mdx-js/react': *mdx '@mdx-js/runtime': *mdxRuntime '@tailwindcss/typography': *tailwindTypography - 'algoliasearch': &algoliasearch '4.17.2' + 'algoliasearch': &algoliasearch '4.18.0' 'd3-dispatch': '3.0.1' 'd3-drag': '3.0.0' 'd3-selection': '3.0.0' @@ -286,7 +286,7 @@ lab: 'lodash.orderby': *_orderby 'lodash.set': *_set 'next': *next - 'next-i18next': &nextI18next '13.3.0' + 'next-i18next': &nextI18next '14.0.0' 'react': *react 'react-copy-to-clipboard': *reactCopyToClipboard 'react-hotkeys-hook': *reactHotkeysHook @@ -314,15 +314,20 @@ org: '@mdx-js/mdx': *mdx '@mdx-js/react': *mdx '@mdx-js/runtime': *mdxRuntime + '@portabletext/react': '^1.0.6' + '@sanity/client': '^6.1.2' '@tailwindcss/typography': *tailwindTypography 'algoliasearch': *algoliasearch 'react-copy-to-clipboard': 5.1.0 'daisyui': *daisyui + 'jotai': &jotai '2.1.1' + 'jotai-location': &jotai-location '0.5.1' 'lodash.get': *_get 'lodash.orderby': *_orderby 'lodash.set': *_set 'luxon': '3.3.0' 'next': *next + 'next-sanity': '^4.3.3' 'react-dropzone': '14.2.3' 'react-hotkeys-hook': *reactHotkeysHook 'react-instantsearch-dom': *reactInstantsearchDom @@ -344,8 +349,8 @@ org: sanity: _: - '@sanity/vision': &sanity '3.12.0' - 'easymde': '2.16.0' + '@sanity/vision': &sanity '3.12.2' + 'easymde': '2.18.0' 'react': *react 'react-dom': *react 'react-is': *react @@ -357,12 +362,12 @@ sanity: 'eslint': *eslint 'prettier': '2.8.8' 'typescript': '5.1.3' - '@sanity/cli': '3.12.1' + '@sanity/cli': '3.12.2' shared: _: '@headlessui/react': *headlessUiReact - '@next/mdx': '13.4.6' + '@next/mdx': '13.4.7' '@resvg/resvg-js': '2.4.1' '@tailwindcss/typography': *tailwindTypography 'Buffer': '0.0.0' @@ -375,8 +380,8 @@ shared: 'front-matter': '4.0.2' 'highlight.js': '11.8.0' 'github-slugger': '2.0.0' - 'jotai': '2.1.1' - 'jotai-location': '0.5.1' + 'jotai': *jotai + 'jotai-location': *jotai-location 'lodash.clonedeep': '4.5.0' 'lodash.orderby': *_orderby 'lodash.unset': *_unset @@ -398,8 +403,8 @@ shared: 'remark-smartypants': '2.0.0' 'sharp': '0.32.1' 'svg-to-pdfkit': 'https://github.com/eriese/SVG-to-PDFKit' - 'tlds': '1.239.0' - 'to-vfile': '7.2.4' + 'tlds': '1.240.0' + 'to-vfile': '8.0.0' 'unist-util-visit': *unist-util-visit 'use-persisted-state': *use-persisted-state 'web-worker': '1.2.0' diff --git a/designs/aaron/src/front.mjs b/designs/aaron/src/front.mjs index 7e7d46d49ab..caf39d0dc75 100644 --- a/designs/aaron/src/front.mjs +++ b/designs/aaron/src/front.mjs @@ -5,6 +5,7 @@ import { dimensions } from './shared.mjs' export const front = { from: base, name: 'aaron.front', + measurements: ['hips'], options: { brianFitCollar: false, brianFitSleeve: false, diff --git a/designs/bee/src/cup.mjs b/designs/bee/src/cup.mjs index c613cfe04b7..673ef90c1d9 100644 --- a/designs/bee/src/cup.mjs +++ b/designs/bee/src/cup.mjs @@ -8,6 +8,7 @@ export const cup = { inherited: true, }, after: neckTie, + measurements: ['bustPointToUnderbust'], options: { topDepth: { pct: 54, min: 50, max: 80, menu: 'fit' }, bottomCupDepth: { pct: 8, min: 0, max: 20, menu: 'fit' }, diff --git a/designs/carlton/src/front.mjs b/designs/carlton/src/front.mjs index 9b1f1ebc075..63dd27f18db 100644 --- a/designs/carlton/src/front.mjs +++ b/designs/carlton/src/front.mjs @@ -483,7 +483,7 @@ export const front = { name: 'carlton.front', from: bentFront, hide: hidePresets.HIDE_TREE, - measurements: ['waist', 'waistToFloor', 'waistToSeat'], + measurements: ['waist', 'waistToFloor', 'waistToSeat', 'seat'], options: { chestEase: { pct: 10, min: 5, max: 20, menu: 'fit' }, buttonSpacingHorizontal: { pct: 43.5, min: 15, max: 60, menu: 'style' }, diff --git a/designs/simone/src/fba-front.mjs b/designs/simone/src/fba-front.mjs index bd056749c54..67a04177e53 100644 --- a/designs/simone/src/fba-front.mjs +++ b/designs/simone/src/fba-front.mjs @@ -415,7 +415,7 @@ function simoneFbaFront({ export const fbaFront = { name: 'simone.fbaFront', from: front, - measurements: ['highBust'], + measurements: ['highBust', 'bustSpan', 'hpsToBust'], hide: { self: true, from: true, diff --git a/markdown/dev/reference/api/snippet/rotate/en.md b/markdown/dev/reference/api/snippet/rotate/en.md new file mode 100644 index 00000000000..3be1bf828ca --- /dev/null +++ b/markdown/dev/reference/api/snippet/rotate/en.md @@ -0,0 +1,38 @@ +--- +title: Snippet.rotate() +--- + +The `Snippet.rotate()` method allows you to scale a snippet. Under the hood, it +sets the `data-rotate` property. + +## Signature + +```js +Snippet snippet.rotate(rotation, overwrite=true) +``` + +This method is chainable as it returns the `Snippet` object + +## Example + + +```js +({ Point, Path, paths, Snippet, snippets, part }) => { + + for (const i of [0,1,2,3,4,5,6]) { + snippets[`demo${i}`] = new Snippet( + "logo", + new Point(60*i, 0) + ).rotate(60 * i) + } + + // Prevent clipping + paths.diag = new Path() + .move(new Point(-30,-50)) + .move(new Point(400,50)) + + return part +} +``` + + diff --git a/markdown/dev/reference/api/snippet/scale/en.md b/markdown/dev/reference/api/snippet/scale/en.md new file mode 100644 index 00000000000..b49ed90f827 --- /dev/null +++ b/markdown/dev/reference/api/snippet/scale/en.md @@ -0,0 +1,38 @@ +--- +title: Snippet.scale() +--- + +The `Snippet.scale()` method allows you to scale a snippet. Under the hood, it +sets the `data-scale` property. + +## Signature + +```js +Snippet snippet.scale(scale, overwrite=true) +``` + +This method is chainable as it returns the `Snippet` object + +## Example + + +```js +({ Point, Path, paths, Snippet, snippets, part }) => { + + for (const i of [1,2,3,4,5,6]) { + snippets[`demo${i}`] = new Snippet( + "logo", + new Point(30*i, 0) + ).scale(i/10) + } + + // Prevent clipping + paths.diag = new Path() + .move(new Point(0,-30)) + .move(new Point(200,20)) + + return part +} +``` + + diff --git a/markdown/org/docs/measurements/waisttohips/en.md b/markdown/org/docs/measurements/waisttohips/en.md index 24ee4fe0f8f..0b7677670b6 100644 --- a/markdown/org/docs/measurements/waisttohips/en.md +++ b/markdown/org/docs/measurements/waisttohips/en.md @@ -2,4 +2,4 @@ title: Waist to hips --- -The **waist to hips** measurement is measured from your waist down to the top of your hip bone (where your trousers sit). Measure it at the side of your body. +The **waist to hips** measurement is measured from your waist down to the top of your hip bone. Measure it at the side of your body. diff --git a/package.json b/package.json index a349d119cd6..eaf79c6ec13 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "handlebars": "^4.7.7", "husky": "^8.0.1", "js-yaml": "^4.0.0", - "lerna": "^6.0.0", + "lerna": "^7.0.2", "lint-staged": "^13.0.3", "mocha": "^10.0.0", "mustache": "^4.0.1", @@ -116,7 +116,7 @@ "version": "0.0.0", "dependencies": { "autoprefixer": "^10.4.0", - "c8": "^7.12.0", + "c8": "^8.0.0", "handlebars": "^4.7.7", "jsonfile": "^6.1.0", "postcss": "^8.4.5", diff --git a/packages/core/package.json b/packages/core/package.json index 692a57818fa..38d702bdeda 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -59,7 +59,7 @@ "lodash.clonedeep": "^4.5.0" }, "devDependencies": { - "eslint": "8.42.0", + "eslint": "8.43.0", "nyc": "15.1.0", "mocha": "10.2.0", "chai": "4.3.7", diff --git a/packages/core/src/part.mjs b/packages/core/src/part.mjs index 8eae9e1cc8d..788d724d29e 100644 --- a/packages/core/src/part.mjs +++ b/packages/core/src/part.mjs @@ -176,7 +176,7 @@ Part.prototype.shorthand = function () { get: function (measurements, name) { if (typeof measurements[name] === 'undefined') self.context.store.log.warning( - `Tried to access \`measurements.${name}\` but it is \`undefined\`` + `${self.name} tried to access \`measurements.${name}\` but it is \`undefined\`` ) return Reflect.get(...arguments) }, diff --git a/packages/core/src/snippet.mjs b/packages/core/src/snippet.mjs index 88244a4d120..85e6a465eae 100644 --- a/packages/core/src/snippet.mjs +++ b/packages/core/src/snippet.mjs @@ -52,6 +52,30 @@ Snippet.prototype.clone = function () { return clone } +/** + * Helper method to scale a snippet + * + * @param {number} scale - The scale to set + * @param {bool} overwrite - Whether to overwrite the existing scale or not (default is true) + * + * @return {Snippet} this - The snippet instance + */ +Snippet.prototype.scale = function (scale, overwrite = true) { + return this.attr('data-scale', scale, overwrite) +} + +/** + * Helper method to rotate a snippet + * + * @param {number} rotation - The rotation to set + * @param {bool} overwrite - Whether to overwrite the existing rotation or not (default is true) + * + * @return {Snippet} this - The snippet instance + */ +Snippet.prototype.rotate = function (rotation, overwrite = true) { + return this.attr('data-rotate', rotation, overwrite) +} + /** * Returns a snippet as an object suitable for inclusion in renderprops * diff --git a/packages/core/tests/snippet.test.mjs b/packages/core/tests/snippet.test.mjs index 9500ad0dc8c..6d80e94c850 100644 --- a/packages/core/tests/snippet.test.mjs +++ b/packages/core/tests/snippet.test.mjs @@ -27,6 +27,16 @@ describe('Snippet', () => { expect(s.attributes.get('class')).to.equal('less') }) + it('Should scale a snippet', () => { + let s = new Snippet('test', new Point(12, -34)).scale(0.1234) + expect(s.attributes.get('data-scale')).to.equal('0.1234') + }) + + it('Should rotate a snippet', () => { + let s = new Snippet('test', new Point(12, -34)).rotate(123) + expect(s.attributes.get('data-rotate')).to.equal('123') + }) + it('Should get a snippet via the snippets proxy', () => { let result const part = { diff --git a/packages/models/tests/models.test.mjs b/packages/models/tests/models.test.mjs index 0f2abfa98ef..38d97f9920d 100644 --- a/packages/models/tests/models.test.mjs +++ b/packages/models/tests/models.test.mjs @@ -1,5 +1,5 @@ import chai from 'chai' -import * as all from './dist/index.mjs' +import * as all from '../src/index.mjs' const expect = chai.expect const { measurements, sizes } = all diff --git a/packages/react-components/src/pattern/defs.mjs b/packages/react-components/src/pattern/defs.mjs index 989d5e8d624..936b1737b11 100644 --- a/packages/react-components/src/pattern/defs.mjs +++ b/packages/react-components/src/pattern/defs.mjs @@ -86,12 +86,14 @@ const PaperlessDefs = ({ units = 'metric', stacks }) => ) -export const Defs = (props) => - props.svg ? ( +export const Defs = (props) => { + console.log(props.svg.defs) + return props.svg ? ( - {props.svg.defs.forSvg ? sanitize(props.svg.defs.forSvg) : null} + {props.svg.defs.list ? sanitize(Object.values(props.svg.defs.list).join('')) : null} {props.settings[0].paperless ? ( ) : null} ) : null +} diff --git a/sites/backend/package.json b/sites/backend/package.json index ffd52eeee13..1907616949d 100644 --- a/sites/backend/package.json +++ b/sites/backend/package.json @@ -28,12 +28,12 @@ }, "peerDependencies": {}, "dependencies": { - "@aws-sdk/client-sesv2": "3.352.0", - "@prisma/client": "4.15.0", + "@aws-sdk/client-sesv2": "3.354.0", + "@prisma/client": "4.16.1", "bcryptjs": "2.4.3", "cors": "2.8.5", "crypto": "1.0.1", - "dotenv": "16.1.4", + "dotenv": "16.3.1", "express": "4.18.2", "js-yaml": "4.1.0", "lodash.get": "4.4.2", @@ -44,17 +44,17 @@ "passport-jwt": "4.0.1", "pino": "8.14.1", "qrcode": "1.5.3", - "swagger-ui-dist": "4.19.0", + "swagger-ui-dist": "5.1.0", "swagger-ui-express": "4.6.3" }, "devDependencies": { "chai": "4.3.7", "chai-http": "4.4.0", - "esbuild": "0.18.2", + "esbuild": "0.18.8", "mocha": "10.2.0", "mocha-steps": "1.3.0", "nodemon": "2.0.22", - "prisma": "4.15.0" + "prisma": "4.16.1" }, "engines": { "node": ">=16.0.0", diff --git a/sites/dev/components/header/index.mjs b/sites/dev/components/header/index.mjs index 09aebd209fc..dd050c191c1 100644 --- a/sites/dev/components/header/index.mjs +++ b/sites/dev/components/header/index.mjs @@ -1,9 +1,8 @@ // Hooks -import { useState, useEffect, useContext } from 'react' +import { useContext } from 'react' import { useTranslation } from 'next-i18next' // Context import { ModalContext } from 'shared/context/modal-context.mjs' -import { LoadingContext } from 'shared/context/loading-context.mjs' // Components import { I18nIcon, @@ -17,7 +16,7 @@ import { FreeSewingIcon, HeartIcon, } from 'shared/components/icons.mjs' -import { Ribbon } from 'shared/components/ribbon.mjs' +import { HeaderWrapper } from 'shared/components/wrappers/header.mjs' import { ModalThemePicker, ns as themeNs } from 'shared/components/modal/theme-picker.mjs' import { ModalMenu } from 'site/components/navigation/modal-menu.mjs' @@ -100,38 +99,11 @@ const NavIcons = ({ setModal }) => { ) } -export const Header = () => { +export const Header = (props) => { const { setModal } = useContext(ModalContext) || {} - const { loading } = useContext(LoadingContext) - 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 ( -
+
{/* Non-mobile content */} @@ -145,7 +117,6 @@ export const Header = () => {
- -
+ ) } diff --git a/sites/dev/components/layouts/docs.mjs b/sites/dev/components/layouts/docs.mjs index 0a80bec6bd0..290579ea4d7 100644 --- a/sites/dev/components/layouts/docs.mjs +++ b/sites/dev/components/layouts/docs.mjs @@ -10,7 +10,7 @@ export const DocsLayout = ({ children = [], pageTitle = false }) => { const { title, crumbs } = useContext(NavigationContext) return ( -
+
{title && ( diff --git a/sites/dev/components/search.mjs b/sites/dev/components/search.mjs index a08401de2fb..07bef9c796c 100644 --- a/sites/dev/components/search.mjs +++ b/sites/dev/components/search.mjs @@ -4,6 +4,8 @@ import { siteConfig } from 'site/site.config.mjs' import Link from 'next/link' import { ClearIcon } from 'shared/components/icons.mjs' +export const ns = ['search'] + const searchClient = algoliasearch(siteConfig.algolia.app, siteConfig.algolia.key) const Hit = (props) => ( diff --git a/sites/dev/components/wrappers/layout.mjs b/sites/dev/components/wrappers/layout.mjs deleted file mode 100644 index 74192275a65..00000000000 --- a/sites/dev/components/wrappers/layout.mjs +++ /dev/null @@ -1,26 +0,0 @@ -import Head from 'next/head' -import { Header, ns as headerNs } from 'site/components/header/index.mjs' -import { Footer, ns as footerNs } from 'shared/components/footer/index.mjs' - -export const ns = [...new Set([...headerNs, ...footerNs])] - -export const LayoutWrapper = ({ children = [], header = false }) => { - const ChosenHeader = header ? header : Header - - return ( -
- - - - -
{children}
-
-
- ) -} diff --git a/sites/dev/package.json b/sites/dev/package.json index ef478a5363d..d5fd431e135 100644 --- a/sites/dev/package.json +++ b/sites/dev/package.json @@ -33,20 +33,20 @@ "@mdx-js/mdx": "2.3.0", "@mdx-js/react": "2.3.0", "@mdx-js/runtime": "2.0.0-next.9", - "@next/bundle-analyzer": "13.4.6", + "@next/bundle-analyzer": "13.4.7", "@tailwindcss/typography": "0.5.9", - "algoliasearch": "4.17.2", - "daisyui": "3.1.0", + "algoliasearch": "4.18.0", + "daisyui": "3.1.1", "lodash.get": "4.4.2", "lodash.orderby": "4.6.0", "lodash.set": "4.3.2", - "next": "13.4.6", + "next": "13.4.7", "react": "18.2.0", "react-copy-to-clipboard": "5.1.0", "react-dom": "18.2.0", "react-hotkeys-hook": "4.4.0", "react-instantsearch-dom": "6.40.0", - "react-instantsearch-hooks-web": "6.44.2", + "react-instantsearch-hooks-web": "6.45.0", "react-markdown": "8.0.7", "react-swipeable": "7.0.1", "react-timeago": "7.1.0", @@ -61,7 +61,7 @@ "devDependencies": { "@playwright/test": "^1.32.3", "autoprefixer": "10.4.14", - "eslint-config-next": "13.4.6", + "eslint-config-next": "13.4.7", "js-yaml": "4.1.0", "postcss": "8.4.24", "playwright": "^1.32.3", diff --git a/sites/lab/components/header/index.mjs b/sites/lab/components/header/index.mjs index fc9975cc04d..a7a3c6a86ce 100644 --- a/sites/lab/components/header/index.mjs +++ b/sites/lab/components/header/index.mjs @@ -1,9 +1,8 @@ // Hooks -import { useState, useEffect, useContext } from 'react' +import { useContext } from 'react' import { useTranslation } from 'next-i18next' // Context import { ModalContext } from 'shared/context/modal-context.mjs' -import { LoadingContext } from 'shared/context/loading-context.mjs' // Components import { DesignIcon, @@ -12,12 +11,12 @@ import { UserIcon, ThemeIcon, I18nIcon, - MeasureIcon, + MeasieIcon, PageIcon, GitHubIcon, PlusIcon, } from 'shared/components/icons.mjs' -import { Ribbon } from 'shared/components/ribbon.mjs' +import { HeaderWrapper } from 'shared/components/wrappers/header.mjs' import { ModalThemePicker, ns as themeNs } from 'shared/components/modal/theme-picker.mjs' import { ModalLocalePicker, ns as localeNs } from 'shared/components/modal/locale-picker.mjs' import { ModalMenu } from 'site/components/navigation/modal-menu.mjs' @@ -53,7 +52,7 @@ const NavIcons = ({ setModal }) => { color={colors[3]} extraClasses="hidden lg:flex" > - + { ) } -export const Header = ({ setSearch }) => { +export const Header = ({ setSearch, show }) => { const { setModal } = useContext(ModalContext) - const { loading } = useContext(LoadingContext) - 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 ( -
+
{/* Non-mobile content */} -
+
@@ -138,7 +110,6 @@ export const Header = ({ setSearch }) => {
- -
+ ) } diff --git a/sites/lab/components/layouts/docs.mjs b/sites/lab/components/layouts/docs.mjs index aca2b765c40..00d85faf839 100644 --- a/sites/lab/components/layouts/docs.mjs +++ b/sites/lab/components/layouts/docs.mjs @@ -10,9 +10,9 @@ export const DocsLayout = ({ children = [], pageTitle = false }) => { const { title, crumbs } = useContext(NavigationContext) return ( -
+
-
+
{title && (
diff --git a/sites/lab/components/layouts/lab.mjs b/sites/lab/components/layouts/lab.mjs index 2a59eb01044..0acaf114474 100644 --- a/sites/lab/components/layouts/lab.mjs +++ b/sites/lab/components/layouts/lab.mjs @@ -14,7 +14,7 @@ export const BeforeNav = ({ app }) => ( ) export const LabLayout = ({ app, AltMenu, children = [] }) => ( -
+
{children}
- - + ) } diff --git a/sites/org/components/layouts/docs.mjs b/sites/org/components/layouts/docs.mjs index 2f8f5754db6..c967924f5f4 100644 --- a/sites/org/components/layouts/docs.mjs +++ b/sites/org/components/layouts/docs.mjs @@ -10,7 +10,7 @@ export const DocsLayout = ({ children = [], pageTitle = false }) => { const { crumbs } = useContext(NavigationContext) return ( -
+
{pageTitle && ( diff --git a/sites/org/components/layouts/workbench.mjs b/sites/org/components/layouts/workbench.mjs index ef1ac12b2f4..48aa2916d00 100644 --- a/sites/org/components/layouts/workbench.mjs +++ b/sites/org/components/layouts/workbench.mjs @@ -1,7 +1,7 @@ export const ns = [] export const WorkbenchLayout = (props) => ( -
+
{props.children}
) diff --git a/sites/org/components/sanity/author.mjs b/sites/org/components/sanity/author.mjs new file mode 100644 index 00000000000..ea79a9e9fea --- /dev/null +++ b/sites/org/components/sanity/author.mjs @@ -0,0 +1,43 @@ +import { SanityMdxWrapper } from './mdx-wrapper.mjs' +import { useTranslation } from 'next-i18next' + +export const Author = ({ author = {} }) => { + const { t } = useTranslation(['posts']) + return ( +
+
+ +
+ +
+ {author.displayname} +
+
+

+

+ +
+
+
+ ) +} diff --git a/sites/org/components/sanity/mdx-wrapper.mjs b/sites/org/components/sanity/mdx-wrapper.mjs new file mode 100644 index 00000000000..8fe4fe79160 --- /dev/null +++ b/sites/org/components/sanity/mdx-wrapper.mjs @@ -0,0 +1,30 @@ +import { compile, run } from '@mdx-js/mdx' +import * as runtime from 'react/jsx-runtime' // Production. +import { useState, useEffect } from 'react' + +import { PlainMdxWrapper } from 'shared/components/wrappers/mdx.mjs' + +export const useEvaledMdx = (mdxStr = '') => { + const [mdxModule, setMdxModule] = useState(false) + + useEffect(() => { + const runEffect = async () => { + const code = await compile(mdxStr, { + outputFormat: 'function-body', + development: false, + }) + const evaled = await run(code, runtime) + setMdxModule(() => evaled.default) + } + runEffect() + }, [mdxStr]) + + return mdxModule +} + +export const MdxEvalWrapper = ({ MDX = false, components = {}, site = 'org' }) => { + const evaled = useEvaledMdx(MDX) + return +} + +export const SanityMdxWrapper = MdxEvalWrapper diff --git a/sites/org/components/sanity/page-wrapper.mjs b/sites/org/components/sanity/page-wrapper.mjs new file mode 100644 index 00000000000..4a4951734c9 --- /dev/null +++ b/sites/org/components/sanity/page-wrapper.mjs @@ -0,0 +1,80 @@ +import Head from 'next/head' +import { PageLink } from 'shared/components/page-link.mjs' +import { Lightbox } from 'shared/components/lightbox.mjs' +import { ImageWrapper } from 'shared/components/wrappers/img.mjs' +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' +import { Author } from './author.mjs' +import { TimeAgo } from 'shared/components/wrappers/mdx.mjs' +import { SanityMdxWrapper } from './mdx-wrapper.mjs' +import { useTranslation } from 'next-i18next' + +export const ns = ['common', 'posts', ...pageNs] + +export const SanityPageWrapper = ({ + post = {}, + author = {}, + page = {}, + namespaces = ['common'], +}) => { + const { t } = useTranslation(namespaces) + return ( + + + + + + + + + + + + + + + + ) +} diff --git a/sites/org/components/sanity/utils.mjs b/sites/org/components/sanity/utils.mjs new file mode 100644 index 00000000000..88f1e0cbda9 --- /dev/null +++ b/sites/org/components/sanity/utils.mjs @@ -0,0 +1,44 @@ +import { createClient } from 'next-sanity' +import { siteConfig } from 'site/site.config.mjs' + +let sanityClient +const cache = {} +export const sanityLoader = async ({ query, language, type, slug, order, filters = '' }) => { + sanityClient = + sanityClient || + createClient({ + projectId: 'hl5bw8cj', + dataset: 'site-content', + apiVersion: '2023-06-17', + // token: process.env.SANITY_TOKEN, + useCdn: false, + }) + + if (!query) { + query = `*[_type == "${type}${language}"` + if (slug) query += ` && slug.current == "${slug}"` + query += ']' + } + + if (order) { + query += ` | order(${order})` + } + + query += filters + + if (cache[query]) return cache[query] + + const result = await sanityClient.fetch(query) + cache[query] = result + return result +} + +export const sanityImage = (image, dataset = 'site-content') => { + const [, assetName, origSize, format] = image.asset._ref.split('-') + return `https://cdn.sanity.io/images/${siteConfig.sanity.project}/${dataset}/${assetName}-${origSize}.${format}` +} + +export const sanitySiteImage = (image) => sanityImage(image, 'site-content') +export const sanityUserImage = (image) => sanityImage(image, 'user-content') + +export const numPerPage = 12 diff --git a/sites/org/next.config.mjs b/sites/org/next.config.mjs index f1dd4ec0438..b4651532780 100644 --- a/sites/org/next.config.mjs +++ b/sites/org/next.config.mjs @@ -6,6 +6,18 @@ import { jargon } from './jargon.mjs' let config = configBuilder({ site: 'org', jargon }) config.i18n = i18nConfig.i18n +config.rewrites = async () => { + return [ + { + source: '/blog', + destination: '/blog/page/1', + }, + { + source: '/showcase', + destination: '/showcase/page/1', + }, + ] +} // Say hi console.log(banner + '\n') diff --git a/sites/org/package.json b/sites/org/package.json index a2e01091375..0daf39c65a9 100644 --- a/sites/org/package.json +++ b/sites/org/package.json @@ -34,15 +34,20 @@ "@mdx-js/mdx": "2.3.0", "@mdx-js/react": "2.3.0", "@mdx-js/runtime": "2.0.0-next.9", + "@portabletext/react": "^1.0.6", + "@sanity/client": "^6.1.2", "@tailwindcss/typography": "0.5.9", - "algoliasearch": "4.17.2", + "algoliasearch": "4.18.0", "react-copy-to-clipboard": "5.1.0", - "daisyui": "3.1.0", + "daisyui": "3.1.1", + "jotai": "2.1.1", + "jotai-location": "0.5.1", "lodash.get": "4.4.2", "lodash.orderby": "4.6.0", "lodash.set": "4.3.2", "luxon": "3.3.0", - "next": "13.4.6", + "next": "13.4.7", + "next-sanity": "^4.3.3", "react-dropzone": "14.2.3", "react-hotkeys-hook": "4.4.0", "react-instantsearch-dom": "6.40.0", @@ -64,7 +69,7 @@ "devDependencies": { "@playwright/test": "^1.32.3", "autoprefixer": "10.4.14", - "eslint-config-next": "13.4.6", + "eslint-config-next": "13.4.7", "js-yaml": "4.1.0", "postcss": "8.4.24", "playwright": "^1.32.3", diff --git a/sites/org/pages/blog/[slug].mjs b/sites/org/pages/blog/[slug].mjs new file mode 100644 index 00000000000..3e5eac1db2e --- /dev/null +++ b/sites/org/pages/blog/[slug].mjs @@ -0,0 +1,67 @@ +import { SanityPageWrapper, ns as sanityNs } from 'site/components/sanity/page-wrapper.mjs' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { sanityLoader, sanityImage } from 'site/components/sanity/utils.mjs' + +const namespaces = [...sanityNs] + +const BlogPostPage = (props) => { + return +} + +/* + * getStaticProps() is used to fetch data at build-time. + * + * On this page, it is loading the blog content from strapi. + * + * This, in combination with getStaticPaths() below means this + * page will be used to render/generate all blog content. + * + * To learn more, see: https://nextjs.org/docs/basic-features/data-fetching + */ +export async function getStaticProps({ params, locale }) { + const { slug } = params + const post = await sanityLoader({ type: 'blog', language: locale, slug }) + .then((data) => data[0]) + .catch((err) => console.log(err)) + + return { + props: { + post: { + slug, + body: post.body, + title: post.title, + date: post.date, + caption: post.caption, + image: sanityImage(post.image), + }, + // FIXME load the author separately + author: { + displayname: post.author, + // slug: post.author.slug, + // about: post.author.about, + // image: strapiImage(post.author.picture, ['small']), + // about: post.author.about, + }, + ...(await serverSideTranslations(locale, namespaces)), + }, + } +} + +export const getStaticPaths = async () => { + const paths = await sanityLoader({ language: 'en', type: 'blog' }) + .then((data) => data.map((post) => `/blog/${post.slug.current}`)) + .catch((err) => console.log(err)) + + return { + paths: [ + ...paths, + ...paths.map((p) => `/de${p}`), + ...paths.map((p) => `/es${p}`), + ...paths.map((p) => `/fr${p}`), + ...paths.map((p) => `/nl${p}`), + ], + fallback: false, + } +} + +export default BlogPostPage diff --git a/sites/org/pages/blog/index.mjs b/sites/org/pages/blog/index.mjs deleted file mode 100644 index 28a23405f71..00000000000 --- a/sites/org/pages/blog/index.mjs +++ /dev/null @@ -1,36 +0,0 @@ -// Dependencies -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -// Components -import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' -import { V3Wip } from 'shared/components/v3-wip.mjs' - -// Translation namespaces used on this page -const namespaces = [...new Set(['designs', ...pageNs])] - -/* - * Each page MUST be wrapped in the PageWrapper component. - * You also MUST spread props.page into this wrapper component - * when path and locale come from static props (as here) - * or set them manually. - */ -const BlogIndexPage = ({ page }) => ( - -
- -
-
-) - -export default BlogIndexPage - -export async function getStaticProps({ locale }) { - return { - props: { - ...(await serverSideTranslations(locale, namespaces)), - page: { - locale, - path: ['blog'], - }, - }, - } -} diff --git a/sites/org/pages/blog/page/[page].mjs b/sites/org/pages/blog/page/[page].mjs new file mode 100644 index 00000000000..d490de7e9f0 --- /dev/null +++ b/sites/org/pages/blog/page/[page].mjs @@ -0,0 +1,131 @@ +// Dependencies +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { sanitySiteImage, numPerPage, sanityLoader } from 'site/components/sanity/utils.mjs' +// Hooks +import { useTranslation } from 'next-i18next' +// Components +import Link from 'next/link' +import { TimeAgo } from 'shared/components/wrappers/mdx.mjs' +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' +import { Pagination } from 'shared/components/navigation/pagination.mjs' +import { siteConfig } from 'site/site.config.mjs' + +// Translation namespaces used on this page +const namespaces = [...new Set(['designs', ...pageNs])] + +const textShadow = { + style: { + textShadow: + '1px 1px 1px #000000, -1px -1px 1px #000000, 1px -1px 1px #000000, -1px 1px 1px #000000, 2px 2px 1px #000000', + }, +} + +const Preview = ({ post, t }) => ( +
+ +
+
+
+
+ {post.title} +
+ +
+
+
+ +
+) +/* + * Each page MUST be wrapped in the PageWrapper component. + * You also MUST spread props.page into this wrapper component + * when path and locale come from static props (as here) + * or set them manually. + */ +const BlogIndexPage = ({ posts, page, current, total }) => { + const { t } = useTranslation() + + return ( + +
+ {posts.map((post) => ( + + ))} +
+ +
+ ) +} + +export default BlogIndexPage + +export async function getStaticProps({ locale, params }) { + const allPosts = await sanityLoader({ + language: locale, + type: 'blog', + order: 'date desc', + filters: '{_id, date, slug, title, author, image}', + }) + const pageNum = parseInt(params.page) + + return { + props: { + posts: allPosts.slice(numPerPage * (pageNum - 1), numPerPage * pageNum), + current: pageNum, + total: allPosts.length, + ...(await serverSideTranslations(locale, namespaces)), + page: { + locale, + // title: 'Freesewing Blog', + path: ['blog', 'page', params.page], + }, + }, + } +} + +export const getStaticPaths = async () => { + const numPosts = await sanityLoader({ query: `count(*[_type == "blogen"])` }) + const numPages = Math.ceil(numPosts / numPerPage) + const paths = [] + for (let i = 0; i < numPages; i++) { + const pathName = `/blog/page/${i + 1}` + siteConfig.languages.forEach((l) => paths.push(`${l.length ? '/' : ''}${l}${pathName}`)) + } + + return { + paths, + fallback: false, + } +} diff --git a/sites/org/pages/new/[design].mjs b/sites/org/pages/new/[design].mjs new file mode 100644 index 00000000000..d1b147abf3d --- /dev/null +++ b/sites/org/pages/new/[design].mjs @@ -0,0 +1,50 @@ +// Hooks +import { useDesign, collection } from 'shared/hooks/use-design.mjs' +// Dependencies +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { nsMerge } from 'shared/utils.mjs' +// Components +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' +import { Workbench, ns as wbNs } from 'shared/components/workbench/new.mjs' +import { WorkbenchLayout } from 'site/components/layouts/workbench.mjs' +import { DynamicOrgDocs as DynamicDocs } from 'site/components/dynamic-org-docs.mjs' + +// Translation namespaces used on this page +const namespaces = nsMerge(wbNs, pageNs) + +const NewDesignPage = ({ page, design }) => { + const Design = useDesign(design) + + return ( + + + + ) +} + +export default NewDesignPage + +export async function getStaticProps({ locale, params }) { + return { + props: { + ...(await serverSideTranslations(locale, [`o_${params.design}`, ...namespaces])), + design: params.design, + page: { + locale, + path: ['new', params.design], + title: '', + }, + }, + } +} + +/* + * getStaticPaths() is used to specify for which routes (think URLs) + * this page should be used to generate the result. + */ +export async function getStaticPaths() { + return { + paths: [...collection.map((design) => `/new/${design}`)], + fallback: 'blocking', + } +} diff --git a/sites/org/pages/new/apikey/index.mjs b/sites/org/pages/new/apikey.mjs similarity index 100% rename from sites/org/pages/new/apikey/index.mjs rename to sites/org/pages/new/apikey.mjs diff --git a/sites/org/pages/showcase/index.mjs b/sites/org/pages/new/pattern.mjs similarity index 61% rename from sites/org/pages/showcase/index.mjs rename to sites/org/pages/new/pattern.mjs index 8b39fb430d3..6a9751f3383 100644 --- a/sites/org/pages/showcase/index.mjs +++ b/sites/org/pages/new/pattern.mjs @@ -2,10 +2,12 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations' // Components import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' -import { V3Wip } from 'shared/components/v3-wip.mjs' +import { ns as authNs } from 'shared/components/wrappers/auth/index.mjs' +import { ns as setsNs } from 'shared/components/account/sets.mjs' +import { DesignPicker, ns as designNs } from 'shared/components/designs/design-picker.mjs' // Translation namespaces used on this page -const namespaces = [...new Set(['showcase', ...pageNs])] +const namespaces = [...new Set([...designNs, ...setsNs, ...authNs, ...pageNs])] /* * Each page MUST be wrapped in the PageWrapper component. @@ -13,15 +15,13 @@ const namespaces = [...new Set(['showcase', ...pageNs])] * when path and locale come from static props (as here) * or set them manually. */ -const DesignsPage = ({ page }) => ( +const NewSetPage = ({ page }) => ( -
- -
+
) -export default DesignsPage +export default NewSetPage export async function getStaticProps({ locale }) { return { @@ -29,7 +29,7 @@ export async function getStaticProps({ locale }) { ...(await serverSideTranslations(locale, namespaces)), page: { locale, - path: ['showcase'], + path: ['new', 'pattern'], }, }, } diff --git a/sites/org/pages/new/set/index.mjs b/sites/org/pages/new/set.mjs similarity index 100% rename from sites/org/pages/new/set/index.mjs rename to sites/org/pages/new/set.mjs diff --git a/sites/org/pages/showcase/[slug].mjs b/sites/org/pages/showcase/[slug].mjs new file mode 100644 index 00000000000..12f3127d3ef --- /dev/null +++ b/sites/org/pages/showcase/[slug].mjs @@ -0,0 +1,71 @@ +import { SanityPageWrapper, ns as sanityNs } from 'site/components/sanity/page-wrapper.mjs' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { sanityLoader, sanityImage } from 'site/components/sanity/utils.mjs' + +const namespaces = [...sanityNs] + +const ShowcasePage = (props) => { + return +} + +/* + * getStaticProps() is used to fetch data at build-time. + * + * On this page, it is loading the showcase content from strapi. + * + * This, in combination with getStaticPaths() below means this + * page will be used to render/generate all showcase content. + * + * To learn more, see: https://nextjs.org/docs/basic-features/data-fetching + */ +export async function getStaticProps({ params, locale }) { + const { slug } = params + const post = await sanityLoader({ type: 'showcase', language: locale, slug }) + .then((data) => data[0]) + .catch((err) => console.log(err)) + + const designs = [post.design1 || null] + if (post.design2 && post.design2.length > 2) designs.push(post.design2) + if (post.design3 && post.design3.length > 2) designs.push(post.design3) + + return { + props: { + post: { + slug, + body: post.body, + title: post.title, + date: post.date, + caption: post.caption, + image: sanityImage(post.image[0]), + designs, + }, + // FIXME load the author separately + author: { + displayname: post.maker, + // slug: post.maker.slug, + // image: strapiImage(post.maker.picture, ['small']), + // ...(await mdxCompiler(post.maker.about)), + }, + ...(await serverSideTranslations(locale, namespaces)), + }, + } +} + +export const getStaticPaths = async () => { + const paths = await sanityLoader({ language: 'en', type: 'showcase' }) + .then((data) => data.map((post) => `/showcase/${post.slug.current}`)) + .catch((err) => console.log(err)) + + return { + paths: [ + ...paths, + ...paths.map((p) => `/de${p}`), + ...paths.map((p) => `/es${p}`), + ...paths.map((p) => `/fr${p}`), + ...paths.map((p) => `/nl${p}`), + ], + fallback: false, + } +} + +export default ShowcasePage diff --git a/sites/org/pages/showcase/page/[page].mjs b/sites/org/pages/showcase/page/[page].mjs new file mode 100644 index 00000000000..ae3a0b2d73b --- /dev/null +++ b/sites/org/pages/showcase/page/[page].mjs @@ -0,0 +1,122 @@ +// Dependencies +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { sanitySiteImage, numPerPage, sanityLoader } from 'site/components/sanity/utils.mjs' +// Hooks +import { useTranslation } from 'next-i18next' +// Components +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' +import Link from 'next/link' +import { Pagination } from 'shared/components/navigation/pagination.mjs' +import { siteConfig } from 'site/site.config.mjs' + +// Translation namespaces used on this page +const namespaces = [...new Set(['common', 'designs', ...pageNs])] + +export const PreviewTile = ({ img, slug, title }) => ( + + +

{title}

+ +) + +// const DesignPosts = ({ design, posts }) => { +// const { t } = useTranslation(['patterns']) +// return ( +//
+//

+// +// {t(`${design}.t`)} +// +//

+ +//
+// ) +// } + +const Posts = ({ posts }) => { + const previews = [] + posts.forEach((post) => { + // for (const design of post.designs) { + // if (typeof designs[design] === 'undefined') designs[design] = [] + // designs[design].push(post) + // } + + previews.push( + + ) + }) + + return ( +
+ {previews} +
+ ) +} + +const ShowcaseIndexPage = ({ posts, page, current, total }) => { + const { t } = useTranslation() + + // const designKeys = useMemo(() => Object.keys(designs).sort(), [designs]) + return ( + +
+ + +
+
+ ) +} + +export default ShowcaseIndexPage + +export async function getStaticProps({ locale, params }) { + const allPosts = await sanityLoader({ + language: locale, + type: 'showcase', + order: 'date desc', + filters: '{_id, date, slug, title, maker, image}', + }) + const pageNum = parseInt(params.page) + + return { + props: { + // designs, + posts: allPosts.slice(numPerPage * (pageNum - 1), numPerPage * pageNum), + current: pageNum, + total: allPosts.length, + ...(await serverSideTranslations(locale, namespaces)), + page: { + locale, + // title: 'Freesewing Blog', + path: ['showcase', 'page', params.page], + }, + }, + } +} + +export const getStaticPaths = async () => { + const numPosts = await sanityLoader({ query: `count(*[_type == "showcaseen"])` }) + const numPages = Math.ceil(numPosts / numPerPage) + const paths = [] + for (let i = 0; i < numPages; i++) { + const pathName = `/showcase/page/${i + 1}` + siteConfig.languages.forEach((l) => paths.push(`${l.length ? '/' : ''}${l}${pathName}`)) + } + + return { + paths, + fallback: false, + } +} diff --git a/sites/org/site.config.mjs b/sites/org/site.config.mjs index a996465e6ea..2f76fd2a2ff 100644 --- a/sites/org/site.config.mjs +++ b/sites/org/site.config.mjs @@ -7,6 +7,9 @@ export const siteConfig = { bugsnag: { key: '1b3a900d6ebbfd071975e39b534e1ff5', }, + sanity: { + project: process.env.SANITY_PROJECT || 'hl5bw8cj', + }, languages: ['en', 'es', 'de', 'fr', 'nl'], site: 'FreeSewing.org', } diff --git a/sites/sanity/package.json b/sites/sanity/package.json index 6d9bf8ce494..e901fd16b2b 100644 --- a/sites/sanity/package.json +++ b/sites/sanity/package.json @@ -20,21 +20,21 @@ }, "peerDependencies": {}, "dependencies": { - "@sanity/vision": "3.12.0", - "easymde": "2.16.0", + "@sanity/vision": "3.12.2", + "easymde": "2.18.0", "react": "18.2.0", "react-dom": "18.2.0", "react-is": "18.2.0", - "sanity": "3.12.0", + "sanity": "3.12.2", "styled-components": "5.3.11", "sanity-plugin-markdown": "4.1.0" }, "devDependencies": { "@sanity/eslint-config-studio": "2.0.1", - "eslint": "8.42.0", + "eslint": "8.43.0", "prettier": "2.8.8", "typescript": "5.1.3", - "@sanity/cli": "3.12.1" + "@sanity/cli": "3.12.2" }, "engines": { "node": ">=16.0.0", diff --git a/sites/sanity/scripts/export-strapi.mjs b/sites/sanity/scripts/export-strapi.mjs index 640b3cf8ecf..f535eb18967 100644 --- a/sites/sanity/scripts/export-strapi.mjs +++ b/sites/sanity/scripts/export-strapi.mjs @@ -65,7 +65,7 @@ const transformBlogPost = async (p, lang) => { const asIs = ['title', 'linktitle', 'caption', 'body'] const post = { - _id: `${lang}.blog.${p.slug}`, + _id: `${lang}--blog--${p.slug}`, _type: `blog${lang}`, } for (const field of asIs) post[field] = p[field] @@ -106,7 +106,7 @@ const transformShowcasePost = async (p, lang) => { const asIs = ['title', 'caption', 'body'] const post = { - _id: `${lang}.showcase.${p.slug}`, + _id: `${lang}--showcase--${p.slug}`, _type: `showcase${lang}`, } for (const field of asIs) post[field] = p[field] @@ -149,7 +149,7 @@ const transformNewsletterPost = async (p) => { const asIs = ['title', 'body'] const post = { - _id: `newsletter.${p.slug}`, + _id: `newsletter--${p.slug}`, _type: 'newsletter', } for (const field of asIs) post[field] = p[field] diff --git a/sites/shared/components/account/sets.mjs b/sites/shared/components/account/sets.mjs index fe62cc568da..4e6c0615427 100644 --- a/sites/shared/components/account/sets.mjs +++ b/sites/shared/components/account/sets.mjs @@ -2,8 +2,7 @@ import { useState, useEffect, useContext, useCallback } from 'react' import { useTranslation } from 'next-i18next' import orderBy from 'lodash.orderby' -import { measurements, isDegreeMeasurement } from 'config/measurements.mjs' -import { measurementAsMm, formatMm } from 'shared/utils.mjs' +import { measurements } from 'config/measurements.mjs' import { measurements as designMeasurements } from 'shared/prebuild/data/design-measurements.mjs' import { freeSewingConfig as conf } from 'shared/config/freesewing.config.mjs' // Hooks @@ -23,7 +22,6 @@ import { ModalDesignPicker } from 'shared/components/modal/design-picker.mjs' import { FilterIcon, ClearIcon, - PlusIcon, OkIcon, NoIcon, TrashIcon, @@ -34,6 +32,7 @@ import Markdown from 'react-markdown' import { Tab } from './bio.mjs' import Timeago from 'react-timeago' import { Spinner } from 'shared/components/spinner.mjs' +import { MeasieRow } from 'shared/components/sets/measie-input.mjs' export const ns = ['account', 'patterns', 'toast'] @@ -140,214 +139,6 @@ export const EditRow = (props) => ( ) -const Mval = ({ m, val = false, imperial = false, className = '' }) => - val ? ( - isDegreeMeasurement(m) ? ( - {val}° - ) : ( - - ) - ) : null - -export const MeasieRow = (props) => { - const { t, m, mset } = props - - const isSet = typeof mset.measies?.[m] === 'undefined' ? false : true - - return ( - -
{t(m)}
- {isSet ? ( - - ) : ( -
- )} - - } - toggle={isSet ? : } - toggleClasses={`btn ${isSet ? 'btn-secondary' : 'btn-neutral bg-opacity-50'}`} - > - - - ) -} - -const MeasieInput = ({ t, m, mset, startLoading, stopLoading, backend, refresh, toast }) => { - const isDegree = isDegreeMeasurement(m) - const factor = isDegree ? 1 : mset.imperial ? 25.4 : 10 - - const isValValid = (val) => - typeof val === 'undefined' || val === '' ? null : val != false && !isNaN(val) - const isValid = (newVal) => (typeof newVal === 'undefined' ? isValValid(val) : isValValid(newVal)) - - const [val, setVal] = useState(mset.measies?.[m] / factor || '') - const [valid, setValid] = useState(isValid(mset.measies?.[m] / factor || '')) - - // Update onChange - const update = (evt) => { - setVal(evt.target.value) - - let useVal = isDegree - ? evt.target.value - : measurementAsMm(evt.target.value, mset.imperial ? 'imperial' : 'metric') - setValid(isValid(useVal)) - } - - const save = async () => { - // FIXME - startLoading() - const measies = {} - measies[m] = val * factor - const result = await backend.updateSet(mset.id, { measies }) - if (result.success) { - refresh() - toast.for.settingsSaved() - } else toast.for.backendError() - stopLoading() - } - - const fraction = (i, base) => update({ target: { value: Math.floor(val) + i / base } }) - - if (!m) return null - - const fractionClasses = - 'h-3 border-2 border-solid border-base-100 hover:border-secondary bg-secondary rounded bg-opacity-50 hover:bg-opacity-100' - - return ( -
-
- - {mset.imperial ? ( -
-
- - 1/2 - -
-
- - 1/4 - - {[1, 2, 3].map((i) => ( -
-
- - 1/8 - - {[1, 2, 3, 4, 5, 6, 7].map((i) => ( -
-
- - 1/16 - - {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].map((i) => ( -
-
- - 1/32 - - {[ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - ].map((i) => ( -
-
- ) : null} -
- -
- ) -} - const EditImg = ({ t, mset, account, backend, startLoading, stopLoading, toast, refresh }) => { const [img, setImg] = useState(mset.img) diff --git a/sites/shared/components/collapse.mjs b/sites/shared/components/collapse.mjs index 22905f3cd43..bffcabc9857 100644 --- a/sites/shared/components/collapse.mjs +++ b/sites/shared/components/collapse.mjs @@ -1,5 +1,5 @@ import { useState } from 'react' -import { CloseIcon } from 'shared/components/icons.mjs' +import { DownIcon } from 'shared/components/icons.mjs' import Link from 'next/link' const OpenTitleButton = ({ @@ -17,12 +17,11 @@ const OpenTitleButton = ({ bg-${color} text-${color}-content px-4 py-1 text-lg font-medium`} onClick={toggle} > - {title} + {} + {!bottom && title}
{openButtons} - +
) @@ -68,15 +67,15 @@ export const Collapse = ({ grow flex flex-row gap-4 py-1 px-4 items-center justify-start hover:cursor-pointer hover:bg-${color} hover:bg-opacity-20`} onClick={onClick ? onClick : () => setOpen(true)} > - {title} + {title} + {toggle ? ( + + ) : ( + buttons + )}
- {toggle ? ( - - ) : ( - buttons - )}
) } diff --git a/sites/shared/components/designs/design.mjs b/sites/shared/components/designs/design.mjs index dba05af20f5..de05ba7eee5 100644 --- a/sites/shared/components/designs/design.mjs +++ b/sites/shared/components/designs/design.mjs @@ -6,7 +6,7 @@ import { DesignTag } from 'shared/components/designs/tag.mjs' export const ns = ['design', 'designs', 'tags'] -const defaultLink = (design) => `/new/pattern/${design}` +const defaultLink = (design) => `/new/${design}` export const Design = ({ name, hrefBuilder = false }) => { const { t } = useTranslation(ns) diff --git a/sites/shared/components/icons.mjs b/sites/shared/components/icons.mjs index e78fc968ebb..c612f49cb78 100644 --- a/sites/shared/components/icons.mjs +++ b/sites/shared/components/icons.mjs @@ -184,7 +184,16 @@ export const DocsIcon = (props) => ( ) - +export const DoubleLeftIcon = (props) => ( + + + +) +export const DoubleRightIcon = (props) => ( + + + +) export const DownIcon = (props) => ( ( export const HelpIcon = (props) => ( - + ) diff --git a/sites/shared/components/mdx/tabbed-example.mjs b/sites/shared/components/mdx/tabbed-example.mjs index 33b127e61f8..ce2dbd446c2 100644 --- a/sites/shared/components/mdx/tabbed-example.mjs +++ b/sites/shared/components/mdx/tabbed-example.mjs @@ -67,15 +67,7 @@ const ShowPattern = ({ renderProps, logs, mode = 'normal' }) => {
) - if (mode === 'xray') - return ( - <> -

xray

- - - ) - - return + return mode === 'xray' ? : } // Wrapper component dealing with the tabs and code view diff --git a/sites/shared/components/navigation/aside.mjs b/sites/shared/components/navigation/aside.mjs index e695c9578db..4dd91d57542 100644 --- a/sites/shared/components/navigation/aside.mjs +++ b/sites/shared/components/navigation/aside.mjs @@ -6,20 +6,18 @@ export const AsideNavigation = ({ mobileOnly = false, before = [], after = [] })