@@ -83,15 +83,26 @@ function isShapeNode(node: BaseNode): node is
8383 ] . some ( ( test : ( node : BaseNode ) => boolean ) => test ( node ) ) ;
8484}
8585
86+ function canHaveIndividualStrokes ( node : BaseNode ) : node is
87+ FrameNode |
88+ ComponentNode |
89+ InstanceNode |
90+ RectangleNode {
91+ return [
92+ isContainerNode ,
93+ isRectangleNode ,
94+ ] . some ( ( test : ( node : BaseNode ) => boolean ) => test ( node ) ) ;
95+ }
96+
8697function canHaveChildren ( node : BaseNode ) : node is
87- | DocumentNode
88- | PageNode
89- | FrameNode
90- | GroupNode
91- | BooleanOperationNode
92- | InstanceNode
93- | ComponentNode
94- | ComponentSetNode {
98+ DocumentNode |
99+ PageNode |
100+ FrameNode |
101+ GroupNode |
102+ BooleanOperationNode |
103+ InstanceNode |
104+ ComponentNode |
105+ ComponentSetNode {
95106 return [
96107 isDocumentNode ,
97108 isPageNode ,
@@ -391,7 +402,7 @@ export class FigmaPluginNode extends PluginNode {
391402 case StyleProperty . gap :
392403 return [
393404 isContainerNode ,
394- ] . some ( ( test : ( node : BaseNode ) => boolean ) => test ( this . _node ) ) ;
405+ ] . some ( ( test : ( node : BaseNode ) => boolean ) => test ( this . _node ) ) ;
395406 case StyleProperty . cornerRadiusTopLeft :
396407 case StyleProperty . cornerRadiusTopRight :
397408 case StyleProperty . cornerRadiusBottomRight :
@@ -510,7 +521,7 @@ export class FigmaPluginNode extends PluginNode {
510521 }
511522
512523 protected handleFontFamily ( node : FigmaPluginNode , values : AppliedStyleValues ) {
513- const fontFamily = values ? .get ( StyleProperty . fontFamily ) ?. value ;
524+ const fontFamily = values . get ( StyleProperty . fontFamily ) ?. value ;
514525 // We'll only set the font if the family is provided.
515526 if ( fontFamily ) {
516527 if ( isTextNode ( node . _node ) ) {
@@ -541,6 +552,38 @@ export class FigmaPluginNode extends PluginNode {
541552 }
542553 }
543554
555+ /**
556+ * Cleans up stroke values based on what we're about to apply.
557+ *
558+ * @remarks
559+ * Figma has a default invisible stroke width of `1`. If you add a stroke in the UI
560+ * it sets the weight to `1` and adds a default color.
561+ * If you change the weight, then remove the strokes (nothing visible) it maintains
562+ * the old stroke weight, but when you add again it resets to `1`.
563+ *
564+ * @param values - The entire list of values to be applied
565+ */
566+ private handleStroke ( values : AppliedStyleValues ) {
567+ const applyingFill = values . has ( StyleProperty . borderFillTop )
568+ || values . has ( StyleProperty . borderFillRight )
569+ || values . has ( StyleProperty . borderFillBottom )
570+ || values . has ( StyleProperty . borderFillLeft ) ;
571+ const applyingThickness = values . has ( StyleProperty . borderThicknessTop )
572+ || values . has ( StyleProperty . borderThicknessRight )
573+ || values . has ( StyleProperty . borderThicknessBottom )
574+ || values . has ( StyleProperty . borderThicknessLeft ) ;
575+
576+ if ( applyingFill && applyingThickness ) {
577+ // We only need to reset "individual" strokes here since we'll set a common stroke later anyway.
578+ if ( canHaveIndividualStrokes ( this . _node ) ) {
579+ ( this . _node as IndividualStrokesMixin ) . strokeTopWeight = 0 ;
580+ ( this . _node as IndividualStrokesMixin ) . strokeRightWeight = 0 ;
581+ ( this . _node as IndividualStrokesMixin ) . strokeBottomWeight = 0 ;
582+ ( this . _node as IndividualStrokesMixin ) . strokeLeftWeight = 0 ;
583+ }
584+ }
585+ }
586+
544587 protected safeNumber ( value : string , defaultValue : number = 0 ) {
545588 return value === STYLE_REMOVE ? defaultValue : Number . parseFloat ( value ) ;
546589 }
@@ -549,6 +592,8 @@ export class FigmaPluginNode extends PluginNode {
549592 // Fonts are complicated in Figma, so pull them out of the normal loop.
550593 this . handleFontFamily ( this , values ) ;
551594
595+ this . handleStroke ( values ) ;
596+
552597 // Paint all applied design tokens on the node
553598 values . forEach ( ( styleValue , target ) => {
554599 // console.log("applied design token eval", target, applied);
@@ -591,7 +636,7 @@ export class FigmaPluginNode extends PluginNode {
591636 case StyleProperty . borderThicknessBottom :
592637 case StyleProperty . borderThicknessLeft :
593638 this . setBoxSizing ( ) ;
594- this . paintStrokeWidth ( value ) ;
639+ this . paintStrokeWidth ( target , value ) ;
595640 break ;
596641 case StyleProperty . cornerRadiusTopLeft :
597642 case StyleProperty . cornerRadiusTopRight :
@@ -879,16 +924,42 @@ export class FigmaPluginNode extends PluginNode {
879924 ( this . _node as MinimalFillsMixin ) . fills = paint ? [ paint ] : [ SOLID_BLACK ] ;
880925 break ;
881926 case StyleProperty . borderFillTop :
927+ case StyleProperty . borderFillRight :
928+ case StyleProperty . borderFillBottom :
929+ case StyleProperty . borderFillLeft :
882930 // TODO: Figma only supports one border color, though it can be hacked using inner shadow.
883931 ( this . _node as MinimalStrokesMixin ) . strokes = paintValue ;
884932 break ;
885933 }
886934 }
887935
888- private paintStrokeWidth ( value : string ) : void {
889- ( this . _node as MinimalStrokesMixin ) . strokeWeight = this . safeNumber ( value ) ;
890- if ( ( this . _node as MinimalStrokesMixin ) . strokes . length === 0 ) {
891- ( this . _node as MinimalStrokesMixin ) . strokes = [ SOLID_TRANSPARENT ] ;
936+ private paintStrokeWidth ( target : StyleProperty , value : string ) : void {
937+ try {
938+ const numValue = this . safeNumber ( value ) ;
939+ if ( canHaveIndividualStrokes ( this . _node ) ) {
940+ switch ( target ) {
941+ case StyleProperty . borderThicknessTop :
942+ ( this . _node as IndividualStrokesMixin ) . strokeTopWeight = numValue ;
943+ break ;
944+ case StyleProperty . borderThicknessRight :
945+ ( this . _node as IndividualStrokesMixin ) . strokeRightWeight = numValue ;
946+ break ;
947+ case StyleProperty . borderThicknessBottom :
948+ ( this . _node as IndividualStrokesMixin ) . strokeBottomWeight = numValue ;
949+ break ;
950+ case StyleProperty . borderThicknessLeft :
951+ ( this . _node as IndividualStrokesMixin ) . strokeLeftWeight = numValue ;
952+ break ;
953+ }
954+ } else {
955+ ( this . _node as MinimalStrokesMixin ) . strokeWeight = this . safeNumber ( value ) ;
956+ }
957+
958+ if ( ( this . _node as MinimalStrokesMixin ) . strokes . length === 0 ) {
959+ ( this . _node as MinimalStrokesMixin ) . strokes = [ SOLID_TRANSPARENT ] ;
960+ }
961+ } catch ( error ) {
962+ console . error ( "paintStrokeWidth" , { target, value, error, ...this . debugInfo } ) ;
892963 }
893964 }
894965
@@ -1010,7 +1081,7 @@ export class FigmaPluginNode extends PluginNode {
10101081 } ]
10111082 } ] ;
10121083
1013- y += restComponent . height + spacing ;
1084+ y += restComponent . height + spacing ;
10141085 } ) ;
10151086
10161087 this . _node . resize ( x - spacing + paddingRight , y - spacing + paddingBottom ) ;
0 commit comments