Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/components/BootstrapBlazor.PdfReader/PdfReader.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@inherits BootstrapModuleComponentBase

<div @attributes="@AdditionalAttributes" id="@Id" class="@ClassString" style="@StyleString">
@if (Options.ShowToolbar)
@if (ShowToolbar)
{
<div class="bb-view-toolbar init">
<div class="bb-view-title">
Expand All @@ -23,12 +23,15 @@
<div class="bb-view-icon bb-view-fit-rotate" @onclick="RotateRight"><i class="fa-solid fa-rotate-right"></i></div>
</div>
<div class="bb-view-controls">
<div class="bb-view-icon bb-view-download"><i class="fa-solid fa-arrow-right-to-bracket fa-rotate-90"></i></div>
@if (ShowDownload)
{
<div class="bb-view-icon bb-view-download" @onclick="OnDownload"><i class="fa-solid fa-arrow-right-to-bracket fa-rotate-90"></i></div>
}
<div class="bb-view-icon bb-view-print"><i class="fa-solid fa-print"></i></div>
<Dropdown TValue="string" Color="Color.None" SkipValidate="true" ShowLabel="false"
IsPopover="false" MenuAlignment="Alignment.Right" Icon="@MoreButtonIcon">
<ItemsTemplate>
@if (Options.ShowTwoPagesOnViewButton)
@if (ShowTwoPagesOneViewButton)
{
<div class="dropdown-item dropdown-item-pages" @onclick="OnToggleTwoPagesOneView"><i class="@_twoPagesOneViewIcon"></i><span>Two pages on view</span></div>
<Divider></Divider>
Expand All @@ -40,7 +43,7 @@
</div>
}
<div class="bb-view-main">
@if (Options.EnableThumbnails)
@if (EnableThumbnails)
{
<div class="bb-view-thumbnails"></div>
}
Expand Down
197 changes: 148 additions & 49 deletions src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,115 @@ namespace BootstrapBlazor.Components;
public partial class PdfReader
{
/// <summary>
/// 获得/设置 <see cref="PdfReaderOptions"/> 配置项实例
/// 获得/设置 是否显示工具栏 默认 true 显示
/// </summary>
[Parameter]
[NotNull]
public PdfReaderOptions? Options { get; set; }
public bool ShowToolbar { get; set; } = true;

/// <summary>
/// 获得/设置 是否显示下载按钮 默认 true 显示
/// </summary>
[Parameter]
public bool ShowDownload { get; set; } = true;

/// <summary>
/// 获得/设置 是否显示缩略图 默认 true 显示
/// </summary>
[Parameter]
public bool EnableThumbnails { get; set; } = true;

/// <summary>
/// 获得/设置 PDF 文档路径
/// </summary>
[Parameter]
public string? Url { get; set; }

/// <summary>
/// 获得/设置 PDF 组件高度 默认 600px
/// </summary>
[Parameter]
public string? ViewHeight { get; set; }

/// <summary>
/// 获得/设置 当前页码
/// </summary>
[Parameter]
public uint CurrentPage { get; set; }

/// <summary>
/// 获得/设置 当前缩放倍率 默认 null 使用 100%
/// </summary>
[Parameter]
public string? CurrentScale { get; set; }

/// <summary>
/// 获得/设置 是否适配当前页面宽度 默认 false
/// </summary>
[Parameter]
public bool IsFitToPage { get; set; }

/// <summary>
/// 获得/设置 是否显示双页单视图按钮 默认 true 显示
/// </summary>
[Parameter]
public bool ShowTwoPagesOneViewButton { get; set; } = true;

/// <summary>
/// 获得/设置 是否启用双页单视图模式 默认 false
/// </summary>
[Parameter]
public bool EnableTwoPagesOneView { get; set; }

/// <summary>
/// 页面初始化回调方法
/// </summary>
[Parameter]
public Func<int, Task>? OnPagesInitAsync { get; set; }

/// <summary>
/// 页面加载完毕回调方法
/// </summary>
[Parameter]
public Func<int, Task>? OnPagesLoadedAsync { get; set; }

/// <summary>
/// 页面初始化回调方法
Comment thread
ArgoZhang marked this conversation as resolved.
/// </summary>
[Parameter]
public Func<uint, Task>? OnPageChangedAsync { get; set; }

/// <summary>
/// 设置双页单视图模式回调方法
/// </summary>
[Parameter]
public Func<bool, Task>? OnTwoPagesOneViewAsync { get; set; }

/// <summary>
/// 获得/设置 更多按钮图标 默认为 null 使用内置图标
/// </summary>
[Parameter]
public string? MoreButtonIcon { get; set; }

/// <summary>
/// 点击下载按钮回调方法 默认 null 使用组件内置下载功能
/// </summary>
[Parameter]
public Func<Task>? OnDownloadAsync { get; set; }
Comment on lines +107 to +110
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Default download behavior is effectively a no-op and DownloadService is unused.

The summary comment says OnDownloadAsync == null should trigger the built-in download and you inject a DownloadService, but OnDownload() currently only invokes the callback and otherwise does nothing. As a result, the default download button does nothing unless a consumer wires a handler. Either wire the default behavior via DownloadService when OnDownloadAsync is null, or change the parameter semantics/docs to require a handler. If you keep a built-in behavior, also guard against null/empty Url before invoking the download.


[Inject, NotNull]
private DownloadService? DownloadService { get; set; }
Comment thread
ArgoZhang marked this conversation as resolved.

private string? ClassString => CssBuilder.Default("bb-pdf-reader")
.AddClassFromAttributes(AdditionalAttributes)
.Build();

private string? StyleString => CssBuilder.Default()
.AddClass($"--bb-pdf-view-height: {Options.ViewHeight};", !string.IsNullOrEmpty(Options.ViewHeight))
.AddClass($"--bb-pdf-view-height: {ViewHeight};", !string.IsNullOrEmpty(ViewHeight))
.AddClassFromAttributes(AdditionalAttributes)
.Build();

private string? ViewBodyString => CssBuilder.Default("bb-view-body")
.AddClass("fit-page", Options.IsFitToPage)
.AddClass("fit-page", IsFitToPage)
.Build();

private string? _docTitle;
Expand All @@ -45,35 +131,36 @@ public partial class PdfReader
private string? _url;
private string? _currentScale;
private bool _enableTwoPagesOneView;
private bool _showTwoPagesOneViewButton;
private string? _twoPagesOneViewIcon;

private readonly HashSet<string> AllowedScaleValues = ["page-actual", "page-width", "page-height", "page-fit", "auto"];

private string CurrentPageString
{
get => Options.CurrentPage.ToString(CultureInfo.InvariantCulture);
get => CurrentPage.ToString(CultureInfo.InvariantCulture);
set => SetCurrentPage(value);
}

private void SetCurrentPage(string value)
{
if (uint.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
{
Options.CurrentPage = num;
CurrentPage = num;
}
}

private string CurrentScaleString
{
get => $"{Options.CurrentScale ?? "100"}%";
get => $"{CurrentScale ?? "100"}%";
set => SetCurrentScale(value);
}

private void SetCurrentScale(string value)
{
if (string.IsNullOrEmpty(value))
{
Options.CurrentScale = "100";
CurrentScale = "100";
}
else if (float.TryParse(value.TrimEnd("%"), out var v))
{
Expand All @@ -84,14 +171,14 @@ private void SetCurrentScale(string value)
_ => v
};

Options.CurrentScale = v.ToString(CultureInfo.InvariantCulture);
CurrentScale = v.ToString(CultureInfo.InvariantCulture);
}
}

private void OnToggleTwoPagesOneView()
{
_enableTwoPagesOneView = !_enableTwoPagesOneView;
Options.EnableTwoPagesOnView = _enableTwoPagesOneView;
EnableTwoPagesOneView = _enableTwoPagesOneView;

_twoPagesOneViewIcon = _enableTwoPagesOneView ? "fa-solid fa-fw fa-check" : "fa-solid fa-fw";
}
Expand All @@ -103,16 +190,14 @@ protected override void OnParametersSet()
{
base.OnParametersSet();

Options ??= new PdfReaderOptions();
MoreButtonIcon ??= "fa-solid fa-ellipsis-vertical";
_twoPagesOneViewIcon ??= "fa-solid fa-fw";

if (Options.CurrentPage == 0)
if (CurrentPage == 0)
{
Options.CurrentPage = 1;
CurrentPage = 1;
}
_docTitle = Path.GetFileName(Options.Url);

MoreButtonIcon ??= "fa-solid fa-ellipsis-vertical";
_twoPagesOneViewIcon ??= "fa-solid fa-fw";
_docTitle = Path.GetFileName(Url);
}

/// <summary>
Expand All @@ -126,37 +211,43 @@ protected override async Task OnAfterRenderAsync(bool firstRender)

if (firstRender)
{
_isFitToPage = Options.IsFitToPage;
_currentPage = Options.CurrentPage;
_url = Options.Url;
_currentScale = Options.CurrentScale;
_enableTwoPagesOneView = Options.EnableTwoPagesOnView;
_isFitToPage = IsFitToPage;
_currentPage = CurrentPage;
_url = Url;
_currentScale = CurrentScale;
_enableTwoPagesOneView = EnableTwoPagesOneView;
_showTwoPagesOneViewButton = ShowTwoPagesOneViewButton;
}

if (_url != Options.Url)
if (_url != Url)
{
_url = Options.Url;
_url = Url;
await InvokeInitAsync();
}

if (_isFitToPage != Options.IsFitToPage)
if (_isFitToPage != IsFitToPage)
{
_isFitToPage = Options.IsFitToPage;
_isFitToPage = IsFitToPage;
await TriggerFit(_isFitToPage ? "fitToPage" : "fitToWidth");
}
if (_currentPage != Options.CurrentPage)
if (_currentPage != CurrentPage)
{
_currentPage = Options.CurrentPage;
_currentPage = CurrentPage;
await NavigateToPageAsync(_currentPage);
}
if (_currentScale != Options.CurrentScale)
if (_currentScale != CurrentScale)
{
_currentScale = Options.CurrentScale;
_currentScale = CurrentScale;
await InvokeVoidAsync("scale", Id, _currentScale);
}
if (_enableTwoPagesOneView != Options.EnableTwoPagesOnView)
if (_enableTwoPagesOneView != EnableTwoPagesOneView)
{
_currentScale = Options.CurrentScale;
_enableTwoPagesOneView = EnableTwoPagesOneView;
await InvokeVoidAsync("setPages", Id, _enableTwoPagesOneView);
}
if (_showTwoPagesOneViewButton != ShowTwoPagesOneViewButton)
{
_showTwoPagesOneViewButton = ShowTwoPagesOneViewButton;
await InvokeVoidAsync("setPages", Id, _enableTwoPagesOneView);
}
}
Expand All @@ -167,13 +258,13 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new
{
Options.Url,
Options.IsFitToPage,
Options.EnableThumbnails,
TriggerPagesInit = Options.OnPagesInitAsync != null,
TriggerPagesLoaded = Options.OnPagesLoadedAsync != null,
TriggerPageChanged = Options.OnPageChangedAsync != null,
TriggerTowPagesOnViewChanged = Options.OnTwoPagesOneViewAsync != null
Url,
IsFitToPage,
EnableThumbnails,
TriggerPagesInit = OnPagesInitAsync != null,
TriggerPagesLoaded = OnPagesLoadedAsync != null,
TriggerPageChanged = OnPageChangedAsync != null,
TriggerTowPagesOnViewChanged = OnTwoPagesOneViewAsync != null
});

/// <summary>
Expand All @@ -186,12 +277,12 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
/// <summary>
/// 适应页面宽度
/// </summary>
public void FitToPage() => Options.IsFitToPage = true;
public void FitToPage() => IsFitToPage = true;

/// <summary>
/// 适应文档宽度
/// </summary>
public void FitToWidth() => Options.IsFitToPage = false;
public void FitToWidth() => IsFitToPage = false;

/// <summary>
/// 旋转页面方法
Expand All @@ -211,6 +302,14 @@ public async Task RotateRight()
await InvokeVoidAsync("rotate", Id, 90);
}

private async Task OnDownload()
{
if (OnDownloadAsync != null)
{
await OnDownloadAsync();
}
}

private Task TriggerFit(string methodName) => InvokeVoidAsync(methodName, Id);

/// <summary>
Expand All @@ -220,9 +319,9 @@ public async Task RotateRight()
[JSInvokable]
public async Task PagesInit(int pagesCount)
{
if (Options.OnPagesInitAsync != null)
if (OnPagesInitAsync != null)
{
await Options.OnPagesInitAsync(pagesCount);
await OnPagesInitAsync(pagesCount);
}
}

Expand All @@ -233,9 +332,9 @@ public async Task PagesInit(int pagesCount)
[JSInvokable]
public async Task PagesLoaded(int pagesCount)
{
if (Options.OnPagesLoadedAsync != null)
if (OnPagesLoadedAsync != null)
{
await Options.OnPagesLoadedAsync(pagesCount);
await OnPagesLoadedAsync(pagesCount);
}
}

Expand All @@ -247,11 +346,11 @@ public async Task PagesLoaded(int pagesCount)
public async Task PageChanged(uint pageIndex)
{
_currentPage = pageIndex;
Options.CurrentPage = pageIndex;
CurrentPage = pageIndex;

if (Options.OnPageChangedAsync != null)
if (OnPageChangedAsync != null)
{
await Options.OnPageChangedAsync(pageIndex);
await OnPageChangedAsync(pageIndex);
}
}
}
Loading
Loading