Skip to content

Commit

Permalink
(feat): new Major 2.x Release to Support Multiple Grid (#75)
Browse files Browse the repository at this point in the history
* refactor(services): refactor singleton Services into transient
- also add new AureliaGridInstance

* refactor(service): rename GridExtraService to GridService

* deprecate(service): delete GridExtraUtil & move function to GridService

* refactor(code): remove deprecated "onBackendEventApi"

* refactor(code): remove deprecated "selectOptions" from SelectFilter

* refactor(code): remove deprecated FormElementType

* refactor(model): remove exportWithFormatter from the GridOptions

* refactor(service): remove deprecated initOptions replaced by init

* refactor(model): remove deprecated "dataFilters"

* refactor(backend): all backend service methods renamed as processOnX
- to remove confusion with Event Emitters, the 3 Backend Service API methods were renamed to start with the prefix "processOnX" instead of "onX"
- for example onFilterChanged is now processOnFilterChanged

* refactor(gridOptions): all Grid Menu showX were renamed hideX
- since we had both "hideX" (in SlickGrid) and "showX", it's better to rename them all to "hideX" for consistencies

* refactor(events): change aurelia event prefix
- to make a distinction between Aurelia Events vs SlickGrid Events, we will use (asg for Aurelia, sg for SlickGrid)

* refactor(i18n): add i18n Grid Options instead of using params

* refactor(grid): add multiple grids in a view

* refactor(searchTerm): remove searchTerm and only use searchTerms
- prior to this, user could predefined searchTerm (singular) or searchTerms (array). To simplify the logic, the singular searchTerm has been dropped in favor of the array searchTerms

* refactor(example): fix multiple grids not displayed

* refactor(styling): change highlight and selected row color to blueish

* refactor(styling): make selected row a little darker

* refactor(formatter): console error should contain new grid options i18n

* update readme

* refactor(services): add singleton(true) decorator for multiple grids

* fix(i18n): make sure all Services use correct Grid Options i18n

* refactor(example): make row selection titles more obvious

* refactor(event): renamed onGridStateServiceChanged to onGridStateChanged

* feat(grid): expose Slick Grid & DataView objects in AureliaGridInstance

* fix(grid): Dynamically Add Column Header was broken with non-singleton

* refactor(styling): change mouse hover & selection background colors

* refactor(example): add 2x grids in the Basic Grid sample

* refactor(editor): move all Editor params into editor
- instead of using the generic "params" to pass collection and other arguments, we will use the "editor" object
- doing this brings TS types and intellisense

* refactor(service): refactored delete/update item functions

* fix(sorter): issue #72 circular dependency from last PR #70

* fix(event): remove e.stopImmediatePropagation to fix issue #60

* refactor(columnDef): make editor.type the Editor class export function

editor.type was enum, but that means the library would be responsible for finding the editor.
Also, special logic would have to be in place for custom editors.
By making editor.type the exported class function all we have to do is pass it is slickgrid to create

* refactor(example): fixed onCellChange event and advertise delete feature

* refactor(event): expose event to columnDef onCellClick and onCellChange

* refactor(event): use subscriptions array and loop to unsubscribe
- also do the same with Services, make an array and loop through when disposing them

* refactor(editor): change ColumnEditor.type to ColumnEditor.model

* refactor(filter): use Filter model on ColumnFilter, remove FilterType

* feat(gridState): Grid State & Presets for columns (position, size, visibility) (#74)
  • Loading branch information
ghiscoding committed Jun 5, 2018
1 parent 3c68819 commit edb0100
Show file tree
Hide file tree
Showing 114 changed files with 2,031 additions and 1,087 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -2,7 +2,7 @@
One of the best javascript datagrid [SlickGrid](https://github.com/mleibman/SlickGrid) which was originally developed by @mleibman is now available to Aurelia. I have tried and used a few datagrids and SlickGrid beats most of them in terms of functionalities and performance (it can easily deal with even a million row).

### SlickGrid Source
We will be using [6pac SlickGrid fork](https://github.com/6pac/SlickGrid/) (the most active fork since the original @mleibman fork was closed some time ago by his author personal reasons).
We will be using [6pac SlickGrid fork](https://github.com/6pac/SlickGrid/) (the most active fork since the original @mleibman fork was closed some time ago by his author for personal reasons).

### Goal
The goal is of course to be able to run SlickGrid within Aurelia but also to incorporate as much as possible the entire list of functionalities (and more) that SlickGrid offers (you can see a vast list of samples on the [6pac SlickGrid examples](https://github.com/6pac/SlickGrid/wiki/Examples) website).
Expand Down
212 changes: 133 additions & 79 deletions aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts

Large diffs are not rendered by default.

Expand Up @@ -39,8 +39,9 @@ export class FloatEditor implements Editor {

getDecimalPlaces() {
// returns the number of fixed decimal places or null
const columnParams = this.args.column.params || {};
let rtn = (columnParams && columnParams.hasOwnProperty('decimalPlaces')) ? columnParams.decimalPlaces : undefined;
const columnEditor = this.args && this.args.column && this.args.column.internalColumnEditor && this.args.column.internalColumnEditor;
let rtn = (columnEditor && columnEditor.params && columnEditor.params.hasOwnProperty('decimalPlaces')) ? columnEditor.params.decimalPlaces : undefined;

if (rtn === undefined) {
rtn = defaultDecimalPlaces;
}
Expand Down
Expand Up @@ -50,7 +50,6 @@ export class MultipleSelectEditor implements Editor {

constructor(private collectionService: CollectionService, private i18n: I18N, private args: any) {
this.gridOptions = this.args.grid.getOptions() as GridOption;
const params = this.gridOptions.params || this.args.column.params || {};

this.defaultOptions = {
container: 'body',
Expand Down Expand Up @@ -85,29 +84,28 @@ export class MultipleSelectEditor implements Editor {
throw new Error('[Aurelia-SlickGrid] An editor must always have an "init()" with valid arguments.');
}

this.columnDef = this.args.column;
this.columnDef = this.args.column as Column;

if (!this.columnDef || !this.columnDef.params || !this.columnDef.params.collection) {
throw new Error('[Aurelia-SlickGrid] You need to pass a "collection" on the params property in the column definition for ' +
'the SingleSelect Editor to work correctly. Also each option should include ' +
'a value/label pair (or value/labelKey when using Locale). For example: { params: { ' +
'{ collection: [{ value: true, label: \'True\' }, { value: false, label: \'False\'}] } } }');
if (!this.columnDef || !this.columnDef.internalColumnEditor || !this.columnDef.internalColumnEditor.collection) {
throw new Error(`[Aurelia-SlickGrid] You need to pass a "collection" inside Column Definition Editor for the MultipleSelect Editor to work correctly.
Also each option should include a value/label pair (or value/labelKey when using Locale).
For example: { editor: { collection: [{ value: true, label: 'True' },{ value: false, label: 'False'}] } }`);
}

this.enableTranslateLabel = (this.columnDef.params.enableTranslateLabel) ? this.columnDef.params.enableTranslateLabel : false;
let newCollection = this.columnDef.params.collection || [];
this.labelName = (this.columnDef.params.customStructure) ? this.columnDef.params.customStructure.label : 'label';
this.valueName = (this.columnDef.params.customStructure) ? this.columnDef.params.customStructure.value : 'value';
this.enableTranslateLabel = (this.columnDef.internalColumnEditor.enableTranslateLabel) ? this.columnDef.internalColumnEditor.enableTranslateLabel : false;
let newCollection = this.columnDef.internalColumnEditor.collection || [];
this.labelName = (this.columnDef.internalColumnEditor.customStructure) ? this.columnDef.internalColumnEditor.customStructure.label : 'label';
this.valueName = (this.columnDef.internalColumnEditor.customStructure) ? this.columnDef.internalColumnEditor.customStructure.value : 'value';

// user might want to filter certain items of the collection
if (this.gridOptions && this.gridOptions.params && this.columnDef.params.collectionFilterBy) {
const filterBy = this.columnDef.params.collectionFilterBy;
if (this.columnDef && this.columnDef.internalColumnEditor && this.columnDef.internalColumnEditor.collectionFilterBy) {
const filterBy = this.columnDef.internalColumnEditor.collectionFilterBy;
newCollection = this.collectionService.filterCollection(newCollection, filterBy);
}

// user might want to sort the collection
if (this.columnDef.params && this.columnDef.params.collectionSortBy) {
const sortBy = this.columnDef.params.collectionSortBy;
if (this.columnDef.internalColumnEditor && this.columnDef.internalColumnEditor.collectionSortBy) {
const sortBy = this.columnDef.internalColumnEditor.collectionSortBy;
newCollection = this.collectionService.sortCollection(newCollection, sortBy, this.enableTranslateLabel);
}

Expand Down Expand Up @@ -230,7 +228,7 @@ export class MultipleSelectEditor implements Editor {
// fallback to bootstrap
this.$editorElm.addClass('form-control');
} else {
const elementOptions = (this.columnDef.params) ? this.columnDef.params.elementOptions : {};
const elementOptions = (this.columnDef.internalColumnEditor) ? this.columnDef.internalColumnEditor.elementOptions : {};
this.editorElmOptions = { ...this.defaultOptions, ...elementOptions };
this.$editorElm = this.$editorElm.multipleSelect(this.editorElmOptions);
setTimeout(() => this.$editorElm.multipleSelect('open'));
Expand Down
Expand Up @@ -80,26 +80,26 @@ export class SingleSelectEditor implements Editor {

this.columnDef = this.args.column;

if (!this.columnDef || !this.columnDef.params || !this.columnDef.params.collection) {
throw new Error(`[Aurelia-SlickGrid] You need to pass a "collection" on the params property in the column definition for the MultipleSelect Editor to work correctly.
if (!this.columnDef || !this.columnDef.internalColumnEditor || !this.columnDef.internalColumnEditor.collection) {
throw new Error(`[Aurelia-SlickGrid] You need to pass a "collection" inside Column Definition Editor for the SingleSelect Editor to work correctly.
Also each option should include a value/label pair (or value/labelKey when using Locale).
For example: { params: { { collection: [{ value: true, label: 'True' },{ value: false, label: 'False'}] } } }`);
For example: { editor: { collection: [{ value: true, label: 'True' },{ value: false, label: 'False'}] } }`);
}

this.enableTranslateLabel = (this.columnDef.params.enableTranslateLabel) ? this.columnDef.params.enableTranslateLabel : false;
let newCollection = this.columnDef.params.collection || [];
this.labelName = (this.columnDef.params.customStructure) ? this.columnDef.params.customStructure.label : 'label';
this.valueName = (this.columnDef.params.customStructure) ? this.columnDef.params.customStructure.value : 'value';
this.enableTranslateLabel = (this.columnDef.internalColumnEditor.enableTranslateLabel) ? this.columnDef.internalColumnEditor.enableTranslateLabel : false;
let newCollection = this.columnDef.internalColumnEditor.collection || [];
this.labelName = (this.columnDef.internalColumnEditor.customStructure) ? this.columnDef.internalColumnEditor.customStructure.label : 'label';
this.valueName = (this.columnDef.internalColumnEditor.customStructure) ? this.columnDef.internalColumnEditor.customStructure.value : 'value';

// user might want to filter certain items of the collection
if (this.gridOptions.params && this.columnDef.params.collectionFilterBy) {
const filterBy = this.columnDef.params.collectionFilterBy;
if (this.columnDef.internalColumnEditor && this.columnDef.internalColumnEditor.collectionFilterBy) {
const filterBy = this.columnDef.internalColumnEditor.collectionFilterBy;
newCollection = this.collectionService.filterCollection(newCollection, filterBy);
}

// user might want to sort the collection
if (this.columnDef.params && this.columnDef.params.collectionSortBy) {
const sortBy = this.columnDef.params.collectionSortBy;
if (this.columnDef.internalColumnEditor && this.columnDef.internalColumnEditor.collectionSortBy) {
const sortBy = this.columnDef.internalColumnEditor.collectionSortBy;
newCollection = this.collectionService.sortCollection(newCollection, sortBy, this.enableTranslateLabel);
}

Expand Down Expand Up @@ -199,7 +199,7 @@ export class SingleSelectEditor implements Editor {
collection.forEach((option: SelectOption) => {
if (!option || (option[this.labelName] === undefined && option.labelKey === undefined)) {
throw new Error('A collection with value/label (or value/labelKey when using ' +
'Locale) is required to populate the Select list, for example: { params: { ' +
'Locale) is required to populate the Select list, for example: { internalColumnEditor: { ' +
'{ collection: [ { value: \'1\', label: \'One\' } ] } } }');
}
const labelKey = (option.labelKey || option[this.labelName]) as string;
Expand All @@ -222,7 +222,7 @@ export class SingleSelectEditor implements Editor {
// fallback to bootstrap
this.$editorElm.addClass('form-control');
} else {
const elementOptions = (this.columnDef.params) ? this.columnDef.params.elementOptions : {};
const elementOptions = (this.columnDef.internalColumnEditor) ? this.columnDef.internalColumnEditor.elementOptions : {};
this.editorElmOptions = { ...this.defaultOptions, ...elementOptions };
this.$editorElm = this.$editorElm.multipleSelect(this.editorElmOptions);
setTimeout(() => this.$editorElm.multipleSelect('open'));
Expand Down
Expand Up @@ -14,7 +14,7 @@ export class TextEditor implements Editor {
}

init(): void {
this.$input = $(`<input type="text" class='editor-text' />`)
this.$input = $(`<input type="text" class="editor-text" />`)
.appendTo(this.args.container)
.on('keydown.nav', (e) => {
if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.RIGHT) {
Expand Down
Expand Up @@ -5,5 +5,6 @@ function parseBoolean(input: boolean | number | string) {
}

export const booleanFilterCondition: FilterCondition = (options: FilterConditionOption) => {
return parseBoolean(options.cellValue) === parseBoolean(options.searchTerm || false);
const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || '';
return parseBoolean(options.cellValue) === parseBoolean(searchTerm);
};
Expand Up @@ -4,13 +4,14 @@ import { testFilterCondition } from './filterUtilities';
import * as moment from 'moment';

export const dateFilterCondition: FilterCondition = (options: FilterConditionOption) => {
const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || '';
const filterSearchType = options.filterSearchType || FieldType.dateIso;
const searchDateFormat = mapMomentDateFormatWithFieldType(filterSearchType);
if (!moment(options.cellValue, moment.ISO_8601).isValid() || !moment(options.searchTerm, searchDateFormat, true).isValid()) {
if (searchTerm === null || searchTerm === '' || !moment(options.cellValue, moment.ISO_8601).isValid() || !moment(searchTerm, searchDateFormat, true).isValid()) {
return false;
}
const dateCell = moment(options.cellValue);
const dateSearch = moment(options.searchTerm);
const dateSearch = moment(searchTerm);

// run the filter condition with date in Unix Timestamp format
return testFilterCondition(options.operator || '==', parseInt(dateCell.format('X'), 10), parseInt(dateSearch.format('X'), 10));
Expand Down
Expand Up @@ -5,11 +5,12 @@ import * as moment from 'moment';
const FORMAT = mapMomentDateFormatWithFieldType(FieldType.dateIso);

export const dateIsoFilterCondition: FilterCondition = (options: FilterConditionOption) => {
if (!moment(options.cellValue, FORMAT, true).isValid() || !moment(options.searchTerm, FORMAT, true).isValid()) {
const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || '';
if (searchTerm === null || searchTerm === '' || !moment(options.cellValue, FORMAT, true).isValid() || !moment(searchTerm, FORMAT, true).isValid()) {
return false;
}
const dateCell = moment(options.cellValue, FORMAT, true);
const dateSearch = moment(options.searchTerm, FORMAT, true);
const dateSearch = moment(searchTerm, FORMAT, true);

// run the filter condition with date in Unix Timestamp format
return testFilterCondition(options.operator || '==', parseInt(dateCell.format('X'), 10), parseInt(dateSearch.format('X'), 10));
Expand Down
Expand Up @@ -5,11 +5,12 @@ import * as moment from 'moment';
const FORMAT = mapMomentDateFormatWithFieldType(FieldType.dateUs);

export const dateUsFilterCondition: FilterCondition = (options: FilterConditionOption) => {
if (!moment(options.cellValue, FORMAT, true).isValid() || !moment(options.searchTerm, FORMAT, true).isValid()) {
const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || '';
if (searchTerm === null || searchTerm === '' || !moment(options.cellValue, FORMAT, true).isValid() || !moment(searchTerm, FORMAT, true).isValid()) {
return false;
}
const dateCell = moment(options.cellValue, FORMAT, true);
const dateSearch = moment(options.searchTerm, FORMAT, true);
const dateSearch = moment(searchTerm, FORMAT, true);

// run the filter condition with date in Unix Timestamp format
return testFilterCondition(options.operator || '==', parseInt(dateCell.format('X'), 10), parseInt(dateSearch.format('X'), 10));
Expand Down
Expand Up @@ -5,11 +5,12 @@ import { mapMomentDateFormatWithFieldType } from './../services/utilities';
const FORMAT = mapMomentDateFormatWithFieldType(FieldType.dateUsShort);

export const dateUsShortFilterCondition: FilterCondition = (options: FilterConditionOption) => {
if (!moment(options.cellValue, FORMAT, true).isValid() || !moment(options.searchTerm, FORMAT, true).isValid()) {
const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || '';
if (searchTerm === null || searchTerm === '' || !moment(options.cellValue, FORMAT, true).isValid() || !moment(searchTerm, FORMAT, true).isValid()) {
return false;
}
const dateCell = moment(options.cellValue, FORMAT, true);
const dateSearch = moment(options.searchTerm, FORMAT, true);
const dateSearch = moment(searchTerm, FORMAT, true);

// run the filter condition with date in Unix Timestamp format
return testFilterCondition(options.operator || '==', parseInt(dateCell.format('X'), 10), parseInt(dateSearch.format('X'), 10));
Expand Down
Expand Up @@ -4,12 +4,13 @@ import { testFilterCondition } from './filterUtilities';
import * as moment from 'moment';

export const dateUtcFilterCondition: FilterCondition = (options: FilterConditionOption) => {
const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || '';
const searchDateFormat = mapMomentDateFormatWithFieldType(options.filterSearchType || options.fieldType);
if (!moment(options.cellValue, moment.ISO_8601).isValid() || !moment(options.searchTerm, searchDateFormat, true).isValid()) {
if (searchTerm === null || searchTerm === '' || !moment(options.cellValue, moment.ISO_8601).isValid() || !moment(searchTerm, searchDateFormat, true).isValid()) {
return false;
}
const dateCell = moment(options.cellValue, moment.ISO_8601, true);
const dateSearch = moment(options.searchTerm, searchDateFormat, true);
const dateSearch = moment(searchTerm, searchDateFormat, true);

// run the filter condition with date in Unix Timestamp format
return testFilterCondition(options.operator || '==', parseInt(dateCell.format('X'), 10), parseInt(dateSearch.format('X'), 10));
Expand Down
Expand Up @@ -3,7 +3,14 @@ import { testFilterCondition } from './filterUtilities';

export const numberFilterCondition: FilterCondition = (options: FilterConditionOption) => {
const cellValue = parseFloat(options.cellValue);
const searchTerm = (typeof options.searchTerm === 'string') ? parseFloat(options.searchTerm) : options.searchTerm;
let searchTerm = (Array.isArray(options.searchTerms) && options.searchTerms[0]) || 0;
if (typeof searchTerm === 'string') {
searchTerm = parseFloat(searchTerm);
}

if (!searchTerm && (!options.operator || options.operator === '')) {
return true;
}

return testFilterCondition(options.operator || '==', cellValue, searchTerm);
};
Expand Up @@ -7,7 +7,10 @@ export const stringFilterCondition: FilterCondition = (options: FilterConditionO

// make both the cell value and search value lower for case insensitive comparison
const cellValue = options.cellValue.toLowerCase();
const searchTerm = (typeof options.searchTerm === 'string') ? options.searchTerm.toLowerCase() : options.searchTerm;
let searchTerm = (Array.isArray(options.searchTerms) && options.searchTerms[0]) || '';
if (typeof searchTerm === 'string') {
searchTerm = searchTerm.toLowerCase();
}

if (options.operator === '*' || options.operator === OperatorType.endsWith) {
return cellValue.endsWith(searchTerm);
Expand Down

0 comments on commit edb0100

Please sign in to comment.