diff --git a/src/lib/components/rating/rating.component.ts b/src/lib/components/rating/rating.component.ts
new file mode 100644
index 00000000..cbb6a242
--- /dev/null
+++ b/src/lib/components/rating/rating.component.ts
@@ -0,0 +1,68 @@
+import { Component, EventEmitter, Input, Output, forwardRef } from '@angular/core';
+import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { Rating } from 'primeng/rating';
+
+export type RatingValue = number | null;
+
+@Component({
+ selector: 'rating',
+ standalone: true,
+ imports: [Rating, FormsModule],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => RatingComponent),
+ multi: true,
+ },
+ ],
+ template: `
+
+ `,
+})
+export class RatingComponent implements ControlValueAccessor {
+ @Input() stars = 5;
+ @Input() readonly = false;
+ @Input() disabled = false;
+ @Input() autofocus = false;
+
+ @Output() onRate = new EventEmitter();
+ @Output() onFocus = new EventEmitter();
+ @Output() onBlur = new EventEmitter();
+
+ modelValue: RatingValue = null;
+
+ private onChange: (value: RatingValue) => void = () => {};
+ private onTouched: () => void = () => {};
+
+ handleChange(value: RatingValue): void {
+ this.modelValue = value;
+ this.onChange(value);
+ this.onTouched();
+ }
+
+ writeValue(value: RatingValue): void {
+ this.modelValue = value;
+ }
+
+ registerOnChange(fn: (value: RatingValue) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+}
diff --git a/src/stories/components/rating/examples/rating-disabled.component.ts b/src/stories/components/rating/examples/rating-disabled.component.ts
new file mode 100644
index 00000000..e2bf475b
--- /dev/null
+++ b/src/stories/components/rating/examples/rating-disabled.component.ts
@@ -0,0 +1,51 @@
+import { Component } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { StoryObj } from '@storybook/angular';
+import { RatingComponent } from '../../../../lib/components/rating/rating.component';
+
+const template = `
+
+
+
+`;
+
+@Component({
+ selector: 'app-rating-disabled',
+ standalone: true,
+ imports: [RatingComponent, FormsModule],
+ template,
+})
+export class RatingDisabledComponent {
+ value = 2;
+}
+
+export const Disabled: StoryObj = {
+ render: () => ({
+ template: ``,
+ }),
+ parameters: {
+ docs: {
+ description: { story: 'Заблокированное состояние — компонент недоступен для взаимодействия.' },
+ source: {
+ language: 'ts',
+ code: `
+import { Component } from '@angular/core';
+import { RatingComponent } from '@cdek-it/angular-ui-kit';
+import { FormsModule } from '@angular/forms';
+
+@Component({
+ selector: 'app-rating-disabled',
+ standalone: true,
+ imports: [RatingComponent, FormsModule],
+ template: \`
+
+ \`,
+})
+export class RatingDisabledComponent {
+ value = 2;
+}
+ `,
+ },
+ },
+ },
+};
diff --git a/src/stories/components/rating/examples/rating-readonly.component.ts b/src/stories/components/rating/examples/rating-readonly.component.ts
new file mode 100644
index 00000000..6b82adaf
--- /dev/null
+++ b/src/stories/components/rating/examples/rating-readonly.component.ts
@@ -0,0 +1,51 @@
+import { Component } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { StoryObj } from '@storybook/angular';
+import { RatingComponent } from '../../../../lib/components/rating/rating.component';
+
+const template = `
+
+
+
+`;
+
+@Component({
+ selector: 'app-rating-readonly',
+ standalone: true,
+ imports: [RatingComponent, FormsModule],
+ template,
+})
+export class RatingReadonlyComponent {
+ value = 4;
+}
+
+export const ReadOnly: StoryObj = {
+ render: () => ({
+ template: ``,
+ }),
+ parameters: {
+ docs: {
+ description: { story: 'Режим только для чтения — значение отображается, но не может быть изменено.' },
+ source: {
+ language: 'ts',
+ code: `
+import { Component } from '@angular/core';
+import { RatingComponent } from '@cdek-it/angular-ui-kit';
+import { FormsModule } from '@angular/forms';
+
+@Component({
+ selector: 'app-rating-readonly',
+ standalone: true,
+ imports: [RatingComponent, FormsModule],
+ template: \`
+
+ \`,
+})
+export class RatingReadonlyComponent {
+ value = 4;
+}
+ `,
+ },
+ },
+ },
+};
diff --git a/src/stories/components/rating/rating.stories.ts b/src/stories/components/rating/rating.stories.ts
new file mode 100644
index 00000000..2d544ebe
--- /dev/null
+++ b/src/stories/components/rating/rating.stories.ts
@@ -0,0 +1,120 @@
+import { Meta, StoryObj, moduleMetadata } from '@storybook/angular';
+import { FormsModule } from '@angular/forms';
+import { RatingComponent } from '../../../lib/components/rating/rating.component';
+import { RatingReadonlyComponent, ReadOnly } from './examples/rating-readonly.component';
+import { RatingDisabledComponent, Disabled } from './examples/rating-disabled.component';
+
+type RatingArgs = RatingComponent;
+
+const meta: Meta = {
+ title: 'Components/Form/Rating',
+ component: RatingComponent,
+ tags: ['autodocs'],
+ decorators: [
+ moduleMetadata({
+ imports: [
+ RatingComponent,
+ FormsModule,
+ RatingReadonlyComponent,
+ RatingDisabledComponent,
+ ],
+ }),
+ ],
+ parameters: {
+ designTokens: { prefix: '--p-rating' },
+ docs: {
+ description: {
+ component: `Компонент для отображения и выбора оценки в виде звёзд.`,
+ },
+ },
+ },
+ argTypes: {
+ // ── Props ────────────────────────────────────────────────
+ stars: {
+ control: 'number',
+ description: 'Количество отображаемых звёзд',
+ table: {
+ category: 'Props',
+ defaultValue: { summary: '5' },
+ type: { summary: 'number' },
+ },
+ },
+ readonly: {
+ 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' },
+ },
+ },
+ autofocus: { table: { disable: true } },
+
+ // ── Events ───────────────────────────────────────────────
+ onRate: {
+ 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: {
+ stars: 5,
+ readonly: false,
+ disabled: false,
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+// ── Default ──────────────────────────────────────────────────────────────────
+export const Default: Story = {
+ name: 'Default',
+ render: (args) => {
+ const parts: string[] = [`[(ngModel)]="value"`];
+ if (args.stars !== 5) parts.push(`[stars]="${args.stars}"`);
+ if (args.readonly) parts.push(`[readonly]="true"`);
+ if (args.disabled) parts.push(`[disabled]="true"`);
+
+ const template = ``;
+ return { props: { ...args, value: 3 }, template };
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.',
+ },
+ },
+ },
+};
+
+// ── Re-exports from example components ────────────────────────────────────
+export { ReadOnly, Disabled };