Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion super_editor/lib/src/default_editor/tables/table_markdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class MarkdownTableComponentBuilder implements ComponentBuilder {
nodeId: cell.id,
createdAt: cell.metadata[NodeMetadata.createdAt],
text: cell.text,
textAlign: cell.getMetadataValue(TextNodeMetadata.textAlign) ?? TextAlign.left,
textAlign: _maybeParseTextAlign(cell.getMetadataValue(TextNodeMetadata.textAlign)) ?? TextAlign.left,
textStyleBuilder: noStyleBuilder,
padding: const EdgeInsets.all(8.0),
// ^ Default padding, can be overridden through the stylesheet.
Expand All @@ -62,6 +62,16 @@ class MarkdownTableComponentBuilder implements ComponentBuilder {
);
}

TextAlign? _maybeParseTextAlign(String? textAlign) {
return switch (textAlign) {
'left' => TextAlign.left,
'center' => TextAlign.center,
'right' => TextAlign.right,
'justify' => TextAlign.justify,
_ => null,
};
}

@override
Widget? createComponent(
SingleColumnDocumentComponentContext componentContext, SingleColumnLayoutComponentViewModel componentViewModel) {
Expand Down
15 changes: 13 additions & 2 deletions super_editor/lib/src/default_editor/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import 'package:super_editor/src/core/edit_context.dart';
import 'package:super_editor/src/core/editor.dart';
import 'package:super_editor/src/core/styles.dart';
import 'package:super_editor/src/default_editor/attributions.dart';
import 'package:super_editor/src/default_editor/text_ai.dart';
import 'package:super_editor/src/default_editor/text/custom_underlines.dart';
import 'package:super_editor/src/default_editor/text_ai.dart';
import 'package:super_editor/src/infrastructure/_logging.dart';
import 'package:super_editor/src/infrastructure/attributed_text_styles.dart';
import 'package:super_editor/src/infrastructure/composable_text.dart';
Expand All @@ -38,7 +38,18 @@ class TextNode extends DocumentNode {
required this.id,
required this.text,
super.metadata,
});
}) {
if (metadata['textAlign'] == null) {
initAddToMetadata({
'textAlign': 'left',
});
} else {
assert(
metadata['textAlign'] is String?,
'Expected "textAlign" metadata to be of type String, but got: ${metadata['textAlign']}',
);
}
}

@override
final String id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:super_editor/src/core/document.dart';
import 'package:super_editor/src/default_editor/selection_upstream_downstream.dart';
import 'package:super_editor/src/default_editor/tables/table_block.dart';
Expand Down Expand Up @@ -111,15 +110,15 @@ extension TableBlockNodeToHtml on TableBlockNode {

String _getTextAlignStyle(TextNode cell) {
final textAlign = cell.getMetadataValue('textAlign');
if (textAlign == TextAlign.left) {
// Default alignment is left, so we don't need to specify it.
return '';
}
final textAlignString = switch (textAlign) {
TextAlign.center => 'center',
TextAlign.right => 'right',
'center' => 'center',
'right' => 'right',
_ => 'left',
};
if (textAlign == 'left') {
// Default alignment is left, so we don't need to specify it.
return '';
}
return textAlign != null ? ' style="text-align:$textAlignString"' : '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:ui';

import 'package:attributed_text/attributed_text.dart';
import 'package:flutter/foundation.dart';
import 'package:markdown/markdown.dart' hide Document;
import 'package:super_editor/src/core/document.dart';
import 'package:super_editor/src/core/document_selection.dart';
import 'package:super_editor/src/default_editor/attributions.dart';
Expand All @@ -14,7 +13,6 @@ import 'package:super_editor/src/default_editor/selection_upstream_downstream.da
import 'package:super_editor/src/default_editor/tables/table_block.dart';
import 'package:super_editor/src/default_editor/tasks.dart';
import 'package:super_editor/src/default_editor/text.dart';

import 'package:super_editor/src/infrastructure/serialization/markdown/super_editor_syntax.dart';

/// Serializes the given [doc] to Markdown text.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,25 @@ class _MarkdownToDocument implements md.NodeVisitor {
if (blockImage != null) {
_addImage(blockImage);
} else {
final attributedText = parseInlineMarkdown(
element.textContent,
inlineMarkdownSyntaxes: inlineMarkdownSyntaxes,
inlineHtmlSyntaxes: inlineHtmlSyntaxes,
encodeHtml: encodeHtml,
);
_addParagraph(attributedText, element.attributes);
final captionAndImage = _maybeParseImageWithCaption(element.textContent);
if (captionAndImage != null) {
final captionText = parseInlineMarkdown(
captionAndImage.caption,
inlineMarkdownSyntaxes: inlineMarkdownSyntaxes,
inlineHtmlSyntaxes: inlineHtmlSyntaxes,
encodeHtml: encodeHtml,
);
_addParagraph(captionText, element.attributes);
_addImage(captionAndImage.image);
} else {
final attributedText = parseInlineMarkdown(
element.textContent,
inlineMarkdownSyntaxes: inlineMarkdownSyntaxes,
inlineHtmlSyntaxes: inlineHtmlSyntaxes,
encodeHtml: encodeHtml,
);
_addParagraph(attributedText, element.attributes);
}
}

break;
Expand Down Expand Up @@ -328,28 +340,25 @@ class _MarkdownToDocument implements md.NodeVisitor {
break;
}

final textAlign = element.attributes['textAlign'];
_content.add(
ParagraphNode(
id: Editor.createNodeId(),
text: _parseInlineText(element.textContent),
metadata: {
'blockType': headerAttribution,
'textAlign': textAlign,
'textAlign': element.attributes['textAlign'] ?? 'left',
},
),
);
}

void _addParagraph(AttributedText attributedText, Map<String, String> attributes) {
final textAlign = attributes['textAlign'];

_content.add(
ParagraphNode(
id: Editor.createNodeId(),
text: attributedText,
metadata: {
'textAlign': textAlign,
'textAlign': attributes['textAlign'] ?? 'left',
},
),
);
Expand Down Expand Up @@ -480,6 +489,25 @@ class _MarkdownToDocument implements md.NodeVisitor {
syntax: syntax,
);
}

/// If the last line of [markdown] is a block image and there is at least one
/// preceding line (the caption), returns a [_CaptionAndImage] containing the
/// caption text and the parsed image. Otherwise returns `null`.
_CaptionAndImage? _maybeParseImageWithCaption(String markdown) {
final newlineIndex = markdown.lastIndexOf('\n');
if (newlineIndex < 0) {
return null;
}

final lastLine = markdown.substring(newlineIndex + 1);
final image = _maybeParseBlockImage(lastLine);
if (image == null) {
return null;
}

final caption = markdown.substring(0, newlineIndex);
return _CaptionAndImage(caption: caption, image: image);
}
}

/// Converts a deserialized Markdown element into a [DocumentNode].
Expand Down Expand Up @@ -842,6 +870,13 @@ class _HeaderWithAlignmentSyntax extends md.BlockSyntax {
}
}

class _CaptionAndImage {
const _CaptionAndImage({required this.caption, required this.image});

final String caption;
final _MarkdownImage image;
}

class _MarkdownImage {
_MarkdownImage({
required this.url,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:ui';

import 'package:markdown/markdown.dart' as md;
import 'package:super_editor/src/core/document.dart';
import 'package:super_editor/src/core/editor.dart';
Expand Down Expand Up @@ -55,7 +53,7 @@ extension ElementTableExtension on md.Element {
),
metadata: const {
NodeMetadata.blockType: tableHeaderAttribution,
TextNodeMetadata.textAlign: TextAlign.center,
TextNodeMetadata.textAlign: 'center',
},
),
);
Expand All @@ -81,17 +79,17 @@ extension ElementTableExtension on md.Element {
throw Exception('Table body cells must be <td> elements');
}
final textAlign = switch ((headerRow.children![i] as md.Element).attributes['align']) {
'left' => TextAlign.left,
'center' => TextAlign.center,
'right' => TextAlign.right,
_ => TextAlign.left,
'left' => 'left',
'center' => 'center',
'right' => 'right',
_ => 'left',
};

row.add(TextNode(
id: Editor.createNodeId(),
text: parseInlineMarkdown(cellElement.textContent),
metadata: {
if (textAlign != TextAlign.left) TextNodeMetadata.textAlign: textAlign,
if (textAlign != 'left') TextNodeMetadata.textAlign: textAlign,
},
));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,9 @@ class TextBlockDeltaSerializer implements DeltaSerializer {
final blockAttributes = <String, dynamic>{};

// Add all the block-level formats that aren't mutually exclusive.
if (textBlock.metadata["textAlign"] != null) {
blockAttributes["align"] = textBlock.metadata["textAlign"];
final textAlign = textBlock.metadata["textAlign"];
if (textAlign != null && textAlign != "left") {
blockAttributes["align"] = textAlign;
}

final blockType = textBlock.metadata["blockType"] as Attribution?;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:ui';

import 'package:flutter_test/flutter_test.dart';
import 'package:super_editor/super_editor.dart';

Expand Down Expand Up @@ -665,12 +663,12 @@ void main() {
TextNode(
id: "1.1.1",
text: AttributedText("Value 1.1"),
metadata: const {"textAlign": TextAlign.right},
metadata: const {"textAlign": "right"},
),
TextNode(
id: "1.1.2",
text: AttributedText("Value 1.2"),
metadata: const {"textAlign": TextAlign.center},
metadata: const {"textAlign": "center"},
),
],
],
Expand Down
Loading
Loading