@rt-tools/ui-kit-v2 0.39.0 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-aside.mjs +4 -4
  2. package/fesm2022/rt-tools-ui-kit-v2-aside.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +2 -2
  4. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +2 -0
  6. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +5 -5
  8. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +45 -44
  10. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +2 -2
  12. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +2 -2
  14. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-field.mjs +2 -2
  16. package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs +4 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs.map +1 -1
  19. package/fesm2022/rt-tools-ui-kit-v2-icon.mjs +10 -0
  20. package/fesm2022/rt-tools-ui-kit-v2-icon.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +2 -2
  22. package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -1
  23. package/fesm2022/rt-tools-ui-kit-v2-input.mjs +2 -2
  24. package/fesm2022/rt-tools-ui-kit-v2-message.mjs +3 -3
  25. package/fesm2022/rt-tools-ui-kit-v2-message.mjs.map +1 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +4 -4
  27. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  28. package/fesm2022/rt-tools-ui-kit-v2-table-core.mjs +101 -0
  29. package/fesm2022/rt-tools-ui-kit-v2-table-core.mjs.map +1 -0
  30. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +36 -37
  31. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  32. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +41 -4
  33. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +2 -0
  35. package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -1
  36. package/package.json +5 -1
  37. package/src/assets/icons-material/bookmark.fill.svg +5 -0
  38. package/src/assets/icons-material/bookmark.svg +5 -0
  39. package/src/assets/icons-material/flag.fill.svg +3 -0
  40. package/src/assets/icons-material/flag.svg +5 -0
  41. package/src/lib/components/aside/footer/CONTEXT.md +27 -0
  42. package/src/lib/components/aside/footer/rt-aside-footer.component.scss +8 -3
  43. package/src/lib/components/aside/header/rt-aside-header.component.scss +11 -3
  44. package/src/lib/components/button/rt-button.directive.scss +5 -1
  45. package/src/lib/components/data-table/CONTEXT.md +8 -2
  46. package/src/lib/components/field/CONTEXT.md +4 -2
  47. package/src/lib/components/field/rt-field.component.scss +14 -0
  48. package/src/lib/components/form-control/CONTEXT.md +13 -8
  49. package/src/lib/components/input/rt-input.component.scss +4 -0
  50. package/src/lib/components/message/CONTEXT.md +1 -1
  51. package/src/lib/components/message/rt-message.component.scss +2 -1
  52. package/src/lib/components/multiselect/rt-multiselect.component.scss +21 -0
  53. package/src/lib/components/service-block/stories/component/test-service-block-panel.component.scss +29 -0
  54. package/src/lib/components/table/CONTEXT.md +3 -0
  55. package/src/lib/components/table/copy-cell/CONTEXT.md +32 -0
  56. package/src/lib/components/table/copy-cell/rt-copy-cell.component.scss +26 -0
  57. package/src/lib/components/tabs/CONTEXT.md +18 -16
  58. package/src/lib/components/tabs/rt-tabs.component.scss +25 -1
  59. package/src/styles/_form.scss +10 -0
  60. package/src/styles/_semantic.scss +2 -1
  61. package/styles/tokens.css +29 -9
  62. package/types/rt-tools-ui-kit-v2-core.d.ts +20 -5
  63. package/types/rt-tools-ui-kit-v2-data-table.d.ts +23 -9
  64. package/types/rt-tools-ui-kit-v2-form-control.d.ts +2 -2
  65. package/types/rt-tools-ui-kit-v2-message.d.ts +1 -1
  66. package/types/rt-tools-ui-kit-v2-table-core.d.ts +76 -0
  67. package/types/rt-tools-ui-kit-v2-table.d.ts +21 -18
  68. package/types/rt-tools-ui-kit-v2-tabs.d.ts +26 -0
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-multiselect.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/multiselect/rt-multiselect-label.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/multiselect/rt-multiselect.component.ts","../../../projects/ui-kit-v2/src/lib/components/multiselect/rt-multiselect.component.html"],"sourcesContent":["import { Pipe, PipeTransform } from '@angular/core';\n\nimport { rtTreeFind } from '@rt-tools/ui-kit-v2/core';\nimport { IRtSelect } from '@rt-tools/ui-kit-v2/select';\n\n/** Лейбл опции по её value для отображения чипа выбранного значения — на любой глубине дерева. */\n@Pipe({ name: 'rtMultiselectLabel' })\nexport class RtMultiselectLabelPipe implements PipeTransform {\n public transform<TValue>(value: TValue, options: ReadonlyArray<IRtSelect.Option<TValue>>): string {\n return rtTreeFind(options, value)?.label ?? String(value);\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n computed,\n contentChild,\n forwardRef,\n inject,\n input,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { RtIconButtonComponent } 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 { RtPopoverDirective } from '@rt-tools/ui-kit-v2/popover';\nimport { IRtPopover } from '@rt-tools/ui-kit-v2/popover';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtOptionList, TRtOptionListAction } from '@rt-tools/ui-kit-v2/core';\nimport {\n rtTreeBranchState,\n rtTreeFind,\n rtTreeIsTree,\n rtTreeLeaves,\n rtTreeOpenFor,\n rtTreeRows,\n rtTreeSideKey,\n rtTreeToggle,\n} from '@rt-tools/ui-kit-v2/core';\nimport { RtSelectTriggerDirective } from '@rt-tools/ui-kit-v2/select';\nimport { IRtSelect } from '@rt-tools/ui-kit-v2/select';\nimport { RtTagComponent } from '@rt-tools/ui-kit-v2/tag';\nimport { RtMultiselectLabelPipe } from './rt-multiselect-label.pipe';\n\nconst BEM_BLOCK: string = 'rt-multiselect';\n\n/** Клавиши, открывающие закрытый dropdown с фокуса на trigger'е. */\n\nlet panelIdSeed: number = 0;\n\nfunction nextPanelId(): number {\n panelIdSeed += 1;\n return panelIdSeed;\n}\n\n/**\n * Multi-select с chip-rendering выбранных значений в trigger.\n *\n * Panel рендерится через CDK Overlay (`[rtPopover]`) — не обрезается overflow,\n * авто-flip. Панель НЕ закрывается после выбора (multi vs single). CVA через\n * общий `RtFormControlBase`. Reuses `IRtSelect.Option<TValue>` (type-import).\n * Чипы закрываются поштучно; крестик (`clearable`) слева от шеврона очищает весь\n * выбор.\n *\n * Keyboard-nav (activedescendant): фокус на trigger'е, ArrowUp/Down — highlight,\n * Enter — toggle опции (panel остаётся открытой), Escape — закрыть. Home/End —\n * deferred.\n */\n@Component({\n selector: 'rt-multiselect',\n templateUrl: './rt-multiselect.component.html',\n styleUrl: './rt-multiselect.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n NgTemplateOutlet,\n\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n RtPopoverDirective,\n RtTagComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n\n // pipes\n RtMultiselectLabelPipe,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtMultiselectComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-multiselect--open]': 'isOpen()',\n '[class.rt-multiselect--disabled]': 'isDisabled()',\n '[class.rt-multiselect--readonly]': 'isReadonly()',\n '[class.rt-multiselect--invalid]': 'isInvalid()',\n '[class.rt-multiselect--borderless]': '!bordered()',\n '[class.rt-multiselect--size--sm]': \"size() === 'sm'\",\n '[class.rt-multiselect--size--lg]': \"size() === 'lg'\",\n },\n})\nexport class RtMultiselectComponent<TValue> extends RtFormControlBase<ReadonlyArray<TValue>> {\n readonly #t_uiSelectValues: Signal<string> = rtKitLabel('uiSelectValues');\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly panelId: string = `rt-multiselect-panel-${nextPanelId()}`;\n\n protected readonly placeholderText: Signal<string> = computed((): string => this.placeholder() || this.#t_uiSelectValues());\n\n protected readonly popover: Signal<RtPopoverDirective> = viewChild.required(RtPopoverDirective);\n\n protected readonly triggerEl: Signal<ElementRef<HTMLButtonElement> | undefined> = viewChild<ElementRef<HTMLButtonElement>>('triggerEl');\n\n /** Раскрытые ветки дерева. Живут, пока открыта панель: при открытии раскрыты ветки над выбранным. */\n protected readonly openBranches: WritableSignal<ReadonlySet<TValue>> = signal<ReadonlySet<TValue>>(new Set<TValue>());\n\n /** Дерево ли список: тогда у строк есть место под стрелку и отступ по уровню. */\n protected readonly isTree: Signal<boolean> = computed((): boolean => rtTreeIsTree(this.options()));\n\n /** Видимые строки: плоский список — как прежде, дерево — по раскрытым веткам. */\n protected readonly rows: Signal<ReadonlyArray<IRtSelect.Row<TValue>>> = computed((): ReadonlyArray<IRtSelect.Row<TValue>> =>\n rtTreeRows(this.options(), this.openBranches())\n );\n\n /** Флажок каждой строки: у листа — выбран ли он, у ветки — выведен из её включённых листьев. */\n protected readonly rowChecks: Signal<ReadonlyArray<IRtSelect.TBranchState>> = computed((): ReadonlyArray<IRtSelect.TBranchState> =>\n this.rows().map((row: IRtSelect.Row<TValue>): IRtSelect.TBranchState => this.#checkOf(row))\n );\n\n protected readonly isOpen: Signal<boolean> = computed((): boolean => this.popover().isOpen());\n\n /** Клавиатура списка: подсветка, клавиши открытия, id для `aria-activedescendant`, прокрутка к опции. */\n protected readonly optionList: RtOptionList = new RtOptionList({\n panelId: (): string => this.panelId,\n isOpen: (): boolean => this.isOpen(),\n count: (): number => this.rows().length,\n isDisabled: (index: number): boolean => Boolean(this.rows()[index]?.option.disabled),\n chooseKeys: new Set<string>(['Enter', ' ']),\n sideKeys: true,\n edgeKeys: true,\n });\n\n protected readonly activeIndex: WritableSignal<number> = this.optionList.activeIndex;\n\n /** Идентификатор подсвеченной опции — тот же, что уходит в `aria-activedescendant`. */\n protected readonly activeOptionId: Signal<string | null> = this.optionList.activeOptionId;\n\n /**\n * Своя разметка указателя, если потребитель её объявил. Маркер тот же, что у выбора одного\n * значения: семьи отличаются только разметкой внутри кнопки.\n */\n protected readonly triggerTpl: Signal<RtSelectTriggerDirective<ReadonlyArray<TValue>> | undefined> =\n contentChild(RtSelectTriggerDirective);\n\n protected readonly hasValue: Signal<boolean> = computed((): boolean => this.value().length > 0);\n\n protected readonly visibleChips: Signal<ReadonlyArray<TValue>> = computed((): ReadonlyArray<TValue> =>\n this.value().slice(0, this.maxChips())\n );\n\n protected readonly extraChipsCount: Signal<number> = computed((): number => Math.max(0, this.value().length - this.maxChips()));\n\n /** Три значения, которые кит отдаёт своей разметке указателя, и не больше. */\n protected readonly triggerState: Signal<IRtSelect.TriggerState<ReadonlyArray<TValue>>> = computed(\n (): IRtSelect.TriggerState<ReadonlyArray<TValue>> => ({\n isOpen: this.isOpen(),\n value: this.value(),\n label: this.displayText(),\n isDisabled: this.isDisabled(),\n })\n );\n\n public readonly displayText: Signal<string> = computed((): string =>\n this.value()\n .map((v: TValue): string => this.labelOf(v))\n .join(', ')\n );\n\n public readonly options: InputSignal<ReadonlyArray<IRtSelect.Option<TValue>>> = input<ReadonlyArray<IRtSelect.Option<TValue>>>([]);\n\n /** Пусто — берётся переведённая подпись по умолчанию */\n public readonly placeholder: InputSignal<string> = input<string>('');\n public readonly maxChips: InputSignal<number> = input<number>(3);\n\n /** Иконка слева в указателе — та же, что у `rt-select`: перед подсказкой или чипами. */\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n\n /**\n * Чем мерится панель. По умолчанию она не уже кнопки и дальше растёт по содержимому:\n * со своим указателем кнопка бывает узкой, и панель по её ширине давила бы содержимое.\n * `trigger` возвращает прежнее — ровно по кнопке, `auto` пускает панель по содержимому\n * целиком. Предел высоты назначается входом `panelMaxHeight`.\n */\n public readonly panelWidth: InputSignal<IRtPopover.Width> = input<IRtPopover.Width>('trigger-min');\n\n /**\n * Предел высоты панели — длина как в стилях, например `20rem`. Пусто — предел кита\n * `--rt-input-panel-max-height`, и длинный список прокручивается внутри панели: без предела\n * он уходил за нижний край экрана, и пункты за краем были недостижимы. `none` снимает предел —\n * для короткого списка, который должен открываться целиком, без прокрутки. Значение ставится\n * на саму панель: она рисуется в наложении, вне поддерева блока.\n */\n public readonly panelMaxHeight: InputSignal<string | null> = input<string | null>(null);\n\n public override setDisabledState(disabled: boolean): void {\n super.setDisabledState(disabled);\n if (disabled) {\n this.popover().close();\n }\n }\n\n protected getEmptyValue(): ReadonlyArray<TValue> {\n return [];\n }\n\n protected focusAfterClear(): void {\n this.triggerEl()?.nativeElement.focus();\n }\n\n protected labelOf(value: TValue): string {\n return rtTreeFind(this.options(), value)?.label ?? String(value);\n }\n\n protected isSelected(value: TValue): boolean {\n return this.value().includes(value);\n }\n\n protected onOpened(): void {\n this.openBranches.set(rtTreeOpenFor(this.options(), this.value()));\n }\n\n protected toggleBranch(event: Event, value: TValue): void {\n event.stopPropagation();\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, value));\n }\n\n protected onClosed(): void {\n this.activeIndex.set(-1);\n this.markTouched();\n }\n\n /**\n * Лист переключается сам. Ветка выбирает все свои включённые листья или снимает их, когда\n * выбраны все; своё значение ветки в выбор не пишется.\n */\n protected toggleOption(opt: IRtSelect.Option<TValue>): void {\n if (opt.disabled || this.isDisabled()) {\n return;\n }\n const leaves: ReadonlyArray<TValue> = rtTreeLeaves(opt);\n const current: ReadonlyArray<TValue> = this.value();\n const clearing: boolean = rtTreeBranchState(opt, current) === 'all';\n const next: ReadonlyArray<TValue> = clearing\n ? current.filter((v: TValue): boolean => !leaves.includes(v))\n : [...current, ...leaves.filter((leaf: TValue): boolean => !current.includes(leaf))];\n this.value.set(next);\n this.emitChange([...next]);\n }\n\n protected removeChip(value: TValue, event: MouseEvent): void {\n event.stopPropagation();\n if (this.isDisabled()) {\n return;\n }\n const next: ReadonlyArray<TValue> = this.value().filter((v: TValue): boolean => v !== value);\n this.value.set(next);\n this.emitChange([...next]);\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.isDisabled()) {\n return;\n }\n if (!this.isOpen()) {\n if (this.optionList.opensBy(event.key)) {\n event.preventDefault();\n this.popover().open();\n this.optionList.move(1);\n }\n return;\n }\n this.#handleOpenKeydown(event);\n }\n\n #handleOpenKeydown(event: KeyboardEvent): void {\n const action: TRtOptionListAction = this.optionList.actionOf(event.key);\n if (action === 'none') {\n return;\n }\n event.preventDefault();\n switch (action) {\n case 'next':\n this.optionList.move(1);\n break;\n case 'previous':\n this.optionList.move(-1);\n break;\n case 'first':\n this.optionList.first();\n break;\n case 'last':\n this.optionList.last();\n break;\n case 'choose':\n this.#toggleActive();\n break;\n case 'close':\n // Escape закрывает один слой: панель. Без остановки событие всплывает до document,\n // и оверлей под панелью — боковая панель, окно — закрывается вместе с ней.\n event.stopPropagation();\n this.popover().close();\n break;\n case 'side':\n this.#sideKey(event.key);\n break;\n }\n }\n\n #checkOf(row: IRtSelect.Row<TValue>): IRtSelect.TBranchState {\n if (row.branch) {\n return rtTreeBranchState(row.option, this.value());\n }\n return this.value().includes(row.option.value) ? 'all' : 'none';\n }\n\n #sideKey(key: string): void {\n const answer: IRtSelect.SideKeyAnswer<TValue> = rtTreeSideKey(this.rows(), this.activeIndex(), key);\n const toggle: TValue | null = answer.toggle;\n if (toggle !== null) {\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, toggle));\n }\n this.activeIndex.set(answer.index);\n }\n\n #toggleActive(): void {\n const opts: ReadonlyArray<IRtSelect.Option<TValue>> = this.rows().map(\n (row: IRtSelect.Row<TValue>): IRtSelect.Option<TValue> => row.option\n );\n const index: number = this.activeIndex();\n if (index >= 0 && index < opts.length) {\n this.toggleOption(opts[index]);\n }\n }\n}\n","<ng-container rtBlock=\"rt-multiselect\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"multiselect-readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"multiselect-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" qa-dataid=\"multiselect-icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (value().length === 0) {\n <span rtElem=\"placeholder\" qa-dataid=\"multiselect-placeholder\">{{ placeholderText() }}</span>\n } @else {\n <span rtElem=\"chips\">\n @for (val of visibleChips(); track val) {\n <span rtElem=\"chip\" qa-dataid=\"multiselect-chip\" [attr.data-value]=\"val\">\n <rt-tag\n severity=\"info\"\n [value]=\"val | rtMultiselectLabel: options()\"\n [closable]=\"true\"\n (closed)=\"removeChip(val, $event)\" />\n </span>\n }\n @if (extraChipsCount() > 0) {\n <span rtElem=\"chip-more\" qa-dataid=\"multiselect-chip-more\">+{{ extraChipsCount() }}</span>\n }\n </span>\n }\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"multiselect-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n <ng-template #panelTpl>\n <div\n rtElem=\"panel\"\n qa-dataid=\"multiselect-panel\"\n role=\"listbox\"\n [style.--rt-multiselect-panel-max-height]=\"panelMaxHeight()\"\n [attr.aria-label]=\"ariaLabel()\">\n <!--\n Опции выбираются мышью через (click); keyboard-nav — на trigger'е\n (activedescendant), опции не focusable индивидуально.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"multiselect-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-multiselect-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: rowChecks()[i] === 'all',\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"rowChecks()[i] === 'all'\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"toggleOption(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"multiselect-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n <input\n rtElem=\"checkbox\"\n qa-dataid=\"multiselect-option-checkbox\"\n type=\"checkbox\"\n tabindex=\"-1\"\n [checked]=\"rowChecks()[i] === 'all'\"\n [indeterminate]=\"rowChecks()[i] === 'some'\" />\n <span rtElem=\"option-label\" qa-dataid=\"multiselect-option-label\">{{ row.option.label }}</span>\n </li>\n } @empty {\n <li rtElem=\"empty\" qa-dataid=\"multiselect-empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-multiselect.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/multiselect/rt-multiselect-label.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/multiselect/rt-multiselect.component.ts","../../../projects/ui-kit-v2/src/lib/components/multiselect/rt-multiselect.component.html"],"sourcesContent":["import { Pipe, PipeTransform } from '@angular/core';\n\nimport { rtTreeFind } from '@rt-tools/ui-kit-v2/core';\nimport { IRtSelect } from '@rt-tools/ui-kit-v2/select';\n\n/** Лейбл опции по её value для отображения чипа выбранного значения — на любой глубине дерева. */\n@Pipe({ name: 'rtMultiselectLabel' })\nexport class RtMultiselectLabelPipe implements PipeTransform {\n public transform<TValue>(value: TValue, options: ReadonlyArray<IRtSelect.Option<TValue>>): string {\n return rtTreeFind(options, value)?.label ?? String(value);\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n computed,\n contentChild,\n forwardRef,\n inject,\n input,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { RtIconButtonComponent } 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 { RtPopoverDirective } from '@rt-tools/ui-kit-v2/popover';\nimport { IRtPopover } from '@rt-tools/ui-kit-v2/popover';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtOptionList, TRtOptionListAction } from '@rt-tools/ui-kit-v2/core';\nimport {\n rtTreeBranchState,\n rtTreeFind,\n rtTreeIsTree,\n rtTreeLeaves,\n rtTreeOpenFor,\n rtTreeRows,\n rtTreeSideKey,\n rtTreeToggle,\n} from '@rt-tools/ui-kit-v2/core';\nimport { RtSelectTriggerDirective } from '@rt-tools/ui-kit-v2/select';\nimport { IRtSelect } from '@rt-tools/ui-kit-v2/select';\nimport { RtTagComponent } from '@rt-tools/ui-kit-v2/tag';\nimport { RtMultiselectLabelPipe } from './rt-multiselect-label.pipe';\n\nconst BEM_BLOCK: string = 'rt-multiselect';\n\n/** Клавиши, открывающие закрытый dropdown с фокуса на trigger'е. */\n\nlet panelIdSeed: number = 0;\n\nfunction nextPanelId(): number {\n panelIdSeed += 1;\n return panelIdSeed;\n}\n\n/**\n * Multi-select с chip-rendering выбранных значений в trigger.\n *\n * Panel рендерится через CDK Overlay (`[rtPopover]`) — не обрезается overflow,\n * авто-flip. Панель НЕ закрывается после выбора (multi vs single). CVA через\n * общий `RtFormControlBase`. Reuses `IRtSelect.Option<TValue>` (type-import).\n * Чипы закрываются поштучно; крестик (`clearable`) слева от шеврона очищает весь\n * выбор.\n *\n * Keyboard-nav (activedescendant): фокус на trigger'е, ArrowUp/Down — highlight,\n * Enter — toggle опции (panel остаётся открытой), Escape — закрыть. Home/End —\n * deferred.\n */\n@Component({\n selector: 'rt-multiselect',\n templateUrl: './rt-multiselect.component.html',\n styleUrl: './rt-multiselect.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n NgTemplateOutlet,\n\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n RtPopoverDirective,\n RtTagComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n\n // pipes\n RtMultiselectLabelPipe,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtMultiselectComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-multiselect--open]': 'isOpen()',\n '[class.rt-multiselect--disabled]': 'isDisabled()',\n '[class.rt-multiselect--readonly]': 'isReadonly()',\n '[class.rt-multiselect--invalid]': 'isInvalid()',\n '[class.rt-multiselect--borderless]': '!bordered()',\n '[class.rt-multiselect--size--sm]': \"size() === 'sm'\",\n '[class.rt-multiselect--size--lg]': \"size() === 'lg'\",\n },\n})\nexport class RtMultiselectComponent<TValue> extends RtFormControlBase<ReadonlyArray<TValue>> {\n readonly #t_uiSelectValues: Signal<string> = rtKitLabel('uiSelectValues');\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly panelId: string = `rt-multiselect-panel-${nextPanelId()}`;\n\n protected readonly placeholderText: Signal<string> = computed((): string => this.placeholder() || this.#t_uiSelectValues());\n\n protected readonly popover: Signal<RtPopoverDirective> = viewChild.required(RtPopoverDirective);\n\n protected readonly triggerEl: Signal<ElementRef<HTMLButtonElement> | undefined> = viewChild<ElementRef<HTMLButtonElement>>('triggerEl');\n\n /** Раскрытые ветки дерева. Живут, пока открыта панель: при открытии раскрыты ветки над выбранным. */\n protected readonly openBranches: WritableSignal<ReadonlySet<TValue>> = signal<ReadonlySet<TValue>>(new Set<TValue>());\n\n /** Дерево ли список: тогда у строк есть место под стрелку и отступ по уровню. */\n protected readonly isTree: Signal<boolean> = computed((): boolean => rtTreeIsTree(this.options()));\n\n /** Видимые строки: плоский список — как прежде, дерево — по раскрытым веткам. */\n protected readonly rows: Signal<ReadonlyArray<IRtSelect.Row<TValue>>> = computed((): ReadonlyArray<IRtSelect.Row<TValue>> =>\n rtTreeRows(this.options(), this.openBranches())\n );\n\n /** Флажок каждой строки: у листа — выбран ли он, у ветки — выведен из её включённых листьев. */\n protected readonly rowChecks: Signal<ReadonlyArray<IRtSelect.TBranchState>> = computed((): ReadonlyArray<IRtSelect.TBranchState> =>\n this.rows().map((row: IRtSelect.Row<TValue>): IRtSelect.TBranchState => this.#checkOf(row))\n );\n\n protected readonly isOpen: Signal<boolean> = computed((): boolean => this.popover().isOpen());\n\n /** Клавиатура списка: подсветка, клавиши открытия, id для `aria-activedescendant`, прокрутка к опции. */\n protected readonly optionList: RtOptionList = new RtOptionList({\n panelId: (): string => this.panelId,\n isOpen: (): boolean => this.isOpen(),\n count: (): number => this.rows().length,\n isDisabled: (index: number): boolean => Boolean(this.rows()[index]?.option.disabled),\n chooseKeys: new Set<string>(['Enter', ' ']),\n sideKeys: true,\n edgeKeys: true,\n });\n\n protected readonly activeIndex: WritableSignal<number> = this.optionList.activeIndex;\n\n /** Идентификатор подсвеченной опции — тот же, что уходит в `aria-activedescendant`. */\n protected readonly activeOptionId: Signal<string | null> = this.optionList.activeOptionId;\n\n /**\n * Своя разметка указателя, если потребитель её объявил. Маркер тот же, что у выбора одного\n * значения: семьи отличаются только разметкой внутри кнопки.\n */\n protected readonly triggerTpl: Signal<RtSelectTriggerDirective<ReadonlyArray<TValue>> | undefined> =\n contentChild(RtSelectTriggerDirective);\n\n protected readonly hasValue: Signal<boolean> = computed((): boolean => this.value().length > 0);\n\n protected readonly visibleChips: Signal<ReadonlyArray<TValue>> = computed((): ReadonlyArray<TValue> =>\n this.value().slice(0, this.maxChips())\n );\n\n protected readonly extraChipsCount: Signal<number> = computed((): number => Math.max(0, this.value().length - this.maxChips()));\n\n /** Три значения, которые кит отдаёт своей разметке указателя, и не больше. */\n protected readonly triggerState: Signal<IRtSelect.TriggerState<ReadonlyArray<TValue>>> = computed(\n (): IRtSelect.TriggerState<ReadonlyArray<TValue>> => ({\n isOpen: this.isOpen(),\n value: this.value(),\n label: this.displayText(),\n isDisabled: this.isDisabled(),\n })\n );\n\n public readonly displayText: Signal<string> = computed((): string =>\n this.value()\n .map((v: TValue): string => this.labelOf(v))\n .join(', ')\n );\n\n public readonly options: InputSignal<ReadonlyArray<IRtSelect.Option<TValue>>> = input<ReadonlyArray<IRtSelect.Option<TValue>>>([]);\n\n /** Пусто — берётся переведённая подпись по умолчанию */\n public readonly placeholder: InputSignal<string> = input<string>('');\n public readonly maxChips: InputSignal<number> = input<number>(3);\n\n /** Иконка слева в указателе — та же, что у `rt-select`: перед подсказкой или чипами. */\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n\n /**\n * Чем мерится панель. По умолчанию она не уже кнопки и дальше растёт по содержимому:\n * со своим указателем кнопка бывает узкой, и панель по её ширине давила бы содержимое.\n * `trigger` возвращает прежнее — ровно по кнопке, `auto` пускает панель по содержимому\n * целиком. Предел высоты назначается входом `panelMaxHeight`.\n */\n public readonly panelWidth: InputSignal<IRtPopover.Width> = input<IRtPopover.Width>('trigger-min');\n\n /**\n * Предел высоты панели — длина как в стилях, например `20rem`. Пусто — предел кита\n * `--rt-input-panel-max-height`, и длинный список прокручивается внутри панели: без предела\n * он уходил за нижний край экрана, и пункты за краем были недостижимы. `none` снимает предел —\n * для короткого списка, который должен открываться целиком, без прокрутки. Значение ставится\n * на саму панель: она рисуется в наложении, вне поддерева блока.\n */\n public readonly panelMaxHeight: InputSignal<string | null> = input<string | null>(null);\n\n public override setDisabledState(disabled: boolean): void {\n super.setDisabledState(disabled);\n if (disabled) {\n this.popover().close();\n }\n }\n\n protected getEmptyValue(): ReadonlyArray<TValue> {\n return [];\n }\n\n protected focusAfterClear(): void {\n this.triggerEl()?.nativeElement.focus();\n }\n\n protected labelOf(value: TValue): string {\n return rtTreeFind(this.options(), value)?.label ?? String(value);\n }\n\n protected isSelected(value: TValue): boolean {\n return this.value().includes(value);\n }\n\n protected onOpened(): void {\n this.openBranches.set(rtTreeOpenFor(this.options(), this.value()));\n }\n\n protected toggleBranch(event: Event, value: TValue): void {\n event.stopPropagation();\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, value));\n }\n\n protected onClosed(): void {\n this.activeIndex.set(-1);\n this.markTouched();\n }\n\n /**\n * Лист переключается сам. Ветка выбирает все свои включённые листья или снимает их, когда\n * выбраны все; своё значение ветки в выбор не пишется.\n */\n protected toggleOption(opt: IRtSelect.Option<TValue>): void {\n if (opt.disabled || this.isDisabled()) {\n return;\n }\n const leaves: ReadonlyArray<TValue> = rtTreeLeaves(opt);\n const current: ReadonlyArray<TValue> = this.value();\n const clearing: boolean = rtTreeBranchState(opt, current) === 'all';\n const next: ReadonlyArray<TValue> = clearing\n ? current.filter((v: TValue): boolean => !leaves.includes(v))\n : [...current, ...leaves.filter((leaf: TValue): boolean => !current.includes(leaf))];\n this.value.set(next);\n this.emitChange([...next]);\n }\n\n protected removeChip(value: TValue, event: MouseEvent): void {\n event.stopPropagation();\n if (this.isDisabled()) {\n return;\n }\n const next: ReadonlyArray<TValue> = this.value().filter((v: TValue): boolean => v !== value);\n this.value.set(next);\n this.emitChange([...next]);\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.isDisabled()) {\n return;\n }\n if (!this.isOpen()) {\n if (this.optionList.opensBy(event.key)) {\n event.preventDefault();\n this.popover().open();\n this.optionList.move(1);\n }\n return;\n }\n this.#handleOpenKeydown(event);\n }\n\n #handleOpenKeydown(event: KeyboardEvent): void {\n const action: TRtOptionListAction = this.optionList.actionOf(event.key);\n if (action === 'none') {\n return;\n }\n event.preventDefault();\n switch (action) {\n case 'next':\n this.optionList.move(1);\n break;\n case 'previous':\n this.optionList.move(-1);\n break;\n case 'first':\n this.optionList.first();\n break;\n case 'last':\n this.optionList.last();\n break;\n case 'choose':\n this.#toggleActive();\n break;\n case 'close':\n // Escape закрывает один слой: панель. Без остановки событие всплывает до document,\n // и оверлей под панелью — боковая панель, окно — закрывается вместе с ней.\n event.stopPropagation();\n this.popover().close();\n break;\n case 'side':\n this.#sideKey(event.key);\n break;\n }\n }\n\n #checkOf(row: IRtSelect.Row<TValue>): IRtSelect.TBranchState {\n if (row.branch) {\n return rtTreeBranchState(row.option, this.value());\n }\n return this.value().includes(row.option.value) ? 'all' : 'none';\n }\n\n #sideKey(key: string): void {\n const answer: IRtSelect.SideKeyAnswer<TValue> = rtTreeSideKey(this.rows(), this.activeIndex(), key);\n const toggle: TValue | null = answer.toggle;\n if (toggle !== null) {\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, toggle));\n }\n this.activeIndex.set(answer.index);\n }\n\n #toggleActive(): void {\n const opts: ReadonlyArray<IRtSelect.Option<TValue>> = this.rows().map(\n (row: IRtSelect.Row<TValue>): IRtSelect.Option<TValue> => row.option\n );\n const index: number = this.activeIndex();\n if (index >= 0 && index < opts.length) {\n this.toggleOption(opts[index]);\n }\n }\n}\n","<ng-container rtBlock=\"rt-multiselect\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"multiselect-readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"multiselect-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" qa-dataid=\"multiselect-icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (value().length === 0) {\n <span rtElem=\"placeholder\" qa-dataid=\"multiselect-placeholder\">{{ placeholderText() }}</span>\n } @else {\n <span rtElem=\"chips\">\n @for (val of visibleChips(); track val) {\n <span rtElem=\"chip\" qa-dataid=\"multiselect-chip\" [attr.data-value]=\"val\">\n <rt-tag\n severity=\"info\"\n [value]=\"val | rtMultiselectLabel: options()\"\n [closable]=\"true\"\n (closed)=\"removeChip(val, $event)\" />\n </span>\n }\n @if (extraChipsCount() > 0) {\n <span rtElem=\"chip-more\" qa-dataid=\"multiselect-chip-more\">+{{ extraChipsCount() }}</span>\n }\n </span>\n }\n @if (showClearButton()) {\n @if (clearTabbable()) {\n <!-- Место крестика в строке: сам крестик в порядке Tab стоит рядом с кнопкой, ниже. -->\n <span rtElem=\"clear-slot\" aria-hidden=\"true\"></span>\n } @else {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"multiselect-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n @if (showClearButton() && clearTabbable() && !triggerTpl()) {\n <!--\n Крестик в порядке обхода — соседняя кнопка поверх своего места в строке, а не кнопка в\n кнопке: вложенную кнопку скринридер не объявляет, а её Enter открывал бы список.\n -->\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"multiselect-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [rtMod]=\"{ outside: true }\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"0\"\n (clicked)=\"clear($event)\" />\n }\n\n <ng-template #panelTpl>\n <div\n rtElem=\"panel\"\n qa-dataid=\"multiselect-panel\"\n role=\"listbox\"\n [style.--rt-multiselect-panel-max-height]=\"panelMaxHeight()\"\n [attr.aria-label]=\"ariaLabel()\">\n <!--\n Опции выбираются мышью через (click); keyboard-nav — на trigger'е\n (activedescendant), опции не focusable индивидуально.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"multiselect-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-multiselect-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: rowChecks()[i] === 'all',\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"rowChecks()[i] === 'all'\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"toggleOption(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"multiselect-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n <input\n rtElem=\"checkbox\"\n qa-dataid=\"multiselect-option-checkbox\"\n type=\"checkbox\"\n tabindex=\"-1\"\n [checked]=\"rowChecks()[i] === 'all'\"\n [indeterminate]=\"rowChecks()[i] === 'some'\" />\n <span rtElem=\"option-label\" qa-dataid=\"multiselect-option-label\">{{ row.option.label }}</span>\n </li>\n } @empty {\n <li rtElem=\"empty\" qa-dataid=\"multiselect-empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n"],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ import { EListSortOrder } from "@rt-tools/utils";
2
+ function rtIsEmptyFilterValue(value, mode) {
3
+ return mode === "falsy" ? !value : value === "";
4
+ }
5
+ function rtFilterOf(filters, propertyName) {
6
+ return filters.find((filter) => filter.propertyName === propertyName) ?? null;
7
+ }
8
+ function rtFiltersWithValue(filters, propertyName, operatorType, value, mode) {
9
+ const current = rtFilterOf(filters, propertyName);
10
+ if (rtIsEmptyFilterValue(value, mode)) return current === null ? filters : filters.filter((filter) => filter !== current);
11
+ if (current === null) return [...filters, {
12
+ propertyName,
13
+ operatorType,
14
+ value
15
+ }];
16
+ if (current.value === value) return filters;
17
+ return filters.map((filter) => filter === current ? {
18
+ ...filter,
19
+ value
20
+ } : filter);
21
+ }
22
+ function rtFiltersWithOperator(filters, propertyName, operatorType, value, mode) {
23
+ const current = rtFilterOf(filters, propertyName);
24
+ if (current === null) return rtIsEmptyFilterValue(value, mode) ? filters : [...filters, {
25
+ propertyName,
26
+ operatorType,
27
+ value
28
+ }];
29
+ if (current.operatorType === operatorType) return filters;
30
+ return filters.map((filter) => filter === current ? {
31
+ ...filter,
32
+ operatorType
33
+ } : filter);
34
+ }
35
+ function rtKeysOf(entities, keyExp) {
36
+ return entities.map((entity) => entity[keyExp]);
37
+ }
38
+ function rtWithPageEntities(selected, page, keyExp) {
39
+ const keys = rtKeysOf(selected, keyExp);
40
+ return [...selected, ...page.filter((entity) => !keys.includes(entity[keyExp]))];
41
+ }
42
+ function rtWithoutPageEntities(selected, page, keyExp) {
43
+ const keys = rtKeysOf(page, keyExp);
44
+ return selected.filter((entity) => !keys.includes(entity[keyExp]));
45
+ }
46
+ function rtWithEntity(selected, entity, checked, keyExp) {
47
+ const without = selected.filter((el) => el[keyExp] !== entity[keyExp]);
48
+ return checked ? [...without, entity] : without;
49
+ }
50
+ function rtAnyOnPage(page, selectedKeys, keyExp) {
51
+ return page.some((entity) => selectedKeys.includes(entity[keyExp]));
52
+ }
53
+ function rtAllOnPage(page, selectedKeys, keyExp) {
54
+ return page.every((entity) => selectedKeys.includes(entity[keyExp]));
55
+ }
56
+ function rtPresetEntities(entities, keys, keyExp) {
57
+ return entities.filter((entity) => keys.includes(entity[keyExp]));
58
+ }
59
+ function rtSelectedAfterPage(selected, page, excluded, keyExp) {
60
+ const excludedKeys = rtKeysOf(excluded, keyExp);
61
+ return rtWithPageEntities(selected, page, keyExp).filter((entity) => !excludedKeys.includes(entity[keyExp]));
62
+ }
63
+ function rtExcludedAfterEntity(excluded, entity, checked, keyExp) {
64
+ return rtWithEntity(excluded, entity, !checked, keyExp);
65
+ }
66
+ function rtExcludedAfterPageMarked(excluded, page, keyExp) {
67
+ return rtWithoutPageEntities(excluded, page, keyExp);
68
+ }
69
+ function rtExcludedAfterPageUnmarked(excluded, page, keyExp) {
70
+ return rtWithPageEntities(excluded, page, keyExp);
71
+ }
72
+ function rtIsSortedBy(current, propertyName) {
73
+ return !!propertyName && current?.propertyName === propertyName;
74
+ }
75
+ function rtSortDirectionOf(current, propertyName) {
76
+ return current && rtIsSortedBy(current, propertyName) ? current.sortDirection : null;
77
+ }
78
+ function rtNextSort(current, propertyName, cycle) {
79
+ const direction = current?.sortDirection?.toLowerCase();
80
+ if (!rtIsSortedBy(current, propertyName)) return {
81
+ propertyName,
82
+ sortDirection: EListSortOrder.ASC
83
+ };
84
+ if (direction === EListSortOrder.ASC) return {
85
+ propertyName,
86
+ sortDirection: EListSortOrder.DESC
87
+ };
88
+ return cycle === "repeats" ? {
89
+ propertyName,
90
+ sortDirection: EListSortOrder.ASC
91
+ } : null;
92
+ }
93
+ function rtAriaSortOf(current, propertyName) {
94
+ if (!rtIsSortedBy(current, propertyName)) return "none";
95
+ const direction = current?.sortDirection?.toLowerCase();
96
+ if (direction === EListSortOrder.ASC) return "ascending";
97
+ return direction === EListSortOrder.DESC ? "descending" : "none";
98
+ }
99
+ export { rtAllOnPage, rtAnyOnPage, rtAriaSortOf, rtExcludedAfterEntity, rtExcludedAfterPageMarked, rtExcludedAfterPageUnmarked, rtFilterOf, rtFiltersWithOperator, rtFiltersWithValue, rtIsEmptyFilterValue, rtIsSortedBy, rtKeysOf, rtNextSort, rtPresetEntities, rtSelectedAfterPage, rtSortDirectionOf, rtWithEntity, rtWithPageEntities, rtWithoutPageEntities };
100
+
101
+ //# sourceMappingURL=rt-tools-ui-kit-v2-table-core.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-table-core.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/table-core/rt-table-filter.core.ts","../../../projects/ui-kit-v2/src/lib/components/table-core/rt-table-selection.core.ts","../../../projects/ui-kit-v2/src/lib/components/table-core/rt-table-sort.core.ts"],"sourcesContent":["import { IFilterModel, TFilterOperatorType } from '@rt-tools/utils';\n\n/** Значение условия в том виде, в каком оно хранится и сверяется. Одно для обеих таблиц. */\nexport type TRtFilterValue = string | number | boolean;\n\n/**\n * Какие значения поля снимают условие колонки.\n *\n * `empty-string` — только пустая строка; `0` и `false` остаются значениями, так ведёт себя `rt-table`.\n * `falsy` — любое ложное значение, как в первом ките; так ведёт себя `rt-data-table`.\n */\nexport type TRtFilterEmptyMode = 'empty-string' | 'falsy';\n\n/** Снимает ли это значение условие колонки при режиме таблицы. */\nexport function rtIsEmptyFilterValue(value: unknown, mode: TRtFilterEmptyMode): boolean {\n return mode === 'falsy' ? !value : value === '';\n}\n\n/** Условие по этой колонке из набора либо `null`, когда колонка ничего не отбирает. */\nexport function rtFilterOf<KEY extends string>(filters: ReadonlyArray<IFilterModel<KEY>>, propertyName: KEY): IFilterModel<KEY> | null {\n return filters.find((filter: IFilterModel<KEY>): boolean => filter.propertyName === propertyName) ?? null;\n}\n\n/**\n * Набор условий после перемены значения по одной колонке.\n *\n * Пустое по режиму значение снимает колонку с отбора, а не уходит пустым условием: условие без\n * значения просит потребителя ответить «подходит всё», и потребитель, прочитавший его буквально, не\n * вернёт ни одной строки.\n *\n * Возвращает прежний набор тем же объектом, когда менять нечего: по этому признаку таблица и решает,\n * сообщать ли наружу.\n */\nexport function rtFiltersWithValue<KEY extends string>(\n filters: ReadonlyArray<IFilterModel<KEY>>,\n propertyName: KEY,\n operatorType: TFilterOperatorType,\n value: TRtFilterValue,\n mode: TRtFilterEmptyMode\n): ReadonlyArray<IFilterModel<KEY>> {\n const current: IFilterModel<KEY> | null = rtFilterOf(filters, propertyName);\n\n if (rtIsEmptyFilterValue(value, mode)) {\n return current === null ? filters : filters.filter((filter: IFilterModel<KEY>): boolean => filter !== current);\n }\n\n if (current === null) {\n return [...filters, { propertyName, operatorType, value }];\n }\n\n if (current.value === value) {\n return filters;\n }\n\n return filters.map((filter: IFilterModel<KEY>): IFilterModel<KEY> => (filter === current ? { ...filter, value } : filter));\n}\n\n/**\n * Набор условий после перемены вида сравнения по одной колонке.\n *\n * Колонка с условием меняет в нём вид сравнения. Колонка без условия, но с набранным значением,\n * получает условие; без значения отбирать нечего, и набор не меняется — наружу ничего не уходит.\n */\nexport function rtFiltersWithOperator<KEY extends string>(\n filters: ReadonlyArray<IFilterModel<KEY>>,\n propertyName: KEY,\n operatorType: TFilterOperatorType,\n value: IFilterModel<KEY>['value'],\n mode: TRtFilterEmptyMode\n): ReadonlyArray<IFilterModel<KEY>> {\n const current: IFilterModel<KEY> | null = rtFilterOf(filters, propertyName);\n\n if (current === null) {\n return rtIsEmptyFilterValue(value, mode) ? filters : [...filters, { propertyName, operatorType, value }];\n }\n\n if (current.operatorType === operatorType) {\n return filters;\n }\n\n return filters.map((filter: IFilterModel<KEY>): IFilterModel<KEY> => (filter === current ? { ...filter, operatorType } : filter));\n}\n","/** Ключи записей списка. */\nexport function rtKeysOf<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n entities: ReadonlyArray<ENTITY_TYPE>,\n keyExp: KEY\n): Array<ENTITY_TYPE[KEY]> {\n return entities.map((entity: ENTITY_TYPE) => entity[keyExp]);\n}\n\n/** Записи страницы, добавленные к отмеченным; повторов не будет. */\nexport function rtWithPageEntities<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n selected: ReadonlyArray<ENTITY_TYPE>,\n page: ReadonlyArray<ENTITY_TYPE>,\n keyExp: KEY\n): ENTITY_TYPE[] {\n const keys: Array<ENTITY_TYPE[KEY]> = rtKeysOf(selected, keyExp);\n\n return [...selected, ...page.filter((entity: ENTITY_TYPE) => !keys.includes(entity[keyExp]))];\n}\n\n/** Отмеченные без записей страницы. */\nexport function rtWithoutPageEntities<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n selected: ReadonlyArray<ENTITY_TYPE>,\n page: ReadonlyArray<ENTITY_TYPE>,\n keyExp: KEY\n): ENTITY_TYPE[] {\n const keys: Array<ENTITY_TYPE[KEY]> = rtKeysOf(page, keyExp);\n\n return selected.filter((entity: ENTITY_TYPE) => !keys.includes(entity[keyExp]));\n}\n\n/** Отмеченные после отметки или снятия одной записи. */\nexport function rtWithEntity<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n selected: ReadonlyArray<ENTITY_TYPE>,\n entity: ENTITY_TYPE,\n checked: boolean,\n keyExp: KEY\n): ENTITY_TYPE[] {\n const without: ENTITY_TYPE[] = selected.filter((el: ENTITY_TYPE) => el[keyExp] !== entity[keyExp]);\n\n return checked ? [...without, entity] : without;\n}\n\n/** Хоть одна запись страницы отмечена. */\nexport function rtAnyOnPage<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n page: ReadonlyArray<ENTITY_TYPE>,\n selectedKeys: ReadonlyArray<ENTITY_TYPE[KEY]>,\n keyExp: KEY\n): boolean {\n return page.some((entity: ENTITY_TYPE) => selectedKeys.includes(entity[keyExp]));\n}\n\n/** Отмечены все записи страницы; пустая страница у первого кита считается отмеченной целиком. */\nexport function rtAllOnPage<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n page: ReadonlyArray<ENTITY_TYPE>,\n selectedKeys: ReadonlyArray<ENTITY_TYPE[KEY]>,\n keyExp: KEY\n): boolean {\n return page.every((entity: ENTITY_TYPE) => selectedKeys.includes(entity[keyExp]));\n}\n\n/** Записи первой страницы, чьи ключи названы приложением как отмеченные заранее. */\nexport function rtPresetEntities<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n entities: ReadonlyArray<ENTITY_TYPE>,\n keys: ReadonlyArray<ENTITY_TYPE[KEY]>,\n keyExp: KEY\n): ENTITY_TYPE[] {\n return entities.filter((entity: ENTITY_TYPE) => keys.includes(entity[keyExp]));\n}\n\n/** Отметки после смены страницы под «отметить все»: пришедшие строки отмечаются, кроме исключённых. */\nexport function rtSelectedAfterPage<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n selected: ReadonlyArray<ENTITY_TYPE>,\n page: ReadonlyArray<ENTITY_TYPE>,\n excluded: ReadonlyArray<ENTITY_TYPE>,\n keyExp: KEY\n): ENTITY_TYPE[] {\n const excludedKeys: Array<ENTITY_TYPE[KEY]> = rtKeysOf(excluded, keyExp);\n\n return rtWithPageEntities(selected, page, keyExp).filter((entity: ENTITY_TYPE) => !excludedKeys.includes(entity[keyExp]));\n}\n\n/** Исключения после отметки или снятия одной записи под «отметить все». */\nexport function rtExcludedAfterEntity<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n excluded: ReadonlyArray<ENTITY_TYPE>,\n entity: ENTITY_TYPE,\n checked: boolean,\n keyExp: KEY\n): ENTITY_TYPE[] {\n return rtWithEntity(excluded, entity, !checked, keyExp);\n}\n\n/** Исключения после отметки всей страницы под «отметить все»: её записи уходят из исключений. */\nexport function rtExcludedAfterPageMarked<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n excluded: ReadonlyArray<ENTITY_TYPE>,\n page: ReadonlyArray<ENTITY_TYPE>,\n keyExp: KEY\n): ENTITY_TYPE[] {\n return rtWithoutPageEntities(excluded, page, keyExp);\n}\n\n/** Исключения после снятия всей страницы под «отметить все»: её записи становятся исключениями. */\nexport function rtExcludedAfterPageUnmarked<ENTITY_TYPE, KEY extends keyof ENTITY_TYPE>(\n excluded: ReadonlyArray<ENTITY_TYPE>,\n page: ReadonlyArray<ENTITY_TYPE>,\n keyExp: KEY\n): ENTITY_TYPE[] {\n return rtWithPageEntities(excluded, page, keyExp);\n}\n","import { EListSortOrder, ISortModel, TListSortOrderType, TNullable } from '@rt-tools/utils';\n\n/** Значение атрибута `aria-sort` шапки: направление либо его отсутствие. Одно для обеих таблиц. */\nexport type TRtAriaSort = 'ascending' | 'descending' | 'none';\n\n/**\n * Что просит нажатие на шапку после убывания.\n *\n * `clears` — порядок снимается: по возрастанию → по убыванию → без порядка, так ведёт себя `rt-table`.\n * `repeats` — снова по возрастанию, как в первом ките; так ведёт себя `rt-data-table`.\n */\nexport type TRtSortCycle = 'clears' | 'repeats';\n\n/** Порядок стоит по этой колонке. */\nexport function rtIsSortedBy(current: TNullable<ISortModel<string>>, propertyName: TNullable<string>): boolean {\n return !!propertyName && current?.propertyName === propertyName;\n}\n\n/** Направление порядка по этой колонке либо `null`, когда порядок стоит по другой или не стоит вовсе. */\nexport function rtSortDirectionOf(current: TNullable<ISortModel<string>>, propertyName: string): TListSortOrderType | null {\n return current && rtIsSortedBy(current, propertyName) ? current.sortDirection : null;\n}\n\n/**\n * Следующий порядок после нажатия на шапку колонки.\n *\n * Оба круга смотрят на нажатую колонку: другая колонка встаёт по возрастанию, та, что идёт по\n * возрастанию, — по убыванию. Круги расходятся только после убывания.\n */\nexport function rtNextSort(current: TNullable<ISortModel<string>>, propertyName: string, cycle: TRtSortCycle): ISortModel<string> | null {\n const direction: string | undefined = current?.sortDirection?.toLowerCase();\n\n if (!rtIsSortedBy(current, propertyName)) {\n return { propertyName, sortDirection: EListSortOrder.ASC };\n }\n\n if (direction === EListSortOrder.ASC) {\n return { propertyName, sortDirection: EListSortOrder.DESC };\n }\n\n return cycle === 'repeats' ? { propertyName, sortDirection: EListSortOrder.ASC } : null;\n}\n\n/** Значение `aria-sort` шапки: скринридер называет порядок колонки, а не только её имя. */\nexport function rtAriaSortOf(current: TNullable<ISortModel<string>>, propertyName: string): TRtAriaSort {\n if (!rtIsSortedBy(current, propertyName)) {\n return 'none';\n }\n\n const direction: string | undefined = current?.sortDirection?.toLowerCase();\n\n if (direction === EListSortOrder.ASC) {\n return 'ascending';\n }\n\n return direction === EListSortOrder.DESC ? 'descending' : 'none';\n}\n"],"mappings":"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"}
@@ -8,6 +8,7 @@ import { RtTooltipDirective } from "@rt-tools/ui-kit-v2/tooltip";
8
8
  import { NgTemplateOutlet } from "@angular/common";
9
9
  import { EFilterOperatorType, EListSortOrder } from "@rt-tools/utils";
10
10
  import { RtIconComponent } from "@rt-tools/ui-kit-v2/icon";
11
+ import { rtAriaSortOf, rtFilterOf, rtFiltersWithOperator, rtFiltersWithValue, rtNextSort, rtSortDirectionOf } from "@rt-tools/ui-kit-v2/table-core";
11
12
  import { CDK_TABLE, CdkCell, CdkCellDef, CdkColumnDef, CdkHeaderCell, CdkHeaderCellDef, CdkTable, DataRowOutlet, FooterRowOutlet, HeaderRowOutlet, NoDataRowOutlet } from "@angular/cdk/table";
12
13
  import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
13
14
  import { RtEmptyStateComponent } from "@rt-tools/ui-kit-v2/empty-state";
@@ -50,7 +51,7 @@ var RtCopyCellComponent = class RtCopyCellComponent {
50
51
  this.#copiedLabel = rtKitLabel(COPIED_KEY);
51
52
  this.copied = signal(false, ...ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []);
52
53
  this.hint = signal("", ...ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []);
53
- this.iconName = computed(() => this.copied() ? "check" : "copy", ...ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []);
54
+ this.iconName = computed(() => this.copied() ? "check" : "ico-copy", ...ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []);
54
55
  this.actionLabel = computed(() => this.copied() ? this.#copiedLabel() : this.#copyLabel(), ...ngDevMode ? [{ debugName: "actionLabel" }] : /* istanbul ignore next */ []);
55
56
  this.value = input(null, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
56
57
  this.variant = input("ghost", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
@@ -58,6 +59,10 @@ var RtCopyCellComponent = class RtCopyCellComponent {
58
59
  ...ngDevMode ? { debugName: "revealOnHover" } : /* istanbul ignore next */ {},
59
60
  transform: booleanAttribute
60
61
  });
62
+ this.framed = input(false, {
63
+ ...ngDevMode ? { debugName: "framed" } : /* istanbul ignore next */ {},
64
+ transform: booleanAttribute
65
+ });
61
66
  this.#destroyRef.onDestroy(() => this.#clearTimer());
62
67
  }
63
68
  measureTruncation() {
@@ -125,6 +130,13 @@ var RtCopyCellComponent = class RtCopyCellComponent {
125
130
  isSignal: true,
126
131
  isRequired: false,
127
132
  transformFunction: null
133
+ },
134
+ framed: {
135
+ classPropertyName: "framed",
136
+ publicName: "framed",
137
+ isSignal: true,
138
+ isRequired: false,
139
+ transformFunction: null
128
140
  }
129
141
  },
130
142
  host: {
@@ -132,12 +144,15 @@ var RtCopyCellComponent = class RtCopyCellComponent {
132
144
  "mouseenter": "measureTruncation()",
133
145
  "focusin": "measureTruncation()"
134
146
  },
135
- properties: { "class.rt-copy-cell--static": "!revealOnHover()" },
147
+ properties: {
148
+ "class.rt-copy-cell--static": "!revealOnHover() || framed()",
149
+ "class.rt-copy-cell--framed": "framed()"
150
+ },
136
151
  classAttribute: "rt-copy-cell"
137
152
  },
138
153
  ngImport: i0,
139
154
  template: "<ng-container rtBlock=\"rt-copy-cell\">\n <span rtElem=\"content\" qa-dataid=\"copy-cell-content\" [rtTooltip]=\"hint()\"><ng-content /></span>\n <rt-icon-button\n rtElem=\"button\"\n qa-dataid=\"copy-cell-button\"\n size=\"sm\"\n [icon]=\"iconName()\"\n [variant]=\"variant()\"\n [ariaLabel]=\"actionLabel()\"\n [tooltip]=\"actionLabel()\"\n (clicked)=\"onCopy($event)\" />\n</ng-container>\n",
140
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-copy-cell{--rt-copy-cell-transition-duration: var(--rt-duration-fast);display:inline-flex;min-width:0;align-items:center;gap:var(--rt-space-xs)}.rt-copy-cell__content{overflow:hidden;min-width:0;text-overflow:ellipsis;white-space:nowrap}.rt-copy-cell__button{flex:0 0 auto;opacity:0;transition:opacity var(--rt-copy-cell-transition-duration) ease}.rt-copy-cell:hover .rt-copy-cell__button,.rt-copy-cell:focus-within .rt-copy-cell__button{opacity:1}.rt-copy-cell--static .rt-copy-cell__button{opacity:1}.rt-table tbody tr:hover .rt-copy-cell__button,.rt-table tbody tr:focus-within .rt-copy-cell__button{opacity:1}}\n"],
155
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-copy-cell{--rt-copy-cell-transition-duration: var(--rt-duration-fast);display:inline-flex;min-width:0;align-items:center;gap:var(--rt-space-xs)}.rt-copy-cell__content{overflow:hidden;min-width:0;text-overflow:ellipsis;white-space:nowrap}.rt-copy-cell__button{flex:0 0 auto;opacity:0;transition:opacity var(--rt-copy-cell-transition-duration) ease}.rt-copy-cell:hover .rt-copy-cell__button,.rt-copy-cell:focus-within .rt-copy-cell__button{opacity:1}.rt-copy-cell--static .rt-copy-cell__button{opacity:1}.rt-copy-cell--framed{display:flex;width:100%;box-sizing:border-box;padding:var(--rt-space-1) var(--rt-space-1) var(--rt-space-1) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);gap:var(--rt-space-sm);line-height:var(--rt-leading-normal)}.rt-copy-cell--framed .rt-copy-cell__content{flex:1 1 auto;overflow-wrap:anywhere;text-overflow:clip;white-space:normal}.rt-table tbody tr:hover .rt-copy-cell__button,.rt-table tbody tr:focus-within .rt-copy-cell__button{opacity:1}}\n"],
141
156
  dependencies: [
142
157
  {
143
158
  kind: "component",
@@ -217,12 +232,13 @@ i0.ɵɵngDeclareClassMetadata({
217
232
  ],
218
233
  host: {
219
234
  class: BEM_BLOCK$5,
220
- "[class.rt-copy-cell--static]": "!revealOnHover()",
235
+ "[class.rt-copy-cell--static]": "!revealOnHover() || framed()",
236
+ "[class.rt-copy-cell--framed]": "framed()",
221
237
  "(mouseenter)": "measureTruncation()",
222
238
  "(focusin)": "measureTruncation()"
223
239
  },
224
240
  template: "<ng-container rtBlock=\"rt-copy-cell\">\n <span rtElem=\"content\" qa-dataid=\"copy-cell-content\" [rtTooltip]=\"hint()\"><ng-content /></span>\n <rt-icon-button\n rtElem=\"button\"\n qa-dataid=\"copy-cell-button\"\n size=\"sm\"\n [icon]=\"iconName()\"\n [variant]=\"variant()\"\n [ariaLabel]=\"actionLabel()\"\n [tooltip]=\"actionLabel()\"\n (clicked)=\"onCopy($event)\" />\n</ng-container>\n",
225
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-copy-cell{--rt-copy-cell-transition-duration: var(--rt-duration-fast);display:inline-flex;min-width:0;align-items:center;gap:var(--rt-space-xs)}.rt-copy-cell__content{overflow:hidden;min-width:0;text-overflow:ellipsis;white-space:nowrap}.rt-copy-cell__button{flex:0 0 auto;opacity:0;transition:opacity var(--rt-copy-cell-transition-duration) ease}.rt-copy-cell:hover .rt-copy-cell__button,.rt-copy-cell:focus-within .rt-copy-cell__button{opacity:1}.rt-copy-cell--static .rt-copy-cell__button{opacity:1}.rt-table tbody tr:hover .rt-copy-cell__button,.rt-table tbody tr:focus-within .rt-copy-cell__button{opacity:1}}\n"]
241
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-copy-cell{--rt-copy-cell-transition-duration: var(--rt-duration-fast);display:inline-flex;min-width:0;align-items:center;gap:var(--rt-space-xs)}.rt-copy-cell__content{overflow:hidden;min-width:0;text-overflow:ellipsis;white-space:nowrap}.rt-copy-cell__button{flex:0 0 auto;opacity:0;transition:opacity var(--rt-copy-cell-transition-duration) ease}.rt-copy-cell:hover .rt-copy-cell__button,.rt-copy-cell:focus-within .rt-copy-cell__button{opacity:1}.rt-copy-cell--static .rt-copy-cell__button{opacity:1}.rt-copy-cell--framed{display:flex;width:100%;box-sizing:border-box;padding:var(--rt-space-1) var(--rt-space-1) var(--rt-space-1) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);gap:var(--rt-space-sm);line-height:var(--rt-leading-normal)}.rt-copy-cell--framed .rt-copy-cell__content{flex:1 1 auto;overflow-wrap:anywhere;text-overflow:clip;white-space:normal}.rt-table tbody tr:hover .rt-copy-cell__button,.rt-table tbody tr:focus-within .rt-copy-cell__button{opacity:1}}\n"]
226
242
  }]
227
243
  }],
228
244
  ctorParameters: () => [],
@@ -250,27 +266,25 @@ i0.ɵɵngDeclareClassMetadata({
250
266
  alias: "revealOnHover",
251
267
  required: false
252
268
  }]
269
+ }],
270
+ framed: [{
271
+ type: i0.Input,
272
+ args: [{
273
+ isSignal: true,
274
+ alias: "framed",
275
+ required: false
276
+ }]
253
277
  }]
254
278
  }
255
279
  });
256
280
  function nextSort(current, propertyName) {
257
- if (current === null || current.propertyName !== propertyName) return {
258
- propertyName,
259
- sortDirection: EListSortOrder.ASC
260
- };
261
- if (current.sortDirection === EListSortOrder.ASC) return {
262
- propertyName,
263
- sortDirection: EListSortOrder.DESC
264
- };
265
- return null;
281
+ return rtNextSort(current, propertyName, "clears");
266
282
  }
267
283
  function sortDirectionOf(current, propertyName) {
268
- return current !== null && current.propertyName === propertyName ? current.sortDirection : null;
284
+ return rtSortDirectionOf(current, propertyName);
269
285
  }
270
286
  function ariaSortOf(current, propertyName) {
271
- const direction = sortDirectionOf(current, propertyName);
272
- if (direction === null) return "none";
273
- return direction === EListSortOrder.ASC ? "ascending" : "descending";
287
+ return rtAriaSortOf(current, propertyName);
274
288
  }
275
289
  function isColumnSortable(config, key) {
276
290
  const column = config.find((candidate) => candidate.key === key);
@@ -307,30 +321,15 @@ function withoutLockedHidden(config, settings) {
307
321
  function filterValueOf(value) {
308
322
  return value instanceof Date ? value.toISOString() : value ?? "";
309
323
  }
324
+ const RT_TABLE_EMPTY_MODE = "empty-string";
310
325
  function filterOf(filters, propertyName) {
311
- return filters.find((filter) => filter.propertyName === propertyName) ?? null;
326
+ return rtFilterOf(filters, propertyName);
312
327
  }
313
328
  function filtersWithValue(filters, propertyName, operatorType, value) {
314
- const current = filterOf(filters, propertyName);
315
- if (value === "") return current === null ? filters : filters.filter((filter) => filter !== current);
316
- if (current === null) return [...filters, {
317
- propertyName,
318
- operatorType,
319
- value
320
- }];
321
- if (current.value === value) return filters;
322
- return filters.map((filter) => filter === current ? {
323
- ...filter,
324
- value
325
- } : filter);
329
+ return rtFiltersWithValue(filters, propertyName, operatorType, value, RT_TABLE_EMPTY_MODE);
326
330
  }
327
331
  function filtersWithOperator(filters, propertyName, operatorType) {
328
- const current = filterOf(filters, propertyName);
329
- if (current === null || current.operatorType === operatorType) return filters;
330
- return filters.map((filter) => filter === current ? {
331
- ...filter,
332
- operatorType
333
- } : filter);
332
+ return rtFiltersWithOperator(filters, propertyName, operatorType, "", RT_TABLE_EMPTY_MODE);
334
333
  }
335
334
  function startOperatorOf(allowed, start) {
336
335
  if (start !== void 0) return start;