Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
751 changes: 0 additions & 751 deletions .claude/CLAUDE-v1.6.md

This file was deleted.

58 changes: 58 additions & 0 deletions src/lib/components/message/message.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Message } from 'primeng/message';
import { ButtonDirective } from 'primeng/button';
import { SharedModule } from 'primeng/api';

export type MessageSeverity = 'success' | 'info' | 'warn' | 'error' | 'secondary' | 'contrast';

const SEVERITY_ICONS: Record<string, string> = {
info: 'ti ti-info-circle',
success: 'ti ti-circle-check',
warn: 'ti ti-alert-triangle',
error: 'ti ti-alert-circle',
};

@Component({
selector: 'ui-message',
standalone: true,
imports: [Message, ButtonDirective, SharedModule],
template: `
<p-message [severity]="severity" [closable]="false" [life]="life">
<ng-template pTemplate="container" let-closeCallback="closeCallback">
<div class="p-message-accent-line"></div>
<i [class]="resolvedIcon + ' p-message-icon'"></i>
<div class="p-message-text">
<span class="p-message-summary">{{ summary }}</span>
@if (detail) {
<div class="p-message-detail">{{ detail }}</div>
}
<ng-content></ng-content>
</div>
@if (closable) {
<button
type="button"
pButton
[text]="true"
icon="ti ti-x"
class="p-message-close-button"
(click)="closeCallback($event); onClose.emit($event)"
></button>
}
</ng-template>
</p-message>
`,
})
export class MessageComponent {
@Input() severity: MessageSeverity = 'info';
@Input() summary = '';
@Input() detail = '';
@Input() icon: string | undefined = undefined;
@Input() closable = false;
@Input() life: number | undefined = undefined;

@Output() onClose = new EventEmitter<Event>();

get resolvedIcon(): string {
return this.icon ?? SEVERITY_ICONS[this.severity] ?? 'ti ti-info-circle';
}
}
5 changes: 5 additions & 0 deletions src/prime-preset/map-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AuraBaseDesignTokens } from '@primeuix/themes/aura/base';
import tokens from './tokens/tokens.json';
import { avatarCss } from './tokens/components/avatar';
import { buttonCss } from './tokens/components/button';
import { messageCss } from './tokens/components/message';
import { tooltipCss } from './tokens/components/tooltip';

const presetTokens: Preset<AuraBaseDesignTokens> = {
Expand All @@ -20,6 +21,10 @@ const presetTokens: Preset<AuraBaseDesignTokens> = {
...(tokens.components.button as unknown as ComponentsDesignTokens['button']),
css: buttonCss,
},
message: {
...(tokens.components.message as unknown as ComponentsDesignTokens['message']),
css: messageCss,
},
tooltip: {
...(tokens.components.tooltip as unknown as ComponentsDesignTokens['tooltip']),
css: tooltipCss,
Expand Down
150 changes: 150 additions & 0 deletions src/prime-preset/tokens/components/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
export const messageCss = ({ dt }: { dt: (token: string) => string }): string => `
/* Основной контейнер message */
.p-message {
width: ${dt('message.extend.width')};
overflow: hidden;
position: relative;
}

/* Контент message с приоритизацией align-items */
.p-message .p-message-content {
display: flex;
align-items: flex-start;
width: stretch;
border-radius: ${dt('message.root.borderRadius')};
}

/* Текстовый блок message */
.p-message-text {
flex: 1;
display: flex;
flex-direction: column;
gap: ${dt('message.extend.extText.gap')};
}

/* Заголовок message */
.p-message-summary {
font-family: ${dt('fonts.fontFamily.base')};
font-weight: ${dt('message.text.fontWeight')};
line-height: ${dt('fonts.lineHeight.250')};
font-size: ${dt('message.text.fontSize')};
}

/* Детальное описание message */
.p-message .p-message-detail {
font-family: ${dt('fonts.fontFamily.base')};
font-size: ${dt('fonts.fontSize.200')};
line-height: ${dt('fonts.lineHeight.250')};
font-weight: ${dt('fonts.fontWeight.regular')};
}

/* Кнопка закрытия message */
.p-message .p-message-content .p-message-close-button {
width: ${dt('message.closeButton.width')};
height: ${dt('message.closeButton.height')};
margin: 0;
padding: 0;
right: 0;
}

/* Общие стили border для кнопки закрытия всех типов message */
.p-message-info .p-message-close-button,
.p-message-success .p-message-close-button,
.p-message-warn .p-message-close-button,
.p-message-error .p-message-close-button {
border: ${dt('message.extend.extCloseButton.width')} solid;
}

/* Общие стили для акцентной линии всех типов message */
.p-message-info .p-message-accent-line,
.p-message-success .p-message-accent-line,
.p-message-warn .p-message-accent-line,
.p-message-error .p-message-accent-line {
width: ${dt('message.extend.extAccentLine.width')};
position: absolute;
left: 0;
top: 0;
bottom: 0;
border-radius: ${dt('message.root.borderRadius')} 0 0 ${dt('message.root.borderRadius')};
}

/* Стили для message типа Info */
.p-message-info .p-message-icon {
color: ${dt('message.extend.extInfo.color')};
}

.p-message-info .p-message-close-button {
color: ${dt('message.extend.extInfo.closeButton.color')};
border-color: ${dt('message.extend.extInfo.closeButton.borderColor')};
}

.p-message.p-message-info .p-message-close-button.p-button-text:not(:disabled):hover {
background: ${dt('message.colorScheme.light.info.closeButton.hoverBackground')};
border-color: ${dt('message.extend.extInfo.closeButton.borderColor')};
color: ${dt('message.extend.extInfo.closeButton.color')};
}

.p-message-info .p-message-accent-line {
background: ${dt('message.extend.extInfo.color')};
}

/* Стили для message типа Success */
.p-message-success .p-message-icon {
color: ${dt('message.extend.extSuccess.color')};
}

.p-message-success .p-message-close-button {
color: ${dt('message.extend.extSuccess.closeButton.color')};
border-color: ${dt('message.extend.extSuccess.closeButton.borderColor')};
}

.p-message.p-message-success .p-message-close-button.p-button-text:not(:disabled):hover {
background: ${dt('message.colorScheme.light.success.closeButton.hoverBackground')};
border-color: ${dt('message.extend.extSuccess.closeButton.borderColor')};
color: ${dt('message.extend.extSuccess.closeButton.color')};
}

.p-message-success .p-message-accent-line {
background: ${dt('message.extend.extSuccess.color')};
}

/* Стили для message типа Warn */
.p-message-warn .p-message-icon {
color: ${dt('message.extend.extWarn.color')};
}

.p-message-warn .p-message-close-button {
color: ${dt('message.extend.extWarn.closeButton.color')};
border-color: ${dt('message.extend.extWarn.closeButton.borderColor')};
}

.p-message.p-message-warn .p-message-close-button.p-button-text:not(:disabled):hover {
background: ${dt('message.colorScheme.light.warn.closeButton.hoverBackground')};
border-color: ${dt('message.extend.extWarn.closeButton.borderColor')};
color: ${dt('message.extend.extWarn.closeButton.color')};
}

.p-message-warn .p-message-accent-line {
background: ${dt('message.extend.extWarn.color')};
}

/* Стили для message типа Error */
.p-message-error .p-message-icon {
color: ${dt('message.extend.extError.color')};
}

.p-message-error .p-message-close-button {
color: ${dt('message.extend.extError.closeButton.color')};
border-color: ${dt('message.extend.extError.closeButton.borderColor')};
}

.p-message.p-message-error .p-message-close-button.p-button-text:not(:disabled):hover {
background: ${dt('message.colorScheme.light.error.closeButton.hoverBackground')};
border-color: ${dt('message.extend.extError.closeButton.borderColor')};
color: ${dt('message.extend.extError.closeButton.color')};
}

.p-message-error .p-message-accent-line {
background: ${dt('message.extend.extError.color')};
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Component } from '@angular/core';
import { StoryObj } from '@storybook/angular';
import { MessageComponent } from '../../../../lib/components/message/message.component';

const template = `
<div class="flex flex-col gap-4">
<ui-message severity="info" summary="Message" detail="caption"></ui-message>
<ui-message severity="success" summary="Message" detail="caption"></ui-message>
<ui-message severity="warn" summary="Message" detail="caption"></ui-message>
<ui-message severity="error" summary="Message" detail="caption"></ui-message>
</div>
`;

@Component({
selector: 'app-message-severities',
standalone: true,
imports: [MessageComponent],
template,
})
export class MessageSeveritiesComponent {}

export const Severities: StoryObj = {
render: () => ({
template: `<app-message-severities></app-message-severities>`,
}),
parameters: {
controls: { disable: true },
docs: {
description: { story: 'Четыре типа сообщений: информация, успех, предупреждение, ошибка.' },
source: {
language: 'html',
code: `
<ui-message severity="info" summary="Message" detail="caption"></ui-message>
<ui-message severity="success" summary="Message" detail="caption"></ui-message>
<ui-message severity="warn" summary="Message" detail="caption"></ui-message>
<ui-message severity="error" summary="Message" detail="caption"></ui-message>
`,
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Component } from '@angular/core';
import { StoryObj } from '@storybook/angular';
import { MessageComponent } from '../../../../lib/components/message/message.component';

const template = `
<div class="flex flex-col gap-4">
<ui-message severity="info" summary="Message" detail="caption" [closable]="true"></ui-message>
<ui-message severity="success" summary="Message" detail="caption" [closable]="true"></ui-message>
<ui-message severity="warn" summary="Message" detail="caption" [closable]="true"></ui-message>
<ui-message severity="error" summary="Message" detail="caption" [closable]="true"></ui-message>
</div>
`;

@Component({
selector: 'app-message-with-close-button',
standalone: true,
imports: [MessageComponent],
template,
})
export class MessageWithCloseButtonComponent {}

export const WithCloseButton: StoryObj = {
render: () => ({
template: `<app-message-with-close-button></app-message-with-close-button>`,
}),
parameters: {
controls: { disable: true },
docs: {
description: { story: 'Сообщения с кнопкой закрытия.' },
source: {
language: 'html',
code: `<ui-message severity="info" summary="Message" detail="caption" [closable]="true"></ui-message>`,
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Component } from '@angular/core';
import { StoryObj } from '@storybook/angular';
import { MessageComponent } from '../../../../lib/components/message/message.component';

const template = `
<div class="flex flex-col gap-4">
<ui-message severity="info" summary="Message" detail="caption" [closable]="true">
<div class="mt-4">
<div class="text-sm">CONTENT</div>
</div>
<div class="flex gap-2 mt-2">
<div class="text-sm">Cell 1</div>
<div class="text-sm">Cell 2</div>
</div>
</ui-message>
<ui-message severity="success" summary="Message" detail="caption" [closable]="true">
<div class="mt-4">
<div class="text-sm">CONTENT</div>
</div>
<div class="flex gap-2 mt-2">
<div class="text-sm">Cell 1</div>
<div class="text-sm">Cell 2</div>
</div>
</ui-message>
<ui-message severity="warn" summary="Message" detail="caption" [closable]="true">
<div class="mt-4">
<div class="text-sm">CONTENT</div>
</div>
<div class="flex gap-2 mt-2">
<div class="text-sm">Cell 1</div>
<div class="text-sm">Cell 2</div>
</div>
</ui-message>
<ui-message severity="error" summary="Message" detail="caption" [closable]="true">
<div class="mt-4">
<div class="text-sm">CONTENT</div>
</div>
<div class="flex gap-2 mt-2">
<div class="text-sm">Cell 1</div>
<div class="text-sm">Cell 2</div>
</div>
</ui-message>
</div>
`;

@Component({
selector: 'app-message-with-content',
standalone: true,
imports: [MessageComponent],
template,
})
export class MessageWithContentComponent {}

export const WithContent: StoryObj = {
render: () => ({
template: `<app-message-with-content></app-message-with-content>`,
}),
parameters: {
controls: { disable: true },
docs: {
description: { story: 'Сообщения с дополнительным контентом и кнопкой закрытия.' },
source: {
language: 'html',
code: `
<ui-message severity="info" summary="Message" detail="caption" [closable]="true">
<div class="mt-4">
<div class="text-sm">CONTENT</div>
</div>
<div class="flex gap-2 mt-2">
<div class="text-sm">Cell 1</div>
<div class="text-sm">Cell 2</div>
</div>
</ui-message>
`,
},
},
},
};
Loading