Fixes before Release (#3172)
This commit is contained in:
parent
844d7c7e4b
commit
d6ee97816f
13 changed files with 208 additions and 52 deletions
|
@ -0,0 +1,29 @@
|
|||
<ng-container *transloco="let t; read: 'actionable'">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{t('title')}}</h4>
|
||||
<button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="modal.close()"></button>
|
||||
</div>
|
||||
<div class="modal-body scrollable-modal">
|
||||
@if (currentLevel.length > 0) {
|
||||
<button class="btn btn-secondary w-100 mb-3 text-start" (click)="handleBack()">
|
||||
← {{t('back-to', {action: t(currentLevel[currentLevel.length - 1])})}}
|
||||
</button>
|
||||
}
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
@for (action of currentItems; track action.title) {
|
||||
@if (willRenderAction(action)) {
|
||||
<button class="btn btn-outline-primary text-start d-flex justify-content-between align-items-center w-100"
|
||||
(click)="handleItemClick(action)">
|
||||
{{t(action.title)}}
|
||||
@if (action.children.length > 0 || action.dynamicList) {
|
||||
<span class="ms-1">→</span>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
|
@ -0,0 +1,98 @@
|
|||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component, DestroyRef,
|
||||
EventEmitter,
|
||||
inject,
|
||||
Input,
|
||||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import {NgClass} from "@angular/common";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
|
||||
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {Action, ActionItem} from "../../_services/action-factory.service";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {tap} from "rxjs";
|
||||
import {User} from "../../_models/user";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
@Component({
|
||||
selector: 'app-actionable-modal',
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgClass,
|
||||
TranslocoDirective
|
||||
],
|
||||
templateUrl: './actionable-modal.component.html',
|
||||
styleUrl: './actionable-modal.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ActionableModalComponent implements OnInit {
|
||||
|
||||
protected readonly utilityService = inject(UtilityService);
|
||||
protected readonly modal = inject(NgbActiveModal);
|
||||
protected readonly accountService = inject(AccountService);
|
||||
protected readonly cdRef = inject(ChangeDetectorRef);
|
||||
protected readonly destroyRef = inject(DestroyRef);
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
|
||||
@Input() actions: ActionItem<any>[] = [];
|
||||
@Input() willRenderAction!: (action: ActionItem<any>) => boolean;
|
||||
@Input() shouldRenderSubMenu!: (action: ActionItem<any>, dynamicList: null | Array<any>) => boolean;
|
||||
@Output() actionPerformed = new EventEmitter<ActionItem<any>>();
|
||||
|
||||
currentLevel: string[] = [];
|
||||
currentItems: ActionItem<any>[] = [];
|
||||
user!: User | undefined;
|
||||
|
||||
ngOnInit() {
|
||||
this.currentItems = this.actions;
|
||||
this.accountService.currentUser$.pipe(tap(user => {
|
||||
this.user = user;
|
||||
this.cdRef.markForCheck();
|
||||
}), takeUntilDestroyed(this.destroyRef)).subscribe();
|
||||
}
|
||||
|
||||
handleItemClick(item: ActionItem<any>) {
|
||||
if (item.children && item.children.length > 0) {
|
||||
this.currentLevel.push(item.title);
|
||||
this.currentItems = item.children;
|
||||
} else if (item.dynamicList) {
|
||||
item.dynamicList.subscribe(dynamicItems => {
|
||||
this.currentLevel.push(item.title);
|
||||
this.currentItems = dynamicItems.map(di => ({
|
||||
...item,
|
||||
title: di.title,
|
||||
_extra: di
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
this.actionPerformed.emit(item);
|
||||
this.modal.close(item);
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
handleBack() {
|
||||
if (this.currentLevel.length > 0) {
|
||||
this.currentLevel.pop();
|
||||
|
||||
let items = this.actions;
|
||||
for (let level of this.currentLevel) {
|
||||
items = items.find(item => item.title === level)?.children || [];
|
||||
}
|
||||
|
||||
this.currentItems = items;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
// willRenderAction(action: ActionItem<any>) {
|
||||
// if (this.user === undefined) return false;
|
||||
//
|
||||
// return this.accountService.canInvokeAction(this.user, action.action);
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,40 +1,45 @@
|
|||
<ng-container *transloco="let t; read: 'actionable'">
|
||||
@if (actions.length > 0) {
|
||||
<div ngbDropdown container="body" class="d-inline-block">
|
||||
<button [disabled]="disabled" class="btn {{btnClass}}" id="actions-{{labelBy}}" ngbDropdownToggle
|
||||
(click)="preventEvent($event)"><i class="fa {{iconClass}}" aria-hidden="true"></i></button>
|
||||
<div ngbDropdownMenu attr.aria-labelledby="actions-{{labelBy}}">
|
||||
<ng-container *ngTemplateOutlet="submenu; context: { list: actions }"></ng-container>
|
||||
@if ((utilityService.activeBreakpoint$ | async)! <= Breakpoint.Tablet) {
|
||||
<button [disabled]="disabled" class="btn {{btnClass}}" id="actions-{{labelBy}}"
|
||||
(click)="openMobileActionableMenu($event)"><i class="fa {{iconClass}}" aria-hidden="true"></i></button>
|
||||
} @else {
|
||||
<div ngbDropdown container="body" class="d-inline-block">
|
||||
<button [disabled]="disabled" class="btn {{btnClass}}" id="actions-{{labelBy}}" ngbDropdownToggle
|
||||
(click)="preventEvent($event)"><i class="fa {{iconClass}}" aria-hidden="true"></i></button>
|
||||
<div ngbDropdownMenu attr.aria-labelledby="actions-{{labelBy}}">
|
||||
<ng-container *ngTemplateOutlet="submenu; context: { list: actions }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #submenu let-list="list">
|
||||
@for(action of list; track action.id) {
|
||||
<!-- Non Submenu items -->
|
||||
@if (action.children === undefined || action?.children?.length === 0 || action.dynamicList !== undefined) {
|
||||
@if (action.dynamicList !== undefined && (action.dynamicList | async | dynamicList); as dList) {
|
||||
@for(dynamicItem of dList; track dynamicItem.title) {
|
||||
<button ngbDropdownItem (click)="performDynamicClick($event, action, dynamicItem)">{{dynamicItem.title}}</button>
|
||||
}
|
||||
} @else if (willRenderAction(action)) {
|
||||
<button ngbDropdownItem (click)="performAction($event, action)" (mouseover)="closeAllSubmenus()">{{t(action.title)}}</button>
|
||||
}
|
||||
} @else {
|
||||
@if (shouldRenderSubMenu(action, action.children?.[0].dynamicList | async)) {
|
||||
<!-- Submenu items -->
|
||||
<div ngbDropdown #subMenuHover="ngbDropdown" placement="right left"
|
||||
(click)="preventEvent($event); openSubmenu(action.title, subMenuHover)"
|
||||
(mouseover)="preventEvent($event); openSubmenu(action.title, subMenuHover)"
|
||||
(mouseleave)="preventEvent($event)">
|
||||
@if (willRenderAction(action)) {
|
||||
<button id="actions-{{action.title}}" class="submenu-toggle" ngbDropdownToggle>{{t(action.title)}} <i class="fa-solid fa-angle-right submenu-icon"></i></button>
|
||||
<ng-template #submenu let-list="list">
|
||||
@for(action of list; track action.id) {
|
||||
<!-- Non Submenu items -->
|
||||
@if (action.children === undefined || action?.children?.length === 0 || action.dynamicList !== undefined) {
|
||||
@if (action.dynamicList !== undefined && (action.dynamicList | async | dynamicList); as dList) {
|
||||
@for(dynamicItem of dList; track dynamicItem.title) {
|
||||
<button ngbDropdownItem (click)="performDynamicClick($event, action, dynamicItem)">{{dynamicItem.title}}</button>
|
||||
}
|
||||
<div ngbDropdownMenu attr.aria-labelledby="actions-{{action.title}}">
|
||||
<ng-container *ngTemplateOutlet="submenu; context: { list: action.children }"></ng-container>
|
||||
} @else if (willRenderAction(action)) {
|
||||
<button ngbDropdownItem (click)="performAction($event, action)" (mouseover)="closeAllSubmenus()">{{t(action.title)}}</button>
|
||||
}
|
||||
} @else {
|
||||
@if (shouldRenderSubMenu(action, action.children?.[0].dynamicList | async)) {
|
||||
<!-- Submenu items -->
|
||||
<div ngbDropdown #subMenuHover="ngbDropdown" placement="right left"
|
||||
(click)="preventEvent($event); openSubmenu(action.title, subMenuHover)"
|
||||
(mouseover)="preventEvent($event); openSubmenu(action.title, subMenuHover)"
|
||||
(mouseleave)="preventEvent($event)">
|
||||
@if (willRenderAction(action)) {
|
||||
<button id="actions-{{action.title}}" class="submenu-toggle" ngbDropdownToggle>{{t(action.title)}} <i class="fa-solid fa-angle-right submenu-icon"></i></button>
|
||||
}
|
||||
<div ngbDropdownMenu attr.aria-labelledby="actions-{{action.title}}">
|
||||
<ng-container *ngTemplateOutlet="submenu; context: { list: action.children }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
}
|
||||
}
|
||||
</ng-container>
|
||||
|
|
|
@ -8,13 +8,16 @@ import {
|
|||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { Action, ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import {AsyncPipe, NgTemplateOutlet} from "@angular/common";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {DynamicListPipe} from "./_pipes/dynamic-list.pipe";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
|
||||
import {NavLinkModalComponent} from "../../nav/_components/nav-link-modal/nav-link-modal.component";
|
||||
import {ActionableModalComponent} from "../actionable-modal/actionable-modal.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-card-actionables',
|
||||
|
@ -29,6 +32,10 @@ export class CardActionablesComponent implements OnInit {
|
|||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly accountService = inject(AccountService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
protected readonly utilityService = inject(UtilityService);
|
||||
protected readonly modalService = inject(NgbModal);
|
||||
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
|
||||
@Input() iconClass = 'fa-ellipsis-v';
|
||||
@Input() btnClass = '';
|
||||
|
@ -110,4 +117,16 @@ export class CardActionablesComponent implements OnInit {
|
|||
action._extra = dynamicItem;
|
||||
this.performAction(event, action);
|
||||
}
|
||||
|
||||
openMobileActionableMenu(event: any) {
|
||||
this.preventEvent(event);
|
||||
|
||||
const ref = this.modalService.open(ActionableModalComponent, {fullscreen: 'sm'});
|
||||
ref.componentInstance.actions = this.actions;
|
||||
ref.componentInstance.willRenderAction = this.willRenderAction.bind(this);
|
||||
ref.componentInstance.shouldRenderSubMenu = this.shouldRenderSubMenu.bind(this);
|
||||
ref.componentInstance.actionPerformed.subscribe((action: ActionItem<any>) => {
|
||||
this.performAction(event, action);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue