diff --git a/sites/shared/components/workbench/menus/core-settings/index.mjs b/sites/shared/components/workbench/menus/core-settings/index.mjs index 7c754ab44ce..972006d4758 100644 --- a/sites/shared/components/workbench/menus/core-settings/index.mjs +++ b/sites/shared/components/workbench/menus/core-settings/index.mjs @@ -224,9 +224,9 @@ export const CoreSettings = ({ return ( <> -
+
{control > 4 ? ( -
+
) : ( <>
diff --git a/sites/shared/components/workbench/menus/design-options/index.mjs b/sites/shared/components/workbench/menus/design-options/index.mjs index ca73977ec14..cf5feb3e0cf 100644 --- a/sites/shared/components/workbench/menus/design-options/index.mjs +++ b/sites/shared/components/workbench/menus/design-options/index.mjs @@ -246,7 +246,7 @@ export const DesignOptions = ({ return ( <> -
+
{control > 4 ? null : ( <>
diff --git a/sites/shared/components/workbench/menus/ui-settings/index.mjs b/sites/shared/components/workbench/menus/ui-settings/index.mjs index cdf58ba7061..a03e977a7bb 100644 --- a/sites/shared/components/workbench/menus/ui-settings/index.mjs +++ b/sites/shared/components/workbench/menus/ui-settings/index.mjs @@ -103,12 +103,16 @@ export const Setting = ({ name, config, current, update, t, changed, loadDocs, c if (control > 4) { // Save gurus some clicks - if (name === 'renderer') + const onClick = { + renderer: () => (current === 'svg' ? update.ui([name]) : update.ui([name], 'svg')), + inspect: () => (current === 1 ? update.ui([name]) : update.ui([name], 1)), + } + if (['renderer', 'inspect'].includes(name)) return ( } - onClick={() => (current === 'svg' ? update.ui([name]) : update.ui([name], 'svg'))} + onClick={onClick[name]} /> ) } @@ -149,9 +153,9 @@ export const UiSettings = ({ design, update, settings, ui, control, language, Dy return ( <> -
+
{control > 4 ? ( -
+
) : ( <>
diff --git a/sites/shared/components/workbench/pattern/path.mjs b/sites/shared/components/workbench/pattern/path.mjs deleted file mode 100644 index a91cbdebbc3..00000000000 --- a/sites/shared/components/workbench/pattern/path.mjs +++ /dev/null @@ -1,608 +0,0 @@ -// Dependencies -import { getProps } from './utils.mjs' -import { round, formatMm, getId } from 'shared/utils.mjs' -// Hooks -import { useTranslation } from 'next-i18next' -// Components -import { TextOnPath } from './text.mjs' -import { RawSpan } from 'shared/components/raw-span.mjs' - -export const pointCoords = (point) => - point ? `[ ${round(point.x, 2)}, ${round(point.y, 2)} ]` : null - -export const Tr = ({ children }) => {children} -export const KeyTd = ({ children }) => {children}: -export const ValTd = ({ children }) => {children} - -export const TextAlongPath = ({ id, size, txt }) => ( - - - - {txt} - - - -) -export const PointCircle = ({ point, size, className = 'stroke-neutral-content' }) => ( - -) - -const CpCircle = ({ point, className = 'fill-lining no-stroke' }) => ( - -) - -const EpCircle = ({ point, className = 'fill-note no-stroke' }) => ( - -) - -const pathDimensions = (from, to, cp1 = false, cp2 = false, path = false) => { - const topLeft = { - x: to.x < from.x ? to.x : from.x, - y: to.y < from.y ? to.y : from.y, - } - const bottomRight = { - x: to.x > from.x ? to.x : from.x, - y: to.y > from.y ? to.y : from.y, - } - let bbox = false - // Deal with curves - if (cp1 && cp2) { - if (cp1.x < topLeft.x) topLeft.x = cp1.x - if (cp2.x < topLeft.x) topLeft.x = cp2.x - if (cp1.x > bottomRight.x) bottomRight.x = cp1.x - if (cp2.x > bottomRight.x) bottomRight.x = cp2.x - if (cp1.y < topLeft.y) topLeft.y = cp1.y - if (cp2.y < topLeft.y) topLeft.y = cp2.y - if (cp1.y > bottomRight.y) bottomRight.y = cp1.y - if (cp2.y > bottomRight.y) bottomRight.y = cp2.y - // This undocumented core methods returns the curve's bounding box - bbox = path.bbox() - } - const w = bottomRight.x - topLeft.x - const h = bottomRight.y - topLeft.y - const size = w > h ? w : h - - return { - topLeft, - bottomRight, - w, - h, - size, - bbox, - } -} - -export const Defs = () => ( - - - - - - - - -) - -export const svgProps = { - xmlns: 'http://www.w3.org/2000/svg', - xmlnsSvg: 'http://www.w3.org/2000/svg', - xmlnsXlink: 'http://www.w3.org/1999/xlink', - style: { maxHeight: 'inherit', strokeLinecap: 'round', strokeLinejoin: 'round' }, -} - -const Line = ({ path, pathName, partName, i, units }) => { - const ops = path.ops - const from = ops[0].to - const to = ops[1].to - const { topLeft, bottomRight, w, h, size } = pathDimensions(from, to) - const id = `${partName}_${pathName}_${i}` - - const xyProps = { - className: 'stroke-neutral-content', - strokeOpacity: '0.5', - fill: 'none', - strokeWidth: size / 300, - } - - return ( - - - - - - - - - - - ) -} - -const Curve = ({ path, pathName, partName, i }) => { - const ops = path.ops - const from = ops[0].to - const { to, cp1, cp2 } = ops[1] - const { topLeft, w, h, size, bbox } = pathDimensions(from, to, cp1, cp2, path) - const id = `${partName}_${pathName}_${i}` - - const cpProps = { - className: 'stroke-success', - strokeOpacity: '0.85', - fill: 'none', - strokeWidth: size / 300, - } - const xyProps = { - ...cpProps, - strokeOpacity: '0.5', - className: 'stroke-neutral-content', - markerEnd: 'url(#arrowTo)', - markerStart: 'url(#arrowFrom)', - } - - return ( - - - - - - - - - - - - - - - - ) -} - -const MiniPath = ({ path, pathName, partName, units }) => { - const bbox = path.bbox() - const id = `${partName}_${pathName}_mini}` - const w = bbox.bottomRight.x - bbox.topLeft.x - const h = bbox.bottomRight.y - bbox.topLeft.y - const size = w > h ? w : h - - const xyProps = { - fill: 'none', - strokeWidth: size / 300, - strokeOpacity: '0.5', - className: 'stroke-neutral-content', - markerEnd: 'url(#arrowTo)', - markerStart: 'url(#arrowFrom)', - } - - return ( - - - - - - - - - ) - - ) -} - -const lineInfo = ({ path, partName, units, i }) => ( -
-
Line info
-
- - - - From - {pointCoords(path.ops[0].to)} - - - To - {pointCoords(path.ops[1].to)} - - - Length - {formatMm(path.length(), units, 'notags')} - - - Width - - - - - - Height - - - - - - Part - {partName} - - - Draw Op - - {i}/{ops.length} - - - -
-
- -
-
-
-) - -const XrayLine = ({ i, path, partName, units, showInfo }) => ( - <> - showInfo(evt, lineInfo({ path, partName, units, i }))} - /> - - - -) - -const curveInfo = ({ i, path, partName, units }) => ( -
-
Curve info
-
- - - - From - {pointCoords(path.ops[0].to)} - - - Cp1 - {pointCoords(path.ops[1].cp1)} - - - Cp2 - {pointCoords(path.ops[1].cp2)} - - - To - {pointCoords(path.ops[1].to)} - - - Length - - - - - - Width - - - - - - Height - - - - - - Part - {partName} - - - Draw Op - - {i}/{ops.length} - - - -
-
- -
-
-
-) - -export const Attributes = ({ list }) => - list ? ( -
    - {Object.keys(list).map((key) => ( -
  • - {key}: {list[key]} -
  • - ))} -
- ) : null - -export const pathInfo = ({ pathName, path, partName, units, showInfo }) => { - const { t } = useTranslation(['workbench']) - const bbox = path.bbox() - - return ( -
-
{t('pathInfo')}
-
-
- - - - {t('name')} - {pathName} - - - {t('length')} - - - - - - {t('width')} - - - - - - {t('height')} - - - - - - {t('topLeft')} - {pointCoords(bbox.topLeft)} - - - {t('bottomRight')} - {pointCoords(bbox.bottomRight)} - - - {t('part')} - {partName} - - - {t('attributes')} - - - - - -
-
- - -
-
- - - {path.ops.map((op, i) => ( - - {i} - - - - - ))} - -
-
- -
-
-
- ) -} - -const PathOp = ({ op }) => { - if (op.type === 'move') - return ( - - Move to {pointCoords(op.to)} - - ) - else if (op.type === 'line') - return ( - - Line to {pointCoords(op.to)} - - ) - else if (op.type === 'curve') - return ( - - Curve to {pointCoords(op.to)} -
- Cp1: {pointCoords(op.cp1)} -
- Cp2: {pointCoords(op.cp2)} -
- ) - else if (op.type === 'noop') return NOOP - else if (op.type === 'close') return Close - else return FIXME: unknown path operation type: {op.type} -} - -const XrayCurve = ({ i, path, ops, units, showInfo }) => { - const from = path.ops[0].to - const { cp1, cp2, to } = path.ops[1] - - return ( - <> - showInfo(evt, curveInfo({ i, path, partName, units }))} - /> - - - - - - - - ) -} - -const XrayPath = ({ path, pathName, partName, units, showInfo }) => { - const classes = path.attributes.get('class') - if (typeof classes === 'string' && classes.includes('noxray')) return null - const ops = path.divide() - - return ( - - showInfo(evt, pathInfo({ pathName, path, partName, units }))} - markerStart="none" - markerEnd="none" - /> - {ops.length > 0 - ? ops.map((op, i) => - op.ops[1].type === 'curve' ? ( - - ) : ( - - ) - ) - : null} - - ) -} - -export const Path = ({ pathName, path, partName, part, units, showInfo, ui, update }) => { - if (path.hidden) return null - const output = [] - const pathId = 'path-' + partName + '-' + pathName - let d = '' - try { - d = path.asPathstring() - } catch (err) { - // Bail out - console.log(`Failed to generate pathstring for path ${pathId} in part ${partName}`, err) - return null - } - - output.push() - if (path.attributes.get('data-text')) - output.push( - - ) - if (ui.xray?.enabled) output.push() - - return output -} diff --git a/sites/shared/components/workbench/views/draft/index.mjs b/sites/shared/components/workbench/views/draft/index.mjs index 608d904245d..4282603c04d 100644 --- a/sites/shared/components/workbench/views/draft/index.mjs +++ b/sites/shared/components/workbench/views/draft/index.mjs @@ -46,6 +46,7 @@ export const DraftView = ({ setInspect(newInspect) }, data: inspect, + pattern: pattern, } let output = null diff --git a/sites/shared/components/workbench/views/draft/inspector/menu.mjs b/sites/shared/components/workbench/views/draft/inspector/menu.mjs index 84848c051e2..1296c12614e 100644 --- a/sites/shared/components/workbench/views/draft/inspector/menu.mjs +++ b/sites/shared/components/workbench/views/draft/inspector/menu.mjs @@ -28,10 +28,8 @@ export const Inspector = ({ return ( <> -
- {control > 4 ? ( -
- ) : ( +
+ {control > 4 ? null : ( <>
@@ -44,6 +42,7 @@ export const Inspector = ({ {Object.values(inspector.data.show).map((props) => ( ))} + {control > 4 ?
: null} ) } diff --git a/sites/shared/components/workbench/views/draft/inspector/path.mjs b/sites/shared/components/workbench/views/draft/inspector/path.mjs new file mode 100644 index 00000000000..7eb9e0f6d1a --- /dev/null +++ b/sites/shared/components/workbench/views/draft/inspector/path.mjs @@ -0,0 +1,278 @@ +// Components +import { Path as ShowPath, utils } from 'pkgs/react-components/src/index.mjs' +import { Attributes, pointCoords, KeyValTable } from './shared.mjs' +import { round, formatMm } from 'shared/utils.mjs' +import { TrashIcon, PrintIcon, SearchIcon } from 'shared/components/icons.mjs' +import { Path as CorePath } from '@freesewing/core' + +const { withinPartBounds, getId, getProps } = utils + +const CpCircle = ({ point }) => ( + +) + +const EpCircle = ({ point }) => ( + +) + +const InspectCurveOp = ({ op }) => { + const from = op.ops[0].to + const { cp1, cp2, to } = op.ops[1] + + return ( + <> + + + + + + + + ) +} + +const Op = ({ op, len, i }) => ( + + {op.type} + {pointCoords(op.to)} + {op.cp1 ? pointCoords(op.cp1) : ''} + {op.cp2 ? pointCoords(op.cp2) : ''} + {len ? formatMm(len) : ''} + +) + +const Ops = ({ ops, path }) => ( + + + + + + + + + + {path.ops.map((op, i) => { + const len = typeof ops[i - 1] === 'undefined' ? 0 : ops[i - 1].length + return + })} + +
TypeToCp1Cp2Length
+) + +const RevealPath = ({ path, pathName, id, inspector }) => ( + +) + +const InspectOp = ({ stackName, pathName, op, i, inspector, t, path }) => { + const id = utils.getId({ stackName, pathName, settings: { idPrefix: `pathOp-${i}-` } }) + const info = { + id, + title: ( +
+ + {op.ops[1].type}: {pathName} | {stackName} + +
+ ), + buttons: [ + , + ], + openButtons: [ + , + , + , + ], + children: ( + + ), + color: 'secondary', + } + + return ( + <> + {inspector.data.reveal[id] ? ( + + ) : null} + {op.ops[1].type === 'curve' ? : null} + inspector.show(info)} + /> + + ) +} + +const InspectPath = ({ stackName, pathName, path, part, settings, t, inspector }) => { + const classes = path.attributes.list.class + if (typeof classes === 'string' && classes.includes('skip-inspector')) return null + + // Pull the instantiated path from the pattern + const pathObj = [...inspector.pattern.stacks[stackName].parts][0].paths[pathName] + const ops = pathObj.divide().map((p) => { + const result = p.asRenderProps() + result.length = p.length() + const bbox = p.bbox() + + return { + ...result, + ...bbox, + width: bbox.bottomRight.x - bbox.topLeft.x, + height: bbox.bottomRight.y - bbox.topLeft.y, + } + }) + + const id = utils.getId({ stackName, pathName, settings: { idPrefix: 'path-' } }) + const info = { + id, + title: ( +
+ + Path: {pathName} | {stackName} + + {path.ops.length} ops +
+ ), + openTitle: ( + + Path: {pathName} | {stackName} + + ), + buttons: [ + , + ], + openButtons: [ + , + , + , + ], + children: ( + <> + ], + [t('topLeft'), pointCoords(path.topLeft)], + [t('bottomRight'), pointCoords(path.bottomRight)], + [t('width'), formatMm(path.width)], + [t('height'), formatMm(path.height)], + [t('length'), formatMm(pathObj.length())], + ]} + /> + + + ), + color: 'primary', + } + + return ( + + {inspector.data.reveal[id] ? ( + + ) : null} + inspector.show(info)} + markerStart="none" + markerEnd="none" + /> + {ops.map((op, i) => ( + + ))} + + ) +} + +export const Path = ({ + stackName, + pathName, + part, + path, + settings, + components, + t, + ui, + update, + inspector, +}) => ( + <> + + + +) diff --git a/sites/shared/components/workbench/views/draft/inspector/pattern.mjs b/sites/shared/components/workbench/views/draft/inspector/pattern.mjs index 8e39d1adc01..dba80eb351a 100644 --- a/sites/shared/components/workbench/views/draft/inspector/pattern.mjs +++ b/sites/shared/components/workbench/views/draft/inspector/pattern.mjs @@ -4,6 +4,7 @@ import { useTranslation } from 'next-i18next' // Components import { PanZoomPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs' import { Point } from './point.mjs' +import { Path } from './path.mjs' export const InspectorPattern = (props) => { const { t } = useTranslation(['workbench']) @@ -11,6 +12,7 @@ export const InspectorPattern = (props) => { const components = { Point: (props) => , + Path: (props) => , } return diff --git a/sites/shared/components/workbench/views/draft/inspector/point.mjs b/sites/shared/components/workbench/views/draft/inspector/point.mjs index 0ed904db60d..0a457fc1c82 100644 --- a/sites/shared/components/workbench/views/draft/inspector/point.mjs +++ b/sites/shared/components/workbench/views/draft/inspector/point.mjs @@ -1,10 +1,6 @@ -// Hooks -import { useState, useEffect, useContext } from 'react' -// Context -import { ModalContext } from 'shared/context/modal-context.mjs' // Components import { Point as ShowPoint, Text, Circle, utils } from 'pkgs/react-components/src/index.mjs' -import { Tr, KeyTd, ValTd, Attributes, pointCoords, useInfoLoader, KeyValTable } from './shared.mjs' +import { Attributes, pointCoords, KeyValTable } from './shared.mjs' import { round } from 'shared/utils.mjs' import { TrashIcon, PrintIcon, SearchIcon } from 'shared/components/icons.mjs' @@ -142,21 +138,7 @@ const InspectPoint = ({ ) } -/* - title, - openTitle = false, - children = [], - buttons = [], - top = true, - bottom = false, - color = 'primary', - opened = false, - toggle = false, - toggleClasses = '', - onClick = false, - openButtons = null, - className = '', -*/ + export const Point = ({ stackName, pointName, @@ -169,19 +151,12 @@ export const Point = ({ update, inspector, }) => { - const showInfo = useInfoLoader() - // Don't include parts outside the part bounding box if (!withinPartBounds(point, part)) return null - const pointProps = { stackName, pointName, part, point, settings, components, t } - return ( <> - + { } export const KeyValTable = ({ rows }) => ( - +
{rows.map((row) => ( - - + + ))} @@ -31,7 +31,7 @@ export const KeyValTable = ({ rows }) => ( ) export const pointCoords = (point) => - point ? `[ ${round(point.x, 2)}, ${round(point.y, 2)} ]` : null + point ? `[${round(point.x, 1)}, ${round(point.y, 1)}]` : null export const Tr = ({ children }) => {children} export const KeyTd = ({ children }) => diff --git a/sites/shared/styles/globals.css b/sites/shared/styles/globals.css index 4ce8c5cc2eb..2003cdb8122 100644 --- a/sites/shared/styles/globals.css +++ b/sites/shared/styles/globals.css @@ -278,6 +278,10 @@ fill: currentColor; animation: pulsefill 3s infinite; } + .pulse-stroke { + stroke: currentColor; + animation: pulsestroke 3s infinite; + } } @keyframes pulsefill { @@ -291,6 +295,17 @@ fill-opacity: 0; } } +@keyframes pulsestroke { + 0% { + stroke-opacity: 0; + } + 50% { + stroke-opacity: 0.3; + } + 100% { + stroke-opacity: 0; + } +} /* Override DaisyUI button text color */ .btn-info {
{row[0]}:{row[1]}{row[0]}:{row[1]}
{children}: