Skip to content

Commit 8115e33

Browse files
authored
Merge pull request #700 from Harbour-Enterprises/har-10152_new-selection-approach
refactor(selection): new approach
2 parents 8587e28 + 284838d commit 8115e33

6 files changed

Lines changed: 93 additions & 50 deletions

File tree

packages/super-editor/src/assets/styles/elements/prosemirror.css

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,10 @@ https://github.com/ProseMirror/prosemirror-tables/blob/master/demo/index.html
353353
.ProseMirror-active-search-match {
354354
background-color: #ff6a0054;
355355
}
356-
357-
.ProseMirror span::selection {
356+
.ProseMirror span.sd-custom-selection::selection {
358357
background: transparent;
359358
}
359+
.sd-custom-selection {
360+
background-color: #d9d9d9;
361+
border-radius: 0.1em;
362+
}

packages/super-editor/src/assets/styles/extensions/comments.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
.sd-editor-comment-highlight:hover {
66
background-color: #1354ff55;
77
}
8+
9+
.sd-editor-comment-highlight.sd-custom-selection {
10+
background-color: #d6c0c6 !important;
11+
}

packages/super-editor/src/assets/styles/extensions/list-items.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121

2222
/* temporary fix */
2323
.sd-editor-list-item-node-view .sd-custom-selection {
24-
font-size: inherit!important;
24+
font-size: inherit !important;
2525
}

packages/super-editor/src/assets/styles/layout/global.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,3 @@
1414
a {
1515
text-decoration: auto;
1616
}
17-
18-
.sd-custom-selection {
19-
background-color: #accef7;
20-
}

packages/super-editor/src/components/SuperEditor.vue

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup>
22
import 'tippy.js/dist/tippy.css';
33
import { NSkeleton } from 'naive-ui';
4-
import { ref, onMounted, onBeforeUnmount, shallowRef, reactive, markRaw, onDeactivated } from 'vue';
4+
import { ref, onMounted, onBeforeUnmount, shallowRef, reactive, markRaw } from 'vue';
55
import { Editor } from '@/index.js';
66
import { getStarterExtensions } from '@extensions/index.js';
77
import SlashMenu from './slash-menu/SlashMenu.vue';
@@ -223,26 +223,9 @@ const handleSuperEditorClick = (event) => {
223223
}
224224
};
225225
226-
const handleClickOutside = (event) => {
227-
const pmElement = editorElem.value?.querySelector('.ProseMirror');
228-
const isInsideEditor = pmElement?.contains(event.target);
229-
230-
if (!isInsideEditor) {
231-
editor.value?.setOptions({
232-
focusTarget: event.target,
233-
});
234-
}
235-
};
236-
237226
onMounted(() => {
238227
initializeData();
239228
if (props.options?.suppressSkeletonLoader || !props.options?.collaborationProvider) editorReady.value = true;
240-
241-
document.addEventListener('mousedown', handleClickOutside);
242-
});
243-
244-
onDeactivated(() => {
245-
document.removeEventListener('mousedown', handleClickOutside);
246229
});
247230
248231
const handleMarginClick = (event) => {

packages/super-editor/src/extensions/custom-selection/custom-selection.js

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,110 @@
11
import { Extension } from '@core/Extension.js';
2-
import { AllSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
2+
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
33
import { Decoration, DecorationSet } from 'prosemirror-view';
44

55
export const CustomSelectionPluginKey = new PluginKey('CustomSelection');
66

7+
const handleClickOutside = (event, editor) => {
8+
const editorElem = editor?.options?.element;
9+
if (!editorElem) return;
10+
11+
const isInsideEditor = editorElem?.contains(event.target);
12+
13+
if (!isInsideEditor) {
14+
editor.setOptions({
15+
focusTarget: event.target,
16+
});
17+
} else {
18+
editor.setOptions({
19+
focusTarget: null,
20+
});
21+
}
22+
};
23+
24+
function getFocusMeta(tr) {
25+
return tr.getMeta(CustomSelectionPluginKey);
26+
}
27+
28+
function setFocusMeta(tr, value) {
29+
return tr.setMeta(CustomSelectionPluginKey, value);
30+
}
31+
32+
function getFocusState(state) {
33+
return CustomSelectionPluginKey.getState(state);
34+
}
35+
736
export const CustomSelection = Extension.create({
837
name: 'customSelection',
9-
1038
addPmPlugins() {
39+
const editor = this.editor;
1140
const customSelectionPlugin = new Plugin({
1241
key: CustomSelectionPluginKey,
13-
1442
state: {
15-
init() {
16-
return DecorationSet.empty;
43+
init: () => false,
44+
apply: (tr, value) => {
45+
return getFocusMeta(tr) ?? value;
1746
},
18-
apply(tr, oldDecorationSet, oldState, newState) {
19-
const sel = tr.selection;
20-
let newDecos = [];
21-
22-
// Only apply to text selections or whole doc selections
23-
if (sel.from !== sel.to && (tr.doc.resolve(sel.from).parent.isTextblock || sel instanceof AllSelection)) {
24-
newDecos.push(
25-
Decoration.inline(sel.from, sel.to, {
26-
class: 'sd-custom-selection',
27-
}),
28-
);
29-
}
47+
},
48+
view: () => {
49+
document?.addEventListener('mousedown', (event) => handleClickOutside(event, editor));
3050

31-
return DecorationSet.create(newState.doc, newDecos);
32-
},
51+
return {
52+
destroy: () => {
53+
document?.removeEventListener('mouseout', handleClickOutside);
54+
},
55+
};
3356
},
3457
props: {
3558
handleDOMEvents: {
36-
focusout: (view, event) => {
37-
const isDropDownOption = this.editor.options.focusTarget?.getAttribute('data-dropdown-option');
38-
if (document.activeElement && !event.relatedTarget && !view.state.selection.empty && !isDropDownOption) {
59+
mousedown: (view) => {
60+
const { selection } = view.state;
61+
const isToolbarButton = this.editor.options.focusTarget?.closest('.toolbar-button');
62+
63+
if (!isToolbarButton) {
64+
view.dispatch(setFocusMeta(view.state.tr, false));
65+
}
66+
if (!selection.empty) {
3967
this.editor.setOptions({
4068
lastSelection: view.state.selection,
4169
});
4270
const clearSelectionTr = view.state.tr.setSelection(TextSelection.create(view.state.doc, 0));
4371

4472
view.dispatch(clearSelectionTr);
4573
}
46-
return false;
74+
},
75+
focus: (view) => {
76+
const isToolbarButton = this.editor.options.focusTarget?.closest('.toolbar-button');
77+
78+
if (isToolbarButton) {
79+
return;
80+
}
81+
82+
view.dispatch(setFocusMeta(view.state.tr, false));
83+
},
84+
85+
blur: (view) => {
86+
const isToolbarButton = this.editor.options.focusTarget?.closest('.toolbar-button');
87+
88+
if (isToolbarButton) {
89+
view.dispatch(setFocusMeta(view.state.tr, true));
90+
return;
91+
}
92+
93+
view.dispatch(setFocusMeta(view.state.tr, false));
4794
},
4895
},
49-
decorations(state) {
50-
return CustomSelectionPluginKey.getState(state);
96+
decorations: (state) => {
97+
const { selection, doc } = state;
98+
99+
if (selection.empty || !getFocusState(state)) {
100+
return null;
101+
}
102+
103+
return DecorationSet.create(doc, [
104+
Decoration.inline(selection.from, selection.to, {
105+
class: 'sd-custom-selection',
106+
}),
107+
]);
51108
},
52109
},
53110
});

0 commit comments

Comments
 (0)