file-prompt
Version:
An interactive prompt for selecting files from a directory.
210 lines (174 loc) • 5.19 kB
JavaScript
import BaseTransform from './base_transform';
import path from 'path';
/**
* Range
* Return a populated array with the values from the min to the max.
*
* @param {int} min - Minimum index value 1-index based
* @param {int} max - Up to the maximum
* @returns {array} Array of numbers from min to max
*/
function range (min, max) {
let length = max - min,
rangeArray = Array(length);
for (let idx = 0; idx <= length; idx += 1, min += 1) {
rangeArray[idx] = min;
}
return rangeArray;
}
export default class MenuTransform extends BaseTransform {
name = 'menu';
filters = {
creator: 'queries',
type: 'query',
};
menu = null;
/** Lifecycle methods */
/**
* Constructor
* Constructs the queries transform class.
*
* @constructor
* @param {object} options - Initialization options
*/
constructor (options={}) {
super(options);
if (options.menu) this.menu = options.menu;
}
/**
* Get Params
* Returns the params object to merge with our defaults
*
* @method
* @private
* @param {object} options - Constructor options
* @returns {object} Initial param values
*/
getParams (options={}) {
let canUnselect = options.canUnselect;
canUnselect = typeof canUnselect === 'undefined' ? true : canUnselect;
return {
canUnselect,
};
}
/** Helpers */
/**
* Returns a copy of ids from the menu
*
* @returns {array} Array of menu option choice ids
*/
getChoiceIds () {
return this.menu.ids().slice();
}
/**
* Is Select Only
* Determines if unselections are allowed. For instance on the main menu
* you are just selecting which action to take so -directories is an
* invalid value.
*
* @method
* @public
* @param {string} action - Given query action
* @returns {boolean} True if action was unselect and it's not allowed
*/
isSelectOnly (action) {
return action === 'unselect' && !this.params.canUnselect;
}
/**
* Validates the selected choices
*
* @param {array} ids - An array of option ids
* @returns {array} List of menu choices
*/
isValid (ids=[]) {
let isLegit = Array.isArray(ids) && ids.every(this.menu.hasId, this.menu);
return isLegit && ids && ids.length > 0;
}
/**
* Selects choices from the menu based on various query types
*
* @param {query} query - A query instance to test values of
* @param {object} params - Extra params that set constraints on queries
* @returns {array} Array of selected option ids
*/
select (query, params) {
let ids = [],
data = query.data;
if (this.isSelectOnly(query.action)) return [];
/**
* Make sure we are adding our own option args to the args we're passing
* around.
*/
Object.assign(params, { canUnselect: this.params.canUnselect });
// Determine what to do by the type of action the query is requesting
switch (data.type) {
// User has input a '*' so select all the things
case 'all':
if (params.maxQueries > 0) return [];
ids = this.getChoiceIds();
break;
// User has input a range like 1-10 so select those items.
case 'range':
// If maxQueries is set then ranges are not allowed.
if (params.maxQueries > 0) return [];
// Test against invalid values
if (!data.value.min || !data.value.max) return [];
// Error if query value.min is greater than the max
else if (data.value.min > data.value.max) return [];
// Create a list of ids in the given range
ids = range(data.value.min, data.value.max);
break;
// Case is id
case 'id':
if (!query.isInteger()) return [];
ids = [data.value];
data.type = 'single';
break;
// Query is a string type so try to find a match by the name
case 'string':
ids = this.menu.getIdByName(query.toString());
data.type = 'single';
break;
}
return ids;
}
/** Transformer */
/**
* Transform
* Transforms the incoming search string into a bunch of queries to process
*
* @method
* @public
* @param {object} transformAction - Transform action from incoming stream
*/
transform (transformAction) {
let { data: query, params } = transformAction,
choices = this.select(query, params);
/**
* If the list of choices is not valid, push a match error.
*/
if (!this.isValid(choices)) {
this.matchError(query);
return;
}
// Start with our choices array
choices
// Convert our array of ids into an array of menu option objects
.map((id) => this.menu.getChoiceById(id).value)
// Build our action objects for each choice
.map((choice) => {
let isPath = choice.slice(0, 1) === path.sep;
return {
type: isPath ? 'file' : 'page',
data: {
operation: query.data.action,
value: choice,
type: query.data.type,
},
params,
};
})
// Push each action down the stream
.forEach(this.pushAction, this);
}
}