Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions docs/components-api/inputgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

# ExtraInputGroup

> ✅ **Реализован**: `ExtraInputGroupComponent` / `ExtraInputGroupAddonComponent`
> (`@cdek-it/angular-ui-kit`) соответствуют спецификации.

Контейнер: оборачивает поле ввода аддонами (текстом или иконкой) слева и/или справа. Поле ввода и аддоны передаются как содержимое (content projection). Событий нет — обрабатываются на вложенном поле.

| Свойство | Описание | Типизация |
| ---------------- | ------------------------------------------ | ----------------------- |
| `addonPosition` | расположение аддона(ов) относительно поля | `left \| right \| both` |
| Свойство | Описание | Типизация |
| --------------- | ----------------------------------------- | ---------------------------------- |
| `addonPosition` | расположение аддона(ов) относительно поля | `left \| right \| both` |
| `size` | размер группы (паддинги и шрифты аддонов) | `small \| base \| large \| xlarge` |

# ExtraInputGroupAddon

Expand Down
20 changes: 16 additions & 4 deletions src/lib/components/inputgroup/input-group-addon.component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { Component } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { InputGroupAddon } from 'primeng/inputgroupaddon';

export type ExtraInputGroupPosition = 'left' | 'right';
export type ExtraInputGroupAddonType = 'text' | 'icon';

@Component({
selector: 'extra-input-group-addon',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [InputGroupAddon],
template: `
<p-inputgroup-addon>
<ng-content></ng-content>
<p-inputgroup-addon [class]="'extra-input-group-addon--' + position">
@if (type === 'icon') {
<i [class]="icon"></i>
} @else {
<ng-content></ng-content>
}
</p-inputgroup-addon>
`
})
export class ExtraInputGroupAddonComponent {}
export class ExtraInputGroupAddonComponent {
@Input() position: ExtraInputGroupPosition = 'left';
@Input() type: ExtraInputGroupAddonType = 'text';
@Input() icon: string | undefined = undefined;
}
15 changes: 10 additions & 5 deletions src/lib/components/inputgroup/input-group.component.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import { Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { NgClass } from '@angular/common';
import { InputGroup } from 'primeng/inputgroup';

export type ExtraInputGroupSize = 'small' | 'base' | 'large' | 'xlarge';
export type ExtraInputGroupAddonPosition = 'left' | 'right' | 'both';

@Component({
selector: 'extra-input-group',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [InputGroup, NgClass],
template: `
<p-inputgroup [ngClass]="sizeClass">
<p-inputgroup [ngClass]="groupClass">
<ng-content></ng-content>
</p-inputgroup>
`
})
export class ExtraInputGroupComponent {
@Input() size: ExtraInputGroupSize = 'base';
@Input() addonPosition: ExtraInputGroupAddonPosition = 'left';

get sizeClass(): string {
if (this.size === 'xlarge') return 'p-inputgroup-xlg';
return '';
get groupClass(): Record<string, boolean> {
return {
'p-inputgroup-xlg': this.size === 'xlarge',
[`extra-input-group--addon-${this.addonPosition}`]: true
};
}
}
52 changes: 36 additions & 16 deletions src/lib/components/inputgroup/inputgroup.figma.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,31 @@ updated: '2026-06-22'
| Свойство | Тип | По умолчанию | Описание |
|----------|-----|--------------|---------|
| `size` | `'small' \| 'base' \| 'large' \| 'xlarge'` | `'base'` | Размер группы; влияет на паддинги и шрифты аддонов. Должен совпадать с `size` вложенного `<extra-input-text>` |

> Позиция аддонов (Figma-свойство `addon-position`) в коде не является `@Input` — она задаётся порядком проекции `<extra-input-group-addon>` относительно `<extra-input-text>` в шаблоне.
| `addonPosition` | `'left' \| 'right' \| 'both'` | `'left'` | Соответствует Figma-свойству `addon-position`. Навешивает CSS-класс `extra-input-group--addon-<value>` на корневой элемент — **не управляет DOM-порядком**: что фактически рендерится слева/справа по-прежнему определяется порядком `<extra-input-group-addon>`/`<extra-input-text>` в шаблоне (см. Do/Don't) |

### `<extra-input-group-addon>` — аддон

`ExtraInputGroupAddonComponent` (символ `ExtraInputGroupAddonComponent`, селектор `extra-input-group-addon`) не имеет `@Input`. Содержимое аддона (иконка, текст или кнопка) передаётся через проекцию контента — см. [Slots](#slots).
| Свойство | Тип | По умолчанию | Описание |
|----------|-----|--------------|---------|
| `position` | `'left' \| 'right'` | `'left'` | Положение конкретного аддона — навешивает класс `extra-input-group-addon--<value>` на `p-inputgroup-addon` |
| `type` | `'text' \| 'icon'` | `'text'` | `'text'` — содержимое аддона передаётся через `<ng-content>`; `'icon'` — рендерится `<i [class]="icon">`, проекция игнорируется |
| `icon` | `string \| undefined` | `undefined` | CSS-класс tabler-иконки (используется при `type='icon'`) |

> ⚠️ **Переоценка прежнего решения**: раньше этот документ утверждал, что позиция — не `@Input`, а
> целиком определяется порядком проекции, и `ExtraInputGroupAddonComponent` не имел входов вовсе.
> По требованию спеки `addonPosition`/`position` реализованы как настоящие `@Input()` (CSS-класс-хуки),
> а `type`/`icon` добавлены как недостающая функциональность — без них аддон мог показывать только
> текст через `<ng-content>`, иконку приходилось оборачивать вручную без стандартного способа это
> обозначить. DOM-порядок по-прежнему определяет фактическое расположение — оба механизма сосуществуют.

> ⚠️ **Найденный баг в CSS-токенах**: `tokens/components/inputgroup.ts` стилизует скругления/границы
> аддонов через селекторы `.p-inputgroup > input-group-addon` / `.p-inputgroup > input-text` — но
> реальные селекторы хост-тегов компонентов — `extra-input-group-addon` / `extra-input-text` (с
> префиксом `extra-`). Проверено Playwright: `border-radius` аддона сейчас одинаковый со всех сторон
> (должен быть скруглён только с внешней стороны, со стороны поля — прямой угол) — аддон и поле
> визуально не сливаются в единый блок, как задумано в Figma. На коде это не чинится — файл токенов
> зона дизайнера (см. «Не трогать» в корневом `CLAUDE.md`), нужно поправить селекторы на
> `extra-input-group-addon`/`extra-input-text`.

## Variants

Expand All @@ -40,8 +59,8 @@ updated: '2026-06-22'
Figma: `<InputGroup>`, addon-position=left — nodeId `437:3504`

```html
<extra-input-group>
<extra-input-group-addon><i class="ti ti-user"></i></extra-input-group-addon>
<extra-input-group addonPosition="left">
<extra-input-group-addon position="left" type="icon" icon="ti ti-user"></extra-input-group-addon>
<extra-input-text placeholder="Введите данные..." [(ngModel)]="value" name="field" [fluid]="true"></extra-input-text>
</extra-input-group>
```
Expand All @@ -51,9 +70,9 @@ Figma: `<InputGroup>`, addon-position=left — nodeId `437:3504`
Figma: `<InputGroup>`, addon-position=right — nodeId `437:3502`

```html
<extra-input-group>
<extra-input-group addonPosition="right">
<extra-input-text placeholder="Сумма" [(ngModel)]="amount" name="amount" [fluid]="true"></extra-input-text>
<extra-input-group-addon>₽</extra-input-group-addon>
<extra-input-group-addon position="right">₽</extra-input-group-addon>
</extra-input-group>
```

Expand All @@ -62,10 +81,10 @@ Figma: `<InputGroup>`, addon-position=right — nodeId `437:3502`
Figma: `<InputGroup>`, addon-position=left & right — nodeId `437:3503`

```html
<extra-input-group>
<extra-input-group-addon><i class="ti ti-currency-dollar"></i></extra-input-group-addon>
<extra-input-group addonPosition="both">
<extra-input-group-addon position="left" type="icon" icon="ti ti-currency-dollar"></extra-input-group-addon>
<extra-input-text placeholder="0.00" [(ngModel)]="price" name="price" [fluid]="true"></extra-input-text>
<extra-input-group-addon>USD</extra-input-group-addon>
<extra-input-group-addon position="right">USD</extra-input-group-addon>
</extra-input-group>
```

Expand All @@ -74,16 +93,16 @@ Figma: `<InputGroup>`, addon-position=left & right — nodeId `437:3503`
Figma: `<InputGroup>`, addon-position=left

```html
<extra-input-group size="xlarge">
<extra-input-group-addon><i class="ti ti-search"></i></extra-input-group-addon>
<extra-input-group size="xlarge" addonPosition="left">
<extra-input-group-addon position="left" type="icon" icon="ti ti-search"></extra-input-group-addon>
<extra-input-text size="xlarge" placeholder="Поиск" [(ngModel)]="query" name="query" [fluid]="true"></extra-input-text>
</extra-input-group>
```

## Slots

- **`default` (обёртка)** — `<extra-input-group>` проецирует через `<ng-content>` дочерние `<extra-input-group-addon>` и `<extra-input-text>`. Позиция аддона определяется его порядком относительно поля.
- **`default` (аддон)** — `<extra-input-group-addon>` проецирует через `<ng-content>` своё содержимое: иконку (см. [icons.md](../../figma-code-connect/icons.md)), текст или кнопку.
- **`default` (обёртка)** — `<extra-input-group>` проецирует через `<ng-content>` дочерние `<extra-input-group-addon>` и `<extra-input-text>`. Фактическое расположение определяется порядком элементов в шаблоне; `addonPosition` на обёртке и `position` на аддоне — CSS-класс-хуки, они не переставляют DOM.
- **`default` (аддон, только при `type='text'`)** — `<extra-input-group-addon>` проецирует через `<ng-content>` текстовое содержимое (единицы измерения, символ валюты и т.п.). При `type='icon'` проекция игнорируется — рендерится `<i [class]="icon">`.

## Related

Expand All @@ -97,10 +116,11 @@ Figma: `<InputGroup>`, addon-position=left
**Do:**
- Задавайте одинаковый `size` на `<extra-input-group>` и на вложенном `<extra-input-text>`, чтобы поле и аддоны совпадали по высоте.
- Используйте `[fluid]="true"` на `<extra-input-text>` внутри группы, чтобы поле занимало доступную ширину.
- Управляйте позицией аддона его порядком в шаблоне: до поля — слева, после — справа, и до, и после — с обеих сторон.
- Располагайте аддон и поле в шаблоне в нужном порядке (до поля — слева, после — справа) **и** задавайте соответствующие `addonPosition`/`position` — оба механизма должны совпадать.
- Используйте `type="icon"` + `icon` для иконочных аддонов вместо ручной вставки `<i>` в проекцию.
- Передавайте иконки в `<extra-input-group-addon>` ссылками из [icons.md](../../figma-code-connect/icons.md).

**Don't:**
- Не задавайте позицию аддона через `@Input` — у `<extra-input-group-addon>` нет входных свойств; позиция определяется порядком проекции.
- Не рассинхронизируйте `addonPosition`/`position` с фактическим порядком элементов в шаблоне — CSS-класс и реальное расположение должны совпадать, иначе стили поедут.
- Не инлайньте значения токенов паддингов/цветов аддонов — используйте [tokens.md](../../figma-code-connect/tokens.md).
- Не вкладывайте несколько `<extra-input-text>` в одну группу — обёртка рассчитана на одно поле с аддонами.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in

const template = `
<div class="bg-surface-ground p-4">
<extra-input-group>
<extra-input-group-addon><i class="ti ti-map-pin"></i></extra-input-group-addon>
<extra-input-group addonPosition="both">
<extra-input-group-addon position="left" type="icon" icon="ti ti-map-pin"></extra-input-group-addon>
<extra-input-text placeholder="Введите адрес..." [(ngModel)]="value"></extra-input-text>
<extra-input-group-addon><i class="ti ti-search"></i></extra-input-group-addon>
<extra-input-group-addon position="right" type="icon" icon="ti ti-search"></extra-input-group-addon>
</extra-input-group>
</div>
`;
Expand All @@ -37,17 +37,17 @@ export const AddonBoth: StoryObj = {
source: {
language: 'ts',
code: `
import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent } from '@cdek-it/angular-ui-kit';
import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-inputgroup-addon-both',
standalone: true,
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent, FormsModule],
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent, FormsModule],
template: \`
<extra-input-group>
<extra-input-group-addon><i class="ti ti-map-pin"></i></extra-input-group-addon>
<extra-input-group addonPosition="both">
<extra-input-group-addon position="left" type="icon" icon="ti ti-map-pin"></extra-input-group-addon>
<extra-input-text placeholder="Введите адрес..." [(ngModel)]="value"></extra-input-text>
<extra-input-group-addon><i class="ti ti-search"></i></extra-input-group-addon>
<extra-input-group-addon position="right" type="icon" icon="ti ti-search"></extra-input-group-addon>
</extra-input-group>
\`,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in

const template = `
<div class="bg-surface-ground p-4">
<extra-input-group>
<extra-input-group addonPosition="right">
<extra-input-text placeholder="Сумма" [(ngModel)]="value"></extra-input-text>
<extra-input-group-addon>руб.</extra-input-group-addon>
<extra-input-group-addon position="right">руб.</extra-input-group-addon>
</extra-input-group>
</div>
`;
Expand All @@ -36,16 +36,16 @@ export const AddonRight: StoryObj = {
source: {
language: 'ts',
code: `
import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent } from '@cdek-it/angular-ui-kit';
import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-inputgroup-addon-right',
standalone: true,
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent, FormsModule],
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent, FormsModule],
template: \`
<extra-input-group>
<extra-input-group addonPosition="right">
<extra-input-text placeholder="Сумма" [(ngModel)]="value"></extra-input-text>
<extra-input-group-addon>руб.</extra-input-group-addon>
<extra-input-group-addon position="right">руб.</extra-input-group-addon>
</extra-input-group>
\`,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in
const template = `
<div class="bg-surface-ground p-4">
<extra-input-group>
<extra-input-group-addon><i class="ti ti-user"></i></extra-input-group-addon>
<extra-input-group-addon position="left" type="icon" icon="ti ti-user"></extra-input-group-addon>
<extra-input-text placeholder="Введите данные..." [formControl]="control"></extra-input-text>
</extra-input-group>
</div>
Expand Down Expand Up @@ -46,7 +46,7 @@ import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputText
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent, ReactiveFormsModule],
template: \`
<extra-input-group>
<extra-input-group-addon><i class="ti ti-user"></i></extra-input-group-addon>
<extra-input-group-addon position="left" type="icon" icon="ti ti-user"></extra-input-group-addon>
<extra-input-text placeholder="Введите данные..." [formControl]="control"></extra-input-text>
</extra-input-group>
\`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in
const template = `
<div class="bg-surface-ground p-4">
<extra-input-group>
<extra-input-group-addon>@</extra-input-group-addon>
<extra-input-group-addon position="left">@</extra-input-group-addon>
<extra-input-text placeholder="Username" [(ngModel)]="value"></extra-input-text>
</extra-input-group>
</div>
Expand Down Expand Up @@ -38,15 +38,15 @@ export const WithText: StoryObj = {
source: {
language: 'ts',
code: `
import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent } from '@cdek-it/angular-ui-kit';
import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-inputgroup-with-text',
standalone: true,
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent, FormsModule],
imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent, FormsModule],
template: \`
<extra-input-group>
<extra-input-group-addon>@</extra-input-group-addon>
<extra-input-group-addon position="left">@</extra-input-group-addon>
<extra-input-text placeholder="Username" [(ngModel)]="value"></extra-input-text>
</extra-input-group>
\`,
Expand Down
Loading
Loading