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
+ setRounded(!rounded)}
+ >
+ {rounded ? 'Show raw' : 'Show rounded'}
+
+
+
+
+
+
+ TopLeft
+
+
+
+
+
+
+ BottomRight
+
+
+
+
+
+
+ Width
+
+
+
+
+
+ Height
+
+
+
+
+
+ Path Length
+
+
+
+
+
+
+
Drawing operations
+
+
+
+ Type
+
+ Coordinates
+ setRounded(!rounded)}
+ >
+ {rounded ? 'Show raw' : 'Show rounded'}
+
+
+
+
+
+ {path.ops.map((op, i) => (
+
+ {op.type}
+ {['move', 'line'].includes(op.type) ? (
+
+ To:
+
+
+
+ ) : null}
+ {op.type === 'close' ? : null}
+ {op.type === 'curve' ? (
+
+
+ Cp1:
+
+
+
+
+ Cp2:
+
+
+
+
+ To:
+
+
+
+
+ ) : null}
+
+ ))}
+
+
+
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
+ setRounded(!rounded)}
+ >
+ {rounded ? 'Show raw' : 'Show rounded'}
+
+
+
+
+
+
+
+ {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