This module contains pre defined commands and transforms, which are the building blocks for modifying the editor.
The default input event command does remove range when a range is selected and an insertText event occurs. In this case, we want to remove range and insert the text related to the input data.
The default key command does remove range when a range is selected and a regular key is pressed. In this case, we want to remove range and insert the character related to that key.
Delete the nodes in the selection, if there is one. Succeeds if the selection is a range selection and a successful remove operation occurred, otherwise returns the error describing why removing the nodes failed.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "hello"
, inlineElement (Element.element image []) []
, plainText "world"
]
)
]
)
)
(Just <| range [ 0, 0 ] 2 [ 0, 2 ] 2)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "he"
, plainText "rld"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 2)
removeRange before == Ok after
--> TrueRemoves the contents in the state's range selection and optionally inserts the given text if possible. Returns an error if the selection is collapsed or it cannot remove the selection.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "hello"
, inlineElement (Element.element image []) []
, plainText "world"
]
)
]
)
)
(Just <| range [ 0, 0 ] 2 [ 0, 2 ] 2)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "het"
, plainText "rld"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 3)
removeRangeAndInsert "t" before == Ok after
--> TrueRemoves a leaf element if it is the selected element, otherwise fails with an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "hello"
, inlineElement (Element.element image []) []
, plainText "world"
]
)
]
)
)
(Just <| caret [ 0, 1 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "hello"
, plainText "world"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 5)
removeSelectedLeafElement before == Ok after
--> TrueRemoves the previous block leaf if the selection is at the beginning of a text block, otherwise returns an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p1" ])
, block
(Element.element horizontalRule [])
Leaf
, block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p2" ])
]
)
)
(Just <| caret [ 2, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p1" ])
, block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p2" ])
]
)
)
(Just <| caret [ 1, 0 ] 0)
backspaceBlock before == Ok after
--> TrueRemoves the previous inline element if the selection is an inline element or text with offset 0. Returns an error if it was unable to remove the element.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, inlineElement (Element.element image []) []
, plainText "text2"
]
)
]
)
)
(Just <| caret [ 0, 2 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, plainText "text2"
]
)
]
)
)
(Just <| caret [ 0, 1 ] 0)
backspaceInlineElement before == Ok after
--> TrueBackspace transform for a single character. This function has a few quirks in order to take advantage of native backspace behavior, namely:
apply
may remove the text node)before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, markedText "text2" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 1 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "tex"
, markedText "text2" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 0 ] 3)
backspaceText before == Ok after
--> TrueRemoves the word before the collapsed selection, otherwise returns an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "this is an ex"
, markedText "ample okay" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 1 ] 6)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "this is an "
, markedText "okay" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 0 ] 11)
backspaceWord before == Ok after
--> TrueIf the selection is collapsed at the beginning of a text block, this will select the previous selectable node, and change the offset to the end if it's a text node. This is useful for default backspace behavior in case a join backward operation could not be made.
Removes the next block leaf if the selection is at the end of a text block, otherwise fails with an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p1" ])
, block
(Element.element horizontalRule [])
Leaf
, block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p2" ])
]
)
)
(Just <| caret [ 0, 0 ] 2)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p1" ])
, block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "p2" ])
]
)
)
(Just <| caret [ 0, 0 ] 2)
deleteBlock before == Ok after
--> TrueRemoves the next inline element if the selection is at the end of a text leaf or inline element. Returns an error if it was unable to remove the element.
Delete (forward) transform for a single character. This function has a few quirks in order to take advantage of native delete behavior, namely:
apply
may remove the text node)before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, markedText "text2" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 0 ] 4)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, markedText "ext2" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 1 ] 0)
deleteText before == Ok after
--> TrueRemoves the word after the collapsed selection, otherwise returns an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "this is an ex"
, markedText "ample okay" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 0 ] 11)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "this is an "
, markedText " okay" [ mark bold [] ]
]
)
]
)
)
(Just <| caret [ 0, 0 ] 11)
deleteWord before == Ok after
--> TrueIf the selection is collapsed at the end of a text block, this will select the next selectable node at offset 0. This is useful for default delete behavior in case a join forward operation could not be made.
Transform that inserts the given block at the selection. This is useful for inserting things like horizontal rules or other block leaf elements.
If the selection is a range selection, the contents of that selection are first removed, then the insert command happens. If the inserted block is selectable, then the resulting selection will be the block, otherwise it will be the next selectable block or inline. Returns an error if the block could not be inserted.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "test" ])
]
)
)
(Just <| caret [ 0, 0 ] 2)
horizontalRuleBlock : Block
horizontalRuleBlock =
block
(Element.element horizontalRule [])
Leaf
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "te" ])
, horizontalRuleBlock
, block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "st" ])
]
)
)
(Just <| caret [ 1 ] 0)
insertBlock horizontalRuleBlock before == Ok after
--> TrueInserts the inline at the current selection. If the inline is selectable, it selects it at offset 0, otherwise the selection becomes the next selectable item if it exists. Returns an error if it cannot insert.
img : Inline
img =
inlineElement (Element.element image []) []
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 2)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "te"
, img
, plainText "xt"
]
)
]
)
)
(Just <| caret [ 0, 1 ] 0)
insertInline before == Ok after
--> TrueInserts a hard break at the current selection.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 2)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "te"
, inlineElement (Element.element hardBreak []) []
, plainText "xt"
]
)
]
)
)
(Just <| caret [ 0, 2 ] 0)
insertLineBreak before == Ok after
--> TrueInserts text at the state's selection, otherwise returns an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 2)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "teinsertxt" ]
)
]
)
)
(Just <| caret [ 0, 0 ] 8)
insertText before == Ok after
--> TrueInsert a newline at the selection in elements with the name whitelisted by the String list. This is used by the code block element, since it only allows text (no line breaks or marks). This is a somewhat specialized method, but may be useful outside of its narrow context.
Inserts the block after a block leaf.
emptyParagraph : Block
emptyParagraph =
block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "test" ])
, block
(Element.element horizontalRule [])
Leaf
]
)
)
(Just <| caret [ 1 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "test" ])
, block
(Element.element horizontalRule [])
Leaf
, emptyParagraph
]
)
)
(Just <| caret [ 2, 0 ] 0)
insertAfterBlockLeaf emptyParagraph before == Ok after
--> TrueIf the selection is collapsed and at the start of a text block, tries to join the current block the previous one. Otherwise, returns an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
]
)
, block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text2"
]
)
]
)
)
(Just <| caret [ 1, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, plainText "text2"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 4)
joinBackward before == Ok after
--> TrueIf the selection is collapsed and at the end of a text block, tries to join the current block the next one. Otherwise, returns an error.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
, block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text2" ])
]
)
)
(Just <| caret [ 0, 0 ] 4)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList
[ plainText "text"
, plainText "text2"
]
)
]
)
)
(Just <| caret [ 0, 0 ] 4)
joinForward before == Ok after
--> TrueLifts the selected block or the closest ancestor block out of its parent node. If the current selection is a range selection, this function lifts all blocks that are in the range. Returns an error if no lift can be done.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element blockquote [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
]
)
)
(Just <| caret [ 0, 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
lift before == Ok after
--> TrueSame as lift but only succeeds if the selection is an empty text block.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element blockquote [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
]
)
)
(Just <| caret [ 0, 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
liftEmpty before == Ok after
--> TrueSplit the ancestor block determined by the passed in function of the selection. If the selection is a range selection, also delete its content.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
, block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 1, 0 ] 0)
splitBlock findTextBlockAncestor before == Ok after
--> TrueDoes the split block logic, but also additionally changes the second part of the split to the given element if it's an empty text block. This is useful for splitting a header to a paragraph block.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element heading [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element heading [])
(inlineChildren <| Array.fromList [ plainText "" ])
, block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 1, 0 ] 0)
splitBlockHeaderToNewParagraph before == after
--> TrueSame as splitBlock but the searches for a text block ancestor if one exists.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
, block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "" ])
]
)
)
(Just <| caret [ 1, 0 ] 0)
splitTextBlock before == Ok after
--> TrueToggle commands for elements and marks
Changes the selected block or blocks to the onElement if one or more blocks is not that element. Otherwise, it changes it to the off element. If an element is not in the allowedElements, then it is unaffected.
The arguments are as follows:
onElement - The element to change to if there is one or more block that is not that elementoffElement - The element to change to if all blocks are the onElementconvertToPlainText - if true, strips the inline content of all marks and inline elements.before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element heading [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
toggleTextBlock (Element.element heading []) (Element.element paragraph []) False before == Ok afterApplies the toggle action for the mark. The affected marks are sorted by the given mark order.
boldMark : Mark
boldMark =
mark bold []
markdownMarkOrder : MarkOrder
markdownMarkOrder =
markOrderFromSpec markdown
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ markedText "" [ boldMark ], plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
toggleMark markdownMarkOrder boldMark Add example == OkUpdates the state's selection to span the first and last element of the document.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| singleNodeRange [ 0, 0 ] 0 4)
selectAll before == Ok after
--> TrueWraps the selection in the given element. The first argument is a mapping function for each
block element affected by the wrap. For normal transforms, using identity is okay, but for things
like lists, you may want to apply a function to wrap an additional list item block around each node.
before : State
before =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block (Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
)
(Just <| caret [ 0, 0 ] 0)
after : State
after =
state
(block
(Element.element doc [])
(blockChildren <|
Array.fromList
[ block
(Element.element blockquote [])
(blockChildren <|
Array.fromList
[ block
(Element.element paragraph [])
(inlineChildren <| Array.fromList [ plainText "text" ])
]
)
]
)
)
(Just <| caret [ 0, 0, 0 ] 0)
wrap identity (Element.element blockquote []) before == Ok after
--> True
A starting point for creating your own command map. Contains deletion, line break, lift, split, select all, and undo/redo behavior.