diff --git a/components/lib/calendar/Calendar.vue b/components/lib/calendar/Calendar.vue index 34c714399..ef0bdb962 100755 --- a/components/lib/calendar/Calendar.vue +++ b/components/lib/calendar/Calendar.vue @@ -2671,7 +2671,7 @@ export default { let innerHTML = ''; if (this.responsiveOptions) { - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; + const comparer = ObjectUtils.localeComparator(); let responsiveOptions = [...this.responsiveOptions].filter((o) => !!(o.breakpoint && o.numMonths)).sort((o1, o2) => -1 * comparer(o1.breakpoint, o2.breakpoint)); for (let i = 0; i < responsiveOptions.length; i++) { diff --git a/components/lib/carousel/Carousel.vue b/components/lib/carousel/Carousel.vue index fb1685e81..f0064fe85 100755 --- a/components/lib/carousel/Carousel.vue +++ b/components/lib/carousel/Carousel.vue @@ -548,7 +548,7 @@ export default { if (this.responsiveOptions && !this.isUnstyled) { let _responsiveOptions = [...this.responsiveOptions]; - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; + const comparer = ObjectUtils.localeComparator(); _responsiveOptions.sort((data1, data2) => { const value1 = data1.breakpoint; diff --git a/components/lib/datatable/BaseDataTable.vue b/components/lib/datatable/BaseDataTable.vue index 51015d0c7..d307f759b 100644 --- a/components/lib/datatable/BaseDataTable.vue +++ b/components/lib/datatable/BaseDataTable.vue @@ -78,6 +78,10 @@ export default { type: Number, default: 1 }, + nullSortOrder: { + type: Number, + default: 1 + }, multiSortMeta: { type: Array, default: null diff --git a/components/lib/datatable/DataTable.vue b/components/lib/datatable/DataTable.vue index 8ee20b53d..931878a38 100755 --- a/components/lib/datatable/DataTable.vue +++ b/components/lib/datatable/DataTable.vue @@ -341,6 +341,7 @@ export default { d_rows: this.rows, d_sortField: this.sortField, d_sortOrder: this.sortOrder, + d_nullSortOrder: this.nullSortOrder, d_multiSortMeta: this.multiSortMeta ? [...this.multiSortMeta] : [], d_groupRowsSortMeta: null, d_selectionKeys: null, @@ -381,6 +382,9 @@ export default { sortOrder(newValue) { this.d_sortOrder = newValue; }, + nullSortOrder(newValue) { + this.d_nullSortOrder = newValue; + }, multiSortMeta(newValue) { this.d_multiSortMeta = newValue; }, @@ -530,27 +534,19 @@ export default { } let data = [...value]; - let resolvedFieldDatas = new Map(); + let resolvedFieldData = new Map(); for (let item of data) { - resolvedFieldDatas.set(item, ObjectUtils.resolveFieldData(item, this.d_sortField)); + resolvedFieldData.set(item, ObjectUtils.resolveFieldData(item, this.d_sortField)); } - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; + const comparer = ObjectUtils.localeComparator(); data.sort((data1, data2) => { - let value1 = resolvedFieldDatas.get(data1); - let value2 = resolvedFieldDatas.get(data2); + let value1 = resolvedFieldData.get(data1); + let value2 = resolvedFieldData.get(data2); - let result = null; - - if (value1 == null && value2 != null) result = -1; - else if (value1 != null && value2 == null) result = 1; - else if (value1 == null && value2 == null) result = 0; - else if (typeof value1 === 'string' && typeof value2 === 'string') result = comparer(value1, value2); - else result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0; - - return this.d_sortOrder * result; + return ObjectUtils.sort(value1, value2, this.d_sortOrder, comparer, this.d_nullSortOrder); }); return data; @@ -579,23 +575,13 @@ export default { multisortField(data1, data2, index) { const value1 = ObjectUtils.resolveFieldData(data1, this.d_multiSortMeta[index].field); const value2 = ObjectUtils.resolveFieldData(data2, this.d_multiSortMeta[index].field); - let result = null; - - if (typeof value1 === 'string' || value1 instanceof String) { - if (value1.localeCompare && value1 !== value2) { - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; - - return this.d_multiSortMeta[index].order * comparer(value1, value2); - } - } else { - result = value1 < value2 ? -1 : 1; - } + const comparer = ObjectUtils.localeComparator(); if (value1 === value2) { return this.d_multiSortMeta.length - 1 > index ? this.multisortField(data1, data2, index + 1) : 0; } - return this.d_multiSortMeta[index].order * result; + return ObjectUtils.sort(value1, value2, this.d_multiSortMeta[index].order, comparer, this.d_nullSortOrder); }, addMultiSortField(field) { let index = this.d_multiSortMeta.findIndex((meta) => meta.field === field); diff --git a/components/lib/dataview/DataView.vue b/components/lib/dataview/DataView.vue index 4cc5d8854..95ca01936 100755 --- a/components/lib/dataview/DataView.vue +++ b/components/lib/dataview/DataView.vue @@ -110,7 +110,7 @@ export default { sort() { if (this.value) { const value = [...this.value]; - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; + const comparer = ObjectUtils.localeComparator(); value.sort((data1, data2) => { let value1 = ObjectUtils.resolveFieldData(data1, this.sortField); diff --git a/components/lib/galleria/GalleriaThumbnails.vue b/components/lib/galleria/GalleriaThumbnails.vue index acd89e8d0..f916eb6bc 100755 --- a/components/lib/galleria/GalleriaThumbnails.vue +++ b/components/lib/galleria/GalleriaThumbnails.vue @@ -438,7 +438,7 @@ export default { if (this.responsiveOptions && !this.isUnstyled) { this.sortedResponsiveOptions = [...this.responsiveOptions]; - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; + const comparer = ObjectUtils.localeComparator(); this.sortedResponsiveOptions.sort((data1, data2) => { const value1 = data1.breakpoint; diff --git a/components/lib/treetable/TreeTable.vue b/components/lib/treetable/TreeTable.vue index ca0dda448..d4635314d 100755 --- a/components/lib/treetable/TreeTable.vue +++ b/components/lib/treetable/TreeTable.vue @@ -429,7 +429,7 @@ export default { }, sortNodesSingle(nodes) { let _nodes = [...nodes]; - const comparer = new Intl.Collator(undefined, { numeric: true }).compare; + const comparer = ObjectUtils.localeComparator(); _nodes.sort((node1, node2) => { const value1 = ObjectUtils.resolveFieldData(node1.data, this.d_sortField); @@ -472,7 +472,7 @@ export default { return this.d_multiSortMeta.length - 1 > index ? this.multisortField(node1, node2, index + 1) : 0; } else { if ((typeof value1 === 'string' || value1 instanceof String) && (typeof value2 === 'string' || value2 instanceof String)) - return this.d_multiSortMeta[index].order * new Intl.Collator(undefined, { numeric: true }).compare(value1, value2); + return this.d_multiSortMeta[index].order * ObjectUtils.localeComparator().compare(value1, value2); else result = value1 < value2 ? -1 : 1; } } diff --git a/components/lib/utils/ObjectUtils.js b/components/lib/utils/ObjectUtils.js index e5e31629a..4f95ab537 100755 --- a/components/lib/utils/ObjectUtils.js +++ b/components/lib/utils/ObjectUtils.js @@ -302,6 +302,37 @@ export default { return index; }, + sort(value1, value2, order = 1, comparator, nullSortOrder = 1) { + const result = this.compare(value1, value2, comparator, order); + let finalSortOrder = order; + + // nullSortOrder == 1 means Excel like sort nulls at bottom + if (this.isEmpty(value1) || this.isEmpty(value2)) { + finalSortOrder = nullSortOrder === 1 ? order : nullSortOrder; + } + + return finalSortOrder * result; + }, + + compare(value1, value2, comparator, order = 1) { + let result = -1; + const emptyValue1 = this.isEmpty(value1); + const emptyValue2 = this.isEmpty(value2); + + if (emptyValue1 && emptyValue2) result = 0; + else if (emptyValue1) result = order; + else if (emptyValue2) result = -order; + else if (typeof value1 === 'string' && typeof value2 === 'string') result = comparator(value1, value2); + else result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0; + + return result; + }, + + localeComparator() { + //performance gain using Int.Collator. It is not recommended to use localeCompare against large arrays. + return new Intl.Collator(undefined, { numeric: true }).compare; + }, + nestedKeys(obj = {}, parentKey = '') { return Object.entries(obj).reduce((o, [key, value]) => { const currentKey = parentKey ? `${parentKey}.${key}` : key; diff --git a/components/lib/utils/Utils.d.ts b/components/lib/utils/Utils.d.ts index 2917f74e8..96daafc7d 100644 --- a/components/lib/utils/Utils.d.ts +++ b/components/lib/utils/Utils.d.ts @@ -89,6 +89,8 @@ export declare class ObjectUtils { static isPrintableCharacter(char: string): boolean; static findLast(value: any[], callback: () => any): any; static findLastIndex(value: any[], callback: () => any): number; + static sort(value1: any, value2: any, order: number, comparator: (a: any, b: any) => any, nullSortOrder: number): number; + static compare(value1: any, value2: any, comparator: (a: any, b: any) => any, order: number): number; static nestedKeys(obj: object, parentKey?: string): string[]; }