wip(shared): Points support in Inspector
This commit is contained in:
parent
eef8d68781
commit
4075e35641
12 changed files with 989 additions and 280 deletions
|
@ -1,3 +1,4 @@
|
|||
// Components
|
||||
import { Pattern as PatternComponent } from './pattern/index.mjs'
|
||||
import { Svg as SvgComponent } from './pattern/svg.mjs'
|
||||
import { Defs as DefsComponent } from './pattern/defs.mjs'
|
||||
|
@ -9,6 +10,8 @@ import { Snippet as SnippetComponent } from './pattern/snippet.mjs'
|
|||
import { Path as PathComponent } from './pattern/path.mjs'
|
||||
import { Grid as GridComponent } from './pattern/grid.mjs'
|
||||
import { Text as TextComponent, TextOnPath as TextOnPathComponent } from './pattern/text.mjs'
|
||||
// Pattern Utils
|
||||
import { getProps, withinPartBounds, getId, translateStrings } from './pattern/utils.mjs'
|
||||
|
||||
/*
|
||||
* Export all components as named exports
|
||||
|
@ -25,3 +28,13 @@ export const Snippet = SnippetComponent
|
|||
export const Grid = GridComponent
|
||||
export const Text = TextComponent
|
||||
export const TextOnPath = TextOnPathComponent
|
||||
|
||||
/*
|
||||
* Export pattern utils
|
||||
*/
|
||||
export const utils = {
|
||||
getProps,
|
||||
withinPartBounds,
|
||||
getId,
|
||||
translateStrings,
|
||||
}
|
||||
|
|
|
@ -6,12 +6,8 @@ export const Stack = ({ stackName, stack, settings, components, t }) => {
|
|||
|
||||
return (
|
||||
<Group {...getProps(stack)}>
|
||||
{[...stack.parts].map((part) => (
|
||||
<Part
|
||||
{...{ settings, components, t, part, stackName }}
|
||||
key={part.name}
|
||||
partName={part.name}
|
||||
/>
|
||||
{[...stack.parts].map((part, key) => (
|
||||
<Part {...{ settings, components, t, part, stackName, key }} />
|
||||
))}
|
||||
</Group>
|
||||
)
|
||||
|
|
|
@ -47,7 +47,7 @@ export const withinPartBounds = (point, part) =>
|
|||
: false
|
||||
|
||||
export const getId = ({
|
||||
settings,
|
||||
settings = {},
|
||||
stackName = false,
|
||||
partName = false,
|
||||
pathName = false,
|
||||
|
|
|
@ -5,8 +5,8 @@ import { pluginFlip } from '@freesewing/plugin-flip'
|
|||
import { pluginGore } from '@freesewing/plugin-gore'
|
||||
import { Design } from '@freesewing/core'
|
||||
import { Svg } from 'pkgs/react-components/src/index.mjs'
|
||||
import { Defs } from '../workbench/pattern/defs'
|
||||
import { Stack } from '../workbench/pattern/stack'
|
||||
//import { Defs } from '../workbench/pattern/defs'
|
||||
//import { Stack } from '../workbench/pattern/stack'
|
||||
import { useState, useEffect } from 'react'
|
||||
import yaml from 'js-yaml'
|
||||
|
||||
|
@ -40,22 +40,26 @@ export const Example = ({
|
|||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Svg {...patternProps} settings={settings} embed={true}>
|
||||
<Defs {...patternProps} />
|
||||
<style>{`:root { --pattern-scale: 1} ${patternProps.svg.style}`}</style>
|
||||
<g>
|
||||
{Object.keys(patternProps.stacks).map((stackName) => (
|
||||
<Stack
|
||||
{...{ showInfo, patternProps, settings, ui }}
|
||||
key={stackName}
|
||||
stackName={stackName}
|
||||
stack={patternProps.stacks[stackName]}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
</Svg>
|
||||
)
|
||||
return null
|
||||
|
||||
// FIXME
|
||||
|
||||
//return (
|
||||
// <Svg {...patternProps} settings={settings} embed={true}>
|
||||
// <Defs {...patternProps} />
|
||||
// <style>{`:root { --pattern-scale: 1} ${patternProps.svg.style}`}</style>
|
||||
// <g>
|
||||
// {Object.keys(patternProps.stacks).map((stackName) => (
|
||||
// <Stack
|
||||
// {...{ showInfo, patternProps, settings, ui }}
|
||||
// key={stackName}
|
||||
// stackName={stackName}
|
||||
// stack={patternProps.stacks[stackName]}
|
||||
// />
|
||||
// ))}
|
||||
// </g>
|
||||
// </Svg>
|
||||
//)
|
||||
}
|
||||
|
||||
// Returns a FreeSewing draft based on code in children
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
import { Text } from './text.mjs'
|
||||
import { Circle } from './circle.mjs'
|
||||
import { Tr, KeyTd, ValTd, Attributes, pointCoords } from './path.mjs'
|
||||
import { withinPartBounds } from './utils.mjs'
|
||||
|
||||
const RevealPoint = ({ point, pointName, scale, part, partName, ui }) => {
|
||||
const r = 15 * scale
|
||||
const { x, y } = point
|
||||
const { topLeft, bottomRight } = part
|
||||
const i = Object.keys(ui.xray.reveal[partName].points).indexOf(pointName) % 10
|
||||
const classes = `stroke-sm stroke-color-${i} stroke-dashed`
|
||||
return (
|
||||
<g>
|
||||
<circle cx={x} cy={y} r={r} className={classes} />
|
||||
<path
|
||||
d={`
|
||||
M ${x} ${topLeft.y} L ${x} ${y - r}
|
||||
m 0 ${2 * r} L ${x} ${bottomRight.y}
|
||||
M ${topLeft.x} ${y} L ${x - r} ${y}
|
||||
m ${2 * r} 0 L ${bottomRight.x} ${y}`}
|
||||
className={classes}
|
||||
/>
|
||||
</g>
|
||||
)
|
||||
}
|
||||
const pointInfo = ({ point, pointName, partName }) =>
|
||||
point ? (
|
||||
<div className="p-4 border bg-neutral bg-opacity-60 shadow rounded-lg">
|
||||
<h5 className="text-neutral-content text-center pb-4">Point info</h5>
|
||||
<table className="border-collapse h-fit">
|
||||
<tbody>
|
||||
<Tr>
|
||||
<KeyTd>Coordinates</KeyTd>
|
||||
<ValTd>{pointCoords(point)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Name</KeyTd>
|
||||
<ValTd>{pointName}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Part</KeyTd>
|
||||
<ValTd>{partName}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Attributes</KeyTd>
|
||||
<ValTd>
|
||||
<Attributes list={point.attributes.list} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex flex-col flex-wrap gap-2 mt-4">
|
||||
<button className="btn btn-success" onClick={() => console.log(point)}>
|
||||
console.log(point)
|
||||
</button>
|
||||
<button className="btn btn-success" onClick={() => console.table(point)}>
|
||||
console.table(point)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
|
||||
const XrayPoint = ({ point, pointName, partName, scale, showInfo }) => (
|
||||
<g>
|
||||
<circle
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r={2 * scale}
|
||||
className="stroke-sm stroke-lining fill-lining fill-opacity-25"
|
||||
/>
|
||||
<circle
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r={7.5 * scale}
|
||||
className="opacity-0 stroke-lining fill-lining hover:opacity-25 hover:cursor-pointer"
|
||||
onClick={(evt) => showInfo(evt, pointInfo({ point, pointName, partName }))}
|
||||
/>
|
||||
</g>
|
||||
)
|
||||
|
||||
export const Point = ({ point, pointName, partName, settings, part, ui, showInfo }) => {
|
||||
// Don't include parts outside the part bounding box
|
||||
if (!withinPartBounds(point, part)) return null
|
||||
const output = []
|
||||
if (ui.xray?.enabled) {
|
||||
// Xray for points
|
||||
output.push(
|
||||
<XrayPoint
|
||||
{...{ point, showInfo, pointName, partName }}
|
||||
scale={settings.scale}
|
||||
key={'xp-' + pointName}
|
||||
/>
|
||||
)
|
||||
// Reveal (based on clicking the seach icon in sidebar
|
||||
if (ui.xray?.reveal?.[partName]?.points?.[pointName])
|
||||
output.push(<RevealPoint scale={settings.scale} key={'rp-' + pointName} />)
|
||||
}
|
||||
// Render text
|
||||
if (point.attributes && point.attributes.get('data-text'))
|
||||
output.push(
|
||||
<Text {...{ point, ui, pointName, partName, showInfo }} key={'point-' + pointName} />
|
||||
)
|
||||
// Render circle
|
||||
if (point.attributes && point.attributes.get('data-circle'))
|
||||
output.push(<Circle point={point} key={'circle-' + pointName} />)
|
||||
|
||||
return output.length < 1 ? null : output
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
import { PanZoomPattern as ReactPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
|
||||
import { useState } from 'react'
|
||||
import { PanZoomPattern as ShowPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
|
||||
import { InspectorPattern } from './inspector/pattern.mjs'
|
||||
import { DraftMenu, ns as menuNs } from './menu.mjs'
|
||||
import { objUpdate } from 'shared/utils.mjs'
|
||||
|
||||
export const ns = menuNs
|
||||
|
||||
|
@ -15,6 +18,36 @@ export const DraftView = ({
|
|||
account,
|
||||
DynamicDocs,
|
||||
}) => {
|
||||
// State for inspector
|
||||
const [inspect, setInspect] = useState({
|
||||
show: {},
|
||||
reveal: {},
|
||||
})
|
||||
const inspector = {
|
||||
show: (data) => {
|
||||
const newInspect = { ...inspect }
|
||||
newInspect.show[data.id] = data
|
||||
setInspect(newInspect)
|
||||
},
|
||||
hide: (id) => {
|
||||
const newInspect = { ...inspect }
|
||||
delete newInspect.show[id]
|
||||
delete newInspect.reveal[id]
|
||||
setInspect(newInspect)
|
||||
},
|
||||
reveal: (id) => {
|
||||
const newInspect = { ...inspect }
|
||||
if (newInspect.reveal[id]) delete newInspect.reveal[id]
|
||||
else newInspect.reveal[id] = 1
|
||||
setInspect(newInspect)
|
||||
},
|
||||
update: (path, val) => {
|
||||
const newInspect = objUpdate({ ...inspect }, path, val)
|
||||
setInspect(newInspect)
|
||||
},
|
||||
data: inspect,
|
||||
}
|
||||
|
||||
let output = null
|
||||
if (ui.renderer === 'svg') {
|
||||
try {
|
||||
|
@ -23,14 +56,17 @@ export const DraftView = ({
|
|||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
} else output = <ReactPattern renderProps={pattern.getRenderProps()} />
|
||||
} else {
|
||||
output = ui.inspect ? (
|
||||
<InspectorPattern renderProps={pattern.getRenderProps()} inspector={inspector} />
|
||||
) : (
|
||||
<ShowPattern renderProps={pattern.getRenderProps()} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
<div className="w-2/3 shrink-0 grow lg:p-4 sticky top-0">
|
||||
<pre>{JSON.stringify(ui, null, 2)}</pre>
|
||||
{output}
|
||||
</div>
|
||||
<div className="w-2/3 shrink-0 grow lg:p-4 sticky top-0">{output}</div>
|
||||
<div className="w-1/3 shrink grow-0 lg:p-4 max-w-2xl h-screen overflow-scroll">
|
||||
<DraftMenu
|
||||
{...{
|
||||
|
@ -43,6 +79,7 @@ export const DraftView = ({
|
|||
language,
|
||||
account,
|
||||
DynamicDocs,
|
||||
inspector,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// Hooks
|
||||
//import { useContext } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
// Context
|
||||
//import { ModalContext } from 'shared/context/modal-context.mjs'
|
||||
//Dependencies
|
||||
//import { loadSettingsConfig } from './config.mjs'
|
||||
// Components
|
||||
//import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
|
||||
import { WrenchIcon, ClearIcon, HelpIcon } from 'shared/components/icons.mjs'
|
||||
import { Collapse } from 'shared/components/collapse.mjs'
|
||||
|
||||
export const ns = ['inspector']
|
||||
|
||||
export const Inspector = ({
|
||||
design,
|
||||
update,
|
||||
settings,
|
||||
patternConfig,
|
||||
language,
|
||||
DynamicDocs,
|
||||
control,
|
||||
ui,
|
||||
inspector,
|
||||
}) => {
|
||||
// FIXME: Update this namespace
|
||||
const { t } = useTranslation(ns)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="px-2 mt-8">
|
||||
{control > 4 ? (
|
||||
<div className="border-t border-solid border-base-300 pb-2 mx-36"></div>
|
||||
) : (
|
||||
<>
|
||||
<h5 className="flex flex-row gap-2 items-center">
|
||||
<WrenchIcon />
|
||||
<span>{t('inspector:inspector')}</span>
|
||||
</h5>
|
||||
<p>{t('inspector:inspector.d')}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{Object.values(inspector.data.show).map((props) => (
|
||||
<Collapse {...props} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Hooks
|
||||
import { useState, useEffect, useContext } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
// Components
|
||||
import { PanZoomPattern } from 'shared/components/workbench/pan-zoom-pattern.mjs'
|
||||
import { Point } from './point.mjs'
|
||||
|
||||
export const InspectorPattern = (props) => {
|
||||
const { t } = useTranslation(['workbench'])
|
||||
const inspector = props.inspector
|
||||
|
||||
const components = {
|
||||
Point: (props) => <Point {...props} inspector={inspector} />,
|
||||
}
|
||||
|
||||
return <PanZoomPattern {...props} {...{ t, components }} />
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
// 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 { round } from 'shared/utils.mjs'
|
||||
import { TrashIcon, PrintIcon, SearchIcon } from 'shared/components/icons.mjs'
|
||||
|
||||
const { withinPartBounds, getId, getProps } = utils
|
||||
|
||||
const RevealPoint = ({ point, pointName, scale, part, id, inspector }) => {
|
||||
const r = 15 * scale
|
||||
const { x, y } = point
|
||||
const { topLeft, bottomRight } = part
|
||||
const classes = `stroke-sm stroke-lining`
|
||||
return (
|
||||
<g>
|
||||
<circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r}
|
||||
className={`${classes} fill-lining pulse-fill hover:cursor-pointer`}
|
||||
onClick={() => inspector.reveal(id)}
|
||||
/>
|
||||
<path
|
||||
d={`
|
||||
M ${x} ${topLeft.y} L ${x} ${y - r}
|
||||
m 0 ${2 * r} L ${x} ${bottomRight.y}
|
||||
M ${topLeft.x} ${y} L ${x - r} ${y}
|
||||
m ${2 * r} 0 L ${bottomRight.x} ${y}`}
|
||||
className={`${classes} lashed`}
|
||||
/>
|
||||
</g>
|
||||
)
|
||||
}
|
||||
|
||||
const Cross = ({ point, color = 'primary' }) => (
|
||||
<path
|
||||
d={`M ${point.x - 2},${point.y - 2} l 4,4 m -4,0 l 4,-4`}
|
||||
className={`stroke-xs ${color}`}
|
||||
/>
|
||||
)
|
||||
|
||||
const InspectPoint = ({
|
||||
point,
|
||||
part,
|
||||
pointName,
|
||||
stackName,
|
||||
scale = 1,
|
||||
t,
|
||||
inspector,
|
||||
color = 'lining',
|
||||
}) => {
|
||||
const id = utils.getId({ stackName, pointName, settings: { idPrefix: 'point-' } })
|
||||
const info = {
|
||||
id,
|
||||
title: (
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
<span>
|
||||
<b>Point</b>: {pointName} | {stackName}
|
||||
</span>
|
||||
{pointCoords(point)}
|
||||
</div>
|
||||
),
|
||||
openTitle: (
|
||||
<span>
|
||||
<b>Point</b>: {pointName} | {stackName}
|
||||
</span>
|
||||
),
|
||||
buttons: [
|
||||
<button key={1} className="btn btn-error" onClick={(evt) => inspector.hide(id)}>
|
||||
<TrashIcon />
|
||||
</button>,
|
||||
],
|
||||
openButtons: [
|
||||
<button
|
||||
className="btn btn-xs btn-ghost px-0"
|
||||
key="log"
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation()
|
||||
console.log(point)
|
||||
}}
|
||||
>
|
||||
<PrintIcon className="w-4 h-4" />
|
||||
</button>,
|
||||
<button
|
||||
className="btn btn-xs btn-ghost px-0"
|
||||
key="reveal"
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation()
|
||||
inspector.reveal(id)
|
||||
}}
|
||||
>
|
||||
<SearchIcon className="w-4 h-4" />
|
||||
</button>,
|
||||
<button
|
||||
className="btn btn-xs btn-ghost px-0"
|
||||
key="remove"
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation()
|
||||
inspector.hide(id)
|
||||
}}
|
||||
>
|
||||
<TrashIcon className="w-4 h-4" />
|
||||
</button>,
|
||||
],
|
||||
children: (
|
||||
<KeyValTable
|
||||
rows={[
|
||||
[t('coordinates'), pointCoords(point)],
|
||||
[t('name'), pointName],
|
||||
['Stack', stackName],
|
||||
[t('attributes'), <Attributes list={point.attributes.list} />],
|
||||
]}
|
||||
/>
|
||||
),
|
||||
color: 'accent',
|
||||
}
|
||||
|
||||
return (
|
||||
<g>
|
||||
<circle cx={point.x} cy={point.y} r={2 * scale} className={`stroke-xs stroke-${color}`} />
|
||||
<circle
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r={2 * scale}
|
||||
className={`stroke-xs fill-${color} opacity-10`}
|
||||
/>
|
||||
<Cross {...{ point, color }} />
|
||||
<circle
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r={5 * scale}
|
||||
className={`opacity-0 stroke-${color} fill-${color} hover:opacity-25 hover:cursor-pointer hover:opacity-30`}
|
||||
onClick={(evt) => inspector.show(info)}
|
||||
/>
|
||||
{inspector.data.reveal[id] ? (
|
||||
<RevealPoint {...{ point, pointName, scale, part, id, inspector }} />
|
||||
) : null}
|
||||
</g>
|
||||
)
|
||||
}
|
||||
/*
|
||||
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,
|
||||
part,
|
||||
point,
|
||||
settings,
|
||||
components,
|
||||
t,
|
||||
ui,
|
||||
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 (
|
||||
<>
|
||||
<ShowPoint
|
||||
{...pointProps}
|
||||
{...{ stackName, pointName, part, point, settings, components, t }}
|
||||
/>
|
||||
<InspectPoint
|
||||
{...{ point, pointName, stackName, inspector, t, part }}
|
||||
scale={settings.scale}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,631 @@
|
|||
// Hooks
|
||||
import { useContext } from 'react'
|
||||
// Context
|
||||
import { ModalContext } from 'shared/context/modal-context.mjs'
|
||||
// Dependencies
|
||||
import { round } from 'shared/utils.mjs'
|
||||
// Components
|
||||
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
|
||||
|
||||
export const useInfoLoader = () => {
|
||||
const { setModal } = useContext(ModalContext)
|
||||
|
||||
const showInfo = (content) => {
|
||||
setModal(<ModalWrapper>{content}</ModalWrapper>)
|
||||
}
|
||||
|
||||
return showInfo
|
||||
}
|
||||
|
||||
export const KeyValTable = ({ rows }) => (
|
||||
<table className="border-collapse h-fit">
|
||||
<tbody>
|
||||
{rows.map((row) => (
|
||||
<tr>
|
||||
<td className="font-bold p-2 pr-0 text-right">{row[0]}:</td>
|
||||
<td className="p-2">{row[1]}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
export const pointCoords = (point) =>
|
||||
point ? `[ ${round(point.x, 2)}, ${round(point.y, 2)} ]` : null
|
||||
|
||||
export const Tr = ({ children }) => <tr className="border border-base-300">{children}</tr>
|
||||
export const KeyTd = ({ children }) => <td className="p-3 text-right">{children}:</td>
|
||||
export const ValTd = ({ children }) => <td className="p-3">{children}</td>
|
||||
|
||||
export const TextAlongPath = ({ id, size, txt }) => (
|
||||
<text>
|
||||
<textPath xlinkHref={`#${id}`} startOffset="50%">
|
||||
<tspan
|
||||
style={{ textAnchor: 'middle', fontSize: size }}
|
||||
className="fill-neutral-content fill-opacity-50"
|
||||
dy={size * -0.4}
|
||||
>
|
||||
{txt}
|
||||
</tspan>
|
||||
</textPath>
|
||||
</text>
|
||||
)
|
||||
export const PointCircle = ({ point, size, className = 'stroke-neutral-content' }) => (
|
||||
<circle
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r={size / 50}
|
||||
className={className}
|
||||
fill="none"
|
||||
strokeWidth={size / 150}
|
||||
strokeOpacity="0.5"
|
||||
/>
|
||||
)
|
||||
|
||||
const CpCircle = ({ point, className = 'fill-lining no-stroke' }) => (
|
||||
<circle cx={point.x} cy={point.y} r={1.5} className={className} />
|
||||
)
|
||||
|
||||
const EpCircle = ({ point, className = 'fill-note no-stroke' }) => (
|
||||
<circle cx={point.x} cy={point.y} r={1.5} className={className} />
|
||||
)
|
||||
|
||||
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 = () => (
|
||||
<defs>
|
||||
<marker orient="auto" refY="0.0" refX="0.0" id="arrowTo" style={{ overflow: 'visible' }}>
|
||||
<path
|
||||
className="fill-neutral-content"
|
||||
d="M 0,0 L -12,-4 C -10,-2 -10,2 -12, 4 z"
|
||||
fillOpacity="0.5"
|
||||
></path>
|
||||
</marker>
|
||||
<marker orient="auto" refY="0.0" refX="0.0" id="arrowFrom" style={{ overflow: 'visible' }}>
|
||||
<path
|
||||
className="fill-neutral-content"
|
||||
d="M 0,0 L 12,-4 C 10,-2 10,2 12, 4 z"
|
||||
fillOpacity="0.5"
|
||||
></path>
|
||||
</marker>
|
||||
</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 (
|
||||
<svg
|
||||
{...svgProps}
|
||||
viewBox={`${topLeft.x - size / 10} ${topLeft.y - size / 10} ${w + size / 5} ${h + size / 5}`}
|
||||
>
|
||||
<path
|
||||
id={`${id}_x`}
|
||||
{...xyProps}
|
||||
d={`M ${topLeft.x},${bottomRight.y} L ${bottomRight.x},${bottomRight.y}`}
|
||||
/>
|
||||
<TextAlongPath
|
||||
id={`${id}_x`}
|
||||
size={size / 18}
|
||||
txt={formatMm(bottomRight.x - topLeft.x, units, 'notags')}
|
||||
/>
|
||||
<path
|
||||
id={`${id}_y`}
|
||||
{...xyProps}
|
||||
d={`M ${topLeft.x},${bottomRight.y} L ${topLeft.x},${topLeft.y}`}
|
||||
/>
|
||||
<TextAlongPath
|
||||
id={`${id}_y`}
|
||||
size={size / 18}
|
||||
txt={formatMm(bottomRight.y - topLeft.y, units, 'notags')}
|
||||
/>
|
||||
<path
|
||||
id={id}
|
||||
d={`M ${from.x},${from.y} L ${to.x},${to.y}`}
|
||||
className="stroke-neutral-content"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={size / 100}
|
||||
/>
|
||||
<TextAlongPath id={id} size={size / 18} txt={formatMm(path.length(), units, 'notags')} />
|
||||
<PointCircle point={from} size={size} />
|
||||
<PointCircle point={to} size={size} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<svg
|
||||
{...svgProps}
|
||||
viewBox={`${topLeft.x - size / 10} ${topLeft.y - size / 10} ${w + size / 5} ${h + size / 5}`}
|
||||
>
|
||||
<Defs />
|
||||
<path
|
||||
id={`${id}_x`}
|
||||
{...xyProps}
|
||||
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.bottomRight.x},${bbox.bottomRight.y}`}
|
||||
/>
|
||||
<TextAlongPath
|
||||
id={`${id}_x`}
|
||||
size={size / 18}
|
||||
txt={formatMm(bbox.bottomRight.x - bbox.topLeft.x, units, 'notags')}
|
||||
/>
|
||||
<path
|
||||
id={`${id}_y`}
|
||||
{...xyProps}
|
||||
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.topLeft.x},${bbox.topLeft.y}`}
|
||||
/>
|
||||
<TextAlongPath
|
||||
id={`${id}_y`}
|
||||
size={size / 18}
|
||||
txt={formatMm(bbox.bottomRight.y - bbox.topLeft.y, units, 'notags')}
|
||||
/>
|
||||
<path id={`${id}_cp1`} {...cpProps} d={`M ${from.x},${from.y} L ${cp1.x},${cp1.y}`} />
|
||||
<PointCircle point={cp1} size={size} className="stroke-success" />
|
||||
<path id={`${id}_cp2`} {...cpProps} d={`M ${to.x},${to.y} L ${cp2.x},${cp2.y}`} />
|
||||
<PointCircle point={cp2} size={size} className="stroke-success" />
|
||||
<path
|
||||
id={id}
|
||||
d={`M ${from.x},${from.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${to.x},${to.y}`}
|
||||
className="stroke-neutral-content"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={size / 100}
|
||||
/>
|
||||
<TextAlongPath id={id} size={size / 18} txt={formatMm(path.length(), units, 'notags')} />
|
||||
<PointCircle point={from} size={size} />
|
||||
<PointCircle point={to} size={size} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<svg
|
||||
{...svgProps}
|
||||
viewBox={`${bbox.topLeft.x - size / 10} ${bbox.topLeft.y - size / 10} ${w + size / 5} ${
|
||||
h + size / 5
|
||||
}`}
|
||||
className="freesewing pattern z-50"
|
||||
>
|
||||
<Defs />
|
||||
<path
|
||||
id={`${id}_x`}
|
||||
{...xyProps}
|
||||
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.bottomRight.x},${bbox.bottomRight.y}`}
|
||||
/>
|
||||
<TextAlongPath
|
||||
id={`${id}_x`}
|
||||
size={size / 18}
|
||||
txt={formatMm(bbox.bottomRight.x - bbox.topLeft.x, units, 'notags')}
|
||||
/>
|
||||
<path
|
||||
id={`${id}_y`}
|
||||
{...xyProps}
|
||||
d={`M ${bbox.topLeft.x},${bbox.bottomRight.y} L ${bbox.topLeft.x},${bbox.topLeft.y}`}
|
||||
/>
|
||||
<TextAlongPath
|
||||
id={`${id}_y`}
|
||||
size={size / 18}
|
||||
txt={formatMm(bbox.bottomRight.y - bbox.topLeft.y, units, 'notags')}
|
||||
/>
|
||||
<path
|
||||
id={id}
|
||||
d={path.asPathstring()}
|
||||
className="stroke-neutral-content"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={size / 100}
|
||||
/>
|
||||
<TextAlongPath id={id} size={size / 18} txt={formatMm(path.length(), units, 'notags')} />
|
||||
<XrayPath {...props} />)
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
const lineInfo = ({ path, partName, units, i }) => (
|
||||
<div className="p-4 border bg-neutral bg-opacity-60 shadow rounded-lg">
|
||||
<h5 className="text-neutral-content text-center pb-4">Line info</h5>
|
||||
<div className="flex flex-row flex-wrap">
|
||||
<table className="border-collapse h-fit">
|
||||
<tbody>
|
||||
<Tr>
|
||||
<KeyTd>From</KeyTd>
|
||||
<ValTd>{pointCoords(path.ops[0].to)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>To</KeyTd>
|
||||
<ValTd>{pointCoords(path.ops[1].to)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Length</KeyTd>
|
||||
<ValTd>{formatMm(path.length(), units, 'notags')}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Width</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(Math.abs(path.ops[0].to.dx(path.ops[1].to)), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Height</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(Math.abs(path.ops[0].to.dy(path.ops[1].to)), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Part</KeyTd>
|
||||
<ValTd>{partName}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Draw Op</KeyTd>
|
||||
<ValTd>
|
||||
{i}/{ops.length}
|
||||
</ValTd>
|
||||
</Tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="max-w-md" style={{ maxHeight: '80vh' }}>
|
||||
<Line {...props} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const XrayLine = ({ i, path, partName, units, showInfo }) => (
|
||||
<>
|
||||
<path
|
||||
d={path.asPathstring()}
|
||||
{...getProps(path)}
|
||||
className="opacity-0 stroke-4xl stroke-note hover:opacity-25 hover:cursor-pointer"
|
||||
onClick={(evt) => showInfo(evt, lineInfo({ path, partName, units, i }))}
|
||||
/>
|
||||
<EpCircle point={path.ops[0].to} />
|
||||
<EpCircle point={path.ops[1].to} />
|
||||
</>
|
||||
)
|
||||
|
||||
const curveInfo = ({ i, path, partName, units }) => (
|
||||
<div className="p-4 border bg-neutral bg-opacity-40 shadow rounded-lg">
|
||||
<h5 className="text-neutral-content text-center pb-4">Curve info</h5>
|
||||
<div className="flex flex-row flex-wrap">
|
||||
<table className="border-collapse h-fit">
|
||||
<tbody>
|
||||
<Tr>
|
||||
<KeyTd>From</KeyTd>
|
||||
<ValTd>{pointCoords(path.ops[0].to)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Cp1</KeyTd>
|
||||
<ValTd>{pointCoords(path.ops[1].cp1)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Cp2</KeyTd>
|
||||
<ValTd>{pointCoords(path.ops[1].cp2)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>To</KeyTd>
|
||||
<ValTd>{pointCoords(path.ops[1].to)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Length</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(path.length(), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Width</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(Math.abs(path.ops[0].to.dx(path.ops[1].to)), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Height</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(Math.abs(path.ops[0].to.dy(path.ops[1].to)), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Part</KeyTd>
|
||||
<ValTd>{partName}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>Draw Op</KeyTd>
|
||||
<ValTd>
|
||||
{i}/{ops.length}
|
||||
</ValTd>
|
||||
</Tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="max-w-md" style={{ maxHeight: '80vh' }}>
|
||||
<Curve {...props} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export const Attributes = ({ list }) =>
|
||||
list ? (
|
||||
<ul>
|
||||
{Object.keys(list).map((key) => (
|
||||
<li key={key}>
|
||||
<strong>{key}</strong>: {list[key]}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null
|
||||
|
||||
export const pathInfo = ({ pathName, path, partName, units, showInfo }) => {
|
||||
const { t } = useTranslation(['workbench'])
|
||||
const bbox = path.bbox()
|
||||
|
||||
return (
|
||||
<div className="p-4 border bg-neutral bg-opacity-40 shadow rounded-lg">
|
||||
<h5 className="text-neutral-content text-center pb-4">{t('pathInfo')}</h5>
|
||||
<div className="flex flex-row flex-wrap overflow-scroll" style={{ maxHeight: '80vh' }}>
|
||||
<div>
|
||||
<table className="border-collapse h-fit">
|
||||
<tbody>
|
||||
<Tr>
|
||||
<KeyTd>{t('name')}</KeyTd>
|
||||
<ValTd>{pathName}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('length')}</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(path.length(), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('width')}</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(Math.abs(bbox.bottomRight.x - bbox.topLeft.x), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('height')}</KeyTd>
|
||||
<ValTd>
|
||||
<RawSpan html={formatMm(Math.abs(bbox.bottomRight.y - bbox.topLeft.y), units)} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('topLeft')}</KeyTd>
|
||||
<ValTd>{pointCoords(bbox.topLeft)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('bottomRight')}</KeyTd>
|
||||
<ValTd>{pointCoords(bbox.bottomRight)}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('part')}</KeyTd>
|
||||
<ValTd>{partName}</ValTd>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<KeyTd>{t('attributes')}</KeyTd>
|
||||
<ValTd>
|
||||
<Attributes list={path.attributes.list} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex flex-row flex-wrap gap-2 mt-4">
|
||||
<button className="btn btn-success" onClick={() => console.log(path)}>
|
||||
console.log(path)
|
||||
</button>
|
||||
<button className="btn btn-success" onClick={() => console.table(path)}>
|
||||
console.table(path)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table className="border-collapse h-fit">
|
||||
<tbody>
|
||||
{path.ops.map((op, i) => (
|
||||
<Tr key={i}>
|
||||
<KeyTd>{i}</KeyTd>
|
||||
<ValTd>
|
||||
<PathOp op={op} />
|
||||
</ValTd>
|
||||
</Tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="max-w-md">
|
||||
<MiniPath {...{ pathName, path, partName, units, showInfo }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const PathOp = ({ op }) => {
|
||||
if (op.type === 'move')
|
||||
return (
|
||||
<span>
|
||||
<strong>Move</strong> to {pointCoords(op.to)}
|
||||
</span>
|
||||
)
|
||||
else if (op.type === 'line')
|
||||
return (
|
||||
<span>
|
||||
<strong>Line</strong> to {pointCoords(op.to)}
|
||||
</span>
|
||||
)
|
||||
else if (op.type === 'curve')
|
||||
return (
|
||||
<span>
|
||||
<strong>Curve</strong> to {pointCoords(op.to)}
|
||||
<br />
|
||||
Cp1: {pointCoords(op.cp1)}
|
||||
<br />
|
||||
Cp2: {pointCoords(op.cp2)}
|
||||
</span>
|
||||
)
|
||||
else if (op.type === 'noop') return <strong>NOOP</strong>
|
||||
else if (op.type === 'close') return <strong>Close</strong>
|
||||
else return <strong>FIXME: unknown path operation type: {op.type}</strong>
|
||||
}
|
||||
|
||||
const XrayCurve = ({ i, path, ops, units, showInfo }) => {
|
||||
const from = path.ops[0].to
|
||||
const { cp1, cp2, to } = path.ops[1]
|
||||
|
||||
return (
|
||||
<>
|
||||
<path
|
||||
d={path.asPathstring()}
|
||||
{...getProps(path)}
|
||||
className="opacity-0 stroke-4xl stroke-lining hover:opacity-25 hover:cursor-pointer"
|
||||
onClick={(evt) => showInfo(evt, curveInfo({ i, path, partName, units }))}
|
||||
/>
|
||||
<path d={`M ${from.x},${from.y} L ${cp1.x},${cp1.y}`} className="lining dotted" />
|
||||
<path d={`M ${to.x},${to.y} L ${cp2.x},${cp2.y}`} className="lining dotted" />
|
||||
<CpCircle point={cp1} />
|
||||
<CpCircle point={cp2} />
|
||||
<EpCircle point={from} />
|
||||
<EpCircle point={to} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<g>
|
||||
<path
|
||||
d={path.asPathstring()}
|
||||
{...getProps(path)}
|
||||
className="opacity-0 stroke-7xl stroke-contrast hover:opacity-25 hover:cursor-pointer"
|
||||
onClick={(evt) => showInfo(evt, pathInfo({ pathName, path, partName, units }))}
|
||||
markerStart="none"
|
||||
markerEnd="none"
|
||||
/>
|
||||
{ops.length > 0
|
||||
? ops.map((op, i) =>
|
||||
op.ops[1].type === 'curve' ? (
|
||||
<XrayCurve path={op} ops={ops} i={i} pathName={`${pathName}_test`} key={i} />
|
||||
) : (
|
||||
<XrayLine path={op} ops={ops} i={i} pathName={`${pathName}_test`} key={i} />
|
||||
)
|
||||
)
|
||||
: null}
|
||||
</g>
|
||||
)
|
||||
}
|
||||
|
||||
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(<path id={pathId} key={pathId} d={d} {...getProps(path)} />)
|
||||
if (path.attributes.get('data-text'))
|
||||
output.push(
|
||||
<TextOnPath
|
||||
key={'text-on-path-' + getId(pathId)}
|
||||
pathId={pathId}
|
||||
{...{ path, ui, showInfo }}
|
||||
/>
|
||||
)
|
||||
if (ui.xray?.enabled) output.push(<XrayPath key={'xpath' + pathId} />)
|
||||
|
||||
return output
|
||||
}
|
|
@ -7,8 +7,9 @@ import {
|
|||
ns as coreMenuNs,
|
||||
} from 'shared/components/workbench/menus/core-settings/index.mjs'
|
||||
import { UiSettings, ns as uiNs } from 'shared/components/workbench/menus/ui-settings/index.mjs'
|
||||
import { Inspector, ns as inspectorNs } from './inspector/menu.mjs'
|
||||
|
||||
export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs]
|
||||
export const ns = [...coreMenuNs, ...designMenuNs, ...uiNs, inspectorNs]
|
||||
|
||||
export const DraftMenu = ({
|
||||
design,
|
||||
|
@ -19,6 +20,7 @@ export const DraftMenu = ({
|
|||
language,
|
||||
account,
|
||||
DynamicDocs,
|
||||
inspector = false,
|
||||
}) => {
|
||||
// Default control level is 2 (in case people are not logged in)
|
||||
const control = account.control || 2
|
||||
|
@ -35,6 +37,7 @@ export const DraftMenu = ({
|
|||
|
||||
return (
|
||||
<nav className="grow mb-12">
|
||||
{ui.inspect ? <Inspector {...menuProps} {...{ ui, inspector }} /> : null}
|
||||
<DesignOptions {...menuProps} />
|
||||
<CoreSettings {...menuProps} />
|
||||
<UiSettings {...menuProps} ui={ui} />
|
||||
|
|
|
@ -255,146 +255,6 @@
|
|||
svg.freesewing.pattern circle.fill-note {
|
||||
@apply fs-fill-note;
|
||||
}
|
||||
.fs-stroke-color-0 {
|
||||
stroke: var(--pattern-color-0);
|
||||
}
|
||||
.fs-stroke-color-1 {
|
||||
stroke: var(--pattern-color-1);
|
||||
}
|
||||
.fs-stroke-color-2 {
|
||||
stroke: var(--pattern-color-2);
|
||||
}
|
||||
.fs-stroke-color-3 {
|
||||
stroke: var(--pattern-color-3);
|
||||
}
|
||||
.fs-stroke-color-4 {
|
||||
stroke: var(--pattern-color-4);
|
||||
}
|
||||
.fs-stroke-color-5 {
|
||||
stroke: var(--pattern-color-5);
|
||||
}
|
||||
.fs-stroke-color-6 {
|
||||
stroke: var(--pattern-color-6);
|
||||
}
|
||||
.fs-stroke-color-7 {
|
||||
stroke: var(--pattern-color-7);
|
||||
}
|
||||
.fs-stroke-color-8 {
|
||||
stroke: var(--pattern-color-8);
|
||||
}
|
||||
.fs-stroke-color-9 {
|
||||
stroke: var(--pattern-color-9);
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-0,
|
||||
svg.freesewing.pattern path.stroke-color-0 {
|
||||
@apply fs-stroke-color-0;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-1,
|
||||
svg.freesewing.pattern path.stroke-color-1 {
|
||||
@apply fs-stroke-color-1;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-2,
|
||||
svg.freesewing.pattern path.stroke-color-2 {
|
||||
@apply fs-stroke-color-2;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-3,
|
||||
svg.freesewing.pattern path.stroke-color-3 {
|
||||
@apply fs-stroke-color-3;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-4,
|
||||
svg.freesewing.pattern path.stroke-color-4 {
|
||||
@apply fs-stroke-color-4;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-5,
|
||||
svg.freesewing.pattern path.stroke-color-5 {
|
||||
@apply fs-stroke-color-5;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-6,
|
||||
svg.freesewing.pattern path.stroke-color-6 {
|
||||
@apply fs-stroke-color-6;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-7,
|
||||
svg.freesewing.pattern path.stroke-color-7 {
|
||||
@apply fs-stroke-color-7;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-8,
|
||||
svg.freesewing.pattern path.stroke-color-8 {
|
||||
@apply fs-stroke-color-8;
|
||||
}
|
||||
svg.freesewing.pattern circle.stroke-color-9,
|
||||
svg.freesewing.pattern path.stroke-color-9 {
|
||||
@apply fs-stroke-color-9;
|
||||
}
|
||||
.fs-fill-color-0 {
|
||||
fill: var(--pattern-color-0);
|
||||
}
|
||||
.fs-fill-color-1 {
|
||||
fill: var(--pattern-color-1);
|
||||
}
|
||||
.fs-fill-color-2 {
|
||||
fill: var(--pattern-color-2);
|
||||
}
|
||||
.fs-fill-color-3 {
|
||||
fill: var(--pattern-color-3);
|
||||
}
|
||||
.fs-fill-color-4 {
|
||||
fill: var(--pattern-color-4);
|
||||
}
|
||||
.fs-fill-color-5 {
|
||||
fill: var(--pattern-color-5);
|
||||
}
|
||||
.fs-fill-color-6 {
|
||||
fill: var(--pattern-color-6);
|
||||
}
|
||||
.fs-fill-color-7 {
|
||||
fill: var(--pattern-color-7);
|
||||
}
|
||||
.fs-fill-color-8 {
|
||||
fill: var(--pattern-color-8);
|
||||
}
|
||||
.fs-fill-color-9 {
|
||||
fill: var(--pattern-color-9);
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-0,
|
||||
svg.freesewing.pattern path.fill-color-0 {
|
||||
@apply fs-fill-color-0;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-1,
|
||||
svg.freesewing.pattern path.fill-color-1 {
|
||||
@apply fs-fill-color-1;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-2,
|
||||
svg.freesewing.pattern path.fill-color-2 {
|
||||
@apply fs-fill-color-2;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-3,
|
||||
svg.freesewing.pattern path.fill-color-3 {
|
||||
@apply fs-fill-color-3;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-4,
|
||||
svg.freesewing.pattern path.fill-color-4 {
|
||||
@apply fs-fill-color-4;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-5,
|
||||
svg.freesewing.pattern path.fill-color-5 {
|
||||
@apply fs-fill-color-5;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-6,
|
||||
svg.freesewing.pattern path.fill-color-6 {
|
||||
@apply fs-fill-color-6;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-7,
|
||||
svg.freesewing.pattern path.fill-color-7 {
|
||||
@apply fs-fill-color-7;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-8,
|
||||
svg.freesewing.pattern path.fill-color-8 {
|
||||
@apply fs-fill-color-8;
|
||||
}
|
||||
svg.freesewing.pattern circle.fill-color-9,
|
||||
svg.freesewing.pattern path.fill-color-9 {
|
||||
@apply fs-fill-color-9;
|
||||
}
|
||||
|
||||
.fs-fill-bg {
|
||||
fill: var(--pattern-bg);
|
||||
|
@ -414,6 +274,22 @@
|
|||
.fill-opacity-50 {
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
.pulse-fill {
|
||||
fill: currentColor;
|
||||
animation: pulsefill 3s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulsefill {
|
||||
0% {
|
||||
fill-opacity: 0;
|
||||
}
|
||||
50% {
|
||||
fill-opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
fill-opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override DaisyUI button text color */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue