refactor: select image/file node in super when clicking inside it
This commit is contained in:
@@ -77,9 +77,8 @@ export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JS
|
|||||||
),
|
),
|
||||||
editor.registerNodeTransform(FileNode, (node) => {
|
editor.registerNodeTransform(FileNode, (node) => {
|
||||||
/**
|
/**
|
||||||
* Before this was added, we used to wrap the file node in a paragraph node,
|
* When adding the node we wrap it with a paragraph to avoid insertion errors,
|
||||||
* which caused issues with selection. We no longer do that, but for existing
|
* however that causes issues with selection. We unwrap the node to fix that.
|
||||||
* notes that have this, we use this transform to remove the wrapper node.
|
|
||||||
*/
|
*/
|
||||||
const parent = node.getParent()
|
const parent = node.getParent()
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { ElementFormatType, NodeKey } from 'lexical'
|
import { $getNodeByKey, CLICK_COMMAND, COMMAND_PRIORITY_LOW, ElementFormatType, NodeKey } from 'lexical'
|
||||||
import { useApplication } from '@/Components/ApplicationProvider'
|
import { useApplication } from '@/Components/ApplicationProvider'
|
||||||
import FilePreview from '@/Components/FilePreview/FilePreview'
|
import FilePreview from '@/Components/FilePreview/FilePreview'
|
||||||
import { FileItem } from '@standardnotes/snjs'
|
import { FileItem } from '@standardnotes/snjs'
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||||
|
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'
|
||||||
|
|
||||||
export type FileComponentProps = Readonly<{
|
export type FileComponentProps = Readonly<{
|
||||||
className: Readonly<{
|
className: Readonly<{
|
||||||
@@ -66,6 +67,29 @@ export function FileComponent({ className, format, nodeKey, fileUuid, zoomLevel,
|
|||||||
[editor, setZoomLevel],
|
[editor, setZoomLevel],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [isSelected, setSelected] = useLexicalNodeSelection(nodeKey)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return editor.registerCommand<MouseEvent>(
|
||||||
|
CLICK_COMMAND,
|
||||||
|
(event) => {
|
||||||
|
if (blockWrapperRef.current?.contains(event.target as Node)) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
$getNodeByKey(nodeKey)?.selectEnd()
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setSelected(!isSelected)
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
COMMAND_PRIORITY_LOW,
|
||||||
|
)
|
||||||
|
}, [editor, isSelected, nodeKey, setSelected])
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return (
|
return (
|
||||||
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
|
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import { isDesktopApplication } from '@/Utils'
|
|||||||
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||||
import { classNames, Platform } from '@standardnotes/snjs'
|
import { classNames, Platform } from '@standardnotes/snjs'
|
||||||
import { ElementFormatType, NodeKey } from 'lexical'
|
import { $getNodeByKey, CLICK_COMMAND, COMMAND_PRIORITY_LOW, ElementFormatType, NodeKey } from 'lexical'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { $createFileNode } from '../EncryptedFilePlugin/Nodes/FileUtils'
|
import { $createFileNode } from '../EncryptedFilePlugin/Nodes/FileUtils'
|
||||||
import { RemoteImageNode } from './RemoteImageNode'
|
import { RemoteImageNode } from './RemoteImageNode'
|
||||||
import { isIOS } from '@standardnotes/ui-services'
|
import { isIOS } from '@standardnotes/ui-services'
|
||||||
|
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
src: string
|
src: string
|
||||||
@@ -66,9 +67,33 @@ const RemoteImageComponent = ({ className, src, alt, node, format, nodeKey }: Pr
|
|||||||
const isBase64OrDataUrl = src.startsWith('data:')
|
const isBase64OrDataUrl = src.startsWith('data:')
|
||||||
const canShowSaveButton = application.isNativeMobileWeb() || isDesktopApplication() || isBase64OrDataUrl
|
const canShowSaveButton = application.isNativeMobileWeb() || isDesktopApplication() || isBase64OrDataUrl
|
||||||
|
|
||||||
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
|
const [isSelected, setSelected] = useLexicalNodeSelection(nodeKey)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return editor.registerCommand<MouseEvent>(
|
||||||
|
CLICK_COMMAND,
|
||||||
|
(event) => {
|
||||||
|
if (ref.current?.contains(event.target as Node)) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
$getNodeByKey(nodeKey)?.selectEnd()
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setSelected(!isSelected)
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
COMMAND_PRIORITY_LOW,
|
||||||
|
)
|
||||||
|
}, [editor, isSelected, nodeKey, setSelected])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
|
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
|
||||||
<div className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
|
<div ref={ref} className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
|
||||||
<img
|
<img
|
||||||
alt={alt}
|
alt={alt}
|
||||||
src={src}
|
src={src}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import Button from '../../Lexical/UI/Button'
|
|||||||
import { DialogActions } from '../../Lexical/UI/Dialog'
|
import { DialogActions } from '../../Lexical/UI/Dialog'
|
||||||
import TextInput from '../../Lexical/UI/TextInput'
|
import TextInput from '../../Lexical/UI/TextInput'
|
||||||
import { INSERT_REMOTE_IMAGE_COMMAND } from '../Commands'
|
import { INSERT_REMOTE_IMAGE_COMMAND } from '../Commands'
|
||||||
import { $createRemoteImageNode } from './RemoteImageNode'
|
import { $createRemoteImageNode, RemoteImageNode } from './RemoteImageNode'
|
||||||
import { $wrapNodeInElement } from '@lexical/utils'
|
import { mergeRegister, $wrapNodeInElement } from '@lexical/utils'
|
||||||
|
|
||||||
export function InsertRemoteImageDialog({ onClose }: { onClose: () => void }) {
|
export function InsertRemoteImageDialog({ onClose }: { onClose: () => void }) {
|
||||||
const [url, setURL] = useState('')
|
const [url, setURL] = useState('')
|
||||||
@@ -35,20 +35,36 @@ export default function RemoteImagePlugin() {
|
|||||||
const [editor] = useLexicalComposerContext()
|
const [editor] = useLexicalComposerContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return editor.registerCommand<string>(
|
return mergeRegister(
|
||||||
INSERT_REMOTE_IMAGE_COMMAND,
|
editor.registerCommand<string>(
|
||||||
(payload) => {
|
INSERT_REMOTE_IMAGE_COMMAND,
|
||||||
const imageNode = $createRemoteImageNode(payload)
|
(payload) => {
|
||||||
$insertNodes([imageNode])
|
const imageNode = $createRemoteImageNode(payload)
|
||||||
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
|
$insertNodes([imageNode])
|
||||||
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd()
|
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
|
||||||
}
|
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd()
|
||||||
const newLineNode = $createParagraphNode()
|
}
|
||||||
imageNode.getParentOrThrow().insertAfter(newLineNode)
|
const newLineNode = $createParagraphNode()
|
||||||
|
imageNode.getParentOrThrow().insertAfter(newLineNode)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
COMMAND_PRIORITY_NORMAL,
|
COMMAND_PRIORITY_NORMAL,
|
||||||
|
),
|
||||||
|
editor.registerNodeTransform(RemoteImageNode, (node) => {
|
||||||
|
/**
|
||||||
|
* When adding the node we wrap it with a paragraph to avoid insertion errors,
|
||||||
|
* however that causes issues with selection. We unwrap the node to fix that.
|
||||||
|
*/
|
||||||
|
const parent = node.getParent()
|
||||||
|
if (!parent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (parent.getChildrenSize() === 1) {
|
||||||
|
parent.insertBefore(node)
|
||||||
|
parent.remove()
|
||||||
|
}
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
}, [editor])
|
}, [editor])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user