diff --git a/example/lib/components/grid_card.dart b/example/lib/components/grid_card.dart index 80f90d1..aea592a 100644 --- a/example/lib/components/grid_card.dart +++ b/example/lib/components/grid_card.dart @@ -31,7 +31,7 @@ class GridCardWidget extends StatelessWidget { return Container( decoration: BoxDecoration( - color: hexColor.isEmpty ? AppColors.white : HexColor(hexColor), + color: hexColor.isEmpty ? AppColors.white : StoryblokColor.fromString(hexColor), boxShadow: [ BoxShadow( color: AppColors.black.withOpacity(0.1), diff --git a/lib/src/fields/rich_text.dart b/lib/src/fields/rich_text.dart index f630f2f..896aa93 100644 --- a/lib/src/fields/rich_text.dart +++ b/lib/src/fields/rich_text.dart @@ -226,7 +226,7 @@ final class RichTextLeafImage extends RichTextLeafMarkable implements RichTextLe source: json["source"], alt: json["alt"], copyright: json["copyright"], - metadata: JSONMap.from(json["meta_data"]), + metadata: JSONMap.from(tryCast(json["meta_data"]) ?? {}), marks: RichTextLeafMarkable.marksFromJson(json), ); } @@ -310,23 +310,23 @@ final class RichTextLeafMarkLink implements RichTextLeafMark { } final class RichTextLeafMarkTextStyle implements RichTextLeafMark { - RichTextLeafMarkTextStyle({required this.colorHex}); + RichTextLeafMarkTextStyle({required this.colorString}); factory RichTextLeafMarkTextStyle.fromJson(JSONMap json) => RichTextLeafMarkTextStyle( - colorHex: json["attrs"]["color"], + colorString: json["attrs"]["color"], ); - /// CSS style color hex e.g. "#FAFAFA" - final String colorHex; + /// CSS style color hex e.g. "#FAFAFA" or "rgb(255, 255, 255)" + final String colorString; } final class RichTextLeafMarkHighlight implements RichTextLeafMark { - RichTextLeafMarkHighlight({required this.colorHex}); + RichTextLeafMarkHighlight({required this.colorString}); factory RichTextLeafMarkHighlight.fromJson(JSONMap json) => RichTextLeafMarkHighlight( - colorHex: json["attrs"]["color"], + colorString: json["attrs"]["color"], ); - /// CSS style color hex e.g. "#FAFAFA" - final String colorHex; + /// CSS style color hex e.g. "#FAFAFA" or "rgb(255, 255, 255)" + final String colorString; } /// Base class of markable leaves diff --git a/lib/src/widgets/hex_color.dart b/lib/src/widgets/hex_color.dart deleted file mode 100644 index 791a971..0000000 --- a/lib/src/widgets/hex_color.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; - -/// Used to convert a color hex string to a Flutter Color -final class HexColor extends Color { - static int _getColorFromHex(String hexColor) { - hexColor = hexColor.toUpperCase().replaceAll("#", ""); - if (hexColor.length == 6) { - hexColor = "FF$hexColor"; - } - return int.parse(hexColor, radix: 16); - } - - HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); -} diff --git a/lib/src/widgets/rich_text.dart b/lib/src/widgets/rich_text.dart index 445a6f9..816a9ea 100644 --- a/lib/src/widgets/rich_text.dart +++ b/lib/src/widgets/rich_text.dart @@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart' as sb; import 'package:flutter_storyblok/src/fields/link.dart'; import 'package:flutter_storyblok/src/fields/rich_text.dart'; import 'package:flutter_storyblok/src/utils.dart'; -import 'package:flutter_storyblok/src/widgets/hex_color.dart'; +import 'package:flutter_storyblok/widgets.dart'; typedef BlockBuilder = Widget Function(BuildContext context, JSONMap data); @@ -153,10 +153,9 @@ extension _RichTextLeafBuildWidget on List { extension _RichTextLeafMarkableWidget on RichTextLeafMarkable { TextStyle buildTextStyle() { - final foregroundColor = - mapIfNotNull(this.foregroundColor?.colorHex, HexColor.new) ?? (link != null ? Colors.black : null); + final foregroundColor = this.foregroundColor?.color ?? (link != null ? Colors.black : null); return TextStyle( - backgroundColor: isCode ? Colors.grey : mapIfNotNull(backgroundColor?.colorHex, HexColor.new), + backgroundColor: isCode ? Colors.grey : backgroundColor?.color, color: foregroundColor, fontStyle: isItalic || isCode ? FontStyle.italic : null, fontWeight: isBold ? FontWeight.bold : null, diff --git a/lib/src/widgets/storyblok_color.dart b/lib/src/widgets/storyblok_color.dart new file mode 100644 index 0000000..7d5a4b3 --- /dev/null +++ b/lib/src/widgets/storyblok_color.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +/// Used to convert a color hex string to a Flutter Color +final class StoryblokColor extends Color { + StoryblokColor(super.value); + factory StoryblokColor.fromString(String colorString) { + final cssColorMatch = cssColorRegex.firstMatch(colorString); + if (cssColorMatch != null) return StoryblokColor(_getColorFromCSS(cssColorMatch)); + return StoryblokColor(_getColorFromHex(colorString)); + } + + static int _getColorFromHex(String hexColor) { + hexColor = hexColor.replaceAll("#", ""); + if (hexColor.length == 6) hexColor = "FF$hexColor"; + return int.parse(hexColor, radix: 16); + } + + static final cssColorRegex = RegExp(r"rgb\((\d{1,3}),\s?(\d{1,3}),\s?(\d{1,3})\)", caseSensitive: false); + static int _getColorFromCSS(RegExpMatch match) { + final r = int.parse(match.group(1)!); + final g = int.parse(match.group(2)!); + final b = int.parse(match.group(3)!); + return 0xFF << 8 * 3 | // + r << 8 * 2 | // + g << 8 * 1 | // + b << 8 * 0; + } +} diff --git a/lib/widgets.dart b/lib/widgets.dart index 79f41b8..3939804 100644 --- a/lib/widgets.dart +++ b/lib/widgets.dart @@ -1,4 +1,16 @@ library; -export 'src/widgets/hex_color.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_storyblok/src/fields/rich_text.dart'; +import 'package:flutter_storyblok/src/widgets/storyblok_color.dart'; + +export 'src/widgets/storyblok_color.dart'; export 'src/widgets/rich_text.dart'; + +extension RichTextLeafMarkTextStyleColor on RichTextLeafMarkTextStyle { + Color get color => StoryblokColor.fromString(colorString); +} + +extension RichTextLeafMarkHighlightColor on RichTextLeafMarkHighlight { + Color get color => StoryblokColor.fromString(colorString); +} diff --git a/pubspec.yaml b/pubspec.yaml index b2ba111..28b2efc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,4 +19,5 @@ dependencies: dev_dependencies: lints: ^4.0.0 + test: ^1.25.8 build_runner: ^2.4.12 diff --git a/test/rich_text_test.dart b/test/rich_text_test.dart new file mode 100644 index 0000000..20a364b --- /dev/null +++ b/test/rich_text_test.dart @@ -0,0 +1,69 @@ +import 'package:flutter_storyblok/fields.dart'; +import 'package:test/test.dart'; + +void main() { + group('Test parsing rich-text', () { + test('Test parse rich-text basic text', () { + final text = RichTextLeafText.fromJson({ + "type": "text", + "text": "Hello world", + }); + expect(text.text, "Hello world"); + expect(text.anchor, null); + expect(text.backgroundColor, null); + expect(text.foregroundColor, null); + expect(text.isBold, false); + expect(text.isCode, false); + expect(text.isItalic, false); + expect(text.isStriked, false); + expect(text.isSubscript, false); + expect(text.isSuperscript, false); + expect(text.isUnderlined, false); + expect(text.link, null); + }); + + test('Test parse rich-text text with foreground color', () { + final text = RichTextLeafText.fromJson({ + "text": "Hello world", + "marks": [ + { + "type": "textStyle", + "attrs": {"color": "#FF0000"} + } + ] + }); + expect(text.foregroundColor!.colorString, "#FF0000"); + }); + + test('Test parse rich-text text with foreground color css', () { + final text = RichTextLeafText.fromJson({ + "text": "Hello world", + "marks": [ + { + "type": "textStyle", + "attrs": {"color": "rgb(255, 0, 0)"} + } + ] + }); + expect(text.foregroundColor!.colorString, "rgb(255, 0, 0)"); + }); + + test('Test parse rich-text image', () { + final image = RichTextLeafImage.fromJson({ + "type": "image", + "attrs": { + "id": 123, + "alt": "hello", + "src": "https://placehold.it/100x100", + "title": "hello", + "source": "hello", + "copyright": "hello", + "meta_data": {"alt": "hello"} + } + }); + expect(image.imageUrl, Uri.parse("https://placehold.it/100x100")); + expect(image.alt, "hello"); + expect(image.metadata["alt"], "hello"); + }); + }); +} diff --git a/test/storyblok_color_test.dart b/test/storyblok_color_test.dart new file mode 100644 index 0000000..bf1884d --- /dev/null +++ b/test/storyblok_color_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_storyblok/widgets.dart'; +import 'package:test/test.dart'; + +void main() { + group('Test parsing colors from storyblok', () { + test('Test parse color hexadecimal', () { + final color = StoryblokColor.fromString("#FF8800"); + expect(color.alpha, 0xFF); + expect(color.red, 0xFF); + expect(color.green, 0x88); + expect(color.blue, 0x00); + }); + + test('Test parse color css', () { + final color = StoryblokColor.fromString("rgb(255, 136, 0)"); + expect(color.alpha, 0xFF); + expect(color.red, 255); + expect(color.green, 136); + expect(color.blue, 0); + }); + }); +}