# Custom Events
taga11y dispatches six custom events on the original input element. All events **bubble**, are **not cancelable**, and carry a `detail` payload.

## Event List

### `taga11y:add`
Fired when a tag is successfully added (via user interaction or the `addTag` / `addTags` API).
**Detail:**
```ts
{ tag: TagData }
```
**Example — via `.on()`:**
```js
widget.on('taga11y:add', (e) => {
  console.log('Added:', e.detail.tag.label, e.detail.tag.value);
});
```
**Example — via `addEventListener`:**
```js
inputEl.addEventListener('taga11y:add', (e) => {
  console.log('Added:', e.detail.tag);
});
```

### `taga11y:remove`
Fired when a tag is removed (via user interaction or the `removeTag` API).
**Detail:**
```ts
{ tag: TagData }
```
**Example:**
```js
widget.on('taga11y:remove', (e) => {
  console.log('Removed:', e.detail.tag.label);
});
```

### `taga11y:clear`
Fired when all tags are cleared (via user interaction or the `clearTags` API).
**Detail:**
```ts
{ tags: TagData[] }
```
The `tags` array contains all tags that were removed.
**Example:**
```js
widget.on('taga11y:clear', (e) => {
  console.log('Cleared', e.detail.tags.length, 'tags');
  e.detail.tags.forEach(t => console.log('  -', t.label));
});
```

### `taga11y:change`
Fired after any tag mutation (add, remove, clear, batch add/replacement). The `tags` array reflects the **current** state after the mutation.
**Detail:**
```ts
{ tags: TagData[] }
```
**Example:**
```js
widget.on('taga11y:change', (e) => {
  console.log('Current tags:', e.detail.tags.map(t => t.label));
});
```

### `taga11y:destroy`
Fired when the instance is destroyed via `widget.destroy()`.
**Detail:**
```ts
{}
```
**Example:**
```js
widget.on('taga11y:destroy', () => {
  console.log('Widget destroyed, original input restored');
});
```

### `taga11y:paste`
Fired once after a paste gesture that contained at least one configured single-character delimiter (e.g. `,`). The pasted text is split into chunks, each non-empty trimmed chunk is attempted as a tag, and the result is reported in aggregate. **Not** fired for paste of text containing no delimiter (which uses native browser paste behaviour and lands in the input as editable text).

In whitelist mode (`enforceSuggestions: true`), chunks that do not resolve to a suggestion label (case-insensitive) are silently skipped — no error chip is shown, no per-item rejection event fires. Use this event to render your own bulk-import feedback ("3 of 5 added").

Per-chunk `taga11y:add` events still fire for each successfully committed tag, followed by a single `taga11y:change` event, followed by `taga11y:paste`.

**Detail:**
```ts
{ added: TagData[], skipped: string[] }
```
- `added` — the tags committed (in commit order)
- `skipped` — chunk strings that were rejected (not in suggestion list in whitelist mode, or already selected, or hit `maxTags`)

**Example:**
```js
widget.on('taga11y:paste', (e) => {
  const { added, skipped } = e.detail;
  if (skipped.length > 0) {
    showToast(`${added.length} added · ${skipped.length} not in list: ${skipped.join(', ')}`);
  }
});
```

## Event Timing
| Event | When fired |
|---|---|
| `taga11y:add` | After a single tag is added successfully |
| `taga11y:add` | After each tag added via `addTags()` (one per tag) |
| `taga11y:add` | After each tag committed during a delimited paste |
| `taga11y:remove` | After a tag is removed |
| `taga11y:clear` | After all tags are cleared; also fired by `setTags()` before adding new tags |
| `taga11y:change` | After any mutation; reflects final state |
| `taga11y:change` | Fired once after `addTags()` (after all additions) |
| `taga11y:change` | Fired once after `setTags()` (after all additions) |
| `taga11y:change` | Fired once after a delimited paste (after all per-chunk `taga11y:add` events) |
| `taga11y:paste` | Fired once after a delimited paste, after `taga11y:change` |
| `taga11y:destroy` | After DOM cleanup and listener detachment |
**Note:** No events are fired when a tag addition is rejected via typing (duplicate, max reached, or whitelist violation). Paste rejections are reported aggregated in `taga11y:paste.detail.skipped` instead of per-item rejection events.

## Removing Event Listeners
Use `.off()` to remove a previously registered handler:
```js
function handleAdd(e) {
  console.log('Added:', e.detail.tag);
}
widget.on('taga11y:add', handleAdd);
// ... later
widget.off('taga11y:add', handleAdd);
```
The same handler can be registered on both the widget instance and the raw input element — they are independent listeners on the same element.
