# Input Number

Input Number - input idea for Shop systems or so.

[![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/input-number) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/input-number.html)

## Contents

- [Overview](#overview)
- [Installation](#installation)
- [Basic usage](#basic-usage)
- [Configuration Options](#configuration-options)
- [JavaScript API](#javascript-api)
- [Events](#events)
- [Common Patterns](#common-patterns)
- [License](#license)

## Overview

The Input Number component provides a numeric input field with increment and decrement buttons. It's ideal for quantity selectors, cart systems, or any interface requiring numeric input with step controls.

**Key Features:**
- Numeric input with step controls
- Increment/decrement buttons
- Min/max value constraints
- Custom step values
- Programmatic control via JavaScript API
- Event system for value tracking
- Disabled state support

## Installation

To get started, install Input Number plugin via npm, else you can skip this step if you are already using Preline UI as a package.

```bash
npm i @preline/input-number
```

### CSS

Use [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.

```css
@import "tailwindcss";

/* @preline/input-number */
@source "../node_modules/@preline/input-number/*.js";
@import "./node_modules/@preline/input-number/variants.css";
@import "./node_modules/@preline/input-number/theme.css";
```

### JavaScript

Include the JavaScript that powers the interactive elements near the end of your `</body>` tag:

```html
<script src="./node_modules/@preline/input-number/index.js"></script>
```

### Manual Initialization

Use the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.

```html
<script type="module">
  import HSInputNumber from "@preline/input-number/non-auto.mjs";

  new HSInputNumber(document.querySelector("#input-number"));
</script>
```

### Via Bundler

When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.

`@preline/input-number` is the auto-init entry: it scans the DOM and initializes matching elements automatically.

```js
import "@preline/input-number";
```

`@preline/input-number/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.

```js
import HSInputNumber from "@preline/input-number/non-auto";

HSInputNumber.autoInit();

// Or initialize a specific element manually
const el = document.querySelector("#input-number");
if (el) new HSInputNumber(el);
```

### TypeScript

This package ships with TypeScript type definitions. No additional `@types/` package is needed.

## Basic usage

The following example demonstrates the minimal HTML structure required for an input number component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The component includes an input field and increment/decrement buttons.

```html
<div data-hs-input-number>
  <input type="text" aria-roledescription="Number field" value="1" data-hs-input-number-input>
  <button type="button" class="" tabindex="-1" aria-label="Decrease" data-hs-input-number-decrement>
    Minus
  </button>
  <button type="button" class="" tabindex="-1" aria-label="Increase" data-hs-input-number-increment>
    Add
  </button>
</div>
```

**Structure Requirements:**
- `data-hs-input-number`: Required on the container element
- `data-hs-input-number-input`: Required on the input element
- `data-hs-input-number-increment`: Required on the increment button
- `data-hs-input-number-decrement`: Required on the decrement button
- Proper ARIA attributes (`aria-roledescription`, `aria-label`)

**Initial State:**
- Input field contains the initial value (e.g., `value="1"`)
- Buttons are enabled/disabled based on min/max constraints

## Configuration Options

### Data Options

Data options are specified in the `data-hs-input-number` attribute.

| Attribute | Target Element | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `data-hs-input-number` | Container | - | - | Activate an Input Number by specifying on an element. Should be added to the container. |
| `:min` | Inside `data-hs-input-number` | number \| `"-Infinity"` | `0` | Defines the minimum possible value. `-Infinity` allows you to enter negative values without restrictions. |
| `:max` | Inside `data-hs-input-number` | number \| null | `null` | Defines the maximum possible value. `null` means no maximum limit. |
| `:step` | Inside `data-hs-input-number` | number | `1` | Determines the step by which the value will increase or decrease when buttons are clicked. |
| `:forceBlankValue` | Inside `data-hs-input-number` | boolean | `false` | Whether the input value should be blank when the value isn't set. When `true`, empty input shows blank instead of default value. |

**Example:**
```html
<div data-hs-input-number='{
  "min": 0,
  "max": 100,
  "step": 5,
  "forceBlankValue": false
}'>
  <!-- Input and buttons -->
</div>
```

### Required CSS Classes / Data Attributes

These data attributes define the structure and must be present for the input number to function.

| Data Attribute | Required On | Purpose |
| --- | --- | --- |
| `data-hs-input-number-input` | Input element | Identifies the numeric input field |
| `data-hs-input-number-increment` | Button element | Identifies the increment button |
| `data-hs-input-number-decrement` | Button element | Identifies the decrement button |

### Tailwind Modifiers

| Name | Description |
| --- | --- |
| `hs-input-number-disabled:*` | A modifier that allows you to set Tailwind classes when input's value is set to zero (or minimum value). |

## JavaScript API

The `HSInputNumber` object is available in the global `window` object after the plugin is loaded.

### Instance Methods

These methods are called on an input number instance.

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `destroy()` | None | `void` | Destroys the input number instance, removes all generated markup, classes, and event listeners. Use when removing input number from DOM. |

### Static Methods

These methods are called directly on the `HSInputNumber` class.

| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `HSInputNumber.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSInputNumber \| { id: number, element: HSInputNumber } \| null` | Returns the input number instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSInputNumber` instance. If `isInstance` is `false` or omitted, returns the `HSInputNumber` instance directly. Returns `null` if input number instance is not found. |

### Usage Examples

**Example 1: Destroying input number instance**
```javascript
const instance = HSInputNumber.getInstance('#hs-input-number', true);

if (instance) {
  const { element } = instance;
  const destroyBtn = document.querySelector('#hs-destroy-btn');

  destroyBtn.addEventListener('click', () => {
    element.destroy();
  });
}
```

**Example 2: Getting instance and accessing properties**
```javascript
// Get the input number instance
const instance = HSInputNumber.getInstance('#hs-input-number', true);

if (instance) {
  const { element } = instance;

  // Access instance properties
  console.log('Current value:', element.inputValue);
  console.log('Min value:', element.minInputValue);
  console.log('Max value:', element.maxInputValue);
  console.log('Step:', element.step);

  // Clean up when removing from DOM
  function removeInputNumber() {
    element.destroy();
  }
}
```

**Example 3: Destroying input number instance**
```javascript
const instance = HSInputNumber.getInstance('#hs-input-number', true);

if (instance) {
  const { element } = instance;
  const removeBtn = document.querySelector('#hs-remove-btn');

  removeBtn.addEventListener('click', () => {
    // Clean up before removing from DOM
    element.destroy();
    // Now safe to remove the element
    document.querySelector('#hs-input-number').remove();
  });
}
```

## Events

Input number instances emit events that can be listened to for value tracking and custom behavior.

| Event Name | When Fired | Callback Parameter | Description |
| --- | --- | --- | --- |
| `on:change` | When input value was changed | `{ inputValue: number }` | Fires when the input value changes (via buttons or direct input). Returns an object with the `inputValue` containing the current numeric value. |

### Event Usage Example

```javascript
// Get input number instance
const instance = HSInputNumber.getInstance('#hs-input-number', true);

if (instance) {
  const { element } = instance;

  // Listen to change event
  element.on('change', ({ inputValue }) => {
    console.log('Input value changed:', inputValue);
    // Perform actions after value changes
    // e.g., update cart, calculate totals, validate
  });
}
```

## Common Patterns

### Pattern 1: Quantity Selector

Create a quantity selector for shopping carts.

```html
<div data-hs-input-number='{
  "min": 1,
  "max": 99,
  "step": 1
}'>
  <input type="text" value="1" data-hs-input-number-input>
  <button data-hs-input-number-decrement>-</button>
  <button data-hs-input-number-increment>+</button>
</div>
```

### Pattern 2: Negative Values Allowed

Allow negative values by setting min to -Infinity.

```html
<div data-hs-input-number='{
  "min": "-Infinity",
  "max": null,
  "step": 1
}'>
  <!-- Input and buttons -->
</div>
```

### Pattern 3: Handling Value Changes

Track value changes with event listener.

```html
<div id="hs-input-number-first" data-hs-input-number>
  <input type="text" value="1" data-hs-input-number-input>
  <button data-hs-input-number-decrement>-</button>
  <button data-hs-input-number-increment>+</button>
</div>

<script>
  const instance = HSInputNumber.getInstance('#hs-input-number-first', true);
  
  if (instance) {
    const { element } = instance;

    element.on('change', ({ inputValue }) => {
      console.log('Quantity:', inputValue);
      // Update cart or perform other actions
    });
  }
</script>
```

## License

Copyright (c) 2024 Htmlstream.

Licensed under the [MIT License](https://opensource.org/licenses/MIT).
