mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-12-28 07:33:45 +00:00
fix: toolbar launch review issues (#7631)
* fix: keep the turn into menu within six-dot same as toolbar * fix: change some icon color within toolbar * fix: improve toolbar * chore: update editor dependency * fix: update editor dependency
This commit is contained in:
parent
76cb23e233
commit
a26ebbccc1
@ -22,6 +22,7 @@ import 'package:appflowy/workspace/presentation/home/af_focus_manager.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide QuoteBlockKeys;
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -350,6 +351,8 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
|
||||
final isViewDeleted = context.read<DocumentBloc>().state.isDeleted;
|
||||
final isLocked =
|
||||
context.read<ViewLockStatusBloc?>()?.state.isLocked ?? false;
|
||||
|
||||
final themeV2 = AFThemeExtensionV2.of(context);
|
||||
final editor = Directionality(
|
||||
textDirection: textDirection,
|
||||
child: AppFlowyEditor(
|
||||
@ -428,7 +431,6 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: FloatingToolbar(
|
||||
floatingToolbarHeight: 40,
|
||||
@ -436,12 +438,18 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
|
||||
style: FloatingToolbarStyle(
|
||||
backgroundColor: Theme.of(context).cardColor,
|
||||
toolbarActiveColor: Color(0xffe0f8fd),
|
||||
toolbarElevation: 10,
|
||||
),
|
||||
items: toolbarItems,
|
||||
decoration: ShapeDecoration(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).cardColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: Offset(0, 4),
|
||||
blurRadius: 24,
|
||||
color: themeV2.shadow_medium,
|
||||
),
|
||||
],
|
||||
),
|
||||
toolbarBuilder: (context, child, onDismiss, isMetricsChanged) =>
|
||||
DesktopFloatingToolbar(
|
||||
|
||||
@ -2,8 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/toolbar_item/text_suggestions_toolbar_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
hide QuoteBlockKeys, quoteNode;
|
||||
@ -149,214 +148,134 @@ class TurnIntoOptionMenu extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (hasNonSupportedTypes) {
|
||||
return buildItem(
|
||||
pateItem,
|
||||
textSuggestionItem,
|
||||
context.read<BlockActionOptionCubit>().editorState,
|
||||
);
|
||||
}
|
||||
|
||||
return _buildTurnIntoOptions(context, node);
|
||||
}
|
||||
|
||||
Widget _buildTurnIntoOptions(BuildContext context, Node node) {
|
||||
final editorState = context.read<BlockActionOptionCubit>().editorState;
|
||||
SuggestionItem currentSuggestionItem = textSuggestionItem;
|
||||
final List<SuggestionItem> suggestionItems = suggestions.sublist(0, 4);
|
||||
final List<SuggestionItem> turnIntoItems =
|
||||
suggestions.sublist(4, suggestions.length);
|
||||
final textColor = Color(0xff99A1A8);
|
||||
|
||||
void refreshSuggestions() {
|
||||
final selection = editorState.selection;
|
||||
if (selection == null || !selection.isSingle) return;
|
||||
final node = editorState.getNodeAtPath(selection.start.path);
|
||||
if (node == null || node.delta == null) return;
|
||||
final nodeType = node.type;
|
||||
SuggestionType? suggestionType;
|
||||
if (nodeType == HeadingBlockKeys.type) {
|
||||
final level = node.attributes[HeadingBlockKeys.level] ?? 1;
|
||||
if (level == 1) {
|
||||
suggestionType = SuggestionType.h1;
|
||||
} else if (level == 2) {
|
||||
suggestionType = SuggestionType.h2;
|
||||
} else if (level == 3) {
|
||||
suggestionType = SuggestionType.h3;
|
||||
}
|
||||
} else if (nodeType == ToggleListBlockKeys.type) {
|
||||
final level = node.attributes[ToggleListBlockKeys.level];
|
||||
if (level == null) {
|
||||
suggestionType = SuggestionType.toggle;
|
||||
} else if (level == 1) {
|
||||
suggestionType = SuggestionType.toggleH1;
|
||||
} else if (level == 2) {
|
||||
suggestionType = SuggestionType.toggleH2;
|
||||
} else if (level == 3) {
|
||||
suggestionType = SuggestionType.toggleH3;
|
||||
}
|
||||
} else {
|
||||
suggestionType = nodeType2SuggestionType[nodeType];
|
||||
}
|
||||
if (suggestionType == null) return;
|
||||
suggestionItems.clear();
|
||||
turnIntoItems.clear();
|
||||
for (final item in suggestions) {
|
||||
if (item.type.group == suggestionType.group &&
|
||||
item.type != suggestionType) {
|
||||
suggestionItems.add(item);
|
||||
} else {
|
||||
turnIntoItems.add(item);
|
||||
}
|
||||
}
|
||||
currentSuggestionItem =
|
||||
suggestions.where((item) => item.type == suggestionType).first;
|
||||
}
|
||||
|
||||
refreshSuggestions();
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildTurnIntoOptions(context, node),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildSubTitle(
|
||||
LocaleKeys.document_toolbar_suggestions.tr(),
|
||||
textColor,
|
||||
),
|
||||
...List.generate(suggestionItems.length, (index) {
|
||||
return buildItem(
|
||||
suggestionItems[index],
|
||||
currentSuggestionItem,
|
||||
editorState,
|
||||
);
|
||||
}),
|
||||
buildSubTitle(LocaleKeys.document_toolbar_turnInto.tr(), textColor),
|
||||
...List.generate(turnIntoItems.length, (index) {
|
||||
return buildItem(
|
||||
turnIntoItems[index],
|
||||
currentSuggestionItem,
|
||||
editorState,
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildTurnIntoOptions(BuildContext context, Node node) {
|
||||
final children = <Widget>[];
|
||||
|
||||
if (hasNonSupportedTypes) {
|
||||
return children
|
||||
..add(
|
||||
_TurnInfoButton(
|
||||
type: SubPageBlockKeys.type,
|
||||
node: node,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
for (final type in EditorOptionActionType.turnInto.supportTypes) {
|
||||
if (type == ToggleListBlockKeys.type) {
|
||||
// toggle list block and toggle heading block are the same type,
|
||||
// but they have different attributes.
|
||||
|
||||
// toggle list block
|
||||
children.add(
|
||||
_TurnInfoButton(
|
||||
type: type,
|
||||
node: node,
|
||||
),
|
||||
);
|
||||
|
||||
// toggle heading block
|
||||
for (final i in [1, 2, 3]) {
|
||||
children.add(
|
||||
_TurnInfoButton(
|
||||
type: type,
|
||||
node: node,
|
||||
level: i,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if (type != HeadingBlockKeys.type) {
|
||||
children.add(
|
||||
_TurnInfoButton(
|
||||
type: type,
|
||||
node: node,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
for (final i in [1, 2, 3]) {
|
||||
children.add(
|
||||
_TurnInfoButton(
|
||||
type: type,
|
||||
node: node,
|
||||
level: i,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
class _TurnInfoButton extends StatelessWidget {
|
||||
const _TurnInfoButton({
|
||||
required this.type,
|
||||
required this.node,
|
||||
this.level,
|
||||
});
|
||||
|
||||
final String type;
|
||||
final Node node;
|
||||
final int? level;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final name = _buildLocalization(type, level: level);
|
||||
final leftIcon = _buildLeftIcon(type, level: level);
|
||||
final rightIcon = _buildRightIcon(type, node, level: level);
|
||||
|
||||
return HoverButton(
|
||||
name: name,
|
||||
leftIcon: FlowySvg(leftIcon),
|
||||
rightIcon: rightIcon,
|
||||
itemHeight: ActionListSizes.itemHeight,
|
||||
onTap: () => BlockActionOptionCubit.turnIntoBlock(
|
||||
type,
|
||||
node,
|
||||
context.read<BlockActionOptionCubit>().editorState,
|
||||
level: level,
|
||||
currentViewId: getIt<MenuSharedState>().latestOpenView?.id,
|
||||
Widget buildSubTitle(String text, Color color) {
|
||||
return Container(
|
||||
height: 32,
|
||||
margin: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FlowyText.semibold(
|
||||
text,
|
||||
color: color,
|
||||
figmaLineHeight: 16,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _buildRightIcon(String type, Node node, {int? level}) {
|
||||
if (type != node.type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.type == HeadingBlockKeys.type) {
|
||||
final nodeLevel = node.attributes[HeadingBlockKeys.level] ?? 1;
|
||||
if (level != nodeLevel) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type == ToggleListBlockKeys.type) {
|
||||
final nodeLevel = node.attributes[ToggleListBlockKeys.level];
|
||||
if (level != nodeLevel) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return const FlowySvg(
|
||||
FlowySvgs.workspace_selected_s,
|
||||
blendMode: null,
|
||||
Widget buildItem(
|
||||
SuggestionItem item,
|
||||
SuggestionItem currentSuggestionItem,
|
||||
EditorState state,
|
||||
) {
|
||||
final isSelected = item.type == currentSuggestionItem.type;
|
||||
return SizedBox(
|
||||
height: 36,
|
||||
child: FlowyButton(
|
||||
leftIconSize: const Size.square(20),
|
||||
leftIcon: FlowySvg(item.svg),
|
||||
iconPadding: 12,
|
||||
text: FlowyText(
|
||||
item.title,
|
||||
fontWeight: FontWeight.w400,
|
||||
figmaLineHeight: 20,
|
||||
),
|
||||
rightIcon: isSelected ? FlowySvg(FlowySvgs.toolbar_check_m) : null,
|
||||
onTap: () => item.onTap.call(state, false),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
FlowySvgData _buildLeftIcon(String type, {int? level}) {
|
||||
if (type == ParagraphBlockKeys.type) {
|
||||
return FlowySvgs.type_text_m;
|
||||
} else if (type == HeadingBlockKeys.type) {
|
||||
switch (level) {
|
||||
case 1:
|
||||
return FlowySvgs.type_h1_m;
|
||||
case 2:
|
||||
return FlowySvgs.type_h2_m;
|
||||
case 3:
|
||||
return FlowySvgs.type_h3_m;
|
||||
default:
|
||||
return FlowySvgs.type_text_m;
|
||||
}
|
||||
} else if (type == QuoteBlockKeys.type) {
|
||||
return FlowySvgs.type_quote_m;
|
||||
} else if (type == BulletedListBlockKeys.type) {
|
||||
return FlowySvgs.type_bulleted_list_m;
|
||||
} else if (type == NumberedListBlockKeys.type) {
|
||||
return FlowySvgs.type_numbered_list_m;
|
||||
} else if (type == TodoListBlockKeys.type) {
|
||||
return FlowySvgs.type_todo_m;
|
||||
} else if (type == CalloutBlockKeys.type) {
|
||||
return FlowySvgs.type_callout_m;
|
||||
} else if (type == SubPageBlockKeys.type) {
|
||||
return FlowySvgs.icon_document_s;
|
||||
} else if (type == ToggleListBlockKeys.type) {
|
||||
switch (level) {
|
||||
case 1:
|
||||
return FlowySvgs.type_toggle_h1_m;
|
||||
case 2:
|
||||
return FlowySvgs.type_toggle_h2_m;
|
||||
case 3:
|
||||
return FlowySvgs.type_toggle_h3_m;
|
||||
default:
|
||||
return FlowySvgs.type_toggle_list_m;
|
||||
}
|
||||
}
|
||||
|
||||
throw UnimplementedError('Unsupported block type: $type');
|
||||
}
|
||||
|
||||
String _buildLocalization(
|
||||
String type, {
|
||||
int? level,
|
||||
}) {
|
||||
switch (type) {
|
||||
case ParagraphBlockKeys.type:
|
||||
return LocaleKeys.document_slashMenu_name_text.tr();
|
||||
case HeadingBlockKeys.type:
|
||||
switch (level) {
|
||||
case 1:
|
||||
return LocaleKeys.document_slashMenu_name_heading1.tr();
|
||||
case 2:
|
||||
return LocaleKeys.document_slashMenu_name_heading2.tr();
|
||||
case 3:
|
||||
return LocaleKeys.document_slashMenu_name_heading3.tr();
|
||||
default:
|
||||
return LocaleKeys.document_slashMenu_name_text.tr();
|
||||
}
|
||||
case QuoteBlockKeys.type:
|
||||
return LocaleKeys.document_slashMenu_name_quote.tr();
|
||||
case BulletedListBlockKeys.type:
|
||||
return LocaleKeys.editor_bulletedListShortForm.tr();
|
||||
case NumberedListBlockKeys.type:
|
||||
return LocaleKeys.editor_numberedListShortForm.tr();
|
||||
case TodoListBlockKeys.type:
|
||||
return LocaleKeys.editor_checkbox.tr();
|
||||
case CalloutBlockKeys.type:
|
||||
return LocaleKeys.document_slashMenu_name_callout.tr();
|
||||
case SubPageBlockKeys.type:
|
||||
return LocaleKeys.editor_page.tr();
|
||||
case ToggleListBlockKeys.type:
|
||||
switch (level) {
|
||||
case 1:
|
||||
return LocaleKeys.editor_toggleHeading1ShortForm.tr();
|
||||
case 2:
|
||||
return LocaleKeys.editor_toggleHeading2ShortForm.tr();
|
||||
case 3:
|
||||
return LocaleKeys.editor_toggleHeading3ShortForm.tr();
|
||||
default:
|
||||
return LocaleKeys.editor_toggleListShortForm.tr();
|
||||
}
|
||||
}
|
||||
|
||||
throw UnimplementedError('Unsupported block type: $type');
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -118,7 +119,7 @@ class _AiWriterToolbarActionListState extends State<AiWriterToolbarActionList> {
|
||||
}
|
||||
|
||||
Widget buildChild(BuildContext context) {
|
||||
final iconColor = Theme.of(context).iconTheme.color;
|
||||
final themeV2 = AFThemeExtensionV2.of(context);
|
||||
final child = FlowyIconButton(
|
||||
width: 48,
|
||||
height: 32,
|
||||
@ -130,13 +131,13 @@ class _AiWriterToolbarActionListState extends State<AiWriterToolbarActionList> {
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_ai_writer_m,
|
||||
size: Size.square(20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_primary,
|
||||
),
|
||||
HSpace(4),
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_arrow_down_m,
|
||||
size: Size(12, 20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_tertiary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -187,7 +188,7 @@ class ImproveWritingButton extends StatelessWidget {
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.toolbar_ai_improve_writing_m,
|
||||
size: Size.square(20.0),
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
color: AFThemeExtensionV2.of(context).icon_primary,
|
||||
),
|
||||
onPressed: () {
|
||||
if (_isAIEnabled(editorState)) {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart';
|
||||
@ -35,9 +37,7 @@ bool onlyShowInSingleTextTypeSelectionAndExcludeTable(
|
||||
notShowInTable(editorState);
|
||||
}
|
||||
|
||||
bool enableSuggestions(
|
||||
EditorState editorState,
|
||||
) {
|
||||
bool enableSuggestions(EditorState editorState) {
|
||||
final selection = editorState.selection;
|
||||
if (selection == null || !selection.isSingle) {
|
||||
return false;
|
||||
@ -46,10 +46,18 @@ bool enableSuggestions(
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (isNarrowWindow(editorState)) return false;
|
||||
|
||||
return (node.delta != null && suggestionsItemTypes.contains(node.type)) &&
|
||||
notShowInTable(editorState);
|
||||
}
|
||||
|
||||
bool isNarrowWindow(EditorState editorState) {
|
||||
final editorSize = editorState.renderBox?.size ?? Size.zero;
|
||||
if (editorSize.width < 650) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
final Set<String> suggestionsItemTypes = {
|
||||
...toolbarItemWhiteList,
|
||||
ToggleListBlockKeys.type,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/toolbar_extension.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -61,10 +62,11 @@ class _DesktopFloatingToolbarState extends State<DesktopFloatingToolbar> {
|
||||
) {
|
||||
const toolbarHeight = 40, topLimit = toolbarHeight + 8;
|
||||
final bool isLongMenu = onlyShowInSingleSelectionAndTextType(editorState);
|
||||
final menuWidth = isLongMenu ? 650.0 : 420.0;
|
||||
final editorOffset =
|
||||
editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero;
|
||||
final editorSize = editorState.renderBox?.size ?? Size.zero;
|
||||
final menuWidth =
|
||||
isLongMenu ? (isNarrowWindow(editorState) ? 490.0 : 660.0) : 420.0;
|
||||
final editorRect = editorOffset & editorSize;
|
||||
final left = rect.left, leftStart = 50;
|
||||
final top =
|
||||
|
||||
@ -148,6 +148,115 @@ class _LinkCreateMenuState extends State<LinkCreateMenu> {
|
||||
}
|
||||
}
|
||||
|
||||
void showLinkCreateMenu(
|
||||
BuildContext context,
|
||||
EditorState editorState,
|
||||
Selection selection,
|
||||
) {
|
||||
final (left, top, right, bottom, alignment) = _getPosition(editorState);
|
||||
|
||||
final node = editorState.getNodeAtPath(selection.end.path);
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayEntry? overlay;
|
||||
|
||||
void dismissOverlay() {
|
||||
keepEditorFocusNotifier.decrease();
|
||||
overlay?.remove();
|
||||
overlay = null;
|
||||
}
|
||||
|
||||
keepEditorFocusNotifier.increase();
|
||||
overlay = FullScreenOverlayEntry(
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
left: left,
|
||||
right: right,
|
||||
dismissCallback: () => keepEditorFocusNotifier.decrease(),
|
||||
builder: (context) {
|
||||
return LinkCreateMenu(
|
||||
alignment: alignment,
|
||||
editorState: editorState,
|
||||
onSubmitted: (link, isPage) async {
|
||||
await editorState.formatDelta(selection, {
|
||||
BuiltInAttributeKey.href: link,
|
||||
kIsPageLink: isPage,
|
||||
});
|
||||
dismissOverlay();
|
||||
},
|
||||
onDismiss: dismissOverlay,
|
||||
);
|
||||
},
|
||||
).build();
|
||||
|
||||
Overlay.of(context, rootOverlay: true).insert(overlay!);
|
||||
}
|
||||
|
||||
// get a proper position for link menu
|
||||
(
|
||||
double? left,
|
||||
double? top,
|
||||
double? right,
|
||||
double? bottom,
|
||||
LinkMenuAlignment alignment,
|
||||
) _getPosition(
|
||||
EditorState editorState,
|
||||
) {
|
||||
final rect = editorState.selectionRects().first;
|
||||
const menuHeight = 222.0, menuWidth = 320.0;
|
||||
|
||||
double? left, right, top, bottom;
|
||||
LinkMenuAlignment alignment = LinkMenuAlignment.topLeft;
|
||||
final editorOffset = editorState.renderBox!.localToGlobal(Offset.zero),
|
||||
editorSize = editorState.renderBox!.size;
|
||||
final editorBottom = editorSize.height + editorOffset.dy,
|
||||
editorRight = editorSize.width + editorOffset.dx;
|
||||
final overflowBottom = rect.bottom + menuHeight > editorBottom,
|
||||
overflowTop = rect.top - menuHeight < 0,
|
||||
overflowLeft = rect.left - menuWidth < 0,
|
||||
overflowRight = rect.right + menuWidth > editorRight;
|
||||
|
||||
if (overflowTop && !overflowBottom) {
|
||||
/// show at bottom
|
||||
top = rect.bottom;
|
||||
} else if (overflowBottom && !overflowTop) {
|
||||
/// show at top
|
||||
bottom = editorBottom - rect.top;
|
||||
} else if (!overflowTop && !overflowBottom) {
|
||||
/// show at bottom
|
||||
top = rect.bottom;
|
||||
} else {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
if (overflowLeft && !overflowRight) {
|
||||
/// show at right
|
||||
left = rect.left;
|
||||
} else if (overflowRight && !overflowLeft) {
|
||||
/// show at left
|
||||
right = editorRight - rect.right;
|
||||
} else if (!overflowLeft && !overflowRight) {
|
||||
/// show at right
|
||||
left = rect.left;
|
||||
} else {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
if (left != null && top != null) {
|
||||
alignment = LinkMenuAlignment.bottomRight;
|
||||
} else if (left != null && bottom != null) {
|
||||
alignment = LinkMenuAlignment.topRight;
|
||||
} else if (right != null && top != null) {
|
||||
alignment = LinkMenuAlignment.bottomLeft;
|
||||
} else if (right != null && bottom != null) {
|
||||
alignment = LinkMenuAlignment.topLeft;
|
||||
}
|
||||
|
||||
return (left, top, right, bottom, alignment);
|
||||
}
|
||||
|
||||
ShapeDecoration buildToolbarLinkDecoration(BuildContext context) =>
|
||||
ShapeDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
|
||||
@ -57,10 +57,19 @@ class _LinkHoverTriggerState extends State<LinkHoverTrigger> {
|
||||
|
||||
Attributes get attribute => widget.attribute;
|
||||
|
||||
HoverTriggerKey get triggerKey => HoverTriggerKey(widget.node.id, selection);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getIt<LinkHoverTriggers>()._add(triggerKey, showLinkHoverMenu);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
hoverMenuController.close();
|
||||
editMenuController.close();
|
||||
getIt<LinkHoverTriggers>()._remove(triggerKey, showLinkHoverMenu);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -217,6 +226,31 @@ class _LinkHoverTriggerState extends State<LinkHoverTrigger> {
|
||||
.setData(ClipboardServiceData(plainText: href));
|
||||
hoverMenuController.close();
|
||||
}
|
||||
|
||||
void removeLink(
|
||||
EditorState editorState,
|
||||
Selection selection,
|
||||
bool isHref,
|
||||
) {
|
||||
if (!isHref) return;
|
||||
final node = editorState.getNodeAtPath(selection.end.path);
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
final index = selection.normalized.startIndex;
|
||||
final length = selection.length;
|
||||
final transaction = editorState.transaction
|
||||
..formatText(
|
||||
node,
|
||||
index,
|
||||
length,
|
||||
{
|
||||
BuiltInAttributeKey.href: null,
|
||||
kIsPageLink: null,
|
||||
},
|
||||
);
|
||||
editorState.apply(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
class LinkHoverMenu extends StatelessWidget {
|
||||
@ -320,3 +354,44 @@ class LinkHoverMenu extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HoverTriggerKey {
|
||||
HoverTriggerKey(this.nodeId, this.selection);
|
||||
|
||||
final String nodeId;
|
||||
final Selection selection;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is HoverTriggerKey &&
|
||||
runtimeType == other.runtimeType &&
|
||||
nodeId == other.nodeId &&
|
||||
selection == other.selection;
|
||||
|
||||
@override
|
||||
int get hashCode => nodeId.hashCode ^ selection.hashCode;
|
||||
}
|
||||
|
||||
class LinkHoverTriggers {
|
||||
final Map<HoverTriggerKey, Set<VoidCallback>> _map = {};
|
||||
|
||||
void _add(HoverTriggerKey key, VoidCallback callback) {
|
||||
final callbacks = _map[key] ?? {};
|
||||
callbacks.add(callback);
|
||||
_map[key] = callbacks;
|
||||
}
|
||||
|
||||
void _remove(HoverTriggerKey key, VoidCallback callback) {
|
||||
final callbacks = _map[key] ?? {};
|
||||
callbacks.remove(callback);
|
||||
_map[key] = callbacks;
|
||||
}
|
||||
|
||||
void call(HoverTriggerKey key) {
|
||||
final callbacks = _map[key] ?? {};
|
||||
for (final callback in callbacks) {
|
||||
callback.call();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:appflowy_editor/src/editor/toolbar/desktop/items/utils/tooltip_util.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -79,7 +80,7 @@ class _FormatToolbarItem extends ToolbarItem {
|
||||
size: Size.square(20.0),
|
||||
color: (isDark && isHighlight)
|
||||
? Color(0xFF282E3A)
|
||||
: Theme.of(context).iconTheme.color,
|
||||
: AFThemeExtensionV2.of(context).icon_primary,
|
||||
),
|
||||
onPressed: () => editorState.toggleAttribute(
|
||||
name,
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/toolbar_extension.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_create_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_hover_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/toolbar_cubit.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -14,7 +17,8 @@ const kIsPageLink = 'is_page_link';
|
||||
final customLinkItem = ToolbarItem(
|
||||
id: ToolbarId.link.id,
|
||||
group: 4,
|
||||
isActive: onlyShowInSingleSelectionAndTextType,
|
||||
isActive: (state) =>
|
||||
!isNarrowWindow(state) && onlyShowInSingleSelectionAndTextType(state),
|
||||
builder: (context, editorState, highlightColor, iconColor, tooltipBuilder) {
|
||||
final selection = editorState.selection!;
|
||||
final nodes = editorState.getNodesInSelection(selection);
|
||||
@ -42,9 +46,11 @@ final customLinkItem = ToolbarItem(
|
||||
onPressed: () {
|
||||
toolbarCubit?.dismiss();
|
||||
if (isHref) {
|
||||
removeLink(editorState, selection, isHref);
|
||||
getIt<LinkHoverTriggers>().call(
|
||||
HoverTriggerKey(nodes.first.id, selection),
|
||||
);
|
||||
} else {
|
||||
_showLinkMenu(context, editorState, selection, isHref);
|
||||
showLinkCreateMenu(context, editorState, selection);
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -62,78 +68,6 @@ final customLinkItem = ToolbarItem(
|
||||
},
|
||||
);
|
||||
|
||||
void removeLink(
|
||||
EditorState editorState,
|
||||
Selection selection,
|
||||
bool isHref,
|
||||
) {
|
||||
if (!isHref) return;
|
||||
final node = editorState.getNodeAtPath(selection.end.path);
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
final index = selection.normalized.startIndex;
|
||||
final length = selection.length;
|
||||
final transaction = editorState.transaction
|
||||
..formatText(
|
||||
node,
|
||||
index,
|
||||
length,
|
||||
{
|
||||
BuiltInAttributeKey.href: null,
|
||||
kIsPageLink: null,
|
||||
},
|
||||
);
|
||||
editorState.apply(transaction);
|
||||
}
|
||||
|
||||
void _showLinkMenu(
|
||||
BuildContext context,
|
||||
EditorState editorState,
|
||||
Selection selection,
|
||||
bool isHref,
|
||||
) {
|
||||
final (left, top, right, bottom, alignment) = _getPosition(editorState);
|
||||
|
||||
final node = editorState.getNodeAtPath(selection.end.path);
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayEntry? overlay;
|
||||
|
||||
void dismissOverlay() {
|
||||
keepEditorFocusNotifier.decrease();
|
||||
overlay?.remove();
|
||||
overlay = null;
|
||||
}
|
||||
|
||||
keepEditorFocusNotifier.increase();
|
||||
overlay = FullScreenOverlayEntry(
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
left: left,
|
||||
right: right,
|
||||
dismissCallback: () => keepEditorFocusNotifier.decrease(),
|
||||
builder: (context) {
|
||||
return LinkCreateMenu(
|
||||
alignment: alignment,
|
||||
editorState: editorState,
|
||||
onSubmitted: (link, isPage) async {
|
||||
await editorState.formatDelta(selection, {
|
||||
BuiltInAttributeKey.href: link,
|
||||
kIsPageLink: isPage,
|
||||
});
|
||||
dismissOverlay();
|
||||
},
|
||||
onDismiss: dismissOverlay,
|
||||
);
|
||||
},
|
||||
).build();
|
||||
|
||||
Overlay.of(context, rootOverlay: true).insert(overlay!);
|
||||
}
|
||||
|
||||
extension AttributeExtension on Attributes {
|
||||
bool get isPage {
|
||||
if (this[kIsPageLink] is bool) {
|
||||
@ -143,69 +77,6 @@ extension AttributeExtension on Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// get a proper position for link menu
|
||||
(
|
||||
double? left,
|
||||
double? top,
|
||||
double? right,
|
||||
double? bottom,
|
||||
LinkMenuAlignment alignment,
|
||||
) _getPosition(
|
||||
EditorState editorState,
|
||||
) {
|
||||
final rect = editorState.selectionRects().first;
|
||||
const menuHeight = 222.0, menuWidth = 320.0;
|
||||
|
||||
double? left, right, top, bottom;
|
||||
LinkMenuAlignment alignment = LinkMenuAlignment.topLeft;
|
||||
final editorOffset = editorState.renderBox!.localToGlobal(Offset.zero),
|
||||
editorSize = editorState.renderBox!.size;
|
||||
final editorBottom = editorSize.height + editorOffset.dy,
|
||||
editorRight = editorSize.width + editorOffset.dx;
|
||||
final overflowBottom = rect.bottom + menuHeight > editorBottom,
|
||||
overflowTop = rect.top - menuHeight < 0,
|
||||
overflowLeft = rect.left - menuWidth < 0,
|
||||
overflowRight = rect.right + menuWidth > editorRight;
|
||||
|
||||
if (overflowTop && !overflowBottom) {
|
||||
/// show at bottom
|
||||
top = rect.bottom;
|
||||
} else if (overflowBottom && !overflowTop) {
|
||||
/// show at top
|
||||
bottom = editorBottom - rect.top;
|
||||
} else if (!overflowTop && !overflowBottom) {
|
||||
/// show at bottom
|
||||
top = rect.bottom;
|
||||
} else {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
if (overflowLeft && !overflowRight) {
|
||||
/// show at right
|
||||
left = rect.left;
|
||||
} else if (overflowRight && !overflowLeft) {
|
||||
/// show at left
|
||||
right = editorRight - rect.right;
|
||||
} else if (!overflowLeft && !overflowRight) {
|
||||
/// show at right
|
||||
left = rect.left;
|
||||
} else {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
if (left != null && top != null) {
|
||||
alignment = LinkMenuAlignment.bottomRight;
|
||||
} else if (left != null && bottom != null) {
|
||||
alignment = LinkMenuAlignment.topRight;
|
||||
} else if (right != null && top != null) {
|
||||
alignment = LinkMenuAlignment.bottomLeft;
|
||||
} else if (right != null && bottom != null) {
|
||||
alignment = LinkMenuAlignment.topLeft;
|
||||
}
|
||||
|
||||
return (left, top, right, bottom, alignment);
|
||||
}
|
||||
|
||||
enum LinkMenuAlignment {
|
||||
topLeft,
|
||||
topRight,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/ai/ai_writer_toolbar_item.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/toolbar_extension.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -43,5 +44,6 @@ ToolbarItem group1PaddingItem =
|
||||
|
||||
ToolbarItem group4PaddingItem = buildPaddingPlaceholderItem(
|
||||
4,
|
||||
isActive: onlyShowInSingleSelectionAndTextType,
|
||||
isActive: (state) =>
|
||||
!isNarrowWindow(state) && onlyShowInSingleSelectionAndTextType(state),
|
||||
);
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/toolbar_extension.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -11,7 +13,8 @@ import 'toolbar_id_enum.dart';
|
||||
final ToolbarItem customTextAlignItem = ToolbarItem(
|
||||
id: ToolbarId.textAlign.id,
|
||||
group: 4,
|
||||
isActive: onlyShowInSingleSelectionAndTextType,
|
||||
isActive: (state) =>
|
||||
!isNarrowWindow(state) && onlyShowInSingleSelectionAndTextType(state),
|
||||
builder: (
|
||||
context,
|
||||
editorState,
|
||||
@ -33,18 +36,29 @@ class TextAlignActionList extends StatefulWidget {
|
||||
required this.editorState,
|
||||
required this.highlightColor,
|
||||
this.tooltipBuilder,
|
||||
this.child,
|
||||
this.onSelect,
|
||||
this.popoverController,
|
||||
this.popoverDirection = PopoverDirection.bottomWithLeftAligned,
|
||||
this.showOffset = const Offset(0, 2),
|
||||
});
|
||||
|
||||
final EditorState editorState;
|
||||
final ToolbarTooltipBuilder? tooltipBuilder;
|
||||
final Color highlightColor;
|
||||
final Widget? child;
|
||||
final VoidCallback? onSelect;
|
||||
final PopoverController? popoverController;
|
||||
final PopoverDirection popoverDirection;
|
||||
final Offset showOffset;
|
||||
|
||||
@override
|
||||
State<TextAlignActionList> createState() => _TextAlignActionListState();
|
||||
}
|
||||
|
||||
class _TextAlignActionListState extends State<TextAlignActionList> {
|
||||
final popoverController = PopoverController();
|
||||
late PopoverController popoverController =
|
||||
widget.popoverController ?? PopoverController();
|
||||
|
||||
bool isSelected = false;
|
||||
|
||||
@ -62,8 +76,8 @@ class _TextAlignActionListState extends State<TextAlignActionList> {
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
controller: popoverController,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
offset: const Offset(0, 2.0),
|
||||
direction: widget.popoverDirection,
|
||||
offset: widget.showOffset,
|
||||
onOpen: () => keepEditorFocusNotifier.increase(),
|
||||
onClose: () {
|
||||
setState(() {
|
||||
@ -72,7 +86,7 @@ class _TextAlignActionListState extends State<TextAlignActionList> {
|
||||
keepEditorFocusNotifier.decrease();
|
||||
},
|
||||
popupBuilder: (context) => buildPopoverContent(),
|
||||
child: buildChild(context),
|
||||
child: widget.child ?? buildChild(context),
|
||||
);
|
||||
}
|
||||
|
||||
@ -82,7 +96,7 @@ class _TextAlignActionListState extends State<TextAlignActionList> {
|
||||
}
|
||||
|
||||
Widget buildChild(BuildContext context) {
|
||||
final iconColor = Theme.of(context).iconTheme.color;
|
||||
final themeV2 = AFThemeExtensionV2.of(context);
|
||||
final child = FlowyIconButton(
|
||||
width: 48,
|
||||
height: 32,
|
||||
@ -94,13 +108,13 @@ class _TextAlignActionListState extends State<TextAlignActionList> {
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_alignment_m,
|
||||
size: Size.square(20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_primary,
|
||||
),
|
||||
HSpace(4),
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_arrow_down_m,
|
||||
size: Size(12, 20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_tertiary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -149,6 +163,7 @@ class _TextAlignActionListState extends State<TextAlignActionList> {
|
||||
isHighlight ? FlowySvg(FlowySvgs.toolbar_check_m) : null,
|
||||
onTap: () {
|
||||
command.onAlignChanged(editorState);
|
||||
widget.onSelect?.call();
|
||||
popoverController.close();
|
||||
},
|
||||
),
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_create_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_hover_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/toolbar_cubit.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
// ignore: implementation_imports
|
||||
@ -10,6 +14,10 @@ import 'package:appflowy_editor/src/editor/toolbar/desktop/items/utils/tooltip_u
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'custom_text_align_toolbar_item.dart';
|
||||
import 'text_suggestions_toolbar_item.dart';
|
||||
|
||||
const _kMoreOptionItemId = 'editor.more_option';
|
||||
const kFontToolbarItemId = 'editor.font';
|
||||
@ -54,7 +62,9 @@ class MoreOptionActionList extends StatefulWidget {
|
||||
|
||||
class _MoreOptionActionListState extends State<MoreOptionActionList> {
|
||||
final popoverController = PopoverController();
|
||||
final fontPopoverController = PopoverController();
|
||||
PopoverController fontPopoverController = PopoverController();
|
||||
PopoverController suggestionsPopoverController = PopoverController();
|
||||
PopoverController textAlignPopoverController = PopoverController();
|
||||
|
||||
bool isSelected = false;
|
||||
|
||||
@ -62,11 +72,15 @@ class _MoreOptionActionListState extends State<MoreOptionActionList> {
|
||||
|
||||
Color get highlightColor => widget.highlightColor;
|
||||
|
||||
MoreOptionCommand? tappedCommand;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
popoverController.close();
|
||||
fontPopoverController.close();
|
||||
suggestionsPopoverController.close();
|
||||
textAlignPopoverController.close();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -154,11 +168,17 @@ class _MoreOptionActionListState extends State<MoreOptionActionList> {
|
||||
Widget buildPopoverContent() {
|
||||
final showFormula = onlyShowInSingleSelectionAndTextType(editorState);
|
||||
const fontColor = Color(0xff99A1A8);
|
||||
final isNarrow = isNarrowWindow(editorState);
|
||||
return MouseRegion(
|
||||
child: SeparatedColumn(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
separatorBuilder: () => const VSpace(4.0),
|
||||
children: [
|
||||
if (isNarrow) ...[
|
||||
buildTurnIntoSelector(),
|
||||
buildCommandItem(MoreOptionCommand.link),
|
||||
buildTextAlignSelector(),
|
||||
],
|
||||
buildFontSelector(),
|
||||
buildCommandItem(
|
||||
MoreOptionCommand.strikethrough,
|
||||
@ -197,6 +217,7 @@ class _MoreOptionActionListState extends State<MoreOptionActionList> {
|
||||
Widget buildCommandItem(
|
||||
MoreOptionCommand command, {
|
||||
Widget? rightIcon,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
final isFontCommand = command == MoreOptionCommand.font;
|
||||
return SizedBox(
|
||||
@ -212,12 +233,14 @@ class _MoreOptionActionListState extends State<MoreOptionActionList> {
|
||||
figmaLineHeight: 20,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
onTap: () {
|
||||
command.onExecute(editorState);
|
||||
if (command != MoreOptionCommand.font) {
|
||||
popoverController.close();
|
||||
}
|
||||
},
|
||||
onTap: onTap ??
|
||||
() {
|
||||
command.onExecute(editorState, context);
|
||||
hideOtherPopovers(command);
|
||||
if (command != MoreOptionCommand.font) {
|
||||
popoverController.close();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -255,9 +278,73 @@ class _MoreOptionActionListState extends State<MoreOptionActionList> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTurnIntoSelector() {
|
||||
final selectionRects = editorState.selectionRects();
|
||||
double height = -6;
|
||||
if (selectionRects.isNotEmpty) height = selectionRects.first.height;
|
||||
return SuggestionsActionList(
|
||||
editorState: editorState,
|
||||
popoverController: suggestionsPopoverController,
|
||||
popoverDirection: PopoverDirection.leftWithTopAligned,
|
||||
showOffset: Offset(-8, height),
|
||||
onSelect: () => context.read<ToolbarCubit?>()?.dismiss(),
|
||||
child: buildCommandItem(
|
||||
MoreOptionCommand.suggestions,
|
||||
rightIcon: FlowySvg(FlowySvgs.toolbar_arrow_right_m),
|
||||
onTap: () {
|
||||
if (tappedCommand == MoreOptionCommand.suggestions) return;
|
||||
hideOtherPopovers(MoreOptionCommand.suggestions);
|
||||
keepEditorFocusNotifier.increase();
|
||||
suggestionsPopoverController.show();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTextAlignSelector() {
|
||||
return TextAlignActionList(
|
||||
editorState: editorState,
|
||||
popoverController: textAlignPopoverController,
|
||||
popoverDirection: PopoverDirection.leftWithTopAligned,
|
||||
showOffset: Offset(-8, 0),
|
||||
onSelect: () => context.read<ToolbarCubit?>()?.dismiss(),
|
||||
highlightColor: highlightColor,
|
||||
child: buildCommandItem(
|
||||
MoreOptionCommand.textAlign,
|
||||
rightIcon: FlowySvg(FlowySvgs.toolbar_arrow_right_m),
|
||||
onTap: () {
|
||||
if (tappedCommand == MoreOptionCommand.textAlign) return;
|
||||
hideOtherPopovers(MoreOptionCommand.textAlign);
|
||||
keepEditorFocusNotifier.increase();
|
||||
textAlignPopoverController.show();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void hideOtherPopovers(MoreOptionCommand currentCommand) {
|
||||
if (tappedCommand == currentCommand) return;
|
||||
if (tappedCommand == MoreOptionCommand.font) {
|
||||
fontPopoverController.close();
|
||||
fontPopoverController = PopoverController();
|
||||
} else if (tappedCommand == MoreOptionCommand.suggestions) {
|
||||
suggestionsPopoverController.close();
|
||||
suggestionsPopoverController = PopoverController();
|
||||
} else if (tappedCommand == MoreOptionCommand.textAlign) {
|
||||
textAlignPopoverController.close();
|
||||
textAlignPopoverController = PopoverController();
|
||||
}
|
||||
tappedCommand = currentCommand;
|
||||
}
|
||||
}
|
||||
|
||||
enum MoreOptionCommand {
|
||||
suggestions(FlowySvgs.turninto_s),
|
||||
link(FlowySvgs.toolbar_link_m),
|
||||
textAlign(
|
||||
FlowySvgs.toolbar_alignment_m,
|
||||
),
|
||||
font(FlowySvgs.type_font_m),
|
||||
strikethrough(FlowySvgs.type_strikethrough_m),
|
||||
formula(FlowySvgs.type_formula_m);
|
||||
@ -268,6 +355,12 @@ enum MoreOptionCommand {
|
||||
|
||||
String get title {
|
||||
switch (this) {
|
||||
case suggestions:
|
||||
return LocaleKeys.document_toolbar_turnInto.tr();
|
||||
case link:
|
||||
return LocaleKeys.document_toolbar_link.tr();
|
||||
case textAlign:
|
||||
return LocaleKeys.button_align.tr();
|
||||
case font:
|
||||
return LocaleKeys.document_toolbar_font.tr();
|
||||
case strikethrough:
|
||||
@ -277,14 +370,26 @@ enum MoreOptionCommand {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onExecute(EditorState editorState) async {
|
||||
if (this == strikethrough) {
|
||||
Future<void> onExecute(EditorState editorState, BuildContext context) async {
|
||||
final selection = editorState.selection!;
|
||||
if (this == link) {
|
||||
final nodes = editorState.getNodesInSelection(selection);
|
||||
final isHref = nodes.allSatisfyInSelection(selection, (delta) {
|
||||
return delta.everyAttributes(
|
||||
(attributes) => attributes[AppFlowyRichTextKeys.href] != null,
|
||||
);
|
||||
});
|
||||
context.read<ToolbarCubit?>()?.dismiss();
|
||||
if (isHref) {
|
||||
getIt<LinkHoverTriggers>().call(
|
||||
HoverTriggerKey(nodes.first.id, selection),
|
||||
);
|
||||
} else {
|
||||
showLinkCreateMenu(context, editorState, selection);
|
||||
}
|
||||
} else if (this == strikethrough) {
|
||||
await editorState.toggleAttribute(name);
|
||||
} else if (this == formula) {
|
||||
final selection = editorState.selection;
|
||||
if (selection == null || selection.isCollapsed) {
|
||||
return;
|
||||
}
|
||||
final node = editorState.getNodeAtPath(selection.start.path);
|
||||
final delta = node?.delta;
|
||||
if (node == null || delta == null) {
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.da
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -77,7 +78,7 @@ class _TextHeadingActionListState extends State<TextHeadingActionList> {
|
||||
}
|
||||
|
||||
Widget buildChild(BuildContext context) {
|
||||
final iconColor = Theme.of(context).iconTheme.color;
|
||||
final themeV2 = AFThemeExtensionV2.of(context);
|
||||
final child = FlowyIconButton(
|
||||
width: 48,
|
||||
height: 32,
|
||||
@ -89,13 +90,13 @@ class _TextHeadingActionListState extends State<TextHeadingActionList> {
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_text_format_m,
|
||||
size: Size.square(20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_primary,
|
||||
),
|
||||
HSpace(4),
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_arrow_down_m,
|
||||
size: Size(12, 20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_tertiary,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
hide QuoteBlockComponentBuilder, quoteNode, QuoteBlockKeys;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -45,17 +47,28 @@ class SuggestionsActionList extends StatefulWidget {
|
||||
super.key,
|
||||
required this.editorState,
|
||||
this.tooltipBuilder,
|
||||
this.child,
|
||||
this.onSelect,
|
||||
this.popoverController,
|
||||
this.popoverDirection = PopoverDirection.bottomWithLeftAligned,
|
||||
this.showOffset = const Offset(0, 2),
|
||||
});
|
||||
|
||||
final EditorState editorState;
|
||||
final ToolbarTooltipBuilder? tooltipBuilder;
|
||||
final Widget? child;
|
||||
final VoidCallback? onSelect;
|
||||
final PopoverController? popoverController;
|
||||
final PopoverDirection popoverDirection;
|
||||
final Offset showOffset;
|
||||
|
||||
@override
|
||||
State<SuggestionsActionList> createState() => _SuggestionsActionListState();
|
||||
}
|
||||
|
||||
class _SuggestionsActionListState extends State<SuggestionsActionList> {
|
||||
final popoverController = PopoverController();
|
||||
late PopoverController popoverController =
|
||||
widget.popoverController ?? PopoverController();
|
||||
|
||||
bool isSelected = false;
|
||||
|
||||
@ -83,8 +96,8 @@ class _SuggestionsActionListState extends State<SuggestionsActionList> {
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
controller: popoverController,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
offset: const Offset(0, 2.0),
|
||||
direction: widget.popoverDirection,
|
||||
offset: widget.showOffset,
|
||||
onOpen: () => keepEditorFocusNotifier.increase(),
|
||||
onClose: () {
|
||||
setState(() {
|
||||
@ -94,7 +107,7 @@ class _SuggestionsActionListState extends State<SuggestionsActionList> {
|
||||
},
|
||||
constraints: const BoxConstraints(maxWidth: 240, maxHeight: 400),
|
||||
popupBuilder: (context) => buildPopoverContent(context),
|
||||
child: buildChild(context),
|
||||
child: widget.child ?? buildChild(context),
|
||||
);
|
||||
}
|
||||
|
||||
@ -104,7 +117,8 @@ class _SuggestionsActionListState extends State<SuggestionsActionList> {
|
||||
}
|
||||
|
||||
Widget buildChild(BuildContext context) {
|
||||
final iconColor = Theme.of(context).iconTheme.color;
|
||||
final themeV2 = AFThemeExtensionV2.of(context);
|
||||
|
||||
final child = FlowyHover(
|
||||
isSelected: () => isSelected,
|
||||
style: HoverStyle(
|
||||
@ -147,7 +161,7 @@ class _SuggestionsActionListState extends State<SuggestionsActionList> {
|
||||
FlowySvg(
|
||||
FlowySvgs.toolbar_arrow_down_m,
|
||||
size: Size(12, 20),
|
||||
color: iconColor,
|
||||
color: themeV2.icon_tertiary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -206,7 +220,8 @@ class _SuggestionsActionListState extends State<SuggestionsActionList> {
|
||||
),
|
||||
rightIcon: isSelected ? FlowySvg(FlowySvgs.toolbar_check_m) : null,
|
||||
onTap: () {
|
||||
item.onTap(widget.editorState);
|
||||
item.onTap(widget.editorState, true);
|
||||
widget.onSelect?.call();
|
||||
popoverController.close();
|
||||
},
|
||||
),
|
||||
@ -289,10 +304,10 @@ class SuggestionItem {
|
||||
final SuggestionType type;
|
||||
final String title;
|
||||
final FlowySvgData svg;
|
||||
final ValueChanged<EditorState> onTap;
|
||||
final Function(EditorState state, bool keepSelection) onTap;
|
||||
}
|
||||
|
||||
enum SuggestionGroup { textHeading, list, toggle, quote }
|
||||
enum SuggestionGroup { textHeading, list, toggle, quote, page }
|
||||
|
||||
enum SuggestionType {
|
||||
text(SuggestionGroup.textHeading),
|
||||
@ -307,7 +322,8 @@ enum SuggestionType {
|
||||
toggleH2(SuggestionGroup.toggle),
|
||||
toggleH3(SuggestionGroup.toggle),
|
||||
callOut(SuggestionGroup.quote),
|
||||
quote(SuggestionGroup.quote);
|
||||
quote(SuggestionGroup.quote),
|
||||
page(SuggestionGroup.page);
|
||||
|
||||
const SuggestionType(this.group);
|
||||
|
||||
@ -318,94 +334,166 @@ final textSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.text,
|
||||
title: AppFlowyEditorL10n.current.text,
|
||||
svg: FlowySvgs.type_text_m,
|
||||
onTap: (state) => formatNodeToText(state),
|
||||
onTap: (state, _) => formatNodeToText(state),
|
||||
);
|
||||
|
||||
final h1SuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.h1,
|
||||
title: LocaleKeys.document_toolbar_h1.tr(),
|
||||
svg: FlowySvgs.type_h1_m,
|
||||
onTap: (state) => _turnInto(state, HeadingBlockKeys.type, level: 1),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
HeadingBlockKeys.type,
|
||||
level: 1,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final h2SuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.h2,
|
||||
title: LocaleKeys.document_toolbar_h2.tr(),
|
||||
svg: FlowySvgs.type_h2_m,
|
||||
onTap: (state) => _turnInto(state, HeadingBlockKeys.type, level: 2),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
HeadingBlockKeys.type,
|
||||
level: 2,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final h3SuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.h3,
|
||||
title: LocaleKeys.document_toolbar_h3.tr(),
|
||||
svg: FlowySvgs.type_h3_m,
|
||||
onTap: (state) => _turnInto(state, HeadingBlockKeys.type, level: 3),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
HeadingBlockKeys.type,
|
||||
level: 3,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final checkboxSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.checkbox,
|
||||
title: LocaleKeys.editor_checkbox.tr(),
|
||||
svg: FlowySvgs.type_todo_m,
|
||||
onTap: (state) => _turnInto(state, TodoListBlockKeys.type),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
TodoListBlockKeys.type,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final bulletedSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.bulleted,
|
||||
title: LocaleKeys.editor_bulletedListShortForm.tr(),
|
||||
svg: FlowySvgs.type_bulleted_list_m,
|
||||
onTap: (state) => _turnInto(state, BulletedListBlockKeys.type),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
BulletedListBlockKeys.type,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final numberedSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.numbered,
|
||||
title: LocaleKeys.editor_numberedListShortForm.tr(),
|
||||
svg: FlowySvgs.type_numbered_list_m,
|
||||
onTap: (state) => _turnInto(state, NumberedListBlockKeys.type),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
NumberedListBlockKeys.type,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final toggleSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.toggle,
|
||||
title: LocaleKeys.editor_toggleListShortForm.tr(),
|
||||
svg: FlowySvgs.type_toggle_list_m,
|
||||
onTap: (state) => _turnInto(state, ToggleListBlockKeys.type),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
ToggleListBlockKeys.type,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final toggleH1SuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.toggleH1,
|
||||
title: LocaleKeys.editor_toggleHeading1ShortForm.tr(),
|
||||
svg: FlowySvgs.type_toggle_h1_m,
|
||||
onTap: (state) => _turnInto(state, ToggleListBlockKeys.type, level: 1),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
ToggleListBlockKeys.type,
|
||||
level: 1,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final toggleH2SuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.toggleH2,
|
||||
title: LocaleKeys.editor_toggleHeading2ShortForm.tr(),
|
||||
svg: FlowySvgs.type_toggle_h2_m,
|
||||
onTap: (state) => _turnInto(state, ToggleListBlockKeys.type, level: 2),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
ToggleListBlockKeys.type,
|
||||
level: 2,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final toggleH3SuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.toggleH3,
|
||||
title: LocaleKeys.editor_toggleHeading3ShortForm.tr(),
|
||||
svg: FlowySvgs.type_toggle_h3_m,
|
||||
onTap: (state) => _turnInto(state, ToggleListBlockKeys.type, level: 3),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
ToggleListBlockKeys.type,
|
||||
level: 3,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final callOutSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.callOut,
|
||||
title: LocaleKeys.document_plugins_callout.tr(),
|
||||
svg: FlowySvgs.type_callout_m,
|
||||
onTap: (state) => _turnInto(state, CalloutBlockKeys.type),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
CalloutBlockKeys.type,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
final quoteSuggestionItem = SuggestionItem(
|
||||
type: SuggestionType.quote,
|
||||
title: LocaleKeys.editor_quote.tr(),
|
||||
svg: FlowySvgs.type_quote_m,
|
||||
onTap: (state) => _turnInto(state, QuoteBlockKeys.type),
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
QuoteBlockKeys.type,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> _turnInto(EditorState state, String type, {int? level}) async {
|
||||
final pateItem = SuggestionItem(
|
||||
type: SuggestionType.page,
|
||||
title: LocaleKeys.editor_page.tr(),
|
||||
svg: FlowySvgs.icon_document_s,
|
||||
onTap: (state, keepSelection) => _turnInto(
|
||||
state,
|
||||
SubPageBlockKeys.type,
|
||||
viewId: getIt<MenuSharedState>().latestOpenView?.id,
|
||||
keepSelection: keepSelection,
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> _turnInto(
|
||||
EditorState state,
|
||||
String type, {
|
||||
int? level,
|
||||
String? viewId,
|
||||
bool keepSelection = true,
|
||||
}) async {
|
||||
final selection = state.selection!;
|
||||
final node = state.getNodeAtPath(selection.start.path)!;
|
||||
await BlockActionOptionCubit.turnIntoBlock(
|
||||
@ -413,7 +501,8 @@ Future<void> _turnInto(EditorState state, String type, {int? level}) async {
|
||||
node,
|
||||
state,
|
||||
level: level,
|
||||
keepSelection: true,
|
||||
currentViewId: viewId,
|
||||
keepSelection: keepSelection,
|
||||
);
|
||||
}
|
||||
|
||||
@ -431,6 +520,7 @@ final suggestions = UnmodifiableListView([
|
||||
toggleH3SuggestionItem,
|
||||
callOutSuggestionItem,
|
||||
quoteSuggestionItem,
|
||||
pateItem,
|
||||
]);
|
||||
|
||||
final nodeType2SuggestionType = UnmodifiableMapView({
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_hover_menu.dart';
|
||||
import 'package:appflowy/util/expand_views.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy_backend/appflowy_backend.dart';
|
||||
@ -185,6 +186,7 @@ Future<void> initGetIt(
|
||||
);
|
||||
getIt.registerSingleton<PluginSandbox>(PluginSandbox());
|
||||
getIt.registerSingleton<ViewExpanderRegistry>(ViewExpanderRegistry());
|
||||
getIt.registerSingleton<LinkHoverTriggers>(LinkHoverTriggers());
|
||||
|
||||
await DependencyResolver.resolve(getIt, mode);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:appflowy/workspace/application/settings/appearance/base_appearan
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra/theme_extension_v2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DesktopAppearance extends BaseAppearance {
|
||||
@ -14,9 +15,8 @@ class DesktopAppearance extends BaseAppearance {
|
||||
) {
|
||||
assert(codeFontFamily.isNotEmpty);
|
||||
|
||||
final theme = brightness == Brightness.light
|
||||
? appTheme.lightTheme
|
||||
: appTheme.darkTheme;
|
||||
final isLight = brightness == Brightness.light;
|
||||
final theme = isLight ? appTheme.lightTheme : appTheme.darkTheme;
|
||||
|
||||
final colorScheme = ColorScheme(
|
||||
brightness: brightness,
|
||||
@ -152,6 +152,11 @@ class DesktopAppearance extends BaseAppearance {
|
||||
lightIconColor: theme.lightIconColor,
|
||||
toolbarHoverColor: theme.toolbarHoverColor,
|
||||
),
|
||||
isLight
|
||||
? lightAFThemeV2
|
||||
: darkAFThemeV2.copyWith(
|
||||
icon_primary: theme.icon,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class AFThemeExtensionV2 extends ThemeExtension<AFThemeExtensionV2> {
|
||||
static AFThemeExtensionV2 of(BuildContext context) =>
|
||||
Theme.of(context).extension<AFThemeExtensionV2>()!;
|
||||
|
||||
static AFThemeExtensionV2? maybeOf(BuildContext context) =>
|
||||
Theme.of(context).extension<AFThemeExtensionV2>();
|
||||
|
||||
const AFThemeExtensionV2({
|
||||
required this.icon_primary,
|
||||
required this.icon_tertiary,
|
||||
required this.border_grey_quaternary,
|
||||
required this.fill_theme_select,
|
||||
required this.fill_grey_thick_alpha_1,
|
||||
required this.shadow_medium,
|
||||
});
|
||||
|
||||
final Color icon_primary;
|
||||
final Color icon_tertiary;
|
||||
final Color border_grey_quaternary;
|
||||
final Color fill_theme_select;
|
||||
final Color fill_grey_thick_alpha_1;
|
||||
final Color shadow_medium;
|
||||
|
||||
@override
|
||||
AFThemeExtensionV2 copyWith({
|
||||
Color? icon_primary,
|
||||
Color? icon_tertiary,
|
||||
Color? border_grey_quaternary,
|
||||
Color? fill_theme_select,
|
||||
Color? fill_grey_thick_alpha_1,
|
||||
Color? shadow_medium,
|
||||
}) =>
|
||||
AFThemeExtensionV2(
|
||||
icon_primary: icon_primary ?? this.icon_primary,
|
||||
icon_tertiary: icon_tertiary ?? this.icon_tertiary,
|
||||
border_grey_quaternary:
|
||||
border_grey_quaternary ?? this.border_grey_quaternary,
|
||||
fill_theme_select: fill_theme_select ?? this.fill_theme_select,
|
||||
fill_grey_thick_alpha_1:
|
||||
fill_grey_thick_alpha_1 ?? this.fill_grey_thick_alpha_1,
|
||||
shadow_medium: shadow_medium ?? this.shadow_medium,
|
||||
);
|
||||
|
||||
@override
|
||||
ThemeExtension<AFThemeExtensionV2> lerp(
|
||||
ThemeExtension<AFThemeExtensionV2>? other, double t) {
|
||||
if (other is! AFThemeExtensionV2) {
|
||||
return this;
|
||||
}
|
||||
return AFThemeExtensionV2(
|
||||
icon_primary:
|
||||
Color.lerp(icon_primary, other.icon_primary, t) ?? icon_primary,
|
||||
icon_tertiary:
|
||||
Color.lerp(icon_tertiary, other.icon_tertiary, t) ?? icon_tertiary,
|
||||
border_grey_quaternary:
|
||||
Color.lerp(border_grey_quaternary, other.border_grey_quaternary, t) ??
|
||||
border_grey_quaternary,
|
||||
fill_theme_select:
|
||||
Color.lerp(fill_theme_select, other.fill_theme_select, t) ??
|
||||
fill_theme_select,
|
||||
fill_grey_thick_alpha_1: Color.lerp(
|
||||
fill_grey_thick_alpha_1, other.fill_grey_thick_alpha_1, t) ??
|
||||
fill_grey_thick_alpha_1,
|
||||
shadow_medium:
|
||||
Color.lerp(shadow_medium, other.shadow_medium, t) ?? shadow_medium,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AFThemeExtensionV2 darkAFThemeV2 = AFThemeExtensionV2(
|
||||
icon_primary: Color(0xFF1F2329),
|
||||
icon_tertiary: Color(0xFF99A1A8),
|
||||
border_grey_quaternary: Color(0xFFE8ECF3),
|
||||
fill_theme_select: Color(0x00BCF01F),
|
||||
fill_grey_thick_alpha_1: Color(0x1F23290F),
|
||||
shadow_medium: Color(0x1F22251F),
|
||||
);
|
||||
|
||||
const AFThemeExtensionV2 lightAFThemeV2 = AFThemeExtensionV2(
|
||||
icon_primary: Color(0xFF1F2329),
|
||||
icon_tertiary: Color(0xFF99A1A8),
|
||||
border_grey_quaternary: Color(0xFFE8ECF3),
|
||||
fill_theme_select: Color(0x00BCF01F),
|
||||
fill_grey_thick_alpha_1: Color(0x1F23290F),
|
||||
shadow_medium: Color(0x1F22251F),
|
||||
);
|
||||
@ -98,8 +98,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "8f314fd"
|
||||
resolved-ref: "8f314fda5981e650a52ba522ba7915e13940d837"
|
||||
ref: "5ad9d77"
|
||||
resolved-ref: "5ad9d771a8496dea95a9c5b1ec77f76df3983037"
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||
source: git
|
||||
version: "5.1.0"
|
||||
|
||||
@ -184,7 +184,7 @@ dependency_overrides:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: "8f314fd"
|
||||
ref: "5ad9d77"
|
||||
|
||||
appflowy_editor_plugins:
|
||||
git:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user