Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
12 changes: 12 additions & 0 deletions src/Bmotion/Bit.Bmotion.Demos/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
26 changes: 26 additions & 0 deletions src/Bmotion/Bit.Bmotion.Demos/Bit.Bmotion.Demos.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<!--
Disable static web asset fingerprinting for this dev/sample app so the running
dev server and the browser's cached boot manifest can't drift and trigger SRI
integrity 404s on fingerprinted _framework assets.
-->
<WasmFingerprintAssets>false</WasmFingerprintAssets>
<StaticWebAssetFingerprintingEnabled>false</StaticWebAssetFingerprintingEnabled>
</PropertyGroup>

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

<ItemGroup>
<ProjectReference Include="..\Bit.Bmotion\Bit.Bmotion.csproj" />
</ItemGroup>

</Project>
19 changes: 19 additions & 0 deletions src/Bmotion/Bit.Bmotion.Demos/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@inherits LayoutComponentBase

<div class="bm-nav">
<span class="logo">Bmotion</span>
<NavLink href="" Match="NavLinkMatch.All">Home</NavLink>
<NavLink href="basic">Basics</NavLink>
<NavLink href="springs">Springs</NavLink>
<NavLink href="gestures">Gestures</NavLink>
<NavLink href="variants">Variants</NavLink>
<NavLink href="keyframes">Keyframes</NavLink>
<NavLink href="presence">AnimatePresence</NavLink>
<NavLink href="drag">Drag</NavLink>
<NavLink href="scroll">Scroll</NavLink>
<NavLink href="layout">Layout</NavLink>
</div>

<div class="bm-page">
@Body
</div>
205 changes: 205 additions & 0 deletions src/Bmotion/Bit.Bmotion.Demos/Pages/AnimatePresencePage.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
@page "/presence"

<div class="demo-section">
<h2>AnimatePresence</h2>
<p>
Wrap conditional content in <strong>AnimatePresence</strong>. When
<c>IsPresent</c> becomes <c>false</c> the children play their <c>Exit</c>
animation before being removed from the DOM.
</p>

<div class="demo-row">
<div class="demo-card" style="min-height:200px; position:relative; overflow:hidden;">
<BmotionAnimatePresence IsPresent="@_visible">
<Bmotion Tag="div"
Class="box"
Initial="@(new BmotionAnimationProps { Opacity = 0, Y = 40, Scale = 0.8 })"
Animate="@(new BmotionAnimationProps { Opacity = 1, Y = 0, Scale = 1 })"
Exit="@(new BmotionAnimationProps { Opacity = 0, Y = -40, Scale = 0.8 })"
Transition="@BmotionTransitionConfig.Spring(stiffness:220, damping:18)" />
</BmotionAnimatePresence>
</div>
</div>

<button class="btn-bm" style="margin-top:1rem" @onclick="@(() => _visible = !_visible)">
@(_visible ? "Remove" : "Add")
</button>

<CodeSnippet Code="@_presenceCode" />
</div>

<div class="demo-section">
<h2>List Items</h2>
<p>Add and remove items from a list with staggered enter/exit animations.</p>

<div class="demo-row">
<div class="demo-card" style="flex-direction:column;gap:.5rem;align-items:stretch;width:100%;min-width:280px;">
@foreach (var item in _items)
{
<BmotionAnimatePresence IsPresent="@item.Present" @key="item.Id">
<Bmotion Tag="div"
Style="display:flex;justify-content:space-between;align-items:center;background:rgba(108,71,255,.12);border:1px solid rgba(108,71,255,.25);border-radius:8px;padding:.6rem 1rem;"
Initial="@(new BmotionAnimationProps { Opacity = 0, X = -30 })"
Animate="@(new BmotionAnimationProps { Opacity = 1, X = 0 })"
Exit="@(new BmotionAnimationProps { Opacity = 0, X = 30 })"
Transition="@BmotionTransitionConfig.Spring(stiffness:300, damping:25)">
<span>@item.Label</span>
<button style="background:none;border:none;color:#ff4785;cursor:pointer;font-size:1.1rem"
@onclick="@(() => RemoveItem(item.Id))">✕</button>
</Bmotion>
</BmotionAnimatePresence>
}
Comment thread
msynk marked this conversation as resolved.
</div>
</div>

<button class="btn-bm" style="margin-top:1rem" @onclick="AddItem">Add item</button>

<CodeSnippet Code="@_listCode" />
</div>

@code {
private bool _visible = true;
private int _nextId = 4;

private sealed class Item(int id, string label)
{
public int Id { get; } = id;
public string Label { get; } = label;
// Drives the per-item AnimatePresence: flipped to false to play the exit animation
// *before* the item is removed from the collection.
public bool Present { get; set; } = true;
}

private List<Item> _items = [new(1,"Item One"), new(2,"Item Two"), new(3,"Item Three")];

void AddItem() => _items.Add(new(_nextId++, $"Item {_nextId - 1}"));

async Task RemoveItem(int id)
{
var item = _items.Find(i => i.Id == id);
if (item is null || !item.Present) return;
item.Present = false; // trigger the exit animation
await Task.Delay(400); // let it finish before dropping the item from the list
_items.Remove(item);
}

private const string _presenceCode = """
@using Bit.Bmotion

<style>
:root { --bm-primary: #6c47ff; --bm-secondary: #ff4785; --bm-card: #1e1e2e; }
.demo-section { margin-bottom: 3rem; }
.demo-section h2 { font-size: 1.4rem; font-weight: 700; margin-bottom: .5rem; }
.demo-row { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: center; }
.demo-card {
background: var(--bm-card); border: 1px solid rgba(255,255,255,.07);
border-radius: 12px; padding: 2rem; min-height: 160px; flex: 1; min-width: 220px;
display: flex; align-items: center; justify-content: center;
}
.box {
width: 80px; height: 80px; border-radius: 10px;
background: linear-gradient(135deg, var(--bm-primary), var(--bm-secondary));
}
.btn-bm {
background: var(--bm-primary); color: #fff; border: none; border-radius: 8px;
padding: .6rem 1.4rem; font-weight: 600; cursor: pointer;
}
</style>

<div class="demo-section">
<h2>AnimatePresence</h2>

<div class="demo-row">
<div class="demo-card" style="min-height:200px; position:relative; overflow:hidden;">
<BmotionAnimatePresence IsPresent="@_visible">
<Bmotion Tag="div"
Class="box"
Initial="@(new BmotionAnimationProps { Opacity = 0, Y = 40, Scale = 0.8 })"
Animate="@(new BmotionAnimationProps { Opacity = 1, Y = 0, Scale = 1 })"
Exit="@(new BmotionAnimationProps { Opacity = 0, Y = -40, Scale = 0.8 })"
Transition="@BmotionTransitionConfig.Spring(stiffness:220, damping:18)" />
</BmotionAnimatePresence>
</div>
</div>

<button class="btn-bm" style="margin-top:1rem" @onclick="@(() => _visible = !_visible)">
@(_visible ? "Remove" : "Add")
</button>
</div>

@code {
private bool _visible = true;
}
""";

private const string _listCode = """
@using Bit.Bmotion

<style>
.demo-section { margin-bottom: 3rem; }
.demo-section h2 { font-size: 1.4rem; font-weight: 700; margin-bottom: .5rem; }
.demo-section p { color: #888; margin-bottom: 1.5rem; line-height: 1.6; }
.demo-row { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: center; }
.demo-card {
background: #1e1e2e; border: 1px solid rgba(255,255,255,.07);
border-radius: 12px; padding: 2rem; min-height: 160px; flex: 1; min-width: 220px;
display: flex; align-items: center; justify-content: center;
}
.btn-bm {
background: #6c47ff; color: #fff; border: none; border-radius: 8px;
padding: .6rem 1.4rem; font-weight: 600; cursor: pointer;
}
</style>

<div class="demo-section">
<h2>List Items</h2>
<p>Add and remove items from a list with staggered enter/exit animations.</p>

<div class="demo-row">
<div class="demo-card" style="flex-direction:column;gap:.5rem;align-items:stretch;width:100%;min-width:280px;">
@foreach (var item in _items)
{
<BmotionAnimatePresence IsPresent="@item.Present" @key="item.Id">
<Bmotion Tag="div"
Style="display:flex;justify-content:space-between;align-items:center;background:rgba(108,71,255,.12);border:1px solid rgba(108,71,255,.25);border-radius:8px;padding:.6rem 1rem;"
Initial="@(new BmotionAnimationProps { Opacity = 0, X = -30 })"
Animate="@(new BmotionAnimationProps { Opacity = 1, X = 0 })"
Exit="@(new BmotionAnimationProps { Opacity = 0, X = 30 })"
Transition="@BmotionTransitionConfig.Spring(stiffness:300, damping:25)">
<span>@item.Label</span>
<button style="background:none;border:none;color:#ff4785;cursor:pointer;font-size:1.1rem"
@onclick="@(() => RemoveItem(item.Id))">✕</button>
</Bmotion>
</BmotionAnimatePresence>
}
</div>
</div>

<button class="btn-bm" style="margin-top:1rem" @onclick="AddItem">Add item</button>
</div>

@code {
private int _nextId = 4;

private sealed class Item(int id, string label)
{
public int Id { get; } = id;
public string Label { get; } = label;
public bool Present { get; set; } = true;
}

private List<Item> _items = [new(1,"Item One"), new(2,"Item Two"), new(3,"Item Three")];

void AddItem() => _items.Add(new(_nextId++, $"Item {_nextId - 1}"));

async Task RemoveItem(int id)
{
var item = _items.Find(i => i.Id == id);
if (item is null || !item.Present) return;
item.Present = false; // trigger the exit animation
await Task.Delay(400); // let it finish before removing the item
_items.Remove(item);
}
}
""";
}
Loading