Skip to content

Commit 6590032

Browse files
Smart editor selection: Ensure start word is fully selected if selection spans multiple words
1 parent a595c8b commit 6590032

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

frontend/src/plate/plugins/plugin-sets/default.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ELEMENT_MALTEKST, ELEMENT_PLACEHOLDER } from '@app/plate/plugins/elemen
1616
import { createNormalizeNodePlugin } from '@app/plate/plugins/normalize-node';
1717
import { createPageBreakPlugin } from '@app/plate/plugins/page-break';
1818
import { createProhibitDeletionPlugin } from '@app/plate/plugins/prohibit-deletion/prohibit-deletion';
19+
import { createSelectionPlugin } from '@app/plate/plugins/selection';
1920

2021
export const defaultPlugins: PlatePlugin[] = [
2122
createInsertDataPlugin(),
@@ -90,6 +91,7 @@ export const defaultPlugins: PlatePlugin[] = [
9091
createPageBreakPlugin(),
9192
createProhibitDeletionPlugin(),
9293
createCopyPlugin(),
94+
createSelectionPlugin(),
9395
createNormalizeNodePlugin(),
9496
createCustomAbbreviationPlugin(),
9597
];
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { createPluginFactory, getNode, getSelectionText, isText } from '@udecode/plate-common';
2+
import { Range } from 'slate';
3+
4+
export const createSelectionPlugin = createPluginFactory({
5+
key: 'selection',
6+
withOverrides: (editor) => {
7+
const { select } = editor;
8+
9+
editor.select = (target) => {
10+
if (!Range.isRange(target) || Range.isCollapsed(target)) {
11+
return select(target);
12+
}
13+
14+
if (!containsMultipleWords(getSelectionText(editor))) {
15+
return select(target);
16+
}
17+
18+
const node = getNode(editor, target.anchor.path);
19+
20+
if (!isText(node)) {
21+
return select(target);
22+
}
23+
24+
const isForward = Range.isForward(target);
25+
26+
const anchorOffset = isForward
27+
? getWordStart(node.text, target.anchor.offset)
28+
: getWordEnd(node.text, target.anchor.offset);
29+
30+
const focusOffset = isForward
31+
? getWordEnd(node.text, target.focus.offset)
32+
: getWordStart(node.text, target.focus.offset);
33+
34+
return select({
35+
anchor: { ...target.anchor, offset: anchorOffset },
36+
focus: { ...target.focus, offset: focusOffset },
37+
});
38+
};
39+
40+
return editor;
41+
},
42+
});
43+
44+
const START_REGEX = /\S+$/;
45+
const END_REGEX = /^\S*/;
46+
const CONTAINS_REGEX = /\s/;
47+
48+
const getWordStart = (text: string, offset: number): number => {
49+
const match = text.slice(0, offset).match(START_REGEX);
50+
51+
return match ? offset - match[0].length : offset;
52+
};
53+
54+
const getWordEnd = (text: string, offset: number): number => {
55+
const match = text.slice(offset).match(END_REGEX);
56+
57+
return match ? offset + match[0].length : offset;
58+
};
59+
60+
const containsMultipleWords = (text: string): boolean => CONTAINS_REGEX.test(text);

0 commit comments

Comments
 (0)