@snabcentr/client-ui 4.5.5 → 4.6.1

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 (27) hide show
  1. package/catalog/catalog-filters/sc-catalog-filters.component.d.ts +7 -4
  2. package/configurators/index.d.ts +1 -0
  3. package/configurators/models/index.d.ts +1 -0
  4. package/configurators/models/sandwich_m2/sc-i-sandwich-m2-settings.d.ts +18 -0
  5. package/configurators/sandwich/sc-sandwich.component.d.ts +24 -0
  6. package/configurators/sandwich_m2/index.d.ts +4 -0
  7. package/configurators/sandwich_m2/sandwich-m2-skeleton/sc-sandwich-m2-skeleton.component.d.ts +8 -0
  8. package/configurators/sandwich_m2/sc-i-new-cart-item-sandwich-m2.d.ts +23 -0
  9. package/configurators/sandwich_m2/sc-sandwich-m2-item-submit-state.d.ts +13 -0
  10. package/configurators/sandwich_m2/sc-sandwich-m2.component.d.ts +267 -0
  11. package/directives/next-input-focus/sc-next-input-focus.directive.d.ts +14 -5
  12. package/esm2022/catalog/catalog-filters/sc-catalog-filters.component.mjs +23 -10
  13. package/esm2022/configurators/index.mjs +2 -1
  14. package/esm2022/configurators/models/index.mjs +2 -1
  15. package/esm2022/configurators/models/sandwich_m2/sc-i-sandwich-m2-settings.mjs +2 -0
  16. package/esm2022/configurators/sandwich/sc-sandwich.component.mjs +57 -6
  17. package/esm2022/configurators/sandwich_m2/index.mjs +5 -0
  18. package/esm2022/configurators/sandwich_m2/sandwich-m2-skeleton/sc-sandwich-m2-skeleton.component.mjs +14 -0
  19. package/esm2022/configurators/sandwich_m2/sc-i-new-cart-item-sandwich-m2.mjs +2 -0
  20. package/esm2022/configurators/sandwich_m2/sc-sandwich-m2-item-submit-state.mjs +5 -0
  21. package/esm2022/configurators/sandwich_m2/sc-sandwich-m2.component.mjs +561 -0
  22. package/esm2022/directives/next-input-focus/sc-next-input-focus.directive.mjs +39 -12
  23. package/fesm2022/snabcentr-client-ui.mjs +672 -24
  24. package/fesm2022/snabcentr-client-ui.mjs.map +1 -1
  25. package/package.json +2 -2
  26. package/release_notes.tmp +4 -2
  27. package/styles/tailwind/tailwind.scss +72 -0
@@ -3,13 +3,13 @@ import { InjectionToken, inject, Injectable, NgZone, signal, EventEmitter, Chang
3
3
  import * as i1 from '@snabcentr/client-core';
4
4
  import { ScContactsService, ScUserService, ScLocationsService, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, ScImage, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe, ScCartService, ScUploadedFile, SC_ORDER_LOADER, ScMimeTypes, ScCatalogService, SC_URLS, IS_SERVER, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScCatalogFilterService, ScIdOrSlugPipe, SC_CATEGORY_INFO, ScConfiguratorService, RESPONSE, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScOrderDraftsService, ScRouteKeys, ScSeoService, scOrderIsLoaded, ScFrequentlyAskedQuestionsService, 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, Observable, pairwise, merge, noop, first, skip } 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, Observable, pairwise, merge, skip, from, concatMap, noop, first } 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
10
  import { TuiDay, TuiTime, TuiValueTransformer, TUI_IS_MOBILE, tuiControlValue, tuiIsPresent, tuiMarkControlAsTouchedAndValidate, tuiIsFalsy, TuiAutoFocus, TuiRepeatTimes, TuiHovered, TuiDayRange, TuiMonth, tuiPure, tuiProvide, TUI_WINDOW_SIZE, TuiValidationError, TUI_TRUE_HANDLER } from '@taiga-ui/cdk';
11
11
  import * as i2$1 from '@taiga-ui/kit';
12
- import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiInputRange, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, TUI_CONFIRM, tuiItemsHandlersProvider as tuiItemsHandlersProvider$1, TuiPagination, TuiAccordionDirective, TuiAccordionItemContent, tuiFilesAccepted } from '@taiga-ui/kit';
12
+ import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiRadio, TuiInputRange, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, TUI_CONFIRM, tuiItemsHandlersProvider as tuiItemsHandlersProvider$1, TuiPagination, TuiAccordionDirective, TuiAccordionItemContent, tuiFilesAccepted } from '@taiga-ui/kit';
13
13
  import { HttpClient, HttpErrorResponse } from '@angular/common/http';
14
14
  import * as i1$1 from '@angular/forms';
15
15
  import { FormControl, FormGroupDirective, NgControl, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -25,7 +25,7 @@ import * as i5 from '@taiga-ui/core/components/label';
25
25
  import * as i8 from '@maskito/angular';
26
26
  import { MaskitoDirective } from '@maskito/angular';
27
27
  import * as i2$2 from '@angular/common';
28
- import { DOCUMENT, CommonModule, NgIf, AsyncPipe, NgFor, NgClass } from '@angular/common';
28
+ import { DOCUMENT, CommonModule, NgIf, AsyncPipe, DecimalPipe, 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';
@@ -838,36 +838,63 @@ const ScLinks = [ScTelLinkDirective, ScEmailLinkDirective, ScPhoneFormatPipe];
838
838
  /* eslint-disable lodash/prefer-lodash-method */
839
839
  /**
840
840
  * Директива для перехода по нажатию клавиши enter на следующее поле ввода формы.
841
+ * Поддерживает как legacy Taiga UI v3 контролы (AbstractTuiControl), так и новые v4
842
+ * (tui-textfield + нативные input) — через DOM-fallback.
841
843
  */
842
844
  class ScNextInputFocusDirective {
845
+ constructor() {
846
+ /**
847
+ * Элемент формы.
848
+ */
849
+ this.el = inject(ElementRef);
850
+ }
843
851
  /**
844
- * Слушатель нажатия на клавишу enter. Предотвращает событие submit, выполняет смену фокуса на следующее поле ввода TuiFocusableElementAccessor.
852
+ * По Enter переход на следующее редактируемое поле формы.
853
+ * Сначала пробует legacy Taiga UI v3 контролы, затем DOM fallback для v4.
845
854
  *
846
- * @param event Объект события нажатия на клавишу.
855
+ * @param event Событие нажатия клавиши.
847
856
  */
848
857
  onFormKeyDownEnter(event) {
849
858
  if (event.code !== 'Enter' && event.key !== 'Enter') {
850
859
  return;
851
860
  }
852
861
  event.preventDefault();
853
- const elements = this.focusableElements.toArray();
854
- const focusedIndex = elements.findIndex((control) => control.focused);
855
- if (focusedIndex !== -1) {
856
- const nextElement = elements[focusedIndex + 1]?.nativeFocusableElement;
857
- if (nextElement) {
858
- nextElement.focus();
862
+ // Taiga UI v3: переход через AbstractTuiControl (legacy-обёртки tui-input, tui-input-number и др.).
863
+ const legacy = this.legacyControls.toArray();
864
+ const legacyIndex = legacy.findIndex((control) => control.focused);
865
+ if (legacyIndex !== -1) {
866
+ const next = legacy[legacyIndex + 1]?.nativeFocusableElement;
867
+ if (next) {
868
+ next.focus();
869
+ return;
859
870
  }
860
871
  }
872
+ // Taiga UI v4: переход через DOM — ищем нативные input внутри формы (tui-textfield + директивы).
873
+ const { target } = event;
874
+ if (!(target instanceof HTMLInputElement) || target.type === 'range') {
875
+ return;
876
+ }
877
+ // eslint-disable-next-line unicorn/prefer-spread
878
+ const inputs = Array.from(this.el.nativeElement.querySelectorAll('input')).filter((element) => element.type !== 'range' && !element.readOnly && !element.disabled);
879
+ const currentIndex = inputs.indexOf(target);
880
+ if (currentIndex === -1) {
881
+ return;
882
+ }
883
+ const nextInput = inputs.at(currentIndex + 1);
884
+ if (nextInput) {
885
+ nextInput.focus();
886
+ nextInput.select();
887
+ }
861
888
  }
862
889
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNextInputFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
863
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: ScNextInputFocusDirective, selector: "form[ScNextInputFocus]", host: { listeners: { "keydown": "onFormKeyDownEnter($event)" } }, queries: [{ propertyName: "focusableElements", predicate: AbstractTuiControl, descendants: true }], ngImport: i0 }); }
890
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: ScNextInputFocusDirective, selector: "form[ScNextInputFocus]", host: { listeners: { "keydown": "onFormKeyDownEnter($event)" } }, queries: [{ propertyName: "legacyControls", predicate: AbstractTuiControl, descendants: true }], ngImport: i0 }); }
864
891
  }
865
892
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNextInputFocusDirective, decorators: [{
866
893
  type: Directive,
867
894
  args: [{
868
895
  selector: 'form[ScNextInputFocus]',
869
896
  }]
870
- }], propDecorators: { focusableElements: [{
897
+ }], propDecorators: { legacyControls: [{
871
898
  type: ContentChildren,
872
899
  args: [AbstractTuiControl, { descendants: true }]
873
900
  }], onFormKeyDownEnter: [{
@@ -5632,15 +5659,14 @@ class ScCatalogFiltersComponent {
5632
5659
  /**
5633
5660
  * {@link Observable} фильтров категории.
5634
5661
  */
5635
- this.filters = toSignal(inject(SC_CATEGORY_INFO).pipe(filter(tuiIsPresent), switchMap((category) => this.catalogFilterService.getFilters$(this.idOrSlugPipe.transform(category), this.isRecursively)), map((filters) => filters.filter((item) => item.type !== 'range' || item.min !== item.max)), tap((filters) => {
5636
- // Обновляем состав контролов формы на основе полученных фильтров.
5662
+ this.filters = toSignal(inject(SC_CATEGORY_INFO).pipe(catchError(() => EMPTY), filter(tuiIsPresent), switchMap((category) => this.catalogFilterService.getFilters$(this.idOrSlugPipe.transform(category), this.isRecursively)), map((filters) => filters.filter((item) => item.type !== 'range' || item.min !== item.max)), tap((filters) => {
5637
5663
  this.updateFormControls(filters);
5638
5664
  }), share()));
5639
5665
  /**
5640
5666
  * FormGroup для фильтров.
5641
5667
  */
5642
5668
  this.form = new FormGroup({});
5643
- this.form.valueChanges.pipe(debounceTime(0), takeUntilDestroyed()).subscribe((filters) => {
5669
+ this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe((filters) => {
5644
5670
  this.catalogProductsFilters$.next(this.buildPropertyFilters(filters));
5645
5671
  });
5646
5672
  }
@@ -5699,11 +5725,17 @@ class ScCatalogFiltersComponent {
5699
5725
  return result;
5700
5726
  }
5701
5727
  /**
5702
- * Создает FormGroup для checkbox.
5728
+ * Создает контрол формы для фильтра типа checkbox.
5729
+ *
5730
+ * Если `isMultiple === false` — возвращает {@link FormControl} строкового идентификатора (логика radio),
5731
+ * иначе — {@link FormGroup} независимых булевых контролов по идентификаторам значений.
5703
5732
  *
5704
5733
  * @param checkboxFilter Фильтр типа checkbox.
5705
5734
  */
5706
5735
  createCheckboxFormControl(checkboxFilter) {
5736
+ if (!checkboxFilter.isMultiple) {
5737
+ return new FormControl(null);
5738
+ }
5707
5739
  const controls = {};
5708
5740
  checkboxFilter.values.forEach((value) => {
5709
5741
  controls[value.id] = new FormControl(false, { nonNullable: true });
@@ -5735,7 +5767,15 @@ class ScCatalogFiltersComponent {
5735
5767
  * @param controlValue Значение контрола формы.
5736
5768
  */
5737
5769
  processCheckboxFilter(result, filterId, controlValue) {
5738
- // Для checkbox: FormGroup с FormControl<boolean> -> string[] (массив ID выбранных значений)
5770
+ // Для isMultiple === false: FormControl<string | null> -> string[] из одного выбранного ID.
5771
+ if (typeof controlValue === 'string') {
5772
+ if (controlValue) {
5773
+ // eslint-disable-next-line security/detect-object-injection, no-param-reassign
5774
+ result[filterId] = [controlValue];
5775
+ }
5776
+ return;
5777
+ }
5778
+ // Для checkbox: FormGroup с FormControl<boolean> -> string[] (массив ID выбранных значений).
5739
5779
  const selectedIds = Object.entries(controlValue)
5740
5780
  .filter(([, isSelected]) => isSelected)
5741
5781
  .map(([id]) => id);
@@ -5776,11 +5816,11 @@ class ScCatalogFiltersComponent {
5776
5816
  }
5777
5817
  }
5778
5818
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScCatalogFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5779
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScCatalogFiltersComponent, isStandalone: true, selector: "sc-catalog-filters", inputs: { isOpenAccordion: { classPropertyName: "isOpenAccordion", publicName: "isOpenAccordion", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (filters()?.length) {\n <div class=\"bg-tui-base-01 shadow-sc mb-4 flex flex-col items-center gap-3 rounded-xl\">\n <tui-accordion class=\"accordion\">\n <button\n [tuiAccordion]=\"isOpenAccordion()\"\n appearance=\"primary\"\n >\n \u0424\u0438\u043B\u044C\u0442\u0440\u044B\n </button>\n <tui-expand>\n <form\n [formGroup]=\"form\"\n class=\"flex flex-col gap-4 pb-1\"\n >\n @for (filter of filters(); track filter.id) {\n <div class=\"flex flex-col gap-2\">\n <label class=\"text-body-s font-medium\">{{ filter.label }}</label>\n\n @if (filter.type === 'checkbox') {\n <div\n [formGroupName]=\"filter.id\"\n class=\"flex flex-col gap-2\"\n >\n @for (value of filter.values; track value.id) {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"value.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">{{ value.label }}</span>\n </label>\n }\n </div>\n }\n\n @if (filter.type === 'range') {\n <tui-input-range\n [formControlName]=\"filter.id\"\n [min]=\"+filter.min\"\n [max]=\"+filter.max\"\n />\n }\n\n @if (filter.type === 'toggle') {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"filter.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">\u0414\u0430</span>\n </label>\n }\n </div>\n }\n </form>\n </tui-expand>\n </tui-accordion>\n </div>\n}\n", styles: [".accordion [tuiAccordion]{border:none;mask:none}.accordion tui-expand{box-shadow:none}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "component", type: i2$1.TuiInputRangeComponent, selector: "tui-input-range", inputs: ["min", "max", "step", "segments", "keySteps", "prefix", "postfix", "quantum", "content"] }, { kind: "component", type: i1$3.TuiAccordionComponent, selector: "tui-accordion", inputs: ["closeOthers", "size"] }, { kind: "directive", type: i1$3.TuiAccordionDirective, selector: "button[tuiAccordion]", inputs: ["tuiAccordion"], outputs: ["tuiAccordionChange"] }, { kind: "component", type: i2$5.TuiExpand, selector: "tui-expand", inputs: ["expanded"] }, { 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: "directive", type: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5819
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScCatalogFiltersComponent, isStandalone: true, selector: "sc-catalog-filters", inputs: { isOpenAccordion: { classPropertyName: "isOpenAccordion", publicName: "isOpenAccordion", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (filters()?.length) {\n <div class=\"bg-tui-base-01 shadow-sc flex flex-col items-center gap-3 rounded-xl\">\n <tui-accordion class=\"accordion\">\n <button\n [tuiAccordion]=\"isOpenAccordion()\"\n appearance=\"primary\"\n >\n \u0424\u0438\u043B\u044C\u0442\u0440\u044B\n </button>\n <tui-expand>\n <form\n [formGroup]=\"form\"\n class=\"flex flex-col gap-4 pb-1\"\n >\n @for (filter of filters(); track filter.id) {\n <div class=\"flex flex-col gap-2\">\n <label class=\"text-body-s font-medium\">{{ filter.label }}</label>\n\n @if (filter.type === 'checkbox') {\n @if (filter.isMultiple === false) {\n <div class=\"flex flex-col gap-2\">\n @for (value of filter.values; track value.id) {\n @let formControl = form.get(filter.id);\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"filter.id\"\n [value]=\"value.id\"\n tuiRadio\n (click)=\"formControl?.setValue(formControl?.value === value.id ? null : value.id)\"\n type=\"radio\"\n />\n <span class=\"text-body-s\">{{ value.label }}</span>\n </label>\n }\n </div>\n } @else {\n <div\n [formGroupName]=\"filter.id\"\n class=\"flex flex-col gap-2\"\n >\n @for (value of filter.values; track value.id) {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"value.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">{{ value.label }}</span>\n </label>\n }\n </div>\n }\n }\n\n @if (filter.type === 'range') {\n <tui-input-range\n [formControlName]=\"filter.id\"\n [min]=\"+filter.min\"\n [max]=\"+filter.max\"\n />\n }\n\n @if (filter.type === 'toggle') {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"filter.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">\u0414\u0430</span>\n </label>\n }\n </div>\n }\n </form>\n </tui-expand>\n </tui-accordion>\n </div>\n}\n", styles: [".accordion [tuiAccordion]{border:none;mask:none}.accordion tui-expand{box-shadow:none}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { 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: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "component", type: i2$1.TuiRadioComponent, selector: "input[type=\"radio\"][tuiRadio]", inputs: ["size"] }, { kind: "component", type: i2$1.TuiInputRangeComponent, selector: "tui-input-range", inputs: ["min", "max", "step", "segments", "keySteps", "prefix", "postfix", "quantum", "content"] }, { kind: "component", type: i1$3.TuiAccordionComponent, selector: "tui-accordion", inputs: ["closeOthers", "size"] }, { kind: "directive", type: i1$3.TuiAccordionDirective, selector: "button[tuiAccordion]", inputs: ["tuiAccordion"], outputs: ["tuiAccordionChange"] }, { kind: "component", type: i2$5.TuiExpand, selector: "tui-expand", inputs: ["expanded"] }, { 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: "directive", type: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5780
5820
  }
5781
5821
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScCatalogFiltersComponent, decorators: [{
5782
5822
  type: Component,
5783
- args: [{ standalone: true, selector: 'sc-catalog-filters', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, TuiTextfield, TuiCheckbox, TuiInputRange, TuiAccordion$1, ReactiveFormsModule], template: "@if (filters()?.length) {\n <div class=\"bg-tui-base-01 shadow-sc mb-4 flex flex-col items-center gap-3 rounded-xl\">\n <tui-accordion class=\"accordion\">\n <button\n [tuiAccordion]=\"isOpenAccordion()\"\n appearance=\"primary\"\n >\n \u0424\u0438\u043B\u044C\u0442\u0440\u044B\n </button>\n <tui-expand>\n <form\n [formGroup]=\"form\"\n class=\"flex flex-col gap-4 pb-1\"\n >\n @for (filter of filters(); track filter.id) {\n <div class=\"flex flex-col gap-2\">\n <label class=\"text-body-s font-medium\">{{ filter.label }}</label>\n\n @if (filter.type === 'checkbox') {\n <div\n [formGroupName]=\"filter.id\"\n class=\"flex flex-col gap-2\"\n >\n @for (value of filter.values; track value.id) {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"value.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">{{ value.label }}</span>\n </label>\n }\n </div>\n }\n\n @if (filter.type === 'range') {\n <tui-input-range\n [formControlName]=\"filter.id\"\n [min]=\"+filter.min\"\n [max]=\"+filter.max\"\n />\n }\n\n @if (filter.type === 'toggle') {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"filter.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">\u0414\u0430</span>\n </label>\n }\n </div>\n }\n </form>\n </tui-expand>\n </tui-accordion>\n </div>\n}\n", styles: [".accordion [tuiAccordion]{border:none;mask:none}.accordion tui-expand{box-shadow:none}\n"] }]
5823
+ args: [{ standalone: true, selector: 'sc-catalog-filters', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, TuiTextfield, TuiCheckbox, TuiRadio, TuiInputRange, TuiAccordion$1, ReactiveFormsModule], template: "@if (filters()?.length) {\n <div class=\"bg-tui-base-01 shadow-sc flex flex-col items-center gap-3 rounded-xl\">\n <tui-accordion class=\"accordion\">\n <button\n [tuiAccordion]=\"isOpenAccordion()\"\n appearance=\"primary\"\n >\n \u0424\u0438\u043B\u044C\u0442\u0440\u044B\n </button>\n <tui-expand>\n <form\n [formGroup]=\"form\"\n class=\"flex flex-col gap-4 pb-1\"\n >\n @for (filter of filters(); track filter.id) {\n <div class=\"flex flex-col gap-2\">\n <label class=\"text-body-s font-medium\">{{ filter.label }}</label>\n\n @if (filter.type === 'checkbox') {\n @if (filter.isMultiple === false) {\n <div class=\"flex flex-col gap-2\">\n @for (value of filter.values; track value.id) {\n @let formControl = form.get(filter.id);\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"filter.id\"\n [value]=\"value.id\"\n tuiRadio\n (click)=\"formControl?.setValue(formControl?.value === value.id ? null : value.id)\"\n type=\"radio\"\n />\n <span class=\"text-body-s\">{{ value.label }}</span>\n </label>\n }\n </div>\n } @else {\n <div\n [formGroupName]=\"filter.id\"\n class=\"flex flex-col gap-2\"\n >\n @for (value of filter.values; track value.id) {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"value.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">{{ value.label }}</span>\n </label>\n }\n </div>\n }\n }\n\n @if (filter.type === 'range') {\n <tui-input-range\n [formControlName]=\"filter.id\"\n [min]=\"+filter.min\"\n [max]=\"+filter.max\"\n />\n }\n\n @if (filter.type === 'toggle') {\n <label class=\"flex items-center gap-2\">\n <input\n [formControlName]=\"filter.id\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span class=\"text-body-s\">\u0414\u0430</span>\n </label>\n }\n </div>\n }\n </form>\n </tui-expand>\n </tui-accordion>\n </div>\n}\n", styles: [".accordion [tuiAccordion]{border:none;mask:none}.accordion tui-expand{box-shadow:none}\n"] }]
5784
5824
  }], ctorParameters: () => [] });
5785
5825
 
5786
5826
  /* eslint-disable @typescript-eslint/unbound-method */
@@ -5857,14 +5897,22 @@ class ScSandwichComponent {
5857
5897
  * Выбранный продукт (материал для раскроя).
5858
5898
  */
5859
5899
  this.product = new FormControl(null, { validators: Validators.required });
5900
+ /**
5901
+ * Сигнал текущего выбранного продукта — используется в шаблоне для диапазонов и кнопок.
5902
+ */
5903
+ this.productSignal = toSignal(tuiControlValue(this.product), { initialValue: null });
5860
5904
  /**
5861
5905
  * Субъект для ручного запуска расчёта.
5862
5906
  */
5863
5907
  this.calculateTrigger$ = new Subject();
5908
+ /**
5909
+ * Источник начального результата расчёта для режима редактирования.
5910
+ */
5911
+ this.seedCalculateResult$ = new BehaviorSubject(undefined);
5864
5912
  /**
5865
5913
  * {@link Observable} расчета оптимизации раскладки.
5866
5914
  */
5867
- this.calculate$ = merge(merge(tuiControlValue(this.product), tuiControlValue(this.items)).pipe(
5915
+ this.calculate$ = merge(this.seedCalculateResult$, merge(tuiControlValue(this.product).pipe(skip(1)), tuiControlValue(this.items).pipe(skip(1))).pipe(
5868
5916
  // eslint-disable-next-line unicorn/no-useless-undefined
5869
5917
  map(() => undefined)), this.calculateTrigger$.pipe(switchMap(() => {
5870
5918
  const productId = this.product.value?.id ?? null;
@@ -5893,6 +5941,14 @@ class ScSandwichComponent {
5893
5941
  * Валидатор для длины изделия.
5894
5942
  */
5895
5943
  this.validatorLength$ = tuiControlValue(this.product).pipe(map((product) => Validators.min(product?.properties?.length ?? 0)));
5944
+ /**
5945
+ * Текст ограничения по ширине («от … до …»), пока выбран товар.
5946
+ */
5947
+ this.widthRangeText = computed(() => this.formatRange(this.settings()?.minWidth ?? null, this.productSignal()?.properties?.width ?? null, 'мм'));
5948
+ /**
5949
+ * Текст ограничения по длине («от … до …»), пока выбран товар.
5950
+ */
5951
+ this.lengthRangeText = computed(() => this.formatRange(this.settings()?.minLength ?? null, this.productSignal()?.properties?.length ?? null, 'мм'));
5896
5952
  /**
5897
5953
  * Признак того, что выполняется запрос на отправку данных выполняется.
5898
5954
  */
@@ -6026,6 +6082,28 @@ class ScSandwichComponent {
6026
6082
  }))
6027
6083
  .subscribe();
6028
6084
  }
6085
+ /**
6086
+ * Форматирование диапазона «от … до …» с учётом отсутствующих границ.
6087
+ *
6088
+ * @param min Нижняя граница.
6089
+ * @param max Верхняя граница.
6090
+ * @param unit Единица измерения.
6091
+ */
6092
+ // eslint-disable-next-line class-methods-use-this
6093
+ formatRange(min, max, unit) {
6094
+ const hasMin = min != null && min > 0;
6095
+ const hasMax = max != null && max > 0;
6096
+ if (!hasMin && !hasMax) {
6097
+ return null;
6098
+ }
6099
+ if (hasMin && hasMax) {
6100
+ return `от ${min} ${unit} до ${max} ${unit}`;
6101
+ }
6102
+ if (hasMin) {
6103
+ return `от ${min} ${unit}`;
6104
+ }
6105
+ return `до ${max} ${unit}`;
6106
+ }
6029
6107
  /**
6030
6108
  * Устанавливает начальные значения для полей формы.
6031
6109
  */
@@ -6045,6 +6123,19 @@ class ScSandwichComponent {
6045
6123
  if (!configuratorParameters?.items.length) {
6046
6124
  this.addItem();
6047
6125
  }
6126
+ // Сбрасываем seed и при наличии сохранённого calculationId восстанавливаем результат расчёта —
6127
+ // чтобы в режиме редактирования сразу была видна кнопка «Изменить», а не «Рассчитать».
6128
+ // eslint-disable-next-line unicorn/no-useless-undefined
6129
+ this.seedCalculateResult$.next(undefined);
6130
+ const existingCalculationId = configuratorParameters?.calculationId;
6131
+ const existingQuantity = this.orderItem()?.quantity;
6132
+ if (existingCalculationId && existingQuantity != null && existingQuantity > 0) {
6133
+ this.seedCalculateResult$.next({
6134
+ id: existingCalculationId,
6135
+ quantity: existingQuantity,
6136
+ sheets: [],
6137
+ });
6138
+ }
6048
6139
  }
6049
6140
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSandwichComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6050
6141
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScSandwichComponent, isStandalone: true, selector: "sc-sandwich", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null }, categoryId: { classPropertyName: "categoryId", publicName: "categoryId", isSignal: true, isRequired: true, transformFunction: null }, editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, orderItem: { classPropertyName: "orderItem", publicName: "orderItem", isSignal: true, isRequired: true, transformFunction: null }, orderId: { classPropertyName: "orderId", publicName: "orderId", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { toggleShowEvent: "toggleShowEvent" }, providers: [
@@ -6056,7 +6147,7 @@ class ScSandwichComponent {
6056
6147
  stringify: signal((x) => x.name),
6057
6148
  identityMatcher: signal((a, b) => a.id === b.id),
6058
6149
  }),
6059
- ], ngImport: i0, template: "@if (!orderItem() && settings()?.allowShowTable) {\n <div class=\"mb-5 flex justify-center\">\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"toggleShowEvent.emit()\"\n type=\"button\"\n iconStart=\"@tui.layout-list\"\n >\n \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432 \u0432\u0438\u0434\u0435 \u0441\u043F\u0438\u0441\u043A\u0430\n </button>\n </div>\n}\n<div class=\"\">\n @let calculateResult = calculateResult$ | async;\n @let products = products$ | async;\n @let productValue = product.value;\n @let validatorWidth = validatorWidth$ | async;\n @let validatorLength = validatorLength$ | async;\n\n <form\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit(calculateResult)\"\n ScNextInputFocus\n class=\"flex flex-col gap-3\"\n >\n <label\n tuiLabel\n class=\"grow\"\n >\n \u0422\u043E\u0432\u0430\u0440\n <tui-textfield\n tuiChevron\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n tuiChevron\n tuiSelect\n [formControl]=\"product\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\"\n />\n\n <tui-data-list-wrapper\n *tuiTextfieldDropdown\n new\n [items]=\"products\"\n />\n </tui-textfield>\n @if (productValue) {\n <div class=\"ml-2 text-tui-text-secondary\">\n \u0421\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C:\n <span class=\"font-bold\">{{ productValue.costRub?.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.</span>\n </div>\n }\n <tui-error\n [formControl]=\"product\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (product.value && validatorWidth && validatorLength) {\n <p class=\"w-full font-bold\">\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439</p>\n <p class=\"w-full\">\n \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0448\u0438\u0440\u0438\u043D\u0430: {{ settings()?.minWidth }} \u043C\u043C.\n <br />\n \u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0448\u0438\u0440\u0438\u043D\u0430: {{ product.value.properties?.width }} \u043C\u043C.\n <br />\n \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430: {{ settings()?.minLength }} \u043C\u043C.\n <br />\n \u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430: {{ product.value.properties?.length }} \u043C\u043C.\n <br />\n </p>\n <div\n formArrayName=\"items\"\n class=\"flex flex-col gap-3\"\n >\n @for (item of items.controls; track item) {\n <div\n class=\"flex grow gap-2\"\n [formGroupName]=\"$index\"\n >\n <div class=\"grid grow grid-cols-3 gap-2\">\n <label tuiLabel>\n \u0428\u0438\u0440\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"width\"\n [min]=\"settings()?.minWidth ?? 0\"\n [max]=\"product.value.properties?.width ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n placeholder=\"\u0428\u0438\u0440\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.width }}</span>\n </div>\n }\n <tui-error\n formControlName=\"width\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u0414\u043B\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"length\"\n [min]=\"settings()?.minLength ?? 0\"\n [max]=\"product.value.properties?.length ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n placeholder=\"\u0414\u043B\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.length }}</span>\n </div>\n }\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n <label tuiLabel>\n <span class=\"whitespace-nowrap\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442</span>\n <tui-textfield [tuiTextfieldCleaner]=\"false\">\n <input\n tuiInputNumber\n formControlName=\"count\"\n [min]=\"1\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"count\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n <button\n tuiIconButton\n (click)=\"removeItem($index)\"\n [disabled]=\"items.length <= 1\"\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.trash-2\"\n appearance=\"secondary\"\n class=\"mt-6\"\n [style.flex]=\"'0 0 auto'\"\n ></button>\n </div>\n }\n </div>\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid\"\n (click)=\"addItem()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.plus\"\n >\n \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\n </button>\n } @else if (product.value) {\n <tui-error error=\"\u0420\u0430\u0441\u0447\u0435\u0442 \u043F\u043E \u043F\u0440\u043E\u0434\u0443\u043A\u0442\u0443 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D. \u041E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u043A \u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\u0443\" />\n }\n\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"marker\"\n placeholder=\"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\"\n autocomplete=\"marker\"\n />\n </tui-textfield>\n\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (product.value) {\n <tui-loader [showLoader]=\"isCalculateLoading()\">\n @if (calculateResult) {\n <div class=\"mt-2 flex flex-wrap items-end gap-2\">\n <div>\n \u0418\u0442\u043E\u0433\u043E: <span class=\"whitespace-nowrap text-xl font-bold\">{{ getTotalCost(calculateResult).toLocaleString() }} {{ product.value.currency.symbol }}</span>\n </div>\n @if (calculateResult?.quantity && productValue) {\n <div class=\"whitespace-nowrap text-lg text-sc-dark-grey\">\n <span class=\"font-bold\">({{ calculateResult.quantity }} \u0448\u0442.</span> x\n <span class=\"font-bold\">{{ productValue.costRub?.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.)</span>\n </div>\n }\n </div>\n }\n </tui-loader>\n }\n\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n @if (!calculateResult) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid || isCalculateLoading()\"\n (click)=\"triggerCalculate()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.calculator\"\n >\n \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044C\n </button>\n } @else {\n <button\n [disabled]=\"form.invalid || isSubmitLoading()\"\n [loading]=\"isSubmitLoading()\"\n tuiButton\n class=\"self-center\"\n iconStart=\"@tui.check\"\n >\n {{ !orderItem() ? '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' : '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' }}\n </button>\n }\n </form>\n</div>\n", styles: [":host{display:block;width:100%;max-width:30rem}.slider-ticks-labels{--t-offset: calc($thumb / 2);display:flex;font:var(--tui-font-text-s);margin-inline-start:var(--t-offset);margin-inline-end:var(--t-offset);color:var(--tui-text-secondary)}.slider-ticks-labels>*{position:relative;flex:2;text-align:center}.slider-ticks-labels>*:first-child{left:calc(-1 * var(--t-offset));inset-inline-start:calc(-1 * var(--t-offset));flex:1;text-align:start}.slider-ticks-labels>*:last-child{right:calc(-1 * var(--t-offset));flex:1;text-align:end}@supports (inset-inline-end: 0){.slider-ticks-labels>*:last-child{right:unset;inset-inline-end:calc(-1 * var(--t-offset))}}tui-input-slider+.slider-ticks-labels{margin-inline-start:calc(var(--tui-radius-m) / 2 + var(--t-offset))}tui-textfield+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-m) / 2 + $thumb / 2)}tui-textfield[data-size=l]+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-l) / 2 + $thumb / 2)}tui-input-range:not([new])+.slider-ticks-labels,tui-range+.slider-ticks-labels{--t-offset: $thumb}tui-input-range[new]+.slider-ticks-labels{--t-offset: calc(map-get($track-inset, $input-size) + $thumb)}\n"], dependencies: [{ kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: ScNextInputFocusModule }, { kind: "directive", type: ScNextInputFocusDirective, selector: "form[ScNextInputFocus]" }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "component", type: TuiDataListWrapperComponent, selector: "tui-data-list-wrapper:not([labels]), tui-data-list-wrapper:not([labels])[new]", inputs: ["items", "disabledItemHandler", "emptyContent", "size", "itemContent"], outputs: ["itemClick"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "directive", type: i1$2.TuiTextfieldDirective, selector: "input[tuiTextfield]:not([tuiInputCard]):not([tuiInputExpire]):not([tuiInputCVC])" }, { kind: "directive", type: i1$2.TuiTextfieldOptionsDirective, selector: "[tuiTextfieldAppearance],[tuiTextfieldSize],[tuiTextfieldCleaner]", inputs: ["tuiTextfieldAppearance", "tuiTextfieldSize", "tuiTextfieldCleaner"] }, { kind: "directive", type: i1$2.TuiTextfieldDropdownDirective, selector: "ng-template[tuiTextfieldDropdown]" }, { kind: "directive", type: TuiChevron, selector: "[tuiChevron]", inputs: ["tuiChevron"] }, { kind: "directive", type: i2$1.TuiSelectDirective, selector: "input[tuiSelect]" }, { kind: "directive", type: i2$1.TuiInputNumberDirective, selector: "input[tuiInputNumber]", inputs: ["min", "max", "prefix", "postfix"] }, { kind: "directive", type: TuiNumberFormat, selector: "[tuiNumberFormat]", inputs: ["tuiNumberFormat"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "component", type: i5$2.TuiSliderComponent, selector: "input[type=range][tuiSlider]", inputs: ["size", "segments"] }, { kind: "directive", type: i2$1.TuiInputSliderDirective, selector: "input[tuiInputSlider]" }, { kind: "directive", type: ScSelectOnFocusinDirective, selector: "tui-input-number, tui-input, tui-input-phone, tui-input-date, tui-input-password, input[tuiInputNumber], input[tuiTextfield], input[tuiInputSlider]" }, { kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6150
+ ], ngImport: i0, template: "@if (!orderItem() && settings()?.allowShowTable) {\n <div class=\"mb-5 flex justify-center\">\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"toggleShowEvent.emit()\"\n type=\"button\"\n iconStart=\"@tui.layout-list\"\n >\n \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432 \u0432\u0438\u0434\u0435 \u0441\u043F\u0438\u0441\u043A\u0430\n </button>\n </div>\n}\n<div>\n @let calculateResult = calculateResult$ | async;\n @let products = products$ | async;\n @let productValue = product.value;\n @let validatorWidth = validatorWidth$ | async;\n @let validatorLength = validatorLength$ | async;\n\n <form\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit(calculateResult)\"\n ScNextInputFocus\n class=\"flex flex-col gap-3\"\n >\n <label\n tuiLabel\n class=\"grow\"\n >\n \u0422\u043E\u0432\u0430\u0440\n <tui-textfield\n tuiChevron\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n tuiChevron\n tuiSelect\n [formControl]=\"product\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\"\n />\n\n <tui-data-list-wrapper\n *tuiTextfieldDropdown\n new\n [items]=\"products\"\n />\n </tui-textfield>\n <tui-error\n [formControl]=\"product\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (productValue && productValue.costRub) {\n <div class=\"-mt-1 text-sm text-tui-text-secondary\">\n <span class=\"font-bold\">{{ productValue.code }}</span>\n \u2014 {{ productValue.costRub.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.\n </div>\n }\n\n @if (product.value && validatorWidth && validatorLength) {\n <div class=\"mt-4 flex flex-col gap-1.5\">\n <div class=\"w-full font-bold\">\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439</div>\n <div class=\"flex flex-col gap-1 text-sm text-tui-text-secondary\">\n @if (widthRangeText()) {\n <div>\u0428\u0438\u0440\u0438\u043D\u0430 {{ widthRangeText() }}.</div>\n }\n @if (lengthRangeText()) {\n <div>\u0414\u043B\u0438\u043D\u0430 {{ lengthRangeText() }}.</div>\n }\n </div>\n </div>\n } @else if (product.value) {\n <tui-error error=\"\u0420\u0430\u0441\u0447\u0435\u0442 \u043F\u043E \u043F\u0440\u043E\u0434\u0443\u043A\u0442\u0443 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D. \u041E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u043A \u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\u0443\" />\n }\n\n <div\n formArrayName=\"items\"\n class=\"flex flex-col gap-3\"\n >\n @for (item of items.controls; track item) {\n <div\n class=\"flex grow gap-2\"\n [formGroupName]=\"$index\"\n >\n <div class=\"grid grow grid-cols-1 items-start gap-2 sm:grid-cols-3\">\n <label tuiLabel>\n \u0428\u0438\u0440\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"width\"\n [min]=\"settings()?.minWidth ?? 0\"\n [max]=\"product.value?.properties?.width ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value\"\n placeholder=\"\u0428\u0438\u0440\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.width }}</span>\n </div>\n }\n <tui-error\n formControlName=\"width\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u0414\u043B\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"length\"\n [min]=\"settings()?.minLength ?? 0\"\n [max]=\"product.value?.properties?.length ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value\"\n placeholder=\"\u0414\u043B\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.length }}</span>\n </div>\n }\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n <label tuiLabel>\n <span class=\"whitespace-nowrap\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442</span>\n <tui-textfield [tuiTextfieldCleaner]=\"false\">\n <input\n tuiInputNumber\n formControlName=\"count\"\n [min]=\"1\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value\"\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"count\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n <button\n tuiIconButton\n (click)=\"removeItem($index)\"\n [disabled]=\"!product.value || items.length <= 1\"\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.trash-2\"\n appearance=\"secondary\"\n class=\"mt-6\"\n [style.flex]=\"'0 0 auto'\"\n ></button>\n </div>\n }\n </div>\n @if (product.value) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid\"\n (click)=\"addItem()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.plus\"\n >\n \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\n </button>\n }\n\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"marker\"\n placeholder=\"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\"\n autocomplete=\"marker\"\n />\n </tui-textfield>\n\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (product.value) {\n <tui-loader [showLoader]=\"isCalculateLoading()\">\n @if (calculateResult) {\n <div class=\"mt-2 flex flex-wrap items-end gap-2\">\n <div>\n \u0418\u0442\u043E\u0433\u043E: <span class=\"whitespace-nowrap text-xl font-bold\">{{ getTotalCost(calculateResult).toLocaleString() }} {{ product.value.currency.symbol }}</span>\n </div>\n @if (calculateResult?.quantity && productValue) {\n <div class=\"whitespace-nowrap text-lg text-sc-dark-grey\">\n <span class=\"font-bold\">({{ calculateResult.quantity }} \u0448\u0442.</span> x\n <span class=\"font-bold\">{{ productValue.costRub?.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.)</span>\n </div>\n }\n </div>\n }\n </tui-loader>\n }\n\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n @if (!calculateResult) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid || isCalculateLoading()\"\n (click)=\"triggerCalculate()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.calculator\"\n >\n \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044C\n </button>\n } @else {\n <button\n [disabled]=\"form.invalid || isSubmitLoading()\"\n [loading]=\"isSubmitLoading()\"\n tuiButton\n class=\"self-center\"\n iconStart=\"@tui.check\"\n >\n {{ !orderItem() ? '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' : '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' }}\n </button>\n }\n </form>\n</div>\n", styles: [":host{display:block;width:100%;max-width:30rem}.slider-ticks-labels{--t-offset: calc($thumb / 2);display:flex;font:var(--tui-font-text-s);margin-inline-start:var(--t-offset);margin-inline-end:var(--t-offset);color:var(--tui-text-secondary)}.slider-ticks-labels>*{position:relative;flex:2;text-align:center}.slider-ticks-labels>*:first-child{left:calc(-1 * var(--t-offset));inset-inline-start:calc(-1 * var(--t-offset));flex:1;text-align:start}.slider-ticks-labels>*:last-child{right:calc(-1 * var(--t-offset));flex:1;text-align:end}@supports (inset-inline-end: 0){.slider-ticks-labels>*:last-child{right:unset;inset-inline-end:calc(-1 * var(--t-offset))}}tui-input-slider+.slider-ticks-labels{margin-inline-start:calc(var(--tui-radius-m) / 2 + var(--t-offset))}tui-textfield+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-m) / 2 + $thumb / 2)}tui-textfield[data-size=l]+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-l) / 2 + $thumb / 2)}tui-input-range:not([new])+.slider-ticks-labels,tui-range+.slider-ticks-labels{--t-offset: $thumb}tui-input-range[new]+.slider-ticks-labels{--t-offset: calc(map-get($track-inset, $input-size) + $thumb)}\n"], dependencies: [{ kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: ScNextInputFocusModule }, { kind: "directive", type: ScNextInputFocusDirective, selector: "form[ScNextInputFocus]" }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "component", type: TuiDataListWrapperComponent, selector: "tui-data-list-wrapper:not([labels]), tui-data-list-wrapper:not([labels])[new]", inputs: ["items", "disabledItemHandler", "emptyContent", "size", "itemContent"], outputs: ["itemClick"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "directive", type: i1$2.TuiTextfieldDirective, selector: "input[tuiTextfield]:not([tuiInputCard]):not([tuiInputExpire]):not([tuiInputCVC])" }, { kind: "directive", type: i1$2.TuiTextfieldOptionsDirective, selector: "[tuiTextfieldAppearance],[tuiTextfieldSize],[tuiTextfieldCleaner]", inputs: ["tuiTextfieldAppearance", "tuiTextfieldSize", "tuiTextfieldCleaner"] }, { kind: "directive", type: i1$2.TuiTextfieldDropdownDirective, selector: "ng-template[tuiTextfieldDropdown]" }, { kind: "directive", type: TuiChevron, selector: "[tuiChevron]", inputs: ["tuiChevron"] }, { kind: "directive", type: i2$1.TuiSelectDirective, selector: "input[tuiSelect]" }, { kind: "directive", type: i2$1.TuiInputNumberDirective, selector: "input[tuiInputNumber]", inputs: ["min", "max", "prefix", "postfix"] }, { kind: "directive", type: TuiNumberFormat, selector: "[tuiNumberFormat]", inputs: ["tuiNumberFormat"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "component", type: i5$2.TuiSliderComponent, selector: "input[type=range][tuiSlider]", inputs: ["size", "segments"] }, { kind: "directive", type: i2$1.TuiInputSliderDirective, selector: "input[tuiInputSlider]" }, { kind: "directive", type: ScSelectOnFocusinDirective, selector: "tui-input-number, tui-input, tui-input-phone, tui-input-date, tui-input-password, input[tuiInputNumber], input[tuiTextfield], input[tuiInputSlider]" }, { kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6060
6151
  }
6061
6152
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSandwichComponent, decorators: [{
6062
6153
  type: Component,
@@ -6088,7 +6179,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
6088
6179
  stringify: signal((x) => x.name),
6089
6180
  identityMatcher: signal((a, b) => a.id === b.id),
6090
6181
  }),
6091
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!orderItem() && settings()?.allowShowTable) {\n <div class=\"mb-5 flex justify-center\">\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"toggleShowEvent.emit()\"\n type=\"button\"\n iconStart=\"@tui.layout-list\"\n >\n \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432 \u0432\u0438\u0434\u0435 \u0441\u043F\u0438\u0441\u043A\u0430\n </button>\n </div>\n}\n<div class=\"\">\n @let calculateResult = calculateResult$ | async;\n @let products = products$ | async;\n @let productValue = product.value;\n @let validatorWidth = validatorWidth$ | async;\n @let validatorLength = validatorLength$ | async;\n\n <form\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit(calculateResult)\"\n ScNextInputFocus\n class=\"flex flex-col gap-3\"\n >\n <label\n tuiLabel\n class=\"grow\"\n >\n \u0422\u043E\u0432\u0430\u0440\n <tui-textfield\n tuiChevron\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n tuiChevron\n tuiSelect\n [formControl]=\"product\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\"\n />\n\n <tui-data-list-wrapper\n *tuiTextfieldDropdown\n new\n [items]=\"products\"\n />\n </tui-textfield>\n @if (productValue) {\n <div class=\"ml-2 text-tui-text-secondary\">\n \u0421\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C:\n <span class=\"font-bold\">{{ productValue.costRub?.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.</span>\n </div>\n }\n <tui-error\n [formControl]=\"product\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (product.value && validatorWidth && validatorLength) {\n <p class=\"w-full font-bold\">\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439</p>\n <p class=\"w-full\">\n \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0448\u0438\u0440\u0438\u043D\u0430: {{ settings()?.minWidth }} \u043C\u043C.\n <br />\n \u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0448\u0438\u0440\u0438\u043D\u0430: {{ product.value.properties?.width }} \u043C\u043C.\n <br />\n \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430: {{ settings()?.minLength }} \u043C\u043C.\n <br />\n \u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430: {{ product.value.properties?.length }} \u043C\u043C.\n <br />\n </p>\n <div\n formArrayName=\"items\"\n class=\"flex flex-col gap-3\"\n >\n @for (item of items.controls; track item) {\n <div\n class=\"flex grow gap-2\"\n [formGroupName]=\"$index\"\n >\n <div class=\"grid grow grid-cols-3 gap-2\">\n <label tuiLabel>\n \u0428\u0438\u0440\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"width\"\n [min]=\"settings()?.minWidth ?? 0\"\n [max]=\"product.value.properties?.width ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n placeholder=\"\u0428\u0438\u0440\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.width }}</span>\n </div>\n }\n <tui-error\n formControlName=\"width\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u0414\u043B\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"length\"\n [min]=\"settings()?.minLength ?? 0\"\n [max]=\"product.value.properties?.length ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n placeholder=\"\u0414\u043B\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.length }}</span>\n </div>\n }\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n <label tuiLabel>\n <span class=\"whitespace-nowrap\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442</span>\n <tui-textfield [tuiTextfieldCleaner]=\"false\">\n <input\n tuiInputNumber\n formControlName=\"count\"\n [min]=\"1\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"count\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n <button\n tuiIconButton\n (click)=\"removeItem($index)\"\n [disabled]=\"items.length <= 1\"\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.trash-2\"\n appearance=\"secondary\"\n class=\"mt-6\"\n [style.flex]=\"'0 0 auto'\"\n ></button>\n </div>\n }\n </div>\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid\"\n (click)=\"addItem()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.plus\"\n >\n \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\n </button>\n } @else if (product.value) {\n <tui-error error=\"\u0420\u0430\u0441\u0447\u0435\u0442 \u043F\u043E \u043F\u0440\u043E\u0434\u0443\u043A\u0442\u0443 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D. \u041E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u043A \u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\u0443\" />\n }\n\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"marker\"\n placeholder=\"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\"\n autocomplete=\"marker\"\n />\n </tui-textfield>\n\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (product.value) {\n <tui-loader [showLoader]=\"isCalculateLoading()\">\n @if (calculateResult) {\n <div class=\"mt-2 flex flex-wrap items-end gap-2\">\n <div>\n \u0418\u0442\u043E\u0433\u043E: <span class=\"whitespace-nowrap text-xl font-bold\">{{ getTotalCost(calculateResult).toLocaleString() }} {{ product.value.currency.symbol }}</span>\n </div>\n @if (calculateResult?.quantity && productValue) {\n <div class=\"whitespace-nowrap text-lg text-sc-dark-grey\">\n <span class=\"font-bold\">({{ calculateResult.quantity }} \u0448\u0442.</span> x\n <span class=\"font-bold\">{{ productValue.costRub?.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.)</span>\n </div>\n }\n </div>\n }\n </tui-loader>\n }\n\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n @if (!calculateResult) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid || isCalculateLoading()\"\n (click)=\"triggerCalculate()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.calculator\"\n >\n \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044C\n </button>\n } @else {\n <button\n [disabled]=\"form.invalid || isSubmitLoading()\"\n [loading]=\"isSubmitLoading()\"\n tuiButton\n class=\"self-center\"\n iconStart=\"@tui.check\"\n >\n {{ !orderItem() ? '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' : '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' }}\n </button>\n }\n </form>\n</div>\n", styles: [":host{display:block;width:100%;max-width:30rem}.slider-ticks-labels{--t-offset: calc($thumb / 2);display:flex;font:var(--tui-font-text-s);margin-inline-start:var(--t-offset);margin-inline-end:var(--t-offset);color:var(--tui-text-secondary)}.slider-ticks-labels>*{position:relative;flex:2;text-align:center}.slider-ticks-labels>*:first-child{left:calc(-1 * var(--t-offset));inset-inline-start:calc(-1 * var(--t-offset));flex:1;text-align:start}.slider-ticks-labels>*:last-child{right:calc(-1 * var(--t-offset));flex:1;text-align:end}@supports (inset-inline-end: 0){.slider-ticks-labels>*:last-child{right:unset;inset-inline-end:calc(-1 * var(--t-offset))}}tui-input-slider+.slider-ticks-labels{margin-inline-start:calc(var(--tui-radius-m) / 2 + var(--t-offset))}tui-textfield+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-m) / 2 + $thumb / 2)}tui-textfield[data-size=l]+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-l) / 2 + $thumb / 2)}tui-input-range:not([new])+.slider-ticks-labels,tui-range+.slider-ticks-labels{--t-offset: $thumb}tui-input-range[new]+.slider-ticks-labels{--t-offset: calc(map-get($track-inset, $input-size) + $thumb)}\n"] }]
6182
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!orderItem() && settings()?.allowShowTable) {\n <div class=\"mb-5 flex justify-center\">\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"toggleShowEvent.emit()\"\n type=\"button\"\n iconStart=\"@tui.layout-list\"\n >\n \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432 \u0432\u0438\u0434\u0435 \u0441\u043F\u0438\u0441\u043A\u0430\n </button>\n </div>\n}\n<div>\n @let calculateResult = calculateResult$ | async;\n @let products = products$ | async;\n @let productValue = product.value;\n @let validatorWidth = validatorWidth$ | async;\n @let validatorLength = validatorLength$ | async;\n\n <form\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit(calculateResult)\"\n ScNextInputFocus\n class=\"flex flex-col gap-3\"\n >\n <label\n tuiLabel\n class=\"grow\"\n >\n \u0422\u043E\u0432\u0430\u0440\n <tui-textfield\n tuiChevron\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n tuiChevron\n tuiSelect\n [formControl]=\"product\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\"\n />\n\n <tui-data-list-wrapper\n *tuiTextfieldDropdown\n new\n [items]=\"products\"\n />\n </tui-textfield>\n <tui-error\n [formControl]=\"product\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (productValue && productValue.costRub) {\n <div class=\"-mt-1 text-sm text-tui-text-secondary\">\n <span class=\"font-bold\">{{ productValue.code }}</span>\n \u2014 {{ productValue.costRub.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.\n </div>\n }\n\n @if (product.value && validatorWidth && validatorLength) {\n <div class=\"mt-4 flex flex-col gap-1.5\">\n <div class=\"w-full font-bold\">\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439</div>\n <div class=\"flex flex-col gap-1 text-sm text-tui-text-secondary\">\n @if (widthRangeText()) {\n <div>\u0428\u0438\u0440\u0438\u043D\u0430 {{ widthRangeText() }}.</div>\n }\n @if (lengthRangeText()) {\n <div>\u0414\u043B\u0438\u043D\u0430 {{ lengthRangeText() }}.</div>\n }\n </div>\n </div>\n } @else if (product.value) {\n <tui-error error=\"\u0420\u0430\u0441\u0447\u0435\u0442 \u043F\u043E \u043F\u0440\u043E\u0434\u0443\u043A\u0442\u0443 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D. \u041E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u043A \u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\u0443\" />\n }\n\n <div\n formArrayName=\"items\"\n class=\"flex flex-col gap-3\"\n >\n @for (item of items.controls; track item) {\n <div\n class=\"flex grow gap-2\"\n [formGroupName]=\"$index\"\n >\n <div class=\"grid grow grid-cols-1 items-start gap-2 sm:grid-cols-3\">\n <label tuiLabel>\n \u0428\u0438\u0440\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"width\"\n [min]=\"settings()?.minWidth ?? 0\"\n [max]=\"product.value?.properties?.width ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value\"\n placeholder=\"\u0428\u0438\u0440\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.width }}</span>\n </div>\n }\n <tui-error\n formControlName=\"width\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u0414\u043B\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"length\"\n [min]=\"settings()?.minLength ?? 0\"\n [max]=\"product.value?.properties?.length ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value\"\n placeholder=\"\u0414\u043B\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n @if (product.value) {\n <div class=\"slider-ticks-labels\">\n <span> 0 </span>\n <span>{{ product.value.properties?.length }}</span>\n </div>\n }\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n <label tuiLabel>\n <span class=\"whitespace-nowrap\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442</span>\n <tui-textfield [tuiTextfieldCleaner]=\"false\">\n <input\n tuiInputNumber\n formControlName=\"count\"\n [min]=\"1\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value\"\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"count\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n <button\n tuiIconButton\n (click)=\"removeItem($index)\"\n [disabled]=\"!product.value || items.length <= 1\"\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.trash-2\"\n appearance=\"secondary\"\n class=\"mt-6\"\n [style.flex]=\"'0 0 auto'\"\n ></button>\n </div>\n }\n </div>\n @if (product.value) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid\"\n (click)=\"addItem()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.plus\"\n >\n \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\n </button>\n }\n\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"marker\"\n placeholder=\"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 \u0440\u0430\u0441\u043F\u0438\u043B\u0430\"\n autocomplete=\"marker\"\n />\n </tui-textfield>\n\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (product.value) {\n <tui-loader [showLoader]=\"isCalculateLoading()\">\n @if (calculateResult) {\n <div class=\"mt-2 flex flex-wrap items-end gap-2\">\n <div>\n \u0418\u0442\u043E\u0433\u043E: <span class=\"whitespace-nowrap text-xl font-bold\">{{ getTotalCost(calculateResult).toLocaleString() }} {{ product.value.currency.symbol }}</span>\n </div>\n @if (calculateResult?.quantity && productValue) {\n <div class=\"whitespace-nowrap text-lg text-sc-dark-grey\">\n <span class=\"font-bold\">({{ calculateResult.quantity }} \u0448\u0442.</span> x\n <span class=\"font-bold\">{{ productValue.costRub?.toLocaleString() }} {{ productValue.currency.symbol }}/\u0448\u0442.)</span>\n </div>\n }\n </div>\n }\n </tui-loader>\n }\n\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n @if (!calculateResult) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"items.invalid || isCalculateLoading()\"\n (click)=\"triggerCalculate()\"\n type=\"button\"\n class=\"self-center\"\n iconStart=\"@tui.calculator\"\n >\n \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044C\n </button>\n } @else {\n <button\n [disabled]=\"form.invalid || isSubmitLoading()\"\n [loading]=\"isSubmitLoading()\"\n tuiButton\n class=\"self-center\"\n iconStart=\"@tui.check\"\n >\n {{ !orderItem() ? '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' : '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' }}\n </button>\n }\n </form>\n</div>\n", styles: [":host{display:block;width:100%;max-width:30rem}.slider-ticks-labels{--t-offset: calc($thumb / 2);display:flex;font:var(--tui-font-text-s);margin-inline-start:var(--t-offset);margin-inline-end:var(--t-offset);color:var(--tui-text-secondary)}.slider-ticks-labels>*{position:relative;flex:2;text-align:center}.slider-ticks-labels>*:first-child{left:calc(-1 * var(--t-offset));inset-inline-start:calc(-1 * var(--t-offset));flex:1;text-align:start}.slider-ticks-labels>*:last-child{right:calc(-1 * var(--t-offset));flex:1;text-align:end}@supports (inset-inline-end: 0){.slider-ticks-labels>*:last-child{right:unset;inset-inline-end:calc(-1 * var(--t-offset))}}tui-input-slider+.slider-ticks-labels{margin-inline-start:calc(var(--tui-radius-m) / 2 + var(--t-offset))}tui-textfield+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-m) / 2 + $thumb / 2)}tui-textfield[data-size=l]+.slider-ticks-labels{--t-offset: calc(var(--tui-radius-l) / 2 + $thumb / 2)}tui-input-range:not([new])+.slider-ticks-labels,tui-range+.slider-ticks-labels{--t-offset: $thumb}tui-input-range[new]+.slider-ticks-labels{--t-offset: calc(map-get($track-inset, $input-size) + $thumb)}\n"] }]
6092
6183
  }], propDecorators: { orderId: [{
6093
6184
  type: Input
6094
6185
  }] } });
@@ -6105,6 +6196,563 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
6105
6196
  args: [{ standalone: true, selector: 'sc-sandwich-skeleton', template: "<div class=\"w-100\">\n <div class=\"flex flex-col gap-3\">\n <!-- \u041F\u043E\u043B\u0435 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \"\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439\". -->\n <div class=\"my-1.5 h-5 w-1/3 rounded bg-sc-light-grey\"></div>\n\n <!-- \u0422\u0435\u043A\u0441\u0442 \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438. -->\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n </div>\n\n <!-- \u0411\u043B\u043E\u043A \u0441 \u0438\u0437\u0434\u0435\u043B\u0438\u044F\u043C\u0438. -->\n <div class=\"flex flex-col gap-3\">\n <!-- \u041F\u0435\u0440\u0432\u043E\u0435 \u0438\u0437\u0434\u0435\u043B\u0438\u0435. -->\n <div class=\"flex grow gap-4\">\n <div class=\"grid grow grid-cols-3 gap-4\">\n <!-- \u0428\u0438\u0440\u0438\u043D\u0430. -->\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-1/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-2 w-2/3 rounded bg-sc-light-grey\"></div>\n </div>\n <!-- \u0414\u043B\u0438\u043D\u0430. -->\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-1/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-2 w-2/3 rounded bg-sc-light-grey\"></div>\n </div>\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E. -->\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n </div>\n <div class=\"mt-6 size-8 shrink-0 rounded-sm bg-sc-light-grey\"></div>\n </div>\n </div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \"\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\". -->\n <button\n tuiButton\n appearance=\"secondary\"\n class=\"sc-skeleton self-center\"\n ></button>\n\n <!-- \u041F\u043E\u043B\u0435 \"\u0418\u0442\u043E\u0433\u043E\u0432\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043B\u0438\u0441\u0442\u043E\u0432\". -->\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n\n <!-- \u041F\u043E\u043B\u0435 \"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\". -->\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C. -->\n <div class=\"my-2.5 h-6 w-1/3 rounded bg-sc-light-grey\"></div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438. -->\n <button\n tuiButton\n class=\"sc-skeleton self-center\"\n ></button>\n </div>\n</div>\n" }]
6106
6197
  }] });
6107
6198
 
6199
+ /**
6200
+ * Начальное состояние позиции — используется при инициализации и сбросе формы.
6201
+ */
6202
+ const IDLE_ITEM_STATE = 'idle';
6203
+
6204
+ /* eslint-disable security/detect-object-injection,@typescript-eslint/unbound-method */
6205
+ /**
6206
+ * Конфигуратор распила сэндвич-панелей по площади.
6207
+ */
6208
+ class ScSandwichM2Component {
6209
+ constructor() {
6210
+ /**
6211
+ * Настройки для конфигуратора распила сэндвич-панелей по площади.
6212
+ */
6213
+ this.settings = input();
6214
+ /**
6215
+ * Идентификатор категории.
6216
+ */
6217
+ this.categoryId = input.required();
6218
+ /**
6219
+ * Название конфигуратора.
6220
+ */
6221
+ this.editor = input.required();
6222
+ /**
6223
+ * Позиция товара/услуги в корзине.
6224
+ */
6225
+ this.orderItem = input.required();
6226
+ /**
6227
+ * Событие переключения отображения товаров.
6228
+ */
6229
+ this.toggleShowEvent = output();
6230
+ /**
6231
+ * Форма добавления/редактирования продукта в корзине.
6232
+ */
6233
+ this.form = new FormGroup({
6234
+ productCategoryId: new FormControl(null, { validators: Validators.required }),
6235
+ configurator: new FormControl(null, { validators: Validators.required }),
6236
+ items: new FormArray([]),
6237
+ });
6238
+ /**
6239
+ * Сервис получения и редактирования списка товаров заказа (корзина / заказ / черновик = заказ).
6240
+ */
6241
+ this.orderService = inject(SC_ORDER_LOADER);
6242
+ /**
6243
+ * Сервис конвертации данных.
6244
+ */
6245
+ this.convertersService = inject(ScConvertersService);
6246
+ /**
6247
+ * Контекст диалогового окна, в котором открыт компонент.
6248
+ */
6249
+ this.context = inject(POLYMORPHEUS_CONTEXT, { optional: true });
6250
+ /**
6251
+ * Сервис для работы с каталогом.
6252
+ */
6253
+ this.catalogService = inject(ScCatalogService);
6254
+ /**
6255
+ * {@link Observable} списка товаров категории.
6256
+ */
6257
+ this.products$ = toObservable(this.categoryId).pipe(switchMap((categoryId) => {
6258
+ if (!categoryId) {
6259
+ return of([]);
6260
+ }
6261
+ return this.catalogService.getProductsCategoryCached$({ categoryId: Number(categoryId) }).pipe(map((products) => products ?? []));
6262
+ }));
6263
+ /**
6264
+ * Выбранный продукт.
6265
+ */
6266
+ this.product = new FormControl(null, { validators: Validators.required });
6267
+ /**
6268
+ * Сигнал текущего выбранного продукта — обновляется при `valueChanges` контрола `product`.
6269
+ */
6270
+ this.productSignal = toSignal(tuiControlValue(this.product), { initialValue: null });
6271
+ /**
6272
+ * Сигнал содержимого FormArray позиций — пересчитывается при любом изменении значений.
6273
+ */
6274
+ this.itemsValue = toSignal(tuiControlValue(this.items).pipe(map(() => this.items.controls.map((c) => c.getRawValue()))), {
6275
+ initialValue: [],
6276
+ });
6277
+ /**
6278
+ * Признак того, что выполняется запрос на отправку данных.
6279
+ */
6280
+ this.isSubmitLoading = computed(() => this.itemStates().includes('loading'));
6281
+ /**
6282
+ * Состояние отправки каждой позиции — параллельный массив к контролу `items` формы.
6283
+ */
6284
+ this.itemStates = signal([]);
6285
+ /**
6286
+ * Признак режима редактирования существующей позиции — `orderItem` задан.
6287
+ */
6288
+ this.isEditingExistingItem = computed(() => this.orderItem() != null);
6289
+ /**
6290
+ * Признак того, что отправлять больше нечего — все позиции либо успешно
6291
+ * отправлены, либо находятся в процессе отправки.
6292
+ */
6293
+ this.hasNoPendingItems = computed(() => {
6294
+ const states = this.itemStates();
6295
+ if (this.items.length === 0) {
6296
+ return true;
6297
+ }
6298
+ return this.items.controls.every((_, index) => {
6299
+ const state = states[index] ?? IDLE_ITEM_STATE;
6300
+ return state === 'success' || state === 'loading';
6301
+ });
6302
+ });
6303
+ /**
6304
+ * Суммарная площадь всех позиций (м²), рассчитанная локально.
6305
+ */
6306
+ this.totalAreaM2 = computed(() => {
6307
+ let total = 0;
6308
+ this.itemsValue().forEach((value) => {
6309
+ total += this.rowTotalPartsAreaM2(value);
6310
+ });
6311
+ return total;
6312
+ });
6313
+ /**
6314
+ * Итоговая стоимость по всем позициям (локальный расчёт).
6315
+ */
6316
+ this.totalCost = computed(() => {
6317
+ const cost = this.productSignal()?.costRub ?? 0;
6318
+ const area = this.totalAreaM2();
6319
+ return Math.round(area * cost * 100) / 100;
6320
+ });
6321
+ /**
6322
+ * Текст ограничения по ширине («от … до …»), пока есть выбранный товар.
6323
+ */
6324
+ this.widthRangeText = computed(() => this.formatRange(this.settings()?.minWidth ?? null, this.productSignal()?.properties?.width ?? null, 'мм'));
6325
+ /**
6326
+ * Текст ограничения по длине («от … до …»), пока есть выбранный товар.
6327
+ */
6328
+ this.lengthRangeText = computed(() => this.formatRange(this.settings()?.minLength ?? null, this.productSignal()?.properties?.length ?? null, 'мм'));
6329
+ /**
6330
+ * Ссылка для автоматического управления уничтожением зависимостей.
6331
+ */
6332
+ this.destroyRef = inject(DestroyRef);
6333
+ }
6334
+ /**
6335
+ * Валидаторы ширины/длины: пока товар не выбран — не накладываем min/max каталога (поля read-only без товара).
6336
+ *
6337
+ * @param min Нижняя граница размера из настроек конфигуратора.
6338
+ * @param maxUpper Верхняя граница из свойств выбранного товара.
6339
+ */
6340
+ dimensionValidators(min, maxUpper) {
6341
+ const chosenProduct = this.product.value;
6342
+ if (!chosenProduct) {
6343
+ return [];
6344
+ }
6345
+ return [Validators.required, Validators.min(min), ...(maxUpper != null && maxUpper > 0 ? [Validators.max(maxUpper)] : []), stepValidator(1)];
6346
+ }
6347
+ /**
6348
+ * Обновляет валидаторы размерных полей после смены товара или настроек.
6349
+ */
6350
+ updateItemsValidators() {
6351
+ const settings = this.settings();
6352
+ const minWidth = settings?.minWidth ?? 0.01;
6353
+ const minLength = settings?.minLength ?? 0.01;
6354
+ const chosenProduct = this.product.value;
6355
+ this.items.controls.forEach((group) => {
6356
+ group.get('width')?.setValidators(this.dimensionValidators(minWidth, chosenProduct?.properties?.width));
6357
+ group.get('length')?.setValidators(this.dimensionValidators(minLength, chosenProduct?.properties?.length));
6358
+ group.get('width')?.updateValueAndValidity({ emitEvent: false });
6359
+ group.get('length')?.updateValueAndValidity({ emitEvent: false });
6360
+ });
6361
+ }
6362
+ /**
6363
+ * Форматирование диапазона «от … до …» с учётом отсутствующих границ.
6364
+ *
6365
+ * @param min Нижняя граница диапазона.
6366
+ * @param max Верхняя граница диапазона.
6367
+ * @param unit Единица измерения для подписи.
6368
+ */
6369
+ // eslint-disable-next-line class-methods-use-this
6370
+ formatRange(min, max, unit) {
6371
+ const hasMin = min != null && min > 0;
6372
+ const hasMax = max != null && max > 0;
6373
+ if (!hasMin && !hasMax) {
6374
+ return null;
6375
+ }
6376
+ if (hasMin && hasMax) {
6377
+ return `от ${min} ${unit} до ${max} ${unit}`;
6378
+ }
6379
+ if (hasMin) {
6380
+ return `от ${min} ${unit}`;
6381
+ }
6382
+ return `до ${max} ${unit}`;
6383
+ }
6384
+ /**
6385
+ * Массив изделий.
6386
+ */
6387
+ get items() {
6388
+ return this.form.controls.items;
6389
+ }
6390
+ /**
6391
+ * {@inheritDoc}
6392
+ */
6393
+ ngOnInit() {
6394
+ this.setDefaultFormValue();
6395
+ this.products$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((products) => {
6396
+ if (this.product.value != null || products.length === 0) {
6397
+ return;
6398
+ }
6399
+ /* При загрузке списка автоматически выбираем первый материал. */
6400
+ this.product.setValue(products[0], { emitEvent: true });
6401
+ });
6402
+ this.product.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((chosenProduct) => {
6403
+ this.updateItemsValidators();
6404
+ if (chosenProduct) {
6405
+ this.items.controls.forEach((item) => {
6406
+ const value = item.getRawValue();
6407
+ const maxW = chosenProduct.properties?.width;
6408
+ const maxL = chosenProduct.properties?.length;
6409
+ item.patchValue({
6410
+ width: maxW ? Math.min(value.width, maxW) : value.width,
6411
+ length: maxL ? Math.min(value.length, maxL) : value.length,
6412
+ });
6413
+ });
6414
+ /*
6415
+ * Смена продукта — сбрасываем состояние позиций: границы каталога изменились,
6416
+ * успешная отправка больше не отражает текущее состояние.
6417
+ */
6418
+ this.resetAllItemStates();
6419
+ }
6420
+ });
6421
+ }
6422
+ /**
6423
+ * Создает группу для изделия.
6424
+ *
6425
+ * @param item Параметры изделия.
6426
+ * @param item.width Ширина изделия.
6427
+ * @param item.length Длина изделия.
6428
+ * @param item.count Количество изделий.
6429
+ * @param item.marker Маркер позиции.
6430
+ */
6431
+ createItemGroup(item) {
6432
+ const group = new FormGroup({
6433
+ width: new FormControl(item?.width ?? 0, { nonNullable: true }),
6434
+ length: new FormControl(item?.length ?? 0, { nonNullable: true }),
6435
+ count: new FormControl(item?.count ?? 1, {
6436
+ validators: [Validators.required, Validators.min(1), stepValidator(1)],
6437
+ nonNullable: true,
6438
+ }),
6439
+ marker: new FormControl(item?.marker ?? null),
6440
+ });
6441
+ // Любое изменение значений позиции сбрасывает её состояние отправки —
6442
+ // ранее успешный submit становится неактуальным при правке полей,
6443
+ // ошибку валидации сервера тоже сбрасываем.
6444
+ group.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
6445
+ const index = this.items.controls.indexOf(group);
6446
+ if (index !== -1) {
6447
+ this.resetItemState(index);
6448
+ ['width', 'length', 'count', 'marker'].forEach((field) => {
6449
+ const control = group.get(field);
6450
+ if (control?.hasError('serverResponse')) {
6451
+ const errors = { ...control.errors };
6452
+ delete errors['serverResponse'];
6453
+ control.setErrors(Object.keys(errors).length > 0 ? errors : null);
6454
+ }
6455
+ });
6456
+ }
6457
+ });
6458
+ return group;
6459
+ }
6460
+ /**
6461
+ * Сбрасывает состояние одной позиции на `idle`, если оно было `success`/`error`.
6462
+ *
6463
+ * @param index Индекс позиции.
6464
+ */
6465
+ resetItemState(index) {
6466
+ const states = this.itemStates();
6467
+ const current = states[index];
6468
+ if (current === 'success' || current === 'error') {
6469
+ const updated = [...states];
6470
+ updated[index] = 'idle';
6471
+ this.itemStates.set(updated);
6472
+ }
6473
+ }
6474
+ /**
6475
+ * Сбрасывает состояния всех позиций на `idle`.
6476
+ */
6477
+ resetAllItemStates() {
6478
+ this.itemStates.set(this.items.controls.map(() => IDLE_ITEM_STATE));
6479
+ }
6480
+ /**
6481
+ * Возвращает состояние позиции по индексу (по умолчанию `idle`).
6482
+ *
6483
+ * @param index Индекс позиции.
6484
+ */
6485
+ getItemState(index) {
6486
+ return this.itemStates()[index] ?? IDLE_ITEM_STATE;
6487
+ }
6488
+ /**
6489
+ * Признак того, что позиция отправляется (поля должны быть заблокированы).
6490
+ *
6491
+ * @param index Индекс позиции.
6492
+ */
6493
+ isItemLoading(index) {
6494
+ return this.getItemState(index) === 'loading';
6495
+ }
6496
+ /**
6497
+ * Признак того, что позиция успешно отправлена и её поля нужно заблокировать.
6498
+ *
6499
+ * @param index Индекс позиции.
6500
+ */
6501
+ isItemLocked(index) {
6502
+ return this.getItemState(index) === 'success';
6503
+ }
6504
+ /**
6505
+ * Добавляет новое изделие в форму.
6506
+ */
6507
+ addItem() {
6508
+ this.items.push(this.createItemGroup());
6509
+ this.itemStates.update((states) => [...states, 'idle']);
6510
+ this.updateItemsValidators();
6511
+ }
6512
+ /**
6513
+ * Удаляет изделие из формы.
6514
+ *
6515
+ * @param index Индекс изделия.
6516
+ */
6517
+ removeItem(index) {
6518
+ this.items.removeAt(index);
6519
+ this.itemStates.update((states) => {
6520
+ const updated = [...states];
6521
+ updated.splice(index, 1);
6522
+ return updated;
6523
+ });
6524
+ this.updateItemsValidators();
6525
+ }
6526
+ /**
6527
+ * Площадь одной единицы изделия без учёта количества, м².
6528
+ *
6529
+ * @param value Значения размеров изделия.
6530
+ * @param value.width Ширина изделия.
6531
+ * @param value.length Длина изделия.
6532
+ */
6533
+ // eslint-disable-next-line class-methods-use-this
6534
+ pieceAreaM2(value) {
6535
+ const { width, length } = value;
6536
+ if (!width || !length) {
6537
+ return 0;
6538
+ }
6539
+ return (width * length) / 1_000_000;
6540
+ }
6541
+ /**
6542
+ * Суммарная площадь по строке позиции: площадь единицы × количество, м².
6543
+ *
6544
+ * @param value Значения позиции.
6545
+ * @param value.width Ширина изделия.
6546
+ * @param value.length Длина изделия.
6547
+ * @param value.count Количество изделий.
6548
+ */
6549
+ rowTotalPartsAreaM2(value) {
6550
+ const qty = value.count;
6551
+ if (!qty || !value.width || value.length === 0) {
6552
+ return 0;
6553
+ }
6554
+ return this.pieceAreaM2(value) * qty;
6555
+ }
6556
+ /**
6557
+ * Возвращает приблизительную стоимость строки (локальный расчёт — на всю строку позиции).
6558
+ *
6559
+ * @param value Значения изделия.
6560
+ * @param value.width Ширина изделия.
6561
+ * @param value.length Длина изделия.
6562
+ * @param value.count Количество изделий.
6563
+ */
6564
+ rowCost(value) {
6565
+ const area = this.rowTotalPartsAreaM2(value);
6566
+ const cost = this.product.value?.costRub ?? 0;
6567
+ return Math.round(area * cost * 100) / 100;
6568
+ }
6569
+ /**
6570
+ * Обновляет состояние позиции на указанное значение.
6571
+ *
6572
+ * @param index Индекс позиции.
6573
+ * @param state Новое состояние.
6574
+ */
6575
+ setItemState(index, state) {
6576
+ this.itemStates.update((states) => {
6577
+ const updated = [...states];
6578
+ updated[index] = state;
6579
+ return updated;
6580
+ });
6581
+ }
6582
+ /**
6583
+ * Применяет ошибки серверной валидации к контролам формы.
6584
+ * Сервер возвращает ключи вида `items.0.<field>` — переводим в фактический индекс позиции.
6585
+ *
6586
+ * @param error Ответ сервера.
6587
+ * @param index Индекс позиции в форме.
6588
+ */
6589
+ applyServerValidationErrors(error, index) {
6590
+ if (!(error instanceof HttpErrorResponse)) {
6591
+ return;
6592
+ }
6593
+ const { errors, message } = error.error;
6594
+ if (errors) {
6595
+ Object.keys(errors).forEach((key) => {
6596
+ this.form.get(['items', index, key])?.setErrors({ serverResponse: errors[key] });
6597
+ });
6598
+ }
6599
+ else if (message) {
6600
+ this.form.setErrors({ serverResponse: [message] });
6601
+ }
6602
+ }
6603
+ /**
6604
+ * Обработчик добавления/редактирования продукта в корзине.
6605
+ */
6606
+ onSubmit() {
6607
+ if (this.form.invalid || !this.product.value) {
6608
+ return;
6609
+ }
6610
+ const productId = this.product.value.id;
6611
+ const productCategoryId = Number(this.categoryId());
6612
+ const configurator = this.editor();
6613
+ const orderData = this.orderItem();
6614
+ const indexesToSubmit = this.items.controls
6615
+ .map((_, index) => index)
6616
+ .filter((index) => {
6617
+ const state = this.getItemState(index);
6618
+ return state !== 'success' && state !== 'loading';
6619
+ });
6620
+ if (indexesToSubmit.length === 0) {
6621
+ return;
6622
+ }
6623
+ // Позиции отправляются последовательно: каждая следующая — только после ответа предыдущей.
6624
+ // При ошибке цепочка прерывается; диалог не закрывается.
6625
+ from(indexesToSubmit)
6626
+ .pipe(concatMap((index, position) => {
6627
+ // Editing-режим: первая позиция (index 0) обновляется, остальные добавляются.
6628
+ const isEdit = orderData != null && index === 0 && position === 0;
6629
+ this.setItemState(index, 'loading');
6630
+ return this.buildItemRequest$(index, productId, productCategoryId, configurator, isEdit, orderData).pipe(tap(() => {
6631
+ this.setItemState(index, 'success');
6632
+ }), catchError((error) => {
6633
+ this.applyServerValidationErrors(error, index);
6634
+ this.setItemState(index, 'error');
6635
+ return throwError(() => error);
6636
+ }));
6637
+ }), takeUntilDestroyed(this.destroyRef))
6638
+ .subscribe({
6639
+ complete: () => {
6640
+ if (this.context) {
6641
+ this.context.$implicit.complete();
6642
+ }
6643
+ else {
6644
+ this.setDefaultFormValue();
6645
+ }
6646
+ },
6647
+ });
6648
+ }
6649
+ /**
6650
+ * Формирует HTTP-запрос для одной позиции формы.
6651
+ *
6652
+ * @param index Индекс позиции в форме.
6653
+ * @param productId Идентификатор выбранного продукта.
6654
+ * @param productCategoryId Идентификатор категории.
6655
+ * @param configurator Название конфигуратора.
6656
+ * @param isEdit Режим обновления существующей позиции.
6657
+ * @param orderData Текущая позиция заказа (только в режиме редактирования).
6658
+ */
6659
+ buildItemRequest$(index, productId, productCategoryId, configurator, isEdit, orderData) {
6660
+ const itemValue = this.items.at(index).getRawValue();
6661
+ const payload = this.convertersService.removeNull({
6662
+ productCategoryId,
6663
+ configurator,
6664
+ productId,
6665
+ width: itemValue.width,
6666
+ length: itemValue.length,
6667
+ quantity: itemValue.count,
6668
+ marker: itemValue.marker,
6669
+ });
6670
+ return isEdit && orderData != null
6671
+ ? this.orderService.updateProduct$(orderData.id, payload, this.orderId)
6672
+ : this.orderService.addProduct$(payload, this.orderId);
6673
+ }
6674
+ /**
6675
+ * Устанавливает начальные значения для полей формы.
6676
+ */
6677
+ setDefaultFormValue() {
6678
+ this.form.reset({
6679
+ productCategoryId: Number(this.categoryId()),
6680
+ configurator: this.editor(),
6681
+ });
6682
+ this.items.clear();
6683
+ const orderItem = this.orderItem();
6684
+ this.items.push(this.createItemGroup({
6685
+ width: (orderItem?.width ?? 0) * 1000,
6686
+ length: (orderItem?.length ?? 0) * 1000,
6687
+ count: orderItem?.quantity ?? 1,
6688
+ marker: orderItem?.marker ?? null,
6689
+ }));
6690
+ this.itemStates.set(this.items.controls.map(() => IDLE_ITEM_STATE));
6691
+ this.updateItemsValidators();
6692
+ this.form.markAsUntouched();
6693
+ }
6694
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSandwichM2Component, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6695
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScSandwichM2Component, isStandalone: true, selector: "sc-sandwich-m2", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null }, categoryId: { classPropertyName: "categoryId", publicName: "categoryId", isSignal: true, isRequired: true, transformFunction: null }, editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, orderItem: { classPropertyName: "orderItem", publicName: "orderItem", isSignal: true, isRequired: true, transformFunction: null }, orderId: { classPropertyName: "orderId", publicName: "orderId", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { toggleShowEvent: "toggleShowEvent" }, providers: [
6696
+ tuiNumberFormatProvider({ precision: 0 }),
6697
+ tuiInputNumberOptionsProvider({
6698
+ min: 0,
6699
+ }),
6700
+ tuiItemsHandlersProvider({
6701
+ stringify: signal((x) => x.name),
6702
+ identityMatcher: signal((a, b) => a.id === b.id),
6703
+ }),
6704
+ ], ngImport: i0, template: "@if (!orderItem() && settings()?.allowShowTable) {\n <div class=\"mb-5 flex justify-center\">\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"toggleShowEvent.emit()\"\n type=\"button\"\n iconStart=\"@tui.layout-list\"\n >\n \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432 \u0432\u0438\u0434\u0435 \u0441\u043F\u0438\u0441\u043A\u0430\n </button>\n </div>\n}\n<div class=\"\">\n @let products = products$ | async;\n @let productValue = product.value;\n\n <form\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit()\"\n ScNextInputFocus\n class=\"flex flex-col gap-2\"\n >\n <label\n tuiLabel\n class=\"grow\"\n >\n \u0422\u043E\u0432\u0430\u0440\n <tui-textfield\n tuiChevron\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n tuiChevron\n tuiSelect\n [formControl]=\"product\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\"\n />\n\n <tui-data-list-wrapper\n *tuiTextfieldDropdown\n new\n [items]=\"products\"\n />\n </tui-textfield>\n <tui-error\n [formControl]=\"product\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (productValue && productValue.costRub !== undefined) {\n <div class=\"-mt-1 text-sm text-tui-text-secondary\">\n <span class=\"font-bold\">{{ productValue.code }}</span>\n \u2014\n {{ productValue.costRub.toLocaleString() }} {{ productValue.currency.symbol }}/\u043C\u00B2\n </div>\n }\n\n @if (productValue) {\n <div class=\"mt-4 mb-3 flex flex-col gap-1.5\">\n <div class=\"w-full font-bold\">\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439</div>\n <div class=\"flex flex-col gap-1 text-sm text-tui-text-secondary\">\n @let wt = widthRangeText();\n @let lt = lengthRangeText();\n @if (wt) {\n <div>\u0428\u0438\u0440\u0438\u043D\u0430 {{ wt }}.</div>\n }\n @if (lt) {\n <div>\u0414\u043B\u0438\u043D\u0430 {{ lt }}.</div>\n }\n </div>\n </div>\n }\n\n <div class=\"flex flex-col gap-3\">\n <div\n formArrayName=\"items\"\n class=\"flex flex-col gap-3\"\n >\n @for (item of items.controls; track item) {\n @if ($index > 0) {\n <hr class=\"border-sc-grey h-px\" />\n }\n @let itemIndex = $index;\n <div\n class=\"relative flex flex-col gap-1\"\n [formGroupName]=\"itemIndex\"\n >\n @if (isItemLocked(itemIndex)) {\n <div class=\"pointer-events-none absolute right-3 bottom-0 z-10 flex size-8 items-center justify-center\">\n <tui-icon\n icon=\"@tui.check\"\n class=\"text-tui-status-positive\"\n />\n </div>\n }\n <tui-loader [showLoader]=\"isItemLoading(itemIndex)\">\n <div\n [class.opacity-70]=\"isItemLocked(itemIndex)\"\n class=\"flex min-w-0 items-start gap-2\"\n >\n <div class=\"flex min-w-0 flex-1 flex-col gap-3\">\n <div class=\"flex flex-col gap-3 lg:!flex-row lg:!items-start lg:!gap-6\">\n <!-- \u0413\u0440\u0443\u043F\u043F\u0430 1: \u0448\u0438\u0440\u0438\u043D\u0430 + \u0434\u043B\u0438\u043D\u0430 -->\n <div class=\"grid grid-cols-1 gap-2 sm:grid-cols-2 lg:!flex-[2]\">\n <label tuiLabel>\n \u0428\u0438\u0440\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"width\"\n [min]=\"settings()?.minWidth ?? 0\"\n [max]=\"product.value?.properties?.width ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n [tuiAutoFocus]=\"!$first\"\n placeholder=\"\u0428\u0438\u0440\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n <tui-error\n formControlName=\"width\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u0414\u043B\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"length\"\n [min]=\"settings()?.minLength ?? 0\"\n [max]=\"product.value?.properties?.length ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n placeholder=\"\u0414\u043B\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n\n <!-- \u0413\u0440\u0443\u043F\u043F\u0430 2: \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E + \u043C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 (\u043D\u0430 lg 1fr | 2fr) -->\n <div class=\"grid grid-cols-1 gap-2 sm:grid-cols-2 lg:!grid-cols-[1fr_2fr] lg:!flex-[3]\">\n <label tuiLabel>\n <span class=\"whitespace-nowrap\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442</span>\n <tui-textfield [tuiTextfieldCleaner]=\"false\">\n <input\n tuiInputNumber\n formControlName=\"count\"\n [min]=\"1\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"count\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"marker\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n placeholder=\"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n </div>\n\n @let rowVal = item.getRawValue();\n\n @if (rowVal.width > 0 && rowVal.length > 0) {\n <div class=\"mt-1 flex flex-col gap-y-1 text-sm lg:!flex-row lg:!gap-6\">\n @if (pieceAreaM2(rowVal) > 0) {\n <div class=\"lg:!flex-[2]\">\n \u041F\u043B\u043E\u0449\u0430\u0434\u044C (S) =\n <span class=\"font-bold\">{{ pieceAreaM2(rowVal) | number: '1.0-4' }} \u043C\u00B2</span>\n </div>\n }\n @if (productValue && rowCost(rowVal) > 0) {\n <div class=\"lg:!flex-[3]\">\n \u0418\u0442\u043E\u0433\u043E:\n <span class=\"font-bold\">{{ rowCost(rowVal).toLocaleString() }} {{ productValue.currency.symbol }}</span>\n </div>\n }\n </div>\n }\n </div>\n\n @if (!isEditingExistingItem()) {\n <button\n tuiIconButton\n title=\"\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\"\n (click)=\"removeItem(itemIndex)\"\n [disabled]=\"items.length <= 1 || !product.value || isItemLoading(itemIndex)\"\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.trash-2\"\n appearance=\"secondary\"\n class=\"mt-6 shrink-0 lg:!ml-4\"\n [style.flex]=\"'0 0 auto'\"\n ></button>\n }\n </div>\n </tui-loader>\n </div>\n }\n </div>\n\n @if (productValue) {\n <hr class=\"border-sc-grey h-px\" />\n <div class=\"min-w-0 gap-1 pt-2 text-right\">\n @if (totalAreaM2() > 0) {\n <div>\n \u041E\u0431\u0449\u0430\u044F \u043F\u043B\u043E\u0449\u0430\u0434\u044C:\n <span class=\"font-bold\">{{ totalAreaM2() | number: '1.0-4' }} \u043C\u00B2</span>\n </div>\n }\n @if (productValue.costRub && totalCost() > 0) {\n <div>\n \u0418\u0442\u043E\u0433\u043E:\n <span class=\"whitespace-nowrap text-xl font-bold\"> {{ totalCost().toLocaleString() }} {{ productValue.currency.symbol }} </span>\n </div>\n }\n </div>\n }\n </div>\n\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n\n <div class=\"flex flex-wrap items-center gap-3\">\n @if (productValue && !isEditingExistingItem()) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"!product.value || items.invalid\"\n (click)=\"addItem()\"\n type=\"button\"\n iconStart=\"@tui.plus\"\n >\n \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\n </button>\n }\n <button\n type=\"submit\"\n [disabled]=\"form.invalid || isSubmitLoading() || hasNoPendingItems()\"\n [loading]=\"isSubmitLoading()\"\n tuiButton\n class=\"ml-auto\"\n iconStart=\"@tui.check\"\n >\n {{ !orderItem() ? '\u0412 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' : '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' }}\n </button>\n </div>\n </form>\n</div>\n", styles: [":host{display:block;width:100%;max-width:36rem}.row-summary{color:var(--tui-text-secondary);font-size:.75rem}\n"], dependencies: [{ kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: ScNextInputFocusModule }, { kind: "directive", type: ScNextInputFocusDirective, selector: "form[ScNextInputFocus]" }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "component", type: TuiDataListWrapperComponent, selector: "tui-data-list-wrapper:not([labels]), tui-data-list-wrapper:not([labels])[new]", inputs: ["items", "disabledItemHandler", "emptyContent", "size", "itemContent"], outputs: ["itemClick"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "directive", type: i1$2.TuiTextfieldDirective, selector: "input[tuiTextfield]:not([tuiInputCard]):not([tuiInputExpire]):not([tuiInputCVC])" }, { kind: "directive", type: i1$2.TuiTextfieldOptionsDirective, selector: "[tuiTextfieldAppearance],[tuiTextfieldSize],[tuiTextfieldCleaner]", inputs: ["tuiTextfieldAppearance", "tuiTextfieldSize", "tuiTextfieldCleaner"] }, { kind: "directive", type: i1$2.TuiTextfieldDropdownDirective, selector: "ng-template[tuiTextfieldDropdown]" }, { kind: "directive", type: TuiChevron, selector: "[tuiChevron]", inputs: ["tuiChevron"] }, { kind: "directive", type: i2$1.TuiSelectDirective, selector: "input[tuiSelect]" }, { kind: "directive", type: i2$1.TuiInputNumberDirective, selector: "input[tuiInputNumber]", inputs: ["min", "max", "prefix", "postfix"] }, { kind: "directive", type: TuiNumberFormat, selector: "[tuiNumberFormat]", inputs: ["tuiNumberFormat"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "component", type: i5$2.TuiSliderComponent, selector: "input[type=range][tuiSlider]", inputs: ["size", "segments"] }, { kind: "directive", type: i2$1.TuiInputSliderDirective, selector: "input[tuiInputSlider]" }, { kind: "directive", type: ScSelectOnFocusinDirective, selector: "tui-input-number, tui-input, tui-input-phone, tui-input-date, tui-input-password, input[tuiInputNumber], input[tuiTextfield], input[tuiInputSlider]" }, { kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "component", type: TuiIcon, selector: "tui-icon", inputs: ["icon", "background"] }, { kind: "directive", type: TuiAutoFocus, selector: "[tuiAutoFocus]", inputs: ["tuiAutoFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6705
+ }
6706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSandwichM2Component, decorators: [{
6707
+ type: Component,
6708
+ args: [{ standalone: true, selector: 'sc-sandwich-m2', imports: [
6709
+ TuiButton,
6710
+ FormsModule,
6711
+ ReactiveFormsModule,
6712
+ ScNextInputFocusModule,
6713
+ TuiLabel,
6714
+ TuiDataListWrapperComponent,
6715
+ TuiError,
6716
+ TuiTextfield,
6717
+ TuiChevron,
6718
+ TuiSelect,
6719
+ TuiInputNumber,
6720
+ TuiNumberFormat,
6721
+ AsyncPipe,
6722
+ DecimalPipe,
6723
+ TuiFieldErrorPipe,
6724
+ TuiInputSlider,
6725
+ ScSelectOnFocusinDirective,
6726
+ TuiLoader,
6727
+ TuiButtonLoading,
6728
+ TuiIcon,
6729
+ TuiAutoFocus,
6730
+ ], providers: [
6731
+ tuiNumberFormatProvider({ precision: 0 }),
6732
+ tuiInputNumberOptionsProvider({
6733
+ min: 0,
6734
+ }),
6735
+ tuiItemsHandlersProvider({
6736
+ stringify: signal((x) => x.name),
6737
+ identityMatcher: signal((a, b) => a.id === b.id),
6738
+ }),
6739
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!orderItem() && settings()?.allowShowTable) {\n <div class=\"mb-5 flex justify-center\">\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"toggleShowEvent.emit()\"\n type=\"button\"\n iconStart=\"@tui.layout-list\"\n >\n \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432 \u0432\u0438\u0434\u0435 \u0441\u043F\u0438\u0441\u043A\u0430\n </button>\n </div>\n}\n<div class=\"\">\n @let products = products$ | async;\n @let productValue = product.value;\n\n <form\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit()\"\n ScNextInputFocus\n class=\"flex flex-col gap-2\"\n >\n <label\n tuiLabel\n class=\"grow\"\n >\n \u0422\u043E\u0432\u0430\u0440\n <tui-textfield\n tuiChevron\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n tuiChevron\n tuiSelect\n [formControl]=\"product\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\"\n />\n\n <tui-data-list-wrapper\n *tuiTextfieldDropdown\n new\n [items]=\"products\"\n />\n </tui-textfield>\n <tui-error\n [formControl]=\"product\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (productValue && productValue.costRub !== undefined) {\n <div class=\"-mt-1 text-sm text-tui-text-secondary\">\n <span class=\"font-bold\">{{ productValue.code }}</span>\n \u2014\n {{ productValue.costRub.toLocaleString() }} {{ productValue.currency.symbol }}/\u043C\u00B2\n </div>\n }\n\n @if (productValue) {\n <div class=\"mt-4 mb-3 flex flex-col gap-1.5\">\n <div class=\"w-full font-bold\">\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439</div>\n <div class=\"flex flex-col gap-1 text-sm text-tui-text-secondary\">\n @let wt = widthRangeText();\n @let lt = lengthRangeText();\n @if (wt) {\n <div>\u0428\u0438\u0440\u0438\u043D\u0430 {{ wt }}.</div>\n }\n @if (lt) {\n <div>\u0414\u043B\u0438\u043D\u0430 {{ lt }}.</div>\n }\n </div>\n </div>\n }\n\n <div class=\"flex flex-col gap-3\">\n <div\n formArrayName=\"items\"\n class=\"flex flex-col gap-3\"\n >\n @for (item of items.controls; track item) {\n @if ($index > 0) {\n <hr class=\"border-sc-grey h-px\" />\n }\n @let itemIndex = $index;\n <div\n class=\"relative flex flex-col gap-1\"\n [formGroupName]=\"itemIndex\"\n >\n @if (isItemLocked(itemIndex)) {\n <div class=\"pointer-events-none absolute right-3 bottom-0 z-10 flex size-8 items-center justify-center\">\n <tui-icon\n icon=\"@tui.check\"\n class=\"text-tui-status-positive\"\n />\n </div>\n }\n <tui-loader [showLoader]=\"isItemLoading(itemIndex)\">\n <div\n [class.opacity-70]=\"isItemLocked(itemIndex)\"\n class=\"flex min-w-0 items-start gap-2\"\n >\n <div class=\"flex min-w-0 flex-1 flex-col gap-3\">\n <div class=\"flex flex-col gap-3 lg:!flex-row lg:!items-start lg:!gap-6\">\n <!-- \u0413\u0440\u0443\u043F\u043F\u0430 1: \u0448\u0438\u0440\u0438\u043D\u0430 + \u0434\u043B\u0438\u043D\u0430 -->\n <div class=\"grid grid-cols-1 gap-2 sm:grid-cols-2 lg:!flex-[2]\">\n <label tuiLabel>\n \u0428\u0438\u0440\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"width\"\n [min]=\"settings()?.minWidth ?? 0\"\n [max]=\"product.value?.properties?.width ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n [tuiAutoFocus]=\"!$first\"\n placeholder=\"\u0428\u0438\u0440\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n <tui-error\n formControlName=\"width\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u0414\u043B\u0438\u043D\u0430, \u043C\u043C\n <tui-textfield>\n <input\n tuiInputSlider\n formControlName=\"length\"\n [min]=\"settings()?.minLength ?? 0\"\n [max]=\"product.value?.properties?.length ?? 0\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n placeholder=\"\u0414\u043B\u0438\u043D\u0430\"\n autocomplete=\"off\"\n />\n @if (product.value) {\n <input\n tuiSlider\n type=\"range\"\n />\n }\n </tui-textfield>\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n\n <!-- \u0413\u0440\u0443\u043F\u043F\u0430 2: \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E + \u043C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 (\u043D\u0430 lg 1fr | 2fr) -->\n <div class=\"grid grid-cols-1 gap-2 sm:grid-cols-2 lg:!grid-cols-[1fr_2fr] lg:!flex-[3]\">\n <label tuiLabel>\n <span class=\"whitespace-nowrap\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442</span>\n <tui-textfield [tuiTextfieldCleaner]=\"false\">\n <input\n tuiInputNumber\n formControlName=\"count\"\n [min]=\"1\"\n [tuiNumberFormat]=\"{ precision: 0 }\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"count\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"marker\"\n [readOnly]=\"!product.value || isItemLocked(itemIndex) || isItemLoading(itemIndex)\"\n placeholder=\"\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\"\n autocomplete=\"off\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n </div>\n </div>\n\n @let rowVal = item.getRawValue();\n\n @if (rowVal.width > 0 && rowVal.length > 0) {\n <div class=\"mt-1 flex flex-col gap-y-1 text-sm lg:!flex-row lg:!gap-6\">\n @if (pieceAreaM2(rowVal) > 0) {\n <div class=\"lg:!flex-[2]\">\n \u041F\u043B\u043E\u0449\u0430\u0434\u044C (S) =\n <span class=\"font-bold\">{{ pieceAreaM2(rowVal) | number: '1.0-4' }} \u043C\u00B2</span>\n </div>\n }\n @if (productValue && rowCost(rowVal) > 0) {\n <div class=\"lg:!flex-[3]\">\n \u0418\u0442\u043E\u0433\u043E:\n <span class=\"font-bold\">{{ rowCost(rowVal).toLocaleString() }} {{ productValue.currency.symbol }}</span>\n </div>\n }\n </div>\n }\n </div>\n\n @if (!isEditingExistingItem()) {\n <button\n tuiIconButton\n title=\"\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\"\n (click)=\"removeItem(itemIndex)\"\n [disabled]=\"items.length <= 1 || !product.value || isItemLoading(itemIndex)\"\n size=\"m\"\n type=\"button\"\n iconStart=\"@tui.trash-2\"\n appearance=\"secondary\"\n class=\"mt-6 shrink-0 lg:!ml-4\"\n [style.flex]=\"'0 0 auto'\"\n ></button>\n }\n </div>\n </tui-loader>\n </div>\n }\n </div>\n\n @if (productValue) {\n <hr class=\"border-sc-grey h-px\" />\n <div class=\"min-w-0 gap-1 pt-2 text-right\">\n @if (totalAreaM2() > 0) {\n <div>\n \u041E\u0431\u0449\u0430\u044F \u043F\u043B\u043E\u0449\u0430\u0434\u044C:\n <span class=\"font-bold\">{{ totalAreaM2() | number: '1.0-4' }} \u043C\u00B2</span>\n </div>\n }\n @if (productValue.costRub && totalCost() > 0) {\n <div>\n \u0418\u0442\u043E\u0433\u043E:\n <span class=\"whitespace-nowrap text-xl font-bold\"> {{ totalCost().toLocaleString() }} {{ productValue.currency.symbol }} </span>\n </div>\n }\n </div>\n }\n </div>\n\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n\n <div class=\"flex flex-wrap items-center gap-3\">\n @if (productValue && !isEditingExistingItem()) {\n <button\n tuiButton\n appearance=\"secondary\"\n [disabled]=\"!product.value || items.invalid\"\n (click)=\"addItem()\"\n type=\"button\"\n iconStart=\"@tui.plus\"\n >\n \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\n </button>\n }\n <button\n type=\"submit\"\n [disabled]=\"form.invalid || isSubmitLoading() || hasNoPendingItems()\"\n [loading]=\"isSubmitLoading()\"\n tuiButton\n class=\"ml-auto\"\n iconStart=\"@tui.check\"\n >\n {{ !orderItem() ? '\u0412 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' : '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' }}\n </button>\n </div>\n </form>\n</div>\n", styles: [":host{display:block;width:100%;max-width:36rem}.row-summary{color:var(--tui-text-secondary);font-size:.75rem}\n"] }]
6740
+ }], propDecorators: { orderId: [{
6741
+ type: Input
6742
+ }] } });
6743
+
6744
+ /**
6745
+ * Skeleton конфигуратора распила сэндвич-панелей по площади.
6746
+ */
6747
+ class ScSandwichM2SkeletonComponent {
6748
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSandwichM2SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6749
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScSandwichM2SkeletonComponent, isStandalone: true, selector: "sc-sandwich-m2-skeleton", ngImport: i0, template: "<div class=\"w-100\">\n <div class=\"flex flex-col gap-3\">\n <!-- \u041F\u043E\u043B\u0435 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \"\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439\". -->\n <div class=\"my-1.5 h-5 w-1/3 rounded bg-sc-light-grey\"></div>\n\n <!-- \u0422\u0435\u043A\u0441\u0442 \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438. -->\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n </div>\n\n <!-- \u0411\u043B\u043E\u043A \u0441 \u0438\u0437\u0434\u0435\u043B\u0438\u044F\u043C\u0438. -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"flex grow gap-4\">\n <div class=\"grid grow grid-cols-4 gap-4\">\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n </div>\n <div class=\"mt-6 size-8 shrink-0 rounded-sm bg-sc-light-grey\"></div>\n </div>\n </div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \"\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\". -->\n <button\n tuiButton\n appearance=\"secondary\"\n class=\"sc-skeleton self-center\"\n ></button>\n\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u043F\u043B\u043E\u0449\u0430\u0434\u044C \u0438 \u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C. -->\n <div class=\"my-2.5 h-6 w-1/3 rounded bg-sc-light-grey\"></div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438. -->\n <button\n tuiButton\n class=\"sc-skeleton self-center\"\n ></button>\n </div>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6750
+ }
6751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSandwichM2SkeletonComponent, decorators: [{
6752
+ type: Component,
6753
+ args: [{ standalone: true, selector: 'sc-sandwich-m2-skeleton', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"w-100\">\n <div class=\"flex flex-col gap-3\">\n <!-- \u041F\u043E\u043B\u0435 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \"\u0420\u0430\u0437\u043C\u0435\u0440\u044B \u0438\u0437\u0434\u0435\u043B\u0438\u0439\". -->\n <div class=\"my-1.5 h-5 w-1/3 rounded bg-sc-light-grey\"></div>\n\n <!-- \u0422\u0435\u043A\u0441\u0442 \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438. -->\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n <div class=\"my-1 h-3 w-2/3 rounded bg-sc-light-grey\"></div>\n </div>\n\n <!-- \u0411\u043B\u043E\u043A \u0441 \u0438\u0437\u0434\u0435\u043B\u0438\u044F\u043C\u0438. -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"flex grow gap-4\">\n <div class=\"grid grow grid-cols-4 gap-4\">\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"my-1 h-3 w-1/2 rounded bg-sc-light-grey\"></div>\n <div class=\"mb-4 mt-2 h-10 w-full rounded-sm bg-sc-light-grey\"></div>\n </div>\n </div>\n <div class=\"mt-6 size-8 shrink-0 rounded-sm bg-sc-light-grey\"></div>\n </div>\n </div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \"\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u0434\u0435\u043B\u0438\u0435\". -->\n <button\n tuiButton\n appearance=\"secondary\"\n class=\"sc-skeleton self-center\"\n ></button>\n\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u043F\u043B\u043E\u0449\u0430\u0434\u044C \u0438 \u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C. -->\n <div class=\"my-2.5 h-6 w-1/3 rounded bg-sc-light-grey\"></div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438. -->\n <button\n tuiButton\n class=\"sc-skeleton self-center\"\n ></button>\n </div>\n</div>\n" }]
6754
+ }] });
6755
+
6108
6756
  /**
6109
6757
  * Компонент карточки менеджера Push-уведомлений.
6110
6758
  */
@@ -9281,5 +9929,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
9281
9929
  * Generated bundle index. Do not edit.
9282
9930
  */
9283
9931
 
9284
- export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, CostWithDiscountComponent, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_BANNER_DURATION, SC_CATALOG_PRODUCTS_FILTERS, SC_CATALOG_SHOW_PRODUCTS_RECURSIVELY, SC_DATE_FORMATTER, SC_DEBOUNCE_TIME_DEFAULT, SC_DIALOG_SERVICE_TOKEN, SC_ERROR_CHANGE_HANDLER, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_HIDDEN_PRINT_ELEMENTS, SC_MANAGER_QR_HANDLER, SC_NOTIFY_WHEN_IN_STOCK_REQUIRED_FIELDS, SC_ORDER_OPTIONS, SC_PAGE_SIZE_OPTIONS$1 as SC_PAGE_SIZE_OPTIONS, SC_SHOW_HELP_NOTIFICATION_IN_PHONE_INPUT, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddOrEditingCartItemDialogComponent, ScAddOrEditingCartItemFormComponent, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderItemComponent, ScOrderItemsByDirection, ScOrderItemsListByDirectionsComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderModule, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareButtonModule, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
9932
+ export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, CostWithDiscountComponent, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IDLE_ITEM_STATE, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_BANNER_DURATION, SC_CATALOG_PRODUCTS_FILTERS, SC_CATALOG_SHOW_PRODUCTS_RECURSIVELY, SC_DATE_FORMATTER, SC_DEBOUNCE_TIME_DEFAULT, SC_DIALOG_SERVICE_TOKEN, SC_ERROR_CHANGE_HANDLER, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_HIDDEN_PRINT_ELEMENTS, SC_MANAGER_QR_HANDLER, SC_NOTIFY_WHEN_IN_STOCK_REQUIRED_FIELDS, SC_ORDER_OPTIONS, SC_PAGE_SIZE_OPTIONS$1 as SC_PAGE_SIZE_OPTIONS, SC_SHOW_HELP_NOTIFICATION_IN_PHONE_INPUT, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddOrEditingCartItemDialogComponent, ScAddOrEditingCartItemFormComponent, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderItemComponent, ScOrderItemsByDirection, ScOrderItemsListByDirectionsComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderModule, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareButtonModule, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
9285
9933
  //# sourceMappingURL=snabcentr-client-ui.mjs.map