# Tag

- Entry point: `@koobiq/components/tags`
- NgModule: `KbqTagsModule`
- Page: https://koobiq.io/en/components/tag/overview

## Overview

**Example: Tag** (`tag-overview`)

```ts tag-overview-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagsModule } from '@koobiq/components/tags';

@Component({
    selector: 'tag-overview-example',
    imports: [
        KbqTagsModule,
        KbqIconModule
    ],
    template: `
        <kbq-tag [color]="colors.ContrastFade">
            Tag
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-5xl layout-align-center-center layout-row'
    }
})
export class TagOverviewExample {
    colors = KbqComponentColors;
}
```

Tags are used within the [`Tag list`](https://koobiq.io/en/components/tag-list.md) component in input fields to represent selected values: [`Select Multiple`](https://koobiq.io/en/components/select.md#multiple-selection), [`Tag autocomplete`](https://koobiq.io/en/components/tag-autocomplete.md), [`Tag input`](https://koobiq.io/en/components/tag-input.md).

### Color

**Example: Tag fill and style** (`tag-fill-and-style`)

```ts tag-fill-and-style-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagsModule } from '@koobiq/components/tags';

@Component({
    selector: 'tag-fill-and-style-example',
    imports: [
        KbqTagsModule,
        KbqIconModule
    ],
    template: `
        <kbq-tag [color]="colors.ContrastFade">
            <i kbqTagPrefix kbq-icon="kbq-circle-check_16"></i>
            Contrast
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
        <kbq-tag [color]="colors.Theme">
            <i kbqTagPrefix kbq-icon="kbq-circle-check_16"></i>
            Theme
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
        <kbq-tag [color]="colors.Error">
            <i kbqTagPrefix kbq-icon="kbq-circle-check_16"></i>
            Error
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
        <kbq-tag [color]="colors.Warning">
            <i kbqTagPrefix kbq-icon="kbq-circle-check_16"></i>
            Warning
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-row layout-wrap layout-gap-l layout-margin-5xl layout-align-center-center'
    }
})
export class TagFillAndStyleExample {
    colors = KbqComponentColors;
}
```

### Disabled State

**Example: Tag Disabled** (`tag-disabled`)

```ts tag-disabled-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagsModule } from '@koobiq/components/tags';

@Component({
    selector: 'tag-disabled-example',
    imports: [
        KbqTagsModule,
        KbqIconModule
    ],
    template: `
        <kbq-tag [color]="colors.ContrastFade" [disabled]="true">
            Tag
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-5xl layout-align-center-center layout-row'
    }
})
export class TagDisabledExample {
    colors = KbqComponentColors;
}
```

### Icon

Use `kbqTagPrefix` and `kbqTagSuffix` to explicitly place content before or after the tag text. `kbqTagRemove` and `kbqTagEditSubmit` are suffix controls already; do not combine them with `kbqTagSuffix`, because it is supplied through their host directives.

**Example: Tag with icon** (`tag-with-icon`)

```ts tag-with-icon-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagsModule } from '@koobiq/components/tags';

@Component({
    selector: 'tag-with-icon-example',
    imports: [
        KbqTagsModule,
        KbqIconModule
    ],
    template: `
        <kbq-tag [color]="colors.ContrastFade">
            <i kbqTagPrefix kbq-icon="kbq-diamond-o_16"></i>
            Tag
            <i kbqTagSuffix kbq-icon="kbq-diamond_16"></i>
        </kbq-tag>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-5xl layout-align-center-center layout-row'
    }
})
export class TagWithIconExample {
    colors = KbqComponentColors;
}
```

### Long Text

Tag text does not wrap to a new line; instead, it is truncated with an ellipsis.

**Example: Tag Long Text** (`tag-long-text`)

```ts tag-long-text-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagsModule } from '@koobiq/components/tags';

@Component({
    selector: 'tag-long-text-example',
    imports: [
        KbqTagsModule,
        KbqIconModule
    ],
    template: `
        <kbq-tag [color]="colors.ContrastFade">
            Typically, tags are associated with the «Delete» button, allowing users to remove the selected value from
            the input field. However, there are instances when it is desirable to prevent the deletion of a tag, in
            which case the cross must be concealed.
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove></i>
        </kbq-tag>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-top-5xl layout-margin-bottom-5xl layout-margin-left-m layout-margin-right-m layout-align-center-center layout-row'
    }
})
export class TagLongTextExample {
    colors = KbqComponentColors;
}
```

### Removal

Tags can be removed in several ways:

- Click on the remove icon (`kbqTagRemove` directive) inside the tag.
- Press `Delete` or `Backspace` key.
- Programmatic removal through the component [API](https://koobiq.io/en/components/tag.md).

The removal option is configured using the `removable` attribute (enabled by default). A tag that cannot be removed, because `removable` is off or the tag is disabled, does not show its remove icon.

**Example: Tag removable** (`tag-removable`)

```ts tag-removable-example.ts
import { ChangeDetectionStrategy, Component, computed, model, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagEvent, KbqTagsModule } from '@koobiq/components/tags';
import { KbqToggleModule } from '@koobiq/components/toggle';

@Component({
    selector: 'tag-removable-example',
    imports: [KbqTagsModule, KbqIconModule, KbqToggleModule, FormsModule],
    template: `
        <kbq-toggle [(ngModel)]="removable">Removable</kbq-toggle>

        @if (display()) {
            <kbq-tag [removable]="removable()" (removed)="removed($event)">
                {{ tag() }}
                <i kbqTagRemove kbq-icon-button="kbq-xmark-s_16"></i>
            </kbq-tag>
        } @else {
            <i kbq-icon-button="kbq-arrow-rotate-left_16" [color]="colors.ContrastFade" (click)="restart()"></i>
        }
    `,
    styles: `
        :host {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: var(--kbq-size-m);
            min-height: var(--kbq-size-7xl);
            margin: var(--kbq-size-5xl);
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class TagRemovableExample {
    protected readonly colors = KbqComponentColors;
    protected readonly removable = model(true);
    protected readonly tag = computed(() => (this.removable() ? 'Removable tag' : 'Not removable tag'));
    protected readonly display = signal(true);

    protected removed(event: KbqTagEvent): void {
        console.log('Tag was removed:', event);

        this.display.set(false);
    }

    protected restart(): void {
        this.display.set(true);
    }
}
```

### Selection

Tags can be selected in several ways:

- Click on the tag or set focus on it.
- `Space` when the tag is focused.
- Programmatic selection through the component [API](https://koobiq.io/en/components/tag.md).

The selection option is configured using the `selectable` attribute (disabled by default).

**Example: Tag selectable** (`tag-selectable`)

```ts tag-selectable-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqTagSelectionChange, KbqTagsModule } from '@koobiq/components/tags';

@Component({
    selector: 'tag-selectable-example',
    imports: [KbqTagsModule],
    template: `
        <kbq-tag selectable (selectionChange)="onSelectionChange($event)">Selectable tag</kbq-tag>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        class: 'layout-margin-5xl layout-align-center-center layout-row'
    }
})
export class TagSelectableExample {
    protected onSelectionChange(event: KbqTagSelectionChange): void {
        console.log('Selection changed:', event);
    }
}
```

### Editing

To enable editing mode, set the `editable` property for `kbq-tag`.

Entering edit mode:

- On double-click on the tag.
- On pressing `Enter` or `F2` key (when the tag is focused).

Saving changes:

- On pressing `Enter` key.
- On clicking the confirmation button (`kbqTagEditSubmit` directive).

Canceling changes:

- On pressing `Escape` key.
- On losing focus.

**Example: Tag editable** (`tag-editable`)

```ts tag-editable-example.ts
import { ChangeDetectionStrategy, Component, model, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqInputModule } from '@koobiq/components/input';
import { KbqTagEditChange, KbqTagEvent, KbqTagsModule } from '@koobiq/components/tags';
import { KbqTitleModule } from '@koobiq/components/title';

const TAG = 'Editable tag';

@Component({
    selector: 'tag-editable-example',
    imports: [KbqTagsModule, KbqIconModule, FormsModule, KbqInputModule, KbqTitleModule],
    template: `
        @if (!isTagRemoved()) {
            <kbq-tag editable kbq-title [value]="tag()" (editChange)="editChange($event)" (removed)="removed($event)">
                {{ tag() }}
                <input kbqInput kbqTagEditInput [(ngModel)]="tag" />
                @if (tag().length > 0) {
                    <i kbq-icon-button="kbq-check-s_16" kbqTagEditSubmit [color]="color.Theme"></i>
                } @else {
                    <i kbq-icon-button="kbq-xmark-s_16" kbqTagEditSubmit [color]="color.Theme"></i>
                }
                <i kbq-icon-button="kbq-xmark-s_16" kbqTagRemove></i>
            </kbq-tag>
        } @else {
            <i kbq-icon-button="kbq-arrow-rotate-left_16" [color]="color.ContrastFade" (click)="restart()"></i>
        }
    `,
    styles: `
        :host {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: var(--kbq-size-m);
            min-height: var(--kbq-size-xxl);
            margin: var(--kbq-size-5xl);
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class TagEditableExample {
    protected readonly tag = model(TAG);
    protected readonly isTagRemoved = signal(false);
    protected readonly color = KbqComponentColors;

    protected editChange({ type, reason, tag }: KbqTagEditChange): void {
        switch (type) {
            case 'start': {
                console.info(`Tag edit was started. Reason: "${reason}".`);
                break;
            }
            case 'cancel': {
                console.info(`Tag edit was canceled. Reason: "${reason}".`);
                break;
            }
            case 'submit': {
                console.info(`Tag edit was submitted. Reason: "${reason}".`);

                if (!tag.value) tag.remove();

                break;
            }
            default:
        }
    }

    protected removed(event: KbqTagEvent): void {
        console.info('Tag was removed: ', event);

        this.isTagRemoved.set(true);
    }

    protected restart(): void {
        this.tag.set(TAG);
        this.isTagRemoved.set(false);
    }
}
```

#### Validation During Editing

Do not use tag editing when complex validation is expected. Editing is configured so that it's impossible to save a tag with an invalid value. To prevent saving, use the `preventEditSubmit` attribute.

**Example: Tag editable with validation** (`tag-editable-with-validation`)

```ts tag-editable-with-validation-example.ts
import { ChangeDetectionStrategy, Component, computed, model, signal, viewChild } from '@angular/core';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
import { KbqComponentColors, PopUpPlacements } from '@koobiq/components/core';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqTagEditChange, KbqTagEvent, KbqTagsModule } from '@koobiq/components/tags';
import { KbqTitleModule } from '@koobiq/components/title';
import { KbqToolTipModule, KbqTooltipTrigger } from '@koobiq/components/tooltip';
import { debounceTime, distinctUntilChanged } from 'rxjs';

const TAG = 'Editable tag with validation';

@Component({
    selector: 'tag-editable-with-validation-example',
    imports: [KbqTagsModule, KbqIconModule, FormsModule, KbqToolTipModule, KbqTitleModule],
    template: `
        @if (tagValue().length > 0) {
            <kbq-tag
                editable
                kbq-title
                [preventEditSubmit]="!isModelValid()"
                [color]="tagColor()"
                (editChange)="editChange($event)"
                (removed)="removed($event)"
            >
                {{ tagValue() }}
                <input
                    kbqTooltip="Maximum {{ initialMaxLength }} characters (actual: {{ trimmedTagModel().length }})"
                    kbqTagEditInput
                    kbqTrigger="none"
                    [kbqTooltipColor]="color.Error"
                    [(ngModel)]="tagModel"
                />
                @if (trimmedTagModel().length === 0) {
                    <i kbq-icon-button="kbq-xmark-s_16" kbqTagEditSubmit [color]="color.Theme"></i>
                } @else {
                    <i
                        kbq-icon-button="kbq-check-s_16"
                        kbqTagEditSubmit
                        [color]="color.Theme"
                        [disabled]="!isModelValid()"
                    ></i>
                }
                <i kbq-icon-button="kbq-xmark-s_16" kbqTagRemove></i>
            </kbq-tag>
        } @else {
            <i kbq-icon-button="kbq-arrow-rotate-left_16" [color]="color.ContrastFade" (click)="restart()"></i>
        }
    `,
    styles: `
        :host {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: var(--kbq-size-m);
            min-height: var(--kbq-size-xxl);
            margin: var(--kbq-size-5xl);
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class TagEditableWithValidationExample {
    private readonly tooltip = viewChild.required(KbqTooltipTrigger);
    protected readonly popUpPlacements = PopUpPlacements;
    protected readonly color = KbqComponentColors;
    protected readonly tagValue = signal(TAG);
    protected readonly initialMaxLength = this.tagValue().length;
    protected readonly tagModel = model(this.tagValue());
    protected readonly trimmedTagModel = computed(() => this.tagModel().trim());
    protected readonly isModelValid = computed(() => this.trimmedTagModel().length <= this.initialMaxLength);
    protected readonly tagColor = computed(() => (this.isModelValid() ? this.color.ContrastFade : this.color.Error));

    constructor() {
        toObservable(this.isModelValid)
            .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed())
            .subscribe((isModelValid) => {
                const tooltip = this.tooltip();

                if (tooltip.isOpen) tooltip.hide();
                if (!isModelValid) tooltip.show(0);
            });
    }

    protected editChange(event: KbqTagEditChange): void {
        switch (event.type) {
            case 'start': {
                this.start(event);
                break;
            }
            case 'cancel': {
                this.cancel(event);
                break;
            }
            case 'submit': {
                this.submit(event);
                break;
            }
            default:
        }
    }

    protected removed(event: KbqTagEvent): void {
        console.info('Tag was removed: ', event);

        this.tagValue.set('');
    }

    protected restart(): void {
        this.tagValue.set(TAG);
        this.tagModel.set(this.tagValue());
    }

    private start({ reason }: KbqTagEditChange): void {
        console.info(`Tag edit was started. Reason: "${reason}".`);
    }

    private cancel(event: KbqTagEditChange): void {
        console.info(`Tag edit was canceled. Reason: "${event.reason}".`);

        if (event.reason === 'focusout' && this.isModelValid()) {
            return this.submit({ ...event, reason: 'Handle submit when focusout' });
        }

        this.tagModel.set(this.tagValue());
    }

    private submit(event: KbqTagEditChange): void {
        console.info(`Tag edit was submitted. Reason: "${event.reason}".`);

        const model = this.trimmedTagModel();

        if (model.length === 0) {
            this.tagModel.set(this.tagValue());

            return event.tag.remove();
        }

        if (!this.isModelValid()) {
            return this.cancel({ ...event, reason: 'Handle cancel when model is invalid' });
        }

        this.tagValue.set(model);
        this.tagModel.set(this.tagValue());
    }
}
```

### Recommendations

Use a [`Badge`](https://koobiq.io/en/components/badge.md) if you need a colored label in a table or key-value list. Tags should be used exclusively as tokens within input controls.

### Keyboard navigation

#### Focus on tag

| Key                | Action               |
| ------------------ | -------------------- |
| Space              | Select/deselect tag. |
| Delete / Backspace | Remove tag.          |
| F2 / Enter         | Start editing.       |
| Ctrl + A           | Select all tags.     |

#### Tag editing mode

| Key   | Action          |
| ----- | --------------- |
| Enter | Save changes.   |
| Esc   | Cancel changes. |

## API

### Components

#### KbqTag (component)

```ts
@Component({
    selector: 'kbq-tag, [kbq-tag], kbq-basic-tag, [kbq-basic-tag]',
    exportAs: 'kbqTag'
})
class KbqTag extends KbqColorDirective implements IFocusableOption, KbqTitleTextRef {
    readonly editable = input<boolean | undefined>();
    readonly preventEditSubmit = input<boolean>(false);
    readonly selected = input(false);
    value = input<NonNullable<unknown> | null | undefined>();
    readonly selectable = input(false);
    readonly removable = input(true);
    tabindex = input<number | null | undefined>();
    disabled = input<boolean | undefined>();
    readonly color = input<T | null | undefined>();
    readonly editChange = output<KbqTagEditChange>();
    readonly selectionChange = output<KbqTagSelectionChange>();
    readonly destroyed = output<KbqTagEvent>();
    readonly removed = output<KbqTagEvent>();
    changeDetectorRef: ChangeDetectorRef;
    readonly contentChildren: Signal<readonly KbqIcon[]>;
    readonly avatar: Signal<KbqTagAvatar | undefined>;
    readonly trailingIcon: Signal<KbqTagTrailingIcon | undefined>;
    readonly removeIcon: Signal<any>;
    get canRemove(): boolean;
    get colorClassName(): string;
    select(): void;
    deselect(): void;
    toggleSelected(isUserInput?: boolean): boolean;
    focus(): void;
    remove(): void;
    setDefaultColor(color: T): void;
}
```

- `[editable]`: `boolean | undefined` — Whether the tag is editable.
- `[preventEditSubmit]`: `boolean` — Whether the tag edits can't be submitted.
- `[selected]`: `Signal<boolean>` — Whether the tag is selected.
- `[value]`: `NonNullable<unknown> | null | undefined` — The value of the tag. Defaults to the content inside `<kbq-tag>` tags.
- `[selectable]`: `Signal<boolean>` — Whether the tag is selectable.
- `[removable]`: `Signal<boolean>` — Determines whether the tag is removable.
- `[tabindex]`: `number | null | undefined` — Tab order of the tag.
- `[disabled]`: `boolean | undefined` — Whether the tag is disabled.
- `[localeOverrides]`: `KbqPartialLocaleData | undefined` — Localized strings for this element and its subtree, keyed by locale section. Every key is optional at
  every depth.
- `[color]`: `T | null | undefined` — Color of the component. A value set in code holds until the input changes, and the default color is used
  while both are unset or falsy.
- `(editChange)`: `KbqTagEditChange` — Emits event when the tag is edited.
- `(selectionChange)`: `KbqTagSelectionChange` — Emitted when the tag is selected or deselected.
- `(destroyed)`: `KbqTagEvent` — Emitted when the tag is destroyed and leaving the DOM.
- `(removed)`: `KbqTagEvent` — Emitted when a tag is to be removed.
  Fires on programmatic and UI removal (click or keyboard).
- `avatar`: `Signal<KbqTagAvatar | undefined>` — The tag avatar
- `trailingIcon`: `Signal<KbqTagTrailingIcon | undefined>` — The tag's trailing icon.
- `removeIcon`: `Signal<any>` — The tag's remove toggler.
- `canRemove`: `boolean` — Whether the user may remove this tag through the controls the tag itself offers — the remove
  control and the `Delete` key. A disabled tag offers neither.

  This is the affordance, not the channel: `remove()` stays open so the list can still clear a
  disabled tag when `clearPredicate` asks for it.
- `colorClassName`: `string` — current class name of color
- `select()`: `void` — Selects the tag.
- `deselect()`: `void` — Deselects the tag.
- `toggleSelected()`: `boolean` — Toggles the current selected state of the tag.
- `focus()`: `void` — Focuses the tag.
- `remove()`: `void` — Allows for programmatic removal of the tag. Called by the KbqTagList when the DELETE or
  BACKSPACE keys are pressed.

  Informs any listeners of the removal request. Does not remove the tag from the DOM.
- `setDefaultColor()`: `void` — this color will be used as a default value. For example [color]="'' | false | undefined | null".

#### KbqTagList (component)

```ts
@Component({ selector: 'kbq-tag-list', exportAs: 'kbqTagList' })
class KbqTagList implements KbqFormFieldControl<any>, ControlValueAccessor, CanUpdateErrorState {
    draggable = input<boolean | undefined>();
    readonly selectable = input<boolean>(true);
    readonly editable = input<boolean>(false);
    readonly clearPredicate = input<(tag: KbqTag) => boolean>(
        (tag) => !tag.disabled
    );
    readonly removable = input<boolean>(true);
    readonly errorStateMatcher = input<ErrorStateMatcher>();
    readonly dropped = output<KbqTagListDroppedEvent>();
    readonly change = output<KbqTagListChange>();
    defaultErrorStateMatcher: ErrorStateMatcher;
    parentForm: NgForm | null;
    parentFormGroup: FormGroupDirective | null;
    ngControl: NgControl;
    readonly focused: Signal<boolean>;
    notifyPendingTagChange(): void;
    focus(): void;
    blur(): void;
    markAsTouched(): void;
}
```

- `[draggable]`: `boolean | undefined` — Whether the tags in the list can be reordered by dragging.
- `[selectable]`: `boolean` — Whether or not this tag list is selectable. When a tag list is not selectable,
  the selected states for all the tags inside the tag list are always ignored.
- `[editable]`: `boolean` — Whether the tags in the list are editable.
- `[clearPredicate]`: `(tag: KbqTag) => boolean` — Decides which tags the projected `KbqCleaner` removes: return `true` to clear the tag, `false` to
  keep it. Disabled tags are kept by default. Bind a stable reference — a new function on every change
  detection re-runs the predicate over every tag.

  A tag the list does not allow to be removed is never offered, whatever the predicate answers.
- `[removable]`: `boolean` — Whether the tags in the list are removable.
- `[errorStateMatcher]`: `ErrorStateMatcher` — An object used to control when error messages are shown.
- `(dropped)`: `KbqTagListDroppedEvent` — Emits when the user drops tag inside tag list container.
- `(change)`: `KbqTagListChange` — Event emitted when the selected tag list value has been changed by the user.
- `focused`: `Signal<boolean>` — Whether any tags or the kbqTagInput inside of this tag-list has focus.
- `notifyPendingTagChange()`: `void` — Notifies that the next `tags.changes` emission is UI-initiated.
- `focus()`: `void` — Focuses the tag list. If there is a tag input, focuses that instead.
- `blur()`: `void` — When blurred, mark the field as touched when focus moved outside the tag list.
- `markAsTouched()`: `void` — Mark the field as touched

### Directives

#### KbqTagEditInput (directive)

Directive to add input behavior for the editable tag.

```ts
@Directive({ selector: '[kbqTagEditInput]', exportAs: 'kbqTagEditInput' })
class KbqTagEditInput {}
```

#### KbqTagEditSubmit (directive)

Directive to add submit behavior for the editable tag.

```ts
@Directive({ selector: '[kbqTagEditSubmit]', exportAs: 'kbqTagEditSubmit' })
class KbqTagEditSubmit {}
```

#### KbqTagInput (directive)

Directive that adds tag-specific behaviors to an input element inside `<kbq-form-field>`.
May be placed inside or outside of an `<kbq-tag-list>`.

```ts
@Directive({
    selector: 'input[kbqTagInputFor]',
    exportAs: 'kbqTagInput, kbqTagInputFor'
})
class KbqTagInput implements KbqTagTextControl {
    readonly separatorKeyCodes = input<number[]>(
        this.defaultOptions.separatorKeyCodes,
        { alias: 'kbqTagInputSeparatorKeyCodes' }
    );
    readonly distinct = input<boolean>(false);
    readonly placeholder = input<string>('');
    readonly id = input<string>(
        inject(_IdGenerator).getId('kbq-tag-list-input-')
    );
    readonly tagList = input<KbqTagList | undefined>(undefined, {
        alias: 'kbqTagInputFor'
    });
    readonly addOnBlur = input<boolean>(true, {
        alias: 'kbqTagInputAddOnBlur'
    });
    readonly addOnPaste = input<boolean>(
        this.defaultOptions.addOnPaste ?? true,
        { alias: 'kbqTagInputAddOnPaste' }
    );
    disabled = input<boolean | undefined>();
    readonly tagEnd = output<KbqTagInputEvent>({
        alias: 'kbqTagInputTokenEnd'
    });
    readonly autofilled: Signal<boolean>;
    readonly empty: Signal<boolean>;
    focus(): void;
}
```

- `[kbqTagInputSeparatorKeyCodes]`: `number[]` — The list of key codes that will trigger a tagEnd event.

  Defaults to `[ENTER]`.
- `[distinct]`: `boolean` — A value indicating whether allow/prevent tags duplication
- `[placeholder]`: `string` — The input's placeholder text.
- `[id]`: `string` — Unique id for the input.
- `[kbqTagInputFor]`: `KbqTagList | undefined` — Register input for tag list.
- `[kbqTagInputAddOnBlur]`: `boolean` — Whether or not the tagEnd event will be emitted when the input is blurred.
- `[kbqTagInputAddOnPaste]`: `boolean` — Whether the tagEnd event will be emitted when the text pasted. Defaults to `addOnPaste` of
  `KBQ_TAGS_DEFAULT_OPTIONS`, or `true`.
- `[disabled]`: `boolean | undefined` — Whether the input is disabled.
- `(kbqTagInputTokenEnd)`: `KbqTagInputEvent` — Emitted when a tag is to be added.
- `autofilled`: `Signal<boolean>` — Whether the control's value was filled in by the browser.
- `empty`: `Signal<boolean>` — Whether the input is empty.
- `focus()`: `void` — Focuses the input.

#### KbqTagPrefix (directive)

Marks content to be projected into the prefix (leading) slot of a tag.

```ts
@Directive({ selector: '[kbqTagPrefix]' })
class KbqTagPrefix {}
```

#### KbqTagRemove (directive)

Example:

    `<kbq-tag>
      <kbq-icon kbqTagRemove>cancel</kbq-icon>
    </kbq-tag>`

You *may* use a custom icon, but you may need to override the `kbq-tag-remove` positioning
styles to properly center the icon within the tag.

```ts
@Directive({ selector: '[kbqTagRemove]' })
class KbqTagRemove {
    readonly ariaLabel = input<string | undefined>(undefined, {
        alias: 'aria-label'
    });
}
```

- `[aria-label]`: `string | undefined` — Accessible name of the button. Defaults to the localized "Remove".

#### KbqTagSuffix (directive)

Marks content to be projected into the suffix (trailing) slot of a tag.

```ts
@Directive({ selector: '[kbqTagSuffix]' })
class KbqTagSuffix {}
```

### Classes

#### KbqTagEditChange (class)

Event object emitted by KbqTag when the tag is edited.

```ts
class KbqTagEditChange {
    readonly tag: KbqTag;
    readonly type: "start" | "cancel" | "submit";
    readonly reason: string;
    constructor(
        tag: KbqTag,
        type: "start" | "cancel" | "submit",
        reason: string
    );
}
```

#### KbqTagListChange (class)

Change event object that is emitted when the tag list value has changed.

```ts
class KbqTagListChange {
    source: KbqTagList;
    value: any;
    constructor(source: KbqTagList, value: any);
}
```

#### KbqTagSelectionChange (class)

Event object emitted by KbqTag when selected or deselected.

```ts
class KbqTagSelectionChange {
    source: KbqTag;
    selected: boolean;
    isUserInput: boolean;
    constructor(
        source: KbqTag,
        selected: boolean,
        isUserInput?: boolean
    );
}
```

### Types

#### KbqTagEvent (type)

Event object emitted by KbqTag.

```ts
type KbqTagEvent = {
    tag: KbqTag;
};
```

#### KbqTagFocusEvent (type)

Event object emitted when the KbqTag is focused.

```ts
type KbqTagFocusEvent = KbqTagEvent & {
    origin: FocusOrigin;
};
```

#### KbqTagInputEvent (interface)

Represents an input event on a `kbqTagInput`.

```ts
interface KbqTagInputEvent {
    input: HTMLInputElement;
    value: string;
}
```

- `input`: `HTMLInputElement` — The native `<input>` element that the event is being fired for.
- `value`: `string` — The value of the input.

#### KbqTagListDroppedEvent (type)

```ts
type KbqTagListDroppedEvent = Pick<CdkDragDrop<unknown>, 'event' | 'previousIndex' | 'currentIndex'> & {
    tag: KbqTag;
};
```

#### KbqTagsDefaultOptions (interface)

Default options for the tags module that can be overridden.

```ts
interface KbqTagsDefaultOptions {
    separatorKeyCodes: number[];
    separators?: KbqTagSeparator[];
    addOnPaste?: boolean;
}
```

- `separatorKeyCodes`: `number[]` — The list of key codes that will trigger a tagEnd event.
- `separators?`: `KbqTagSeparator[]` — Custom separator definitions to use instead of the built-in defaults.
- `addOnPaste?`: `boolean` — Whether the tagEnd event will be emitted when text is pasted.

#### KbqTagSeparator (interface)

Defines a character or pattern that ends a tag, and the contexts in which it applies.

```ts
interface KbqTagSeparator {
    symbol: RegExp;
    key?: string;
    keyCode?: number;
    appliesTo?: KbqTagSeparatorContext[];
}
```

- `symbol`: `RegExp` — Regular expression used to match/split this separator in text.
- `key?`: `string` — `KeyboardEvent.key` that triggers this separator while typing.
  Omit for separators with no single-keystroke equivalent (e.g. a run of whitespace) —
  those are implicitly paste-only, since they can never match a `keydown`.
- `keyCode?`: `number` — Numeric key code gating whether this separator is enabled, set via `kbqTagInputSeparatorKeyCodes`.
  Omit together with `key` for separators that should always be active regardless of that input.
- `appliesTo?`: `KbqTagSeparatorContext[]` — Contexts this separator applies to. Defaults to `['input', 'paste']`.

#### KbqTagSeparatorContext (type)

Contexts a `KbqTagSeparator` can be active in.

```ts
type KbqTagSeparatorContext = 'input' | 'paste';
```

### Constants

#### KBQ_TAGS_DEFAULT_OPTIONS (const)

Injection token to be used to override the default options.

```ts
const KBQ_TAGS_DEFAULT_OPTIONS: InjectionToken<KbqTagsDefaultOptions>;
```

#### kbqTagsDefaultOptionsProvider (const)

Utility provider for `KBQ_TAGS_DEFAULT_OPTIONS`.

```ts
const kbqTagsDefaultOptionsProvider: (
    options: Partial<KbqTagsDefaultOptions>
) => Provider;
```
