diff --git a/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml b/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml
deleted file mode 100644
index 103bc6c1e35..00000000000
--- a/packages/i18n/src/locales/en/plugin/plugins/cutlist.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-canvas: Canvas
-cut: Cut
-cuttingLayout: Suggested Cutting Layout
-fabric: Main Fabric
-fabricSize: "{length} of {width} wide material"
-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
-edgeOfFabric: Edge of Fabric
diff --git a/packages/react-components/src/pattern/part.mjs b/packages/react-components/src/pattern/part.mjs
index 9d56af4a682..ece24d2f8b3 100644
--- a/packages/react-components/src/pattern/part.mjs
+++ b/packages/react-components/src/pattern/part.mjs
@@ -13,7 +13,7 @@ export const PartInner = forwardRef(
path={part.paths[pathName]}
topLeft={part.topLeft}
bottomRight={part.bottomRight}
- units={settings.units}
+ units={settings[0].units}
{...{ stackName, partName, pathName, part, settings, components, t }}
/>
))}
diff --git a/sites/shared/components/workbench/exporting/export-handler.mjs b/sites/shared/components/workbench/exporting/export-handler.mjs
index 4dd1258d915..4cf0626c76e 100644
--- a/sites/shared/components/workbench/exporting/export-handler.mjs
+++ b/sites/shared/components/workbench/exporting/export-handler.mjs
@@ -2,11 +2,11 @@ import Worker from 'web-worker'
import fileSaver from 'file-saver'
import { themePlugin } from '@freesewing/plugin-theme'
import { pluginI18n } from '@freesewing/plugin-i18n'
-import { pagesPlugin, fabricPlugin } from 'shared/plugins/plugin-layout-part.mjs'
+import { pagesPlugin, materialPlugin } from 'shared/plugins/plugin-layout-part.mjs'
import { pluginAnnotations } from '@freesewing/plugin-annotations'
-import { cutLayoutPlugin } from '../layout/cut/plugin-cut-layout.mjs'
-import { fabricSettingsOrDefault } from '../layout/cut/index.mjs'
-import { useFabricLength } from '../layout/cut/settings.mjs'
+import { cutLayoutPlugin } from 'shared/plugins/plugin-cut-layout.mjs'
+import { materialSettingsOrDefault } from 'shared/components/workbench/views/cut/hooks.mjs'
+import { useMaterialLength } from 'shared/components/workbench/views/cut/hooks.mjs'
import { capitalize, formatMm } from 'shared/utils.mjs'
import {
defaultPrintSettings,
@@ -14,7 +14,7 @@ import {
} from 'shared/components/workbench/views/print/config.mjs'
import get from 'lodash.get'
-export const ns = ['plugin', 'common']
+export const ns = ['cut', 'plugin', 'common']
export const exportTypes = {
exportForPrinting: ['a4', 'a3', 'a2', 'a1', 'a0', 'letter', 'tabloid'],
exportForEditing: ['svg', 'pdf'],
@@ -48,39 +48,39 @@ const themedPattern = (Design, settings, overwrite, format, t) => {
* @param {Object} settings the settings
* @param {string} format the export format this pattern will be prepared for
* @param {function} t the i18n function
- * @return {Object} a dictionary of svgs and related translation strings, keyed by fabric
+ * @return {Object} a dictionary of svgs and related translation strings, keyed by material
*/
const generateCutLayouts = (pattern, Design, settings, format, t, ui) => {
- // get the fabrics from the already drafted base pattern
- const fabrics = pattern.setStores[pattern.activeSet].cutlist.getCutFabrics(
+ // get the materials from the already drafted base pattern
+ const materials = pattern.setStores[pattern.activeSet].cutlist.getCutFabrics(
pattern.settings[0]
- ) || ['fabric']
- if (!fabrics.length) return
+ ) || ['material']
+ if (!materials.length) return
const isImperial = settings.units === 'imperial'
const cutLayouts = {}
- // each fabric
- fabrics.forEach((f) => {
- // get the settings and layout for that fabric
- const fabricSettings = fabricSettingsOrDefault(settings.units, ui, f)
- const fabricLayout = get(ui, ['layouts', 'cut', f], true)
+ // each material
+ materials.forEach((f) => {
+ // get the settings and layout for that material
+ const materialSettings = materialSettingsOrDefault(settings.units, ui, f)
+ const materialLayout = get(ui, ['layouts', 'cut', f], true)
// make a new pattern
- const fabricPattern = themedPattern(Design, settings, { layout: fabricLayout }, format, t)
- // add cut layout plugin and fabric plugin
- .use(cutLayoutPlugin(f, fabricSettings.grainDirection))
- .use(fabricPlugin({ ...fabricSettings, printStyle: true, setPatternSize: 'width' }))
+ const materialPattern = themedPattern(Design, settings, { layout: materialLayout }, format, t)
+ // add cut layout plugin and material plugin
+ .use(cutLayoutPlugin(f, materialSettings.grainDirection))
+ .use(materialPlugin({ ...materialSettings, printStyle: true, setPatternSize: 'width' }))
// draft and render
- fabricPattern.draft()
- const svg = fabricPattern.render()
+ materialPattern.draft()
+ const svg = materialPattern.render()
// include translations
cutLayouts[f] = {
svg,
- title: t('plugin:' + f),
- dimensions: t('plugin:fabricSize', {
- width: formatMm(fabricSettings.sheetWidth, settings.units, 'notags'),
- length: useFabricLength(isImperial, fabricPattern.height, 'notags'),
+ title: t('cut:' + f),
+ dimensions: t('cut:materialSize', {
+ width: formatMm(materialSettings.sheetWidth, settings.units, 'notags'),
+ length: useMaterialLength(isImperial, materialPattern.height, 'notags'),
interpolation: { escapeValue: false },
}),
}
@@ -160,7 +160,6 @@ export const handleExport = async ({
}
try {
- throw new Error('bad bad bad')
// add pages to pdf exports
if (format !== 'svg') {
pattern.use(
@@ -177,7 +176,7 @@ export const handleExport = async ({
design: capitalize(design),
tagline: t('common:sloganCome') + '. ' + t('common:sloganStay'),
url: window.location.href,
- cuttingLayout: t('plugin:cuttingLayout'),
+ cuttingLayout: t('cut:cuttingLayout'),
}
}
diff --git a/sites/shared/components/workbench/exporting/pdf-maker.mjs b/sites/shared/components/workbench/exporting/pdf-maker.mjs
index 08979c6b715..8598821d72c 100644
--- a/sites/shared/components/workbench/exporting/pdf-maker.mjs
+++ b/sites/shared/components/workbench/exporting/pdf-maker.mjs
@@ -170,8 +170,8 @@ export class PdfMaker {
}
/** generate the title for a cutting layout page */
- async generateCutLayoutTitle(fabricTitle, fabricDimensions) {
- this.addText(this.strings.cuttingLayout, 12, 2).addText(fabricTitle, 28)
+ async generateCutLayoutTitle(materialTitle, materialDimensions) {
+ this.addText(this.strings.cuttingLayout, 12, 2).addText(materialTitle, 28)
this.pdf.lineWidth(1)
this.pdf
@@ -180,16 +180,16 @@ export class PdfMaker {
.stroke()
this.lineLevel += 5
- this.addText(fabricDimensions, 16)
+ this.addText(materialDimensions, 16)
}
/** generate all cutting layout pages */
async generateCutLayoutPages() {
if (!this.pageSettings.cutlist || !this.cutLayouts) return
- for (const fabric in this.cutLayouts) {
+ for (const material in this.cutLayouts) {
this.nextPage()
- const { title, dimensions, svg } = this.cutLayouts[fabric]
+ const { title, dimensions, svg } = this.cutLayouts[material]
await this.generateCutLayoutTitle(title, dimensions)
await this.generateSvgPage(svg)
}
diff --git a/sites/shared/components/workbench/index.mjs b/sites/shared/components/workbench/index.mjs
index bef27f45400..8d351684bb6 100644
--- a/sites/shared/components/workbench/index.mjs
+++ b/sites/shared/components/workbench/index.mjs
@@ -15,8 +15,9 @@ import { ModalSpinner } from 'shared/components/modal/spinner.mjs'
import { DraftView, ns as draftNs } from 'shared/components/workbench/views/draft/index.mjs'
import { SaveView, ns as saveNs } from 'shared/components/workbench/views/save/index.mjs'
import { PrintView, ns as printNs } from 'shared/components/workbench/views/print/index.mjs'
+import { CutView, ns as cutNs } from 'shared/components/workbench/views/cut/index.mjs'
-export const ns = ['account', 'workbench', ...draftNs, ...saveNs, ...printNs]
+export const ns = ['account', 'workbench', ...draftNs, ...saveNs, ...printNs, ...cutNs]
const defaultUi = {
renderer: 'react',
@@ -25,6 +26,7 @@ const defaultUi = {
const views = {
draft: DraftView,
print: PrintView,
+ cut: CutView,
}
const draftViews = ['draft', 'test']
diff --git a/sites/shared/components/workbench/layout/cut/index.mjs b/sites/shared/components/workbench/layout/cut/index.mjs
deleted file mode 100644
index 26ad576c549..00000000000
--- a/sites/shared/components/workbench/layout/cut/index.mjs
+++ /dev/null
@@ -1,125 +0,0 @@
-import { useTranslation } from 'next-i18next'
-import { CutLayoutSettings } from './settings.mjs'
-// import { Draft } from '../draft/index.mjs'
-import { fabricPlugin } from 'shared/plugins/plugin-layout-part.mjs'
-import { cutLayoutPlugin } from './plugin-cut-layout.mjs'
-import { pluginAnnotations } from '@freesewing/plugin-annotations'
-import { measurementAsMm } from 'shared/utils.mjs'
-import { useEffect } from 'react'
-import get from 'lodash.get'
-
-export const fabricSettingsOrDefault = (units, ui, fabric) => {
- const isImperial = units === 'imperial'
- const sheetHeight = measurementAsMm(isImperial ? 36 : 100, units)
- const uiSettings = get(ui, ['cut', 'fabric', fabric], {})
- const sheetWidth = uiSettings.sheetWidth || measurementAsMm(isImperial ? 54 : 120, units)
- const grainDirection = uiSettings.grainDirection === undefined ? 90 : uiSettings.grainDirection
-
- return { activeFabric: fabric, sheetWidth, grainDirection, sheetHeight }
-}
-
-const activeFabricPath = ['_state', 'layout', 'forCutting', 'activeFabric']
-const useFabricSettings = (gist) => {
- const activeFabric = get(gist, activeFabricPath) || 'fabric'
- return fabricSettingsOrDefault(gist, activeFabric)
-}
-
-const useFabricDraft = (gist, design, fabricSettings) => {
- // get the appropriate layout for the view
- const layout =
- get(gist, ['layouts', gist._state.view, fabricSettings.activeFabric]) || gist.layout || true
- // hand it separately to the design
- const draft = new design({ ...gist, layout })
-
- const layoutSettings = {
- sheetWidth: fabricSettings.sheetWidth,
- sheetHeight: fabricSettings.sheetHeight,
- }
-
- let patternProps
- try {
- // add the fabric plugin to the draft
- draft.use(fabricPlugin(layoutSettings))
- // add the cutLayout plugin
- draft.use(cutLayoutPlugin(fabricSettings.activeFabric, fabricSettings.grainDirection))
- // also, pluginAnnotations and pluginFlip are needed
- draft.use(pluginAnnotations)
-
- // draft the pattern
- draft.draft()
- patternProps = draft.getRenderProps()
- } catch (err) {
- console.log(err, gist)
- }
-
- return { draft, patternProps }
-}
-
-const useFabricList = (draft) => {
- return draft.setStores[0].cutlist.getCutFabrics(draft.settings[0])
-}
-
-const bgProps = { fill: 'none' }
-export const CutLayout = (props) => {
- const { t } = useTranslation(['workbench', 'plugin'])
- const { gist, design, updateGist } = props
-
- // disable xray
- useEffect(() => {
- if (gist?._state?.xray?.enabled) updateGist(['_state', 'xray', 'enabled'], false)
- })
-
- const fabricSettings = useFabricSettings(gist)
- const { draft, patternProps } = useFabricDraft(gist, design, fabricSettings)
- const fabricList = useFabricList(draft)
-
- const setCutFabric = (newFabric) => {
- updateGist(activeFabricPath, newFabric)
- }
-
- let name = design.designConfig.data.name
- name = name.replace('@freesewing/', '')
-
- const settingsProps = {
- gist,
- updateGist,
- patternProps,
- unsetGist: props.unsetGist,
- ...fabricSettings,
- }
-
- return patternProps ? (
-
-
{t('layoutThing', { thing: name }) + ': ' + t('forCutting')}
-
-
- {fabricList.length > 1 ? (
-
- {fabricList.map((title) => (
-
- ))}
-
- ) : null}
- {/*
*/}
-
-
- ) : null
-}
diff --git a/sites/shared/components/workbench/layout/cut/settings.mjs b/sites/shared/components/workbench/layout/cut/settings.mjs
deleted file mode 100644
index 02543bebcea..00000000000
--- a/sites/shared/components/workbench/layout/cut/settings.mjs
+++ /dev/null
@@ -1,139 +0,0 @@
-import { ClearIcon, IconWrapper } from 'shared/components/icons.mjs'
-import { useTranslation } from 'next-i18next'
-import { formatFraction128, measurementAsMm, round, formatMm } from 'shared/utils.mjs'
-import { ShowButtonsToggle } from '../draft/buttons.mjs'
-
-const SheetIcon = (props) => (
-
-
-
-)
-
-const GrainIcon = (props) => (
-
-
-
-
-
-
-
-)
-const FabricSizer = ({ gist, updateGist, activeFabric, sheetWidth }) => {
- const { t } = useTranslation(['workbench'])
-
- let val = formatMm(sheetWidth, gist.units, 'none')
- // onChange
- const update = (evt) => {
- evt.stopPropagation()
- let evtVal = evt.target.value
- // set Val immediately so that the input reflects it
- val = evtVal
-
- let useVal = measurementAsMm(evtVal, gist.units)
- // only set to the gist if it's valid
- if (!isNaN(useVal)) {
- updateGist(['_state', 'layout', 'forCutting', 'fabric', activeFabric, 'sheetWidth'], useVal)
- }
- }
-
- return (
-
- )
-}
-export const GrainDirectionPicker = ({ grainDirection, activeFabric, updateGist }) => {
- const { t } = useTranslation(['workbench'])
-
- return (
-
- )
-}
-
-export const useFabricLength = (isImperial, height, format = 'none') => {
- // regular conversion from mm to inches or cm
- const unit = isImperial ? 25.4 : 10
- // conversion from inches or cm to yards or meters
- const fabricUnit = isImperial ? 36 : 100
- // for fabric, these divisions are granular enough
- const rounder = isImperial ? 16 : 10
-
- // we convert the used fabric height to the right units so we can round it
- const inFabricUnits = height / (fabricUnit * unit)
- // we multiply it by the rounder, round it up, then divide by the rounder again to get the rounded amount
- const roundCount = Math.ceil(rounder * inFabricUnits) / rounder
- // format as a fraction for imperial, a decimal for metric
- const count = isImperial ? formatFraction128(roundCount, format) : round(roundCount, 1)
-
- return `${count}${isImperial ? 'yds' : 'm'}`
-}
-
-export const CutLayoutSettings = ({
- gist,
- patternProps,
- unsetGist,
- updateGist,
- activeFabric,
- sheetWidth,
- grainDirection,
-}) => {
- const { t } = useTranslation(['workbench'])
-
- const fabricLength = useFabricLength(gist.units === 'imperial', patternProps.height)
-
- return (
-
-
-
-
-
-
-
- {fabricLength}
-
-
-
-
-
-
- )
-}
diff --git a/sites/shared/components/workbench/layout/print/orientation-picker.mjs b/sites/shared/components/workbench/layout/print/orientation-picker.mjs
deleted file mode 100644
index ba20d81a6e2..00000000000
--- a/sites/shared/components/workbench/layout/print/orientation-picker.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-import { PageIcon } from 'shared/components/icons.mjs'
-import { useTranslation } from 'next-i18next'
-
-export const PageOrientationPicker = ({ gist, updateGist }) => {
- const { t } = useTranslation(['workbench'])
-
- return (
-
- )
-}
diff --git a/sites/shared/components/workbench/layout/print/pagesize-picker.mjs b/sites/shared/components/workbench/layout/print/pagesize-picker.mjs
deleted file mode 100644
index 5f586e59ed9..00000000000
--- a/sites/shared/components/workbench/layout/print/pagesize-picker.mjs
+++ /dev/null
@@ -1,58 +0,0 @@
-import { PageSizeIcon } from 'shared/components/icons.mjs'
-import { useTranslation } from 'next-i18next'
-import { Popout } from 'shared/components/popout.mjs'
-
-const sizes = ['a4', 'a3', 'a2', 'a1', 'a0', 'letter', 'tabloid']
-
-export const PageSizePicker = ({ gist, updateGist }) => {
- const { t } = useTranslation(['workbench'])
- const setSize = (size) => {
- updateGist(['_state', 'layout', 'forPrinting', 'page', 'size'], size)
- if (!gist._state?.layout?.forPrinting?.page?.orientation) {
- updateGist(['_state', 'layout', 'forPrinting', 'page', 'orientation'], 'portrait')
- }
- }
-
- if (
- !gist._state?.layout?.forPrinting?.page?.size ||
- sizes.indexOf(gist._state.layout.forPrinting.page.size) === -1
- )
- return (
-
- {t('startBySelectingAThing', { thing: t('pageSize') })}
-
- {sizes.map((size) => (
-
- ))}
-
-
- )
-
- return (
-
-
-
-
{t(`pageSize`)}:
-
{gist._state.layout.forPrinting.page.size}
-
-
- {sizes.map((size) => (
- -
-
-
- ))}
-
-
- )
-}
diff --git a/sites/shared/components/workbench/layout/print/settings.mjs b/sites/shared/components/workbench/layout/print/settings.mjs
deleted file mode 100644
index beb60d37097..00000000000
--- a/sites/shared/components/workbench/layout/print/settings.mjs
+++ /dev/null
@@ -1,129 +0,0 @@
-import { PageSizePicker } from './pagesize-picker.mjs'
-import { PageOrientationPicker } from './orientation-picker.mjs'
-import { PrintIcon, RightIcon, ClearIcon, ExportIcon } from 'shared/components/icons.mjs'
-import { useTranslation } from 'next-i18next'
-import { ShowButtonsToggle } from '../draft/buttons.mjs'
-
-export const PrintLayoutSettings = (props) => {
- const { t } = useTranslation(['workbench'])
- let pages = props.draft?.setStores[0].get('pages')
- if (!pages) return null
- const { cols, rows, count } = pages
-
- const setMargin = (evt) => {
- props.updateGist(
- ['_state', 'layout', 'forPrinting', 'page', 'margin'],
- parseInt(evt.target.value)
- )
- }
-
- const setCoverPage = () => {
- props.updateGist(
- ['_state', 'layout', 'forPrinting', 'page', 'coverPage'],
- !props.layoutSettings.coverPage
- )
- }
-
- const setCutlist = () => {
- props.updateGist(
- ['_state', 'layout', 'forPrinting', 'page', 'cutlist'],
- !props.layoutSettings.cutlist
- )
- }
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{count}
-
|
-
-
{cols}
-
|
-
-
-
-
{rows}
-
-
-
- )
-}
diff --git a/sites/shared/components/workbench/menus/core-settings/config.mjs b/sites/shared/components/workbench/menus/core-settings/config.mjs
index b605586c821..4e5387ab160 100644
--- a/sites/shared/components/workbench/menus/core-settings/config.mjs
+++ b/sites/shared/components/workbench/menus/core-settings/config.mjs
@@ -71,7 +71,7 @@ export const loadSettingsConfig = ({
units: {
control: 1, // Show when control > 2
list: ['metric', 'imperial'],
- dflt: units,
+ dflt: 'metric',
choiceTitles: {
metric: 'metric',
imperial: 'imperial',
diff --git a/sites/shared/components/workbench/menus/shared/index.mjs b/sites/shared/components/workbench/menus/shared/index.mjs
index 74ec217ec4e..22b5cda31c3 100644
--- a/sites/shared/components/workbench/menus/shared/index.mjs
+++ b/sites/shared/components/workbench/menus/shared/index.mjs
@@ -1,7 +1,6 @@
import { useContext } from 'react'
import { MenuItemGroup } from './menu-item.mjs'
import { useTranslation } from 'next-i18next'
-import { HelpIcon } from 'shared/components/icons.mjs'
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
import { ModalContext } from 'shared/context/modal-context.mjs'
diff --git a/sites/shared/components/workbench/menus/shared/inputs.mjs b/sites/shared/components/workbench/menus/shared/inputs.mjs
index e80fd4edcc4..f32d174cbfa 100644
--- a/sites/shared/components/workbench/menus/shared/inputs.mjs
+++ b/sites/shared/components/workbench/menus/shared/inputs.mjs
@@ -85,9 +85,7 @@ export const ListToggle = ({ config, changed, updateFunc, name }) => {
return (
@@ -276,4 +274,25 @@ export const MmInput = (props) => {
}
/** A placeholder for an input to handle constant values */
-export const ConstantInput = () => FIXME: Constant options are not implemented (yet)
+export const ConstantInput = ({
+ type = 'number',
+ name,
+ current,
+ updateFunc,
+ t,
+ changed,
+ config,
+}) => (
+ <>
+ {t(`${name}.d`)}
+ updateFunc([name], evt.target.value)}
+ />
+ >
+)
diff --git a/sites/shared/components/workbench/menus/shared/menu-item.mjs b/sites/shared/components/workbench/menus/shared/menu-item.mjs
index d4fa9afe791..d2fbf7b152e 100644
--- a/sites/shared/components/workbench/menus/shared/menu-item.mjs
+++ b/sites/shared/components/workbench/menus/shared/menu-item.mjs
@@ -119,7 +119,7 @@ export const MenuItem = ({
className={open ? openButtonClass : 'btn btn-accent'}
onClick={(evt) => {
evt.stopPropagation()
- updateFunc(name)
+ updateFunc([name])
}}
>
diff --git a/sites/shared/components/workbench/pattern/movable/index.mjs b/sites/shared/components/workbench/pattern/movable/index.mjs
index da9f2db4b06..1a7024ad362 100644
--- a/sites/shared/components/workbench/pattern/movable/index.mjs
+++ b/sites/shared/components/workbench/pattern/movable/index.mjs
@@ -1,20 +1,11 @@
import { useRef } from 'react'
-import { Stack } from './stack.mjs'
-// import { SvgWrapper } from '../../pattern/svg.mjs'
-// import { PartInner } from '../../pattern/part.mjs'
import { PanZoomPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
import { MovableStack } from './stack.mjs'
-import get from 'lodash.get'
export const MovablePattern = ({
- design,
- pattern,
renderProps,
- patternConfig,
- settings,
showButtons = true,
update,
- bgProps = {},
fitImmovable = false,
immovable = [],
layoutPath,
@@ -68,10 +59,6 @@ export const MovablePattern = ({
}
}
- const viewBox = layout.topLeft
- ? `${layout.topLeft.x} ${layout.topLeft.y} ${layout.width} ${layout.height}`
- : false
-
const sortedStacks = {}
Object.keys(renderProps.stacks)
.sort((a, b) => {
@@ -90,13 +77,13 @@ export const MovablePattern = ({
{...{
stackName,
stack,
- settings,
components,
t,
movable: !immovable.includes(stackName),
layout: layout.stacks[stackName],
updateLayout,
showButtons,
+ settings,
}}
/>
)
diff --git a/sites/shared/components/workbench/pattern/movable/stack.mjs b/sites/shared/components/workbench/pattern/movable/stack.mjs
index 3da5d3f856f..17f5618adb5 100644
--- a/sites/shared/components/workbench/pattern/movable/stack.mjs
+++ b/sites/shared/components/workbench/pattern/movable/stack.mjs
@@ -43,25 +43,23 @@
* I've sort of left it at this because I'm starting to wonder if we should perhaps re-think
* how custom layouts are supported in the core. And I would like to discuss this with the core team.
*/
-import { useRef, useState, useEffect } from 'react'
+import { useRef, useState, useEffect, useCallback } from 'react'
import { generateStackTransform, getTransformedBounds } from '@freesewing/core'
-import { Stack } from 'pkgs/react-components/src/pattern/stack.mjs'
import { getProps, angle } from 'pkgs/react-components/src/pattern/utils.mjs'
import { drag } from 'd3-drag'
import { select } from 'd3-selection'
import { Buttons } from './transform-buttons.mjs'
-import get from 'lodash.get'
export const MovableStack = ({
stackName,
stack,
- settings,
components,
t,
movable = true,
layout,
updateLayout,
showButtons,
+ settings,
}) => {
const stackExists = !movable || typeof layout?.move?.x !== 'undefined'
@@ -72,100 +70,80 @@ export const MovableStack = ({
// State variable to switch between moving or rotating the part
const [rotate, setRotate] = useState(false)
- // update the layout on mount
- useEffect(() => {
- // only update if there's a rendered part and it's not the pages or fabric part
- if (stackRef.current && movable) {
- updateStacklayout(false)
- }
- }, [stackRef, layout])
-
- // Initialize drag handler
- useEffect(() => {
- // don't drag the pages
- if (!movable || !stackExists) return
- handleDrag(select(stackRef.current))
- }, [rotate, stackRef, layout])
-
- // // Don't just assume this makes sense
- if (!stackExists) return null
-
- if (!movable) return
-
- // These are kept as vars because re-rendering on drag would kill performance
+ // This is kept as state to avoid re-rendering on drag, which would kill performance
+ // It's a bit of an anti-pattern, but we'll directly manipulate the properties instead of updating the state
// Managing the difference between re-render and direct DOM updates makes this
// whole thing a bit tricky to wrap your head around
- let translateX = layout.move.x
- let translateY = layout.move.y
- let stackRotation = layout.rotate || 0
- let rotation = stackRotation
- let flipX = !!layout.flipX
- let flipY = !!layout.flipY
+ const stackRotation = layout?.rotate || 0
+ const [liveTransforms] = useState({
+ translateX: layout?.move.x,
+ translateY: layout?.move.y,
+ rotation: stackRotation,
+ flipX: !!layout?.flipX,
+ flipY: !!layout?.flipY,
+ })
- const center = {
+ const center = stack.topLeft && {
x: stack.topLeft.x + (stack.bottomRight.x - stack.topLeft.x) / 2,
y: stack.topLeft.y + (stack.bottomRight.y - stack.topLeft.y) / 2,
}
- /** get the delta rotation from the start of the drag event to now */
- const getRotation = (event) =>
- angle(center, event.subject) - angle(center, { x: event.x, y: event.y })
-
- const setTransforms = () => {
+ const setTransforms = useCallback(() => {
// get the transform attributes
+ const { translateX, translateY, rotation, flipX, flipY } = liveTransforms
const transforms = generateStackTransform(translateX, translateY, rotation, flipX, flipY, stack)
const me = select(stackRef.current)
me.attr('transform', transforms.join(' '))
return transforms
- }
+ }, [liveTransforms, stackRef, stack])
- let didDrag = false
- const handleDrag = drag()
- // subject allows us to save data from the start of the event to use throughout event handing
- .subject(function (event) {
- return rotate
- ? // if we're rotating, the subject is the mouse position
- { x: event.x, y: event.y }
- : // if we're moving, the subject is the part's x,y coordinates
- { x: translateX, y: translateY }
- })
- .on('drag', function (event) {
- if (!event.dx && !event.dy) return
+ /** update the layout either locally or in the gist */
+ const updateStacklayout = useCallback(
+ (history = true) => {
+ /** don't mess with what we don't lay out */
+ if (!stackRef.current || !movable) return
- if (rotate) {
- let newRotation = getRotation(event)
- // shift key to snap the rotation
- if (event.sourceEvent.shiftKey) {
- newRotation = Math.ceil(newRotation / 15) * 15
- }
- // reverse the rotation direction one time per flip. if we're flipped both directions, rotation will be positive again
- if (flipX) newRotation *= -1
- if (flipY) newRotation *= -1
+ // set the transforms on the stack in order to calculate from the latest position
+ const transforms = setTransforms()
- rotation = stackRotation + newRotation
- } else {
- translateX = event.x
- translateY = event.y
- }
+ // apply the transforms to the bounding box to get the new extents of the stack
+ const { tl, br } = getTransformedBounds(stack, transforms)
- // a drag happened, so we should update the layout when we're done
- didDrag = true
- setTransforms()
- })
- .on('end', function () {
- // save to gist if anything actually changed
- if (didDrag) updateStacklayout()
+ // update it on the draft component
+ updateLayout(
+ stackName,
+ {
+ move: {
+ x: liveTransforms.translateX,
+ y: liveTransforms.translateY,
+ },
+ rotate: liveTransforms.rotation % 360,
+ flipX: liveTransforms.flipX,
+ flipY: liveTransforms.flipY,
+ tl,
+ br,
+ },
+ history
+ )
+ },
+ [stackRef, setTransforms, updateLayout, liveTransforms, movable, stack, stackName]
+ )
- didDrag = false
- })
+ // update the layout on mount
+ useEffect(() => {
+ // only update if there's a rendered part and it's not an imovable part
+ if (stackRef.current && movable) {
+ updateStacklayout(false)
+ }
+ }, [stackRef, movable, updateStacklayout])
/** reset the part's transforms */
const resetPart = () => {
- rotation = 0
- flipX = 0
- flipY = 0
+ liveTransforms.rotation = 0
+ liveTransforms.flipX = 0
+ liveTransforms.flipY = 0
updateStacklayout()
}
@@ -179,58 +157,85 @@ export const MovableStack = ({
setRotate(!rotate)
}
- /** update the layout either locally or in the gist */
- const updateStacklayout = (history = true) => {
- /** don't mess with what we don't lay out */
- if (!stackRef.current || !movable) return
-
- // set the transforms on the stack in order to calculate from the latest position
- const transforms = setTransforms()
-
- // apply the transforms to the bounding box to get the new extents of the stack
- const { tl, br } = getTransformedBounds(stack, transforms)
-
- // update it on the draft component
- updateLayout(
- stackName,
- {
- move: {
- x: translateX,
- y: translateY,
- },
- rotate: rotation % 360,
- flipX,
- flipY,
- tl,
- br,
- },
- history
- )
- }
-
/** Method to flip (mirror) the part along the X or Y axis */
const flip = (axis) => {
- if (axis === 'x') flipX = !flipX
- else flipY = !flipY
+ if (axis === 'x') liveTransforms.flipX = !liveTransforms.flipX
+ else liveTransforms.flipY = !liveTransforms.flipY
updateStacklayout()
}
/** method to rotate 90 degrees */
const rotate90 = (direction = 1) => {
- if (flipX) direction *= -1
- if (flipY) direction *= -1
+ if (liveTransforms.flipX) direction *= -1
+ if (liveTransforms.flipY) direction *= -1
- rotation += 90 * direction
+ liveTransforms.rotation += 90 * direction
updateStacklayout()
}
+ /** get the delta rotation from the start of the drag event to now */
+ const getRotation = (event) =>
+ angle(center, event.subject) - angle(center, { x: event.x, y: event.y })
+
+ let didDrag = false
+ const handleDrag =
+ movable &&
+ drag()
+ // subject allows us to save data from the start of the event to use throughout event handing
+ .subject(function (event) {
+ return rotate
+ ? // if we're rotating, the subject is the mouse position
+ { x: event.x, y: event.y }
+ : // if we're moving, the subject is the part's x,y coordinates
+ { x: liveTransforms.translateX, y: liveTransforms.translateY }
+ })
+ .on('drag', function (event) {
+ if (!event.dx && !event.dy) return
+
+ if (rotate) {
+ let newRotation = getRotation(event)
+ // shift key to snap the rotation
+ if (event.sourceEvent.shiftKey) {
+ newRotation = Math.ceil(newRotation / 15) * 15
+ }
+ // reverse the rotation direction one time per flip. if we're flipped both directions, rotation will be positive again
+ if (liveTransforms.flipX) newRotation *= -1
+ if (liveTransforms.flipY) newRotation *= -1
+
+ liveTransforms.rotation = stackRotation + newRotation
+ } else {
+ liveTransforms.translateX = event.x
+ liveTransforms.translateY = event.y
+ }
+
+ // a drag happened, so we should update the layout when we're done
+ didDrag = true
+ setTransforms()
+ })
+ .on('end', function () {
+ // save to gist if anything actually changed
+ if (didDrag) updateStacklayout()
+
+ didDrag = false
+ })
+
+ // Initialize drag handler
+ useEffect(() => {
+ // don't drag the pages
+ if (!movable || !stackExists) return
+ handleDrag(select(stackRef.current))
+ }, [stackRef, movable, stackExists, handleDrag])
+
+ // // Don't just assume this makes sense
+ if (!stackExists) return null
+
const { Group, Part } = components
return (
{[...stack.parts].map((part, key) => (
-
+
))}
{movable && (
@@ -246,9 +251,11 @@ export const MovableStack = ({
/>
{showButtons ? (
(
{
)
}
-export const ShowButtonsToggle = ({ gist, layoutSetType, updateGist }) => {
+export const ShowButtonsToggle = ({ ui, update }) => {
const { t } = useTranslation('workbench')
- const path = ['_state', 'layout', layoutSetType, 'showButtons']
- const showButtons = get(gist, path, true)
- const setShowButtons = () => updateGist(path, !showButtons)
-
+ const hideButtons = (evt) => {
+ update.ui('hideMovableButtons', !evt.target.checked)
+ }
return (
-