{"id":32195056,"url":"https://github.com/istvan-ujjmeszaros/touchspin-angular","last_synced_at":"2026-01-17T10:49:04.906Z","repository":{"id":318780791,"uuid":"1076491725","full_name":"istvan-ujjmeszaros/touchspin-angular","owner":"istvan-ujjmeszaros","description":null,"archived":false,"fork":false,"pushed_at":"2025-10-24T04:09:55.000Z","size":491,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-26T21:10:21.226Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/istvan-ujjmeszaros.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-15T00:06:59.000Z","updated_at":"2025-10-24T04:09:59.000Z","dependencies_parsed_at":"2025-10-15T19:00:31.788Z","dependency_job_id":"09f64f2d-0775-4980-aaf5-2a7e812eb3f8","html_url":"https://github.com/istvan-ujjmeszaros/touchspin-angular","commit_stats":null,"previous_names":["istvan-ujjmeszaros/touchspin-angular"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/istvan-ujjmeszaros/touchspin-angular","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/istvan-ujjmeszaros%2Ftouchspin-angular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/istvan-ujjmeszaros%2Ftouchspin-angular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/istvan-ujjmeszaros%2Ftouchspin-angular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/istvan-ujjmeszaros%2Ftouchspin-angular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/istvan-ujjmeszaros","download_url":"https://codeload.github.com/istvan-ujjmeszaros/touchspin-angular/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/istvan-ujjmeszaros%2Ftouchspin-angular/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28506593,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T10:25:30.148Z","status":"ssl_error","status_checked_at":"2026-01-17T10:25:29.718Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-22T02:01:49.149Z","updated_at":"2026-01-17T10:49:04.899Z","avatar_url":"https://github.com/istvan-ujjmeszaros.png","language":"TypeScript","readme":"# TouchSpin Angular\n\nAngular adapter for TouchSpin numeric input spinner - Native Angular components with full framework integration.\n\n## Features\n\n- Native Angular components with ControlValueAccessor\n- Reactive and template-driven forms support\n- Per-renderer subpath imports (Bootstrap 3/4/5, Tailwind, Vanilla)\n- Standalone components (Angular 14+)\n- SSR/Angular Universal compatible\n- Full keyboard navigation and ARIA attributes\n- Comprehensive test coverage\n- Complete TouchSpin API support\n\n## Installation\n\n```bash\nnpm install @touchspin/angular @touchspin/core @touchspin/renderer-bootstrap5\n# or\nyarn add @touchspin/angular @touchspin/core @touchspin/renderer-bootstrap5\n# or\npnpm add @touchspin/angular @touchspin/core @touchspin/renderer-bootstrap5\n```\n\n## Quick Start\n\n```typescript\nimport { Component } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\nimport '@touchspin/renderer-vanilla/css';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [FormsModule, TouchSpinVanillaComponent],\n  template: `\n    \u003ctouch-spin\n      [(ngModel)]=\"value\"\n      [min]=\"0\"\n      [max]=\"100\"\n      [step]=\"1\"\n    \u003e\u003c/touch-spin\u003e\n    \u003cp\u003eValue: {{ value }}\u003c/p\u003e\n  `\n})\nexport class AppComponent {\n  value = 25;\n}\n```\n\n## Available Renderers\n\nChoose the renderer that matches your design system:\n\n| Renderer | Import | CSS Import | Description |\n|----------|--------|------------|-------------|\n| **Vanilla** | `@touchspin/angular/vanilla` | `@touchspin/renderer-vanilla/css` | Clean, framework-free styling |\n| **Bootstrap 5** | `@touchspin/angular/bootstrap5` | `@touchspin/renderer-bootstrap5/css` | Bootstrap 5 compatible |\n| **Bootstrap 4** | `@touchspin/angular/bootstrap4` | `@touchspin/renderer-bootstrap4/css` | Bootstrap 4 compatible |\n| **Bootstrap 3** | `@touchspin/angular/bootstrap3` | `@touchspin/renderer-bootstrap3/css` | Bootstrap 3 compatible |\n| **Tailwind** | `@touchspin/angular/tailwind` | `@touchspin/renderer-tailwind/css` | Tailwind CSS styling |\n\n## API Reference\n\n### Inputs (Properties)\n\n#### Value Management\n```typescript\n\u003cTouchSpin\n  [(ngModel)]=\"controlledValue\"        // Template-driven forms\n  [value]=\"controlledValue\"            // Reactive forms\n  [defaultValue]=\"initialValue\"        // Uncontrolled mode\n  (valueChange)=\"onValueChange($event)\" // Value change event\n/\u003e\n```\n\n#### Configuration\n```typescript\n\u003cTouchSpin\n  [min]=\"number\"                       // Minimum value\n  [max]=\"number\"                       // Maximum value\n  [step]=\"number\"                      // Increment/decrement step\n  [decimals]=\"number\"                  // Decimal places\n  [prefix]=\"'string'\"                  // Text before input\n  [suffix]=\"'string'\"                  // Text after input\n/\u003e\n```\n\n#### State \u0026 Behavior\n```typescript\n\u003cTouchSpin\n  [disabled]=\"boolean\"                 // Disable input and buttons\n  [readOnly]=\"boolean\"                 // Make input read-only\n/\u003e\n```\n\n#### Form Integration\n```typescript\n\u003cTouchSpin\n  [name]=\"'fieldName'\"                 // Form field name\n  [id]=\"'fieldId'\"                     // Input element ID\n/\u003e\n```\n\n#### Styling\n```typescript\n\u003cTouchSpin\n  [class]=\"'custom-class'\"             // Wrapper CSS class\n  [inputClass]=\"'input-class'\"         // Input CSS class\n/\u003e\n```\n\n#### Events\n```typescript\n\u003cTouchSpin\n  (blur)=\"onBlur()\"                   // Input blur event\n  (focus)=\"onFocus()\"                 // Input focus event\n\n  // TouchSpin Events\n  (onMin)=\"onMin()\"                   // Fired at minimum boundary\n  (onMax)=\"onMax()\"                   // Fired at maximum boundary\n  (onStartSpin)=\"onStartSpin()\"       // Fired when spinning starts\n  (onStopSpin)=\"onStopSpin()\"         // Fired when spinning stops\n  (onStartUpSpin)=\"onStartUpSpin()\"   // Fired when upward spinning starts\n  (onStartDownSpin)=\"onStartDownSpin()\" // Fired when downward spinning starts\n  (onStopUpSpin)=\"onStopUpSpin()\"     // Fired when upward spinning stops\n  (onStopDownSpin)=\"onStopDownSpin()\" // Fired when downward spinning stops\n  (onSpeedChange)=\"onSpeedChange()\"   // Fired when spin speed increases\n/\u003e\n```\n\n### Imperative API (ViewChild)\n\n```typescript\nimport { Component, ViewChild } from '@angular/core';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-example',\n  template: `\n    \u003ctouch-spin #touchSpinRef [defaultValue]=\"50\"\u003e\u003c/touch-spin\u003e\n    \u003cbutton (click)=\"increment()\"\u003e+1\u003c/button\u003e\n  `\n})\nexport class ExampleComponent {\n  @ViewChild('touchSpinRef') touchSpin!: TouchSpinVanillaComponent;\n\n  increment() {\n    this.touchSpin.increment();\n  }\n}\n```\n\n#### TouchSpinHandle Methods\n\n```typescript\ninterface TouchSpinHandle {\n  // Focus Management\n  focus(): void;                    // Focus the input\n  blur(): void;                     // Blur the input\n\n  // Value Control\n  increment(): void;                // Increment by step\n  decrement(): void;                // Decrement by step\n  getValue(): number;               // Get current value\n  setValue(value: number): void;    // Set new value\n\n  // Continuous Spinning\n  startUpSpin(): void;              // Start continuous upward spinning\n  startDownSpin(): void;            // Start continuous downward spinning\n  stopSpin(): void;                 // Stop any continuous spinning\n\n  // Configuration\n  updateSettings(opts: Partial\u003cTouchSpinCoreOptions\u003e): void;\n                                  // Update settings at runtime\n}\n```\n\n## Usage Examples\n\n### Basic Controlled Component\n\n```typescript\nimport { Component } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-basic',\n  standalone: true,\n  imports: [FormsModule, TouchSpinVanillaComponent],\n  template: `\n    \u003cdiv\u003e\n      \u003ctouch-spin\n        [(ngModel)]=\"value\"\n        [min]=\"0\"\n        [max]=\"100\"\n        [step]=\"5\"\n      \u003e\u003c/touch-spin\u003e\n      \u003cp\u003eValue: {{ value }}\u003c/p\u003e\n    \u003c/div\u003e\n  `\n})\nexport class BasicComponent {\n  value = 25;\n}\n```\n\n### With Prefix/Suffix\n\n```typescript\nimport { Component } from '@angular/core';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-currency',\n  standalone: true,\n  imports: [TouchSpinVanillaComponent],\n  template: `\n    \u003ctouch-spin\n      [(ngModel)]=\"price\"\n      [min]=\"0\"\n      [max]=\"1000\"\n      [step]=\"0.01\"\n      [decimals]=\"2\"\n      [prefix]=\"'$'\"\n      [suffix]=\"' USD'\"\n    \u003e\u003c/touch-spin\u003e\n  `\n})\nexport class CurrencyComponent {\n  price = 29.99;\n}\n```\n\n### Event Handling\n\n```typescript\nimport { Component } from '@angular/core';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-events',\n  standalone: true,\n  imports: [TouchSpinVanillaComponent],\n  template: `\n    \u003cdiv\u003e\n      \u003ctouch-spin\n        [defaultValue]=\"50\"\n        [min]=\"0\"\n        [max]=\"100\"\n        (onMin)=\"addEvent('Reached minimum')\"\n        (onMax)=\"addEvent('Reached maximum')\"\n        (onStartSpin)=\"addEvent('Spin started')\"\n        (onStopSpin)=\"addEvent('Spin stopped')\"\n      \u003e\u003c/touch-spin\u003e\n\n      \u003ch3\u003eEvent Log:\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli *ngFor=\"let event of events; trackBy: trackByIndex\"\u003e{{ event }}\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  `\n})\nexport class EventsComponent {\n  events: string[] = [];\n\n  addEvent(message: string) {\n    this.events.unshift(`${new Date().toLocaleTimeString()}: ${message}`);\n  }\n\n  trackByIndex(index: number) {\n    return index;\n  }\n}\n```\n\n### Imperative Control\n\n```typescript\nimport { Component, ViewChild } from '@angular/core';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-imperative',\n  standalone: true,\n  imports: [TouchSpinVanillaComponent],\n  template: `\n    \u003cdiv\u003e\n      \u003cdiv\u003e\n        \u003cbutton (click)=\"setValue42()\"\u003eSet to 42\u003c/button\u003e\n        \u003cbutton (click)=\"startSpinning()\"\u003eStart Spinning Up\u003c/button\u003e\n        \u003cbutton (click)=\"stopSpinning()\"\u003eStop Spinning\u003c/button\u003e\n        \u003cbutton (click)=\"showValue()\"\u003eShow Current Value\u003c/button\u003e\n      \u003c/div\u003e\n\n      \u003ctouch-spin\n        #touchSpin\n        [defaultValue]=\"25\"\n        [min]=\"0\"\n        [max]=\"100\"\n      \u003e\u003c/touch-spin\u003e\n\n      \u003cp\u003eCurrent value: {{ currentValue }}\u003c/p\u003e\n    \u003c/div\u003e\n  `\n})\nexport class ImperativeComponent {\n  @ViewChild('touchSpin') touchSpin!: TouchSpinVanillaComponent;\n  currentValue = 0;\n\n  setValue42() {\n    this.touchSpin.setValue(42);\n  }\n\n  startSpinning() {\n    this.touchSpin.startUpSpin();\n  }\n\n  stopSpinning() {\n    this.touchSpin.stopSpin();\n  }\n\n  showValue() {\n    this.currentValue = this.touchSpin.getValue();\n  }\n}\n```\n\n### Reactive Forms\n\n```typescript\nimport { Component } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, FormGroup } from '@angular/forms';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-reactive',\n  standalone: true,\n  imports: [ReactiveFormsModule, TouchSpinVanillaComponent],\n  template: `\n    \u003cform [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\"\u003e\n      \u003clabel\u003e\n        Quantity:\n        \u003ctouch-spin\n          formControlName=\"quantity\"\n          [min]=\"1\"\n          [max]=\"99\"\n        \u003e\u003c/touch-spin\u003e\n      \u003c/label\u003e\n      \u003cbutton type=\"submit\"\u003eAdd to Cart\u003c/button\u003e\n    \u003c/form\u003e\n  `\n})\nexport class ReactiveComponent {\n  form: FormGroup;\n\n  constructor(private fb: FormBuilder) {\n    this.form = this.fb.group({\n      quantity: [1]\n    });\n  }\n\n  onSubmit() {\n    console.log('Quantity:', this.form.value.quantity);\n  }\n}\n```\n\n## Advanced Configuration\n\n### Custom Core Options\n\n```typescript\nimport { Component } from '@angular/core';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\n@Component({\n  selector: 'app-advanced',\n  standalone: true,\n  imports: [TouchSpinVanillaComponent],\n  template: `\n    \u003ctouch-spin\n      [defaultValue]=\"50\"\n      [min]=\"0\"\n      [max]=\"100\"\n      [step]=\"1\"\n      [coreOptions]=\"{\n        verticalbuttons: true,\n        buttonup_class: 'custom-up',\n        buttondown_class: 'custom-down'\n      }\"\n    \u003e\u003c/touch-spin\u003e\n  `\n})\nexport class AdvancedComponent {}\n```\n\n## Testing\n\n```typescript\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { FormsModule } from '@angular/forms';\nimport { TouchSpinVanillaComponent } from '@touchspin/angular/vanilla';\n\ndescribe('TouchSpinComponent', () =\u003e {\n  let component: TestComponent;\n  let fixture: ComponentFixture\u003cTestComponent\u003e;\n\n  beforeEach(async () =\u003e {\n    await TestBed.configureTestingModule({\n      imports: [FormsModule, TouchSpinVanillaComponent, TestComponent]\n    }).compileComponents();\n\n    fixture = TestBed.createComponent(TestComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should increment value', () =\u003e {\n    const touchSpin = fixture.nativeElement.querySelector('touch-spin');\n    const incrementBtn = touchSpin.querySelector('button:last-child');\n\n    incrementBtn.click();\n    fixture.detectChanges();\n\n    expect(component.value).toBe(6);\n  });\n});\n\n@Component({\n  selector: 'test-component',\n  template: '\u003ctouch-spin [(ngModel)]=\"value\" [defaultValue]=\"5\"\u003e\u003c/touch-spin\u003e'\n})\nclass TestComponent {\n  value = 5;\n}\n```\n\n## Development\n\n```bash\n# Install dependencies\nyarn install\n\n# Build all packages\nyarn build\n\n# Run tests\nyarn test\n\n# Run tests with coverage\nyarn test:coverage\n\n# Run tests in watch mode\nyarn test:watch\n\n# Type checking\nyarn typecheck\n\n# Linting\nyarn lint\n```\n\n## Related Packages\n\n### Core\n- **@touchspin/core** - Core TouchSpin logic and API\n\n### Renderers\n- **@touchspin/renderer-vanilla** - Vanilla CSS renderer\n- **@touchspin/renderer-bootstrap3** - Bootstrap 3 renderer\n- **@touchspin/renderer-bootstrap4** - Bootstrap 4 renderer\n- **@touchspin/renderer-bootstrap5** - Bootstrap 5 renderer\n- **@touchspin/renderer-tailwind** - Tailwind CSS renderer\n\n### Adapters\n- **@touchspin/angular** - Angular adapter (this package)\n- **@touchspin/react** - React adapter\n- **@touchspin/jquery** - jQuery plugin\n- **@touchspin/webcomponent** - Web Components\n- **@touchspin/standalone** - Standalone bundle\n\n## Contributing\n\nContributions welcome! Please see the [main TouchSpin repository](https://github.com/istvan-ujjmeszaros/touchspin) for [contribution guidelines](https://github.com/istvan-ujjmeszaros/touchspin/blob/main/CONTRIBUTING.md).\n\n## License\n\nMIT © Istvan Ujj-Meszaros","funding_links":[],"categories":["Third Party Components"],"sub_categories":["Form Controls"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fistvan-ujjmeszaros%2Ftouchspin-angular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fistvan-ujjmeszaros%2Ftouchspin-angular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fistvan-ujjmeszaros%2Ftouchspin-angular/lists"}