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

fix chips multiselect #1975 #1754 #2027

Merged
merged 7 commits into from
Sep 19, 2024
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
@@ -0,0 +1,32 @@
<div class="content">
<prizm-loader [showLoader]="isLoading" [overlay]="true" [size]="'xl'">
<div class="form__main-bottom">
<div class="control-outer">
<prizm-input-layout [outer]="true" label="Города" size="m">
<prizm-input-multi-select
[formControl]="control"
[searchable]="true"
[identityMatcher]="identityMatcher"
[items]="(controlList$ | async)!"
[stringify]="stringify"
placeholder="Выберите город"
></prizm-input-multi-select>
</prizm-input-layout>
</div>
</div>

<prizm-paginator
[totalRecords]="totalElements"
[page]="pageNumber"
[rows]="pageSize"
[rowsCountOptions]="[]"
[pageLinkSize]="7"
[paginatorOptions]="{
noRowsSelectorLabel: true,
noToFirstPageBtn: true,
noToLastPageBtn: true
}"
(paginatorChange)="changePage($event)"
></prizm-paginator>
</prizm-loader>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import {
PrizmMultiSelectIdentityMatcher,
PrizmMultiSelectItemStringifyFunc,
PrizmMultiSelectItemStringifyItem,
PrizmPaginatorOutput,
} from '@prizm-ui/components';
import { PrizmDestroyService } from 'libs/helpers/src/lib/services/destroy';
import { BehaviorSubject, Observable, delay, of, takeUntil, tap } from 'rxjs';

export const locations = [
{
content: [
{
id: '6a14e006-83a3-46cd-b3fe-bc67718f76ee',
name: 'Адлер',
},
{
id: 'ea93af81-f078-4815-818b-efbf5682b8eb',
name: 'Барнаул',
},
{
id: '5b89b3d3-6ec1-496b-86b0-b3bd558bc651',
name: 'Верхненовокутлумбетьево',
},
{
id: 'd21d6a35-0913-4cda-b6b7-ab2f90209578',
name: 'Иваново',
},
{
id: '4f832f01-b30c-41a8-8e41-ade841879673',
name: 'Калининград',
},
{
id: '98287645-538d-4a21-8b8c-6782e64c29b8',
name: 'Москва',
},
{
id: 'b140c019-8774-441c-972d-db013d129388',
name: 'Нижний Новгород',
},
{
id: '524fc404-fe15-4989-b282-ae45b7e16182',
name: 'Пермь',
},
{
id: '89c0980b-5b73-4b15-8f47-a31ac506e670',
name: 'Псков',
},
{
id: '45c4cc5d-ad10-41be-8456-6a92a19e6408',
name: 'Самара',
},
],
totalElements: 20,
},
{
content: [
{
id: '6a14e006-83a3-46cd-b3fe-bc67718f77ee',
name: 'Санкт-Петербург',
},
{
id: 'ea93af81-f078-4815-818b-efbf5682b7eb',
name: 'Сургут',
},
{
id: '5b89b3d3-6ec1-496b-86b0-b3bd558bc751',
name: 'Тверь',
},
{
id: 'd21d6a35-0913-4cda-b6b7-ab2f90209778',
name: 'Томск',
},
{
id: '4f832f01-b30c-41a8-8e41-ade841879773',
name: 'Тула',
},
{
id: '98287645-538d-4a21-8b8c-6782e67c29b8',
name: 'Ульяновск',
},
{
id: 'b140c019-8774-441c-972d-db017d129388',
name: 'Уфа',
},
{
id: '524fc404-fe15-4989-b282-ae47b7e16182',
name: 'Хабаровск',
},
{
id: '89c0980b-5b73-4b15-8f47-a71ac506e670',
name: 'Челябинск',
},
{
id: '45c4cc5d-ad10-41be-8456-7a92a19e6408',
name: 'Элиста ',
},
],
totalElements: 20,
},
];

export type PaginatedResult<T> = {
content: T[];
totalElements: number;
};

export type City = {
id: string;
name: string;
};

@Component({
selector: 'prizm-multi-select-async-example',
templateUrl: './multi-select-async-example.component.html',
styles: [
`
.box {
display: flex;
gap: 1rem;
}
`,
],
providers: [PrizmDestroyService],
})
export class PrizmInputMultiSelectAsyncExampleComponent implements OnInit {
public isLoading = true;
public pageNumber = 1;
public pageSize = 10;
public totalElements = 0;
public control: FormControl<City[] | null> = new FormControl([], []);
public controlList$ = new BehaviorSubject<City[] | undefined>(undefined);
public readonly backendList$ = new BehaviorSubject<City[]>([]);

public selectedList: City[] = [];

constructor(private readonly destroy$: PrizmDestroyService) {}

readonly identityMatcher: PrizmMultiSelectIdentityMatcher<City> = (a: City, b: City) => {
return a?.id === b?.id;
};
readonly stringify: PrizmMultiSelectItemStringifyFunc<City> = (
item: PrizmMultiSelectItemStringifyItem<City>
) => {
return item.obj?.name;
};

ngOnInit(): void {
this.control.valueChanges
.pipe(
tap(value => {
const currentPageSelectedList = this.backendList$.value?.filter(city =>
value?.some(el => el.id === city.id)
);

if (currentPageSelectedList) {
this.selectedList = [...this.selectedList, ...currentPageSelectedList];
}
}),
takeUntil(this.destroy$)
)
.subscribe();

this.getData().pipe(takeUntil(this.destroy$)).subscribe();
}

public changePage(event: PrizmPaginatorOutput): void {
this.pageNumber = event.page;
this.pageSize = event.rows;
this.getData().pipe(takeUntil(this.destroy$)).subscribe();
}

private getData(): Observable<PaginatedResult<{ id: string; name: string }>> {
this.isLoading = true;
return of(locations[this.pageNumber - 1] as PaginatedResult<{ id: string; name: string }>).pipe(
delay(1000),
tap(res => {
this.isLoading = false;
this.totalElements = res.totalElements;

const selectedNameList = this.selectedList.map(city => city.name);
const currentPageBackendnameList = res.content.map(city => city.name);

if (selectedNameList.every(name => currentPageBackendnameList.includes(name))) {
this.controlList$.next(res.content);
} else {
this.controlList$.next(Array.from(new Set([...this.selectedList, ...res.content])));
}
this.backendList$.next(res.content);
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
<prizm-multi-select-base-example></prizm-multi-select-base-example>
</prizm-doc-example>

<prizm-doc-example id="async" [content]="exampleAsync" heading="Async">
<prizm-multi-select-async-example></prizm-multi-select-async-example>
</prizm-doc-example>

<prizm-doc-example id="with-search" [content]="exampleWithSearch" heading="With Search">
<prizm-multi-select-with-search-example></prizm-multi-select-with-search-example>
</prizm-doc-example>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ export class InputInputMultiSelectComponent {
HTML: import('./examples/base/multi-select-base-example.component.html?raw'),
};

readonly exampleAsync: TuiDocExample = {
TypeScript: import('./examples/async/multi-select-async-example.component.ts?raw'),
HTML: import('./examples/async/multi-select-async-example.component.html?raw'),
};

readonly exampleWithTemplate: TuiDocExample = {
TypeScript: import('./examples/with-template/multi-select-with-template-example.component.ts?raw'),
HTML: import('./examples/with-template/multi-select-with-template-example.component.html?raw'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { CommonModule } from '@angular/common';
import { PrizmAddonDocModule, prizmDocGenerateRoutes } from '@prizm-ui/doc';
import { RouterModule } from '@angular/router';
import { InputInputMultiSelectComponent } from './input-multi-select.component';
import { PolymorphModule, PrizmButtonModule, PrizmInputMultiSelectModule } from '@prizm-ui/components';
import {
PolymorphModule,
PrizmButtonModule,
PrizmInputMultiSelectModule,
PrizmLoaderComponent,
PrizmPaginatorComponent,
} from '@prizm-ui/components';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { PrizmInputMultiSelectWithTemplateExampleComponent } from './examples/with-template/multi-select-with-template-example.component';
import { PrizmInputMultiSelectBaseExampleComponent } from './examples/base/multi-select-base-example.component';
Expand All @@ -12,6 +18,7 @@ import { PrizmInputMultiSelectWithObjectExampleComponent } from './examples/with
import { PrizmInputMultiSelectValidatorsExampleComponent } from './examples/validators/multi-select-validators-example.component';
import { PrizmInputMultiSelectWithTransformerExampleComponent } from './examples/with-transformer/multi-select-with-transformer-example.component';
import { PrizmIconsFullComponent } from '@prizm-ui/icons';
import { PrizmInputMultiSelectAsyncExampleComponent } from './examples/async/multi-select-async-example.component';

@NgModule({
imports: [
Expand All @@ -24,6 +31,9 @@ import { PrizmIconsFullComponent } from '@prizm-ui/icons';
PrizmInputMultiSelectModule,
RouterModule.forChild(prizmDocGenerateRoutes(InputInputMultiSelectComponent)),
PrizmIconsFullComponent,
PrizmLoaderComponent,
PrizmPaginatorComponent,
PrizmInputMultiSelectModule,
],
declarations: [
PrizmInputMultiSelectWithTransformerExampleComponent,
Expand All @@ -33,6 +43,7 @@ import { PrizmIconsFullComponent } from '@prizm-ui/icons';
PrizmInputMultiSelectWithTemplateExampleComponent,
PrizmInputMultiSelectWithObjectExampleComponent,
InputInputMultiSelectComponent,
PrizmInputMultiSelectAsyncExampleComponent,
],
exports: [InputInputMultiSelectComponent],
})
Expand Down
89 changes: 29 additions & 60 deletions libs/components/src/lib/components/chips/chips.component.html
Original file line number Diff line number Diff line change
@@ -1,70 +1,39 @@
<ng-container *prizmLet="chipsList$ | async as chipsList">
<div
class="chips-list"
#prizmElementReady="prizmElementReady"
#parent
#overflowHost="prizmOverflowHost"
*ngIf="!!chipsList?.length"
[active]="singleLine"
[class.hidden]="singleLine"
[checker]="ready"
prizmElementReady
prizmOverflowHost
>
<ng-container *ngIf="prizmElementReady.ready$ | async">
<ng-container
*ngFor="let item of chipsList; let i = index; trackBy: trackByIdx"
[ngTemplateOutlet]="buttonTemplate"
[ngTemplateOutletContext]="{
item: item,
idx: i,
allChipsCount: chipsList?.length ?? 0,
parent: parent,
singleLine: singleLine
}"
>
</ng-container>
<prizm-chips-item
#overflowItem="prizmOverflowItem"
*ngFor="let item of chipsList; let i = index; trackBy: trackByIdx"
[hintCanShow]="hintCanShow"
[hintDirection]="hintDirection"
[class.single-line]="singleLine"
[hintText]="item"
[deletable]="deletable"
[disabled]="accessorIsDisabled"
[prizmContext]="item"
[prizmContextKey]="overflowItem"
(deleted)="removeChips($event, i)"
(click)="chipClick(item)"
prizmOverflowItem
>
{{ item }}
</prizm-chips-item>

<ng-container *ngIf="overflowedChipsList$ | async as chipsOverflowedList">
<div
class="more-item"
*ngIf="chipsOverflowedList.size"
[prizmHint]="getOverflowedChipsListHint()"
[prizmHintDirection]="hintDirection"
>
...
</div>
</ng-container>
<ng-container *ngIf="overflowHost.active">
<div
class="more-item"
*ngIf="(overflowHost.hiddenElements$ | async)?.length"
[prizmHint]="overflowHost.hiddenElements$ | prizmGetContextByKeys | async | prizmCallFunc : joinHints"
[prizmHintDirection]="hintDirection"
>
...
</div>
</ng-container>
</div>
</ng-container>

<ng-template
#buttonTemplate
let-item="item"
let-idx="idx"
let-parent="parent"
let-background="background"
let-hint="hint"
let-allChipsCount="allChipsCount"
let-hideDelete="hideDelete"
let-singleLine="singleLine"
let-forceShowHint="forceShowHint"
>
<prizm-chips-item
class="{{
prizmLifecycle.afterViewInit$
| prizmCallFunc : isChipsContent$ : parent : singleLine : item : idx : allChipsCount
| async
}}"
#prizmLifecycle="prizmLifecycle"
[hintCanShow]="hintCanShow"
[hintDirection]="hintDirection"
[class.single-line]="singleLine"
[hintText]="item"
[deletable]="!hideDelete && deletable"
[disabled]="accessorIsDisabled"
(deleted)="removeChips($event, idx)"
(click)="chipClick(item)"
prizmLifecycle
>
{{ item }}
</prizm-chips-item>
</ng-template>
Loading
Loading