Skip to content

Commit c996f2f

Browse files
committed
chore(*): Merging with master
2 parents 2830b0f + df48102 commit c996f2f

33 files changed

+955
-569
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ For more detailed information, see the [README](https://github.com/IgniteUI/igni
4949
- A new boolean `hideGroupedColumns` input controls whether the grouped columns should be hidden as well (defaults to false).
5050
- **Breaking change** `cellClasses` input on `IgxColumnComponent` now accepts an object literal to allow conditional cell styling.
5151
- Exposing a mechanism for cells to grow according to their content.
52+
- `sortStrategy` input exposed to provide custom sort strategy for the `IgxColumnComponent`. The custom strategy should implement the `ISortingStrategy` interface, or can extend the base `SortingStrategy` class and override all or some of its public/protected members.
5253
- `igxFor`
5354
- Added support for variable heights.
5455
- `igx-datePicker` selector is deprecated. Use `igx-date-picker` selector instead.
@@ -155,6 +156,8 @@ When you focus a specific cell and press one of the following key combinations,
155156

156157
## 6.1.8
157158

159+
### General
160+
158161
### Bug fixes
159162

160163
- Fix sorting and groupby expression not syncing when there are already sorted columns. #2786

gulpfile.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ gulp.task('typedoc-build:doc:ja:localization', ['typedoc-build', 'typedoc:clean-
247247
shell.task(`typedoc ${TYPEDOC.PROJECT_PATH} --generate-from-json ${TYPEDOC.OUTPUT_PATH}/${TYPEDOC.REPO.TRANSLATIONS_REPO_NAME}/ja/ --templateStrings ${TYPEDOC.TEMPLATE_STRINGS_PATH} --localize jp`)
248248
);
249249

250+
gulp.task('typedoc-build:doc:en:localization', ['typedoc-build', 'typedoc:clean-docs-dir', 'typedoc:copy-translations'],
251+
shell.task(`typedoc ${TYPEDOC.PROJECT_PATH} --generate-from-json ${TYPEDOC.OUTPUT_PATH}/${TYPEDOC.REPO.TRANSLATIONS_REPO_NAME}/en/`)
252+
);
253+
250254
gulp.task('typedoc-serve', ['typedoc-watch'], () => {
251255
browserSync.init({
252256
server: './dist/igniteui-angular/docs/typescript'

projects/igniteui-angular/src/lib/calendar/calendar.component.spec.ts

Lines changed: 476 additions & 296 deletions
Large diffs are not rendered by default.

projects/igniteui-angular/src/lib/calendar/calendar.component.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
IgxCalendarSubheaderTemplateDirective
2323
} from './calendar.directives';
2424
import { DateRangeDescriptor, DateRangeType } from '../core/dates/dateRange';
25+
import { isDate } from 'util';
2526

2627
let NEXT_ID = 0;
2728

@@ -830,6 +831,10 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
830831
*```
831832
*/
832833
public selectDate(value: Date | Date[]) {
834+
if (value === null || value === undefined || (Array.isArray(value) && value.length === 0)) {
835+
throw new Error('Date or array should be set for the selectDate method.');
836+
}
837+
833838
switch (this.selection) {
834839
case 'single':
835840
this.selectSingle(value as Date);
@@ -850,8 +855,13 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
850855
*````
851856
*/
852857
public deselectDate(value?: Date | Date[]) {
858+
if (this.selectedDates === null || this.selectedDates === []) {
859+
return;
860+
}
861+
853862
if (value === null || value === undefined) {
854863
this.selectedDates = this.selection === 'single' ? null : [];
864+
this._onChangeCallback(this.selectedDates);
855865
return;
856866
}
857867

@@ -1164,8 +1174,8 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
11641174
if (Array.isArray(value)) {
11651175
this.rangeStarted = false;
11661176
value.sort((a: Date, b: Date) => a.valueOf() - b.valueOf());
1167-
start = this.getDateOnly(value.shift());
1168-
end = this.getDateOnly(value.pop());
1177+
start = this.getDateOnly(value[0]);
1178+
end = this.getDateOnly(value[value.length - 1]);
11691179
this.selectedDates = [start, ...this.generateDateRange(start, end)];
11701180
} else {
11711181
if (!this.rangeStarted) {
@@ -1201,7 +1211,7 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
12011211
* @hidden
12021212
*/
12031213
private deselectSingle(value: Date) {
1204-
if (this.selectedDates !== null && value !== null &&
1214+
if (this.selectedDates !== null &&
12051215
this.getDateOnlyInMs(value as Date) === this.getDateOnlyInMs(this.selectedDates)) {
12061216
this.selectedDates = null;
12071217
this._onChangeCallback(this.selectedDates);
@@ -1213,10 +1223,6 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
12131223
* @hidden
12141224
*/
12151225
private deselectMultiple(value: Date[]) {
1216-
if (value === null) {
1217-
return;
1218-
}
1219-
12201226
value = value.filter(v => v !== null);
12211227
const selectedDatesCount = this.selectedDates.length;
12221228
const datesInMsToDeselect: Set<number> = new Set<number>(
@@ -1238,21 +1244,23 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
12381244
* @hidden
12391245
*/
12401246
private deselectRange(value: Date[]) {
1241-
if (value === null) {
1242-
return;
1243-
}
1244-
1245-
value = value.filter(v => v !== null);
1246-
if (value.length < 2) {
1247+
value = value.filter(v => v !== null && isDate(v));
1248+
if (value.length < 1) {
12471249
return;
12481250
}
12491251

12501252
value.sort((a: Date, b: Date) => a.valueOf() - b.valueOf());
1251-
const start = this.getDateOnly(value.shift());
1252-
const end = this.getDateOnly(value.pop());
1253+
const valueStart = this.getDateOnlyInMs(value[0]);
1254+
const valueEnd = this.getDateOnlyInMs(value[value.length - 1]);
1255+
1256+
this.selectedDates.sort((a: Date, b: Date) => a.valueOf() - b.valueOf());
1257+
const selectedDatesStart = this.getDateOnlyInMs(this.selectedDates[0]);
1258+
const selectedDatesEnd = this.getDateOnlyInMs(this.selectedDates[this.selectedDates.length - 1]);
12531259

1254-
const deselectRange = [start, ...this.generateDateRange(start, end)];
1255-
this.deselectMultiple(deselectRange);
1260+
if (!(valueEnd < selectedDatesStart) && !(valueStart > selectedDatesEnd)) {
1261+
this.selectedDates = [];
1262+
this._onChangeCallback(this.selectedDates);
1263+
}
12561264
}
12571265

12581266
/**

projects/igniteui-angular/src/lib/core/styles/base/utilities/_functions.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,5 +401,9 @@
401401
@return map-get($themes, $theme);
402402
}
403403

404-
@return call(get-function(#{$theme}-theme));
404+
@if (function-exists('get-function')) {
405+
@return call(get-function(#{$theme}-theme));
406+
} @else {
407+
@return call((#{$theme}-theme));
408+
}
405409
}

projects/igniteui-angular/src/lib/core/utils.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,20 @@ export function isDate(value: any) {
8787
return Object.prototype.toString.call(value) === '[object Date]';
8888
}
8989

90+
/**
91+
* Cehcks if the two passed arguments are equal
92+
* Currently supports date objects
93+
* @param obj1
94+
* @param obj2
95+
* @returns: `boolean`
96+
*/
97+
export function isEqual(obj1, obj2): boolean {
98+
if (isDate(obj1) && isDate(obj2)) {
99+
return obj1.getTime() === obj2.getTime();
100+
}
101+
return obj1 === obj2;
102+
}
103+
90104
/**
91105
*@hidden
92106
*/

projects/igniteui-angular/src/lib/data-operations/sorting-expression.interface.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ISortingStrategy } from './sorting-strategy';
2+
13
/**
24
* Represents sorting expressions.
35
*/
@@ -11,4 +13,5 @@ export interface ISortingExpression {
1113
fieldName: string;
1214
dir: SortingDirection;
1315
ignoreCase?: boolean;
16+
strategy?: ISortingStrategy;
1417
}

projects/igniteui-angular/src/lib/data-operations/sorting-strategy.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,18 @@ export class SortingStrategy implements ISortingStrategy {
3838
}
3939
return a > b ? 1 : a < b ? -1 : 0;
4040
}
41-
protected compareObjects(obj1: object, obj2: object, key: string, reverse: number, ignoreCase: boolean) {
41+
protected compareObjects(obj1: object, obj2: object, key: string, reverse: number, ignoreCase: boolean, strategy: ISortingStrategy) {
4242
let a = obj1[key];
4343
let b = obj2[key];
4444
if (ignoreCase) {
4545
a = a && a.toLowerCase ? a.toLowerCase() : a;
4646
b = b && b.toLowerCase ? b.toLowerCase() : b;
4747
}
48-
return reverse * this.compareValues(a, b);
48+
if (strategy) {
49+
return reverse * strategy.compareValues(a, b);
50+
} else {
51+
return reverse * this.compareValues(a, b);
52+
}
4953
}
5054
protected arraySort<T>(data: T[], compareFn?): T[] {
5155
return data.sort(compareFn);
@@ -78,7 +82,7 @@ export class SortingStrategy implements ISortingStrategy {
7882
false;
7983
const reverse = (expression.dir === SortingDirection.Desc ? -1 : 1);
8084
const cmpFunc = (obj1, obj2) => {
81-
return this.compareObjects(obj1, obj2, key, reverse, ignoreCase);
85+
return this.compareObjects(obj1, obj2, key, reverse, ignoreCase, expression.strategy);
8286
};
8387
return this.arraySort(data, cmpFunc);
8488
}

projects/igniteui-angular/src/lib/drop-down/drop-down.component.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ let NEXT_ID = 0;
3333
*
3434
* @export
3535
*/
36-
export interface ISelectionEventArgs {
36+
export interface ISelectionEventArgs extends CancelableEventArgs {
3737
oldSelection: IgxDropDownItemBase;
3838
newSelection: IgxDropDownItemBase;
39-
cancel: boolean;
4039
}
4140

4241
/** @hidden */
@@ -419,7 +418,7 @@ export class IgxDropDownBase implements OnInit, IToggleView {
419418
* @hidden
420419
*/
421420
onToggleOpening(e: CancelableEventArgs) {
422-
const eventArgs = { cancel: false};
421+
const eventArgs = { cancel: false };
423422
this.onOpening.emit(eventArgs);
424423
e.cancel = eventArgs.cancel;
425424
if (eventArgs.cancel) {
@@ -449,7 +448,7 @@ export class IgxDropDownBase implements OnInit, IToggleView {
449448
* @hidden
450449
*/
451450
onToggleClosing(e: CancelableEventArgs) {
452-
const eventArgs = { cancel: false};
451+
const eventArgs = { cancel: false };
453452
this.onClosing.emit(eventArgs);
454453
e.cancel = eventArgs.cancel;
455454
}
@@ -605,7 +604,7 @@ export class IgxDropDownItemNavigationDirective {
605604
const key = event.code ? event.code.toLowerCase() : event.key.toLowerCase();
606605
if (!this.target.collapsed) { // If dropdown is opened
607606
const navKeys = ['esc', 'escape', 'enter', 'tab', 'space', 'spacebar',
608-
'arrowup', 'up', 'arrowdown', 'down', 'home', 'end'];
607+
'arrowup', 'up', 'arrowdown', 'down', 'home', 'end'];
609608
if (navKeys.indexOf(key) === -1) { // If key has appropriate function in DD
610609
return;
611610
}

projects/igniteui-angular/src/lib/grids/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ Inputs available on the **IgxGridColumnComponent** to define columns:
278278
|`field`|string|Column field name|
279279
|`header`|string|Column header text|
280280
|`sortable`|boolean|Set column to be sorted or not|
281+
|`sortStrategy`| Provide custom sort strategy to be used when sorting|
281282
|`editable`|boolean|Set column values to be editable|
282283
|`filterable`|boolean|Set column values to be filterable|
283284
|`hasSummary`| boolean |Sets whether or not the specific column has summaries enabled.|

projects/igniteui-angular/src/lib/grids/api.service.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable } from '@angular/core';
22
import { Subject } from 'rxjs';
3-
import { cloneArray } from '../core/utils';
3+
import { cloneArray, isEqual } from '../core/utils';
44
import { DataUtil } from '../data-operations/data-util';
55
import { IFilteringExpression, FilteringLogic } from '../data-operations/filtering-expression.interface';
66
import { IGroupByExpandState } from '../data-operations/groupby-expand-state.interface';
@@ -13,6 +13,8 @@ import { IgxRowComponent } from './row.component';
1313
import { IFilteringOperation } from '../data-operations/filtering-condition';
1414
import { IFilteringExpressionsTree, FilteringExpressionsTree } from '../data-operations/filtering-expressions-tree';
1515
import { Transaction, TransactionType } from '../services/index';
16+
import { ISortingStrategy } from '../data-operations/sorting-strategy';
17+
import { SortingStateDefaults } from '../data-operations/sorting-state.interface';
1618
/**
1719
*@hidden
1820
*/
@@ -285,8 +287,7 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
285287
}
286288

287289
// if edit (new) value is same as old value do nothing here
288-
if (oldValue !== undefined && oldValue === args.newValue) { return; }
289-
290+
if (oldValue !== undefined && isEqual(oldValue, args.newValue)) { return; }
290291
const transaction: Transaction = { id: rowID, type: TransactionType.UPDATE, newValue: { [column.field]: args.newValue } };
291292
if (grid.transactions.enabled) {
292293
grid.transactions.add(transaction, rowData);
@@ -329,13 +330,13 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
329330
}
330331
}
331332

332-
public sort(id: string, fieldName: string, dir: SortingDirection, ignoreCase: boolean): void {
333+
public sort(id: string, fieldName: string, dir: SortingDirection, ignoreCase: boolean, strategy: ISortingStrategy): void {
333334
if (dir === SortingDirection.None) {
334335
this.remove_grouping_expression(id, fieldName);
335336
}
336337
const sortingState = cloneArray(this.get(id).sortingExpressions);
337-
338-
this.prepare_sorting_expression([sortingState], { fieldName, dir, ignoreCase });
338+
strategy = strategy ? strategy : this.getSortStrategyPerColumn(id, fieldName);
339+
this.prepare_sorting_expression([sortingState], { fieldName, dir, ignoreCase, strategy });
339340
this.get(id).sortingExpressions = sortingState;
340341
}
341342

@@ -346,6 +347,7 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
346347
if (each.dir === SortingDirection.None) {
347348
this.remove_grouping_expression(id, each.fieldName);
348349
}
350+
each.strategy = each.strategy ? each.strategy : this.getSortStrategyPerColumn(id, each.fieldName);
349351
this.prepare_sorting_expression([sortingState], each);
350352
}
351353

@@ -494,4 +496,9 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
494496

495497
protected remove_grouping_expression(id, fieldName) {
496498
}
499+
500+
protected getSortStrategyPerColumn(id: string, fieldName: string) {
501+
return this.get_column_by_name(this.get(id).id, fieldName) ?
502+
this.get_column_by_name(id, fieldName).sortStrategy : undefined;
503+
}
497504
}

projects/igniteui-angular/src/lib/grids/cell.component.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -526,21 +526,17 @@ export class IgxGridCellComponent implements OnInit, AfterViewInit {
526526
if (this.selected) {
527527
return;
528528
}
529-
const editRowState = this.grid.rowEditable ? this.gridAPI.get_edit_row_state(this.gridID) : null; // Get current editted row
530-
const inEditRow = this.belongsToEditRow; // Check if cell is in current editable mode, if any
529+
const editRowState = this.grid.rowEditable ? this.gridAPI.get_edit_row_state(this.gridID) : null; // Get current edited row
531530
this._clearCellSelection();
532531
this._saveCellSelection();
533532
const hasFilteredResults = this.grid.filteredData ? this.grid.filteredData.length > 0 : true;
534533
if (hasFilteredResults) {
535534
if (this.column.editable && this.previousCellEditMode && hasFilteredResults) {
536535
this.inEditMode = true;
537536
}
538-
if (editRowState) { // If there is a row being edited
539-
if (inEditRow && !this.column.editable) { // and this cell is in the row and is NOT editable, submit the values and close
540-
this.exitRowEdit(true, true, editRowState);
541-
} else if (!inEditRow) { // or this is not in the editted row
542-
this.exitRowEdit(true, !this.column.editable, editRowState); // submit data and close the overlay depending on editable
543-
}
537+
if (editRowState && !this.inEditMode) {
538+
// If there is a row being edited & this cell did not enter edit mode (!editable, row.deleted)
539+
this.exitRowEdit(true, true, editRowState);
544540
}
545541
this.selected = true;
546542
if (fireFocus) {
@@ -550,14 +546,7 @@ export class IgxGridCellComponent implements OnInit, AfterViewInit {
550546
}
551547
}
552548

553-
private get belongsToEditRow(): boolean { // If the cell belongs to the row that is currently being edited
554-
const cellInEditMode = this.gridAPI.get_cell_inEditMode(this.gridID);
555-
if (cellInEditMode && this.grid.rowEditable) {
556-
return this.cellID.rowID === cellInEditMode.cellID.rowID;
557-
}
558-
return false;
559-
}
560-
549+
/** TODO: Refactor away, move to grid.endRowEdit! */
561550
private exitRowEdit(commit = true, close = true, row?: {rowID: any, rowIndex: number}) {
562551
const grid = this.grid;
563552
if (grid.rowEditable) {

0 commit comments

Comments
 (0)