Skip to content

Commit 06f67b4

Browse files
authored
Merge pull request #17 from RZEROSTERN/feat/phase-2-viewer-completeness
Added available components to library
2 parents 9463302 + 30f979a commit 06f67b4

33 files changed

Lines changed: 1272 additions & 105 deletions

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ CLAUDE.local.md
8282
# FVM Version Cache
8383
.fvm/
8484

85-
.vscode/
85+
.vscode/
86+
87+
PR_DESCRIPTION.md

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
## [0.2.0] - 2026-03-25 — Phase 2: Viewer & editor completeness
2+
3+
### New block types (viewer + editor)
4+
* `quote` — blockquote with optional caption and left/center/right alignment.
5+
* `code` — monospace code block with horizontal scroll, selectable text, and a copy-to-clipboard button.
6+
* `checklist` — checkbox list with checked/unchecked state; strikethrough rendered on checked items.
7+
* `table` — 2-D table with optional heading row, horizontal scroll, and add/remove row & column controls in the editor.
8+
* `warning` — highlighted alert box with title and message fields.
9+
10+
### Nested list support
11+
* `ListBlock` items are now `ListItem` objects with a recursive `items` field.
12+
* `ListMapper` handles both flat string items (`@editorjs/list`) and nested object items (`@editorjs/nested-list`) transparently.
13+
* `ListRenderer` renders nested items with indentation per depth level.
14+
15+
### HTML sanitization
16+
* Added `HtmlSanitizer` utility in the data layer that strips `<script>`, `<iframe>`, and `on*` event handler attributes before passing HTML to `flutter_html`.
17+
* Applied in `ParagraphRenderer` and will apply to any renderer that calls `HtmlSanitizer.sanitize()`.
18+
19+
### Toolbar
20+
* Added toolbar buttons for all new block types: Quote, Code, Checklist, Table, Warning.
21+
22+
### Exports
23+
* All five new block entity classes are exported from the public barrel file.
24+
25+
---
26+
127
## [0.1.0] - 2026-03-25 — Clean Architecture refactor (breaking)
228

329
This release is a full architectural rewrite. The public API has changed.

lib/editorjs_flutter.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ export 'src/domain/entities/blocks/paragraph_block.dart';
2828
export 'src/domain/entities/blocks/list_block.dart';
2929
export 'src/domain/entities/blocks/delimiter_block.dart';
3030
export 'src/domain/entities/blocks/image_block.dart';
31+
export 'src/domain/entities/blocks/quote_block.dart';
32+
export 'src/domain/entities/blocks/code_block.dart';
33+
export 'src/domain/entities/blocks/checklist_block.dart';
34+
export 'src/domain/entities/blocks/table_block.dart';
35+
export 'src/domain/entities/blocks/warning_block.dart';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import '../../domain/entities/blocks/checklist_block.dart';
2+
import 'block_mapper.dart';
3+
4+
class ChecklistMapper implements BlockMapper<ChecklistBlock> {
5+
const ChecklistMapper();
6+
7+
@override
8+
String get supportedType => 'checklist';
9+
10+
@override
11+
ChecklistBlock fromJson(Map<String, dynamic> data) {
12+
final rawItems = (data['items'] as List<dynamic>?) ?? [];
13+
return ChecklistBlock(
14+
items: rawItems.whereType<Map<String, dynamic>>().map((i) {
15+
return ChecklistItem(
16+
text: (i['text'] as String?) ?? '',
17+
checked: (i['checked'] as bool?) ?? false,
18+
);
19+
}).toList(),
20+
);
21+
}
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import '../../domain/entities/blocks/code_block.dart';
2+
import 'block_mapper.dart';
3+
4+
class CodeMapper implements BlockMapper<CodeBlock> {
5+
const CodeMapper();
6+
7+
@override
8+
String get supportedType => 'code';
9+
10+
@override
11+
CodeBlock fromJson(Map<String, dynamic> data) =>
12+
CodeBlock(code: (data['code'] as String?) ?? '');
13+
}

lib/src/data/mappers/list_mapper.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,23 @@ class ListMapper implements BlockMapper<ListBlock> {
1414

1515
return ListBlock(
1616
style: styleStr == 'ordered' ? ListStyle.ordered : ListStyle.unordered,
17-
items: rawItems.map((e) => e.toString()).toList(),
17+
items: rawItems.map(_parseItem).toList(),
1818
);
1919
}
20+
21+
/// Handles both flat string items (standard @editorjs/list) and
22+
/// nested object items (@editorjs/nested-list).
23+
ListItem _parseItem(dynamic raw) {
24+
if (raw is String) {
25+
return ListItem(content: raw);
26+
}
27+
if (raw is Map<String, dynamic>) {
28+
final nestedRaw = (raw['items'] as List<dynamic>?) ?? [];
29+
return ListItem(
30+
content: (raw['content'] as String?) ?? '',
31+
items: nestedRaw.map(_parseItem).toList(),
32+
);
33+
}
34+
return ListItem(content: raw.toString());
35+
}
2036
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import '../../domain/entities/blocks/quote_block.dart';
2+
import 'block_mapper.dart';
3+
4+
class QuoteMapper implements BlockMapper<QuoteBlock> {
5+
const QuoteMapper();
6+
7+
@override
8+
String get supportedType => 'quote';
9+
10+
@override
11+
QuoteBlock fromJson(Map<String, dynamic> data) {
12+
final alignStr = (data['alignment'] as String?) ?? 'left';
13+
return QuoteBlock(
14+
text: (data['text'] as String?) ?? '',
15+
caption: data['caption'] as String?,
16+
alignment: QuoteAlignment.values.firstWhere(
17+
(a) => a.name == alignStr,
18+
orElse: () => QuoteAlignment.left,
19+
),
20+
);
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import '../../domain/entities/blocks/table_block.dart';
2+
import 'block_mapper.dart';
3+
4+
class TableMapper implements BlockMapper<TableBlock> {
5+
const TableMapper();
6+
7+
@override
8+
String get supportedType => 'table';
9+
10+
@override
11+
TableBlock fromJson(Map<String, dynamic> data) {
12+
final rawContent = (data['content'] as List<dynamic>?) ?? [];
13+
return TableBlock(
14+
withHeadings: (data['withHeadings'] as bool?) ?? false,
15+
content: rawContent.map((row) {
16+
final List<dynamic> rowList = row is List ? row : <dynamic>[];
17+
return rowList.map((cell) => cell.toString()).toList();
18+
}).toList(),
19+
);
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import '../../domain/entities/blocks/warning_block.dart';
2+
import 'block_mapper.dart';
3+
4+
class WarningMapper implements BlockMapper<WarningBlock> {
5+
const WarningMapper();
6+
7+
@override
8+
String get supportedType => 'warning';
9+
10+
@override
11+
WarningBlock fromJson(Map<String, dynamic> data) => WarningBlock(
12+
title: (data['title'] as String?) ?? '',
13+
message: (data['message'] as String?) ?? '',
14+
);
15+
}

lib/src/data/registry/block_type_registry.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import '../../domain/entities/block_entity.dart';
22
import '../mappers/block_mapper.dart';
3+
import '../mappers/checklist_mapper.dart';
4+
import '../mappers/code_mapper.dart';
35
import '../mappers/delimiter_mapper.dart';
46
import '../mappers/header_mapper.dart';
57
import '../mappers/image_mapper.dart';
68
import '../mappers/list_mapper.dart';
79
import '../mappers/paragraph_mapper.dart';
10+
import '../mappers/quote_mapper.dart';
11+
import '../mappers/table_mapper.dart';
12+
import '../mappers/warning_mapper.dart';
813

914
/// Maps EditorJS block type strings to their [BlockMapper] implementations.
1015
///
@@ -19,6 +24,11 @@ class BlockTypeRegistry {
1924
register(const ListMapper());
2025
register(const DelimiterMapper());
2126
register(const ImageMapper());
27+
register(const QuoteMapper());
28+
register(const CodeMapper());
29+
register(const ChecklistMapper());
30+
register(const TableMapper());
31+
register(const WarningMapper());
2232
}
2333

2434
/// Registers a [BlockMapper]. Overwrites any existing mapper for the same type.

0 commit comments

Comments
 (0)