Add dynamic color support for receivers
Introduced a `Color` property to `ReceiverDraft` and `SignatureFieldDraft` models, enabling dynamic color assignment from a predefined palette (`ReceiverPalette`). Updated the UI to reflect receiver-specific colors in the sender-receiver chips, placement mode hint bar, and signature placement button. Refactored PDF rendering logic to dynamically derive visual styles (fill, border, and text colors) from receiver colors. Added a `HexToXColor` utility for converting hex color strings to `PdfSharp.Drawing.XColor`. Removed hardcoded visual styles and replaced them with dynamic, receiver-specific styling. Simplified receiver addition logic to automatically assign colors from the palette. These changes improve clarity and maintainability while enhancing the user experience.
This commit is contained in:
@@ -74,7 +74,8 @@
|
|||||||
<div class="sender-receivers-list">
|
<div class="sender-receivers-list">
|
||||||
@foreach (var receiver in _receivers)
|
@foreach (var receiver in _receivers)
|
||||||
{
|
{
|
||||||
<div class="sender-receiver-chip">
|
<div class="sender-receiver-chip"
|
||||||
|
style="border-left: 3px solid @receiver.Color;">
|
||||||
<div class="sender-receiver-chip__body">
|
<div class="sender-receiver-chip__body">
|
||||||
<div class="sender-receiver-chip__name">@receiver.FullName</div>
|
<div class="sender-receiver-chip__name">@receiver.FullName</div>
|
||||||
<div class="sender-receiver-chip__email">@receiver.Email</div>
|
<div class="sender-receiver-chip__email">@receiver.Email</div>
|
||||||
@@ -87,6 +88,7 @@
|
|||||||
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change sender-toolbar-action-btn sender-toolbar-action-btn--compact
|
<button class="pdf-toolbar__btn pdf-toolbar__btn--signature-change sender-toolbar-action-btn sender-toolbar-action-btn--compact
|
||||||
@(_pendingReceiverForPlacement?.Id == receiver.Id ? "pdf-toolbar__btn--signature-change-active" : "")"
|
@(_pendingReceiverForPlacement?.Id == receiver.Id ? "pdf-toolbar__btn--signature-change-active" : "")"
|
||||||
@onclick="() => ActivatePlacementForReceiver(receiver)"
|
@onclick="() => ActivatePlacementForReceiver(receiver)"
|
||||||
|
style="@(_pendingReceiverForPlacement?.Id == receiver.Id ? $"background:{receiver.Color};color:#fff;border-color:{receiver.Color};" : $"color:{receiver.Color};border-color:{receiver.Color};")"
|
||||||
title="Signaturfeld platzieren">
|
title="Signaturfeld platzieren">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10z" />
|
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10z" />
|
||||||
@@ -155,7 +157,7 @@
|
|||||||
@* Placement mode hint bar *@
|
@* Placement mode hint bar *@
|
||||||
@if (_pendingReceiverForPlacement is not null)
|
@if (_pendingReceiverForPlacement is not null)
|
||||||
{
|
{
|
||||||
<div style="background: #4F46E5; color: white; font-size: 0.75rem; font-weight: 500;
|
<div style="background: @_pendingReceiverForPlacement.Color; color: white; font-size: 0.75rem; font-weight: 500;
|
||||||
padding: 0.3rem 1.5rem; text-align: center; letter-spacing: 0.01em;">
|
padding: 0.3rem 1.5rem; text-align: center; letter-spacing: 0.01em;">
|
||||||
📌 Klicken Sie auf die Stelle im Dokument für <strong>@_pendingReceiverForPlacement.FullName</strong>.
|
📌 Klicken Sie auf die Stelle im Dokument für <strong>@_pendingReceiverForPlacement.FullName</strong>.
|
||||||
<button @onclick="CancelPlacement"
|
<button @onclick="CancelPlacement"
|
||||||
@@ -483,7 +485,8 @@
|
|||||||
XPt: xPt,
|
XPt: xPt,
|
||||||
YPt: yPt,
|
YPt: yPt,
|
||||||
Page: page1Based,
|
Page: page1Based,
|
||||||
ReceiverName: _pendingReceiverForPlacement.FullName);
|
ReceiverName: _pendingReceiverForPlacement.FullName,
|
||||||
|
Color: _pendingReceiverForPlacement.Color);
|
||||||
|
|
||||||
_signatureFields.Add(field);
|
_signatureFields.Add(field);
|
||||||
_pendingReceiverForPlacement = null;
|
_pendingReceiverForPlacement = null;
|
||||||
@@ -565,25 +568,6 @@
|
|||||||
var document = PdfSharp.Pdf.IO.PdfReader.Open(
|
var document = PdfSharp.Pdf.IO.PdfReader.Open(
|
||||||
inputMs, PdfSharp.Pdf.IO.PdfDocumentOpenMode.Modify);
|
inputMs, PdfSharp.Pdf.IO.PdfDocumentOpenMode.Modify);
|
||||||
|
|
||||||
// Visual style — same palette as the receiver-side placeholder
|
|
||||||
var fillBrush = new PdfSharp.Drawing.XSolidBrush(PdfSharp.Drawing.XColor.FromArgb( 40, 60, 80, 160));
|
|
||||||
var borderPen = new PdfSharp.Drawing.XPen(PdfSharp.Drawing.XColor.FromArgb(200, 60, 80, 200), 1.5);
|
|
||||||
var textBrush = new PdfSharp.Drawing.XSolidBrush(PdfSharp.Drawing.XColor.FromArgb(200, 40, 60, 140));
|
|
||||||
var nameBrush = new PdfSharp.Drawing.XSolidBrush(PdfSharp.Drawing.XColor.FromArgb(255, 30, 30, 100));
|
|
||||||
var fontLabel = new PdfSharp.Drawing.XFont("Arial", 9, PdfSharp.Drawing.XFontStyleEx.Bold);
|
|
||||||
var fontName = new PdfSharp.Drawing.XFont("Arial", 7, PdfSharp.Drawing.XFontStyleEx.Regular);
|
|
||||||
|
|
||||||
var fmtCenter = new PdfSharp.Drawing.XStringFormat
|
|
||||||
{
|
|
||||||
Alignment = PdfSharp.Drawing.XStringAlignment.Center,
|
|
||||||
LineAlignment = PdfSharp.Drawing.XLineAlignment.Center,
|
|
||||||
};
|
|
||||||
var fmtBottomCenter = new PdfSharp.Drawing.XStringFormat
|
|
||||||
{
|
|
||||||
Alignment = PdfSharp.Drawing.XStringAlignment.Center,
|
|
||||||
LineAlignment = PdfSharp.Drawing.XLineAlignment.Far,
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var field in fields)
|
foreach (var field in fields)
|
||||||
{
|
{
|
||||||
int pageIndex = field.Page - 1;
|
int pageIndex = field.Page - 1;
|
||||||
@@ -592,6 +576,21 @@
|
|||||||
var page = document.Pages[pageIndex];
|
var page = document.Pages[pageIndex];
|
||||||
using var gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(page);
|
using var gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(page);
|
||||||
|
|
||||||
|
// Derive colours from the receiver's hex colour
|
||||||
|
var fillBrush = new PdfSharp.Drawing.XSolidBrush(HexToXColor(field.Color, alpha: 35));
|
||||||
|
var borderPen = new PdfSharp.Drawing.XPen(HexToXColor(field.Color, alpha: 210), 1.5);
|
||||||
|
var textBrush = new PdfSharp.Drawing.XSolidBrush(HexToXColor(field.Color, alpha: 200));
|
||||||
|
var nameBrush = new PdfSharp.Drawing.XSolidBrush(HexToXColor(field.Color, alpha: 230));
|
||||||
|
|
||||||
|
var fontLabel = new PdfSharp.Drawing.XFont("Arial", 9, PdfSharp.Drawing.XFontStyleEx.Bold);
|
||||||
|
var fontName = new PdfSharp.Drawing.XFont("Arial", 7, PdfSharp.Drawing.XFontStyleEx.Regular);
|
||||||
|
|
||||||
|
var fmtCenter = new PdfSharp.Drawing.XStringFormat
|
||||||
|
{
|
||||||
|
Alignment = PdfSharp.Drawing.XStringAlignment.Center,
|
||||||
|
LineAlignment = PdfSharp.Drawing.XLineAlignment.Center,
|
||||||
|
};
|
||||||
|
|
||||||
var rect = new PdfSharp.Drawing.XRect(field.XPt, field.YPt, SigWidthPt, SigHeightPt);
|
var rect = new PdfSharp.Drawing.XRect(field.XPt, field.YPt, SigWidthPt, SigHeightPt);
|
||||||
|
|
||||||
gfx.DrawRectangle(fillBrush, rect);
|
gfx.DrawRectangle(fillBrush, rect);
|
||||||
@@ -616,6 +615,13 @@
|
|||||||
return outputMs.ToArray();
|
return outputMs.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Converts a CSS hex colour string (e.g. "#4F46E5") to a PdfSharp XColor.</summary>
|
||||||
|
static PdfSharp.Drawing.XColor HexToXColor(string hex, int alpha)
|
||||||
|
{
|
||||||
|
var c = System.Drawing.ColorTranslator.FromHtml(hex);
|
||||||
|
return PdfSharp.Drawing.XColor.FromArgb(alpha, c.R, c.G, c.B);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Receiver popup ──
|
// ── Receiver popup ──
|
||||||
void OpenAddReceiverPopup()
|
void OpenAddReceiverPopup()
|
||||||
{
|
{
|
||||||
@@ -777,22 +783,36 @@
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
_receivers.Add(new ReceiverDraft(Guid.NewGuid(), fullName, email, phoneNumber));
|
_receivers.Add(new ReceiverDraft(Guid.NewGuid(), fullName, email, phoneNumber,
|
||||||
|
ReceiverPalette[_receivers.Count % ReceiverPalette.Length]));
|
||||||
PersistSession();
|
PersistSession();
|
||||||
CloseAddReceiverPopup();
|
CloseAddReceiverPopup();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Models ──
|
// ── Models ──
|
||||||
record SignatureFieldDraft(double XPt, double YPt, int Page, string ReceiverName);
|
record SignatureFieldDraft(double XPt, double YPt, int Page, string ReceiverName, string Color);
|
||||||
|
|
||||||
record NormalisedCoords(double NormX, double NormY, int PageIndex);
|
record NormalisedCoords(double NormX, double NormY, int PageIndex);
|
||||||
|
|
||||||
record ReceiverDraft(Guid Id, string FullName, string Email, string PhoneNumber);
|
record ReceiverDraft(Guid Id, string FullName, string Email, string PhoneNumber, string Color);
|
||||||
|
|
||||||
record EditorSessionData(
|
record EditorSessionData(
|
||||||
byte[] OriginalPdfBytes,
|
byte[] OriginalPdfBytes,
|
||||||
List<SignatureFieldDraft> Fields,
|
List<SignatureFieldDraft> Fields,
|
||||||
string FileName,
|
string FileName,
|
||||||
List<ReceiverDraft> Receivers);
|
List<ReceiverDraft> Receivers);
|
||||||
|
|
||||||
|
// ── Receiver colour palette (cycles when > 8 receivers) ──
|
||||||
|
static readonly string[] ReceiverPalette =
|
||||||
|
[
|
||||||
|
"#4F46E5", // indigo
|
||||||
|
"#059669", // emerald
|
||||||
|
"#DC2626", // red
|
||||||
|
"#D97706", // amber
|
||||||
|
"#7C3AED", // violet
|
||||||
|
"#0891B2", // cyan
|
||||||
|
"#BE185D", // pink
|
||||||
|
"#65A30D", // lime
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user