Add document preview feature to wizard popup
Introduced a document preview feature in the `CreateEnvelopeWizardPopup` component. This includes: - Injected `PdfRenderService` for rendering PDF pages. - Replaced document metadata display with a preview card. - Added `SenderPdfImageEditor` for interactive PDF previews. - Implemented state management for preview pages and overlays. - Added fallback logic to estimate PDF page count. - Enhanced `ResetWizardState` to reset preview-related fields. - Styled the preview card and wrap with new CSS classes. - Added placeholder event handlers for editor interactions.
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
@inject IStringLocalizer<Model> ModelLocalizer
|
@inject IStringLocalizer<Model> ModelLocalizer
|
||||||
@inject IStringLocalizer<Resource> UiLocalizer
|
@inject IStringLocalizer<Resource> UiLocalizer
|
||||||
@inject ReceiverService ReceiverService
|
@inject ReceiverService ReceiverService
|
||||||
|
@inject PdfRenderService PdfRenderService
|
||||||
|
|
||||||
<DxPopup Visible="@_isVisible"
|
<DxPopup Visible="@_isVisible"
|
||||||
VisibleChanged="OnPopupVisibleChangedAsync"
|
VisibleChanged="OnPopupVisibleChangedAsync"
|
||||||
@@ -238,10 +239,23 @@
|
|||||||
|
|
||||||
@if (_documentBytes is not null)
|
@if (_documentBytes is not null)
|
||||||
{
|
{
|
||||||
|
<div class="sender-create-wizard__document-preview-card">
|
||||||
<div class="sender-create-wizard__document-meta">
|
<div class="sender-create-wizard__document-meta">
|
||||||
<div class="sender-create-wizard__document-name">@_documentFileName</div>
|
<div class="sender-create-wizard__document-name">@_documentFileName</div>
|
||||||
<div class="sender-create-wizard__document-size">@_documentSizeText</div>
|
<div class="sender-create-wizard__document-size">@_documentSizeText</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sender-create-wizard__document-preview-wrap">
|
||||||
|
<SenderPdfImageEditor IsPlacementMode="false"
|
||||||
|
PageCount="@_documentPreviewPageCount"
|
||||||
|
Pages="@_documentPreviewPages"
|
||||||
|
PdfDataUrl="@DocumentPreviewDataUrl"
|
||||||
|
PdfFileName="@_documentFileName"
|
||||||
|
Fields="@_documentPreviewFieldOverlays"
|
||||||
|
EditorClick="OnDocumentPreviewEditorClickAsync"
|
||||||
|
RemoveField="OnDocumentPreviewRemoveFieldAsync"
|
||||||
|
MoveField="OnDocumentPreviewMoveFieldAsync" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -331,10 +345,14 @@
|
|||||||
private byte[]? _documentBytes;
|
private byte[]? _documentBytes;
|
||||||
private string? _documentFileName;
|
private string? _documentFileName;
|
||||||
private string _documentSizeText = string.Empty;
|
private string _documentSizeText = string.Empty;
|
||||||
|
private int _documentPreviewPageCount;
|
||||||
|
private List<PdfPageImageDto> _documentPreviewPages = [];
|
||||||
private bool _isDocumentUploading;
|
private bool _isDocumentUploading;
|
||||||
private bool _isStepActionRunning;
|
private bool _isStepActionRunning;
|
||||||
private bool _isFinishing;
|
private bool _isFinishing;
|
||||||
|
|
||||||
|
private readonly IReadOnlyList<SenderPdfFieldOverlay> _documentPreviewFieldOverlays = [];
|
||||||
|
|
||||||
private string? _validationMessage;
|
private string? _validationMessage;
|
||||||
|
|
||||||
private bool IsActionLocked => _isDocumentUploading || _isStepActionRunning || _isFinishing;
|
private bool IsActionLocked => _isDocumentUploading || _isStepActionRunning || _isFinishing;
|
||||||
@@ -346,6 +364,10 @@
|
|||||||
private IReadOnlyList<RecentReceiverSuggestionDto> FilteredRecentSuggestions =>
|
private IReadOnlyList<RecentReceiverSuggestionDto> FilteredRecentSuggestions =>
|
||||||
BuildFilteredRecentSuggestions();
|
BuildFilteredRecentSuggestions();
|
||||||
|
|
||||||
|
private string? DocumentPreviewDataUrl => _documentBytes is { Length: > 0 }
|
||||||
|
? $"data:application/pdf;base64,{Convert.ToBase64String(_documentBytes)}"
|
||||||
|
: null;
|
||||||
|
|
||||||
private string CurrentStepTitle => _currentStep switch
|
private string CurrentStepTitle => _currentStep switch
|
||||||
{
|
{
|
||||||
0 => L("SenderWizard_Step_Basics", "Grunddaten"),
|
0 => L("SenderWizard_Step_Basics", "Grunddaten"),
|
||||||
@@ -525,6 +547,22 @@
|
|||||||
_documentBytes = ms.ToArray();
|
_documentBytes = ms.ToArray();
|
||||||
_documentFileName = file.Name;
|
_documentFileName = file.Name;
|
||||||
_documentSizeText = FormatSize(_documentBytes.Length);
|
_documentSizeText = FormatSize(_documentBytes.Length);
|
||||||
|
|
||||||
|
_documentPreviewPages = [];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_documentPreviewPages = (await PdfRenderService.RenderPagesAsync(_documentBytes))
|
||||||
|
.OrderBy(x => x.PageNumber)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_documentPreviewPages = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_documentPreviewPageCount = _documentPreviewPages.Count > 0
|
||||||
|
? _documentPreviewPages.Count
|
||||||
|
: EstimatePdfPageCount(_documentBytes) ?? 1;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -771,6 +809,51 @@
|
|||||||
return $"{bytes} B";
|
return $"{bytes} B";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int? EstimatePdfPageCount(byte[]? documentBytes)
|
||||||
|
{
|
||||||
|
if (documentBytes is null || documentBytes.Length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var text = System.Text.Encoding.ASCII.GetString(documentBytes);
|
||||||
|
const string marker = "/Type /Page";
|
||||||
|
var count = 0;
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
index = text.IndexOf(marker, index, StringComparison.Ordinal);
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
index += marker.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count > 0 ? count : null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task OnDocumentPreviewEditorClickAsync(MouseEventArgs _)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task OnDocumentPreviewRemoveFieldAsync(int _)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task OnDocumentPreviewMoveFieldAsync(SenderPdfFieldMoveRequest _)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
private void ResetWizardState()
|
private void ResetWizardState()
|
||||||
{
|
{
|
||||||
_currentStep = 0;
|
_currentStep = 0;
|
||||||
@@ -792,6 +875,8 @@
|
|||||||
_documentBytes = null;
|
_documentBytes = null;
|
||||||
_documentFileName = null;
|
_documentFileName = null;
|
||||||
_documentSizeText = string.Empty;
|
_documentSizeText = string.Empty;
|
||||||
|
_documentPreviewPageCount = 0;
|
||||||
|
_documentPreviewPages = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private int? GetDefaultEnvelopeTypeId()
|
private int? GetDefaultEnvelopeTypeId()
|
||||||
|
|||||||
@@ -1533,6 +1533,28 @@
|
|||||||
padding: 0.55rem 0.7rem;
|
padding: 0.55rem 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sender-create-wizard__document-preview-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sender-create-wizard__document-preview-wrap {
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.38);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(248, 250, 252, 0.96);
|
||||||
|
height: 320px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sender-create-wizard__document-preview-wrap .sender-image-editor-shell {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sender-create-wizard__document-preview-wrap .sender-image-editor {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.sender-create-wizard__document-name {
|
.sender-create-wizard__document-name {
|
||||||
color: #064e3b;
|
color: #064e3b;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user