Browse Source

Merge branch 'dev' into issue-19444

pull/19469/head
Sinan Öztürk 2 years ago
committed by GitHub
parent
commit
531261a6c2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      Directory.Packages.props
  2. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 38
      docs/en/UI/Blazor/Block-Busy.md
  4. 4
      docs/en/docs-nav.json
  5. 2
      framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming/ComponentsComponentsBundleContributor.cs
  6. 1
      framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalStyleContributor.cs
  7. 27
      framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/BlockUi/AbpBlockUiService.cs
  8. 56
      framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/css/abp.css
  9. 48
      framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/abp.js
  10. 1
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs
  11. 10
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/BlockUi/IBlockUiService.cs
  12. 17
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/BlockUi/NullBlockUiService.cs
  13. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs
  14. 1
      modules/setting-management/src/Volo.Abp.SettingManagement.Blazor/Pages/SettingManagement/EmailSettingGroup/EmailSettingGroupViewComponent.razor
  15. 2
      npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts
  16. 2
      npm/ng-packs/apps/dev-app/src/app/app.module.ts
  17. 5
      npm/ng-packs/apps/dev-app/src/app/home/home.component.ts
  18. 16
      npm/ng-packs/packages/account/src/lib/components/login/login.component.ts
  19. 29
      npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts
  20. 22
      npm/ng-packs/packages/core/src/lib/services/internet-connection-service.ts
  21. 36
      npm/ng-packs/packages/core/src/lib/services/router-events.service.ts
  22. 8
      npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts
  23. 5
      npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.html
  24. 60
      npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts
  25. 83
      npm/ng-packs/packages/theme-shared/src/lib/components/internet-connection-status/internet-connection-status.component.ts
  26. 14
      npm/ng-packs/packages/theme-shared/src/lib/constants/default-errors.ts
  27. 4
      npm/ng-packs/packages/theme-shared/src/lib/constants/index.ts
  28. 2
      npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts
  29. 17
      npm/ng-packs/packages/theme-shared/src/lib/services/create-error-component.service.ts
  30. 5
      npm/ng-packs/packages/theme-shared/src/public-api.ts
  31. 1
      templates/app-nolayers/angular/src/app/app.component.ts
  32. 3
      templates/app-nolayers/angular/src/app/app.module.ts
  33. 1
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.css
  34. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.js
  35. 4
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Components/App.razor
  36. 1
      templates/app/angular/src/app/app.component.ts
  37. 3
      templates/app/angular/src/app/app.module.ts
  38. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/wwwroot/global.css
  39. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/wwwroot/global.js
  40. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/wwwroot/global.css
  41. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/wwwroot/global.js
  42. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/wwwroot/global.css
  43. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/wwwroot/global.js
  44. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/App.razor
  45. 1
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/wwwroot/global.css
  46. 2
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/wwwroot/global.js
  47. 4
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/App.razor

10
Directory.Packages.props

@ -115,11 +115,11 @@
<PackageVersion Include="NUglify" Version="1.21.0" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Octokit" Version="9.0.0" />
<PackageVersion Include="OpenIddict.Abstractions" Version="5.4.0" />
<PackageVersion Include="OpenIddict.Core" Version="5.4.0" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="5.4.0" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="5.4.0" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="5.4.0" />
<PackageVersion Include="OpenIddict.Abstractions" Version="5.2.0" />
<PackageVersion Include="OpenIddict.Core" Version="5.2.0" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="5.2.0" />
<PackageVersion Include="OpenIddict.Validation.AspNetCore" Version="5.2.0" />
<PackageVersion Include="OpenIddict.Validation.ServerIntegration" Version="5.2.0" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="8.21.121" />
<PackageVersion Include="Polly" Version="8.2.0" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />

3
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -620,6 +620,7 @@
"SelectCountryPlaceholder": "Country",
"DeleteImageConfirmationMessage": "Are you sure you want to delete the image for \"{0}\"?",
"DeleteImageSuccessMessage": "Image successfully deleted",
"DeleteImage": "Delete Image"
"DeleteImage": "Delete Image",
"NetTerms": "Terms (Days)"
}
}

38
docs/en/UI/Blazor/Block-Busy.md

@ -0,0 +1,38 @@
# Blazor UI: Block/Busy Service
UI Block disables (blocks) the page or a part of the page.
## Quick Example
Simply [inject](../../Dependency-Injection.md) `IBlockUiService` to your page or component and call the `Block/UnBlock` method to disables (blocks) the element.
```csharp
namespace MyProject.Blazor.Pages
{
public partial class Index
{
private readonly IBlockUiService _blockUiService;
public Index(IBlockUiService _blockUiService)
{
_blockUiService = blockUiService;
}
public async Task BlockForm()
{
/*
Parameters of Block method:
selectors: A string containing one or more selectors to match. https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector#selectors
busy : Set to true to show a progress indicator on the blocked area.
*/
await _blockUiService.Block(selectors: "#MySelectors", busy: true);
}
}
}
```
The resulting UI will look like below:
![ui-busy](../../images/ui-busy.png)
Then you can use `_blockUiService.UnBlock()` to re-enable the busy area/page.

4
docs/en/docs-nav.json

@ -958,6 +958,10 @@
{
"text": "Page Progress",
"path": "UI/Blazor/Page-Progress.md"
},
{
"text": "Block/Busy",
"path": "UI/Blazor/Block-Busy.md"
}
]
},

2
framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming/ComponentsComponentsBundleContributor.cs

@ -23,7 +23,7 @@ public class ComponentsComponentsBundleContributor : IBundleContributor
Source = "_content/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming/libs/fontawesome/css/all.css"
});
}
context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/css/abp.css");
context.Add("_content/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming/libs/flag-icon/css/flag-icon.css");
context.Add("_content/Blazorise/blazorise.css");
context.Add("_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css");

1
framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalStyleContributor.cs

@ -14,6 +14,7 @@ public class BlazorGlobalStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/css/abp.css");
context.Files.AddIfNotContains("/_content/Blazorise/blazorise.css");
context.Files.AddIfNotContains("/_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css");
context.Files.AddIfNotContains("/_content/Blazorise.Snackbar/blazorise.snackbar.css");

27
framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/BlockUi/AbpBlockUiService.cs

@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Microsoft.JSInterop;
using Volo.Abp.AspNetCore.Components.BlockUi;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Components.Web.BlockUi;
[Dependency(ReplaceServices = true)]
public class AbpBlockUiService : IBlockUiService, IScopedDependency
{
public IJSRuntime JsRuntime { get; }
public AbpBlockUiService(IJSRuntime jsRuntime)
{
JsRuntime = jsRuntime;
}
public async Task Block(string? selectors, bool busy = false)
{
await JsRuntime.InvokeVoidAsync("abp.ui.block", selectors, busy);
}
public async Task UnBlock()
{
await JsRuntime.InvokeVoidAsync("abp.ui.unblock");
}
}

56
framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/css/abp.css

@ -0,0 +1,56 @@
@keyframes spin {
0% {
transform: translateZ(0) rotate(0deg);
}
100% {
transform: translateZ(0) rotate(360deg);
}
}
.abp-block-area {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 102;
background-color: #fff !important;
opacity: .8;
transition: opacity .25s;
}
.abp-block-area.abp-block-area-disappearing {
opacity: 0;
}
.abp-block-area.abp-block-area-busy:after {
content: attr(data-text);
display: block;
max-width: 125px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
font-family: sans-serif;
color: #343a40;
text-align: center;
text-transform: uppercase;
}
.abp-block-area.abp-block-area-busy:before {
content: "";
display: block;
width: 150px;
height: 150px;
border-radius: 50%;
border-width: 2px;
border-style: solid;
border-color: transparent #228ae6 #228ae6 #228ae6;
position: absolute;
top: calc(50% - 75px);
left: calc(50% - 75px);
will-change: transform;
animation: spin .75s infinite ease-in-out;
}

48
framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/abp.js

@ -182,4 +182,52 @@ var abp = abp || {};
}
}
}
/* UI *******************************************************/
abp.ui = abp.ui || {};
/* UI BLOCK */
//Defines UI Block API and implements basically
var $abpBlockArea = document.createElement('div');
$abpBlockArea.classList.add('abp-block-area');
/* opts: { //Can be an object with options or a string for query a selector
* elm: a query selector (optional - default: document.body)
* busy: boolean (optional - default: false)
* }
*/
abp.ui.block = function (elm, busy) {
var $elm = document.querySelector(elm) || document.body;
if (busy) {
$abpBlockArea.classList.add('abp-block-area-busy');
} else {
$abpBlockArea.classList.remove('abp-block-area-busy');
}
if (document.querySelector(elm)) {
$abpBlockArea.style.position = 'absolute';
} else {
$abpBlockArea.style.position = 'fixed';
}
$elm.appendChild($abpBlockArea);
};
abp.ui.unblock = function () {
var element = document.querySelector('.abp-block-area');
if (element) {
element.classList.add('abp-block-area-disappearing');
setTimeout(function () {
if (element) {
element.classList.remove('abp-block-area-disappearing');
if (element.parentElement) {
element.parentElement.removeChild(element);
}
}
}, 250);
}
};
})();

1
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs

@ -25,6 +25,7 @@ public class ComponentsComponentsBundleContributor : IBundleContributor
});
}
context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/css/abp.css");
context.Add("_content/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/libs/flag-icon/css/flag-icon.css");
context.Add("_content/Blazorise/blazorise.css");
context.Add("_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css");

10
framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/BlockUi/IBlockUiService.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Components.BlockUi;
public interface IBlockUiService
{
Task Block(string? selectors, bool busy = false);
Task UnBlock();
}

17
framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/BlockUi/NullBlockUiService.cs

@ -0,0 +1,17 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Components.BlockUi;
public class NullBlockUiService : IBlockUiService, ISingletonDependency
{
public Task Block(string? selectors, bool busy = false)
{
return Task.CompletedTask;
}
public Task UnBlock()
{
return Task.CompletedTask;
}
}

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs

@ -106,16 +106,9 @@ public class BundlingService : IBundlingService, ITransientDependency
if (!bundleConfig.InteractiveAuto)
{
string fileName;
if (bundleConfig.IsBlazorWebApp)
{
var projectDirectory = Path.GetDirectoryName(directory);
fileName = Directory.GetFiles(projectDirectory!, "App.razor", SearchOption.AllDirectories).FirstOrDefault();
}
else
{
fileName = Path.Combine(PathHelper.GetWwwRootPath(directory), "index.html");
}
var fileName = bundleConfig.IsBlazorWebApp
? Directory.GetFiles(Path.GetDirectoryName(projectFilePath)!.Replace(".Client", ""), "App.razor", SearchOption.AllDirectories).FirstOrDefault()
: Path.Combine(PathHelper.GetWwwRootPath(directory), "index.html");
await UpdateDependenciesInBlazorFileAsync(fileName, styleDefinitions, scriptDefinitions);

1
modules/setting-management/src/Volo.Abp.SettingManagement.Blazor/Pages/SettingManagement/EmailSettingGroup/EmailSettingGroupViewComponent.razor

@ -90,6 +90,7 @@
</Column>
</Row>
</Validations>
<hr />
<Row>
<Column>
@if (HasSendTestEmailPermission)

2
npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts

@ -31,4 +31,4 @@ const routes: Routes = [
imports: [RouterModule.forRoot(routes, {})],
exports: [RouterModule],
})
export class AppRoutingModule { }
export class AppRoutingModule {}

2
npm/ng-packs/apps/dev-app/src/app/app.module.ts

@ -39,7 +39,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
AccountLayoutModule.forRoot(),
InternetConnectionStatusComponent
InternetConnectionStatusComponent,
],
providers: [APP_ROUTE_PROVIDER],
declarations: [AppComponent],

5
npm/ng-packs/apps/dev-app/src/app/home/home.component.ts

@ -1,17 +1,18 @@
import { AuthService } from '@abp/ng.core';
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
protected readonly authService = inject(AuthService);
loading = false;
get hasLoggedIn(): boolean {
return this.authService.isAuthenticated;
}
constructor(private authService: AuthService) {}
login() {
this.loading = true;
this.authService.navigateToLogin();

16
npm/ng-packs/packages/account/src/lib/components/login/login.component.ts

@ -1,6 +1,6 @@
import { AuthService, ConfigStateService } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, Injector, OnInit } from '@angular/core';
import { Component, Injector, OnInit, inject } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { throwError } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
@ -14,6 +14,12 @@ const { maxLength, required } = Validators;
templateUrl: './login.component.html',
})
export class LoginComponent implements OnInit {
protected injector = inject(Injector);
protected fb = inject(UntypedFormBuilder);
protected toasterService = inject(ToasterService);
protected authService = inject(AuthService);
protected configState = inject(ConfigStateService);
form!: UntypedFormGroup;
inProgress?: boolean;
@ -22,14 +28,6 @@ export class LoginComponent implements OnInit {
authWrapperKey = eAccountComponents.AuthWrapper;
constructor(
protected injector: Injector,
protected fb: UntypedFormBuilder,
protected toasterService: ToasterService,
protected authService: AuthService,
protected configState: ConfigStateService,
) {}
ngOnInit() {
this.init();
this.buildForm();

29
npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts

@ -11,6 +11,7 @@ import { SubscriptionService } from '../services/subscription.service';
import { findRoute, getRoutePath } from '../utils/route-utils';
import { TreeNode } from '../utils/tree-utils';
import { DYNAMIC_LAYOUTS_TOKEN } from '../tokens/dynamic-layout.token';
import { EnvironmentService } from '../services';
@Component({
selector: 'abp-dynamic-layout',
@ -27,13 +28,15 @@ export class DynamicLayoutComponent implements OnInit {
readonly layouts = inject(DYNAMIC_LAYOUTS_TOKEN);
isLayoutVisible = true;
private readonly router = inject(Router);
private readonly route = inject(ActivatedRoute);
private readonly routes = inject(RoutesService);
private localizationService = inject(LocalizationService);
private replaceableComponents = inject(ReplaceableComponentsService);
private subscription = inject(SubscriptionService);
private routerEvents = inject(RouterEvents);
protected readonly router = inject(Router);
protected readonly route = inject(ActivatedRoute);
protected readonly routes = inject(RoutesService);
protected readonly localizationService = inject(LocalizationService);
protected readonly replaceableComponents = inject(ReplaceableComponentsService);
protected readonly subscription = inject(SubscriptionService);
protected readonly routerEvents = inject(RouterEvents);
protected readonly environment = inject(EnvironmentService);
constructor(@Optional() @SkipSelf() dynamicLayoutComponent: DynamicLayoutComponent) {
if (dynamicLayoutComponent) {
@ -43,12 +46,16 @@ export class DynamicLayoutComponent implements OnInit {
this.checkLayoutOnNavigationEnd();
this.listenToLanguageChange();
}
ngOnInit(): void {
if (this.layout) {
return;
}
this.getLayout();
const { oAuthConfig } = this.environment.getEnvironment();
if (oAuthConfig.responseType === 'code') {
this.getLayout();
}
}
private checkLayoutOnNavigationEnd() {
@ -77,10 +84,6 @@ export class DynamicLayoutComponent implements OnInit {
const routeData = this.route.snapshot.data || {};
let expectedLayout = routeData['layout'] as eLayoutType;
if (expectedLayout) {
return expectedLayout;
}
let node = findRoute(this.routes, getRoutePath(this.router));
node = { parent: node } as TreeNode<ABP.Route>;

22
npm/ng-packs/packages/core/src/lib/services/internet-connection-service.ts

@ -5,28 +5,28 @@ import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class InternetConnectionService{
readonly document = inject(DOCUMENT)
export class InternetConnectionService {
readonly document = inject(DOCUMENT);
readonly window = this.document.defaultView;
readonly navigator = this.window.navigator;
private status$ = new BehaviorSubject<boolean>(this.navigator.onLine)
private status$ = new BehaviorSubject<boolean>(this.navigator.onLine);
private status = signal(this.navigator.onLine);
networkStatus = computed(() => this.status())
constructor(){
networkStatus = computed(() => this.status());
constructor() {
this.window.addEventListener('offline', () => this.setStatus(false));
this.window.addEventListener('online', () => this.setStatus(true));
}
setStatus(val:boolean){
this.status.set(val)
this.status$.next(val)
setStatus(val: boolean) {
this.status.set(val);
this.status$.next(val);
}
get networkStatus$(){
return this.status$.asObservable()
get networkStatus$() {
return this.status$.asObservable();
}
}

36
npm/ng-packs/packages/core/src/lib/services/router-events.service.ts

@ -1,4 +1,4 @@
import { Injectable, Type } from '@angular/core';
import { Injectable, Type, inject, signal } from '@angular/core';
import {
NavigationCancel,
NavigationEnd,
@ -6,8 +6,10 @@ import {
NavigationStart,
Router,
RouterEvent,
Event
Event,
RouterState,
} from '@angular/router';
import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
export const NavigationEvent = {
@ -19,12 +21,34 @@ export const NavigationEvent = {
@Injectable({ providedIn: 'root' })
export class RouterEvents {
constructor(private router: Router) {}
protected readonly router = inject(Router);
getEvents<T extends RouterEventConstructors>(...eventTypes: T) {
readonly #previousNavigation = signal<string | undefined>(undefined);
previousNavigation = this.#previousNavigation.asReadonly();
readonly #currentNavigation = signal<string | undefined>(undefined);
currentNavigation = this.#currentNavigation.asReadonly();
constructor() {
this.listenToNavigation();
}
const filterRouterEvents = (event: Event) =>
eventTypes.some(type => event instanceof type);
protected listenToNavigation(): void {
(
this.router.events.pipe(
filter(e => e instanceof NavigationEvent.End && !e.url.includes('error')),
) as Observable<NavigationEnd>
).subscribe(event => {
// It must be "NavigationTransition" but it is not exported in Angular
//https://github.com/angular/angular/blob/9c486c96827a9282cbdbff176761bc95554a260b/packages/router/src/navigation_transition.ts#L282
this.#previousNavigation.set(this.#currentNavigation());
this.#currentNavigation.set(event.url);
});
}
getEvents<T extends RouterEventConstructors>(...eventTypes: T) {
const filterRouterEvents = (event: Event) => eventTypes.some(type => event instanceof type);
return this.router.events.pipe(filter(filterRouterEvents));
}

8
npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts

@ -1,9 +1,8 @@
import {eLayoutType, SubscriptionService} from '@abp/ng.core';
import { eLayoutType, SubscriptionService } from '@abp/ng.core';
import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared';
import {AfterViewInit, Component} from '@angular/core';
import { AfterViewInit, Component, inject } from '@angular/core';
import { LayoutService } from '../../services/layout.service';
@Component({
selector: 'abp-layout-application',
templateUrl: './application-layout.component.html',
@ -11,11 +10,10 @@ import { LayoutService } from '../../services/layout.service';
providers: [LayoutService, SubscriptionService],
})
export class ApplicationLayoutComponent implements AfterViewInit {
public readonly service = inject(LayoutService);
// required for dynamic component
static type = eLayoutType.application;
constructor(public service: LayoutService) {}
ngAfterViewInit() {
this.service.subscribeWindowSize();
}

5
npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.html

@ -18,10 +18,7 @@
</div>
<div class="error-actions">
@if (isHomeShow) {
<a
(click)="destroy()"
routerLink="/"
class="btn btn-primary btn-md mt-2"
<a (click)="goHome()" class="btn btn-primary btn-md mt-2"
><span class="glyphicon glyphicon-home"></span>
{{ { key: '::Menu:Home', defaultValue: 'Home' } | abpLocalization }}
</a>

60
npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts

@ -1,38 +1,40 @@
import {
ApplicationRef,
Component,
Injector,
inject,
OnInit,
ComponentFactoryResolver,
ElementRef,
EmbeddedViewRef,
Type,
ViewChild,
AfterViewInit,
OnDestroy,
createComponent,
EnvironmentInjector,
DestroyRef,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DOCUMENT } from '@angular/common';
import { Router } from '@angular/router';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, filter } from 'rxjs/operators';
import { LocalizationParam, SubscriptionService } from '@abp/ng.core';
import { LocalizationParam } from '@abp/ng.core';
import { ErrorScreenErrorCodes } from '../../models';
@Component({
selector: 'abp-http-error-wrapper',
templateUrl: './http-error-wrapper.component.html',
styleUrls: ['http-error-wrapper.component.scss'],
providers: [SubscriptionService],
})
export class HttpErrorWrapperComponent implements OnInit, AfterViewInit, OnDestroy {
protected readonly destroyRef = inject(DestroyRef);
protected readonly document = inject(DOCUMENT);
protected readonly window = this.document.defaultView;
protected readonly router = inject(Router);
appRef!: ApplicationRef;
cfRes!: ComponentFactoryResolver;
injector!: Injector;
environmentInjector!: EnvironmentInjector;
status: ErrorScreenErrorCodes = 0;
@ -57,21 +59,32 @@ export class HttpErrorWrapperComponent implements OnInit, AfterViewInit, OnDestr
return this.status ? `[${this.status}]` : '';
}
constructor(private subscription: SubscriptionService) {}
ngOnInit(): void {
this.backgroundColor =
this.window.getComputedStyle(this.document.body)?.getPropertyValue('background-color') || '#fff';
const computedStyle = this.window.getComputedStyle(this.document.body);
const backgroundColor = computedStyle?.getPropertyValue('background-color');
this.backgroundColor = backgroundColor || '#fff';
}
ngAfterViewInit(): void {
if (this.customComponent) {
const customComponentRef = this.cfRes
.resolveComponentFactory(this.customComponent)
.create(this.injector);
const customComponentRef = createComponent(this.customComponent, {
environmentInjector: this.environmentInjector,
});
customComponentRef.instance.errorStatus = this.status;
//In our custom "HttpErrorComponent", we have a "status" property.
//We used to have "errorStatus", but it wasn't signal type. "status" variable is signal type.
//I've checked because of backward compatibility. Developers might have their own custom HttpErrorComponent.
//We need to deprecated and remove "errorStatus" in the future.
if (customComponentRef.instance.status) {
customComponentRef.instance.status.set(this.status);
}
customComponentRef.instance.destroy$ = this.destroy$;
this.appRef.attachView(customComponentRef.hostView);
if (this.containerRef) {
this.containerRef.nativeElement.appendChild(
(customComponentRef.hostView as EmbeddedViewRef<any>).rootNodes[0],
@ -80,14 +93,17 @@ export class HttpErrorWrapperComponent implements OnInit, AfterViewInit, OnDestr
customComponentRef.changeDetectorRef.detectChanges();
}
const keyup$ = fromEvent<KeyboardEvent>(this.document, 'keyup').pipe(
debounceTime(150),
filter((key: KeyboardEvent) => key && key.key === 'Escape'),
);
this.subscription.addOne(keyup$, () => this.destroy());
fromEvent<KeyboardEvent>(this.document, 'keyup')
.pipe(
debounceTime(150),
filter((key: KeyboardEvent) => key && key.key === 'Escape'),
takeUntilDestroyed(this.destroyRef),
)
.subscribe(() => this.destroy());
}
ngOnDestroy(): void {
goHome(): void {
this.router.navigateByUrl('/', { onSameUrlNavigation: 'reload' });
this.destroy();
}
@ -95,4 +111,8 @@ export class HttpErrorWrapperComponent implements OnInit, AfterViewInit, OnDestr
this.destroy$.next();
this.destroy$.complete();
}
ngOnDestroy(): void {
this.destroy();
}
}

83
npm/ng-packs/packages/theme-shared/src/lib/components/internet-connection-status/internet-connection-status.component.ts

@ -1,47 +1,60 @@
import { Component, computed, inject } from '@angular/core';
import { NgIf } from '@angular/common'
import { InternetConnectionService , LocalizationModule } from '@abp/ng.core';
import { Component, inject } from '@angular/core';
import { InternetConnectionService, LocalizationModule } from '@abp/ng.core';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'abp-internet-status',
standalone: true,
imports:[NgIf, LocalizationModule],
imports: [LocalizationModule, NgbTooltip],
template: `
<div class="status-icon" *ngIf="!isOnline()">
<i data-toggle="tooltip" title="{{ 'AbpUi::InternetConnectionInfo' | abpLocalization }}" data-placement="left" class="fa fa-circle text-blinking blink">
</i>
</div>
`,
styles: [`
.blink {
animation: blinker 0.9s cubic-bezier(.5, 0, 1, 1) infinite alternate;
}
@keyframes blinker {
0% { color:#c1c1c1 }
70% { color: #DC3545 }
100% { color: #DC3545 }
}
.text-blinking{
font-size:1.2rem;
@if (!isOnline()) {
<div class="status-icon">
<i
ngbTooltip="{{ 'AbpUi::InternetConnectionInfo' | abpLocalization }}"
container="body"
placement="left-top"
class="fa fa-wifi text-blinking blink"
>
</i>
</div>
}
`,
styles: [
`
.blink {
animation: blinker 0.9s cubic-bezier(0.5, 0, 1, 1) infinite alternate;
}
@keyframes blinker {
0% {
color: #c1c1c1;
}
70% {
color: #fa2379;
}
100% {
color: #fa2379;
}
}
.status-icon{
position: fixed;
z-index: 999999;
top: 10px;
right: 10px;
}
.text-blinking {
font-size: 30px;
}
@media (width < 768px){
.status-icon{
top: 26px;
right: 134px;
.status-icon {
position: fixed;
z-index: 999999;
top: 50%;
left: 50%;
width: 30px;
text-align: center;
margin-left: -15px;
margin-top: -15px;
translate: transform(-50%, -50%);
}
}
`]
`,
],
})
export class InternetConnectionStatusComponent{
export class InternetConnectionStatusComponent {
internetConnectionService = inject(InternetConnectionService);
isOnline = this.internetConnectionService.networkStatus
isOnline = this.internetConnectionService.networkStatus;
}

14
npm/ng-packs/packages/theme-shared/src/lib/constants/default-errors.ts

@ -51,3 +51,17 @@ export const CUSTOM_HTTP_ERROR_HANDLER_PRIORITY = Object.freeze({
high: 9,
veryHigh: 99,
});
export const HTTP_ERROR_STATUS = {
'401': 'AbpUi::401Message',
'403': 'AbpUi::403Message',
'404': 'AbpUi::404Message',
'500': 'AbpUi::500Message',
};
export const HTTP_ERROR_DETAIL = {
'401': 'AbpUi::DefaultErrorMessage401Detail',
'403': 'AbpUi::DefaultErrorMessage403Detail',
'404': 'AbpUi::DefaultErrorMessage404Detail',
'500': 'AbpUi::DefaultErrorMessage',
};

4
npm/ng-packs/packages/theme-shared/src/lib/constants/index.ts

@ -0,0 +1,4 @@
export * from './validation';
export * from './default-errors';
export * from './styles';
export * from './scripts';

2
npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts

@ -102,7 +102,7 @@ export class NgxDatatableListDirective implements OnChanges, OnInit, DoCheck {
}
protected refreshPageIfDataExist() {
if (this.table.rows.length < 1 && this.table.count > 0) {
if (this.table.rows?.length < 1 && this.table.count > 0) {
let maxPage = Math.floor(Number(this.table.count / this.list.maxResultCount));
if (this.table.count < this.list.maxResultCount) {

17
npm/ng-packs/packages/theme-shared/src/lib/services/create-error-component.service.ts

@ -1,8 +1,9 @@
import {
ApplicationRef,
ComponentFactoryResolver,
ComponentRef,
createComponent,
EmbeddedViewRef,
EnvironmentInjector,
inject,
Injectable,
Injector,
@ -21,9 +22,9 @@ import { ErrorScreenErrorCodes } from '../models/common';
export class CreateErrorComponentService {
protected readonly document = inject(DOCUMENT);
protected readonly rendererFactory = inject(RendererFactory2);
protected readonly cfRes = inject(ComponentFactoryResolver);
protected readonly routerEvents = inject(RouterEvents);
private readonly injector = inject(Injector);
protected readonly injector = inject(Injector);
protected readonly envInjector = inject(EnvironmentInjector);
protected readonly httpErrorConfig = inject(HTTP_ERROR_CONFIG);
componentRef: ComponentRef<HttpErrorWrapperComponent> | null = null;
@ -65,9 +66,9 @@ export class CreateErrorComponentService {
const hostElement = this.getErrorHostElement();
const host = renderer.selectRootElement(hostElement, true);
this.componentRef = this.cfRes
.resolveComponentFactory(HttpErrorWrapperComponent)
.create(this.injector);
this.componentRef = createComponent(HttpErrorWrapperComponent, {
environmentInjector: this.envInjector,
});
for (const key in instance) {
/* istanbul ignore else */
@ -80,9 +81,8 @@ export class CreateErrorComponentService {
const appRef = this.injector.get(ApplicationRef);
if (this.canCreateCustomError(instance.status as ErrorScreenErrorCodes)) {
this.componentRef.instance.cfRes = this.cfRes;
this.componentRef.instance.appRef = appRef;
this.componentRef.instance.injector = this.injector;
this.componentRef.instance.environmentInjector = this.envInjector;
this.componentRef.instance.customComponent = this.httpErrorConfig.errorScreen?.component;
}
@ -91,6 +91,7 @@ export class CreateErrorComponentService {
const destroy$ = new Subject<void>();
this.componentRef.instance.destroy$ = destroy$;
destroy$.subscribe(() => {
this.componentRef?.destroy();
this.componentRef = null;

5
npm/ng-packs/packages/theme-shared/src/public-api.ts

@ -2,11 +2,9 @@
* Public API Surface of theme-shared
*/
export * from './lib/adapters'
export * from './lib/adapters';
export * from './lib/animations';
export * from './lib/components';
export * from './lib/constants/validation';
export * from './lib/constants/default-errors';
export * from './lib/directives';
export * from './lib/enums';
export * from './lib/handlers';
@ -16,3 +14,4 @@ export * from './lib/services';
export * from './lib/theme-shared.module';
export * from './lib/tokens';
export * from './lib/utils';
export * from './lib/constants';

1
templates/app-nolayers/angular/src/app/app.component.ts

@ -5,6 +5,7 @@ import { Component } from '@angular/core';
template: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
<abp-internet-status></abp-internet-status>
`,
})
export class AppComponent {}

3
templates/app-nolayers/angular/src/app/app.module.ts

@ -6,7 +6,7 @@ import { SettingManagementConfigModule } from '@abp/ng.setting-management/config
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x';
import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { InternetConnectionStatusComponent, ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -35,6 +35,7 @@ import { AbpOAuthModule } from '@abp/ng.oauth';
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
FeatureManagementModule.forRoot(),
InternetConnectionStatusComponent
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],

1
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.css

File diff suppressed because one or more lines are too long

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.js

File diff suppressed because one or more lines are too long

4
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Components/App.razor

@ -9,7 +9,7 @@
<base href="/" />
<!--ABP:Styles-->
<link href="global.css?_v=638472371837228870" rel="stylesheet"/>
<link href="global.css?_v=638473198173942238" rel="stylesheet"/>
<link href="main.css" rel="stylesheet"/>
<!--/ABP:Styles-->
<link href="MyCompanyName.MyProjectName.Blazor.styles.css" rel="stylesheet"/>
@ -37,7 +37,7 @@
</div>
<!--ABP:Scripts-->
<script src="global.js?_v=638472371838439398"></script>
<script src="global.js?_v=638473198175165417"></script>
<!--/ABP:Scripts-->
</body>

1
templates/app/angular/src/app/app.component.ts

@ -5,6 +5,7 @@ import { Component } from '@angular/core';
template: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
<abp-internet-status></abp-internet-status>
`,
})
export class AppComponent {}

3
templates/app/angular/src/app/app.module.ts

@ -6,7 +6,7 @@ import { SettingManagementConfigModule } from '@abp/ng.setting-management/config
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x';
import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { InternetConnectionStatusComponent, ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -36,6 +36,7 @@ import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account';
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
FeatureManagementModule.forRoot(),
InternetConnectionStatusComponent
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/wwwroot/global.css

File diff suppressed because one or more lines are too long

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/wwwroot/global.js

File diff suppressed because one or more lines are too long

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/wwwroot/global.css

File diff suppressed because one or more lines are too long

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/wwwroot/global.js

File diff suppressed because one or more lines are too long

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/wwwroot/global.css

File diff suppressed because one or more lines are too long

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/wwwroot/global.js

File diff suppressed because one or more lines are too long

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/App.razor

@ -8,7 +8,7 @@
<base href="/" />
<!--ABP:Styles-->
<link href="global.css?_v=638472371105418474" rel="stylesheet"/>
<link href="global.css?_v=638473199568040519" rel="stylesheet"/>
<link href="main.css" rel="stylesheet"/>
<!--/ABP:Styles-->
<link href="MyCompanyName.MyProjectName.Blazor.Client.styles.css" rel="stylesheet"/>
@ -36,7 +36,7 @@
</div>
<!--ABP:Scripts-->
<script src="global.js?_v=638472371106655051"></script>
<script src="global.js?_v=638473199570937070"></script>
<!--/ABP:Scripts-->
<!-- <TEMPLATE-REMOVE IF-NOT='PWA'> -->

1
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/wwwroot/global.css

File diff suppressed because one or more lines are too long

2
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/wwwroot/global.js

File diff suppressed because one or more lines are too long

4
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/App.razor

@ -8,7 +8,7 @@
<base href="/" />
<!--ABP:Styles-->
<link href="global.css?_v=638472371674955462" rel="stylesheet"/>
<link href="global.css?_v=638473199135168410" rel="stylesheet"/>
<link href="main.css" rel="stylesheet"/>
<!--/ABP:Styles-->
<link href="MyCompanyName.MyProjectName.Blazor.Host.Client.styles.css" rel="stylesheet"/>
@ -30,7 +30,7 @@
</div>
<!--ABP:Scripts-->
<script src="global.js?_v=638472371675508116"></script>
<script src="global.js?_v=638473199135794271"></script>
<!--/ABP:Scripts-->
</body>

Loading…
Cancel
Save