@dxos/react-ui-editor 0.6.2-main.8a232a5 → 0.6.2-main.bd2f985
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 +84 -24
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +8 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/package.json +25 -25
- package/src/components/Toolbar/Toolbar.tsx +2 -2
- package/src/extensions/automerge/sync.ts +4 -1
- package/src/extensions/comments.ts +95 -19
|
@@ -101,29 +101,48 @@ export const commentsState = StateField.define<CommentsState>({
|
|
|
101
101
|
//
|
|
102
102
|
|
|
103
103
|
const styles = EditorView.baseTheme({
|
|
104
|
-
'
|
|
105
|
-
|
|
104
|
+
'.cm-comment, .cm-comment-current': {
|
|
105
|
+
cursor: 'pointer',
|
|
106
|
+
borderWidth: '1px',
|
|
107
|
+
borderStyle: 'solid',
|
|
108
|
+
borderRadius: '2px',
|
|
109
|
+
transition: 'background-color 0.1s ease',
|
|
110
|
+
},
|
|
111
|
+
'&light .cm-comment, &light .cm-comment-current': {
|
|
112
|
+
mixBlendMode: 'darken',
|
|
113
|
+
borderColor: getToken('extend.colors.yellow.100'),
|
|
114
|
+
},
|
|
115
|
+
'&dark .cm-comment, &dark .cm-comment-current': {
|
|
116
|
+
mixBlendMode: 'plus-lighter',
|
|
117
|
+
borderColor: getToken('extend.colors.yellow.900'),
|
|
118
|
+
},
|
|
106
119
|
'&light .cm-comment': {
|
|
107
120
|
backgroundColor: getToken('extend.colors.yellow.50'),
|
|
108
121
|
},
|
|
109
|
-
'&light .cm-comment
|
|
110
|
-
|
|
111
|
-
},
|
|
122
|
+
'&light .cm-comment:hover': { backgroundColor: getToken('extend.colors.yellow.100') },
|
|
123
|
+
'&light .cm-comment-current': { backgroundColor: getToken('extend.colors.yellow.100') },
|
|
124
|
+
'&light .cm-comment-current:hover': { backgroundColor: getToken('extend.colors.yellow.150') },
|
|
112
125
|
'&dark .cm-comment': {
|
|
113
126
|
color: getToken('extend.colors.yellow.50'),
|
|
114
127
|
backgroundColor: getToken('extend.colors.yellow.900'),
|
|
115
128
|
},
|
|
129
|
+
'&dark .cm-comment:hover': { backgroundColor: getToken('extend.colors.yellow.800') },
|
|
116
130
|
'&dark .cm-comment-current': {
|
|
117
131
|
color: getToken('extend.colors.yellow.100'),
|
|
118
132
|
backgroundColor: getToken('extend.colors.yellow.950'),
|
|
119
133
|
},
|
|
134
|
+
'&dark .cm-comment-current:hover': { backgroundColor: getToken('extend.colors.yellow.900') },
|
|
120
135
|
});
|
|
121
136
|
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
const createCommentMark = (id: string, isCurrent: boolean) => {
|
|
138
|
+
return Decoration.mark({
|
|
139
|
+
class: isCurrent ? 'cm-comment-current' : 'cm-comment',
|
|
140
|
+
attributes: {
|
|
141
|
+
'data-testid': 'cm-comment',
|
|
142
|
+
'data-comment-id': id,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
};
|
|
127
146
|
|
|
128
147
|
/**
|
|
129
148
|
* Decorate ranges.
|
|
@@ -145,17 +164,39 @@ const commentsDecorations = EditorView.decorations.compute([commentsState], (sta
|
|
|
145
164
|
return undefined;
|
|
146
165
|
}
|
|
147
166
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
} else {
|
|
151
|
-
return commentMark.range(range.from, range.to);
|
|
152
|
-
}
|
|
167
|
+
const mark = createCommentMark(comment.comment.id, comment.comment.id === current);
|
|
168
|
+
return mark.range(range.from, range.to);
|
|
153
169
|
})
|
|
154
170
|
.filter(nonNullable);
|
|
155
171
|
|
|
156
172
|
return Decoration.set(decorations);
|
|
157
173
|
});
|
|
158
174
|
|
|
175
|
+
const commentClickedEffect = StateEffect.define<string>();
|
|
176
|
+
|
|
177
|
+
const handleCommentClick = EditorView.domEventHandlers({
|
|
178
|
+
click: (event, view) => {
|
|
179
|
+
let target = event.target as HTMLElement;
|
|
180
|
+
const editorRoot = view.dom;
|
|
181
|
+
|
|
182
|
+
// Traverse up the DOM tree looking for an element with data-comment-id
|
|
183
|
+
// Stop if we reach the editor root or find the comment id
|
|
184
|
+
while (target && target !== editorRoot && !target.hasAttribute('data-comment-id')) {
|
|
185
|
+
target = target.parentElement as HTMLElement;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check if we found a comment id and are still within the editor
|
|
189
|
+
if (target && target !== editorRoot) {
|
|
190
|
+
const commentId = target.getAttribute('data-comment-id');
|
|
191
|
+
if (commentId) {
|
|
192
|
+
view.dispatch({ effects: commentClickedEffect.of(commentId) });
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return false;
|
|
198
|
+
},
|
|
199
|
+
});
|
|
159
200
|
//
|
|
160
201
|
// Cut-and-paste.
|
|
161
202
|
//
|
|
@@ -372,6 +413,7 @@ export const comments = (options: CommentsOptions = {}): Extension => {
|
|
|
372
413
|
documentId.of(options.id),
|
|
373
414
|
commentsState,
|
|
374
415
|
commentsDecorations,
|
|
416
|
+
handleCommentClick,
|
|
375
417
|
styles,
|
|
376
418
|
|
|
377
419
|
//
|
|
@@ -533,6 +575,13 @@ export const selectionOverlapsComment = (state: EditorState): boolean => {
|
|
|
533
575
|
return false;
|
|
534
576
|
};
|
|
535
577
|
|
|
578
|
+
/**
|
|
579
|
+
* Check if there is one or more active (non-empty) selections in the editor state.
|
|
580
|
+
*/
|
|
581
|
+
const hasActiveSelection = (state: EditorState): boolean => {
|
|
582
|
+
return state.selection.ranges.some((range) => !range.empty);
|
|
583
|
+
};
|
|
584
|
+
|
|
536
585
|
/**
|
|
537
586
|
* Update comments state field.
|
|
538
587
|
*/
|
|
@@ -554,18 +603,45 @@ export const useComments = (view: EditorView | null | undefined, id: string, com
|
|
|
554
603
|
* Hook provides an extension to compute the current comment state under the selection.
|
|
555
604
|
* NOTE(Zan): I think this conceptually belongs in 'formatting.ts' but we can't import ESM modules there atm.
|
|
556
605
|
*/
|
|
557
|
-
export const useCommentState = (): [boolean, Extension] => {
|
|
558
|
-
const [
|
|
606
|
+
export const useCommentState = (): [{ comment: boolean; selection: boolean }, Extension] => {
|
|
607
|
+
const [state, setState] = useState<{ comment: boolean; selection: boolean }>({
|
|
608
|
+
comment: false,
|
|
609
|
+
selection: false,
|
|
610
|
+
});
|
|
559
611
|
|
|
560
612
|
const observer = useMemo(
|
|
561
613
|
() =>
|
|
562
614
|
EditorView.updateListener.of((update) => {
|
|
563
615
|
if (update.docChanged || update.selectionSet) {
|
|
564
|
-
|
|
616
|
+
setState({
|
|
617
|
+
comment: selectionOverlapsComment(update.state),
|
|
618
|
+
selection: hasActiveSelection(update.state),
|
|
619
|
+
});
|
|
565
620
|
}
|
|
566
621
|
}),
|
|
567
622
|
[],
|
|
568
623
|
);
|
|
569
624
|
|
|
570
|
-
return [
|
|
625
|
+
return [state, observer];
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Hook provides an extension to listen for comment clicks and invoke a handler.
|
|
630
|
+
*/
|
|
631
|
+
export const useCommentClickListener = (onCommentClick: (commentId: string) => void): Extension => {
|
|
632
|
+
const observer = useMemo(
|
|
633
|
+
() =>
|
|
634
|
+
EditorView.updateListener.of((update) => {
|
|
635
|
+
update.transactions.forEach((transaction) => {
|
|
636
|
+
transaction.effects.forEach((effect) => {
|
|
637
|
+
if (effect.is(commentClickedEffect)) {
|
|
638
|
+
onCommentClick(effect.value);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
}),
|
|
643
|
+
[onCommentClick],
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
return observer;
|
|
571
647
|
};
|