From 64e078d211759024ac79668279f58038bbd1099d Mon Sep 17 00:00:00 2001 From: cbourget Date: Fri, 22 Feb 2019 15:29:40 -0500 Subject: [PATCH] feat(toolbox): refactor context toolbar into a better and reusable toolbox --- demo/src/app/app.component.html | 1 + demo/src/app/app.module.ts | 2 + .../src/app/common/action/action.component.ts | 7 +- .../app/common/tool/tool-routing.module.ts | 15 ++ demo/src/app/common/tool/tool.component.html | 36 ++++ demo/src/app/common/tool/tool.component.scss | 12 ++ demo/src/app/common/tool/tool.component.ts | 90 ++++++++ demo/src/app/common/tool/tool.module.ts | 35 ++++ .../dynamic-outlet.component.ts | 4 +- .../src/lib/form/shared/form-field.service.ts | 5 + packages/common/src/public_api.ts | 3 + projects/common/src/lib/tool/index.ts | 1 + projects/common/src/lib/tool/shared/index.ts | 4 + .../src/lib/tool/shared/tool-component.ts | 10 + .../src/lib/tool/shared/tool.interface.ts | 9 + .../src/lib/tool/shared/tool.service.spec.ts | 17 ++ .../src/lib/tool/shared/tool.service.ts | 38 ++++ .../common/src/lib/tool/shared/toolbox.ts | 114 +++++++++++ projects/common/src/lib/tool/tool.module.ts | 25 +++ .../src/lib/tool/toolbox/toolbox.animation.ts | 23 +++ .../lib/tool/toolbox/toolbox.component.html | 20 ++ .../lib/tool/toolbox/toolbox.component.scss | 44 ++++ .../src/lib/tool/toolbox/toolbox.component.ts | 193 ++++++++++++++++++ .../src/lib/tool/toolbox/toolbox.module.ts | 27 +++ 24 files changed, 731 insertions(+), 4 deletions(-) create mode 100644 demo/src/app/common/tool/tool-routing.module.ts create mode 100644 demo/src/app/common/tool/tool.component.html create mode 100644 demo/src/app/common/tool/tool.component.scss create mode 100644 demo/src/app/common/tool/tool.component.ts create mode 100644 demo/src/app/common/tool/tool.module.ts create mode 100644 projects/common/src/lib/tool/index.ts create mode 100644 projects/common/src/lib/tool/shared/index.ts create mode 100644 projects/common/src/lib/tool/shared/tool-component.ts create mode 100644 projects/common/src/lib/tool/shared/tool.interface.ts create mode 100644 projects/common/src/lib/tool/shared/tool.service.spec.ts create mode 100644 projects/common/src/lib/tool/shared/tool.service.ts create mode 100644 projects/common/src/lib/tool/shared/toolbox.ts create mode 100644 projects/common/src/lib/tool/tool.module.ts create mode 100644 projects/common/src/lib/tool/toolbox/toolbox.animation.ts create mode 100644 projects/common/src/lib/tool/toolbox/toolbox.component.html create mode 100644 projects/common/src/lib/tool/toolbox/toolbox.component.scss create mode 100644 projects/common/src/lib/tool/toolbox/toolbox.component.ts create mode 100644 projects/common/src/lib/tool/toolbox/toolbox.module.ts diff --git a/demo/src/app/app.component.html b/demo/src/app/app.component.html index 1bbd8e4573..8abbdc951d 100644 --- a/demo/src/app/app.component.html +++ b/demo/src/app/app.component.html @@ -29,6 +29,7 @@

{{title}}

Entity Table Form Table + Tool Widget
diff --git a/demo/src/app/app.module.ts b/demo/src/app/app.module.ts index 3672121833..94c902eb9c 100644 --- a/demo/src/app/app.module.ts +++ b/demo/src/app/app.module.ts @@ -22,6 +22,7 @@ import { AppDynamicComponentModule } from './common/dynamic-component/dynamic-co import { AppEntityTableModule } from './common/entity-table/entity-table.module'; import { AppFormModule } from './common/form/form.module'; import { AppTableModule } from './common/table/table.module'; +import { AppToolModule } from './common/tool/tool.module'; import { AppWidgetModule } from './common/widget/widget.module'; import { AppAuthFormModule } from './auth/auth-form/auth-form.module'; @@ -67,6 +68,7 @@ import { AppComponent } from './app.component'; AppEntityTableModule, AppFormModule, AppTableModule, + AppToolModule, AppWidgetModule, AppAuthFormModule, diff --git a/demo/src/app/common/action/action.component.ts b/demo/src/app/common/action/action.component.ts index 887f397803..c807a1d60a 100644 --- a/demo/src/app/common/action/action.component.ts +++ b/demo/src/app/common/action/action.component.ts @@ -49,8 +49,11 @@ export class AppActionComponent implements OnInit, OnDestroy { title: 'Edit', icon: 'edit', tooltip: 'Edit Tooltip', - handler: () => alert('Edit!'), - conditions: [added] + handler: (item: string) => { + alert(`Edit item ${item}!`); + }, + conditions: [added], + args: ['1'] }, { id: 'delete', diff --git a/demo/src/app/common/tool/tool-routing.module.ts b/demo/src/app/common/tool/tool-routing.module.ts new file mode 100644 index 0000000000..5e4d586359 --- /dev/null +++ b/demo/src/app/common/tool/tool-routing.module.ts @@ -0,0 +1,15 @@ +import { Routes, RouterModule } from '@angular/router'; +import { ModuleWithProviders } from '@angular/core'; + +import { AppToolComponent } from './tool.component'; + +const routes: Routes = [ + { + path: 'tool', + component: AppToolComponent + } +]; + +export const AppToolRoutingModule: ModuleWithProviders = RouterModule.forChild( + routes +); diff --git a/demo/src/app/common/tool/tool.component.html b/demo/src/app/common/tool/tool.component.html new file mode 100644 index 0000000000..5cf5da3325 --- /dev/null +++ b/demo/src/app/common/tool/tool.component.html @@ -0,0 +1,36 @@ + + Common + Tool + + See the code of this example + + + + + + + + + + + + + diff --git a/demo/src/app/common/tool/tool.component.scss b/demo/src/app/common/tool/tool.component.scss new file mode 100644 index 0000000000..f21bfc1bb3 --- /dev/null +++ b/demo/src/app/common/tool/tool.component.scss @@ -0,0 +1,12 @@ +pre, +code { + font-family: monospace, monospace; +} +pre { + overflow: auto; +} +pre > code { + display: block; + padding: 1rem; + word-wrap: normal; +} diff --git a/demo/src/app/common/tool/tool.component.ts b/demo/src/app/common/tool/tool.component.ts new file mode 100644 index 0000000000..ce98fe9dbb --- /dev/null +++ b/demo/src/app/common/tool/tool.component.ts @@ -0,0 +1,90 @@ +import { + Component, + OnInit, + OnDestroy, + Input, + ChangeDetectionStrategy, + ChangeDetectorRef +} from '@angular/core'; + +import { BehaviorSubject } from 'rxjs'; + +import { + OnUpdateInputs, + Tool, + Toolbox, + ToolComponent, + ToolService +} from '@igo2/common'; + +@ToolComponent({ + name: 'salutation', + title: 'Salutation', + icon: 'person', + options: {name: 'Jack'} +}) +@Component({ + selector: 'app-salutation-tool', + template: ` +

Hello, my name is {{name}}.

+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AppSalutationToolComponent implements OnUpdateInputs { + + @Input() name: string; + + constructor(private cdRef: ChangeDetectorRef) {} + + onUpdateInputs() { + this.cdRef.detectChanges(); + } + +} + +@ToolComponent({ + name: 'about', + title: 'About', + icon: 'info' +}) +@Component({ + selector: 'app-about-tool', + template: ` +

I'm a tool inside a toolbox.

+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AppAboutToolComponent {} + +@Component({ + selector: 'app-tool', + templateUrl: './tool.component.html', + styleUrls: ['./tool.component.scss'] +}) +export class AppToolComponent implements OnInit, OnDestroy { + + toolbox = new Toolbox(); + + get activeTool$(): BehaviorSubject { + return this.toolbox.activeTool$; + } + + get panelTitle(): string { + return this.activeTool$.value ? this.activeTool$.value.title : 'Toolbox'; + } + + constructor(private toolService: ToolService) {} + + ngOnInit() { + this.toolbox.setTools(this.toolService.getTools()); + } + + ngOnDestroy() { + this.toolbox.destroy(); + } + + activateSalutationTool() { + this.toolbox.activateTool('salutation', {name: 'Bob'}); + } + +} diff --git a/demo/src/app/common/tool/tool.module.ts b/demo/src/app/common/tool/tool.module.ts new file mode 100644 index 0000000000..e2d7c39191 --- /dev/null +++ b/demo/src/app/common/tool/tool.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule, MatIconModule, MatCardModule } from '@angular/material'; + +import { IgoPanelModule, IgoToolModule } from '@igo2/common'; + +import { + AppToolComponent, + AppSalutationToolComponent, + AppAboutToolComponent +} from './tool.component'; +import { AppToolRoutingModule } from './tool-routing.module'; + +@NgModule({ + declarations: [ + AppToolComponent, + AppSalutationToolComponent, + AppAboutToolComponent + ], + imports: [ + CommonModule, + AppToolRoutingModule, + MatButtonModule, + MatIconModule, + MatCardModule, + IgoPanelModule, + IgoToolModule.forRoot() + ], + exports: [AppToolComponent], + entryComponents: [ + AppSalutationToolComponent, + AppAboutToolComponent + ] +}) +export class AppToolModule {} diff --git a/packages/common/src/lib/dynamic-component/dynamic-outlet/dynamic-outlet.component.ts b/packages/common/src/lib/dynamic-component/dynamic-outlet/dynamic-outlet.component.ts index 06e4fb4e97..01ae01a795 100644 --- a/packages/common/src/lib/dynamic-component/dynamic-outlet/dynamic-outlet.component.ts +++ b/packages/common/src/lib/dynamic-component/dynamic-outlet/dynamic-outlet.component.ts @@ -31,12 +31,12 @@ export class DynamicOutletComponent implements OnChanges, OnDestroy { /** * The dynamic component inputs */ - @Input() inputs: {[key: string]: any}; + @Input() inputs: {[key: string]: any} = {}; /** * The subscribers to the dynamic component outputs */ - @Input() subscribers: {[key: string]: (event: any) => void}; + @Input() subscribers: {[key: string]: (event: any) => void} = {}; /** * The dynamic component diff --git a/packages/common/src/lib/form/shared/form-field.service.ts b/packages/common/src/lib/form/shared/form-field.service.ts index 860a0f2f78..ec4dddd7a1 100644 --- a/packages/common/src/lib/form/shared/form-field.service.ts +++ b/packages/common/src/lib/form/shared/form-field.service.ts @@ -1,6 +1,11 @@ +import { Injectable } from '@angular/core'; + /** * Service where all available form fields are registered. */ +@Injectable({ + providedIn: 'root' +}) export class FormFieldService { static fields: {[key: string]: any} = {}; diff --git a/packages/common/src/public_api.ts b/packages/common/src/public_api.ts index 10938dcaf8..98c5da8afb 100644 --- a/packages/common/src/public_api.ts +++ b/packages/common/src/public_api.ts @@ -29,6 +29,8 @@ export * from './lib/sidenav/sidenav.module'; export * from './lib/spinner/spinner.module'; export * from './lib/stop-propagation/stop-propagation.module'; export * from './lib/table/table.module'; +export * from './lib/tool/tool.module'; +export * from './lib/tool/toolbox/toolbox.module'; export * from './lib/widget/widget.module'; export * from './lib/widget/widget-outlet/widget-outlet.module'; @@ -53,4 +55,5 @@ export * from './lib/sidenav'; export * from './lib/spinner'; export * from './lib/stop-propagation'; export * from './lib/table'; +export * from './lib/tool'; export * from './lib/widget'; diff --git a/projects/common/src/lib/tool/index.ts b/projects/common/src/lib/tool/index.ts new file mode 100644 index 0000000000..c3da79f741 --- /dev/null +++ b/projects/common/src/lib/tool/index.ts @@ -0,0 +1 @@ +export * from './shared'; diff --git a/projects/common/src/lib/tool/shared/index.ts b/projects/common/src/lib/tool/shared/index.ts new file mode 100644 index 0000000000..19607f55c6 --- /dev/null +++ b/projects/common/src/lib/tool/shared/index.ts @@ -0,0 +1,4 @@ +export * from './tool.interface'; +export * from './tool.service'; +export * from './tool-component'; +export * from './toolbox'; diff --git a/projects/common/src/lib/tool/shared/tool-component.ts b/projects/common/src/lib/tool/shared/tool-component.ts new file mode 100644 index 0000000000..464b4261ed --- /dev/null +++ b/projects/common/src/lib/tool/shared/tool-component.ts @@ -0,0 +1,10 @@ +import { Tool } from './tool.interface'; +import { ToolService } from './tool.service'; + +export function ToolComponent(tool: Partial): (cls: any) => any { + return function (compType: any) { + ToolService.register(Object.assign({}, tool, { + component: compType + } as Tool)); + }; +} diff --git a/projects/common/src/lib/tool/shared/tool.interface.ts b/projects/common/src/lib/tool/shared/tool.interface.ts new file mode 100644 index 0000000000..b2b1b4df71 --- /dev/null +++ b/projects/common/src/lib/tool/shared/tool.interface.ts @@ -0,0 +1,9 @@ +export interface Tool { + name: string; + component: any; + title?: string; + icon?: string; + iconImage?: string; + tooltip?: string; + options?: { [key: string]: any }; +} diff --git a/projects/common/src/lib/tool/shared/tool.service.spec.ts b/projects/common/src/lib/tool/shared/tool.service.spec.ts new file mode 100644 index 0000000000..b5d38c1045 --- /dev/null +++ b/projects/common/src/lib/tool/shared/tool.service.spec.ts @@ -0,0 +1,17 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientModule } from '@angular/common/http'; + +import { ToolService } from './tool.service'; + +describe('ToolService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientModule], + providers: [ToolService] + }); + }); + + it('should ...', inject([ToolService], (service: ToolService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/projects/common/src/lib/tool/shared/tool.service.ts b/projects/common/src/lib/tool/shared/tool.service.ts new file mode 100644 index 0000000000..0bf008c917 --- /dev/null +++ b/projects/common/src/lib/tool/shared/tool.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; + +import { Tool } from './tool.interface'; + +/** + * Service where runtime tool configurations are registered + */ +@Injectable({ + providedIn: 'root' +}) +export class ToolService { + + static tools: {[key: string]: Tool} = {}; + + static register(tool: Tool) { + ToolService.tools[tool.name] = tool; + } + + constructor() {} + + /** + * Return a tool + * @param name Tool name + * @returns tool Tool + */ + getTool(name: string): Tool { + return ToolService.tools[name]; + } + + /** + * Return all tools + * @returns tTols + */ + getTools(): Tool[] { + return Object.values(ToolService.tools); + } + +} diff --git a/projects/common/src/lib/tool/shared/toolbox.ts b/projects/common/src/lib/tool/shared/toolbox.ts new file mode 100644 index 0000000000..ff5962300e --- /dev/null +++ b/projects/common/src/lib/tool/shared/toolbox.ts @@ -0,0 +1,114 @@ +import { EntityRecord, EntityStore } from '../../entity'; +import { Tool } from './tool.interface'; +import { BehaviorSubject, Subscription } from 'rxjs'; + +/** + * Service where all available tools and their component are registered. + */ +export class Toolbox { + + activeTool$: BehaviorSubject = new BehaviorSubject(undefined); + + activeToolHistory: string[] = []; + + private activeTool$$: Subscription; + + private store = new EntityStore([], { + getKey: (tool: Tool) => tool.name + }); + + constructor() { + this.initStore(); + } + + destroy() { + this.activeTool$$.unsubscribe(); + this.store.destroy(); + } + + /** + * Return a tool + * @param name Tool name + * @returns tool Tool + */ + getTool(name: string): Tool { + return this.store.get(name); + } + + /** + * Return all tools + * @returns Array of tools + */ + getTools(): Tool[] { + return this.store.all(); + } + + addTool(tool: Tool) { + this.store.update(tool); + } + + /** + * Set tool configurations + * @param tools Tools + */ + setTools(tools: Tool[]) { + this.store.load(tools); + } + + activateTool(name: string, options: {[key: string]: any} = {}) { + const tool = this.getTool(name); + this.store.state.update(tool, {active: true, options}, true); + } + + activatePreviousTool() { + if (this.activeToolHistory.length <= 1) { + this.deactivateTool(); + return; + } + const [previous, current] = this.activeToolHistory.splice(-2, 2); + this.activateTool(previous); + } + + deactivateTool() { + this.clearActiveToolHistory(); + this.store.state.updateAll({active: false}); + } + + private initStore() { + this.store = new EntityStore([], { + getKey: (entity: Tool) => entity.name + }); + + this.activeTool$$ = this.store.stateView + .firstBy$((record: EntityRecord) => record.state.active === true) + .subscribe((record: EntityRecord) => { + if (record === undefined) { + this.setActiveTool(undefined); + return; + } + + const tool = record.entity; + const options = Object.assign( + {}, + tool.options || {}, + record.state.options || {} + ); + this.setActiveTool(Object.assign({}, tool, {options})); + }); + } + + private setActiveTool(tool: Tool | undefined) { + this.activeTool$.next(tool); + if (tool === undefined) { + this.clearActiveToolHistory(); + } else { + this.activeToolHistory = this.activeToolHistory + .filter((name: string) => name !== tool.name) + .concat([tool.name]); + } + } + + private clearActiveToolHistory() { + this.activeToolHistory = []; + } +} diff --git a/projects/common/src/lib/tool/tool.module.ts b/projects/common/src/lib/tool/tool.module.ts new file mode 100644 index 0000000000..d795ebb5a2 --- /dev/null +++ b/projects/common/src/lib/tool/tool.module.ts @@ -0,0 +1,25 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ToolService } from './shared/tool.service'; +import { IgoToolboxModule } from './toolbox/toolbox.module'; + +@NgModule({ + imports: [ + CommonModule + ], + exports: [ + IgoToolboxModule + ], + declarations: [] +}) +export class IgoToolModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: IgoToolModule, + providers: [ + ToolService + ] + }; + } +} diff --git a/projects/common/src/lib/tool/toolbox/toolbox.animation.ts b/projects/common/src/lib/tool/toolbox/toolbox.animation.ts new file mode 100644 index 0000000000..80114fbbe0 --- /dev/null +++ b/projects/common/src/lib/tool/toolbox/toolbox.animation.ts @@ -0,0 +1,23 @@ +import { + trigger, + state, + style, + transition, + animate, + AnimationTriggerMetadata +} from '@angular/animations'; + +export function toolSlideInOut( + speed = '300ms', + type = 'ease-in-out' +): AnimationTriggerMetadata { + return trigger('toolSlideInOut', [ + state( + 'enter', + style({ + transform: 'translate3d(0, 0, 0)' + }) + ), + transition('void => enter', animate(speed + ' ' + type)) + ]); +} diff --git a/projects/common/src/lib/tool/toolbox/toolbox.component.html b/projects/common/src/lib/tool/toolbox/toolbox.component.html new file mode 100644 index 0000000000..def14ebf05 --- /dev/null +++ b/projects/common/src/lib/tool/toolbox/toolbox.component.html @@ -0,0 +1,20 @@ + + + +
+ + + + +
diff --git a/projects/common/src/lib/tool/toolbox/toolbox.component.scss b/projects/common/src/lib/tool/toolbox/toolbox.component.scss new file mode 100644 index 0000000000..f0eaa5076e --- /dev/null +++ b/projects/common/src/lib/tool/toolbox/toolbox.component.scss @@ -0,0 +1,44 @@ +:host { + display: block; + position: relative; + overflow: hidden; + width: 100%; + height: 100%; +} + +/*** Actionbar and toolbox ***/ + +.igo-tool-container { + position: absolute; + top: 0; + bottom: 0; + left: 51px; + right: 0; + transform: translate3d(100%, 0, 0); +} + +.igo-tool-container-empty { + width: 0; +} + +igo-actionbar { + height: 100%; +} + +igo-actionbar.with-title { + width: 100%; + overflow: auto; +} + +igo-actionbar:not(.with-title) { + width: 48px; + overflow: hidden; + -moz-box-shadow: 2px 0px 2px 0px #dddddd; + -webkit-box-shadow: 2px 0px 2px 0px #dddddd; + -o-box-shadow: 2px 0px 2px 0px #dddddd; + box-shadow: 2px 0px 2px 0px #dddddd; +} + +igo-actionbar ::ng-deep igo-list { + overflow: inherit; +} \ No newline at end of file diff --git a/projects/common/src/lib/tool/toolbox/toolbox.component.ts b/projects/common/src/lib/tool/toolbox/toolbox.component.ts new file mode 100644 index 0000000000..16f53b27e3 --- /dev/null +++ b/projects/common/src/lib/tool/toolbox/toolbox.component.ts @@ -0,0 +1,193 @@ +import { + Component, + Input, + OnDestroy, + OnInit, + ChangeDetectionStrategy +} from '@angular/core'; + +import { Subscription, BehaviorSubject } from 'rxjs'; + +import { ActionStore } from '../../action'; +import { Tool } from '../shared/tool.interface'; +import { Toolbox } from '../shared/toolbox'; +import { toolSlideInOut } from './toolbox.animation'; + +@Component({ + selector: 'igo-toolbox', + templateUrl: 'toolbox.component.html', + styleUrls: ['toolbox.component.scss'], + animations: [toolSlideInOut()], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ToolboxComponent implements OnInit, OnDestroy { + + /** + * Observable of the active tool + */ + activeTool$: BehaviorSubject = new BehaviorSubject(undefined); + + /** + * Store of actions that toggle tools + */ + actionStore: ActionStore = new ActionStore([]); + + /** + * Observable of he anmation state + */ + animation$: BehaviorSubject = new BehaviorSubject('none'); + + /** + * Subscription to the active tool + */ + private activeTool$$: Subscription; + + /** + * Observable of the ongoing animation. This is useful when + * multiple animations are triggered at once i.e. when the user clicks + * too fast on different actions + */ + private animating$ = new BehaviorSubject(false); + + /** + * Subscription to the ongoing animation + */ + private animating$$: Subscription; + + /** + * Toolbox + */ + @Input() toolbox: Toolbox; + + /** + * Whether the toolbox should animate the first tool entering + */ + @Input() animate: boolean = false; + + /** + * Whether the Toolbar should display actions' titles + */ + get toolbarWithTitle(): boolean { + return this.activeTool$.value === undefined; + } + + /** + * Initialize the toolbar and subscribe to the active tool + * @internal + */ + ngOnInit() { + this.initToolbar(); + this.activeTool$$ = this.toolbox.activeTool$ + .subscribe((tool: Tool) => this.onActiveToolChange(tool)); + } + + /** + * Unsubscribe to the active tool and destroy the action store + * @internal + */ + ngOnDestroy() { + this.activeTool$$.unsubscribe(); + this.actionStore.destroy(); + } + + /** + * Track the starting animation + * @internal + */ + onAnimationStart() { + this.animating$.next(true); + } + + /** + * Untrack the completed animation + * @internal + */ + onAnimationComplete() { + this.animating$.next(false); + } + + /** + * Return a tool's inputs + * @param tool Tool + * @returns Tool inputs + * @internal + */ + getToolInputs(tool: Tool): {[key: string]: any} { + return tool.options || {}; + } + + /** + * Activate a tool and trigger an animation or not + * @param tool Tool to activate + */ + private onActiveToolChange(tool: Tool) { + if (!this.animate) { + this.setActiveTool(tool); + return; + } + this.onAnimate(() => this.setActiveTool(tool)); + } + + /** + * Set the active tool + * @param tool Tool to activate + */ + private setActiveTool(tool: Tool | undefined) { + if (tool === undefined) { + this.actionStore.state.updateAll({active: false}); + } else { + const action = this.actionStore.get(tool.name); + this.actionStore.state.update(action, {active: true}, true); + } + + this.activeTool$.next(tool); + if (this.animate) { + this.animation$.next('enter'); + } + } + + /** + * Initialize the toolbar + */ + private initToolbar() { + const actions = this.toolbox.getTools().map((tool: Tool) => { + return { + id: tool.name, + title: tool.title, + icon: tool.icon, + iconImage: tool.iconImage, + tooltip: tool.tooltip, + args: [tool, this.toolbox], + handler: (_tool: Tool, _toolbox: Toolbox) => { + _toolbox.activateTool(_tool.name); + } + }; + }); + this.actionStore.load(actions); + } + + /** + * Observe the ongoing animation and ignore any incoming animation + * while one is still ongoing. + * @param callback Callback to execute when the animation completes + */ + private onAnimate(callback: () => void) { + this.unAnimate(); + this.animating$$ = this.animating$.subscribe((animation: boolean) => { + if (!animation) { + callback.call(this); + this.unAnimate(); + } + }); + } + + /** + * Stop observing an animation when it's complete + */ + private unAnimate() { + if (this.animating$$) { + this.animating$$.unsubscribe(); + } + } + +} diff --git a/projects/common/src/lib/tool/toolbox/toolbox.module.ts b/projects/common/src/lib/tool/toolbox/toolbox.module.ts new file mode 100644 index 0000000000..f44e8f187b --- /dev/null +++ b/projects/common/src/lib/tool/toolbox/toolbox.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { IgoActionModule } from '../../action/action.module'; +import { + IgoDynamicComponentModule +} from '../../dynamic-component/dynamic-component.module'; + +import { ToolboxComponent } from './toolbox.component'; + +/** + * @ignore + */ +@NgModule({ + imports: [ + CommonModule, + IgoActionModule, + IgoDynamicComponentModule + ], + exports: [ + ToolboxComponent + ], + declarations: [ + ToolboxComponent + ] +}) +export class IgoToolboxModule {}