From 5dea3c104e3d1fcb96f0e3c2af41c3322e6e6fae Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Tue, 7 Apr 2026 23:43:36 +0700 Subject: [PATCH 1/2] =?UTF-8?q?progressspinner:=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F,=20=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=81=D1=8B,=20=D0=BE=D0=B1=D1=91=D1=80=D1=82?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../progressspinner.component.ts | 35 ++++++ src/prime-preset/map-tokens.ts | 5 + .../tokens/components/progressspinner.ts | 35 ++++++ .../progressspinner-monochrome.component.ts | 51 ++++++++ .../progressspinner-sizes.component.ts | 51 ++++++++ .../progressspinner.stories.ts | 118 ++++++++++++++++++ 6 files changed, 295 insertions(+) create mode 100644 src/lib/components/progressspinner/progressspinner.component.ts create mode 100644 src/prime-preset/tokens/components/progressspinner.ts create mode 100644 src/stories/components/progressspinner/examples/progressspinner-monochrome.component.ts create mode 100644 src/stories/components/progressspinner/examples/progressspinner-sizes.component.ts create mode 100644 src/stories/components/progressspinner/progressspinner.stories.ts diff --git a/src/lib/components/progressspinner/progressspinner.component.ts b/src/lib/components/progressspinner/progressspinner.component.ts new file mode 100644 index 00000000..d4647781 --- /dev/null +++ b/src/lib/components/progressspinner/progressspinner.component.ts @@ -0,0 +1,35 @@ +import { Component, Input } from '@angular/core'; +import { ProgressSpinnerModule } from 'primeng/progressspinner'; // We use Module since PrimeNG 17/18 might export it this way. Wait, earlier we saw ProgressSpinner is standalone? Actually ProgressSpinner in v18 is standalone, but importing it as ProgressSpinner works. +// Let's import the component directly. Wait, index.d.ts exports { ProgressSpinner, ProgressSpinnerModule }. Either is fine. Let's use ProgressSpinner. +import { ProgressSpinner } from 'primeng/progressspinner'; + +export type ProgressSpinnerSize = 'small' | 'medium' | 'large' | 'xlarge'; + +@Component({ + selector: 'progressspinner', + standalone: true, + imports: [ProgressSpinner], + template: ` + + ` +}) +export class ProgressSpinnerComponent { + @Input() size: ProgressSpinnerSize = 'medium'; + @Input() multicolor = true; + @Input() strokeWidth = '2'; + @Input() fill = 'none'; + @Input() animationDuration = '2s'; + @Input() ariaLabel: string | undefined = undefined; + + get primeStyleClass(): string { + const sizeClass = `p-progressspinner-${this.size}`; + const colorClass = this.multicolor ? '' : 'p-progressspinner-monochrome'; + return `${sizeClass} ${colorClass}`.trim(); + } +} diff --git a/src/prime-preset/map-tokens.ts b/src/prime-preset/map-tokens.ts index 39627587..1d2c0588 100644 --- a/src/prime-preset/map-tokens.ts +++ b/src/prime-preset/map-tokens.ts @@ -4,6 +4,7 @@ import type { AuraBaseDesignTokens } from '@primeuix/themes/aura/base'; import tokens from './tokens/tokens.json'; import { buttonCss } from './tokens/components/button'; +import { progressspinnerCss } from './tokens/components/progressspinner'; const presetTokens: Preset = { primitive: tokens.primitive as unknown as AuraBaseDesignTokens['primitive'], @@ -14,6 +15,10 @@ const presetTokens: Preset = { ...(tokens.components.button as unknown as ComponentsDesignTokens['button']), css: buttonCss, }, + progressspinner: { + ...(tokens.components.progressspinner as unknown as ComponentsDesignTokens['progressspinner']), + css: progressspinnerCss, + }, } as ComponentsDesignTokens, }; diff --git a/src/prime-preset/tokens/components/progressspinner.ts b/src/prime-preset/tokens/components/progressspinner.ts new file mode 100644 index 00000000..d901e3e1 --- /dev/null +++ b/src/prime-preset/tokens/components/progressspinner.ts @@ -0,0 +1,35 @@ +export const progressspinnerCss = ({ dt }: { dt: (token: string) => string }): string => ` +.p-progressspinner-circle { + stroke-width: ${dt('progressspinner.root.borderWidth')}; +} + +/* multicolor false */ +.p-progressspinner.p-progressspinner-monochrome .p-progressspinner-circle { + stroke: ${dt('primary.color')}; + animation: p-progressspinner-dash 1.5s ease-in-out infinite; +} + +.p-progressspinner.p-progressspinner-small, +.p-progressspinner.p-progressspinner-small .p-progressspinner-circle { + width: ${dt('progressspinner.extend.small')}; + height: ${dt('progressspinner.extend.small')}; +} + +.p-progressspinner.p-progressspinner-medium, +.p-progressspinner.p-progressspinner-medium .p-progressspinner-circle { + width: ${dt('progressspinner.extend.medium')}; + height: ${dt('progressspinner.extend.medium')}; +} + +.p-progressspinner.p-progressspinner-large, +.p-progressspinner.p-progressspinner-large .p-progressspinner-circle { + width: ${dt('progressspinner.extend.large')}; + height: ${dt('progressspinner.extend.large')}; +} + +.p-progressspinner.p-progressspinner-xlarge, +.p-progressspinner.p-progressspinner-xlarge .p-progressspinner-circle { + width: ${dt('progressspinner.extend.xlarge')}; + height: ${dt('progressspinner.extend.xlarge')}; +} +`; diff --git a/src/stories/components/progressspinner/examples/progressspinner-monochrome.component.ts b/src/stories/components/progressspinner/examples/progressspinner-monochrome.component.ts new file mode 100644 index 00000000..8e2f58f5 --- /dev/null +++ b/src/stories/components/progressspinner/examples/progressspinner-monochrome.component.ts @@ -0,0 +1,51 @@ +import { Component, Input } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ProgressSpinnerComponent } from '../../../../lib/components/progressspinner/progressspinner.component'; + +const template = ` + +`; + +@Component({ + selector: 'progressspinner-monochrome', + standalone: true, + imports: [ProgressSpinnerComponent], + template +}) +export class ProgressSpinnerMonochromeComponent { + @Input() size: any = 'medium'; + @Input() multicolor = false; +} + +export const Monochrome: StoryObj = { + render: (args) => ({ + props: args, + template: `` + }), + args: { + size: 'medium', + multicolor: false + }, + parameters: { + docs: { + description: { + story: 'Одноцветный вариант спиннера (monochrome).' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ProgressSpinnerComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + selector: 'progressspinner-monochrome', + standalone: true, + imports: [ProgressSpinnerComponent], + template: \`\` +}) +export class ProgressSpinnerMonochromeComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/progressspinner/examples/progressspinner-sizes.component.ts b/src/stories/components/progressspinner/examples/progressspinner-sizes.component.ts new file mode 100644 index 00000000..397cc189 --- /dev/null +++ b/src/stories/components/progressspinner/examples/progressspinner-sizes.component.ts @@ -0,0 +1,51 @@ +import { Component, Input } from '@angular/core'; +import { StoryObj } from '@storybook/angular'; +import { ProgressSpinnerComponent, ProgressSpinnerSize } from '../../../../lib/components/progressspinner/progressspinner.component'; + +const template = ` + +`; + +@Component({ + selector: 'progressspinner-sizes', + standalone: true, + imports: [ProgressSpinnerComponent], + template +}) +export class ProgressSpinnerSizesComponent { + @Input() size: ProgressSpinnerSize = 'xlarge'; + @Input() multicolor = true; +} + +export const Sizes: StoryObj = { + render: (args) => ({ + props: args, + template: `` + }), + args: { + size: 'xlarge', + multicolor: true + }, + parameters: { + docs: { + description: { + story: 'Изменение размера спиннера. Используйте Controls для выбора других вариантов.' + }, + source: { + language: 'ts', + code: ` +import { Component } from '@angular/core'; +import { ProgressSpinnerComponent } from '@cdek-it/angular-ui-kit'; + +@Component({ + selector: 'progressspinner-sizes', + standalone: true, + imports: [ProgressSpinnerComponent], + template: \`\` +}) +export class ProgressSpinnerSizesComponent {} + ` + } + } + } +}; diff --git a/src/stories/components/progressspinner/progressspinner.stories.ts b/src/stories/components/progressspinner/progressspinner.stories.ts new file mode 100644 index 00000000..0676ec2b --- /dev/null +++ b/src/stories/components/progressspinner/progressspinner.stories.ts @@ -0,0 +1,118 @@ +import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; +import { ProgressSpinnerComponent } from '../../../lib/components/progressspinner/progressspinner.component'; +import { Sizes, ProgressSpinnerSizesComponent } from './examples/progressspinner-sizes.component'; +import { Monochrome, ProgressSpinnerMonochromeComponent } from './examples/progressspinner-monochrome.component'; + +type ProgressSpinnerArgs = ProgressSpinnerComponent; + +const meta: Meta = { + title: 'Prime/Misc/ProgressSpinner', + component: ProgressSpinnerComponent, + tags: ['autodocs'], + decorators: [ + moduleMetadata({ imports: [ProgressSpinnerComponent, ProgressSpinnerSizesComponent, ProgressSpinnerMonochromeComponent] }) + ], + parameters: { + docs: { + description: { + component: `Используется для отображения индикатора процесса/состояния загрузки неопределенного времени. + +\`\`\`typescript +import { ProgressSpinnerComponent } from '@cdek-it/angular-ui-kit'; +\`\`\``, + }, + }, + designTokens: { prefix: '--p-progressspinner' }, + }, + argTypes: { + size: { + control: 'select', + options: ['small', 'medium', 'large', 'xlarge'], + description: 'Размер спиннера (задает вычисленные CSS-классы).', + table: { + category: 'Props', + defaultValue: { summary: 'medium' }, + type: { summary: "'small' | 'medium' | 'large' | 'xlarge'" }, + }, + }, + multicolor: { + control: 'boolean', + description: 'Включить многоцветную анимацию.', + table: { + category: 'Props', + defaultValue: { summary: 'true' }, + type: { summary: 'boolean' }, + }, + }, + strokeWidth: { + table: { disable: true }, + }, + fill: { + table: { disable: true }, + }, + animationDuration: { + control: 'text', + description: 'Длительность одной итерации анимации вращения.', + table: { + category: 'Props', + defaultValue: { summary: '2s' }, + type: { summary: 'string' }, + }, + }, + ariaLabel: { + table: { disable: true }, + }, + }, + args: { + size: 'medium', + multicolor: true, + strokeWidth: '2', + fill: 'none', + animationDuration: '2s', + }, +}; + +export default meta; +type Story = StoryObj; + +const commonTemplate = ` + +`; + +// ── Default ────────────────────────────────────────────────────────────────── +export const Default: Story = { + name: 'Default', + render: (args) => { + const parts: string[] = []; + + if (args.size && args.size !== 'medium') parts.push(`size="${args.size}"`); + if (!args.multicolor) parts.push(`[multicolor]="false"`); + if (args.strokeWidth && args.strokeWidth !== '2') parts.push(`strokeWidth="${args.strokeWidth}"`); + if (args.fill && args.fill !== 'none') parts.push(`fill="${args.fill}"`); + if (args.animationDuration && args.animationDuration !== '2s') parts.push(`animationDuration="${args.animationDuration}"`); + + const properties = parts.length > 0 ? ' ' + parts.join('\n ') : ''; + + const template = ` + +`; + return { props: args, template }; + }, + parameters: { + docs: { + description: { + story: 'Базовый пример компонента. Используйте Controls для изменения размера, цвета и толщины линии.', + }, + }, + }, +}; + +// ── Вариации ───────────────────────────────────────────────────────────────── + +export { Sizes, Monochrome }; From d2b737a5e5f2081c4954b3e34917c08e520386e0 Mon Sep 17 00:00:00 2001 From: Danil Khaliulin Date: Wed, 15 Apr 2026 15:50:09 +0700 Subject: [PATCH 2/2] =?UTF-8?q?map-tokens.ts=20=D0=B2=D0=BE=D1=81=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/prime-preset/map-tokens.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/prime-preset/map-tokens.ts b/src/prime-preset/map-tokens.ts index 437e87f4..0194af83 100644 --- a/src/prime-preset/map-tokens.ts +++ b/src/prime-preset/map-tokens.ts @@ -6,6 +6,8 @@ import tokens from './tokens/tokens.json'; import { avatarCss } from './tokens/components/avatar'; 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 { tooltipCss } from './tokens/components/tooltip'; const presetTokens: Preset = { @@ -25,10 +27,18 @@ const presetTokens: Preset = { ...(tokens.components.button as unknown as ComponentsDesignTokens['button']), css: buttonCss, }, + progressspinner: { + ...(tokens.components.progressspinner as unknown as ComponentsDesignTokens['progressspinner']), + css: progressspinnerCss, + }, tag: { ...(tokens.components.tag as unknown as ComponentsDesignTokens['tag']), css: tagCss, }, + tooltip: { + ...(tokens.components.tooltip as unknown as ComponentsDesignTokens['tooltip']), + css: tooltipCss, + }, } as ComponentsDesignTokens, };