diff --git a/BootstrapBlazor.Extensions.sln b/BootstrapBlazor.Extensions.sln
index d5ae678a..f062a7e4 100644
--- a/BootstrapBlazor.Extensions.sln
+++ b/BootstrapBlazor.Extensions.sln
@@ -192,6 +192,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.PdfViewer",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Vditor", "src\components\BootstrapBlazor.Vditor\BootstrapBlazor.Vditor.csproj", "{D417E1B9-D146-4983-81D0-79F3193B322B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.TouchSocket", "src\extensions\BootstrapBlazor.TouchSocket\BootstrapBlazor.TouchSocket.csproj", "{FD23CEA1-78EB-85D7-8EDF-047657355B52}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OfficeViewer", "src\components\BootstrapBlazor.OfficeViewer\BootstrapBlazor.OfficeViewer.csproj", "{2436940C-5920-D801-8A81-721F4C20A355}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -522,6 +526,14 @@ Global
{D417E1B9-D146-4983-81D0-79F3193B322B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D417E1B9-D146-4983-81D0-79F3193B322B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D417E1B9-D146-4983-81D0-79F3193B322B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FD23CEA1-78EB-85D7-8EDF-047657355B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FD23CEA1-78EB-85D7-8EDF-047657355B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FD23CEA1-78EB-85D7-8EDF-047657355B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FD23CEA1-78EB-85D7-8EDF-047657355B52}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2436940C-5920-D801-8A81-721F4C20A355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2436940C-5920-D801-8A81-721F4C20A355}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -612,6 +624,8 @@ Global
{08458CA3-BF81-48E8-870D-9389DC037808} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{4757B038-70E4-40B0-9B73-700EE5632B07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
{D417E1B9-D146-4983-81D0-79F3193B322B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
+ {FD23CEA1-78EB-85D7-8EDF-047657355B52} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
+ {2436940C-5920-D801-8A81-721F4C20A355} = {FF1089BE-C704-4374-B629-C57C08E1798F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
diff --git a/src/components/BootstrapBlazor.OfficeViewer/BootstrapBlazor.OfficeViewer.csproj b/src/components/BootstrapBlazor.OfficeViewer/BootstrapBlazor.OfficeViewer.csproj
new file mode 100644
index 00000000..0f96d940
--- /dev/null
+++ b/src/components/BootstrapBlazor.OfficeViewer/BootstrapBlazor.OfficeViewer.csproj
@@ -0,0 +1,21 @@
+
+
+
+ 9.0.0
+
+
+
+ Bootstrap Blazor WebAssembly wasm UI Components Office Viewer
+ Bootstrap UI components extensions of Microsoft Office Documentation Viewer
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor b/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor
new file mode 100644
index 00000000..c713d001
--- /dev/null
+++ b/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor
@@ -0,0 +1,5 @@
+@namespace BootstrapBlazor.Components
+@inherits BootstrapModuleComponentBase
+@attribute [JSModuleAutoLoader("./_content/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.js", JSObjectReference = true, AutoInvokeDispose = false)]
+
+
diff --git a/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.cs b/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.cs
new file mode 100644
index 00000000..66b60a85
--- /dev/null
+++ b/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.cs
@@ -0,0 +1,109 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+using Microsoft.AspNetCore.Components;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Represents a viewer component for displaying Office documents in a web application.
+///
+/// The component allows users to display Office files such as Word,
+/// Excel, or PowerPoint documents. It provides configurable options for the document URL, viewer height, and a callback
+/// for when the document is loaded.
+public partial class OfficeViewer
+{
+ ///
+ /// Gets or sets the url for the Office file to be displayed.
+ ///
+ [Parameter]
+ public string? Url { get; set; }
+
+ ///
+ /// Gets or sets the viewer height. Default is null.
+ ///
+ [Parameter]
+ public string? Height { get; set; }
+
+ ///
+ /// Gets or sets the document loaded event callback.
+ ///
+ [Parameter]
+ public Func? OnLoaded { get; set; }
+
+ [Inject, NotNull]
+ private NavigationManager? NavigationManager { get; set; }
+
+ private string? ClassString => CssBuilder.Default("bb-office-viewer-container")
+ .AddClassFromAttributes(AdditionalAttributes)
+ .Build();
+
+ private string? StyleString => CssBuilder.Default()
+ .AddClass($"--bb-office-viewer-height: {Height};", !string.IsNullOrEmpty(Height))
+ .Build();
+
+ private string? _url;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await base.OnAfterRenderAsync(firstRender);
+
+ if (firstRender)
+ {
+ _url = Url;
+ return;
+ }
+
+ var rerender = false;
+ if (_url != Url)
+ {
+ _url = Url;
+ rerender = true;
+ }
+
+ if (rerender)
+ {
+ await InvokeVoidAsync("load", Id, GetAbsoluteUri(_url));
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new
+ {
+ LoadedCallaback = nameof(TriggerOnLoaded),
+ Url = GetAbsoluteUri(Url)
+ });
+
+ private string GetAbsoluteUri(string? url)
+ {
+ url ??= string.Empty;
+ if (string.IsNullOrEmpty(url))
+ {
+ return url;
+ }
+ var uri = NavigationManager.ToAbsoluteUri(url);
+ return uri.AbsoluteUri;
+ }
+
+ ///
+ /// Trigger OnLoaded callback when the PDF document is loaded.
+ ///
+ ///
+ [JSInvokable]
+ public async Task TriggerOnLoaded()
+ {
+ if (OnLoaded != null)
+ {
+ await OnLoaded();
+ }
+ }
+}
diff --git a/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.js b/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.js
new file mode 100644
index 00000000..0337f9d4
--- /dev/null
+++ b/src/components/BootstrapBlazor.OfficeViewer/OfficeViewer.razor.js
@@ -0,0 +1,37 @@
+import { addLink } from "../BootstrapBlazor/modules/utility.js"
+import Data from "../BootstrapBlazor/modules/data.js"
+
+export async function init(id, invoke, options) {
+ await addLink("./_content/BootstrapBlazor.OfficeViewer/office-viewer.css");
+
+ const el = document.getElementById(id);
+ const officeViewer = { el, invoke, options };
+ Data.set(id, officeViewer);
+
+ await load(id, options.url);
+}
+
+export async function load(id, url) {
+ const officeViewer = Data.get(id);
+ const { el, invoke, options } = officeViewer;
+
+ el.innerHTML = '';
+
+ if (url) {
+ const { frame } = officeViewer;
+ const viewer = frame || createFrame(el);
+ if (options.loadedCallaback) {
+ viewer.onload = () => {
+ invoke.invokeMethodAsync(options.loadedCallaback);
+ };
+ }
+ viewer.src = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(url)}`;
+ }
+}
+
+const createFrame = el => {
+ const frame = document.createElement('iframe');
+ frame.classList.add('bb-office-viewer');
+ el.appendChild(frame);
+ return frame;
+}
diff --git a/src/components/BootstrapBlazor.OfficeViewer/_Imports.razor b/src/components/BootstrapBlazor.OfficeViewer/_Imports.razor
new file mode 100644
index 00000000..d46585f8
--- /dev/null
+++ b/src/components/BootstrapBlazor.OfficeViewer/_Imports.razor
@@ -0,0 +1,2 @@
+@using BootstrapBlazor.Components;
+@using Microsoft.AspNetCore.Components.Web
diff --git a/src/components/BootstrapBlazor.OfficeViewer/wwwroot/office-viewer.css b/src/components/BootstrapBlazor.OfficeViewer/wwwroot/office-viewer.css
new file mode 100644
index 00000000..0cdbffd0
--- /dev/null
+++ b/src/components/BootstrapBlazor.OfficeViewer/wwwroot/office-viewer.css
@@ -0,0 +1,9 @@
+.bb-office-viewer-container {
+ width: 100%;
+ height: var(--bb-office-viewer-height, 500px);
+}
+
+.bb-office-viewer {
+ width: 100%;
+ height: 100%;
+}