diff --git a/sites/shared/components/workbench/draft/stack.js b/sites/shared/components/workbench/draft/stack.js index 95aeea434e2..5cbea16ff24 100644 --- a/sites/shared/components/workbench/draft/stack.js +++ b/sites/shared/components/workbench/draft/stack.js @@ -1,13 +1,14 @@ import Part from './part' import { getProps } from './utils' -const Stack = props => { - const { stack, gist, app, updateGist, unsetGist, showInfo } = props +const Stack = (props) => { + const { stack, gist, updateGist, unsetGist, showInfo } = props return ( {[...stack.parts].map((part) => ( - { - const { patternProps=false, gist, app, updateGist, unsetGist, showInfo } = props +const SvgWrapper = forwardRef((props, ref) => { + const { patternProps = false, gist, updateGist, unsetGist, showInfo, viewBox } = props if (!patternProps) return null + console.log(props.children) - return {({ size }) => ( - - -
- - - - - {Object.keys(patternProps.stacks).map((stackName) => ( - - ))} - - -
-
-
- )}
-} + return ( + + {({ size }) => ( + + +
+ + + + + {props.children || + Object.keys(patternProps.stacks).map((stackName) => ( + + ))} + + +
+
+
+ )} +
+ ) +}) export default SvgWrapper - diff --git a/sites/shared/components/workbench/layout/draft/index.js b/sites/shared/components/workbench/layout/draft/index.js index 288a1798ed2..75c0f0b7f79 100644 --- a/sites/shared/components/workbench/layout/draft/index.js +++ b/sites/shared/components/workbench/layout/draft/index.js @@ -1,35 +1,43 @@ -import { useRef} from 'react' +import { useRef } from 'react' import Svg from '../../draft/svg' import Defs from '../../draft/defs' -import Part from './part' -import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch" +import Stack from './part' +import SvgWrapper from '../../draft/svg-wrapper' +import { getProps } from '../../draft/utils' -const Draft = props => { - const { draft, patternProps, gist, updateGist, app, bgProps={}, fitLayoutPart = false, layoutType="printingLayout"} = props +const Draft = (props) => { + const { + draft, + patternProps, + gist, + updateGist, + app, + bgProps = {}, + fitLayoutPart = false, + layoutType = 'printingLayout', + } = props - const svgRef = useRef(null); + const svgRef = useRef(null) if (!patternProps) return null // keep a fresh copy of the layout because we might manipulate it without saving to the gist - let layout = draft.settings.layout === true ? { - ...patternProps.autoLayout, - width: patternProps.width, - height: patternProps.height - } : {...draft.settings.layout} - - + let layout = draft.settings[0].layouts?.printingLayout || { + ...patternProps.autoLayout, + width: patternProps.width, + height: patternProps.height, + } // Helper method to update part layout and re-calculate width * height - const updateLayout = (name, config, history=true) => { + const updateLayout = (name, config, history = true) => { // Start creating new layout - const newLayout = {...layout} - newLayout.parts[name] = config + const newLayout = { ...layout } + newLayout.stacks[name] = config // Pattern topLeft and bottomRight - let topLeft = {x: 0, y: 0} - let bottomRight = {x: 0, y: 0} - for (const [pname, part] of Object.entries(patternProps.parts)) { + let topLeft = { x: 0, y: 0 } + let bottomRight = { x: 0, y: 0 } + for (const [pname, part] of Object.entries(patternProps.stacks)) { if (pname == props.layoutPart && !fitLayoutPart) continue - let partLayout = newLayout.parts[pname]; + let partLayout = newLayout.stacks[pname] // Pages part does not have its topLeft and bottomRight set by core since it's added post-draft if (partLayout?.tl) { @@ -37,7 +45,7 @@ const Draft = props => { topLeft.x = Math.min(topLeft.x, partLayout.tl.x) topLeft.y = Math.min(topLeft.y, partLayout.tl.y) bottomRight.x = Math.max(bottomRight.x, partLayout.br.x) - bottomRight.y = Math.max(bottomRight.y, partLayout.br.y); + bottomRight.y = Math.max(bottomRight.y, partLayout.br.y) } } @@ -54,52 +62,41 @@ const Draft = props => { } } - // We need to make sure the `pages` part is at the bottom of the pile // so we can drag-drop all parts on top of it. // Bottom in SVG means we need to draw it first - const partList = Object.keys(patternProps.parts) + const viewBox = layout.topLeft + ? `${layout.topLeft.x} ${layout.topLeft.y} ${layout.width} ${layout.height}` + : false + + const stacks = [] + for (var stackName in patternProps.stacks) { + let stack = patternProps.stacks[stackName] + + const stackPart = ( + + ) + + stacks[stack === props.layoutPart ? 'unshift' : 'push'](stackPart) + } return ( -
- - - - - - - - {[ - partList.filter(name => name === props.layoutPart), - partList.filter(name => name !== props.layoutPart), - ].map(list => list.map(name => ( - - )))} - - - - -
+ + + {stacks} + ) } export default Draft - diff --git a/sites/shared/components/workbench/layout/draft/part.js b/sites/shared/components/workbench/layout/draft/part.js index cb88084ae19..fbcf53149b8 100644 --- a/sites/shared/components/workbench/layout/draft/part.js +++ b/sites/shared/components/workbench/layout/draft/part.js @@ -43,25 +43,24 @@ * 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 {PartInner} from '../../draft/part' -import { generatePartTransform } from '@freesewing/core' +import Part from '../../draft/part' +import { generateStackTransform } from '@freesewing/core' import { getProps, angle } from '../../draft/utils' import { drag } from 'd3-drag' import { select } from 'd3-selection' -import { useRef, useState, useEffect} from 'react' +import { useRef, useState, useEffect } from 'react' import Buttons from './buttons' +const Stack = (props) => { + const { layout, stack, stackName, gist } = props -const Part = props => { - const { layout, part, partName} = props + const stackLayout = layout.stacks?.[stackName] - const partLayout = layout.parts?.[partName] - - // Don't just assume this makes sense - if (typeof partLayout?.move?.x === 'undefined') return null + // // Don't just assume this makes sense + if (typeof stackLayout?.move?.x === 'undefined') return null // Use a ref for direct DOM manipulation - const partRef = useRef(null) + const stackRef = useRef(null) const centerRef = useRef(null) const innerRef = useRef(null) @@ -71,81 +70,81 @@ const Part = props => { // update the layout on mount useEffect(() => { // only update if there's a rendered part and it's not the pages or fabric part - if (partRef.current && !props.isLayoutPart) { + if (stackRef.current && !props.isLayoutPart) { updateLayout(false) } - }, [partRef, partLayout]) + }, [stackRef, stackLayout]) // Initialize drag handler useEffect(() => { // don't drag the pages if (props.isLayoutPart) return - handleDrag(select(partRef.current)) - }, [rotate, partRef, partLayout]) + handleDrag(select(stackRef.current)) + }, [rotate, stackRef, stackLayout]) // These are kept as vars because re-rendering on drag would kill performance // Managing the difference between re-render and direct DOM updates makes this // whole thing a bit tricky to wrap your head around - let translateX = partLayout.move.x - let translateY = partLayout.move.y - let partRotation = partLayout.rotate || 0; - let rotation = partRotation; - let flipX = !!partLayout.flipX - let flipY = !!partLayout.flipY + let translateX = stackLayout.move.x + let translateY = stackLayout.move.y + let stackRotation = stackLayout.rotate || 0 + let rotation = stackRotation + let flipX = !!stackLayout.flipX + let flipY = !!stackLayout.flipY const center = { - x: part.topLeft.x + (part.bottomRight.x - part.topLeft.x)/2, - y: part.topLeft.y + (part.bottomRight.y - part.topLeft.y)/2, + 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 getRotation = (event) => + angle(center, event.subject) - angle(center, { x: event.x, y: event.y }) const setTransforms = () => { // get the transform attributes - const transforms = generatePartTransform(translateX, translateY, rotation, flipX, flipY, part); + const transforms = generateStackTransform(translateX, translateY, rotation, flipX, flipY, stack) - const me = select(partRef.current); + const me = select(stackRef.current) for (var t in transforms) { me.attr(t, transforms[t]) } } - let didDrag = false; + 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} + .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) { + .on('drag', function (event) { if (!event.dx && !event.dy) return if (rotate) { - let newRotation = getRotation(event); + let newRotation = getRotation(event) // shift key to snap the rotation if (event.sourceEvent.shiftKey) { - newRotation = Math.ceil(newRotation/15) * 15 + 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 - rotation = partRotation + newRotation - } - else { + rotation = stackRotation + newRotation + } else { translateX = event.x translateY = event.y } // a drag happened, so we should update the layout when we're done - didDrag = true; + didDrag = true setTransforms() }) - .on('end', function(event) { + .on('end', function (event) { // save to gist if anything actually changed if (didDrag) updateLayout() @@ -163,49 +162,55 @@ const Part = props => { /** toggle between dragging and rotating */ const toggleDragRotate = () => { // only respond if the part should be able to drag/rotate - if (!partRef.current || props.isLayoutPart) {return} + if (!stackRef.current || props.isLayoutPart) { + return + } setRotate(!rotate) } /** update the layout either locally or in the gist */ - const updateLayout = (history=true) => { + const updateLayout = (history = true) => { /** don't mess with what we don't lay out */ - if (!partRef.current || props.isLayoutPart) return + if (!stackRef.current || props.isLayoutPart) return // set the transforms on the part in order to calculate from the latest position setTransforms() // get the bounding box and the svg's current transform matrix - const partRect = innerRef.current.getBoundingClientRect(); - const matrix = innerRef.current.ownerSVGElement.getScreenCTM().inverse(); + const stackRect = innerRef.current.getBoundingClientRect() + const matrix = innerRef.current.ownerSVGElement.getScreenCTM().inverse() // a function to convert dom space to svg space const domToSvg = (point) => { - const {x, y} = DOMPointReadOnly.fromPoint(point).matrixTransform(matrix) - return {x, y} + const { x, y } = DOMPointReadOnly.fromPoint(point).matrixTransform(matrix) + return { x, y } } // include the new top left and bottom right to ease calculating the pattern width and height - const tl = domToSvg({x: partRect.left, y: partRect.top}); - const br = domToSvg({x: partRect.right, y: props.isLayoutPart ? 0 : partRect.bottom}); + const tl = domToSvg({ x: stackRect.left, y: stackRect.top }) + const br = domToSvg({ x: stackRect.right, y: props.isLayoutPart ? 0 : stackRect.bottom }) // update it on the draft component - props.updateLayout(partName, { - move: { - x: translateX, - y: translateY, + props.updateLayout( + stackName, + { + move: { + x: translateX, + y: translateY, + }, + rotate: rotation % 360, + flipX, + flipY, + tl, + br, }, - rotate: rotation % 360, - flipX, - flipY, - tl, - br - }, history) + history + ) } /** Method to flip (mirror) the part along the X or Y axis */ - const flip = axis => { + const flip = (axis) => { if (axis === 'x') flipX = !flipX else flipY = !flipY updateLayout() @@ -222,39 +227,42 @@ const Part = props => { } // don't render if the part is empty - if (Object.keys(part.snippets).length === 0 && Object.keys(part.paths).length === 0) return null; + // if (Object.keys(part.snippets).length === 0 && Object.keys(part.paths).length === 0) return null; return ( - - - {!props.isLayoutPart && <> - - - - } + + + {stack.parts.map((part) => ( + + ))} + + {!props.isLayoutPart && ( + <> + + + + + )} ) } -export default Part +export default Stack diff --git a/sites/shared/components/workbench/layout/print/index.js b/sites/shared/components/workbench/layout/print/index.js index 49cfaba67b0..6d4070a5512 100644 --- a/sites/shared/components/workbench/layout/print/index.js +++ b/sites/shared/components/workbench/layout/print/index.js @@ -2,17 +2,17 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'next-i18next' import Settings from './settings' import Draft from '../draft/index' -import {pagesPlugin} from './plugin' -import {handleExport, defaultPdfSettings} from 'shared/components/workbench/exporting/export-handler' +import { pagesPlugin } from './plugin' +import { + handleExport, + defaultPdfSettings, +} from 'shared/components/workbench/exporting/export-handler' import Popout from 'shared/components/popout' -const PrintLayout = props => { +const PrintLayout = (props) => { // disable xray useEffect(() => { - if (props.gist?._state?.xray?.enabled) props.updateGist( - ['_state', 'xray', 'enabled'], - false - ) + if (props.gist?._state?.xray?.enabled) props.updateGist(['_state', 'xray', 'enabled'], false) }, []) const { t } = useTranslation(['workbench']) @@ -23,45 +23,44 @@ const PrintLayout = props => { // add the pages plugin to the draft const layoutSettings = { ...defaultPdfSettings, - ...props.gist?._state?.layout?.forPrinting?.page + ...props.gist?._state?.layout?.forPrinting?.page, } - draft.use(pagesPlugin( - layoutSettings - )) + draft.use(pagesPlugin(layoutSettings)) let patternProps try { // draft the pattern draft.draft() patternProps = draft.getRenderProps() - } catch(err) { + console.log(patternProps) + } catch (err) { console.log(err, props.gist) } - const bgProps = { fill: "url(#page)" } + const bgProps = { fill: 'url(#page)' } const exportIt = () => { setError(false) - handleExport('pdf', props.gist, props.design, t, props.app, (e) => setError(false), (e) => setError(true)) + handleExport( + 'pdf', + props.gist, + props.design, + t, + props.app, + (e) => setError(false), + (e) => setError(true) + ) } let name = props.design.designConfig.data.name name = name.replace('@freesewing/', '') return (
-

- { - t('layoutThing', { thing: name }) - + ': ' - + t('forPrinting') - } -

+

{t('layoutThing', { thing: name }) + ': ' + t('forPrinting')}

- + {error && ( - - {t('error')}: - + {t('error')}: {t('somethingWentWrong')} )} diff --git a/sites/shared/components/workbench/layout/print/plugin.js b/sites/shared/components/workbench/layout/print/plugin.js index 6355498c9db..b513b7c7a4e 100644 --- a/sites/shared/components/workbench/layout/print/plugin.js +++ b/sites/shared/components/workbench/layout/print/plugin.js @@ -1,13 +1,13 @@ const name = 'Pages Plugin' const version = '1.0.0' export const sizes = { - a4: [ 210, 297 ], - a3: [ 297, 420 ], - a2: [ 420, 594 ], - a1: [ 594, 841 ], - a0: [ 841, 1188 ], - letter: [ 215.9, 279.4 ], - tabloid: [ 279.4, 431.8 ], + a4: [210, 297], + a3: [297, 420], + a2: [420, 594], + a1: [594, 841], + a0: [841, 1188], + letter: [215.9, 279.4], + tabloid: [279.4, 431.8], } /** get a letter to represent an index less than 26*/ @@ -19,32 +19,35 @@ const indexStr = (i) => { let quotient = i / 26 let result - if (i <= 26) {return indexLetter(i)} //Number is within single digit bounds of our encoding letter alphabet + if (i <= 26) { + return indexLetter(i) + } //Number is within single digit bounds of our encoding letter alphabet if (quotient >= 1) { - //This number was bigger than the alphabet, recursively perform this function until we're done - if (index === 0) {quotient--} //Accounts for the edge case of the last letter in the dictionary string - result = indexStr(quotient) + //This number was bigger than the alphabet, recursively perform this function until we're done + if (index === 0) { + quotient-- + } //Accounts for the edge case of the last letter in the dictionary string + result = indexStr(quotient) } - if (index === 0) {index = 26} //Accounts for the edge case of the final letter; avoids getting an empty string + if (index === 0) { + index = 26 + } //Accounts for the edge case of the final letter; avoids getting an empty string return result + indexLetter(index) } /** * A plugin to add printer pages * */ -export const pagesPlugin = ({ - size='a4', - ...settings -}) => { +export const pagesPlugin = ({ size = 'a4', ...settings }) => { const ls = settings.orientation === 'landscape' let sheetHeight = sizes[size][ls ? 0 : 1] let sheetWidth = sizes[size][ls ? 1 : 0] sheetWidth -= settings.margin * 2 sheetHeight -= settings.margin * 2 - return basePlugin({...settings, sheetWidth, sheetHeight}) + return basePlugin({ ...settings, sheetWidth, sheetHeight }) } const doScanForBlanks = (parts, layout, x, y, w, h) => { @@ -56,10 +59,10 @@ const doScanForBlanks = (parts, layout, x, y, w, h) => { // get the position of the part let partLayout = layout.parts[p] - let partMinX = (partLayout.tl?.x || (partLayout.move.x + part.topLeft.x)) - let partMinY = (partLayout.tl?.y || (partLayout.move.y + part.topLeft.y)) - let partMaxX = (partLayout.br?.x || (partMinX + part.width)) - let partMaxY = (partLayout.br?.y || (partMinY + part.height)) + let partMinX = partLayout.tl?.x || partLayout.move.x + part.topLeft.x + let partMinY = partLayout.tl?.y || partLayout.move.y + part.topLeft.y + let partMaxX = partLayout.br?.x || partMinX + part.width + let partMaxY = partLayout.br?.y || partMinY + part.height // check if the part overlaps the page extents if ( @@ -71,16 +74,17 @@ const doScanForBlanks = (parts, layout, x, y, w, h) => { partMaxX > x && // and the bottom of the part is below the top to the page partMaxY > y - ) { + ) { // the part has content inside the page - hasContent = true; + hasContent = true // so we stop looking - break; + break } } return hasContent } + /** * The base plugin for adding a layout helper part like pages or fabric * sheetWidth: the width of the helper part @@ -92,217 +96,242 @@ const doScanForBlanks = (parts, layout, x, y, w, h) => { const basePlugin = ({ sheetWidth, sheetHeight, - boundary=false, - partName="pages", - responsiveColumns=true, - printStyle=false, - scanForBlanks=true, - renderBlanks=true, - setPatternSize=false + boundary = false, + partName = 'pages', + responsiveColumns = true, + printStyle = false, + scanForBlanks = true, + renderBlanks = true, + setPatternSize = false, }) => ({ name, version, hooks: { - postLayout: function(pattern) { - // Add part - pattern.parts[partName] = pattern.Part(partName) - // Keep part out of layout - pattern.parts[partName].layout = false + preDraft: function (pattern) { // But add the part to the autoLayout property - pattern.autoLayout.parts[partName] = { - move: { x: 0, y: 0 } - } + // pattern.autoLayout.parts[partName] = { + // move: { x: 0, y: 0 } + // } // TODO migrate this to v3 parts adding // Add pages - const { macro } = pattern.parts[partName].shorthand() + // const { macro } = pattern.config.parts[partName].shorthand() let { height, width } = pattern - if (!responsiveColumns) width = sheetWidth; + if (!responsiveColumns) width = sheetWidth if (pattern.settings.layout?.topLeft) { height += pattern.settings.layout.topLeft.y responsiveColumns && (width += pattern.settings.layout.topLeft.x) } - const layout = typeof pattern.settings.layout === 'object' ? pattern.settings.layout : pattern.autoLayout + // Add part + pattern.addPart({ + name: partName, + layout: false, + draft: (shorthand) => { + pluginMacros.addPages( + { size: [sheetHeight, sheetWidth], height, width, layout }, + shorthand + ) + return shorthand.part + }, + }) + pattern.getConfig() + console.log(pattern.config, pattern.designConfig) - macro('addPages', { size: [sheetHeight,sheetWidth, ], height, width, layout }) + const layout = + typeof pattern.settings.layout === 'object' ? pattern.settings.layout : pattern.autoLayout - if (boundary) pattern.parts[partName].boundary(); + // macro('addPages', { size: [sheetHeight,sheetWidth, ], height, width, layout }) + + if (boundary) pattern.parts[partName].boundary() if (setPatternSize) { pattern.width = sheetWidth * pattern.parts[partName].pages.cols pattern.height = sheetHeight * pattern.parts[partName].pages.rows } - } + }, }, - macros: { - /** draft the pages */ - addPages: function(so) { - const [h,w] = so.size - const cols = Math.ceil(so.width / w) - const rows = Math.ceil(so.height / h) - const { points, Point, paths, Path, macro } = this.shorthand() - let count = 0 - let withContent = {} - // get the layout from the pattern - const {layout} = so; - for (let row=0;row