diff --git a/lib/src/editor/block_component/bulleted_list_block_component/bulleted_list_block_component.dart b/lib/src/editor/block_component/bulleted_list_block_component/bulleted_list_block_component.dart index 2d281aa70..523c16595 100644 --- a/lib/src/editor/block_component/bulleted_list_block_component/bulleted_list_block_component.dart +++ b/lib/src/editor/block_component/bulleted_list_block_component/bulleted_list_block_component.dart @@ -15,16 +15,18 @@ class BulletedListBlockKeys { } Node bulletedListNode({ + String? text, Delta? delta, String? textDirection, Attributes? attributes, Iterable? children, }) { - attributes ??= {BulletedListBlockKeys.delta: (delta ?? Delta()).toJson()}; return Node( type: BulletedListBlockKeys.type, attributes: { - ...attributes, + BulletedListBlockKeys.delta: + (delta ?? (Delta()..insert(text ?? ''))).toJson(), + if (attributes != null) ...attributes, if (textDirection != null) BulletedListBlockKeys.textDirection: textDirection, }, diff --git a/lib/src/editor/toolbar/desktop/floating_toolbar.dart b/lib/src/editor/toolbar/desktop/floating_toolbar.dart index 13d4ccaaa..ca2a7a0dc 100644 --- a/lib/src/editor/toolbar/desktop/floating_toolbar.dart +++ b/lib/src/editor/toolbar/desktop/floating_toolbar.dart @@ -6,9 +6,11 @@ import 'package:flutter/material.dart'; class FloatingToolbarStyle { const FloatingToolbarStyle({ this.backgroundColor = Colors.black, + this.toolbarActiveColor = Colors.lightBlue, }); final Color backgroundColor; + final Color toolbarActiveColor; } /// A floating toolbar that displays at the top of the editor when the selection @@ -168,6 +170,7 @@ class _FloatingToolbarState extends State items: widget.items, editorState: editorState, backgroundColor: widget.style.backgroundColor, + toolbarActiveColor: widget.style.toolbarActiveColor, ); return _toolbarWidget!; } diff --git a/lib/src/editor/toolbar/desktop/floating_toolbar_widget.dart b/lib/src/editor/toolbar/desktop/floating_toolbar_widget.dart index eb06629e6..1385fd9c4 100644 --- a/lib/src/editor/toolbar/desktop/floating_toolbar_widget.dart +++ b/lib/src/editor/toolbar/desktop/floating_toolbar_widget.dart @@ -8,12 +8,14 @@ class FloatingToolbarWidget extends StatefulWidget { const FloatingToolbarWidget({ super.key, this.backgroundColor = Colors.black, + required this.toolbarActiveColor, required this.items, required this.editorState, }); final List items; final Color backgroundColor; + final Color toolbarActiveColor; final EditorState editorState; @override @@ -40,7 +42,11 @@ class _FloatingToolbarWidgetState extends State { children: activeItems.map((item) { final builder = item.builder; return Center( - child: builder!(context, widget.editorState), + child: builder!( + context, + widget.editorState, + widget.toolbarActiveColor, + ), ); }).toList(growable: false), ), diff --git a/lib/src/editor/toolbar/desktop/items/bulleted_list_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/bulleted_list_toolbar_item.dart index 6771cf220..395c785b0 100644 --- a/lib/src/editor/toolbar/desktop/items/bulleted_list_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/bulleted_list_toolbar_item.dart @@ -4,13 +4,14 @@ final ToolbarItem bulletedListItem = ToolbarItem( id: 'editor.bulleted_list', group: 3, isActive: onlyShowInSingleSelectionAndTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final node = editorState.getNodeAtPath(selection.start.path)!; final isHighlight = node.type == 'bulleted_list'; return SVGIconItemWidget( iconName: 'toolbar/bulleted_list', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: AppFlowyEditorLocalizations.current.bulletedList, onPressed: () => editorState.formatNode( selection, diff --git a/lib/src/editor/toolbar/desktop/items/color/highlight_color_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/color/highlight_color_toolbar_item.dart index 970640cfe..846a44b19 100644 --- a/lib/src/editor/toolbar/desktop/items/color/highlight_color_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/color/highlight_color_toolbar_item.dart @@ -6,7 +6,7 @@ ToolbarItem buildHighlightColorItem({List? colorOptions}) { id: 'editor.highlightColor', group: 4, isActive: onlyShowInTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { String? highlightColorHex; final selection = editorState.selection!; @@ -21,6 +21,7 @@ ToolbarItem buildHighlightColorItem({List? colorOptions}) { iconName: 'toolbar/highlight_color', iconSize: const Size.square(14), isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: AppFlowyEditorLocalizations.current.highlightColor, onPressed: () { showColorMenu( diff --git a/lib/src/editor/toolbar/desktop/items/color/text_color_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/color/text_color_toolbar_item.dart index b9b5d3e93..c06d38fde 100644 --- a/lib/src/editor/toolbar/desktop/items/color/text_color_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/color/text_color_toolbar_item.dart @@ -8,7 +8,7 @@ ToolbarItem buildTextColorItem({ id: 'editor.textColor', group: 4, isActive: onlyShowInTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { String? textColorHex; final selection = editorState.selection!; final nodes = editorState.getNodesInSelection(selection); @@ -21,6 +21,7 @@ ToolbarItem buildTextColorItem({ return SVGIconItemWidget( iconName: 'toolbar/text_color', isHighlight: isHighlight, + highlightColor: highlightColor, iconSize: const Size.square(14), tooltip: AppFlowyEditorLocalizations.current.textColor, onPressed: () { diff --git a/lib/src/editor/toolbar/desktop/items/format_toolbar_items.dart b/lib/src/editor/toolbar/desktop/items/format_toolbar_items.dart index 1ad43bda4..94a328c7d 100644 --- a/lib/src/editor/toolbar/desktop/items/format_toolbar_items.dart +++ b/lib/src/editor/toolbar/desktop/items/format_toolbar_items.dart @@ -43,7 +43,7 @@ class _FormatToolbarItem extends ToolbarItem { id: 'editor.$id', group: 2, isActive: onlyShowInTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final nodes = editorState.getNodesInSelection(selection); final isHighlight = nodes.allSatisfyInSelection(selection, (delta) { @@ -54,6 +54,7 @@ class _FormatToolbarItem extends ToolbarItem { return SVGIconItemWidget( iconName: 'toolbar/$name', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: tooltip, onPressed: () => editorState.toggleAttribute(name), ); diff --git a/lib/src/editor/toolbar/desktop/items/heading_toolbar_items.dart b/lib/src/editor/toolbar/desktop/items/heading_toolbar_items.dart index 021e004d0..92d24f4df 100644 --- a/lib/src/editor/toolbar/desktop/items/heading_toolbar_items.dart +++ b/lib/src/editor/toolbar/desktop/items/heading_toolbar_items.dart @@ -12,7 +12,7 @@ class _HeadingToolbarItem extends ToolbarItem { id: 'editor.h$level', group: 1, isActive: onlyShowInSingleSelectionAndTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final node = editorState.getNodeAtPath(selection.start.path)!; final isHighlight = @@ -21,6 +21,7 @@ class _HeadingToolbarItem extends ToolbarItem { return SVGIconItemWidget( iconName: 'toolbar/h$level', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: levelToTooltips(level), onPressed: () => editorState.formatNode( selection, diff --git a/lib/src/editor/toolbar/desktop/items/highlight_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/highlight_toolbar_item.dart index 22cd17a19..a7bf4b81c 100644 --- a/lib/src/editor/toolbar/desktop/items/highlight_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/highlight_toolbar_item.dart @@ -6,7 +6,7 @@ final highlightItem = ToolbarItem( id: 'editor.highlight', group: 5, isActive: (editorState) => editorState.selection?.isSingle ?? false, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final nodes = editorState.getNodesInSelection(selection); final isHighlight = nodes.allSatisfyInSelection(selection, (delta) { @@ -17,6 +17,7 @@ final highlightItem = ToolbarItem( return SVGIconItemWidget( iconName: 'toolbar/link', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: '${AppFlowyEditorLocalizations.current.link}${shortcutTooltips("⌘ + K", "CTRL + K", "CTRL + K")}', onPressed: () {}, diff --git a/lib/src/editor/toolbar/desktop/items/icon_item_widget.dart b/lib/src/editor/toolbar/desktop/items/icon_item_widget.dart index dc3225692..4a8cb389e 100644 --- a/lib/src/editor/toolbar/desktop/items/icon_item_widget.dart +++ b/lib/src/editor/toolbar/desktop/items/icon_item_widget.dart @@ -9,7 +9,7 @@ class SVGIconItemWidget extends StatelessWidget { this.iconName, this.iconBuilder, required this.isHighlight, - this.highlightColor = Colors.lightBlue, + required this.highlightColor, this.tooltip, this.onPressed, }); diff --git a/lib/src/editor/toolbar/desktop/items/link/link_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/link/link_toolbar_item.dart index dd2e17e10..010800d34 100644 --- a/lib/src/editor/toolbar/desktop/items/link/link_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/link/link_toolbar_item.dart @@ -7,7 +7,7 @@ final linkItem = ToolbarItem( id: 'editor.link', group: 4, isActive: onlyShowInSingleSelectionAndTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final nodes = editorState.getNodesInSelection(selection); final isHref = nodes.allSatisfyInSelection(selection, (delta) { @@ -19,6 +19,7 @@ final linkItem = ToolbarItem( return SVGIconItemWidget( iconName: 'toolbar/link', isHighlight: isHref, + highlightColor: highlightColor, tooltip: AppFlowyEditorLocalizations.current.link, onPressed: () { showLinkMenu(context, editorState, selection, isHref); diff --git a/lib/src/editor/toolbar/desktop/items/numbered_list_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/numbered_list_toolbar_item.dart index 05fac9b11..4ffd6b70d 100644 --- a/lib/src/editor/toolbar/desktop/items/numbered_list_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/numbered_list_toolbar_item.dart @@ -4,13 +4,14 @@ final ToolbarItem numberedListItem = ToolbarItem( id: 'editor.numbered_list', group: 3, isActive: onlyShowInSingleSelectionAndTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final node = editorState.getNodeAtPath(selection.start.path)!; final isHighlight = node.type == 'numbered_list'; return SVGIconItemWidget( iconName: 'toolbar/numbered_list', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: AppFlowyEditorLocalizations.current.numberedList, onPressed: () => editorState.formatNode( selection, diff --git a/lib/src/editor/toolbar/desktop/items/paragraph_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/paragraph_toolbar_item.dart index fda30dd99..1c961248c 100644 --- a/lib/src/editor/toolbar/desktop/items/paragraph_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/paragraph_toolbar_item.dart @@ -4,7 +4,7 @@ final ToolbarItem paragraphItem = ToolbarItem( id: 'editor.paragraph', group: 1, isActive: onlyShowInSingleSelectionAndTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final node = editorState.getNodeAtPath(selection.start.path)!; final isHighlight = node.type == 'paragraph'; @@ -12,6 +12,7 @@ final ToolbarItem paragraphItem = ToolbarItem( return SVGIconItemWidget( iconName: 'toolbar/text', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: AppFlowyEditorLocalizations.current.text, onPressed: () => editorState.formatNode( selection, diff --git a/lib/src/editor/toolbar/desktop/items/placeholder_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/placeholder_toolbar_item.dart index 1a19b0dec..1d0b88366 100644 --- a/lib/src/editor/toolbar/desktop/items/placeholder_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/placeholder_toolbar_item.dart @@ -7,7 +7,7 @@ final ToolbarItem placeholderItem = ToolbarItem( id: placeholderItemId, group: -1, isActive: (editorState) => true, - builder: (_, __) { + builder: (_, __, ___) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5), child: Container( diff --git a/lib/src/editor/toolbar/desktop/items/quote_toolbar_item.dart b/lib/src/editor/toolbar/desktop/items/quote_toolbar_item.dart index d87f11807..147f149ef 100644 --- a/lib/src/editor/toolbar/desktop/items/quote_toolbar_item.dart +++ b/lib/src/editor/toolbar/desktop/items/quote_toolbar_item.dart @@ -4,13 +4,14 @@ final ToolbarItem quoteItem = ToolbarItem( id: 'editor.quote', group: 3, isActive: onlyShowInSingleSelectionAndTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final node = editorState.getNodeAtPath(selection.start.path)!; final isHighlight = node.type == 'quote'; return SVGIconItemWidget( iconName: 'toolbar/quote', isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: AppFlowyEditorLocalizations.current.quote, onPressed: () => editorState.formatNode( selection, diff --git a/lib/src/editor/toolbar/desktop/items/text_direction_toolbar_items.dart b/lib/src/editor/toolbar/desktop/items/text_direction_toolbar_items.dart index f1bc4fab8..67317fd95 100644 --- a/lib/src/editor/toolbar/desktop/items/text_direction_toolbar_items.dart +++ b/lib/src/editor/toolbar/desktop/items/text_direction_toolbar_items.dart @@ -32,7 +32,7 @@ class _TextDirectionToolbarItem extends ToolbarItem { id: 'editor.$id', group: 6, isActive: onlyShowInTextType, - builder: (context, editorState) { + builder: (context, editorState, highlightColor) { final selection = editorState.selection!; final nodes = editorState.getNodesInSelection(selection); final isHighlight = nodes.every( @@ -42,9 +42,10 @@ class _TextDirectionToolbarItem extends ToolbarItem { iconBuilder: (_) => Icon( icon, size: 16, - color: isHighlight ? Colors.lightBlue : Colors.white, + color: isHighlight ? highlightColor : Colors.white, ), isHighlight: isHighlight, + highlightColor: highlightColor, tooltip: tooltip, onPressed: () => editorState.formatNode( selection, diff --git a/lib/src/render/toolbar/toolbar_item.dart b/lib/src/render/toolbar/toolbar_item.dart index 81b41dd05..7b588a618 100644 --- a/lib/src/render/toolbar/toolbar_item.dart +++ b/lib/src/render/toolbar/toolbar_item.dart @@ -26,7 +26,11 @@ class ToolbarItem { final String id; final int group; final bool Function(EditorState editorState)? isActive; - final Widget Function(BuildContext context, EditorState editorState)? builder; + final Widget Function( + BuildContext context, + EditorState editorState, + Color highlightColor, + )? builder; // deprecated final int type; diff --git a/lib/src/render/toolbar/toolbar_widget.dart b/lib/src/render/toolbar/toolbar_widget.dart index d6022fd0f..8af8f9803 100644 --- a/lib/src/render/toolbar/toolbar_widget.dart +++ b/lib/src/render/toolbar/toolbar_widget.dart @@ -15,6 +15,7 @@ class ToolbarWidget extends StatefulWidget { required this.editorState, required this.layerLink, required this.offset, + required this.highlightColor, required this.items, this.alignment = Alignment.topLeft, }) : super(key: key); @@ -22,6 +23,7 @@ class ToolbarWidget extends StatefulWidget { final EditorState editorState; final LayerLink layerLink; final Offset offset; + final Color highlightColor; final List items; @@ -44,7 +46,7 @@ class _ToolbarWidgetState extends State with ToolbarMixin { showWhenUnlinked: true, offset: widget.offset, followerAnchor: widget.alignment, - child: _buildToolbar(context), + child: _buildToolbar(context, widget.highlightColor), ), ); } @@ -55,7 +57,7 @@ class _ToolbarWidgetState extends State with ToolbarMixin { _listToolbarOverlay = null; } - Widget _buildToolbar(BuildContext context) { + Widget _buildToolbar(BuildContext context, Color highlightColor) { return Material( borderRadius: BorderRadius.circular(8.0), child: Padding( @@ -67,7 +69,11 @@ class _ToolbarWidgetState extends State with ToolbarMixin { children: widget.items .map( (item) => Center( - child: item.builder?.call(context, widget.editorState) ?? + child: item.builder?.call( + context, + widget.editorState, + highlightColor, + ) ?? item.itemBuilder?.call(context, widget.editorState) ?? ToolbarItemWidget( item: item, diff --git a/test/customer/custom_toolbar_item_color_test.dart b/test/customer/custom_toolbar_item_color_test.dart new file mode 100644 index 000000000..bb3276fa9 --- /dev/null +++ b/test/customer/custom_toolbar_item_color_test.dart @@ -0,0 +1,78 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() async { + setUpAll(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + + testWidgets('customize highlight color', (tester) async { + const text = 'Hello World'; + final document = Document.blank() + ..insert( + [0], + [bulletedListNode(text: text)], + ); + final editorState = EditorState(document: document); + final widget = CustomToolbarItemColor( + editorState: editorState, + ); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); + + // update selection and show the toolbar + editorState.updateSelectionWithReason( + Selection.single(path: [0], startOffset: 0, endOffset: text.length), + ); + await tester.pumpAndSettle(const Duration(milliseconds: 500)); + expect(find.byType(FloatingToolbar), findsOneWidget); + final bulletedListItem = tester.widget( + find.byType(SVGIconItemWidget), + ); + expect(bulletedListItem.highlightColor, Colors.green); + }); +} + +class CustomToolbarItemColor extends StatelessWidget { + CustomToolbarItemColor({ + super.key, + required this.editorState, + }); + + final EditorState editorState; + final scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + return MaterialApp( + localizationsDelegates: const [ + AppFlowyEditorLocalizations.delegate, + ], + supportedLocales: const [Locale('en', 'US')], + home: Scaffold( + body: SafeArea( + child: Container( + width: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.blue), + ), + child: FloatingToolbar( + items: [bulletedListItem], + style: const FloatingToolbarStyle( + backgroundColor: Colors.red, + toolbarActiveColor: Colors.green, + ), + editorState: editorState, + scrollController: scrollController, + child: AppFlowyEditor( + editorState: editorState, + scrollController: scrollController, + ), + ), + ), + ), + ), + ); + } +}