diff --git a/sites/shared/components/workbench/draft/utils.js b/sites/shared/components/workbench/draft/utils.js index f023e26b58b..6e439318c01 100644 --- a/sites/shared/components/workbench/draft/utils.js +++ b/sites/shared/components/workbench/draft/utils.js @@ -34,3 +34,13 @@ export const getProps = (obj) => { return props } + +export const dx = (pointA, pointB) => pointB.x - pointA.x +export const dy = (pointA, pointB) => pointB.y - pointA.y +export const rad2deg = radians => radians * 57.29577951308232 +export const angle = (pointA, pointB) => { + let rad = Math.atan2(-1 * dy(pointA, pointB), dx(pointA, pointB)) + while (rad < 0) rad += 2 * Math.PI + + return rad2deg(rad) +} diff --git a/sites/shared/components/workbench/layout/draft.js b/sites/shared/components/workbench/layout/draft/index.js similarity index 55% rename from sites/shared/components/workbench/layout/draft.js rename to sites/shared/components/workbench/layout/draft/index.js index c67232ab5a4..2c2df9d5909 100644 --- a/sites/shared/components/workbench/layout/draft.js +++ b/sites/shared/components/workbench/layout/draft/index.js @@ -73,197 +73,10 @@ * how custom layouts are supported in the core. And I would like to discuss this with the core team. */ import { useEffect, useRef, useState } from 'react' -import Svg from '../draft/svg' -import Defs from '../draft/defs' -import Path from '../draft/path' -import Point from '../draft/point' -import Snippet from '../draft/snippet' -import {PartInner} from '../draft/part' -import { getProps } from '../draft/utils' -import { drag } from 'd3-drag' -import { select } from 'd3-selection' - -const Buttons = ({ transform, flip, rotate, setRotate, resetPart }) => { - const letter = 'F' - const style = { style: {fill: 'white', fontSize: 18, fontWeight: 'bold', textAnchor: 'middle'} } - - return ( - - {rotate - ? - : - } - - - {letter} - - flip('y')}> - - {letter} - - flip('x')}> - - {letter} - - - ) -} - -const dx = (pointA, pointB) => pointB.x - pointA.x -const dy = (pointA, pointB) => pointB.y - pointA.y -const rad2deg = radians => radians * 57.29577951308232 -const angle = (pointA, pointB) => { - let rad = Math.atan2(-1 * dy(pointA, pointB), dx(pointA, pointB)) - while (rad < 0) rad += 2 * Math.PI - - return rad2deg(rad) -} - -const generateTransform = (x, y, rot, flipX, flipY, part) => { - const center = { - x: part.topLeft.x + (part.bottomRight.x - part.topLeft.x)/2, - y: part.topLeft.y + (part.bottomRight.y - part.topLeft.y)/2, - } - const dx = part.topLeft.x - center.x - const dy = part.topLeft.y - center.y - const transforms = [`translate(${x},${y})`] - if (flipX) transforms.push( - `translate(${center.x * -1}, ${center.y * -1})`, - 'scale(-1, 1)', - `translate(${center.x * -1 + 2 * dx}, ${center.y})` - ) - if (flipY) transforms.push( - `translate(${center.x * -1}, ${center.y * -1})`, - 'scale(1, -1)', - `translate(${center.x}, ${center.y * -1 + 2 * dy})`, - ) - if (rot) transforms.push( - `rotate(${rot}, ${center.x}, ${center.y})` - ) - - return transforms.join(' ') -} - -const Part = props => { - const { layout, gist, name, part} = props - - const partLayout = layout.parts[name] - - // Don't just assume this makes sense - if (typeof layout.parts?.[name]?.move?.x === 'undefined') return null - - // Use a ref for direct DOM manipulation - const partRef = useRef(null) - const centerRef = useRef(null) - - // State variable to switch between moving or rotating the part - const [rotate, setRotate] = useState(false) - - // Initialize drag handler - useEffect(() => { - handleDrag(select(partRef.current)) - }, [rotate, layout]) - - // 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 ? true : false - let flipY = partLayout.flipY ? true : false - let partRect - - const center = { - x: part.topLeft.x + (part.bottomRight.x - part.topLeft.x)/2, - y: part.topLeft.y + (part.bottomRight.y - part.topLeft.y)/2, - } - - const getRotation = (event) => angle(center, event.subject) - angle(center, { x:event.x, y: event.y }); - - const handleDrag = drag() - .subject(function(event) { - return rotate ? { x: event.x, y: event.y } : {x: translateX, y: translateY} - }) - .on('start', function(event) { - partRect = partRef.current.getBoundingClientRect() - }) - .on('drag', function(event) { - if (rotate) { - let newRotation = getRotation(event); - if (flipX) newRotation *= -1 - if (flipY) newRotation *= -1 - rotation = partRotation + newRotation - } - else { - translateX = event.x - translateY = event.y - } - const me = select(this); - me.attr('transform', generateTransform(translateX, translateY, rotation, flipX, flipY, part)); - }) - .on('end', function(event) { - updateLayout() - }) - - const resetPart = () => { - rotation = 0 - flipX = 0 - flipY = 0 - updateLayout() - } - const toggleDragRotate = () => { - updateLayout() - setRotate(!rotate) - } - const updateLayout = () => { - props.updateLayout(name, { - move: { - x: translateX, - y: translateY, - }, - rotate: rotation, - flipX, - flipY - }) - } - - // Method to flip (mirror) the part along the X or Y axis - const flip = axis => { - if (axis === 'x') flipX = !flipX - else flipY = !flipY - updateLayout() - } - - return ( - - {PartInner(props)} - {props.name !== 'pages' && <> - - - - } - - ) -} +import Svg from '../../draft/svg' +import Defs from '../../draft/defs' +import { angle } from '../../draft/utils' +import Part from './part' const Draft = props => { if (!props.gistReady) {return null} @@ -288,8 +101,7 @@ const Draft = props => { // Helper method to update part layout and re-calculate width * height const updateLayout = (name, config) => { // Start creating new layout - const oldLayout = {...layout } || false; - const newLayout = {...oldLayout} + const newLayout = {...layout} newLayout.parts[name] = config newLayout.width = layout.width newLayout.height = layout.height diff --git a/sites/shared/components/workbench/layout/draft/part.js b/sites/shared/components/workbench/layout/draft/part.js new file mode 100644 index 00000000000..e3887b78374 --- /dev/null +++ b/sites/shared/components/workbench/layout/draft/part.js @@ -0,0 +1,182 @@ +import Path from '../../draft/path' +import Point from '../../draft/point' +import Snippet from '../../draft/snippet' +import {PartInner} from '../../draft/part' +import { getProps, angle } from '../../draft/utils' +import { drag } from 'd3-drag' +import { select } from 'd3-selection' +import { useRef, useState, useEffect} from 'react' + +const Buttons = ({ transform, flip, rotate, setRotate, resetPart }) => { + const letter = 'F' + const style = { style: {fill: 'white', fontSize: 18, fontWeight: 'bold', textAnchor: 'middle'} } + + return ( + + {rotate + ? + : + } + + + {letter} + + flip('y')}> + + {letter} + + flip('x')}> + + {letter} + + + ) +} + +const generateTransform = (x, y, rot, flipX, flipY, part) => { + const center = { + x: part.topLeft.x + (part.bottomRight.x - part.topLeft.x)/2, + y: part.topLeft.y + (part.bottomRight.y - part.topLeft.y)/2, + } + const dx = part.topLeft.x - center.x + const dy = part.topLeft.y - center.y + const transforms = [`translate(${x},${y})`] + if (flipX) transforms.push( + `translate(${center.x * -1}, ${center.y * -1})`, + 'scale(-1, 1)', + `translate(${center.x * -1 + 2 * dx}, ${center.y})` + ) + if (flipY) transforms.push( + `translate(${center.x * -1}, ${center.y * -1})`, + 'scale(1, -1)', + `translate(${center.x}, ${center.y * -1 + 2 * dy})`, + ) + if (rot) transforms.push( + `rotate(${rot}, ${center.x}, ${center.y})` + ) + + return transforms.join(' ') +} + +const Part = props => { + const { layout, gist, name, part} = props + + const partLayout = layout.parts[name] + + // Don't just assume this makes sense + if (typeof layout.parts?.[name]?.move?.x === 'undefined') return null + + // Use a ref for direct DOM manipulation + const partRef = useRef(null) + const centerRef = useRef(null) + + // State variable to switch between moving or rotating the part + const [rotate, setRotate] = useState(false) + + // Initialize drag handler + useEffect(() => { + handleDrag(select(partRef.current)) + }, [rotate, layout]) + + // 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 ? true : false + let flipY = partLayout.flipY ? true : false + let partRect + + const center = { + x: part.topLeft.x + (part.bottomRight.x - part.topLeft.x)/2, + y: part.topLeft.y + (part.bottomRight.y - part.topLeft.y)/2, + } + + const getRotation = (event) => angle(center, event.subject) - angle(center, { x:event.x, y: event.y }); + + const handleDrag = drag() + .subject(function(event) { + return rotate ? { x: event.x, y: event.y } : {x: translateX, y: translateY} + }) + .on('start', function(event) { + partRect = partRef.current.getBoundingClientRect() + }) + .on('drag', function(event) { + if (rotate) { + let newRotation = getRotation(event); + if (flipX) newRotation *= -1 + if (flipY) newRotation *= -1 + rotation = partRotation + newRotation + } + else { + translateX = event.x + translateY = event.y + } + const me = select(this); + me.attr('transform', generateTransform(translateX, translateY, rotation, flipX, flipY, part)); + }) + .on('end', function(event) { + updateLayout() + }) + + const resetPart = () => { + rotation = 0 + flipX = 0 + flipY = 0 + updateLayout() + } + const toggleDragRotate = () => { + updateLayout() + setRotate(!rotate) + } + const updateLayout = () => { + props.updateLayout(name, { + move: { + x: translateX, + y: translateY, + }, + rotate: rotation, + flipX, + flipY + }) + } + + // Method to flip (mirror) the part along the X or Y axis + const flip = axis => { + if (axis === 'x') flipX = !flipX + else flipY = !flipY + updateLayout() + } + + return ( + + {PartInner(props)} + {props.name !== 'pages' && <> + + + + } + + ) +} + +export default Part diff --git a/sites/shared/components/workbench/layout/print/index.js b/sites/shared/components/workbench/layout/print/index.js index 7075008a216..fe54ede1963 100644 --- a/sites/shared/components/workbench/layout/print/index.js +++ b/sites/shared/components/workbench/layout/print/index.js @@ -1,7 +1,7 @@ import { useEffect } from 'react' import { useTranslation } from 'next-i18next' import Settings from './settings' -import Draft from '../draft' +import Draft from '../draft/index' import pluginBuilder from './plugin' const PrintLayout = props => {