diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 5077460b3cd..e8e7cde6bad 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -99,9 +99,9 @@ jaeger: '@freesewing/plugin-mirror': *freesewing new-design: _: - 'axios': '1.3.3' + 'axios': '1.3.4' 'chalk': '5.0.1' - 'execa': '6.1.0' + 'execa': '7.0.0' 'mustache': '4.2.0' 'ora': '6.1.0' 'prompts': '2.4.2' @@ -222,14 +222,14 @@ backend: 'pino': '8.11.0' 'qrcode': '1.5.1' 'swagger-ui-dist': '4.17.1' - 'swagger-ui-express': '4.6.0' + 'swagger-ui-express': '4.6.2' dev: 'chai': *chai 'chai-http': '4.3.0' - 'esbuild': '0.17.10' + 'esbuild': '0.17.11' 'mocha': *mocha 'mocha-steps': '1.3.0' - 'nodemon': '2.0.20' + 'nodemon': '2.0.21' 'prisma': *prisma dev: @@ -238,7 +238,7 @@ dev: '@mdx-js/mdx': *mdx '@mdx-js/react': *mdx '@mdx-js/runtime': &mdxRuntime '2.0.0-next.9' - '@next/bundle-analyzer': &next '13.2.3' + '@next/bundle-analyzer': &next '13.2.4' '@tailwindcss/typography': &tailwindTypography '0.5.9' 'algoliasearch': '4.15.0' 'daisyui': &daisyui '2.51.3' @@ -361,7 +361,7 @@ sanity: 'eslint': *eslint 'prettier': '2.8.4' 'typescript': '4.9.5' - '@sanity/cli': '3.2.6' + '@sanity/cli': '3.6.0' shared: _: @@ -380,6 +380,7 @@ shared: 'lodash.clonedeep': '4.5.0' 'lodash.orderby': *_orderby 'lodash.unset': *_unset + 'lodash.get': *_get 'mdast-util-toc': '6.1.1' 'pdfkit': '0.13.0' 'postcss-for': '2.1.1' @@ -388,7 +389,7 @@ shared: 'react-markdown': *reactMarkdown 'react-sizeme': '3.0.2' 'react-timeago': *reactTimeago - 'react-zoom-pan-pinch': '2.6.1' + 'react-zoom-pan-pinch': '3.0.2' 'rehype-autolink-headings': *rehypeAutolinkHeadings 'rehype-highlight': *rehypeHighlight 'remark-smartypants': '2.0.0' diff --git a/designs/bella/src/back.mjs b/designs/bella/src/back.mjs index adb64cd9590..9d4565bf127 100644 --- a/designs/bella/src/back.mjs +++ b/designs/bella/src/back.mjs @@ -65,6 +65,7 @@ export const back = { measurements, log, part, + addCut, }) => { // Get to work points.cbNeck = new Point(0, measurements.neck * options.backNeckCutout) @@ -269,6 +270,8 @@ export const back = { on: ['armholePitch', 'bustCenter'], }) + addCut() + if (sa) paths.sa = paths.saBase.offset(sa).attr('class', 'fabric sa') if (paperless) { diff --git a/designs/carlita/src/front.mjs b/designs/carlita/src/front.mjs index 6b5fe78866f..8d9ca2c66a8 100644 --- a/designs/carlita/src/front.mjs +++ b/designs/carlita/src/front.mjs @@ -18,6 +18,7 @@ function draftCarlitaFront({ paths, Path, part, + addCut, }) { /** * we're adding half of the proportionate amount of chest east for the bust span @@ -348,6 +349,10 @@ function draftCarlitaFront({ .attr('class', 'fabric help') if (complete) { + if (typeof addCut === 'function') { + addCut() + addCut({ material: 'lining' }) + } snippets.button1Left = new Snippet('button', points.button1Left).attr('data-scale', 2) snippets.button1Right = new Snippet('button', points.button1Right).attr('data-scale', 2) snippets.button2Left = new Snippet('button', points.button2Left).attr('data-scale', 2) diff --git a/designs/carlita/src/side.mjs b/designs/carlita/src/side.mjs index 5e7edfd0664..90187faab84 100644 --- a/designs/carlita/src/side.mjs +++ b/designs/carlita/src/side.mjs @@ -13,6 +13,7 @@ function draftCarlitaSide({ paths, Path, part, + addCut, }) { // Give points their original names for (let i of store.get('side')) points[i] = points[i + 'Rot2'].clone() @@ -38,6 +39,10 @@ function draftCarlitaSide({ paths.seam = paths.saBase.clone().line(points.hem).close().attr('class', 'fabric') if (complete) { + if (typeof addCut === 'function') { + addCut() + addCut({ material: 'lining' }) + } points.title = points.bustPoint.shiftFractionTowards(points.waist, 0.5) macro('title', { at: points.title, diff --git a/designs/carlton/src/back.mjs b/designs/carlton/src/back.mjs index 69605c62e53..22c42e97f92 100644 --- a/designs/carlton/src/back.mjs +++ b/designs/carlton/src/back.mjs @@ -1,6 +1,7 @@ import { back as bentBack } from '@freesewing/bent' import { calculateRatios } from './shared.mjs' import { hidePresets } from '@freesewing/core' +import { pluginCutlist } from '@freesewing/plugin-cutlist' function draftCarltonBack({ paperless, @@ -17,6 +18,7 @@ function draftCarltonBack({ paths, Path, part, + addCut, }) { calculateRatios(part) // Belt width @@ -96,6 +98,9 @@ function draftCarltonBack({ .line(points.bpStart) .attr('class', 'dashed') + addCut() + addCut({ cut: 2, material: 'lining' }) + if (complete) { macro('sprinkle', { snippet: 'bnotch', @@ -239,5 +244,6 @@ export const back = { waistEase: { pct: 14, min: 8, max: 25, menu: 'fit' }, seatEase: { pct: 14, min: 8, max: 25, menu: 'fit' }, }, + plugins: [pluginCutlist], draft: draftCarltonBack, } diff --git a/designs/carlton/src/belt.mjs b/designs/carlton/src/belt.mjs index 0701ad78d50..00bf0b748fe 100644 --- a/designs/carlton/src/belt.mjs +++ b/designs/carlton/src/belt.mjs @@ -13,6 +13,7 @@ function draftCarltonBelt({ paths, Path, part, + addCut, }) { let length = 1.6 * (store.get('cbToDart') + store.get('dartToSide')) let width = store.get('beltWidth') @@ -49,6 +50,7 @@ function draftCarltonBelt({ .close() .attr('class', 'fabric') + addCut({ cut: 4 }) if (complete) { snippets.button = new Snippet('button', points.button).attr('data-scale', 2) points.title = new Point(points.bottomRight.x / 2, points.bottomRight.y / 2) diff --git a/designs/carlton/src/chestpocketbag.mjs b/designs/carlton/src/chestpocketbag.mjs index 25c57d0d89b..b13b943b7a1 100644 --- a/designs/carlton/src/chestpocketbag.mjs +++ b/designs/carlton/src/chestpocketbag.mjs @@ -12,6 +12,7 @@ function draftCarltonChestPocketBag({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point( @@ -43,6 +44,8 @@ function draftCarltonChestPocketBag({ .line(points.startRight) .attr('class', 'lining dashed') + addCut({ material: 'lining' }) + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/chestpocketwelt.mjs b/designs/carlton/src/chestpocketwelt.mjs index 2505aec8800..ee5715b707b 100644 --- a/designs/carlton/src/chestpocketwelt.mjs +++ b/designs/carlton/src/chestpocketwelt.mjs @@ -11,6 +11,7 @@ function draftCarltonChestPocketWelt({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point(store.get('chestPocketWidth') * 2, store.get('chestPocketHeight')) @@ -30,6 +31,9 @@ function draftCarltonChestPocketWelt({ paths.fold = new Path().move(points.topMid).line(points.bottomMid).attr('class', 'dashed') + addCut() + addCut({ material: 'lmhCanvas' }) + if (complete) { points.title = new Point(points.bottomRight.x / 4, points.bottomRight.y / 2) macro('title', { diff --git a/designs/carlton/src/collar.mjs b/designs/carlton/src/collar.mjs index 11805ef1074..7f38719cb75 100644 --- a/designs/carlton/src/collar.mjs +++ b/designs/carlton/src/collar.mjs @@ -16,6 +16,7 @@ function draftCarltonCollar({ paths, Path, part, + addCut, }) { // We're going to slash and spread this collar. Slashing first: // Divide top in 5 parts @@ -178,13 +179,6 @@ function draftCarltonCollar({ paths.seam = paths.saBase.clone().line(points.standTop).close().attr('class', 'fabric') if (complete) { - points.title = points.standTopCp.clone() - macro('title', { - at: points.title, - nr: 8, - title: 'collar', - }) - // Remove grainline from collarstand part delete paths.grainline macro('cutonfold', { @@ -193,6 +187,16 @@ function draftCarltonCollar({ grainline: true, }) + addCut({ cut: 1 }) + addCut({ cut: 1, bias: true }) + addCut({ cut: 2, material: 'lining', bias: true, ignoreOnFold: true }) + + points.title = points.standTopCp.clone() + macro('title', { + at: points.title, + nr: 8, + title: 'collar', + }) if (sa) { paths.sa = paths.saBase.offset(sa) paths.sa = paths.sa diff --git a/designs/carlton/src/collarstand.mjs b/designs/carlton/src/collarstand.mjs index 605337aced4..01add0ef39f 100644 --- a/designs/carlton/src/collarstand.mjs +++ b/designs/carlton/src/collarstand.mjs @@ -14,6 +14,7 @@ function draftCarltonCollarStand({ paths, Path, part, + addCut, }) { let height = measurements.chest * options.collarHeight let length = store.get('frontCollarLength') + store.get('backCollarLength') @@ -45,6 +46,9 @@ function draftCarltonCollarStand({ .close() .attr('class', 'fabric') + addCut() + addCut({ cut: 1, material: 'lmhCanvas' }) + if (complete) { points.title = points.bottomLeftCp.clone() macro('title', { diff --git a/designs/carlton/src/cufffacing.mjs b/designs/carlton/src/cufffacing.mjs index ba3ef9e7bd7..04d625800fb 100644 --- a/designs/carlton/src/cufffacing.mjs +++ b/designs/carlton/src/cufffacing.mjs @@ -12,6 +12,7 @@ function draftCarltonCuffFacing({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point( @@ -46,6 +47,9 @@ function draftCarltonCuffFacing({ .close() .attr('class', 'fabric') + addCut() + addCut({ cut: 2, material: 'lmhCanvas' }) + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/front.mjs b/designs/carlton/src/front.mjs index 9ca1b686cfa..97700df2efc 100644 --- a/designs/carlton/src/front.mjs +++ b/designs/carlton/src/front.mjs @@ -1,6 +1,7 @@ import { front as bentFront } from '@freesewing/bent' import { calculateRatios } from './shared.mjs' import { hidePresets } from '@freesewing/core' +import { pluginCutlist } from '@freesewing/plugin-cutlist' function draftCarltonFront({ paperless, @@ -18,6 +19,7 @@ function draftCarltonFront({ paths, Path, part, + addCut, }) { calculateRatios(part) @@ -299,6 +301,8 @@ function draftCarltonFront({ .close() .attr('class', 'fabric help') + addCut() + if (complete) { snippets.button1Left = new Snippet('button', points.button1Left).attr('data-scale', 2) snippets.button1Right = new Snippet('button', points.button1Right).attr('data-scale', 2) @@ -502,5 +506,6 @@ export const front = { seatEase: { pct: 14, min: 8, max: 25, menu: 'fit' }, innerPocketWeltHeight: { pct: 3.5, min: 2.5, max: 5, menu: 'pockets' }, }, + plugins: [pluginCutlist], draft: draftCarltonFront, } diff --git a/designs/carlton/src/innerpocketbag.mjs b/designs/carlton/src/innerpocketbag.mjs index fca9223fe62..b51971730c9 100644 --- a/designs/carlton/src/innerpocketbag.mjs +++ b/designs/carlton/src/innerpocketbag.mjs @@ -13,6 +13,7 @@ function draftCarltonInnerPocketBag({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point( @@ -44,6 +45,8 @@ function draftCarltonInnerPocketBag({ .line(points.startRight) .attr('class', 'lining dashed') + addCut({ material: 'lining' }) + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/innerpockettab.mjs b/designs/carlton/src/innerpockettab.mjs index 69cf1bd7011..d91da082578 100644 --- a/designs/carlton/src/innerpockettab.mjs +++ b/designs/carlton/src/innerpockettab.mjs @@ -11,6 +11,7 @@ function draftCarltonInnerPocketTab({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.topRight = new Point(store.get('innerPocketWidth') * 1.2, 0) @@ -30,6 +31,8 @@ function draftCarltonInnerPocketTab({ paths.hint = new Path().move(points.top).line(points.bottom).attr('class', 'lining dashed') + addCut({ cut: 1, material: 'lining' }) + if (complete) { points.title = points.top.shiftFractionTowards(points.bottom, 0.5) macro('title', { diff --git a/designs/carlton/src/innerpocketwelt.mjs b/designs/carlton/src/innerpocketwelt.mjs index 156e4475c53..436feb9b7a9 100644 --- a/designs/carlton/src/innerpocketwelt.mjs +++ b/designs/carlton/src/innerpocketwelt.mjs @@ -11,6 +11,7 @@ function draftCarltonInnerPocketWelt({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point( @@ -49,6 +50,9 @@ function draftCarltonInnerPocketWelt({ .close() .attr('class', 'lashed') + addCut() + addCut({ material: 'lmhCanvas' }) + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/pocket.mjs b/designs/carlton/src/pocket.mjs index b7b238db5bc..bf04daedabd 100644 --- a/designs/carlton/src/pocket.mjs +++ b/designs/carlton/src/pocket.mjs @@ -12,6 +12,7 @@ function draftCarltonPocket({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point(store.get('pocketWidth'), store.get('pocketHeight')) @@ -53,6 +54,8 @@ function draftCarltonPocket({ paths.fold = new Path().move(points.topLeft).line(points.topRight).attr('class', 'fabric dashed') + addCut() + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/pocketflap.mjs b/designs/carlton/src/pocketflap.mjs index 7efbe4ef82e..f72bd4e1288 100644 --- a/designs/carlton/src/pocketflap.mjs +++ b/designs/carlton/src/pocketflap.mjs @@ -12,6 +12,7 @@ function draftCarltonPocketFlap({ paths, Path, part, + addCut, }) { points.topLeft = new Point(0, 0) points.bottomRight = new Point(store.get('pocketWidth'), store.get('pocketFlapHeight')) @@ -45,6 +46,9 @@ function draftCarltonPocketFlap({ paths.seam = paths.seam.line(points.topRight).line(points.topLeft).close().attr('class', 'fabric') + addCut({ cut: 4 }) + addCut({ material: 'lmhCanvas' }) + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/pocketlining.mjs b/designs/carlton/src/pocketlining.mjs index fc78736cbd9..9091be47017 100644 --- a/designs/carlton/src/pocketlining.mjs +++ b/designs/carlton/src/pocketlining.mjs @@ -12,6 +12,7 @@ function draftCarltonPocketLining({ paths, Path, part, + addCut, }) { points.topLeft = points.bottomLeft.shiftFractionTowards(points.topLeft, 0.75) points.topRight = new Point(points.bottomRight.x, points.topLeft.y) @@ -45,6 +46,8 @@ function draftCarltonPocketLining({ delete paths.fold + addCut({ material: 'lining' }) + if (complete) { points.title = points.topLeft.shiftFractionTowards(points.bottomRight, 0.5) macro('title', { diff --git a/designs/carlton/src/tail.mjs b/designs/carlton/src/tail.mjs index 57b5651d2ec..c88d55de646 100644 --- a/designs/carlton/src/tail.mjs +++ b/designs/carlton/src/tail.mjs @@ -13,6 +13,7 @@ function draftCarltonTail({ paths, Path, part, + addCut, }) { let length = store.get('waistToHem') - store.get('beltWidth') / 2 @@ -68,6 +69,9 @@ function draftCarltonTail({ .line(points.fold5Bottom) .attr('class', 'fabric dashed') + addCut() + addCut({ material: 'lining' }) + if (complete) { points.title = points.fold4Top.shiftFractionTowards(points.waistBottom, 0.5) macro('title', { diff --git a/designs/carlton/src/topsleeve.mjs b/designs/carlton/src/topsleeve.mjs index 859946262af..7af55e2f372 100644 --- a/designs/carlton/src/topsleeve.mjs +++ b/designs/carlton/src/topsleeve.mjs @@ -1,5 +1,6 @@ import { topSleeve as bentTopSleeve } from '@freesewing/bent' import { front as bentFront } from '@freesewing/bent' +import { pluginCutlist } from '@freesewing/plugin-cutlist' function draftCarltonTopSleeve({ paperless, @@ -15,6 +16,7 @@ function draftCarltonTopSleeve({ Snippet, snippets, part, + addCut, }) { // Add cuff let length = measurements.shoulderToWrist * options.cuffLength @@ -53,6 +55,9 @@ function draftCarltonTopSleeve({ .close() .attr('class', 'fabric') + addCut() + addCut({ material: 'lining' }) + if (complete) { macro('grainline', { from: points.boxBottom, @@ -175,5 +180,6 @@ export const topSleeve = { sleevecapHeight: { pct: 45, min: 40, max: 60, menu: 'advanced' }, sleevecapEase: { pct: 1, min: 0, max: 10, menu: 'advanced' }, }, + plugins: [pluginCutlist], draft: draftCarltonTopSleeve, } diff --git a/designs/carlton/src/undersleeve.mjs b/designs/carlton/src/undersleeve.mjs index 1ba8482cd43..e16a298ba5f 100644 --- a/designs/carlton/src/undersleeve.mjs +++ b/designs/carlton/src/undersleeve.mjs @@ -1,5 +1,6 @@ import { underSleeve as bentUnderSleeve } from '@freesewing/bent' import { front as bentFront } from '@freesewing/bent' +import { pluginCutlist } from '@freesewing/plugin-cutlist' function draftCarltonUnderSleeve({ paperless, @@ -14,6 +15,7 @@ function draftCarltonUnderSleeve({ paths, Path, part, + addCut, }) { // Add cuff let length = measurements.shoulderToWrist * options.cuffLength @@ -49,6 +51,8 @@ function draftCarltonUnderSleeve({ .close() .attr('class', 'fabric') + addCut() + addCut({ material: 'lining' }) if (complete) { macro('grainline', { from: points.boxBottom, @@ -153,5 +157,6 @@ export const underSleeve = { sleevecapHeight: { pct: 45, min: 40, max: 60, menu: 'advanced' }, sleevecapEase: { pct: 1, min: 0, max: 10, menu: 'advanced' }, }, + plugins: [pluginCutlist], draft: draftCarltonUnderSleeve, } diff --git a/markdown/dev/howtos/design/cutlist/en.md b/markdown/dev/howtos/design/cutlist/en.md new file mode 100644 index 00000000000..eb52e7c2895 --- /dev/null +++ b/markdown/dev/howtos/design/cutlist/en.md @@ -0,0 +1,150 @@ +--- +title: "Include Cutting Instructions" +--- + +To include cutting instructions with your part, use the [cutlist plugin](/reference/plugins/cutlist) to add the [`addCut` method](/reference/plugins/cutlist#addcut) to your part's [`draft` method](/reference/api/part/draft) + +When you use the cutlist plugin, the [grainline plugin](/reference/plugins/grainline) and the [cut on fold plugin](/reference/plugins/cutonfold) will automatically add grain and fold information to the cutting instructions + +These cutting instructions get used by the [title macro](/reference/macros/title), so be sure to add them before adding your part's title. + + +
+ addCut() Parameters + +Pass an object to the `addCut` method with any of the following keys; any you don't provide will be filled with the defaults: + +| Key | Type | Default | Description | +| :-- | :--- | :------ | :---------- | +| cut | Number\|false | 2 | the number of pieces to cut from the specified material. Pass `false` to clear all cutting instructions for the material | +| material | String | 'fabric' | the translation key of the material to cut from | +| identical | Boolean | false | should even numbers of pieces be cut in the same direction? false for mirrored | +| bias | Boolean | false | should the pieces in these cutting instructions be cut on the bias | +| ignoreOnFold | Boolean | false | should these cutting instructions ignore any cutOnFold information set by the part | + + +You can use any `string` you want for your material, but here are some standard ones we have translation for + +| Key | Translation | +|:--|:--| +| fabric | Main Fabric | +| lining | Lining | +| canvas | Canvas | +| lmhCanavas | Light to Medium Hair Canvas | +| heavyCanvas | Heavyweight Hair Canvas | +| interfacing | Interfacing | +| plastic | Plastic | +| ribbing | Ribbing | + +
+
+ + +## Basic Usage +For simple cutting instructions, you can rely on the default method parameters + +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, addCut}) => { + // add instructions to cut two mirrored from main fabric + addCut() + } +} +``` + +## Intermediate Usage +For many designs, you'll want more than just "Cut 2 mirrored from Main Fabric" + +### Specifying materials, number of pieces, orientation + +You can override the default values to specify different materials, number of pieces to cut, and whether they should be mirrored or identical + +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, addCut}) => { + // add instructions to cut three identical from lining + addCut({cut: 3, material: 'lining', identical: true}) + } +} +``` + +### Instructions for multiple materials +You can add as many sets of instructions as you need + +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, addCut}) => { + // add instructions to cut four mirrored from main fabric + addCut({cut: 4}) + // add instructions to cut three identical from lining + addCut({cut: 3, material: 'lining', identical: true}) + } +} +``` + +## Advanced usage + +### Cut some on the fold, some not +Sometimes you want some pieces cut on the fold and others cut as halves to seam together. + +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' +import {pluginCutonfold} from '@freesewing/plugin-cutonfold' + +const part = { + name: 'example.front', + plugins: [pluginCutlist, pluginCutonfold], + draft: ({part, points, Point, macro, addCut}) => { + // set the cut on fold line + points.p1 = new Point(0, 0) + points.p2 = new Point(0, 10) + + // pieces should be cut on the fold + macro('cutonfold', {from: points.p1, to: points.p2}) + + // cut two on the fold + addCut() + // cut two, not on the fold + addCut({cut: 2, ignoreOnFold: true}) + } +} +``` + + +### Cut some on the grain, some on the bias +You set the grainline on a piece, but you also need some to be cut on the bias + +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' +import {pluginGrainline} from '@freesewing/plugin-grainline' + +const part = { + name: 'example.front', + plugins: [pluginCutlist, pluginGrainline], + draft: ({part, points, Point, macro, addCut}) => { + // set the cut on fold line + points.p1 = new Point(0, 0) + points.p2 = new Point(0, 10) + + // the grain runs from p1 to p2 + macro('grainline', {from: points.p1, to: points.p2}) + + // cut two mirrored on the grain + addCut() + // cut two mirrored on the bias + addCut({cut: 2, bias: true}) + } +} +``` diff --git a/markdown/dev/reference/api/part/draft/en.md b/markdown/dev/reference/api/part/draft/en.md index c9936bf1771..3a3f7943538 100644 --- a/markdown/dev/reference/api/part/draft/en.md +++ b/markdown/dev/reference/api/part/draft/en.md @@ -46,4 +46,4 @@ access the following properties: || **_Return value_** | | `part` | Your draft method **must** return this | - + Some plugins, such as the [cutlist plugin](/reference/plugins/cutlist) add additional methods to this object that can be accessed through the same destructuring diff --git a/markdown/dev/reference/macros/title/en.md b/markdown/dev/reference/macros/title/en.md index 6fbe9e3d57b..198b34b262b 100644 --- a/markdown/dev/reference/macros/title/en.md +++ b/markdown/dev/reference/macros/title/en.md @@ -11,6 +11,7 @@ It is provided by the [title plugin](/reference/plugins/title). macro('title', { Boolean append, Point at, + Boolean cutlist String nr, String prefix, Number rotation, @@ -50,11 +51,12 @@ macro('title', { | Property | Default | Type | Description | | ----------:| :-----: | ------------------- | ----------- | +| `append` | `false` | Boolean | Set this to `true` to append the `nr` to any text already set in Point `at`'s attributes, rather than overwrite it | | `at` | | [Point](/reference/api/point) | The point at which to insert the title | +| `cutlist` | `true` | Boolean | Whether to include cutting instructions | | `nr` | | String | The number of the pattern part | | `title` | | String | The name of the pattern part. If title is not set or is an empty string, this won't be rendered, and the version will go beneath the nr.| | `prefix` | | String | A prefix to add to the created points. This allow for more than 1 title per part, as long as you give them a different prefix.| -| `append` | `false` | Boolean | Set this to `true` to append the `nr` to any text already set in Point `at`'s attributes, rather than overwrite it | | `rotation` | 0 | Number | An optional rotation in degrees | | `scale` | 1 | Number | An optional scaling factor | @@ -64,6 +66,7 @@ macro('title', { |-------------------|-------------| | `points._${prefix}_titleNr` | Point anchoring the part number text | | `points._${prefix}_titleName` | Point anchoring the part name text | +| `points._${prefix}_titleCut_${material}_${i} | Points anchoring the cutting instructions, by material key and instruction index | | `points._${prefix}_titlePattern` | Point anchoring the pattern name text | | `points._${prefix}_titleFor` | Point anchoring the name of the person for whom the pattern was made, if that information exists | | `points._${prefix}_exportDate` | Point anchoring the pattern export date | diff --git a/markdown/dev/reference/plugins/cutlist/en.md b/markdown/dev/reference/plugins/cutlist/en.md new file mode 100644 index 00000000000..6d739c494e0 --- /dev/null +++ b/markdown/dev/reference/plugins/cutlist/en.md @@ -0,0 +1,188 @@ +--- +title: plugin-cutlist +--- + +Published as [@freesewing/plugin-cutlist][1], this plugin provides additional methods to the [part draft function](/reference/api/part/draft) which allow you to configure cutting instructions for your parts. + + For an in-depth look at how to add cutting instructions to your part, see our [cutlist how-to](/howtos/design/cutlist) + +## Installation + +```sh +npm install @freesewing/plugin-cutonfold +``` + +## Usage + +Either [add it as a part plugin](/reference/api/part/config/plugins) in your +design, or [add it to a pattern instance with +Pattern.use()](/reference/api/pattern/use). + +To import the plugin for use: +```js +import { cutlistPlugin } from '@freesewing/plugin-cutlist' +// or +import { pluginCutlist } from '@freesewing/plugin-cutlist' +``` + +## Methods + +The cutlist plugin adds the following methods to the part draft method parameter + +### addCut + +The `addCut()` method will add a set of cutting instructions for the part + +#### Signature +```js +addCut(Object so) +```` + +Pass an object to the `addCut` method with any of the following keys; any you don't provide will be filled with the defaults: + +| Key | Type | Default | Description | +| :-- | :--- | :------ | :---------- | +| cut | Number\|false | 2 | the number of pieces to cut from the specified material. Pass `false` to clear all cutting instructions for the material | +| material | String | 'fabric' | the translation key of the material to cut from | +| identical | Boolean | false | should even numbers of pieces be cut in the same direction? false for mirrored | +| bias | Boolean | false | should the pieces in these cutting instructions be cut on the bias | +| ignoreOnFold | Boolean | false | should these cutting instructions ignore any cutOnFold information set by the part | + + +
+ You can use any `string` you want for your material, but here are some standard ones we have translation for +
+ +| Key | Translation | +|:--|:--| +| fabric | Main Fabric | +| lining | Lining | +| canvas | Canvas | +| lmhCanavas | Light to Medium Hair Canvas | +| heavyCanvas | Heavyweight Hair Canvas | +| interfacing | Interfacing | +| plastic | Plastic | +| ribbing | Ribbing | + +
+
+
+ +#### Example + +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, addCut}) => { + // add instructions to cut two from main fabric + addCut() + // add instructions to cut four on the biad from lining + addCut({cut: 4, material: 'lining', bias: true, }) + return part + } +} +``` + +You can also add multiple sets of cutting instructions for the same material +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, addCut}) => { + // add instructions to 1 from lining + addCut({cut: 1, material: 'lining'}) + // add instructions to cut 1 on the bias from lining + addCut({cut: 1, material: 'lining', bias: true, }) + return part + } +} +``` + +### removeCut + +The `removeCut()` method will remove cutting instructions from the part + +#### Signature + +```js +removeCut(String material) +``` + +#### Example +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, removeCut}) => { + // remove all cutting instructions for all materials + removeCut() + + // remove cutting instructions for just one material + removeCut('fabric') + return part + } +} +``` +### setGrain + +The `setGrain()` method will record the angle of the grainline annotation. This method is called internally by [`plugin-grainline`](/reference/plugins/grainline) to store information for cutting layout tools. You shouldn't have to call it, but it's there if you need it. + +#### Signature + +```js +setGrain(Number grainAngle) +``` + +#### Example +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, setGrain}) => { + // set the grainline angle + setGrain(0) + return part + } +} +``` + +### setCutOnFold +The `setCutOnFold()` method will record the points that make up the cut on fold line. This method is called internally by [`plugin-cutonfold`](/reference/plugins/cutonfold) to store information for cutting layout tools. You shouldn't have to call it, but it's there if you need it. + +#### Signature + +```js +setCutOnFold(Point p1, Point p2) +``` + +#### Example +```js +import {pluginCutlist} from '@freesewing/plugin-cutlist' + +const part = { + name: 'example.front', + plugins: [pluginCutlist], + draft: ({part, points, Point, setCutOnFold}) => { + // set the cut on fold line + points.p1 = new Point(0, 0) + points.p2 = new Point(0, 10) + setCutOnFold(points.p1, points.p2) + return part + } +} +``` + +## Notes + +The cutlist plugin is part of our [plugin-bundle](/reference/plugins/bundle) + +[1]: https://www.npmjs.com/package/@freesewing/plugin-cutlist diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index e01f3cdbd60..1e5d184a0c0 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -163,6 +163,9 @@ Pattern.prototype.draftPartForSet = function (partName, set) { try { this.__runHooks('prePartDraft') const result = this.config.parts[partName].draft(this.parts[set][partName].shorthand()) + if (!this.__wants(partName, set)) { + result.hide() + } this.__runHooks('postPartDraft') if (typeof result === 'undefined') { this.setStores[set].log.error( diff --git a/packages/i18n/src/locales/en/plugin/index.js b/packages/i18n/src/locales/en/plugin/index.js index 21533176bec..c96001efcd6 100644 --- a/packages/i18n/src/locales/en/plugin/index.js +++ b/packages/i18n/src/locales/en/plugin/index.js @@ -9,6 +9,7 @@ import cutonfold from './plugins/cutonfold.yaml' import grainline from './plugins/grainline.yaml' import scalebox from './plugins/scalebox.yaml' import title from './plugins/title.yaml' +import cutlist from './plugins/cutlist/yaml' const files = { brian, @@ -22,6 +23,7 @@ const files = { grainline, scalebox, title, + cutlist, } const messages = {} diff --git a/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml b/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml new file mode 100644 index 00000000000..b35b0c444fe --- /dev/null +++ b/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml @@ -0,0 +1,13 @@ +canvas: Canvas +cut: Cut +fabric: Main Fabric +heavyCanvas: Heavy Canvas +interfacing: Interfacing +lining: Lining +lmhCanvas: Light to Medium Hair Canvas +mirrored: mirrored +onFoldLower: on the fold +onFoldAndBias: folded on the bias +onBias: on the bias +plastic: Plastic +ribbing: Ribbing diff --git a/packages/new-design/package.json b/packages/new-design/package.json index ef311a798e1..11ef9365544 100644 --- a/packages/new-design/package.json +++ b/packages/new-design/package.json @@ -32,9 +32,9 @@ }, "peerDependencies": {}, "dependencies": { - "axios": "1.3.3", + "axios": "1.3.4", "chalk": "5.0.1", - "execa": "6.1.0", + "execa": "7.0.0", "mustache": "4.2.0", "ora": "6.1.0", "prompts": "2.4.2", diff --git a/plugins/plugin-bundle/src/index.mjs b/plugins/plugin-bundle/src/index.mjs index d72b9f954fe..0a4f63ea1f2 100644 --- a/plugins/plugin-bundle/src/index.mjs +++ b/plugins/plugin-bundle/src/index.mjs @@ -12,6 +12,7 @@ import { roundPlugin } from '../../plugin-round/src/index.mjs' import { scaleboxPlugin } from '../../plugin-scalebox/src/index.mjs' import { sprinklePlugin } from '../../plugin-sprinkle/src/index.mjs' import { titlePlugin } from '../../plugin-title/src/index.mjs' +import { pluginCutlist } from '../../plugin-cutlist/src/index.mjs' import { name, version } from '../data.mjs' const bundledPlugins = [ @@ -29,38 +30,44 @@ const bundledPlugins = [ scaleboxPlugin, sprinklePlugin, titlePlugin, + pluginCutlist, ] -function bundleHooks() { - const hooks = {} - for (const plugin of bundledPlugins) { - for (const i in plugin.hooks) { - if (typeof hooks[i] === 'undefined') hooks[i] = [] - const hook = plugin.hooks[i] - if (typeof hook === 'function') hooks[i].push(hook) - else if (typeof hook === 'object') { - for (let method of hook) hooks[i].push(method) - } +const hooks = {} +const macros = {} +const store = [] + +function bundleHooks(plugin) { + for (const i in plugin.hooks) { + if (typeof hooks[i] === 'undefined') hooks[i] = [] + const hook = plugin.hooks[i] + if (typeof hook === 'function') hooks[i].push(hook) + else if (typeof hook === 'object') { + for (let method of hook) hooks[i].push(method) } } - - return hooks } -function bundleMacros() { - const macros = {} - for (const plugin of bundledPlugins) { - for (const i in plugin.macros) macros[i] = plugin.macros[i] - } +function bundleMacros(plugin) { + for (const i in plugin.macros) macros[i] = plugin.macros[i] +} - return macros +function bundleStore(plugin) { + if (plugin.store) store.push(...plugin.store) +} + +for (const plugin of bundledPlugins) { + bundleHooks(plugin) + bundleMacros(plugin) + bundleStore(plugin) } export const plugin = { name, version, - hooks: bundleHooks(), - macros: bundleMacros(), + hooks, + macros, + store, } // More specifically named exports diff --git a/plugins/plugin-cutlist/src/index.mjs b/plugins/plugin-cutlist/src/index.mjs index f40a9e1dd8d..9ff14095d57 100644 --- a/plugins/plugin-cutlist/src/index.mjs +++ b/plugins/plugin-cutlist/src/index.mjs @@ -16,8 +16,19 @@ export const plugin = { export const cutlistPlugin = plugin export const pluginCutlist = plugin -/** Method to add the cut info */ -function addCut(store, partName, cut = 2, material = 'fabric', identical = false) { +/** + * Add a set of cutting instructions for the part + * @param {Store} store the Store + * @param {string} partName the name of the part + * @param {Object} so a set of cutting instructions for a material + * @param {number} so.cut = 2 the number of pieces to cut from the specified fabric + * @param {string} so.material = fabric the name of the material to cut from + * @param {boolean} so.identical = false should even numbers of pieces be cut in the same direction or mirrored + * @param {boolean} so.bias = false should the pieces in these cutting instructions be cut on the bias + * @param {boolean} so.ignoreOnFold should these cutting instructions ignore any cutOnFold information set by the part + */ +function addCut(store, partName, so = {}) { + const { cut = 2, material = 'fabric', identical = false, bias = false, ignoreOnFold = false } = so if (cut === false) { if (material === false) store.unset(['cutlist', partName, 'materials']) else store.unset(['cutlist', partName, 'materials', material]) @@ -32,15 +43,15 @@ function addCut(store, partName, cut = 2, material = 'fabric', identical = false return store } const path = ['cutlist', partName, 'materials', material] - store.set([...path, 'cut'], cut) - store.set([...path, 'identical'], identical) + const existing = store.get(path) || [] + store.set(path, existing.concat({ cut, identical, bias, ignoreOnFold })) return store } /** Method to remove the cut info */ function removeCut(store, partName, material = false) { - return addCut(store, partName, false, material) + return addCut(store, partName, { cut: false, material }) } /** Method to add the grain info */ diff --git a/plugins/plugin-cutlist/tests/plugin.test.mjs b/plugins/plugin-cutlist/tests/plugin.test.mjs index 7ea607d56c8..458d26bd6ec 100644 --- a/plugins/plugin-cutlist/tests/plugin.test.mjs +++ b/plugins/plugin-cutlist/tests/plugin.test.mjs @@ -38,15 +38,20 @@ describe('Cutlist Plugin Tests', () => { const Test = new Design({ parts: [part] }) const pattern = new Test() pattern.draft() - expect(pattern.setStores[0].cutlist.example_part.materials.fabric.cut).to.equal(2) - expect(pattern.setStores[0].cutlist.example_part.materials.fabric.identical).to.equal(false) + expect(pattern.setStores[0].cutlist.example_part.materials.fabric).to.have.lengthOf(1) + expect(pattern.setStores[0].cutlist.example_part.materials.fabric[0]).to.deep.equal({ + cut: 2, + identical: false, + bias: false, + ignoreOnFold: false, + }) }) it('Should handle addCut() with non-defaults', () => { const part = { name: 'example_part', draft: ({ addCut, part }) => { - addCut(3, 'lining', true) + addCut({ cut: 3, material: 'lining', identical: true }) return part }, @@ -55,8 +60,13 @@ describe('Cutlist Plugin Tests', () => { const Test = new Design({ parts: [part] }) const pattern = new Test() pattern.draft() - expect(pattern.setStores[0].cutlist.example_part.materials.lining.cut).to.equal(3) - expect(pattern.setStores[0].cutlist.example_part.materials.lining.identical).to.equal(true) + expect(pattern.setStores[0].cutlist.example_part.materials.lining).to.have.lengthOf(1) + expect(pattern.setStores[0].cutlist.example_part.materials.lining[0]).to.deep.equal({ + cut: 3, + identical: true, + bias: false, + ignoreOnFold: false, + }) }) it('Should remove cut info via addCut(false)', () => { @@ -93,7 +103,7 @@ describe('Cutlist Plugin Tests', () => { const pattern = new Test() pattern.draft() expect(typeof pattern.setStores[0].cutlist.example_part.materials.lining).to.equal('undefined') - expect(pattern.setStores[0].cutlist.example_part.materials.fabric.cut).to.equal(2) + expect(pattern.setStores[0].cutlist.example_part.materials.fabric[0].cut).to.equal(2) }) it('Should remove cut info for all materials via removeCut(true)', () => { diff --git a/plugins/plugin-cutonfold/src/index.mjs b/plugins/plugin-cutonfold/src/index.mjs index 7b932995e14..b3d18b421d7 100644 --- a/plugins/plugin-cutonfold/src/index.mjs +++ b/plugins/plugin-cutonfold/src/index.mjs @@ -24,7 +24,7 @@ export const plugin = { delete points.cutonfoldTo delete points.cutonfoldVia1 delete points.cutonfoldVia2 - delete paths.cutonfold + delete paths.cutonfoldCutonfold // setCutOnFold relies on plugin-cutlist if (typeof setCutOnFold === 'function') { setCutOnFold(false) // Restore default diff --git a/plugins/plugin-i18n/src/index.mjs b/plugins/plugin-i18n/src/index.mjs index 86ed6e50f1a..d2aaf837654 100644 --- a/plugins/plugin-i18n/src/index.mjs +++ b/plugins/plugin-i18n/src/index.mjs @@ -11,10 +11,14 @@ export const plugin = { ) return text } - const prefix = data.prefix || '' - return typeof data.strings[locale][prefix + text] === 'undefined' - ? text - : data.strings[locale][prefix + text] + if (data.t) { + return data.t(text) + } else { + const prefix = data.prefix || '' + return typeof data.strings[locale][prefix + text] === 'undefined' + ? text + : data.strings[locale][prefix + text] + } }, }, } diff --git a/plugins/plugin-title/src/index.mjs b/plugins/plugin-title/src/index.mjs index f6a43442013..9f8d1d840eb 100644 --- a/plugins/plugin-title/src/index.mjs +++ b/plugins/plugin-title/src/index.mjs @@ -31,20 +31,17 @@ export const plugin = { }, }, macros: { - title: function (so, { points, scale, locale, store }) { + title: function (so, { points, scale, locale, store, part }) { const prefix = so.prefix || '' + let overwrite = !so.append // Passing `false` will remove the title - if (so === false) { - for (const id of [ - `_${prefix}_titleNr`, - `_${prefix}_titleName`, - `_${prefix}_titlePattern`, - `_${prefix}_titleFor`, - `_${prefix}_exportDate`, - ]) - delete points[id] - return true + if (so === false || overwrite) { + Object.keys(points).forEach((p) => { + if (p.startsWith(`_${prefix}_title`) || p === `_${prefix}_exportDate`) delete points[p] + }) + + if (so === false) return true } const transform = function (anchor) { @@ -53,44 +50,74 @@ export const plugin = { return `matrix(${so.scale}, 0, 0, ${so.scale}, ${cx}, ${cy}) rotate(${so.rotation} ${anchor.x} ${anchor.y})` } + let shift = 8 + const nextPoint = (text, textClass, shiftAmt = shift) => { + const newPoint = so.at + .shift(-90 - so.rotation, shiftAmt * so.scale) + .addText(text, textClass) + newPoint.attr('data-text-transform', transform(newPoint)) + return newPoint + } const defaults = { scale: 1, rotation: 0, + cutlist: true, } so = { ...defaults, ...so } so.scale = so.scale * scale - let overwrite = true - if (so.append) overwrite = false + points[`_${prefix}_titleNr`] = so.at .clone() .attr('data-text', so.nr, overwrite) .attr('data-text-class', 'text-4xl fill-note font-bold') .attr('data-text-transform', transform(so.at)) - let shift = 8 + if (so.title) { - points[`_${prefix}_titleName`] = so.at - .shift(-90 - so.rotation, shift * so.scale) - .attr('data-text', so.title) - .attr('data-text-class', 'text-lg fill-current font-bold') - .attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, 13 * so.scale))) + points[`_${prefix}_titleName`] = nextPoint(so.title, 'text-lg fill-current font-bold') shift += 8 } + + // Cut List instructions + const partCutlist = store.get(['cutlist', part.name]) + // if there's a cutlist and it should be included + if (so.cutlist && partCutlist?.materials) { + // get the default cutonfold + const cutonfold = partCutlist.cutOnFold + // each material + for (const material in partCutlist.materials) { + // each set of instructions + partCutlist.materials[material].forEach(({ cut, identical, bias, ignoreOnFold }, c) => { + // make a new point for this set of instructions + const cutPoint = nextPoint('plugin:cut', 'text-md fill-current').addText(cut) + + // if they're not identical, add that to the point's text + if (!identical && cut > 1) cutPoint.addText('plugin:mirrored') + + // if they should be cut on the fold add that, with bias or without + if (cutonfold && !ignoreOnFold) + cutPoint.addText(bias ? 'plugin:onFoldAndBias' : 'plugin:onFoldLower') + // otherwise if they should be on the bias, say so + else if (bias) cutPoint.addText('plugin:onBias') + + // add 'from' the material + cutPoint.addText('plugin:from').addText('plugin:' + material) + + // save and shift + points[`_${prefix}_titleCut_${material}_${c}`] = cutPoint + shift += 8 + }) + } + } + let name = store.data?.name || 'No Name' name = name.replace('@freesewing/', '') - points[`_${prefix}_titlePattern`] = so.at - .shift(-90 - so.rotation, shift * so.scale) - .attr('data-text', name) - .attr('data-text', 'v' + (store.data?.version || 'No Version')) - .attr('data-text-class', 'fill-note') - .attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, shift * so.scale))) + name += 'v' + (store.data?.version || 'No Version') + points[`_${prefix}_titlePattern`] = nextPoint(name, 'fill-note') + if (store.data.for) { shift += 8 - points[`_${prefix}_titleFor`] = so.at - .shift(-90 - so.rotation, shift * so.scale) - .attr('data-text', '( ' + store.data.for + ' )') - .attr('data-text-class', 'fill-current font-bold') - .attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, shift * so.scale))) + points[`_${prefix}_titleFor`] = nextPoint(`( ${store.data.for} )`, 'fill-current font-bold') } shift += 6 const now = new Date() @@ -98,20 +125,13 @@ export const plugin = { let mins = now.getMinutes() if (hours < 10) hours = `0${hours}` if (mins < 10) mins = `0${mins}` - points[`_${prefix}_exportDate`] = so.at - .shift(-90 - so.rotation, shift * so.scale) - .attr( - 'data-text', - now.toLocaleDateString(locale || 'en', { - weekday: 'long', - year: 'numeric', - month: 'short', - day: 'numeric', - }) - ) - .attr('data-text', `@ ${hours}:${mins}`) - .attr('data-text-class', 'text-sm') - .attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, shift * so.scale))) + const exportDate = now.toLocaleDateString(locale || 'en', { + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric', + }) + points[`_${prefix}_exportDate`] = nextPoint(`${exportDate}@ ${hours}:${mins}`, 'text-sm') }, }, } diff --git a/sites/backend/package.json b/sites/backend/package.json index a296195e1b5..e9c1804a074 100644 --- a/sites/backend/package.json +++ b/sites/backend/package.json @@ -44,15 +44,15 @@ "pino": "8.11.0", "qrcode": "1.5.1", "swagger-ui-dist": "4.17.1", - "swagger-ui-express": "4.6.0" + "swagger-ui-express": "4.6.2" }, "devDependencies": { "chai": "4.2.0", "chai-http": "4.3.0", - "esbuild": "0.17.10", + "esbuild": "0.17.11", "mocha": "10.0.0", "mocha-steps": "1.3.0", - "nodemon": "2.0.20", + "nodemon": "2.0.21", "prisma": "4.10.1" }, "engines": { diff --git a/sites/dev/components/layouts/docs.mjs b/sites/dev/components/layouts/docs.mjs index 87986e01f69..0eed64fd8bd 100644 --- a/sites/dev/components/layouts/docs.mjs +++ b/sites/dev/components/layouts/docs.mjs @@ -7,11 +7,18 @@ import { ThemePicker } from 'shared/components/theme-picker/index.mjs' import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs' import { getCrumbs } from 'shared/utils.mjs' import { HomeIcon } from 'shared/components/icons.mjs' +import { useState, useEffect } from 'react' export const DocsLayout = ({ app, title = false, crumbs = false, children = [] }) => { const router = useRouter() - const slug = router.asPath.slice(1) - const breadcrumbs = crumbs ? crumbs : getCrumbs(app, slug, title) + const [slug, setSlug] = useState('') + const [breadcrumbs, setBreadcrumbs] = useState(crumbs) + + useEffect(() => { + const newSlug = router.asPath.slice(1) + setSlug(newSlug) + if (!breadcrumbs) setBreadcrumbs(getCrumbs(app, newSlug, title)) + }, [router.asPath, breadcrumbs, app, title]) return (
diff --git a/sites/dev/components/navigation/aside.mjs b/sites/dev/components/navigation/aside.mjs index 43a9a0f292c..a10a59d9580 100644 --- a/sites/dev/components/navigation/aside.mjs +++ b/sites/dev/components/navigation/aside.mjs @@ -3,17 +3,17 @@ import { MainSections, ActiveSection } from './primary.mjs' export const AsideNavigation = ({ app, slug, mobileOnly = false, before = [], after = [] }) => (