UNPKG

@svar-ui/react-comments

Version:

Lightweight React component for adding a modern-looking comments section to your app

463 lines (462 loc) 14.3 kB
import { jsx as a, jsxs as y, Fragment as oe } from "react/jsx-runtime"; import { useMemo as p, useRef as W, useContext as $, useCallback as V, useEffect as Q, createContext as re, useState as P } from "react"; import { context as I, TextArea as ae, Button as ie, Locale as se, Willow as O, WillowDark as U } from "@svar-ui/react-core"; import { useWritableProp as E } from "@svar-ui/lib-react"; import { ActionMenu as le } from "@svar-ui/react-menu"; import { uid as ce } from "@svar-ui/lib-state"; import { dateToString as ue } from "@svar-ui/lib-dom"; import { en as de } from "@svar-ui/comments-locales"; import { en as fe } from "@svar-ui/core-locales"; function me(e) { if (e.startsWith("hsl")) return xe(e)[2] > 55 ? "dark" : "light"; if (e.startsWith("#") || e.startsWith("rgb")) { const t = we(e, !0); return Math.round( (t[0] * 299 + t[1] * 587 + t[2] * 114) / 1e3 ) > 180 ? "dark" : "light"; } return "light"; } function xe(e) { return e.match(/\d+/g).map(Number); } function we(e, t = !1) { if (e.startsWith("rgb")) return t ? he(e) : e; let n = 0, o = 0, i = 0; return e.length == 4 ? (n = "0x" + e[1] + e[1], o = "0x" + e[2] + e[2], i = "0x" + e[3] + e[3]) : e.length == 7 && (n = "0x" + e[1] + e[2], o = "0x" + e[3] + e[4], i = "0x" + e[5] + e[6]), n = +n, o = +o, i = +i, t ? { r: n, g: o, b: i } : "rgb(" + n + "," + o + "," + i + ")"; } function he(e) { const t = e.indexOf(",") > -1 ? "," : " ", n = e.substring(4).split(")")[0].split(t), o = (+n[0]).toString(16), i = (+n[1]).toString(16), m = (+n[2]).toString(16); return { r: +o, g: +i, b: +m }; } function R({ data: e = { name: "", avatar: "", color: "" }, noTransform: t = !1, size: n = "small", border: o = !0 }) { const i = p( () => e.name.split(" ").map((s) => s[0]).join(""), [e.name] ), m = p( () => e.color ? { background: e.color } : void 0, [e.color] ), c = p( () => e.color ? `wx-comments-avatar-color-${me(e.color)}` : "", [e.color] ); return /* @__PURE__ */ a( "div", { className: `wx-cyzBpibr wx-user wx-${n} ${c} ${o ? "wx-border" : ""}`, style: m, children: e.avatar ? /* @__PURE__ */ a("img", { src: e.avatar, alt: e.name }) : t ? e.name : i } ); } function G(e) { const { focus: t = !1, buttonLabel: n = "Send", flow: o = !1, value: i, author: m, onPost: c } = e, [s, l] = E(i || ""), v = W(null), u = W(null), N = W(null), h = $(I.i18n), f = p(() => h.getGroup("comments"), [h]); return N.current = V( (d) => { d && (c && c({ value: d }), l(""), u.current && u.current.focus()); }, [c, s] ), Q(() => { v.current && (u.current = v.current.querySelector("textarea"), t && u.current && u.current.focus(), u.current.onkeydown = (d) => { d.key === "Enter" && (d.ctrlKey || d.metaKey) && (d.preventDefault(), N.current(s)); }); }, []), /* @__PURE__ */ y( "div", { className: `wx-v2rD0VHO wx-comments-textarea${o ? " wx-flow" : ""}`, "data-focus": "yes", ref: v, children: [ /* @__PURE__ */ y("div", { className: "wx-v2rD0VHO wx-textarea-wrapper", children: [ m ? /* @__PURE__ */ a("div", { className: "wx-v2rD0VHO wx-textarea-avatar", children: /* @__PURE__ */ a(R, { data: m }) }) : null, /* @__PURE__ */ a( ae, { placeholder: f("Add a comment..."), value: s, onChange: ({ value: d }) => l(d) } ) ] }), /* @__PURE__ */ a("div", { className: "wx-v2rD0VHO wx-textarea-bottombar", children: /* @__PURE__ */ a(ie, { type: "primary", onClick: () => N.current(s), children: f(n) }) }) ] } ); } const Z = re({ dateStr: (e) => e.toString() }); function ve({ owned: e, author: t, date: n, edit: o, children: i }) { const c = $(Z).dateStr; return /* @__PURE__ */ a("div", { className: `wx-aluyyvxH wx-bubble ${e ? "wx-owned" : ""}`, children: /* @__PURE__ */ y("div", { className: "wx-aluyyvxH wx-bubble-wrapper", children: [ /* @__PURE__ */ a("div", { className: "wx-aluyyvxH wx-avatar", children: /* @__PURE__ */ a(R, { data: t }) }), /* @__PURE__ */ y("div", { className: "wx-aluyyvxH wx-main-bubble", children: [ /* @__PURE__ */ a("span", { className: "wx-aluyyvxH wx-author-name", children: t.name }), e ? /* @__PURE__ */ y("div", { className: "wx-aluyyvxH wx-agent-message", children: [ /* @__PURE__ */ y("div", { className: "wx-aluyyvxH wx-message", children: [ i, o !== e ? /* @__PURE__ */ a("div", { className: "wx-aluyyvxH wx-comment-date", children: c(n) }) : null ] }), /* @__PURE__ */ a( "div", { className: "wx-aluyyvxH wx-menu-icon", "data-comment-menu-id": e, children: /* @__PURE__ */ a("i", { className: "wx-aluyyvxH wx-icon wxi-dots-v" }) } ) ] }) : /* @__PURE__ */ y("div", { className: "wx-aluyyvxH wx-message", children: [ i, /* @__PURE__ */ a("div", { className: "wx-aluyyvxH wx-comment-date", children: c(n) }) ] }) ] }) ] }) }); } function ge(e) { const { owned: t, author: n, date: o, edit: i, children: m } = e, c = $(Z).dateStr; return /* @__PURE__ */ y("div", { className: `wx-N2LqQbZL wx-flow${t ? " wx-owned" : ""}`, children: [ /* @__PURE__ */ y("div", { className: "wx-N2LqQbZL wx-flow-toolbar", children: [ /* @__PURE__ */ a(R, { data: n }), /* @__PURE__ */ a("span", { className: "wx-N2LqQbZL wx-author-name", children: n.name }), t && t !== i && /* @__PURE__ */ a( "div", { className: "wx-N2LqQbZL wx-menu-icon", "data-comment-menu-id": t, children: /* @__PURE__ */ a("i", { className: "wx-N2LqQbZL wx-icon wxi-dots-v" }) } ) ] }), /* @__PURE__ */ a("div", { className: "wx-N2LqQbZL wx-comment-date", children: c(o) }), /* @__PURE__ */ a("div", { className: "wx-N2LqQbZL wx-message", children: m }) ] }); } function pe(e, t, n) { let o, i, m, c, s, l, v, u; if (t ? { linestart: v, headerlevel: m, checkbehind: s, unsurepoint: l, content: o, ctype: i, newLineCounter: c, i: u } = t.state : (t = { nodes: [{ d: [], t: "p", f: !1, i: 0 }], length: 0, state: {} }, v = 0, m = 0, s = -1, l = -1, o = "", i = "", c = 0, u = 0), l) { const f = t.nodes[t.nodes.length - 1]; f.d.length && (t.length -= f.d[f.d.length - 1].c.length, f.d.pop()); } const N = (f) => { o !== "" && h("", f); const d = t.nodes[t.nodes.length - 1]; d.f = f !== 1, d.i = u, m > 0 && (d.t = `h${m}`, m = -1), f === 0 && t.nodes.push({ d: [], t: "p", f: !1, i: 0 }); }, h = (f, d = 2) => { const D = t.nodes[t.nodes.length - 1], C = D.d[D.d.length - 1], L = d === 2 ? i : ""; if (C && C.t === L && l < 0) { const k = C; t.length += o.length, k.c = k.c + o, k.i = u; } else { const k = L === "" && l >= 0 ? e.substring(l) : o; t.length += k.length, D.d.push({ c: k, t: L, f: d !== 1, i: u }); } d === 2 && (f ? l = s : l = -1), d !== 1 && (o = "", i = f, s = -1); }; for (; u < e.length; u++) { const f = e[u]; if (f === ` `) { c++, v = u + 1; continue; } if (!((f === " " || f === " ") && c > 0)) if (c > 0 && (c > 1 ? N(0) : o += ` `, c = 0), f == "*" || f == "`" || f == "#") e[u - 1] == "\\" ? o += f : s === -1 && (s = u); else { if (s >= 0) { const d = u - s; if (e[u - 1] == "*") d === 2 ? h(i === "strong" ? "" : "strong") : d === 1 && h(i === "em" ? "" : "em"), s = -1; else if (e[u - 1] == "`" && d == 1) h(i === "code" ? "" : "code"), s = -1; else if (e[u - 1] == "#" && e[u] === " " && s === v && u - s <= 6) { m = u - s, s = -1; continue; } } s !== -1 && (o += e.substring(s, u), s = -1), o += f; } } return N(-1), t.state = {}, t; } function _(e) { let t = ""; for (const n of e) typeof n.d < "u" ? (t += "<" + n.t + ">", t += _(n.d), t += "</" + n.t + ">") : (n.t && (t += "<" + n.t + ">"), t += n.c, n.t && (t += "</" + n.t + ">")); return t; } function be(e) { return _(pe(e).nodes); } function ye({ content: e }) { return /* @__PURE__ */ a("div", { dangerouslySetInnerHTML: { __html: be(e) } }); } function Ne({ content: e }) { return /* @__PURE__ */ a(oe, { children: e }); } const ke = { bubbles: ve, flow: ge }, Ce = { markdown: ye, text: Ne }; function Le({ content: e, date: t, owned: n, render: o, format: i = "text", author: m, edit: c, onPost: s, onCancel: l }) { const v = p( () => typeof o == "string" ? ke[o] : o, [o] ), u = p( () => typeof i == "string" ? Ce[i] : i, [i] ); return /* @__PURE__ */ a(v, { owned: n, edit: c, author: m, date: t, children: c && c === n ? /* @__PURE__ */ a( G, { focus: !0, onPost: s, onCancel: l, value: e } ) : /* @__PURE__ */ a(u, { content: e }) }); } function He({ data: e, render: t = "blocks", format: n, author: o, edit: i, onPost: m, onCancel: c }) { const s = p( () => typeof t == "string" ? t : "blocks", [t] ); return /* @__PURE__ */ a("div", { className: `wx-6HAxmtjJ wx-messages wx-${s}`, children: e.map((l) => /* @__PURE__ */ a( Le, { content: l.content, date: l.date, author: l.author, owned: l.author.id === o.id ? l.id : null, render: t, edit: i, format: l.format || n, onPost: m, onCancel: c }, l.id )) }); } function De(e) { let { onAction: t, onChange: n, readonly: o = !1, render: i = "flow", format: m = "text", users: c, data: s, activeUser: l, focus: v = !1 } = e; const u = $(I.i18n), { calendar: N, comments: h, formats: f } = u.getRaw(), d = u.getGroup("comments"), D = p( () => ue(h.dateFormat || f.dateFormat, N), [N, h, f] ), [C, L] = P(null), [k, q] = P(""), [b, M] = E(s); Q(() => { q(""), L(null); }, [b]); const j = { id: 0, name: d("Unknown"), color: "hsl(0, 0%, 85%)" }, S = p(() => c ? c.map((r) => r.color ? r : { ...r, color: "hsl(" + K(r.id + r.name) + ", 100%, 85%)" }) : [], [c]), T = p(() => { if (typeof l == "object") return l; const r = S.find((x) => x.id === l) || j; return r || { id: l || -1, name: d("Me"), color: "hsl(225, 76%, 67%)" }; }, [l, S, d]), B = p(() => b ? b.map((r) => { if (typeof r.author == "object") return r; const x = S ? S.find((w) => w.id === r.user) : null; return { ...r, author: x || j }; }) : [], [b, S]); function K(r) { let x = 0; for (let w = 0; w < r.length; w++) x = r.charCodeAt(w) + ((x << 5) - x); return x % 60 * 6; } function X(r) { const x = { id: ce(), content: r, author: T, user: T.id, date: /* @__PURE__ */ new Date() }, w = [...B, x]; if (M(w), n) { const g = n({ value: w, comment: x, action: "add" }); g && typeof g == "object" && (g.then ? g.then((H) => { F(x.id, H); }) : F(x.id, g)); } } function F(r, x) { const w = b ? b.findIndex((H) => H.id === r) : -1; if (w === -1) return; const g = [...b]; g[w] = { ...b[w], ...x }, M(g); } function z(r) { const x = (b || []).filter((w) => w.id !== r); M(x), n && n({ value: x, id: r, action: "delete" }); } function J(r, x) { let w; const g = (b || []).map((H) => H.id === r ? (w = { ...H, content: x }, w) : H); M(g), n && n({ value: g, id: r, action: "update", comment: w }); } function Y() { L(null); } function ee(r) { const { context: x, action: w } = r; if (w) switch (t && t({ action: "menu-clicked" }), w.id) { case "edit-comment": L(x); break; case "delete-comment": z(x); break; } } const te = [ { id: "edit-comment", text: d("Edit"), icon: "wxi-edit-outline" }, { id: "delete-comment", text: d("Delete"), icon: "wxi-delete-outline" } ], A = W(null), ne = V( (r) => { A.current && typeof A.current.show == "function" && A.current.show(r); }, [A] ); return /* @__PURE__ */ a( Z.Provider, { value: { dateStr: (r) => D(r) }, children: /* @__PURE__ */ y("div", { className: "wx-8ZGHQX6e wx-comments-list", children: [ /* @__PURE__ */ a( le, { at: "bottom", ref: A, options: te, resolver: (r) => r, dataKey: "commentMenuId", onClick: ee } ), /* @__PURE__ */ a("div", { className: "wx-8ZGHQX6e wx-list", onClick: ne, children: /* @__PURE__ */ a( He, { author: T, render: i, data: B, format: m, edit: C, onPost: (r) => J(C, r.value), onCancel: Y } ) }), !o && !C && /* @__PURE__ */ a( G, { author: T, flow: i === "flow", focus: v, onPost: (r) => X(r.value), buttonLabel: "Add", value: k, onChange: ({ value: r }) => q(r) } ) ] }) } ); } function Re(e) { const { onData: t, onChange: n, value: o, ...i } = e; let [m, c] = P([]); Q(() => { t && o ? Promise.resolve(t(o)).then((l) => c(l)) : c(o || []); }, [t, o]); const s = V( (l) => (l.originalValue = o, n && n(l)), [n, o] ); return /* @__PURE__ */ a(se, { words: { ...fe, ...de }, optional: !0, children: /* @__PURE__ */ a(De, { data: m, ...i, onChange: s }) }); } const Ze = ({ children: e, fonts: t }) => e ? /* @__PURE__ */ a(O, { fonts: t, children: e }) : /* @__PURE__ */ a(O, { fonts: t }), qe = ({ children: e, fonts: t }) => e ? /* @__PURE__ */ a(U, { fonts: t, children: e }) : /* @__PURE__ */ a(U, { fonts: t }); export { Re as Comments, Ze as Willow, qe as WillowDark };