Skip to content

Commit ee895ad

Browse files
committed
refactor: select image/file node in super when clicking inside it
1 parent 14a8294 commit ee895ad

File tree

4 files changed

+86
-22
lines changed

4 files changed

+86
-22
lines changed

packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,8 @@ export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JS
7777
),
7878
editor.registerNodeTransform(FileNode, (node) => {
7979
/**
80-
* Before this was added, we used to wrap the file node in a paragraph node,
81-
* which caused issues with selection. We no longer do that, but for existing
82-
* notes that have this, we use this transform to remove the wrapper node.
80+
* When adding the node we wrap it with a paragraph to avoid insertion errors,
81+
* however that causes issues with selection. We unwrap the node to fix that.
8382
*/
8483
const parent = node.getParent()
8584
if (!parent) {

packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/Nodes/FileComponent.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
22
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3-
import { ElementFormatType, NodeKey } from 'lexical'
3+
import { $getNodeByKey, CLICK_COMMAND, COMMAND_PRIORITY_LOW, ElementFormatType, NodeKey } from 'lexical'
44
import { useApplication } from '@/Components/ApplicationProvider'
55
import FilePreview from '@/Components/FilePreview/FilePreview'
66
import { FileItem } from '@standardnotes/snjs'
77
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
8+
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'
89

910
export type FileComponentProps = Readonly<{
1011
className: Readonly<{
@@ -66,6 +67,29 @@ export function FileComponent({ className, format, nodeKey, fileUuid, zoomLevel,
6667
[editor, setZoomLevel],
6768
)
6869

70+
const [isSelected, setSelected] = useLexicalNodeSelection(nodeKey)
71+
72+
useEffect(() => {
73+
return editor.registerCommand<MouseEvent>(
74+
CLICK_COMMAND,
75+
(event) => {
76+
if (blockWrapperRef.current?.contains(event.target as Node)) {
77+
event.preventDefault()
78+
79+
$getNodeByKey(nodeKey)?.selectEnd()
80+
81+
setTimeout(() => {
82+
setSelected(!isSelected)
83+
})
84+
return true
85+
}
86+
87+
return false
88+
},
89+
COMMAND_PRIORITY_LOW,
90+
)
91+
}, [editor, isSelected, nodeKey, setSelected])
92+
6993
if (!file) {
7094
return (
7195
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>

packages/web/src/javascripts/Components/SuperEditor/Plugins/RemoteImagePlugin/RemoteImageComponent.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { isDesktopApplication } from '@/Utils'
55
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
66
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
77
import { classNames, Platform } from '@standardnotes/snjs'
8-
import { ElementFormatType, NodeKey } from 'lexical'
9-
import { useCallback, useState } from 'react'
8+
import { $getNodeByKey, CLICK_COMMAND, COMMAND_PRIORITY_LOW, ElementFormatType, NodeKey } from 'lexical'
9+
import { useCallback, useEffect, useRef, useState } from 'react'
1010
import { $createFileNode } from '../EncryptedFilePlugin/Nodes/FileUtils'
1111
import { RemoteImageNode } from './RemoteImageNode'
1212
import { isIOS } from '@standardnotes/ui-services'
13+
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'
1314

1415
type Props = {
1516
src: string
@@ -66,9 +67,33 @@ const RemoteImageComponent = ({ className, src, alt, node, format, nodeKey }: Pr
6667
const isBase64OrDataUrl = src.startsWith('data:')
6768
const canShowSaveButton = application.isNativeMobileWeb() || isDesktopApplication() || isBase64OrDataUrl
6869

70+
const ref = useRef<HTMLDivElement>(null)
71+
const [isSelected, setSelected] = useLexicalNodeSelection(nodeKey)
72+
73+
useEffect(() => {
74+
return editor.registerCommand<MouseEvent>(
75+
CLICK_COMMAND,
76+
(event) => {
77+
if (ref.current?.contains(event.target as Node)) {
78+
event.preventDefault()
79+
80+
$getNodeByKey(nodeKey)?.selectEnd()
81+
82+
setTimeout(() => {
83+
setSelected(!isSelected)
84+
})
85+
return true
86+
}
87+
88+
return false
89+
},
90+
COMMAND_PRIORITY_LOW,
91+
)
92+
}, [editor, isSelected, nodeKey, setSelected])
93+
6994
return (
7095
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
71-
<div className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
96+
<div ref={ref} className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
7297
<img
7398
alt={alt}
7499
src={src}

packages/web/src/javascripts/Components/SuperEditor/Plugins/RemoteImagePlugin/RemoteImagePlugin.tsx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import Button from '../../Lexical/UI/Button'
55
import { DialogActions } from '../../Lexical/UI/Dialog'
66
import TextInput from '../../Lexical/UI/TextInput'
77
import { INSERT_REMOTE_IMAGE_COMMAND } from '../Commands'
8-
import { $createRemoteImageNode } from './RemoteImageNode'
9-
import { $wrapNodeInElement } from '@lexical/utils'
8+
import { $createRemoteImageNode, RemoteImageNode } from './RemoteImageNode'
9+
import { mergeRegister, $wrapNodeInElement } from '@lexical/utils'
1010

1111
export function InsertRemoteImageDialog({ onClose }: { onClose: () => void }) {
1212
const [url, setURL] = useState('')
@@ -35,20 +35,36 @@ export default function RemoteImagePlugin() {
3535
const [editor] = useLexicalComposerContext()
3636

3737
useEffect(() => {
38-
return editor.registerCommand<string>(
39-
INSERT_REMOTE_IMAGE_COMMAND,
40-
(payload) => {
41-
const imageNode = $createRemoteImageNode(payload)
42-
$insertNodes([imageNode])
43-
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
44-
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd()
45-
}
46-
const newLineNode = $createParagraphNode()
47-
imageNode.getParentOrThrow().insertAfter(newLineNode)
38+
return mergeRegister(
39+
editor.registerCommand<string>(
40+
INSERT_REMOTE_IMAGE_COMMAND,
41+
(payload) => {
42+
const imageNode = $createRemoteImageNode(payload)
43+
$insertNodes([imageNode])
44+
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
45+
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd()
46+
}
47+
const newLineNode = $createParagraphNode()
48+
imageNode.getParentOrThrow().insertAfter(newLineNode)
4849

49-
return true
50-
},
51-
COMMAND_PRIORITY_NORMAL,
50+
return true
51+
},
52+
COMMAND_PRIORITY_NORMAL,
53+
),
54+
editor.registerNodeTransform(RemoteImageNode, (node) => {
55+
/**
56+
* When adding the node we wrap it with a paragraph to avoid insertion errors,
57+
* however that causes issues with selection. We unwrap the node to fix that.
58+
*/
59+
const parent = node.getParent()
60+
if (!parent) {
61+
return
62+
}
63+
if (parent.getChildrenSize() === 1) {
64+
parent.insertBefore(node)
65+
parent.remove()
66+
}
67+
}),
5268
)
5369
}, [editor])
5470

0 commit comments

Comments
 (0)