Skip to content

Commit 944e064

Browse files
committed
Merge branch 'main' into feat/notification
2 parents 20a859b + a26ebbc commit 944e064

33 files changed

Lines changed: 877 additions & 623 deletions

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'package:appflowy/workspace/presentation/home/af_focus_manager.dart';
2222
import 'package:appflowy_editor/appflowy_editor.dart' hide QuoteBlockKeys;
2323
import 'package:collection/collection.dart';
2424
import 'package:flowy_infra/theme_extension.dart';
25+
import 'package:flowy_infra/theme_extension_v2.dart';
2526
import 'package:flutter/material.dart';
2627
import 'package:flutter/services.dart';
2728
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -350,6 +351,8 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
350351
final isViewDeleted = context.read<DocumentBloc>().state.isDeleted;
351352
final isLocked =
352353
context.read<ViewLockStatusBloc?>()?.state.isLocked ?? false;
354+
355+
final themeV2 = AFThemeExtensionV2.of(context);
353356
final editor = Directionality(
354357
textDirection: textDirection,
355358
child: AppFlowyEditor(
@@ -428,20 +431,25 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
428431
),
429432
);
430433
}
431-
432434
return Center(
433435
child: FloatingToolbar(
434436
floatingToolbarHeight: 40,
435437
padding: EdgeInsets.symmetric(horizontal: 6),
436438
style: FloatingToolbarStyle(
437439
backgroundColor: Theme.of(context).cardColor,
438440
toolbarActiveColor: Color(0xffe0f8fd),
439-
toolbarElevation: 10,
440441
),
441442
items: toolbarItems,
442-
decoration: ShapeDecoration(
443+
decoration: BoxDecoration(
444+
borderRadius: BorderRadius.circular(8),
443445
color: Theme.of(context).cardColor,
444-
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
446+
boxShadow: [
447+
BoxShadow(
448+
offset: Offset(0, 4),
449+
blurRadius: 24,
450+
color: themeV2.shadow_medium,
451+
),
452+
],
445453
),
446454
toolbarBuilder: (context, child, onDismiss, isMetricsChanged) =>
447455
DesktopFloatingToolbar(

frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/turn_into_option_action.dart

Lines changed: 113 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
22
import 'package:appflowy/generated/locale_keys.g.dart';
33
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart';
44
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
5-
import 'package:appflowy/startup/startup.dart';
6-
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
5+
import 'package:appflowy/plugins/document/presentation/editor_plugins/toolbar_item/text_suggestions_toolbar_item.dart';
76
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
87
import 'package:appflowy_editor/appflowy_editor.dart'
98
hide QuoteBlockKeys, quoteNode;
@@ -149,214 +148,134 @@ class TurnIntoOptionMenu extends StatelessWidget {
149148

150149
@override
151150
Widget build(BuildContext context) {
152-
return Column(
153-
mainAxisSize: MainAxisSize.min,
154-
children: _buildTurnIntoOptions(context, node),
155-
);
156-
}
157-
158-
List<Widget> _buildTurnIntoOptions(BuildContext context, Node node) {
159-
final children = <Widget>[];
160-
161151
if (hasNonSupportedTypes) {
162-
return children
163-
..add(
164-
_TurnInfoButton(
165-
type: SubPageBlockKeys.type,
166-
node: node,
167-
),
168-
);
152+
return buildItem(
153+
pateItem,
154+
textSuggestionItem,
155+
context.read<BlockActionOptionCubit>().editorState,
156+
);
169157
}
170158

171-
for (final type in EditorOptionActionType.turnInto.supportTypes) {
172-
if (type == ToggleListBlockKeys.type) {
173-
// toggle list block and toggle heading block are the same type,
174-
// but they have different attributes.
175-
176-
// toggle list block
177-
children.add(
178-
_TurnInfoButton(
179-
type: type,
180-
node: node,
181-
),
182-
);
159+
return _buildTurnIntoOptions(context, node);
160+
}
183161

184-
// toggle heading block
185-
for (final i in [1, 2, 3]) {
186-
children.add(
187-
_TurnInfoButton(
188-
type: type,
189-
node: node,
190-
level: i,
191-
),
192-
);
162+
Widget _buildTurnIntoOptions(BuildContext context, Node node) {
163+
final editorState = context.read<BlockActionOptionCubit>().editorState;
164+
SuggestionItem currentSuggestionItem = textSuggestionItem;
165+
final List<SuggestionItem> suggestionItems = suggestions.sublist(0, 4);
166+
final List<SuggestionItem> turnIntoItems =
167+
suggestions.sublist(4, suggestions.length);
168+
final textColor = Color(0xff99A1A8);
169+
170+
void refreshSuggestions() {
171+
final selection = editorState.selection;
172+
if (selection == null || !selection.isSingle) return;
173+
final node = editorState.getNodeAtPath(selection.start.path);
174+
if (node == null || node.delta == null) return;
175+
final nodeType = node.type;
176+
SuggestionType? suggestionType;
177+
if (nodeType == HeadingBlockKeys.type) {
178+
final level = node.attributes[HeadingBlockKeys.level] ?? 1;
179+
if (level == 1) {
180+
suggestionType = SuggestionType.h1;
181+
} else if (level == 2) {
182+
suggestionType = SuggestionType.h2;
183+
} else if (level == 3) {
184+
suggestionType = SuggestionType.h3;
185+
}
186+
} else if (nodeType == ToggleListBlockKeys.type) {
187+
final level = node.attributes[ToggleListBlockKeys.level];
188+
if (level == null) {
189+
suggestionType = SuggestionType.toggle;
190+
} else if (level == 1) {
191+
suggestionType = SuggestionType.toggleH1;
192+
} else if (level == 2) {
193+
suggestionType = SuggestionType.toggleH2;
194+
} else if (level == 3) {
195+
suggestionType = SuggestionType.toggleH3;
193196
}
194-
} else if (type != HeadingBlockKeys.type) {
195-
children.add(
196-
_TurnInfoButton(
197-
type: type,
198-
node: node,
199-
),
200-
);
201197
} else {
202-
for (final i in [1, 2, 3]) {
203-
children.add(
204-
_TurnInfoButton(
205-
type: type,
206-
node: node,
207-
level: i,
208-
),
209-
);
198+
suggestionType = nodeType2SuggestionType[nodeType];
199+
}
200+
if (suggestionType == null) return;
201+
suggestionItems.clear();
202+
turnIntoItems.clear();
203+
for (final item in suggestions) {
204+
if (item.type.group == suggestionType.group &&
205+
item.type != suggestionType) {
206+
suggestionItems.add(item);
207+
} else {
208+
turnIntoItems.add(item);
210209
}
211210
}
211+
currentSuggestionItem =
212+
suggestions.where((item) => item.type == suggestionType).first;
212213
}
213214

214-
return children;
215-
}
216-
}
217-
218-
class _TurnInfoButton extends StatelessWidget {
219-
const _TurnInfoButton({
220-
required this.type,
221-
required this.node,
222-
this.level,
223-
});
224-
225-
final String type;
226-
final Node node;
227-
final int? level;
228-
229-
@override
230-
Widget build(BuildContext context) {
231-
final name = _buildLocalization(type, level: level);
232-
final leftIcon = _buildLeftIcon(type, level: level);
233-
final rightIcon = _buildRightIcon(type, node, level: level);
215+
refreshSuggestions();
234216

235-
return HoverButton(
236-
name: name,
237-
leftIcon: FlowySvg(leftIcon),
238-
rightIcon: rightIcon,
239-
itemHeight: ActionListSizes.itemHeight,
240-
onTap: () => BlockActionOptionCubit.turnIntoBlock(
241-
type,
242-
node,
243-
context.read<BlockActionOptionCubit>().editorState,
244-
level: level,
245-
currentViewId: getIt<MenuSharedState>().latestOpenView?.id,
246-
),
217+
return Column(
218+
mainAxisSize: MainAxisSize.min,
219+
crossAxisAlignment: CrossAxisAlignment.start,
220+
children: [
221+
buildSubTitle(
222+
LocaleKeys.document_toolbar_suggestions.tr(),
223+
textColor,
224+
),
225+
...List.generate(suggestionItems.length, (index) {
226+
return buildItem(
227+
suggestionItems[index],
228+
currentSuggestionItem,
229+
editorState,
230+
);
231+
}),
232+
buildSubTitle(LocaleKeys.document_toolbar_turnInto.tr(), textColor),
233+
...List.generate(turnIntoItems.length, (index) {
234+
return buildItem(
235+
turnIntoItems[index],
236+
currentSuggestionItem,
237+
editorState,
238+
);
239+
}),
240+
],
247241
);
248242
}
249243

250-
Widget? _buildRightIcon(String type, Node node, {int? level}) {
251-
if (type != node.type) {
252-
return null;
253-
}
254-
255-
if (node.type == HeadingBlockKeys.type) {
256-
final nodeLevel = node.attributes[HeadingBlockKeys.level] ?? 1;
257-
if (level != nodeLevel) {
258-
return null;
259-
}
260-
}
261-
262-
if (node.type == ToggleListBlockKeys.type) {
263-
final nodeLevel = node.attributes[ToggleListBlockKeys.level];
264-
if (level != nodeLevel) {
265-
return null;
266-
}
267-
}
268-
269-
return const FlowySvg(
270-
FlowySvgs.workspace_selected_s,
271-
blendMode: null,
244+
Widget buildSubTitle(String text, Color color) {
245+
return Container(
246+
height: 32,
247+
margin: EdgeInsets.symmetric(horizontal: 8),
248+
child: Align(
249+
alignment: Alignment.centerLeft,
250+
child: FlowyText.semibold(
251+
text,
252+
color: color,
253+
figmaLineHeight: 16,
254+
),
255+
),
272256
);
273257
}
274258

275-
FlowySvgData _buildLeftIcon(String type, {int? level}) {
276-
if (type == ParagraphBlockKeys.type) {
277-
return FlowySvgs.type_text_m;
278-
} else if (type == HeadingBlockKeys.type) {
279-
switch (level) {
280-
case 1:
281-
return FlowySvgs.type_h1_m;
282-
case 2:
283-
return FlowySvgs.type_h2_m;
284-
case 3:
285-
return FlowySvgs.type_h3_m;
286-
default:
287-
return FlowySvgs.type_text_m;
288-
}
289-
} else if (type == QuoteBlockKeys.type) {
290-
return FlowySvgs.type_quote_m;
291-
} else if (type == BulletedListBlockKeys.type) {
292-
return FlowySvgs.type_bulleted_list_m;
293-
} else if (type == NumberedListBlockKeys.type) {
294-
return FlowySvgs.type_numbered_list_m;
295-
} else if (type == TodoListBlockKeys.type) {
296-
return FlowySvgs.type_todo_m;
297-
} else if (type == CalloutBlockKeys.type) {
298-
return FlowySvgs.type_callout_m;
299-
} else if (type == SubPageBlockKeys.type) {
300-
return FlowySvgs.icon_document_s;
301-
} else if (type == ToggleListBlockKeys.type) {
302-
switch (level) {
303-
case 1:
304-
return FlowySvgs.type_toggle_h1_m;
305-
case 2:
306-
return FlowySvgs.type_toggle_h2_m;
307-
case 3:
308-
return FlowySvgs.type_toggle_h3_m;
309-
default:
310-
return FlowySvgs.type_toggle_list_m;
311-
}
312-
}
313-
314-
throw UnimplementedError('Unsupported block type: $type');
315-
}
316-
317-
String _buildLocalization(
318-
String type, {
319-
int? level,
320-
}) {
321-
switch (type) {
322-
case ParagraphBlockKeys.type:
323-
return LocaleKeys.document_slashMenu_name_text.tr();
324-
case HeadingBlockKeys.type:
325-
switch (level) {
326-
case 1:
327-
return LocaleKeys.document_slashMenu_name_heading1.tr();
328-
case 2:
329-
return LocaleKeys.document_slashMenu_name_heading2.tr();
330-
case 3:
331-
return LocaleKeys.document_slashMenu_name_heading3.tr();
332-
default:
333-
return LocaleKeys.document_slashMenu_name_text.tr();
334-
}
335-
case QuoteBlockKeys.type:
336-
return LocaleKeys.document_slashMenu_name_quote.tr();
337-
case BulletedListBlockKeys.type:
338-
return LocaleKeys.editor_bulletedListShortForm.tr();
339-
case NumberedListBlockKeys.type:
340-
return LocaleKeys.editor_numberedListShortForm.tr();
341-
case TodoListBlockKeys.type:
342-
return LocaleKeys.editor_checkbox.tr();
343-
case CalloutBlockKeys.type:
344-
return LocaleKeys.document_slashMenu_name_callout.tr();
345-
case SubPageBlockKeys.type:
346-
return LocaleKeys.editor_page.tr();
347-
case ToggleListBlockKeys.type:
348-
switch (level) {
349-
case 1:
350-
return LocaleKeys.editor_toggleHeading1ShortForm.tr();
351-
case 2:
352-
return LocaleKeys.editor_toggleHeading2ShortForm.tr();
353-
case 3:
354-
return LocaleKeys.editor_toggleHeading3ShortForm.tr();
355-
default:
356-
return LocaleKeys.editor_toggleListShortForm.tr();
357-
}
358-
}
359-
360-
throw UnimplementedError('Unsupported block type: $type');
259+
Widget buildItem(
260+
SuggestionItem item,
261+
SuggestionItem currentSuggestionItem,
262+
EditorState state,
263+
) {
264+
final isSelected = item.type == currentSuggestionItem.type;
265+
return SizedBox(
266+
height: 36,
267+
child: FlowyButton(
268+
leftIconSize: const Size.square(20),
269+
leftIcon: FlowySvg(item.svg),
270+
iconPadding: 12,
271+
text: FlowyText(
272+
item.title,
273+
fontWeight: FontWeight.w400,
274+
figmaLineHeight: 20,
275+
),
276+
rightIcon: isSelected ? FlowySvg(FlowySvgs.toolbar_check_m) : null,
277+
onTap: () => item.onTap.call(state, false),
278+
),
279+
);
361280
}
362281
}

0 commit comments

Comments
 (0)