Skip to content

Commit

Permalink
feat(web): enable block drag'n'drop in super editor (#2029)
Browse files Browse the repository at this point in the history
  • Loading branch information
amanharwara committed Nov 18, 2022
1 parent c847cc1 commit dab4f67
Show file tree
Hide file tree
Showing 19 changed files with 119 additions and 201 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 1 addition & 5 deletions packages/blocks-editor/src/Editor/BlocksEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugi
import {truncateString} from './Utils';
import {SuperEditorContentId} from './Constants';

const BlockDragEnabled = false;

type BlocksEditorProps = {
onChange: (value: string, preview: string) => void;
className?: string;
Expand Down Expand Up @@ -130,11 +128,9 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
<>
<FloatingTextFormatToolbarPlugin anchorElem={floatingAnchorElem} />
<FloatingLinkEditorPlugin />
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
</>
)}
{floatingAnchorElem && BlockDragEnabled && (
<>{<DraggableBlockPlugin anchorElem={floatingAnchorElem} />}</>
)}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@
}

.draggable-block-menu .icon {
width: 16px;
height: 16px;
opacity: 0.3;
background-image: url(#{$blocks-editor-icons-path}/draggable-block-menu.svg);
width: 1rem;
height: 1rem;
opacity: 0.4;
}

.draggable-block-menu:active {
cursor: grabbing;
}

.draggable-block-menu:hover {
background-color: #efefef;
background-color: var(--sn-stylekit-contrast-background-color);
padding: 3px;
}

.draggable-block-target-line {
pointer-events: none;
background: deepskyblue;
height: 4px;
background: var(--sn-stylekit-info-color);
height: 0.25rem;
position: absolute;
left: 0;
top: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ import {
} from 'lexical';
import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
import {createPortal} from 'react-dom';
import {LexicalDraggableBlockMenu} from '@standardnotes/icons';

import {isHTMLElement} from '../../Utils/guard';
import {Point} from '../../Utils/point';
import {Rect} from '../../Utils/rect';

const SPACE = 4;
const SPACE = -16;
const TARGET_LINE_HALF_HEIGHT = 2;
const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu';
const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
const TEXT_BOX_HORIZONTAL_PADDING = 28;
const TARGET_LINE_SPACE_FROM_LEFT = 0;

const Downward = 1;
const Upward = -1;
Expand Down Expand Up @@ -179,7 +181,7 @@ function setTargetLine(
}

const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT;
const left = TEXT_BOX_HORIZONTAL_PADDING - SPACE;
const left = TARGET_LINE_SPACE_FROM_LEFT;

targetLineElem.style.transform = `translate(${left}px, ${top}px)`;
targetLineElem.style.width = `${
Expand Down Expand Up @@ -347,7 +349,9 @@ function useDraggableBlockMenu(
draggable={true}
onDragStart={onDragStart}
onDragEnd={onDragEnd}>
<div className={isEditable ? 'icon' : ''} />
<div className={isEditable ? 'icon' : ''}>
<LexicalDraggableBlockMenu className="text-text pointer-events-none" />
</div>
</div>
<div className="draggable-block-target-line" ref={targetLineRef} />
</>,
Expand Down
2 changes: 2 additions & 0 deletions packages/icons/src/Lexical/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import TypeSubscript from './type-subscript.svg'
import TypeSuperscript from './type-superscript.svg'
import TypeUnderline from './type-underline.svg'
import LexicalPencilFill from './pencil-fill.svg'
import LexicalDraggableBlockMenu from './draggable-block-menu.svg'

export {
LexicalCaretRightFill,
Expand Down Expand Up @@ -58,4 +59,5 @@ export {
TypeSuperscript,
TypeUnderline,
LexicalPencilFill,
LexicalDraggableBlockMenu,
}
3 changes: 0 additions & 3 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@
"prettier-plugin-tailwindcss": "^0.1.13",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-refresh": "^0.14.0",
"sass-loader": "*",
Expand Down
10 changes: 1 addition & 9 deletions packages/web/src/javascripts/Components/Tags/DragNDrop.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
export enum ItemTypes {
TAG = 'TAG',
}

export type DropItemTag = { uuid: string }

export type DropItem = DropItemTag

export type DropProps = { isOver: boolean; canDrop: boolean }
export const TagDragDataFormat = 'application/x-sn-drag-tag'
57 changes: 34 additions & 23 deletions packages/web/src/javascripts/Components/Tags/RootTagDropZone.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
import Icon from '@/Components/Icon/Icon'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { FeaturesController } from '@/Controllers/FeaturesController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import { classNames } from '@/Utils/ConcatenateClassNames'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'react'
import { useDrop } from 'react-dnd'
import { DropItem, DropProps, ItemTypes } from './DragNDrop'
import { DragEventHandler, FunctionComponent, useCallback, useState } from 'react'
import { TagDragDataFormat } from './DragNDrop'

type Props = {
tagsState: NavigationController
featuresState: FeaturesController
}

const RootTagDropZone: FunctionComponent<Props> = ({ tagsState }) => {
const premiumModal = usePremiumModal()
const [isOver, setIsOver] = useState(false)

const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
() => ({
accept: ItemTypes.TAG,
canDrop: (item) => {
return tagsState.hasParent(item.uuid)
},
drop: (item) => {
tagsState.assignParent(item.uuid, undefined).catch(console.error)
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
}),
[tagsState, premiumModal],
const removeDragIndicator = useCallback(() => {
setIsOver(false)
}, [])

const onDragOver: DragEventHandler<HTMLDivElement> = useCallback((event): void => {
if (event.dataTransfer.types.includes(TagDragDataFormat)) {
event.preventDefault()
setIsOver(true)
}
}, [])

const onDrop: DragEventHandler<HTMLDivElement> = useCallback(
(event): void => {
setIsOver(false)
const draggedTagUuid = event.dataTransfer.getData(TagDragDataFormat)
if (!draggedTagUuid) {
return
}
if (draggedTagUuid) {
void tagsState.assignParent(draggedTagUuid, undefined)
}
},
[tagsState],
)

return (
<div ref={dropRef} className={`root-drop ${canDrop ? 'active' : ''} ${isOver ? 'is-drag-over' : ''}`}>
<div
className={classNames('root-drop', isOver && 'active is-drag-over')}
onDragExit={removeDragIndicator}
onDragOver={onDragOver}
onDragLeave={removeDragIndicator}
onDrop={onDrop}
>
<Icon className="text-neutral" type="link-off" />
<p className="content">
Move the tag here to <br />
Expand Down
15 changes: 3 additions & 12 deletions packages/web/src/javascripts/Components/Tags/TagsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
import { SNTag } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback } from 'react'
import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'
import RootTagDropZone from './RootTagDropZone'
import { TagListSectionType } from './TagListSection'
import { TagsListItem } from './TagsListItem'
Expand All @@ -17,8 +15,6 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
const navigationController = viewControllerManager.navigationController
const allTags = type === 'all' ? navigationController.allLocalRootTags : navigationController.starredTags

const backend = HTML5Backend

const openTagContextMenu = useCallback(
(posX: number, posY: number) => {
viewControllerManager.navigationController.setContextMenuClickLocation({
Expand All @@ -40,7 +36,7 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
)

return (
<DndProvider backend={backend}>
<>
{allTags.length === 0 ? (
<div className="no-tags-placeholder text-base opacity-[0.4] lg:text-sm">
No tags or folders. Create one using the add button above.
Expand All @@ -61,15 +57,10 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
/>
)
})}
{type === 'all' && (
<RootTagDropZone
tagsState={viewControllerManager.navigationController}
featuresState={viewControllerManager.featuresController}
/>
)}
{type === 'all' && <RootTagDropZone tagsState={viewControllerManager.navigationController} />}
</>
)}
</DndProvider>
</>
)
}

Expand Down

0 comments on commit dab4f67

Please sign in to comment.