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
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,12 @@ export class PoButtonBaseComponent {
* @description
* Ícone exibido ao lado esquerdo do label do botão.
*
* É possível usar qualquer um dos ícones da [Biblioteca de ícones](https://po-ui.io/icons). conforme exemplo abaixo:
* É possível usar qualquer um dos ícones da [Biblioteca de ícones](https://po-ui.io/icons), conforme exemplo:
* ```
* <po-button p-icon="an an-user" p-label="PO button"></po-button>
* ```
* Também é possível utilizar outras fontes de ícones, por exemplo a biblioteca *Font Awesome*, da seguinte forma:
* Também é possível utilizar outras fontes de ícones, por exemplo a biblioteca *Font Awesome*, desde que a biblioteca
* esteja carregada no projeto:
* ```
* <po-button p-icon="fa fa-podcast" p-label="PO button"></po-button>
* ```
Expand Down
12 changes: 9 additions & 3 deletions projects/ui/src/lib/components/po-icon/po-icon-dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,13 @@ export const AnimaliaIconDictionary: { [key: string]: string } = {
ICON_ALIGN_JUSTIFY: 'an an-text-align-justify',
ICON_ALIGN_LEFT: 'an an-text-align-left',
ICON_ALIGN_RIGHT: 'an an-text-align-right',
ICON_ARROW_ARC_LEFT: 'an an-arrow-arc-left',
ICON_ARROW_DOWN: 'an an-caret-down',
ICON_OTHER_ARROW_DOWN: 'an an-arrow-down',
ICON_ARROW_LEFT: 'an an-caret-left',
ICON_ARROW_RIGHT: 'an an-caret-right',
ICON_ARROW_UP: 'an an-caret-up',
ICON_OTHER_ARROW_UP: 'an an-arrow-up',
ICON_CALENDAR: 'an an-calendar-blank',
ICON_CLEAR_CONTENT: 'an an-x-circle',
ICON_CLOCK: 'an an-clock',
Expand All @@ -204,6 +207,7 @@ export const AnimaliaIconDictionary: { [key: string]: string } = {
ICON_EYE: 'an an-eye',
ICON_EYE_OFF: 'an an-eye-closed',
ICON_FILTER: 'an an-funnel',
ICON_FUNNEL: 'an an-funnel-simple',
ICON_HELP: 'an an-question',
ICON_INFO: 'an an-info',
ICON_LAST_PAGE: 'an an-caret-double-right',
Expand All @@ -223,14 +227,16 @@ export const AnimaliaIconDictionary: { [key: string]: string } = {
ICON_PARAMETERS: 'an an-sliders-horizontal',
ICON_PICTURE: 'an an-image',
ICON_PICTURE_BROKEN: 'an an-image-broken',
ICON_PLUS: 'an an-plus',
ICON_PROHIBIT: 'an an-prohibit',
ICON_PUSH_PIN: 'an an-push-pin',
ICON_PUSH_PIN_SLASH: 'an an-push-pin-slash',
ICON_REFRESH: 'an an-arrow-clockwise',
ICON_SEARCH: 'an an-magnifying-glass',
ICON_SETTINGS: 'an an-gear-six',
ICON_SORT: 'an an-caret-up-down ',
ICON_SORT_ASC: 'an an-caret-up',
ICON_SORT_DESC: 'an an-caret-down',
ICON_SORT: 'an an-arrows-down-up',
ICON_SORT_ASC: 'an an-arrow-up',
ICON_SORT_DESC: 'an an-arrow-down',
ICON_STAR: 'an an-star',
ICON_TELEPHONE: 'an an-phone',
ICON_TEXT_BOLD: 'an an-text-b',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@if (class) {
<i [class]="class" aria-hidden="true">
<i #iconElement [class]="class" aria-hidden="true">
<ng-content></ng-content>
</i>
} @else {
Expand Down
12 changes: 11 additions & 1 deletion projects/ui/src/lib/components/po-icon/po-icon.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { ChangeDetectionStrategy, Component, Inject, Input, Optional, TemplateRef } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Inject,
Input,
Optional,
TemplateRef,
ViewChild
} from '@angular/core';
import { ICONS_DICTIONARY, AnimaliaIconDictionary } from './po-icon-dictionary';
/**
* @docsPrivate
Expand All @@ -16,6 +25,7 @@ import { ICONS_DICTIONARY, AnimaliaIconDictionary } from './po-icon-dictionary';
standalone: false
})
export class PoIconComponent {
@ViewChild('iconElement', { static: false }) iconElement: ElementRef;
class: string;
private _icon: string | TemplateRef<void>;
private _iconToken: { [key: string]: string };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ export class PoItemListBaseComponent {
*/
@Input('p-icon') icon: string | TemplateRef<void>;

/**
* @optional
*
* @description
*
* Define se deve ser exibido o ícone indicando subnível.
*/
@Input('p-icon-arrow-right') iconArrowRight: string;

// Define a posição do ícone: 'left' (padrão) ou 'right'.
@Input('p-icon-position') iconPosition: 'left' | 'right' = 'left';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[class.po-item-list__danger]="danger"
class="po-item-list po-item-list__action"
>
@if (icon && iconPosition === 'left') {
@if (icon) {
<po-icon
class="po-popup-icon-item po-field-icon"
[class.po-field-icon-aa]="size === 'small'"
Expand All @@ -21,8 +21,8 @@
}
<span class="po-item-list-label">{{ label }}</span>

@if (icon && iconPosition === 'right') {
<po-icon class="po-popup-icon-item-right po-field-icon" [p-icon]="icon"></po-icon>
@if (iconArrowRight) {
<po-icon class="po-popup-icon-item-right po-field-icon" [p-icon]="iconArrowRight"></po-icon>
}
</div>
}
Expand Down
83 changes: 46 additions & 37 deletions projects/ui/src/lib/components/po-listbox/po-listbox.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,14 @@
(click)="onSelectItem(item, $event)"
(keydown)="onKeyDown(item, $event)"
>
@if (item.subItems?.length) {
@if (item.subItems?.length || item.$subItemTemplate) {
<po-item-list
[p-label]="item.label"
[p-item]="item"
[p-separator]="item.separator || separator"
[p-icon]="'po-icon-arrow-right'"
[p-icon-position]="'right'"
[p-icon]="item.icon"
[p-icon-arrow-right]="'po-icon-arrow-right'"
[p-selected]="isSelectedItem(item) || item.selected"
>
</po-item-list>
} @else if (!item.subItems?.length && returnBooleanValue(item, 'visible') !== false) {
Expand Down Expand Up @@ -176,47 +177,55 @@
[cdkOption]="'back-option'"
[attr.aria-label]="literals?.backToPreviousGroup"
(click)="goBack($event)"
(keydown)="onKeydownGoBack($event)"
(keydown)="onKeydownGoBack($event, currentGroup)"
>
<po-icon class="po-field-icon" [p-icon]="'po-icon-arrow-left'"></po-icon>
<po-tag [p-value]="currentGroup.label"></po-tag>
</li>

@for (subItem of currentItems; track subItem.label) {
@if (subItem.subItems?.length) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
<po-item-list
[p-label]="subItem.label"
[p-item]="subItem"
[p-separator]="subItem.separator || separator"
[p-icon]="'po-icon-arrow-right'"
[p-icon-position]="'right'"
@if (currentGroup.$subItemTemplate) {
<div (click)="$event.stopPropagation()" (keydown)="onKeydownTemplate($event)">
<ng-container
*ngTemplateOutlet="currentGroup.$subItemTemplate; context: { $implicit: currentGroup }"
></ng-container>
</div>
} @else {
@for (subItem of currentItems; track subItem.label) {
@if (subItem.subItems?.length) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
</po-item-list>
</li>
} @else if (!subItem.subItems?.length && returnBooleanValue(subItem, 'visible') !== false) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
<po-item-list
class="po-listbox-item-sub"
[p-disabled]="returnBooleanValue(subItem, 'disabled')"
[p-visible]="returnBooleanValue(subItem, 'visible')"
[p-label]="subItem[fieldLabel]"
[p-item]="subItem"
[p-icon]="subItem.icon"
[p-separator]="subItem.separator || separator"
[p-danger]="subItem.type === 'danger'"
[p-selected]="isSelectedItem(subItem) || subItem.selected"
<po-item-list
[p-label]="subItem.label"
[p-item]="subItem"
[p-separator]="subItem.separator || separator"
[p-icon]="'po-icon-arrow-right'"
[p-icon-position]="'right'"
>
</po-item-list>
</li>
} @else if (!subItem.subItems?.length && returnBooleanValue(subItem, 'visible') !== false) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
</po-item-list>
</li>
<po-item-list
class="po-listbox-item-sub"
[p-disabled]="returnBooleanValue(subItem, 'disabled')"
[p-visible]="returnBooleanValue(subItem, 'visible')"
[p-label]="subItem[fieldLabel]"
[p-item]="subItem"
[p-icon]="subItem.icon"
[p-separator]="subItem.separator || separator"
[p-danger]="subItem.type === 'danger'"
[p-selected]="isSelectedItem(subItem) || subItem.selected"
>
</po-item-list>
</li>
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,56 @@ describe('PoListBoxComponent', () => {
expect((component as any).navigationStack.length).toBe(2);
});

it('onKeydownTemplate: should emit closeEvent if press Tab out .po-listbox-dropdown', () => {
const emitSpy = spyOn(component.closeEvent, 'emit');
const stopSpy = jasmine.createSpy('stopPropagation');

const event = {
code: 'Tab',
target: document.createElement('div'),
stopPropagation: stopSpy
} as unknown as KeyboardEvent;

component['onKeydownTemplate'](event);

expect(stopSpy).not.toHaveBeenCalled();
expect(emitSpy).toHaveBeenCalled();
});

it('onKeydownTemplate: should call stopPropagation and not emit closeEvent when press Tab in .po-listbox-dropdown', () => {
const emitSpy = spyOn(component.closeEvent, 'emit');
const stopSpy = jasmine.createSpy('stopPropagation');

const dropdown = document.createElement('div');
dropdown.classList.add('po-listbox-dropdown');

const child = document.createElement('div');
dropdown.appendChild(child);
document.body.appendChild(dropdown);

const event = {
code: 'Tab',
target: child,
stopPropagation: stopSpy
} as unknown as KeyboardEvent;

component['onKeydownTemplate'](event);

expect(stopSpy).toHaveBeenCalled();
expect(emitSpy).not.toHaveBeenCalled();

dropdown.remove();
});

it('onKeydownGoBack: should not emit closeEvent if subItemTemplate is true', () => {
const eventTab = new KeyboardEvent('keydown', { code: 'Tab' });

spyOn(component.closeEvent, 'emit');

component.onKeydownGoBack(eventTab, { label: 'item', $subItemTemplate: true as any });
expect(component.closeEvent.emit).not.toHaveBeenCalled();
});

it('onKeydownGoBack should call goBack on Enter, emit closeEvent on Escape or Tab', () => {
const eventEnter = new KeyboardEvent('keydown', { key: 'Enter' });
const eventEscape = new KeyboardEvent('keydown', { code: 'Escape' });
Expand Down
17 changes: 15 additions & 2 deletions projects/ui/src/lib/components/po-listbox/po-listbox.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,25 @@ export class PoListBoxComponent extends PoListBoxBaseComponent implements OnInit
});
}

public onKeydownGoBack(event: KeyboardEvent): void {
public onKeydownGoBack(event: KeyboardEvent, currentGroup?: PoDropdownAction): void {
if (event.key === 'Enter') {
this.goBack(event);
}

if (event?.code === 'Escape' || event.code === 'Tab') {
if (event.code === 'Tab' && !event.shiftKey && currentGroup?.$subItemTemplate) {
return;
}
this.closeEvent.emit();
}
}

protected onKeydownTemplate(event: KeyboardEvent): void {
if (event.code === 'Tab') {
if ((event.target as HTMLElement)?.closest('.po-listbox-dropdown')) {
event.stopPropagation();
return;
}
this.closeEvent.emit();
}
}
Expand Down Expand Up @@ -184,7 +197,7 @@ export class PoListBoxComponent extends PoListBoxBaseComponent implements OnInit
return this.openUrl(itemListAction.url);
}

if (itemListAction?.subItems?.length) {
if (itemListAction?.subItems?.length || itemListAction?.$subItemTemplate) {
this.openGroup(itemListAction, event);
} else if (this.listboxSubitems) {
this.closeEvent.emit();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TemplateRef } from '@angular/core';

/**
* @usedBy PoModalComponent
*
Expand All @@ -19,6 +21,50 @@ export interface PoModalAction {
/** Desabilita o botão impossibilitando que sua ação seja executada. */
disabled?: boolean;

/**
* Ícone exibido ao lado esquerdo do label do botão.
*
* É possível usar qualquer um dos ícones da [Biblioteca de ícones](https://po-ui.io/icons), conforme exemplo:
* ```
* modalAction: PoModalAction = {
* action: () => {},
* label: 'Botão com ícone PO',
* icon: 'an an-user'
* };
* ```
* Também é possível utilizar outras fontes de ícones, por exemplo a biblioteca *Font Awesome*, desde que a biblioteca
* esteja carregada no projeto:
* ```
* modalAction: PoModalAction = {
* action: () => {},
* label: 'Botão com ícone Font Awesome',
* icon: 'fa fa-user'
* };
* ```
* Outra opção seria a customização do ícone através do `TemplateRef`, conforme exemplo abaixo:
* ```
* // Template HTML
* <ng-template #customIcon>
* <span class="fa fa-user"></span>
* </ng-template>
*
* // Componente TypeScript
* @ViewChild('customIcon', { static: true }) customIcon: TemplateRef<void>;
*
* modalAction: PoModalAction = {
* action: () => {},
* label: 'Botão com ícone customizado',
* };
*
* // Atribuição do TemplateRef à propriedade icon após a inicialização da view
* ngAfterViewInit() {
* this.modalAction.icon = this.customIcon;
* }
* ```
* > Para o ícone enquadrar corretamente, deve-se utilizar `font-size: inherit` caso o ícone utilizado não aplique-o.
*/
icon?: string | TemplateRef<void>;

/** Rótulo do botão. */
label: string;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<po-button
[p-danger]="getSecondaryActionButtonDanger()"
[p-disabled]="secondaryAction.disabled"
[p-icon]="secondaryAction.icon"
[p-label]="secondaryAction.label"
[p-loading]="secondaryAction.loading"
p-kind="secondary"
Expand All @@ -48,6 +49,7 @@
class="po-button-modal-first-action"
[p-danger]="primaryAction.danger"
[p-disabled]="primaryAction.disabled"
[p-icon]="primaryAction.icon"
[p-label]="primaryAction.label"
[p-loading]="primaryAction.loading"
p-kind="primary"
Expand Down
Loading
Loading