@thebuoyant-tsdev/mui-ts-library 3.12.0 → 3.14.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.
@@ -0,0 +1,65 @@
1
+ export type ColorPickerFormat = "hex" | "rgb" | "hsl";
2
+ /** Clean payload passed to `onChange` alongside the primary hex string — no internal HSV state exposed. */
3
+ export type ColorPickerColorInfo = {
4
+ hex: string;
5
+ rgb: {
6
+ r: number;
7
+ g: number;
8
+ b: number;
9
+ a: number;
10
+ };
11
+ hsl: {
12
+ h: number;
13
+ s: number;
14
+ l: number;
15
+ a: number;
16
+ };
17
+ };
18
+ export type ColorPickerTranslation = {
19
+ formatLabel: string;
20
+ hexFieldLabel: string;
21
+ redLabel: string;
22
+ greenLabel: string;
23
+ blueLabel: string;
24
+ hueFieldLabel: string;
25
+ saturationFieldLabel: string;
26
+ lightnessFieldLabel: string;
27
+ alphaFieldLabel: string;
28
+ eyeDropperLabel: string;
29
+ savedColorsLabel: string;
30
+ gradientAreaLabel: string;
31
+ hueSliderLabel: string;
32
+ };
33
+ export declare const DEFAULT_COLOR_PICKER_TRANSLATION: Required<ColorPickerTranslation>;
34
+ export type ColorPickerProps = {
35
+ /** Current color — accepts hex (#rgb, #rgba, #rrggbb, #rrggbbaa), rgb()/rgba(), or hsl()/hsla(). */
36
+ value: string;
37
+ /** Fires on every change — live while dragging, on every valid keystroke, and on swatch/eyedropper picks — with a normalized hex string plus a clean rgb/hsl breakdown. */
38
+ onChange: (hex: string, info: ColorPickerColorInfo) => void;
39
+ /** Fires once per "gesture" instead of continuously: on pointer-up after a drag, on blur after typing, or immediately for atomic actions (swatch click, eyedropper). Same dual-callback pattern as MUI's own `Slider` — use this instead of debouncing `onChange` yourself for expensive side effects (persisting to a backend, etc.). */
40
+ onChangeCommitted?: (hex: string, info: ColorPickerColorInfo) => void;
41
+ /** Initial display format for the value field — uncontrolled after mount (default: 'hex'). */
42
+ defaultFormat?: ColorPickerFormat;
43
+ /** Fires when the user switches the display format via the dropdown. */
44
+ onFormatChange?: (format: ColorPickerFormat) => void;
45
+ /** Shows the alpha slider and opacity field (default: true). */
46
+ showAlpha?: boolean;
47
+ /** Shows the eyedropper tool — auto-hidden when the browser doesn't support the EyeDropper API regardless of this prop (default: true). */
48
+ showEyeDropper?: boolean;
49
+ /** Shows the eyedropper button + hue/alpha slider row (default: true). Set `false` for a minimal gradient-area-only picker. */
50
+ showSliderSection?: boolean;
51
+ /** Shows the format dropdown + hex/RGB/HSL/alpha value fields row (default: true). Set `false` for a slider-only picker. */
52
+ showInputSection?: boolean;
53
+ /** Swatches shown below the picker — click to select. Purely a display list; the caller owns persistence. */
54
+ savedColors?: string[];
55
+ disabled?: boolean;
56
+ /** Scales the gradient area, slider thickness, and swatch size (default: 'medium'). */
57
+ colorGradientSize?: "small" | "medium";
58
+ /** Size of the format dropdown and value/alpha input fields, independent of `colorGradientSize` (default: 'medium'). */
59
+ inputSize?: "small" | "medium";
60
+ /** Overall panel width in px (default: 280). */
61
+ width?: number;
62
+ /** Form-integration: renders a hidden input so the value participates in native/React Hook Form/Formik form submission. */
63
+ name?: string;
64
+ translation?: Partial<ColorPickerTranslation>;
65
+ };
@@ -0,0 +1,18 @@
1
+ //#region src/components/color-picker/ColorPicker.types.ts
2
+ var e = {
3
+ formatLabel: "Color format",
4
+ hexFieldLabel: "Hex value",
5
+ redLabel: "Red",
6
+ greenLabel: "Green",
7
+ blueLabel: "Blue",
8
+ hueFieldLabel: "Hue",
9
+ saturationFieldLabel: "Saturation",
10
+ lightnessFieldLabel: "Lightness",
11
+ alphaFieldLabel: "Alpha",
12
+ eyeDropperLabel: "Pick color from screen",
13
+ savedColorsLabel: "Saved colors",
14
+ gradientAreaLabel: "Saturation and brightness",
15
+ hueSliderLabel: "Hue"
16
+ };
17
+ //#endregion
18
+ export { e as DEFAULT_COLOR_PICKER_TRANSLATION };
@@ -0,0 +1,51 @@
1
+ export type RgbaColor = {
2
+ r: number;
3
+ g: number;
4
+ b: number;
5
+ a: number;
6
+ };
7
+ export type HsvaColor = {
8
+ h: number;
9
+ s: number;
10
+ v: number;
11
+ a: number;
12
+ };
13
+ export type HslaColor = {
14
+ h: number;
15
+ s: number;
16
+ l: number;
17
+ a: number;
18
+ };
19
+ export declare function clamp(value: number, min: number, max: number): number;
20
+ export declare function rgbaToHex({ r, g, b, a }: RgbaColor): string;
21
+ export declare function hexToRgba(hex: string): RgbaColor | null;
22
+ export declare function hsvToRgb(h: number, s: number, v: number): {
23
+ r: number;
24
+ g: number;
25
+ b: number;
26
+ };
27
+ export declare function rgbToHsv(r: number, g: number, b: number): {
28
+ h: number;
29
+ s: number;
30
+ v: number;
31
+ };
32
+ export declare function hslToRgb(h: number, s: number, l: number): {
33
+ r: number;
34
+ g: number;
35
+ b: number;
36
+ };
37
+ export declare function rgbToHsl(r: number, g: number, b: number): {
38
+ h: number;
39
+ s: number;
40
+ l: number;
41
+ };
42
+ export declare function hsvaToRgba({ h, s, v, a }: HsvaColor): RgbaColor;
43
+ export declare function rgbaToHsva({ r, g, b, a }: RgbaColor): HsvaColor;
44
+ export declare function hsvaToHsla({ h, s, v, a }: HsvaColor): HslaColor;
45
+ export declare function hslaToHsva({ h, s, l, a }: HslaColor): HsvaColor;
46
+ /** Accepts #hex (3/4/6/8 digits), rgb()/rgba(), and hsl()/hsla() — returns null when unparseable. */
47
+ export declare function parseColorString(input: string): RgbaColor | null;
48
+ export declare function formatRgbString({ r, g, b, a }: RgbaColor): string;
49
+ export declare function formatHslString({ h, s, l, a }: HslaColor): string;
50
+ /** WCAG-style luminance heuristic — picks black or white text for readable contrast on a given background. */
51
+ export declare function getContrastTextColor(r: number, g: number, b: number): "#000000" | "#ffffff";
@@ -0,0 +1,166 @@
1
+ //#region src/components/color-picker/util/colorConversion.util.ts
2
+ function e(e, t, n) {
3
+ return Math.min(n, Math.max(t, e));
4
+ }
5
+ function t(t) {
6
+ return e(Math.round(t), 0, 255).toString(16).padStart(2, "0");
7
+ }
8
+ function n({ r: e, g: n, b: r, a: i }) {
9
+ let a = `#${t(e)}${t(n)}${t(r)}`;
10
+ return i < 1 ? `${a}${t(i * 255)}` : a;
11
+ }
12
+ function r(e) {
13
+ let t = e.trim().replace(/^#/, ""), n, r, i, a = 1;
14
+ if (t.length === 3 || t.length === 4) n = parseInt(t[0] + t[0], 16), r = parseInt(t[1] + t[1], 16), i = parseInt(t[2] + t[2], 16), t.length === 4 && (a = parseInt(t[3] + t[3], 16) / 255);
15
+ else if (t.length === 6 || t.length === 8) n = parseInt(t.slice(0, 2), 16), r = parseInt(t.slice(2, 4), 16), i = parseInt(t.slice(4, 6), 16), t.length === 8 && (a = parseInt(t.slice(6, 8), 16) / 255);
16
+ else return null;
17
+ return [
18
+ n,
19
+ r,
20
+ i,
21
+ a
22
+ ].some((e) => Number.isNaN(e)) ? null : {
23
+ r: n,
24
+ g: r,
25
+ b: i,
26
+ a
27
+ };
28
+ }
29
+ function i(e, t, n) {
30
+ let r = t / 100, i = n / 100, a = i * r, o = a * (1 - Math.abs(e / 60 % 2 - 1)), s = i - a, [c, l, u] = e < 60 ? [
31
+ a,
32
+ o,
33
+ 0
34
+ ] : e < 120 ? [
35
+ o,
36
+ a,
37
+ 0
38
+ ] : e < 180 ? [
39
+ 0,
40
+ a,
41
+ o
42
+ ] : e < 240 ? [
43
+ 0,
44
+ o,
45
+ a
46
+ ] : e < 300 ? [
47
+ o,
48
+ 0,
49
+ a
50
+ ] : [
51
+ a,
52
+ 0,
53
+ o
54
+ ];
55
+ return {
56
+ r: (c + s) * 255,
57
+ g: (l + s) * 255,
58
+ b: (u + s) * 255
59
+ };
60
+ }
61
+ function a(e, t, n) {
62
+ let r = e / 255, i = t / 255, a = n / 255, o = Math.max(r, i, a), s = o - Math.min(r, i, a), c = 0;
63
+ s !== 0 && (c = o === r ? 60 * ((i - a) / s % 6) : o === i ? 60 * ((a - r) / s + 2) : 60 * ((r - i) / s + 4)), c < 0 && (c += 360);
64
+ let l = o === 0 ? 0 : s / o;
65
+ return {
66
+ h: c,
67
+ s: l * 100,
68
+ v: o * 100
69
+ };
70
+ }
71
+ function o(e, t, n) {
72
+ let r = t / 100, i = n / 100, a = (1 - Math.abs(2 * i - 1)) * r, o = a * (1 - Math.abs(e / 60 % 2 - 1)), s = i - a / 2, [c, l, u] = e < 60 ? [
73
+ a,
74
+ o,
75
+ 0
76
+ ] : e < 120 ? [
77
+ o,
78
+ a,
79
+ 0
80
+ ] : e < 180 ? [
81
+ 0,
82
+ a,
83
+ o
84
+ ] : e < 240 ? [
85
+ 0,
86
+ o,
87
+ a
88
+ ] : e < 300 ? [
89
+ o,
90
+ 0,
91
+ a
92
+ ] : [
93
+ a,
94
+ 0,
95
+ o
96
+ ];
97
+ return {
98
+ r: (c + s) * 255,
99
+ g: (l + s) * 255,
100
+ b: (u + s) * 255
101
+ };
102
+ }
103
+ function s(e, t, n) {
104
+ let r = e / 255, i = t / 255, a = n / 255, o = Math.max(r, i, a), s = Math.min(r, i, a), c = (o + s) / 2, l = o - s, u = 0, d = 0;
105
+ return l !== 0 && (d = c > .5 ? l / (2 - o - s) : l / (o + s), u = o === r ? 60 * ((i - a) / l % 6) : o === i ? 60 * ((a - r) / l + 2) : 60 * ((r - i) / l + 4)), u < 0 && (u += 360), {
106
+ h: u,
107
+ s: d * 100,
108
+ l: c * 100
109
+ };
110
+ }
111
+ function c({ h: e, s: t, v: n, a: r }) {
112
+ let { r: a, g: o, b: s } = i(e, t, n);
113
+ return {
114
+ r: a,
115
+ g: o,
116
+ b: s,
117
+ a: r
118
+ };
119
+ }
120
+ function l({ r: e, g: t, b: n, a: r }) {
121
+ let { h: i, s: o, v: s } = a(e, t, n);
122
+ return {
123
+ h: i,
124
+ s: o,
125
+ v: s,
126
+ a: r
127
+ };
128
+ }
129
+ function u({ h: e, s: t, v: n, a: r }) {
130
+ let { r: a, g: o, b: c } = i(e, t, n);
131
+ return {
132
+ ...s(a, o, c),
133
+ a: r
134
+ };
135
+ }
136
+ function d({ h: e, s: t, l: n, a: r }) {
137
+ let { r: i, g: s, b: c } = o(e, t, n);
138
+ return {
139
+ ...a(i, s, c),
140
+ a: r
141
+ };
142
+ }
143
+ function f(e) {
144
+ let t = e.trim();
145
+ if (t.startsWith("#")) return r(t);
146
+ let n = t.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)$/i);
147
+ if (n) return {
148
+ r: Number(n[1]),
149
+ g: Number(n[2]),
150
+ b: Number(n[3]),
151
+ a: n[4] === void 0 ? 1 : Number(n[4])
152
+ };
153
+ let i = t.match(/^hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*(?:,\s*([\d.]+)\s*)?\)$/i);
154
+ if (i) {
155
+ let { r: e, g: t, b: n } = o(Number(i[1]), Number(i[2]), Number(i[3]));
156
+ return {
157
+ r: e,
158
+ g: t,
159
+ b: n,
160
+ a: i[4] === void 0 ? 1 : Number(i[4])
161
+ };
162
+ }
163
+ return null;
164
+ }
165
+ //#endregion
166
+ export { e as clamp, r as hexToRgba, d as hslaToHsva, u as hsvaToHsla, c as hsvaToRgba, f as parseColorString, n as rgbaToHex, l as rgbaToHsva };
@@ -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, name, placeholder, readonly, showCharacterCount, showToolbar, showWordCount, toolbarConfig, translation, value, width, onBlur, onChange, onFocus, onMarkdownChange, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
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, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
@@ -1,56 +1,83 @@
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
- import { RichTextEditorContent as r } from "./RichTextEditorContent.js";
4
- import { RichTextEditorToolbar as i } from "./RichTextEditorToolbar.js";
5
- import { RichTextEditorFooter as a } from "./RichTextEditorFooter.js";
6
- import { useEffect as o, useRef as s, useState as c } from "react";
7
- import { Box as l, Divider as u, Paper as d } from "@mui/material";
8
- import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
9
- import { useEditor as h } from "@tiptap/react";
10
- import { StarterKit as g } from "@tiptap/starter-kit";
11
- import { TextStyle as _ } from "@tiptap/extension-text-style";
12
- import { Color as v } from "@tiptap/extension-color";
13
- import { Highlight as ee } from "@tiptap/extension-highlight";
14
- import { Placeholder as y } from "@tiptap/extension-placeholder";
15
- import { CharacterCount as b } from "@tiptap/extension-character-count";
16
- import { TableKit as x } from "@tiptap/extension-table";
17
- import { Image as S } from "@tiptap/extension-image";
18
- import { Markdown as C } from "tiptap-markdown";
3
+ import { buildMentionSuggestion as r } from "./RichTextEditorMentionSuggestion.js";
4
+ import { RichTextEditorContent as i } from "./RichTextEditorContent.js";
5
+ import { RichTextEditorToolbar as a } from "./RichTextEditorToolbar.js";
6
+ import { RichTextEditorFooter as ee } from "./RichTextEditorFooter.js";
7
+ import { useEffect as o, useMemo as te, useRef as s, useState as c } from "react";
8
+ import { Box as l, Divider as u, Paper as ne } from "@mui/material";
9
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import { useEditor as re } from "@tiptap/react";
11
+ import { Mention as m } from "@tiptap/extension-mention";
12
+ import { StarterKit as h } from "@tiptap/starter-kit";
13
+ import { TextStyle as ie } from "@tiptap/extension-text-style";
14
+ import { Color as g } from "@tiptap/extension-color";
15
+ import { Highlight as _ } from "@tiptap/extension-highlight";
16
+ import { Placeholder as ae } from "@tiptap/extension-placeholder";
17
+ import { CharacterCount as v } from "@tiptap/extension-character-count";
18
+ import { TableKit as oe } from "@tiptap/extension-table";
19
+ import { Image as y } from "@tiptap/extension-image";
20
+ import { Markdown as b } from "tiptap-markdown";
19
21
  //#region src/components/rich-text-editor/RichTextEditor.tsx
20
- function w({ disabled: w = !1, error: T = !1, height: E, helperText: D, maxCharacters: O, name: k, placeholder: A, readonly: j = !1, showCharacterCount: M = !1, showToolbar: N = !0, showWordCount: P = !1, toolbarConfig: F, translation: I, value: L, width: R, onBlur: z, onChange: B, onFocus: V, onMarkdownChange: te }) {
22
+ function x({ disabled: x = !1, error: S = !1, height: se, helperText: C, maxCharacters: w, mentionItems: T, mentionTriggerChar: E = "@", name: D, onMentionSearch: O, placeholder: k, readonly: A = !1, showCharacterCount: j = !1, showToolbar: M = !0, showWordCount: N = !1, toolbarConfig: P, translation: F, value: I, width: L, onBlur: R, onChange: z, onFocus: B, onMarkdownChange: V }) {
21
23
  let H = {
22
24
  ...n,
23
- ...I
25
+ ...F
24
26
  }, U = {
25
27
  ...t,
26
- ...F
27
- }, [W, G] = c(!1), [K, q] = c(!1), J = s(K);
28
+ ...P
29
+ }, [W, G] = c(!1), [K, ce] = c(!1), q = s(K), J = s(T ?? []);
28
30
  o(() => {
29
- J.current = K;
31
+ J.current = T ?? [];
32
+ }, [T]), o(() => {
33
+ q.current = K;
30
34
  }, [K]);
31
- let Y = e(E), X = e(R), Z = Y === "auto", Q = Z ? void 0 : Y ?? 200, ne = O !== void 0 && O > 0 || M || P, $ = h({
35
+ let Y = e(se), le = e(L), X = Y === "auto", ue = X ? void 0 : Y ?? 200, Z = T !== void 0 || O !== void 0, Q = te(() => Z ? m.configure({
36
+ HTMLAttributes: { class: "rte-mention" },
37
+ renderHTML({ options: e, node: t }) {
38
+ return [
39
+ "span",
40
+ {
41
+ ...e.HTMLAttributes,
42
+ "data-id": t.attrs.id,
43
+ "data-label": t.attrs.label
44
+ },
45
+ `${E}${t.attrs.label}`
46
+ ];
47
+ },
48
+ suggestion: r({
49
+ getItems: () => J.current,
50
+ onMentionSearch: O,
51
+ noResultsLabel: H.mentionNoResults ?? "No results"
52
+ })
53
+ }) : null, [
54
+ Z,
55
+ E,
56
+ O
57
+ ]), de = w !== void 0 && w > 0 || j || N, $ = re({
32
58
  shouldRerenderOnTransaction: !0,
33
59
  extensions: [
34
- g.configure({ link: { openOnClick: !1 } }),
35
- _,
36
- v,
37
- ee.configure({ multicolor: !0 }),
38
- C.configure({
60
+ h.configure({ link: { openOnClick: !1 } }),
61
+ ie,
62
+ g,
63
+ _.configure({ multicolor: !0 }),
64
+ b.configure({
39
65
  transformPastedText: !0,
40
66
  transformCopiedText: !1
41
67
  }),
42
- y.configure({ placeholder: A ?? "" }),
43
- ...U.showTableButton ? [x] : [],
44
- ...U.showImageButton ? [S.configure({
68
+ ae.configure({ placeholder: k ?? "" }),
69
+ ...U.showTableButton ? [oe] : [],
70
+ ...U.showImageButton ? [y.configure({
45
71
  inline: !1,
46
72
  allowBase64: !0
47
73
  })] : [],
48
- ...ne ? O !== void 0 && O > 0 ? [b.configure({ limit: O })] : [b] : []
74
+ ...Q ? [Q] : [],
75
+ ...de ? w !== void 0 && w > 0 ? [v.configure({ limit: w })] : [v] : []
49
76
  ],
50
- content: L ?? "",
51
- editable: !w && !j,
77
+ content: I ?? "",
78
+ editable: !x && !A,
52
79
  editorProps: { handlePaste(e, t) {
53
- if (!J.current) return !1;
80
+ if (!q.current) return !1;
54
81
  let n = t.clipboardData?.getData("text/plain");
55
82
  if (!n) return !1;
56
83
  t.preventDefault();
@@ -58,26 +85,26 @@ function w({ disabled: w = !1, error: T = !1, height: E, helperText: D, maxChara
58
85
  return e.dispatch(r.tr.insertText(n, r.selection.from, r.selection.to)), !0;
59
86
  } },
60
87
  onUpdate({ editor: e }) {
61
- B?.(e.getHTML()), te?.(e.storage.markdown.getMarkdown());
88
+ z?.(e.getHTML()), V?.(e.storage.markdown.getMarkdown());
62
89
  },
63
90
  onBlur() {
64
- z?.();
91
+ R?.();
65
92
  },
66
93
  onFocus() {
67
- V?.();
94
+ B?.();
68
95
  }
69
96
  });
70
97
  o(() => {
71
- !$ || L === void 0 || $.getHTML() !== L && $.commands.setContent(L, { emitUpdate: !1 });
72
- }, [$, L]), o(() => {
73
- $ && $.setEditable(!w && !j);
98
+ !$ || I === void 0 || $.getHTML() !== I && $.commands.setContent(I, { emitUpdate: !1 });
99
+ }, [$, I]), o(() => {
100
+ $ && $.setEditable(!x && !A);
74
101
  }, [
75
102
  $,
76
- w,
77
- j
103
+ x,
104
+ A
78
105
  ]);
79
- let re = $?.storage.characterCount?.characters?.() ?? 0, ie = $?.storage.characterCount?.words?.() ?? 0, ae = M || O !== void 0 && O > 0 || P || !!D;
80
- return /* @__PURE__ */ m(l, {
106
+ let fe = $?.storage.characterCount?.characters?.() ?? 0, pe = $?.storage.characterCount?.words?.() ?? 0, me = j || w !== void 0 && w > 0 || N || !!C;
107
+ return /* @__PURE__ */ p(l, {
81
108
  sx: W ? {
82
109
  position: "fixed",
83
110
  top: 0,
@@ -90,60 +117,60 @@ function w({ disabled: w = !1, error: T = !1, height: E, helperText: D, maxChara
90
117
  bgcolor: "background.default",
91
118
  p: 1
92
119
  } : {
93
- width: X ?? "100%",
94
- ...Z ? {
120
+ width: le ?? "100%",
121
+ ...X ? {
95
122
  display: "flex",
96
123
  flexDirection: "column",
97
124
  flex: 1
98
125
  } : {}
99
126
  },
100
127
  children: [
101
- /* @__PURE__ */ m(d, {
128
+ /* @__PURE__ */ p(ne, {
102
129
  variant: "outlined",
103
130
  sx: {
104
131
  display: "flex",
105
132
  flexDirection: "column",
106
133
  overflow: "hidden",
107
- ...W || Z ? { flex: 1 } : { height: Q },
108
- borderColor: T ? "error.main" : void 0,
134
+ ...W || X ? { flex: 1 } : { height: ue },
135
+ borderColor: S ? "error.main" : void 0,
109
136
  "&:focus-within": {
110
- borderColor: T ? "error.main" : "primary.main",
137
+ borderColor: S ? "error.main" : "primary.main",
111
138
  borderWidth: 2
112
139
  }
113
140
  },
114
- children: [N && !j && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(i, {
141
+ children: [M && !A && /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(a, {
115
142
  editor: $,
116
143
  toolbarConfig: U,
117
144
  translation: H,
118
- disabled: w,
145
+ disabled: x,
119
146
  isFullscreen: W,
120
147
  onToggleFullscreen: () => G((e) => !e),
121
148
  pasteAsPlainText: K,
122
- onTogglePasteAsPlainText: () => q((e) => !e)
123
- }), /* @__PURE__ */ p(u, {})] }), /* @__PURE__ */ p(r, {
149
+ onTogglePasteAsPlainText: () => ce((e) => !e)
150
+ }), /* @__PURE__ */ f(u, {})] }), /* @__PURE__ */ f(i, {
124
151
  editor: $,
125
- error: T,
126
- disabled: w,
127
- readonly: j
152
+ error: S,
153
+ disabled: x,
154
+ readonly: A
128
155
  })]
129
156
  }),
130
- ae && /* @__PURE__ */ p(a, {
131
- helperText: D,
132
- error: T,
133
- showCharacterCount: M || O !== void 0 && O > 0,
134
- charCount: re,
135
- maxCharacters: O && O > 0 ? O : void 0,
136
- showWordCount: P,
137
- wordCount: ie,
157
+ me && /* @__PURE__ */ f(ee, {
158
+ helperText: C,
159
+ error: S,
160
+ showCharacterCount: j || w !== void 0 && w > 0,
161
+ charCount: fe,
162
+ maxCharacters: w && w > 0 ? w : void 0,
163
+ showWordCount: N,
164
+ wordCount: pe,
138
165
  translation: H
139
166
  }),
140
- k && /* @__PURE__ */ p("input", {
167
+ D && /* @__PURE__ */ f("input", {
141
168
  type: "hidden",
142
- name: k,
169
+ name: D,
143
170
  value: $ ? $.getHTML() : ""
144
171
  })
145
172
  ]
146
173
  });
147
174
  }
148
175
  //#endregion
149
- export { w as RichTextEditor };
176
+ export { x as RichTextEditor };
@@ -1,3 +1,7 @@
1
+ export type MentionItem = {
2
+ id: string;
3
+ label: string;
4
+ };
1
5
  export type RichTextEditorToolbarConfig = {
2
6
  showBold?: boolean;
3
7
  showItalic?: boolean;
@@ -99,6 +103,8 @@ export type RichTextEditorTranslation = {
99
103
  markdownDialogCopy?: string;
100
104
  /** @since 3.8.0 */
101
105
  markdownDialogCopied?: string;
106
+ /** Text shown in mention dropdown when no items match the query @since 3.14.0 */
107
+ mentionNoResults?: string;
102
108
  };
103
109
  export declare const DEFAULT_RICH_TEXT_EDITOR_TRANSLATION: Required<RichTextEditorTranslation>;
104
110
  export type RichTextEditorProps = {
@@ -121,6 +127,24 @@ export type RichTextEditorProps = {
121
127
  value?: string;
122
128
  /** Breite des Editors. Zahlen → px. "auto" oder leer → 100% des Elternelements. */
123
129
  width?: number | string;
130
+ /**
131
+ * List of items for @-mention autocomplete. The Mention extension is only active when this prop is provided.
132
+ * Used for client-side filtering when `onMentionSearch` is not provided.
133
+ * @since 3.14.0
134
+ */
135
+ mentionItems?: MentionItem[];
136
+ /**
137
+ * Custom search/filter function called on each keystroke after @.
138
+ * When provided, `mentionItems` is ignored for filtering — return the relevant subset.
139
+ * Supports async for server-side search.
140
+ * @since 3.14.0
141
+ */
142
+ onMentionSearch?: (query: string) => MentionItem[] | Promise<MentionItem[]>;
143
+ /**
144
+ * Character that triggers the mention autocomplete popup. Default: "@"
145
+ * @since 3.14.0
146
+ */
147
+ mentionTriggerChar?: string;
124
148
  onBlur?: () => void;
125
149
  onChange?: (value: string) => void;
126
150
  onFocus?: () => void;
@@ -79,7 +79,8 @@ var e = {
79
79
  markdownDialogApply: "Apply",
80
80
  markdownDialogCancel: "Cancel",
81
81
  markdownDialogCopy: "Copy",
82
- markdownDialogCopied: "Copied!"
82
+ markdownDialogCopied: "Copied!",
83
+ mentionNoResults: "No results"
83
84
  };
84
85
  //#endregion
85
86
  export { e as DEFAULT_RICH_TEXT_EDITOR_TOOLBAR_CONFIG, t as DEFAULT_RICH_TEXT_EDITOR_TRANSLATION };
@@ -0,0 +1,11 @@
1
+ import type { MentionItem } from "./RichTextEditor.types";
2
+ export type MentionListProps = {
3
+ items: MentionItem[];
4
+ noResultsLabel: string;
5
+ clientRect: (() => DOMRect | null) | null;
6
+ onSelect: (item: MentionItem) => void;
7
+ };
8
+ export type MentionListRef = {
9
+ onKeyDown: (event: KeyboardEvent) => boolean;
10
+ };
11
+ export declare const RichTextEditorMentionList: import("react").ForwardRefExoticComponent<MentionListProps & import("react").RefAttributes<MentionListRef>>;
@@ -0,0 +1,54 @@
1
+ import { forwardRef as e, useEffect as t, useImperativeHandle as n, useRef as r, useState as i } from "react";
2
+ import { List as a, ListItemButton as o, Paper as s, Popper as c, Typography as l } from "@mui/material";
3
+ import { jsx as u } from "react/jsx-runtime";
4
+ //#region src/components/rich-text-editor/RichTextEditorMentionList.tsx
5
+ var d = e(function({ items: e, noResultsLabel: d, clientRect: f, onSelect: p }, m) {
6
+ let [h, g] = i(0), [_, v] = i(e), y = r(null);
7
+ e !== _ && (v(e), g(0)), t(() => {
8
+ (y.current?.children[h])?.scrollIntoView({ block: "nearest" });
9
+ }, [h]), n(m, () => ({ onKeyDown(t) {
10
+ return t.key === "ArrowUp" ? (g((t) => (t - 1 + e.length) % e.length), !0) : t.key === "ArrowDown" ? (g((t) => (t + 1) % e.length), !0) : t.key === "Enter" ? (e[h] && p(e[h]), !0) : !1;
11
+ } }));
12
+ let b = f ? { getBoundingClientRect: f } : null;
13
+ return /* @__PURE__ */ u(c, {
14
+ open: !!b,
15
+ anchorEl: b,
16
+ placement: "bottom-start",
17
+ modifiers: [{
18
+ name: "offset",
19
+ options: { offset: [0, 4] }
20
+ }],
21
+ style: { zIndex: 1400 },
22
+ children: /* @__PURE__ */ u(s, {
23
+ elevation: 4,
24
+ sx: {
25
+ maxHeight: 240,
26
+ overflow: "auto",
27
+ minWidth: 180
28
+ },
29
+ children: e.length === 0 ? /* @__PURE__ */ u(l, {
30
+ variant: "body2",
31
+ sx: {
32
+ px: 2,
33
+ py: 1,
34
+ color: "text.secondary"
35
+ },
36
+ children: d
37
+ }) : /* @__PURE__ */ u(a, {
38
+ ref: y,
39
+ dense: !0,
40
+ disablePadding: !0,
41
+ children: e.map((e, t) => /* @__PURE__ */ u(o, {
42
+ selected: t === h,
43
+ onMouseDown: (t) => {
44
+ t.preventDefault(), p(e);
45
+ },
46
+ onMouseEnter: () => g(t),
47
+ children: e.label
48
+ }, e.id))
49
+ })
50
+ })
51
+ });
52
+ });
53
+ //#endregion
54
+ export { d as RichTextEditorMentionList };