diff --git a/docs/components-api/inputgroup.md b/docs/components-api/inputgroup.md index 6f81f375..639ffddd 100644 --- a/docs/components-api/inputgroup.md +++ b/docs/components-api/inputgroup.md @@ -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 diff --git a/src/lib/components/inputgroup/input-group-addon.component.ts b/src/lib/components/inputgroup/input-group-addon.component.ts index bf90efd0..ef4e3ff3 100644 --- a/src/lib/components/inputgroup/input-group-addon.component.ts +++ b/src/lib/components/inputgroup/input-group-addon.component.ts @@ -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: ` - - + + @if (type === 'icon') { + + } @else { + + } ` }) -export class ExtraInputGroupAddonComponent {} +export class ExtraInputGroupAddonComponent { + @Input() position: ExtraInputGroupPosition = 'left'; + @Input() type: ExtraInputGroupAddonType = 'text'; + @Input() icon: string | undefined = undefined; +} diff --git a/src/lib/components/inputgroup/input-group.component.ts b/src/lib/components/inputgroup/input-group.component.ts index 2a45f880..5aecf422 100644 --- a/src/lib/components/inputgroup/input-group.component.ts +++ b/src/lib/components/inputgroup/input-group.component.ts @@ -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: ` - + ` }) 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 { + return { + 'p-inputgroup-xlg': this.size === 'xlarge', + [`extra-input-group--addon-${this.addonPosition}`]: true + }; } } diff --git a/src/lib/components/inputgroup/inputgroup.figma.md b/src/lib/components/inputgroup/inputgroup.figma.md index a8185e5b..47ed1f7e 100644 --- a/src/lib/components/inputgroup/inputgroup.figma.md +++ b/src/lib/components/inputgroup/inputgroup.figma.md @@ -26,12 +26,31 @@ updated: '2026-06-22' | Свойство | Тип | По умолчанию | Описание | |----------|-----|--------------|---------| | `size` | `'small' \| 'base' \| 'large' \| 'xlarge'` | `'base'` | Размер группы; влияет на паддинги и шрифты аддонов. Должен совпадать с `size` вложенного `` | - -> Позиция аддонов (Figma-свойство `addon-position`) в коде не является `@Input` — она задаётся порядком проекции `` относительно `` в шаблоне. +| `addonPosition` | `'left' \| 'right' \| 'both'` | `'left'` | Соответствует Figma-свойству `addon-position`. Навешивает CSS-класс `extra-input-group--addon-` на корневой элемент — **не управляет DOM-порядком**: что фактически рендерится слева/справа по-прежнему определяется порядком ``/`` в шаблоне (см. Do/Don't) | ### `` — аддон -`ExtraInputGroupAddonComponent` (символ `ExtraInputGroupAddonComponent`, селектор `extra-input-group-addon`) не имеет `@Input`. Содержимое аддона (иконка, текст или кнопка) передаётся через проекцию контента — см. [Slots](#slots). +| Свойство | Тип | По умолчанию | Описание | +|----------|-----|--------------|---------| +| `position` | `'left' \| 'right'` | `'left'` | Положение конкретного аддона — навешивает класс `extra-input-group-addon--` на `p-inputgroup-addon` | +| `type` | `'text' \| 'icon'` | `'text'` | `'text'` — содержимое аддона передаётся через ``; `'icon'` — рендерится ``, проекция игнорируется | +| `icon` | `string \| undefined` | `undefined` | CSS-класс tabler-иконки (используется при `type='icon'`) | + +> ⚠️ **Переоценка прежнего решения**: раньше этот документ утверждал, что позиция — не `@Input`, а +> целиком определяется порядком проекции, и `ExtraInputGroupAddonComponent` не имел входов вовсе. +> По требованию спеки `addonPosition`/`position` реализованы как настоящие `@Input()` (CSS-класс-хуки), +> а `type`/`icon` добавлены как недостающая функциональность — без них аддон мог показывать только +> текст через ``, иконку приходилось оборачивать вручную без стандартного способа это +> обозначить. 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 @@ -40,8 +59,8 @@ updated: '2026-06-22' Figma: ``, addon-position=left — nodeId `437:3504` ```html - - + + ``` @@ -51,9 +70,9 @@ Figma: ``, addon-position=left — nodeId `437:3504` Figma: ``, addon-position=right — nodeId `437:3502` ```html - + - ₽ + ₽ ``` @@ -62,10 +81,10 @@ Figma: ``, addon-position=right — nodeId `437:3502` Figma: ``, addon-position=left & right — nodeId `437:3503` ```html - - + + - USD + USD ``` @@ -74,16 +93,16 @@ Figma: ``, addon-position=left & right — nodeId `437:3503` Figma: ``, addon-position=left ```html - - + + ``` ## Slots -- **`default` (обёртка)** — `` проецирует через `` дочерние `` и ``. Позиция аддона определяется его порядком относительно поля. -- **`default` (аддон)** — `` проецирует через `` своё содержимое: иконку (см. [icons.md](../../figma-code-connect/icons.md)), текст или кнопку. +- **`default` (обёртка)** — `` проецирует через `` дочерние `` и ``. Фактическое расположение определяется порядком элементов в шаблоне; `addonPosition` на обёртке и `position` на аддоне — CSS-класс-хуки, они не переставляют DOM. +- **`default` (аддон, только при `type='text'`)** — `` проецирует через `` текстовое содержимое (единицы измерения, символ валюты и т.п.). При `type='icon'` проекция игнорируется — рендерится ``. ## Related @@ -97,10 +116,11 @@ Figma: ``, addon-position=left **Do:** - Задавайте одинаковый `size` на `` и на вложенном ``, чтобы поле и аддоны совпадали по высоте. - Используйте `[fluid]="true"` на `` внутри группы, чтобы поле занимало доступную ширину. -- Управляйте позицией аддона его порядком в шаблоне: до поля — слева, после — справа, и до, и после — с обеих сторон. +- Располагайте аддон и поле в шаблоне в нужном порядке (до поля — слева, после — справа) **и** задавайте соответствующие `addonPosition`/`position` — оба механизма должны совпадать. +- Используйте `type="icon"` + `icon` для иконочных аддонов вместо ручной вставки `` в проекцию. - Передавайте иконки в `` ссылками из [icons.md](../../figma-code-connect/icons.md). **Don't:** -- Не задавайте позицию аддона через `@Input` — у `` нет входных свойств; позиция определяется порядком проекции. +- Не рассинхронизируйте `addonPosition`/`position` с фактическим порядком элементов в шаблоне — CSS-класс и реальное расположение должны совпадать, иначе стили поедут. - Не инлайньте значения токенов паддингов/цветов аддонов — используйте [tokens.md](../../figma-code-connect/tokens.md). - Не вкладывайте несколько `` в одну группу — обёртка рассчитана на одно поле с аддонами. diff --git a/src/stories/components/inputgroup/examples/inputgroup-addon-both.component.ts b/src/stories/components/inputgroup/examples/inputgroup-addon-both.component.ts index eea91b74..c6d207b0 100644 --- a/src/stories/components/inputgroup/examples/inputgroup-addon-both.component.ts +++ b/src/stories/components/inputgroup/examples/inputgroup-addon-both.component.ts @@ -7,10 +7,10 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in const template = `
- - + + - +
`; @@ -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: \` - - + + - + \`, }) diff --git a/src/stories/components/inputgroup/examples/inputgroup-addon-right.component.ts b/src/stories/components/inputgroup/examples/inputgroup-addon-right.component.ts index caebb572..df352534 100644 --- a/src/stories/components/inputgroup/examples/inputgroup-addon-right.component.ts +++ b/src/stories/components/inputgroup/examples/inputgroup-addon-right.component.ts @@ -7,9 +7,9 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in const template = `
- + - руб. + руб.
`; @@ -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: \` - + - руб. + руб. \`, }) diff --git a/src/stories/components/inputgroup/examples/inputgroup-disabled.component.ts b/src/stories/components/inputgroup/examples/inputgroup-disabled.component.ts index dc334bc4..1f24892e 100644 --- a/src/stories/components/inputgroup/examples/inputgroup-disabled.component.ts +++ b/src/stories/components/inputgroup/examples/inputgroup-disabled.component.ts @@ -8,7 +8,7 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in const template = `
- +
@@ -46,7 +46,7 @@ import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputText imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent, ReactiveFormsModule], template: \` - + \`, diff --git a/src/stories/components/inputgroup/examples/inputgroup-with-text.component.ts b/src/stories/components/inputgroup/examples/inputgroup-with-text.component.ts index f077f078..cb22a4be 100644 --- a/src/stories/components/inputgroup/examples/inputgroup-with-text.component.ts +++ b/src/stories/components/inputgroup/examples/inputgroup-with-text.component.ts @@ -8,7 +8,7 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in const template = `
- @ + @
@@ -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: \` - @ + @ \`, diff --git a/src/stories/components/inputgroup/examples/inputgroup-xlarge.component.ts b/src/stories/components/inputgroup/examples/inputgroup-xlarge.component.ts index 088db6aa..686924d4 100644 --- a/src/stories/components/inputgroup/examples/inputgroup-xlarge.component.ts +++ b/src/stories/components/inputgroup/examples/inputgroup-xlarge.component.ts @@ -8,8 +8,8 @@ import { ExtraInputTextComponent } from '../../../../lib/components/inputtext/in const template = `
- - + +
`; @@ -36,16 +36,16 @@ export const Sizes: 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-xlarge', standalone: true, - imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, InputTextComponent, FormsModule], + imports: [ExtraInputGroupComponent, ExtraInputGroupAddonComponent, ExtraInputTextComponent, FormsModule], template: \` - - + + \`, }) diff --git a/src/stories/components/inputgroup/inputgroup.stories.ts b/src/stories/components/inputgroup/inputgroup.stories.ts index 20c212d5..b176da0b 100644 --- a/src/stories/components/inputgroup/inputgroup.stories.ts +++ b/src/stories/components/inputgroup/inputgroup.stories.ts @@ -46,14 +46,25 @@ import { ExtraInputGroupComponent, ExtraInputGroupAddonComponent } from '@cdek-i options: ['small', 'base', 'large', 'xlarge'], description: 'Размер группы (влияет на паддинги и шрифты аддонов)', table: { - category: 'Props', + category: 'Свойства', defaultValue: { summary: "'base'" }, type: { summary: "'small' | 'base' | 'large' | 'xlarge'" } } + }, + addonPosition: { + control: 'select', + options: ['left', 'right', 'both'], + description: 'Расположение аддона(ов) относительно поля (CSS-класс-хук, не переставляет DOM)', + table: { + category: 'Свойства', + defaultValue: { summary: "'left'" }, + type: { summary: "'left' | 'right' | 'both'" } + } } }, args: { - size: 'base' + size: 'base', + addonPosition: 'left' } }; @@ -65,6 +76,7 @@ export const Default: Story = { render: (args) => { const groupParts: string[] = []; if (args.size && args.size !== 'base') groupParts.push(`size="${args.size}"`); + if (args.addonPosition && args.addonPosition !== 'left') groupParts.push(`addonPosition="${args.addonPosition}"`); const groupOpen = groupParts.length ? `` : ``; @@ -72,7 +84,7 @@ export const Default: Story = { const template = ` ${groupOpen} - + `;