@blocknote/react
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
420 lines (419 loc) • 13.4 kB
JavaScript
import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
import { f as t, g as n, i as r, l as i, m as a, n as o, p as s, r as c, s as l, t as u } from "./defaultCommentEditorSchema-3DnX6Knw.js";
import d, { memo as f, useCallback as p, useEffect as m, useMemo as h, useRef as g, useState as _, useSyncExternalStore as v } from "react";
import { CommentsExtension as y } from "@blocknote/core/comments";
import { flip as b, offset as x, shift as S } from "@floating-ui/react";
import { mergeCSSClasses as C } from "@blocknote/core";
import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
import { RiArrowGoBackFill as D, RiCheckFill as O, RiDeleteBinFill as k, RiEditFill as A, RiEmotionLine as j, RiMoreFill as M } from "react-icons/ri";
//#region src/components/Comments/EmojiMartPicker.tsx
var N;
async function P() {
return N || (N = (async () => {
let [e, t] = await Promise.all([import("emoji-mart"), import("@emoji-mart/data")]), n = "default" in e ? e.default : e, r = "default" in t ? t.default : t;
return await n.init({ data: r }), {
emojiMart: n,
emojiData: r
};
})(), N);
}
function F(e) {
let t = g(null), n = g(null);
return n.current && n.current.update(e), m(() => ((async () => {
let { emojiMart: r } = await P();
n.current = new r.Picker({
...e,
ref: t
});
})(), () => {
n.current = null;
}), []), d.createElement("div", { ref: t });
}
//#endregion
//#region src/components/Comments/EmojiPicker.tsx
var I = (e) => {
let [t, r] = _(!1), a = i(), o = n(), s = o.editor?.portalElement;
if (!s) throw Error("Portal root not found");
return /* @__PURE__ */ E(a.Generic.Popover.Root, {
open: t,
portalRoot: s,
children: [/* @__PURE__ */ T(a.Generic.Popover.Trigger, { children: /* @__PURE__ */ T("div", {
onClick: (n) => {
n.preventDefault(), n.stopPropagation(), r(!t), e.onOpenChange?.(!t);
},
style: {
display: "flex",
justifyContent: "center",
alignItems: "center"
},
children: e.children
}) }), /* @__PURE__ */ T(a.Generic.Popover.Content, {
className: "bn-emoji-picker-popover",
variant: "panel-popover",
children: /* @__PURE__ */ T(F, {
perLine: 7,
onClickOutside: () => {
r(!1), e.onOpenChange?.(!1);
},
onEmojiSelect: (t) => {
e.onEmojiSelect(t), r(!1), e.onOpenChange?.(!1);
},
theme: o?.colorSchemePreference
})
})]
});
};
//#endregion
//#region src/components/Comments/useUsers.ts
function L(e) {
return R([e]).get(e);
}
function R(e) {
let n = t(y).userStore, r = p(() => {
let t = /* @__PURE__ */ new Map();
for (let r of e) {
let e = n.getUser(r);
e && t.set(r, e);
}
return t;
}, [n, e]), i = h(() => ({ current: r() }), [r]);
return v(p((t) => {
let a = n.subscribe((e) => {
i.current = r(), t();
});
return n.loadUsers(e), a;
}, [
n,
r,
e,
i
]), () => i.current);
}
//#endregion
//#region src/components/Comments/ReactionBadge.tsx
var z = (e) => {
let n = i(), r = l(), a = t(y), o = e.comment.reactions.find((t) => t.emoji === e.emoji);
if (!o) throw Error("Trying to render reaction badge for non-existing reaction");
let [s, c] = _([]), u = R(s);
return /* @__PURE__ */ T(n.Generic.Badge.Root, {
className: C("bn-badge", "bn-comment-reaction"),
text: o.userIds.length.toString(),
icon: o.emoji,
isSelected: a.threadStore.auth.canDeleteReaction(e.comment, o.emoji),
onClick: () => e.onReactionSelect(o.emoji),
onMouseEnter: () => c(o.userIds),
mainTooltip: r.comments.reactions.reacted_by,
secondaryTooltip: `${Array.from(u.values()).map((e) => e.username).join("\n")}`
}, o.emoji);
}, B = f(({ isEmpty: e, comment: t, isEditing: n, threadStore: r, onReactionSelect: i, onEditSubmit: a, onEditCancel: o, onEmojiPickerOpenChange: s, Components: c, dict: l }) => {
let u = r.auth.canAddReaction(t);
return /* @__PURE__ */ E(w, { children: [t.reactions.length > 0 && !n && /* @__PURE__ */ E(c.Generic.Badge.Group, {
className: C("bn-badge-group", "bn-comment-reactions"),
children: [t.reactions.map((e) => /* @__PURE__ */ T(z, {
comment: t,
emoji: e.emoji,
onReactionSelect: i
}, e.emoji)), u && /* @__PURE__ */ T(I, {
onEmojiSelect: (e) => i(e.native),
onOpenChange: s,
children: /* @__PURE__ */ T(c.Generic.Badge.Root, {
className: C("bn-badge", "bn-comment-add-reaction"),
text: "+",
icon: /* @__PURE__ */ T(j, { size: 16 }),
mainTooltip: l.comments.actions.add_reaction
})
})]
}), n && /* @__PURE__ */ E(c.Generic.Toolbar.Root, {
variant: "action-toolbar",
className: C("bn-action-toolbar", "bn-comment-actions"),
children: [/* @__PURE__ */ T(c.Generic.Toolbar.Button, {
mainTooltip: l.comments.save_button_text,
variant: "compact",
onClick: a,
isDisabled: e,
children: l.comments.save_button_text
}), /* @__PURE__ */ T(c.Generic.Toolbar.Button, {
className: "bn-button",
mainTooltip: l.comments.cancel_button_text,
variant: "compact",
onClick: o,
children: l.comments.cancel_button_text
})]
})] });
}), V = ({ comment: e, thread: n, showResolveButton: r }) => {
let a = t(y), s = l(), d = c({
initialContent: e.body,
trailingBlock: !1,
dictionary: {
...s,
placeholders: { emptyDocument: s.placeholders.edit_comment }
},
schema: a.commentEditorSchema || u
}), f = i(), [m, h] = _(!1), [g, v] = _(!1), b = a.threadStore, x = p(() => {
h(!0);
}, []), S = p(() => {
d.replaceBlocks(d.document, e.body), h(!1);
}, [d, e.body]), w = p(async (t) => {
await b.updateComment({
commentId: e.id,
comment: { body: d.document },
threadId: n.id
}), h(!1);
}, [
e,
n.id,
d,
b
]), N = p(async () => {
await b.deleteComment({
commentId: e.id,
threadId: n.id
});
}, [
e,
n.id,
b
]), P = p(async (t) => {
b.auth.canAddReaction(e, t) ? await b.addReaction({
threadId: n.id,
commentId: e.id,
emoji: t
}) : b.auth.canDeleteReaction(e, t) && await b.deleteReaction({
threadId: n.id,
commentId: e.id,
emoji: t
});
}, [
b,
e,
n.id
]), F = p(async () => {
await b.resolveThread({ threadId: n.id });
}, [n.id, b]), R = p(async () => {
await b.unresolveThread({ threadId: n.id });
}, [n.id, b]), z = L(e.userId);
if (!e.body) return null;
let V, H = b.auth.canAddReaction(e), U = b.auth.canDeleteComment(e), W = b.auth.canUpdateComment(e), G = r && (n.resolved ? b.auth.canUnresolveThread(n) : b.auth.canResolveThread(n));
m || (V = /* @__PURE__ */ E(f.Generic.Toolbar.Root, {
className: C("bn-action-toolbar", "bn-comment-actions"),
variant: "action-toolbar",
children: [
H && /* @__PURE__ */ T(I, {
onEmojiSelect: (e) => P(e.native),
onOpenChange: v,
children: /* @__PURE__ */ T(f.Generic.Toolbar.Button, {
mainTooltip: s.comments.actions.add_reaction,
variant: "compact",
children: /* @__PURE__ */ T(j, { size: 16 })
}, "add-reaction")
}),
G && (n.resolved ? /* @__PURE__ */ T(f.Generic.Toolbar.Button, {
mainTooltip: s.comments.actions.reopen,
variant: "compact",
onClick: R,
children: /* @__PURE__ */ T(D, { size: 16 })
}, "reopen") : /* @__PURE__ */ T(f.Generic.Toolbar.Button, {
mainTooltip: s.comments.actions.resolve,
variant: "compact",
onClick: F,
children: /* @__PURE__ */ T(O, { size: 16 })
}, "resolve")),
(U || W) && /* @__PURE__ */ E(f.Generic.Menu.Root, {
position: "bottom-start",
children: [/* @__PURE__ */ T(f.Generic.Menu.Trigger, { children: /* @__PURE__ */ T(f.Generic.Toolbar.Button, {
mainTooltip: s.comments.actions.more_actions,
variant: "compact",
children: /* @__PURE__ */ T(M, { size: 16 })
}, "more-actions") }), /* @__PURE__ */ E(f.Generic.Menu.Dropdown, {
className: "bn-menu-dropdown",
children: [W && /* @__PURE__ */ T(f.Generic.Menu.Item, {
icon: /* @__PURE__ */ T(A, {}),
onClick: x,
children: s.comments.actions.edit_comment
}, "edit-comment"), U && /* @__PURE__ */ T(f.Generic.Menu.Item, {
icon: /* @__PURE__ */ T(k, {}),
onClick: N,
children: s.comments.actions.delete_comment
}, "delete-comment")]
})]
})
]
}));
let K = e.createdAt.toLocaleDateString(void 0, {
month: "short",
day: "numeric"
});
if (!e.body) throw Error("soft deletes are not yet supported");
return /* @__PURE__ */ T(f.Comments.Comment, {
authorInfo: z ?? "loading",
timeString: K,
edited: e.updatedAt.getTime() !== e.createdAt.getTime(),
showActions: "hover",
actions: V,
className: "bn-thread-comment",
emojiPickerOpen: g,
children: /* @__PURE__ */ T(o, {
autoFocus: m,
editor: d,
editable: m,
actions: e.reactions.length > 0 || m ? ({ isFocused: t, isEmpty: n }) => /* @__PURE__ */ T(B, {
isFocused: t,
isEmpty: n,
comment: e,
isEditing: m,
threadStore: b,
onReactionSelect: P,
onEditSubmit: w,
onEditCancel: S,
onEmojiPickerOpenChange: v,
Components: f,
dict: s
}) : void 0
})
});
}, H = ({ thread: e, maxCommentsBeforeCollapse: t }) => {
let n = i(), r = l(), a = R(e.resolvedBy ? [e.resolvedBy] : []), o = e.comments.map((t, n) => /* @__PURE__ */ T(V, {
thread: e,
comment: t,
showResolveButton: n === 0
}, t.id + JSON.stringify(t.body || "{}")));
if (e.resolved && e.resolvedUpdatedAt && e.resolvedBy) {
if (!a.get(e.resolvedBy)) throw Error(`User ${e.resolvedBy} resolved thread ${e.id}, but their data could not be found.`);
let t = e.comments.findLastIndex((t) => e.resolvedUpdatedAt.getTime() > t.createdAt.getTime()) + 1;
o.splice(t, 0, /* @__PURE__ */ T(n.Comments.Comment, {
className: "bn-thread-comment",
authorInfo: e.resolvedBy && a.get(e.resolvedBy) || "loading",
timeString: e.resolvedUpdatedAt.toLocaleDateString(void 0, {
month: "short",
day: "numeric"
}),
edited: !1,
showActions: !1,
children: /* @__PURE__ */ T("div", {
className: "bn-resolved-text",
children: r.comments.sidebar.marked_as_resolved
})
}, "resolved-comment"));
}
return t && o.length > t && o.splice(1, o.length - 2, /* @__PURE__ */ T(n.Comments.ExpandSectionsPrompt, {
className: "bn-thread-expand-prompt",
children: r.comments.sidebar.more_replies(e.comments.length - 2)
}, "expand-prompt")), o;
}, U = f(({ isEmpty: e, onNewCommentSave: t, Components: n, dict: r }) => e ? null : /* @__PURE__ */ T(n.Generic.Toolbar.Root, {
variant: "action-toolbar",
className: C("bn-action-toolbar", "bn-comment-actions"),
children: /* @__PURE__ */ T(n.Generic.Toolbar.Button, {
mainTooltip: r.comments.save_button_text,
variant: "compact",
isDisabled: e,
onClick: t,
children: r.comments.save_button_text
})
})), W = ({ thread: e, selected: n, orphaned: r, referenceText: a, maxCommentsBeforeCollapse: s, onFocus: d, onBlur: f, tabIndex: m }) => {
let h = i(), g = l(), _ = t(y), v = c({
trailingBlock: !1,
dictionary: {
...g,
placeholders: { emptyDocument: g.placeholders.comment_reply }
},
schema: _.commentEditorSchema || u
}), b = p(async () => {
await _.threadStore.addComment({
comment: { body: v.document },
threadId: e.id
}), v.removeBlocks(v.document);
}, [
_,
v,
e.id
]);
return /* @__PURE__ */ E(h.Comments.Card, {
className: C("bn-thread", r && "bn-thread-orphaned"),
headerText: a,
onFocus: d,
onBlur: f,
selected: n,
tabIndex: m,
children: [/* @__PURE__ */ T(h.Comments.CardSection, {
className: "bn-thread-comments",
children: /* @__PURE__ */ T(H, {
thread: e,
maxCommentsBeforeCollapse: n ? void 0 : s || 5
})
}), n && /* @__PURE__ */ T(h.Comments.CardSection, {
className: "bn-thread-composer",
children: /* @__PURE__ */ T(o, {
autoFocus: !1,
editable: !0,
editor: v,
actions: ({ isFocused: e, isEmpty: t }) => /* @__PURE__ */ T(U, {
isFocused: e,
isEmpty: t,
onNewCommentSave: b,
Components: h,
dict: g
})
})
})]
});
};
//#endregion
//#region src/components/Comments/useThreads.ts
function G() {
let e = t(y).threadStore, n = g(void 0);
return n.current ||= e.getThreads(), v(p((t) => e.subscribe((e) => {
n.current = e, t();
}), [e]), () => n.current);
}
//#endregion
//#region src/components/Comments/FloatingThreadController.tsx
var K = /* @__PURE__ */ e({ default: () => q });
function q(e) {
let n = a(), i = t(y), o = s(y, {
editor: n,
selector: (e) => e.selectedThreadId ? {
id: e.selectedThreadId,
position: e.threadPositions.get(e.selectedThreadId)
} : void 0
}), c = G(), l = h(() => o ? c.get(o.id) : void 0, [o, c]), u = h(() => ({
...e.floatingUIOptions,
useFloatingOptions: {
open: !!o,
onOpenChange: (e, t, r) => {
r === "escape-key" && n.focus(), e || i.selectThread(void 0);
},
placement: "bottom",
middleware: [
x(10),
S(),
b()
],
...e.floatingUIOptions?.useFloatingOptions
},
focusManagerProps: {
disabled: !0,
...e.floatingUIOptions?.focusManagerProps
},
elementProps: {
style: { zIndex: 30 },
...e.floatingUIOptions?.elementProps
}
}), [
i,
n,
e.floatingUIOptions,
o
]), d = e.floatingThread || W;
return /* @__PURE__ */ T(r, {
position: o?.position,
portalElement: e.portalElement,
...u,
children: l && /* @__PURE__ */ T(d, {
thread: l,
selected: !0
})
});
}
//#endregion
export { H as a, R as c, W as i, K as n, V as o, G as r, L as s, q as t };
//# sourceMappingURL=FloatingThreadController-BZkNOp17.js.map