# `hb-input-number`

Integrator guide for the **`hb-input-number`** custom element: a Bulma-styled native **`<input type="number">`** driven by a single **`schemaentry`** object (JSON on the host). The control reports its value with **`setVal`** and optionally handles **Enter** with **`clickEnter`** when no native **`min`** / **`max`** constraints are configured.

---

## What it does

- Renders **nothing** until **`schemaentry`** parses to a valid object; then it shows a Bulma **`field`** / **`control`** / **`input.input`**.
- Keeps an internal numeric **`value`** in sync with **`schemaentry`** when the schema fingerprint changes (local typing does not overwrite the schema on every keystroke unless the parent updates **`schemaentry`**).
- Computes **`valid`**: if the field is **not** **`required`**, **`valid`** is always **`true`**. If **`required`**, **`valid`** is **`false`** when **`value`** is **`undefined`** or **`null`**; when **`value`** is set, optional **`params.min`** / **`params.max`** (only if the corresponding key exists) are applied as lower/upper bounds (see [Bounds (`params`)](#bounds-params)).
- When **`show_validation`** is **`"yes"`** and the field is **`required`**, the input gets Bulma **`is-success`** / **`is-danger`** and an optional **`validationTip`** is shown as **`help is-danger`** (exposed as a CSS **`::part`**).
- **`is_small="yes"`** maps to **`input.is-small`**. For **`type="number"`** + **`is-small`**, spin buttons are hidden in shadow styles so the control height aligns with compact selects (values remain editable via keyboard).

---

## Custom element

| Name | Tag |
|------|-----|
| `hb-input-number` | `<hb-input-number …></hb-input-number>` |

---

## Host API (attributes / properties)

Web component **reflected** surface is **string-oriented** in HTML: pass booleans as **`"yes"`** / **`"no"`**, and pass **`schemaentry`** as a **JSON string**. In Svelte or other frameworks you may also assign the **`schemaentry`** property as an **object** (the parser accepts both; see `parseSchemaentryProp` in `../lib/schemaentry`).

| Name | Type (logical) | HTML / notes |
|------|----------------|--------------|
| **`schemaentry`** | `FormSchemaEntry \| undefined` | **JSON string** on the attribute. Required for UI; invalid JSON yields no field. |
| **`show_validation`** | `"yes" \| "no"` | Default **`"no"`**. When **`"yes"`** and **`schemaentry.required`**, shows success/danger styling and invalid **`validationTip`**. |
| **`is_small`** | `"yes" \| "no"` | Default **`"no"`**. When **`"yes"`**, applies **`input.is-small`**. |
| **`id`** | `string` | Optional host id (component typings). |
| **`style`** | `string` | Optional inline host style (component typings). |

---

## `schemaentry` shape

Authoritative TypeScript lives in **`types/webcomponent.type.d.ts`** (`FormSchemaEntry`, **`InputNumberParams`**, **`Component`**, **`Events`**). The entry extends the shared form row shape (see **`../../form/types/webcomponent.type`**) with number-specific pieces:

### Shared fields (subset used here)

| Field | Purpose |
|-------|---------|
| **`id`** | Field id; used in event payloads and on the **`<input>`** element. |
| **`label`** | Optional; not rendered as separate markup in this component (schema is mainly for id, validation, placeholders). |
| **`value`** | Optional **number** initial / synced value from schema. |
| **`required`** | When truthy, **`valid`** is false if **`value`** is **`undefined`** / **`null`**, and range rules apply when **`params`** keys exist. |
| **`placeholder`** | Passed to the input. |
| **`readonly`** | Passed to the input. |
| **`disabled`** | Disables the input when truthy. |
| **`validationTip`** | Shown as **`p.help.is-danger`** when **`show_validation="yes"`** and the value is invalid. |

Other **`FormSchemaEntryShared`** fields (e.g. **`type`**, **`dependencies`**, **`validationRegex`**) exist on the shared type; this component focuses on number input behavior above.

### Bounds (`params`)

**`InputNumberParams`** (optional **`schemaentry.params`**):

```ts
type InputNumberParams = {
  min?: number;
  max?: number;
};
```

**Important:** a native DOM **`min`** or **`max`** attribute is set **only if** that key **exists** on **`params`** (checked with **`Object.prototype.hasOwnProperty.call`**). Omit a key entirely if you do not want that attribute.

- When **`min`** / **`max`** keys are present, they participate in validation for **`required`** fields and are reflected on the input.
- **Enter key:** **`clickEnter`** is wired **only** when **both** **`min`** and **`max`** keys are absent from **`params`** (no native min/max mode). Otherwise the handler is not attached.

---

## Events

Listen with **`addEventListener`** or framework equivalents. Payloads match **`types/webcomponent.type.d.ts`** (`Events`).

| Event | `detail` | When |
|-------|----------|------|
| **`setVal`** | `{ value: number; valid: boolean; id: string }` | Emitted when **`schemaentry.id`** is set and **`value`**, **`valid`**, or **`id`** changes compared to the last payload (deduplicated). **`value`** may be **`undefined`** when empty. |
| **`clickEnter`** | `{ value: string; valid: boolean; id?: string }` | **Enter** inside the input, **only** if **`params`** defines **neither** **`min`** nor **`max`**. **`preventDefault`** is called on the key event. |

---

## Styling

Bulma **1.x** form styles are bundled in the shadow root; theme tokens are driven by **`--bulma-*`** on **`:host`** (see [Bulma CSS variables](https://bulma.io/documentation/features/css-variables/)).

### CSS custom properties (`styleSetup`)

| Variable | Role |
|----------|------|
| **`--bulma-text`** | Label, input value, and help text. |
| **`--bulma-border`** | Default input outline before success/danger. |
| **`--bulma-danger`** | Invalid (**`is-danger`**) border and feedback. |
| **`--bulma-success`** | Valid (**`is-success`**) accents when validation feedback is shown. |
| **`--bulma-scheme-main`** | Control surface / scheme background where Bulma maps scheme to controls. |

### CSS parts

| Part | Role |
|------|------|
| **`invalid-feedback`** | The **`p.help.is-danger`** node for **`validationTip`** when **`show_validation="yes"`** and the value is invalid. Style from the light DOM with **`::part(invalid-feedback)`**. |

### Slots

None. Configuration is entirely through **`schemaentry`** JSON.

---

## HTML example (vanilla)

```html
<hb-input-number
  schemaentry='{"id":"quantity","label":"Quantity","required":true,"validationTip":"Enter a number between 3 and 10.","placeholder":"0","params":{"min":3,"max":10},"value":4}'
  show_validation="yes"
></hb-input-number>

<script>
  const el = document.querySelector("hb-input-number");
  el.addEventListener("setVal", (e) => {
    console.log(e.detail); // { value, valid, id }
  });
</script>
```

**Compact height** (e.g. next to pagination or dense toolbars):

```html
<hb-input-number
  is_small="yes"
  schemaentry='{"id":"pageSize","label":"Rows","value":10}'
></hb-input-number>
```

**Enter to submit** (no **`min`** / **`max`** keys on **`params`**):

```html
<hb-input-number
  schemaentry='{"id":"code","required":true,"placeholder":"Numeric code"}'
></hb-input-number>
<script>
  document.querySelector("hb-input-number").addEventListener("clickEnter", (e) => {
    console.log("Enter pressed", e.detail);
  });
</script>
```

---

## TypeScript imports

For authoring against the published shape in this package:

```ts
import type {
  Component,
  Events,
  FormSchemaEntry,
  InputNumberParams,
} from "./types/webcomponent.type";
```

Use **`Component`** for props and **`Events`** for **`CustomEvent`** detail typing.

---

## Metadata and Storybook

**`extra/docs.ts`** defines **`styleSetup`** (vars, parts), **`componentSetup.description`**, **`storybookArgs`**, and **`examples`** (required, min/max combinations, zero values, disabled). Use those examples as additional configuration references alongside this file.
