├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── release-drafter.yml └── workflows │ ├── ci-main.yml │ ├── ci-pr.yml │ └── release-drafter.yml ├── .gitignore ├── Blazored.Modal.sln ├── LICENSE ├── README.md ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── configuration │ │ ├── _category_.json │ │ ├── background-click.md │ │ ├── close-button.md │ │ ├── focus-trap.md │ │ ├── hide-header.md │ │ ├── position.md │ │ └── size.md │ ├── getting-started.md │ ├── styling │ │ ├── _category_.json │ │ ├── animation.md │ │ ├── custom-layout.md │ │ ├── custom-overlay.md │ │ └── modal-style.md │ └── usage │ │ ├── _category_.json │ │ ├── closing-programmatically.md │ │ ├── modal-reference.md │ │ ├── multiple-modals.md │ │ ├── passing-data.md │ │ └── returning-data.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ └── markdown-page.md └── static │ ├── .nojekyll │ └── img │ ├── favicon.ico │ └── logo.svg ├── samples ├── InteractiveServer │ ├── Components │ │ ├── App.razor │ │ ├── Layout │ │ │ ├── MainLayout.razor │ │ │ ├── MainLayout.razor.css │ │ │ ├── NavMenu.razor │ │ │ └── NavMenu.razor.css │ │ ├── Pages │ │ │ ├── Animation.razor │ │ │ ├── BackgroundCancel.razor │ │ │ ├── CustomLayout.razor │ │ │ ├── CustomOverlay.razor │ │ │ ├── CustomStyle.razor │ │ │ ├── Error.razor │ │ │ ├── HideCloseButton.razor │ │ │ ├── Home.razor │ │ │ ├── LongRunningTask.razor │ │ │ ├── MultipleModals.razor │ │ │ ├── PassDataToModal.razor │ │ │ ├── Position.razor │ │ │ ├── ReturnDataFromModal.razor │ │ │ └── Size.razor │ │ ├── Routes.razor │ │ ├── Shared │ │ │ ├── Confirm.razor │ │ │ ├── CustomBootstrapModal.razor │ │ │ ├── DisplayMessage.razor │ │ │ ├── FullContentPage.razor │ │ │ ├── Loading.razor │ │ │ ├── MessageForm.razor │ │ │ ├── TestClass.cs │ │ │ ├── YesNoPrompt.razor │ │ │ └── YesNoPromptAnimation.razor │ │ └── _Imports.razor │ ├── InteractiveServer.csproj │ ├── Program.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ ├── app.css │ │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── favicon.png └── InteractiveWebAssembly │ ├── App.razor │ ├── InteractiveWebAssembly.csproj │ ├── Layout │ ├── MainLayout.razor │ ├── MainLayout.razor.css │ ├── NavMenu.razor │ └── NavMenu.razor.css │ ├── Pages │ ├── Animation.razor │ ├── BackGroundCancel.razor │ ├── CustomLayout.razor │ ├── CustomOverlay.razor │ ├── CustomStyle.razor │ ├── HideCloseButton.razor │ ├── Home.razor │ ├── LongRunningTask.razor │ ├── MultipleModals.razor │ ├── NavigateAfterModal.razor │ ├── PassDataToModal.razor │ ├── Position.razor │ ├── ReturnDataFromModal.razor │ └── Size.razor │ ├── Program.cs │ ├── Shared │ ├── CodeBlock.razor │ ├── CodeBlock.razor.js │ ├── Confirm.razor │ ├── CustomBootstrapModal.razor │ ├── DisplayMessage.razor │ ├── FullContentPage.razor │ ├── Loading.razor │ ├── MessageForm.razor │ ├── YesNoPrompt.razor │ └── YesNoPromptAnimation.razor │ ├── _Imports.razor │ └── wwwroot │ ├── .nojekyll │ ├── 404.html │ ├── css │ ├── app.css │ └── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── favicon.png │ ├── icon-192.png │ ├── index.html │ └── js │ ├── cshtml-razor.min.js │ ├── decode.js │ └── highlight.min.js ├── screenshot.png ├── src └── Blazored.Modal │ ├── Blazored.Modal.csproj │ ├── BlazoredModal.razor │ ├── BlazoredModal.razor.js │ ├── BlazoredModalInstance.razor │ ├── BlazoredModalInstance.razor.cs │ ├── BlazoredModalInstance.razor.css │ ├── CascadingBlazoredModal.razor │ ├── Configuration │ ├── IModalReference.cs │ ├── ModalAnimationType.cs │ ├── ModalOptions.cs │ ├── ModalParameters.cs │ ├── ModalPosition.cs │ ├── ModalReference.cs │ └── ModalSize.cs │ ├── FocusTrap.razor │ ├── ServiceCollectionExtensions.cs │ ├── Services │ ├── IModalService.cs │ ├── ModalResult.cs │ └── ModalService.cs │ ├── _Imports.razor │ └── icon.png └── tests └── Blazored.Modal.Tests ├── Assets ├── MockNavigationManager.cs └── TestComponent.razor ├── Blazored.Modal.Tests.csproj ├── DisplayTests.cs ├── ModalOptionsTests.cs └── _Imports.razor /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [chrissainty] 2 | -------------------------------------------------------------------------------- /.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, Triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Hosting Model (is this issue happening with a certain hosting model?):** 27 | - Blazor Server 28 | - Blazor WebAssembly 29 | - etc... 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature Request]" 5 | labels: Feature Request, Triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | template: | 4 | # What's Changed 5 | 6 | $CHANGES 7 | categories: 8 | - title: '🚀 Features' 9 | labels: 10 | - 'Feature' 11 | - title: '🐛 Bug Fixes' 12 | labels: 13 | - 'Bug' 14 | - title: '🧰 Maintenance' 15 | label: 'Maintenance' 16 | - title: '📖 Documentation' 17 | label: 'Docs' 18 | change-template: '- (#$NUMBER) $TITLE - Thanks to @$AUTHOR ' -------------------------------------------------------------------------------- /.github/workflows/ci-main.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test Main 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | release: 7 | types: [published] 8 | 9 | env: 10 | NETCORE_VERSION: "8.0.x" 11 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 12 | DOTNET_CLI_TELEMETRY_OPTOUT: true 13 | PROJECT_NAME: Modal 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | NUGET_FEED: https://api.nuget.org/v3/index.json 16 | NUGET_KEY: ${{ secrets.NUGET_KEY }} 17 | 18 | jobs: 19 | build: 20 | name: Build, test and pack 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v2 25 | 26 | - name: Setup .NET Core ${{ env.NETCORE_VERSION }} 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: ${{ env.NETCORE_VERSION }} 30 | env: 31 | NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 32 | 33 | - name: Restore 34 | run: dotnet restore 35 | 36 | - name: Build Modal 37 | run: dotnet build -c Release --no-restore src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.csproj 38 | 39 | - name: Test 40 | run: dotnet test -c Release --no-restore --verbosity normal 41 | 42 | deploy: 43 | needs: build 44 | if: github.event_name == 'release' 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v2 48 | 49 | - name: Setup .NET Core 50 | uses: actions/setup-dotnet@v1 51 | with: 52 | dotnet-version: ${{ env.NETCORE_VERSION }} 53 | 54 | - name: Create Release NuGet package 55 | run: | 56 | arrTag=(${GITHUB_REF//\// }) 57 | VERSION="${arrTag[2]}" 58 | echo Version: $VERSION 59 | VERSION="${VERSION//v}" 60 | echo Clean Version: $VERSION 61 | dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.csproj 62 | 63 | - name: Push to NuGet Feed 64 | run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --api-key $NUGET_KEY --skip-duplicate 65 | 66 | docs: 67 | name: Publish docs 68 | runs-on: ubuntu-latest 69 | defaults: 70 | run: 71 | working-directory: ./docs 72 | steps: 73 | - uses: actions/checkout@v2 74 | - uses: actions/setup-node@v3 75 | with: 76 | node-version: 18 77 | cache: npm 78 | cache-dependency-path: './docs/package-lock.json' 79 | 80 | - name: Install dependencies 81 | run: npm ci 82 | - name: Build website 83 | run: npm run build 84 | 85 | # Popular action to deploy to GitHub Pages: 86 | # Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus 87 | - name: Deploy to GitHub Pages 88 | uses: peaceiris/actions-gh-pages@v3 89 | with: 90 | github_token: ${{ secrets.GITHUB_TOKEN }} 91 | # Build output to publish to the `gh-pages` branch: 92 | publish_dir: ./docs/build 93 | # The following lines assign commit authorship to the official 94 | # GH-Actions bot for deploys to `gh-pages` branch: 95 | # https://github.com/actions/checkout/issues/13#issuecomment-724415212 96 | # The GH actions bot is used by default if you didn't specify the two fields. 97 | # You can swap them out with your own user credentials. 98 | user_name: github-actions[bot] 99 | user_email: 41898282+github-actions[bot]@users.noreply.github.com 100 | -------------------------------------------------------------------------------- /.github/workflows/ci-pr.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test PR 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | 7 | env: 8 | PROJECT_NAME: Blazored.Modal 9 | NETCORE_VERSION: '8.0.x' 10 | 11 | jobs: 12 | build: 13 | name: Build and test 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Setting up .NET Core SDK ${{ env.NETCORE_VERSION }}... 19 | uses: actions/setup-dotnet@v1 20 | with: 21 | dotnet-version: ${{ env.NETCORE_VERSION }} 22 | 23 | - name: Restoring packages... 24 | run: dotnet restore 25 | 26 | - name: Building project... 27 | run: dotnet build --configuration Release --no-restore src/$PROJECT_NAME/$PROJECT_NAME.csproj 28 | 29 | - name: Testing... 30 | run: dotnet test --no-restore --verbosity normal 31 | 32 | docs-test-deploy: 33 | name: Docs test deployment 34 | runs-on: ubuntu-latest 35 | defaults: 36 | run: 37 | working-directory: ./docs 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-node@v3 41 | with: 42 | node-version: 18 43 | cache: npm 44 | cache-dependency-path: './docs/package-lock.json' 45 | 46 | - name: Install dependencies 47 | run: npm ci 48 | - name: Test build website 49 | run: npm run build -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@v5 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # Custom rules 7 | src/Blazored.Modal/wwwroot/*.js 8 | src/Blazored.Modal/wwwroot/*.map 9 | .DS_Store 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Build results 21 | [Dd]ebug/ 22 | [Dd]ebugPublic/ 23 | [Rr]elease/ 24 | [Rr]eleases/ 25 | x64/ 26 | x86/ 27 | bld/ 28 | [Bb]in/ 29 | [Oo]bj/ 30 | [Ll]og/ 31 | 32 | # Visual Studio 2015/2017 cache/options directory 33 | .vs/ 34 | # Uncomment if you have tasks that create the project's static files in wwwroot 35 | #wwwroot/ 36 | 37 | # Visual Studio 2017 auto generated files 38 | Generated\ Files/ 39 | 40 | # MSTest test Results 41 | [Tt]est[Rr]esult*/ 42 | [Bb]uild[Ll]og.* 43 | 44 | # NUNIT 45 | *.VisualState.xml 46 | TestResult.xml 47 | 48 | # Build Results of an ATL Project 49 | [Dd]ebugPS/ 50 | [Rr]eleasePS/ 51 | dlldata.c 52 | 53 | # Benchmark Results 54 | BenchmarkDotNet.Artifacts/ 55 | 56 | # .NET Core 57 | project.lock.json 58 | project.fragment.lock.json 59 | artifacts/ 60 | **/Properties/launchSettings.json 61 | 62 | # StyleCop 63 | StyleCopReport.xml 64 | 65 | # Files built by Visual Studio 66 | *_i.c 67 | *_p.c 68 | *_i.h 69 | *.ilk 70 | *.meta 71 | *.obj 72 | *.iobj 73 | *.pch 74 | *.pdb 75 | *.ipdb 76 | *.pgc 77 | *.pgd 78 | *.rsp 79 | *.sbr 80 | *.tlb 81 | *.tli 82 | *.tlh 83 | *.tmp 84 | *.tmp_proj 85 | *.log 86 | *.vspscc 87 | *.vssscc 88 | .builds 89 | *.pidb 90 | *.svclog 91 | *.scc 92 | 93 | # Chutzpah Test files 94 | _Chutzpah* 95 | 96 | # Visual C++ cache files 97 | ipch/ 98 | *.aps 99 | *.ncb 100 | *.opendb 101 | *.opensdf 102 | *.sdf 103 | *.cachefile 104 | *.VC.db 105 | *.VC.VC.opendb 106 | 107 | # Visual Studio profiler 108 | *.psess 109 | *.vsp 110 | *.vspx 111 | *.sap 112 | 113 | # Visual Studio Trace Files 114 | *.e2e 115 | 116 | # TFS 2012 Local Workspace 117 | $tf/ 118 | 119 | # Guidance Automation Toolkit 120 | *.gpState 121 | 122 | # ReSharper is a .NET coding add-in 123 | _ReSharper*/ 124 | *.[Rr]e[Ss]harper 125 | *.DotSettings.user 126 | 127 | # JustCode is a .NET coding add-in 128 | .JustCode 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # The packages folder can be ignored because of Package Restore 188 | **/[Pp]ackages/* 189 | # except build/, which is used as an MSBuild target. 190 | !**/[Pp]ackages/build/ 191 | # Uncomment if necessary however generally it will be regenerated when needed 192 | #!**/[Pp]ackages/repositories.config 193 | # NuGet v3's project.json files produces more ignorable files 194 | *.nuget.props 195 | *.nuget.targets 196 | 197 | # Microsoft Azure Build Output 198 | csx/ 199 | *.build.csdef 200 | 201 | # Microsoft Azure Emulator 202 | ecf/ 203 | rcf/ 204 | 205 | # Windows Store app package directories and files 206 | AppPackages/ 207 | BundleArtifacts/ 208 | Package.StoreAssociation.xml 209 | _pkginfo.txt 210 | *.appx 211 | 212 | # Visual Studio cache files 213 | # files ending in .cache can be ignored 214 | *.[Cc]ache 215 | # but keep track of directories ending in .cache 216 | !*.[Cc]ache/ 217 | 218 | # Others 219 | ClientBin/ 220 | ~$* 221 | *~ 222 | *.dbmdl 223 | *.dbproj.schemaview 224 | *.jfm 225 | *.pfx 226 | *.publishsettings 227 | orleans.codegen.cs 228 | 229 | # Including strong name files can present a security risk 230 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 231 | #*.snk 232 | 233 | # Since there are multiple workflows, uncomment next line to ignore bower_components 234 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 235 | #bower_components/ 236 | 237 | # RIA/Silverlight projects 238 | Generated_Code/ 239 | 240 | # Backup & report files from converting an old project file 241 | # to a newer Visual Studio version. Backup files are not needed, 242 | # because we have git ;-) 243 | _UpgradeReport_Files/ 244 | Backup*/ 245 | UpgradeLog*.XML 246 | UpgradeLog*.htm 247 | ServiceFabricBackup/ 248 | *.rptproj.bak 249 | 250 | # SQL Server files 251 | *.mdf 252 | *.ldf 253 | *.ndf 254 | 255 | # Business Intelligence projects 256 | *.rdl.data 257 | *.bim.layout 258 | *.bim_*.settings 259 | *.rptproj.rsuser 260 | 261 | # Microsoft Fakes 262 | FakesAssemblies/ 263 | 264 | # GhostDoc plugin setting file 265 | *.GhostDoc.xml 266 | 267 | # Node.js Tools for Visual Studio 268 | .ntvs_analysis.dat 269 | node_modules/ 270 | 271 | # Visual Studio 6 build log 272 | *.plg 273 | 274 | # Visual Studio 6 workspace options file 275 | *.opt 276 | 277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 278 | *.vbw 279 | 280 | # Visual Studio LightSwitch build output 281 | **/*.HTMLClient/GeneratedArtifacts 282 | **/*.DesktopClient/GeneratedArtifacts 283 | **/*.DesktopClient/ModelManifest.xml 284 | **/*.Server/GeneratedArtifacts 285 | **/*.Server/ModelManifest.xml 286 | _Pvt_Extensions 287 | 288 | # Paket dependency manager 289 | .paket/paket.exe 290 | paket-files/ 291 | 292 | # FAKE - F# Make 293 | .fake/ 294 | 295 | # JetBrains Rider 296 | .idea/ 297 | *.sln.iml 298 | 299 | # CodeRush 300 | .cr/ 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | -------------------------------------------------------------------------------- /Blazored.Modal.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28531.58 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.Modal", "src\Blazored.Modal\Blazored.Modal.csproj", "{997CB0B4-2B3F-4625-9259-A560CD82AC8C}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{AC842EAA-F630-4597-A0FC-6758FE9F6268}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E8AD9151-AB7E-4531-A1DF-8952B1D5580D}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.md = README.md 13 | .gitignore = .gitignore 14 | .github\workflows\ci-main.yml = .github\workflows\ci-main.yml 15 | .github\workflows\ci-pr.yml = .github\workflows\ci-pr.yml 16 | EndProjectSection 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DF45C5D5-16E0-47FC-A764-08ED8AF713E2}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazored.Modal.Tests", "tests\Blazored.Modal.Tests\Blazored.Modal.Tests.csproj", "{14B17729-C172-4F0C-90FF-65479AF6408F}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteractiveWebAssembly", "samples\InteractiveWebAssembly\InteractiveWebAssembly.csproj", "{E2C5884F-4FE4-4A5B-B9A3-046E9C546454}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteractiveServer", "samples\InteractiveServer\InteractiveServer.csproj", "{393CCD7E-66D0-4BB5-A087-425F1B643EAF}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Debug|x64 = Debug|x64 30 | Debug|x86 = Debug|x86 31 | Release|Any CPU = Release|Any CPU 32 | Release|x64 = Release|x64 33 | Release|x86 = Release|x86 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Debug|x64.Build.0 = Debug|Any CPU 40 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Debug|x86.Build.0 = Debug|Any CPU 42 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Release|x64.ActiveCfg = Release|Any CPU 45 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Release|x64.Build.0 = Release|Any CPU 46 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Release|x86.ActiveCfg = Release|Any CPU 47 | {997CB0B4-2B3F-4625-9259-A560CD82AC8C}.Release|x86.Build.0 = Release|Any CPU 48 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Debug|x64.ActiveCfg = Debug|Any CPU 51 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Debug|x64.Build.0 = Debug|Any CPU 52 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Debug|x86.ActiveCfg = Debug|Any CPU 53 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Debug|x86.Build.0 = Debug|Any CPU 54 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Release|x64.ActiveCfg = Release|Any CPU 57 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Release|x64.Build.0 = Release|Any CPU 58 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Release|x86.ActiveCfg = Release|Any CPU 59 | {14B17729-C172-4F0C-90FF-65479AF6408F}.Release|x86.Build.0 = Release|Any CPU 60 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Debug|x64.ActiveCfg = Debug|Any CPU 63 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Debug|x64.Build.0 = Debug|Any CPU 64 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Debug|x86.ActiveCfg = Debug|Any CPU 65 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Debug|x86.Build.0 = Debug|Any CPU 66 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Release|x64.ActiveCfg = Release|Any CPU 69 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Release|x64.Build.0 = Release|Any CPU 70 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Release|x86.ActiveCfg = Release|Any CPU 71 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454}.Release|x86.Build.0 = Release|Any CPU 72 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Debug|x64.ActiveCfg = Debug|Any CPU 75 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Debug|x64.Build.0 = Debug|Any CPU 76 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Debug|x86.ActiveCfg = Debug|Any CPU 77 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Debug|x86.Build.0 = Debug|Any CPU 78 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Release|Any CPU.Build.0 = Release|Any CPU 80 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Release|x64.ActiveCfg = Release|Any CPU 81 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Release|x64.Build.0 = Release|Any CPU 82 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Release|x86.ActiveCfg = Release|Any CPU 83 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF}.Release|x86.Build.0 = Release|Any CPU 84 | EndGlobalSection 85 | GlobalSection(SolutionProperties) = preSolution 86 | HideSolutionNode = FALSE 87 | EndGlobalSection 88 | GlobalSection(NestedProjects) = preSolution 89 | {14B17729-C172-4F0C-90FF-65479AF6408F} = {DF45C5D5-16E0-47FC-A764-08ED8AF713E2} 90 | {E2C5884F-4FE4-4A5B-B9A3-046E9C546454} = {AC842EAA-F630-4597-A0FC-6758FE9F6268} 91 | {393CCD7E-66D0-4BB5-A087-425F1B643EAF} = {AC842EAA-F630-4597-A0FC-6758FE9F6268} 92 | EndGlobalSection 93 | GlobalSection(ExtensibilityGlobals) = postSolution 94 | SolutionGuid = {FB2D90F4-21CF-4118-82E7-479E89C15FB3} 95 | EndGlobalSection 96 | EndGlobal 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Blazored 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 | # Blazored Modal 2 | 3 | A powerful and customizable modal implementation for [Blazor](https://blazor.net) applications. 4 | 5 | [![Nuget version](https://img.shields.io/nuget/v/Blazored.Modal.svg?logo=nuget)](https://www.nuget.org/packages/Blazored.Modal/) 6 | [![Nuget downloads](https://img.shields.io/nuget/dt/Blazored.Modal?logo=nuget)](https://www.nuget.org/packages/Blazored.Modal/) 7 | ![Build & Test Main](https://github.com/Blazored/Modal/workflows/Build%20&%20Test%20Main/badge.svg) 8 | 9 | ![Screenshot of Blazored Modal](https://user-images.githubusercontent.com/6171719/205016381-79dd5c65-8090-4bac-91e9-b4aee6e246d6.png) 10 | 11 | ## Documentation 12 | For information on getting Blazored Modal setup in your application, as well as the many customisation options, please checkout our [docs](https://blazored.github.io/Modal/). 13 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/configuration/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Configuration", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "The configuration options available for Blazored Modal." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/configuration/background-click.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Background Click 6 | By default, a modal is cancelled if the user clicks anywhere outside the modal. This behavior can be disabled by setting `DisableBackgroundCancel` option to `true`. 7 | 8 | #### Configuring for all modals 9 | ```razor 10 | 11 | ``` 12 | 13 | #### Configuring for a single modal 14 | 15 | ```csharp 16 | var options = new ModalOptions() 17 | { 18 | DisableBackgroundCancel = true 19 | }; 20 | 21 | Modal.Show("Are you sure?", options); 22 | ``` -------------------------------------------------------------------------------- /docs/docs/configuration/close-button.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Close Button 6 | 7 | By default, modals will display a close button in the top right corner. However, if you prefer, you can remove the close button using the `HideCloseButton` option. 8 | 9 | :::info 10 | 11 | If you're hiding the header using the [`HideHeader`](./hide-header) option, the `HideCloseButton` option will have no effect. 12 | 13 | ::: 14 | 15 | ### Configuring for all modals 16 | ```razor 17 | 18 | ``` 19 | 20 | ### Configuring for a single modal 21 | 22 | ```csharp 23 | var options = new ModalOptions() 24 | { 25 | HideCloseButton = true 26 | }; 27 | 28 | Modal.Show("Are you sure?", options); 29 | ``` -------------------------------------------------------------------------------- /docs/docs/configuration/focus-trap.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Focus Trap 6 | Blazored Modal comes with a built-in focus trap. Focus traps are an important feature for accessability as they stop focus dropping behind the modal when pressing the *tab* key. 7 | 8 | If you do wish to disable this feature, you can do so using the `ActivateFocusTrap` option and set it to `false`. 9 | 10 | #### Configuring for all modals 11 | ```razor 12 | 13 | ``` 14 | 15 | #### Configuring for a single modal 16 | 17 | ```csharp 18 | var options = new ModalOptions() 19 | { 20 | ActivateFocusTrap = false 21 | }; 22 | 23 | Modal.Show("Are you sure?", options); 24 | ``` -------------------------------------------------------------------------------- /docs/docs/configuration/hide-header.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Hide Header 6 | 7 | When displaying a modal, a header is rendered showing the title of the modal and the close button. However, if you're planning to render your own header as part of the component being displayed, or you just don't want a header, you can turn it off. 8 | 9 | ### Configuring for all modals 10 | ```razor 11 | 12 | ``` 13 | 14 | ### Configuring for a single modal 15 | 16 | ```csharp 17 | var options = new ModalOptions() 18 | { 19 | HideHeader = true 20 | }; 21 | 22 | Modal.Show("Are you sure?", options); 23 | ``` -------------------------------------------------------------------------------- /docs/docs/configuration/position.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Position 6 | 7 | Modals are shown in the center of the screen near the top of the viewport. This can be customised via the `Position` option using the `ModalPosition` enum. The options available are: 8 | 9 | - Top Left 10 | - Top Right 11 | - Top Center 12 | - Middle 13 | - Bottom Left 14 | - Bottom Right 15 | - Custom 16 | 17 | ### Using a predefined position 18 | To configure Blazored Modal to use a predefined position, see the code examples below. 19 | 20 | 21 | #### Configuring for all modals 22 | ```razor 23 | 24 | ``` 25 | 26 | #### Configuring for a single modal 27 | 28 | ```csharp 29 | var options = new ModalOptions() 30 | { 31 | Position = ModalPosition.TopLeft 32 | }; 33 | 34 | Modal.Show("Are you sure?", options); 35 | ``` 36 | 37 | ### Using a custom position 38 | When using the `Custom` position setting, you will need to provide a CSS class, via the `PositionCustomClass` option, that sets the modal position. 39 | 40 | #### Configuring for all modals 41 | 42 | ```razor 43 | 44 | ``` 45 | 46 | #### Configuring for a single modal 47 | 48 | ```csharp 49 | var options = new ModalOptions() 50 | { 51 | Position = ModalPosition.Custom, 52 | PositionCustomClass = "custom-modal-position" 53 | }; 54 | 55 | Modal.Show("Are you sure?", options); 56 | ``` -------------------------------------------------------------------------------- /docs/docs/configuration/size.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Size 6 | 7 | The width of a modal can be configured using the `Size` option. Blazored Modal has a range of built-in sizes as well as the ability to define a custom size. 8 | 9 | - Automatic (Size is determined by the content) 10 | - Small (300px) 11 | - Medium (500px) 12 | - Large (800px) 13 | - Extra Large (1140px) 14 | - Custom 15 | 16 | If no `Size` option is specified the default size is `Medium`. 17 | 18 | ### Using a predefined size 19 | To configure Blazored Modal to use a predefined size, see the code examples below. 20 | 21 | #### Configuring for all modals 22 | To set the `Size` for all modals in your application, you can use the `Size` parameter on the `CascadingBlazoredModal` component. 23 | 24 | ```razor 25 | 26 | ``` 27 | 28 | #### Configuring for a single modal 29 | To set the `Size` for a single modal, use the `ModalOptions` type and pass it into the `Show` method. 30 | 31 | ```csharp 32 | var options = new ModalOptions() 33 | { 34 | Size = ModalSize.Large 35 | }; 36 | 37 | Modal.Show("Are you sure?", options); 38 | ``` 39 | 40 | ### Using a custom size 41 | If you don't want to use one of the default sizes provided out-of-the-box, you can set the `Size` to `ModalSize.Custom`. When using the `Custom` option, you will need to provide a CSS class which sets the size of the modal. Do this using the `SizeCustomClass` option. 42 | 43 | #### Configuring for all modals 44 | To set a custom size for all modals in your application, use the `CascadingBlazoredModal` component. 45 | 46 | ```razor 47 | 48 | ``` 49 | 50 | #### Configuring for a single modal 51 | To set a custom size for a single modal, use the `ModalOptions` type and pass it into the `Show` method. 52 | 53 | ```csharp 54 | var options = new ModalOptions() 55 | { 56 | Size = ModalSize.Custom, 57 | SizeCustomClass = "custom-modal" 58 | }; 59 | 60 | Modal.Show("Are you sure?", options); 61 | ``` -------------------------------------------------------------------------------- /docs/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: / 3 | title: Getting Started 4 | sidebar_position: 1 5 | --- 6 | 7 | # Blazored Modal Docs 8 | 9 | [![Nuget](https://img.shields.io/nuget/v/blazored.modal.svg?logo=nuget)](https://www.nuget.org/packages/Blazored.Modal/) 10 | [![Issues](https://img.shields.io/github/issues/Blazored/Modal?logo=github)](https://github.com/Blazored/Modal/issues) 11 | ![Nuget](https://img.shields.io/nuget/dt/Blazored.Modal?logo=nuget) 12 | 13 | Blazored Modal is a powerful and customizable modal implementation for [Blazor applications](https://blazor.net). 14 | 15 | ## Getting Started 16 | 17 | The first step is to install the Blazored.Modal NuGet package into your project. You can install the package via the nuget package manager, just search for *Blazored.Modal*. You can also install via powershell using the following command. 18 | 19 | ```powershell 20 | Install-Package Blazored.Modal 21 | ``` 22 | 23 | Or via the dotnet CLI. 24 | 25 | ```bash 26 | dotnet add package Blazored.Modal 27 | ``` 28 | 29 | ### Register Services 30 | 31 | Blazored Modal uses a service to coordinate modals. To register this service you need to add the following using statement and call to `AddBlazoredModal` in your applications `Program.cs` file. 32 | 33 | ```csharp 34 | using Blazored.Modal; 35 | ``` 36 | 37 | ```csharp 38 | builder.Services.AddBlazoredModal(); 39 | ``` 40 | 41 | :::info 42 | 43 | The above code assumes the use of top level statements. If your application is not using them, please add the call to `AddBlazoredModal` where you're registering services for your app. 44 | 45 | ::: 46 | 47 | ### Add Imports 48 | 49 | To avoid having to add using statements for Blazored Modal to lots of components in your project, it's recommended that you add the following to your root *_Imports.razor* file. This will make the following usings available to all component in that project. 50 | 51 | ```razor 52 | @using Blazored.Modal 53 | @using Blazored.Modal.Services 54 | ``` 55 | 56 | ### Add CSS Reference 57 | 58 | Blazored Modal uses CSS isolation. If your application is already using CSS isolation then the styles for Modal will be included automatically and you can skip this step. However, if your application isn't using isolated CSS, you will need to add a reference to the CSS bundle. You can checkout the [Microsoft Docs](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/css-isolation?view=aspnetcore-6.0#css-isolation-bundling) for additional details. 59 | 60 | ```html 61 | 62 | ``` 63 | 64 | ### Add the CascadingBlazoredModal Component 65 | 66 | The `` component cascades an instance of the `IModalService` to all decendant components. This should be added to the root component of your application (usually `App.razor`) wrapping the Router as per the example below. 67 | 68 | ```html 69 | 70 | 71 | ... 72 | 73 | 74 | ``` 75 | 76 | ### Displaying a modal 77 | 78 | In order to display a modal, you must define a cascading parameter on the component that will invoke the modal: 79 | 80 | ```csharp 81 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 82 | ``` 83 | 84 | Once you have the cascading parameter setup, you can call the `Show` method on the `IModalService` passing in the title for the modal and the type of the component you want the modal to display. For example, if you have a component called `Movies` that you want to display in a modal from the `Home` component on a button click. 85 | 86 | ```html 87 | @page "/" 88 | 89 |

Hello, world!

90 | 91 | Welcome to Blazored Modal. 92 | 93 | 94 | 95 | @code { 96 | [CascadingParameter] public IModalService Modal { get; set; } 97 | } 98 | ``` -------------------------------------------------------------------------------- /docs/docs/styling/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Styling", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "How to customise the style of Blazored Modal" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/styling/animation.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Animation 6 | By default, the modal is shown with a subtle fade in out animation. However, this can be disabled so the modal shows and hides immediately. 7 | 8 | #### Configuring for all modals 9 | ```razor 10 | 11 | ``` 12 | 13 | 14 | #### Configuring for a single modal 15 | ```csharp 16 | var options = new ModalOptions() 17 | { 18 | AnimationType = ModalAnimationType.None 19 | }; 20 | 21 | Modal.Show("Are you sure?", options); 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/docs/styling/custom-layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Custom Layout 6 | Blazored Modal provides a default look, this includes an overlay that covers the viewport and the default modal styling. However, you can remove all of this using the custom layout option. When set to `true` all of the default UI is removed and you become responsible for providing an alternative one. This option allows complete control over the look and feel of the modal. 7 | 8 | An example usecase would be an application that used the bootstrap UI library and wanted the modal and overlay to match it's design. This could be achieved in the following way. 9 | 10 | Define a component to be displayed by Blazored Modal that uses Bootstraps HTML structure and CSS classes. 11 | 12 | ```razor title="BootstrapModal.razor" 13 | 33 | 34 | @code { 35 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 36 | 37 | [Parameter] public string? Message { get; set; } 38 | 39 | private async Task Close() => await BlazoredModal.CloseAsync(ModalResult.Ok(true)); 40 | private async Task Cancel() => await BlazoredModal.CancelAsync(); 41 | } 42 | ``` 43 | 44 | Show the `BootstrapModal` component using Blazored Modal, setting `CustomLayout` to `true`. 45 | 46 | ```csharp 47 | var options = new ModalOptions 48 | { 49 | UseCustomLayout = true 50 | }; 51 | 52 | var parameters = new ModalParameters() 53 | .Add(nameof(BootstrapModal.Message), "Hello Bootstrap modal!!"); 54 | 55 | Modal.Show("Bootstrap Modal", parameters, options); 56 | ``` -------------------------------------------------------------------------------- /docs/docs/styling/custom-overlay.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Custom Overlay 6 | The overlay can be customised by providing one or more custom CSS classes to augment or overwrite the default style. Note that you may need to add the `!important` keyword to some properties due the the use of scoped CSS, which can create a higher specificity for the default styles. 7 | 8 | The following example shows how to define a CSS class that overrides the default overlay background colour. 9 | 10 | ```css title="my-app.css" 11 | .custom-modal-overlay { 12 | background-color: rgba(255, 0, 0, 0.5) !important; 13 | } 14 | ``` 15 | 16 | This can then be applied to your modal with either of the following options. 17 | 18 | #### Configuring for all modals 19 | ```razor 20 | 21 | ``` 22 | 23 | #### Configuring for a single modal 24 | 25 | ```csharp 26 | var options = new ModalOptions() 27 | { 28 | OverlayCustomClass = "custom-modal-overlay" 29 | }; 30 | 31 | Modal.Show("Are you sure?", options); 32 | ``` -------------------------------------------------------------------------------- /docs/docs/styling/modal-style.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Modal Style 6 | If you want to change to default look of the modal you can pass in your own CSS classes which will replace the default style classes that are applied. This allows you complete control over the look of the modal. 7 | 8 | #### Configuring for all modals 9 | ```razor 10 | 11 | ``` 12 | 13 | 14 | #### Configuring for a single modal 15 | ```csharp 16 | var options = new ModalOptions() 17 | { 18 | Class = "my-custom-modal-class" 19 | }; 20 | 21 | Modal.Show("Are you sure?", options); 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /docs/docs/usage/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Usage", 3 | "position": 1, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Examples of using Blazored Modal" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/usage/closing-programmatically.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Closing Programmatically 6 | 7 | While most modals will be dismissed by a user via buttons on the UI, sometimes you may want to dismiss a modal programmatically. An example of such a usecase is opening a modal with a loading spinner while data is being loaded from a UI or other long running process. 8 | 9 | Closing a modal programmatically is done using the `IModalReference.Close()` method. In the following example, a `Spinner` component is shown in the modal while a long running task is being awaited. Once the long running task completes, the modal is closed programmatically. 10 | 11 | ```csharp 12 | [CascadingParameter] IModalService Modal { get; set; } = default!; 13 | 14 | private async Task ShowSpinner() 15 | { 16 | var spinnerModal = Modal.Show(); 17 | 18 | await MyLongRunningTask(); 19 | 20 | spinnerModal.Close(); 21 | } 22 | ``` -------------------------------------------------------------------------------- /docs/docs/usage/modal-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Modal Reference 6 | When you open a modal you can capture a reference to it and await the result of that modal. This is useful when you want to perform an action when a modal is closed or cancelled. 7 | 8 | ```razor 9 | @page "/movies" 10 | 11 |

Movies

12 | 13 | 14 | 15 | @code { 16 | 17 | [CascadingParameter] IModalService Modal { get; set; } = default!; 18 | 19 | private async Task ShowModal() 20 | { 21 | var moviesModal = Modal.Show("My Movies"); 22 | var result = await moviesModal.Result; 23 | 24 | if (result.Cancelled) 25 | { 26 | Console.WriteLine("Modal was cancelled"); 27 | } 28 | else if (result.Confirmed) 29 | { 30 | Console.WriteLine("Modal was closed"); 31 | } 32 | } 33 | } 34 | ``` -------------------------------------------------------------------------------- /docs/docs/usage/multiple-modals.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Multiple Modals 6 | It's possible to show multiple modals at the same time, however, each new modal needs to be shown from the currently active modal. 7 | 8 | In the following example, `ModalOne` is displayed from the `Homepage` component. 9 | 10 | ```razor title="Homepage.razor" 11 | 12 | 13 | @code { 14 | [CascadingParameter] IModalService ModalService { get; set; } = default!; 15 | 16 | private void ShowModal() => ModalService.Show("First Modal"); 17 | } 18 | ``` 19 | 20 | `ModalOne` then has two buttons *Show Modal Two* and *Close*. Clicking *Show Modal Two* will then create a second modal, `ModalTwo`, which renders on top of `ModalOne`. 21 | 22 | ```razor title="ModalOne.razor" 23 |

Modal One

24 | 25 | 26 | 27 | 28 | @code { 29 | [CascadingParameter] BlazoredModalInstance ModalOne { get; set; } = default!; 30 | [CascadingParameter] IModalService ModalService { get; set; } = default!; 31 | 32 | private async Task ShowModalTwo() 33 | { 34 | var modalTwo = ModalService.Show("Second Modal"); 35 | _ = await modalTwo.Result; 36 | 37 | await ModalOne.CloseAsync(); 38 | } 39 | 40 | private async Task Close() => await ModalOne.CloseAsync(); 41 | } 42 | ``` 43 | 44 | ```csharp title="ModalTwo.razor" 45 |

Modal Two

46 | 47 | 48 | 49 | @code { 50 | [CascadingParameter] BlazoredModalInstance ModalTwo { get; set; } = default!; 51 | 52 | private async Task Close() => await ModalTwo.CloseAsync(); 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/docs/usage/passing-data.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Passing Data 6 | Data can be passed to a modal by using the `ModalParameters` object. The items you add to this collection must match the parameters defined on the component being displayed in the modal. Let's look at an example. 7 | 8 | The following component is going to be displayed using Blazored Modal. It defines a `Message` parameter, which is then displayed in a `p`. 9 | 10 | ```razor title="DisplayMessage.razor" 11 |
12 |

@Message

13 | 14 | 15 |
16 | 17 | @code { 18 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 19 | 20 | [Parameter] public string? Message { get; set; } 21 | 22 | private async Task Close() => await BlazoredModal.CloseAsync(); 23 | } 24 | ``` 25 | 26 | In order to pass a value to the `Message` parameter of the `DisplayMessage` component, we do the following. 27 | 28 | ```csharp 29 | var parameters = new ModalParameters() 30 | .Add(nameof(DisplayMessage.Message), "Hello, World!"); 31 | 32 | Modal.Show("Passing Data", parameters); 33 | ``` 34 | 35 | Note the use of `nameof`. Although you can define the parameter name as a string, using `nameof` gives strong typing. -------------------------------------------------------------------------------- /docs/docs/usage/returning-data.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Returning Data 6 | 7 | Data can be returned from a modal by using the `ModalResult.Data` property. You can return simple strings as well as complex objects. 8 | 9 | In the example below, a message can be entered into the `MessageForm` component and when the form is submitted, the text entered is returned to the calling component in the modal result object. 10 | 11 | ```razor title="MessageForm.razor" 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | @code { 23 | private readonly Form _form = new(); 24 | 25 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 26 | 27 | protected override void OnInitialized() => BlazoredModal.SetTitle("Enter a Message"); 28 | 29 | private async Task SubmitForm() => await BlazoredModal.CloseAsync(ModalResult.Ok(_form.Message)); 30 | private async Task Cancel() => await BlazoredModal.CancelAsync(); 31 | } 32 | ``` 33 | 34 | The component that invoked the modal can access the message by awaiting the result of the modal and then accessing the `result.Data` property. 35 | 36 | ```csharp 37 | var messageForm = Modal.Show(); 38 | var result = await messageForm.Result; 39 | 40 | if (result.Confirmed) 41 | { 42 | _message = result.Data.ToString(); 43 | } 44 | ``` -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | import {themes as prismThemes} from 'prism-react-renderer'; 5 | 6 | /** @type {import('@docusaurus/types').Config} */ 7 | const config = { 8 | title: 'Blazored Modal', 9 | tagline: 'Blazored Modal', 10 | url: 'https://blazored.github.io', 11 | baseUrl: '/Modal/', 12 | onBrokenLinks: 'throw', 13 | onBrokenMarkdownLinks: 'warn', 14 | favicon: 'img/favicon.ico', 15 | 16 | // GitHub pages deployment config. 17 | // If you aren't using GitHub pages, you don't need these. 18 | organizationName: 'Blazored', // Usually your GitHub org/user name. 19 | projectName: 'Modal', // Usually your repo name. 20 | deploymentBranch: 'gh-pages', 21 | trailingSlash: false, 22 | // Even if you don't use internalization, you can use this field to set useful 23 | // metadata like html lang. For example, if your site is Chinese, you may want 24 | // to replace "en" with "zh-Hans". 25 | i18n: { 26 | defaultLocale: 'en', 27 | locales: ['en'], 28 | }, 29 | 30 | presets: [ 31 | [ 32 | 'classic', 33 | /** @type {import('@docusaurus/preset-classic').Options} */ 34 | ({ 35 | docs: { 36 | routeBasePath: '/', // Serve the docs at the site's root 37 | sidebarPath: require.resolve('./sidebars.js'), 38 | // Please change this to your repo. 39 | // Remove this to remove the "edit this page" links. 40 | editUrl: 41 | 'https://github.com/blazored/modal/tree/main/docs/', 42 | }, 43 | blog: false, 44 | theme: { 45 | customCss: require.resolve('./src/css/custom.css'), 46 | }, 47 | }), 48 | ], 49 | ], 50 | 51 | themeConfig: 52 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 53 | ({ 54 | navbar: { 55 | title: 'Blazored Modal Docs', 56 | logo: { 57 | alt: 'Blazored Logo', 58 | src: 'img/logo.svg', 59 | }, 60 | items: [ 61 | { 62 | href: 'https://github.com/blazored/modal', 63 | label: 'GitHub', 64 | position: 'right', 65 | }, 66 | ], 67 | }, 68 | footer: { 69 | style: 'dark', 70 | links: [ 71 | { 72 | title: 'Community', 73 | items: [ 74 | { 75 | label: 'Discord', 76 | href: 'https://discord.gg/anPDeyhcyV', 77 | }, 78 | { 79 | label: 'Stack Overflow', 80 | href: 'https://stackoverflow.com/questions/tagged/blazored', 81 | }, 82 | { 83 | label: 'Twitter', 84 | href: 'https://twitter.com/blazoredtweets', 85 | }, 86 | ], 87 | }, 88 | ], 89 | copyright: `Copyright © ${new Date().getFullYear()} Blazored`, 90 | }, 91 | prism: { 92 | theme: prismThemes.github, 93 | darkTheme: prismThemes.vsDark, 94 | additionalLanguages: ['csharp', 'cshtml'] 95 | }, 96 | }), 97 | }; 98 | 99 | module.exports = config; 100 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^3.1.1", 18 | "@docusaurus/preset-classic": "^3.1.1", 19 | "@mdx-js/react": "^3.0.0", 20 | "clsx": "^1.2.1", 21 | "prism-react-renderer": "^2.1.0", 22 | "react": "^18.2.0", 23 | "react-dom": "^18.2.0" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "^3.1.1" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "engines": { 41 | "node": ">=18.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #5b21b6; 10 | --ifm-color-primary-dark: #521ea4; 11 | --ifm-color-primary-darker: #4d1c9b; 12 | --ifm-color-primary-darkest: #40177f; 13 | --ifm-color-primary-light: #6424c8; 14 | --ifm-color-primary-lighter: #6926d1; 15 | --ifm-color-primary-lightest: #7a3cdc; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #ff6905; 23 | --ifm-color-primary-dark: #ea5e00; 24 | --ifm-color-primary-darker: #dd5800; 25 | --ifm-color-primary-darkest: #b64900; 26 | --ifm-color-primary-light: #ff791f; 27 | --ifm-color-primary-lighter: #ff802c; 28 | --ifm-color-primary-lightest: #ff9853; 29 | } 30 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
-------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | width: 1.25rem; 31 | height: 1.25rem; 32 | margin-right: 0.75rem; 33 | top: -1px; 34 | background-size: cover; 35 | } 36 | 37 | .bi-house-door-fill-nav-menu { 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 39 | } 40 | 41 | .bi-plus-square-fill-nav-menu { 42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 43 | } 44 | 45 | .bi-list-nested-nav-menu { 46 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 47 | } 48 | 49 | .nav-item { 50 | font-size: 0.9rem; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .nav-item:first-of-type { 55 | padding-top: 1rem; 56 | } 57 | 58 | .nav-item:last-of-type { 59 | padding-bottom: 1rem; 60 | } 61 | 62 | .nav-item ::deep .nav-link { 63 | color: #d7d7d7; 64 | background: none; 65 | border: none; 66 | border-radius: 4px; 67 | height: 3rem; 68 | display: flex; 69 | align-items: center; 70 | line-height: 3rem; 71 | width: 100%; 72 | } 73 | 74 | .nav-item ::deep a.active { 75 | background-color: rgba(255,255,255,0.37); 76 | color: white; 77 | } 78 | 79 | .nav-item ::deep .nav-link:hover { 80 | background-color: rgba(255,255,255,0.1); 81 | color: white; 82 | } 83 | 84 | .nav-scrollable { 85 | display: none; 86 | } 87 | 88 | .navbar-toggler:checked ~ .nav-scrollable { 89 | display: block; 90 | } 91 | 92 | @media (min-width: 641px) { 93 | .navbar-toggler { 94 | display: none; 95 | } 96 | 97 | .nav-scrollable { 98 | /* Never collapse the sidebar for wide screens */ 99 | display: block; 100 | 101 | /* Allow sidebar to scroll for tall menus */ 102 | height: calc(100vh - 3.5rem); 103 | overflow-y: auto; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/Animation.razor: -------------------------------------------------------------------------------- 1 | @page "/animation" 2 | 3 |

Animating the modal

4 | 5 |
6 | 7 |

8 | By default, the modal is shown with a subtle fade in out animation. However, this can be disabled so the modal shows and hides immediately. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { AnimationType = ModalAnimation.None };") 17 |
18 | @("Modal.Show(\"Animation Type: None\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @(" ") 30 | 31 |

32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 |

41 | It is also possible to have multiple modals (like in the Multiple Modals example) with different animations. With the below modal, the first modal will appear without an animation. The second modal will fade-in and fade-out. 42 |

43 | 44 | 45 | 46 | @code { 47 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 48 | 49 | void AnimationDefault() 50 | { 51 | Modal.Show("Default Animation"); 52 | } 53 | 54 | void AnimationPopInOut() 55 | { 56 | var options = new ModalOptions { AnimationType = ModalAnimationType.PopInOut }; 57 | 58 | Modal.Show("Animation Type: PopInOut", options); 59 | } 60 | 61 | void AnimationPopIn() 62 | { 63 | var options = new ModalOptions { AnimationType = ModalAnimationType.PopIn }; 64 | 65 | Modal.Show("Animation Type: PopIn", options); 66 | } 67 | 68 | void NoAnimation() 69 | { 70 | var options = new ModalOptions { AnimationType = ModalAnimationType.None }; 71 | 72 | Modal.Show("Animation Type: None", options); 73 | } 74 | 75 | void MultipleModals() 76 | { 77 | var options = new ModalOptions 78 | { 79 | AnimationType = ModalAnimationType.None 80 | }; 81 | 82 | Modal.Show("Multiple Modals", options); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/BackgroundCancel.razor: -------------------------------------------------------------------------------- 1 | @page "/backgroundcancel" 2 | 3 |

Background Click

4 | 5 |
6 | 7 |

8 | By default, a modal is cancelled if the user clicks anywhere outside the modal. This behavior can 9 | be disabled by setting DisableBackgroundCancel to true. 10 |

11 | 12 |
13 |
Setting on a per modal basis
14 |
15 |

16 | 17 | @("var options = new ModalOptions() { DisableBackgroundCancel = true };") 18 |
19 | @("Modal.Show(\"Background Cancel Disabled\", options);") 20 |
21 |

22 |
23 |
24 | 25 |
26 |
Setting globally for all modals
27 |
28 |

29 | 30 | @("") 31 | 32 |

33 |
34 |
35 | 36 | 37 | 38 | 39 | @code { 40 | 41 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 42 | 43 | void BackgroundCancelEnabled() 44 | { 45 | Modal.Show("Background Cancel Enabled (Default)"); 46 | } 47 | 48 | void BackgroundCancelDisabled() 49 | { 50 | var options = new ModalOptions { DisableBackgroundCancel = true }; 51 | 52 | Modal.Show("Background Cancel Disabled", options); 53 | } 54 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/CustomLayout.razor: -------------------------------------------------------------------------------- 1 | @page "/customlayout" 2 | 3 | 4 |

Custom Layout

5 | 6 |
7 | 8 |

9 | Custom layout can be set globally or on a per modal basis to change the look of modals. 10 |

11 | 12 |
13 |
Setting on a per modal basis
14 |
15 |

16 | 17 | @("var options = new ModalOptions() { UseCustomLayout = true };") 18 | 19 |

20 |
21 |
22 | 23 |
24 |
Setting globally for all modals
25 |
26 |

27 | 28 | @(" ") 29 | 30 |

31 |
32 |
33 | 34 | 35 | 36 | @code { 37 | 38 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 39 | 40 | void ShowModalCustomLayout() 41 | { 42 | var options = new ModalOptions { UseCustomLayout = true }; 43 | var parameters = new ModalParameters {{nameof(CustomBootstrapModal.Message), "Hello custom modal!!"}}; 44 | Modal.Show("Custom Layout", parameters, options); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/CustomOverlay.razor: -------------------------------------------------------------------------------- 1 | @page "/customoverlay" 2 | 3 |

Custom Overlay

4 | 5 |
6 | 7 |

8 | The overlay can be customised by providing one or more custom classes to augment or overwrite the default styles. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { OverlayCustomClass = \"my-custom-class\" };") 17 |
18 | @("Modal.Show(\"Custom Overlay\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @("") 30 | 31 |

32 |
33 |
34 | 35 | 36 | 37 | @code { 38 | 39 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 40 | 41 | void ShowModalCustomOverlay() 42 | { 43 | var options = new ModalOptions { OverlayCustomClass = "custom-modal-overlay" }; 44 | Modal.Show("Custom Overlay", options); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/CustomStyle.razor: -------------------------------------------------------------------------------- 1 | @page "/custom" 2 | 3 |

Custom Styling

4 | 5 |
6 | 7 |

8 | Custom CSS classes can be set globally or on a per modal basis to change the look of modals by overriding the default CSS class .blazored-modal. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { Class = \"my-custom-modal-class\" };") 17 |
18 | @("Modal.Show(\"Custom Styling\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @("") 30 | 31 |

32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 | @code { 41 | 42 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 43 | 44 | void ShowModalCustomStyle() 45 | { 46 | var options = new ModalOptions { Class = "my-custom-modal-class" }; 47 | Modal.Show("Custom Style", options); 48 | } 49 | 50 | void ShowDefaultModal() 51 | { 52 | Modal.Show("Default Style"); 53 | } 54 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

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

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] private HttpContext? HttpContext { get; set; } 29 | 30 | private string? RequestId { get; set; } 31 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 32 | 33 | protected override void OnInitialized() => 34 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 35 | 36 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/HideCloseButton.razor: -------------------------------------------------------------------------------- 1 | @page "/closebutton" 2 | 3 |

Hiding the modal close button

4 | 5 |
6 | 7 |

8 | By default, a close button is shown in the modal. If you prefer, the button can be hidden and you can handle closing the modal yourself. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { HideCloseButton = true };") 17 |
18 | @("Modal.Show(\"Hiding Close Button\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @("") 30 | 31 |

32 |
33 |
34 | 35 | 36 | 37 | 38 | @code { 39 | 40 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 41 | 42 | void CloseButtonShown() 43 | { 44 | Modal.Show("Showing Close Button (default)"); 45 | } 46 | 47 | void CloseButtonHidden() 48 | { 49 | var options = new ModalOptions { HideCloseButton = true }; 50 | Modal.Show("Hiding Close Button", options); 51 | } 52 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Blazored Modal Samples 4 | 5 |

Blazored Modal Sample

6 | 7 |
8 | 9 |

10 | This is an example of using Blazored Modal in its most simplistic form. google 11 |

12 | 13 |
14 |
15 |

16 | 17 | @("Modal.Show(\"Welcome to Blazored Modal\", options);") 18 | 19 |

20 |
21 |
22 | 23 | 24 | 25 | @code { 26 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 27 | 28 | void ShowModal() 29 | => Modal.Show("Welcome to Blazored Modal"); 30 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/LongRunningTask.razor: -------------------------------------------------------------------------------- 1 | @page "/longrunningtask" 2 | 3 |

Closing Modal after long-running task

4 | 5 |
6 | 7 |

8 | A long-running task can be executed and a modal can be closed programatically after it. 9 |

10 | 11 |
12 |
13 |

14 | 15 | @("var loading = Modal.Show();")
16 | @("await Task.Delay(5000);")
17 |
18 | @("loading.Close();")
19 |
20 |

21 |
22 |
23 | 24 |

Result: @_result;

25 | 26 | 27 | 28 | 29 | 30 | 31 | @code { 32 | 33 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 34 | 35 | string? _result; 36 | 37 | async Task ShowModal() 38 | { 39 | var options = new ModalOptions 40 | { 41 | HideCloseButton = false, 42 | DisableBackgroundCancel = true, 43 | HideHeader = true 44 | }; 45 | var loading = Modal.Show(string.Empty, options); 46 | 47 | await Task.Delay(5000); 48 | 49 | loading.Close(); 50 | var result = await loading.Result; 51 | if (result.DataType == typeof(object)) 52 | _result = "Modal returned with default ModalResult"; 53 | 54 | StateHasChanged(); 55 | } 56 | 57 | async Task ShowOkResultModal() 58 | { 59 | var options = new ModalOptions 60 | { 61 | HideCloseButton = false, 62 | DisableBackgroundCancel = true, 63 | HideHeader = true 64 | }; 65 | var loading = Modal.Show(string.Empty, options); 66 | 67 | await Task.Delay(5000); 68 | 69 | loading.Close(ModalResult.Ok("Closed with OK result")); 70 | var result = await loading.Result; 71 | if (result.Data is not null && result.DataType == typeof(string)) 72 | _result = result.Data.ToString()!; 73 | 74 | StateHasChanged(); 75 | } 76 | 77 | async Task ShowCancelModal() 78 | { 79 | var options = new ModalOptions 80 | { 81 | HideCloseButton = false, 82 | DisableBackgroundCancel = true, 83 | HideHeader = true 84 | }; 85 | var loading = Modal.Show(string.Empty, options); 86 | 87 | await Task.Delay(5000); 88 | 89 | loading.Close(ModalResult.Cancel()); 90 | var result = await loading.Result; 91 | if (result.Cancelled) 92 | _result = "Closed with Cancelled result"; 93 | 94 | StateHasChanged(); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/MultipleModals.razor: -------------------------------------------------------------------------------- 1 | @page "/multiplemodals" 2 | 3 |

Multiple Modals

4 | 5 |
6 | 7 |

8 | It is possible to show multiple modals at the same time. Each new modal needs to be shown from the currently active modal. 9 | In this example an initial confirmation modal is shown, if you select Yes then a second confirmation modal is shown. 10 | When you Close the second modal both modals will be closed. If you Cancel the second modal, only the second 11 | modal will be closed. 12 |

13 | 14 | 15 | 16 | @code { 17 | 18 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 19 | 20 | void ShowModal() => Modal.Show("First Modal"); 21 | 22 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/PassDataToModal.razor: -------------------------------------------------------------------------------- 1 | @page "/passingdata" 2 | 3 |

Passing Data to a Modal

4 | 5 |
6 | 7 |

8 | Data can be passed to a modal by using the ModalParameters object. The items you add to this collection must match the parameters 9 | defined on the component being displayed in the modal. In the example below, you can type a message and see it displayed in the modal. 10 |

11 | 12 |
13 |
14 |

15 | 16 | @("var parameters = new ModalParameters")
17 | @("{")
18 |     @("{ nameof(DisplayMessage.Message), _message }")
19 | @("};")
20 |
21 | @("Modal.Show(\"Passing Data\", parameters);")
22 |
23 |

24 |
25 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 33 | 34 | @code { 35 | 36 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 37 | 38 | string _message = ""; 39 | 40 | void ShowModal() 41 | { 42 | var parameters = new ModalParameters 43 | { 44 | { nameof(DisplayMessage.Message), _message }, 45 | }; 46 | 47 | Modal.Show("Passing Data", parameters); 48 | _message = ""; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/Position.razor: -------------------------------------------------------------------------------- 1 | @page "/position" 2 | 3 |

Positioning the modal

4 | 5 |
6 | 7 |

8 | By default, the modal is shown in the center of the viewport. The modal can be shown 9 | in a number of different positions by setting the Position option. 10 |

11 | 12 |
13 |
Setting on a per modal basis
14 |
15 |

16 | 17 | @("var options = new ModalOptions() { Position = ModalPosition.TopLeft };") 18 |
19 | @("Modal.Show(\"Position: TopLeft\", options);") 20 |
21 |

22 |
23 |
24 | 25 |
26 |
Setting globally for all modals
27 |
28 |

29 | 30 | @("") 31 | 32 |

33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | @code { 45 | 46 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 47 | 48 | void PositionCenter() 49 | { 50 | Modal.Show("Top Center Modal (Default)"); 51 | } 52 | 53 | void PositionCustom(ModalPosition position) 54 | { 55 | var options = new ModalOptions { Position = position }; 56 | 57 | if (position == ModalPosition.Custom) 58 | options.PositionCustomClass = "my-custom-position"; 59 | 60 | Modal.Show($"Position: {position}", options); 61 | } 62 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/ReturnDataFromModal.razor: -------------------------------------------------------------------------------- 1 | @page "/returningdata" 2 | 3 |

Returning Data From a Modal

4 | 5 |
6 | 7 |

8 | Data can be returned from a modal by using the ModalResult.Data property. You can return simple strings as well as complex objects. 9 | In the example below, you can add a message in the modal that will be show here when you close the modal. 10 |

11 | 12 |
13 |
14 |

15 | 16 | @("var messageForm = Modal.Show();")
17 | @("var result = await messageForm.Result;")
18 |
19 | @("if (!result.Cancelled)")
20 | @(" _message = result.Data.ToString();") 21 |
22 |

23 |
24 |
25 | 26 | 27 | 28 | @if (!string.IsNullOrWhiteSpace(_message)) 29 | { 30 |
31 |

Your message was:

32 |

@_message

33 | } 34 | 35 | @code { 36 | 37 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 38 | 39 | string? _message; 40 | 41 | async Task ShowModal() 42 | { 43 | var parameters = new ModalParameters 44 | { 45 | { nameof(MessageForm.TestClass), new TestClass() } 46 | }; 47 | 48 | var messageForm = Modal.Show(parameters); 49 | var result = await messageForm.Result; 50 | 51 | if (!result.Cancelled) 52 | _message = result.Data?.ToString() ?? string.Empty; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Pages/Size.razor: -------------------------------------------------------------------------------- 1 | @page "/size" 2 | 3 |

Sizing the modal

4 | 5 |
6 | 7 |

8 | The modal has a default width. However, this can be changed by setting the Size option. 9 |

10 |

11 | There are 5 different sizes available: 12 |

    13 |
  • Small - 300px
  • 14 |
  • Medium - 500px (default)
  • 15 |
  • Large - 800px
  • 16 |
  • Extra Large - 1140px
  • 17 |
  • Custom - Provide your own class, via the SizeCustomClass parameter, which sets the width
  • 18 |
19 |

20 | 21 |
22 |
Setting on a per modal basis
23 |
24 |

25 | 26 | @("var options = new ModalOptions() { Size = ModalSize.Large };") 27 |
28 | @("Modal.Show(\"Size: Large\", options);") 29 |
30 |

31 |
32 |
33 | 34 |
35 |
Setting globally for all modals
36 |
37 |

38 | 39 | @("") 40 | 41 |

42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | @code { 53 | 54 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 55 | 56 | void DefaultSize() 57 | { 58 | Modal.Show("Size: Medium (Default)"); 59 | } 60 | 61 | void SetSize(ModalSize size) 62 | { 63 | var options = new ModalOptions { Size = size }; 64 | 65 | if (size == ModalSize.Custom) 66 | options.SizeCustomClass = "my-custom-size"; 67 | 68 | Modal.Show($"Size: {size}", options); 69 | } 70 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/Confirm.razor: -------------------------------------------------------------------------------- 1 | 
2 |

Please click one of the buttons below to close or cancel the modal.

3 | 4 | 5 | 6 |
7 | 8 | @code { 9 | 10 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 11 | 12 | async Task Close() => await BlazoredModal.CloseAsync(ModalResult.Ok(true)); 13 | async Task Cancel() =>await BlazoredModal.CancelAsync(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/CustomBootstrapModal.razor: -------------------------------------------------------------------------------- 1 |  20 | 21 | @code { 22 | 23 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 24 | 25 | [Parameter] public string? Message { get; set; } 26 | 27 | async Task Close() => await BlazoredModal.CloseAsync(ModalResult.Ok(true)); 28 | async Task Cancel() => await BlazoredModal.CancelAsync(); 29 | 30 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/DisplayMessage.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 |

@Message

4 | 5 | 6 | 7 |
8 | 9 | @code { 10 | 11 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 12 | 13 | [Parameter] public string? Message { get; set; } 14 | 15 | async Task SubmitForm() => await BlazoredModal.CloseAsync(); 16 | async Task Cancel() => await BlazoredModal.CancelAsync(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/FullContentPage.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 |

HTML Ipsum Presents

4 | 5 |

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

6 | 7 |

Header Level 2

8 | 9 |
    10 |
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. 11 |
  3. Aliquam tincidunt mauris eu risus.
  4. 12 |
13 | 14 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

15 | 16 |

Header Level 3

17 | 18 |
    19 |
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • 20 |
  • Aliquam tincidunt mauris eu risus.
  • 21 |
22 | 23 |

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus

24 | 25 |
    26 |
  • Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.
  • 27 |
  • Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.
  • 28 |
  • Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.
  • 29 |
  • Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc.
  • 30 |
31 | 32 | 33 | 34 | 35 |
36 | 37 | @code { 38 | 39 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 40 | 41 | [Parameter] public string? Message { get; set; } 42 | 43 | async Task SubmitForm() => await BlazoredModal.CloseAsync(); 44 | async Task Cancel() => await BlazoredModal.CancelAsync(); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/Loading.razor: -------------------------------------------------------------------------------- 1 |  107 | 108 |
109 |
110 |
111 | 112 | @code { 113 | 114 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 115 | } 116 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/MessageForm.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 |
4 | 5 | 6 |
7 | 8 | 9 | 10 |
11 | 12 | @code { 13 | 14 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 15 | 16 | [Parameter] public TestClass? TestClass { get; set; } 17 | 18 | string? Message { get; set; } 19 | 20 | protected override void OnInitialized() => BlazoredModal.SetTitle("Enter a Message"); 21 | 22 | async Task SubmitForm() => await BlazoredModal.CloseAsync(ModalResult.Ok(Message)); 23 | async Task Cancel() => await BlazoredModal.CancelAsync(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/TestClass.cs: -------------------------------------------------------------------------------- 1 | namespace InteractiveServer.Components.Shared; 2 | 3 | public class TestClass : IEquatable 4 | { 5 | public int MyProperty { get; set; } 6 | 7 | public override bool Equals(object? obj) 8 | { 9 | return Equals(obj as TestClass); 10 | } 11 | 12 | public bool Equals(TestClass? other) 13 | { 14 | return other is not null && 15 | MyProperty == other.MyProperty; 16 | } 17 | 18 | public override int GetHashCode() 19 | { 20 | return HashCode.Combine(MyProperty); 21 | } 22 | } -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/YesNoPrompt.razor: -------------------------------------------------------------------------------- 1 | @inject IModalService ModalService 2 | 3 |
4 |

Are you sure you want to delete the record?

5 | 6 | 7 | 8 |
9 | 10 | @code { 11 | 12 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 13 | 14 | async Task Yes() 15 | { 16 | var confirmationModal = ModalService.Show("Second Modal"); 17 | var result = await confirmationModal.Result; 18 | 19 | if (result.Cancelled) 20 | return; 21 | 22 | await BlazoredModal.CloseAsync(); 23 | } 24 | 25 | async Task No() => await BlazoredModal.CancelAsync(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/Shared/YesNoPromptAnimation.razor: -------------------------------------------------------------------------------- 1 | @inject IModalService ModalService 2 | 3 |
4 |

Are you sure you want to delete the record?

5 | 6 | 7 | 8 |
9 | 10 | @code { 11 | 12 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 13 | 14 | async Task Yes() 15 | { 16 | var options = new ModalOptions { AnimationType = ModalAnimationType.FadeInOut }; 17 | 18 | var confirmationModal = ModalService.Show("Second Modal", options); 19 | var result = await confirmationModal.Result; 20 | 21 | if (result.Cancelled) 22 | return; 23 | 24 | await BlazoredModal.CloseAsync(); 25 | } 26 | 27 | async Task No() => await BlazoredModal.CancelAsync(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using InteractiveServer 10 | @using InteractiveServer.Components 11 | @using InteractiveServer.Components.Shared 12 | 13 | @using Blazored.Modal 14 | @using Blazored.Modal.Services -------------------------------------------------------------------------------- /samples/InteractiveServer/InteractiveServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /samples/InteractiveServer/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazored.Modal; 2 | using InteractiveServer.Components; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // Add services to the container. 7 | builder.Services.AddRazorComponents() 8 | .AddInteractiveServerComponents(); 9 | builder.Services.AddBlazoredModal(); 10 | 11 | var app = builder.Build(); 12 | 13 | // Configure the HTTP request pipeline. 14 | if (!app.Environment.IsDevelopment()) 15 | { 16 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 17 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 18 | app.UseHsts(); 19 | } 20 | 21 | app.UseHttpsRedirection(); 22 | 23 | app.UseStaticFiles(); 24 | app.UseAntiforgery(); 25 | 26 | app.MapRazorComponents() 27 | .AddInteractiveServerRenderMode(); 28 | 29 | app.Run(); -------------------------------------------------------------------------------- /samples/InteractiveServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/InteractiveServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/InteractiveServer/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | 53 | .my-custom-position .blazored-modal { 54 | position: absolute; 55 | top: 20%; 56 | left: 10%; 57 | } 58 | 59 | .my-custom-size { 60 | width: 88%; 61 | margin-left: auto; 62 | margin-right: auto; 63 | } 64 | 65 | .my-custom-modal-class { 66 | background-color: #b6effb; 67 | width: 90%; 68 | margin-top: 64px; 69 | margin-left: auto; 70 | margin-right: auto; 71 | padding: 16px; 72 | } 73 | 74 | .custom-modal-overlay { 75 | background-color: rgba(255, 0, 0, 0.5) !important; 76 | } 77 | -------------------------------------------------------------------------------- /samples/InteractiveServer/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/samples/InteractiveServer/wwwroot/favicon.png -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Not found 9 | 10 |

Sorry, there's nothing at this address.

11 |
12 |
13 |
14 |
-------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/InteractiveWebAssembly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <_ContentIncludedByDefault Remove="wwwroot\sample-data\weather.json" /> 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 |
3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 |
-------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 | 79 | 80 | @code { 81 | private bool _collapseNavMenu = true; 82 | 83 | private string? NavMenuCssClass => _collapseNavMenu ? "collapse" : null; 84 | 85 | private void ToggleNavMenu() 86 | { 87 | _collapseNavMenu = !_collapseNavMenu; 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .bi { 15 | display: inline-block; 16 | position: relative; 17 | width: 1.25rem; 18 | height: 1.25rem; 19 | margin-right: 0.75rem; 20 | top: -1px; 21 | background-size: cover; 22 | } 23 | 24 | .bi-house-door-fill-nav-menu { 25 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 26 | } 27 | 28 | .bi-plus-square-fill-nav-menu { 29 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 30 | } 31 | 32 | .bi-list-nested-nav-menu { 33 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 34 | } 35 | 36 | .nav-item { 37 | font-size: 0.9rem; 38 | padding-bottom: 0.5rem; 39 | } 40 | 41 | .nav-item:first-of-type { 42 | padding-top: 1rem; 43 | } 44 | 45 | .nav-item:last-of-type { 46 | padding-bottom: 1rem; 47 | } 48 | 49 | .nav-item ::deep a { 50 | color: #d7d7d7; 51 | border-radius: 4px; 52 | height: 3rem; 53 | display: flex; 54 | align-items: center; 55 | line-height: 3rem; 56 | } 57 | 58 | .nav-item ::deep a.active { 59 | background-color: rgba(255,255,255,0.37); 60 | color: white; 61 | } 62 | 63 | .nav-item ::deep a:hover { 64 | background-color: rgba(255,255,255,0.1); 65 | color: white; 66 | } 67 | 68 | @media (min-width: 641px) { 69 | .navbar-toggler { 70 | display: none; 71 | } 72 | 73 | .collapse { 74 | /* Never collapse the sidebar for wide screens */ 75 | display: block; 76 | } 77 | 78 | .nav-scrollable { 79 | /* Allow sidebar to scroll for tall menus */ 80 | height: calc(100vh - 3.5rem); 81 | overflow-y: auto; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/Animation.razor: -------------------------------------------------------------------------------- 1 | @page "/animation" 2 | 3 |

Animating the modal

4 | 5 |
6 | 7 |

8 | By default, the modal is shown with a subtle fade in out animation. However, this can be disabled so the modal shows and hides immediately. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { AnimationType = ModalAnimation.None };") 17 |
18 | @("Modal.Show(\"Animation Type: None\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @(" ") 30 | 31 |

32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 |

41 | It is also possible to have multiple modals (like in the Multiple Modals example) with different animations. With the below modal, the first modal will appear without an animation. The second modal will fade-in and fade-out. 42 |

43 | 44 | 45 | 46 | @code { 47 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 48 | 49 | void AnimationDefault() 50 | { 51 | Modal.Show("Default Animation"); 52 | } 53 | 54 | void AnimationPopInOut() 55 | { 56 | var options = new ModalOptions { AnimationType = ModalAnimationType.PopInOut }; 57 | 58 | Modal.Show("Animation Type: PopInOut", options); 59 | } 60 | 61 | void AnimationPopIn() 62 | { 63 | var options = new ModalOptions { AnimationType = ModalAnimationType.PopIn }; 64 | 65 | Modal.Show("Animation Type: PopIn", options); 66 | } 67 | 68 | void NoAnimation() 69 | { 70 | var options = new ModalOptions { AnimationType = ModalAnimationType.None }; 71 | 72 | Modal.Show("Animation Type: None", options); 73 | } 74 | 75 | void MultipleModals() 76 | { 77 | var options = new ModalOptions 78 | { 79 | AnimationType = ModalAnimationType.None 80 | }; 81 | 82 | Modal.Show("Multiple Modals", options); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/BackGroundCancel.razor: -------------------------------------------------------------------------------- 1 | @page "/backgroundcancel" 2 | 3 |

Background Click

4 | 5 |
6 | 7 |

8 | By default, a modal is cancelled if the user clicks anywhere outside the modal. This behavior can 9 | be disabled by setting DisableBackgroundCancel to true. 10 |

11 | 12 |
13 |
Setting on a per modal basis
14 |
15 |

16 | 17 | @("var options = new ModalOptions() { DisableBackgroundCancel = true };") 18 |
19 | @("Modal.Show(\"Background Cancel Disabled\", options);") 20 |
21 |

22 |
23 |
24 | 25 |
26 |
Setting globally for all modals
27 |
28 |

29 | 30 | @("") 31 | 32 |

33 |
34 |
35 | 36 | 37 | 38 | 39 | @code { 40 | 41 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 42 | 43 | void BackgroundCancelEnabled() 44 | { 45 | Modal.Show("Background Cancel Enabled (Default)"); 46 | } 47 | 48 | void BackgroundCancelDisabled() 49 | { 50 | var options = new ModalOptions { DisableBackgroundCancel = true }; 51 | 52 | Modal.Show("Background Cancel Disabled", options); 53 | } 54 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/CustomLayout.razor: -------------------------------------------------------------------------------- 1 | @page "/customlayout" 2 | 3 | 4 |

Custom Layout

5 | 6 |
7 | 8 |

9 | Custom layout can be set globally or on a per modal basis to change the look of modals. 10 |

11 | 12 |
13 |
Setting on a per modal basis
14 |
15 |

16 | 17 | @("var options = new ModalOptions() { UseCustomLayout = true };") 18 | 19 |

20 |
21 |
22 | 23 |
24 |
Setting globally for all modals
25 |
26 |

27 | 28 | @(" ") 29 | 30 |

31 |
32 |
33 | 34 | 35 | 36 | @code { 37 | 38 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 39 | 40 | void ShowModalCustomLayout() 41 | { 42 | var options = new ModalOptions { UseCustomLayout = true }; 43 | var parameters = new ModalParameters(); 44 | parameters.Add(nameof(CustomBootstrapModal.Message), "Hello Bootstrap modal!!"); 45 | Modal.Show("Custom Layout", parameters, options); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/CustomOverlay.razor: -------------------------------------------------------------------------------- 1 | @page "/customoverlay" 2 | 3 |

Custom Overlay

4 | 5 |
6 | 7 |

8 | The overlay can be customised by providing one or more custom classes to augment or overwrite the default styles. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { OverlayCustomClass = \"my-custom-class\" };") 17 |
18 | @("Modal.Show(\"Custom Overlay\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @("") 30 | 31 |

32 |
33 |
34 | 35 | 36 | 37 | @code { 38 | 39 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 40 | 41 | void ShowModalCustomOverlay() 42 | { 43 | var options = new ModalOptions { OverlayCustomClass = "custom-modal-overlay" }; 44 | Modal.Show("Custom Overlay", options); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/CustomStyle.razor: -------------------------------------------------------------------------------- 1 | @page "/custom" 2 | 3 |

Custom Styling

4 | 5 |
6 | 7 |

8 | Custom CSS classes can be set globally or on a per modal basis to change the look of modals overriding the default CSS class .blazored-modal. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { Class = \"my-custom-modal-class\" };") 17 |
18 | @("Modal.Show(\"Custom Styling\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @("") 30 | 31 |

32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 | @code { 41 | 42 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 43 | 44 | void ShowModalCustomStyle() 45 | { 46 | var options = new ModalOptions { Class = "my-custom-modal-class" }; 47 | Modal.Show("Custom Style", options); 48 | } 49 | 50 | void ShowDefaultModal() 51 | { 52 | Modal.Show("Default Style"); 53 | } 54 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/HideCloseButton.razor: -------------------------------------------------------------------------------- 1 | @page "/closebutton" 2 | 3 |

Hiding the modal close button

4 | 5 |
6 | 7 |

8 | By default, a close button is shown in the modal. If you prefer, the button can be hidden and you can handle closing the modal yourself. 9 |

10 | 11 |
12 |
Setting on a per modal basis
13 |
14 |

15 | 16 | @("var options = new ModalOptions() { HideCloseButton = true };") 17 |
18 | @("Modal.Show(\"Hiding Close Button\", options);") 19 |
20 |

21 |
22 |
23 | 24 |
25 |
Setting globally for all modals
26 |
27 |

28 | 29 | @("") 30 | 31 |

32 |
33 |
34 | 35 | 36 | 37 | 38 | @code { 39 | 40 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 41 | 42 | void CloseButtonShown() 43 | { 44 | Modal.Show("Showing Close Button (default)"); 45 | } 46 | 47 | void CloseButtonHidden() 48 | { 49 | var options = new ModalOptions { HideCloseButton = true }; 50 | Modal.Show("Hiding Close Button", options); 51 | } 52 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Blazored Modal Samples 4 | 5 |

Blazored Modal Samples

6 | 7 |
8 | 9 |

10 | This is an example of using Blazored Modal in its most simplistic form. 11 |

12 | 13 |
14 |
15 |

16 | @("Modal.Show(\"Welcome to Blazored Modal\", options);") 17 |

18 |
19 |
20 | 21 | 22 | 23 | @code { 24 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 25 | 26 | void ShowModal() 27 | => Modal.Show("Welcome to Blazored Modal"); 28 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/LongRunningTask.razor: -------------------------------------------------------------------------------- 1 | @page "/longrunningtask" 2 | 3 |

Closing Modal after long-running task

4 | 5 |
6 | 7 |

8 | A long-running task can be executed and a modal can be closed programatically after it. 9 |

10 | 11 |
12 |
13 |

14 | 15 | @("var loading = Modal.Show();")
16 | @("await Task.Delay(5000);")
17 |
18 | @("loading.Close();")
19 |
20 |

21 |
22 |
23 | 24 |

Result: @_result;

25 | 26 | 27 | 28 | 29 | 30 | 31 | @code { 32 | 33 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 34 | 35 | string? _result; 36 | 37 | async Task ShowModal() 38 | { 39 | var options = new ModalOptions 40 | { 41 | HideCloseButton = false, 42 | DisableBackgroundCancel = true, 43 | HideHeader = true 44 | }; 45 | var loading = Modal.Show(string.Empty, options); 46 | 47 | await Task.Delay(5000); 48 | 49 | loading.Close(); 50 | var result = await loading.Result; 51 | if (result.DataType == typeof(object)) 52 | _result = "Modal returned with default ModalResult"; 53 | 54 | StateHasChanged(); 55 | } 56 | 57 | async Task ShowOkResultModal() 58 | { 59 | var options = new ModalOptions 60 | { 61 | HideCloseButton = false, 62 | DisableBackgroundCancel = true, 63 | HideHeader = true 64 | }; 65 | var loading = Modal.Show(string.Empty, options); 66 | 67 | await Task.Delay(5000); 68 | 69 | loading.Close(ModalResult.Ok("Closed with OK result")); 70 | var result = await loading.Result; 71 | if (result.Data is not null && result.DataType == typeof(string)) 72 | _result = result.Data.ToString()!; 73 | 74 | StateHasChanged(); 75 | } 76 | 77 | async Task ShowCancelModal() 78 | { 79 | var options = new ModalOptions 80 | { 81 | HideCloseButton = false, 82 | DisableBackgroundCancel = true, 83 | HideHeader = true 84 | }; 85 | var loading = Modal.Show(string.Empty, options); 86 | 87 | await Task.Delay(5000); 88 | 89 | loading.Close(ModalResult.Cancel()); 90 | var result = await loading.Result; 91 | if (result.Cancelled) 92 | _result = "Closed with Cancelled result"; 93 | 94 | StateHasChanged(); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/MultipleModals.razor: -------------------------------------------------------------------------------- 1 | @page "/multiplemodals" 2 | 3 |

Multiple Modals

4 | 5 |
6 | 7 |

8 | It is possible to show multiple modals at the same time. Each new modal needs to be shown from the currently active modal. 9 | In this example an initial confirmation modal is shown, if you select Yes then a second confirmation modal is shown. 10 | When you Close the second modal both modals will be closed. If you Cancel the second modal, only the second 11 | modal will be closed. 12 |

13 | 14 | 15 | 16 | @code { 17 | 18 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 19 | 20 | void ShowModal() => Modal.Show("First Modal"); 21 | 22 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/NavigateAfterModal.razor: -------------------------------------------------------------------------------- 1 | @page "/navigateAway" 2 | 3 |

Navigate away

4 | 5 |
6 | 7 | @inject NavigationManager Navigator 8 | 9 |

10 | This is an example of using Blazored Modal and navigating to a different page afterwards 11 |

12 | 13 |
14 |
15 |

16 | 17 | @("Modal.Show(\"Welcome to Blazored Modal\", options);") 18 | 19 |

20 |
21 |
22 | 23 | 24 | 25 | @code { 26 | 27 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 28 | 29 | async void ShowModal() 30 | { 31 | var modal = Modal.Show("Do you want to navigate to a different page?"); 32 | var result = await modal.Result; 33 | 34 | if (!result.Cancelled && (result.Data is bool accepted) && accepted) 35 | { 36 | Navigator.NavigateTo ("/"); 37 | } 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/PassDataToModal.razor: -------------------------------------------------------------------------------- 1 | @page "/passingdata" 2 | 3 |

Passing Data to a Modal

4 | 5 |
6 | 7 |

8 | Data can be passed to a modal by using the ModalParameters object. The items you add to this collection must match the parameters 9 | defined on the component being displayed in the modal. In the example below, you can type a message and see it displayed in the modal. 10 |

11 | 12 |
13 |
14 |

15 | 16 | @("var parameters = new ModalParameters")
17 | @("{")
18 |     @("{ nameof(DisplayMessage.Message), _message }")
19 | @("};")
20 |
21 | @("Modal.Show(\"Passing Data\", parameters);")
22 |
23 |

24 |
25 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 33 | 34 | @code { 35 | 36 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 37 | 38 | string _message = ""; 39 | 40 | void ShowModal() 41 | { 42 | var parameters = new ModalParameters 43 | { 44 | { nameof(DisplayMessage.Message), _message } 45 | }; 46 | 47 | Modal.Show("Passing Data", parameters); 48 | _message = ""; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/Position.razor: -------------------------------------------------------------------------------- 1 | @page "/position" 2 | 3 |

Positioning the modal

4 | 5 |
6 | 7 |

8 | By default, the modal is shown in the center of the viewport. The modal can be shown 9 | in a number of different positions by setting the Position option. 10 |

11 | 12 |
13 |
Setting on a per modal basis
14 |
15 |

16 | 17 | @("var options = new ModalOptions() { Position = ModalPosition.TopLeft };") 18 |
19 | @("Modal.Show(\"Position: TopLeft\", options);") 20 |
21 |

22 |
23 |
24 | 25 |
26 |
Setting globally for all modals
27 |
28 |

29 | 30 | @("") 31 | 32 |

33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | @code { 45 | 46 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 47 | 48 | void PositionCenter() 49 | { 50 | Modal.Show("Top Center Modal (Default)"); 51 | } 52 | 53 | void PositionCustom(ModalPosition position) 54 | { 55 | var options = new ModalOptions { Position = position }; 56 | 57 | if (position == ModalPosition.Custom) 58 | options.PositionCustomClass = "my-custom-position"; 59 | 60 | Modal.Show($"Position: {position}", options); 61 | } 62 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/ReturnDataFromModal.razor: -------------------------------------------------------------------------------- 1 | @page "/returningdata" 2 | 3 |

Returning Data From a Modal

4 | 5 |
6 | 7 |

8 | Data can be returned from a modal by using the ModalResult.Data property. You can return simple strings as well as complex objects. 9 | In the example below, you can add a message in the modal that will be show here when you close the modal. 10 |

11 | 12 |
13 |
14 |

15 | 16 | @("var messageForm = Modal.Show();")
17 | @("var result = await messageForm.Result;")
18 |
19 | @("if (!result.Cancelled)")
20 | @(" _message = result.Data.ToString();") 21 |
22 |

23 |
24 |
25 | 26 | 27 | 28 | @if (!string.IsNullOrWhiteSpace(_message)) 29 | { 30 |
31 |

Your message was:

32 |

@_message

33 | } 34 | 35 | @code { 36 | 37 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 38 | 39 | string? _message; 40 | 41 | async Task ShowModal() 42 | { 43 | var messageForm = Modal.Show(); 44 | var result = await messageForm.Result; 45 | 46 | if (!result.Cancelled) 47 | _message = result.Data?.ToString() ?? string.Empty; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Pages/Size.razor: -------------------------------------------------------------------------------- 1 | @page "/size" 2 | 3 |

Sizing the modal

4 | 5 |
6 | 7 |

8 | The modal has a default width. However, this can be changed by setting the Size option. 9 |

10 |

11 | There are 5 different sizes available: 12 |

    13 |
  • Small - 300px
  • 14 |
  • Medium - 500px (default)
  • 15 |
  • Large - 800px
  • 16 |
  • Extra Large - 1140px
  • 17 |
  • Custom - Provide your own class, via the SizeCustomClass parameter, which sets the width
  • 18 |
19 |

20 | 21 |
22 |
Setting on a per modal basis
23 |
24 |

25 | 26 | @("var options = new ModalOptions() { Size = ModalSize.Large };") 27 |
28 | @("Modal.Show(\"Size: Large\", options);") 29 |
30 |

31 |
32 |
33 | 34 |
35 |
Setting globally for all modals
36 |
37 |

38 | 39 | @("") 40 | 41 |

42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | @code { 53 | 54 | [CascadingParameter] public IModalService Modal { get; set; } = default!; 55 | 56 | void DefaultSize() 57 | { 58 | Modal.Show("Size: Medium (Default)"); 59 | } 60 | 61 | void SetSize(ModalSize size) 62 | { 63 | var options = new ModalOptions { Size = size }; 64 | 65 | if (size == ModalSize.Custom) 66 | options.SizeCustomClass = "my-custom-size"; 67 | 68 | Modal.Show($"Size: {size}", options); 69 | } 70 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazored.Modal; 2 | using Microsoft.AspNetCore.Components.Web; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using InteractiveWebAssembly; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.RootComponents.Add("head::after"); 9 | 10 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 11 | builder.Services.AddBlazoredModal(); 12 | 13 | await builder.Build().RunAsync(); -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/CodeBlock.razor: -------------------------------------------------------------------------------- 1 | @inject IJSRuntime JsRuntime 2 | @implements IAsyncDisposable 3 | 4 |
@ChildContent
5 | 6 | @code { 7 | private IJSObjectReference? _codeBlockJs; 8 | private ElementReference _codeBlock; 9 | 10 | [Parameter, EditorRequired] public RenderFragment ChildContent { get; set; } = default!; 11 | [Parameter] public string Language { get; set; } = "razor"; 12 | 13 | protected override async Task OnAfterRenderAsync(bool firstRender) 14 | { 15 | if (firstRender) 16 | { 17 | _codeBlockJs = await JsRuntime.InvokeAsync("import", "./Shared/CodeBlock.razor.js"); 18 | } 19 | 20 | await _codeBlockJs!.InvokeVoidAsync("highlightCode", _codeBlock); 21 | } 22 | 23 | async ValueTask IAsyncDisposable.DisposeAsync() 24 | { 25 | if (_codeBlockJs is not null) 26 | { 27 | await _codeBlockJs.DisposeAsync(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/CodeBlock.razor.js: -------------------------------------------------------------------------------- 1 | export function highlightCode(element) { 2 | hljs.highlightElement(element); 3 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/Confirm.razor: -------------------------------------------------------------------------------- 1 | 
2 |

Please click one of the buttons below to close or cancel the modal.

3 | 4 | 5 | 6 |
7 | 8 | @code { 9 | 10 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 11 | 12 | async Task Close() => await BlazoredModal.CloseAsync(ModalResult.Ok(true)); 13 | async Task Cancel() => await BlazoredModal.CancelAsync(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/CustomBootstrapModal.razor: -------------------------------------------------------------------------------- 1 |  21 | 22 | @code { 23 | 24 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 25 | 26 | [Parameter] public string? Message { get; set; } 27 | 28 | async Task Close() => await BlazoredModal.CloseAsync(ModalResult.Ok(true)); 29 | async Task Cancel() => await BlazoredModal.CancelAsync(); 30 | 31 | } -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/DisplayMessage.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 |

@Message

4 | 5 | 6 | 7 |
8 | 9 | @code { 10 | 11 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 12 | 13 | [Parameter] public string? Message { get; set; } 14 | 15 | async Task SubmitForm() => await BlazoredModal.CloseAsync(); 16 | async Task Cancel() => await BlazoredModal.CancelAsync(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/FullContentPage.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 |

HTML Ipsum Presents

4 | 5 |

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

6 | 7 |

Header Level 2

8 | 9 |
    10 |
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. 11 |
  3. Aliquam tincidunt mauris eu risus.
  4. 12 |
13 | 14 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

15 | 16 |

Header Level 3

17 | 18 |
    19 |
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • 20 |
  • Aliquam tincidunt mauris eu risus.
  • 21 |
22 | 23 |

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus

24 | 25 |
    26 |
  • Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.
  • 27 |
  • Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.
  • 28 |
  • Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.
  • 29 |
  • Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc.
  • 30 |
31 | 32 | 33 | 34 | 35 |
36 | 37 | @code { 38 | 39 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 40 | 41 | [Parameter] public string? Message { get; set; } 42 | 43 | async Task SubmitForm() => await BlazoredModal.CloseAsync(); 44 | async Task Cancel() => await BlazoredModal.CancelAsync(); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/Loading.razor: -------------------------------------------------------------------------------- 1 |  107 | 108 |
109 |
110 |
111 | 112 | @code { 113 | 114 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 115 | } 116 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/MessageForm.razor: -------------------------------------------------------------------------------- 1 | @using System.ComponentModel.DataAnnotations 2 | 3 |
4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 |
16 |
17 | 18 | @code { 19 | 20 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 21 | 22 | readonly Form _form = new(); 23 | 24 | protected override void OnInitialized() => BlazoredModal.SetTitle("Enter a Message"); 25 | 26 | async Task SubmitForm() => await BlazoredModal.CloseAsync(ModalResult.Ok(_form.Message)); 27 | async Task Cancel() => await BlazoredModal.CancelAsync(); 28 | 29 | public class Form 30 | { 31 | [Required(ErrorMessage = "Please enter a message")] 32 | public string? Message { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/YesNoPrompt.razor: -------------------------------------------------------------------------------- 1 | 
2 |

Are you sure you want to delete the record?

3 | 4 | 5 | 6 |
7 | 8 | @code { 9 | 10 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 11 | [CascadingParameter] private IModalService Modal { get; set; } = default!; 12 | 13 | async Task Yes() 14 | { 15 | var confirmationModal = Modal.Show("Second Modal"); 16 | var result = await confirmationModal.Result; 17 | 18 | if (result.Cancelled) 19 | return; 20 | 21 | await BlazoredModal.CloseAsync(); 22 | } 23 | 24 | async Task No() => await BlazoredModal.CloseAsync(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/Shared/YesNoPromptAnimation.razor: -------------------------------------------------------------------------------- 1 | @inject IModalService ModalService 2 | 3 |
4 |

Are you sure you want to delete the record?

5 | 6 | 7 | 8 |
9 | 10 | @code { 11 | 12 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!; 13 | 14 | async Task Yes() 15 | { 16 | var options = new ModalOptions { AnimationType = ModalAnimationType.FadeInOut }; 17 | 18 | var confirmationModal = ModalService.Show("Second Modal", options); 19 | var result = await confirmationModal.Result; 20 | 21 | if (result.Cancelled) 22 | return; 23 | 24 | await BlazoredModal.CloseAsync(); 25 | } 26 | 27 | async Task No() => await BlazoredModal.CancelAsync(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using InteractiveWebAssembly 10 | @using InteractiveWebAssembly.Layout 11 | @using InteractiveWebAssembly.Shared 12 | 13 | @using Blazored.Modal 14 | @using Blazored.Modal.Services -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/.nojekyll: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/404.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Single Page Apps for GitHub Pages 6 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | h1:focus { 6 | outline: none; 7 | } 8 | 9 | a, .btn-link { 10 | color: #0071c1; 11 | } 12 | 13 | .btn-primary { 14 | color: #fff; 15 | background-color: #1b6ec2; 16 | border-color: #1861ac; 17 | } 18 | 19 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 20 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 21 | } 22 | 23 | .content { 24 | padding-top: 1.1rem; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid red; 33 | } 34 | 35 | .validation-message { 36 | color: red; 37 | } 38 | 39 | #blazor-error-ui { 40 | background: lightyellow; 41 | bottom: 0; 42 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 43 | display: none; 44 | left: 0; 45 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 46 | position: fixed; 47 | width: 100%; 48 | z-index: 1000; 49 | } 50 | 51 | #blazor-error-ui .dismiss { 52 | cursor: pointer; 53 | position: absolute; 54 | right: 0.75rem; 55 | top: 0.5rem; 56 | } 57 | 58 | .blazor-error-boundary { 59 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 60 | padding: 1rem 1rem 1rem 3.7rem; 61 | color: white; 62 | } 63 | 64 | .blazor-error-boundary::after { 65 | content: "An error has occurred." 66 | } 67 | 68 | .loading-progress { 69 | position: relative; 70 | display: block; 71 | width: 8rem; 72 | height: 8rem; 73 | margin: 20vh auto 1rem auto; 74 | } 75 | 76 | .loading-progress circle { 77 | fill: none; 78 | stroke: #e0e0e0; 79 | stroke-width: 0.6rem; 80 | transform-origin: 50% 50%; 81 | transform: rotate(-90deg); 82 | } 83 | 84 | .loading-progress circle:last-child { 85 | stroke: #1b6ec2; 86 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; 87 | transition: stroke-dasharray 0.05s ease-in-out; 88 | } 89 | 90 | .loading-progress-text { 91 | position: absolute; 92 | text-align: center; 93 | font-weight: bold; 94 | inset: calc(20vh + 3.25rem) 0 auto 0.2rem; 95 | } 96 | 97 | .loading-progress-text:after { 98 | content: var(--blazor-load-percentage-text, "Loading"); 99 | } 100 | 101 | code { 102 | color: #c02d76; 103 | } 104 | 105 | .my-custom-position .blazored-modal { 106 | position: absolute; 107 | top: 20%; 108 | left: 10%; 109 | } 110 | 111 | .my-custom-size { 112 | width: 88%; 113 | margin-left: auto; 114 | margin-right: auto; 115 | } 116 | 117 | .my-custom-modal-class { 118 | background-color: #b6effb; 119 | width: 90%; 120 | margin-top: 64px; 121 | margin-left: auto; 122 | margin-right: auto; 123 | padding: 16px; 124 | } 125 | 126 | .custom-modal-overlay { 127 | background-color: rgba(255, 0, 0, 0.5) !important; 128 | } 129 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/samples/InteractiveWebAssembly/wwwroot/favicon.png -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/samples/InteractiveWebAssembly/wwwroot/icon-192.png -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | InteractiveWebAssembly 8 | 9 | 10 | 11 | 12 | 13 | 14 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 50 | 51 | 52 |
53 |
54 | 55 |
56 | An unhandled error has occurred. 57 | Reload 58 | 🗙 59 |
60 | 61 | 62 | 63 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /samples/InteractiveWebAssembly/wwwroot/js/cshtml-razor.min.js: -------------------------------------------------------------------------------- 1 | hljs.registerLanguage("cshtml-razor",(()=>{"use strict";return n=>{ 2 | var e="built_in",s={},a={begin:"}",className:e,endsParent:!0},i={begin:"{", 3 | end:"}",contains:[n.QUOTE_STRING_MODE,"self"]},r=n.COMMENT("@\\*","\\*@",{ 4 | relevance:10}),g={begin:"@[A-Za-z0-9\\._:-]+",returnBegin:!0, 5 | end:"(\\r|\\n|<|\\s|\"|')",subLanguage:"csharp",contains:[{begin:"@",className:e 6 | },{begin:"\\[",end:"\\]",skip:!0},{begin:"\\(",end:"\\)",skip:!0}],returnEnd:!0 7 | },t={begin:"[@]{0,1}",returnBegin:!0,end:"",returnEnd:!0, 8 | subLanguage:"cshtml-razor",contains:[{begin:"[@]{0,1}",className:e},{ 9 | begin:"",className:e,endsParent:!0}]},c={begin:"@\\(",end:"\\)", 10 | returnBegin:!0,returnEnd:!0,subLanguage:"csharp",contains:[{begin:"@\\(", 11 | className:e},{begin:"\\(",end:"\\)",subLanguage:"csharp", 12 | contains:[n.QUOTE_STRING_MODE,"self",t]},t,{begin:"\\)",className:e, 13 | endsParent:!0}]},b=((n,e)=>{var s={endsWithParent:!0,illegal:/`]+/}]}]}]} 17 | ;return[{className:"meta",begin:"",relevance:10,contains:[{ 18 | begin:"\\[",end:"\\]"}]},n.COMMENT("\x3c!--","--\x3e",{relevance:10}),{ 19 | begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},{className:"meta", 20 | begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag", 21 | begin:"|$)",end:">",keywords:{name:"style"},contains:[s],starts:{ 22 | end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", 23 | begin:"|$)",end:">",keywords:{name:"script"},contains:[s], 24 | starts:{end:"<\/script>",returnEnd:!0, 25 | subLanguage:["actionscript","javascript","handlebars","xml"]}},{className:"tag", 26 | begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0 27 | },s]}].concat(e) 28 | })(n,[g,c]),l="^\\s*@(page|model|using|inherits|inject|layout)",u={ 29 | begin:l+"[^\\r\\n{\\(]*$",end:"$",returnBegin:!0,returnEnd:!0,contains:[{ 30 | begin:l,className:e},{variants:[{begin:"\\r|\\n",endsParent:!0},{ 31 | begin:"\\s[^\\r\\n]+",end:"$"},{begin:"$"}],className:"type",endsParent:!0}] 32 | },d={variants:[{begin:"@\\{",end:"}"},{begin:"@code\\s*\\{",end:"}"}], 33 | returnBegin:!0,returnEnd:!0,subLanguage:"csharp",contains:[{ 34 | begin:"@(code\\s*)?\\{",className:e},s,{begin:"{",end:"}",contains:["self"], 35 | skip:!0},a]},o={begin:"^\\s*@helper[\\s]*[^{]+[\\s]*{",returnBegin:!0, 36 | returnEnd:!0,end:"}",subLanguage:"cshtml-razor",contains:[{begin:"@helper", 37 | className:e},{begin:"{",className:e},a]},m=[{begin:"@for[\\s]*\\([^{]+[\\s]*{", 38 | end:"}"},{begin:"@if[\\s]*\\([^{]+[\\s]*{",end:"}"},{ 39 | begin:"@switch[\\s]*\\([^{]+[\\s]*{",end:"}"},{ 40 | begin:"@while[\\s]*\\([^{]+[\\s]*{",end:"}"},{ 41 | begin:"@using[\\s]*\\([^{]+[\\s]*{",end:"}"},{ 42 | begin:"@lock[\\s]*\\([^{]+[\\s]*{",end:"}"},{ 43 | begin:"@foreach[\\s]*\\([^{]+[\\s]*{",end:"}"}],N={variants:m,returnBegin:!0, 44 | returnEnd:!0,subLanguage:"csharp",contains:[{variants:m.map((n=>({begin:n.begin 45 | }))),returnBegin:!0,contains:[{begin:"@",className:e},{variants:m.map((n=>({ 46 | begin:n.begin.substr(1,n.begin.length-2)}))),subLanguage:"csharp"},{begin:"{", 47 | className:e}]},s,{variants:[{begin:"}[\\s]*else\\sif[\\s]*\\([^{]+[\\s]*{"},{ 48 | begin:"}[\\s]*else[\\s]*{"}],returnBegin:!0,contains:[{begin:"}",className:e},{ 49 | variants:[{begin:"[\\s]*else\\sif[\\s]*\\([^{]+[\\s]*{"},{ 50 | begin:"[\\s]*else[\\s]*"}],subLanguage:"csharp"},{begin:"{",className:e}]},i,a] 51 | },h={begin:"@try[\\s]*{",end:"}",returnBegin:!0,returnEnd:!0, 52 | subLanguage:"csharp",contains:[{begin:"@",className:e},{begin:"try[\\s]*{", 53 | subLanguage:"csharp"},{variants:[{begin:"}[\\s]*catch[\\s]*\\([^\\)]+\\)[\\s]*{" 54 | },{begin:"}[\\s]*finally[\\s]*{"}],returnBegin:!0,contains:[{begin:"}", 55 | className:e},{variants:[{begin:"[\\s]*catch[\\s]*\\([^\\)]+\\)[\\s]*"},{ 56 | begin:"[\\s]*finally[\\s]*"}],subLanguage:"csharp"},{begin:"{",className:e}] 57 | },s,i,a]},p="@section[\\s]+[a-zA-Z0-9]+[\\s]*{",v=[u,o,d,N,{begin:p, 58 | returnBegin:!0,returnEnd:!0,end:"}",subLanguage:"cshtml-razor",contains:[{ 59 | begin:p,className:e},i,a]},{begin:"@await ",returnBegin:!0,subLanguage:"csharp", 60 | end:"(\\r|\\n|<|\\s)",contains:[{begin:"@await ",className:e},{ 61 | begin:"[<\\r\\n]",endsParent:!0}]},h,{variants:[{begin:"@@"},{begin:"[a-zA-Z]+@" 62 | }],skip:!0},t,r,c,{className:"meta",begin:"",relevance:10, 63 | contains:[{begin:"\\[",end:"\\]"}]},{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>", 64 | relevance:10}].concat(b);return[d,N,h].forEach((n=>{ 65 | var e=v.filter((e=>e!==n)),a=n.contains.indexOf(s) 66 | ;n.contains.splice.apply(n.contains,[a,1].concat(e))})),{ 67 | aliases:["cshtml","razor","razor-cshtml","cshtml-razor"],contains:v}}})()); -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/screenshot.png -------------------------------------------------------------------------------- /src/Blazored.Modal/Blazored.Modal.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | Chris Sainty 9 | 10 | Copyright 2021 (c) Chris Sainty. All rights reserved. 11 | A powerful and customizable modal implementation for Blazor applications. 12 | README.md 13 | true 14 | snupkg 15 | 16 | 17 | Blazored.Modal 18 | Blazored;Blazor;Razor;Components;Modal;Dialogue;ASP.NET Core;CSharp;Web 19 | MIT 20 | https://github.com/Blazored/Modal 21 | icon.png 22 | git 23 | https://github.com/Blazored/Modal 24 | 25 | 26 | true 27 | true 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/Blazored.Modal/BlazoredModal.razor: -------------------------------------------------------------------------------- 1 | @using System.Collections.ObjectModel 2 | @using Microsoft.AspNetCore.Components.Routing 3 | 4 | @inject NavigationManager NavigationManager 5 | @inject IJSRuntime JsRuntime 6 | @implements IAsyncDisposable 7 | 8 | 9 | 10 | @foreach (var modal in _modals) 11 | { 12 | @modal.ModalInstance 13 | } 14 | 15 | 16 | 17 | @code { 18 | [CascadingParameter] private IModalService CascadedModalService { get; set; } = default!; 19 | 20 | [Parameter] public bool? HideHeader { get; set; } 21 | [Parameter] public bool? HideCloseButton { get; set; } 22 | [Parameter] public bool? DisableBackgroundCancel { get; set; } 23 | [Parameter] public string? OverlayCustomClass { get; set; } 24 | [Parameter] public ModalPosition? Position { get; set; } 25 | [Parameter] public string? PositionCustomClass { get; set; } 26 | [Parameter] public ModalSize? Size { get; set; } 27 | [Parameter] public string? SizeCustomClass { get; set; } 28 | [Parameter] public string? Class { get; set; } 29 | [Parameter] public ModalAnimationType? AnimationType { get; set; } 30 | [Parameter] public bool? UseCustomLayout { get; set; } 31 | [Parameter] public bool? ActivateFocusTrap { get; set; } 32 | 33 | private readonly Collection _modals = new(); 34 | private readonly ModalOptions _globalModalOptions = new(); 35 | private IJSObjectReference? _styleFunctions; 36 | private bool _haveActiveModals; 37 | 38 | internal event Action? OnModalClosed; 39 | 40 | protected override void OnInitialized() 41 | { 42 | if (CascadedModalService == null) 43 | { 44 | throw new InvalidOperationException($"{GetType()} requires a cascading parameter of type {nameof(IModalService)}."); 45 | } 46 | 47 | ((ModalService) CascadedModalService).OnModalInstanceAdded += Update; 48 | ((ModalService) CascadedModalService).OnModalCloseRequested += CloseInstance; 49 | NavigationManager.LocationChanged += CancelModals; 50 | 51 | _globalModalOptions.Class = Class; 52 | _globalModalOptions.DisableBackgroundCancel = DisableBackgroundCancel; 53 | _globalModalOptions.HideCloseButton = HideCloseButton; 54 | _globalModalOptions.HideHeader = HideHeader; 55 | _globalModalOptions.Position = Position; 56 | _globalModalOptions.PositionCustomClass = PositionCustomClass; 57 | _globalModalOptions.Size = Size; 58 | _globalModalOptions.SizeCustomClass = SizeCustomClass; 59 | _globalModalOptions.AnimationType = AnimationType; 60 | _globalModalOptions.OverlayCustomClass = OverlayCustomClass; 61 | 62 | _globalModalOptions.UseCustomLayout = UseCustomLayout; 63 | _globalModalOptions.ActivateFocusTrap = ActivateFocusTrap; 64 | } 65 | 66 | protected override async Task OnAfterRenderAsync(bool firstRender) 67 | { 68 | if (firstRender) 69 | { 70 | _styleFunctions = await JsRuntime.InvokeAsync("import", "./_content/Blazored.Modal/BlazoredModal.razor.js"); 71 | } 72 | } 73 | 74 | internal async Task CloseInstance(ModalReference? modal, ModalResult result) 75 | { 76 | if (modal?.ModalInstanceRef != null) 77 | { 78 | // Gracefully close the modal 79 | await modal.ModalInstanceRef.CloseAsync(result); 80 | if (!_modals.Any()) 81 | { 82 | await ClearBodyStyles(); 83 | } 84 | OnModalClosed?.Invoke(); 85 | } 86 | else 87 | { 88 | await DismissInstance(modal, result); 89 | } 90 | } 91 | 92 | internal Task DismissInstance(Guid id, ModalResult result) 93 | { 94 | var reference = GetModalReference(id); 95 | return DismissInstance(reference, result); 96 | } 97 | 98 | internal async Task DismissInstance(ModalReference? modal, ModalResult result) 99 | { 100 | if (modal != null) 101 | { 102 | modal.Dismiss(result); 103 | _modals.Remove(modal); 104 | if (!_modals.Any()) 105 | { 106 | await ClearBodyStyles(); 107 | } 108 | await InvokeAsync(StateHasChanged); 109 | OnModalClosed?.Invoke(); 110 | } 111 | } 112 | 113 | private async void CancelModals(object? sender, LocationChangedEventArgs e) 114 | { 115 | foreach (var modalReference in _modals.ToList()) 116 | { 117 | modalReference.Dismiss(ModalResult.Cancel()); 118 | } 119 | 120 | _modals.Clear(); 121 | await ClearBodyStyles(); 122 | await InvokeAsync(StateHasChanged); 123 | } 124 | 125 | private async Task Update(ModalReference modalReference) 126 | { 127 | _modals.Add(modalReference); 128 | 129 | if (!_haveActiveModals) 130 | { 131 | _haveActiveModals = true; 132 | if (_styleFunctions is not null) 133 | { 134 | await _styleFunctions.InvokeVoidAsync("setBodyStyle"); 135 | } 136 | } 137 | 138 | await InvokeAsync(StateHasChanged); 139 | } 140 | 141 | private ModalReference? GetModalReference(Guid id) 142 | => _modals.SingleOrDefault(x => x.Id == id); 143 | 144 | private async Task ClearBodyStyles() 145 | { 146 | _haveActiveModals = false; 147 | if (_styleFunctions is not null) 148 | { 149 | await _styleFunctions.InvokeVoidAsync("removeBodyStyle"); 150 | } 151 | } 152 | 153 | async ValueTask IAsyncDisposable.DisposeAsync() 154 | { 155 | if (_styleFunctions is not null) 156 | { 157 | try 158 | { 159 | await _styleFunctions.DisposeAsync(); 160 | } 161 | catch (JSDisconnectedException) 162 | { 163 | // If the browser is gone, we don't need it to clean up any browser-side state 164 | } 165 | } 166 | } 167 | 168 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/BlazoredModal.razor.js: -------------------------------------------------------------------------------- 1 | const el = document.body; 2 | const computedBodyStyle = getComputedStyle(el); 3 | const originalProps = { overflow: computedBodyStyle.overflow, paddingRight: computedBodyStyle.paddingRight }; 4 | const getScrollBarWidth = () => { 5 | let el = document.createElement("div"); 6 | el.style.cssText = "overflow:scroll; visibility:hidden; position:absolute;"; 7 | document.body.appendChild(el); 8 | let width = el.offsetWidth - el.clientWidth; 9 | el.remove(); 10 | return width; 11 | } 12 | const isScrollbarPresent = () => { 13 | const beforeScrollbarHidden = document.body.clientWidth; 14 | const overflowState = document.body?.style.overflow; 15 | document.body.style.overflow = 'hidden'; 16 | const afterScrollbarHidden = document.body.clientWidth; 17 | document.body.style.overflow = overflowState; 18 | return beforeScrollbarHidden !== afterScrollbarHidden; 19 | }; 20 | 21 | export function setBodyStyle() { 22 | if (isScrollbarPresent()) { 23 | el.style.paddingRight = `${getScrollBarWidth()}px`; 24 | } 25 | 26 | el.style.overflow = 'hidden'; 27 | } 28 | 29 | export function removeBodyStyle() { 30 | el.style.overflow = originalProps.overflow || 'auto'; 31 | el.style.paddingRight = originalProps.paddingRight; 32 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/BlazoredModalInstance.razor: -------------------------------------------------------------------------------- 1 | @if (UseCustomLayout) 2 | { 3 | 4 | @Content 5 | 6 | } 7 | else 8 | { 9 |
14 | 15 | 16 | 38 | 39 | 40 |
41 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/BlazoredModalInstance.razor.css: -------------------------------------------------------------------------------- 1 | .bm-container { 2 | display: block; 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | z-index: 100; 9 | overflow-x: hidden; 10 | overflow-y: auto; 11 | outline: 0; 12 | background-color: rgba(0,0,0,0.5); 13 | } 14 | 15 | .blazored-modal { 16 | display: flex; 17 | flex-direction: column; 18 | background-color: #fff; 19 | border-radius: 4px; 20 | border: 1px solid #fff; 21 | padding: 1.5rem; 22 | box-shadow: 0 2px 2px rgba(0,0,0,.25); 23 | margin: 1.75rem; 24 | } 25 | 26 | .size-small { 27 | max-width: 300px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | 32 | .size-medium { 33 | max-width: 500px; 34 | margin-left: auto; 35 | margin-right: auto; 36 | } 37 | 38 | .size-large { 39 | max-width: 800px; 40 | margin-left: auto; 41 | margin-right: auto; 42 | } 43 | 44 | .size-extra-large { 45 | max-width: 1140px; 46 | margin-left: auto; 47 | margin-right: auto; 48 | } 49 | 50 | .size-automatic { 51 | width: -moz-fit-content; 52 | width: fit-content; 53 | margin-left: auto; 54 | margin-right: auto; 55 | } 56 | 57 | .bm-header { 58 | display: flex; 59 | align-items: flex-start; 60 | justify-content: space-between; 61 | padding: 0 0 2rem 0; 62 | } 63 | 64 | .bm-title { 65 | margin-bottom: 0; 66 | } 67 | 68 | .bm-close { 69 | padding: 1rem; 70 | margin: -1rem -1rem -1rem auto; 71 | background-color: transparent; 72 | border: 0; 73 | -webkit-appearance: none; 74 | cursor: pointer; 75 | font-size: 1.5rem; 76 | font-weight: bold; 77 | } 78 | 79 | .position-topleft .blazored-modal { 80 | margin-left: 1.75rem; 81 | } 82 | 83 | .position-topright .blazored-modal { 84 | margin-right: 1.75rem; 85 | } 86 | 87 | .position-bottomleft .blazored-modal { 88 | position: absolute; 89 | bottom: 0; 90 | left: 1.75rem; 91 | } 92 | 93 | .position-bottomright .blazored-modal { 94 | position: absolute; 95 | bottom: 0; 96 | right: 1.75rem; 97 | } 98 | 99 | .position-middle { 100 | display: grid; 101 | justify-content: center; 102 | align-items: center; 103 | } 104 | 105 | .fade-in { 106 | animation: 300ms ease-out 0s ModalFadeIn; 107 | } 108 | 109 | .fade-out { 110 | animation: 300ms ease-out 0s ModalFadeOut; 111 | opacity: 0; 112 | } 113 | 114 | @keyframes ModalFadeIn { 115 | 0% { 116 | opacity: 0; 117 | } 118 | 119 | 100% { 120 | opacity: 1; 121 | } 122 | } 123 | 124 | @keyframes ModalFadeOut { 125 | 0% { 126 | opacity: 1; 127 | } 128 | 129 | 100% { 130 | opacity: 0; 131 | } 132 | } 133 | 134 | .bm-container.pop-in .blazored-modal { 135 | animation: 300ms ease-out 0s ModalPopIn; 136 | } 137 | 138 | .bm-container.pop-out .blazored-modal { 139 | animation: 300ms ease-in 0s ModalPopOut; 140 | transform: scale(0%); 141 | } 142 | 143 | @keyframes ModalPopIn { 144 | 0% { 145 | transform: scale(0%); 146 | } 147 | 148 | 50% { 149 | transform: scale(110%); 150 | } 151 | 152 | 100% { 153 | transform: scale(100%); 154 | } 155 | } 156 | 157 | @keyframes ModalPopOut { 158 | 0% { 159 | transform: scale(100%); 160 | } 161 | 162 | 50% { 163 | transform: scale(110%); 164 | } 165 | 166 | 100% { 167 | transform: scale(0%); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/Blazored.Modal/CascadingBlazoredModal.razor: -------------------------------------------------------------------------------- 1 | @inject IModalService ModalService 2 | 3 | 4 | 16 | @ChildContent 17 | 18 | 19 | @code { 20 | [Parameter] public RenderFragment? ChildContent { get; set; } 21 | [Parameter] public bool? HideHeader { get; set; } 22 | [Parameter] public bool? HideCloseButton { get; set; } 23 | [Parameter] public bool? DisableBackgroundCancel { get; set; } 24 | [Parameter] public ModalPosition? Position { get; set; } 25 | [Parameter] public ModalSize? Size { get; set; } 26 | [Parameter] public string? Class { get; set; } 27 | [Parameter] public ModalAnimationType? AnimationType { get; set; } 28 | [Parameter] public bool? UseCustomLayout { get; set; } 29 | [Parameter] public string? OverlayCustomClass { get; set; } 30 | [Parameter] public bool? ContentScrollable { get; set; } 31 | [Parameter] public bool? ActivateFocusTrap { get; set; } 32 | [Parameter] public string? PositionCustomClass { get; set; } 33 | [Parameter] public string? SizeCustomClass { get; set; } 34 | } 35 | -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/IModalReference.cs: -------------------------------------------------------------------------------- 1 | using Blazored.Modal.Services; 2 | 3 | namespace Blazored.Modal; 4 | 5 | public interface IModalReference 6 | { 7 | Task Result { get; } 8 | 9 | void Close(); 10 | void Close(ModalResult result); 11 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/ModalAnimationType.cs: -------------------------------------------------------------------------------- 1 | namespace Blazored.Modal; 2 | 3 | public enum ModalAnimationType 4 | { 5 | FadeInOut, 6 | PopInOut, 7 | PopIn, 8 | None 9 | } 10 | -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/ModalOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Blazored.Modal; 2 | 3 | public class ModalOptions 4 | { 5 | public ModalPosition? Position { get; set; } 6 | public string? PositionCustomClass { get; set; } 7 | public ModalSize? Size { get; set; } 8 | public string? SizeCustomClass { get; set; } 9 | public string? OverlayCustomClass { get; set; } 10 | public string? Class { get; set; } 11 | public bool? DisableBackgroundCancel { get; set; } 12 | public bool? HideHeader { get; set; } 13 | public bool? HideCloseButton { get; set; } 14 | public ModalAnimationType? AnimationType { get; set; } 15 | public bool? UseCustomLayout { get; set; } 16 | public bool? ActivateFocusTrap { get; set; } 17 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/ModalParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Blazored.Modal; 4 | 5 | public class ModalParameters : IEnumerable> 6 | { 7 | internal readonly Dictionary Parameters; 8 | 9 | public ModalParameters() 10 | { 11 | Parameters = new Dictionary(); 12 | } 13 | 14 | public ModalParameters Add(string parameterName, object? value) 15 | { 16 | Parameters[parameterName] = value; 17 | return this; 18 | } 19 | 20 | public T Get(string parameterName) 21 | { 22 | if (!Parameters.TryGetValue(parameterName, out var value)) 23 | { 24 | throw new KeyNotFoundException($"{parameterName} does not exist in modal parameters"); 25 | } 26 | 27 | if (value is not T typedValue) 28 | { 29 | throw new InvalidOperationException($"The value for parameter '{parameterName}' is not of the expected type {typeof(T)}."); 30 | } 31 | 32 | return typedValue; 33 | } 34 | 35 | public T? TryGet(string parameterName) where T : class 36 | { 37 | return Parameters.TryGetValue(parameterName, out var objValue) && objValue is T typedValue 38 | ? typedValue 39 | : null; 40 | } 41 | 42 | public IEnumerator> GetEnumerator() 43 | => Parameters.GetEnumerator(); 44 | 45 | IEnumerator IEnumerable.GetEnumerator() 46 | => Parameters.GetEnumerator(); 47 | } 48 | -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/ModalPosition.cs: -------------------------------------------------------------------------------- 1 | namespace Blazored.Modal; 2 | 3 | public enum ModalPosition 4 | { 5 | TopCenter, 6 | TopLeft, 7 | TopRight, 8 | Middle, 9 | BottomLeft, 10 | BottomRight, 11 | Custom 12 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/ModalReference.cs: -------------------------------------------------------------------------------- 1 | using Blazored.Modal.Services; 2 | using Microsoft.AspNetCore.Components; 3 | 4 | namespace Blazored.Modal; 5 | 6 | public class ModalReference : IModalReference 7 | { 8 | private readonly TaskCompletionSource _resultCompletion = new(TaskCreationOptions.RunContinuationsAsynchronously); 9 | private readonly Action _closed; 10 | private readonly ModalService _modalService; 11 | 12 | internal Guid Id { get; } 13 | internal RenderFragment ModalInstance { get; } 14 | internal BlazoredModalInstance? ModalInstanceRef { get; set; } 15 | 16 | public ModalReference(Guid modalInstanceId, RenderFragment modalInstance, ModalService modalService) 17 | { 18 | Id = modalInstanceId; 19 | ModalInstance = modalInstance; 20 | _closed = HandleClosed; 21 | _modalService = modalService; 22 | } 23 | 24 | public void Close() 25 | => _modalService.Close(this); 26 | 27 | public void Close(ModalResult result) 28 | => _modalService.Close(this, result); 29 | 30 | public Task Result => _resultCompletion.Task; 31 | 32 | internal void Dismiss(ModalResult result) 33 | => _closed.Invoke(result); 34 | 35 | private void HandleClosed(ModalResult obj) 36 | => _ = _resultCompletion.TrySetResult(obj); 37 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/Configuration/ModalSize.cs: -------------------------------------------------------------------------------- 1 | namespace Blazored.Modal; 2 | 3 | public enum ModalSize 4 | { 5 | Small, 6 | Medium, 7 | Large, 8 | ExtraLarge, 9 | Custom, 10 | Automatic 11 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/FocusTrap.razor: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | @ChildContent 5 |
6 |
7 |
8 | 9 | @code { 10 | private ElementReference _container; 11 | private ElementReference _startFirst; 12 | private ElementReference _startSecond; 13 | private ElementReference _endFirst; 14 | private ElementReference _endSecond; 15 | private bool _shiftPressed; 16 | 17 | [Parameter] public RenderFragment ChildContent { get; set; } = default!; 18 | [Parameter] public bool IsActive { get; set; } 19 | 20 | protected override bool ShouldRender() 21 | => false; 22 | 23 | protected override async Task OnAfterRenderAsync(bool firstRender) 24 | { 25 | if (firstRender) 26 | { 27 | await _startFirst.FocusAsync(); 28 | } 29 | } 30 | 31 | internal async Task SetFocus() 32 | => await _startFirst.FocusAsync(); 33 | 34 | private async Task FocusStartAsync(FocusEventArgs args) 35 | { 36 | if (!_shiftPressed) 37 | { 38 | await _startFirst.FocusAsync(); 39 | } 40 | } 41 | 42 | private async Task FocusEndAsync(FocusEventArgs args) 43 | { 44 | if (_shiftPressed) 45 | { 46 | await _endFirst.FocusAsync(); 47 | } 48 | } 49 | 50 | private void HandleKeyPresses(KeyboardEventArgs args) 51 | { 52 | if (args.Key == "Tab") 53 | { 54 | _shiftPressed = args.ShiftKey; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Blazored.Modal.Services; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Blazored.Modal; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddBlazoredModal(this IServiceCollection services) 9 | => services.AddScoped(); 10 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/Services/IModalService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace Blazored.Modal.Services; 4 | 5 | public interface IModalService 6 | { 7 | /// 8 | /// Shows a modal containing the specified . 9 | /// 10 | IModalReference Show() where TComponent : IComponent; 11 | 12 | /// 13 | /// Shows a modal containing a with the specified . 14 | /// 15 | /// Options to configure the modal. 16 | IModalReference Show(ModalOptions options) where TComponent : IComponent; 17 | 18 | /// 19 | /// Shows a modal containing a with the specified . 20 | /// 21 | /// Key/Value collection of parameters to pass to component being displayed. 22 | IModalReference Show(ModalParameters parameters) where TComponent : IComponent; 23 | 24 | /// 25 | /// Shows a modal containing a with the specified 26 | /// and . 27 | /// 28 | /// Key/Value collection of parameters to pass to component being displayed. 29 | /// Options to configure the modal. 30 | IModalReference Show(ModalParameters parameters, ModalOptions options) where TComponent : IComponent; 31 | 32 | /// 33 | /// Shows a modal containing a with the specified . 34 | /// 35 | /// Modal title 36 | IModalReference Show(string title) where TComponent : IComponent; 37 | 38 | /// 39 | /// Shows a modal containing a with the specified and . 40 | /// 41 | /// Modal title 42 | /// Options to configure the modal 43 | IModalReference Show(string title, ModalOptions options) where TComponent : IComponent; 44 | 45 | /// 46 | /// Shows a modal containing a with the specified and . 47 | /// 48 | /// Modal title 49 | /// Key/Value collection of parameters to pass to component being displayed 50 | IModalReference Show(string title, ModalParameters parameters) where TComponent : IComponent; 51 | 52 | /// 53 | /// Shows a modal containing a with the specified , 54 | /// and . 55 | /// 56 | /// Modal title. 57 | /// Key/Value collection of parameters to pass to component being displayed. 58 | /// Options to configure the modal. 59 | IModalReference Show(string title, ModalParameters parameters, ModalOptions options) where TComponent : IComponent; 60 | 61 | /// 62 | /// Shows a modal containing a . 63 | /// 64 | /// Type of component to display. 65 | IModalReference Show(Type component); 66 | 67 | /// 68 | /// Shows a modal containing a with the specified . 69 | /// 70 | /// Type of component to display. 71 | /// Modal title. 72 | IModalReference Show(Type component, string title); 73 | 74 | /// 75 | /// Shows a modal containing a with the specified and . 76 | /// 77 | /// Type of component to display. 78 | /// Modal title. 79 | /// Options to configure the modal. 80 | IModalReference Show(Type component, string title, ModalOptions options); 81 | 82 | /// 83 | /// Shows a modal containing a with the specified and . 84 | /// 85 | /// Type of component to display. 86 | /// Modal title. 87 | /// Key/Value collection of parameters to pass to component being displayed. 88 | IModalReference Show(Type component, string title, ModalParameters parameters); 89 | 90 | /// 91 | /// Shows a modal containing a with the specified , 92 | /// and . 93 | /// 94 | /// Type of component to display. 95 | /// Modal title. 96 | /// Key/Value collection of parameters to pass to component being displayed. 97 | /// Options to configure the modal. 98 | IModalReference Show(Type component, string title, ModalParameters parameters, ModalOptions options); 99 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/Services/ModalResult.cs: -------------------------------------------------------------------------------- 1 | namespace Blazored.Modal.Services; 2 | 3 | public class ModalResult 4 | { 5 | public object? Data { get; } 6 | public Type? DataType { get; } 7 | public bool Cancelled { get; } 8 | public bool Confirmed => !Cancelled; 9 | 10 | private ModalResult(object? data, Type? resultType, bool cancelled) 11 | { 12 | Data = data; 13 | DataType = resultType; 14 | Cancelled = cancelled; 15 | } 16 | 17 | public static ModalResult Ok(T result) 18 | => Ok(result, typeof(T)); 19 | 20 | public static ModalResult Ok(T result, Type? dataType) 21 | => new(result, dataType, false); 22 | 23 | public static ModalResult Ok() 24 | => new(null, null, false); 25 | 26 | public static ModalResult Cancel() 27 | => new(null, null, true); 28 | 29 | public static ModalResult Cancel(T payload) 30 | => new(payload, null, true); 31 | } -------------------------------------------------------------------------------- /src/Blazored.Modal/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @using Blazored.Modal.Services 3 | @using Microsoft.JSInterop -------------------------------------------------------------------------------- /src/Blazored.Modal/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Blazored/Modal/92db30baaf607c8a8a8ac35199402280ed80927a/src/Blazored.Modal/icon.png -------------------------------------------------------------------------------- /tests/Blazored.Modal.Tests/Assets/MockNavigationManager.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace Blazored.Modal.Tests.Assets 4 | { 5 | internal class MockNavigationManager : NavigationManager 6 | { 7 | public MockNavigationManager() 8 | { 9 | } 10 | 11 | public MockNavigationManager(string baseUri = null, string uri = null) 12 | { 13 | Initialize(baseUri ?? "http://example.com/", uri ?? baseUri ?? "http://example.com/welcome-page"); 14 | } 15 | 16 | public new void Initialize(string baseUri, string uri) 17 | { 18 | base.Initialize(baseUri, uri); 19 | } 20 | 21 | protected override void NavigateToCore(string uri, bool forceLoad) 22 | { 23 | throw new System.NotImplementedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Blazored.Modal.Tests/Assets/TestComponent.razor: -------------------------------------------------------------------------------- 1 | 
2 |

@Title

3 | 4 |
5 | 6 | @code { 7 | [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } 8 | 9 | [Parameter] public string Title { get; set; } = DefaultTitle; 10 | 11 | public static string DefaultTitle = "My Test Component"; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /tests/Blazored.Modal.Tests/Blazored.Modal.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/Blazored.Modal.Tests/DisplayTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Bunit; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.AspNetCore.Components; 5 | using Blazored.Modal.Tests.Assets; 6 | using Blazored.Modal.Services; 7 | using static Bunit.ComponentParameterFactory; 8 | 9 | namespace Blazored.Modal.Tests 10 | { 11 | public class DisplayTests : TestContext 12 | { 13 | public DisplayTests() 14 | { 15 | Services.AddScoped(); 16 | Services.AddBlazoredModal(); 17 | 18 | JSInterop.Mode = JSRuntimeMode.Loose; 19 | } 20 | 21 | [Fact] 22 | public void ModalIsNotVisibleByDefault() 23 | { 24 | // Arrange 25 | var modalService = Services.GetService(); 26 | 27 | // Act 28 | var cut = RenderComponent(CascadingValue(modalService)); 29 | 30 | // Assert 31 | Assert.Empty(cut.FindAll(".bm-container")); 32 | } 33 | 34 | [Fact] 35 | public void ModalIsVisibleWhenShowCalled() 36 | { 37 | // Arrange 38 | var modalService = Services.GetService(); 39 | var cut = RenderComponent(CascadingValue(modalService)); 40 | 41 | // Act 42 | modalService.Show(); 43 | 44 | // Assert 45 | Assert.NotNull(cut.FindComponent()); 46 | } 47 | 48 | [Fact] 49 | public void MultipleModalsAreVisibleWhenShowCalledMultipleTimes() 50 | { 51 | // Arrange 52 | var modalService = Services.GetService(); 53 | var cut = RenderComponent(CascadingValue(modalService)); 54 | 55 | // Act 56 | modalService.Show(); 57 | modalService.Show(); 58 | 59 | // Assert 60 | Assert.Equal(2, cut.FindAll(".bm-container").Count); 61 | } 62 | 63 | [Fact] 64 | public void ModalHidesWhenCloseCalled() 65 | { 66 | // Arrange 67 | var modalService = Services.GetService(); 68 | var cut = RenderComponent(CascadingValue(modalService)); 69 | 70 | // Act 71 | var options = new ModalOptions 72 | { 73 | AnimationType = ModalAnimationType.None 74 | }; 75 | modalService.Show("", options); 76 | Assert.Single(cut.FindAll(".bm-container")); 77 | 78 | var closeButton = cut.Find(".test-component__close-button"); 79 | closeButton.Click(); 80 | 81 | // Assert 82 | Assert.Empty(cut.FindAll(".bm-container")); 83 | } 84 | 85 | [Fact] 86 | public void ModalHidesWhenCancelCalled() 87 | { 88 | // Arrange 89 | var modalService = Services.GetService(); 90 | var cut = RenderComponent(CascadingValue(modalService)); 91 | 92 | // Act 93 | var options = new ModalOptions 94 | { 95 | AnimationType = ModalAnimationType.None 96 | }; 97 | modalService.Show("", options); 98 | Assert.Single(cut.FindAll(".bm-container")); 99 | 100 | var closeButton = cut.Find(".bm-close"); 101 | closeButton.Click(); 102 | 103 | // Assert 104 | Assert.Empty(cut.FindAll(".bm-container")); 105 | } 106 | 107 | [Fact] 108 | public void ModalHidesWhenReferenceCloseCalled() 109 | { 110 | // Arrange 111 | var modalService = Services.GetService(); 112 | var cut = RenderComponent(CascadingValue(modalService)); 113 | 114 | // Act 115 | var options = new ModalOptions 116 | { 117 | AnimationType = ModalAnimationType.None 118 | }; 119 | var modalReferece = modalService.Show("", options); 120 | Assert.Single(cut.FindAll(".bm-container")); 121 | 122 | modalReferece.Close(); 123 | 124 | // Assert 125 | Assert.Empty(cut.FindAll(".bm-container")); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /tests/Blazored.Modal.Tests/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @using Microsoft.Extensions.DependencyInjection 3 | 4 | @using Bunit 5 | @using Xunit 6 | 7 | @using Blazored.Modal 8 | @using Blazored.Modal.Services 9 | @using Blazored.Modal.Tests.Assets --------------------------------------------------------------------------------