howdy/ui/rich_text/document
A rich text document: what howdy/ui/rich_text edits, as Gleam values.
The editor posts its document as JSON. Decoding that JSON is also the
safety check: anything that is not a paragraph, heading, quote, list,
code block, divider, mention or attachment, or a link that is not
http, https, mailto, tel or a path on your site, is refused.
Store the document with to_string, and show it with view, which
renders through Lustre, so its text is always escaped.
use body <- result.try(document.decode(form.value(fields, "body")))
use person <- list.each(document.mentions(body))
// Check `person.id` is someone this user may mention, then tell them.
save(document.to_string(body))
// Later, drawing mentions from what is true now, not when it was saved:
document.view_with(
body,
mention: fn(mention) {
document.mention_view(mention, href: Some("/people/" <> mention.id))
},
attachment: fn(file) {
document.attachment_view(file, url: Some(files.url(file.key)))
},
)
Mentions and attachments keep a label, a name and a size from the moment they were added. Like Rails’ Action Text, render them from your own records when you show the document, so a renamed person or a deleted file shows as it is now.
Types
A file stored under key, as your upload endpoint named it. The name,
type and size are what the browser said when it was added.
pub type Attachment {
Attachment(
key: String,
name: String,
content_type: String,
size: Int,
)
}
Constructors
-
Attachment( key: String, name: String, content_type: String, size: Int, )
pub type Block {
Paragraph(content: List(Inline))
Heading(level: Int, content: List(Inline))
Quote(blocks: List(Block))
BulletList(items: List(List(Block)))
NumberedList(start: Int, items: List(List(Block)))
CodeBlock(code: String)
Divider
Attached(attachment: Attachment)
}
Constructors
-
Paragraph(content: List(Inline)) -
Heading(level: Int, content: List(Inline))A heading from 1, the largest, to 6.
-
Quote(blocks: List(Block)) -
BulletList(items: List(List(Block)))A list of items, each made of blocks. An item starts with a paragraph and may hold more lists.
-
NumberedList(start: Int, items: List(List(Block))) -
CodeBlock(code: String) -
DividerA horizontal rule.
-
Attached(attachment: Attachment)A file, such as an image, sent to storage as the document was written.
pub type Mark {
Bold
Italic
Strike
Code
Link(href: String)
}
Constructors
-
Bold -
Italic -
Strike -
Code -
Link(href: String)
Someone or something mentioned, such as @Ada Lovelace or #42.
kind and id are what your search endpoint gave the editor; label
is the text it showed then.
pub type Mention {
Mention(
trigger: String,
kind: String,
id: String,
label: String,
)
}
Constructors
-
Mention(trigger: String, kind: String, id: String, label: String)
Values
pub fn attachment_view(
attachment: Attachment,
url url: option.Option(String),
) -> element.Element(msg)
An attachment as a picture, for an image with a url, or otherwise as
a card with its name and size, which links to the file if there is a
url.
pub fn attachments(document: Document) -> List(Attachment)
Every attached file, in order. Check each key is one your app handed to this user before you keep it.
pub fn decode(json_text: String) -> Result(Document, String)
Read a document from the JSON the editor posts, refusing anything it would not have made. The error says what was wrong, for logs; it is not for people.
pub fn decoder() -> decode.Decoder(Document)
The decoder decode uses, for a document inside other JSON.
pub fn elements(
document: Document,
mention mention: fn(Mention) -> element.Element(msg),
attachment attachment: fn(Attachment) -> element.Element(msg),
) -> List(element.Element(msg))
A document’s elements, without the prose around them, for a container of your own.
pub fn figure_class() -> css.Class
pub fn file_link_class() -> css.Class
pub fn from_text(text: String) -> Document
A document from plain text: a paragraph for each run of lines separated by a blank line, keeping single line breaks. For bringing existing text columns into rich text.
pub fn is_empty(document: Document) -> Bool
Whether a document has nothing worth keeping: no text but spaces, no mentions, attachments or dividers. A required editor refuses to submit such a document; check with this on the server too.
pub fn is_safe_link(href: String) -> Bool
Whether a link may go in a document: an http, https, mailto or
tel address, or a path, query or fragment on the same site. Anything
else, such as javascript:, is refused, as are spaces and control
characters, which browsers strip from a scheme before reading it.
pub fn mention_class() -> css.Class
pub fn mention_view(
mention: Mention,
href href: option.Option(String),
) -> element.Element(msg)
A mention as a chip, such as @Ada Lovelace, linking to href if
given.
pub fn mentions(document: Document) -> List(Mention)
Everyone and everything mentioned, in the order they first appear, each once. Check each against your own records before acting on it: the document came from a browser.
pub fn to_json(document: Document) -> json.Json
A document as JSON, in the shape the editor reads and posts.
pub fn to_plain_text(document: Document) -> String
The text of a document, for search, previews, notifications and email:
paragraphs separated by blank lines, list items marked - or 1.,
quotes with >, mentions as @Ada Lovelace and attachments as their
names in brackets.
pub fn to_string(document: Document) -> String
A document as a JSON string, for storing.
pub fn view(document: Document) -> element.Element(msg)
A document as prose, with mentions as chips and attachments as cards
without links. Use view_with to link them to your own pages and files.
pub fn view_with(
document: Document,
mention mention: fn(Mention) -> element.Element(msg),
attachment attachment: fn(Attachment) -> element.Element(msg),
) -> element.Element(msg)
A document as prose, drawing each mention and attachment with the functions given, such as from your current records.