Skip to content

Commit

Permalink
feat(modal): allow open instance of component into the dialog (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
pimenovoleg authored Jul 10, 2019
1 parent 6a1a4bc commit 8315eef
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 16 deletions.
45 changes: 43 additions & 2 deletions packages/mosaic-dev/modal/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ export class ModalDemoComponent {
});
}

createModalComponent() {
const modalRef = this.modalService.open({
mcComponent: McModalFullCustomComponent
});
}

createLongModal() {

const modal = this.modalService.create({
Expand Down Expand Up @@ -163,15 +169,50 @@ export class McModalCustomComponent {
}
}

@Component({
selector: 'mc-modal-full-custom-component',
template: `
<mc-modal-title>
Modal Title
</mc-modal-title>
<mc-modal-body>
<h2>{{ title }}</h2>
<h4>{{ subtitle }}</h4>
<p>
<span>Get Modal instance in component</span>
<button mc-button color="primary" (click)="destroyModal()">destroy modal in the component</button>
</p>
</mc-modal-body>
<div mc-modal-footer>
<button mc-button color="primary" >Save</button>
<button mc-button autofocus>Close</button>
</div>
`
})
export class McModalFullCustomComponent {
@Input() title: string;
@Input() subtitle: string;

constructor(private modal: McModalRef) { }

destroyModal() {
this.modal.destroy({ data: 'this the result data' });
}
}

@NgModule({
declarations: [
ModalDemoComponent,
McModalCustomComponent,
McModalLongCustomComponent
McModalLongCustomComponent,
McModalFullCustomComponent
],
entryComponents: [
McModalCustomComponent,
McModalLongCustomComponent
McModalLongCustomComponent,
McModalFullCustomComponent
],
imports: [
BrowserModule,
Expand Down
3 changes: 3 additions & 0 deletions packages/mosaic-dev/modal/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@
mcCancelText="Cancel"
[mcZIndex]="1001"
mcTitle="Non-service html modal">This is a non-service html modal</mc-modal>

<p class="mc-headline">Modal with looong component</p>
<button mc-button color="primary" (click)="createModalComponent()">Open Component</button>
34 changes: 34 additions & 0 deletions packages/mosaic/modal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
The dialog is currently divided into 3 modes - `default`, `confirm box`, `custom`.


#### Using service to create Confirm Mode

```ts
showConfirm() {
this.modalService.success({
mcContent : 'Save changes made to the request "All assets with Windows"?',
mcOkText : 'Save',
mcCancelText: 'Cancel',
mcOnOk : () => console.log('OK')
});
}
```

#### Using service to open modal Custom Mode

```ts
let dialogRef = modalService.open({
mcComponent: CustomComponent
});
```

```ts
@Component({/* ... */})
export class CustomComponent {
constructor(private dialogRef: McModalRef) { }

closeDialog() {
this.modal.destroy({ data: 'this the result data' });
}
}
```
7 changes: 7 additions & 0 deletions packages/mosaic/modal/modal.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,19 @@
[ngTemplateOutlet]="tplContentDefault"></ng-container>
<ng-container *ngSwitchCase="isModalType('confirm')"
[ngTemplateOutlet]="tplContentConfirm"></ng-container>
<ng-container *ngSwitchCase="isModalType('custom')"
[ngTemplateOutlet]="tplContentCustom"></ng-container>
</ng-container>
</div>
</div>
</div>
</div>

<ng-template #tplContentCustom>
<ng-container #bodyContainer></ng-container>
</ng-template>


<!-- [Predefined] Default Modal Content -->
<ng-template #tplContentDefault>
<div *ngIf="mcTitle" class="mc-modal-header">
Expand Down
12 changes: 9 additions & 3 deletions packages/mosaic/modal/modal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export class McModalComponent<T = any, R = any> extends McModalRef<T, R>

// tslint:disable-next-line:no-any
@Input() mcModalType: ModalType = 'default';

// The instance of component opened into the dialog.
@Input() mcComponent: Type<T>;
// If not specified, will use <ng-content>
@Input() mcContent: string | TemplateRef<{}> | Type<T>;
// available when mcContent is a component
Expand Down Expand Up @@ -117,7 +120,7 @@ export class McModalComponent<T = any, R = any> extends McModalRef<T, R>

@Input() @Output() mcOnCancel: EventEmitter<T> | OnClickCallback<T> = new EventEmitter<T>();

@ViewChild('modalContainer', {static: false}) modalContainer: ElementRef;
@ViewChild('modalContainer', { static: true }) modalContainer: ElementRef;
@ViewChild('bodyContainer', { read: ViewContainerRef, static: false}) bodyContainer: ViewContainerRef;
// Only aim to focus the ok button that needs to be auto focused
@ViewChild('autoFocusButtonOk', { read: ElementRef, static: false}) autoFocusButtonOk: ElementRef;
Expand Down Expand Up @@ -184,6 +187,11 @@ export class McModalComponent<T = any, R = any> extends McModalRef<T, R>
this.mcFooter = this.formatModalButtons(this.mcFooter as IModalButtonOptions<T>[]);
}


if (this.isComponent(this.mcComponent)) {
this.createDynamicComponent(this.mcComponent as Type<T>);
}

// Place the modal dom to elsewhere
this.container = typeof this.mcGetContainer === 'function' ? this.mcGetContainer() : this.mcGetContainer;
if (this.container instanceof HTMLElement) {
Expand Down Expand Up @@ -479,8 +487,6 @@ export class McModalComponent<T = any, R = any> extends McModalRef<T, R>
// Do the first change detection immediately
// (or we do detection at ngAfterViewInit, multi-changes error will be thrown)
this.contentComponentRef.changeDetectorRef.detectChanges();


}

// Update transform-origin to the last click position on document
Expand Down
26 changes: 26 additions & 0 deletions packages/mosaic/modal/modal.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Directive } from '@angular/core';


@Directive({
selector: `[mc-modal-title], mc-modal-title, [mcModalTitle]`,
host: {
class: 'mc-modal-header mc-modal-title'
}
})
export class McModalTitle {}

@Directive({
selector: `[mc-modal-body], mc-modal-body, [mcModalBody]`,
host: {
class: 'mc-modal-body'
}
})
export class McModalBody {}

@Directive({
selector: `[mc-modal-footer], mc-modal-footer, [mcModalFooter]`,
host: {
class: 'mc-modal-footer'
}
})
export class McModalFooter {}
16 changes: 14 additions & 2 deletions packages/mosaic/modal/modal.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,25 @@ import { McIconModule } from '@ptsecurity/mosaic/icon';
import { CssUnitPipe } from './css-unit.pipe';
import { McModalControlService } from './modal-control.service';
import { McModalComponent } from './modal.component';
import { McModalBody, McModalFooter, McModalTitle } from './modal.directive';
import { McModalService } from './modal.service';


@NgModule({
imports: [CommonModule, OverlayModule, A11yModule, McButtonModule, McIconModule],
exports: [McModalComponent],
declarations: [McModalComponent, CssUnitPipe],
exports: [
McModalComponent,
McModalTitle,
McModalBody,
McModalFooter
],
declarations: [
McModalComponent,
McModalTitle,
McModalBody,
McModalFooter,
CssUnitPipe
],
entryComponents: [McModalComponent],
providers: [McModalControlService, McModalService]
})
Expand Down
9 changes: 3 additions & 6 deletions packages/mosaic/modal/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,22 @@

.mc-modal-header {
padding: 14px 16px;

display: block;
border-radius: 4px 4px 0 0;
}

.mc-modal-body {
display: block;
padding: 16px 24px 24px 24px;

max-height: calc(100vh - 260px);

word-wrap: break-word;

overflow-y: auto;
}

.mc-modal-footer {
display: block;
padding: 16px 16px;

border-radius: 0 0 4px 4px;

text-align: right;

button + button {
Expand Down
11 changes: 9 additions & 2 deletions packages/mosaic/modal/modal.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentType, Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { ComponentRef, Injectable } from '@angular/core';
import { ComponentRef, Injectable, TemplateRef } from '@angular/core';
import { ESCAPE } from '@ptsecurity/cdk/keycodes';
import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
Expand Down Expand Up @@ -123,6 +123,13 @@ export class McModalService {
return this.create(options);
}

open<T>(options: IModalOptionsForService<T> = {}): McModalRef<T> {

options.mcModalType = 'custom';

return this.create(options);
}

success<T>(options: IModalOptionsForService<T> = {}): McModalRef<T> {
return this.simpleConfirm(options, 'success');
}
Expand Down
3 changes: 2 additions & 1 deletion packages/mosaic/modal/modal.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { EventEmitter, TemplateRef, Type } from '@angular/core';
export type OnClickCallback<T> = ((instance: T) => (false | void | {}) | Promise<false | void | {}>);

// Different modal styles we have supported
export type ModalType = 'default' | 'confirm';
export type ModalType = 'default' | 'confirm' | 'custom';

// Subtypes of Confirm Modal
export type ConfirmType = 'confirm' | 'success' | 'warn';
Expand All @@ -21,6 +21,7 @@ export interface IModalOptions<T = any, R = any> {
mcStyle?: object;
mcTitle?: string | TemplateRef<{}>;
mcContent?: string | TemplateRef<{}> | Type<T>;
mcComponent?: Type<T>; // The instance of component opened into the dialog.
mcComponentParams?: Partial<T>;
mcClosable?: boolean;
mcMask?: boolean;
Expand Down

0 comments on commit 8315eef

Please sign in to comment.