Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Message API Improvements #349

Merged
merged 6 commits into from
Jun 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions admin/tabs/configs/differ/ConfigDiffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {button, dialog} from '@xh/hoist/kit/blueprint';
import {filler, fragment, panel} from '@xh/hoist/cmp/layout';
import {grid} from '@xh/hoist/cmp/grid';
import {comboField, label} from '@xh/hoist/cmp/form';
import {message} from '@xh/hoist/cmp/message';
import {toolbar} from '@xh/hoist/cmp/toolbar';

import {configDifferDetail} from './ConfigDifferDetail';
Expand Down Expand Up @@ -56,16 +55,13 @@ export class ConfigDiffer extends Component {
onClick: this.onLoadDiffClick
})
),
items: [
grid({
model: model.gridModel,
onRowDoubleClicked: this.onRowDoubleClicked,
agOptions: {
popupParent: null
}
}),
message({model: model.messageModel})
],
item: grid({
model: model.gridModel,
onRowDoubleClicked: this.onRowDoubleClicked,
agOptions: {
popupParent: null
}
}),
bbar: toolbar(
filler(),
button({
Expand Down
8 changes: 4 additions & 4 deletions admin/tabs/configs/differ/ConfigDifferModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {XH, HoistModel} from '@xh/hoist/core';
import {LocalStore} from '@xh/hoist/data';
import {p} from '@xh/hoist/cmp/layout';
import {GridModel} from '@xh/hoist/cmp/grid';
import {MessageModel} from '@xh/hoist/cmp/message';
import {StoreContextMenu} from '@xh/hoist/cmp/contextmenu';
import {ToastManager} from '@xh/hoist/toast';
import {baseCol} from '@xh/hoist/columns/Core';
Expand All @@ -25,7 +24,6 @@ import {ConfigDifferDetailModel} from './ConfigDifferDetailModel';
@HoistModel()
export class ConfigDifferModel {

messageModel = new MessageModel({title: 'Warning', icon: Icon.warning({size: 'lg'})});
detailModel = new ConfigDifferDetailModel({parent: this});

@observable isOpen = false;
Expand Down Expand Up @@ -147,7 +145,9 @@ export class ConfigDifferModel {
</div>
);

this.messageModel.confirm({
XH.confirm({
title: 'Warning',
icon: Icon.warning({size: 'lg'}),
message,
onConfirm: () => this.doApplyRemote(filteredData)
});
Expand Down Expand Up @@ -188,6 +188,6 @@ export class ConfigDifferModel {
}

destroy() {
XH.safeDestroy(this.messageModel, this.detailModel, this.gridModel);
XH.safeDestroy(this.detailModel, this.gridModel);
}
}
4 changes: 3 additions & 1 deletion app/AppContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {observable, observer, setter} from '@xh/hoist/mobx';
import {elemFactory, LoadState, XH} from '@xh/hoist/core';
import {contextMenu} from '@xh/hoist/cmp/contextmenu';
import {loadMask} from '@xh/hoist/cmp/mask';
import {messageSource} from '@xh/hoist/cmp/message';
import {div, frame, vframe, viewport, vspacer} from '@xh/hoist/cmp/layout';
import {logoutButton} from '@xh/hoist/cmp/button';
import {Icon} from '@xh/hoist/icon';
Expand All @@ -30,7 +31,7 @@ import {lockoutPanel} from './';
* Top-level wrapper to provide core Hoist Application layout and infrastructure to an application's
* root Component. Provides initialized Hoist services and a standard viewport that also includes
* standard UI elements such as an impersonation bar header, version bar footer, app-wide load mask,
* context menu, and error dialog.
* context menu, and popup message support.
*
* Construction of this container triggers the init of the core XH singleton, which queries for an
* authorized user and then proceeds to init all core Hoist and app-level services.
Expand Down Expand Up @@ -82,6 +83,7 @@ export class AppContainer extends Component {
versionBar()
),
loadMask({model: XH.appLoadModel}),
messageSource({model: XH.messageSourceModel}),
aboutDialog()
);
default:
Expand Down
5 changes: 4 additions & 1 deletion cmp/message/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {dialog, dialogBody, button} from '@xh/hoist/kit/blueprint';

/**
* A modal dialog that supports imperative alert/confirm.
* @see MessageModel for supported configuration options.
*
* @see MessageModel for supported configuration options - and an important note on built-in support
* for showing one-off messages via convenience methods on XH (vs. needing to instantiate this
* component directly).
*/
@HoistComponent()
class Message extends Component {
Expand Down
72 changes: 48 additions & 24 deletions cmp/message/MessageModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,36 @@ import {observable, action} from '@xh/hoist/mobx';
import {HoistModel} from '@xh/hoist/core';

/**
* Model for convenient, imperative Alerting/Confirming.
* Model for imperative Alerting/Confirming.
*
* This object may be constructed with default arguments and used to show multiple messages
* using the alert() and confirm() methods.
* Rather than creating Messages / MessageModels directly, most applications can leverage the global
* methods XH.message(), XH.alert(), and XH.confirm(). These convenience methods will create
* on-the-fly MessageModel instances which will be rendered in the global AppContainer and
* automatically destroyed upon closing.
*
* Expert applications may choose to manage a dedicated instance of this class directly. This may
* be useful when it is desired to set and re-use default settings and/or ensure that only a single
* message is shown at a time from a given source. In this case, the application will be
* responsible for rendering this model in a Message component and destroying it when complete.
*/
@HoistModel()
export class MessageModel {

/**
* Is the message currently being displayed?
*/
@observable isOpen = false;

/**
* Default Settings.
* These can be overridden in the constructor, the alert(), or the confirm() methods.
* These will be specified in the constructor or the show() method.
*/
defaults = {
title: null,
icon: null,
message: null,
confirmText: 'OK',
cancelText: 'Cancel',
confirmText: null,
cancelText: null,
confirmIntent: null,
cancelIntent: null,
onConfirm: null,
Expand All @@ -36,34 +46,53 @@ export class MessageModel {

/**
* @param {Object} [config] - default options for this instance.
* @param {String} config.message - icon to be displayed.
* @param {String} [config.title] - title of message box.
* @param {element} [config.icon] - icon to be displayed.
* @param {String} [config.confirmText] - Text for confirm button. If null, no button will be shown.
* @param {String} [config.cancelText] - Text for cancel button. If null, no button will be shown.
* @param {String} [config.confirmIntent] - Intent for confirm button.
* @param {String} [config.cancelIntent] - Intent for cancel button.
* @param {function} [config.onConfirm] - Callback to execute when confirm is clicked.
* @param {function} [config.onCancel] - Callback to execute when cancel is clicked.
*/
constructor(config) {
this.initialConfig = config;
}

/**
* Show a confirmation, with cancellation option.
* Show a message.
*
* This methods accepts additional arguments for this showing that will override any initial
* configuration specified in the constructor.
*
* @param {Object} [config] - options for this particular showing of the dialog.
* @param {String} config.message - text to be displayed.
* @param {String} [config.title] - title of message box.
* @param {element} [config.icon] - icon to be displayed.
* @param {String} [config.confirmText] - Text for confirm button. If null, button will not be shown.
* @param {String} [config.cancelText] - Text for cancel button. If null, button will not be shown.
* @param {String} [config.confirmIntent] - Intent for confirm button.
* @param {String} [config.cancelIntent] - Intent for cancel button.
* @param {function} [config.onConfirm] - Callback to execute when confirm is clicked.
* @param {function} [config.onCancel] - Callback to execute when cancel is clicked.
*/
confirm(config) {
@action
show(config) {
Object.assign(this, this.defaults, this.initialConfig, config);
this.open();
this.isOpen = true;
}

/**
* Show a simple alert, with no cancellation option.
* @param {Object} [config] - options for this particular showing of the dialog.
* Dismiss this message programmatically.
*
* Note that dismissing a message is typically triggered by user actions on provided buttons.
* This method should not typically need to be called by applications.
*/
alert(config) {
Object.assign(this, this.defaults, this.initialConfig, config, {
onCancel: null,
cancelText: null,
cancelIntent: null
});
this.open();
hide() {
this.close();
}


//------------------------
// Implementation
//------------------------
Expand All @@ -79,11 +108,6 @@ export class MessageModel {
this.close();
}

@action
open() {
this.isOpen = true;
}

@action
close() {
this.isOpen = false;
Expand Down
24 changes: 24 additions & 0 deletions cmp/message/impl/MessageSource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* This file belongs to Hoist, an application development toolkit
* developed by Extremely Heavy Industries (www.xh.io | [email protected])
*
* Copyright © 2018 Extremely Heavy Industries Inc.
*/
import {Component} from 'react';
import {HoistComponent, elemFactory} from '@xh/hoist/core';
import {fragment} from '@xh/hoist/cmp/layout';
import {message} from '../Message';

/**
* Support for hosting multiple global Messages in an application.
* Automatically created and installed into the layout by AppContainer.
* @private
*/
@HoistComponent()
export class MessageSource extends Component {
render() {
const children = this.model.msgModels.map(model => message({model}));
return children.length ? fragment(...children) : null;
}
}
export const messageSource = elemFactory(MessageSource);
43 changes: 43 additions & 0 deletions cmp/message/impl/MessageSourceModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file belongs to Hoist, an application development toolkit
* developed by Extremely Heavy Industries (www.xh.io | [email protected])
*
* Copyright © 2018 Extremely Heavy Industries Inc.
*/
import {HoistModel} from '@xh/hoist/core';
import {observable, action} from '@xh/hoist/mobx';
import {MessageModel} from '../MessageModel';

/**
* Support for hosting multiple global Messages in an application.
* This class supports the public XH.message(), XH.alert() and XH.confirm() methods.
* @private
*/
@HoistModel()
export class MessageSourceModel {

@observable.ref msgModels = [];

show(config) {
const ret = new MessageModel(config);
ret.show();
this.addModel(ret);
return ret;
}

//-----------------------------------
// Implementation
//------------------------------------
@action
addModel(model) {
// Add new model and simultaneously cull any models we are done with.
const models = this.msgModels,
keepModels = models.filter(it => it.isOpen),
cullModels = models.filter(it => !it.isOpen);

keepModels.push(model);

this.msgModels = keepModels;
cullModels.forEach(it => it.destroy());
}
}
2 changes: 2 additions & 0 deletions cmp/message/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
*/
export * from './Message';
export * from './MessageModel';
export * from './impl/MessageSource';
export * from './impl/MessageSourceModel';
12 changes: 7 additions & 5 deletions cmp/rest/RestForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
*/

import {Component} from 'react';
import {HoistComponent, elemFactory} from '@xh/hoist/core';
import {HoistComponent, elemFactory, XH} from '@xh/hoist/core';
import {loadMask} from '@xh/hoist/cmp/mask';
import {message} from '@xh/hoist/cmp/message';
import {toolbar} from '@xh/hoist/cmp/toolbar';
import {filler, vframe} from '@xh/hoist/cmp/layout';
import {Icon} from '@xh/hoist/icon';
Expand Down Expand Up @@ -41,7 +40,6 @@ export class RestForm extends Component {
return [
dialogBody(this.getForm()),
toolbar(this.getButtons()),
message({model: model.messageModel}),
loadMask({model: model.loadModel})
];
}
Expand Down Expand Up @@ -90,8 +88,10 @@ export class RestForm extends Component {
warning = model.actionWarning.del;

if (warning) {
model.messageModel.confirm({
XH.confirm({
message: warning,
title: 'Warning',
icon: Icon.warning({size: 'lg'}),
onConfirm: () => model.deleteRecord()
});
} else {
Expand All @@ -105,8 +105,10 @@ export class RestForm extends Component {
warning = model.actionWarning[isAdd ? 'add' : 'edit'];

if (warning) {
model.messageModel.confirm({
XH.confirm({
message: warning,
title: 'Warning',
icon: Icon.warning({size: 'lg'}),
onConfirm: () => model.saveRecord()
});
} else {
Expand Down
6 changes: 1 addition & 5 deletions cmp/rest/RestFormModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import {XH, HoistModel} from '@xh/hoist/core';
import {start} from '@xh/hoist/promise';
import {observable, computed, action} from '@xh/hoist/mobx';
import {MessageModel} from '@xh/hoist/cmp/message';
import {Icon} from '@xh/hoist/icon';
import {isEqual} from 'lodash';

import {RestControlModel} from './RestControlModel';
Expand All @@ -19,7 +17,6 @@ export class RestFormModel {

parent = null;
controlModels = [];
messageModel = new MessageModel({title: 'Warning', icon: Icon.warning({size: 'lg'})});

// If not null, form will be open and display it
@observable record = null;
Expand Down Expand Up @@ -88,7 +85,6 @@ export class RestFormModel {
@action
close() {
this.originalRecord = this.record = null;
this.messageModel.close();
}

@action
Expand All @@ -112,6 +108,6 @@ export class RestFormModel {
}

destroy() {
XH.safeDestroy(this.messageModel, ...this.controlModels);
XH.safeDestroy(...this.controlModels);
}
}
4 changes: 1 addition & 3 deletions cmp/rest/RestGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {Component} from 'react';
import {HoistComponent, elemFactory} from '@xh/hoist/core';
import {grid} from '@xh/hoist/cmp/grid';
import {panel, fragment} from '@xh/hoist/cmp/layout';
import {message} from '@xh/hoist/cmp/message';

import {restGridToolbar} from './RestGridToolbar';
import {restForm} from './RestForm';
Expand All @@ -31,8 +30,7 @@ export class RestGrid extends Component {
agOptions
})
}),
restForm({model: model.formModel}),
message({model: model.messageModel})
restForm({model: model.formModel})
);
}

Expand Down
Loading