Implementation
Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
Style style = Style();
declarations.forEach((property, value) {
if (value.isNotEmpty) {
switch (property) {
case 'background-color':
style.backgroundColor = ExpressionMapping.expressionToColor(value.first) ?? style.backgroundColor;
break;
case 'border':
List<css.LiteralTerm?>? borderWidths = value.whereType<css.LiteralTerm>().toList();
/// 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]
borderWidths.removeWhere((element) => element == null || (element.text != "thin"
&& element.text != "medium" && element.text != "thick"
&& element is! css.LengthTerm && element is! css.PercentageTerm
&& element is! css.EmTerm && element is! css.RemTerm
&& element is! css.NumberTerm)
);
List<css.Expression?>? borderColors = value.where((element) => ExpressionMapping.expressionToColor(element) != null).toList();
List<css.LiteralTerm?>? potentialStyles = value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = ["dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "none", "hidden"];
/// 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]
potentialStyles.removeWhere((element) => element == null || !possibleBorderValues.contains(element.text));
List<css.LiteralTerm?>? borderStyles = potentialStyles;
style.border = ExpressionMapping.expressionToBorder(borderWidths, borderStyles, borderColors);
break;
case 'border-left':
List<css.LiteralTerm?>? borderWidths = value.whereType<css.LiteralTerm>().toList();
/// 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]
borderWidths.removeWhere((element) => element == null || (element.text != "thin"
&& element.text != "medium" && element.text != "thick"
&& element is! css.LengthTerm && element is! css.PercentageTerm
&& element is! css.EmTerm && element is! css.RemTerm
&& element is! css.NumberTerm)
);
css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) => ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles = value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = ["dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "none", "hidden"];
/// 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]
potentialStyles.removeWhere((element) => element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ?? BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ?? Colors.black,
),
right: style.border?.right ?? BorderSide.none,
top: style.border?.top ?? BorderSide.none,
bottom: style.border?.bottom ?? BorderSide.none,
);
style.border = newBorder;
break;
case 'border-right':
List<css.LiteralTerm?>? borderWidths = value.whereType<css.LiteralTerm>().toList();
/// 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]
borderWidths.removeWhere((element) => element == null || (element.text != "thin"
&& element.text != "medium" && element.text != "thick"
&& element is! css.LengthTerm && element is! css.PercentageTerm
&& element is! css.EmTerm && element is! css.RemTerm
&& element is! css.NumberTerm)
);
css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) => ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles = value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = ["dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "none", "hidden"];
/// 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]
potentialStyles.removeWhere((element) => element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left ?? BorderSide.none,
right: style.border?.right.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ?? BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ?? Colors.black,
),
top: style.border?.top ?? BorderSide.none,
bottom: style.border?.bottom ?? BorderSide.none,
);
style.border = newBorder;
break;
case 'border-top':
List<css.LiteralTerm?>? borderWidths = value.whereType<css.LiteralTerm>().toList();
/// 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]
borderWidths.removeWhere((element) => element == null || (element.text != "thin"
&& element.text != "medium" && element.text != "thick"
&& element is! css.LengthTerm && element is! css.PercentageTerm
&& element is! css.EmTerm && element is! css.RemTerm
&& element is! css.NumberTerm)
);
css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) => ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles = value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = ["dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "none", "hidden"];
/// 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]
potentialStyles.removeWhere((element) => element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left ?? BorderSide.none,
right: style.border?.right ?? BorderSide.none,
top: style.border?.top.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ?? BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ?? Colors.black,
),
bottom: style.border?.bottom ?? BorderSide.none,
);
style.border = newBorder;
break;
case 'border-bottom':
List<css.LiteralTerm?>? borderWidths = value.whereType<css.LiteralTerm>().toList();
/// 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]
borderWidths.removeWhere((element) => element == null || (element.text != "thin"
&& element.text != "medium" && element.text != "thick"
&& element is! css.LengthTerm && element is! css.PercentageTerm
&& element is! css.EmTerm && element is! css.RemTerm
&& element is! css.NumberTerm)
);
css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) => ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles = value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = ["dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "none", "hidden"];
/// 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]
potentialStyles.removeWhere((element) => element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left ?? BorderSide.none,
right: style.border?.right ?? BorderSide.none,
top: style.border?.top ?? BorderSide.none,
bottom: style.border?.bottom.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ?? BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ?? Colors.black,
),
);
style.border = newBorder;
break;
case 'color':
style.color = ExpressionMapping.expressionToColor(value.first) ?? style.color;
break;
case 'direction':
style.direction = ExpressionMapping.expressionToDirection(value.first);
break;
case 'display':
style.display = ExpressionMapping.expressionToDisplay(value.first);
break;
case 'line-height':
style.lineHeight = ExpressionMapping.expressionToLineHeight(value.first);
break;
case 'font-family':
style.fontFamily = ExpressionMapping.expressionToFontFamily(value.first) ?? style.fontFamily;
break;
case 'font-feature-settings':
style.fontFeatureSettings = ExpressionMapping.expressionToFontFeatureSettings(value);
break;
case 'font-size':
style.fontSize = ExpressionMapping.expressionToFontSize(value.first) ?? style.fontSize;
break;
case 'font-style':
style.fontStyle = ExpressionMapping.expressionToFontStyle(value.first);
break;
case 'font-weight':
style.fontWeight = ExpressionMapping.expressionToFontWeight(value.first);
break;
case 'list-style':
css.LiteralTerm? position = value.firstWhereOrNull((e) => e is css.LiteralTerm && (e.text == "outside" || e.text == "inside")) as css.LiteralTerm?;
css.UriTerm? image = value.firstWhereOrNull((e) => e is css.UriTerm) as css.UriTerm?;
css.LiteralTerm? type = value.firstWhereOrNull((e) => e is css.LiteralTerm && e.text != "outside" && e.text != "inside") as css.LiteralTerm?;
if (position != null) {
switch (position.text) {
case 'outside':
style.listStylePosition = ListStylePosition.OUTSIDE;
break;
case 'inside':
style.listStylePosition = ListStylePosition.INSIDE;
break;
}
}
if (image != null) {
style.listStyleType = ExpressionMapping.expressionToListStyleType(image) ?? style.listStyleType;
} else if (type != null) {
style.listStyleType = ExpressionMapping.expressionToListStyleType(type) ?? style.listStyleType;
}
break;
case 'list-style-image':
if (value.first is css.UriTerm) {
style.listStyleType = ExpressionMapping.expressionToListStyleType(value.first as css.UriTerm) ?? style.listStyleType;
}
break;
case 'list-style-position':
if (value.first is css.LiteralTerm) {
switch ((value.first as css.LiteralTerm).text) {
case 'outside':
style.listStylePosition = ListStylePosition.OUTSIDE;
break;
case 'inside':
style.listStylePosition = ListStylePosition.INSIDE;
break;
}
}
break;
case 'height':
style.height = ExpressionMapping.expressionToPaddingLength(value.first) ?? style.height;
break;
case 'list-style-type':
if (value.first is css.LiteralTerm) {
style.listStyleType = ExpressionMapping.expressionToListStyleType(value.first as css.LiteralTerm) ?? style.listStyleType;
}
break;
case 'margin':
List<css.LiteralTerm>? marginLengths = value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for margin length, so make sure to remove those before passing it to [ExpressionMapping]
marginLengths.removeWhere((element) => element is! css.LengthTerm
&& element is! css.EmTerm
&& element is! css.RemTerm
&& element is! css.NumberTerm
);
List<double?> margin = ExpressionMapping.expressionToPadding(marginLengths);
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
left: margin[0],
right: margin[1],
top: margin[2],
bottom: margin[3],
);
break;
case 'margin-left':
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
left: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'margin-right':
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
right: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'margin-top':
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
top: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'margin-bottom':
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
bottom: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'padding':
List<css.LiteralTerm>? paddingLengths = value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for padding length, so make sure to remove those before passing it to [ExpressionMapping]
paddingLengths.removeWhere((element) => element is! css.LengthTerm
&& element is! css.EmTerm
&& element is! css.RemTerm
&& element is! css.NumberTerm
);
List<double?> padding = ExpressionMapping.expressionToPadding(paddingLengths);
style.padding = (style.padding ?? EdgeInsets.zero).copyWith(
left: padding[0],
right: padding[1],
top: padding[2],
bottom: padding[3],
);
break;
case 'padding-left':
style.padding = (style.padding ?? EdgeInsets.zero).copyWith(
left: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'padding-right':
style.padding = (style.padding ?? EdgeInsets.zero).copyWith(
right: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'padding-top':
style.padding = (style.padding ?? EdgeInsets.zero).copyWith(
top: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'padding-bottom':
style.padding = (style.padding ?? EdgeInsets.zero).copyWith(
bottom: ExpressionMapping.expressionToPaddingLength(value.first));
break;
case 'text-align':
style.textAlign = ExpressionMapping.expressionToTextAlign(value.first);
break;
case 'text-decoration':
List<css.LiteralTerm?>? textDecorationList = value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [textDecorationList], so make sure to remove those before passing it to [ExpressionMapping]
textDecorationList.removeWhere((element) => element == null || (element.text != "none"
&& element.text != "overline" && element.text != "underline" && element.text != "line-through"));
List<css.Expression?>? nullableList = value;
css.Expression? textDecorationColor;
textDecorationColor = nullableList.firstWhereOrNull(
(element) => element is css.HexColorTerm || element is css.FunctionTerm);
List<css.LiteralTerm?>? potentialStyles = value.whereType<css.LiteralTerm>().toList();
/// 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]
potentialStyles.removeWhere((element) => element == null || (element.text != "solid"
&& element.text != "double" && element.text != "dashed" && element.text != "dotted" && element.text != "wavy"));
css.LiteralTerm? textDecorationStyle = potentialStyles.isNotEmpty ? potentialStyles.last : null;
style.textDecoration = ExpressionMapping.expressionToTextDecorationLine(textDecorationList);
if (textDecorationColor != null) {
style.textDecorationColor = ExpressionMapping.expressionToColor(textDecorationColor)
?? style.textDecorationColor;
}
if (textDecorationStyle != null) style.textDecorationStyle = ExpressionMapping.expressionToTextDecorationStyle(textDecorationStyle);
break;
case 'text-decoration-color':
style.textDecorationColor = ExpressionMapping.expressionToColor(value.first) ?? style.textDecorationColor;
break;
case 'text-decoration-line':
List<css.LiteralTerm?>? textDecorationList = value.whereType<css.LiteralTerm>().toList();
style.textDecoration = ExpressionMapping.expressionToTextDecorationLine(textDecorationList);
break;
case 'text-decoration-style':
style.textDecorationStyle = ExpressionMapping.expressionToTextDecorationStyle(value.first as css.LiteralTerm);
break;
case 'text-shadow':
style.textShadow = ExpressionMapping.expressionToTextShadow(value);
break;
case 'text-transform':
final val = (value.first as css.LiteralTerm).text;
if (val == 'uppercase') {
style.textTransform = TextTransform.uppercase;
} else if (val == 'lowercase') {
style.textTransform = TextTransform.lowercase;
} else if (val == 'capitalize') {
style.textTransform = TextTransform.capitalize;
} else {
style.textTransform = TextTransform.none;
}
break;
case 'width':
style.width = ExpressionMapping.expressionToPaddingLength(value.first) ?? style.width;
break;
}
}
});
return style;
}