diff --git a/src/components/NFTArticle/SlateEditor/RenderElements.module.scss b/src/components/NFTArticle/SlateEditor/RenderElements.module.scss index 8c2e2940c..409392587 100644 --- a/src/components/NFTArticle/SlateEditor/RenderElements.module.scss +++ b/src/components/NFTArticle/SlateEditor/RenderElements.module.scss @@ -29,6 +29,7 @@ color: var(--color-white); } + &>div { width: 30px; } @@ -45,7 +46,6 @@ } &:not(.opened) { - &:hover, &.buttons_visible { .buttons { @@ -54,6 +54,24 @@ } } } + + &.insertAbove { + &::after { + top: 0; + } + } + + &.dragOver { + &::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 2px; + background-color: var(--color-secondary); + } + } } .add_block_wrapper { @@ -65,4 +83,4 @@ .add_block { transform: translateX(-50%); } -} \ No newline at end of file +} diff --git a/src/components/NFTArticle/SlateEditor/RenderElements.tsx b/src/components/NFTArticle/SlateEditor/RenderElements.tsx index bb1e0ab35..677bd0211 100644 --- a/src/components/NFTArticle/SlateEditor/RenderElements.tsx +++ b/src/components/NFTArticle/SlateEditor/RenderElements.tsx @@ -1,7 +1,13 @@ import style from "./RenderElements.module.scss" import cs from "classnames" import { ReactEditor, RenderElementProps, useSlateStatic } from "slate-react" -import React, { PropsWithChildren, useEffect, useMemo, useState } from "react" +import React, { + PropsWithChildren, + DragEvent, + useMemo, + useRef, + useState, +} from "react" import { AddBlock } from "./UI/AddBlock" import { getArticleBlockDefinition } from "./Blocks" import { Path, Transforms, Node } from "slate" @@ -37,7 +43,11 @@ function EditableElementWrapper({ const [showAddBlock, setShowAddBlock] = useState(false) const [showExtraMenu, setShowExtraMenu] = useState(false) const [showSettings, setShowSettings] = useState(false) + const [isDragOver, setIsDragOver] = useState(false) + const [isDragging, setIsDragging] = useState(false) + const [insertAbove, setInsertAbove] = useState(false) + const draggableRef = useRef(null) const editor = useSlateStatic() const path = ReactEditor.findPath(editor, element) @@ -72,6 +82,81 @@ function EditableElementWrapper({ } } + const handleStartDragging = () => { + if (!draggableRef.current) return + ;(draggableRef.current as HTMLElement).setAttribute("draggable", "true") + setIsDragging(true) + } + + const handleEndDragging = () => { + if (!draggableRef.current) return + ;(draggableRef.current as HTMLElement).setAttribute("draggable", "false") + setIsDragging(false) + } + + const handleDragStart = (e: DragEvent) => { + if (!isDragging) return + const domElement = (e.target as HTMLElement).children[0] as HTMLElement + if (!domElement) return + ReactEditor.deselect(editor) + const fromPath = ReactEditor.findPath(editor, element) + e.dataTransfer.setDragImage(domElement, domElement.offsetWidth, 0) + e.dataTransfer.setData("text/plain", JSON.stringify(fromPath)) + } + + const handleDragOver = (e: DragEvent) => { + const elemPath = ReactEditor.findPath(editor, element) + // For the first block we want to be able to drop elements above it + if (elemPath[0] === 0) { + const { height, top } = (e.target as HTMLElement).getBoundingClientRect() + if (e.clientY < top + height / 2) { + setInsertAbove(true) + } else { + setInsertAbove(false) + } + } else if (insertAbove) { + setInsertAbove(false) + } + setIsDragOver(true) + } + + const handleDragLeave = () => { + setIsDragOver(false) + } + + const handleDrop = (e: DragEvent) => { + e.preventDefault() + e.stopPropagation() + setIsDragOver(false) + const data = e.dataTransfer.getData("text/plain") + if (!data) return + const at = JSON.parse(e.dataTransfer.getData("text/plain")) as Path + const targetPath = ReactEditor.findPath(editor, element) + const insertAfter = !insertAbove && at[0] > targetPath[0] + Transforms.moveNodes(editor, { + at, + to: insertAfter ? Path.next(targetPath) : targetPath, + }) + } + + const handleMoveDown = () => { + const at = ReactEditor.findPath(editor, element) + if (at[0] === editor.children.length - 1) return + const to = Path.next(at) + Transforms.moveNodes(editor, { at, to }) + Transforms.select(editor, to) + Transforms.collapse(editor) + } + + const handleMoveUp = () => { + const at = ReactEditor.findPath(editor, element) + if (at[0] === 0) return + const to = Path.previous(at) + Transforms.moveNodes(editor, { at, to }) + Transforms.select(editor, to) + Transforms.collapse(editor) + } + const deleteNode = () => { Transforms.removeNodes(editor, { at: path, @@ -98,9 +183,17 @@ function EditableElementWrapper({ return (
{children} @@ -118,6 +211,7 @@ function EditableElementWrapper({
)} + + +
{showAddBlock && ( <> diff --git a/src/containers/Article/Editor/AutosaveArticle.tsx b/src/containers/Article/Editor/AutosaveArticle.tsx index f69e4aef6..a38d9254c 100644 --- a/src/containers/Article/Editor/AutosaveArticle.tsx +++ b/src/containers/Article/Editor/AutosaveArticle.tsx @@ -40,6 +40,7 @@ const _AutosaveArticle = ({ minted: isMinted, }, }) + const article = loadLocalArticle(id); if (JSON.stringify(article.form) !== JSON.stringify(articleFormState)) { setStatus('failed')