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
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Blazor UI Kit Roadmap Bootstrap Italia Components
# Blazor UI Kit Roadmap Bootstrap Italia Components

This document outlines the development roadmap for a Blazor UI kit library implementing the official [Bootstrap Italia](https://italia.github.io/bootstrap-italia/docs) components. The project targets .NET 9.

Expand Down
7 changes: 7 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ Pagination control for navigating large data sets across multiple pages.
- Optional jump-to-page input and total-items summary
- Customizable previous/next buttons and alignment

#### [Toolbar](components/toolbar.md)
Navigation toolbar for grouping icon-based action items.
- Horizontal and vertical orientations
- Three size variants (default, medium, small)
- Badge counts and labels on items
- `BitToolbarDivider` sub-component for visual separation

### Form Components

BitBlazor provides a comprehensive set of form components that integrate seamlessly with ASP.NET Core Blazor's form system and Bootstrap Italia styling.
Expand Down
190 changes: 190 additions & 0 deletions docs/components/toolbar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# BitToolbar

The `BitToolbar` component represents a [toolbar using Bootstrap Italia styles](https://italia.github.io/bootstrap-italia/docs/menu-di-navigazione/toolbar/).

## Namespace

```csharp
BitBlazor.Components
```

## Description

The Toolbar component provides a navigation bar for grouping and displaying icon-based action items. It supports horizontal and vertical orientations, three size variants, optional badge counts, and a divider sub-component. Items support an active and a disabled state, and are compatible with both static (SSR) and interactive rendering modes.

Each `BitToolbarItem` can be used as a navigation link (via `Href`), a click handler (via `OnClick`), or both at the same time. When both are provided, `OnClick` takes precedence in interactive rendering while `Href` remains available for browser-native behaviors such as right-click → Open in new tab. In static SSR rendering, `Href` is the only navigation mechanism.

## Components

| Component | Description |
|-----------|-------------|
| `BitToolbar` | Root container that renders a `<nav>` with an inner `<ul>` and cascades itself to child items |
| `BitToolbarItem` | Individual action item rendered as an `<li>` with an icon and a label |
| `BitToolbarDivider` | Visual and semantic separator between items |

## BitToolbar Parameters

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `ChildContent` | `RenderFragment` | ✓ | - | One or more `BitToolbarItem` or `BitToolbarDivider` components to render inside the toolbar |
| `Size` | `ToolbarSize` | ✗ | `ToolbarSize.Default` | Controls the visual size of the toolbar |
| `Orientation` | `Orientation` | ✗ | `Orientation.Horizontal` | Determines whether the toolbar is laid out horizontally or vertically |
| `Id` | `string?` | ✗ | `null` | Sets the `id` HTML attribute on the root element |
| `CssClass` | `string?` | ✗ | `null` | Additional CSS classes to apply to the root `<nav>` element |
| `AdditionalAttributes` | `IDictionary<string, object>?` | ✗ | - | Additional HTML attributes forwarded to the root element |

## BitToolbarItem Parameters

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `Label` | `string` | ✓ | - | The visible label text for the item |
| `IconName` | `string` | ✓ | - | The Bootstrap Italia icon name to display |
| `Href` | `string?` | ✗ | `null` | The URL the item links to. In SSR rendering the browser follows this URL on click. In interactive rendering it is a navigation fallback when `OnClick` has no delegate, and a secondary browser target (right-click, Ctrl+Click) when `OnClick` is set. |
| `Active` | `bool` | ✗ | `false` | When `true`, applies the active style to the item |
| `Disabled` | `bool` | ✗ | `false` | When `true`, disables the item and adds `aria-disabled="true"` |
| `BadgeCount` | `int?` | ✗ | `null` | A numeric badge count shown on the item; hidden when `null` or `0` |
| `BadgeLabel` | `string?` | ✗ | `null` | A text label shown next to the badge; displayed in different positions depending on `Size` |
| `OnClick` | `EventCallback` | ✗ | - | Primary interactive callback invoked when the item is clicked. Takes precedence over `Href` navigation in interactive rendering. Not invoked during static (SSR) rendering — provide `Href` as a navigation fallback for SSR contexts. |
| `Id` | `string?` | ✗ | `null` | Sets the `id` HTML attribute on the root element |
| `CssClass` | `string?` | ✗ | `null` | Additional CSS classes to apply to the item |
| `AdditionalAttributes` | `IDictionary<string, object>?` | ✗ | - | Additional HTML attributes forwarded to the root element |

## Used Enumerations

### ToolbarSize

| Value | Description |
|-------|-------------|
| `Default` | Standard size; badge count and badge label are shown inline next to the label |
| `Medium` | Reduced size; badge label is shown in the badge wrapper area instead of next to the label |
| `Small` | Smallest size; badge count is hidden and only the badge label is shown |

### Orientation

| Value | Description |
|-------|-------------|
| `Horizontal` | Items are arranged from left to right (default) |
| `Vertical` | Items are stacked from top to bottom |

## Usage Examples

### Basic horizontal toolbar

```razor
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>
```

### With active and disabled items

```razor
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" Active="true" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarItem IconName="it-lock" Label="Locked" Disabled="true" />
</BitToolbar>
```

### With divider

```razor
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarDivider />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>
```

### With badge count and label

```razor
<BitToolbar>
<BitToolbarItem IconName="it-mail" Label="Messages" BadgeCount="5" BadgeLabel="new" />
<BitToolbarItem IconName="it-bell" Label="Notifications" BadgeCount="12" />
</BitToolbar>
```

### Medium size

```razor
<BitToolbar Size="ToolbarSize.Medium">
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>
```

### Small size

```razor
<BitToolbar Size="ToolbarSize.Small">
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>
```

### Vertical orientation

```razor
<BitToolbar Orientation="Orientation.Vertical">
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>
```

### With click callback

```razor
<BitToolbar>
<BitToolbarItem IconName="it-download" Label="Download" OnClick="HandleDownload" />
<BitToolbarItem IconName="it-delete" Label="Delete" OnClick="HandleDelete" />
</BitToolbar>

@code {
private void HandleDownload() { /* ... */ }
private void HandleDelete() { /* ... */ }
}
```

### Navigation items (SSR-compatible)

When only `Href` is provided the item works as a plain navigation link in both SSR and interactive rendering.

```razor
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarDivider />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>
```

### Mixed mode (OnClick takes precedence)

When both `Href` and `OnClick` are provided, `OnClick` handles the primary click in interactive rendering. `Href` is still rendered on the `<a>` element so that right-click → Open in new tab continues to work.

```razor
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" OnClick="HandleHome" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" OnClick="HandleSearch" />
</BitToolbar>

@code {
private void HandleHome() { /* custom SPA navigation or analytics */ }
private void HandleSearch() { /* open search panel */ }
}
```

### With additional attributes

```razor
<BitToolbar data-testid="main-toolbar" aria-label="Main actions">
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
</BitToolbar>
```
24 changes: 24 additions & 0 deletions docs/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,30 @@ This guide provides a quick overview of all BitBlazor components with basic exam
</BitPagination>
```

### Toolbar
```razor
<!-- Basic horizontal toolbar -->
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarItem IconName="it-settings" Label="Settings" Href="/settings" />
</BitToolbar>

<!-- With active item, disabled item, and divider -->
<BitToolbar>
<BitToolbarItem IconName="it-home" Label="Home" Href="/" Active="true" />
<BitToolbarItem IconName="it-search" Label="Search" Href="/search" />
<BitToolbarDivider />
<BitToolbarItem IconName="it-lock" Label="Locked" Disabled="true" />
</BitToolbar>

<!-- Small vertical toolbar with badge -->
<BitToolbar Size="ToolbarSize.Small" Orientation="Orientation.Vertical">
<BitToolbarItem IconName="it-mail" Label="Messages" BadgeCount="5" BadgeLabel="new" />
<BitToolbarItem IconName="it-bell" Label="Notifications" BadgeCount="3" />
</BitToolbar>
```

## Form Components

### BitTextField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,31 @@
</p>
</div>

<!-- Azioni rapide -->
<div class="mb-3">
<BitToolbar>
<BitToolbarItem IconName="@Icons.ItPlusCircle"
Label="Nuova Pratica"
Active="@(_activeToolbarItem == "nuova-pratica")"
OnClick="NuovaPraticaAsync" />
<BitToolbarItem IconName="@Icons.ItDownload"
Label="Scarica"
BadgeCount="3"
BadgeLabel="in scadenza"
Active="@(_activeToolbarItem == "scarica")"
OnClick="ScaricaAsync" />
<BitToolbarItem IconName="@Icons.ItPrint"
Label="Stampa"
Active="@(_activeToolbarItem == "stampa")"
OnClick="StampaAsync" />
<BitToolbarDivider />
<BitToolbarItem IconName="@Icons.ItSettings"
Label="Impostazioni"
Active="@(_activeToolbarItem == "impostazioni")"
OnClick="ImpostazioniAsync" />
</BitToolbar>
</div>

<!-- Filtri stato -->
<div class="d-flex flex-wrap gap-2 mb-4" role="group" aria-label="Filtra per stato">
@foreach (var filtro in StatiFiltro)
Expand Down Expand Up @@ -103,6 +128,7 @@ else

private int currentPage = 1;
private string? statoFiltroAttivo;
private string? _activeToolbarItem;
private PraticheResult? result;
private int totalPages;

Expand Down Expand Up @@ -143,4 +169,28 @@ else
"In Attesa" => Color.Warning,
_ => Color.Secondary
};

private async Task NuovaPraticaAsync()
{
_activeToolbarItem = _activeToolbarItem == "nuova-pratica" ? null : "nuova-pratica";
await InvokeAsync(StateHasChanged);
}

private Task ScaricaAsync()
{
_activeToolbarItem = _activeToolbarItem == "scarica" ? null : "scarica";
return Task.CompletedTask;
}

private Task StampaAsync()
{
_activeToolbarItem = _activeToolbarItem == "stampa" ? null : "stampa";
return Task.CompletedTask;
}

private Task ImpostazioniAsync()
{
_activeToolbarItem = _activeToolbarItem == "impostazioni" ? null : "impostazioni";
return Task.CompletedTask;
}
}
11 changes: 11 additions & 0 deletions src/BitBlazor/Components/Toolbar/BitToolbar.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@namespace BitBlazor.Components

@inherits BitComponentBase

<nav class="@ComputeContainerCssClass()" @attributes="AdditionalAttributes">
<ul>
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
</ul>
</nav>
69 changes: 69 additions & 0 deletions src/BitBlazor/Components/Toolbar/BitToolbar.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using BitBlazor.Core;
using Microsoft.AspNetCore.Components;

namespace BitBlazor.Components;

/// <summary>
/// Represents a toolbar component that can contain multiple <see cref="BitToolbarItem"/> components. It provides a container for organizing and displaying toolbar items
/// </summary>
public partial class BitToolbar : BitComponentBase
{
/// <summary>
/// Gets or sets the content to be rendered inside the toolbar.
/// This can include one or more <see cref="BitToolbarItem"/> components,
/// optionally separated by <see cref="BitToolbarDivider"/> components.
/// </summary>
[Parameter]
[EditorRequired]
public RenderFragment ChildContent { get; set; }

/// <summary>
/// Gets or sets the size of the toolbar.
/// This property allows you to specify the visual size of the toolbar, which can affect its layout and appearance.
/// The default value is <see cref="ToolbarSize.Default"/>.
/// </summary>
[Parameter]
public ToolbarSize Size { get; set; } = ToolbarSize.Default;

/// <summary>
/// Gets or sets the orientation of the toolbar.
/// This property determines whether the toolbar items are arranged horizontally or vertically.
/// The default value is <see cref="Orientation.Horizontal"/>.
/// </summary>
[Parameter]
public Orientation Orientation { get; set; } = Orientation.Horizontal;

private string ComputeContainerCssClass()
{
var builder = new CssClassBuilder("toolbar");
AddSizeClass(builder);
AddOrientationClass(builder);

AddCustomCssClass(builder);

return builder.Build();
}

private void AddOrientationClass(CssClassBuilder builder)
{
var orientationClass = Orientation switch
{
Orientation.Vertical => "toolbar-vertical",
_ => string.Empty
};

builder.Add(orientationClass);
}

private void AddSizeClass(CssClassBuilder builder)
{
var sizeClass = Size switch
{
ToolbarSize.Small => "toolbar-small",
ToolbarSize.Medium => "toolbar-medium",
_ => string.Empty
};

builder.Add(sizeClass);
}
}
3 changes: 3 additions & 0 deletions src/BitBlazor/Components/Toolbar/BitToolbarDivider.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@namespace BitBlazor.Components

<li class="toolbar-divider" role="separator" aria-orientation="vertical"></li>
Comment thread
albx marked this conversation as resolved.
Loading
Loading