If you haven't yet, read introduction to Zefyr document model called Notus here.
Style attributes in Notus documents are simple key-value pairs, where
keys identify the attribute and value describes the style applied, for
instance, { "heading": 1 }
defines heading style for a line of
text with value 1
(equivalent of h1
in HTML).
Note that one attribute can describe multiple different styles depending
on the current value. E.g. attribute with key "heading" can be set to values
1
, 2
or 3
, equivalents of h1
, h2
and h3
in HTML. This prevents
a line of text from being formatted as h1
and h2
heading at the same time,
which is intentional.
Additionally, each attribute gets assigned one of two scopes. An attribute can be either inline-scoped or line-scoped, but not both. A good example of an inline-scoped attribute is "bold" attribute. Bold style can be applied to any character within a line, but not to the line itself. Similarly "heading" style is line-scoped and has effect only on the line as a whole.
Below table summarizes information about all currently supported attributes in Zefyr:
Name | Key | Scope | Type | Valid values |
---|---|---|---|---|
Bold | b |
inline |
bool |
true |
Italic | i |
inline |
bool |
true |
Link | a |
inline |
String |
Non-empty string |
Heading | heading |
line |
int |
1 , 2 and 3 |
Block | block |
line |
String |
"ul" , "ol" , "code" and "quote" |
Embed | embed |
inline |
Map |
"hr" , "image" |
Removing a specific style is as simple as setting corresponding
attribute to null
.
Here is an example of applying some styles to a document:
import 'package:notus/notus.dart';
void makeItPretty(NotusDocument document) {
/// All attributes can be accessed through [NotusAttribute] class.
// Format 5 characters starting at index 0 as bold.
document.format(0, 5, NotusAttribute.bold);
// Similarly for italic.
document.format(0, 5, NotusAttribute.italic);
// Format the first line as a heading (level 1).
// Note that there is no need to specify character range of the whole
// line. Simply set index position to anywhere within the line and
// length to 0.
document.format(0, 0, NotusAttribute.heading.level1);
// Add a link:
document.format(10, 15, NotusAttribute.link.fromString('https://github.com'));
// Format a line as code block. Similarly to heading styles there is no need
// to specify the whole character range of the line. In following example:
// whichever line is at character index 23 in the document will get
// formatted as code block.
document.format(23, 0, NotusAttribute.block.code);
// Remove heading style from the first line. All attributes
// have `unset` property which can be used the same way.
document.format(0, 0, NotusAttribute.heading.unset);
}
As mentioned previously a document delta consists of a sequence of insert
operations. Attributes (if any) are stored as metadata on each of the
operations.
There is a difference in how line and inline-scoped attributes are handled. Since Deltas are essentially a flat data structure there is nothing in the format itself to represent a line of text, which is required to allow storing line-scoped style attributes.
To solve this issue Notus document (similarly to Quill.js) reserves the
newline character (aka \n
and 0x0A
) as storage for line-scoped
styles.
Notus document model is designed to enforce this rule and prevents malformed changes from being composed into a document. For instance, an attempt to apply "bold" style to a newline character will have no effect.
Below is an example of Notus document's Delta with two lines of text.
The first line is formatted as an h1
heading and on the second line
there is bold-styled word "Flutter":
var delta = new Delta();
delta
..insert('Zefyr Editor')
..insert('\n', attributes: {'heading': 1})
..insert('A rich text editor for ');
..insert('Flutter', attributes: {'b': true});
..insert('\n');
Note that there is no block-level scope for style attributes. Again, given flat structure of Deltas there is nothing that can represent a block of lines which share the same style, e.g. bullet list. And we already reserved newline character for line styles.
As it turns out, this is not a big issue and it is possible to achieve a friendly user experience without this extra level in a document model.
The block
attribute in Notus documents is line-scoped. To change a
group of lines from "bullet list" to "number list" we need to update
block style on each of the lines individually. Zefyr editor abstracts
away such details with help of heuristic rules.