Skip to content

Commit e609e51

Browse files
authored
Merge pull request #3738 from udecode/focus/selection
Focus/selection
2 parents 0b9be46 + 0d209ba commit e609e51

File tree

10 files changed

+66
-38
lines changed

10 files changed

+66
-38
lines changed

.changeset/clever-buttons-attend.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@udecode/plate-selection': patch
3+
---
4+
5+
New api `editor.getApi(BlockSelectionPlugin).blockSelection.focus();`
6+
Fix the issue where block selection should not be unselect when the block context menu is open.

apps/www/content/docs/block-selection.mdx

+8-5
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,15 @@ export function BlockSelection() {
167167

168168
This component should be rendered inside each block element for consistent selection feedback. Plate UI is doing it in `plate-element.tsx`.
169169

170-
### Disable browser default scroll behavior
170+
## Prevent unselect
171171

172-
When selecting text and moving the cursor to the bottom of the page, the browser's default scroll behavior can conflict with the `BlockSelectionPlugin`. To mitigate this, you can add a non-selectable area to the right side of the editor:
172+
To prevent unselecting blocks when clicking on certain elements, add the `data-plate-prevent-unselect` attribute to those components
173173

174+
For example:
174175
```tsx
175-
<div className="absolute right-0 top-0 h-full w-4 select-none" />
176+
<YourSpecialButtoon data-plate-prevent-unselect />
176177
```
177178

178-
This helps prevent unexpected scrolling during selection operations.
179-
180179
## Plugins
181180

182181
### BlockSelectionPlugin
@@ -232,6 +231,10 @@ A set of IDs for the currently selected blocks.
232231

233232
## API
234233

234+
235+
### editor.api.blockSelection.focus
236+
Focuses the block selection shadow input. This input handles copy, delete, and paste events for selected blocks.
237+
235238
### editor.api.blockSelection.addSelectedRow
236239

237240
Adds a selected row to the block selection.

apps/www/content/docs/components/changelog.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ Use the [CLI](https://platejs.org/docs/components/cli) to install the latest ver
1111

1212
## November 2024 #16
1313

14+
### November 7 #16.4
15+
16+
- `block-context-menu`: prevent unselect when clicking on the context menu
17+
- `block-selection`: Add `editor.getApi(BlockSelectionPlugin).blockSelection.focus()` in onCloseAutoFocus.
18+
1419
### November 6 #16.3
1520

1621
- `editor`: add `overflow-x-hidden` to prevent horizontal scrolling

apps/www/src/registry/default/plate-ui/block-context-menu.tsx

+12-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,17 @@ export function BlockContextMenu({ children }: { children: React.ReactNode }) {
6161
);
6262

6363
return (
64-
<ContextMenu modal={false}>
64+
<ContextMenu
65+
onOpenChange={(open) => {
66+
if (!open) {
67+
// prevent unselect the block selection
68+
setTimeout(() => {
69+
api.blockMenu.hide();
70+
}, 0);
71+
}
72+
}}
73+
modal={false}
74+
>
6575
<ContextMenuTrigger
6676
asChild
6777
onContextMenu={(event) => {
@@ -83,6 +93,7 @@ export function BlockContextMenu({ children }: { children: React.ReactNode }) {
8393
className="w-64"
8494
onCloseAutoFocus={(e) => {
8595
e.preventDefault();
96+
editor.getApi(BlockSelectionPlugin).blockSelection.focus();
8697

8798
if (value === 'askAI') {
8899
editor.getApi(AIChatPlugin).aiChat.show();

packages/selection/src/react/BlockMenuPlugin.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const BlockMenuPlugin = createTPlatePlugin<BlockMenuConfig>({
7070
handlers: {
7171
onMouseDown: ({ event, getOptions }) => {
7272
if (event.button === 0 && getOptions().openId) {
73-
// event.preventDefault();
73+
event.preventDefault();
7474
api.blockMenu.hide();
7575
}
7676
if (event.button === 2) event.preventDefault();

packages/selection/src/react/BlockSelectionPlugin.tsx

+28-4
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import {
4646
} from './transforms/setBlockSelectionNodes';
4747
import {
4848
copySelectedBlocks,
49-
onChangeBlockSelection,
5049
pasteSelectedBlocks,
5150
selectInsertedBlocks,
5251
} from './utils';
@@ -62,6 +61,7 @@ export type BlockSelectionConfig = PluginConfig<
6261
query?: QueryNodeOptions;
6362
rightSelectionAreaClassName?: string;
6463
selectedIds?: Set<string>;
64+
shadowInputRef?: React.RefObject<HTMLInputElement>;
6565
onKeyDownSelecting?: (e: KeyboardEvent) => void;
6666
} & BlockSelectionSelectors,
6767
{
@@ -82,6 +82,7 @@ export type BlockSelectionApi = {
8282
setSelectedIds: (
8383
options: Partial<ChangedElements> & { ids?: string[] }
8484
) => void;
85+
focus: () => void;
8586
getNodes: () => TNodeEntry[];
8687
resetSelectedIds: () => void;
8788
selectedAll: () => void;
@@ -90,7 +91,7 @@ export type BlockSelectionApi = {
9091

9192
export const BlockSelectionAfterEditable: EditableSiblingComponent = () => {
9293
const editor = useEditorRef();
93-
const { api, getOption, getOptions, useOption } =
94+
const { api, getOption, getOptions, setOption, useOption } =
9495
useEditorPlugin<BlockSelectionConfig>({ key: 'blockSelection' });
9596
const isSelecting = useOption('isSelecting');
9697
const selectedIds = useOption('selectedIds');
@@ -102,11 +103,12 @@ export const BlockSelectionAfterEditable: EditableSiblingComponent = () => {
102103

103104
React.useEffect(() => {
104105
setIsMounted(true);
106+
setOption('shadowInputRef', inputRef);
105107

106108
return () => {
107109
setIsMounted(false);
108110
};
109-
}, []);
111+
}, [setOption]);
110112

111113
React.useEffect(() => {
112114
if (isSelecting && inputRef.current) {
@@ -278,6 +280,7 @@ export const BlockSelectionPlugin = createTPlatePlugin<BlockSelectionConfig>({
278280
maxLevel: 1,
279281
},
280282
selectedIds: new Set(),
283+
shadowInputRef: { current: null },
281284
},
282285
plugins: [BlockMenuPlugin],
283286
render: {
@@ -293,8 +296,22 @@ export const BlockSelectionPlugin = createTPlatePlugin<BlockSelectionConfig>({
293296
afterEditable: BlockSelectionAfterEditable,
294297
},
295298
handlers: {
296-
onChange: onChangeBlockSelection,
297299
onKeyDown: onKeyDownSelection,
300+
onMouseDown: ({ api, editor, event, getOptions }) => {
301+
const target = event.target as HTMLElement;
302+
303+
if (target.dataset.platePreventUnselect) return;
304+
305+
console.log(editor.getOption(BlockMenuPlugin, 'openId'), 'fj');
306+
307+
if (
308+
event.button === 0 &&
309+
getOptions().selectedIds!.size > 0 &&
310+
!editor.getOption(BlockMenuPlugin, 'openId')
311+
) {
312+
api.blockSelection.unselect();
313+
}
314+
},
298315
},
299316
})
300317
.extendOptions(({ getOptions }) => ({
@@ -303,6 +320,13 @@ export const BlockSelectionPlugin = createTPlatePlugin<BlockSelectionConfig>({
303320
}))
304321
.extendApi<Partial<BlockSelectionApi>>(
305322
({ editor, getOption, getOptions, setOption }) => ({
323+
focus: () => {
324+
const shadowInputRef = getOption('shadowInputRef');
325+
326+
if (shadowInputRef?.current) {
327+
shadowInputRef.current.focus({ preventScroll: true });
328+
}
329+
},
306330
getNodes: () => {
307331
const selectedIds = getOption('selectedIds');
308332

packages/selection/src/react/transforms/duplicateBlockSelectionNodes.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ export const duplicateBlockSelectionNodes = (
3030
})
3131
.filter(Boolean);
3232

33+
const api = editor.getApi(BlockSelectionPlugin);
34+
3335
setTimeout(() => {
34-
editor
35-
.getApi(BlockSelectionPlugin)
36-
.blockSelection.setSelectedIds({ ids } as any);
36+
api.blockSelection.setSelectedIds({ ids } as any);
3737
}, 0);
3838
};

packages/selection/src/react/transforms/setBlockSelectionNodes.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export const setBlockSelectionIndent = (
3535
indent: number,
3636
options?: SetNodesOptions
3737
) => {
38+
const api = editor.getApi(BlockSelectionPlugin);
39+
3840
withoutNormalizing(editor, () => {
39-
const blocks = editor
40-
.getApi(BlockSelectionPlugin)
41-
.blockSelection.getNodes();
41+
const blocks = api.blockSelection.getNodes();
4242

4343
blocks.forEach(([node, path]) => {
4444
const prevIndent = (node as any).indent ?? 0;

packages/selection/src/react/utils/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33
*/
44

55
export * from './copySelectedBlocks';
6-
export * from './onChangeBlockSelection';
76
export * from './pasteSelectedBlocks';
87
export * from './selectInsertedBlocks';

packages/selection/src/react/utils/onChangeBlockSelection.ts

-20
This file was deleted.

0 commit comments

Comments
 (0)