@snabcentr/client-ui 4.9.2 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/pages/frequently-asked-questions/sc-faq-query-params.mjs +5 -0
- package/esm2022/pages/frequently-asked-questions/sc-get-faq-group-query-params.mjs +14 -0
- package/esm2022/pages/frequently-asked-questions-group-selector/sc-frequently-asked-questions-group-selector.component.mjs +46 -17
- package/esm2022/pages/frequently-asked-questions-with-groups/sc-frequently-asked-questions-with-groups.component.mjs +29 -33
- package/esm2022/pages/index.mjs +4 -1
- package/esm2022/pages/news-list/sc-news-list.component.mjs +90 -0
- package/esm2022/pagination/index.mjs +2 -0
- package/esm2022/pagination/sc-pagination-links/sc-pagination-links.component.mjs +99 -0
- package/esm2022/public-api.mjs +2 -1
- package/fesm2022/snabcentr-client-ui.mjs +253 -46
- package/fesm2022/snabcentr-client-ui.mjs.map +1 -1
- package/package.json +2 -2
- package/pages/frequently-asked-questions/sc-faq-query-params.d.ts +4 -0
- package/pages/frequently-asked-questions/sc-get-faq-group-query-params.d.ts +10 -0
- package/pages/frequently-asked-questions-group-selector/sc-frequently-asked-questions-group-selector.component.d.ts +23 -4
- package/pages/frequently-asked-questions-with-groups/sc-frequently-asked-questions-with-groups.component.d.ts +14 -16
- package/pages/index.d.ts +3 -0
- package/pages/news-list/sc-news-list.component.d.ts +57 -0
- package/pagination/index.d.ts +1 -0
- package/pagination/sc-pagination-links/sc-pagination-links.component.d.ts +60 -0
- package/public-api.d.ts +1 -0
- package/release_notes.tmp +8 -3
- package/styles/tailwind/tailwind.scss +4 -4
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, Injectable, NgZone, signal, EventEmitter, ChangeDetectorRef, Directive, Input, Output, Pipe, ElementRef, HostListener, Renderer2, ContentChildren, NgModule, input, computed, Component, ChangeDetectionStrategy, Inject, HostBinding, model, output, SkipSelf, DestroyRef, effect, ContentChild, ViewChild, viewChild, Optional, contentChild, TemplateRef, forwardRef } from '@angular/core';
|
|
3
3
|
import * as i1 from '@snabcentr/client-core';
|
|
4
|
-
import { ScContactsService, ScUserService, ScLocationsService, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, ScImage, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe, ScCartService, ScUploadedFile, loadingPlaceholder, SC_ORDER_LOADER, ScMimeTypes, ScCatalogService, SC_URLS, IS_SERVER, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScCatalogFilterService, ScIdOrSlugPipe, SC_CATEGORY_INFO, ScConfiguratorService, RESPONSE, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScDeliveryAddressService, ScOrdersService, ScUIService, SC_USER_INFO as SC_USER_INFO$1, ScPaymentType, ScDeliveryType, TERMINAL_PROVIDERS, ScOrderDraftsService, ScRouteKeys, ScSeoService, scOrderIsLoaded, ScFrequentlyAskedQuestionsService, SchemaOrgFactory, SC_COMPANY_INFO, ScFeedbackService } from '@snabcentr/client-core';
|
|
4
|
+
import { ScContactsService, ScUserService, ScLocationsService, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, ScImage, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe, ScCartService, ScUploadedFile, loadingPlaceholder, SC_ORDER_LOADER, ScMimeTypes, ScCatalogService, SC_URLS, IS_SERVER, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScCatalogFilterService, ScIdOrSlugPipe, SC_CATEGORY_INFO, ScConfiguratorService, RESPONSE, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScDeliveryAddressService, ScOrdersService, ScUIService, SC_USER_INFO as SC_USER_INFO$1, ScPaymentType, ScDeliveryType, TERMINAL_PROVIDERS, ScOrderDraftsService, ScRouteKeys, ScSeoService, scOrderIsLoaded, SC_PAGE_QUERY_PARAM, ScFrequentlyAskedQuestionsService, SchemaOrgFactory, scGetPageQueryParamChanges, ScNewsService, SC_COMPANY_INFO, ScFeedbackService } from '@snabcentr/client-core';
|
|
5
5
|
import * as i5$1 from 'rxjs';
|
|
6
6
|
import { EMPTY, BehaviorSubject, switchMap, of, shareReplay, map, Subject, filter, tap, catchError, finalize, startWith, share, timer, scan, takeWhile, endWith, distinctUntilChanged, debounceTime, throwError, combineLatest, Observable, pairwise, Subscription, merge, skip, from, concatMap, noop, first, retry } from 'rxjs';
|
|
7
7
|
import { WA_WINDOW } from '@ng-web-apis/common';
|
|
8
8
|
import { takeUntilDestroyed, toSignal, outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
|
|
9
9
|
import * as i4$1 from '@taiga-ui/cdk';
|
|
10
|
-
import { TuiDay, TuiTime, TuiValueTransformer, TUI_IS_MOBILE, tuiControlValue, tuiIsPresent, tuiMarkControlAsTouchedAndValidate, tuiIsFalsy, TuiAutoFocus, TuiRepeatTimes, TuiHovered, TuiDayRange, TuiMonth, tuiPure, tuiProvide, TuiValidationError, TUI_WINDOW_SIZE, TUI_TRUE_HANDLER } from '@taiga-ui/cdk';
|
|
10
|
+
import { TuiDay, TuiTime, TuiValueTransformer, TUI_IS_MOBILE, tuiControlValue, tuiIsPresent, tuiMarkControlAsTouchedAndValidate, tuiIsFalsy, TuiAutoFocus, TuiRepeatTimes, TuiHovered, TuiDayRange, TuiMonth, tuiPure, tuiProvide, TuiValidationError, tuiClamp, TUI_WINDOW_SIZE, TUI_TRUE_HANDLER } from '@taiga-ui/cdk';
|
|
11
11
|
import * as i2$1 from '@taiga-ui/kit';
|
|
12
|
-
import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiFade, TuiRange, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, tuiFilesAccepted, TuiInputFiles, TuiInputFilesDirective, TuiFilesComponent, TuiFile, TuiFileRejectedPipe, TUI_CONFIRM, tuiItemsHandlersProvider as tuiItemsHandlersProvider$1,
|
|
12
|
+
import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiFade, TuiRange, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, tuiFilesAccepted, TuiInputFiles, TuiInputFilesDirective, TuiFilesComponent, TuiFile, TuiFileRejectedPipe, TUI_CONFIRM, tuiItemsHandlersProvider as tuiItemsHandlersProvider$1, TUI_PAGINATION_TEXTS, TuiAccordionDirective, TuiAccordionItemContent } from '@taiga-ui/kit';
|
|
13
13
|
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
14
14
|
import * as i1$1 from '@angular/forms';
|
|
15
15
|
import { FormControl, FormGroupDirective, NgControl, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
16
16
|
import * as i1$2 from '@taiga-ui/core';
|
|
17
|
-
import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiNumberFormat, TuiHint, TUI_MONTHS, TuiScrollbar, tuiNumberFormatProvider, tuiItemsHandlersProvider, tuiTextfieldOptionsProvider, tuiHintOptionsProvider, TuiFormatNumberPipe, TuiHintDriver, TuiHintHover, TuiHintPosition, TuiHintDirective, TuiDataListComponent, TuiOption, TUI_BUTTON_OPTIONS, TUI_BUTTON_DEFAULT_OPTIONS, TuiSurface, tuiFadeIn } from '@taiga-ui/core';
|
|
17
|
+
import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiNumberFormat, TuiHint, TUI_MONTHS, TuiScrollbar, tuiNumberFormatProvider, tuiItemsHandlersProvider, tuiTextfieldOptionsProvider, tuiHintOptionsProvider, TuiFormatNumberPipe, TuiHintDriver, TuiHintHover, TuiHintPosition, TuiHintDirective, TuiDataListComponent, TuiOption, TUI_BUTTON_OPTIONS, TUI_BUTTON_DEFAULT_OPTIONS, TUI_SPIN_ICONS, TuiSurface, tuiFadeIn } from '@taiga-ui/core';
|
|
18
18
|
import { isValidPhoneNumber } from 'libphonenumber-js/max';
|
|
19
19
|
import * as i4 from '@taiga-ui/legacy/components/primitive-textfield';
|
|
20
20
|
import * as i3 from '@taiga-ui/legacy';
|
|
@@ -9454,6 +9454,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9454
9454
|
}]
|
|
9455
9455
|
}] });
|
|
9456
9456
|
|
|
9457
|
+
/**
|
|
9458
|
+
* Наименование query-параметра идентификатора группы часто задаваемых вопросов.
|
|
9459
|
+
*/
|
|
9460
|
+
const SC_FAQ_GROUP_QUERY_PARAM = 'group';
|
|
9461
|
+
|
|
9457
9462
|
/**
|
|
9458
9463
|
* Компонент часто задаваемых вопросов.
|
|
9459
9464
|
*/
|
|
@@ -9496,48 +9501,178 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9496
9501
|
], host: { ngSkipHydration: 'true' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<tui-loader\n [showLoader]=\"showLoading()\"\n [overlay]=\"true\"\n>\n <tui-accordion\n class=\"accordion\"\n [closeOthers]=\"false\"\n >\n @for (item of items(); track item.id) {\n <button\n [tuiAccordion]=\"isServer\"\n class=\"!whitespace-normal !text-body-m-bold\"\n >\n {{ item.question }}\n </button>\n <tui-expand>\n <div [innerHTML]=\"item.answer\"></div>\n </tui-expand>\n }\n </tui-accordion>\n</tui-loader>\n", styles: [".accordion{border-radius:0}.accordion,.accordion[data-size=s],.accordion[data-size=m],.accordion[data-size=l],.accordion .tui-list{font:inherit}.accordion [tuiAccordion]{border-block:1px solid var(--tui-border-normal);border-block-end-color:#fff!important;font-weight:400;border-color:var(--tui-border-normal);mask:none;block-size:auto;padding-block:.5rem;min-block-size:var(--t-size)}.accordion [tuiAccordion]._open{border-block-end-color:var(--tui-border-normal)!important}.accordion tui-expand,.accordion [tuiAccordion]{box-shadow:none;white-space:normal}\n"] }]
|
|
9497
9502
|
}] });
|
|
9498
9503
|
|
|
9504
|
+
/**
|
|
9505
|
+
* Возвращает query-параметры ссылки на группу часто задаваемых вопросов.
|
|
9506
|
+
*
|
|
9507
|
+
* При смене группы пагинация сбрасывается на первую страницу.
|
|
9508
|
+
*
|
|
9509
|
+
* @param group Группа вопросов.
|
|
9510
|
+
*/
|
|
9511
|
+
const scGetFaqGroupQueryParams = (group) => ({
|
|
9512
|
+
[SC_FAQ_GROUP_QUERY_PARAM]: group.id ?? null,
|
|
9513
|
+
[SC_PAGE_QUERY_PARAM]: null,
|
|
9514
|
+
});
|
|
9515
|
+
|
|
9499
9516
|
/**
|
|
9500
9517
|
* Компонент выбора группы часто задаваемых вопросов.
|
|
9518
|
+
*
|
|
9519
|
+
* TODO: [TAIGA:5]: Заменить legacy tui-select и tuiItemsHandlersProvider на актуальные аналоги.
|
|
9501
9520
|
*/
|
|
9502
9521
|
class ScFrequentlyAskedQuestionsGroupSelectorComponent {
|
|
9522
|
+
/**
|
|
9523
|
+
* Инициализирует экземпляр класса {@link ScFrequentlyAskedQuestionsGroupSelectorComponent}.
|
|
9524
|
+
*/
|
|
9503
9525
|
constructor() {
|
|
9526
|
+
/**
|
|
9527
|
+
* Сервис маршрутизации.
|
|
9528
|
+
*/
|
|
9529
|
+
this.router = inject(Router);
|
|
9530
|
+
/**
|
|
9531
|
+
* Активный маршрут.
|
|
9532
|
+
*/
|
|
9533
|
+
this.route = inject(ActivatedRoute);
|
|
9504
9534
|
/**
|
|
9505
9535
|
* {@link Observable} списка группы часто задаваемых вопросов.
|
|
9506
9536
|
*/
|
|
9507
|
-
this.groups$ = inject(ScFrequentlyAskedQuestionsService).groups
|
|
9508
|
-
if (groups.length > 0) {
|
|
9509
|
-
this.groupControl.setValue(groups[0]);
|
|
9510
|
-
}
|
|
9511
|
-
}));
|
|
9537
|
+
this.groups$ = inject(ScFrequentlyAskedQuestionsService).groups$;
|
|
9512
9538
|
/**
|
|
9513
9539
|
* {@link FormControl} выбранной группы.
|
|
9514
9540
|
*/
|
|
9515
9541
|
this.groupControl = new FormControl(null);
|
|
9516
9542
|
/**
|
|
9517
|
-
* {@link
|
|
9543
|
+
* {@link InputSignal} идентификатора выбранной группы.
|
|
9518
9544
|
*/
|
|
9519
|
-
this.
|
|
9520
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
9521
|
-
map((group) => group.id)));
|
|
9545
|
+
this.selectedGroupId = input(null);
|
|
9522
9546
|
/**
|
|
9523
9547
|
* Формат отображения. 'list' - в виде списка, 'select' в виде выпадающего списка.
|
|
9524
9548
|
*/
|
|
9525
9549
|
this.viewMode = input('list');
|
|
9550
|
+
/**
|
|
9551
|
+
* Возвращает query-параметры ссылки на группу вопросов.
|
|
9552
|
+
*/
|
|
9553
|
+
this.getGroupQueryParams = scGetFaqGroupQueryParams;
|
|
9554
|
+
// Синхронизирует значение выпадающего списка с выбранной группой.
|
|
9555
|
+
combineLatest([this.groups$, toObservable(this.selectedGroupId)])
|
|
9556
|
+
.pipe(takeUntilDestroyed())
|
|
9557
|
+
.subscribe(([groups, selectedGroupId]) => {
|
|
9558
|
+
this.groupControl.setValue(groups.find((group) => (group.id ?? null) === selectedGroupId) ?? null, { emitEvent: false });
|
|
9559
|
+
});
|
|
9560
|
+
// Выбор группы в выпадающем списке переводит на адрес этой группы.
|
|
9561
|
+
this.groupControl.valueChanges.pipe(filter(tuiIsPresent), takeUntilDestroyed()).subscribe((group) => {
|
|
9562
|
+
this.router
|
|
9563
|
+
.navigate([], {
|
|
9564
|
+
relativeTo: this.route,
|
|
9565
|
+
queryParams: this.getGroupQueryParams(group),
|
|
9566
|
+
queryParamsHandling: 'merge',
|
|
9567
|
+
})
|
|
9568
|
+
.catch(noop);
|
|
9569
|
+
});
|
|
9526
9570
|
}
|
|
9527
9571
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScFrequentlyAskedQuestionsGroupSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9528
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFrequentlyAskedQuestionsGroupSelectorComponent, isStandalone: true, selector: "sc-frequently-asked-questions-group-selector", inputs: {
|
|
9572
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFrequentlyAskedQuestionsGroupSelectorComponent, isStandalone: true, selector: "sc-frequently-asked-questions-group-selector", inputs: { selectedGroupId: { classPropertyName: "selectedGroupId", publicName: "selectedGroupId", isSignal: true, isRequired: false, transformFunction: null }, viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
9529
9573
|
tuiItemsHandlersProvider$1({
|
|
9530
9574
|
stringify: (item) => item.name,
|
|
9531
9575
|
}),
|
|
9532
|
-
], ngImport: i0, template: "@let groups = groups$ | async;\n\n@if (groups && groups.length) {\n @switch (viewMode()) {\n @case ('list') {\n <div class=\"flex flex-col items-center gap-3 rounded-xl p-4 shadow-sc\">\n <div class=\"flex w-full flex-col gap-2\">\n @for (item of groups; track $index) {\n <
|
|
9576
|
+
], ngImport: i0, template: "@let groups = groups$ | async;\n\n@if (groups && groups.length) {\n @switch (viewMode()) {\n @case ('list') {\n <div class=\"flex flex-col items-center gap-3 rounded-xl p-4 shadow-sc\">\n <div class=\"flex w-full flex-col gap-2\">\n @for (item of groups; track $index) {\n @let isSelected = item.id === selectedGroupId();\n\n <a\n tuiButton\n size=\"xs\"\n [tuiAppearanceState]=\"isSelected ? 'hover' : null\"\n [attr.aria-current]=\"isSelected ? 'page' : null\"\n [class.pointer-events-none]=\"isSelected\"\n [routerLink]=\"[]\"\n [queryParams]=\"getGroupQueryParams(item)\"\n queryParamsHandling=\"merge\"\n appearance=\"flat-grayscale\"\n class=\"!justify-start !text-body-s-bold\"\n >\n {{ item.name }}\n </a>\n }\n </div>\n </div>\n }\n\n @case ('select') {\n <form>\n <tui-select\n [formControl]=\"groupControl\"\n [tuiTextfieldLabelOutside]=\"true\"\n >\n <tui-data-list-wrapper\n *tuiDataList\n [items]=\"groups\"\n />\n </tui-select>\n </form>\n }\n }\n}\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: TuiSelectModule }, { kind: "component", type: i3.TuiSelectComponent, selector: "tui-select", inputs: ["stringify", "identityMatcher", "valueContent"] }, { kind: "directive", type: i3.TuiSelectDirective, selector: "tui-select" }, { kind: "directive", type: i3$1.TuiDataListDirective, selector: "ng-template[tuiDataList]" }, { kind: "component", type: i3$3.TuiDataListWrapperComponent, selector: "tui-data-list-wrapper:not([labels]), tui-data-list-wrapper:not([labels])[new]", inputs: ["items", "disabledItemHandler", "emptyContent", "size", "itemContent"], outputs: ["itemClick"] }, { kind: "ngmodule", type: TuiTextfieldControllerModule }, { kind: "directive", type: i3.TuiTextfieldLabelOutsideDirective, selector: "[tuiTextfieldLabelOutside]", inputs: ["tuiTextfieldLabelOutside"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9533
9577
|
}
|
|
9534
9578
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScFrequentlyAskedQuestionsGroupSelectorComponent, decorators: [{
|
|
9535
9579
|
type: Component,
|
|
9536
|
-
args: [{ standalone: true, selector: 'sc-frequently-asked-questions-group-selector', imports: [AsyncPipe, TuiSelectModule, TuiTextfieldControllerModule, ReactiveFormsModule, TuiDataList, TuiDataListWrapper, NgIf, TuiButton, FormsModule], providers: [
|
|
9580
|
+
args: [{ standalone: true, selector: 'sc-frequently-asked-questions-group-selector', imports: [AsyncPipe, TuiSelectModule, TuiTextfieldControllerModule, ReactiveFormsModule, TuiDataList, TuiDataListWrapper, NgIf, TuiButton, FormsModule, RouterLink], providers: [
|
|
9537
9581
|
tuiItemsHandlersProvider$1({
|
|
9538
9582
|
stringify: (item) => item.name,
|
|
9539
9583
|
}),
|
|
9540
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let groups = groups$ | async;\n\n@if (groups && groups.length) {\n @switch (viewMode()) {\n @case ('list') {\n <div class=\"flex flex-col items-center gap-3 rounded-xl p-4 shadow-sc\">\n <div class=\"flex w-full flex-col gap-2\">\n @for (item of groups; track $index) {\n <
|
|
9584
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let groups = groups$ | async;\n\n@if (groups && groups.length) {\n @switch (viewMode()) {\n @case ('list') {\n <div class=\"flex flex-col items-center gap-3 rounded-xl p-4 shadow-sc\">\n <div class=\"flex w-full flex-col gap-2\">\n @for (item of groups; track $index) {\n @let isSelected = item.id === selectedGroupId();\n\n <a\n tuiButton\n size=\"xs\"\n [tuiAppearanceState]=\"isSelected ? 'hover' : null\"\n [attr.aria-current]=\"isSelected ? 'page' : null\"\n [class.pointer-events-none]=\"isSelected\"\n [routerLink]=\"[]\"\n [queryParams]=\"getGroupQueryParams(item)\"\n queryParamsHandling=\"merge\"\n appearance=\"flat-grayscale\"\n class=\"!justify-start !text-body-s-bold\"\n >\n {{ item.name }}\n </a>\n }\n </div>\n </div>\n }\n\n @case ('select') {\n <form>\n <tui-select\n [formControl]=\"groupControl\"\n [tuiTextfieldLabelOutside]=\"true\"\n >\n <tui-data-list-wrapper\n *tuiDataList\n [items]=\"groups\"\n />\n </tui-select>\n </form>\n }\n }\n}\n" }]
|
|
9585
|
+
}], ctorParameters: () => [] });
|
|
9586
|
+
|
|
9587
|
+
/**
|
|
9588
|
+
* Компонент пагинации, в котором страницы являются ссылками с номером страницы в query-параметрах.
|
|
9589
|
+
*
|
|
9590
|
+
* Ссылки позволяют переходить между страницами при отключенном JavaScript и корректно работают при SSR.
|
|
9591
|
+
* Внешний вид повторяет tui-pagination из @taiga-ui/kit.
|
|
9592
|
+
*
|
|
9593
|
+
* TODO: [TAIGA:5]: Проверить, появилась ли в tui-pagination поддержка ссылок, и заменить компонент на неё.
|
|
9594
|
+
*/
|
|
9595
|
+
class ScPaginationLinksComponent {
|
|
9596
|
+
constructor() {
|
|
9597
|
+
/**
|
|
9598
|
+
* Иконки стрелок переключения страниц.
|
|
9599
|
+
*/
|
|
9600
|
+
this.icons = inject(TUI_SPIN_ICONS);
|
|
9601
|
+
/**
|
|
9602
|
+
* {@link Observable} подписей стрелок переключения страниц.
|
|
9603
|
+
*/
|
|
9604
|
+
this.texts$ = inject(TUI_PAGINATION_TEXTS);
|
|
9605
|
+
/**
|
|
9606
|
+
* {@link InputSignal} общего количества страниц.
|
|
9607
|
+
*/
|
|
9608
|
+
this.length = input.required();
|
|
9609
|
+
/**
|
|
9610
|
+
* {@link InputSignal} номера текущей страницы, начиная с единицы.
|
|
9611
|
+
*/
|
|
9612
|
+
this.index = input.required();
|
|
9613
|
+
/**
|
|
9614
|
+
* {@link InputSignal} наименования query-параметра, в котором хранится номер страницы.
|
|
9615
|
+
*/
|
|
9616
|
+
this.queryParam = input(SC_PAGE_QUERY_PARAM);
|
|
9617
|
+
/**
|
|
9618
|
+
* {@link InputSignal} количества страниц, отображаемых по обе стороны от текущей.
|
|
9619
|
+
*/
|
|
9620
|
+
this.activePadding = input(1);
|
|
9621
|
+
/**
|
|
9622
|
+
* {@link InputSignal} количества страниц, отображаемых по краям.
|
|
9623
|
+
*/
|
|
9624
|
+
this.sidePadding = input(1);
|
|
9625
|
+
/**
|
|
9626
|
+
* {@link Signal} номеров отображаемых страниц, где `null` - разделитель пропущенных страниц.
|
|
9627
|
+
*
|
|
9628
|
+
* Состав страниц рассчитывается так же, как в tui-pagination: количество отображаемых элементов постоянно,
|
|
9629
|
+
* а окно вокруг текущей страницы расширяется у краёв списка.
|
|
9630
|
+
*/
|
|
9631
|
+
this.pages = computed(() => {
|
|
9632
|
+
const length = this.length();
|
|
9633
|
+
const sidePadding = this.sidePadding();
|
|
9634
|
+
// Половина элементов: страницы у края, разделитель и страницы вокруг текущей.
|
|
9635
|
+
const maxHalfLength = sidePadding + 1 + this.activePadding();
|
|
9636
|
+
const maxElementsLength = maxHalfLength * 2 + 1;
|
|
9637
|
+
const elementsLength = Math.min(length, maxElementsLength);
|
|
9638
|
+
const lastIndex = length - 1;
|
|
9639
|
+
const index = this.index() - 1;
|
|
9640
|
+
const reverseIndex = lastIndex - index;
|
|
9641
|
+
const hasCollapsedItems = (value) => length > maxElementsLength && value > maxHalfLength;
|
|
9642
|
+
return Array.from({ length: elementsLength }, (_, elementIndex) => {
|
|
9643
|
+
const reverseElementIndex = elementsLength - 1 - elementIndex;
|
|
9644
|
+
if (elementIndex < sidePadding) {
|
|
9645
|
+
return elementIndex + 1;
|
|
9646
|
+
}
|
|
9647
|
+
if (elementIndex === sidePadding && hasCollapsedItems(index)) {
|
|
9648
|
+
return null;
|
|
9649
|
+
}
|
|
9650
|
+
if (reverseElementIndex < sidePadding) {
|
|
9651
|
+
return lastIndex - reverseElementIndex + 1;
|
|
9652
|
+
}
|
|
9653
|
+
if (reverseElementIndex === sidePadding && hasCollapsedItems(reverseIndex)) {
|
|
9654
|
+
return null;
|
|
9655
|
+
}
|
|
9656
|
+
return tuiClamp(index - maxHalfLength + elementIndex, elementIndex, lastIndex - reverseElementIndex) + 1;
|
|
9657
|
+
});
|
|
9658
|
+
});
|
|
9659
|
+
}
|
|
9660
|
+
/**
|
|
9661
|
+
* Возвращает query-параметры ссылки на страницу.
|
|
9662
|
+
*
|
|
9663
|
+
* Для первой страницы параметр удаляется, чтобы её адрес совпадал с адресом страницы без пагинации.
|
|
9664
|
+
*
|
|
9665
|
+
* @param page Номер страницы.
|
|
9666
|
+
*/
|
|
9667
|
+
getPageQueryParams(page) {
|
|
9668
|
+
return { [this.queryParam()]: page > 1 ? page : null };
|
|
9669
|
+
}
|
|
9670
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScPaginationLinksComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9671
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScPaginationLinksComponent, isStandalone: true, selector: "sc-pagination-links", inputs: { length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: true, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, queryParam: { classPropertyName: "queryParam", publicName: "queryParam", isSignal: true, isRequired: false, transformFunction: null }, activePadding: { classPropertyName: "activePadding", publicName: "activePadding", isSignal: true, isRequired: false, transformFunction: null }, sidePadding: { classPropertyName: "sidePadding", publicName: "sidePadding", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let texts = texts$ | async;\n\n@if (texts) {\n <div class=\"t-content\">\n @if (index() > 1) {\n <a\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.decrement\"\n [routerLink]=\"[]\"\n [queryParams]=\"getPageQueryParams(index() - 1)\"\n queryParamsHandling=\"merge\"\n >\n {{ texts[0] }}\n </a>\n } @else {\n <button\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n type=\"button\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.decrement\"\n [disabled]=\"true\"\n >\n {{ texts[0] }}\n </button>\n }\n\n @for (page of pages(); track $index) {\n @if (page) {\n <a\n automation-id=\"sc-pagination-links__element\"\n tuiButton\n size=\"s\"\n class=\"t-button\"\n [appearance]=\"page === index() ? 'primary' : 'flat'\"\n [attr.aria-current]=\"page === index() ? 'page' : null\"\n [routerLink]=\"[]\"\n [queryParams]=\"getPageQueryParams(page)\"\n queryParamsHandling=\"merge\"\n >\n {{ page }}\n </a>\n } @else {\n <div class=\"t-dots\"></div>\n }\n }\n\n @if (index() < length()) {\n <a\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.increment\"\n [routerLink]=\"[]\"\n [queryParams]=\"getPageQueryParams(index() + 1)\"\n queryParamsHandling=\"merge\"\n >\n {{ texts[1] }}\n </a>\n } @else {\n <button\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n type=\"button\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.increment\"\n [disabled]=\"true\"\n >\n {{ texts[1] }}\n </button>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;font:var(--tui-font-text-s);color:var(--tui-text-primary);text-align:center}.t-content{display:flex;justify-content:center}.t-button{flex-shrink:0;margin:0 .125rem}.t-button[tuiButton]{min-inline-size:var(--tui-height-s);padding:0 .5rem}.t-button:first-child{margin-inline-start:0}.t-button:last-child{margin-inline-end:0}.t-dots{flex-shrink:0;inline-size:var(--tui-height-s);block-size:var(--tui-height-s);line-height:var(--tui-height-s);margin:0 .125rem;color:var(--tui-text-action);text-align:center;cursor:default}.t-dots:before{content:\"\\2026\"}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9672
|
+
}
|
|
9673
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScPaginationLinksComponent, decorators: [{
|
|
9674
|
+
type: Component,
|
|
9675
|
+
args: [{ standalone: true, selector: 'sc-pagination-links', imports: [AsyncPipe, RouterLink, TuiButton], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let texts = texts$ | async;\n\n@if (texts) {\n <div class=\"t-content\">\n @if (index() > 1) {\n <a\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.decrement\"\n [routerLink]=\"[]\"\n [queryParams]=\"getPageQueryParams(index() - 1)\"\n queryParamsHandling=\"merge\"\n >\n {{ texts[0] }}\n </a>\n } @else {\n <button\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n type=\"button\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.decrement\"\n [disabled]=\"true\"\n >\n {{ texts[0] }}\n </button>\n }\n\n @for (page of pages(); track $index) {\n @if (page) {\n <a\n automation-id=\"sc-pagination-links__element\"\n tuiButton\n size=\"s\"\n class=\"t-button\"\n [appearance]=\"page === index() ? 'primary' : 'flat'\"\n [attr.aria-current]=\"page === index() ? 'page' : null\"\n [routerLink]=\"[]\"\n [queryParams]=\"getPageQueryParams(page)\"\n queryParamsHandling=\"merge\"\n >\n {{ page }}\n </a>\n } @else {\n <div class=\"t-dots\"></div>\n }\n }\n\n @if (index() < length()) {\n <a\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.increment\"\n [routerLink]=\"[]\"\n [queryParams]=\"getPageQueryParams(index() + 1)\"\n queryParamsHandling=\"merge\"\n >\n {{ texts[1] }}\n </a>\n } @else {\n <button\n tuiIconButton\n size=\"s\"\n appearance=\"flat\"\n type=\"button\"\n tabIndex=\"-1\"\n class=\"t-button\"\n [iconStart]=\"icons.increment\"\n [disabled]=\"true\"\n >\n {{ texts[1] }}\n </button>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;font:var(--tui-font-text-s);color:var(--tui-text-primary);text-align:center}.t-content{display:flex;justify-content:center}.t-button{flex-shrink:0;margin:0 .125rem}.t-button[tuiButton]{min-inline-size:var(--tui-height-s);padding:0 .5rem}.t-button:first-child{margin-inline-start:0}.t-button:last-child{margin-inline-end:0}.t-dots{flex-shrink:0;inline-size:var(--tui-height-s);block-size:var(--tui-height-s);line-height:var(--tui-height-s);margin:0 .125rem;color:var(--tui-text-action);text-align:center;cursor:default}.t-dots:before{content:\"\\2026\"}\n"] }]
|
|
9541
9676
|
}] });
|
|
9542
9677
|
|
|
9543
9678
|
/**
|
|
@@ -9573,9 +9708,13 @@ class ScFrequentlyAskedQuestionsWithGroupsComponent {
|
|
|
9573
9708
|
*/
|
|
9574
9709
|
this.router = inject(Router);
|
|
9575
9710
|
/**
|
|
9576
|
-
*
|
|
9711
|
+
* Активный маршрут.
|
|
9712
|
+
*/
|
|
9713
|
+
this.route = inject(ActivatedRoute);
|
|
9714
|
+
/**
|
|
9715
|
+
* Признак, что приложение запущено на сервере.
|
|
9577
9716
|
*/
|
|
9578
|
-
this.
|
|
9717
|
+
this.isServer = inject(IS_SERVER);
|
|
9579
9718
|
/**
|
|
9580
9719
|
* Количество записей на странице пагинации.
|
|
9581
9720
|
*/
|
|
@@ -9585,13 +9724,15 @@ class ScFrequentlyAskedQuestionsWithGroupsComponent {
|
|
|
9585
9724
|
*/
|
|
9586
9725
|
this.perPage$ = new BehaviorSubject(this.pageSizeOptions[0]);
|
|
9587
9726
|
/**
|
|
9588
|
-
* {@link
|
|
9727
|
+
* {@link Observable} изменения номера страницы пагинации из query-параметров.
|
|
9589
9728
|
*/
|
|
9590
|
-
this.page$ =
|
|
9729
|
+
this.page$ = scGetPageQueryParamChanges();
|
|
9591
9730
|
/**
|
|
9592
|
-
* {@link
|
|
9731
|
+
* {@link Observable} изменения идентификатора выбранной группы.
|
|
9732
|
+
*
|
|
9733
|
+
* Идентификатор берётся из query-параметров, а если параметр не задан - используется первая группа списка.
|
|
9593
9734
|
*/
|
|
9594
|
-
this.groupId$ =
|
|
9735
|
+
this.groupId$ = combineLatest([this.route.queryParamMap, this.frequentlyAskedQuestionsService.groups$]).pipe(map(([params, groups]) => Number(params.get(SC_FAQ_GROUP_QUERY_PARAM)) || (groups[0]?.id ?? null)), distinctUntilChanged(), shareReplay({ refCount: true, bufferSize: 1 }));
|
|
9595
9736
|
/**
|
|
9596
9737
|
* Сигнал нажатия на кнопку "Задать вопрос".
|
|
9597
9738
|
*/
|
|
@@ -9602,7 +9743,7 @@ class ScFrequentlyAskedQuestionsWithGroupsComponent {
|
|
|
9602
9743
|
this.request$ = combineLatest({
|
|
9603
9744
|
page: this.page$,
|
|
9604
9745
|
perPage: this.perPage$,
|
|
9605
|
-
group: this.groupId
|
|
9746
|
+
group: this.groupId$.pipe(filter(tuiIsPresent)),
|
|
9606
9747
|
paginate: of(true),
|
|
9607
9748
|
}).pipe(
|
|
9608
9749
|
// Нулевой debounceTime для случая, когда меняются направление и поле сортировки одновременно.
|
|
@@ -9621,7 +9762,7 @@ class ScFrequentlyAskedQuestionsWithGroupsComponent {
|
|
|
9621
9762
|
/**
|
|
9622
9763
|
* {@link Observable} изменения номера страницы пагинации из данных пагинации запроса.
|
|
9623
9764
|
*/
|
|
9624
|
-
this.currentPage$ = this.request$.pipe(filter(tuiIsPresent), map(({ meta }) => meta.currentPage
|
|
9765
|
+
this.currentPage$ = this.request$.pipe(filter(tuiIsPresent), map(({ meta }) => meta.currentPage));
|
|
9625
9766
|
/**
|
|
9626
9767
|
* {@link Observable} изменения состояния загрузки данных.
|
|
9627
9768
|
*/
|
|
@@ -9633,33 +9774,99 @@ class ScFrequentlyAskedQuestionsWithGroupsComponent {
|
|
|
9633
9774
|
/**
|
|
9634
9775
|
* {@link Observable} изменения формата отображения фильтра групп.
|
|
9635
9776
|
*/
|
|
9636
|
-
this.viewMode$ =
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
paginationChange(pageIndex) {
|
|
9644
|
-
this.page$.next(pageIndex + 1);
|
|
9645
|
-
}
|
|
9646
|
-
/**
|
|
9647
|
-
* Устанавливает идентификатор группы.
|
|
9648
|
-
*
|
|
9649
|
-
* @param id Идентификатор группы.
|
|
9650
|
-
*/
|
|
9651
|
-
groupIdChange(id) {
|
|
9652
|
-
this.groupId$.next(id);
|
|
9653
|
-
this.paginationChange(0);
|
|
9777
|
+
this.viewMode$ = this.isServer
|
|
9778
|
+
? of('list')
|
|
9779
|
+
: inject(TUI_WINDOW_SIZE).pipe(map((rect) => (rect.width > 768 ? 'list' : 'select')));
|
|
9780
|
+
/**
|
|
9781
|
+
* Наименование query-параметра номера страницы пагинации.
|
|
9782
|
+
*/
|
|
9783
|
+
this.pageQueryParam = SC_PAGE_QUERY_PARAM;
|
|
9654
9784
|
}
|
|
9655
9785
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScFrequentlyAskedQuestionsWithGroupsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9656
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFrequentlyAskedQuestionsWithGroupsComponent, isStandalone: true, selector: "sc-frequently-asked-questions-with-groups", outputs: { clickSendQuestion: "clickSendQuestion" }, ngImport: i0, template: "@let items = data$ | async;\n@let pageCount = pageCount$ | async;\n@let viewMode = viewMode$ | async;\n\n<div\n *ngIf=\"viewMode\"\n class=\"flex gap-6\"\n [class.flex-col]=\"viewMode === 'select'\"\n>\n <div class=\"flex flex-col gap-7\">\n <sc-frequently-asked-questions-group-selector\n
|
|
9786
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFrequentlyAskedQuestionsWithGroupsComponent, isStandalone: true, selector: "sc-frequently-asked-questions-with-groups", outputs: { clickSendQuestion: "clickSendQuestion" }, host: { attributes: { "ngSkipHydration": "true" } }, ngImport: i0, template: "@let items = data$ | async;\n@let pageCount = pageCount$ | async;\n@let viewMode = viewMode$ | async;\n\n<div\n *ngIf=\"viewMode\"\n class=\"flex gap-6\"\n [class.flex-col]=\"viewMode === 'select'\"\n>\n <div class=\"flex flex-col gap-7\">\n <sc-frequently-asked-questions-group-selector\n [selectedGroupId]=\"groupId$ | async\"\n [viewMode]=\"viewMode\"\n class=\"shrink-0\"\n [class.w-64]=\"viewMode !== 'select'\"\n />\n\n <button\n *ngIf=\"viewMode === 'list'\"\n tuiButton\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.circle-help\"\n appearance=\"secondary\"\n (click)=\"clickSendQuestion.emit()\"\n class=\"self-center\"\n >\n \u0417\u0430\u0434\u0430\u0442\u044C \u0432\u043E\u043F\u0440\u043E\u0441\n </button>\n </div>\n\n <div class=\"grow\">\n @if (items && items.length) {\n <sc-frequently-asked-questions\n [items]=\"items\"\n [showLoading]=\"isLoading()\"\n />\n\n <sc-pagination-links\n *ngIf=\"pageCount && pageCount > 1\"\n [length]=\"pageCount\"\n [index]=\"(currentPage$ | async) ?? 1\"\n [queryParam]=\"pageQueryParam\"\n class=\"mt-4\"\n />\n } @else {\n <p class=\"text-body-s-bold\">\u0421\u043F\u0438\u0441\u043E\u043A \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u043F\u0443\u0441\u0442</p>\n }\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ScFrequentlyAskedQuestionsGroupSelectorComponent, selector: "sc-frequently-asked-questions-group-selector", inputs: ["selectedGroupId", "viewMode"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: ScFrequentlyAskedQuestionsComponent, selector: "sc-frequently-asked-questions", inputs: ["items", "showLoading"] }, { kind: "component", type: ScPaginationLinksComponent, selector: "sc-pagination-links", inputs: ["length", "index", "queryParam", "activePadding", "sidePadding"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9657
9787
|
}
|
|
9658
9788
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScFrequentlyAskedQuestionsWithGroupsComponent, decorators: [{
|
|
9659
9789
|
type: Component,
|
|
9660
|
-
args: [{ standalone: true, selector: 'sc-frequently-asked-questions-with-groups', imports: [AsyncPipe, NgIf, ScFrequentlyAskedQuestionsGroupSelectorComponent, TuiButton, ScFrequentlyAskedQuestionsComponent,
|
|
9790
|
+
args: [{ standalone: true, selector: 'sc-frequently-asked-questions-with-groups', imports: [AsyncPipe, NgIf, ScFrequentlyAskedQuestionsGroupSelectorComponent, TuiButton, ScFrequentlyAskedQuestionsComponent, ScPaginationLinksComponent], host: { ngSkipHydration: 'true' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let items = data$ | async;\n@let pageCount = pageCount$ | async;\n@let viewMode = viewMode$ | async;\n\n<div\n *ngIf=\"viewMode\"\n class=\"flex gap-6\"\n [class.flex-col]=\"viewMode === 'select'\"\n>\n <div class=\"flex flex-col gap-7\">\n <sc-frequently-asked-questions-group-selector\n [selectedGroupId]=\"groupId$ | async\"\n [viewMode]=\"viewMode\"\n class=\"shrink-0\"\n [class.w-64]=\"viewMode !== 'select'\"\n />\n\n <button\n *ngIf=\"viewMode === 'list'\"\n tuiButton\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.circle-help\"\n appearance=\"secondary\"\n (click)=\"clickSendQuestion.emit()\"\n class=\"self-center\"\n >\n \u0417\u0430\u0434\u0430\u0442\u044C \u0432\u043E\u043F\u0440\u043E\u0441\n </button>\n </div>\n\n <div class=\"grow\">\n @if (items && items.length) {\n <sc-frequently-asked-questions\n [items]=\"items\"\n [showLoading]=\"isLoading()\"\n />\n\n <sc-pagination-links\n *ngIf=\"pageCount && pageCount > 1\"\n [length]=\"pageCount\"\n [index]=\"(currentPage$ | async) ?? 1\"\n [queryParam]=\"pageQueryParam\"\n class=\"mt-4\"\n />\n } @else {\n <p class=\"text-body-s-bold\">\u0421\u043F\u0438\u0441\u043E\u043A \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u043F\u0443\u0441\u0442</p>\n }\n </div>\n</div>\n" }]
|
|
9661
9791
|
}] });
|
|
9662
9792
|
|
|
9793
|
+
/**
|
|
9794
|
+
* Компонент списка новостей с пагинацией через query-параметр страницы.
|
|
9795
|
+
*
|
|
9796
|
+
* Ссылки пагинации корректно работают при SSR и позволяют поисковым роботам обходить страницы списка.
|
|
9797
|
+
*/
|
|
9798
|
+
class ScNewsListComponent {
|
|
9799
|
+
/**
|
|
9800
|
+
* Инициализирует экземпляр класса {@link ScNewsListComponent}.
|
|
9801
|
+
*/
|
|
9802
|
+
constructor() {
|
|
9803
|
+
/**
|
|
9804
|
+
* Сервис для получения списка новостей.
|
|
9805
|
+
*/
|
|
9806
|
+
this.newsService = inject(ScNewsService);
|
|
9807
|
+
/**
|
|
9808
|
+
* Сервис маршрутизации.
|
|
9809
|
+
*/
|
|
9810
|
+
this.router = inject(Router);
|
|
9811
|
+
/**
|
|
9812
|
+
* {@link InputSignal} количества записей на странице пагинации.
|
|
9813
|
+
*/
|
|
9814
|
+
this.perPage = input(16);
|
|
9815
|
+
/**
|
|
9816
|
+
* {@link Observable} изменения номера страницы пагинации из query-параметров.
|
|
9817
|
+
*/
|
|
9818
|
+
this.page$ = scGetPageQueryParamChanges();
|
|
9819
|
+
/**
|
|
9820
|
+
* {@link Observable} запроса данных списка новостей.
|
|
9821
|
+
*/
|
|
9822
|
+
this.request$ = combineLatest([
|
|
9823
|
+
combineLatest({
|
|
9824
|
+
page: this.page$,
|
|
9825
|
+
perPage: toObservable(this.perPage),
|
|
9826
|
+
paginate: of(true),
|
|
9827
|
+
}),
|
|
9828
|
+
inject(SC_USER_INFO$1),
|
|
9829
|
+
]).pipe(
|
|
9830
|
+
// Нулевой debounceTime для случая, когда номер страницы и количество записей меняются одновременно.
|
|
9831
|
+
debounceTime(0), switchMap(([value]) => this.newsService.getListOfNews$(value).pipe(startWith(null))), share());
|
|
9832
|
+
/**
|
|
9833
|
+
* {@link Observable} изменения количества страниц пагинации.
|
|
9834
|
+
*/
|
|
9835
|
+
this.pagesLength$ = this.request$.pipe(filter(tuiIsPresent), map(({ meta }) => meta.lastPage), startWith(20));
|
|
9836
|
+
/**
|
|
9837
|
+
* {@link Observable} изменения номера страницы пагинации из данных пагинации запроса.
|
|
9838
|
+
*/
|
|
9839
|
+
this.currentPage$ = this.request$.pipe(filter(tuiIsPresent), map(({ meta }) => meta.currentPage));
|
|
9840
|
+
/**
|
|
9841
|
+
* {@link Observable} изменения состояния загрузки данных.
|
|
9842
|
+
*/
|
|
9843
|
+
this.loading$ = this.request$.pipe(map(tuiIsFalsy));
|
|
9844
|
+
/**
|
|
9845
|
+
* {@link Observable} изменения списка новостей страницы пагинации.
|
|
9846
|
+
*/
|
|
9847
|
+
this.data$ = this.request$.pipe(filter(tuiIsPresent), map((data) => data.data),
|
|
9848
|
+
// eslint-disable-next-line unicorn/no-array-callback-reference
|
|
9849
|
+
map((newsList) => newsList.filter(tuiIsPresent)), startWith(null));
|
|
9850
|
+
/**
|
|
9851
|
+
* Событие изменения списка новостей страницы пагинации.
|
|
9852
|
+
*/
|
|
9853
|
+
this.dataChangeEvent = outputFromObservable(this.data$.pipe(filter(tuiIsPresent)));
|
|
9854
|
+
inject(SC_USER_INFO$1)
|
|
9855
|
+
.pipe(skip(1), takeUntilDestroyed())
|
|
9856
|
+
.subscribe(() => {
|
|
9857
|
+
// Смена данных пользователя сбрасывает пагинацию на первую страницу.
|
|
9858
|
+
// eslint-disable-next-line no-void
|
|
9859
|
+
void this.router.navigate([], { queryParams: { [SC_PAGE_QUERY_PARAM]: null }, queryParamsHandling: 'merge' });
|
|
9860
|
+
});
|
|
9861
|
+
}
|
|
9862
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNewsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9863
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ScNewsListComponent, isStandalone: true, selector: "sc-news-list", inputs: { perPage: { classPropertyName: "perPage", publicName: "perPage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dataChangeEvent: "dataChangeEvent" }, ngImport: i0, template: "@let newsList = data$ | async;\n<tui-loader\n class=\"mb-4\"\n [showLoader]=\"!!(loading$ | async) && !!newsList\"\n>\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(17rem,1fr))] gap-8\">\n <ng-container *ngIf=\"newsList; else newsListSkeleton\">\n <sc-news-card\n *ngFor=\"let news of newsList\"\n [news]=\"news\"\n [href]=\"'/news/' + news.id\"\n />\n </ng-container>\n <ng-template #newsListSkeleton>\n <sc-news-card-skeleton *ngFor=\"let _ of [].constructor(perPage())\" />\n </ng-template>\n </div>\n</tui-loader>\n<tui-loader [showLoader]=\"!!(loading$ | async)\">\n <sc-pagination-links\n [index]=\"(currentPage$ | async) ?? 1\"\n [length]=\"(pagesLength$ | async)!\"\n />\n</tui-loader>\n", dependencies: [{ kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ScNewsModule }, { kind: "component", type: ScNewsCardComponent, selector: "sc-news-card", inputs: ["news", "href"] }, { kind: "component", type: ScNewsCardSkeletonComponent, selector: "sc-news-card-skeleton", inputs: ["hasImg"] }, { kind: "component", type: ScPaginationLinksComponent, selector: "sc-pagination-links", inputs: ["length", "index", "queryParam", "activePadding", "sidePadding"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9864
|
+
}
|
|
9865
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNewsListComponent, decorators: [{
|
|
9866
|
+
type: Component,
|
|
9867
|
+
args: [{ standalone: true, selector: 'sc-news-list', imports: [TuiLoader, NgIf, NgFor, ScNewsModule, ScPaginationLinksComponent, AsyncPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let newsList = data$ | async;\n<tui-loader\n class=\"mb-4\"\n [showLoader]=\"!!(loading$ | async) && !!newsList\"\n>\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(17rem,1fr))] gap-8\">\n <ng-container *ngIf=\"newsList; else newsListSkeleton\">\n <sc-news-card\n *ngFor=\"let news of newsList\"\n [news]=\"news\"\n [href]=\"'/news/' + news.id\"\n />\n </ng-container>\n <ng-template #newsListSkeleton>\n <sc-news-card-skeleton *ngFor=\"let _ of [].constructor(perPage())\" />\n </ng-template>\n </div>\n</tui-loader>\n<tui-loader [showLoader]=\"!!(loading$ | async)\">\n <sc-pagination-links\n [index]=\"(currentPage$ | async) ?? 1\"\n [length]=\"(pagesLength$ | async)!\"\n />\n</tui-loader>\n" }]
|
|
9868
|
+
}], ctorParameters: () => [] });
|
|
9869
|
+
|
|
9663
9870
|
/**
|
|
9664
9871
|
* Компонент политики обработки персональных данных пользователей сайта.
|
|
9665
9872
|
*/
|
|
@@ -10819,5 +11026,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
10819
11026
|
* Generated bundle index. Do not edit.
|
|
10820
11027
|
*/
|
|
10821
11028
|
|
|
10822
|
-
export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, CostWithDiscountComponent, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IDLE_ITEM_STATE, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_BANNER_DURATION, SC_CATALOG_PRODUCTS_FILTERS, SC_CATALOG_SHOW_PRODUCTS_RECURSIVELY, SC_DATE_FORMATTER, SC_DEBOUNCE_TIME_DEFAULT, SC_DIALOG_SERVICE_TOKEN, SC_ERROR_CHANGE_HANDLER, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_HIDDEN_PRINT_ELEMENTS, SC_MANAGER_QR_HANDLER, SC_NOTIFY_WHEN_IN_STOCK_REQUIRED_FIELDS, SC_ORDER_FORM_CONFIG, SC_ORDER_OPTIONS, SC_PAGE_SIZE_OPTIONS$1 as SC_PAGE_SIZE_OPTIONS, SC_SHOW_HELP_NOTIFICATION_IN_PHONE_INPUT, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddOrEditingCartItemDialogComponent, ScAddOrEditingCartItemFormComponent, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFilterCollapsibleComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderDirectionItemsComponent, ScOrderFormComponent, ScOrderItemComponent, ScOrderItemsByDirection, ScOrderItemsListByDirectionsComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderModule, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareButtonModule, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, cleanupRecaptchaArtifacts, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
|
|
11029
|
+
export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, CostWithDiscountComponent, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IDLE_ITEM_STATE, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_BANNER_DURATION, SC_CATALOG_PRODUCTS_FILTERS, SC_CATALOG_SHOW_PRODUCTS_RECURSIVELY, SC_DATE_FORMATTER, SC_DEBOUNCE_TIME_DEFAULT, SC_DIALOG_SERVICE_TOKEN, SC_ERROR_CHANGE_HANDLER, SC_FAQ_GROUP_QUERY_PARAM, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_HIDDEN_PRINT_ELEMENTS, SC_MANAGER_QR_HANDLER, SC_NOTIFY_WHEN_IN_STOCK_REQUIRED_FIELDS, SC_ORDER_FORM_CONFIG, SC_ORDER_OPTIONS, SC_PAGE_SIZE_OPTIONS$1 as SC_PAGE_SIZE_OPTIONS, SC_SHOW_HELP_NOTIFICATION_IN_PHONE_INPUT, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddOrEditingCartItemDialogComponent, ScAddOrEditingCartItemFormComponent, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFilterCollapsibleComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsListComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderDirectionItemsComponent, ScOrderFormComponent, ScOrderItemComponent, ScOrderItemsByDirection, ScOrderItemsListByDirectionsComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderModule, ScPaginationLinksComponent, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareButtonModule, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, cleanupRecaptchaArtifacts, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scGetFaqGroupQueryParams, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
|
|
10823
11030
|
//# sourceMappingURL=snabcentr-client-ui.mjs.map
|