diff --git a/components/editor/editor/RenderPost.tsx b/components/editor/editor/RenderPost.tsx index 5fd946f2..3bd1e912 100644 --- a/components/editor/editor/RenderPost.tsx +++ b/components/editor/editor/RenderPost.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { CustomCodeBlockReadOnly, TiptapExtensions } from "./extensions"; +import { TiptapExtensions } from "./extensions"; import { EditorContent, useEditor } from "@tiptap/react"; interface RenderPostProps { @@ -11,7 +11,7 @@ const RenderPost = ({ json }: RenderPostProps) => { const editor = useEditor({ editable: false, - extensions: [...TiptapExtensions, CustomCodeBlockReadOnly], + extensions: [...TiptapExtensions], content, }); diff --git a/components/editor/editor/components/CodeBlock/CodeBlock.module.css b/components/editor/editor/components/CodeBlock/CodeBlock.module.css deleted file mode 100644 index c0ea71df..00000000 --- a/components/editor/editor/components/CodeBlock/CodeBlock.module.css +++ /dev/null @@ -1,5 +0,0 @@ -/* For styling codeblock theme */ -.code-block div { - background-color: black; - color: white; -} diff --git a/components/editor/editor/components/CodeBlock/CodeBlock.tsx b/components/editor/editor/components/CodeBlock/CodeBlock.tsx deleted file mode 100644 index 0abe4459..00000000 --- a/components/editor/editor/components/CodeBlock/CodeBlock.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { NodeViewProps } from "@tiptap/react"; -import { NodeViewContent, NodeViewWrapper } from "@tiptap/react"; -import type { ChangeEvent, FunctionComponent } from "react"; - -import styles from "./CodeBlock.module.css"; - -interface CodeBlockProps { - readOnly: boolean; -} - -const CodeBlock: FunctionComponent = ({ - node: { - attrs: { language: defaultLanguage }, - }, - updateAttributes, - extension, - readOnly, -}) => { - return ( - - {/* Read only mode removes selector ability dropdown - more elegant solution potentially possible */} - {!readOnly && ( - - )} -
-        
-      
-
- ); -}; - -export default CodeBlock; diff --git a/components/editor/editor/components/Table/CustomTableNodeView.tsx b/components/editor/editor/components/Table/CustomTableNodeView.tsx deleted file mode 100644 index 44b39bff..00000000 --- a/components/editor/editor/components/Table/CustomTableNodeView.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import type { NodeViewProps } from "@tiptap/react"; -import { NodeViewContent, NodeViewWrapper } from "@tiptap/react"; -import { - BookmarkMinus, - ColumnsIcon, - DeleteIcon, - RowsIcon, - Trash2Icon, -} from "lucide-react"; -import type { FunctionComponent, ReactNode } from "react"; - -import styles from "../Toolbar/Toolbar.module.css"; - -interface TableButtonProps { - disabled: boolean; - onClick: () => boolean | void; - icon: ReactNode; - className: string; - title: string; -} - -const TableButton: FunctionComponent = ({ - onClick, - disabled, - icon, - className, - title, -}) => { - return ( -
- -
- ); -}; - -const CustomTableNodeView = (props: NodeViewProps) => { - const { editor } = props; - - return ( - -
- editor.chain().focus().addColumnAfter().run()} - disabled={!editor.can().addColumnAfter()} - className="bg-neutral-900 hover:bg-neutral-600" - icon={} - title="Add Column" - /> - editor.chain().focus().addRowAfter().run()} - disabled={!editor.can().addColumnAfter()} - className="bg-neutral-900 hover:bg-neutral-600" - title="Add Row" - icon={} - /> - editor.chain().focus().deleteColumn().run()} - disabled={!editor.can().deleteColumn()} - className="bg-red-700 hover:bg-red-400" - title="Delete Column" - icon={} - /> - - editor.chain().focus().deleteRow().run()} - disabled={!editor.can().deleteRow()} - className="bg-red-700 hover:bg-red-400" - title="Delete Row" - icon={} - /> - editor.chain().focus().deleteTable().run()} - disabled={!editor.can().deleteTable()} - className="bg-red-700 hover:bg-red-400" - title="Delete Table" - icon={} - /> -
- -
- ); -}; - -export default CustomTableNodeView; diff --git a/components/editor/editor/components/Toolbar/Toolbar.module.css b/components/editor/editor/components/Toolbar/Toolbar.module.css deleted file mode 100644 index 9e47edf3..00000000 --- a/components/editor/editor/components/Toolbar/Toolbar.module.css +++ /dev/null @@ -1,102 +0,0 @@ -.sticky { - position: sticky; - top: 0; - z-index: 10; - width: 100%; - margin-bottom: 1em; -} - -.flex { - display: flex; - justify-content: space-between; - align-items: center; -} - -.menu { - flex-grow: 1; -} - -.buttons { - display: flex; - gap: 1em; -} - -.switch { - display: inline-block; - position: relative; - width: 4rem; - height: 1.5rem; - margin-left: auto; -} - -.switch input { - opacity: 0; - width: 0; - height: 0; -} - -.slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 1.5rem; - transition: 0.4s; -} - -.slider::before { - position: absolute; - content: ""; - height: 1.25rem; - width: 1.25rem; - left: 0.125rem; - bottom: 0.125rem; - background-color: whitesmoke; - border-radius: 50%; - transition: 0.4s; -} - -input:checked + .slider::before { - transform: translateX(1.25rem); -} - -.gradientText { - background-image: linear-gradient(60deg, #e21143, #ffb03a); - background-clip: text; - color: transparent; -} - -.tooltip { - visibility: 0; - position: absolute; - background-color: #333; - color: white; - text-align: center; - border-radius: 4px; - padding: 4px 8px; - opacity: 0; - margin: auto; - transition: - opacity 0.2s ease-in-out, - transform 0.3s ease-in-out; - transition-delay: 0.75s; - pointer-events: none; - z-index: 1000; -} - -.buttonContainer { - padding: 0; - margin: 0; - justify-content: center; - align-items: center; - display: flex; -} - -.buttonContainer:hover .tooltip { - visibility: 1; - opacity: 1; -} - - diff --git a/components/editor/editor/components/Toolbar/Toolbar.tsx b/components/editor/editor/components/Toolbar/Toolbar.tsx deleted file mode 100644 index 2ccdcf8a..00000000 --- a/components/editor/editor/components/Toolbar/Toolbar.tsx +++ /dev/null @@ -1,441 +0,0 @@ -import styles from "./Toolbar.module.css"; -import type { BubbleMenuProps } from "@tiptap/react"; -import type { ChangeEvent } from "react"; -import { useState } from "react"; -import { - BoldIcon, - ItalicIcon, - UnderlineIcon, - StrikethroughIcon, - CodeIcon, - Heading2Icon, - Heading3Icon, - ListOrderedIcon, - ListIcon, - SquareCodeIcon, - QuoteIcon, - RectangleHorizontalIcon, - UndoIcon, - RedoIcon, - AlignLeftIcon, - AlignCenterIcon, - AlignRightIcon, - SubscriptIcon, - SuperscriptIcon, - ImageIcon, - YoutubeIcon, - TableIcon, -} from "lucide-react"; - -import ToolBarItemButton from "./ToolbarItemButton"; -import ImageDetailsModal from "@/components/ImageDetailsModal/ImageDetailsModal"; - -type ToolbarProps = Omit; - -export interface ToolbarItem { - name: string; - isActive: () => boolean; - command: () => void; - icon: typeof BoldIcon; -} - -function Toolbar({ editor }: ToolbarProps) { - const [isOpen, setIsOpen] = useState(true); - const [, setIsTableEditing] = useState(false); - const [isImageDetailsModalOpen, setIsImageDetailsModalOpen] = useState(false); - - const isRootNode = () => { - try { - return editor?.view.state.selection.$from.before() === 0; - } catch (e) { - // Handle or log the exception if necessary - return false; - } - }; - - const handleExpand = (e: ChangeEvent) => { - setIsOpen(e.target.checked); - }; - - if (!editor) { - return null; - } - - const addYoutubeVideo = () => { - const url = prompt("Enter YouTube URL"); - - if (url) { - editor.commands.setYoutubeVideo({ - src: url, - }); - } - }; - - return ( -
- -
-
-
- editor.chain().focus().toggleBold().run()} - icon={ - - } - /> - editor.chain().focus().toggleItalic().run()} - icon={ - - } - /> - editor.chain().focus().toggleUnderline().run()} - icon={ - - } - /> - editor.chain().focus().toggleStrike().run()} - icon={ - - } - /> - editor.chain().focus().toggleCode().run()} - icon={ - - } - /> - - - editor.chain().focus().toggleHeading({ level: 2 }).run() - } - icon={ - - } - /> - - - editor.chain().focus().toggleHeading({ level: 3 }).run() - } - icon={ - - } - /> - - editor.chain().focus().toggleBulletList().run()} - icon={ - - } - /> - - editor.chain().focus().toggleOrderedList().run()} - icon={ - - } - /> - - editor.chain().focus().toggleCodeBlock().run()} - icon={ - - } - /> - - editor.chain().focus().toggleBlockquote().run()} - icon={ - - } - /> - - editor.chain().focus().setHorizontalRule().run()} - icon={ - - } - /> - editor.chain().focus().undo().run()} - icon={} - /> - editor.chain().focus().redo().run()} - icon={} - /> - - editor.chain().focus().setTextAlign("left").run()} - icon={ - - } - /> - - editor.chain().focus().setTextAlign("center").run() - } - icon={ - - } - /> - editor.chain().focus().setTextAlign("right").run()} - icon={ - - } - /> - - { - if (editor.isActive("superscript")) { - editor.chain().focus().toggleSuperscript().run(); - } - editor.chain().focus().toggleSubscript().run(); - }} - icon={ - - } - /> - - { - if (editor.isActive("subscript")) { - editor.chain().focus().toggleSubscript().run(); - } - - editor.chain().focus().toggleSuperscript().run(); - }} - icon={ - - } - /> - <> - { - editor - .chain() - .focus() - .insertTable({ rows: 3, cols: 3 }) - .run(); - setIsTableEditing(true); - }} - icon={ - - } - /> - - setIsImageDetailsModalOpen(true)} - icon={} - /> - } - /> -
-
- {/* TODO: temporary label needs replacing */} - {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} - -
-
- ); -} - -export default Toolbar; diff --git a/components/editor/editor/components/Toolbar/ToolbarItemButton.tsx b/components/editor/editor/components/Toolbar/ToolbarItemButton.tsx deleted file mode 100644 index 53b0c139..00000000 --- a/components/editor/editor/components/Toolbar/ToolbarItemButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { FunctionComponent, ReactNode } from "react"; - -import styles from "./Toolbar.module.css"; - -interface ToolBarItemButtonProps { - isRootNode: () => boolean; - icon: ReactNode; - title: string; - onClick: () => boolean | void; - className?: string; -} - -const ToolBarItemButton: FunctionComponent = ({ - title, - isRootNode, - icon, - onClick, - className, -}) => { - return ( -
- -
- ); -}; - -export default ToolBarItemButton; diff --git a/components/editor/editor/components/bubble-menu.tsx b/components/editor/editor/components/bubble-menu.tsx index 40f3ee87..a1dcf67f 100644 --- a/components/editor/editor/components/bubble-menu.tsx +++ b/components/editor/editor/components/bubble-menu.tsx @@ -5,7 +5,6 @@ import { useState } from "react"; import { BoldIcon, ItalicIcon, - StrikethroughIcon, CodeIcon, } from "lucide-react"; @@ -36,12 +35,6 @@ export const EditorBubbleMenu: FC = (props) => { command: () => props.editor.chain().focus().toggleItalic().run(), icon: ItalicIcon, }, - { - name: "strike", - isActive: () => props.editor.isActive("strike"), - command: () => props.editor.chain().focus().toggleStrike().run(), - icon: StrikethroughIcon, - }, { name: "code", isActive: () => props.editor.isActive("code"), diff --git a/components/editor/editor/components/color-selector.tsx b/components/editor/editor/components/color-selector.tsx deleted file mode 100644 index 0064e73e..00000000 --- a/components/editor/editor/components/color-selector.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import type { Editor } from "@tiptap/core"; -import { Check, ChevronDown } from "lucide-react"; -import type { Dispatch, FC, SetStateAction } from "react"; - -export interface BubbleColorMenuItem { - name: string; - color: string | null; -} - -interface ColorSelectorProps { - editor: Editor; - isOpen: boolean; - setIsOpen: Dispatch>; -} - -const TEXT_COLORS: BubbleColorMenuItem[] = [ - { - name: "Default", - color: "var(--novel-black)", - }, - { - name: "Purple", - color: "#9333EA", - }, - { - name: "Red", - color: "#E00000", - }, - { - name: "Yellow", - color: "#EAB308", - }, - { - name: "Blue", - color: "#2563EB", - }, - { - name: "Green", - color: "#008A00", - }, - { - name: "Orange", - color: "#FFA500", - }, - { - name: "Pink", - color: "#BA4081", - }, - { - name: "Gray", - color: "#A8A29E", - }, -]; - -const HIGHLIGHT_COLORS: BubbleColorMenuItem[] = [ - { - name: "Default", - color: "var(--novel-highlight-default)", - }, - { - name: "Purple", - color: "var(--novel-highlight-purple)", - }, - { - name: "Red", - color: "var(--novel-highlight-red)", - }, - { - name: "Yellow", - color: "var(--novel-highlight-yellow)", - }, - { - name: "Blue", - color: "var(--novel-highlight-blue)", - }, - { - name: "Green", - color: "var(--novel-highlight-green)", - }, - { - name: "Orange", - color: "var(--novel-highlight-orange)", - }, - { - name: "Pink", - color: "var(--novel-highlight-pink)", - }, - { - name: "Gray", - color: "var(--novel-highlight-gray)", - }, -]; - -export const ColorSelector: FC = ({ - editor, - isOpen, - setIsOpen, -}) => { - const activeColorItem = TEXT_COLORS.find(({ color }) => - editor.isActive("textStyle", { color }), - ); - - const activeHighlightItem = HIGHLIGHT_COLORS.find(({ color }) => - editor.isActive("highlight", { color }), - ); - - return ( -
- - - {isOpen && ( -
-
Color
- {TEXT_COLORS.map(({ name, color }, index) => ( - - ))} - -
- Background -
- - {HIGHLIGHT_COLORS.map(({ name, color }, index) => ( - - ))} -
- )} -
- ); -}; diff --git a/components/editor/editor/extensions/index.tsx b/components/editor/editor/extensions/index.tsx index 1b84a688..5965c3d2 100644 --- a/components/editor/editor/extensions/index.tsx +++ b/components/editor/editor/extensions/index.tsx @@ -3,29 +3,18 @@ import HorizontalRule from "@tiptap/extension-horizontal-rule"; import TiptapLink from "@tiptap/extension-link"; import Link from "@tiptap/extension-link"; import Placeholder from "@tiptap/extension-placeholder"; -import TiptapUnderline from "@tiptap/extension-underline"; import TextStyle from "@tiptap/extension-text-style"; -import { Color } from "@tiptap/extension-color"; import { Markdown } from "tiptap-markdown"; -import Highlight from "@tiptap/extension-highlight"; import SlashCommand from "./slash-command"; import { InputRule } from "@tiptap/core"; import UpdatedImage from "./updated-image"; import Document from "@tiptap/extension-document"; import Paragraph from "@tiptap/extension-paragraph"; import Text from "@tiptap/extension-text"; -import TextAlign from "@tiptap/extension-text-align"; -import Subscript from "@tiptap/extension-subscript"; -import Superscript from "@tiptap/extension-superscript"; import Youtube from "@tiptap/extension-youtube"; -import Table from "@tiptap/extension-table"; -import TableCell from "@tiptap/extension-table-cell"; -import TableHeader from "@tiptap/extension-table-header"; -import TableRow from "@tiptap/extension-table-row"; import type { NodeViewProps } from "@tiptap/react"; import { ReactNodeViewRenderer } from "@tiptap/react"; -import CustomTableNodeView from "../components/Table/CustomTableNodeView"; import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; import { common, createLowlight } from "lowlight"; @@ -34,7 +23,6 @@ const lowlight = createLowlight(common); // Highlight syntax select your style from here (https://highlightjs.org/examples) import "highlight.js/styles/monokai-sublime.css"; -import CodeBlock from "../components/CodeBlock/CodeBlock"; import DisableHeadingTextStyleShortcuts from "./disable-heading-text-style-shortcuts"; // const CustomImage = TiptapImage.extend({ @@ -47,53 +35,13 @@ const CustomDocument = Document.extend({ content: "heading block*", }); -export const CustomCodeBlockEdit = CodeBlockLowlight.extend({ - addNodeView() { - return ReactNodeViewRenderer(CodeBlock); - }, -}).configure({ lowlight }); - -// Two CodeBlockNodes need to be created to disable selector menu -export const CustomCodeBlockReadOnly = CodeBlockLowlight.extend({ - addNodeView() { - return ReactNodeViewRenderer((props: NodeViewProps) => ( - - )); - }, -}).configure({ lowlight }); - -export const CustomTable = Table.extend({ - addNodeView() { - return ReactNodeViewRenderer((props: NodeViewProps) => ( - - )); - }, -}); export const TiptapExtensions = [ CustomDocument, - CustomTable, // Table.configure({ // HTMLAttributes: { // class: "bg-neutral-100 w-full overflow-scroll", // }, // }), - TableRow.configure({ - HTMLAttributes: { - class: "bg-neutral-900 border border-neutral-500 bg-red-500 flex-1 flex", - }, - }), - TableHeader.configure({ - HTMLAttributes: { - class: - "text-neutral-900 bg-neutral-300 border border-neutral-500 flex-1 flex text-center p-1", - }, - }), - TableCell.configure({ - HTMLAttributes: { - class: - "text-neutral-900 bg-neutral-100 border border-neutral-500 flex-1 flex p-1", - }, - }), Paragraph, Text, StarterKit.configure({ @@ -191,27 +139,17 @@ export const TiptapExtensions = [ }, }), SlashCommand, - TiptapUnderline, TextStyle, - Color, Link.configure({ HTMLAttributes: { class: "text-stone-400 underline underline-offset-[3px] hover:text-stone-600 transition-colors cursor-pointer", }, }), - Highlight.configure({ - multicolor: true, - }), Markdown.configure({ html: false, transformCopiedText: true, }), - TextAlign.configure({ - types: ["heading", "paragraph"], - }), - Subscript, - Superscript, // margin controlled in global.css Youtube.configure({ width: 480, diff --git a/components/editor/editor/index.tsx b/components/editor/editor/index.tsx index 0bab16e9..32dfcb60 100644 --- a/components/editor/editor/index.tsx +++ b/components/editor/editor/index.tsx @@ -5,10 +5,9 @@ import Typography from '@tiptap/extension-typography' import { EditorContent, useEditor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import { TiptapEditorProps } from "./props"; -import { CustomCodeBlockEdit, TiptapExtensions } from "./extensions"; +import { TiptapExtensions } from "./extensions"; import { EditorBubbleMenu } from "./components/bubble-menu"; import { MediaResizer } from "./components/image-resizer"; -import Toolbar from "./components/Toolbar/Toolbar"; interface EditorProps { initialValue: string; @@ -40,7 +39,6 @@ export default function Editor({ onChange, initialValue }: EditorProps) { editor?.chain().focus().run(); }} > - {editor && } {editor && } {editor && (