diff --git a/src/BootstrapBlazor.WebConsole/Pages/Buttons.razor b/src/BootstrapBlazor.WebConsole/Pages/Buttons.razor
index 8b6829ec298b33741d4da661610d10cdbd45a239..a19cb4719724311d4af1c10dcf9f06ae3632bd52 100644
--- a/src/BootstrapBlazor.WebConsole/Pages/Buttons.razor
+++ b/src/BootstrapBlazor.WebConsole/Pages/Buttons.razor
@@ -10,30 +10,30 @@
-
\ No newline at end of file
+
diff --git a/src/BootstrapBlazor.WebConsole/Properties/launchSettings.json b/src/BootstrapBlazor.WebConsole/Properties/launchSettings.json
index 3f8d9b78007e8139fb63b784338306992ba12fa8..e01a67e3198ee8c459e01e62c2770e0c8d1f44c3 100644
--- a/src/BootstrapBlazor.WebConsole/Properties/launchSettings.json
+++ b/src/BootstrapBlazor.WebConsole/Properties/launchSettings.json
@@ -1,16 +1,32 @@
{
"iisSettings": {
"windowsAuthentication": false,
- "anonymousAuthentication": true
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:50853/",
+ "sslPort": 0
+ }
},
"profiles": {
- "BootstrapBlazor.WebConsole": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "launchBrowser": true,
+ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
+ },
+ "Bootstrap.Admin": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
- "applicationUrl": "http://localhost:5000"
+ "applicationUrl": "http://localhost:50853/"
}
}
}
\ No newline at end of file
diff --git a/src/BootstrapBlazor.WebConsole/_Imports.razor b/src/BootstrapBlazor.WebConsole/_Imports.razor
index 0ea699e1e55f73fed27f2ac53dcb0e1fa2d266ab..a6006fd9c0f17df7a6d8134fab616414308832b3 100644
--- a/src/BootstrapBlazor.WebConsole/_Imports.razor
+++ b/src/BootstrapBlazor.WebConsole/_Imports.razor
@@ -7,3 +7,4 @@
@using Microsoft.JSInterop
@using BootstrapBlazor.WebConsole
@using BootstrapBlazor.WebConsole.Shared
+@using BootstrapBlazor.Components
\ No newline at end of file
diff --git a/src/BootstrapBlazor/Components/Button/Button.razor b/src/BootstrapBlazor/Components/Button/Button.razor
new file mode 100644
index 0000000000000000000000000000000000000000..2cb67b857839961b0b0d9d26b2fe450b7143686d
--- /dev/null
+++ b/src/BootstrapBlazor/Components/Button/Button.razor
@@ -0,0 +1,6 @@
+@namespace BootstrapBlazor.Components
+@inherits ButtonBase
+
+
+ @ChildContent
+
diff --git a/src/BootstrapBlazor/Components/Button/ButtonBase.cs b/src/BootstrapBlazor/Components/Button/ButtonBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e78ae061bc1de685a4f5390a9c0ab2b6c2ab8c34
--- /dev/null
+++ b/src/BootstrapBlazor/Components/Button/ButtonBase.cs
@@ -0,0 +1,126 @@
+using BootstrapBlazor.Utils;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Web;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace BootstrapBlazor.Components
+{
+ ///
+ /// Button 按钮组件
+ ///
+ public abstract class ButtonBase : ComponentBase
+ {
+ ///
+ /// 获得/设置 用户自定义属性
+ ///
+ ///
+ [Parameter(CaptureUnmatchedValues = true)]
+ public IDictionary? AdditionalAttributes { get; set; }
+
+ ///
+ /// 获得 按钮样式集合
+ ///
+ ///
+ protected string ClassName => new CssBuilder("btn")
+ .AddClass($"btn-outline-{Color.ToDescriptionString()}", IsOutline)
+ .AddClass($"btn-{Color.ToDescriptionString()}", Color != Color.None && !IsOutline)
+ .AddClass($"btn-{Size.ToDescriptionString()}", Size != Size.None)
+ .AddClass("btn-block", IsBlock)
+ .AddClass("disabled", ButtonType == ButtonType.Link && IsDisabled)
+ .AddClass(Class)
+ .Build();
+
+ ///
+ /// 获得/设置 按钮 disabled 属性
+ ///
+ protected string? Tag { get; set; } = "button";
+
+ ///
+ /// 获得 按钮 disabled 属性
+ ///
+ protected string? Disabled => IsDisabled ? "true" : null;
+
+ ///
+ /// 获得 按钮 tabindex 属性
+ ///
+ protected string? Tab => IsDisabled ? "-1" : null;
+
+ ///
+ /// 获得 按钮类型
+ ///
+ protected string type => ButtonType switch
+ {
+ ButtonType.Submit => "submit",
+ ButtonType.Reset => "reset",
+ _ => "button"
+ };
+
+ ///
+ /// OnClick 事件
+ ///
+ [Parameter] public EventCallback OnClick { get; set; }
+
+ ///
+ /// 获得/设置 按钮颜色
+ ///
+ [Parameter] public Color Color { get; set; } = Color.Primary;
+
+ private ButtonType _buttonType = ButtonType.Button;
+
+ ///
+ ///
+ ///
+ [Parameter]
+ public ButtonType ButtonType
+ {
+ get => _buttonType;
+ set
+ {
+ _buttonType = value;
+ Tag = _buttonType switch
+ {
+ ButtonType.Link => "a",
+ ButtonType.Input => "input",
+ ButtonType.Reset => "input",
+ _ => "button"
+ };
+ }
+ }
+
+ ///
+ ///
+ ///
+ [Parameter] public bool IsOutline { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter] public Size Size { get; set; } = Size.None;
+
+ ///
+ ///
+ ///
+ [Parameter] public bool IsBlock { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter] public string? Value { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter] public bool IsDisabled { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter] public string Class { get; set; } = "";
+
+ ///
+ ///
+ ///
+ [Parameter] public RenderFragment? ChildContent { get; set; }
+ }
+}
diff --git a/src/BootstrapBlazor/Components/Button/ButtonType.cs b/src/BootstrapBlazor/Components/Button/ButtonType.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e190fe0163c87948afe90d3ceda50345a3b57b24
--- /dev/null
+++ b/src/BootstrapBlazor/Components/Button/ButtonType.cs
@@ -0,0 +1,29 @@
+namespace BootstrapBlazor.Components
+{
+ ///
+ /// 按钮类型枚举
+ ///
+ public enum ButtonType
+ {
+ ///
+ /// 正常按钮
+ ///
+ Button,
+ ///
+ /// 提交按钮
+ ///
+ Submit,
+ ///
+ /// Link 按钮
+ ///
+ Link,
+ ///
+ /// 重置按钮
+ ///
+ Reset,
+ ///
+ /// 普通按钮
+ ///
+ Input,
+ }
+}
diff --git a/src/BootstrapBlazor/Components/DynamicElement.cs b/src/BootstrapBlazor/Components/DynamicElement.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3fa2516160dde0b22a609d53dc348ac3b068ec41
--- /dev/null
+++ b/src/BootstrapBlazor/Components/DynamicElement.cs
@@ -0,0 +1,66 @@
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Rendering;
+using System;
+using System.Collections.Generic;
+
+namespace BootstrapBlazor.Components
+{
+ ///
+ /// Renders an element with the specified name and attributes. This is useful
+ /// when you want to combine a set of attributes declared at compile time with
+ /// another set determined at runtime.
+ ///
+ public class DynamicElement : ComponentBase
+ {
+ ///
+ /// Gets or sets the name of the element to render.
+ ///
+ [Parameter] public string TagName { get; set; } = "";
+
+ ///
+ ///
+ ///
+ [Parameter] public ElementReference? ElementRef { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter] public Action? ElementRefChanged { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter] public RenderFragment? ChildContent { get; set; }
+
+ ///
+ ///
+ ///
+ [Parameter(CaptureUnmatchedValues = true)]
+ public IDictionary? AdditionalAttributes { get; set; }
+
+ ///
+ ///
+ ///
+ ///
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ var index = 0;
+ builder.OpenElement(index++, TagName);
+ if (AdditionalAttributes != null)
+ {
+ if (AdditionalAttributes.Remove("@onclick", out var v))
+ {
+ builder.AddAttribute(index++, "onclick", v);
+ }
+ builder.AddMultipleAttributes(index++, AdditionalAttributes);
+ }
+ if (ChildContent != null) builder.AddContent(index++, ChildContent);
+ if (ElementRefChanged != null) builder.AddElementReferenceCapture(index++, capturedRef =>
+ {
+ ElementRef = capturedRef;
+ ElementRefChanged.Invoke(capturedRef);
+ });
+ builder.CloseElement();
+ }
+ }
+}
diff --git a/src/BootstrapBlazor/Enums/Color.cs b/src/BootstrapBlazor/Enums/Color.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5b022259944a7054e281fee5c7d66744c5b72209
--- /dev/null
+++ b/src/BootstrapBlazor/Enums/Color.cs
@@ -0,0 +1,75 @@
+using System.ComponentModel;
+
+namespace BootstrapBlazor.Components
+{
+ ///
+ /// 颜色枚举类型
+ ///
+ public enum Color
+ {
+ ///
+ /// 无颜色
+ ///
+ None,
+
+ ///
+ /// active
+ ///
+ [Description("active")]
+ Active,
+
+ ///
+ /// primary
+ ///
+ [Description("primary")]
+ Primary,
+
+ ///
+ /// secondary
+ ///
+ [Description("secondary")]
+ Secondary,
+
+ ///
+ /// success
+ ///
+ [Description("success")]
+ Success,
+
+ ///
+ /// danger
+ ///
+ [Description("danger")]
+ Danger,
+
+ ///
+ /// warning
+ ///
+ [Description("warning")]
+ Warning,
+
+ ///
+ /// info
+ ///
+ [Description("info")]
+ Info,
+
+ ///
+ /// light
+ ///
+ [Description("light")]
+ Light,
+
+ ///
+ /// dark
+ ///
+ [Description("dark")]
+ Dark,
+
+ ///
+ /// link
+ ///
+ [Description("link")]
+ Link
+ }
+}
diff --git a/src/BootstrapBlazor/Enums/Size.cs b/src/BootstrapBlazor/Enums/Size.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a29614466eeca27ec148a6020b80f0f86b6de546
--- /dev/null
+++ b/src/BootstrapBlazor/Enums/Size.cs
@@ -0,0 +1,45 @@
+using System.ComponentModel;
+
+namespace BootstrapBlazor.Components
+{
+ ///
+ /// Size 枚举类型
+ ///
+ public enum Size
+ {
+ ///
+ /// 无设置
+ ///
+ None,
+
+ ///
+ /// xs 超小设置
+ ///
+ [Description("xs")]
+ ExtraSmall,
+
+ ///
+ /// xs 小设置
+ ///
+ [Description("sm")]
+ Small,
+
+ ///
+ /// xs 中等设置
+ ///
+ [Description("md")]
+ Medium,
+
+ ///
+ /// xs 大设置
+ ///
+ [Description("lg")]
+ Large,
+
+ ///
+ /// xs 超大设置
+ ///
+ [Description("xl")]
+ ExtraLarge
+ }
+}
diff --git a/src/BootstrapBlazor/Utils/CssBuilder.cs b/src/BootstrapBlazor/Utils/CssBuilder.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db3a26d7a08c6d06f74214e45593af1e11205da5
--- /dev/null
+++ b/src/BootstrapBlazor/Utils/CssBuilder.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+
+namespace BootstrapBlazor.Utils
+{
+ ///
+ /// Css 生成操作类
+ ///
+ public struct CssBuilder
+ {
+ private string stringBuffer;
+
+ ///
+ /// Creates a CssBuilder used to define conditional CSS classes used in a component.
+ /// Call Build() to return the completed CSS Classes as a string.
+ ///
+ ///
+ public static CssBuilder Default(string value) => new CssBuilder(value);
+
+ ///
+ /// Creates an Empty CssBuilder used to define conditional CSS classes used in a component.
+ /// Call Build() to return the completed CSS Classes as a string.
+ ///
+ public static CssBuilder Empty() => new CssBuilder();
+
+ ///
+ /// Creates a CssBuilder used to define conditional CSS classes used in a component.
+ /// Call Build() to return the completed CSS Classes as a string.
+ ///
+ ///
+ public CssBuilder(string value) => stringBuffer = value;
+
+ ///
+ /// Adds a raw string to the builder that will be concatenated with the next class or value added to the builder.
+ ///
+ ///
+ /// CssBuilder
+ public CssBuilder AddValue(string value)
+ {
+ stringBuffer += value;
+ return this;
+ }
+
+ ///
+ /// Adds a CSS Class to the builder with space separator.
+ ///
+ /// CSS Class to add
+ /// CssBuilder
+ public CssBuilder AddClass(string value) => AddValue(" " + value);
+
+ ///
+ /// Adds a conditional CSS Class to the builder with space separator.
+ ///
+ /// CSS Class to conditionally add.
+ /// Condition in which the CSS Class is added.
+ /// CssBuilder
+ public CssBuilder AddClass(string value, bool when = true) => when ? AddClass(value) : this;
+
+ ///
+ /// Adds a conditional CSS Class to the builder with space separator.
+ ///
+ /// CSS Class to conditionally add.
+ /// Condition in which the CSS Class is added.
+ /// CssBuilder
+ public CssBuilder AddClass(string value, Func when) => AddClass(value, when());
+
+ ///
+ /// Adds a conditional CSS Class to the builder with space separator.
+ ///
+ /// Function that returns a CSS Class to conditionally add.
+ /// Condition in which the CSS Class is added.
+ /// CssBuilder
+ public CssBuilder AddClass(Func value, bool when = true) => when ? AddClass(value()) : this;
+
+ ///
+ /// Adds a conditional CSS Class to the builder with space separator.
+ ///
+ /// Function that returns a CSS Class to conditionally add.
+ /// Condition in which the CSS Class is added.
+ /// CssBuilder
+ public CssBuilder AddClass(Func value, Func when) => AddClass(value, when());
+
+ ///
+ /// Adds a conditional nested CssBuilder to the builder with space separator.
+ ///
+ /// CSS Class to conditionally add.
+ /// Condition in which the CSS Class is added.
+ /// CssBuilder
+ public CssBuilder AddClass(CssBuilder builder, bool when = true) => when ? AddClass(builder.Build()) : this;
+
+ ///
+ /// Adds a conditional CSS Class to the builder with space separator.
+ ///
+ /// CSS Class to conditionally add.
+ /// Condition in which the CSS Class is added.
+ /// CssBuilder
+ public CssBuilder AddClass(CssBuilder builder, Func when) => AddClass(builder, when());
+
+ ///
+ /// Adds a conditional CSS Class when it exists in a dictionary to the builder with space separator.
+ /// Null safe operation.
+ ///
+ /// Additional Attribute splat parameters
+ /// CssBuilder
+ public CssBuilder AddClassFromAttributes(IReadOnlyDictionary additionalAttributes) =>
+ additionalAttributes == null ? this :
+ additionalAttributes.TryGetValue("class", out var c) ? AddClass(((string)c ?? "").ToString()) : this;
+
+ ///
+ /// Finalize the completed CSS Classes as a string.
+ ///
+ /// string
+ public string Build()
+ {
+ // String buffer finalization code
+ return stringBuffer != null ? stringBuffer.Trim() : string.Empty;
+ }
+
+ ///
+ /// ToString should only and always call Build to finalize the rendered string.
+ ///
+ ///
+ public override string ToString() => Build();
+ }
+}
diff --git a/src/BootstrapBlazor/Utils/EnumExtensions.cs b/src/BootstrapBlazor/Utils/EnumExtensions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3fce88da2373851877dae94bb5befd070d47be7a
--- /dev/null
+++ b/src/BootstrapBlazor/Utils/EnumExtensions.cs
@@ -0,0 +1,58 @@
+using BootstrapBlazor.Components;
+using System.ComponentModel;
+
+namespace BootstrapBlazor.Utils
+{
+ ///
+ ///
+ ///
+ public static class EnumExtensions
+ {
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string ToDescriptionString(this Color val)
+ {
+ var attributes = val.GetType().GetField(val.ToString())?.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[] ?? new DescriptionAttribute[0];
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string ToDescriptionString(this Size val)
+ {
+ var attributes = val.GetType().GetField(val.ToString())?.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[] ?? new DescriptionAttribute[0];
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
+
+ /*
+ public static string ToDescriptionString(this InputType val)
+ {
+ var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
+
+ public static string ToDescriptionString(this DropdownDirection val)
+ {
+ var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
+
+ public static string ToDescriptionString(this Placement val)
+ {
+ var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
+ public static string ToDescriptionString(this HeadingSize val)
+ {
+ var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
+ */
+ }
+}