Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fin-hypergrid

Package Overview
Dependencies
Maintainers
2
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fin-hypergrid - npm Package Compare versions

Comparing version 0.2.8 to 1.0.0

jsdoc-tutorials/cell-editors.md

10

package.json
{
"name": "fin-hypergrid",
"version": "0.2.8",
"version": "1.0.0",
"description": "Canvas-based high-performance spreadsheet",
"repository": {
"type": "git",
"url": "git://github.com/openfin/fin-hypergrid.git"
},
"main": "src/Hypergrid.js",

@@ -17,7 +21,7 @@ "author": "SWirts, JEiten, DJones",

"extend-me": "^2.2.2",
"filter-tree": "^0.3.25",
"filter-tree": "^0.3.26",
"finbars": "^1.5.1",
"fincanvas": "^1.2.1",
"hyper-analytics": "^0.10.4",
"list-dragon": "^1.3.1",
"list-dragon": "^1.3.3",
"lru-cache": "^2.7.0",

@@ -24,0 +28,0 @@ "mustache": "^2.2.0",

@@ -13,2 +13,6 @@ /* eslint-env browser */

function deriveSchema() {
return new ColumnSchemaFactory(this.columns).schema;
}
var noExportProperties = [

@@ -40,12 +44,29 @@ 'columnHeader',

* @param {Hypergrid} grid
* @param {function|menuItem[]} [schema=derivedSchema] - Passed to behavior constructor. May be:
* * A schema array
* * A function returning a schema array. Called at filter reset time with behavior as context.
* * Omit to generate a basic schema from `this.columns`.
* @memberOf Behavior.prototype
*/
initialize: function(grid) { //formerly installOn
this.setGrid(grid);
initialize: function(grid, schema) {
/**
* @type {Hypergrid}
* @memberOf Behavior.prototype
*/
this.grid = grid;
this.schema = schema || deriveSchema;
/**
* @type {DataModel}
* @memberOf Behavior.prototype
*/
this.dataModel = this.getNewDataModel();
grid.setBehavior(this);
this.reset();
this.initializeFeatureChain(grid);
this.cellProvider = this.createCellProvider();
this.renderedColumnCount = 30;
this.renderedRowCount = 60;
this.dataUpdates = {}; //for overriding with edit values;
},

@@ -60,63 +81,33 @@

var self = this;
/**
* @summary Hash of feature class names.
* @desc Built here but otherwise not in use.
* @type {object}
* @memberOf Behavior.prototype
*/
this.featureMap = {};
this.features.forEach(function(FeatureConstructor) {
self.setNextFeature(new FeatureConstructor);
var newFeature = new FeatureConstructor;
self.featureMap[newFeature.$$CLASS_NAME] = newFeature;
if (self.featureChain) {
self.featureChain.setNext(newFeature);
} else {
/**
* @summary Controller chain of command.
* @desc Each feature is linked to the next feature.
* @type {Feature}
* @memberOf Behavior.prototype
*/
self.featureChain = newFeature;
}
});
this.featureChain.initializeOn(grid);
if (this.featureChain) {
this.featureChain.initializeOn(grid);
}
},
features: [], // in case implementing class has no features TODO: Will this ever happen?
features: [], // override in implementing class unless no features
/**
* memento for the user configured visual properties of the table
* @type {object}
* @memberOf Behavior.prototype
*/
tableState: null,
/**
* @type {Hypergrid}
* @memberOf Behavior.prototype
*/
grid: null,
/**
* list of default cell editor names
* @type {string[]}
* @memberOf Behavior.prototype
*/
editorTypes: [
'choice',
'textfield',
'color',
'slider',
'spinner',
'date'
],
/**
* controller chain of command
* @type {object}
* @memberOf Behavior.prototype
*/
featureChain: null,
dataModel: null,
baseModel: null,
scrollPositionX: 0,
scrollPositionY: 0,
featureMap: {},
/**
* @type {Column[]}
*/
allColumns: [],
/**
* @type {Column[]}
*/
columns: [],
reset: function() {

@@ -127,2 +118,3 @@ this.cellProvider = this.createCellProvider();

this.dataUpdates = {}; //for overriding with edit values;
this.scrollPositionX = this.scrollPositionY = 0;
this.clearColumns();

@@ -135,4 +127,14 @@ this.clearState();

clearColumns: function() {
/**
* @type {Column[]}
* @memberOf Behavior.prototype
*/
this.columns = [];
/**
* @type {Column[]}
* @memberOf Behavior.prototype
*/
this.allColumns = [];
this.columns[-1] = this.newColumn(-1, '');

@@ -252,5 +254,2 @@ this.columns[-2] = this.newColumn(-2, 'Tree');

properties.columnHeader = Object.create(properties, {
format: {
value: 'default'
},
font: {

@@ -493,7 +492,7 @@ configurable: true,

getColumnWidth: function(x) {
var col = this.getColumn(x);
if (!col) {
var column = this.getColumn(x);
if (!column) {
return this.resolveProperty('defaultColumnWidth');
}
var width = col.getWidth();
var width = column.getWidth();
return width;

@@ -569,2 +568,7 @@ },

clearState: function() {
/**
* memento for the user configured visual properties of the table
* @type {object}
* @memberOf Behavior.prototype
*/
this.tableState = null;

@@ -583,3 +587,2 @@ },

rowHeights: {},
cellProperties: {},
columnProperties: []

@@ -612,16 +615,7 @@ });

this.createColumns();
this.setColumnOrder(memento.columnIndexes);
this._setColumnOrder(memento.columnIndexes);
_(state).extendOwn(memento);
this.setAllColumnProperties(colProperties);
memento.columnProperties = colProperties;
//memento.columnProperties = colProperties;
// this.dataModel.setState(memento);
// var self = this;
// requestAnimationFrame(function() {
// self.applySorts();
// self.changed();
// self.stateChanged();
// });
//just to be close/ it's easier on the eyes

@@ -641,3 +635,6 @@ this.setColumnWidth(-1, 24.193359375);

setColumnOrder: function(indexes) {
_setColumnOrder: function(indexes) {
if (!Array.isArray(indexes)){
return;
}
if (!indexes) {

@@ -653,6 +650,2 @@ this.columns.length = 0;

applySorts: function() {
//if I have sorts, apply them now//
},
/**

@@ -688,16 +681,2 @@ * @memberOf Behavior.prototype

/**
* @memberOf Behavior.prototype
* @desc add nextFeature to me If I don't have a next node, otherwise pass it along
* @param {Feature}
*/
setNextFeature: function(nextFeature) {
this.featureMap[nextFeature.$$CLASS_NAME] = nextFeature;
if (this.featureChain) {
this.featureChain.setNext(nextFeature);
} else {
this.featureChain = nextFeature;
}
},
lookupFeature: function(key) {

@@ -722,7 +701,2 @@ return this.featureMap[key];

setGrid: function(grid) {
this.grid = grid;
this.dataModel = this.dataModel || this.getDefaultDataModel();
this.dataModel.setGrid(grid);
grid.setBehavior(this);
this.clearColumns();
},

@@ -793,3 +767,4 @@

getCellProperties: function(x, y) {
return this.getColumn(x).getCellProperties(y);
var column = this.allColumns[x];
return column && column.getCellProperties(y);
},

@@ -805,5 +780,5 @@

setCellProperties: function(x, y, value) {
var col = this.getColumn(x);
if (col) {
col.setCellProperties(y, value);
var column = this.allColumns[x];
if (column) {
column.setCellProperties(y, value);
}

@@ -1006,3 +981,2 @@ },

onTap: function(grid, event) {
if (this.featureChain) {

@@ -1181,7 +1155,7 @@ this.featureChain.handleTap(grid, event);

getColumnProperties: function(columnIndex) {
var col = this.columns[columnIndex];
if (!col) {
var column = this.columns[columnIndex];
if (!column) {
return isNull;
}
var properties = col.getProperties(); //TODO: returns `null` on Hypergrid.prototype.reset();
var properties = column.getProperties(); //TODO: returns `null` on Hypergrid.prototype.reset();
if (!properties) {

@@ -1194,3 +1168,4 @@ return isNull;

setColumnProperties: function(columnIndex, properties) {
var columnProperties = this.allColumns[columnIndex].getProperties();
var column = this.allColumns[columnIndex];
var columnProperties = column.getProperties();
_(columnProperties).extendOwn(properties);

@@ -1201,2 +1176,19 @@ this.changed();

/**
* Clears all cell properties of given column or of all columns.
* @param {number} [columnIndex] - Omit for all columns.
*/
clearAllCellProperties: function(columnIndex) {
if (columnIndex === undefined) {
for (var i = this.allColumns.length - 1; i >= 0; --i) {
this.allColumns[i].clearAllCellProperties();
}
} else {
var column = this.allColumns[i];
if (column) {
column.clearAllCellProperties();
}
}
},
/**
* @memberOf Behavior.prototype

@@ -1221,18 +1213,14 @@ * @return {string} The field at `colIndex`.

* @memberOf Behavior.prototype
* @desc this is called by the column editor post closing; rebuild the column order indexes
* @param {Array} list - list of column objects from the column editor
* @desc Rebuild the column order indexes
* @param {Array} columnIndexes - list of column indexes
* @param {Boolean} [silent=false] - whether to trigger column changed event
*/
setColumnDescriptors: function(lists) {
//assumes there is one row....
var visible = lists.visible;
setColumnIndexes: function(columnIndexes, silent) {
var tableState = this.getPrivateState();
var columnCount = visible.length;
var indexes = [];
var i;
for (i = 0; i < columnCount; i++) {
indexes.push(visible[i].id);
this._setColumnOrder(columnIndexes);
tableState.columnIndexes = columnIndexes;
this.changed();
if (!silent) {
this.grid.fireSyntheticOnColumnsChangedEvent();
}
tableState.columnIndexes = indexes;
this.changed();
},

@@ -1413,2 +1401,6 @@

setScrollPositionX: function(x) {
/**
* @memberOf Behavior.prototype
* @type {number}
*/
this.scrollPositionX = x;

@@ -1427,2 +1419,6 @@ },

setScrollPositionY: function(y) {
/**
* @memberOf Behavior.prototype
* @type {number}
*/
this.scrollPositionY = y;

@@ -1442,17 +1438,8 @@ },

*/
_getCellEditorAt: function(x, y, isDblClick) {
var column = this.getColumn(x);
getCellEditorAt: function(x, y) {
return this.grid.isFilterRow(y)
? this.grid.createCellEditor('combobox')
: column.getCellEditorAt(x, y) || this.grid.createCellEditor(column.getType());
? this.grid.createCellEditor('filterbox')
: this.getColumn(x).getCellEditorAt(y);
},
getCellEditorAt: function(x, y) {
if (this.grid.isFilterRow(y)) {
return this.grid.cellEditors.textfield;
}
return this.dataModel.getCellEditorAt(x, y);
},
/**

@@ -1482,7 +1469,4 @@ * @memberOf Behavior.prototype

*/
cellPropertiesPrePaintNotification: function(cellProperties) {
var row = this.getRow(cellProperties.y);
var columnId = this.getHeader(cellProperties.x);
cellProperties.row = row;
cellProperties.columnId = columnId;
cellPropertiesPrePaintNotification: function(cell) {
},

@@ -1631,13 +1615,13 @@

getNewFilter: function(options) {
getNewFilter: function() {
var newFilter;
if (this.columns.length) {
options = options || {};
if (!options.schema) {
var factory = new ColumnSchemaFactory(this.columns);
options.schema = factory.schema;
}
options.caseSensitiveColumnNames = this.grid.resolveProperty('filterCaseSensitiveColumnNames');
options.resolveAliases = this.grid.resolveProperty('filterResolveAliases');
var options = {
schema: typeof this.schema === 'function' ? this.schema(this.columns) : this.schema,
caseSensitiveColumnNames: this.grid.resolveProperty('filterCaseSensitiveColumnNames'),
resolveAliases: this.grid.resolveProperty('filterResolveAliases'),
defaultColumnFilterOperator: this.grid.resolveProperty('filterDefaultColumnFilterOperator')
};
newFilter = new DefaultFilter(options);
newFilter.loadColumnPropertiesFromSchema(this.columns);
}

@@ -1644,0 +1628,0 @@ return newFilter;

@@ -6,2 +6,3 @@ /* eslint-env browser */

var _ = require('object-iterators');
var localization = require('../lib/localization');

@@ -19,2 +20,3 @@ /**

this.label = label;
this.cellProperties = [];
}

@@ -51,9 +53,13 @@

getCellProperties: function(y) {
return this.behavior.getPrivateState().cellProperties[this.index + ',' + y];
return this.cellProperties[y];
},
setCellProperties: function(y, value) {
this.behavior.getPrivateState().cellProperties[this.index + ',' + y] = value;
this.cellProperties[y] = value;
},
clearAllCellProperties: function() {
this.cellProperties.length = 0;
},
checkColumnAutosizing: function(force) {

@@ -135,3 +141,3 @@ var properties = this.getProperties();

setProperties: function(properties) {
var current = this.behavior.getPrivateState().columnProperties[this.index];
var current = this.getProperties();
this.clearObjectProperties(current, false);

@@ -145,3 +151,7 @@ _(current).extendOwn(properties);

getCellEditorAt: function(x, y) {
unSort: function(deferred) {
this.dataModel.unSortColumn(this.index, deferred);
},
getCellEditorAt: function(y) {
return this.dataModel.getCellEditorAt(this.index, y);

@@ -156,2 +166,6 @@ },

return this.dataModel.getFields()[this.index];
},
getFormatter: function() {
return localization.get(this.getProperties().format).localize;
}

@@ -158,0 +172,0 @@ };

@@ -21,6 +21,9 @@ 'use strict';

* @param grid - the hypergrid
* @param {object[]} dataRows - array of uniform data objects
* @param {undefined|function|menuItem[]} schema - Already consumed by Behavior's {@link Behavior#initialize|initialize}.
* @param {object[]} dataRows - May be:
* * An array of congruent raw data objects
* * A function returning same
* @memberOf behaviors.JSON.prototype
*/
initialize: function(grid, dataRows) {
initialize: function(grid, schema, dataRows) {
this.setData(dataRows);

@@ -63,3 +66,3 @@ },

getDefaultDataModel: function() {
getNewDataModel: function() {
return new DataModelJSON(this.grid);

@@ -88,2 +91,10 @@ },

},
/**
* @memberOf behaviors.JSON.prototype
* @return {string} The field at `colIndex`.
* @param {number} colIndex - the column index of interest
*/
getField: function(colIndex) {
return colIndex === -1 ? 'tree' : this.getColumnFromFullList(colIndex).getField();
},

@@ -213,3 +224,7 @@ /**

},
getColumnFromFullList: function(x) {
return this.allColumns[x];
},
getRowSelectionMatrix: function(selectedRows) {

@@ -270,2 +285,16 @@ return this.dataModel.getRowSelectionMatrix(selectedRows);

return this.grid.selectionModel.getSelections();
},
getSortedColumnIndexes: function(){
return this.dataModel.getSortedColumnIndexes();
},
sortChanged: function(hiddenColumns){
var dirty = removeHiddenColumns(
this.getSortedColumnIndexes(),
(hiddenColumns || this.getHiddenColumns())
);
if (dirty){
this.applyAnalytics();
}
}

@@ -275,2 +304,21 @@

//Logic to moved to adapter layer outside of Hypergrid Core
function removeHiddenColumns(oldSorted, hiddenColumns){
var dirty = false;
oldSorted.forEach(function(i) {
var j = 0,
colIndex;
while (j < hiddenColumns.length) {
colIndex = hiddenColumns[j].index + 1; //hack to get around 0 index
if (colIndex === i) {
hiddenColumns[j].unSort();
dirty = true;
break;
}
j++;
}
});
return dirty;
}
module.exports = JSON;

@@ -6,3 +6,5 @@ /* eslint-env browser */

var mustache = require('mustache');
var Base = require('../lib/Base');
var localization = require('../lib/localization');

@@ -16,6 +18,73 @@ var extract = /\/\*\s*([^]+?)\s+\*\//; // finds the string inside the /* ... */; the (group) excludes the whitespace

initialize: function(grid) {
initialize: function(grid, localizer) {
/**
* my instance of hypergrid
* @type {Hypergrid}
* @memberOf CellEditor.prototype
*/
this.grid = grid;
if (localizer) {
this.localizer = localizer;
}
this.editorPoint = {
x: 0,
y: 0
};
this.reset();
var self = this;
this.el.addEventListener('keyup', this.keyup.bind(this));
this.el.addEventListener('keydown', function(e) { grid.fireSyntheticEditorKeyDownEvent(self, e); });
this.el.addEventListener('keypress', function(e) { grid.fireSyntheticEditorKeyPressEvent(self, e); });
this.el.onblur = function(e) { self.cancelEditing(); };
},
reset: function() {
var container = document.createElement('DIV');
container.innerHTML = this.getHTML();
/**
* This object's input control, one of:
* * *input element* - an `HTMLElement` that has a `value` attribute, such as `HTMLInputElement`, `HTMLButtonElement`, etc.
* * *container element* - an `HTMLElement` containing one or more input elements, only one of which contains the editor value.
*
* For access to the input control itself (which may or may not be the same as `this.el`), see `this.input`.
*
* @type {HTMLElement}
* @default null
* @memberOf CellEditor.prototype
*/
this.el = container.firstChild;
this.input = this.el;
},
specialKeyups: {
//0x08: 'clearStopEditing', // backspace
0x09: 'stopEditing', // tab
0x0d: 'stopEditing', // return/enter
0x1b: 'cancelEditing' // escape
},
keyup: function(e) {
if (e) {
var specialKeyup = this.specialKeyups[e.keyCode];
if (specialKeyup) {
e.preventDefault();
this[specialKeyup]();
this.grid.repaint();
this.grid.takeFocus();
}
this.grid.fireSyntheticEditorKeyUpEvent(this, e);
}
},
localizer: localization.get(null),
/**

@@ -40,18 +109,2 @@ * the point that I am editing at right now

/**
* my instance of hypergrid
* @type {Hypergrid}
* @default null
* @memberOf CellEditor.prototype
*/
grid: null,
/**
* the value before editing
* @type {type}
* @default null
* @memberOf CellEditor.prototype
*/
initialValue: null,
/** @deprecated Use `.grid.behavior` property instead.

@@ -82,4 +135,4 @@ * @memberOf CellEditor.prototype

/**
* @desc turn on checkEditorPositionFlag boolean field
* @memberOf CellEditor.prototype
* @desc turn on checkEditorPositionFlag boolean field
*/

@@ -92,15 +145,51 @@ setCheckEditorPositionFlag: function() {

* @memberOf CellEditor.prototype
* @desc move the editor to the current editor point
*/
moveEditor: function() {
var editorPoint = this.getEditorPoint();
var cellBounds = this.grid._getBoundsOfCell(editorPoint.x, editorPoint.y);
//hack to accommodate bootstrap margin issues...
var xOffset =
this.grid.div.getBoundingClientRect().left -
this.grid.divCanvas.getBoundingClientRect().left;
cellBounds.x -= xOffset;
this.setBounds(cellBounds);
},
/**
* @desc begin editing at location point
* @param {Point} point - the location to start editing at
* @memberOf CellEditor.prototype
*/
beginEditAt: function(point) {
this.editorPoint = point;
if (!this.isAdded) {
this.isAdded = true;
this.attachEditor();
}
this.setEditorPoint(point);
var value = this.grid.behavior.getValue(point.x, point.y);
if (value instanceof Array) {
value = value[1]; //it's a nested object
}
if (this.grid.fireRequestCellEdit(point, value)) {
this.initialValue = value;
this.setCheckEditorPositionFlag();
this.checkEditor();
}
},
/**
* @memberOf CellEditor.prototype
* @desc put value into our editor
* @param {object} value - whatever value we want to edit
* @memberOf CellEditor.prototype
*/
setEditorValue: function(value) {},
setEditorValue: function(value) {
this.input.value = this.localizer.localize(value);
},

@@ -130,3 +219,5 @@ /**

*/
showEditor: nullPattern,
showEditor: function() {
this.el.style.display = 'inline';
},

@@ -137,3 +228,5 @@ /**

*/
hideEditor: nullPattern,
hideEditor: function() {
this.el.style.display = 'none';
},

@@ -145,7 +238,8 @@ /**

stopEditing: function() {
if (this.grid.fireSyntheticEditorDataChangeEvent(this, this.initialValue, this.getEditorValue())) {
this.saveEditorValue();
var value = this.getEditorValue();
if (this.grid.fireSyntheticEditorDataChangeEvent(this, this.initialValue, value)) {
this.saveEditorValue(value);
this.hideEditor();
this.grid.cellEditor.el.remove();
this.grid.cellEditor = null;
this.el.remove();
}

@@ -155,33 +249,106 @@ },

cancelEditing: function() {
this.hideEditor();
this.grid.cellEditor.el.remove();
this.grid.cellEditor = null;
if (this.grid.cellEditor) { // because stopEditing's .remove triggers blur which comes here
this.setEditorValue(this.initialValue);
this.hideEditor();
this.grid.cellEditor = null;
this.el.remove();
}
},
/**
* @desc save the new value into the behavior (model)
* @memberOf CellEditor.prototype
* @desc save the new value into the behavior(model)
*/
saveEditorValue: nullPattern,
saveEditorValue: function(value) {
var point = this.getEditorPoint();
if (
!(value && value === this.initialValue) && // data changed
this.grid.fireBeforeCellEdit(point, this.initialValue, value, this) // not aborting
) {
this.grid.behavior.setValue(point.x, point.y, value);
this.grid.fireAfterCellEdit(point, this.initialValue, value, this);
}
},
/**
* @desc return the current editor's value
* @returns {object} the current editor's value
* @memberOf CellEditor.prototype
* @desc return the current editor's value
*/
getEditorValue: nullPattern,
getEditorValue: function() {
return this.localizer.standardize(this.input.value);
},
/**
* @summary Request focus for my input control.
* @desc See GRID-95 "Scrollbar moves inward" for issue and work-around explanation.
* @memberOf CellEditor.prototype
* @desc request focus
*/
takeFocus: nullPattern,
takeFocus: function() {
var self = this;
setTimeout(function() {
var input = self.el,
transformWas = input.style.transform;
input.style.transform = 'translate(0,0)'; // work-around: move to upper left
self.input.focus();
self.selectAll();
input.style.transform = transformWas;
});
},
/**
* @memberOf CellEditor.prototype
* @desc select everything
*/
selectAll: nullPattern,
/**
* @memberOf CellEditor.prototype
* @desc set the bounds of my input control
* @param {rectangle} rectangle - the bounds to move to
*/
setBounds: function(cellBounds) {
var input = this.el;
input.style.transform = translate(
cellBounds.x - 1,
cellBounds.y - 1
);
input.style.width = px(cellBounds.width + 2);
input.style.height = px(cellBounds.height + 2);
},
/**
* @desc check that the editor is in the correct location, and is showing/hidden appropriately
* @memberOf CellEditor.prototype
*/
checkEditor: function() {
if (this.checkEditorPositionFlag) {
this.checkEditorPositionFlag = false;
var editorPoint = this.getEditorPoint();
if (this.grid.isDataVisible(editorPoint.x, editorPoint.y)) {
this.setEditorValue(this.initialValue);
this.attachEditor();
this.moveEditor();
this.takeFocus();
this.showEditor();
} else {
this.hideEditor();
}
}
},
attachEditor: function() {
var input = this.el,
div = this.grid.div,
referenceNode = div.querySelectorAll('.finbar-horizontal, .finbar-vertical');
div.insertBefore(input, referenceNode.length ? referenceNode[0] : null);
},
/** @deprecated Use `.grid` property instead. */

@@ -192,8 +359,4 @@ getGrid: function() {

template: function() {
/*
template: function() {/**/},
*/
},
getHTML: function() {

@@ -207,3 +370,6 @@ var template = this.template.toString().match(extract)[1];

function nullPattern() {}
function px(n) { return n + 'px'; }
function translate(x, y) { return 'translate(' + px(x) + ',' + px(y) + ')'; }
CellEditor.abstract = true; // don't instantiate directly

@@ -210,0 +376,0 @@

'use strict';
var Simple = require('./Simple');
var CellEditor = require('./CellEditor');

@@ -8,7 +8,7 @@ /**

*/
var Color = Simple.extend('Color', {
var Color = CellEditor.extend('Color', {
template: function() {
/*
<input id="editor" type="color">
<input type="color">
*/

@@ -15,0 +15,0 @@ }

'use strict';
var Simple = require('./Simple');
var CellEditor = require('./CellEditor');

@@ -8,3 +8,3 @@ /**

*/
var Combo = Simple.extend('Combo', {
var Combo = CellEditor.extend('Combo', {

@@ -11,0 +11,0 @@ /**

// ComboBox.js - A combo-box is a combination of a text-box and a drop-down.
// User may type into it and/or select an item from the drop-down (by clicking on the triangle at the right).
// The drop-down has sections which are toggled from a control area between the text-box and the drop-down.

@@ -8,10 +9,7 @@ /* eslint-env browser */

var onTransitionEnd = require('../lib/queueless');
var prototype = require('./Simple').prototype;
var Textfield = require('./Textfield');
var prototype = require('./CellEditor').prototype;
var Queueless = require('../lib/queueless');
var elfor = require('../lib/elfor');
var Textfield = require('./Textfield');
var popMenu = require('pop-menu'); //temp
/*********************************/

@@ -23,60 +21,34 @@ /* eslint-disable no-unused-vars */

var modes = {
operators: {
label: 'Conjunctions',
selector: 'optgroup:not([class])' // all optgroups with no value for class attribute
},
var stateToActionMap = {
hidden: slideDown,
visible: slideUp
};
distinctValues: {
label: 'Distinct Values',
selector: 'optgroup.submenu-distinctValues',
appendOptions: function(optgroup) {
// get the distinct column values and sort them
var distinct = {},
d = [],
columnName = this.columnName;
/**
* A combo box is a text box that also has a drop-down containing options. The drop-down consists of an actual drop-down list (a `<select>` list) plus a _control area_ above it containing toggles. The toggles control the visibility of the various "mode lists."
* @constructor
*/
var ComboBox = Textfield.extend('ComboBox', {
this.grid.behavior.getData().forEach(function(dataRow) {
var val = dataRow[columnName];
distinct[val] = (distinct[val] || 0) + 1;
});
initialize: function() {
var el = this.el;
for (var key in distinct) {
d.push(key);
}
this.input = el.querySelector('input');
this.dropper = el.querySelector('span');
this.options = el.querySelector('div');
this.controls = this.options.querySelector('div');
this.dropdown = this.options.querySelector('select');
d.sort().forEach(function(val) {
var option = new Option(val + ' (' + distinct[val] + ')', val);
optgroup.appendChild(option);
});
this.controllable = this.modes.length > 1;
return d.length;
}
// set up a transition end controller
this.optionsTransition = new Queueless(this.options, this);
// wire-ups
this.dropper.addEventListener('mousedown', this.toggleDropDown.bind(this));
this.dropdown.addEventListener('mousewheel', function(e) { e.stopPropagation(); });
this.dropdown.addEventListener('change', this.insertText.bind(this));
el.onblur = null; // void this one, set by super's initialize
},
columnNames: {
label: 'Column Names',
selector: 'optgroup.submenu-columnNames',
appendOptions: function(optgroup) {
var columns = this.grid.behavior.columns,
x = this.editorPoint.x;
columns.forEach(function(column, index) {
if (index !== x) {
var name = column.getField(),
option = new Option(name);
option.title = '[' + name + ']\r"' + column.getHeader() + '"';
optgroup.appendChild(option);
}
});
return columns.length;
}
}
};
/**
* @constructor
*/
var ComboBox = Textfield.extend('ComboBox', {
template: function() {

@@ -88,7 +60,3 @@ /*

<div>
<div>
<span class="toggle-mode-operators" title="Toggle operators"></span>
<span class="toggle-mode-distinctValues" title="Toggle distinct values"></span>
<span class="toggle-mode-columnNames" title="Toggle column names"></span>
</div>
<div></div>
<select size="12"></select>

@@ -100,48 +68,48 @@ </div>

/**
* A combo box is a text box that also has a drop-down containing options. THe drop-down consists of an actual drop-down list (a `<select>` list) and a _control area_ containing buttons.
*
* The select list consists of the following sets of drop-down items:
* * `operators` (icon *&lt;*) The particular selection of operators for this column. Comes from the filter tree.
* * `distinctValues` (icon *#*) List of distinct column values. Calculated from inspection of column values on _and_ on icon click.
* * `columnNames` (icon *T*) List other column names. Calculated from inspection of column values on _and_ on icon click.
*
* The control area reflects the `modes` array (above). It is modeled by a 'menuModes` object, a hash with boolean properties representing the state of each of the sets of menu items outlined above. Missing properties are falsy by implication. The state semantics are:
* * `1` or `true` means adds CSS class `active` to icon _and_ shows set's items in drop-down.
* * `0` or `false` means removes (CSS class `active` from icon _and_ hides set's items in drop-down.
*
* *Persisting changes:* The only change this UI supports (besides the filter text itself) is the menu mode states, which are expected to be "sticky." That is, they are "persisted" (written back) to the filter. However, there is a problem: When the column filter is blank it doesn't actually exist yet in the filter, so there is nowhere to save it. The solution is to read the `menuModes` hash _from_ the filter tree but don't modify it until end of editing. Reading it from the filter tree picks up previous setting if there was an extant column filter or the default if there was not. But then, rather than modifying this structure (because it might be the default and we don't want to overwrite that), we hang a proxy copy off the behavior's column object for this column. This will persist it for the duration of the app session. At end of editing, if and only if there is now a column filter (text is not blank), we copy it to the column filter's subtree node in the filter tree.
*/
showEditor: function() {
var filter = this.grid.getGlobalFilter(),
column = this.column = this.grid.behavior.columns[this.editorPoint.x],
columnName = this.columnName = column.getField(),
modeNames = Object.keys(modes),
self = this;
beginEditAt: function(point) {
this.column = this.grid.behavior.columns[point.x];
this.menuModesSource = this.column.menuModes || { distinctValues: true };
prototype.beginEditAt.call(this, point);
},
// look in the filter, under column filters, for a column filter for this column
var columnFilters = this.grid.getGlobalFilter().columnFilters,
columnFilterSubtree = filter.getColumnFilter(columnName);
modes: [
{
name: 'distinctValues',
appendOptions: function(optgroup) {
// get the distinct column values and sort them
var distinct = {},
d = [],
columnName = this.column.getField(),
formatter = this.column.getFormatter();
// get the column filter's `operators` list
var columnSchema = filter.schema.lookup(columnName), // as column filter may not yet exist, refer to it's schema
opMenu = columnSchema && ( // schema should exist
columnSchema.opMenu || // pull operator list from column schema if it has one; IF it doesn't...
columnSchema.type && // BUT it has a type...
filter.typeOpMenu && // AND the filter has a defined type-operator map...
filter.typeOpMenu[columnSchema.type] // THEN use the operator list for the column's type if there is one
) || // if both of above strategies fail...
filter.treeOpMenu; // use the default operator list (which itself defaults to `Conditionals.defaultOpMenu`)
this.grid.behavior.getData().forEach(function(dataRow) {
var val = formatter(dataRow[columnName]);
distinct[val] = (distinct[val] || 0) + 1;
});
// get the column filter's `menuModes` object -- contains the states of the drop-down option icons
var menuModesSource =
column.menuModes || // (1) use proxy from last time (editing ended without a column filter to put in the filter tree)
columnFilterSubtree && columnFilterSubtree.menuModes || // (2) use column filter's `menuModes` WHEN available
columnSchema && columnSchema.menuModes || // ELSE (3) use column schema's `menuModes` when defined
columnFilters.menuModes; // ELSE (4) use the filter default (which itself defaults to operators ON, others OFF; see definition at top of DefaultFilter.js)
for (var key in distinct) {
d.push(key);
}
var menuModes = this.menuModes = column.menuModes = {};
while (optgroup.firstElementChild) {
optgroup.firstElementChild.remove();
}
d.sort().forEach(function(val) {
var option = new Option(val + ' (' + distinct[val] + ')', val);
optgroup.appendChild(option);
});
return d.length;
}
}
],
showEditor: function() {
var menuModesSource = this.menuModesSource,
menuModes = this.menuModes = {};
// build the proxy
modeNames.forEach(function(modeName) {
this.modes.forEach(function(mode) {
var modeName = mode.name;
if (modeName in menuModesSource) {

@@ -152,127 +120,76 @@ menuModes[modeName] = menuModesSource[modeName];

// override the template's empty drop-down with a new one built from opMenu (the column's operator list)
var olddrop = this.dropdown,
dropdown = this.dropdown = popMenu.build(olddrop, opMenu, {
//group: function(groupName) { return FilterTree.Conditionals.groups[groupName]; },
prompt: null
});
// wire-ups
if (this.controllable) {
this.controls.addEventListener('click', onModeIconClick.bind(this));
}
// locate the mode icon container element
var modesContainer = this.options.querySelector('.toggle-mode-operators').parentElement;
// set the initial state of the mode toggles
this.modes.forEach(function(mode) {
// create a toggle
var toggle = document.createElement('span');
if (this.controllable) {
toggle.className = TOGGLE_MODE_PREFIX + mode.name;
toggle.title = 'Toggle ' + (mode.label || mode.name).toLowerCase();
toggle.textContent = mode.symbol;
}
this.controls.appendChild(toggle);
// set the initial state of the mode toggles
modeNames.forEach(function(modeName) {
// create and label a new optgroup
var optgroup = document.createElement('optgroup');
optgroup.label = modes[modeName].label;
// wire-ups
modesContainer.addEventListener('click', onModeIconClick.bind(self));
// build the optgroup
if (modeName === 'operators') {
// Miscellaneous operator optgroups come along with the menu build above and vary per column.
// This list of conjunctions is an extra and is for all columns. All operator optgroups are classless.
optgroup.appendChild(new Option('and', ' and '));
optgroup.appendChild(new Option('or', ' or '));
optgroup.appendChild(new Option('nor', ' nor '));
} else {
optgroup.className = 'submenu-' + modeName;
if (mode.selector) {
var optgroup = document.createElement('optgroup');
optgroup.label = mode.label;
optgroup.className = 'submenu-' + mode.name;
optgroup.style.backgroundColor = mode.backgroundColor;
this.dropdown.add(optgroup);
}
dropdown.add(optgroup);
setModeIconAndOptgroup.call(this, toggle, mode.name, menuModes[mode.name]);
}.bind(this));
var className = '.' + TOGGLE_MODE_PREFIX + modeName,
ctrl = modesContainer.querySelector(className),
modeState = menuModes[modeName];
setModeIconAndOptgroup.call(self, ctrl, modeName, modeState);
});
dropdown.size = olddrop.size;
this.el.replaceChild(dropdown, olddrop);
prototype.showEditor.call(this);
},
/**
* Write the `menuModes` proxy to the filter tree's column filter subtree node.
* We look up the node again here because it might be new; or may have been deleted & recreated during editing.
*/
hideEditor: function() {
// look in the filter, under column filters, for a column filter for this column
var filter = this.grid.getGlobalFilter(),
columnName = this.columnName,
columnFilterSubtree = filter.getColumnFilter(columnName);
if (columnFilterSubtree) {
columnFilterSubtree.menuModes = this.column.menuModes;
delete this.column.menuModes;
}
// this is where you would persist this.menuModes
prototype.hideEditor.call(this);
},
initialize: function() {
var el = this.el;
this.input = el.querySelector('input');
this.dropper = el.querySelector('span');
this.options = el.querySelector('div');
this.dropdown = this.options.querySelector('select');
this.transit = onTransitionEnd(this.options, 'options', this);
// wire-ups
this.dropper.addEventListener('mousedown', toggleDropDown.bind(this));
this.dropdown.addEventListener('mousewheel', function(e) { e.stopPropagation(); });
this.dropdown.addEventListener('change', insertText.bind(this));
el.onblur = null; // void this one, set by super's initialize
toggleDropDown: function() {
if (!this.optionsTransition.transitioning) {
var state = window.getComputedStyle(this.dropdown).visibility;
stateToActionMap[state].call(this);
}
},
/* following moved to bottom of file because extend-me does not properly accept getters yet :(
insertText: function(e) {
// replace the input text with the drop-down text
this.input.focus();
this.input.value = this.dropdown.value;
this.input.setSelectionRange(0, this.input.value.length);
get input() {
return this.el.firstElementChild;
},
*/
keyup: function(e) {
if (e) {
prototype.keyup.call(this, e);
if (this.grid.isFilterRow(this.getEditorPoint().y)) {
if (this.grid.resolveProperty('filteringMode') === 'immediate') {
this.saveEditorValue();
this._moveEditor();
}
}
}
// close the drop-down
this.toggleDropDown();
}
});
var stateToActionMap = {
hidden: slideDown,
visible: slideUp
};
function onModeIconClick(e) {
var ctrl = e.target;
// extract the mode name from the toggle control's class name
var modeClassName = Array.prototype.find.call(ctrl.classList, function(className) {
return className.indexOf(TOGGLE_MODE_PREFIX) === 0;
}),
modeName = modeClassName.substr(TOGGLE_MODE_PREFIX.length);
if (ctrl.tagName === 'SPAN') {
// extra ct the mode name from the toggle control's class name
var modeClassName = Array.prototype.find.call(ctrl.classList, function(className) {
return className.indexOf(TOGGLE_MODE_PREFIX) === 0;
}),
modeName = modeClassName.substr(TOGGLE_MODE_PREFIX.length);
// toggle mode in the filter
var modeState = this.menuModes[modeName] ^= 1;
// toggle mode in the filter
var modeState = this.menuModes[modeName] ^= 1;
setModeIconAndOptgroup.call(this, ctrl, modeName, modeState);
setModeIconAndOptgroup.call(this, ctrl, modeName, modeState);
}
}
function setModeIconAndOptgroup(ctrl, name, state) {
var mode = modes[name],
displayMode = state ? null : 'none';
var style, optgroup, sum, display,
mode = this.modes.find(function(mode) { return mode.name === name; }); // eslint-disable-line no-shadow

@@ -283,42 +200,37 @@ // set icon state (color)

// empty the optgroup if hiding; rebuild it if showing
if (mode.appendOptions) {
var optgroup = this.dropdown.querySelector(mode.selector);
if (state) { // rebuild it
// show progress cursor for (at least) 1/3 second
style = this.el.style;
style.cursor = 'progress';
setTimeout(function() { style.cursor = null; }, 333);
if (state) { // rebuild it
// show progress cursor for (at least) 1/3 second
var style = this.el.style;
style.cursor = 'progress';
setTimeout(function() { style.cursor = null; }, 333);
if (mode.selector) {
optgroup = this.dropdown.querySelector(mode.selector);
sum = mode.appendOptions.call(this, optgroup);
var sum = mode.appendOptions.call(this, optgroup);
// update sum
optgroup.label = optgroup.label.replace(/ \(\d+\)$/, ''); // remove old sum
optgroup.label += ' (' + sum + ')';
} else { // empty it
while (optgroup.firstElementChild) {
optgroup.firstElementChild.remove();
} else {
sum = mode.appendOptions.call(this, this.dropdown);
if (!this.controllable) {
ctrl.textContent = sum + ' values';
}
}
display = null;
} else {
display = 'none';
}
// set optgroup state (hide/show)
// hide/show the group
elfor.each(
mode.selector,
function(el) { el.style.display = displayMode; },
mode.selector || ':scope>option,:scope>optgroup:not([class])',
function iteratee(el) { el.style.display = display; },
this.dropdown
);
// TODO: Reset the width of this.options to the natural width of this.dropdown. To do this, we need to remove the latter's "width: 100%" from the CSS and then set an explicity this.options.style.width based on the computed with of this.dropdown. (There will be issues with this if we try to do it before it is in the DOM.)
// TODO: Reset the width of this.options to the natural width of this.dropdown. To do this, we need to remove the latter's "width: 100%" from the CSS and then set an explicit this.options.style.width based on the computed width of this.dropdown. This is complicated by the fact that it cannot be done before it is in the DOM.
}
function toggleDropDown() {
var transitionInProgress = this.transit();
if (!transitionInProgress) {
var state = window.getComputedStyle(this.dropdown).visibility;
stateToActionMap[state].call(this);
}
}
function slideDown() {

@@ -330,3 +242,3 @@ // preserve the text box's current text selection, which is about to be lost

// clean up the select list from last usage
this.dropdown.style.selectedIndex = -1; // be kind (remove previous selection)
this.dropdown.selectedIndex = -1; // be kind (remove previous selection)
this.dropdown.style.scrollTop = 0; // rewind

@@ -342,4 +254,4 @@

// schedule the transition flag
this.transit(null);
// wait for transition to end
this.optionsTransition.begin();
}

@@ -355,17 +267,8 @@

// schedule the hide to occur after the slide up effect
this.transit(function(el) {
el.style.visibility = 'hidden';
this.optionsTransition.begin(function(event) {
this.style.visibility = 'hidden';
});
}
function insertText(e) {
// insert the text at the insertion point or over the selected text
this.input.focus();
this.input.setRangeText(this.dropdown.value, this.selectionStart, this.selectionEnd, 'end');
// close the drop-down
toggleDropDown.call(this);
}
module.exports = ComboBox;

@@ -5,16 +5,42 @@ /* eslint-env browser */

var Simple = require('./Simple');
var Formatters = require('../lib/Formatters');
var CellEditor = require('./CellEditor');
var localization = require('../lib/localization');
function parseDate(input) {
var parts = input.match(/(\d+)/g);
// new Date(year, month [, date [, hours[, minutes[, seconds[, ms]]]]])
return new window.Date(parts[0], parts[1] - 1, parts[2]); // months are 0-based
}
var isChromium = window.chrome,
winNav = window.navigator,
vendorName = winNav.vendor,
isOpera = winNav.userAgent.indexOf('OPR') > -1,
isIEedge = winNav.userAgent.indexOf('Edge') > -1,
isIOSChrome = winNav.userAgent.match('CriOS'),
isChrome = !isIOSChrome &&
isChromium !== null &&
isChromium !== undefined &&
vendorName === 'Google Inc.' &&
isOpera == false && isIEedge == false; // eslint-disable-line eqeqeq
/**
/**
* @constructor
*/
var Date = Simple.extend('Date', {
var Date = CellEditor.extend('Date', {
initialize: function(grid, localizer) {
var usesDateInputControl = isChrome;
if (!usesDateInputControl) {
this.template = {
/*
<input type="text">
*/
};
this.selectAll = function() {
var lastCharPlusOne = this.getEditorValue().length;
this.input.setSelectionRange(0, lastCharPlusOne);
};
this.localizer = localization.get('date');
}
},
template: function() {

@@ -26,17 +52,6 @@ /*

setEditorValue: function(value) {
if (value != null && value.constructor.name === 'Date') {
value = Formatters.date(value);
}
this.input.value = value + '';
},
localizer: localization.get('chromeDate')
});
getEditorValue: function() {
var value = this.el.value;
value = parseDate(value);
return value;
},
});
module.exports = Date;

@@ -0,14 +1,96 @@

/**
* @module cellEditors
*/
'use strict';
var localization = require('../lib/localization');
/**
* @summary Hash of cell editor object constructors.
* @desc This hash's only purpose is to support the convenience methods defined herein: {@link cellEditors.extend|extend}, {@link cellEditors.register|register}, and {@link cellEditors.instantiate|instantiate}. If you do not need these methods' functionality, you do not need to register your cell editors.
* @type {object}
*/
var constructors = {};
/**
* @summary Register a cell editor (class).
* @desc Adds a custom cell editor to the `constructors` hash using the provided name (or the class name) converted to all lower case.
*
* > All native cell editors are "preregistered" by constructor. If you plan to instantiate a number of grids, rather than registering all your custom cell editor(s) on all your grids, if that's what you were going to do, you might instead let them "go native" by adding them to `cellEditors` hash _before_ instantiating your grids so the constructor will do the work for you on each grid.
*
* @param {YourCellEditor.prototype.constructor} Constructor - A constructor, typically extended from `CellEditor` (or a descendant therefrom).
*
* @param {string} [editorName] - Case-insensitive editor key. If not given, `YourCellEditor.prototype.$$CLASS_NAME` is used.
*
* > Note: `$$CLASS_NAME` can be easily set up by providing a string as the (optional) first parameter in your {@link https://www.npmjs.com/package/extend-me|CellEditor.extend} call. (Formal parameter name: `alias`.)
*
* @memberOf module:cellEditors
*/
function register(Constructor, editorName) {
editorName = editorName || Constructor.prototype.$$CLASS_NAME;
editorName = editorName && editorName.toLowerCase();
constructors[editorName] = Constructor;
return Constructor;
}
/**
* Must be called with YOUR Hypergrid object as context!
* @returns {CellEditor} New instance of the named cell editor.
* @param {string} editorName
* @this {Hypergrid}
* @memberOf module:cellEditors
*/
function instantiate(editorName) {
editorName = editorName && editorName.toLowerCase();
var CellEditorConstructor = constructors[editorName];
return CellEditorConstructor && new CellEditorConstructor(this);
}
/**
* @summary Create a new localized cell editor class.
*
* @desc Extend the provided cell editor ('baseClassName') using the named localizer (`localizerName`), naming it after the localizer unless otherwise specified (in `newClassName`)
*
* @param {string} localizerName
*
* @param {string} [baseClassName='textfield'] - The base class must have been previously registered.
*
* @param {string} [newClassName=localizerName] - Provide a value here to name the cell editor differently from its localizer.
*
* @returns {function} The new cell editor constructor.
*
* @memberOf module:cellEditors
*/
function extend(localizerName, baseClassName, newClassName) {
baseClassName = baseClassName || 'textfield';
newClassName = newClassName || localizerName;
return constructors[baseClassName].extend(newClassName, {
localizer: localization.get(localizerName)
});
}
module.exports = {
CellEditor: require('./CellEditor'), // abstract base class
Textfield: require('./Textfield'),
ComboBox: require('./ComboBox'),
Choice: require('./Choice'),
//Combo: require('./Combo'),
Color: require('./Color'),
Date: require('./Date'),
Simple: require('./Simple'),
Slider: require('./Slider'),
Spinner: require('./Spinner')
constructors: constructors,
register: register,
instantiate: instantiate,
extend: extend
};
register(require('./CellEditor'));
register(require('./ComboBox'));
//register(require('./Combo'));
register(require('./Color'));
register(require('./Date'));
register(require('./FilterBox'));
register(require('./Number'));
register(require('./Slider'));
exports.int = exports.float = register(require('./Spinner'));
register(require('./Textfield'));
'use strict';
var Simple = require('./Simple');
var CellEditor = require('./CellEditor');

@@ -8,7 +8,7 @@ /**

*/
var Slider = Simple.extend('Slider', {
var Slider = CellEditor.extend('Slider', {
template: function() {
/*
<input id="editor" type="range">
<input type="range">
*/

@@ -15,0 +15,0 @@ }

'use strict';
var Simple = require('./Simple');
var CellEditor = require('./CellEditor.js');

@@ -8,7 +8,7 @@ /**

*/
var Spinner = Simple.extend('Spinner', {
var Spinner = CellEditor.extend('Spinner', {
template: function() {
/*
<input id="editor" type="number">
<input type="number">
*/

@@ -15,0 +15,0 @@ }

'use strict';
var Simple = require('./Simple');
var CellEditor = require('./CellEditor.js');

@@ -8,7 +8,7 @@ /**

*/
var Textfield = Simple.extend('Textfield', {
var Textfield = CellEditor.extend('Textfield', {
template: function() {
/*
<input id="editor" class="hypergrid-input-textbox">
<input type="text">
*/

@@ -20,8 +20,2 @@ },

this.input.setSelectionRange(0, lastCharPlusOne);
},
specialKeyups: {
0x09: 'stopEditing', // tab
0x0d: 'stopEditing', // return/enter
0x1b: 'cancelEditing' // escape
}

@@ -28,0 +22,0 @@ });

@@ -17,6 +17,2 @@ 'use strict';

initialize: function(grid) {
this.setGrid(grid);
},
setGrid: function(grid) {
this.grid = grid;

@@ -65,7 +61,28 @@ },

/**
* This method as written returns the `cellEditor` property (containing the cell editor class name) from the cell properties hash or, failing that, the column properties hash. The name is then used to instantiate a cell editor of that name found in the list of registered cel editors. If neither hash has such a property, or if no such editor is registered, returns `undefined` &mdash; which has the effect of making the cell editor non-editable.
*
* An easy way of making all cell editors non-editable regardless of the property settings (either temporarily or permanently) is to override this method with a null method (that returns `undefined`).
*
* The application developer may also wish to override this method to instantiate and return a `CellEditor` to be determined more precisely at run-time. This selection is usually based on column (`x`) but may in fact vary by row as well. Besides putting off the decision of which cell editor to use, this approach also has the advantage of being able to set attributes on the cell editor after instantiation but before it is rendered.
*
* @param {number} x - Column index in `behavior.allColumns` array.
* @param {number} y - Row index in `dataRows` (raw `dataSource.data`) array.
*
* @returns {undefined|CellEditor} An object instantiated from a constructor extended from CellEditor. If return value is `undefined` (or otherwise falsy), the cell will not be editable.
*/
getCellEditorAt: function(x, y) {
},
var cellProperties, columnProperties,
column = this.grid.behavior.allColumns[x];
return this.grid.createCellEditor(
(cellProperties = column.getCellProperties(y) || {}).editor ||
(columnProperties = column.getProperties()).editor ||
cellProperties.format ||
columnProperties.format
);
}
});
module.exports = DataModel;

@@ -219,3 +219,3 @@ 'use strict';

} else if (isFilterRow) {
this.setFilter(x, value);
this.setFilter(x, value, { alert: true });
} else {

@@ -484,2 +484,26 @@ return this._setHeader(x, value);

* @param {number} colIndex
* @param {boolean} deferred
*/
unSortColumn: function(colIndex, deferred) {
colIndex++; //hack to get around 0 index
var already = this.getColumnSortState(colIndex);
if (already > -1) {
this.removeColumnSortState(colIndex, already);
if (!deferred) {
this.applyAnalytics(true);
}
}
},
/**
* @memberOf dataModels.JSON.prototype
*/
getSortedColumnIndexes: function() {
var state = this.getPrivateState();
return state.sorts && state.sorts.slice() || [];
},
/**
* @memberOf dataModels.JSON.prototype
* @param {number} colIndex
* @param {string[]} keys

@@ -492,11 +516,8 @@ */

state.sorts = state.sorts || [];
var already = state.sorts.indexOf(colIndex);
if (already === -1) {
already = state.sorts.indexOf(-1 * colIndex);
}
var already = this.getColumnSortState(colIndex);
if (already > -1) {
if (state.sorts[already] > 0) {
state.sorts[already] = -1 * state.sorts[already];
state.sorts[already] = -1 * state.sorts[already]; //descending
} else {
state.sorts.splice(already, 1);
this.removeColumnSortState(colIndex, already);
}

@@ -509,2 +530,3 @@ } else if (hasCTRL || state.sorts.length === 0) {

}
//Minor improvement, but this check can happen earlier and terminate earlier
if (state.sorts.length > 3) {

@@ -517,2 +539,36 @@ state.sorts.length = 3;

* @memberOf dataModels.JSON.prototype
* @param {number} colIndex
* @returns {number}
*/
getColumnSortState: function(colIndex) {
//assumption is that colIndex has been hacked to get around 0
var already,
state = this.getPrivateState();
state.sorts = state.sorts || [];
//Check data columns
already = state.sorts.indexOf(colIndex);
//Check columns with negative indices. Meta columns??
if (already === -1) {
already = state.sorts.indexOf(-1 * colIndex);
}
return already;
},
/**
* @memberOf dataModels.JSON.prototype
* @param {number} colIndex
* @param {number} sortPosition
*/
removeColumnSortState: function(colIndex, sortPosition) {
//assumption is that colIndex has been hacked to get around 0
var state = this.getPrivateState();
state.sorts = state.sorts || [];
state.sorts.splice(sortPosition, 1);
},
/**
* @memberOf dataModels.JSON.prototype
* @param index

@@ -519,0 +575,0 @@ * @param returnAsString

@@ -65,4 +65,9 @@ /* eslint-env browser */

foregroundSelectionColor: 'rgb(0, 0, 128)',
/**
* @default true
* @type {boolean}
* @instance
*/
sortOnHiddenColumns: true,
/**
* Background color for selected cell(s).

@@ -248,4 +253,2 @@ * @default 'rgba(147, 185, 255, 0.45)'

filterCellBorderThickness: 0.4,
/********** SECTION: TREE COLUMN COLORS **********/

@@ -658,6 +661,10 @@ // The "tree column" contains the hierarchical drill-down controls.

/** Name of a formatters for cell text.
* @see /src/Formatters.js
/** Name of a formatter for cell text.
* The default (`null`) does no formatting.
* @default undefined
* @type {undefined|string}
* @see /lib/localizers.js
*/
format: 'default',
format: null,
editor: null,

@@ -664,0 +671,0 @@ /********** HOVER COLORS **********/

@@ -19,4 +19,6 @@ /* eslint-env browser */

initialize: function(grid, options) {
var behavior = this.behavior = grid.behavior;
var behavior = grid.behavior;
this.grid = grid;
if (behavior.isColumnReorderable()) {

@@ -44,2 +46,4 @@ // grab the lists from the behavior

this.sortOnHiddenColumns = this.wasSortOnHiddenColumns = grid.resolveProperty('sortOnHiddenColumns');
// parse & add the drag-and-drop stylesheet addendum

@@ -71,2 +75,8 @@ var stylesheetAddendum = css.inject('list-dragon-addendum');

});
//Listen to the visible column changes
listSets[1].modelLists[1].element.addEventListener('listchanged', function(e){
grid.fireSyntheticOnColumnsChangedEvent();
});
this.sortOnHiddenColumns = this.grid.resolveProperty('sortOnHiddenColumns');
} else {

@@ -80,2 +90,18 @@ var div = document.createElement('div');

// Add checkbox to control panel for sorting on hidden fields
var label = document.createElement('label');
label.innerHTML = '<input type="checkbox"> Allow sorting on hidden columns';
label.style.fontWeight = 'normal';
label.style.marginRight = '2em';
var checkbox = label.querySelector('input');
checkbox.checked = this.sortOnHiddenColumns;
checkbox.addEventListener('click', function(e){
self.sortOnHiddenColumns = checkbox.checked;
e.stopPropagation();
});
var panel = this.el.querySelector('.hypergrid-dialog-control-panel');
panel.insertBefore(label, panel.firstChild);
// add the dialog to the DOM

@@ -87,5 +113,7 @@ this.open(options.container);

if (this.visibleColumns) {
var columns = this.behavior.columns,
var behavior = this.grid.behavior,
columns = behavior.columns,
tree = columns[0];
// TODO: breaking encapsulation; should be using setters and getters on the behavior
columns.length = 0;

@@ -98,9 +126,13 @@ if (tree && tree.label === 'Tree') {

});
var groupBys = this.selectedGroups.models.map(function(e) {
return e.id;
});
this.behavior.dataModel.setGroups(groupBys);
behavior.dataModel.setGroups(groupBys);
this.behavior.changed();
if (this.sortOnHiddenColumns !== this.wasSortOnHiddenColumns) {
this.grid.addProperties({ sortOnHiddenColumns: this.sortOnHiddenColumns });
behavior.sortChanged(this.hiddenColumns.models);
}
behavior.changed();
}

@@ -107,0 +139,0 @@ }

@@ -26,7 +26,8 @@ /* eslint-env browser */

* Creates a basic dialog box in `this.el`.
* @param {CellEditor} [context] - Cell editor object possibly containing `stopEditing` and `beginSettings` methods for the close box and settings gear icons and `onClick` for custom handling.
* @param {string|function|Node|Node[]} nodes
* @param {Hypergrid} grid
* @param {object} [options]
* @param {string|function} [options.dialogTemplate] - An alternate dialog template. The last child element must be the "control panel."
* @param {boolean} [options.settings=true] - Control box has settings icon. (Settings icon must be included in template. This option removes it. That is, if explicitly `false` _and_ there is a settings control, remove it.)
* @param {string|boolean} [options.backgroundImage=images.dialog.src] - A URI for a background image. If explicitly `false`, background image is suppressed.
* @param {function} [terminate]
*/

@@ -33,0 +34,0 @@ initialize: function(grid, options) {

'use strict';
var Feature = require('./Feature.js');
var Simple = require('../cellEditors/Simple');
var CellEditor = require('../cellEditors/CellEditor');

@@ -83,3 +83,3 @@ /**

editor = grid.onEditorActivate(pseudoEvent);
if (editor instanceof Simple) {
if (editor instanceof CellEditor) {
if (isVisibleChar) {

@@ -86,0 +86,0 @@ editor.setEditorValue(char);

@@ -473,2 +473,3 @@ /* eslint-env browser */

columnIndex: columnIndex,
startIndex: columnIndex,
ctx: draggerCTX,

@@ -590,3 +591,3 @@ startX: startX,

var scrollLeft = grid.getHScrollValue();
if (!grid.dragging || scrollLeft > (grid.sbHScrollConfig.rangeStop - 2)) {
if (!grid.dragging || scrollLeft > (grid.sbHScroller.range.max - 2)) {
return;

@@ -654,3 +655,3 @@ }

var d = dragger;
var changed = grid.renderOverridesCache.dragger.startIndex !== grid.renderOverridesCache.dragger.columnIndex;
self.setCrossBrowserProperty(d, 'transition', (self.isWebkit ? '-webkit-' : '') + 'transform ' + columnAnimationTime + 'ms ease, box-shadow ' + columnAnimationTime + 'ms ease');

@@ -665,3 +666,6 @@ self.setCrossBrowserProperty(d, 'transform', 'translate(' + startX + 'px, ' + -1 + 'px)');

d.style.display = 'none';
grid.endDragColumnNotification();
grid.endDragColumnNotification(); //internal notification
if (changed){
grid.fireSyntheticOnColumnsChangedEvent(); //public notification
}
});

@@ -668,0 +672,0 @@ }, columnAnimationTime + 50);

@@ -82,3 +82,3 @@ 'use strict';

result = convertLikeToPseudoOp(result);
var defaultOp = this.schema.lookup(this.column).defaultOp || '=';
var defaultOp = this.schema.lookup(this.column).defaultOp || this.root.parserCQL.defaultOp; // mimics logic in parser-CQL.js, line 110
if (result.toUpperCase().indexOf(defaultOp) === 0) {

@@ -141,14 +141,15 @@ result = result.substr(defaultOp.length);

preInitialize: function(options) {
if (options) {
options = options || {};
// Set up the default "Hyperfilter" profile (see function comments)
options.state = options.state || this.makeNewRoot();
// Set up the default "Hyperfilter" profile (see function comments)
var state = options.state = options.state || this.makeNewRoot();
// Upon creation of a 'columnFilter' node, force the schema to the one column
if ((options.type || options.state && options.state.type) === 'columnFilter') {
this.schema = [
options.parent.root.schema.lookup(options.state.children[0].column)
];
}
// Upon creation of a 'columnFilter' node, force the schema to the one column
if ((options.type || state && state.type) === 'columnFilter') {
this.schema = [
options.parent.root.schema.lookup(state.children[0].column)
];
}
return [options];
},

@@ -168,4 +169,3 @@

schema: this.schema,
caseSensitiveColumnNames: options.caseSensitiveColumnNames,
resolveAliases: options.resolveAliases
defaultOp: options.defaultColumnFilterOperator
});

@@ -268,21 +268,15 @@ }

getColumnFilterState: function(rawColumnName, options) {
var result,
subexpression;
var result = '',
columnSchema = this.schema.lookup(rawColumnName);
var columnName = this.schema.lookup(rawColumnName).name;
if (columnSchema) {
var subexpression = this.getColumnFilter(columnSchema.name);
if (!columnName) {
throw 'Unknown column name "' + rawColumnName + '"';
}
subexpression = this.getColumnFilter(columnName);
if (subexpression) {
if (!(options && options.syntax)) {
options = options || {};
options.syntax = 'CQL';
if (subexpression) {
if (!(options && options.syntax)) {
options = options || {};
options.syntax = 'CQL';
}
result = subexpression.getState(options);
}
result = subexpression.getState(options);
} else {
result = '';
}

@@ -355,2 +349,3 @@

}
options.throw = true;
error = subexpression.invalid(options);

@@ -468,2 +463,12 @@ }

return result;
},
loadColumnPropertiesFromSchema: function(columns) {
this.root.schema.walk(function(columnSchema) {
var column = columns.find(function(thisColumn) {
return thisColumn.name === columnSchema.name || columnSchema;
});
column.type = columnSchema.type || column.type;
column.label = columnSchema.alias || column.label;
});
}

@@ -470,0 +475,0 @@ });

@@ -12,4 +12,3 @@ 'use strict';

'<=': '≤',
'<>': '≠',
undefined: '='
'<>': '≠'
};

@@ -33,7 +32,7 @@

* @param {menuItem[]} [options.schema] - Column schema for column name/alias validation. Throws an error if name fails validation (but see `resolveAliases`). Omit to skip column name validation.
* @param {boolean} [options.resolveAliases] - Validate column aliases against schema and use the associated column name in the returned expression state object. Requires `options.schema`. Throws error if no such column found.
* @param {boolean} [options.caseSensitiveColumnNames] - Ignore case while validating column names and aliases.
* @param {boolean} [options.defaultOp='='] - Default operator for column when not defined in column schema.
*/
function ParserCQL(options) {
this.schema = options && options.schema;
this.defaultOp = options && options.defaultOp || '=';
}

@@ -113,3 +112,4 @@

var op = parts[1] && parts[1] || // as specified by user
self.schema && self.schema.lookup(columnName).defaultOp; // column's default operator from schema
self.schema && self.schema.lookup(columnName).defaultOp || // column's default operator from schema
self.defaultOp; // grid's default operator

@@ -178,4 +178,2 @@ op = opMap[op] || op; // accommodate alternate spellings of operators

//this.state = state;
return state;

@@ -182,0 +180,0 @@ }

@@ -143,3 +143,3 @@ 'use strict';

val = valOrFunc(val, config);
val = config.formatter(val);
val = config.formatValue(val);

@@ -518,5 +518,6 @@ font = config.isSelected ? config.foregroundSelectionFont : config.font;

this.cellCache.linkCellRenderer = {
paint: function(gc, x, y, width, height) {
paint: function(gc, config) {
self.config = this.config;
self.defaultCellPaint(gc, x, y, width, height, true);
//todo: needs config.link ??
self.defaultCellPaint(gc, config);
}

@@ -523,0 +524,0 @@ };

'use strict';
// todo: publish this to npm!
// called it "queueless" because it does not attempt to implement a full-blown transitions queue. These few lines give you the info you need most often: Is the transition still going? When does it end? Do this when it ends, please....
function Queueless(element, context) {
this.element = element;
this.context = context;
this.transitioning = false;
}
/**
* Sets up an event listener to wait for the end of the transition and then executes the given code.
*
* Even without a call back, this is useful because it also maintains a unique flag (derived from `name`). Check this flag by giving `null` as 3rd parameter to ignore user interactions that would otherwise start a new transition.
*
* @param {HTMLElement} element
*
* @param {string} name
*
* @param {null|function|object} [callback] - One of the following modes:
* * If omitted, simply returns state of the transition (`true` means active; `false` means inactive). (This is a utility call; it does not set up the event listener.)
* * If an object, resets the state flag and returns a new function, itself but bound to this object as context, and `element` and `name` as first two parameters. For this mode only, the context is this 3rd parameter; for all other modes, the context is `this`. (This is a utility call; it does not set up the event listener.)
* * If a function: Sets up the event listener; when it fires the callback is called in context.
* * Otherwise (e.g., `null`): Sets up the event listener merely to track the transition's state (i.e., there will be no callback).
*
* @returns {boolean|function} Returns a boolean or a function, respectively, for the first two options listed above for the `callback` paremeter.
*/
function onTransitionEnd(element, name, callback) {
var context = this,
mode = typeof callback,
flagName = name + 'Transitioning';
Queueless.prototype.begin = function(callback) {
var self = this;
switch (mode) {
case 'undefined':
return context[flagName];
case 'object':
if (callback !== null) { // oh my, `null` is lexically an object
context = callback;
context[flagName] = false;
return onTransitionEnd.bind(context, element, name);
}
default: // eslint-disable-line no-fallthrough
context[flagName] = true;
this.transitioning = true;
element.addEventListener('transitionend', function done() {
element.removeEventListener('transitionend', done);
if (mode === 'function') {
callback.call(context, element);
}
context[flagName] = false;
});
}
}
this.element.addEventListener('transitionend', function end(transEvent) {
self.element.removeEventListener('transitionend', end);
module.exports = onTransitionEnd;
if (callback) {
callback.call(this, transEvent, self);
}
self.transitioning = false;
});
};
module.exports = Queueless;

@@ -1069,3 +1069,3 @@ /* eslint-env browser */

var cell = behavior.getCellRenderer(cellProperties, c, r);
var overrides = behavior.getCellProperties(c, r);
var overrides = behavior.getCellProperties(behavior.getColumn(c).index, r);

@@ -1077,4 +1077,4 @@ //declarative cell properties

cellProperties.buttonCells = this.buttonCells;
var formatType = cellProperties.isUserDataArea ? cellProperties.format : 'default';
cellProperties.formatter = grid.getFormatter(formatType);
var formatName = cellProperties.isUserDataArea && cellProperties.format;
cellProperties.formatValue = grid.getFormatter(formatName);
cell.paint(gc, cellProperties);

@@ -1081,0 +1081,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc