Browse Source

feat(admin-ui): Simplify API for adding menu items, custom controls

Michael Bromley 5 years ago
parent
commit
2b9e4c4011

+ 34 - 0
packages/admin-ui/src/lib/core/src/providers/custom-field-component/custom-field-component.service.ts

@@ -1,9 +1,11 @@
 import {
+    APP_INITIALIZER,
     ComponentFactory,
     ComponentFactoryResolver,
     ComponentRef,
     Injectable,
     Injector,
+    Provider,
 } from '@angular/core';
 import { FormControl } from '@angular/forms';
 import { Type } from '@vendure/common/lib/shared-types';
@@ -18,6 +20,38 @@ export interface CustomFieldControl {
 
 export type CustomFieldEntityName = Exclude<keyof CustomFields, '__typename'>;
 
+/**
+ * @description
+ * Registers a custom component to act as the form input control for the given custom field.
+ * This should be used in the NgModule `providers` array of your ui extension module.
+ *
+ * @example
+ * ```TypeScript
+ * \@NgModule({
+ *   imports: [SharedModule],
+ *   declarations: [MyCustomFieldControl],
+ *   providers: [
+ *       registerCustomFieldComponent('Product', 'someCustomField', MyCustomFieldControl),
+ *   ],
+ * })
+ * export class MyUiExtensionModule {}
+ * ```
+ */
+export function registerCustomFieldComponent(
+    entity: CustomFieldEntityName,
+    fieldName: string,
+    component: Type<CustomFieldControl>,
+): Provider {
+    return {
+        provide: APP_INITIALIZER,
+        multi: true,
+        useFactory: (customFieldComponentService: CustomFieldComponentService) => () => {
+            customFieldComponentService.registerCustomFieldComponent(entity, fieldName, component);
+        },
+        deps: [CustomFieldComponentService],
+    };
+}
+
 /**
  * This service allows the registration of custom controls for customFields.
  */

+ 114 - 2
packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder.service.ts

@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { APP_INITIALIZER, Injectable, Provider } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
 import { map, shareReplay } from 'rxjs/operators';
@@ -7,6 +7,116 @@ import { Permission } from '../../common/generated-types';
 
 import { ActionBarItem, NavMenuItem, NavMenuSection, RouterLinkDefinition } from './nav-builder-types';
 
+/**
+ * @description
+ * Add a section to the main nav menu. Providing the `before` argument will
+ * move the section before any existing section with the specified id. If
+ * omitted (or if the id is not found) the section will be appended to the
+ * existing set of sections.
+ * This should be used in the NgModule `providers` array of your ui extension module.
+ *
+ * @example
+ * ```TypeScript
+ * \@NgModule({
+ *   imports: [SharedModule],
+ *   providers: [
+ *     addNavMenuSection({
+ *       id: 'reviews',
+ *       label: 'Product Reviews',
+ *       routerLink: ['/extensions/reviews'],
+ *       icon: 'star',
+ *     },
+ *     'settings'),
+ *   ],
+ * })
+ * export class MyUiExtensionModule {}
+ * ```
+ */
+export function addNavMenuSection(config: NavMenuSection, before?: string): Provider {
+    return {
+        provide: APP_INITIALIZER,
+        multi: true,
+        useFactory: (navBuilderService: NavBuilderService) => () => {
+            navBuilderService.addNavMenuSection(config, before);
+        },
+        deps: [NavBuilderService],
+    };
+}
+
+/**
+ * @description
+ * Add a menu item to an existing section specified by `sectionId`. The id of the section
+ * can be found by inspecting the DOM and finding the `data-section-id` attribute.
+ * Providing the `before` argument will move the item before any existing item with the specified id.
+ * If omitted (or if the name is not found) the item will be appended to the
+ * end of the section.
+ *
+ * This should be used in the NgModule `providers` array of your ui extension module.
+ *
+ * @example
+ * ```TypeScript
+ * \@NgModule({
+ *   imports: [SharedModule],
+ *   providers: [
+ *     addNavMenuItem({
+ *       id: 'reports',
+ *       label: 'Reports',
+ *       items: [{
+ *           // ...
+ *       }],
+ *     },
+ *     'marketing'),
+ *   ],
+ * })
+ * export class MyUiExtensionModule {}
+ * ```
+ */
+export function addNavMenuItem(config: NavMenuItem, sectionId: string, before?: string): Provider {
+    return {
+        provide: APP_INITIALIZER,
+        multi: true,
+        useFactory: (navBuilderService: NavBuilderService) => () => {
+            navBuilderService.addNavMenuItem(config, sectionId, before);
+        },
+        deps: [NavBuilderService],
+    };
+}
+
+/**
+ * @description
+ * Adds a button 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.
+ *
+ * This should be used in the NgModule `providers` array of your ui extension module.
+ *
+ * @example
+ * ```TypeScript
+ * \@NgModule({
+ *   imports: [SharedModule],
+ *   providers: [
+ *     addActionBarItem({
+ *      id: 'print-invoice'
+ *      label: 'Print Invoice',
+ *      locationId: 'order-detail',
+ *      routerLink: ['/extensions/invoicing'],
+ *     }),
+ *   ],
+ * })
+ * export class MyUiExtensionModule {}
+ * ```
+ */
+export function addActionBarItem(config: ActionBarItem): Provider {
+    return {
+        provide: APP_INITIALIZER,
+        multi: true,
+        useFactory: (navBuilderService: NavBuilderService) => () => {
+            navBuilderService.addActionBarItem(config);
+        },
+        deps: [NavBuilderService],
+    };
+}
+
 /**
  * This service is used to define the contents of configurable menus in the application.
  */
@@ -109,7 +219,9 @@ export class NavBuilderService {
                     if (!section) {
                         // tslint:disable-next-line:no-console
                         console.error(
-                            `Could not add menu item "${item.config.id}", section "${item.sectionId}" does not exist`,
+                            `Could not add menu item "${item.config.id}", section "${
+                                item.sectionId
+                            }" does not exist`,
                         );
                     } else {
                         const index = section.items.findIndex(i => i.id === item.before);