Browse Source

Merge branch 'dev' into update-angular-17

pull/18134/head
Masum ULU 2 years ago
committed by GitHub
parent
commit
4c20357877
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      Directory.Packages.props
  2. 4
      docs/en/Community-Articles/2023-10-23-NET-8-Feature-containers/POST.md
  3. 4
      docs/en/Community-Articles/2023-11-05-EF-Core-8-Complex-Types/POST.MD
  4. 21
      docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/Post.md
  5. BIN
      docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/cover-image.png
  6. 2
      docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/POST.md
  7. 5
      docs/en/Community-Articles/2023-11-97-AOT-Compilation/Post.md
  8. 14
      framework/Volo.Abp.sln
  9. 61
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs
  10. 3
      framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xml
  11. 30
      framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xsd
  12. 28
      framework/src/Volo.Abp.Imaging.SkiaSharp/Volo.Abp.Imaging.SkiaSharp.csproj
  13. 8
      framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/AbpImagingSkiaSharpModule.cs
  14. 98
      framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs
  15. 16
      framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpResizerOptions.cs
  16. 19
      framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo.Abp.Imaging.SkiaSharp.Tests.csproj
  17. 14
      framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingMagickNetTestModule.cs
  18. 11
      framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingSkiaSharpTestModule.cs
  19. 75
      framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/SkiaSharpImageResizerTests.cs
  20. 2
      npm/ng-packs/.gitignore
  21. 7
      npm/ng-packs/packages/components/extensible/src/lib/components/date-time-picker/extensible-date-time-picker.component.ts
  22. 2
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.html
  23. 133
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.ts
  24. 123
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form.component.ts
  25. 51
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts
  26. 5
      npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.ts
  27. 5
      npm/ng-packs/packages/components/extensible/src/lib/components/page-toolbar/page-toolbar.component.ts
  28. 1
      npm/ng-packs/packages/components/extensible/src/lib/directives/prop-data.directive.ts
  29. 35
      npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts
  30. 1
      npm/ng-packs/packages/components/extensible/src/lib/pipes/create-injector.pipe.ts
  31. 77
      npm/ng-packs/packages/components/extensible/src/lib/services/extensible-form-prop.service.ts
  32. 5
      npm/ng-packs/packages/components/page/src/page.module.ts
  33. 8
      npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts
  34. 4
      npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts
  35. 4
      npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts
  36. 11
      npm/ng-packs/packages/theme-shared/src/lib/components/password/password.component.ts
  37. 7
      npm/ng-packs/packages/theme-shared/src/lib/directives/ellipsis.directive.ts
  38. 1
      npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts
  39. 1
      npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts
  40. 11
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  41. 1
      nupkg/common.ps1
  42. 3
      templates/NuGet.Config
  43. 4
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj
  44. 4
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj
  45. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj
  46. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj
  47. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.csproj
  48. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj
  49. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj
  50. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj
  51. 2
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj
  52. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj
  53. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj
  54. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj
  55. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj
  56. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj
  57. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj
  58. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj

3
Directory.Packages.props

@ -140,6 +140,9 @@
<PackageVersion Include="Shouldly" Version="4.0.3" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.0.2" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.0.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.6" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.6" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.6" />
<PackageVersion Include="Slugify.Core" Version="4.0.1" />
<PackageVersion Include="Spectre.Console" Version="0.47.0" />
<PackageVersion Include="StackExchange.Redis" Version="2.6.122" />

4
docs/en/Community-Articles/2023-10-23-NET-8-Feature-containers/POST.md

@ -17,8 +17,8 @@ It's important to note that this change only affects the default port used when
If you want your application to continue using port 80, you can still specify it during the application launch or configure it in the application settings.
* Recommended: Explicitly set the `ASPNETCORE_HTTP_PORTS`, `ASPNETCORE_HTTPS_PORTS``, and `ASPNETCORE_URLS` environment variables to the desired port. Example: `docker run --rm -it -p 9999:80 -e ASPNETCORE_HTTP_PORTS=80 <my-app>``
* Update existing commands and configuration that rely on the expected default port of port 80 to reference port 8080 instead. Example: `docker run --rm -it -p 9999:8080 <my-app>``
* Recommended: Explicitly set the `ASPNETCORE_HTTP_PORTS`, `ASPNETCORE_HTTPS_PORTS`, and `ASPNETCORE_URLS` environment variables to the desired port. Example: `docker run --rm -it -p 9999:80 -e ASPNETCORE_HTTP_PORTS=80 <my-app>`
* Update existing commands and configuration that rely on the expected default port of port 80 to reference port 8080 instead. Example: `docker run --rm -it -p 9999:8080 <my-app>`
> The `dockerfile` of ABP templates has been updated to use port `80`.

4
docs/en/Community-Articles/2023-11-05-EF-Core-8-Complex-Types/POST.MD

@ -105,7 +105,7 @@ And update the database:
dotnet ef database update
````
If you check the fields of the `Customers` table in your dayabase, you will see the following fields:
If you check the fields of the `Customers` table in your database, you will see the following fields:
* `Id`
* `Name`
@ -180,7 +180,7 @@ For more details and examples, see the Microsoft's document in the *References*
You can find the sample project here:
https://github.com/hikalkan/samples/tree/master/EfCoreComplexTypeDemo
[https://github.com/hikalkan/samples/tree/master/EfCoreComplexTypeDemo](https://github.com/hikalkan/samples/tree/master/EfCoreComplexTypeDemo)
## References

21
docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/Post.md

@ -1,6 +1,9 @@
# Blazor's History and Full-stack Web UI
Blazor is a web framework that allows developers to build interactive web applications using .NET instead of JavaScript. The first version of Blazor was **released on May 14, 2020**. Since its initial release, Blazor has evolved with the new versions. Until now, six different versions have been declared. Sometimes, it can be not very clear to see the differences between these approaches. First, let's try to understand these.
![Cover Image](cover-image.png)
Blazor is a web framework that allows developers to build interactive web applications using .NET instead of JavaScript. The first version of Blazor was released on May 14, 2020. Since its initial release, Blazor has evolved with the new versions. Until now, six different versions have been declared. Sometimes, it can be not very clear to see the differences between these approaches. First, let's try to understand these.
* **Blazor-Server**: >> *Loads fast at first* >> In this version, heavy things are done in the server. Browsers are thin clients and download a small page for the first load. The page updates are done via SignalR connection. This was released with .NET Core 3.
* **Blazor WebAssembly (WASM):** >> *Loads slow at first* >> In this version, some binary files are being downloaded to the browser. This approach takes longer initialization time than the "Server" approach. The hard work is done on the browser.
@ -40,7 +43,7 @@ You can switch between two rendering modes and even mix them on the same page. W
## How it works?
## How it works?
### Rendering on Server
@ -50,25 +53,27 @@ You can add `WebComponentRenderMode.Server` to your Blazor components so that th
And sure you can add `WebComponentRenderMode.Server` to your page level, and the complete page will be rendered as a server component. All inputs on this page can work as an interactive server component like SPA mode.
And sure, you can add `WebComponentRenderMode.Server` to your page level, and the complete page will be rendered as a server component. All inputs on this page can work as an interactive server component like SPA mode.
![image-20231106172638604](image-20231106172638604.png)
### Rendering on client
### Rendering on Client
You can switch to WebAssembly mode by writing `WebComponentRenderMode.WebAssembly` attribute to your page. By doing so, the whole page should run interactively using WebAssembly. This time there's no server connection anymore because it loads the binaries (WebAssembly runtimes) at the page load.
![image-20231106173021958](image-20231106173021958.png)
## How it works?
## Enabling the Blazor Fullstack UI?
To enable Blazor Full-stack Web UI, you need to write `net8.0;net7.0-browser` into the `TargetFrameworks` area of your `csproj` file. These two keywords change your app like this; `net8.0` framework renders on the server, and `net7.0-browser` framework renders on the browser.
![image-20231106173411309](image-20231106173411309.png)
## Let the System decide on WebAssembly or Server approach
## Let the System Decide WebAssembly or Server Approach
You can let the system decide whether it uses `WebAssembly` or `Server`. This can be done with the `Auto` mode of the `WebComponentRenderMode`. In this case, it will not load binary files (WebAssembly files) for the initial page that has `WebComponentRenderMode.Server` attribute, but whenever the user navigates to a page that has `WebComponentRenderMode.WebAssembly`, it will download the runtimes. This will allow us to load the initial page very fast, and when we need interactivity, we can switch to `WebAssembly` and wait for the binaries to download. But this download will be done one time because it will be cached.
@ -80,10 +85,10 @@ You can let the system decide whether it uses `WebAssembly` or `Server`. This ca
I summarized the new generation Blazor in a very simple way. This architecture will be useful to everyone who uses Blazor.
---
*Resources:*
* You can check out Dan Roth's GitHub issue 👉 [github.com/dotnet/aspnetcore/issues/46636](https://github.com/dotnet/aspnetcore/issues/46636).
* You can check Dan Roth's GitHub issue 👉 [github.com/dotnet/aspnetcore/issues/46636](https://github.com/dotnet/aspnetcore/issues/46636).
* Steven Sanderson's YouTube video is very good for understanding these concepts 👉 [Blazor United Prototype Video](https://youtu.be/48G_CEGXZZM).

BIN
docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

2
docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/POST.md

@ -76,4 +76,4 @@ await context.SaveChangesAsync();
For more information about hierarchy id, see the following resource:
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#hierarchyid-in-net-and-ef-core
- [https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#hierarchyid-in-net-and-ef-core](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#hierarchyid-in-net-and-ef-core)

5
docs/en/Community-Articles/2023-11-97-AOT-Compilation/Post.md

@ -39,7 +39,7 @@ I have created a simple console application to test the Native AOT Compilation.
| | Size | Speed |
| --- | --- | --- |
| --- | --- | --- |
| .NET 8 <br/>_(Self-Contained, Single File)_ | 65938 kb | 00.0051806 ~5ms |
| .NET 7 AOT (default) | 4452 kb | 00.0029823 ~2ms |
| .NET 8 AOT (default) | 1242 kb | 00.0028638 ~2ms |
@ -65,11 +65,8 @@ Always consider the specific needs and constraints of your project before decidi
Native AOT Compilation is a great feature that improves the performance of .NET applications. It's still in early-stages and not all libraries support it yet. But it's a great beginning for the future of .NET 🚀
## Links
- Native AOT deployment overview - .NET | Microsoft Learn. https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/.
-
- Optimize AOT deployments https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/optimizing
-
- What's new in .NET 8 | Microsoft Learn. https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8.

14
framework/Volo.Abp.sln

@ -459,6 +459,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Imaging.AspNetCore
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Maui.Client", "src\Volo.Abp.Maui.Client\Volo.Abp.Maui.Client.csproj", "{F19A6E0C-F719-4ED9-A024-14E4B8D40883}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp", "src\Volo.Abp.Imaging.SkiaSharp\Volo.Abp.Imaging.SkiaSharp.csproj", "{198683D0-7DC6-40F2-B81B-8E446E70A9DE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp.Tests", "test\Volo.Abp.Imaging.SkiaSharp.Tests\Volo.Abp.Imaging.SkiaSharp.Tests.csproj", "{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1369,6 +1373,14 @@ Global
{F19A6E0C-F719-4ED9-A024-14E4B8D40883}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F19A6E0C-F719-4ED9-A024-14E4B8D40883}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F19A6E0C-F719-4ED9-A024-14E4B8D40883}.Release|Any CPU.Build.0 = Release|Any CPU
{198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Release|Any CPU.Build.0 = Release|Any CPU
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1600,6 +1612,8 @@ Global
{62B2B8C9-8F24-4D31-894F-C1F0728D32AB} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{983B0136-384B-4439-B374-31111FFAA286} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{F19A6E0C-F719-4ED9-A024-14E4B8D40883} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{198683D0-7DC6-40F2-B81B-8E446E70A9DE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

61
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs

@ -2,11 +2,11 @@ using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public class BundleFile
public class BundleFile : IEquatable<BundleFile>, IComparable<BundleFile>
{
public string FileName { get; set; }
public string FileName { get; }
public bool IsExternalFile { get; set; }
public bool IsExternalFile { get; }
public BundleFile(string fileName)
{
@ -28,4 +28,59 @@ public class BundleFile
{
return new BundleFile(fileName);
}
public bool Equals(BundleFile? other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return FileName == other.FileName;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != this.GetType())
{
return false;
}
return Equals((BundleFile)obj);
}
public override int GetHashCode()
{
return FileName.GetHashCode();
}
public int CompareTo(BundleFile? other)
{
if (ReferenceEquals(this, other))
{
return 0;
}
if (ReferenceEquals(null, other))
{
return 1;
}
return string.Compare(FileName, other.FileName, StringComparison.Ordinal);
}
}

3
framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

28
framework/src/Volo.Abp.Imaging.SkiaSharp/Volo.Abp.Imaging.SkiaSharp.csproj

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<PackageId>Volo.Abp.Imaging.SkiaSharp</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Imaging.Abstractions\Volo.Abp.Imaging.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Condition="$([MSBuild]::IsOSPlatform('Linux'))" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Condition="$([MSBuild]::IsOSPlatform('OSX'))" />
</ItemGroup>
</Project>

8
framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/AbpImagingSkiaSharpModule.cs

@ -0,0 +1,8 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Imaging;
[DependsOn(typeof(AbpImagingAbstractionsModule))]
public class AbpImagingSkiaSharpModule : AbpModule
{
}

98
framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs

@ -0,0 +1,98 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using SkiaSharp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
namespace Volo.Abp.Imaging;
public class SkiaSharpImageResizerContributor : IImageResizerContributor, ITransientDependency
{
protected SkiaSharpResizerOptions Options { get; }
public SkiaSharpImageResizerContributor(IOptions<SkiaSharpResizerOptions> options)
{
Options = options.Value;
}
public virtual async Task<ImageResizeResult<byte[]>> TryResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs, string? mimeType = null, CancellationToken cancellationToken = default)
{
if (!mimeType.IsNullOrWhiteSpace() && !CanResize(mimeType))
{
return new ImageResizeResult<byte[]>(bytes, ImageProcessState.Unsupported);
}
using (var memoryStream = new MemoryStream(bytes))
{
var result = await TryResizeAsync(memoryStream, resizeArgs, mimeType, cancellationToken);
if (result.State != ImageProcessState.Done)
{
return new ImageResizeResult<byte[]>(bytes, result.State);
}
var newBytes = await result.Result.GetAllBytesAsync(cancellationToken);
result.Result.Dispose();
return new ImageResizeResult<byte[]>(newBytes, result.State);
}
}
public virtual async Task<ImageResizeResult<Stream>> TryResizeAsync(Stream stream, ImageResizeArgs resizeArgs, string? mimeType = null, CancellationToken cancellationToken = default)
{
if (!mimeType.IsNullOrWhiteSpace() && !CanResize(mimeType))
{
return new ImageResizeResult<Stream>(stream, ImageProcessState.Unsupported);
}
var (memoryBitmapStream, memorySkCodecStream) = await CreateMemoryStream(stream);
using (var original = SKBitmap.Decode(memoryBitmapStream))
{
using (var resized = original.Resize(new SKImageInfo(resizeArgs.Width, resizeArgs.Height), Options.SKFilterQuality))
{
using (var image = SKImage.FromBitmap(resized))
{
using (var codec = SKCodec.Create(memorySkCodecStream))
{
var memoryStream = new MemoryStream();
image.Encode(codec.EncodedFormat, Options.Quality).SaveTo(memoryStream);
return new ImageResizeResult<Stream>(memoryStream, ImageProcessState.Done);
}
}
}
}
}
protected virtual async Task<(MemoryStream, MemoryStream)> CreateMemoryStream(Stream stream)
{
var streamPosition = stream.Position;
var memoryBitmapStream = new MemoryStream();
var memorySkCodecStream = new MemoryStream();
await stream.CopyToAsync(memoryBitmapStream);
stream.Position = streamPosition;
await stream.CopyToAsync(memorySkCodecStream);
stream.Position = streamPosition;
memoryBitmapStream.Position = 0;
memorySkCodecStream.Position = 0;
return (memoryBitmapStream, memorySkCodecStream);
}
protected virtual bool CanResize(string? mimeType)
{
return mimeType switch {
MimeTypes.Image.Jpeg => true,
MimeTypes.Image.Png => true,
MimeTypes.Image.Webp => true,
_ => false
};
}
}

16
framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpResizerOptions.cs

@ -0,0 +1,16 @@
using SkiaSharp;
namespace Volo.Abp.Imaging;
public class SkiaSharpResizerOptions
{
public SKFilterQuality SKFilterQuality { get; set; }
public int Quality { get; set; }
public SkiaSharpResizerOptions()
{
SKFilterQuality = SKFilterQuality.None;
Quality = 75;
}
}

19
framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo.Abp.Imaging.SkiaSharp.Tests.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<ProjectReference Include="..\Volo.Abp.Imaging.Abstractions.Tests\Volo.Abp.Imaging.Abstractions.Tests.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Imaging.SkiaSharp\Volo.Abp.Imaging.SkiaSharp.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\Imaging\Files\**" />
</ItemGroup>
</Project>

14
framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingMagickNetTestModule.cs

@ -0,0 +1,14 @@
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.Imaging;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpImagingSkiaSharpModule),
typeof(AbpTestBaseModule)
)]
public class AbpImagingSkiaSharpTestModule : AbpModule
{
}

11
framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingSkiaSharpTestModule.cs

@ -0,0 +1,11 @@
using Volo.Abp.Testing;
namespace Volo.Abp.Imaging;
public abstract class AbpImagingSkiaSharpTestBase : AbpIntegratedTest<AbpImagingSkiaSharpTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}

75
framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/SkiaSharpImageResizerTests.cs

@ -0,0 +1,75 @@
using System.IO;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.Imaging;
public class SkiaSharpImageResizerTests : AbpImagingSkiaSharpTestBase
{
public IImageResizer ImageResizer { get; }
public SkiaSharpImageResizerTests()
{
ImageResizer = GetRequiredService<IImageResizer>();
}
[Fact]
public async Task Should_Resize_Jpg()
{
await using var jpegImage = ImageFileHelper.GetJpgTestFileStream();
var resizedImage = await ImageResizer.ResizeAsync(jpegImage, new ImageResizeArgs(100, 100));
resizedImage.ShouldNotBeNull();
resizedImage.State.ShouldBe(ImageProcessState.Done);
resizedImage.Result.Length.ShouldBeLessThan(jpegImage.Length);
resizedImage.Result.Dispose();
}
[Fact]
public async Task Should_Resize_Png()
{
await using var pngImage = ImageFileHelper.GetPngTestFileStream();
var resizedImage = await ImageResizer.ResizeAsync(pngImage, new ImageResizeArgs(100, 100));
resizedImage.ShouldNotBeNull();
resizedImage.State.ShouldBe(ImageProcessState.Done);
resizedImage.Result.Length.ShouldBeLessThan(pngImage.Length);
resizedImage.Result.Dispose();
}
[Fact]
public async Task Should_Resize_Webp()
{
await using var webpImage = ImageFileHelper.GetWebpTestFileStream();
var resizedImage = await ImageResizer.ResizeAsync(webpImage, new ImageResizeArgs(100, 100));
resizedImage.ShouldNotBeNull();
resizedImage.State.ShouldBe(ImageProcessState.Done);
resizedImage.Result.Length.ShouldBeLessThan(webpImage.Length);
resizedImage.Result.Dispose();
}
[Fact]
public async Task Should_Resize_Stream_And_Byte_Array_The_Same()
{
await using var jpegImage = ImageFileHelper.GetJpgTestFileStream();
var resizedImage1 = await ImageResizer.ResizeAsync(jpegImage, new ImageResizeArgs(100, 100));
var resizedImage2 = await ImageResizer.ResizeAsync(await jpegImage.GetAllBytesAsync(), new ImageResizeArgs(100, 100));
resizedImage1.ShouldNotBeNull();
resizedImage1.State.ShouldBe(ImageProcessState.Done);
resizedImage1.Result.Length.ShouldBeLessThan(jpegImage.Length);
resizedImage2.ShouldNotBeNull();
resizedImage2.State.ShouldBe(ImageProcessState.Done);
resizedImage2.Result.LongLength.ShouldBeLessThan(jpegImage.Length);
resizedImage1.Result.Length.ShouldBe(resizedImage2.Result.LongLength);
resizedImage1.Result.Dispose();
}
}

2
npm/ng-packs/.gitignore

@ -57,4 +57,4 @@ Thumbs.db
.angular
.nx/cache
.nx/

7
npm/ng-packs/packages/components/extensible/src/lib/components/date-time-picker/extensible-date-time-picker.component.ts

@ -7,19 +7,24 @@ import {
SkipSelf,
ViewChild,
} from '@angular/core';
import { ControlContainer } from '@angular/forms';
import { ControlContainer, ReactiveFormsModule } from '@angular/forms';
import {
NgbDateAdapter,
NgbDatepickerModule,
NgbInputDatepicker,
NgbTimeAdapter,
NgbTimepicker,
NgbTimepickerModule,
} from '@ng-bootstrap/ng-bootstrap';
import { FormProp } from '../../models/form-props';
import { selfFactory } from '../../utils/factory.util';
import { DateTimeAdapter } from '@abp/ng.theme.shared';
import { CommonModule } from '@angular/common';
@Component({
exportAs: 'abpExtensibleDateTimePicker',
standalone: true,
imports: [CommonModule, NgbDatepickerModule, ReactiveFormsModule, NgbTimepickerModule],
selector: 'abp-extensible-date-time-picker',
template: `
<input

2
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.html

@ -124,7 +124,7 @@
<ng-template ngSwitchCase="dateTime">
<ng-template [ngTemplateOutlet]="label"></ng-template>
<abp-extensible-date-time-picker [prop]="prop" [meridian]="meridian" />
<abp-extensible-date-time-picker [prop]="prop" [meridian]="meridian$ | async" />
</ng-template>
<ng-template ngSwitchCase="textarea">

133
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.ts

@ -1,11 +1,12 @@
import { EXTENSIONS_FORM_PROP, EXTENSIONS_FORM_PROP_DATA } from './../../tokens/extensions.token';
import { ABP, AbpValidators, ConfigStateService, TrackByService } from '@abp/ng.core';
import { ABP, CoreModule, TrackByService } from '@abp/ng.core';
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
inject,
Injector,
Input,
OnChanges,
@ -17,26 +18,53 @@ import {
import {
ControlContainer,
FormGroupDirective,
UntypedFormGroup,
ReactiveFormsModule,
ValidatorFn,
Validators,
} from '@angular/forms';
import { NgbDateAdapter, NgbTimeAdapter } from '@ng-bootstrap/ng-bootstrap';
import {
NgbDateAdapter,
NgbDatepickerModule,
NgbTimeAdapter,
NgbTimepickerModule,
NgbTypeaheadModule,
} from '@ng-bootstrap/ng-bootstrap';
import { Observable, of } from 'rxjs';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { DateAdapter, TimeAdapter } from '@abp/ng.theme.shared';
import {
DateAdapter,
DisabledDirective,
PasswordComponent,
TimeAdapter,
} from '@abp/ng.theme.shared';
import { EXTRA_PROPERTIES_KEY } from '../../constants/extra-properties';
import { ePropType } from '../../enums/props.enum';
import { FormProp } from '../../models/form-props';
import { PropData } from '../../models/props';
import { selfFactory } from '../../utils/factory.util';
import { addTypeaheadTextSuffix } from '../../utils/typeahead.util';
import { eThemeSharedComponents } from "../../enums/components";
import { eThemeSharedComponents } from '../../enums/components';
import { ExtensibleDateTimePickerComponent } from '../date-time-picker/extensible-date-time-picker.component';
import { NgxValidateCoreModule } from '@ngx-validate/core';
import { ExtensibleFormPropService } from '../../services/extensible-form-prop.service';
import {CreateInjectorPipe} from "../../pipes/create-injector.pipe";
@Component({
selector: 'abp-extensible-form-prop',
templateUrl: './extensible-form-prop.component.html',
standalone: true,
imports: [
CoreModule,
ExtensibleDateTimePickerComponent,
NgbDatepickerModule,
NgbTimepickerModule,
ReactiveFormsModule,
DisabledDirective,
NgxValidateCoreModule,
NgbTypeaheadModule,
PasswordComponent,
CreateInjectorPipe
],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [ExtensibleFormPropService],
viewProviders: [
{
provide: ControlContainer,
@ -48,32 +76,27 @@ import { eThemeSharedComponents } from "../../enums/components";
],
})
export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
@Input() data!: PropData;
protected service = inject(ExtensibleFormPropService);
public readonly cdRef = inject(ChangeDetectorRef);
public readonly track = inject(TrackByService);
#groupDirective = inject(FormGroupDirective);
private injector = inject(Injector);
private readonly form = this.#groupDirective.form;
@Input() data!: PropData;
@Input() prop!: FormProp;
@Input() first?: boolean;
@ViewChild('field') private fieldRef!: ElementRef<HTMLElement>;
public injectorForCustomComponent?: Injector;
injectorForCustomComponent?: Injector;
asterisk = '';
containerClassName = 'mb-2';
options$: Observable<ABP.Option<any>[]> = of([]);
validators: ValidatorFn[] = [];
readonly!: boolean;
typeaheadModel: any;
passwordKey = eThemeSharedComponents.PasswordComponent;
private readonly form: UntypedFormGroup;
disabledFn = (data: PropData) => false;
get disabled() {
@ -100,27 +123,13 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
typeaheadFormatter = (option: ABP.Option<any>) => option.key;
get meridian() {
return (
this.configState.getDeep('localization.currentCulture.dateTimeFormat.shortTimePattern') || ''
).includes('tt');
}
meridian$ = this.service.meridian$;
get isInvalid() {
const control = this.form.get(this.prop.name);
return control?.touched && control.invalid;
}
constructor(
public readonly cdRef: ChangeDetectorRef,
public readonly track: TrackByService,
protected configState: ConfigStateService,
groupDirective: FormGroupDirective,
private injector: Injector,
) {
this.form = groupDirective.form;
}
private getTypeaheadControls() {
const { name } = this.prop;
const extraPropName = `${EXTRA_PROPERTIES_KEY}.${name}`;
@ -132,7 +141,7 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
}
private setAsterisk() {
this.asterisk = this.validators.some(isRequired) ? '*' : '';
this.asterisk = this.service.calcAsterisks(this.validators);
}
ngAfterViewInit() {
@ -143,51 +152,11 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
}
getComponent(prop: FormProp): string {
if (prop.template) {
return 'template';
}
switch (prop.type) {
case ePropType.Boolean:
return 'checkbox';
case ePropType.Date:
return 'date';
case ePropType.DateTime:
return 'dateTime';
case ePropType.Hidden:
return 'hidden';
case ePropType.MultiSelect:
return 'multiselect';
case ePropType.Text:
return 'textarea';
case ePropType.Time:
return 'time';
case ePropType.Typeahead:
return 'typeahead';
case ePropType.PasswordInputGroup:
return 'passwordinputgroup';
default:
return prop.options ? 'select' : 'input';
}
return this.service.getComponent(prop);
}
getType(prop: FormProp): string {
switch (prop.type) {
case ePropType.Date:
case ePropType.String:
return 'text';
case ePropType.Boolean:
return 'checkbox';
case ePropType.Number:
return 'number';
case ePropType.Email:
return 'email';
case ePropType.Password:
return 'password';
case ePropType.PasswordInputGroup:
return 'passwordinputgroup';
default:
return 'hidden';
}
return this.service.getType(prop);
}
ngOnChanges({ prop, data }: SimpleChanges) {
@ -229,11 +198,3 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
this.typeaheadModel = { key: keyControl.value, value: valueControl.value };
}
}
function isRequired(validator: ValidatorFn) {
return (
validator === Validators.required ||
validator === AbpValidators.required ||
validator.name === 'required'
);
}

123
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form.component.ts

@ -1,73 +1,76 @@
import { TrackByService } from '@abp/ng.core';
import {TrackByService} from '@abp/ng.core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Inject,
Input,
Optional,
QueryList,
SkipSelf,
ViewChildren,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component, inject,
Input,
Optional,
QueryList,
SkipSelf,
ViewChildren,
} from '@angular/core';
import { ControlContainer, UntypedFormGroup } from '@angular/forms';
import { EXTRA_PROPERTIES_KEY } from '../../constants/extra-properties';
import { FormPropList, GroupedFormPropList } from '../../models/form-props';
import { ExtensionsService } from '../../services/extensions.service';
import { EXTENSIONS_IDENTIFIER } from '../../tokens/extensions.token';
import { selfFactory } from '../../utils/factory.util';
import { ExtensibleFormPropComponent } from './extensible-form-prop.component';
import {ControlContainer, ReactiveFormsModule, UntypedFormGroup} from '@angular/forms';
import {EXTRA_PROPERTIES_KEY} from '../../constants/extra-properties';
import {FormPropList, GroupedFormPropList} from '../../models/form-props';
import {ExtensionsService} from '../../services/extensions.service';
import {EXTENSIONS_IDENTIFIER} from '../../tokens/extensions.token';
import {selfFactory} from '../../utils/factory.util';
import {ExtensibleFormPropComponent} from './extensible-form-prop.component';
import {CommonModule} from "@angular/common";
import {PropDataDirective} from "../../directives/prop-data.directive";
@Component({
exportAs: 'abpExtensibleForm',
selector: 'abp-extensible-form',
templateUrl: './extensible-form.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [
{
provide: ControlContainer,
useFactory: selfFactory,
deps: [[new Optional(), new SkipSelf(), ControlContainer]],
},
],
exportAs: 'abpExtensibleForm',
selector: 'abp-extensible-form',
templateUrl: './extensible-form.component.html',
standalone:true,
imports:[CommonModule, PropDataDirective,ReactiveFormsModule,ExtensibleFormPropComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [
{
provide: ControlContainer,
useFactory: selfFactory,
deps: [[new Optional(), new SkipSelf(), ControlContainer]],
},
],
})
export class ExtensibleFormComponent<R = any> {
@ViewChildren(ExtensibleFormPropComponent)
formProps!: QueryList<ExtensibleFormPropComponent>;
@Input()
set selectedRecord(record: R) {
const type = !record || JSON.stringify(record) === '{}' ? 'create' : 'edit';
const propList = this.extensions[`${type}FormProps`].get(this.identifier).props;
this.groupedPropList = this.createGroupedList(propList);
this.record = record;
}
@ViewChildren(ExtensibleFormPropComponent)
formProps!: QueryList<ExtensibleFormPropComponent>;
extraPropertiesKey = EXTRA_PROPERTIES_KEY;
groupedPropList!: GroupedFormPropList;
record!: R;
@Input()
set selectedRecord(record: R) {
const type = !record || JSON.stringify(record) === '{}' ? 'create' : 'edit';
const propList = this.extensions[`${type}FormProps`].get(this.identifier).props;
this.groupedPropList = this.createGroupedList(propList);
this.record = record;
}
createGroupedList(propList: FormPropList<R>) {
const groupedFormPropList = new GroupedFormPropList();
propList.forEach(item => {
groupedFormPropList.addItem(item.value);
});
return groupedFormPropList;
}
extraPropertiesKey = EXTRA_PROPERTIES_KEY;
groupedPropList!: GroupedFormPropList;
record!: R;
get form(): UntypedFormGroup {
return (this.container ? this.container.control : { controls: {} }) as UntypedFormGroup;
}
public readonly cdRef = inject(ChangeDetectorRef)
public readonly track = inject(TrackByService)
private container = inject(ControlContainer)
private extensions = inject(ExtensionsService);
private identifier = inject(EXTENSIONS_IDENTIFIER)
get extraProperties(): UntypedFormGroup {
return (this.form.controls.extraProperties || { controls: {} }) as UntypedFormGroup;
}
createGroupedList(propList: FormPropList<R>) {
const groupedFormPropList = new GroupedFormPropList();
propList.forEach(item => {
groupedFormPropList.addItem(item.value);
});
return groupedFormPropList;
}
get form(): UntypedFormGroup {
return (this.container ? this.container.control : {controls: {}}) as UntypedFormGroup;
}
get extraProperties(): UntypedFormGroup {
return (this.form.controls.extraProperties || {controls: {}}) as UntypedFormGroup;
}
constructor(
public readonly cdRef: ChangeDetectorRef,
public readonly track: TrackByService,
private container: ControlContainer,
private extensions: ExtensionsService,
@Inject(EXTENSIONS_IDENTIFIER) private identifier: string,
) {}
}

51
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts

@ -1,17 +1,19 @@
import {
ABP,
ConfigStateService,
CoreModule,
getShortDateFormat,
getShortDateShortTimeFormat,
getShortTimeFormat,
ListService,
PermissionService,
} from '@abp/ng.core';
import { formatDate } from '@angular/common';
import { CommonModule, formatDate } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
inject,
Inject,
Injector,
Input,
@ -31,16 +33,28 @@ import { PropData } from '../../models/props';
import { ExtensionsService } from '../../services/extensions.service';
import {
ENTITY_PROP_TYPE_CLASSES,
EntityPropTypeClass,
EXTENSIONS_IDENTIFIER,
PROP_DATA_STREAM,
} from '../../tokens/extensions.token';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { GridActionsComponent } from '../grid-actions/grid-actions.component';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { NgxDatatableDefaultDirective, NgxDatatableListDirective } from '@abp/ng.theme.shared';
const DEFAULT_ACTIONS_COLUMN_WIDTH = 150;
@Component({
@Component({
exportAs: 'abpExtensibleTable',
selector: 'abp-extensible-table',
standalone: true,
imports: [
CoreModule,
NgxDatatableModule,
GridActionsComponent,
NgbTooltip,
NgxDatatableDefaultDirective,
NgxDatatableListDirective,
],
templateUrl: './extensible-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
@ -50,6 +64,7 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
set actionsText(value: string) {
this._actionsText = value;
}
get actionsText(): string {
return this._actionsText ?? (this.actionList.length > 1 ? 'AbpUi::Actions' : '');
}
@ -57,19 +72,17 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
@Input() data!: R[];
@Input() list!: ListService;
@Input() recordsTotal!: number;
@Input() set actionsColumnWidth(width: number) {
this.setColumnWidths(width ? Number(width) : undefined);
}
@Input() actionsTemplate?: TemplateRef<any>;
@Output() tableActivate = new EventEmitter();
getInjected: typeof this.injector.get
hasAtLeastOnePermittedAction: boolean;
entityPropTypeClasses: EntityPropTypeClass;
readonly columnWidths!: number[];
readonly propList: EntityPropList<R>;
@ -78,20 +91,20 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
readonly trackByFn: TrackByFunction<EntityProp<R>> = (_, item) => item.name;
constructor(
@Inject(LOCALE_ID) private locale: string,
private config: ConfigStateService,
private injector: Injector,
) {
this.entityPropTypeClasses = injector.get(ENTITY_PROP_TYPE_CLASSES);
this.getInjected = injector.get.bind(injector);
const extensions = injector.get(ExtensionsService);
const name = injector.get(EXTENSIONS_IDENTIFIER);
locale = inject(LOCALE_ID);
private config = inject(ConfigStateService);
entityPropTypeClasses = inject(ENTITY_PROP_TYPE_CLASSES);
#injector = inject(Injector);
getInjected = this.#injector.get.bind(this.#injector);
constructor() {
const extensions = this.#injector.get(ExtensionsService);
const name = this.#injector.get(EXTENSIONS_IDENTIFIER);
this.propList = extensions.entityProps.get(name).props;
this.actionList = extensions['entityActions'].get(name)
.actions as unknown as EntityActionList<R>;
const permissionService = injector.get(PermissionService);
const permissionService = this.#injector.get(PermissionService);
this.hasAtLeastOnePermittedAction =
permissionService.filterItemsByPolicy(
this.actionList.toArray().map(action => ({ requiredPolicy: action.permission })),
@ -149,7 +162,7 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
if (!data?.currentValue) return;
if (data.currentValue.length < 1) {
this.list.totalCount = this.recordsTotal
this.list.totalCount = this.recordsTotal;
}
this.data = data.currentValue.map((record: any, index: number) => {
@ -170,7 +183,7 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
useValue: value,
},
],
parent: this.injector,
parent: this.#injector,
});
record[propKey].component = prop.value.component;
}

5
npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.ts

@ -8,9 +8,14 @@ import {
import { EntityAction, EntityActionList } from '../../models/entity-actions';
import { EXTENSIONS_ACTION_TYPE } from '../../tokens/extensions.token';
import { AbstractActionsComponent } from '../abstract-actions/abstract-actions.component';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { CoreModule } from '@abp/ng.core';
import {EllipsisDirective} from "@abp/ng.theme.shared";
@Component({
exportAs: 'abpGridActions',
standalone: true,
imports: [ CoreModule, NgbDropdownModule, EllipsisDirective],
selector: 'abp-grid-actions',
templateUrl: './grid-actions.component.html',
providers: [

5
npm/ng-packs/packages/components/extensible/src/lib/components/page-toolbar/page-toolbar.component.ts

@ -8,10 +8,14 @@ import {
} from '../../models/toolbar-actions';
import { EXTENSIONS_ACTION_TYPE } from '../../tokens/extensions.token';
import { AbstractActionsComponent } from '../abstract-actions/abstract-actions.component';
import { CreateInjectorPipe } from '../../pipes/create-injector.pipe';
import { CoreModule } from '@abp/ng.core';
@Component({
exportAs: 'abpPageToolbar',
selector: 'abp-page-toolbar',
standalone: true,
imports: [ CoreModule, CreateInjectorPipe],
templateUrl: './page-toolbar.component.html',
providers: [
{
@ -31,6 +35,7 @@ export class PageToolbarComponent<R = any>
readonly trackByFn: TrackByFunction<ToolbarComponent<R>> = (_, item) =>
item.action || item.component;
constructor(public readonly injector: Injector) {
super(injector);
}

1
npm/ng-packs/packages/components/extensible/src/lib/directives/prop-data.directive.ts

@ -13,6 +13,7 @@ import { PropData, PropList } from '../models/props';
@Directive({
exportAs: 'abpPropData',
selector: '[abpPropData]',
standalone: true,
})
export class PropDataDirective<L extends PropList<any>>
extends PropData<InferredData<L>>

35
npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts

@ -15,23 +15,25 @@ import {
ExtensibleTableComponent,
GridActionsComponent,
PageToolbarComponent,
ExtensibleDateTimePickerComponent
ExtensibleDateTimePickerComponent,
} from './components';
import { PropDataDirective } from './directives/prop-data.directive';
import { CreateInjectorPipe } from './pipes/create-injector.pipe';
import { DisabledDirective } from '@abp/ng.theme.shared';
const importWithExport = [
DisabledDirective,
ExtensibleDateTimePickerComponent,
ExtensibleFormPropComponent,
GridActionsComponent,
PropDataDirective,
PageToolbarComponent,
CreateInjectorPipe,
ExtensibleFormComponent,
ExtensibleTableComponent,
];
@NgModule({
declarations: [
ExtensibleDateTimePickerComponent,
PageToolbarComponent,
GridActionsComponent,
ExtensibleFormPropComponent,
ExtensibleFormComponent,
ExtensibleTableComponent,
PropDataDirective,
CreateInjectorPipe,
],
declarations: [],
imports: [
CoreModule,
ThemeSharedModule,
@ -41,15 +43,8 @@ import { DisabledDirective } from '@abp/ng.theme.shared';
NgbTimepickerModule,
NgbTypeaheadModule,
NgbTooltipModule,
DisabledDirective,
],
exports: [
PageToolbarComponent,
GridActionsComponent,
ExtensibleFormComponent,
ExtensibleTableComponent,
PropDataDirective,
CreateInjectorPipe,
...importWithExport,
],
exports: [...importWithExport],
})
export class ExtensibleModule {}

1
npm/ng-packs/packages/components/extensible/src/lib/pipes/create-injector.pipe.ts

@ -12,6 +12,7 @@ import { EXTENSIONS_ACTION_CALLBACK, EXTENSIONS_ACTION_DATA } from '../tokens/ex
@Pipe({
name: 'createInjector',
standalone: true,
})
export class CreateInjectorPipe<R> implements PipeTransform {
public transform(

77
npm/ng-packs/packages/components/extensible/src/lib/services/extensible-form-prop.service.ts

@ -0,0 +1,77 @@
import { inject, Injectable } from '@angular/core';
import { ValidatorFn, Validators } from '@angular/forms';
import { AbpValidators, ConfigStateService } from '@abp/ng.core';
import { map } from 'rxjs/operators';
import { FormProp } from '../models/form-props';
import { ePropType } from '../enums/props.enum';
@Injectable()
export class ExtensibleFormPropService {
readonly #configStateService = inject(ConfigStateService);
meridian$ = this.#configStateService
.getDeep$('localization.currentCulture.dateTimeFormat.shortTimePattern')
.pipe(map((shortTimePattern: string | undefined) => (shortTimePattern || '').includes('tt')));
isRequired(validator: ValidatorFn) {
return (
validator === Validators.required ||
validator === AbpValidators.required ||
validator.name === 'required'
);
}
getComponent(prop: FormProp) {
if (prop.template) {
return 'template';
}
switch (prop.type) {
case ePropType.Boolean:
return 'checkbox';
case ePropType.Date:
return 'date';
case ePropType.DateTime:
return 'dateTime';
case ePropType.Hidden:
return 'hidden';
case ePropType.MultiSelect:
return 'multiselect';
case ePropType.Text:
return 'textarea';
case ePropType.Time:
return 'time';
case ePropType.Typeahead:
return 'typeahead';
case ePropType.PasswordInputGroup:
return 'passwordinputgroup';
default:
return prop.options ? 'select' : 'input';
}
}
getType(prop: FormProp) {
switch (prop.type) {
case ePropType.Date:
case ePropType.String:
return 'text';
case ePropType.Boolean:
return 'checkbox';
case ePropType.Number:
return 'number';
case ePropType.Email:
return 'email';
case ePropType.Password:
return 'password';
case ePropType.PasswordInputGroup:
return 'passwordinputgroup';
default:
return 'hidden';
}
}
calcAsterisks(validators: ValidatorFn[]) {
if (!validators) return '';
const required = validators.find(v => this.isRequired(v));
return required ? '*' : '';
}
}

5
npm/ng-packs/packages/components/page/src/page.module.ts

@ -1,6 +1,5 @@
import { CoreModule } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { PagePartDirective } from './page-part.directive';
import {
@ -9,7 +8,7 @@ import {
PageToolbarContainerComponent,
} from './page-parts.component';
import { PageComponent } from './page.component';
import {ExtensibleModule} from "@abp/ng.components/extensible";
import {PageToolbarComponent} from "@abp/ng.components/extensible";
const exportedDeclarations = [
PageComponent,
@ -21,7 +20,7 @@ const exportedDeclarations = [
@NgModule({
declarations: [...exportedDeclarations],
imports: [CommonModule, CoreModule, ThemeSharedModule, ExtensibleModule],
imports: [CoreModule, ThemeSharedModule, PageToolbarComponent],
exports: [...exportedDeclarations],
})
export class PageModule {}

8
npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts

@ -1,11 +1,11 @@
import { ChangeDetectorRef, Component, Injector, Input } from '@angular/core';
import { ChangeDetectorRef, Component, inject, Input } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
// Not an abstract class on purpose. Do not change!
@Component({ template: '' })
export class AbstractNgModelComponent<T = any, U = T> implements ControlValueAccessor {
protected _value!: T;
protected cdRef: ChangeDetectorRef;
protected cdRef = inject(ChangeDetectorRef);
onChange?: (value: T) => void;
onTouched?: () => void;
@ -39,10 +39,6 @@ export class AbstractNgModelComponent<T = any, U = T> implements ControlValueAcc
return this._value;
}
constructor(public injector: Injector) {
this.cdRef = injector.get(ChangeDetectorRef);
}
notifyValueChange(): void {
if (this.onChange) {
this.onChange(this.value);

4
npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts

@ -43,8 +43,4 @@ export class FormCheckboxComponent extends AbstractNgModelComponent {
@Input() checkboxReadonly = false;
@Output() checkboxBlur = new EventEmitter<void>();
@Output() checkboxFocus = new EventEmitter<void>();
constructor(injector: Injector) {
super(injector);
}
}

4
npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts

@ -45,8 +45,4 @@ export class FormInputComponent extends AbstractNgModelComponent {
@Input() inputClass = 'form-control';
@Output() formBlur = new EventEmitter<void>();
@Output() formFocus = new EventEmitter<void>();
constructor(injector: Injector) {
super(injector);
}
}

11
npm/ng-packs/packages/theme-shared/src/lib/components/password/password.component.ts

@ -1,9 +1,12 @@
import { Component, forwardRef, Injector, Input } from '@angular/core';
import { Component, forwardRef, Input } from '@angular/core';
import { AbstractNgModelComponent } from '@abp/ng.core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { CommonModule } from '@angular/common';
@Component({
selector: 'abp-password',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: `./password.component.html`,
providers: [
{
@ -18,10 +21,6 @@ export class PasswordComponent extends AbstractNgModelComponent {
@Input() formControlName!: string;
fieldTextType?: boolean;
constructor(injector: Injector) {
super(injector);
}
toggleFieldTextType() {
this.fieldTextType = !this.fieldTextType;
}

7
npm/ng-packs/packages/theme-shared/src/lib/directives/ellipsis.directive.ts

@ -10,6 +10,7 @@ import {
@Directive({
selector: '[abpEllipsis]',
standalone:true
})
export class EllipsisDirective implements AfterViewInit {
@Input('abpEllipsis')
@ -44,9 +45,3 @@ export class EllipsisDirective implements AfterViewInit {
this.cdRef.detectChanges();
}
}
@NgModule({
exports: [EllipsisDirective],
declarations: [EllipsisDirective],
})
export class EllipsisModule {}

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

@ -7,6 +7,7 @@ import { debounceTime } from 'rxjs/operators';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: 'ngx-datatable[default]',
standalone:true,
exportAs: 'ngxDatatableDefault',
})
export class NgxDatatableDefaultDirective implements AfterViewInit, OnDestroy {

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

@ -21,6 +21,7 @@ import {
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: 'ngx-datatable[list]',
standalone: true,
exportAs: 'ngxDatatableList',
})
export class NgxDatatableListDirective implements OnChanges, OnDestroy, OnInit {

11
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -22,7 +22,7 @@ import { ModalComponent } from './components/modal/modal.component';
import { ToastContainerComponent } from './components/toast-container/toast-container.component';
import { ToastComponent } from './components/toast/toast.component';
import { DEFAULT_VALIDATION_BLUEPRINTS } from './constants/validation';
import { EllipsisModule } from './directives/ellipsis.directive';
import { EllipsisDirective } from './directives/ellipsis.directive';
import { LoadingDirective } from './directives/loading.directive';
import { NgxDatatableDefaultDirective } from './directives/ngx-datatable-default.directive';
import { NgxDatatableListDirective } from './directives/ngx-datatable-list.directive';
@ -52,9 +52,6 @@ const declarationsWithExports = [
ModalComponent,
ToastComponent,
ToastContainerComponent,
PasswordComponent,
NgxDatatableDefaultDirective,
NgxDatatableListDirective,
LoadingDirective,
ModalCloseDirective,
AbpVisibleDirective,
@ -68,14 +65,16 @@ const declarationsWithExports = [
NgxDatatableModule,
NgxValidateCoreModule,
NgbPaginationModule,
EllipsisModule,
EllipsisDirective,
CardModule,
PasswordComponent,
NgxDatatableDefaultDirective,
NgxDatatableListDirective,
DisabledDirective
],
declarations: [...declarationsWithExports, HttpErrorWrapperComponent],
exports: [
NgxDatatableModule,
EllipsisModule,
NgxValidateCoreModule,
CardModule,
DisabledDirective,

1
nupkg/common.ps1

@ -204,6 +204,7 @@ $projects = (
"framework/src/Volo.Abp.Imaging.AspNetCore",
"framework/src/Volo.Abp.Imaging.ImageSharp",
"framework/src/Volo.Abp.Imaging.MagickNet",
"framework/src/Volo.Abp.Imaging.SkiaSharp",
"framework/src/Volo.Abp.Json",
"framework/src/Volo.Abp.Json.Abstractions",
"framework/src/Volo.Abp.Json.Newtonsoft",

3
templates/NuGet.Config

@ -2,5 +2,6 @@
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="ABP Nightly" value="https://www.myget.org/F/abp-nightly/api/v3/index.json" />
</packageSources>
</configuration>
</configuration>

4
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj

@ -22,8 +22,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Server.Theming\Volo.Abp.AspNetCore.Components.Server.Theming.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

4
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj

@ -22,8 +22,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Server.Theming\Volo.Abp.AspNetCore.Components.Server.Theming.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj

@ -21,7 +21,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Http.Client.IdentityModel.WebAssembly\Volo.Abp.Http.Client.IdentityModel.WebAssembly.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj

@ -74,7 +74,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.csproj

@ -75,7 +75,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj

@ -70,7 +70,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj

@ -71,7 +71,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj

@ -18,7 +18,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj

@ -18,7 +18,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj

@ -51,7 +51,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj

@ -30,8 +30,8 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Server.Theming\Volo.Abp.AspNetCore.Components.Server.Theming.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="3.0.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj

@ -28,8 +28,8 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Server.Theming\Volo.Abp.AspNetCore.Components.Server.Theming.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme" Version="3.0.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj

@ -24,7 +24,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Http.Client.IdentityModel.WebAssembly\Volo.Abp.Http.Client.IdentityModel.WebAssembly.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj

@ -26,7 +26,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj

@ -28,7 +28,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj

@ -49,7 +49,7 @@
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.4.*-*" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="3.0.*-*" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save