1
0
Fork 0
mirror of https://github.com/TryGhost/Ghost-Admin.git synced 2023-12-14 02:33:04 +01:00
Ghost-Admin/app/components/gh-task-button.js
Kevin Ansfield 7eefbba69f 💄🐷 sort-imports eslint rule (#712)
no issue

- adds `eslint-plugin-sort-imports-es6-autofix` dependency
  - implements ESLint's base `sort-imports` rule but has a distinction in that `import {foo} from 'bar';` is considered `multiple` rather than `single`
  - fixes ESLint's autofix behaviour so `eslint --fix` will actually fix the sort order
- updates all unordered import rules by using `eslint --fix`

With the increased number of `import` statements since Ember+ecosystem started moving towards es6 modules I've found it frustrating at times trying to search through randomly ordered import statements. Recently I've been sorting imports manually when I've added new code or touched old code so I thought I'd add an ESLint rule to codify it.
2017-05-29 20:50:03 +02:00

146 lines
4.6 KiB
JavaScript

import Component from 'ember-component';
import computed, {reads} from 'ember-computed';
import observer from 'ember-metal/observer';
import {invokeAction} from 'ember-invoke-action';
import {isBlank} from 'ember-utils';
import {task, timeout} from 'ember-concurrency';
/**
* 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',
'isIdleClass',
'isRunningClass',
'isSuccessClass',
'isFailureClass'
],
attributeBindings: ['disabled', 'type', 'tabindex'],
task: null,
disabled: false,
buttonText: 'Save',
runningText: reads('buttonText'),
idleClass: '',
runningClass: '',
successText: 'Saved',
successClass: 'gh-btn-green',
failureText: 'Retry',
failureClass: 'gh-btn-red',
// hasRun is needed so that a newly rendered button does not show the last
// state of the associated task
hasRun: false,
isRunning: reads('task.last.isRunning'),
isIdleClass: computed('isIdle', function () {
if (this.get('isIdle')) {
return this.get('idleClass');
}
}),
isRunningClass: computed('isRunning', function () {
if (this.get('isRunning')) {
return this.get('runningClass') || this.get('idleClass');
}
}),
isSuccess: computed('hasRun', 'isRunning', 'task.last.value', function () {
if (!this.get('hasRun') || 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('hasRun', 'isRunning', 'isSuccess', 'task.last.error', function () {
if (!this.get('hasRun') || 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();
this.get('_restartAnimation').perform();
// prevent the click from bubbling and triggering form actions
return false;
},
setSize: observer('isRunning', function () {
if (this.get('isRunning')) {
this.set('hasRun', true);
// this.$().width(this.$().width());
// this.$().height(this.$().height());
} else {
// this.$().width('');
// this.$().height('');
}
}),
// when local validation fails there's no transition from failed->running
// so we want to restart the retry spinner animation to show something
// has happened when the button is clicked
_restartAnimation: task(function* () {
if (this.$('.retry-animated').length) {
// eslint-disable-next-line
let elem = this.$('.retry-animated')[0];
elem.classList.remove('retry-animated');
yield timeout(10);
elem.classList.add('retry-animated');
}
})
});
GhTaskButton.reopenClass({
positionalParams: ['buttonText']
});
export default GhTaskButton;