From 432fe101f654ad5ac0e48246498a00b1a4561ba8 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Tue, 7 Apr 2026 20:22:00 +0700 Subject: [PATCH 1/4] =?UTF-8?q?toggleswitch:=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F,=20=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=81=D1=8B,=20=D0=BE=D0=B1=D1=91=D1=80=D1=82?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toggleswitch/toggleswitch.component.ts | 62 ++++++ src/prime-preset/map-tokens.ts | 5 + .../tokens/components/toggleswitch.ts | 11 + .../toggleswitch-checked.component.ts | 49 +++++ .../toggleswitch-disabled.component.ts | 49 +++++ .../toggleswitch-invalid.component.ts | 49 +++++ .../toggleswitch/toggleswitch.stories.ts | 208 ++++++++++++++++++ 7 files changed, 433 insertions(+) create mode 100644 src/lib/components/toggleswitch/toggleswitch.component.ts create mode 100644 src/prime-preset/tokens/components/toggleswitch.ts create mode 100644 src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts create mode 100644 src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts create mode 100644 src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts create mode 100644 src/stories/components/toggleswitch/toggleswitch.stories.ts diff --git a/src/lib/components/toggleswitch/toggleswitch.component.ts b/src/lib/components/toggleswitch/toggleswitch.component.ts new file mode 100644 index 0000000..8ab8b2b --- /dev/null +++ b/src/lib/components/toggleswitch/toggleswitch.component.ts @@ -0,0 +1,62 @@ +import { Component, EventEmitter, Input, Output, forwardRef } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ToggleSwitch } from 'primeng/toggleswitch'; + +@Component({ + selector: 'toggleswitch', + standalone: true, + imports: [ToggleSwitch, FormsModule], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ToggleSwitchComponent), + multi: true, + }, + ], + template: ` + + `, +}) +export class ToggleSwitchComponent implements ControlValueAccessor { + @Input() invalid = false; + @Input() disabled = false; + + @Output() onChange = new EventEmitter(); + @Output() onFocus = new EventEmitter(); + @Output() onBlur = new EventEmitter(); + + modelValue = false; + + private _onChange: (value: boolean) => void = () => {}; + private _onTouched: () => void = () => {}; + + handleChange(value: boolean): void { + this.modelValue = value; + this._onChange(value); + this._onTouched(); + } + + writeValue(value: boolean): void { + this.modelValue = value ?? false; + } + + registerOnChange(fn: (value: boolean) => void): void { + this._onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this._onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } +} diff --git a/src/prime-preset/map-tokens.ts b/src/prime-preset/map-tokens.ts index 3962758..97bde3c 100644 --- a/src/prime-preset/map-tokens.ts +++ b/src/prime-preset/map-tokens.ts @@ -4,6 +4,7 @@ import type { AuraBaseDesignTokens } from '@primeuix/themes/aura/base'; import tokens from './tokens/tokens.json'; import { buttonCss } from './tokens/components/button'; +import { toggleswitchCss } from './tokens/components/toggleswitch'; const presetTokens: Preset = { primitive: tokens.primitive as unknown as AuraBaseDesignTokens['primitive'], @@ -14,6 +15,10 @@ const presetTokens: Preset = { ...(tokens.components.button as unknown as ComponentsDesignTokens['button']), css: buttonCss, }, + toggleswitch: { + ...(tokens.components.toggleswitch as unknown as ComponentsDesignTokens['toggleswitch']), + css: toggleswitchCss, + }, } as ComponentsDesignTokens, }; diff --git a/src/prime-preset/tokens/components/toggleswitch.ts b/src/prime-preset/tokens/components/toggleswitch.ts new file mode 100644 index 0000000..8793c20 --- /dev/null +++ b/src/prime-preset/tokens/components/toggleswitch.ts @@ -0,0 +1,11 @@ +export const toggleswitchCss = ({ dt }: { dt: (token: string) => string }): string => ` + /* Focus ring для валидных состояний */ + .p-toggleswitch:not(.p-disabled):not(.p-invalid):has(.p-toggleswitch-input:focus-visible) .p-toggleswitch-slider { + box-shadow: 0 0 0 ${dt('toggleswitch.root.focusRing.width')} ${dt('focusRing.extend.success')}; + } + + /* Focus ring для состояния ошибки */ + .p-toggleswitch.p-invalid:not(.p-disabled):has(.p-toggleswitch-input:focus-visible) .p-toggleswitch-slider { + box-shadow: 0 0 0 ${dt('focusRing.width')} ${dt('focusRing.extend.invalid')}; + } +`; diff --git a/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts b/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts new file mode 100644 index 0000000..3e9275e --- /dev/null +++ b/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts @@ -0,0 +1,49 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { StoryObj } from '@storybook/angular'; +import { ToggleSwitchComponent } from '../../../../lib/components/toggleswitch/toggleswitch.component'; + +const template = ` + +`; + +@Component({ + selector: 'app-toggleswitch-checked', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template, +}) +export class ToggleSwitchCheckedComponent { + value = true; +} + +export const Checked: StoryObj = { + render: () => ({ + template: ``, + }), + parameters: { + docs: { + description: { story: 'Переключатель во включённом состоянии.' }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-toggleswitch-checked', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template: \` + + \`, +}) +export class ToggleSwitchCheckedComponent { + value = true; +} + `, + }, + }, + }, +}; diff --git a/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts b/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts new file mode 100644 index 0000000..09db1a3 --- /dev/null +++ b/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts @@ -0,0 +1,49 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { StoryObj } from '@storybook/angular'; +import { ToggleSwitchComponent } from '../../../../lib/components/toggleswitch/toggleswitch.component'; + +const template = ` + +`; + +@Component({ + selector: 'app-toggleswitch-disabled', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template, +}) +export class ToggleSwitchDisabledComponent { + value = false; +} + +export const Disabled: StoryObj = { + render: () => ({ + template: ``, + }), + parameters: { + docs: { + description: { story: 'Заблокированное состояние переключателя.' }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-toggleswitch-disabled', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template: \` + + \`, +}) +export class ToggleSwitchDisabledComponent { + value = false; +} + `, + }, + }, + }, +}; diff --git a/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts b/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts new file mode 100644 index 0000000..8c51a23 --- /dev/null +++ b/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts @@ -0,0 +1,49 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { StoryObj } from '@storybook/angular'; +import { ToggleSwitchComponent } from '../../../../lib/components/toggleswitch/toggleswitch.component'; + +const template = ` + +`; + +@Component({ + selector: 'app-toggleswitch-invalid', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template, +}) +export class ToggleSwitchInvalidComponent { + value = false; +} + +export const Invalid: StoryObj = { + render: () => ({ + template: ``, + }), + parameters: { + docs: { + description: { story: 'Невалидное состояние переключателя.' }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-toggleswitch-invalid', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template: \` + + \`, +}) +export class ToggleSwitchInvalidComponent { + value = false; +} + `, + }, + }, + }, +}; diff --git a/src/stories/components/toggleswitch/toggleswitch.stories.ts b/src/stories/components/toggleswitch/toggleswitch.stories.ts new file mode 100644 index 0000000..6eb393e --- /dev/null +++ b/src/stories/components/toggleswitch/toggleswitch.stories.ts @@ -0,0 +1,208 @@ +import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; +import { FormsModule } from '@angular/forms'; +import { ToggleSwitchComponent } from '../../../lib/components/toggleswitch/toggleswitch.component'; +import { ToggleSwitchCheckedComponent } from './examples/toggleswitch-checked.component'; +import { ToggleSwitchInvalidComponent } from './examples/toggleswitch-invalid.component'; +import { ToggleSwitchDisabledComponent } from './examples/toggleswitch-disabled.component'; + +type ToggleSwitchArgs = ToggleSwitchComponent; + +const meta: Meta = { + title: 'Prime/Form/ToggleSwitch', + component: ToggleSwitchComponent, + tags: ['autodocs'], + decorators: [ + moduleMetadata({ + imports: [ + ToggleSwitchComponent, + FormsModule, + ToggleSwitchCheckedComponent, + ToggleSwitchInvalidComponent, + ToggleSwitchDisabledComponent, + ], + }), + ], + parameters: { + designTokens: { prefix: '--p-toggleswitch' }, + docs: { + description: { + component: `Компонент для переключения между двумя состояниями.`, + }, + }, + }, + argTypes: { + // ── Props ──────────────────────────────────────────────── + invalid: { + control: 'boolean', + description: 'Подсвечивает переключатель как невалидный', + table: { + category: 'Props', + defaultValue: { summary: 'false' }, + type: { summary: 'boolean' }, + }, + }, + disabled: { + control: 'boolean', + description: 'Отключает возможность взаимодействия', + table: { + category: 'Props', + defaultValue: { summary: 'false' }, + type: { summary: 'boolean' }, + }, + }, + + // ── Events ─────────────────────────────────────────────── + onChange: { + control: false, + description: 'Событие изменения состояния', + table: { + category: 'Events', + type: { summary: 'EventEmitter' }, + }, + }, + onFocus: { + control: false, + description: 'Событие фокуса', + table: { + category: 'Events', + type: { summary: 'EventEmitter' }, + }, + }, + onBlur: { + control: false, + description: 'Событие потери фокуса', + table: { + category: 'Events', + type: { summary: 'EventEmitter' }, + }, + }, + }, + args: { + invalid: false, + disabled: false, + }, +}; + +export default meta; +type Story = StoryObj; + +const commonTemplate = ` + +`; + +// ── Default ────────────────────────────────────────────────────────────────── +export const Default: Story = { + name: 'Default', + render: (args) => { + const parts: string[] = [`[(ngModel)]="value"`]; + if (args.invalid) parts.push(`[invalid]="true"`); + if (args.disabled) parts.push(`[disabled]="true"`); + + const template = ``; + return { props: { ...args, value: false }, template }; + }, + parameters: { + docs: { + description: { + story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.', + }, + }, + }, +}; + +// ── Checked ─────────────────────────────────────────────────────────────────── +export const Checked: Story = { + render: (args) => ({ props: { ...args, value: true }, template: commonTemplate }), + args: { invalid: false, disabled: false }, + parameters: { + docs: { + description: { story: 'Переключатель во включённом состоянии.' }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-toggleswitch-checked', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template: \` + + \`, +}) +export class ToggleSwitchCheckedComponent { + value = true; +} + `, + }, + }, + }, +}; + +// ── Invalid ─────────────────────────────────────────────────────────────────── +export const Invalid: Story = { + render: (args) => ({ props: { ...args, value: false }, template: commonTemplate }), + args: { invalid: true, disabled: false }, + parameters: { + docs: { + description: { story: 'Невалидное состояние переключателя.' }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-toggleswitch-invalid', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template: \` + + \`, +}) +export class ToggleSwitchInvalidComponent { + value = false; +} + `, + }, + }, + }, +}; + +// ── Disabled ────────────────────────────────────────────────────────────────── +export const Disabled: Story = { + render: (args) => ({ props: { ...args, value: false }, template: commonTemplate }), + args: { invalid: false, disabled: true }, + parameters: { + docs: { + description: { story: 'Заблокированное состояние переключателя.' }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-toggleswitch-disabled', + standalone: true, + imports: [ToggleSwitchComponent, FormsModule], + template: \` + + \`, +}) +export class ToggleSwitchDisabledComponent { + value = false; +} + `, + }, + }, + }, +}; From 5ce287395159155e78e5c107d71e34c48df7ae1c Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Tue, 7 Apr 2026 21:09:05 +0700 Subject: [PATCH 2/4] =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D1=8B=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stories/components/toggleswitch/toggleswitch.stories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stories/components/toggleswitch/toggleswitch.stories.ts b/src/stories/components/toggleswitch/toggleswitch.stories.ts index 6eb393e..90e6e51 100644 --- a/src/stories/components/toggleswitch/toggleswitch.stories.ts +++ b/src/stories/components/toggleswitch/toggleswitch.stories.ts @@ -8,7 +8,7 @@ import { ToggleSwitchDisabledComponent } from './examples/toggleswitch-disabled. type ToggleSwitchArgs = ToggleSwitchComponent; const meta: Meta = { - title: 'Prime/Form/ToggleSwitch', + title: 'Components/Form/ToggleSwitch', component: ToggleSwitchComponent, tags: ['autodocs'], decorators: [ From bfb29948640cdb324965b79d1600d169d07a854f Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Tue, 14 Apr 2026 20:33:20 +0700 Subject: [PATCH 3/4] =?UTF-8?q?toggleswitch:=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20@Input=20invalid/disabled,=20=D1=87=D0=B8=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=B8=D0=B7=20NgControl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toggleswitch/toggleswitch.component.ts | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/lib/components/toggleswitch/toggleswitch.component.ts b/src/lib/components/toggleswitch/toggleswitch.component.ts index 8ab8b2b..7135892 100644 --- a/src/lib/components/toggleswitch/toggleswitch.component.ts +++ b/src/lib/components/toggleswitch/toggleswitch.component.ts @@ -1,24 +1,17 @@ -import { Component, EventEmitter, Input, Output, forwardRef } from '@angular/core'; -import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Component, EventEmitter, Optional, Output, Self } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NgControl } from '@angular/forms'; import { ToggleSwitch } from 'primeng/toggleswitch'; @Component({ selector: 'toggleswitch', standalone: true, imports: [ToggleSwitch, FormsModule], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ToggleSwitchComponent), - multi: true, - }, - ], template: ` (); @Output() onFocus = new EventEmitter(); @Output() onBlur = new EventEmitter(); modelValue = false; + private _disabled = false; private _onChange: (value: boolean) => void = () => {}; private _onTouched: () => void = () => {}; + constructor(@Optional() @Self() private ngControl: NgControl) { + if (ngControl) { + ngControl.valueAccessor = this; + } + } + + get isDisabled(): boolean { + return this._disabled; + } + + get isInvalid(): boolean { + return !!this.ngControl?.invalid; + } + handleChange(value: boolean): void { this.modelValue = value; this._onChange(value); @@ -57,6 +62,6 @@ export class ToggleSwitchComponent implements ControlValueAccessor { } setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; + this._disabled = isDisabled; } } From 413a9038a0f698dff041ee548a74cb2d1a9a5057 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Tue, 14 Apr 2026 20:33:24 +0700 Subject: [PATCH 4/4] =?UTF-8?q?toggleswitch:=20stories=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0?= =?UTF-8?q?=20FormControl=20=D0=B8=20Validators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toggleswitch-checked.component.ts | 22 ++- .../toggleswitch-disabled.component.ts | 24 ++-- .../toggleswitch-invalid.component.ts | 25 ++-- .../toggleswitch/toggleswitch.stories.ts | 127 +++++++----------- 4 files changed, 83 insertions(+), 115 deletions(-) diff --git a/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts b/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts index 3e9275e..b0c6a88 100644 --- a/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts +++ b/src/stories/components/toggleswitch/examples/toggleswitch-checked.component.ts @@ -1,20 +1,18 @@ import { Component } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { StoryObj } from '@storybook/angular'; import { ToggleSwitchComponent } from '../../../../lib/components/toggleswitch/toggleswitch.component'; -const template = ` - -`; - @Component({ selector: 'app-toggleswitch-checked', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], - template, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: ` + + `, }) export class ToggleSwitchCheckedComponent { - value = true; + control = new FormControl(true); } export const Checked: StoryObj = { @@ -28,19 +26,19 @@ export const Checked: StoryObj = { language: 'ts', code: ` import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; -import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-toggleswitch-checked', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], + imports: [ToggleSwitchComponent, ReactiveFormsModule], template: \` - + \`, }) export class ToggleSwitchCheckedComponent { - value = true; + control = new FormControl(true); } `, }, diff --git a/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts b/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts index 09db1a3..7d041f4 100644 --- a/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts +++ b/src/stories/components/toggleswitch/examples/toggleswitch-disabled.component.ts @@ -1,20 +1,18 @@ import { Component } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { StoryObj } from '@storybook/angular'; import { ToggleSwitchComponent } from '../../../../lib/components/toggleswitch/toggleswitch.component'; -const template = ` - -`; - @Component({ selector: 'app-toggleswitch-disabled', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], - template, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: ` + + `, }) export class ToggleSwitchDisabledComponent { - value = false; + control = new FormControl({ value: false, disabled: true }); } export const Disabled: StoryObj = { @@ -23,24 +21,24 @@ export const Disabled: StoryObj = { }), parameters: { docs: { - description: { story: 'Заблокированное состояние переключателя.' }, + description: { story: 'Заблокированное состояние переключателя через FormControl.' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; -import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-toggleswitch-disabled', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], + imports: [ToggleSwitchComponent, ReactiveFormsModule], template: \` - + \`, }) export class ToggleSwitchDisabledComponent { - value = false; + control = new FormControl({ value: false, disabled: true }); } `, }, diff --git a/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts b/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts index 8c51a23..aced21f 100644 --- a/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts +++ b/src/stories/components/toggleswitch/examples/toggleswitch-invalid.component.ts @@ -1,20 +1,19 @@ import { Component } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { StoryObj } from '@storybook/angular'; import { ToggleSwitchComponent } from '../../../../lib/components/toggleswitch/toggleswitch.component'; -const template = ` - -`; - @Component({ selector: 'app-toggleswitch-invalid', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], - template, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: ` + + `, }) export class ToggleSwitchInvalidComponent { - value = false; + // Validators.requiredTrue требует значение true, поэтому false делает контрол невалидным + control = new FormControl(false, [Validators.requiredTrue]); } export const Invalid: StoryObj = { @@ -23,24 +22,24 @@ export const Invalid: StoryObj = { }), parameters: { docs: { - description: { story: 'Невалидное состояние переключателя.' }, + description: { story: 'Невалидное состояние переключателя через FormControl и Validators.' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; -import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-toggleswitch-invalid', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], + imports: [ToggleSwitchComponent, ReactiveFormsModule], template: \` - + \`, }) export class ToggleSwitchInvalidComponent { - value = false; + control = new FormControl(false, [Validators.requiredTrue]); } `, }, diff --git a/src/stories/components/toggleswitch/toggleswitch.stories.ts b/src/stories/components/toggleswitch/toggleswitch.stories.ts index 90e6e51..640568d 100644 --- a/src/stories/components/toggleswitch/toggleswitch.stories.ts +++ b/src/stories/components/toggleswitch/toggleswitch.stories.ts @@ -1,13 +1,11 @@ import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; -import { FormsModule } from '@angular/forms'; +import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { ToggleSwitchComponent } from '../../../lib/components/toggleswitch/toggleswitch.component'; import { ToggleSwitchCheckedComponent } from './examples/toggleswitch-checked.component'; import { ToggleSwitchInvalidComponent } from './examples/toggleswitch-invalid.component'; import { ToggleSwitchDisabledComponent } from './examples/toggleswitch-disabled.component'; -type ToggleSwitchArgs = ToggleSwitchComponent; - -const meta: Meta = { +const meta: Meta = { title: 'Components/Form/ToggleSwitch', component: ToggleSwitchComponent, tags: ['autodocs'], @@ -15,7 +13,7 @@ const meta: Meta = { moduleMetadata({ imports: [ ToggleSwitchComponent, - FormsModule, + ReactiveFormsModule, ToggleSwitchCheckedComponent, ToggleSwitchInvalidComponent, ToggleSwitchDisabledComponent, @@ -26,31 +24,11 @@ const meta: Meta = { designTokens: { prefix: '--p-toggleswitch' }, docs: { description: { - component: `Компонент для переключения между двумя состояниями.`, + component: `Компонент для переключения между двумя состояниями. Состояния \`disabled\` и \`invalid\` управляются через \`FormControl\`, не через пропсы.`, }, }, }, argTypes: { - // ── Props ──────────────────────────────────────────────── - invalid: { - control: 'boolean', - description: 'Подсвечивает переключатель как невалидный', - table: { - category: 'Props', - defaultValue: { summary: 'false' }, - type: { summary: 'boolean' }, - }, - }, - disabled: { - control: 'boolean', - description: 'Отключает возможность взаимодействия', - table: { - category: 'Props', - defaultValue: { summary: 'false' }, - type: { summary: 'boolean' }, - }, - }, - // ── Events ─────────────────────────────────────────────── onChange: { control: false, @@ -77,38 +55,39 @@ const meta: Meta = { }, }, }, - args: { - invalid: false, - disabled: false, - }, }; export default meta; -type Story = StoryObj; - -const commonTemplate = ` - -`; +type Story = StoryObj; // ── Default ────────────────────────────────────────────────────────────────── export const Default: Story = { name: 'Default', - render: (args) => { - const parts: string[] = [`[(ngModel)]="value"`]; - if (args.invalid) parts.push(`[invalid]="true"`); - if (args.disabled) parts.push(`[disabled]="true"`); - - const template = ``; - return { props: { ...args, value: false }, template }; - }, + render: () => ({ + props: { control: new FormControl(false) }, + template: ``, + }), parameters: { docs: { description: { - story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.', + story: 'Базовый пример. Управление значением и состоянием через `FormControl`.', + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + standalone: true, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: \`\`, +}) +export class Example { + control = new FormControl(false); +} + `, }, }, }, @@ -116,8 +95,9 @@ export const Default: Story = { // ── Checked ─────────────────────────────────────────────────────────────────── export const Checked: Story = { - render: (args) => ({ props: { ...args, value: true }, template: commonTemplate }), - args: { invalid: false, disabled: false }, + render: () => ({ + template: ``, + }), parameters: { docs: { description: { story: 'Переключатель во включённом состоянии.' }, @@ -125,19 +105,16 @@ export const Checked: Story = { language: 'ts', code: ` import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; -import { FormsModule } from '@angular/forms'; @Component({ - selector: 'app-toggleswitch-checked', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], - template: \` - - \`, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: \`\`, }) export class ToggleSwitchCheckedComponent { - value = true; + control = new FormControl(true); } `, }, @@ -147,28 +124,26 @@ export class ToggleSwitchCheckedComponent { // ── Invalid ─────────────────────────────────────────────────────────────────── export const Invalid: Story = { - render: (args) => ({ props: { ...args, value: false }, template: commonTemplate }), - args: { invalid: true, disabled: false }, + render: () => ({ + template: ``, + }), parameters: { docs: { - description: { story: 'Невалидное состояние переключателя.' }, + description: { story: 'Невалидное состояние через `FormControl` и `Validators`.' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; -import { FormsModule } from '@angular/forms'; @Component({ - selector: 'app-toggleswitch-invalid', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], - template: \` - - \`, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: \`\`, }) export class ToggleSwitchInvalidComponent { - value = false; + control = new FormControl(false, [Validators.requiredTrue]); } `, }, @@ -178,28 +153,26 @@ export class ToggleSwitchInvalidComponent { // ── Disabled ────────────────────────────────────────────────────────────────── export const Disabled: Story = { - render: (args) => ({ props: { ...args, value: false }, template: commonTemplate }), - args: { invalid: false, disabled: true }, + render: () => ({ + template: ``, + }), parameters: { docs: { - description: { story: 'Заблокированное состояние переключателя.' }, + description: { story: 'Заблокированное состояние через `FormControl`.' }, source: { language: 'ts', code: ` import { Component } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { ToggleSwitchComponent } from '@cdek-it/angular-ui-kit'; -import { FormsModule } from '@angular/forms'; @Component({ - selector: 'app-toggleswitch-disabled', standalone: true, - imports: [ToggleSwitchComponent, FormsModule], - template: \` - - \`, + imports: [ToggleSwitchComponent, ReactiveFormsModule], + template: \`\`, }) export class ToggleSwitchDisabledComponent { - value = false; + control = new FormControl({ value: false, disabled: true }); } `, },