This module contains convenience functions for working with Block and Inline nodes.
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]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) rootNodeReturns 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 ]) rootNodeThis 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
--> TrueRemoves 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
--> TrueDetermine 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 ] rootDetermine if any elements in range satisfy some test.
-- Query to determine if any elements in range are selectable
allRange isSelectable [ 0, 0 ] [ 0, 2 ] rootTrue 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
--> TrueTrue 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 elseTrue 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 elseMap 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
--> TrueSame 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 nodeIf 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 pNodeReverseApply 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 childrenType 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.
Returns the last path and node in the block.
( lastPath, lastNode ) =
last nodeReturns 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)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)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)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")Starting from the given path, scans the node backward until the predicate has been met or it reaches the last node.
Starting from but excluding the given path, scans the node backward until the predicate has been met or it reaches the last node.
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]Starting from the given path, scans the node forward until the predicate has been met or it reaches the last node.
Starting from but excluding the given path, scans the node forward until the predicate has been met or it reaches the last node.
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 ==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" ]Same as foldl but only applied the nodes between the given paths, inclusive.
foldlRange [] [ 1 ] nodeNameOrTextValue [] (Block rootNode)
--> [ "sample2", "sample1", "paragraph" ]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" ]Same as foldr but only applied the nodes between the given paths, inclusive.
foldlRange [ 0 ] [ 0, 1 ] nodeNameOrTextValue [] (Block rootNode)
--> [ "paragraph", "sample1", "sample2" ]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 ], [] ]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 ] ]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 nodeWithTextLeafToSplitSplits a text leaf into two based on the given offset.
splitTextLeaf 1 (emptyText <| withText "test")
--> (Text "t", Text "est")Runs the toggle action on the node for the given mark.
toggleMark Add markOrder bold node
--> Adds bold to the given nodeThese 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.
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.