Skip to content
Merged
14 changes: 14 additions & 0 deletions src/lib/components/inputgroup/input-group-addon.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { InputGroupAddon } from 'primeng/inputgroupaddon';

@Component({
selector: 'input-group-addon',
standalone: true,
imports: [InputGroupAddon],
template: `
<p-inputgroup-addon>
<ng-content></ng-content>
</p-inputgroup-addon>
`,
})
export class InputGroupAddonComponent {}
24 changes: 24 additions & 0 deletions src/lib/components/inputgroup/input-group.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component, Input } from '@angular/core';
import { NgClass } from '@angular/common';
import { InputGroup } from 'primeng/inputgroup';

export type InputGroupSize = 'small' | 'base' | 'large' | 'xlarge';

@Component({
selector: 'input-group',
standalone: true,
imports: [InputGroup, NgClass],
template: `
<p-inputgroup [ngClass]="sizeClass">
<ng-content></ng-content>
</p-inputgroup>
`,
})
export class InputGroupComponent {
@Input() size: InputGroupSize = 'base';

get sizeClass(): string {
if (this.size === 'xlarge') return 'p-inputgroup-xlg';
return '';
}
}
5 changes: 5 additions & 0 deletions src/prime-preset/map-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { passwordCss } from './tokens/components/password';
import { tagCss } from './tokens/components/tag';
import { textareaCss } from './tokens/components/textarea';
import { tooltipCss } from './tokens/components/tooltip';
import { inputgroupCss } from './tokens/components/inputgroup'
import { megamenuCss } from './tokens/components/megamenu';
import { selectCss } from './tokens/components/select';
import { messageCss } from './tokens/components/message';
Expand Down Expand Up @@ -59,6 +60,10 @@ const presetTokens: Preset<AuraBaseDesignTokens> = {
inputmask: {
css: inputmaskCss,
},
inputgroup: {
...(tokens.components.inputgroup as unknown as ComponentsDesignTokens['inputgroup']),
css: inputgroupCss,
},
tag: {
...(tokens.components.tag as unknown as ComponentsDesignTokens['tag']),
css: tagCss,
Expand Down
100 changes: 100 additions & 0 deletions src/prime-preset/tokens/components/inputgroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
export const inputgroupCss = ({ dt }: { dt: (token: string) => string }): string => `

/* ─── Корректировка flex-layout через Angular-обёртки ─── */

/*
* display: contents делает враппер-элементы прозрачными в layout-дереве:
* p-inputgroupaddon и input.p-inputtext становятся прямыми flex-элементами
* p-inputgroup. Это позволяет:
* - align-items: stretch работать напрямую → правильная высота аддона
* - границам быть на одном уровне → нет удвоения top/bottom border
* - flex: 1 1 auto; width: 1% на input работать нативно через PrimeNG
* CSS-селекторы по-прежнему работают (DOM-дерево не меняется).
*/
.p-inputgroup > input-group-addon {
display: contents;
}

.p-inputgroup > input-text {
display: contents;
}

/* ─── Корректировка border-radius и границ ─── */

/*
* p-inputgroupaddon является :first-child И :last-child своего прямого родителя
* input-group-addon, поэтому PrimeNG добавляет ему оба inline-бордера.
* Сбрасываем их и переназначаем по позиции аддона в группе.
*/
.p-inputgroup > input-group-addon > .p-inputgroupaddon {
border-radius: 0;
border-inline-start: none;
border-inline-end: none;
}

/* Первый элемент группы — аддон: левые углы + левая граница */
.p-inputgroup > input-group-addon:first-child > .p-inputgroupaddon {
border-start-start-radius: ${dt('inputgroup.addon.borderRadius')};
border-end-start-radius: ${dt('inputgroup.addon.borderRadius')};
border-inline-start: ${dt('inputgroup.extend.borderWidth')} solid ${dt('inputgroup.addon.borderColor')};
}

/* Последний элемент группы — аддон: правые углы + правая граница */
.p-inputgroup > input-group-addon:last-child > .p-inputgroupaddon {
border-start-end-radius: ${dt('inputgroup.addon.borderRadius')};
border-end-end-radius: ${dt('inputgroup.addon.borderRadius')};
border-inline-end: ${dt('inputgroup.extend.borderWidth')} solid ${dt('inputgroup.addon.borderColor')};
}

/* Аддон сразу после другого аддона: левая граница как разделитель */
.p-inputgroup > input-group-addon + input-group-addon > .p-inputgroupaddon {
border-inline-start: ${dt('inputgroup.extend.borderWidth')} solid ${dt('inputgroup.addon.borderColor')};
}

/* Сброс border-radius у input внутри input-text-обёртки */
.p-inputgroup > input-text .p-inputtext {
border-radius: 0;
margin: 0;
}

/* Аддон: только горизонтальные границы (top/bottom), inline-бордеры управляются позиционно */
.p-inputgroup > input-group-addon > .p-inputgroupaddon {
border-block-start: ${dt('inputgroup.extend.borderWidth')} solid ${dt('inputgroup.addon.borderColor')};
border-block-end: ${dt('inputgroup.extend.borderWidth')} solid ${dt('inputgroup.addon.borderColor')};
}

/* Первый элемент группы — input: левые углы */
.p-inputgroup > input-text:first-child .p-inputtext {
border-start-start-radius: ${dt('inputgroup.addon.borderRadius')};
border-end-start-radius: ${dt('inputgroup.addon.borderRadius')};
}

/* Последний элемент группы — input: правые углы */
.p-inputgroup > input-text:last-child .p-inputtext {
border-start-end-radius: ${dt('inputgroup.addon.borderRadius')};
border-end-end-radius: ${dt('inputgroup.addon.borderRadius')};
}

/* ─── Addon в disabled состоянии ─── */
.p-inputgroup:has(input[disabled]) .p-inputgroupaddon,
.p-inputgroup:has(.p-inputtext[disabled]) .p-inputgroupaddon,
.p-inputgroup:has(.p-component[disabled]) .p-inputgroupaddon {
background: ${dt('inputtext.root.disabledBackground')};
color: ${dt('inputtext.root.disabledColor')};
}

/* ─── Иконка внутри addon ─── */
.p-inputgroup .p-inputgroupaddon i {
font-size: ${dt('inputgroup.extend.iconSize')};
}

/* ─── Extra Large ─── */
.p-inputgroup.p-inputgroup-xlg .p-inputgroupaddon {
font-size: ${dt('inputtext.extend.extXlg.fontSize')};
padding: ${dt('inputtext.extend.extXlg.paddingY')} ${dt('inputtext.extend.extXlg.paddingX')};
}

.p-inputgroup.p-inputgroup-xlg .p-inputgroupaddon i {
font-size: ${dt('inputtext.extend.extXlg.fontSize')};
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Component } from '@angular/core';
import { StoryObj } from '@storybook/angular';
import { FormsModule } from '@angular/forms';
import { InputGroupComponent } from '../../../../lib/components/inputgroup/input-group.component';
import { InputGroupAddonComponent } from '../../../../lib/components/inputgroup/input-group-addon.component';
import { InputTextComponent } from '../../../../lib/components/inputtext/inputtext.component';

const template = `
<div class="bg-surface-ground p-4">
<input-group>
<input-group-addon><i class="ti ti-map-pin"></i></input-group-addon>
<input-text placeholder="Введите адрес..." [(ngModel)]="value" [fluid]="true"></input-text>
<input-group-addon><i class="ti ti-search"></i></input-group-addon>
</input-group>
</div>
`;
const styles = '';

@Component({
selector: 'app-inputgroup-addon-both',
standalone: true,
imports: [InputGroupComponent, InputGroupAddonComponent, InputTextComponent, FormsModule],
template,
styles,
})
export class InputGroupAddonBothComponent {
value = '';
}

export const AddonBoth: StoryObj = {
render: () => ({
template: `<app-inputgroup-addon-both></app-inputgroup-addon-both>`,
}),
parameters: {
docs: {
description: { story: 'Аддоны расположены с обеих сторон — например, иконка-префикс и кнопка поиска.' },
source: {
language: 'ts',
code: `
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { InputGroupComponent, InputGroupAddonComponent, InputTextComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-inputgroup-addon-both',
standalone: true,
imports: [InputGroupComponent, InputGroupAddonComponent, InputTextComponent, FormsModule],
template: \`
<input-group>
<input-group-addon><i class="ti ti-map-pin"></i></input-group-addon>
<input-text placeholder="Введите адрес..." [(ngModel)]="value" [fluid]="true"></input-text>
<input-group-addon><i class="ti ti-search"></i></input-group-addon>
</input-group>
\`,
})
export class InputGroupAddonBothComponent {
value = '';
}
`,
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Component } from '@angular/core';
import { StoryObj } from '@storybook/angular';
import { FormsModule } from '@angular/forms';
import { InputGroupComponent } from '../../../../lib/components/inputgroup/input-group.component';
import { InputGroupAddonComponent } from '../../../../lib/components/inputgroup/input-group-addon.component';
import { InputTextComponent } from '../../../../lib/components/inputtext/inputtext.component';

const template = `
<div class="bg-surface-ground p-4">
<input-group>
<input-text placeholder="Сумма" [(ngModel)]="value" [fluid]="true"></input-text>
<input-group-addon>руб.</input-group-addon>
</input-group>
</div>
`;
const styles = '';

@Component({
selector: 'app-inputgroup-addon-right',
standalone: true,
imports: [InputGroupComponent, InputGroupAddonComponent, InputTextComponent, FormsModule],
template,
styles,
})
export class InputGroupAddonRightComponent {
value = '';
}

export const AddonRight: StoryObj = {
render: () => ({
template: `<app-inputgroup-addon-right></app-inputgroup-addon-right>`,
}),
parameters: {
docs: {
description: { story: 'Аддон расположен справа — используется для единиц измерения, валюты или суффиксов.' },
source: {
language: 'ts',
code: `
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { InputGroupComponent, InputGroupAddonComponent, InputTextComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-inputgroup-addon-right',
standalone: true,
imports: [InputGroupComponent, InputGroupAddonComponent, InputTextComponent, FormsModule],
template: \`
<input-group>
<input-text placeholder="Сумма" [(ngModel)]="value" [fluid]="true"></input-text>
<input-group-addon>руб.</input-group-addon>
</input-group>
\`,
})
export class InputGroupAddonRightComponent {
value = '';
}
`,
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Component } from '@angular/core';
import { StoryObj } from '@storybook/angular';
import { FormsModule } from '@angular/forms';
import { InputGroupComponent } from '../../../../lib/components/inputgroup/input-group.component';
import { InputGroupAddonComponent } from '../../../../lib/components/inputgroup/input-group-addon.component';
import { InputTextComponent } from '../../../../lib/components/inputtext/inputtext.component';

const template = `
<div class="bg-surface-ground p-4">
<input-group>
<input-group-addon><i class="ti ti-user"></i></input-group-addon>
<input-text placeholder="Введите данные..." [disabled]="true" [(ngModel)]="value" [fluid]="true"></input-text>
</input-group>
</div>
`;
const styles = '';

@Component({
selector: 'app-inputgroup-disabled',
standalone: true,
imports: [InputGroupComponent, InputGroupAddonComponent, InputTextComponent, FormsModule],
template,
styles,
})
export class InputGroupDisabledComponent {
value = '';
}

export const Disabled: StoryObj = {
render: () => ({
template: `<app-inputgroup-disabled></app-inputgroup-disabled>`,
}),
parameters: {
docs: {
description: { story: 'Отключённое состояние — аддоны автоматически получают стили disabled вместе с полем ввода.' },
source: {
language: 'ts',
code: `
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { InputGroupComponent, InputGroupAddonComponent, InputTextComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-inputgroup-disabled',
standalone: true,
imports: [InputGroupComponent, InputGroupAddonComponent, InputTextComponent, FormsModule],
template: \`
<input-group>
<input-group-addon><i class="ti ti-user"></i></input-group-addon>
<input-text placeholder="Введите данные..." [disabled]="true" [(ngModel)]="value" [fluid]="true"></input-text>
</input-group>
\`,
})
export class InputGroupDisabledComponent {
value = '';
}
`,
},
},
},
};
Loading
Loading