Turn the top nav bar into a sidebar

Add an edit mode
This commit is contained in:
Neil Brommer 2021-12-07 17:12:20 -08:00
parent 91a660ef43
commit f63798e897
11 changed files with 266 additions and 49 deletions

View file

@ -58,13 +58,16 @@
<BookmarkGroup Group="group" /> <BookmarkGroup Group="group" />
} }
<div class="addBookmarkGroupButton text-center"> @if (this.state.Value.EditMode)
<button type="button" class="btn tooltip tooltip-bottom" {
@onclick="this.ShowCreateGroupForm" <div class="addBookmarkGroupButton text-center">
aria-label="Create Group" data-tooltip="Create Group"> <button type="button" class="btn tooltip tooltip-bottom"
<i class="icon icon-plus"></i> @onclick="this.ShowCreateGroupForm"
</button> aria-label="Create Group" data-tooltip="Create Group">
</div> <i class="icon icon-plus"></i>
</button>
</div>
}
</div> </div>
} }
</div> </div>

View file

@ -1,14 +1,17 @@
@using System.Drawing @using System.Drawing
@using Fluxor
@using Start.Client.Store.State
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
@inject IState<RootState> state
@inject IDispatcher dispatch
<div class="card bookmarkGroup"> <div class="card bookmarkGroup">
<div class="card-header" style="background-color: @this.Group.Color"> <div class="card-header" style="background-color: @this.Group.Color">
<h2 class="card-title h5 d-inline-block @this.ForegroundTitleColorClass"> <h2 class="card-title h6 d-inline-block @this.ForegroundTitleColorClass">
@this.Group.Title @this.Group.Title
</h2> </h2>
<button type="button" class="addBookmarkButton btn btn-sm tooltip tooltip-left"
aria-label="Create Bookmark" data-tooltip="Create Bookmark">
<i class="icon icon-plus"></i>
</button>
</div> </div>
<div class="card-body"> <div class="card-body">
<ul class="bookmarks"> <ul class="bookmarks">
@ -34,6 +37,16 @@
{ {
<Bookmark Model="bookmark" /> <Bookmark Model="bookmark" />
} }
@if (this.state.Value.EditMode)
{
<li class="addBookmarkItem">
<button type="button" class="addBookmarkButton btn">
<i class="icon icon-plus"></i>
Create Bookmark
</button>
</li>
}
} }
</ul> </ul>
</div> </div>
@ -44,8 +57,10 @@
[Parameter] [Parameter]
public BookmarkGroupDto Group { get; set; } = null!; public BookmarkGroupDto Group { get; set; } = null!;
protected string ForegroundTitleColorClass { protected string ForegroundTitleColorClass
get { {
get
{
const int threshold = 105; const int threshold = 105;
Color bgColor = ColorTranslator.FromHtml(this.Group.Color); Color bgColor = ColorTranslator.FromHtml(this.Group.Color);

View file

@ -0,0 +1,87 @@
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Start.Client.Store.Features.Sidebar
@using Start.Client.Store.State
@using Fluxor
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
@inject IState<RootState> state
@inject IDispatcher dispatch
@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager
<div class="off-canvas">
@* No off-canvas-toggle - add something that dispatches ShowSidebarAction *@
<div id="sidebar" class="off-canvas-sidebar @(this.state.Value.ShowSidebar ? "active" : "")">
<div id="sidebarHeading">
<h1>Start</h1>
<button class="btn btn-link" @onclick="this.OnSidebarHideClicked">
<i class="icon icon-cross icon-2x"></i>
</button>
</div>
<ul id="sidebarItems" class="nav">
<li class="nav-item">
<div class="form-group">
<label class="form-switch">
<input type="checkbox" @onclick="this.OnToggleEditMode" />
<i class="form-icon"></i>
Edit Mode
</label>
</div>
</li>
</ul>
<ul class="nav accountActions">
<AuthorizeView>
<Authorized>
<li class="nav-item accountName">
<figure class="avatar">
<i class="icon icon-people ml-2 mt-2"></i>
</figure>
@context.User.Identity?.Name
</li>
<li class="nav-item">
<a href="authentication/profile">Account</a>
</li>
<li class="nav-item">
<button class="nav-link btn btn-link" @onclick="BeginSignOut">Log out</button>
</li>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>
</ul>
</div>
<a class="off-canvas-overlay" @onclick="this.OnSidebarHideClicked"></a>
<div class="off-canvas-content">
@this.ChildContent
</div>
</div>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = null!;
protected void OnSidebarHideClicked()
{
dispatch.Dispatch(new HideSidebarAction());
}
protected void OnToggleEditMode()
{
dispatch.Dispatch(new ToggleEditModeAction());
}
private async Task BeginSignOut(MouseEventArgs args)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}

View file

@ -8,6 +8,7 @@
@using Start.Client.Store.Features.CurrentContainer @using Start.Client.Store.Features.CurrentContainer
@using Start.Client.Store.Features.CreateContainer @using Start.Client.Store.Features.CreateContainer
@using Start.Client.Store.Features.DeleteContainer @using Start.Client.Store.Features.DeleteContainer
@using Start.Client.Store.Features.Sidebar
@using Fluxor @using Fluxor
@* Distinguish from Refit.Authorize *@ @* Distinguish from Refit.Authorize *@
@ -17,7 +18,8 @@
@inject IState<RootState> state @inject IState<RootState> state
@inject IDispatcher dispatch @inject IDispatcher dispatch
@if (this.state.Value.ContainerListState.ErrorMessage != null) { @if (this.state.Value.ContainerListState.ErrorMessage != null)
{
<Alert Type="Alert.AlertType.Error"> <Alert Type="Alert.AlertType.Error">
<b>Error</b> @this.state.Value.ContainerListState.ErrorMessage <b>Error</b> @this.state.Value.ContainerListState.ErrorMessage
</Alert> </Alert>
@ -34,41 +36,60 @@
} }
else else
{ {
<ul class="containerList tab"> <Sidebar>
@foreach (BookmarkContainerDto container in this.state.Value.ContainerListState.Containers)
{
string itemClasses = "tab-item";
if (container.BookmarkContainerId == this.state.Value.CurrentContainerState.Container?.BookmarkContainerId)
itemClasses += " active";
<li class="@itemClasses"> <div id="containerTabStrip">
<a @onclick="() => OnContainerSelected(container.BookmarkContainerId)"> <button id="menuButton" class="btn btn-link" @onclick="this.ShowSidebar">
@container.Title <i class="icon icon-menu"></i>
<button class="btn btn-clear"
@onclick="() => this.OnDeleteContainerClicked(container.BookmarkContainerId)">
</button>
</a>
</li>
}
<li class="tab-item tab-action">
<button @onclick="OnCreateContainerClicked" class="btn btn-link tooltip tooltip-left"
title="Create New Container" aria-label="Create New Container"
data-tooltip="Create Container">
+
</button> </button>
</li> <ul class="containerList tab">
</ul> @foreach (BookmarkContainerDto container in this.state.Value.ContainerListState.Containers)
{
string itemClasses = "tab-item";
if (container.BookmarkContainerId == this.state.Value.CurrentContainerState.Container?.BookmarkContainerId)
itemClasses += " active";
<BookmarkContainer /> <li class="@itemClasses">
<a @onclick="() => OnContainerSelected(container.BookmarkContainerId)">
@container.Title
@if (this.state.Value.EditMode)
{
<button class="btn btn-clear"
@onclick="() => this.OnDeleteContainerClicked(container.BookmarkContainerId)">
</button>
}
</a>
</li>
}
@if (this.state.Value.EditMode)
{
<li class="tab-item tab-action">
<button @onclick="OnCreateContainerClicked" class="btn btn-link tooltip tooltip-left"
title="Create New Container" aria-label="Create New Container"
data-tooltip="Create Container">
+
</button>
</li>
}
</ul>
</div>
<CreateContainer /> <BookmarkContainer />
<DeleteContainer />
<CreateGroup /> <CreateContainer />
<DeleteContainer />
<CreateGroup />
</Sidebar>
} }
@code @code
{ {
protected void ShowSidebar()
{
dispatch.Dispatch(new ShowSidebarAction());
}
protected void OnContainerSelected(int bookmarkContainerId) protected void OnContainerSelected(int bookmarkContainerId)
{ {
dispatch.Dispatch(new LoadCurrentContainerAction(bookmarkContainerId)); dispatch.Dispatch(new LoadCurrentContainerAction(bookmarkContainerId));

View file

@ -2,11 +2,6 @@
<div class="page"> <div class="page">
<div class="main"> <div class="main">
<div class="top-row px-4 auth">
<LoginDisplay />
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<div class="content px-4"> <div class="content px-4">
@Body @Body
</div> </div>

View file

@ -47,6 +47,7 @@
<None Remove="Store\Features\ContainersList\" /> <None Remove="Store\Features\ContainersList\" />
<None Remove="Store\Features\CurrentContainer\" /> <None Remove="Store\Features\CurrentContainer\" />
<None Remove="Store\Features\CreateGroup\" /> <None Remove="Store\Features\CreateGroup\" />
<None Remove="Store\Features\Sidebar\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Remove="wwwroot\css\Spectre\" /> <Content Remove="wwwroot\css\Spectre\" />
@ -60,5 +61,6 @@
<Folder Include="Store\Features\ContainersList\" /> <Folder Include="Store\Features\ContainersList\" />
<Folder Include="Store\Features\CurrentContainer\" /> <Folder Include="Store\Features\CurrentContainer\" />
<Folder Include="Store\Features\CreateGroup\" /> <Folder Include="Store\Features\CreateGroup\" />
<Folder Include="Store\Features\Sidebar\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -0,0 +1,5 @@
namespace Start.Client.Store.Features.Sidebar {
public class ShowSidebarAction { }
public class HideSidebarAction { }
public class ToggleEditModeAction { }
}

View file

@ -0,0 +1,27 @@
using Fluxor;
using Start.Client.Store.State;
namespace Start.Client.Store.Features.Sidebar {
public static class SidebarReducers {
[ReducerMethod(typeof(ShowSidebarAction))]
public static RootState ShowSidebar(RootState state) {
return state with {
ShowSidebar = true
};
}
[ReducerMethod(typeof(HideSidebarAction))]
public static RootState HideSidebar(RootState state) {
return state with {
ShowSidebar = false
};
}
[ReducerMethod(typeof(ToggleEditModeAction))]
public static RootState ToggleEditMode(RootState state) {
return state with {
EditMode = !state.EditMode
};
}
}
}

View file

@ -8,6 +8,9 @@ namespace Start.Client.Store.State {
public ContainerListState ContainerListState { get; init; } public ContainerListState ContainerListState { get; init; }
public CurrentContainerState CurrentContainerState { get; init; } public CurrentContainerState CurrentContainerState { get; init; }
public bool ShowSidebar { get; init; }
public bool EditMode { get; init; }
public RootState() { public RootState() {
this.ContainerListState = new ContainerListState(); this.ContainerListState = new ContainerListState();
this.CurrentContainerState = new CurrentContainerState(); this.CurrentContainerState = new CurrentContainerState();

View file

@ -47,6 +47,65 @@
align-items: center; align-items: center;
} }
#sidebar {
display: flex;
flex-direction: column;
}
#sidebar #sidebarHeading {
padding: 0.5rem 1.5rem;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#sidebar #sidebarHeading h1 {
margin: 0;
}
#sidebar .nav {
padding: 0.5rem 1.5rem;
}
#sidebar #sidebarItems {
flex: 1; /* Fill remaining space */
overflow-y: auto;
}
#sidebar .accountActions {
border-top: solid 1px #dadee4;
}
#sidebar .accountActions .accountName {
font-weight: bold;
margin-bottom: 1em;
}
.off-canvas .off-canvas-content {
padding: 0;
}
#menubutton .icon {
transform: scale(1.5);
}
#containerTabStrip {
width: 100%;
display: flex;
align-items: center;
}
#containerTabStrip #menuButton {
flex: 0 0 auto;
margin-right: 1em;
}
#containerTabStrip .containerList {
flex: 1 0 0;
}
#bookmarkGroups { #bookmarkGroups {
margin-top: 1em; margin-top: 1em;
margin-left: auto; margin-left: auto;
@ -71,7 +130,7 @@
} }
} }
@media screen and (min-width: 1280px) { @media screen and (min-width: 1440) {
#bookmarkGroups { #bookmarkGroups {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
width: 70%; width: 70%;
@ -113,6 +172,5 @@ li.noBookmarksItem {
} }
button.addBookmarkButton { button.addBookmarkButton {
float: right; width: 100%;
margin-top: 0.5em;
} }

View file

@ -10,6 +10,7 @@
<script src="_content/Fluxor.Blazor.Web/scripts/index.js"></script> <script src="_content/Fluxor.Blazor.Web/scripts/index.js"></script>
<link href="css/spectre/spectre.min.css" rel="stylesheet" /> <link href="css/spectre/spectre.min.css" rel="stylesheet" />
<link href="css/spectre/spectre-exp.min.css" rel="stylesheet" />
<link href="css/spectre/spectre-icons.css" rel="stylesheet" /> <link href="css/spectre/spectre-icons.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" />
<link href="Start.Client.styles.css" rel="stylesheet" /> <link href="Start.Client.styles.css" rel="stylesheet" />