feat(ds.dev): example is not a shared component
This commit is contained in:
parent
cbac621186
commit
e705da9e7f
18 changed files with 784 additions and 108 deletions
|
@ -1,106 +0,0 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import examples from '@freesewing/examples'
|
|
||||||
import rendertest from '@freesewing/rendertest'
|
|
||||||
import tutorial from '@freesewing/tutorial'
|
|
||||||
import Draft from '@freesewing/components/Draft'
|
|
||||||
import Icon from 'shared/components/icon'
|
|
||||||
import Pattern from '../../../react-pattern/dist/index.mjs'
|
|
||||||
//import Design from '../Workbench/Design'
|
|
||||||
//
|
|
||||||
const Design = props => <p>TODO: Design</p>
|
|
||||||
|
|
||||||
const Example = ({
|
|
||||||
pattern = 'examples',
|
|
||||||
design = true,
|
|
||||||
children=null,
|
|
||||||
options = {},
|
|
||||||
settings,
|
|
||||||
part = '',
|
|
||||||
sample
|
|
||||||
}) => {
|
|
||||||
const [designMode, setDesignMode] = useState(false)
|
|
||||||
const [focus, setFocus] = useState(null)
|
|
||||||
|
|
||||||
const raiseEvent = (type, data) => {
|
|
||||||
if (type === 'clearFocusAll') return setFocus(null)
|
|
||||||
let f = {}
|
|
||||||
if (focus !== null) f = { ...focus }
|
|
||||||
if (typeof f[data.part] === 'undefined') f[data.part] = { paths: [], points: [], coords: [] }
|
|
||||||
if (type === 'point') f[data.part].points.push(data.name)
|
|
||||||
else if (type === 'path') f[data.part].paths.push(data.name)
|
|
||||||
else if (type === 'coords') f[data.part].coords.push(data.coords)
|
|
||||||
else if (type === 'clearFocus') {
|
|
||||||
let i = focus[data.part][data.type].indexOf(data.name)
|
|
||||||
f[data.part][data.type].splice(i, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
setFocus(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
let focusCount = 0
|
|
||||||
if (focus !== null) {
|
|
||||||
for (let p of Object.keys(focus)) {
|
|
||||||
for (let i in focus[p].points) focusCount++
|
|
||||||
for (let i in focus[p].paths) focusCount++
|
|
||||||
for (let i in focus[p].coords) focusCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const patterns = {
|
|
||||||
examples,
|
|
||||||
rendertest,
|
|
||||||
tutorial
|
|
||||||
}
|
|
||||||
settings = {
|
|
||||||
options: { ...options },
|
|
||||||
measurements: { head: 390 },
|
|
||||||
...settings
|
|
||||||
}
|
|
||||||
if (part !== '') settings.only = [part]
|
|
||||||
const patternInstance = new patterns[pattern](settings)
|
|
||||||
if (sample) patternInstance.sample()
|
|
||||||
else patternInstance.draft()
|
|
||||||
const patternProps = patternInstance.getRenderProps()
|
|
||||||
return (
|
|
||||||
<figure className={designMode ? 'design example' : 'example'}>
|
|
||||||
<div className="example text-base-content">
|
|
||||||
<div className="actions">
|
|
||||||
<button
|
|
||||||
disabled={!designMode}
|
|
||||||
color="primary"
|
|
||||||
onClick={() => raiseEvent('clearFocusAll', null)}
|
|
||||||
>
|
|
||||||
<Icon icon='settings' />
|
|
||||||
</button>
|
|
||||||
<div className="form-control">
|
|
||||||
<label className="cursor-pointer label justify-start gap-4 font-lg lg:font-xl font-bold">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={designMode}
|
|
||||||
className="toggle toggle-secondary"
|
|
||||||
onChange={() => setDesignMode(!designMode)}
|
|
||||||
/>
|
|
||||||
<span className="label-text text-secondary">{designMode ? 'Disable' : 'Enable'} Developer View</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="shadow rounded border border-base-200">
|
|
||||||
<Pattern {...patternProps} design={designMode} focus={focus} raiseEvent={raiseEvent} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<figcaption className="text-base-content text-center text-base lg:text-lg italic">{children}</figcaption>
|
|
||||||
{designMode && (
|
|
||||||
<div className="design">
|
|
||||||
<Design
|
|
||||||
focus={focus}
|
|
||||||
design={designMode}
|
|
||||||
raiseEvent={raiseEvent}
|
|
||||||
parts={patternProps.parts}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</figure>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Example
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Circle = (props) => (
|
||||||
|
<circle
|
||||||
|
cx={props.point.x}
|
||||||
|
cy={props.point.y}
|
||||||
|
r={props.point.attributes.get('data-circle')}
|
||||||
|
{...props.point.attributes.asPropsIfPrefixIs('data-circle-')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Circle
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const style = ` style="fill: none; stroke: currentColor;" `
|
||||||
|
const grids = {
|
||||||
|
imperial: `
|
||||||
|
<pattern id="grid" height="25.4" width="25.4" patternUnits="userSpaceOnUse" key="grid">
|
||||||
|
<path class="gridline lg imperial" d="M 0 0 L 0 25.4 L 25.4 25.4" ${style} />
|
||||||
|
<path
|
||||||
|
class="gridline lg imperial"
|
||||||
|
d="M 12.7 0 L 12.7 25.4 M 0 12.7 L 25.4 12.7"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="gridline sm imperial"
|
||||||
|
d="M 3.175 0 L 3.175 25.4 M 6.32 0 L 6.35 25.4 M 9.525 0 L 9.525 25.4 M 15.875 0 L 15.875 25.4 M 19.05 0 L 19.05 25.4 M 22.225 0 L 22.225 25.4"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="gridline sm imperial"
|
||||||
|
d="M 0 3.175 L 25.4 3.175 M 0 6.32 L 25.4 6.35 M 0 9.525 L 25.4 9.525 M 0 15.875 L 25.4 15.875 M 0 19.05 L 25.4 19.05 M 0 22.225 L 25.4 22.225"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
</pattern>
|
||||||
|
`,
|
||||||
|
metric: `
|
||||||
|
<pattern id="grid" height="100" width="100" patternUnits="userSpaceOnUse" key="grid">
|
||||||
|
<path class="gridline lg metric" d="M 0 0 L 0 100 L 100 100" ${style} />
|
||||||
|
<path class="gridline metric" d="M 50 0 L 50 100 M 0 50 L 100 50" ${style} />
|
||||||
|
<path
|
||||||
|
class="gridline sm metric"
|
||||||
|
d="M 10 0 L 10 100 M 20 0 L 20 100 M 30 0 L 30 100 M 40 0 L 40 100 M 60 0 L 60 100 M 70 0 L 70 100 M 80 0 L 80 100 M 90 0 L 90 100"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="gridline sm metric"
|
||||||
|
d="M 0 10 L 100 10 M 0 20 L 100 20 M 0 30 L 100 30 M 0 40 L 100 40 M 0 60 L 100 60 M 0 70 L 100 70 M 0 80 L 100 80 M 0 90 L 100 90"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="gridline xs metric"
|
||||||
|
d="M 5 0 L 5 100 M 15 0 L 15 100 M 25 0 L 25 100 M 35 0 L 35 100 M 45 0 L 45 100 M 55 0 L 55 100 M 65 0 L 65 100 M 75 0 L 75 100 M 85 0 L 85 100 M 95 0 L 95 100"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
class="gridline xs metric"
|
||||||
|
d="M 0 5 L 100 5 M 0 15 L 100 15 M 0 25 L 100 25 M 0 35 L 100 35 M 0 45 L 100 45 M 0 55 L 100 55 M 0 65 L 100 65 M 0 75 L 100 75 M 0 85 L 100 85 M 0 95 L 100 95"
|
||||||
|
${style}
|
||||||
|
/>
|
||||||
|
</pattern>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
const Defs = (props) => {
|
||||||
|
let defs = props.svg.defs
|
||||||
|
if (props.settings.paperless) {
|
||||||
|
defs += grids[props.settings.units || 'metric']
|
||||||
|
for (let p in props.parts) {
|
||||||
|
let anchor = { x: 0, y: 0 }
|
||||||
|
if (typeof props.parts[p].points.gridAnchor !== 'undefined')
|
||||||
|
anchor = props.parts[p].points.gridAnchor
|
||||||
|
else if (typeof props.parts[p].points.anchor !== 'undefined')
|
||||||
|
anchor = props.parts[p].points.anchor
|
||||||
|
|
||||||
|
defs += `<pattern id="grid-${p}" key="grid-${p}" xlink:href="#grid" x="${anchor.x}" y="${anchor.y}" />`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <defs dangerouslySetInnerHTML={{ __html: defs }} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Defs
|
109
packages/freesewing.shared/components/mdx/example/Develop.js
Normal file
109
packages/freesewing.shared/components/mdx/example/Develop.js
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Develop = (props) => {
|
||||||
|
// Methods
|
||||||
|
const renderAttributes = (attr) => {
|
||||||
|
let list = []
|
||||||
|
for (let a in attr.list)
|
||||||
|
list.push(
|
||||||
|
<li key={a}>
|
||||||
|
<b>{a}</b>: {renderAttributeValue(attr.list[a])}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderAttributeValue = (val) => {
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
if (val.length === 1) return val.pop()
|
||||||
|
let list = []
|
||||||
|
for (let v of val) list.push(<li key={v}>{v}</li>)
|
||||||
|
return <ul>{list}</ul>
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
const round = (val) => Math.round(val * 10) / 10
|
||||||
|
|
||||||
|
if (!props.develop || props.focus === null || Object.keys(props.focus).length < 1) return null
|
||||||
|
let info = []
|
||||||
|
for (let part of Object.keys(props.focus)) {
|
||||||
|
let points = []
|
||||||
|
let paths = []
|
||||||
|
for (let i in props.focus[part].paths) {
|
||||||
|
let name = props.focus[part].paths[i]
|
||||||
|
let path = props.parts[part].paths[name]
|
||||||
|
paths.push(
|
||||||
|
<details key={'patitle-' + name} className={'path c' + (i % 8)}>
|
||||||
|
<summary>
|
||||||
|
<span>path.<b>{name}</b></span>
|
||||||
|
<button
|
||||||
|
className="px-2 py-1 rounded text-secondary border-secondary border text-sm ml-4"
|
||||||
|
onClick={() => console.log(`parts.${part}.paths.${name}:`, path)}
|
||||||
|
>
|
||||||
|
console.log
|
||||||
|
</button>
|
||||||
|
</summary>
|
||||||
|
{path.attributes.length > 0 && (
|
||||||
|
<ul key={'ops-' + name}>
|
||||||
|
<li>
|
||||||
|
<b>attributes</b>: {renderAttributes(path.attributes)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (let i in props.focus[part].points) {
|
||||||
|
let name = props.focus[part].points[i]
|
||||||
|
let point = props.parts[part].points[name]
|
||||||
|
points.push(
|
||||||
|
<details key={'potitle-' + name} className={'point c' + (i % 8)}>
|
||||||
|
<summary>
|
||||||
|
<span>point.<b>{name}</b></span>
|
||||||
|
<button
|
||||||
|
className="px-2 py-1 rounded text-secondary border-secondary border text-sm hover:bold ml-4"
|
||||||
|
onClick={() => console.log(`parts.${part}.points.${name}:`, point)}
|
||||||
|
>
|
||||||
|
console.log
|
||||||
|
</button>
|
||||||
|
</summary>
|
||||||
|
<ul key={'pdata-' + name} className="text-sm">
|
||||||
|
<li>
|
||||||
|
<b>x</b>: {round(point.x)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>y</b>: {round(point.y)}
|
||||||
|
</li>
|
||||||
|
{point.attributes.length > 0 && (
|
||||||
|
<li>
|
||||||
|
<b>attributes</b>: {renderAttributes(point.attributes)}
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
info.push(
|
||||||
|
<details key={'part-' + part} open>
|
||||||
|
<summary className="flex flex-row justify-between py-2 mr-2">
|
||||||
|
<span>parts.<b>{part}</b></span>
|
||||||
|
<button
|
||||||
|
className="px-2 rounded text-secondary border-secondary border text-sm"
|
||||||
|
onClick={() => props.raiseEvent('part', part)}
|
||||||
|
>
|
||||||
|
isolate
|
||||||
|
</button>
|
||||||
|
</summary>
|
||||||
|
{points.length > 0 && <ul>{points}</ul>}
|
||||||
|
{paths.length > 0 && <ul>{paths}</ul>}
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="develop">{info}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Develop
|
|
@ -0,0 +1,65 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const DevelopPath = (props) => {
|
||||||
|
let output = []
|
||||||
|
let i = 0
|
||||||
|
let from = null
|
||||||
|
for (let op of props.path.ops) {
|
||||||
|
let key = props.part + props.name + i
|
||||||
|
if (op.type === 'curve') {
|
||||||
|
output.push(
|
||||||
|
<path
|
||||||
|
key={key + 'cp1'}
|
||||||
|
d={`M ${from.x},${from.y} L ${op.cp1.x},${op.cp1.y}`}
|
||||||
|
className="develop path cp"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
i++
|
||||||
|
output.push(
|
||||||
|
<path
|
||||||
|
key={key + 'cp2'}
|
||||||
|
d={`M ${op.to.x},${op.to.y} L ${op.cp2.x},${op.cp2.y}`}
|
||||||
|
className="develop path cp"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
i++
|
||||||
|
output.push(
|
||||||
|
<circle
|
||||||
|
key={key + 'cpcirc1'}
|
||||||
|
cx={op.cp1.x}
|
||||||
|
cy={op.cp1.y}
|
||||||
|
r={3.5}
|
||||||
|
className="develop path cp"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
i++
|
||||||
|
output.push(
|
||||||
|
<circle
|
||||||
|
key={key + 'cpcirc2'}
|
||||||
|
cx={op.cp2.x}
|
||||||
|
cy={op.cp2.y}
|
||||||
|
r={3.5}
|
||||||
|
className="develop path cp"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
from = op.to
|
||||||
|
} else if (op.type !== 'close') from = op.to
|
||||||
|
}
|
||||||
|
output.push(
|
||||||
|
<path
|
||||||
|
key={props.part + props.name + 'dpath'}
|
||||||
|
d={props.path.asPathstring()}
|
||||||
|
onClick={() =>
|
||||||
|
props.raiseEvent('path', {
|
||||||
|
path: props.path,
|
||||||
|
name: props.name,
|
||||||
|
part: props.part
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="develop hovertrap"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DevelopPath
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const DevelopPoint = (props) => (
|
||||||
|
<g className={props.className}>
|
||||||
|
<circle cx={props.point.x} cy={props.point.y} r="2" className="center" />
|
||||||
|
<circle
|
||||||
|
cx={props.point.x}
|
||||||
|
cy={props.point.y}
|
||||||
|
r="7.5"
|
||||||
|
className="hovertrap"
|
||||||
|
onClick={() =>
|
||||||
|
props.raiseEvent('point', {
|
||||||
|
point: props.point,
|
||||||
|
name: props.name,
|
||||||
|
part: props.part
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default DevelopPoint
|
File diff suppressed because one or more lines are too long
138
packages/freesewing.shared/components/mdx/example/Part/index.js
Normal file
138
packages/freesewing.shared/components/mdx/example/Part/index.js
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Path from '../Path'
|
||||||
|
import Point from '../Point'
|
||||||
|
import Snippet from '../Snippet'
|
||||||
|
import { getProps } from '../utils'
|
||||||
|
|
||||||
|
const Part = (props) => {
|
||||||
|
const focusPoint = (point, i) => {
|
||||||
|
const p = props.part.points[point]
|
||||||
|
const pathString = `M ${p.x} ${props.part.topLeft.y} `
|
||||||
|
+ `L ${p.x} ${props.part.bottomRight.y} `
|
||||||
|
+ `M ${props.part.topLeft.x} ${p.y} `
|
||||||
|
+ `L ${props.part.bottomRight.x} ${p.y} `
|
||||||
|
const classes = 'focus point c' + (i % 8) // Cycle through 8 colors
|
||||||
|
return (
|
||||||
|
<React.Fragment key={'fp' + point}>
|
||||||
|
<path d={pathString} className={classes} />
|
||||||
|
<circle
|
||||||
|
cx={p.x}
|
||||||
|
cy={p.y}
|
||||||
|
r="5"
|
||||||
|
className="contrast"
|
||||||
|
onClick={() =>
|
||||||
|
props.raiseEvent('clearFocus', {
|
||||||
|
part: props.name,
|
||||||
|
type: 'points',
|
||||||
|
name: point
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const focusCoords = (p, i) => {
|
||||||
|
let pathString = `M ${p.x} ${props.part.topLeft.y} `
|
||||||
|
pathString += `L ${p.x} ${props.part.bottomRight.y} `
|
||||||
|
pathString += `M ${props.part.topLeft.x} ${p.y} `
|
||||||
|
pathString += `L ${props.part.bottomRight.x} ${p.y} `
|
||||||
|
let classes = 'focus coords c' + (i % 4) // Cycle through 4 CSS classes
|
||||||
|
return (
|
||||||
|
<React.Fragment key={'cp' + i}>
|
||||||
|
<path d={pathString} className={classes} />
|
||||||
|
<circle
|
||||||
|
cx={p.x}
|
||||||
|
cy={p.y}
|
||||||
|
r="5"
|
||||||
|
className={classes}
|
||||||
|
onClick={() =>
|
||||||
|
props.raiseEvent('clearFocus', {
|
||||||
|
part: props.name,
|
||||||
|
type: 'coords',
|
||||||
|
data: p
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let grid = props.paperless ? (
|
||||||
|
<rect
|
||||||
|
x={props.part.topLeft.x}
|
||||||
|
y={props.part.topLeft.y}
|
||||||
|
width={props.part.width}
|
||||||
|
height={props.part.height}
|
||||||
|
className="grid"
|
||||||
|
fill={'url(#grid-' + props.name + ')'}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
let focus = []
|
||||||
|
if (props.develop) {
|
||||||
|
if (props.focus && typeof props.focus[props.name] !== 'undefined') {
|
||||||
|
for (let i in props.focus[props.name].points)
|
||||||
|
focus.push(focusPoint(props.focus[props.name].points[i], i))
|
||||||
|
for (let i in props.focus[props.name].paths) {
|
||||||
|
let name = props.focus[props.name].paths[i]
|
||||||
|
focus.push(
|
||||||
|
<path
|
||||||
|
key={'fpa-' + name}
|
||||||
|
d={props.part.paths[name].asPathstring()}
|
||||||
|
className={'focus path c' + (i % 4)}
|
||||||
|
onClick={() =>
|
||||||
|
props.raiseEvent('clearFocus', {
|
||||||
|
part: props.name,
|
||||||
|
type: 'paths',
|
||||||
|
name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (let i in props.focus[props.name].coords)
|
||||||
|
focus.push(focusCoords(props.focus[props.name].coords[i], i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g {...getProps(props.part)} id={`part-${props.name}`}>
|
||||||
|
{grid}
|
||||||
|
{Object.keys(props.part.paths).map((name) => (
|
||||||
|
<Path
|
||||||
|
key={name}
|
||||||
|
name={name}
|
||||||
|
part={props.name}
|
||||||
|
language={props.language}
|
||||||
|
path={props.part.paths[name]}
|
||||||
|
focus={props.focus}
|
||||||
|
topLeft={props.part.topLeft}
|
||||||
|
bottomRight={props.part.bottomRight}
|
||||||
|
develop={props.develop}
|
||||||
|
raiseEvent={props.raiseEvent}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{Object.keys(props.part.points).map((name) => (
|
||||||
|
<Point
|
||||||
|
key={name}
|
||||||
|
name={name}
|
||||||
|
part={props.name}
|
||||||
|
language={props.language}
|
||||||
|
point={props.part.points[name]}
|
||||||
|
focus={props.focus}
|
||||||
|
topLeft={props.part.topLeft}
|
||||||
|
bottomRight={props.part.bottomRight}
|
||||||
|
develop={props.develop}
|
||||||
|
raiseEvent={props.raiseEvent}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{Object.keys(props.part.snippets).map((name) => (
|
||||||
|
<Snippet key={name} name={name} snippet={props.part.snippets[name]} />
|
||||||
|
))}
|
||||||
|
{focus}
|
||||||
|
</g>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Part
|
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react'
|
||||||
|
import TextOnPath from '../TextOnPath'
|
||||||
|
import DevelopPath from '../DevelopPath'
|
||||||
|
import { getProps } from '../utils'
|
||||||
|
|
||||||
|
const Path = (props) => {
|
||||||
|
if (!props.path.render) return null
|
||||||
|
const output = []
|
||||||
|
const pathId = 'path-' + props.part + '-' + props.name
|
||||||
|
if (props.develop) output.push(<DevelopPath {...props} key={'dpa-' + props.name} />)
|
||||||
|
output.push(
|
||||||
|
<path id={pathId} key={pathId} d={props.path.asPathstring()} {...getProps(props.path)} />
|
||||||
|
)
|
||||||
|
if (props.path.attributes.get('data-text'))
|
||||||
|
output.push(<TextOnPath key={'text-on-path-' + props.name} pathId={pathId} {...props} />)
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Path
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react'
|
||||||
|
import DevelopPoint from '../DevelopPoint'
|
||||||
|
import Text from '../Text'
|
||||||
|
import Circle from '../Circle'
|
||||||
|
|
||||||
|
const Point = (props) => {
|
||||||
|
const output = []
|
||||||
|
if (props.develop)
|
||||||
|
output.push(<DevelopPoint {...props} key={'dp-' + props.name} className="develop point" />)
|
||||||
|
if (props.point.attributes && props.point.attributes.get('data-text'))
|
||||||
|
output.push(<Text {...props} key={'point-' + props.name} />)
|
||||||
|
if (props.point.attributes && props.point.attributes.get('data-circle'))
|
||||||
|
output.push(<Circle point={props.point} key={'circle-' + props.name} />)
|
||||||
|
|
||||||
|
return output.length < 1 ? null : output
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Point
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { getProps } from '../utils'
|
||||||
|
|
||||||
|
const Snippet = (props) => {
|
||||||
|
const snippetProps = {
|
||||||
|
xlinkHref: '#' + props.snippet.def,
|
||||||
|
x: props.snippet.anchor.x,
|
||||||
|
y: props.snippet.anchor.y
|
||||||
|
}
|
||||||
|
let scale = props.snippet.attributes.get('data-scale')
|
||||||
|
let rotate = props.snippet.attributes.get('data-rotate')
|
||||||
|
if (scale || rotate) {
|
||||||
|
snippetProps.transform = ''
|
||||||
|
if (scale) {
|
||||||
|
snippetProps.transform += `translate(${snippetProps.x}, ${snippetProps.y}) `
|
||||||
|
snippetProps.transform += `scale(${scale}) `
|
||||||
|
snippetProps.transform += `translate(${snippetProps.x * -1}, ${snippetProps.y * -1}) `
|
||||||
|
}
|
||||||
|
if (rotate) {
|
||||||
|
snippetProps.transform += `rotate(${rotate}, ${snippetProps.x}, ${snippetProps.y}) `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <use {...snippetProps} {...getProps(props.snippet)} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Snippet
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Svg = ({
|
||||||
|
embed = true,
|
||||||
|
develop = false,
|
||||||
|
language = 'en',
|
||||||
|
className = 'freesewing pattern',
|
||||||
|
style = {},
|
||||||
|
viewBox = false,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
children
|
||||||
|
}) => {
|
||||||
|
let attributes = {
|
||||||
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
|
'xmlns:svg': 'http://www.w3.org/2000/svg',
|
||||||
|
xmlnsXlink: 'http://www.w3.org/1999/xlink',
|
||||||
|
xmlLang: language,
|
||||||
|
viewBox: viewBox || `0 0 ${width} ${height}`,
|
||||||
|
className,
|
||||||
|
style
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!embed) {
|
||||||
|
attributes.width = width + 'mm'
|
||||||
|
attributes.height = height + 'mm'
|
||||||
|
}
|
||||||
|
if (develop) attributes.className += ' develop'
|
||||||
|
|
||||||
|
return <svg {...attributes}>{children}</svg>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Svg
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { strings } from '@freesewing/i18n'
|
||||||
|
|
||||||
|
const Text = (props) => {
|
||||||
|
let text = []
|
||||||
|
// Handle translation
|
||||||
|
let translated = ''
|
||||||
|
for (let string of props.point.attributes.getAsArray('data-text')) {
|
||||||
|
if (strings[props.language]['plugin.' + string])
|
||||||
|
translated += strings[props.language]['plugin.' + string]
|
||||||
|
else translated += string.toString().replace(/"/g, '"')
|
||||||
|
translated += ' '
|
||||||
|
}
|
||||||
|
// Handle muti-line text
|
||||||
|
if (translated.indexOf('\n') !== -1) {
|
||||||
|
let key = 0
|
||||||
|
let lines = translated.split('\n')
|
||||||
|
text.push(<tspan key={'tspan-' + key}>{lines.shift()}</tspan>)
|
||||||
|
for (let line of lines) {
|
||||||
|
key++
|
||||||
|
text.push(
|
||||||
|
<tspan
|
||||||
|
key={'tspan-' + key}
|
||||||
|
x={props.point.x}
|
||||||
|
dy={props.point.attributes.get('data-text-lineheight') || 12}
|
||||||
|
>
|
||||||
|
{line.toString().replace(/"/g, '"')}
|
||||||
|
</tspan>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else text.push(<tspan key="tspan-1">{translated}</tspan>)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<text
|
||||||
|
x={props.point.x}
|
||||||
|
y={props.point.y}
|
||||||
|
{...props.point.attributes.asPropsIfPrefixIs('data-text-')}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Text
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { strings } from '@freesewing/i18n'
|
||||||
|
|
||||||
|
const TextOnPath = (props) => {
|
||||||
|
let text = []
|
||||||
|
// Handle translation
|
||||||
|
let translated = ''
|
||||||
|
for (let string of props.path.attributes.getAsArray('data-text')) {
|
||||||
|
if (strings[props.language]['plugin.' + string])
|
||||||
|
translated += strings[props.language]['plugin.' + string]
|
||||||
|
else translated += string.toString().replace(/"/g, '"')
|
||||||
|
translated += ' '
|
||||||
|
}
|
||||||
|
let textPathProps = {
|
||||||
|
xlinkHref: '#' + props.pathId,
|
||||||
|
startOffset: '0%'
|
||||||
|
}
|
||||||
|
let align = props.path.attributes.get('data-text-class')
|
||||||
|
if (align && align.indexOf('center') > -1) textPathProps.startOffset = '50%'
|
||||||
|
else if (align && align.indexOf('right') > -1) textPathProps.startOffset = '100%'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<text>
|
||||||
|
<textPath {...textPathProps}>
|
||||||
|
<tspan {...props.path.attributes.asPropsIfPrefixIs('data-text-')}>{translated}</tspan>
|
||||||
|
</textPath>
|
||||||
|
</text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TextOnPath
|
148
packages/freesewing.shared/components/mdx/example/index.js
Normal file
148
packages/freesewing.shared/components/mdx/example/index.js
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import Svg from './Svg'
|
||||||
|
import Defs from './Defs'
|
||||||
|
import Part from './Part'
|
||||||
|
import Develop from './Develop'
|
||||||
|
|
||||||
|
const PatternSvg = props => (
|
||||||
|
<Svg
|
||||||
|
embed={props.settings.embed}
|
||||||
|
width={props.width}
|
||||||
|
height={props.height}
|
||||||
|
language={props.settings.locale}
|
||||||
|
id={props.settings.idPrefix + 'svg'}
|
||||||
|
develop={props.develop || false}
|
||||||
|
style={props.style || {}}
|
||||||
|
viewBox={props.viewBox}
|
||||||
|
className={props.className || 'freesewing pattern'}
|
||||||
|
>
|
||||||
|
<Defs {...props} />
|
||||||
|
<style>{`:root { --pattern-scale: ${props.settings.scale || 1}}`}</style>
|
||||||
|
<g>
|
||||||
|
{Object.keys(props.parts).map((name) => (
|
||||||
|
<Part
|
||||||
|
part={props.parts[name]}
|
||||||
|
language={props.settings.locale}
|
||||||
|
paperless={props.settings.paperless}
|
||||||
|
units={props.settings.units}
|
||||||
|
key={name}
|
||||||
|
name={name}
|
||||||
|
focus={props.focus || false}
|
||||||
|
develop={props.develop || false}
|
||||||
|
raiseEvent={props.raiseEvent}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</g>
|
||||||
|
</Svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Pattern = props => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
pattern = 'examples',
|
||||||
|
patterns = {},
|
||||||
|
children=null,
|
||||||
|
options = {},
|
||||||
|
measurements = { head: 390},
|
||||||
|
settings,
|
||||||
|
part = '',
|
||||||
|
sample,
|
||||||
|
svgOnly=false,
|
||||||
|
allowDevelop=true
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const [develop, setDevelop] = useState(false)
|
||||||
|
const [focus, setFocus] = useState(null)
|
||||||
|
|
||||||
|
// Don't continue if there's no pattern
|
||||||
|
if (!pattern || !patterns[pattern]) return <pre>{JSON.stringify(props,null,4)}</pre> //null
|
||||||
|
|
||||||
|
/* Helper method to handle user clicks */
|
||||||
|
const raiseEvent = (type, data) => {
|
||||||
|
if (type === 'clearFocusAll') return setFocus(null)
|
||||||
|
let f = {}
|
||||||
|
if (focus !== null) f = { ...focus }
|
||||||
|
if (typeof f[data.part] === 'undefined') f[data.part] = { paths: [], points: [], coords: [] }
|
||||||
|
if (type === 'point') f[data.part].points.push(data.name)
|
||||||
|
else if (type === 'path') f[data.part].paths.push(data.name)
|
||||||
|
else if (type === 'coords') f[data.part].coords.push(data.coords)
|
||||||
|
else if (type === 'clearFocus') {
|
||||||
|
let i = focus[data.part][data.type].indexOf(data.name)
|
||||||
|
f[data.part][data.type].splice(i, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocus(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle various elements with focus */
|
||||||
|
let focusCount = 0
|
||||||
|
if (focus !== null) {
|
||||||
|
for (let p of Object.keys(focus)) {
|
||||||
|
for (let i in focus[p].points) focusCount++
|
||||||
|
for (let i in focus[p].paths) focusCount++
|
||||||
|
for (let i in focus[p].coords) focusCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up settings object */
|
||||||
|
settings = {
|
||||||
|
options: { ...options },
|
||||||
|
measurements: { ...measurements },
|
||||||
|
...settings
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part !== '') settings.only = [part]
|
||||||
|
const patternInstance = new patterns[pattern](settings)
|
||||||
|
if (sample) patternInstance.sample()
|
||||||
|
else patternInstance.draft()
|
||||||
|
const patternProps = patternInstance.getRenderProps()
|
||||||
|
return svgOnly
|
||||||
|
? <PatternSvg {...patternProps} develop={develop} focus={focus} raiseEvent={raiseEvent} />
|
||||||
|
: (
|
||||||
|
<figure className={develop ? 'develop example' : 'example'}>
|
||||||
|
<div className="example text-base-content">
|
||||||
|
{allowDevelop && (
|
||||||
|
<div className="actions">
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="cursor-pointer label justify-start gap-4 font-lg lg:font-xl font-bold">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={develop}
|
||||||
|
className="toggle toggle-secondary"
|
||||||
|
onChange={() => setDevelop(!develop)}
|
||||||
|
/>
|
||||||
|
<span className="label-text text-secondary">{develop ? 'Disable' : 'Enable'} Developer View</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{develop && (
|
||||||
|
<div className="develop p-4 py-2 border rounded mb-2">
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<h5>Developer info</h5>
|
||||||
|
<button
|
||||||
|
disabled={!develop}
|
||||||
|
className="px-2 py-1 rounded text-secondary border-secondary border text-sm"
|
||||||
|
onClick={() => raiseEvent('clearFocusAll', null)}
|
||||||
|
>
|
||||||
|
<strong>Clear</strong>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<Develop
|
||||||
|
focus={focus}
|
||||||
|
develop={develop}
|
||||||
|
raiseEvent={raiseEvent}
|
||||||
|
parts={patternProps.parts}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="shadow rounded border border-base-200">
|
||||||
|
<PatternSvg {...patternProps} develop={develop} focus={focus} raiseEvent={raiseEvent} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<figcaption className="text-base-content text-center text-base lg:text-lg italic">{children}</figcaption>
|
||||||
|
</figure>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Pattern
|
36
packages/freesewing.shared/components/mdx/example/utils.js
Normal file
36
packages/freesewing.shared/components/mdx/example/utils.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
export const getProps = (obj) => {
|
||||||
|
/** I can't believe it but there seems to be no method on NPM todo this */
|
||||||
|
const cssKey = (key) => {
|
||||||
|
let chunks = key.split('-')
|
||||||
|
if (chunks.length > 1) {
|
||||||
|
key = chunks.shift()
|
||||||
|
for (let s of chunks) key += s.charAt(0).toUpperCase() + s.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
const convert = (css) => {
|
||||||
|
let style = {}
|
||||||
|
let rules = css.split(';')
|
||||||
|
for (let rule of rules) {
|
||||||
|
let chunks = rule.split(':')
|
||||||
|
if (chunks.length === 2) style[cssKey(chunks[0].trim())] = chunks[1].trim()
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
|
||||||
|
let rename = {
|
||||||
|
class: 'className',
|
||||||
|
'marker-start': 'markerStart',
|
||||||
|
'marker-end': 'markerEnd'
|
||||||
|
}
|
||||||
|
let props = {}
|
||||||
|
for (let key in obj.attributes.list) {
|
||||||
|
if (key === 'style') props[key] = convert(obj.attributes.get(key))
|
||||||
|
if (Object.keys(rename).indexOf(key) !== -1) props[rename[key]] = obj.attributes.get(key)
|
||||||
|
else if (key !== 'style') props[key] = obj.attributes.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return props
|
||||||
|
}
|
|
@ -3,14 +3,20 @@ import Highlight from './highlight.js'
|
||||||
import YouTube from './youtube.js'
|
import YouTube from './youtube.js'
|
||||||
import DesignIterator from './design-iterator.js'
|
import DesignIterator from './design-iterator.js'
|
||||||
import Figure from './figure.js'
|
import Figure from './figure.js'
|
||||||
import Example from './example.js'
|
|
||||||
import ReadMore from './read-more.js'
|
import ReadMore from './read-more.js'
|
||||||
|
import Example from './example/index.js'
|
||||||
|
import examples from '@freesewing/examples'
|
||||||
|
import rendertest from '@freesewing/rendertest'
|
||||||
|
import tutorial from '@freesewing/tutorial'
|
||||||
|
|
||||||
|
|
||||||
const mdxCustomComponents = {
|
const mdxCustomComponents = {
|
||||||
// Custom components
|
// Custom components
|
||||||
DesignIterator,
|
DesignIterator,
|
||||||
Example,
|
Example: props => <Example
|
||||||
|
{...props}
|
||||||
|
patterns={{examples, rendertest, tutorial}}
|
||||||
|
>{props.children}</Example>,
|
||||||
Fixme: props => <Popout {...props} fixme />,
|
Fixme: props => <Popout {...props} fixme />,
|
||||||
Link: props => <Popout {...props} link />,
|
Link: props => <Popout {...props} link />,
|
||||||
Note: props => <Popout {...props} note />,
|
Note: props => <Popout {...props} note />,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue