From d5331b0f990aa2d5e439c05184e0287d0c930afb Mon Sep 17 00:00:00 2001 From: Alexander Gusev <95075261+GusevAlexander-DevExpress@users.noreply.github.com> Date: Mon, 14 Jul 2025 20:01:40 +0400 Subject: [PATCH 01/13] Support dynamic tabs, refactore updated to 25.1 enabled Fluent theme removed extra files refactored supported dynamic tabs --- CS/DxBlazorApplication1.sln | 22 --- .../Components/Layout/Drawer.razor.css | 37 ---- .../Components/Layout/MainLayout.razor | 45 ----- .../Components/Layout/MainLayout.razor.css | 165 ------------------ .../Components/Layout/NavMenu.razor | 14 -- .../Components/Layout/NavMenu.razor.css | 85 --------- .../Components/MDI/MDIStateHelper.cs | 30 ---- .../Components/MDI/MDITab.cs | 13 -- .../Components/MDI/MDITabCollection.cs | 55 ------ .../Components/Pages/Counter.razor | 26 --- .../Components/Pages/Counter.razor.css | 37 ---- .../Components/Pages/Error.razor | 36 ---- .../Components/Pages/Form.razor | 27 --- .../Components/Pages/Index.razor | 146 ---------------- .../Components/Pages/Weather.razor | 34 ---- .../DxBlazorApplication1.csproj | 10 -- .../Services/WeatherForecast.cs | 11 -- .../Services/WeatherForecastService.cs | 17 -- .../wwwroot/images/form.svg | 3 - CS/DxBlazorApplication1/wwwroot/js/mdi.js | 27 --- CS/blazor_multi_tab_ui.sln | 25 +++ .../Components/App.razor | 14 +- .../Components/Layout/Drawer.razor | 19 +- .../Components/Layout/Drawer.razor.css | 86 +++++++++ .../Components/Layout/MainLayout.razor | 51 ++++++ .../Components/Layout/MainLayout.razor.css | 59 +++++++ .../Components/Layout/NavMenu.razor | 7 + .../Components/Layout/NavMenu.razor.css | 48 +++++ .../Components/MDI/MdiTabs.razor | 105 +++++++++++ .../Components/MDI/Tabs/Chart.razor | 60 +++++++ .../Components/MDI/Tabs/Customers.razor | 36 ++++ .../Components/MDI/Tabs/Orders.razor | 24 +++ .../Components/MDI/Tabs/Unknown.razor | 5 + .../Components/MDI/TabsContextMenu.razor | 104 +++++++++++ .../Components/MDI/TabsContextMenu.razor.js | 31 ++++ .../Components/Pages/Index.razor | 10 ++ .../Components/Pages/Index.razor.css | 0 .../Components/Routes.razor | 0 .../Components/_Imports.razor | 6 +- .../Models/CustomerModel.cs | 13 ++ .../Models/CustomersChartPoint.cs | 9 + .../Models/MdiStateModel.cs | 8 + CS/blazor_multi_tab_ui/Models/MdiTabModel.cs | 50 ++++++ CS/blazor_multi_tab_ui/Models/OrderModel.cs | 10 ++ .../NOTICE.txt | 0 .../Program.cs | 23 ++- .../Services/DataService.cs | 56 ++++++ .../Services/MdiStateService.cs | 137 +++++++++++++++ .../Services/UrlGenerator.cs | 13 +- .../appsettings.Development.json | 0 .../appsettings.json | 0 .../blazor_multi_tab_ui.csproj | 12 ++ .../wwwroot/css/bootstrap/bootstrap.css | 0 .../wwwroot/css/bootstrap/bootstrap.min.css | 0 .../wwwroot/css/open-iconic}/FONT-LICENSE.txt | 0 .../wwwroot/css/open-iconic}/ICON-LICENSE.txt | 0 .../wwwroot/css/open-iconic}/README.md | 0 .../font/css/open-iconic-bootstrap.min.css | 0 .../open-iconic}/font/fonts/open-iconic.eot | Bin .../open-iconic}/font/fonts/open-iconic.otf | Bin .../open-iconic}/font/fonts/open-iconic.svg | 0 .../open-iconic}/font/fonts/open-iconic.ttf | Bin .../open-iconic}/font/fonts/open-iconic.woff | Bin .../wwwroot/css/site.css | 38 ++-- .../wwwroot/css/theme-bs.css | 18 ++ .../wwwroot/css/theme-fluent.css | 43 +++++ .../wwwroot/favicon.ico | Bin .../wwwroot/images/account/log-in-fluent.svg | 3 + .../wwwroot/images/account/log-in.svg | 0 .../wwwroot/images/account/log-out-fluent.svg | 4 + .../wwwroot/images/account/log-out.svg | 0 .../images/account/manage-email-fluent.svg | 3 + .../wwwroot/images/account/manage-email.svg | 0 .../images/account/manage-password-fluent.svg | 3 + .../images/account/manage-password.svg | 0 .../images/account/manage-personal-fluent.svg | 3 + .../images/account/manage-personal.svg | 0 .../images/account/manage-profile-fluent.svg | 3 + .../wwwroot/images/account/manage-profile.svg | 0 .../account/manage-two-factor-fluent.svg | 3 + .../images/account/manage-two-factor.svg | 0 .../providers/facebook-logo-fluent.svg | 4 + .../account/providers/facebook-logo.svg | 0 .../account/providers/google-logo-fluent.svg | 6 + .../images/account/providers/google-logo.svg | 0 .../providers/microsoft-logo-fluent.svg | 6 + .../account/providers/microsoft-logo.svg | 0 .../account/providers/x-logo-fluent.svg | 3 + .../images/account/providers/x-logo.svg | 0 .../images/account/settings-fluent.svg | 3 + .../wwwroot/images/account/settings.svg | 0 .../wwwroot/images/account/user-fluent.svg | 3 + .../wwwroot/images/account/user.svg | 0 .../wwwroot/images/back-fluent.svg | 3 + .../wwwroot/images/back.svg | 0 .../wwwroot/images/cards.svg | 4 +- .../wwwroot/images/close-fluent.svg | 3 + .../wwwroot/images/close.svg | 0 .../wwwroot/images/counter-fluent.svg | 4 + .../wwwroot/images/counter.svg | 0 .../wwwroot/images/demos-fluent.svg | 3 + .../wwwroot/images/demos.svg | 0 .../wwwroot/images/doc-fluent.svg | 3 + .../wwwroot/images/doc.svg | 0 .../wwwroot/images/home-fluent.svg | 3 + .../wwwroot/images/home.svg | 0 .../wwwroot/images/logo.svg | 0 .../wwwroot/images/menu-fluent.svg | 3 + .../wwwroot/images/menu.svg | 0 .../wwwroot/images/weather-fluent.svg | 3 + .../wwwroot/images/weather.svg | 0 111 files changed, 1152 insertions(+), 884 deletions(-) delete mode 100644 CS/DxBlazorApplication1.sln delete mode 100644 CS/DxBlazorApplication1/Components/Layout/Drawer.razor.css delete mode 100644 CS/DxBlazorApplication1/Components/Layout/MainLayout.razor delete mode 100644 CS/DxBlazorApplication1/Components/Layout/MainLayout.razor.css delete mode 100644 CS/DxBlazorApplication1/Components/Layout/NavMenu.razor delete mode 100644 CS/DxBlazorApplication1/Components/Layout/NavMenu.razor.css delete mode 100644 CS/DxBlazorApplication1/Components/MDI/MDIStateHelper.cs delete mode 100644 CS/DxBlazorApplication1/Components/MDI/MDITab.cs delete mode 100644 CS/DxBlazorApplication1/Components/MDI/MDITabCollection.cs delete mode 100644 CS/DxBlazorApplication1/Components/Pages/Counter.razor delete mode 100644 CS/DxBlazorApplication1/Components/Pages/Counter.razor.css delete mode 100644 CS/DxBlazorApplication1/Components/Pages/Error.razor delete mode 100644 CS/DxBlazorApplication1/Components/Pages/Form.razor delete mode 100644 CS/DxBlazorApplication1/Components/Pages/Index.razor delete mode 100644 CS/DxBlazorApplication1/Components/Pages/Weather.razor delete mode 100644 CS/DxBlazorApplication1/DxBlazorApplication1.csproj delete mode 100644 CS/DxBlazorApplication1/Services/WeatherForecast.cs delete mode 100644 CS/DxBlazorApplication1/Services/WeatherForecastService.cs delete mode 100644 CS/DxBlazorApplication1/wwwroot/images/form.svg delete mode 100644 CS/DxBlazorApplication1/wwwroot/js/mdi.js create mode 100644 CS/blazor_multi_tab_ui.sln rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Components/App.razor (62%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Components/Layout/Drawer.razor (54%) create mode 100644 CS/blazor_multi_tab_ui/Components/Layout/Drawer.razor.css create mode 100644 CS/blazor_multi_tab_ui/Components/Layout/MainLayout.razor create mode 100644 CS/blazor_multi_tab_ui/Components/Layout/MainLayout.razor.css create mode 100644 CS/blazor_multi_tab_ui/Components/Layout/NavMenu.razor create mode 100644 CS/blazor_multi_tab_ui/Components/Layout/NavMenu.razor.css create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/MdiTabs.razor create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/Tabs/Chart.razor create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/Tabs/Customers.razor create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/Tabs/Orders.razor create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/Tabs/Unknown.razor create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/TabsContextMenu.razor create mode 100644 CS/blazor_multi_tab_ui/Components/MDI/TabsContextMenu.razor.js create mode 100644 CS/blazor_multi_tab_ui/Components/Pages/Index.razor rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Components/Pages/Index.razor.css (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Components/Routes.razor (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Components/_Imports.razor (76%) create mode 100644 CS/blazor_multi_tab_ui/Models/CustomerModel.cs create mode 100644 CS/blazor_multi_tab_ui/Models/CustomersChartPoint.cs create mode 100644 CS/blazor_multi_tab_ui/Models/MdiStateModel.cs create mode 100644 CS/blazor_multi_tab_ui/Models/MdiTabModel.cs create mode 100644 CS/blazor_multi_tab_ui/Models/OrderModel.cs rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/NOTICE.txt (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Program.cs (56%) create mode 100644 CS/blazor_multi_tab_ui/Services/DataService.cs create mode 100644 CS/blazor_multi_tab_ui/Services/MdiStateService.cs rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/Services/UrlGenerator.cs (84%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/appsettings.Development.json (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/appsettings.json (100%) create mode 100644 CS/blazor_multi_tab_ui/blazor_multi_tab_ui.csproj rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/css/bootstrap/bootstrap.css (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/css/bootstrap/bootstrap.min.css (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/FONT-LICENSE.txt (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/ICON-LICENSE.txt (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/README.md (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/font/css/open-iconic-bootstrap.min.css (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/font/fonts/open-iconic.eot (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/font/fonts/open-iconic.otf (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/font/fonts/open-iconic.svg (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/font/fonts/open-iconic.ttf (100%) rename CS/{DxBlazorApplication1/wwwroot/css/open.iconic => blazor_multi_tab_ui/wwwroot/css/open-iconic}/font/fonts/open-iconic.woff (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/css/site.css (66%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/css/theme-bs.css create mode 100644 CS/blazor_multi_tab_ui/wwwroot/css/theme-fluent.css rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/favicon.ico (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/log-in-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/log-in.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/log-out-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/log-out.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/manage-email-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/manage-email.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/manage-password-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/manage-password.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/manage-personal-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/manage-personal.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/manage-profile-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/manage-profile.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/manage-two-factor-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/manage-two-factor.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/providers/facebook-logo-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/providers/facebook-logo.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/providers/google-logo-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/providers/google-logo.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/providers/microsoft-logo-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/providers/microsoft-logo.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/providers/x-logo-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/providers/x-logo.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/settings-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/settings.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/account/user-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/account/user.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/back-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/back.svg (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/cards.svg (98%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/close-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/close.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/counter-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/counter.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/demos-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/demos.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/doc-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/doc.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/home-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/home.svg (100%) rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/logo.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/menu-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/menu.svg (100%) create mode 100644 CS/blazor_multi_tab_ui/wwwroot/images/weather-fluent.svg rename CS/{DxBlazorApplication1 => blazor_multi_tab_ui}/wwwroot/images/weather.svg (100%) diff --git a/CS/DxBlazorApplication1.sln b/CS/DxBlazorApplication1.sln deleted file mode 100644 index 741863f..0000000 --- a/CS/DxBlazorApplication1.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DxBlazorApplication1", "DxBlazorApplication1\DxBlazorApplication1.csproj", "{59B78194-0176-4F83-89C4-6A5F871FB9D5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {59B78194-0176-4F83-89C4-6A5F871FB9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {59B78194-0176-4F83-89C4-6A5F871FB9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {59B78194-0176-4F83-89C4-6A5F871FB9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {59B78194-0176-4F83-89C4-6A5F871FB9D5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/CS/DxBlazorApplication1/Components/Layout/Drawer.razor.css b/CS/DxBlazorApplication1/Components/Layout/Drawer.razor.css deleted file mode 100644 index 270840b..0000000 --- a/CS/DxBlazorApplication1/Components/Layout/Drawer.razor.css +++ /dev/null @@ -1,37 +0,0 @@ -::deep .navigation-drawer { - --dxbl-drawer-panel-footer-justify-content: center; - height: 100vh; - max-height: 100%; -} - -::deep .panel-open:not(.mobile) .menu-button { - display: none; -} - -@media (max-width: 768px) { - ::deep .panel-open:not(.mobile) .menu-button { - display: inline-flex; - } - - .mobile-drawer-closed .shading-copy { - display: none; - visibility: hidden; - } - - ::deep .shading-copy { - background-color: var(--dxbl-drawer-content-shading-bg); - height: 100%; - position: absolute; - transition: opacity ease var(--dxbl-drawer-animation-duration); - visibility: visible; - width: 100%; - z-index: 99; - opacity: var(--dxbl-drawer-content-shading-opacity); - } - - ::deep .panel-open .shading-copy { - opacity: 0; - visibility: unset; - height: unset; - } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/Layout/MainLayout.razor b/CS/DxBlazorApplication1/Components/Layout/MainLayout.razor deleted file mode 100644 index 032dd89..0000000 --- a/CS/DxBlazorApplication1/Components/Layout/MainLayout.razor +++ /dev/null @@ -1,45 +0,0 @@ -@inherits LayoutComponentBase -@inject NavigationManager NavigationManager - -
- - -
- -
-
- - - @Body - - -
-
- -@code { - public IMenuItemInfo? ClickedMenuItemName { get; set; } - - private void OnItemClick(MenuItemClickEventArgs e){ - ClickedMenuItemName = e.ItemInfo; - } - - [SupplyParameterFromQuery(Name = UrlGenerator.ToggleSidebarName)] - public bool ToggledSidebar { get; set; } - - private RenderFragment drawerHeader => @; - - private RenderFragment drawerFooter => @
- - - - - - -
; - - -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/Layout/MainLayout.razor.css b/CS/DxBlazorApplication1/Components/Layout/MainLayout.razor.css deleted file mode 100644 index 20dfd2d..0000000 --- a/CS/DxBlazorApplication1/Components/Layout/MainLayout.razor.css +++ /dev/null @@ -1,165 +0,0 @@ -.page { - height: 100%; - font-family: var(--bs-font-sans-serif); -} - -::deep .navigation-drawer > .dxbl-drawer-panel { - background-image: linear-gradient(180deg, var(--bs-primary) 0%, var(--bs-black) 150%); -} - -::deep .dxbl-drawer .dxbl-drawer-content { - height: 100vh; - overflow: auto; -} - -::deep .dxbl-drawer > .dxbl-drawer-panel .dxbl-drawer-header { - border-bottom: none; - padding: 2rem 1rem; -} - -::deep .dxbl-drawer-panel .dxbl-drawer-header .navigation-drawer-header { - width: 100%; - display: flex; - justify-content: space-between; -} - -::deep .dxbl-drawer > .dxbl-drawer-panel > .dxbl-drawer-body { - --dxbl-drawer-panel-body-padding-x: 0; - --dxbl-drawer-panel-body-padding-y: 1rem; -} - -::deep .dxbl-drawer > .dxbl-drawer-panel .dxbl-drawer-footer { - --dxbl-drawer-panel-footer-justify-content: center; - border-top: none; - padding-bottom: 1.5rem; -} - -::deep .content { - overflow: auto; - display: flex; - flex-direction: column; -} - -::deep .icon { - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - mask-position: center center; - -webkit-mask-mask-position: center center; - width: 1rem; - height: 1rem; - background-repeat: no-repeat; - background-color: var(--dxbl-btn-color); -} - -::deep .icon-back { - -webkit-mask-image: url("images/back.svg"); - mask-image: url("images/back.svg"); -} - -::deep .icon-close { - -webkit-mask-image: url("images/close.svg"); - mask-image: url("images/close.svg"); -} - -::deep .icon-menu { - -webkit-mask-image: url("images/menu.svg"); - mask-image: url("images/menu.svg"); -} - -::deep .icon-log-in { - -webkit-mask-image: url("images/account/log-in.svg"); - mask-image: url("images/account/log-in.svg"); -} - -::deep .icon-log-out { - -webkit-mask-image: url("images/account/log-out.svg"); - mask-image: url("images/account/log-out.svg"); -} - -::deep .icon-user { - -webkit-mask-image: url("images/account/user.svg"); - mask-image: url("images/account/user.svg"); -} - -::deep .docs-icon { - mask-image: url("images/doc.svg"); - -webkit-mask-image: url("images/doc.svg"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: var(--dxbl-btn-color); -} - -::deep .demos-icon { - mask-image: url("images/demos.svg"); - -webkit-mask-image: url("images/demos.svg"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - background-color: var(--dxbl-btn-color); -} - -::deep .footer-button:hover .demos-icon { - background-color: var(--dxbl-btn-hover-color); -} - -::deep .footer-button:hover .docs-icon { - background-color: var(--dxbl-btn-hover-color); -} - -::deep .menu-button:hover .icon { - background-color: var(--dxbl-btn-hover-color); -} - -::deep .menu-button-nav:hover .icon { - background-color: var(--dxbl-btn-hover-color); -} - -.panel-open .menu-button { - display: inline-flex; -} - -.menu-button-nav { - background-image: url("images/close.svg"); - width: 1.875rem; - height: 1.875rem; -} - -.nav-buttons-container { - display: flex; - gap: 10px; - padding: 2rem 1rem; -} - - .nav-buttons-container ::deep .menubutton-float-end { - margin-left: auto; - } - - .nav-buttons-container ::deep .dxbl-btn-icon-only { - --dxbl-btn-padding-x: 0.75rem; - --dxbl-btn-padding-y: 0.25rem; - } - -::deep .navigation-drawer > .dxbl-drawer-panel { - display: flex; -} - -::deep .navigation-drawer.mobile > .dxbl-drawer-panel { - display: none; -} - -::deep .navigation-drawer.mobile > .dxbl-drawer-shading { - display: none; -} - -@media (max-width: 768px) { - ::deep .navigation-drawer > .dxbl-drawer-panel { - display: none; - } - - ::deep .navigation-drawer.mobile > .dxbl-drawer-panel { - display: flex; - } - - ::deep .navigation-drawer.mobile > .dxbl-drawer-shading { - display: block; - } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/Layout/NavMenu.razor b/CS/DxBlazorApplication1/Components/Layout/NavMenu.razor deleted file mode 100644 index 526c61a..0000000 --- a/CS/DxBlazorApplication1/Components/Layout/NavMenu.razor +++ /dev/null @@ -1,14 +0,0 @@ - - -@code{ - [Parameter] - public EventCallback ItemClick { get; set; } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/Layout/NavMenu.razor.css b/CS/DxBlazorApplication1/Components/Layout/NavMenu.razor.css deleted file mode 100644 index fa90228..0000000 --- a/CS/DxBlazorApplication1/Components/Layout/NavMenu.razor.css +++ /dev/null @@ -1,85 +0,0 @@ -#sidebar { - min-width: 15rem; - max-width: 15rem; - transition: transform 0.1s ease-out; - height: 100%; - max-height: 100%; - display: block; - background: inherit; -} - -.logo { - text-align: center; -} - -::deep .menu.display-mobile { - margin-bottom: 2rem; -} - -::deep .menu.display-iam { - margin-bottom: 2rem; -} - -::deep .menu { - background-color: inherit; -} - - ::deep .menu .dxbl-menu-item-list { - gap: 0.5rem; - } - -::deep .menu-item { - color: var(--bs-white); -} - -::deep .icon { - width: 1rem; - height: 1rem; - background-position: center; - background-repeat: no-repeat; - margin-left: 0.5rem; -} - -::deep .home-icon { - background-image: url("images/home.svg"); -} - -::deep .weather-icon { - background-image: url("images/weather.svg"); -} - -::deep .form-icon { - background-image: url("images/form.svg"); -} - -::deep .counter-icon { - background-image: url("images/counter.svg"); -} - -::deep .settings-icon { - background-image: url("images/account/settings.svg"); -} - -::deep .log-in-icon { - background-image: url("images/account/log-in.svg"); -} - -::deep .log-out-icon { - background-image: url("images/account/log-out.svg"); -} - -::deep .user-icon { - background-image: url("images/account/user.svg"); -} - -@media (max-width: 768px) { - #sidebar { - min-width: inherit; - max-width: inherit; - display: block; - } - - .logo { - text-align: inherit; - } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/MDI/MDIStateHelper.cs b/CS/DxBlazorApplication1/Components/MDI/MDIStateHelper.cs deleted file mode 100644 index 8119bc0..0000000 --- a/CS/DxBlazorApplication1/Components/MDI/MDIStateHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.JSInterop; -using System.Text.Json; - -namespace DxBlazorApplication1.Components.MDI -{ - public class MDIStateHelper { - private const string LOCAL_STORAGE_KEY = "MDI-Layout"; - private readonly IJSRuntime js; - - public MDIStateHelper(IJSRuntime js) { - this.js = js; - } - - public async Task SaveLayoutToLocalStorageAsync(MDITabCollection tabData) { - try { - var json = JsonSerializer.Serialize(tabData); - await js.InvokeVoidAsync("localStorage.setItem", LOCAL_STORAGE_KEY, json); - } - catch { return; } - } - - public async Task LoadLayoutFromLocalStorageAsync() { - try { - var json = await js.InvokeAsync("localStorage.getItem", LOCAL_STORAGE_KEY); - return JsonSerializer.Deserialize(json); - } - catch { return null; } - } - } -} diff --git a/CS/DxBlazorApplication1/Components/MDI/MDITab.cs b/CS/DxBlazorApplication1/Components/MDI/MDITab.cs deleted file mode 100644 index a354e7b..0000000 --- a/CS/DxBlazorApplication1/Components/MDI/MDITab.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace DxBlazorApplication1.Components.MDI -{ - public class MDITab { - public string Text { get; set; } - public int VisibleIndex { get; set; } - public bool Visible { get; set; } - public MDITab(string text, int visibleIndex, bool visible) { - Text = text; - VisibleIndex = visibleIndex; - Visible = visible; - } - } -} diff --git a/CS/DxBlazorApplication1/Components/MDI/MDITabCollection.cs b/CS/DxBlazorApplication1/Components/MDI/MDITabCollection.cs deleted file mode 100644 index cc4bbbe..0000000 --- a/CS/DxBlazorApplication1/Components/MDI/MDITabCollection.cs +++ /dev/null @@ -1,55 +0,0 @@ -using DevExpress.Blazor; -using System.Collections; -using System.Text.Json.Serialization; - -namespace DxBlazorApplication1.Components.MDI -{ - public class MDITabCollection { - [JsonInclude] - private List tabs; - - public int Count => tabs.Count; - - public MDITabCollection() { - tabs = new List(); - } - - public MDITabCollection(IEnumerable tabs) { - this.tabs = tabs.Select(t => new MDITab(t.Text, - t.VisibleIndex == -1 ? tabs.ToList().IndexOf(t) : t.VisibleIndex, - t.Visible)).ToList(); - } - - public void SetVisibleAllTabs(bool visible) { - tabs.ForEach((t) => t.Visible = visible); - } - - public string? GetTabTextByTabInfo(ITabInfo tabInfo) { - return tabs.FirstOrDefault(t => t.Text == tabInfo.Text)?.Text; - } - - public int GetVisibleIndexByTabText(string? text) { - return tabs.Find(t => t.Text == text)?.VisibleIndex ?? -1; - } - - public bool GetVisibleByTabText(string? text) { - return tabs.Find(t => t.Text == text)?.Visible ?? true; - } - - public void SetVisibleByTabText(string? text, bool visible) { - var tab = tabs.Find(t => t.Text == text); - if(tab != null) - { - tab.Visible = visible; - } - } - - public void SetVisibleIndexByTabText(string? text, int visibleIndex) { - var tab = tabs.Find(t => t.Text == text); - if(tab != null) - { - tab.VisibleIndex = visibleIndex; - } - } - } -} diff --git a/CS/DxBlazorApplication1/Components/Pages/Counter.razor b/CS/DxBlazorApplication1/Components/Pages/Counter.razor deleted file mode 100644 index d5c756a..0000000 --- a/CS/DxBlazorApplication1/Components/Pages/Counter.razor +++ /dev/null @@ -1,26 +0,0 @@ -@page "/counter" -@rendermode InteractiveServer - -

Counter

- -
-
-
- @currentCount -
-
- current count -
-
-
- Click me -
- -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/CS/DxBlazorApplication1/Components/Pages/Counter.razor.css b/CS/DxBlazorApplication1/Components/Pages/Counter.razor.css deleted file mode 100644 index 08d0ae2..0000000 --- a/CS/DxBlazorApplication1/Components/Pages/Counter.razor.css +++ /dev/null @@ -1,37 +0,0 @@ -.counter-block { - display: flex; - padding: 2.5rem 1.5rem 1.5rem 1.5rem; - flex-direction: column; - border-radius: 1rem; - gap: 1.5rem; - justify-content: center; - align-items: center; - width: 16.875rem; - height: 17rem; - position: relative; -} - - .counter-block .counter-content { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; - } - - .counter-block .counter-count { - font-size: 7.5rem; - font-weight: 400; - line-height: 7.75rem; - } - - .counter-block .counter-block-back { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: var(--bs-body-color); - opacity: 0.05; - border-radius: 1rem; - z-index: -2; - } diff --git a/CS/DxBlazorApplication1/Components/Pages/Error.razor b/CS/DxBlazorApplication1/Components/Pages/Error.razor deleted file mode 100644 index 576cc2d..0000000 --- a/CS/DxBlazorApplication1/Components/Pages/Error.razor +++ /dev/null @@ -1,36 +0,0 @@ -@page "/Error" -@using System.Diagnostics - -Error - -

Error.

-

An error occurred while processing your request.

- -@if (ShowRequestId) -{ -

- Request ID: @RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

- -@code{ - [CascadingParameter] - private HttpContext? HttpContext { get; set; } - - private string? RequestId { get; set; } - private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - protected override void OnInitialized() => - RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; -} diff --git a/CS/DxBlazorApplication1/Components/Pages/Form.razor b/CS/DxBlazorApplication1/Components/Pages/Form.razor deleted file mode 100644 index b87fd23..0000000 --- a/CS/DxBlazorApplication1/Components/Pages/Form.razor +++ /dev/null @@ -1,27 +0,0 @@ -@page "/form" - - - - - - - - - - - - - - - - - - -@code { - DateTime Today = DateTime.Today; -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/Pages/Index.razor b/CS/DxBlazorApplication1/Components/Pages/Index.razor deleted file mode 100644 index a7afff0..0000000 --- a/CS/DxBlazorApplication1/Components/Pages/Index.razor +++ /dev/null @@ -1,146 +0,0 @@ -@page "/" -@using System.Text.Json -@using DxBlazorApplication1.Components.MDI -@inject IJSRuntime JS -@inject MDIStateHelper StateHelper - -Multi-Tab Interface - -
- - -
- -
-
- -
- -
-
- -
-
-
-
-
-
- - - - - - - - - - -@code { - [CascadingParameter(Name = "ClickedMenuItemName")] - public IMenuItemInfo? ClickedMenuItem { get; set; } - - private ElementReference divContainer; - private DxContextMenu? menu; - private DxTabs? tabs; - - private MDITabCollection collection = new MDITabCollection(); - - private int activeTabIndex; - private string? clickedTabText; - private bool isReordering; - - [JSInvokable] - public async Task ShowContextMenu(MouseEventArgs e, string tabText) { - clickedTabText = tabText; - if(menu != null) { - await menu.ShowAsync(e); - } - } - - #region DxTabs Event Handlers - - private async Task OnTabClosing(TabCloseEventArgs e) { - collection.SetVisibleByTabText(e.TabInfo.Text, false); - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - } - - private async Task OnTabReordering(TabReorderEventArgs e) { - var vi1 = collection.GetVisibleIndexByTabText(e.FromTabInfo.Text); - var vi2 = collection.GetVisibleIndexByTabText(e.ToTabInfo.Text); - collection.SetVisibleIndexByTabText(e.FromTabInfo.Text, vi2); - collection.SetVisibleIndexByTabText(e.ToTabInfo.Text, vi1); - isReordering = true; - } - - #endregion - - #region DxContextMenuItem.Click Event Handlers - - private async Task CloseTab() { - collection.SetVisibleByTabText(clickedTabText, false); - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - } - - private async Task CloseAllTabs() { - collection.SetVisibleAllTabs(false); - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - } - - private async Task CloseOtherTabs() { - collection.SetVisibleAllTabs(false); - collection.SetVisibleByTabText(clickedTabText, true); - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - } - - private async Task RestoreAllTabs() { - collection.SetVisibleAllTabs(true); - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - } - - #endregion - - #region Life-Cycle Event Handlers - - protected override async Task OnParametersSetAsync() { - await base.OnParametersSetAsync(); - - if(ClickedMenuItem is null) - return; - - if(!collection.GetVisibleByTabText(ClickedMenuItem.Name)) { - collection.SetVisibleByTabText(ClickedMenuItem.Name, true); - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - } - activeTabIndex = collection.GetVisibleIndexByTabText(ClickedMenuItem.Name); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) { - if(firstRender) { - var cacheCollection = await StateHelper.LoadLayoutFromLocalStorageAsync(); - if(cacheCollection is null || cacheCollection.Count == 0) { - collection = new MDITabCollection(tabs!.GetOrderedTabs()); - } - else { - collection = cacheCollection; - } - await InvokeAsync(StateHasChanged); - } - - var dotNetInstance = DotNetObjectReference.Create(this); - foreach(ITabInfo tab in tabs!.GetOrderedTabs()) { - await JS.InvokeVoidAsync("addContextMenuHandler", divContainer, tab.CssClass, collection.GetTabTextByTabInfo(tab), dotNetInstance); - } - - if(isReordering) { - await StateHelper.SaveLayoutToLocalStorageAsync(collection); - isReordering = false; - } - } - - #endregion -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Components/Pages/Weather.razor b/CS/DxBlazorApplication1/Components/Pages/Weather.razor deleted file mode 100644 index 5518c06..0000000 --- a/CS/DxBlazorApplication1/Components/Pages/Weather.razor +++ /dev/null @@ -1,34 +0,0 @@ -@page "/weather" - -@using DxBlazorApplication1.Services -@attribute [StreamRendering(true)] -@rendermode InteractiveServer -@inject WeatherForecastService ForecastService - -

Weather

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - -} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now)); - } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/DxBlazorApplication1.csproj b/CS/DxBlazorApplication1/DxBlazorApplication1.csproj deleted file mode 100644 index 965f5d0..0000000 --- a/CS/DxBlazorApplication1/DxBlazorApplication1.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - net8.0 - enable - enable - - - - - \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Services/WeatherForecast.cs b/CS/DxBlazorApplication1/Services/WeatherForecast.cs deleted file mode 100644 index 7fec132..0000000 --- a/CS/DxBlazorApplication1/Services/WeatherForecast.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace DxBlazorApplication1.Services { - public class WeatherForecast { - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } - } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/Services/WeatherForecastService.cs b/CS/DxBlazorApplication1/Services/WeatherForecastService.cs deleted file mode 100644 index cd5e2bf..0000000 --- a/CS/DxBlazorApplication1/Services/WeatherForecastService.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace DxBlazorApplication1.Services { - public class WeatherForecastService { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - public Task GetForecastAsync(DateOnly startDate) { - var rng = new Random(); - return Task.FromResult(Enumerable.Range(1, 20).Select(index => new WeatherForecast { - Date = startDate.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }).ToArray()); - } - } -} \ No newline at end of file diff --git a/CS/DxBlazorApplication1/wwwroot/images/form.svg b/CS/DxBlazorApplication1/wwwroot/images/form.svg deleted file mode 100644 index c2e491e..0000000 --- a/CS/DxBlazorApplication1/wwwroot/images/form.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/CS/DxBlazorApplication1/wwwroot/js/mdi.js b/CS/DxBlazorApplication1/wwwroot/js/mdi.js deleted file mode 100644 index eea9fae..0000000 --- a/CS/DxBlazorApplication1/wwwroot/js/mdi.js +++ /dev/null @@ -1,27 +0,0 @@ -function addContextMenuHandler(container, tabClass, text, dotNetObject) { - var tabElement = container.getElementsByClassName(tabClass)[0]; - if (!tabElement || tabElement.hasAttribute("cp_ctx")) return; - tabElement.setAttribute("cp_ctx", true); - tabElement.addEventListener('contextmenu', (event) => { - event.preventDefault(); - let eventArgs = { - clientX: event.clientX, - clientY: event.clientY, - screenX: event.screenX, - screenY: event.screenY, - offsetX: event.offsetX, - offsetY: event.offsetY, - pageX: event.pageX, - pageY: event.pageY, - button: event.button, - buttons: event.buttons, - ctrlKey: event.ctrlKey, - shiftKey: event.shiftKey, - altKey: event.altKey, - metaKey: event.metaKey, - detail: event.detail, - type: event.type - }; - dotNetObject.invokeMethodAsync("ShowContextMenu", eventArgs, text); - }); -}; \ No newline at end of file diff --git a/CS/blazor_multi_tab_ui.sln b/CS/blazor_multi_tab_ui.sln new file mode 100644 index 0000000..a86be68 --- /dev/null +++ b/CS/blazor_multi_tab_ui.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36202.13 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "blazor_multi_tab_ui", "blazor_multi_tab_ui\blazor_multi_tab_ui.csproj", "{8936C643-54B5-0391-8F8D-B0D3E8670BDC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8936C643-54B5-0391-8F8D-B0D3E8670BDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8936C643-54B5-0391-8F8D-B0D3E8670BDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8936C643-54B5-0391-8F8D-B0D3E8670BDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8936C643-54B5-0391-8F8D-B0D3E8670BDC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8D1D4A02-3C87-4CFF-99EC-856B074165D4} + EndGlobalSection +EndGlobal diff --git a/CS/DxBlazorApplication1/Components/App.razor b/CS/blazor_multi_tab_ui/Components/App.razor similarity index 62% rename from CS/DxBlazorApplication1/Components/App.razor rename to CS/blazor_multi_tab_ui/Components/App.razor index 637bd5d..c39a505 100644 --- a/CS/DxBlazorApplication1/Components/App.razor +++ b/CS/blazor_multi_tab_ui/Components/App.razor @@ -2,25 +2,25 @@ @inject IFileVersionProvider FileVersionProvider - + - - + @DxResourceManager.RegisterTheme(Themes.Fluent.Clone(properties => properties.AddFilePaths($"css/theme-fluent.css"))) @DxResourceManager.RegisterScripts() - + - + -@code{ +@code { private string AppendVersion(string path) => FileVersionProvider.AddFileVersionToPath("/", path); -} \ No newline at end of file + +} diff --git a/CS/DxBlazorApplication1/Components/Layout/Drawer.razor b/CS/blazor_multi_tab_ui/Components/Layout/Drawer.razor similarity index 54% rename from CS/DxBlazorApplication1/Components/Layout/Drawer.razor rename to CS/blazor_multi_tab_ui/Components/Layout/Drawer.razor index 8ee9250..020fa2d 100644 --- a/CS/DxBlazorApplication1/Components/Layout/Drawer.razor +++ b/CS/blazor_multi_tab_ui/Components/Layout/Drawer.razor @@ -2,17 +2,30 @@ @inject NavigationManager NavigationManager
- + @DrawerBody - + @DrawerBody -
+