Browse Source

feat(admin-ui): Add support for custom action bar dropdown menus

Closes #2678
Michael Bromley 1 year ago
parent
commit
4d8bc74253
50 changed files with 473 additions and 143 deletions
  1. 2 1
      packages/admin-ui/src/lib/catalog/src/components/asset-detail/asset-detail.component.html
  2. 5 7
      packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.html
  3. 2 1
      packages/admin-ui/src/lib/catalog/src/components/collection-list/collection-list.component.html
  4. 2 1
      packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.html
  5. 2 1
      packages/admin-ui/src/lib/catalog/src/components/facet-list/facet-list.component.html
  6. 1 0
      packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.html
  7. 5 10
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.html
  8. 1 0
      packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.html
  9. 34 0
      packages/admin-ui/src/lib/core/src/extension/add-action-bar-dropdown-menu-item.ts
  10. 51 0
      packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts
  11. 18 0
      packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder.service.ts
  12. 2 0
      packages/admin-ui/src/lib/core/src/public_api.ts
  13. 0 0
      packages/admin-ui/src/lib/core/src/shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component.css
  14. 28 0
      packages/admin-ui/src/lib/core/src/shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component.html
  15. 130 0
      packages/admin-ui/src/lib/core/src/shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component.ts
  16. 6 4
      packages/admin-ui/src/lib/core/src/shared/components/dropdown/dropdown-item.directive.ts
  17. 12 9
      packages/admin-ui/src/lib/core/src/shared/components/ui-extension-point/ui-extension-point.component.ts
  18. 2 0
      packages/admin-ui/src/lib/core/src/shared/shared.module.ts
  19. 1 0
      packages/admin-ui/src/lib/customer/src/components/customer-detail/customer-detail.component.html
  20. 1 0
      packages/admin-ui/src/lib/customer/src/components/customer-group-detail/customer-group-detail.component.html
  21. 1 0
      packages/admin-ui/src/lib/customer/src/components/customer-group-list/customer-group-list.component.html
  22. 1 0
      packages/admin-ui/src/lib/customer/src/components/customer-list/customer-list.component.html
  23. 2 1
      packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html
  24. 35 13
      packages/admin-ui/src/lib/marketing/src/components/promotion-list/promotion-list.component.html
  25. 6 11
      packages/admin-ui/src/lib/order/src/components/draft-order-detail/draft-order-detail.component.html
  26. 38 45
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html
  27. 2 1
      packages/admin-ui/src/lib/order/src/components/order-list/order-list.component.html
  28. 2 1
      packages/admin-ui/src/lib/settings/src/components/admin-detail/admin-detail.component.html
  29. 2 1
      packages/admin-ui/src/lib/settings/src/components/administrator-list/administrator-list.component.html
  30. 2 1
      packages/admin-ui/src/lib/settings/src/components/channel-detail/channel-detail.component.html
  31. 2 1
      packages/admin-ui/src/lib/settings/src/components/channel-list/channel-list.component.html
  32. 3 2
      packages/admin-ui/src/lib/settings/src/components/country-detail/country-detail.component.html
  33. 14 5
      packages/admin-ui/src/lib/settings/src/components/country-list/country-list.component.html
  34. 2 1
      packages/admin-ui/src/lib/settings/src/components/global-settings/global-settings.component.html
  35. 3 2
      packages/admin-ui/src/lib/settings/src/components/payment-method-detail/payment-method-detail.component.html
  36. 20 6
      packages/admin-ui/src/lib/settings/src/components/payment-method-list/payment-method-list.component.html
  37. 2 1
      packages/admin-ui/src/lib/settings/src/components/role-detail/role-detail.component.html
  38. 2 1
      packages/admin-ui/src/lib/settings/src/components/role-list/role-list.component.html
  39. 2 1
      packages/admin-ui/src/lib/settings/src/components/seller-detail/seller-detail.component.html
  40. 2 1
      packages/admin-ui/src/lib/settings/src/components/seller-list/seller-list.component.html
  41. 3 2
      packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.html
  42. 4 3
      packages/admin-ui/src/lib/settings/src/components/shipping-method-list/shipping-method-list.component.html
  43. 2 1
      packages/admin-ui/src/lib/settings/src/components/stock-location-detail/stock-location-detail.component.html
  44. 2 1
      packages/admin-ui/src/lib/settings/src/components/stock-location-list/stock-location-list.component.html
  45. 2 1
      packages/admin-ui/src/lib/settings/src/components/tax-category-detail/tax-category-detail.component.html
  46. 2 1
      packages/admin-ui/src/lib/settings/src/components/tax-category-list/tax-category-list.component.html
  47. 2 1
      packages/admin-ui/src/lib/settings/src/components/tax-rate-detail/tax-rate-detail.component.html
  48. 2 1
      packages/admin-ui/src/lib/settings/src/components/tax-rate-list/tax-rate-list.component.html
  49. 2 1
      packages/admin-ui/src/lib/settings/src/components/zone-detail/zone-detail.component.html
  50. 4 2
      packages/admin-ui/src/lib/settings/src/components/zone-list/zone-list.component.html

+ 2 - 1
packages/admin-ui/src/lib/catalog/src/components/asset-detail/asset-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left></vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="asset-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="asset-detail" />
             <button
                 *vdrIfPermissions="['UpdateCatalog', 'UpdateAsset']"
                 class="btn btn-primary"
@@ -12,6 +12,7 @@
             >
                 {{ 'common.update' | translate }}
             </button>
+            <vdr-action-bar-dropdown-menu locationId="asset-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 5 - 7
packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.html

@@ -9,7 +9,7 @@
             ></vdr-language-selector>
         </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="collection-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="collection-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -27,8 +27,9 @@
                 >
                     {{ 'common.update' | translate }}
                 </button>
-            </ng-template></vdr-ab-right
-        >
+            </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="collection-detail" />
+        </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>
 <form class="form" [formGroup]="detailForm">
@@ -138,10 +139,7 @@
                 ></vdr-assets>
             </vdr-card>
             <vdr-card [title]="'catalog.filters' | translate">
-                <vdr-form-field
-                    [label]="'catalog.filter-inheritance' | translate"
-                    for="inheritFilters"
-                >
+                <vdr-form-field [label]="'catalog.filter-inheritance' | translate" for="inheritFilters">
                     <clr-toggle-wrapper>
                         <input
                             type="checkbox"

+ 2 - 1
packages/admin-ui/src/lib/catalog/src/components/collection-list/collection-list.component.html

@@ -8,7 +8,7 @@
             ></vdr-language-selector>
         </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="collection-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="collection-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateCatalog', 'CreateCollection']"
@@ -17,6 +17,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'catalog.create-new-collection' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="collection-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.html

@@ -10,7 +10,7 @@
         </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="facet-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="facet-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -29,6 +29,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="facet-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/catalog/src/components/facet-list/facet-list.component.html

@@ -8,7 +8,7 @@
             ></vdr-language-selector>
         </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="facet-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="facet-list" />
             <a
                 class="btn btn-primary"
                 [routerLink]="['./create']"
@@ -17,6 +17,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'catalog.create-new-facet' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="facet-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 1 - 0
packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.html

@@ -30,6 +30,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="product-detail"></vdr-action-bar-dropdown-menu>
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 5 - 10
packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.html

@@ -17,17 +17,12 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'catalog.create-new-product' | translate }}
             </a>
-            <vdr-dropdown>
-                <button class="icon-button" vdrDropdownTrigger>
-                    <clr-icon shape="ellipsis-vertical"></clr-icon>
+            <vdr-action-bar-dropdown-menu [alwaysShow]="true" locationId="product-list">
+                <button type="button" vdrDropdownItem (click)="rebuildSearchIndex()">
+                    <clr-icon shape="refresh" class=""></clr-icon>
+                    {{ 'catalog.rebuild-search-index' | translate }}
                 </button>
-                <vdr-dropdown-menu vdrPosition="bottom-right">
-                    <button type="button" vdrDropdownItem (click)="rebuildSearchIndex()">
-                        <clr-icon shape="refresh" class=""></clr-icon>
-                        {{ 'catalog.rebuild-search-index' | translate }}
-                    </button>
-                </vdr-dropdown-menu>
-            </vdr-dropdown>
+            </vdr-action-bar-dropdown-menu>
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 1 - 0
packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.html

@@ -32,6 +32,7 @@
             >
                 {{ 'common.update' | translate }}
             </button>
+            <vdr-action-bar-dropdown-menu locationId="product-variant-detail"></vdr-action-bar-dropdown-menu>
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 34 - 0
packages/admin-ui/src/lib/core/src/extension/add-action-bar-dropdown-menu-item.ts

@@ -0,0 +1,34 @@
+import { APP_INITIALIZER, Provider } from '@angular/core';
+import { ActionBarDropdownMenuItem } from '../providers/nav-builder/nav-builder-types';
+import { NavBuilderService } from '../providers/nav-builder/nav-builder.service';
+
+/**
+ * @description
+ * Adds a dropdown menu item to the ActionBar at the top right of each list or detail view. The locationId can
+ * be determined by pressing `ctrl + u` when running the Admin UI in dev mode.
+ *
+ * @example
+ * ```ts title="providers.ts"
+ * import { addActionBarDropdownMenuItem } from '\@vendure/admin-ui/core';
+ *
+ * export default [
+ *     addActionBarDropdownMenuItem({
+ *         id: 'print-invoice',
+ *         label: 'Print Invoice',
+ *         locationId: 'order-detail',
+ *         routerLink: ['/extensions/invoicing'],
+ *     }),
+ * ];
+ * ```
+ * @docsCategory action-bar
+ */
+export function addActionBarDropdownMenuItem(config: ActionBarDropdownMenuItem): Provider {
+    return {
+        provide: APP_INITIALIZER,
+        multi: true,
+        useFactory: (navBuilderService: NavBuilderService) => () => {
+            navBuilderService.addActionBarDropdownMenuItem(config);
+        },
+        deps: [NavBuilderService],
+    };
+}

+ 51 - 0
packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts

@@ -105,6 +105,10 @@ export interface ActionBarButtonState {
  * @docsCategory action-bar
  */
 export interface ActionBarItem {
+    /**
+     * @description
+     * A unique identifier for the item.
+     */
     id: string;
     label: string;
     locationId: ActionBarLocationId;
@@ -145,6 +149,53 @@ export interface ActionBarItem {
     requiresPermission?: string | string[];
 }
 
+/**
+ * @description
+ * A dropdown menu item in the ActionBar area at the top of one of the list or detail views.
+ *
+ * @docsCategory action-bar
+ * @since 2.2.0
+ */
+export interface ActionBarDropdownMenuItem {
+    /**
+     * @description
+     * A unique identifier for the item.
+     */
+    id: string;
+    label: string;
+    locationId: ActionBarLocationId;
+    /**
+     * @description
+     * Whether to render a divider above this item.
+     */
+    hasDivider?: boolean;
+    /**
+     * @description
+     * A function which returns an observable of the button state, allowing you to
+     * dynamically enable/disable or show/hide the button.
+     */
+    buttonState?: (context: ActionBarContext) => Observable<ActionBarButtonState>;
+    onClick?: (event: MouseEvent, context: ActionBarContext) => void;
+    routerLink?: RouterLinkDefinition;
+    icon?: string;
+    /**
+     * @description
+     * Control the display of this item based on the user permissions. Note: if you attempt to pass a
+     * {@link PermissionDefinition} object, you will get a compilation error. Instead, pass the plain
+     * string version. For example, if the permission is defined as:
+     * ```ts
+     * export const MyPermission = new PermissionDefinition('ProductReview');
+     * ```
+     * then the generated permission strings will be:
+     *
+     * - `CreateProductReview`
+     * - `ReadProductReview`
+     * - `UpdateProductReview`
+     * - `DeleteProductReview`
+     */
+    requiresPermission?: string | string[];
+}
+
 /**
  * @description
  * A function which returns the router link for an {@link ActionBarItem} or {@link NavMenuItem}.

+ 18 - 0
packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder.service.ts

@@ -8,6 +8,7 @@ import { Permission } from '../../common/generated-types';
 
 import {
     ActionBarContext,
+    ActionBarDropdownMenuItem,
     ActionBarItem,
     NavMenuBadgeType,
     NavMenuItem,
@@ -24,6 +25,7 @@ import {
 export class NavBuilderService {
     menuConfig$: Observable<NavMenuSection[]>;
     actionBarConfig$: Observable<ActionBarItem[]>;
+    actionBarDropdownConfig$: Observable<ActionBarDropdownMenuItem[]>;
     sectionBadges: { [sectionId: string]: Observable<NavMenuBadgeType> } = {};
 
     private initialNavMenuConfig$ = new BehaviorSubject<NavMenuSection[]>([]);
@@ -34,6 +36,7 @@ export class NavBuilderService {
         before?: string;
     }> = [];
     private addedActionBarItems: ActionBarItem[] = [];
+    private addedActionBarDropdownMenuItems: ActionBarDropdownMenuItem[] = [];
 
     constructor() {
         this.setupStreams();
@@ -86,6 +89,20 @@ export class NavBuilderService {
         }
     }
 
+    /**
+     * Adds a dropdown menu to the ActionBar at the top right of each list or detail view. The locationId can
+     * be determined by inspecting the DOM and finding the `<vdr-action-bar>` element and its
+     * `data-location-id` attribute.
+     */
+    addActionBarDropdownMenuItem(config: ActionBarDropdownMenuItem) {
+        if (!this.addedActionBarDropdownMenuItems.find(item => item.id === config.id)) {
+            this.addedActionBarDropdownMenuItems.push({
+                requiresPermission: Permission.Authenticated,
+                ...config,
+            });
+        }
+    }
+
     getRouterLink(
         config: { routerLink?: RouterLinkDefinition; context: ActionBarContext },
         route: ActivatedRoute,
@@ -185,5 +202,6 @@ export class NavBuilderService {
         );
 
         this.actionBarConfig$ = of(this.addedActionBarItems);
+        this.actionBarDropdownConfig$ = of(this.addedActionBarDropdownMenuItems);
     }
 }

+ 2 - 0
packages/admin-ui/src/lib/core/src/public_api.ts

@@ -74,6 +74,7 @@ export * from './data/utils/add-custom-fields';
 export * from './data/utils/get-server-location';
 export * from './data/utils/remove-readonly-custom-fields';
 export * from './data/utils/transform-relation-custom-field-inputs';
+export * from './extension/add-action-bar-dropdown-menu-item';
 export * from './extension/add-action-bar-item';
 export * from './extension/add-nav-menu-item';
 export * from './extension/components/angular-route.component';
@@ -124,6 +125,7 @@ export * from './providers/overlay-host/overlay-host.service';
 export * from './providers/page/page.service';
 export * from './providers/permissions/permissions.service';
 export * from './shared/components/action-bar/action-bar.component';
+export * from './shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component';
 export * from './shared/components/action-bar-items/action-bar-items.component';
 export * from './shared/components/address-form/address-form.component';
 export * from './shared/components/affixed-input/affixed-input.component';

+ 0 - 0
packages/admin-ui/src/lib/core/src/shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component.css


+ 28 - 0
packages/admin-ui/src/lib/core/src/shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component.html

@@ -0,0 +1,28 @@
+<vdr-ui-extension-point [locationId]="locationId" api="actionBarDropdown" [leftPx]="-24">
+    <vdr-dropdown #dropdownComponent *ngIf="alwaysShow || (items$ | async)?.length">
+        <button class="icon-button" vdrDropdownTrigger>
+            <clr-icon shape="ellipsis-vertical"></clr-icon>
+        </button>
+        <vdr-dropdown-menu vdrPosition="bottom-right">
+            <ng-content />
+            <ng-container *ngFor="let item of items$ | async">
+                <ng-container *ngIf="buttonStates[item.id] | async as buttonState">
+                    <div class="dropdown-divider" *ngIf="item.hasDivider === true"></div>
+                    <button
+                        type="button"
+                        vdrDropdownItem
+                        *vdrIfPermissions="item.requiresPermission"
+                        [routerLink]="getRouterLink(item)"
+                        [class.hidden]="buttonState.visible === false"
+                        [disabled]="buttonState.disabled"
+                        (click)="handleClick($event, item)"
+                        class="mr-2"
+                    >
+                        <clr-icon *ngIf="item.icon" [attr.shape]="item.icon"></clr-icon>
+                        {{ item.label | translate }}
+                    </button>
+                </ng-container>
+            </ng-container>
+        </vdr-dropdown-menu>
+    </vdr-dropdown>
+</vdr-ui-extension-point>

+ 130 - 0
packages/admin-ui/src/lib/core/src/shared/components/action-bar-dropdown-menu/action-bar-dropdown-menu.component.ts

@@ -0,0 +1,130 @@
+import {
+    AfterViewInit,
+    ChangeDetectionStrategy,
+    Component,
+    HostBinding,
+    Injector,
+    Input,
+    OnChanges,
+    OnInit,
+    Self,
+    SimpleChanges,
+    ViewChild,
+} from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { BehaviorSubject, combineLatest, 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 {
+    ActionBarButtonState,
+    ActionBarContext,
+    ActionBarDropdownMenuItem,
+} from '../../../providers/nav-builder/nav-builder-types';
+import { NavBuilderService } from '../../../providers/nav-builder/nav-builder.service';
+import { NotificationService } from '../../../providers/notification/notification.service';
+import { DropdownComponent } from '../dropdown/dropdown.component';
+
+@Component({
+    selector: 'vdr-action-bar-dropdown-menu',
+    templateUrl: './action-bar-dropdown-menu.component.html',
+    styleUrls: ['./action-bar-dropdown-menu.component.css'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    providers: [
+        // This is a rather involved work-around to allow the {@link DropdownItemDirective} to
+        // be able to access the DropdownComponent instance even when it is not a direct parent,
+        // as is the case when this component is used.
+        {
+            provide: DropdownComponent,
+            useFactory: (actionBarDropdownMenuComponent: ActionBarDropdownMenuComponent) => {
+                return new Promise(resolve =>
+                    actionBarDropdownMenuComponent.onDropdownComponentResolved(cmp => resolve(cmp)),
+                );
+            },
+            deps: [[new Self(), ActionBarDropdownMenuComponent]],
+        },
+    ],
+})
+export class ActionBarDropdownMenuComponent implements OnInit, OnChanges, AfterViewInit {
+    @ViewChild('dropdownComponent')
+    dropdownComponent: DropdownComponent;
+
+    @Input()
+    alwaysShow = false;
+
+    @HostBinding('attr.data-location-id')
+    @Input()
+    locationId: ActionBarLocationId;
+
+    items$: Observable<ActionBarDropdownMenuItem[]>;
+    buttonStates: { [id: string]: Observable<ActionBarButtonState> } = {};
+    private locationId$ = new BehaviorSubject<string>('');
+    private onDropdownComponentResolvedFn: (dropdownComponent: DropdownComponent) => void;
+
+    constructor(
+        private navBuilderService: NavBuilderService,
+        private route: ActivatedRoute,
+        private dataService: DataService,
+        private notificationService: NotificationService,
+        private injector: Injector,
+    ) {}
+
+    ngOnInit() {
+        this.items$ = combineLatest(this.navBuilderService.actionBarDropdownConfig$, 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$;
+                }
+            }),
+        );
+    }
+
+    ngOnChanges(changes: SimpleChanges): void {
+        if ('locationId' in changes) {
+            this.locationId$.next(changes['locationId'].currentValue);
+        }
+    }
+
+    ngAfterViewInit() {
+        if (this.onDropdownComponentResolvedFn) {
+            this.onDropdownComponentResolvedFn(this.dropdownComponent);
+        }
+    }
+
+    onDropdownComponentResolved(fn: (dropdownComponent: DropdownComponent) => void) {
+        this.onDropdownComponentResolvedFn = fn;
+    }
+
+    handleClick(event: MouseEvent, item: ActionBarDropdownMenuItem) {
+        if (typeof item.onClick === 'function') {
+            item.onClick(event, this.createContext());
+        }
+    }
+
+    getRouterLink(item: ActionBarDropdownMenuItem): any[] | null {
+        return this.navBuilderService.getRouterLink(
+            { routerLink: item.routerLink, context: this.createContext() },
+            this.route,
+        );
+    }
+
+    private createContext(): ActionBarContext {
+        return {
+            route: this.route,
+            injector: this.injector,
+            dataService: this.dataService,
+            notificationService: this.notificationService,
+        };
+    }
+}

+ 6 - 4
packages/admin-ui/src/lib/core/src/shared/components/dropdown/dropdown-item.directive.ts

@@ -1,4 +1,4 @@
-import { Directive, HostListener } from '@angular/core';
+import { Directive, HostListener, Inject } from '@angular/core';
 
 import { DropdownComponent } from './dropdown.component';
 
@@ -8,10 +8,12 @@ import { DropdownComponent } from './dropdown.component';
     host: { '[class.dropdown-item]': 'true' },
 })
 export class DropdownItemDirective {
-    constructor(private dropdown: DropdownComponent) {}
+    constructor(
+        @Inject(DropdownComponent) private dropdown: DropdownComponent | Promise<DropdownComponent>,
+    ) {}
 
     @HostListener('click', ['$event'])
-    onDropdownItemClick(event: any): void {
-        this.dropdown.onClick();
+    async onDropdownItemClick() {
+        (await this.dropdown).onClick();
     }
 }

+ 12 - 9
packages/admin-ui/src/lib/core/src/shared/components/ui-extension-point/ui-extension-point.component.ts

@@ -17,7 +17,7 @@ import { UIExtensionLocationId } from '../../../common/component-registry-types'
 import { DataService } from '../../../data/providers/data.service';
 import { DropdownComponent } from '../dropdown/dropdown.component';
 
-type UiExtensionType = 'actionBar' | 'navMenu' | 'detailComponent' | 'dataTable';
+type UiExtensionType = 'actionBar' | 'actionBarDropdown' | 'navMenu' | 'detailComponent' | 'dataTable';
 
 @Component({
     selector: 'vdr-ui-extension-point',
@@ -25,7 +25,7 @@ type UiExtensionType = 'actionBar' | 'navMenu' | 'detailComponent' | 'dataTable'
     styleUrls: ['./ui-extension-point.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class UiExtensionPointComponent implements OnInit, AfterViewInit {
+export class UiExtensionPointComponent implements OnInit {
     @Input() locationId: UIExtensionLocationId;
     @Input() metadata?: any;
     @Input() topPx: number;
@@ -66,13 +66,6 @@ export class UiExtensionPointComponent implements OnInit, AfterViewInit {
                 }),
             );
     }
-
-    ngAfterViewInit() {
-        // this.dropdownComponent.onOpenChange(isOpen => {
-        //     if (isOpen) {
-        //     }
-        // });
-    }
 }
 
 function highlightTsCode(tsCode: string) {
@@ -108,6 +101,16 @@ export default [
     label: 'My Action',
     locationId: '${locationId}',
   }),
+];`,
+    actionBarDropdown: locationId => `
+import { addActionBarDropdownMenuItem } from '@vendure/admin-ui/core';
+
+export default [
+  addActionBarDropdownMenuItem({
+    id: 'my-dropdown-item',
+    label: 'My Action',
+    locationId: '${locationId}',
+  }),
 ];`,
     navMenu: locationId => `
 import { addNavMenuSection } from '@vendure/admin-ui/core';

+ 2 - 0
packages/admin-ui/src/lib/core/src/shared/shared.module.ts

@@ -173,6 +173,7 @@ import { LanguageCodeSelectorComponent } from './components/language-code-select
 import { DataTableFilterPresetsComponent } from './components/data-table-filter-presets/data-table-filter-presets.component';
 import { AddFilterPresetButtonComponent } from './components/data-table-filter-presets/add-filter-preset-button.component';
 import { RenameFilterPresetDialogComponent } from './components/data-table-filter-presets/rename-filter-preset-dialog.component';
+import { ActionBarDropdownMenuComponent } from './components/action-bar-dropdown-menu/action-bar-dropdown-menu.component';
 
 const IMPORTS = [
     ClarityModule,
@@ -192,6 +193,7 @@ const DECLARATIONS = [
     ActionBarComponent,
     ActionBarLeftComponent,
     ActionBarRightComponent,
+    ActionBarDropdownMenuComponent,
     AssetPreviewComponent,
     AssetPreviewDialogComponent,
     AssetSearchInputComponent,

+ 1 - 0
packages/admin-ui/src/lib/customer/src/components/customer-detail/customer-detail.component.html

@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="customer-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 1 - 0
packages/admin-ui/src/lib/customer/src/components/customer-group-detail/customer-group-detail.component.html

@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="customer-group-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 1 - 0
packages/admin-ui/src/lib/customer/src/components/customer-group-list/customer-group-list.component.html

@@ -7,6 +7,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'customer.create-new-customer-group' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="customer-group-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 1 - 0
packages/admin-ui/src/lib/customer/src/components/customer-list/customer-list.component.html

@@ -7,6 +7,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'customer.create-new-customer' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="customer-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html

@@ -12,7 +12,7 @@
         </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="promotion-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="promotion-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -31,6 +31,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="promotion-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 35 - 13
packages/admin-ui/src/lib/marketing/src/components/promotion-list/promotion-list.component.html

@@ -2,17 +2,18 @@
     <vdr-action-bar>
         <vdr-ab-left>
             <vdr-language-selector
-                   [availableLanguageCodes]="availableLanguages$ | async"
-                   [currentLanguageCode]="contentLanguage$ | async"
-                   (languageCodeChange)="setLanguage($event)"
-               ></vdr-language-selector>
+                [availableLanguageCodes]="availableLanguages$ | async"
+                [currentLanguageCode]="contentLanguage$ | async"
+                (languageCodeChange)="setLanguage($event)"
+            />
         </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="promotion-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="promotion-list" />
             <a class="btn btn-primary" [routerLink]="['./create']" *vdrIfPermissions="'CreatePromotion'">
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'marketing.create-new-promotion' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="promotion-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>
@@ -40,7 +41,8 @@
         <ng-template let-promotion="item">{{ promotion.id }}</ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'common.created-at' | translate" id="created-at"
+        [heading]="'common.created-at' | translate"
+        id="created-at"
         [hiddenByDefault]="true"
         [sort]="sorts.get('createdAt')"
     >
@@ -49,7 +51,8 @@
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'common.updated-at' | translate" id="updated-at"
+        [heading]="'common.updated-at' | translate"
+        id="updated-at"
         [hiddenByDefault]="true"
         [sort]="sorts.get('updatedAt')"
     >
@@ -57,7 +60,12 @@
             {{ promotion.updatedAt | localeDate : 'short' }}
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column [heading]="'common.name' | translate" id="name" [optional]="false" [sort]="sorts.get('name')">
+    <vdr-dt2-column
+        [heading]="'common.name' | translate"
+        id="name"
+        [optional]="false"
+        [sort]="sorts.get('name')"
+    >
         <ng-template let-promotion="item">
             <a class="button-ghost" [routerLink]="['./', promotion.id]"
                 ><span> {{ promotion.name }}</span>
@@ -75,12 +83,20 @@
             }}</vdr-chip>
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column [heading]="'marketing.coupon-code' | translate" id="coupon-code" [sort]="sorts.get('couponCode')">
+    <vdr-dt2-column
+        [heading]="'marketing.coupon-code' | translate"
+        id="coupon-code"
+        [sort]="sorts.get('couponCode')"
+    >
         <ng-template let-promotion="item">
             {{ promotion.couponCode }}
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column [heading]="'marketing.starts-at' | translate" id="starts-at" [sort]="sorts.get('startsAt')">
+    <vdr-dt2-column
+        [heading]="'marketing.starts-at' | translate"
+        id="starts-at"
+        [sort]="sorts.get('startsAt')"
+    >
         <ng-template let-promotion="item">
             {{ promotion.startsAt | localeDate : 'short' }}
         </ng-template>
@@ -91,7 +107,8 @@
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'marketing.per-customer-limit' | translate" id="per-customer-limit"
+        [heading]="'marketing.per-customer-limit' | translate"
+        id="per-customer-limit"
         [sort]="sorts.get('perCustomerUsageLimit')"
         [hiddenByDefault]="true"
     >
@@ -100,7 +117,8 @@
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'marketing.per-customer-limit' | translate" id="per-customer-limit"
+        [heading]="'marketing.per-customer-limit' | translate"
+        id="per-customer-limit"
         [sort]="sorts.get('usageLimit')"
         [hiddenByDefault]="true"
     >
@@ -108,5 +126,9 @@
             {{ promotion.usageLimit }}
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-custom-field-column *ngFor="let customField of customFields" [customField]="customField" [sorts]="sorts" />
+    <vdr-dt2-custom-field-column
+        *ngFor="let customField of customFields"
+        [customField]="customField"
+        [sorts]="sorts"
+    />
 </vdr-data-table-2>

+ 6 - 11
packages/admin-ui/src/lib/order/src/components/draft-order-detail/draft-order-detail.component.html

@@ -7,7 +7,7 @@
         </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="draft-order-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="draft-order-detail" />
             <button
                 class="btn btn-primary"
                 (click)="completeOrder()"
@@ -16,17 +16,12 @@
                 <clr-icon shape="check"></clr-icon>
                 {{ 'order.complete-draft-order' | translate }}
             </button>
-            <vdr-dropdown>
-                <button class="icon-button" vdrDropdownTrigger>
-                    <clr-icon shape="ellipsis-vertical"></clr-icon>
+            <vdr-action-bar-dropdown-menu [alwaysShow]="true" locationId="draft-order-detail">
+                <button type="button" vdrDropdownItem (click)="deleteOrder()">
+                    <clr-icon shape="trash" class="is-danger"></clr-icon>
+                    {{ 'order.delete-draft-order' | translate }}
                 </button>
-                <vdr-dropdown-menu vdrPosition="bottom-right">
-                    <button type="button" vdrDropdownItem (click)="deleteOrder()">
-                        <clr-icon shape="trash" class="is-danger"></clr-icon>
-                        {{ 'order.delete-draft-order' | translate }}
-                    </button>
-                </vdr-dropdown-menu>
-            </vdr-dropdown>
+            </vdr-action-bar-dropdown-menu>
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 38 - 45
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html

@@ -37,57 +37,50 @@
             >
                 {{ 'order.fulfill-order' | translate }}
             </button>
-            <vdr-dropdown>
-                <button class="icon-button" vdrDropdownTrigger>
-                    <clr-icon shape="ellipsis-vertical"></clr-icon>
-                </button>
-                <vdr-dropdown-menu vdrPosition="bottom-right">
-                    <ng-container
-                        *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Modifying')"
-                    >
-                        <button type="button" vdrDropdownItem (click)="transitionToModifying()">
-                            <clr-icon shape="pencil"></clr-icon>
-                            {{ 'order.modify-order' | translate }}
-                        </button>
-                        <div class="dropdown-divider"></div>
+            <vdr-action-bar-dropdown-menu [alwaysShow]="true" locationId="order-detail">
+                <ng-container *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Modifying')">
+                    <button type="button" vdrDropdownItem (click)="transitionToModifying()">
+                        <clr-icon shape="pencil"></clr-icon>
+                        {{ 'order.modify-order' | translate }}
+                    </button>
+                    <div class="dropdown-divider"></div>
+                </ng-container>
+                <button
+                    type="button"
+                    vdrDropdownItem
+                    *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Cancelled')"
+                    (click)="cancelOrRefund(order)"
+                >
+                    <clr-icon shape="error-standard" class="is-error"></clr-icon>
+                    <ng-container *ngIf="orderHasSettledPayments(order); else cancelOnly">
+                        {{ 'order.refund-and-cancel-order' | translate }}
                     </ng-container>
+                    <ng-template #cancelOnly>
+                        {{ 'order.cancel-order' | translate }}
+                    </ng-template>
+                </button>
+
+                <ng-container *ngIf="(nextStates$ | async)?.length">
+                    <div class="dropdown-divider"></div>
                     <button
+                        *ngFor="let nextState of nextStates$ | async"
                         type="button"
                         vdrDropdownItem
-                        *ngIf="order.type !== 'Aggregate' && order.nextStates.includes('Cancelled')"
-                        (click)="cancelOrRefund(order)"
+                        (click)="transitionToState(nextState)"
                     >
-                        <clr-icon shape="error-standard" class="is-error"></clr-icon>
-                        <ng-container *ngIf="orderHasSettledPayments(order); else cancelOnly">
-                            {{ 'order.refund-and-cancel-order' | translate }}
-                        </ng-container>
-                        <ng-template #cancelOnly>
-                            {{ 'order.cancel-order' | translate }}
-                        </ng-template>
+                        <clr-icon shape="step-forward-2"></clr-icon>
+                        {{
+                            'order.transition-to-state'
+                                | translate : { state: (nextState | stateI18nToken | translate) }
+                        }}
                     </button>
-
-                    <ng-container *ngIf="(nextStates$ | async)?.length">
-                        <div class="dropdown-divider"></div>
-                        <button
-                            *ngFor="let nextState of nextStates$ | async"
-                            type="button"
-                            vdrDropdownItem
-                            (click)="transitionToState(nextState)"
-                        >
-                            <clr-icon shape="step-forward-2"></clr-icon>
-                            {{
-                                'order.transition-to-state'
-                                    | translate : { state: (nextState | stateI18nToken | translate) }
-                            }}
-                        </button>
-                    </ng-container>
-                    <div class="dropdown-divider"></div>
-                    <button type="button" vdrDropdownItem (click)="manuallyTransitionToState(order)">
-                        <clr-icon shape="step-forward-2" class="is-warning"></clr-icon>
-                        {{ 'order.manually-transition-to-state' | translate }}
-                    </button>
-                </vdr-dropdown-menu>
-            </vdr-dropdown>
+                </ng-container>
+                <div class="dropdown-divider"></div>
+                <button type="button" vdrDropdownItem (click)="manuallyTransitionToState(order)">
+                    <clr-icon shape="step-forward-2" class="is-warning"></clr-icon>
+                    {{ 'order.manually-transition-to-state' | translate }}
+                </button>
+            </vdr-action-bar-dropdown-menu>
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/order/src/components/order-list/order-list.component.html

@@ -1,13 +1,14 @@
 <vdr-page-block>
     <vdr-action-bar>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="order-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="order-list" />
             <ng-container *ngIf="canCreateDraftOrder">
                 <a class="btn" *vdrIfPermissions="['CreateOrder']" [routerLink]="['./draft/create']">
                     <clr-icon shape="plus"></clr-icon>
                     {{ 'catalog.create-draft-order' | translate }}
                 </a>
             </ng-container>
+            <vdr-action-bar-dropdown-menu locationId="order-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/admin-detail/admin-detail.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="administrator-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="administrator-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -21,6 +21,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="administrator-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/administrator-list/administrator-list.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="administrator-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="administrator-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateAdministrator']"
@@ -11,6 +11,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'admin.create-new-administrator' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="administrator-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/channel-detail/channel-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left></vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="channel-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="channel-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="channel-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/channel-list/channel-list.component.html

@@ -2,11 +2,12 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="channel-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="channel-list" />
             <a class="btn btn-primary" *vdrIfPermissions="['SuperAdmin', 'CreateChannel']" [routerLink]="['./', 'create']">
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-channel' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="channel-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 3 - 2
packages/admin-ui/src/lib/settings/src/components/country-detail/country-detail.component.html

@@ -6,10 +6,10 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="languageCode$ | async"
                 (languageCodeChange)="setLanguage($event)"
-            ></vdr-language-selector>
+            />
         </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="country-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="country-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -28,6 +28,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="country-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 14 - 5
packages/admin-ui/src/lib/settings/src/components/country-list/country-list.component.html

@@ -5,9 +5,10 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="contentLanguage$ | async"
                 (languageCodeChange)="setLanguage($event)"
-            ></vdr-language-selector></vdr-ab-left>
+            />
+        </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="country-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="country-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateSettings', 'CreateCountry']"
@@ -16,6 +17,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-country' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="country-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>
@@ -45,7 +47,8 @@
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'common.created-at' | translate" id="created-at"
+        [heading]="'common.created-at' | translate"
+        id="created-at"
         [hiddenByDefault]="true"
         [sort]="sorts.get('createdAt')"
     >
@@ -54,7 +57,8 @@
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'common.updated-at' | translate" id="updated-at"
+        [heading]="'common.updated-at' | translate"
+        id="updated-at"
         [hiddenByDefault]="true"
         [sort]="sorts.get('updatedAt')"
     >
@@ -62,7 +66,12 @@
             {{ country.updatedAt | localeDate : 'short' }}
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column [heading]="'common.name' | translate" id="name" [optional]="false" [sort]="sorts.get('name')">
+    <vdr-dt2-column
+        [heading]="'common.name' | translate"
+        id="name"
+        [optional]="false"
+        [sort]="sorts.get('name')"
+    >
         <ng-template let-country="item">
             <a class="button-ghost" [routerLink]="['./', country.id]"
                 ><span>{{ country.name }}</span>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/global-settings/global-settings.component.html

@@ -1,7 +1,7 @@
 <vdr-page-block>
     <vdr-action-bar>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="global-settings-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="global-setting-detail" />
             <button
                 class="btn btn-primary"
                 (click)="save()"
@@ -10,6 +10,7 @@
             >
                 {{ 'common.update' | translate }}
             </button>
+            <vdr-action-bar-dropdown-menu locationId="global-setting-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 3 - 2
packages/admin-ui/src/lib/settings/src/components/payment-method-detail/payment-method-detail.component.html

@@ -6,11 +6,11 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="languageCode$ | async"
                 (languageCodeChange)="setLanguage($event)"
-            ></vdr-language-selector>
+            />
         </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="payment-method-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="payment-method-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -29,6 +29,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="payment-method-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 20 - 6
packages/admin-ui/src/lib/settings/src/components/payment-method-list/payment-method-list.component.html

@@ -5,9 +5,10 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="contentLanguage$ | async"
                 (languageCodeChange)="setLanguage($event)"
-        /></vdr-ab-left>
+            />
+        </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="payment-method-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="payment-method-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateSettings', 'CreatePaymentMethod']"
@@ -16,6 +17,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-payment-method' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="payment-method-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>
@@ -38,13 +40,19 @@
         [searchTermControl]="searchTermControl"
         [searchTermPlaceholder]="'catalog.filter-by-name' | translate"
     ></vdr-dt2-search>
-    <vdr-dt2-column [heading]="'common.id' | translate" id="id" [hiddenByDefault]="true" [sort]="sorts.get('id')">
+    <vdr-dt2-column
+        [heading]="'common.id' | translate"
+        id="id"
+        [hiddenByDefault]="true"
+        [sort]="sorts.get('id')"
+    >
         <ng-template let-paymentMethod="item">
             {{ paymentMethod.id }}
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'common.created-at' | translate" id="created-at"
+        [heading]="'common.created-at' | translate"
+        id="created-at"
         [hiddenByDefault]="true"
         [sort]="sorts.get('createdAt')"
     >
@@ -53,7 +61,8 @@
         </ng-template>
     </vdr-dt2-column>
     <vdr-dt2-column
-        [heading]="'common.updated-at' | translate" id="updated-at"
+        [heading]="'common.updated-at' | translate"
+        id="updated-at"
         [hiddenByDefault]="true"
         [sort]="sorts.get('updatedAt')"
     >
@@ -61,7 +70,12 @@
             {{ paymentMethod.updatedAt | localeDate : 'short' }}
         </ng-template>
     </vdr-dt2-column>
-    <vdr-dt2-column [heading]="'common.name' | translate" id="name" [optional]="false" [sort]="sorts.get('name')">
+    <vdr-dt2-column
+        [heading]="'common.name' | translate"
+        id="name"
+        [optional]="false"
+        [sort]="sorts.get('name')"
+    >
         <ng-template let-paymentMethod="item">
             <a class="button-ghost" [routerLink]="['./', paymentMethod.id]"
                 ><span>{{ paymentMethod.name }}</span>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/role-detail/role-detail.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left></vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="role-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="role-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -21,6 +21,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="role-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/role-list/role-list.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="role-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="role-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateAdministrator']"
@@ -11,6 +11,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-role' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="role-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/seller-detail/seller-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left> </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="seller-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="seller-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="seller-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/seller-list/seller-list.component.html

@@ -2,11 +2,12 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="seller-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="seller-list" />
             <a class="btn btn-primary" *vdrIfPermissions="['SuperAdmin', 'CreateSeller']" [routerLink]="['./', 'create']">
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-seller' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="seller-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 3 - 2
packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.html

@@ -6,11 +6,11 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="languageCode$ | async"
                 (languageCodeChange)="setLanguage($event)"
-            ></vdr-language-selector>
+            />
         </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="shipping-method-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="shipping-method-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -33,6 +33,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="shipping-method-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 4 - 3
packages/admin-ui/src/lib/settings/src/components/shipping-method-list/shipping-method-list.component.html

@@ -5,10 +5,10 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="contentLanguage$ | async"
                 (languageCodeChange)="setLanguage($event)"
-            ></vdr-language-selector
-        ></vdr-ab-left>
+            />
+        </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="shipping-method-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="shipping-method-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateSettings', 'CreateShippingMethod']"
@@ -17,6 +17,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-shipping-method' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="shipping-method-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/stock-location-detail/stock-location-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left></vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="stock-location-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="stock-location-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="stock-location-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/stock-location-list/stock-location-list.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="stock-location-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="stock-location-list" />
             <a
                 class="button primary"
                 *vdrIfPermissions="['CreateStockLocation']"
@@ -11,6 +11,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'catalog.create-new-stock-location' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="stock-location-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/tax-category-detail/tax-category-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left> </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="tax-category-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="tax-category-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="tax-category-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/tax-category-list/tax-category-list.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="tax-category-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="tax-category-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateSettings', 'CreateTaxCategory']"
@@ -11,6 +11,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-tax-category' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="tax-category-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/tax-rate-detail/tax-rate-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left></vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="tax-rate-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="tax-rate-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="tax-rate-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/tax-rate-list/tax-rate-list.component.html

@@ -2,7 +2,7 @@
     <vdr-action-bar>
         <vdr-ab-left> </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="tax-rate-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="tax-rate-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateSettings', 'CreateTaxRate']"
@@ -11,6 +11,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-tax-rate' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="tax-rate-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 2 - 1
packages/admin-ui/src/lib/settings/src/components/zone-detail/zone-detail.component.html

@@ -3,7 +3,7 @@
         <vdr-ab-left> </vdr-ab-left>
 
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="zone-detail"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="zone-detail" />
             <button
                 class="btn btn-primary"
                 *ngIf="isNew$ | async; else updateButton"
@@ -22,6 +22,7 @@
                     {{ 'common.update' | translate }}
                 </button>
             </ng-template>
+            <vdr-action-bar-dropdown-menu locationId="zone-detail" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>

+ 4 - 2
packages/admin-ui/src/lib/settings/src/components/zone-list/zone-list.component.html

@@ -5,9 +5,10 @@
                 [availableLanguageCodes]="availableLanguages$ | async"
                 [currentLanguageCode]="contentLanguage$ | async"
                 (languageCodeChange)="setLanguage($event)"
-            /></vdr-ab-left>
+            />
+        </vdr-ab-left>
         <vdr-ab-right>
-            <vdr-action-bar-items locationId="zone-list"></vdr-action-bar-items>
+            <vdr-action-bar-items locationId="zone-list" />
             <a
                 class="btn btn-primary"
                 *vdrIfPermissions="['CreateSettings', 'CreateZone']"
@@ -16,6 +17,7 @@
                 <clr-icon shape="plus"></clr-icon>
                 {{ 'settings.create-new-zone' | translate }}
             </a>
+            <vdr-action-bar-dropdown-menu locationId="zone-list" />
         </vdr-ab-right>
     </vdr-action-bar>
 </vdr-page-block>