Ver código fonte

Merge pull request #157 from memspace/gitbook

Gitbook for docs
Anatoly Pulyaevskiy 5 anos atrás
pai
commit
cedaea6c5c
Nenhuma conta vinculada ao e-mail do autor do commit

+ 0
- 4
.gitbook.yaml Ver arquivo

@@ -1,4 +0,0 @@
1
-root: ./doc/
2
-
3
-structure:
4
-  readme: ./../README.md

+ 2
- 1
.gitignore Ver arquivo

@@ -1 +1,2 @@
1
-.idea/
1
+.DS_Store
2
+.idea/

+ 7
- 18
README.md Ver arquivo

@@ -1,27 +1,16 @@
1
-# Zefyr [![Build Status](https://travis-ci.com/memspace/zefyr.svg?branch=master)](https://travis-ci.com/memspace/zefyr) [![codecov](https://codecov.io/gh/memspace/zefyr/branch/master/graph/badge.svg)](https://codecov.io/gh/memspace/zefyr)
1
+# About Zefyr
2
+
3
+[![Build Status](https://travis-ci.com/memspace/zefyr.svg?branch=master)](https://travis-ci.com/memspace/zefyr) [![codecov](https://codecov.io/gh/memspace/zefyr/branch/master/graph/badge.svg)](https://codecov.io/gh/memspace/zefyr)
2 4
 
3 5
 *Soft and gentle rich text editing for Flutter applications.*
4 6
 
5 7
 Zefyr is currently in **early preview**. If you have a feature
6 8
 request or found a bug, please file it at the [issue tracker][].
7 9
 
8
-[issue tracker]: https://github.com/memspace/zefyr/issues
9
-
10
-### Documentation
10
+For questions and general discussions check out our
11
+[Spectrum community](https://spectrum.chat/zefyr).
11 12
 
12
-* [Quick Start][quick_start]
13
-* [Data Format and Document Model][data_and_document]
14
-* [Style attributes][attributes]
15
-* [Heuristic rules][heuristics]
16
-* [Images][images]
17
-* [FAQ][faq]
18
-
19
-[quick_start]: /doc/quick_start.md
20
-[data_and_document]: /doc/data_and_document.md
21
-[attributes]: /doc/attributes.md
22
-[heuristics]: /doc/heuristics.md
23
-[images]: /doc/images.md
24
-[faq]: /doc/faq.md
13
+[issue tracker]: https://github.com/memspace/zefyr/issues
25 14
 
26 15
 ## Clean and modern look
27 16
 
@@ -52,7 +41,7 @@ collaborative editing use cases or whenever there is a need for
52 41
 conflict-free resolution of changes.
53 42
 
54 43
 > Zefyr editor uses Quill.js **Delta** as underlying data format. Read
55
-> more about Zefyr and Deltas in our [documentation][data_and_document].
44
+> more about Zefyr and Deltas in our [documentation](doc/concepts/data-and-document.md).
56 45
 > Make sure to checkout [official documentation][delta] for Delta format
57 46
 > as well.
58 47
 

+ 17
- 0
SUMMARY.md Ver arquivo

@@ -0,0 +1,17 @@
1
+# Summary
2
+
3
+* [Release Notes](doc/release-notes.md)
4
+  * [Zefyr Changelog](packages/zefyr/CHANGELOG.md)
5
+  * [Notus Changelog](packages/notus/CHANGELOG.md)
6
+* [Quick Start](doc/quick-start.md)
7
+* [Concepts](doc/concepts/readme.md)
8
+  * [Data Format and Document Model](doc/concepts/data-and-document.md)
9
+  * [Attributes](doc/concepts/attributes.md)
10
+  * [Heuristic rules](doc/concepts/heuristics.md)
11
+* [Embedding Images](doc/images.md)
12
+* [FAQ](doc/faq.md)
13
+
14
+## Community
15
+
16
+* [Contribute on Github](https://github.com/memspace/zefyr)
17
+* [Discuss on Spectrum](https://spectrum.chat/zefyr)

BIN
assets/quick-start-rec-01.gif Ver arquivo


BIN
assets/quick-start-rec-02.gif Ver arquivo


BIN
assets/quick-start-rec-03.gif Ver arquivo


BIN
assets/quick-start-screen-01.png Ver arquivo


BIN
assets/quick-start-screen-02.png Ver arquivo


doc/attributes.md → doc/concepts/attributes.md Ver arquivo

@@ -1,7 +1,7 @@
1 1
 ## Style Attributes
2 2
 
3 3
 > If you haven't yet, read introduction to Zefyr document model called
4
-> Notus [here][data_and_document];
4
+> Notus [here](data-and-document.md).
5 5
 
6 6
 Style attributes in Notus documents are simple key-value pairs, where
7 7
 keys identify the attribute and value describes the style applied, for
@@ -116,16 +116,4 @@ a friendly user experience without this extra level in a document model.
116 116
 The `block` attribute in Notus documents is line-scoped. To change a
117 117
 group of lines from "bullet list" to "number list" we need to update
118 118
 block style on each of the lines individually. Zefyr editor abstracts
119
-away such details with help of [heuristic rules][heuristics].
120
-
121
-### Next up
122
-
123
-* [Heuristics][heuristics]
124
-
125
-[heuristics]: /doc/heuristics.md
126
-
127
-### Previous
128
-
129
-* [Data Format and Document Model][data_and_document]
130
-
131
-[data_and_document]: /doc/data_and_document.md
119
+away such details with help of [heuristic rules](heuristics.md).

doc/data_and_document.md → doc/concepts/data-and-document.md Ver arquivo

@@ -18,7 +18,7 @@ essentially JSON, and is human readable.
18 18
 [Delta]: https://quilljs.com/docs/delta/
19 19
 [ot]: https://en.wikipedia.org/wiki/Operational_transformation
20 20
 [github-delta]: https://github.com/quilljs/delta
21
-[pub-delta]: https://pub.dartlang.com/packages/quill_delta
21
+[pub-delta]: https://pub.dev/packages/quill_delta
22 22
 
23 23
 ### Deltas quick start
24 24
 
@@ -91,13 +91,9 @@ for more details.
91 91
 Notus documents are represented as a tree of nodes. There are 3 main
92 92
 types of nodes:
93 93
 
94
-* `LeafNode` - a leaf node which represents a segment of styled text
95
-  within a document. There are two kinds of leaf nodes - text and
96
-  embeds.
97
-* `LineNode` - represents an individual line of text within a document.
98
-  Line nodes are containers for leaf nodes.
99
-* `Block` - represents a group of adjacent lines which share the same
100
-  style. Examples of blocks include lists, quotes or code blocks.
94
+* `LeafNode` - a leaf node which represents a segment of styled text within a document. There are two kinds of leaf nodes - text and embeds.
95
+* `LineNode` - represents an individual line of text within a document. Line nodes are containers for leaf nodes.
96
+* `Block` - represents a group of adjacent lines which share the same style. Examples of blocks include lists, quotes or code blocks.
101 97
 
102 98
 Given above description, here is ASCII-style visualization of a Notus
103 99
 document tree:
@@ -128,13 +124,5 @@ fairly simple and predictable.
128 124
 Learn more about other building blocks of Notus documents in
129 125
 documentation for [attributes][] and [heuristics][].
130 126
 
131
-[heuristics]: /doc/heuristics.md
132
-[attributes]: /doc/attributes.md
133
-
134
-### Next
135
-
136
-* [Attributes](/doc/attributes.md)
137
-
138
-### Previous
139
-
140
-* [Usage](/doc/usage.md)
127
+[heuristics]: heuristics.md
128
+[attributes]: attributes.md

doc/heuristics.md → doc/concepts/heuristics.md Ver arquivo

@@ -11,15 +11,13 @@ a new list item when user presses `Enter` key.
11 11
 In Notus (document model used by Zefyr editor), such rules are called
12 12
 *heuristic rules*. There are two main purposes for heuristic rules:
13 13
 
14
-1. User experience: rules like above-mentioned autoformatting of links
15
-   are here to make editing a user friendly process.
16
-2. Semantics preservation: this is mostly invisible for the user but
17
-  is very important nevertheless. There is a set of rules to make sure
18
-  that a document change conforms to the data format and model
19
-  semantics.
14
+1. User experience: rules like above-mentioned autoformatting of links are here to make editing a user friendly process.
15
+2. Semantics preservation: this is mostly invisible for the user but is very important nevertheless. There is a set of rules to make sure that a document change conforms to the data format and model semantics.
20 16
 
21 17
 Let's cover the second item in more detail.
22 18
 
19
+### Example heuristic rule
20
+
23 21
 Say, a user is editing following document (cursor position is indicated
24 22
 by pipe `|` character):
25 23
 
@@ -39,7 +37,8 @@ var change = doc.format(
39 37
 ```
40 38
 
41 39
 If we try to apply this change as-is it would have no effect or, more
42
-likely, result in an `AssertionError`. This is why all methods in
40
+likely, result in an `AssertionError` because we are trying to apply line style
41
+to a character in the middle of a line. This is why all methods in
43 42
 `NotusDocument` have an extra step which applies heuristic rules to
44 43
 the change (there is one method which skips this step, `compose`,
45 44
 read more on it later) before actually composing it.
@@ -62,40 +61,21 @@ what user intended to do.
62 61
 There are more similar scenarios which are covered by heuristic rules
63 62
 to ensure consistency with the document model and provide better UX.
64 63
 
65
-### `NotusDocument.compose()` and skipping heuristic rules.
64
+### `NotusDocument.compose()` and skipping heuristic rules
66 65
 
67 66
 The `compose()` method is the only method which skips the step of
68 67
 applying heuristic rules and therefore **should be used with great
69 68
 care** as it can result in corrupted document state.
70 69
 
71
-Use this method when you sure that the change you are about to compose
70
+Use this method when you are sure that the change you are about to compose
72 71
 conforms to the document model and data format semantics.
73 72
 
74 73
 This method exists mostly to enable following use cases:
75 74
 
76
-* **Collaborative editing**, when a change came from a different site and
77
-  has already been normalized by heuristic rules on that site. Care must
78
-  be taken to ensure that this change is based on the same revision
79
-  of the document, and if not, transformed against any local changes
80
-  before composing.
81
-* **Change history and revisioning**, when a change came from a revision
82
-  history stored on a server or a database. Similarly, care must be
83
-  taken to transform the change against any local (uncommitted) changes
84
-  before composing.
75
+* **Collaborative editing**, when a change came from a different site and has already been normalized by heuristic rules on that site. Care must be taken to ensure that this change is based on the same revision of the document, and if not, transformed against any local changes before composing.
76
+* **Change history and revisioning**, when a change came from a revision history stored on a server or a database. Similarly, care must be taken to transform the change against any local (uncommitted) changes before composing.
85 77
 
86 78
 When composing a change which came from a different site or server make
87 79
 sure to use `ChangeSource.remote` when calling `compose()`. This allows
88 80
 you to distinguish such changes from local changes made by the user
89 81
 when listening on `NotusDocument.changes` stream.
90
-
91
-### Next up
92
-
93
-* [Images][images]
94
-
95
-[images]: /doc/images.md
96
-
97
-### Previous
98
-
99
-* [Style attributes][attributes]
100
-
101
-[attributes]: /doc/attributes.md

+ 1
- 1
doc/faq.md Ver arquivo

@@ -1,6 +1,6 @@
1 1
 ## Frequently asked questions
2 2
 
3
-### Are Notus documents compatible with Quill documents?
3
+### Q: Are Notus documents compatible with Quill documents?
4 4
 
5 5
 Short answer is no. Even though Notus uses Quill Delta as underlying
6 6
 representation for its documents there are at least differences in

+ 3
- 6
doc/images.md Ver arquivo

@@ -1,4 +1,4 @@
1
-## Images
1
+## Embedding Images
2 2
 
3 3
 > Note that Image API is considered experimental and is likely to be
4 4
 > changed in backward incompatible ways. If this happens all changes will be
@@ -167,8 +167,5 @@ class MyAppPageState extends State<MyAppPage> {
167 167
 }
168 168
 ```
169 169
 
170
-### Previous
171
-
172
-* [Heuristics][heuristics]
173
-
174
-[heuristics]: /doc/heuristics.md
170
+When `imageDelegate` field is set to non-null value it automatically enables
171
+image selection button in Zefyr's style toolbar.

+ 340
- 0
doc/quick-start.md Ver arquivo

@@ -0,0 +1,340 @@
1
+## Quick Start
2
+
3
+In this tutorial you'll create a simple Flutter app that supports rich text
4
+editing with Zefyr. What you'll learn:
5
+
6
+* How to create a new screen for the editor
7
+* Basic widget layout required by Zefyr
8
+* How to load and save documents using JSON serialization
9
+
10
+### 01. Create a new Flutter project
11
+
12
+If you haven't installed Flutter yet then [install it first](https://flutter.dev/docs/get-started/install).
13
+
14
+Create a new project using Terminal and `flutter create` command:
15
+
16
+```shell
17
+$ flutter create myapp
18
+$ cd myapp
19
+```
20
+
21
+For more methods of creating a project see [official documentation](https://flutter.dev/docs/get-started/test-drive).
22
+
23
+### 02. Add Zefyr to your project
24
+
25
+Add `zefyr` package as a dependency to `pubspec.yaml` of your new project:
26
+
27
+```yaml
28
+dependencies:
29
+  zefyr: [latest_version]
30
+```
31
+
32
+And run `flutter packages get`.
33
+This installs [zefyr](https://pub.dev/packages/zefyr) and all required
34
+dependencies, including [notus](https://pub.dev/packages/notus) package which
35
+implements Zefyr's document model.
36
+
37
+> Notus package is platform-agnostic and can be used outside of Flutter apps
38
+> (in web or server-side Dart projects).
39
+
40
+### 03. Create editor page
41
+
42
+We start by creating a `StatefulWidget` that will be responsible for handling
43
+all the state and interactions with Zefyr. In this example we'll assume
44
+that there is dedicated editor page in our app.
45
+
46
+Create a new file `lib/src/editor_page.dart` and type in (or paste) the
47
+following:
48
+
49
+```dart
50
+import 'package:flutter/material.dart';
51
+import 'package:quill_delta/quill_delta.dart';
52
+import 'package:zefyr/zefyr.dart';
53
+
54
+class EditorPage extends StatefulWidget {
55
+  @override
56
+  EditorPageState createState() => EditorPageState();
57
+}
58
+
59
+class EditorPageState extends State<EditorPage> {
60
+  /// Allows to control the editor and the document.
61
+  ZefyrController _controller;
62
+
63
+  /// Zefyr editor like any other input field requires a focus node.
64
+  FocusNode _focusNode;
65
+
66
+  @override
67
+  void initState() {
68
+    super.initState();
69
+    // Here we must load the document and pass it to Zefyr controller.
70
+    final document = _loadDocument();
71
+    _controller = ZefyrController(document);
72
+    _focusNode = FocusNode();
73
+  }
74
+
75
+  @override
76
+  Widget build(BuildContext context) {
77
+    // Note that the editor requires special `ZefyrScaffold` widget to be
78
+    // one of its parents.
79
+    return Scaffold(
80
+      appBar: AppBar(title: Text("Editor page")),
81
+      body: ZefyrScaffold(
82
+        child: ZefyrEditor(
83
+          padding: EdgeInsets.all(16),
84
+          controller: _controller,
85
+          focusNode: _focusNode,
86
+        ),
87
+      ),
88
+    );
89
+  }
90
+
91
+  /// Loads the document to be edited in Zefyr.
92
+  NotusDocument _loadDocument() {
93
+    // For simplicity we hardcode a simple document with one line of text
94
+    // saying "Zefyr Quick Start".
95
+    // (Note that delta must always end with newline.)
96
+    final Delta delta = Delta()..insert("Zefyr Quick Start\n");
97
+    return NotusDocument.fromDelta(delta);
98
+  }
99
+}
100
+```
101
+
102
+Above example widget creates a page with an `AppBar` and Zefyr editor in its
103
+body. We also initialize our editor with a simple one-line document.
104
+
105
+Now we need to wire it up with our app. Open `lib/main.dart` and replace
106
+autogenerated contents with this:
107
+
108
+```dart
109
+import 'package:flutter/material.dart';
110
+
111
+import 'src/editor_page.dart';
112
+
113
+void main() {
114
+  runApp(QuickStartApp());
115
+}
116
+
117
+class QuickStartApp extends StatelessWidget {
118
+  @override
119
+  Widget build(BuildContext context) {
120
+    return MaterialApp(
121
+      title: 'Quick Start',
122
+      home: HomePage(),
123
+      routes: {
124
+        "/editor": (context) => EditorPage(),
125
+      },
126
+    );
127
+  }
128
+}
129
+
130
+class HomePage extends StatelessWidget {
131
+  @override
132
+  Widget build(BuildContext context) {
133
+    final navigator = Navigator.of(context);
134
+    return Scaffold(
135
+      appBar: AppBar(title: Text("Quick Start")),
136
+      body: Center(
137
+        child: FlatButton(
138
+          child: Text("Open editor"),
139
+          onPressed: () => navigator.pushNamed("/editor"),
140
+        ),
141
+      ),
142
+    );
143
+  }
144
+}
145
+```
146
+
147
+Here is how it might look when we run the app and navigate to editor page:
148
+
149
+<img src="https://github.com/memspace/zefyr/raw/gitbook/assets/quick-start-rec-01.gif" width="600">
150
+
151
+### 04. Save document to JSON file
152
+
153
+At this point we can already edit the document and apply styles, however if
154
+we navigate back from this page our changes will be lost. Let's fix this and
155
+add a button which saves the document to the device's file system.
156
+
157
+First we need a function to save the document. Update `lib/src/editor_page.dart`
158
+as follows:
159
+
160
+```dart
161
+// change: add these two lines to imports section at the top of the file
162
+import 'dart:convert'; // access to jsonEncode()
163
+import 'dart:io'; // access to File and Directory classes
164
+
165
+class EditorPageState extends State<EditorPage> {
166
+
167
+  // change: add after _loadDocument()
168
+
169
+  void _saveDocument(BuildContext context) {
170
+    // Notus documents can be easily serialized to JSON by passing to
171
+    // `jsonEncode` directly
172
+    final contents = jsonEncode(_controller.document);
173
+    // For this example we save our document to a temporary file.
174
+    final file = File(Directory.systemTemp.path + "/quick_start.json");
175
+    // And show a snack bar on success.
176
+    file.writeAsString(contents).then((_) {
177
+      Scaffold.of(context).showSnackBar(SnackBar(content: Text("Saved.")));
178
+    });
179
+  }
180
+}
181
+```
182
+
183
+This function converts our document using `jsonEncode()` function and writes
184
+the result to a file `quick_start.json` in the system's temporary directory.
185
+
186
+Note that `File.writeAsString` is an asynchronous method and returns Dart's
187
+`Future`. This is why we register a completion callback with a call to
188
+`Future.then`.
189
+
190
+One more important bit here is that we pass `BuildContext` argument to
191
+`_saveDocument`. This is required to get access to our page's `Scaffold` state,
192
+so that we can show a `SnackBar`.
193
+
194
+Now we just need to add a button to the AppBar, so we need to modify `build`
195
+method as follows:
196
+
197
+```dart
198
+class EditorPageState extends State<EditorPage> {
199
+
200
+  // change: replace build() method with following
201
+
202
+  @override
203
+  Widget build(BuildContext context) {
204
+    // Note that the editor requires special `ZefyrScaffold` widget to be
205
+    // present somewhere up the widget tree.
206
+    return Scaffold(
207
+      appBar: AppBar(
208
+        title: Text("Editor page"),
209
+        // <<< begin change
210
+        actions: <Widget>[
211
+          Builder(
212
+            builder: (context) => IconButton(
213
+              icon: Icon(Icons.save),
214
+              onPressed: () => _saveDocument(context),
215
+            ),
216
+          )
217
+        ],
218
+        // end change >>>
219
+      ),
220
+      body: ZefyrScaffold(
221
+        child: ZefyrEditor(
222
+          padding: EdgeInsets.all(16),
223
+          controller: _controller,
224
+          focusNode: _focusNode,
225
+        ),
226
+      ),
227
+    );
228
+  }
229
+}
230
+```
231
+
232
+We have to use `Builder` here for our icon button because we need `BuildContext`
233
+which has access to `Scaffold` widget's state.
234
+
235
+Now we can reload our app, hit "Save" button and see the snack bar.
236
+
237
+<img src="https://github.com/memspace/zefyr/raw/gitbook/assets/quick-start-rec-02.gif" width="600">
238
+
239
+### 05. Load document from JSON file
240
+
241
+Since we now have this document saved to a file, let's update our
242
+`_loadDocument` method to load saved file if it exists.
243
+
244
+```dart
245
+class EditorPageState extends State<EditorPage> {
246
+
247
+  // change: replace _loadDocument() method with following
248
+
249
+  /// Loads the document asynchronously from a file if it exists, otherwise
250
+  /// returns default document.
251
+  Future<NotusDocument> _loadDocument() async {
252
+    final file = File(Directory.systemTemp.path + "/quick_start.json");
253
+    if (await file.exists()) {
254
+      final contents = await file.readAsString();
255
+      return NotusDocument.fromJson(jsonDecode(contents));
256
+    }
257
+    final Delta delta = Delta()..insert("Zefyr Quick Start\n");
258
+    return NotusDocument.fromDelta(delta);
259
+  }
260
+}
261
+```
262
+
263
+We had to convert this method to be __async__ because file system operations
264
+are asynchronous. This breaks our `initState` logic so we need to fix it next.
265
+However we can no longer initialize `ZefyrController` in `initState` and
266
+therefore can't display the editor until document is loaded.
267
+
268
+One way to fix this is to show loader animation while we are reading our
269
+document from file. But first, we still need to update `initState` method:
270
+
271
+```dart
272
+class EditorPageState extends State<EditorPage> {
273
+
274
+  // change: replace initState() method with following
275
+
276
+  @override
277
+  void initState() {
278
+    super.initState();
279
+    _focusNode = FocusNode();
280
+    _loadDocument().then((document) {
281
+      setState(() {
282
+        _controller = ZefyrController(document);
283
+      });
284
+    });
285
+  }
286
+}
287
+```
288
+
289
+We initialize `_controller` only when our document is fully loaded from the file
290
+system. An important part here is to update `_controller` field inside of
291
+`setState` call as required by Flutter's `StatefulWidget`'s contract.
292
+
293
+The only thing left is to update `build()` method to show loader animation:
294
+
295
+```dart
296
+class EditorPageState extends State<EditorPage> {
297
+
298
+  // change: replace build() method with following
299
+
300
+  @override
301
+  Widget build(BuildContext context) {
302
+    // If _controller is null we show Material Design loader, otherwise
303
+    // display Zefyr editor.
304
+    final Widget body = (_controller == null)
305
+        ? Center(child: CircularProgressIndicator())
306
+        : ZefyrScaffold(
307
+            child: ZefyrEditor(
308
+              padding: EdgeInsets.all(16),
309
+              controller: _controller,
310
+              focusNode: _focusNode,
311
+            ),
312
+          );
313
+
314
+    return Scaffold(
315
+      appBar: AppBar(
316
+        title: Text("Editor page"),
317
+        actions: <Widget>[
318
+          Builder(
319
+            builder: (context) => IconButton(
320
+              icon: Icon(Icons.save),
321
+              onPressed: () => _saveDocument(context),
322
+            ),
323
+          )
324
+        ],
325
+      ),
326
+      body: body,
327
+    );
328
+  }
329
+}
330
+```
331
+
332
+If we save changes now and reload the app we should see something like this:
333
+
334
+<img src="https://github.com/memspace/zefyr/raw/gitbook/assets/quick-start-rec-03.gif" width="600">
335
+
336
+Note that in your tests you'll likely not notice any loading animation at all.
337
+This is because reading a tiny file from disk is too fast. For the above
338
+recording we added an artificial delay of 1 second in order to demonstrate
339
+loading. If you'd like to replicate this, we'll leave implementation of this
340
+task to you as an exercise.

+ 0
- 83
doc/quick_start.md Ver arquivo

@@ -1,83 +0,0 @@
1
-## Quick Start
2
-
3
-Zefyr project is split in two main packages:
4
-
5
-1. `zefyr` - Flutter package which provides the UI part
6
-2. `notus` - platform-agnostic package which provides document model
7
-   used by `zefyr` package.
8
-
9
-### Installation
10
-
11
-Add `zefyr` package as a dependency to your `pubspec.yaml`:
12
-
13
-```yaml
14
-dependencies:
15
-  zefyr: [latest_version]
16
-```
17
-
18
-And run `flutter packages get` to install. This installs both `zefyr`
19
-and `notus` packages.
20
-
21
-### Usage
22
-
23
-There are 4 main objects you would normally interact with in your code:
24
-
25
-* `NotusDocument`, represents a rich text document and provides
26
-  high-level methods for manipulating the document's state, like
27
-  inserting, deleting and formatting of text.
28
-  Read [documentation][data_and_docs] for more details on Notus
29
-  document model and data format.
30
-* `ZefyrEditor`, a Flutter widget responsible for rendering of rich text
31
-  on the screen and reacting to user actions.
32
-* `ZefyrController`, ties the above two objects together.
33
-* `ZefyrScaffold`, allows embedding Zefyr toolbar into any custom layout.
34
-
35
-`ZefyrEditor` depends on presence of `ZefyrScaffold` somewhere up the widget tree.
36
-
37
-Normally you would need to place `ZefyrEditor` inside of a
38
-`StatefulWidget`. Shown below is a minimal setup required to use the
39
-editor:
40
-
41
-```dart
42
-import 'package:flutter/material.dart';
43
-import 'package:zefyr/zefyr.dart';
44
-
45
-class MyWidget extends StatefulWidget {
46
-  @override
47
-  MyWidgetState createState() => MyWidgetState();
48
-}
49
-
50
-class MyWidgetState extends State<MyWidget> {
51
-  ZefyrController _controller;
52
-  FocusNode _focusNode;
53
-
54
-  @override
55
-  void initState() {
56
-    super.initState();
57
-    // Create an empty document or load existing if you have one.
58
-    // Here we create an empty document:
59
-    final document = new NotusDocument();
60
-    _controller = new ZefyrController(document);
61
-    _focusNode = new FocusNode();
62
-  }
63
-
64
-  @override
65
-  Widget build(BuildContext context) {
66
-    return ZefyrScaffold(
67
-      child: ZefyrEditor(
68
-        controller: _controller,
69
-        focusNode: _focusNode,
70
-      ),
71
-    );
72
-  }
73
-}
74
-```
75
-
76
-In following sections you will learn more about document
77
-model, Deltas, attributes and other aspects of the editor.
78
-
79
-### Next
80
-
81
-* [Data Format and Document Model][data_and_docs]
82
-
83
-[data_and_docs]: /doc/data_and_document.md

+ 27
- 0
doc/release-notes.md Ver arquivo

@@ -0,0 +1,27 @@
1
+# Release notes
2
+
3
+Current version of Zefyr editor is `0.7` ([changelog](./../packages/zefyr/CHANGELOG.md)).
4
+
5
+### 0.7
6
+
7
+__This is a breaking change release.__
8
+
9
+This version introduces first set of changes aimed at addressing some
10
+common pain points reported by users of this library. In addition this release
11
+is the first step towards making Zefyr easier to extend and customize.
12
+
13
+This version of Zefyr also comes with revamped documentation website and
14
+some of the articles completely rewritten to help new users to get started. More
15
+articles and tutorials will be added in future releases.
16
+
17
+Highlights of this release include:
18
+
19
+* Zefyr no longer depends on `image_picker` package. This introduced breaking
20
+  changes described in the changelog.
21
+* Selection overlay has been refactored to enable customization work in
22
+  follow up releases.
23
+* Zefyr now supports selection-only workflow via new `ZefyrMode` object which
24
+  replaces previous `enabled` field (breaking change).
25
+
26
+For more details on breaking changes and upgrade instructions please read
27
+[changelog](./../packages/zefyr/CHANGELOG.md).

+ 7
- 7
packages/notus/CHANGELOG.md Ver arquivo

@@ -1,18 +1,18 @@
1 1
 ## 0.1.3
2 2
 
3
-- Fixed handling of user input around embeds.
4
-- Added new heuristic rule to preserve block style on paste
3
+* Fixed handling of user input around embeds
4
+* Added new heuristic rule to preserve block style on paste
5 5
 
6 6
 ## 0.1.2
7 7
 
8
-* Upgraded dependency on quiver_hashcode to 2.0.0.
8
+* Upgraded dependency on quiver_hashcode to 2.0.0
9 9
 
10 10
 ## 0.1.1
11 11
 
12
-* Added `meta` package to dependencies.
13
-* Fixed analysis warnings.
14
-* Added example.
12
+* Added `meta` package to dependencies
13
+* Fixed analysis warnings
14
+* Added example
15 15
 
16 16
 ## 0.1.0
17 17
 
18
-*  Initial release.
18
+*  Initial release

+ 15
- 34
packages/zefyr/CHANGELOG.md Ver arquivo

@@ -2,22 +2,14 @@
2 2
 
3 3
 This release contains breaking changes.
4 4
 
5
-* Breaking change: `ZefyrEditor.enabled` field replaced by `ZefyrEditor.mode` which can take
6
-  one of three default values:
5
+* Breaking change: `ZefyrEditor.enabled` field replaced by `ZefyrEditor.mode` which can take one of three default values:
7 6
     - `ZefyrMode.edit`: the same as `enabled: true`, all editing controls are available to the user
8
-    - `ZefyrMode.select`: user can't modify text itself, but allowed to select it and optionally
9
-       apply formatting.
7
+    - `ZefyrMode.select`: user can't modify text itself, but allowed to select it and optionally apply formatting.
10 8
     - `ZefyrMode.view`: the same as `enabled: false`, read-only.
11
-* Added optional `selectionControls` field to `ZefyrEditor` and `ZefyrEditableText`. If not provided
12
-  then by default uses platform-specific implementation.
9
+* Added optional `selectionControls` field to `ZefyrEditor` and `ZefyrEditableText`. If not provided then by default uses platform-specific implementation.
13 10
 * Added support for "selectAll" action in selection toolbar.
14
-* Breaking change: removed `ZefyrDefaultImageDelegate` as well as dependency on
15
-  `image_picker` plugin. Users are required to provide their own implementation. If image delegate
16
-  is not provided then image toolbar button is disabled.
17
-* Breaking change: added `ZefyrImageDelegate.cameraSource` and `ZefyrImageDelegate.gallerySource`
18
-  fields. For users of `image_picker` plugin these should return `ImageSource.camera` and
19
-  `ImageSource.gallery` respectively. See documentation on implementing image support for more
20
-  details.
11
+* Breaking change: removed `ZefyrDefaultImageDelegate` as well as dependency on `image_picker` plugin. Users are required to provide their own implementation. If image delegate is not provided then image toolbar button is disabled.
12
+* Breaking change: added `ZefyrImageDelegate.cameraSource` and `ZefyrImageDelegate.gallerySource` fields. For users of `image_picker` plugin these should return `ImageSource.camera` and `ImageSource.gallery` respectively. See documentation on implementing image support for more details.
21 13
 
22 14
 ## 0.6.1
23 15
 
@@ -32,22 +24,15 @@ This release contains breaking changes.
32 24
 ## 0.5.0
33 25
 
34 26
 * Updated to support Flutter 1.2
35
-* Experimental: Added non-scrollable `ZefyrView` widget which allows previewing Notus documents
36
-  inside layouts using their own scrollables like ListView.
37
-* Breaking change: renamed `EditableRichText` to `ZefyrRichText`. User code is unlikely to be
38
-  affected unless you've extended Zefyr with custom implementations of block widgets.
39
-* Breaking change: renamed `RenderEditableParagraph` to `RenderZefyrParagraph`. User code is
40
-  unlikely to be affected unless you've extended Zefyr with custom implementations of block widgets.
41
-* Added `ZefyrScope` class - replaces previously used scope objects `ZefyrEditableTextScope` and
42
-  `ZefyrEditorScope`. Unified all shared resources under one class.
43
-* Breaking change: removed `ZefyrEditor.of` and `ZefyrEditableText.of` static methods.
44
-  Use `ZefyrScope.of` instead.
27
+* Experimental: Added non-scrollable `ZefyrView` widget which allows previewing Notus documents inside layouts using their own scrollables like ListView.
28
+* Breaking change: renamed `EditableRichText` to `ZefyrRichText`. User code is unlikely to be affected unless you've extended Zefyr with custom implementations of block widgets.
29
+* Breaking change: renamed `RenderEditableParagraph` to `RenderZefyrParagraph`. User code is unlikely to be affected unless you've extended Zefyr with custom implementations of block widgets.
30
+* Added `ZefyrScope` class - replaces previously used scope objects `ZefyrEditableTextScope` and `ZefyrEditorScope`. Unified all shared resources under one class.
31
+* Breaking change: removed `ZefyrEditor.of` and `ZefyrEditableText.of` static methods. Use `ZefyrScope.of` instead.
45 32
 
46 33
 ## 0.4.0
47 34
 
48
-* Breaking change: upgraded `image_picker` to `^0.5.0` and `url_launcher` to `^5.0.0` which
49
-  requires migration to Android X. You must migrate your app in order to use this version.
50
-  For details on how to migrate see:
35
+* Breaking change: upgraded `image_picker` to `^0.5.0` and `url_launcher` to `^5.0.0` which requires migration to Android X. You must migrate your app in order to use this version. For details on how to migrate see:
51 36
   - https://flutter.io/docs/development/packages-and-plugins/androidx-compatibility
52 37
   - https://developer.android.com/jetpack/androidx/migrate
53 38
 
@@ -58,11 +43,9 @@ This release contains breaking changes.
58 43
 
59 44
 ## 0.3.0
60 45
 
61
-This version introduces new widget `ZefyrScaffold` which allows embedding Zefyr in custom
62
-layouts, like forms with multiple input fields.
46
+This version introduces new widget `ZefyrScaffold` which allows embedding Zefyr in custom layouts, like forms with multiple input fields.
63 47
 
64
-It is now required to always wrap `ZefyrEditor` with an instance of this new widget. See examples
65
-and readme for more details.
48
+It is now required to always wrap `ZefyrEditor` with an instance of this new widget. See examples and readme for more details.
66 49
 
67 50
 There is also new `ZefyrField` widget which integrates Zefyr with material design decorations.
68 51
 
@@ -73,8 +56,7 @@ There is also new `ZefyrField` widget which integrates Zefyr with material desig
73 56
 
74 57
 ## 0.2.0
75 58
 
76
-* Breaking change: `ZefyrImageDelegate.createImageProvider` replaced with
77
-  `ZefyrImageDelegate.buildImage`.
59
+* Breaking change: `ZefyrImageDelegate.createImageProvider` replaced with `ZefyrImageDelegate.buildImage`.
78 60
 * Fixed redundant updates on composing range for Android.
79 61
 * Added TextCapitalization.sentences
80 62
 * Added docs for embedding images.
@@ -82,8 +64,7 @@ There is also new `ZefyrField` widget which integrates Zefyr with material desig
82 64
 ## 0.1.2
83 65
 
84 66
 * Fixed analysis warnings.
85
-* UX: User taps on padding area around the editor and in empty space inside it now look for the nearest
86
-  paragraph to move caret to.
67
+* UX: User taps on padding area around the editor and in empty space inside it now look for the nearest paragraph to move caret to.
87 68
 * UX: Toggle selection toolbar on double tap instead of refreshing it.
88 69
 
89 70
 ## 0.1.1

+ 2
- 18
packages/zefyr/README.md Ver arquivo

@@ -15,8 +15,7 @@ For documentation see [https://github.com/memspace/zefyr](https://github.com/mem
15 15
 
16 16
 Official releases of Zefyr can be installed from Dart's Pub package repository.
17 17
 
18
-> Note that versions from Pub track stable channel of Flutter. If you are on master channel
19
-> check out instructions below in this document.
18
+> Note that versions from Pub track stable channel of Flutter.
20 19
 
21 20
 
22 21
 To install Zefyr from Pub add `zefyr` package as a dependency to your `pubspec.yaml`:
@@ -28,20 +27,5 @@ dependencies:
28 27
 
29 28
 And run `flutter packages get`.
30 29
 
31
-#### Installing version of Zefyr compatible with master channel of Flutter.
32
-
33
-You need to add git dependency to your pubspec.yaml that points to `flutter-master` branch:
34
-
35
-```yaml
36
-dependencies:
37
-  zefyr:
38
-    git:
39
-      url: https://github.com/memspace/zefyr.git
40
-      ref: flutter-master
41
-      path: packages/zefyr
42
-```
43
-
44
-And run `flutter packages get`.
45
-
46
-Continue to [https://github.com/memspace/zefyr/blob/master/doc/quick_start.md](documentation) to
30
+Continue to [https://github.com/memspace/zefyr/blob/master/doc/concepts/quick_start.md](documentation) to
47 31
 learn more about Zefyr and how to use it in your projects.

+ 39
- 0
packages/zefyr/example/lib/quick_start.dart Ver arquivo

@@ -0,0 +1,39 @@
1
+// Copyright (c) 2018, the Zefyr project authors.  Please see the AUTHORS file
2
+// for details. All rights reserved. Use of this source code is governed by a
3
+// BSD-style license that can be found in the LICENSE file.
4
+import 'package:flutter/material.dart';
5
+
6
+import 'src/editor_page.dart';
7
+
8
+void main() {
9
+  runApp(QuickStartApp());
10
+}
11
+
12
+class QuickStartApp extends StatelessWidget {
13
+  @override
14
+  Widget build(BuildContext context) {
15
+    return MaterialApp(
16
+      title: 'Quick Start',
17
+      home: HomePage(),
18
+      routes: {
19
+        "/editor": (context) => EditorPage(),
20
+      },
21
+    );
22
+  }
23
+}
24
+
25
+class HomePage extends StatelessWidget {
26
+  @override
27
+  Widget build(BuildContext context) {
28
+    final navigator = Navigator.of(context);
29
+    return Scaffold(
30
+      appBar: AppBar(title: Text("Quick Start")),
31
+      body: Center(
32
+        child: FlatButton(
33
+          child: Text("Open editor"),
34
+          onPressed: () => navigator.pushNamed("/editor"),
35
+        ),
36
+      ),
37
+    );
38
+  }
39
+}

+ 84
- 0
packages/zefyr/example/lib/src/editor_page.dart Ver arquivo

@@ -0,0 +1,84 @@
1
+import 'dart:convert';
2
+import 'dart:io';
3
+
4
+import 'package:flutter/material.dart';
5
+import 'package:quill_delta/quill_delta.dart';
6
+import 'package:zefyr/zefyr.dart';
7
+
8
+class EditorPage extends StatefulWidget {
9
+  @override
10
+  EditorPageState createState() => EditorPageState();
11
+}
12
+
13
+class EditorPageState extends State<EditorPage> {
14
+  /// Allows to control the editor and the document.
15
+  ZefyrController _controller;
16
+
17
+  /// Zefyr editor like any other input field requires a focus node.
18
+  FocusNode _focusNode;
19
+
20
+  @override
21
+  void initState() {
22
+    super.initState();
23
+    _focusNode = new FocusNode();
24
+    _loadDocument().then((document) {
25
+      setState(() {
26
+        _controller = new ZefyrController(document);
27
+      });
28
+    });
29
+  }
30
+
31
+  @override
32
+  Widget build(BuildContext context) {
33
+    final Widget body = (_controller == null)
34
+        ? Center(child: CircularProgressIndicator())
35
+        : ZefyrScaffold(
36
+            child: ZefyrEditor(
37
+              padding: EdgeInsets.all(16),
38
+              controller: _controller,
39
+              focusNode: _focusNode,
40
+            ),
41
+          );
42
+
43
+    return Scaffold(
44
+      appBar: AppBar(
45
+        title: Text("Editor page"),
46
+        actions: <Widget>[
47
+          Builder(
48
+            builder: (context) => IconButton(
49
+              icon: Icon(Icons.save),
50
+              onPressed: () => _saveDocument(context),
51
+            ),
52
+          )
53
+        ],
54
+      ),
55
+      body: body,
56
+    );
57
+  }
58
+
59
+  /// Loads the document asynchronously from a file if it exists, otherwise
60
+  /// returns default document.
61
+  Future<NotusDocument> _loadDocument() async {
62
+    final file = File(Directory.systemTemp.path + "/quick_start.json");
63
+    if (await file.exists()) {
64
+      final contents = await file
65
+          .readAsString()
66
+          .then((data) => Future.delayed(Duration(seconds: 1), () => data));
67
+      return NotusDocument.fromJson(jsonDecode(contents));
68
+    }
69
+    final Delta delta = Delta()..insert("Zefyr Quick Start\n");
70
+    return NotusDocument.fromDelta(delta);
71
+  }
72
+
73
+  void _saveDocument(BuildContext context) {
74
+    // Notus documents can be easily serialized to JSON by passing to
75
+    // `jsonEncode` directly:
76
+    final contents = jsonEncode(_controller.document);
77
+    // For this example we save our document to a temporary file.
78
+    final file = File(Directory.systemTemp.path + "/quick_start.json");
79
+    // And show a snack bar on success.
80
+    file.writeAsString(contents).then((_) {
81
+      Scaffold.of(context).showSnackBar(SnackBar(content: Text("Saved.")));
82
+    });
83
+  }
84
+}