Jelajahi Sumber

feat(admin-ui): Improved control over ActionBar buttons

This commit implements setting disabled and visible states, as well
as providing the `Injector` instance to the click handler.
Michael Bromley 2 tahun lalu
induk
melakukan
065a2b4cb2

+ 14 - 3
packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts

@@ -1,7 +1,8 @@
+import { Injector } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { Observable } from 'rxjs';
 
-import { ActionBarLocationId, UIExtensionLocationId } from '../../common/component-registry-types';
+import { ActionBarLocationId } from '../../common/component-registry-types';
 import { DataService } from '../../data/providers/data.service';
 import { NotificationService } from '../notification/notification.service';
 
@@ -74,12 +75,18 @@ export interface NavMenuSection {
  *
  * @docsCategory action-bar
  */
-export interface OnClickContext {
+export interface ActionBarContext {
     route: ActivatedRoute;
+    injector: Injector;
     dataService: DataService;
     notificationService: NotificationService;
 }
 
+export interface ActionBarButtonState {
+    disabled: boolean;
+    visible: boolean;
+}
+
 /**
  * @description
  * A button in the ActionBar area at the top of one of the list or detail views.
@@ -90,8 +97,12 @@ export interface ActionBarItem {
     id: string;
     label: string;
     locationId: ActionBarLocationId;
+    /**
+     * @deprecated - use `buttonState` instead.
+     */
     disabled?: Observable<boolean>;
-    onClick?: (event: MouseEvent, context: OnClickContext) => void;
+    buttonState?: (context: ActionBarContext) => Observable<ActionBarButtonState>;
+    onClick?: (event: MouseEvent, context: ActionBarContext) => void;
     routerLink?: RouterLinkDefinition;
     buttonColor?: 'primary' | 'success' | 'warning';
     buttonStyle?: 'solid' | 'outline' | 'link';

+ 14 - 11
packages/admin-ui/src/lib/core/src/shared/components/action-bar-items/action-bar-items.component.html

@@ -1,15 +1,18 @@
 <vdr-ui-extension-point [locationId]="locationId" api="actionBar" [leftPx]="-24">
     <ng-container *ngFor="let item of items$ | async">
-        <button
-            *vdrIfPermissions="item.requiresPermission"
-            [routerLink]="getRouterLink(item)"
-            [disabled]="item.disabled ? (item.disabled | async) : false"
-            (click)="handleClick($event, item)"
-            [ngClass]="getButtonStyles(item)"
-            class="mr-2"
-        >
-            <clr-icon *ngIf="item.icon" [attr.shape]="item.icon"></clr-icon>
-            {{ item.label | translate }}
-        </button>
+        <ng-container *ngIf="buttonStates[item.id] | async as buttonState">
+            <button
+                *vdrIfPermissions="item.requiresPermission"
+                [routerLink]="getRouterLink(item)"
+                [class.hidden]="buttonState.visible === false"
+                [disabled]="buttonState.disabled || (item.disabled ? (item.disabled | async) : false)"
+                (click)="handleClick($event, item)"
+                [ngClass]="getButtonStyles(item)"
+                class="mr-2"
+            >
+                <clr-icon *ngIf="item.icon" [attr.shape]="item.icon"></clr-icon>
+                {{ item.label | translate }}
+            </button>
+        </ng-container>
     </ng-container>
 </vdr-ui-extension-point>

+ 4 - 0
packages/admin-ui/src/lib/core/src/shared/components/action-bar-items/action-bar-items.component.scss

@@ -1,3 +1,7 @@
 :host {
     display: inline-block;
 }
+
+button.hidden {
+    display: none;
+}

+ 33 - 8
packages/admin-ui/src/lib/core/src/shared/components/action-bar-items/action-bar-items.component.ts

@@ -2,6 +2,7 @@ import {
     ChangeDetectionStrategy,
     Component,
     HostBinding,
+    Injector,
     Input,
     OnChanges,
     OnInit,
@@ -9,12 +10,16 @@ import {
 } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { assertNever } from '@vendure/common/lib/shared-utils';
-import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
-import { filter, map } from 'rxjs/operators';
+import { BehaviorSubject, combineLatest, mergeAll, Observable, of } from 'rxjs';
+import { map, tap } from 'rxjs/operators';
 
 import { ActionBarLocationId } from '../../../common/component-registry-types';
 import { DataService } from '../../../data/providers/data.service';
-import { ActionBarItem } from '../../../providers/nav-builder/nav-builder-types';
+import {
+    ActionBarButtonState,
+    ActionBarContext,
+    ActionBarItem,
+} from '../../../providers/nav-builder/nav-builder-types';
 import { NavBuilderService } from '../../../providers/nav-builder/nav-builder.service';
 import { NotificationService } from '../../../providers/notification/notification.service';
 
@@ -30,6 +35,7 @@ export class ActionBarItemsComponent implements OnInit, OnChanges {
     locationId: ActionBarLocationId;
 
     items$: Observable<ActionBarItem[]>;
+    buttonStates: { [id: string]: Observable<ActionBarButtonState> } = {};
     private locationId$ = new BehaviorSubject<string>('');
 
     constructor(
@@ -37,11 +43,25 @@ export class ActionBarItemsComponent implements OnInit, OnChanges {
         private route: ActivatedRoute,
         private dataService: DataService,
         private notificationService: NotificationService,
+        private injector: Injector,
     ) {}
 
     ngOnInit() {
         this.items$ = combineLatest(this.navBuilderService.actionBarConfig$, this.locationId$).pipe(
             map(([items, locationId]) => items.filter(config => config.locationId === locationId)),
+            tap(items => {
+                const context = this.createContext();
+                for (const item of items) {
+                    const buttonState$ =
+                        typeof item.buttonState === 'function'
+                            ? item.buttonState(context)
+                            : of({
+                                  disabled: false,
+                                  visible: true,
+                              });
+                    this.buttonStates[item.id] = buttonState$;
+                }
+            }),
         );
     }
 
@@ -53,11 +73,7 @@ export class ActionBarItemsComponent implements OnInit, OnChanges {
 
     handleClick(event: MouseEvent, item: ActionBarItem) {
         if (typeof item.onClick === 'function') {
-            item.onClick(event, {
-                route: this.route,
-                dataService: this.dataService,
-                notificationService: this.notificationService,
-            });
+            item.onClick(event, this.createContext());
         }
     }
 
@@ -90,4 +106,13 @@ export class ActionBarItemsComponent implements OnInit, OnChanges {
                 return '';
         }
     }
+
+    private createContext(): ActionBarContext {
+        return {
+            route: this.route,
+            injector: this.injector,
+            dataService: this.dataService,
+            notificationService: this.notificationService,
+        };
+    }
 }