# xpl-input-phone

The Phone Input Field allows users to input properly formatted phone numbers. It supports two modes: **international** (country selector + dial code prefix) and **domestic** (single country, no selector). Used as a subcomponent of `xpl-input` when `type="phone"`. The value is always emitted in **E.164** format (e.g. `+14155551234`).

## Usage

### International mode (default)

Use via the parent component for full international support with a country selector:

```html
<xpl-input type="phone" label="Phone number" name="phone"></xpl-input>
```

With preferred countries pinned to the top of the list:

```html
<xpl-input
  type="phone"
  label="Phone number"
  name="phone"
  preferred-countries='["US","CA","GB"]'
></xpl-input>
```

### Domestic mode

Set `is-international="false"` to lock the input to a single country. The country selector trigger and dropdown are hidden, and no dial code is shown in the input. The emitted value is still E.164.

```html
<xpl-input
  type="phone"
  label="Phone number"
  name="phone"
  default-country="US"
  is-international="false"
></xpl-input>
```

### Pre-populating a value

Pass an E.164 string to `value`. The component parses it into the correct country and national number:

```html
<xpl-input type="phone" label="Phone" value="+14155551234"></xpl-input>
```

## Value format

- **value** / **valueChange**: E.164 string (e.g. `+14155551234`). You can set `value` to pre-fill; the component parses it into country and national number.
- **defaultCountry**: ISO 3166-1 alpha-2 code (e.g. `"US"`) used when value is empty. In domestic mode, this locks the country permanently.
- **preferredCountries**: Optional array of country codes to show at the top of the country list (international mode only).
- **isInternational**: When `true` (default), the full international UI is shown. When `false`, the component is a simple domestic phone input locked to `defaultCountry`.

## Design and behaviour

### Country selector (international mode)

- The **trigger** displays a country flag icon (`xpl-icon`) and a chevron-down icon.
- Clicking the trigger opens an `xpl-dropdown` panel containing a **type-ahead search** field and a scrollable list of countries. Each option shows its flag icon, name, and dial code.
- Search filters by country name, dial code, or ISO country code.
- When a country is selected, focus returns to the phone input and the cursor is positioned after the dial code.
- Countries sharing a dial code (e.g. US and Canada both use `+1`) are handled correctly: changing the country in the dropdown preserves the selection even when the dial code is the same.

### Domestic mode

No country selector is rendered. The input behaves as a standard phone field with country-specific formatting and validation based on `defaultCountry`. The dial code is not displayed in the input, but the emitted value is still E.164.

### Input handling

- **Sanitization**: Non-numeric characters are automatically stripped on every keystroke, paste, or autofill. The input only accepts digits.
- **Max length**: Enforced per country based on the national number format (e.g. 10 digits for US/CA, 9 for AU/FR).
- **Cursor protection** (international mode): The dial code prefix (e.g. `+1 `) is read-only within the input. Users cannot backspace into it, click before it, or use Home/ArrowLeft to move the cursor before it.
- **Clear button**: A clear (`x-circle`) button appears when there is input and the field is not disabled or readonly.

### Phone formatting

The national number is formatted as the user types (e.g. US: `415 555 1234`, FR: `6 12 34 56 78`). The stored and emitted value remains E.164. Cursor position is intelligently preserved during formatting so it does not jump.

Countries with custom formatters: US/CA, GB, AU, DE/AT/CH, FR, BR, MX, JP, CN, IN, KR. All other countries use a generic grouping format.

### Validation and errors

- When `required` is set, the component emits a `hasErrorStateChanged` event with `"required"` if the national number is empty on change.
- The parent `xpl-input` can pass an `error` prop for external error messages (e.g. server-side validation).
- The component applies `xpl-input-phone--error` to the host when an internal error state is active.

### Accessibility

- The country selector trigger has `aria-haspopup="listbox"` and `aria-expanded`.
- The country selector trigger has `aria-label="Select country"` for screen-reader clarity.
- The dropdown panel has `aria-label="Country"`.
- The clear button has `aria-label="Clear phone number"`.
- The input uses `inputmode="numeric"` to hint a numeric keyboard on mobile and `autocomplete="tel-national"` (international) or `autocomplete="tel"` (domestic).
- The input sets `aria-invalid="true"` when in an error state (e.g. required field left empty).
- Decorative icons (flag, chevron) use `aria-hidden="true"` to avoid screen reader noise.

## Supported countries

28 countries are currently supported. See `phone-country-data.ts` for the full list. Countries with custom number formatting are noted above. To add a new country, add an entry to the `PHONE_COUNTRIES` array and optionally add a formatter in `phone-format.ts`.

### Design Tokens

Tokens used by this component from `@xplortech/apollo-foundation`.

**Color**
- `--xpl-background-surface-subtlest` — trigger background
- `--xpl-background-surface-information` — trigger hover / open background
- `--xpl-border-negative` — error state border
- `--xpl-border-focus` — wrapper and trigger focus outline
- `--xpl-border-default` — trigger divider, search border
- `--xpl-text-default` — trigger text, dial code
- `--xpl-text-subdued` — clear button color
- `--xpl-icon-default` — search icon

**Border**
- `--xpl-border-small` — trigger divider width (1px)

**Focus**
- `--xpl-border-focus` — `:focus-visible` outline (inset, 2px)

<!-- Auto Generated Below -->


## Properties

| Property             | Attribute          | Description                                                                                                                                                                                                                    | Type       | Default     |
| -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ----------- |
| `defaultCountry`     | `default-country`  | Default country code (ISO 3166-1 alpha-2) when value is empty, e.g. "US".                                                                                                                                                      | `string`   | `'US'`      |
| `disabled`           | `disabled`         | Whether the field is disabled                                                                                                                                                                                                  | `boolean`  | `undefined` |
| `error`              | `error`            | Whether the field is in an error state. When true, the input renders with the error border styling. The error message itself is owned by the parent `xpl-input` wrapper.                                                       | `boolean`  | `undefined` |
| `inputId`            | `input-id`         | The `inputId` is used to associate the input with a label.                                                                                                                                                                     | `string`   | `undefined` |
| `isInternational`    | `is-international` | Enables international mode with a country selector and dial code prefix. When false, the input is locked to `defaultCountry` with no country picker and no dial code shown in the display value. Emitted value is still E.164. | `boolean`  | `true`      |
| `name`               | `name`             | Most inputs are used in forms, and should have a `name` associated with the input for handling form data.                                                                                                                      | `string`   | `undefined` |
| `placeholder`        | `placeholder`      | Placeholder text that appears when the field has no value                                                                                                                                                                      | `string`   | `undefined` |
| `preferredCountries` | --                 | Country codes to show at the top of the country list, e.g. ["US", "CA", "GB"].                                                                                                                                                 | `string[]` | `undefined` |
| `readonly`           | `readonly`         | Whether the input is editable                                                                                                                                                                                                  | `boolean`  | `undefined` |
| `required`           | `required`         | Whether the input is required                                                                                                                                                                                                  | `boolean`  | `undefined` |
| `value`              | `value`            | Including a `value` will pre-populate the input (E.164 format, e.g. +14155551234).                                                                                                                                             | `string`   | `undefined` |


## Events

| Event                  | Description                                                     | Type                  |
| ---------------------- | --------------------------------------------------------------- | --------------------- |
| `hasErrorStateChanged` | Event that is emitted when the hasErrorState state changes.     | `CustomEvent<string>` |
| `valueChange`          | Event that emits the current value of the input (E.164 format). | `CustomEvent<string>` |


## Dependencies

### Used by

 - [xpl-input](..)

### Depends on

- [xpl-icon](../../xpl-icon)
- [xpl-dropdown](../../xpl-dropdown)
- [xpl-input](..)

### Graph
```mermaid
graph TD;
  xpl-input-phone --> xpl-icon
  xpl-input-phone --> xpl-dropdown
  xpl-input-phone --> xpl-input
  xpl-dropdown --> xpl-dropdown-group
  xpl-dropdown --> xpl-dropdown-option
  xpl-dropdown-group --> xpl-dropdown-group
  xpl-dropdown-group --> xpl-dropdown-option
  xpl-dropdown-group --> xpl-dropdown-heading
  xpl-dropdown-option --> xpl-icon
  xpl-input --> xpl-input-phone
  xpl-input-file --> xpl-icon
  xpl-input-color --> xpl-popover
  xpl-input-color --> xpl-icon
  xpl-input-color --> xpl-input
  xpl-input-date --> xpl-icon
  xpl-input-time --> xpl-input
  xpl-input-time --> xpl-icon
  xpl-input-time --> xpl-dropdown
  xpl-input-search --> xpl-icon
  style xpl-input-phone fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
