1+ import React from 'react'
12import { getBoundariesFromPattern } from './functions'
23import { State } from './types'
34
4- export const Preview : React . FC < { state : State } > = ( { state } ) => {
5- const viewBox = ( ( ) => {
6- let xMin = 0
7- let xMax = 1
8- let yMin = 0
9- let yMax = 1
10-
11- // Make sure that default viewBox contains entire pattern
12- for ( const p of state . patterns ) {
13- const b = getBoundariesFromPattern ( p )
14-
15- xMin = Math . min ( xMin , b . xMin )
16- xMax = Math . max ( xMax , b . xMax )
17- yMin = Math . min ( yMin , b . yMin )
18- yMax = Math . max ( yMax , b . yMax )
19- }
20-
21- // Add some spacing around the bounding box
22- xMin -= 0.1
23- xMax += 0.1
24- yMin -= 0.1
25- yMax += 0.1
26-
27- return `${ xMin } ${ yMin } ${ xMax - xMin } ${ yMax - yMin } `
28- } ) ( )
5+ function getViewBox ( state : State ) : string {
6+ let xMin = 0
7+ let xMax = 1
8+ let yMin = 0
9+ let yMax = 1
10+
11+ // Make sure that default viewBox contains entire pattern
12+ for ( const p of state . patterns ) {
13+ const b = getBoundariesFromPattern ( p )
14+
15+ xMin = Math . min ( xMin , b . xMin )
16+ xMax = Math . max ( xMax , b . xMax )
17+ yMin = Math . min ( yMin , b . yMin )
18+ yMax = Math . max ( yMax , b . yMax )
19+ }
20+
21+ // Add some spacing around the bounding box
22+ xMin -= 0.1
23+ xMax += 0.1
24+ yMin -= 0.1
25+ yMax += 0.1
26+
27+ return `${ xMin } ${ yMin } ${ xMax - xMin } ${ yMax - yMin } `
28+ }
2929
30+ export const Preview : React . FC < { state : State } > = ( { state } ) => {
3031 return (
3132 < div className = 'border-1 border-amber-300 aspect-square' >
32- < svg viewBox = { viewBox } xmlns = 'http://www.w3.org/2000/svg' className = 'w-full aspect-square' >
33+ < svg viewBox = { getViewBox ( state ) } xmlns = 'http://www.w3.org/2000/svg' className = 'w-full aspect-square' >
3334 < rect
3435 className = 'stroke-amber-100'
3536 vectorEffect = 'non-scaling-stroke'
@@ -43,18 +44,70 @@ export const Preview: React.FC<{ state: State }> = ({ state }) => {
4344 { state . patterns . map ( ( p , i ) => {
4445 const { xMin, xMax, yMin, yMax } = getBoundariesFromPattern ( p )
4546
47+ const width = xMax - xMin
48+ const height = yMax - yMin
49+
4650 return (
47- < rect
48- key = { i }
49- className = 'stroke-amber-100'
50- vectorEffect = 'non-scaling-stroke'
51- fill = 'none'
52- strokeWidth = '1px'
53- x = { xMin . toFixed ( 3 ) }
54- y = { yMin . toFixed ( 3 ) }
55- width = { ( xMax - xMin ) . toFixed ( 3 ) }
56- height = { ( yMax - yMin ) . toFixed ( 3 ) }
57- />
51+ < React . Fragment key = { i } >
52+ < rect
53+ className = 'stroke-amber-100'
54+ vectorEffect = 'non-scaling-stroke'
55+ fill = 'none'
56+ strokeWidth = '1px'
57+ x = { xMin . toFixed ( 3 ) }
58+ y = { yMin . toFixed ( 3 ) }
59+ width = { width . toFixed ( 3 ) }
60+ height = { height . toFixed ( 3 ) }
61+ />
62+ { /* Partial line from anchor to target in order to show the direction */ }
63+ { ( ( ) => {
64+ const [ anchorX , anchorY ] = p . anchor
65+ const [ targetX , targetY ] = p . target
66+
67+ const x1 = anchorX
68+ const y1 = anchorY
69+ const x2 = targetX - ( targetX - anchorX ) * 0.5
70+ const y2 = targetY - ( targetY - anchorY ) * 0.5
71+
72+ return (
73+ < line
74+ className = 'stroke-amber-100'
75+ vectorEffect = 'non-scaling-stroke'
76+ strokeWidth = '1px'
77+ x1 = { x1 . toFixed ( 3 ) }
78+ y1 = { y1 . toFixed ( 3 ) }
79+ x2 = { x2 . toFixed ( 3 ) }
80+ y2 = { y2 . toFixed ( 3 ) }
81+ />
82+ )
83+ } ) ( ) }
84+ { /* Click surfaces for rotation and resizing actions */ }
85+ { [
86+ // top left
87+ { x : xMin , y : yMin , rotation : 0 } ,
88+ // top right
89+ { x : xMax , y : yMin , rotation : 90 } ,
90+ // bottom right
91+ { x : xMax , y : yMax , rotation : 180 } ,
92+ // bottom left
93+ { x : xMin , y : yMax , rotation : 270 } ,
94+ ] . map ( ( { x, y, rotation } , j ) => {
95+ const r_handle = 0.05 // Radius of the handle
96+
97+ // Define the base handle shape (for top-left corner)
98+ // This creates a quarter-circle arc pointing outward from the corner
99+ const basePathD = `M 0 0 L -${ r_handle } 0 A ${ r_handle } ${ r_handle } 0 0 1 0 -${ r_handle } Z`
100+
101+ return (
102+ < g
103+ key = { `corner-handle-${ i } -${ j } ` }
104+ transform = { `translate(${ x . toFixed ( 3 ) } , ${ y . toFixed ( 3 ) } ) rotate(${ rotation } )` }
105+ >
106+ < path d = { basePathD } className = 'fill-slate-400 hover:fill-blue-600 cursor-grab' />
107+ </ g >
108+ )
109+ } ) }
110+ </ React . Fragment >
58111 )
59112 } ) }
60113 </ svg >
0 commit comments