1
0
Fork 0
freesewing/packages/react/components/Pattern/index.mjs
2025-05-30 11:29:55 +02:00

127 lines
3.7 KiB
JavaScript

// Components
import React, { forwardRef } from 'react'
import { Svg as DefaultSvg } from './svg.mjs'
import { Defs as DefaultDefs } from './defs.mjs'
import { Group as DefaultGroup } from './group.mjs'
import { Stack as DefaultStack } from './stack.mjs'
import { Part as DefaultPart } from './part.mjs'
import { Point as DefaultPoint } from './point.mjs'
import { Snippet as DefaultSnippet } from './snippet.mjs'
import { Path as DefaultPath } from './path.mjs'
import { Grid as DefaultGrid } from './grid.mjs'
import { Text as DefaultText, TextOnPath as DefaultTextOnPath } from './text.mjs'
import { Circle as DefaultCircle } from './circle.mjs'
import { getId, getProps, withinPartBounds, translateStrings } from './utils.mjs'
/**
* Default Pattern components that you can override
*
* @public
* @constant
*/
const defaultComponents = {
Svg: DefaultSvg,
Defs: DefaultDefs,
Group: DefaultGroup,
Stack: DefaultStack,
Part: DefaultPart,
Point: DefaultPoint,
Path: DefaultPath,
Snippet: DefaultSnippet,
Grid: DefaultGrid,
Text: DefaultText,
TextOnPath: DefaultTextOnPath,
Circle: DefaultCircle,
}
/**
* A component to render a FreeSewing pattern based on its renderProps
*
* @component
* @param {object} props - All component props
* @param {JSX.Element} props.children - The component children, if they are set, we will not render any stacks
* @param {string} [props.className = 'freesewing pattern'] - SVG classes to set on the SVG element
* @param {object} [props.components = {}] - Any custom components to use in the pattern
* @param {object} [props.strings = {}] - Strings to use for translation
* @param {object} props.renderProps - The pattern's renderProps as generated by FreeSewing core
* @returns {JSX.Element}
*/
const Pattern = forwardRef((props, ref) => {
if (!props.renderProps) return null
// Destructure props
const {
renderProps = false,
strings = {},
children = false,
className = 'freesewing pattern',
components = {},
} = props
// Merge default and swizzled components
const mergedComponents = {
...defaultComponents,
...components,
}
const { Svg, Defs, Stack, Group } = mergedComponents
const optionalProps = {}
if (className) optionalProps.className = className
return (
<Svg
viewBox={`0 0 ${renderProps.width} ${renderProps.height}`}
embed={renderProps.settings.embed}
{...renderProps}
{...optionalProps}
ref={ref}
>
<Defs {...renderProps} />
<style>{`:root { --pattern-scale: ${renderProps.settings.scale || 1}} ${
renderProps.svg.style
}`}</style>
<Group>
{children
? children
: Object.keys(renderProps.stacks).map((stackName) => (
<Stack
key={stackName}
stackName={stackName}
stack={renderProps.stacks[stackName]}
settings={renderProps.settings}
components={mergedComponents}
strings={strings}
/>
))}
</Group>
</Svg>
)
})
Pattern.displayName = 'Pattern'
export {
// utils
getId,
getProps,
withinPartBounds,
translateStrings,
// default components
defaultComponents,
// The Pattern component itself
Pattern,
}
// Also export default components
export const Svg = DefaultSvg
export const Defs = DefaultDefs
export const Group = DefaultGroup
export const Stack = DefaultStack
export const Part = DefaultPart
export const Point = DefaultPoint
export const Path = DefaultPath
export const Snippet = DefaultSnippet
export const Grid = DefaultGrid
export const Text = DefaultText
export const TextOnPath = DefaultTextOnPath
export const Circle = DefaultCircle