@thebuoyant-tsdev/mui-ts-library 3.19.0 → 3.20.0
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/README.de.md +7 -0
- package/README.md +7 -0
- package/dist/components/rich-text-editor/RichTextEditor.d.ts +1 -1
- package/dist/components/rich-text-editor/RichTextEditor.js +90 -85
- package/dist/components/rich-text-editor/RichTextEditor.types.d.ts +7 -0
- package/dist/components/rich-text-editor/RichTextEditorMentionSuggestion.d.ts +7 -1
- package/dist/components/rich-text-editor/RichTextEditorMentionSuggestion.js +25 -20
- package/dist/index.cjs +2 -2
- package/package.json +8 -2
package/README.de.md
CHANGED
|
@@ -475,6 +475,13 @@ Dieses Projekt folgt [Semantic Versioning](https://semver.org/). In der Praxis b
|
|
|
475
475
|
|
|
476
476
|
## Changelog
|
|
477
477
|
|
|
478
|
+
### [3.20.0] — 2026-07-09
|
|
479
|
+
|
|
480
|
+
**Hinzugefügt**
|
|
481
|
+
- `RichTextEditor`: neue Prop `onMentionInserted?: (item: MentionItem) => void` — feuert wenn der Nutzer ein Element aus dem `@`-Mention-Dropdown auswählt. Das vollständige `MentionItem` (`{ id, label }`) wird übergeben — kein HTML-Parsen nötig. Rein additiv. Siehe [Full Changelog](https://github.com/thebuoyant/mui-ts-library/blob/main/CHANGELOG.md) für Details.
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
478
485
|
### [3.19.0] — 2026-07-08
|
|
479
486
|
|
|
480
487
|
**Hinzugefügt**
|
package/README.md
CHANGED
|
@@ -475,6 +475,13 @@ This project follows [Semantic Versioning](https://semver.org/). In practice:
|
|
|
475
475
|
|
|
476
476
|
## Changelog
|
|
477
477
|
|
|
478
|
+
### [3.20.0] — 2026-07-09
|
|
479
|
+
|
|
480
|
+
**Added**
|
|
481
|
+
- `RichTextEditor`: new `onMentionInserted?: (item: MentionItem) => void` prop — fires when the user selects an item from the `@` mention dropdown. The full `MentionItem` (`{ id, label }`) is passed to the callback, so no HTML parsing is needed to track who was mentioned. Purely additive. See [Full Changelog](https://github.com/thebuoyant/mui-ts-library/blob/main/CHANGELOG.md) for details.
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
478
485
|
### [3.19.0] — 2026-07-08
|
|
479
486
|
|
|
480
487
|
**Added**
|
|
@@ -5,4 +5,4 @@ declare module "@tiptap/core" {
|
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
import { type RichTextEditorProps } from "./RichTextEditor.types";
|
|
8
|
-
export declare function RichTextEditor({ defaultValue, disabled, error, height, helperText, maxCharacters, mentionItems, mentionTriggerChar, name, onMentionSearch, placeholder, readonly, showCharacterCount, showToolbar, showWordCount, toolbarConfig, translation, value, width, onBlur, onChange, onFocus, onMarkdownChange, onSave, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function RichTextEditor({ defaultValue, disabled, error, height, helperText, maxCharacters, mentionItems, mentionTriggerChar, name, onMentionInserted, onMentionSearch, placeholder, readonly, showCharacterCount, showToolbar, showWordCount, toolbarConfig, translation, value, width, onBlur, onChange, onFocus, onMarkdownChange, onSave, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
import { normalizeSize as e } from "../shared/normalizeSize.js";
|
|
2
2
|
import { DEFAULT_RICH_TEXT_EDITOR_TOOLBAR_CONFIG as t, DEFAULT_RICH_TEXT_EDITOR_TRANSLATION as n } from "./RichTextEditor.types.js";
|
|
3
3
|
import { buildMentionSuggestion as r } from "./RichTextEditorMentionSuggestion.js";
|
|
4
|
-
import { RichTextEditorContent as
|
|
5
|
-
import { RichTextEditorToolbar as
|
|
4
|
+
import { RichTextEditorContent as ee } from "./RichTextEditorContent.js";
|
|
5
|
+
import { RichTextEditorToolbar as i } from "./RichTextEditorToolbar.js";
|
|
6
6
|
import { RichTextEditorFooter as a } from "./RichTextEditorFooter.js";
|
|
7
7
|
import { useEffect as o, useMemo as te, useRef as s, useState as c } from "react";
|
|
8
|
-
import { Box as
|
|
9
|
-
import { Fragment as
|
|
10
|
-
import { useEditor as
|
|
8
|
+
import { Box as l, Divider as u, Paper as ne } from "@mui/material";
|
|
9
|
+
import { Fragment as re, jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
10
|
+
import { useEditor as ie } from "@tiptap/react";
|
|
11
11
|
import { Mention as p } from "@tiptap/extension-mention";
|
|
12
|
-
import { StarterKit as
|
|
13
|
-
import { TextStyle as
|
|
14
|
-
import { Color as
|
|
15
|
-
import { Highlight as
|
|
16
|
-
import { Placeholder as
|
|
17
|
-
import { CharacterCount as
|
|
18
|
-
import { TableKit as
|
|
19
|
-
import { Image as
|
|
20
|
-
import { Markdown as
|
|
12
|
+
import { StarterKit as ae } from "@tiptap/starter-kit";
|
|
13
|
+
import { TextStyle as oe } from "@tiptap/extension-text-style";
|
|
14
|
+
import { Color as se } from "@tiptap/extension-color";
|
|
15
|
+
import { Highlight as ce } from "@tiptap/extension-highlight";
|
|
16
|
+
import { Placeholder as le } from "@tiptap/extension-placeholder";
|
|
17
|
+
import { CharacterCount as m } from "@tiptap/extension-character-count";
|
|
18
|
+
import { TableKit as h } from "@tiptap/extension-table";
|
|
19
|
+
import { Image as g } from "@tiptap/extension-image";
|
|
20
|
+
import { Markdown as _ } from "tiptap-markdown";
|
|
21
21
|
//#region src/components/rich-text-editor/RichTextEditor.tsx
|
|
22
|
-
function
|
|
23
|
-
let
|
|
22
|
+
function v({ defaultValue: v, disabled: y = !1, error: b = !1, height: ue, helperText: x, maxCharacters: S, mentionItems: C, mentionTriggerChar: w = "@", name: T, onMentionInserted: E, onMentionSearch: D, placeholder: de, readonly: O = !1, showCharacterCount: k = !1, showToolbar: A = !0, showWordCount: j = !1, toolbarConfig: M, translation: N, value: P, width: F, onBlur: I, onChange: L, onFocus: R, onMarkdownChange: z, onSave: B }) {
|
|
23
|
+
let V = {
|
|
24
24
|
...n,
|
|
25
|
-
...P
|
|
26
|
-
}, U = {
|
|
27
|
-
...t,
|
|
28
25
|
...N
|
|
29
|
-
},
|
|
26
|
+
}, H = {
|
|
27
|
+
...t,
|
|
28
|
+
...M
|
|
29
|
+
}, [U, fe] = c(!1), [W, pe] = c(!1), G = s(W), K = s(B), q = s(C ?? []);
|
|
30
|
+
o(() => {
|
|
31
|
+
q.current = C ?? [];
|
|
32
|
+
}, [C]);
|
|
33
|
+
let J = s(E);
|
|
30
34
|
o(() => {
|
|
31
|
-
J.current =
|
|
32
|
-
}, [
|
|
33
|
-
|
|
34
|
-
}, [
|
|
35
|
-
|
|
36
|
-
}, [
|
|
37
|
-
let Y = e(
|
|
35
|
+
J.current = E;
|
|
36
|
+
}, [E]), o(() => {
|
|
37
|
+
G.current = W;
|
|
38
|
+
}, [W]), o(() => {
|
|
39
|
+
K.current = B;
|
|
40
|
+
}, [B]);
|
|
41
|
+
let Y = e(ue), me = e(F), X = Y === "auto", he = X ? void 0 : Y ?? 200, Z = C !== void 0 || D !== void 0, Q = te(() => Z ? p.configure({
|
|
38
42
|
HTMLAttributes: { class: "rte-mention" },
|
|
39
43
|
renderHTML({ options: e, node: t }) {
|
|
40
44
|
return [
|
|
@@ -44,43 +48,44 @@ function y({ defaultValue: y, disabled: b = !1, error: x = !1, height: le, helpe
|
|
|
44
48
|
"data-id": t.attrs.id,
|
|
45
49
|
"data-label": t.attrs.label
|
|
46
50
|
},
|
|
47
|
-
`${
|
|
51
|
+
`${w}${t.attrs.label}`
|
|
48
52
|
];
|
|
49
53
|
},
|
|
50
54
|
suggestion: r({
|
|
51
|
-
getItems: () =>
|
|
55
|
+
getItems: () => q.current,
|
|
52
56
|
onMentionSearch: D,
|
|
53
|
-
|
|
57
|
+
onMentionInserted: (e) => J.current?.(e),
|
|
58
|
+
noResultsLabel: V.mentionNoResults ?? "No results"
|
|
54
59
|
})
|
|
55
60
|
}) : null, [
|
|
56
61
|
Z,
|
|
57
|
-
|
|
62
|
+
w,
|
|
58
63
|
D
|
|
59
|
-
]),
|
|
64
|
+
]), ge = S !== void 0 && S > 0 || k || j, $ = ie({
|
|
60
65
|
shouldRerenderOnTransaction: !0,
|
|
61
66
|
extensions: [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
ae.configure({ link: { openOnClick: !1 } }),
|
|
68
|
+
oe,
|
|
69
|
+
se,
|
|
70
|
+
ce.configure({ multicolor: !0 }),
|
|
71
|
+
_.configure({
|
|
67
72
|
transformPastedText: !0,
|
|
68
73
|
transformCopiedText: !1
|
|
69
74
|
}),
|
|
70
|
-
|
|
71
|
-
...
|
|
72
|
-
...
|
|
75
|
+
le.configure({ placeholder: de ?? "" }),
|
|
76
|
+
...H.showTableButton ? [h] : [],
|
|
77
|
+
...H.showImageButton ? [g.configure({
|
|
73
78
|
inline: !1,
|
|
74
79
|
allowBase64: !0
|
|
75
80
|
})] : [],
|
|
76
81
|
...Q ? [Q] : [],
|
|
77
|
-
...
|
|
82
|
+
...ge ? S !== void 0 && S > 0 ? [m.configure({ limit: S })] : [m] : []
|
|
78
83
|
],
|
|
79
|
-
content:
|
|
80
|
-
editable: !
|
|
84
|
+
content: P ?? v ?? "",
|
|
85
|
+
editable: !y && !O,
|
|
81
86
|
editorProps: {
|
|
82
87
|
handlePaste(e, t) {
|
|
83
|
-
if (!
|
|
88
|
+
if (!G.current) return !1;
|
|
84
89
|
let n = t.clipboardData?.getData("text/plain");
|
|
85
90
|
if (!n) return !1;
|
|
86
91
|
t.preventDefault();
|
|
@@ -88,31 +93,31 @@ function y({ defaultValue: y, disabled: b = !1, error: x = !1, height: le, helpe
|
|
|
88
93
|
return e.dispatch(r.tr.insertText(n, r.selection.from, r.selection.to)), !0;
|
|
89
94
|
},
|
|
90
95
|
handleKeyDown(e, t) {
|
|
91
|
-
return (t.ctrlKey || t.metaKey) && t.key === "s" ? (t.preventDefault(),
|
|
96
|
+
return (t.ctrlKey || t.metaKey) && t.key === "s" ? (t.preventDefault(), K.current?.(), !0) : !1;
|
|
92
97
|
}
|
|
93
98
|
},
|
|
94
99
|
onUpdate({ editor: e }) {
|
|
95
|
-
|
|
100
|
+
L?.(e.getHTML()), z?.(e.storage.markdown.getMarkdown());
|
|
96
101
|
},
|
|
97
102
|
onBlur() {
|
|
98
|
-
|
|
103
|
+
I?.();
|
|
99
104
|
},
|
|
100
105
|
onFocus() {
|
|
101
|
-
|
|
106
|
+
R?.();
|
|
102
107
|
}
|
|
103
108
|
});
|
|
104
109
|
o(() => {
|
|
105
|
-
!$ ||
|
|
106
|
-
}, [$,
|
|
107
|
-
$ && $.setEditable(!
|
|
110
|
+
!$ || P === void 0 || $.getHTML() !== P && $.commands.setContent(P, { emitUpdate: !1 });
|
|
111
|
+
}, [$, P]), o(() => {
|
|
112
|
+
$ && $.setEditable(!y && !O);
|
|
108
113
|
}, [
|
|
109
114
|
$,
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
y,
|
|
116
|
+
O
|
|
112
117
|
]);
|
|
113
|
-
let
|
|
114
|
-
return /* @__PURE__ */
|
|
115
|
-
sx:
|
|
118
|
+
let _e = $?.storage.characterCount?.characters?.() ?? 0, ve = $?.storage.characterCount?.words?.() ?? 0, ye = k || S !== void 0 && S > 0 || j || !!x;
|
|
119
|
+
return /* @__PURE__ */ f(l, {
|
|
120
|
+
sx: U ? {
|
|
116
121
|
position: "fixed",
|
|
117
122
|
top: 0,
|
|
118
123
|
left: 0,
|
|
@@ -124,7 +129,7 @@ function y({ defaultValue: y, disabled: b = !1, error: x = !1, height: le, helpe
|
|
|
124
129
|
bgcolor: "background.default",
|
|
125
130
|
p: 1
|
|
126
131
|
} : {
|
|
127
|
-
width:
|
|
132
|
+
width: me ?? "100%",
|
|
128
133
|
...X ? {
|
|
129
134
|
display: "flex",
|
|
130
135
|
flexDirection: "column",
|
|
@@ -132,52 +137,52 @@ function y({ defaultValue: y, disabled: b = !1, error: x = !1, height: le, helpe
|
|
|
132
137
|
} : {}
|
|
133
138
|
},
|
|
134
139
|
children: [
|
|
135
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ f(ne, {
|
|
136
141
|
variant: "outlined",
|
|
137
142
|
sx: {
|
|
138
143
|
display: "flex",
|
|
139
144
|
flexDirection: "column",
|
|
140
145
|
overflow: "hidden",
|
|
141
|
-
...
|
|
142
|
-
borderColor:
|
|
146
|
+
...U || X ? { flex: 1 } : { height: he },
|
|
147
|
+
borderColor: b ? "error.main" : void 0,
|
|
143
148
|
"&:focus-within": {
|
|
144
|
-
borderColor:
|
|
149
|
+
borderColor: b ? "error.main" : "primary.main",
|
|
145
150
|
borderWidth: 2
|
|
146
151
|
}
|
|
147
152
|
},
|
|
148
|
-
children: [
|
|
153
|
+
children: [A && !O && /* @__PURE__ */ f(re, { children: [/* @__PURE__ */ d(i, {
|
|
149
154
|
editor: $,
|
|
150
|
-
toolbarConfig:
|
|
151
|
-
translation:
|
|
152
|
-
disabled:
|
|
153
|
-
isFullscreen:
|
|
154
|
-
onToggleFullscreen: () =>
|
|
155
|
-
pasteAsPlainText:
|
|
156
|
-
onTogglePasteAsPlainText: () =>
|
|
157
|
-
}), /* @__PURE__ */ u
|
|
155
|
+
toolbarConfig: H,
|
|
156
|
+
translation: V,
|
|
157
|
+
disabled: y,
|
|
158
|
+
isFullscreen: U,
|
|
159
|
+
onToggleFullscreen: () => fe((e) => !e),
|
|
160
|
+
pasteAsPlainText: W,
|
|
161
|
+
onTogglePasteAsPlainText: () => pe((e) => !e)
|
|
162
|
+
}), /* @__PURE__ */ d(u, {})] }), /* @__PURE__ */ d(ee, {
|
|
158
163
|
editor: $,
|
|
159
|
-
error:
|
|
160
|
-
disabled:
|
|
161
|
-
readonly:
|
|
164
|
+
error: b,
|
|
165
|
+
disabled: y,
|
|
166
|
+
readonly: O
|
|
162
167
|
})]
|
|
163
168
|
}),
|
|
164
|
-
|
|
165
|
-
helperText:
|
|
166
|
-
error:
|
|
167
|
-
showCharacterCount:
|
|
168
|
-
charCount:
|
|
169
|
-
maxCharacters:
|
|
170
|
-
showWordCount:
|
|
171
|
-
wordCount:
|
|
172
|
-
translation:
|
|
169
|
+
ye && /* @__PURE__ */ d(a, {
|
|
170
|
+
helperText: x,
|
|
171
|
+
error: b,
|
|
172
|
+
showCharacterCount: k || S !== void 0 && S > 0,
|
|
173
|
+
charCount: _e,
|
|
174
|
+
maxCharacters: S && S > 0 ? S : void 0,
|
|
175
|
+
showWordCount: j,
|
|
176
|
+
wordCount: ve,
|
|
177
|
+
translation: V
|
|
173
178
|
}),
|
|
174
|
-
|
|
179
|
+
T && /* @__PURE__ */ d("input", {
|
|
175
180
|
type: "hidden",
|
|
176
|
-
name:
|
|
181
|
+
name: T,
|
|
177
182
|
value: $ ? $.getHTML() : ""
|
|
178
183
|
})
|
|
179
184
|
]
|
|
180
185
|
});
|
|
181
186
|
}
|
|
182
187
|
//#endregion
|
|
183
|
-
export {
|
|
188
|
+
export { v as RichTextEditor };
|
|
@@ -147,6 +147,13 @@ export type RichTextEditorProps = {
|
|
|
147
147
|
* @since 3.14.0
|
|
148
148
|
*/
|
|
149
149
|
onMentionSearch?: (query: string) => MentionItem[] | Promise<MentionItem[]>;
|
|
150
|
+
/**
|
|
151
|
+
* Called when the user selects a mention item from the autocomplete dropdown.
|
|
152
|
+
* Fires immediately after the mention node is inserted into the editor.
|
|
153
|
+
* Useful for tracking which people are mentioned without parsing the HTML output.
|
|
154
|
+
* @since 3.20.0
|
|
155
|
+
*/
|
|
156
|
+
onMentionInserted?: (item: MentionItem) => void;
|
|
150
157
|
/**
|
|
151
158
|
* Character that triggers the mention autocomplete popup. Default: "@"
|
|
152
159
|
* @since 3.14.0
|
|
@@ -3,7 +3,13 @@ import type { MentionItem } from "./RichTextEditor.types";
|
|
|
3
3
|
type BuildSuggestionOptions = {
|
|
4
4
|
getItems: () => MentionItem[];
|
|
5
5
|
onMentionSearch?: (query: string) => MentionItem[] | Promise<MentionItem[]>;
|
|
6
|
+
onMentionInserted?: (item: MentionItem) => void;
|
|
6
7
|
noResultsLabel: string;
|
|
7
8
|
};
|
|
8
|
-
|
|
9
|
+
/** Pure handler factory — extracted for testability without mocking ReactRenderer. */
|
|
10
|
+
export declare function createMentionSelectHandler(command: (attrs: {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}) => void, onMentionInserted?: (item: MentionItem) => void): (item: MentionItem) => void;
|
|
14
|
+
export declare function buildMentionSuggestion({ getItems, onMentionSearch, onMentionInserted, noResultsLabel, }: BuildSuggestionOptions): Partial<SuggestionOptions<MentionItem>>;
|
|
9
15
|
export {};
|
|
@@ -1,45 +1,50 @@
|
|
|
1
1
|
import { RichTextEditorMentionList as e } from "./RichTextEditorMentionList.js";
|
|
2
2
|
import { ReactRenderer as t } from "@tiptap/react";
|
|
3
3
|
//#region src/components/rich-text-editor/RichTextEditorMentionSuggestion.ts
|
|
4
|
-
function n(
|
|
4
|
+
function n(e, t) {
|
|
5
|
+
return (n) => {
|
|
6
|
+
e({
|
|
7
|
+
id: n.id,
|
|
8
|
+
label: n.label
|
|
9
|
+
}), t?.(n);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function r({ getItems: r, onMentionSearch: i, onMentionInserted: a, noResultsLabel: o }) {
|
|
5
13
|
return {
|
|
6
14
|
items: async ({ query: e }) => {
|
|
7
|
-
if (
|
|
8
|
-
let t =
|
|
9
|
-
return t.filter((e) => e.label.toLowerCase().includes(
|
|
15
|
+
if (i) return i(e);
|
|
16
|
+
let t = r(), n = e.toLowerCase();
|
|
17
|
+
return t.filter((e) => e.label.toLowerCase().includes(n));
|
|
10
18
|
},
|
|
11
19
|
render() {
|
|
12
|
-
let
|
|
20
|
+
let r = null;
|
|
13
21
|
return {
|
|
14
|
-
onStart(
|
|
15
|
-
|
|
22
|
+
onStart(i) {
|
|
23
|
+
r = new t(e, {
|
|
16
24
|
props: {
|
|
17
|
-
items:
|
|
18
|
-
noResultsLabel:
|
|
19
|
-
clientRect:
|
|
20
|
-
onSelect: (
|
|
21
|
-
id: e.id,
|
|
22
|
-
label: e.label
|
|
23
|
-
})
|
|
25
|
+
items: i.items,
|
|
26
|
+
noResultsLabel: o,
|
|
27
|
+
clientRect: i.clientRect,
|
|
28
|
+
onSelect: n(i.command, a)
|
|
24
29
|
},
|
|
25
|
-
editor:
|
|
26
|
-
}), document.body.appendChild(
|
|
30
|
+
editor: i.editor
|
|
31
|
+
}), document.body.appendChild(r.element);
|
|
27
32
|
},
|
|
28
33
|
onUpdate(e) {
|
|
29
|
-
|
|
34
|
+
r?.updateProps({
|
|
30
35
|
items: e.items,
|
|
31
36
|
clientRect: e.clientRect
|
|
32
37
|
});
|
|
33
38
|
},
|
|
34
39
|
onKeyDown(e) {
|
|
35
|
-
return e.event.key === "Escape" ? (
|
|
40
|
+
return e.event.key === "Escape" ? (r?.destroy(), r = null, !0) : r?.ref?.onKeyDown(e.event) ?? !1;
|
|
36
41
|
},
|
|
37
42
|
onExit() {
|
|
38
|
-
|
|
43
|
+
r?.element.remove(), r?.destroy(), r = null;
|
|
39
44
|
}
|
|
40
45
|
};
|
|
41
46
|
}
|
|
42
47
|
};
|
|
43
48
|
}
|
|
44
49
|
//#endregion
|
|
45
|
-
export {
|
|
50
|
+
export { r as buildMentionSuggestion };
|