src/types/view/index.js
import Immutable, {List, Map} from 'immutable';
import {valueRenderers} from '~/renderers';
import Type from '../type';
let viewIds = 0;
/**
* The base view type. Every registered view type must eventually inherit from this.
*/
export default class ViewType extends Type {
static typeName = '';
static parseOptions(field, parseField) {
return field
.remove('type')
.update('label', label => Map.isMap(label) ?
parseField(label) :
label
);
}
/**
* Base class implementation that children can call such as:
* `super.initialize(this.getChildren())`
* If a child does not override this, it means that this function will be
* called with no arguments, which is a no-op.
*
* @param {RenderData} renderData - the render data to initialize the type to.
* @param {ViewType|List<ViewType>} children - a single view type or a list of view types to call initialize on
*/
initialize(renderData, children) {
const label = this.options.get('label');
const labelPromise = label instanceof ViewType
? valueRenderers.initialize(label, renderData)
: null;
const childrenPromises = List.isList(children)
? children
.map(child => valueRenderers.initialize(child, renderData))
.toArray()
: children
? [valueRenderers.initialize(children, renderData)]
: [];
return Promise.all([...childrenPromises, labelPromise]);
}
/**
* Creates a new instance of a view type.
* @param {object} options - Options to apply to this instance.
*/
constructor(options) {
super();
this.options = Immutable.fromJS(options || {});
this.uniqueId = viewIds++;
}
// TODO: Put default width / min width as overrideable by each sub view type.
// TODO: Separate form / table width options so that you can specify a single
// view for each.
getWidth() {
return this.options.get('width');
}
getMinWidth() {
return this.options.has('width')
? this.options.get('minWidth')
: this.options.get('minWidth', 180);
}
getDefaultFlex() {
return this.options.has('width') ? 0 : 1;
}
getFlexGrow() {
return this.options.get('flexGrow', this.getDefaultFlex());
}
getFlexShrink() {
return this.options.get('flexShrink', this.getDefaultFlex());
}
/**
* Returns a label using 1 of 3 options. If the internal label is a basic
* value, return it. If it is a view type, get its associated display value.
* If it is a function, call the function with the render data.
*
* @param {RenderData} renderData - The data to maybe generate the label from.
* @param {ViewType|string} secondLabel - A second label to use in place of the built in label.
* @return {string} They label, if any, associated with the view.
*/
getLabel(renderData, secondLabel) {
const label = secondLabel || this.options.get('label') || '';
if (label instanceof ViewType) {
return label.getDisplay(renderData);
} else if (typeof label == 'function') {
return label(renderData);
}
return label;
}
/**
* Returns display information for table based displays.
* Currently, the only display used is react-virtualized, so the options are
* entirely based on that library.
*
* For a view type to be used as a table column, it must have a string label.
* TODO: Consider allowing any labels, but pass in "dummy" render data that
* always returns empty (or default) values. In the current setup, this would
* work by just passing the data type and using `undefined` for the data
* value.
*
* If a width is supplied to the view type, the default shrink / grow factor
* is 0. Otherwise, the default factor is 1 and the default width is 100.
*
* @return {object}
*/
getTableProps(label) {
label = typeof label != 'undefined' ?
label :
(this.options.get('label') || '');
if (typeof label != 'string') {
throw new Error(`Error ${this.constructor.name}: labels must only be plain strings when used with tables.`);
}
return {
key: label,
viewType: this,
label: label,
dataKey: label,
width: this.getWidth() || 140,
minWidth: this.getMinWidth(),
flexGrow: this.getFlexGrow(),
flexShrink: this.getFlexShrink(),
filterType: 'equals',
filter: this.filter.bind(this)
};
}
filter(filterValue, rowValue) {
return filterValue == rowValue;
}
asType(Type) {
const newType = new this.constructor(this.options);
newType.constructor = Type;
return newType;
}
}