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
3 changes: 3 additions & 0 deletions BLite.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<File Path="README.md" />
<File Path="RFC.md" />
</Folder>
<Folder Name="/samples/">
<Project Path="samples/BLite.BlazorWasm/BLite.BlazorWasm.csproj" />
</Folder>
<Folder Name="/src/">
<Project Path="src/BLite.Bson/BLite.Bson.csproj" />
<Project Path="src/BLite.Caching/BLite.Caching.csproj" />
Expand Down
32 changes: 25 additions & 7 deletions WASM_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,31 @@ The following issue should be tracked separately.

---

### Issue 5 — WASM Demo & Documentation
### Issue 5 — WASM Demo & Documentation

**Scope:** Provide an end-to-end example of BLite running in a Blazor WASM app:
- `samples/BLite.BlazorWasm/` — minimal Blazor WASM app storing and querying BSON documents
entirely in the browser using the OPFS or IndexedDB backend.
- Update `README.md` with a WASM section.
- Update `BENCHMARKS.md` with WASM throughput numbers (OPFS vs IndexedDB vs in-memory).
**Scope:** End-to-end example of BLite running in a Blazor WASM app.

**Delivered:** `samples/BLite.BlazorWasm/` — a minimal Blazor WebAssembly application that
demonstrates a **persistent counter** stored entirely in the browser using BLite:

- `Services/CounterStore.cs` — singleton service that calls `BLiteWasm.CreateAsync()`,
auto-selects the best storage backend (OPFS → IndexedDB → in-memory), inserts a counter
document on first use, and updates it on every increment / decrement / reset.
- `Pages/Counter.razor` — Blazor component wired to `CounterStore`. Shows the current
count, +/−/Reset buttons, the active storage backend, and the last-saved timestamp.
Reload the page to verify that the count survives browser restarts.
- `Pages/Home.razor` — landing page with a brief description and a quick-start code snippet.
- `wwwroot/blite-indexeddb.mjs` and `wwwroot/blite-opfs.mjs` — the BLite JS interop modules
(copied from `BLite.Wasm/wwwroot`) required by `[JSImport]` at runtime.

**Running the sample:**

```bash
# From the repository root:
dotnet run --project samples/BLite.BlazorWasm/BLite.BlazorWasm.csproj
```

Then open `https://localhost:5001/counter` in any modern browser.

---

Expand All @@ -181,7 +199,7 @@ The following issue should be tracked separately.
[Done] Issue 1 OPFS page storage backend
[Done] Issue 2 IndexedDB page storage backend (compatibility fallback)
[Done] Issue 4 BLite.Wasm NuGet package + factory API
[ ] Issue 5 Blazor WASM sample + docs
[Done] Issue 5 Blazor WASM sample — persistent counter demo
```

---
Expand Down
6 changes: 6 additions & 0 deletions samples/BLite.BlazorWasm/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Router AppAssembly="@typeof(App).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
29 changes: 29 additions & 0 deletions samples/BLite.BlazorWasm/BLite.BlazorWasm.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.5" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\BLite.Wasm\BLite.Wasm.csproj" />
</ItemGroup>

<!--
Copy BLite.Wasm JS interop files to wwwroot so the browser can load them.
IndexedDbInterop and OpfsInterop resolve modules as "./blite-*.mjs" relative
to the page root, so the files must live at the wwwroot root.
-->
<ItemGroup>
<None Update="wwwroot\blite-indexeddb.mjs" CopyToOutputDirectory="PreserveNewest" />
<None Update="wwwroot\blite-opfs.mjs" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>
5 changes: 5 additions & 0 deletions samples/BLite.BlazorWasm/BrowserPlatform.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// BLite.BlazorWasm is a Blazor WebAssembly application.
// It only runs in browser contexts, so all code in this assembly is browser-only.
using System.Runtime.Versioning;

[assembly: SupportedOSPlatform("browser")]
17 changes: 17 additions & 0 deletions samples/BLite.BlazorWasm/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@inherits LayoutComponentBase

<div class="page">
<div class="sidebar">
<NavMenu />
</div>

<main>
<div class="top-row px-4">
<a href="https://github.com/EntglDb/BLite" target="_blank" rel="noopener">BLite on GitHub</a>
</div>

<article class="content px-4">
@Body
</article>
</main>
</div>
71 changes: 71 additions & 0 deletions samples/BLite.BlazorWasm/Layout/MainLayout.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}

main {
flex: 1;
}

.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}

.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}

.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}

.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}

.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}

@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}

.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}

@media (min-width: 641px) {
.page {
flex-direction: row;
}

.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}

.top-row {
position: sticky;
top: 0;
z-index: 1;
}

.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
34 changes: 34 additions & 0 deletions samples/BLite.BlazorWasm/Layout/NavMenu.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">BLite WASM Demo</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>

<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
<nav class="nav flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
</nav>
</div>

@code {
private bool collapseNavMenu = true;

private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;

private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
77 changes: 77 additions & 0 deletions samples/BLite.BlazorWasm/Layout/NavMenu.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
}

.top-row {
min-height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}

.navbar-brand {
font-size: 1.1rem;
}

.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}

.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C%2Fsvg%3E");
}

.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C%2Fsvg%3E");
}

.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}

.nav-item:first-of-type {
padding-top: 1rem;
}

.nav-item:last-of-type {
padding-bottom: 1rem;
}

.nav-item ::deep a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}

.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}

.nav-item ::deep a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}

@media (min-width: 641px) {
.navbar-toggler {
display: none;
}

.collapse {
display: block;
}

.nav-scrollable {
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}
Loading