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.

64 changes: 64 additions & 0 deletions src/lib/components/drawer/drawer.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
Component,
ContentChild,
EventEmitter,
Input,
Output,
TemplateRef,
} from '@angular/core';
import { Drawer } from 'primeng/drawer';
import { SharedModule } from 'primeng/api';
import { NgIf, NgTemplateOutlet } from '@angular/common';

@Component({
selector: 'drawer',
standalone: true,
imports: [Drawer, SharedModule, NgIf, NgTemplateOutlet],
template: `
<p-drawer
[visible]="visible"
[header]="header"
[modal]="modal"
[fullScreen]="fullScreen"
[dismissible]="dismissible"
[showCloseIcon]="showCloseIcon"
[closeOnEscape]="closeOnEscape"
[blockScroll]="blockScroll"
[styleClass]="sizeClass"
[position]="position"
[closeButtonProps]="{ text: true, rounded: true }"
(visibleChange)="visibleChange.emit($event)"
(onShow)="onShow.emit()"
(onHide)="onHide.emit()"
>
<ng-content></ng-content>
<ng-template pTemplate="footer" *ngIf="footerTemplate">
<ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
</ng-template>
</p-drawer>
`,
})
export class DrawerComponent {
@Input() visible = false;
@Input() header: string | undefined;
@Input() position: 'left' | 'right' | 'top' | 'bottom' = 'right';
@Input() size: 'default' | 'sm' | 'lg' | 'xlg' = 'default';
@Input() modal = true;
@Input() fullScreen = false;
@Input() dismissible = true;
@Input() showCloseIcon = true;
@Input() closeOnEscape = true;
@Input() blockScroll = true;

@Output() visibleChange = new EventEmitter<boolean>();
@Output() onShow = new EventEmitter<void>();
@Output() onHide = new EventEmitter<void>();

@ContentChild('drawerFooter') footerTemplate: TemplateRef<unknown> | null =
null;

get sizeClass(): string {
if (this.size === 'default') return '';
return `p-drawer-${this.size}`;
}
}
5 changes: 5 additions & 0 deletions src/prime-preset/map-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { buttonCss } from './tokens/components/button';
import { checkboxCss } from './tokens/components/checkbox';
import { progressspinnerCss } from './tokens/components/progressspinner';
import { tagCss } from './tokens/components/tag';
import { drawerCss } from './tokens/components/drawer';
import { tooltipCss } from './tokens/components/tooltip';

const presetTokens: Preset<AuraBaseDesignTokens> = {
Expand All @@ -19,6 +20,10 @@ const presetTokens: Preset<AuraBaseDesignTokens> = {
...(tokens.components.avatar as unknown as ComponentsDesignTokens['avatar']),
css: avatarCss,
},
drawer: {
...(tokens.components.drawer as unknown as ComponentsDesignTokens['drawer']),
css: drawerCss,
},
checkbox: {
...(tokens.components.checkbox as unknown as ComponentsDesignTokens['checkbox']),
css: checkboxCss,
Expand Down
66 changes: 66 additions & 0 deletions src/prime-preset/tokens/components/drawer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const drawerCss = ({ dt }: { dt: (token: string) => string }): string => `

/* Скругление углов */
.p-drawer.p-component {
border-radius: ${dt('drawer.extend.borderRadius')};
}

/* Нижняя граница и внутренние отступы заголовка */
.p-drawer.p-component .p-drawer-header {
border-bottom: 1px solid ${dt('drawer.extend.extHeader.borderColor')};
padding: ${dt('overlay.modal.padding.300')} ${dt('overlay.modal.padding.300')} ${dt('overlay.modal.padding.100')} ${dt('overlay.modal.padding.300')};
}

/* Типографика */
.p-drawer.p-component .p-drawer-title {
font-weight: ${dt('drawer.title.fontWeight')};
font-size: ${dt('drawer.title.fontSize')};
}

/* Кнопки действий в заголовке - расстояние между элементами */
.p-drawer.p-component .p-drawer-header-actions {
gap: ${dt('drawer.extend.extHeader.gap')};
}

/* Внутренние отступы контента и футера */
.p-drawer.p-component .p-drawer-content {
padding: ${dt('overlay.modal.padding.300')};
}

.p-drawer.p-component .p-drawer-footer {
padding: 0 ${dt('overlay.modal.padding.300')} ${dt('overlay.modal.padding.300')} ${dt('overlay.modal.padding.300')};
}

/* Боковые drawer (слева/справа) - базовые размеры и отступы от краев экрана */
.p-drawer.p-component.p-drawer-left,
.p-drawer.p-component.p-drawer-right {
margin: ${dt('drawer.extend.padding')};
width: ${dt('drawer.root.width')};
height: calc(100% - ${dt('drawer.extend.padding')} * 2);
}

.p-drawer.p-component.p-drawer-left.p-drawer-sm,
.p-drawer.p-component.p-drawer-right.p-drawer-sm {
width: ${dt('drawer.sm.width')};
}

.p-drawer.p-component.p-drawer-left.p-drawer-lg,
.p-drawer.p-component.p-drawer-right.p-drawer-lg {
width: ${dt('drawer.lg.width')};
}

.p-drawer.p-component.p-drawer-left.p-drawer-xlg,
.p-drawer.p-component.p-drawer-right.p-drawer-xlg {
width: ${dt('drawer.xlg.width')};
}

/* Горизонтальные drawer (сверху/снизу) - базовые размеры и отступы от краев экрана */
.p-drawer.p-component.p-drawer-top,
.p-drawer.p-component.p-drawer-bottom {
margin: ${dt('drawer.extend.padding')};
width: calc(100% - ${dt('drawer.extend.padding')} * 2);
}

`;

export { drawerCss };
14 changes: 12 additions & 2 deletions src/prime-preset/tokens/tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -2805,10 +2805,20 @@
"background": "{overlay.modal.background}",
"borderColor": "{overlay.modal.borderColor}",
"color": "{overlay.modal.color}",
"shadow": "{overlay.modal.shadow}"
"shadow": "{overlay.modal.shadow}",
"width": "{overlay.width}"
},
"sm": {
"width": "{overlay.sm.width}"
},
"lg": {
"width": "{overlay.lg.width}"
},
"xlg": {
"width": "{overlay.xlg.width}"
},
"header": {
"padding": "{overlay.modal.padding.300} {overlay.modal.padding.300} {overlay.modal.padding.100} {overlay.modal.padding.300} "
"padding": "{overlay.modal.padding.300} {overlay.modal.padding.300} {overlay.modal.padding.200} {overlay.modal.padding.300} "
},
"title": {
"fontSize": "{fonts.fontSize.500}",
Expand Down
202 changes: 202 additions & 0 deletions src/stories/components/drawer/drawer.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { DrawerComponent } from '../../../lib/components/drawer/drawer.component';
import { ButtonComponent } from '../../../lib/components/button/button.component';
import { DrawerSizesComponent, Sizes as SizesStory } from './examples/drawer-sizes.component';
import { DrawerPositionComponent, Position as PositionStory } from './examples/drawer-position.component';
import { DrawerWithFooterComponent, WithFooter as WithFooterStory } from './examples/drawer-with-footer.component';
import { DrawerFullScreenComponent, FullScreen as FullScreenStory } from './examples/drawer-full-screen.component';
import { DrawerWithoutModalComponent, WithoutModal as WithoutModalStory } from './examples/drawer-without-modal.component';

type DrawerArgs = DrawerComponent & { visible: boolean };

const meta: Meta<DrawerArgs> = {
title: 'Components/Overlay/Drawer',
component: DrawerComponent,
tags: ['autodocs'],
decorators: [
moduleMetadata({
imports: [
DrawerComponent,
ButtonComponent,
DrawerSizesComponent,
DrawerPositionComponent,
DrawerWithFooterComponent,
DrawerFullScreenComponent,
DrawerWithoutModalComponent,
],
}),
],
parameters: {
docs: {
description: {
component: `Drawer — панель, отображаемая как оверлей у края экрана.

\`\`\`typescript
import { DrawerComponent } from '@cdek-it/angular-ui-kit';
\`\`\``,
},
},
designTokens: { prefix: '--p-drawer' },
},
argTypes: {
visible: {
control: 'boolean',
description: 'Видимость drawer (two-way binding)',
table: {
category: 'Props',
defaultValue: { summary: 'false' },
type: { summary: 'boolean' },
},
},
header: {
control: 'text',
description: 'Заголовок drawer',
table: {
category: 'Props',
defaultValue: { summary: 'undefined' },
type: { summary: 'string' },
},
},
position: {
control: 'select',
options: ['left', 'right', 'top', 'bottom'],
description: 'Позиция drawer',
table: {
category: 'Props',
defaultValue: { summary: 'right' },
type: { summary: "'left' | 'right' | 'top' | 'bottom'" },
},
},
size: {
control: 'select',
options: ['default', 'sm', 'lg', 'xlg'],
description: 'Размер drawer',
table: {
category: 'Props',
defaultValue: { summary: 'default' },
type: { summary: "'default' | 'sm' | 'lg' | 'xlg'" },
},
},
modal: {
control: 'boolean',
description: 'Показывать модальный оверлей',
table: {
category: 'Props',
defaultValue: { summary: 'true' },
type: { summary: 'boolean' },
},
},
fullScreen: {
control: 'boolean',
description: 'Полноэкранный режим',
table: {
category: 'Props',
defaultValue: { summary: 'false' },
type: { summary: 'boolean' },
},
},
dismissible: {
control: 'boolean',
description: 'Закрытие по клику на оверлей',
table: {
category: 'Props',
defaultValue: { summary: 'true' },
type: { summary: 'boolean' },
},
},
showCloseIcon: {
control: 'boolean',
description: 'Показывать кнопку закрытия',
table: {
category: 'Props',
defaultValue: { summary: 'true' },
type: { summary: 'boolean' },
},
},
closeOnEscape: {
control: 'boolean',
description: 'Закрытие по Escape',
table: {
category: 'Props',
defaultValue: { summary: 'true' },
type: { summary: 'boolean' },
},
},
blockScroll: {
control: 'boolean',
description: 'Блокировка прокрутки страницы',
table: {
category: 'Props',
defaultValue: { summary: 'true' },
type: { summary: 'boolean' },
},
},
},
};

export default meta;
type Story = StoryObj<DrawerArgs>;

// ── Default ───────────────────────────────────────────────────────────────────

export const Default: Story = {
name: 'Default',
render: (args) => ({
props: { ...args, visible: false },
template: `
<button label="Open Drawer" (click)="visible = true"></button>
<drawer
[(visible)]="visible"
[header]="header"
[position]="position"
[size]="size"
[modal]="modal"
[fullScreen]="fullScreen"
[dismissible]="dismissible"
[showCloseIcon]="showCloseIcon"
[closeOnEscape]="closeOnEscape"
[blockScroll]="blockScroll"
>
<p>Drawer content.</p>
</drawer>
`,
}),
args: {
header: 'Drawer',
position: 'right',
size: 'default',
modal: true,
fullScreen: false,
dismissible: true,
showCloseIcon: true,
closeOnEscape: true,
blockScroll: true,
},
parameters: {
docs: {
description: {
story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.',
},
},
},
};

// ── Sizes ─────────────────────────────────────────────────────────────────────

export const Sizes: Story = SizesStory;

// ── Position ──────────────────────────────────────────────────────────────────

export const Position: Story = PositionStory;

// ── WithFooter ────────────────────────────────────────────────────────────────

export const WithFooter: Story = WithFooterStory;

// ── FullScreen ────────────────────────────────────────────────────────────────

export const FullScreen: Story = FullScreenStory;

// ── WithoutModal ──────────────────────────────────────────────────────────────

export const WithoutModal: Story = WithoutModalStory;
Loading
Loading