feat(web): enable block drag'n'drop in super editor (#2029)
This commit is contained in:
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.
@@ -31,8 +31,6 @@ import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugi
|
|||||||
import {truncateString} from './Utils';
|
import {truncateString} from './Utils';
|
||||||
import {SuperEditorContentId} from './Constants';
|
import {SuperEditorContentId} from './Constants';
|
||||||
|
|
||||||
const BlockDragEnabled = false;
|
|
||||||
|
|
||||||
type BlocksEditorProps = {
|
type BlocksEditorProps = {
|
||||||
onChange: (value: string, preview: string) => void;
|
onChange: (value: string, preview: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -130,11 +128,9 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
|||||||
<>
|
<>
|
||||||
<FloatingTextFormatToolbarPlugin anchorElem={floatingAnchorElem} />
|
<FloatingTextFormatToolbarPlugin anchorElem={floatingAnchorElem} />
|
||||||
<FloatingLinkEditorPlugin />
|
<FloatingLinkEditorPlugin />
|
||||||
|
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{floatingAnchorElem && BlockDragEnabled && (
|
|
||||||
<>{<DraggableBlockPlugin anchorElem={floatingAnchorElem} />}</>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,10 +10,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.draggable-block-menu .icon {
|
.draggable-block-menu .icon {
|
||||||
width: 16px;
|
width: 1rem;
|
||||||
height: 16px;
|
height: 1rem;
|
||||||
opacity: 0.3;
|
opacity: 0.4;
|
||||||
background-image: url(#{$blocks-editor-icons-path}/draggable-block-menu.svg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-block-menu:active {
|
.draggable-block-menu:active {
|
||||||
@@ -21,13 +20,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.draggable-block-menu:hover {
|
.draggable-block-menu:hover {
|
||||||
background-color: #efefef;
|
background-color: var(--sn-stylekit-contrast-background-color);
|
||||||
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-block-target-line {
|
.draggable-block-target-line {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: deepskyblue;
|
background: var(--sn-stylekit-info-color);
|
||||||
height: 4px;
|
height: 0.25rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -19,16 +19,18 @@ import {
|
|||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
|
import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
|
||||||
import {createPortal} from 'react-dom';
|
import {createPortal} from 'react-dom';
|
||||||
|
import {LexicalDraggableBlockMenu} from '@standardnotes/icons';
|
||||||
|
|
||||||
import {isHTMLElement} from '../../Utils/guard';
|
import {isHTMLElement} from '../../Utils/guard';
|
||||||
import {Point} from '../../Utils/point';
|
import {Point} from '../../Utils/point';
|
||||||
import {Rect} from '../../Utils/rect';
|
import {Rect} from '../../Utils/rect';
|
||||||
|
|
||||||
const SPACE = 4;
|
const SPACE = -16;
|
||||||
const TARGET_LINE_HALF_HEIGHT = 2;
|
const TARGET_LINE_HALF_HEIGHT = 2;
|
||||||
const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu';
|
const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu';
|
||||||
const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
|
const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
|
||||||
const TEXT_BOX_HORIZONTAL_PADDING = 28;
|
const TEXT_BOX_HORIZONTAL_PADDING = 28;
|
||||||
|
const TARGET_LINE_SPACE_FROM_LEFT = 0;
|
||||||
|
|
||||||
const Downward = 1;
|
const Downward = 1;
|
||||||
const Upward = -1;
|
const Upward = -1;
|
||||||
@@ -179,7 +181,7 @@ function setTargetLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT;
|
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.transform = `translate(${left}px, ${top}px)`;
|
||||||
targetLineElem.style.width = `${
|
targetLineElem.style.width = `${
|
||||||
@@ -347,7 +349,9 @@ function useDraggableBlockMenu(
|
|||||||
draggable={true}
|
draggable={true}
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
onDragEnd={onDragEnd}>
|
onDragEnd={onDragEnd}>
|
||||||
<div className={isEditable ? 'icon' : ''} />
|
<div className={isEditable ? 'icon' : ''}>
|
||||||
|
<LexicalDraggableBlockMenu className="text-text pointer-events-none" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="draggable-block-target-line" ref={targetLineRef} />
|
<div className="draggable-block-target-line" ref={targetLineRef} />
|
||||||
</>,
|
</>,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import TypeSubscript from './type-subscript.svg'
|
|||||||
import TypeSuperscript from './type-superscript.svg'
|
import TypeSuperscript from './type-superscript.svg'
|
||||||
import TypeUnderline from './type-underline.svg'
|
import TypeUnderline from './type-underline.svg'
|
||||||
import LexicalPencilFill from './pencil-fill.svg'
|
import LexicalPencilFill from './pencil-fill.svg'
|
||||||
|
import LexicalDraggableBlockMenu from './draggable-block-menu.svg'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
LexicalCaretRightFill,
|
LexicalCaretRightFill,
|
||||||
@@ -58,4 +59,5 @@ export {
|
|||||||
TypeSuperscript,
|
TypeSuperscript,
|
||||||
TypeUnderline,
|
TypeUnderline,
|
||||||
LexicalPencilFill,
|
LexicalPencilFill,
|
||||||
|
LexicalDraggableBlockMenu,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,9 +99,6 @@
|
|||||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "^18.2.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-dom": "^18.2.0",
|
||||||
"react-refresh": "^0.14.0",
|
"react-refresh": "^0.14.0",
|
||||||
"sass-loader": "*",
|
"sass-loader": "*",
|
||||||
|
|||||||
@@ -1,9 +1 @@
|
|||||||
export enum ItemTypes {
|
export const TagDragDataFormat = 'application/x-sn-drag-tag'
|
||||||
TAG = 'TAG',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DropItemTag = { uuid: string }
|
|
||||||
|
|
||||||
export type DropItem = DropItemTag
|
|
||||||
|
|
||||||
export type DropProps = { isOver: boolean; canDrop: boolean }
|
|
||||||
|
|||||||
@@ -1,39 +1,50 @@
|
|||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
|
||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
|
||||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent } from 'react'
|
import { DragEventHandler, FunctionComponent, useCallback, useState } from 'react'
|
||||||
import { useDrop } from 'react-dnd'
|
import { TagDragDataFormat } from './DragNDrop'
|
||||||
import { DropItem, DropProps, ItemTypes } from './DragNDrop'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tagsState: NavigationController
|
tagsState: NavigationController
|
||||||
featuresState: FeaturesController
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RootTagDropZone: FunctionComponent<Props> = ({ tagsState }) => {
|
const RootTagDropZone: FunctionComponent<Props> = ({ tagsState }) => {
|
||||||
const premiumModal = usePremiumModal()
|
const [isOver, setIsOver] = useState(false)
|
||||||
|
|
||||||
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
|
const removeDragIndicator = useCallback(() => {
|
||||||
() => ({
|
setIsOver(false)
|
||||||
accept: ItemTypes.TAG,
|
}, [])
|
||||||
canDrop: (item) => {
|
|
||||||
return tagsState.hasParent(item.uuid)
|
const onDragOver: DragEventHandler<HTMLDivElement> = useCallback((event): void => {
|
||||||
},
|
if (event.dataTransfer.types.includes(TagDragDataFormat)) {
|
||||||
drop: (item) => {
|
event.preventDefault()
|
||||||
tagsState.assignParent(item.uuid, undefined).catch(console.error)
|
setIsOver(true)
|
||||||
},
|
}
|
||||||
collect: (monitor) => ({
|
}, [])
|
||||||
isOver: !!monitor.isOver(),
|
|
||||||
canDrop: !!monitor.canDrop(),
|
const onDrop: DragEventHandler<HTMLDivElement> = useCallback(
|
||||||
}),
|
(event): void => {
|
||||||
}),
|
setIsOver(false)
|
||||||
[tagsState, premiumModal],
|
const draggedTagUuid = event.dataTransfer.getData(TagDragDataFormat)
|
||||||
|
if (!draggedTagUuid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (draggedTagUuid) {
|
||||||
|
void tagsState.assignParent(draggedTagUuid, undefined)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[tagsState],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
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" />
|
<Icon className="text-neutral" type="link-off" />
|
||||||
<p className="content">
|
<p className="content">
|
||||||
Move the tag here to <br />
|
Move the tag here to <br />
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
|||||||
import { SNTag } from '@standardnotes/snjs'
|
import { SNTag } from '@standardnotes/snjs'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent, useCallback } from 'react'
|
import { FunctionComponent, useCallback } from 'react'
|
||||||
import { DndProvider } from 'react-dnd'
|
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend'
|
|
||||||
import RootTagDropZone from './RootTagDropZone'
|
import RootTagDropZone from './RootTagDropZone'
|
||||||
import { TagListSectionType } from './TagListSection'
|
import { TagListSectionType } from './TagListSection'
|
||||||
import { TagsListItem } from './TagsListItem'
|
import { TagsListItem } from './TagsListItem'
|
||||||
@@ -17,8 +15,6 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
|
|||||||
const navigationController = viewControllerManager.navigationController
|
const navigationController = viewControllerManager.navigationController
|
||||||
const allTags = type === 'all' ? navigationController.allLocalRootTags : navigationController.starredTags
|
const allTags = type === 'all' ? navigationController.allLocalRootTags : navigationController.starredTags
|
||||||
|
|
||||||
const backend = HTML5Backend
|
|
||||||
|
|
||||||
const openTagContextMenu = useCallback(
|
const openTagContextMenu = useCallback(
|
||||||
(posX: number, posY: number) => {
|
(posX: number, posY: number) => {
|
||||||
viewControllerManager.navigationController.setContextMenuClickLocation({
|
viewControllerManager.navigationController.setContextMenuClickLocation({
|
||||||
@@ -40,7 +36,7 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndProvider backend={backend}>
|
<>
|
||||||
{allTags.length === 0 ? (
|
{allTags.length === 0 ? (
|
||||||
<div className="no-tags-placeholder text-base opacity-[0.4] lg:text-sm">
|
<div className="no-tags-placeholder text-base opacity-[0.4] lg:text-sm">
|
||||||
No tags or folders. Create one using the add button above.
|
No tags or folders. Create one using the add button above.
|
||||||
@@ -61,15 +57,10 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{type === 'all' && (
|
{type === 'all' && <RootTagDropZone tagsState={viewControllerManager.navigationController} />}
|
||||||
<RootTagDropZone
|
|
||||||
tagsState={viewControllerManager.navigationController}
|
|
||||||
featuresState={viewControllerManager.featuresController}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DndProvider>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import { FOCUSABLE_BUT_NOT_TABBABLE, TAG_FOLDERS_FEATURE_NAME } from '@/Constants/Constants'
|
import { FOCUSABLE_BUT_NOT_TABBABLE, TAG_FOLDERS_FEATURE_NAME } from '@/Constants/Constants'
|
||||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
|
||||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
@@ -9,6 +8,7 @@ import { IconType, SNTag } from '@standardnotes/snjs'
|
|||||||
import { computed } from 'mobx'
|
import { computed } from 'mobx'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import {
|
import {
|
||||||
|
DragEventHandler,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
FunctionComponent,
|
FunctionComponent,
|
||||||
KeyboardEventHandler,
|
KeyboardEventHandler,
|
||||||
@@ -19,16 +19,15 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useDrag, useDrop } from 'react-dnd'
|
|
||||||
import { DropItem, DropProps, ItemTypes } from './DragNDrop'
|
|
||||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
import { mergeRefs } from '@/Hooks/mergeRefs'
|
|
||||||
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||||
import { LinkingController } from '@/Controllers/LinkingController'
|
import { LinkingController } from '@/Controllers/LinkingController'
|
||||||
import { TagListSectionType } from './TagListSection'
|
import { TagListSectionType } from './TagListSection'
|
||||||
import { log, LoggingDomain } from '@/Logging'
|
import { log, LoggingDomain } from '@/Logging'
|
||||||
|
import { TagDragDataFormat } from './DragNDrop'
|
||||||
|
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tag: SNTag
|
tag: SNTag
|
||||||
@@ -68,6 +67,8 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
const [showChildren, setShowChildren] = useState(tag.expanded)
|
const [showChildren, setShowChildren] = useState(tag.expanded)
|
||||||
const [hadChildren, setHadChildren] = useState(hasChildren)
|
const [hadChildren, setHadChildren] = useState(hasChildren)
|
||||||
|
|
||||||
|
const [isBeingDraggedOver, setIsBeingDraggedOver] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hadChildren && hasChildren) {
|
if (!hadChildren && hasChildren) {
|
||||||
setShowChildren(true)
|
setShowChildren(true)
|
||||||
@@ -151,43 +152,6 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
}
|
}
|
||||||
}, [subtagInputRef, isAddingSubtag])
|
}, [subtagInputRef, isAddingSubtag])
|
||||||
|
|
||||||
const [, dragRef] = useDrag(
|
|
||||||
() => ({
|
|
||||||
type: ItemTypes.TAG,
|
|
||||||
item: { uuid: tag.uuid },
|
|
||||||
canDrag: () => {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isDragging: !!monitor.isDragging(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
[tag],
|
|
||||||
)
|
|
||||||
|
|
||||||
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
|
|
||||||
() => ({
|
|
||||||
accept: ItemTypes.TAG,
|
|
||||||
canDrop: (item) => {
|
|
||||||
return navigationController.isValidTagParent(tag, item as SNTag)
|
|
||||||
},
|
|
||||||
drop: (item) => {
|
|
||||||
if (!hasFolders) {
|
|
||||||
premiumModal.activate(TAG_FOLDERS_FEATURE_NAME)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
navigationController.assignParent(item.uuid, tag.uuid).catch(console.error)
|
|
||||||
},
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isOver: !!monitor.isOver(),
|
|
||||||
canDrop: !!monitor.canDrop(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
[tag, navigationController, hasFolders, premiumModal],
|
|
||||||
)
|
|
||||||
|
|
||||||
const readyToDrop = isOver && canDrop
|
|
||||||
|
|
||||||
const toggleContextMenu: MouseEventHandler<HTMLAnchorElement> = useCallback(
|
const toggleContextMenu: MouseEventHandler<HTMLAnchorElement> = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -236,14 +200,59 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
|
|
||||||
log(LoggingDomain.NavigationList, 'Rendering TagsListItem')
|
log(LoggingDomain.NavigationList, 'Rendering TagsListItem')
|
||||||
|
|
||||||
|
const onDragStart: DragEventHandler<HTMLDivElement> = useCallback(
|
||||||
|
(event) => {
|
||||||
|
event.dataTransfer.setData(TagDragDataFormat, tag.uuid)
|
||||||
|
},
|
||||||
|
[tag.uuid],
|
||||||
|
)
|
||||||
|
|
||||||
|
const onDragEnter: DragEventHandler<HTMLDivElement> = useCallback((event): void => {
|
||||||
|
if (event.dataTransfer.types.includes(TagDragDataFormat)) {
|
||||||
|
event.preventDefault()
|
||||||
|
setIsBeingDraggedOver(true)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const removeDragIndicator = useCallback(() => {
|
||||||
|
setIsBeingDraggedOver(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onDragOver: DragEventHandler<HTMLDivElement> = useCallback((event): void => {
|
||||||
|
if (event.dataTransfer.types.includes(TagDragDataFormat)) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onDrop: DragEventHandler<HTMLDivElement> = useCallback(
|
||||||
|
(event): void => {
|
||||||
|
setIsBeingDraggedOver(false)
|
||||||
|
const draggedTagUuid = event.dataTransfer.getData(TagDragDataFormat)
|
||||||
|
if (!draggedTagUuid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!navigationController.isValidTagParent(tag, { uuid: draggedTagUuid } as SNTag)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!hasFolders) {
|
||||||
|
premiumModal.activate(TAG_FOLDERS_FEATURE_NAME)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (draggedTagUuid) {
|
||||||
|
void navigationController.assignParent(draggedTagUuid, tag.uuid)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[hasFolders, navigationController, premiumModal, tag],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||||
className={classNames('tag px-3.5', isSelected && 'selected', readyToDrop && 'is-drag-over')}
|
className={classNames('tag px-3.5', isSelected && 'selected', isBeingDraggedOver && 'is-drag-over')}
|
||||||
onClick={selectCurrentTag}
|
onClick={selectCurrentTag}
|
||||||
ref={mergeRefs([dragRef, tagRef])}
|
ref={tagRef}
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
|
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
|
||||||
}}
|
}}
|
||||||
@@ -251,9 +260,16 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
onContextMenu(tag, e.clientX, e.clientY)
|
onContextMenu(tag, e.clientX, e.clientY)
|
||||||
}}
|
}}
|
||||||
|
draggable={true}
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
onDragEnter={onDragEnter}
|
||||||
|
onDragExit={removeDragIndicator}
|
||||||
|
onDragOver={onDragOver}
|
||||||
|
onDragLeave={removeDragIndicator}
|
||||||
|
onDrop={onDrop}
|
||||||
>
|
>
|
||||||
<div className="tag-info" title={title} ref={dropRef}>
|
<div className="tag-info" title={title}>
|
||||||
<div onClick={selectCurrentTag} className={'tag-icon draggable mr-2'} ref={dragRef}>
|
<div onClick={selectCurrentTag} className={'tag-icon draggable mr-2'}>
|
||||||
<Icon
|
<Icon
|
||||||
type={tag.iconString as IconType}
|
type={tag.iconString as IconType}
|
||||||
className={`cursor-pointer ${isSelected ? 'text-info' : 'text-neutral'}`}
|
className={`cursor-pointer ${isSelected ? 'text-info' : 'text-neutral'}`}
|
||||||
|
|||||||
@@ -36,10 +36,7 @@ $content-horizontal-padding: 16px;
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
.sn-icon {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|||||||
92
yarn.lock
92
yarn.lock
@@ -2111,7 +2111,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
|
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.8.4":
|
||||||
version: 7.19.4
|
version: 7.19.4
|
||||||
resolution: "@babel/runtime@npm:7.19.4"
|
resolution: "@babel/runtime@npm:7.19.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5067,27 +5067,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@react-dnd/asap@npm:^5.0.1":
|
|
||||||
version: 5.0.2
|
|
||||||
resolution: "@react-dnd/asap@npm:5.0.2"
|
|
||||||
checksum: 18f040e53512983f11c542ef21e6e4cac605d585a10cd764b13bc1b2f3ac7490e0fa40503adc348d8387aa45bc8e7eebe9cb33003b960a30bb5fde666ff2adde
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@react-dnd/invariant@npm:^4.0.1":
|
|
||||||
version: 4.0.2
|
|
||||||
resolution: "@react-dnd/invariant@npm:4.0.2"
|
|
||||||
checksum: 594f6d78896c19bb8f023e101334fd91a9fdff686117bd8e830ba53737ec0a6042dab66971d3d63c7afbc622103909aff7a64c5c6767e0aa8d9561fd42705016
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@react-dnd/shallowequal@npm:^4.0.1":
|
|
||||||
version: 4.0.2
|
|
||||||
resolution: "@react-dnd/shallowequal@npm:4.0.2"
|
|
||||||
checksum: 7f21d691bddbfd4d2830948cbeefecca1600b2b46bcb1934926795f07ae8a1fa60a3dfd3a2112be5ef682c3820c80a99711e9fa15843f7e300acb25a4ecb70ab
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@react-native-community/async-storage@npm:1.12.1":
|
"@react-native-community/async-storage@npm:1.12.1":
|
||||||
version: 1.12.1
|
version: 1.12.1
|
||||||
resolution: "@react-native-community/async-storage@npm:1.12.1"
|
resolution: "@react-native-community/async-storage@npm:1.12.1"
|
||||||
@@ -6368,9 +6347,6 @@ __metadata:
|
|||||||
prettier-plugin-tailwindcss: ^0.1.13
|
prettier-plugin-tailwindcss: ^0.1.13
|
||||||
qrcode.react: ^3.1.0
|
qrcode.react: ^3.1.0
|
||||||
react: ^18.2.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-dom: ^18.2.0
|
||||||
react-refresh: ^0.14.0
|
react-refresh: ^0.14.0
|
||||||
sass-loader: "*"
|
sass-loader: "*"
|
||||||
@@ -13215,17 +13191,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"dnd-core@npm:^16.0.1":
|
|
||||||
version: 16.0.1
|
|
||||||
resolution: "dnd-core@npm:16.0.1"
|
|
||||||
dependencies:
|
|
||||||
"@react-dnd/asap": ^5.0.1
|
|
||||||
"@react-dnd/invariant": ^4.0.1
|
|
||||||
redux: ^4.2.0
|
|
||||||
checksum: b7d3ef4664f433af796f440ddd27ad9d7fef0205f26c4b7c0af6ebf612ffa9b33e64d095d3e79190c4baaed34aa36570f321ebe0d2cc8ff1031ff158a0907b3f
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"dns-equal@npm:^1.0.0":
|
"dns-equal@npm:^1.0.0":
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
resolution: "dns-equal@npm:1.0.0"
|
resolution: "dns-equal@npm:1.0.0"
|
||||||
@@ -16555,7 +16520,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
|
"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0":
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
resolution: "hoist-non-react-statics@npm:3.3.2"
|
resolution: "hoist-non-react-statics@npm:3.3.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -25141,50 +25106,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-dnd-html5-backend@npm:^16.0.1":
|
|
||||||
version: 16.0.1
|
|
||||||
resolution: "react-dnd-html5-backend@npm:16.0.1"
|
|
||||||
dependencies:
|
|
||||||
dnd-core: ^16.0.1
|
|
||||||
checksum: e2368bf85d5632a5cd867b743feb54c9052d909ea5331608860fa455edf3c633ac791f5b338e3db29b19ea8670c0ba5fb43c9c1c2510760bea030811d726cdfa
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"react-dnd-touch-backend@npm:^16.0.1":
|
|
||||||
version: 16.0.1
|
|
||||||
resolution: "react-dnd-touch-backend@npm:16.0.1"
|
|
||||||
dependencies:
|
|
||||||
"@react-dnd/invariant": ^4.0.1
|
|
||||||
dnd-core: ^16.0.1
|
|
||||||
checksum: 5362c5f4266e3655d02c716f341dc55c61fae53e12eb3b239245bafb7a3f3cdb953e27e187e8dcb9d86c01bc738853d764d7c85b87104a49a8a8f1ecc4d35755
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"react-dnd@npm:^16.0.1":
|
|
||||||
version: 16.0.1
|
|
||||||
resolution: "react-dnd@npm:16.0.1"
|
|
||||||
dependencies:
|
|
||||||
"@react-dnd/invariant": ^4.0.1
|
|
||||||
"@react-dnd/shallowequal": ^4.0.1
|
|
||||||
dnd-core: ^16.0.1
|
|
||||||
fast-deep-equal: ^3.1.3
|
|
||||||
hoist-non-react-statics: ^3.3.2
|
|
||||||
peerDependencies:
|
|
||||||
"@types/hoist-non-react-statics": ">= 3.3.1"
|
|
||||||
"@types/node": ">= 12"
|
|
||||||
"@types/react": ">= 16"
|
|
||||||
react: ">= 16.14"
|
|
||||||
peerDependenciesMeta:
|
|
||||||
"@types/hoist-non-react-statics":
|
|
||||||
optional: true
|
|
||||||
"@types/node":
|
|
||||||
optional: true
|
|
||||||
"@types/react":
|
|
||||||
optional: true
|
|
||||||
checksum: e8da2186aaafcd5bb41c090a995c963a7c3c73c20991667a2cfc0c800d7f7f73913414b2e61c437cdb6221bb2151bd5174088b8b42c17056a896fc4d1da5729f
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"react-dom@link:../web/node_modules/react-dom::locator=%40standardnotes%2Ftoast%40workspace%3Apackages%2Ftoast":
|
"react-dom@link:../web/node_modules/react-dom::locator=%40standardnotes%2Ftoast%40workspace%3Apackages%2Ftoast":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "react-dom@link:../web/node_modules/react-dom::locator=%40standardnotes%2Ftoast%40workspace%3Apackages%2Ftoast"
|
resolution: "react-dom@link:../web/node_modules/react-dom::locator=%40standardnotes%2Ftoast%40workspace%3Apackages%2Ftoast"
|
||||||
@@ -25948,15 +25869,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"redux@npm:^4.2.0":
|
|
||||||
version: 4.2.0
|
|
||||||
resolution: "redux@npm:4.2.0"
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime": ^7.9.2
|
|
||||||
checksum: 75f3955c89b3f18edf5411e5fb482aa2e4f41a416183e8802a6bf6472c4fc3d47675b8b321d147f8af8e0f616436ac507bf5a25f1c4d6180e797b549c7db2c1d
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"reflect-metadata@npm:^0.1.13":
|
"reflect-metadata@npm:^0.1.13":
|
||||||
version: 0.1.13
|
version: 0.1.13
|
||||||
resolution: "reflect-metadata@npm:0.1.13"
|
resolution: "reflect-metadata@npm:0.1.13"
|
||||||
|
|||||||
Reference in New Issue
Block a user