diff --git a/packages/react/components/Account/profile.mjs b/packages/react/components/Account/profile.mjs deleted file mode 100644 index c1679c75a01..00000000000 --- a/packages/react/components/Account/profile.mjs +++ /dev/null @@ -1,54 +0,0 @@ -// Hooks -import { useAccount } from 'shared/hooks/use-account.mjs' -// Components -import Link from 'next/link' -import { Mdx } from 'shared/components/mdx/dynamic.mjs' -import { HeartIcon } from 'shared/components/icons.mjs' -import { ModalWrapper } from 'shared/components/wrappers/modal.mjs' - -export const ns = ['account'] - -export const Avatar = ({ img, app = false }) => ( -
- - app.setModal( - - - - ) - : null - } - className={app ? 'hover:cursor-zoom-in' : 'hover:cursor-zoom-out'} - /> -
-) - -export const AccountProfile = ({ app }) => { - const { account } = useAccount() - - if (!account) return null - - return ( -
-
- - {!account.patron ? ( - - - - ) : null} -
-

{account.username}

-
-
-
-
- -
-
- ) -} diff --git a/packages/react/components/Editor/components/views/InspectView.mjs b/packages/react/components/Editor/components/views/InspectView.mjs new file mode 100644 index 00000000000..e0f0efc1d00 --- /dev/null +++ b/packages/react/components/Editor/components/views/InspectView.mjs @@ -0,0 +1,72 @@ +// Dependencies +import React, { useContext } from 'react' +import { draft, missingMeasurements } from '../../lib/index.mjs' +// Context +import { ModalContext } from '@freesewing/react/context/Modal' +// Components +import { ModalWrapper } from '@freesewing/react/components/Modal' +import { Null } from '../Null.mjs' +import { ZoomablePattern } from '../ZoomablePattern.mjs' +import { PatternLayout } from '../PatternLayout.mjs' +import { DraftMenu } from '../menus/DraftMenu.mjs' +import { Xray } from '@freesewing/react/components/Xray' + +/** + * The inspect view allows users to inspect their pattern + * + * @param (object) props - All the props + * @param {function} props.config - The editor configuration + * @param {function} props.Design - The design constructor + * @param {array} props.missingMeasurements - List of missing measurements for the current design + * @param {object} props.state - The ViewWrapper state object + * @param {object} props.state.settings - The current settings + * @param {object} props.update - Helper object for updating the ViewWrapper state + * @return {function} DraftView - React component + */ +export const InspectView = ({ Design, state, update, config }) => { + const { setModal, clearModal, modalContent } = useContext(ModalContext) + + const info = { + set: (info) => setModal({info}), + clear: clearModal, + } + + /* + * Don't trust that we have all measurements + * + * We do not need to change the view here. That is done in the central + * ViewWrapper componenet. However, checking the measurements against + * the design takes a brief moment, so this component will typically + * render before that happens, and if measurements are missing it will + * throw and error. + * + * So when measurements are missing, we just return here and the view + * will switch on the next render loop. + */ + if (missingMeasurements(state)) return + + /* + * First, attempt to draft + */ + const { pattern } = draft(Design, state.settings) + + const renderProps = pattern.getRenderProps() + const output = ( + <> + + + + {modalContent} + + ) + + return +} diff --git a/packages/react/components/Editor/components/views/index.mjs b/packages/react/components/Editor/components/views/index.mjs index 74d3177a3d4..3affd539885 100644 --- a/packages/react/components/Editor/components/views/index.mjs +++ b/packages/react/components/Editor/components/views/index.mjs @@ -3,6 +3,7 @@ import { ViewPicker } from './ViewPicker.mjs' import { DesignsView } from './DesignsView.mjs' import { MeasurementsView } from './MeasurementsView.mjs' import { DraftView } from './DraftView.mjs' +import { InspectView } from './InspectView.mjs' import { SaveView } from './SaveView.mjs' import { ExportView } from './ExportView.mjs' import { UndosView } from './UndosView.mjs' @@ -63,6 +64,7 @@ export const View = (props) => { if (view === 'layout') return if (view === 'docs') return if (view === 'editSettings') return + if (view === 'inspect') return return

No view component for view {props.view}

} diff --git a/packages/react/components/Editor/index.mjs b/packages/react/components/Editor/index.mjs index 234190c5f7a..ec94d5c9377 100644 --- a/packages/react/components/Editor/index.mjs +++ b/packages/react/components/Editor/index.mjs @@ -11,6 +11,8 @@ import { View } from './components/views/index.mjs' import { Spinner } from '@freesewing/react/components/Spinner' import { AsideViewMenu } from './components/AsideViewMenu.mjs' import { LoadingStatus } from './components/LoadingStatus.mjs' +import { ModalContextProvider } from '@freesewing/react/context/Modal' +import { LoadingStatusContextProvider } from '@freesewing/react/context/LoadingStatus' /** * FreeSewing's pattern editor @@ -93,11 +95,15 @@ export const Editor = ({ config = {}, design = false, preload = {} }) => { } > - + + + + + ) diff --git a/packages/react/components/Pattern/part.mjs b/packages/react/components/Pattern/part.mjs index cf5305002a6..6528a4ec570 100644 --- a/packages/react/components/Pattern/part.mjs +++ b/packages/react/components/Pattern/part.mjs @@ -4,7 +4,7 @@ import React, { forwardRef } from 'react' import { getId, getProps } from './utils.mjs' export const PartInner = forwardRef( - ({ stackName, partName, part, settings, components, t }, ref) => { + ({ stackName, partName, part, settings, components, t, drillProps }, ref) => { const { Group, Path, Point, Snippet } = components return ( @@ -16,7 +16,7 @@ export const PartInner = forwardRef( topLeft={part.topLeft} bottomRight={part.bottomRight} units={settings[0].units} - {...{ stackName, partName, pathName, part, settings, components, t }} + {...{ stackName, partName, pathName, part, settings, components, t, drillProps }} /> ))} {Object.keys(part.points).map((pointName) => ( @@ -25,14 +25,14 @@ export const PartInner = forwardRef( point={part.points[pointName]} topLeft={part.topLeft} bottomRight={part.bottomRight} - {...{ stackName, partName, pointName, part, settings, components, t }} + {...{ stackName, partName, pointName, part, settings, components, t, drillProps }} /> ))} {Object.keys(part.snippets).map((snippetName) => ( ))} @@ -42,12 +42,12 @@ export const PartInner = forwardRef( PartInner.displayName = 'PartInner' -export const Part = ({ stackName, partName, part, settings, components, t }) => { +export const Part = ({ stackName, partName, part, settings, components, t, drillProps }) => { const { Group } = components return ( - + ) } diff --git a/packages/react/components/Pattern/stack.mjs b/packages/react/components/Pattern/stack.mjs index 619f22ba9f7..19d69f44812 100644 --- a/packages/react/components/Pattern/stack.mjs +++ b/packages/react/components/Pattern/stack.mjs @@ -3,14 +3,14 @@ import React from 'react' import { getProps } from './utils.mjs' -export const Stack = ({ stackName, stack, settings, components, t }) => { +export const Stack = ({ stackName, stack, settings, components, t, drillProps }) => { const { Group, Part, Grid } = components return ( - {settings[0].paperless ? : null} + {settings[0].paperless ? : null} {[...stack.parts].map((part, key) => ( - + ))} ) diff --git a/packages/react/components/Xray/index.mjs b/packages/react/components/Xray/index.mjs index 318eb44525e..ecf28f555e9 100644 --- a/packages/react/components/Xray/index.mjs +++ b/packages/react/components/Xray/index.mjs @@ -21,6 +21,7 @@ export const Xray = forwardRef((props, ref) => { children = false, className = 'freesewing pattern', components = {}, + drillProps = {}, } = props // Merge pattern, default, and custom components @@ -31,7 +32,6 @@ export const Xray = forwardRef((props, ref) => { } const { Svg, Defs, Stack, Group } = mergedComponents - const optionalProps = {} if (className) optionalProps.className = className @@ -58,6 +58,7 @@ export const Xray = forwardRef((props, ref) => { settings={renderProps.settings} components={mergedComponents} t={t} + drillProps={drillProps} /> ))} diff --git a/packages/react/components/Xray/path.mjs b/packages/react/components/Xray/path.mjs index 449426019b0..251922d4ae8 100644 --- a/packages/react/components/Xray/path.mjs +++ b/packages/react/components/Xray/path.mjs @@ -1,8 +1,12 @@ -import React from 'react' +import React, { useState } from 'react' import { getProps, defaultComponents as patternComponents, } from '@freesewing/react/components/Pattern' +import { H5, H6 } from '@freesewing/react/components/Heading' +import { KeyVal } from '@freesewing/react/components/KeyVal' +import { Highlight } from '@freesewing/react/components/Highlight' +import { round, pathLength } from '@freesewing/utils' const coords = (point) => `${point.x},${point.y}` @@ -10,7 +14,25 @@ const Cp = ({ at }) => ( ) -export const Xray = ({ path, components }) => { +export const PathXray = ({ + stackName, + pathName, + part, + path, + settings, + components, + t, + drillProps, +}) => { + /* + * We use the Path component from Pattern here + * If we would extract Path from the components passed down, + * we'd create a recursion loop as the Path we call below + * would be this very PathXray component. + */ + const { Path } = patternComponents + const { info = {} } = drillProps + const output = [] let prev let i = 0 @@ -23,6 +45,11 @@ export const Xray = ({ path, components }) => { key={i} d={`M ${coords(prev.to)} L ${coords(op.cp1)} M ${coords(op.to)} L ${coords(op.cp2)}`} className={`text-warning stroke-sm dashed opacity-50`} + style={{ + stroke: 'var(--pattern-lining)', + strokeWidth: '0.666', + }} + strokeDasharray="10 5" markerStart="none" markerEnd="none" /> @@ -32,24 +59,168 @@ export const Xray = ({ path, components }) => { i++ } output.push( - + , + + info?.set ? info.set() : null + } + > + + ) - return output -} - -export const PathXray = ({ stackName, pathName, part, path, settings, components, t }) => { - /* - * We use the Path component from Pattern here - * If we would extract Path from the components passed down, - * we'd create a recursion loop as the Path we call below - * would be this very PathXray component. - */ - const { Path } = patternComponents return ( - - + {output} + ) } + +const PathXrayInfo = ({ path, pathName, stackName, part }) => { + const [rounded, setRounded] = useState(true) + const log = (val) => console.log(val) + const rounder = rounded ? round : (val) => val + + return ( +
+
+ Path {pathName} of {stackName} +
+ {Object.keys(path.attributes.list).length > 0 ? ( + <> +
Attributes
+
+ {Object.entries(path.attributes.list).map(([k, val]) => ( + + ))} + +
+ + ) : null} +
Dimensions
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type + Coordinates + +
TopLeft + + +
BottomRight + + +
Width + +
Height + +
Path Length + +
+
Drawing operations
+ + + + + + + + + {path.ops.map((op, i) => ( + + + {['move', 'line'].includes(op.type) ? ( + + ) : null} + {op.type === 'close' ? : null} + {op.type === 'curve' ? ( + + ) : null} + + ))} + +
Type + Coordinates + +
{op.type} + To: + + + +
+ Cp1: + + +
+
+ Cp2: + + +
+
+ To: + + +
+
+
Pathstring
+ {path.d} +
+ ) +} diff --git a/packages/react/components/Xray/point.mjs b/packages/react/components/Xray/point.mjs index 3e601cf4261..a711702a1c2 100644 --- a/packages/react/components/Xray/point.mjs +++ b/packages/react/components/Xray/point.mjs @@ -1,13 +1,27 @@ -import React from 'react' +import React, { useState } from 'react' import { withinPartBounds, defaultComponents as patternComponents, } from '@freesewing/react/components/Pattern' +import { H5, H6 } from '@freesewing/react/components/Heading' +import { KeyVal } from '@freesewing/react/components/KeyVal' +import { round } from '@freesewing/utils' -export const PointXray = ({ stackName, pointName, part, point, settings, components, t }) => { +export const PointXray = ({ + stackName, + pointName, + part, + point, + settings, + components, + t, + drillProps = {}, +}) => { // Don't include parts outside the part bounding box if (!withinPartBounds(point, part)) return null + const { info = {} } = drillProps + /* * We use the Point component from Pattern here * If we would extract Point from the components passed down, @@ -15,15 +29,80 @@ export const PointXray = ({ stackName, pointName, part, point, settings, compone * would be this very PointXray component. */ const { Point } = patternComponents + return ( <> - + + + + info?.set + ? info.set() + : null + } + > + + {pointName} + + {round(point.x)},{round(point.y)} + + + ) } + +const PointXrayInfo = ({ point, pointName, stackName, part }) => { + const [rounded, setRounded] = useState(true) + const rounder = rounded ? round : (val) => val + + return ( +
+
+ Point {pointName} of {stackName} +
+
+
+ Coordinates + +
+
+
+ + +
+ {Object.keys(point.attributes.list).length > 0 ? ( + <> +
Attributes
+
+ {Object.entries(point.attributes.list).map(([k, val]) => ( + + ))} +
+ + ) : null} +
+ ) +} diff --git a/packages/utils/src/index.mjs b/packages/utils/src/index.mjs index 306db2ff92d..dc77dbc6481 100644 --- a/packages/utils/src/index.mjs +++ b/packages/utils/src/index.mjs @@ -5,6 +5,7 @@ import _set from 'lodash/set.js' import _unset from 'lodash/unset.js' import _orderBy from 'lodash/orderBy.js' import { loadingMessages } from './loading-messages.mjs' +import { Path, Point } from '@freesewing/core' /* * Re-export lodash utils @@ -351,6 +352,32 @@ export const mutateObject = (obj = {}, path, val = 'unset') => { return obj } +/** + * This calculates teh length of a path that is obtained from renderprops + * + * In other words, a plain POJO with the path data, and not an instantiated Path object from core. + * This is useful if you want to know the path length after rendering. + * + * @param {object} path - A path object as available from renderProps + * @return {number} length - The path length in mm + */ +export function pathLength(path) { + let p = new Path() + for (const op of path.ops) { + if (op.type === 'move') p = p.move(new Point(op.to.x, op.to.y)) + if (op.type === 'line') p = p.line(new Point(op.to.x, op.to.y)) + if (op.type === 'curve') + p = p.curve( + new Point(op.cp1.x, op.cp1.y), + new Point(op.cp2.x, op.cp2.y), + new Point(op.to.x, op.to.y) + ) + if (op.type === 'close') p = p.close() + } + + return p.length() +} + /** Generate a URL to create a new pattern with a given design, settings, and view */ export const patternUrlFromState = (state = {}, includeMeasurements = false, view = 'draft') => { // Avoid changing state by accident