From 7421e595875354145c3270d90baf28a9c70002da Mon Sep 17 00:00:00 2001 From: Ivica Batinic Date: Sat, 22 May 2021 11:40:52 +0200 Subject: [PATCH] Add support for compare fn on View.sortMethod --- packages/datx/src/View.ts | 12 ++++++-- packages/datx/src/helpers/view.ts | 5 ++++ packages/datx/src/types.ts | 3 ++ packages/datx/test/view.test.ts | 46 ++++++++++++++++++++++++++++--- 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 packages/datx/src/helpers/view.ts create mode 100644 packages/datx/src/types.ts diff --git a/packages/datx/src/View.ts b/packages/datx/src/View.ts index 7f446284d..b31082bbd 100644 --- a/packages/datx/src/View.ts +++ b/packages/datx/src/View.ts @@ -3,6 +3,7 @@ import { IArraySplice, IRawModel, mapItems, mobx, removeFromArray, replaceInArra import { ToMany } from './buckets/ToMany'; import { error } from './helpers/format'; import { getModelId, getModelType, isReference } from './helpers/model/utils'; +import { isPropertySelectorFn } from './helpers/view'; import { IIdentifier } from './interfaces/IIdentifier'; import { IModelConstructor } from './interfaces/IModelConstructor'; import { IModelRef } from './interfaces/IModelRef'; @@ -11,17 +12,18 @@ import { IType } from './interfaces/IType'; import { TChange } from './interfaces/TChange'; import { PureCollection } from './PureCollection'; import { PureModel } from './PureModel'; +import { SortMethod } from './types'; export class View extends ToMany { public readonly modelType: IType; @mobx.observable - public sortMethod?: string | ((item: T) => any); + public sortMethod?: SortMethod; constructor( modelType: IModelConstructor | IType, protected __collection: PureCollection, - sortMethod?: string | ((item: T) => any), + sortMethod?: SortMethod, models: Array = [], public unique: boolean = false, ) { @@ -50,7 +52,11 @@ export class View extends ToMany { ? (item): any => item[this.sortMethod as 'string'] : this.sortMethod; - list.sort((a: T, b: T) => (sortFn(a) === sortFn(b) ? 0 : sortFn(a) > sortFn(b) ? 1 : -1)); + if (isPropertySelectorFn(sortFn)) { + list.sort((a: T, b: T) => (sortFn(a) === sortFn(b) ? 0 : sortFn(a) > sortFn(b) ? 1 : -1)); + } else { + list.sort(sortFn); + } } const instances = mobx.observable.array(list, { deep: false }); diff --git a/packages/datx/src/helpers/view.ts b/packages/datx/src/helpers/view.ts new file mode 100644 index 000000000..44b830e47 --- /dev/null +++ b/packages/datx/src/helpers/view.ts @@ -0,0 +1,5 @@ +import { PropertySelectorFn, CompareFn } from '../types'; + +export function isPropertySelectorFn(fn: PropertySelectorFn | CompareFn): fn is PropertySelectorFn { + return fn.length === 1; +} diff --git a/packages/datx/src/types.ts b/packages/datx/src/types.ts new file mode 100644 index 000000000..554f2f7e3 --- /dev/null +++ b/packages/datx/src/types.ts @@ -0,0 +1,3 @@ +export type PropertySelectorFn = (item: T) => any; +export type CompareFn = (a: T, b: T) => number; +export type SortMethod = string | PropertySelectorFn | CompareFn; diff --git a/packages/datx/test/view.test.ts b/packages/datx/test/view.test.ts index 4c0fb7f0a..86c9dc983 100644 --- a/packages/datx/test/view.test.ts +++ b/packages/datx/test/view.test.ts @@ -239,6 +239,44 @@ describe('View', () => { expect(item2b && item2b.key).toBe(2); }); + it('should be able to sort with compare function and non-unique props', () => { + class Foo extends Model { + public static type = 'foo'; + + @Attribute() + public notUnique!: number; + @Attribute() + public unique!: number; + } + class AppCollection extends Collection { + public static types = [Foo]; + } + + const collection = new AppCollection(); + const foos = collection.add([{ notUnique: 2, unique: 3 }, { notUnique: 2, unique: 1 }, { notUnique: 1, unique: 2 }], Foo); + + const compareFn = (a: Foo, b: Foo): number => { + if (a.notUnique === b.notUnique){ + return a.unique < b.unique ? -1 : 1 + } else { + return a.notUnique < b.notUnique ? -1 : 1 + } + } + + const viewInstance = new View(Foo, collection, compareFn, foos); + + expect(viewInstance.length).toBe(3); + const item0a = viewInstance.list[0]; + const item1a = viewInstance.list[1]; + const item2a = viewInstance.list[2]; + expect(item0a && item0a.notUnique).toBe(1); + expect(item0a && item0a.unique).toBe(2); + expect(item1a && item1a.notUnique).toBe(2); + expect(item1a && item1a.unique).toBe(1); + expect(item2a && item2a.notUnique).toBe(2); + expect(item2a && item2a.unique).toBe(3); + }); + it('should be able to remove models', () => { class Foo extends Model { public static type = 'foo'; @@ -277,19 +315,19 @@ describe('View', () => { if (mobx.useRealMobX) { viewInstance.list.push(foo1); expect(viewInstance).toHaveLength(4); - + viewInstance.list.unshift(foo2); expect(viewInstance).toHaveLength(5); - + viewInstance.list.splice(2, 2); expect(viewInstance).toHaveLength(3); } else { viewInstance.list = [...viewInstance.list, foo1]; expect(viewInstance).toHaveLength(4); - + viewInstance.list = [foo2, ...viewInstance.list]; expect(viewInstance).toHaveLength(5); - + viewInstance.list = [...viewInstance.list.slice(0, 2), ...viewInstance.list.slice(4)]; expect(viewInstance).toHaveLength(3); }