mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: customize appflowy editor selection menu style
This commit is contained in:
parent
8656cfeb1e
commit
23a65bfa2a
@ -39,6 +39,8 @@ class MyApp extends StatelessWidget {
|
||||
primarySwatch: Colors.blue,
|
||||
// extensions: [HeadingPluginStyle.light],
|
||||
),
|
||||
darkTheme: ThemeData.dark(),
|
||||
themeMode: ThemeMode.dark,
|
||||
home: const MyHomePage(title: 'AppFlowyEditor Example'),
|
||||
);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ ShortcutEventHandler _ignorekHandler = (editorState, event) {
|
||||
|
||||
SelectionMenuItem codeBlockMenuItem = SelectionMenuItem(
|
||||
name: () => 'Code Block',
|
||||
icon: const Icon(
|
||||
icon: (_, __) => const Icon(
|
||||
Icons.abc,
|
||||
color: Colors.black,
|
||||
size: 18.0,
|
||||
|
@ -38,7 +38,7 @@ ShortcutEventHandler _insertHorzaontalRule = (editorState, event) {
|
||||
|
||||
SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem(
|
||||
name: () => 'Horizontal rule',
|
||||
icon: const Icon(
|
||||
icon: (_, __) => const Icon(
|
||||
Icons.horizontal_rule,
|
||||
color: Colors.black,
|
||||
size: 18.0,
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter_math_fork/flutter_math.dart';
|
||||
|
||||
SelectionMenuItem teXBlockMenuItem = SelectionMenuItem(
|
||||
name: () => 'Tex',
|
||||
icon: const Icon(
|
||||
icon: (_, __) => const Icon(
|
||||
Icons.text_fields_rounded,
|
||||
color: Colors.black,
|
||||
size: 18.0,
|
||||
|
@ -3,7 +3,7 @@ import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service
|
||||
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SelectionMenuItemWidget extends StatelessWidget {
|
||||
class SelectionMenuItemWidget extends StatefulWidget {
|
||||
const SelectionMenuItemWidget({
|
||||
Key? key,
|
||||
required this.editorState,
|
||||
@ -11,7 +11,6 @@ class SelectionMenuItemWidget extends StatelessWidget {
|
||||
required this.item,
|
||||
required this.isSelected,
|
||||
this.width = 140.0,
|
||||
this.selectedColor = const Color(0xFFE0F8FF),
|
||||
}) : super(key: key);
|
||||
|
||||
final EditorState editorState;
|
||||
@ -19,33 +18,52 @@ class SelectionMenuItemWidget extends StatelessWidget {
|
||||
final SelectionMenuItem item;
|
||||
final double width;
|
||||
final bool isSelected;
|
||||
final Color selectedColor;
|
||||
|
||||
@override
|
||||
State<SelectionMenuItemWidget> createState() =>
|
||||
_SelectionMenuItemWidgetState();
|
||||
}
|
||||
|
||||
class _SelectionMenuItemWidgetState extends State<SelectionMenuItemWidget> {
|
||||
var _onHover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final editorStyle = widget.editorState.editorStyle;
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(8.0, 5.0, 8.0, 5.0),
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
width: widget.width,
|
||||
child: TextButton.icon(
|
||||
icon: item.icon,
|
||||
icon: widget.item
|
||||
.icon(widget.editorState, widget.isSelected || _onHover),
|
||||
style: ButtonStyle(
|
||||
alignment: Alignment.centerLeft,
|
||||
overlayColor: MaterialStateProperty.all(selectedColor),
|
||||
backgroundColor: isSelected
|
||||
? MaterialStateProperty.all(selectedColor)
|
||||
overlayColor: MaterialStateProperty.all(
|
||||
editorStyle.selectionMenuItemSelectedColor),
|
||||
backgroundColor: widget.isSelected
|
||||
? MaterialStateProperty.all(
|
||||
editorStyle.selectionMenuItemSelectedColor)
|
||||
: MaterialStateProperty.all(Colors.transparent),
|
||||
),
|
||||
label: Text(
|
||||
item.name(),
|
||||
widget.item.name(),
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14.0,
|
||||
style: TextStyle(
|
||||
color: widget.isSelected || _onHover
|
||||
? editorStyle.selectionMenuItemSelectedTextColor
|
||||
: editorStyle.selectionMenuItemTextColor,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
item.handler(editorState, menuService, context);
|
||||
widget.item
|
||||
.handler(widget.editorState, widget.menuService, context);
|
||||
},
|
||||
onHover: (value) {
|
||||
setState(() {
|
||||
_onHover = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -131,7 +131,8 @@ List<SelectionMenuItem> get defaultSelectionMenuItems =>
|
||||
final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.text,
|
||||
icon: _selectionMenuIcon('text'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('text', editorState, onSelected),
|
||||
keywords: ['text'],
|
||||
handler: (editorState, _, __) {
|
||||
insertTextNodeAfterSelection(editorState, {});
|
||||
@ -139,7 +140,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.heading1,
|
||||
icon: _selectionMenuIcon('h1'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('h1', editorState, onSelected),
|
||||
keywords: ['heading 1, h1'],
|
||||
handler: (editorState, _, __) {
|
||||
insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h1);
|
||||
@ -147,7 +149,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.heading2,
|
||||
icon: _selectionMenuIcon('h2'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('h2', editorState, onSelected),
|
||||
keywords: ['heading 2, h2'],
|
||||
handler: (editorState, _, __) {
|
||||
insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h2);
|
||||
@ -155,7 +158,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.heading3,
|
||||
icon: _selectionMenuIcon('h3'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('h3', editorState, onSelected),
|
||||
keywords: ['heading 3, h3'],
|
||||
handler: (editorState, _, __) {
|
||||
insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h3);
|
||||
@ -163,13 +167,15 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.image,
|
||||
icon: _selectionMenuIcon('image'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('image', editorState, onSelected),
|
||||
keywords: ['image'],
|
||||
handler: showImageUploadMenu,
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.bulletedList,
|
||||
icon: _selectionMenuIcon('bulleted_list'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('bulleted_list', editorState, onSelected),
|
||||
keywords: ['bulleted list', 'list', 'unordered list'],
|
||||
handler: (editorState, _, __) {
|
||||
insertBulletedListAfterSelection(editorState);
|
||||
@ -177,7 +183,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.numberedList,
|
||||
icon: _selectionMenuIcon('number'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('number', editorState, onSelected),
|
||||
keywords: ['numbered list', 'list', 'ordered list'],
|
||||
handler: (editorState, _, __) {
|
||||
insertNumberedListAfterSelection(editorState);
|
||||
@ -185,7 +192,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.checkbox,
|
||||
icon: _selectionMenuIcon('checkbox'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('checkbox', editorState, onSelected),
|
||||
keywords: ['todo list', 'list', 'checkbox list'],
|
||||
handler: (editorState, _, __) {
|
||||
insertCheckboxAfterSelection(editorState);
|
||||
@ -193,7 +201,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.quote,
|
||||
icon: _selectionMenuIcon('quote'),
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('quote', editorState, onSelected),
|
||||
keywords: ['quote', 'refer'],
|
||||
handler: (editorState, _, __) {
|
||||
insertQuoteAfterSelection(editorState);
|
||||
@ -201,10 +210,13 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||
),
|
||||
];
|
||||
|
||||
Widget _selectionMenuIcon(String name) {
|
||||
Widget _selectionMenuIcon(
|
||||
String name, EditorState editorState, bool onSelected) {
|
||||
return FlowySvg(
|
||||
name: 'selection_menu/$name',
|
||||
color: Colors.black,
|
||||
color: onSelected
|
||||
? editorState.editorStyle.selectionMenuItemSelectedIconColor
|
||||
: editorState.editorStyle.selectionMenuItemIconColor,
|
||||
width: 18.0,
|
||||
height: 18.0,
|
||||
);
|
||||
|
@ -29,7 +29,7 @@ class SelectionMenuItem {
|
||||
}
|
||||
|
||||
final String Function() name;
|
||||
final Widget icon;
|
||||
final Widget Function(EditorState editorState, bool onSelected) icon;
|
||||
|
||||
/// Customizes keywords for item.
|
||||
///
|
||||
@ -142,7 +142,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
|
||||
onKey: _onKey,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: widget.editorState.editorStyle.selectionMenuBackgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 5,
|
||||
|
@ -14,6 +14,14 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
final Color? cursorColor;
|
||||
final Color? selectionColor;
|
||||
|
||||
// Selection menu styles
|
||||
final Color? selectionMenuBackgroundColor;
|
||||
final Color? selectionMenuItemTextColor;
|
||||
final Color? selectionMenuItemIconColor;
|
||||
final Color? selectionMenuItemSelectedTextColor;
|
||||
final Color? selectionMenuItemSelectedIconColor;
|
||||
final Color? selectionMenuItemSelectedColor;
|
||||
|
||||
// Text styles
|
||||
final EdgeInsets? textPadding;
|
||||
final TextStyle? textStyle;
|
||||
@ -33,6 +41,12 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
required this.padding,
|
||||
required this.cursorColor,
|
||||
required this.selectionColor,
|
||||
required this.selectionMenuBackgroundColor,
|
||||
required this.selectionMenuItemTextColor,
|
||||
required this.selectionMenuItemIconColor,
|
||||
required this.selectionMenuItemSelectedTextColor,
|
||||
required this.selectionMenuItemSelectedIconColor,
|
||||
required this.selectionMenuItemSelectedColor,
|
||||
required this.textPadding,
|
||||
required this.textStyle,
|
||||
required this.placeholderTextStyle,
|
||||
@ -51,6 +65,12 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
EdgeInsets? padding,
|
||||
Color? cursorColor,
|
||||
Color? selectionColor,
|
||||
Color? selectionMenuBackgroundColor,
|
||||
Color? selectionMenuItemTextColor,
|
||||
Color? selectionMenuItemIconColor,
|
||||
Color? selectionMenuItemSelectedTextColor,
|
||||
Color? selectionMenuItemSelectedIconColor,
|
||||
Color? selectionMenuItemSelectedColor,
|
||||
TextStyle? textStyle,
|
||||
TextStyle? placeholderTextStyle,
|
||||
TextStyle? bold,
|
||||
@ -66,6 +86,18 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
padding: padding ?? this.padding,
|
||||
cursorColor: cursorColor ?? this.cursorColor,
|
||||
selectionColor: selectionColor ?? this.selectionColor,
|
||||
selectionMenuBackgroundColor:
|
||||
selectionMenuBackgroundColor ?? this.selectionMenuBackgroundColor,
|
||||
selectionMenuItemTextColor:
|
||||
selectionMenuItemTextColor ?? this.selectionMenuItemTextColor,
|
||||
selectionMenuItemIconColor:
|
||||
selectionMenuItemIconColor ?? this.selectionMenuItemIconColor,
|
||||
selectionMenuItemSelectedTextColor: selectionMenuItemSelectedTextColor ??
|
||||
selectionMenuItemSelectedTextColor,
|
||||
selectionMenuItemSelectedIconColor: selectionMenuItemSelectedIconColor ??
|
||||
selectionMenuItemSelectedIconColor,
|
||||
selectionMenuItemSelectedColor:
|
||||
selectionMenuItemSelectedColor ?? this.selectionMenuItemSelectedColor,
|
||||
textPadding: textPadding ?? textPadding,
|
||||
textStyle: textStyle ?? this.textStyle,
|
||||
placeholderTextStyle: placeholderTextStyle ?? this.placeholderTextStyle,
|
||||
@ -91,6 +123,22 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
cursorColor: Color.lerp(cursorColor, other.cursorColor, t),
|
||||
textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t),
|
||||
selectionColor: Color.lerp(selectionColor, other.selectionColor, t),
|
||||
selectionMenuBackgroundColor: Color.lerp(
|
||||
selectionMenuBackgroundColor, other.selectionMenuBackgroundColor, t),
|
||||
selectionMenuItemTextColor: Color.lerp(
|
||||
selectionMenuItemTextColor, other.selectionMenuItemTextColor, t),
|
||||
selectionMenuItemIconColor: Color.lerp(
|
||||
selectionMenuItemIconColor, other.selectionMenuItemIconColor, t),
|
||||
selectionMenuItemSelectedTextColor: Color.lerp(
|
||||
selectionMenuItemSelectedTextColor,
|
||||
other.selectionMenuItemSelectedTextColor,
|
||||
t),
|
||||
selectionMenuItemSelectedIconColor: Color.lerp(
|
||||
selectionMenuItemSelectedIconColor,
|
||||
other.selectionMenuItemSelectedIconColor,
|
||||
t),
|
||||
selectionMenuItemSelectedColor: Color.lerp(selectionMenuItemSelectedColor,
|
||||
other.selectionMenuItemSelectedColor, t),
|
||||
textStyle: TextStyle.lerp(textStyle, other.textStyle, t),
|
||||
placeholderTextStyle:
|
||||
TextStyle.lerp(placeholderTextStyle, other.placeholderTextStyle, t),
|
||||
@ -109,6 +157,12 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
padding: const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0),
|
||||
cursorColor: const Color(0xFF00BCF0),
|
||||
selectionColor: const Color.fromARGB(53, 111, 201, 231),
|
||||
selectionMenuBackgroundColor: const Color(0xFFFFFFFF),
|
||||
selectionMenuItemTextColor: const Color(0xFF333333),
|
||||
selectionMenuItemIconColor: const Color(0xFF333333),
|
||||
selectionMenuItemSelectedTextColor: const Color(0xFF333333),
|
||||
selectionMenuItemSelectedIconColor: const Color(0xFF333333),
|
||||
selectionMenuItemSelectedColor: const Color(0xFFE0F8FF),
|
||||
textPadding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
textStyle: const TextStyle(fontSize: 16.0, color: Colors.black),
|
||||
placeholderTextStyle: const TextStyle(fontSize: 16.0, color: Colors.grey),
|
||||
@ -135,5 +189,11 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
|
||||
fontSize: 16.0,
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
),
|
||||
selectionMenuBackgroundColor: const Color(0xFF282E3A),
|
||||
selectionMenuItemTextColor: const Color(0xFFBBC3CD),
|
||||
selectionMenuItemIconColor: const Color(0xFFBBC3CD),
|
||||
selectionMenuItemSelectedTextColor: const Color(0xFF131720),
|
||||
selectionMenuItemSelectedIconColor: const Color(0xFF131720),
|
||||
selectionMenuItemSelectedColor: const Color(0xFF00BCF0),
|
||||
);
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ Future<EditorWidgetTester> _prepare(WidgetTester tester) async {
|
||||
);
|
||||
|
||||
for (final item in defaultSelectionMenuItems) {
|
||||
expect(find.byWidget(item.icon), findsOneWidget);
|
||||
expect(find.text(item.name()), findsOneWidget);
|
||||
}
|
||||
|
||||
return Future.value(editor);
|
||||
|
@ -30,7 +30,7 @@ void main() async {
|
||||
);
|
||||
|
||||
for (final item in defaultSelectionMenuItems) {
|
||||
expect(find.byWidget(item.icon), findsOneWidget);
|
||||
expect(find.text(item.name()), findsOneWidget);
|
||||
}
|
||||
|
||||
await editor.updateSelection(Selection.single(path: [1], startOffset: 0));
|
||||
|
Loading…
Reference in New Issue
Block a user