diff --git a/designs/carlton/src/collar.mjs b/designs/carlton/src/collar.mjs index b0b75cdc080..3fe0ad44237 100644 --- a/designs/carlton/src/collar.mjs +++ b/designs/carlton/src/collar.mjs @@ -182,13 +182,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', { @@ -204,6 +197,13 @@ function draftCarltonCollar({ addCut(2, 'lining') setCutOnFold(false, undefined, 'lining') setGrain(defaultGrain + 45, 'lining') + + 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/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..ad1de0f9ba4 --- /dev/null +++ b/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml @@ -0,0 +1,7 @@ +lmhCanvas: Light to Medium Hair Canvas +fabric: Main Fabric +lining: Lining +cut: Cut +paired: paired +onFoldLower: on the fold +onBias: on the bias diff --git a/plugins/plugin-cutlist/src/index.mjs b/plugins/plugin-cutlist/src/index.mjs index a61db262b49..25796651db7 100644 --- a/plugins/plugin-cutlist/src/index.mjs +++ b/plugins/plugin-cutlist/src/index.mjs @@ -9,6 +9,7 @@ export const plugin = { ['removeCut', removeCut], ['setGrain', setGrain], ['setCutOnFold', setCutOnFold], + ['getCutOnFold', getCutOnFold], ], } @@ -71,3 +72,10 @@ function setCutOnFold(store, partName, p1, p2, material = false) { return store } + +function getCutOnFold(store, partName, material = false) { + if (!material) return store.get(['cutlist', partName, 'cutOnFold']) + + const matFold = store.get(['cutlist', partName, 'materials', material, 'cutOnFold']) + return matFold === undefined ? store.get(['cutlist', partName, 'cutOnFold']) : matFold +} diff --git a/plugins/plugin-title/src/index.mjs b/plugins/plugin-title/src/index.mjs index f6a43442013..6e9d9c89ae0 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, getCutOnFold }) { 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,55 @@ 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) + newPoint.attr('data-text-transform', transform(newPoint)).addText(text, textClass) + 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 } + + const partCutlist = store.get(['cutlist', part.name]) + if (so.cutlist && partCutlist?.materials) { + for (const material in partCutlist.materials) { + const matCut = partCutlist.materials[material] + const cutPoint = nextPoint('plugin:cut', 'text-md fill-current') + cutPoint.addText(matCut.cut) + if (!matCut.indentical && matCut.cut > 1) cutPoint.addText('plugin:paired') + if (typeof getCutOnFold(material) === 'number') cutPoint.addText('plugin:onFoldLower') + cutPoint.addText('plugin:from').addText('plugin:' + material) + + points[`_${prefix}_titleCut_${material}`] = 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 +106,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/shared/components/workbench/draft/text.mjs b/sites/shared/components/workbench/draft/text.mjs index a438c8348ff..5b6150c8200 100644 --- a/sites/shared/components/workbench/draft/text.mjs +++ b/sites/shared/components/workbench/draft/text.mjs @@ -53,7 +53,7 @@ const XrayText = (props) => ( ) const TextSpans = ({ point, className = '', style = {}, onClick = null }) => { - const { t } = useTranslation(['app']) + const { t } = useTranslation(['plugin']) let text = [] // Handle translation let translated = '' @@ -117,7 +117,7 @@ const XrayTextOnPath = (props) => ( ) export const TextOnPath = (props) => { - const { t } = useTranslation(['app']) + const { t } = useTranslation(['plugin']) // Handle translation (and spaces) let translated = '' for (let string of props.path.attributes.getAsArray('data-text')) { diff --git a/sites/shared/components/workbench/exporting/export-handler.mjs b/sites/shared/components/workbench/exporting/export-handler.mjs index 4a332c7dcf9..1e82c9aa462 100644 --- a/sites/shared/components/workbench/exporting/export-handler.mjs +++ b/sites/shared/components/workbench/exporting/export-handler.mjs @@ -1,6 +1,7 @@ import Worker from 'web-worker' import fileSaver from 'file-saver' import { themePlugin } from '@freesewing/plugin-theme' +import { pluginI18n } from '@freesewing/plugin-i18n' import { pagesPlugin } from '../layout/plugin-layout-part.mjs' import { capitalize } from 'shared/utils.mjs' @@ -75,14 +76,7 @@ export const handleExport = async (format, gist, design, t, app, onComplete, onE // add the theme and translation to the pattern pattern.use(themePlugin, { stripped: format !== 'svg', skipGrid: ['pages'] }) - pattern.use( - { - hooks: { - insertText: (locale, text, { t }) => t(text), - }, - }, - { t } - ) + pattern.use(pluginI18n, { t }) // a specified size should override the gist one if (format !== 'pdf') { diff --git a/sites/shared/components/workbench/exporting/index.mjs b/sites/shared/components/workbench/exporting/index.mjs index d7210d02fe7..10267cf1cb6 100644 --- a/sites/shared/components/workbench/exporting/index.mjs +++ b/sites/shared/components/workbench/exporting/index.mjs @@ -9,7 +9,7 @@ export const ExportDraft = ({ gist, design, app }) => { const [error, setError] = useState(false) const [format, setFormat] = useState(false) - const { t } = useTranslation(['app']) + const { t } = useTranslation(['app', , 'plugin']) const doExport = (format) => { setLink(false) setError(false) diff --git a/sites/shared/components/workbench/layout/cut/index.mjs b/sites/shared/components/workbench/layout/cut/index.mjs index a3ada1d56f2..135923b7270 100644 --- a/sites/shared/components/workbench/layout/cut/index.mjs +++ b/sites/shared/components/workbench/layout/cut/index.mjs @@ -5,6 +5,7 @@ import { fabricPlugin } from '../plugin-layout-part.mjs' import { cutLayoutPlugin } from './plugin-cut-layout.mjs' import { pluginCutlist } from '@freesewing/plugin-cutlist' import { pluginFlip } from '@freesewing/plugin-flip' +import { pluginI18n } from '@freesewing/plugin-i18n' import { measurementAsMm } from 'shared/utils.mjs' import { useEffect } from 'react' import get from 'lodash.get' @@ -22,7 +23,7 @@ const useFabricSettings = (gist) => { return { activeFabric, sheetWidth, grainDirection, sheetHeight } } -const useFabricDraft = (gist, design, fabricSettings) => { +const useFabricDraft = (gist, design, fabricSettings, t) => { // get the appropriate layout for the view const layout = get(gist, ['layouts', gist._state.view, fabricSettings.activeFabric]) || gist.layout || true @@ -43,6 +44,8 @@ const useFabricDraft = (gist, design, fabricSettings) => { // also, pluginCutlist and pluginFlip are needed draft.use(pluginCutlist) draft.use(pluginFlip) + // add translation + draft.use(pluginI18n, { t }) // draft the pattern draft.draft() @@ -77,7 +80,7 @@ export const CutLayout = (props) => { }) const fabricSettings = useFabricSettings(gist) - const { draft, patternProps } = useFabricDraft(gist, design, fabricSettings) + const { draft, patternProps } = useFabricDraft(gist, design, fabricSettings, t) const fabricList = useFabricList(draft) const setCutFabric = (newFabric) => { diff --git a/sites/shared/components/workbench/layout/print/index.mjs b/sites/shared/components/workbench/layout/print/index.mjs index 3c0a79f2cfc..ca99b1831cd 100644 --- a/sites/shared/components/workbench/layout/print/index.mjs +++ b/sites/shared/components/workbench/layout/print/index.mjs @@ -15,7 +15,7 @@ export const PrintLayout = (props) => { if (props.gist?._state?.xray?.enabled) props.updateGist(['_state', 'xray', 'enabled'], false) }) - const { t } = useTranslation(['workbench']) + const { t } = useTranslation(['workbench', 'plugin']) const [error, setError] = useState(false) const draft = props.draft diff --git a/sites/shared/components/wrappers/workbench.mjs b/sites/shared/components/wrappers/workbench.mjs index c1619fbfaf4..c66fb9ae818 100644 --- a/sites/shared/components/wrappers/workbench.mjs +++ b/sites/shared/components/wrappers/workbench.mjs @@ -1,8 +1,10 @@ // Hooks import { useEffect, useState, useMemo } from 'react' import { useGist } from 'shared/hooks/useGist' +import { useTranslation } from 'next-i18next' // Dependencies import { pluginTheme } from '@freesewing/plugin-theme' +import { pluginI18n } from '@freeSewing/plugin-i18n' import { preloaders } from 'shared/components/workbench/preloaders.mjs' // Components import { WorkbenchMenu } from 'shared/components/workbench/menu/index.mjs' @@ -70,6 +72,8 @@ export const WorkbenchWrapper = ({ const [messages, setMessages] = useState([]) const [popup, setPopup] = useState(false) const [preloaded, setPreloaded] = useState(false) + // we'll only use this if the renderer is svg, but we can't call hooks conditionally + const { t } = useTranslation(['plugin']) // We'll use this in more than one location const hasRequiredMeasurements = hasRequiredMeasurementsMethod(design, gist) @@ -128,7 +132,10 @@ export const WorkbenchWrapper = ({ //draft.__init() // add theme to svg renderer - if (gist.renderer === 'svg') draft.use(pluginTheme, { skipGrid: ['pages'] }) + if (gist.renderer === 'svg') { + draft.use(pluginI18n, { t }) + draft.use(pluginTheme, { skipGrid: ['pages'] }) + } // draft it for draft and event views. Other views may add plugins, etc and we don't want to draft twice try {