bootstrap-touchspin
Version:
A mobile and touch friendly input spinner component for Bootstrap 3, 4 & 5. jQuery plugin with IIFE/UMD builds for backward compatibility.
346 lines (264 loc) • 12.5 kB
Markdown
# TouchSpin v5
> **Formerly Bootstrap TouchSpin** — Now a modern ESM-first monorepo
[](https://github.com/sponsors/istvan-ujjmeszaros)
TouchSpin (formerly **Bootstrap TouchSpin**) is a modern rewrite of the popular spinner component. The v5 line ships as an ESM-first monorepo with framework-specific packages so you can pick the delivery mode that fits your stack—core logic, renderer bundles, a jQuery bridge, or a Web Component.
## 💖 Support This Project
**TouchSpin v5 represents nearly 1,000 hours of development work** — a complete ground-up rewrite to bring modern ESM architecture, tree-shaking, and multi-framework support to this popular component.
**[⭐ Become a Sponsor](https://github.com/sponsors/istvan-ujjmeszaros)** to help sustain ongoing development, faster bug fixes, and new features.
Your sponsorship keeps this project:
- ✅ **Free and open-source** for everyone
- 🚀 **Actively maintained** with regular updates
- 🐛 **Well-tested** with comprehensive test coverage
- 📚 **Well-documented** with migration guides
*Every contribution, no matter the size, makes a real difference!*
---
## Packages at a Glance
| Package | Purpose | Primary Entry | Bundled Assets |
|---------|---------|---------------|----------------|
| `@touchspin/core` | Framework-agnostic logic + renderer contracts | `dist/index.js` (ESM) | Declarations only |
| `@touchspin/standalone` | Standalone mount API (core + renderer) | `dist/index.js` (ESM) | Per-renderer subpaths (`dist/bootstrapX.js`) |
| `@touchspin/jquery` | Drop-in jQuery wrapper | `dist/index.js` (ESM) | `dist/umd/jquery.touchspin-*.umd.js` globals |
| `@touchspin/webcomponent` | `<touchspin-input>` custom element | Per-renderer subpaths | `dist/umd/*.touchspin.umd.js` globals |
| `@touchspin/renderer-bootstrap3` | Bootstrap 3 renderer + CSS | `dist/index.js` (ESM) | `dist/touchspin-bootstrap3.css` |
| `@touchspin/renderer-bootstrap4` | Bootstrap 4 renderer + CSS | `dist/index.js` (ESM) | `dist/touchspin-bootstrap4.css` |
| `@touchspin/renderer-bootstrap5` | Bootstrap 5 renderer + CSS | `dist/index.js` (ESM) | `dist/touchspin-bootstrap5.css` |
| `@touchspin/renderer-tailwind` | Tailwind-friendly renderer | `dist/index.js` (ESM) | `dist/touchspin-tailwind.css` |
| `@touchspin/renderer-vanilla` | Framework-free renderer + theme | `dist/index.js` (ESM) | `dist/touchspin-vanilla.css`, `dist/themes/vanilla.css` |
All packages declare `"type": "module"`, target Node 22 (the configuration used for builds), and include licenses in the published tarballs. Renderer packages list their CSS under `files` and expose the stylesheet via `exports."./css"`.
## Framework Adapters (Separate Repositories)
Framework-specific adapters are maintained in separate repositories with native tooling:
- **[@touchspin/react](https://github.com/istvan-ujjmeszaros/touchspin-react)** (v5.0.1-alpha.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-react
- Installation: `npm install @touchspin/react@alpha @touchspin/core@alpha`
- Per-renderer subpath imports with controlled/uncontrolled patterns
- **[@touchspin/angular](https://github.com/istvan-ujjmeszaros/touchspin-angular)** (v5.0.1-alpha.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-angular
- Installation: `npm install @touchspin/angular@alpha @touchspin/core@alpha`
- ControlValueAccessor integration with Angular forms
- **[@touchspin/vue](https://github.com/istvan-ujjmeszaros/touchspin-vue)** (v5.1.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-vue
- Installation: `npm install @touchspin/vue @touchspin/core`
- Per-renderer subpath imports with v-model support
- **[@touchspin/svelte](https://github.com/istvan-ujjmeszaros/touchspin-svelte)** (v5.0.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-svelte
- Installation: `npm install @touchspin/svelte @touchspin/core`
- Svelte 5 Runes support with bind:value directive
These adapters are independently versioned to match core compatibility. React and Angular are currently in alpha while Vue and Svelte are stable.
## Quick Install
### Standalone Adapter (Recommended)
The simplest way to use TouchSpin with a mount API:
```bash
npm install @touchspin/standalone
```
```ts
import { mount } from '@touchspin/standalone/bootstrap5';
const api = mount('#quantity', {
min: 0,
max: 100,
step: 1,
cancelable: true // Enable cancelable change events
});
// Listen to events
const input = document.querySelector('#quantity');
input.addEventListener('change:start', (event) => {
// Prevent change if needed
if (someCondition) {
event.preventDefault();
}
});
input.addEventListener('speedchange', (event) => {
console.log('Speed changed to:', event.detail.speed);
});
```
**Browser via CDN (ESM)**:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@touchspin/renderer-bootstrap5@5/dist/touchspin-bootstrap5.css">
<script type="module">
import { mount } from 'https://cdn.jsdelivr.net/npm/@touchspin/standalone@5/dist/bootstrap5.js';
mount('#quantity', { min: 0, max: 100 });
</script>
```
### Modern (ESM) projects
For advanced use with direct core access:
```bash
npm install @touchspin/core @touchspin/renderer-bootstrap5
```
```ts
import { TouchSpin } from '@touchspin/core';
import Bootstrap5Renderer from '@touchspin/renderer-bootstrap5';
import '@touchspin/renderer-bootstrap5/css';
const input = document.querySelector('#quantity');
TouchSpin(input, {
renderer: Bootstrap5Renderer,
min: 0,
max: 100,
step: 1,
cancelable: true // Enable cancelable change events
});
// Listen to events
input.addEventListener('change:start', (event) => {
// Can prevent the change
event.preventDefault();
});
```
### jQuery integration
```bash
npm install @touchspin/jquery jquery
```
**UMD (Browser)**:
```html
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@touchspin/jquery@5/dist/umd/jquery.touchspin-bootstrap5.umd.js"></script>
<script>
// Canonical (recommended)
$('#quantity').touchspin({ min: 0, max: 100 });
// Legacy alias (still supported)
$('#quantity').TouchSpin({ min: 0, max: 100 });
</script>
```
**ESM**:
```ts
import { autoInstall } from '@touchspin/jquery';
import { mount } from '@touchspin/standalone/bootstrap5';
import $ from 'jquery';
autoInstall(mount);
$('#quantity').touchspin({ min: 0, max: 100 });
```
### Web Component
```bash
npm install @touchspin/webcomponent
```
```ts
import '@touchspin/webcomponent/bootstrap5';
```
```html
<touchspin-input min="0" max="100" value="42"></touchspin-input>
```
### React
```bash
npm install @touchspin/react react react-dom
```
**Controlled:**
```tsx
import { useState } from 'react';
import TouchSpin from '@touchspin/react/bootstrap5';
function App() {
const [value, setValue] = useState(50);
return <TouchSpin value={value} onChange={setValue} min={0} max={100} />;
}
```
**Uncontrolled:**
```tsx
import TouchSpin from '@touchspin/react/vanilla';
<TouchSpin defaultValue={25} onChange={(val) => console.log(val)} />
```
**Imperative API:**
```tsx
import { useRef } from 'react';
import TouchSpin from '@touchspin/react/tailwind';
import type { TouchSpinHandle } from '@touchspin/react/tailwind';
const ref = useRef<TouchSpinHandle>(null);
<TouchSpin ref={ref} defaultValue={10} />
ref.current?.increment();
```
**Per-renderer imports:** `bootstrap3`, `bootstrap4`, `bootstrap5`, `tailwind`, `vanilla`
**SSR-safe:** Works with Next.js, Remix, and other React frameworks
**Example app:** [touchspin-react-example](https://github.com/istvan-ujjmeszaros/touchspin-react-example)
See the [@touchspin/react repository](https://github.com/istvan-ujjmeszaros/touchspin-react) for complete API documentation.
### Angular
```bash
npm install @touchspin/angular @angular/core @angular/common @angular/forms
```
**Template-driven forms:**
```typescript
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TouchSpinBootstrap5Component } from '@touchspin/angular/bootstrap5';
@Component({
selector: 'app-example',
standalone: true,
imports: [FormsModule, TouchSpinBootstrap5Component],
template: `
<touch-spin
[(ngModel)]="quantity"
[min]="0"
[max]="100"
[step]="1"
></touch-spin>
`
})
export class ExampleComponent {
quantity = 50;
}
```
**Reactive forms:**
```typescript
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { TouchSpinBootstrap5Component } from '@touchspin/angular/bootstrap5';
@Component({
selector: 'app-example',
standalone: true,
imports: [ReactiveFormsModule, TouchSpinBootstrap5Component],
template: `<touch-spin [formControl]="amountControl"></touch-spin>`
})
export class ExampleComponent {
amountControl = new FormControl(50);
}
```
**Imperative API:**
```typescript
import { Component, ViewChild } from '@angular/core';
import { TouchSpinBootstrap5Component, TouchSpinHandle } from '@touchspin/angular/bootstrap5';
@Component({
selector: 'app-example',
standalone: true,
imports: [TouchSpinBootstrap5Component],
template: `<touch-spin #spinner [(ngModel)]="value"></touch-spin>`
})
export class ExampleComponent {
@ViewChild('spinner') spinner?: TouchSpinHandle;
value = 0;
increment() {
this.spinner?.increment();
}
}
```
**Per-renderer imports:** `bootstrap3`, `bootstrap4`, `bootstrap5`, `tailwind`, `vanilla`
**ControlValueAccessor:** Full integration with Angular forms
**SSR-safe:** Compatible with Angular Universal
See the [@touchspin/angular repository](https://github.com/istvan-ujjmeszaros/touchspin-angular) for complete API documentation.
## CDN Builds
All CDN-facing entry points are catalogued in [`docs/cdn-assets.md`](./docs/cdn-assets.md). Remember that most packages are ESM-only; only the jQuery and Web Component adapters surface UMD globals.
For native ESM in the browser, supply an import map:
```html
<script type="importmap">
{
"imports": {
"@touchspin/core": "https://cdn.jsdelivr.net/npm/@touchspin/core@5/dist/index.js",
"@touchspin/renderer-bootstrap5": "https://cdn.jsdelivr.net/npm/@touchspin/renderer-bootstrap5@5/dist/index.js"
}
}
</script>
```
## Renderer Selection
- **Bootstrap projects**: pick the renderer matching your Bootstrap major and include Bootstrap + Popper according to Bootstrap’s own peer dependencies.
- **Headless/vanilla apps**: use `@touchspin/renderer-vanilla` for a lightweight, framework-free theme.
- **Utility-first CSS**: `@touchspin/renderer-tailwind` ships a Tailwind-flavoured stylesheet you can scope or customize.
All renderers attach `data-touchspin-injected` attributes so the core can wire events without relying on framework-specific selectors.
## Release Channels & Dist-Tags
| Tag | Intended audience | Notes |
|-----|-------------------|-------|
| `alpha` | packaging in flux | Current default while we stabilize exports and docs. |
| `beta` | freeze candidate | Set once packaging + docs are finalized. |
| `next` | rolling canary | Use for CI smoke tests and early adopters. |
| `latest` | production | Published only after graduating from beta. |
Publishing is orchestrated through Changesets and the GitHub Actions workflow in `.github/workflows/release.yml`. See [docs/releasing.md](docs/releasing.md) for the release playbook, dist-tag promotion policy, and provenance requirements.
## Migration from v4
Moving from the legacy `bootstrap-touchspin@4.x` package? Start with the concise [MIGRATION.md](MIGRATION.md) and consult deeper architectural notes in `docs/architecture/migration-guide.md` if you need step-by-step coverage of custom renderers or event differences.
## Contributing & Support
- Start with the workspace overview in [`AGENTS.md`](AGENTS.md) for cross-repo structure and expectations.
- Read [CONTRIBUTING.md](CONTRIBUTING.md) for workspace guidelines, required build steps, and Changesets usage.
- Packaging or security concerns? Follow the disclosure process in [SECURITY.md](SECURITY.md).
- Dev server, scripts, and architecture references remain in the `/docs` tree for contributors—see [docs/index.md](docs/index.md) for a full sitemap.
TouchSpin is MIT-licensed. Every published package carries its own `LICENSE` file generated from the project’s root license.