@alihbuzaid/ember-ui
Version:
Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.
384 lines (333 loc) • 9.99 kB
JavaScript
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action, set, get, getProperties, setProperties } from '@ember/object';
import { assert } from '@ember/debug';
import { isArray } from '@ember/array';
import { defer } from 'rsvp';
const { assign } = Object;
export default class ModalsManagerService extends Service {
modalIsOpened = false;
modalDefer = null;
componentToRender = null;
options = {};
defaultOptions = {
title: null,
body: null,
footer: null,
confirmButtonDefaultText: 'Yes',
confirmButtonFulfilledText: 'Yes',
confirmButtonPendingText: 'Yes',
confirmButtonRejectedText: 'Yes',
declineButtonDefaultText: 'No',
declineButtonFulfilledText: 'No',
declineButtonPendingText: 'No',
declineButtonRejectedText: 'No',
cancel: 'Cancel',
backdrop: true,
backdropClose: true,
backdropTransitionDuration: 150,
fade: true,
keyboard: true,
position: 'top',
renderInPlace: false,
scrollable: false,
size: null,
transitionDuration: 300,
confirmIsActive: false,
confirmButtonSize: 'md',
confirmButtonType: 'primary',
confirmIconActive: '',
confirmIconInactive: '',
declineIsActive: false,
declineButtonSize: 'md',
declineButtonType: 'secondary',
declineIconActive: '',
declineIconInactive: '',
modalClass: '',
};
/**
* @throws {Error} if some modal is already opened
* @param componentToRender Component's child-class represents needed modal
* @param options options passed to the rendered modal
*/
show(componentToRender, options) {
assert('Only one modal may be opened in the same time!', !this.modalIsOpened);
const component = componentToRender;
const opts = assign({}, this.defaultOptions, options);
this.componentToRender = component;
this.modalIsOpened = true;
this.options = opts;
const modalDefer = defer();
this.modalDefer = modalDefer;
return modalDefer.promise;
}
/**
* Shows a confirmation dialog
*
* @method confirm
* @param {object} options
* @return {RSVP.Promise}
*/
confirm(options = {}) {
let modalClass = 'flb--confirm-modal modal-sm';
if (typeof options.modalClass === 'string') {
modalClass = `flb--confirm-modal ${options.modalClass}`;
}
options = assign(options, {
hideTitle: true,
modalClass,
});
return this.show('modal/layouts/confirm', options);
}
/**
* Shows a alert dialog
*/
alert(options = {}) {
let modalClass = 'flb--alert-modal modal-sm';
if (typeof options.modalClass === 'string') {
modalClass = `flb--alert-modal ${options.modalClass}`;
}
options = assign(options, {
hideTitle: true,
hideAcceptButton: true,
declineButtonText: 'OK',
modalClass,
});
return this.show('modal/layouts/alert', options);
}
/**
* Shows a prompt dialog
*/
prompt(options = {}) {
return this.show('modal/layouts/prompt', options);
}
/**
* Shows a bulk action dialog
*/
bulk(options = {}) {
return this.show('modal/layouts/bulk-action', options);
}
/**
* Shows a progress dialog
* @category Default Modals
* @throws {Error} if `options.promises` is not an array
*/
progress(options = {}) {
assert('`options.promises` must be an array', options && isArray(options.promises));
return this.show('modal/layouts/progress', options);
}
/**
* Shows a process dialog
* @category Default Modals
* @throws {Error} if `options.process` is not defined
*/
process(options = {}) {
assert('`options.process` must be defined', !!(options && options?.process));
return this.show('modal/layouts/process', options);
}
/**
* Shows a loading dialog
*
* @method confirm
* @param {object} options
* @return {RSVP.Promise}
*/
async displayLoader(options = {}) {
await this.done();
this.show('modal/layouts/loading', { title: 'Loading...', ...options });
}
/**
* Alias for showing a loading dialog
*
* @method confirm
* @param {object} options
* @return {RSVP.Promise}
*/
async loader(options = {}) {
return this.displayLoader(options);
}
/**
* Shows a dialog that allows user to select options from prompt
*
* @method confirm
* @param {object} options
* @return {RSVP.Promise}
*/
async userSelectOption(title, promptOptions = [], modalOptions = {}) {
await this.done();
return new Promise((resolve) => {
const selected = null;
this.show('modal/layouts/option-prompt', {
title,
promptOptions,
selected,
selectOption: (event) => {
const { value } = event.target;
this.setOption('selected', value);
},
confirm: () => {
this.startLoading();
const selected = this.getOption('selected');
this.done();
resolve(selected);
},
decline: () => {
this.done();
resolve(null);
},
...modalOptions,
});
});
}
/**
* Same as onClickConfirm but allows a handler to run then resolve by user
*
* @param {EbmmModalOptions} v
*/
onClickConfirmWithDone() {
const done = this.done.bind(this, this, 'onConfirm');
const { confirm, keepOpen } = this.options;
if (typeof confirm === 'function') {
const response = confirm(this, done);
// hack keep dialog open until hold is true
if (keepOpen === true) {
return;
}
if (response && typeof response.then === 'function') {
return response.finally(() => {
return done();
});
}
return;
}
return done();
}
/**
* Same as onClickDecline but allows a handler to run then resolve by user
*
* @param {EbmmModalOptions} v
*/
onClickDeclineWithDone() {
const done = this.done.bind(this, this, 'onDecline');
const { decline, keepOpen } = this.options;
if (typeof decline === 'function') {
const response = decline(this, done);
// hack keep dialog open until hold is true
if (keepOpen === true) {
return;
}
if (response && typeof response.then === 'function') {
return response.finally(() => {
return done();
});
}
return;
}
return done();
}
/**
* Closes the modal and cleans up
*
* @void
*/
done(instance, action) {
return new Promise((resolve) => {
const callback = get(this, `options.${action}`);
const onFinish = get(this, `options.onFinish`);
set(this, 'modalIsOpened', false);
this.modalDefer?.resolve(this);
this.clearOptions();
if (typeof callback === 'function') {
callback(this.options);
}
if (typeof onFinish === 'function') {
onFinish(this.options);
}
resolve(true);
});
}
/**
* Retrieves an option
*
* @param {String} key
* @param {Mixed} defaultValue
* @return {Mixed}
*/
getOption(key, defaultValue = null) {
if (isArray(key)) {
return this.getOptions(key);
}
const value = get(this.options, key);
if (value === undefined) {
return defaultValue;
}
return value;
}
/**
* Allows multiple options to be get
*
* @param {Object} options
* @void
*/
getOptions(props = []) {
if (props?.length === 0) {
return this.options ?? {};
}
return getProperties(this.options, props);
}
/**
* Updates an option in the service
*
* @param {String} key
* @param {Mixed} value
* @void
*/
setOption(key, value) {
set(this.options, key, value);
}
/**
* Allows multiple options to be updated
*
* @param {Object} options
* @void
*/
setOptions(options = {}) {
setProperties(this.options, options);
}
/**
* Executes a function passed in options
*
* @void
*/
invoke(fn, ...params) {
const callable = get(this.options, fn);
if (typeof callable === 'function') {
return callable(...params);
}
return null;
}
/**
* Alias to start loading indicator on modal
*
* @void
*/
startLoading() {
this.setOption('isLoading', true);
}
/**
* Alias to stop loading indicator on modal
*
* @void
*/
stopLoading() {
this.setOption('isLoading', false);
}
/**
* Clear modalsManager options.
*
* @memberof ModalsManagerService
*/
clearOptions() {
this.options = {};
}
}