@thebuoyant-tsdev/mui-ts-library 3.18.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 +14 -0
- package/README.md +14 -0
- package/dist/components/rich-text-editor/RichTextEditor.d.ts +1 -1
- package/dist/components/rich-text-editor/RichTextEditor.js +49 -44
- package/dist/components/rich-text-editor/RichTextEditor.types.d.ts +14 -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,20 @@ 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
|
+
|
|
485
|
+
### [3.19.0] — 2026-07-08
|
|
486
|
+
|
|
487
|
+
**Hinzugefügt**
|
|
488
|
+
- `RichTextEditor`: neue Prop `defaultValue?: string` für unkontrollierten Einsatz — setzt den initialen HTML-Inhalt einmalig beim Mount, kein externer State erforderlich. Analog zu MUI TextFields `defaultValue`. Wenn sowohl `value` als auch `defaultValue` angegeben werden, hat `value` Vorrang. Rein additiv. Siehe [Full Changelog](https://github.com/thebuoyant/mui-ts-library/blob/main/CHANGELOG.md) für Details.
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
478
492
|
### [3.18.0] — 2026-07-08
|
|
479
493
|
|
|
480
494
|
**Hinzugefügt**
|
package/README.md
CHANGED
|
@@ -475,6 +475,20 @@ 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
|
+
|
|
485
|
+
### [3.19.0] — 2026-07-08
|
|
486
|
+
|
|
487
|
+
**Added**
|
|
488
|
+
- `RichTextEditor`: new `defaultValue?: string` prop for uncontrolled usage — sets the initial HTML content once on mount, no external state required. Analogous to MUI TextField's `defaultValue`. When both `value` and `defaultValue` are provided, `value` takes precedence. Purely additive — no existing behaviour changes. See [Full Changelog](https://github.com/thebuoyant/mui-ts-library/blob/main/CHANGELOG.md) for details.
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
478
492
|
### [3.18.0] — 2026-07-08
|
|
479
493
|
|
|
480
494
|
**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({ 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 l, Divider as
|
|
9
|
-
import { Fragment as
|
|
10
|
-
import { useEditor as
|
|
11
|
-
import { Mention 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
|
+
import { Mention as p } from "@tiptap/extension-mention";
|
|
12
12
|
import { StarterKit as ae } from "@tiptap/starter-kit";
|
|
13
13
|
import { TextStyle as oe } from "@tiptap/extension-text-style";
|
|
14
14
|
import { Color as se } from "@tiptap/extension-color";
|
|
15
15
|
import { Highlight as ce } from "@tiptap/extension-highlight";
|
|
16
|
-
import { Placeholder as
|
|
17
|
-
import { CharacterCount as
|
|
18
|
-
import { TableKit as
|
|
19
|
-
import { Image as
|
|
20
|
-
import { Markdown as
|
|
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
|
|
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
23
|
let V = {
|
|
24
24
|
...n,
|
|
25
25
|
...N
|
|
26
26
|
}, H = {
|
|
27
27
|
...t,
|
|
28
28
|
...M
|
|
29
|
-
}, [U,
|
|
29
|
+
}, [U, fe] = c(!1), [W, pe] = c(!1), G = s(W), K = s(B), q = s(C ?? []);
|
|
30
30
|
o(() => {
|
|
31
|
-
|
|
32
|
-
}, [C])
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
q.current = C ?? [];
|
|
32
|
+
}, [C]);
|
|
33
|
+
let J = s(E);
|
|
34
|
+
o(() => {
|
|
35
|
+
J.current = E;
|
|
36
|
+
}, [E]), o(() => {
|
|
37
|
+
G.current = W;
|
|
38
|
+
}, [W]), o(() => {
|
|
39
|
+
K.current = B;
|
|
36
40
|
}, [B]);
|
|
37
|
-
let Y = e(
|
|
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 [
|
|
@@ -48,39 +52,40 @@ function y({ disabled: y = !1, error: b = !1, height: le, helperText: x, maxChar
|
|
|
48
52
|
];
|
|
49
53
|
},
|
|
50
54
|
suggestion: r({
|
|
51
|
-
getItems: () =>
|
|
52
|
-
onMentionSearch:
|
|
55
|
+
getItems: () => q.current,
|
|
56
|
+
onMentionSearch: D,
|
|
57
|
+
onMentionInserted: (e) => J.current?.(e),
|
|
53
58
|
noResultsLabel: V.mentionNoResults ?? "No results"
|
|
54
59
|
})
|
|
55
60
|
}) : null, [
|
|
56
61
|
Z,
|
|
57
62
|
w,
|
|
58
|
-
|
|
59
|
-
]),
|
|
63
|
+
D
|
|
64
|
+
]), ge = S !== void 0 && S > 0 || k || j, $ = ie({
|
|
60
65
|
shouldRerenderOnTransaction: !0,
|
|
61
66
|
extensions: [
|
|
62
67
|
ae.configure({ link: { openOnClick: !1 } }),
|
|
63
68
|
oe,
|
|
64
69
|
se,
|
|
65
70
|
ce.configure({ multicolor: !0 }),
|
|
66
|
-
|
|
71
|
+
_.configure({
|
|
67
72
|
transformPastedText: !0,
|
|
68
73
|
transformCopiedText: !1
|
|
69
74
|
}),
|
|
70
|
-
|
|
71
|
-
...H.showTableButton ? [
|
|
72
|
-
...H.showImageButton ? [
|
|
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: P ?? "",
|
|
84
|
+
content: P ?? v ?? "",
|
|
80
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,7 +93,7 @@ function y({ disabled: y = !1, error: b = !1, height: le, helperText: x, maxChar
|
|
|
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 }) {
|
|
@@ -110,7 +115,7 @@ function y({ disabled: y = !1, error: b = !1, height: le, helperText: x, maxChar
|
|
|
110
115
|
y,
|
|
111
116
|
O
|
|
112
117
|
]);
|
|
113
|
-
let
|
|
118
|
+
let _e = $?.storage.characterCount?.characters?.() ?? 0, ve = $?.storage.characterCount?.words?.() ?? 0, ye = k || S !== void 0 && S > 0 || j || !!x;
|
|
114
119
|
return /* @__PURE__ */ f(l, {
|
|
115
120
|
sx: U ? {
|
|
116
121
|
position: "fixed",
|
|
@@ -124,7 +129,7 @@ function y({ disabled: y = !1, error: b = !1, height: le, helperText: x, maxChar
|
|
|
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,43 +137,43 @@ function y({ disabled: y = !1, error: b = !1, height: le, helperText: x, maxChar
|
|
|
132
137
|
} : {}
|
|
133
138
|
},
|
|
134
139
|
children: [
|
|
135
|
-
/* @__PURE__ */ f(
|
|
140
|
+
/* @__PURE__ */ f(ne, {
|
|
136
141
|
variant: "outlined",
|
|
137
142
|
sx: {
|
|
138
143
|
display: "flex",
|
|
139
144
|
flexDirection: "column",
|
|
140
145
|
overflow: "hidden",
|
|
141
|
-
...U || X ? { flex: 1 } : { height:
|
|
146
|
+
...U || X ? { flex: 1 } : { height: he },
|
|
142
147
|
borderColor: b ? "error.main" : void 0,
|
|
143
148
|
"&:focus-within": {
|
|
144
149
|
borderColor: b ? "error.main" : "primary.main",
|
|
145
150
|
borderWidth: 2
|
|
146
151
|
}
|
|
147
152
|
},
|
|
148
|
-
children: [A && !O && /* @__PURE__ */ f(
|
|
153
|
+
children: [A && !O && /* @__PURE__ */ f(re, { children: [/* @__PURE__ */ d(i, {
|
|
149
154
|
editor: $,
|
|
150
155
|
toolbarConfig: H,
|
|
151
156
|
translation: V,
|
|
152
157
|
disabled: y,
|
|
153
158
|
isFullscreen: U,
|
|
154
|
-
onToggleFullscreen: () =>
|
|
155
|
-
pasteAsPlainText:
|
|
156
|
-
onTogglePasteAsPlainText: () =>
|
|
157
|
-
}), /* @__PURE__ */ d(
|
|
159
|
+
onToggleFullscreen: () => fe((e) => !e),
|
|
160
|
+
pasteAsPlainText: W,
|
|
161
|
+
onTogglePasteAsPlainText: () => pe((e) => !e)
|
|
162
|
+
}), /* @__PURE__ */ d(u, {})] }), /* @__PURE__ */ d(ee, {
|
|
158
163
|
editor: $,
|
|
159
164
|
error: b,
|
|
160
165
|
disabled: y,
|
|
161
166
|
readonly: O
|
|
162
167
|
})]
|
|
163
168
|
}),
|
|
164
|
-
|
|
169
|
+
ye && /* @__PURE__ */ d(a, {
|
|
165
170
|
helperText: x,
|
|
166
171
|
error: b,
|
|
167
172
|
showCharacterCount: k || S !== void 0 && S > 0,
|
|
168
|
-
charCount:
|
|
173
|
+
charCount: _e,
|
|
169
174
|
maxCharacters: S && S > 0 ? S : void 0,
|
|
170
175
|
showWordCount: j,
|
|
171
|
-
wordCount:
|
|
176
|
+
wordCount: ve,
|
|
172
177
|
translation: V
|
|
173
178
|
}),
|
|
174
179
|
T && /* @__PURE__ */ d("input", {
|
|
@@ -180,4 +185,4 @@ function y({ disabled: y = !1, error: b = !1, height: le, helperText: x, maxChar
|
|
|
180
185
|
});
|
|
181
186
|
}
|
|
182
187
|
//#endregion
|
|
183
|
-
export {
|
|
188
|
+
export { v as RichTextEditor };
|
|
@@ -125,6 +125,13 @@ export type RichTextEditorProps = {
|
|
|
125
125
|
toolbarConfig?: RichTextEditorToolbarConfig;
|
|
126
126
|
translation?: Partial<RichTextEditorTranslation>;
|
|
127
127
|
value?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Initial HTML content for uncontrolled usage — set once on mount, never re-synced.
|
|
130
|
+
* Analogous to MUI TextField's `defaultValue`: the editor manages its own content
|
|
131
|
+
* after initialisation, no external state required.
|
|
132
|
+
* When both `value` and `defaultValue` are provided, `value` takes precedence.
|
|
133
|
+
*/
|
|
134
|
+
defaultValue?: string;
|
|
128
135
|
/** Breite des Editors. Zahlen → px. "auto" oder leer → 100% des Elternelements. */
|
|
129
136
|
width?: number | string;
|
|
130
137
|
/**
|
|
@@ -140,6 +147,13 @@ export type RichTextEditorProps = {
|
|
|
140
147
|
* @since 3.14.0
|
|
141
148
|
*/
|
|
142
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;
|
|
143
157
|
/**
|
|
144
158
|
* Character that triggers the mention autocomplete popup. Default: "@"
|
|
145
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 };
|