From 97a92096c77f0b2a978ac9ac47af7fc4770c5c5b Mon Sep 17 00:00:00 2001 From: Karoline Date: Mon, 16 Mar 2026 19:29:34 +0100 Subject: [PATCH] feat: add confetti --- package-lock.json | 18 +++++++++++++ package.json | 2 ++ src/app/_components/UI/Confetti.tsx | 39 +++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 src/app/_components/UI/Confetti.tsx diff --git a/package-lock.json b/package-lock.json index d44febb8a..de80c32d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "@babel/preset-typescript": "^7.28.5", "@jest/globals": "^29.7.0", "@types/bcrypt": "^5.0.2", + "@types/canvas-confetti": "^1.9.0", "@types/jest": "^29.5.14", "@types/jsonwebtoken": "^9.0.10", "@types/luxon": "^3.7.1", @@ -3679,6 +3680,13 @@ "@types/node": "*" } }, + "node_modules/@types/canvas-confetti": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz", + "integrity": "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/codemirror": { "version": "5.60.15", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", @@ -4976,6 +4984,16 @@ "node": ">=6" } }, + "node_modules/canvas-confetti": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz", + "integrity": "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==", + "license": "ISC", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", diff --git a/package.json b/package.json index 9671d750d..f16e39bea 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@react-email/render": "^1.4.0", "bcrypt": "^5.1.1", "eslint-plugin-react-hooks": "^6.1.1", + "canvas-confetti": "^1.9.4", "html5-qrcode": "^2.3.8", "jsonwebtoken": "^9.0.3", "jsqr": "^1.4.0", @@ -65,6 +66,7 @@ "@babel/preset-typescript": "^7.28.5", "@jest/globals": "^29.7.0", "@types/bcrypt": "^5.0.2", + "@types/canvas-confetti": "^1.9.0", "@types/jest": "^29.5.14", "@types/jsonwebtoken": "^9.0.10", "@types/luxon": "^3.7.1", diff --git a/src/app/_components/UI/Confetti.tsx b/src/app/_components/UI/Confetti.tsx new file mode 100644 index 000000000..b7cbe5881 --- /dev/null +++ b/src/app/_components/UI/Confetti.tsx @@ -0,0 +1,39 @@ +'use client' +import canvasConfetti from 'canvas-confetti' + +type ConfettiArguments = { + angle?: number, + spread?: number, + duration?: number + colors?: string[], + particleCount?: number, + origin?: { x: number, y: number }, +} + +/** + * Summons confetti :) + */ +export function confetti({ + angle = 90, + spread = 150, + duration = 1000, + colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff',], + particleCount = 10, + origin = { x: 0.5, y: 1 } +}: ConfettiArguments = {}) { + const end = Date.now() + duration; + + (function frame() { + canvasConfetti({ + particleCount, + angle, + spread, + origin, + colors, + }) + + if (Date.now() < end) { + requestAnimationFrame(frame) + } + }()) +}