refactor: upgrade tiptap dependencies and enhance toolbar functionality

- Updated Tiptap dependencies to version 3.x, including core, extensions, and starter kit.
- Added Link extension to the editor for hyperlink support.
- Refactored ToolBar component to utilize useEditorState for better state management.
- Enhanced toolbar with additional heading options (H5, H6) and improved button states for formatting actions.
- Added a button to clear all marks in the editor.
This commit is contained in:
Marco Pedone 2025-08-06 15:03:05 +02:00
parent 0ea14d0650
commit 7a50ba4147
5 changed files with 423 additions and 4866 deletions

View file

@ -1,5 +1,4 @@
NEW IDEA TODOS:
//TODO predere privacy policy sito vecchio e adattarla
- trigger pag sucecss event from ordini tab and gen payment button in order detail

File diff suppressed because it is too large Load diff

View file

@ -32,10 +32,12 @@
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.12",
"@tiptap/extension-underline": "^2.23.0",
"@tiptap/pm": "^2.25.0",
"@tiptap/react": "^2.23.0",
"@tiptap/starter-kit": "^2.25.0",
"@tiptap/core": "^3.0.9",
"@tiptap/extension-link": "^3.0.9",
"@tiptap/extension-underline": "^3.0.9",
"@tiptap/pm": "^3.0.9",
"@tiptap/react": "^3.0.9",
"@tiptap/starter-kit": "^3.0.9",
"@trpc/client": "^11.1.0",
"@trpc/next": "^11.4.3",
"@trpc/react-query": "^11.1.0",

View file

@ -2,6 +2,7 @@
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import Link from "@tiptap/extension-link";
import ToolBar from "~/components/tip-tap/toolbar";
function Tiptap({
value,
@ -13,7 +14,13 @@ function Tiptap({
id: string;
}) {
const editor = useEditor({
extensions: [StarterKit, Underline],
extensions: [
StarterKit,
Underline,
Link.configure({
defaultProtocol: "https",
}),
],
content: value,
editorProps: {
attributes: {
@ -27,6 +34,10 @@ function Tiptap({
immediatelyRender: false,
});
if (!editor) {
return null;
}
return (
<div
className="flex h-full min-h-[250px] w-full flex-col justify-stretch"

View file

@ -1,12 +1,15 @@
"use client";
import { type Editor } from "@tiptap/react";
import { useEditorState, type Editor } from "@tiptap/react";
import {
Bold,
BrushCleaning,
Heading1,
Heading2,
Heading3,
Heading4,
Heading5,
Heading6,
Italic,
List,
ListOrdered,
@ -21,26 +24,47 @@ import {
import { Toggle } from "~/components/ui/toggle";
import { Button } from "~/components/ui/button";
type Props = {
editor: Editor | null;
function ToolBar({ editor }: { editor: Editor }) {
const editorState = useEditorState({
editor,
selector: (ctx) => {
return {
isBold: ctx.editor.isActive("bold") ?? false,
canBold: ctx.editor.can().chain().toggleBold().run() ?? false,
isItalic: ctx.editor.isActive("italic") ?? false,
canItalic: ctx.editor.can().chain().toggleItalic().run() ?? false,
isUnderline: ctx.editor.isActive("underline") ?? false,
canUnderline: ctx.editor.can().chain().toggleUnderline().run() ?? false,
isStrike: ctx.editor.isActive("strike") ?? false,
canStrike: ctx.editor.can().chain().toggleStrike().run() ?? false,
canClearMarks: ctx.editor.can().chain().unsetAllMarks().run() ?? false,
isParagraph: ctx.editor.isActive("paragraph") ?? false,
isHeading1: ctx.editor.isActive("heading", { level: 1 }) ?? false,
isHeading2: ctx.editor.isActive("heading", { level: 2 }) ?? false,
isHeading3: ctx.editor.isActive("heading", { level: 3 }) ?? false,
isHeading4: ctx.editor.isActive("heading", { level: 4 }) ?? false,
isHeading5: ctx.editor.isActive("heading", { level: 5 }) ?? false,
isHeading6: ctx.editor.isActive("heading", { level: 6 }) ?? false,
isBulletList: ctx.editor.isActive("bulletList") ?? false,
isOrderedList: ctx.editor.isActive("orderedList") ?? false,
isBlockquote: ctx.editor.isActive("blockquote") ?? false,
canUndo: ctx.editor.can().chain().undo().run() ?? false,
canRedo: ctx.editor.can().chain().redo().run() ?? false,
};
function ToolBar({ editor }: Props) {
if (!editor) {
return null;
}
},
});
return (
<div className="border-input my-2 flex flex-wrap gap-3 rounded-lg border p-1">
<Toggle
onPressedChange={() => editor.chain().focus().setParagraph().run()}
pressed={editor.isActive("paragraph")}
pressed={editorState.isParagraph}
>
<Text className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("heading", { level: 1 })}
pressed={editorState.isHeading1}
onPressedChange={() =>
editor.chain().focus().toggleHeading({ level: 1 }).run()
}
@ -49,7 +73,7 @@ function ToolBar({ editor }: Props) {
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("heading", { level: 2 })}
pressed={editorState.isHeading2}
onPressedChange={() =>
editor.chain().focus().toggleHeading({ level: 2 }).run()
}
@ -58,7 +82,7 @@ function ToolBar({ editor }: Props) {
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("heading", { level: 3 })}
pressed={editorState.isHeading3}
onPressedChange={() =>
editor.chain().focus().toggleHeading({ level: 3 }).run()
}
@ -67,59 +91,81 @@ function ToolBar({ editor }: Props) {
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("heading", { level: 4 })}
pressed={editorState.isHeading4}
onPressedChange={() =>
editor.chain().focus().toggleHeading({ level: 4 }).run()
}
>
<Heading4 className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editorState.isHeading5}
onPressedChange={() =>
editor.chain().focus().toggleHeading({ level: 5 }).run()
}
>
<Heading5 className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editorState.isHeading6}
onPressedChange={() =>
editor.chain().focus().toggleHeading({ level: 6 }).run()
}
>
<Heading6 className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("bold")}
pressed={editorState.isBold}
disabled={!editorState.canBold}
onPressedChange={() => editor.chain().focus().toggleBold().run()}
>
<Bold className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("italic")}
pressed={editorState.isItalic}
disabled={!editorState.canItalic}
onPressedChange={() => editor.chain().focus().toggleItalic().run()}
>
<Italic className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("underline")}
pressed={editorState.isUnderline}
disabled={!editorState.canUnderline}
onPressedChange={() => editor.chain().focus().toggleUnderline().run()}
>
<Underline className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("strike")}
pressed={editorState.isStrike}
disabled={!editorState.canStrike}
onPressedChange={() => editor.chain().focus().toggleStrike().run()}
>
<StrikethroughIcon className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("bulletList")}
pressed={editorState.isBulletList}
onPressedChange={() => editor.chain().focus().toggleBulletList().run()}
>
<List className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("orderedList")}
pressed={editorState.isOrderedList}
onPressedChange={() => editor.chain().focus().toggleOrderedList().run()}
>
<ListOrdered className="size-4" />
</Toggle>
<Toggle
size="sm"
pressed={editor.isActive("blockquote")}
pressed={editorState.isBlockquote}
onPressedChange={() => editor.chain().focus().toggleBlockquote().run()}
>
<Quote className="size-4" />
@ -138,10 +184,19 @@ function ToolBar({ editor }: Props) {
>
Hard break
</Button>
<Button
variant="ghost"
type="button"
disabled={!editorState.canClearMarks}
onClick={() => editor.chain().focus().unsetAllMarks().run()}
>
<BrushCleaning className="size-4" />
</Button>
<Button
variant="ghost"
type="button"
disabled={!editorState.canUndo}
onClick={() => editor.chain().focus().undo().run()}
>
<Undo className="size-4" />
@ -149,6 +204,7 @@ function ToolBar({ editor }: Props) {
<Button
variant="ghost"
type="button"
disabled={!editorState.canRedo}
onClick={() => editor.chain().focus().redo().run()}
>
<Redo className="size-4" />