@@ -56,6 +56,7 @@ export class Element {
5656 componentType ?: ComponentType
5757 skipChildren : boolean = false
5858 assets : Record < string , ( ) => Promise < Uint8Array > > = { }
59+ components : Record < string , ( ) => Promise < string > > = { }
5960 constructor ( node : SceneNode , parent ?: Element ) {
6061 this . node = node
6162 this . parent = parent
@@ -174,14 +175,25 @@ export class Element {
174175 break
175176 case 'RECTANGLE' : {
176177 if (
177- ( this . node . fills as any ) . length === 1 &&
178- ( this . node . fills as any ) [ 0 ] . type === 'IMAGE'
178+ ( this . node . fills as any ) . find ( ( fill : any ) => fill . type === 'IMAGE' )
179179 ) {
180- this . componentType = 'Image'
180+ const css = await this . getCss ( )
181+ this . componentType =
182+ ( this . node . fills as any ) . length === 1 ? 'Image' : 'Box'
181183 Object . assign (
182184 this . additionalProps ,
183185 this . getImageProps ( 'images' , 'png' ) ,
184186 )
187+ if ( this . componentType !== 'Image' ) {
188+ this . additionalProps . bg = css . background . replace (
189+ '<path-to-image>' ,
190+ this . additionalProps . src ,
191+ )
192+
193+ delete this . additionalProps . src
194+ } else {
195+ this . additionalProps . bg = ''
196+ }
185197 this . addAsset ( this . node , 'png' )
186198 }
187199 break
@@ -266,23 +278,46 @@ export class Element {
266278 return this . assets
267279 }
268280 addAsset ( node : SceneNode , type : 'svg' | 'png' ) {
281+ if (
282+ type === 'svg' &&
283+ this . getChildren ( ) . length &&
284+ this . getChildren ( ) . every ( ( c ) => typeof c !== 'string' && ! c . node . visible )
285+ ) {
286+ return
287+ }
269288 if ( this . parent ) this . parent . addAsset ( node , type )
270289 else
271290 this . assets [ node . name + '.' + type ] = async ( ) => {
272291 const isSvg = type === 'svg'
273- const data = await node . exportAsync ( {
292+ const options : ExportSettings = {
274293 format : isSvg ? 'SVG' : 'PNG' ,
275- constraint : isSvg
276- ? undefined
277- : {
278- type : 'SCALE' ,
279- value : 1.5 ,
280- } ,
281- } )
294+ }
295+ if ( options . format !== 'SVG' ) {
296+ ; ( options as any ) . constraint = {
297+ type : 'SCALE' ,
298+ value : 1.5 ,
299+ }
300+ } else {
301+ ; ( options as any ) . useAbsoluteBounds = true
302+ }
303+ const data = await node . exportAsync ( options )
282304 return data
283305 }
284306 }
285307
308+ async getComponents ( ) : Promise < Record < string , ( ) => Promise < string > > > {
309+ await this . render ( )
310+ return this . components
311+ }
312+
313+ addComponent ( node : SceneNode ) {
314+ if ( this . parent ) this . parent . addComponent ( node )
315+ else
316+ this . components [ toPascal ( node . name ) + '.tsx' ] = async ( ) => {
317+ return ( await new Element ( node ) . render ( ) ) . trim ( )
318+ }
319+ }
320+
286321 async render ( dep : number = 0 ) : Promise < string > {
287322 if ( ! this . node . visible ) return ''
288323
@@ -303,6 +338,7 @@ export class Element {
303338 const value = (
304339 await this . node . exportAsync ( {
305340 format : 'SVG_STRING' ,
341+ useAbsoluteBounds : true ,
306342 } )
307343 ) . toString ( )
308344
@@ -492,6 +528,7 @@ export class Element {
492528 }
493529
494530 if ( this . node . type === 'COMPONENT' ) {
531+ this . addComponent ( this . node )
495532 const componentName = toPascal ( this . node . name )
496533 const interfaceDecl = createInterface (
497534 componentName ,
0 commit comments