Permalink
Browse files

feat(all): remove "lock" setting

add "keyboard" setting
add "backdropDismiss" setting
add "showCloseButton" bindable property to ai-dialog-header
  • Loading branch information...
StrahilKazlachev committed Mar 4, 2017
1 parent 188ab2b commit a0fd54a18f1b45609148a690228f0b1a210c27d7
View
@@ -1,4 +1,4 @@
-import {customElement, inlineView} from 'aurelia-templating';
+import { customElement, inlineView } from 'aurelia-templating';
@customElement('ai-dialog-body')
@inlineView(`
View
@@ -1,5 +1,5 @@
-import {customElement, bindable, inlineView} from 'aurelia-templating';
-import {DialogController} from './dialog-controller';
+import { customElement, bindable, inlineView } from 'aurelia-templating';
+import { DialogController } from './dialog-controller';
/**
* View-model for footer of Dialog.
@@ -27,6 +27,9 @@ export class AiDialogFooter {
@bindable public buttons: any[] = [];
@bindable public useDefaultButtons: boolean = false;
+ /**
+ * @internal
+ */
public static inject = [DialogController];
constructor(public controller: DialogController) { }
View
@@ -1,5 +1,5 @@
-import {customElement, inlineView} from 'aurelia-templating';
-import {DialogController} from './dialog-controller';
+import { customElement, bindable, inlineView } from 'aurelia-templating';
+import { DialogController } from './dialog-controller';
@customElement('ai-dialog-header')
@inlineView(`
@@ -8,7 +8,7 @@ import {DialogController} from './dialog-controller';
type="button"
class="dialog-close"
aria-label="Close"
- if.bind="!controller.settings.lock"
+ show.bind="showCloseButton"
click.trigger="controller.cancel()">
<span aria-hidden="true">&times;</span>
</button>
@@ -20,6 +20,11 @@ import {DialogController} from './dialog-controller';
`)
export class AiDialogHeader {
+ @bindable() public showCloseButton: boolean = false;
+
+ /**
+ * @internal
+ */
public static inject = [DialogController];
constructor(public controller: DialogController) { }
}
View
@@ -1,4 +1,4 @@
-import {customElement, inlineView} from 'aurelia-templating';
+import { customElement, inlineView } from 'aurelia-templating';
@customElement('ai-dialog')
@inlineView(`
View
@@ -1,10 +1,13 @@
-import {customAttribute, ComponentAttached} from 'aurelia-templating';
-import {DOM} from 'aurelia-pal';
+import { customAttribute, ComponentAttached } from 'aurelia-templating';
+import { DOM } from 'aurelia-pal';
@customAttribute('attach-focus')
export class AttachFocus implements ComponentAttached {
public value: boolean | string;
+ /**
+ * @internal
+ */
public static inject = [DOM.Element];
constructor(private element: HTMLElement) {
this.value = true;
View
@@ -1,5 +1,5 @@
-import {FrameworkConfiguration} from 'aurelia-framework';
-import {DialogConfiguration} from './dialog-configuration';
+import { FrameworkConfiguration } from 'aurelia-framework';
+import { DialogConfiguration } from './dialog-configuration';
export function configure(
frameworkConfig: FrameworkConfiguration,
@@ -14,19 +14,16 @@ export function configure(
applyConfig();
}
-export {AiDialog} from './ai-dialog';
-export {AiDialogHeader} from './ai-dialog-header';
-export {AiDialogBody} from './ai-dialog-body';
-export {AiDialogFooter} from './ai-dialog-footer';
-export {AttachFocus} from './attach-focus';
+export * from './ai-dialog';
+export * from './ai-dialog-header';
+export * from './ai-dialog-body';
+export * from './ai-dialog-footer';
+export * from './attach-focus';
export * from './interfaces';
-export {DialogSettings} from './dialog-settings';
-export {DialogConfiguration, DialogResourceName} from './dialog-configuration';
-export {RendererStatic, Renderer} from './renderer';
-export {DialogCancelError} from './dialog-cancel-error';
-export {
- DialogCancelableOperationResult, DialogCancelResult,
- DialogOpenResult, DialogCloseResult
-} from './dialog-result';
-export {DialogService, DialogOpenPromise} from './dialog-service';
-export {DialogController} from './dialog-controller';
+export * from './dialog-settings';
+export * from './dialog-configuration';
+export * from './renderer';
+export * from './dialog-cancel-error';
+export * from './dialog-result';
+export * from './dialog-service';
+export * from './dialog-controller';
@@ -6,6 +6,9 @@ export interface DialogCancelError extends Error {
output?: any;
}
+/**
+ * @internal
+ */
export function createDialogCancelError(output?: any): DialogCancelError {
const error = new Error('Operation cancelled.') as DialogCancelError;
error.wasCancelled = true;
@@ -1,8 +1,8 @@
-import {FrameworkConfiguration} from 'aurelia-framework';
-import {Renderer, RendererStatic} from './renderer';
-import {DialogSettings, DefaultDialogSettings} from './dialog-settings';
-import {DialogRenderer} from './dialog-renderer';
-import {DOM} from 'aurelia-pal';
+import { FrameworkConfiguration } from 'aurelia-framework';
+import { Renderer, RendererStatic } from './renderer';
+import { DialogSettings, DefaultDialogSettings } from './dialog-settings';
+import { DialogRenderer } from './dialog-renderer';
+import { DOM } from 'aurelia-pal';
const defaultRenderer: RendererStatic = DialogRenderer;
View
@@ -1,17 +1,21 @@
-import {Controller} from 'aurelia-templating';
-import {Renderer} from './renderer';
-import {DialogCancelableOperationResult, DialogCloseResult, DialogCancelResult} from './dialog-result';
-import {DialogSettings} from './dialog-settings';
-import {invokeLifecycle} from './lifecycle';
-import {createDialogCancelError} from './dialog-cancel-error';
+import { Controller } from 'aurelia-templating';
+import { Renderer } from './renderer';
+import { DialogCancelableOperationResult, DialogCloseResult, DialogCancelResult } from './dialog-result';
+import { DialogSettings } from './dialog-settings';
+import { invokeLifecycle } from './lifecycle';
+import { createDialogCancelError } from './dialog-cancel-error';
/**
* A controller object for a Dialog instance.
*/
export class DialogController {
private resolve: (data?: any) => void;
private reject: (reason: any) => void;
- private closePromise: Promise<any> | undefined;
+
+ /**
+ * @internal
+ */
+ public closePromise: Promise<any> | undefined;
/**
* The settings used by this controller.
@@ -20,6 +24,9 @@ export class DialogController {
public renderer: Renderer;
public controller: Controller;
+ /**
+ * @internal
+ */
public static inject = [Renderer];
/**
* Creates an instance of DialogController.
@@ -35,13 +42,19 @@ export class DialogController {
this.renderer = renderer;
}
- private releaseResources(): Promise<void> {
+ /**
+ * @internal
+ */
+ public releaseResources(): Promise<void> {
return invokeLifecycle(this.controller.viewModel || {}, 'deactivate')
.then(() => this.renderer.hideDialog(this))
.then(() => { this.controller.unbind(); });
}
- private cancelOperation(): DialogCancelResult {
+ /**
+ * @internal
+ */
+ public cancelOperation(): DialogCancelResult {
if (!this.settings.rejectOnCancel) {
return { wasCancelled: true };
}
View
@@ -1,7 +1,8 @@
-import {DOM} from 'aurelia-pal';
-import {transient} from 'aurelia-dependency-injection';
-import {Renderer} from './renderer';
-import {DialogController} from './dialog-controller';
+import { DOM } from 'aurelia-pal';
+import { transient } from 'aurelia-dependency-injection';
+import { ActionKey } from './dialog-settings';
+import { Renderer } from './renderer';
+import { DialogController } from './dialog-controller';
const containerTagName = 'ai-dialog-container';
const overlayTagName = 'ai-dialog-overlay';
@@ -46,22 +47,37 @@ export const hasTransition = (() => {
const body = DOM.querySelectorAll('body')[0] as HTMLBodyElement;
+function getActionKey(e: KeyboardEvent): ActionKey | undefined {
+ if ((e.code || e.key) === 'Escape' || e.keyCode === 27) {
+ return 'Escape';
+ }
+ if ((e.code || e.key) === 'Enter' || e.keyCode === 13) {
+ return 'Enter';
+ }
+ return undefined;
+}
+
@transient()
export class DialogRenderer implements Renderer {
public static dialogControllers: DialogController[] = [];
- public static escapeKeyEventHandler(e: KeyboardEvent) {
- if (e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27) {
- const top = DialogRenderer.dialogControllers[DialogRenderer.dialogControllers.length - 1];
- if (top && (top.settings.lock !== true || top.settings.enableEscClose === true)) {
- top.cancel();
- }
+ public static keyboardEventHandler(e: KeyboardEvent) {
+ const key = getActionKey(e);
+ if (!key) { return; }
+ const top = DialogRenderer.dialogControllers[DialogRenderer.dialogControllers.length - 1];
+ if (!top || !top.settings.keyboard) { return; }
+ const keyboard = top.settings.keyboard;
+ if (key === 'Escape'
+ && (keyboard === true || keyboard === key || (Array.isArray(keyboard) && keyboard.indexOf(key) > -1))) {
+ top.cancel();
+ } else if (key === 'Enter' && (keyboard === key || (Array.isArray(keyboard) && keyboard.indexOf(key) > -1))) {
+ top.ok();
}
}
public static trackController(dialogController: DialogController): void {
if (!DialogRenderer.dialogControllers.length) {
- DOM.addEventListener('keyup', DialogRenderer.escapeKeyEventHandler, false);
+ DOM.addEventListener('keyup', DialogRenderer.keyboardEventHandler, false);
}
DialogRenderer.dialogControllers.push(dialogController);
}
@@ -72,7 +88,7 @@ export class DialogRenderer implements Renderer {
DialogRenderer.dialogControllers.splice(i, 1);
}
if (!DialogRenderer.dialogControllers.length) {
- DOM.removeEventListener('keyup', DialogRenderer.escapeKeyEventHandler, false);
+ DOM.removeEventListener('keyup', DialogRenderer.keyboardEventHandler, false);
}
}
@@ -128,7 +144,7 @@ export class DialogRenderer implements Renderer {
private setupClickHandling(dialogController: DialogController): void {
this.stopPropagation = e => { e._aureliaDialogHostClicked = true; };
this.closeDialogClick = e => {
- if (!dialogController.settings.lock && !e._aureliaDialogHostClicked) {
+ if (dialogController.settings.backdropDismiss && !e._aureliaDialogHostClicked) {
dialogController.cancel();
return;
}
View
@@ -1,4 +1,4 @@
-import {DialogController} from './dialog-controller';
+import { DialogController } from './dialog-controller';
/**
* The result that a dialog cancelable operation resolves to.
View
@@ -1,25 +1,23 @@
-import {Container} from 'aurelia-dependency-injection';
-import {Origin} from 'aurelia-metadata';
-import {CompositionEngine, Controller, ViewSlot, CompositionContext} from 'aurelia-templating';
-import {DialogOpenResult, DialogCloseResult, DialogCancelResult} from './dialog-result';
-import {DialogSettings, DefaultDialogSettings} from './dialog-settings';
-import {createDialogCancelError} from './dialog-cancel-error';
-import {invokeLifecycle} from './lifecycle';
-import {DialogController} from './dialog-controller';
+import { Container } from 'aurelia-dependency-injection';
+import { Origin } from 'aurelia-metadata';
+import { CompositionEngine, Controller, ViewSlot, CompositionContext } from 'aurelia-templating';
+import { DialogOpenResult, DialogCloseResult, DialogCancelResult } from './dialog-result';
+import { DialogSettings, DefaultDialogSettings } from './dialog-settings';
+import { createDialogCancelError } from './dialog-cancel-error';
+import { invokeLifecycle } from './lifecycle';
+import { DialogController } from './dialog-controller';
export type DialogCancellableOpenResult = DialogOpenResult | DialogCancelResult;
/* tslint:disable:max-line-length */
-export interface WhenClosed {
+export interface DialogOpenPromise<T extends DialogCancellableOpenResult> extends Promise<T> {
whenClosed(onfulfilled?: ((value: DialogCloseResult) => DialogCloseResult | PromiseLike<DialogCloseResult>) | undefined | null, onrejected?: ((reason: any) => DialogCloseResult | PromiseLike<DialogCloseResult>) | undefined | null): Promise<DialogCloseResult>;
whenClosed<TResult>(onfulfilled: ((value: DialogCloseResult) => DialogCloseResult | PromiseLike<DialogCloseResult>) | undefined | null, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<DialogCloseResult | TResult>;
whenClosed<TResult>(onfulfilled: (value: DialogCloseResult) => TResult | PromiseLike<TResult>, onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TResult>;
whenClosed<TResult1, TResult2>(onfulfilled: (value: DialogCloseResult) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
}
/* tslint:enable:max-line-length */
-export type DialogOpenPromise<T extends DialogCancellableOpenResult> = Promise<T> & WhenClosed;
-
function whenClosed(this: Promise<DialogCancellableOpenResult>, onfulfilled?: any, onrejected?: any) {
return this.then<DialogCloseResult>(r => r.wasCancelled ? r : r.closeResult).then(onfulfilled, onrejected);
}
@@ -47,6 +45,9 @@ export class DialogService {
public hasOpenDialog: boolean = false;
public hasActiveDialog: boolean = false;
+ /**
+ * @internal
+ */
public static inject = [Container, CompositionEngine, DefaultDialogSettings];
constructor(container: Container, compositionEngine: CompositionEngine, defaultSettings: DialogSettings) {
this.container = container;
@@ -170,7 +171,7 @@ export class DialogService {
* @return Promise<DialogController[]> All controllers whose close operation was cancelled.
*/
public closeAll(): Promise<DialogController[]> {
- return Promise.all(this.controllers.slice(0).map((controller) => {
+ return Promise.all(this.controllers.slice(0).map(controller => {
if (!controller.settings.rejectOnCancel) {
return controller.cancel().then(result => {
if (result.wasCancelled) {
@@ -185,7 +186,7 @@ export class DialogService {
}
return Promise.reject(reason);
});
- })).then((unclosedControllers) => unclosedControllers.filter(unclosed => !!unclosed));
+ })).then(unclosedControllers => unclosedControllers.filter(unclosed => !!unclosed));
}
}
View
@@ -1,4 +1,6 @@
-import {ViewStrategy} from 'aurelia-templating';
+import { ViewStrategy } from 'aurelia-templating';
+
+export type ActionKey = 'Escape' | 'Enter';
/**
* All available dialog settings.
@@ -22,15 +24,20 @@ export interface DialogSettings {
model?: any;
/**
- * When set to false allows the dialog to be closed with ESC key or clicking outside the dialog.
- * When set to true the dialog does not close on ESC key or clicking outside it.
+ * Allows for closing the top most dialog via the keyboard.
+ * When set to "false" no action will be taken.
+ * If set to "true", "Escape" or an array containing "Escape"
+ * the dialog will be "cancel" closed when the ESC key is pressed.
+ * If set to "Enter" or and array containing "Enter"
+ * the dialog will be "ok" closed when the ENTER key is pressed.
+ * Using the array format allows combining the ESC and ENTER keys.
*/
- lock?: boolean;
+ keyboard?: boolean | ActionKey | ActionKey[];
/**
- * When set to true the dialog can be closed with the ESC key.
+ * When set to "true" allows for the dismissal of the dialog by clicking outside of it.
*/
- enableEscClose?: boolean;
+ backdropDismiss?: boolean;
/**
* The z-index of the dialog.
@@ -65,8 +72,8 @@ export interface DialogSettings {
*/
export class DefaultDialogSettings implements DialogSettings {
[setting: string]: any;
- public lock = true;
- public enableEscClose = false;
+ public keyboard = false;
+ public backdropDismiss: false;
public startingZIndex = 1000;
public centerHorizontalOnly = false;
public rejectOnCancel = false;
Oops, something went wrong.

0 comments on commit a0fd54a

Please sign in to comment.