This module contains convenience functions for working with Block and Inline nodes.
insertAfter : RichText.Model.Node.Path -> Fragment -> RichText.Model.Node.Block -> Result String RichText.Model.Node.Block
Inserts the fragments after the node at the given path and returns the result. Returns an error if the path is invalid or the fragment cannot be inserted.
insertAfter [ 0, 0 ] fragment root
--> Inserts the fragment after the node at path [0, 0]
insertBefore : RichText.Model.Node.Path -> Fragment -> RichText.Model.Node.Block -> Result String RichText.Model.Node.Block
Inserts the fragments before the node at the given path and returns the result. Returns an error if the path is invalid or the fragment cannot be inserted.
insertBefore [ 0, 0 ] fragment root
--> Inserts the fragment before the node at path [0, 0]
replace : RichText.Model.Node.Path -> Node -> RichText.Model.Node.Block -> Result String RichText.Model.Node.Block
Replaces the node at the path with the given editor node.
-- replaces the node at [0, 0] with the inline text
replace [ 0, 0 ] (Inline textNode) rootNode
replaceWithFragment : RichText.Model.Node.Path -> Fragment -> RichText.Model.Node.Block -> Result String RichText.Model.Node.Block
Returns a Ok Block that replaces the node at the node path with the given fragment. If it is unable to replace it do to an invalid path or the wrong type of node, a Err string describing the error is returned.
-- replaces the node at [0, 0] with the given inline fragment
replaceWithFragment [ 0, 0 ] (InlineFragment <| Array.fromList [ textNode ]) rootNode
removeInRange : RichText.Model.Node.Path -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> RichText.Model.Node.Block
This method removes all the nodes inclusive to both the start and end path. Note that an ancestor is not removed if the start path or end path is a child node.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
emptyRoot : Block
emptyRoot =
block
(Element.element doc [])
(blockChildren <| Array.empty)
removeInRange [0] [0] root == emptyRoot
--> True
removeNodeAndEmptyParents : RichText.Model.Node.Path -> RichText.Model.Node.Block -> RichText.Model.Node.Block
Removes the node at the given path, and recursively removes parent blocks that have no remaining child nodes, excluding the root.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <|
Array.fromList [ removedPHtmlNode ]
)
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode ]
)
textNode : Inline
textNode =
plainText "sample1"
removedRoot : Block
removedRoot =
block
(Element.element doc [])
(blockChildren Array.empty)
removeNodeAndEmptyParents [0, 0] root == removedRoot
--> True
allRange : (Node -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Basics.Bool
Determine if all elements in range satisfy some test.
-- Query to determine if all the elements in range are selectable
allRange isSelectable [ 0, 0 ] [ 0, 2 ] root
anyRange : (Node -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Basics.Bool
Determine if any elements in range satisfy some test.
-- Query to determine if any elements in range are selectable
allRange isSelectable [ 0, 0 ] [ 0, 2 ] root
isEmptyTextBlock : Node -> Basics.Bool
True if this block has inline content with no children or a single empty text node, false otherwise
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ emptyText ]
)
isEmptyTextBlock pNode
--> True
selectionIsBeginningOfTextBlock : RichText.Model.Selection.Selection -> RichText.Model.Node.Block -> Basics.Bool
True if the selection is collapsed at the beginning of a text block, false otherwise.
-- selectionIsBeginningOfTextBlock is used for things like lift and join backward
if selectionIsBeginningOfTextBlock selection (State.root editorState) then
-- Do join backward logic
else
-- Do something else
selectionIsEndOfTextBlock : RichText.Model.Selection.Selection -> RichText.Model.Node.Block -> Basics.Bool
True if the selection is collapsed at the end of a text block, false otherwise.
-- selectionIsEndOfTextBlock is used for things like join forward
if selectionIsEndOfTextBlock selection (State.root editorState) then
-- Do join forward logic
else
-- Do something else
concatMap : (Node -> List Node) -> RichText.Model.Node.Block -> RichText.Model.Node.Block
Map a given function onto a block's children recursively and flatten the resulting list.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
textNode1 : Inline
textNode1 =
plainText "sample1"
textNode2 : Inline
textNode2 =
plainText "sample2"
doubleRoot : Block
doubleRoot =
block
(Element.element doc [])
(blockChildren <|
Array.fromList [ doublePNode, doublePNode ]
)
doublePNode : Block
doublePNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode1, textNode2, textNode2 ]
)
concatMap (\node -> [ node, node ]) rootNode == doubleRoot
--> True
indexedMap : (RichText.Model.Node.Path -> Node -> Node) -> Node -> Node
Same as map but the function is also applied with the path of each element (starting at []).
indexedMap
(\path node ->
if path == [ 0, 0 ] then
text2
else
node
)
(Block rootNode)
--> replaces the node at [0, 0] with the text2 node
joinBlocks : RichText.Model.Node.Block -> RichText.Model.Node.Block -> Maybe RichText.Model.Node.Block
If the two blocks have the same type of children, returns the joined block. Otherwise, if the blocks have different children or one or more is a leaf node, then Nothing is return.
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
pNodeReverse : Block
pNodeReverse =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode2, textNode1 ]
)
pNodeExpectedJoin : Block
pNodeExpectedJoin =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2, textNode2, textNode1 ]
)
pNodeExpectedJoin == joinBlocks pNode pNodeReverse
map : (Node -> Node) -> Node -> Node
Apply a function to this node and all child nodes.
setAnnotations : String -> Node -> Node
setAnnotations mark node =
let
annotations =
Set.fromList [ mark ]
in
case node of
Block bn ->
let
params =
Node.element bn
in
Block (bn |> withElement (params |> Element.withAnnotations annotations))
Inline il ->
case il of
Text tl ->
Inline (Text (tl |> Text.withAnnotations annotations))
InlineElement l ->
let
params =
InlineElement.element l
in
Inline (InlineElement (l |> InlineElement.withElement (params |> Element.withAnnotations annotations)))
setDummyAnnotation : Node -> Node
setDummyAnnotation node =
setAnnotations dummyAnnotation node
map setDummyAnnotation (Block rootNode)
--> Recursively adds a dummy annotation to rootNode and all its children
RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path
, Node
}
Type alias for a function that takes a path and a root block and returns a path and node. Useful for generalizing functions like previous and next that can iterate through a Block.
last : RichText.Model.Node.Block -> ( RichText.Model.Node.Path, Node )
Returns the last path and node in the block.
( lastPath, lastNode ) =
last node
next : RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, Node )
Returns the next path and node, if one exists, relative to the given path.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
textNode1 : Inline
textNode1 =
plainText "sample1"
textNode2 : Inline
textNode2 =
plainText "sample2"
next [0, 0] rootNode == Just ([0, 1], Inline textNode2)
nodeAt : RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe Node
Returns the node at the specified path if it exists.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
nodeAt [0] rootNode == Just (Block pNode)
previous : RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, Node )
Returns the previous path and node, if one exists, relative to the given path.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
textNode1 : Inline
textNode1 =
plainText "sample1"
textNode2 : Inline
textNode2 =
plainText "sample2"
previous [0, 1] rootNode == Just ([0, 0], Inline textNode1)
findAncestor : (RichText.Model.Node.Block -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, RichText.Model.Node.Block )
Find ancestor from path finds the closest ancestor from the given NodePath that matches the predicate.
-- Finds the closest list item ancestor if it exists
findAncestor (\n -> Element.name (Node.element n) == "list_item")
findBackwardFrom : (RichText.Model.Node.Path -> Node -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, Node )
Starting from the given path, scans the node backward until the predicate has been met or it reaches the last node.
findBackwardFromExclusive : (RichText.Model.Node.Path -> Node -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, Node )
Starting from but excluding the given path, scans the node backward until the predicate has been met or it reaches the last node.
findClosestBlockPath : RichText.Model.Node.Path -> RichText.Model.Node.Block -> RichText.Model.Node.Path
If the node specified by the path is an inline node, returns the parent. If the node at the path is a block, then returns the same path. Otherwise if the path is invalid, returns the root path.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
textNode1 : Inline
textNode1 =
plainText "sample1"
textNode2 : Inline
textNode2 =
plainText "sample2"
findClosestBlockPath [0, 0] rootNode
--> [0]
findClosestBlockPath [0] rootNode
--> [0]
findForwardFrom : (RichText.Model.Node.Path -> Node -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, Node )
Starting from the given path, scans the node forward until the predicate has been met or it reaches the last node.
findForwardFromExclusive : (RichText.Model.Node.Path -> Node -> Basics.Bool) -> RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, Node )
Starting from but excluding the given path, scans the node forward until the predicate has been met or it reaches the last node.
findTextBlockNodeAncestor : RichText.Model.Node.Path -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Path, RichText.Model.Node.Block )
Returns Just the parent of the given path if the path refers to an inline node, otherwise inline content, otherwisereturn Nothing.
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
textNode1 : Inline
textNode1 =
plainText "sample1"
textNode2 : Inline
textNode2 =
plainText "sample2"
findTextBlockNodeAncestor [ 0, 0 ] rootNode
--> Just ( [ 0 ], pNode )
findTextBlockNodeAncestor [ 0 ] rootNode
--> Nothing ==
foldl : (Node -> b -> b) -> b -> Node -> b
Reduce a node from the top left (e.g. from first to last).
rootNode : Block
rootNode =
block
(Element.element doc [])
(blockChildren <| Array.fromList [ pNode ])
pNode : Block
pNode =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1, textNode2 ]
)
textNode1 : Inline
textNode1 =
plainText "sample1"
textNode2 : Inline
textNode2 =
plainText "sample2"
nodeNameOrTextValue : Node -> List String -> List String
nodeNameOrTextValue node list =
(case node of
Block bn ->
Element.name (Node.element bn)
Inline il ->
case il of
Text tl ->
text tl
InlineElement p ->
Element.name (InlineElement.element p)
)
:: list
foldl nodeNameOrTextValue [] (Block rootNode)
--> [ "sample2", "sample1", "paragraph", "doc" ]
foldlRange : RichText.Model.Node.Path -> RichText.Model.Node.Path -> (Node -> b -> b) -> b -> RichText.Model.Node.Block -> b
Same as foldl
but only applied the nodes between the given paths, inclusive.
foldlRange [] [ 1 ] nodeNameOrTextValue [] (Block rootNode)
--> [ "sample2", "sample1", "paragraph" ]
foldr : (Node -> b -> b) -> b -> Node -> b
Reduce a node from the bottom right (e.g. from last to first).
nodeNameOrTextValue : Node -> List String -> List String
nodeNameOrTextValue node list =
(case node of
Block bn ->
Element.name (Node.element bn)
Inline il ->
case il of
Text tl ->
text tl
InlineElement p ->
Element.name (InlineElement.element p)
)
:: list
foldr nodeNameOrTextValue [] (Block rootNode)
--> [ "doc", "paragraph", "sample1", "sample2" ]
foldrRange : RichText.Model.Node.Path -> RichText.Model.Node.Path -> (Node -> b -> b) -> b -> RichText.Model.Node.Block -> b
Same as foldr
but only applied the nodes between the given paths, inclusive.
foldlRange [ 0 ] [ 0, 1 ] nodeNameOrTextValue [] (Block rootNode)
--> [ "paragraph", "sample1", "sample2" ]
indexedFoldl : (RichText.Model.Node.Path -> Node -> b -> b) -> b -> Node -> b
Same as foldl
but the reduce function also has the current node's path.
pathList : Path -> Node -> List Path -> List Path
pathList path _ list =
path :: list
(indexedFoldl pathList [] (Block rootNode)) == [ [ 0, 1 ], [ 0, 0 ], [ 0 ], [] ]
indexedFoldr : (RichText.Model.Node.Path -> Node -> b -> b) -> b -> Node -> b
Same as foldr
but the reduce function also has the current node's path.
pathList : Path -> Node -> List Path -> List Path
pathList path _ list =
path :: list
(indexedFoldr pathList [] (Block rootNode)) == [ [], [ 0 ], [ 0, 0 ], [ 0, 1 ] ]
splitBlockAtPathAndOffset : RichText.Model.Node.Path -> Basics.Int -> RichText.Model.Node.Block -> Maybe ( RichText.Model.Node.Block, RichText.Model.Node.Block )
Splits a block at the given path and offset and returns Just the split nodes. If the path is invalid or the node cannot be split, Nothing is returned.
textNode1 : Inline
textNode1 =
plainText "sample1"
nodeWithTextLeafToSplit : Block
nodeWithTextLeafToSplit =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ textNode1 ]
)
nodeBeforeTextLeafSplit : Block
nodeBeforeTextLeafSplit =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ plainText "sam" ]
)
nodeAfterTextLeafSplit : Block
nodeAfterTextLeafSplit =
block
(Element.element paragraph [])
(inlineChildren <|
Array.fromList [ plainText "ple1" ]
)
Just (nodeBeforeTextLeafSplit, nodeAfterTextLeafSplit) ==
splitBlockAtPathAndOffset [ 0 ] 3 nodeWithTextLeafToSplit
splitTextLeaf : Basics.Int -> RichText.Model.Text.Text -> ( RichText.Model.Text.Text, RichText.Model.Text.Text )
Splits a text leaf into two based on the given offset.
splitTextLeaf 1 (emptyText <| withText "test")
--> (Text "t", Text "est")
toggleMark : RichText.Model.Mark.ToggleAction -> RichText.Model.Mark.MarkOrder -> RichText.Model.Mark.Mark -> Node -> Node
Runs the toggle action on the node for the given mark.
toggleMark Add markOrder bold node
--> Adds bold to the given node
These are convenience types to wrap around inline and block node and arrays
A Fragment
represents an array of Block
or Inline
nodes. It's a convenience type used
for things like insertion or deserialization.
Node represents either a Block
or Inline
. It's a convenience type that wraps an argument
or return value of a function that can use either block or inline, like nodeAt
or replace
.