Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/07-collaboration/05-comments/src/style.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.comments-main-container {
.comments-main-container.bn-container {
align-items: center;
background-color: var(--bn-colors-disabled-background);
display: flex;
Expand Down
16 changes: 15 additions & 1 deletion packages/core/src/comments/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ function getUpdatedThreadPositions(doc: Node, markType: string) {
export const CommentsExtension = createExtension(
({
editor,
options: { schema: commentEditorSchema, threadStore, resolveUsers },
options: {
schema: commentEditorSchema,
threadStore,
resolveUsers,
confirmBeforeDiscard = true,
},
}: ExtensionOptions<{
/**
* The thread store implementation to use for storing and retrieving comment threads
Expand All @@ -76,6 +81,14 @@ export const CommentsExtension = createExtension(
* A schema to use for the comment editor (which allows you to customize the blocks and styles that are available in the comment editor)
*/
schema?: CustomBlockNoteSchema<any, any, any>;
/**
* Whether to ask the user for confirmation before discarding unsaved text
* in a comment composer (a new comment, a reply, or an in-progress edit)
* when it's dismissed (e.g. by clicking outside or pressing Escape).
*
* @default true
*/
confirmBeforeDiscard?: boolean;
}>) => {
if (!resolveUsers) {
throw new Error(
Expand Down Expand Up @@ -364,6 +377,7 @@ export const CommentsExtension = createExtension(
},
userStore,
commentEditorSchema,
confirmBeforeDiscard,
} as const;
},
);
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ export const ar: Dictionary = {
save_button_text: "حفظ",
cancel_button_text: "إلغاء",
deleted_reference_text: "تم حذف المحتوى الأصلي",
discard_pending_comment: "هل أنت متأكد أنك تريد تجاهل هذا التعليق؟",
actions: {
add_reaction: "أضف تفاعلًا",
resolve: "حل",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ export const de: Dictionary = {
save_button_text: "Speichern",
cancel_button_text: "Abbrechen",
deleted_reference_text: "Originalinhalt gelöscht",
discard_pending_comment: "Möchten Sie diesen Kommentar wirklich verwerfen?",
actions: {
add_reaction: "Reaktion hinzufügen",
resolve: "Lösen",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export const en = {
save_button_text: "Save",
cancel_button_text: "Cancel",
deleted_reference_text: "Original content deleted",
discard_pending_comment: "Are you sure you want to discard this comment?",
actions: {
add_reaction: "Add reaction",
resolve: "Resolve",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ export const es: Dictionary = {
save_button_text: "Guardar",
cancel_button_text: "Cancelar",
deleted_reference_text: "Contenido original eliminado",
discard_pending_comment: "¿Seguro que quieres descartar este comentario?",
actions: {
add_reaction: "Agregar reacción",
resolve: "Resolver",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/fa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ export const fa = {
save_button_text: "ذخیره",
cancel_button_text: "لغو",
deleted_reference_text: "محتوای اصلی حذف شد",
discard_pending_comment: "آیا مطمئن هستید که می‌خواهید این دیدگاه را نادیده بگیرید؟",
actions: {
add_reaction: "افزودن واکنش",
resolve: "حل کردن",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ export const fr: Dictionary = {
save_button_text: "Enregistrer",
cancel_button_text: "Annuler",
deleted_reference_text: "Contenu d'origine supprimé",
discard_pending_comment: "Voulez-vous vraiment abandonner ce commentaire ?",
actions: {
add_reaction: "Ajouter une réaction",
resolve: "Résoudre",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/he.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export const he: Dictionary = {
save_button_text: "שמור",
cancel_button_text: "בטל",
deleted_reference_text: "התוכן המקורי נמחק",
discard_pending_comment: "האם אתה בטוח שברצונך לבטל את התגובה הזו?",
actions: {
add_reaction: "הוסף תגובה",
resolve: "סמן כפתור",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/hr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ export const hr: Dictionary = {
save_button_text: "Spremi",
cancel_button_text: "Odustani",
deleted_reference_text: "Originalni sadržaj je obrisan",
discard_pending_comment: "Jeste li sigurni da želite odbaciti ovaj komentar?",
actions: {
add_reaction: "Dodaj reakciju",
resolve: "Riješi",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ export const is: Dictionary = {
save_button_text: "Vista",
cancel_button_text: "Hætta",
deleted_reference_text: "Upprunalegu efni eytt",
discard_pending_comment: "Ertu viss um að þú viljir henda þessari athugasemd?",
actions: {
add_reaction: "Bæta við viðbrögðum",
resolve: "Leysa",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ export const it: Dictionary = {
save_button_text: "Salva",
cancel_button_text: "Annulla",
deleted_reference_text: "Contenuto originale eliminato",
discard_pending_comment: "Vuoi davvero eliminare questo commento?",
actions: {
add_reaction: "Aggiungi reazione",
resolve: "Risolvi",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ export const ja: Dictionary = {
save_button_text: "保存",
cancel_button_text: "キャンセル",
deleted_reference_text: "元のコンテンツが削除されました",
discard_pending_comment: "このコメントを破棄してもよろしいですか?",
actions: {
add_reaction: "リアクションを追加",
resolve: "解決",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ export const ko: Dictionary = {
save_button_text: "저장",
cancel_button_text: "취소",
deleted_reference_text: "원본 콘텐츠 삭제됨",
discard_pending_comment: "이 댓글을 삭제하시겠습니까?",
actions: {
add_reaction: "반응 추가",
resolve: "해결",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export const nl: Dictionary = {
save_button_text: "Opslaan",
cancel_button_text: "Annuleren",
deleted_reference_text: "Originele inhoud verwijderd",
discard_pending_comment: "Weet je zeker dat je deze reactie wilt verwijderen?",
actions: {
add_reaction: "Reactie toevoegen",
resolve: "Oplossen",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/no.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ export const no: Dictionary = {
save_button_text: "Lagre",
cancel_button_text: "Avbryt",
deleted_reference_text: "Originalt innhold slettet",
discard_pending_comment: "Er du sikker på at du vil forkaste denne kommentaren?",
actions: {
add_reaction: "Legg til reaksjon",
resolve: "Løs",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ export const pl: Dictionary = {
save_button_text: "Zapisz",
cancel_button_text: "Anuluj",
deleted_reference_text: "Oryginalna treść usunięta",
discard_pending_comment: "Czy na pewno chcesz odrzucić ten komentarz?",
actions: {
add_reaction: "Dodaj reakcję",
resolve: "Rozwiąż",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ export const pt: Dictionary = {
save_button_text: "Salvar",
cancel_button_text: "Cancelar",
deleted_reference_text: "Conteúdo original excluído",
discard_pending_comment: "Tem certeza de que deseja descartar este comentário?",
actions: {
add_reaction: "Adicionar reação",
resolve: "Resolver",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ export const ru: Dictionary = {
save_button_text: "Сохранить",
cancel_button_text: "Отменить",
deleted_reference_text: "Исходный контент удалён",
discard_pending_comment: "Вы уверены, что хотите отменить этот комментарий?",
actions: {
add_reaction: "Добавить реакцию",
resolve: "Решить",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/sk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ export const sk = {
save_button_text: "Uložiť",
cancel_button_text: "Zrušiť",
deleted_reference_text: "Pôvodný obsah odstránený",
discard_pending_comment: "Naozaj chcete zahodiť tento komentár?",
actions: {
add_reaction: "Pridať reakciu",
resolve: "Vyriešiť",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/uk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ export const uk: Dictionary = {
save_button_text: "Зберегти",
cancel_button_text: "Скасувати",
deleted_reference_text: "Оригінальний вміст видалено",
discard_pending_comment: "Ви впевнені, що хочете відхилити цей коментар?",
actions: {
add_reaction: "Додати реакцію",
resolve: "Вирішити",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/uz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ export const uz: Dictionary = {
save_button_text: "Saqlash",
cancel_button_text: "Bekor qilish",
deleted_reference_text: "Asl tarkib o‘chirildi",
discard_pending_comment: "Haqiqatan ham bu izohni bekor qilmoqchimisiz?",
actions: {
add_reaction: "Reaksiya qo‘shish",
resolve: "Hal qilish",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export const vi: Dictionary = {
save_button_text: "Lưu",
cancel_button_text: "Hủy",
deleted_reference_text: "Nội dung gốc đã bị xóa",
discard_pending_comment: "Bạn có chắc chắn muốn hủy bình luận này không?",
actions: {
add_reaction: "Thêm phản ứng",
resolve: "Giải quyết",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/zh-tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ export const zhTW: Dictionary = {
save_button_text: "儲存",
cancel_button_text: "取消",
deleted_reference_text: "原始內容已刪除",
discard_pending_comment: "確定要捨棄此評論嗎?",
actions: {
add_reaction: "新增回應",
resolve: "解決",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/i18n/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ export const zh: Dictionary = {
save_button_text: "保存",
cancel_button_text: "取消",
deleted_reference_text: "原始内容已删除",
discard_pending_comment: "确定要放弃此评论吗?",
actions: {
add_reaction: "添加反应",
resolve: "解决",
Expand Down
29 changes: 12 additions & 17 deletions packages/react/src/components/Comments/FloatingComposer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
BlockNoteEditor,
BlockSchema,
DefaultBlockSchema,
DefaultInlineContentSchema,
Expand All @@ -9,19 +10,16 @@ import {
StyleSchema,
} from "@blocknote/core";
import { CommentsExtension } from "@blocknote/core/comments";
import { TextSelection } from "@tiptap/pm/state";
import { memo, useCallback } from "react";

import {
Components,
useComponentsContext,
} from "../../editor/ComponentsContext.js";
import { useCreateBlockNote } from "../../hooks/useCreateBlockNote.js";
import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
import { useExtension } from "../../hooks/useExtension.js";
import { useDictionary } from "../../i18n/dictionary.js";
import { CommentEditor } from "./CommentEditor.js";
import { defaultCommentEditorSchema } from "./defaultCommentEditorSchema.js";
import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
import { TextSelection } from "@tiptap/pm/state";

type FloatingComposerActionsProps = {
isFocused: boolean;
Expand Down Expand Up @@ -59,25 +57,22 @@ export function FloatingComposer<
B extends BlockSchema = DefaultBlockSchema,
I extends InlineContentSchema = DefaultInlineContentSchema,
S extends StyleSchema = DefaultStyleSchema,
>() {
>(props: {
/**
* The (empty) editor used to compose the new comment. Created and owned by
* the `FloatingComposerController`, so it can check for unsaved text before
* the composer is dismissed.
*/
newCommentEditor: BlockNoteEditor<any, any, any>;
}) {
Comment thread
YousefED marked this conversation as resolved.
const editor = useBlockNoteEditor<B, I, S>();
const newCommentEditor = props.newCommentEditor;

const comments = useExtension(CommentsExtension);

const Components = useComponentsContext()!;
const dict = useDictionary();

const newCommentEditor = useCreateBlockNote({
trailingBlock: false,
dictionary: {
...dict,
placeholders: {
emptyDocument: dict.placeholders.new_comment,
},
},
schema: comments.commentEditorSchema || defaultCommentEditorSchema,
});

const onSave = useCallback(async () => {
// (later) For REST API, we should implement a loading state and error state
await comments.createThread({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import { flip, offset, shift } from "@floating-ui/react";
import { ComponentProps, FC, useMemo } from "react";

import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
import { useCreateBlockNote } from "../../hooks/useCreateBlockNote.js";
import { useEditorState } from "../../hooks/useEditorState.js";
import { useExtension, useExtensionState } from "../../hooks/useExtension.js";
import { useDictionary } from "../../i18n/dictionary.js";
import { FloatingUIOptions } from "../Popovers/FloatingUIOptions.js";
import { PositionPopover } from "../Popovers/PositionPopover.js";
import { confirmDiscardUnsavedComment } from "./confirmDiscardUnsavedComment.js";
import { defaultCommentEditorSchema } from "./defaultCommentEditorSchema.js";
import { FloatingComposer } from "./FloatingComposer.js";

export default function FloatingComposerController<
Expand All @@ -32,6 +36,7 @@ export default function FloatingComposerController<
portalElement?: HTMLElement | null;
}) {
const editor = useBlockNoteEditor<B, I, S>();
const dict = useDictionary();

const comments = useExtension(CommentsExtension);

Expand All @@ -40,6 +45,24 @@ export default function FloatingComposerController<
selector: (state) => state.pendingComment,
});

// The editor used to compose a new comment. We own it here (rather than in
// `FloatingComposer`) so that the dismiss handler below can check whether the
// user has typed anything before discarding it. A fresh editor is created for
// each pending comment, so it always starts empty.
const newCommentEditor = useCreateBlockNote(
{
trailingBlock: false,
dictionary: {
...dict,
placeholders: {
emptyDocument: dict.placeholders.new_comment,
},
},
schema: comments.commentEditorSchema || defaultCommentEditorSchema,
},
[pendingComment],
);

const position = useEditorState({
editor,
selector: ({ editor }) =>
Expand All @@ -60,6 +83,19 @@ export default function FloatingComposerController<
// open state.
onOpenChange: (open) => {
if (!open) {
// If the user has typed a comment that hasn't been saved yet, ask
// for confirmation before discarding it (e.g. when clicking
// outside the composer). Otherwise the unsaved comment is lost.
if (
!confirmDiscardUnsavedComment({
hasUnsavedContent: !newCommentEditor.isEmpty,
confirmBeforeDiscard: comments.confirmBeforeDiscard,
message: dict.comments.discard_pending_comment,
})
) {
// Keep the composer open so the user can continue editing.
return;
}
comments.stopPendingComment();
editor.focus();
}
Expand All @@ -78,7 +114,14 @@ export default function FloatingComposerController<
...props.floatingUIOptions?.elementProps,
},
}),
[comments, editor, pendingComment, props.floatingUIOptions],
[
comments,
dict,
editor,
newCommentEditor,
pendingComment,
props.floatingUIOptions,
],
);

// nice to have improvements would be:
Expand All @@ -93,7 +136,7 @@ export default function FloatingComposerController<
portalElement={props.portalElement}
{...floatingUIOptions}
>
<Component />
<Component newCommentEditor={newCommentEditor} />
</PositionPopover>
);
}
Loading
Loading