diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor
index 21f5adc71..52b8b9e65 100644
--- a/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor
+++ b/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor
@@ -11,7 +11,7 @@
diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor
index d8ed9bb3a..a65862c40 100644
--- a/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor
+++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor
@@ -13,7 +13,7 @@ else
diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor
index 5f3b4a35c..2c360051f 100644
--- a/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor
+++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor
@@ -8,7 +8,7 @@
diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor
index 2c1a72e3e..3ac386699 100644
--- a/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor
+++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor
@@ -4,16 +4,14 @@
diff --git a/src/AliasVault.Client/Main/Pages/Settings/Vault.razor b/src/AliasVault.Client/Main/Pages/Settings/Vault.razor
index 550a687eb..d4580262c 100644
--- a/src/AliasVault.Client/Main/Pages/Settings/Vault.razor
+++ b/src/AliasVault.Client/Main/Pages/Settings/Vault.razor
@@ -5,15 +5,11 @@
Export vault
diff --git a/src/AliasVault.Client/Main/Pages/Test/Test1.razor b/src/AliasVault.Client/Main/Pages/Test/Test1.razor
index cd4408a0e..e82467dd0 100644
--- a/src/AliasVault.Client/Main/Pages/Test/Test1.razor
+++ b/src/AliasVault.Client/Main/Pages/Test/Test1.razor
@@ -4,15 +4,11 @@
Test webapi call 1
-
-
-
-
-
Test webapi call 1
-
-
Test webapi call 1.
-
-
+
+
@if (IsLoading)
{
diff --git a/src/AliasVault.Client/Main/Pages/Test/Test2.razor b/src/AliasVault.Client/Main/Pages/Test/Test2.razor
index d6dfac6ce..a633ec842 100644
--- a/src/AliasVault.Client/Main/Pages/Test/Test2.razor
+++ b/src/AliasVault.Client/Main/Pages/Test/Test2.razor
@@ -4,15 +4,11 @@
Test webapi call 2
-
-
-
-
-
Test webapi call 2
-
-
Test webapi call 2.
-
-
+
+
@if (IsLoading)
{
diff --git a/src/AliasVault.Client/_Imports.razor b/src/AliasVault.Client/_Imports.razor
index 6793fed06..9006a1adb 100644
--- a/src/AliasVault.Client/_Imports.razor
+++ b/src/AliasVault.Client/_Imports.razor
@@ -27,6 +27,7 @@
@using AliasVault.RazorComponents
@using AliasVault.RazorComponents.Alerts
@using AliasVault.RazorComponents.Buttons
+@using AliasVault.RazorComponents.Headings
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Blazored.LocalStorage
diff --git a/src/AliasVault.Client/tailwind.config.js b/src/AliasVault.Client/tailwind.config.js
index d02ab3413..4b67e5747 100644
--- a/src/AliasVault.Client/tailwind.config.js
+++ b/src/AliasVault.Client/tailwind.config.js
@@ -3,6 +3,7 @@ module.exports = {
content: [
'./**/*.html',
'./**/*.razor',
+ '../Shared/AliasVault.RazorComponents/**/*.cs',
'../Shared/AliasVault.RazorComponents/**/*.razor',
],
safelist: [
diff --git a/src/AliasVault.Client/wwwroot/css/tailwind.css b/src/AliasVault.Client/wwwroot/css/tailwind.css
index b88d6885c..dc72fc79c 100644
--- a/src/AliasVault.Client/wwwroot/css/tailwind.css
+++ b/src/AliasVault.Client/wwwroot/css/tailwind.css
@@ -554,40 +554,6 @@ video {
--tw-contain-style: ;
}
-.container {
- width: 100%;
-}
-
-@media (min-width: 640px) {
- .container {
- max-width: 640px;
- }
-}
-
-@media (min-width: 768px) {
- .container {
- max-width: 768px;
- }
-}
-
-@media (min-width: 1024px) {
- .container {
- max-width: 1024px;
- }
-}
-
-@media (min-width: 1280px) {
- .container {
- max-width: 1280px;
- }
-}
-
-@media (min-width: 1536px) {
- .container {
- max-width: 1536px;
- }
-}
-
.sr-only {
position: absolute;
width: 1px;
@@ -600,6 +566,10 @@ video {
border-width: 0;
}
+.visible {
+ visibility: visible;
+}
+
.static {
position: static;
}
@@ -1072,6 +1042,12 @@ video {
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
+.space-x-3 > :not([hidden]) ~ :not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(0.75rem * var(--tw-space-x-reverse));
+ margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
+}
+
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(1rem * var(--tw-space-x-reverse));
@@ -1517,10 +1493,6 @@ video {
padding-right: 0.75rem;
}
-.pt-10 {
- padding-top: 2.5rem;
-}
-
.pt-16 {
padding-top: 4rem;
}
@@ -1913,11 +1885,6 @@ video {
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
-.hover\:bg-red-700:hover {
- --tw-bg-opacity: 1;
- background-color: rgb(185 28 28 / var(--tw-bg-opacity));
-}
-
.hover\:bg-red-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
@@ -2119,11 +2086,6 @@ video {
background-color: rgb(17 24 39 / var(--tw-bg-opacity));
}
-.dark\:bg-green-500:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(34 197 94 / var(--tw-bg-opacity));
-}
-
.dark\:bg-green-600:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
@@ -2139,11 +2101,6 @@ video {
background-color: rgb(214 131 56 / var(--tw-bg-opacity));
}
-.dark\:bg-red-500:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(239 68 68 / var(--tw-bg-opacity));
-}
-
.dark\:bg-red-600:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
@@ -2267,11 +2224,6 @@ video {
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
}
-.dark\:hover\:bg-green-600:hover:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(22 163 74 / var(--tw-bg-opacity));
-}
-
.dark\:hover\:bg-green-700:hover:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(21 128 61 / var(--tw-bg-opacity));
@@ -2287,11 +2239,6 @@ video {
background-color: rgb(184 112 47 / var(--tw-bg-opacity));
}
-.dark\:hover\:bg-red-600:hover:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(220 38 38 / var(--tw-bg-opacity));
-}
-
.dark\:hover\:bg-red-700:hover:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
@@ -2569,11 +2516,6 @@ video {
.lg\:gap-4 {
gap: 1rem;
}
-
- .lg\:px-0 {
- padding-left: 0px;
- padding-right: 0px;
- }
}
@media (min-width: 1280px) {
diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor b/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor
index a89ebbdbc..c473cca9e 100644
--- a/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor
+++ b/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor
@@ -52,17 +52,9 @@
///
A string containing the CSS classes for the button.
private string GetButtonClasses()
{
- var baseClasses = "flex center items-center px-3 py-2 text-sm font-medium text-white rounded-lg focus:outline-none focus:ring-4";
- var colorClasses = Color switch
- {
- "primary" => "bg-primary-700 hover:bg-primary-800 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800",
- "danger" => "bg-red-700 hover:bg-red-800 focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800",
- "success" => "bg-green-700 hover:bg-green-800 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800",
- "secondary" => "bg-secondary-700 hover:bg-secondary-800 focus:ring-secondary-300 dark:bg-secondary-600 dark:hover:bg-secondary-700 dark:focus:ring-secondary-800",
- _ => "bg-gray-700 hover:bg-gray-800 focus:ring-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-800"
- };
- var disabledClasses = IsDisabled ? "bg-gray-400 cursor-not-allowed" : "";
+ var colorClasses = ButtonStyles.GetColorClasses(Color);
+ var disabledClasses = IsDisabled ? ButtonStyles.DisabledClasses : "";
- return $"{baseClasses} {colorClasses} {disabledClasses} {AdditionalClasses}".Trim();
+ return $"{ButtonStyles.BaseClasses} {colorClasses} {disabledClasses} {AdditionalClasses}".Trim();
}
}
diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/ButtonStyles.cs b/src/Shared/AliasVault.RazorComponents/Buttons/ButtonStyles.cs
new file mode 100644
index 000000000..4c38344dc
--- /dev/null
+++ b/src/Shared/AliasVault.RazorComponents/Buttons/ButtonStyles.cs
@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) lanedirt. All rights reserved.
+// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
+//
+//-----------------------------------------------------------------------
+
+namespace AliasVault.RazorComponents.Buttons;
+
+///
+/// A static class that provides CSS classes for buttons.
+///
+public static class ButtonStyles
+{
+ ///
+ /// Gets the base CSS classes for buttons.
+ ///
+ public static string BaseClasses => "flex center items-center px-3 py-2 text-sm font-medium text-white rounded-lg focus:outline-none focus:ring-4";
+
+ ///
+ /// Gets the CSS classes for a disabled button.
+ ///
+ public static string DisabledClasses => "bg-gray-400 cursor-not-allowed";
+
+ ///
+ /// Gets the color-specific CSS classes for a button based on the provided color.
+ ///
+ ///
The color name for the button (e.g., "primary", "danger", "success", "secondary").
+ ///
A string containing the appropriate CSS classes for the specified color.
+ public static string GetColorClasses(string color) => color switch
+ {
+ "primary" => "bg-primary-700 hover:bg-primary-800 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800",
+ "danger" => "bg-red-700 hover:bg-red-800 focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800",
+ "success" => "bg-green-700 hover:bg-green-800 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800",
+ "secondary" => "bg-secondary-700 hover:bg-secondary-800 focus:ring-secondary-300 dark:bg-secondary-600 dark:hover:bg-secondary-700 dark:focus:ring-secondary-800",
+ _ => "bg-gray-700 hover:bg-gray-800 focus:ring-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-800",
+ };
+}
diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/CancelButton.razor b/src/Shared/AliasVault.RazorComponents/Buttons/CancelButton.razor
new file mode 100644
index 000000000..5e222bdfe
--- /dev/null
+++ b/src/Shared/AliasVault.RazorComponents/Buttons/CancelButton.razor
@@ -0,0 +1,26 @@
+
+ @ChildContent
+
+
+@code {
+ ///
+ /// The event to call in the parent when the cancel button is clicked.
+ ///
+ [Parameter]
+ public EventCallback OnClick { get; set; }
+
+ ///
+ /// The content to be displayed inside the button.
+ ///
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ ///
+ /// Handles the button click event.
+ ///
+ private async Task HandleClick()
+ {
+ await OnClick.InvokeAsync();
+ }
+}
diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/ConfirmButton.razor b/src/Shared/AliasVault.RazorComponents/Buttons/ConfirmButton.razor
new file mode 100644
index 000000000..273d11c06
--- /dev/null
+++ b/src/Shared/AliasVault.RazorComponents/Buttons/ConfirmButton.razor
@@ -0,0 +1,26 @@
+
+ @ChildContent
+
+
+@code {
+ ///
+ /// The event to call in the parent when the confirm button is clicked.
+ ///
+ [Parameter]
+ public EventCallback OnClick { get; set; }
+
+ ///
+ /// The content to be displayed inside the button.
+ ///
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ ///
+ /// Handles the button click event.
+ ///
+ private async Task HandleClick()
+ {
+ await OnClick.InvokeAsync();
+ }
+}
diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/LinkButton.razor b/src/Shared/AliasVault.RazorComponents/Buttons/LinkButton.razor
new file mode 100644
index 000000000..f4601c237
--- /dev/null
+++ b/src/Shared/AliasVault.RazorComponents/Buttons/LinkButton.razor
@@ -0,0 +1,46 @@
+
+ @Text @AdditionalText
+
+
+@code {
+ ///
+ /// Gets or sets the URL that the hyperlink points to.
+ ///
+ [Parameter]
+ public string Href { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the main text of the button.
+ ///
+ [Parameter]
+ public string Text { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the additional text that appears on larger screens.
+ ///
+ [Parameter]
+ public string AdditionalText { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the color theme of the button.
+ ///
+ [Parameter]
+ public string Color { get; set; } = "primary";
+
+ ///
+ /// Gets or sets additional CSS classes to apply to the button.
+ ///
+ [Parameter]
+ public string AdditionalClasses { get; set; } = string.Empty;
+
+ ///
+ /// Gets the CSS classes for the link button based on the color and additional classes.
+ ///
+ ///
A string containing the CSS classes for the link button.
+ private string GetButtonClasses()
+ {
+ var colorClasses = ButtonStyles.GetColorClasses(Color);
+
+ return $"{ButtonStyles.BaseClasses} {colorClasses} {AdditionalClasses}".Trim();
+ }
+}
diff --git a/src/AliasVault.Client/Main/Components/Layout/Breadcrumb.razor b/src/Shared/AliasVault.RazorComponents/Headings/Breadcrumb.razor
similarity index 96%
rename from src/AliasVault.Client/Main/Components/Layout/Breadcrumb.razor
rename to src/Shared/AliasVault.RazorComponents/Headings/Breadcrumb.razor
index e40d4516d..3ec229606 100644
--- a/src/AliasVault.Client/Main/Components/Layout/Breadcrumb.razor
+++ b/src/Shared/AliasVault.RazorComponents/Headings/Breadcrumb.razor
@@ -1,6 +1,7 @@
-@inherits ComponentBase
+@using AliasVault.Client.Main.Models
+@inherits ComponentBase
-
+
diff --git a/src/Shared/AliasVault.RazorComponents/Headings/H1.razor b/src/Shared/AliasVault.RazorComponents/Headings/H1.razor
new file mode 100644
index 000000000..f193275b1
--- /dev/null
+++ b/src/Shared/AliasVault.RazorComponents/Headings/H1.razor
@@ -0,0 +1,9 @@
+@ChildContent
+
+@code {
+ ///
+ /// The content to be displayed inside the heading.
+ ///
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+}
diff --git a/src/Shared/AliasVault.RazorComponents/Headings/PageHeader.razor b/src/Shared/AliasVault.RazorComponents/Headings/PageHeader.razor
new file mode 100644
index 000000000..6fb4ae3d0
--- /dev/null
+++ b/src/Shared/AliasVault.RazorComponents/Headings/PageHeader.razor
@@ -0,0 +1,43 @@
+@using AliasVault.Client.Main.Models
+
+
+
+
+
+
@Title
+ @if (CustomActions != null)
+ {
+
+ @CustomActions
+
+ }
+
+
@Description
+
+
+
+@code {
+ ///
+ /// Gets or sets the breadcrumb items for the header.
+ ///
+ [Parameter]
+ public List BreadcrumbItems { get; set; } = new List();
+
+ ///
+ /// Gets or sets the title of the header.
+ ///
+ [Parameter]
+ public string Title { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the description text below the title.
+ ///
+ [Parameter]
+ public string Description { get; set; } = string.Empty;
+
+ ///
+ /// The caller can provide a custom action button section to be displayed on the right side of the header.
+ ///
+ [Parameter]
+ public RenderFragment? CustomActions { get; set; }
+}
diff --git a/src/AliasVault.Client/Main/Models/BreadcrumbItem.cs b/src/Shared/AliasVault.RazorComponents/Models/BreadcrumbItem.cs
similarity index 100%
rename from src/AliasVault.Client/Main/Models/BreadcrumbItem.cs
rename to src/Shared/AliasVault.RazorComponents/Models/BreadcrumbItem.cs