Skip to content

Commit 082485c

Browse files
[SuperEditor][Quill] - Create super_editor_quill package - parse Quill Deltas into a MutableDocument (Resolves #2105) (#2108)
1 parent 3b8b21b commit 082485c

File tree

15 files changed

+1795
-0
lines changed

15 files changed

+1795
-0
lines changed

.github/workflows/pr_validation.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ jobs:
122122
# Run all tests
123123
- run: flutter test
124124

125+
test_super_editor_quill:
126+
runs-on: ubuntu-latest
127+
defaults:
128+
run:
129+
working-directory: ./super_editor_quill
130+
steps:
131+
# Checkout the PR branch
132+
- uses: actions/checkout@v3
133+
134+
# Setup Flutter environment
135+
- uses: subosito/flutter-action@v2
136+
with:
137+
channel: "master"
138+
139+
# Download all the packages that the app uses
140+
- run: flutter pub get
141+
142+
# TODO: Enforce static analysis
143+
144+
# Run all tests
145+
- run: flutter test
146+
125147
test_super_text_layout:
126148
runs-on: ubuntu-latest
127149
defaults:

super_editor_quill/.gitignore

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# rbenv related
19+
**/.ruby-version
20+
21+
# The .vscode folder contains launch configuration and tasks you configure in
22+
# VS Code which you may wish to be included in version control, so this line
23+
# is commented out by default.
24+
#.vscode/
25+
26+
# Flutter/Dart/Pub related
27+
**/doc/api/
28+
.dart_tool/
29+
.flutter-plugins
30+
.flutter-plugins-dependencies
31+
.packages
32+
.pub-cache/
33+
.pub/
34+
build/
35+
36+
# Ignore the pubspec.lock file for the super_editor library, but not the example app.
37+
/pubspec.lock
38+
39+
# Android related
40+
**/android/**/gradle-wrapper.jar
41+
**/android/.gradle
42+
**/android/captures/
43+
**/android/gradlew
44+
**/android/gradlew.bat
45+
**/android/local.properties
46+
**/android/**/GeneratedPluginRegistrant.java
47+
48+
# iOS/XCode related
49+
**/ios/**/*.mode1v3
50+
**/ios/**/*.mode2v3
51+
**/ios/**/*.moved-aside
52+
**/ios/**/*.pbxuser
53+
**/ios/**/*.perspectivev3
54+
**/ios/**/*sync/
55+
**/ios/**/.sconsign.dblite
56+
**/ios/**/.tags*
57+
**/ios/**/.vagrant/
58+
**/ios/**/DerivedData/
59+
**/ios/**/Icon?
60+
**/ios/**/Pods/
61+
**/ios/**/.symlinks/
62+
**/ios/**/profile
63+
**/ios/**/xcuserdata
64+
**/ios/.generated/
65+
**/ios/Flutter/App.framework
66+
**/ios/Flutter/Flutter.framework
67+
**/ios/Flutter/Flutter.podspec
68+
**/ios/Flutter/Generated.xcconfig
69+
**/ios/Flutter/app.flx
70+
**/ios/Flutter/app.zip
71+
**/ios/Flutter/flutter_assets/
72+
**/ios/Flutter/flutter_export_environment.sh
73+
**/ios/ServiceDefinitions.json
74+
**/ios/Runner/GeneratedPluginRegistrant.*
75+
76+
# Exceptions to above rules.
77+
!**/ios/**/default.mode1v3
78+
!**/ios/**/default.mode2v3
79+
!**/ios/**/default.pbxuser
80+
!**/ios/**/default.perspectivev3

super_editor_quill/.metadata

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: "af84f6b8471c761d61332dc499880cd4e486799d"
8+
channel: "master"
9+
10+
project_type: package

super_editor_quill/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.0.1
2+
3+
* TODO: Describe initial release.

super_editor_quill/LICENSE

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright (c) 2021 Superlist, SuperDeclarative! and the contributors
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

super_editor_quill/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Super Editor Quill
2+
Extensions on Super Editor to support the Quill Deltas document format.
3+
4+
## What is Quill?
5+
Quill is an open source JavaScript text editor created by Facebook.
6+
7+
https://quilljs.com/docs/quickstart
8+
9+
## What is the Quill Delta format?
10+
Quill Delta is the name given to the data structure that describes a Quill document. In other words,
11+
when a Quill editor loads a document, it's loading a document in the Quill Delta format. When a
12+
Quill editor alters a document, the changes are expressed in the Quill Delta format.
13+
14+
The following is a tiny example of a Quill Delta document:
15+
16+
```json
17+
{
18+
"ops": [
19+
{ "insert": "Gandalf", "attributes": { "bold": true } },
20+
{ "insert": " the " },
21+
{ "insert": "Grey", "attributes": { "color": "#cccccc" } }
22+
]
23+
}
24+
```
25+
26+
For more info on Quill Delta, see the official docs: https://quilljs.com/docs/delta/
27+
28+
## What is `super_editor_quill`?
29+
The `super_editor_quill` package is a Flutter package that adds Quill Delta format support to the
30+
`super_editor` package ([Super Editor on Pub](https://pub.dev/packages/super_editor)).
31+
32+
Supporting the Quill Delta format means that a `SuperEditor` document can be constructed from a
33+
Quill Delta document. Also, a `SuperEditor` document can be serialized to a Quill Delta document.
34+
35+
Regardless of the incoming or outgoing document format, the actual editing pipeline within `SuperEditor`
36+
remains the same. Thus, you could start a document from Markdown, and then export a document to
37+
Quill Delta, or vis-a-versa. `SuperEditor` internals are format agnostic.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
include: package:flutter_lints/flutter.yaml
2+
3+
# Additional information about this file can be found at
4+
# https://dart.dev/guides/language/analysis-options
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import 'package:super_editor/super_editor.dart';
2+
3+
/// An [Attribution] that sets the font size of text based on a given size
4+
/// name, e.g., "huge", "large", "normal", "small".
5+
class NamedFontSizeAttribution implements Attribution {
6+
const NamedFontSizeAttribution(this.fontSizeName);
7+
8+
/// The ID that determines whether two overlapping attributions conflict
9+
/// with each other (aren't allowed to overlap).
10+
///
11+
/// In the case of [NamedFontSizeAttribution], this ID needs to match
12+
/// [FontSizeAttribution] because they both impact font size in the final
13+
/// display.
14+
@override
15+
String get id => "font_size";
16+
17+
/// The name of the font size to use for the text attributed with this
18+
/// attribution, e.g., "huge", "large", "normal", "small".
19+
final String fontSizeName;
20+
21+
@override
22+
bool canMergeWith(Attribution other) {
23+
return this == other;
24+
}
25+
26+
@override
27+
bool operator ==(Object other) =>
28+
identical(this, other) ||
29+
other is NamedFontSizeAttribution && runtimeType == other.runtimeType && fontSizeName == other.fontSizeName;
30+
31+
@override
32+
int get hashCode => fontSizeName.hashCode;
33+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import 'package:flutter/cupertino.dart';
2+
import 'package:super_editor/super_editor.dart';
3+
4+
/// [DocumentNode] that represents a video at a URL.
5+
class VideoNode extends UrlMediaNode {
6+
static const videoAttribution = NamedAttribution("video");
7+
8+
VideoNode({
9+
required super.id,
10+
required super.url,
11+
super.altText = '',
12+
super.blockAttribution = videoAttribution,
13+
});
14+
}
15+
16+
/// [DocumentNode] that represents an audio source at a URL.
17+
class AudioNode extends UrlMediaNode {
18+
static const audioAttribution = NamedAttribution("audio");
19+
20+
AudioNode({
21+
required super.id,
22+
required super.url,
23+
super.altText = '',
24+
super.blockAttribution = audioAttribution,
25+
});
26+
}
27+
28+
/// [DocumentNode] that represents a file at a URL.
29+
class FileNode extends UrlMediaNode {
30+
static const fileAttribution = NamedAttribution("file");
31+
32+
FileNode({
33+
required super.id,
34+
required super.url,
35+
super.altText = '',
36+
super.blockAttribution = fileAttribution,
37+
});
38+
}
39+
40+
/// [DocumentNode] that represents a media source that exists a given [url].
41+
class UrlMediaNode extends BlockNode with ChangeNotifier {
42+
UrlMediaNode({
43+
required this.id,
44+
required String url,
45+
String altText = '',
46+
required Attribution blockAttribution,
47+
Map<String, dynamic>? metadata,
48+
}) : _url = url,
49+
_altText = altText {
50+
this.metadata = metadata;
51+
putMetadataValue("blockType", blockAttribution);
52+
}
53+
54+
@override
55+
final String id;
56+
57+
String get url => _url;
58+
String _url;
59+
set url(String newUrl) {
60+
if (newUrl != _url) {
61+
_url = newUrl;
62+
notifyListeners();
63+
}
64+
}
65+
66+
String get altText => _altText;
67+
String _altText;
68+
set altText(String newAltText) {
69+
if (newAltText != _altText) {
70+
_altText = newAltText;
71+
notifyListeners();
72+
}
73+
}
74+
75+
@override
76+
String? copyContent(dynamic selection) {
77+
if (selection is! UpstreamDownstreamNodeSelection) {
78+
throw Exception('ImageNode can only copy content from a UpstreamDownstreamNodeSelection.');
79+
}
80+
81+
return !selection.isCollapsed ? _url : null;
82+
}
83+
84+
@override
85+
bool hasEquivalentContent(DocumentNode other) {
86+
return other is UrlMediaNode && url == other.url && altText == other.altText;
87+
}
88+
89+
@override
90+
bool operator ==(Object other) =>
91+
identical(this, other) ||
92+
other is UrlMediaNode &&
93+
runtimeType == other.runtimeType &&
94+
id == other.id &&
95+
_url == other._url &&
96+
_altText == other._altText;
97+
98+
@override
99+
int get hashCode => id.hashCode ^ _url.hashCode ^ _altText.hashCode;
100+
}

0 commit comments

Comments
 (0)