UDP Visor
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 12s

This commit is contained in:
2026-06-30 16:26:31 +03:00
parent 7aa990a12b
commit 38a825b50b
34 changed files with 1181 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.32916.344
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Visor", "Visor\Visor.csproj", "{A1073A31-9C1C-4B5F-8752-F4397F84E210}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1073A31-9C1C-4B5F-8752-F4397F84E210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1073A31-9C1C-4B5F-8752-F4397F84E210}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1073A31-9C1C-4B5F-8752-F4397F84E210}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1073A31-9C1C-4B5F-8752-F4397F84E210}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C59BEA9D-058E-406A-BE53-A3BC184DCF72}
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
{
"servers": {
"blazorise-docs": {
"type": "http",
"url": "https://mcp.blazorise.com/mcp"
}
}
}
+47
View File
@@ -0,0 +1,47 @@
<Blazorise.ThemeProvider Theme="@theme">
<Router AppAssembly="typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Visor.Layouts.MainLayout)" />
</Found>
<NotFound>
<p>Sorry, there's nothing at this address.</p>
</NotFound>
</Router>
<MessageProvider />
<PageProgressProvider />
</Blazorise.ThemeProvider>
@code {
private Theme theme = new()
{
BarOptions = new()
{
HorizontalHeight = "72px"
},
ColorOptions = new()
{
Primary = "#0288D1",
Secondary = "#A65529",
Success = "#23C02E",
Info = "#9BD8FE",
Warning = "#F8B86C",
Danger = "#F95741",
Light = "#F0F0F0",
Dark = "#535353",
},
BackgroundOptions = new()
{
Primary = "#0288D1",
Secondary = "#A65529",
Success = "#23C02E",
Info = "#9BD8FE",
Warning = "#F8B86C",
Danger = "#F95741",
Light = "#F0F0F0",
Dark = "#535353",
},
InputOptions = new()
{
CheckColor = "#0288D1",
}
};
}
@@ -0,0 +1,4 @@
@using System.Reflection
<Bar Padding="Padding.Is1" Background="Background.Dark">
<Heading TextAlignment="TextAlignment.Center" Width="Width.Is100" TextColor="TextColor.White" Size="HeadingSize.Is6">@($"{Assembly.GetExecutingAssembly().GetName().Name} v{Assembly.GetExecutingAssembly().GetName().Version}")</Heading>
</Bar>
@@ -0,0 +1,56 @@
<Bar Breakpoint="Breakpoint.Desktop" NavigationBreakpoint="Breakpoint.Tablet" ThemeContrast="ThemeContrast.Dark"
Mode="BarMode.VerticalInline" CollapseMode="BarCollapseMode.Small">
<BarToggler />
<BarBrand>
<BarItem>
<BarLink To="">
<BarIcon IconName="customIcon" />
Visor
</BarLink>
</BarItem>
</BarBrand>
<BarMenu>
<BarStart>
<BarItem>
<BarLink To="/">
<BarIcon IconName="IconName.Dashboard" />
Dashboard
</BarLink>
</BarItem>
<BarItem>
<BarDropdown @bind-Visible="pagesBarVisible">
<BarDropdownToggle>
<BarIcon IconName="IconName.Edit" />
Pages
</BarDropdownToggle>
<BarDropdownMenu>
<BarDropdownItem To="/simple-form">Simple Form</BarDropdownItem>
</BarDropdownMenu>
<BarDropdownMenu>
<BarDropdownItem To="/simple-datagrid">Simple DataGrid</BarDropdownItem>
</BarDropdownMenu>
</BarDropdown>
</BarItem>
</BarStart>
<BarEnd>
<BarItem>
<BarDropdown>
<BarDropdownToggle>
<BarIcon IconName="IconName.Smartphone" />
Apps
</BarDropdownToggle>
<BarDropdownMenu>
<BarDropdownItem To="/apps/todo">
Todo
</BarDropdownItem>
</BarDropdownMenu>
</BarDropdown>
</BarItem>
</BarEnd>
</BarMenu>
</Bar>
@code {
private bool pagesBarVisible = true;
RenderFragment customIcon =@<img src="/brand-logo.png" style="width:32px; height: 32px" />;
}
@@ -0,0 +1,26 @@
<div>
@foreach ( var color in ThemeColors.Items.Values )
{
<div @key="color.Key">
@foreach ( var shade in color.Shades.Values )
{
var temp = shade.Value;
<div @key="shade.Key" class="demo-theme-color-item" style="background: @temp" @onclick="@(()=>OnThemeColorSelect(temp))"></div>
}
</div>
}
</div>
@code{
[Parameter]
public string? Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
Task OnThemeColorSelect( string value )
{
Value = value;
return ValueChanged.InvokeAsync(value);
}
}
@@ -0,0 +1,16 @@
.demo-theme-color-item {
display: table-cell;
width: 36px;
height: 36px;
vertical-align: top;
text-align: center;
color: white;
cursor: pointer;
}
.demo-theme-color-item .material-icons {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
+163
View File
@@ -0,0 +1,163 @@
@using Blazorise.Localization
<Bar @bind-Visible="@topbarVisible" Breakpoint="Breakpoint.Desktop" Background="Background.Primary" ThemeContrast="ThemeContrast.Light">
<BarBrand>
<BarItem>
<BarLink To="">
<BarIcon Margin="Margin.Is2.FromEnd" IconName="IconName.Dashboard" />
Visor
</BarLink>
</BarItem>
</BarBrand>
<BarToggler />
<BarMenu>
<BarStart>
<BarItem>
<BarLink To="/">Home</BarLink>
</BarItem>
<BarItem>
<BarLink To="https://blazorise.com/docs/">Documentation</BarLink>
</BarItem>
<BarItem>
<BarDropdown>
<BarDropdownToggle>More</BarDropdownToggle>
<BarDropdownMenu>
<BarDropdownItem To="https://blazorise.com/docs/start/">
Quick-Start Guide
</BarDropdownItem>
<BarDropdownDivider />
<BarDropdownItem To="https://blazorise.com/docs/usage/">
Usage
</BarDropdownItem>
</BarDropdownMenu>
</BarDropdown>
</BarItem>
<BarItem>
<BarDropdown>
<BarDropdownToggle>Layout</BarDropdownToggle>
<BarDropdownMenu>
<BarDropdownItem Clicked="@(()=>OnLayoutTypeChecked("fixed-header"))">
@if ( LayoutType == "fixed-header" )
{
<Icon Name="IconName.CheckCircle" TextColor="TextColor.Success" />
}
Fixed Header
</BarDropdownItem>
<BarDropdownItem Clicked="@(()=>OnLayoutTypeChecked("fixed-header-footer-only"))">
@if ( LayoutType == "fixed-header-footer-only" )
{
<Icon Name="IconName.CheckCircle" TextColor="TextColor.Success" />
}
Fixed Header and Footer only
</BarDropdownItem>
<BarDropdownItem Clicked="@(()=>OnLayoutTypeChecked("sider-with-header-on-top"))">
@if ( LayoutType == "sider-with-header-on-top" )
{
<Icon Name="IconName.CheckCircle" TextColor="TextColor.Success" />
}
Sider with Header on top
</BarDropdownItem>
</BarDropdownMenu>
</BarDropdown>
</BarItem>
</BarStart>
<BarEnd>
<BarItem>
<BarDropdown RightAligned>
<BarDropdownToggle><Icon Name="IconName.Language" /></BarDropdownToggle>
<BarDropdownMenu>
@foreach ( var cultureInfo in LocalizationService!.AvailableCultures )
{
<BarDropdownItem @key="@cultureInfo.Name" Clicked="@(()=>SelectCulture(cultureInfo.Name))">
@if ( cultureInfo.IsNeutralCulture )
{
@cultureInfo.EnglishName
}
else
{
@cultureInfo.Parent.EnglishName
}
</BarDropdownItem>
}
</BarDropdownMenu>
</BarDropdown>
</BarItem>
<BarItem>
<BarDropdown RightAligned>
<BarDropdownToggle><Icon Name="IconName.Tint" /> Theme</BarDropdownToggle>
<BarDropdownMenu Style="padding: 15px; min-width:550px;">
<Row>
<Column Margin="Margin.Is2.FromBottom">
<Field>
<Switch TValue="bool" Value="@(Theme?.Enabled == true)" ValueChanged="@ThemeEnabledChanged">Theme enabled</Switch>
</Field>
</Column>
</Row>
<Row>
<Column Margin="Margin.Is2.FromBottom">
<Field>
<Check TValue="bool" Value="@(Theme?.IsGradient == true)" ValueChanged="@ThemeGradientChanged">Gradient colors</Check>
</Field>
<Field>
<Check TValue="bool" Value="@(Theme?.IsRounded == true)" ValueChanged="@ThemeRoundedChanged">Rounded elements</Check>
</Field>
</Column>
</Row>
<Row>
<Column>
<Container Fluid>
<ThemeColorSelector Value="@(Theme?.ColorOptions?.Primary)" ValueChanged="@ThemeColorChanged"></ThemeColorSelector>
</Container>
</Column>
</Row>
</BarDropdownMenu>
</BarDropdown>
</BarItem>
<BarItem>
<BarLink To="https://github.com/Megabit/Blazorise">GitHub</BarLink>
</BarItem>
</BarEnd>
</BarMenu>
</Bar>
@code {
protected override async Task OnInitializedAsync()
{
await SelectCulture( "en-US" );
await base.OnInitializedAsync();
}
Task SelectCulture( string name )
{
LocalizationService!.ChangeLanguage( name );
return Task.CompletedTask;
}
private bool topbarVisible = false;
Task OnLayoutTypeChecked( string layoutType )
{
LayoutType = layoutType;
return LayoutTypeChanged.InvokeAsync( layoutType );
}
[Parameter] public EventCallback<bool> ThemeEnabledChanged { get; set; }
[Parameter] public EventCallback<bool> ThemeGradientChanged { get; set; }
[Parameter] public EventCallback<bool> ThemeRoundedChanged { get; set; }
[Parameter] public EventCallback<string> ThemeColorChanged { get; set; }
[Parameter] public string? LayoutType { get; set; }
[Parameter] public EventCallback<string> LayoutTypeChanged { get; set; }
[Inject] protected ITextLocalizerService? LocalizationService { get; set; }
[CascadingParameter] protected Theme? Theme { get; set; }
}
@@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Components;
namespace Visor.Components.TodoApp
{
public abstract class BaseTodoItems : ComponentBase
{
protected Validations? validations;
protected string? description;
protected Filter filter = Filter.All;
protected List<Todo> todos = new()
{
new() { Description = "Buy milk" },
new() { Description = "Call John regarding the meeting" },
new() { Description = "Walk a dog" },
};
protected IEnumerable<Todo> Todos
{
get
{
var query = from t in todos select t;
if (filter == Filter.Active)
query = from q in query where !q.Completed select q;
if (filter == Filter.Completed)
query = from q in query where q.Completed select q;
return query;
}
}
protected void SetFilter(Filter filter)
{
this.filter = filter;
}
protected void OnCheckAll(bool isChecked)
{
todos.ForEach(x => x.Completed = isChecked);
}
protected async Task OnAddTodo()
{
if (await validations!.ValidateAll())
{
todos.Add(new() { Description = description });
description = null;
await validations.ClearAll();
}
}
protected void OnClearCompleted()
{
todos.RemoveAll(x => x.Completed);
filter = Filter.All;
}
protected Task OnTodoStatusChanged(bool isChecked)
{
return InvokeAsync(StateHasChanged);
}
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Visor.Components.TodoApp
{
public enum Filter
{
All,
Active,
Completed,
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Visor.Components.TodoApp
{
public class Todo
{
public bool Completed { get; set; }
public string? Description { get; set; }
}
}
@@ -0,0 +1,25 @@
@if (Todo is not null)
{
<ListGroupItem>
<Field Horizontal Padding="Padding.IsAuto.OnAll">
<FieldBody ColumnSize="ColumnSize.Is1">
<Check TValue="bool" Value="@Todo.Completed" ValueChanged="@OnCheckedChanged"></Check>
</FieldBody>
<FieldBody ColumnSize="ColumnSize.Is11">
@Todo.Description
</FieldBody>
</Field>
</ListGroupItem>
}
@code {
Task OnCheckedChanged(bool isChecked)
{
Todo!.Completed = isChecked;
return StatusChanged?.Invoke(isChecked) ?? Task.CompletedTask;
}
[Parameter] public Todo? Todo { get; set; }
[Parameter] public Func<bool, Task>? StatusChanged { get; set; }
}
@@ -0,0 +1,57 @@
@inherits BaseTodoItems
<Container Fluid>
<Row>
<Column>
<Card>
<CardHeader Padding="Padding.Is1.FromBottom">
<CardTitle Size="HeadingSize.Is4">Todo List</CardTitle>
</CardHeader>
<CardBody Padding="Padding.Is0.FromBottom">
<Fields>
<Column ColumnSize="ColumnSize.Is1">
<Check TValue="bool" Value="@Todos.All(x=>x.Completed)" ValueChanged="@OnCheckAll">All</Check>
</Column>
<Column ColumnSize="ColumnSize.Is11">
<Addons>
<Addon AddonType="AddonType.Body">
<Validations @ref="validations" Mode="ValidationMode.Manual">
<Validation Validator="@ValidationRule.IsNotEmpty">
<TextInput @bind-Value="@description" Placeholder="What needs to be done?"></TextInput>
</Validation>
</Validations>
</Addon>
<Addon AddonType="AddonType.End">
<Button Color="Color.Primary" Clicked="@OnAddTodo">
<Icon Name="IconName.Add" />Add
</Button>
</Addon>
</Addons>
</Column>
</Fields>
</CardBody>
<CardBody Padding="Padding.Is0.OnY">
<ListGroup Flush>
@foreach ( var todo in Todos )
{
<TodoItem Todo="@todo" StatusChanged="@OnTodoStatusChanged" />
}
</ListGroup>
</CardBody>
<CardFooter Padding="Padding.Is3.FromBottom">
<Field Horizontal>
<FieldBody ColumnSize="ColumnSize.Is10">
<Buttons Role="ButtonsRole.Addons">
<Button Color="Color.Info" Clicked="@(() => SetFilter( Filter.All ))" Active="@(filter == Filter.All)">All</Button>
<Button Color="Color.Info" Clicked="@(() => SetFilter( Filter.Active ))" Active="@(filter == Filter.Active)">Active</Button>
<Button Color="Color.Info" Clicked="@(() => SetFilter( Filter.Completed ))" Active="@(filter == Filter.Completed)">Completed</Button>
</Buttons>
</FieldBody>
<FieldBody ColumnSize="ColumnSize.Is2">
<Button Color="Color.Warning" Float="Float.End" Clicked="@OnClearCompleted" Display="@(todos.Any(x=>x.Completed) ? Display.Always : Display.None)">Clear Completed</Button>
</FieldBody>
</Field>
</CardFooter>
</Card>
</Column>
</Row>
</Container>
+2
View File
@@ -0,0 +1,2 @@
global using Blazorise;
global using Blazorise.DataGrid;
+69
View File
@@ -0,0 +1,69 @@
@using Visor.Components.Layout
@inherits LayoutComponentBase
@if ( layoutType == "fixed-header" )
{
<Layout Sider>
<LayoutSider>
<LayoutSiderContent>
<SideMenu />
</LayoutSiderContent>
</LayoutSider>
<Layout>
<LayoutHeader Fixed>
<TopMenu ThemeEnabledChanged="@OnThemeEnabledChanged"
ThemeGradientChanged="@OnThemeGradientChanged"
ThemeRoundedChanged="@OnThemeRoundedChanged"
ThemeColorChanged="@OnThemeColorChanged"
@bind-LayoutType="@layoutType" />
</LayoutHeader>
<LayoutContent Padding="Padding.Is4.OnX.Is4.FromTop">
@Body
</LayoutContent>
</Layout>
</Layout>
}
else if ( layoutType == "fixed-header-footer-only" )
{
<Layout>
<LayoutHeader Fixed>
<TopMenu ThemeEnabledChanged="@OnThemeEnabledChanged"
ThemeGradientChanged="@OnThemeGradientChanged"
ThemeRoundedChanged="@OnThemeRoundedChanged"
ThemeColorChanged="@OnThemeColorChanged"
@bind-LayoutType="@layoutType" />
</LayoutHeader>
<LayoutContent Padding="Padding.Is4.OnX.Is4.FromTop">
@Body
</LayoutContent>
<LayoutFooter Fixed>
<FooterMenu></FooterMenu>
</LayoutFooter>
</Layout>
}
else if ( layoutType == "sider-with-header-on-top" )
{
<Layout>
<LayoutHeader Fixed>
<TopMenu ThemeEnabledChanged="@OnThemeEnabledChanged"
ThemeGradientChanged="@OnThemeGradientChanged"
ThemeRoundedChanged="@OnThemeRoundedChanged"
ThemeColorChanged="@OnThemeColorChanged"
@bind-LayoutType="@layoutType" />
</LayoutHeader>
<Layout Sider>
<LayoutSider>
<LayoutSiderContent>
<SideMenu />
</LayoutSiderContent>
</LayoutSider>
<Layout>
<LayoutContent Padding="Padding.Is4.OnX.Is4.FromTop">
@Body
</LayoutContent>
</Layout>
</Layout>
</Layout>
}
+89
View File
@@ -0,0 +1,89 @@
using Blazorise;
using Blazorise.Localization;
using Microsoft.AspNetCore.Components;
namespace Visor.Layouts
{
public partial class MainLayout
{
[Inject] protected ITextLocalizerService? LocalizationService { get; set; }
[CascadingParameter] protected Theme? Theme { get; set; }
protected string layoutType = "fixed-header";
protected override async Task OnInitializedAsync()
{
await SelectCulture("en-US");
await base.OnInitializedAsync();
}
private Task SelectCulture(string name)
{
LocalizationService!.ChangeLanguage(name);
return Task.CompletedTask;
}
Task OnThemeEnabledChanged(bool value)
{
if (Theme is null)
return Task.CompletedTask;
Theme.Enabled = value;
return InvokeAsync(Theme.ThemeHasChanged);
}
Task OnThemeGradientChanged(bool value)
{
if (Theme is null)
return Task.CompletedTask;
Theme.IsGradient = value;
return InvokeAsync(Theme.ThemeHasChanged);
}
Task OnThemeRoundedChanged(bool value)
{
if (Theme is null)
return Task.CompletedTask;
Theme.IsRounded = value;
return InvokeAsync(Theme.ThemeHasChanged);
}
Task OnThemeColorChanged(string value)
{
if (Theme is null)
return Task.CompletedTask;
Theme.ColorOptions ??= new();
Theme.BackgroundOptions ??= new();
Theme.TextColorOptions ??= new();
Theme.ColorOptions.Primary = value;
Theme.BackgroundOptions.Primary = value;
Theme.TextColorOptions.Primary = value;
Theme.InputOptions ??= new();
Theme.InputOptions.CheckColor = value;
Theme.InputOptions.SliderColor = value;
Theme.SpinKitOptions ??= new();
Theme.SpinKitOptions.Color = value;
return InvokeAsync(Theme.ThemeHasChanged);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
@page "/"
@inject IVersionProvider VersionProvider
<Heading Size="HeadingSize.Is1" Margin="Margin.Is3.FromBottom">Blazorise</Heading>
<Paragraph>
Blazorise is a component library built on top of Blazor and CSS frameworks like <Anchor To="https://getbootstrap.com/" Target="Target.Blank">Bootstrap</Anchor>, <Anchor To="https://fluent2.microsoft.design/" Target="Target.Blank">FluentUI2</Anchor>, <Anchor To="https://tailwindcss.com/" Target="Target.Blank">Tailwind</Anchor>, <Anchor To="https://bulma.io/" Target="Target.Blank">Bulma</Anchor>, <Anchor To="https://ant.design/" Target="Target.Blank">Ant Design</Anchor>, and <Anchor To="http://daemonite.github.io/material/" Target="Target.Blank">Material</Anchor>. It can be used to build responsive, single-page web applications.
</Paragraph>
<Alert Color="Color.Info" Visible>
<Paragraph>
This is a Blazorise Starting Template allowing you to quickly get started building your project!
</Paragraph>
<Paragraph>
The following Blazorise packages have been installed for you:
</Paragraph>
<UnorderedList>
<UnorderedListItem>Blazorise @($"v{VersionProvider.MilestoneVersion}")</UnorderedListItem>
<UnorderedListItem>The <Anchor To="https://blazorise.com/docs/usage/bootstrap5">Blazorise Bootstrap5</Anchor> Provider</UnorderedListItem>
<UnorderedListItem>The Blazorise FontAwesome Icon Provider</UnorderedListItem>
<UnorderedListItem>The <Anchor To="https://blazorise.com/docs/extensions/datagrid/getting-started">Blazorise DataGrid</Anchor> extension</UnorderedListItem>
</UnorderedList>
<Paragraph>
However Blazorise has many more extensions at your disposal.
You can find them <Anchor To="https://blazorise.com/docs/extensions" Target="Target.Blank">here</Anchor>.
</Paragraph>
<Paragraph>
Please visit the official <Anchor To="https://bootstrapdemo.blazorise.com/" Target="Target.Blank">Blazorise Demo</Anchor> for component examples.
</Paragraph>
<Paragraph>
Please visit the official <Anchor To="https://blazorise.com/docs/" Target="Target.Blank">Blazorise Documentation</Anchor> to learn more about the available components.
</Paragraph>
</Alert>
+37
View File
@@ -0,0 +1,37 @@
@page
@model Visor.Pages.ErrorModel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Error</title>
</head>
<body>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</body>
</html>
+27
View File
@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;
namespace Visor.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
@@ -0,0 +1,75 @@
@page "/simple-datagrid"
<DataGrid TItem="Book"
Data="data"
Editable
Sortable
SortMode="DataGridSortMode.Single"
ShowPager
ShowPageSizes
Resizable
Responsive
Bordered
Hoverable
Narrow
Groupable
ShowGrouping
ResizeMode="TableResizeMode.Columns"
NewItemCreator="() => new Book(string.Empty, string.Empty, string.Empty, DateOnly.MinValue, string.Empty)">
<DataGridColumns>
<DataGridColumn Field="@nameof(Book.Title)" Caption="Title" Editable></DataGridColumn>
<DataGridColumn Field="@nameof(Book.Author)" Caption="Author" Editable Groupable></DataGridColumn>
<DataGridColumn Field="@nameof(Book.Isbn)" Caption="Isbn" Editable></DataGridColumn>
<DataGridColumn Field="@nameof(Book.PublicationDate)" Caption="Publication Date" Editable></DataGridColumn>
<DataGridColumn Field="@nameof(Book.Publisher)" Caption="Publisher" Editable></DataGridColumn>
<DataGridCommandColumn PreventRowClick>
<NewCommandTemplate>
<Button Color="Color.Success" Clicked="@context.Clicked">@context.LocalizationString</Button>
</NewCommandTemplate>
<EditCommandTemplate>
<Button Color="Color.Primary" Clicked="@context.Clicked">@context.LocalizationString</Button>
</EditCommandTemplate>
<SaveCommandTemplate>
<Button Type="ButtonType.Submit" PreventDefaultOnSubmit Color="Color.Primary" Clicked="@context.Clicked">@context.LocalizationString</Button>
</SaveCommandTemplate>
<DeleteCommandTemplate>
<Button Color="Color.Danger" Clicked="@context.Clicked">@context.LocalizationString</Button>
</DeleteCommandTemplate>
<CancelCommandTemplate>
<Button Color="Color.Secondary" Clicked="@context.Clicked">@context.LocalizationString</Button>
</CancelCommandTemplate>
<ClearFilterCommandTemplate>
<Button Color="Color.Warning" Clicked="@context.Clicked">@context.LocalizationString</Button>
</ClearFilterCommandTemplate>
</DataGridCommandColumn>
</DataGridColumns>
</DataGrid>
@code {
private List<Book> data = new()
{
new("Harry Potter and the Sorcerer's Stone", "J.K. Rowling","9780590353427", new(1997,6,26), "Scholastic Press" ),
new("To Kill a Mockingbird", "Harper Lee", "9780061120084", new(1960,7,11), "J.B. Lippincott & Co." ),
new("The Catcher in the Rye", "J.D. Salinger","9780316769488", new(1951,7,16), "Little, Brown and Company" ),
new("The Great Gatsby", "F. Scott Fitzgerald","9780743273565", new(1925,4,10), "Charles Scribner's Sons" ),
new("The Lord of the Rings", "J.R.R. Tolkien", "9780544003415", new(1954,7,29), "George Allen & Unwin"),
new("The Hobbit", "J.R.R. Tolkien", "9780547928227", new(1937,9,21), "George Allen & Unwin"),
new("The Chronicles of Narnia: The Lion, the Witch, and the Wardrobe", "C.S. Lewis", "9780064471073", new(1950,10,16), "Geoffrey Bles"),
new("Animal Farm", "George Orwell", "978451219811", new(1945,8,17), "Secker & Warburg"),
new("The Adventures of Huckleberry Finn", "Mark Twain", "9780142437254", new(1884,12,10), "Chatto & Windus"),
new("The Diary of a Young Girl", "Anne Frank", "9780385473788", new(1947,6,25), "Doubleday"),
new("The Hunger Games", "Suzanne Collins", "9780439023481", new(2008,9,14), "Scholastic Press"),
new("The Maze Runner", "James Dashner", "9780385737965", new(2009,10,6), "Delacorte Press"),
new("The Secret Life of Bees", "Sue Monk Kidd", "9780670034852", new(2002,5,5), "Viking Press"),
new("The Strange Case of Dr. Jekyll and Mr. Hyde", "Robert Louis Stevenson", "9781593080171", new(1886,1,5), "Longmans, Green and Co"),
new("Harry Potter and the Chamber of Secrets", "J.K. Rowling", "9780439064866", new(1998,7,2), "Scholastic Press"),
new("Harry Potter and the Prisoner of Azkaban", "J.K. Rowling", "9780439136365", new(1999,7,8), "Scholastic Press"),
new("Catching Fire", "Suzanne Collins", "9780439023498", new(2009,9,1), "Scholastic Press"),
new("Mockingjay", "Suzanne Collins", "9780439023528", new(2010,8,24), "Scholastic Press"),
new("The Children of Hurin", "J.R.R. Tolkien", "9780544003422", new(2007,4,17), "Houghton Mifflin Harcourt"),
new("Beren and Luthien", "J.R.R. Tolkien", "9780544116813", new(2017,5,23), "Houghton Mifflin Harcourt")
};
private record Book( string Title, string Author, string Isbn, DateOnly PublicationDate, string Publisher );
}
+124
View File
@@ -0,0 +1,124 @@
@page "/simple-form"
<Row>
<Column ColumnSize="ColumnSize.Is6.OnDesktop">
<Card Margin="Margin.Is4.FromBottom">
<CardHeader>
<CardTitle>Basic Example</CardTitle>
</CardHeader>
<CardBody>
<Validations @ref="validationsBasicExampleRef" Mode="ValidationMode.Auto" ValidateOnLoad="false">
<Validation Validator="@ValidationRule.IsEmail">
<Field>
<FieldLabel>Email address</FieldLabel>
<TextInput Placeholder="Enter email" />
<FieldHelp>We'll never share your email with anyone else.</FieldHelp>
</Field>
</Validation>
<Validation Validator="@ValidatePassword">
<Field>
<FieldLabel>Password</FieldLabel>
<TextInput Role="TextRole.Password" Placeholder="Password" />
</Field>
</Validation>
<Field>
<Check TValue="bool">Remember me?</Check>
</Field>
<Button Color="Color.Primary" Clicked="SubmitBasicExample">Submit</Button>
</Validations>
</CardBody>
</Card>
</Column>
<Column ColumnSize="ColumnSize.Is6.OnDesktop">
<Card Margin="Margin.Is4.FromBottom">
<CardHeader>
<CardTitle>Horizontal Form</CardTitle>
</CardHeader>
<CardBody>
<Validations @ref="validationsHorizontalFormRef" Mode="ValidationMode.Auto" ValidateOnLoad="false">
<Validation Validator="@ValidationRule.IsEmail">
<Field Horizontal>
<FieldLabel ColumnSize="ColumnSize.IsFull.OnTablet.Is3.OnDesktop">Email address</FieldLabel>
<FieldBody ColumnSize="ColumnSize.IsFull.OnTablet.Is9.OnDesktop">
<TextInput Placeholder="Email" />
</FieldBody>
</Field>
</Validation>
<Validation Validator="@ValidatePassword">
<Field Horizontal>
<FieldLabel ColumnSize="ColumnSize.IsFull.OnTablet.Is3.OnDesktop">Password</FieldLabel>
<FieldBody ColumnSize="ColumnSize.IsFull.OnTablet.Is9.OnDesktop">
<TextInput Role="TextRole.Password" Placeholder="Password" @bind-Value="@password" />
</FieldBody>
</Field>
</Validation>
<Validation Validator="@ValidatePassword2">
<Field Horizontal>
<FieldLabel ColumnSize="ColumnSize.IsFull.OnTablet.Is3.OnDesktop">Re Password</FieldLabel>
<FieldBody ColumnSize="ColumnSize.IsFull.OnTablet.Is9.OnDesktop">
<TextInput Role="TextRole.Password" Placeholder="Retype password" />
</FieldBody>
</Field>
</Validation>
<Field Horizontal JustifyContent="JustifyContent.End">
<FieldBody ColumnSize="ColumnSize.Is9.Is3.WithOffset">
<Check TValue="bool">Remember me?</Check>
</FieldBody>
</Field>
<Field Horizontal JustifyContent="JustifyContent.End">
<FieldBody ColumnSize="ColumnSize.Is9.Is3.WithOffset">
<Button Color="Color.Primary" Clicked="SubmitHorizontalForm">Submit</Button>
</FieldBody>
</Field>
</Validations>
</CardBody>
</Card>
</Column>
</Row>
@code {
Validations? validationsBasicExampleRef;
Validations? validationsHorizontalFormRef;
string? password;
void ValidatePassword( ValidatorEventArgs e )
{
e.Status = Convert.ToString( e.Value )?.Length >= 6 ? ValidationStatus.Success : ValidationStatus.Error;
}
void ValidatePassword2( ValidatorEventArgs e )
{
var password2 = Convert.ToString( e.Value );
if ( password2?.Length < 6 )
{
e.Status = ValidationStatus.Error;
e.ErrorText = "Password must be at least 6 characters long!";
}
else if ( password2 != password )
{
e.Status = ValidationStatus.Error;
}
else
{
e.Status = ValidationStatus.Success;
}
}
async Task SubmitBasicExample()
{
if ( await validationsBasicExampleRef!.ValidateAll() )
{
await validationsBasicExampleRef.ClearAll();
}
}
async Task SubmitHorizontalForm()
{
if ( await validationsHorizontalFormRef!.ValidateAll() )
{
await validationsHorizontalFormRef.ClearAll();
}
}
}
+3
View File
@@ -0,0 +1,3 @@
@using Visor.Components.TodoApp
@page "/apps/todo"
<TodoItems />
+8
View File
@@ -0,0 +1,8 @@
@page "/"
@namespace Visor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = "_Layout";
}
<component type="typeof(App)" render-mode="Server" />
+41
View File
@@ -0,0 +1,41 @@
@using Microsoft.AspNetCore.Components.Web
@namespace Visor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Visor</title>
<base href="~/" />
<component type="typeof(HeadOutlet)" render-mode="Server" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<link href="_content/Blazorise.Icons.FontAwesome/v6/css/all.min.css" rel="stylesheet">
<link href="_content/Blazorise/blazorise.min.css?v=2.2.1.0" rel="stylesheet" />
<link href="_content/Blazorise.Bootstrap5/blazorise.bootstrap5.min.css?v=2.2.1.0" rel="stylesheet" />
<link href="Visor.Styles.css" rel="stylesheet" />
<link rel="stylesheet" href="css/blazor-ui.css" />
</head>
<body>
@RenderBody()
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
+41
View File
@@ -0,0 +1,41 @@
using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
AddBlazorise(builder.Services);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
void AddBlazorise(IServiceCollection services)
{
services
.AddBlazorise();
services
.AddBootstrap5Providers()
.AddFontAwesomeIcons();
}
@@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:61646",
"sslPort": 44332
}
},
"profiles": {
"Visor": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7297;http://localhost:5255",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>2.2.1</Version>
<Authors>Megabit</Authors>
<Company>Megabit</Company>
<Description>
This is a Blazorise Template for a Blazor Server Application.
Blazorise is a component library built on top of Blazor and CSS frameworks like Bootstrap, FluentUI2, Tailwind, Bulma and Material.
</Description>
<Copyright>Copyright 2018-2026 Megabit</Copyright>
</PropertyGroup>
<ItemGroup>
<None Remove=".mcp.json" />
<None Include=".mcp.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Blazorise" Version="2.2.1" />
<PackageReference Include="Blazorise.Bootstrap5" Version="2.2.1" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="2.2.1" />
<PackageReference Include="Blazorise.DataGrid" Version="2.2.1" />
</ItemGroup>
</Project>
+12
View File
@@ -0,0 +1,12 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Visor
@using Blazorise
@using Blazorise.DataGrid
+9
View File
@@ -0,0 +1,9 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+9
View File
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+28
View File
@@ -0,0 +1,28 @@
#blazor-error-ui {
background: #ffffe0;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, .2);
display: none;
left: 0;
padding: .6rem 1.25rem .7rem 1.25rem;
position: fixed;
right: 0;
z-index: 1000
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: .75rem;
top: .5rem
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB