From d199df911b1b5c431a1fc3e4a19e6071c52dbcb4 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Wed, 15 Apr 2026 14:28:17 +0700 Subject: [PATCH 1/8] =?UTF-8?q?toast:=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F,=20=D1=81=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D1=81=D1=8B,=20=D0=BE=D0=B1=D1=91=D1=80=D1=82=D0=BA?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/CLAUDE-v1.6.md | 751 ------------------ src/lib/components/toast/toast.component.ts | 51 ++ src/prime-preset/map-tokens.ts | 6 + src/prime-preset/tokens/components/toast.ts | 144 ++++ .../toast/examples/toast-default.component.ts | 79 ++ .../examples/toast-position.component.ts | 83 ++ .../examples/toast-severities.component.ts | 117 +++ .../toast/examples/toast-width.component.ts | 97 +++ .../toast-with-close-button.component.ts | 105 +++ .../examples/toast-with-content.component.ts | 123 +++ src/stories/components/toast/toast.stories.ts | 123 +++ 11 files changed, 928 insertions(+), 751 deletions(-) delete mode 100644 .claude/CLAUDE-v1.6.md create mode 100644 src/lib/components/toast/toast.component.ts create mode 100644 src/prime-preset/tokens/components/toast.ts create mode 100644 src/stories/components/toast/examples/toast-default.component.ts create mode 100644 src/stories/components/toast/examples/toast-position.component.ts create mode 100644 src/stories/components/toast/examples/toast-severities.component.ts create mode 100644 src/stories/components/toast/examples/toast-width.component.ts create mode 100644 src/stories/components/toast/examples/toast-with-close-button.component.ts create mode 100644 src/stories/components/toast/examples/toast-with-content.component.ts create mode 100644 src/stories/components/toast/toast.stories.ts diff --git a/.claude/CLAUDE-v1.6.md b/.claude/CLAUDE-v1.6.md deleted file mode 100644 index 2cf230a4..00000000 --- a/.claude/CLAUDE-v1.6.md +++ /dev/null @@ -1,751 +0,0 @@ -# CLAUDE.md — Angular UI Kit (@cdek-it/angular-ui-kit) - -Инструкции для Claude Code по генерации компонентов, обёрток и сторисов. - ---- - -## Стек и версии - -| Технология | Версия | -|----------------|---------| -| Angular | 20 | -| PrimeNG | 20 | -| Storybook | 10 | -| Tailwind CSS | 3 | -| TypeScript | 5 | - ---- - -## Структура проекта - -``` -src/ -├── lib/ -│ └── components/ -│ └── {name}/ -│ └── {name}.component.ts ← компонент-обёртка -├── stories/ -│ └── components/ -│ └── {name}/ -│ ├── {name}.stories.ts ← сторисы -│ └── examples/ ← примеры для сторисов -├── prime-preset/ -│ └── tokens/ -│ └── components/ -│ └── {name}.ts ← CSS-токены компонента -└── styles.scss ← Tailwind + иконки + шрифты -``` - ---- - -## Паттерн компонента-обёртки - -Каждый компонент — standalone Angular-компонент, оборачивающий PrimeNG. - -### Правила - -1. Файл: `src/lib/components/{name}/{name}.component.ts` -2. `selector` — с приставкой `extra-` + имя компонента строчными буквами (например `selector: 'extra-button'`) -3. Импортировать PrimeNG-компонент и указать его в `imports: []` -4. Для каждого типа Input создавать отдельный `type`-алиас -5. Все `@Input()` отражают **свой** API обёртки, не PrimeNG напрямую -6. Computed-геттеры маппят API обёртки → PrimeNG API -7. Шаблон компонента использует только геттеры, не сырые инпуты - -### Эталон — ButtonComponent - -```typescript -import { Component, Input } from '@angular/core'; -import { Button, ButtonSeverity as PrimeButtonSeverity } from 'primeng/button'; - -// Типы — отдельные алиасы, не inline union -export type ButtonVariant = 'primary' | 'secondary' | 'outlined' | 'text' | 'link'; -export type ButtonSeverity = 'success' | 'warning' | 'danger' | 'info' | null; -export type ButtonSize = 'small' | 'base' | 'large' | 'xlarge'; -export type ButtonIconPos = 'prefix' | 'postfix' | null; -export type BadgeSeverity = 'success' | 'info' | 'warning' | 'danger' | 'secondary' | 'contrast' | null; - -@Component({ - selector: 'extra-button', - standalone: true, - imports: [Button], - template: ` - - ` -}) -export class ButtonComponent { - @Input() label = 'Button'; - @Input() variant: ButtonVariant = 'primary'; - @Input() severity: ButtonSeverity = null; - @Input() size: ButtonSize = 'base'; - @Input() rounded = false; - @Input() iconPos: ButtonIconPos = null; - @Input() iconOnly = false; - @Input() icon = ''; - @Input() disabled = false; - @Input() loading = false; - @Input() badge = ''; - @Input() badgeSeverity: BadgeSeverity = null; - @Input() showBadge = false; - @Input() fluid = false; - @Input() ariaLabel: string | undefined = undefined; - @Input() autofocus = false; - @Input() tabindex: number | undefined = undefined; - @Input() text = false; - - // Геттеры — маппинг в PrimeNG API - get primeSize(): 'small' | 'large' | undefined { - if (this.size === 'small') return 'small'; - if (this.size === 'large') return 'large'; - return undefined; - } - - get primeIconPos(): 'left' | 'right' { - return this.iconPos === 'postfix' ? 'right' : 'left'; - } - - get primeSeverity(): PrimeButtonSeverity | null { - if (this.variant === 'secondary') return 'secondary'; - if (this.severity === 'warning') return 'warn'; - return this.severity; - } - - get primeBadgeSeverity() { - if (this.badgeSeverity === 'warning') return 'warn'; - return this.badgeSeverity; - } -} -``` - ---- - -## Паттерн сторисов - -### Файл: `src/stories/components/{name}/{name}.stories.ts` - -**Все тексты описаний — на русском языке.** - -### Правило: title сториса - -Формат: `Components/{Category}/{ComponentName}` - -Категории соответствуют группировке на [primeng.org](https://primeng.org/): - -| Категория | Компоненты | -|-----------|-----------------------------------------------------------------------------------------------| -| Button | Button, SpeedDial, SplitButton | -| Data | DataTable, DataView, OrderList, OrgChart, Paginator, PickList, Timeline, Tree, TreeTable | -| Form | AutoComplete, Checkbox, ColorPicker, DatePicker, InputMask, InputNumber, InputOtp, InputText, Knob, Listbox, MultiSelect, Password, RadioButton, Rating, Select, SelectButton, Slider, Textarea, ToggleButton, ToggleSwitch, TreeSelect | -| Menu | Breadcrumb, ContextMenu, Dock, Menu, Menubar, MegaMenu, PanelMenu, Steps, TabMenu, TieredMenu | -| Messages | Message, Toast | -| Misc | Avatar, Badge, BlockUI, Chip, Inplace, MeterGroup, ProgressBar, ProgressSpinner, ScrollTop, Skeleton, Tag | -| Overlay | ConfirmDialog, ConfirmPopup, Dialog, Drawer, Popover, Tooltip | -| Panel | Accordion, Card, Divider, Fieldset, Panel, ScrollPanel, Splitter, Stepper, Tabs | -| Media | Carousel, Galleria, Image, ImageCompare | - -```typescript -// ❌ Запрещено -title: 'Prime/Button' -title: 'Components/Button' - -// ✅ Правильно -title: 'Components/Button/Button' -title: 'Components/Panel/Card' -title: 'Components/Panel/Divider' -title: 'Components/Form/InputText' -``` - -### Полный шаблон сториса - -```typescript -import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; -import { XxxComponent } from '../../../lib/components/xxx/xxx.component'; - -// Расширяем тип для Events, которых нет в @Output() -type XxxArgs = XxxComponent & { onClick?: (event: MouseEvent) => void }; - -const meta: Meta = { - title: 'Components/{Category}/Xxx', - component: XxxComponent, - tags: ['autodocs'], - decorators: [ - moduleMetadata({ imports: [XxxComponent] }) - ], - parameters: { - docs: { - description: { - // 1. Одна строка — для чего компонент - // 2. Ссылка на Figma - // 3. Блок импорта - component: `Описание компонента. [Figma Design](https://www.figma.com/design/...). - -\`\`\`typescript -import { XxxModule } from 'primeng/xxx'; -\`\`\``, - }, - }, - }, - argTypes: { - // ── Props ──────────────────────────────────────────────── - propName: { - control: 'text' | 'boolean' | 'select' | 'number', - options: [...], // только для select - description: 'Описание на русском', - table: { - category: 'Props', - defaultValue: { summary: 'значение' }, - type: { summary: "'тип1' | 'тип2'" }, - }, - }, - // ── Badge ──────────────────────────────────────────────── - badge: { - // ... category: 'Badge' - }, - // ── Events ─────────────────────────────────────────────── - onClick: { - control: false, - description: 'Событие клика', - table: { - category: 'Events', - type: { summary: 'EventEmitter' }, - }, - }, - }, - args: { - // Дефолты для полей, которые нужно явно инициализировать - }, -}; - -// commonTemplate — для сторисов-вариаций (НЕ для Default) -const commonTemplate = ` - -`; - -export default meta; -type Story = StoryObj; - -// ── Default ────────────────────────────────────────────────────────────────── -// Динамический render: template генерируется из текущих args. -// Storybook Angular захватывает template как source code → -// при смене controls сниппет обновляется автоматически. - -export const Default: Story = { - name: 'Default', - render: (args) => { - const parts: string[] = []; - - if (args.label) parts.push(`label="${args.label}"`); - if (args.variant) parts.push(`variant="${args.variant}"`); - if (args.severity) parts.push(`severity="${args.severity}"`); - // ... остальные пропсы - if (args.rounded) parts.push(`[rounded]="true"`); - if (args.disabled) parts.push(`[disabled]="true"`); - - const template = parts.length - ? `` - : ``; - - return { props: args, template }; - }, - args: { label: 'Label' }, - parameters: { - docs: { - description: { - story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.', - }, - }, - }, -}; - -// ── Сторисы-вариации ───────────────────────────────────────────────────────── -// Каждая сторис — ОДИН вариант компонента. -// Используют commonTemplate + props: args → controls работают. -// source.code — статичный минимальный пример. - -export const Sizes: Story = { - render: (args) => ({ props: args, template: commonTemplate }), - args: { label: 'Button', size: 'large' }, - parameters: { - docs: { - description: { story: 'Описание вариации.' }, - source: { - code: ``, - }, - }, - }, -}; -``` - ---- - -## Паттерн examples/ - -### Назначение - -Папка `src/stories/components/{name}/examples/` содержит **standalone Angular-компоненты** — каждый инкапсулирует один вариант использования компонента. -В блоке **Source** в Storybook показывается полноценный Angular-компонент (TypeScript), который пользователь библиотеки может скопировать к себе как есть. - -Это принципиально отличается от подхода в `{name}.stories.ts`, где `source.code` показывает просто HTML-шаблон. - -### Структура файла - -```typescript -import { Component } from '@angular/core'; -import { StoryObj } from '@storybook/angular'; -import { XxxComponent } from '../../../../lib/components/xxx/xxx.component'; - -// 1. Шаблон выносится в const — чтобы переиспользовать в source.code -const template = ` -
- -
-`; -const styles = ''; - -// 2. Standalone-компонент с реальным шаблоном -@Component({ - selector: 'app-xxx-variant', - standalone: true, - imports: [XxxComponent], - template, - styles, -}) -export class XxxVariantComponent {} - -// 3. StoryObj — рендерит компонент; source.code — код компонента для копирования -export const Variant: StoryObj = { - render: () => ({ - template: ``, - }), - parameters: { - docs: { - description: { story: 'Описание на русском.' }, - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { XxxComponent } from '@cdek-it/angular-ui-kit'; - -@Component({ - selector: 'app-xxx-variant', - standalone: true, - imports: [XxxComponent], - template: \` - - \`, -}) -export class XxxVariantComponent {} - `, - }, - }, - }, -}; -``` - -### Правила - -1. Каждый файл — **одна вариация**, один `@Component` + один `StoryObj` -2. Шаблон выносится в `const template` — чтобы использовать в `source.code` -3. `render: () => ({ template: '' })` — **только** для статичных примеров, где controls не нужны (форм-контролы с `ngModel`, группы компонентов). Для простых prop-вариаций controls не будут работать при таком подходе — см. правило ниже. -4. `source.code` содержит полный TypeScript-код компонента с импортами из `@cdek-it/angular-ui-kit` -5. Обёртка `
` — фон preview; **`p-4` не добавлять** -6. Именование файлов: `{name}-{variant}.component.ts` (например `avatar-label.component.ts`) -7. Именование selector компонента: `app-{name}-{variant}` (например `app-avatar-label`) -8. Класс компонента: `{Name}{Variant}Component` (например `AvatarLabelComponent`) - -### Когда создавать examples/ - -`examples/` создаётся **обязательно для каждого компонента** — для всех вариационных сторисов (кроме `Default`). - -`Default` (интерактивный playground) в examples/ **не дублируется** — он живёт только в `{name}.stories.ts`. - -Каждая вариационная сторис (`WithIcon`, `Removable`, `Disabled` и т.д.) имеет соответствующий файл в `examples/`. - ---- - -## Структура сторисов — обязательные разделы - -| Порядок | Сторис | Описание | -|---------|-------------|-----------------------------------------------------------------------| -| 1 | **Default** | Интерактивный playground. Динамический render. Все пропсы в Controls. | -| 2+ | Вариации | По одному варианту: Sizes, Icons, Rounded, Loading, Disabled, и т.д. | - -### Правило: один экземпляр компонента на сторис - -**Каждая сторис показывает ровно один вариант компонента. Все остальные виды компонента настраиваются с помощью пропсов через `args`.** - -В каждой вариационной сторис шаблон содержит **ровно один** экземпляр компонента. -Это правило распространяется как на сторисы в `{name}.stories.ts`, так и на компоненты в `examples/`. -Вариация демонстрируется через `args` (пропсы), а не через дублирование тегов. - -```typescript -// ❌ Запрещено — несколько экземпляров компонента в шаблоне -export const Sizes: Story = { - render: (args) => ({ - props: args, - template: ` - - - - `, - }), -}; - -// ❌ Запрещено — то же нарушение внутри examples/ -@Component({ template: ` - - - -` }) -export class TagSeveritiesComponent {} - -// ✅ Правильно — один экземпляр, вариация задаётся через args -export const Sizes: Story = { - render: (args) => ({ props: args, template: commonTemplate }), - args: { label: 'Button', size: 'large' }, -}; - -export const Severity: Story = { - render: (args) => ({ props: args, template: commonTemplate }), - args: { value: 'Success', severity: 'success' }, -}; -``` - -**Исключение**: составные компоненты (например группа радиокнопок / чекбоксов), где несколько дочерних элементов — это **сам смысл компонента**, а не визуальное перечисление вариантов. - -### Обязательные вариации для большинства компонентов -- `Sizes` — один компонент с нужным `size` в `args` -- `Icons` — один компонент с иконкой в `args` -- `Loading` — один компонент с `loading: true` в `args` -- `Rounded` — один компонент с `rounded: true` в `args` -- `Severity` — один компонент с нужным `severity` в `args` -- `Disabled` — один компонент с `disabled: true` в `args` - -### Правило: controls (пропсы) работают во всех вариационных сторисах - -**Вариационные сторисы ВСЕГДА рендерят через `commonTemplate + args`** — это единственный способ, при котором Storybook передаёт значения controls в компонент. - -`render: () => ({ template: '' })` — статичный рендер, controls **не работают**. Такой подход применим только для форм-контролов с `ngModel` и групп компонентов. - -```typescript -// ❌ Controls не работают — props не передаются в статичный компонент -export const Rounded: Story = { - render: () => ({ - template: ``, - }), -}; - -// ✅ Controls работают — props передаются через args -export const Rounded: Story = { - render: (args) => ({ props: args, template: commonTemplate }), - args: { value: 'Rounded', severity: 'success', rounded: true }, - parameters: { - docs: { - source: { - language: 'ts', - code: ` -import { Component } from '@angular/core'; -import { TagComponent } from '@cdek-it/angular-ui-kit'; - -@Component({ - selector: 'app-tag-rounded', - standalone: true, - imports: [TagComponent], - template: \\\` - - \\\`, -}) -export class TagRoundedComponent {} - `, - }, - }, - }, -}; -``` - -**Если для вариации существует example-файл:** example-компонент регистрируется в `moduleMetadata`, но сторис рендерит через `commonTemplate + args`. В `source.code` сторис показывает TypeScript-код из example-файла (дублируется вручную). - ---- - -## Ключевые технические решения - -### Почему Default story использует динамический render - -В Storybook 10 Angular `source.transform` **не реактивен** — вызывается один раз. -Единственный способ обновлять code-сниппет при смене controls: -генерировать `template` строку прямо в `render(args)`. -Storybook Angular использует `template` из render как source code. - -```typescript -// ✅ Правильно — template обновляется при смене controls -render: (args) => { - const template = ``; - return { props: args, template }; -}, - -// ❌ Неправильно — source.transform не реактивен -parameters: { - docs: { source: { transform: (src, ctx) => ... } } -} -``` - -### Почему НЕ используется самозакрывающийся тег - -Angular JIT-компилятор запрещает ``. -` - -``` - -Поиск иконок: https://tabler.io/icons - ---- - -## Стилизация компонентов - -### Порядок слоёв - -``` -Компонент-обёртка → PrimeNG (p-button) → PrimeNG Aura тема -→ Токены (src/prime-preset/tokens/components/{name}.ts) -→ Tailwind CSS -``` - -### Добавление CSS-токенов - -Файл: `src/prime-preset/tokens/components/{name}.ts` - -Структура токенов соответствует PrimeNG Aura preset. -Кастомные расширения — через префикс `--p-{name}-extend-*`. - -### Tailwind в шаблонах сторисов - -```html - -
- -
-``` - ---- - -## Референс Vue UI Kit - -Vue UI Kit (PrimeVue) — источник референса по структуре сторисов и вариациям компонентов: - -- **Репозиторий**: `~/Downloads/vue-ui-kit-3/src/plugins/prime/stories/` -- **Запущен локально**: `http://localhost:6006` - -### Что брать как референс - -| Vue файл | Что переносить в Angular | -|-----------------------------------|--------------------------------------------------| -| `Button/Button.stories.js` | argTypes, stories args, описания | -| `Button/Button.template.js` | Шаблоны вариаций (grid-матрица размеров/severity)| -| `Button/Button.mdx` | Структура документации, порядок сторисов | - -### Как адаптировать Vue → Angular - -| Vue | Angular | -|-----------------------------|----------------------------------------------| -| `v-bind="args"` | `[prop]="prop"` (через `props: args`) | -| `variant="text"` | Остаётся `variant="text"` (статичный шаблон) | -| ` + \`, +}) +export class ExampleComponent { + constructor(private messageService: MessageService) {} + + show(): void { + this.messageService.add({ + group: 'my-toast', + severity: 'info', + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon: 'ti ti-info-circle', + }); + } +} + `, + }, + }, + }, +}; diff --git a/src/stories/components/toast/examples/toast-width.component.ts b/src/stories/components/toast/examples/toast-width.component.ts new file mode 100644 index 00000000..70e81593 --- /dev/null +++ b/src/stories/components/toast/examples/toast-width.component.ts @@ -0,0 +1,97 @@ +import { Component } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { Toast } from 'primeng/toast'; +import { Button } from 'primeng/button'; +import { MessageService, SharedModule } from 'primeng/api'; + +const SIZES = [ + { key: 'sm', label: 'Small (20rem)', width: '20rem' }, + { key: 'base', label: 'Base (25rem)', width: '25rem' }, + { key: 'lg', label: 'Large (30rem)', width: '30rem' }, + { key: 'xlg', label: 'X-Large (45rem)', width: '45rem' }, +] as const; + +@Component({ + selector: 'app-toast-width', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template: ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
+ +
+ @for (s of sizes; track s.key) { +
+
+
+ +
+ Сообщение +
Ширина {{ s.key }}: {{ s.width }}
+
+
+
+ } +
+ +
+ @for (s of sizes; track s.key) { + + } +
+ `, +}) +export class ToastWidthComponent { + readonly sizes = SIZES; + currentWidth = '25rem'; + + constructor(private readonly messageService: MessageService) {} + + show(width: string): void { + this.currentWidth = width; + this.messageService.clear('width-preview'); + this.messageService.add({ + key: 'width-preview', + severity: 'info', + summary: 'Сообщение', + detail: 'Ширина: ' + width, + life: 3000, + icon: 'ti ti-info-circle', + }); + } +} + +export const Width: StoryObj = { + render: () => ({ + template: ``, + }), + parameters: { + docs: { + description: { story: 'Ширина задаётся через CSS-переменную `--p-toast-width` с помощью пропа `pt`.' }, + source: { + language: 'html', + code: ` + + ... + + `, + }, + }, + }, +}; diff --git a/src/stories/components/toast/examples/toast-with-close-button.component.ts b/src/stories/components/toast/examples/toast-with-close-button.component.ts new file mode 100644 index 00000000..821a75eb --- /dev/null +++ b/src/stories/components/toast/examples/toast-with-close-button.component.ts @@ -0,0 +1,105 @@ +import { Component } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { Toast } from 'primeng/toast'; +import { Button } from 'primeng/button'; +import { MessageService, SharedModule } from 'primeng/api'; + +const SEVERITIES = [ + { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, + { type: 'success', icon: 'ti ti-circle-check', label: 'Успех' }, + { type: 'warn', icon: 'ti ti-alert-triangle', label: 'Предупреждение' }, + { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, +] as const; + +@Component({ + selector: 'app-toast-with-close-button', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template: ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
+ +
+ @for (s of severities; track s.type) { +
+
+
+ +
+ Сообщение +
Подпись
+
+ +
+
+ } +
+ +
+ @for (s of severities; track s.type) { + + } +
+ `, +}) +export class ToastWithCloseButtonComponent { + readonly severities = SEVERITIES; + + constructor(private readonly messageService: MessageService) {} + + show(severity: string, icon: string): void { + this.messageService.add({ + key: 'with-close', + severity, + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon, + closable: true, + }); + } +} + +export const WithCloseButton: StoryObj = { + render: () => ({ + template: ``, + }), + parameters: { + docs: { + description: { story: 'Уведомления с кнопкой закрытия (closable: true).' }, + source: { + language: 'ts', + code: ` +this.messageService.add({ + group: 'my-toast', + severity: 'info', + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon: 'ti ti-info-circle', + closable: true, +}); + `, + }, + }, + }, +}; diff --git a/src/stories/components/toast/examples/toast-with-content.component.ts b/src/stories/components/toast/examples/toast-with-content.component.ts new file mode 100644 index 00000000..6f4453af --- /dev/null +++ b/src/stories/components/toast/examples/toast-with-content.component.ts @@ -0,0 +1,123 @@ +import { Component } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { Toast } from 'primeng/toast'; +import { Button } from 'primeng/button'; +import { MessageService, SharedModule } from 'primeng/api'; + +const SEVERITIES = [ + { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, + { type: 'success', icon: 'ti ti-circle-check', label: 'Успех' }, + { type: 'warn', icon: 'ti ti-alert-triangle', label: 'Предупреждение' }, + { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, +] as const; + +@Component({ + selector: 'app-toast-with-content', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template: ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
Дополнительный контент
+
+
+
Ячейка 1
+
Ячейка 2
+
+
+
+
+ +
+ @for (s of severities; track s.type) { +
+
+
+ +
+ Сообщение +
Подпись
+
+
Дополнительный контент
+
+
+
Ячейка 1
+
Ячейка 2
+
+
+ +
+
+ } +
+ +
+ @for (s of severities; track s.type) { + + } +
+ `, +}) +export class ToastWithContentComponent { + readonly severities = SEVERITIES; + + constructor(private readonly messageService: MessageService) {} + + show(severity: string, icon: string): void { + this.messageService.add({ + key: 'with-content', + severity, + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon, + closable: true, + }); + } +} + +export const WithContent: StoryObj = { + render: () => ({ + template: ``, + }), + parameters: { + docs: { + description: { story: 'Уведомления с дополнительным контентом под заголовком.' }, + source: { + language: 'html', + code: ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
Дополнительный контент
+
+
+
+
+ `, + }, + }, + }, +}; diff --git a/src/stories/components/toast/toast.stories.ts b/src/stories/components/toast/toast.stories.ts new file mode 100644 index 00000000..74f3aca7 --- /dev/null +++ b/src/stories/components/toast/toast.stories.ts @@ -0,0 +1,123 @@ +import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; +import { MessageService } from 'primeng/api'; +import { Toast } from 'primeng/toast'; +import { Button } from 'primeng/button'; +import { ToastComponent } from '../../../lib/components/toast/toast.component'; +import { ToastDefaultComponent } from './examples/toast-default.component'; +import { ToastSeveritiesComponent, Severities } from './examples/toast-severities.component'; +import { ToastWithCloseButtonComponent, WithCloseButton } from './examples/toast-with-close-button.component'; +import { ToastWithContentComponent, WithContent } from './examples/toast-with-content.component'; +import { ToastWidthComponent, Width } from './examples/toast-width.component'; +import { ToastPositionComponent, Position } from './examples/toast-position.component'; + +const meta: Meta = { + title: 'Components/Feedback/Toast', + component: ToastComponent, + tags: ['autodocs'], + decorators: [ + moduleMetadata({ + imports: [ + ToastComponent, + ToastDefaultComponent, + ToastSeveritiesComponent, + ToastWithCloseButtonComponent, + ToastWithContentComponent, + ToastWidthComponent, + ToastPositionComponent, + Toast, + Button, + ], + providers: [MessageService], + }), + ], + parameters: { + designTokens: { prefix: '--p-toast' }, + docs: { + description: { + component: `Компонент для отображения всплывающих уведомлений поверх интерфейса. Требует подключения \`MessageService\`. + +\`\`\`typescript +import { ToastComponent } from '@cdek-it/angular-ui-kit'; +import { MessageService } from 'primeng/api'; + +@Component({ + providers: [MessageService], +}) +export class MyComponent { + constructor(private messageService: MessageService) {} + + show(): void { + this.messageService.add({ + severity: 'info', + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon: 'ti ti-info-circle', + }); + } +} +\`\`\``, + }, + }, + }, + argTypes: { + position: { + control: 'select', + options: ['top-right', 'top-left', 'top-center', 'bottom-right', 'bottom-left', 'bottom-center', 'center'], + description: 'Позиция тоста на экране.', + table: { + category: 'Props', + defaultValue: { summary: 'top-right' }, + type: { summary: "'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center' | 'center'" }, + }, + }, + key: { + control: 'text', + description: 'Ключ для адресной отправки сообщений через MessageService.', + table: { + category: 'Props', + type: { summary: 'string' }, + }, + }, + life: { + control: 'number', + description: 'Время (мс) до автоматического закрытия тоста.', + table: { + category: 'Props', + defaultValue: { summary: '5000' }, + type: { summary: 'number' }, + }, + }, + }, + args: { + position: 'top-right', + key: undefined, + life: 5000, + }, +}; + +export default meta; +type Story = StoryObj; + +// ── Default ────────────────────────────────────────────────────────────────── + +export const Default: Story = { + name: 'Default', + render: (args) => ({ + props: { + position: args.position, + life: args.life, + }, + template: ``, + }), + parameters: { + docs: { + description: { + story: 'Базовый пример компонента. Используйте Controls для изменения позиции и времени жизни тоста.', + }, + }, + }, +}; + +// ── Re-exports from example components ──────────────────────────────────── +export { Severities, WithCloseButton, WithContent, Width, Position }; From 97e61d74a74cf507ee24c4b0c66a490b8ed7bb36 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Wed, 15 Apr 2026 14:54:06 +0700 Subject: [PATCH 2/8] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B8=20?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D1=80=D0=B8=D1=81=D0=BE=D0=B2=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D1=81=D0=BD=D0=BE=20=D0=BF=D0=B0=D1=82=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D0=BD=D0=BE=D0=B2=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toast/examples/toast-default.component.ts | 79 ----------- .../examples/toast-position.component.ts | 68 ++++----- .../examples/toast-severities.component.ts | 99 +++++++------ .../toast/examples/toast-width.component.ts | 101 +++++++------- .../toast-with-close-button.component.ts | 94 +++++++------ .../examples/toast-with-content.component.ts | 130 +++++++++--------- src/stories/components/toast/toast.stories.ts | 53 +------ 7 files changed, 262 insertions(+), 362 deletions(-) delete mode 100644 src/stories/components/toast/examples/toast-default.component.ts diff --git a/src/stories/components/toast/examples/toast-default.component.ts b/src/stories/components/toast/examples/toast-default.component.ts deleted file mode 100644 index 969a1ea1..00000000 --- a/src/stories/components/toast/examples/toast-default.component.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Component, Input, OnChanges } from '@angular/core'; -import { Toast } from 'primeng/toast'; -import { Button } from 'primeng/button'; -import { MessageService, SharedModule } from 'primeng/api'; - -const SEVERITIES = [ - { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, - { type: 'success', icon: 'ti ti-circle-check', label: 'Успех' }, - { type: 'warn', icon: 'ti ti-alert-triangle', label: 'Предупреждение' }, - { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, -] as const; - -@Component({ - selector: 'app-toast-default', - standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], - template: ` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
- -
- @for (s of severities; track s.type) { -
-
-
- -
- Сообщение -
Подпись
-
-
-
- } -
- -
- @for (s of severities; track s.type) { - - } -
- `, -}) -export class ToastDefaultComponent implements OnChanges { - @Input() position = 'top-right'; - @Input() life = 5000; - - readonly severities = SEVERITIES; - - constructor(private readonly messageService: MessageService) {} - - ngOnChanges(): void { - this.messageService.clear('default-story'); - } - - show(severity: string, icon: string): void { - this.messageService.add({ - key: 'default-story', - severity, - summary: 'Сообщение', - detail: 'Подпись', - life: this.life, - icon, - }); - } -} diff --git a/src/stories/components/toast/examples/toast-position.component.ts b/src/stories/components/toast/examples/toast-position.component.ts index 49a8738b..4b6492f2 100644 --- a/src/stories/components/toast/examples/toast-position.component.ts +++ b/src/stories/components/toast/examples/toast-position.component.ts @@ -13,35 +13,39 @@ const POSITIONS = [ { position: 'bottom-right', label: 'Вниз справа', key: 'pos-bottom-right' }, ] as const; +const template = ` +@for (p of positions; track p.key) { + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
+} + +
+ @for (p of positions; track p.key) { + + } +
+`; +const styles = ''; + @Component({ selector: 'app-toast-position', standalone: true, imports: [Toast, Button, SharedModule], providers: [MessageService], - template: ` - @for (p of positions; track p.key) { - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
- } - -
- @for (p of positions; track p.key) { - - } -
- `, + template, + styles, }) export class ToastPositionComponent { readonly positions = POSITIONS; @@ -66,16 +70,16 @@ export const Position: StoryObj = { }), parameters: { docs: { - description: { story: 'Расположение тоста задаётся через `position` и `group`.' }, + description: { story: 'Расположение тоста задаётся через `position` и `key`.' }, source: { - language: 'html', + language: 'ts', code: ` - - - - - - + + + + + + `, }, }, diff --git a/src/stories/components/toast/examples/toast-severities.component.ts b/src/stories/components/toast/examples/toast-severities.component.ts index 5b6e6f20..9dbbe33c 100644 --- a/src/stories/components/toast/examples/toast-severities.component.ts +++ b/src/stories/components/toast/examples/toast-severities.component.ts @@ -11,49 +11,53 @@ const SEVERITIES = [ { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, ] as const; -@Component({ - selector: 'app-toast-severities', - standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], - template: ` - - +const template = ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
+ +
+ @for (s of severities; track s.type) { +
+
- +
- {{ message.summary }} -
{{ message.detail }}
+ Сообщение +
Подпись
- - - -
- @for (s of severities; track s.type) { -
-
-
- -
- Сообщение -
Подпись
-
-
-
- } +
+ } +
-
- @for (s of severities; track s.type) { - - } -
- `, +
+ @for (s of severities; track s.type) { + + } +
+`; +const styles = ''; + +@Component({ + selector: 'app-toast-severities', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template, + styles, }) export class ToastSeveritiesComponent { readonly severities = SEVERITIES; @@ -83,16 +87,25 @@ export const Severities: StoryObj = { language: 'ts', code: ` import { Component } from '@angular/core'; -import { MessageService } from 'primeng/api'; -import { ToastComponent } from '@cdek-it/angular-ui-kit'; +import { MessageService, SharedModule } from 'primeng/api'; +import { Toast } from 'primeng/toast'; @Component({ selector: 'app-example', standalone: true, - imports: [ToastComponent], + imports: [Toast, SharedModule], providers: [MessageService], template: \` - + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
\`, }) @@ -101,7 +114,7 @@ export class ExampleComponent { show(): void { this.messageService.add({ - group: 'my-toast', + key: 'my-toast', severity: 'info', summary: 'Сообщение', detail: 'Подпись', diff --git a/src/stories/components/toast/examples/toast-width.component.ts b/src/stories/components/toast/examples/toast-width.component.ts index 70e81593..7c78814b 100644 --- a/src/stories/components/toast/examples/toast-width.component.ts +++ b/src/stories/components/toast/examples/toast-width.component.ts @@ -5,57 +5,64 @@ import { Button } from 'primeng/button'; import { MessageService, SharedModule } from 'primeng/api'; const SIZES = [ - { key: 'sm', label: 'Small (20rem)', width: '20rem' }, - { key: 'base', label: 'Base (25rem)', width: '25rem' }, - { key: 'lg', label: 'Large (30rem)', width: '30rem' }, - { key: 'xlg', label: 'X-Large (45rem)', width: '45rem' }, + { key: 'sm', label: 'Small (20rem)', width: '20rem', cssVar: '20rem' }, + { key: 'base', label: 'Base (25rem)', width: '25rem', cssVar: '25rem' }, + { key: 'lg', label: 'Large (30rem)', width: '30rem', cssVar: '30rem' }, + { key: 'xlg', label: 'X-Large (45rem)', width: '45rem', cssVar: '45rem' }, ] as const; -@Component({ - selector: 'app-toast-width', - standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], - template: ` - + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
+ +
+ @for (s of sizes; track s.key) { +
- +
- +
- {{ message.summary }} -
{{ message.detail }}
-
- - - -
- @for (s of sizes; track s.key) { -
-
-
- -
- Сообщение -
Ширина {{ s.key }}: {{ s.width }}
-
-
+ Сообщение +
Ширина {{ s.key }}: {{ s.width }}
- } +
+ } +
-
- @for (s of sizes; track s.key) { - - } -
- `, +
+ @for (s of sizes; track s.key) { + + } +
+`; +const styles = ''; + +@Component({ + selector: 'app-toast-width', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template, + styles, }) export class ToastWidthComponent { readonly sizes = SIZES; @@ -63,14 +70,14 @@ export class ToastWidthComponent { constructor(private readonly messageService: MessageService) {} - show(width: string): void { - this.currentWidth = width; + show(cssVar: string): void { + this.currentWidth = cssVar; this.messageService.clear('width-preview'); this.messageService.add({ key: 'width-preview', severity: 'info', summary: 'Сообщение', - detail: 'Ширина: ' + width, + detail: 'Ширина: ' + cssVar, life: 3000, icon: 'ti ti-info-circle', }); @@ -85,7 +92,7 @@ export const Width: StoryObj = { docs: { description: { story: 'Ширина задаётся через CSS-переменную `--p-toast-width` с помощью пропа `pt`.' }, source: { - language: 'html', + language: 'ts', code: ` ... diff --git a/src/stories/components/toast/examples/toast-with-close-button.component.ts b/src/stories/components/toast/examples/toast-with-close-button.component.ts index 821a75eb..eabd523a 100644 --- a/src/stories/components/toast/examples/toast-with-close-button.component.ts +++ b/src/stories/components/toast/examples/toast-with-close-button.component.ts @@ -11,55 +11,59 @@ const SEVERITIES = [ { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, ] as const; -@Component({ - selector: 'app-toast-with-close-button', - standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], - template: ` - - +const template = ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
+
+ +
+ @for (s of severities; track s.type) { +
+
- +
- {{ message.summary }} -
{{ message.detail }}
-
- - - -
- @for (s of severities; track s.type) { -
-
-
- -
- Сообщение -
Подпись
-
- -
+ Сообщение +
Подпись
- } + +
+ } +
-
- @for (s of severities; track s.type) { - - } -
- `, +
+ @for (s of severities; track s.type) { + + } +
+`; +const styles = ''; + +@Component({ + selector: 'app-toast-with-close-button', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template, + styles, }) export class ToastWithCloseButtonComponent { readonly severities = SEVERITIES; @@ -90,7 +94,7 @@ export const WithCloseButton: StoryObj = { language: 'ts', code: ` this.messageService.add({ - group: 'my-toast', + key: 'my-toast', severity: 'info', summary: 'Сообщение', detail: 'Подпись', diff --git a/src/stories/components/toast/examples/toast-with-content.component.ts b/src/stories/components/toast/examples/toast-with-content.component.ts index 6f4453af..5aadaf6b 100644 --- a/src/stories/components/toast/examples/toast-with-content.component.ts +++ b/src/stories/components/toast/examples/toast-with-content.component.ts @@ -11,19 +11,34 @@ const SEVERITIES = [ { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, ] as const; -@Component({ - selector: 'app-toast-with-content', - standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], - template: ` - - +const template = ` + + +
+ +
+ {{ message.summary }} +
{{ message.detail }}
+
+
Дополнительный контент
+
+
+
Ячейка 1
+
Ячейка 2
+
+
+
+
+ +
+ @for (s of severities; track s.type) { +
+
- +
- {{ message.summary }} -
{{ message.detail }}
+ Сообщение +
Подпись
Дополнительный контент
@@ -32,48 +47,37 @@ const SEVERITIES = [
Ячейка 2
- - - -
- @for (s of severities; track s.type) { -
-
-
- -
- Сообщение -
Подпись
-
-
Дополнительный контент
-
-
-
Ячейка 1
-
Ячейка 2
-
-
- -
-
- } + +
+ } +
-
- @for (s of severities; track s.type) { - - } -
- `, +
+ @for (s of severities; track s.type) { + + } +
+`; +const styles = ''; + +@Component({ + selector: 'app-toast-with-content', + standalone: true, + imports: [Toast, Button, SharedModule], + providers: [MessageService], + template, + styles, }) export class ToastWithContentComponent { readonly severities = SEVERITIES; @@ -101,21 +105,17 @@ export const WithContent: StoryObj = { docs: { description: { story: 'Уведомления с дополнительным контентом под заголовком.' }, source: { - language: 'html', + language: 'ts', code: ` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
Дополнительный контент
-
-
-
-
+this.messageService.add({ + key: 'my-toast', + severity: 'info', + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon: 'ti ti-info-circle', + closable: true, +}); `, }, }, diff --git a/src/stories/components/toast/toast.stories.ts b/src/stories/components/toast/toast.stories.ts index 74f3aca7..6ca7b620 100644 --- a/src/stories/components/toast/toast.stories.ts +++ b/src/stories/components/toast/toast.stories.ts @@ -1,9 +1,6 @@ import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; import { MessageService } from 'primeng/api'; -import { Toast } from 'primeng/toast'; -import { Button } from 'primeng/button'; import { ToastComponent } from '../../../lib/components/toast/toast.component'; -import { ToastDefaultComponent } from './examples/toast-default.component'; import { ToastSeveritiesComponent, Severities } from './examples/toast-severities.component'; import { ToastWithCloseButtonComponent, WithCloseButton } from './examples/toast-with-close-button.component'; import { ToastWithContentComponent, WithContent } from './examples/toast-with-content.component'; @@ -18,14 +15,11 @@ const meta: Meta = { moduleMetadata({ imports: [ ToastComponent, - ToastDefaultComponent, ToastSeveritiesComponent, ToastWithCloseButtonComponent, ToastWithContentComponent, ToastWidthComponent, ToastPositionComponent, - Toast, - Button, ], providers: [MessageService], }), @@ -39,23 +33,6 @@ const meta: Meta = { \`\`\`typescript import { ToastComponent } from '@cdek-it/angular-ui-kit'; import { MessageService } from 'primeng/api'; - -@Component({ - providers: [MessageService], -}) -export class MyComponent { - constructor(private messageService: MessageService) {} - - show(): void { - this.messageService.add({ - severity: 'info', - summary: 'Сообщение', - detail: 'Подпись', - life: 5000, - icon: 'ti ti-info-circle', - }); - } -} \`\`\``, }, }, @@ -72,12 +49,7 @@ export class MyComponent { }, }, key: { - control: 'text', - description: 'Ключ для адресной отправки сообщений через MessageService.', - table: { - category: 'Props', - type: { summary: 'string' }, - }, + table: { disable: true }, }, life: { control: 'number', @@ -97,27 +69,6 @@ export class MyComponent { }; export default meta; -type Story = StoryObj; - -// ── Default ────────────────────────────────────────────────────────────────── - -export const Default: Story = { - name: 'Default', - render: (args) => ({ - props: { - position: args.position, - life: args.life, - }, - template: ``, - }), - parameters: { - docs: { - description: { - story: 'Базовый пример компонента. Используйте Controls для изменения позиции и времени жизни тоста.', - }, - }, - }, -}; // ── Re-exports from example components ──────────────────────────────────── -export { Severities, WithCloseButton, WithContent, Width, Position }; +export { Severities as Default, WithCloseButton, WithContent, Width, Position }; From e04c0f21435dbb5162dc38eeedd61342819ade33 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 20 Apr 2026 09:51:07 +0700 Subject: [PATCH 3/8] =?UTF-8?q?refactor(toast):=20=D1=81=D0=BE=D0=B7=D0=B4?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20UiToastService=20=D0=BA=D0=B0=D0=BA=20?= =?UTF-8?q?=D0=BE=D0=B1=D1=91=D1=80=D1=82=D0=BA=D1=83=20=D0=BD=D0=B0=D0=B4?= =?UTF-8?q?=20MessageService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Аналогично UiDialogService — сервис скрывает primeng от потребителей. ToastComponent: убран scoped MessageService, добавлен проброс pt. --- src/lib/components/toast/toast.component.ts | 5 ++-- src/lib/components/toast/toast.service.ts | 30 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/lib/components/toast/toast.service.ts diff --git a/src/lib/components/toast/toast.component.ts b/src/lib/components/toast/toast.component.ts index 21a557fd..d9a86641 100644 --- a/src/lib/components/toast/toast.component.ts +++ b/src/lib/components/toast/toast.component.ts @@ -1,6 +1,5 @@ import { Component, Input } from '@angular/core'; import { Toast } from 'primeng/toast'; -import { MessageService } from 'primeng/api'; import { SharedModule } from 'primeng/api'; export type ToastSeverity = 'success' | 'info' | 'warn' | 'error' | 'secondary' | 'contrast'; @@ -24,9 +23,8 @@ const SEVERITY_ICONS: Record = { selector: 'ui-toast', standalone: true, imports: [Toast, SharedModule], - providers: [MessageService], template: ` - +
@@ -44,6 +42,7 @@ export class ToastComponent { @Input() position: ToastPosition = 'top-right'; @Input() key: string | undefined = undefined; @Input() life = 5000; + @Input() pt: Record | undefined = undefined; resolveIcon(message: { severity?: string; icon?: string }): string { return message.icon ?? SEVERITY_ICONS[message.severity ?? 'info'] ?? 'ti ti-info-circle'; diff --git a/src/lib/components/toast/toast.service.ts b/src/lib/components/toast/toast.service.ts new file mode 100644 index 00000000..20b84326 --- /dev/null +++ b/src/lib/components/toast/toast.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { MessageService } from 'primeng/api'; +import { ToastSeverity } from './toast.component'; + +export interface UiToastMessage { + key?: string; + severity?: ToastSeverity; + summary?: string; + detail?: string; + life?: number; + icon?: string; + closable?: boolean; + data?: unknown; +} + +export { MessageService }; + +@Injectable({ providedIn: 'root' }) +export class UiToastService { + + constructor(private readonly messageService: MessageService) {} + + add(message: UiToastMessage): void { + this.messageService.add(message); + } + + clear(key?: string): void { + this.messageService.clear(key); + } +} From 0f944e8e682f5a7d5bbface5eb7d07f08bb6889d Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 20 Apr 2026 09:51:14 +0700 Subject: [PATCH 4/8] =?UTF-8?q?refactor(toast):=20=D1=81=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D1=81=D1=8B=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D1=83=D1=8E=D1=82=20ToastComponent=20=D0=B8=20UiToastService?= =?UTF-8?q?=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20primeng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Все toast example-компоненты переведены на ui-toast и UiToastService — прямые импорты Toast, MessageService, SharedModule из primeng убраны. --- .../examples/toast-position.component.ts | 34 +++++--------- .../examples/toast-severities.component.ts | 45 +++++-------------- .../toast/examples/toast-width.component.ts | 31 +++++-------- .../toast-with-close-button.component.ts | 26 ++++------- .../examples/toast-with-content.component.ts | 33 ++++---------- 5 files changed, 50 insertions(+), 119 deletions(-) diff --git a/src/stories/components/toast/examples/toast-position.component.ts b/src/stories/components/toast/examples/toast-position.component.ts index 4b6492f2..dcd3e43f 100644 --- a/src/stories/components/toast/examples/toast-position.component.ts +++ b/src/stories/components/toast/examples/toast-position.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; -import { Toast } from 'primeng/toast'; import { Button } from 'primeng/button'; -import { MessageService, SharedModule } from 'primeng/api'; +import { ToastComponent } from '../../../../lib/components/toast/toast.component'; +import { UiToastService } from '../../../../lib/components/toast/toast.service'; const POSITIONS = [ { position: 'top-left', label: 'Вверх слева', key: 'pos-top-left' }, @@ -15,16 +15,7 @@ const POSITIONS = [ const template = ` @for (p of positions; track p.key) { - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
+ }
@@ -42,18 +33,17 @@ const styles = ''; @Component({ selector: 'app-toast-position', standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], + imports: [ToastComponent, Button], template, styles, }) export class ToastPositionComponent { readonly positions = POSITIONS; - constructor(private readonly messageService: MessageService) {} + constructor(private readonly toastService: UiToastService) {} show(key: string, position: string): void { - this.messageService.add({ + this.toastService.add({ key, severity: 'info', summary: 'Сообщение', @@ -74,12 +64,12 @@ export const Position: StoryObj = { source: { language: 'ts', code: ` - - - - - - + + + + + + `, }, }, diff --git a/src/stories/components/toast/examples/toast-severities.component.ts b/src/stories/components/toast/examples/toast-severities.component.ts index 9dbbe33c..b6b60345 100644 --- a/src/stories/components/toast/examples/toast-severities.component.ts +++ b/src/stories/components/toast/examples/toast-severities.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; -import { Toast } from 'primeng/toast'; import { Button } from 'primeng/button'; -import { MessageService, SharedModule } from 'primeng/api'; +import { ToastComponent } from '../../../../lib/components/toast/toast.component'; +import { UiToastService } from '../../../../lib/components/toast/toast.service'; const SEVERITIES = [ { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, @@ -12,16 +12,7 @@ const SEVERITIES = [ ] as const; const template = ` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
+
@for (s of severities; track s.type) { @@ -54,20 +45,19 @@ const styles = ''; @Component({ selector: 'app-toast-severities', standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], + imports: [ToastComponent, Button], template, styles, }) export class ToastSeveritiesComponent { readonly severities = SEVERITIES; - constructor(private readonly messageService: MessageService) {} + constructor(private readonly toastService: UiToastService) {} show(severity: string, icon: string): void { - this.messageService.add({ + this.toastService.add({ key: 'severities', - severity, + severity: severity as any, summary: 'Сообщение', detail: 'Подпись', life: 5000, @@ -87,33 +77,22 @@ export const Severities: StoryObj = { language: 'ts', code: ` import { Component } from '@angular/core'; -import { MessageService, SharedModule } from 'primeng/api'; -import { Toast } from 'primeng/toast'; +import { ToastComponent, UiToastService } from '@cdek-it/angular-ui-kit'; @Component({ selector: 'app-example', standalone: true, - imports: [Toast, SharedModule], - providers: [MessageService], + imports: [ToastComponent], template: \` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
+ \`, }) export class ExampleComponent { - constructor(private messageService: MessageService) {} + constructor(private toastService: UiToastService) {} show(): void { - this.messageService.add({ + this.toastService.add({ key: 'my-toast', severity: 'info', summary: 'Сообщение', diff --git a/src/stories/components/toast/examples/toast-width.component.ts b/src/stories/components/toast/examples/toast-width.component.ts index 7c78814b..5bbcf9e1 100644 --- a/src/stories/components/toast/examples/toast-width.component.ts +++ b/src/stories/components/toast/examples/toast-width.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; -import { Toast } from 'primeng/toast'; import { Button } from 'primeng/button'; -import { MessageService, SharedModule } from 'primeng/api'; +import { ToastComponent } from '../../../../lib/components/toast/toast.component'; +import { UiToastService } from '../../../../lib/components/toast/toast.service'; const SIZES = [ { key: 'sm', label: 'Small (20rem)', width: '20rem', cssVar: '20rem' }, @@ -12,19 +12,10 @@ const SIZES = [ ] as const; const template = ` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
+>
@for (s of sizes; track s.key) { @@ -59,8 +50,7 @@ const styles = ''; @Component({ selector: 'app-toast-width', standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], + imports: [ToastComponent, Button], template, styles, }) @@ -68,12 +58,12 @@ export class ToastWidthComponent { readonly sizes = SIZES; currentWidth = '25rem'; - constructor(private readonly messageService: MessageService) {} + constructor(private readonly toastService: UiToastService) {} show(cssVar: string): void { this.currentWidth = cssVar; - this.messageService.clear('width-preview'); - this.messageService.add({ + this.toastService.clear('width-preview'); + this.toastService.add({ key: 'width-preview', severity: 'info', summary: 'Сообщение', @@ -94,9 +84,8 @@ export const Width: StoryObj = { source: { language: 'ts', code: ` - - ... - + + `, }, }, diff --git a/src/stories/components/toast/examples/toast-with-close-button.component.ts b/src/stories/components/toast/examples/toast-with-close-button.component.ts index eabd523a..f308c0a6 100644 --- a/src/stories/components/toast/examples/toast-with-close-button.component.ts +++ b/src/stories/components/toast/examples/toast-with-close-button.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; -import { Toast } from 'primeng/toast'; import { Button } from 'primeng/button'; -import { MessageService, SharedModule } from 'primeng/api'; +import { ToastComponent } from '../../../../lib/components/toast/toast.component'; +import { UiToastService } from '../../../../lib/components/toast/toast.service'; const SEVERITIES = [ { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, @@ -12,16 +12,7 @@ const SEVERITIES = [ ] as const; const template = ` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
-
+
@for (s of severities; track s.type) { @@ -60,20 +51,19 @@ const styles = ''; @Component({ selector: 'app-toast-with-close-button', standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], + imports: [ToastComponent, Button], template, styles, }) export class ToastWithCloseButtonComponent { readonly severities = SEVERITIES; - constructor(private readonly messageService: MessageService) {} + constructor(private readonly toastService: UiToastService) {} show(severity: string, icon: string): void { - this.messageService.add({ + this.toastService.add({ key: 'with-close', - severity, + severity: severity as any, summary: 'Сообщение', detail: 'Подпись', life: 5000, @@ -93,7 +83,7 @@ export const WithCloseButton: StoryObj = { source: { language: 'ts', code: ` -this.messageService.add({ +this.toastService.add({ key: 'my-toast', severity: 'info', summary: 'Сообщение', diff --git a/src/stories/components/toast/examples/toast-with-content.component.ts b/src/stories/components/toast/examples/toast-with-content.component.ts index 5aadaf6b..cde8df69 100644 --- a/src/stories/components/toast/examples/toast-with-content.component.ts +++ b/src/stories/components/toast/examples/toast-with-content.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; -import { Toast } from 'primeng/toast'; import { Button } from 'primeng/button'; -import { MessageService, SharedModule } from 'primeng/api'; +import { ToastComponent } from '../../../../lib/components/toast/toast.component'; +import { UiToastService } from '../../../../lib/components/toast/toast.service'; const SEVERITIES = [ { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, @@ -12,23 +12,7 @@ const SEVERITIES = [ ] as const; const template = ` - - -
- -
- {{ message.summary }} -
{{ message.detail }}
-
-
Дополнительный контент
-
-
-
Ячейка 1
-
Ячейка 2
-
-
-
-
+
@for (s of severities; track s.type) { @@ -74,20 +58,19 @@ const styles = ''; @Component({ selector: 'app-toast-with-content', standalone: true, - imports: [Toast, Button, SharedModule], - providers: [MessageService], + imports: [ToastComponent, Button], template, styles, }) export class ToastWithContentComponent { readonly severities = SEVERITIES; - constructor(private readonly messageService: MessageService) {} + constructor(private readonly toastService: UiToastService) {} show(severity: string, icon: string): void { - this.messageService.add({ + this.toastService.add({ key: 'with-content', - severity, + severity: severity as any, summary: 'Сообщение', detail: 'Подпись', life: 5000, @@ -107,7 +90,7 @@ export const WithContent: StoryObj = { source: { language: 'ts', code: ` -this.messageService.add({ +this.toastService.add({ key: 'my-toast', severity: 'info', summary: 'Сообщение', From 9dae52d7e11a5dd0945ed17f53b8a3fab0bad2b8 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 20 Apr 2026 09:51:21 +0700 Subject: [PATCH 5/8] =?UTF-8?q?refactor(toast):=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20stories=20=D0=BD=D0=B0?= =?UTF-8?q?=20UiToastService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Документация в toast.stories.ts теперь указывает на UiToastService вместо MessageService из primeng. --- src/stories/components/toast/toast.stories.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/stories/components/toast/toast.stories.ts b/src/stories/components/toast/toast.stories.ts index 6ca7b620..05b37cb5 100644 --- a/src/stories/components/toast/toast.stories.ts +++ b/src/stories/components/toast/toast.stories.ts @@ -28,11 +28,10 @@ const meta: Meta = { designTokens: { prefix: '--p-toast' }, docs: { description: { - component: `Компонент для отображения всплывающих уведомлений поверх интерфейса. Требует подключения \`MessageService\`. + component: `Компонент для отображения всплывающих уведомлений поверх интерфейса. Требует подключения \`UiToastService\`. \`\`\`typescript -import { ToastComponent } from '@cdek-it/angular-ui-kit'; -import { MessageService } from 'primeng/api'; +import { ToastComponent, UiToastService } from '@cdek-it/angular-ui-kit'; \`\`\``, }, }, From 0bf96eff066ec7f6ece0a5c85edb357a99531e6c Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Mon, 20 Apr 2026 10:06:16 +0700 Subject: [PATCH 6/8] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D1=91?= =?UTF-8?q?=D0=BD=20.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 808f9bdc..ce8e3c03 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,5 @@ src/assets/components/themes /storybook-static /debug-storybook.log /documentation.json + +.claude/* \ No newline at end of file From daf8812ca46382c86475f796a35d7baf9ff4a308 Mon Sep 17 00:00:00 2001 From: "ak.dmitriev" Date: Fri, 22 May 2026 14:12:21 +0700 Subject: [PATCH 7/8] DS-513 --- src/lib/components/toast/ng-package.json | 7 +++ src/lib/components/toast/provide-toast.ts | 17 +++++++ src/lib/components/toast/public_api.ts | 6 +++ src/lib/components/toast/toast.component.ts | 4 +- src/lib/components/toast/toast.service.ts | 8 ++-- .../examples/toast-position.component.ts | 22 ++++----- .../examples/toast-severities.component.ts | 18 ++++---- .../toast/examples/toast-width.component.ts | 16 +++---- .../toast-with-close-button.component.ts | 10 ++--- .../examples/toast-with-content.component.ts | 10 ++--- src/stories/components/toast/toast.stories.ts | 45 +++++++++++++++---- 11 files changed, 109 insertions(+), 54 deletions(-) create mode 100644 src/lib/components/toast/ng-package.json create mode 100644 src/lib/components/toast/provide-toast.ts create mode 100644 src/lib/components/toast/public_api.ts diff --git a/src/lib/components/toast/ng-package.json b/src/lib/components/toast/ng-package.json new file mode 100644 index 00000000..ecdf8fea --- /dev/null +++ b/src/lib/components/toast/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "public_api.ts" + } +} + diff --git a/src/lib/components/toast/provide-toast.ts b/src/lib/components/toast/provide-toast.ts new file mode 100644 index 00000000..639e9f51 --- /dev/null +++ b/src/lib/components/toast/provide-toast.ts @@ -0,0 +1,17 @@ +import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core'; +import { MessageService } from 'primeng/api'; + +/** + * Регистрирует зависимости, необходимые для работы `ExtraToastService` и ``. + * Вызывать один раз в `ApplicationConfig.providers` или в `bootstrapApplication`. + * + * @example + * // app.config.ts + * export const appConfig: ApplicationConfig = { + * providers: [provideExtraToast()], + * }; + */ +export function provideExtraToast(): EnvironmentProviders { + return makeEnvironmentProviders([MessageService]); +} + diff --git a/src/lib/components/toast/public_api.ts b/src/lib/components/toast/public_api.ts new file mode 100644 index 00000000..b4c66422 --- /dev/null +++ b/src/lib/components/toast/public_api.ts @@ -0,0 +1,6 @@ +export * from './toast.component'; +export * from './toast.service'; +export * from './provide-toast'; + + + diff --git a/src/lib/components/toast/toast.component.ts b/src/lib/components/toast/toast.component.ts index d9a86641..6fedad9a 100644 --- a/src/lib/components/toast/toast.component.ts +++ b/src/lib/components/toast/toast.component.ts @@ -20,7 +20,7 @@ const SEVERITY_ICONS: Record = { }; @Component({ - selector: 'ui-toast', + selector: 'extra-toast', standalone: true, imports: [Toast, SharedModule], template: ` @@ -38,7 +38,7 @@ const SEVERITY_ICONS: Record = { `, }) -export class ToastComponent { +export class ExtraToastComponent { @Input() position: ToastPosition = 'top-right'; @Input() key: string | undefined = undefined; @Input() life = 5000; diff --git a/src/lib/components/toast/toast.service.ts b/src/lib/components/toast/toast.service.ts index 20b84326..eac20f46 100644 --- a/src/lib/components/toast/toast.service.ts +++ b/src/lib/components/toast/toast.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { MessageService } from 'primeng/api'; import { ToastSeverity } from './toast.component'; -export interface UiToastMessage { +export interface ExtraToastMessage { key?: string; severity?: ToastSeverity; summary?: string; @@ -13,14 +13,12 @@ export interface UiToastMessage { data?: unknown; } -export { MessageService }; - @Injectable({ providedIn: 'root' }) -export class UiToastService { +export class ExtraToastService { constructor(private readonly messageService: MessageService) {} - add(message: UiToastMessage): void { + add(message: ExtraToastMessage): void { this.messageService.add(message); } diff --git a/src/stories/components/toast/examples/toast-position.component.ts b/src/stories/components/toast/examples/toast-position.component.ts index dcd3e43f..1dcf7ca4 100644 --- a/src/stories/components/toast/examples/toast-position.component.ts +++ b/src/stories/components/toast/examples/toast-position.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; import { Button } from 'primeng/button'; -import { ToastComponent } from '../../../../lib/components/toast/toast.component'; -import { UiToastService } from '../../../../lib/components/toast/toast.service'; +import { ExtraToastComponent } from '../../../../lib/components/toast/toast.component'; +import { ExtraToastService } from '../../../../lib/components/toast/toast.service'; const POSITIONS = [ { position: 'top-left', label: 'Вверх слева', key: 'pos-top-left' }, @@ -15,7 +15,7 @@ const POSITIONS = [ const template = ` @for (p of positions; track p.key) { - + }
@@ -33,14 +33,14 @@ const styles = ''; @Component({ selector: 'app-toast-position', standalone: true, - imports: [ToastComponent, Button], + imports: [ExtraToastComponent, Button], template, styles, }) export class ToastPositionComponent { readonly positions = POSITIONS; - constructor(private readonly toastService: UiToastService) {} + constructor(private readonly toastService: ExtraToastService) {} show(key: string, position: string): void { this.toastService.add({ @@ -64,12 +64,12 @@ export const Position: StoryObj = { source: { language: 'ts', code: ` - - - - - - + + + + + + `, }, }, diff --git a/src/stories/components/toast/examples/toast-severities.component.ts b/src/stories/components/toast/examples/toast-severities.component.ts index b6b60345..c5ba2f79 100644 --- a/src/stories/components/toast/examples/toast-severities.component.ts +++ b/src/stories/components/toast/examples/toast-severities.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; import { Button } from 'primeng/button'; -import { ToastComponent } from '../../../../lib/components/toast/toast.component'; -import { UiToastService } from '../../../../lib/components/toast/toast.service'; +import { ExtraToastComponent } from '../../../../lib/components/toast/toast.component'; +import { ExtraToastService } from '../../../../lib/components/toast/toast.service'; const SEVERITIES = [ { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, @@ -12,7 +12,7 @@ const SEVERITIES = [ ] as const; const template = ` - +
@for (s of severities; track s.type) { @@ -45,14 +45,14 @@ const styles = ''; @Component({ selector: 'app-toast-severities', standalone: true, - imports: [ToastComponent, Button], + imports: [ExtraToastComponent, Button], template, styles, }) export class ToastSeveritiesComponent { readonly severities = SEVERITIES; - constructor(private readonly toastService: UiToastService) {} + constructor(private readonly toastService: ExtraToastService) {} show(severity: string, icon: string): void { this.toastService.add({ @@ -77,19 +77,19 @@ export const Severities: StoryObj = { language: 'ts', code: ` import { Component } from '@angular/core'; -import { ToastComponent, UiToastService } from '@cdek-it/angular-ui-kit'; +import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; @Component({ selector: 'app-example', standalone: true, - imports: [ToastComponent], + imports: [ExtraToastComponent], template: \` - + \`, }) export class ExampleComponent { - constructor(private toastService: UiToastService) {} + constructor(private toastService: ExtraToastService) {} show(): void { this.toastService.add({ diff --git a/src/stories/components/toast/examples/toast-width.component.ts b/src/stories/components/toast/examples/toast-width.component.ts index 5bbcf9e1..ea823172 100644 --- a/src/stories/components/toast/examples/toast-width.component.ts +++ b/src/stories/components/toast/examples/toast-width.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; import { Button } from 'primeng/button'; -import { ToastComponent } from '../../../../lib/components/toast/toast.component'; -import { UiToastService } from '../../../../lib/components/toast/toast.service'; +import { ExtraToastComponent } from '../../../../lib/components/toast/toast.component'; +import { ExtraToastService } from '../../../../lib/components/toast/toast.service'; const SIZES = [ { key: 'sm', label: 'Small (20rem)', width: '20rem', cssVar: '20rem' }, @@ -12,10 +12,10 @@ const SIZES = [ ] as const; const template = ` - +>
@for (s of sizes; track s.key) { @@ -50,7 +50,7 @@ const styles = ''; @Component({ selector: 'app-toast-width', standalone: true, - imports: [ToastComponent, Button], + imports: [ExtraToastComponent, Button], template, styles, }) @@ -58,7 +58,7 @@ export class ToastWidthComponent { readonly sizes = SIZES; currentWidth = '25rem'; - constructor(private readonly toastService: UiToastService) {} + constructor(private readonly toastService: ExtraToastService) {} show(cssVar: string): void { this.currentWidth = cssVar; @@ -84,8 +84,8 @@ export const Width: StoryObj = { source: { language: 'ts', code: ` - - + + `, }, }, diff --git a/src/stories/components/toast/examples/toast-with-close-button.component.ts b/src/stories/components/toast/examples/toast-with-close-button.component.ts index f308c0a6..04b4ef4e 100644 --- a/src/stories/components/toast/examples/toast-with-close-button.component.ts +++ b/src/stories/components/toast/examples/toast-with-close-button.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; import { Button } from 'primeng/button'; -import { ToastComponent } from '../../../../lib/components/toast/toast.component'; -import { UiToastService } from '../../../../lib/components/toast/toast.service'; +import { ExtraToastComponent } from '../../../../lib/components/toast/toast.component'; +import { ExtraToastService } from '../../../../lib/components/toast/toast.service'; const SEVERITIES = [ { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, @@ -12,7 +12,7 @@ const SEVERITIES = [ ] as const; const template = ` - +
@for (s of severities; track s.type) { @@ -51,14 +51,14 @@ const styles = ''; @Component({ selector: 'app-toast-with-close-button', standalone: true, - imports: [ToastComponent, Button], + imports: [ExtraToastComponent, Button], template, styles, }) export class ToastWithCloseButtonComponent { readonly severities = SEVERITIES; - constructor(private readonly toastService: UiToastService) {} + constructor(private readonly toastService: ExtraToastService) {} show(severity: string, icon: string): void { this.toastService.add({ diff --git a/src/stories/components/toast/examples/toast-with-content.component.ts b/src/stories/components/toast/examples/toast-with-content.component.ts index cde8df69..dea3c99c 100644 --- a/src/stories/components/toast/examples/toast-with-content.component.ts +++ b/src/stories/components/toast/examples/toast-with-content.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { StoryObj } from '@storybook/angular'; import { Button } from 'primeng/button'; -import { ToastComponent } from '../../../../lib/components/toast/toast.component'; -import { UiToastService } from '../../../../lib/components/toast/toast.service'; +import { ExtraToastComponent } from '../../../../lib/components/toast/toast.component'; +import { ExtraToastService } from '../../../../lib/components/toast/toast.service'; const SEVERITIES = [ { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, @@ -12,7 +12,7 @@ const SEVERITIES = [ ] as const; const template = ` - +
@for (s of severities; track s.type) { @@ -58,14 +58,14 @@ const styles = ''; @Component({ selector: 'app-toast-with-content', standalone: true, - imports: [ToastComponent, Button], + imports: [ExtraToastComponent, Button], template, styles, }) export class ToastWithContentComponent { readonly severities = SEVERITIES; - constructor(private readonly toastService: UiToastService) {} + constructor(private readonly toastService: ExtraToastService) {} show(severity: string, icon: string): void { this.toastService.add({ diff --git a/src/stories/components/toast/toast.stories.ts b/src/stories/components/toast/toast.stories.ts index 05b37cb5..e349a19b 100644 --- a/src/stories/components/toast/toast.stories.ts +++ b/src/stories/components/toast/toast.stories.ts @@ -1,37 +1,64 @@ -import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; -import { MessageService } from 'primeng/api'; -import { ToastComponent } from '../../../lib/components/toast/toast.component'; +import { Meta, applicationConfig, moduleMetadata } from '@storybook/angular'; +import { ExtraToastComponent } from '../../../lib/components/toast/toast.component'; +import { provideExtraToast } from '../../../lib/components/toast/provide-toast'; import { ToastSeveritiesComponent, Severities } from './examples/toast-severities.component'; import { ToastWithCloseButtonComponent, WithCloseButton } from './examples/toast-with-close-button.component'; import { ToastWithContentComponent, WithContent } from './examples/toast-with-content.component'; import { ToastWidthComponent, Width } from './examples/toast-width.component'; import { ToastPositionComponent, Position } from './examples/toast-position.component'; -const meta: Meta = { +const meta: Meta = { title: 'Components/Feedback/Toast', - component: ToastComponent, + component: ExtraToastComponent, tags: ['autodocs'], decorators: [ + applicationConfig({ providers: [provideExtraToast()] }), moduleMetadata({ imports: [ - ToastComponent, + ExtraToastComponent, ToastSeveritiesComponent, ToastWithCloseButtonComponent, ToastWithContentComponent, ToastWidthComponent, ToastPositionComponent, ], - providers: [MessageService], }), ], parameters: { designTokens: { prefix: '--p-toast' }, docs: { description: { - component: `Компонент для отображения всплывающих уведомлений поверх интерфейса. Требует подключения \`UiToastService\`. + component: `Компонент для отображения всплывающих уведомлений поверх интерфейса. + +## Подключение + +Добавьте \`provideExtraToast()\` в провайдеры приложения: \`\`\`typescript -import { ToastComponent, UiToastService } from '@cdek-it/angular-ui-kit'; +// app.config.ts +import { provideExtraToast } from '@cdek-it/angular-ui-kit'; + +export const appConfig: ApplicationConfig = { + providers: [provideExtraToast()], +}; +\`\`\` + +## Использование + +\`\`\`typescript +import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; + +@Component({ + imports: [ExtraToastComponent], + template: \`\`, +}) +export class AppComponent { + private toast = inject(ExtraToastService); + + show() { + this.toast.add({ severity: 'success', summary: 'Готово', detail: 'Операция выполнена успешно' }); + } +} \`\`\``, }, }, From e46f4c807243d3ccf705f638cf0940e3ca8d4521 Mon Sep 17 00:00:00 2001 From: "ak.dmitriev" Date: Fri, 22 May 2026 14:16:35 +0700 Subject: [PATCH 8/8] DS-516 --- .../examples/toast-position.component.ts | 55 ++++++++++++-- .../examples/toast-severities.component.ts | 34 +++++++-- .../toast/examples/toast-width.component.ts | 53 +++++++++++++- .../toast-with-close-button.component.ts | 56 ++++++++++++--- .../examples/toast-with-content.component.ts | 71 ++++++++++++++++--- 5 files changed, 236 insertions(+), 33 deletions(-) diff --git a/src/stories/components/toast/examples/toast-position.component.ts b/src/stories/components/toast/examples/toast-position.component.ts index 1dcf7ca4..bed9bfa4 100644 --- a/src/stories/components/toast/examples/toast-position.component.ts +++ b/src/stories/components/toast/examples/toast-position.component.ts @@ -64,12 +64,55 @@ export const Position: StoryObj = { source: { language: 'ts', code: ` - - - - - - +import { Component } from '@angular/core'; +import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; +import { Button } from 'primeng/button'; + +const POSITIONS = [ + { position: 'top-left', label: 'Вверх слева', key: 'pos-top-left' }, + { position: 'top-center', label: 'Вверх по центру', key: 'pos-top-center' }, + { position: 'top-right', label: 'Вверх справа', key: 'pos-top-right' }, + { position: 'bottom-left', label: 'Вниз слева', key: 'pos-bottom-left' }, + { position: 'bottom-center', label: 'Вниз по центру', key: 'pos-bottom-center' }, + { position: 'bottom-right', label: 'Вниз справа', key: 'pos-bottom-right' }, +] as const; + +@Component({ + selector: 'app-example', + standalone: true, + imports: [ExtraToastComponent, Button], + template: \` + @for (p of positions; track p.key) { + + } + +
+ @for (p of positions; track p.key) { + + } +
+ \`, +}) +export class ExampleComponent { + readonly positions = POSITIONS; + + constructor(private toastService: ExtraToastService) {} + + show(key: string, position: string): void { + this.toastService.add({ + key, + severity: 'info', + summary: 'Сообщение', + detail: 'Позиция: ' + position, + life: 3000, + icon: 'ti ti-info-circle', + }); + } +} `, }, }, diff --git a/src/stories/components/toast/examples/toast-severities.component.ts b/src/stories/components/toast/examples/toast-severities.component.ts index c5ba2f79..a7bb19b6 100644 --- a/src/stories/components/toast/examples/toast-severities.component.ts +++ b/src/stories/components/toast/examples/toast-severities.component.ts @@ -78,27 +78,47 @@ export const Severities: StoryObj = { code: ` import { Component } from '@angular/core'; import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; +import { Button } from 'primeng/button'; + +const SEVERITIES = [ + { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, + { type: 'success', icon: 'ti ti-circle-check', label: 'Успех' }, + { type: 'warn', icon: 'ti ti-alert-triangle', label: 'Предупреждение' }, + { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, +] as const; @Component({ selector: 'app-example', standalone: true, - imports: [ExtraToastComponent], + imports: [ExtraToastComponent, Button], template: \` - - + + +
+ @for (s of severities; track s.type) { + + } +
\`, }) export class ExampleComponent { + readonly severities = SEVERITIES; + constructor(private toastService: ExtraToastService) {} - show(): void { + show(severity: string, icon: string): void { this.toastService.add({ - key: 'my-toast', - severity: 'info', + key: 'severities', + severity: severity as any, summary: 'Сообщение', detail: 'Подпись', life: 5000, - icon: 'ti ti-info-circle', + icon, }); } } diff --git a/src/stories/components/toast/examples/toast-width.component.ts b/src/stories/components/toast/examples/toast-width.component.ts index ea823172..9af4f71d 100644 --- a/src/stories/components/toast/examples/toast-width.component.ts +++ b/src/stories/components/toast/examples/toast-width.component.ts @@ -84,8 +84,57 @@ export const Width: StoryObj = { source: { language: 'ts', code: ` - - +import { Component } from '@angular/core'; +import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; +import { Button } from 'primeng/button'; + +const SIZES = [ + { key: 'sm', label: 'Small (20rem)', cssVar: '20rem' }, + { key: 'base', label: 'Base (25rem)', cssVar: '25rem' }, + { key: 'lg', label: 'Large (30rem)', cssVar: '30rem' }, + { key: 'xlg', label: 'X-Large (45rem)', cssVar: '45rem' }, +] as const; + +@Component({ + selector: 'app-example', + standalone: true, + imports: [ExtraToastComponent, Button], + template: \` + + +
+ @for (s of sizes; track s.key) { + + } +
+ \`, +}) +export class ExampleComponent { + readonly sizes = SIZES; + currentWidth = '25rem'; + + constructor(private toastService: ExtraToastService) {} + + show(cssVar: string): void { + this.currentWidth = cssVar; + this.toastService.clear('width-preview'); + this.toastService.add({ + key: 'width-preview', + severity: 'info', + summary: 'Сообщение', + detail: 'Ширина: ' + cssVar, + life: 3000, + icon: 'ti ti-info-circle', + }); + } +} `, }, }, diff --git a/src/stories/components/toast/examples/toast-with-close-button.component.ts b/src/stories/components/toast/examples/toast-with-close-button.component.ts index 04b4ef4e..39ae8968 100644 --- a/src/stories/components/toast/examples/toast-with-close-button.component.ts +++ b/src/stories/components/toast/examples/toast-with-close-button.component.ts @@ -83,15 +83,53 @@ export const WithCloseButton: StoryObj = { source: { language: 'ts', code: ` -this.toastService.add({ - key: 'my-toast', - severity: 'info', - summary: 'Сообщение', - detail: 'Подпись', - life: 5000, - icon: 'ti ti-info-circle', - closable: true, -}); +import { Component } from '@angular/core'; +import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; +import { Button } from 'primeng/button'; + +const SEVERITIES = [ + { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, + { type: 'success', icon: 'ti ti-circle-check', label: 'Успех' }, + { type: 'warn', icon: 'ti ti-alert-triangle', label: 'Предупреждение' }, + { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, +] as const; + +@Component({ + selector: 'app-example', + standalone: true, + imports: [ExtraToastComponent, Button], + template: \` + + +
+ @for (s of severities; track s.type) { + + } +
+ \`, +}) +export class ExampleComponent { + readonly severities = SEVERITIES; + + constructor(private toastService: ExtraToastService) {} + + show(severity: string, icon: string): void { + this.toastService.add({ + key: 'with-close', + severity: severity as any, + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon, + closable: true, + }); + } +} `, }, }, diff --git a/src/stories/components/toast/examples/toast-with-content.component.ts b/src/stories/components/toast/examples/toast-with-content.component.ts index dea3c99c..c8dea34a 100644 --- a/src/stories/components/toast/examples/toast-with-content.component.ts +++ b/src/stories/components/toast/examples/toast-with-content.component.ts @@ -90,15 +90,68 @@ export const WithContent: StoryObj = { source: { language: 'ts', code: ` -this.toastService.add({ - key: 'my-toast', - severity: 'info', - summary: 'Сообщение', - detail: 'Подпись', - life: 5000, - icon: 'ti ti-info-circle', - closable: true, -}); +import { Component } from '@angular/core'; +import { ExtraToastComponent, ExtraToastService } from '@cdek-it/angular-ui-kit'; +import { Button } from 'primeng/button'; + +const SEVERITIES = [ + { type: 'info', icon: 'ti ti-info-circle', label: 'Информация' }, + { type: 'success', icon: 'ti ti-circle-check', label: 'Успех' }, + { type: 'warn', icon: 'ti ti-alert-triangle', label: 'Предупреждение' }, + { type: 'error', icon: 'ti ti-alert-circle', label: 'Ошибка' }, +] as const; + +// Кастомный шаблон сообщения передаётся через ng-template +@Component({ + selector: 'app-example', + standalone: true, + imports: [ExtraToastComponent, Button], + template: \` + + +
+ {{ message.summary }} +
{{ message.detail }}
+
+
Дополнительный контент
+
+
+
Ячейка 1
+
Ячейка 2
+
+
+
+
+ +
+ @for (s of severities; track s.type) { + + } +
+ \`, +}) +export class ExampleComponent { + readonly severities = SEVERITIES; + + constructor(private toastService: ExtraToastService) {} + + show(severity: string, icon: string): void { + this.toastService.add({ + key: 'with-content', + severity: severity as any, + summary: 'Сообщение', + detail: 'Подпись', + life: 5000, + icon, + closable: true, + }); + } +} `, }, },