diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 51d0758..2de5a6f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,20 +17,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- - uses: pnpm/action-setup@v2.2.4
+ - uses: pnpm/action-setup@v4
with:
- version: 8
+ version: 9
- name: Setup Node.js
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
- node-version: 18.x
cache: pnpm
- - run: pnpm install --frozen-lockfile
+ - run: pnpm install
- name: Compile
run: pnpm -r compile
@@ -44,9 +43,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
-
- # TODO alert discord
- # - name: Send a Slack notification if a publish happens
- # if: steps.changesets.outputs.published == 'true'
- # # You can do something when a publish happens.
- # run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!"
diff --git a/README.md b/README.md
index 9a0e777..171fe3c 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,16 @@ Confetti explosion across all frameworks 🎉🎉
This is a monorepo containing the following packages:
- [@neoconfetti/svelte](https://github.com/PuruVJ/neoconfetti/tree/main/packages/svelte#readme)
+- [@neoconfetti/react](https://github.com/PuruVJ/neoconfetti/tree/main/packages/react#readme)
+- [@neoconfetti/vue](https://github.com/PuruVJ/neoconfetti/tree/main/packages/vue#readme)
+- [@neoconfetti/vanilla](https://github.com/PuruVJ/neoconfetti/tree/main/packages/vanilla#readme)
+- [@neoconfetti/solid](https://github.com/PuruVJ/neoconfetti/tree/main/packages/solid#readme)
## Roadmap
- [x] @neoconfetti/svelte
-- [ ] @neoconfetti/react
-- [ ] @neoconfetti/vue
-- [ ] @neoconfetti/solid
+- [x] @neoconfetti/react
+- [x] @neoconfetti/vue
+- [x] @neoconfetti/vanilla
+- [x] @neoconfetti/solid
- [ ] @neoconfetti/lit
-- [ ] @neoconfetti/vanilla
diff --git a/package.json b/package.json
index c75a2c8..5024d88 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
"scripts": {
"changeset": "changeset",
"ci:version": "changeset version",
- "ci:release": "changeset publish"
+ "ci:release": "changeset publish",
+ "sizes": "tsx scripts/sizes.ts"
},
"repository": {
"type": "git",
@@ -22,15 +23,22 @@
"devDependencies": {
"@rollup/pluginutils": "^5.1.0",
"@types/node": "^20.10.5",
+ "@types/react": "^18.2.45",
+ "@types/react-dom": "^18.2.18",
"brotli-size": "^4.0.0",
"brotli-size-cli": "^1.0.0",
- "bun-types": "^1.0.18",
- "esbuild": "^0.19.9",
+ "esbuild": "^0.19.10",
+ "fast-glob": "^3.3.2",
"lightningcss": "^1.22.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "solid-js": "^1.8.7",
"svelte": "^4.2.8",
"terser": "^5.26.0",
"tsup": "^8.0.1",
- "typescript": "^5.3.3"
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.3",
+ "vue": "^3.3.13"
},
"dependencies": {
"@changesets/cli": "^2.27.1"
diff --git a/packages/config/index.ts b/packages/config/index.ts
index 0cd44e6..014eac6 100644
--- a/packages/config/index.ts
+++ b/packages/config/index.ts
@@ -3,9 +3,13 @@ import { Plugin } from 'esbuild';
import { transform } from 'lightningcss';
import fs from 'node:fs';
import { resolve } from 'node:path';
-import { defineConfig } from 'tsup';
+import { defineConfig, type Options } from 'tsup';
-export const coreConfig = ({ dtsBanner }: { dtsBanner: string } = { dtsBanner: '' }) =>
+export const coreConfig = ({
+ jsBanner = '',
+ dtsBanner = '',
+ treeshake = 'smallest',
+}: { jsBanner?: string; dtsBanner?: string; treeshake?: Options['treeshake'] } = {}) =>
defineConfig([
{
entry: ['./src/index.ts'],
@@ -14,8 +18,9 @@ export const coreConfig = ({ dtsBanner }: { dtsBanner: string } = { dtsBanner: '
target: 'es2022',
dts: { resolve: true, banner: dtsBanner },
clean: true,
- treeshake: 'smallest',
+ treeshake,
esbuildPlugins: [processCSS()],
+ banner: { js: jsBanner },
},
{
entry: ['./src/index.ts'],
@@ -25,7 +30,7 @@ export const coreConfig = ({ dtsBanner }: { dtsBanner: string } = { dtsBanner: '
target: 'es2022',
clean: true,
outDir: 'dist/min',
- treeshake: 'smallest',
+ treeshake,
esbuildPlugins: [processCSS()],
},
]);
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index f9faab1..1e142ae 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,23 @@
# @neoconfetti/core
+## 2.2.1
+
+### Patch Changes
+
+- 51f551a: Fix particleShape rectangles
+
+## 2.2.0
+
+### Minor Changes
+
+- 4eb52f1: Add particleClass property, optimize code
+
+## 2.1.0
+
+### Minor Changes
+
+- 01a3b51: Fixes, setting any option to undefined reverts it to default value, more fine grained reactivity. If the particlesCount doesn't change, confetti isn't recreated.
+
## 2.0.0
### Major Changes
diff --git a/packages/core/demo/package.json b/packages/core/demo/package.json
index 1bcdd8a..718b42f 100644
--- a/packages/core/demo/package.json
+++ b/packages/core/demo/package.json
@@ -13,7 +13,7 @@
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@tsconfig/svelte": "^5.0.2",
"svelte": "^4.2.8",
- "svelte-preprocess": "^5.1.2",
+ "svelte-preprocess": "^5.1.3",
"tslib": "^2.4.0",
"typescript": "^5.3.3",
"vite": "^5.0.10"
diff --git a/packages/core/demo/src/lib/Counter.svelte b/packages/core/demo/src/lib/Counter.svelte
index 82b1259..0dedb06 100644
--- a/packages/core/demo/src/lib/Counter.svelte
+++ b/packages/core/demo/src/lib/Counter.svelte
@@ -1,8 +1,12 @@
+
+
{#if showConfetti}
-
+
{/if}
diff --git a/packages/core/package.json b/packages/core/package.json
index 821cbfe..60ec111 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,7 +1,7 @@
{
"name": "@neoconfetti/core",
"private": true,
- "version": "2.0.0",
+ "version": "2.2.1",
"main": "./dist/index.js",
"module": "./dist/index.js",
"type": "module",
@@ -26,7 +26,7 @@
"compile": "tsup",
"size": "brotli-size dist/min/index.js"
},
- "description": "Let's party 🎊🎊 with Svelte! @neodrag/core allows you to show an awesome confetti explosion on your page, with Svelte!",
+ "description": "Let's party 🎊🎊 with Svelte! @neoconfetti/core allows you to show an awesome confetti explosion on your page, with Svelte!",
"keywords": [],
"author": "Puru Vijay",
"license": "MIT"
diff --git a/packages/core/src/global.d.ts b/packages/core/src/global.d.ts
index 607a974..ae2050c 100644
--- a/packages/core/src/global.d.ts
+++ b/packages/core/src/global.d.ts
@@ -1,7 +1,7 @@
declare module '*.module.css?map' {
const container: string;
const particle: string;
- export { container, particle };
+ export { container as c, particle as p };
}
declare module '*?inline' {
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index d64525e..da76130 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,5 +1,5 @@
import styles from './style.module.css?inline';
-import { container as cContainer, particle as cParticle } from './style.module.css?map';
+import { c as c_container, p as c_particle } from './style.module.css?map';
type ParticleShape = 'mix' | 'circles' | 'rectangles';
type Particle = {
@@ -32,6 +32,13 @@ type ConfettiOptions = {
*/
particleSize?: number;
+ /**
+ * Class to apply to each confetti particle
+ *
+ * @default undefined
+ */
+ particleClass?: string;
+
/**
* Duration of the animation in milliseconds
*
@@ -77,187 +84,240 @@ type ConfettiOptions = {
destroyAfterDone?: boolean;
};
-export function confetti(node: HTMLElement, options: ConfettiOptions = {}) {
+// Take DEFAULT.COLORS, etc anf convert to DEFAULT_COLORS, etc
+const DEFAULT_COLORS = ['#FFC700', '#FF0000', '#2E3191', '#41BBC7'];
+export const DEFAULT_DURATION = 3500;
+const DEFAULT_FORCE = 0.5;
+const DEFAULT_PARTICLE_COUNT = 150;
+const DEFAULT_PARTICLE_SHAPE = 'mix' as ParticleShape;
+const DEFAULT_PARTICLE_SIZE = 12;
+const DEFAULT_PARTICLE_CLASS = '';
+const DEFAULT_DESTROY_AFTER_DONE = true;
+const DEFAULT_STAGE_HEIGHT = 800;
+const DEFAULT_STAGE_WIDTH = 1600;
+
+export function confetti(container: HTMLElement, options: ConfettiOptions = {}) {
let {
- colors = ['#FFC700', '#FF0000', '#2E3191', '#41BBC7'],
- duration = 3500,
- force = 0.5,
- particleCount = 150,
- particleShape = 'mix',
- particleSize = 12,
- destroyAfterDone = true,
- stageHeight = 800,
- stageWidth = 1600,
+ colors = DEFAULT_COLORS,
+ duration = DEFAULT_DURATION,
+ force = DEFAULT_FORCE,
+ particleCount = DEFAULT_PARTICLE_COUNT,
+ particleShape = DEFAULT_PARTICLE_SHAPE,
+ particleSize = DEFAULT_PARTICLE_SIZE,
+ particleClass = DEFAULT_PARTICLE_CLASS,
+ destroyAfterDone = DEFAULT_DESTROY_AFTER_DONE,
+ stageHeight = DEFAULT_STAGE_HEIGHT,
+ stageWidth = DEFAULT_STAGE_WIDTH,
} = options;
- appendStyles(styles);
- node.classList.add(cContainer);
+ append_styles(styles);
+ container.classList.add(c_container);
// stage-height
- node.style.setProperty('--sh', stageHeight + 'px');
+ container.style.setProperty('--sh', stageHeight + 'px');
- let particles = createParticles(particleCount, colors);
- let nodes = createParticleNodes(node, particles);
+ let particles: Particle[] = [];
+ let nodes: HTMLElement[] = [];
- function confettiStyles(node: HTMLElement, degree: number) {
+ const calc_rotation_transform = () => math_round(random() * (POSSIBLE_ROTATION_TRANSFORMS - 1));
+ const get_is_circle = (particle_shape: ParticleShape, rotation_transform: number) =>
+ particleShape !== 'rectangles' &&
+ (particle_shape === 'circles' || should_be_circle(rotation_transform));
+
+ function confetti_styles(node: HTMLElement, degree: number) {
// Crazy calculations for generating styles
- const rotationTransform = mathRound(random() * (POSSIBLE_ROTATION_TRANSFORMS - 1));
- const isCircle =
- particleShape !== 'rectangles' &&
- (particleShape === 'circles' || shouldBeCircle(rotationTransform));
+ const rotation_transform = calc_rotation_transform();
+ const is_circle = get_is_circle(particleShape, rotation_transform);
- const setCSSVar = (key: string, val: string | number | string[]) =>
+ const set_css_var = (key: string, val: string | number | string[]) =>
node.style.setProperty(key, val + '');
// Get x landing point for it
- setCSSVar(
+ set_css_var(
// x landing point
'--xlp',
- mapRange(abs(rotate(degree, 90) - 180), 0, 180, -stageWidth / 2, stageWidth / 2) + 'px'
+ map_range(abs(rotate(degree, 90) - 180), 0, 180, -stageWidth / 2, stageWidth / 2) + 'px'
);
- setCSSVar(
+ set_css_var(
// duration chaos
'--dc',
- duration - mathRound(random() * 1e3) + 'ms'
+ duration - math_round(random() * 1e3) + 'ms'
);
// x-axis disturbance, roughly the distance the particle will initially deviate from its target
const x1 =
random() < CRAZY_PARTICLES_FREQUENCY ? round(random() * CRAZY_PARTICLE_CRAZINESS, 2) : 0;
- setCSSVar('--x1', x1);
- setCSSVar('--x2', x1 * -1);
- setCSSVar('--x3', x1);
+ set_css_var('--x1', x1);
+ set_css_var('--x2', x1 * -1);
+ set_css_var('--x3', x1);
// x-axis arc of explosion, so 90deg and 270deg particles have curve of 1, 0deg and 180deg have 0
- setCSSVar('--x4', round(abs(mapRange(abs(rotate(degree, 90) - 180), 0, 180, -1, 1)), 4));
+ set_css_var('--x4', round(abs(map_range(abs(rotate(degree, 90) - 180), 0, 180, -1, 1)), 4));
// roughly how fast particle reaches end of its explosion curve
- setCSSVar('--y1', round(random() * BEZIER_MEDIAN, 4));
+ set_css_var('--y1', round(random() * BEZIER_MEDIAN, 4));
// roughly maps to the distance particle goes before reaching free-fall
- setCSSVar('--y2', round(random() * force * (coinFlip() ? 1 : -1), 4));
+ set_css_var('--y2', round(random() * force * (coin_flip() ? 1 : -1), 4));
// roughly how soon the particle transitions from explosion to free-fall
- setCSSVar('--y3', BEZIER_MEDIAN);
+ set_css_var('--y3', BEZIER_MEDIAN);
// roughly the ease of free-fall
- setCSSVar('--y4', round(max(mapRange(abs(degree - 180), 0, 180, force, -force), 0), 4));
+ set_css_var('--y4', round(max(map_range(abs(degree - 180), 0, 180, force, -force), 0), 4));
// set --width and --height here
- setCSSVar(
+ set_css_var(
// --width
'--w',
- (isCircle ? particleSize : mathRound(random() * 4) + particleSize / 2) + 'px'
+ (is_circle ? particleSize : math_round(random() * 4) + particleSize / 2) + 'px'
);
- setCSSVar(
+ set_css_var(
// --height
'--h',
- (isCircle ? particleSize : mathRound(random() * 2) + particleSize) + 'px'
+ (is_circle ? particleSize : math_round(random() * 2) + particleSize) + 'px'
);
- const rotation = rotationTransform.toString(2).padStart(3, '0').split('');
- setCSSVar(
+ const rotation = rotation_transform.toString(2).padStart(3, '0').split('');
+ set_css_var(
// --half-rotation
'--hr',
rotation.map((n) => +n / 2 + '').join(' ')
);
- setCSSVar(
+ set_css_var(
// --rotation
'--r',
rotation.join(' ')
);
- setCSSVar(
+ set_css_var(
// --rotation-duration
'--rd',
round(random() * (ROTATION_SPEED_MAX - ROTATION_SPEED_MIN) + ROTATION_SPEED_MIN) + 'ms'
);
- setCSSVar(
+ set_css_var(
// --border-radius
'--br',
- isCircle ? '50%' : 0
+ is_circle ? '50%' : 0
);
}
- for (const [i, node] of Object.entries(nodes)) confettiStyles(node, particles[+i].degree);
-
let timer: ReturnType;
- Promise.resolve().then(
- () => (timer = setTimeout(() => destroyAfterDone && (node.innerHTML = ''), duration))
- );
+ function scratch() {
+ container.innerHTML = '';
+ clearTimeout(timer);
- return {
- update(newOptions: ConfettiOptions) {
- const newParticleCount = newOptions.particleCount ?? particleCount;
- const newColors = newOptions.colors ?? colors;
- const newStageHeight = newOptions.stageHeight ?? stageHeight;
+ particles = create_particles(particleCount, colors);
+ nodes = create_particle_nodes(container, particles, particleClass);
- particles = createParticles(newParticleCount, newColors);
+ for (const [i, node] of object_entries(nodes)) confetti_styles(node, particles[+i].degree);
+ timer = setTimeout(() => {
+ if (destroyAfterDone) container.innerHTML = '';
+ }, duration);
+ }
+
+ scratch();
+
+ return {
+ update(new_options: ConfettiOptions) {
+ const new_particle_count = new_options.particleCount ?? DEFAULT_PARTICLE_COUNT;
+ const new_particle_shape = new_options.particleShape ?? DEFAULT_PARTICLE_SHAPE;
+ const new_particle_size = new_options.particleSize ?? DEFAULT_PARTICLE_SIZE;
+ const new_particle_class = new_options.particleClass ?? DEFAULT_PARTICLE_CLASS;
+ const new_colors = new_options.colors ?? DEFAULT_COLORS;
+ const new_stage_height = new_options.stageHeight ?? DEFAULT_STAGE_HEIGHT;
+ const new_duration = new_options.duration ?? DEFAULT_DURATION;
+ const new_force = new_options.force ?? DEFAULT_FORCE;
+ const new_stage_width = new_options.stageWidth ?? DEFAULT_STAGE_WIDTH;
+ const new_destroy_after_done = new_options.destroyAfterDone ?? DEFAULT_DESTROY_AFTER_DONE;
+
+ particles = create_particles(new_particle_count, new_colors);
+
+ let start_from_scratch = false;
// Other might have changed. First, check the diff for colors, as only that matters
- if (
- newParticleCount === particleCount &&
- JSON.stringify(colors) !== JSON.stringify(newColors)
- )
- // In this, case only update the particles' colors, on every DOM element
- for (const [i, { color }] of Object.entries(particles))
- nodes[+i].style.setProperty(
- // bgcolor
- '--bgc',
- color
- );
-
- if (newParticleCount !== particleCount) {
- // Recreate all particles
- // Delete existing ones
- node.innerHTML = '';
-
- // Now create new particles
- nodes = createParticleNodes(node, particles);
+ if (new_particle_count === particleCount) {
+ // Why the hell this works but directly setting the CSS variables on nodes doesn't??
+ nodes = Array.from(container.querySelectorAll(`.${c_particle}`));
+
+ for (const [i, { color }] of object_entries(particles)) {
+ const node = nodes[+i];
+ if (JSON.stringify(colors) !== JSON.stringify(new_colors)) {
+ node.style.setProperty('--bgc', color);
+ }
+
+ if (new_particle_shape !== particleShape) {
+ node.style.setProperty(
+ // --border-radius
+ '--br',
+ get_is_circle(new_particle_shape, calc_rotation_transform()) ? '50%' : '0'
+ );
+ }
+
+ if (new_particle_class !== particleClass) {
+ // Surgically remove the old particleClass from all particles, and replace with new_particle_class
+ if (particleClass) node.classList.remove(particleClass);
+ if (new_particle_class) node.classList.add(new_particle_class);
+ }
+ }
+ } else {
+ start_from_scratch = true;
}
// Dont destroy component if destroyAfterDone is false now
- if (destroyAfterDone && !newOptions.destroyAfterDone) clearTimeout(timer);
+ if (destroyAfterDone && !new_destroy_after_done) {
+ clearTimeout(timer);
+ }
// Update stageHeight
- node.style.setProperty('--sh', newStageHeight + 'px');
-
- colors = newColors;
- duration = newOptions.duration ?? duration;
- force = newOptions.force ?? force;
- particleCount = newParticleCount;
- particleShape = newOptions.particleShape ?? particleShape;
- particleSize = newOptions.particleSize ?? particleSize;
- destroyAfterDone = newOptions.destroyAfterDone ?? destroyAfterDone;
- stageHeight = newStageHeight;
- stageWidth = newOptions.stageWidth ?? stageWidth;
+ container.style.setProperty('--sh', new_stage_height + 'px');
+
+ duration = new_duration;
+ colors = new_colors;
+ force = new_force;
+ particleCount = new_particle_count;
+ particleShape = new_particle_shape;
+ particleSize = new_particle_size;
+ particleClass = new_particle_class;
+ destroyAfterDone = new_destroy_after_done;
+ stageHeight = new_stage_height;
+ stageWidth = new_stage_width;
+
+ if (start_from_scratch) scratch();
},
destroy() {
+ container.innerHTML = '';
clearTimeout(timer);
},
};
}
-function appendStyles(styles: string) {
+function append_styles(styles: string) {
+ if (document.querySelector(`style[data-neoconfetti]`)) return;
+
const style = element('style');
style.dataset.neoconfetti = '';
style.textContent = styles;
- appendChild(document.head, style);
+ append_child(document.head, style);
}
-function createParticleNodes(node: HTMLElement, particles: Particle[] = []) {
- const particleNodes: HTMLElement[] = [];
+function create_particle_nodes(
+ container: HTMLElement,
+ particles: Particle[] = [],
+ particleClass: ConfettiOptions['particleClass']
+) {
+ const particle_nodes: HTMLElement[] = [];
for (const { color } of particles) {
- const particleNode = element('div');
- particleNode.className = cParticle;
- particleNode.style.setProperty('--bgc', color);
+ const particle_node = element('div');
+ particle_node.className = `${c_particle} ${particleClass}`;
+ particle_node.style.setProperty('--bgc', color);
- const innerParticle = element('div');
+ const inner_particle = element('div');
- appendChild(particleNode, innerParticle);
+ append_child(particle_node, inner_particle);
- appendChild(node, particleNode);
- particleNodes.push(particleNode);
+ append_child(container, particle_node);
+ particle_nodes.push(particle_node);
}
- return particleNodes;
+ return particle_nodes;
}
const ROTATION_SPEED_MIN = 200; // minimum possible duration of single particle full rotation
@@ -268,13 +328,13 @@ const BEZIER_MEDIAN = 0.5; // utility for mid-point bezier curves, to ensure smo
const abs = Math.abs,
random = Math.random,
- mathRound = Math.round,
+ math_round = Math.round,
max = Math.max;
const element = (name: K) => document.createElement(name);
-const appendChild = (parent: HTMLElement, child: HTMLElement) => parent.appendChild(child);
+const append_child = (parent: HTMLElement, child: HTMLElement) => parent.appendChild(child);
-const createParticles = (count: number, colors: string[]) =>
+const create_particles = (count: number, colors: string[]) =>
Array.from({ length: count }, (_, i) => ({
color: colors[i % colors.length],
degree: (i * 360) / count,
@@ -282,15 +342,17 @@ const createParticles = (count: number, colors: string[]) =>
// From here: https://stackoverflow.com/a/11832950
const round = (num: number, precision: number = 2) =>
- mathRound((num + Number.EPSILON) * 10 ** precision) / 10 ** precision;
+ math_round((num + Number.EPSILON) * 10 ** precision) / 10 ** precision;
-const mapRange = (value: number, x1: number, y1: number, x2: number, y2: number) =>
+const map_range = (value: number, x1: number, y1: number, x2: number, y2: number) =>
((value - x1) * (y2 - x2)) / (y1 - x1) + x2;
const rotate = (degree: number, amount: number) =>
degree + amount > 360 ? degree + amount - 360 : degree + amount;
-const coinFlip = () => random() > 0.5;
+const coin_flip = () => random() > 0.5;
+
+const object_entries = Object.entries;
// We can use the first three bits to flag which axis to rotate on.
// x = binary 100 = decimal 4
@@ -303,6 +365,6 @@ const coinFlip = () => random() > 0.5;
const POSSIBLE_ROTATION_TRANSFORMS = 6;
// avoid rotation on z axis (001 = 1) for circles as it has no visual effect.
-const shouldBeCircle = (rotationTransform: number) => rotationTransform !== 1 && coinFlip();
+const should_be_circle = (rotation_transform: number) => rotation_transform !== 1 && coin_flip();
export type { ConfettiOptions, ParticleShape as ConfettiParticleShape };
diff --git a/packages/core/src/style.module.css b/packages/core/src/style.module.css
index 2f3feae..998b90b 100644
--- a/packages/core/src/style.module.css
+++ b/packages/core/src/style.module.css
@@ -1,16 +1,19 @@
-@keyframes y-axis {
+/* y-axis */
+@keyframes ya {
to {
translate: 0 var(--sh);
}
}
-@keyframes x-axis {
+/* x-axis */
+@keyframes xa {
to {
translate: var(--xlp) 0;
}
}
-@keyframes rotation {
+/* rotation */
+@keyframes r {
50% {
rotate: var(--hr) 180deg;
}
@@ -19,7 +22,8 @@
}
}
-.container {
+/* Container */
+.c {
width: 0;
height: 0;
@@ -30,24 +34,25 @@
z-index: 1200;
}
-.particle {
- animation: x-axis var(--dc) forwards cubic-bezier(var(--x1), var(--x2), var(--x3), var(--x4));
- animation-name: x-axis;
+/* Particle */
+.p {
+ animation: xa var(--dc) forwards cubic-bezier(var(--x1), var(--x2), var(--x3), var(--x4));
+ animation-name: xa;
}
-.particle > div {
+.p > div {
position: absolute;
top: 0;
left: 0;
- animation: y-axis var(--dc) forwards cubic-bezier(var(--y1), var(--y2), var(--y3), var(--y4));
- animation-name: y-axis;
+ animation: ya var(--dc) forwards cubic-bezier(var(--y1), var(--y2), var(--y3), var(--y4));
+ animation-name: ya;
width: var(--w);
height: var(--h);
}
-.particle > div::before {
+.p > div::before {
display: block;
height: 100%;
@@ -56,8 +61,8 @@
content: '';
background-color: var(--bgc);
- animation: rotation var(--rd) infinite linear;
- animation-name: rotation;
+ animation: r var(--rd) infinite linear;
+ animation-name: r;
border-radius: var(--br);
}
diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md
new file mode 100644
index 0000000..5f0bc53
--- /dev/null
+++ b/packages/react/CHANGELOG.md
@@ -0,0 +1,19 @@
+# @neoconfetti/react
+
+## 1.0.0
+
+### Major Changes
+
+- e0e8a6b: Compatible with React Server Components
+
+## 0.2.0
+
+### Minor Changes
+
+- 248c6be: Add React client directive
+
+## 0.1.1
+
+### Patch Changes
+
+- 51f551a: Fix particleShape rectangles
diff --git a/packages/react/README.md b/packages/react/README.md
new file mode 100644
index 0000000..cbb74f6
--- /dev/null
+++ b/packages/react/README.md
@@ -0,0 +1,202 @@
+# @neoconfetti/react
+
+Let's party 🎊🎊 with React! `@neoconfetti/react` allows you to show an awesome confetti explosion on your page, with React/Preact/Million!
+
+## Features
+
+- 🤏 Tiny - 1.61KB min+br.
+- 🐇 Simple - Quite simple to use, and effectively no-config required!
+- 🗃️ Customizable - Offers tons of options that you can modify to get different behaviors.
+- 🖥️ SSR friendly - Works seamlessly in NextJS/Remix/Gatsby/Redwood and other Server Side Rendering environments!
+
+
+
+
+## Installing
+
+```bash
+# pnpm
+pnpm add @neoconfetti/react
+
+# bun
+bun install @neoconfetti/react
+
+# npm
+npm install @neoconfetti/react
+```
+
+## Usage
+
+Basic usage:
+
+```tsx
+import { Confetti } from '@neoconfetti/react';
+
+;
+```
+
+Customizing behavior with options:
+
+```tsx
+
+```
+
+## Props
+
+There's tons of options available for this package. All of them are already documented within the code itself, so you'll never have to leave the code editor.
+
+### particleCount
+
+Number of confetti particles to create.
+
+**type:** `number`
+
+**Default value:** 150
+
+**Example:**
+
+```tsx
+
+```
+
+### particleSize
+
+Size of the confetti particles in pixels
+
+**type:** `number`
+
+**Default value:** 12
+
+**Example:**
+
+```tsx
+
+```
+
+### particleShape
+
+Shape of particles to use. Can be `mix`, `circles` or `rectangles`
+
+`mix` will use both circles and rectangles
+`circles` will use only circles
+`rectangles` will use only rectangles
+
+**type:** `'mix' | 'circles' | 'rectangles'`
+
+**Default value:** `'mix'`
+
+**Example:**
+
+```tsx
+
+```
+
+### duration
+
+Duration of the animation in milliseconds
+
+**type:** `number`
+
+**Default value:** 3500
+
+**Example:**
+
+```tsx
+
+```
+
+### colors
+
+Colors to use for the confetti particles. Pass string array of colors. Can use hex colors, named colors, CSS Variables, literally anything valid in plain CSS.
+
+**type:** `Array`
+
+**Default value:** `['#FFC700', '#FF0000', '#2E3191', '#41BBC7']`
+
+**Example:**
+
+```tsx
+
+```
+
+### force
+
+Force of the confetti particles. Between 0 and 1. 0 is no force, 1 is maximum force. Will error out if you pass a value outside of this range.
+
+**type:** `number`
+
+**Default value:** 0.5
+
+**Example:**
+
+```tsx
+
+```
+
+### stageHeight
+
+Height of the stage in pixels. Confetti will only fall within this height.
+
+**type:** `number`
+
+**Default value:** 800
+
+**Example:**
+
+```tsx
+
+```
+
+### stageWidth
+
+Width of the stage in pixels. Confetti will only fall within this width.
+
+**type:** `number`
+
+**Default value:** 1600
+
+**Example:**
+
+```tsx
+
+```
+
+### destroyAfterDone
+
+Whether or not destroy all confetti nodes after the `duration` period has passed. By default it destroys all nodes, to free up memory.
+
+**type:** `boolean`
+
+**Default value:** `true`
+
+**Example:**
+
+```tsx
+
+```
+
+
+
+
+
+
+## Fine-grained reactivity
+
+Changing the options will destroy the existing confetti mid-flight, and create a new one with the new options. Exception: If `particlesCount` isn't changed, and properties like `colors` or `particleShape` is changed, the confetti particles will change their colors or shape mid-flight.
+
+## Performance
+
+This library functions by creating 2 DOM nodes for every single confetti. By default, if the `particlesCount` is set to 150, it will create 300 nodes. This is a lot of nodes. For most devices, these many nodes are not a big issue, but I recommend checking your target devices' performance if you choose to go with a higher number, like 400 or 500.
+
+Also, after the specified `duration`, all the confetti DOM nodes will be destroyed. This is to free up memory. If you wish to keep them around, set `destroyAfterDone` to `false`.
+
+## Credits
+
+This library is the port of the amazing [react-confetti-explosion](https://www.npmjs.com/package//react-confetti-explosion) package, just **10X** smaller and faster. All the logic is from that package only, optimisation and Svelte code are mine 😉
+
+## License
+
+MIT License
+© [Puru Vijay](https://twitter.com/puruvjdev)
diff --git a/packages/react/demo/.gitignore b/packages/react/demo/.gitignore
new file mode 100644
index 0000000..d451ff1
--- /dev/null
+++ b/packages/react/demo/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
diff --git a/packages/react/demo/index.html b/packages/react/demo/index.html
new file mode 100644
index 0000000..38f3861
--- /dev/null
+++ b/packages/react/demo/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/packages/react/demo/package.json b/packages/react/demo/package.json
new file mode 100644
index 0000000..8a13c4c
--- /dev/null
+++ b/packages/react/demo/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "demo",
+ "private": true,
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@neoconfetti/react": "workspace:*"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.2.1",
+ "typescript": "^5.3.3",
+ "vite": "^5.0.10"
+ }
+}
diff --git a/packages/react/demo/src/App.tsx b/packages/react/demo/src/App.tsx
new file mode 100644
index 0000000..46c2a22
--- /dev/null
+++ b/packages/react/demo/src/App.tsx
@@ -0,0 +1,23 @@
+import { Confetti, ConfettiProps } from '@neoconfetti/react';
+import { useEffect, useState } from 'react';
+
+function App() {
+ const [options, setOptions] = useState({ destroyAfterDone: false });
+ const [render, setRender] = useState(true);
+
+ useEffect(() => {
+ setTimeout(() => {
+ setOptions((o) => ({ ...o, colors: ['#eee', 'black'] }));
+ // explode();
+ }, 1000);
+ }, []);
+
+ return (
+ <>
+
+ {render && }
+ >
+ );
+}
+
+export default App;
diff --git a/packages/react/demo/src/index.css b/packages/react/demo/src/index.css
new file mode 100644
index 0000000..ec2585e
--- /dev/null
+++ b/packages/react/demo/src/index.css
@@ -0,0 +1,13 @@
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
diff --git a/packages/react/demo/src/main.tsx b/packages/react/demo/src/main.tsx
new file mode 100644
index 0000000..5a02646
--- /dev/null
+++ b/packages/react/demo/src/main.tsx
@@ -0,0 +1,5 @@
+import { createRoot } from 'react-dom/client';
+import App from './App';
+import './index.css';
+
+createRoot(document.getElementById('root')!).render();
diff --git a/packages/react/demo/src/vite-env.d.ts b/packages/react/demo/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/packages/react/demo/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/react/demo/tsconfig.json b/packages/react/demo/tsconfig.json
new file mode 100644
index 0000000..4b6d9a3
--- /dev/null
+++ b/packages/react/demo/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": false,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["./src"]
+}
diff --git a/packages/react/demo/vite.config.ts b/packages/react/demo/vite.config.ts
new file mode 100644
index 0000000..c600eab
--- /dev/null
+++ b/packages/react/demo/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+});
diff --git a/packages/react/package.json b/packages/react/package.json
new file mode 100644
index 0000000..246f051
--- /dev/null
+++ b/packages/react/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "@neoconfetti/react",
+ "version": "1.0.0",
+ "description": "Confetti explosion in React 🎉🎊",
+ "author": "Puru Vijay",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/puruvj/neoconfetti/issues"
+ },
+ "homepage": "https://github.com/puruvj/neoconfetti/tree/main/packages/react#readme",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/puruvj/neoconfetti.git"
+ },
+ "keywords": [
+ "confetti",
+ "party",
+ "fun",
+ "badass",
+ "badassery",
+ "svelte",
+ "sveltekit",
+ "small",
+ "tiny",
+ "performant",
+ "react",
+ "nextjs",
+ "remix"
+ ],
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "type": "module",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist/*"
+ ],
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": {
+ "production": "./dist/min/index.js",
+ "development": "./dist/index.js"
+ },
+ "default": "./dist/min/index.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "compile:watch": "tsup --watch",
+ "compile": "tsup",
+ "size": "echo 'React size:' && brotli-size dist/min/index.js",
+ "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
+ "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
+ },
+ "devDependencies": {
+ "@neoconfetti/core": "workspace:*"
+ }
+}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
new file mode 100644
index 0000000..dc58663
--- /dev/null
+++ b/packages/react/src/index.ts
@@ -0,0 +1,29 @@
+import { confetti, type ConfettiOptions, type ConfettiParticleShape } from '@neoconfetti/core';
+import { createElement, useEffect, useRef } from 'react';
+
+interface ConfettiProps extends ConfettiOptions {
+ class?: string;
+}
+
+export function Confetti({ class: className, ...options }: ConfettiProps) {
+ const target_ref = useRef(null);
+ const instance_ref = useRef>();
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+ if (!target_ref.current) return;
+
+ if (!instance_ref.current) {
+ instance_ref.current = confetti(target_ref.current, options);
+ return;
+ }
+
+ instance_ref.current.update(options);
+
+ return instance_ref.current.destroy;
+ }, [options]);
+
+ return createElement('div', { ref: target_ref, className });
+}
+
+export type { ConfettiParticleShape, ConfettiProps };
diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json
new file mode 100644
index 0000000..1b38567
--- /dev/null
+++ b/packages/react/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "declarationDir": "./dist",
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "lib": ["DOM", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": false,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["./src"]
+}
diff --git a/packages/react/tsup.config.ts b/packages/react/tsup.config.ts
new file mode 100644
index 0000000..d873926
--- /dev/null
+++ b/packages/react/tsup.config.ts
@@ -0,0 +1,6 @@
+import { coreConfig } from '../config';
+
+export default coreConfig({
+ jsBanner: '"use client";',
+ treeshake: false,
+});
diff --git a/packages/solid/README.md b/packages/solid/README.md
new file mode 100644
index 0000000..d7b8142
--- /dev/null
+++ b/packages/solid/README.md
@@ -0,0 +1,277 @@
+# @neoconfetti/solid
+
+Let's party 🎊🎊 with Solid! `@neoconfetti/solid` allows you to show an awesome confetti explosion on your page, with SolidJS!
+
+## Features
+
+- 🤏 Tiny - 1.55KB min+br.
+- 🐇 Simple - Quite simple to use, and effectively no-config required!
+- 🧙♀️ Elegant - Directive `use:confetti`.
+- 🗃️ Customizable - Offers tons of options that you can modify to get different behaviors.
+- 🖥️ SSR friendly - Works seamlessly in SolidStart!
+
+
+
+## Installing
+
+```bash
+# pnpm
+pnpm add @neoconfetti/solid
+
+# npm
+npm install @neoconfetti/solid
+
+# yarn
+yarn add @neoconfetti/solid
+```
+
+## Usage
+
+Basic usage:
+
+```tsx
+import { createConfetti } from '@neoconfetti/solid';
+
+function App() {
+ const { confetti } = createConfetti();
+
+ return ;
+}
+```
+
+Customizing behavior with options:
+
+```tsx
+
+```
+
+## Props
+
+There's tons of options available for this package. All of them are already documented within the code itself, so you'll never have to leave the code editor.
+
+### particleCount
+
+Number of confetti particles to create.
+
+**type:** `number`
+
+**Default value:** 150
+
+**Example:**
+
+```tsx
+
+```
+
+### particleSize
+
+Size of the confetti particles in pixels
+
+**type:** `number`
+
+**Default value:** 12
+
+**Example:**
+
+```tsx
+
+```
+
+### particleShape
+
+Shape of particles to use. Can be `mix`, `circles` or `rectangles`
+
+`mix` will use both circles and rectangles
+`circles` will use only circles
+`rectangles` will use only rectangles
+
+**type:** `'mix' | 'circles' | 'rectangles'`
+
+**Default value:** `'mix'`
+
+**Example:**
+
+```tsx
+
+```
+
+### duration
+
+Duration of the animation in milliseconds
+
+**type:** `number`
+
+**Default value:** 3500
+
+**Example:**
+
+```tsx
+
+```
+
+### colors
+
+Colors to use for the confetti particles. Pass string array of colors. Can use hex colors, named colors, CSS Variables, literally anything valid in plain CSS.
+
+**type:** `Array`
+
+**Default value:** `['#FFC700', '#FF0000', '#2E3191', '#41BBC7']`
+
+**Example:**
+
+```tsx
+
+```
+
+### force
+
+Force of the confetti particles. Between 0 and 1. 0 is no force, 1 is maximum force. Will error out if you pass a value outside of this range.
+
+**type:** `number`
+
+**Default value:** 0.5
+
+**Example:**
+
+```tsx
+
+```
+
+### stageHeight
+
+Height of the stage in pixels. Confetti will only fall within this height.
+
+**type:** `number`
+
+**Default value:** 800
+
+**Example:**
+
+```tsx
+
+```
+
+### stageWidth
+
+Width of the stage in pixels. Confetti will only fall within this width.
+
+**type:** `number`
+
+**Default value:** 1600
+
+**Example:**
+
+```tsx
+
+```
+
+### destroyAfterDone
+
+Whether or not destroy all confetti nodes after the `duration` period has passed. By default it destroys all nodes, to free up memory.
+
+**type:** `boolean`
+
+**Default value:** `true`
+
+**Example:**
+
+```tsx
+
+```
+
+
+
+## TypeScript
+
+This library ships with proper TypeScript typings, for the best Developer Experience, whether authoring JS or TS.
+
+However, if you destructure the result of `createConfetti()` to something other than `confetti`, you will get errors.
+If you rename it, like this:
+
+```tsx
+import { createConfetti } from '@neoconfetti/solid';
+
+const { confetti: myCustomConfetti } = createConfetti();
+
+;
+```
+
+It will give an error
+
+```plaintext
+Property 'use:myCustomConfetti' does not exist on type 'HTMLAttributes'.
+```
+
+In that case, you have to manually add to your `globals.d.ts` file this snippet:
+
+```ts
+import { ConfettiOptions } from '@neoconfetti/solid';
+import 'solid-js';
+
+declare module 'solid-js' {
+ namespace JSX {
+ interface Directives {
+ myCustomConfetti: ConfettiOptions;
+ }
+ }
+}
+```
+
+## Fine-grained reactivity
+
+Changing the options will destroy the existing confetti mid-flight, and create a new one with the new options. Exception: If `particlesCount` isn't changed, and properties like `colors` or `particleShape` is changed, the confetti particles will change their colors or shape mid-flight.
+
+## Performance
+
+This library functions by creating 2 DOM nodes for every single confetti. By default, if the `particlesCount` is set to 150, it will create 300 nodes. This is a lot of nodes. For most devices, these many nodes are not a big issue, but I recommend checking your target devices' performance if you choose to go with a higher number, like 400 or 500.
+
+Also, after the specified `duration`, all the confetti DOM nodes will be destroyed. This is to free up memory. If you wish to keep them around, set `destroyAfterDone` to `false`.
+
+## Future plans
+
+Right now, if you look at the syntax, it looks like this:
+
+```tsx
+import { createConfetti } from '@neoconfetti/solid';
+
+const { confetti } = createConfetti();
+
+;
+```
+
+If you look closely, the `createConfetti` is completely useless. It could've just been:
+
+```tsx
+import { confetti } from '@neoconfetti/solid';
+
+;
+```
+
+So why an extra function? Because `use:confetti`, after compiled, is just treated as a string. This means, the typescript compiler/rollup will just remove the import named `confetti` completely, breaking the whole code. Hence a wrapper function is needed.
+
+In future, if SolidJS introduces a mechanism similar to [Svelte's actions](https://svelte.dev/tutorial/actions), I'll be able to get rid of that extra call entirely! So if you find this syntax a little more verbose, bear with me, it might become better :)
+
+## Credits
+
+This library is the port of the amazing [react-confetti-explosion](https://www.npmjs.com/package//react-confetti-explosion) package, just **10X** smaller and faster. All the logic is from that package only, optimisation and Svelte code are mine 😉
+
+## License
+
+MIT License
+© [Puru Vijay](https://twitter.com/puruvjdev)
diff --git a/packages/solid/demo/README.md b/packages/solid/demo/README.md
new file mode 100644
index 0000000..434f7bb
--- /dev/null
+++ b/packages/solid/demo/README.md
@@ -0,0 +1,34 @@
+## Usage
+
+Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
+
+This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
+
+```bash
+$ npm install # or pnpm install or yarn install
+```
+
+### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm dev` or `npm start`
+
+Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
+
+The page will reload if you make edits.
+
+### `npm run build`
+
+Builds the app for production to the `dist` folder.
+It correctly bundles Solid in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.
+Your app is ready to be deployed!
+
+## Deployment
+
+You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
diff --git a/packages/solid/demo/index.html b/packages/solid/demo/index.html
new file mode 100644
index 0000000..0afc97f
--- /dev/null
+++ b/packages/solid/demo/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Solid App
+
+
+
+
+
+
+
+
diff --git a/packages/solid/demo/package.json b/packages/solid/demo/package.json
new file mode 100644
index 0000000..747b039
--- /dev/null
+++ b/packages/solid/demo/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "demo",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "start": "vite",
+ "dev": "vite",
+ "build": "vite build",
+ "serve": "vite preview"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "typescript": "^5.3.3",
+ "vite": "^5.0.10",
+ "vite-plugin-solid": "^2.8.0"
+ },
+ "dependencies": {
+ "@neoconfetti/solid": "workspace:*",
+ "solid-js": "^1.8.7"
+ }
+}
diff --git a/packages/solid/demo/pnpm-lock.yaml b/packages/solid/demo/pnpm-lock.yaml
new file mode 100644
index 0000000..d75579c
--- /dev/null
+++ b/packages/solid/demo/pnpm-lock.yaml
@@ -0,0 +1,880 @@
+lockfileVersion: 5.4
+
+specifiers:
+ solid-js: ^1.4.2
+ typescript: ^4.6.4
+ vite: ^2.9.9
+ vite-plugin-solid: ^2.2.6
+
+dependencies:
+ solid-js: 1.4.2
+
+devDependencies:
+ typescript: 4.6.4
+ vite: 2.9.9
+ vite-plugin-solid: 2.2.6
+
+packages:
+
+ /@ampproject/remapping/2.1.2:
+ resolution: {integrity: sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.4
+ dev: true
+
+ /@babel/code-frame/7.16.7:
+ resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/highlight': 7.16.10
+ dev: true
+
+ /@babel/compat-data/7.17.7:
+ resolution: {integrity: sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/core/7.17.8:
+ resolution: {integrity: sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@ampproject/remapping': 2.1.2
+ '@babel/code-frame': 7.16.7
+ '@babel/generator': 7.17.7
+ '@babel/helper-compilation-targets': 7.17.7_@babel+core@7.17.8
+ '@babel/helper-module-transforms': 7.17.7
+ '@babel/helpers': 7.17.8
+ '@babel/parser': 7.17.8
+ '@babel/template': 7.16.7
+ '@babel/traverse': 7.17.3
+ '@babel/types': 7.17.0
+ convert-source-map: 1.8.0
+ debug: 4.3.4
+ gensync: 1.0.0-beta.2
+ json5: 2.2.1
+ semver: 6.3.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/generator/7.17.7:
+ resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ jsesc: 2.5.2
+ source-map: 0.5.7
+ dev: true
+
+ /@babel/helper-annotate-as-pure/7.16.7:
+ resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.18.0
+ dev: true
+
+ /@babel/helper-compilation-targets/7.17.7_@babel+core@7.17.8:
+ resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/compat-data': 7.17.7
+ '@babel/core': 7.17.8
+ '@babel/helper-validator-option': 7.16.7
+ browserslist: 4.20.2
+ semver: 6.3.0
+ dev: true
+
+ /@babel/helper-create-class-features-plugin/7.17.6_@babel+core@7.17.8:
+ resolution: {integrity: sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.17.8
+ '@babel/helper-annotate-as-pure': 7.16.7
+ '@babel/helper-environment-visitor': 7.16.7
+ '@babel/helper-function-name': 7.16.7
+ '@babel/helper-member-expression-to-functions': 7.17.7
+ '@babel/helper-optimise-call-expression': 7.16.7
+ '@babel/helper-replace-supers': 7.16.7
+ '@babel/helper-split-export-declaration': 7.16.7
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/helper-environment-visitor/7.16.7:
+ resolution: {integrity: sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-function-name/7.16.7:
+ resolution: {integrity: sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-get-function-arity': 7.16.7
+ '@babel/template': 7.16.7
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-get-function-arity/7.16.7:
+ resolution: {integrity: sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-hoist-variables/7.16.7:
+ resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-member-expression-to-functions/7.17.7:
+ resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.18.0
+ dev: true
+
+ /@babel/helper-module-imports/7.16.0:
+ resolution: {integrity: sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-module-imports/7.16.7:
+ resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-module-transforms/7.17.7:
+ resolution: {integrity: sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-environment-visitor': 7.16.7
+ '@babel/helper-module-imports': 7.16.7
+ '@babel/helper-simple-access': 7.17.7
+ '@babel/helper-split-export-declaration': 7.16.7
+ '@babel/helper-validator-identifier': 7.16.7
+ '@babel/template': 7.16.7
+ '@babel/traverse': 7.17.3
+ '@babel/types': 7.17.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/helper-optimise-call-expression/7.16.7:
+ resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.18.0
+ dev: true
+
+ /@babel/helper-plugin-utils/7.16.7:
+ resolution: {integrity: sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-replace-supers/7.16.7:
+ resolution: {integrity: sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-environment-visitor': 7.16.7
+ '@babel/helper-member-expression-to-functions': 7.17.7
+ '@babel/helper-optimise-call-expression': 7.16.7
+ '@babel/traverse': 7.17.3
+ '@babel/types': 7.18.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/helper-simple-access/7.17.7:
+ resolution: {integrity: sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-split-export-declaration/7.16.7:
+ resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/helper-validator-identifier/7.16.7:
+ resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-validator-option/7.16.7:
+ resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helpers/7.17.8:
+ resolution: {integrity: sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.16.7
+ '@babel/traverse': 7.17.3
+ '@babel/types': 7.17.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/highlight/7.16.10:
+ resolution: {integrity: sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.16.7
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ dev: true
+
+ /@babel/parser/7.17.8:
+ resolution: {integrity: sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/plugin-syntax-jsx/7.16.7_@babel+core@7.17.8:
+ resolution: {integrity: sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.17.8
+ '@babel/helper-plugin-utils': 7.16.7
+ dev: true
+
+ /@babel/plugin-syntax-typescript/7.16.7_@babel+core@7.17.8:
+ resolution: {integrity: sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.17.8
+ '@babel/helper-plugin-utils': 7.16.7
+ dev: true
+
+ /@babel/plugin-transform-typescript/7.16.8_@babel+core@7.17.8:
+ resolution: {integrity: sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.17.8
+ '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.17.8
+ '@babel/helper-plugin-utils': 7.16.7
+ '@babel/plugin-syntax-typescript': 7.16.7_@babel+core@7.17.8
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/preset-typescript/7.16.7_@babel+core@7.17.8:
+ resolution: {integrity: sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.17.8
+ '@babel/helper-plugin-utils': 7.16.7
+ '@babel/helper-validator-option': 7.16.7
+ '@babel/plugin-transform-typescript': 7.16.8_@babel+core@7.17.8
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/template/7.16.7:
+ resolution: {integrity: sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.16.7
+ '@babel/parser': 7.17.8
+ '@babel/types': 7.17.0
+ dev: true
+
+ /@babel/traverse/7.17.3:
+ resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.16.7
+ '@babel/generator': 7.17.7
+ '@babel/helper-environment-visitor': 7.16.7
+ '@babel/helper-function-name': 7.16.7
+ '@babel/helper-hoist-variables': 7.16.7
+ '@babel/helper-split-export-declaration': 7.16.7
+ '@babel/parser': 7.17.8
+ '@babel/types': 7.17.0
+ debug: 4.3.4
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/types/7.17.0:
+ resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.16.7
+ to-fast-properties: 2.0.0
+ dev: true
+
+ /@babel/types/7.18.0:
+ resolution: {integrity: sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.16.7
+ to-fast-properties: 2.0.0
+ dev: true
+
+ /@jridgewell/resolve-uri/3.0.5:
+ resolution: {integrity: sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
+ /@jridgewell/sourcemap-codec/1.4.11:
+ resolution: {integrity: sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==}
+ dev: true
+
+ /@jridgewell/trace-mapping/0.3.4:
+ resolution: {integrity: sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.0.5
+ '@jridgewell/sourcemap-codec': 1.4.11
+ dev: true
+
+ /ansi-styles/3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+ dependencies:
+ color-convert: 1.9.3
+ dev: true
+
+ /babel-plugin-jsx-dom-expressions/0.32.11_@babel+core@7.17.8:
+ resolution: {integrity: sha512-hytqY33SGW6B3obSLt8K5X510UwtNkTktCCWgwba+QOOV0CowDFiqeL+0ru895FLacFaYANHFTu1y76dg3GVtw==}
+ dependencies:
+ '@babel/helper-module-imports': 7.16.0
+ '@babel/plugin-syntax-jsx': 7.16.7_@babel+core@7.17.8
+ '@babel/types': 7.17.0
+ html-entities: 2.3.2
+ transitivePeerDependencies:
+ - '@babel/core'
+ dev: true
+
+ /babel-preset-solid/1.3.13_@babel+core@7.17.8:
+ resolution: {integrity: sha512-MZnmsceI9yiHlwwFCSALTJhadk2eea/+2UP4ec4jkPZFR+XRKTLoIwRkrBh7uLtvHF+3lHGyUaXtZukOmmUwhA==}
+ dependencies:
+ babel-plugin-jsx-dom-expressions: 0.32.11_@babel+core@7.17.8
+ transitivePeerDependencies:
+ - '@babel/core'
+ dev: true
+
+ /browserslist/4.20.2:
+ resolution: {integrity: sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+ dependencies:
+ caniuse-lite: 1.0.30001320
+ electron-to-chromium: 1.4.93
+ escalade: 3.1.1
+ node-releases: 2.0.2
+ picocolors: 1.0.0
+ dev: true
+
+ /caniuse-lite/1.0.30001320:
+ resolution: {integrity: sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==}
+ dev: true
+
+ /chalk/2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+ dev: true
+
+ /color-convert/1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+ dependencies:
+ color-name: 1.1.3
+ dev: true
+
+ /color-name/1.1.3:
+ resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
+ dev: true
+
+ /convert-source-map/1.8.0:
+ resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
+ dependencies:
+ safe-buffer: 5.1.2
+ dev: true
+
+ /debug/4.3.4:
+ resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.2
+ dev: true
+
+ /electron-to-chromium/1.4.93:
+ resolution: {integrity: sha512-ywq9Pc5Gwwpv7NG767CtoU8xF3aAUQJjH9//Wy3MBCg4w5JSLbJUq2L8IsCdzPMjvSgxuue9WcVaTOyyxCL0aQ==}
+ dev: true
+
+ /esbuild-android-64/0.14.39:
+ resolution: {integrity: sha512-EJOu04p9WgZk0UoKTqLId9VnIsotmI/Z98EXrKURGb3LPNunkeffqQIkjS2cAvidh+OK5uVrXaIP229zK6GvhQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-android-arm64/0.14.39:
+ resolution: {integrity: sha512-+twajJqO7n3MrCz9e+2lVOnFplRsaGRwsq1KL/uOy7xK7QdRSprRQcObGDeDZUZsacD5gUkk6OiHiYp6RzU3CA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-darwin-64/0.14.39:
+ resolution: {integrity: sha512-ImT6eUw3kcGcHoUxEcdBpi6LfTRWaV6+qf32iYYAfwOeV+XaQ/Xp5XQIBiijLeo+LpGci9M0FVec09nUw41a5g==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-darwin-arm64/0.14.39:
+ resolution: {integrity: sha512-/fcQ5UhE05OiT+bW5v7/up1bDsnvaRZPJxXwzXsMRrr7rZqPa85vayrD723oWMT64dhrgWeA3FIneF8yER0XTw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-freebsd-64/0.14.39:
+ resolution: {integrity: sha512-oMNH8lJI4wtgN5oxuFP7BQ22vgB/e3Tl5Woehcd6i2r6F3TszpCnNl8wo2d/KvyQ4zvLvCWAlRciumhQg88+kQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-freebsd-arm64/0.14.39:
+ resolution: {integrity: sha512-1GHK7kwk57ukY2yI4ILWKJXaxfr+8HcM/r/JKCGCPziIVlL+Wi7RbJ2OzMcTKZ1HpvEqCTBT/J6cO4ZEwW4Ypg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-32/0.14.39:
+ resolution: {integrity: sha512-g97Sbb6g4zfRLIxHgW2pc393DjnkTRMeq3N1rmjDUABxpx8SjocK4jLen+/mq55G46eE2TA0MkJ4R3SpKMu7dg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-64/0.14.39:
+ resolution: {integrity: sha512-4tcgFDYWdI+UbNMGlua9u1Zhu0N5R6u9tl5WOM8aVnNX143JZoBZLpCuUr5lCKhnD0SCO+5gUyMfupGrHtfggQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-arm/0.14.39:
+ resolution: {integrity: sha512-t0Hn1kWVx5UpCzAJkKRfHeYOLyFnXwYynIkK54/h3tbMweGI7dj400D1k0Vvtj2u1P+JTRT9tx3AjtLEMmfVBQ==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-arm64/0.14.39:
+ resolution: {integrity: sha512-23pc8MlD2D6Px1mV8GMglZlKgwgNKAO8gsgsLLcXWSs9lQsCYkIlMo/2Ycfo5JrDIbLdwgP8D2vpfH2KcBqrDQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-mips64le/0.14.39:
+ resolution: {integrity: sha512-epwlYgVdbmkuRr5n4es3B+yDI0I2e/nxhKejT9H0OLxFAlMkeQZxSpxATpDc9m8NqRci6Kwyb/SfmD1koG2Zuw==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-ppc64le/0.14.39:
+ resolution: {integrity: sha512-W/5ezaq+rQiQBThIjLMNjsuhPHg+ApVAdTz2LvcuesZFMsJoQAW2hutoyg47XxpWi7aEjJGrkS26qCJKhRn3QQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-riscv64/0.14.39:
+ resolution: {integrity: sha512-IS48xeokcCTKeQIOke2O0t9t14HPvwnZcy+5baG13Z1wxs9ZrC5ig5ypEQQh4QMKxURD5TpCLHw2W42CLuVZaA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-s390x/0.14.39:
+ resolution: {integrity: sha512-zEfunpqR8sMomqXhNTFEKDs+ik7HC01m3M60MsEjZOqaywHu5e5682fMsqOlZbesEAAaO9aAtRBsU7CHnSZWyA==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-netbsd-64/0.14.39:
+ resolution: {integrity: sha512-Uo2suJBSIlrZCe4E0k75VDIFJWfZy+bOV6ih3T4MVMRJh1lHJ2UyGoaX4bOxomYN3t+IakHPyEoln1+qJ1qYaA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-openbsd-64/0.14.39:
+ resolution: {integrity: sha512-secQU+EpgUPpYjJe3OecoeGKVvRMLeKUxSMGHnK+aK5uQM3n1FPXNJzyz1LHFOo0WOyw+uoCxBYdM4O10oaCAA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-sunos-64/0.14.39:
+ resolution: {integrity: sha512-qHq0t5gePEDm2nqZLb+35p/qkaXVS7oIe32R0ECh2HOdiXXkj/1uQI9IRogGqKkK+QjDG+DhwiUw7QoHur/Rwg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-windows-32/0.14.39:
+ resolution: {integrity: sha512-XPjwp2OgtEX0JnOlTgT6E5txbRp6Uw54Isorm3CwOtloJazeIWXuiwK0ONJBVb/CGbiCpS7iP2UahGgd2p1x+Q==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-windows-64/0.14.39:
+ resolution: {integrity: sha512-E2wm+5FwCcLpKsBHRw28bSYQw0Ikxb7zIMxw3OPAkiaQhLVr3dnVO8DofmbWhhf6b97bWzg37iSZ45ZDpLw7Ow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-windows-arm64/0.14.39:
+ resolution: {integrity: sha512-sBZQz5D+Gd0EQ09tZRnz/PpVdLwvp/ufMtJ1iDFYddDaPpZXKqPyaxfYBLs3ueiaksQ26GGa7sci0OqFzNs7KA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild/0.14.39:
+ resolution: {integrity: sha512-2kKujuzvRWYtwvNjYDY444LQIA3TyJhJIX3Yo4+qkFlDDtGlSicWgeHVJqMUP/2sSfH10PGwfsj+O2ro1m10xQ==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ esbuild-android-64: 0.14.39
+ esbuild-android-arm64: 0.14.39
+ esbuild-darwin-64: 0.14.39
+ esbuild-darwin-arm64: 0.14.39
+ esbuild-freebsd-64: 0.14.39
+ esbuild-freebsd-arm64: 0.14.39
+ esbuild-linux-32: 0.14.39
+ esbuild-linux-64: 0.14.39
+ esbuild-linux-arm: 0.14.39
+ esbuild-linux-arm64: 0.14.39
+ esbuild-linux-mips64le: 0.14.39
+ esbuild-linux-ppc64le: 0.14.39
+ esbuild-linux-riscv64: 0.14.39
+ esbuild-linux-s390x: 0.14.39
+ esbuild-netbsd-64: 0.14.39
+ esbuild-openbsd-64: 0.14.39
+ esbuild-sunos-64: 0.14.39
+ esbuild-windows-32: 0.14.39
+ esbuild-windows-64: 0.14.39
+ esbuild-windows-arm64: 0.14.39
+ dev: true
+
+ /escalade/3.1.1:
+ resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /escape-string-regexp/1.0.5:
+ resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
+ engines: {node: '>=0.8.0'}
+ dev: true
+
+ /fsevents/2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /function-bind/1.1.1:
+ resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+ dev: true
+
+ /gensync/1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /globals/11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /has-flag/3.0.0:
+ resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /has/1.0.3:
+ resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+ engines: {node: '>= 0.4.0'}
+ dependencies:
+ function-bind: 1.1.1
+ dev: true
+
+ /html-entities/2.3.2:
+ resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==}
+ dev: true
+
+ /is-core-module/2.9.0:
+ resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
+ dependencies:
+ has: 1.0.3
+ dev: true
+
+ /is-what/4.1.7:
+ resolution: {integrity: sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==}
+ engines: {node: '>=12.13'}
+ dev: true
+
+ /js-tokens/4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ dev: true
+
+ /jsesc/2.5.2:
+ resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+ engines: {node: '>=4'}
+ hasBin: true
+ dev: true
+
+ /json5/2.2.1:
+ resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
+ engines: {node: '>=6'}
+ hasBin: true
+ dev: true
+
+ /merge-anything/5.0.2:
+ resolution: {integrity: sha512-POPQBWkBC0vxdgzRJ2Mkj4+2NTKbvkHo93ih+jGDhNMLzIw+rYKjO7949hOQM2X7DxMHH1uoUkwWFLIzImw7gA==}
+ engines: {node: '>=12.13'}
+ dependencies:
+ is-what: 4.1.7
+ ts-toolbelt: 9.6.0
+ dev: true
+
+ /ms/2.1.2:
+ resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+ dev: true
+
+ /nanoid/3.3.4:
+ resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+ dev: true
+
+ /node-releases/2.0.2:
+ resolution: {integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==}
+ dev: true
+
+ /path-parse/1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ dev: true
+
+ /picocolors/1.0.0:
+ resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+ dev: true
+
+ /postcss/8.4.14:
+ resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.4
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+ dev: true
+
+ /resolve/1.22.0:
+ resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.9.0
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+ dev: true
+
+ /rollup/2.74.1:
+ resolution: {integrity: sha512-K2zW7kV8Voua5eGkbnBtWYfMIhYhT9Pel2uhBk2WO5eMee161nPze/XRfvEQPFYz7KgrCCnmh2Wy0AMFLGGmMA==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /safe-buffer/5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+ dev: true
+
+ /semver/6.3.0:
+ resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+ hasBin: true
+ dev: true
+
+ /solid-js/1.4.2:
+ resolution: {integrity: sha512-IU5yKuT8P/n5F5g8j1rTXqxUdPYmoZDk/074TG94AEYf/nyXAeG82BSge4/lLIbCfUcnGUJ6DRdebIjujOAYyg==}
+
+ /solid-refresh/0.4.0_solid-js@1.4.2:
+ resolution: {integrity: sha512-5XCUz845n/sHPzKK2i2G2EeV61tAmzv6SqzqhXcPaYhrgzVy7nKTQaBpKK8InKrriq9Z2JFF/mguIU00t/73xw==}
+ peerDependencies:
+ solid-js: ^1.3.0
+ dependencies:
+ '@babel/generator': 7.17.7
+ '@babel/helper-module-imports': 7.16.7
+ '@babel/types': 7.17.0
+ solid-js: 1.4.2
+ dev: true
+
+ /source-map-js/1.0.2:
+ resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /source-map/0.5.7:
+ resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /supports-color/5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+ dependencies:
+ has-flag: 3.0.0
+ dev: true
+
+ /supports-preserve-symlinks-flag/1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /to-fast-properties/2.0.0:
+ resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /ts-toolbelt/9.6.0:
+ resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==}
+ dev: true
+
+ /typescript/4.6.4:
+ resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==}
+ engines: {node: '>=4.2.0'}
+ hasBin: true
+ dev: true
+
+ /vite-plugin-solid/2.2.6:
+ resolution: {integrity: sha512-J1RnmqkZZJSNYDW7vZj0giKKHLWGr9tS/gxR70WDSTYfhyXrgukbZdIfSEFbtrsg8ZiQ2t2zXcvkWoeefenqKw==}
+ dependencies:
+ '@babel/core': 7.17.8
+ '@babel/preset-typescript': 7.16.7_@babel+core@7.17.8
+ babel-preset-solid: 1.3.13_@babel+core@7.17.8
+ merge-anything: 5.0.2
+ solid-js: 1.4.2
+ solid-refresh: 0.4.0_solid-js@1.4.2
+ vite: 2.9.9
+ transitivePeerDependencies:
+ - less
+ - sass
+ - stylus
+ - supports-color
+ dev: true
+
+ /vite/2.9.9:
+ resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==}
+ engines: {node: '>=12.2.0'}
+ hasBin: true
+ peerDependencies:
+ less: '*'
+ sass: '*'
+ stylus: '*'
+ peerDependenciesMeta:
+ less:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ dependencies:
+ esbuild: 0.14.39
+ postcss: 8.4.14
+ resolve: 1.22.0
+ rollup: 2.74.1
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
diff --git a/packages/solid/demo/src/App.module.css b/packages/solid/demo/src/App.module.css
new file mode 100644
index 0000000..e69de29
diff --git a/packages/solid/demo/src/App.tsx b/packages/solid/demo/src/App.tsx
new file mode 100644
index 0000000..7adbb6a
--- /dev/null
+++ b/packages/solid/demo/src/App.tsx
@@ -0,0 +1,47 @@
+import { ConfettiOptions, createConfetti } from '@neoconfetti/solid';
+import { Component, createEffect, createSignal } from 'solid-js';
+
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+const App: Component = () => {
+ const { confetti } = createConfetti();
+ const [options, setOptions] = createSignal({});
+ const [render, setRender] = createSignal(false);
+
+ createEffect(() => {
+ async function run() {
+ if (!render) return;
+
+ await sleep(2000);
+
+ setOptions((o) => ({
+ ...o,
+ colors: ['red', 'blue'],
+ particleShape: 'circles',
+ }));
+
+ await sleep(600);
+
+ // Override entire options, re-triggers the confetti
+ setOptions({
+ particleCount: 200,
+ duration: 3500,
+ colors: ['#eee', 'black', 'hotpink' /* Sorry 😅 */],
+ });
+ }
+
+ run();
+ });
+
+ return (
+ <>
+ <>
+
+
+ {render() && }
+ >
+ >
+ );
+};
+
+export default App;
diff --git a/packages/solid/demo/src/global.d.ts b/packages/solid/demo/src/global.d.ts
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/packages/solid/demo/src/global.d.ts
@@ -0,0 +1 @@
+
diff --git a/packages/solid/demo/src/index.tsx b/packages/solid/demo/src/index.tsx
new file mode 100644
index 0000000..2006c75
--- /dev/null
+++ b/packages/solid/demo/src/index.tsx
@@ -0,0 +1,6 @@
+/* @refresh reload */
+import { render } from 'solid-js/web';
+
+import App from './App';
+
+render(() => , document.getElementById('root') as HTMLElement);
diff --git a/packages/solid/demo/tsconfig.json b/packages/solid/demo/tsconfig.json
new file mode 100644
index 0000000..249b273
--- /dev/null
+++ b/packages/solid/demo/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "node",
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "jsx": "preserve",
+ "jsxImportSource": "solid-js",
+ "types": ["vite/client"],
+ "noEmit": true,
+ "isolatedModules": true
+ }
+}
diff --git a/packages/solid/demo/vite.config.ts b/packages/solid/demo/vite.config.ts
new file mode 100644
index 0000000..d52d794
--- /dev/null
+++ b/packages/solid/demo/vite.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vite';
+import solidPlugin from 'vite-plugin-solid';
+
+export default defineConfig({
+ plugins: [solidPlugin()],
+ build: {
+ target: 'esnext',
+ polyfillDynamicImport: false,
+ },
+});
diff --git a/packages/solid/package.json b/packages/solid/package.json
new file mode 100644
index 0000000..e2c7c2c
--- /dev/null
+++ b/packages/solid/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@neoconfetti/solid",
+ "version": "0.1.0",
+ "description": "Confetti explosion in SolidJS 🎉🎊",
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "type": "module",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist/*"
+ ],
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": {
+ "production": "./dist/min/index.js",
+ "development": "./dist/index.js"
+ },
+ "default": "./dist/min/index.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/PuruVJ/neoconfetti.git"
+ },
+ "keywords": [
+ "confetti",
+ "party",
+ "fun",
+ "solid",
+ "solidstart",
+ "badass",
+ "badassery",
+ "svelte",
+ "sveltekit",
+ "small",
+ "tiny",
+ "performant",
+ "lightweight"
+ ],
+ "author": "Puru Vijay",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/PuruVJ/neoconfetti/issues"
+ },
+ "homepage": "https://github.com/PuruVJ/neoconfetti/tree/main/packages/solid#readme",
+ "scripts": {
+ "compile": "tsup",
+ "compile:watch": "tsup --watch",
+ "size": "echo 'Solid size:' && brotli-size dist/min/index.js",
+ "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
+ "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
+ },
+ "devDependencies": {
+ "@neoconfetti/core": "workspace:*"
+ },
+ "peerDependencies": {
+ "solid-js": "^1.0.0"
+ }
+}
diff --git a/packages/solid/src/index.ts b/packages/solid/src/index.ts
new file mode 100644
index 0000000..2b64098
--- /dev/null
+++ b/packages/solid/src/index.ts
@@ -0,0 +1,13 @@
+import { confetti, type ConfettiOptions, type ConfettiParticleShape } from '@neoconfetti/core';
+import { createEffect, onCleanup, type Accessor } from 'solid-js';
+
+export const createConfetti = () => ({
+ confetti: (node: HTMLElement, options: Accessor) => {
+ const { update, destroy } = confetti(node, options());
+
+ onCleanup(destroy);
+ createEffect(() => update(options()));
+ },
+});
+
+export type { ConfettiOptions, ConfettiParticleShape };
diff --git a/packages/solid/tsconfig.json b/packages/solid/tsconfig.json
new file mode 100644
index 0000000..0ca3c46
--- /dev/null
+++ b/packages/solid/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "declarationDir": "./dist",
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "lib": ["DOM", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": false,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "preserveSymlinks": false
+ },
+ "include": ["./src"]
+}
diff --git a/packages/solid/tsup.config.ts b/packages/solid/tsup.config.ts
new file mode 100644
index 0000000..7b6019a
--- /dev/null
+++ b/packages/solid/tsup.config.ts
@@ -0,0 +1,14 @@
+import { coreConfig } from '../config';
+
+export default coreConfig({
+ dtsBanner: `import 'solid-js';
+
+declare module 'solid-js' {
+ namespace JSX {
+ interface Directives {
+ confetti: ConfettiOptions | boolean;
+ }
+ }
+}
+`,
+});
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index 2dc33d5..e1e81de 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,29 @@
# @neoconfetti/svelte
+## 2.2.2
+
+### Patch Changes
+
+- bbc8ff4: Add svelte peerDependency
+
+## 2.2.1
+
+### Patch Changes
+
+- 51f551a: Fix particleShape rectangles
+
+## 2.2.0
+
+### Minor Changes
+
+- 4eb52f1: Add particleClass property, optimize code
+
+## 2.1.0
+
+### Minor Changes
+
+- 01a3b51: Fixes, setting any option to undefined reverts it to default value, more fine grained reactivity. If the particlesCount doesn't change, confetti isn't recreated.
+
## 2.0.0
### Major Changes
diff --git a/packages/svelte/README.md b/packages/svelte/README.md
index 5edfe9c..720bf51 100644
--- a/packages/svelte/README.md
+++ b/packages/svelte/README.md
@@ -2,17 +2,15 @@
Let's party 🎊🎊 with Svelte! `@neoconfetti/svelte` allows you to show an awesome confetti explosion on your page, with Svelte!
-> This library is the port of the amazing [react-confetti-explosion](https://www.npmjs.com/package//react-confetti-explosion) package. All the logic is from that package only, optimisation and Svelte code are mine 😉
-
## Features
-- 🤏 Tiny - 1.66KB min+br.
+- 🤏 Tiny - 1.49KB min+br.
- 🐇 Simple - Quite simple to use, and effectively no-config required!
- 🧙♀️ Elegant - Svelte action `use:confetti` rather than setting things up in `onMount` hook.
- 🗃️ Customizable - Offers tons of options that you can modify to get different behaviors.
- 🖥️ SSR friendly - Works seamlessly in Sveltekit and other Server Side Rendering environments!
-[Try it in Svelte REPL](https://svelte.dev/repl/4e41a080739a4427a1f2c98b7f5d4b24)
+[Try it in Svelte REPL](https://svelte.dev/playground/4e41a080739a4427a1f2c98b7f5d4b24)
## Installing
@@ -181,9 +179,13 @@ Whether or not destroy all confetti nodes after the `duration` period has passed
## Examples
-[Basic Example](https://svelte.dev/repl/4e41a080739a4427a1f2c98b7f5d4b24?version=3.50.1)
+[Basic Example](https://svelte.dev/playground/4e41a080739a4427a1f2c98b7f5d4b24?version=3.50.1)
+
+[Confetti where mouse click](https://svelte.dev/playground/dbe0ab06c34f4f25aa6f948fdd1982c7?version=3.50.1)
+
+## Fine-grained reactivity
-[Confetti where mouse click](https://svelte.dev/repl/dbe0ab06c34f4f25aa6f948fdd1982c7?version=3.50.1)
+Changing the options will destroy the existing confetti mid-flight, and create a new one with the new options. Exception: If `particlesCount` isn't changed, and properties like `colors` or `particleShape` is changed, the confetti particles will change their colors or shape mid-flight.
## Performance
@@ -191,6 +193,10 @@ This library functions by creating 2 DOM nodes for every single confetti. By def
Also, after the specified `duration`, all the confetti DOM nodes will be destroyed. This is to free up memory. If you wish to keep them around, set `destroyAfterDone` to `false`.
+## Credits
+
+This library is the port of the amazing [react-confetti-explosion](https://www.npmjs.com/package//react-confetti-explosion) package, just **10X** smaller and faster. All the logic is from that package only, optimisation and Svelte code are mine 😉
+
## License
MIT License
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 674d246..508c2af 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@neoconfetti/svelte",
- "version": "2.0.0",
+ "version": "2.2.2",
"description": "Confetti explosion in Svelte 🎉🎊",
"author": "Puru Vijay",
"license": "MIT",
@@ -34,8 +34,12 @@
"sideEffects": false,
"exports": {
".": {
- "import": "./dist/index.js",
- "module": "./dist/index.js"
+ "types": "./dist/index.d.ts",
+ "import": {
+ "production": "./dist/min/index.js",
+ "development": "./dist/index.js"
+ },
+ "default": "./dist/min/index.js"
},
"./package.json": "./package.json"
},
@@ -43,10 +47,13 @@
"compile:watch": "tsup --watch",
"compile": "tsup",
"size": "echo 'Svelte size:' && brotli-size dist/min/index.js",
- "pub": "pnpm build && pnpm publish --no-git-checks --access public",
- "pub:dry": "pnpm build && pnpm publish --dry-run --no-git-checks --access public"
+ "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
+ "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
},
"devDependencies": {
"@neoconfetti/core": "workspace:*"
+ },
+ "peerDependencies": {
+ "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
}
}
diff --git a/packages/vanilla/CHANGELOG.md b/packages/vanilla/CHANGELOG.md
new file mode 100644
index 0000000..628d691
--- /dev/null
+++ b/packages/vanilla/CHANGELOG.md
@@ -0,0 +1,13 @@
+# @neoconfetti/vanilla
+
+## 0.2.1
+
+### Patch Changes
+
+- 51f551a: Fix particleShape rectangles
+
+## 0.2.0
+
+### Minor Changes
+
+- 4eb52f1: Add particleClass property, optimize code
diff --git a/packages/vanilla/README.md b/packages/vanilla/README.md
new file mode 100644
index 0000000..79b039b
--- /dev/null
+++ b/packages/vanilla/README.md
@@ -0,0 +1,278 @@
+# @neoconfetti/vanilla
+
+Let's party 🎊🎊 with JavaScript! `@neoconfetti/vanilla` allows you to show an awesome confetti explosion on your page!
+
+## Features
+
+- 🤏 Tiny - 1.64KB min+br.
+- 🐇 Simple - Quite simple to use, and effectively no-config required!
+- 🧙♀️ Elegant - `new Confetti()` -> `confetti.explode()`
+- 🗃️ Customizable - Offers tons of options that you can modify to get different behaviors.
+
+[Try it in Stackblitz](https://stackblitz.com/edit/vitejs-vite-uv6ebi?file=src/main.ts,src/style.css,index.html,package.json&terminal=dev)
+
+## Installing
+
+```bash
+pnpm add @neoconfetti/vanilla
+
+# npm
+npm install @neoconfetti/vanilla
+
+# yarn
+yarn add @neoconfetti/vanilla
+```
+
+# Usage
+
+Basic usage
+
+```tsx
+import { Confetti } from '@neoconfetti/vanilla';
+
+const confetti = new Confetti(document.querySelector('#confetti'));
+
+// Explode the confetti
+confetti.explode();
+```
+
+With options
+
+```tsx
+import { Confetti } from '@neoconfetti/vanilla';
+
+const confetti = new Confetti(document.querySelector('#confetti'), {
+ color: ['#ff0000', '#00ff00', '#0000ff'],
+ force: 0.9,
+});
+
+confetti.explode();
+```
+
+Defining options elsewhere with typescript
+
+```tsx
+import { type ConfettiOptions, Confetti } from '@neoconfetti/vanilla';
+
+const options: ConfettiOptions = {
+ color: ['#ff0000', '#00ff00', '#0000ff'],
+ force: 0.9,
+};
+
+const confetti = new Confetti(document.querySelector('#confetti'), options);
+confetti.explode();
+```
+
+Update options:
+
+```ts
+import { Confetti } from '@neoconfetti/vanilla';
+
+const confetti = new Confetti(document.querySelector('#confetti'), {
+ color: ['#ff0000', '#00ff00', '#0000ff'],
+ force: 0.9,
+});
+
+// Update the specific options. Will be merged with the existing options.
+confetti.options.colors = ['white', 'black'];
+
+// Completely overrides existing options, in this case, the `force` property is set to its default value.
+confetti.options = { colors: ['white', 'black'] };
+```
+
+Destroy:
+
+You must destroy the confetti once its not needed
+
+```ts
+confetti.destroy();
+```
+
+Waiting for confetti to finish:
+
+```ts
+// confetti.explode() returns a promise that resolves when the confetti is done exploding. You can use this to wait for the confetti to finish exploding before doing something else.
+await confetti.explode();
+```
+
+## Props
+
+There's tons of options available for this package. All of them are already documented within the code itself, so you'll never have to leave the code editor.
+
+### particleCount
+
+Number of confetti particles to create.
+
+**type:** `number`
+
+**Default value:** 150
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ particleCount: 200,
+});
+```
+
+### particleSize
+
+Size of the confetti particles in pixels
+
+**type:** `number`
+
+**Default value:** 12
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ particleSize: 20,
+});
+```
+
+### particleShape
+
+Shape of particles to use. Can be `mix`, `circles` or `rectangles`
+
+`mix` will use both circles and rectangles
+`circles` will use only circles
+`rectangles` will use only rectangles
+
+**type:** `'mix' | 'circles' | 'rectangles'`
+
+**Default value:** `'mix'`
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ particleShape: 'circles',
+});
+```
+
+### duration
+
+Duration of the animation in milliseconds
+
+**type:** `number`
+
+**Default value:** 3500
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ duration: 5000,
+});
+```
+
+### colors
+
+Colors to use for the confetti particles. Pass string array of colors. Can use hex colors, named colors, CSS Variables, literally anything valid in CSS.
+
+**type:** `Array`
+
+**Default value:** `['#FFC700', '#FF0000', '#2E3191', '#41BBC7']`
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ colors: ['var(--yellow)', 'var(--red)', '#2E3191', '#41BBC7'],
+});
+```
+
+### force
+
+Force of the confetti particles. Between 0 and 1. 0 is no force, 1 is maximum force. Will error out if you pass a value outside of this range.
+
+**type:** `number`
+
+**Default value:** 0.5
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ force: 0.3,
+});
+```
+
+### stageHeight
+
+Height of the stage in pixels. Confetti will only fall within this height.
+
+**type:** `number`
+
+**Default value:** 800
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ stageHeight: 500,
+});
+```
+
+### stageWidth
+
+Width of the stage in pixels. Confetti will only fall within this width.
+
+**type:** `number`
+
+**Default value:** 1600
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ stageWidth: 500,
+});
+```
+
+### destroyAfterDone
+
+Whether or not destroy all confetti nodes after the `duration` period has passed. By default it destroys all nodes, to free up memory.
+
+**type:** `boolean`
+
+**Default value:** `true`
+
+**Example:**
+
+```tsx
+new Confetti(document.querySelector('#confetti'), {
+ destroyAfterDone: false,
+});
+```
+
+
+
+## Fine-grained reactivity
+
+Changing the options will destroy the existing confetti mid-flight, and create a new one with the new options. Exception: If `particlesCount` isn't changed, and properties like `colors` or `particleShape` is changed, the confetti particles will change their colors or shape mid-flight.
+
+## Performance
+
+This library functions by creating 2 DOM nodes for every single confetti. By default, if the `particlesCount` is set to 150, it will create 300 nodes. This is a lot of nodes. For most devices, these many nodes are not a big issue, but I recommend checking your target devices' performance if you choose to go with a higher number, like 400 or 500.
+
+Also, after the specified `duration`, all the confetti DOM nodes will be destroyed. This is to free up memory. If you wish to keep them around, set `destroyAfterDone` to `false`.
+
+## License
+
+MIT License
+© [Puru Vijay](https://twitter.com/puruvjdev)
+
+## Credits
+
+This library is the port of the amazing [react-confetti-explosion](https://www.npmjs.com/package//react-confetti-explosion) package, just **10X** smaller and faster. All the logic is from that package only, optimisation and Svelte code are mine 😉
+
+# License
+
+MIT License © Puru Vijay
diff --git a/packages/vanilla/demo/.gitignore b/packages/vanilla/demo/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/packages/vanilla/demo/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/packages/vanilla/demo/favicon.svg b/packages/vanilla/demo/favicon.svg
new file mode 100644
index 0000000..de4aedd
--- /dev/null
+++ b/packages/vanilla/demo/favicon.svg
@@ -0,0 +1,15 @@
+
diff --git a/packages/vanilla/demo/index.html b/packages/vanilla/demo/index.html
new file mode 100644
index 0000000..254ebba
--- /dev/null
+++ b/packages/vanilla/demo/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/packages/vanilla/demo/package.json b/packages/vanilla/demo/package.json
new file mode 100644
index 0000000..13c29d9
--- /dev/null
+++ b/packages/vanilla/demo/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "demo",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "typescript": "^5.3.3",
+ "vite": "^5.0.10"
+ },
+ "dependencies": {
+ "@neoconfetti/vanilla": "workspace:*"
+ }
+}
diff --git a/packages/vanilla/demo/src/main.ts b/packages/vanilla/demo/src/main.ts
new file mode 100644
index 0000000..7ba111c
--- /dev/null
+++ b/packages/vanilla/demo/src/main.ts
@@ -0,0 +1,38 @@
+import './style.css';
+import { Confetti } from '@neoconfetti/vanilla';
+
+const button = document.querySelector('.button')!;
+const targetEl = document.querySelector('.target')!;
+
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+const confetti = new Confetti(targetEl);
+
+button.onclick = () => {
+ setTimeout(async () => {
+ console.log('Trigger confetti');
+
+ confetti.explode();
+
+ console.log('Changing colors now');
+ await sleep(1000);
+
+ confetti.options.colors = ['red', 'blue'];
+ confetti.options.particleShape = 'circles';
+ confetti.options.force = 1;
+
+ await sleep(600);
+ console.log('Changing again');
+
+ confetti.options = { particleCount: 100, duration: 7000, colors: ['red', 'blue'] };
+
+ await sleep(1000);
+ confetti.options.particleShape = 'circles';
+
+ await sleep(1000);
+ confetti.options.particleShape = 'rectangles';
+
+ await sleep(1000);
+ confetti.options.particleShape = 'mix';
+ }, 10);
+};
diff --git a/packages/vanilla/demo/src/style.css b/packages/vanilla/demo/src/style.css
new file mode 100644
index 0000000..852de7a
--- /dev/null
+++ b/packages/vanilla/demo/src/style.css
@@ -0,0 +1,8 @@
+#app {
+ font-family: Avenir, Helvetica, Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-align: center;
+ color: #2c3e50;
+ margin-top: 60px;
+}
diff --git a/packages/vanilla/demo/src/vite-env.d.ts b/packages/vanilla/demo/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/packages/vanilla/demo/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/vanilla/demo/tsconfig.json b/packages/vanilla/demo/tsconfig.json
new file mode 100644
index 0000000..fbd0225
--- /dev/null
+++ b/packages/vanilla/demo/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ESNext", "DOM"],
+ "moduleResolution": "Node",
+ "strict": true,
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/vanilla/package.json b/packages/vanilla/package.json
new file mode 100644
index 0000000..b4363e4
--- /dev/null
+++ b/packages/vanilla/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@neoconfetti/vanilla",
+ "version": "0.2.1",
+ "description": "Confetti explosion in JavaScript 🎉🎊",
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "type": "module",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist/*"
+ ],
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": {
+ "production": "./dist/min/index.js",
+ "development": "./dist/index.js"
+ },
+ "default": "./dist/min/index.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/PuruVJ/neoconfetti.git"
+ },
+ "keywords": [
+ "confetti",
+ "party",
+ "fun",
+ "badass",
+ "badassery",
+ "svelte",
+ "sveltekit",
+ "small",
+ "tiny",
+ "performant",
+ "react",
+ "nextjs",
+ "remix",
+ "vanilla",
+ "javascript",
+ "typescript"
+ ],
+ "author": "Puru Vijay",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/PuruVJ/neoconfetti/issues"
+ },
+ "homepage": "https://github.com/PuruVJ/neoconfetti/tree/main/packages/vanilla#readme",
+ "scripts": {
+ "compile": "tsup",
+ "compile:watch": "tsup --watch",
+ "size": "echo 'Vanilla size:' && brotli-size dist/min/index.js",
+ "pub": "pnpm compile && pnpm publish --no-git-checks --access public",
+ "pub:dry": "pnpm compile && pnpm publish --dry-run --no-git-checks --access public"
+ },
+ "devDependencies": {
+ "@neoconfetti/core": "workspace:*"
+ }
+}
diff --git a/packages/vanilla/src/index.ts b/packages/vanilla/src/index.ts
new file mode 100644
index 0000000..e66540b
--- /dev/null
+++ b/packages/vanilla/src/index.ts
@@ -0,0 +1,76 @@
+import {
+ DEFAULT_DURATION,
+ confetti,
+ type ConfettiOptions,
+ type ConfettiParticleShape,
+} from '@neoconfetti/core';
+
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+export class Confetti {
+ #node: HTMLElement;
+ #options: ConfettiOptions = {};
+
+ #instance?: ReturnType;
+
+ /**
+ * Initialize Confetti. Doesn't explode until you call `explode()`
+ */
+ constructor(node: HTMLElement, options: ConfettiOptions = {}) {
+ this.#node = node;
+ this.options = options; // Use the setter to initialize options
+ }
+
+ /**
+ * Options Proxy Creator
+ */
+ #create_proxy = (options: ConfettiOptions) => {
+ return new Proxy(options, {
+ set: (target, property, value) => {
+ Reflect.set(target, property, value);
+ this.#instance?.update(this.#options); // Update confetti instance when options change
+ return true;
+ },
+ });
+ };
+
+ /**
+ * options passed to confetti instance
+ */
+ get options() {
+ return JSON.parse(JSON.stringify(this.#options));
+ }
+
+ set options(value: ConfettiOptions) {
+ this.#options = this.#create_proxy(value); // Initialize options with a proxy
+ this.#instance?.update(this.#options); // Update confetti instance on setting new options
+ }
+
+ /**
+ * Explode confetti.
+ * @returns Promise that resolves after confetti duration
+ *
+ * @example
+ *
+ * ```ts
+ * const confetti = new Confetti(targetEl);
+ *
+ * console.time('confetti')
+ * await confetti.explode();
+ * console.time('confetti') // Will log ~3500ms
+ * ```
+ */
+ async explode() {
+ this.#instance = confetti(this.#node, this.#options);
+ return sleep(this.#options.duration ?? DEFAULT_DURATION);
+ }
+
+ /**
+ * Destroy confetti instance
+ */
+ destroy() {
+ this.#instance?.destroy();
+ }
+}
+
+export type { ConfettiOptions, ConfettiParticleShape };
diff --git a/packages/vanilla/tsconfig.json b/packages/vanilla/tsconfig.json
new file mode 100644
index 0000000..0ca3c46
--- /dev/null
+++ b/packages/vanilla/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "declarationDir": "./dist",
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "lib": ["DOM", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": false,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "preserveSymlinks": false
+ },
+ "include": ["./src"]
+}
diff --git a/packages/vanilla/tsup.config.ts b/packages/vanilla/tsup.config.ts
new file mode 100644
index 0000000..20c24eb
--- /dev/null
+++ b/packages/vanilla/tsup.config.ts
@@ -0,0 +1,3 @@
+import { coreConfig } from '../config';
+
+export default coreConfig;
diff --git a/packages/vue/CHANGELOG.md b/packages/vue/CHANGELOG.md
new file mode 100644
index 0000000..8374166
--- /dev/null
+++ b/packages/vue/CHANGELOG.md
@@ -0,0 +1,19 @@
+# @neoconfetti/vue
+
+## 2.2.1
+
+### Patch Changes
+
+- 51f551a: Fix particleShape rectangles
+
+## 2.2.0
+
+### Minor Changes
+
+- 4eb52f1: Add particleClass property, optimize code
+
+## 2.1.0
+
+### Minor Changes
+
+- 01a3b51: Fixes, setting any option to undefined reverts it to default value, more fine grained reactivity. If the particlesCount doesn't change, confetti isn't recreated.
diff --git a/packages/vue/README.md b/packages/vue/README.md
new file mode 100644
index 0000000..81fb298
--- /dev/null
+++ b/packages/vue/README.md
@@ -0,0 +1,204 @@
+# @neoconfetti/vue
+
+Let's party 🎊🎊 with Vue! `@neoconfetti/vue` allows you to show an awesome confetti explosion on your page, with Vue!
+
+## Features
+
+- 🤏 Tiny - 1.56KB min+br.
+- 🐇 Simple - Quite simple to use, and effectively no-config required!
+- 🧙♀️ Elegant - Vue directive `v-confetti` rather than setting things up in `mounted()` hook.
+- 🗃️ Customizable - Offers tons of options that you can modify to get different behaviors.
+- 🖥️ SSR friendly - Works seamlessly in NuxtJS, VitePress and other Server Side Rendering environments!
+
+
+
+## Installing
+
+```bash
+# pnpm
+pnpm add @neoconfetti/vue
+
+# npm
+npm install @neoconfetti/vue
+
+# yarn
+yarn add @neoconfetti/vue
+```
+
+## Usage
+
+Basic usage:
+
+```vue
+
+
+
+```
+
+Customizing behavior with options:
+
+```vue
+
+```
+
+## Props
+
+There's tons of options available for this package. All of them are already documented within the code itself, so you'll never have to leave the code editor.
+
+### particleCount
+
+Number of confetti particles to create.
+
+**type:** `number`
+
+**Default value:** 150
+
+**Example:**
+
+```vue
+
+```
+
+### particleSize
+
+Size of the confetti particles in pixels
+
+**type:** `number`
+
+**Default value:** 12
+
+**Example:**
+
+```vue
+
+```
+
+### particleShape
+
+Shape of particles to use. Can be `mix`, `circles` or `rectangles`
+
+`mix` will use both circles and rectangles
+`circles` will use only circles
+`rectangles` will use only rectangles
+
+**type:** `'mix' | 'circles' | 'rectangles'`
+
+**Default value:** `'mix'`
+
+**Example:**
+
+```vue
+
+```
+
+### duration
+
+Duration of the animation in milliseconds
+
+**type:** `number`
+
+**Default value:** 3500
+
+**Example:**
+
+```vue
+
+```
+
+### colors
+
+Colors to use for the confetti particles. Pass string array of colors. Can use hex colors, named colors, CSS Variables, literally anything valid in plain CSS.
+
+**type:** `Array`
+
+**Default value:** `['#FFC700', '#FF0000', '#2E3191', '#41BBC7']`
+
+**Example:**
+
+```vue
+
+```
+
+### force
+
+Force of the confetti particles. Between 0 and 1. 0 is no force, 1 is maximum force. Will error out if you pass a value outside of this range.
+
+**type:** `number`
+
+**Default value:** 0.5
+
+**Example:**
+
+```vue
+
+```
+
+### stageHeight
+
+Height of the stage in pixels. Confetti will only fall within this height.
+
+**type:** `number`
+
+**Default value:** 800
+
+**Example:**
+
+```vue
+
+```
+
+### stageWidth
+
+Width of the stage in pixels. Confetti will only fall within this width.
+
+**type:** `number`
+
+**Default value:** 1600
+
+**Example:**
+
+```vue
+
+```
+
+### destroyAfterDone
+
+Whether or not destroy all confetti nodes after the `duration` period has passed. By default it destroys all nodes, to free up memory.
+
+**type:** `boolean`
+
+**Default value:** `true`
+
+**Example:**
+
+```vue
+
+```
+
+
+
+## Fine-grained reactivity
+
+Changing the options will destroy the existing confetti mid-flight, and create a new one with the new options. Exception: If `particlesCount` isn't changed, and properties like `colors` or `particleShape` is changed, the confetti particles will change their colors or shape mid-flight.
+
+## Performance
+
+This library functions by creating 2 DOM nodes for every single confetti. By default, if the `particlesCount` is set to 150, it will create 300 nodes. This is a lot of nodes. For most devices, these many nodes are not a big issue, but I recommend checking your target devices' performance if you choose to go with a higher number, like 400 or 500.
+
+Also, after the specified `duration`, all the confetti DOM nodes will be destroyed. This is to free up memory. If you wish to keep them around, set `destroyAfterDone` to `false`.
+
+## Credits
+
+This library is the port of the amazing [react-confetti-explosion](https://www.npmjs.com/package//react-confetti-explosion) package, just **10X** smaller and faster. All the logic is from that package only, optimisation and Svelte code are mine 😉
+
+## License
+
+MIT License
+© [Puru Vijay](https://twitter.com/puruvjdev)
diff --git a/packages/vue/demo/.gitignore b/packages/vue/demo/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/packages/vue/demo/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/packages/vue/demo/CHANGELOG.md b/packages/vue/demo/CHANGELOG.md
new file mode 100644
index 0000000..2179d7e
--- /dev/null
+++ b/packages/vue/demo/CHANGELOG.md
@@ -0,0 +1,22 @@
+# demo
+
+## 0.0.9
+
+### Patch Changes
+
+- Updated dependencies [51f551a]
+ - @neoconfetti/vue@2.2.1
+
+## 0.0.8
+
+### Patch Changes
+
+- Updated dependencies [4eb52f1]
+ - @neoconfetti/vue@2.2.0
+
+## 0.0.7
+
+### Patch Changes
+
+- Updated dependencies [01a3b51]
+ - @neoconfetti/vue@2.1.0
diff --git a/packages/vue/demo/README.md b/packages/vue/demo/README.md
new file mode 100644
index 0000000..f5342b7
--- /dev/null
+++ b/packages/vue/demo/README.md
@@ -0,0 +1,11 @@
+# Vue 3 + Typescript + Vite
+
+This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 `
+
+
+
+
+