npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

drop-spinner

v0.0.14

Published

Random picker with drop animation — a spinner alternative.

Readme

drop-spinner

drop-spinner es una librería ligera para seleccionar elementos al azar con una interfaz visual opcional. Está pensada para ser usada como componente React, como utilidad programática y (opcionalmente) como Web Component. El paquete está preparado para publicarse en npm y es compatible con entornos SSR como Next.js.

Contenido del README:

  • Instalación
  • Uso rápido (React)
  • Uso en Next.js (SSR)
  • Uso programático
  • API
  • Build y publicación
  • Ejemplo local

Instalación

npm install drop-spinner

Nota: el paquete debe declarar react y react-dom como peerDependencies. Asegúrate de instalarlos en tu proyecto consumidor.


Uso rápido — Componente React

import { RandomImagePicker, ImageOption } from 'drop-spinner';

const items: ImageOption[] = [
  { name: 'Apple', url: '/img/apple.png' },
  { name: 'Banana', url: '/img/banana.png' },
  { name: 'Cherry' }
];

export default function Page() {
  return <RandomImagePicker items={items} />;
}

Uso adicional: CircularWheel

El paquete también incluye un componente visual tipo rueda/slot llamado CircularWheel pensado para mostrar imágenes alrededor de una rueda o en un viewport de 'slot'. Usa el mismo tipo ImageOption.

Ejemplo de uso:

import { CircularWheel, ImageOption } from 'drop-spinner';

const images: ImageOption[] = [
  { name: 'Burger', url: 'https://images.unsplash.com/photo-1568901346375-23c9450c58cd' },
  { name: 'Pizza', url: 'https://images.unsplash.com/photo-1544982503-9f984c14501a' },
  { name: 'Coffee', url: 'https://images.unsplash.com/photo-1533776992670-a72f4c28235e' },
  { name: 'Donut', url: 'https://images.unsplash.com/photo-1733754348967-62a6870f51b7' }
];

export default function Page() {
  const handleSpin = (winnerIndex: number, winnerName: string) => {
    console.log('Ganador:', winnerIndex, winnerName);
  };

  return <CircularWheel images={images} size={360} onSpin={handleSpin} />;
}

Notas:

  • images acepta ImageOption[] donde ImageOption = { name: string; url?: string }.
  • onSpin se invoca cuando la animación termina con (winnerIndex, winnerName).

El componente es sencillo: recibe items: ImageOption[] y muestra el elemento seleccionado (o un texto si no hay imagen). ImageOption es { name: string; url?: string }.


Uso en Next.js / SSR

Si usas características que dependen del DOM (registro de Web Components o utilidades que montan nodos), registra el componente solo en el cliente:

import { useEffect } from 'react';
import { registerDropSpinner, dropSpinner } from 'drop-spinner';

export default function Page() {
  useEffect(() => {
    if (typeof registerDropSpinner === 'function') registerDropSpinner();
  }, []);

  async function handlePick() {
    const result = await dropSpinner({ items: ['A','B','C'] });
    alert('Picked: ' + result);
  }

  return <button onClick={handlePick}>Pick</button>;
}

Registra el Web Component (si existe) dentro de useEffect para evitar fallos en SSR (no existe window en el servidor).

Uso recomendado: wrapper server + componente cliente

El paquete exporta dos variantes:

  • RandomImagePicker — server-safe placeholder (se puede importar desde componentes server sin causar errores de hidratación).
  • RandomImagePickerClient — componente con toda la interactividad; debe importarse desde un Client Component.

Ejemplo (page.tsx — Server Component):

import { RandomImagePicker } from 'drop-spinner';

export default function Page() {
  const items = [{ name: 'Apple' }, { name: 'Banana' }, { name: 'Cherry' }];
  return <RandomImagePicker items={items} />; // server-safe
}

Ejemplo (PickerClient.tsx — Client Component):

'use client';
import { RandomImagePickerClient } from 'drop-spinner';

export default function PickerClient({ items }) {
  return <RandomImagePickerClient items={items} />;
}

Si quieres montar dinámicamente el componente cliente desde un componente server puedes usar next/dynamic:

import dynamic from 'next/dynamic';
const DynamicPicker = dynamic(() => import('drop-spinner').then(m => m.RandomImagePickerClient), { ssr: false });

export default function Page() {
  const items = [{ name: 'Apple' }, { name: 'Banana' }];
  return <DynamicPicker items={items} />;
}

Uso programático

La utilidad dropSpinner(options) permite invocar la selección programáticamente. En cliente retorna una Promise que se resuelve tras la animación; en entornos sin DOM puede comportarse de forma síncrona.

import { dropSpinner } from 'drop-spinner';

const winner = await dropSpinner({
  items: ['🍎','🍌','🍇'],
  animationSpeed: 1200,
  circleSize: 200,
  onPick: (item) => console.log('picked', item)
});

API (resumen)

  • RandomImagePicker — componente React. Props:

    • items: ImageOption[]
  • getRandomItem(items: ImageOption[]): ImageOption | undefined — devuelve un item aleatorio o undefined si la lista está vacía.

  • dropSpinner(options: DropSpinnerOptions) — función útil para lanzar la selección con animación.

  • registerDropSpinner() — registra el Web Component en el cliente (si el paquete lo exporta).

Tipos importantes:

  • ImageOption = { name: string; url?: string }
  • DropSpinnerOptions = { items: string[] | ImageOption[]; animationSpeed?: number; circleSize?: number; onPick?: (item: any) => void }

Build y publicación

El proyecto usa tsup para generar bundles ESM/CJS y tipados.

Antes de publicar:

  1. Asegúrate de que package.json tiene los campos name, version, main, module, types y exports correctamente configurados.
  2. Ejecuta:
npm run build
  1. Comprueba el contenido del paquete:
npm pack --dry-run
  1. Publica en npm:
npm publish --access public

Recomendación: mantener react/react-dom en peerDependencies y @types/react en devDependencies.


Ejemplo local (Next.js)

El repositorio incluye un ejemplo minimal en example/next-app. Para ejecutarlo localmente:

cd example/next-app
npm install
npm run dev

El ejemplo está configurado para usar el paquete local empaquetado (tarball) como dependencia de desarrollo.


Limpieza y publicación segura

  • Añade .npmignore para excluir código fuente y archivos de desarrollo del paquete publicado.
  • Añade .gitignore para evitar subir node_modules, artefactos de build y tarballs.

Contribuciones

PRs y issues son bienvenidos. Incluye tests y actualiza README.md si cambias la API.


Licencia

MIT