├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other.md ├── renovate.json └── workflows │ ├── cicd.yml │ ├── pr.yml │ └── release.yml ├── .gitignore ├── .releaserc ├── BlazorTable.gif ├── BlazorTable.sln ├── LICENSE ├── README.md ├── global.json └── src ├── BlazorTable.Sample.Server ├── BlazorTable.Sample.Server.csproj ├── Pages │ └── _Host.cshtml ├── Program.cs ├── Properties │ └── launchSettings.json ├── 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 │ └── sample-data │ ├── MOCK_DATA.json │ └── MOCK_DATA_BAD.json ├── BlazorTable.Sample.Shared ├── App.razor ├── BlazorTable.Sample.Shared.csproj ├── Bugs │ ├── 104.razor │ ├── 114.razor │ ├── 135.razor │ ├── 146.razor │ ├── 152.razor │ └── 157.razor ├── MainLayout.razor ├── NavMenu.razor ├── Pages │ ├── AddColumn.razor │ ├── CustomSelectExample.razor │ ├── Detail.razor │ ├── DynamicColumn.razor │ ├── EditMode.razor │ ├── EmptyData.razor │ ├── Error.razor │ ├── GlobalSearch.razor │ ├── Index.razor │ ├── JObjectpg.razor │ ├── LoadingData.razor │ ├── Row.razor │ ├── RowTemplate.razor │ ├── ServerSideData.razor │ ├── TableFooter.razor │ └── ToggleColumnVisibility.razor └── _Imports.razor ├── BlazorTable.Sample.Wasm ├── BlazorTable.Sample.Wasm.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── _Imports.razor └── wwwroot │ ├── _redirects │ ├── 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 │ ├── index.html │ └── sample-data │ ├── MOCK_DATA.json │ └── MOCK_DATA_BAD.json ├── BlazorTable.Tests ├── AddNullChecks.cs ├── BlazorTable.Tests.csproj ├── BrowserTests.cs └── BrowserTestsAddress.config └── BlazorTable ├── BlazorTable.csproj ├── Components ├── AggregateType.cs ├── Align.cs ├── Column.razor ├── Column.razor.cs ├── CustomRow.razor ├── CustomRow.razor.cs ├── CustomSelectOption.razor ├── CustomSelectOption.razor.cs ├── DetailTemplate.razor ├── DetailTemplate.razor.cs ├── DynamicColumns.razor ├── DynamicColumns.razor.cs ├── EmptyDataTemplate.razor ├── EmptyDataTemplate.razor.cs ├── FilterManager.razor ├── FilterManager.razor.cs ├── LoadingDataTemplate.razor ├── LoadingDataTemplate.razor.cs ├── Pager.razor ├── Pager.razor.cs ├── Popover.razor ├── Popover.razor.cs ├── SelectionType.cs ├── ServerSide │ ├── FilterData.cs │ └── PaginationResult.cs ├── Table.razor └── Table.razor.cs ├── Filters ├── BooleanFilter.razor ├── BooleanFilter.razor.cs ├── CustomSelect.razor ├── CustomSelect.razor.cs ├── DateFilter.razor ├── DateFilter.razor.cs ├── EnumFilter.razor ├── EnumFilter.razor.cs ├── NumberFilter.razor ├── NumberFilter.razor.cs ├── StringFilter.razor └── StringFilter.razor.cs ├── GlobalSuppressions.cs ├── IServiceCollectionExtensions.cs ├── Interfaces ├── IColumn.cs ├── ICustomSelect.cs ├── IDataLoader.cs ├── IFilter.cs ├── ITable.cs └── ITableGen.cs ├── LinkerConfig.xml ├── Localization ├── Localization.Designer.cs ├── Localization.da.resx ├── Localization.de.resx ├── Localization.es.resx ├── Localization.fr.resx ├── Localization.pt-BR.resx ├── Localization.resx └── Localization.zh-CN.resx ├── LocalizedDescriptionAttribute.cs ├── Utillities.cs ├── _Imports.razor ├── icon.png └── wwwroot ├── BlazorTable.min.js └── images ├── filter.png ├── minus.png ├── plus.png ├── sort-asc.png └── sort-desc.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Please create a standalone Page which reproduces the issue and paste the code here in a code block. 15 | [More Examples](https://github.com/IvanJosipovic/BlazorTable/tree/master/src/BlazorTable.Sample.Shared/Bugs) 16 | 17 | ``` 18 | @page "/Bug152" 19 | 20 | 21 | 22 | 23 | @context.ShortId 24 | 25 |
26 | 27 | @code 28 | { 29 | private PersonData[] data; 30 | 31 | protected override void OnInitialized() 32 | { 33 | data = new PersonData[] 34 | { 35 | new PersonData() 36 | { 37 | ShortId = 5 38 | } 39 | }; 40 | } 41 | 42 | public class PersonData 43 | { 44 | public int ShortId { get; set; } 45 | } 46 | } 47 | ``` 48 | 49 | 50 | **Expected behavior** 51 | A clear and concise description of what you expected to happen. 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feat]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Any other reason 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "timezone": "America/Vancouver", 4 | "dependencyDashboard": true, 5 | "dependencyDashboardTitle": "Renovate Dashboard", 6 | "commitMessageSuffix": "", 7 | "commitBody": "", 8 | "semanticCommits": true, 9 | "suppressNotifications": ["prIgnoreNotification"], 10 | "rebaseWhen": "conflicted", 11 | "extends": [ 12 | "config:base" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - '*' 5 | - '!master' 6 | - '!alpha' 7 | - '!beta' 8 | 9 | name: CI/CD 10 | jobs: 11 | build: 12 | name: CI/CD 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | steps: 16 | 17 | - name: Checkout code 18 | uses: actions/checkout@v2 19 | 20 | - name: Setup .NET Core 21 | uses: actions/setup-dotnet@v1 22 | with: 23 | dotnet-version: 3.1.x 24 | 25 | - name: Dotnet Build 26 | run: dotnet build -c Release 27 | 28 | - name: Dotnet Publish 29 | working-directory: src/BlazorTable.Sample.Wasm 30 | run: dotnet publish -c Release 31 | 32 | - name: Deploy to Test 33 | id: netlify 34 | uses: netlify/actions/cli@master 35 | with: 36 | args: deploy --json -d src/BlazorTable.Sample.Wasm/bin/Release/netstandard2.1/publish/wwwroot 37 | env: 38 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 39 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 40 | 41 | - name: Get Test Address 42 | run: | 43 | Set-Content -Path "src/BlazorTable.Tests/BrowserTestsAddress.config" -Value "${{ steps.netlify.outputs.NETLIFY_URL }}"; 44 | shell: pwsh 45 | 46 | - name: Dotnet Test 47 | run: dotnet test -c Release -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | branches: 4 | - '*' 5 | 6 | name: Build 7 | jobs: 8 | build: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 10 12 | steps: 13 | 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup .NET Core 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 3.1.x 21 | 22 | - name: Dotnet Build 23 | run: dotnet build -c Release -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - alpha 6 | - beta 7 | 8 | name: Create Release 9 | jobs: 10 | build: 11 | name: Create Release 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 10 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup .NET Core 19 | uses: actions/setup-dotnet@v1 20 | with: 21 | dotnet-version: 3.1.x 22 | 23 | - name: Dotnet Publish 24 | working-directory: src/BlazorTable.Sample.Wasm 25 | run: dotnet publish -c Release 26 | 27 | - name: Deploy to Production 28 | if: github.ref == 'refs/heads/master' 29 | id: netlify 30 | uses: netlify/actions/cli@master 31 | with: 32 | args: deploy --prod --json -d src/BlazorTable.Sample.Wasm/bin/Release/netstandard2.1/publish/wwwroot 33 | env: 34 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 35 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 36 | 37 | - name: Set Test Address 38 | if: github.ref == 'refs/heads/master' 39 | run: | 40 | Set-Content -Path "src/BlazorTable.Tests/BrowserTestsAddress.config" -Value "${{ steps.netlify.outputs.NETLIFY_URL }}"; 41 | shell: pwsh 42 | 43 | - name: Deploy to Staging 44 | if: github.ref != 'refs/heads/master' 45 | id: netlify2 46 | uses: netlify/actions/cli@master 47 | with: 48 | args: deploy --json -d src/BlazorTable.Sample.Wasm/bin/Release/netstandard2.1/publish/wwwroot 49 | env: 50 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 51 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 52 | 53 | - name: Set Test Address 54 | if: github.ref != 'refs/heads/master' 55 | run: | 56 | Set-Content -Path "src/BlazorTable.Tests/BrowserTestsAddress.config" -Value "${{ steps.netlify2.outputs.NETLIFY_URL }}"; 57 | shell: pwsh 58 | 59 | - name: Dotnet Test 60 | run: dotnet test --configuration Release 61 | 62 | - name: Semantic Release 63 | uses: cycjimmy/semantic-release-action@v2 64 | id: semantic 65 | with: 66 | semantic_version: 17.4 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | 70 | - name: Dotnet Pack 71 | if: steps.semantic.outputs.new_release_published == 'true' 72 | working-directory: src/BlazorTable 73 | run: dotnet pack -c Release -p:Version=${{ steps.semantic.outputs.new_release_version }} 74 | 75 | - name: Dotnet Nuget Push 76 | if: steps.semantic.outputs.new_release_published == 'true' 77 | working-directory: src/BlazorTable/bin/Release 78 | run: dotnet nuget push BlazorTable.*.nupkg -s https://api.nuget.org/v3/index.json -k ${{ secrets.NUGET_API_KEY }} 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["master", {name: 'beta', prerelease: true}, {name: 'alpha', prerelease: true}], 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | "@semantic-release/github" 7 | ] 8 | } -------------------------------------------------------------------------------- /BlazorTable.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/BlazorTable.gif -------------------------------------------------------------------------------- /BlazorTable.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29411.138 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable", "src\BlazorTable\BlazorTable.csproj", "{2F9538AF-9B01-4759-A46C-40C21F45F2FA}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable.Sample.Wasm", "src\BlazorTable.Sample.Wasm\BlazorTable.Sample.Wasm.csproj", "{AC356BF1-0B40-4138-AC10-8585B5082FD2}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3CBA7388-6E6D-4D90-8C11-FE6970C465A3}" 11 | ProjectSection(SolutionItems) = preProject 12 | .github\workflows\build.yml = .github\workflows\build.yml 13 | .github\workflows\cicd.yml = .github\workflows\cicd.yml 14 | .dependabot\dependabot.yaml = .dependabot\dependabot.yaml 15 | README.md = README.md 16 | .github\workflows\release.yml = .github\workflows\release.yml 17 | EndProjectSection 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable.Sample.Server", "src\BlazorTable.Sample.Server\BlazorTable.Sample.Server.csproj", "{537D6905-461A-4180-B2F6-D64ADBBE9941}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable.Sample.Shared", "src\BlazorTable.Sample.Shared\BlazorTable.Sample.Shared.csproj", "{25E8AB75-2F08-463E-8499-0C5BBB20BC50}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable.Tests", "src\BlazorTable.Tests\BlazorTable.Tests.csproj", "{64B71D26-A307-4541-9B17-BD6709211F21}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {2F9538AF-9B01-4759-A46C-40C21F45F2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {2F9538AF-9B01-4759-A46C-40C21F45F2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {2F9538AF-9B01-4759-A46C-40C21F45F2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {2F9538AF-9B01-4759-A46C-40C21F45F2FA}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {AC356BF1-0B40-4138-AC10-8585B5082FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {AC356BF1-0B40-4138-AC10-8585B5082FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {AC356BF1-0B40-4138-AC10-8585B5082FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {AC356BF1-0B40-4138-AC10-8585B5082FD2}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {537D6905-461A-4180-B2F6-D64ADBBE9941}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {537D6905-461A-4180-B2F6-D64ADBBE9941}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {537D6905-461A-4180-B2F6-D64ADBBE9941}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {537D6905-461A-4180-B2F6-D64ADBBE9941}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {64B71D26-A307-4541-9B17-BD6709211F21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {64B71D26-A307-4541-9B17-BD6709211F21}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {64B71D26-A307-4541-9B17-BD6709211F21}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {64B71D26-A307-4541-9B17-BD6709211F21}.Release|Any CPU.Build.0 = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(ExtensibilityGlobals) = postSolution 56 | SolutionGuid = {FE5E5FAF-B880-4435-892E-12E9BBAAE487} 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Josipovic 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 | # BlazorTable 2 | [![Demo](https://img.shields.io/badge/Live-Demo-Blue?style=flat-square)](https://BlazorTable.netlify.com/) 3 | [![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/BlazorTable.svg?style=flat-square)](https://www.nuget.org/packages/BlazorTable) 4 | [![Nuget (with prereleases)](https://img.shields.io/nuget/dt/BlazorTable.svg?style=flat-square)](https://www.nuget.org/packages/BlazorTable) 5 | ![](https://github.com/IvanJosipovic/BlazorTable/workflows/CI/CD/badge.svg) 6 | 7 | Blazor Table Component with Sorting, Paging and Filtering 8 | 9 | [![Sample Gif](https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/master/BlazorTable.gif)](/BlazorTable.gif) 10 | 11 | ## Install 12 | 13 | - Add [BlazorTable Nuget](https://www.nuget.org/packages/BlazorTable) 14 | - dotnet add package BlazorTable 15 | - Add to the index.html or _Hosts.cshtml 16 | - `` 17 | - Add call to Program.cs or Startup.cs 18 | - Services.AddBlazorTable(); 19 | 20 | Note: If installing `BlazorTable` in a hosted Blazor WASM application, these steps should be performed in the [WASM Client](https://docs.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-5.0#blazor-webassembly-1) project. 21 | 22 | ## Features 23 | - Column Reordering 24 | - Edit Mode ([Template Switching](https://github.com/IvanJosipovic/BlazorTable/blob/master/src/BlazorTable.Sample.Shared/Pages/EditMode.razor)) 25 | - Client Side 26 | - Paging 27 | - Sorting 28 | - Filtering 29 | - Strings 30 | - Numbers 31 | - Dates 32 | - Enums 33 | - Custom Component 34 | ## Dependencies 35 | - Bootstrap 4 CSS 36 | 37 | ## Sample 38 | [Example Page](https://github.com/IvanJosipovic/BlazorTable/blob/master/src/BlazorTable.Sample.Shared/Pages/Index.razor) 39 | 40 | ```csharp 41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 | 51 | 52 | 53 | 56 | 57 | 58 |
59 | ``` 60 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "3.1.404", 4 | "rollForward": "latestFeature" 5 | } 6 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/BlazorTable.Sample.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace BlazorTable.SampleServer.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @{ 5 | Layout = null; 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | BlazorTable.SampleServer 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 | 38 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace BlazorTable.Sample.Server 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | CreateHostBuilder(args).Build().Run(); 19 | } 20 | 21 | public static IHostBuilder CreateHostBuilder(string[] args) => 22 | Host.CreateDefaultBuilder(args) 23 | .ConfigureWebHostDefaults(webBuilder => 24 | { 25 | webBuilder.UseStartup(); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:49484", 7 | "sslPort": 44312 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "BlazorTable.Sample.Server": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:51075/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Components; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.HttpsPolicy; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Hosting; 13 | 14 | namespace BlazorTable.Sample.Server 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | services.AddRazorPages(); 30 | services.AddServerSideBlazor(); 31 | // Server Side Blazor doesn't register HttpClient by default 32 | if (!services.Any(x => x.ServiceType == typeof(HttpClient))) 33 | { 34 | // Setup HttpClient for server side in a client side compatible fashion 35 | services.AddScoped(s => 36 | { 37 | // Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it. 38 | var uriHelper = s.GetRequiredService(); 39 | return new HttpClient 40 | { 41 | BaseAddress = new Uri(uriHelper.BaseUri) 42 | }; 43 | }); 44 | } 45 | services.AddBlazorTable(); 46 | } 47 | 48 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 49 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 50 | { 51 | if (env.IsDevelopment()) 52 | { 53 | app.UseDeveloperExceptionPage(); 54 | } 55 | else 56 | { 57 | app.UseExceptionHandler("/Error"); 58 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 59 | app.UseHsts(); 60 | } 61 | 62 | app.UseHttpsRedirection(); 63 | app.UseStaticFiles(); 64 | 65 | app.UseRouting(); 66 | 67 | app.UseEndpoints(endpoints => 68 | { 69 | endpoints.MapBlazorHub(); 70 | endpoints.MapFallbackToPage("/_Host"); 71 | }); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/_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 BlazorTable.Sample.Shared 9 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/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 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/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 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/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. -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/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 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Server/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/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 | overflow: auto; 52 | } 53 | 54 | .sidebar .top-row { 55 | background-color: rgba(0,0,0,0.4); 56 | } 57 | 58 | .sidebar .navbar-brand { 59 | font-size: 1.1rem; 60 | } 61 | 62 | .sidebar .oi { 63 | width: 2rem; 64 | font-size: 1.1rem; 65 | vertical-align: text-top; 66 | top: -2px; 67 | } 68 | 69 | .sidebar .nav-item { 70 | font-size: 0.9rem; 71 | padding-bottom: 0.5rem; 72 | } 73 | 74 | .sidebar .nav-item:first-of-type { 75 | padding-top: 1rem; 76 | } 77 | 78 | .sidebar .nav-item:last-of-type { 79 | padding-bottom: 1rem; 80 | } 81 | 82 | .sidebar .nav-item a { 83 | color: #d7d7d7; 84 | border-radius: 4px; 85 | height: 3rem; 86 | display: flex; 87 | align-items: center; 88 | line-height: 3rem; 89 | } 90 | 91 | .sidebar .nav-item a.active { 92 | background-color: rgba(255,255,255,0.25); 93 | color: white; 94 | } 95 | 96 | .sidebar .nav-item a:hover { 97 | background-color: rgba(255,255,255,0.1); 98 | color: white; 99 | } 100 | 101 | .content { 102 | padding-top: 1.1rem; 103 | } 104 | 105 | .navbar-toggler { 106 | background-color: rgba(255, 255, 255, 0.1); 107 | } 108 | 109 | .valid.modified:not([type=checkbox]) { 110 | outline: 1px solid #26b050; 111 | } 112 | 113 | .invalid { 114 | outline: 1px solid red; 115 | } 116 | 117 | .validation-message { 118 | color: red; 119 | } 120 | 121 | #blazor-error-ui { 122 | background: lightyellow; 123 | bottom: 0; 124 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 125 | display: none; 126 | left: 0; 127 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 128 | position: fixed; 129 | width: 100%; 130 | z-index: 1000; 131 | } 132 | 133 | #blazor-error-ui .dismiss { 134 | cursor: pointer; 135 | position: absolute; 136 | right: 0.75rem; 137 | top: 0.5rem; 138 | } 139 | 140 | @media (max-width: 767.98px) { 141 | .main .top-row:not(.auth) { 142 | display: none; 143 | } 144 | 145 | .main .top-row.auth { 146 | justify-content: space-between; 147 | } 148 | 149 | .main .top-row a, .main .top-row .btn-link { 150 | margin-left: 0; 151 | } 152 | } 153 | 154 | @media (min-width: 768px) { 155 | app { 156 | flex-direction: row; 157 | } 158 | 159 | .sidebar { 160 | width: 250px; 161 | height: 100vh; 162 | position: sticky; 163 | top: 0; 164 | } 165 | 166 | .main .top-row { 167 | position: sticky; 168 | top: 0; 169 | } 170 | 171 | .main > div { 172 | padding-left: 2rem !important; 173 | padding-right: 1.5rem !important; 174 | } 175 | 176 | .navbar-toggler { 177 | display: none; 178 | } 179 | 180 | .sidebar .collapse { 181 | /* Never collapse the sidebar for wide screens */ 182 | display: block; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Server/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Server/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/BlazorTable.Sample.Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Bugs/104.razor: -------------------------------------------------------------------------------- 1 | @page "/104" 2 | 3 | @using BlazorTable 4 | 5 |

Issue #104

6 | 7 | 8 | 9 | 19 | 20 |
21 | 22 | @code 23 | { 24 | private List items = new List(); 25 | 26 | protected override void OnInitialized() 27 | { 28 | for (int i = 0; i < 5; i++) 29 | { 30 | items.Add(new Parent() { Name = i.ToString(), Child = new Child() { Name = i.ToString() } }); 31 | items.Add(new Parent() { Name = i.ToString() }); 32 | } 33 | } 34 | 35 | private class Parent 36 | { 37 | public string Name { get; set; } 38 | 39 | public Child Child { get; set; } 40 | } 41 | 42 | private class Child 43 | { 44 | public string Name { get; set; } 45 | 46 | public GrandChild GrandChild { get; set; } 47 | } 48 | 49 | private class GrandChild 50 | { 51 | public string Name { get; set; } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Bugs/114.razor: -------------------------------------------------------------------------------- 1 | @page "/114" 2 | 3 | @using BlazorTable 4 | 5 | 6 | 7 | 8 |
9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | @code 22 | { 23 | [Inject] 24 | private HttpClient Http { get; set; } 25 | 26 | private PersonData[] data; 27 | 28 | protected override async Task OnInitializedAsync() 29 | { 30 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 31 | } 32 | 33 | public class PersonData 34 | { 35 | public int? id { get; set; } 36 | public string full_name { get; set; } 37 | public string email { get; set; } 38 | public bool? paid { get; set; } 39 | public decimal? price { get; set; } 40 | public CreditCard? cc_type { get; set; } 41 | public DateTime? created_date { get; set; } 42 | } 43 | 44 | public enum CreditCard 45 | { 46 | none = 0, 47 | [Description("MasterCard")] 48 | MasterCard = 1, 49 | Visa = 2 50 | } 51 | 52 | private void HandleClick() 53 | { 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Bugs/135.razor: -------------------------------------------------------------------------------- 1 | @page "/135" 2 | 3 | @using BlazorTable 4 | 5 | 6 | 7 | 8 | 11 | 12 |
13 | 14 | @code 15 | { 16 | private PersonData[] data; 17 | 18 | protected override void OnInitialized() 19 | { 20 | data = new PersonData[] 21 | { 22 | new PersonData() 23 | { 24 | ShortId = 5 25 | } 26 | }; 27 | } 28 | 29 | public class PersonData 30 | { 31 | public int ShortId { get; set; } 32 | } 33 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Bugs/146.razor: -------------------------------------------------------------------------------- 1 | @page "/146" 2 | 3 | @using BlazorTable 4 | 5 | 6 | 7 | 10 | 11 | 12 | 15 | 16 | 17 |
18 | 19 | @code 20 | { 21 | private Root[] data; 22 | 23 | protected override void OnInitialized() 24 | { 25 | data = new Root[] 26 | { 27 | new Root() 28 | { 29 | Child = new Child(){ Data = "test1" } 30 | }, 31 | new Root() 32 | { 33 | Child = new Child(){ Data = "test2" } 34 | }, 35 | new Root() 36 | { 37 | Child = new Child(){ Data = "test3" } 38 | }, 39 | new Root() 40 | { 41 | Child = new Child() 42 | }, 43 | new Root() 44 | { 45 | Child = null 46 | } 47 | }; 48 | } 49 | 50 | public class Root 51 | { 52 | public Child Child { get; set; } 53 | } 54 | 55 | public class Child 56 | { 57 | public string Data { get; set; } 58 | } 59 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Bugs/152.razor: -------------------------------------------------------------------------------- 1 | @page "/152" 2 | 3 | @using BlazorTable 4 | 5 | 6 | 7 | 8 | @context.ShortId 9 | 10 |
11 | 12 | @code 13 | { 14 | private PersonData[] data; 15 | 16 | protected override void OnInitialized() 17 | { 18 | data = new PersonData[] 19 | { 20 | new PersonData() 21 | { 22 | ShortId = 5 23 | } 24 | }; 25 | } 26 | 27 | public class PersonData 28 | { 29 | public int ShortId { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Bugs/157.razor: -------------------------------------------------------------------------------- 1 | @page "/157" 2 | 3 | @using BlazorTable 4 | 5 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | @code { 14 | private List data; 15 | 16 | protected override void OnInitialized() 17 | { 18 | data = new List() 19 | { 20 | new MyData() 21 | { 22 | MyString = "One", 23 | MyInt = 1, 24 | MyEnum = MyEnum.One 25 | }, 26 | new MyData() 27 | { 28 | MyString = "Two", 29 | MyInt = 2, 30 | MyEnum = MyEnum.Two 31 | }, 32 | new MyData() 33 | { 34 | MyString = "Three", 35 | MyInt = 3, 36 | MyEnum = MyEnum.Three 37 | } 38 | }; 39 | 40 | 41 | base.OnInitialized(); 42 | } 43 | public enum MyEnum 44 | { 45 | One, 46 | Two, 47 | Three 48 | } 49 | 50 | public class MyData 51 | { 52 | public string MyString { get; set; } 53 | public int MyInt { get; set; } 54 | public MyEnum MyEnum { get; set; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  7 | 8 |
9 | 86 |
87 | 88 | @code { 89 | private bool collapseNavMenu = true; 90 | 91 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 92 | 93 | private void ToggleNavMenu() 94 | { 95 | collapseNavMenu = !collapseNavMenu; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/AddColumn.razor: -------------------------------------------------------------------------------- 1 | @page "/AddColumn" 2 | @inject HttpClient Http 3 | @using BlazorTable 4 | @using System.ComponentModel 5 | 6 |

Add Column Programmatically

7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 |
15 | 16 | @code 17 | { 18 | private ITable Table; 19 | 20 | private PersonData[] data; 21 | 22 | protected override async Task OnInitializedAsync() 23 | { 24 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 25 | } 26 | 27 | public class PersonData 28 | { 29 | public int? id { get; set; } 30 | public string full_name { get; set; } 31 | public string email { get; set; } 32 | public bool? paid { get; set; } 33 | public decimal? price { get; set; } 34 | public CreditCard? cc_type { get; set; } 35 | public DateTime? created_date { get; set; } 36 | } 37 | 38 | public enum CreditCard 39 | { 40 | none = 0, 41 | [Description("MasterCard")] 42 | MasterCard = 1, 43 | Visa = 2 44 | } 45 | 46 | private void Add() 47 | { 48 | var col = new Column() 49 | { 50 | Title = "Enum", 51 | Field = (x) => x.cc_type, 52 | Sortable = true, 53 | Filterable = true, 54 | Width = "10%" 55 | }; 56 | 57 | Table.AddColumn(col); 58 | } 59 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/CustomSelectExample.razor: -------------------------------------------------------------------------------- 1 | @page "/CustomSelect" 2 | 3 | @using BlazorTable 4 | 5 |

CustomSelect

6 | 7 |

The CustomSelect can be used to display custom filter options.

8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | @code 24 | { 25 | [Inject] 26 | private HttpClient Http { get; set; } 27 | 28 | private PersonData[] data; 29 | 30 | protected override async Task OnInitializedAsync() 31 | { 32 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 33 | } 34 | 35 | public class PersonData 36 | { 37 | public int? id { get; set; } 38 | public string full_name { get; set; } 39 | public string email { get; set; } 40 | public bool? paid { get; set; } 41 | public decimal? price { get; set; } 42 | public CreditCard? cc_type { get; set; } 43 | public DateTime? created_date { get; set; } 44 | } 45 | 46 | public enum CreditCard 47 | { 48 | none = 0, 49 | [Description("MasterCard")] 50 | MasterCard = 1, 51 | Visa = 2 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/Detail.razor: -------------------------------------------------------------------------------- 1 | @page "/Detail" 2 | 3 | @using BlazorTable 4 | 5 |

DetailTemplate

6 | 7 |

The DetailTemplate can be used to display additional details below an item.

8 |
9 |
10 |
11 | 12 | 13 |
14 |
15 |
16 |
Row Number:
17 |
18 | 19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 | @if (errorMessage != "") 27 | { 28 | 29 | } 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 53 | 54 | 55 | Name @context.full_name 56 |
57 | Email @context.email 58 |
59 | Created Date@context.created_date 60 |
61 |
62 | 63 |
64 | 65 | @code 66 | { 67 | [Inject] 68 | private HttpClient Http { get; set; } 69 | 70 | private ITable table; 71 | 72 | private PersonData[] data; 73 | 74 | private int rowNumber; 75 | 76 | private string errorMessage = ""; 77 | 78 | protected override async Task OnInitializedAsync() 79 | { 80 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 81 | } 82 | 83 | public class PersonData 84 | { 85 | public int? id { get; set; } 86 | public string full_name { get; set; } 87 | public string email { get; set; } 88 | public bool? paid { get; set; } 89 | public decimal? price { get; set; } 90 | public CreditCard? cc_type { get; set; } 91 | public DateTime? created_date { get; set; } 92 | } 93 | 94 | public enum CreditCard 95 | { 96 | none = 0, 97 | [Description("MasterCard")] 98 | MasterCard = 1, 99 | Visa = 2 100 | } 101 | 102 | private void Try(Action action) 103 | { 104 | try 105 | { 106 | errorMessage = ""; 107 | action(); 108 | } 109 | catch (Exception e) 110 | { 111 | errorMessage = e.Message; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/DynamicColumn.razor: -------------------------------------------------------------------------------- 1 | @page "/DynamicColumns" 2 | 3 |

Dynamic Columns

4 | 5 | 6 | 7 | 8 |
9 | 10 | @code 11 | { 12 | [Inject] private HttpClient Http { get; set; } 13 | 14 | private PersonData[] data; 15 | 16 | protected override async Task OnInitializedAsync() 17 | { 18 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 19 | } 20 | 21 | public class PersonData 22 | { 23 | public int? id { get; set; } 24 | public string full_name { get; set; } 25 | public string email { get; set; } 26 | public bool? paid { get; set; } 27 | public decimal? price { get; set; } 28 | public CreditCard? cc_type { get; set; } 29 | public DateTime? created_date { get; set; } 30 | //public SubData SubData {get;set;} 31 | } 32 | 33 | public class SubData 34 | { 35 | public string test { get; set; } 36 | } 37 | 38 | public enum CreditCard 39 | { 40 | none = 0, 41 | [Description("MasterCard")] 42 | MasterCard = 1, 43 | Visa = 2 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/EditMode.razor: -------------------------------------------------------------------------------- 1 | @page "/EditMode" 2 | 3 | @using BlazorTable 4 | 5 |

Edit Mode

6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 |
52 | 53 | @code 54 | { 55 | [Inject] 56 | private HttpClient Http { get; set; } 57 | 58 | private ITable Table; 59 | 60 | private PersonData[] data; 61 | 62 | protected override async Task OnInitializedAsync() 63 | { 64 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 65 | } 66 | 67 | public class PersonData 68 | { 69 | public int? id { get; set; } 70 | public string full_name { get; set; } 71 | public string email { get; set; } 72 | public bool? paid { get; set; } 73 | public decimal? price { get; set; } 74 | public CreditCard? cc_type { get; set; } 75 | public DateTime? created_date { get; set; } 76 | } 77 | 78 | public enum CreditCard 79 | { 80 | none = 0, 81 | [Description("MasterCard")] 82 | MasterCard = 1, 83 | Visa = 2 84 | } 85 | 86 | private void ToggleEdit() 87 | { 88 | Table.ToggleEditMode(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/EmptyData.razor: -------------------------------------------------------------------------------- 1 | @page "/EmptyData" 2 | @inject HttpClient Http 3 | @using BlazorTable 4 | 5 |

EmptyDataTemplate

6 | 7 |

The EmptyDataTemplate can be used to display custom text when there are no rows to display.

8 |
9 | 10 | 11 | 12 | 13 |
14 | No rows found! 15 |
16 |
17 | 18 |
19 | 20 | @code 21 | { 22 | private ITable Table; 23 | 24 | private SampleData[] data; 25 | 26 | protected override async Task OnInitializedAsync() 27 | { 28 | await Task.Delay(250); 29 | data = Array.Empty(); 30 | } 31 | 32 | public class SampleData 33 | { 34 | public int? id { get; set; } 35 | public string full_name { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/error" 2 | 3 | 4 |

Error.

5 |

An error occurred while processing your request.

-------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/GlobalSearch.razor: -------------------------------------------------------------------------------- 1 | @page "/GlobalSearch" 2 | 3 | @using BlazorTable 4 | 5 |

Global Search

6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 30 | 31 | 32 |
33 | 34 | @code 35 | { 36 | [Inject] 37 | private HttpClient Http { get; set; } 38 | 39 | private PersonData[] data; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 44 | } 45 | 46 | public class PersonData 47 | { 48 | public int? id { get; set; } 49 | public string full_name { get; set; } 50 | public string email { get; set; } 51 | public bool? paid { get; set; } 52 | public decimal? price { get; set; } 53 | public CreditCard? cc_type { get; set; } 54 | public DateTime? created_date { get; set; } 55 | } 56 | 57 | public enum CreditCard 58 | { 59 | none = 0, 60 | [Description("MasterCard")] 61 | MasterCard = 1, 62 | Visa = 2 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | @using BlazorTable 4 | 5 |

BlazorTable

6 | 7 | GitHub stars 8 |   9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 62 | 63 | 64 |
65 | 66 | 67 | 68 | 69 | @code 70 | { 71 | [Inject] 72 | private HttpClient Http { get; set; } 73 | 74 | private PersonData[] data; 75 | 76 | protected override async Task OnInitializedAsync() 77 | { 78 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 79 | } 80 | 81 | public class PersonData 82 | { 83 | public int? id { get; set; } 84 | public string full_name { get; set; } 85 | public string email { get; set; } 86 | public bool? paid { get; set; } 87 | public decimal? price { get; set; } 88 | public CreditCard? cc_type { get; set; } 89 | public DateTime? created_date { get; set; } 90 | 91 | /// 92 | /// Returns row CSS if price over 800 93 | /// 94 | public string RowClass => price.GetValueOrDefault(0) > 800 ? "table-danger" : null; 95 | } 96 | 97 | public enum CreditCard 98 | { 99 | none = 0, 100 | [Description("MasterCard")] 101 | MasterCard = 1, 102 | Visa = 2 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/JObjectpg.razor: -------------------------------------------------------------------------------- 1 | @page "/JObject" 2 | @using BlazorTable 3 | @using Newtonsoft.Json.Linq 4 | 5 |

JObject

6 | 7 |
8 | 9 | ())" Type="@(typeof(string))" Sortable="true" Filterable="true" DefaultSortColumn="true" /> 10 | ())" Type="@(typeof(int))" Sortable="true" Filterable="true" /> 11 | ().Property("Name").Value.Value())" Type="@(typeof(string))" Sortable="true" Filterable="true" /> 12 | 13 |
14 | 15 | @code 16 | { 17 | private List data = new List(); 18 | 19 | protected override void OnInitialized() 20 | { 21 | var row1 = new JObject(); 22 | row1["Column1"] = "B"; 23 | row1["Column2"] = 2; 24 | row1["Column3"] = new JObject(); 25 | row1["Column3"]["Name"] = "D"; 26 | data.Add(row1); 27 | 28 | var row2 = new JObject(); 29 | row2["Column1"] = "B"; 30 | row2["Column2"] = 1; 31 | row2["Column3"] = new JObject(); 32 | row2["Column3"]["Name"] = "D"; 33 | data.Add(row2); 34 | 35 | var row3 = new JObject(); 36 | row3["Column1"] = "A"; 37 | row3["Column2"] = 2; 38 | row3["Column3"] = new JObject(); 39 | row3["Column3"]["Name"] = "C"; 40 | data.Add(row3); 41 | 42 | var row4 = new JObject(); 43 | row4["Column1"] = "A"; 44 | row4["Column2"] = 1; 45 | row4["Column3"] = new JObject(); 46 | row4["Column3"]["Name"] = "C"; 47 | data.Add(row4); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/LoadingData.razor: -------------------------------------------------------------------------------- 1 | @page "/LoadingData" 2 | @inject HttpClient Http 3 | @using BlazorTable 4 | @using System.ComponentModel 5 | 6 |

LoadingDataTemplate

7 | 8 |

The LoadingDataTemplate can be used to display custom text when the rows are null or loading.

9 |
10 | 11 | 12 | 13 | 14 |
15 |

Custom Loading Template

16 |
17 |
18 | 19 |
20 | 21 | @code 22 | { 23 | private ITable Table; 24 | 25 | private SampleData[] data; 26 | 27 | protected override async Task OnInitializedAsync() 28 | { 29 | await Task.Delay(2000); 30 | data = Array.Empty(); 31 | } 32 | 33 | public class SampleData 34 | { 35 | public int? id { get; set; } 36 | public string full_name { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/Row.razor: -------------------------------------------------------------------------------- 1 | @page "/Row" 2 | 3 | @using BlazorTable 4 | 5 |

Row

6 | 7 | Selection Type: 8 | 14 | 15 | Last Clicked: @selected?.full_name 16 |
17 | Selected: @(selectedItems.Any() ? selectedItems.Select(x => x.full_name).Aggregate((c, n) => $"{c},{n}") : "None") 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 |
37 | 38 | @code 39 | { 40 | [Inject] 41 | private HttpClient Http { get; set; } 42 | 43 | private PersonData[] data; 44 | 45 | private SelectionType selectionType; 46 | 47 | private PersonData selected; 48 | 49 | private List selectedItems = new List(); 50 | 51 | protected override async Task OnInitializedAsync() 52 | { 53 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 54 | } 55 | 56 | public class PersonData 57 | { 58 | public int? id { get; set; } 59 | public string full_name { get; set; } 60 | public string email { get; set; } 61 | public bool? paid { get; set; } 62 | public decimal? price { get; set; } 63 | public CreditCard? cc_type { get; set; } 64 | public DateTime? created_date { get; set; } 65 | } 66 | 67 | public enum CreditCard 68 | { 69 | none = 0, 70 | [Description("MasterCard")] 71 | MasterCard = 1, 72 | Visa = 2 73 | } 74 | 75 | public void RowClick(PersonData data) 76 | { 77 | selected = data; 78 | StateHasChanged(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/RowTemplate.razor: -------------------------------------------------------------------------------- 1 | @page "/CustomRow" 2 | @inject HttpClient Http 3 | @using BlazorTable 4 | @using System.ComponentModel 5 | 6 |

Row Template - Custom Row

7 | 8 |

9 | 10 |

Use

11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 |
39 |
Custom Row
40 |
45 | 46 | @code 47 | { 48 | [Inject] 49 | private HttpClient httpClient { get; set; } 50 | 51 | private ITable table; 52 | 53 | private PersonData[] data; 54 | 55 | private int i = 0; 56 | 57 | protected override async Task OnInitializedAsync() 58 | { 59 | data = await httpClient.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 60 | } 61 | 62 | public class PersonData 63 | { 64 | public int? id { get; set; } 65 | public string full_name { get; set; } 66 | public string email { get; set; } 67 | public bool? paid { get; set; } 68 | public decimal? price { get; set; } 69 | public CreditCard? cc_type { get; set; } 70 | public DateTime? created_date { get; set; } 71 | } 72 | 73 | public enum CreditCard 74 | { 75 | none = 0, 76 | [Description("MasterCard")] 77 | MasterCard = 1, 78 | Visa = 2 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/ServerSideData.razor: -------------------------------------------------------------------------------- 1 | @page "/ServerSideData" 2 | @inject HttpClient Http 3 | @using BlazorTable 4 | @using BlazorTable.Interfaces 5 | @using BlazorTable.Components.ServerSide 6 | 7 |

Server side data

8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 27 | 28 | 29 |
30 | 31 | @code 32 | { 33 | [Inject] 34 | private HttpClient httpClient { get; set; } 35 | 36 | public class PersonData 37 | { 38 | public int? id { get; set; } 39 | public string full_name { get; set; } 40 | public string email { get; set; } 41 | public bool? paid { get; set; } 42 | public decimal? price { get; set; } 43 | public DateTime? created_date { get; set; } 44 | } 45 | 46 | private IDataLoader _loader; 47 | 48 | private IEnumerable data; 49 | 50 | protected override async Task OnInitializedAsync() 51 | { 52 | _loader = new PersonDataLoader(httpClient); 53 | data = (await _loader.LoadDataAsync(null)).Records; 54 | } 55 | 56 | public class PersonDataLoader : IDataLoader 57 | { 58 | private readonly HttpClient _client; 59 | public PersonDataLoader(HttpClient client) 60 | { 61 | _client = client; 62 | } 63 | public async Task> LoadDataAsync(FilterData parameters) 64 | { 65 | 66 | var data = await _client.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 67 | IQueryable query = data.AsQueryable(); 68 | if (parameters?.Query != null) 69 | { 70 | query = query.Where( 71 | x => x.email.ToLowerInvariant().Contains(parameters.Query.ToLowerInvariant()) || 72 | x.full_name.ToLowerInvariant().Contains(parameters.Query.ToLowerInvariant())); 73 | } 74 | if (parameters?.OrderBy != null) 75 | { 76 | var orderBy = parameters.OrderBy.Split(" "); 77 | if (orderBy.Length == 2) 78 | { 79 | var isSortDescending = orderBy[1] == "desc"; 80 | var prop = typeof(PersonData).GetProperty(orderBy[0]); 81 | query = isSortDescending ? query.OrderByDescending(x => prop.GetValue(x, null)) 82 | : query.OrderBy(x => prop.GetValue(x, null)); 83 | } 84 | } 85 | var results = parameters?.Top.HasValue ?? false ? 86 | query.Skip(parameters.Skip.GetValueOrDefault()) 87 | .Take(parameters.Top.Value).ToArray() : 88 | query.ToArray(); 89 | 90 | return new PaginationResult 91 | { 92 | Records = results, 93 | Skip = parameters?.Skip ?? 0, 94 | Total = query.ToList().Count, 95 | Top = parameters?.Top ?? 0 96 | }; 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/TableFooter.razor: -------------------------------------------------------------------------------- 1 | @page "/TableFooter" 2 | 3 | @using BlazorTable 4 | 5 |

Table Footer

6 | 7 |

This example is to demonstrate the table footer feature. There are 5 aggregation types built in: Sum, Average, Count, Min, and Max. You have ability to set any string value into the footer by using SetFooterValue attribute for the Column tag if necessary.

8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 |
33 | 34 | @code 35 | { 36 | [Inject] 37 | private HttpClient Http { get; set; } 38 | 39 | private PersonData[] data; 40 | 41 | public string TotalFooterText = "Totals"; 42 | 43 | protected override async Task OnInitializedAsync() 44 | { 45 | data = await Http.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 46 | } 47 | 48 | public class PersonData 49 | { 50 | public int? id { get; set; } 51 | public string full_name { get; set; } 52 | public string email { get; set; } 53 | public bool? paid { get; set; } 54 | public decimal? price { get; set; } 55 | public CreditCard? cc_type { get; set; } 56 | public DateTime? created_date { get; set; } 57 | } 58 | 59 | public enum CreditCard 60 | { 61 | none = 0, 62 | [Description("MasterCard")] 63 | MasterCard = 1, 64 | Visa = 2 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/Pages/ToggleColumnVisibility.razor: -------------------------------------------------------------------------------- 1 | @page "/ToggleColumnVisibility" 2 | @inject HttpClient Http 3 | @using BlazorTable 4 | @using System.ComponentModel 5 | 6 |

Toggle Column Visibility

7 | 8 |

Users can change the visibility of a column if its "Hideable" attribute is set to true.

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 35 | 36 | 37 |
38 | 39 | @code 40 | { 41 | [Inject] 42 | private HttpClient httpClient { get; set; } 43 | 44 | private ITable table; 45 | 46 | private PersonData[] data; 47 | 48 | private bool showSearchBar; 49 | 50 | protected override async Task OnInitializedAsync() 51 | { 52 | data = await httpClient.GetFromJsonAsync("sample-data/MOCK_DATA.json"); 53 | 54 | Random random = new Random(123); 55 | 56 | foreach (IColumn column in table.Columns) 57 | { 58 | column.Visible = random.Next(2) == 1 ? true : false; // column visibility 59 | } 60 | } 61 | 62 | public class PersonData 63 | { 64 | public int? id { get; set; } 65 | public string full_name { get; set; } 66 | public string email { get; set; } 67 | public bool? paid { get; set; } 68 | public decimal? price { get; set; } 69 | public CreditCard? cc_type { get; set; } 70 | public DateTime? created_date { get; set; } 71 | } 72 | 73 | public enum CreditCard 74 | { 75 | none = 0, 76 | [Description("MasterCard")] 77 | MasterCard = 1, 78 | Visa = 2 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Shared/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using BlazorTable.Sample.Shared 2 | @using BlazorTable.Sample.Shared.Pages 3 | @using System.Net.Http 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.JSInterop 8 | @using System.ComponentModel 9 | @using System.Net.Http.Json -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/BlazorTable.Sample.Wasm.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorTable.Sample.Shared; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System.Net.Http; 6 | using System; 7 | 8 | namespace BlazorTable.Sample.Wasm 9 | { 10 | public class Program 11 | { 12 | public static async Task Main(string[] args) 13 | { 14 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 15 | 16 | builder.RootComponents.Add("app"); 17 | 18 | builder.Services.AddSingleton(new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 19 | builder.Services.AddBlazorTable(); 20 | 21 | await builder.Build().RunAsync(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:51076/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "BlazorTable.Sample.Wasm": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "applicationUrl": "http://localhost:51076/" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/_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 BlazorTable.Sample.Shared -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/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 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/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. -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/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 | -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Wasm/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/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 | overflow: auto; 52 | } 53 | 54 | .sidebar .top-row { 55 | background-color: rgba(0,0,0,0.4); 56 | } 57 | 58 | .sidebar .navbar-brand { 59 | font-size: 1.1rem; 60 | } 61 | 62 | .sidebar .oi { 63 | width: 2rem; 64 | font-size: 1.1rem; 65 | vertical-align: text-top; 66 | top: -2px; 67 | } 68 | 69 | .sidebar .nav-item { 70 | font-size: 0.9rem; 71 | padding-bottom: 0.5rem; 72 | } 73 | 74 | .sidebar .nav-item:first-of-type { 75 | padding-top: 1rem; 76 | } 77 | 78 | .sidebar .nav-item:last-of-type { 79 | padding-bottom: 1rem; 80 | } 81 | 82 | .sidebar .nav-item a { 83 | color: #d7d7d7; 84 | border-radius: 4px; 85 | height: 3rem; 86 | display: flex; 87 | align-items: center; 88 | line-height: 3rem; 89 | } 90 | 91 | .sidebar .nav-item a.active { 92 | background-color: rgba(255,255,255,0.25); 93 | color: white; 94 | } 95 | 96 | .sidebar .nav-item a:hover { 97 | background-color: rgba(255,255,255,0.1); 98 | color: white; 99 | } 100 | 101 | .content { 102 | padding-top: 1.1rem; 103 | } 104 | 105 | .navbar-toggler { 106 | background-color: rgba(255, 255, 255, 0.1); 107 | } 108 | 109 | .valid.modified:not([type=checkbox]) { 110 | outline: 1px solid #26b050; 111 | } 112 | 113 | .invalid { 114 | outline: 1px solid red; 115 | } 116 | 117 | .validation-message { 118 | color: red; 119 | } 120 | 121 | #blazor-error-ui { 122 | background: lightyellow; 123 | bottom: 0; 124 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 125 | display: none; 126 | left: 0; 127 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 128 | position: fixed; 129 | width: 100%; 130 | z-index: 1000; 131 | } 132 | 133 | #blazor-error-ui .dismiss { 134 | cursor: pointer; 135 | position: absolute; 136 | right: 0.75rem; 137 | top: 0.5rem; 138 | } 139 | 140 | @media (max-width: 767.98px) { 141 | .main .top-row:not(.auth) { 142 | display: none; 143 | } 144 | 145 | .main .top-row.auth { 146 | justify-content: space-between; 147 | } 148 | 149 | .main .top-row a, .main .top-row .btn-link { 150 | margin-left: 0; 151 | } 152 | } 153 | 154 | @media (min-width: 768px) { 155 | app { 156 | flex-direction: row; 157 | } 158 | 159 | .sidebar { 160 | width: 250px; 161 | height: 100vh; 162 | position: sticky; 163 | top: 0; 164 | } 165 | 166 | .main .top-row { 167 | position: sticky; 168 | top: 0; 169 | } 170 | 171 | .main > div { 172 | padding-left: 2rem !important; 173 | padding-right: 1.5rem !important; 174 | } 175 | 176 | .navbar-toggler { 177 | display: none; 178 | } 179 | 180 | .sidebar .collapse { 181 | /* Never collapse the sidebar for wide screens */ 182 | display: block; 183 | } 184 | } 185 | 186 | .spinner { 187 | margin: 100px auto; 188 | width: 50px; 189 | height: 40px; 190 | text-align: center; 191 | font-size: 10px; 192 | } 193 | 194 | .spinner > div { 195 | background-color: #fff; 196 | height: 100%; 197 | width: 6px; 198 | display: inline-block; 199 | -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; 200 | animation: sk-stretchdelay 1.2s infinite ease-in-out; 201 | } 202 | 203 | .spinner .rect2 { 204 | -webkit-animation-delay: -1.1s; 205 | animation-delay: -1.1s; 206 | } 207 | 208 | .spinner .rect3 { 209 | -webkit-animation-delay: -1.0s; 210 | animation-delay: -1.0s; 211 | } 212 | 213 | .spinner .rect4 { 214 | -webkit-animation-delay: -0.9s; 215 | animation-delay: -0.9s; 216 | } 217 | 218 | .spinner .rect5 { 219 | -webkit-animation-delay: -0.8s; 220 | animation-delay: -0.8s; 221 | } 222 | 223 | @-webkit-keyframes sk-stretchdelay { 224 | 0%, 40%, 100% { 225 | -webkit-transform: scaleY(0.4) 226 | } 227 | 228 | 20% { 229 | -webkit-transform: scaleY(1.0) 230 | } 231 | } 232 | 233 | @keyframes sk-stretchdelay { 234 | 0%, 40%, 100% { 235 | transform: scaleY(0.4); 236 | -webkit-transform: scaleY(0.4); 237 | } 238 | 239 | 20% { 240 | transform: scaleY(1.0); 241 | -webkit-transform: scaleY(1.0); 242 | } 243 | } 244 | 245 | .modal-overlay { 246 | position: fixed; 247 | top: 0; 248 | bottom: 0; 249 | left: 0; 250 | right: 0; 251 | background-color: rgba(28,28,28); 252 | z-index: 2000; 253 | display: flex; 254 | align-items: center; 255 | justify-content: center; 256 | flex-direction: column; 257 | --sk-color: white; 258 | } -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable.Sample.Wasm/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/BlazorTable.Sample.Wasm/wwwroot/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | BlazorTable.Sample 7 | 8 | 9 | 10 | 11 | 12 | 13 | 22 | 23 | 24 |
25 | An unhandled error has occurred. 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/BlazorTable.Tests/AddNullChecks.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using Xunit; 7 | 8 | namespace BlazorTable.Tests 9 | { 10 | public class AddNullChecks 11 | { 12 | [Fact] 13 | public void AddToSingle() 14 | { 15 | Expression> Field = x => x.Child; 16 | var exp = Field.Body.CreateNullChecks(); 17 | exp.ToString().ShouldBe("(x.Child != null)"); 18 | } 19 | 20 | [Fact] 21 | public void AddToMulti() 22 | { 23 | Expression> Field = x => x.Child.Name; 24 | var exp = Field.Body.CreateNullChecks(); 25 | exp.ToString().ShouldBe("((x.Child != null) AndAlso (x.Child.Name != null))"); 26 | } 27 | 28 | [Fact] 29 | public void AddToMulti2() 30 | { 31 | Expression> Field = x => x.Child.GrandChild.Name; 32 | var exp = Field.Body.CreateNullChecks(); 33 | exp.ToString().ShouldBe("(((x.Child != null) AndAlso (x.Child.GrandChild != null)) AndAlso (x.Child.GrandChild.Name != null))"); 34 | } 35 | 36 | [Fact] 37 | public void Skip() 38 | { 39 | Expression> Field = x => x.Child; 40 | var exp = Field.Body.CreateNullChecks(true); 41 | exp.ToString().ShouldBe("(x.Child != null)"); 42 | } 43 | 44 | [Fact] 45 | public void Skip2() 46 | { 47 | Expression> Field = x => x.Child.Name; 48 | var exp = Field.Body.CreateNullChecks(true); 49 | exp.ToString().ShouldBe("(x.Child != null)"); 50 | } 51 | 52 | [Fact] 53 | public void Skip3() 54 | { 55 | Expression> Field = x => x.Child.GrandChild.Name; 56 | var exp = Field.Body.CreateNullChecks(true); 57 | exp.ToString().ShouldBe("((x.Child != null) AndAlso (x.Child.GrandChild != null))"); 58 | } 59 | 60 | [Fact] 61 | public void NonNullable() 62 | { 63 | Expression> Field = x => x.Enum; 64 | var exp = Field.Body.CreateNullChecks(); 65 | exp.ToString().ShouldBe("(True == True)"); 66 | } 67 | 68 | [Fact] 69 | public void NonNullable2() 70 | { 71 | Expression> Field = x => x.Child.Enum; 72 | var exp = Field.Body.CreateNullChecks(); 73 | exp.ToString().ShouldBe("(x.Child != null)"); 74 | } 75 | 76 | private class Parent 77 | { 78 | public Child Child { get; set; } 79 | 80 | public MyEnum Enum { get; set; } 81 | } 82 | 83 | private class Child 84 | { 85 | public string Name { get; set; } 86 | 87 | public GrandChild GrandChild { get; set; } 88 | 89 | public MyEnum Enum { get; set; } 90 | } 91 | 92 | private class GrandChild 93 | { 94 | public string Name { get; set; } 95 | } 96 | 97 | private enum MyEnum 98 | { 99 | One, 100 | Two, 101 | Three 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/BlazorTable.Tests/BlazorTable.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Always 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/BlazorTable.Tests/BrowserTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using PuppeteerSharp; 6 | using PuppeteerSharp.Contrib.Extensions; 7 | using Shouldly; 8 | using Xunit; 9 | 10 | namespace BlazorTable.Tests 11 | { 12 | public class BrowserTests : IAsyncLifetime 13 | { 14 | private string BaseAddress; 15 | 16 | private Browser Browser { get; set; } 17 | 18 | public async Task InitializeAsync() 19 | { 20 | string filename = "BrowserTestsAddress.config"; 21 | 22 | if (File.Exists(filename)) 23 | BaseAddress = File.ReadAllText(filename); 24 | else 25 | throw new Exception($"Missing {filename}"); 26 | 27 | await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision); 28 | 29 | Browser = await Puppeteer.LaunchAsync(new LaunchOptions 30 | { 31 | Headless = true 32 | }); 33 | } 34 | 35 | public async Task DisposeAsync() 36 | { 37 | await Browser?.CloseAsync(); 38 | } 39 | 40 | private async Task PrintPerf(Page page) 41 | { 42 | var perf = await page.EvaluateExpressionAsync("window.performance.timing.domContentLoadedEventEnd - window.performance.timing.navigationStart"); 43 | Console.WriteLine($"Load Time: {perf}ms"); 44 | } 45 | 46 | [Fact] 47 | public async Task CheckRoot() 48 | { 49 | bool hasError = false; 50 | 51 | var page = await Browser.NewPageAsync(); 52 | 53 | page.Console += Page_Console; 54 | 55 | void Page_Console(object sender, ConsoleEventArgs e) 56 | { 57 | if (e.Message.Type == ConsoleType.Error) 58 | hasError = true; 59 | } 60 | 61 | await page.GoToAsync(BaseAddress); 62 | 63 | var selector = await page.WaitForSelectorAsync("div.table-responsive > table > tbody > tr:nth-child(1) > td:nth-child(3)"); 64 | 65 | (await selector.InnerTextAsync()).ShouldBe("Astrix Mariette"); 66 | 67 | hasError.ShouldBeFalse(); 68 | 69 | await PrintPerf(page); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/BlazorTable.Tests/BrowserTestsAddress.config: -------------------------------------------------------------------------------- 1 | http://localhost:51076 -------------------------------------------------------------------------------- /src/BlazorTable/BlazorTable.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 3.0 6 | true 7 | BlazorTable 8 | BlazorTable 9 | Blazor Table Component with Sorting, Paging and Filtering 10 | Ivan Josipovic 11 | https://BlazorTable.netlify.app 12 | Blazor;Table;Component;Grid;DataTable;Data;Sort;Filter;AspNetCore;AspNet 13 | icon.png 14 | https://github.com/IvanJosipovic/BlazorTable 15 | git 16 | MIT 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | $(MSBuildProjectName).xml 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | all 36 | runtime; build; native; contentfiles; analyzers; buildtransitive 37 | 38 | 39 | 40 | 41 | 42 | <_Parameter1>$(AssemblyName).Tests 43 | 44 | 45 | 46 | 47 | 48 | 49 | True 50 | True 51 | Localization.resx 52 | 53 | 54 | 55 | 56 | 57 | ResXFileCodeGenerator 58 | Localization.Designer.cs 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/AggregateType.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorTable 2 | { 3 | public enum AggregateType 4 | { 5 | Sum, 6 | Average, 7 | Count, 8 | Min, 9 | Max 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/Align.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorTable 2 | { 3 | public enum Align 4 | { 5 | None, 6 | Left, 7 | Center, 8 | Right 9 | } 10 | } -------------------------------------------------------------------------------- /src/BlazorTable/Components/Column.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/CustomRow.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/CustomRow.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using System; 3 | 4 | namespace BlazorTable 5 | { 6 | /// 7 | /// A custom row template 8 | /// 9 | /// 10 | public partial class CustomRow : ComponentBase 11 | { 12 | /// 13 | /// Parent Table 14 | /// 15 | [CascadingParameter(Name = "Table")] 16 | public ITable Table { get; set; } 17 | 18 | /// 19 | /// Function that defines if the custom row is active for the TableItem inserted as parameter 20 | /// 21 | [Parameter] 22 | public Func IsActiveForItem { get; set; } 23 | 24 | /// 25 | /// Content to show 26 | /// 27 | [Parameter] 28 | public RenderFragment ChildContent { get; set; } 29 | 30 | /// 31 | /// When initialized, add custom row to table 32 | /// 33 | protected override void OnInitialized() 34 | { 35 | Table.AddCustomRow(this); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/CustomSelectOption.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable -------------------------------------------------------------------------------- /src/BlazorTable/Components/CustomSelectOption.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace BlazorTable 4 | { 5 | /// 6 | /// Option for CustomSelect 7 | /// 8 | public partial class CustomSelectOption 9 | { 10 | /// 11 | /// Parent table 12 | /// 13 | [CascadingParameter(Name = "CustomSelect")] 14 | public ICustomSelect CustomSelect { get; set; } 15 | 16 | [Parameter] 17 | public string Key { get; set; } 18 | 19 | [Parameter] 20 | public object Value { get; set; } 21 | 22 | /// 23 | /// When initialized, tell CustomSelect of this item 24 | /// 25 | protected override void OnInitialized() 26 | { 27 | CustomSelect.AddSelect(Key, Value); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/DetailTemplate.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem -------------------------------------------------------------------------------- /src/BlazorTable/Components/DetailTemplate.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace BlazorTable 4 | { 5 | /// 6 | /// Detail Template 7 | /// 8 | /// 9 | public partial class DetailTemplate 10 | { 11 | /// 12 | /// Parent table 13 | /// 14 | [CascadingParameter(Name = "Table")] 15 | public ITable Table { get; set; } 16 | 17 | /// 18 | /// Content to show 19 | /// 20 | [Parameter] 21 | public RenderFragment ChildContent { get; set; } 22 | 23 | /// 24 | /// When initialized, tell table of this item 25 | /// 26 | protected override void OnInitialized() 27 | { 28 | Table.SetDetailTemplate(this); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/DynamicColumns.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @foreach (var propertyInfo in (Type == null ? typeof(TableItem).GetProperties() : Type.GetProperties() )) 5 | { 6 | if(propertyInfo.PropertyType.MemberType == System.Reflection.MemberTypes.NestedType) 7 | { 8 | 9 | } else { 10 | 11 | var member = GenerateMemberExpression(propertyInfo); 12 | 13 | 14 | 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/DynamicColumns.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Reflection; 8 | 9 | namespace BlazorTable 10 | { 11 | /// 12 | /// BlazorTable Dynamic Columns 13 | /// 14 | /// 15 | public partial class DynamicColumns 16 | { 17 | /// 18 | /// Parent Table 19 | /// 20 | [CascadingParameter(Name = "Table")] 21 | public ITable Table { get; set; } 22 | 23 | /// 24 | /// Column can be sorted 25 | /// 26 | [Parameter] 27 | public bool Sortable { get; set; } 28 | 29 | /// 30 | /// Column can be filtered 31 | /// 32 | [Parameter] 33 | public bool Filterable { get; set; } 34 | 35 | /// 36 | /// Horizontal alignment 37 | /// 38 | [Parameter] 39 | public Align Align { get; set; } 40 | 41 | /// 42 | /// Column CSS Class 43 | /// 44 | [Parameter] 45 | public string Class { get; set; } 46 | 47 | [Parameter] 48 | public Type Type { get; set; } 49 | 50 | private static Expression> GenerateMemberExpression(PropertyInfo propertyInfo) 51 | { 52 | var entityParam = Expression.Parameter(typeof(TModel), "x"); 53 | 54 | Expression columnExpr = null; 55 | 56 | if (propertyInfo.ReflectedType.Name == "JObject") 57 | { 58 | var assembly = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.ManifestModule.Name == "Newtonsoft.Json.dll").First(); 59 | 60 | var type = propertyInfo.ReflectedType;// assembly.GetType("Newtonsoft.Json.Linq.JToken"); 61 | var exttype = assembly.GetType("Newtonsoft.Json.Linq.Extensions"); 62 | 63 | columnExpr = Expression.Call(exttype.GetMethod("Value", new[] { typeof(IEnumerable<>).MakeGenericType(type) }).MakeGenericMethod(new[] {type}), 64 | Expression.Property( 65 | Expression.Call(entityParam, "Property", null, Expression.Constant(propertyInfo.Name)), 66 | "Value") 67 | ); 68 | } 69 | else 70 | { 71 | columnExpr = Expression.Property(entityParam, propertyInfo); 72 | 73 | if (propertyInfo.PropertyType != typeof(T)) 74 | columnExpr = Expression.Convert(columnExpr, typeof(T)); 75 | } 76 | 77 | return Expression.Lambda>(columnExpr, entityParam); 78 | } 79 | 80 | private string RenderProperty(TableItem data, PropertyInfo property, Func func = null) 81 | { 82 | if (property.ReflectedType.Name == "JObject") 83 | { 84 | return "";// func.Invoke(data)?.ToString(); 85 | } 86 | 87 | object rawData = property.GetValue(data); 88 | 89 | if (rawData == null) 90 | return ""; 91 | 92 | if (rawData.GetType().IsEnum) 93 | { 94 | Type enumType = property.GetValue(data).GetType(); 95 | 96 | MemberInfo[] memberInfo = enumType.GetMember(rawData.ToString()); 97 | if (memberInfo != null && memberInfo.Length > 0) 98 | { 99 | object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); 100 | 101 | if (attrs != null && attrs.Length > 0) 102 | { 103 | //Pull out the description value 104 | return ((DescriptionAttribute)attrs[0]).Description; 105 | } 106 | } 107 | } 108 | 109 | return rawData.ToString(); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /src/BlazorTable/Components/EmptyDataTemplate.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable -------------------------------------------------------------------------------- /src/BlazorTable/Components/EmptyDataTemplate.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace BlazorTable 4 | { 5 | /// 6 | /// Child content for empty dataset 7 | /// 8 | public partial class EmptyDataTemplate 9 | { 10 | /// 11 | /// Parent table 12 | /// 13 | [CascadingParameter(Name = "Table")] 14 | public ITable Table { get; set; } 15 | 16 | /// 17 | /// Content to show 18 | /// 19 | [Parameter] 20 | public RenderFragment ChildContent { get; set; } 21 | 22 | /// 23 | /// When initialized, tell table of this item 24 | /// 25 | protected override void OnInitialized() 26 | { 27 | Table.SetEmptyDataTemplate(this); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/FilterManager.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | 5 | @ChildContent 6 | 7 |
8 | 9 |
10 | 11 | 12 | 13 |
14 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/FilterManager.razor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Components; 3 | using Microsoft.Extensions.Localization; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace BlazorTable 7 | { 8 | public partial class FilterManager 9 | { 10 | [CascadingParameter(Name = "Column")] 11 | public IColumn Column { get; set; } 12 | 13 | [Parameter] 14 | public RenderFragment ChildContent { get; set; } 15 | 16 | [Inject] 17 | public ILogger> Logger { get; set; } 18 | 19 | [Inject] 20 | IStringLocalizer Localization { get; set; } 21 | 22 | private async Task ApplyFilterAsync() 23 | 24 | { 25 | Column.ToggleFilter(); 26 | 27 | if (Column.FilterControl != null) 28 | { 29 | Column.Filter = Column.FilterControl.GetFilter(); 30 | await Column.Table.UpdateAsync().ConfigureAwait(false); 31 | await Column.Table.FirstPageAsync().ConfigureAwait(false); 32 | } 33 | else 34 | { 35 | Logger.LogInformation("Filter is null"); 36 | } 37 | } 38 | 39 | private async Task ClearFilterAsync() 40 | { 41 | Column.ToggleFilter(); 42 | 43 | if (Column.Filter != null) 44 | { 45 | Column.Filter = null; 46 | await Column.Table.UpdateAsync().ConfigureAwait(false); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/LoadingDataTemplate.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/LoadingDataTemplate.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace BlazorTable 4 | { 5 | /// 6 | /// Child content for null dataset 7 | /// 8 | public partial class LoadingDataTemplate 9 | { 10 | /// 11 | /// Parent table 12 | /// 13 | [CascadingParameter(Name = "Table")] 14 | public ITable Table { get; set; } 15 | 16 | /// 17 | /// Content to show 18 | /// 19 | [Parameter] 20 | public RenderFragment ChildContent { get; set; } 21 | 22 | /// 23 | /// When initialized, tell table of this item 24 | /// 25 | protected override void OnInitialized() 26 | { 27 | Table.SetLoadingDataTemplate(this); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/Pager.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | 3 | @if (AlwaysShow || (Table.TotalPages > 1)) 4 | { 5 |
6 | 50 |
51 | } 52 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/Pager.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.Extensions.Localization; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorTable 7 | { 8 | /// 9 | /// BlazorTable Pager 10 | /// 11 | public partial class Pager 12 | { 13 | [CascadingParameter(Name = "Table")] 14 | public ITable Table { get; set; } 15 | 16 | /// 17 | /// Always show Pager, otherwise only show if TotalPages > 1 18 | /// 19 | [Parameter] 20 | public bool AlwaysShow { get; set; } 21 | 22 | /// 23 | /// Show current page number 24 | /// 25 | [Parameter] 26 | public bool ShowPageNumber { get; set; } 27 | 28 | /// 29 | /// Show total item count 30 | /// 31 | [Parameter] 32 | public bool ShowTotalCount { get; set; } 33 | 34 | /// 35 | /// Page size options 36 | /// 37 | [Parameter] 38 | public List PageSizes { get; set; } = new List() { 15, 30, 60 }; 39 | 40 | /// 41 | /// Show Page Size Options 42 | /// 43 | [Parameter] 44 | public bool ShowPageSizes { get; set; } 45 | 46 | [Inject] 47 | IStringLocalizer Localization { get; set; } 48 | 49 | private async Task SetPageSizeAsync(ChangeEventArgs args) 50 | { 51 | if (int.TryParse(args.Value.ToString(), out int result)) 52 | { 53 | await Table.SetPageSizeAsync(result).ConfigureAwait(false); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/Popover.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | 3 | @if (IsOpen ?? false) 4 | { 5 | 9 | } -------------------------------------------------------------------------------- /src/BlazorTable/Components/Popover.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.JSInterop; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorTable 8 | { 9 | public partial class Popover 10 | { 11 | [Parameter(CaptureUnmatchedValues = true)] 12 | public IReadOnlyDictionary UnknownParameters { get; set; } 13 | 14 | [Parameter] 15 | public EventCallback IsOpenChanged { get; set; } 16 | 17 | [Parameter] 18 | public bool? IsOpen 19 | { 20 | get => _isOpen; 21 | set 22 | { 23 | if (value != null) 24 | { 25 | Manual = true; 26 | if (value.Value != _isOpen) 27 | { 28 | Changed(value ?? false); 29 | StateHasChanged(); 30 | } 31 | _isOpen = value ?? false; 32 | } 33 | } 34 | } 35 | 36 | [Parameter] 37 | public Placement Placement { get; set; } = Placement.Auto; 38 | 39 | [Parameter] 40 | public ElementReference Reference { get; set; } 41 | 42 | [Parameter] 43 | public RenderFragment ChildContent { get; set; } 44 | 45 | [Parameter] 46 | public bool DismissOnNextClick { get; set; } = true; 47 | 48 | [Inject] 49 | protected IJSRuntime JSRuntime { get; set; } 50 | 51 | public bool Manual { get; set; } 52 | 53 | private bool _isOpen { get; set; } 54 | 55 | protected virtual Task Changed(bool e) 56 | { 57 | return Task.CompletedTask; 58 | } 59 | 60 | public virtual void Hide() 61 | { 62 | _isOpen = false; 63 | if (!Manual) Changed(_isOpen); 64 | IsOpenChanged.InvokeAsync(false); 65 | } 66 | 67 | protected string Classname => $"popover bs-popover-{Placement.ToDescriptionString()} {(IsOpen == true ? "show" : string.Empty)}"; 68 | 69 | protected ElementReference MyRef { get; set; } 70 | 71 | protected ElementReference Arrow { get; set; } 72 | 73 | protected override void OnAfterRender(bool firstRender) 74 | { 75 | if (IsOpen ?? false) 76 | { 77 | var placement = Placement.ToDescriptionString(); 78 | JSRuntime.InvokeVoidAsync("BlazorTablePopper", Reference, MyRef, Arrow, placement); 79 | } 80 | } 81 | 82 | protected void OnClick() 83 | { 84 | if (DismissOnNextClick) 85 | { 86 | Hide(); 87 | } 88 | } 89 | } 90 | 91 | public enum Placement 92 | { 93 | [Description("auto")] 94 | Auto, 95 | 96 | [Description("auto-start")] 97 | AutoStart, 98 | 99 | [Description("auto-end")] 100 | AutoEnd, 101 | 102 | [Description("top")] 103 | Top, 104 | 105 | [Description("top-start")] 106 | TopStart, 107 | 108 | [Description("top-end")] 109 | TopEnd, 110 | 111 | [Description("right")] 112 | Right, 113 | 114 | [Description("right-start")] 115 | RightStart, 116 | 117 | [Description("right-end")] 118 | EightEnd, 119 | 120 | [Description("bottom")] 121 | Bottom, 122 | 123 | [Description("bottom-start")] 124 | BottomStart, 125 | 126 | [Description("bottom-end")] 127 | BottomEnd, 128 | 129 | [Description("left")] 130 | Left, 131 | 132 | [Description("left-start")] 133 | LeftStart, 134 | 135 | [Description("left-end")] 136 | LeftEnd 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/SelectionType.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorTable 2 | { 3 | public enum SelectionType 4 | { 5 | None, 6 | Single, 7 | Multiple 8 | } 9 | } -------------------------------------------------------------------------------- /src/BlazorTable/Components/ServerSide/FilterData.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorTable.Components.ServerSide 2 | { 3 | public class FilterData 4 | { 5 | public string OrderBy { get; set; } 6 | 7 | public string Query { get; set; } 8 | 9 | public int? Top { get; set; } 10 | 11 | public int? Skip { get; set; } 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/BlazorTable/Components/ServerSide/PaginationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BlazorTable.Components.ServerSide 6 | { 7 | public class PaginationResult 8 | { 9 | public int Top { get; set; } 10 | 11 | public int Skip { get; set; } 12 | 13 | public int? Total { get; set; } 14 | 15 | public IEnumerable Records { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BlazorTable/Filters/BooleanFilter.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @if (Column.FilterControl == this) 5 | { 6 | 12 | } -------------------------------------------------------------------------------- /src/BlazorTable/Filters/BooleanFilter.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorTable.Localization; 2 | using Microsoft.AspNetCore.Components; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq.Expressions; 6 | 7 | namespace BlazorTable 8 | { 9 | public partial class BooleanFilter : IFilter 10 | { 11 | [CascadingParameter(Name = "Column")] 12 | public IColumn Column { get; set; } 13 | 14 | private BooleanCondition Condition { get; set; } 15 | 16 | public List FilterTypes => new List() 17 | { 18 | typeof(bool) 19 | }; 20 | 21 | protected override void OnInitialized() 22 | { 23 | if (FilterTypes.Contains(Column.Type.GetNonNullableType())) 24 | { 25 | Column.FilterControl = this; 26 | 27 | if (Column.Filter != null) 28 | { 29 | var nodeType = Column.Filter.Body.NodeType; 30 | 31 | if (Column.Filter.Body is BinaryExpression binaryExpression 32 | && binaryExpression.NodeType == ExpressionType.AndAlso) 33 | { 34 | nodeType = binaryExpression.Right.NodeType; 35 | } 36 | 37 | switch (nodeType) 38 | { 39 | case ExpressionType.IsTrue: 40 | Condition = BooleanCondition.True; 41 | break; 42 | case ExpressionType.IsFalse: 43 | Condition = BooleanCondition.False; 44 | break; 45 | case ExpressionType.Equal: 46 | Condition = BooleanCondition.IsNull; 47 | break; 48 | case ExpressionType.NotEqual: 49 | Condition = BooleanCondition.IsNotNull; 50 | break; 51 | } 52 | } 53 | } 54 | } 55 | 56 | public Expression> GetFilter() 57 | { 58 | return Condition switch 59 | { 60 | BooleanCondition.True => 61 | Expression.Lambda>( 62 | Expression.AndAlso( 63 | Column.Field.Body.CreateNullChecks(), 64 | Expression.IsTrue(Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()))), 65 | Column.Field.Parameters), 66 | 67 | BooleanCondition.False => 68 | Expression.Lambda>( 69 | Expression.AndAlso( 70 | Column.Field.Body.CreateNullChecks(), 71 | Expression.IsFalse(Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()))), 72 | Column.Field.Parameters), 73 | 74 | BooleanCondition.IsNull => 75 | Expression.Lambda>( 76 | Expression.AndAlso( 77 | Column.Field.Body.CreateNullChecks(true), 78 | Expression.Equal(Column.Field.Body, Expression.Constant(null))), 79 | Column.Field.Parameters), 80 | 81 | BooleanCondition.IsNotNull => 82 | Expression.Lambda>( 83 | Expression.AndAlso( 84 | Column.Field.Body.CreateNullChecks(true), 85 | Expression.NotEqual(Column.Field.Body, Expression.Constant(null))), 86 | Column.Field.Parameters), 87 | 88 | _ => null, 89 | }; 90 | } 91 | } 92 | 93 | public enum BooleanCondition 94 | { 95 | [LocalizedDescription("BooleanConditionTrue", typeof(Localization.Localization))] 96 | True, 97 | 98 | [LocalizedDescription("BooleanConditionFalse", typeof(Localization.Localization))] 99 | False, 100 | 101 | [LocalizedDescription("BooleanConditionIsNull", typeof(Localization.Localization))] 102 | IsNull, 103 | 104 | [LocalizedDescription("BooleanConditionIsNotNull", typeof(Localization.Localization))] 105 | IsNotNull 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/BlazorTable/Filters/CustomSelect.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @if (Column.FilterControl == this) 5 | { 6 | 12 | 13 | @if (Condition != CustomSelectCondition.IsNull && Condition != CustomSelectCondition.IsNotNull) 14 | { 15 | 21 | } 22 | } 23 | 24 | 25 | @ChildContent 26 | -------------------------------------------------------------------------------- /src/BlazorTable/Filters/CustomSelect.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorTable.Localization; 2 | using Microsoft.AspNetCore.Components; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | 9 | namespace BlazorTable 10 | { 11 | public partial class CustomSelect : IFilter, ICustomSelect 12 | { 13 | [CascadingParameter(Name = "Column")] 14 | public IColumn Column { get; set; } 15 | 16 | [Parameter] 17 | public RenderFragment ChildContent { get; set; } 18 | 19 | private List> Items = new List>(); 20 | 21 | private CustomSelectCondition Condition { get; set; } 22 | 23 | private object FilterValue { get; set; } 24 | 25 | protected override void OnInitialized() 26 | { 27 | Column.FilterControl = this; 28 | 29 | if (Column.Filter?.Body is BinaryExpression binaryExpression 30 | && binaryExpression.Right is BinaryExpression logicalBinary 31 | && logicalBinary.Right is ConstantExpression constant) 32 | { 33 | switch (logicalBinary.NodeType) 34 | { 35 | case ExpressionType.Equal: 36 | Condition = constant.Value == null ? CustomSelectCondition.IsNull : CustomSelectCondition.IsEqualTo; 37 | break; 38 | case ExpressionType.NotEqual: 39 | Condition = constant.Value == null ? CustomSelectCondition.IsNotNull : CustomSelectCondition.IsNotEqualTo; 40 | break; 41 | } 42 | 43 | FilterValue = constant.Value; 44 | } 45 | } 46 | 47 | public Expression> GetFilter() 48 | { 49 | return Condition switch 50 | { 51 | CustomSelectCondition.IsEqualTo => 52 | Expression.Lambda>( 53 | Expression.AndAlso( 54 | Column.Field.Body.CreateNullChecks(), 55 | Expression.Equal( 56 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 57 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 58 | Column.Field.Parameters), 59 | 60 | CustomSelectCondition.IsNotEqualTo => Expression.Lambda>( 61 | Expression.AndAlso( 62 | Column.Field.Body.CreateNullChecks(), 63 | Expression.NotEqual( 64 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 65 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 66 | Column.Field.Parameters), 67 | 68 | CustomSelectCondition.IsNull => 69 | Expression.Lambda>( 70 | Expression.AndAlso( 71 | Column.Field.Body.CreateNullChecks(true), 72 | Expression.Equal(Column.Field.Body, Expression.Constant(null))), 73 | Column.Field.Parameters), 74 | 75 | CustomSelectCondition.IsNotNull => 76 | Expression.Lambda>( 77 | Expression.AndAlso( 78 | Column.Field.Body.CreateNullChecks(true), 79 | Expression.NotEqual(Column.Field.Body, Expression.Constant(null))), 80 | Column.Field.Parameters), 81 | 82 | _ => throw new ArgumentException(Condition + " is not defined!"), 83 | }; 84 | } 85 | 86 | public void AddSelect(string key, object value) 87 | { 88 | Items.Add(new KeyValuePair(key, value)); 89 | 90 | if (FilterValue == null) 91 | { 92 | FilterValue = Items.FirstOrDefault().Value; 93 | } 94 | 95 | StateHasChanged(); 96 | } 97 | 98 | public enum CustomSelectCondition 99 | { 100 | [LocalizedDescription("CustomSelectConditionIsEqualTo", typeof(Localization.Localization))] 101 | IsEqualTo, 102 | 103 | [LocalizedDescription("CustomSelectConditionIsNotEqualTo", typeof(Localization.Localization))] 104 | IsNotEqualTo, 105 | 106 | [LocalizedDescription("CustomSelectConditionIsNull", typeof(Localization.Localization))] 107 | IsNull, 108 | 109 | [LocalizedDescription("CustomSelectConditionIsNotNull", typeof(Localization.Localization))] 110 | IsNotNull 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/BlazorTable/Filters/DateFilter.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @if (Column.FilterControl == this) 5 | { 6 | 12 | @if (Condition != NumberCondition.IsNull && Condition != NumberCondition.IsNotNull) 13 | { 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /src/BlazorTable/Filters/DateFilter.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace BlazorTable 6 | { 7 | public partial class DateFilter : IFilter 8 | { 9 | [CascadingParameter(Name = "Column")] 10 | public IColumn Column { get; set; } 11 | 12 | private NumberCondition Condition { get; set; } 13 | 14 | private DateTime FilterValue { get; set; } = DateTime.Now; 15 | 16 | protected override void OnInitialized() 17 | { 18 | if (Column.Type.GetNonNullableType() == typeof(DateTime)) 19 | { 20 | Column.FilterControl = this; 21 | 22 | if (Column.Filter?.Body is BinaryExpression binaryExpression 23 | && binaryExpression.Right is BinaryExpression logicalBinary 24 | && logicalBinary.Right is ConstantExpression constant) 25 | { 26 | switch (binaryExpression.Right.NodeType) 27 | { 28 | case ExpressionType.Equal: 29 | Condition = constant.Value == null ? NumberCondition.IsNull : NumberCondition.IsEqualTo; 30 | break; 31 | case ExpressionType.NotEqual: 32 | Condition = constant.Value == null ? NumberCondition.IsNotNull : NumberCondition.IsNotEqualTo; 33 | break; 34 | case ExpressionType.GreaterThanOrEqual: 35 | Condition = NumberCondition.IsGreaterThanOrEqualTo; 36 | break; 37 | case ExpressionType.GreaterThan: 38 | Condition = NumberCondition.IsGreaterThan; 39 | break; 40 | case ExpressionType.LessThanOrEqual: 41 | Condition = NumberCondition.IsLessThanOrEqualTo; 42 | break; 43 | case ExpressionType.LessThan: 44 | Condition = NumberCondition.IsLessThan; 45 | break; 46 | } 47 | 48 | if (constant.Value != null && DateTime.TryParse(constant.Value.ToString(), out DateTime result)) 49 | { 50 | FilterValue = result; 51 | } 52 | } 53 | } 54 | } 55 | 56 | public Expression> GetFilter() 57 | { 58 | return Condition switch 59 | { 60 | NumberCondition.IsEqualTo => 61 | Expression.Lambda>( 62 | Expression.AndAlso( 63 | Column.Field.Body.CreateNullChecks(), 64 | Expression.Equal( 65 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 66 | Expression.Constant(FilterValue))), 67 | Column.Field.Parameters), 68 | 69 | NumberCondition.IsNotEqualTo => 70 | Expression.Lambda>( 71 | Expression.AndAlso( 72 | Column.Field.Body.CreateNullChecks(), 73 | Expression.NotEqual( 74 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 75 | Expression.Constant(FilterValue))), 76 | Column.Field.Parameters), 77 | 78 | NumberCondition.IsGreaterThanOrEqualTo => 79 | Expression.Lambda>( 80 | Expression.AndAlso( 81 | Column.Field.Body.CreateNullChecks(), 82 | Expression.GreaterThanOrEqual( 83 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 84 | Expression.Constant(FilterValue))), 85 | Column.Field.Parameters), 86 | 87 | NumberCondition.IsGreaterThan => 88 | Expression.Lambda>( 89 | Expression.AndAlso( 90 | Column.Field.Body.CreateNullChecks(), 91 | Expression.GreaterThan( 92 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 93 | Expression.Constant(FilterValue))), 94 | Column.Field.Parameters), 95 | 96 | NumberCondition.IsLessThanOrEqualTo => 97 | Expression.Lambda>( 98 | Expression.AndAlso( 99 | Column.Field.Body.CreateNullChecks(), 100 | Expression.LessThanOrEqual( 101 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 102 | Expression.Constant(FilterValue))), 103 | Column.Field.Parameters), 104 | 105 | NumberCondition.IsLessThan => 106 | Expression.Lambda>( 107 | Expression.AndAlso( 108 | Column.Field.Body.CreateNullChecks(), 109 | Expression.LessThan( 110 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 111 | Expression.Constant(FilterValue))), 112 | Column.Field.Parameters), 113 | 114 | NumberCondition.IsNull => 115 | Expression.Lambda>( 116 | Expression.AndAlso( 117 | Column.Field.Body.CreateNullChecks(true), 118 | Expression.Equal(Column.Field.Body, Expression.Constant(null))), 119 | Column.Field.Parameters), 120 | 121 | NumberCondition.IsNotNull => 122 | Expression.Lambda>( 123 | Expression.AndAlso( 124 | Column.Field.Body.CreateNullChecks(true), 125 | Expression.NotEqual(Column.Field.Body, Expression.Constant(null))), 126 | Column.Field.Parameters), 127 | 128 | _ => throw new ArgumentException(Condition + " is not defined!"), 129 | }; 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /src/BlazorTable/Filters/EnumFilter.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @if (Column.FilterControl == this) 5 | { 6 | 12 | 13 | @if (Condition != EnumCondition.IsNull && Condition != EnumCondition.IsNotNull) 14 | { 15 | 21 | } 22 | } -------------------------------------------------------------------------------- /src/BlazorTable/Filters/EnumFilter.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorTable.Localization; 2 | using Microsoft.AspNetCore.Components; 3 | using System; 4 | using System.Globalization; 5 | using System.Linq.Expressions; 6 | using Microsoft.Extensions.Localization; 7 | 8 | namespace BlazorTable 9 | { 10 | public partial class EnumFilter : IFilter 11 | { 12 | [CascadingParameter(Name = "Column")] 13 | public IColumn Column { get; set; } 14 | 15 | [Inject] 16 | IStringLocalizer Localization { get; set; } 17 | 18 | private EnumCondition Condition { get; set; } 19 | 20 | private object FilterValue { get; set; } 21 | 22 | protected override void OnInitialized() 23 | { 24 | if (Column.Type.GetNonNullableType().IsEnum) 25 | { 26 | Column.FilterControl = this; 27 | 28 | if (Column.Filter?.Body is BinaryExpression binaryExpression 29 | && binaryExpression.Right is BinaryExpression logicalBinary 30 | && logicalBinary.Right is ConstantExpression constant) 31 | { 32 | switch (binaryExpression.Right.NodeType) 33 | { 34 | case ExpressionType.Equal: 35 | Condition = constant.Value == null ? EnumCondition.IsNull : EnumCondition.IsEqualTo; 36 | break; 37 | case ExpressionType.NotEqual: 38 | Condition = constant.Value == null ? EnumCondition.IsNotNull : EnumCondition.IsNotEqualTo; 39 | break; 40 | } 41 | 42 | FilterValue = constant.Value; 43 | } 44 | 45 | if (FilterValue == null) 46 | { 47 | FilterValue = Enum.GetValues(Column.Type.GetNonNullableType()).GetValue(0); 48 | } 49 | } 50 | } 51 | 52 | public Expression> GetFilter() 53 | { 54 | return Condition switch 55 | { 56 | EnumCondition.IsEqualTo => 57 | Expression.Lambda>( 58 | Expression.AndAlso( 59 | Column.Field.Body.CreateNullChecks(), 60 | Expression.Equal( 61 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 62 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 63 | Column.Field.Parameters), 64 | 65 | EnumCondition.IsNotEqualTo => 66 | Expression.Lambda>( 67 | Expression.AndAlso( 68 | Column.Field.Body.CreateNullChecks(), 69 | Expression.NotEqual( 70 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 71 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 72 | Column.Field.Parameters), 73 | 74 | EnumCondition.IsNull => 75 | Expression.Lambda>( 76 | Expression.AndAlso( 77 | Column.Field.Body.CreateNullChecks(true), 78 | Expression.Equal(Column.Field.Body, Expression.Constant(null))), 79 | Column.Field.Parameters), 80 | 81 | EnumCondition.IsNotNull => 82 | Expression.Lambda>( 83 | Expression.AndAlso( 84 | Column.Field.Body.CreateNullChecks(true), 85 | Expression.NotEqual(Column.Field.Body, Expression.Constant(null))), 86 | Column.Field.Parameters), 87 | 88 | _ => throw new ArgumentException(Condition + " is not defined!"), 89 | }; 90 | } 91 | 92 | public enum EnumCondition 93 | { 94 | [LocalizedDescription("EnumConditionIsEqualTo", typeof(Localization.Localization))] 95 | IsEqualTo, 96 | 97 | [LocalizedDescription("EnumConditionIsNotEqualTo", typeof(Localization.Localization))] 98 | IsNotEqualTo, 99 | 100 | [LocalizedDescription("EnumConditionIsNull", typeof(Localization.Localization))] 101 | IsNull, 102 | 103 | [LocalizedDescription("EnumConditionIsNotNull", typeof(Localization.Localization))] 104 | IsNotNull 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/BlazorTable/Filters/NumberFilter.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @if (Column.FilterControl == this) 5 | { 6 | 12 | @if (Condition != NumberCondition.IsNull && Condition != NumberCondition.IsNotNull) 13 | { 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /src/BlazorTable/Filters/NumberFilter.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorTable.Localization; 2 | using Microsoft.AspNetCore.Components; 3 | using System; 4 | using System.Globalization; 5 | using System.Linq.Expressions; 6 | 7 | namespace BlazorTable 8 | { 9 | public partial class NumberFilter : IFilter 10 | { 11 | [CascadingParameter(Name = "Column")] 12 | public IColumn Column { get; set; } 13 | 14 | [Inject] 15 | Microsoft.Extensions.Localization.IStringLocalizer Localization { get; set; } 16 | 17 | private NumberCondition Condition { get; set; } 18 | 19 | private string FilterValue { get; set; } 20 | 21 | protected override void OnInitialized() 22 | { 23 | if (Column.Type.IsNumeric() && !Column.Type.GetNonNullableType().IsEnum) 24 | { 25 | Column.FilterControl = this; 26 | 27 | if (Column.Filter?.Body is BinaryExpression binaryExpression 28 | && binaryExpression.Right is BinaryExpression logicalBinary 29 | && logicalBinary.Right is ConstantExpression constant) 30 | { 31 | switch (binaryExpression.Right.NodeType) 32 | { 33 | case ExpressionType.Equal: 34 | Condition = constant.Value == null ? NumberCondition.IsNull : NumberCondition.IsEqualTo; 35 | break; 36 | case ExpressionType.NotEqual: 37 | Condition = constant.Value == null ? NumberCondition.IsNotNull : NumberCondition.IsNotEqualTo; 38 | break; 39 | case ExpressionType.GreaterThanOrEqual: 40 | Condition = NumberCondition.IsGreaterThanOrEqualTo; 41 | break; 42 | case ExpressionType.GreaterThan: 43 | Condition = NumberCondition.IsGreaterThan; 44 | break; 45 | case ExpressionType.LessThanOrEqual: 46 | Condition = NumberCondition.IsLessThanOrEqualTo; 47 | break; 48 | case ExpressionType.LessThan: 49 | Condition = NumberCondition.IsLessThan; 50 | break; 51 | } 52 | 53 | if (constant.Value != null) 54 | { 55 | FilterValue = constant.Value.ToString(); 56 | } 57 | } 58 | } 59 | } 60 | 61 | public Expression> GetFilter() 62 | { 63 | if (Condition != NumberCondition.IsNull && Condition != NumberCondition.IsNotNull && string.IsNullOrEmpty(FilterValue)) 64 | { 65 | return null; 66 | } 67 | 68 | return Condition switch 69 | { 70 | NumberCondition.IsEqualTo => 71 | Expression.Lambda>( 72 | Expression.AndAlso( 73 | Column.Field.Body.CreateNullChecks(), 74 | Expression.Equal( 75 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 76 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 77 | Column.Field.Parameters), 78 | 79 | NumberCondition.IsNotEqualTo => 80 | Expression.Lambda>( 81 | Expression.AndAlso( 82 | Column.Field.Body.CreateNullChecks(), 83 | Expression.NotEqual( 84 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 85 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 86 | Column.Field.Parameters), 87 | 88 | NumberCondition.IsGreaterThanOrEqualTo => 89 | Expression.Lambda>( 90 | Expression.AndAlso( 91 | Column.Field.Body.CreateNullChecks(), 92 | Expression.GreaterThanOrEqual( 93 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 94 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 95 | Column.Field.Parameters), 96 | 97 | NumberCondition.IsGreaterThan => 98 | Expression.Lambda>( 99 | Expression.AndAlso( 100 | Column.Field.Body.CreateNullChecks(), 101 | Expression.GreaterThan( 102 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 103 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 104 | Column.Field.Parameters), 105 | 106 | NumberCondition.IsLessThanOrEqualTo => 107 | Expression.Lambda>( 108 | Expression.AndAlso( 109 | Column.Field.Body.CreateNullChecks(), 110 | Expression.LessThanOrEqual( 111 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 112 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 113 | Column.Field.Parameters), 114 | 115 | NumberCondition.IsLessThan => 116 | Expression.Lambda>( 117 | Expression.AndAlso( 118 | Column.Field.Body.CreateNullChecks(), 119 | Expression.LessThan( 120 | Expression.Convert(Column.Field.Body, Column.Type.GetNonNullableType()), 121 | Expression.Constant(Convert.ChangeType(FilterValue, Column.Type.GetNonNullableType(), CultureInfo.InvariantCulture)))), 122 | Column.Field.Parameters), 123 | 124 | NumberCondition.IsNull => 125 | Expression.Lambda>( 126 | Expression.AndAlso( 127 | Column.Field.Body.CreateNullChecks(true), 128 | Expression.Equal(Column.Field.Body, Expression.Constant(null))), 129 | Column.Field.Parameters), 130 | 131 | NumberCondition.IsNotNull => 132 | Expression.Lambda>( 133 | Expression.AndAlso( 134 | Column.Field.Body.CreateNullChecks(true), 135 | Expression.NotEqual(Column.Field.Body, Expression.Constant(null))), 136 | Column.Field.Parameters), 137 | 138 | _ => throw new ArgumentException(Condition + " is not defined!"), 139 | }; 140 | } 141 | } 142 | 143 | public enum NumberCondition 144 | { 145 | [LocalizedDescription("NumberConditionIsEqualTo", typeof(Localization.Localization))] 146 | IsEqualTo, 147 | 148 | [LocalizedDescription("NumberConditionIsnotEqualTo", typeof(Localization.Localization))] 149 | IsNotEqualTo, 150 | 151 | [LocalizedDescription("NumberConditionIsGreaterThanOrEqualTo", typeof(Localization.Localization))] 152 | IsGreaterThanOrEqualTo, 153 | 154 | [LocalizedDescription("NumberConditionIsGreaterThan", typeof(Localization.Localization))] 155 | IsGreaterThan, 156 | 157 | [LocalizedDescription("NumberConditionIsLessThanOrEqualTo", typeof(Localization.Localization))] 158 | IsLessThanOrEqualTo, 159 | 160 | [LocalizedDescription("NumberConditionIsLessThan", typeof(Localization.Localization))] 161 | IsLessThan, 162 | 163 | [LocalizedDescription("NumberConditionIsNull", typeof(Localization.Localization))] 164 | IsNull, 165 | 166 | [LocalizedDescription("NumberConditionIsNotNull", typeof(Localization.Localization))] 167 | IsNotNull 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/BlazorTable/Filters/StringFilter.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @typeparam TableItem 3 | 4 | @if (Column.FilterControl == this) 5 | { 6 | 12 | 13 | @if (Condition != StringCondition.IsNotNulOrEmpty && Condition != StringCondition.IsNullOrEmpty) 14 | { 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /src/BlazorTable/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "", Scope = "namespaceanddescendants", Target = "BlazorTable")] 7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "", Scope = "namespaceanddescendants", Target = "BlazorTable")] 8 | -------------------------------------------------------------------------------- /src/BlazorTable/IServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace BlazorTable 4 | { 5 | public static class IServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddBlazorTable(this IServiceCollection services) 8 | { 9 | return services.AddLocalization(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/BlazorTable/Interfaces/IColumn.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorTable 7 | { 8 | /// 9 | /// Table Column 10 | /// 11 | /// 12 | public interface IColumn 13 | { 14 | /// 15 | /// Parent Table 16 | /// 17 | ITable Table { get; set; } 18 | 19 | /// 20 | /// Title (Optional, will use Field Name if null) 21 | /// 22 | string Title { get; set; } 23 | 24 | /// 25 | /// Width auto|value|initial|inherit 26 | /// 27 | string Width { get; set; } 28 | 29 | /// 30 | /// Column can be sorted 31 | /// 32 | bool Sortable { get; set; } 33 | 34 | /// 35 | /// Column can be filtered 36 | /// 37 | bool Filterable { get; set; } 38 | 39 | /// 40 | /// Column can be hidden 41 | /// 42 | bool Hideable { get; set; } 43 | 44 | /// 45 | /// Set the format for values if no template 46 | /// 47 | string Format { get; set; } 48 | 49 | /// 50 | /// Filter Panel is open 51 | /// 52 | bool FilterOpen { get; } 53 | 54 | /// 55 | /// Column visibility 56 | /// True if current column is visible else false. 57 | /// 58 | bool Visible { get; set; } 59 | 60 | /// 61 | /// Opens/Closes the Filter Panel 62 | /// 63 | void ToggleFilter(); 64 | 65 | /// 66 | /// Sort by this column 67 | /// 68 | Task SortByAsync(); 69 | 70 | /// 71 | /// Column Data Type 72 | /// 73 | Type Type { get; set; } 74 | 75 | /// 76 | /// Field which this column is for
77 | /// Required when Sortable = true
78 | /// Required when Filterable = true 79 | ///
80 | Expression> Field { get; set; } 81 | 82 | /// 83 | /// Filter expression 84 | /// 85 | Expression> Filter { get; set; } 86 | 87 | /// 88 | /// Edit Mode Item Template 89 | /// 90 | RenderFragment EditTemplate { get; set; } 91 | 92 | /// 93 | /// Normal Item Template 94 | /// 95 | RenderFragment Template { get; set; } 96 | 97 | /// 98 | /// Set custom Footer column value 99 | /// 100 | string SetFooterValue { get; set; } 101 | 102 | /// 103 | /// Currently applied Filter Control 104 | /// 105 | IFilter FilterControl { get; set; } 106 | 107 | /// 108 | /// Place custom controls which implement IFilter 109 | /// 110 | RenderFragment> CustomIFilters { get; set; } 111 | 112 | /// 113 | /// True if this is the current Sort Column 114 | /// 115 | bool SortColumn { get; set; } 116 | 117 | /// 118 | /// Direction of sorting 119 | /// 120 | bool SortDescending { get; set; } 121 | 122 | /// 123 | /// ARIA sort value, if any 124 | /// 125 | string AriaSort => SortColumn ? (SortDescending ? "descending" : "ascending") : null; 126 | 127 | /// 128 | /// Horizontal alignment 129 | /// 130 | Align Align { get; set; } 131 | 132 | /// 133 | /// Aggregates table column for the footer. It can only be applied to numerical fields (e.g. int, long decimal, double, etc.). 134 | /// 135 | AggregateType? Aggregate { get; set; } 136 | 137 | /// 138 | /// Filter Icon Element 139 | /// 140 | ElementReference FilterRef { get; set; } 141 | 142 | /// 143 | /// Column CSS Class 144 | /// 145 | string Class { get; set; } 146 | 147 | /// 148 | /// Column Footer CSS Class 149 | /// 150 | string ColumnFooterClass { get; set; } 151 | 152 | /// 153 | /// True if this is the default Sort Column 154 | /// 155 | bool? DefaultSortColumn { get; set; } 156 | 157 | /// 158 | /// Direction of default sorting 159 | /// 160 | bool? DefaultSortDescending { get; set; } 161 | 162 | 163 | /// 164 | /// Returns aggregation of this column for the table footer based on given type: Sum, Average, Count, Min, or Max. 165 | /// 166 | /// string results 167 | string GetFooterValue(); 168 | 169 | /// 170 | /// Default render if no Template specified 171 | /// 172 | /// 173 | /// 174 | string Render(TableItem item); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/BlazorTable/Interfaces/ICustomSelect.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorTable 2 | { 3 | public interface ICustomSelect 4 | { 5 | public void AddSelect(string key, object value); 6 | } 7 | } -------------------------------------------------------------------------------- /src/BlazorTable/Interfaces/IDataLoader.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using BlazorTable.Components.ServerSide; 3 | 4 | namespace BlazorTable.Interfaces 5 | { 6 | public interface IDataLoader 7 | { 8 | public Task> LoadDataAsync(FilterData parameters); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorTable/Interfaces/IFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace BlazorTable 5 | { 6 | /// 7 | /// Filter Component Interface 8 | /// 9 | /// 10 | public interface IFilter 11 | { 12 | /// 13 | /// Get Filter Expression 14 | /// 15 | /// 16 | Expression> GetFilter(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorTable/Interfaces/ITable.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BlazorTable 4 | { 5 | /// 6 | /// BlazorTable Interface 7 | /// 8 | public interface ITable 9 | { 10 | /// 11 | /// Page Size 12 | /// 13 | int PageSize { get; } 14 | 15 | /// 16 | /// Allow Columns to be reordered 17 | /// 18 | bool ColumnReorder { get; set; } 19 | 20 | /// 21 | /// Current Page Number 22 | /// 23 | int PageNumber { get; } 24 | 25 | /// 26 | /// Total Count of Items 27 | /// 28 | int TotalCount { get; } 29 | 30 | /// 31 | /// Total Pages 32 | /// 33 | public int TotalPages { get; } 34 | 35 | /// 36 | /// Is Table in Edit mode 37 | /// 38 | bool IsEditMode { get; } 39 | 40 | 41 | /// 42 | /// Go to First Page Async 43 | /// 44 | Task FirstPageAsync(); 45 | 46 | /// 47 | /// Go to Next Page 48 | /// 49 | Task NextPageAsync(); 50 | 51 | /// 52 | /// Go to Previous Page 53 | /// 54 | Task PreviousPageAsync(); 55 | 56 | /// 57 | /// Go to Last Page 58 | /// 59 | Task LastPageAsync(); 60 | 61 | /// 62 | /// Redraws the Table using EditTemplate instead of Template 63 | /// 64 | void ToggleEditMode(); 65 | 66 | /// 67 | /// Table Element CSS 68 | /// 69 | string TableClass { get; set; } 70 | 71 | /// 72 | /// Table Body CSS 73 | /// 74 | string TableBodyClass { get; set; } 75 | 76 | /// 77 | /// Table Head CSS 78 | /// 79 | string TableHeadClass { get; set; } 80 | 81 | /// 82 | /// Redraws Table without Getting Data 83 | /// 84 | void Refresh(); 85 | 86 | /// 87 | /// Gets Data and redraws the Table 88 | /// 89 | Task UpdateAsync(); 90 | /// 91 | /// Open/Close detail view in specified row. 92 | /// 93 | /// number of row to toggle detail view 94 | /// true for openening detail view, false for closing detail view 95 | void ToggleDetailView(int row, bool open); 96 | 97 | /// 98 | /// Open/Close all detail views. 99 | /// 100 | /// true for openening detail view, false for closing detail view 101 | void ToggleAllDetailsView(bool open); 102 | 103 | /// 104 | /// Set the EmptyDataTemplate for the table 105 | /// 106 | /// 107 | void SetEmptyDataTemplate(EmptyDataTemplate template); 108 | 109 | /// 110 | /// Set the LoadingDataTemplate for the table 111 | /// 112 | /// 113 | void SetLoadingDataTemplate(LoadingDataTemplate template); 114 | 115 | 116 | /// 117 | /// Select Type: None, Single or Multiple 118 | /// 119 | public SelectionType SelectionType { get; set; } 120 | 121 | /// 122 | /// Search all columns for the specified string, supports spaces as a delimiter 123 | /// 124 | string GlobalSearch { get; set; } 125 | 126 | /// 127 | /// Shows Search Bar above the table 128 | /// 129 | bool ShowSearchBar { get; set; } 130 | 131 | /// 132 | /// Show or hide table footer. Hide by default. 133 | /// 134 | bool ShowFooter { get; set; } 135 | 136 | /// 137 | /// Set Table Page Size 138 | /// 139 | /// 140 | Task SetPageSizeAsync(int pageSize); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/BlazorTable/Interfaces/ITableGen.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace BlazorTable 7 | { 8 | /// 9 | /// BlazorTable Interface 10 | /// 11 | /// 12 | public interface ITable : ITable 13 | { 14 | /// 15 | /// List of All Available Columns 16 | /// 17 | List> Columns { get; } 18 | 19 | /// 20 | /// Adds a Column to the Table 21 | /// 22 | /// 23 | void AddColumn(IColumn column); 24 | 25 | /// 26 | /// Removes a Column from the Table 27 | /// 28 | /// 29 | void RemoveColumn(IColumn column); 30 | 31 | /// 32 | /// IQueryable data source to display in the table 33 | /// 34 | IQueryable ItemsQueryable { get; set; } 35 | 36 | /// 37 | /// Collection to display in the table 38 | /// 39 | IEnumerable Items { get; set; } 40 | 41 | /// 42 | /// Collection of filtered items 43 | /// 44 | IEnumerable FilteredItems { get; } 45 | 46 | /// 47 | /// Action performed when the row is clicked 48 | /// 49 | Action RowClickAction { get; set; } 50 | 51 | /// 52 | /// Collection of selected items 53 | /// 54 | List SelectedItems { get; } 55 | 56 | /// 57 | /// Set the SetDetailTemplate for the table 58 | /// 59 | /// 60 | void SetDetailTemplate(DetailTemplate template); 61 | 62 | /// 63 | /// Add custom row to table 64 | /// 65 | /// Custom row to add 66 | void AddCustomRow(CustomRow customRow); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/BlazorTable/LinkerConfig.xml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/BlazorTable/LocalizedDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Reflection; 4 | using System.Resources; 5 | 6 | namespace BlazorTable 7 | { 8 | public class LocalizedDescriptionAttribute : DescriptionAttribute 9 | { 10 | private readonly string _resourceKey; 11 | private readonly ResourceManager _resource; 12 | public LocalizedDescriptionAttribute(string resourceKey, Type resourceType) 13 | { 14 | _resource = new ResourceManager(resourceType); 15 | _resourceKey = resourceKey; 16 | } 17 | 18 | public override string Description 19 | { 20 | get 21 | { 22 | string displayName = _resource.GetString(_resourceKey); 23 | 24 | return string.IsNullOrEmpty(displayName) 25 | ? string.Format("[[{0}]]", _resourceKey) 26 | : displayName; 27 | } 28 | } 29 | } 30 | 31 | public static class EnumExtensions 32 | { 33 | public static string GetDescription(this Enum enumValue) 34 | { 35 | FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString()); 36 | 37 | DescriptionAttribute[] attributes = 38 | (DescriptionAttribute[])fi.GetCustomAttributes( 39 | typeof(DescriptionAttribute), 40 | false); 41 | 42 | if (attributes != null && 43 | attributes.Length > 0) 44 | return attributes[0].Description; 45 | else 46 | return enumValue.ToString(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/BlazorTable/_Imports.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorTable 2 | @using System.Net.Http 3 | @using Microsoft.AspNetCore.Components 4 | @using Microsoft.AspNetCore.Components.Web -------------------------------------------------------------------------------- /src/BlazorTable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable/icon.png -------------------------------------------------------------------------------- /src/BlazorTable/wwwroot/images/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable/wwwroot/images/filter.png -------------------------------------------------------------------------------- /src/BlazorTable/wwwroot/images/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable/wwwroot/images/minus.png -------------------------------------------------------------------------------- /src/BlazorTable/wwwroot/images/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable/wwwroot/images/plus.png -------------------------------------------------------------------------------- /src/BlazorTable/wwwroot/images/sort-asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable/wwwroot/images/sort-asc.png -------------------------------------------------------------------------------- /src/BlazorTable/wwwroot/images/sort-desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvanJosipovic/BlazorTable/758cdeef0ffda428889ced0e13d34007be29bd54/src/BlazorTable/wwwroot/images/sort-desc.png --------------------------------------------------------------------------------