Ghost/ghost/admin/app/components/gh-task-button.js
Kevin Ansfield 9344fd6a1a 🎨 refactor signin screen to use ember-concurrency & gh-task-button (#571)
refs https://github.com/TryGhost/Ghost/issues/7865
- convert all signin related actions to ember-concurrency tasks and consolidate in the signin controller rather than spread across controller+route
- add `successClass` and `failureClass` params to `gh-task-button` that can be used to override the default success/failure button classes
- prevent clicks on `gh-task-button` from triggering form actions (this behaviour should never be necessary, task buttons should either be separate to the form as in the "forgot?" button or the form action performs the same task and can be triggered by a standard form submit)
2017-03-09 14:48:54 -07:00

107 lines
3.3 KiB
JavaScript

import Component from 'ember-component';
import observer from 'ember-metal/observer';
import computed, {reads} from 'ember-computed';
import {isBlank} from 'ember-utils';
import {invokeAction} from 'ember-invoke-action';
/**
* Task Button works exactly like Spin button, but with one major difference:
*
* Instead of passing a "submitting" parameter (which is bound to the parent object),
* you pass an ember-concurrency task. All of the "submitting" behavior is handled automatically.
*
* As another bonus, there's no need to handle canceling the promises when something
* like a controller changes. Because the only task running is handled through this
* component, all running promises will automatically be cancelled when this
* component is removed from the DOM
*/
const GhTaskButton = Component.extend({
tagName: 'button',
classNameBindings: ['isRunning:appear-disabled', 'isSuccessClass', 'isFailureClass'],
attributeBindings: ['disabled', 'type', 'tabindex'],
task: null,
disabled: false,
buttonText: 'Save',
runningText: reads('buttonText'),
successText: 'Saved',
successClass: 'gh-btn-green',
failureText: 'Retry',
failureClass: 'gh-btn-red',
isRunning: reads('task.last.isRunning'),
isSuccess: computed('isRunning', 'task.last.value', function () {
if (this.get('isRunning')) {
return false;
}
let value = this.get('task.last.value');
return !isBlank(value) && value !== false;
}),
isSuccessClass: computed('isSuccess', function () {
if (this.get('isSuccess')) {
return this.get('successClass');
}
}),
isFailure: computed('isRunning', 'isSuccess', 'task.last.error', function () {
if (this.get('isRunning') || this.get('isSuccess')) {
return false;
}
return this.get('task.last.error') !== undefined;
}),
isFailureClass: computed('isFailure', function () {
if (this.get('isFailure')) {
return this.get('failureClass');
}
}),
isIdle: computed('isRunning', 'isSuccess', 'isFailure', function () {
return !this.get('isRunning') && !this.get('isSuccess') && !this.get('isFailure');
}),
click() {
// do nothing if disabled externally
if (this.get('disabled')) {
return false;
}
let task = this.get('task');
let taskName = this.get('task.name');
let lastTaskName = this.get('task.last.task.name');
// task-buttons are never disabled whilst running so that clicks when a
// taskGroup is running don't get dropped BUT that means we need to check
// here to avoid spamming actions from multiple clicks
if (this.get('isRunning') && taskName === lastTaskName) {
return;
}
invokeAction(this, 'action');
task.perform();
// prevent the click from bubbling and triggering form actions
return false;
},
setSize: observer('isRunning', function () {
if (this.get('isRunning')) {
this.$().width(this.$().width());
this.$().height(this.$().height());
} else {
this.$().width('');
this.$().height('');
}
})
});
GhTaskButton.reopenClass({
positionalParams: ['buttonText']
});
export default GhTaskButton;