@@ -3,6 +3,8 @@ import 'dart:ui';
33import 'package:csslib/visitor.dart' as css;
44import 'package:csslib/parser.dart' as cssparser;
55import 'package:flutter/cupertino.dart' ;
6+ import 'package:flutter/material.dart' ;
7+ import 'package:flutter_html/src/utils.dart' ;
68import 'package:flutter_html/style.dart' ;
79
810Style declarationsToStyle (Map <String ?, List <css.Expression >> declarations) {
@@ -13,6 +15,24 @@ Style declarationsToStyle(Map<String?, List<css.Expression>> declarations) {
1315 case 'background-color' :
1416 style.backgroundColor = ExpressionMapping .expressionToColor (value.first) ?? style.backgroundColor;
1517 break ;
18+ case 'border' :
19+ List <css.LiteralTerm ?>? borderWidths = value.whereType< css.LiteralTerm > ().toList ();
20+ /// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width] , so make sure to remove those before passing it to [ExpressionMapping]
21+ borderWidths.removeWhere ((element) => element != null && element.text != "thin"
22+ && element.text != "medium" && element.text != "thick"
23+ && ! (element is css.LengthTerm ) && ! (element is css.PercentageTerm )
24+ && ! (element is css.EmTerm ) && ! (element is css.RemTerm )
25+ && ! (element is css.NumberTerm )
26+ );
27+ List <css.Expression ?>? borderColors = value.where ((element) => ExpressionMapping .expressionToColor (element) != null ).toList ();
28+ List <css.LiteralTerm ?>? potentialStyles = value.whereType< css.LiteralTerm > ().toList ();
29+ /// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
30+ List <String > possibleBorderValues = ["dotted" , "dashed" , "solid" , "double" , "groove" , "ridge" , "inset" , "outset" , "none" , "hidden" ];
31+ /// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style] , so make sure to remove those before passing it to [ExpressionMapping]
32+ potentialStyles.removeWhere ((element) => element != null && ! possibleBorderValues.contains (element.text));
33+ List <css.LiteralTerm ?>? borderStyles = potentialStyles;
34+ style.border = ExpressionMapping .expressionToBorder (borderWidths, borderStyles, borderColors);
35+ break ;
1636 case 'color' :
1737 style.color = ExpressionMapping .expressionToColor (value.first) ?? style.color;
1838 break ;
@@ -49,8 +69,15 @@ Style declarationsToStyle(Map<String?, List<css.Expression>> declarations) {
4969 textDecorationList.removeWhere ((element) => element != null && element.text != "none"
5070 && element.text != "overline" && element.text != "underline" && element.text != "line-through" );
5171 List <css.Expression ?>? nullableList = value;
52- css.Expression ? textDecorationColor = nullableList.firstWhere (
53- (css.Expression ? element) => element is css.HexColorTerm || element is css.FunctionTerm , orElse: () => null );
72+ css.Expression ? textDecorationColor;
73+ /// orElse: will not allow me to return null (even if the compiler says its okay, it errors on runtime).
74+ /// try/catch is a workaround for this.
75+ try {
76+ textDecorationColor = nullableList.firstWhere (
77+ (css.Expression ? element) => element is css.HexColorTerm || element is css.FunctionTerm );
78+ } catch (e) {
79+ textDecorationColor = null ;
80+ }
5481 List <css.LiteralTerm ?>? potentialStyles = value.whereType< css.LiteralTerm > ().toList ();
5582 /// List<css.LiteralTerm> might include other values than the ones we want for [textDecorationStyle] , so make sure to remove those before passing it to [ExpressionMapping]
5683 potentialStyles.removeWhere ((element) => element != null && element.text != "solid"
@@ -112,14 +139,132 @@ class DeclarationVisitor extends css.Visitor {
112139
113140//Mapping functions
114141class ExpressionMapping {
115- static Color ? expressionToColor (css.Expression value) {
116- if (value is css.HexColorTerm ) {
117- return stringToColor (value.text);
118- } else if (value is css.FunctionTerm ) {
119- if (value.text == 'rgba' ) {
120- return rgbOrRgbaToColor (value.span! .text);
121- } else if (value.text == 'rgb' ) {
122- return rgbOrRgbaToColor (value.span! .text);
142+
143+ static Border expressionToBorder (List <css.Expression ?>? borderWidths, List <css.LiteralTerm ?>? borderStyles, List <css.Expression ?>? borderColors) {
144+ CustomBorderSide left = CustomBorderSide ();
145+ CustomBorderSide top = CustomBorderSide ();
146+ CustomBorderSide right = CustomBorderSide ();
147+ CustomBorderSide bottom = CustomBorderSide ();
148+ if (borderWidths != null && borderWidths.isNotEmpty) {
149+ top.width = expressionToBorderWidth (borderWidths.first);
150+ if (borderWidths.length == 4 ) {
151+ right.width = expressionToBorderWidth (borderWidths[1 ]);
152+ bottom.width = expressionToBorderWidth (borderWidths[2 ]);
153+ left.width = expressionToBorderWidth (borderWidths.last);
154+ }
155+ if (borderWidths.length == 3 ) {
156+ left.width = expressionToBorderWidth (borderWidths[1 ]);
157+ right.width = expressionToBorderWidth (borderWidths[1 ]);
158+ bottom.width = expressionToBorderWidth (borderWidths.last);
159+ }
160+ if (borderWidths.length == 2 ) {
161+ bottom.width = expressionToBorderWidth (borderWidths.first);
162+ left.width = expressionToBorderWidth (borderWidths.last);
163+ right.width = expressionToBorderWidth (borderWidths.last);
164+ }
165+ if (borderWidths.length == 1 ) {
166+ bottom.width = expressionToBorderWidth (borderWidths.first);
167+ left.width = expressionToBorderWidth (borderWidths.first);
168+ right.width = expressionToBorderWidth (borderWidths.first);
169+ }
170+ }
171+ if (borderStyles != null && borderStyles.isNotEmpty) {
172+ top.style = expressionToBorderStyle (borderStyles.first);
173+ if (borderStyles.length == 4 ) {
174+ right.style = expressionToBorderStyle (borderStyles[1 ]);
175+ bottom.style = expressionToBorderStyle (borderStyles[2 ]);
176+ left.style = expressionToBorderStyle (borderStyles.last);
177+ }
178+ if (borderStyles.length == 3 ) {
179+ left.style = expressionToBorderStyle (borderStyles[1 ]);
180+ right.style = expressionToBorderStyle (borderStyles[1 ]);
181+ bottom.style = expressionToBorderStyle (borderStyles.last);
182+ }
183+ if (borderStyles.length == 2 ) {
184+ bottom.style = expressionToBorderStyle (borderStyles.first);
185+ left.style = expressionToBorderStyle (borderStyles.last);
186+ right.style = expressionToBorderStyle (borderStyles.last);
187+ }
188+ if (borderStyles.length == 1 ) {
189+ bottom.style = expressionToBorderStyle (borderStyles.first);
190+ left.style = expressionToBorderStyle (borderStyles.first);
191+ right.style = expressionToBorderStyle (borderStyles.first);
192+ }
193+ }
194+ if (borderColors != null && borderColors.isNotEmpty) {
195+ top.color = expressionToColor (borderColors.first);
196+ if (borderColors.length == 4 ) {
197+ right.color = expressionToColor (borderColors[1 ]);
198+ bottom.color = expressionToColor (borderColors[2 ]);
199+ left.color = expressionToColor (borderColors.last);
200+ }
201+ if (borderColors.length == 3 ) {
202+ left.color = expressionToColor (borderColors[1 ]);
203+ right.color = expressionToColor (borderColors[1 ]);
204+ bottom.color = expressionToColor (borderColors.last);
205+ }
206+ if (borderColors.length == 2 ) {
207+ bottom.color = expressionToColor (borderColors.first);
208+ left.color = expressionToColor (borderColors.last);
209+ right.color = expressionToColor (borderColors.last);
210+ }
211+ if (borderColors.length == 1 ) {
212+ bottom.color = expressionToColor (borderColors.first);
213+ left.color = expressionToColor (borderColors.first);
214+ right.color = expressionToColor (borderColors.first);
215+ }
216+ }
217+ return Border (
218+ top: BorderSide (width: top.width, color: top.color ?? Colors .black, style: top.style),
219+ right: BorderSide (width: right.width, color: right.color ?? Colors .black, style: right.style),
220+ bottom: BorderSide (width: bottom.width, color: bottom.color ?? Colors .black, style: bottom.style),
221+ left: BorderSide (width: left.width, color: left.color ?? Colors .black, style: left.style)
222+ );
223+ }
224+
225+ static double expressionToBorderWidth (css.Expression ? value) {
226+ if (value is css.NumberTerm ) {
227+ return double .tryParse (value.text) ?? 1.0 ;
228+ } else if (value is css.PercentageTerm ) {
229+ return (double .tryParse (value.text) ?? 400 ) / 100 ;
230+ } else if (value is css.EmTerm ) {
231+ return double .tryParse (value.text) ?? 1.0 ;
232+ } else if (value is css.RemTerm ) {
233+ return double .tryParse (value.text) ?? 1.0 ;
234+ } else if (value is css.LengthTerm ) {
235+ return double .tryParse (value.text.replaceAll (new RegExp (r'\s+(\d+\.\d+)\s+' ), '' )) ?? 1.0 ;
236+ } else if (value is css.LiteralTerm ) {
237+ switch (value.text) {
238+ case "thin" :
239+ return 2.0 ;
240+ case "medium" :
241+ return 4.0 ;
242+ case "thick" :
243+ return 6.0 ;
244+ }
245+ }
246+ return 4.0 ;
247+ }
248+
249+ static BorderStyle expressionToBorderStyle (css.LiteralTerm ? value) {
250+ if (value != null && value.text != "none" && value.text != "hidden" ) {
251+ return BorderStyle .solid;
252+ }
253+ return BorderStyle .none;
254+ }
255+
256+ static Color ? expressionToColor (css.Expression ? value) {
257+ if (value != null ) {
258+ if (value is css.HexColorTerm ) {
259+ return stringToColor (value.text);
260+ } else if (value is css.FunctionTerm ) {
261+ if (value.text == 'rgba' || value.text == 'rgb' ) {
262+ return rgbOrRgbaToColor (value.span! .text);
263+ } else if (value.text == 'hsla' || value.text == 'hsl' ) {
264+ return hslToRgbToColor (value.span! .text);
265+ }
266+ } else if (value is css.LiteralTerm ) {
267+ return namedColorToColor (value.text);
123268 }
124269 }
125270 return null ;
@@ -359,21 +504,21 @@ class ExpressionMapping {
359504 css.LiteralTerm ? exp4 = list.length > 3 ? list[3 ] as css.LiteralTerm ? : null ;
360505 RegExp nonNumberRegex = RegExp (r'\s+(\d+\.\d+)\s+' );
361506 if (exp is css.LiteralTerm && exp2 is css.LiteralTerm ) {
362- if (exp3 != null && (exp3 is css. HexColorTerm || exp3 is css. FunctionTerm ) ) {
507+ if (exp3 != null && ExpressionMapping . expressionToColor (exp3) != null ) {
363508 shadow.add (Shadow (
364- color: expressionToColor (exp3)! ,
509+ color: expressionToColor (exp3)! ,
365510 offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! )
366511 ));
367512 } else if (exp3 != null && exp3 is css.LiteralTerm ) {
368- if (exp4 != null && (exp4 is css. HexColorTerm || exp4 is css. FunctionTerm ) ) {
513+ if (exp4 != null && ExpressionMapping . expressionToColor (exp4) != null ) {
369514 shadow.add (Shadow (
370- color: expressionToColor (exp4)! ,
371- offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
515+ color: expressionToColor (exp4)! ,
516+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
372517 blurRadius: double .tryParse (exp3.text.replaceAll (nonNumberRegex, '' ))!
373518 ));
374519 } else {
375520 shadow.add (Shadow (
376- offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
521+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
377522 blurRadius: double .tryParse (exp3.text.replaceAll (nonNumberRegex, '' ))!
378523 ));
379524 }
@@ -427,4 +572,33 @@ class ExpressionMapping {
427572 return null ;
428573 }
429574 }
575+
576+ static Color hslToRgbToColor (String text) {
577+ final hslText = text.replaceAll (')' , '' ).replaceAll (' ' , '' );
578+ final hslValues = hslText.split (',' ).toList ();
579+ List <double ?> parsedHsl = [];
580+ hslValues.forEach ((element) {
581+ if (element.contains ("%" ) && double .tryParse (element.replaceAll ("%" , "" )) != null ) {
582+ parsedHsl.add (double .tryParse (element.replaceAll ("%" , "" ))! * 0.01 );
583+ } else {
584+ if (element != hslValues.first && (double .tryParse (element) == null || double .tryParse (element)! > 1 )) {
585+ parsedHsl.add (null );
586+ } else {
587+ parsedHsl.add (double .tryParse (element));
588+ }
589+ }
590+ });
591+ if (parsedHsl.length == 4 && ! parsedHsl.contains (null )) {
592+ return HSLColor .fromAHSL (parsedHsl.last! , parsedHsl.first! , parsedHsl[1 ]! , parsedHsl[2 ]! ).toColor ();
593+ } else if (parsedHsl.length == 3 && ! parsedHsl.contains (null )) {
594+ return HSLColor .fromAHSL (1.0 , parsedHsl.first! , parsedHsl[1 ]! , parsedHsl.last! ).toColor ();
595+ } else return Colors .black;
596+ }
597+
598+ static Color ? namedColorToColor (String text) {
599+ String namedColor = namedColors.keys.firstWhere ((element) => element.toLowerCase () == text.toLowerCase (), orElse: () => "" );
600+ if (namedColor != "" ) {
601+ return stringToColor (namedColors[namedColor]! );
602+ } else return null ;
603+ }
430604}
0 commit comments