Skip to content

Commit

Permalink
refactor(editor): move all Editor params into editor
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ghiscoding committed May 31, 2018
1 parent e9306b3 commit 0ca7bf8
Show file tree
Hide file tree
Showing 19 changed files with 372 additions and 66 deletions.
30 changes: 29 additions & 1 deletion aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts
Expand Up @@ -23,14 +23,16 @@ import 'slickgrid/plugins/slick.rowselectionmodel';
import { bindable, BindingEngine, bindingMode, Container, Factory, inject } from 'aurelia-framework';
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';
import { GlobalGridOptions } from './global-grid-options';
import { AVAILABLE_EDITORS } from './editors/index';
import {
AureliaGridInstance,
BackendServiceOption,
Column,
EditorType,
GridOption,
GridStateChange,
GridStateType,
Pagination,
AureliaGridInstance,
} from './models/index';
import {
ControlAndPluginService,
Expand Down Expand Up @@ -133,6 +135,13 @@ export class AureliaSlickgridCustomElement {
} else {
this.dataview = new Slick.Data.DataView();
}

// for convenience, we provide the property "editor" as an Aurelia-Slickgrid editor complex object
// however "editor" is used internally by SlickGrid for it's Editor Factory
// so in our lib we will swap "editor" and copy it into "internalColumnEditor"
// then take back "editor.type" and make it the new "editor" so that SlickGrid Editor Factory still works
this._columnDefinitions = this.columnDefinitions.map((c: Column | any) => ({ ...c, editor: this.getEditor((c.editor && c.editor.type), c), internalColumnEditor: { ...c.editor } }));

this.controlAndPluginService.createPluginBeforeGridCreation(this._columnDefinitions, this.gridOptions);
this.grid = new Slick.Grid(`#${this.gridId}`, this.dataview, this._columnDefinitions, this.gridOptions);
this.controlAndPluginService.attachDifferentControlOrPlugins(this.grid, this.dataview, this.groupItemMetadataProvider);
Expand Down Expand Up @@ -277,6 +286,25 @@ export class AureliaSlickgridCustomElement {
}
}

/**
* From the list of available editors, find the editor associated to it's type
* and if it's a custom one, return the "customEditor" from the column
* @param type
* @param column
*/
getEditor(type: EditorType, column: Column) {
if (type === EditorType.custom && column && column.editor && column.editor.hasOwnProperty('customEditor')) {
return column.editor['customEditor'];
}

const editorFound = AVAILABLE_EDITORS.find(editor => editor.type === type);
if (editorFound && editorFound.editor) {
return editorFound.editor;
}

return undefined;
}

/**
* Define what our internal Post Process callback, it will execute internally after we get back result from the Process backend call
* For now, this is GraphQL Service only feautre and it will basically refresh the Dataset & Pagination without having the user to create his own PostProcess every time
Expand Down
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
16 changes: 16 additions & 0 deletions aurelia-slickgrid/src/aurelia-slickgrid/editors/index.ts
Expand Up @@ -6,6 +6,11 @@ import { LongTextEditor } from './longTextEditor';
import { MultipleSelectEditor } from './multipleSelectEditor';
import { SingleSelectEditor } from './singleSelectEditor';
import { TextEditor } from './textEditor';
import { Editor, EditorType } from '../models/index';

export class AvailableEditor {
constructor(public type: EditorType, public editor: any) { }
}

export const Editors = {
checkbox: CheckboxEditor,
Expand All @@ -17,3 +22,14 @@ export const Editors = {
singleSelect: SingleSelectEditor,
text: TextEditor
};

export const AVAILABLE_EDITORS: AvailableEditor[] = [
{ type: EditorType.checkbox, editor: CheckboxEditor },
{ type: EditorType.date, editor: DateEditor },
{ type: EditorType.float, editor: FloatEditor },
{ type: EditorType.integer, editor: IntegerEditor },
{ type: EditorType.longText, editor: LongTextEditor },
{ type: EditorType.multipleSelect, editor: MultipleSelectEditor },
{ type: EditorType.singleSelect, editor: SingleSelectEditor },
{ type: EditorType.text, editor: TextEditor },
];
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
@@ -1,6 +1,5 @@
import { inject } from 'aurelia-framework';
import { I18N } from 'aurelia-i18n';
import { htmlEntityEncode } from './../services/utilities';
import {
Column,
FieldType,
Expand Down
@@ -0,0 +1,27 @@
import { arrayToCsvFormatter } from './arrayToCsvFormatter';
import { Column, Formatter } from './../models/index';
import { findOrDefault } from '../services/index';

/**
* A formatter to show the label property value of a internalColumnEditor collection
*/
export const collectionEditorFormatter: Formatter = (row: number, cell: number, value: any, columnDef: Column, dataContext: any) => {
if (!value || !columnDef || !columnDef.internalColumnEditor || !columnDef.internalColumnEditor.collection
|| !columnDef.internalColumnEditor.collection.length) {
return '';
}

const { internalColumnEditor, internalColumnEditor: { collection } } = columnDef;
const labelName = (internalColumnEditor.customStructure) ? internalColumnEditor.customStructure.label : 'label';
const valueName = (internalColumnEditor.customStructure) ? internalColumnEditor.customStructure.value : 'value';

if (Array.isArray(value)) {
return arrayToCsvFormatter(row,
cell,
value.map((v: any) => findOrDefault(collection, (c: any) => c[valueName] === v)[labelName]),
columnDef,
dataContext);
}

return findOrDefault(collection, (c: any) => c[valueName] === value)[labelName] || '';
};
12 changes: 11 additions & 1 deletion aurelia-slickgrid/src/aurelia-slickgrid/formatters/index.ts
Expand Up @@ -3,6 +3,8 @@ import { arrayToCsvFormatter } from './arrayToCsvFormatter';
import { boldFormatter } from './boldFormatter';
import { checkboxFormatter } from './checkboxFormatter';
import { checkmarkFormatter } from './checkmarkFormatter';
import { collectionFormatter } from './collectionFormatter';
import { collectionEditorFormatter } from './collectionEditorFormatter';
import { complexObjectFormatter } from './complexObjectFormatter';
import { dateIsoFormatter } from './dateIsoFormatter';
import { dateTimeIsoAmPmFormatter } from './dateTimeIsoAmPmFormatter';
Expand All @@ -27,7 +29,6 @@ import { translateFormatter } from './translateFormatter';
import { translateBooleanFormatter } from './translateBooleanFormatter';
import { uppercaseFormatter } from './uppercaseFormatter';
import { yesNoFormatter } from './yesNoFormatter';
import { collectionFormatter } from './collectionFormatter';

/** Provides a list of different Formatters that will change the cell value displayed in the UI */
export const Formatters = {
Expand Down Expand Up @@ -59,6 +60,15 @@ export const Formatters = {
*/
collection: collectionFormatter,

/**
* Looks up values from the columnDefinition.editor.collection property and displays the label in CSV or string format
* @example
* // the grid will display 'foo' and 'bar' and not 1 and 2 from your dataset
* { params: { collection: [{ value: 1, label: 'foo'}, {value: 2, label: 'bar' }] }}
* const dataset = [{ value: 1 },{ value: 2 }];
*/
collectionEditor: collectionEditorFormatter,

/** Takes a Date object and displays it as an ISO Date format */
dateIso: dateIsoFormatter,

Expand Down
@@ -1,5 +1,7 @@
import { ColumnEditor } from './columnEditor.interface';
import { ColumnFilter } from './columnFilter.interface';
import { Editor } from './editor.interface';
import { EditorType } from './editorType.enum';
import { FieldType } from './fieldType.enum';
import { Formatter } from './formatter.interface';
import { GroupTotalsFormatter } from './groupTotalsFormatter.interface';
Expand Down Expand Up @@ -28,7 +30,7 @@ export interface Column {
defaultSortAsc?: boolean;

/** Inline editor for the cell value */
editor?: Editor | any;
editor?: EditorType | ColumnEditor;

/** Default to false, which leads to exclude the column from the export? */
excludeFromExport?: boolean;
Expand Down Expand Up @@ -108,6 +110,11 @@ export interface Column {
/** ID of the column, each row have to be unique or SlickGrid will throw an error. */
id: number | string;

/**
* @internal used internally by Aurelia-Slickgrid, to copy over the Column Editor Options
*/
internalColumnEditor?: any;

/** is the column editable? Goes with grid option "editable: true". */
isEditable?: boolean;

Expand Down
@@ -0,0 +1,51 @@
import { EditorType } from './editorType.enum';
import { TextEditor } from './../editors/textEditor';
import { SingleSelectEditor } from './../editors/singleSelectEditor';
import { MultipleSelectEditor } from './../editors/multipleSelectEditor';
import { LongTextEditor } from './../editors/longTextEditor';
import { IntegerEditor } from './../editors/integerEditor';
import { FloatEditor } from './../editors/floatEditor';
import { DateEditor } from './../editors/dateEditor';
import { CheckboxEditor } from './../editors/checkboxEditor';
import {
CollectionFilterBy,
CollectionSortBy,
Editor,
MultipleSelectOption
} from './../models/index';

export interface ColumnEditor {
/** Custom Editor */
customEditor?: any;

/** Editor Type to use (input, multipleSelect, singleSelect, select, custom) */
type?: EditorType;

collection?: any[];

/** We could filter some items from the collection */
collectionFilterBy?: CollectionFilterBy;

/** We could sort the collection by their value, or by translated value when enableTranslateLabel is True */
collectionSortBy?: CollectionSortBy;

/** Options that could be provided to the Editor, example: { container: 'body', maxHeight: 250} */
editorOptions?: MultipleSelectOption | any;

/** Do we want the Editor to handle translation (localization)? */
enableTranslateLabel?: boolean;

/** A custom structure can be used instead of the default label/value pair. Commonly used with Select/Multi-Select Editor */
customStructure?: {
label: string;
value: string;
};

/**
* Use "params" to pass any type of arguments to your Custom Editor (type: EditorType.custom)
* or regular Editor like the EditorType.float
* for example, to pass the option collection to a select Filter we can type this:
* params: { decimalPlaces: 2 }
*/
params?: any;
}
28 changes: 28 additions & 0 deletions aurelia-slickgrid/src/aurelia-slickgrid/models/editorType.enum.ts
@@ -0,0 +1,28 @@
export enum EditorType {
/** Custom Editor type */
custom,

/** Checkbox Editor */
checkbox,

/** Date Picker Editor */
date,

/** Float Editor */
float,

/** Integer Editor */
integer,

/** Long Text Editor */
longText,

/** Multiple Select Editor */
multipleSelect,

/** Single Select Editor */
singleSelect,

/** Text Editor */
text,
}

0 comments on commit 0ca7bf8

Please sign in to comment.