feat: Handle absolute input on pct options
This commit is contained in:
parent
987ba5419c
commit
752dd69c50
14 changed files with 645 additions and 394 deletions
370
packages/react/components/Editor/components/MovablePattern.mjs
Normal file
370
packages/react/components/Editor/components/MovablePattern.mjs
Normal file
|
@ -0,0 +1,370 @@
|
|||
import React, { useRef } from 'react'
|
||||
import { PanZoomPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
|
||||
import { MovableStack } from './stack.mjs'
|
||||
|
||||
export const MovablePattern = ({
|
||||
renderProps,
|
||||
showButtons = true,
|
||||
update,
|
||||
fitImmovable = false,
|
||||
immovable = [],
|
||||
layoutPath,
|
||||
}) => {
|
||||
const svgRef = useRef(null)
|
||||
if (!renderProps) return null
|
||||
|
||||
// keep a fresh copy of the layout because we might manipulate it without saving to the gist
|
||||
let layout =
|
||||
renderProps.settings[0].layout === true
|
||||
? {
|
||||
...renderProps.autoLayout,
|
||||
width: renderProps.width,
|
||||
height: renderProps.height,
|
||||
}
|
||||
: renderProps.settings[0].layout
|
||||
|
||||
// Helper method to update part layout and re-calculate width * height
|
||||
const updateLayout = (name, config, history = true) => {
|
||||
// Start creating new layout
|
||||
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 in renderProps.stacks) {
|
||||
if (immovable.includes(pname) && !fitImmovable) continue
|
||||
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) {
|
||||
// set the pattern extremes
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
newLayout.width = bottomRight.x - topLeft.x
|
||||
newLayout.height = bottomRight.y - topLeft.y
|
||||
newLayout.bottomRight = bottomRight
|
||||
newLayout.topLeft = topLeft
|
||||
|
||||
if (history) {
|
||||
update.ui(layoutPath, newLayout)
|
||||
} else {
|
||||
// we don't put it in the gist if it shouldn't contribute to history because we need some of 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. This is what allows the layout to respond appropriately to settings changes. Once the user has starting playing with the layout, all bets are off
|
||||
layout = newLayout
|
||||
}
|
||||
}
|
||||
|
||||
const sortedStacks = {}
|
||||
Object.keys(renderProps.stacks)
|
||||
.sort((a, b) => {
|
||||
const hasA = immovable.includes(a)
|
||||
const hasB = immovable.includes(b)
|
||||
if (hasA && !hasB) return -1
|
||||
if (!hasA && hasB) return 1
|
||||
return 0
|
||||
})
|
||||
.forEach((s) => (sortedStacks[s] = renderProps.stacks[s]))
|
||||
|
||||
const sortedRenderProps = { ...renderProps, stacks: sortedStacks }
|
||||
|
||||
const Stack = ({ stackName, stack, settings, components, t }) => (
|
||||
<MovableStack
|
||||
{...{
|
||||
stackName,
|
||||
stack,
|
||||
components,
|
||||
t,
|
||||
movable: !immovable.includes(stackName),
|
||||
layout: layout.stacks[stackName],
|
||||
updateLayout,
|
||||
showButtons,
|
||||
settings,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<PanZoomPattern
|
||||
{...{
|
||||
renderProps: sortedRenderProps,
|
||||
components: { Stack },
|
||||
}}
|
||||
ref={svgRef}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* This React component is a long way from perfect, but it's a start for
|
||||
* handling custom layouts.
|
||||
*
|
||||
* There are a few reasons that (at least in my opinion) implementing this is non-trivial:
|
||||
*
|
||||
* 1) React re-render vs DOM updates
|
||||
*
|
||||
* For performance reasons, we can't re-render with React when the user drags a
|
||||
* pattern part (or rotates it). It would kill performance.
|
||||
* So, we don't re-render with React upon dragging/rotating, but instead manipulate
|
||||
* the DOM directly.
|
||||
*
|
||||
* So far so good, but of course we don't want a pattern that's only correctly laid
|
||||
* out in the DOM. We want to update the pattern gist so that the new layout is stored.
|
||||
* For this, we re-render with React on the end of the drag (or rotate).
|
||||
*
|
||||
* Handling this balance between DOM updates and React re-renders is a first contributing
|
||||
* factor to why this component is non-trivial
|
||||
*
|
||||
* 2) SVG vs DOM coordinates
|
||||
*
|
||||
* When we drag or rotate with the mouse, all the events are giving us coordinates of
|
||||
* where the mouse is in the DOM.
|
||||
*
|
||||
* The layout uses coordinates from the embedded SVG which are completely different.
|
||||
*
|
||||
* We run `getScreenCTM().inverse()` on the svg element to pass to
|
||||
* `matrixTransform` on a `DOMPointReadOnly` for dom to svg space
|
||||
* conversions.
|
||||
*
|
||||
* 3) Part-level transforms
|
||||
*
|
||||
* All parts use their center as the transform-origin to simplify
|
||||
* transforms, especially flipping and rotating.
|
||||
*
|
||||
* 4) Bounding box
|
||||
*
|
||||
* We use `getBoundingClientRect` rather than `getBBox` because it provides
|
||||
* more data and factors in the transforms. We then use our `domToSvg`
|
||||
* function to move the points back into the SVG space.
|
||||
*/
|
||||
//import { useRef, useState, useEffect, useCallback } from 'react'
|
||||
//import { generateStackTransform, getTransformedBounds } from '@freesewing/core'
|
||||
//import { getProps } from 'pkgs/react-components/src/pattern/utils.mjs'
|
||||
//import { angle } from '../utils.mjs'
|
||||
//import { drag } from 'd3-drag'
|
||||
//import { select } from 'd3-selection'
|
||||
//import { Buttons } from './transform-buttons.mjs'
|
||||
|
||||
export const MovableStack = ({
|
||||
stackName,
|
||||
stack,
|
||||
components,
|
||||
t,
|
||||
movable = true,
|
||||
layout,
|
||||
updateLayout,
|
||||
showButtons,
|
||||
settings,
|
||||
}) => {
|
||||
const stackExists = !movable || typeof layout?.move?.x !== 'undefined'
|
||||
|
||||
// Use a ref for direct DOM manipulation
|
||||
const stackRef = useRef(null)
|
||||
const innerRef = useRef(null)
|
||||
|
||||
// State variable to switch between moving or rotating the part
|
||||
const [rotate, setRotate] = useState(false)
|
||||
|
||||
// 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
|
||||
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 = stack.topLeft && {
|
||||
x: stack.topLeft.x + (stack.bottomRight.x - stack.topLeft.x) / 2,
|
||||
y: stack.topLeft.y + (stack.bottomRight.y - stack.topLeft.y) / 2,
|
||||
}
|
||||
|
||||
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])
|
||||
|
||||
/** 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
|
||||
|
||||
// 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 { topLeft, bottomRight } = getTransformedBounds(stack, transforms)
|
||||
|
||||
// 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: topLeft,
|
||||
br: bottomRight,
|
||||
},
|
||||
history
|
||||
)
|
||||
},
|
||||
[stackRef, setTransforms, updateLayout, liveTransforms, movable, stack, stackName]
|
||||
)
|
||||
|
||||
// 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 = () => {
|
||||
liveTransforms.rotation = 0
|
||||
liveTransforms.flipX = 0
|
||||
liveTransforms.flipY = 0
|
||||
updateStacklayout()
|
||||
}
|
||||
|
||||
/** toggle between dragging and rotating */
|
||||
const toggleDragRotate = () => {
|
||||
// only respond if the part should be able to drag/rotate
|
||||
if (!stackRef.current || !movable) {
|
||||
return
|
||||
}
|
||||
|
||||
setRotate(!rotate)
|
||||
}
|
||||
|
||||
/** Method to flip (mirror) the part along the X or Y axis */
|
||||
const flip = (axis) => {
|
||||
if (axis === 'x') liveTransforms.flipX = !liveTransforms.flipX
|
||||
else liveTransforms.flipY = !liveTransforms.flipY
|
||||
updateStacklayout()
|
||||
}
|
||||
|
||||
/** method to rotate 90 degrees */
|
||||
const rotate90 = (direction = 1) => {
|
||||
if (liveTransforms.flipX) direction *= -1
|
||||
if (liveTransforms.flipY) direction *= -1
|
||||
|
||||
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 (
|
||||
<Group id={`stack-${stackName}`} {...getProps(stack)} ref={stackRef}>
|
||||
<Group id={`stack-inner-${stackName}`} ref={innerRef}>
|
||||
{[...stack.parts].map((part, key) => (
|
||||
<Part {...{ components, t, part, stackName, settings }} key={key} />
|
||||
))}
|
||||
</Group>
|
||||
{movable && (
|
||||
<>
|
||||
<rect
|
||||
x={stack.topLeft.x}
|
||||
y={stack.topLeft.y}
|
||||
width={stack.width}
|
||||
height={stack.height}
|
||||
className={`layout-rect ${rotate ? 'rotate' : 'move'}`}
|
||||
id={`${stackName}-layout-rect`}
|
||||
onClick={toggleDragRotate}
|
||||
/>
|
||||
{showButtons ? (
|
||||
<Buttons
|
||||
transform={`translate(${center.x}, ${
|
||||
center.y
|
||||
}) rotate(${-liveTransforms.rotation}) scale(${liveTransforms.flipX ? -1 : 1},${
|
||||
liveTransforms.flipY ? -1 : 1
|
||||
})`}
|
||||
flip={flip}
|
||||
rotate={rotate}
|
||||
setRotate={setRotate}
|
||||
resetPart={resetPart}
|
||||
rotate90={rotate90}
|
||||
partName={stackName}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Group>
|
||||
)
|
||||
}
|
|
@ -106,11 +106,6 @@ export const MenuItem = ({
|
|||
return (
|
||||
<>
|
||||
<FormControl
|
||||
FIXME_REMOVED_label={
|
||||
<span className="tw-text-base tw-font-normal">
|
||||
{config.choiceTitles ? config.choiceTitles[current] : i18n?.en?.o?.[name]?.d}
|
||||
</span>
|
||||
}
|
||||
label={false}
|
||||
id={config.name}
|
||||
labelBR={<div className="tw-flex tw-flex-row tw-items-center tw-gap-2">{buttons}</div>}
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
MenuSliderInput,
|
||||
MenuDegInput,
|
||||
MenuListInput,
|
||||
MenuMmInput,
|
||||
MenuPctInput,
|
||||
} from './Input.mjs'
|
||||
import {
|
||||
|
@ -18,7 +19,7 @@ import {
|
|||
MenuCountOptionValue,
|
||||
MenuDegOptionValue,
|
||||
MenuListOptionValue,
|
||||
MenyMmOptionValue,
|
||||
MenuMmOptionValue,
|
||||
MenuPctOptionValue,
|
||||
} from './Value.mjs'
|
||||
import { MenuItemGroup, MenuItem } from './Container.mjs'
|
||||
|
@ -57,7 +58,7 @@ export const DesignOptionsMenu = ({ Design, isFirst = true, state, i18n, update
|
|||
),
|
||||
deg: (props) => <MenuDegInput {...drillProps} {...props} />,
|
||||
list: (props) => <MenuListInput {...drillProps} {...props} isDesignOption />,
|
||||
mm: () => <span>FIXME: Mm options are deprecated. Please report this </span>,
|
||||
mm: (props) => <MenuMmInput {...drillProps} {...props} />,
|
||||
pct: (props) => <MenuPctInput {...drillProps} {...props} />,
|
||||
}
|
||||
const values = {
|
||||
|
@ -107,7 +108,7 @@ export const DesignOption = ({ config, settings, ux, inputs, values, ...rest })
|
|||
const type = designOptionType(config)
|
||||
const Input = inputs[type]
|
||||
const Value = values[type]
|
||||
const allowOverride = ['pct', 'count', 'deg'].includes(type)
|
||||
const allowOverride = ['pct', 'count', 'deg', 'mm'].includes(type)
|
||||
const allowToggle = (ux > 3 && type === 'bool') || (type == 'list' && config.list.length === 2)
|
||||
|
||||
// Hide option?
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import React, { useMemo, useCallback, useState } from 'react'
|
||||
import { i18n } from '@freesewing/collection'
|
||||
import { designOptionType, round, measurementAsUnits, measurementAsMm } from '@freesewing/utils'
|
||||
import {
|
||||
designOptionType,
|
||||
round,
|
||||
measurementAsUnits,
|
||||
measurementAsMm,
|
||||
formatMm,
|
||||
formatFraction128,
|
||||
} from '@freesewing/utils'
|
||||
import { menuRoundPct } from '../../lib/index.mjs'
|
||||
import { ButtonFrame, NumberInput } from '@freesewing/react/components/Input'
|
||||
import { defaultConfig } from '../../config/index.mjs'
|
||||
import { ApplyIcon } from '@freesewing/react/components/Icon'
|
||||
import { capitalize } from '@freesewing/core'
|
||||
import { capitalize, mergeOptions } from '@freesewing/core'
|
||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
|
||||
/** A boolean version of {@see MenuListInput} that sets up the necessary configuration */
|
||||
export const MenuBoolInput = (props) => {
|
||||
|
@ -67,7 +75,6 @@ const getTitleAndDesc = (config = {}, i18n = {}, isDesignOption = false) => {
|
|||
: `${name}.o.${entry}`
|
||||
if (!config.choiceTitles && i18n && i18n.en.o[`${name}.${entry}`])
|
||||
titleKey = i18n.en.o[`${name}.${entry}`]
|
||||
console.log({ titleKey, titles: config.choiceTitles, isDesignOption })
|
||||
const title = config.title
|
||||
? config.title
|
||||
: config.titleMethod
|
||||
|
@ -180,7 +187,9 @@ export const MenuListToggle = ({ config, changed, updateHandler, name }) => {
|
|||
}
|
||||
|
||||
export const MenuMmInput = (props) => {
|
||||
const { units, updateHandler, current, config } = props
|
||||
const { updateHandler, current, config } = props
|
||||
const units = props.state.settings?.units
|
||||
const imperial = units === 'imperial'
|
||||
const mmUpdateHandler = useCallback(
|
||||
(path, newCurrent) => {
|
||||
const calcCurrent =
|
||||
|
@ -200,6 +209,8 @@ export const MenuMmInput = (props) => {
|
|||
config: {
|
||||
step: defaultStep,
|
||||
...config,
|
||||
min: imperial ? config.min / 25.4 : config.min,
|
||||
max: imperial ? config.max / 25.4 : config.min,
|
||||
dflt: measurementAsUnits(config.dflt, units),
|
||||
},
|
||||
current: current === undefined ? undefined : measurementAsUnits(current, units),
|
||||
|
@ -260,6 +271,8 @@ export const MenuSliderInput = ({
|
|||
children,
|
||||
changed,
|
||||
i18n,
|
||||
state,
|
||||
Design,
|
||||
}) => {
|
||||
const { max, min } = config
|
||||
const handleChange = useSharedHandlers({
|
||||
|
@ -283,6 +296,7 @@ export const MenuSliderInput = ({
|
|||
handleChange,
|
||||
min,
|
||||
max,
|
||||
state,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -296,11 +310,28 @@ export const MenuSliderInput = ({
|
|||
<span className="tw-opacity-50">
|
||||
<span dangerouslySetInnerHTML={{ __html: valFormatter(min) + suffix }} />
|
||||
</span>
|
||||
<span
|
||||
<div
|
||||
className={`tw-font-bold ${val === config.dflt ? 'tw-text-secondary' : 'tw-text-accent'}`}
|
||||
>
|
||||
<span dangerouslySetInnerHTML={{ __html: valFormatter(val) + suffix }} />
|
||||
</span>
|
||||
{typeof config.toAbs === 'function' ? (
|
||||
<span>
|
||||
<span className="tw-px-2">|</span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: formatMm(
|
||||
config.toAbs(
|
||||
val / 100,
|
||||
state.settings,
|
||||
mergeOptions(state.settings, Design.patternConfig.options)
|
||||
),
|
||||
state.settings?.units
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<span className="tw-opacity-50">
|
||||
<span dangerouslySetInnerHTML={{ __html: valFormatter(max) + suffix }} />
|
||||
</span>
|
||||
|
@ -320,30 +351,59 @@ export const MenuSliderInput = ({
|
|||
}
|
||||
|
||||
export const MenuEditOption = (props) => {
|
||||
const [manualEdit, setManualEdit] = useState(props.current)
|
||||
const { config, handleChange } = props
|
||||
const type = designOptionType(config)
|
||||
|
||||
const [manualEdit, setManualEdit] = useState(props.current)
|
||||
const [abs, setAbs] = useState(false)
|
||||
const [units, setUnits] = useState(
|
||||
abs
|
||||
? props.state.settings?.units === 'imperial'
|
||||
? 'inch'
|
||||
: 'cm'
|
||||
: defaultConfig.menuOptionEditLabels[type]
|
||||
)
|
||||
|
||||
const onUpdate = useCallback(
|
||||
(validVal) => {
|
||||
if (validVal !== null && validVal !== false) handleChange(validVal)
|
||||
(validVal, units) => {
|
||||
if (validVal !== null && validVal !== false) {
|
||||
if (type === 'pct' && units === 'cm')
|
||||
return handleChange(config.fromAbs(Number(validVal) * 1000, props.state.settings))
|
||||
if (type === 'pct' && units === 'inch')
|
||||
return handleChange(config.fromAbs(Number(validVal) * 2540, props.state.settings))
|
||||
return handleChange(validVal)
|
||||
}
|
||||
},
|
||||
[handleChange]
|
||||
)
|
||||
|
||||
const toggleInputUnits = () => {
|
||||
if (abs) setUnits(defaultConfig.menuOptionEditLabels[type])
|
||||
else setUnits(props.state.settings?.units === 'imperial' ? 'inch' : 'cm')
|
||||
setAbs(!abs)
|
||||
console.log('in toogg;e')
|
||||
}
|
||||
|
||||
if (!['pct', 'count', 'deg', 'mm'].includes(type))
|
||||
return <p>This design option type does not have a component to handle manual input.</p>
|
||||
|
||||
return (
|
||||
<div className="tw-daisy-form-control tw-mb-2 tw-w-full">
|
||||
<label className="tw-daisy-label tw-font-medium tw-text-accent">
|
||||
<em>Enter a custom value ({defaultConfig.menuOptionEditLabels[type]})</em>
|
||||
</label>
|
||||
<label className="tw-daisy-input-group tw-daisy-input-group-sm tw-flex tw-flex-row tw-items-center tw-gap-2 tw--mt-4">
|
||||
<div className="tw-daisy-label tw-font-medium tw-text-accent">
|
||||
<label className="tw-daisy-label-text">
|
||||
<em>Enter a custom value</em> {units}
|
||||
</label>
|
||||
{type === 'pct' && typeof config.fromAbs === 'function' ? (
|
||||
<label className="tw-daisy-label-text">
|
||||
<KeyVal k="units" val={units} onClick={toggleInputUnits} color="secondary" />
|
||||
</label>
|
||||
) : null}
|
||||
</div>
|
||||
<label className="tw-daisy-input-group tw-daisy-input-group-sm tw-flex tw-flex-row tw-items-end tw-gap-2 tw--mt-4">
|
||||
<NumberInput value={manualEdit} update={setManualEdit} />
|
||||
<button
|
||||
className="tw-daisy-btn tw-daisy-btn-secondary tw-mt-4"
|
||||
onClick={() => onUpdate(manualEdit)}
|
||||
onClick={() => onUpdate(manualEdit, units)}
|
||||
>
|
||||
<ApplyIcon />
|
||||
</button>
|
||||
|
|
|
@ -92,8 +92,14 @@ export const MenuBoolValue = MenuListOptionValue
|
|||
/**
|
||||
* Displays the MmOptions are not supported
|
||||
*/
|
||||
export const MenuMmOptionValue = () => (
|
||||
<span className="text-error">FIXME: No Mm Options are not supported</span>
|
||||
export const MenuMmOptionValue = ({ config, changed, current, state }) => (
|
||||
<MenuHighlightValue changed={changed}>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: formatMm(changed ? current : config.dflt, state.settings?.units),
|
||||
}}
|
||||
/>
|
||||
</MenuHighlightValue>
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -111,13 +117,6 @@ export const MenuMmValue = ({ current, config, units, changed }) => (
|
|||
|
||||
/**
|
||||
* Displays the current percentage value, and the absolute value if configured
|
||||
*
|
||||
**************************************************************************
|
||||
* SliderIcon Title THIS *
|
||||
* min% max% *
|
||||
* ----------------------0----------------------------------------------- *
|
||||
* msg PencilIcon ResetIcon *
|
||||
**************************************************************************
|
||||
*/
|
||||
export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => {
|
||||
const val = changed ? current : config.pct / 100
|
||||
|
|
142
packages/react/components/Editor/components/views/LayoutView.mjs
Normal file
142
packages/react/components/Editor/components/views/LayoutView.mjs
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Dependencies
|
||||
import { defaultPrintSettings, printSettingsPath, handleExport } from '../../lib/export/index.mjs'
|
||||
import { tilerPlugin } from '../../lib/export/plugin-tiler.mjs'
|
||||
import { get } from '@freesewing/utils'
|
||||
import { draft } from '../../lib/index.mjs'
|
||||
import React from 'react'
|
||||
//import {
|
||||
// handleExport,
|
||||
// ns as exportNs,
|
||||
//} from 'shared/components/workbench/exporting/export-handler.mjs'
|
||||
//import { pagesPlugin } from 'shared/plugins/plugin-layout-part.mjs'
|
||||
//import get from 'lodash.get'
|
||||
//import { defaultPrintSettings, printSettingsPath } from './config.mjs'
|
||||
//// Hooks
|
||||
//import { useContext } from 'react'
|
||||
//import { useTranslation } from 'next-i18next'
|
||||
//// Context
|
||||
//import { LoadingStatusContext } from 'shared/context/loading-status-context.mjs'
|
||||
// Components
|
||||
import { ZoomablePattern } from '../ZoomablePattern.mjs'
|
||||
import { PatternLayout } from '../PatternLayout.mjs'
|
||||
//import { MovablePattern } from 'shared/components/workbench/pattern/movable/index.mjs'
|
||||
//import { PrintMenu, ns as menuNs } from './menu.mjs'
|
||||
//import { PatternWithMenu, ns as wrapperNs } from '../pattern-with-menu.mjs'
|
||||
|
||||
export const LayoutView = (props) => {
|
||||
// design,
|
||||
// pattern,
|
||||
// patternConfig,
|
||||
// settings,
|
||||
// setSettings,
|
||||
// ui,
|
||||
// update,
|
||||
// language,
|
||||
// account,
|
||||
// Design,
|
||||
//}) => {
|
||||
|
||||
const { config, state, update, Design } = props
|
||||
const defaultSettings = defaultPrintSettings(state.settings?.units)
|
||||
|
||||
// Settings for the tiler plugin
|
||||
const pageSettings = {
|
||||
...defaultSettings,
|
||||
...get(state.ui, printSettingsPath, {}),
|
||||
}
|
||||
|
||||
/*
|
||||
* Now draft the pattern
|
||||
*/
|
||||
const { pattern, failure, errors } = draft(Design, state.settings, [tilerPlugin(pageSettings)])
|
||||
if (failure) return <p>Draft failed. FIXME: Handle this gracefully.</p>
|
||||
|
||||
const renderProps = pattern.getRenderProps()
|
||||
|
||||
const exportIt = () => {
|
||||
update.startLoading('layout', { msg: 'Generating PDF' })
|
||||
handleExport({
|
||||
format: pageSettings.size,
|
||||
settings,
|
||||
design,
|
||||
t,
|
||||
Design,
|
||||
ui,
|
||||
startLoading: loading.startLoading,
|
||||
stopLoading: loading.stopLoading,
|
||||
onComplete: () => {
|
||||
setLoadingStatus([true, 'pdfReady', true, true])
|
||||
},
|
||||
onError: (err) => {
|
||||
setLoadingStatus([true, 'pdfFailed', true, true])
|
||||
console.log(err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const output = (
|
||||
<MovablePattern
|
||||
{...{
|
||||
renderProps,
|
||||
update,
|
||||
immovable: ['pages'],
|
||||
layoutPath: ['layouts', 'print'],
|
||||
showButtons: !ui.hideMovableButtons,
|
||||
}}
|
||||
/>
|
||||
//<ZoomablePattern
|
||||
// renderProps={renderProps}
|
||||
// patternLocale="en"
|
||||
// rotate={state.ui.rotate}
|
||||
///>
|
||||
)
|
||||
|
||||
return <PatternLayout {...{ update, Design, output, state, pattern, config }} />
|
||||
|
||||
return (
|
||||
<>
|
||||
<PatternWithMenu
|
||||
noHeader
|
||||
{...{
|
||||
settings,
|
||||
ui,
|
||||
update,
|
||||
control: account.control,
|
||||
account,
|
||||
design,
|
||||
setSettings,
|
||||
title: (
|
||||
<h2 className="text-center lg:text-left capitalize">{t('workbench:printLayout')}</h2>
|
||||
),
|
||||
pattern: (
|
||||
<MovablePattern
|
||||
{...{
|
||||
renderProps,
|
||||
update,
|
||||
immovable: ['pages'],
|
||||
layoutPath: ['layouts', 'print'],
|
||||
showButtons: !ui.hideMovableButtons,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
menu: (
|
||||
<PrintMenu
|
||||
{...{
|
||||
design,
|
||||
pattern,
|
||||
patternConfig,
|
||||
setSettings,
|
||||
settings,
|
||||
ui,
|
||||
update,
|
||||
language,
|
||||
account,
|
||||
exportIt,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -6,6 +6,7 @@ import { DraftView } from './DraftView.mjs'
|
|||
import { SaveView } from './SaveView.mjs'
|
||||
import { ExportView } from './ExportView.mjs'
|
||||
import { UndosView } from './UndosView.mjs'
|
||||
//import { LayoutView } from './LayoutView.mjs'
|
||||
import { ErrorIcon } from '@freesewing/react/components/Icon'
|
||||
import {
|
||||
OptionsIcon,
|
||||
|
@ -33,7 +34,7 @@ const viewIcons = {
|
|||
measurements: MeasurementsIcon,
|
||||
test: BeakerIcon,
|
||||
timing: GaugeIcon,
|
||||
printLayout: PrintIcon,
|
||||
layout: PrintIcon,
|
||||
save: SaveIcon,
|
||||
export: ExportIcon,
|
||||
editSettings: EditIcon,
|
||||
|
@ -57,6 +58,7 @@ export const View = (props) => {
|
|||
if (view === 'save') return <SaveView {...props} />
|
||||
if (view === 'export') return <ExportView {...props} />
|
||||
if (view === 'undos') return <UndosView {...props} />
|
||||
//if (view === 'layout') return <LayoutView {...props} />
|
||||
/*
|
||||
viewComponents: {
|
||||
draft: 'DraftView',
|
||||
|
@ -65,7 +67,7 @@ export const View = (props) => {
|
|||
export: 'ViewPicker',
|
||||
measurements: 'MeasurementsView',
|
||||
undos: 'UndosView',
|
||||
printLayout: 'ViewPicker',
|
||||
layout: 'ViewPicker',
|
||||
editSettings: 'ViewPicker',
|
||||
docs: 'ViewPicker',
|
||||
inspect: 'ViewPicker',
|
||||
|
@ -106,7 +108,7 @@ export const viewLabels = {
|
|||
t: 'Time Design',
|
||||
d: 'Shows detailed timing of the pattern being drafted, allowing you to find bottlenecks in performance',
|
||||
},
|
||||
printLayout: {
|
||||
layout: {
|
||||
t: 'Pattern Layout',
|
||||
d: 'Organize your pattern parts to minimize paper use',
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@ export const defaultConfig = {
|
|||
cloudImageVariants: ['public', 'sq100', 'sq200', 'sq500', 'w200', 'w500', 'w1000', 'w2000'],
|
||||
// Views
|
||||
mainViews: ['draft', 'designs', 'save', 'export'],
|
||||
extraViews: ['measurements', 'undos', 'printLayout', 'docs'],
|
||||
extraViews: ['measurements', 'undos', 'layout', 'docs'],
|
||||
devViews: ['editSettings', 'inspect', 'logs', 'test', 'timing'],
|
||||
utilViews: ['picker'],
|
||||
measurementsFreeViews: ['designs', 'measurements', 'docs', 'picker'],
|
||||
|
@ -36,7 +36,7 @@ export const defaultConfig = {
|
|||
export: 'ViewPicker',
|
||||
measurements: 'MeasurementsView',
|
||||
undos: 'UndosView',
|
||||
printLayout: 'ViewPicker',
|
||||
layout: 'ViewPicker',
|
||||
editSettings: 'ViewPicker',
|
||||
docs: 'ViewPicker',
|
||||
inspect: 'ViewPicker',
|
||||
|
@ -97,7 +97,7 @@ export const defaultConfig = {
|
|||
measurements: 1,
|
||||
test: 4,
|
||||
timing: 4,
|
||||
printLayout: 2,
|
||||
layout: 2,
|
||||
export: 1,
|
||||
save: 1,
|
||||
undos: 2,
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
# Popout component
|
||||
comment: Comment
|
||||
note: Note
|
||||
tip: Tip
|
||||
warning: Warning
|
||||
fixme: FIXME
|
||||
link: Link
|
||||
related: Related
|
||||
|
||||
# Designs view
|
||||
chooseADesign: Choose a Design
|
||||
# Mesurements view
|
||||
measurements: Measurements
|
||||
measurementsAreOk: We have all required measurements to draft this pattern.
|
||||
editMeasurements: Edit Measurements
|
||||
editMeasurementsDesc: You can manually set or override measurements below.
|
||||
requiredMeasurements: Required Measurements
|
||||
optionalMeasurements: Optional Measurements
|
||||
missingMeasurements: Missing Measurements
|
||||
missingMeasurementsInfo: To generate this pattern, we need the following additional measurements
|
||||
missingMeasurementsNotify: To generate this pattern, we need some additional measurements
|
||||
|
||||
# Measurements sets
|
||||
noOwnSets: You do not have any of your own measurements sets (yet)
|
||||
pleaseMtm: Because our patterns are bespoke, we strongly suggest you take accurate measurements.
|
||||
noOwnSetsMsg: You can store your measurements as a measurements set, after which you can generate as many patterns as you want for them.
|
||||
chooseFromOwnSets: Choose one of your own measurements sets
|
||||
chooseFromOwnSetsDesc: Pick any of your own measurements sets that have all required measurements to generate this pattern.
|
||||
newSet: Create a new measurements set
|
||||
someSetsLacking: Some of your measurements sets lack the measurements required to generate this pattern
|
||||
chooseFromBookmarkedSets: Choose one of the measurements sets you've bookmarked
|
||||
chooseFromBookmarkedSetsDesc: If you've bookmarked any measurements sets, you can select from those too.
|
||||
chooseFromCuratedSets: Choose one of FreeSewing's curated measurements sets
|
||||
chooseFromCuratedSetsDesc: If you're just looking to try out our platform, you can select from our list of curated measurements sets.
|
||||
|
||||
# View wrapper
|
||||
requiredPropsMissing.t: Required props are missing
|
||||
requiredPropsMissing.d: The FreeSewing pattern editor needs to be initialized properly. It currently lacks some props to be able to bootstrap.
|
||||
|
||||
# View picker
|
||||
chooseAnActivity: Choose an activity
|
||||
chooseAnotherActivity: Choose a different activity
|
||||
view.draft.t: Draft Pattern
|
||||
view.draft.d: Choose this if you are not certain what to pick
|
||||
view.measurements.t: Pattern Measurements
|
||||
view.measurements.d: Update or load measurements to generate a pattern for
|
||||
view.test.t: Test Design
|
||||
view.test.d: See how different options or changes in measurements influence the design
|
||||
view.timing.t: Time Design
|
||||
view.timing.d: Shows detailed timing of the pattern being drafted, allowing you to find bottlenecks in performance
|
||||
view.printLayout.t: Print Layout
|
||||
view.printLayout.d: Organize your pattern parts to minimize paper use
|
||||
view.save.t: Save pattern as...
|
||||
view.save.d: Save the changes to this pattern in your account, or save it as a new pattern
|
||||
view.export.t: Export Pattern
|
||||
view.export.d: Export this pattern into a variety of formats
|
||||
view.editSettings.t: Edit settings by hand
|
||||
view.editSettings.d: Throw caution to the wind, and hand-edit the pattern's settings
|
||||
view.logs.t: Pattern Logs
|
||||
view.logs.d: Show the logs generated by the pattern, useful to troubleshoot problems
|
||||
view.inspect.t: Pattern inspector
|
||||
view.inspect.d: Load the pattern in the inspector, giving you in-depth info about a pattern's components
|
||||
view.docs.t: Documentation
|
||||
view.docs.d: More information and links to documentation
|
||||
view.designs.t: Choose a different Design
|
||||
view.designs.d: "Current design: {- design }"
|
||||
view.picker.t: Choose a different view
|
||||
view.undos.t: Undo History
|
||||
view.undos.d: Time-travel through your recent pattern changes
|
||||
showAdvancedOptions: Show advanced options
|
||||
hideAdvancedOptions: Hide advanced options
|
||||
views.t: Views
|
||||
views.d: Choose between the main views of the pattern editor
|
||||
measurementsFreeViewsOnly.t: We are only showing activities that do not require measurements
|
||||
measurementsFreeViewsOnly.d: Once we have all measurements required to generate a pattern, you will have more choices here.
|
||||
|
||||
|
||||
# menus
|
||||
youAreUsingTheDefaultValue: You are using the default value
|
||||
youAreUsingACustomValue: You are using a custom value
|
||||
designOptions.t: Design Options
|
||||
designOptions.d: These options are specific to this design. You can use them to customize your pattern in a variety of ways.
|
||||
fit.t: Fit
|
||||
style.t: Style
|
||||
advanced.t: Advanced
|
||||
coreSettings.t: Core Settings
|
||||
coreSettings.d: These settings are not specific to the design, but instead allow you to customize various parameters of the FreeSewing core library, which generates the design for you.
|
||||
paperless.t: Paperless
|
||||
paperless.d: Trees are awesome, and taping together sewing patterns is not much fun. Try our paperless mode to avoid the need to print out your pattern altogether.
|
||||
samm.t: Seam Allowance Size
|
||||
samm.d: Controls the amount of seam allowance used in your pattern
|
||||
sabool.t: Include Seam Allowance
|
||||
sabool.d: Controls whether or not to include seam allowance in your pattern
|
||||
complete.t: Details
|
||||
complete.d: Controls how detailed the pattern is; Either a complete pattern with all details, or a basic outline of the pattern parts
|
||||
expand.t: Expand
|
||||
expand.d: Controls efforts to save paper. Disable this to expand all pattern parts at the cost of using more space.
|
||||
only.t: Included Parts
|
||||
only.d: Use this to control exactly which pattern parts will be included in your pattern
|
||||
units.t: Units
|
||||
units.d: This setting determines how unit are displayed on your pattern
|
||||
margin.t: Margin
|
||||
margin.d: Controls the margin around pattern parts
|
||||
scale.t: Scale
|
||||
scale.d: Controls the overall line width, font size, and other elements that do not scale with the pattern's measurements
|
||||
yes: Yes
|
||||
no: No
|
||||
completeYes.t: Generate a complete pattern
|
||||
completeYes.d: This will generate a complete pattern with all notations, lines, markings. Use this if you are not certain what to choose.
|
||||
completeNo.t: Generate a pattern outline
|
||||
completeNo.d: Only generate the outline of the pattern parts. Use this if you are looking to use a laser cutter or have other specific needs.
|
||||
expandYes.t: Expand all pattern parts
|
||||
expandYes.d: This will generate a pattern where all pattern parts are drawn to their full size, even if they are simple rectangles.
|
||||
expandNo.t: Keep patterns parts compact where possible
|
||||
expandNo.d: This will draw a more dense representation of the pattern which includes all info without using up too much space & paper.
|
||||
saNo.t: Do not include seam allowance
|
||||
saNo.d: This generates a pattern which does not include any seam allowance. The size of the seam allowance does not matter as no seam allowance will be included.
|
||||
saYes.t: Include seam allowance
|
||||
saYes.d: This generates a pattern that will include seam allowance. The size of the seam allowance is set individually.
|
||||
paperlessNo.t: Generate a regular pattern
|
||||
paperlessNo.d: This will generate a regular pattern, which you can then print out.
|
||||
paperlessYes.t: Generate a paperless pattern
|
||||
paperlessYes.d: This generates a pattern with dimensions and a grid, which allows you to transfer it on fabric or another medium without the need to print out the pattern.
|
||||
metric: Metric
|
||||
uiPreferences.t: UI Preferences
|
||||
uiPreferences.d: These preferences control the UI (User Interface) of the pattern editor
|
||||
renderer.t: Render Engine
|
||||
renderer.d: Controls how the pattern is rendered (drawn) on the screen
|
||||
renderWithReact.t: Render with FreeSewing's React components
|
||||
renderWithReact.d: Render as SVG through our React components. Allows interactivity and is optimized for screen. Use this if you are not sure what to pick.
|
||||
renderWithCore.t: Render with FreeSewing's Core library
|
||||
renderWithCore.d: Render directly to SVG from Core. Allows no interactivity and is optimized for print. Use this if you want to know what it will look like when exported.
|
||||
kiosk.t: Kiosk Mode
|
||||
kiosk.d: Controls how the pattern editor is embedded in the web page.
|
||||
aside.t: Aside Menu
|
||||
aside.d: Controls whether or not to show menus on the side of larger screens
|
||||
withAside.t: Also show menus on the side
|
||||
withAside.d: Shows menus both on the side of the screen, as well as the drop-downs in the header (this only applies to larger screens)
|
||||
noAside.t: Only show menus in the header
|
||||
noAside.d: Only shows the drop-down variant of the menus, making more room for your pattern
|
||||
rotate.t: Rotate pattern
|
||||
rotate.d: Allows you to rotate your pattern 90 degrees to better fit your screen
|
||||
rotateNo.t: Do not rotate pattern
|
||||
rotateNo.d: Show the pattern as it is
|
||||
rotateYes.t: Rotate pattern 90 degrees
|
||||
rotateYes.d: Rotate the pattern 90 degrees counter clockwise
|
||||
websiteMode.t: Use inline mode
|
||||
websiteMode.d: Embeds the pattern editor in the natural flow of the web page.
|
||||
kioskMode.t: Use kiosk mode
|
||||
kioskMode.d: Breaks out the pattern editor to fill the entire page.
|
||||
ux.t: User Experience
|
||||
ux.d: Which user experience do you prefer? From keep it simple, to give me all the powers.
|
||||
inspect.t: Inspect
|
||||
inspect.d: Enabling this will allow you to drill down into the pattern, and pull up information about its various parts, paths, and points.
|
||||
inspectNo.t: Disable the inspector
|
||||
inspectNo.d: This is the default, the pattern inspector is disabled and the pattern is displayed as usual.
|
||||
inspectYes.t: Enable the inspector
|
||||
inspectYes.d: With the pattern inspector enabled and the React rendering engine selected, we will add interactivity to the pattern to allow you to inspect the various elements that make up the pattern.
|
||||
draft: Draft
|
||||
test: Test
|
||||
print: Print layout
|
||||
cut: Cut Layout
|
||||
save: Save
|
||||
export: Export
|
||||
edit: Edit
|
||||
draft.t: Draft your pattern
|
||||
draft.d: Launches FreeSewing flagship pattern editor, where you can tweak your pattern to your heart's desire
|
||||
test.t: Test your pattern
|
||||
test.d: See how your pattern adapts to changes in options, or measurements
|
||||
print.t: Print Layout
|
||||
print.d: Allows you to arrange your pattern pieces so you can printing your pattern on as little pages as possible
|
||||
cut.t: Cutting layout
|
||||
cut.d: Allows you to arrange your pattern pieces so you can determine exactly how much fabric you need to make it.
|
||||
save.t: Save your pattern
|
||||
save.d: Save the current pattern to your FreeSewing account
|
||||
export.t: Export your pattern
|
||||
export.d: Allows you to export this pattern to a variety of formats
|
||||
logs.t: Pattern logs
|
||||
enterCustomValue: Enter a custom value
|
||||
|
||||
# ux
|
||||
ux1.t: Keep it as simple as possible
|
||||
ux1.d: Hides all but the most essential features.
|
||||
ux2.t: Keep it simple, but not too simple
|
||||
ux2.d: Hides the majority of features.
|
||||
ux3.t: Balance simplicity with power
|
||||
ux3.d: Reveals the majority of features, but not all.
|
||||
ux4.t: Give me all powers, but keep me safe
|
||||
ux4.d: Reveals all features, keeps handrails and safety checks.
|
||||
ux5.t: Get out of my way
|
||||
ux5.d: Reveals all features, removes all handrails and safety checks.
|
||||
|
||||
# Tooltips
|
||||
tt.changeEditorView: Change to a different view
|
||||
tt.toggleSa: Turns Seam Allowance on or off (see Core Settings)
|
||||
tt.togglePaperless: Turns Paperless on or off (see Core Settings)
|
||||
tt.toggleComplete: Turns Details on or off (see Core Settings)
|
||||
tt.toggleExpand: Turns Expand on or off (see Core Settings)
|
||||
tt.toggleUnits: Switches Units between metric and imperial (see Core Settings)
|
||||
tt.changeUx: Changes your UX setting (see UI Preferences)
|
||||
tt.toggleAside: Turn the Aside Menu on or off (see UI Preferences)
|
||||
tt.toggleKiosk: Turns Kiosk Mode on or off (see UI Preferences)
|
||||
tt.toggleRotate: Turns Rotate Pattern on or off (see UI Preferences)
|
||||
tt.toggleRenderer: Switches the Render Engine between React and SVG (see UI Preferences)
|
||||
tt.exportPattern: Export pattern
|
||||
tt.savePattern: Save pattern
|
||||
tt.savePatternAs: Save pattern as...
|
||||
tt.undo: Undo most recent change
|
||||
tt.undoAll: Undo all changes since the last save point
|
||||
tt.resetDesign: Reset all settings, but keep the design and measurements
|
||||
tt.resetAll: Reset the editor completely
|
||||
|
||||
# flags
|
||||
apply: Apply
|
||||
decrease: Decrease
|
||||
disable: Disable
|
||||
dismiss: Dismiss
|
||||
expandIsOff.t: This design saves space (and trees) because expand is disabled
|
||||
expandIsOff.d: "Because the **expand** core setting is currently disabled, some parts are not fully drawn or not shown at all. Typically, these are simple rectangles that only take up space, or things that can be cut on the fold. \n\nTo expand all pattern parts to their full size, enable the expand setting."
|
||||
expandIsOn.t: This design can save space (and trees)
|
||||
expandIsOn.d: "Because the **expand** core setting is currently enabled, all parts are fully drawn. You can display this design in a more compact way by disabling the **expand** setting. \n\nDoing so will mean that some parts are not fully drawn or not shown at all. Typically, these are simple rectangles that only take up space, or things that can be cut on the fold."
|
||||
enable: Enable
|
||||
flags: Flags
|
||||
flagMenu.t: Flags
|
||||
flagMenuOne.d: A specific issue about your current pattern needs your attention.
|
||||
flagMenuMany.d: Some issues about your current pattern need your attention.
|
||||
hide: Hide
|
||||
increase: Increase
|
||||
show: Show
|
||||
saIncluded: (This includes seam allowance)
|
||||
saExcluded: (This does not include seam allowance)
|
||||
saUnused: (This part does not require any seam allowance)
|
||||
partHiddenByExpand: This part is not shown because the **expand** core setting is currently disabled. Enable it to show this pattern part.
|
||||
|
||||
# Auth
|
||||
authRequired: Authentication required
|
||||
membersOnly: This functionality requires a FreeSewing account.
|
||||
signUp: Sign Up
|
||||
signIn: Sign In
|
||||
statusUnknown: Account status warning
|
||||
statusUnknownMsg: Your account status prohibits us from processing your data. Please contact support.
|
||||
consentLacking: Consent lacking
|
||||
consentLackingMsg: We do not have your consent for processing your data. Without consent, we have no legal basis to process your data.
|
||||
accountProhibited: Your account has been disabled
|
||||
accountProhibitedMsg: Your account has been administratively disabled.
|
||||
accountDisabled: Account disabled
|
||||
accountDisabledMsg: You cannot re-enable a disabled account. You need to contact support to address this.
|
||||
accountInactive: Your account is inactive
|
||||
accountInactiveMsg: You must activate your account via the signup link we sent you.
|
||||
signupAgain: If you cannot find the link, you can receive a new one by signing up again.
|
||||
cannotUse: A disabled account cannot be used.
|
||||
contactSupport: Contact support
|
||||
reviewConsent: Review your consent
|
||||
roleLacking: You lack the required role to access this content
|
||||
roleLackingMsg: This content requires the <b>{ requiredRole }</b> role. Your role is <b>{ role }</b> which does not grant you access to this content.
|
||||
|
||||
# save pattern
|
||||
bookmarkPattern: Bookmark pattern
|
||||
savePattern: Save pattern
|
||||
saveAsNewPattern: Save as a New Pattern
|
||||
savePatternAs: Save pattern as...
|
||||
savePatternAsHellip: Save pattern as...
|
||||
patternBookmarkCreated: Pattern bookmark created
|
||||
see: See
|
||||
addNotes: Add notes
|
||||
addSettingsToNotes: Add settings to notes
|
||||
exporting: Exporting
|
||||
exportAsData: Export as data
|
||||
exportForEditing: Export for editing
|
||||
exportForPrinting: Export for printing
|
||||
exportPattern-txt: Export a PDF suitable for your printer, or download this pattern in a variety of formats
|
||||
exportPattern: Export pattern
|
||||
settings: Settings
|
||||
patternTitle: Pattern Title
|
||||
patternNotes: Pattern Notes
|
||||
toAccessPatternsGoTo: To access your patterns, go to
|
||||
genericLoadingMessage: Hang tight, we're working on it...
|
||||
patternSavedAs: Pattern saved as
|
||||
cancel: Cancel
|
||||
|
||||
# undo history
|
||||
secondsAgo: seconds ago
|
||||
minutesAgo: minutes ago
|
||||
hoursAgo: hours ago
|
||||
undos.unknown.t: Unknown Change
|
||||
defaultRestored: Cleared (default restored)
|
||||
includeAllParts: Include all parts
|
||||
allFirstLetter: A
|
||||
undo: Undo
|
||||
xMeasurementsChanged: "{ count } Measurements changed"
|
|
@ -22,10 +22,10 @@ export function menuDesignOptionsStructure(design, options, settings, asFullList
|
|||
sorted[name] = {
|
||||
...option,
|
||||
name,
|
||||
title: i18n[design].en.o[name].t,
|
||||
title: i18n[design]?.en?.o?.[name]?.t || name,
|
||||
about: (
|
||||
<span>
|
||||
{i18n[design].en.o[name].d}
|
||||
{i18n[design]?.en?.o?.[name]?.d || name}
|
||||
<DesignDocsLink item={name} design={design} />
|
||||
</span>
|
||||
),
|
||||
|
|
|
@ -20,12 +20,15 @@ import { Spinner } from '@freesewing/react/components/Spinner'
|
|||
*
|
||||
* @param {function} Design - The Design constructor
|
||||
* @param {object} settings - The settings for the pattern
|
||||
* @param {array} plugins - Any (extra) plugins to load into the pattern
|
||||
* @return {object} data - The drafted pattern, along with errors and failure data
|
||||
*/
|
||||
export function draft(Design, settings) {
|
||||
export function draft(Design, settings, plugins = []) {
|
||||
const pattern = new Design(settings)
|
||||
for (const plugin of plugins) pattern.use(plugin)
|
||||
const data = {
|
||||
// The pattern
|
||||
pattern: new Design(settings),
|
||||
pattern,
|
||||
// Any errors logged by the pattern
|
||||
errors: [],
|
||||
// If the pattern fails to draft, this will hold the error
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# Popout component
|
||||
comment: Opmerking
|
||||
note: Notitie
|
||||
tip: Tip
|
||||
warning: Waarschuwing
|
||||
fixme: FIXME
|
||||
link: Link
|
||||
related: Gerelateerd
|
||||
|
||||
# Mesurements view
|
||||
measurements: Maten
|
||||
measurementsAreOk: We hebben alle benodigde maten om dit patroon te tekenen.
|
||||
editMeasurements: Maten Aanpassen
|
||||
editMeasurementsDesc: Hier kan je manueel de maten aanpassen.
|
||||
requiredMeasurements: Vereiste Maten
|
||||
optionalMeasurements: Optionele Maten
|
||||
|
||||
# Designs view
|
||||
pickADesign: Kies een ontwerp
|
||||
|
||||
# View wrapper
|
||||
requiredPropsMissing.t: Vereiste props ontbreken
|
||||
requiredPropsMissing.d: De FreeSewing patroon editor moet correct geinitialiseerd worden. Momenteel ontbreken een aantal props die noodzakelijk zijn om de editor te starten.
|
|
@ -1,26 +0,0 @@
|
|||
# List of props that can be passed to the pattern editor
|
||||
|
||||
| Prop | Default | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| `design` | `undefined` | Name of the current design (key in the `objects` prop).<br>Note that this will set the initial state, but it can be changed by the user. |
|
||||
| `designs` | `{}` |Object holding all designs that are available. |
|
||||
| `locale` | `en` | Language code |
|
||||
| `imperial`| `false` | Whether to use imperial units as the default, or not |
|
||||
| `components` | `{}` | Object holding swizzled components |
|
||||
| `hooks` | `{}` | Object holding swizzled hooks |
|
||||
| `methods` | `{}` | Object holding swizzled methods |
|
||||
|
||||
|
||||
## Defaults object
|
||||
|
||||
```mjs
|
||||
{
|
||||
locale: 'en',
|
||||
imperial: 'false',
|
||||
ui: {
|
||||
renderer: 'react',
|
||||
kiosk: false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -2,7 +2,14 @@ import React, { useState, useContext } from 'react'
|
|||
import { CopyToClipboard as Copy } from 'react-copy-to-clipboard'
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
export const KeyVal = ({ k, val, color = 'primary', small = false, href = false }) => {
|
||||
export const KeyVal = ({
|
||||
k,
|
||||
val,
|
||||
color = 'primary',
|
||||
small = false,
|
||||
href = false,
|
||||
onClick = false,
|
||||
}) => {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||
|
||||
|
@ -24,21 +31,32 @@ export const KeyVal = ({ k, val, color = 'primary', small = false, href = false
|
|||
|
||||
if (href) return <LinkKeyVal {...{ k, val, color, small, href, colorClasses1, colorClasses2 }} />
|
||||
|
||||
return (
|
||||
const inner = (
|
||||
<>
|
||||
<span
|
||||
className={`${sharedClasses} ${small ? 'tw-rounded-l' : 'tw-rounded-l-lg'} ${colorClasses1} ${small ? 'tw-text-xs' : ''} tw-pr-0.5`}
|
||||
>
|
||||
{k}
|
||||
</span>
|
||||
<span
|
||||
className={`${sharedClasses} ${small ? 'tw-rounded-r' : 'tw-rounded-r-lg'} ${colorClasses2} ${small ? 'tw-text-xs' : ''} tw-pl-0.5`}
|
||||
>
|
||||
{val}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
|
||||
return onClick === false ? (
|
||||
<Copy text={val} onCopy={() => (noCopy ? null : handleCopied(setCopied, setLoadingStatus, k))}>
|
||||
<button className="tw-daisy-btn-ghost tw-p-0">
|
||||
<span
|
||||
className={`${sharedClasses} ${small ? 'tw-rounded-l' : 'tw-rounded-l-lg'} ${colorClasses1} ${small ? 'tw-text-xs' : ''} tw-pr-0.5`}
|
||||
>
|
||||
{k}
|
||||
</span>
|
||||
<span
|
||||
className={`${sharedClasses} ${small ? 'tw-rounded-r' : 'tw-rounded-r-lg'} ${colorClasses2} ${small ? 'tw-text-xs' : ''} tw-pl-0.5`}
|
||||
>
|
||||
{val}
|
||||
</span>
|
||||
</button>
|
||||
<button className="tw-daisy-btn-ghost tw-p-0">{inner}</button>
|
||||
</Copy>
|
||||
) : (
|
||||
<button
|
||||
className="tw-daisy-btn-ghost tw-p-0"
|
||||
onClick={typeof onClick === 'function' ? onClick : null}
|
||||
>
|
||||
{inner}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue