fix: Enter at beginning of collapsed heading should create a new heading above (#3754)
This commit is contained in:
@@ -405,6 +405,7 @@ const EditorStyles = styled.div<{
|
|||||||
&.collapsed {
|
&.collapsed {
|
||||||
svg {
|
svg {
|
||||||
transform: rotate(${(props) => (props.rtl ? "90deg" : "-90deg")});
|
transform: rotate(${(props) => (props.rtl ? "90deg" : "-90deg")});
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
transition-delay: 0.1s;
|
transition-delay: 0.1s;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|||||||
@@ -13,49 +13,83 @@ export default function splitHeading(type: NodeType) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that the caret is at the end of the content, if it isn't then
|
// is the selection at the beginning of the node
|
||||||
// standard node splitting behaviour applies
|
const startPos = $from.before() + 1;
|
||||||
const endPos = $to.after() - 1;
|
if (startPos === from) {
|
||||||
if (endPos !== to) {
|
const collapsedNodes = findCollapsedNodes(state.doc);
|
||||||
|
const allBlocks = findBlockNodes(state.doc);
|
||||||
|
const previousBlock = allBlocks
|
||||||
|
.filter((a) => a.pos + a.node.nodeSize < startPos)
|
||||||
|
.pop();
|
||||||
|
const previousBlockIsCollapsed = !!collapsedNodes.find(
|
||||||
|
(a) => a.pos === previousBlock?.pos
|
||||||
|
);
|
||||||
|
|
||||||
|
if (previousBlockIsCollapsed) {
|
||||||
|
// Insert a new heading directly before this one
|
||||||
|
const transaction = state.tr.insert(
|
||||||
|
$from.before(),
|
||||||
|
type.create({ ...$from.parent.attrs, collapsed: false })
|
||||||
|
);
|
||||||
|
|
||||||
|
// Move the selection into the new heading node and make sure it's on screen
|
||||||
|
dispatch(
|
||||||
|
transaction
|
||||||
|
.setSelection(
|
||||||
|
TextSelection.near(transaction.doc.resolve($from.before()))
|
||||||
|
)
|
||||||
|
.scrollIntoView()
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node isn't collapsed standard behavior applies
|
// If the heading isn't collapsed standard behavior applies
|
||||||
if (!$from.parent.attrs.collapsed) {
|
if (!$from.parent.attrs.collapsed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the next visible block after this one. It takes into account nested
|
// is the selection at the end of the node. If not standard node-splitting
|
||||||
// collapsed headings and reaching the end of the document
|
// behavior applies
|
||||||
const allBlocks = findBlockNodes(state.doc);
|
const endPos = $to.after() - 1;
|
||||||
const collapsedBlocks = findCollapsedNodes(state.doc);
|
if (endPos === to) {
|
||||||
const visibleBlocks = allBlocks.filter(
|
// Find the next visible block after this one. It takes into account nested
|
||||||
(a) => !collapsedBlocks.find((b) => b.pos === a.pos)
|
// collapsed headings and reaching the end of the document
|
||||||
);
|
const collapsedNodes = findCollapsedNodes(state.doc);
|
||||||
const nextVisibleBlock = visibleBlocks.find((a) => a.pos > from);
|
const allBlocks = findBlockNodes(state.doc);
|
||||||
const pos = nextVisibleBlock
|
const visibleBlocks = allBlocks.filter(
|
||||||
? nextVisibleBlock.pos
|
(a) => !collapsedNodes.find((b) => b.pos === a.pos)
|
||||||
: state.doc.content.size;
|
);
|
||||||
|
const nextVisibleBlock = visibleBlocks.find((a) => a.pos > from);
|
||||||
|
const pos = nextVisibleBlock
|
||||||
|
? nextVisibleBlock.pos
|
||||||
|
: state.doc.content.size;
|
||||||
|
|
||||||
// Insert our new heading directly before the next visible block
|
// Insert a new heading directly before the next visible block
|
||||||
const transaction = state.tr.insert(
|
const transaction = state.tr.insert(
|
||||||
pos,
|
pos,
|
||||||
type.create({ ...$from.parent.attrs, collapsed: false })
|
type.create({ ...$from.parent.attrs, collapsed: false })
|
||||||
);
|
);
|
||||||
|
|
||||||
// Move the selection into the new heading node and make sure it's on screen
|
// Move the selection into the new heading node and make sure it's on screen
|
||||||
dispatch(
|
dispatch(
|
||||||
transaction
|
transaction
|
||||||
.setSelection(
|
.setSelection(
|
||||||
TextSelection.near(
|
TextSelection.near(
|
||||||
transaction.doc.resolve(
|
transaction.doc.resolve(
|
||||||
Math.min(pos + 1, transaction.doc.content.size)
|
Math.min(pos + 1, transaction.doc.content.size)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
.scrollIntoView()
|
||||||
.scrollIntoView()
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user