Add document node styling + extensions to handle clicking links with ctrl or cmd
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -14,23 +14,76 @@ import { Strike } from "@tiptap/extension-strike";
|
||||
import { Text } from "@tiptap/extension-text";
|
||||
import { Underline } from "@tiptap/extension-underline";
|
||||
import { Dropcursor, Gapcursor, UndoRedo } from "@tiptap/extensions";
|
||||
import { type Content, EditorContent, useEditor, useEditorState } from "@tiptap/react";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
import { type Content, EditorContent, getAttributes, useEditor, useEditorState } from "@tiptap/react";
|
||||
import { BubbleMenu, FloatingMenu } from "@tiptap/react/menus";
|
||||
import { type ComponentProps, useEffect } from "react";
|
||||
import { tv } from "tailwind-variants";
|
||||
|
||||
export const ControlClickLink = Link.extend({
|
||||
addProseMirrorPlugins: () => {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey("handleControlClick"),
|
||||
props: {
|
||||
handleKeyDown: (view, event) => {
|
||||
if (event.key === "Control" || event.key === "Meta") {
|
||||
view.dom.classList.add("pointer-on-hovered-link");
|
||||
}
|
||||
},
|
||||
handleDOMEvents: {
|
||||
keyup: (view, event) => {
|
||||
if (event.key === "Control" || event.key === "Meta") {
|
||||
view.dom.classList.remove("pointer-on-hovered-link");
|
||||
}
|
||||
},
|
||||
},
|
||||
handleClick: (view, _, event) => {
|
||||
const { ctrlKey, metaKey } = event; // Check for Ctrl (Windows) or Cmd (Mac)
|
||||
const keyPressed = ctrlKey || metaKey;
|
||||
|
||||
if (keyPressed) {
|
||||
// Get attributes of the mark at the clicked position
|
||||
const attrs = getAttributes(view.state, "link");
|
||||
const link = (event.target as Element | null)?.closest("a");
|
||||
|
||||
if (link && attrs.href) {
|
||||
window.open(attrs.href as string, "_blank", "noopener,noreferrer"); // Open link in a new tab
|
||||
return true; // Handle the event
|
||||
}
|
||||
}
|
||||
return false; // Let other handlers run
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
}).configure({
|
||||
openOnClick: false,
|
||||
});
|
||||
|
||||
const extensions = [
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
Heading.configure({ levels: [1, 2, 3] }),
|
||||
Paragraph.configure({
|
||||
HTMLAttributes: {
|
||||
class: "text-base py-2",
|
||||
},
|
||||
}),
|
||||
Text.configure({
|
||||
HTMLAttributes: {
|
||||
class: "text-base",
|
||||
},
|
||||
}),
|
||||
Heading.configure({
|
||||
levels: [1, 2, 3],
|
||||
}),
|
||||
Bold,
|
||||
Italic,
|
||||
Strike,
|
||||
Underline,
|
||||
Code,
|
||||
CodeBlock,
|
||||
Link.configure({ openOnClick: false }),
|
||||
ControlClickLink,
|
||||
Blockquote,
|
||||
BulletList,
|
||||
OrderedList,
|
||||
@@ -190,6 +243,11 @@ export function RichEditor(props: RichEditorProps) {
|
||||
active={editor.isActive("orderedList")}
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
/>
|
||||
<MenuButton
|
||||
label="Code"
|
||||
active={editor.isActive("code")}
|
||||
onClick={() => editor.chain().focus().toggleCode().run()}
|
||||
/>
|
||||
<MenuButton
|
||||
label="Code Block"
|
||||
active={editor.isActive("codeBlock")}
|
||||
|
||||
@@ -281,4 +281,68 @@ button[data-cell]:focus-visible {
|
||||
.ProseMirror-focused {
|
||||
outline-width: 0 !important;
|
||||
outline-color: transparent !important;
|
||||
outline-style: none !important;
|
||||
}
|
||||
|
||||
.tiptap p { @apply py-2; }
|
||||
|
||||
.tiptap strong { @apply font-semibold; }
|
||||
|
||||
.tiptap h1 {
|
||||
@apply text-4xl font-semibold pt-4;
|
||||
}
|
||||
|
||||
.tiptap h2 {
|
||||
@apply text-3xl font-semibold pt-3;
|
||||
}
|
||||
|
||||
.tiptap h3 {
|
||||
@apply text-2xl font-semibold pt-2;
|
||||
}
|
||||
|
||||
.tiptap ul {
|
||||
@apply list-disc list-inside py-2;
|
||||
|
||||
li {
|
||||
p { @apply inline-block py-0 }
|
||||
}
|
||||
li::marker { @apply mx-2!; }
|
||||
}
|
||||
|
||||
.tiptap ol {
|
||||
@apply list-decimal list-inside py-2;
|
||||
|
||||
li {
|
||||
p { @apply inline-block py-0 }
|
||||
}
|
||||
li::marker { @apply mx-2!; }
|
||||
}
|
||||
|
||||
.tiptap a {
|
||||
@apply text-txt-info underline font-normal;
|
||||
}
|
||||
|
||||
.tiptap.pointer-on-hovered-link {
|
||||
a:hover { cursor: pointer; }
|
||||
}
|
||||
|
||||
.tiptap blockquote {
|
||||
@apply py-2 pl-4 border-l-4 border-l-border-solid;
|
||||
p { @apply py-0; }
|
||||
}
|
||||
|
||||
.tiptap hr {
|
||||
@apply my-4 border-t-border-solid border-t;
|
||||
}
|
||||
|
||||
.tiptap pre {
|
||||
@apply py-4 px-6 bg-border-solid rounded-lg text-sm;
|
||||
}
|
||||
|
||||
.tiptap code {
|
||||
@apply text-inherit font-mono bg-border-solid rounded-md;
|
||||
}
|
||||
|
||||
.tiptap p code {
|
||||
@apply py-0.5 px-1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user