Skip to content

Commit dd4b9a3

Browse files
fix: Image block selection clears on mouse leave in Safari (#2613)
* Made resize handles change `display` style to toggle visibility instead of mount/unmount * Added fix for React * Updated test snapshots * Updated snapshot
1 parent a850078 commit dd4b9a3

17 files changed

Lines changed: 74 additions & 40 deletions

File tree

packages/core/src/blocks/File/helpers/render/createResizableFileBlockWrapper.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ export const createResizableFileBlockWrapper = (
4747
const leftResizeHandle = document.createElement("div");
4848
leftResizeHandle.className = "bn-resize-handle";
4949
leftResizeHandle.style.left = "4px";
50+
leftResizeHandle.style.display = "none";
51+
resizeHandlesContainerElement.appendChild(leftResizeHandle);
5052
const rightResizeHandle = document.createElement("div");
5153
rightResizeHandle.className = "bn-resize-handle";
5254
rightResizeHandle.style.right = "4px";
55+
rightResizeHandle.style.display = "none";
56+
resizeHandlesContainerElement.appendChild(rightResizeHandle);
5357

5458
// This element ensures `mousemove` and `mouseup` events are captured while
5559
// resizing when the cursor is over the wrapper content. This is because
@@ -75,13 +79,9 @@ export const createResizableFileBlockWrapper = (
7579
// offset from when the resize began, and which resize handle is being used.
7680
const windowMouseMoveHandler = (event: MouseEvent | TouchEvent) => {
7781
if (!resizeParams) {
78-
if (
79-
!editor.isEditable &&
80-
resizeHandlesContainerElement.contains(leftResizeHandle) &&
81-
resizeHandlesContainerElement.contains(rightResizeHandle)
82-
) {
83-
resizeHandlesContainerElement.removeChild(leftResizeHandle);
84-
resizeHandlesContainerElement.removeChild(rightResizeHandle);
82+
if (!editor.isEditable) {
83+
leftResizeHandle.style.display = "none";
84+
rightResizeHandle.style.display = "none";
8585
}
8686

8787
return;
@@ -128,14 +128,12 @@ export const createResizableFileBlockWrapper = (
128128
const windowMouseUpHandler = (event: MouseEvent | TouchEvent) => {
129129
// Hides the drag handles if the cursor is no longer over the element.
130130
if (
131-
(!event.target ||
132-
!wrapper.contains(event.target as Node) ||
133-
!editor.isEditable) &&
134-
resizeHandlesContainerElement.contains(leftResizeHandle) &&
135-
resizeHandlesContainerElement.contains(rightResizeHandle)
131+
!event.target ||
132+
!wrapper.contains(event.target as Node) ||
133+
!editor.isEditable
136134
) {
137-
resizeHandlesContainerElement.removeChild(leftResizeHandle);
138-
resizeHandlesContainerElement.removeChild(rightResizeHandle);
135+
leftResizeHandle.style.display = "none";
136+
rightResizeHandle.style.display = "none";
139137
}
140138

141139
if (!resizeParams) {
@@ -158,8 +156,8 @@ export const createResizableFileBlockWrapper = (
158156
// Shows the resize handles when hovering over the wrapper with the cursor.
159157
const wrapperMouseEnterHandler = () => {
160158
if (editor.isEditable) {
161-
resizeHandlesContainerElement.appendChild(leftResizeHandle);
162-
resizeHandlesContainerElement.appendChild(rightResizeHandle);
159+
leftResizeHandle.style.display = "";
160+
rightResizeHandle.style.display = "";
163161
}
164162
};
165163
// Hides the resize handles when the cursor leaves the wrapper, unless the
@@ -176,13 +174,9 @@ export const createResizableFileBlockWrapper = (
176174
return;
177175
}
178176

179-
if (
180-
editor.isEditable &&
181-
resizeHandlesContainerElement.contains(leftResizeHandle) &&
182-
resizeHandlesContainerElement.contains(rightResizeHandle)
183-
) {
184-
resizeHandlesContainerElement.removeChild(leftResizeHandle);
185-
resizeHandlesContainerElement.removeChild(rightResizeHandle);
177+
if (editor.isEditable) {
178+
leftResizeHandle.style.display = "none";
179+
rightResizeHandle.style.display = "none";
186180
}
187181
};
188182

packages/react/src/blocks/Audio/block.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ export const AudioBlock = (
8383
export const ReactAudioBlock = createReactBlockSpec(
8484
createAudioBlockConfig,
8585
(config) => ({
86+
meta: {
87+
fileBlockAccept: ["audio/*"],
88+
},
8689
render: AudioBlock,
8790
parse: audioParse(config),
8891
toExternalHTML: AudioToExternalHTML,

packages/react/src/blocks/File/helpers/render/ResizableFileBlockWrapper.tsx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -178,22 +178,24 @@ export const ResizableFileBlockWrapper = (
178178
ref={ref}
179179
>
180180
{props.children}
181-
{(hovered || resizeParams) && (
182-
<>
183-
<div
184-
className={"bn-resize-handle"}
185-
style={{ left: "4px" }}
186-
onMouseDown={leftResizeHandleMouseDownHandler}
187-
onTouchStart={leftResizeHandleMouseDownHandler}
188-
/>
189-
<div
190-
className={"bn-resize-handle"}
191-
style={{ right: "4px" }}
192-
onMouseDown={rightResizeHandleMouseDownHandler}
193-
onTouchStart={rightResizeHandleMouseDownHandler}
194-
/>
195-
</>
196-
)}
181+
<div
182+
className={"bn-resize-handle"}
183+
style={{
184+
left: "4px",
185+
display: hovered || resizeParams ? "initial" : "none",
186+
}}
187+
onMouseDown={leftResizeHandleMouseDownHandler}
188+
onTouchStart={leftResizeHandleMouseDownHandler}
189+
/>
190+
<div
191+
className={"bn-resize-handle"}
192+
style={{
193+
right: "4px",
194+
display: hovered || resizeParams ? "initial" : "none",
195+
}}
196+
onMouseDown={rightResizeHandleMouseDownHandler}
197+
onTouchStart={rightResizeHandleMouseDownHandler}
198+
/>
197199
{/* This element ensures `mousemove` and `mouseup` events are captured
198200
while resizing when the cursor is over the wrapper content. This is
199201
because embeds are treated as separate HTML documents, so if the

packages/react/src/blocks/Image/block.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,12 @@ export const ImageBlock = (
8888
export const ReactImageBlock = createReactBlockSpec(
8989
createImageBlockConfig,
9090
(config) => ({
91+
meta: {
92+
fileBlockAccept: ["image/*"],
93+
},
9194
render: ImageBlock,
9295
parse: imageParse(config),
9396
toExternalHTML: ImageToExternalHTML,
97+
runsBefore: ["file"],
9498
}),
9599
);

packages/react/src/blocks/Video/block.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ export const VideoBlock = (
8282
export const ReactVideoBlock = createReactBlockSpec(
8383
createVideoBlockConfig,
8484
(config) => ({
85+
meta: {
86+
fileBlockAccept: ["video/*"],
87+
},
8588
render: VideoBlock,
8689
parse: videoParse(config),
8790
toExternalHTML: VideoToExternalHTML,
91+
runsBefore: ["file"],
8892
}),
8993
);

packages/react/src/schema/ReactBlockSpec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ export function createReactBlockSpec<
242242
blockProps={block.props}
243243
propSchema={blockConfig.propSchema}
244244
domAttributes={this.blockContentDOMAttributes}
245+
isFileBlock={
246+
blockImplementation.meta?.fileBlockAccept !== undefined
247+
}
245248
>
246249
<BlockContent
247250
block={block as any}

packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`Test ServerBlockNoteEditor > converts to HTML (blocksToFullHTML) 1`] = `"<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="heading" data-background-color="blue" data-text-color="yellow" data-text-alignment="right" data-level="2"><h2 class="bn-inline-content"><strong><u>Heading </u></strong><em><s>2</s></em></h2></div><div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2"><div class="bn-block-content" data-content-type="paragraph" data-background-color="red"><p class="bn-inline-content">Paragraph</p></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="3"><div class="bn-block" data-node-type="blockContainer" data-id="3"><div class="bn-block-content" data-content-type="bulletListItem"><p class="bn-inline-content">list item</p></div></div></div></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="4"><div class="bn-block" data-node-type="blockContainer" data-id="4"><div class="bn-block-content" data-content-type="image" data-name="Example" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper" style="position: relative; width: 256px;"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" src="exampleURL" alt="Example" draggable="false"></div><p class="bn-file-caption">Caption</p></div></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="5"><div class="bn-block" data-node-type="blockContainer" data-id="5"><div class="bn-block-content" data-content-type="image" data-name="Example" data-url="exampleURL" data-caption="Caption" data-show-preview="false" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper" style="position: relative;"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name">Example</p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div>"`;
3+
exports[`Test ServerBlockNoteEditor > converts to HTML (blocksToFullHTML) 1`] = `"<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="heading" data-background-color="blue" data-text-color="yellow" data-text-alignment="right" data-level="2"><h2 class="bn-inline-content"><strong><u>Heading </u></strong><em><s>2</s></em></h2></div><div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2"><div class="bn-block-content" data-content-type="paragraph" data-background-color="red"><p class="bn-inline-content">Paragraph</p></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="3"><div class="bn-block" data-node-type="blockContainer" data-id="3"><div class="bn-block-content" data-content-type="bulletListItem"><p class="bn-inline-content">list item</p></div></div></div></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="4"><div class="bn-block" data-node-type="blockContainer" data-id="4"><div class="bn-block-content" data-content-type="image" data-name="Example" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper" style="position: relative; width: 256px;"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" src="exampleURL" alt="Example" draggable="false"><div class="bn-resize-handle" style="left: 4px; display: none;"></div><div class="bn-resize-handle" style="right: 4px; display: none;"></div></div><p class="bn-file-caption">Caption</p></div></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="5"><div class="bn-block" data-node-type="blockContainer" data-id="5"><div class="bn-block-content" data-content-type="image" data-name="Example" data-url="exampleURL" data-caption="Caption" data-show-preview="false" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper" style="position: relative;"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name">Example</p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div>"`;
44

55
exports[`Test ServerBlockNoteEditor > converts to and from HTML (blocksToHTMLLossy) 1`] = `"<h2 style="background-color: rgb(221, 235, 241); color: rgb(223, 171, 1); text-align: right;" data-background-color="blue" data-text-color="yellow" data-text-alignment="right" data-level="2"><strong><u>Heading </u></strong><em><s>2</s></em></h2><p style="background-color: rgb(251, 228, 228);" data-background-color="red" data-nesting-level="1">Paragraph</p><ul><li data-nesting-level="1"><p class="bn-inline-content">list item</p></li></ul><figure data-name="Example" data-url="exampleURL" data-caption="Caption" data-preview-width="256"><img src="exampleURL" alt="Example" width="256"><figcaption>Caption</figcaption></figure><div data-name="Example" data-url="exampleURL" data-caption="Caption" data-show-preview="false" data-preview-width="256"><a href="exampleURL">Example</a><p>Caption</p></div>"`;
66

tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
alt="BlockNote image"
1919
draggable="false"
2020
/>
21+
<div class="bn-resize-handle" style="left: 4px; display: none;"></div>
22+
<div class="bn-resize-handle" style="right: 4px; display: none;"></div>
2123
</div>
2224
</div>
2325
</div>

tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
>
1717
<div class="bn-visual-media-wrapper">
1818
<img class="bn-visual-media" src="exampleURL" alt="example" draggable="false" />
19+
<div class="bn-resize-handle" style="left: 4px; display: none;"></div>
20+
<div class="bn-resize-handle" style="right: 4px; display: none;"></div>
1921
</div>
2022
<p class="bn-file-caption">Caption</p>
2123
</div>

tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
>
1616
<div class="bn-visual-media-wrapper">
1717
<img class="bn-visual-media" src="exampleURL" alt="Caption" draggable="false" />
18+
<div class="bn-resize-handle" style="left: 4px; display: none;"></div>
19+
<div class="bn-resize-handle" style="right: 4px; display: none;"></div>
1820
</div>
1921
<p class="bn-file-caption">Caption</p>
2022
</div>
@@ -36,6 +38,8 @@
3638
>
3739
<div class="bn-visual-media-wrapper">
3840
<img class="bn-visual-media" src="exampleURL" alt="Caption" draggable="false" />
41+
<div class="bn-resize-handle" style="left: 4px; display: none;"></div>
42+
<div class="bn-resize-handle" style="right: 4px; display: none;"></div>
3943
</div>
4044
<p class="bn-file-caption">Caption</p>
4145
</div>

0 commit comments

Comments
 (0)