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
22 changes: 22 additions & 0 deletions src/lib/components/scroll-panel/scroll-panel.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Component, Input } from '@angular/core';
import { ScrollPanel } from 'primeng/scrollpanel';

@Component({
selector: 'scroll-panel',
host: { style: 'display: block' },
standalone: true,
imports: [ScrollPanel],
template: `
<p-scrollpanel
[step]="step"
[style]="{ width: width, height: height }"
>
<ng-content></ng-content>
</p-scrollpanel>
`,
})
export class ScrollPanelComponent {
@Input() step = 10;
@Input() height = '100px';
@Input() width = '100%';
}
20 changes: 20 additions & 0 deletions src/prime-preset/tokens/components/scroll-panel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Кастомная CSS-стилизация для компонента p-scrollpanel.
* Подключается в map-tokens.ts: `import { scrollPanelCss } from './tokens/components/scroll-panel'`
*/
export const scrollPanelCss = ({ dt }: { dt: (token: string) => string }): string => `
/* ─── Полоса прокрутки ─── */
.p-scrollpanel-bar {
background: ${dt('scrollpanel.bar.background')};
border-radius: ${dt('scrollpanel.bar.borderRadius')};
transition-duration: ${dt('scrollpanel.root.transitionDuration')};
}

.p-scrollpanel-bar:focus-visible {
outline-width: ${dt('scrollpanel.bar.focusRing.width')};
outline-style: ${dt('scrollpanel.bar.focusRing.style')};
outline-color: ${dt('scrollpanel.bar.focusRing.color')};
outline-offset: ${dt('scrollpanel.bar.focusRing.offset')};
box-shadow: ${dt('scrollpanel.bar.focusRing.shadow')};
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import { ScrollPanelComponent } from '../../../../lib/components/scroll-panel/scroll-panel.component';

const template = `
<div class="bg-surface-ground">
<scroll-panel height="200px">
<div style="width: max-content">
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123456 · Москва → Новосибирск · 2.5 кг · 3 места · Принят 14 апр 09:15 · Доставлен 15 апр 14:20</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123457 · Санкт-Петербург → Казань · 0.8 кг · 1 место · Принят 13 апр 11:00 · Доставлен 15 апр 10:30</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123458 · Екатеринбург → Краснодар · 5.2 кг · 2 места · Принят 12 апр 08:45 · В пути</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123459 · Нижний Новгород → Омск · 1.1 кг · 1 место · Принят 14 апр 15:00 · Ожидает отправки</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123460 · Самара → Ростов-на-Дону · 3.7 кг · 4 места · Принят 11 апр 13:20 · Доставлен 14 апр 09:00</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123461 · Уфа → Владивосток · 7.3 кг · 5 мест · Принят 10 апр 10:00 · Задержан на сортировке</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123462 · Пермь → Иркутск · 2.0 кг · 2 места · Принят 13 апр 09:30 · В пути</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123463 · Воронеж → Красноярск · 4.5 кг · 3 места · Принят 12 апр 16:00 · В пути</p>
</div>
</scroll-panel>
</div>
`;
const styles = '';

@Component({
selector: 'app-scroll-panel-both',
standalone: true,
imports: [ScrollPanelComponent],
template,
styles,
})
export class ScrollPanelBothComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Component } from '@angular/core';
import { ScrollPanelComponent } from '../../../../lib/components/scroll-panel/scroll-panel.component';

const template = `
<div class="bg-surface-ground">
<scroll-panel height="80px">
<p style="white-space: nowrap; margin: 0; line-height: 1.5">
Заказ №ЦД-00123456 · Москва → Новосибирск · Принят 14 апр 09:15 · Передан перевозчику 14 апр 14:30 · Отправлен из Москвы 14 апр 23:50 · Прибыл в Новосибирск 15 апр 08:00 · Передан курьеру Петрову А.В. 15 апр 12:00 · Доставлен получателю 15 апр 14:20
</p>
</scroll-panel>
</div>
`;
const styles = '';

@Component({
selector: 'app-scroll-panel-horizontal',
standalone: true,
imports: [ScrollPanelComponent],
template,
styles,
})
export class ScrollPanelHorizontalComponent {}
200 changes: 200 additions & 0 deletions src/stories/components/scroll-panel/scroll-panel.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { ScrollPanelComponent } from '../../../lib/components/scroll-panel/scroll-panel.component';
import { ScrollPanelHorizontalComponent } from './examples/scroll-panel-horizontal.component';
import { ScrollPanelBothComponent } from './examples/scroll-panel-both.component';

const TRACKING_CONTENT = `
<div class="p-3">
<p class="font-semibold m-0 mb-2">Заказ №ЦД-00123456 · Москва → Новосибирск</p>
<p class="m-0 mb-1 text-sm">14 апр, 09:15 — Принят в сортировочном центре «Москва-Север»</p>
<p class="m-0 mb-1 text-sm">14 апр, 14:30 — Передан перевозчику CDEK</p>
<p class="m-0 mb-1 text-sm">14 апр, 23:50 — Отправлен из Москвы</p>
<p class="m-0 mb-1 text-sm">15 апр, 08:00 — Прибыл в сортировочный центр «Новосибирск»</p>
<p class="m-0 mb-1 text-sm">15 апр, 12:00 — Передан курьеру Петрову А.В.</p>
<p class="m-0 mb-1 text-sm">15 апр, 14:20 — Попытка доставки (получатель отсутствовал)</p>
<p class="m-0 mb-1 text-sm">15 апр, 18:00 — Ожидает получения в ПВЗ на ул. Ленина, 42</p>
<p class="m-0 mb-1 text-sm">16 апр, 10:30 — Доставлен получателю</p>
</div>
`;

const HORIZONTAL_CONTENT = `
<p style="white-space: nowrap; margin: 0; line-height: 1.5">
Заказ №ЦД-00123456 · Москва → Новосибирск · Принят 14 апр 09:15 · Передан перевозчику 14 апр 14:30 · Отправлен из Москвы 14 апр 23:50 · Прибыл в Новосибирск 15 апр 08:00 · Передан курьеру Петрову А.В. 15 апр 12:00 · Доставлен получателю 15 апр 14:20
</p>
`;

const BOTH_CONTENT = `
<div style="width: max-content">
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123456 · Москва → Новосибирск · 2.5 кг · 3 места · Принят 14 апр 09:15 · Доставлен 15 апр 14:20</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123457 · Санкт-Петербург → Казань · 0.8 кг · 1 место · Принят 13 апр 11:00 · Доставлен 15 апр 10:30</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123458 · Екатеринбург → Краснодар · 5.2 кг · 2 места · Принят 12 апр 08:45 · В пути</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123459 · Нижний Новгород → Омск · 1.1 кг · 1 место · Принят 14 апр 15:00 · Ожидает отправки</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123460 · Самара → Ростов-на-Дону · 3.7 кг · 4 места · Принят 11 апр 13:20 · Доставлен 14 апр 09:00</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123461 · Уфа → Владивосток · 7.3 кг · 5 мест · Принят 10 апр 10:00 · Задержан на сортировке</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123462 · Пермь → Иркутск · 2.0 кг · 2 места · Принят 13 апр 09:30 · В пути</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123463 · Воронеж → Красноярск · 4.5 кг · 3 места · Принят 12 апр 16:00 · В пути</p>
</div>
`;

const meta: Meta<ScrollPanelComponent> = {
title: 'Components/Panel/ScrollPanel',
component: ScrollPanelComponent,
tags: ['autodocs'],
decorators: [
moduleMetadata({
imports: [
ScrollPanelComponent,
ScrollPanelHorizontalComponent,
ScrollPanelBothComponent,
],
}),
],
parameters: {
docs: {
description: {
component: `Кроссбраузерная панель с кастомной полосой прокрутки. Заменяет стандартный скроллбар браузера на стилизованный в соответствии с темой.

\`\`\`typescript
import { ScrollPanelModule } from 'primeng/scrollpanel';
\`\`\``,
},
},
designTokens: { prefix: '--p-scrollpanel' },
},
argTypes: {
step: {
control: 'number',
description: 'Шаг прокрутки при нажатии клавиш со стрелками (в пикселях)',
table: {
category: 'Props',
defaultValue: { summary: '10' },
type: { summary: 'number' },
},
},
height: {
control: 'text',
description: 'Высота области прокрутки',
table: {
category: 'Props',
defaultValue: { summary: '100px' },
type: { summary: 'string' },
},
},
width: {
control: 'text',
description: 'Ширина области прокрутки',
table: {
category: 'Props',
defaultValue: { summary: '100%' },
type: { summary: 'string' },
},
},
},
args: {
step: 10,
height: '100px',
width: '100%',
},
};

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

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

export const Default: Story = {
name: 'Default',
render: (args) => {
const parts: string[] = [];

if (args.height && args.height !== '100px') parts.push(`height="${args.height}"`);
if (args.width && args.width !== '100%') parts.push(`width="${args.width}"`);
if (args.step !== 10) parts.push(`[step]="${args.step}"`);

const attrStr = parts.length ? `\n ${parts.join('\n ')}\n` : '';
const template = `<scroll-panel${attrStr}>${TRACKING_CONTENT}</scroll-panel>`;

return { props: args, template };
},
parameters: {
docs: {
description: {
story: 'Базовый пример компонента. Используйте Controls для интерактивного изменения пропсов.',
},
},
},
};

// ── Stories ──────────────────────────────────────────────────────────────────

export const Horizontal: Story = {
render: (args) => ({
props: args,
template: `<scroll-panel [step]="step" height="80px" [width]="width">${HORIZONTAL_CONTENT}</scroll-panel>`,
}),
args: { step: 10, height: '80px', width: '100%' },
parameters: {
docs: {
description: { story: 'Горизонтальная прокрутка. Контент без переносов выходит за ширину контейнера.' },
source: {
language: 'ts',
code: `
import { Component } from '@angular/core';
import { ScrollPanelComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-scroll-panel-horizontal',
standalone: true,
imports: [ScrollPanelComponent],
template: \`
<scroll-panel height="80px">
<p style="white-space: nowrap; margin: 0; line-height: 1.5">
Заказ №ЦД-00123456 · Москва → Новосибирск · Принят 14 апр 09:15 · Передан перевозчику 14 апр 14:30 · Отправлен из Москвы 14 апр 23:50 · Прибыл в Новосибирск 15 апр 08:00 · Доставлен получателю 15 апр 14:20
</p>
</scroll-panel>
\`,
})
export class ScrollPanelHorizontalComponent {}
`,
},
},
},
};

export const Both: Story = {
render: (args) => ({
props: args,
template: `<scroll-panel [step]="step" [height]="height" [width]="width">${BOTH_CONTENT}</scroll-panel>`,
}),
args: { step: 10, height: '200px', width: '50%' },
parameters: {
docs: {
description: { story: 'Прокрутка в обоих направлениях — таблица отправлений шире и длиннее контейнера.' },
source: {
language: 'ts',
code: `
import { Component } from '@angular/core';
import { ScrollPanelComponent } from '@cdek-it/angular-ui-kit';

@Component({
selector: 'app-scroll-panel-both',
standalone: true,
imports: [ScrollPanelComponent],
template: \`
<scroll-panel height="200px" width="50%">
<div style="width: max-content">
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123456 · Москва → Новосибирск · 2.5 кг · 3 места · Принят 14 апр · Доставлен 15 апр</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123457 · Санкт-Петербург → Казань · 0.8 кг · 1 место · Принят 13 апр · Доставлен 15 апр</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123458 · Екатеринбург → Краснодар · 5.2 кг · 2 места · Принят 12 апр · В пути</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123459 · Нижний Новгород → Омск · 1.1 кг · 1 место · Принят 14 апр · Ожидает отправки</p>
<p style="white-space: nowrap; margin: 0 0 0.5rem 0">№ЦД-00123460 · Самара → Ростов-на-Дону · 3.7 кг · 4 места · Принят 11 апр · Доставлен 14 апр</p>
</div>
</scroll-panel>
\`,
})
export class ScrollPanelBothComponent {}
`,
},
},
},
};
Loading