atom-user-support-helper
Version:
A helper for user support features in Atom.io
228 lines (198 loc) • 7.01 kB
JavaScript
'use babel';
let Promise = null;
let loophole = null;
let allowUnsafeEval = null;
let allowUnsafeNewFunction = null;
let pug = null;
let CompositeDisposable
let Disposable
let PromptViewList = null;
let InputInterfaces = null
let templates = {};
let initialized = false;
function initialize() {
if (initialized) return ;
// Initialize the modules
Promise = require('bluebird');
loophole = require('loophole');
allowUnsafeEval = loophole.allowUnsafeEval;
allowUnsafeNewFunction = loophole.allowUnsafeNewFunction;
const atom = require('atom')
CompositeDisposable = atom.CompositeDisposable
Disposable = atom.Disposable
// Initilaize the local modules
PromptViewList = require('./prompt-view-list')
InputInterfaces = require('./input-interfaces')
// Compile the pug files
allowUnsafeEval(() => {
allowUnsafeNewFunction(() => {
pug = require('pug');
templates.promptView = pug.compile(require('../pug/prompt-view.js'));
})
});
initialized = true;
}
const DEFAULT_WORDS = {
back: "Back",
skip: "Skip",
next: "Next",
finish: "Finish"
}
/**
* Prompt like {@link https://github.com/SBoudrias/Inquirer.js Inquirer.js}
*/
class PromptView {
constructor(prompt, words) {
initialize();
// TODO: implement 'confirm'
console.assert(
prompt.type === 'input' || prompt.type === 'list' ||
prompt.type === 'dropdown' || prompt.type === 'multipleList'
);
// Obtain the parameters from prompt
this.type = prompt.type;
this.name = prompt.name || "";
this.choices = prompt.choices
this.inputGenerator = InputInterfaces[this.type];
this.validate = prompt.validate || ((result) => { return true; })
this.map = prompt.map || ((result) => { return result; })
this.inverseMap = prompt.inverseMap || ((result) => { return result; })
// Generate a HTML
const elemString = templates.promptView({
name: this.name,
message: prompt.message || "",
detail: prompt.detail || "",
className: prompt.className || "",
words: words || DEFAULT_WORDS
});
// Generate a DOM
const dom = document.createElement("div")
dom.innerHTML = elemString;
this.element = dom.children[0];
dom.remove()
// Initialize the fields
this.updateInput(prompt.default, this.choices);
}
updateInput(defaultValue, choices) {
choices = choices || this.choices
// Update the input interface
this.interface = this.inputGenerator({
name: this.name,
choices: choices
});
const contentDom = this.element.querySelector('.user-support-helper .container .input');
const l = contentDom.children.length
for (let i = 0; i < l; i++) {
const child = contentDom.children[0]
contentDom.removeChild(child)
child.remove();
}
contentDom.appendChild(this.interface.elem)
// Set default value
if (defaultValue !== null && defaultValue !== undefined) {
this.interface.setValue(this.inverseMap(defaultValue));
}
// Set validate function
const finish = this.element.querySelector('.user-support-helper>.section-footer .prompt-finish');
const next = this.element.querySelector('.user-support-helper>.section-footer .prompt-next');
const label = this.element.querySelector('.user-support-helper>.container>.row>.label');
const updateButtons = () => {
if (this.tooltips) {
this.tooltips.dispose()
}
const retval = this.validate(this.interface.getValue());
if (retval === true) {
finish.removeAttribute('disabled');
next.removeAttribute('disabled');
label.classList.remove('label-danger');
label.classList.add('label-success');
label.innerHTML = 'valid';
} else {
finish.setAttribute('disabled', 'true');
next.setAttribute('disabled', 'true');
// Add a tooltip
const addTooltip = (elem) => {
elem.setAttribute('data-toggle', 'tooltip');
elem.setAttribute('data-placement', 'auto');
elem.setAttribute('title', retval);
return new Disposable(() => {
elem.removeAttribute('data-toggle');
elem.removeAttribute('data-placement');
elem.removeAttribute('title');
})
}
if ((typeof retval) === 'string') {
this.tooltips = new CompositeDisposable()
this.tooltips.add(addTooltip(finish))
this.tooltips.add(addTooltip(next))
this.tooltips.add(addTooltip(label))
}
label.classList.add('label-danger');
label.classList.remove('label-success');
label.innerHTML = 'invalid';
}
};
this.interface.onChange(updateButtons);
updateButtons();
}
updateMenu(isOptional, finished, started) {
// TODO: should be able to disable 'back' button
this.isOptional = isOptional;
this.finished = finished;
this.started = started
// Update the buttons
const back = this.element.querySelector('.user-support-helper>.section-footer>.prompt-back');
const skip = this.element.querySelector('.user-support-helper>.section-footer>.prompt-skip');
const finish = this.element.querySelector('.user-support-helper>.section-footer>.prompt-finish');
const next = this.element.querySelector('.user-support-helper>.section-footer>.prompt-next');
if (started) {
back.style.display = "none";
} else {
back.style.display = "block";
}
if (this.isOptional) {
skip.removeAttribute('disabled');
} else {
skip.setAttribute('disabled', 'true');
}
if (this.finished) {
finish.style.display = "block";
next.style.display = "none";
} else {
next.style.display = "block";
finish.style.display = "none";
}
}
activate() {
const back = this.getElement().querySelector('.user-support-helper>.section-footer>.prompt-back');
const skip = this.getElement().querySelector('.user-support-helper>.section-footer>.prompt-skip');
const finish = this.getElement().querySelector('.user-support-helper>.section-footer>.prompt-finish');
const next = this.getElement().querySelector('.user-support-helper>.section-footer>.prompt-next');
promise = new Promise((resolve, reject) => {
const handler = (event, dom) => {
const h = () => {
resolve({
event: event,
value: this.map(this.interface.getValue())
});
dom.removeEventListener('click', h);
};
return h;
}
back.addEventListener('click', handler('back', back));
skip.addEventListener('click', handler('skip', skip));
finish.addEventListener('click', handler('success', finish));
next.addEventListener('click', handler('success', next));
});
return promise;
}
serialize() {}
// Tear down any state and detach
destroy() {
this.element.remove();
}
getElement() {
return this.element;
}
}
export default PromptView;