From bb7b3af13ea56dc777e674b4fe68ccf77bda1728 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Thu, 5 Aug 2021 21:25:40 +0300 Subject: [PATCH] add sorting and utility types --- .eslintrc.json | 3 +- src/index.ts | 3 ++ .../circle-button/circle-button.component.ts | 4 +- .../sorting/models/sorting-option.model.ts | 6 +++ src/lib/sorting/models/sorting-order.type.ts | 14 +++++++ src/lib/sorting/sorting.service.ts | 42 +++++++++++++++++++ .../utils/decorators/required.decorator.ts | 2 +- src/lib/utils/types/tooltip-positions.type.ts | 6 ++- src/lib/utils/types/utility-types.ts | 38 +++++++++++++++++ 9 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 src/lib/sorting/models/sorting-option.model.ts create mode 100644 src/lib/sorting/models/sorting-order.type.ts create mode 100644 src/lib/sorting/sorting.service.ts create mode 100644 src/lib/utils/types/utility-types.ts diff --git a/.eslintrc.json b/.eslintrc.json index 009a6f6..1393c66 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -24,7 +24,8 @@ "prefix": "iqser", "style": "kebab-case" } - ] + ], + "no-param-reassign": "error" }, "plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"] }, diff --git a/src/index.ts b/src/index.ts index 9e12573..d495af5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,3 +11,6 @@ export * from './lib/filtering/filter-utils'; export * from './lib/filtering/models/filter-group.model'; export * from './lib/filtering/models/nested-filter.model'; export * from './lib/filtering/models/filter.model'; +export * from './lib/sorting/sorting.service'; +export * from './lib/sorting/models/sorting-option.model'; +export * from './lib/sorting/models/sorting-order.type'; diff --git a/src/lib/buttons/circle-button/circle-button.component.ts b/src/lib/buttons/circle-button/circle-button.component.ts index 1973f36..54fdea5 100644 --- a/src/lib/buttons/circle-button/circle-button.component.ts +++ b/src/lib/buttons/circle-button/circle-button.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, On import { MatTooltip } from '@angular/material/tooltip'; import { CircleButtonType, CircleButtonTypes } from './circle-button.type'; import { Required } from '../../utils/decorators/required.decorator'; -import { TooltipPositionsType, TooltipPositionsTypes } from '../../utils/types/tooltip-positions.type'; +import { IqserTooltipPosition, IqserTooltipPositions } from '../../utils/types/tooltip-positions.type'; @Component({ selector: 'iqser-circle-button', @@ -17,7 +17,7 @@ export class CircleButtonComponent implements OnInit { @Input() tooltip?: string; @Input() tooltipClass?: string; @Input() showDot = false; - @Input() tooltipPosition: TooltipPositionsType = TooltipPositionsTypes.above; + @Input() tooltipPosition: IqserTooltipPosition = IqserTooltipPositions.above; @Input() disabled = false; @Input() type: CircleButtonType = CircleButtonTypes.default; @Input() removeTooltip = false; diff --git a/src/lib/sorting/models/sorting-option.model.ts b/src/lib/sorting/models/sorting-option.model.ts new file mode 100644 index 0000000..d15a158 --- /dev/null +++ b/src/lib/sorting/models/sorting-option.model.ts @@ -0,0 +1,6 @@ +import { SortingOrder } from './sorting-order.type'; + +export interface SortingOption { + readonly order: SortingOrder; + readonly column: string; +} diff --git a/src/lib/sorting/models/sorting-order.type.ts b/src/lib/sorting/models/sorting-order.type.ts new file mode 100644 index 0000000..afb5738 --- /dev/null +++ b/src/lib/sorting/models/sorting-order.type.ts @@ -0,0 +1,14 @@ +import { NonFunctionKeys } from '../../utils/types/utility-types'; + +function inverseOf(order?: SortingOrder) { + if (order === undefined) return SortingOrders.asc; + return order === SortingOrders.asc ? SortingOrders.desc : SortingOrders.asc; +} + +export const SortingOrders = { + asc: 'asc', + desc: 'desc', + inverseOf: inverseOf +} as const; + +export type SortingOrder = NonFunctionKeys; diff --git a/src/lib/sorting/sorting.service.ts b/src/lib/sorting/sorting.service.ts new file mode 100644 index 0000000..bcc952e --- /dev/null +++ b/src/lib/sorting/sorting.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@angular/core'; +import { orderBy } from 'lodash'; +import { BehaviorSubject } from 'rxjs'; +import { SortingOption } from './models/sorting-option.model'; +import { SortingOrder, SortingOrders } from './models/sorting-order.type'; + +@Injectable() +export class SortingService { + private readonly _sortingOption$ = new BehaviorSubject(undefined); + readonly sortingOption$ = this._sortingOption$.asObservable(); + + get sortingOption(): SortingOption | undefined { + return this._sortingOption$.getValue(); + } + + setSortingOption(value: SortingOption): void { + this._sortingOption$.next(value); + } + + sort(values: T[], order?: SortingOrder, column?: string): T[] { + if (!values || values.length <= 1 || !order) return values; + + if (!column) { + /** sort 1d array */ + const result = [...values].sort(); + return order === SortingOrders.asc ? result : result.reverse(); + } + + return orderBy(values, [column], [order]); + } + + defaultSort(values: T[]): T[] { + return this.sort(values, this.sortingOption?.order, this.sortingOption?.column); + } + + toggleSort(column: string): void { + const sameColumn = this.sortingOption?.column === column; + const order = sameColumn ? SortingOrders.inverseOf(this.sortingOption?.order) : SortingOrders.asc; + + this._sortingOption$.next({ column, order }); + } +} diff --git a/src/lib/utils/decorators/required.decorator.ts b/src/lib/utils/decorators/required.decorator.ts index 4f3bf4b..3af62a3 100644 --- a/src/lib/utils/decorators/required.decorator.ts +++ b/src/lib/utils/decorators/required.decorator.ts @@ -1,4 +1,4 @@ -export function Required(message?: string) { +export function Required(message?: string): PropertyDecorator { return function (target: Object, propertyKey: PropertyKey) { Object.defineProperty(target, propertyKey, { get() { diff --git a/src/lib/utils/types/tooltip-positions.type.ts b/src/lib/utils/types/tooltip-positions.type.ts index 029ea2d..35f6e6a 100644 --- a/src/lib/utils/types/tooltip-positions.type.ts +++ b/src/lib/utils/types/tooltip-positions.type.ts @@ -1,8 +1,10 @@ -export const TooltipPositionsTypes = { +import { KeysOf } from './utility-types'; + +export const IqserTooltipPositions = { below: 'below', above: 'above', before: 'before', after: 'after' } as const; -export type TooltipPositionsType = keyof typeof TooltipPositionsTypes; +export type IqserTooltipPosition = KeysOf; diff --git a/src/lib/utils/types/utility-types.ts b/src/lib/utils/types/utility-types.ts new file mode 100644 index 0000000..82a2d1f --- /dev/null +++ b/src/lib/utils/types/utility-types.ts @@ -0,0 +1,38 @@ +export type KeysOf = keyof T; + +export type ValuesOf = T[KeysOf]; + +/** + * NonUndefined + * @desc Exclude undefined from set `A` + * @example + * // Expect: "string | null" + * SymmetricDifference; + */ +export type NonUndefined = T extends undefined ? never : T; + +/** + * FunctionKeys + * @desc Get union type of keys that are functions in object type `T` + * @example + * type MixedProps = {name: string; setName: (name: string) => void; someKeys?: string; someFn?: (...args: any) => any;}; + * + * // Expect: "setName | someFn" + * type Keys = FunctionKeys; + */ +export type FunctionKeys = { + [K in keyof T]-?: NonUndefined extends Function ? K : never; +}[keyof T]; + +/** + * NonFunctionKeys + * @desc Get union type of keys that are non-functions in object type `T` + * @example + * type MixedProps = {name: string; setName: (name: string) => void; someKeys?: string; someFn?: (...args: any) => any;}; + * + * // Expect: "name | someKey" + * type Keys = NonFunctionKeys; + */ +export type NonFunctionKeys = { + [K in keyof T]-?: NonUndefined extends Function ? never : K; +}[keyof T];