Skip to content

Commit

Permalink
feat(article): add drag n drop for blocks
Browse files Browse the repository at this point in the history
- fix #330
  • Loading branch information
maerzhase committed Sep 5, 2022
1 parent ca8a11e commit cfa2537
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 7 deletions.
21 changes: 19 additions & 2 deletions src/components/NFTArticle/SlateEditor/RenderElements.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
}

&:not(.opened) {

&:hover,
&.buttons_visible {
.buttons {
Expand All @@ -54,6 +53,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 {
Expand All @@ -65,4 +82,4 @@
.add_block {
transform: translateX(-50%);
}
}
}
82 changes: 77 additions & 5 deletions src/components/NFTArticle/SlateEditor/RenderElements.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
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"
import { Path, Transforms, Node, } from "slate"
import { BlockExtraMenu } from "./UI/BlockExtraMenu"
import { BlockMenu } from "./UI/BlockMenu"
import { TEditNodeFn, TEditNodeFnFactory } from "../../../types/ArticleEditor/Transforms"
import { withStopPropagation } from "../../../utils/events"
import { TAttributesEditorWrapper } from "../../../types/ArticleEditor/BlockDefinition";


interface IEditableElementWrapperProps {
element: any
}
Expand All @@ -35,7 +34,11 @@ function EditableElementWrapper({
const [showAddBlock, setShowAddBlock] = useState<boolean>(false)
const [showExtraMenu, setShowExtraMenu] = useState<boolean>(false)
const [showSettings, setShowSettings] = useState<boolean>(false)
const [isDragOver, setIsDragOver] = useState<boolean>(false)
const [isDragging, setIsDragging] = useState<boolean>(false)
const [insertAbove, setInsertAbove] = useState<boolean>(false)

const draggableRef = useRef(null);
const editor = useSlateStatic()
const path = ReactEditor.findPath(editor, element)

Expand Down Expand Up @@ -70,6 +73,57 @@ 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<HTMLDivElement>) => {
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<HTMLDivElement>) => {
// For the first block we want to be able to drop elements above it
if(path === [0]){
const {height, top} = (e.target as HTMLElement).getBoundingClientRect();
if (e.clientY < (top + height/2)) {
setInsertAbove(true)
} else {
setInsertAbove(false)
}
}
setIsDragOver(true)
}

const handleDragLeave = () => {
setIsDragOver(false)
}

const handleDrop = (e:DragEvent<HTMLDivElement>) => {
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 to = ReactEditor.findPath(editor, element)
const moveUp = !insertAbove && at[0] > to[0];
Transforms.moveNodes(editor, {at, to: moveUp ? Path.next(to) : to})
}

const deleteNode = () => {
Transforms.removeNodes(editor, {
at: path
Expand All @@ -94,16 +148,25 @@ function EditableElementWrapper({
return factory(editor, element, path)
}, [definition, editor, element, path])


return (
<div
ref={draggableRef}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={cs(style.element_wrapper, {
[style.opened]: showAddBlock || showExtraMenu,
[style.buttons_visible]: selected,
[style.buttons_visible]: selected,
[style.dragOver]: isDragOver,
[style.insertAbove]: insertAbove
})}
>
{children}
<div contentEditable={false} className={cs(style.buttons)}>
{definition.editAttributeComp ? (

{definition.editAttributeComp ? (
<button
type="button"
contentEditable={false}
Expand Down Expand Up @@ -136,6 +199,15 @@ function EditableElementWrapper({
>
<i className="fa-solid fa-ellipsis" aria-hidden/>
</button>
<button
onMouseDown={handleStartDragging}
onMouseUp={handleEndDragging}
type="button"
contentEditable={false}
tabIndex={-1}
>
<i className="fa-solid fa-grip-dots"/>
</button>
</div>
{showAddBlock && (
<>
Expand Down

0 comments on commit cfa2537

Please sign in to comment.