├── .gitattributes ├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENCE ├── README.md └── Source ├── .editorconfig ├── Lib ├── Directory.Build.props ├── Morris.Blazor.FluentValidation │ ├── FluentValidationPropertiesExtensions.cs │ ├── FluentValidationRepository.cs │ ├── FluentValidationValidatorProvider.cs │ ├── Morris.Blazor.FluentValidation.csproj │ ├── ValidationConfigurationFluentValidationExtensions.cs │ └── blazor-fluentvalidation.png └── Morris.Blazor.Validation │ ├── DataAnnotationsValidatorProvider.cs │ ├── Extensions │ └── EditContextExtensions.cs │ ├── IValidationProvider.cs │ ├── IValidationProviderRepository.cs │ ├── Morris.Blazor.Validation.csproj │ ├── Validate.cs │ ├── ValidationConfiguration.cs │ ├── ValidationProperties.cs │ ├── ValidationProviderRepository.cs │ ├── ValidationServiceCollectionExtensions.cs │ └── blazor-validation.png ├── Morris.Blazor.Validation.sln └── Samples ├── 01-DataAnnotationsValidation └── DataAnnotationsValidationSample │ ├── App.razor │ ├── Components │ └── AddressEdit.razor │ ├── DataAnnotationsValidationSample.csproj │ ├── Models │ ├── Address.cs │ ├── NamedAddress.cs │ └── Person.cs │ ├── Pages │ ├── Index.razor │ └── _Imports.razor │ ├── Program.cs │ ├── Shared │ ├── MainLayout.razor │ └── NavMenu.razor │ ├── _Imports.razor │ └── wwwroot │ ├── css │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── open-iconic │ │ ├── FONT-LICENSE │ │ ├── ICON-LICENSE │ │ ├── README.md │ │ └── font │ │ │ ├── css │ │ │ └── open-iconic-bootstrap.min.css │ │ │ └── fonts │ │ │ ├── open-iconic.eot │ │ │ ├── open-iconic.otf │ │ │ ├── open-iconic.svg │ │ │ ├── open-iconic.ttf │ │ │ └── open-iconic.woff │ └── site.css │ └── index.html └── 02-FluentValidation └── FluentValidationSample ├── App.razor ├── Components └── AddressEdit.razor ├── FluentValidationSample.csproj ├── FluentValidators ├── AddressValidator.cs ├── NamedAddressValidator.cs └── PersonValidator.cs ├── Models ├── Address.cs ├── NamedAddress.cs └── Person.cs ├── Pages ├── Error.cshtml ├── Index.razor └── _Host.cshtml ├── Program.cs ├── Shared ├── MainLayout.razor └── NavMenu.razor ├── Startup.cs ├── _Imports.razor ├── appsettings.Development.json ├── appsettings.json └── wwwroot ├── css ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map ├── open-iconic │ ├── FONT-LICENSE │ ├── ICON-LICENSE │ ├── README.md │ └── font │ │ ├── css │ │ └── open-iconic-bootstrap.min.css │ │ └── fonts │ │ ├── open-iconic.eot │ │ ├── open-iconic.otf │ │ ├── open-iconic.svg │ │ ├── open-iconic.ttf │ │ └── open-iconic.woff └── site.css └── favicon.ico /.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 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | release: 4 | types: [created] 5 | 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | 11 | - name: Setup .NET 12 | uses: actions/setup-dotnet@v4 13 | with: 14 | dotnet-version: '9.x' 15 | 16 | - name: Check out repository code 17 | uses: actions/checkout@v4 18 | - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." 19 | 20 | - name: Build 21 | run: dotnet build "./Source/" --configuration Release -p:Version=${{ github.event.release.tag_name }} -p:AssemblyVersion=${{ github.event.release.tag_name }} -p:FileVersion=${{ github.event.release.tag_name }} 22 | 23 | - name: Pack 24 | run: dotnet pack "./Source/" --configuration Release --no-build -p:Version=${{ github.event.release.tag_name }} -p:AssemblyVersion=${{ github.event.release.tag_name }} -p:FileVersion=${{ github.event.release.tag_name }} -o ./artifacts 25 | 26 | - name: Push to NuGet 27 | run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bat 2 | *.snk 3 | secrets.txt 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015/2017 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # Visual Studio 2017 auto generated files 37 | Generated\ Files/ 38 | 39 | # MSTest test Results 40 | [Tt]est[Rr]esult*/ 41 | [Bb]uild[Ll]og.* 42 | 43 | # NUNIT 44 | *.VisualState.xml 45 | TestResult.xml 46 | 47 | # Build Results of an ATL Project 48 | [Dd]ebugPS/ 49 | [Rr]eleasePS/ 50 | dlldata.c 51 | 52 | # Benchmark Results 53 | BenchmarkDotNet.Artifacts/ 54 | 55 | # .NET Core 56 | project.lock.json 57 | project.fragment.lock.json 58 | artifacts/ 59 | **/Properties/launchSettings.json 60 | 61 | # StyleCop 62 | StyleCopReport.xml 63 | 64 | # Files built by Visual Studio 65 | *_i.c 66 | *_p.c 67 | *_i.h 68 | *.ilk 69 | *.meta 70 | *.obj 71 | *.iobj 72 | *.pch 73 | *.pdb 74 | *.ipdb 75 | *.pgc 76 | *.pgd 77 | *.rsp 78 | *.sbr 79 | *.tlb 80 | *.tli 81 | *.tlh 82 | *.tmp 83 | *.tmp_proj 84 | *.log 85 | *.vspscc 86 | *.vssscc 87 | .builds 88 | *.pidb 89 | *.svclog 90 | *.scc 91 | 92 | # Chutzpah Test files 93 | _Chutzpah* 94 | 95 | # Visual C++ cache files 96 | ipch/ 97 | *.aps 98 | *.ncb 99 | *.opendb 100 | *.opensdf 101 | *.sdf 102 | *.cachefile 103 | *.VC.db 104 | *.VC.VC.opendb 105 | 106 | # Visual Studio profiler 107 | *.psess 108 | *.vsp 109 | *.vspx 110 | *.sap 111 | 112 | # Visual Studio Trace Files 113 | *.e2e 114 | 115 | # TFS 2012 Local Workspace 116 | $tf/ 117 | 118 | # Guidance Automation Toolkit 119 | *.gpState 120 | 121 | # ReSharper is a .NET coding add-in 122 | _ReSharper*/ 123 | *.[Rr]e[Ss]harper 124 | *.DotSettings.user 125 | 126 | # JustCode is a .NET coding add-in 127 | .JustCode 128 | 129 | # TeamCity is a build add-in 130 | _TeamCity* 131 | 132 | # DotCover is a Code Coverage Tool 133 | *.dotCover 134 | 135 | # AxoCover is a Code Coverage Tool 136 | .axoCover/* 137 | !.axoCover/settings.json 138 | 139 | # Visual Studio code coverage results 140 | *.coverage 141 | *.coveragexml 142 | 143 | # NCrunch 144 | _NCrunch_* 145 | .*crunch*.local.xml 146 | nCrunchTemp_* 147 | 148 | # MightyMoose 149 | *.mm.* 150 | AutoTest.Net/ 151 | 152 | # Web workbench (sass) 153 | .sass-cache/ 154 | 155 | # Installshield output folder 156 | [Ee]xpress/ 157 | 158 | # DocProject is a documentation generator add-in 159 | DocProject/buildhelp/ 160 | DocProject/Help/*.HxT 161 | DocProject/Help/*.HxC 162 | DocProject/Help/*.hhc 163 | DocProject/Help/*.hhk 164 | DocProject/Help/*.hhp 165 | DocProject/Help/Html2 166 | DocProject/Help/html 167 | 168 | # Click-Once directory 169 | publish/ 170 | 171 | # Publish Web Output 172 | *.[Pp]ublish.xml 173 | *.azurePubxml 174 | # Note: Comment the next line if you want to checkin your web deploy settings, 175 | # but database connection strings (with potential passwords) will be unencrypted 176 | *.pubxml 177 | *.publishproj 178 | 179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 180 | # checkin your Azure Web App publish settings, but sensitive information contained 181 | # in these scripts will be unencrypted 182 | PublishScripts/ 183 | 184 | # NuGet Packages 185 | *.nupkg 186 | # The packages folder can be ignored because of Package Restore 187 | **/[Pp]ackages/* 188 | # except build/, which is used as an MSBuild target. 189 | !**/[Pp]ackages/build/ 190 | # Uncomment if necessary however generally it will be regenerated when needed 191 | #!**/[Pp]ackages/repositories.config 192 | # NuGet v3's project.json files produces more ignorable files 193 | *.nuget.props 194 | *.nuget.targets 195 | 196 | # Microsoft Azure Build Output 197 | csx/ 198 | *.build.csdef 199 | 200 | # Microsoft Azure Emulator 201 | ecf/ 202 | rcf/ 203 | 204 | # Windows Store app package directories and files 205 | AppPackages/ 206 | BundleArtifacts/ 207 | Package.StoreAssociation.xml 208 | _pkginfo.txt 209 | *.appx 210 | 211 | # Visual Studio cache files 212 | # files ending in .cache can be ignored 213 | *.[Cc]ache 214 | # but keep track of directories ending in .cache 215 | !*.[Cc]ache/ 216 | 217 | # Others 218 | ClientBin/ 219 | ~$* 220 | *~ 221 | *.dbmdl 222 | *.dbproj.schemaview 223 | *.jfm 224 | *.pfx 225 | *.publishsettings 226 | orleans.codegen.cs 227 | 228 | # Including strong name files can present a security risk 229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 230 | #*.snk 231 | 232 | # Since there are multiple workflows, uncomment next line to ignore bower_components 233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 234 | #bower_components/ 235 | 236 | # RIA/Silverlight projects 237 | Generated_Code/ 238 | 239 | # Backup & report files from converting an old project file 240 | # to a newer Visual Studio version. Backup files are not needed, 241 | # because we have git ;-) 242 | _UpgradeReport_Files/ 243 | Backup*/ 244 | UpgradeLog*.XML 245 | UpgradeLog*.htm 246 | ServiceFabricBackup/ 247 | *.rptproj.bak 248 | 249 | # SQL Server files 250 | *.mdf 251 | *.ldf 252 | *.ndf 253 | 254 | # Business Intelligence projects 255 | *.rdl.data 256 | *.bim.layout 257 | *.bim_*.settings 258 | *.rptproj.rsuser 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush 299 | .cr/ 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Peter Morris 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blazor-Validation 2 | 3 | 4 | Blazor-Validation is a validation agnostic library for validating forms in Blazor- [Microsoft aspdotnet Blazor project]. 5 | 6 | [![Join the chat at https://gitter.im/mrpmorris/blazor-validation](https://badges.gitter.im/mrpmorris/blazor-validation.svg)](https://gitter.im/mrpmorris/blazor-validation?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 7 | 8 | ## Installation 9 | You can download the latest release / pre-release NuGet packages from the official NuGet pages: 10 | - [Blazor-Validation] [![NuGet version (Morris.Blazor.Validation)](https://img.shields.io/nuget/v/Morris.Blazor.Validation.svg?style=flat-square)](https://www.nuget.org/packages/Morris.Blazor.Validation/) 11 | - [Blazor-FluentValidation] [![NuGet version (Morris.Blazor.FluentValidation)](https://img.shields.io/nuget/v/Morris.Blazor.FluentValidation.svg?style=flat-square)](https://www.nuget.org/packages/Morris.Blazor.FluentValidation/) 12 | 13 | ## Getting started 14 | 1. Add a reference to Morris.Blazor.Validation 15 | 2. Inside the `` in your razor files, add `` 16 | 3. In startup.cs add `using Morris.Blazor.Validation` and then add the relevant validation in the `ConfigureServices` method. 17 | 18 | - `services.AddFormValidation(config => config.AddDataAnnotationsValidation());` 19 | - `services.AddFormValidation(config => config.AddFluentValidation(typeof(SomeValidator).Assembly));` 20 | 21 | It is possible to add as many validation providers as you wish 22 | ```c# 23 | services.AddFormValidation(config => 24 | config 25 | .AddDataAnnotationsValidation() 26 | .AddFluentValidation(typeof(SomeValidator).Assembly) 27 | ); 28 | ``` 29 | 30 | Also you can have the `FluentValidation` extension scan multiple assemblies 31 | 32 | ```c# 33 | services.AddFormValidation(config => 34 | config 35 | .AddFluentValidation( 36 | typeof(SomeValidator).Assembly, 37 | typeof(ClassInAnotherDll).Assembly, 38 | andAnotherAssembly, 39 | andYetAnotherAssembly)); 40 | ``` 41 | 42 | The standard Blazor components `` and `` will now work with your selected validation options. 43 | 44 | ### Sample projects 45 | More sample projects will be added as the framework develops. 46 | - [Data Annotations Sample]- Shows how to use DataAnnotations to validate. 47 | - [FluentValidation Sample]- Shows how to use the [FluentValidation.com] library to validate. 48 | 49 | ## What's new 50 | 51 | ### New in 3.1.1 52 | - Support for .net 8 and 9 only 53 | 54 | ### New in 3.1.0 55 | - Support for .Net 8 56 | 57 | ### New in 3.0.0 58 | - Add OnTransformModel delegate to allow the model to be transformed before validation (This is useful when using endpoint-centric APIs) 59 | - Major change because it changes the signature of the public IValidationProvider interface. 60 | 61 | ### New in 2.0.0 62 | - Add net7.0 target framework. 63 | 64 | ### New in 1.8.0 65 | - Use `Services.TryAddScoped` instead of `Services.AddScoped` for validators, in case 66 | the consuming app has already registered validators with a different lifetime. 67 | 68 | ### New in 1.7.0 69 | - Upgrade to FluentValidation V10 70 | - Prevent ValidateObjectTree from visiting struct properties [Bug #33](https://github.com/mrpmorris/blazor-validation/issues/33) 71 | 72 | ### New in 1.6.0 73 | - Suport FluentValidation's RuleForEach and ChildRules 74 | 75 | ### New in 1.5.0 76 | - Support .NET 5.0 77 | 78 | ### New in 1.4.0 79 | - Upgrade to FluentValidation 9 80 | 81 | ### New in 1.3.1 82 | - Add new EditContext.ValidateProperties for validating sub-sets of an object 83 | 84 | ### New in 1.2.0 85 | - Return `bool` from EditContext.ValidateProperty 86 | 87 | ### New in 1.0.0 88 | - Updated FluentValidationSample 89 | - First major release 90 | 91 | ### New in 0.10 92 | - Remove old EditContextExtensions file 93 | - Ensure strings are not enumerated when traversing a whole object tree to validate 94 | 95 | ### New in 0.9 96 | - Upgraded to .NETCore 3 97 | 98 | ### New in 0.8 99 | - Upgraded to Blazor RC1 100 | 101 | ### New in 0.7 102 | - Added an `EditContext.ValidateObjectTree` 103 | - Upgraded to Blazor Preview 9 104 | 105 | ### New in 0.6.1 106 | - Fixed bug in FluentValidation that prevented objects with complex property types from being validated 107 | 108 | ### New in 0.6.0 109 | - Upgraded to Blazor Preview 8 110 | 111 | ### New in 0.5.0 112 | - Upgraded to Blazor Preview 7 113 | 114 | ### New in 0.4.0 115 | - Initial public release 116 | 117 | [Microsoft aspdotnet blazor project]: 118 | [Blazor-Validation]: 119 | [Blazor-FluentValidation]: 120 | [Data Annotations Sample]: 121 | [FluentValidation Sample]: 122 | [Blazored FluentValidation]: 123 | -------------------------------------------------------------------------------- /Source/.editorconfig: -------------------------------------------------------------------------------- 1 | # Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Data\Mine\Code\blazor-fluxor\src codebase based on best match to current usage at 27/07/2018 2 | # You can modify the rules from these initially generated values to suit your own policies 3 | # You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 4 | [*.cs] 5 | 6 | #Core editorconfig formatting - indentation 7 | 8 | #use hard tabs for indentation 9 | indent_style = tab 10 | 11 | #Formatting - new line options 12 | 13 | #require braces to be on a new line for methods, anonymous_types, accessors, properties, control_blocks, lambdas, and types (also known as "Allman" style) 14 | csharp_new_line_before_open_brace = methods, anonymous_types, accessors, properties, control_blocks, lambdas, types 15 | 16 | #Formatting - organize using options 17 | 18 | #do not place System.* using directives before other using directives 19 | dotnet_sort_system_directives_first = false 20 | 21 | #Formatting - spacing options 22 | 23 | #require NO space between a cast and the value 24 | csharp_space_after_cast = false 25 | #require a space before the colon for bases or interfaces in a type declaration 26 | csharp_space_after_colon_in_inheritance_clause = true 27 | #require a space before the colon for bases or interfaces in a type declaration 28 | csharp_space_before_colon_in_inheritance_clause = true 29 | #remove space within empty argument list parentheses 30 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 31 | #remove space between method call name and opening parenthesis 32 | csharp_space_between_method_call_name_and_opening_parenthesis = false 33 | #do not place space characters after the opening parenthesis and before the closing parenthesis of a method call 34 | csharp_space_between_method_call_parameter_list_parentheses = false 35 | #remove space within empty parameter list parentheses for a method declaration 36 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 37 | #place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. 38 | csharp_space_between_method_declaration_parameter_list_parentheses = false 39 | 40 | #Formatting - wrapping options 41 | 42 | #leave code block on single line 43 | csharp_preserve_single_line_blocks = true 44 | 45 | #Style - expression bodied member options 46 | 47 | #prefer expression-bodied members for accessors 48 | csharp_style_expression_bodied_accessors = true:suggestion 49 | #prefer block bodies for methods 50 | csharp_style_expression_bodied_methods = false:suggestion 51 | #prefer expression-bodied members for properties 52 | csharp_style_expression_bodied_properties = true:suggestion 53 | 54 | #Style - expression level options 55 | 56 | #prefer out variables to be declared inline in the argument list of a method call when possible 57 | csharp_style_inlined_variable_declaration = true:suggestion 58 | #prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them 59 | dotnet_style_predefined_type_for_member_access = true:suggestion 60 | 61 | #Style - implicit and explicit types 62 | 63 | #prefer explicit type over var to declare variables with built-in system types such as int 64 | csharp_style_var_for_built_in_types = false:suggestion 65 | #prefer var when the type is already mentioned on the right-hand side of a declaration expression 66 | csharp_style_var_when_type_is_apparent = true:suggestion 67 | 68 | #Style - language keyword and framework type options 69 | 70 | #prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them 71 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 72 | 73 | #Style - qualification options 74 | 75 | #prefer fields not to be prefaced with this. or Me. in Visual Basic 76 | dotnet_style_qualification_for_field = false:suggestion 77 | #prefer methods not to be prefaced with this. or Me. in Visual Basic 78 | dotnet_style_qualification_for_method = false:suggestion 79 | #prefer properties not to be prefaced with this. or Me. in Visual Basic 80 | dotnet_style_qualification_for_property = false:suggestion 81 | -------------------------------------------------------------------------------- /Source/Lib/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Peter Morris 6 | 7 | Peter Morris 8 | 9 | MIT 10 | net8.0;net9.0 11 | 12 | https://github.com/mrpmorris/blazor-validation 13 | 14 | https://github.com/mrpmorris/blazor-validation.git 15 | git 16 | latest 17 | 3.0 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.FluentValidation/FluentValidationPropertiesExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Morris.Blazor.Validation; 3 | 4 | namespace Morris.Blazor.FluentValidation 5 | { 6 | public static class FluentValidationPropertiesExtensions 7 | { 8 | public const string FluentValidatorKey = "FluentValidatorKey"; 9 | 10 | public static ValidationProperties FluentValidator(this ValidationProperties properties) 11 | where T : IValidator 12 | { 13 | return properties.Value("x", "y"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.FluentValidation/FluentValidationRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | 6 | namespace Morris.Blazor.FluentValidation 7 | { 8 | internal class FluentValidationRepository 9 | { 10 | private readonly ReadOnlyDictionary> ValidatorTypesByTypeToValidate; 11 | 12 | public FluentValidationRepository(IEnumerable>> typeAndValidatorTypes) 13 | { 14 | if (typeAndValidatorTypes == null) 15 | throw new ArgumentNullException(nameof(typeAndValidatorTypes)); 16 | 17 | ValidatorTypesByTypeToValidate = new ReadOnlyDictionary>( 18 | typeAndValidatorTypes.ToDictionary(x => x.Key, x => x.Value) 19 | ); 20 | } 21 | 22 | public IEnumerable GetValidatorTypesForObject(object instance) 23 | { 24 | if (instance == null) 25 | throw new ArgumentNullException(nameof(instance)); 26 | 27 | Type typeToValidate = instance.GetType(); 28 | if (!ValidatorTypesByTypeToValidate.TryGetValue(typeToValidate, out IEnumerable validatorTypes)) 29 | return Array.Empty(); 30 | return validatorTypes; 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.FluentValidation/FluentValidationValidatorProvider.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using FluentValidation.Internal; 3 | using FluentValidation.Results; 4 | using Microsoft.AspNetCore.Components.Forms; 5 | using Morris.Blazor.Validation; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Threading.Tasks; 11 | 12 | namespace Morris.Blazor.FluentValidation 13 | { 14 | public class FluentValidationValidatorProvider : IValidationProvider 15 | { 16 | private Func OnTransformModel { get; set; } = null; 17 | 18 | public void InitializeEditContext 19 | ( 20 | EditContext editContext, 21 | IServiceProvider serviceProvider, 22 | ValidationProperties properties, 23 | Func transformModel = null 24 | ) 25 | { 26 | if (editContext == null) 27 | throw new ArgumentNullException(nameof(editContext)); 28 | if (serviceProvider == null) 29 | throw new ArgumentNullException(nameof(serviceProvider)); 30 | properties ??= ValidationProperties.Set; 31 | OnTransformModel = transformModel; 32 | 33 | var messages = new ValidationMessageStore(editContext); 34 | editContext.OnValidationRequested += 35 | (sender, eventArgs) => 36 | { 37 | _ = ValidateModel((EditContext)sender, messages, serviceProvider, properties); 38 | }; 39 | 40 | editContext.OnFieldChanged += 41 | (sender, eventArgs) => 42 | { 43 | _ = ValidateField(editContext, messages, eventArgs.FieldIdentifier, serviceProvider); 44 | }; 45 | } 46 | 47 | private async Task ValidateModel( 48 | EditContext editContext, 49 | ValidationMessageStore messages, 50 | IServiceProvider serviceProvider, 51 | ValidationProperties properties) 52 | { 53 | if (editContext == null) 54 | throw new ArgumentNullException(nameof(editContext)); 55 | if (messages == null) 56 | throw new ArgumentNullException(nameof(messages)); 57 | if (serviceProvider == null) 58 | throw new ArgumentNullException(nameof(serviceProvider)); 59 | if (editContext.Model == null) 60 | throw new NullReferenceException($"{nameof(editContext)}.{nameof(editContext.Model)}"); 61 | 62 | messages.Clear(); 63 | editContext.NotifyValidationStateChanged(); 64 | 65 | object transformedModel = GetTransformedModel(editContext.Model); 66 | 67 | IEnumerable validators = GetValidatorsForObject(transformedModel, serviceProvider); 68 | 69 | var validationContext = new ValidationContext(transformedModel); 70 | 71 | var validationResults = new List(); 72 | foreach (IValidator validator in validators) 73 | { 74 | var validationResult = await validator.ValidateAsync(validationContext); 75 | validationResults.Add(validationResult); 76 | } 77 | 78 | IEnumerable validationFailures = validationResults.SelectMany(x => x.Errors); 79 | foreach (var validationError in validationFailures) 80 | { 81 | GetParentObjectAndPropertyName(transformedModel, validationError.PropertyName, out object parentObject, out string propertyName); 82 | if (parentObject != null) 83 | messages.Add(new FieldIdentifier(parentObject, propertyName), validationError.ErrorMessage); 84 | } 85 | 86 | editContext.NotifyValidationStateChanged(); 87 | } 88 | private object GetTransformedModel(object model) 89 | { 90 | return OnTransformModel is not null ? OnTransformModel(model) : model; 91 | } 92 | 93 | private void GetParentObjectAndPropertyName( 94 | object model, 95 | string propertyPath, 96 | out object parentObject, 97 | out string propertyName) 98 | { 99 | if (model == null) 100 | throw new ArgumentNullException(nameof(model)); 101 | 102 | var propertyPathParts = new Queue(propertyPath.Split('.')); 103 | Type modelType = model.GetType(); 104 | while (propertyPathParts.Count > 1) 105 | { 106 | string name = propertyPathParts.Dequeue(); 107 | 108 | string propertyIndexString = null; 109 | int bracketIndex = name.IndexOf('['); 110 | if (bracketIndex > 0) 111 | { 112 | propertyIndexString = name.Substring(bracketIndex + 1, name.Length - bracketIndex - 2); 113 | name = name.Remove(bracketIndex); 114 | } 115 | 116 | PropertyInfo propertyInfo = modelType.GetProperty(name); 117 | model = model == null 118 | ? null 119 | : propertyInfo.GetValue(model, null); 120 | 121 | if (propertyIndexString == null) 122 | modelType = propertyInfo.PropertyType; 123 | else 124 | { 125 | List collection = ((IEnumerable)model).ToList(); 126 | int propertyIndex = int.Parse(propertyIndexString); 127 | model = collection[propertyIndex]; 128 | } 129 | } 130 | 131 | parentObject = model; 132 | propertyName = propertyPathParts.Dequeue(); 133 | } 134 | 135 | private async Task ValidateField 136 | ( 137 | EditContext editContext, 138 | ValidationMessageStore messages, 139 | FieldIdentifier fieldIdentifier, 140 | IServiceProvider serviceProvider 141 | ) 142 | { 143 | if (editContext == null) 144 | throw new ArgumentNullException(nameof(editContext)); 145 | if (messages == null) 146 | throw new ArgumentNullException(nameof(messages)); 147 | if (serviceProvider == null) 148 | throw new ArgumentNullException(nameof(serviceProvider)); 149 | if (editContext.Model == null) 150 | throw new NullReferenceException($"{nameof(editContext)}.{nameof(editContext.Model)}"); 151 | 152 | object transformedModel = GetTransformedModel(editContext.Model); 153 | var propertiesToValidate = new string[] { fieldIdentifier.FieldName }; 154 | var fluentValidationContext = 155 | new ValidationContext( 156 | instanceToValidate: transformedModel, 157 | propertyChain: new PropertyChain(), 158 | validatorSelector: new MemberNameValidatorSelector(propertiesToValidate) 159 | ); 160 | 161 | messages.Clear(fieldIdentifier); 162 | editContext.NotifyValidationStateChanged(); 163 | 164 | IEnumerable validators = GetValidatorsForObject(transformedModel, serviceProvider); 165 | var validationResults = new List(); 166 | 167 | foreach (IValidator validator in validators) 168 | { 169 | var validationResult = await validator.ValidateAsync(fluentValidationContext); 170 | validationResults.Add(validationResult); 171 | } 172 | 173 | IEnumerable errorMessages = 174 | validationResults 175 | .SelectMany(x => x.Errors) 176 | .Select(x => x.ErrorMessage) 177 | .Distinct(); 178 | 179 | foreach (string errorMessage in errorMessages) 180 | messages.Add(fieldIdentifier, errorMessage); 181 | 182 | editContext.NotifyValidationStateChanged(); 183 | } 184 | 185 | private static IEnumerable GetValidatorsForObject( 186 | object model, 187 | IServiceProvider serviceProvider) 188 | { 189 | var validatorTypesRepository = (FluentValidationRepository)serviceProvider.GetService(typeof(FluentValidationRepository)); 190 | IEnumerable validatorTypes = validatorTypesRepository.GetValidatorTypesForObject(model); 191 | IEnumerable validators = validatorTypes.Select(x => (IValidator)serviceProvider.GetService(x)); 192 | return validators; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.FluentValidation/Morris.Blazor.FluentValidation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | FluentValidation for Blazor 5 | Blazor FluentValidation 6 | true 7 | blazor-fluentvalidation.png 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | True 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.FluentValidation/ValidationConfigurationFluentValidationExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.DependencyInjection.Extensions; 4 | using Morris.Blazor.FluentValidation; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | 10 | namespace Morris.Blazor.Validation 11 | { 12 | public static class ValidationConfigurationFluentValidationExtensions 13 | { 14 | public static ValidationConfiguration AddFluentValidation(this ValidationConfiguration config, Assembly assemblyToScan, params Assembly[] additionalAssembliesToScan) 15 | { 16 | if (assemblyToScan == null) 17 | throw new ArgumentNullException(nameof(assemblyToScan)); 18 | 19 | var allAssembliesToScan = new List(); 20 | allAssembliesToScan.Add(assemblyToScan); 21 | if (additionalAssembliesToScan != null) 22 | allAssembliesToScan.AddRange(additionalAssembliesToScan); 23 | 24 | ScanForValidators(config.Services, allAssembliesToScan); 25 | config.Services.AddScoped(); 26 | config.Repository.Add(typeof(FluentValidationValidatorProvider)); 27 | return config; 28 | } 29 | 30 | private static void ScanForValidators(IServiceCollection services, IEnumerable assembliesToScan) 31 | { 32 | if (assembliesToScan == null || !assembliesToScan.Any()) 33 | throw new ArgumentNullException(nameof(assembliesToScan)); 34 | 35 | var validatorsByType = new Dictionary>(); 36 | 37 | IEnumerable validatorTypesInAssembly = assembliesToScan 38 | .SelectMany(x => x.GetTypes()) 39 | .Where(x => x.IsClass) 40 | .Where(x => !x.IsAbstract && !x.IsGenericTypeDefinition) 41 | .Where(x => 42 | x.GetInterfaces() 43 | .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>)) 44 | ); 45 | 46 | foreach(Type validatorType in validatorTypesInAssembly) 47 | { 48 | Type typeToValidate = validatorType.BaseType.GetGenericArguments()[0]; 49 | List validatorTypesForTypeToValidate; 50 | if (!validatorsByType.TryGetValue(typeToValidate, out validatorTypesForTypeToValidate)) 51 | { 52 | validatorTypesForTypeToValidate = new List(); 53 | validatorsByType[typeToValidate] = validatorTypesForTypeToValidate; 54 | } 55 | validatorTypesForTypeToValidate.Add(validatorType); 56 | } 57 | 58 | var repository = new FluentValidationRepository( 59 | validatorsByType.Select(x => new KeyValuePair>(x.Key, x.Value)) 60 | ); 61 | 62 | validatorsByType 63 | .SelectMany(x => x.Value) 64 | .Distinct() 65 | .ToList() 66 | .ForEach(x => services.TryAddScoped(x)); 67 | 68 | services.AddSingleton(repository); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.FluentValidation/blazor-fluentvalidation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Lib/Morris.Blazor.FluentValidation/blazor-fluentvalidation.png -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/DataAnnotationsValidatorProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Forms; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace Morris.Blazor.Validation 6 | { 7 | public class DataAnnotationsValidatorProvider : IValidationProvider 8 | { 9 | public void InitializeEditContext 10 | ( 11 | EditContext editContext, 12 | IServiceProvider serviceProvider, 13 | ValidationProperties properties, 14 | Func transformModel = null 15 | ) 16 | { 17 | #if NET7_0_OR_GREATER 18 | editContext.EnableDataAnnotationsValidation(serviceProvider); 19 | #else 20 | editContext.EnableDataAnnotationsValidation(); 21 | #endif 22 | } 23 | } 24 | 25 | public static class ValidationConfigurationDataAnnotationsExtensions 26 | { 27 | public static ValidationConfiguration AddDataAnnotationsValidation( 28 | this ValidationConfiguration config) 29 | { 30 | config.Services.AddScoped(); 31 | config.Repository.Add(typeof(DataAnnotationsValidatorProvider)); 32 | return config; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/Extensions/EditContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Forms; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace Morris.Blazor.Validation.Extensions 9 | { 10 | public static class EditContextExtensions 11 | { 12 | static PropertyInfo IsModifiedProperty; 13 | static MethodInfo GetFieldStateMethod; 14 | 15 | /// 16 | /// Validates an entire object tree 17 | /// 18 | /// The EditContext to validate the Model of 19 | /// True if valid, otherwise false 20 | public static bool ValidateObjectTree(this EditContext editContext) 21 | { 22 | var validatedObjects = new HashSet(); 23 | ValidateObject(editContext, editContext.Model, validatedObjects); 24 | editContext.NotifyValidationStateChanged(); 25 | return !editContext.GetValidationMessages().Any(); 26 | } 27 | 28 | public static bool ValidateProperty( 29 | this EditContext editContext, 30 | FieldIdentifier fieldIdentifier) 31 | { 32 | var propertyInfo = fieldIdentifier.Model.GetType().GetProperty( 33 | fieldIdentifier.FieldName, 34 | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static); 35 | 36 | var validatedObjects = new HashSet(); 37 | ValidateProperty(editContext, fieldIdentifier.Model, propertyInfo, validatedObjects); 38 | 39 | return !editContext.GetValidationMessages(fieldIdentifier).Any(); 40 | } 41 | 42 | public static bool ValidateProperties( 43 | this EditContext editContext, 44 | params FieldIdentifier[] properties) 45 | { 46 | if (properties == null || properties.Length == 0) 47 | throw new ArgumentNullException(nameof(properties)); 48 | 49 | bool valid = true; 50 | foreach (FieldIdentifier property in properties) 51 | valid &= editContext.ValidateProperty(property); 52 | return valid; 53 | } 54 | 55 | private static void ValidateObject( 56 | EditContext editContext, 57 | object instance, 58 | HashSet validatedObjects) 59 | { 60 | if (instance == null) 61 | return; 62 | 63 | if (validatedObjects.Contains(instance)) 64 | return; 65 | 66 | if (instance is IEnumerable && !(instance is string)) 67 | { 68 | foreach (object value in (IEnumerable)instance) 69 | ValidateObject(editContext, value, validatedObjects); 70 | return; 71 | } 72 | 73 | if (instance.GetType().Assembly == typeof(string).Assembly) 74 | return; 75 | 76 | validatedObjects.Add(instance); 77 | 78 | var properties = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 79 | foreach (PropertyInfo property in properties) 80 | ValidateProperty(editContext, instance, property, validatedObjects); 81 | } 82 | 83 | private static void ValidateProperty( 84 | EditContext editContext, 85 | object instance, 86 | PropertyInfo property, 87 | HashSet validatedObjects) 88 | { 89 | if (property.PropertyType.IsValueType) 90 | return; 91 | 92 | NotifyPropertyChanged(editContext, instance, property.Name); 93 | 94 | object value = property.GetValue(instance); 95 | ValidateObject(editContext, value, validatedObjects); 96 | } 97 | 98 | private static void NotifyPropertyChanged( 99 | EditContext editContext, 100 | object instance, 101 | string propertyName) 102 | { 103 | var fieldIdentifier = new FieldIdentifier(instance, propertyName); 104 | object fieldState = GetFieldState(editContext, fieldIdentifier); 105 | 106 | if (IsModifiedProperty == null) 107 | { 108 | IsModifiedProperty = fieldState.GetType().GetProperty( 109 | "IsModified", 110 | BindingFlags.Public | BindingFlags.Instance); 111 | } 112 | 113 | object originalIsModified = IsModifiedProperty.GetValue(fieldState); 114 | editContext.NotifyFieldChanged(fieldIdentifier); 115 | IsModifiedProperty.SetValue(fieldState, originalIsModified); 116 | } 117 | 118 | private static object GetFieldState(EditContext editContext, FieldIdentifier fieldIdentifier) 119 | { 120 | var parameters = new object[] { fieldIdentifier }; 121 | EnsureGetFieldStateMethod(editContext); 122 | return GetFieldStateMethod.Invoke(editContext, parameters); 123 | } 124 | 125 | private static void EnsureGetFieldStateMethod(EditContext editContext) 126 | { 127 | var methodname = "GetOrAddFieldState"; 128 | 129 | if (GetFieldStateMethod == null) 130 | { 131 | GetFieldStateMethod = editContext.GetType().GetMethod(methodname, 132 | BindingFlags.NonPublic | BindingFlags.Instance); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/IValidationProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Forms; 2 | using System; 3 | 4 | namespace Morris.Blazor.Validation 5 | { 6 | public interface IValidationProvider 7 | { 8 | void InitializeEditContext 9 | ( 10 | EditContext editContext, 11 | IServiceProvider serviceProvider, 12 | ValidationProperties properties, 13 | Func transformModel = null 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/IValidationProviderRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Morris.Blazor.Validation 5 | { 6 | public interface IValidationProviderRepository 7 | { 8 | IEnumerable All { get; } 9 | IValidationProviderRepository Add(Type providerType); 10 | IValidationProviderRepository Remove(Type providerType); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/Morris.Blazor.Validation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | true 6 | Morris.Blazor.Validation 7 | Morris.Blazor.Validation 8 | Validation for Blazor 9 | Blazor Validation 10 | blazor-validation.png 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | True 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/Validate.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.AspNetCore.Components.Forms; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Morris.Blazor.Validation 7 | { 8 | public class Validate : ComponentBase 9 | { 10 | [CascadingParameter] 11 | EditContext CurrentEditContext { get; set; } 12 | 13 | [Inject] 14 | IValidationProviderRepository Repository { get; set; } 15 | 16 | [Parameter] 17 | public ValidationProperties ValidationProperties { get; set; } 18 | 19 | [Inject] 20 | IServiceProvider ServiceProvider { get; set; } 21 | [Parameter] 22 | public Func OnTransformModel { get; set; } = null; 23 | 24 | public override async Task SetParametersAsync(ParameterView parameters) 25 | { 26 | EditContext previousEditContext = CurrentEditContext; 27 | 28 | await base.SetParametersAsync(parameters); 29 | 30 | if (CurrentEditContext == null) 31 | throw new InvalidOperationException($"{nameof(DataAnnotationsValidator)} requires a cascading " + 32 | $"parameter of type {nameof(EditContext)}. For example, you can use {nameof(DataAnnotationsValidator)} " + 33 | $"inside an {nameof(EditForm)}."); 34 | 35 | if (CurrentEditContext != previousEditContext) 36 | EditContextChanged(); 37 | } 38 | 39 | private void EditContextChanged() 40 | { 41 | foreach (Type providerType in Repository.All) 42 | { 43 | var validationProvider = (IValidationProvider)ServiceProvider.GetService(providerType); 44 | validationProvider.InitializeEditContext(CurrentEditContext, ServiceProvider, ValidationProperties, OnTransformModel); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/ValidationConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | 4 | namespace Morris.Blazor.Validation 5 | { 6 | public class ValidationConfiguration 7 | { 8 | public IServiceCollection Services { get; } 9 | public IValidationProviderRepository Repository { get; } 10 | 11 | public ValidationConfiguration( 12 | IServiceCollection services, 13 | IValidationProviderRepository repository) 14 | { 15 | Services = services ?? throw new ArgumentNullException(nameof(services)); 16 | Repository = repository ?? throw new ArgumentNullException(nameof(repository)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/ValidationProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace Morris.Blazor.Validation 6 | { 7 | public class ValidationProperties 8 | { 9 | private readonly Dictionary MutableValues = new Dictionary(); 10 | 11 | public readonly IReadOnlyDictionary Values; 12 | 13 | private ValidationProperties() 14 | { 15 | Values = new ReadOnlyDictionary(MutableValues); 16 | } 17 | 18 | public static ValidationProperties Set => new ValidationProperties(); 19 | 20 | public ValidationProperties Value(string name, object value) 21 | { 22 | if (string.IsNullOrWhiteSpace(name)) 23 | throw new ArgumentException(message: "Required", paramName: nameof(name)); 24 | if (value is null) 25 | throw new ArgumentNullException(nameof(value)); 26 | 27 | MutableValues.Add(name, value); 28 | return this; 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/ValidationProviderRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Morris.Blazor.Validation 6 | { 7 | public class ValidationProviderRepository : IValidationProviderRepository 8 | { 9 | private Type[] Providers = new Type[0]; 10 | public IEnumerable All => Providers; 11 | 12 | public IValidationProviderRepository Add(Type providerType) 13 | { 14 | if (providerType == null) 15 | throw new ArgumentNullException(nameof(providerType)); 16 | if (!typeof(IValidationProvider).IsAssignableFrom(providerType)) 17 | throw new ArgumentException($"{providerType.Name} " + 18 | $"does not implement {nameof(IValidationProvider)}"); 19 | 20 | Providers = Providers.Concat(new Type[] { providerType }).ToArray(); 21 | return this; 22 | } 23 | 24 | public IValidationProviderRepository Remove(Type providerType) 25 | { 26 | Providers = Providers.Where(x => x != providerType).ToArray(); 27 | return this; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/ValidationServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | 4 | namespace Morris.Blazor.Validation 5 | { 6 | public static class ValidationServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddFormValidation( 9 | this IServiceCollection instance, 10 | Action config = null) 11 | { 12 | var repository = new ValidationProviderRepository(); 13 | instance.AddScoped((_) => repository); 14 | if (config != null) 15 | { 16 | var c = new ValidationConfiguration(instance, repository); 17 | config(c); 18 | } 19 | return instance; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Lib/Morris.Blazor.Validation/blazor-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Lib/Morris.Blazor.Validation/blazor-validation.png -------------------------------------------------------------------------------- /Source/Morris.Blazor.Validation.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32421.90 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Morris.Blazor.Validation", "Lib\Morris.Blazor.Validation\Morris.Blazor.Validation.csproj", "{07CE8E70-DF46-4A93-864B-1BBB64E52426}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Morris.Blazor.FluentValidation", "Lib\Morris.Blazor.FluentValidation\Morris.Blazor.FluentValidation.csproj", "{9E544B03-8471-4C99-8405-C6D55ABAEFD6}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{D40EFCC3-037E-40A4-9B7C-8A9E61ED3AB2}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataAnnotationsValidationSample", "Samples\01-DataAnnotationsValidation\DataAnnotationsValidationSample\DataAnnotationsValidationSample.csproj", "{3B756D20-10A2-4F68-89CC-CB3F201F4CE4}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-DataAnnotations", "01-DataAnnotations", "{C76D2D7E-0AF1-483D-8C7C-96E05CE38AC3}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-FluentValidation", "02-FluentValidation", "{F3597B78-7306-40ED-8FF8-B2C6B1ED9539}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{451C13F4-69B5-4AB3-B1A8-008A112DE521}" 19 | ProjectSection(SolutionItems) = preProject 20 | Lib\Directory.Build.props = Lib\Directory.Build.props 21 | EndProjectSection 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentValidationSample", "Samples\02-FluentValidation\FluentValidationSample\FluentValidationSample.csproj", "{11850E97-B92B-4436-B471-3D43A1802A90}" 24 | EndProject 25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A3D40341-FD91-4A62-8EAA-B3548AFD762A}" 26 | ProjectSection(SolutionItems) = preProject 27 | .editorconfig = .editorconfig 28 | ..\README.md = ..\README.md 29 | EndProjectSection 30 | EndProject 31 | Global 32 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 33 | Debug|Any CPU = Debug|Any CPU 34 | Release|Any CPU = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 37 | {07CE8E70-DF46-4A93-864B-1BBB64E52426}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {07CE8E70-DF46-4A93-864B-1BBB64E52426}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {07CE8E70-DF46-4A93-864B-1BBB64E52426}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {07CE8E70-DF46-4A93-864B-1BBB64E52426}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {9E544B03-8471-4C99-8405-C6D55ABAEFD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {9E544B03-8471-4C99-8405-C6D55ABAEFD6}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {9E544B03-8471-4C99-8405-C6D55ABAEFD6}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {9E544B03-8471-4C99-8405-C6D55ABAEFD6}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {3B756D20-10A2-4F68-89CC-CB3F201F4CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {3B756D20-10A2-4F68-89CC-CB3F201F4CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {3B756D20-10A2-4F68-89CC-CB3F201F4CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {3B756D20-10A2-4F68-89CC-CB3F201F4CE4}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {11850E97-B92B-4436-B471-3D43A1802A90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {11850E97-B92B-4436-B471-3D43A1802A90}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {11850E97-B92B-4436-B471-3D43A1802A90}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {11850E97-B92B-4436-B471-3D43A1802A90}.Release|Any CPU.Build.0 = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(SolutionProperties) = preSolution 55 | HideSolutionNode = FALSE 56 | EndGlobalSection 57 | GlobalSection(NestedProjects) = preSolution 58 | {07CE8E70-DF46-4A93-864B-1BBB64E52426} = {451C13F4-69B5-4AB3-B1A8-008A112DE521} 59 | {9E544B03-8471-4C99-8405-C6D55ABAEFD6} = {451C13F4-69B5-4AB3-B1A8-008A112DE521} 60 | {3B756D20-10A2-4F68-89CC-CB3F201F4CE4} = {C76D2D7E-0AF1-483D-8C7C-96E05CE38AC3} 61 | {C76D2D7E-0AF1-483D-8C7C-96E05CE38AC3} = {D40EFCC3-037E-40A4-9B7C-8A9E61ED3AB2} 62 | {F3597B78-7306-40ED-8FF8-B2C6B1ED9539} = {D40EFCC3-037E-40A4-9B7C-8A9E61ED3AB2} 63 | {11850E97-B92B-4436-B471-3D43A1802A90} = {F3597B78-7306-40ED-8FF8-B2C6B1ED9539} 64 | EndGlobalSection 65 | GlobalSection(ExtensibilityGlobals) = postSolution 66 | SolutionGuid = {D95C3764-54F3-40FD-8A52-CB4D1F984806} 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Components/AddressEdit.razor: -------------------------------------------------------------------------------- 1 | @using Models 2 | 3 |
4 |
5 | @if (!string.IsNullOrEmpty(Title)) 6 | { 7 |
8 | @Title 9 |
10 | } 11 |
12 |
13 |
14 | 15 |
16 |
17 | 18 | Address.Line1) /> 19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 | 28 | Address.Line2) /> 29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 | 38 | Address.Line3) /> 39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 | 48 | Address.Town) /> 49 |
50 |
51 | 52 |
53 |
54 | 55 |
56 |
57 | 58 | Address.Area) /> 59 |
60 |
61 | 62 |
63 |
64 | 65 |
66 |
67 | 68 | Address.Country) /> 69 |
70 |
71 | 72 |
73 |
74 | 75 |
76 |
77 | 78 | Address.PostalCode) /> 79 |
80 |
81 |
82 |
83 |
84 | 85 | @code 86 | { 87 | [Parameter] 88 | public string Title { get; set; } 89 | 90 | [Parameter] 91 | public Address Address { get; set; } 92 | } -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/DataAnnotationsValidationSample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Models/Address.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace DataAnnotationsValidationSample.Models 8 | { 9 | public class Address 10 | { 11 | [Required] 12 | public string Line1 { get; set; } 13 | public string Line2 { get; set; } 14 | public string Line3 { get; set; } 15 | [Required] 16 | public string Town { get; set; } 17 | [Required] 18 | public string Area { get; set; } 19 | [Required] 20 | public string Country { get; set; } 21 | [Required] 22 | public string PostalCode { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Models/NamedAddress.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace DataAnnotationsValidationSample.Models 4 | { 5 | public class NamedAddress 6 | { 7 | [Required] 8 | public string Name { get; set; } 9 | [Required] 10 | public Address Address { get; set; } = new Address(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Models/Person.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace DataAnnotationsValidationSample.Models 5 | { 6 | public class Person 7 | { 8 | [Required] 9 | public string Salutation { get; set; } 10 | [Required] 11 | public string GivenName { get; set; } 12 | public string MiddleNames { get; set; } 13 | [Required] 14 | public string FamilyName { get; set; } 15 | [Required, EmailAddress] 16 | public string EmailAddress { get; set; } 17 | [Required] 18 | public List Addresses { get; set; } 19 | 20 | public Person() 21 | { 22 | Addresses = new List 23 | { 24 | new NamedAddress 25 | { 26 | Name = "Home" 27 | }, 28 | new NamedAddress 29 | { 30 | Name = "Work" 31 | } 32 | }; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using Morris.Blazor.Validation.Extensions 3 | 4 | 5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 | Person.Salutation) /> 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 | 21 | Person.GivenName) /> 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 | 30 | Person.MiddleNames) /> 31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 | 39 | Person.FamilyName) /> 40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 | 48 | Person.EmailAddress) /> 49 |
50 |
51 | @foreach (var namedAddress in Person.Addresses) 52 | { 53 |
54 | 55 |
56 | } 57 | 58 |
59 | 60 | @code{ 61 | Models.Person Person = new Models.Person(); 62 | 63 | void SubmitForm(EditContext editContext) 64 | { 65 | editContext.ValidateObjectTree(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Pages/_Imports.razor: -------------------------------------------------------------------------------- 1 | @layout MainLayout 2 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Morris.Blazor.Validation; 4 | using System; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | namespace DataAnnotationsValidationSample 9 | { 10 | public class Program 11 | { 12 | public static async Task Main(string[] args) 13 | { 14 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 15 | builder.RootComponents.Add("app"); 16 | 17 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 18 | builder.Services.AddFormValidation(config => config.AddDataAnnotationsValidation()); 19 | 20 | await builder.Build().RunAsync(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  7 | 8 |
9 | 16 |
17 | 18 | @code { 19 | bool collapseNavMenu = true; 20 | 21 | string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 22 | 23 | void ToggleNavMenu() 24 | { 25 | collapseNavMenu = !collapseNavMenu; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.JSInterop 6 | @using DataAnnotationsValidationSample 7 | @using DataAnnotationsValidationSample.Components 8 | @using DataAnnotationsValidationSample.Shared -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | app { 8 | position: relative; 9 | display: flex; 10 | flex-direction: column; 11 | } 12 | 13 | .top-row { 14 | height: 3.5rem; 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .main { 20 | flex: 1; 21 | } 22 | 23 | .main .top-row { 24 | background-color: #e6e6e6; 25 | border-bottom: 1px solid #d6d5d5; 26 | } 27 | 28 | .sidebar { 29 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 30 | } 31 | 32 | .sidebar .top-row { 33 | background-color: rgba(0,0,0,0.4); 34 | } 35 | 36 | .sidebar .navbar-brand { 37 | font-size: 1.1rem; 38 | } 39 | 40 | .sidebar .oi { 41 | width: 2rem; 42 | font-size: 1.1rem; 43 | vertical-align: text-top; 44 | top: -2px; 45 | } 46 | 47 | .nav-item { 48 | font-size: 0.9rem; 49 | padding-bottom: 0.5rem; 50 | } 51 | 52 | .nav-item:first-of-type { 53 | padding-top: 1rem; 54 | } 55 | 56 | .nav-item:last-of-type { 57 | padding-bottom: 1rem; 58 | } 59 | 60 | .nav-item a { 61 | color: #d7d7d7; 62 | border-radius: 4px; 63 | height: 3rem; 64 | display: flex; 65 | align-items: center; 66 | line-height: 3rem; 67 | } 68 | 69 | .nav-item a.active { 70 | background-color: rgba(255,255,255,0.25); 71 | color: white; 72 | } 73 | 74 | .nav-item a:hover { 75 | background-color: rgba(255,255,255,0.1); 76 | color: white; 77 | } 78 | 79 | .content { 80 | padding-top: 1.1rem; 81 | } 82 | 83 | .navbar-toggler { 84 | background-color: rgba(255, 255, 255, 0.1); 85 | } 86 | 87 | .valid.modified:not([type=checkbox]) { 88 | outline: 1px solid #26b050; 89 | } 90 | 91 | .invalid { 92 | outline: 1px solid red; 93 | } 94 | 95 | .validation-message { 96 | color: red; 97 | } 98 | 99 | @media (max-width: 767.98px) { 100 | .main .top-row { 101 | display: none; 102 | } 103 | } 104 | 105 | @media (min-width: 768px) { 106 | app { 107 | flex-direction: row; 108 | } 109 | 110 | .sidebar { 111 | width: 250px; 112 | height: 100vh; 113 | position: sticky; 114 | top: 0; 115 | } 116 | 117 | .main .top-row { 118 | position: sticky; 119 | top: 0; 120 | } 121 | 122 | .main > div { 123 | padding-left: 2rem !important; 124 | padding-right: 1.5rem !important; 125 | } 126 | 127 | .navbar-toggler { 128 | display: none; 129 | } 130 | 131 | .sidebar .collapse { 132 | /* Never collapse the sidebar for wide screens */ 133 | display: block; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Source/Samples/01-DataAnnotationsValidation/DataAnnotationsValidationSample/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DataAnnotationsValidationSample 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |

Sorry, there's nothing at this address.

9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Components/AddressEdit.razor: -------------------------------------------------------------------------------- 1 | 
2 |
3 | @if (TitleTemplate == null) 4 | { 5 | @if (!string.IsNullOrEmpty(Title)) 6 | { 7 |
8 | @Title 9 |
10 | } 11 | } 12 | else 13 | { 14 | @TitleTemplate 15 | } 16 |
17 |
18 |
19 | 20 |
21 |
22 | 23 | Address.Line1) /> 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 | Address.Line2) /> 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 | Address.Line3) /> 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 | 53 | Address.Town) /> 54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 |
62 | 63 | Address.Area) /> 64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 |
72 | 73 | Address.Country) /> 74 |
75 |
76 | 77 |
78 |
79 | 80 |
81 |
82 | 83 | Address.PostalCode) /> 84 |
85 |
86 |
87 |
88 |
89 | 90 | @code 91 | { 92 | [Parameter] 93 | public string Title { get; set; } 94 | 95 | [Parameter] 96 | public Address Address { get; set; } 97 | 98 | [Parameter] 99 | public RenderFragment TitleTemplate { get; set; } 100 | } 101 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/FluentValidationSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/FluentValidators/AddressValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using FluentValidationSample.Models; 3 | 4 | namespace FluentValidationSample.FluentValidators 5 | { 6 | public class AddressValidator : AbstractValidator
7 | { 8 | public AddressValidator() 9 | { 10 | RuleFor(x => x.Line1).NotEmpty(); 11 | RuleFor(x => x.Town).NotEmpty(); 12 | RuleFor(x => x.Area).NotEmpty(); 13 | RuleFor(x => x.Country).NotEmpty(); 14 | RuleFor(x => x.PostalCode).NotEmpty(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/FluentValidators/NamedAddressValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using FluentValidationSample.Models; 3 | 4 | namespace FluentValidationSample.FluentValidators 5 | { 6 | public class NamedAddressValidator : AbstractValidator 7 | { 8 | public NamedAddressValidator() 9 | { 10 | RuleFor(x => x.Name).NotEmpty(); 11 | RuleFor(x => x.Address).NotNull(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/FluentValidators/PersonValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentValidation; 5 | using FluentValidationSample.Models; 6 | 7 | namespace FluentValidationSample.FluentValidators 8 | { 9 | public class PersonValidator : AbstractValidator 10 | { 11 | public PersonValidator() 12 | { 13 | RuleFor(x => x.Salutation) 14 | .Cascade(CascadeMode.Stop) 15 | .NotEmpty() 16 | .MustAsync(LongRunningAsyncMethod).WithMessage("Cannot be DR"); 17 | RuleFor(x => x.GivenName).NotEmpty(); 18 | RuleFor(x => x.FamilyName).NotEmpty(); 19 | RuleFor(x => x.EmailAddress).NotEmpty().EmailAddress(); 20 | RuleFor(x => x.Addresses).NotEmpty().WithMessage("At least one address is required"); 21 | } 22 | 23 | private async Task LongRunningAsyncMethod(string arg1, CancellationToken arg2) 24 | { 25 | await Task.Delay(1000); 26 | if ("DR".Equals(arg1, StringComparison.InvariantCultureIgnoreCase)) 27 | return false; 28 | return true; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Models/Address.cs: -------------------------------------------------------------------------------- 1 | namespace FluentValidationSample.Models 2 | { 3 | public class Address 4 | { 5 | public string Line1 { get; set; } 6 | public string Line2 { get; set; } 7 | public string Line3 { get; set; } 8 | public string Town { get; set; } 9 | public string Area { get; set; } 10 | public string Country { get; set; } 11 | public string PostalCode { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Models/NamedAddress.cs: -------------------------------------------------------------------------------- 1 | namespace FluentValidationSample.Models 2 | { 3 | public class NamedAddress 4 | { 5 | public string Name { get; set; } 6 | public Address Address { get; set; } = new Address(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Models/Person.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace FluentValidationSample.Models 4 | { 5 | public class Person 6 | { 7 | public string Salutation { get; set; } 8 | public string GivenName { get; set; } 9 | public string MiddleNames { get; set; } 10 | public string FamilyName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public List Addresses { get; set; } 13 | 14 | public Person() 15 | { 16 | Addresses = new List 17 | { 18 | new NamedAddress 19 | { 20 | Name = "Duplicated address - Home" 21 | }, 22 | new NamedAddress 23 | { 24 | Name = "Duplicated address - Home" 25 | } 26 | }; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | 3 | 4 |

Error.

5 |

An error occurred while processing your request.

6 | 7 |

Development Mode

8 |

9 | Swapping to Development environment will display more detailed information about the error that occurred. 10 |

11 |

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

17 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using FluentValidationSample.FluentValidators 3 | @using Morris.Blazor.Validation 4 | @using Morris.Blazor.Validation.Extensions 5 | @using Morris.Blazor.FluentValidation 6 | 7 | 8 | ()) OnTransformModel=OnTransformModel /> 9 |
10 |
11 | 12 |
13 |
14 | 15 | Person.Salutation) /> 16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | 24 | Person.GivenName) /> 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 | Person.MiddleNames) /> 34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 | 42 | Person.FamilyName) /> 43 |
44 |
45 |
46 |
47 | 48 |
49 |
50 | 51 | Person.EmailAddress) /> 52 |
53 |
54 | Person.Addresses ) /> 55 | @foreach (var namedAddress in Person.Addresses) 56 | { 57 |
58 | 59 | 60 |
61 | @namedAddress.Name 62 | 66 |
67 |
68 |
69 |
70 | } 71 | 72 |
73 | 74 | @code{ 75 | Models.Person Person = new Models.Person(); 76 | 77 | private object OnTransformModel(object model) 78 | { 79 | // Confirm that the model is of the expected type 80 | // Do transformations here 81 | // return the transformed model 82 | return model; 83 | } 84 | void SubmitForm(EditContext editContext) 85 | { 86 | editContext.ValidateObjectTree(); 87 | } 88 | 89 | void DeleteAddress(Models.NamedAddress namedAddress) 90 | { 91 | Person.Addresses = Person.Addresses 92 | .Where(x => x != namedAddress) 93 | .ToList(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace FluentValidationSample.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @{ 5 | Layout = null; 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | FluentValidationSample 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | An error has occurred. This application may no longer respond until reloaded. 26 | 27 | 28 | An unhandled exception has occurred. See browser dev tools for details. 29 | 30 | Reload 31 | 🗙 32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace FluentValidationSample 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 6 | 7 |
8 |
9 | @Body 10 |
11 |
12 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  7 | 8 |
9 | 16 |
17 | 18 | @code { 19 | bool collapseNavMenu = true; 20 | 21 | string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 22 | 23 | void ToggleNavMenu() 24 | { 25 | collapseNavMenu = !collapseNavMenu; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/Startup.cs: -------------------------------------------------------------------------------- 1 | using FluentValidationSample.Models; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Morris.Blazor.Validation; 8 | 9 | namespace FluentValidationSample 10 | { 11 | public class Startup 12 | { 13 | public Startup(IConfiguration configuration) 14 | { 15 | Configuration = configuration; 16 | } 17 | 18 | public IConfiguration Configuration { get; } 19 | 20 | // This method gets called by the runtime. Use this method to add services to the container. 21 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddRazorPages(); 25 | services.AddServerSideBlazor(); 26 | services.AddFormValidation(config => config.AddFluentValidation(typeof(Person).Assembly)); 27 | } 28 | 29 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 30 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 31 | { 32 | if (env.IsDevelopment()) 33 | { 34 | app.UseDeveloperExceptionPage(); 35 | } 36 | else 37 | { 38 | app.UseExceptionHandler("/Error"); 39 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 40 | app.UseHsts(); 41 | } 42 | 43 | app.UseHttpsRedirection(); 44 | app.UseStaticFiles(); 45 | 46 | app.UseRouting(); 47 | 48 | app.UseEndpoints(endpoints => 49 | { 50 | endpoints.MapBlazorHub(); 51 | endpoints.MapFallbackToPage("/_Host"); 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.JSInterop 8 | @using FluentValidationSample 9 | @using FluentValidationSample.Shared 10 | @using FluentValidationSample.Components 11 | @using FluentValidationSample.Models 12 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft": "Warning", 7 | "Microsoft.Hosting.Lifetime": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 9 | By P.J. Onori 10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) 11 | 12 | 13 | 14 | 27 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 45 | 47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 74 | 76 | 79 | 81 | 84 | 86 | 88 | 91 | 93 | 95 | 98 | 100 | 102 | 104 | 106 | 109 | 112 | 115 | 117 | 121 | 123 | 125 | 127 | 130 | 132 | 134 | 136 | 138 | 141 | 143 | 145 | 147 | 149 | 151 | 153 | 155 | 157 | 159 | 162 | 165 | 167 | 169 | 172 | 174 | 177 | 179 | 181 | 183 | 185 | 189 | 191 | 194 | 196 | 198 | 200 | 202 | 205 | 207 | 209 | 211 | 213 | 215 | 218 | 220 | 222 | 224 | 226 | 228 | 230 | 232 | 234 | 236 | 238 | 241 | 243 | 245 | 247 | 249 | 251 | 253 | 256 | 259 | 261 | 263 | 265 | 267 | 269 | 272 | 274 | 276 | 280 | 282 | 285 | 287 | 289 | 292 | 295 | 298 | 300 | 302 | 304 | 306 | 309 | 312 | 314 | 316 | 318 | 320 | 322 | 324 | 326 | 330 | 334 | 338 | 340 | 343 | 345 | 347 | 349 | 351 | 353 | 355 | 358 | 360 | 363 | 365 | 367 | 369 | 371 | 373 | 375 | 377 | 379 | 381 | 383 | 386 | 388 | 390 | 392 | 394 | 396 | 399 | 401 | 404 | 406 | 408 | 410 | 412 | 414 | 416 | 419 | 421 | 423 | 425 | 428 | 431 | 435 | 438 | 440 | 442 | 444 | 446 | 448 | 451 | 453 | 455 | 457 | 460 | 462 | 464 | 466 | 468 | 471 | 473 | 477 | 479 | 481 | 483 | 486 | 488 | 490 | 492 | 494 | 496 | 499 | 501 | 504 | 506 | 509 | 512 | 515 | 517 | 520 | 522 | 524 | 526 | 529 | 532 | 534 | 536 | 539 | 542 | 543 | 544 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | a, .btn-link { 8 | color: #0366d6; 9 | } 10 | 11 | .btn-primary { 12 | color: #fff; 13 | background-color: #1b6ec2; 14 | border-color: #1861ac; 15 | } 16 | 17 | app { 18 | position: relative; 19 | display: flex; 20 | flex-direction: column; 21 | } 22 | 23 | .top-row { 24 | height: 3.5rem; 25 | display: flex; 26 | align-items: center; 27 | } 28 | 29 | .main { 30 | flex: 1; 31 | } 32 | 33 | .main .top-row { 34 | background-color: #f7f7f7; 35 | border-bottom: 1px solid #d6d5d5; 36 | justify-content: flex-end; 37 | } 38 | 39 | .main .top-row > a, .main .top-row .btn-link { 40 | white-space: nowrap; 41 | margin-left: 1.5rem; 42 | } 43 | 44 | .main .top-row a:first-child { 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | } 48 | 49 | .sidebar { 50 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 51 | } 52 | 53 | .sidebar .top-row { 54 | background-color: rgba(0,0,0,0.4); 55 | } 56 | 57 | .sidebar .navbar-brand { 58 | font-size: 1.1rem; 59 | } 60 | 61 | .sidebar .oi { 62 | width: 2rem; 63 | font-size: 1.1rem; 64 | vertical-align: text-top; 65 | top: -2px; 66 | } 67 | 68 | .sidebar .nav-item { 69 | font-size: 0.9rem; 70 | padding-bottom: 0.5rem; 71 | } 72 | 73 | .sidebar .nav-item:first-of-type { 74 | padding-top: 1rem; 75 | } 76 | 77 | .sidebar .nav-item:last-of-type { 78 | padding-bottom: 1rem; 79 | } 80 | 81 | .sidebar .nav-item a { 82 | color: #d7d7d7; 83 | border-radius: 4px; 84 | height: 3rem; 85 | display: flex; 86 | align-items: center; 87 | line-height: 3rem; 88 | } 89 | 90 | .sidebar .nav-item a.active { 91 | background-color: rgba(255,255,255,0.25); 92 | color: white; 93 | } 94 | 95 | .sidebar .nav-item a:hover { 96 | background-color: rgba(255,255,255,0.1); 97 | color: white; 98 | } 99 | 100 | .content { 101 | padding-top: 1.1rem; 102 | } 103 | 104 | .navbar-toggler { 105 | background-color: rgba(255, 255, 255, 0.1); 106 | } 107 | 108 | .valid.modified:not([type=checkbox]) { 109 | outline: 1px solid #26b050; 110 | } 111 | 112 | .invalid { 113 | outline: 1px solid red; 114 | } 115 | 116 | .validation-message { 117 | color: red; 118 | } 119 | 120 | #blazor-error-ui { 121 | background: lightyellow; 122 | bottom: 0; 123 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 124 | display: none; 125 | left: 0; 126 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 127 | position: fixed; 128 | width: 100%; 129 | z-index: 1000; 130 | } 131 | 132 | #blazor-error-ui .dismiss { 133 | cursor: pointer; 134 | position: absolute; 135 | right: 0.75rem; 136 | top: 0.5rem; 137 | } 138 | 139 | @media (max-width: 767.98px) { 140 | .main .top-row:not(.auth) { 141 | display: none; 142 | } 143 | 144 | .main .top-row.auth { 145 | justify-content: space-between; 146 | } 147 | 148 | .main .top-row a, .main .top-row .btn-link { 149 | margin-left: 0; 150 | } 151 | } 152 | 153 | @media (min-width: 768px) { 154 | app { 155 | flex-direction: row; 156 | } 157 | 158 | .sidebar { 159 | width: 250px; 160 | height: 100vh; 161 | position: sticky; 162 | top: 0; 163 | } 164 | 165 | .main .top-row { 166 | position: sticky; 167 | top: 0; 168 | } 169 | 170 | .main > div { 171 | padding-left: 2rem !important; 172 | padding-right: 1.5rem !important; 173 | } 174 | 175 | .navbar-toggler { 176 | display: none; 177 | } 178 | 179 | .sidebar .collapse { 180 | /* Never collapse the sidebar for wide screens */ 181 | display: block; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrpmorris/blazor-validation/1c9f1179dd3de56d49a85a1a20151a77999d5bef/Source/Samples/02-FluentValidation/FluentValidationSample/wwwroot/favicon.ico --------------------------------------------------------------------------------