first commit

This commit is contained in:
2026-05-18 15:53:59 +03:30
commit 2c100028a1
534 changed files with 94240 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
# SbAddButton
A specialized button for adding new items in builder interfaces. Supports a dropdown menu of options or direct add action. Perfect for visual builders and content editors.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Label | string? | null | Button label text |
| Options | List<SbAddOption> | [] | Dropdown options for different add types |
| Variant | SbAddButtonVariant | Default | Button variant (Default, Primary, Outline, Ghost) |
| Size | SbAddButtonSize | Medium | Button size (Small, Medium, Large) |
| Disabled | bool | false | Whether the button is disabled |
| AriaLabel | string | "Add item" | Accessibility label |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| OnOptionSelected | EventCallback<SbAddOption> | Fired when a dropdown option is selected |
| OnAdd | EventCallback | Fired on click when no options are provided |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| Icon | RenderFragment? | Custom icon content (replaces default + icon) |
## SbAddOption Class
| Property | Type | Description |
|----------|------|-------------|
| Id | string | Unique identifier |
| Label | string | Display label |
| Description | string? | Optional description text |
| IconText | string? | Icon text (emoji or symbol) |
| Icon | RenderFragment? | Custom icon content |
| Disabled | bool | Whether the option is disabled |
| Data | object? | Custom data associated with option |
## CSS Classes
- `sb-add-button-container` - Container wrapper
- `sb-add-button` - Button element
- `sb-add-button--active` - Menu is open
- `sb-add-button__icon` - Icon container
- `sb-add-button__icon--rotated` - Rotated when open
- `sb-add-button__label` - Label text
- `sb-add-button__menu` - Dropdown menu
- `sb-add-button__option` - Menu option
- `sb-add-button__option-icon` - Option icon
- `sb-add-button__option-label` - Option label
- `sb-add-button__option-description` - Option description
## Examples
### Simple Add Button
```razor
<SbAddButton Label="Add Item" OnAdd="HandleAdd" />
@code {
private void HandleAdd()
{
// Add new item
}
}
```
### With Dropdown Options
```razor
<SbAddButton Label="Add Block"
Options="@blockOptions"
OnOptionSelected="HandleOptionSelected" />
@code {
private List<SbAddOption> blockOptions = new()
{
new() { Id = "text", Label = "Text Block", IconText = "📝", Description = "Add a paragraph" },
new() { Id = "image", Label = "Image", IconText = "🖼️", Description = "Add an image" },
new() { Id = "video", Label = "Video", IconText = "🎬", Description = "Embed a video" },
new() { Id = "divider", Label = "Divider", IconText = "—", Description = "Add a separator" }
};
private void HandleOptionSelected(SbAddOption option)
{
Console.WriteLine($"Selected: {option.Id}");
}
}
```
### Variants
```razor
<SbAddButton Variant="SbAddButtonVariant.Default" Label="Default" OnAdd="Add" />
<SbAddButton Variant="SbAddButtonVariant.Primary" Label="Primary" OnAdd="Add" />
<SbAddButton Variant="SbAddButtonVariant.Outline" Label="Outline" OnAdd="Add" />
<SbAddButton Variant="SbAddButtonVariant.Ghost" Label="Ghost" OnAdd="Add" />
```
### Sizes
```razor
<SbAddButton Size="SbAddButtonSize.Small" Label="Small" OnAdd="Add" />
<SbAddButton Size="SbAddButtonSize.Medium" Label="Medium" OnAdd="Add" />
<SbAddButton Size="SbAddButtonSize.Large" Label="Large" OnAdd="Add" />
```
### Custom Icon
```razor
<SbAddButton OnAdd="Add">
<Icon>
<SbIcon Name="plus-circle" />
</Icon>
</SbAddButton>
```
### Icon Only
```razor
<SbAddButton OnAdd="Add" AriaLabel="Add new item" />
```
### Page Builder Example
```razor
<div class="page-builder">
@foreach (var block in blocks)
{
<div class="block">@block.Content</div>
}
<SbAddButton Label="Add Content Block"
Options="@contentOptions"
OnOptionSelected="AddBlock"
Variant="SbAddButtonVariant.Outline" />
</div>
@code {
private List<Block> blocks = new();
private List<SbAddOption> contentOptions = new()
{
new() { Id = "heading", Label = "Heading", IconText = "H", Description = "Add a heading" },
new() { Id = "paragraph", Label = "Paragraph", IconText = "¶", Description = "Add text content" },
new() { Id = "list", Label = "List", IconText = "•", Description = "Add a bulleted list" },
new() { Id = "quote", Label = "Quote", IconText = "❝", Description = "Add a blockquote" },
new() { Id = "code", Label = "Code", IconText = "</>", Description = "Add a code block" }
};
private void AddBlock(SbAddOption option)
{
blocks.Add(new Block { Type = option.Id, Content = $"New {option.Label}" });
}
}
```
+148
View File
@@ -0,0 +1,148 @@
# SbDragHandle
A dedicated drag handle element for reordering items. Provides both mouse drag and keyboard navigation support for accessibility.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Data | object? | null | Data to transfer during drag |
| Disabled | bool | false | Whether the handle is disabled |
| AriaLabel | string | "Drag to reorder" | Accessibility label |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| OnDragStarted | EventCallback<DragEventArgs> | Fired when drag begins |
| OnDragEnded | EventCallback<DragEventArgs> | Fired when drag ends |
| OnMoveUp | EventCallback | Fired on ArrowUp key press |
| OnMoveDown | EventCallback | Fired on ArrowDown key press |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ChildContent | RenderFragment? | Custom handle content (default: ⋮⋮) |
## CSS Classes
- `sb-drag-handle` - Base class
- `sb-drag-handle--disabled` - Disabled state
- `sb-drag-handle__icon` - Default icon element
## Accessibility
- Uses `role="button"` for screen readers
- Supports `tabindex` for keyboard focus
- Arrow keys trigger `OnMoveUp`/`OnMoveDown` for keyboard reordering
- `aria-label` describes the handle's purpose
## Examples
### Basic Handle
```razor
<div class="list-item">
<SbDragHandle />
<span>Item content</span>
</div>
```
### With Keyboard Support
```razor
@foreach (var (item, index) in items.Select((item, i) => (item, i)))
{
<div class="list-item">
<SbDragHandle OnMoveUp="() => MoveUp(index)"
OnMoveDown="() => MoveDown(index)"
OnDragStarted="e => StartDrag(index)"
OnDragEnded="e => EndDrag()" />
<span>@item.Name</span>
</div>
}
@code {
private List<Item> items = new() { /* ... */ };
private void MoveUp(int index)
{
if (index > 0)
{
var temp = items[index];
items[index] = items[index - 1];
items[index - 1] = temp;
}
}
private void MoveDown(int index)
{
if (index < items.Count - 1)
{
var temp = items[index];
items[index] = items[index + 1];
items[index + 1] = temp;
}
}
}
```
### Custom Handle Icon
```razor
<SbDragHandle>
<SbIcon Name="grip-vertical" />
</SbDragHandle>
```
### Disabled Handle
```razor
<SbDragHandle Disabled="@item.IsLocked" />
```
### In a Sortable Card List
```razor
<div class="card-list">
@foreach (var card in cards)
{
<SbCard Class="sortable-card">
<Header>
<SbStack Direction="SbDirection.Row" Justify="SbJustify.SpaceBetween">
<SbDragHandle />
<SbHeading Level="4">@card.Title</SbHeading>
<SbIconButton Icon="more-vertical" />
</SbStack>
</Header>
<ChildContent>
@card.Content
</ChildContent>
</SbCard>
}
</div>
```
### With Data Transfer
```razor
<SbDragHandle Data="@item"
OnDragStarted="HandleDragStart"
OnDragEnded="HandleDragEnd">
<span class="grip">⠿</span>
</SbDragHandle>
@code {
private void HandleDragStart(DragEventArgs args)
{
// Store dragged item reference
}
private void HandleDragEnd(DragEventArgs args)
{
// Clean up
}
}
```
+148
View File
@@ -0,0 +1,148 @@
# SbDraggableItem
A wrapper component that makes its content draggable via HTML5 drag and drop. Used in visual builders for drag-and-drop interfaces.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Data | object? | null | Data to transfer during drag operation |
| ItemType | string? | null | Type identifier for filtering drop targets |
| Disabled | bool | false | Whether dragging is disabled |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| OnDragStarted | EventCallback<SbDragStartEventArgs> | Fired when drag begins |
| OnDragEnded | EventCallback | Fired when drag ends |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ChildContent | RenderFragment? | Content to make draggable |
## SbDragStartEventArgs Class
| Property | Type | Description |
|----------|------|-------------|
| Data | object? | The data being dragged |
| ItemType | string? | The item type identifier |
## CSS Classes
- `sb-draggable-item` - Base class
- `sb-draggable-item--dragging` - Applied during drag
- `sb-draggable-item--disabled` - Disabled state
## Examples
### Basic Draggable Item
```razor
<SbDraggableItem Data="@item" OnDragStarted="HandleDragStart">
<div class="my-item">
Drag me!
</div>
</SbDraggableItem>
@code {
private object item = new { Id = 1, Name = "Item 1" };
private void HandleDragStart(SbDragStartEventArgs args)
{
Console.WriteLine($"Dragging: {args.Data}");
}
}
```
### With Item Type
```razor
<SbDraggableItem Data="@widget" ItemType="widget" OnDragStarted="HandleDragStart">
<div class="widget-card">
<SbIcon Name="grid" />
<span>Widget</span>
</div>
</SbDraggableItem>
```
### Palette of Draggable Components
```razor
<div class="component-palette">
@foreach (var component in availableComponents)
{
<SbDraggableItem Data="@component"
ItemType="@component.Type"
OnDragStarted="HandleDragStart"
OnDragEnded="HandleDragEnd">
<div class="palette-item">
<SbIcon Name="@component.Icon" />
<span>@component.Name</span>
</div>
</SbDraggableItem>
}
</div>
@code {
private List<ComponentInfo> availableComponents = new()
{
new() { Type = "button", Name = "Button", Icon = "square" },
new() { Type = "text", Name = "Text", Icon = "type" },
new() { Type = "image", Name = "Image", Icon = "image" }
};
private ComponentInfo? draggedComponent;
private void HandleDragStart(SbDragStartEventArgs args)
{
draggedComponent = args.Data as ComponentInfo;
}
private void HandleDragEnd()
{
draggedComponent = null;
}
}
```
### Disabled State
```razor
<SbDraggableItem Data="@item" Disabled="@isLocked">
<div class="locked-item">
@if (isLocked)
{
<SbIcon Name="lock" />
}
Item Content
</div>
</SbDraggableItem>
```
### Combined with SbDropZone
```razor
<div class="builder-layout">
<div class="toolbox">
<SbDraggableItem Data="@textBlock" ItemType="block">
<div class="tool">Text Block</div>
</SbDraggableItem>
<SbDraggableItem Data="@imageBlock" ItemType="block">
<div class="tool">Image Block</div>
</SbDraggableItem>
</div>
<SbDropZone AcceptedTypes="@(new[] { "block" })"
OnItemDropped="HandleDrop"
PlaceholderText="Drag blocks here">
@foreach (var block in pageBlocks)
{
<div class="placed-block">@block.Name</div>
}
</SbDropZone>
</div>
```
+164
View File
@@ -0,0 +1,164 @@
# SbDropZone
A designated area that accepts dragged items. Provides visual feedback during drag operations and can filter acceptable item types.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| PlaceholderText | string | "Drag items here" | Text shown when zone is empty |
| DropText | string | "Drop here" | Text shown during drag over |
| AcceptedTypes | string[]? | null | Array of accepted item types (null accepts all) |
| Disabled | bool | false | Whether the zone accepts drops |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| OnItemDropped | EventCallback<SbDropEventArgs> | Fired when item is dropped |
| OnDragEnter | EventCallback | Fired when drag enters zone |
| OnDragExit | EventCallback | Fired when drag leaves zone |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ChildContent | RenderFragment? | Content to show inside the zone |
## SbDropEventArgs Class
| Property | Type | Description |
|----------|------|-------------|
| ClientX | double | X coordinate of drop |
| ClientY | double | Y coordinate of drop |
| Data | object? | Dropped data |
## CSS Classes
- `sb-dropzone` - Base class
- `sb-dropzone--drag-over` - Item is being dragged over
- `sb-dropzone--disabled` - Disabled state
- `sb-dropzone__indicator` - Drop indicator
- `sb-dropzone__placeholder` - Empty placeholder
- `sb-dropzone__icon` - Icon element
- `sb-dropzone__text` - Text element
## Examples
### Basic Drop Zone
```razor
<SbDropZone OnItemDropped="HandleDrop" />
@code {
private void HandleDrop(SbDropEventArgs args)
{
Console.WriteLine($"Dropped at ({args.ClientX}, {args.ClientY})");
}
}
```
### With Custom Placeholder
```razor
<SbDropZone PlaceholderText="Drop components here"
DropText="Release to add"
OnItemDropped="HandleDrop" />
```
### With Type Filtering
```razor
<SbDropZone AcceptedTypes="@(new[] { "widget", "component" })"
PlaceholderText="Drop widgets or components here"
OnItemDropped="HandleDrop" />
```
### With Content
```razor
<SbDropZone OnItemDropped="HandleDrop">
@if (droppedItems.Any())
{
@foreach (var item in droppedItems)
{
<div class="dropped-item">@item.Name</div>
}
}
</SbDropZone>
```
### Page Builder Canvas
```razor
<div class="page-builder">
<aside class="toolbox">
<SbDraggableItem Data="@textBlock" ItemType="block">
<div class="tool">📝 Text</div>
</SbDraggableItem>
<SbDraggableItem Data="@imageBlock" ItemType="block">
<div class="tool">🖼️ Image</div>
</SbDraggableItem>
</aside>
<main class="canvas">
<SbDropZone AcceptedTypes="@(new[] { "block" })"
PlaceholderText="Drag blocks to build your page"
DropText="Drop to add block"
OnItemDropped="AddBlock"
OnDragEnter="ShowDropGuide"
OnDragExit="HideDropGuide">
@foreach (var block in pageBlocks)
{
<div class="page-block">
@block.Content
</div>
}
</SbDropZone>
</main>
</div>
@code {
private List<Block> pageBlocks = new();
private void AddBlock(SbDropEventArgs args)
{
if (args.Data is Block block)
{
pageBlocks.Add(block);
}
}
}
```
### Multiple Drop Zones
```razor
<div class="kanban-board">
@foreach (var column in columns)
{
<div class="kanban-column">
<h3>@column.Title</h3>
<SbDropZone AcceptedTypes="@(new[] { "task" })"
OnItemDropped="e => MoveTask(e, column.Id)"
PlaceholderText="Drop tasks here">
@foreach (var task in column.Tasks)
{
<SbDraggableItem Data="@task" ItemType="task">
<div class="task-card">@task.Title</div>
</SbDraggableItem>
}
</SbDropZone>
</div>
}
</div>
```
### Disabled Drop Zone
```razor
<SbDropZone Disabled="@isReadOnly"
PlaceholderText="@(isReadOnly ? "Read-only mode" : "Drop items here")"
OnItemDropped="HandleDrop" />
```
+203
View File
@@ -0,0 +1,203 @@
# SbInlineToolbar
A floating toolbar for context-specific actions. Typically appears near selected elements in visual editors and builders.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Actions | List<SbToolbarAction> | [] | Toolbar action buttons |
| Visible | bool | true | Whether the toolbar is visible |
| Position | SbToolbarPosition | Top | Position relative to content |
| X | double? | null | Absolute X position (when Position is Float) |
| Y | double? | null | Absolute Y position (when Position is Float) |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| OnAction | EventCallback<SbToolbarAction> | Fired when any action is clicked |
## SbToolbarAction Class
| Property | Type | Description |
|----------|------|-------------|
| Id | string | Unique identifier |
| Label | string? | Button label text |
| IconText | string? | Icon text (emoji or symbol) |
| Icon | RenderFragment? | Custom icon content |
| Tooltip | string? | Tooltip text |
| Disabled | bool | Whether action is disabled |
| IsActive | bool | Whether action is active/selected |
| IsDanger | bool | Whether action is destructive |
| IsSeparator | bool | Renders as separator instead of button |
| OnClick | Action? | Direct click handler |
## Position Options
| Value | Description |
|-------|-------------|
| Top | Above the content |
| Bottom | Below the content |
| Left | Left side of content |
| Right | Right side of content |
| Float | Absolute positioning using X/Y |
## Static Methods
```csharp
SbToolbarAction.Separator() // Creates a separator action
```
## CSS Classes
- `sb-inline-toolbar` - Base class
- `sb-inline-toolbar--visible` - Visible state
- `sb-inline-toolbar__separator` - Separator element
- `sb-inline-toolbar__btn` - Action button
- `sb-inline-toolbar__btn--active` - Active state
- `sb-inline-toolbar__btn--danger` - Danger variant
- `sb-inline-toolbar__icon` - Icon element
- `sb-inline-toolbar__label` - Label element
## Examples
### Basic Toolbar
```razor
<SbInlineToolbar Actions="@toolbarActions" OnAction="HandleAction" />
@code {
private List<SbToolbarAction> toolbarActions = new()
{
new() { Id = "bold", IconText = "B", Tooltip = "Bold" },
new() { Id = "italic", IconText = "I", Tooltip = "Italic" },
new() { Id = "underline", IconText = "U", Tooltip = "Underline" }
};
private void HandleAction(SbToolbarAction action)
{
Console.WriteLine($"Action: {action.Id}");
}
}
```
### With Separators and Danger Actions
```razor
<SbInlineToolbar Actions="@editActions" />
@code {
private List<SbToolbarAction> editActions = new()
{
new() { Id = "edit", IconText = "✏️", Tooltip = "Edit" },
new() { Id = "duplicate", IconText = "📋", Tooltip = "Duplicate" },
SbToolbarAction.Separator(),
new() { Id = "delete", IconText = "🗑️", Tooltip = "Delete", IsDanger = true }
};
}
```
### With Active State
```razor
<SbInlineToolbar Actions="@formatActions" />
@code {
private List<SbToolbarAction> formatActions = new()
{
new() { Id = "bold", IconText = "B", IsActive = isBold },
new() { Id = "italic", IconText = "I", IsActive = isItalic },
new() { Id = "underline", IconText = "U", IsActive = isUnderline }
};
private bool isBold, isItalic, isUnderline;
}
```
### Floating Position
```razor
<SbInlineToolbar Actions="@actions"
Position="SbToolbarPosition.Float"
X="@toolbarX"
Y="@toolbarY"
Visible="@showToolbar" />
@code {
private double toolbarX, toolbarY;
private bool showToolbar;
private void ShowToolbarAt(double x, double y)
{
toolbarX = x;
toolbarY = y;
showToolbar = true;
}
}
```
### Rich Text Editor Toolbar
```razor
<div class="editor-container">
<SbInlineToolbar Actions="@editorActions"
Position="SbToolbarPosition.Top"
Visible="@hasSelection"
OnAction="ApplyFormat" />
<div class="editor-content" @onmouseup="CheckSelection">
@content
</div>
</div>
@code {
private bool hasSelection;
private List<SbToolbarAction> editorActions = new()
{
new() { Id = "bold", IconText = "B", Tooltip = "Bold (Ctrl+B)" },
new() { Id = "italic", IconText = "I", Tooltip = "Italic (Ctrl+I)" },
SbToolbarAction.Separator(),
new() { Id = "link", IconText = "🔗", Tooltip = "Insert Link" },
new() { Id = "highlight", IconText = "🖍️", Tooltip = "Highlight" }
};
}
```
### Element Selection Toolbar
```razor
@if (selectedElement != null)
{
<SbInlineToolbar Actions="@elementActions"
Position="SbToolbarPosition.Top"
OnAction="HandleElementAction">
</SbInlineToolbar>
}
@code {
private object? selectedElement;
private List<SbToolbarAction> elementActions = new()
{
new() { Id = "move", IconText = "↕️", Tooltip = "Move" },
new() { Id = "resize", IconText = "↔️", Tooltip = "Resize" },
new() { Id = "settings", IconText = "⚙️", Tooltip = "Settings" },
SbToolbarAction.Separator(),
new() { Id = "delete", IconText = "🗑️", Tooltip = "Delete", IsDanger = true }
};
}
```
### Hidden/Visible Toggle
```razor
<SbInlineToolbar Actions="@actions"
Visible="@isToolbarVisible" />
<SbButton OnClick="() => isToolbarVisible = !isToolbarVisible">
Toggle Toolbar
</SbButton>
```
+177
View File
@@ -0,0 +1,177 @@
# SbInspectorPanel
A collapsible properties panel for inspecting and editing selected elements. Commonly used as a sidebar in visual builders and design tools.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Title | string | "Properties" | Panel title |
| Collapsible | bool | true | Whether panel can be collapsed |
| Collapsed | bool | false | Current collapsed state |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| CollapsedChanged | EventCallback<bool> | Fired when collapsed state changes |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ChildContent | RenderFragment? | Panel content (used when Sections is not provided) |
| Sections | RenderFragment? | Container for SbInspectorSection components |
| HeaderActions | RenderFragment? | Action buttons in the header |
## CSS Classes
- `sb-inspector-panel` - Base class
- `sb-inspector-panel--collapsed` - Collapsed state
- `sb-inspector-panel__header` - Header container
- `sb-inspector-panel__title` - Title text
- `sb-inspector-panel__toggle` - Collapse toggle button
- `sb-inspector-panel__header-actions` - Header actions container
- `sb-inspector-panel__body` - Body content area
## Examples
### Basic Inspector Panel
```razor
<SbInspectorPanel Title="Properties">
<SbTextField Label="Name" @bind-Value="name" />
<SbNumberField Label="Width" @bind-Value="width" />
<SbNumberField Label="Height" @bind-Value="height" />
</SbInspectorPanel>
```
### With Sections
```razor
<SbInspectorPanel Title="Element Inspector">
<Sections>
<SbInspectorSection Title="Layout" DefaultExpanded="true">
<SbNumberField Label="X" @bind-Value="x" />
<SbNumberField Label="Y" @bind-Value="y" />
<SbNumberField Label="Width" @bind-Value="width" />
<SbNumberField Label="Height" @bind-Value="height" />
</SbInspectorSection>
<SbInspectorSection Title="Appearance">
<SbColorPicker Label="Background" @bind-Value="bgColor" />
<SbSlider Label="Opacity" @bind-Value="opacity" Min="0" Max="100" />
<SbNumberField Label="Border Radius" @bind-Value="borderRadius" />
</SbInspectorSection>
<SbInspectorSection Title="Typography" DefaultExpanded="false">
<SbSelect Label="Font Family" @bind-Value="fontFamily" Items="@fonts" />
<SbNumberField Label="Font Size" @bind-Value="fontSize" />
<SbColorPicker Label="Color" @bind-Value="textColor" />
</SbInspectorSection>
</Sections>
</SbInspectorPanel>
```
### With Header Actions
```razor
<SbInspectorPanel Title="Component Settings">
<HeaderActions>
<SbIconButton Icon="refresh" Size="SbSize.Sm" OnClick="Reset" />
<SbIconButton Icon="copy" Size="SbSize.Sm" OnClick="CopyStyles" />
</HeaderActions>
<ChildContent>
<!-- Properties -->
</ChildContent>
</SbInspectorPanel>
```
### Controlled Collapse
```razor
<SbInspectorPanel Title="Details"
Collapsible="true"
Collapsed="@isCollapsed"
CollapsedChanged="@(v => isCollapsed = v)">
<p>Panel content here</p>
</SbInspectorPanel>
<SbButton OnClick="() => isCollapsed = !isCollapsed">
@(isCollapsed ? "Expand" : "Collapse")
</SbButton>
@code {
private bool isCollapsed = false;
}
```
### Non-Collapsible Panel
```razor
<SbInspectorPanel Title="Required Settings" Collapsible="false">
<SbTextField Label="ID" @bind-Value="elementId" Required="true" />
</SbInspectorPanel>
```
### Visual Builder Sidebar
```razor
<aside class="builder-sidebar">
@if (selectedElement != null)
{
<SbInspectorPanel Title="@selectedElement.Type">
<HeaderActions>
<SbIconButton Icon="trash"
Size="SbSize.Sm"
OnClick="DeleteElement" />
</HeaderActions>
<Sections>
<SbInspectorSection Title="Content">
@switch (selectedElement.Type)
{
case "text":
<SbTextArea Label="Text" @bind-Value="selectedElement.Content" />
break;
case "image":
<SbTextField Label="Image URL" @bind-Value="selectedElement.Src" />
<SbTextField Label="Alt Text" @bind-Value="selectedElement.Alt" />
break;
}
</SbInspectorSection>
<SbInspectorSection Title="Style">
<SbPropertyGrid Properties="@GetStyleProperties()"
OnPropertyChanged="UpdateStyle" />
</SbInspectorSection>
</Sections>
</SbInspectorPanel>
}
else
{
<SbEmptyState Title="No Selection"
Description="Select an element to edit its properties" />
}
</aside>
```
### Multiple Panels
```razor
<div class="inspector-container">
<SbInspectorPanel Title="Transform" Collapsed="false">
<SbNumberField Label="X" @bind-Value="x" />
<SbNumberField Label="Y" @bind-Value="y" />
</SbInspectorPanel>
<SbInspectorPanel Title="Fill" Collapsed="true">
<SbColorPicker Label="Color" @bind-Value="fillColor" />
</SbInspectorPanel>
<SbInspectorPanel Title="Stroke" Collapsed="true">
<SbColorPicker Label="Color" @bind-Value="strokeColor" />
<SbNumberField Label="Width" @bind-Value="strokeWidth" />
</SbInspectorPanel>
</div>
```
@@ -0,0 +1,143 @@
# SbInspectorSection
A collapsible section within an SbInspectorPanel. Groups related properties together with an expandable/collapsible header.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Title | string | "" | Section title |
| DefaultExpanded | bool | true | Initial expanded state |
| Class | string? | null | Additional CSS classes |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ChildContent | RenderFragment? | Section content |
| Badge | RenderFragment? | Optional badge shown next to title |
## CSS Classes
- `sb-inspector-section` - Base class
- `sb-inspector-section--expanded` - Expanded state
- `sb-inspector-section__header` - Clickable header
- `sb-inspector-section__chevron` - Expand/collapse indicator
- `sb-inspector-section__title` - Title text
- `sb-inspector-section__badge` - Badge container
- `sb-inspector-section__content` - Content area
## Examples
### Basic Section
```razor
<SbInspectorSection Title="Layout">
<SbNumberField Label="Width" @bind-Value="width" />
<SbNumberField Label="Height" @bind-Value="height" />
</SbInspectorSection>
```
### Initially Collapsed
```razor
<SbInspectorSection Title="Advanced" DefaultExpanded="false">
<SbTextField Label="Custom CSS" @bind-Value="customCss" />
<SbTextField Label="Custom ID" @bind-Value="customId" />
</SbInspectorSection>
```
### With Badge
```razor
<SbInspectorSection Title="Filters">
<Badge>
<SbBadge Value="@activeFilters" Color="SbColor.Primary" />
</Badge>
<ChildContent>
<SbCheckbox Label="Show Active" @bind-Value="showActive" />
<SbCheckbox Label="Show Completed" @bind-Value="showCompleted" />
</ChildContent>
</SbInspectorSection>
```
### Multiple Sections in Panel
```razor
<SbInspectorPanel Title="Element Properties">
<Sections>
<SbInspectorSection Title="Position">
<SbStack Direction="SbDirection.Row" Gap="SbSpacing.Sm">
<SbNumberField Label="X" @bind-Value="x" />
<SbNumberField Label="Y" @bind-Value="y" />
</SbStack>
</SbInspectorSection>
<SbInspectorSection Title="Size">
<SbStack Direction="SbDirection.Row" Gap="SbSpacing.Sm">
<SbNumberField Label="W" @bind-Value="width" />
<SbNumberField Label="H" @bind-Value="height" />
</SbStack>
<SbCheckbox Label="Lock Aspect Ratio" @bind-Value="lockRatio" />
</SbInspectorSection>
<SbInspectorSection Title="Appearance" DefaultExpanded="true">
<SbColorPicker Label="Fill" @bind-Value="fillColor" />
<SbSlider Label="Opacity" @bind-Value="opacity" Min="0" Max="100" />
</SbInspectorSection>
<SbInspectorSection Title="Effects" DefaultExpanded="false">
<Badge>
@if (hasEffects)
{
<span class="effect-indicator">●</span>
}
</Badge>
<ChildContent>
<SbSlider Label="Blur" @bind-Value="blur" Min="0" Max="20" />
<SbSlider Label="Shadow" @bind-Value="shadow" Min="0" Max="50" />
</ChildContent>
</SbInspectorSection>
</Sections>
</SbInspectorPanel>
```
### Typography Inspector
```razor
<SbInspectorSection Title="Typography">
<SbSelect Label="Font" @bind-Value="fontFamily">
<SbSelectOption Value="sans-serif">Sans Serif</SbSelectOption>
<SbSelectOption Value="serif">Serif</SbSelectOption>
<SbSelectOption Value="monospace">Monospace</SbSelectOption>
</SbSelect>
<SbStack Direction="SbDirection.Row" Gap="SbSpacing.Sm">
<SbNumberField Label="Size" @bind-Value="fontSize" />
<SbNumberField Label="Line Height" @bind-Value="lineHeight" />
</SbStack>
<SbSelect Label="Weight" @bind-Value="fontWeight">
<SbSelectOption Value="300">Light</SbSelectOption>
<SbSelectOption Value="400">Regular</SbSelectOption>
<SbSelectOption Value="500">Medium</SbSelectOption>
<SbSelectOption Value="700">Bold</SbSelectOption>
</SbSelect>
<SbColorPicker Label="Color" @bind-Value="textColor" />
</SbInspectorSection>
```
### Dynamic Sections
```razor
@foreach (var group in propertyGroups)
{
<SbInspectorSection Title="@group.Name" DefaultExpanded="@group.IsDefault">
@foreach (var prop in group.Properties)
{
@RenderPropertyEditor(prop)
}
</SbInspectorSection>
}
```
+199
View File
@@ -0,0 +1,199 @@
# SbPropertyGrid
A grid-based property editor that automatically generates input controls based on property definitions. Useful for displaying and editing object properties in a consistent format.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Properties | List<SbPropertyDefinition> | [] | Property definitions to display |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| OnPropertyChanged | EventCallback<SbPropertyChangeEventArgs> | Fired when any property value changes |
## SbPropertyDefinition Class
| Property | Type | Description |
|----------|------|-------------|
| Name | string | Property identifier (used as input id) |
| Label | string | Display label |
| Value | object? | Current value |
| EditorType | SbPropertyEditorType | Type of editor to render |
| Required | bool | Whether property is required |
| Options | SbPropertyOption[]? | Options for Select editor type |
## SbPropertyEditorType Enum
| Value | Description |
|-------|-------------|
| Text | Text input |
| Number | Numeric input |
| Checkbox | Boolean checkbox |
| Select | Dropdown select |
| Color | Color picker |
## SbPropertyOption Class
| Property | Type | Description |
|----------|------|-------------|
| Value | string | Option value |
| Label | string | Display label |
## SbPropertyChangeEventArgs Class
| Property | Type | Description |
|----------|------|-------------|
| Property | SbPropertyDefinition | Changed property definition |
| NewValue | object? | New property value |
## CSS Classes
- `sb-property-grid` - Base class
- `sb-property-grid__row` - Property row
- `sb-property-grid__label` - Property label
- `sb-property-grid__required` - Required indicator
- `sb-property-grid__value` - Value container
- `sb-property-grid__input` - Text/number input
- `sb-property-grid__checkbox` - Checkbox input
- `sb-property-grid__select` - Select input
- `sb-property-grid__color` - Color input
## Examples
### Basic Property Grid
```razor
<SbPropertyGrid Properties="@properties" OnPropertyChanged="HandleChange" />
@code {
private List<SbPropertyDefinition> properties = new()
{
new() { Name = "name", Label = "Name", EditorType = SbPropertyEditorType.Text, Value = "My Element" },
new() { Name = "width", Label = "Width", EditorType = SbPropertyEditorType.Number, Value = 100 },
new() { Name = "visible", Label = "Visible", EditorType = SbPropertyEditorType.Checkbox, Value = true }
};
private void HandleChange(SbPropertyChangeEventArgs args)
{
Console.WriteLine($"{args.Property.Name} = {args.NewValue}");
}
}
```
### With All Editor Types
```razor
<SbPropertyGrid Properties="@allProperties" OnPropertyChanged="HandleChange" />
@code {
private List<SbPropertyDefinition> allProperties = new()
{
new()
{
Name = "title",
Label = "Title",
EditorType = SbPropertyEditorType.Text,
Value = "Hello",
Required = true
},
new()
{
Name = "count",
Label = "Count",
EditorType = SbPropertyEditorType.Number,
Value = 42
},
new()
{
Name = "enabled",
Label = "Enabled",
EditorType = SbPropertyEditorType.Checkbox,
Value = true
},
new()
{
Name = "size",
Label = "Size",
EditorType = SbPropertyEditorType.Select,
Value = "md",
Options = new SbPropertyOption[]
{
new() { Value = "sm", Label = "Small" },
new() { Value = "md", Label = "Medium" },
new() { Value = "lg", Label = "Large" }
}
},
new()
{
Name = "color",
Label = "Color",
EditorType = SbPropertyEditorType.Color,
Value = "#3b82f6"
}
};
}
```
### In Inspector Panel
```razor
<SbInspectorPanel Title="Button Properties">
<SbPropertyGrid Properties="@buttonProperties"
OnPropertyChanged="UpdateButton" />
</SbInspectorPanel>
@code {
private List<SbPropertyDefinition> buttonProperties = new()
{
new() { Name = "text", Label = "Text", EditorType = SbPropertyEditorType.Text, Value = "Click Me" },
new() { Name = "variant", Label = "Variant", EditorType = SbPropertyEditorType.Select,
Value = "primary",
Options = new SbPropertyOption[]
{
new() { Value = "primary", Label = "Primary" },
new() { Value = "secondary", Label = "Secondary" },
new() { Value = "outlined", Label = "Outlined" }
}
},
new() { Name = "disabled", Label = "Disabled", EditorType = SbPropertyEditorType.Checkbox, Value = false }
};
}
```
### Dynamic Properties from Object
```razor
@code {
private Element selectedElement;
private List<SbPropertyDefinition> GetPropertiesFromElement()
{
return new List<SbPropertyDefinition>
{
new() { Name = "id", Label = "ID", EditorType = SbPropertyEditorType.Text,
Value = selectedElement.Id, Required = true },
new() { Name = "x", Label = "X Position", EditorType = SbPropertyEditorType.Number,
Value = selectedElement.X },
new() { Name = "y", Label = "Y Position", EditorType = SbPropertyEditorType.Number,
Value = selectedElement.Y },
new() { Name = "background", Label = "Background", EditorType = SbPropertyEditorType.Color,
Value = selectedElement.BackgroundColor }
};
}
private void HandleChange(SbPropertyChangeEventArgs args)
{
switch (args.Property.Name)
{
case "id": selectedElement.Id = args.NewValue?.ToString(); break;
case "x": selectedElement.X = Convert.ToDouble(args.NewValue); break;
case "y": selectedElement.Y = Convert.ToDouble(args.NewValue); break;
case "background": selectedElement.BackgroundColor = args.NewValue?.ToString(); break;
}
}
}
```
+213
View File
@@ -0,0 +1,213 @@
# SbResizable
A container that allows users to resize its content by dragging edge and corner handles. Useful for resizable panels, widgets, and elements in visual builders.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Width | double | 0 | Current width in pixels |
| Height | double | 0 | Current height in pixels |
| MinWidth | double | 50 | Minimum allowed width |
| MinHeight | double | 50 | Minimum allowed height |
| MaxWidth | double? | null | Maximum allowed width (null = unlimited) |
| MaxHeight | double? | null | Maximum allowed height (null = unlimited) |
| EnableTop | bool | false | Enable top edge resize |
| EnableRight | bool | true | Enable right edge resize |
| EnableBottom | bool | true | Enable bottom edge resize |
| EnableLeft | bool | false | Enable left edge resize |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| WidthChanged | EventCallback<double> | Fired when width changes |
| HeightChanged | EventCallback<double> | Fired when height changes |
| OnResizeStart | EventCallback | Fired when resize begins |
| OnResizeEnd | EventCallback | Fired when resize ends |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ChildContent | RenderFragment? | Content to make resizable |
## Properties
| Property | Type | Description |
|----------|------|-------------|
| IsResizing | bool | Indicates if currently resizing |
## Methods
| Method | Return Type | Description |
|--------|-------------|-------------|
| StopResize() | Task | Programmatically stops resize operation |
## CSS Classes
- `sb-resizable` - Base class
- `sb-resizable--resizing` - During resize
- `sb-resizable__handle` - Base handle class
- `sb-resizable__handle--top` - Top edge handle
- `sb-resizable__handle--right` - Right edge handle
- `sb-resizable__handle--bottom` - Bottom edge handle
- `sb-resizable__handle--left` - Left edge handle
- `sb-resizable__handle--top-right` - Top-right corner handle
- `sb-resizable__handle--bottom-right` - Bottom-right corner handle
- `sb-resizable__handle--bottom-left` - Bottom-left corner handle
- `sb-resizable__handle--top-left` - Top-left corner handle
## Examples
### Basic Resizable
```razor
<SbResizable @bind-Width="width" @bind-Height="height">
<div class="resizable-content">
Resize me! (@width x @height)
</div>
</SbResizable>
@code {
private double width = 200;
private double height = 150;
}
```
### All Directions
```razor
<SbResizable @bind-Width="width"
@bind-Height="height"
EnableTop="true"
EnableRight="true"
EnableBottom="true"
EnableLeft="true">
<div class="content">
Resizable from all sides and corners
</div>
</SbResizable>
```
### With Constraints
```razor
<SbResizable @bind-Width="width"
@bind-Height="height"
MinWidth="100"
MinHeight="100"
MaxWidth="500"
MaxHeight="400">
<div class="constrained-box">
Min: 100x100, Max: 500x400
</div>
</SbResizable>
```
### Horizontal Only
```razor
<SbResizable @bind-Width="width"
@bind-Height="height"
EnableRight="true"
EnableLeft="true"
EnableTop="false"
EnableBottom="false">
<div class="horizontal-resize">
Horizontal resize only
</div>
</SbResizable>
```
### Vertical Only
```razor
<SbResizable @bind-Width="width"
@bind-Height="height"
EnableRight="false"
EnableLeft="false"
EnableTop="true"
EnableBottom="true">
<div class="vertical-resize">
Vertical resize only
</div>
</SbResizable>
```
### With Resize Events
```razor
<SbResizable @bind-Width="width"
@bind-Height="height"
OnResizeStart="HandleResizeStart"
OnResizeEnd="HandleResizeEnd">
<div class="@(isResizing ? "resizing" : "")">
@(isResizing ? "Resizing..." : "Ready")
</div>
</SbResizable>
@code {
private double width = 200, height = 200;
private bool isResizing;
private void HandleResizeStart()
{
isResizing = true;
}
private void HandleResizeEnd()
{
isResizing = false;
Console.WriteLine($"Final size: {width}x{height}");
}
}
```
### Resizable Panel in Layout
```razor
<div class="layout">
<SbResizable @bind-Width="sidebarWidth"
Height="@double.MaxValue"
MinWidth="200"
MaxWidth="400"
EnableRight="true"
EnableTop="false"
EnableBottom="false"
EnableLeft="false">
<nav class="sidebar">
<h3>Sidebar</h3>
<SbNavMenu Items="@menuItems" />
</nav>
</SbResizable>
<main class="main-content">
Main Content Area
</main>
</div>
@code {
private double sidebarWidth = 280;
}
```
### Resizable Widget
```razor
<SbResizable @bind-Width="widgetWidth"
@bind-Height="widgetHeight"
MinWidth="150"
MinHeight="100"
Class="dashboard-widget">
<SbCard>
<Header>
<SbHeading Level="4">Widget</SbHeading>
</Header>
<ChildContent>
<SbText>Resizable widget content</SbText>
</ChildContent>
</SbCard>
</SbResizable>
```
+198
View File
@@ -0,0 +1,198 @@
# SbSortableList
A generic list component that supports drag-and-drop reordering. Items can be sorted by dragging, with optional remove functionality.
## Type Parameters
| Parameter | Description |
|-----------|-------------|
| TItem | Type of items in the list |
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Items | List<TItem> | [] | List of items to display |
| ItemTemplate | RenderFragment<TItem> | required | Template for rendering each item |
| ShowHandles | bool | true | Whether to show drag handles |
| Removable | bool | false | Whether items can be removed |
| Orientation | SbSortableOrientation | Vertical | List orientation |
| Disabled | bool | false | Whether sorting is disabled |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| ItemsChanged | EventCallback<List<TItem>> | Fired when items are reordered |
| OnItemRemoved | EventCallback<TItem> | Fired when an item is removed |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| ItemTemplate | RenderFragment<TItem> | Required template for each item |
| EmptyTemplate | RenderFragment? | Content shown when list is empty |
## Orientation Options
| Value | Description |
|-------|-------------|
| Vertical | Items stacked vertically (default) |
| Horizontal | Items arranged horizontally |
## CSS Classes
- `sb-sortable-list` - Base class
- `sb-sortable-list--horizontal` - Horizontal orientation
- `sb-sortable-list__item` - Item wrapper
- `sb-sortable-list__item--dragging` - Being dragged
- `sb-sortable-list__item--drop-target` - Drop target
- `sb-sortable-list__handle` - Drag handle container
- `sb-sortable-list__content` - Item content
- `sb-sortable-list__remove` - Remove button
- `sb-sortable-list__empty` - Empty state container
## Examples
### Basic Sortable List
```razor
<SbSortableList Items="@items" @bind-Items="items">
<ItemTemplate Context="item">
<span>@item.Name</span>
</ItemTemplate>
</SbSortableList>
@code {
private List<Item> items = new()
{
new() { Id = 1, Name = "First Item" },
new() { Id = 2, Name = "Second Item" },
new() { Id = 3, Name = "Third Item" }
};
}
```
### With Remove Button
```razor
<SbSortableList Items="@tasks"
@bind-Items="tasks"
Removable="true"
OnItemRemoved="HandleRemove">
<ItemTemplate Context="task">
<SbCheckbox Label="@task.Title" @bind-Value="task.IsComplete" />
</ItemTemplate>
</SbSortableList>
@code {
private List<Task> tasks = new() { /* ... */ };
private void HandleRemove(Task task)
{
Console.WriteLine($"Removed: {task.Title}");
}
}
```
### Without Handles
```razor
<SbSortableList Items="@items" @bind-Items="items" ShowHandles="false">
<ItemTemplate Context="item">
<SbCard>
<ChildContent>@item.Content</ChildContent>
</SbCard>
</ItemTemplate>
</SbSortableList>
```
### Horizontal Orientation
```razor
<SbSortableList Items="@tabs"
@bind-Items="tabs"
Orientation="SbSortableOrientation.Horizontal">
<ItemTemplate Context="tab">
<SbChip>@tab.Label</SbChip>
</ItemTemplate>
</SbSortableList>
```
### With Empty Template
```razor
<SbSortableList Items="@items" @bind-Items="items" Removable="true">
<ItemTemplate Context="item">
<div class="list-item">@item.Name</div>
</ItemTemplate>
<EmptyTemplate>
<SbEmptyState IconText="📝"
Title="No items"
Description="Add some items to get started" />
</EmptyTemplate>
</SbSortableList>
```
### Disabled State
```razor
<SbSortableList Items="@items"
@bind-Items="items"
Disabled="@isLocked">
<ItemTemplate Context="item">
<span>@item.Name</span>
</ItemTemplate>
</SbSortableList>
<SbSwitch Label="Lock Order" @bind-Value="isLocked" />
```
### Task Prioritization
```razor
<SbCard>
<Header>
<SbHeading Level="4">Priority Tasks</SbHeading>
</Header>
<ChildContent>
<SbSortableList Items="@priorityTasks"
@bind-Items="priorityTasks"
Removable="true"
OnItemRemoved="ArchiveTask">
<ItemTemplate Context="task">
<div class="task-item">
<span class="priority-badge">#@(priorityTasks.IndexOf(task) + 1)</span>
<span class="task-title">@task.Title</span>
<SbStatusPill Status="@GetStatus(task)" />
</div>
</ItemTemplate>
<EmptyTemplate>
<SbEmptyState Title="All caught up!"
Description="No priority tasks remaining" />
</EmptyTemplate>
</SbSortableList>
</ChildContent>
</SbCard>
```
### Layer Ordering
```razor
<SbInspectorPanel Title="Layers">
<SbSortableList Items="@layers" @bind-Items="layers">
<ItemTemplate Context="layer">
<div class="layer-item">
<SbIcon Name="@(layer.Visible ? "eye" : "eye-off")"
Size="SbSize.Sm"
@onclick="() => layer.Visible = !layer.Visible" />
<span>@layer.Name</span>
<SbIcon Name="@(layer.Locked ? "lock" : "unlock")"
Size="SbSize.Sm"
@onclick="() => layer.Locked = !layer.Locked" />
</div>
</ItemTemplate>
</SbSortableList>
</SbInspectorPanel>
```
+204
View File
@@ -0,0 +1,204 @@
# SbSplitPane
A resizable split container that divides space between two panes. The divider can be dragged to adjust the split ratio.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| Orientation | SbSplitOrientation | Horizontal | Split direction |
| SplitPosition | double | 50 | Initial split position (percentage) |
| MinFirstPane | double | 10 | Minimum first pane size (percentage) |
| MinSecondPane | double | 10 | Minimum second pane size (percentage) |
| Collapsible | bool | false | Whether panes can be collapsed (reserved) |
| Class | string? | null | Additional CSS classes |
## Events
| Event | Type | Description |
|-------|------|-------------|
| SplitPositionChanged | EventCallback<double> | Fired when split position changes |
## Templates / Slots
| Slot | Type | Description |
|------|------|-------------|
| FirstPane | RenderFragment? | Content for the first (left/top) pane |
| SecondPane | RenderFragment? | Content for the second (right/bottom) pane |
## Orientation Options
| Value | Description |
|-------|-------------|
| Horizontal | Left/right split (vertical divider) |
| Vertical | Top/bottom split (horizontal divider) |
## CSS Classes
- `sb-split-pane` - Base class
- `sb-split-pane--horizontal` - Horizontal orientation
- `sb-split-pane--vertical` - Vertical orientation
- `sb-split-pane__panel` - Panel container
- `sb-split-pane__panel--first` - First panel
- `sb-split-pane__panel--second` - Second panel
- `sb-split-pane__divider` - Divider element
- `sb-split-pane__divider--dragging` - During resize
- `sb-split-pane__divider-handle` - Visual handle
## Examples
### Basic Horizontal Split
```razor
<SbSplitPane>
<FirstPane>
<div class="panel">Left Panel</div>
</FirstPane>
<SecondPane>
<div class="panel">Right Panel</div>
</SecondPane>
</SbSplitPane>
```
### Vertical Split
```razor
<SbSplitPane Orientation="SbSplitOrientation.Vertical">
<FirstPane>
<div class="panel">Top Panel</div>
</FirstPane>
<SecondPane>
<div class="panel">Bottom Panel</div>
</SecondPane>
</SbSplitPane>
```
### Custom Split Position
```razor
<SbSplitPane SplitPosition="30">
<FirstPane>
<div class="sidebar">Sidebar (30%)</div>
</FirstPane>
<SecondPane>
<div class="main">Main Content (70%)</div>
</SecondPane>
</SbSplitPane>
```
### With Minimum Sizes
```razor
<SbSplitPane MinFirstPane="20" MinSecondPane="30">
<FirstPane>
<div class="panel">Min 20%</div>
</FirstPane>
<SecondPane>
<div class="panel">Min 30%</div>
</SecondPane>
</SbSplitPane>
```
### Controlled Split Position
```razor
<SbSplitPane SplitPosition="@splitPosition"
SplitPositionChanged="@(v => splitPosition = v)">
<FirstPane>
<div class="panel">First (@splitPosition.ToString("F0")%)</div>
</FirstPane>
<SecondPane>
<div class="panel">Second (@((100 - splitPosition).ToString("F0"))%)</div>
</SecondPane>
</SbSplitPane>
<SbButton OnClick="() => splitPosition = 50">Reset to 50/50</SbButton>
@code {
private double splitPosition = 50;
}
```
### IDE-Style Layout
```razor
<div class="ide-layout">
<SbSplitPane SplitPosition="20" MinFirstPane="15" MinSecondPane="50">
<FirstPane>
<SbInspectorPanel Title="Explorer">
<SbTreeView Items="@files" />
</SbInspectorPanel>
</FirstPane>
<SecondPane>
<SbSplitPane Orientation="SbSplitOrientation.Vertical"
SplitPosition="70">
<FirstPane>
<div class="editor">
Code Editor Area
</div>
</FirstPane>
<SecondPane>
<SbTabs>
<SbTab Title="Terminal">Terminal output...</SbTab>
<SbTab Title="Problems">No problems detected</SbTab>
<SbTab Title="Output">Build output...</SbTab>
</SbTabs>
</SecondPane>
</SbSplitPane>
</SecondPane>
</SbSplitPane>
</div>
```
### Nested Splits
```razor
<SbSplitPane SplitPosition="25">
<FirstPane>
<nav class="left-sidebar">Navigation</nav>
</FirstPane>
<SecondPane>
<SbSplitPane SplitPosition="75">
<FirstPane>
<main class="content">Main Content</main>
</FirstPane>
<SecondPane>
<aside class="right-sidebar">Details Panel</aside>
</SecondPane>
</SbSplitPane>
</SecondPane>
</SbSplitPane>
```
### Email Client Layout
```razor
<SbSplitPane SplitPosition="30">
<FirstPane>
<div class="email-list">
@foreach (var email in emails)
{
<div class="email-item" @onclick="() => SelectEmail(email)">
<strong>@email.Subject</strong>
<span>@email.From</span>
</div>
}
</div>
</FirstPane>
<SecondPane>
@if (selectedEmail != null)
{
<div class="email-content">
<h2>@selectedEmail.Subject</h2>
<p>From: @selectedEmail.From</p>
<SbDivider />
<div>@selectedEmail.Body</div>
</div>
}
else
{
<SbEmptyState Title="No email selected" />
}
</SecondPane>
</SbSplitPane>
```