@snabcentr/client-ui 4.16.10-profstroy → 4.16.12-profstroy

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.
Files changed (49) hide show
  1. package/esm2022/feedback/feedback-form/sc-feedback-form.component.mjs +11 -21
  2. package/esm2022/order/order-items-list/sc-order-items-list.component.mjs +94 -20
  3. package/esm2022/providers/index.mjs +1 -2
  4. package/esm2022/public-api.mjs +2 -1
  5. package/esm2022/samples/sc-sample.module.mjs +3 -10
  6. package/esm2022/services/index.mjs +2 -1
  7. package/esm2022/services/sc-scripts.service.mjs +106 -0
  8. package/esm2022/smart-captcha/index.mjs +5 -0
  9. package/esm2022/smart-captcha/interfaces/index.mjs +6 -0
  10. package/esm2022/smart-captcha/interfaces/sc-smart-captcha-api.mjs +2 -0
  11. package/esm2022/smart-captcha/interfaces/sc-smart-captcha-event.mjs +2 -0
  12. package/esm2022/smart-captcha/interfaces/sc-smart-captcha-language.mjs +2 -0
  13. package/esm2022/smart-captcha/interfaces/sc-smart-captcha-render-params.mjs +2 -0
  14. package/esm2022/smart-captcha/interfaces/sc-smart-captcha-settings.mjs +2 -0
  15. package/esm2022/smart-captcha/interfaces/sc-smart-captcha-window.mjs +2 -0
  16. package/esm2022/smart-captcha/sc-smart-captcha.component.mjs +167 -0
  17. package/esm2022/smart-captcha/services/index.mjs +2 -0
  18. package/esm2022/smart-captcha/services/sc-smart-captcha-loader.service.mjs +87 -0
  19. package/esm2022/smart-captcha/tokens/index.mjs +3 -0
  20. package/esm2022/smart-captcha/tokens/sc-smart-captcha-load-timeout.token.mjs +8 -0
  21. package/esm2022/smart-captcha/tokens/sc-smart-captcha-settings.token.mjs +6 -0
  22. package/feedback/feedback-form/sc-feedback-form.component.d.ts +1 -1
  23. package/fesm2022/snabcentr-client-ui.mjs +459 -82
  24. package/fesm2022/snabcentr-client-ui.mjs.map +1 -1
  25. package/order/order-items-list/sc-order-items-list.component.d.ts +45 -0
  26. package/package.json +2 -3
  27. package/providers/index.d.ts +0 -1
  28. package/public-api.d.ts +1 -0
  29. package/release_notes.tmp +5 -2
  30. package/samples/sc-sample.module.d.ts +1 -2
  31. package/services/index.d.ts +1 -0
  32. package/services/sc-scripts.service.d.ts +51 -0
  33. package/smart-captcha/index.d.ts +4 -0
  34. package/smart-captcha/interfaces/index.d.ts +5 -0
  35. package/smart-captcha/interfaces/sc-smart-captcha-api.d.ts +36 -0
  36. package/smart-captcha/interfaces/sc-smart-captcha-event.d.ts +4 -0
  37. package/smart-captcha/interfaces/sc-smart-captcha-language.d.ts +4 -0
  38. package/smart-captcha/interfaces/sc-smart-captcha-render-params.d.ts +16 -0
  39. package/smart-captcha/interfaces/sc-smart-captcha-settings.d.ts +16 -0
  40. package/smart-captcha/interfaces/sc-smart-captcha-window.d.ts +14 -0
  41. package/smart-captcha/sc-smart-captcha.component.d.ts +92 -0
  42. package/smart-captcha/services/index.d.ts +1 -0
  43. package/smart-captcha/services/sc-smart-captcha-loader.service.d.ts +50 -0
  44. package/smart-captcha/tokens/index.d.ts +2 -0
  45. package/smart-captcha/tokens/sc-smart-captcha-load-timeout.token.d.ts +5 -0
  46. package/smart-captcha/tokens/sc-smart-captcha-settings.token.d.ts +6 -0
  47. package/styles/tailwind/tailwind.scss +40 -2
  48. package/esm2022/providers/cleanup-recaptcha-artifacts.mjs +0 -30
  49. package/providers/cleanup-recaptcha-artifacts.d.ts +0 -7
@@ -1,18 +1,20 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, NgZone, signal, EventEmitter, ChangeDetectorRef, Directive, Input, Output, untracked, Pipe, ElementRef, HostListener, Renderer2, ContentChildren, NgModule, input, computed, Component, ChangeDetectionStrategy, Inject, HostBinding, model, output, SkipSelf, DestroyRef, effect, ContentChild, ViewChild, viewChild, Optional, contentChild, TemplateRef, forwardRef, ViewEncapsulation, Injector, viewChildren, afterNextRender } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, NgZone, RendererFactory2, signal, EventEmitter, ChangeDetectorRef, Directive, Input, Output, untracked, Pipe, ElementRef, HostListener, Renderer2, ContentChildren, NgModule, input, computed, Component, ChangeDetectionStrategy, Inject, HostBinding, model, output, SkipSelf, DestroyRef, effect, ContentChild, ViewChild, viewChild, Optional, contentChild, TemplateRef, forwardRef, ViewEncapsulation, Injector, viewChildren, afterNextRender } 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, ScSuggestionAddressQuality, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScNumberHelper, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe, ScUploadedFile, ScCartService, loadingPlaceholder, SC_ORDER_LOADER, ScMimeTypes, ScCatalogService, SC_URLS, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScCatalogFilterService, ScIdOrSlugPipe, SC_CATEGORY_INFO, ScConfiguratorService, IS_SERVER, RESPONSE, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScOrderItemsGroup, ScOrderItem, ScDeliveryAddressService, ScOrdersService, ScUIService, SC_USER_INFO as SC_USER_INFO$1, ScPaymentType, ScDeliveryType, TERMINAL_PROVIDERS, ScOrderDraft, SC_UPDATE_INTERVAL, ScOrderDraftsService, ScOrderDraftActualizationStatus, ScRouteKeys, ScSeoService, scOrderIsLoaded, SC_PAGE_QUERY_PARAM, ScFrequentlyAskedQuestionsService, SchemaOrgFactory, scGetPageQueryParamChanges, ScNewsService, SC_COMPANY_INFO, ScFeedbackService } from '@snabcentr/client-core';
4
+ import { ScContactsService, ScUserService, ScLocationsService, IS_BROWSER, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, ScImage, ScSuggestionAddressQuality, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScNumberHelper, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe, ScUploadedFile, ScCartService, loadingPlaceholder, SC_ORDER_LOADER, ScMimeTypes, ScCatalogService, SC_URLS, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScCatalogFilterService, ScIdOrSlugPipe, SC_CATEGORY_INFO, ScConfiguratorService, IS_SERVER, RESPONSE, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScOrderItemsGroup, ScOrderItem, ScDeliveryAddressService, ScOrdersService, ScUIService, SC_USER_INFO as SC_USER_INFO$1, ScPaymentType, ScDeliveryType, TERMINAL_PROVIDERS, ScOrderDraft, SC_UPDATE_INTERVAL, ScOrderDraftsService, ScOrderDraftActualizationStatus, 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
- import { EMPTY, BehaviorSubject, switchMap, of, shareReplay, map, Subject, filter, tap, catchError, finalize, startWith, share, timer, scan, takeWhile, endWith, distinctUntilChanged, debounceTime, throwError, combineLatest, pairwise, Observable, Subscription, merge, skip, from, concatMap, noop, first, retry, defaultIfEmpty, exhaustMap, fromEvent, animationFrames, take, takeUntil } from 'rxjs';
6
+ import { EMPTY, BehaviorSubject, switchMap, of, shareReplay, map, Observable, Subject, filter, tap, catchError, finalize, startWith, share, timer, scan, takeWhile, endWith, distinctUntilChanged, debounceTime, throwError, combineLatest, pairwise, Subscription, merge, skip, from, concatMap, noop, first, retry, defaultIfEmpty, exhaustMap, fromEvent, animationFrames, take, takeUntil, defer, timeout, ignoreElements } 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, TuiValidationError, tuiControlValue, tuiIsPresent, tuiMarkControlAsTouchedAndValidate, tuiIsFalsy, TuiAutoFocus, TuiRepeatTimes, TuiSwipeService, TUI_SWIPE_OPTIONS, TuiHovered, TuiDayRange, TuiMonth, tuiPure, tuiProvide, TUI_WINDOW_SIZE, tuiClamp, TUI_TRUE_HANDLER } from '@taiga-ui/cdk';
10
+ import { TuiDay, TuiTime, TuiValueTransformer, TUI_IS_MOBILE, TuiValidationError, tuiControlValue, tuiIsPresent, tuiMarkControlAsTouchedAndValidate, tuiIsFalsy, TuiAutoFocus, TuiRepeatTimes, TuiSwipeService, TUI_SWIPE_OPTIONS, TuiHovered, TuiDayRange, TuiMonth, tuiPure, tuiProvide, TUI_WINDOW_SIZE, tuiClamp, tuiZonefree, TUI_TRUE_HANDLER } from '@taiga-ui/cdk';
11
11
  import * as i2$1 from '@taiga-ui/kit';
12
12
  import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TUI_CONFIRM_WORDS, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiFade, TuiRange, TuiBlock, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, TUI_CONFIRM, tuiFilesAccepted, TuiInputFiles, TuiInputFilesDirective, TuiFilesComponent, TuiFile, TuiFileRejectedPipe, TUI_INPUT_FILES_OPTIONS, tuiCreateFileFormatValidator, tuiCreateFileSizeValidator, TUI_INPUT_FILE_TEXTS, tuiItemsHandlersProvider as tuiItemsHandlersProvider$1, TUI_PAGINATION_TEXTS, TuiAccordionDirective, TuiAccordionItemContent } from '@taiga-ui/kit';
13
+ import * as i2$2 from '@angular/common';
14
+ import { DOCUMENT, CommonModule, NgIf, AsyncPipe, NgTemplateOutlet, NgFor, NgClass } from '@angular/common';
13
15
  import { HttpClient, HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
14
16
  import * as i1$1 from '@angular/forms';
15
- import { FormControl, NgControl, FormGroupDirective, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
17
+ import { FormControl, NgControl, FormGroupDirective, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
16
18
  import * as i1$2 from '@taiga-ui/core';
17
19
  import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiFormatNumberPipe, TuiNumberFormat, TuiHint, TUI_MONTHS, TuiScrollbar, tuiNumberFormatProvider, tuiItemsHandlersProvider, tuiTextfieldOptionsProvider, tuiHintOptionsProvider, TuiHintDriver, TuiHintHover, TuiHintPosition, TuiHintDirective, TuiAlertService, TuiDataListComponent, TuiOption, TuiIconPipe, TUI_BUTTON_OPTIONS, TUI_BUTTON_DEFAULT_OPTIONS, TUI_SPIN_ICONS, TuiSurface, tuiFadeIn } from '@taiga-ui/core';
18
20
  import { isValidPhoneNumber } from 'libphonenumber-js/max';
@@ -24,8 +26,6 @@ import { isNil, isUndefined, isObject, isArray, isString } from 'lodash-es';
24
26
  import * as i5 from '@taiga-ui/core/components/label';
25
27
  import * as i8 from '@maskito/angular';
26
28
  import { MaskitoDirective } from '@maskito/angular';
27
- import * as i2$2 from '@angular/common';
28
- import { DOCUMENT, CommonModule, NgIf, AsyncPipe, NgTemplateOutlet, NgFor, NgClass } from '@angular/common';
29
29
  import * as i2$3 from '@taiga-ui/polymorpheus';
30
30
  import { POLYMORPHEUS_CONTEXT, PolymorpheusComponent, PolymorpheusTemplate, PolymorpheusOutlet, injectContext } from '@taiga-ui/polymorpheus';
31
31
  import * as i2 from 'angularx-qrcode';
@@ -55,8 +55,6 @@ import { Clipboard } from '@angular/cdk/clipboard';
55
55
  import * as i2$5 from '@taiga-ui/experimental/components/expand';
56
56
  import * as i3$3 from '@taiga-ui/kit/components/data-list-wrapper';
57
57
  import { TuiGroup } from '@taiga-ui/core/directives/group';
58
- import * as i4$3 from 'ng-recaptcha-2';
59
- import { RecaptchaFormsModule, RecaptchaModule, RecaptchaComponent } from 'ng-recaptcha-2';
60
58
 
61
59
  /**
62
60
  * Поток для закрытия уведомлений помощи.
@@ -356,36 +354,6 @@ const SC_HIDDEN_PRINT_ELEMENTS = new InjectionToken('SC_HIDDEN_PRINT_ELEMENTS',
356
354
  providedIn: 'root',
357
355
  });
358
356
 
359
- /**
360
- * Селектор iframe reCAPTCHA.
361
- */
362
- const RECAPTCHA_IFRAME_SELECTOR = 'iframe[src*="recaptcha"]';
363
- /**
364
- * Селектор хоста reCAPTCHA.
365
- */
366
- const RECAPTCHA_HOST_SELECTOR = 're-captcha';
367
- /**
368
- * Освобождает ресурсы reCAPTCHA и удаляет осиротевшие iframe после смены маршрута.
369
- *
370
- * @param documentReference Объект документа.
371
- * @param windowReference Объект окна браузера.
372
- */
373
- function cleanupRecaptchaArtifacts(documentReference, windowReference) {
374
- const windowWithRecaptcha = windowReference;
375
- try {
376
- windowWithRecaptcha.grecaptcha?.reset();
377
- }
378
- catch {
379
- // Ошибки reCAPTCHA не должны блокировать навигацию.
380
- }
381
- documentReference.querySelectorAll(RECAPTCHA_IFRAME_SELECTOR).forEach((iframe) => {
382
- if (iframe.closest(RECAPTCHA_HOST_SELECTOR)) {
383
- return;
384
- }
385
- iframe.remove();
386
- });
387
- }
388
-
389
357
  /**
390
358
  * Сервис по работе с печатью страницы.
391
359
  */
@@ -431,6 +399,107 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
431
399
  }]
432
400
  }] });
433
401
 
402
+ /**
403
+ * Сервис для вставки в {@link Document} внешних и дополнительных скриптов.
404
+ */
405
+ class ScScriptsService {
406
+ constructor() {
407
+ /**
408
+ * Объект {@link Document}, предоставляющий доступ к DOM страницы.
409
+ */
410
+ this.document = inject(DOCUMENT);
411
+ /**
412
+ * Объект {@link Renderer2}, предоставляющий доступ к DOM страницы.
413
+ */
414
+ this.renderer = inject(RendererFactory2).createRenderer(null, null);
415
+ /**
416
+ * Признак того, что скрипт выполняется в браузере.
417
+ */
418
+ this.isBrowser = inject(IS_BROWSER);
419
+ /**
420
+ * {@link NgZone} для вставки скриптов вне зоны Angular.
421
+ */
422
+ this.ngZone = inject(NgZone);
423
+ }
424
+ /**
425
+ * Вставляет в конец {@link Document.body} скрипты, заданные строками HTML, вне зоны Angular. Из каждой строки берётся первый `<script>`. На сервере ничего не делает.
426
+ * Принимает только доверенные строки из конфигурации приложения: строка разбирается как HTML и может выполнить код.
427
+ *
428
+ * @param scriptStrings Строки HTML скриптов.
429
+ */
430
+ appendScripts(scriptStrings) {
431
+ if (!this.isBrowser) {
432
+ return;
433
+ }
434
+ this.ngZone.runOutsideAngular(() => {
435
+ scriptStrings.forEach((scriptString) => {
436
+ const script = this.createScript(scriptString);
437
+ if (script) {
438
+ this.renderer.appendChild(this.document.body, script);
439
+ }
440
+ });
441
+ });
442
+ }
443
+ /**
444
+ * Загружает внешний скрипт: при подписке добавляет его в {@link Document.head}, после загрузки выдаёт элемент скрипта и завершается.
445
+ * Если скрипт не загрузился или подписка отменена раньше, удаляет его со страницы; удаление не отменяет загрузку и выполнение уже вставленного скрипта. На сервере завершается без загрузки.
446
+ *
447
+ * @param url Адрес скрипта.
448
+ */
449
+ loadScript$(url) {
450
+ if (!this.isBrowser) {
451
+ return EMPTY;
452
+ }
453
+ return new Observable((subscriber) => {
454
+ const script = this.renderer.createElement('script');
455
+ let isLoaded = false;
456
+ const unlistenLoad = this.renderer.listen(script, 'load', () => {
457
+ isLoaded = true;
458
+ subscriber.next(script);
459
+ subscriber.complete();
460
+ });
461
+ const unlistenError = this.renderer.listen(script, 'error', () => {
462
+ subscriber.error(new Error(`Failed to load script ${url}`));
463
+ });
464
+ this.renderer.setProperty(script, 'src', url);
465
+ this.renderer.appendChild(this.document.head, script);
466
+ return () => {
467
+ unlistenLoad();
468
+ unlistenError();
469
+ if (!isLoaded) {
470
+ this.removeScript(script);
471
+ }
472
+ };
473
+ });
474
+ }
475
+ /**
476
+ * Удаляет скрипт со страницы. При подключённых анимациях Angular узел удаляется при ближайшей проверке изменений.
477
+ *
478
+ * @param script Элемент скрипта.
479
+ */
480
+ removeScript(script) {
481
+ this.renderer.removeChild(script.parentNode, script);
482
+ }
483
+ /**
484
+ * Создаёт {@link HTMLScriptElement} из строки HTML скрипта.
485
+ *
486
+ * @param scriptString Строка HTML скрипта.
487
+ */
488
+ createScript(scriptString) {
489
+ const range = this.document.createRange();
490
+ const fragment = range.createContextualFragment(scriptString);
491
+ return fragment.querySelector('script');
492
+ }
493
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScScriptsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
494
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScScriptsService, providedIn: 'root' }); }
495
+ }
496
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScScriptsService, decorators: [{
497
+ type: Injectable,
498
+ args: [{
499
+ providedIn: 'root',
500
+ }]
501
+ }] });
502
+
434
503
  /**
435
504
  * Проверяет корректность введенного номера телефона.
436
505
  */
@@ -8971,6 +9040,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
8971
9040
  * Компонент списка товаров корзины, заказа или черновика.
8972
9041
  */
8973
9042
  class ScOrderItemsListComponent {
9043
+ /**
9044
+ * Возвращает копию набора с добавленными или убранными ключами.
9045
+ *
9046
+ * @param keys Набор.
9047
+ * @param changed Ключи.
9048
+ * @param isIncluded Признак добавления.
9049
+ */
9050
+ static withKeys(keys, changed, isIncluded) {
9051
+ const next = new Set(keys);
9052
+ changed.forEach((key) => {
9053
+ if (isIncluded) {
9054
+ next.add(key);
9055
+ }
9056
+ else {
9057
+ next.delete(key);
9058
+ }
9059
+ });
9060
+ return next;
9061
+ }
8974
9062
  /**
8975
9063
  * Инициализирует экземпляр класса {@link ScOrderItemsListComponent}.
8976
9064
  * Входы выбора и пометки недоступных позиций могут прийти после списка позиций, поэтому при их изменении выбор синхронизируется заново.
@@ -9025,6 +9113,19 @@ class ScOrderItemsListComponent {
9025
9113
  * Идентификаторы позиций, изменение или удаление которых ждёт очереди или выполняется.
9026
9114
  */
9027
9115
  this.pendingItemsIds = signal(new Set());
9116
+ /**
9117
+ * Названия удаляемых групп.
9118
+ */
9119
+ this.deletingGroupsNames = signal(new Set());
9120
+ /**
9121
+ * Названия групп с позициями, ждущими ответа: такие группы удалить нельзя.
9122
+ */
9123
+ this.pendingGroupsNames = computed(() => {
9124
+ const pendingIds = this.pendingItemsIds();
9125
+ return new Set(this.groups()
9126
+ .filter((group) => group.items.some((item) => pendingIds.has(item.id)))
9127
+ .map((group) => group.name));
9128
+ });
9028
9129
  /**
9029
9130
  * Внутренняя группа формы для выбора позиций.
9030
9131
  */
@@ -9081,6 +9182,10 @@ class ScOrderItemsListComponent {
9081
9182
  * Признак, что можно редактировать позиции.
9082
9183
  */
9083
9184
  this.canEdit = computed(() => this.editable() && 'updateProduct$' in this.orderItemsListLoader);
9185
+ /**
9186
+ * Признак, что группу можно удалить целиком.
9187
+ */
9188
+ this.canDeleteGroups = computed(() => this.canEdit() && 'deleteProducts$' in this.orderItemsListLoader);
9084
9189
  /**
9085
9190
  * Настройки вывода сумм в рублях.
9086
9191
  */
@@ -9196,6 +9301,31 @@ class ScOrderItemsListComponent {
9196
9301
  }), takeUntilDestroyed(this.destroyRef))
9197
9302
  .subscribe({ error: noop });
9198
9303
  }
9304
+ /**
9305
+ * Удаляет все позиции группы после подтверждения. Состав берётся на момент подтверждения.
9306
+ *
9307
+ * @param group Группа позиций.
9308
+ */
9309
+ deleteGroup(group) {
9310
+ if (!this.canDeleteGroups() || this.pendingGroupsNames().has(group.name)) {
9311
+ return;
9312
+ }
9313
+ this.confirmGroupDelete$(group)
9314
+ .pipe(switchMap(() => {
9315
+ const items = this.groups().find(({ name }) => name === group.name)?.items ?? [];
9316
+ const ids = items.map((item) => item.id);
9317
+ if (ids.length === 0) {
9318
+ return EMPTY;
9319
+ }
9320
+ this.setItemsPending(ids, true);
9321
+ this.deletingGroupsNames.update((names) => ScOrderItemsListComponent.withKeys(names, [group.name], true));
9322
+ return this.orderItemsListLoader.deleteProducts$(items, this.orderOptions?.orderId()).pipe(finalize(() => {
9323
+ this.setItemsPending(ids, false);
9324
+ this.deletingGroupsNames.update((names) => ScOrderItemsListComponent.withKeys(names, [group.name], false));
9325
+ }));
9326
+ }), takeUntilDestroyed(this.destroyRef))
9327
+ .subscribe({ error: noop });
9328
+ }
9199
9329
  /**
9200
9330
  * Обновляет количество товара. Количество неактивной позиции не меняется. Пустое или нулевое значение возвращается к сохранённому количеству.
9201
9331
  *
@@ -9268,12 +9398,25 @@ class ScOrderItemsListComponent {
9268
9398
  if (!target) {
9269
9399
  return of(true);
9270
9400
  }
9271
- return this.dialogService
9272
- .open(TUI_CONFIRM, {
9273
- label: `Удалить позицию «${item.product.name}» ${target}?`,
9274
- data: { yes: 'Да, удалить', no: 'Нет, оставить' },
9275
- })
9276
- .pipe(filter(Boolean));
9401
+ return this.confirmRemoval$(`Удалить позицию «${item.product.name}» ${target}?`);
9402
+ }
9403
+ /**
9404
+ * Возвращает {@link Observable}, который выдаёт значение, когда удаление группы подтверждено.
9405
+ *
9406
+ * @param group Группа позиций.
9407
+ */
9408
+ confirmGroupDelete$(group) {
9409
+ const target = this.deleteConfirmationTarget();
9410
+ const source = target ? ` ${target}` : '';
9411
+ return this.confirmRemoval$(`Удалить все позиции группы «${group.name}»${source}?`);
9412
+ }
9413
+ /**
9414
+ * Возвращает {@link Observable}, который выдаёт значение, когда удаление подтверждено.
9415
+ *
9416
+ * @param label Вопрос.
9417
+ */
9418
+ confirmRemoval$(label) {
9419
+ return this.dialogService.open(TUI_CONFIRM, { label, data: { yes: 'Да, удалить', no: 'Нет, оставить' } }).pipe(filter(Boolean));
9277
9420
  }
9278
9421
  /**
9279
9422
  * Отмечает позицию как ожидающую ответа или снимает отметку.
@@ -9282,16 +9425,16 @@ class ScOrderItemsListComponent {
9282
9425
  * @param isPending Признак ожидания.
9283
9426
  */
9284
9427
  setItemPending(id, isPending) {
9285
- this.pendingItemsIds.update((ids) => {
9286
- const next = new Set(ids);
9287
- if (isPending) {
9288
- next.add(id);
9289
- }
9290
- else {
9291
- next.delete(id);
9292
- }
9293
- return next;
9294
- });
9428
+ this.setItemsPending([id], isPending);
9429
+ }
9430
+ /**
9431
+ * Ставит или снимает отметку ожидания у позиций.
9432
+ *
9433
+ * @param itemsIds Идентификаторы позиций.
9434
+ * @param isPending Признак ожидания.
9435
+ */
9436
+ setItemsPending(itemsIds, isPending) {
9437
+ this.pendingItemsIds.update((ids) => ScOrderItemsListComponent.withKeys(ids, itemsIds, isPending));
9295
9438
  }
9296
9439
  /**
9297
9440
  * Предупреждает, что количество не может быть пустым или нулевым, а убрать позицию можно удалением. Поле возвращается к сохранённому количеству
@@ -9304,7 +9447,7 @@ class ScOrderItemsListComponent {
9304
9447
  }
9305
9448
  }
9306
9449
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9307
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListComponent, isStandalone: true, selector: "sc-order-items-list", inputs: { selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, markUnavailable: { classPropertyName: "markUnavailable", publicName: "markUnavailable", isSignal: true, isRequired: false, transformFunction: null }, useLinePrice: { classPropertyName: "useLinePrice", publicName: "useLinePrice", isSignal: true, isRequired: false, transformFunction: null }, measuredInDialog: { classPropertyName: "measuredInDialog", publicName: "measuredInDialog", isSignal: true, isRequired: false, transformFunction: null }, deleteConfirmationTarget: { classPropertyName: "deleteConfirmationTarget", publicName: "deleteConfirmationTarget", isSignal: true, isRequired: false, transformFunction: null }, totalLabel: { classPropertyName: "totalLabel", publicName: "totalLabel", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: false, isRequired: true, transformFunction: null } }, outputs: { selectedItemsIdsChange: "selectedItemsIdsChange" }, ngImport: i0, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n [disabled]=\"selectionFormGroup.disabled\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (group of groups(); track group.name) {\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0433\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (group.name) {\n <div class=\"flex flex-wrap items-end justify-between gap-x-4 gap-y-1 border-b border-tui-base-03 pb-2\">\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"break-words text-body-l-bold\">{{ group.name }}</span>\n <span class=\"text-body-s text-tui-text-02\">\u043F\u043E\u0437\u0438\u0446\u0438\u0439: {{ group.items.length }}, \u0432 \u0437\u0430\u043A\u0430\u0437: {{ group.orderableCount }}</span>\n </div>\n <span class=\"whitespace-nowrap text-body-m-bold\">{{ group.orderableSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</span>\n </div>\n }\n\n @for (item of group.items; track item.id) {\n @if ($index !== 0) {\n <hr class=\"mt-auto h-px w-full bg-tui-clear\" />\n }\n\n <div class=\"flex items-center gap-4 relative\">\n @let isActive = !selectable() || selectionFormGroup.get(item.id)?.value || (markUnavailable() && !item.isOrderable);\n @let isPending = pendingItemsIds().has(item.id);\n @let editedInDialog = isEditedInDialog(item);\n\n <!-- \u0427\u0435\u043A\u0431\u043E\u043A\u0441 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (selectable()) {\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [formControlName]=\"item.id\"\n />\n }\n\n <!-- \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-order-item\n #itemComponent\n [product]=\"item.product\"\n [orderItem]=\"item\"\n [showQuantityControl]=\"canEdit() && !item.configurator && !editedInDialog\"\n [isShowEditControls]=\"canEdit()\"\n [useLinePrice]=\"useLinePrice()\"\n [markUnavailable]=\"markUnavailable()\"\n [isPending]=\"isPending\"\n [settingsAvailable]=\"canEdit() && measuredInDialog() && !item.configurator && !item.isInactive\"\n class=\"w-full duration-200\"\n [class.opacity-60]=\"isItemDimmed(item)\"\n (clickCardEvent)=\"openProductInfoDialog(item.product, item, orderOptions?.orderId())\"\n (quantityValueChanges)=\"patchItem($event, itemComponent)\"\n (clickPriceHistoryEvent)=\"openPriceHistoryChartDialog(item.product)\"\n (clickSettings)=\"openSettingsDialog(item)\"\n [class.pointer-events-none]=\"!isActive\"\n />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (canEdit()) {\n <button\n tuiIconButton\n iconStart=\"@tui.trash\"\n (click)=\"deleteItem(item)\"\n [loading]=\"isPending\"\n size=\"s\"\n appearance=\"secondary\"\n class=\"!absolute left-1 top-1 lg:!relative lg:left-0 lg:top-0 lg:!self-center\"\n ></button>\n }\n </div>\n }\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430. -->\n <div\n scPrint=\"cost\"\n class=\"flex flex-col items-end gap-2.5\"\n >\n @let totalSum = sumSelectedItems();\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0443\u043C\u043C\u0430. -->\n <div class=\"text-base font-bold\">{{ totalLabel() }}: {{ totalSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</div>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TuiFormatNumberPipe, name: "tuiFormatNumber" }, { kind: "component", type: ScOrderItemComponent, selector: "sc-order-item", inputs: ["isShowEditControls", "useLinePrice", "markUnavailable", "isPending", "settingsAvailable"], outputs: ["clickSettings"] }, { kind: "directive", type: ScPrintDirective, selector: "[scPrint]", inputs: ["scPrint"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "pipe", type: TuiCurrencyPipe, name: "tuiCurrency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9450
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListComponent, isStandalone: true, selector: "sc-order-items-list", inputs: { selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, markUnavailable: { classPropertyName: "markUnavailable", publicName: "markUnavailable", isSignal: true, isRequired: false, transformFunction: null }, useLinePrice: { classPropertyName: "useLinePrice", publicName: "useLinePrice", isSignal: true, isRequired: false, transformFunction: null }, measuredInDialog: { classPropertyName: "measuredInDialog", publicName: "measuredInDialog", isSignal: true, isRequired: false, transformFunction: null }, deleteConfirmationTarget: { classPropertyName: "deleteConfirmationTarget", publicName: "deleteConfirmationTarget", isSignal: true, isRequired: false, transformFunction: null }, totalLabel: { classPropertyName: "totalLabel", publicName: "totalLabel", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: false, isRequired: true, transformFunction: null } }, outputs: { selectedItemsIdsChange: "selectedItemsIdsChange" }, ngImport: i0, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n [disabled]=\"selectionFormGroup.disabled\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (group of groups(); track group.name) {\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0433\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (group.name) {\n <div class=\"flex flex-wrap items-end justify-between gap-x-4 gap-y-1 border-b border-tui-base-03 pb-2\">\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"break-words text-body-l-bold\">{{ group.name }}</span>\n <span class=\"text-body-s text-tui-text-02\">\u043F\u043E\u0437\u0438\u0446\u0438\u0439: {{ group.items.length }}, \u0432 \u0437\u0430\u043A\u0430\u0437: {{ group.orderableCount }}</span>\n </div>\n <div class=\"flex flex-col items-end gap-1\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0433\u0440\u0443\u043F\u043F\u044B. -->\n @if (canDeleteGroups()) {\n <button\n tuiButton\n type=\"button\"\n appearance=\"flat\"\n size=\"xs\"\n scPrint=\"defaultNoPrint\"\n [loading]=\"deletingGroupsNames().has(group.name)\"\n [disabled]=\"pendingGroupsNames().has(group.name)\"\n (click)=\"deleteGroup(group)\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C\n </button>\n }\n <span class=\"whitespace-nowrap text-body-m-bold\">{{ group.orderableSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</span>\n </div>\n </div>\n }\n\n @for (item of group.items; track item.id) {\n @if ($index !== 0) {\n <hr class=\"mt-auto h-px w-full bg-tui-clear\" />\n }\n\n <div class=\"flex items-center gap-4 relative\">\n @let isActive = !selectable() || selectionFormGroup.get(item.id)?.value || (markUnavailable() && !item.isOrderable);\n @let isPending = pendingItemsIds().has(item.id);\n @let editedInDialog = isEditedInDialog(item);\n\n <!-- \u0427\u0435\u043A\u0431\u043E\u043A\u0441 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (selectable()) {\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [formControlName]=\"item.id\"\n />\n }\n\n <!-- \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-order-item\n #itemComponent\n [product]=\"item.product\"\n [orderItem]=\"item\"\n [showQuantityControl]=\"canEdit() && !item.configurator && !editedInDialog\"\n [isShowEditControls]=\"canEdit()\"\n [useLinePrice]=\"useLinePrice()\"\n [markUnavailable]=\"markUnavailable()\"\n [isPending]=\"isPending\"\n [settingsAvailable]=\"canEdit() && measuredInDialog() && !item.configurator && !item.isInactive\"\n class=\"w-full duration-200\"\n [class.opacity-60]=\"isItemDimmed(item)\"\n (clickCardEvent)=\"openProductInfoDialog(item.product, item, orderOptions?.orderId())\"\n (quantityValueChanges)=\"patchItem($event, itemComponent)\"\n (clickPriceHistoryEvent)=\"openPriceHistoryChartDialog(item.product)\"\n (clickSettings)=\"openSettingsDialog(item)\"\n [class.pointer-events-none]=\"!isActive\"\n />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (canEdit()) {\n <button\n tuiIconButton\n iconStart=\"@tui.trash\"\n (click)=\"deleteItem(item)\"\n [loading]=\"isPending\"\n size=\"s\"\n appearance=\"secondary\"\n class=\"!absolute left-1 top-1 lg:!relative lg:left-0 lg:top-0 lg:!self-center\"\n ></button>\n }\n </div>\n }\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430. -->\n <div\n scPrint=\"cost\"\n class=\"flex flex-col items-end gap-2.5\"\n >\n @let totalSum = sumSelectedItems();\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0443\u043C\u043C\u0430. -->\n <div class=\"text-base font-bold\">{{ totalLabel() }}: {{ totalSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</div>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TuiFormatNumberPipe, name: "tuiFormatNumber" }, { kind: "component", type: ScOrderItemComponent, selector: "sc-order-item", inputs: ["isShowEditControls", "useLinePrice", "markUnavailable", "isPending", "settingsAvailable"], outputs: ["clickSettings"] }, { kind: "directive", type: ScPrintDirective, selector: "[scPrint]", inputs: ["scPrint"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "pipe", type: TuiCurrencyPipe, name: "tuiCurrency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9308
9451
  }
9309
9452
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListComponent, decorators: [{
9310
9453
  type: Component,
@@ -9320,7 +9463,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
9320
9463
  TuiButton,
9321
9464
  TuiButtonLoading,
9322
9465
  TuiCurrencyPipe,
9323
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n [disabled]=\"selectionFormGroup.disabled\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (group of groups(); track group.name) {\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0433\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (group.name) {\n <div class=\"flex flex-wrap items-end justify-between gap-x-4 gap-y-1 border-b border-tui-base-03 pb-2\">\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"break-words text-body-l-bold\">{{ group.name }}</span>\n <span class=\"text-body-s text-tui-text-02\">\u043F\u043E\u0437\u0438\u0446\u0438\u0439: {{ group.items.length }}, \u0432 \u0437\u0430\u043A\u0430\u0437: {{ group.orderableCount }}</span>\n </div>\n <span class=\"whitespace-nowrap text-body-m-bold\">{{ group.orderableSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</span>\n </div>\n }\n\n @for (item of group.items; track item.id) {\n @if ($index !== 0) {\n <hr class=\"mt-auto h-px w-full bg-tui-clear\" />\n }\n\n <div class=\"flex items-center gap-4 relative\">\n @let isActive = !selectable() || selectionFormGroup.get(item.id)?.value || (markUnavailable() && !item.isOrderable);\n @let isPending = pendingItemsIds().has(item.id);\n @let editedInDialog = isEditedInDialog(item);\n\n <!-- \u0427\u0435\u043A\u0431\u043E\u043A\u0441 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (selectable()) {\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [formControlName]=\"item.id\"\n />\n }\n\n <!-- \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-order-item\n #itemComponent\n [product]=\"item.product\"\n [orderItem]=\"item\"\n [showQuantityControl]=\"canEdit() && !item.configurator && !editedInDialog\"\n [isShowEditControls]=\"canEdit()\"\n [useLinePrice]=\"useLinePrice()\"\n [markUnavailable]=\"markUnavailable()\"\n [isPending]=\"isPending\"\n [settingsAvailable]=\"canEdit() && measuredInDialog() && !item.configurator && !item.isInactive\"\n class=\"w-full duration-200\"\n [class.opacity-60]=\"isItemDimmed(item)\"\n (clickCardEvent)=\"openProductInfoDialog(item.product, item, orderOptions?.orderId())\"\n (quantityValueChanges)=\"patchItem($event, itemComponent)\"\n (clickPriceHistoryEvent)=\"openPriceHistoryChartDialog(item.product)\"\n (clickSettings)=\"openSettingsDialog(item)\"\n [class.pointer-events-none]=\"!isActive\"\n />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (canEdit()) {\n <button\n tuiIconButton\n iconStart=\"@tui.trash\"\n (click)=\"deleteItem(item)\"\n [loading]=\"isPending\"\n size=\"s\"\n appearance=\"secondary\"\n class=\"!absolute left-1 top-1 lg:!relative lg:left-0 lg:top-0 lg:!self-center\"\n ></button>\n }\n </div>\n }\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430. -->\n <div\n scPrint=\"cost\"\n class=\"flex flex-col items-end gap-2.5\"\n >\n @let totalSum = sumSelectedItems();\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0443\u043C\u043C\u0430. -->\n <div class=\"text-base font-bold\">{{ totalLabel() }}: {{ totalSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</div>\n </div>\n</div>\n" }]
9466
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n [disabled]=\"selectionFormGroup.disabled\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (group of groups(); track group.name) {\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0433\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (group.name) {\n <div class=\"flex flex-wrap items-end justify-between gap-x-4 gap-y-1 border-b border-tui-base-03 pb-2\">\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"break-words text-body-l-bold\">{{ group.name }}</span>\n <span class=\"text-body-s text-tui-text-02\">\u043F\u043E\u0437\u0438\u0446\u0438\u0439: {{ group.items.length }}, \u0432 \u0437\u0430\u043A\u0430\u0437: {{ group.orderableCount }}</span>\n </div>\n <div class=\"flex flex-col items-end gap-1\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0433\u0440\u0443\u043F\u043F\u044B. -->\n @if (canDeleteGroups()) {\n <button\n tuiButton\n type=\"button\"\n appearance=\"flat\"\n size=\"xs\"\n scPrint=\"defaultNoPrint\"\n [loading]=\"deletingGroupsNames().has(group.name)\"\n [disabled]=\"pendingGroupsNames().has(group.name)\"\n (click)=\"deleteGroup(group)\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C\n </button>\n }\n <span class=\"whitespace-nowrap text-body-m-bold\">{{ group.orderableSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</span>\n </div>\n </div>\n }\n\n @for (item of group.items; track item.id) {\n @if ($index !== 0) {\n <hr class=\"mt-auto h-px w-full bg-tui-clear\" />\n }\n\n <div class=\"flex items-center gap-4 relative\">\n @let isActive = !selectable() || selectionFormGroup.get(item.id)?.value || (markUnavailable() && !item.isOrderable);\n @let isPending = pendingItemsIds().has(item.id);\n @let editedInDialog = isEditedInDialog(item);\n\n <!-- \u0427\u0435\u043A\u0431\u043E\u043A\u0441 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (selectable()) {\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [formControlName]=\"item.id\"\n />\n }\n\n <!-- \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-order-item\n #itemComponent\n [product]=\"item.product\"\n [orderItem]=\"item\"\n [showQuantityControl]=\"canEdit() && !item.configurator && !editedInDialog\"\n [isShowEditControls]=\"canEdit()\"\n [useLinePrice]=\"useLinePrice()\"\n [markUnavailable]=\"markUnavailable()\"\n [isPending]=\"isPending\"\n [settingsAvailable]=\"canEdit() && measuredInDialog() && !item.configurator && !item.isInactive\"\n class=\"w-full duration-200\"\n [class.opacity-60]=\"isItemDimmed(item)\"\n (clickCardEvent)=\"openProductInfoDialog(item.product, item, orderOptions?.orderId())\"\n (quantityValueChanges)=\"patchItem($event, itemComponent)\"\n (clickPriceHistoryEvent)=\"openPriceHistoryChartDialog(item.product)\"\n (clickSettings)=\"openSettingsDialog(item)\"\n [class.pointer-events-none]=\"!isActive\"\n />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (canEdit()) {\n <button\n tuiIconButton\n iconStart=\"@tui.trash\"\n (click)=\"deleteItem(item)\"\n [loading]=\"isPending\"\n size=\"s\"\n appearance=\"secondary\"\n class=\"!absolute left-1 top-1 lg:!relative lg:left-0 lg:top-0 lg:!self-center\"\n ></button>\n }\n </div>\n }\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430. -->\n <div\n scPrint=\"cost\"\n class=\"flex flex-col items-end gap-2.5\"\n >\n @let totalSum = sumSelectedItems();\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0443\u043C\u043C\u0430. -->\n <div class=\"text-base font-bold\">{{ totalLabel() }}: {{ totalSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</div>\n </div>\n</div>\n" }]
9324
9467
  }], ctorParameters: () => [], propDecorators: { items: [{
9325
9468
  type: Input,
9326
9469
  args: [{ required: true }]
@@ -12203,18 +12346,14 @@ class ScPreviewSampleModule {
12203
12346
  TuiHovered,
12204
12347
  TuiButton,
12205
12348
  TuiButtonLoading,
12206
- TuiLabel,
12207
- RecaptchaFormsModule,
12208
- RecaptchaModule], exports: [ScPreviewSampleComponent] }); }
12349
+ TuiLabel], exports: [ScPreviewSampleComponent] }); }
12209
12350
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScPreviewSampleModule, imports: [CommonModule,
12210
12351
  ReactiveFormsModule,
12211
12352
  TuiError,
12212
12353
  ScFormFieldsModule,
12213
12354
  TuiInputModule,
12214
12355
  TuiInputPhoneModule, i2$1.TuiCarouselComponent, TuiAvatar,
12215
- TuiButtonLoading,
12216
- RecaptchaFormsModule,
12217
- RecaptchaModule] }); }
12356
+ TuiButtonLoading] }); }
12218
12357
  }
12219
12358
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScPreviewSampleModule, decorators: [{
12220
12359
  type: NgModule,
@@ -12234,8 +12373,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
12234
12373
  TuiButton,
12235
12374
  TuiButtonLoading,
12236
12375
  TuiLabel,
12237
- RecaptchaFormsModule,
12238
- RecaptchaModule,
12239
12376
  ],
12240
12377
  exports: [ScPreviewSampleComponent],
12241
12378
  }]
@@ -12255,6 +12392,255 @@ const scGetCurrentRoute = (route) => {
12255
12392
  return currentRoute;
12256
12393
  };
12257
12394
 
12395
+ /**
12396
+ * Токен времени ожидания загрузки скрипта Yandex SmartCaptcha в миллисекундах.
12397
+ */
12398
+ const SC_SMART_CAPTCHA_LOAD_TIMEOUT = new InjectionToken('SC_SMART_CAPTCHA_LOAD_TIMEOUT', {
12399
+ factory: () => 20_000,
12400
+ });
12401
+
12402
+ /**
12403
+ * Сервис загрузки скрипта Yandex SmartCaptcha.
12404
+ */
12405
+ class ScSmartCaptchaLoaderService {
12406
+ constructor() {
12407
+ /**
12408
+ * Сервис для вставки внешних скриптов.
12409
+ */
12410
+ this.scriptsService = inject(ScScriptsService);
12411
+ /**
12412
+ * Окно браузера с API Yandex SmartCaptcha.
12413
+ */
12414
+ this.window = inject(WA_WINDOW);
12415
+ /**
12416
+ * Признак того, что приложение запущено в браузере.
12417
+ */
12418
+ this.isBrowser = inject(IS_BROWSER);
12419
+ /**
12420
+ * Время ожидания загрузки скрипта в миллисекундах.
12421
+ */
12422
+ this.loadTimeout = inject(SC_SMART_CAPTCHA_LOAD_TIMEOUT);
12423
+ /**
12424
+ * Имя функции окна, которую скрипт вызывает после загрузки.
12425
+ */
12426
+ this.onloadCallbackName = 'scSmartCaptchaOnload';
12427
+ /**
12428
+ * Адрес скрипта.
12429
+ */
12430
+ this.scriptUrl = `https://smartcaptcha.cloud.yandex.ru/captcha.js?render=onload&onload=${this.onloadCallbackName}`;
12431
+ /**
12432
+ * Загрузка API, общая для всех виджетов. Скрипт загружается один раз; отписка и истечение ожидания у подписчика загрузку не прерывают, после ошибки следующая подписка загружает скрипт заново.
12433
+ */
12434
+ this.loading$ = defer(() => this.load$()).pipe(shareReplay(1));
12435
+ /**
12436
+ * {@link Observable} API Yandex SmartCaptcha. Значения и ошибки приходят вне зоны Angular, ожидание первого значения ограничено {@link SC_SMART_CAPTCHA_LOAD_TIMEOUT}.
12437
+ */
12438
+ this.api$ = this.loading$.pipe(timeout({ first: this.loadTimeout }), tuiZonefree());
12439
+ }
12440
+ /**
12441
+ * Возвращает API Yandex SmartCaptcha, при необходимости загружая скрипт. На сервере ничего не возвращает.
12442
+ */
12443
+ load$() {
12444
+ if (!this.isBrowser) {
12445
+ return EMPTY;
12446
+ }
12447
+ if (this.window.smartCaptcha) {
12448
+ return of(this.window.smartCaptcha);
12449
+ }
12450
+ return merge(this.onload$(), this.scriptsService.loadScript$(this.scriptUrl).pipe(ignoreElements()));
12451
+ }
12452
+ /**
12453
+ * Ждёт вызова функции из параметра `onload` адреса скрипта и возвращает API, которое скрипт к этому моменту создал.
12454
+ */
12455
+ onload$() {
12456
+ return new Observable((subscriber) => {
12457
+ this.window[this.onloadCallbackName] = () => {
12458
+ if (this.window.smartCaptcha) {
12459
+ subscriber.next(this.window.smartCaptcha);
12460
+ subscriber.complete();
12461
+ }
12462
+ else {
12463
+ subscriber.error(new Error('Yandex SmartCaptcha API is unavailable'));
12464
+ }
12465
+ };
12466
+ return () => {
12467
+ this.window[this.onloadCallbackName] = undefined;
12468
+ };
12469
+ });
12470
+ }
12471
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
12472
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaLoaderService, providedIn: 'root' }); }
12473
+ }
12474
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaLoaderService, decorators: [{
12475
+ type: Injectable,
12476
+ args: [{
12477
+ providedIn: 'root',
12478
+ }]
12479
+ }] });
12480
+
12481
+ /**
12482
+ * Токен настроек Yandex SmartCaptcha. Задаётся приложением.
12483
+ */
12484
+ const SC_SMART_CAPTCHA_SETTINGS = new InjectionToken('SC_SMART_CAPTCHA_SETTINGS');
12485
+
12486
+ /* eslint-disable no-use-before-define */
12487
+ /**
12488
+ * Виджет Yandex SmartCaptcha. Значение поля ввода - токен прохождения проверки, `null` пока проверка не пройдена или токен истёк.
12489
+ */
12490
+ class ScSmartCaptchaComponent {
12491
+ /**
12492
+ * Инициализирует экземпляр класса {@link ScSmartCaptchaComponent}.
12493
+ */
12494
+ constructor() {
12495
+ /**
12496
+ * Контейнер, в котором отрисовывается виджет.
12497
+ */
12498
+ this.container = viewChild.required('container');
12499
+ /**
12500
+ * Настройки Yandex SmartCaptcha.
12501
+ */
12502
+ this.settings = inject(SC_SMART_CAPTCHA_SETTINGS);
12503
+ /**
12504
+ * Сервис загрузки скрипта Yandex SmartCaptcha.
12505
+ */
12506
+ this.loader = inject(ScSmartCaptchaLoaderService);
12507
+ /**
12508
+ * {@link NgZone} для отрисовки виджета вне зоны Angular и возврата его событий в зону.
12509
+ */
12510
+ this.ngZone = inject(NgZone);
12511
+ /**
12512
+ * Ссылка для автоматического управления уничтожением зависимостей.
12513
+ */
12514
+ this.destroyRef = inject(DestroyRef);
12515
+ /**
12516
+ * Признак того, что капчу не удалось загрузить.
12517
+ */
12518
+ this.hasError = signal(false);
12519
+ /**
12520
+ * Функции отписки от событий виджета.
12521
+ */
12522
+ this.unsubscribers = [];
12523
+ /**
12524
+ * Обработчик изменения значения поля ввода.
12525
+ */
12526
+ this.onChange = noop;
12527
+ /**
12528
+ * Обработчик касания поля ввода.
12529
+ */
12530
+ this.onTouched = noop;
12531
+ afterNextRender(() => {
12532
+ this.load();
12533
+ });
12534
+ this.destroyRef.onDestroy(() => {
12535
+ this.destroyWidget();
12536
+ });
12537
+ }
12538
+ /** @inheritDoc */
12539
+ writeValue(token) {
12540
+ const { api, widgetId } = this;
12541
+ if (!token && api && widgetId !== undefined) {
12542
+ this.ngZone.runOutsideAngular(() => {
12543
+ api.reset(widgetId);
12544
+ });
12545
+ }
12546
+ }
12547
+ /** @inheritDoc */
12548
+ registerOnChange(onChange) {
12549
+ this.onChange = onChange;
12550
+ }
12551
+ /** @inheritDoc */
12552
+ registerOnTouched(onTouched) {
12553
+ this.onTouched = onTouched;
12554
+ }
12555
+ /**
12556
+ * Повторяет загрузку капчи после ошибки.
12557
+ */
12558
+ retry() {
12559
+ this.hasError.set(false);
12560
+ this.load();
12561
+ }
12562
+ /**
12563
+ * Загружает API Yandex SmartCaptcha и отрисовывает виджет (API приходит вне зоны Angular). При ошибке загрузки или отрисовки показывает сообщение.
12564
+ */
12565
+ load() {
12566
+ this.loader.api$
12567
+ .pipe(tap((api) => {
12568
+ this.render(api);
12569
+ }), takeUntilDestroyed(this.destroyRef))
12570
+ .subscribe({
12571
+ error: () => {
12572
+ this.showError();
12573
+ },
12574
+ });
12575
+ }
12576
+ /**
12577
+ * Отрисовывает виджет и подписывается на его события.
12578
+ *
12579
+ * @param api API Yandex SmartCaptcha.
12580
+ */
12581
+ render(api) {
12582
+ const widgetId = api.render(this.container().nativeElement, { sitekey: this.settings.sitekey, hl: this.settings.language });
12583
+ this.api = api;
12584
+ this.widgetId = widgetId;
12585
+ this.unsubscribers = [
12586
+ api.subscribe(widgetId, 'success', (token) => {
12587
+ this.updateValue(typeof token === 'string' ? token : null);
12588
+ }),
12589
+ api.subscribe(widgetId, 'token-expired', () => {
12590
+ this.updateValue(null);
12591
+ api.reset(widgetId);
12592
+ }),
12593
+ api.subscribe(widgetId, 'javascript-error', () => {
12594
+ this.showError();
12595
+ }),
12596
+ ];
12597
+ }
12598
+ /**
12599
+ * Передаёт форме новое значение токена.
12600
+ *
12601
+ * @param token Токен прохождения проверки.
12602
+ */
12603
+ updateValue(token) {
12604
+ this.ngZone.run(() => {
12605
+ this.onChange(token);
12606
+ this.onTouched();
12607
+ });
12608
+ }
12609
+ /**
12610
+ * Удаляет виджет, очищает токен и показывает сообщение об ошибке.
12611
+ */
12612
+ showError() {
12613
+ this.destroyWidget();
12614
+ this.ngZone.run(() => {
12615
+ this.hasError.set(true);
12616
+ this.onChange(null);
12617
+ });
12618
+ }
12619
+ /**
12620
+ * Удаляет виджет вместе с созданными им элементами и обработчиками.
12621
+ */
12622
+ destroyWidget() {
12623
+ this.unsubscribers.forEach((unsubscribe) => {
12624
+ unsubscribe();
12625
+ });
12626
+ this.unsubscribers = [];
12627
+ const { api, widgetId } = this;
12628
+ if (api && widgetId !== undefined) {
12629
+ this.ngZone.runOutsideAngular(() => {
12630
+ api.destroy(widgetId);
12631
+ });
12632
+ }
12633
+ this.api = undefined;
12634
+ this.widgetId = undefined;
12635
+ }
12636
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12637
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScSmartCaptchaComponent, isStandalone: true, selector: "sc-smart-captcha", providers: [tuiProvide(NG_VALUE_ACCESSOR, ScSmartCaptchaComponent, true)], viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #container\n class=\"min-h-[7.375rem]\"\n [class.hidden]=\"hasError()\"\n></div>\n@if (hasError()) {\n <p\n role=\"alert\"\n class=\"text-body-s text-tui-text-negative\"\n >\n \u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u00AB\u042F \u043D\u0435 \u0440\u043E\u0431\u043E\u0442\u00BB.\n <button\n tuiLink\n type=\"button\"\n (click)=\"retry()\"\n >\n \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C\n </button>\n </p>\n}\n", dependencies: [{ kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12638
+ }
12639
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaComponent, decorators: [{
12640
+ type: Component,
12641
+ args: [{ standalone: true, selector: 'sc-smart-captcha', imports: [TuiLink], providers: [tuiProvide(NG_VALUE_ACCESSOR, ScSmartCaptchaComponent, true)], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #container\n class=\"min-h-[7.375rem]\"\n [class.hidden]=\"hasError()\"\n></div>\n@if (hasError()) {\n <p\n role=\"alert\"\n class=\"text-body-s text-tui-text-negative\"\n >\n \u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u00AB\u042F \u043D\u0435 \u0440\u043E\u0431\u043E\u0442\u00BB.\n <button\n tuiLink\n type=\"button\"\n (click)=\"retry()\"\n >\n \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C\n </button>\n </p>\n}\n" }]
12642
+ }], ctorParameters: () => [] });
12643
+
12258
12644
  /**
12259
12645
  * Компонент формы обратной связи.
12260
12646
  */
@@ -12326,7 +12712,7 @@ class ScFeedbackFormComponent {
12326
12712
  }
12327
12713
  }));
12328
12714
  /**
12329
- * Поле ввода для recaptcha.
12715
+ * Поле ввода токена капчи, имя `recaptcha` задано API.
12330
12716
  */
12331
12717
  this.recaptchaControl = new FormControl(null, Validators.required);
12332
12718
  /**
@@ -12368,8 +12754,10 @@ class ScFeedbackFormComponent {
12368
12754
  }),
12369
12755
  // eslint-disable-next-line rxjs/no-implicit-any-catch
12370
12756
  catchError((error) => {
12757
+ // Токен капчи одноразовый: для повторной отправки нужна новая проверка.
12758
+ this.recaptchaControl.reset();
12371
12759
  this.form.markAllAsTouched();
12372
- const { errors, message } = error.error;
12760
+ const { errors, message } = (error.error ?? {});
12373
12761
  if (errors) {
12374
12762
  Object.keys(errors).forEach((key) => {
12375
12763
  const control = this.form.get(key);
@@ -12406,16 +12794,6 @@ class ScFeedbackFormComponent {
12406
12794
  * Список стран.
12407
12795
  */
12408
12796
  this.countries = getCountries();
12409
- /**
12410
- * Это решение ошибки #4940, было @see [предложено](https://github.com/DethAriel/ng-recaptcha/issues/123#issuecomment-426112101) автором {@link RecaptchaComponent}.
12411
- */
12412
- RecaptchaComponent.prototype.ngOnDestroy = function () {
12413
- // eslint-disable-next-line @typescript-eslint/dot-notation
12414
- if (this['subscription']) {
12415
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/dot-notation
12416
- this['subscription'].unsubscribe();
12417
- }
12418
- };
12419
12797
  merge(this.user$, this.accepted$.pipe(tap((files) => {
12420
12798
  this.form.controls.files.reset([]);
12421
12799
  this.onUploadFile(files);
@@ -12539,7 +12917,7 @@ class ScFeedbackFormComponent {
12539
12917
  return ScPhoneService.isValidPhoneNumberForMessenger(value);
12540
12918
  }
12541
12919
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScFeedbackFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12542
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFeedbackFormComponent, isStandalone: true, selector: "sc-feedback-form", inputs: { slugInput: { classPropertyName: "slugInput", publicName: "slug", isSignal: true, isRequired: false, transformFunction: null }, fieldsInput: { classPropertyName: "fieldsInput", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickOffer: "clickOffer", sendSuccess: "sendSuccess" }, ngImport: i0, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- recaptcha -->\n @if (form.get('recaptcha')) {\n <div\n class=\"mb-3 max-h-20 overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': form.get('recaptcha')?.valid }\"\n >\n <re-captcha formControlName=\"recaptcha\" />\n </div>\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "ngmodule", type: ScFormFieldsModule }, { kind: "component", type: ScSuggestionFieldComponent, selector: "sc-suggestion-field", inputs: ["type"], outputs: ["selectedClick"] }, { kind: "ngmodule", type: TuiInputModule }, { kind: "component", type: i3.TuiInputComponent, selector: "tui-input" }, { kind: "directive", type: i3.TuiInputDirective, selector: "tui-input" }, { kind: "ngmodule", type: TuiInputPhoneModule }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "ngmodule", type: RecaptchaFormsModule }, { kind: "directive", type: i4$3.RecaptchaValueAccessorDirective, selector: "re-captcha[formControlName],re-captcha[formControl],re-captcha[ngModel]" }, { kind: "ngmodule", type: RecaptchaModule }, { kind: "component", type: i4$3.RecaptchaComponent, selector: "re-captcha", inputs: ["id", "siteKey", "theme", "type", "size", "tabIndex", "badge", "errorMode"], outputs: ["resolved", "error", "errored"], exportAs: ["reCaptcha"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TuiTextareaModule }, { kind: "component", type: i3.TuiTextareaComponent, selector: "tui-textarea", inputs: ["rows", "maxLength", "expandable"] }, { kind: "directive", type: i3.TuiTextareaDirective, selector: "tui-textarea" }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiTitle, selector: "[tuiTitle]", inputs: ["tuiTitle"] }, { kind: "component", type: i2$1.TuiFile, selector: "tui-file,a[tuiFile],button[tuiFile]", inputs: ["file", "state", "size", "showDelete", "showSize", "leftContent"], outputs: ["remove"] }, { kind: "component", type: i2$1.TuiInputFiles, selector: "label[tuiInputFiles]" }, { kind: "component", type: i2$1.TuiFilesComponent, selector: "tui-files", inputs: ["max", "expanded"], outputs: ["expandedChange"] }, { kind: "pipe", type: i2$1.TuiFileRejectedPipe, name: "tuiFileRejected" }, { kind: "directive", type: i2$1.TuiInputFilesDirective, selector: "input[tuiInputFiles]", outputs: ["reject"] }, { kind: "component", type: TuiInputPhoneInternational, selector: "input[tuiInputPhoneInternational]", inputs: ["countrySearch", "countries", "countryIsoCode"], outputs: ["countryIsoCodeChange"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "pipe", type: TuiSortCountriesPipe, name: "tuiSortCountries" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12920
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFeedbackFormComponent, isStandalone: true, selector: "sc-feedback-form", inputs: { slugInput: { classPropertyName: "slugInput", publicName: "slug", isSignal: true, isRequired: false, transformFunction: null }, fieldsInput: { classPropertyName: "fieldsInput", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickOffer: "clickOffer", sendSuccess: "sendSuccess" }, ngImport: i0, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- \u041A\u0430\u043F\u0447\u0430 -->\n @if (form.get('recaptcha'); as recaptcha) {\n <div\n class=\"mb-3 max-h-[7.5rem] overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': recaptcha.valid }\"\n [attr.inert]=\"recaptcha.valid ? '' : null\"\n >\n <sc-smart-captcha formControlName=\"recaptcha\" />\n </div>\n @if (recaptcha.getError('serverResponse'); as serverErrors) {\n <tui-error [error]=\"serverErrors.join(' ')\" />\n }\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "ngmodule", type: ScFormFieldsModule }, { kind: "component", type: ScSuggestionFieldComponent, selector: "sc-suggestion-field", inputs: ["type"], outputs: ["selectedClick"] }, { kind: "ngmodule", type: TuiInputModule }, { kind: "component", type: i3.TuiInputComponent, selector: "tui-input" }, { kind: "directive", type: i3.TuiInputDirective, selector: "tui-input" }, { kind: "ngmodule", type: TuiInputPhoneModule }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "component", type: ScSmartCaptchaComponent, selector: "sc-smart-captcha" }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TuiTextareaModule }, { kind: "component", type: i3.TuiTextareaComponent, selector: "tui-textarea", inputs: ["rows", "maxLength", "expandable"] }, { kind: "directive", type: i3.TuiTextareaDirective, selector: "tui-textarea" }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiTitle, selector: "[tuiTitle]", inputs: ["tuiTitle"] }, { kind: "component", type: i2$1.TuiFile, selector: "tui-file,a[tuiFile],button[tuiFile]", inputs: ["file", "state", "size", "showDelete", "showSize", "leftContent"], outputs: ["remove"] }, { kind: "component", type: i2$1.TuiInputFiles, selector: "label[tuiInputFiles]" }, { kind: "component", type: i2$1.TuiFilesComponent, selector: "tui-files", inputs: ["max", "expanded"], outputs: ["expandedChange"] }, { kind: "pipe", type: i2$1.TuiFileRejectedPipe, name: "tuiFileRejected" }, { kind: "directive", type: i2$1.TuiInputFilesDirective, selector: "input[tuiInputFiles]", outputs: ["reject"] }, { kind: "component", type: TuiInputPhoneInternational, selector: "input[tuiInputPhoneInternational]", inputs: ["countrySearch", "countries", "countryIsoCode"], outputs: ["countryIsoCodeChange"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "pipe", type: TuiSortCountriesPipe, name: "tuiSortCountries" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12543
12921
  }
12544
12922
  __decorate([
12545
12923
  tuiPure
@@ -12559,8 +12937,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
12559
12937
  TuiButton,
12560
12938
  TuiButtonLoading,
12561
12939
  TuiLabel,
12562
- RecaptchaFormsModule,
12563
- RecaptchaModule,
12940
+ ScSmartCaptchaComponent,
12564
12941
  TuiLink,
12565
12942
  NgIf,
12566
12943
  TuiTextareaModule,
@@ -12570,7 +12947,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
12570
12947
  TuiInputPhoneInternational,
12571
12948
  ...TuiTextfield,
12572
12949
  TuiSortCountriesPipe,
12573
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- recaptcha -->\n @if (form.get('recaptcha')) {\n <div\n class=\"mb-3 max-h-20 overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': form.get('recaptcha')?.valid }\"\n >\n <re-captcha formControlName=\"recaptcha\" />\n </div>\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n" }]
12950
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- \u041A\u0430\u043F\u0447\u0430 -->\n @if (form.get('recaptcha'); as recaptcha) {\n <div\n class=\"mb-3 max-h-[7.5rem] overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': recaptcha.valid }\"\n [attr.inert]=\"recaptcha.valid ? '' : null\"\n >\n <sc-smart-captcha formControlName=\"recaptcha\" />\n </div>\n @if (recaptcha.getError('serverResponse'); as serverErrors) {\n <tui-error [error]=\"serverErrors.join(' ')\" />\n }\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n" }]
12574
12951
  }], ctorParameters: () => [], propDecorators: { isValidPhoneNumberForMessenger: [] } });
12575
12952
 
12576
12953
  /**
@@ -13157,5 +13534,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
13157
13534
  * Generated bundle index. Do not edit.
13158
13535
  */
13159
13536
 
13160
- 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_BLOCK_INCOMPLETE_ADDRESS, SC_CATALOG_FILTER_PREVIEWS, 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_FAQ_QUESTION_ANCHOR_PREFIX, 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, ScAddressQualityDirective, ScAddressQualityService, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFilterCollapsibleComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScConfirmAlertComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScDraftExpirationClockService, ScDraftListHelper, ScDraftListUpdatesService, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewOrderItemsComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsListComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderFormComponent, ScOrderItemComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderItemsListWithActionsComponent, ScOrderModule, ScPaginationLinksComponent, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScProfstroyActualizationPanelComponent, ScProfstroyBadgeComponent, ScProfstroyDraftBadgesComponent, ScProfstroyDraftCreateDialogComponent, ScProfstroyDraftHelper, ScProfstroyDraftState, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareDialogComponent, ScShareMessengersLinksComponent, ScShareService, 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, scGetFaqQuestionAnchor, scGetFaqQuestionIdFromAnchor, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
13537
+ 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_BLOCK_INCOMPLETE_ADDRESS, SC_CATALOG_FILTER_PREVIEWS, 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_FAQ_QUESTION_ANCHOR_PREFIX, 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_SMART_CAPTCHA_LOAD_TIMEOUT, SC_SMART_CAPTCHA_SETTINGS, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddOrEditingCartItemDialogComponent, ScAddOrEditingCartItemFormComponent, ScAddressQualityDirective, ScAddressQualityService, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFilterCollapsibleComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScConfirmAlertComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScDraftExpirationClockService, ScDraftListHelper, ScDraftListUpdatesService, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewOrderItemsComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsListComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderFormComponent, ScOrderItemComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderItemsListWithActionsComponent, ScOrderModule, ScPaginationLinksComponent, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScProfstroyActualizationPanelComponent, ScProfstroyBadgeComponent, ScProfstroyDraftBadgesComponent, ScProfstroyDraftCreateDialogComponent, ScProfstroyDraftHelper, ScProfstroyDraftState, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScScriptsService, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareDialogComponent, ScShareMessengersLinksComponent, ScShareService, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSmartCaptchaComponent, ScSmartCaptchaLoaderService, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scGetFaqGroupQueryParams, scGetFaqQuestionAnchor, scGetFaqQuestionIdFromAnchor, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
13161
13538
  //# sourceMappingURL=snabcentr-client-ui.mjs.map