Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(addon-table): SortBy add directive for server-side sorting by column name #1659

Merged
merged 2 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
AbstractTuiNullableControl,
isNativeFocused,
isNativeFocusedIn,
setNativeFocused,
TUI_FOCUSABLE_ITEM_ACCESSOR,
TuiBooleanHandler,
TuiCreditCardAutofillName,
Expand Down Expand Up @@ -338,9 +337,7 @@ export class TuiInputCardGroupedComponent
this.open = false;
this.expireInert = !!expire;

if (element) {
setNativeFocused(element);
}
element?.focus();
}

onCardChange(card: string) {
Expand All @@ -355,7 +352,7 @@ export class TuiInputCardGroupedComponent
this.updateBin(bin);

if (this.cardValidator(this.card) && !this.expire && this.inputExpire) {
setNativeFocused(this.inputExpire.nativeElement, true, true);
this.focusExpire();
}
}

Expand All @@ -377,8 +374,8 @@ export class TuiInputCardGroupedComponent
this.inputExpire.nativeElement.value = expire;
this.updateProperty(expire, 'expire');

if (expire.length === 5 && this.inputCVC) {
setNativeFocused(this.inputCVC.nativeElement);
if (expire.length === 5) {
this.focusCVC();
}
}

Expand Down Expand Up @@ -410,10 +407,7 @@ export class TuiInputCardGroupedComponent

clear() {
this.updateValue(null);

if (this.inputCard) {
setNativeFocused(this.inputCard.nativeElement);
}
this.focusCard();
}

toggle() {
Expand All @@ -428,6 +422,19 @@ export class TuiInputCardGroupedComponent
this.expireInert = !!this.expire && this.cardPrefilled;
}

/** Public API for manual focus management */
focusCard() {
this.inputCard?.nativeElement.focus();
}

focusExpire() {
this.inputExpire?.nativeElement.focus({preventScroll: true});
}

focusCVC() {
this.inputCVC?.nativeElement.focus();
}

private get cardFocused(): boolean {
return !!this.inputCard && isNativeFocused(this.inputCard.nativeElement);
}
Expand Down Expand Up @@ -472,8 +479,6 @@ export class TuiInputCardGroupedComponent
(this.expireFocusable && this.inputExpire?.nativeElement) ||
this.inputCVC?.nativeElement;

if (element) {
setNativeFocused(element);
}
element?.focus();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
ContentChildren,
Directive,
Inject,
Input,
Output,
QueryList,
} from '@angular/core';
import {TuiComparator} from '@taiga-ui/addon-table/types';
import {EMPTY_QUERY, tuiDefaultProp} from '@taiga-ui/cdk';
import {filter, map} from 'rxjs/operators';

import {TuiSortableDirective} from './sortable.directive';
import {TuiTableDirective} from './table.directive';

@Directive({
selector: 'table[tuiTable][tuiSortBy]',
})
export class TuiSortByDirective<T> {
@ContentChildren(TuiSortableDirective, {descendants: true})
private readonly sortables: QueryList<TuiSortableDirective<T>> = EMPTY_QUERY;

@Input()
@tuiDefaultProp()
tuiSortBy: keyof T | string | null = null;

@Output()
readonly tuiSortByChange = this.table.sorterChange.pipe(
filter(() => !!this.sortables.length),
map(sorter => this.getKey(sorter)),
);

constructor(
@Inject(TuiTableDirective) private readonly table: TuiTableDirective<T>,
) {}

private getKey(sorter: TuiComparator<T> | null): keyof T | null {
return this.sortables.find(s => s.sorter === sorter)?.key ?? null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {Directive, DoCheck, Inject} from '@angular/core';

import {TuiThComponent} from '../th/th.component';
import {TuiSortByDirective} from './sort-by.directive';
import {TuiTableDirective} from './table.directive';

@Directive({
selector: 'th[tuiTh][tuiSortable]',
})
export class TuiSortableDirective<T> implements DoCheck {
constructor(
@Inject(TuiSortByDirective) private readonly sortBy: TuiSortByDirective<T>,
@Inject(TuiTableDirective) private readonly table: TuiTableDirective<T>,
@Inject(TuiThComponent) private readonly th: TuiThComponent<T>,
) {
this.th.sorter = this.sorter;
}

readonly sorter = () => 0;
splincode marked this conversation as resolved.
Show resolved Hide resolved

get key(): keyof T {
return this.th.key;
}

ngDoCheck() {
if (this.sortBy.tuiSortBy === this.key && this.table.sorter !== this.sorter) {
this.table.updateSorter(this.sorter);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ export class TuiTableDirective<T> extends TuiController {
@tuiDefaultProp()
sorter: TuiComparator<T> = () => 0;

updateSorter(sorter: TuiComparator<T>) {
updateSorter(sorter: TuiComparator<T> | null) {
if (this.sorter === sorter) {
this.direction = this.direction === 1 ? -1 : 1;
this.directionChange.emit(this.direction);
} else {
this.sorter = sorter;
this.sorter = sorter || (() => 0);
waterplea marked this conversation as resolved.
Show resolved Hide resolved
this.sorterChange.emit(this.sorter);
this.direction = 1;
this.directionChange.emit(1);
Expand Down
2 changes: 2 additions & 0 deletions projects/addon-table/components/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export * from './directives/cell.directive';
export * from './directives/head.directive';
export * from './directives/resized.directive';
export * from './directives/row.directive';
export * from './directives/sort-by.directive';
export * from './directives/sortable.directive';
export * from './directives/table.directive';
export * from './directives/thead.directive';
export * from './pipes/table-sort.pipe';
Expand Down
6 changes: 6 additions & 0 deletions projects/addon-table/components/table/table.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {TuiCellDirective} from './directives/cell.directive';
import {TuiHeadDirective} from './directives/head.directive';
import {TuiResizedDirective} from './directives/resized.directive';
import {TuiRowDirective} from './directives/row.directive';
import {TuiSortByDirective} from './directives/sort-by.directive';
import {TuiSortableDirective} from './directives/sortable.directive';
import {TuiTableDirective} from './directives/table.directive';
import {TuiTheadDirective} from './directives/thead.directive';
import {TuiTableSortPipe} from './pipes/table-sort.pipe';
Expand All @@ -29,6 +31,8 @@ import {TuiTrComponent} from './tr/tr.component';
TuiCellDirective,
TuiHeadDirective,
TuiRowDirective,
TuiSortByDirective,
TuiSortableDirective,
TuiTheadDirective,
TuiResizedDirective,
TuiTableSortPipe,
Expand All @@ -43,6 +47,8 @@ import {TuiTrComponent} from './tr/tr.component';
TuiCellDirective,
TuiHeadDirective,
TuiRowDirective,
TuiSortByDirective,
TuiSortableDirective,
TuiTheadDirective,
TuiTableSortPipe,
],
Expand Down
10 changes: 5 additions & 5 deletions projects/demo/src/modules/tables/table/examples/4/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,31 @@
tuiTable
class="table"
[columns]="columns"
[sorter]="sorter$ | async"
[direction]="direction$ | async"
(sorterChange)="sorter$.next($event)"
[tuiSortBy]="sorter$ | async"
(tuiSortByChange)="sorter$.next($event)"
(directionChange)="direction$.next($event)"
>
<thead>
<tr tuiThGroup>
<th
*tuiHead="'name'"
tuiTh
[sorter]="sorters.name"
tuiSortable
>
Name
</th>
<th
*tuiHead="'dob'"
tuiTh
[sorter]="sorters.dob"
tuiSortable
>
Date of Birth
</th>
<th
*tuiHead="'age'"
tuiTh
[sorter]="sorters.age"
tuiSortable
>
Age
</th>
Expand Down
32 changes: 7 additions & 25 deletions projects/demo/src/modules/tables/table/examples/4/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
tuiReplayedValueChangesFrom,
} from '@taiga-ui/cdk';
import {TUI_ARROW} from '@taiga-ui/kit';
import {BehaviorSubject, combineLatest, Observable, Subject, timer} from 'rxjs';
import {BehaviorSubject, combineLatest, Observable, timer} from 'rxjs';
import {
debounceTime,
filter,
Expand Down Expand Up @@ -76,26 +76,19 @@ const KEYS: Record<string, Key> = {
encapsulation,
})
export class TuiTableExample4 {
private readonly size$ = new Subject<number>();
private readonly page$ = new Subject<number>();

readonly sorters: Record<Key, TuiComparator<User>> = {
name: () => 0,
dob: () => 0,
age: () => 0,
};
private readonly size$ = new BehaviorSubject(10);
private readonly page$ = new BehaviorSubject(0);

readonly direction$ = new BehaviorSubject<-1 | 1>(1);

readonly sorter$ = new BehaviorSubject<TuiComparator<User>>(this.sorters.name);
readonly sorter$ = new BehaviorSubject<Key>('name');

readonly minAge = new FormControl(21);

readonly request$ = combineLatest([
this.sorter$.pipe(map(sorter => getKey(sorter, this.sorters))),
this.sorter$,
this.direction$,
this.page$.pipe(startWith(0)),
this.size$.pipe(startWith(10)),
this.page$,
this.size$,
tuiReplayedValueChangesFrom<number>(this.minAge),
]).pipe(
// zero time debounce for a case when both key and direction change
Expand Down Expand Up @@ -176,17 +169,6 @@ export class TuiTableExample4 {
}
}

function getKey(
sorter: TuiComparator<User>,
dictionary: Record<Key, TuiComparator<User>>,
): Key {
const pair = Object.entries(dictionary).find<[Key, TuiComparator<User>]>(
(item): item is [Key, TuiComparator<User>] => item[1] === sorter,
);

return pair ? pair[0] : 'name';
}

function sortBy(key: 'name' | 'dob' | 'age', direction: -1 | 1): TuiComparator<User> {
return (a, b) =>
key === 'age'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
id="server"
i18n-heading
heading="Server sorting"
description="With tuiSortBy directive to work with column titles instead of sorters"
[content]="example4"
>
<tui-table-example-4></tui-table-example-4>
Expand Down