mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-23 19:49:56 +01:00
editor: change heading collapse icon pos (#8953)
* editor: enable heading in table, change collapse icon pos, && disable empty heading collapse Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * editor: migrate empty collapsed headings in parseHTML instead of plugin Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * editor: remove migration for empty collapsed headings Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * editor: fix heading collapse on mobile Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * editor: use ontouchend instead of ontouchstart --------- Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
This commit is contained in:
@@ -23,10 +23,8 @@ import {
|
||||
textblockTypeInputRule
|
||||
} from "@tiptap/core";
|
||||
import { Heading as TiptapHeading } from "@tiptap/extension-heading";
|
||||
import { isClickWithinBounds } from "../../utils/prosemirror.js";
|
||||
import { Plugin, PluginKey, Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { Node } from "@tiptap/pm/model";
|
||||
import { useToolbarStore } from "../../toolbar/stores/toolbar-store.js";
|
||||
|
||||
const COLLAPSIBLE_BLOCK_TYPES = [
|
||||
"paragraph",
|
||||
@@ -168,6 +166,14 @@ export const Heading = TiptapHeading.extend({
|
||||
addNodeView() {
|
||||
return ({ node, getPos, editor, HTMLAttributes }) => {
|
||||
const heading = document.createElement(`h${node.attrs.level}`);
|
||||
const contentWrapper = document.createElement("div");
|
||||
const icon = document.createElement("span");
|
||||
|
||||
// providing a minWidth so that empty headings show the blinking cursor
|
||||
contentWrapper.style.minWidth = "1px";
|
||||
|
||||
icon.className = "heading-collapse-icon";
|
||||
icon.contentEditable = "false";
|
||||
|
||||
for (const attr in HTMLAttributes) {
|
||||
heading.setAttribute(attr, HTMLAttributes[attr]);
|
||||
@@ -176,15 +182,16 @@ export const Heading = TiptapHeading.extend({
|
||||
if (node.attrs.collapsed) heading.dataset.collapsed = "true";
|
||||
else delete heading.dataset.collapsed;
|
||||
|
||||
function onClick(e: MouseEvent | TouchEvent) {
|
||||
if (e instanceof MouseEvent && e.button !== 0) return;
|
||||
if (!(e.target instanceof HTMLHeadingElement)) return;
|
||||
function onIconClick(e: MouseEvent | TouchEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
const pos = typeof getPos === "function" ? getPos() : 0;
|
||||
if (typeof pos !== "number") return;
|
||||
|
||||
const resolvedPos = editor.state.doc.resolve(pos);
|
||||
const forbiddenParents = ["callout", "table"];
|
||||
const forbiddenParents = ["callout"];
|
||||
if (
|
||||
findParentNodeClosestToPos(resolvedPos, (node) =>
|
||||
forbiddenParents.includes(node.type.name)
|
||||
@@ -193,36 +200,28 @@ export const Heading = TiptapHeading.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isClickWithinBounds(
|
||||
e,
|
||||
resolvedPos,
|
||||
useToolbarStore.getState().isMobile ? "right" : "left"
|
||||
)
|
||||
) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
editor.commands.command(({ tr }) => {
|
||||
const currentNode = tr.doc.nodeAt(pos);
|
||||
if (currentNode && currentNode.type.name === "heading") {
|
||||
const shouldCollapse = !currentNode.attrs.collapsed;
|
||||
const headingLevel = currentNode.attrs.level;
|
||||
|
||||
editor.commands.command(({ tr }) => {
|
||||
const currentNode = tr.doc.nodeAt(pos);
|
||||
if (currentNode && currentNode.type.name === "heading") {
|
||||
const shouldCollapse = !currentNode.attrs.collapsed;
|
||||
const headingLevel = currentNode.attrs.level;
|
||||
|
||||
tr.setNodeAttribute(pos, "collapsed", shouldCollapse);
|
||||
toggleNodesUnderHeading(tr, pos, headingLevel, shouldCollapse);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
tr.setNodeAttribute(pos, "collapsed", shouldCollapse);
|
||||
toggleNodesUnderHeading(tr, pos, headingLevel, shouldCollapse);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
heading.onmousedown = onClick;
|
||||
heading.ontouchstart = onClick;
|
||||
icon.onmousedown = onIconClick;
|
||||
icon.ontouchend = onIconClick;
|
||||
|
||||
heading.appendChild(contentWrapper);
|
||||
heading.appendChild(icon);
|
||||
|
||||
return {
|
||||
dom: heading,
|
||||
contentDOM: heading,
|
||||
contentDOM: contentWrapper,
|
||||
update: (updatedNode) => {
|
||||
if (updatedNode.type !== this.type) {
|
||||
return false;
|
||||
|
||||
@@ -870,163 +870,131 @@ del.diffdel {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ProseMirror h1,
|
||||
.ProseMirror h2,
|
||||
.ProseMirror h3,
|
||||
.ProseMirror h4,
|
||||
.ProseMirror h5,
|
||||
.ProseMirror h1 ,
|
||||
.ProseMirror h2 ,
|
||||
.ProseMirror h3 ,
|
||||
.ProseMirror h4 ,
|
||||
.ProseMirror h5 ,
|
||||
.ProseMirror h6 {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ProseMirror h1::before,
|
||||
.ProseMirror h2::before,
|
||||
.ProseMirror h3::before,
|
||||
.ProseMirror h4::before,
|
||||
.ProseMirror h5::before,
|
||||
.ProseMirror h6::before {
|
||||
position: absolute;
|
||||
.ProseMirror h1 .heading-collapse-icon,
|
||||
.ProseMirror h2 .heading-collapse-icon,
|
||||
.ProseMirror h3 .heading-collapse-icon,
|
||||
.ProseMirror h4 .heading-collapse-icon,
|
||||
.ProseMirror h5 .heading-collapse-icon,
|
||||
.ProseMirror h6 .heading-collapse-icon {
|
||||
cursor: pointer;
|
||||
content: "";
|
||||
background-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-inline-start: 8px;
|
||||
|
||||
background-color: var(--icon);
|
||||
mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik03LjQxIDguNThMMTIgMTMuMTdsNC41OS00LjU5TDE4IDEwbC02IDZsLTYtNmwxLjQxLTEuNDJaIi8+PC9zdmc+)
|
||||
no-repeat 50% 50%;
|
||||
mask-size: cover;
|
||||
border: 1px solid var(--background);
|
||||
|
||||
transform: rotate(0);
|
||||
transition: transform 250ms ease, opacity 200ms ease;
|
||||
left: -22px;
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ProseMirror h1[dir="rtl"]::before,
|
||||
.ProseMirror h2[dir="rtl"]::before,
|
||||
.ProseMirror h3[dir="rtl"]::before,
|
||||
.ProseMirror h4[dir="rtl"]::before,
|
||||
.ProseMirror h5[dir="rtl"]::before,
|
||||
.ProseMirror h6[dir="rtl"]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ProseMirror h1[dir="rtl"]::after,
|
||||
.ProseMirror h2[dir="rtl"]::after,
|
||||
.ProseMirror h3[dir="rtl"]::after,
|
||||
.ProseMirror h4[dir="rtl"]::after,
|
||||
.ProseMirror h5[dir="rtl"]::after,
|
||||
.ProseMirror h6[dir="rtl"]::after {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
content: "";
|
||||
background-size: 18px;
|
||||
.ProseMirror h1 .heading-collapse-icon {
|
||||
margin-top: 3.5px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
||||
background-color: var(--icon);
|
||||
mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik03LjQxIDguNThMMTIgMTMuMTdsNC41OS00LjU5TDE4IDEwbC02IDZsLTYtNmwxLjQxLTEuNDJaIi8+PC9zdmc+)
|
||||
no-repeat 50% 50%;
|
||||
mask-size: cover;
|
||||
border: 1px solid var(--background);
|
||||
|
||||
transform: rotate(0deg);
|
||||
transition: transform 250ms ease, opacity 200ms ease;
|
||||
right: -22px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ProseMirror h1::before,
|
||||
.ProseMirror h1::after {
|
||||
top: 8px;
|
||||
.ProseMirror h2 .heading-collapse-icon {
|
||||
margin-top: 3px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ProseMirror h2::before,
|
||||
.ProseMirror h2::after
|
||||
{
|
||||
top: 3px;
|
||||
.ProseMirror h3 .heading-collapse-icon {
|
||||
margin-top: 2.3px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.ProseMirror h3::before,
|
||||
.ProseMirror h3::after {
|
||||
top: 0px;
|
||||
.ProseMirror h4 .heading-collapse-icon {
|
||||
margin-top: 1.8px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.ProseMirror h4::before,
|
||||
.ProseMirror h4::after {
|
||||
top: -1px;
|
||||
.ProseMirror h5 .heading-collapse-icon {
|
||||
margin-top: 1.3px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.ProseMirror h5::before,
|
||||
.ProseMirror h5::after {
|
||||
top: -2px;
|
||||
.ProseMirror h6 .heading-collapse-icon {
|
||||
margin-top: 0.3px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.ProseMirror h6::before,
|
||||
.ProseMirror h6::after {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
.ProseMirror h1[data-collapsed="true"]::before,
|
||||
.ProseMirror h2[data-collapsed="true"]::before,
|
||||
.ProseMirror h3[data-collapsed="true"]::before,
|
||||
.ProseMirror h4[data-collapsed="true"]::before,
|
||||
.ProseMirror h5[data-collapsed="true"]::before,
|
||||
.ProseMirror h6[data-collapsed="true"]::before {
|
||||
.ProseMirror h1[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h2[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h3[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h4[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h5[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h6[data-collapsed="true"] .heading-collapse-icon {
|
||||
transform: rotate(-90deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ProseMirror h1[data-collapsed="true"]::after,
|
||||
.ProseMirror h2[data-collapsed="true"]::after,
|
||||
.ProseMirror h3[data-collapsed="true"]::after,
|
||||
.ProseMirror h4[data-collapsed="true"]::after,
|
||||
.ProseMirror h5[data-collapsed="true"]::after,
|
||||
.ProseMirror h6[data-collapsed="true"]::after {
|
||||
.ProseMirror h1[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h2[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h3[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h4[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h5[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h6[data-collapsed="true"][dir="rtl"] .heading-collapse-icon {
|
||||
transform: rotate(90deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.ProseMirror h1:hover::before,
|
||||
.ProseMirror h2:hover::before,
|
||||
.ProseMirror h3:hover::before,
|
||||
.ProseMirror h4:hover::before,
|
||||
.ProseMirror h5:hover::before,
|
||||
.ProseMirror h6:hover::before,
|
||||
.ProseMirror h1:hover::after,
|
||||
.ProseMirror h2:hover::after,
|
||||
.ProseMirror h3:hover::after,
|
||||
.ProseMirror h4:hover::after,
|
||||
.ProseMirror h5:hover::after,
|
||||
.ProseMirror h6:hover::after {
|
||||
.ProseMirror h1:hover .heading-collapse-icon,
|
||||
.ProseMirror h2:hover .heading-collapse-icon,
|
||||
.ProseMirror h3:hover .heading-collapse-icon,
|
||||
.ProseMirror h4:hover .heading-collapse-icon,
|
||||
.ProseMirror h5:hover .heading-collapse-icon,
|
||||
.ProseMirror h6:hover .heading-collapse-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ProseMirror div.callout h1::before,
|
||||
.ProseMirror div.callout h2::before,
|
||||
.ProseMirror div.callout h3::before,
|
||||
.ProseMirror div.callout h4::before,
|
||||
.ProseMirror div.callout h5::before,
|
||||
.ProseMirror div.callout h6::before {
|
||||
.ProseMirror div.callout h1 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h2 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h3 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h4 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h5 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h6 .heading-collapse-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ProseMirror table h1::before,
|
||||
.ProseMirror table h2::before,
|
||||
.ProseMirror table h3::before,
|
||||
.ProseMirror table h4::before,
|
||||
.ProseMirror table h5::before,
|
||||
.ProseMirror table h6::before,
|
||||
.ProseMirror table h1::after,
|
||||
.ProseMirror table h2::after,
|
||||
.ProseMirror table h3::after,
|
||||
.ProseMirror table h4::after,
|
||||
.ProseMirror table h5::after,
|
||||
.ProseMirror table h6::after {
|
||||
display: none;
|
||||
/* hide collapse icon when heading is empty (only contains trailing break) */
|
||||
.ProseMirror h1:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h2:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h3:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h4:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h5:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h6:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.ProseMirror h1 .heading-collapse-icon,
|
||||
.ProseMirror h2 .heading-collapse-icon,
|
||||
.ProseMirror h3 .heading-collapse-icon,
|
||||
.ProseMirror h4 .heading-collapse-icon,
|
||||
.ProseMirror h5 .heading-collapse-icon,
|
||||
.ProseMirror h6 .heading-collapse-icon {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
[data-hidden="true"] {
|
||||
@@ -1047,3 +1015,4 @@ del.diffdel {
|
||||
pre[class*="language-"] {
|
||||
overflow: initial !important;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user