mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: improve outline block (#4722)
This commit is contained in:
parent
2abb396467
commit
cea1c17b76
@ -114,9 +114,12 @@ enum OptionAlignType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum OptionDepthType {
|
enum OptionDepthType {
|
||||||
h1(1, "H1"),
|
h1(1, 'H1'),
|
||||||
h2(2, "H2"),
|
h2(2, 'H2'),
|
||||||
h3(3, "H3");
|
h3(3, 'H3'),
|
||||||
|
h4(4, 'H4'),
|
||||||
|
h5(5, 'H5'),
|
||||||
|
h6(6, 'H6');
|
||||||
|
|
||||||
const OptionDepthType(this.level, this.description);
|
const OptionDepthType(this.level, this.description);
|
||||||
|
|
||||||
|
@ -32,6 +32,12 @@ Node outlineBlockNode() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum _OutlineBlockStatus {
|
||||||
|
noHeadings,
|
||||||
|
noMatchHeadings,
|
||||||
|
success;
|
||||||
|
}
|
||||||
|
|
||||||
class OutlineBlockComponentBuilder extends BlockComponentBuilder {
|
class OutlineBlockComponentBuilder extends BlockComponentBuilder {
|
||||||
OutlineBlockComponentBuilder({
|
OutlineBlockComponentBuilder({
|
||||||
super.configuration,
|
super.configuration,
|
||||||
@ -75,7 +81,7 @@ class _OutlineBlockWidgetState extends State<OutlineBlockWidget>
|
|||||||
BlockComponentTextDirectionMixin,
|
BlockComponentTextDirectionMixin,
|
||||||
BlockComponentBackgroundColorMixin {
|
BlockComponentBackgroundColorMixin {
|
||||||
// Change the value if the heading block type supports heading levels greater than '3'
|
// Change the value if the heading block type supports heading levels greater than '3'
|
||||||
static const finalHeadingLevel = 3;
|
static const maxVisibleDepth = 6;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BlockComponentConfiguration get configuration => widget.configuration;
|
BlockComponentConfiguration get configuration => widget.configuration;
|
||||||
@ -120,81 +126,97 @@ class _OutlineBlockWidgetState extends State<OutlineBlockWidget>
|
|||||||
final textDirection = calculateTextDirection(
|
final textDirection = calculateTextDirection(
|
||||||
layoutDirection: Directionality.maybeOf(context),
|
layoutDirection: Directionality.maybeOf(context),
|
||||||
);
|
);
|
||||||
|
final (status, headings) = getHeadingNodes();
|
||||||
|
|
||||||
final children = getHeadingNodes()
|
Widget child;
|
||||||
.map(
|
|
||||||
(e) => Container(
|
switch (status) {
|
||||||
padding: const EdgeInsets.only(
|
case _OutlineBlockStatus.noHeadings:
|
||||||
bottom: 4.0,
|
child = Align(
|
||||||
),
|
alignment: Alignment.centerLeft,
|
||||||
width: double.infinity,
|
child: Text(
|
||||||
child: OutlineItemWidget(
|
LocaleKeys.document_plugins_outline_addHeadingToCreateOutline.tr(),
|
||||||
node: e,
|
style: configuration.placeholderTextStyle(node),
|
||||||
textDirection: textDirection,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
.toList();
|
case _OutlineBlockStatus.noMatchHeadings:
|
||||||
|
child = Align(
|
||||||
final child = children.isEmpty
|
alignment: Alignment.centerLeft,
|
||||||
? Align(
|
child: Text(
|
||||||
alignment: Alignment.centerLeft,
|
LocaleKeys.document_plugins_outline_noMatchHeadings.tr(),
|
||||||
child: Text(
|
style: configuration.placeholderTextStyle(node),
|
||||||
LocaleKeys.document_plugins_outline_addHeadingToCreateOutline
|
),
|
||||||
.tr(),
|
);
|
||||||
style: configuration.placeholderTextStyle(node),
|
case _OutlineBlockStatus.success:
|
||||||
),
|
final children = headings
|
||||||
)
|
.map(
|
||||||
: Container(
|
(e) => Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.only(
|
||||||
vertical: 2.0,
|
bottom: 4.0,
|
||||||
horizontal: 5.0,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
|
||||||
color: backgroundColor,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
key: ValueKey(children.hashCode),
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
textDirection: textDirection,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
LocaleKeys.document_outlineBlock_placeholder.tr(),
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
),
|
||||||
const VSpace(8.0),
|
width: double.infinity,
|
||||||
Padding(
|
child: OutlineItemWidget(
|
||||||
padding: const EdgeInsets.only(left: 15.0),
|
node: e,
|
||||||
child: Column(
|
textDirection: textDirection,
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
)
|
||||||
);
|
.toList();
|
||||||
|
child = Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 15.0),
|
||||||
|
child: Column(
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
),
|
),
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: child,
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 2.0,
|
||||||
|
horizontal: 5.0,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
||||||
|
color: backgroundColor,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
textDirection: textDirection,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
LocaleKeys.document_outlineBlock_placeholder.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const VSpace(8.0),
|
||||||
|
child,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<Node> getHeadingNodes() {
|
(_OutlineBlockStatus, Iterable<Node>) getHeadingNodes() {
|
||||||
final children = editorState.document.root.children;
|
final children = editorState.document.root.children;
|
||||||
final int level =
|
final int level =
|
||||||
node.attributes[OutlineBlockKeys.depth] ?? finalHeadingLevel;
|
node.attributes[OutlineBlockKeys.depth] ?? maxVisibleDepth;
|
||||||
|
var headings = children.where(
|
||||||
return children.where(
|
(e) => e.type == HeadingBlockKeys.type && e.delta?.isNotEmpty == true,
|
||||||
(element) =>
|
|
||||||
element.type == HeadingBlockKeys.type &&
|
|
||||||
element.delta?.isNotEmpty == true &&
|
|
||||||
element.attributes[HeadingBlockKeys.level] <= level,
|
|
||||||
);
|
);
|
||||||
|
if (headings.isEmpty) {
|
||||||
|
return (_OutlineBlockStatus.noHeadings, []);
|
||||||
|
}
|
||||||
|
headings =
|
||||||
|
headings.where((e) => e.attributes[HeadingBlockKeys.level] <= level);
|
||||||
|
if (headings.isEmpty) {
|
||||||
|
return (_OutlineBlockStatus.noMatchHeadings, []);
|
||||||
|
}
|
||||||
|
return (_OutlineBlockStatus.success, headings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,12 +285,8 @@ extension on Node {
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
final level = attributes[HeadingBlockKeys.level];
|
final level = attributes[HeadingBlockKeys.level];
|
||||||
if (level == 2) {
|
final indent = (level - 1) * 15.0 + 10.0;
|
||||||
return 20;
|
return indent;
|
||||||
} else if (level == 3) {
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get outlineItemText {
|
String get outlineItemText {
|
||||||
|
@ -793,7 +793,8 @@
|
|||||||
"copiedToPasteBoard": "The link has been copied to the clipboard"
|
"copiedToPasteBoard": "The link has been copied to the clipboard"
|
||||||
},
|
},
|
||||||
"outline": {
|
"outline": {
|
||||||
"addHeadingToCreateOutline": "Add headings to create a table of contents."
|
"addHeadingToCreateOutline": "Add headings to create a table of contents.",
|
||||||
|
"noMatchHeadings": "No matching headings found."
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"addAfter": "Add after",
|
"addAfter": "Add after",
|
||||||
|
Loading…
Reference in New Issue
Block a user