@dxos/react-ui-editor 0.6.3-next.2f65b78 → 0.6.3-staging.0f23fb2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +61 -33
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/extensions/comments.d.ts +8 -8
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/package.json +26 -26
- package/src/extensions/comments.ts +95 -37
|
@@ -12,11 +12,21 @@ import {
|
|
|
12
12
|
type ChangeDesc,
|
|
13
13
|
type EditorState,
|
|
14
14
|
} from '@codemirror/state';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
hoverTooltip,
|
|
17
|
+
keymap,
|
|
18
|
+
type Command,
|
|
19
|
+
Decoration,
|
|
20
|
+
EditorView,
|
|
21
|
+
type Rect,
|
|
22
|
+
type PluginValue,
|
|
23
|
+
ViewPlugin,
|
|
24
|
+
} from '@codemirror/view';
|
|
16
25
|
import sortBy from 'lodash.sortby';
|
|
17
26
|
import { useEffect, useMemo, useState } from 'react';
|
|
18
27
|
|
|
19
|
-
import {
|
|
28
|
+
import { type ThreadType } from '@braneframe/types';
|
|
29
|
+
import { debounce, type UnsubscribeCallback } from '@dxos/async';
|
|
20
30
|
import { log } from '@dxos/log';
|
|
21
31
|
import { nonNullable } from '@dxos/util';
|
|
22
32
|
|
|
@@ -108,30 +118,42 @@ const styles = EditorView.baseTheme({
|
|
|
108
118
|
borderRadius: '2px',
|
|
109
119
|
transition: 'background-color 0.1s ease',
|
|
110
120
|
},
|
|
111
|
-
|
|
121
|
+
// Light theme.
|
|
122
|
+
'&light .cm-comment': {
|
|
123
|
+
backgroundColor: getToken('extend.colors.yellow.50'),
|
|
112
124
|
mixBlendMode: 'darken',
|
|
113
125
|
borderColor: getToken('extend.colors.yellow.100'),
|
|
114
126
|
},
|
|
115
|
-
'&
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
'&light .cm-comment:hover': { backgroundColor: getToken('extend.colors.yellow.100') },
|
|
128
|
+
'&light .cm-comment-current': {
|
|
129
|
+
backgroundColor: getToken('extend.colors.primary.100'),
|
|
130
|
+
borderColor: getToken('extend.colors.primary.200'),
|
|
118
131
|
},
|
|
119
|
-
'&light .cm-comment': {
|
|
120
|
-
backgroundColor: getToken('extend.colors.
|
|
132
|
+
'&light .cm-comment-current:hover': {
|
|
133
|
+
backgroundColor: getToken('extend.colors.primary.150'),
|
|
134
|
+
borderColor: getToken('extend.colors.primary.250'),
|
|
121
135
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
'&light .cm-comment-current:hover': { backgroundColor: getToken('extend.colors.yellow.150') },
|
|
136
|
+
|
|
137
|
+
// Dark theme.
|
|
125
138
|
'&dark .cm-comment': {
|
|
126
139
|
color: getToken('extend.colors.yellow.50'),
|
|
127
|
-
backgroundColor: getToken('extend.colors.yellow.
|
|
140
|
+
backgroundColor: getToken('extend.colors.yellow.800'),
|
|
141
|
+
borderColor: getToken('extend.colors.yellow.700'),
|
|
142
|
+
mixBlendMode: 'plus-lighter',
|
|
143
|
+
},
|
|
144
|
+
'&dark .cm-comment:hover': {
|
|
145
|
+
backgroundColor: getToken('extend.colors.yellow.700'),
|
|
146
|
+
borderColor: getToken('extend.colors.yellow.650'),
|
|
128
147
|
},
|
|
129
|
-
'&dark .cm-comment:hover': { backgroundColor: getToken('extend.colors.yellow.800') },
|
|
130
148
|
'&dark .cm-comment-current': {
|
|
131
|
-
color: getToken('extend.colors.
|
|
132
|
-
backgroundColor: getToken('extend.colors.
|
|
149
|
+
color: getToken('extend.colors.primary.50'),
|
|
150
|
+
backgroundColor: getToken('extend.colors.primary.800'),
|
|
151
|
+
borderColor: getToken('extend.colors.primary.700'),
|
|
152
|
+
},
|
|
153
|
+
'&dark .cm-comment-current:hover': {
|
|
154
|
+
backgroundColor: getToken('extend.colors.primary.700'),
|
|
155
|
+
borderColor: getToken('extend.colors.primary.650'),
|
|
133
156
|
},
|
|
134
|
-
'&dark .cm-comment-current:hover': { backgroundColor: getToken('extend.colors.yellow.900') },
|
|
135
157
|
});
|
|
136
158
|
|
|
137
159
|
const createCommentMark = (id: string, isCurrent: boolean) =>
|
|
@@ -585,34 +607,52 @@ export const selectionOverlapsComment = (state: EditorState): boolean => {
|
|
|
585
607
|
return false;
|
|
586
608
|
};
|
|
587
609
|
|
|
588
|
-
/**
|
|
589
|
-
* Check if there is one or more active (non-empty) selections in the editor state.
|
|
590
|
-
*/
|
|
591
610
|
const hasActiveSelection = (state: EditorState): boolean => {
|
|
592
611
|
return state.selection.ranges.some((range) => !range.empty);
|
|
593
612
|
};
|
|
594
613
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
614
|
+
class ExternalCommentSync implements PluginValue {
|
|
615
|
+
private unsubscribe: () => void;
|
|
616
|
+
|
|
617
|
+
constructor(
|
|
618
|
+
view: EditorView,
|
|
619
|
+
id: string,
|
|
620
|
+
subscribe: (sink: () => void) => UnsubscribeCallback,
|
|
621
|
+
getThreads: () => ThreadType[],
|
|
622
|
+
) {
|
|
623
|
+
const updateComments = () => {
|
|
624
|
+
const threads = getThreads();
|
|
625
|
+
const comments = threads
|
|
626
|
+
.filter(nonNullable)
|
|
627
|
+
.filter((thread) => thread.anchor)
|
|
628
|
+
.map((thread) => ({ id: thread.id, cursor: thread.anchor! }));
|
|
629
|
+
|
|
603
630
|
if (id === view.state.facet(documentId)) {
|
|
604
|
-
view.dispatch({
|
|
605
|
-
effects: setComments.of({ id, comments: comments ?? [] }),
|
|
606
|
-
});
|
|
631
|
+
queueMicrotask(() => view.dispatch({ effects: setComments.of({ id, comments }) }));
|
|
607
632
|
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
this.unsubscribe = subscribe(updateComments);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
destroy = () => {
|
|
639
|
+
this.unsubscribe();
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export const createExternalCommentSync = (
|
|
644
|
+
id: string,
|
|
645
|
+
subscribe: (sink: () => void) => UnsubscribeCallback,
|
|
646
|
+
getThreads: () => ThreadType[],
|
|
647
|
+
): Extension =>
|
|
648
|
+
ViewPlugin.fromClass(
|
|
649
|
+
class {
|
|
650
|
+
constructor(view: EditorView) {
|
|
651
|
+
return new ExternalCommentSync(view, id, subscribe, getThreads);
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
);
|
|
611
655
|
|
|
612
|
-
/**
|
|
613
|
-
* Hook provides an extension to compute the current comment state under the selection.
|
|
614
|
-
* NOTE(Zan): I think this conceptually belongs in 'formatting.ts' but we can't import ESM modules there atm.
|
|
615
|
-
*/
|
|
616
656
|
export const useCommentState = (): [{ comment: boolean; selection: boolean }, Extension] => {
|
|
617
657
|
const [state, setState] = useState<{ comment: boolean; selection: boolean }>({
|
|
618
658
|
comment: false,
|
|
@@ -635,6 +675,24 @@ export const useCommentState = (): [{ comment: boolean; selection: boolean }, Ex
|
|
|
635
675
|
return [state, observer];
|
|
636
676
|
};
|
|
637
677
|
|
|
678
|
+
/**
|
|
679
|
+
* @deprecated This hook will be removed in future versions. Use the new comment sync extension instead.
|
|
680
|
+
* Update comments state field.
|
|
681
|
+
*/
|
|
682
|
+
export const useComments = (view: EditorView | null | undefined, id: string, comments?: Comment[]) => {
|
|
683
|
+
useEffect(() => {
|
|
684
|
+
if (view) {
|
|
685
|
+
// Check same document.
|
|
686
|
+
// NOTE: Hook might be called before editor state is updated.
|
|
687
|
+
if (id === view.state.facet(documentId)) {
|
|
688
|
+
view.dispatch({
|
|
689
|
+
effects: setComments.of({ id, comments: comments ?? [] }),
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
};
|
|
695
|
+
|
|
638
696
|
/**
|
|
639
697
|
* Hook provides an extension to listen for comment clicks and invoke a handler.
|
|
640
698
|
*/
|