1
0
Fork 0

separate print layout from draft layout. add better buttons

This commit is contained in:
Enoch Riese 2022-08-09 16:16:06 -05:00
parent 236f35f765
commit 1a65a16e56
16 changed files with 246 additions and 112 deletions

View file

@ -362,25 +362,41 @@ export function pctBasedOn(measurement) {
/** Generates the transform attributes needed for a given part */ /** Generates the transform attributes needed for a given part */
export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => { export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => {
const center = {
x: part.topLeft.x + (part.bottomRight.x - part.topLeft.x)/2, const transforms = []
y: part.topLeft.y + (part.bottomRight.y - part.topLeft.y)/2, let xTotal = x || 0;
let yTotal = y || 0;
let scaleX = 1
let scaleY = 1
if (flipX) {
scaleX = -1
xTotal += part.topLeft.x * 2 + part.width
}
if (flipY) {
scaleY = -1
yTotal += part.topLeft.y * 2 + part.height
} }
const transforms = [`translate(${x},${y})`] if (scaleX + scaleY < 2) {
if (flipX) transforms.push( transforms.push(`scale(${scaleX} ${scaleY})`)
'scale(-1, 1)', }
)
if (flipY) transforms.push( if (rotate) {
'scale(1, -1)', const center = {
) x: part.topLeft.x + part.width/2,
if (rotate) transforms.push( y: part.topLeft.y + part.height/2,
`rotate(${rotate})` }
transforms.push(`rotate(${rotate} ${center.x} ${center.y})`)
}
if (xTotal > 0 || yTotal > 0) transforms.unshift(
`translate(${xTotal} ${yTotal})`
) )
return { return {
transform: transforms.join(' '), transform: transforms.join(' '),
'transform-origin': `${center.x} ${center.y}` // 'transform-origin': `${center.x} ${center.y}`
} }
} }

View file

@ -1,6 +1,8 @@
export const ClearIconInner = () => <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M3 12l6.414 6.414a2 2 0 001.414.586H19a2 2 0 002-2V7a2 2 0 00-2-2h-8.172a2 2 0 00-1.414.586L3 12z" />
const ClearIcon = ({ className='h-6 w-6' }) => ( const ClearIcon = ({ className='h-6 w-6' }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M3 12l6.414 6.414a2 2 0 001.414.586H19a2 2 0 002-2V7a2 2 0 00-2-2h-8.172a2 2 0 00-1.414.586L3 12z" /> <ClearIconInner></ClearIconInner>
</svg> </svg>
) )

View file

@ -0,0 +1,15 @@
const Triangle = ({transform='translate(0,0)', fill="currentColor"}) => <path strokeLinecap="round" strokeLinejoin="round" transform={transform} style={{fill}} transform-origin="12 12" d="M1 12m9 3m-6 4h2c3 0 3 -3 3-3L9 3c-0-1.732 -2.25-2.6125 -3.325 -.77L2 16c-.77 1.333.192 3 1.732 3z" />
const Line = () => <path strokeLinecap="round" strokeLinejoin="round" transform="translate(12, 2)" d="M0 0L0 20" />
export const FlipIconInner = ({x=0, y=0, rotate=0, ...style}) => <g transform={`translate(${x},${y}) rotate(${rotate})`} transform-origin="12 12" style={style}>
<Triangle fill="none" transform="translate(0, 2.5)"/>
<Line/>
<Triangle transform="scale(-1,1) translate(0,2.5)"/>
</g>
export default function({ className="h-6 w-6" }) {
return <svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<FlipIconInner></FlipIconInner>
</svg>
}

View file

@ -0,0 +1,7 @@
export const RotateIconInner = ({flipX=false}) => <path strokeLinecap="round" strokeLinejoin="round" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" transform={flipX ? 'scale(-1,1)' : ''} transform-origin="12 12"/>
export default function () {
return <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<RotateIconInner></RotateIconInner>
</svg>
}

View file

@ -0,0 +1,6 @@
/* Sourced from heroicons.com - Thanks guys! */
export default function () {
return <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
<path strokeLinecap="round" strokeLinejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2z" />
</svg>}

View file

@ -1,4 +1,4 @@
import React from 'react' import {forwardRef} from 'react'
import Path from '../path' import Path from '../path'
import Point from '../point' import Point from '../point'
import Snippet from '../snippet' import Snippet from '../snippet'
@ -85,7 +85,7 @@ const XrayPart = props => {
) )
} }
export const PartInner = props => { export const PartInner = forwardRef((props, ref) => {
const { partName, part, gist } = props const { partName, part, gist } = props
const grid = gist.paperless ? ( const grid = gist.paperless ? (
@ -99,7 +99,7 @@ export const PartInner = props => {
/> />
) : null ) : null
return (<> return (<g ref={ref}>
{grid} {grid}
{ {
gist._state?.xray?.enabled && gist._state?.xray?.enabled &&
@ -133,14 +133,15 @@ export const PartInner = props => {
{...props} {...props}
/> />
))} ))}
</>) </g>)
} })
const Part = props => { const Part = props => {
const { partName, part} = props const { partName, part} = props
return ( return (
<g {...getProps(part)} id={`part-${partName}`}> <g {...getProps(part)} id={`part-${partName}`}>
{PartInner(props)} <PartInner {...props}/>
</g> </g>
) )
} }

View file

@ -0,0 +1,66 @@
import {FlipIconInner} from 'shared/components/icons/flip'
import {RotateIconInner} from 'shared/components/icons/rotate'
import {ClearIconInner} from 'shared/components/icons/clear'
import { useTranslation } from 'next-i18next'
const rectSize = 24
const Button = ({onClickCb, transform, Icon, children}) => {
const _onClick = (event) => {
event.stopPropagation();
onClickCb(event);
}
return <g className="svg-layout-button group" transform={transform}>
<rect width={rectSize} height={rectSize} className="button"/>
<Icon />
<text className="invisible group-hover:visible text-xl">{children}</text>
<rect width={rectSize} height={rectSize} onClick={_onClick} className="fill-transparent"/>
</g>}
/** buttons for manipulating the part */
const Buttons = ({ transform, flip, rotate, setRotate, resetPart, rotate90}) => {
const {t} = useTranslation('workbench')
return (
<g transform={transform} >
{rotate
? <circle cx="0" cy="0" r="50" className='stroke-2xl muted' />
: <path d="M -50, 0 l 100,0 M 0,-50 l 0,100" className="stroke-2xl muted" />
}
<Button
onClickCb={resetPart}
transform={`translate(${rectSize/-2}, ${rectSize/-2})`}
Icon={ClearIconInner}>
{t('toolbar.resetPart')}
</Button>
<Button
onClickCb={() => rotate90(-1)}
transform={`translate(${rectSize* -2.7}, ${rectSize/-2})`}
Icon={RotateIconInner}
>
{t('toolbar.rotateCCW')}
</Button>
<Button
onClickCb={() => flip('y')}
transform={`translate(${rectSize* 0.6}, ${rectSize/-2})`}
Icon={() => <FlipIconInner rotate="270" />}
>
{t('toolbar.flipY')}
</Button>
<Button
onClickCb={() => flip('x')}
transform={`translate(${rectSize* -1.6}, ${rectSize/-2})`}
Icon={FlipIconInner}>
{t('toolbar.flipX')}
</Button>
<Button
onClickCb={() => rotate90()}
transform={`translate(${rectSize* 1.7}, ${rectSize/-2})`}
Icon={() => <RotateIconInner flipX={true}/>}>
{t('toolbar.rotateCW')}
</Button>
</g>
)
}
export default Buttons

View file

@ -2,27 +2,15 @@ import { useEffect, useRef} from 'react'
import Svg from '../../draft/svg' import Svg from '../../draft/svg'
import Defs from '../../draft/defs' import Defs from '../../draft/defs'
import Part from './part' import Part from './part'
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"
const Draft = props => { const Draft = props => {
if (!props.gistReady) {return null} const { patternProps, gist, updateGist, app, bgProps={}, fitLayoutPart = false, layoutType="printLayout"} = props
const { patternProps, gist, updateGist ,app, bgProps={} } = props
const { layout=false } = gist
let layout = {...props.layout}
const svgRef = useRef(null); const svgRef = useRef(null);
useEffect(() => { if (!patternProps) return null
if (!layout) {
// On the initial draft, core does the layout, so we set the layout to the auto-layout
// After this, core won't handle layout anymore. It's up to the user from this point onwards
updateGist(['layout'], {
...patternProps.autoLayout,
width: patternProps.width,
height: patternProps.height
}, false)
}
}, [layout])
if (!patternProps || !layout) return null
// Helper method to update part layout and re-calculate width * height // Helper method to update part layout and re-calculate width * height
const updateLayout = (name, config, history=true) => { const updateLayout = (name, config, history=true) => {
@ -34,7 +22,9 @@ const Draft = props => {
let topLeft = {x: 0, y: 0} let topLeft = {x: 0, y: 0}
let bottomRight = {x: 0, y: 0} let bottomRight = {x: 0, y: 0}
for (const [pname, part] of Object.entries(patternProps.parts)) { for (const [pname, part] of Object.entries(patternProps.parts)) {
if (pname == props.layoutPart && !fitLayoutPart) continue
let partLayout = newLayout.parts[pname]; let partLayout = newLayout.parts[pname];
// Pages part does not have its topLeft and bottomRight set by core since it's added post-draft // Pages part does not have its topLeft and bottomRight set by core since it's added post-draft
if (partLayout?.tl) { if (partLayout?.tl) {
// set the pattern extremes // set the pattern extremes
@ -49,7 +39,13 @@ const Draft = props => {
newLayout.height = bottomRight.y - topLeft.y newLayout.height = bottomRight.y - topLeft.y
newLayout.bottomRight = bottomRight newLayout.bottomRight = bottomRight
newLayout.topLeft = topLeft newLayout.topLeft = topLeft
updateGist(['layout'], newLayout, history)
if (history) {
updateGist(['layouts', layoutType], newLayout, history)
} else {
// we don't put it in the gist if it shouldn't contribute to history because we need some the data calculated here for rendering purposes on the initial layout, but we don't want to actually save a layout until the user manipulates it
layout = newLayout
}
} }
@ -60,19 +56,25 @@ const Draft = props => {
return ( return (
<div className="my-8 w-11/12 m-auto border-2 border-dotted border-base-content shadow"> <div className="my-8 w-11/12 m-auto border-2 border-dotted border-base-content shadow">
<TransformWrapper
minScale={0.1}
centerZoomedOut={true}
wheel={{ activationKeys: ['Control'] }}
>
<TransformComponent>
<Svg {...patternProps} <Svg {...patternProps}
embed={gist.embed} embed={gist.embed}
ref={svgRef} ref={svgRef}
viewBox={layout.topLeft ? `${layout.topLeft.x} ${layout.topLeft.y} ${layout.width} ${layout.height}` : false} viewBox={layout.topLeft ? `${layout.topLeft.x} ${layout.topLeft.y} ${layout.width} ${layout.height}` : false}
style={{maxHeight: '100vh'}} style={{height: '90vh'}}
> >
<Defs {...patternProps} /> <Defs {...patternProps} />
<style>{`:root { --pattern-scale: ${gist.scale || 1}}`}</style> <style>{`:root { --pattern-scale: ${gist.scale || 1}}`}</style>
<g> <g>
<rect x="0" y="0" width={patternProps.width} height={patternProps.height} {...bgProps} /> <rect x="0" y="0" width={layout.width} height={layout.height} {...bgProps} />
{[ {[
partList.filter(name => name === 'pages'), partList.filter(name => name === props.layoutPart),
partList.filter(name => name !== 'pages'), partList.filter(name => name !== props.layoutPart),
].map(list => list.map(name => ( ].map(list => list.map(name => (
<Part {...{ <Part {...{
key:name, key:name,
@ -82,10 +84,13 @@ const Draft = props => {
app, app,
gist, gist,
updateLayout, updateLayout,
isLayoutPart: name === props.layoutPart
}}/> }}/>
)))} )))}
</g> </g>
</Svg> </Svg>
</TransformComponent>
</TransformWrapper>
</div> </div>
) )
} }

View file

@ -49,33 +49,8 @@ import { getProps, angle } from '../../draft/utils'
import { drag } from 'd3-drag' import { drag } from 'd3-drag'
import { select } from 'd3-selection' import { select } from 'd3-selection'
import { useRef, useState, useEffect} from 'react' import { useRef, useState, useEffect} from 'react'
import Buttons from './buttons'
/** buttons for manipulating the part */
const Buttons = ({ transform, flip, rotate, setRotate, resetPart }) => {
const letter = 'F'
const style = { style: {fill: 'white', fontSize: 18, fontWeight: 'bold', textAnchor: 'middle'} }
return (
<g transform={transform}>
{rotate
? <circle cx="0" cy="0" r="50" className='stroke-2xl muted' />
: <path d="M -50, 0 l 100,0 M 0,-50 l 0,100" className="stroke-2xl muted" />
}
<g className="svg-layout-button" onClick={resetPart}>
<rect x="-10" y="-10" width="20" height="20" />
<text x="0" y="10" {...style}>{letter}</text>
</g>
<g className="svg-layout-button" onClick={() => flip('y')}>
<rect x="10" y="-10" width="20" height="20" className="button" />
<text x="20" y="10" {...style} transform="scale(1,-1)">{letter}</text>
</g>
<g className="svg-layout-button" onClick={() => flip('x')}>
<rect x="-30" y="-10" width="20" height="20" className="button" />
<text x="20" y="10" {...style} transform="scale(-1,1)">{letter}</text>
</g>
</g>
)
}
const Part = props => { const Part = props => {
const { layout, part, partName} = props const { layout, part, partName} = props
@ -88,20 +63,23 @@ const Part = props => {
// Use a ref for direct DOM manipulation // Use a ref for direct DOM manipulation
const partRef = useRef(null) const partRef = useRef(null)
const centerRef = useRef(null) const centerRef = useRef(null)
const innerRef = useRef(null)
// State variable to switch between moving or rotating the part // State variable to switch between moving or rotating the part
const [rotate, setRotate] = useState(false) const [rotate, setRotate] = useState(false)
// update the layout on mount // update the layout on mount
useEffect(() => { useEffect(() => {
if (partRef.current) updateLayout(false) if (partRef.current && !props.isLayoutPart) {
}, [partRef]) updateLayout(false)
}
}, [partRef, partLayout])
// Initialize drag handler // Initialize drag handler
useEffect(() => { useEffect(() => {
if (!partRef.current) {return} if (props.isLayoutPart) return
handleDrag(select(partRef.current)) handleDrag(select(partRef.current))
}, [rotate, layout]) }, [rotate, partRef, partLayout])
// These are kept as vars because re-rendering on drag would kill performance // 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 // Managing the difference between re-render and direct DOM updates makes this
@ -121,6 +99,17 @@ const Part = props => {
/** get the delta rotation from the start of the drag event to now */ /** 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 me = select(partRef.current);
for (var t in transforms) {
me.attr(t, transforms[t])
}
}
let didDrag = false;
const handleDrag = drag() const handleDrag = drag()
// subject allows us to save data from the start of the event to use throughout event handing // subject allows us to save data from the start of the event to use throughout event handing
.subject(function(event) { .subject(function(event) {
@ -131,8 +120,13 @@ const Part = props => {
{x: translateX, y: translateY} {x: translateX, y: translateY}
}) })
.on('drag', function(event) { .on('drag', function(event) {
if (!event.dx && !event.dy) return
if (rotate) { if (rotate) {
let newRotation = getRotation(event); let newRotation = getRotation(event);
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 // reverse the rotation direction one time per flip. if we're flipped both directions, rotation will be positive again
if (flipX) newRotation *= -1 if (flipX) newRotation *= -1
if (flipY) newRotation *= -1 if (flipY) newRotation *= -1
@ -144,46 +138,47 @@ const Part = props => {
translateY = event.y translateY = event.y
} }
// get the transform attributes didDrag = true;
const transforms = generatePartTransform(translateX, translateY, rotation, flipX, flipY, part); setTransforms()
const me = select(this);
for (var t in transforms) {
me.attr(t, transforms[t])
}
}) })
.on('end', function(event) { .on('end', function(event) {
// save to gist // save to gist
updateLayout() if (didDrag) updateLayout()
didDrag = false
}) })
const resetPart = () => { const resetPart = (event) => {
rotation = 0 rotation = 0
flipX = 0 flipX = 0
flipY = 0 flipY = 0
updateLayout() updateLayout()
} }
const toggleDragRotate = () => { const toggleDragRotate = () => {
if (!partRef.current) {return} if (!partRef.current || props.isLayoutPart) {return}
updateLayout() updateLayout()
setRotate(!rotate) setRotate(!rotate)
} }
const updateLayout = (history=true) => { const updateLayout = (history=true) => {
const partRect = partRef.current.getBoundingClientRect(); if (!partRef.current || props.isLayoutPart) return
const matrix = partRef.current.ownerSVGElement.getScreenCTM().inverse();
setTransforms()
const partRect = innerRef.current.getBoundingClientRect();
const matrix = innerRef.current.ownerSVGElement.getScreenCTM().inverse();
const domToSvg = (point) => DOMPointReadOnly.fromPoint(point).matrixTransform(matrix) const domToSvg = (point) => DOMPointReadOnly.fromPoint(point).matrixTransform(matrix)
// include the new top left and bottom right to ease calculating the pattern width and height // 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 tl = domToSvg({x: partRect.left, y: partRect.top});
const br = domToSvg({x: partRect.right, y: partRect.bottom}); const br = domToSvg({x: partRect.right, y: props.isLayoutPart ? 0 : partRect.bottom});
props.updateLayout(partName, { props.updateLayout(partName, {
move: { move: {
x: translateX, x: translateX,
y: translateY, y: translateY,
}, },
rotate: rotation, rotate: rotation % 360,
flipX, flipX,
flipY, flipY,
tl, tl,
@ -198,30 +193,44 @@ const Part = props => {
updateLayout() updateLayout()
} }
const rotate90 = (direction = 1) => {
if (flipX) direction *= -1
if (flipY) direction *= -1
rotation += 90 * direction
updateLayout()
}
if (Object.keys(part.snippets).length === 0 && Object.keys(part.paths).length === 0) return null;
return ( return (
<g <g
{...getProps(part)}
id={`part-${partName}`} id={`part-${partName}`}
ref={partName === 'pages' ? null : partRef} ref={partRef}
onClick={toggleDragRotate} {...getProps(part)}
transform-origin={`${center.x} ${center.y}`}
> >
{PartInner(props)} <PartInner {...props} ref={innerRef}/>
{partName !== 'pages' && <> {!props.isLayoutPart && <>
<text x={center.x} y={center.y} ref={centerRef} /> <text x={center.x} y={center.y} ref={centerRef} />
<rect <rect
ref={partRef}
x={part.topLeft.x} x={part.topLeft.x}
y={part.topLeft.y} y={part.topLeft.y}
width={part.width} width={part.width}
height={part.height} height={part.height}
className={`layout-rect ${rotate ? 'rotate' : 'move'}`} className={`layout-rect ${rotate ? 'rotate' : 'move'}`}
id={`${partName}-layout-rect`}
onClick={toggleDragRotate}
/> />
<Buttons <Buttons
transform={`translate(${part.topLeft.x + part.width/2}, ${part.topLeft.y + part.height/2})`} transform={`translate(${center.x}, ${center.y}) rotate(${-rotation}) scale(${flipX ? -1 : 1},${flipY ? -1 : 1})`}
flip={flip} flip={flip}
rotate={rotate} rotate={rotate}
setRotate={setRotate} setRotate={setRotate}
resetPart={resetPart} resetPart={resetPart}
rotate90={rotate90}
partName={partName}
/> />
</>} </>}
</g> </g>

View file

@ -1,11 +1,10 @@
import { useEffect } from 'react' import { useEffect, useRef } from 'react'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import Settings from './settings' import Settings from './settings'
import Draft from '../draft/index' import Draft from '../draft/index'
import pluginBuilder from './plugin' import pluginBuilder from './plugin'
const PrintLayout = props => { const PrintLayout = props => {
useEffect(() => { useEffect(() => {
if (props.gist?._state?.xray?.enabled) props.updateGist( if (props.gist?._state?.xray?.enabled) props.updateGist(
['_state', 'xray', 'enabled'], ['_state', 'xray', 'enabled'],
@ -15,14 +14,21 @@ const PrintLayout = props => {
const { t } = useTranslation(['workbench']) const { t } = useTranslation(['workbench'])
const draft = new props.design(props.gist).use(pluginBuilder( const draft = props.draft
draft.use(pluginBuilder(
props.gist?._state?.layout?.forPrinting?.page?.size, props.gist?._state?.layout?.forPrinting?.page?.size,
props.gist?._state?.layout?.forPrinting?.page?.orientation, props.gist?._state?.layout?.forPrinting?.page?.orientation,
)) ))
let patternProps let patternProps
let layout
try { try {
draft.draft() draft.draft()
patternProps = draft.getRenderProps() patternProps = draft.getRenderProps()
layout = draft.settings.layout === true ? {
...patternProps.autoLayout,
width: patternProps.width,
height: patternProps.height
} : draft.settings.layout
} catch(err) { } catch(err) {
console.log(err, props.gist) console.log(err, props.gist)
} }
@ -47,6 +53,8 @@ const PrintLayout = props => {
patternProps={patternProps} patternProps={patternProps}
bgProps={bgProps} bgProps={bgProps}
gistReady={props.gistReady} gistReady={props.gistReady}
layoutPart="pages"
layout={layout}
/> />
</div> </div>
) )

View file

@ -26,7 +26,7 @@ const PrintLayoutSettings = props => {
</div> </div>
<button <button
key="reset" key="reset"
onClick={() => props.unsetGist('layout')} onClick={() => props.unsetGist(['layouts', 'printLayout'])}
className="btn btn-primary btn-outline" className="btn btn-primary btn-outline"
> >
<ClearIcon className="h-6 w-6 mr-2"/> <ClearIcon className="h-6 w-6 mr-2"/>

View file

@ -86,7 +86,7 @@ const WorkbenchMenu = props => {
return ( return (
<nav className="grow mb-12"> <nav className="grow mb-12">
<ViewMenu {...props} /> <ViewMenu {...props} />
{props.gist?._state?.view === 'draft' && ( {['draft', 'cuttingLayout', 'printingLayout'].indexOf(props.gist?._state?.view) > -1 && (
<> <>
<DesignOptions {...props} /> <DesignOptions {...props} />
<CoreSettings {...props} /> <CoreSettings {...props} />

View file

@ -90,6 +90,7 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
// Helper methods to manage the gist state // Helper methods to manage the gist state
const updateWBGist = useMemo(() => (path, value, closeNav=false, addToHistory=true) => { const updateWBGist = useMemo(() => (path, value, closeNav=false, addToHistory=true) => {
console.warn('updating gist')
updateGist(path, value, addToHistory) updateGist(path, value, addToHistory)
// Force close of menu on mobile if it is open // Force close of menu on mobile if it is open
if (closeNav && app.primaryMenu) app.setPrimaryMenu(false) if (closeNav && app.primaryMenu) app.setPrimaryMenu(false)
@ -112,8 +113,9 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
// Generate the draft here so we can pass it down // Generate the draft here so we can pass it down
let draft = false let draft = false
if (['draft', 'events', 'test'].indexOf(gist._state?.view) !== -1) { if (['draft', 'events', 'test', 'printingLayout'].indexOf(gist._state?.view) !== -1) {
draft = new design(gist) const layout = (gist._state.view === 'printingLayout' && gist.layouts?.printLayout) || gist.layout || true
draft = new design({...gist, layout})
if (gist.renderer === 'svg') draft.use(theme) if (gist.renderer === 'svg') draft.use(theme)
try { try {
if (gist._state.view !== 'test') draft.draft() if (gist._state.view !== 'test') draft.draft()

View file

@ -1,7 +1,7 @@
import useLocalStorage from './useLocalStorage'; import useLocalStorage from './useLocalStorage';
import set from 'lodash.set' import set from 'lodash.set'
import unset from 'lodash.unset' import unset from 'lodash.unset'
import cloneDeep from 'lodash.clonedeep' import cloneDeep from 'lodash.cloneDeep'
import defaultSettings from 'shared/components/workbench/default-settings.js' import defaultSettings from 'shared/components/workbench/default-settings.js'
import {useState} from 'react' import {useState} from 'react'
@ -29,7 +29,8 @@ export function useGist(design, app) {
let oldGist let oldGist
_setGist((gistState) => { _setGist((gistState) => {
// have to clone it or nested objects will be referenced instead of copied, which defeats the purpose // have to clone it or nested objects will be referenced instead of copied, which defeats the purpose
oldGist = cloneDeep(gistState); if (addToHistory) oldGist = cloneDeep(gistState)
return typeof newGist === 'function' ? newGist(cloneDeep(gistState)) : newGist return typeof newGist === 'function' ? newGist(cloneDeep(gistState)) : newGist
}) })

View file

@ -267,14 +267,6 @@ summary::-webkit-details-marker {
display: none; display: none;
} }
/* Style for slider track */
input[type=range]::-webkit-slider-runnable-track {
background: #ffffff44;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #ffffff66;
}
input[type=range]::-moz-range-track { input[type=range]::-moz-range-track {
background: #3071a9; background: #3071a9;

View file

@ -61,6 +61,7 @@ svg.freesewing.pattern {
.bold { font-weight: bold; } .bold { font-weight: bold; }
.center { text-anchor: middle; } .center { text-anchor: middle; }
.baseline-center {dominant-baseline: central;}
.right { text-anchor: end; } .right { text-anchor: end; }
/* muted page numbers */ /* muted page numbers */
@ -84,10 +85,13 @@ svg.freesewing.pattern {
} }
.svg-layout-button > rect.button { .svg-layout-button > rect.button {
fill: var(--pattern-note); fill: var(--pattern-note);
fill-opacity: 0.3; fill-opacity: 0.5;
stroke: none; stroke: none;
} }
.svg-layout-button:hover > rect { .svg-layout-button path {
stroke-width: calc(var(--pattern-stroke) * 2 * var(--pattern-scale));;
}
.svg-layout-button:hover > rect.button {
fill: var(--pattern-lining); fill: var(--pattern-lining);
stroke: none; stroke: none;
fill-opacity: 1; fill-opacity: 1;