diff --git a/packages/notus/lib/convert.dart b/packages/notus/lib/convert.dart index f2fd672d6..fd70759a4 100644 --- a/packages/notus/lib/convert.dart +++ b/packages/notus/lib/convert.dart @@ -6,8 +6,11 @@ library notus.convert; import 'src/convert/markdown.dart'; +import 'src/convert/quill.dart'; export 'src/convert/markdown.dart'; +export 'src/convert/quill.dart'; /// Markdown codec for Notus documents. const NotusMarkdownCodec notusMarkdown = NotusMarkdownCodec(); +const NotusQuillCodec notusQuill = NotusQuillCodec(); diff --git a/packages/notus/lib/src/convert/quill.dart b/packages/notus/lib/src/convert/quill.dart new file mode 100644 index 000000000..52459c5da --- /dev/null +++ b/packages/notus/lib/src/convert/quill.dart @@ -0,0 +1,115 @@ +// Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; + +import 'package:quill_delta/quill_delta.dart'; + +class NotusQuillCodec extends Codec { + const NotusQuillCodec(); + + @override + Converter get decoder => _NotusQuillDecoder(); + + @override + Converter get encoder => _NotusQuillEncoder(); +} + +class _NotusQuillEncoder extends Converter { + @override + Delta convert(Delta input) { + final result = Delta(); + + for (final op in input.toList()) { + if (!op.isInsert) continue; + + final attributes = {}; + op.attributes?.forEach((String key, dynamic value) { + switch (key) { + case 'b': + attributes['bold'] = value; + break; + case 'i': + attributes['italic'] = value; + break; + case 'heading': + attributes['header'] = value; + break; + case 'block': + if (value == 'ul') { + attributes['list'] = 'bullet'; + } else if (value == 'ol') { + attributes['list'] = 'ordered'; + } else if (value == 'code') { + attributes['code-block'] = true; + } else if (value == 'quote') { + attributes['blockquote'] = true; + } else { + attributes[key] = value; + } + break; + case 'a': + attributes['link'] = value; + break; + default: + attributes[key] = value; + } + }); + result.insert(op.data, attributes.isEmpty ? null : attributes); + } + return result; + } +} + +class _NotusQuillDecoder extends Converter { + @override + Delta convert(Delta input) { + final result = Delta(); + + for (final op in input.toList()) { + if (!op.isInsert) continue; + + final attributes = {}; + op.attributes?.forEach((String key, dynamic value) { + switch (key) { + case 'bold': + attributes['b'] = value; + break; + case 'italic': + attributes['i'] = value; + break; + case 'header': + attributes['heading'] = value; + break; + case 'list': + if (value == 'bullet') { + attributes['block'] = 'ul'; + } else if (value == 'ordered') { + attributes['block'] = 'ol'; + } else { + attributes[key] = value; + } + break; + case 'link': + attributes['a'] = value; + break; + case 'code-block': + if (value == true) { + attributes['block'] = 'code'; + } + break; + case 'blockquote': + if (value == true) { + attributes['block'] = 'quote'; + } + break; + default: + attributes[key] = value; + } + }); + result.insert(op.data, attributes.isEmpty ? null : attributes); + } + return result; + } +} diff --git a/packages/notus/test/convert/quill_test.dart b/packages/notus/test/convert/quill_test.dart new file mode 100644 index 000000000..825a5fa24 --- /dev/null +++ b/packages/notus/test/convert/quill_test.dart @@ -0,0 +1,34 @@ +// Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; + +import 'package:notus/convert.dart'; +import 'package:quill_delta/quill_delta.dart'; +import 'package:test/test.dart'; + +void main() { + group('$NotusQuillCodec.encode', () { + test('Should return correct string', () { + var result = notusQuill.encode(notus_doc); + + expect(jsonEncode(result.toJson()), quill_string); + }); + }); + + group('$NotusQuillCodec.decode', () { + test('Should return correct string', () { + var result = notusQuill.decode(quill_doc); + + expect(jsonEncode(result.toJson()), notus_string); + }); + }); +} + +final notus_string = + r'[{"insert":"Zefyr"},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\nZefyr is an "},{"insert":"early preview","attributes":{"b":true}},{"insert":" open source library.\nDocumentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"block":"ol"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and flexibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:notus/notus.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}}]'; +final notus_doc = Delta.fromJson(json.decode(notus_string) as List); + +final quill_string = + r'[{"insert":"Zefyr"},{"insert":"\n","attributes":{"header":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"italic":true}},{"insert":"\nZefyr is an "},{"insert":"early preview","attributes":{"bold":true}},{"insert":" open source library.\nDocumentation"},{"insert":"\n","attributes":{"header":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"header":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and flexibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"code-block":true}},{"insert":"import ‘package:notus/notus.dart’;"},{"insert":"\n\n","attributes":{"code-block":true}},{"insert":"void main() {"},{"insert":"\n","attributes":{"code-block":true}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"code-block":true}},{"insert":"}"},{"insert":"\n","attributes":{"code-block":true}}]'; +final quill_doc = Delta.fromJson(json.decode(notus_string) as List);