├── .EditorConfig
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── Blazor.Confetti.sln
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── confetti.gif
└── icon.png
├── samples
├── KristofferStrube.Blazor.Confetti.WasmSample
│ ├── App.razor
│ ├── KristofferStrube.Blazor.Confetti.WasmSample.csproj
│ ├── Layout
│ │ ├── MainLayout.razor
│ │ ├── MainLayout.razor.css
│ │ ├── NavMenu.razor
│ │ └── NavMenu.razor.css
│ ├── Pages
│ │ ├── Counter.razor
│ │ └── Home.razor
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── _Imports.razor
│ └── wwwroot
│ │ ├── 404.html
│ │ ├── css
│ │ ├── app.css
│ │ └── bootstrap
│ │ │ ├── bootstrap.min.css
│ │ │ └── bootstrap.min.css.map
│ │ ├── favicon.png
│ │ ├── icon-192.png
│ │ ├── index.html
│ │ └── sample-data
│ │ └── weather.json
└── KristofferStrube.Blazor.Confetti.WebAppSample
│ ├── Components
│ ├── App.razor
│ ├── Layout
│ │ ├── MainLayout.razor
│ │ ├── MainLayout.razor.css
│ │ ├── NavMenu.razor
│ │ └── NavMenu.razor.css
│ ├── Pages
│ │ ├── Counter.razor
│ │ ├── Error.razor
│ │ └── Home.razor
│ ├── Routes.razor
│ └── _Imports.razor
│ ├── KristofferStrube.Blazor.Confetti.WebAppSample.csproj
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── wwwroot
│ ├── app.css
│ ├── bootstrap
│ ├── bootstrap.min.css
│ └── bootstrap.min.css.map
│ └── favicon.png
└── src
└── KristofferStrube.Blazor.Confetti
├── Confetti.razor
├── ConfettiAnimator.razor
├── ConfettiOptions.cs
├── ConfettiOriginMode.cs
├── ConfettiPiece.cs
├── ConfettiService.cs
├── ConfettiType.cs
├── DoubleExtensions.cs
├── IServiceExtensions.cs
└── KristofferStrube.Blazor.Confetti.csproj
/.EditorConfig:
--------------------------------------------------------------------------------
1 | [*]
2 | # All files
3 | dotnet_style_qualification_for_field = false
4 | dotnet_style_qualification_for_property = false
5 | dotnet_style_qualification_for_method = false
6 | dotnet_style_qualification_for_event = false
7 | dotnet_diagnostic.IDE0003.severity = warning
8 | dotnet_style_predefined_type_for_locals_parameters_members = true
9 | dotnet_style_predefined_type_for_member_access = true
10 | dotnet_diagnostic.IDE0049.severity = suggestion
11 | csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
12 | dotnet_diagnostic.IDE0036.severity = error
13 | dotnet_style_require_accessibility_modifiers = always
14 | dotnet_diagnostic.IDE0040.severity = warning
15 | dotnet_style_readonly_field = true
16 | dotnet_diagnostic.IDE0044.severity = error
17 | csharp_prefer_static_local_function = true
18 | dotnet_diagnostic.IDE0062.severity = warning
19 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
20 | dotnet_diagnostic.IDE0047.severity = warning
21 | dotnet_diagnostic.IDE0048.severity = warning
22 | dotnet_diagnostic.IDE0010.severity = error
23 | dotnet_style_object_initializer = true
24 | dotnet_diagnostic.IDE0017.severity = suggestion
25 | csharp_style_inlined_variable_declaration = true
26 | dotnet_diagnostic.IDE0018.severity = suggestion
27 | dotnet_style_collection_initializer = true
28 | dotnet_diagnostic.IDE0028.severity = warning
29 | dotnet_style_prefer_auto_properties = true
30 | dotnet_diagnostic.IDE0032.severity = suggestion
31 | dotnet_style_explicit_tuple_names = true
32 | dotnet_diagnostic.IDE0033.severity = warning
33 | csharp_prefer_simple_default_expression = false
34 | dotnet_diagnostic.IDE0034.severity = warning
35 | dotnet_style_prefer_inferred_tuple_names = true
36 | dotnet_style_prefer_inferred_anonymous_type_member_names = true
37 | dotnet_diagnostic.IDE0037.severity = suggestion
38 | csharp_style_prefer_local_over_anonymous_function = true
39 | dotnet_diagnostic.IDE0039.severity = warning
40 | csharp_style_deconstructed_variable_declaration = true
41 | dotnet_diagnostic.IDE0042.severity = suggestion
42 | dotnet_style_prefer_conditional_expression_over_assignment = true
43 | dotnet_diagnostic.IDE0045.severity = warning
44 | dotnet_style_prefer_conditional_expression_over_return = true
45 | dotnet_diagnostic.IDE0046.severity = warning
46 | dotnet_style_prefer_compound_assignment = true
47 | dotnet_diagnostic.IDE0054.severity = warning
48 | dotnet_diagnostic.IDE0074.severity = warning
49 | csharp_style_prefer_index_operator = true
50 | dotnet_diagnostic.IDE0056.severity = warning
51 | csharp_style_prefer_range_operator = true
52 | dotnet_diagnostic.IDE0057.severity = warning
53 | dotnet_diagnostic.IDE0070.severity = error
54 | dotnet_style_prefer_simplified_interpolation = true
55 | dotnet_diagnostic.IDE0071.severity = warning
56 | dotnet_diagnostic.IDE0072.severity = error
57 | dotnet_style_prefer_simplified_boolean_expressions = true
58 | dotnet_diagnostic.IDE0075.severity = warning
59 | dotnet_diagnostic.IDE0082.severity = error
60 | csharp_style_implicit_object_creation_when_type_is_apparent = true
61 | dotnet_diagnostic.IDE0090.severity = error
62 | dotnet_diagnostic.IDE0180.severity = warning
63 | csharp_style_namespace_declarations = file_scoped
64 | dotnet_diagnostic.IDE0160.severity = error
65 | dotnet_diagnostic.IDE0161.severity = error
66 | csharp_style_throw_expression = true
67 | dotnet_diagnostic.IDE0016.severity = warning
68 | dotnet_style_coalesce_expression = true
69 | dotnet_diagnostic.IDE0029.severity = warning
70 | dotnet_diagnostic.IDE0030.severity = warning
71 | dotnet_style_null_propagation = true
72 | dotnet_diagnostic.IDE0031.severity = warning
73 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true
74 | dotnet_diagnostic.IDE0041.severity = warning
75 | csharp_style_prefer_null_check_over_type_check = true
76 | dotnet_diagnostic.IDE0150.severity = warning
77 | csharp_style_conditional_delegate_call = false
78 | dotnet_diagnostic.IDE1005.severity = warning
79 | csharp_style_var_for_built_in_types = false
80 | csharp_style_var_when_type_is_apparent = true
81 | csharp_style_var_elsewhere = false
82 | dotnet_diagnostic.IDE0007.severity = warning
83 | dotnet_diagnostic.IDE0008.severity = warning
84 | dotnet_diagnostic.IDE0001.severity = error
85 | dotnet_diagnostic.IDE0002.severity = error
86 | dotnet_diagnostic.IDE0004.severity = error
87 | dotnet_diagnostic.IDE0005.severity = error
88 | dotnet_diagnostic.IDE0035.severity = warning
89 | dotnet_diagnostic.IDE0051.severity = warning
90 | dotnet_diagnostic.IDE0052.severity = warning
91 | csharp_style_unused_value_expression_statement_preference = discard_variable
92 | dotnet_diagnostic.IDE0058.severity = warning
93 | csharp_style_unused_value_assignment_preference = discard_variable
94 | dotnet_diagnostic.IDE0059.severity = warning
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: 'Publish application'
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - '**/README.md'
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | # Checkout the code
15 | - uses: actions/checkout@v2
16 |
17 | # Install .NET 8.0 SDK
18 | - name: Setup .NET 8 preview
19 | uses: actions/setup-dotnet@v1
20 | with:
21 | dotnet-version: '8.0.x'
22 | include-prerelease: true
23 |
24 | # Added Ahead-Of-Time workload
25 | - name: Add AOT Workload
26 | run: |
27 | dotnet workload install wasm-tools-net7
28 | dotnet workload restore
29 |
30 | # Generate the website
31 | - name: Publish
32 | run: dotnet publish samples/KristofferStrube.Blazor.Confetti.WasmSample/KristofferStrube.Blazor.Confetti.WasmSample.csproj --configuration Release --output build
33 |
34 | # Publish the website
35 | - name: GitHub Pages action
36 | if: ${{ github.ref == 'refs/heads/main' }} # Publish only when the push is on main
37 | uses: peaceiris/actions-gh-pages@v3.6.1
38 | with:
39 | github_token: ${{ secrets.PUBLISH_TOKEN }}
40 | publish_branch: gh-pages
41 | publish_dir: build/wwwroot
42 | allow_empty_commit: false
43 | keep_files: false
44 | force_orphan: true
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from `dotnet new gitignore`
5 |
6 | # dotenv files
7 | .env
8 |
9 | # User-specific files
10 | *.rsuser
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # User-specific files (MonoDevelop/Xamarin Studio)
17 | *.userprefs
18 |
19 | # Mono auto generated files
20 | mono_crash.*
21 |
22 | # Build results
23 | [Dd]ebug/
24 | [Dd]ebugPublic/
25 | [Rr]elease/
26 | [Rr]eleases/
27 | x64/
28 | x86/
29 | [Ww][Ii][Nn]32/
30 | [Aa][Rr][Mm]/
31 | [Aa][Rr][Mm]64/
32 | bld/
33 | [Bb]in/
34 | [Oo]bj/
35 | [Ll]og/
36 | [Ll]ogs/
37 |
38 | # Visual Studio 2015/2017 cache/options directory
39 | .vs/
40 | # Uncomment if you have tasks that create the project's static files in wwwroot
41 | #wwwroot/
42 |
43 | # Visual Studio 2017 auto generated files
44 | Generated\ Files/
45 |
46 | # MSTest test Results
47 | [Tt]est[Rr]esult*/
48 | [Bb]uild[Ll]og.*
49 |
50 | # NUnit
51 | *.VisualState.xml
52 | TestResult.xml
53 | nunit-*.xml
54 |
55 | # Build Results of an ATL Project
56 | [Dd]ebugPS/
57 | [Rr]eleasePS/
58 | dlldata.c
59 |
60 | # Benchmark Results
61 | BenchmarkDotNet.Artifacts/
62 |
63 | # .NET
64 | project.lock.json
65 | project.fragment.lock.json
66 | artifacts/
67 |
68 | # Tye
69 | .tye/
70 |
71 | # ASP.NET Scaffolding
72 | ScaffoldingReadMe.txt
73 |
74 | # StyleCop
75 | StyleCopReport.xml
76 |
77 | # Files built by Visual Studio
78 | *_i.c
79 | *_p.c
80 | *_h.h
81 | *.ilk
82 | *.meta
83 | *.obj
84 | *.iobj
85 | *.pch
86 | *.pdb
87 | *.ipdb
88 | *.pgc
89 | *.pgd
90 | *.rsp
91 | *.sbr
92 | *.tlb
93 | *.tli
94 | *.tlh
95 | *.tmp
96 | *.tmp_proj
97 | *_wpftmp.csproj
98 | *.log
99 | *.tlog
100 | *.vspscc
101 | *.vssscc
102 | .builds
103 | *.pidb
104 | *.svclog
105 | *.scc
106 |
107 | # Chutzpah Test files
108 | _Chutzpah*
109 |
110 | # Visual C++ cache files
111 | ipch/
112 | *.aps
113 | *.ncb
114 | *.opendb
115 | *.opensdf
116 | *.sdf
117 | *.cachefile
118 | *.VC.db
119 | *.VC.VC.opendb
120 |
121 | # Visual Studio profiler
122 | *.psess
123 | *.vsp
124 | *.vspx
125 | *.sap
126 |
127 | # Visual Studio Trace Files
128 | *.e2e
129 |
130 | # TFS 2012 Local Workspace
131 | $tf/
132 |
133 | # Guidance Automation Toolkit
134 | *.gpState
135 |
136 | # ReSharper is a .NET coding add-in
137 | _ReSharper*/
138 | *.[Rr]e[Ss]harper
139 | *.DotSettings.user
140 |
141 | # TeamCity is a build add-in
142 | _TeamCity*
143 |
144 | # DotCover is a Code Coverage Tool
145 | *.dotCover
146 |
147 | # AxoCover is a Code Coverage Tool
148 | .axoCover/*
149 | !.axoCover/settings.json
150 |
151 | # Coverlet is a free, cross platform Code Coverage Tool
152 | coverage*.json
153 | coverage*.xml
154 | coverage*.info
155 |
156 | # Visual Studio code coverage results
157 | *.coverage
158 | *.coveragexml
159 |
160 | # NCrunch
161 | _NCrunch_*
162 | .*crunch*.local.xml
163 | nCrunchTemp_*
164 |
165 | # MightyMoose
166 | *.mm.*
167 | AutoTest.Net/
168 |
169 | # Web workbench (sass)
170 | .sass-cache/
171 |
172 | # Installshield output folder
173 | [Ee]xpress/
174 |
175 | # DocProject is a documentation generator add-in
176 | DocProject/buildhelp/
177 | DocProject/Help/*.HxT
178 | DocProject/Help/*.HxC
179 | DocProject/Help/*.hhc
180 | DocProject/Help/*.hhk
181 | DocProject/Help/*.hhp
182 | DocProject/Help/Html2
183 | DocProject/Help/html
184 |
185 | # Click-Once directory
186 | publish/
187 |
188 | # Publish Web Output
189 | *.[Pp]ublish.xml
190 | *.azurePubxml
191 | # Note: Comment the next line if you want to checkin your web deploy settings,
192 | # but database connection strings (with potential passwords) will be unencrypted
193 | *.pubxml
194 | *.publishproj
195 |
196 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
197 | # checkin your Azure Web App publish settings, but sensitive information contained
198 | # in these scripts will be unencrypted
199 | PublishScripts/
200 |
201 | # NuGet Packages
202 | *.nupkg
203 | # NuGet Symbol Packages
204 | *.snupkg
205 | # The packages folder can be ignored because of Package Restore
206 | **/[Pp]ackages/*
207 | # except build/, which is used as an MSBuild target.
208 | !**/[Pp]ackages/build/
209 | # Uncomment if necessary however generally it will be regenerated when needed
210 | #!**/[Pp]ackages/repositories.config
211 | # NuGet v3's project.json files produces more ignorable files
212 | *.nuget.props
213 | *.nuget.targets
214 |
215 | # Microsoft Azure Build Output
216 | csx/
217 | *.build.csdef
218 |
219 | # Microsoft Azure Emulator
220 | ecf/
221 | rcf/
222 |
223 | # Windows Store app package directories and files
224 | AppPackages/
225 | BundleArtifacts/
226 | Package.StoreAssociation.xml
227 | _pkginfo.txt
228 | *.appx
229 | *.appxbundle
230 | *.appxupload
231 |
232 | # Visual Studio cache files
233 | # files ending in .cache can be ignored
234 | *.[Cc]ache
235 | # but keep track of directories ending in .cache
236 | !?*.[Cc]ache/
237 |
238 | # Others
239 | ClientBin/
240 | ~$*
241 | *~
242 | *.dbmdl
243 | *.dbproj.schemaview
244 | *.jfm
245 | *.pfx
246 | *.publishsettings
247 | orleans.codegen.cs
248 |
249 | # Including strong name files can present a security risk
250 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
251 | #*.snk
252 |
253 | # Since there are multiple workflows, uncomment next line to ignore bower_components
254 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
255 | #bower_components/
256 |
257 | # RIA/Silverlight projects
258 | Generated_Code/
259 |
260 | # Backup & report files from converting an old project file
261 | # to a newer Visual Studio version. Backup files are not needed,
262 | # because we have git ;-)
263 | _UpgradeReport_Files/
264 | Backup*/
265 | UpgradeLog*.XML
266 | UpgradeLog*.htm
267 | ServiceFabricBackup/
268 | *.rptproj.bak
269 |
270 | # SQL Server files
271 | *.mdf
272 | *.ldf
273 | *.ndf
274 |
275 | # Business Intelligence projects
276 | *.rdl.data
277 | *.bim.layout
278 | *.bim_*.settings
279 | *.rptproj.rsuser
280 | *- [Bb]ackup.rdl
281 | *- [Bb]ackup ([0-9]).rdl
282 | *- [Bb]ackup ([0-9][0-9]).rdl
283 |
284 | # Microsoft Fakes
285 | FakesAssemblies/
286 |
287 | # GhostDoc plugin setting file
288 | *.GhostDoc.xml
289 |
290 | # Node.js Tools for Visual Studio
291 | .ntvs_analysis.dat
292 | node_modules/
293 |
294 | # Visual Studio 6 build log
295 | *.plg
296 |
297 | # Visual Studio 6 workspace options file
298 | *.opt
299 |
300 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
301 | *.vbw
302 |
303 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
304 | *.vbp
305 |
306 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
307 | *.dsw
308 | *.dsp
309 |
310 | # Visual Studio 6 technical files
311 | *.ncb
312 | *.aps
313 |
314 | # Visual Studio LightSwitch build output
315 | **/*.HTMLClient/GeneratedArtifacts
316 | **/*.DesktopClient/GeneratedArtifacts
317 | **/*.DesktopClient/ModelManifest.xml
318 | **/*.Server/GeneratedArtifacts
319 | **/*.Server/ModelManifest.xml
320 | _Pvt_Extensions
321 |
322 | # Paket dependency manager
323 | .paket/paket.exe
324 | paket-files/
325 |
326 | # FAKE - F# Make
327 | .fake/
328 |
329 | # CodeRush personal settings
330 | .cr/personal
331 |
332 | # Python Tools for Visual Studio (PTVS)
333 | __pycache__/
334 | *.pyc
335 |
336 | # Cake - Uncomment if you are using it
337 | # tools/**
338 | # !tools/packages.config
339 |
340 | # Tabs Studio
341 | *.tss
342 |
343 | # Telerik's JustMock configuration file
344 | *.jmconfig
345 |
346 | # BizTalk build output
347 | *.btp.cs
348 | *.btm.cs
349 | *.odx.cs
350 | *.xsd.cs
351 |
352 | # OpenCover UI analysis results
353 | OpenCover/
354 |
355 | # Azure Stream Analytics local run output
356 | ASALocalRun/
357 |
358 | # MSBuild Binary and Structured Log
359 | *.binlog
360 |
361 | # NVidia Nsight GPU debugger configuration file
362 | *.nvuser
363 |
364 | # MFractors (Xamarin productivity tool) working folder
365 | .mfractor/
366 |
367 | # Local History for Visual Studio
368 | .localhistory/
369 |
370 | # Visual Studio History (VSHistory) files
371 | .vshistory/
372 |
373 | # BeatPulse healthcheck temp database
374 | healthchecksdb
375 |
376 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
377 | MigrationBackup/
378 |
379 | # Ionide (cross platform F# VS Code tools) working folder
380 | .ionide/
381 |
382 | # Fody - auto-generated XML schema
383 | FodyWeavers.xsd
384 |
385 | # VS Code files for those working on multiple tools
386 | .vscode/*
387 | !.vscode/settings.json
388 | !.vscode/tasks.json
389 | !.vscode/launch.json
390 | !.vscode/extensions.json
391 | *.code-workspace
392 |
393 | # Local History for Visual Studio Code
394 | .history/
395 |
396 | # Windows Installer files from build outputs
397 | *.cab
398 | *.msi
399 | *.msix
400 | *.msm
401 | *.msp
402 |
403 | # JetBrains Rider
404 | *.sln.iml
405 | .idea
406 |
407 | ##
408 | ## Visual studio for Mac
409 | ##
410 |
411 |
412 | # globs
413 | Makefile.in
414 | *.userprefs
415 | *.usertasks
416 | config.make
417 | config.status
418 | aclocal.m4
419 | install-sh
420 | autom4te.cache/
421 | *.tar.gz
422 | tarballs/
423 | test-results/
424 |
425 | # Mac bundle stuff
426 | *.dmg
427 | *.app
428 |
429 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
430 | # General
431 | .DS_Store
432 | .AppleDouble
433 | .LSOverride
434 |
435 | # Icon must end with two \r
436 | Icon
437 |
438 |
439 | # Thumbnails
440 | ._*
441 |
442 | # Files that might appear in the root of a volume
443 | .DocumentRevisions-V100
444 | .fseventsd
445 | .Spotlight-V100
446 | .TemporaryItems
447 | .Trashes
448 | .VolumeIcon.icns
449 | .com.apple.timemachine.donotpresent
450 |
451 | # Directories potentially created on remote AFP share
452 | .AppleDB
453 | .AppleDesktop
454 | Network Trash Folder
455 | Temporary Items
456 | .apdisk
457 |
458 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
459 | # Windows thumbnail cache files
460 | Thumbs.db
461 | ehthumbs.db
462 | ehthumbs_vista.db
463 |
464 | # Dump file
465 | *.stackdump
466 |
467 | # Folder config file
468 | [Dd]esktop.ini
469 |
470 | # Recycle Bin used on file shares
471 | $RECYCLE.BIN/
472 |
473 | # Windows Installer files
474 | *.cab
475 | *.msi
476 | *.msix
477 | *.msm
478 | *.msp
479 |
480 | # Windows shortcuts
481 | *.lnk
482 |
483 | # Vim temporary swap files
484 | *.swp
485 |
--------------------------------------------------------------------------------
/Blazor.Confetti.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31903.59
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KristofferStrube.Blazor.Confetti", "src\KristofferStrube.Blazor.Confetti\KristofferStrube.Blazor.Confetti.csproj", "{1E7A2A39-2B64-48D5-BE54-D36F3C3DAE8A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KristofferStrube.Blazor.Confetti.WebAppSample", "samples\KristofferStrube.Blazor.Confetti.WebAppSample\KristofferStrube.Blazor.Confetti.WebAppSample.csproj", "{C3641E65-04A0-45F7-9A19-69F42FB0AA79}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KristofferStrube.Blazor.Confetti.WasmSample", "samples\KristofferStrube.Blazor.Confetti.WasmSample\KristofferStrube.Blazor.Confetti.WasmSample.csproj", "{58B99DE9-BD3A-454D-AF36-3675FBBE9FF1}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {1E7A2A39-2B64-48D5-BE54-D36F3C3DAE8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {1E7A2A39-2B64-48D5-BE54-D36F3C3DAE8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {1E7A2A39-2B64-48D5-BE54-D36F3C3DAE8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {1E7A2A39-2B64-48D5-BE54-D36F3C3DAE8A}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {C3641E65-04A0-45F7-9A19-69F42FB0AA79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {C3641E65-04A0-45F7-9A19-69F42FB0AA79}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {C3641E65-04A0-45F7-9A19-69F42FB0AA79}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {C3641E65-04A0-45F7-9A19-69F42FB0AA79}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {58B99DE9-BD3A-454D-AF36-3675FBBE9FF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {58B99DE9-BD3A-454D-AF36-3675FBBE9FF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {58B99DE9-BD3A-454D-AF36-3675FBBE9FF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {58B99DE9-BD3A-454D-AF36-3675FBBE9FF1}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ## [0.2.1] - 2024-04-07
10 | ### Fixed
11 | - Fixed that confetti pieces would render for a short time before the animation started.
12 |
13 | ## [0.2.0] - 2024-04-07
14 | ### Added
15 | - Added that `Size`, `SizeVariation` options to `ConfettiOptions` for configuring the size of confetti pieces.
16 | - Added `Type` option to `ConfettiOptions` to configure what the confetti looks like. This currently includes the options `SkewedRectangle`, `Heart`, `Star` and `HandsClapping`.
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 | Contributing includes many different actions. It is not only writing code. Contributions like discussing solutions in open issues are easily as valuable as contributing code as we can then find the best solution with the perspective of multiple people.
3 |
4 | ## Bugs and feature requests
5 | If you find any bugs in the project or have requests for features, then please [Open a new Issue](https://github.com/KristofferStrube/Blazor.Confetti/issues/new).
6 |
7 | ## Contributing Code and Samples
8 | Before you contribute to the project, you will need to get confirmation from the library author that the contribution is welcome.
9 | This can help align the scope of the contribution so that you and the author agree on the solution and how you ensure the change is maintainable with the existing users in mind.
10 | Once you are ready to start coding try to follow these code guidelines:
11 | - Make a fork of the current `main` branch.
12 | - Follow the existing coding conventions used in the project. This includes tab style, naming conventions, etc.
13 | - If your contribution is a new feature, try to add a demo that demonstrates how this will be used in the sample project.
14 | - Any code or sample you share as a part of the resulting Pull Request should fall under the MIT license agreement.
15 | - You don't need to update the version number of the project as the maintainer will do this when making the next release after the Pull Request has been merged.
16 | - Keep your Pull Request to the point. I.e., if your Pull Request is related to fixing a bug then try not to touch any other files than the ones related to that issue as this will make the chance of the PR being merged without change requests more likely.
17 |
18 | ## Submitting a Pull Request
19 | If you don't know what a pull request is, read this article: https://help.github.com/articles/using-pull-requests. Make sure the repository can be built and that the related sample project still works as intended. It is also a good idea to familiarize yourself with the project workflow and our coding conventions.
20 |
21 | ## Ensuring that your contribution will be accepted
22 | You might also read these two blog posts on contributing code: [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza and [Don't "Push" Your Pull Requests](https://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. These blog posts highlight good open-source collaboration etiquette and help align expectations between you and us.
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Kristoffer Strube
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 | [](/LICENSE)
2 | [](https://github.com/KristofferStrube/Blazor.Confetti/issues)
3 | [](https://github.com/KristofferStrube/Blazor.Confetti/network/members)
4 | [](https://github.com/KristofferStrube/Blazor.Confetti/stargazers)
5 | [](https://www.nuget.org/packages/KristofferStrube.Blazor.Confetti/)
6 |
7 | # Blazor.Confetti
8 | A small service that can make confetti in your Blazor application. Works for both WASM and Server render mode.
9 |
10 | 
11 |
12 | # Demo
13 | The sample project can be demoed at https://kristofferstrube.github.io/Blazor.Confetti/
14 |
15 | On each page, you can find the corresponding code for the example in the top right corner.
16 |
17 | # Getting Started
18 | ## Prerequisites
19 | You need to install .NET 8.0 or newer to use the library.
20 |
21 | [Download .NET 8](https://dotnet.microsoft.com/download/dotnet/8.0)
22 |
23 | ## Installation
24 | You can install the package via NuGet with the Package Manager in your IDE or alternatively using the command line:
25 | ```bash
26 | dotnet add package KristofferStrube.Blazor.Confetti
27 | ```
28 |
29 | # Usage
30 | The package can be used in Blazor WebAssembly, Blazor Server, and Blazor WebApp projects both with interactive WASM and Server render modes.
31 | ## Import
32 | You need to reference the package in order to use it in your pages. This can be done in `_Import.razor` by adding the following.
33 | ```razor
34 | @using KristofferStrube.Blazor.Confetti
35 | ```
36 |
37 | ## Add to service collection
38 | The library has one service which is the `ConfettiService` which can be used to start a confetti animation. An easy way to make the service available on all your pages is by registering it in the `IServiceCollection` so that it can be dependency injected in the pages that need it. This is done in `Program.cs` by using our extension `AddConfettiService()` before you build the host like we do in the following code block.
39 | ```csharp
40 | using KristofferStrube.Blazor.Confetti;
41 |
42 | WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
43 |
44 | // Adding other services
45 |
46 | // Adding our service
47 | builder.Services.AddConfettiService();
48 |
49 | WebApplication app = builder.Build();
50 |
51 | // Configure middleware
52 |
53 | app.Run();
54 | ```
55 |
56 | ## Renderer
57 | For the confetti to appear somewhere we also need to place a component at the root of our application which will work as the drawing space for the confetti animation. A good place to do this could be in `MainLayout.razor` after all other markup. If you are creating a Blazor WebApp you might need to add the attribute `@rendermode="InteractiveServer"` to the `Confetti` component to make it interactive.
58 | ```razor
59 | @inherits LayoutComponentBase
60 |
61 |
64 |
65 |
66 |
69 |
70 |
71 | @Body
72 |
73 |
74 |
75 |
76 |
77 |
78 | ```
79 |
80 | ## Injecting and activating the confetti service
81 | Now we are ready to inject the `ConfettiService` in one of our pages or components to make some confetti.
82 | ```razor
83 | @inject ConfettiService ConfettiService
84 |
85 | Celebration 🎉
86 |
87 | @code {
88 | private void Activate()
89 | {
90 | ConfettiService.Activate(new());
91 | }
92 | }
93 | ```
94 |
95 | With the above we create confetti from the bottom of the page with all the default settings. We can further customize the confetti colors, amount, origin, speed, and speed variation.
96 | ```razor
97 | @inject ConfettiService ConfettiService
98 |
99 | Celebration 🎉
100 |
101 | @code {
102 | private ElementReference button;
103 |
104 | private void Activate()
105 | {
106 | ConfettiOptions options = new()
107 | {
108 | Colors = ["silver", "gold", "#B87333"],
109 | Pieces = 500,
110 | Mode = ConfettiOriginMode.FromElement,
111 | Origin = button,
112 | Milliseconds = 2000,
113 | VariationInMilliseconds = 500
114 | };
115 |
116 | ConfettiService.Activate(options);
117 | }
118 | }
119 | ```
120 |
121 |
122 | # Related repositories
123 | The library uses the following other packages to support its features:
124 | - https://github.com/KristofferStrube/Blazor.SVGAnimation (To start and stop SVG animations)
125 |
126 | # Related articles
127 | This repository was built with inspiration and help from the following series of articles:
128 |
129 | - [Wrapping JavaScript libraries in Blazor WebAssembly/WASM](https://blog.elmah.io/wrapping-javascript-libraries-in-blazor-webassembly-wasm/)
130 | - [Blazor WASM 404 error and fix for GitHub Pages](https://blog.elmah.io/blazor-wasm-404-error-and-fix-for-github-pages/)
131 | - [How to fix Blazor WASM base path problems](https://blog.elmah.io/how-to-fix-blazor-wasm-base-path-problems/)
132 |
--------------------------------------------------------------------------------
/docs/confetti.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.Confetti/cb2562c8411705a6e5784132e47e5962df788e2d/docs/confetti.gif
--------------------------------------------------------------------------------
/docs/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.Confetti/cb2562c8411705a6e5784132e47e5962df788e2d/docs/icon.png
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 | Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/KristofferStrube.Blazor.Confetti.WasmSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/Layout/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 | @inject NavigationManager NavigationManager
3 |
4 |
5 |
8 |
9 |
10 |
15 |
16 |
17 | @Body
18 |
19 |
20 |
21 |
22 |
23 |
24 | @code {
25 | private string relativeUri => NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
26 |
27 | protected string page => (string.IsNullOrEmpty(relativeUri) ? "Home" : relativeUri) + ".razor";
28 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/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/KristofferStrube.Blazor.Confetti.WasmSample/Layout/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
9 |
10 |
24 |
25 | @code {
26 | private bool collapseNavMenu = true;
27 |
28 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
29 |
30 | private void ToggleNavMenu()
31 | {
32 | collapseNavMenu = !collapseNavMenu;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/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/KristofferStrube.Blazor.Confetti.WasmSample/Pages/Counter.razor:
--------------------------------------------------------------------------------
1 | @page "/counter"
2 |
3 | Counter
4 |
5 | Counter
6 |
7 | Current count: @currentCount
8 |
9 | Confetti from bottom of page
10 |
11 |
12 | Confetti with custom colors and size
13 |
14 |
15 | Slow confetti
16 |
17 |
18 | Heart confetti from top
19 |
20 |
21 | Hands clapping from bottom
22 |
23 |
24 | Stars from top
25 |
26 | Confetti from this button
27 |
28 | @code {
29 | private ElementReference button;
30 |
31 | [Inject]
32 | public required ConfettiService ConfettiService { get; set; }
33 |
34 | private int currentCount = 0;
35 |
36 | private void FromBottom()
37 | {
38 | ConfettiService.Activate(new() { Mode = ConfettiOriginMode.FromBottomCenter });
39 | currentCount++;
40 | }
41 |
42 | private void WithCustomColors()
43 | {
44 | ConfettiService.Activate(new()
45 | {
46 | Mode = ConfettiOriginMode.FromBottomCenter,
47 | Colors = ["silver", "gold", "#B87333"],
48 | Size = 10,
49 | SizeVariation = 5
50 | });
51 | currentCount++;
52 | }
53 |
54 | private void Slowly()
55 | {
56 | ConfettiService.Activate(new() { Mode = ConfettiOriginMode.FromBottomCenter, Milliseconds = 3000, VariationInMilliseconds = 500 });
57 | currentCount++;
58 | }
59 |
60 | private void Heart()
61 | {
62 | ConfettiService.Activate(new()
63 | {
64 | Type = ConfettiType.Heart,
65 | Size = 30,
66 | SizeVariation = 30,
67 | Mode = ConfettiOriginMode.FromTopCurtain,
68 | Milliseconds = 3000,
69 | VariationInMilliseconds = 500,
70 | Pieces = 50
71 | });
72 | currentCount++;
73 | }
74 |
75 | private void Clapping()
76 | {
77 | ConfettiService.Activate(new()
78 | {
79 | Type = ConfettiType.HandsClapping,
80 | Size = 200,
81 | SizeVariation = 0,
82 | Mode = ConfettiOriginMode.FromBottomCenter,
83 | Milliseconds = 2000,
84 | VariationInMilliseconds = 0,
85 | Pieces = 5
86 | });
87 | currentCount++;
88 | }
89 |
90 | private void Stars()
91 | {
92 | ConfettiService.Activate(new()
93 | {
94 | Type = ConfettiType.Star,
95 | Size = 30,
96 | SizeVariation = 30,
97 | Mode = ConfettiOriginMode.FromTopCurtain,
98 | Milliseconds = 2000,
99 | VariationInMilliseconds = 500,
100 | Pieces = 100
101 | });
102 | currentCount++;
103 | }
104 |
105 | private void FromButton()
106 | {
107 | ConfettiService.Activate(new() { Mode = ConfettiOriginMode.FromElement, Origin = button });
108 | currentCount++;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/Pages/Home.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 | Home
4 |
5 | Hello, world!
6 |
7 | Welcome to this demo app. Try out the new counter demo with some more power.
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/Program.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.Confetti;
2 | using KristofferStrube.Blazor.Confetti.WasmSample;
3 | using Microsoft.AspNetCore.Components.Web;
4 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
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 |
12 | builder.Services.AddConfettiService();
13 |
14 | await builder.Build().RunAsync();
15 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:30084",
8 | "sslPort": 44386
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
17 | "applicationUrl": "http://localhost:5269",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
27 | "applicationUrl": "https://localhost:7081;http://localhost:5269",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/_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 KristofferStrube.Blazor.Confetti.WasmSample
10 | @using KristofferStrube.Blazor.Confetti.WasmSample.Layout
11 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Blazor Confetti
6 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/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 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.Confetti/cb2562c8411705a6e5784132e47e5962df788e2d/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/favicon.png
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.Confetti/cb2562c8411705a6e5784132e47e5962df788e2d/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/icon-192.png
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Blazor Confetti
8 |
9 |
10 |
32 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | An unhandled error has occurred.
60 |
Reload
61 |
🗙
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WasmSample/wwwroot/sample-data/weather.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "date": "2022-01-06",
4 | "temperatureC": 1,
5 | "summary": "Freezing"
6 | },
7 | {
8 | "date": "2022-01-07",
9 | "temperatureC": 14,
10 | "summary": "Bracing"
11 | },
12 | {
13 | "date": "2022-01-08",
14 | "temperatureC": -13,
15 | "summary": "Freezing"
16 | },
17 | {
18 | "date": "2022-01-09",
19 | "temperatureC": -16,
20 | "summary": "Balmy"
21 | },
22 | {
23 | "date": "2022-01-10",
24 | "temperatureC": -2,
25 | "summary": "Chilly"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/Components/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/Components/Layout/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 | @inject NavigationManager NavigationManager
3 |
4 |
5 |
8 |
9 |
10 |
15 |
16 |
17 | @Body
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | An unhandled error has occurred.
26 |
Reload
27 |
🗙
28 |
29 |
30 | @code {
31 | private string relativeUri => NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
32 |
33 | protected string page => (string.IsNullOrEmpty(relativeUri) ? "Home" : relativeUri) + ".razor";
34 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/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/KristofferStrube.Blazor.Confetti.WebAppSample/Components/Layout/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
24 |
25 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/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/KristofferStrube.Blazor.Confetti.WebAppSample/Components/Pages/Counter.razor:
--------------------------------------------------------------------------------
1 | @page "/counter"
2 | @rendermode InteractiveServer
3 |
4 | Counter
5 |
6 | Counter
7 |
8 | Current count: @currentCount
9 |
10 | Confetti from bottom of page
11 |
12 |
13 | Confetti with custom colors and size
14 |
15 |
16 | Slow confetti
17 |
18 |
19 | Heart confetti from top
20 |
21 |
22 | Hands clapping from bottom
23 |
24 |
25 | Stars from top
26 |
27 | Confetti from this button
28 |
29 | @code {
30 | private ElementReference button;
31 |
32 | [Inject]
33 | public required ConfettiService ConfettiService { get; set; }
34 |
35 | private int currentCount = 0;
36 |
37 | private void FromBottom()
38 | {
39 | ConfettiService.Activate(new() { Mode = ConfettiOriginMode.FromBottomCenter });
40 | currentCount++;
41 | }
42 |
43 | private void WithCustomColors()
44 | {
45 | ConfettiService.Activate(new()
46 | {
47 | Mode = ConfettiOriginMode.FromBottomCenter,
48 | Colors = ["silver", "gold", "#B87333"],
49 | Size = 10,
50 | SizeVariation = 5
51 | });
52 | currentCount++;
53 | }
54 |
55 | private void Slowly()
56 | {
57 | ConfettiService.Activate(new() { Mode = ConfettiOriginMode.FromBottomCenter, Milliseconds = 3000, VariationInMilliseconds = 500 });
58 | currentCount++;
59 | }
60 |
61 | private void Heart()
62 | {
63 | ConfettiService.Activate(new()
64 | {
65 | Type = ConfettiType.Heart,
66 | Size = 30,
67 | SizeVariation = 30,
68 | Mode = ConfettiOriginMode.FromTopCurtain,
69 | Milliseconds = 3000,
70 | VariationInMilliseconds = 500,
71 | Pieces = 50
72 | });
73 | currentCount++;
74 | }
75 |
76 | private void Clapping()
77 | {
78 | ConfettiService.Activate(new()
79 | {
80 | Type = ConfettiType.HandsClapping,
81 | Size = 200,
82 | SizeVariation = 0,
83 | Mode = ConfettiOriginMode.FromBottomCenter,
84 | Milliseconds = 2000,
85 | VariationInMilliseconds = 0,
86 | Pieces = 5
87 | });
88 | currentCount++;
89 | }
90 |
91 | private void Stars()
92 | {
93 | ConfettiService.Activate(new()
94 | {
95 | Type = ConfettiType.Star,
96 | Size = 30,
97 | SizeVariation = 30,
98 | Mode = ConfettiOriginMode.FromTopCurtain,
99 | Milliseconds = 2000,
100 | VariationInMilliseconds = 500,
101 | Pieces = 100
102 | });
103 | currentCount++;
104 | }
105 |
106 | private void FromButton()
107 | {
108 | ConfettiService.Activate(new() { Mode = ConfettiOriginMode.FromElement, Origin = button });
109 | currentCount++;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/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]
29 | private HttpContext? HttpContext { get; set; }
30 |
31 | private string? RequestId { get; set; }
32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
33 |
34 | protected override void OnInitialized() =>
35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
36 | }
37 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/Components/Pages/Home.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 | Home
4 |
5 | Hello, world!
6 |
7 | Welcome to this demo app. Try out the new counter demo with some more power.
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/Components/Routes.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/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 KristofferStrube.Blazor.Confetti.WebAppSample
10 | @using KristofferStrube.Blazor.Confetti.WebAppSample.Components
11 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/KristofferStrube.Blazor.Confetti.WebAppSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/Program.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.Confetti;
2 | using KristofferStrube.Blazor.Confetti.WebAppSample.Components;
3 |
4 | WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
5 |
6 | // Add services to the container.
7 | builder.Services
8 | .AddRazorComponents()
9 | .AddInteractiveServerComponents();
10 |
11 | builder.Services.AddConfettiService();
12 |
13 | WebApplication app = builder.Build();
14 |
15 | // Configure the HTTP request pipeline.
16 | if (!app.Environment.IsDevelopment())
17 | {
18 | app.UseExceptionHandler("/Error", createScopeForErrors: true);
19 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
20 | app.UseHsts();
21 | }
22 |
23 | app.UseHttpsRedirection();
24 |
25 | app.UseStaticFiles();
26 | app.UseAntiforgery();
27 |
28 | app.MapRazorComponents()
29 | .AddInteractiveServerRenderMode();
30 |
31 | app.Run();
32 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:23347",
8 | "sslPort": 44377
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "applicationUrl": "http://localhost:5258",
17 | "environmentVariables": {
18 | "ASPNETCORE_ENVIRONMENT": "Development"
19 | }
20 | },
21 | "https": {
22 | "commandName": "Project",
23 | "dotnetRunMessages": true,
24 | "launchBrowser": true,
25 | "applicationUrl": "https://localhost:7212;http://localhost:5258",
26 | "environmentVariables": {
27 | "ASPNETCORE_ENVIRONMENT": "Development"
28 | }
29 | },
30 | "IIS Express": {
31 | "commandName": "IISExpress",
32 | "launchBrowser": true,
33 | "environmentVariables": {
34 | "ASPNETCORE_ENVIRONMENT": "Development"
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/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 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.Confetti.WebAppSample/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.Confetti/cb2562c8411705a6e5784132e47e5962df788e2d/samples/KristofferStrube.Blazor.Confetti.WebAppSample/wwwroot/favicon.png
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/Confetti.razor:
--------------------------------------------------------------------------------
1 | @using KristofferStrube.Blazor.DOM.Extensions
2 | @using Microsoft.JSInterop
3 | @implements IDisposable
4 |
5 |
6 | @foreach (var confettiPiece in confettiPieces)
7 | {
8 |
9 | }
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 | @code {
327 | private double width;
328 | private double height;
329 | private bool playing = false;
330 |
331 | [Inject]
332 | public required IJSRuntime JSRuntime { get; set; }
333 |
334 | [Inject]
335 | public required ConfettiService ConfettiService { get; set; }
336 |
337 | List confettiPieces = new();
338 |
339 | protected override async Task OnAfterRenderAsync(bool firstRender)
340 | {
341 | if (!firstRender) return;
342 |
343 | ConfettiService.Activated += HandleActivated;
344 |
345 | IJSObjectReference windowReference = await JSRuntime.InvokeAsync("window.valueOf");
346 |
347 | var helper = await JSRuntime.GetHelperAsync();
348 | width = await helper.InvokeAsync("getAttribute", windowReference, "innerWidth");
349 | height = await helper.InvokeAsync("getAttribute", windowReference, "innerHeight");
350 | }
351 |
352 | public record ClientRect(double x, double y, double width, double height);
353 |
354 | public async void HandleActivated(object? sender, ConfettiOptions options)
355 | {
356 | (double X, double Y)? originPosition = null;
357 | if (options.Origin is {} origin)
358 | {
359 | var helper = await JSRuntime.GetHelperAsync();
360 | var jSInstance = await helper.InvokeAsync("getJSReference", origin);
361 | var client = await jSInstance.InvokeAsync("getBoundingClientRect");
362 | originPosition = (X: client.x + client.width / 2, Y: client.y + client.height / 2);
363 | }
364 |
365 | var newPieces = Enumerable
366 | .Range(0, options.Pieces)
367 | .Select(i =>
368 | new ConfettiPiece(
369 | options.Type,
370 | options.Colors[Random.Shared.Next(options.Colors.Length)],
371 | options.Size + (int)((Random.Shared.NextDouble() * 2 - 1) * options.SizeVariation),
372 | options.Milliseconds + (int)((Random.Shared.NextDouble() * 2 - 1) * options.VariationInMilliseconds),
373 | options.Mode switch
374 | {
375 | ConfettiOriginMode.FromBottomCenter => FromBottomCenterPositions(),
376 | ConfettiOriginMode.FromTopCurtain => FromTopCurtainPositions(),
377 | ConfettiOriginMode.FromElement when originPosition is not null => FromElementPositions(originPosition.Value),
378 | _ => FromBottomCenterPositions()
379 | }
380 | )
381 | ).ToList();
382 |
383 | lock (confettiPieces)
384 | {
385 | confettiPieces.AddRange(newPieces);
386 | }
387 |
388 | StateHasChanged();
389 | await Task.Delay(options.Milliseconds + options.VariationInMilliseconds + 100);
390 | lock (confettiPieces)
391 | {
392 | confettiPieces = confettiPieces.Where(p => !newPieces.Contains(p)).ToList();
393 | }
394 | StateHasChanged();
395 |
396 |
397 | }
398 |
399 | private (double X, double Y)[] FromBottomCenterPositions()
400 | {
401 | var x = Random.Shared.NextDouble() - 0.5;
402 | var y = Random.Shared.NextDouble();
403 | var end = Random.Shared.NextDouble();
404 | return [(width / 2, height), (width / 2 + x * width / 3 * 2, height / 2 - y * height / 2), (width / 2 + x * width / 3 * 4, height + end * height / 3)];
405 | }
406 |
407 | private (double X, double Y)[] FromTopCurtainPositions()
408 | {
409 | var x = Random.Shared.NextDouble() * width;
410 | return [(x, 0), (x, height)];
411 | }
412 |
413 | private (double X, double Y)[] FromElementPositions((double x, double y) originPosition)
414 | {
415 | var x = Random.Shared.NextDouble() - 0.5;
416 | var y = Random.Shared.NextDouble();
417 | var end = Random.Shared.NextDouble();
418 | return [originPosition, (originPosition.x + x * width / 3 * 2, height / 2 - y * height / 2), (originPosition.x + x * width / 3 * 4, height + end * height / 3)];
419 | }
420 |
421 | public void Dispose()
422 | {
423 | ConfettiService.Activated -= HandleActivated;
424 | }
425 | }
426 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/ConfettiAnimator.razor:
--------------------------------------------------------------------------------
1 | @using KristofferStrube.Blazor.SVGAnimation
2 | @using Microsoft.JSInterop
3 |
4 |
5 | @if (ConfettiPiece.Type is ConfettiType.SkewedRectangle)
6 | {
7 |
8 | }
9 | else if (ConfettiPiece.Type is ConfettiType.Heart)
10 | {
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | }
25 | else if (ConfettiPiece.Type is ConfettiType.HandsClapping)
26 | {
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | }
80 | else if (ConfettiPiece.Type is ConfettiType.Star)
81 | {
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | }
93 |
99 |
100 |
101 | @code {
102 | private ElementReference animateReference { get; set; }
103 | private SVGAnimationElement? animationElement;
104 | private bool visible = false;
105 |
106 | [Inject]
107 | public required IJSRuntime JSRuntime { get; set; }
108 |
109 | [Parameter, EditorRequired]
110 | public required ConfettiPiece ConfettiPiece { get; set; }
111 |
112 | protected override async Task OnAfterRenderAsync(bool firstRender)
113 | {
114 | if (!firstRender) return;
115 |
116 | try
117 | {
118 | animationElement = await SVGAnimationElement.CreateAsync(JSRuntime, animateReference);
119 |
120 | await animationElement.BeginElementAsync();
121 | await animationElement.EndElementAtAsync(ConfettiPiece.Milliseconds);
122 | visible = true;
123 | StateHasChanged();
124 | }
125 | catch (Exception e)
126 | {
127 | #if DEBUG
128 | Console.WriteLine(e);
129 | #endif
130 | }
131 | }
132 |
133 | private string SmoothPath((double X, double Y)[] points)
134 | {
135 | // Parameter for smoothness of path in interval [0, 0.5]
136 | double smoothness = 1.0 / 3.0;
137 |
138 | var result = "";
139 | if (points.Length >= 2)
140 | {
141 | result = $"M {points[0].X.AsString()} {points[0].Y.AsString()} ";
142 | for (int i = 1; i < points.Length - 1; i++)
143 | {
144 | result += $"S {(points[i - 1].X * smoothness / 2 + points[i].X - points[i + 1].X * smoothness / 2).AsString()} {(points[i - 1].Y * smoothness / 2 + points[i].Y - points[i + 1].Y * smoothness / 2).AsString()} {points[i].X.AsString()} {points[i].Y.AsString()} ";
145 | }
146 | result += $"S {(points[^2].X * smoothness + points[^1].X * (1 - smoothness)).AsString()} {(points[^2].Y * smoothness + points[^1].Y * (1 - smoothness)).AsString()} {points[^1].X.AsString()} {points[^1].Y.AsString()} ";
147 | }
148 | return result;
149 | }
150 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/ConfettiOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 |
3 | namespace KristofferStrube.Blazor.Confetti;
4 |
5 | public class ConfettiOptions : EventArgs
6 | {
7 | ///
8 | /// The number of pieces of confetti to generate. The default is 300 .
9 | ///
10 | public int Pieces { get; set; } = 300;
11 |
12 | ///
13 | /// The width of each confetti piece. The default is 10 .
14 | ///
15 | public int Size { get; set; } = 10;
16 |
17 | ///
18 | /// The amount that the size can vary. The default is 10 .
19 | ///
20 | public int SizeVariation { get; set; } = 1;
21 |
22 | ///
23 | /// The type of confetti there will be used.
24 | ///
25 | public ConfettiType Type { get; set; } = ConfettiType.SkewedRectangle;
26 |
27 | ///
28 | /// The time that the average confetti piece will be animated for. The default is 1000 milliseconds.
29 | ///
30 | public int Milliseconds { get; set; } = 1000;
31 |
32 | ///
33 | /// How much the animation time of each confetti piece can vary.
34 | ///
35 | public int VariationInMilliseconds { get; set; } = 200;
36 |
37 | ///
38 | /// The color of the confetti pieces. The default colors are "#F4F" , "#44F" , "#4F4" , "#F44" , and "#9F0" .
39 | ///
40 | public string[] Colors { get; set; } = ["#F4F", "#44F", "#4F4", "#F44", "#9F0"];
41 |
42 | ///
43 | /// How the confetti should be rendered. The default is
44 | ///
45 | public ConfettiOriginMode Mode { get; set; } = ConfettiOriginMode.FromBottomCenter;
46 |
47 | ///
48 | /// From where the animation should start if the value is chosen for the .
49 | ///
50 | public ElementReference? Origin { get; set; }
51 | }
52 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/ConfettiOriginMode.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.Confetti;
2 |
3 | public enum ConfettiOriginMode
4 | {
5 | FromBottomCenter,
6 | FromTopCurtain,
7 | FromElement,
8 | }
9 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/ConfettiPiece.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.Confetti;
2 |
3 | public record struct ConfettiPiece(ConfettiType Type, string Color, int Size, int Milliseconds, (double X, double Y)[] Positions);
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/ConfettiService.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.Confetti;
2 |
3 | public class ConfettiService
4 | {
5 | public event ActivatedEventHandler? Activated;
6 |
7 | public void Activate(ConfettiOptions options)
8 | {
9 | Activated?.Invoke(this, options);
10 | }
11 |
12 | public delegate void ActivatedEventHandler(object sender, ConfettiOptions e);
13 | }
14 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/ConfettiType.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.Confetti;
2 |
3 | public enum ConfettiType
4 | {
5 | SkewedRectangle,
6 | Heart,
7 | Star,
8 | HandsClapping
9 | }
10 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/DoubleExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace KristofferStrube.Blazor.Confetti;
4 |
5 | internal static class DoubleExtensions
6 | {
7 | internal static string AsString(this double d)
8 | {
9 | return d.ToString(CultureInfo.InvariantCulture);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/IServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace KristofferStrube.Blazor.Confetti;
4 |
5 | public static class IServiceExtensions
6 | {
7 | public static IServiceCollection AddConfettiService(this IServiceCollection serviceCollection)
8 | {
9 | return serviceCollection.AddScoped();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.Confetti/KristofferStrube.Blazor.Confetti.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | Blazor Confetti
8 | Confetti component for Blazor.
9 | KristofferStrube.Blazor.Confetti
10 | Blazor;Confetti;SVG;Animation;SVGAnimation;JSInterop
11 | https://github.com/KristofferStrube/Blazor.Confetti
12 | git
13 | MIT
14 | 0.2.1
15 | Kristoffer Strube
16 | README.md
17 | icon.png
18 | true
19 |
20 |
21 |
22 |
23 | True
24 | \
25 |
26 |
27 | True
28 | \
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------