Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
51 changes: 51 additions & 0 deletions src/lib/components/menu/menu.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component, Input, ViewChild } from '@angular/core';
import { Menu } from 'primeng/menu';
import { MenuItem, PrimeTemplate } from 'primeng/api';

export interface MenuModel extends MenuItem {
caption?: string;
}

@Component({
selector: 'menu',
host: { style: 'display: contents' },
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

для чего?

Copy link
Copy Markdown
Author

@khaliulin khaliulin Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxyIX делает хост-элемент menu прозрачным для layout (flex/grid родителя его не видят). Без этого menu был бы блочным элементом, ломающим позиционирование.

standalone: true,
imports: [Menu, PrimeTemplate],
template: `
<p-menu #menuRef [model]="model" [popup]="popup" [appendTo]="popup ? 'body' : null">
<ng-template pTemplate="item" let-item>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

опять же вопрос с кастомизациями. разработчикам не придется свои шаблоны передавать?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxyIX добавлен itemTemplate для кастомизации элемента меню 7896268

<a
class="p-menu-item-link"
role="menuitem"
tabindex="0"
[class.p-disabled]="item.disabled"
[attr.href]="item.url || null"
[attr.target]="item.target || null"
(click)="!item.disabled && item.command && item.command({ originalEvent: $event, item: item })"
>
@if (item.icon) {
<span [class]="item.icon + ' p-menu-item-icon'"></span>
}
@if ($any(item).caption) {
<div class="menu-item-label">
<span class="p-menu-item-label">{{ item.label }}</span>
<small class="menu-item-caption">{{ $any(item).caption }}</small>
</div>
} @else {
<span class="p-menu-item-label">{{ item.label }}</span>
}
</a>
</ng-template>
</p-menu>
`,
})
export class MenuComponent {
@ViewChild('menuRef') menuRef!: Menu;

@Input() model: MenuModel[] = [];
@Input() popup = false;

toggle(event: Event): void {
this.menuRef.toggle(event);
}
}
5 changes: 5 additions & 0 deletions src/prime-preset/map-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 { menuCss } from './tokens/components/menu';

const presetTokens: Preset<AuraBaseDesignTokens> = {
primitive: tokens.primitive as unknown as AuraBaseDesignTokens['primitive'],
Expand All @@ -14,6 +15,10 @@ const presetTokens: Preset<AuraBaseDesignTokens> = {
...(tokens.components.button as unknown as ComponentsDesignTokens['button']),
css: buttonCss,
},
menu: {
...(tokens.components.menu as unknown as ComponentsDesignTokens['menu']),
css: menuCss,
},
} as ComponentsDesignTokens,
};

Expand Down
67 changes: 67 additions & 0 deletions src/prime-preset/tokens/components/menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export const menuCss = ({ dt }: { dt: (token: string) => string }): string => `
.p-menu.p-component {
padding: ${dt('menu.extend.paddingY')} ${dt('menu.extend.paddingX')};
}

.p-menu .p-menu-item-content .p-menu-item-link .p-menu-item-label {
font-family: ${dt('fonts.fontFamily.base')};
font-size: ${dt('fonts.fontSize.300')};
font-weight: ${dt('fonts.fontWeight.regular')};
line-height: ${dt('fonts.lineHeight.400')};
}

.p-menu .p-menu-item-content .menu-item-label {
display: flex;
flex-direction: column;
gap: ${dt('menu.extend.extItem.caption.gap')};
}

.p-menu .p-menu-item-content .menu-item-caption {
font-family: ${dt('fonts.fontFamily.base')};
font-size: ${dt('fonts.fontSize.200')};
font-weight: ${dt('fonts.fontWeight.regular')};
color: ${dt('menu.colorScheme.light.extend.extItem.caption.color')};
}

.p-menu .p-menu-item:not(.p-disabled) .p-menu-item-content:hover,
.p-menu .p-menu-item:not(.p-disabled) .p-menu-item-content:hover .p-menu-item-link,
.p-menu .p-menu-item:not(.p-disabled) .p-menu-item-content:hover .p-menu-item-label,
.p-menu .p-menu-item:not(.p-disabled) .p-menu-item-content:hover .p-menu-item-icon {
background: ${dt('menu.colorScheme.light.item.focusBackground')};
color: ${dt('menu.colorScheme.light.item.focusColor')};
}

.p-menu .p-menu-item.p-menuitem-checked > .p-menu-item-content,
.p-menu .p-menu-item.p-focus > .p-menu-item-content {
background: ${dt('menu.extend.extItem.activeBackground')};
color: ${dt('menu.extend.extItem.activeColor')};
}

.p-menu .p-menu-item.p-menuitem-checked > .p-menu-item-content .p-menu-item-link,
.p-menu .p-menu-item.p-menuitem-checked > .p-menu-item-content .p-menu-item-label,
.p-menu .p-menu-item.p-focus > .p-menu-item-content .p-menu-item-link,
.p-menu .p-menu-item.p-focus > .p-menu-item-content .p-menu-item-label {
color: ${dt('menu.extend.extItem.activeColor')};
}

.p-menu .p-menu-item.p-menuitem-checked > .p-menu-item-content .p-menu-item-icon,
.p-menu .p-menu-item.p-focus > .p-menu-item-content .p-menu-item-icon {
color: ${dt('menu.colorScheme.light.extend.extItem.icon.activeColor')};
}

.p-menu .p-menu-item.p-menuitem-checked:not(.p-disabled) > .p-menu-item-content:hover {
background: ${dt('menu.colorScheme.light.item.focusBackground')};
color: ${dt('menu.colorScheme.light.item.focusColor')};
}

.p-menu .p-menu-item.p-menuitem-checked:not(.p-disabled) > .p-menu-item-content:hover .p-menu-item-icon {
color: ${dt('menu.colorScheme.light.item.focusColor')};
}

.p-menu .p-menu-submenu-label {
text-transform: uppercase;
font-size: ${dt('fonts.fontSize.200')};
font-family: ${dt('fonts.fontFamily.heading')};
line-height: ${dt('fonts.lineHeight.400')};
}
`;
23 changes: 23 additions & 0 deletions src/stories/components/menu/examples/menu-basic.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Component } from '@angular/core';
import { MenuComponent, MenuModel } from '../../../../lib/components/menu/menu.component';

const template = `
<div class="bg-surface-ground">
<menu [model]="items"></menu>
</div>
`;

@Component({
selector: 'app-menu-basic',
standalone: true,
imports: [MenuComponent],
template,
})
export class MenuBasicComponent {
items: MenuModel[] = [
{ label: 'Новый заказ' },
{ label: 'Поиск отправления' },
{ separator: true },
{ label: 'Экспорт' },
];
}
35 changes: 35 additions & 0 deletions src/stories/components/menu/examples/menu-custom.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Component } from '@angular/core';
import { MenuComponent, MenuModel } from '../../../../lib/components/menu/menu.component';

const template = `
<div class="bg-surface-ground">
<menu [model]="items"></menu>
</div>
`;

@Component({
selector: 'app-menu-custom',
standalone: true,
imports: [MenuComponent],
template,
})
export class MenuCustomComponent {
items: MenuModel[] = [
{
label: 'Создать отправление',
caption: 'Оформление нового заказа',
icon: 'ti ti-file-plus',
},
{
label: 'Найти посылку',
caption: 'Поиск по трек-номеру',
icon: 'ti ti-map-pin',
},
{ separator: true },
{
label: 'Экспорт данных',
caption: 'Выгрузка в CSV или Excel',
icon: 'ti ti-download',
},
];
}
35 changes: 35 additions & 0 deletions src/stories/components/menu/examples/menu-grouped.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Component } from '@angular/core';
import { MenuComponent, MenuModel } from '../../../../lib/components/menu/menu.component';

const template = `
<div class="bg-surface-ground">
<menu [model]="items"></menu>
</div>
`;

@Component({
selector: 'app-menu-grouped',
standalone: true,
imports: [MenuComponent],
template,
})
export class MenuGroupedComponent {
items: MenuModel[] = [
{
label: 'Заказы',
items: [
{ label: 'Новый заказ', icon: 'ti ti-plus' },
{ label: 'Список заказов', icon: 'ti ti-list' },
{ label: 'Архив', icon: 'ti ti-archive' },
],
},
{
label: 'Отправления',
items: [
{ label: 'Создать накладную', icon: 'ti ti-file-invoice' },
{ label: 'Отследить посылку', icon: 'ti ti-map-pin' },
{ label: 'Отменить отправление', icon: 'ti ti-ban' },
],
},
];
}
31 changes: 31 additions & 0 deletions src/stories/components/menu/examples/menu-popup.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Component, ViewChild } from '@angular/core';
import { Button } from 'primeng/button';
import { MenuComponent, MenuModel } from '../../../../lib/components/menu/menu.component';

const template = `
<div class="bg-surface-ground">
<p-button label="Действия с заказом" severity="contrast" (onClick)="toggle($event)"></p-button>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

кнопка должна быть наша

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxyIX фикс 0f93319

<menu #menuRef [model]="items" [popup]="true"></menu>
</div>
`;

@Component({
selector: 'app-menu-popup',
standalone: true,
imports: [MenuComponent, Button],
template,
})
export class MenuPopupComponent {
@ViewChild('menuRef') menuRef!: MenuComponent;

items: MenuModel[] = [
{ label: 'Создать отправление', icon: 'ti ti-file-plus' },
{ label: 'Найти по трек-номеру', icon: 'ti ti-search' },
{ separator: true },
{ label: 'Экспорт данных', icon: 'ti ti-download' },
];

toggle(event: Event): void {
this.menuRef.toggle(event);
}
}
25 changes: 25 additions & 0 deletions src/stories/components/menu/examples/menu-with-icons.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Component } from '@angular/core';
import { MenuComponent, MenuModel } from '../../../../lib/components/menu/menu.component';

const template = `
<div class="bg-surface-ground">
<menu [model]="items"></menu>
</div>
`;

@Component({
selector: 'app-menu-with-icons',
standalone: true,
imports: [MenuComponent],
template,
})
export class MenuWithIconsComponent {
items: MenuModel[] = [
{ label: 'Создать отправление', icon: 'ti ti-file-plus' },
{ label: 'Открыть список заказов', icon: 'ti ti-folder-open' },
{ label: 'Сохранить черновик', icon: 'ti ti-device-floppy' },
{ separator: true },
{ label: 'Распечатать накладную', icon: 'ti ti-printer' },
{ label: 'Экспорт данных', icon: 'ti ti-download' },
];
}
Loading
Loading