Skip to content
Merged
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
71 changes: 42 additions & 29 deletions src/lib/components/select-button/select-button.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { Component, EventEmitter, Input, Optional, Output, Self } from '@angular/core';
import { NgClass } from '@angular/common';
import { ControlValueAccessor, FormsModule, NgControl } from '@angular/forms';
import { SelectButton } from 'primeng/selectbutton';
import { SelectButton, SelectButtonChangeEvent } from 'primeng/selectbutton';
import { SharedModule } from 'primeng/api';

export interface ExtraSelectButtonItem {
label: string;
value: string;
export interface ExtraSelectButtonOption {
name: string;
code: string | number;
icon?: string;
disabled?: boolean;
}

export interface ExtraSelectButtonChangeEvent {
value: any;
originalEvent?: Event;
}

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

@Component({
selector: 'extra-select-button',
standalone: true,
Expand All @@ -20,13 +27,15 @@ export interface ExtraSelectButtonItem {
[options]="options"
[ngModel]="value"
(ngModelChange)="onValueChange($event)"
(onChange)="onChangeHandler($event)"
[optionLabel]="optionLabel"
[optionValue]="optionValue"
[optionDisabled]="optionDisabled"
[multiple]="multiple"
[allowEmpty]="allowEmpty"
[disabled]="isDisabled"
[ngClass]="sizeClass"
[size]="primeSize"
[styleClass]="primeStyleClass"
>
<ng-template pTemplate="item" let-item>
@if ($any(item)['icon']) {
Expand All @@ -38,21 +47,21 @@ export interface ExtraSelectButtonItem {
`,
})
export class ExtraSelectButtonComponent implements ControlValueAccessor {
@Input() options: unknown[] = [];
@Input() optionLabel = 'label';
@Input() optionValue = 'value';
@Input() options: ExtraSelectButtonOption[] | any[] = [];
@Input() optionLabel = 'name';
@Input() optionValue = 'code';
@Input() optionDisabled = 'disabled';
@Input() size: 'base' | 'small' | 'large' | 'xlarge' = 'base';
@Input() size: ExtraSelectButtonSize = 'base';
@Input() multiple = false;
@Input() allowEmpty = true;

@Output() valueChange = new EventEmitter<string | string[]>();
@Output() onChange = new EventEmitter<ExtraSelectButtonChangeEvent>();

value: string | string[] = '';
value: any = null;

private _disabled = false;
private onChange = (_: string | string[]) => {};
private onTouched = () => {};
private _onChange = (_: any) => {};
private _onTouched = () => {};

constructor(@Optional() @Self() private ngControl: NgControl) {
if (ngControl) ngControl.valueAccessor = this;
Expand All @@ -62,35 +71,39 @@ export class ExtraSelectButtonComponent implements ControlValueAccessor {
return this._disabled;
}

get sizeClass(): string {
const sizeMap: Record<string, string> = {
small: 'p-selectbutton-small',
large: 'p-selectbutton-large',
xlarge: 'p-selectbutton-xlarge',
};
return sizeMap[this.size] ?? '';
get primeSize(): 'small' | 'large' | undefined {
if (this.size === 'small') return 'small';
if (this.size === 'large') return 'large';
return undefined;
}

writeValue(value: string | string[]): void {
this.value = value ?? '';
get primeStyleClass(): string {
return this.size === 'xlarge' ? 'p-selectbutton-xlarge' : '';
}

registerOnChange(fn: (value: string | string[]) => void): void {
this.onChange = fn;
writeValue(value: any): void {
this.value = value ?? null;
}

registerOnChange(fn: (value: any) => void): void {
this._onChange = fn;
}

registerOnTouched(fn: () => void): void {
this.onTouched = fn;
this._onTouched = fn;
}

setDisabledState(isDisabled: boolean): void {
this._disabled = isDisabled;
}

onValueChange(newValue: string | string[]): void {
onValueChange(newValue: any): void {
this.value = newValue;
this.onChange(newValue);
this.onTouched();
this.valueChange.emit(newValue);
this._onChange(newValue);
this._onTouched();
}

onChangeHandler(event: SelectButtonChangeEvent): void {
this.onChange.emit({ value: event.value, originalEvent: event.originalEvent });
}
}
2 changes: 1 addition & 1 deletion src/lib/components/select-button/select-button.figma.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ updated: '2026-06-22'
| `optionLabel` | `string` | `'label'` | Имя поля объекта опции, из которого берётся подпись сегмента |
| `optionValue` | `string` | `'value'` | Имя поля объекта опции, из которого берётся значение модели |
| `optionDisabled` | `string` | `'disabled'` | Имя поля объекта опции, отключающего отдельный сегмент |
| `size` | `'small' \| 'base' \| 'large' \| 'xLarge'` | `'base'` | Размер контрола |
| `size` | `'small' \| 'base' \| 'large' \| 'xlarge'` | `'base'` | Размер контрола |
| `multiple` | `boolean` | `false` | Множественный выбор: модель — массив значений вместо одиночного значения |
| `allowEmpty` | `boolean` | `true` | Разрешает снять выбор (пустое значение) повторным нажатием на активный сегмент |
| `disabled` | `boolean` | `false` | Отключённое состояние всего контрола — соответствует Figma-свойству `state=disabled`; задаётся через `[disabled]` или `setDisabledState` формы |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { StoryObj } from '@storybook/angular';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '../../../../lib/components/select-button/select-button.component';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '../../../../lib/components/select-button/select-button.component';

const template = `
<div class="bg-surface-ground p-4">
Expand All @@ -19,10 +19,10 @@ const styles = '';
})
export class SelectButtonDisabledComponent {
control = new FormControl({ value: '1', disabled: true });
options: ExtraSelectButtonItem[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
options: ExtraSelectButtonOption[] = [
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2' },
{ name: 'Option 3', code: '3' },
];
}

Expand All @@ -40,7 +40,7 @@ export const Disabled: StoryObj = {
code: `
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angular-ui-kit';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-select-button-disabled',
Expand All @@ -52,10 +52,10 @@ import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angu
})
export class SelectButtonDisabledComponent {
control = new FormControl({ value: '1', disabled: true });
options: ExtraSelectButtonItem[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
options: ExtraSelectButtonOption[] = [
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2' },
{ name: 'Option 3', code: '3' },
];
}
`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { StoryObj } from '@storybook/angular';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '../../../../lib/components/select-button/select-button.component';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '../../../../lib/components/select-button/select-button.component';

const template = `
<div class="bg-surface-ground p-4">
Expand All @@ -19,10 +19,10 @@ const styles = '';
})
export class SelectButtonIconsComponent {
control = new FormControl('left');
options: ExtraSelectButtonItem[] = [
{ label: 'Left', value: 'left', icon: 'ti ti-align-left' },
{ label: 'Center', value: 'center', icon: 'ti ti-align-center' },
{ label: 'Right', value: 'right', icon: 'ti ti-align-right' },
options: ExtraSelectButtonOption[] = [
{ name: 'Left', code: 'left', icon: 'ti ti-align-left' },
{ name: 'Center', code: 'center', icon: 'ti ti-align-center' },
{ name: 'Right', code: 'right', icon: 'ti ti-align-right' },
];
}

Expand All @@ -40,7 +40,7 @@ export const WithIcons: StoryObj = {
code: `
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angular-ui-kit';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-select-button-icons',
Expand All @@ -52,10 +52,10 @@ import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angu
})
export class SelectButtonIconsComponent {
control = new FormControl('left');
options: ExtraSelectButtonItem[] = [
{ label: 'Left', value: 'left', icon: 'ti ti-align-left' },
{ label: 'Center', value: 'center', icon: 'ti ti-align-center' },
{ label: 'Right', value: 'right', icon: 'ti ti-align-right' },
options: ExtraSelectButtonOption[] = [
{ name: 'Left', code: 'left', icon: 'ti ti-align-left' },
{ name: 'Center', code: 'center', icon: 'ti ti-align-center' },
{ name: 'Right', code: 'right', icon: 'ti ti-align-right' },
];
}
`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { StoryObj } from '@storybook/angular';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '../../../../lib/components/select-button/select-button.component';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '../../../../lib/components/select-button/select-button.component';

const template = `
<div class="bg-surface-ground p-4">
Expand All @@ -19,10 +19,10 @@ const styles = '';
})
export class SelectButtonSelectedComponent {
control = new FormControl('2');
options: ExtraSelectButtonItem[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
options: ExtraSelectButtonOption[] = [
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2' },
{ name: 'Option 3', code: '3' },
];
}

Expand All @@ -40,7 +40,7 @@ export const Selected: StoryObj = {
code: `
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angular-ui-kit';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-select-button-selected',
Expand All @@ -52,10 +52,10 @@ import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angu
})
export class SelectButtonSelectedComponent {
control = new FormControl('2');
options: ExtraSelectButtonItem[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
options: ExtraSelectButtonOption[] = [
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2' },
{ name: 'Option 3', code: '3' },
];
}
`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { StoryObj } from '@storybook/angular';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '../../../../lib/components/select-button/select-button.component';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '../../../../lib/components/select-button/select-button.component';

const template = `
<div class="bg-surface-ground p-4">
Expand All @@ -19,10 +19,10 @@ const styles = '';
})
export class SelectButtonSemiDisabledComponent {
control = new FormControl('1');
options: ExtraSelectButtonItem[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2', disabled: true },
{ label: 'Option 3', value: '3' },
options: ExtraSelectButtonOption[] = [
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2', disabled: true },
{ name: 'Option 3', code: '3' },
];
}

Expand All @@ -40,7 +40,7 @@ export const SemiDisabled: StoryObj = {
code: `
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angular-ui-kit';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-select-button-semi-disabled',
Expand All @@ -52,10 +52,10 @@ import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angu
})
export class SelectButtonSemiDisabledComponent {
control = new FormControl('1');
options: ExtraSelectButtonItem[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2', disabled: true },
{ label: 'Option 3', value: '3' },
options: ExtraSelectButtonOption[] = [
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2', disabled: true },
{ name: 'Option 3', code: '3' },
];
}
`,
Expand Down
14 changes: 7 additions & 7 deletions src/stories/components/select-button/select-button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const meta: Meta<SelectButtonArgs> = {
component: `Группа кнопок-переключателей с поддержкой одиночного и множественного выбора.

\`\`\`typescript
import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angular-ui-kit';
import { ExtraSelectButtonComponent, ExtraSelectButtonOption } from '@cdek-it/angular-ui-kit';
\`\`\``,
},
},
Expand All @@ -45,12 +45,12 @@ import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angu
},
size: {
control: 'radio',
options: ['base', 'small', 'large', 'xlarge'],
options: ['small', 'base', 'large', 'xlarge'],
description: 'Размер компонента',
table: {
category: 'Props',
defaultValue: { summary: 'base' },
type: { summary: "'base' | 'small' | 'large' | 'xlarge'" },
type: { summary: "'small' | 'base' | 'large' | 'xlarge'" },
},
},
multiple: {
Expand All @@ -76,7 +76,7 @@ import { ExtraSelectButtonComponent, ExtraSelectButtonItem } from '@cdek-it/angu
description: 'Массив опций',
table: {
category: 'Props',
type: { summary: 'ExtraSelectButtonItem[]' },
type: { summary: 'ExtraSelectButtonOption[]' },
},
},
},
Expand Down Expand Up @@ -105,9 +105,9 @@ export const Default: Story = {
args: {
value: '1',
options: [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
{ name: 'Option 1', code: '1' },
{ name: 'Option 2', code: '2' },
{ name: 'Option 3', code: '3' },
],
size: 'base',
multiple: false,
Expand Down
Loading