feat(email): text colour menu in WYSIWYG (LinumIQ blue + presets)

This commit is contained in:
Gerhard Scheikl
2026-05-09 16:11:49 +02:00
parent 26e4af97bc
commit f97d6dc9d2
4 changed files with 100 additions and 10 deletions
+60
View File
@@ -2,8 +2,19 @@ import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Link from "@tiptap/extension-link";
import Image from "@tiptap/extension-image";
import { TextStyle } from "@tiptap/extension-text-style";
import { Color } from "@tiptap/extension-color";
import { useEffect, useState } from "react";
const PRESET_COLORS = [
{ name: "Default", value: null as string | null },
{ name: "LinumIQ blue", value: "#0883DA" },
{ name: "Black", value: "#000000" },
{ name: "Grey", value: "#6d7175" },
{ name: "Red", value: "#d72c0d" },
{ name: "Green", value: "#1a7e3a" },
];
interface RichTextEditorProps {
/** Hidden form field name; the rendered HTML is mirrored into it. */
name: string;
@@ -70,6 +81,8 @@ export function RichTextEditor({
};
},
}).configure({ inline: false, allowBase64: true }),
TextStyle,
Color,
],
content: swapCidToLogo(defaultValue || "<p></p>", logoDataUrl),
editorProps: {
@@ -190,6 +203,8 @@ export function RichTextEditor({
title="Insert/remove link"
/>
<Sep />
<ColorMenu editor={editor} />
<Sep />
<ToolbarButton
onClick={() => editor?.chain().focus().undo().run()}
active={false}
@@ -276,6 +291,51 @@ function Sep() {
return <span aria-hidden style={{ width: 1, background: "#c9cccf", margin: "2px 4px" }} />;
}
function ColorMenu({ editor }: { editor: ReturnType<typeof useEditor> | null }) {
if (!editor) return null;
return (
<span style={{ display: "inline-flex", gap: 2, alignItems: "center" }} title="Text colour">
{PRESET_COLORS.map((c) => (
<button
key={c.name}
type="button"
onClick={() =>
c.value
? editor.chain().focus().setColor(c.value).run()
: editor.chain().focus().unsetColor().run()
}
title={c.name}
style={{
width: 20,
height: 20,
border: "1px solid #c9cccf",
borderRadius: 4,
background: c.value ?? "#fff",
cursor: "pointer",
position: "relative",
}}
>
{c.value === null ? (
<span
aria-hidden
style={{
position: "absolute",
inset: 0,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 11,
}}
>
×
</span>
) : null}
</button>
))}
</span>
);
}
/**
* Replaces `src="cid:invoice-logo"` with the supplied URL so the editor
* can display the actual logo. Done as a string replace because TipTap
+10 -10
View File
@@ -9,8 +9,8 @@
*/
const DE_HTML = `\
<h2 style="color:#3070c0;margin:0 0 8px;font-family:Arial,Helvetica,sans-serif;">{{companyName}}</h2>
<h3 style="color:#3070c0;margin:0 0 16px;font-family:Arial,Helvetica,sans-serif;">Danke für deinen Einkauf!</h3>
<h2 style="margin:0 0 8px;font-family:Arial,Helvetica,sans-serif;"><span style="color:#0883DA">{{companyName}}</span></h2>
<h3 style="margin:0 0 16px;font-family:Arial,Helvetica,sans-serif;"><span style="color:#0883DA">Danke für deinen Einkauf!</span></h3>
<p style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.5;">
Die Rechnung befindet sich im Anhang.
</p>
@@ -22,14 +22,14 @@ Besten Dank!
<p style="margin-top:24px;">
<img src="cid:invoice-logo" alt="{{companyName}}" style="max-height:48px;">
</p>
<p style="font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:1.6;color:#3070c0;">
✉ <a href="mailto:{{shopEmail}}" style="color:#3070c0;">Kontakt</a><br>
🌐 <a href="{{shopWebsite}}" style="color:#3070c0;">{{shopWebsite}}</a>
<p style="font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:1.6;color:#0883DA;">
✉ <a href="mailto:{{shopEmail}}" style="color:#0883DA;">Kontakt</a><br>
🌐 <a href="{{shopWebsite}}" style="color:#0883DA;">{{shopWebsite}}</a>
</p>`;
const EN_HTML = `\
<h2 style="color:#3070c0;margin:0 0 8px;font-family:Arial,Helvetica,sans-serif;">{{companyName}}</h2>
<h3 style="color:#3070c0;margin:0 0 16px;font-family:Arial,Helvetica,sans-serif;">Thank you for your purchase!</h3>
<h2 style="margin:0 0 8px;font-family:Arial,Helvetica,sans-serif;"><span style="color:#0883DA">{{companyName}}</span></h2>
<h3 style="margin:0 0 16px;font-family:Arial,Helvetica,sans-serif;"><span style="color:#0883DA">Thank you for your purchase!</span></h3>
<p style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.5;">
Please find the invoice attached.
</p>
@@ -41,9 +41,9 @@ Thanks a lot!
<p style="margin-top:24px;">
<img src="cid:invoice-logo" alt="{{companyName}}" style="max-height:48px;">
</p>
<p style="font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:1.6;color:#3070c0;">
✉ <a href="mailto:{{shopEmail}}" style="color:#3070c0;">Contact</a><br>
🌐 <a href="{{shopWebsite}}" style="color:#3070c0;">{{shopWebsite}}</a>
<p style="font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:1.6;color:#0883DA;">
✉ <a href="mailto:{{shopEmail}}" style="color:#0883DA;">Contact</a><br>
🌐 <a href="{{shopWebsite}}" style="color:#0883DA;">{{shopWebsite}}</a>
</p>`;
export const DEFAULT_EMAIL_SUBJECT_DE = "Rechnung {{invoiceNumber}} {{companyName}}";