From 0c63f21718520d49709aa9163f4bb7eacbfbe39f Mon Sep 17 00:00:00 2001 From: "M. Palanikannan" <73993394+Palanikannan1437@users.noreply.github.com> Date: Sat, 18 Nov 2023 16:20:35 +0530 Subject: [PATCH] fix: Task List Behaviour in Editor (#2789) * better variable names and comments * drag drop migrated * custom horizontal rule created * init transaction hijack * fixed code block with better contrast, keyboard tripple enter press disabled and syntax highlighting * fixed link selector closing on open behaviour * added better keymaps and syntax highlights * made drag and drop working for code blocks * fixed drag drop for code blocks * moved drag drop only to rich text editor * fixed drag and drop only for description * enabled drag handles for peek overview and main issues * got images to old state * fixed task lists to be smaller * removed validate image functions and uncessary imports * table icons svg attributes fixed * custom list keymap extension added * more uncessary imports of validate image removed * removed console logs * fixed drag-handle styles * space styles updated for the editor * removed showing quotes from blockquotes * removed validateImage for now * added better comments and improved redundant renders * removed uncessary console logs * created util for creating the drag handle element * fixed file names --- packages/editor/core/package.json | 5 +- .../editor/core/src/types/validate-image.ts | 1 - .../ui/extensions/custom-list-keymap/index.ts | 1 + .../list-helpers/find-list-item-pos.ts | 30 +++++ .../list-helpers/get-next-list-depth.ts | 20 ++++ .../list-helpers/handle-backspace.ts | 78 +++++++++++++ .../list-helpers/handle-delete.ts | 34 ++++++ .../list-helpers/has-list-before.ts | 15 +++ .../list-helpers/has-list-item-after.ts | 17 +++ .../list-helpers/has-list-item-before.ts | 17 +++ .../custom-list-keymap/list-helpers/index.ts | 9 ++ .../list-helpers/next-list-is-deeper.ts | 19 ++++ .../list-helpers/next-list-is-higher.ts | 19 ++++ .../custom-list-keymap/list-keymap.ts | 94 ++++++++++++++++ .../editor/core/src/ui/extensions/index.tsx | 7 +- .../src/ui/extensions/table/table/icons.ts | 18 ++- .../editor/core/src/ui/hooks/useEditor.tsx | 11 +- .../src/lib/utils/DragHandleElement.ts | 23 ++++ .../src/ui/extensions/drag-drop.tsx | 23 +--- .../editor/rich-text-editor/src/ui/index.tsx | 4 - .../src/ui/menus/bubble-menu/index.tsx | 2 - space/styles/editor.css | 103 +++++++++++++++++- web/services/file.service.ts | 9 -- web/styles/editor.css | 37 +++---- 24 files changed, 508 insertions(+), 88 deletions(-) delete mode 100644 packages/editor/core/src/types/validate-image.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/index.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-before.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-after.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-before.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/index.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts create mode 100644 packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts create mode 100644 packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts diff --git a/packages/editor/core/package.json b/packages/editor/core/package.json index 072dc28c6..04100a729 100644 --- a/packages/editor/core/package.json +++ b/packages/editor/core/package.json @@ -31,11 +31,10 @@ "@blueprintjs/popover2": "^2.0.10", "@tiptap/core": "^2.1.7", "@tiptap/extension-code-block-lowlight": "^2.1.12", - "highlight.js": "^11.8.0", - "lowlight": "^3.0.0", "@tiptap/extension-color": "^2.1.11", "@tiptap/extension-image": "^2.1.7", "@tiptap/extension-link": "^2.1.7", + "@tiptap/extension-list-item": "^2.1.12", "@tiptap/extension-mention": "^2.1.12", "@tiptap/extension-table": "^2.1.6", "@tiptap/extension-table-cell": "^2.1.6", @@ -58,7 +57,9 @@ "eslint": "8.36.0", "eslint-config-next": "13.2.4", "eventsource-parser": "^0.1.0", + "highlight.js": "^11.8.0", "jsx-dom-cjs": "^8.0.3", + "lowlight": "^3.0.0", "lucide-react": "^0.244.0", "prosemirror-async-query": "^0.0.4", "react-markdown": "^8.0.7", diff --git a/packages/editor/core/src/types/validate-image.ts b/packages/editor/core/src/types/validate-image.ts deleted file mode 100644 index d976d5cdb..000000000 --- a/packages/editor/core/src/types/validate-image.ts +++ /dev/null @@ -1 +0,0 @@ -export type ValidateImage = (assetUrlWithWorkspaceId: string) => Promise; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/index.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/index.ts new file mode 100644 index 000000000..b91209e92 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/index.ts @@ -0,0 +1 @@ +export * from "./list-keymap"; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos.ts new file mode 100644 index 000000000..17e80b6af --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos.ts @@ -0,0 +1,30 @@ +import { getNodeType } from '@tiptap/core' +import { NodeType } from '@tiptap/pm/model' +import { EditorState } from '@tiptap/pm/state' + +export const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => { + const { $from } = state.selection + const nodeType = getNodeType(typeOrName, state.schema) + + let currentNode = null + let currentDepth = $from.depth + let currentPos = $from.pos + let targetDepth: number | null = null + + while (currentDepth > 0 && targetDepth === null) { + currentNode = $from.node(currentDepth) + + if (currentNode.type === nodeType) { + targetDepth = currentDepth + } else { + currentDepth -= 1 + currentPos -= 1 + } + } + + if (targetDepth === null) { + return null + } + + return { $pos: state.doc.resolve(currentPos), depth: targetDepth } +} diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts new file mode 100644 index 000000000..e81b19592 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts @@ -0,0 +1,20 @@ +import { getNodeAtPosition } from "@tiptap/core"; +import { EditorState } from "@tiptap/pm/state"; + +import { findListItemPos } from "./find-list-item-pos"; + +export const getNextListDepth = (typeOrName: string, state: EditorState) => { + const listItemPos = findListItemPos(typeOrName, state); + + if (!listItemPos) { + return false; + } + + const [, depth] = getNodeAtPosition( + state, + typeOrName, + listItemPos.$pos.pos + 4, + ); + + return depth; +}; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts new file mode 100644 index 000000000..1eac3ae4a --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts @@ -0,0 +1,78 @@ +import { Editor, isAtStartOfNode, isNodeActive } from "@tiptap/core"; +import { Node } from "@tiptap/pm/model"; + +import { findListItemPos } from "./find-list-item-pos"; +import { hasListBefore } from "./has-list-before"; + +export const handleBackspace = ( + editor: Editor, + name: string, + parentListTypes: string[], +) => { + // this is required to still handle the undo handling + if (editor.commands.undoInputRule()) { + return true; + } + + // if the cursor is not at the start of a node + // do nothing and proceed + if (!isAtStartOfNode(editor.state)) { + return false; + } + + // if the current item is NOT inside a list item & + // the previous item is a list (orderedList or bulletList) + // move the cursor into the list and delete the current item + if ( + !isNodeActive(editor.state, name) && + hasListBefore(editor.state, name, parentListTypes) + ) { + const { $anchor } = editor.state.selection; + + const $listPos = editor.state.doc.resolve($anchor.before() - 1); + + const listDescendants: Array<{ node: Node; pos: number }> = []; + + $listPos.node().descendants((node, pos) => { + if (node.type.name === name) { + listDescendants.push({ node, pos }); + } + }); + + const lastItem = listDescendants.at(-1); + + if (!lastItem) { + return false; + } + + const $lastItemPos = editor.state.doc.resolve( + $listPos.start() + lastItem.pos + 1, + ); + + return editor + .chain() + .cut( + { from: $anchor.start() - 1, to: $anchor.end() + 1 }, + $lastItemPos.end(), + ) + .joinForward() + .run(); + } + + // if the cursor is not inside the current node type + // do nothing and proceed + if (!isNodeActive(editor.state, name)) { + return false; + } + + const listItemPos = findListItemPos(name, editor.state); + + if (!listItemPos) { + return false; + } + + // if current node is a list item and cursor it at start of a list node, + // simply lift the list item i.e. remove it as a list item (task/bullet/ordered) + // irrespective of above node being a list or not + return editor.chain().liftListItem(name).run(); +}; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts new file mode 100644 index 000000000..5f47baf9d --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts @@ -0,0 +1,34 @@ +import { Editor, isAtEndOfNode, isNodeActive } from "@tiptap/core"; + +import { nextListIsDeeper } from "./next-list-is-deeper"; +import { nextListIsHigher } from "./next-list-is-higher"; + +export const handleDelete = (editor: Editor, name: string) => { + // if the cursor is not inside the current node type + // do nothing and proceed + if (!isNodeActive(editor.state, name)) { + return false; + } + + // if the cursor is not at the end of a node + // do nothing and proceed + if (!isAtEndOfNode(editor.state, name)) { + return false; + } + + // check if the next node is a list with a deeper depth + if (nextListIsDeeper(name, editor.state)) { + return editor + .chain() + .focus(editor.state.selection.from + 4) + .lift(name) + .joinBackward() + .run(); + } + + if (nextListIsHigher(name, editor.state)) { + return editor.chain().joinForward().joinBackward().run(); + } + + return editor.commands.joinItemForward(); +}; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-before.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-before.ts new file mode 100644 index 000000000..f8ae97462 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-before.ts @@ -0,0 +1,15 @@ +import { EditorState } from '@tiptap/pm/state' + +export const hasListBefore = (editorState: EditorState, name: string, parentListTypes: string[]) => { + const { $anchor } = editorState.selection + + const previousNodePos = Math.max(0, $anchor.pos - 2) + + const previousNode = editorState.doc.resolve(previousNodePos).node() + + if (!previousNode || !parentListTypes.includes(previousNode.type.name)) { + return false + } + + return true +} diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-after.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-after.ts new file mode 100644 index 000000000..6a4445514 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-after.ts @@ -0,0 +1,17 @@ +import { EditorState } from '@tiptap/pm/state' + +export const hasListItemAfter = (typeOrName: string, state: EditorState): boolean => { + const { $anchor } = state.selection + + const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2) + + if ($targetPos.index() === $targetPos.parent.childCount - 1) { + return false + } + + if ($targetPos.nodeAfter?.type.name !== typeOrName) { + return false + } + + return true +} diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-before.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-before.ts new file mode 100644 index 000000000..c5d413cb3 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/has-list-item-before.ts @@ -0,0 +1,17 @@ +import { EditorState } from '@tiptap/pm/state' + +export const hasListItemBefore = (typeOrName: string, state: EditorState): boolean => { + const { $anchor } = state.selection + + const $targetPos = state.doc.resolve($anchor.pos - 2) + + if ($targetPos.index() === 0) { + return false + } + + if ($targetPos.nodeBefore?.type.name !== typeOrName) { + return false + } + + return true +} diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/index.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/index.ts new file mode 100644 index 000000000..644953b92 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/index.ts @@ -0,0 +1,9 @@ +export * from "./find-list-item-pos"; +export * from "./get-next-list-depth"; +export * from "./handle-backspace"; +export * from "./handle-delete"; +export * from "./has-list-before"; +export * from "./has-list-item-after"; +export * from "./has-list-item-before"; +export * from "./next-list-is-deeper"; +export * from "./next-list-is-higher"; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts new file mode 100644 index 000000000..425458b2a --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts @@ -0,0 +1,19 @@ +import { EditorState } from "@tiptap/pm/state"; + +import { findListItemPos } from "./find-list-item-pos"; +import { getNextListDepth } from "./get-next-list-depth"; + +export const nextListIsDeeper = (typeOrName: string, state: EditorState) => { + const listDepth = getNextListDepth(typeOrName, state); + const listItemPos = findListItemPos(typeOrName, state); + + if (!listItemPos || !listDepth) { + return false; + } + + if (listDepth > listItemPos.depth) { + return true; + } + + return false; +}; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts new file mode 100644 index 000000000..8b853b5af --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts @@ -0,0 +1,19 @@ +import { EditorState } from "@tiptap/pm/state"; + +import { findListItemPos } from "./find-list-item-pos"; +import { getNextListDepth } from "./get-next-list-depth"; + +export const nextListIsHigher = (typeOrName: string, state: EditorState) => { + const listDepth = getNextListDepth(typeOrName, state); + const listItemPos = findListItemPos(typeOrName, state); + + if (!listItemPos || !listDepth) { + return false; + } + + if (listDepth < listItemPos.depth) { + return true; + } + + return false; +}; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts new file mode 100644 index 000000000..b61695973 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts @@ -0,0 +1,94 @@ +import { Extension } from "@tiptap/core"; + +import { handleBackspace, handleDelete } from "./list-helpers"; + +export type ListKeymapOptions = { + listTypes: Array<{ + itemName: string; + wrapperNames: string[]; + }>; +}; + +export const ListKeymap = Extension.create({ + name: "listKeymap", + + addOptions() { + return { + listTypes: [ + { + itemName: "listItem", + wrapperNames: ["bulletList", "orderedList"], + }, + { + itemName: "taskItem", + wrapperNames: ["taskList"], + }, + ], + }; + }, + + addKeyboardShortcuts() { + return { + Delete: ({ editor }) => { + let handled = false; + + this.options.listTypes.forEach(({ itemName }) => { + if (editor.state.schema.nodes[itemName] === undefined) { + return; + } + + if (handleDelete(editor, itemName)) { + handled = true; + } + }); + + return handled; + }, + "Mod-Delete": ({ editor }) => { + let handled = false; + + this.options.listTypes.forEach(({ itemName }) => { + if (editor.state.schema.nodes[itemName] === undefined) { + return; + } + + if (handleDelete(editor, itemName)) { + handled = true; + } + }); + + return handled; + }, + Backspace: ({ editor }) => { + let handled = false; + + this.options.listTypes.forEach(({ itemName, wrapperNames }) => { + if (editor.state.schema.nodes[itemName] === undefined) { + return; + } + + if (handleBackspace(editor, itemName, wrapperNames)) { + handled = true; + } + }); + + return handled; + }, + "Mod-Backspace": ({ editor }) => { + let handled = false; + + this.options.listTypes.forEach(({ itemName, wrapperNames }) => { + if (editor.state.schema.nodes[itemName] === undefined) { + return; + } + + if (handleBackspace(editor, itemName, wrapperNames)) { + handled = true; + } + }); + + return handled; + }, + }; + }, +}); diff --git a/packages/editor/core/src/ui/extensions/index.tsx b/packages/editor/core/src/ui/extensions/index.tsx index 8106bbd8f..2c6d51ad9 100644 --- a/packages/editor/core/src/ui/extensions/index.tsx +++ b/packages/editor/core/src/ui/extensions/index.tsx @@ -11,7 +11,6 @@ import TableHeader from "./table/table-header/table-header"; import Table from "./table/table"; import TableCell from "./table/table-cell/table-cell"; import TableRow from "./table/table-row/table-row"; -import DragDrop from "./drag-drop"; import HorizontalRule from "./horizontal-rule"; import ImageExtension from "./image"; @@ -20,10 +19,10 @@ import { DeleteImage } from "../../types/delete-image"; import { isValidHttpUrl } from "../../lib/utils"; import { IMentionSuggestion } from "../../types/mention-suggestion"; import { Mentions } from "../mentions"; -import { ValidateImage } from "../../types/validate-image"; import { CustomKeymap } from "./keymap"; import { CustomCodeBlock } from "./code"; +import { ListKeymap } from "./custom-list-keymap"; export const CoreEditorExtensions = ( mentionConfig: { @@ -31,7 +30,6 @@ export const CoreEditorExtensions = ( mentionHighlights: string[]; }, deleteFile: DeleteImage, - validateFile?: ValidateImage, cancelUploadImage?: () => any, ) => [ StarterKit.configure({ @@ -64,6 +62,7 @@ export const CoreEditorExtensions = ( }, }), CustomKeymap, + ListKeymap, TiptapLink.configure({ protocols: ["http", "https"], validate: (url) => isValidHttpUrl(url), @@ -72,7 +71,7 @@ export const CoreEditorExtensions = ( "text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer", }, }), - ImageExtension(deleteFile, validateFile, cancelUploadImage).configure({ + ImageExtension(deleteFile, cancelUploadImage).configure({ HTMLAttributes: { class: "rounded-lg border border-custom-border-300", }, diff --git a/packages/editor/core/src/ui/extensions/table/table/icons.ts b/packages/editor/core/src/ui/extensions/table/table/icons.ts index eda520759..65e8b8540 100644 --- a/packages/editor/core/src/ui/extensions/table/table/icons.ts +++ b/packages/editor/core/src/ui/extensions/table/table/icons.ts @@ -1,11 +1,10 @@ const icons = { - colorPicker: ``, - deleteColumn: ``, - deleteRow: ``, + colorPicker: ``, + deleteColumn: ``, + deleteRow: ``, insertLeftTableIcon: ` void; @@ -37,7 +30,6 @@ interface CustomEditorProps { export const useEditor = ({ uploadFile, deleteFile, - validateFile, cancelUploadImage, editorProps = {}, value, @@ -62,7 +54,6 @@ export const useEditor = ({ mentionHighlights: mentionHighlights ?? [], }, deleteFile, - validateFile, cancelUploadImage, ), ...extensions, diff --git a/packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts b/packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts new file mode 100644 index 000000000..a84ca1bb0 --- /dev/null +++ b/packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts @@ -0,0 +1,23 @@ +export function createDragHandleElement(): HTMLElement { + let dragHandleElement = document.createElement("div"); + dragHandleElement.draggable = true; + dragHandleElement.dataset.dragHandle = ""; + dragHandleElement.classList.add("drag-handle"); + + const dragHandleContainer = document.createElement("div"); + dragHandleContainer.classList.add("drag-handle-container"); + dragHandleElement.appendChild(dragHandleContainer); + + const dotsContainer = document.createElement("div"); + dotsContainer.classList.add("drag-handle-dots"); + + for (let i = 0; i < 6; i++) { + const spanElement = document.createElement("span"); + spanElement.classList.add("drag-handle-dot"); + dotsContainer.appendChild(spanElement); + } + + dragHandleContainer.appendChild(dotsContainer); + + return dragHandleElement; +} diff --git a/packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx b/packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx index 69542c1ce..60153daa9 100644 --- a/packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx +++ b/packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx @@ -3,6 +3,7 @@ import { Extension } from "@tiptap/core"; import { PluginKey, NodeSelection, Plugin } from "@tiptap/pm/state"; // @ts-ignore import { __serializeForClipboard, EditorView } from "@tiptap/pm/view"; +import { createDragHandleElement } from "../../lib/utils/DragHandleElement"; export interface DragHandleOptions { dragHandleWidth: number; @@ -135,25 +136,7 @@ function DragHandle(options: DragHandleOptions) { return new Plugin({ key: new PluginKey("dragHandle"), view: (view) => { - dragHandleElement = document.createElement("div"); - dragHandleElement.draggable = true; - dragHandleElement.dataset.dragHandle = ""; - dragHandleElement.classList.add("drag-handle"); - - const dragHandleContainer = document.createElement("div"); - dragHandleContainer.classList.add("drag-handle-container"); - dragHandleElement.appendChild(dragHandleContainer); - - const dotsContainer = document.createElement("div"); - dotsContainer.classList.add("drag-handle-dots"); - - for (let i = 0; i < 6; i++) { - const spanElement = document.createElement("span"); - spanElement.classList.add("drag-handle-dot"); - dotsContainer.appendChild(spanElement); - } - - dragHandleContainer.appendChild(dotsContainer); + dragHandleElement = createDragHandleElement(); dragHandleElement.addEventListener("dragstart", (e) => { handleDragStart(e, view); }); @@ -213,7 +196,7 @@ function DragHandle(options: DragHandleOptions) { if (!dragHandleElement) return; dragHandleElement.style.left = `${rect.left - rect.width}px`; - dragHandleElement.style.top = `${rect.top}px`; + dragHandleElement.style.top = `${rect.top + 3}px`; showDragHandle(); }, keydown: () => { diff --git a/packages/editor/rich-text-editor/src/ui/index.tsx b/packages/editor/rich-text-editor/src/ui/index.tsx index ba320a25c..81bbdb597 100644 --- a/packages/editor/rich-text-editor/src/ui/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/index.tsx @@ -11,7 +11,6 @@ import { RichTextEditorExtensions } from "./extensions"; export type UploadImage = (file: File) => Promise; export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise; -export type ValidateImage = (assetUrlWithWorkspaceId: string) => Promise; export type IMentionSuggestion = { id: string; @@ -29,7 +28,6 @@ interface IRichTextEditor { dragDropEnabled?: boolean; uploadFile: UploadImage; deleteFile: DeleteImage; - validateFile?: ValidateImage; noBorder?: boolean; borderOnFocus?: boolean; cancelUploadImage?: () => any; @@ -65,7 +63,6 @@ const RichTextEditor = ({ value, uploadFile, deleteFile, - validateFile, noBorder, cancelUploadImage, borderOnFocus, @@ -82,7 +79,6 @@ const RichTextEditor = ({ value, uploadFile, cancelUploadImage, - validateFile, deleteFile, forwardedRef, extensions: RichTextEditorExtensions( diff --git a/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx index bb5d1b534..a6b90bdde 100644 --- a/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx @@ -103,7 +103,6 @@ export const EditorBubbleMenu: FC = (props: any) => { editor={props.editor!} isOpen={isNodeSelectorOpen} setIsOpen={() => { - console.log("setIsNodeSelectorOpen"); setIsNodeSelectorOpen(!isNodeSelectorOpen); setIsLinkSelectorOpen(false); }} @@ -113,7 +112,6 @@ export const EditorBubbleMenu: FC = (props: any) => { editor={props.editor!!} isOpen={isLinkSelectorOpen} setIsOpen={() => { - console.log("setIsLinkSelectorOpen"); setIsLinkSelectorOpen(!isLinkSelectorOpen); setIsNodeSelectorOpen(false); }} diff --git a/space/styles/editor.css b/space/styles/editor.css index 85d881eeb..9f5623874 100644 --- a/space/styles/editor.css +++ b/space/styles/editor.css @@ -53,11 +53,12 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { background-color: rgb(var(--color-background-100)); margin: 0; cursor: pointer; - width: 1.2rem; - height: 1.2rem; + width: 0.8rem; + height: 0.8rem; position: relative; - border: 2px solid rgb(var(--color-text-100)); - margin-right: 0.3rem; + border: 1.5px solid rgb(var(--color-text-100)); + margin-right: 0.2rem; + margin-top: 0.15rem; display: grid; place-content: center; @@ -71,8 +72,8 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { &::before { content: ""; - width: 0.65em; - height: 0.65em; + width: 0.5em; + height: 0.5em; transform: scale(0); transition: 120ms transform ease-in-out; box-shadow: inset 1em 1em; @@ -229,3 +230,93 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { .ProseMirror table * .is-empty::before { opacity: 0; } + +.ProseMirror pre { + background: rgba(var(--color-background-80)); + border-radius: 0.5rem; + color: rgba(var(--color-text-100)); + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; +} + +.ProseMirror pre code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; +} + +.ProseMirror:not(.dragging) .ProseMirror-selectednode:not(img):not(pre) { + outline: none !important; + border-radius: 0.2rem; + background-color: rgb(var(--color-background-90)); + border: 1px solid #5abbf7; + padding: 4px 2px 4px 2px; + transition: background-color 0.2s; + box-shadow: none; +} + +.drag-handle { + position: fixed; + opacity: 1; + transition: opacity ease-in 0.2s; + height: 18px; + width: 15px; + display: grid; + place-items: center; + z-index: 10; + cursor: grab; + border-radius: 2px; + background-color: rgb(var(--color-background-90)); +} + +.drag-handle:hover { + background-color: rgb(var(--color-background-80)); + transition: background-color 0.2s; +} + +.drag-handle.hidden { + opacity: 0; + pointer-events: none; +} + +@media screen and (max-width: 600px) { + .drag-handle { + display: none; + pointer-events: none; + } +} + +.drag-handle-container { + height: 15px; + width: 15px; + cursor: grab; + display: grid; + place-items: center; +} + +.drag-handle-dots { + height: 100%; + width: 12px; + display: grid; + grid-template-columns: repeat(2, 1fr); + place-items: center; +} + +.drag-handle-dot { + height: 2.75px; + width: 3px; + background-color: rgba(var(--color-text-200)); + border-radius: 50%; +} + +div[data-type="horizontalRule"] { + line-height: 0; + padding: 0.25rem 0; + margin-top: 0; + margin-bottom: 0; + + & > div { + border-bottom: 1px solid rgb(var(--color-text-100)); + } +} diff --git a/web/services/file.service.ts b/web/services/file.service.ts index cd0d6f023..d92c55e18 100644 --- a/web/services/file.service.ts +++ b/web/services/file.service.ts @@ -34,7 +34,6 @@ export class FileService extends APIService { constructor() { super(API_BASE_URL); this.uploadFile = this.uploadFile.bind(this); - // this.validateFile = this.validateFile.bind(this); this.deleteImage = this.deleteImage.bind(this); this.cancelUpload = this.cancelUpload.bind(this); } @@ -63,14 +62,6 @@ export class FileService extends APIService { this.cancelSource.cancel("Upload cancelled"); } - // async validateFile(assetUrlWithWorkspaceId: string): Promise { - // console.log("bruh", assetUrlWithWorkspaceId); - // const res = await this.get(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`); - // const data = res?.data; - // console.log("data inside fucntion"); - // return data.status; - // } - // getUploadFileFunction(workspaceSlug: string): (file: File) => Promise { return async (file: File) => { const formData = new FormData(); diff --git a/web/styles/editor.css b/web/styles/editor.css index 981f53703..d5e4cf8a9 100644 --- a/web/styles/editor.css +++ b/web/styles/editor.css @@ -6,6 +6,12 @@ height: 0; } +/* block quotes */ +.ProseMirror blockquote p::before, +.ProseMirror blockquote p::after { + display: none; +} + .ProseMirror .is-empty::before { content: attr(data-placeholder); float: left; @@ -53,11 +59,12 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { background-color: rgb(var(--color-background-100)); margin: 0; cursor: pointer; - width: 1.2rem; - height: 1.2rem; + width: 0.8rem; + height: 0.8rem; position: relative; - border: 2px solid rgb(var(--color-text-100)); - margin-right: 0.3rem; + border: 1.5px solid rgb(var(--color-text-100)); + margin-right: 0.2rem; + margin-top: 0.15rem; display: grid; place-content: center; @@ -71,8 +78,8 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { &::before { content: ""; - width: 0.65em; - height: 0.65em; + width: 0.5em; + height: 0.5em; transform: scale(0); transition: 120ms transform ease-in-out; box-shadow: inset 1em 1em; @@ -259,26 +266,18 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { position: fixed; opacity: 1; transition: opacity ease-in 0.2s; + height: 18px; + width: 15px; display: grid; place-items: center; - height: 20px; - width: 15px; z-index: 10; cursor: grab; border-radius: 2px; background-color: rgb(var(--color-background-90)); - &:hover { - background-color: rgb(var(--color-background-80)); - } - - &.hide { - opacity: 0; - pointer-events: none; - } } .drag-handle:hover { - background-color: #0d0d0d 10; + background-color: rgb(var(--color-background-80)); transition: background-color 0.2s; } @@ -295,7 +294,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { } .drag-handle-container { - height: 20px; + height: 15px; width: 15px; cursor: grab; display: grid; @@ -313,7 +312,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { .drag-handle-dot { height: 2.75px; width: 3px; - background-color: rgba(var(--color-text-100)); + background-color: rgba(var(--color-text-200)); border-radius: 50%; }