@rt-tools/ui-kit-v2 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs +1 -0
  2. package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs +2 -1
  4. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +1 -0
  6. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs +2 -1
  8. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs +1 -0
  10. package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +4 -2
  12. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +1 -0
  14. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +1 -0
  16. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +2 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -1
  19. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +5 -2
  20. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-field.mjs +1 -0
  22. package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
  23. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs +2 -1
  24. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs.map +1 -1
  25. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs +2 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs.map +1 -1
  27. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +2 -1
  28. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -1
  29. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs +2 -1
  30. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs.map +1 -1
  31. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs +2 -1
  32. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs.map +1 -1
  33. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs +2 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs.map +1 -1
  35. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +1 -0
  36. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  37. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +10 -6
  38. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
  39. package/fesm2022/rt-tools-ui-kit-v2-popover.mjs +22 -2
  40. package/fesm2022/rt-tools-ui-kit-v2-popover.mjs.map +1 -1
  41. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +36 -5
  42. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  43. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs +2 -1
  44. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs.map +1 -1
  45. package/fesm2022/rt-tools-ui-kit-v2-select.mjs +1 -0
  46. package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
  47. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +4 -2
  48. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  49. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs +3 -1
  50. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs.map +1 -1
  51. package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs +1 -0
  52. package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs.map +1 -1
  53. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +3 -1
  54. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  55. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +2 -1
  56. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
  57. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +2 -1
  58. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  59. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +53 -5
  60. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
  61. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +2 -1
  62. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -1
  63. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs +37 -0
  64. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs.map +1 -1
  65. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +2 -1
  66. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -1
  67. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +2 -1
  68. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -1
  69. package/fesm2022/rt-tools-ui-kit-v2-usage-ring.mjs +4 -3
  70. package/fesm2022/rt-tools-ui-kit-v2-usage-ring.mjs.map +1 -1
  71. package/package.json +1 -1
  72. package/src/lib/components/page-header/rt-page-header-mobile.scss +6 -0
  73. package/src/lib/components/popover/CONTEXT.md +4 -0
  74. package/src/lib/components/thread-list/rt-thread-list.component.scss +7 -0
  75. package/src/lib/components/tooltip/CONTEXT.md +4 -0
  76. package/src/rich-editor/lib/components/chat/rt-chat.component.scss +31 -1
  77. package/types/rt-tools-ui-kit-v2-popover.d.ts +11 -0
  78. package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +7 -0
  79. package/types/rt-tools-ui-kit-v2-thread-list.d.ts +7 -1
  80. package/types/rt-tools-ui-kit-v2-tooltip.d.ts +12 -0
@@ -262,6 +262,7 @@ var RtFieldComponent = class RtFieldComponent {
262
262
  "rtPopoverFitViewport",
263
263
  "rtPopoverContext",
264
264
  "rtPopoverPanelClass",
265
+ "rtPopoverBackdropClass",
265
266
  "rtPopoverDisabled",
266
267
  "rtPopoverOffsetY",
267
268
  "rtPopoverOffsetX",
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-field.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/field/rt-field-hint.directive.ts","../../../projects/ui-kit-v2/src/lib/components/field/rt-field.model.ts","../../../projects/ui-kit-v2/src/lib/components/field/rt-field.component.ts","../../../projects/ui-kit-v2/src/lib/components/field/rt-field.component.html"],"sourcesContent":["import { Directive } from '@angular/core';\n\n/**\n * Маркер проецируемого hint'а для rt-field. Нужен, когда подсказке требуется\n * разметка (`<strong>`, интерполяция значений), которую плоский string-input\n * `[hint]` передать не может. Если проецируемый hint присутствует — rt-field\n * рендерит его вместо строкового `[hint]` (тот остаётся fallback'ом).\n */\n@Directive({\n selector: '[rtFieldHint]',\n})\nexport class RtFieldHintDirective {}\n","import { TRtKitLabelKey } from '@rt-tools/ui-kit-v2/core';\n\n/** Контракт поля-обёртки common/ui. */\nexport namespace IRtField {\n /** Карта validatorKey → человекочитаемое сообщение. */\n export type ErrorMessages = Record<string, string>;\n}\n\n/**\n * Ключи сообщений об ошибках по имени валидатора (перекрываются input'ом\n * [errors] поштучно, уже готовым текстом).\n *\n * Здесь ключи, а не переводы: карта собирается при загрузке модуля, когда язык\n * страницы ещё неизвестен, и готовый текст остался бы на языке по умолчанию\n * даже под переведённой подписью поля. Переводит сам компонент поля.\n */\nexport const RT_FIELD_DEFAULT_ERROR_KEYS: Readonly<Record<string, TRtKitLabelKey>> = {\n required: 'fieldErrorRequired',\n email: 'fieldErrorEmail',\n minlength: 'fieldErrorMinLength',\n maxlength: 'fieldErrorMaxLength',\n min: 'fieldErrorMin',\n max: 'fieldErrorMax',\n pattern: 'fieldErrorPattern',\n};\n\n/** Текст-плейсхолдер read-only поля без значения. */\nexport const RT_FIELD_EMPTY_VALUE: string = '—';\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n computed,\n contentChild,\n effect,\n inject,\n input,\n viewChild,\n AfterContentInit,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n InputSignalWithTransform,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { ValidationErrors } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelKey, TRtKitLabelMap, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { rtNotchWidth, RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtPopoverDirective } from '@rt-tools/ui-kit-v2/popover';\nimport { RtSkeletonWrapperComponent } from '@rt-tools/ui-kit-v2/skeleton-wrapper';\nimport { RtFieldHintDirective } from './rt-field-hint.directive';\nimport { RT_FIELD_DEFAULT_ERROR_KEYS, RT_FIELD_EMPTY_VALUE, IRtField } from './rt-field.model';\n\nconst BEM_BLOCK: string = 'rt-field';\n\n/** Счётчик для авто-id (связка label[for] ↔ controlId спроецированного контрола). */\nlet uidSeed: number = 0;\n\nfunction nextAutoId(): string {\n uidSeed += 1;\n return `rt-field-${uidSeed}`;\n}\n\n/**\n * Обёртка единой анатомии поля: label (+ опц. help-иконка с popover, + авто «*»\n * для required) сверху, спроецированный rt-контрол посередине, hint/error снизу.\n * `required` объявляет поле обязательным там, где валидатора на контроле нет (шаблонная форма).\n * `hideRequiredMark` глушит авто-«*» — для форм, где все поля обязательны и\n * маркер лишний (логин).\n * `reserveHintSpace` держит под контролом строку высотой в одно сообщение даже\n * когда ни ошибки, ни hint нет — чтобы появление ошибки не сдвигало форму вниз\n * (нужно для полей без постоянного hint, напр. логин).\n *\n * Читает контрол через contentChild(RtFormControlBase): авто-связывает label[for],\n * прокидывает read-only режим, показывает текст ошибки/required-маркер. На loading\n * оборачивает зону контрола в rt-skeleton-wrapper (label/hint остаются видимыми) —\n * нужно для route-aside, где поля рендерятся до прихода данных.\n */\n@Component({\n selector: 'rt-field',\n templateUrl: './rt-field.component.html',\n styleUrls: ['./rt-field.component.scss', './rt-field-label-inside.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n RtPopoverDirective,\n RtSkeletonWrapperComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n host: {\n class: BEM_BLOCK,\n '[class.rt-field--filled]': 'filled()',\n '[class.rt-field--invalid]': 'showError()',\n '[class.rt-field--static]': 'isStatic()',\n '[class.rt-field--label-inside]': 'labelInside()',\n '[style.--rt-field-notch-width]': \"notchWidth() + 'px'\",\n },\n})\nexport class RtFieldComponent implements AfterContentInit {\n // contentChild нельзя объявлять на ES-private (#) поле — Angular это запрещает,\n // поэтому проекция контрола живёт в protected-поле.\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly projectedControl: Signal<RtFormControlBase<unknown> | undefined> = contentChild(RtFormControlBase);\n\n // Проецируемый hint (с разметкой) — если присутствует, рендерится вместо\n // строкового [hint].\n protected readonly projectedHint: Signal<RtFieldHintDirective | undefined> = contentChild(RtFieldHintDirective);\n\n readonly #autoId: string = nextAutoId();\n\n /** Строка подписи: по её ширине материальный набор вырезает разрыв в рамке. `#`-поле `viewChild` не принимает. */\n protected readonly labelRow: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('labelRow');\n\n /**\n * Ширина подписи на рамке, px. Разрыв рамки под подписью, как у поля Material, режется по этому\n * числу: ширину подписи стили сами не знают.\n */\n protected readonly notchWidth: Signal<number> = rtNotchWidth(this.labelRow);\n\n /** Итоговый id: явный controlId контрола важнее авто-id. */\n protected readonly controlId: Signal<string> = computed((): string => this.projectedControl()?.effectiveControlId() ?? this.#autoId);\n\n protected readonly isRequired: Signal<boolean> = computed((): boolean => this.projectedControl()?.required() ?? false);\n\n protected readonly showError: Signal<boolean> = computed((): boolean => !!this.projectedControl()?.invalid());\n\n /**\n * У контрола есть значение. Под материальным набором по нему подпись поднимается на рамку:\n * текст значения есть у всех контролов, поэтому одно правило служит полю, области текста,\n * числу и выбору.\n */\n protected readonly filled: Signal<boolean> = computed((): boolean => (this.projectedControl()?.displayText() ?? '') !== '');\n\n /** Поле без рамки ввода — только для чтения или в загрузке: подпись остаётся над значением. */\n protected readonly isStatic: Signal<boolean> = computed(\n (): boolean => this.readonly() || this.loading() || !!this.projectedControl()?.isReadonly()\n );\n\n /** Подпись материального набора ложится внутрь контрола: только у полей с местом подсказки. */\n protected readonly labelInside: Signal<boolean> = computed(\n (): boolean => !this.isStatic() && !!this.projectedControl()?.fieldLabelInside\n );\n\n /** Имя первого сработавшего валидатора: сообщений показываем по одному */\n readonly #failedValidator: Signal<string> = computed((): string => {\n const errs: ValidationErrors | null = this.projectedControl()?.errors() ?? null;\n return errs ? (Object.keys(errs)[0] ?? '') : '';\n });\n\n /**\n * Умолчание переводится по ключу.\n *\n * Ключ передаётся сигналом, а не строкой: так подпись пересчитывается и при\n * смене валидатора, и при смене языка — пользователь переключает его\n * на лету, и текст ошибки обязан переключиться вместе с формой.\n */\n readonly #defaultErrorText: Signal<string> = rtKitLabel(\n computed((): TRtKitLabelKey | '' => RT_FIELD_DEFAULT_ERROR_KEYS[this.#failedValidator()] ?? '')\n );\n\n /** Сообщение об ошибке: текст, переданный формой, важнее умолчания */\n protected readonly errorText: Signal<string> = computed((): string => {\n const validator: string = this.#failedValidator();\n if (!validator) {\n return '';\n }\n const own: string | undefined = this.errors()[validator];\n if (own) {\n return own;\n }\n return RT_FIELD_DEFAULT_ERROR_KEYS[validator] ? this.#defaultErrorText() : '';\n });\n\n protected readonly emptyValue: string = RT_FIELD_EMPTY_VALUE;\n\n public readonly label: InputSignal<string> = input<string>('');\n public readonly hint: InputSignal<string> = input<string>('');\n public readonly help: InputSignal<string> = input<string>('');\n public readonly readonly: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly loading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /**\n * Поле обязательно, хотя валидатора на контроле нет: шаблонная форма его не кладёт. Даёт «*» и\n * ошибку под тронутым пустым полем. Форму недействительной не делает — решение о записи\n * остаётся у формы.\n */\n public readonly required: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly hideRequiredMark: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly reserveHintSpace: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly errors: InputSignal<IRtField.ErrorMessages> = input<IRtField.ErrorMessages>({});\n\n constructor() {\n // Реактивно прокидываем read-only режим в спроецированный контрол.\n effect((): void => {\n this.projectedControl()?.setReadonly(this.readonly());\n });\n effect((): void => {\n this.projectedControl()?.setRequired(this.required());\n });\n }\n\n public ngAfterContentInit(): void {\n const ctrl: RtFormControlBase<unknown> | undefined = this.projectedControl();\n // Авто-id только если у контрола нет явного controlId — для label[for].\n if (ctrl && ctrl.effectiveControlId() === null) {\n ctrl.assignControlId(this.#autoId);\n }\n }\n}\n","<ng-container rtBlock=\"rt-field\">\n @if (label()) {\n <span #labelRow rtElem=\"label-row\">\n <!-- Текст подписи — свой узел: пробел между ним и звёздочкой сборка выбрасывает, и зазор до\n звёздочки задаёт свойство, а не пробел в разметке. -->\n <label rtElem=\"label\" qa-dataid=\"field-label\" [attr.for]=\"controlId()\">\n <span>{{ label() }}</span>\n @if (isRequired() && !hideRequiredMark()) {\n <span rtElem=\"required\" qa-dataid=\"field-required\" aria-hidden=\"true\">*</span>\n }\n </label>\n @if (help()) {\n <button\n rtElem=\"help\"\n qa-dataid=\"field-help\"\n type=\"button\"\n rtPopoverTrigger=\"hover\"\n [attr.aria-label]=\"t().uiHint\"\n [rtPopover]=\"helpTpl\">\n <rt-icon name=\"ico-help\" size=\"sm\" color=\"muted\" />\n </button>\n <ng-template #helpTpl>\n <span rtElem=\"help-popover\" qa-dataid=\"field-help-popover\">{{ help() }}</span>\n </ng-template>\n }\n </span>\n }\n\n <rt-skeleton-wrapper rtElem=\"control\" height=\"var(--rt-input-height)\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n\n <!-- С резервом hint остаётся в потоке даже под ошибкой (скрытый), а сама\n ошибка кладётся в ту же ячейку грида: высота зоны равна большему из\n двух сообщений и не меняется при их подмене. Без резерва — как было,\n одно сообщение за раз, чтобы плотные формы не набирали лишний воздух. -->\n <span rtElem=\"messages\" [rtMod]=\"{ reserved: reserveHintSpace() }\">\n @if (projectedHint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n <ng-content select=\"[rtFieldHint]\" />\n </small>\n } @else if (hint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n {{ hint() }}\n </small>\n }\n\n @if (showError()) {\n <small rtElem=\"error\" qa-dataid=\"field-error\">{{ errorText() }}</small>\n }\n </span>\n</ng-container>\n"],"mappings":";;;;;;;;AAWA,IAAa,uBAAb,MAAa,qBAAoB;CAApB;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAoB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAApB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAoB,cAAA;GAAA,UAAA;GAAA,UAAA;EAAA,CAAA;CAAA;;AAApB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EAHhC,MAAA;EAAU,MAAA,CAAA,EACP,UAAU,gBACb,CAAA;;;ACMD,MAAa,8BAAwE;CACjF,UAAU;CACV,OAAO;CACP,WAAW;CACX,WAAW;CACX,KAAK;CACL,KAAK;CACL,SAAS;AACZ;AAGD,MAAa,uBAA+B;ACG5C,MAAM,YAAoB;AAG1B,IAAI,UAAkB;AAEtB,SAAS,aAAU;CACf,WAAW;CACX,OAAO,YAAY;AACvB;AAyCA,IAAa,mBAAb,MAAa,iBAAgB;CAWhB;CAoCA;CAYA;CA4CT,cAAA;EApGmB,KAAA,IAA4B,OAAO,aAAa;EAEhD,KAAA,mBAAmE,aAAa,mBAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;EAIlG,KAAA,gBAA0D,aAAa,sBAAqB,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;EAEtG,KAAA,UAAkB,WAAU;EAGlB,KAAA,WAAwD,UAAmC,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;EAMtG,KAAA,aAA6B,aAAa,KAAK,QAAQ;EAGvD,KAAA,YAA4B,eAAuB,KAAK,iBAAgB,CAAE,EAAE,mBAAkB,KAAM,KAAK,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAEjH,KAAA,aAA8B,eAAwB,KAAK,iBAAgB,CAAE,EAAE,SAAQ,KAAM,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;EAEnG,KAAA,YAA6B,eAAwB,CAAC,CAAC,KAAK,iBAAgB,CAAE,EAAE,QAAO,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAO1F,KAAA,SAA0B,gBAAyB,KAAK,iBAAgB,CAAE,EAAE,YAAW,KAAM,QAAQ,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;EAGxG,KAAA,WAA4B,eAC5B,KAAK,SAAQ,KAAM,KAAK,QAAO,KAAM,CAAC,CAAC,KAAK,iBAAgB,CAAE,EAAE,WAAU,GAC5F,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;EAGkB,KAAA,cAA+B,eAC/B,CAAC,KAAK,SAAQ,KAAM,CAAC,CAAC,KAAK,iBAAgB,CAAE,EAAE,kBACjE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAGQ,KAAA,mBAAmC,eAAsB;GAC9D,MAAM,OAAgC,KAAK,iBAAgB,CAAE,EAAE,OAAM,KAAM;GAC3E,OAAO,OAAQ,OAAO,KAAK,IAAI,CAAC,CAAC,MAAM,KAAM;EACjD,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;EASO,KAAA,oBAAoC,WACzC,eAAoC,4BAA4B,KAAK,iBAAgB,MAAO,EAAE,CAAC;EAIhF,KAAA,YAA4B,eAAsB;GACjE,MAAM,YAAoB,KAAK,iBAAgB;GAC/C,IAAI,CAAC,WACD,OAAO;GAEX,MAAM,MAA0B,KAAK,OAAM,CAAE,CAAC;GAC9C,IAAI,KACA,OAAO;GAEX,OAAO,4BAA4B,aAAa,KAAK,kBAAiB,IAAK;EAC/E,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAEiB,KAAA,aAAA;EAEH,KAAA,QAA6B,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAC9C,KAAA,OAA4B,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAC7C,KAAA,OAA4B,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAC7C,KAAA,WAA4D,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,WAAA,+BAAA,CAAA;GAC1G,WAAW;EAAgB,CAAA;EAEf,KAAA,UAA2D,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,UAAA,+BAAA,CAAA;GACzG,WAAW;EAAgB,CAAA;EAOf,KAAA,WAA4D,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,WAAA,+BAAA,CAAA;GAC1G,WAAW;EAAgB,CAAA;EAEf,KAAA,mBAAoE,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,mBAAA,+BAAA,CAAA;GAClH,WAAW;EAAgB,CAAA;EAEf,KAAA,mBAAoE,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,mBAAA,+BAAA,CAAA;GAClH,WAAW;EAAgB,CAAA;EAEf,KAAA,SAA8C,MAA8B,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;EAI3F,aAAkB;GACd,KAAK,iBAAgB,CAAE,EAAE,YAAY,KAAK,SAAQ,CAAE;EACxD,CAAC;EACD,aAAkB;GACd,KAAK,iBAAgB,CAAE,EAAE,YAAY,KAAK,SAAQ,CAAE;EACxD,CAAC;CACL;CAEO,qBAAkB;EACrB,MAAM,OAA+C,KAAK,iBAAgB;EAE1E,IAAI,QAAQ,KAAK,mBAAkB,MAAO,MACtC,KAAK,gBAAgB,KAAK,OAAO;CAEzC;CAvHS;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAgB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAAhB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAgB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,MAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,MAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,UAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,UAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,kBAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,kBAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,QAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,MAAA;IAAA,YAAA;KAAA,0BAAA;KAAA,2BAAA;KAAA,0BAAA;KAAA,gCAAA;KAAA,gCAAA;IAAA;IAAA,gBAAA;GAAA;GAAA,SAAA,CAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAK0E;IAAiB,aAAA;IAAA,UAAA;GAAA,GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAI1B;IAAoB,aAAA;IAAA,UAAA;GAAA,CAAA;GAAA,aAAA,CAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,UAAA;IAAA,aAAA;IAAA,UAAA;GAAA,CAAA;GAAA,UAAA;GAAA,UCxFlH;GAoDA,QAAA,CAAA,+nEAAA,o0PAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MDWQ;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAkB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,mBAAA,iBAAA;KAAA,UAAA,CAAA,WAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAClB;KAA0B,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAC1B;KAAc,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACd;KAAa,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACb;KAAY,UAAA;IAAA;GAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAWP,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAxB5B,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAU,iBAGH,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IAEL;IACA;IACA;IACA;IACA;IACA;GACH;GAAA,MACK;IACF,OAAO;IACP,4BAA4B;IAC5B,6BAA6B;IAC7B,4BAA4B;IAC5B,kCAAkC;IAClC,kCAAkC;GACrC;GAAA,UAAA;GAAA,QAAA,CAAA,+nEAAA,o0PAAA;EAAA,CAAA;CAOkG,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,kBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAAA,iBAAiB,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAI1B,oBAAoB,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAKA,YAAU,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,kBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,kBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-field.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/field/rt-field-hint.directive.ts","../../../projects/ui-kit-v2/src/lib/components/field/rt-field.model.ts","../../../projects/ui-kit-v2/src/lib/components/field/rt-field.component.ts","../../../projects/ui-kit-v2/src/lib/components/field/rt-field.component.html"],"sourcesContent":["import { Directive } from '@angular/core';\n\n/**\n * Маркер проецируемого hint'а для rt-field. Нужен, когда подсказке требуется\n * разметка (`<strong>`, интерполяция значений), которую плоский string-input\n * `[hint]` передать не может. Если проецируемый hint присутствует — rt-field\n * рендерит его вместо строкового `[hint]` (тот остаётся fallback'ом).\n */\n@Directive({\n selector: '[rtFieldHint]',\n})\nexport class RtFieldHintDirective {}\n","import { TRtKitLabelKey } from '@rt-tools/ui-kit-v2/core';\n\n/** Контракт поля-обёртки common/ui. */\nexport namespace IRtField {\n /** Карта validatorKey → человекочитаемое сообщение. */\n export type ErrorMessages = Record<string, string>;\n}\n\n/**\n * Ключи сообщений об ошибках по имени валидатора (перекрываются input'ом\n * [errors] поштучно, уже готовым текстом).\n *\n * Здесь ключи, а не переводы: карта собирается при загрузке модуля, когда язык\n * страницы ещё неизвестен, и готовый текст остался бы на языке по умолчанию\n * даже под переведённой подписью поля. Переводит сам компонент поля.\n */\nexport const RT_FIELD_DEFAULT_ERROR_KEYS: Readonly<Record<string, TRtKitLabelKey>> = {\n required: 'fieldErrorRequired',\n email: 'fieldErrorEmail',\n minlength: 'fieldErrorMinLength',\n maxlength: 'fieldErrorMaxLength',\n min: 'fieldErrorMin',\n max: 'fieldErrorMax',\n pattern: 'fieldErrorPattern',\n};\n\n/** Текст-плейсхолдер read-only поля без значения. */\nexport const RT_FIELD_EMPTY_VALUE: string = '—';\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n computed,\n contentChild,\n effect,\n inject,\n input,\n viewChild,\n AfterContentInit,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n InputSignalWithTransform,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { ValidationErrors } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelKey, TRtKitLabelMap, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { rtNotchWidth, RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtPopoverDirective } from '@rt-tools/ui-kit-v2/popover';\nimport { RtSkeletonWrapperComponent } from '@rt-tools/ui-kit-v2/skeleton-wrapper';\nimport { RtFieldHintDirective } from './rt-field-hint.directive';\nimport { RT_FIELD_DEFAULT_ERROR_KEYS, RT_FIELD_EMPTY_VALUE, IRtField } from './rt-field.model';\n\nconst BEM_BLOCK: string = 'rt-field';\n\n/** Счётчик для авто-id (связка label[for] ↔ controlId спроецированного контрола). */\nlet uidSeed: number = 0;\n\nfunction nextAutoId(): string {\n uidSeed += 1;\n return `rt-field-${uidSeed}`;\n}\n\n/**\n * Обёртка единой анатомии поля: label (+ опц. help-иконка с popover, + авто «*»\n * для required) сверху, спроецированный rt-контрол посередине, hint/error снизу.\n * `required` объявляет поле обязательным там, где валидатора на контроле нет (шаблонная форма).\n * `hideRequiredMark` глушит авто-«*» — для форм, где все поля обязательны и\n * маркер лишний (логин).\n * `reserveHintSpace` держит под контролом строку высотой в одно сообщение даже\n * когда ни ошибки, ни hint нет — чтобы появление ошибки не сдвигало форму вниз\n * (нужно для полей без постоянного hint, напр. логин).\n *\n * Читает контрол через contentChild(RtFormControlBase): авто-связывает label[for],\n * прокидывает read-only режим, показывает текст ошибки/required-маркер. На loading\n * оборачивает зону контрола в rt-skeleton-wrapper (label/hint остаются видимыми) —\n * нужно для route-aside, где поля рендерятся до прихода данных.\n */\n@Component({\n selector: 'rt-field',\n templateUrl: './rt-field.component.html',\n styleUrls: ['./rt-field.component.scss', './rt-field-label-inside.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n RtPopoverDirective,\n RtSkeletonWrapperComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n host: {\n class: BEM_BLOCK,\n '[class.rt-field--filled]': 'filled()',\n '[class.rt-field--invalid]': 'showError()',\n '[class.rt-field--static]': 'isStatic()',\n '[class.rt-field--label-inside]': 'labelInside()',\n '[style.--rt-field-notch-width]': \"notchWidth() + 'px'\",\n },\n})\nexport class RtFieldComponent implements AfterContentInit {\n // contentChild нельзя объявлять на ES-private (#) поле — Angular это запрещает,\n // поэтому проекция контрола живёт в protected-поле.\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly projectedControl: Signal<RtFormControlBase<unknown> | undefined> = contentChild(RtFormControlBase);\n\n // Проецируемый hint (с разметкой) — если присутствует, рендерится вместо\n // строкового [hint].\n protected readonly projectedHint: Signal<RtFieldHintDirective | undefined> = contentChild(RtFieldHintDirective);\n\n readonly #autoId: string = nextAutoId();\n\n /** Строка подписи: по её ширине материальный набор вырезает разрыв в рамке. `#`-поле `viewChild` не принимает. */\n protected readonly labelRow: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('labelRow');\n\n /**\n * Ширина подписи на рамке, px. Разрыв рамки под подписью, как у поля Material, режется по этому\n * числу: ширину подписи стили сами не знают.\n */\n protected readonly notchWidth: Signal<number> = rtNotchWidth(this.labelRow);\n\n /** Итоговый id: явный controlId контрола важнее авто-id. */\n protected readonly controlId: Signal<string> = computed((): string => this.projectedControl()?.effectiveControlId() ?? this.#autoId);\n\n protected readonly isRequired: Signal<boolean> = computed((): boolean => this.projectedControl()?.required() ?? false);\n\n protected readonly showError: Signal<boolean> = computed((): boolean => !!this.projectedControl()?.invalid());\n\n /**\n * У контрола есть значение. Под материальным набором по нему подпись поднимается на рамку:\n * текст значения есть у всех контролов, поэтому одно правило служит полю, области текста,\n * числу и выбору.\n */\n protected readonly filled: Signal<boolean> = computed((): boolean => (this.projectedControl()?.displayText() ?? '') !== '');\n\n /** Поле без рамки ввода — только для чтения или в загрузке: подпись остаётся над значением. */\n protected readonly isStatic: Signal<boolean> = computed(\n (): boolean => this.readonly() || this.loading() || !!this.projectedControl()?.isReadonly()\n );\n\n /** Подпись материального набора ложится внутрь контрола: только у полей с местом подсказки. */\n protected readonly labelInside: Signal<boolean> = computed(\n (): boolean => !this.isStatic() && !!this.projectedControl()?.fieldLabelInside\n );\n\n /** Имя первого сработавшего валидатора: сообщений показываем по одному */\n readonly #failedValidator: Signal<string> = computed((): string => {\n const errs: ValidationErrors | null = this.projectedControl()?.errors() ?? null;\n return errs ? (Object.keys(errs)[0] ?? '') : '';\n });\n\n /**\n * Умолчание переводится по ключу.\n *\n * Ключ передаётся сигналом, а не строкой: так подпись пересчитывается и при\n * смене валидатора, и при смене языка — пользователь переключает его\n * на лету, и текст ошибки обязан переключиться вместе с формой.\n */\n readonly #defaultErrorText: Signal<string> = rtKitLabel(\n computed((): TRtKitLabelKey | '' => RT_FIELD_DEFAULT_ERROR_KEYS[this.#failedValidator()] ?? '')\n );\n\n /** Сообщение об ошибке: текст, переданный формой, важнее умолчания */\n protected readonly errorText: Signal<string> = computed((): string => {\n const validator: string = this.#failedValidator();\n if (!validator) {\n return '';\n }\n const own: string | undefined = this.errors()[validator];\n if (own) {\n return own;\n }\n return RT_FIELD_DEFAULT_ERROR_KEYS[validator] ? this.#defaultErrorText() : '';\n });\n\n protected readonly emptyValue: string = RT_FIELD_EMPTY_VALUE;\n\n public readonly label: InputSignal<string> = input<string>('');\n public readonly hint: InputSignal<string> = input<string>('');\n public readonly help: InputSignal<string> = input<string>('');\n public readonly readonly: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly loading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /**\n * Поле обязательно, хотя валидатора на контроле нет: шаблонная форма его не кладёт. Даёт «*» и\n * ошибку под тронутым пустым полем. Форму недействительной не делает — решение о записи\n * остаётся у формы.\n */\n public readonly required: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly hideRequiredMark: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly reserveHintSpace: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n public readonly errors: InputSignal<IRtField.ErrorMessages> = input<IRtField.ErrorMessages>({});\n\n constructor() {\n // Реактивно прокидываем read-only режим в спроецированный контрол.\n effect((): void => {\n this.projectedControl()?.setReadonly(this.readonly());\n });\n effect((): void => {\n this.projectedControl()?.setRequired(this.required());\n });\n }\n\n public ngAfterContentInit(): void {\n const ctrl: RtFormControlBase<unknown> | undefined = this.projectedControl();\n // Авто-id только если у контрола нет явного controlId — для label[for].\n if (ctrl && ctrl.effectiveControlId() === null) {\n ctrl.assignControlId(this.#autoId);\n }\n }\n}\n","<ng-container rtBlock=\"rt-field\">\n @if (label()) {\n <span #labelRow rtElem=\"label-row\">\n <!-- Текст подписи — свой узел: пробел между ним и звёздочкой сборка выбрасывает, и зазор до\n звёздочки задаёт свойство, а не пробел в разметке. -->\n <label rtElem=\"label\" qa-dataid=\"field-label\" [attr.for]=\"controlId()\">\n <span>{{ label() }}</span>\n @if (isRequired() && !hideRequiredMark()) {\n <span rtElem=\"required\" qa-dataid=\"field-required\" aria-hidden=\"true\">*</span>\n }\n </label>\n @if (help()) {\n <button\n rtElem=\"help\"\n qa-dataid=\"field-help\"\n type=\"button\"\n rtPopoverTrigger=\"hover\"\n [attr.aria-label]=\"t().uiHint\"\n [rtPopover]=\"helpTpl\">\n <rt-icon name=\"ico-help\" size=\"sm\" color=\"muted\" />\n </button>\n <ng-template #helpTpl>\n <span rtElem=\"help-popover\" qa-dataid=\"field-help-popover\">{{ help() }}</span>\n </ng-template>\n }\n </span>\n }\n\n <rt-skeleton-wrapper rtElem=\"control\" height=\"var(--rt-input-height)\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n\n <!-- С резервом hint остаётся в потоке даже под ошибкой (скрытый), а сама\n ошибка кладётся в ту же ячейку грида: высота зоны равна большему из\n двух сообщений и не меняется при их подмене. Без резерва — как было,\n одно сообщение за раз, чтобы плотные формы не набирали лишний воздух. -->\n <span rtElem=\"messages\" [rtMod]=\"{ reserved: reserveHintSpace() }\">\n @if (projectedHint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n <ng-content select=\"[rtFieldHint]\" />\n </small>\n } @else if (hint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n {{ hint() }}\n </small>\n }\n\n @if (showError()) {\n <small rtElem=\"error\" qa-dataid=\"field-error\">{{ errorText() }}</small>\n }\n </span>\n</ng-container>\n"],"mappings":"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"}
@@ -278,7 +278,8 @@ var RtFileCardComponent = class RtFileCardComponent {
278
278
  "rtTooltipPlacement",
279
279
  "rtTooltipShowDelay",
280
280
  "rtTooltipWhenTruncated",
281
- "rtTooltipHideOnClick"
281
+ "rtTooltipHideOnClick",
282
+ "rtTooltipShowOnTap"
282
283
  ],
283
284
  exportAs: ["rtTooltip"]
284
285
  },
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-file-card.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/file-card/file-card.helper.ts","../../../projects/ui-kit-v2/src/lib/components/file-card/rt-truncate-middle.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/file-card/rt-file-card.component.ts","../../../projects/ui-kit-v2/src/lib/components/file-card/rt-file-card.component.html"],"sourcesContent":["import { IRtIcon } from '@rt-tools/ui-kit-v2/core';\n\n/** Иконка Word-семейства (несколько расширений делят один глиф). */\nconst ICON_WORD: IRtIcon.Name = 'attach-docx-doc';\n\n/** Иконка растровых изображений. */\nconst ICON_IMAGE: IRtIcon.Name = 'attach-jpg-png';\n\n/** Иконка презентаций. */\nconst ICON_PRESENTATION: IRtIcon.Name = 'attach-ppt-pptx';\n\n/** Иконка таблиц Excel-семейства. */\nconst ICON_SPREADSHEET: IRtIcon.Name = 'attach-xlsx-xls';\n\n/** Иконка архивов. */\nconst ICON_ARCHIVE: IRtIcon.Name = 'attach-zip-rar';\n\n/** Иконка web-форматов изображений/видео. */\nconst ICON_WEB_MEDIA: IRtIcon.Name = 'attach-webp';\n\n/**\n * Маппинг расширения файла → имя цветной иконки типа (`attach-*` в rt-icon-реестре).\n */\nconst FILE_TYPE_ICON: Readonly<Record<string, IRtIcon.Name>> = {\n doc: ICON_WORD,\n docx: ICON_WORD,\n odt: ICON_WORD,\n gif: ICON_IMAGE,\n jpg: ICON_IMAGE,\n jpeg: ICON_IMAGE,\n png: ICON_IMAGE,\n pdf: 'attach-pdf',\n ppt: ICON_PRESENTATION,\n pptx: ICON_PRESENTATION,\n txt: 'attach-txt',\n webp: ICON_WEB_MEDIA,\n webm: ICON_WEB_MEDIA,\n xls: ICON_SPREADSHEET,\n xlsx: ICON_SPREADSHEET,\n zip: ICON_ARCHIVE,\n rar: ICON_ARCHIVE,\n msg: 'attach-msg',\n eml: 'attach-eml',\n};\n\n/**\n * Возвращает имя иконки типа файла по его имени (берётся расширение после\n * последней точки). Неизвестное/отсутствующее расширение → нейтральный `attach-other`.\n */\nexport function getFileTypeIcon(fileName: string): IRtIcon.Name {\n const ext: string = fileName.split('.').pop()?.toLowerCase() ?? '';\n return FILE_TYPE_ICON[ext] ?? 'attach-other';\n}\n\n/**\n * Выводит читаемый заголовок из имени файла: отбрасывает расширение и заменяет\n * разделители `_`/`-` на пробелы (CamelCase намеренно НЕ разбивается — это давало\n * бы непредсказуемый результат на произвольных именах). Если после очистки строка\n * пустая (например имя было `.pdf`) — возвращается исходное имя.\n */\nexport function deriveFileTitle(fileName: string): string {\n const withoutExt: string = fileName.replace(/\\.[^./\\\\]+$/, '');\n const cleaned: string = withoutExt.replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').trim();\n return cleaned || fileName;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n/** Многоточие, вставляемое в середину строки при усечении. */\nconst ELLIPSIS: string = '...';\n\n/** Длина строки по умолчанию, при которой включается усечение. */\nconst DEFAULT_MAX_LENGTH: number = 32;\n\n/**\n * Усекает длинное имя файла по середине, сохраняя расширение видимым:\n * `10588-1_EvaluationSearchReport.pdf` → `10588-1_EvaluationSearchRe...pdf`.\n *\n * Обычный CSS `text-overflow: ellipsis` режет хвост и теряет расширение —\n * поэтому усечение по середине делается здесь, а не в стилях. Усечение\n * посимвольное (не по ширине контейнера): предсказуемо и без измерений DOM.\n *\n * Pure-pipe: пересчитывается только при смене входа.\n */\n@Pipe({ name: 'rtTruncateMiddle' })\nexport class RtTruncateMiddlePipe implements PipeTransform {\n public transform(value: string | null | undefined, maxLength: number = DEFAULT_MAX_LENGTH): string {\n if (!value) {\n return '';\n }\n if (value.length <= maxLength) {\n return value;\n }\n\n const lastDot: number = value.lastIndexOf('.');\n const hasExt: boolean = lastDot > 0 && lastDot < value.length - 1;\n const ext: string = hasExt ? value.slice(lastDot + 1) : '';\n const base: string = hasExt ? value.slice(0, lastDot) : value;\n\n const headLength: number = maxLength - ext.length - ELLIPSIS.length;\n if (headLength <= 0) {\n // Расширение длиннее лимита — деградируем до простого усечения хвоста.\n return value.slice(0, Math.max(0, maxLength - ELLIPSIS.length)) + ELLIPSIS;\n }\n\n return base.slice(0, headLength) + ELLIPSIS + ext;\n }\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n ChangeDetectionStrategy,\n Component,\n InputSignal,\n InputSignalWithTransform,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { formatFileSize } from '@rt-tools/ui-kit-v2/core';\n\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { IRtIconButton } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { deriveFileTitle, getFileTypeIcon } from './file-card.helper';\nimport { IRtFileCard } from './rt-file-card.model';\nimport { RtTruncateMiddlePipe } from './rt-truncate-middle.pipe';\n\nconst BEM_BLOCK: string = 'rt-file-card';\n\n/** Иконка типа файла крупнее прочего: по ней карточку находят взглядом. */\nconst ICON_SIZES: Readonly<Record<IRtFileCard.Size, IRtIcon.Size>> = {\n sm: 'sm',\n md: 'xl',\n lg: 'xl',\n};\n\n/** Кнопки действий: в компактной карточке они не должны задавать её высоту. */\nconst ACTION_SIZES: Readonly<Record<IRtFileCard.Size, IRtIconButton.Size>> = {\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n};\n\n/**\n * Карточка загруженного файла: цветная иконка типа, заголовок (выведенный из имени),\n * усечённое по середине имя файла, бейдж размера и опциональные действия справа\n * (скачать / переименовать / удалить).\n *\n * Презентационный компонент: сам ничего не решает, только эмиттит события действий.\n * Кнопки включаются точечно через `showDownload`/`showRemove`/`showRename` — в режиме\n * загрузки обычно нужен только remove, при показе сохранённых файлов — download.\n *\n * `showRename` по умолчанию выключен и сейчас нигде не включается: бэкенд не\n * поддерживает переименование (`original_name` неизменяем после создания). Вход\n * оставлен, чтобы кнопку можно было включить без правки компонента, когда появится API.\n */\n@Component({\n selector: 'rt-file-card',\n templateUrl: './rt-file-card.component.html',\n styleUrls: ['./rt-file-card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n RtIconButtonComponent,\n RtTooltipDirective,\n BlockDirective,\n ElemDirective,\n\n // pipes\n RtTruncateMiddlePipe,\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-file-card--sm]': \"size() === 'sm'\",\n '[class.rt-file-card--md]': \"size() === 'md'\",\n '[class.rt-file-card--lg]': \"size() === 'lg'\",\n },\n})\nexport class RtFileCardComponent {\n /** Имя цветной иконки типа файла по расширению. */\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly iconName: Signal<IRtIcon.Name> = computed((): IRtIcon.Name => getFileTypeIcon(this.name()));\n\n /** Заголовок карточки, выведенный из имени файла (без расширения, разделители → пробелы). */\n protected readonly title: Signal<string> = computed((): string => deriveFileTitle(this.name()));\n\n /** Отформатированный размер; пустая строка → размер неизвестен. */\n protected readonly sizeLabel: Signal<string> = computed((): string => formatFileSize(this.sizeBytes()));\n\n /** Показывать ли бейдж размера. */\n protected readonly hasSize: Signal<boolean> = computed((): boolean => this.sizeLabel() !== '');\n\n /** Размер иконки типа файла — маппится от размера карточки. */\n protected readonly iconSize: Signal<IRtIcon.Size> = computed((): IRtIcon.Size => ICON_SIZES[this.size()]);\n\n /** Размер кнопок действий — маппится от размера карточки. */\n protected readonly actionSize: Signal<IRtIconButton.Size> = computed((): IRtIconButton.Size => ACTION_SIZES[this.size()]);\n\n /** Имя файла с расширением — обязательно. */\n public readonly name: InputSignal<string> = input.required<string>();\n\n /** Размер в байтах. `null` → бейдж размера скрыт. */\n public readonly sizeBytes: InputSignal<number | null> = input<number | null>(null);\n\n /** Плотность карточки: `sm` для тесных мест вроде композера, `lg` — для страниц. */\n public readonly size: InputSignal<IRtFileCard.Size> = input<IRtFileCard.Size>('md');\n\n /** Показывать кнопку скачивания. */\n public readonly showDownload: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Показывать кнопку удаления. */\n public readonly showRemove: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Показывать кнопку переименования (отключена до появления API на бэке). */\n public readonly showRename: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Блокирует кнопки действий (например, во время сабмита формы). */\n public readonly disabled: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Запрошено скачивание файла. */\n public readonly download: OutputEmitterRef<void> = output<void>();\n\n /** Запрошено удаление файла. */\n public readonly removed: OutputEmitterRef<void> = output<void>();\n\n /** Запрошено переименование файла. */\n public readonly rename: OutputEmitterRef<void> = output<void>();\n\n protected onDownload(): void {\n this.download.emit();\n }\n\n protected onRemove(): void {\n this.removed.emit();\n }\n\n protected onRename(): void {\n this.rename.emit();\n }\n}\n","<ng-container rtBlock=\"rt-file-card\">\n <rt-icon rtElem=\"icon\" [size]=\"iconSize()\" [name]=\"iconName()\" />\n\n <span rtElem=\"text\">\n <span rtElem=\"title\" qa-dataid=\"file-card-title\" [rtTooltip]=\"title()\">{{ title() }}</span>\n <span rtElem=\"name\" qa-dataid=\"file-card-name\" [rtTooltip]=\"name()\">{{ name() | rtTruncateMiddle }}</span>\n </span>\n\n <span rtElem=\"meta\">\n @if (hasSize()) {\n <span rtElem=\"size\" qa-dataid=\"file-card-size\">{{ sizeLabel() }}</span>\n }\n\n @if (showDownload()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-download\"\n icon=\"ico-download\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiDownload\"\n [tooltip]=\"t().uiDownload\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onDownload()\" />\n }\n\n @if (showRename()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-rename\"\n icon=\"ico-edit\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiRename\"\n [tooltip]=\"t().uiRename\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onRename()\" />\n }\n\n @if (showRemove()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-remove\"\n icon=\"ico-trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiRemove\"\n [tooltip]=\"t().uiRemove\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onRemove()\" />\n }\n </span>\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-file-card.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/file-card/file-card.helper.ts","../../../projects/ui-kit-v2/src/lib/components/file-card/rt-truncate-middle.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/file-card/rt-file-card.component.ts","../../../projects/ui-kit-v2/src/lib/components/file-card/rt-file-card.component.html"],"sourcesContent":["import { IRtIcon } from '@rt-tools/ui-kit-v2/core';\n\n/** Иконка Word-семейства (несколько расширений делят один глиф). */\nconst ICON_WORD: IRtIcon.Name = 'attach-docx-doc';\n\n/** Иконка растровых изображений. */\nconst ICON_IMAGE: IRtIcon.Name = 'attach-jpg-png';\n\n/** Иконка презентаций. */\nconst ICON_PRESENTATION: IRtIcon.Name = 'attach-ppt-pptx';\n\n/** Иконка таблиц Excel-семейства. */\nconst ICON_SPREADSHEET: IRtIcon.Name = 'attach-xlsx-xls';\n\n/** Иконка архивов. */\nconst ICON_ARCHIVE: IRtIcon.Name = 'attach-zip-rar';\n\n/** Иконка web-форматов изображений/видео. */\nconst ICON_WEB_MEDIA: IRtIcon.Name = 'attach-webp';\n\n/**\n * Маппинг расширения файла → имя цветной иконки типа (`attach-*` в rt-icon-реестре).\n */\nconst FILE_TYPE_ICON: Readonly<Record<string, IRtIcon.Name>> = {\n doc: ICON_WORD,\n docx: ICON_WORD,\n odt: ICON_WORD,\n gif: ICON_IMAGE,\n jpg: ICON_IMAGE,\n jpeg: ICON_IMAGE,\n png: ICON_IMAGE,\n pdf: 'attach-pdf',\n ppt: ICON_PRESENTATION,\n pptx: ICON_PRESENTATION,\n txt: 'attach-txt',\n webp: ICON_WEB_MEDIA,\n webm: ICON_WEB_MEDIA,\n xls: ICON_SPREADSHEET,\n xlsx: ICON_SPREADSHEET,\n zip: ICON_ARCHIVE,\n rar: ICON_ARCHIVE,\n msg: 'attach-msg',\n eml: 'attach-eml',\n};\n\n/**\n * Возвращает имя иконки типа файла по его имени (берётся расширение после\n * последней точки). Неизвестное/отсутствующее расширение → нейтральный `attach-other`.\n */\nexport function getFileTypeIcon(fileName: string): IRtIcon.Name {\n const ext: string = fileName.split('.').pop()?.toLowerCase() ?? '';\n return FILE_TYPE_ICON[ext] ?? 'attach-other';\n}\n\n/**\n * Выводит читаемый заголовок из имени файла: отбрасывает расширение и заменяет\n * разделители `_`/`-` на пробелы (CamelCase намеренно НЕ разбивается — это давало\n * бы непредсказуемый результат на произвольных именах). Если после очистки строка\n * пустая (например имя было `.pdf`) — возвращается исходное имя.\n */\nexport function deriveFileTitle(fileName: string): string {\n const withoutExt: string = fileName.replace(/\\.[^./\\\\]+$/, '');\n const cleaned: string = withoutExt.replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').trim();\n return cleaned || fileName;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n/** Многоточие, вставляемое в середину строки при усечении. */\nconst ELLIPSIS: string = '...';\n\n/** Длина строки по умолчанию, при которой включается усечение. */\nconst DEFAULT_MAX_LENGTH: number = 32;\n\n/**\n * Усекает длинное имя файла по середине, сохраняя расширение видимым:\n * `10588-1_EvaluationSearchReport.pdf` → `10588-1_EvaluationSearchRe...pdf`.\n *\n * Обычный CSS `text-overflow: ellipsis` режет хвост и теряет расширение —\n * поэтому усечение по середине делается здесь, а не в стилях. Усечение\n * посимвольное (не по ширине контейнера): предсказуемо и без измерений DOM.\n *\n * Pure-pipe: пересчитывается только при смене входа.\n */\n@Pipe({ name: 'rtTruncateMiddle' })\nexport class RtTruncateMiddlePipe implements PipeTransform {\n public transform(value: string | null | undefined, maxLength: number = DEFAULT_MAX_LENGTH): string {\n if (!value) {\n return '';\n }\n if (value.length <= maxLength) {\n return value;\n }\n\n const lastDot: number = value.lastIndexOf('.');\n const hasExt: boolean = lastDot > 0 && lastDot < value.length - 1;\n const ext: string = hasExt ? value.slice(lastDot + 1) : '';\n const base: string = hasExt ? value.slice(0, lastDot) : value;\n\n const headLength: number = maxLength - ext.length - ELLIPSIS.length;\n if (headLength <= 0) {\n // Расширение длиннее лимита — деградируем до простого усечения хвоста.\n return value.slice(0, Math.max(0, maxLength - ELLIPSIS.length)) + ELLIPSIS;\n }\n\n return base.slice(0, headLength) + ELLIPSIS + ext;\n }\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n ChangeDetectionStrategy,\n Component,\n InputSignal,\n InputSignalWithTransform,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { formatFileSize } from '@rt-tools/ui-kit-v2/core';\n\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { IRtIconButton } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { deriveFileTitle, getFileTypeIcon } from './file-card.helper';\nimport { IRtFileCard } from './rt-file-card.model';\nimport { RtTruncateMiddlePipe } from './rt-truncate-middle.pipe';\n\nconst BEM_BLOCK: string = 'rt-file-card';\n\n/** Иконка типа файла крупнее прочего: по ней карточку находят взглядом. */\nconst ICON_SIZES: Readonly<Record<IRtFileCard.Size, IRtIcon.Size>> = {\n sm: 'sm',\n md: 'xl',\n lg: 'xl',\n};\n\n/** Кнопки действий: в компактной карточке они не должны задавать её высоту. */\nconst ACTION_SIZES: Readonly<Record<IRtFileCard.Size, IRtIconButton.Size>> = {\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n};\n\n/**\n * Карточка загруженного файла: цветная иконка типа, заголовок (выведенный из имени),\n * усечённое по середине имя файла, бейдж размера и опциональные действия справа\n * (скачать / переименовать / удалить).\n *\n * Презентационный компонент: сам ничего не решает, только эмиттит события действий.\n * Кнопки включаются точечно через `showDownload`/`showRemove`/`showRename` — в режиме\n * загрузки обычно нужен только remove, при показе сохранённых файлов — download.\n *\n * `showRename` по умолчанию выключен и сейчас нигде не включается: бэкенд не\n * поддерживает переименование (`original_name` неизменяем после создания). Вход\n * оставлен, чтобы кнопку можно было включить без правки компонента, когда появится API.\n */\n@Component({\n selector: 'rt-file-card',\n templateUrl: './rt-file-card.component.html',\n styleUrls: ['./rt-file-card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n RtIconButtonComponent,\n RtTooltipDirective,\n BlockDirective,\n ElemDirective,\n\n // pipes\n RtTruncateMiddlePipe,\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-file-card--sm]': \"size() === 'sm'\",\n '[class.rt-file-card--md]': \"size() === 'md'\",\n '[class.rt-file-card--lg]': \"size() === 'lg'\",\n },\n})\nexport class RtFileCardComponent {\n /** Имя цветной иконки типа файла по расширению. */\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly iconName: Signal<IRtIcon.Name> = computed((): IRtIcon.Name => getFileTypeIcon(this.name()));\n\n /** Заголовок карточки, выведенный из имени файла (без расширения, разделители → пробелы). */\n protected readonly title: Signal<string> = computed((): string => deriveFileTitle(this.name()));\n\n /** Отформатированный размер; пустая строка → размер неизвестен. */\n protected readonly sizeLabel: Signal<string> = computed((): string => formatFileSize(this.sizeBytes()));\n\n /** Показывать ли бейдж размера. */\n protected readonly hasSize: Signal<boolean> = computed((): boolean => this.sizeLabel() !== '');\n\n /** Размер иконки типа файла — маппится от размера карточки. */\n protected readonly iconSize: Signal<IRtIcon.Size> = computed((): IRtIcon.Size => ICON_SIZES[this.size()]);\n\n /** Размер кнопок действий — маппится от размера карточки. */\n protected readonly actionSize: Signal<IRtIconButton.Size> = computed((): IRtIconButton.Size => ACTION_SIZES[this.size()]);\n\n /** Имя файла с расширением — обязательно. */\n public readonly name: InputSignal<string> = input.required<string>();\n\n /** Размер в байтах. `null` → бейдж размера скрыт. */\n public readonly sizeBytes: InputSignal<number | null> = input<number | null>(null);\n\n /** Плотность карточки: `sm` для тесных мест вроде композера, `lg` — для страниц. */\n public readonly size: InputSignal<IRtFileCard.Size> = input<IRtFileCard.Size>('md');\n\n /** Показывать кнопку скачивания. */\n public readonly showDownload: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Показывать кнопку удаления. */\n public readonly showRemove: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Показывать кнопку переименования (отключена до появления API на бэке). */\n public readonly showRename: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Блокирует кнопки действий (например, во время сабмита формы). */\n public readonly disabled: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Запрошено скачивание файла. */\n public readonly download: OutputEmitterRef<void> = output<void>();\n\n /** Запрошено удаление файла. */\n public readonly removed: OutputEmitterRef<void> = output<void>();\n\n /** Запрошено переименование файла. */\n public readonly rename: OutputEmitterRef<void> = output<void>();\n\n protected onDownload(): void {\n this.download.emit();\n }\n\n protected onRemove(): void {\n this.removed.emit();\n }\n\n protected onRename(): void {\n this.rename.emit();\n }\n}\n","<ng-container rtBlock=\"rt-file-card\">\n <rt-icon rtElem=\"icon\" [size]=\"iconSize()\" [name]=\"iconName()\" />\n\n <span rtElem=\"text\">\n <span rtElem=\"title\" qa-dataid=\"file-card-title\" [rtTooltip]=\"title()\">{{ title() }}</span>\n <span rtElem=\"name\" qa-dataid=\"file-card-name\" [rtTooltip]=\"name()\">{{ name() | rtTruncateMiddle }}</span>\n </span>\n\n <span rtElem=\"meta\">\n @if (hasSize()) {\n <span rtElem=\"size\" qa-dataid=\"file-card-size\">{{ sizeLabel() }}</span>\n }\n\n @if (showDownload()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-download\"\n icon=\"ico-download\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiDownload\"\n [tooltip]=\"t().uiDownload\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onDownload()\" />\n }\n\n @if (showRename()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-rename\"\n icon=\"ico-edit\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiRename\"\n [tooltip]=\"t().uiRename\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onRename()\" />\n }\n\n @if (showRemove()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-remove\"\n icon=\"ico-trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiRemove\"\n [tooltip]=\"t().uiRemove\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onRemove()\" />\n }\n </span>\n</ng-container>\n"],"mappings":"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"}
@@ -168,7 +168,8 @@ var RtHybridTreeSelectorComponent = class RtHybridTreeSelectorComponent extends
168
168
  "rtTooltipPlacement",
169
169
  "rtTooltipShowDelay",
170
170
  "rtTooltipWhenTruncated",
171
- "rtTooltipHideOnClick"
171
+ "rtTooltipHideOnClick",
172
+ "rtTooltipShowOnTap"
172
173
  ],
173
174
  exportAs: ["rtTooltip"]
174
175
  },
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-hybrid-tree-selector.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/hybrid-tree-selector/rt-hybrid-tree-selector.component.ts","../../../projects/ui-kit-v2/src/lib/components/tree-selector/rt-tree-selector.component.html"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\n\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtButtonDirective } from '@rt-tools/ui-kit-v2/button';\nimport { RtHybridTreeComponent } from '@rt-tools/ui-kit-v2/hybrid-tree';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport { RtToggleSwitchComponent } from '@rt-tools/ui-kit-v2/toggle-switch';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtTreeComponent } from '@rt-tools/ui-kit-v2/tree';\nimport { RtTreeSelectorComponent } from '@rt-tools/ui-kit-v2/tree-selector';\n\n/** Блок разметки — блок `rt-tree-selector`: панель у селекторов одна, отличается дерево внутри. */\nconst BEM_BLOCK: string = 'rt-tree-selector';\n\n/**\n * Выбор гибридным деревом: поле поиска, строка контролов и `rt-hybrid-tree` под ними.\n *\n * Панель, черновик, «Применить», поиск по словам и кнопки строки берёт у `rt-tree-selector` — та\n * же разметка и те же стили. Своё здесь одно: внутри стоит дерево, где часть групп выбирает один\n * лист. Узлы — `IRtHybridTree.Node`, признак `single` доходит до дерева нетронутым.\n */\n@Component({\n selector: 'rt-hybrid-tree-selector',\n templateUrl: '../tree-selector/rt-tree-selector.component.html',\n styleUrl: '../tree-selector/rt-tree-selector.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n FormsModule,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtButtonDirective,\n RtIconComponent,\n RtInputComponent,\n RtToggleSwitchComponent,\n RtTooltipDirective,\n RtTreeComponent,\n RtHybridTreeComponent,\n ],\n host: { class: BEM_BLOCK },\n})\nexport class RtHybridTreeSelectorComponent<TValue> extends RtTreeSelectorComponent<TValue> {\n protected override readonly hybrid: boolean = true;\n}\n","<ng-container rtBlock=\"rt-tree-selector\">\n <div rtElem=\"head\">\n @if (label()) {\n <div rtElem=\"label\" qa-dataid=\"tree-selector-label\">{{ label() }}</div>\n }\n\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"tree-selector-search\"\n iconLeft=\"search\"\n [disabled]=\"disabled()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [ngModel]=\"term()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"onTerm($event)\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (underSearchTpl(); as underSearch) {\n <div rtElem=\"under-search\" qa-dataid=\"tree-selector-under-search\">\n <ng-container [ngTemplateOutlet]=\"underSearch.templateRef\" />\n </div>\n }\n\n <div rtElem=\"controls\">\n <div rtElem=\"row\">\n @if (isExpandShown()) {\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-expand-all\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"secondary\"\n size=\"sm\"\n icon=\"expand-all\"\n [rtTooltip]=\"expandAllLabel()\"\n [attr.aria-label]=\"expandAllLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"expandAll()\"></button>\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-collapse-all\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"secondary\"\n size=\"sm\"\n icon=\"collapse-all\"\n [rtTooltip]=\"collapseAllLabel()\"\n [attr.aria-label]=\"collapseAllLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"collapseAll()\"></button>\n }\n @if (isRevertShown()) {\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-revert\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"secondary\"\n size=\"sm\"\n icon=\"undo\"\n [rtTooltip]=\"revertLabel()\"\n [attr.aria-label]=\"revertLabel()\"\n [disabled]=\"disabled() || !isDraftChanged()\"\n (click)=\"onRevert()\"></button>\n }\n @if (isClearShown()) {\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-clear\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"danger\"\n size=\"sm\"\n icon=\"trash\"\n [rtTooltip]=\"clearLabel()\"\n [attr.aria-label]=\"clearLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onClear()\"></button>\n }\n @if (controlsTpl(); as controls) {\n <div rtElem=\"own\">\n <ng-container [ngTemplateOutlet]=\"controls.templateRef\" />\n </div>\n }\n </div>\n\n @if (isMultiToggleShown()) {\n <div rtElem=\"multi\">\n <rt-toggle-switch\n qa-dataid=\"tree-selector-multi\"\n [ariaLabel]=\"multiLabel()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"multiOn()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"onMultiToggle($event)\" />\n <span rtElem=\"multi-label\">{{ multiLabel() }}</span>\n <rt-icon rtElem=\"multi-hint-icon\" name=\"info-circle\" size=\"sm\" [rtTooltip]=\"multiHintLabel()\" />\n </div>\n }\n </div>\n </div>\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"tree-selector-empty\" [rtMod]=\"{ 'with-icon': !!emptyIcon() }\">\n @if (emptyIcon(); as icon) {\n <rt-icon rtElem=\"empty-icon\" qa-dataid=\"tree-selector-empty-icon\" size=\"lg\" [name]=\"icon\" />\n }\n {{ nothingFoundLabel() }}\n </div>\n } @else {\n @if (hybrid) {\n <rt-hybrid-tree\n rtElem=\"tree\"\n qa-dataid=\"tree-selector-tree\"\n [nodes]=\"shownNodes()\"\n [value]=\"draft()\"\n [mode]=\"mode()\"\n [cascade]=\"cascade()\"\n [branchMarks]=\"branchMarks()\"\n [exclusive]=\"isExclusive()\"\n [showSelectAll]=\"selectAll()\"\n [searchTerm]=\"markTerm()\"\n [filter]=\"false\"\n [emptyIcon]=\"emptyIcon()\"\n [nodeEndTemplate]=\"endTemplate()\"\n [nodeMetaTemplate]=\"metaTemplate()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTreeChange($event)\"\n (picked)=\"onPicked()\" />\n } @else {\n <rt-tree\n rtElem=\"tree\"\n qa-dataid=\"tree-selector-tree\"\n [nodes]=\"shownNodes()\"\n [value]=\"draft()\"\n [mode]=\"mode()\"\n [cascade]=\"cascade()\"\n [branchMarks]=\"branchMarks()\"\n [exclusive]=\"isExclusive()\"\n [showSelectAll]=\"selectAll()\"\n [searchTerm]=\"markTerm()\"\n [filter]=\"false\"\n [emptyIcon]=\"emptyIcon()\"\n [nodeEndTemplate]=\"endTemplate()\"\n [nodeMetaTemplate]=\"metaTemplate()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTreeChange($event)\"\n (picked)=\"onPicked()\" />\n }\n }\n\n @if (isFooterShown()) {\n <div rtElem=\"foot\">\n <button\n rtButton\n qa-dataid=\"tree-selector-cancel\"\n type=\"button\"\n appearance=\"text\"\n [label]=\"cancelLabel()\"\n [attr.aria-label]=\"cancelLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"cancel()\"></button>\n <button\n rtButton\n qa-dataid=\"tree-selector-apply\"\n type=\"button\"\n [label]=\"applyLabel()\"\n [attr.aria-label]=\"applyLabel()\"\n [disabled]=\"!canApply()\"\n (click)=\"apply()\"></button>\n </div>\n }\n</ng-container>\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAM,YAAoB;AA+B1B,IAAa,gCAAb,MAAa,sCAA8C,wBAA+B;CAtB1F,cAAA;;EAuBgC,KAAA,SAAkB;CACjD;CAFY;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAA6B,MAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAA7B;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAA6B,cAAA;GAAA,UAAA;GAAA,MAAA,EAAA,gBAAA,mBAAA;GAAA,iBAAA;GAAA,UAAA;GAAA,UChD1C;GAoLA,QAAA,CAAA,g/DAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MDnJQ;KAAgB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAChB;IAAW;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,eAAA;KAAA,UAAA,CAAA,SAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACX;KAAc,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACd;KAAa,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACb;KAAY,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACZ;KAAiB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACjB;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAgB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,iBAAA,gBAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAChB;KAAuB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACvB;KAAkB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,UAAA,CAAA,WAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAClB;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,eAAA,QAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAqB,UAAA;IAAA;GAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAIhB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA6B,YAAA,CAAA;EAtBzC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAyB,iBAGlB,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IACL;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACH;GAAA,MACK,EAAE,OAAO,UAAS;GAAE,UAAA;GAAA,QAAA,CAAA,g/DAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-hybrid-tree-selector.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/hybrid-tree-selector/rt-hybrid-tree-selector.component.ts","../../../projects/ui-kit-v2/src/lib/components/tree-selector/rt-tree-selector.component.html"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\n\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtButtonDirective } from '@rt-tools/ui-kit-v2/button';\nimport { RtHybridTreeComponent } from '@rt-tools/ui-kit-v2/hybrid-tree';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport { RtToggleSwitchComponent } from '@rt-tools/ui-kit-v2/toggle-switch';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtTreeComponent } from '@rt-tools/ui-kit-v2/tree';\nimport { RtTreeSelectorComponent } from '@rt-tools/ui-kit-v2/tree-selector';\n\n/** Блок разметки — блок `rt-tree-selector`: панель у селекторов одна, отличается дерево внутри. */\nconst BEM_BLOCK: string = 'rt-tree-selector';\n\n/**\n * Выбор гибридным деревом: поле поиска, строка контролов и `rt-hybrid-tree` под ними.\n *\n * Панель, черновик, «Применить», поиск по словам и кнопки строки берёт у `rt-tree-selector` — та\n * же разметка и те же стили. Своё здесь одно: внутри стоит дерево, где часть групп выбирает один\n * лист. Узлы — `IRtHybridTree.Node`, признак `single` доходит до дерева нетронутым.\n */\n@Component({\n selector: 'rt-hybrid-tree-selector',\n templateUrl: '../tree-selector/rt-tree-selector.component.html',\n styleUrl: '../tree-selector/rt-tree-selector.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n FormsModule,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtButtonDirective,\n RtIconComponent,\n RtInputComponent,\n RtToggleSwitchComponent,\n RtTooltipDirective,\n RtTreeComponent,\n RtHybridTreeComponent,\n ],\n host: { class: BEM_BLOCK },\n})\nexport class RtHybridTreeSelectorComponent<TValue> extends RtTreeSelectorComponent<TValue> {\n protected override readonly hybrid: boolean = true;\n}\n","<ng-container rtBlock=\"rt-tree-selector\">\n <div rtElem=\"head\">\n @if (label()) {\n <div rtElem=\"label\" qa-dataid=\"tree-selector-label\">{{ label() }}</div>\n }\n\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"tree-selector-search\"\n iconLeft=\"search\"\n [disabled]=\"disabled()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [ngModel]=\"term()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"onTerm($event)\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (underSearchTpl(); as underSearch) {\n <div rtElem=\"under-search\" qa-dataid=\"tree-selector-under-search\">\n <ng-container [ngTemplateOutlet]=\"underSearch.templateRef\" />\n </div>\n }\n\n <div rtElem=\"controls\">\n <div rtElem=\"row\">\n @if (isExpandShown()) {\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-expand-all\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"secondary\"\n size=\"sm\"\n icon=\"expand-all\"\n [rtTooltip]=\"expandAllLabel()\"\n [attr.aria-label]=\"expandAllLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"expandAll()\"></button>\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-collapse-all\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"secondary\"\n size=\"sm\"\n icon=\"collapse-all\"\n [rtTooltip]=\"collapseAllLabel()\"\n [attr.aria-label]=\"collapseAllLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"collapseAll()\"></button>\n }\n @if (isRevertShown()) {\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-revert\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"secondary\"\n size=\"sm\"\n icon=\"undo\"\n [rtTooltip]=\"revertLabel()\"\n [attr.aria-label]=\"revertLabel()\"\n [disabled]=\"disabled() || !isDraftChanged()\"\n (click)=\"onRevert()\"></button>\n }\n @if (isClearShown()) {\n <button\n rtButton\n rtElem=\"control\"\n qa-dataid=\"tree-selector-clear\"\n type=\"button\"\n appearance=\"outlined\"\n theme=\"danger\"\n size=\"sm\"\n icon=\"trash\"\n [rtTooltip]=\"clearLabel()\"\n [attr.aria-label]=\"clearLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"onClear()\"></button>\n }\n @if (controlsTpl(); as controls) {\n <div rtElem=\"own\">\n <ng-container [ngTemplateOutlet]=\"controls.templateRef\" />\n </div>\n }\n </div>\n\n @if (isMultiToggleShown()) {\n <div rtElem=\"multi\">\n <rt-toggle-switch\n qa-dataid=\"tree-selector-multi\"\n [ariaLabel]=\"multiLabel()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"multiOn()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"onMultiToggle($event)\" />\n <span rtElem=\"multi-label\">{{ multiLabel() }}</span>\n <rt-icon rtElem=\"multi-hint-icon\" name=\"info-circle\" size=\"sm\" [rtTooltip]=\"multiHintLabel()\" />\n </div>\n }\n </div>\n </div>\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"tree-selector-empty\" [rtMod]=\"{ 'with-icon': !!emptyIcon() }\">\n @if (emptyIcon(); as icon) {\n <rt-icon rtElem=\"empty-icon\" qa-dataid=\"tree-selector-empty-icon\" size=\"lg\" [name]=\"icon\" />\n }\n {{ nothingFoundLabel() }}\n </div>\n } @else {\n @if (hybrid) {\n <rt-hybrid-tree\n rtElem=\"tree\"\n qa-dataid=\"tree-selector-tree\"\n [nodes]=\"shownNodes()\"\n [value]=\"draft()\"\n [mode]=\"mode()\"\n [cascade]=\"cascade()\"\n [branchMarks]=\"branchMarks()\"\n [exclusive]=\"isExclusive()\"\n [showSelectAll]=\"selectAll()\"\n [searchTerm]=\"markTerm()\"\n [filter]=\"false\"\n [emptyIcon]=\"emptyIcon()\"\n [nodeEndTemplate]=\"endTemplate()\"\n [nodeMetaTemplate]=\"metaTemplate()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTreeChange($event)\"\n (picked)=\"onPicked()\" />\n } @else {\n <rt-tree\n rtElem=\"tree\"\n qa-dataid=\"tree-selector-tree\"\n [nodes]=\"shownNodes()\"\n [value]=\"draft()\"\n [mode]=\"mode()\"\n [cascade]=\"cascade()\"\n [branchMarks]=\"branchMarks()\"\n [exclusive]=\"isExclusive()\"\n [showSelectAll]=\"selectAll()\"\n [searchTerm]=\"markTerm()\"\n [filter]=\"false\"\n [emptyIcon]=\"emptyIcon()\"\n [nodeEndTemplate]=\"endTemplate()\"\n [nodeMetaTemplate]=\"metaTemplate()\"\n [ariaLabel]=\"ariaLabel()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTreeChange($event)\"\n (picked)=\"onPicked()\" />\n }\n }\n\n @if (isFooterShown()) {\n <div rtElem=\"foot\">\n <button\n rtButton\n qa-dataid=\"tree-selector-cancel\"\n type=\"button\"\n appearance=\"text\"\n [label]=\"cancelLabel()\"\n [attr.aria-label]=\"cancelLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"cancel()\"></button>\n <button\n rtButton\n qa-dataid=\"tree-selector-apply\"\n type=\"button\"\n [label]=\"applyLabel()\"\n [attr.aria-label]=\"applyLabel()\"\n [disabled]=\"!canApply()\"\n (click)=\"apply()\"></button>\n </div>\n }\n</ng-container>\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAM,YAAoB;AA+B1B,IAAa,gCAAb,MAAa,sCAA8C,wBAA+B;CAtB1F,cAAA;;EAuBgC,KAAA,SAAkB;CACjD;CAFY;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAA6B,MAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAA7B;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAA6B,cAAA;GAAA,UAAA;GAAA,MAAA,EAAA,gBAAA,mBAAA;GAAA,iBAAA;GAAA,UAAA;GAAA,UChD1C;GAoLA,QAAA,CAAA,g/DAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MDnJQ;KAAgB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAChB;IAAW;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,eAAA;KAAA,UAAA,CAAA,SAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACX;KAAc,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACd;KAAa,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACb;KAAY,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACZ;KAAiB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACjB;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAgB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,iBAAA,gBAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAChB;KAAuB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACvB;KAAkB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,UAAA,CAAA,WAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAClB;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,eAAA,QAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAqB,UAAA;IAAA;GAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAIhB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA6B,YAAA,CAAA;EAtBzC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAyB,iBAGlB,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IACL;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACH;GAAA,MACK,EAAE,OAAO,UAAS;GAAE,UAAA;GAAA,QAAA,CAAA,g/DAAA;EAAA,CAAA"}
@@ -270,7 +270,8 @@ var RtHybridTreeComponent = class RtHybridTreeComponent extends RtTreeComponent
270
270
  "rtTooltipPlacement",
271
271
  "rtTooltipShowDelay",
272
272
  "rtTooltipWhenTruncated",
273
- "rtTooltipHideOnClick"
273
+ "rtTooltipHideOnClick",
274
+ "rtTooltipShowOnTap"
274
275
  ],
275
276
  exportAs: ["rtTooltip"]
276
277
  },
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-hybrid-tree.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/hybrid-tree/rt-hybrid-tree.logic.ts","../../../projects/ui-kit-v2/src/lib/components/hybrid-tree/rt-hybrid-tree.component.ts","../../../projects/ui-kit-v2/src/lib/components/tree/rt-tree.component.html"],"sourcesContent":["import { rtTreeLeaves } from '@rt-tools/ui-kit-v2/core';\nimport { rtTreeAloneBase, rtTreeChoose, rtTreeChooseAlone, rtTreeMark, rtTreeVisibleLeaves } from '@rt-tools/ui-kit-v2/tree';\nimport { IRtTree } from '@rt-tools/ui-kit-v2/tree';\nimport { IRtHybridTree } from './rt-hybrid-tree.model';\n\n/**\n * Расчёт выбора `rt-hybrid-tree` — чистые функции над неизменяемыми узлами.\n *\n * Всё, что у гибридного дерева общее с `rt-tree`, считает модуль `rt-tree`: здесь только правила\n * группы, где выбирается один лист. Её листья не набираются «выбрать всё» и каскадом ветки, а\n * выбор одного снимает соседей по группе. Каждая функция отвечает новым массивом выбора.\n */\n\ntype TNode<TValue> = IRtHybridTree.Node<TValue>;\n\nfunction childrenOf<TValue>(node: TNode<TValue>): ReadonlyArray<TNode<TValue>> {\n return node.children ?? [];\n}\n\nfunction isBranch<TValue>(node: TNode<TValue>): boolean {\n return childrenOf(node).length > 0;\n}\n\n/** Ветка, в которой выбирается один прямой лист. */\nexport function rtHybridTreeIsSingleGroup<TValue>(node: TNode<TValue>): boolean {\n return !!node.single && isBranch(node);\n}\n\nfunction without<TValue>(value: ReadonlyArray<TValue>, drop: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n return value.filter((item: TValue): boolean => !drop.includes(item));\n}\n\nfunction withAll<TValue>(value: ReadonlyArray<TValue>, add: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n return [...value, ...add.filter((item: TValue): boolean => !value.includes(item))];\n}\n\n/** Прямые листья группы — и включённые, и выключенные. */\nfunction directLeaves<TValue>(group: TNode<TValue>): ReadonlyArray<TNode<TValue>> {\n return childrenOf(group).filter((child: TNode<TValue>): boolean => !isBranch(child));\n}\n\n/** Все листья под узлом, выключенные тоже: счёт выбранного видит и их. */\nfunction allLeaves<TValue>(node: TNode<TValue>): ReadonlyArray<TValue> {\n return isBranch(node) ? childrenOf(node).flatMap((child: TNode<TValue>): ReadonlyArray<TValue> => allLeaves(child)) : [node.value];\n}\n\nfunction collectSingles<TValue>(nodes: ReadonlyArray<TNode<TValue>>, into: Set<TValue>): void {\n nodes.forEach((node: TNode<TValue>): void => {\n if (rtHybridTreeIsSingleGroup(node)) {\n directLeaves(node).forEach((leaf: TNode<TValue>): void => {\n into.add(leaf.value);\n });\n }\n collectSingles(childrenOf(node), into);\n });\n}\n\n/** Листья групп, где выбирается один: их не набирают ни «выбрать всё», ни каскад ветки. */\nexport function rtHybridTreeSingleLeaves<TValue>(nodes: ReadonlyArray<TNode<TValue>>): ReadonlySet<TValue> {\n const singles: Set<TValue> = new Set<TValue>();\n collectSingles(nodes, singles);\n return singles;\n}\n\n/** Группа «один лист», прямым листом которой стоит значение. */\nexport function rtHybridTreeGroupOf<TValue>(nodes: ReadonlyArray<TNode<TValue>>, value: TValue): TNode<TValue> | null {\n for (const node of nodes) {\n if (rtHybridTreeIsSingleGroup(node) && directLeaves(node).some((leaf: TNode<TValue>): boolean => leaf.value === value)) {\n return node;\n }\n const found: TNode<TValue> | null = rtHybridTreeGroupOf(childrenOf(node), value);\n if (found) {\n return found;\n }\n }\n return null;\n}\n\nfunction freeLeaves<TValue>(node: TNode<TValue>, singles: ReadonlySet<TValue>): ReadonlyArray<TValue> {\n return rtTreeLeaves(node).filter((leaf: TValue): boolean => !singles.has(leaf));\n}\n\n/**\n * Отметка строки. Группа «один лист» отмечена, пока выбран любой её лист. Прочая ветка в каскаде\n * отмечена по своим свободным листьям; без выбранных свободных — частью, пока под ней что-то выбрано.\n */\nexport function rtHybridTreeMark<TValue>(\n node: TNode<TValue>,\n value: ReadonlyArray<TValue>,\n cascade: boolean,\n singles: ReadonlySet<TValue>\n): IRtTree.Mark {\n if (rtHybridTreeIsSingleGroup(node)) {\n return directLeaves(node).some((leaf: TNode<TValue>): boolean => value.includes(leaf.value)) ? 'all' : 'none';\n }\n if (!cascade || !isBranch(node)) {\n return rtTreeMark(node, value, cascade);\n }\n const free: ReadonlyArray<TValue> = freeLeaves(node, singles);\n const count: number = free.filter((leaf: TValue): boolean => value.includes(leaf)).length;\n if (count > 0) {\n return count === free.length ? 'all' : 'some';\n }\n return allLeaves(node).some((leaf: TValue): boolean => value.includes(leaf)) ? 'some' : 'none';\n}\n\n/** Клик по листу группы «один лист»: лист выбирается вместо соседей, выбранный снимается. */\nfunction chooseSingleLeaf<TValue>(group: TNode<TValue>, leaf: TValue, value: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n if (value.includes(leaf)) {\n return without(value, [leaf]);\n }\n const siblings: ReadonlyArray<TValue> = directLeaves(group)\n .filter((child: TNode<TValue>): boolean => !child.disabled && child.value !== leaf)\n .map((child: TNode<TValue>): TValue => child.value);\n return withAll(without(value, siblings), [leaf]);\n}\n\n/** Радио группы «один лист»: снимает выбор группы или выбирает её первый включённый лист. */\nfunction chooseSingleGroup<TValue>(group: TNode<TValue>, value: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n const enabled: ReadonlyArray<TValue> = directLeaves(group)\n .filter((child: TNode<TValue>): boolean => !child.disabled)\n .map((child: TNode<TValue>): TValue => child.value);\n if (directLeaves(group).some((child: TNode<TValue>): boolean => value.includes(child.value))) {\n return without(value, enabled);\n }\n return enabled.length > 0 ? withAll(value, [enabled[0]]) : value;\n}\n\n/** Каскад ветки: добавляет свободные листья, а когда добавлять нечего — снимает все, листья групп «один лист» тоже. */\nfunction chooseBranch<TValue>(node: TNode<TValue>, value: ReadonlyArray<TValue>, singles: ReadonlySet<TValue>): ReadonlyArray<TValue> {\n const free: ReadonlyArray<TValue> = freeLeaves(node, singles);\n if (free.length === 0 || rtHybridTreeMark(node, value, true, singles) === 'all') {\n return without(value, rtTreeLeaves(node));\n }\n return withAll(value, free);\n}\n\n/** Выбор после клика в режиме флажков, без правила одного узла на клик. */\nfunction chooseMultiple<TValue>(\n nodes: ReadonlyArray<TNode<TValue>>,\n node: TNode<TValue>,\n value: ReadonlyArray<TValue>,\n cascade: boolean,\n singles: ReadonlySet<TValue>\n): ReadonlyArray<TValue> {\n if (rtHybridTreeIsSingleGroup(node)) {\n return chooseSingleGroup(node, value);\n }\n const group: TNode<TValue> | null = singles.has(node.value) ? rtHybridTreeGroupOf(nodes, node.value) : null;\n if (group) {\n return chooseSingleLeaf(group, node.value, value);\n }\n if (cascade && isBranch(node)) {\n return chooseBranch(node, value, singles);\n }\n return rtTreeChoose(node, value, 'multiple', cascade);\n}\n\n/**\n * Выбор после клика по узлу. В режиме флажков действуют правила групп «один лист»: клик без Ctrl и\n * Cmd в исключающем режиме оставляет свободный узел одним, а лист группы «один лист» выбор вне\n * группы не трогает. Остальные режимы считает `rt-tree`.\n */\nexport function rtHybridTreeChoose<TValue>(\n nodes: ReadonlyArray<TNode<TValue>>,\n node: TNode<TValue>,\n value: ReadonlyArray<TValue>,\n options: IRtHybridTree.ChooseOptions\n): ReadonlyArray<TValue> {\n if (node.disabled) {\n return value;\n }\n if (options.mode !== 'multiple') {\n return rtTreeChoose(node, value, options.mode, options.cascade);\n }\n const singles: ReadonlySet<TValue> = rtHybridTreeSingleLeaves(nodes);\n if (!options.alone || singles.has(node.value)) {\n return chooseMultiple(nodes, node, value, options.cascade, singles);\n }\n if (singles.size === 0) {\n return rtTreeChooseAlone(nodes, node, value, options.cascade);\n }\n return chooseMultiple(nodes, node, rtTreeAloneBase(nodes, node, value), options.cascade, singles);\n}\n\n/** Отметка «выбрать всё» по свободным листьям видимых строк. */\nexport function rtHybridTreeSelectAllMark<TValue>(\n rows: ReadonlyArray<IRtTree.Row<TValue>>,\n value: ReadonlyArray<TValue>,\n singles: ReadonlySet<TValue>\n): IRtTree.Mark {\n const free: ReadonlyArray<TValue> = rtTreeVisibleLeaves(rows).filter((leaf: TValue): boolean => !singles.has(leaf));\n const count: number = free.filter((leaf: TValue): boolean => value.includes(leaf)).length;\n if (count === 0) {\n return 'none';\n }\n return count === free.length ? 'all' : 'some';\n}\n\n/**\n * Выбор после клика по «выбрать всё»: свободные листья видимых строк добавляются, листья групп\n * «один лист» остаются как были. Когда выбраны все свободные — снимаются все видимые листья.\n */\nexport function rtHybridTreeSelectAll<TValue>(\n rows: ReadonlyArray<IRtTree.Row<TValue>>,\n value: ReadonlyArray<TValue>,\n singles: ReadonlySet<TValue>\n): ReadonlyArray<TValue> {\n const leaves: ReadonlyArray<TValue> = rtTreeVisibleLeaves(rows);\n if (rtHybridTreeSelectAllMark(rows, value, singles) === 'all') {\n return without(value, leaves);\n }\n return withAll(\n value,\n leaves.filter((leaf: TValue): boolean => !singles.has(leaf))\n );\n}\n\n/** Сколько листьев под узлом выбрано. У листа счёта нет. */\nexport function rtHybridTreeChosenCount<TValue>(node: TNode<TValue>, value: ReadonlyArray<TValue>): number {\n if (!isBranch(node)) {\n return 0;\n }\n return allLeaves(node).filter((leaf: TValue): boolean => value.includes(leaf)).length;\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { computed, ChangeDetectionStrategy, Component, Signal, ViewEncapsulation } from '@angular/core';\n\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtCheckboxComponent } from '@rt-tools/ui-kit-v2/checkbox';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtRadioButtonComponent } from '@rt-tools/ui-kit-v2/radio-button';\nimport { RtTagComponent } from '@rt-tools/ui-kit-v2/tag';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtTreeComponent } from '@rt-tools/ui-kit-v2/tree';\nimport { IRtTree } from '@rt-tools/ui-kit-v2/tree';\nimport {\n rtHybridTreeChoose,\n rtHybridTreeChosenCount,\n rtHybridTreeIsSingleGroup,\n rtHybridTreeMark,\n rtHybridTreeSelectAll,\n rtHybridTreeSelectAllMark,\n rtHybridTreeSingleLeaves,\n} from './rt-hybrid-tree.logic';\n\n/** Блок разметки — блок `rt-tree`: разметка и стили у деревьев одни, отличается только расчёт выбора. */\nconst BEM_BLOCK: string = 'rt-tree';\n\n/**\n * Дерево выбора, где часть групп выбирает один лист.\n *\n * Строки, клавиши, поиск, метки и разметку приложения берёт у `rt-tree` — та же разметка и те же\n * стили. Своё здесь только то, как считается выбор: группа с `single` держит один прямой лист, её\n * листья не набираются ни «выбрать всё», ни каскадом ветки, а группа без отметки показывает число\n * выбранного под ней. Узлы — `IRtHybridTree.Node`.\n */\n@Component({\n selector: 'rt-hybrid-tree',\n templateUrl: '../tree/rt-tree.component.html',\n styleUrl: '../tree/rt-tree.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n FormsModule,\n RtCheckboxComponent,\n RtIconComponent,\n RtRadioButtonComponent,\n RtTagComponent,\n RtTooltipDirective,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n host: {\n class: BEM_BLOCK,\n role: 'tree',\n '[attr.tabindex]': 'disabled() ? -1 : 0',\n '[attr.aria-disabled]': 'disabled() || null',\n '[attr.aria-label]': 'ariaLabel()',\n '[attr.aria-multiselectable]': \"mode() === 'multiple'\",\n '(keydown)': 'handleKeydown($event)',\n },\n})\nexport class RtHybridTreeComponent<TValue> extends RtTreeComponent<TValue> {\n /** Листья групп «один лист». */\n protected readonly singles: Signal<ReadonlySet<TValue>> = computed((): ReadonlySet<TValue> => rtHybridTreeSingleLeaves(this.nodes()));\n\n protected override rowState(row: IRtTree.Row<TValue>, value: ReadonlyArray<TValue>, cascade: boolean): IRtTree.RowState {\n const node: IRtTree.Node<TValue> = row.option;\n const single: boolean = this.singles().has(node.value) || rtHybridTreeIsSingleGroup(node);\n const count: number = rtHybridTreeChosenCount(node, value);\n return {\n mark: rtHybridTreeMark(node, value, cascade, this.singles()),\n radio: this.mode() === 'single' || (this.mode() === 'multiple' && single),\n count: count > 0 ? count : null,\n };\n }\n\n protected override selectAllMarkOf(rows: ReadonlyArray<IRtTree.Row<TValue>>, value: ReadonlyArray<TValue>): IRtTree.Mark {\n return rtHybridTreeSelectAllMark(rows, value, this.singles());\n }\n\n protected override selectAllChoice(rows: ReadonlyArray<IRtTree.Row<TValue>>, value: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n return rtHybridTreeSelectAll(rows, value, this.singles());\n }\n\n protected override nextChoice(node: IRtTree.Node<TValue>, additive: boolean): ReadonlyArray<TValue> {\n return rtHybridTreeChoose(this.nodes(), node, this.value(), {\n mode: this.mode(),\n cascade: this.cascade(),\n alone: this.exclusive() && !additive,\n });\n }\n}\n","<ng-container rtBlock=\"rt-tree\">\n <!--\n Строки выбираются мышью через (click); клавиатура — на самом дереве (handleKeydown), строки\n по отдельности фокус не берут: подсветка — третье состояние рядом с фокусом и выбором.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n @if (isSelectAllShown()) {\n <div\n rtElem=\"select-all\"\n qa-dataid=\"tree-select-all\"\n role=\"presentation\"\n [rtMod]=\"{ disabled: disabled(), 'over-leaf': isSelectAllOverLeaf() }\"\n (click)=\"onSelectAll()\">\n @if (isSelectAllUnderMark()) {\n <span rtElem=\"toggle-space\"></span>\n }\n <rt-checkbox\n rtElem=\"check\"\n inert\n [disabled]=\"disabled()\"\n [indeterminate]=\"selectAllMark() === 'some'\"\n [ngModel]=\"selectAllMark() === 'all'\"\n [ngModelOptions]=\"{ standalone: true }\" />\n <span rtElem=\"label\">{{ t().uiSelectAll }}</span>\n </div>\n }\n\n @for (view of views(); track view.row.option.value) {\n <div\n rtElem=\"row\"\n qa-dataid=\"tree-row\"\n role=\"treeitem\"\n [attr.data-value]=\"view.row.option.value\"\n [attr.aria-level]=\"view.row.level + 1\"\n [attr.aria-expanded]=\"view.row.branch ? view.row.open : null\"\n [attr.aria-selected]=\"view.marked ? view.mark === 'all' : null\"\n [attr.aria-disabled]=\"view.disabled\"\n [style.--rt-tree-level]=\"view.row.level\"\n [rtMod]=\"{ highlighted: view.highlighted, disabled: view.disabled, chosen: view.mark === 'all' }\"\n (click)=\"onRowClick(view.row, $event)\">\n @if (view.row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"tree-row-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"view.row.open ? t().uiCollapse : t().uiExpand\"\n (click)=\"onToggleClick($event, view.row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"view.row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n\n @if (view.marked && !view.radio) {\n <rt-checkbox\n rtElem=\"check\"\n qa-dataid=\"tree-row-checkbox\"\n inert\n [disabled]=\"view.disabled\"\n [indeterminate]=\"view.mark === 'some'\"\n [ngModel]=\"view.mark === 'all'\"\n [ngModelOptions]=\"{ standalone: true }\" />\n } @else if (view.marked) {\n <rt-radio-button\n rtElem=\"check\"\n qa-dataid=\"tree-row-radio\"\n inert\n [value]=\"view.row.option.value\"\n [checked]=\"view.mark === 'all'\"\n [disabled]=\"view.disabled\" />\n }\n\n <span rtElem=\"text\">\n <span rtElem=\"label\" qa-dataid=\"tree-row-label\" rtTooltipWhenTruncated [rtTooltip]=\"view.row.option.label\">\n @for (part of view.parts.label; track $index) {\n <span rtElem=\"part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n @if (view.parts.description.length > 0) {\n <span\n rtElem=\"description\"\n qa-dataid=\"tree-row-description\"\n rtTooltipWhenTruncated\n [rtTooltip]=\"view.row.option.description\">\n @for (part of view.parts.description; track $index) {\n <span rtElem=\"part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n }\n @if (view.row.option.badges?.length || metaTemplate()) {\n <span rtElem=\"meta\" qa-dataid=\"tree-row-meta\">\n @for (badge of view.row.option.badges; track $index) {\n <rt-tag size=\"sm\" [value]=\"badge.text\" [severity]=\"badge.severity ?? 'neutral'\" [highlight]=\"searchTerm()\" />\n }\n @if (metaTemplate(); as meta) {\n <ng-container [ngTemplateOutlet]=\"meta\" [ngTemplateOutletContext]=\"{ $implicit: view.row.option }\" />\n }\n </span>\n }\n </span>\n\n @if (view.count) {\n <span rtElem=\"count\" qa-dataid=\"tree-row-count\">{{ view.count }}</span>\n }\n\n @if (endTemplate(); as end) {\n <span rtElem=\"end\" qa-dataid=\"tree-row-end\">\n <ng-container [ngTemplateOutlet]=\"end\" [ngTemplateOutletContext]=\"{ $implicit: view.row.option }\" />\n </span>\n }\n </div>\n } @empty {\n <div rtElem=\"empty\" qa-dataid=\"tree-empty\" [rtMod]=\"{ 'with-icon': !!emptyIcon() }\">\n @if (emptyIcon(); as icon) {\n <rt-icon rtElem=\"empty-icon\" qa-dataid=\"tree-empty-icon\" size=\"lg\" [name]=\"icon\" />\n }\n {{ emptyText() }}\n </div>\n }\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-hybrid-tree.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/hybrid-tree/rt-hybrid-tree.logic.ts","../../../projects/ui-kit-v2/src/lib/components/hybrid-tree/rt-hybrid-tree.component.ts","../../../projects/ui-kit-v2/src/lib/components/tree/rt-tree.component.html"],"sourcesContent":["import { rtTreeLeaves } from '@rt-tools/ui-kit-v2/core';\nimport { rtTreeAloneBase, rtTreeChoose, rtTreeChooseAlone, rtTreeMark, rtTreeVisibleLeaves } from '@rt-tools/ui-kit-v2/tree';\nimport { IRtTree } from '@rt-tools/ui-kit-v2/tree';\nimport { IRtHybridTree } from './rt-hybrid-tree.model';\n\n/**\n * Расчёт выбора `rt-hybrid-tree` — чистые функции над неизменяемыми узлами.\n *\n * Всё, что у гибридного дерева общее с `rt-tree`, считает модуль `rt-tree`: здесь только правила\n * группы, где выбирается один лист. Её листья не набираются «выбрать всё» и каскадом ветки, а\n * выбор одного снимает соседей по группе. Каждая функция отвечает новым массивом выбора.\n */\n\ntype TNode<TValue> = IRtHybridTree.Node<TValue>;\n\nfunction childrenOf<TValue>(node: TNode<TValue>): ReadonlyArray<TNode<TValue>> {\n return node.children ?? [];\n}\n\nfunction isBranch<TValue>(node: TNode<TValue>): boolean {\n return childrenOf(node).length > 0;\n}\n\n/** Ветка, в которой выбирается один прямой лист. */\nexport function rtHybridTreeIsSingleGroup<TValue>(node: TNode<TValue>): boolean {\n return !!node.single && isBranch(node);\n}\n\nfunction without<TValue>(value: ReadonlyArray<TValue>, drop: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n return value.filter((item: TValue): boolean => !drop.includes(item));\n}\n\nfunction withAll<TValue>(value: ReadonlyArray<TValue>, add: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n return [...value, ...add.filter((item: TValue): boolean => !value.includes(item))];\n}\n\n/** Прямые листья группы — и включённые, и выключенные. */\nfunction directLeaves<TValue>(group: TNode<TValue>): ReadonlyArray<TNode<TValue>> {\n return childrenOf(group).filter((child: TNode<TValue>): boolean => !isBranch(child));\n}\n\n/** Все листья под узлом, выключенные тоже: счёт выбранного видит и их. */\nfunction allLeaves<TValue>(node: TNode<TValue>): ReadonlyArray<TValue> {\n return isBranch(node) ? childrenOf(node).flatMap((child: TNode<TValue>): ReadonlyArray<TValue> => allLeaves(child)) : [node.value];\n}\n\nfunction collectSingles<TValue>(nodes: ReadonlyArray<TNode<TValue>>, into: Set<TValue>): void {\n nodes.forEach((node: TNode<TValue>): void => {\n if (rtHybridTreeIsSingleGroup(node)) {\n directLeaves(node).forEach((leaf: TNode<TValue>): void => {\n into.add(leaf.value);\n });\n }\n collectSingles(childrenOf(node), into);\n });\n}\n\n/** Листья групп, где выбирается один: их не набирают ни «выбрать всё», ни каскад ветки. */\nexport function rtHybridTreeSingleLeaves<TValue>(nodes: ReadonlyArray<TNode<TValue>>): ReadonlySet<TValue> {\n const singles: Set<TValue> = new Set<TValue>();\n collectSingles(nodes, singles);\n return singles;\n}\n\n/** Группа «один лист», прямым листом которой стоит значение. */\nexport function rtHybridTreeGroupOf<TValue>(nodes: ReadonlyArray<TNode<TValue>>, value: TValue): TNode<TValue> | null {\n for (const node of nodes) {\n if (rtHybridTreeIsSingleGroup(node) && directLeaves(node).some((leaf: TNode<TValue>): boolean => leaf.value === value)) {\n return node;\n }\n const found: TNode<TValue> | null = rtHybridTreeGroupOf(childrenOf(node), value);\n if (found) {\n return found;\n }\n }\n return null;\n}\n\nfunction freeLeaves<TValue>(node: TNode<TValue>, singles: ReadonlySet<TValue>): ReadonlyArray<TValue> {\n return rtTreeLeaves(node).filter((leaf: TValue): boolean => !singles.has(leaf));\n}\n\n/**\n * Отметка строки. Группа «один лист» отмечена, пока выбран любой её лист. Прочая ветка в каскаде\n * отмечена по своим свободным листьям; без выбранных свободных — частью, пока под ней что-то выбрано.\n */\nexport function rtHybridTreeMark<TValue>(\n node: TNode<TValue>,\n value: ReadonlyArray<TValue>,\n cascade: boolean,\n singles: ReadonlySet<TValue>\n): IRtTree.Mark {\n if (rtHybridTreeIsSingleGroup(node)) {\n return directLeaves(node).some((leaf: TNode<TValue>): boolean => value.includes(leaf.value)) ? 'all' : 'none';\n }\n if (!cascade || !isBranch(node)) {\n return rtTreeMark(node, value, cascade);\n }\n const free: ReadonlyArray<TValue> = freeLeaves(node, singles);\n const count: number = free.filter((leaf: TValue): boolean => value.includes(leaf)).length;\n if (count > 0) {\n return count === free.length ? 'all' : 'some';\n }\n return allLeaves(node).some((leaf: TValue): boolean => value.includes(leaf)) ? 'some' : 'none';\n}\n\n/** Клик по листу группы «один лист»: лист выбирается вместо соседей, выбранный снимается. */\nfunction chooseSingleLeaf<TValue>(group: TNode<TValue>, leaf: TValue, value: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n if (value.includes(leaf)) {\n return without(value, [leaf]);\n }\n const siblings: ReadonlyArray<TValue> = directLeaves(group)\n .filter((child: TNode<TValue>): boolean => !child.disabled && child.value !== leaf)\n .map((child: TNode<TValue>): TValue => child.value);\n return withAll(without(value, siblings), [leaf]);\n}\n\n/** Радио группы «один лист»: снимает выбор группы или выбирает её первый включённый лист. */\nfunction chooseSingleGroup<TValue>(group: TNode<TValue>, value: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n const enabled: ReadonlyArray<TValue> = directLeaves(group)\n .filter((child: TNode<TValue>): boolean => !child.disabled)\n .map((child: TNode<TValue>): TValue => child.value);\n if (directLeaves(group).some((child: TNode<TValue>): boolean => value.includes(child.value))) {\n return without(value, enabled);\n }\n return enabled.length > 0 ? withAll(value, [enabled[0]]) : value;\n}\n\n/** Каскад ветки: добавляет свободные листья, а когда добавлять нечего — снимает все, листья групп «один лист» тоже. */\nfunction chooseBranch<TValue>(node: TNode<TValue>, value: ReadonlyArray<TValue>, singles: ReadonlySet<TValue>): ReadonlyArray<TValue> {\n const free: ReadonlyArray<TValue> = freeLeaves(node, singles);\n if (free.length === 0 || rtHybridTreeMark(node, value, true, singles) === 'all') {\n return without(value, rtTreeLeaves(node));\n }\n return withAll(value, free);\n}\n\n/** Выбор после клика в режиме флажков, без правила одного узла на клик. */\nfunction chooseMultiple<TValue>(\n nodes: ReadonlyArray<TNode<TValue>>,\n node: TNode<TValue>,\n value: ReadonlyArray<TValue>,\n cascade: boolean,\n singles: ReadonlySet<TValue>\n): ReadonlyArray<TValue> {\n if (rtHybridTreeIsSingleGroup(node)) {\n return chooseSingleGroup(node, value);\n }\n const group: TNode<TValue> | null = singles.has(node.value) ? rtHybridTreeGroupOf(nodes, node.value) : null;\n if (group) {\n return chooseSingleLeaf(group, node.value, value);\n }\n if (cascade && isBranch(node)) {\n return chooseBranch(node, value, singles);\n }\n return rtTreeChoose(node, value, 'multiple', cascade);\n}\n\n/**\n * Выбор после клика по узлу. В режиме флажков действуют правила групп «один лист»: клик без Ctrl и\n * Cmd в исключающем режиме оставляет свободный узел одним, а лист группы «один лист» выбор вне\n * группы не трогает. Остальные режимы считает `rt-tree`.\n */\nexport function rtHybridTreeChoose<TValue>(\n nodes: ReadonlyArray<TNode<TValue>>,\n node: TNode<TValue>,\n value: ReadonlyArray<TValue>,\n options: IRtHybridTree.ChooseOptions\n): ReadonlyArray<TValue> {\n if (node.disabled) {\n return value;\n }\n if (options.mode !== 'multiple') {\n return rtTreeChoose(node, value, options.mode, options.cascade);\n }\n const singles: ReadonlySet<TValue> = rtHybridTreeSingleLeaves(nodes);\n if (!options.alone || singles.has(node.value)) {\n return chooseMultiple(nodes, node, value, options.cascade, singles);\n }\n if (singles.size === 0) {\n return rtTreeChooseAlone(nodes, node, value, options.cascade);\n }\n return chooseMultiple(nodes, node, rtTreeAloneBase(nodes, node, value), options.cascade, singles);\n}\n\n/** Отметка «выбрать всё» по свободным листьям видимых строк. */\nexport function rtHybridTreeSelectAllMark<TValue>(\n rows: ReadonlyArray<IRtTree.Row<TValue>>,\n value: ReadonlyArray<TValue>,\n singles: ReadonlySet<TValue>\n): IRtTree.Mark {\n const free: ReadonlyArray<TValue> = rtTreeVisibleLeaves(rows).filter((leaf: TValue): boolean => !singles.has(leaf));\n const count: number = free.filter((leaf: TValue): boolean => value.includes(leaf)).length;\n if (count === 0) {\n return 'none';\n }\n return count === free.length ? 'all' : 'some';\n}\n\n/**\n * Выбор после клика по «выбрать всё»: свободные листья видимых строк добавляются, листья групп\n * «один лист» остаются как были. Когда выбраны все свободные — снимаются все видимые листья.\n */\nexport function rtHybridTreeSelectAll<TValue>(\n rows: ReadonlyArray<IRtTree.Row<TValue>>,\n value: ReadonlyArray<TValue>,\n singles: ReadonlySet<TValue>\n): ReadonlyArray<TValue> {\n const leaves: ReadonlyArray<TValue> = rtTreeVisibleLeaves(rows);\n if (rtHybridTreeSelectAllMark(rows, value, singles) === 'all') {\n return without(value, leaves);\n }\n return withAll(\n value,\n leaves.filter((leaf: TValue): boolean => !singles.has(leaf))\n );\n}\n\n/** Сколько листьев под узлом выбрано. У листа счёта нет. */\nexport function rtHybridTreeChosenCount<TValue>(node: TNode<TValue>, value: ReadonlyArray<TValue>): number {\n if (!isBranch(node)) {\n return 0;\n }\n return allLeaves(node).filter((leaf: TValue): boolean => value.includes(leaf)).length;\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { computed, ChangeDetectionStrategy, Component, Signal, ViewEncapsulation } from '@angular/core';\n\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtCheckboxComponent } from '@rt-tools/ui-kit-v2/checkbox';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtRadioButtonComponent } from '@rt-tools/ui-kit-v2/radio-button';\nimport { RtTagComponent } from '@rt-tools/ui-kit-v2/tag';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtTreeComponent } from '@rt-tools/ui-kit-v2/tree';\nimport { IRtTree } from '@rt-tools/ui-kit-v2/tree';\nimport {\n rtHybridTreeChoose,\n rtHybridTreeChosenCount,\n rtHybridTreeIsSingleGroup,\n rtHybridTreeMark,\n rtHybridTreeSelectAll,\n rtHybridTreeSelectAllMark,\n rtHybridTreeSingleLeaves,\n} from './rt-hybrid-tree.logic';\n\n/** Блок разметки — блок `rt-tree`: разметка и стили у деревьев одни, отличается только расчёт выбора. */\nconst BEM_BLOCK: string = 'rt-tree';\n\n/**\n * Дерево выбора, где часть групп выбирает один лист.\n *\n * Строки, клавиши, поиск, метки и разметку приложения берёт у `rt-tree` — та же разметка и те же\n * стили. Своё здесь только то, как считается выбор: группа с `single` держит один прямой лист, её\n * листья не набираются ни «выбрать всё», ни каскадом ветки, а группа без отметки показывает число\n * выбранного под ней. Узлы — `IRtHybridTree.Node`.\n */\n@Component({\n selector: 'rt-hybrid-tree',\n templateUrl: '../tree/rt-tree.component.html',\n styleUrl: '../tree/rt-tree.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n FormsModule,\n RtCheckboxComponent,\n RtIconComponent,\n RtRadioButtonComponent,\n RtTagComponent,\n RtTooltipDirective,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n host: {\n class: BEM_BLOCK,\n role: 'tree',\n '[attr.tabindex]': 'disabled() ? -1 : 0',\n '[attr.aria-disabled]': 'disabled() || null',\n '[attr.aria-label]': 'ariaLabel()',\n '[attr.aria-multiselectable]': \"mode() === 'multiple'\",\n '(keydown)': 'handleKeydown($event)',\n },\n})\nexport class RtHybridTreeComponent<TValue> extends RtTreeComponent<TValue> {\n /** Листья групп «один лист». */\n protected readonly singles: Signal<ReadonlySet<TValue>> = computed((): ReadonlySet<TValue> => rtHybridTreeSingleLeaves(this.nodes()));\n\n protected override rowState(row: IRtTree.Row<TValue>, value: ReadonlyArray<TValue>, cascade: boolean): IRtTree.RowState {\n const node: IRtTree.Node<TValue> = row.option;\n const single: boolean = this.singles().has(node.value) || rtHybridTreeIsSingleGroup(node);\n const count: number = rtHybridTreeChosenCount(node, value);\n return {\n mark: rtHybridTreeMark(node, value, cascade, this.singles()),\n radio: this.mode() === 'single' || (this.mode() === 'multiple' && single),\n count: count > 0 ? count : null,\n };\n }\n\n protected override selectAllMarkOf(rows: ReadonlyArray<IRtTree.Row<TValue>>, value: ReadonlyArray<TValue>): IRtTree.Mark {\n return rtHybridTreeSelectAllMark(rows, value, this.singles());\n }\n\n protected override selectAllChoice(rows: ReadonlyArray<IRtTree.Row<TValue>>, value: ReadonlyArray<TValue>): ReadonlyArray<TValue> {\n return rtHybridTreeSelectAll(rows, value, this.singles());\n }\n\n protected override nextChoice(node: IRtTree.Node<TValue>, additive: boolean): ReadonlyArray<TValue> {\n return rtHybridTreeChoose(this.nodes(), node, this.value(), {\n mode: this.mode(),\n cascade: this.cascade(),\n alone: this.exclusive() && !additive,\n });\n }\n}\n","<ng-container rtBlock=\"rt-tree\">\n <!--\n Строки выбираются мышью через (click); клавиатура — на самом дереве (handleKeydown), строки\n по отдельности фокус не берут: подсветка — третье состояние рядом с фокусом и выбором.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n @if (isSelectAllShown()) {\n <div\n rtElem=\"select-all\"\n qa-dataid=\"tree-select-all\"\n role=\"presentation\"\n [rtMod]=\"{ disabled: disabled(), 'over-leaf': isSelectAllOverLeaf() }\"\n (click)=\"onSelectAll()\">\n @if (isSelectAllUnderMark()) {\n <span rtElem=\"toggle-space\"></span>\n }\n <rt-checkbox\n rtElem=\"check\"\n inert\n [disabled]=\"disabled()\"\n [indeterminate]=\"selectAllMark() === 'some'\"\n [ngModel]=\"selectAllMark() === 'all'\"\n [ngModelOptions]=\"{ standalone: true }\" />\n <span rtElem=\"label\">{{ t().uiSelectAll }}</span>\n </div>\n }\n\n @for (view of views(); track view.row.option.value) {\n <div\n rtElem=\"row\"\n qa-dataid=\"tree-row\"\n role=\"treeitem\"\n [attr.data-value]=\"view.row.option.value\"\n [attr.aria-level]=\"view.row.level + 1\"\n [attr.aria-expanded]=\"view.row.branch ? view.row.open : null\"\n [attr.aria-selected]=\"view.marked ? view.mark === 'all' : null\"\n [attr.aria-disabled]=\"view.disabled\"\n [style.--rt-tree-level]=\"view.row.level\"\n [rtMod]=\"{ highlighted: view.highlighted, disabled: view.disabled, chosen: view.mark === 'all' }\"\n (click)=\"onRowClick(view.row, $event)\">\n @if (view.row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"tree-row-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"view.row.open ? t().uiCollapse : t().uiExpand\"\n (click)=\"onToggleClick($event, view.row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"view.row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n\n @if (view.marked && !view.radio) {\n <rt-checkbox\n rtElem=\"check\"\n qa-dataid=\"tree-row-checkbox\"\n inert\n [disabled]=\"view.disabled\"\n [indeterminate]=\"view.mark === 'some'\"\n [ngModel]=\"view.mark === 'all'\"\n [ngModelOptions]=\"{ standalone: true }\" />\n } @else if (view.marked) {\n <rt-radio-button\n rtElem=\"check\"\n qa-dataid=\"tree-row-radio\"\n inert\n [value]=\"view.row.option.value\"\n [checked]=\"view.mark === 'all'\"\n [disabled]=\"view.disabled\" />\n }\n\n <span rtElem=\"text\">\n <span rtElem=\"label\" qa-dataid=\"tree-row-label\" rtTooltipWhenTruncated [rtTooltip]=\"view.row.option.label\">\n @for (part of view.parts.label; track $index) {\n <span rtElem=\"part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n @if (view.parts.description.length > 0) {\n <span\n rtElem=\"description\"\n qa-dataid=\"tree-row-description\"\n rtTooltipWhenTruncated\n [rtTooltip]=\"view.row.option.description\">\n @for (part of view.parts.description; track $index) {\n <span rtElem=\"part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n }\n @if (view.row.option.badges?.length || metaTemplate()) {\n <span rtElem=\"meta\" qa-dataid=\"tree-row-meta\">\n @for (badge of view.row.option.badges; track $index) {\n <rt-tag size=\"sm\" [value]=\"badge.text\" [severity]=\"badge.severity ?? 'neutral'\" [highlight]=\"searchTerm()\" />\n }\n @if (metaTemplate(); as meta) {\n <ng-container [ngTemplateOutlet]=\"meta\" [ngTemplateOutletContext]=\"{ $implicit: view.row.option }\" />\n }\n </span>\n }\n </span>\n\n @if (view.count) {\n <span rtElem=\"count\" qa-dataid=\"tree-row-count\">{{ view.count }}</span>\n }\n\n @if (endTemplate(); as end) {\n <span rtElem=\"end\" qa-dataid=\"tree-row-end\">\n <ng-container [ngTemplateOutlet]=\"end\" [ngTemplateOutletContext]=\"{ $implicit: view.row.option }\" />\n </span>\n }\n </div>\n } @empty {\n <div rtElem=\"empty\" qa-dataid=\"tree-empty\" [rtMod]=\"{ 'with-icon': !!emptyIcon() }\">\n @if (emptyIcon(); as icon) {\n <rt-icon rtElem=\"empty-icon\" qa-dataid=\"tree-empty-icon\" size=\"lg\" [name]=\"icon\" />\n }\n {{ emptyText() }}\n </div>\n }\n</ng-container>\n"],"mappings":"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"}
@@ -330,7 +330,8 @@ var RtIconButtonComponent = class RtIconButtonComponent {
330
330
  "rtTooltipPlacement",
331
331
  "rtTooltipShowDelay",
332
332
  "rtTooltipWhenTruncated",
333
- "rtTooltipHideOnClick"
333
+ "rtTooltipHideOnClick",
334
+ "rtTooltipShowOnTap"
334
335
  ],
335
336
  exportAs: ["rtTooltip"]
336
337
  },