├── _config.yml ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md ├── LazZiya.TagHelpers ├── files │ ├── icon.ico │ ├── icon.png │ ├── license.txt │ └── Templates │ │ ├── LocalizationValidationScripts_local.html │ │ └── LocalizationValidationScripts_jsdeliver.html ├── RenderMode.cs ├── PagingAjaxMode.cs ├── LanguageNavModels.cs ├── PhoneNumberTagHelper.cs ├── EmailTagHelper.cs ├── LocalizationValidiationScriptsTagHelper.cs ├── Utilities │ ├── GenericTempDataExtensions.cs │ ├── RomanNumerals.cs │ └── NumberFormats.cs ├── SelectEnumTagHelper.cs ├── LazZiya.TagHelpers.csproj ├── AlertStylesTagHelper.cs ├── LocalizationValidiationScriptsTagHelperComponent.cs ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── Alerts │ ├── AlertModels.cs │ └── AlertPageModelExtensions.cs ├── AlertTagHelper.cs ├── LanguageNavTagHelper.cs ├── PagingTagHelper.cs └── LazZiya.TagHelpers.xml ├── LICENSE ├── TagHelpers.sln ├── README.md ├── ReleseHistory.md ├── .gitattributes └── .gitignore /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: lazziya 2 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/files/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazZiya/TagHelpers/HEAD/LazZiya.TagHelpers/files/icon.ico -------------------------------------------------------------------------------- /LazZiya.TagHelpers/files/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazZiya/TagHelpers/HEAD/LazZiya.TagHelpers/files/icon.png -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/RenderMode.cs: -------------------------------------------------------------------------------- 1 | namespace LazZiya.TagHelpers 2 | { 3 | /// 4 | /// choose render mode style, 5 | /// classic: regular dropdown select list 6 | /// Bootstrap4: HTML5 div with Bootstrap4 support 7 | /// 8 | public enum RenderMode 9 | { 10 | /// 11 | /// regular dropdown list 12 | /// 13 | Classic = 0, 14 | 15 | /// 16 | /// HTML5 div with Bootstrap 4 support 17 | /// 18 | Bootstrap = 1, 19 | 20 | /// 21 | /// Render as form control 22 | /// 23 | FormControl = 2, 24 | /// 25 | /// HTML5 div with Bootstrap 5 support 26 | /// 27 | Bootstrap5 = 3 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/PagingAjaxMode.cs: -------------------------------------------------------------------------------- 1 | namespace LazZiya.TagHelpers 2 | { 3 | /// 4 | /// ajax update modes 5 | /// 6 | public enum PagingAjaxMode 7 | { 8 | /// 9 | /// update before target content 10 | /// 11 | before, 12 | 13 | /// 14 | /// update after target content 15 | /// 16 | after, 17 | 18 | /// 19 | /// replace target content 20 | /// 21 | replace 22 | } 23 | 24 | /// 25 | /// The http request method 26 | /// 27 | public enum AjaxMethod 28 | { 29 | /// 30 | /// get request 31 | /// 32 | get, 33 | 34 | /// 35 | /// post request 36 | /// 37 | post 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/LanguageNavModels.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LazZiya.TagHelpers 4 | { 5 | /// 6 | /// The label to display for language dropdown list on language names 7 | /// 8 | public enum LanguageLabel 9 | { 10 | /// 11 | /// Culture name 12 | /// 13 | Name, 14 | 15 | /// 16 | /// Culture display name 17 | /// 18 | DisplayName, 19 | 20 | /// 21 | /// Culture English name 22 | /// 23 | EnglishName, 24 | 25 | /// 26 | /// Culture native name 27 | /// 28 | NativeName, 29 | 30 | /// 31 | /// Two letter ISO language name 32 | /// 33 | TwoLetterISOLanguageName 34 | } 35 | 36 | internal class LanguageItem 37 | { 38 | public string Name { get; set; } 39 | public string DisplayText { get; set; } 40 | public string Url { get; set; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 [Ziya Mollamahmut](http://ziyad.info) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TagHelpers.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.31903.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LazZiya.TagHelpers", "LazZiya.TagHelpers\LazZiya.TagHelpers.csproj", "{080D63B6-33A9-47D5-99FF-F636542694EB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {080D63B6-33A9-47D5-99FF-F636542694EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {080D63B6-33A9-47D5-99FF-F636542694EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {080D63B6-33A9-47D5-99FF-F636542694EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {080D63B6-33A9-47D5-99FF-F636542694EB}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {ED31C13F-823E-406C-87BA-4BC36A23816A} 24 | BuildVersion_StartDate = 2000/1/1 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/files/license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | 4 | Copyright (c) 2018 [Ziya Mollamahmut](http://ziyad.info) 5 | 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | 9 | of this software and associated documentation files (the "Software"), to deal 10 | 11 | in the Software without restriction, including without limitation the rights 12 | 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | 15 | copies of the Software, and to permit persons to whom the Software is 16 | 17 | furnished to do so, subject to the following conditions: 18 | 19 | 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | 23 | copies or substantial portions of the Software. 24 | 25 | 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | 33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | 37 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 38 | 39 | SOFTWARE. -------------------------------------------------------------------------------- /LazZiya.TagHelpers/PhoneNumberTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Razor.TagHelpers; 2 | using System.Threading.Tasks; 3 | 4 | namespace LazZiya.TagHelpers 5 | { 6 | /// 7 | /// creates a display for phone number 8 | /// 9 | public class PhoneNumberTagHelper : TagHelper 10 | { 11 | /// 12 | /// boolean value to indicate if the phone number has been confirmed 13 | /// 14 | public bool PhoneNumberConfirmed { get; set; } 15 | 16 | /// 17 | /// process creating phone number tag helper 18 | /// 19 | /// 20 | /// 21 | /// 22 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 23 | { 24 | output.TagName = "span"; 25 | 26 | var content = await output.GetChildContentAsync(); 27 | 28 | var target = content.GetContent(); 29 | output.Content.SetContent(target.Replace("+", "+")); 30 | output.Attributes.SetAttribute("dir", "ltr"); 31 | 32 | if (PhoneNumberConfirmed) 33 | output.PreContent.SetHtmlContent(""); 34 | else 35 | output.PreContent.SetHtmlContent(""); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/EmailTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Razor.TagHelpers; 2 | using System.Threading.Tasks; 3 | 4 | namespace LazZiya.TagHelpers 5 | { 6 | /// 7 | /// creates email link with mark if email is confirmed 8 | /// 9 | public class EmailTagHelper : TagHelper 10 | { 11 | /// 12 | /// boolean value to indicate if the email is confirmed or not 13 | /// 14 | public bool EmailConfirmed { get; set; } 15 | 16 | /// 17 | /// process in creating email tag helper 18 | /// 19 | /// 20 | /// 21 | /// 22 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 23 | { 24 | output.TagName = "a"; // Replaces with tag 25 | 26 | var content = await output.GetChildContentAsync(); 27 | var target = content.GetContent(); 28 | 29 | output.Attributes.SetAttribute("href", "mailto:" + target); 30 | output.Content.SetContent(target); 31 | 32 | if (EmailConfirmed) 33 | output.PreContent.SetHtmlContent(""); 34 | else 35 | output.PreContent.SetHtmlContent(""); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/files/Templates/LocalizationValidationScripts_local.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 34 | 35 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/LocalizationValidiationScriptsTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | 6 | namespace LazZiya.TagHelpers 7 | { 8 | /// 9 | /// defines location to load localization valdiation scripts 10 | /// 11 | public enum ScriptSource 12 | { 13 | /// 14 | /// valdiation scripts are located under wwwroot/lib folder 15 | /// 16 | Local, 17 | 18 | /// 19 | /// valdiation scripts will be loaded from jsdelivr 20 | /// 21 | JsDeliver 22 | } 23 | 24 | /// 25 | /// Tag helper for client side localized validation scripts. 26 | /// 27 | [HtmlTargetElement("localization-validation-scripts")] 28 | public class LocalizationValidationScriptsTagHelperComponentTagHelper : TagHelperComponentTagHelper 29 | { 30 | /// 31 | /// (optional) define where to load scripts from, Local or JsDelivr. 32 | /// Default: JsDelivr 33 | /// 34 | [HtmlAttributeName("source")] 35 | public ScriptSource Source { get; set; } = ScriptSource.JsDeliver; 36 | 37 | /// 38 | /// (optional) set cldr version to load. 39 | /// Default: 35.1 40 | /// 41 | [HtmlAttributeName("cldr-core-version")] 42 | public string CldrVersion { get; set; } = "35.1.0"; 43 | 44 | /// 45 | /// Tag helper for client side localized validation scripts. 46 | /// 47 | public LocalizationValidationScriptsTagHelperComponentTagHelper(ITagHelperComponentManager manager, ILoggerFactory loggerFactory) : base(manager, loggerFactory) 48 | { 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Utilities/GenericTempDataExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 6 | using Newtonsoft.Json; 7 | #else 8 | using System.Text.Json; 9 | #endif 10 | 11 | namespace LazZiya.TagHelpers.Utilities 12 | { 13 | /// 14 | /// Generic extension to TempData for adding complex object and fix serialization problem 15 | /// 16 | /// 17 | public static class GenericTempDataExtensions 18 | { 19 | /// 20 | /// Add object to temp data 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | public static void Put(this ITempDataDictionary tempData, string key, T value) where T : class 27 | { 28 | #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 29 | tempData[key] = JsonConvert.SerializeObject(value); 30 | #else 31 | tempData[key] = JsonSerializer.Serialize(value); 32 | #endif 33 | } 34 | 35 | /// 36 | /// Read object from temp data 37 | /// 38 | /// 39 | /// 40 | /// 41 | /// 42 | public static T Get(this ITempDataDictionary tempData, string key) where T : class 43 | { 44 | tempData.TryGetValue(key, out object o); 45 | 46 | #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 47 | var obj = JsonConvert.DeserializeObject((string)o); 48 | #else 49 | var obj = JsonSerializer.Deserialize((string)o); 50 | #endif 51 | return o == null ? null : obj; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LazZiya.TagHelpers 2 | 3 | ## What is it? 4 | A collection of useful TagHelpers for any ASP.NET Core project. 5 | 6 | ## Documentation : 7 | See all documentation in [DOCS.Ziya.info.tr][1]. 8 | 9 | ### [Paging TagHelper][1] 10 | Create a pagination control _styled with bootstrap 4.x_ using simple html tag. 11 | 12 | ````html 13 | 16 | 17 | ```` 18 | [![PagingTagHelper default](https://github.com/LazZiya/WebXRObjects/raw/master/Shared/Images/LazZiya.TagHelpers/paging-tag-helper-full.PNG)][1] 19 | 20 | ### [Alert TagHelper ][1] 21 | Create bootstrap alerts using very simple html tag. 22 | 23 | #### Front end alert 24 | ````html 25 | 26 | My alert text ... 27 | 28 | ```` 29 | 30 | #### Backend alert 31 | ````cs 32 | TempData.Success("My alert text ...") 33 | ```` 34 | 35 | Catch all backend alerts in frontend: 36 | ````html 37 | 38 | ```` 39 | 40 | [![AlertTagHelper - success](https://github.com/LazZiya/WebXRObjects/blob/master/Shared/Images/LazZiya.TagHelpers/alert-taghelper-success.PNG)][1] 41 | 42 | ### [Language Navigation TagHelper][1] 43 | Create a language dropdown navigation for websites. Supported cultures will be used to create the navigation items. 44 | 45 | ````html 46 | 47 | ```` 48 | [![LanguageNavTagHelper with flags](https://github.com/LazZiya/WebXRObjects/blob/master/Shared/Images/LazZiya.TagHelpers/languagenav-taghelper-with-flags.PNG)][1] 49 | 50 | ### [Localization Validation Scripts TagHelper][1] 51 | Add all client side scripts that are required for validating localized inputs like decimal numbers, dates, ..etc. 52 | ````html 53 | 54 | ```` 55 | [![Localization number es](https://github.com/LazZiya/WebXRObjects/blob/master/Shared/Images/LazZiya.TagHelpers/localization-validiation-scripts-number-es.PNG)][1] 56 | 57 | ## Live demos: 58 | http://demo.ziyad.info/en/ 59 | 60 | [1]:https://docs.ziya.info.tr 61 | -------------------------------------------------------------------------------- /ReleseHistory.md: -------------------------------------------------------------------------------- 1 | # LazZiya.TagHelpers Release History 2 | 3 | ## v3.0.0-preview1 4 | _02 Sep 2019_ 5 | 6 | - **DotNetCore 3.0 Support :** Added support for dotnet core 3.0. 7 | - **LanguageTagHelper :** Auto RTL direction for Language TagHelper dropdown 8 | 9 | ## v2.2.1 10 | _20 June 2019_ 11 | 12 | - **Hotfix for Alert TagHelper :** See related isse here https://github.com/LazZiya/TagHelpers/issues/2 13 | 14 | ## v2.2.0 15 | _07 June 2019_ 16 | 17 | - **Alert TagHelper :** Show bootstrap alerts from c# backend or razor side using simple html tags. 18 | - Documentation : http://www.ziyad.info/en/articles/37-Alert_TagHelpers 19 | - Live demo : http://demo.ziyad.info/en/Alerts 20 | 21 | ## v2.1.0 22 | _26 May 2019_ 23 | 24 | - **LocalizationValidationScripts TagHelper :** TagHelperComponent to inject all cldr-data and localization validation scripts to razor pages. 25 | - Documentation : http://www.ziyad.info/en/articles/34-Localization_Validation_Scripts 26 | - Live demo : http://demo.ziyad.info/en/Trips 27 | 28 | ## v2.0.0 29 | _14 April 2019_ 30 | 31 | - **Backward compatibility :** support for multiple target frameworks starting from .Net Core v1.0+ 32 | - **LanguageNav TagHelper :** Create language navigation for multicultural web applications 33 | - Documentation : http://www.ziyad.info/en/articles/32-Language_Navigation_TagHelper 34 | - Live demo : http://demo.ziyad.info/en/LanguageNav 35 | 36 | ## v1.0.3 37 | _14 February 2019_ 38 | 39 | - **SelectEnum TagHelper :** Create dropdown list from enum with localization support 40 | - Documentation : http://www.ziyad.info/en/articles/28-Select_Enum_TagHelper 41 | - Live demo : http://demo.ziyad.info/en/SelectEnum 42 | 43 | ## v1.0.1 44 | _17 September 2019_ 45 | 46 | - **Paging TagHelper :** easily create pagination control 47 | - Documentation : http://www.ziyad.info/en/articles/21-Paging_TagHelper_for_ASP_NET_Core 48 | - Live demo : http://demo.ziyad.info/en/Paging 49 | - Step by step tutorial : http://www.ziyad.info/en/articles/38-How_to_build_an_efficient_pagination_system 50 | 51 | ## Project site: 52 | http://ziyad.info/en/articles/27-LazZiya_TagHelpers 53 | 54 | ## Live Demos : 55 | http://demo.ziyad.info/en/ 56 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Utilities/RomanNumerals.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace LazZiya.TagHelpers.Utilities 5 | { 6 | /// 7 | /// Convert decimals to roman numerals 8 | /// 9 | public static class RomanNumerals 10 | { 11 | /// 12 | /// Dictionary of roman numbers and their equavilant decimals 13 | /// 14 | /// 15 | public static Dictionary RomanNumbers = 16 | new Dictionary 17 | { 18 | { 1000000, "M̅" }, 19 | { 900000, "C̅M̅" }, 20 | 21 | { 500000, "D̅" }, 22 | { 400000, "C̅D̅" }, 23 | 24 | { 100000, "C̅" }, 25 | { 90000, "X̅C̅" }, 26 | 27 | { 50000, "L̅" }, 28 | { 40000, "X̅L̅" }, 29 | 30 | { 10000, "X̅" }, 31 | { 9000, "I̅X̅" }, 32 | 33 | { 5000, "V̅" }, 34 | { 4000, "I̅V̅" }, 35 | 36 | { 1000, "M" }, 37 | { 900, "DM" }, 38 | 39 | { 500, "D" }, 40 | { 400, "CD" }, 41 | 42 | { 100, "C" }, 43 | { 90, "XC" }, 44 | 45 | { 50, "L" }, 46 | { 40, "XL" }, 47 | 48 | { 10, "X" }, 49 | { 9, "IX" }, 50 | 51 | { 5, "V" }, 52 | { 4, "IV" }, 53 | 54 | { 1, "I" }, 55 | }; 56 | 57 | /// 58 | /// Convert decimal number to roman number 59 | /// 60 | /// unsigned number 61 | /// Roman number 62 | public static string ToRoman(this uint number) 63 | { 64 | var romanNum = string.Empty; 65 | 66 | while (number > 0) 67 | { 68 | var item = RomanNumbers 69 | .OrderByDescending(x => x.Key) 70 | .First(x => x.Key <= number); 71 | romanNum += item.Value; 72 | number -= item.Key; 73 | } 74 | 75 | return romanNum; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/files/Templates/LocalizationValidationScripts_jsdeliver.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 34 | 35 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/SelectEnumTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Rendering; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.Linq; 7 | 8 | namespace LazZiya.TagHelpers 9 | { 10 | /// 11 | /// creates a dropdown list from custom enum with supports for localization 12 | /// 13 | public class SelectEnumTagHelper : TagHelper 14 | { 15 | private readonly ILogger _log; 16 | 17 | /// 18 | /// (int)MyEnum.ValueName 19 | /// 20 | public int SelectedValue { get; set; } 21 | 22 | /// 23 | /// typeof(MyEnum) 24 | /// 25 | public Type EnumType { get; set; } 26 | 27 | /// 28 | /// A delegate function for getting locaized value. 29 | /// 30 | public Func TextLocalizerDelegate { get; set; } 31 | 32 | /// 33 | /// Initialize a new instance of SelectEnum taghelper 34 | /// 35 | /// 36 | public SelectEnumTagHelper(ILogger log) 37 | { 38 | _log = log; 39 | } 40 | 41 | /// 42 | /// start creating select-enum tag helper 43 | /// 44 | /// 45 | /// 46 | public override void Process(TagHelperContext context, TagHelperOutput output) 47 | { 48 | output.TagName = "select"; 49 | 50 | foreach (int e in Enum.GetValues(EnumType)) 51 | { 52 | var op = new TagBuilder("option"); 53 | 54 | op.Attributes.Add("value", $"{e}"); 55 | 56 | var displayText = TextLocalizerDelegate == null 57 | ? GetEnumFieldDisplayName(e) 58 | : GetEnumFieldLocalizedDisplayName(e); 59 | 60 | op.InnerHtml.Append(displayText); 61 | 62 | if (e == SelectedValue) 63 | op.Attributes.Add("selected", "selected"); 64 | 65 | output.Content.AppendHtml(op); 66 | } 67 | } 68 | 69 | private string GetEnumFieldDisplayName(int value) 70 | { 71 | // get enum field name 72 | var fieldName = Enum.GetName(EnumType, value); 73 | 74 | //get Display(Name = "Field name") 75 | var displayName = EnumType.GetField(fieldName).GetCustomAttributes(false).OfType().SingleOrDefault()?.Name; 76 | 77 | return displayName ?? fieldName; 78 | } 79 | 80 | private string GetEnumFieldLocalizedDisplayName(int value) 81 | { 82 | var text = GetEnumFieldDisplayName(value); 83 | 84 | return TextLocalizerDelegate(text); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/LazZiya.TagHelpers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0 5 | files\icon.ico 6 | Ziya Mollamahmut 7 | Ziyad.info 8 | Collection of tag helpers for ASP.NET Core web applications, like Paging, SelectEnum and LanguageNav dropdown tag helpers for MVC and Razor Pages. 9 | Ziyad.info 10 | https://docs.ziyad.info/en/LazZiya.TagHelpers/v6.0/index.md 11 | icon.png 12 | true 13 | asp.net, core, razor, mvc, taghelpers, taghelper,tag,helper,language,culture,dropdown, pagination, select, enum 14 | 15 | - New public param "url-template" use custom url template for the paging buttons. 16 | - Fixed #21 https://github.com/LazZiya/TagHelpers/issues/21 17 | - Fixed #27 https://github.com/LazZiya/TagHelpers/issues/27 18 | - See all release notes in https://github.com/LazZiya/TagHelpers/releases 19 | 20 | 6.0.2 21 | 22 | 6.0.2.0 23 | 6.0.2.0 24 | false 25 | MIT 26 | https://raw.githubusercontent.com/LazZiya/TagHelpers/master/LazZiya.TagHelpers/files/icon.png 27 | https://github.com/LazZiya/TagHelpers 28 | 29 | 30 | 31 | LazZiya.TagHelpers.xml 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Always 58 | 59 | 60 | Always 61 | 62 | 63 | 64 | 65 | 66 | 67 | True 68 | True 69 | Resources.resx 70 | 71 | 72 | 73 | 74 | 75 | ResXFileCodeGenerator 76 | Resources.Designer.cs 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/AlertStylesTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Razor.TagHelpers; 2 | using LazZiya.TagHelpers.Alerts; 3 | using System.Threading.Tasks; 4 | 5 | namespace LazZiya.TagHelpers 6 | { 7 | /// 8 | /// Create primary alert 9 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 10 | /// 11 | public class AlertPrimaryTagHelper : AlertTagHelper 12 | { 13 | /// 14 | /// Create primary alert 15 | /// 16 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 17 | { 18 | base.AlertStyle = AlertStyle.Primary; 19 | await base.ProcessAsync(context, output); 20 | } 21 | } 22 | 23 | /// 24 | /// Create secondary alert 25 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 26 | /// 27 | public class AlertSecondaryTagHelper : AlertTagHelper 28 | { 29 | /// 30 | /// Create secondary alert 31 | /// 32 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 33 | { 34 | base.AlertStyle = AlertStyle.Secondary; 35 | await base.ProcessAsync(context, output); 36 | } 37 | } 38 | 39 | /// 40 | /// Create success alert 41 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 42 | /// 43 | public class AlertSuccessTagHelper : AlertTagHelper 44 | { 45 | /// 46 | /// Create success alert 47 | /// 48 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 49 | { 50 | base.AlertStyle = AlertStyle.Success; 51 | await base.ProcessAsync(context, output); 52 | } 53 | } 54 | 55 | /// 56 | /// Create danger alert 57 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 58 | /// 59 | public class AlertDangerTagHelper : AlertTagHelper 60 | { 61 | /// 62 | /// Create danger alert 63 | /// 64 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 65 | { 66 | base.AlertStyle = AlertStyle.Danger; 67 | await base.ProcessAsync(context, output); 68 | } 69 | } 70 | 71 | /// 72 | /// Create warning alert 73 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 74 | /// 75 | public class AlertWarningTagHelper : AlertTagHelper 76 | { 77 | /// 78 | /// Create danger alert 79 | /// 80 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 81 | { 82 | base.AlertStyle = AlertStyle.Warning; 83 | await base.ProcessAsync(context, output); 84 | } 85 | } 86 | 87 | /// 88 | /// Create info alert 89 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 90 | /// 91 | public class AlertInfoTagHelper : AlertTagHelper 92 | { 93 | /// 94 | /// Create info alert 95 | /// 96 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 97 | { 98 | base.AlertStyle = AlertStyle.Info; 99 | await base.ProcessAsync(context, output); 100 | } 101 | } 102 | 103 | /// 104 | /// Create light alert 105 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 106 | /// 107 | public class AlertLightTagHelper : AlertTagHelper 108 | { 109 | /// 110 | /// Create light alert 111 | /// 112 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 113 | { 114 | base.AlertStyle = AlertStyle.Light; 115 | await base.ProcessAsync(context, output); 116 | } 117 | } 118 | 119 | /// 120 | /// Create dark alert 121 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 122 | /// 123 | public class AlertDarkTagHelper : AlertTagHelper 124 | { 125 | /// 126 | /// Create dark alert 127 | /// 128 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 129 | { 130 | base.AlertStyle = AlertStyle.Dark; 131 | await base.ProcessAsync(context, output); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/LocalizationValidiationScriptsTagHelperComponent.cs: -------------------------------------------------------------------------------- 1 | using LazZiya.TagHelpers.Properties; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Razor.TagHelpers; 4 | using System; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | 9 | namespace LazZiya.TagHelpers 10 | { 11 | /// 12 | /// Tag helper component for client side localized validation scripts. 13 | /// 14 | public class LocalizationValidationScriptsTagHelperComponent : TagHelperComponent 15 | { 16 | 17 | #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 18 | private readonly IHostingEnvironment _hosting; 19 | 20 | /// 21 | /// Tag helper component for client side localized validation scripts. 22 | /// 23 | /// 24 | public LocalizationValidationScriptsTagHelperComponent(IHostingEnvironment hosting) 25 | { 26 | _hosting = hosting; 27 | } 28 | #else 29 | private readonly IWebHostEnvironment _hosting; 30 | 31 | /// 32 | /// inserts all localizaiton validation scripts into relevant tag 33 | /// 34 | /// 35 | public LocalizationValidationScriptsTagHelperComponent(IWebHostEnvironment hosting) 36 | { 37 | _hosting = hosting; 38 | } 39 | #endif 40 | /// 41 | /// default order is 0 42 | /// 43 | public override int Order => 1; 44 | 45 | /// 46 | /// generate the taghelper 47 | /// 48 | /// 49 | /// 50 | public override void Process(TagHelperContext context, TagHelperOutput output) 51 | { 52 | if (string.Equals(context.TagName, "localization-validation-scripts", StringComparison.OrdinalIgnoreCase)) 53 | { 54 | //read source attribute 55 | var sourceAttribute = GetAttribute(context, "source", ScriptSource.JsDeliver); 56 | 57 | //get the value of the source property 58 | Enum.TryParse(sourceAttribute.Value.ToString(), out ScriptSource _scriptSource); 59 | //assign relevant script file accordingly 60 | var _script = _scriptSource == ScriptSource.JsDeliver 61 | ? Resources.LocalizationValidationScripts_jsdeliver 62 | : Resources.LocalizationValidationScripts_local; 63 | 64 | //read cldr-core-version attribute 65 | var cldrCoreVersionAttribute = GetAttribute(context, "cldr-core-version", "35.1.0"); 66 | var cldrCoreVersion = cldrCoreVersionAttribute.Value.ToString(); 67 | 68 | var culture = _scriptSource == ScriptSource.JsDeliver 69 | ? CultureInfo.CurrentCulture.Name 70 | : GetCultureName(); 71 | 72 | // Some cultures do not have scripts in jsDelivr e.g. en-us, es-es, 73 | // so switch to parent culture 74 | string[] nonSupportedCultres = { "en-us", "es-es" }; 75 | culture = nonSupportedCultres.Any(x => x.Equals(culture, StringComparison.OrdinalIgnoreCase)) 76 | ? culture.Split('-')[0] 77 | : culture; 78 | 79 | output.PostContent.AppendHtml(_script.Replace("{culture}", culture) 80 | .Replace("{cldr-core-version}", cldrCoreVersion)); 81 | } 82 | } 83 | 84 | private TagHelperAttribute GetAttribute(TagHelperContext context, string tagName, object defaultValue) 85 | { 86 | //get the source property from the taghelper 87 | context.AllAttributes.TryGetAttribute(tagName, out TagHelperAttribute attribute); 88 | 89 | return attribute ?? new TagHelperAttribute(tagName, defaultValue); 90 | } 91 | 92 | /// 93 | /// find json files related to the current culture, if not found return parent culture, if not found return default culture. 94 | /// see ClientSideValidationScripts.html for how to configure paths 95 | /// 96 | /// culture name e.g. tr 97 | private string GetCultureName() 98 | { 99 | // use this pattern to check if the relevant json folder are available 100 | const string localePattern = "lib\\cldr-data\\main\\{0}"; 101 | var currentCulture = CultureInfo.CurrentCulture; 102 | var cultureToUse = "en"; //Default regionalisation to use 103 | 104 | if (Directory.Exists(Path.Combine(_hosting.WebRootPath, string.Format(localePattern, currentCulture.Name)))) 105 | cultureToUse = currentCulture.Name; 106 | else if (Directory.Exists(Path.Combine(_hosting.WebRootPath, string.Format(localePattern, currentCulture.TwoLetterISOLanguageName)))) 107 | cultureToUse = currentCulture.TwoLetterISOLanguageName; 108 | 109 | return cultureToUse; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace LazZiya.TagHelpers.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LazZiya.TagHelpers.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to <!-- cldr scripts (needed for globalize) --> 65 | ///<script src="https://cdn.jsdelivr.net/gh/rxaviers/cldrjs@0.5.1/dist/cldr.js"></script> 66 | ///<script src="https://cdn.jsdelivr.net/gh/rxaviers/cldrjs@0.5.1/dist/cldr/event.js"></script> 67 | ///<script src="https://cdn.jsdelivr.net/gh/rxaviers/cldrjs@0.5.1/dist/cldr/supplemental.js"></script> 68 | /// 69 | ///<!-- globalize scripts --> 70 | ///<script src="https://cdn.jsdelivr.net/gh/globalizejs/globalize@1.4.2/dist/globalize.js"></script> 71 | ///<script src="https://cdn.jsdelivr.net/gh/globalizejs/g [rest of string was truncated]";. 72 | /// 73 | internal static string LocalizationValidationScripts_jsdeliver { 74 | get { 75 | return ResourceManager.GetString("LocalizationValidationScripts_jsdeliver", resourceCulture); 76 | } 77 | } 78 | 79 | /// 80 | /// Looks up a localized string similar to <!-- cldr scripts (needed for globalize) --> 81 | ///<script src="/lib/cldr/dist/cldr.min.js"></script> 82 | ///<script src="/lib/cldr/dist/cldr/event.min.js"></script> 83 | ///<script src="/lib/cldr/dist/cldr/supplemental.min.js"></script> 84 | /// 85 | ///<!-- globalize scripts --> 86 | ///<script src="/lib/globalize/dist/globalize.min.js"></script> 87 | ///<script src="/lib/globalize/dist/globalize/number.min.js"></script> 88 | ///<script src="/lib/globalize/dist/globalize/date.min.js"></script> 89 | ///<script src="/lib/globalize/dist/globalize/currency.min.js"></s [rest of string was truncated]";. 90 | /// 91 | internal static string LocalizationValidationScripts_local { 92 | get { 93 | return ResourceManager.GetString("LocalizationValidationScripts_local", resourceCulture); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Alerts/AlertModels.cs: -------------------------------------------------------------------------------- 1 | namespace LazZiya.TagHelpers.Alerts 2 | { 3 | /// 4 | /// Define alert style depending on Bootstrap4.x alert classes 5 | /// 6 | internal enum AlertStyle { 7 | /// 8 | /// alert-primary 9 | /// 10 | Primary, 11 | 12 | /// 13 | /// alert-secondary 14 | /// 15 | Secondary, 16 | 17 | /// 18 | /// alert-success 19 | /// 20 | Success, 21 | 22 | /// 23 | /// alert-danger 24 | /// 25 | Danger, 26 | 27 | /// 28 | /// alert-warning 29 | /// 30 | Warning, 31 | 32 | /// 33 | /// alert-info 34 | /// 35 | Info, 36 | 37 | /// 38 | /// alert-light 39 | /// 40 | Light, 41 | 42 | /// 43 | /// alert-dark 44 | /// 45 | Dark 46 | } 47 | 48 | /// 49 | /// Choose where to get alert icons from 50 | /// 51 | public enum IconsSource 52 | { 53 | /// 54 | /// Bootstrap 55 | /// 56 | Bootstrap, 57 | 58 | /// 59 | /// Bootstrap5 60 | /// 61 | Bootstrap5, 62 | 63 | /// 64 | /// FontAwesome 65 | /// 66 | FontAwesome 67 | } 68 | 69 | internal struct Bootstrap5Icons 70 | { 71 | internal const string Success = ""; 72 | 73 | internal const string Warning = ""; 74 | 75 | internal const string Info = ""; 76 | 77 | internal const string Danger = ""; 78 | 79 | internal const string Default = Info; 80 | } 81 | 82 | internal struct BootstrapIcons 83 | { 84 | internal const string Success = "bi bi-check-circle-fill"; 85 | internal const string Warning = "bi bi-exclamation-triangle-fill"; 86 | internal const string Info = "bi bi-info-circle-fill"; 87 | internal const string Danger = "bi bi-x-circle-fill"; 88 | internal const string Default = "bi bi-info-circle"; 89 | } 90 | 91 | internal struct FontAwesomeIcons 92 | { 93 | internal const string Success = "fas fa-check-circle"; 94 | internal const string Warning = "fas fa-exclamation-triangle"; 95 | internal const string Info = "fas fa-info-circle"; 96 | internal const string Danger = "fas fa-times-circle"; 97 | internal const string Default = "fas fa-chevron-circle-right"; 98 | } 99 | 100 | /// 101 | /// Alert item that can be created in the backend manually for pushing alert to the temp data 102 | /// 103 | internal class Alert 104 | { 105 | /// 106 | /// Key to find alerts in TempData dictionary 107 | /// 108 | public const string TempDataKey = "TempDataAlert"; 109 | 110 | /// 111 | /// Alert style depending on Bootstrap 4.x classes 112 | /// 113 | public AlertStyle AlertStyle { get; set; } = AlertStyle.Primary; 114 | 115 | /// 116 | /// Header text for the alert message 117 | /// 118 | public string AlertHeading { get; set; } 119 | 120 | /// 121 | /// Alert message body 122 | /// 123 | public string AlertMessage { get; set; } 124 | 125 | /// 126 | /// true for dismissable alert 127 | /// 128 | public bool Dismissable { get; set; } = true; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Alerts/AlertPageModelExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LazZiya.TagHelpers.Utilities; 3 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 4 | #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 5 | using Newtonsoft.Json; 6 | #else 7 | using System.Text.Json; 8 | #endif 9 | 10 | namespace LazZiya.TagHelpers.Alerts 11 | { 12 | /// 13 | /// Extensions for TempData for creating coder friendly alerts easily 14 | /// 15 | public static class TempDataExtensions 16 | { 17 | /// 18 | /// Create primary alert 19 | /// 20 | /// TempData 21 | /// message body 22 | /// message header 23 | /// Show closing button 24 | public static void Primary(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 25 | { 26 | AddAlert(tempData, AlertStyle.Primary, alertMessage, alertHeader, dismissable); 27 | } 28 | 29 | /// 30 | /// Create secondary alert 31 | /// 32 | /// TempData 33 | /// message body 34 | /// message header 35 | /// Show closing button 36 | public static void Secondary(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 37 | { 38 | AddAlert(tempData, AlertStyle.Secondary, alertMessage, alertHeader, dismissable); 39 | } 40 | 41 | /// 42 | /// Create success alert 43 | /// 44 | /// TempData 45 | /// message body 46 | /// message header 47 | /// Show closing button 48 | public static void Success(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 49 | { 50 | AddAlert(tempData, AlertStyle.Success, alertMessage, alertHeader, dismissable); 51 | } 52 | 53 | /// 54 | /// Create danger alert 55 | /// 56 | /// TempData 57 | /// message body 58 | /// message header 59 | /// Show closing button 60 | public static void Danger(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 61 | { 62 | AddAlert(tempData, AlertStyle.Danger, alertMessage, alertHeader, dismissable); 63 | } 64 | 65 | /// 66 | /// Create warning alert 67 | /// 68 | /// TempData 69 | /// message body 70 | /// message header 71 | /// Show closing button 72 | public static void Warning(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 73 | { 74 | AddAlert(tempData, AlertStyle.Warning, alertMessage, alertHeader, dismissable); 75 | } 76 | 77 | /// 78 | /// Create info alert 79 | /// 80 | /// TempData 81 | /// message body 82 | /// message header 83 | /// Show closing button 84 | public static void Info(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 85 | { 86 | AddAlert(tempData, AlertStyle.Info, alertMessage, alertHeader, dismissable); 87 | } 88 | 89 | /// 90 | /// Create light alert 91 | /// 92 | /// TempData 93 | /// message body 94 | /// message header 95 | /// Show closing button 96 | public static void Light(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 97 | { 98 | AddAlert(tempData, AlertStyle.Light, alertMessage, alertHeader, dismissable); 99 | } 100 | 101 | /// 102 | /// Create dark alert 103 | /// 104 | /// TempData 105 | /// message body 106 | /// message header 107 | /// Show closing button 108 | public static void Dark(this ITempDataDictionary tempData, string alertMessage, string alertHeader = "", bool dismissable = true) 109 | { 110 | AddAlert(tempData, AlertStyle.Dark, alertMessage, alertHeader, dismissable); 111 | } 112 | 113 | private static void AddAlert(ITempDataDictionary tempData, AlertStyle alertStyle, string message, string header, bool dismissable) 114 | { 115 | #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 116 | var alerts = tempData.ContainsKey(Alert.TempDataKey) 117 | ? JsonConvert.DeserializeObject>(tempData[Alert.TempDataKey].ToString()) 118 | : new List(); 119 | #else 120 | var alerts = tempData.ContainsKey(Alert.TempDataKey) 121 | ? JsonSerializer.Deserialize>(tempData[Alert.TempDataKey].ToString()) 122 | : new List(); 123 | #endif 124 | alerts.Add(new Alert 125 | { 126 | AlertStyle = alertStyle, 127 | AlertHeading = header, 128 | AlertMessage = message, 129 | Dismissable = dismissable 130 | }); 131 | 132 | tempData.Put>(Alert.TempDataKey, alerts); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\files\templates\localizationvalidationscripts_jsdeliver.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 123 | 124 | 125 | ..\files\templates\localizationvalidationscripts_local.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 126 | 127 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/Utilities/NumberFormats.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace LazZiya.TagHelpers.Utilities 5 | { 6 | /// 7 | /// Number formats for different cultures. 8 | /// https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/numberingSystems.json 9 | /// 10 | public static class NumberFormats 11 | { 12 | /// 13 | /// Receives a number in system format, and converts it to any other format. 14 | /// See 15 | /// 16 | /// 17 | /// 18 | /// 19 | public static string ToNumberFormat(this int number, string targetFormat) 20 | { 21 | string _str = string.Empty; 22 | switch (targetFormat) 23 | { 24 | case NumberFormats.Default: _str = number.ToString("N0"); break; 25 | case NumberFormats.Hex: _str = number.ToString("X"); break; 26 | case NumberFormats.Roman: _str = ((uint)number).ToRoman(); break; 27 | default: 28 | var numberStr = number.ToString(); 29 | var newNum = string.Empty; 30 | 31 | for (int i = 0; i < numberStr.Length; i++) 32 | newNum += targetFormat.Split(' ')[int.Parse(numberStr[i].ToString())]; 33 | 34 | _str = string.Join("", newNum); 35 | break; 36 | } 37 | 38 | return _str; 39 | } 40 | 41 | /// 42 | /// System default numbering format 43 | /// 44 | public const string Default = "default"; 45 | 46 | /// 47 | /// 0123456789 48 | /// 49 | public const string Arabic = "0 1 2 3 4 5 6 7 8 9"; 50 | 51 | /// 52 | /// Use hexadecimal numbering system 53 | /// 54 | public const string Hex = "hex"; 55 | 56 | /// 57 | /// I II III IV V VI 58 | /// 59 | public const string Roman = "roman"; 60 | 61 | /// 62 | /// ٠١٢٣٤٥٦٧٨٩ 63 | /// 64 | public const string Hindi = "٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩"; 65 | 66 | /// 67 | /// 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯 68 | /// 69 | public const string Brah = "𑁦 𑁧 𑁨 𑁩 𑁪 𑁫 𑁬 𑁭 𑁮 𑁯"; 70 | 71 | /// 72 | /// ০১২৩৪৫৬৭৮৯ 73 | /// 74 | public const string Beng = "০ ১ ২ ৩ ৪ ৫ ৬ ৭ ৮ ৯"; 75 | 76 | /// 77 | /// ०१२३४५६७८९ 78 | /// 79 | public const string Deva = "० १ २ ३ ४ ५ ६ ७ ८ ९"; 80 | 81 | /// 82 | /// ۰۱۲۳۴۵۶۷۸۹ 83 | /// 84 | public const string Farsi = "۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹"; 85 | 86 | /// 87 | /// 0123456789 88 | /// 89 | public const string Fullwide = "0 1 2 3 4 5 6 7 8 9"; 90 | 91 | /// 92 | /// ೦೧೨೩೪೫೬೭೮೯ 93 | /// 94 | public const string Knda = "೦ ೧ ೨ ೩ ೪ ೫ ೬ ೭ ೮ ೯"; 95 | 96 | /// 97 | /// ૦૧૨૩૪૫૬૭૮૯ 98 | /// 99 | public const string Gujr = "૦ ૧ ૨ ૩ ૪ ૫ ૬ ૭ ૮ ૯"; 100 | 101 | /// 102 | /// ੦੧੨੩੪੫੬੭੮੯ 103 | /// 104 | public const string Guru = "੦ ੧ ੨ ੩ ੪ ੫ ੬ ੭ ੮ ੯"; 105 | 106 | /// 107 | /// 〇一二三四五六七八九 108 | /// 109 | public const string Hanidec = "〇 一 二 三 四 五 六 七 八 九"; 110 | 111 | /// 112 | /// ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙ 113 | /// 114 | public const string Java = "꧐ ꧑ ꧒ ꧓ ꧔ ꧕ ꧖ ꧗ ꧘ ꧙"; 115 | 116 | /// 117 | /// ០១២៣៤៥៦៧៨៩ 118 | /// 119 | public const string Khmr = "០ ១ ២ ៣ ៤ ៥ ៦ ៧ ៨ ៩"; 120 | 121 | /// 122 | /// ໐໑໒໓໔໕໖໗໘໙ 123 | /// 124 | public const string Laoo = "໐ ໑ ໒ ໓ ໔ ໕ ໖ ໗ ໘ ໙"; 125 | 126 | /// 127 | /// 0123456789 128 | /// 129 | public const string Latin = "0 1 2 3 4 5 6 7 8 9"; 130 | 131 | /// 132 | /// 𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗 133 | /// 134 | public const string Mathbold = "𝟎 𝟏 𝟐 𝟑 𝟒 𝟓 𝟔 𝟕 𝟖 𝟗"; 135 | 136 | /// 137 | /// 𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡 138 | /// 139 | public const string Mathborder = "𝟘 𝟙 𝟚 𝟛 𝟜 𝟝 𝟞 𝟟 𝟠 𝟡"; 140 | 141 | /// 142 | /// 𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿 143 | /// 144 | public const string Mathmono = "𝟶 𝟷 𝟸 𝟹 𝟺 𝟻 𝟼 𝟽 𝟾 𝟿"; 145 | 146 | /// 147 | /// 𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵 148 | /// 149 | public const string Mathanb = "𝟬 𝟭 𝟮 𝟯 𝟰 𝟱 𝟲 𝟳 𝟴 𝟵"; 150 | 151 | /// 152 | /// 𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫 153 | /// 154 | public const string Mathsans = "𝟢 𝟣 𝟤 𝟥 𝟦 𝟧 𝟨 𝟩 𝟪 𝟫"; 155 | 156 | /// 157 | /// ൦൧൨൩൪൫൬൭൮൯ 158 | /// 159 | public const string Mlym = "൦ ൧ ൨ ൩ ൪ ൫ ൬ ൭ ൮ ൯"; 160 | 161 | /// 162 | /// ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ 163 | /// 164 | public const string Mong = "᠐ ᠑ ᠒ ᠓ ᠔ ᠕ ᠖ ᠗ ᠘ ᠙"; 165 | 166 | /// 167 | /// ၀၁၂၃၄၅၆၇၈၉ 168 | /// 169 | public const string Mymr = "၀ ၁ ၂ ၃ ၄ ၅ ၆ ၇ ၈ ၉"; 170 | 171 | /// 172 | /// ႐႑႒႓႔႕႖႗႘႙ 173 | /// 174 | public const string Mymrshan = "႐ ႑ ႒ ႓ ႔ ႕ ႖ ႗ ႘ ႙"; 175 | 176 | /// 177 | /// ꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹ 178 | /// 179 | public const string Mymtlng = "꧰ ꧱ ꧲ ꧳ ꧴ ꧵ ꧶ ꧷ ꧸ ꧹"; 180 | 181 | /// 182 | /// ߀߁߂߃߄߅߆߇߈߉ 183 | /// 184 | public const string Nkoo = "߀ ߁ ߂ ߃ ߄ ߅ ߆ ߇ ߈ ߉"; 185 | 186 | /// 187 | /// ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ 188 | /// 189 | public const string Olck = "᱐ ᱑ ᱒ ᱓ ᱔ ᱕ ᱖ ᱗ ᱘ ᱙"; 190 | 191 | /// 192 | /// ୦୧୨୩୪୫୬୭୮୯ 193 | /// 194 | public const string Orya = "୦ ୧ ୨ ୩ ୪ ୫ ୬ ୭ ୮ ୯"; 195 | 196 | /// 197 | /// 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩 198 | /// 199 | public const string Osma = "𐒠 𐒡 𐒢 𐒣 𐒤 𐒥 𐒦 𐒧 𐒨 𐒩"; 200 | 201 | /// 202 | /// ෦෧෨෩෪෫෬෭෮෯ 203 | /// 204 | public const string Sinh = "෦ ෧ ෨ ෩ ෪ ෫ ෬ ෭ ෮ ෯"; 205 | 206 | /// 207 | /// ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙ 208 | /// 209 | public const string Talu = "᧐ ᧑ ᧒ ᧓ ᧔ ᧕ ᧖ ᧗ ᧘ ᧙"; 210 | 211 | /// 212 | /// ௦௧௨௩௪௫௬௭௮௯ 213 | /// 214 | public const string Tamldec = "௦ ௧ ௨ ௩ ௪ ௫ ௬ ௭ ௮ ௯"; 215 | 216 | /// 217 | /// ౦౧౨౩౪౫౬౭౮౯ 218 | /// 219 | public const string Telu = "౦ ౧ ౨ ౩ ౪ ౫ ౬ ౭ ౮ ౯"; 220 | 221 | /// 222 | /// ๐๑๒๓๔๕๖๗๘๙ 223 | /// 224 | public const string Thai = "๐ ๑ ๒ ๓ ๔ ๕ ๖ ๗ ๘ ๙"; 225 | 226 | /// 227 | /// ༠༡༢༣༤༥༦༧༨༩ 228 | /// 229 | public const string Tibt = "༠ ༡ ༢ ༣ ༤ ༥ ༦ ༧ ༨ ༩"; 230 | 231 | /// 232 | /// ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ 233 | /// 234 | public const string Vaii = "꘠ ꘡ ꘢ ꘣ ꘤ ꘥ ꘦ ꘧ ꘨ ꘩"; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/AlertTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Rendering; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | using System; 4 | using System.Collections.Generic; 5 | using LazZiya.TagHelpers.Alerts; 6 | using System.Threading.Tasks; 7 | using LazZiya.TagHelpers.Utilities; 8 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 9 | using Microsoft.Extensions.Localization; 10 | using System.Linq; 11 | 12 | namespace LazZiya.TagHelpers 13 | { 14 | /// 15 | /// Create alert messages styled with bootstrap 4.x 16 | /// Alert contents must be replaced between alert tags e.g. job done!]]> 17 | /// 18 | public class AlertTagHelper : TagHelper 19 | { 20 | internal AlertStyle AlertStyle { get; set; } = AlertStyle.Primary; 21 | 22 | /// 23 | /// Header text for the alert 24 | /// 25 | public string AlertHeading { get; set; } 26 | 27 | /// 28 | /// Show closing button, default is true 29 | /// 30 | public bool Dismissable { get; set; } = true; 31 | 32 | /// 33 | /// Show multiple alerts as slides 34 | /// 35 | public bool SlideAlerts { get; set; } = true; 36 | 37 | /// 38 | /// Show alert icons from fontawesome. 39 | /// Requires fontawesome css or bootstrap 40 | /// 41 | public bool ShowIcons { get; set; } = true; 42 | 43 | /// 44 | /// Choose where to get icons source from. "Bootstrap" or "FontAwesome". 45 | /// 46 | public IconsSource IconsSource { get; set; } = IconsSource.FontAwesome; 47 | 48 | /// 49 | /// Choose render mode: Bootstrap5 if your project is using bootstrap5, otherwise default is Bootstrap for earlier versions. 50 | /// 51 | public RenderMode RenderMode { get; set; } = RenderMode.Bootstrap; 52 | 53 | /// 54 | /// Parse localizer instance to localize alert message 55 | /// 56 | public IStringLocalizer Localizer { get; set; } 57 | 58 | /// 59 | /// ViewContext property is not required to be passed as parameter, it will be assigned automatically by the tag helper. 60 | /// View context is required to access TempData dictionary that contains the alerts coming from backend 61 | /// 62 | [ViewContext] 63 | public ViewContext ViewContext { get; set; } = null; 64 | 65 | /// 66 | /// Create alert messages styled with bootstrap 4.x 67 | /// 68 | public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 69 | { 70 | output.TagName = "div"; 71 | 72 | if (ViewContext != null) 73 | { 74 | var alerts = ViewContext.TempData.ContainsKey(Alert.TempDataKey) 75 | ? ViewContext.TempData.Get>(Alert.TempDataKey) 76 | : new List(); 77 | if (alerts.Count > 1 && SlideAlerts) 78 | output.Content.AppendHtml(AddAlertCarousel(alerts)); 79 | else 80 | alerts.ForEach(a => output.Content.AppendHtml(AddAlert(a))); 81 | 82 | ViewContext.TempData.Remove(Alert.TempDataKey); 83 | } 84 | 85 | // read alerts contents from inner html 86 | var msg = await output.GetChildContentAsync(); 87 | 88 | if (!string.IsNullOrWhiteSpace(msg.GetContent())) 89 | { 90 | var manualAlert = AddAlert(new Alert 91 | { 92 | AlertHeading = this.AlertHeading, 93 | AlertMessage = msg.GetContent(), 94 | AlertStyle = this.AlertStyle, 95 | Dismissable = this.Dismissable 96 | }); 97 | output.Content.AppendHtml(manualAlert); 98 | } 99 | 100 | } 101 | 102 | private TagBuilder AddAlert(Alert alert) 103 | { 104 | var _alert = new TagBuilder("div"); 105 | 106 | string alertIcon; 107 | switch (alert.AlertStyle) 108 | { 109 | case AlertStyle.Success: 110 | alertIcon = 111 | IconsSource == IconsSource.Bootstrap ? BootstrapIcons.Success : 112 | IconsSource == IconsSource.Bootstrap5 ? Bootstrap5Icons.Success : 113 | FontAwesomeIcons.Success; 114 | break; 115 | 116 | case AlertStyle.Warning: 117 | alertIcon = 118 | IconsSource == IconsSource.Bootstrap ? BootstrapIcons.Warning : 119 | IconsSource == IconsSource.Bootstrap5 ? Bootstrap5Icons.Warning : 120 | FontAwesomeIcons.Warning; 121 | break; 122 | 123 | case AlertStyle.Info: 124 | alertIcon = 125 | IconsSource == IconsSource.Bootstrap ? BootstrapIcons.Info : 126 | IconsSource == IconsSource.Bootstrap5 ? Bootstrap5Icons.Info : 127 | FontAwesomeIcons.Info; 128 | break; 129 | 130 | case AlertStyle.Danger: 131 | alertIcon = 132 | IconsSource == IconsSource.Bootstrap ? BootstrapIcons.Danger : 133 | IconsSource == IconsSource.Bootstrap5 ? Bootstrap5Icons.Danger : 134 | FontAwesomeIcons.Danger; 135 | break; 136 | default: 137 | alertIcon = 138 | IconsSource == IconsSource.Bootstrap ? BootstrapIcons.Default : 139 | IconsSource == IconsSource.Bootstrap5 ? Bootstrap5Icons.Default : 140 | FontAwesomeIcons.Default; 141 | break; 142 | } 143 | 144 | var alertStyle = Enum.GetName(typeof(AlertStyle), alert.AlertStyle).ToLowerInvariant(); 145 | _alert.AddCssClass($"alert alert-{alertStyle}"); 146 | _alert.Attributes.Add("role", "alert"); 147 | 148 | if (alert.Dismissable) 149 | { 150 | if (RenderMode == RenderMode.Bootstrap5) 151 | { 152 | _alert.AddCssClass("alert-dismissible fade show"); 153 | _alert.InnerHtml.AppendHtml(""); 154 | } 155 | else 156 | _alert.InnerHtml.AppendHtml(""); 157 | } 158 | 159 | if (!string.IsNullOrWhiteSpace(alert.AlertHeading)) 160 | { 161 | var heading = Localizer == null ? alert.AlertHeading : Localizer[alert.AlertHeading]; 162 | _alert.InnerHtml.AppendHtml($"

{heading}

"); 163 | } 164 | 165 | if (!string.IsNullOrWhiteSpace(alert.AlertMessage)) 166 | { 167 | var msg = Localizer == null ? alert.AlertMessage : Localizer[alert.AlertMessage]; 168 | 169 | if (RenderMode == RenderMode.Bootstrap5 && IconsSource == IconsSource.Bootstrap5) 170 | { 171 | _alert.AddCssClass("d-flex align-items-center"); 172 | var icon = ShowIcons ? alertIcon : string.Empty; 173 | _alert.InnerHtml.AppendHtml($"
{icon}{msg}
"); 174 | } 175 | else 176 | { 177 | var icon = ShowIcons ? $" " : string.Empty; 178 | _alert.InnerHtml.AppendHtml($"

{icon}{msg}

"); 179 | } 180 | } 181 | 182 | return _alert; 183 | } 184 | 185 | private TagBuilder AddAlertCarousel(List alerts) 186 | { 187 | var carouselId = "alertCarouselControls"; 188 | 189 | var indic = new TagBuilder("ol"); 190 | indic.AddCssClass("carousel-indicators"); 191 | 192 | for (int i = 0; i < alerts.Count; i++) 193 | { 194 | var indicItem = new TagBuilder("li"); 195 | 196 | if (RenderMode == RenderMode.Bootstrap5) 197 | { 198 | indicItem.Attributes.Add("data-bs-target", $"#{carouselId}"); 199 | indicItem.Attributes.Add("data-bs-slide-to", $"{i}"); 200 | } 201 | else 202 | { 203 | indicItem.Attributes.Add("data-target", $"#{carouselId}"); 204 | indicItem.Attributes.Add("data-slide-to", $"{i}"); 205 | } 206 | if (i == 0) 207 | indicItem.AddCssClass("active"); 208 | indic.InnerHtml.AppendHtml(indicItem); 209 | } 210 | 211 | var carouselInner = new TagBuilder("div"); 212 | carouselInner.AddCssClass("carousel-inner"); 213 | for (int i = 0; i < alerts.Count; i++) 214 | { 215 | var slide = new TagBuilder("div"); 216 | if (i == 0) 217 | slide.AddCssClass("carousel-item active"); 218 | else 219 | slide.AddCssClass("carousel-item"); 220 | 221 | // remove dismissabel property from inner alerts 222 | // only the most outer alert will have dismissable property 223 | alerts[i].Dismissable = false; 224 | slide.InnerHtml.AppendHtml(AddAlert(alerts[i])); 225 | 226 | carouselInner.InnerHtml.AppendHtml(slide); 227 | } 228 | 229 | var carouselDiv = new TagBuilder("div"); 230 | carouselDiv.AddCssClass("carousel slide"); 231 | 232 | carouselDiv.Attributes.Add("id", carouselId); 233 | if (RenderMode == RenderMode.Bootstrap5) 234 | carouselDiv.Attributes.Add("data-bs-ride", "carousel"); 235 | else 236 | carouselDiv.Attributes.Add("data-ride", "carousel"); 237 | 238 | carouselDiv.InnerHtml.AppendHtml(indic); 239 | carouselDiv.InnerHtml.AppendHtml(carouselInner); 240 | 241 | // This is the main alert that will hold the alerts carousel 242 | var mainAlert = new TagBuilder("div"); 243 | mainAlert.Attributes.Add("role", "alert"); 244 | mainAlert.InnerHtml.AppendHtml(carouselDiv); 245 | 246 | if (RenderMode == RenderMode.Bootstrap5) 247 | { 248 | mainAlert.AddCssClass("alert alert-dismissible fade show p-0"); 249 | mainAlert.InnerHtml.AppendHtml(""); 250 | } 251 | else 252 | { 253 | mainAlert.AddCssClass("alert alert-dismissible p-0"); 254 | mainAlert.InnerHtml.AppendHtml(""); 255 | } 256 | return mainAlert; 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/LanguageNavTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Mvc.Rendering; 3 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 4 | using Microsoft.AspNetCore.Razor.TagHelpers; 5 | using Microsoft.Extensions.Options; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using System.Linq; 10 | using System.Text; 11 | 12 | namespace LazZiya.TagHelpers 13 | { 14 | /// 15 | /// creates a language navigation menu, depends on supported cultures 16 | /// 17 | public class LanguageNavTagHelper : TagHelper 18 | { 19 | /// 20 | /// optional: manually specify list of supported cultures 21 | /// 22 | /// en-US,tr, ar 23 | /// 24 | /// 25 | public string SupportedCultures { get; set; } 26 | 27 | /// 28 | /// optional: what to display as label for language dropdown 29 | /// default: LanguageLabel.EnglishName 30 | /// 31 | public LanguageLabel LanguageLabel { get; set; } = LanguageLabel.EnglishName; 32 | 33 | /// 34 | /// ViewContext property is not required to be passed as parameter, it will be auto assigned by the tag helpoer. 35 | /// current view context to access RouteData.Values and Request.Query collection 36 | /// 37 | [ViewContext] 38 | public ViewContext ViewContext { get; set; } 39 | 40 | /// 41 | /// Choose render mode: classis for regular dropdown list, Bootstrap4 for HTML5 div with Bootstrap4 style. 42 | /// 43 | public RenderMode RenderMode { get; set; } = RenderMode.Bootstrap; 44 | 45 | /// 46 | /// Set the handler url for setting culture cookie on language change 47 | /// 48 | public string CookieHandlerUrl { get; set; } 49 | 50 | /// 51 | /// The url to redirect to on langugae change. 52 | /// The url must have at one place holder for culture value. 53 | /// e.g. /{0}/Home 54 | /// 55 | public string RedirectToUrl { get; set; } 56 | 57 | /// 58 | /// Show relevant country flag for specific culture. 59 | /// Flags will be shown only if the culture is country specific. 60 | /// e.g. "tr" will not render any flag, but "tr-sy" will render Turkish flag. 61 | /// Required reference to flag-icon-css 62 | /// 63 | public bool Flags { get; set; } = false; 64 | 65 | /// 66 | /// true: Show flags in squared images, 67 | /// false: Show flags in rounded images, 68 | /// 69 | public bool FlagsSquared { get; set; } = false; 70 | 71 | /// 72 | /// Whether show border or not 73 | /// 74 | public bool Border { get; set; } = true; 75 | 76 | /// 77 | /// required for listing supported cultures. 78 | /// The handler must contain two place holders for culture name and return url. 79 | /// e.g.: 80 | /// 81 | private readonly IOptions _ops; 82 | 83 | /// 84 | /// creates a language navigation menu, depends on supported cultures 85 | /// 86 | /// 87 | public LanguageNavTagHelper(IOptions ops) 88 | { 89 | _ops = ops; 90 | } 91 | 92 | /// 93 | /// start creating the language navigation dropdown 94 | /// 95 | /// 96 | /// 97 | public override void Process(TagHelperContext context, TagHelperOutput output) 98 | { 99 | var langDictionary = CreateNavDictionary(); 100 | 101 | switch (RenderMode) 102 | { 103 | case RenderMode.Bootstrap: 104 | CreateBootstrapItems(ref output, langDictionary); 105 | break; 106 | case RenderMode.Bootstrap5: 107 | CreateBootstrap5Items(ref output, langDictionary); 108 | break; 109 | case RenderMode.Classic: 110 | CreateClassicItems(ref output, langDictionary); 111 | break; 112 | case RenderMode.FormControl: 113 | CreateFormControlItems(ref output, langDictionary); 114 | break; 115 | } 116 | } 117 | 118 | /// 119 | /// create classic list items list 120 | /// English]]> 121 | /// 122 | /// language name-URL dictionary 123 | /// reference to TagHelperOuput 124 | /// 125 | private void CreateClassicItems(ref TagHelperOutput output, List langDictionary) 126 | { 127 | output.TagName = "select"; 128 | output.Attributes.Add("onchange", "this.options[this.selectedIndex].value && (window.location = this.options[this.selectedIndex].value);"); 129 | 130 | foreach (var lang in langDictionary.OrderBy(x => x.DisplayText)) 131 | { 132 | var option = new TagBuilder("option"); 133 | option.Attributes.Add("value", lang.Url); 134 | option.InnerHtml.AppendHtml(lang.DisplayText); 135 | 136 | if (CultureInfo.CurrentCulture.Name == lang.Name) 137 | option.Attributes.Add("selected", "selected"); 138 | 139 | output.Content.AppendHtml(option); 140 | } 141 | } 142 | 143 | /// 144 | /// create a dropdown form control 145 | /// English]]> 146 | /// 147 | /// language name-URL dictionary 148 | /// reference to TagHelperOuput 149 | /// 150 | private void CreateFormControlItems(ref TagHelperOutput output, List langDictionary) 151 | { 152 | output.TagName = "select"; 153 | 154 | foreach (var lang in langDictionary.OrderBy(x => x.DisplayText)) 155 | { 156 | var option = new TagBuilder("option"); 157 | option.Attributes.Add("value", lang.Name); 158 | option.InnerHtml.AppendHtml(lang.DisplayText); 159 | 160 | if (CultureInfo.CurrentCulture.Name == lang.Name) 161 | option.Attributes.Add("selected", "selected"); 162 | 163 | output.Content.AppendHtml(option); 164 | } 165 | } 166 | 167 | ///
168 | /// create classic list items list 169 | /// English]]> 170 | /// 171 | /// language name-URL dictionary 172 | /// reference to TagHelperOuput 173 | /// 174 | private void CreateBootstrapItems(ref TagHelperOutput output, List langDictionary) 175 | { 176 | var div = new TagBuilder("div"); 177 | 178 | if (CultureInfo.CurrentCulture.TextInfo.IsRightToLeft) 179 | div.AddCssClass("dropdown-menu dropdown-menu-left"); 180 | else 181 | div.AddCssClass("dropdown-menu dropdown-menu-right"); 182 | 183 | div.Attributes.Add("aria-labeledby", "dropdownlang"); 184 | 185 | foreach (var lang in langDictionary.Where(x => x.Name != CultureInfo.CurrentCulture.Name).OrderBy(x => x.DisplayText)) 186 | { 187 | 188 | var a = new TagBuilder("a"); 189 | a.AddCssClass("dropdown-item small"); 190 | a.Attributes.Add("href", lang.Url); 191 | 192 | if (Flags) 193 | { 194 | var flagName = lang.Name.Split('-'); 195 | if (flagName.Length == 2) 196 | { 197 | 198 | if (FlagsSquared) 199 | a.InnerHtml.AppendHtml($" "); 200 | else 201 | a.InnerHtml.AppendHtml($" "); 202 | } 203 | } 204 | 205 | a.InnerHtml.Append(lang.DisplayText); 206 | 207 | div.InnerHtml.AppendHtml(a); 208 | } 209 | 210 | output.TagName = "div"; 211 | output.Attributes.Add("class", "dropdown"); 212 | 213 | var toggle = CreateToggle(); 214 | output.Content.AppendHtml(toggle); 215 | 216 | output.Content.AppendHtml(div); 217 | } 218 | 219 | /// 220 | /// create classic list items list 221 | /// English]]> 222 | /// 223 | /// language name-URL dictionary 224 | /// reference to TagHelperOuput 225 | /// 226 | private void CreateBootstrap5Items(ref TagHelperOutput output, List langDictionary) 227 | { 228 | var ul = new TagBuilder("ul"); 229 | 230 | if (CultureInfo.CurrentCulture.TextInfo.IsRightToLeft) 231 | ul.AddCssClass("dropdown-menu dropdown-menu-left"); 232 | else 233 | ul.AddCssClass("dropdown-menu dropdown-menu-right"); 234 | 235 | ul.Attributes.Add("aria-labeledby", "dropdownlang"); 236 | 237 | foreach (var lang in langDictionary.Where(x => x.Name != CultureInfo.CurrentCulture.Name).OrderBy(x => x.DisplayText)) 238 | { 239 | var li = new TagBuilder("li"); 240 | var a = new TagBuilder("a"); 241 | a.AddCssClass("dropdown-item small"); 242 | a.Attributes.Add("href", lang.Url); 243 | 244 | if (Flags) 245 | { 246 | var flagName = lang.Name.Split('-'); 247 | if (flagName.Length == 2) 248 | { 249 | if (FlagsSquared) 250 | a.InnerHtml.AppendHtml($" "); 251 | else 252 | a.InnerHtml.AppendHtml($" "); 253 | } 254 | } 255 | 256 | a.InnerHtml.Append(lang.DisplayText); 257 | li.InnerHtml.AppendHtml(a); 258 | ul.InnerHtml.AppendHtml(li); 259 | } 260 | 261 | output.TagName = "div"; 262 | output.Attributes.Add("class", "dropdown"); 263 | 264 | var toggle = CreateToggle(); 265 | output.Content.AppendHtml(toggle); 266 | 267 | output.Content.AppendHtml(ul); 268 | } 269 | 270 | /// 271 | /// create dictonary for all supported cultures 272 | /// Key: Language display name for label 273 | /// Value: Navigation URL 274 | /// 275 | /// 276 | private List CreateNavDictionary() 277 | { 278 | var dic = new List(); 279 | var cultures = GetSupportedCultures(); 280 | 281 | foreach (var cul in cultures) 282 | { 283 | var redUrl = RedirectToUrl ?? "/{0}"; 284 | 285 | var url = string.Format(Uri.UnescapeDataString(redUrl), cul.Name); 286 | 287 | if (!string.IsNullOrWhiteSpace(CookieHandlerUrl)) 288 | { 289 | url = string.Format(Uri.UnescapeDataString(CookieHandlerUrl), cul.Name, url); 290 | } 291 | 292 | var label = GetLanguageLabel(cul); 293 | dic.Add(new LanguageItem { Name = cul.Name, DisplayText = label, Url = url }); 294 | } 295 | 296 | return dic; 297 | } 298 | 299 | /// 300 | /// private list of supported CultureInfo, 301 | /// 302 | private IEnumerable GetSupportedCultures() 303 | { 304 | // if the user didn't specify manually list of supported cultures, 305 | // then create cultures list with reference to supported cultures defined in localization settings in startup 306 | if (string.IsNullOrWhiteSpace(SupportedCultures)) 307 | return _ops.Value.SupportedCultures; 308 | 309 | //if the user will specify supported cultures manually, then this list will be created accordingly 310 | var cList = new List(); 311 | foreach (var c in SupportedCultures.Split(new[] { ',', '|', ';', ' ' }, System.StringSplitOptions.RemoveEmptyEntries)) 312 | { 313 | cList.Add(new CultureInfo(c)); 314 | } 315 | 316 | return cList; 317 | } 318 | 319 | private string GetLanguageLabel(CultureInfo cul) 320 | { 321 | switch (LanguageLabel) 322 | { 323 | case LanguageLabel.Name: return cul.Name; 324 | case LanguageLabel.DisplayName: return cul.DisplayName; 325 | case LanguageLabel.EnglishName: return cul.EnglishName; 326 | case LanguageLabel.NativeName: return cul.NativeName; 327 | case LanguageLabel.TwoLetterISOLanguageName: return cul.TwoLetterISOLanguageName; 328 | 329 | default: return cul.EnglishName; 330 | } 331 | } 332 | 333 | private TagBuilder CreateToggle() 334 | { 335 | var toggle = new TagBuilder("a"); 336 | 337 | var cssBuilder = new StringBuilder("btn-sm btn-default border-secondary dropdown-toggle"); 338 | 339 | if (Border) cssBuilder.Append(" border"); 340 | toggle.AddCssClass(cssBuilder.ToString()); 341 | 342 | toggle.Attributes.Add("id", "dropdownLang"); 343 | toggle.Attributes.Add("href", "#"); 344 | toggle.Attributes.Add("role", "button"); 345 | 346 | if (RenderMode == RenderMode.Bootstrap5) 347 | toggle.Attributes.Add("data-bs-toggle", "dropdown"); 348 | else 349 | toggle.Attributes.Add("data-toggle", "dropdown"); 350 | 351 | toggle.Attributes.Add("aria-haspopup", "true"); 352 | toggle.Attributes.Add("aria-expanded", "false"); 353 | 354 | var labelTxt = GetLanguageLabel(CultureInfo.CurrentCulture); 355 | 356 | if (Flags) 357 | { 358 | var flagName = CultureInfo.CurrentCulture.Name.Split('-'); 359 | if (flagName.Length == 2) 360 | { 361 | if (FlagsSquared) 362 | toggle.InnerHtml.AppendHtml($" "); 363 | else 364 | toggle.InnerHtml.AppendHtml($" "); 365 | } 366 | } 367 | 368 | toggle.InnerHtml.AppendHtml(labelTxt); 369 | 370 | return toggle; 371 | } 372 | /* 373 | private TagBuilder CreateToggle5() 374 | { 375 | var toggle = new TagBuilder("a"); 376 | toggle.AddCssClass("btn-sm btn-default border border-secondary dropdown-toggle"); 377 | toggle.Attributes.Add("id", "dropdownLang"); 378 | toggle.Attributes.Add("href", "#"); 379 | toggle.Attributes.Add("role", "button"); 380 | toggle.Attributes.Add("data-bs-toggle", "dropdown"); 381 | toggle.Attributes.Add("aria-haspopup", "true"); 382 | toggle.Attributes.Add("aria-expanded", "false"); 383 | 384 | var labelTxt = GetLanguageLabel(CultureInfo.CurrentCulture); 385 | 386 | if (Flags) 387 | { 388 | var flagName = CultureInfo.CurrentCulture.Name.Split('-'); 389 | if (flagName.Length == 2) 390 | { 391 | if (FlagsSquared) 392 | toggle.InnerHtml.AppendHtml($" "); 393 | else 394 | toggle.InnerHtml.AppendHtml($" "); 395 | } 396 | } 397 | 398 | toggle.InnerHtml.AppendHtml(labelTxt); 399 | 400 | return toggle; 401 | }*/ 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/PagingTagHelper.cs: -------------------------------------------------------------------------------- 1 | using LazZiya.TagHelpers.Utilities; 2 | using Microsoft.AspNetCore.Mvc.Rendering; 3 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 4 | using Microsoft.AspNetCore.Razor.TagHelpers; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Logging; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | 11 | namespace LazZiya.TagHelpers 12 | { 13 | /// 14 | /// Creates a pagination control 15 | /// 16 | public class PagingTagHelper : TagHelper 17 | { 18 | private IConfiguration Configuration { get; } 19 | private readonly ILogger _logger; 20 | 21 | /// 22 | /// Dictonary object to hold all ajax attributes 23 | /// 24 | private AttributeDictionary AjaxAttributes { get; set; } 25 | 26 | /// 27 | /// A URL template for paging buttons 28 | /// e.g. 29 | /// 30 | /// 31 | public string UrlTemplate { get; set; } 32 | 33 | /// 34 | /// ViewContext property is not required to be passed as parameter, it will be assigned automatically by the tag helper. 35 | /// View context is required to access TempData dictionary that contains the alerts coming from backend 36 | /// 37 | [ViewContext] 38 | public ViewContext ViewContext { get; set; } = null; 39 | 40 | /// 41 | /// Creates a pagination control 42 | /// 43 | public PagingTagHelper(IConfiguration configuration, ILogger logger) 44 | { 45 | Configuration = configuration; 46 | _logger = logger; 47 | } 48 | 49 | #region Settings 50 | 51 | /// 52 | /// current page number. 53 | /// default: 1 54 | /// example: p=1 55 | /// 56 | public int PageNo { get; set; } = 1; 57 | 58 | /// 59 | /// how many items to get from db per page per request 60 | /// default: 10 61 | /// example: pageSize=10 62 | /// 63 | public int PageSize { get; set; } = 10; 64 | 65 | /// 66 | /// Total count of records in the db 67 | /// default: 0 68 | /// 69 | public int TotalRecords { get; set; } = 0; 70 | 71 | /// 72 | /// if count of pages is too much, restrict shown pages count to specific number 73 | /// default: 10 74 | /// 75 | public int? MaxDisplayedPages { get; set; } 76 | 77 | /// 78 | /// name of the settings section in appSettings.json 79 | /// default: "default" 80 | /// 81 | public string SettingsJson { get; set; } = "default"; 82 | 83 | /// 84 | /// Force adding url path to the navigation url 85 | /// in some scenarios when the page is under some area/subFolder 86 | /// The navigation links are pointing to the home page. 87 | /// To force adding url path enable this property 88 | /// 89 | public bool? FixUrlPath { get; set; } = true; 90 | 91 | #endregion 92 | 93 | #region Page size navigation 94 | /// 95 | /// A list of dash delimitted numbers for page size dropdown. 96 | /// default: "10-25-50" 97 | /// 98 | public string PageSizeDropdownItems { get; set; } 99 | 100 | #endregion 101 | 102 | #region QueryString 103 | 104 | /// 105 | /// Query string paramter name for current page. 106 | /// default: p 107 | /// exmaple: p=1 108 | /// 109 | public string QueryStringKeyPageNo { get; set; } 110 | 111 | /// 112 | /// Query string parameter name for page size 113 | /// default: s 114 | /// example: s=25 115 | /// 116 | public string QueryStringKeyPageSize { get; set; } 117 | 118 | #endregion 119 | 120 | #region Display settings 121 | 122 | /// 123 | /// Show drop down list for different page size options 124 | /// default: true 125 | /// options: true, false 126 | /// 127 | public bool? ShowPageSizeNav { get; set; } 128 | 129 | /// 130 | /// Show a three dots after first page or before last page 131 | /// when there is a gap in pages at the beginnig or end 132 | /// 133 | public bool? ShowGap { get; set; } 134 | 135 | /// 136 | /// Show/hide First-Last buttons 137 | /// default: true, if set to false and total pages > max displayed pages it will be true 138 | /// 139 | public bool? ShowFirstLast { get; set; } 140 | 141 | /// 142 | /// Show/hide Previous-Next buttons 143 | /// default: true 144 | /// 145 | public bool? ShowPrevNext { get; set; } 146 | 147 | /// 148 | /// Show or hide total pages count 149 | /// default: true 150 | /// 151 | public bool? ShowTotalPages { get; set; } 152 | 153 | /// 154 | /// Show or hide total records count 155 | /// default: true 156 | /// 157 | public bool? ShowTotalRecords { get; set; } 158 | #endregion 159 | 160 | #region Texts 161 | /// 162 | /// The text to display at page size dropdown list label 163 | /// default: Page size 164 | /// 165 | public string TextPageSize { get; set; } 166 | 167 | 168 | /// 169 | /// Text to show on the "Go To First" Page button 170 | /// 171 | /// 172 | /// 173 | public string TextFirst { get; set; } 174 | 175 | /// 176 | /// Text to show on "Go to last page" button 177 | /// 178 | /// 179 | /// 180 | public string TextLast { get; set; } 181 | 182 | /// 183 | /// Next button text 184 | /// 185 | /// 186 | /// 187 | public string TextNext { get; set; } 188 | 189 | /// 190 | /// previous button text 191 | /// 192 | /// 193 | /// 194 | public string TextPrevious { get; set; } 195 | 196 | /// 197 | /// Display text for total pages label 198 | /// default: page 199 | /// 200 | public string TextTotalPages { get; set; } 201 | 202 | /// 203 | /// Display text for total records label 204 | /// default: records 205 | /// 206 | public string TextTotalRecords { get; set; } 207 | 208 | /// 209 | /// The number display format for page numbers. Use a list of numbers splitted by space e.g. "0 1 2 3 4 5 6 7 8 9" or use one from a pre-defined numbers formats in : 210 | /// 211 | /// 212 | public string NumberFormat { get; set; } 213 | #endregion 214 | 215 | #region Screen Reader 216 | /// 217 | /// Text for screen readers only 218 | /// 219 | public string SrTextFirst { get; set; } 220 | 221 | /// 222 | /// text for screen readers only 223 | /// 224 | public string SrTextLast { get; set; } 225 | 226 | /// 227 | /// text for screenreaders only 228 | /// 229 | public string SrTextNext { get; set; } 230 | 231 | /// 232 | /// text for screen readers only 233 | /// 234 | public string SrTextPrevious { get; set; } 235 | 236 | #endregion 237 | 238 | #region Styling 239 | 240 | /// 241 | /// Select bootstrap version 242 | /// 243 | public RenderMode RenderMode { get; set; } = RenderMode.Bootstrap; 244 | 245 | /// 246 | /// add custom class to content div 247 | /// 248 | public string Class { get; set; } 249 | 250 | /// 251 | /// css class for pagination div 252 | /// 253 | public string ClassPagingControlDiv { get; set; } 254 | 255 | /// 256 | /// css class for page count/record count div 257 | /// 258 | public string ClassInfoDiv { get; set; } 259 | 260 | /// 261 | /// styling class for page size div 262 | /// 263 | public string ClassPageSizeDiv { get; set; } 264 | 265 | /// 266 | /// pagination control class 267 | /// default: pagination 268 | /// 269 | public string ClassPagingControl { get; set; } 270 | 271 | /// 272 | /// class name for the active page 273 | /// default: active 274 | /// examples: disabled, active, ... 275 | /// 276 | public string ClassActivePage { get; set; } 277 | 278 | /// 279 | /// name of the class when jumping button is disabled. 280 | /// jumping buttons are prev-next and first-last buttons 281 | /// default: disabled 282 | /// example: disabled, d-hidden 283 | /// 284 | public string ClassDisabledJumpingButton { get; set; } 285 | 286 | /// 287 | /// css class for total records info 288 | /// default: badge badge-light 289 | /// 290 | public string ClassTotalRecords { get; set; } 291 | 292 | /// 293 | /// css class for total pages info 294 | /// default: badge badge-light 295 | /// 296 | public string ClassTotalPages { get; set; } 297 | 298 | /// 299 | /// css class for page link, use for styling bg and text colors 300 | /// 301 | public string ClassPageLink { get; set; } 302 | 303 | #endregion 304 | 305 | #region Ajax 306 | /// 307 | /// Set to true to use ajax pagination 308 | /// 309 | public bool Ajax { get; set; } = false; 310 | 311 | /// 312 | /// The message to display in a confirmation window before a request is submitted. 313 | /// 314 | public string AjaxConfirm { get; set; } 315 | 316 | /// 317 | /// The mode that specifies how to insert the response into the target DOM element. Valid values are before, after and replace. Default is replace 318 | /// 319 | public PagingAjaxMode AjaxMode { get; set; } = PagingAjaxMode.replace; 320 | 321 | /// 322 | /// A value, in milliseconds, that controls the duration of the animation when showing or hiding the loading element. 323 | /// 324 | public int AjaxLoadingDuration { get; set; } 325 | 326 | /// 327 | /// The id attribute of an HTML element that is displayed while the Ajax function is loading. Default is #loading-spinner 328 | /// 329 | public string AjaxLoading { get; set; } = "#loading-spinner"; 330 | 331 | /// 332 | /// The name of the JavaScript function to call immediately before the page is updated. 333 | /// 334 | public string AjaxBegin { get; set; } 335 | 336 | /// 337 | /// The JavaScript function to call when response data has been instantiated but before the page is updated. 338 | /// 339 | public string AjaxComplete { get; set; } 340 | 341 | /// 342 | /// The JavaScript function to call if the page update fails. 343 | /// 344 | public string AjaxFailure { get; set; } 345 | 346 | /// 347 | /// The JavaScript function to call after the page is successfully updated. 348 | /// 349 | public string AjaxSuccess { get; set; } 350 | 351 | /// 352 | /// The ID of the DOM element to update by using the response from the server. 353 | /// 354 | public string AjaxUpdate { get; set; } 355 | 356 | /// 357 | /// The URL to make the request to. 358 | /// 359 | public string AjaxUrl { get; set; } 360 | #endregion 361 | 362 | private int TotalPages => (int)Math.Ceiling(TotalRecords / (double)PageSize); 363 | 364 | private class Boundaries 365 | { 366 | public int Start { get; set; } 367 | public int End { get; set; } 368 | } 369 | 370 | /// 371 | /// process creating paging tag helper 372 | /// 373 | /// 374 | /// 375 | public override void Process(TagHelperContext context, TagHelperOutput output) 376 | { 377 | SetDefaults(); 378 | 379 | if (TotalPages > 0) 380 | { 381 | var pagingControl = new TagBuilder("ul"); 382 | pagingControl.AddCssClass($"{ClassPagingControl}"); 383 | 384 | // If ajax is anabled, create a dictionary of all ajax attributes 385 | if (Ajax) 386 | { 387 | // Add loader element 388 | output.PreElement.SetHtmlContent(""); 389 | 390 | AjaxAttributes = SetupAjaxAttributes(); 391 | } 392 | 393 | // show-hide first-last buttons on user options 394 | if (ShowFirstLast == true) 395 | { 396 | ShowFirstLast = true; 397 | } 398 | 399 | if(string.IsNullOrWhiteSpace(UrlTemplate)) 400 | UrlTemplate = CreatePagingUrlTemplate(); 401 | 402 | if (ShowFirstLast == true) 403 | { 404 | var first = CreatePagingLink(1, TextFirst, SrTextFirst, ClassDisabledJumpingButton); 405 | pagingControl.InnerHtml.AppendHtml(first); 406 | } 407 | 408 | if (ShowPrevNext == true) 409 | { 410 | var prevPage = PageNo - 1 <= 1 ? 1 : PageNo - 1; 411 | var prev = CreatePagingLink(prevPage, TextPrevious, SrTextPrevious, ClassDisabledJumpingButton); 412 | pagingControl.InnerHtml.AppendHtml(prev); 413 | } 414 | 415 | if (MaxDisplayedPages == 1) 416 | { 417 | var numTag = CreatePagingLink(PageNo, null, null, ClassActivePage); 418 | pagingControl.InnerHtml.AppendHtml(numTag); 419 | } 420 | else if (MaxDisplayedPages > 1) 421 | { 422 | // Boundaries are the start-end currently displayed pages 423 | var boundaries = CalculateBoundaries(PageNo, TotalPages, MaxDisplayedPages.Value); 424 | 425 | string gapStr = "
  •  ... 
  • "; 426 | 427 | if (ShowGap == true && boundaries.End > MaxDisplayedPages) 428 | { 429 | // add page no 1 430 | var num1Tag = CreatePagingLink(1, null, null, ClassActivePage); 431 | pagingControl.InnerHtml.AppendHtml(num1Tag); 432 | 433 | // Add gap after first page 434 | pagingControl.InnerHtml.AppendHtml(gapStr); 435 | } 436 | 437 | for (int i = boundaries.Start; i <= boundaries.End; i++) 438 | { 439 | var numTag = CreatePagingLink(i, null, null, ClassActivePage); 440 | pagingControl.InnerHtml.AppendHtml(numTag); 441 | } 442 | 443 | if (ShowGap == true && boundaries.End < TotalPages) 444 | { 445 | // Add gap before last page 446 | pagingControl.InnerHtml.AppendHtml(gapStr); 447 | 448 | // add last page 449 | var numLastTag = CreatePagingLink(TotalPages, null, null, ClassActivePage); 450 | pagingControl.InnerHtml.AppendHtml(numLastTag); 451 | } 452 | } 453 | 454 | if (ShowPrevNext == true) 455 | { 456 | var nextPage = PageNo + 1 > TotalPages ? TotalPages : PageNo + 1; 457 | var next = CreatePagingLink(nextPage, TextNext, SrTextNext, ClassDisabledJumpingButton); 458 | pagingControl.InnerHtml.AppendHtml(next); 459 | } 460 | 461 | if (ShowFirstLast == true) 462 | { 463 | var last = CreatePagingLink(TotalPages, TextLast, SrTextLast, ClassDisabledJumpingButton); 464 | pagingControl.InnerHtml.AppendHtml(last); 465 | } 466 | 467 | var pagingControlDiv = new TagBuilder("div"); 468 | pagingControlDiv.AddCssClass($"{ClassPagingControlDiv}"); 469 | pagingControlDiv.InnerHtml.AppendHtml(pagingControl); 470 | 471 | output.TagName = "div"; 472 | output.Attributes.SetAttribute("class", $"{Class}"); 473 | output.Content.AppendHtml(pagingControlDiv); 474 | 475 | if (ShowPageSizeNav == true) 476 | { 477 | var psDropdown = CreatePageSizeControl(); 478 | 479 | var psDiv = new TagBuilder("div"); 480 | psDiv.AddCssClass($"{ClassPageSizeDiv}"); 481 | psDiv.InnerHtml.AppendHtml(psDropdown); 482 | 483 | output.Content.AppendHtml(psDiv); 484 | } 485 | 486 | if (ShowTotalPages == true || ShowTotalRecords == true) 487 | { 488 | var infoDiv = AddDisplayInfo(); 489 | 490 | output.Content.AppendHtml(infoDiv); 491 | } 492 | 493 | } 494 | } 495 | 496 | private AttributeDictionary SetupAjaxAttributes() 497 | { 498 | if (string.IsNullOrWhiteSpace(AjaxUpdate)) 499 | throw new ArgumentNullException(nameof(AjaxUpdate)); 500 | 501 | if (string.IsNullOrWhiteSpace(AjaxUrl)) 502 | throw new ArgumentNullException(nameof(AjaxUrl)); 503 | 504 | var ajaxAttributes = new AttributeDictionary 505 | { 506 | { "data-ajax", "true" }, 507 | { "data-ajax-mode", $"{AjaxMode}" }, 508 | { "data-ajax-update", AjaxUpdate } 509 | }; 510 | 511 | if (!string.IsNullOrWhiteSpace(AjaxBegin)) 512 | ajaxAttributes.Add("data-ajax-begin", AjaxBegin); 513 | 514 | if (!string.IsNullOrWhiteSpace(AjaxComplete)) 515 | ajaxAttributes.Add("data-ajax-complete", AjaxComplete); 516 | 517 | if (!string.IsNullOrWhiteSpace(AjaxConfirm)) 518 | ajaxAttributes.Add("data-ajax-confirm", AjaxConfirm); 519 | 520 | if (!string.IsNullOrWhiteSpace(AjaxFailure)) 521 | ajaxAttributes.Add("data-ajax-failure", AjaxFailure); 522 | 523 | if (!string.IsNullOrWhiteSpace(AjaxLoading)) 524 | ajaxAttributes.Add("data-ajax-loading", AjaxLoading); 525 | 526 | if (AjaxLoadingDuration > 0) 527 | ajaxAttributes.Add("data-ajax-loading-duration", $"{AjaxLoadingDuration}"); 528 | 529 | if (!string.IsNullOrWhiteSpace(AjaxSuccess)) 530 | ajaxAttributes.Add("data-ajax-success", AjaxSuccess); 531 | 532 | return ajaxAttributes; 533 | } 534 | 535 | /// 536 | /// This method will assign the values by checking three places 537 | /// 1- Property value if set from HTML code 538 | /// 2- Default values in appSettings.json 539 | /// 3- Hard coded default value in code 540 | /// 541 | private void SetDefaults() 542 | { 543 | var _settingsJson = SettingsJson ?? "default"; 544 | 545 | _logger.LogInformation($"----> PagingTagHelper SettingsJson: {SettingsJson} - {_settingsJson}"); 546 | 547 | MaxDisplayedPages = MaxDisplayedPages == null ? int.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:max-displayed-pages"], out int _dp) ? _dp : 10 : MaxDisplayedPages; 548 | 549 | PageSizeDropdownItems = PageSizeDropdownItems ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:page-size-dropdown-items"] ?? "10-25-50"; 550 | 551 | QueryStringKeyPageNo = QueryStringKeyPageNo ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:query-string-key-page-no"] ?? "p"; 552 | 553 | QueryStringKeyPageSize = QueryStringKeyPageSize ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:query-string-key-page-size"] ?? "s"; 554 | 555 | ShowGap = ShowGap == null ? 556 | bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:show-gap"], out bool _sg) ? _sg : true : ShowGap; 557 | 558 | ShowFirstLast = ShowFirstLast == null ? 559 | bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:show-first-last"], out bool _sfl) ? _sfl : true : ShowFirstLast; 560 | 561 | ShowPrevNext = ShowPrevNext == null ? bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:show-prev-next"], out bool _sprn) ? _sprn : true : ShowPrevNext; 562 | 563 | ShowPageSizeNav = ShowPageSizeNav == null ? bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:show-page-size-nav"], out bool _spsn) ? _spsn : true : ShowPageSizeNav; 564 | 565 | ShowTotalPages = ShowTotalPages == null ? bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:show-total-pages"], out bool _stp) ? _stp : true : ShowTotalPages; 566 | 567 | ShowTotalRecords = ShowTotalRecords == null ? bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:show-total-records"], out bool _str) ? _str : true : ShowTotalRecords; 568 | 569 | NumberFormat = NumberFormat ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:number-format"] ?? NumberFormats.Default; 570 | 571 | TextPageSize = TextPageSize ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-page-size"]; 572 | 573 | TextFirst = TextFirst ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-first"] ?? "«"; 574 | 575 | TextLast = TextLast ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-last"] ?? "»"; 576 | 577 | TextPrevious = TextPrevious ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-previous"] ?? "‹"; 578 | 579 | TextNext = TextNext ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-next"] ?? "›"; 580 | 581 | TextTotalPages = TextTotalPages ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-total-pages"] ?? "pages"; 582 | 583 | TextTotalRecords = TextTotalRecords ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:text-total-records"] ?? "records"; 584 | 585 | SrTextFirst = SrTextFirst ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:sr-text-first"] ?? "First"; 586 | 587 | SrTextLast = SrTextLast ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:sr-text-last"] ?? "Last"; 588 | 589 | SrTextPrevious = SrTextPrevious ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:sr-text-previous"] ?? "Previous"; 590 | 591 | SrTextNext = SrTextNext ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:sr-text-next"] ?? "Next"; 592 | 593 | Class = Class ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class"] ?? "row"; 594 | 595 | ClassActivePage = ClassActivePage ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-active-page"] ?? "active"; 596 | 597 | ClassDisabledJumpingButton = ClassDisabledJumpingButton ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-disabled-jumping-button"] ?? "disabled"; 598 | 599 | ClassInfoDiv = ClassInfoDiv ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-info-div"] ?? "col-2"; 600 | 601 | ClassPageSizeDiv = ClassPageSizeDiv ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-page-size-div"] ?? "col-1"; 602 | 603 | ClassPagingControlDiv = ClassPagingControlDiv ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-paging-control-div"] ?? "col"; 604 | 605 | ClassPagingControl = ClassPagingControl ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-paging-control"] ?? "pagination"; 606 | 607 | ClassTotalPages = ClassTotalPages ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-total-pages"] ?? (RenderMode == RenderMode.Bootstrap ? "badge badge-light" : "badge bg-light text-dark"); 608 | 609 | ClassTotalRecords = ClassTotalRecords ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-total-records"] ?? (RenderMode == RenderMode.Bootstrap ? "badge badge-dark" : "badge bg-dark"); 610 | 611 | ClassPageLink = ClassPageLink ?? Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:class-page-link"] ?? ""; 612 | 613 | FixUrlPath = FixUrlPath == null ? 614 | bool.TryParse(Configuration[$"lazziya:pagingTagHelper:{_settingsJson}:fix-url-path"], out bool _fPath) ? _fPath : true : FixUrlPath; 615 | 616 | _logger.LogInformation($"----> PagingTagHelper - " + 617 | $"{nameof(PageNo)}: {PageNo}, " + 618 | $"{nameof(PageSize)}: {PageSize}, " + 619 | $"{nameof(TotalRecords)}: {TotalRecords}, " + 620 | $"{nameof(TotalPages)}: {TotalPages}, " + 621 | $"{nameof(QueryStringKeyPageNo)}: {QueryStringKeyPageNo}, " + 622 | $"{nameof(QueryStringKeyPageSize)}: {QueryStringKeyPageSize}, " + 623 | $""); 624 | } 625 | 626 | private TagBuilder AddDisplayInfo() 627 | { 628 | var infoDiv = new TagBuilder("div"); 629 | infoDiv.AddCssClass($"{ClassInfoDiv}"); 630 | 631 | var txt = string.Empty; 632 | if (ShowTotalPages == true) 633 | { 634 | infoDiv.InnerHtml.AppendHtml($"{TotalPages.ToNumberFormat(NumberFormat)} {TextTotalPages}"); 635 | } 636 | 637 | if (ShowTotalRecords == true) 638 | { 639 | infoDiv.InnerHtml.AppendHtml($"{TotalRecords.ToNumberFormat(NumberFormat)} {TextTotalRecords}"); 640 | } 641 | 642 | return infoDiv; 643 | } 644 | 645 | /// 646 | /// Calculate the boundaries of the currently rendered page numbers 647 | /// 648 | /// 649 | /// 650 | /// 651 | /// 652 | private Boundaries CalculateBoundaries(int currentPageNo, int totalPages, int maxDisplayedPages) 653 | { 654 | int _start, _end; 655 | 656 | int _gap = (int)Math.Ceiling(maxDisplayedPages / 2.0); 657 | 658 | if (maxDisplayedPages > totalPages) 659 | maxDisplayedPages = totalPages; 660 | 661 | if (totalPages == 1) 662 | { 663 | _start = _end = 1; 664 | } 665 | // << < 1 2 (3) 4 5 6 7 8 9 10 > >> 666 | else if (currentPageNo < maxDisplayedPages) 667 | { 668 | _start = 1; 669 | _end = maxDisplayedPages; 670 | } 671 | // << < 91 92 93 94 95 96 97 (98) 99 100 > >> 672 | else if (currentPageNo + maxDisplayedPages == totalPages) 673 | { 674 | _start = totalPages - maxDisplayedPages > 0 ? totalPages - maxDisplayedPages - 1 : 1; 675 | _end = totalPages - 2; 676 | } 677 | // << < 91 92 93 94 95 96 97 (98) 99 100 > >> 678 | else if (currentPageNo + maxDisplayedPages == totalPages + 1) 679 | { 680 | _start = totalPages - maxDisplayedPages > 0 ? totalPages - maxDisplayedPages : 1; 681 | _end = totalPages - 1; 682 | } 683 | // << < 91 92 93 94 95 96 97 (98) 99 100 > >> 684 | else if (currentPageNo + maxDisplayedPages > totalPages + 1) 685 | { 686 | _start = totalPages - maxDisplayedPages > 0 ? totalPages - maxDisplayedPages + 1 : 1; 687 | _end = totalPages; 688 | } 689 | 690 | // << < 21 22 23 34 (25) 26 27 28 29 30 > >> 691 | else 692 | { 693 | _start = currentPageNo - _gap > 0 ? currentPageNo - _gap + 1 : 1; 694 | _end = _start + maxDisplayedPages - 1; 695 | } 696 | 697 | return new Boundaries { Start = _start, End = _end }; 698 | } 699 | 700 | private TagBuilder CreatePagingLink(int targetPageNo, string text, string textSr, string pClass) 701 | { 702 | var liTag = new TagBuilder("li"); 703 | liTag.AddCssClass("page-item"); 704 | 705 | var pageUrl = CreatePagingUrl(targetPageNo, PageSize); 706 | 707 | var aTag = new TagBuilder("a"); 708 | aTag.AddCssClass($"page-link {ClassPageLink}"); 709 | aTag.Attributes.Add("href", pageUrl); 710 | 711 | // If no text provided for screen readers 712 | // use the actual page number 713 | if (string.IsNullOrWhiteSpace(textSr)) 714 | { 715 | var pageNoText = targetPageNo.ToNumberFormat(NumberFormat); 716 | 717 | aTag.InnerHtml.Append($"{pageNoText}"); 718 | } 719 | else 720 | { 721 | liTag.MergeAttribute("area-label", textSr); 722 | aTag.InnerHtml.AppendHtml($"{text}"); 723 | 724 | if (RenderMode == RenderMode.Bootstrap5) 725 | aTag.InnerHtml.AppendHtml($"{textSr}"); 726 | else 727 | aTag.InnerHtml.AppendHtml($"{textSr}"); 728 | } 729 | 730 | if (PageNo == targetPageNo) 731 | { 732 | liTag.AddCssClass($"{pClass}"); 733 | aTag.Attributes.Add("tabindex", "-1"); 734 | aTag.Attributes.Remove("class"); 735 | aTag.AddCssClass($"page-link {pClass}"); 736 | aTag.Attributes.Remove("href"); 737 | } 738 | 739 | if (Ajax) 740 | { 741 | var ajaxUrl = pageUrl.Replace("?", $"{AjaxUrl}&"); 742 | aTag.Attributes.Add("data-ajax-url", ajaxUrl); 743 | 744 | foreach (var att in AjaxAttributes) 745 | { 746 | aTag.Attributes.Add(att.Key, att.Value); 747 | } 748 | } 749 | 750 | liTag.InnerHtml.AppendHtml(aTag); 751 | return liTag; 752 | } 753 | 754 | /// 755 | /// dropdown list for changing page size (items per page) 756 | /// 757 | /// 758 | private TagBuilder CreatePageSizeControl() 759 | { 760 | var dropDownDiv = new TagBuilder("div"); 761 | dropDownDiv.AddCssClass("dropdown"); 762 | 763 | var dropDownBtn = new TagBuilder("button"); 764 | dropDownBtn.AddCssClass("btn btn-light dropdown-toggle"); 765 | dropDownBtn.Attributes.Add("type", "button"); 766 | dropDownBtn.Attributes.Add("id", "pagingDropDownMenuBtn"); 767 | 768 | if (RenderMode == RenderMode.Bootstrap5) 769 | dropDownBtn.Attributes.Add("data-bs-toggle", "dropdown"); 770 | else 771 | dropDownBtn.Attributes.Add("data-toggle", "dropdown"); 772 | dropDownBtn.Attributes.Add("aria-haspopup", "true"); 773 | dropDownBtn.Attributes.Add("aria-expanded", "false"); 774 | 775 | var psText = string.IsNullOrWhiteSpace(TextPageSize) 776 | ? $"{PageSize.ToNumberFormat(NumberFormat)}" 777 | : string.Format(TextPageSize, $"{PageSize.ToNumberFormat(NumberFormat)}"); 778 | dropDownBtn.InnerHtml.Append(psText); 779 | 780 | var dropDownMenu = new TagBuilder("div"); 781 | dropDownMenu.AddCssClass("dropdown-menu dropdown-menu-right"); 782 | dropDownMenu.Attributes.Add("aria-labelledby", "pagingDropDownMenuBtn"); 783 | 784 | var pageSizeDropdownItems = PageSizeDropdownItems.Split("-", StringSplitOptions.RemoveEmptyEntries); 785 | 786 | for (int i = 0; i < pageSizeDropdownItems.Length; i++) 787 | { 788 | var n = int.Parse(pageSizeDropdownItems[i]); 789 | 790 | var pageUrl = $"{CreatePagingUrl(1, n)}"; 791 | 792 | var option = new TagBuilder("a"); 793 | option.AddCssClass("dropdown-item"); 794 | option.Attributes.Add("href", pageUrl); 795 | 796 | option.InnerHtml.Append($"{n.ToNumberFormat(NumberFormat)}"); 797 | 798 | if (n == PageSize) 799 | option.AddCssClass("active"); 800 | 801 | if (Ajax) 802 | { 803 | var ajaxUrl = pageUrl.Replace("?", $"{AjaxUrl}&"); 804 | option.Attributes.Add("data-ajax-url", ajaxUrl); 805 | 806 | foreach (var att in AjaxAttributes) 807 | { 808 | option.Attributes.Add(att.Key, att.Value); 809 | } 810 | } 811 | 812 | dropDownMenu.InnerHtml.AppendHtml(option); 813 | } 814 | 815 | dropDownDiv.InnerHtml.AppendHtml(dropDownBtn); 816 | dropDownDiv.InnerHtml.AppendHtml(dropDownMenu); 817 | 818 | return dropDownDiv; 819 | } 820 | 821 | /// 822 | /// edit the url for each page, so it navigates to its target page number 823 | /// 824 | /// 825 | /// 826 | /// 827 | private string CreatePagingUrl(int pageNo, int pageSize) 828 | { 829 | return string.Format(UrlTemplate, pageNo, pageSize); 830 | } 831 | 832 | 833 | /// 834 | /// edit the url for each page, so it navigates to its target page number 835 | /// 836 | /// a string with placeholders for page no and page size 837 | private string CreatePagingUrlTemplate() 838 | { 839 | var queryString = ViewContext.HttpContext.Request.QueryString.Value; 840 | 841 | if (!string.IsNullOrWhiteSpace(AjaxUrl)) 842 | queryString = queryString.Replace($"{AjaxUrl}&", ""); 843 | 844 | var urlTemplate = string.IsNullOrWhiteSpace(queryString) 845 | ? $"{QueryStringKeyPageNo}=1&{QueryStringKeyPageSize}={PageSize}".Split('&').ToList() 846 | : queryString.TrimStart('?').Split('&').ToList(); 847 | 848 | var qDic = new List> 849 | { 850 | new Tuple(QueryStringKeyPageNo, "{0}"), 851 | new Tuple(QueryStringKeyPageSize, "{1}") 852 | }; 853 | 854 | var excludedKeys = new List { "X-Requested-With", "_", QueryStringKeyPageNo, QueryStringKeyPageSize }; 855 | foreach (var item in urlTemplate) 856 | { 857 | var key = item.Split('=')[0]; 858 | var value = item.Split('=')[1]; 859 | 860 | if (!excludedKeys.Contains(key)) 861 | { 862 | qDic.Add(new Tuple(key, value)); 863 | } 864 | } 865 | 866 | var path = ViewContext.HttpContext.Request.Path; 867 | 868 | return FixUrlPath ?? true 869 | ? path + "?" + string.Join("&", qDic.Select(q => q.Item1 + "=" + q.Item2)) 870 | : "?" + string.Join("&", qDic.Select(q => q.Item1 + "=" + q.Item2)); 871 | } 872 | } 873 | } 874 | -------------------------------------------------------------------------------- /LazZiya.TagHelpers/LazZiya.TagHelpers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LazZiya.TagHelpers 5 | 6 | 7 | 8 | 9 | Create primary alert 10 | Alert contents must be replaced between alert tags e.g. job done!]]> 11 | 12 | 13 | 14 | 15 | Create primary alert 16 | 17 | 18 | 19 | 20 | Create secondary alert 21 | Alert contents must be replaced between alert tags e.g. job done!]]> 22 | 23 | 24 | 25 | 26 | Create secondary alert 27 | 28 | 29 | 30 | 31 | Create success alert 32 | Alert contents must be replaced between alert tags e.g. job done!]]> 33 | 34 | 35 | 36 | 37 | Create success alert 38 | 39 | 40 | 41 | 42 | Create danger alert 43 | Alert contents must be replaced between alert tags e.g. job done!]]> 44 | 45 | 46 | 47 | 48 | Create danger alert 49 | 50 | 51 | 52 | 53 | Create warning alert 54 | Alert contents must be replaced between alert tags e.g. job done!]]> 55 | 56 | 57 | 58 | 59 | Create danger alert 60 | 61 | 62 | 63 | 64 | Create info alert 65 | Alert contents must be replaced between alert tags e.g. job done!]]> 66 | 67 | 68 | 69 | 70 | Create info alert 71 | 72 | 73 | 74 | 75 | Create light alert 76 | Alert contents must be replaced between alert tags e.g. job done!]]> 77 | 78 | 79 | 80 | 81 | Create light alert 82 | 83 | 84 | 85 | 86 | Create dark alert 87 | Alert contents must be replaced between alert tags e.g. job done!]]> 88 | 89 | 90 | 91 | 92 | Create dark alert 93 | 94 | 95 | 96 | 97 | Define alert style depending on Bootstrap4.x alert classes 98 | 99 | 100 | 101 | 102 | alert-primary 103 | 104 | 105 | 106 | 107 | alert-secondary 108 | 109 | 110 | 111 | 112 | alert-success 113 | 114 | 115 | 116 | 117 | alert-danger 118 | 119 | 120 | 121 | 122 | alert-warning 123 | 124 | 125 | 126 | 127 | alert-info 128 | 129 | 130 | 131 | 132 | alert-light 133 | 134 | 135 | 136 | 137 | alert-dark 138 | 139 | 140 | 141 | 142 | Choose where to get alert icons from 143 | 144 | 145 | 146 | 147 | Bootstrap 148 | 149 | 150 | 151 | 152 | Bootstrap5 153 | 154 | 155 | 156 | 157 | FontAwesome 158 | 159 | 160 | 161 | 162 | Alert item that can be created in the backend manually for pushing alert to the temp data 163 | 164 | 165 | 166 | 167 | Key to find alerts in TempData dictionary 168 | 169 | 170 | 171 | 172 | Alert style depending on Bootstrap 4.x classes 173 | 174 | 175 | 176 | 177 | Header text for the alert message 178 | 179 | 180 | 181 | 182 | Alert message body 183 | 184 | 185 | 186 | 187 | true for dismissable alert 188 | 189 | 190 | 191 | 192 | Extensions for TempData for creating coder friendly alerts easily 193 | 194 | 195 | 196 | 197 | Create primary alert 198 | 199 | TempData 200 | message body 201 | message header 202 | Show closing button 203 | 204 | 205 | 206 | Create secondary alert 207 | 208 | TempData 209 | message body 210 | message header 211 | Show closing button 212 | 213 | 214 | 215 | Create success alert 216 | 217 | TempData 218 | message body 219 | message header 220 | Show closing button 221 | 222 | 223 | 224 | Create danger alert 225 | 226 | TempData 227 | message body 228 | message header 229 | Show closing button 230 | 231 | 232 | 233 | Create warning alert 234 | 235 | TempData 236 | message body 237 | message header 238 | Show closing button 239 | 240 | 241 | 242 | Create info alert 243 | 244 | TempData 245 | message body 246 | message header 247 | Show closing button 248 | 249 | 250 | 251 | Create light alert 252 | 253 | TempData 254 | message body 255 | message header 256 | Show closing button 257 | 258 | 259 | 260 | Create dark alert 261 | 262 | TempData 263 | message body 264 | message header 265 | Show closing button 266 | 267 | 268 | 269 | Create alert messages styled with bootstrap 4.x 270 | Alert contents must be replaced between alert tags e.g. job done!]]> 271 | 272 | 273 | 274 | 275 | Header text for the alert 276 | 277 | 278 | 279 | 280 | Show closing button, default is true 281 | 282 | 283 | 284 | 285 | Show multiple alerts as slides 286 | 287 | 288 | 289 | 290 | Show alert icons from fontawesome. 291 | Requires fontawesome css or bootstrap 292 | 293 | 294 | 295 | 296 | Choose where to get icons source from. "Bootstrap" or "FontAwesome". 297 | 298 | 299 | 300 | 301 | Choose render mode: Bootstrap5 if your project is using bootstrap5, otherwise default is Bootstrap for earlier versions. 302 | 303 | 304 | 305 | 306 | Parse localizer instance to localize alert message 307 | 308 | 309 | 310 | 311 | ViewContext property is not required to be passed as parameter, it will be assigned automatically by the tag helper. 312 | View context is required to access TempData dictionary that contains the alerts coming from backend 313 | 314 | 315 | 316 | 317 | Create alert messages styled with bootstrap 4.x 318 | 319 | 320 | 321 | 322 | creates email link with mark if email is confirmed 323 | 324 | 325 | 326 | 327 | boolean value to indicate if the email is confirmed or not 328 | 329 | 330 | 331 | 332 | process in creating email tag helper 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | The label to display for language dropdown list on language names 341 | 342 | 343 | 344 | 345 | Culture name 346 | 347 | 348 | 349 | 350 | Culture display name 351 | 352 | 353 | 354 | 355 | Culture English name 356 | 357 | 358 | 359 | 360 | Culture native name 361 | 362 | 363 | 364 | 365 | Two letter ISO language name 366 | 367 | 368 | 369 | 370 | creates a language navigation menu, depends on supported cultures 371 | 372 | 373 | 374 | 375 | optional: manually specify list of supported cultures 376 | 377 | en-US,tr, ar 378 | 379 | 380 | 381 | 382 | 383 | optional: what to display as label for language dropdown 384 | default: LanguageLabel.EnglishName 385 | 386 | 387 | 388 | 389 | ViewContext property is not required to be passed as parameter, it will be auto assigned by the tag helpoer. 390 | current view context to access RouteData.Values and Request.Query collection 391 | 392 | 393 | 394 | 395 | Choose render mode: classis for regular dropdown list, Bootstrap4 for HTML5 div with Bootstrap4 style. 396 | 397 | 398 | 399 | 400 | Set the handler url for setting culture cookie on language change 401 | 402 | 403 | 404 | 405 | The url to redirect to on langugae change. 406 | The url must have at one place holder for culture value. 407 | e.g. /{0}/Home 408 | 409 | 410 | 411 | 412 | Show relevant country flag for specific culture. 413 | Flags will be shown only if the culture is country specific. 414 | e.g. "tr" will not render any flag, but "tr-sy" will render Turkish flag. 415 | Required reference to flag-icon-css 416 | 417 | 418 | 419 | 420 | true: Show flags in squared images, 421 | false: Show flags in rounded images, 422 | 423 | 424 | 425 | 426 | Whether show border or not 427 | 428 | 429 | 430 | 431 | required for listing supported cultures. 432 | The handler must contain two place holders for culture name and return url. 433 | e.g.: 434 | 435 | 436 | 437 | 438 | creates a language navigation menu, depends on supported cultures 439 | 440 | 441 | 442 | 443 | 444 | start creating the language navigation dropdown 445 | 446 | 447 | 448 | 449 | 450 | 451 | create classic list items list 452 | English]]> 453 | 454 | language name-URL dictionary 455 | reference to TagHelperOuput 456 | 457 | 458 | 459 | 460 | create a dropdown form control 461 | English]]> 462 | 463 | language name-URL dictionary 464 | reference to TagHelperOuput 465 | 466 | 467 | 468 | 469 | create classic list items list 470 | English]]> 471 | 472 | language name-URL dictionary 473 | reference to TagHelperOuput 474 | 475 | 476 | 477 | 478 | create classic list items list 479 | English]]> 480 | 481 | language name-URL dictionary 482 | reference to TagHelperOuput 483 | 484 | 485 | 486 | 487 | create dictonary for all supported cultures 488 | Key: Language display name for label 489 | Value: Navigation URL 490 | 491 | 492 | 493 | 494 | 495 | private list of supported CultureInfo, 496 | 497 | 498 | 499 | 500 | defines location to load localization valdiation scripts 501 | 502 | 503 | 504 | 505 | valdiation scripts are located under wwwroot/lib folder 506 | 507 | 508 | 509 | 510 | valdiation scripts will be loaded from jsdelivr 511 | 512 | 513 | 514 | 515 | Tag helper for client side localized validation scripts. 516 | 517 | 518 | 519 | 520 | (optional) define where to load scripts from, Local or JsDelivr. 521 | Default: JsDelivr 522 | 523 | 524 | 525 | 526 | (optional) set cldr version to load. 527 | Default: 35.1 528 | 529 | 530 | 531 | 532 | Tag helper for client side localized validation scripts. 533 | 534 | 535 | 536 | 537 | Tag helper component for client side localized validation scripts. 538 | 539 | 540 | 541 | 542 | inserts all localizaiton validation scripts into relevant tag 543 | 544 | 545 | 546 | 547 | 548 | default order is 0 549 | 550 | 551 | 552 | 553 | generate the taghelper 554 | 555 | 556 | 557 | 558 | 559 | 560 | find json files related to the current culture, if not found return parent culture, if not found return default culture. 561 | see ClientSideValidationScripts.html for how to configure paths 562 | 563 | culture name e.g. tr 564 | 565 | 566 | 567 | ajax update modes 568 | 569 | 570 | 571 | 572 | update before target content 573 | 574 | 575 | 576 | 577 | update after target content 578 | 579 | 580 | 581 | 582 | replace target content 583 | 584 | 585 | 586 | 587 | The http request method 588 | 589 | 590 | 591 | 592 | get request 593 | 594 | 595 | 596 | 597 | post request 598 | 599 | 600 | 601 | 602 | Creates a pagination control 603 | 604 | 605 | 606 | 607 | Dictonary object to hold all ajax attributes 608 | 609 | 610 | 611 | 612 | A URL template for paging buttons 613 | e.g. 614 | 615 | 616 | 617 | 618 | 619 | ViewContext property is not required to be passed as parameter, it will be assigned automatically by the tag helper. 620 | View context is required to access TempData dictionary that contains the alerts coming from backend 621 | 622 | 623 | 624 | 625 | Creates a pagination control 626 | 627 | 628 | 629 | 630 | current page number. 631 | default: 1 632 | example: p=1 633 | 634 | 635 | 636 | 637 | how many items to get from db per page per request 638 | default: 10 639 | example: pageSize=10 640 | 641 | 642 | 643 | 644 | Total count of records in the db 645 | default: 0 646 | 647 | 648 | 649 | 650 | if count of pages is too much, restrict shown pages count to specific number 651 | default: 10 652 | 653 | 654 | 655 | 656 | name of the settings section in appSettings.json 657 | default: "default" 658 | 659 | 660 | 661 | 662 | Force adding url path to the navigation url 663 | in some scenarios when the page is under some area/subFolder 664 | The navigation links are pointing to the home page. 665 | To force adding url path enable this property 666 | 667 | 668 | 669 | 670 | A list of dash delimitted numbers for page size dropdown. 671 | default: "10-25-50" 672 | 673 | 674 | 675 | 676 | Query string paramter name for current page. 677 | default: p 678 | exmaple: p=1 679 | 680 | 681 | 682 | 683 | Query string parameter name for page size 684 | default: s 685 | example: s=25 686 | 687 | 688 | 689 | 690 | Show drop down list for different page size options 691 | default: true 692 | options: true, false 693 | 694 | 695 | 696 | 697 | Show a three dots after first page or before last page 698 | when there is a gap in pages at the beginnig or end 699 | 700 | 701 | 702 | 703 | Show/hide First-Last buttons 704 | default: true, if set to false and total pages > max displayed pages it will be true 705 | 706 | 707 | 708 | 709 | Show/hide Previous-Next buttons 710 | default: true 711 | 712 | 713 | 714 | 715 | Show or hide total pages count 716 | default: true 717 | 718 | 719 | 720 | 721 | Show or hide total records count 722 | default: true 723 | 724 | 725 | 726 | 727 | The text to display at page size dropdown list label 728 | default: Page size 729 | 730 | 731 | 732 | 733 | Text to show on the "Go To First" Page button 734 | 735 | 736 | 737 | 738 | 739 | 740 | Text to show on "Go to last page" button 741 | 742 | 743 | 744 | 745 | 746 | 747 | Next button text 748 | 749 | 750 | 751 | 752 | 753 | 754 | previous button text 755 | 756 | 757 | 758 | 759 | 760 | 761 | Display text for total pages label 762 | default: page 763 | 764 | 765 | 766 | 767 | Display text for total records label 768 | default: records 769 | 770 | 771 | 772 | 773 | The number display format for page numbers. Use a list of numbers splitted by space e.g. "0 1 2 3 4 5 6 7 8 9" or use one from a pre-defined numbers formats in : 774 | 775 | 776 | 777 | 778 | 779 | Text for screen readers only 780 | 781 | 782 | 783 | 784 | text for screen readers only 785 | 786 | 787 | 788 | 789 | text for screenreaders only 790 | 791 | 792 | 793 | 794 | text for screen readers only 795 | 796 | 797 | 798 | 799 | Select bootstrap version 800 | 801 | 802 | 803 | 804 | add custom class to content div 805 | 806 | 807 | 808 | 809 | css class for pagination div 810 | 811 | 812 | 813 | 814 | css class for page count/record count div 815 | 816 | 817 | 818 | 819 | styling class for page size div 820 | 821 | 822 | 823 | 824 | pagination control class 825 | default: pagination 826 | 827 | 828 | 829 | 830 | class name for the active page 831 | default: active 832 | examples: disabled, active, ... 833 | 834 | 835 | 836 | 837 | name of the class when jumping button is disabled. 838 | jumping buttons are prev-next and first-last buttons 839 | default: disabled 840 | example: disabled, d-hidden 841 | 842 | 843 | 844 | 845 | css class for total records info 846 | default: badge badge-light 847 | 848 | 849 | 850 | 851 | css class for total pages info 852 | default: badge badge-light 853 | 854 | 855 | 856 | 857 | css class for page link, use for styling bg and text colors 858 | 859 | 860 | 861 | 862 | Set to true to use ajax pagination 863 | 864 | 865 | 866 | 867 | The message to display in a confirmation window before a request is submitted. 868 | 869 | 870 | 871 | 872 | The mode that specifies how to insert the response into the target DOM element. Valid values are before, after and replace. Default is replace 873 | 874 | 875 | 876 | 877 | A value, in milliseconds, that controls the duration of the animation when showing or hiding the loading element. 878 | 879 | 880 | 881 | 882 | The id attribute of an HTML element that is displayed while the Ajax function is loading. Default is #loading-spinner 883 | 884 | 885 | 886 | 887 | The name of the JavaScript function to call immediately before the page is updated. 888 | 889 | 890 | 891 | 892 | The JavaScript function to call when response data has been instantiated but before the page is updated. 893 | 894 | 895 | 896 | 897 | The JavaScript function to call if the page update fails. 898 | 899 | 900 | 901 | 902 | The JavaScript function to call after the page is successfully updated. 903 | 904 | 905 | 906 | 907 | The ID of the DOM element to update by using the response from the server. 908 | 909 | 910 | 911 | 912 | The URL to make the request to. 913 | 914 | 915 | 916 | 917 | process creating paging tag helper 918 | 919 | 920 | 921 | 922 | 923 | 924 | This method will assign the values by checking three places 925 | 1- Property value if set from HTML code 926 | 2- Default values in appSettings.json 927 | 3- Hard coded default value in code 928 | 929 | 930 | 931 | 932 | Calculate the boundaries of the currently rendered page numbers 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | dropdown list for changing page size (items per page) 942 | 943 | 944 | 945 | 946 | 947 | edit the url for each page, so it navigates to its target page number 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | edit the url for each page, so it navigates to its target page number 956 | 957 | a string with placeholders for page no and page size 958 | 959 | 960 | 961 | creates a display for phone number 962 | 963 | 964 | 965 | 966 | boolean value to indicate if the phone number has been confirmed 967 | 968 | 969 | 970 | 971 | process creating phone number tag helper 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | A strongly-typed resource class, for looking up localized strings, etc. 980 | 981 | 982 | 983 | 984 | Returns the cached ResourceManager instance used by this class. 985 | 986 | 987 | 988 | 989 | Overrides the current thread's CurrentUICulture property for all 990 | resource lookups using this strongly typed resource class. 991 | 992 | 993 | 994 | 995 | Looks up a localized string similar to <!-- cldr scripts (needed for globalize) --> 996 | <script src="https://cdn.jsdelivr.net/gh/rxaviers/cldrjs@0.5.1/dist/cldr.js"></script> 997 | <script src="https://cdn.jsdelivr.net/gh/rxaviers/cldrjs@0.5.1/dist/cldr/event.js"></script> 998 | <script src="https://cdn.jsdelivr.net/gh/rxaviers/cldrjs@0.5.1/dist/cldr/supplemental.js"></script> 999 | 1000 | <!-- globalize scripts --> 1001 | <script src="https://cdn.jsdelivr.net/gh/globalizejs/globalize@1.4.2/dist/globalize.js"></script> 1002 | <script src="https://cdn.jsdelivr.net/gh/globalizejs/g [rest of string was truncated]";. 1003 | 1004 | 1005 | 1006 | 1007 | Looks up a localized string similar to <!-- cldr scripts (needed for globalize) --> 1008 | <script src="/lib/cldr/dist/cldr.min.js"></script> 1009 | <script src="/lib/cldr/dist/cldr/event.min.js"></script> 1010 | <script src="/lib/cldr/dist/cldr/supplemental.min.js"></script> 1011 | 1012 | <!-- globalize scripts --> 1013 | <script src="/lib/globalize/dist/globalize.min.js"></script> 1014 | <script src="/lib/globalize/dist/globalize/number.min.js"></script> 1015 | <script src="/lib/globalize/dist/globalize/date.min.js"></script> 1016 | <script src="/lib/globalize/dist/globalize/currency.min.js"></s [rest of string was truncated]";. 1017 | 1018 | 1019 | 1020 | 1021 | choose render mode style, 1022 | classic: regular dropdown select list 1023 | Bootstrap4: HTML5 div with Bootstrap4 support 1024 | 1025 | 1026 | 1027 | 1028 | regular dropdown list 1029 | 1030 | 1031 | 1032 | 1033 | HTML5 div with Bootstrap 4 support 1034 | 1035 | 1036 | 1037 | 1038 | Render as form control 1039 | 1040 | 1041 | 1042 | 1043 | HTML5 div with Bootstrap 5 support 1044 | 1045 | 1046 | 1047 | 1048 | creates a dropdown list from custom enum with supports for localization 1049 | 1050 | 1051 | 1052 | 1053 | (int)MyEnum.ValueName 1054 | 1055 | 1056 | 1057 | 1058 | typeof(MyEnum) 1059 | 1060 | 1061 | 1062 | 1063 | A delegate function for getting locaized value. 1064 | 1065 | 1066 | 1067 | 1068 | Initialize a new instance of SelectEnum taghelper 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | start creating select-enum tag helper 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | Generic extension to TempData for adding complex object and fix serialization problem 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | Add object to temp data 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | Read object from temp data 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | Number formats for different cultures. 1106 | https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/numberingSystems.json 1107 | 1108 | 1109 | 1110 | 1111 | Receives a number in system format, and converts it to any other format. 1112 | See 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | System default numbering format 1121 | 1122 | 1123 | 1124 | 1125 | 0123456789 1126 | 1127 | 1128 | 1129 | 1130 | Use hexadecimal numbering system 1131 | 1132 | 1133 | 1134 | 1135 | I II III IV V VI 1136 | 1137 | 1138 | 1139 | 1140 | ٠١٢٣٤٥٦٧٨٩ 1141 | 1142 | 1143 | 1144 | 1145 | 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯 1146 | 1147 | 1148 | 1149 | 1150 | ০১২৩৪৫৬৭৮৯ 1151 | 1152 | 1153 | 1154 | 1155 | ०१२३४५६७८९ 1156 | 1157 | 1158 | 1159 | 1160 | ۰۱۲۳۴۵۶۷۸۹ 1161 | 1162 | 1163 | 1164 | 1165 | 0123456789 1166 | 1167 | 1168 | 1169 | 1170 | ೦೧೨೩೪೫೬೭೮೯ 1171 | 1172 | 1173 | 1174 | 1175 | ૦૧૨૩૪૫૬૭૮૯ 1176 | 1177 | 1178 | 1179 | 1180 | ੦੧੨੩੪੫੬੭੮੯ 1181 | 1182 | 1183 | 1184 | 1185 | 〇一二三四五六七八九 1186 | 1187 | 1188 | 1189 | 1190 | ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙ 1191 | 1192 | 1193 | 1194 | 1195 | ០១២៣៤៥៦៧៨៩ 1196 | 1197 | 1198 | 1199 | 1200 | ໐໑໒໓໔໕໖໗໘໙ 1201 | 1202 | 1203 | 1204 | 1205 | 0123456789 1206 | 1207 | 1208 | 1209 | 1210 | 𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗 1211 | 1212 | 1213 | 1214 | 1215 | 𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡 1216 | 1217 | 1218 | 1219 | 1220 | 𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿 1221 | 1222 | 1223 | 1224 | 1225 | 𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵 1226 | 1227 | 1228 | 1229 | 1230 | 𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫 1231 | 1232 | 1233 | 1234 | 1235 | ൦൧൨൩൪൫൬൭൮൯ 1236 | 1237 | 1238 | 1239 | 1240 | ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ 1241 | 1242 | 1243 | 1244 | 1245 | ၀၁၂၃၄၅၆၇၈၉ 1246 | 1247 | 1248 | 1249 | 1250 | ႐႑႒႓႔႕႖႗႘႙ 1251 | 1252 | 1253 | 1254 | 1255 | ꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹ 1256 | 1257 | 1258 | 1259 | 1260 | ߀߁߂߃߄߅߆߇߈߉ 1261 | 1262 | 1263 | 1264 | 1265 | ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ 1266 | 1267 | 1268 | 1269 | 1270 | ୦୧୨୩୪୫୬୭୮୯ 1271 | 1272 | 1273 | 1274 | 1275 | 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩 1276 | 1277 | 1278 | 1279 | 1280 | ෦෧෨෩෪෫෬෭෮෯ 1281 | 1282 | 1283 | 1284 | 1285 | ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙ 1286 | 1287 | 1288 | 1289 | 1290 | ௦௧௨௩௪௫௬௭௮௯ 1291 | 1292 | 1293 | 1294 | 1295 | ౦౧౨౩౪౫౬౭౮౯ 1296 | 1297 | 1298 | 1299 | 1300 | ๐๑๒๓๔๕๖๗๘๙ 1301 | 1302 | 1303 | 1304 | 1305 | ༠༡༢༣༤༥༦༧༨༩ 1306 | 1307 | 1308 | 1309 | 1310 | ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ 1311 | 1312 | 1313 | 1314 | 1315 | Convert decimals to roman numerals 1316 | 1317 | 1318 | 1319 | 1320 | Dictionary of roman numbers and their equavilant decimals 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | Convert decimal number to roman number 1327 | 1328 | unsigned number 1329 | Roman number 1330 | 1331 | 1332 | 1333 | --------------------------------------------------------------------------------