-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add outline block #2750
feat: add outline block #2750
Conversation
@LucasXu0 I was able to achieve the following output so far. Some of the issues I am facing are:
I would love to know what you have to suggest for these issues. Also please let me know about your views on the progress so far. |
You can filter the transaction. only rebuilding if the heading is changed.
I think we just need to display the heading without its inline styles, such as color and href. |
class OutlineBlockKeys { | ||
const OutlineBlockKeys._(); | ||
|
||
static const String type = 'outline_block'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static const String type = 'outline_block'; | |
static const String type = 'outline'; |
// defining the callout block menu item for selection | ||
SelectionMenuItem outlineItem = SelectionMenuItem.node( | ||
name: 'Outline', | ||
iconData: Icons.clear_all, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clear_all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list_alt looks better.
Let me checkout your branch and add the suggestion comment. |
@@ -1,6 +1,7 @@ | |||
import 'package:appflowy/plugins/document/application/doc_bloc.dart'; | |||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/option_action.dart'; | |||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_list.dart'; | |||
import 'package:appflowy/plugins/document/presentation/editor_plugins/outline/outline_block_component.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add the file path to frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart
|
||
// defining the callout block menu item for selection | ||
SelectionMenuItem outlineItem = SelectionMenuItem.node( | ||
name: 'Outline', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use l10n instead. Adding 'outline' to en.json
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked out other plugins for reference but no one uses i10n for the SelectionMenuItem
name. Let me know if the following approach would be fine:
en.json
"outline": {
"name": "Outline",
"heading": "Table of Contents"
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
@override | ||
bool validate(Node node) => true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validate the outline_block.children must be empty.
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text( | ||
"TABLE OF CONTENTS: ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l10n
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for clarification would the following do fine?
"mathEquation": {
"addMathEquation": "Add Math Equation",
"editMathEquation": "Edit Math Equation"
},
"outline":{
"heading": "TABLE OF CONTENTS:"
},
Similar to the
mathEquation
plugin I also created theoutline
key.
children: [ | ||
Text( | ||
"TABLE OF CONTENTS: ", | ||
style: editorState.editorStyle.textStyleConfiguration.text, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: editorState.editorStyle.textStyleConfiguration.text, | |
style: configuration.textStyle(node), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The configuration.textStyle(node)
doesn't work as expected when the font size of the document is changed. However the earlier approach does work as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. we should use editorState.editorStyle.textStyleConfiguration.text
here. I misread it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR looks good to me, except for the missing test. I will merge it once you add the tests and ensure that all the CI passes.
@AmanNegi Please check the comments and add the tests. |
Codecov Report
@@ Coverage Diff @@
## main #2750 +/- ##
===========================================
+ Coverage 12.36% 66.70% +54.34%
===========================================
Files 403 407 +4
Lines 19093 19333 +240
===========================================
+ Hits 2360 12897 +10537
+ Misses 16733 6436 -10297
Flags with carried forward coverage won't be shown. Click here to find out more.
|
@override | ||
Node get node => widget.node; | ||
|
||
late EditorState editorState = context.read<EditorState>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Late but you assign it a value immediately?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The context
is not available at the starting phase, so it basically waits for the widget to initialise and then it assigns the variable. If you want I could put the assignment separately in initState
for easier understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah fair enough, I'm not used to this way of accessing buildcontext, never used it much tbh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I'll change it to more commonly found syntax:
late EditorState editorState;
@override
void initState() {
super.initState();
editorState = context.read<EditorState>();
}
Let me know if this looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, both of them look good to me. I prefer to the existing one, because no need to override the initState
.
child = BlockComponentActionWrapper( | ||
node: widget.node, | ||
actionBuilder: widget.actionBuilder!, | ||
child: _buildOutlineBlock(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
child: _buildOutlineBlock(), | |
child: child, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not advisable to use 'child' here. The widget will not be updated within the builder if 'child' is returned; instead, we should rebuild the widget.
The below code is wrong too. It should return _buildOutlineBlock()
directly, not the child.
), | ||
Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: headingNodes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
children: headingNodes | |
children: getHeadingNodes() |
No need to have a variable with this if you only use it in one place.
const Divider( | ||
color: Colors.white54, | ||
), | ||
Column( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need a Column here? You're already inside a Column with the exact same crossAxisAlignment.
Just do
...getHeadingNodes().map((e) => OutlineItemWidget(node: e)).toList(),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah simply spreading the list of widgets would be a good idea. Thanks ✨
- Add localized strings - Fix minor suggested issues
cc @LucasXu0 Could you please guide me with this. |
@LucasXu0 I have added the outline tests. Could you please verify the tests. |
@AmanNegi Sure. |
extension on Node { | ||
double get verticalSpacing { | ||
if (type != HeadingBlockKeys.type) { | ||
assert(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed this assert, feels like a hack to me. There are two similar ones further below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because only node with heading type can fetch this values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but assert(false)
is not meaningful, and does not provide any information that would lead one to resolve it.
Hence why I feel that it is a hack.
It would make more sense to have assert(type == HeadingBlockKeys.type)
, as that would give a meaningful assertion error. I understand its use, and I'm not saying we should change it since it's only in debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
FYI, @AmanNegi. I refactor the UI part.
|
Merged! 🎉🎊 Thanks a lot for your support @LucasXu0. |
Feature Preview
Fixes: #2688
table_of_contents.mp4
PR Checklist