11import Color from 'colorjs.io'
2- import { linearAngleToString } from './linear'
32import { isCylindricalSpace } from './colorspace'
43
54export type LayerSnapshot = {
@@ -19,23 +18,33 @@ function spaceToString(space: string, interpolation: string) {
1918}
2019
2120function radialPositionToString ( radial : LayerSnapshot [ 'radial' ] ) {
21+ const named = ( radial as any ) . named_position
22+ if ( named && named !== '--' ) {
23+ if ( named === 'center' ) return ''
24+ return named
25+ }
2226 if ( radial . position ?. x != null ) {
27+ const x = radial . position . x
2328 const y = radial . position . y ?? '50'
24- return radial . position . x + '% ' + y + '%'
25- }
26- else {
27- return radial . named_position
29+ if ( String ( x ) === '50' && String ( y ) === '50' ) return ''
30+ return x + '% ' + y + '%'
2831 }
32+ return ''
2933}
3034
3135function conicPositionToString ( conic : LayerSnapshot [ 'conic' ] ) {
36+ const named = ( conic as any ) . named_position
37+ if ( named && named !== '--' ) {
38+ if ( named === 'center' ) return ''
39+ return named
40+ }
3241 if ( conic . position ?. x != null ) {
42+ const x = conic . position . x
3343 const y = conic . position . y ?? '50'
34- return conic . position . x + '% ' + y + '%'
35- }
36- else {
37- return conic . named_position
44+ if ( String ( x ) === '50' && String ( y ) === '50' ) return ''
45+ return x + '% ' + y + '%'
3846 }
47+ return ''
3948}
4049
4150function maybeConvertColor ( color : string , convert_colors ?: boolean ) {
@@ -74,14 +83,23 @@ function stopsToStrings(stops: any[], { convert_colors, new_lines }: { convert_c
7483 return ! ! ( m && Number ( m [ 1 ] ) === 100 )
7584 }
7685
86+ function isPctFifty ( p : any ) {
87+ if ( p == null ) return false
88+ const m = String ( p ) . match ( / ^ ( - ? \d + (?: \. \d + ) ? ) % $ / )
89+ return ! ! ( m && Number ( m [ 1 ] ) === 50 )
90+ }
91+
7792 return stops
7893 . map ( ( s , i ) => {
7994 if ( s . kind === 'stop' ) {
8095 let p1 = s . position1
8196 let p2 = s . position2
8297
98+ // If position equals computed auto position, omit it
8399 if ( p1 != null && s . auto != null && p1 == s . auto ) p1 = null
100+ if ( p2 != null && s . auto != null && p2 == s . auto ) p2 = null
84101
102+ // Omit default endpoints
85103 if ( i === firstStopIdx && isPctZero ( p1 ) ) p1 = null
86104 if ( i === lastStopIdx && isPctHundred ( p2 ) ) p2 = null
87105
@@ -105,36 +123,71 @@ function stopsToStrings(stops: any[], { convert_colors, new_lines }: { convert_c
105123 return maybeConvertColor ( s . color , convert_colors )
106124 }
107125 else if ( s . kind === 'hint' ) {
108- if ( s . percentage == null ) return null
109- return s . percentage + '%'
126+ // Omit default/auto hints (like 50%)
127+ const pct = s . percentage
128+ if ( pct == null ) return null
129+ if ( s . auto != null && String ( pct ) == String ( s . auto ) ) return null
130+ if ( isPctFifty ( pct ) ) return null
131+ return pct + '%'
110132 }
111133 return null
112134 } )
113135 . filter ( Boolean )
114136 . join ( new_lines === true ? ',\n ' : ', ' )
115137}
116138
139+ function linearAngleToken ( linear : LayerSnapshot [ 'linear' ] ) {
140+ // Omit default 'to bottom' or 180deg
141+ const named = linear . named_angle
142+ const ang = linear . angle
143+ if ( named && named !== '--' ) {
144+ if ( named === 'to bottom' ) return ''
145+ return named
146+ }
147+ if ( ang != null ) {
148+ const n = Number ( ang )
149+ if ( ! Number . isNaN ( n ) && ( n % 360 === 180 ) ) return ''
150+ return String ( ang ) + 'deg'
151+ }
152+ return ''
153+ }
154+
117155function modernString ( layer : LayerSnapshot ) {
118156 if ( layer . type === 'linear' ) {
119- return `linear-gradient(\n ${ linearAngleToString ( layer . linear . angle as any , layer . linear . named_angle as any ) } ${ spaceToString ( layer . space , layer . interpolation ) } ,\n ${ stopsToStrings ( layer . stops , { new_lines : false } ) } \n )`
157+ const tokens = [ linearAngleToken ( layer . linear ) , spaceToString ( layer . space , layer . interpolation ) ] . filter ( Boolean ) . join ( ' ' )
158+ return `linear-gradient(\n ${ tokens } ,\n ${ stopsToStrings ( layer . stops , { new_lines : false } ) } \n )`
120159 }
121160 else if ( layer . type === 'radial' ) {
122- return `radial-gradient(\n ${ layer . radial . size } ${ layer . radial . shape } at ${ radialPositionToString ( layer . radial ) } ${ spaceToString ( layer . space , layer . interpolation ) } ,\n ${ stopsToStrings ( layer . stops , { new_lines : false } ) } \n )`
161+ const pos = radialPositionToString ( layer . radial )
162+ const posPart = pos && pos !== 'center' ? 'at ' + pos : ''
163+ const tokens = [ layer . radial . size , layer . radial . shape , posPart , spaceToString ( layer . space , layer . interpolation ) ] . filter ( Boolean ) . join ( ' ' )
164+ return `radial-gradient(\n ${ tokens } ,\n ${ stopsToStrings ( layer . stops , { new_lines : false } ) } \n )`
123165 }
124166 else {
125- return `conic-gradient(\n from ${ layer . conic . angle } deg at ${ conicPositionToString ( layer . conic ) } ${ spaceToString ( layer . space , layer . interpolation ) } ,\n ${ stopsToStrings ( layer . stops , { new_lines : false } ) } \n )`
167+ const pos = conicPositionToString ( layer . conic )
168+ const posPart = pos && pos !== 'center' ? 'at ' + pos : ''
169+ const fromPart = ( Number ( layer . conic . angle ) || 0 ) % 360 === 0 ? '' : `from ${ layer . conic . angle } deg`
170+ const tokens = [ fromPart , posPart , spaceToString ( layer . space , layer . interpolation ) ] . filter ( Boolean ) . join ( ' ' )
171+ return `conic-gradient(\n ${ tokens } ,\n ${ stopsToStrings ( layer . stops , { new_lines : false } ) } \n )`
126172 }
127173}
128174
129175function classicString ( layer : LayerSnapshot ) {
130176 if ( layer . type === 'linear' ) {
131- return `linear-gradient(${ linearAngleToString ( layer . linear . angle as any , layer . linear . named_angle as any ) } , ${ stopsToStrings ( layer . stops , { convert_colors : true , new_lines : false } ) } )`
177+ const angleToken = linearAngleToken ( layer . linear )
178+ const header = angleToken ? angleToken + ', ' : ''
179+ return `linear-gradient(${ header } ${ stopsToStrings ( layer . stops , { convert_colors : true , new_lines : false } ) } )`
132180 }
133181 else if ( layer . type === 'radial' ) {
134- return `radial-gradient(\n ${ layer . radial . size } ${ layer . radial . shape } at ${ radialPositionToString ( layer . radial ) } ,\n ${ stopsToStrings ( layer . stops , { convert_colors : true , new_lines : false } ) } \n )`
182+ const pos = radialPositionToString ( layer . radial )
183+ const posPart = pos && pos !== 'center' ? ' at ' + pos : ''
184+ return `radial-gradient(${ layer . radial . size } ${ layer . radial . shape } ${ posPart } , ${ stopsToStrings ( layer . stops , { convert_colors : true , new_lines : false } ) } )`
135185 }
136186 else {
137- return `conic-gradient(\n from ${ layer . conic . angle } deg at ${ conicPositionToString ( layer . conic ) } ,\n ${ stopsToStrings ( layer . stops , { convert_colors : true , new_lines : false } ) } \n )`
187+ const pos = conicPositionToString ( layer . conic )
188+ const posPart = pos && pos !== 'center' ? ' at ' + pos : ''
189+ const fromPart = ( Number ( layer . conic . angle ) || 0 ) % 360 === 0 ? '' : `from ${ layer . conic . angle } deg `
190+ return `conic-gradient(${ fromPart . trim ( ) } ${ posPart } , ${ stopsToStrings ( layer . stops , { convert_colors : true , new_lines : false } ) } )`
138191 }
139192}
140193
0 commit comments