@rt-tools/ui-kit-v2 0.39.0 → 0.39.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/rt-tools-ui-kit-v2-aside.mjs +4 -4
- package/fesm2022/rt-tools-ui-kit-v2-aside.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs +14 -0
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +5 -5
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +48 -45
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +62 -9
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +93 -12
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs +4 -1
- package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-icon.mjs +10 -0
- package/fesm2022/rt-tools-ui-kit-v2-icon.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +76 -5
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-message.mjs +3 -3
- package/fesm2022/rt-tools-ui-kit-v2-message.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +4 -4
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs +37 -16
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-table-core.mjs +101 -0
- package/fesm2022/rt-tools-ui-kit-v2-table-core.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs +130 -42
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +41 -4
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +2 -0
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -1
- package/package.json +5 -1
- package/src/assets/icons-material/bookmark.fill.svg +5 -0
- package/src/assets/icons-material/bookmark.svg +5 -0
- package/src/assets/icons-material/flag.fill.svg +3 -0
- package/src/assets/icons-material/flag.svg +5 -0
- package/src/lib/components/aside/footer/CONTEXT.md +27 -0
- package/src/lib/components/aside/footer/rt-aside-footer.component.scss +8 -3
- package/src/lib/components/aside/header/rt-aside-header.component.scss +11 -3
- package/src/lib/components/button/rt-button.directive.scss +5 -1
- package/src/lib/components/data-table/CONTEXT.md +8 -2
- package/src/lib/components/date-picker/panel/rt-date-panel.component.scss +51 -2
- package/src/lib/components/date-range/panel/rt-date-range-panel.component.scss +95 -2
- package/src/lib/components/detail-list/CONTEXT.md +3 -0
- package/src/lib/components/detail-list/rt-detail-row.component.scss +1 -0
- package/src/lib/components/field/CONTEXT.md +4 -2
- package/src/lib/components/field/rt-field.component.scss +14 -0
- package/src/lib/components/form-control/CONTEXT.md +13 -8
- package/src/lib/components/input/rt-input.component.scss +4 -0
- package/src/lib/components/input-number/CONTEXT.md +8 -0
- package/src/lib/components/input-number/rt-input-number.component.scss +50 -0
- package/src/lib/components/message/CONTEXT.md +1 -1
- package/src/lib/components/message/rt-message.component.scss +2 -1
- package/src/lib/components/multiselect/rt-multiselect.component.scss +21 -0
- package/src/lib/components/service-block/stories/component/test-service-block-panel.component.scss +29 -0
- package/src/lib/components/stat-tile/CONTEXT.md +11 -6
- package/src/lib/components/stat-tile/rt-stat-tile.component.scss +21 -0
- package/src/lib/components/stat-tile/stories/component/test-stat-tile-matrix.component.scss +15 -0
- package/src/lib/components/table/CONTEXT.md +23 -0
- package/src/lib/components/table/copy-cell/CONTEXT.md +32 -0
- package/src/lib/components/table/copy-cell/rt-copy-cell.component.scss +26 -0
- package/src/lib/components/table/rt-table.component.scss +77 -22
- package/src/lib/components/table/stories/component/test-table-pinned.component.scss +76 -0
- package/src/lib/components/tabs/CONTEXT.md +18 -16
- package/src/lib/components/tabs/rt-tabs.component.scss +25 -1
- package/src/styles/_form.scss +10 -0
- package/src/styles/_primitives.scss +2 -0
- package/src/styles/_semantic.scss +11 -1
- package/src/styles/_theme-dark.scss +2 -0
- package/styles/tokens.css +95 -9
- package/types/rt-tools-ui-kit-v2-core.d.ts +28 -5
- package/types/rt-tools-ui-kit-v2-data-table.d.ts +23 -9
- package/types/rt-tools-ui-kit-v2-date-picker.d.ts +14 -0
- package/types/rt-tools-ui-kit-v2-form-control.d.ts +2 -2
- package/types/rt-tools-ui-kit-v2-input-number.d.ts +30 -2
- package/types/rt-tools-ui-kit-v2-message.d.ts +1 -1
- package/types/rt-tools-ui-kit-v2-stat-tile.d.ts +29 -6
- package/types/rt-tools-ui-kit-v2-table-core.d.ts +76 -0
- package/types/rt-tools-ui-kit-v2-table.d.ts +52 -18
- 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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { ChangeDetectionStrategy, Component, ViewEncapsulation, computed, inject, input } from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, ViewEncapsulation, booleanAttribute, computed, inject, input } from "@angular/core";
|
|
3
3
|
import { BlockDirective, ElemDirective, ModDirective } from "@rt-tools/core";
|
|
4
4
|
import { RT_KIT_LABELS, rtKitLabel } from "@rt-tools/ui-kit-v2/core";
|
|
5
5
|
import { RtIconComponent } from "@rt-tools/ui-kit-v2/icon";
|
|
@@ -15,18 +15,16 @@ function directionOf(percent) {
|
|
|
15
15
|
if (percent > 0) return "up";
|
|
16
16
|
return percent < 0 ? "down" : "flat";
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
return `${percent > 0 ? "+" : ""}${String(percent).replace(".", ",")} %`;
|
|
18
|
+
function signedNumber(value) {
|
|
19
|
+
return `${value > 0 ? "+" : ""}${String(value).replace(".", ",")}`;
|
|
21
20
|
}
|
|
22
|
-
function deltaRow(key, delta) {
|
|
23
|
-
|
|
24
|
-
const direction = directionOf(delta.percent);
|
|
21
|
+
function deltaRow(key, delta, percent, pointsText) {
|
|
22
|
+
const direction = directionOf(percent);
|
|
25
23
|
return {
|
|
26
24
|
key,
|
|
27
25
|
direction,
|
|
28
26
|
label: delta.label,
|
|
29
|
-
text:
|
|
27
|
+
text: delta.unit === "points" ? pointsText : `${signedNumber(percent)} %`,
|
|
30
28
|
baseline: delta.baseline ?? "",
|
|
31
29
|
icon: DIRECTION_ICONS[direction]
|
|
32
30
|
};
|
|
@@ -35,9 +33,11 @@ var RtStatTileComponent = class RtStatTileComponent {
|
|
|
35
33
|
constructor() {
|
|
36
34
|
this.#baselineParams = computed(() => ({ value: this.deltaPrimary()?.baseline ?? "" }), ...ngDevMode ? [{ debugName: "#baselineParams" }] : /* istanbul ignore next */ []);
|
|
37
35
|
this.#baselineLine = rtKitLabel("uiBaseline", this.#baselineParams);
|
|
36
|
+
this.#primaryPoints = rtKitLabel("uiPercentagePoints", computed(() => ({ value: signedNumber(this.deltaPrimary()?.percent ?? 0) })));
|
|
37
|
+
this.#yearPoints = rtKitLabel("uiPercentagePoints", computed(() => ({ value: signedNumber(this.deltaSecondary()?.percent ?? 0) })));
|
|
38
38
|
this.t = inject(RT_KIT_LABELS);
|
|
39
|
-
this.primaryDelta = computed(() => this.#visibleDelta(this.deltaPrimary()), ...ngDevMode ? [{ debugName: "primaryDelta" }] : /* istanbul ignore next */ []);
|
|
40
|
-
this.yearDelta = computed(() => this.#visibleDelta(this.deltaSecondary()), ...ngDevMode ? [{ debugName: "yearDelta" }] : /* istanbul ignore next */ []);
|
|
39
|
+
this.primaryDelta = computed(() => this.#visibleDelta("primary", this.deltaPrimary(), this.#primaryPoints()), ...ngDevMode ? [{ debugName: "primaryDelta" }] : /* istanbul ignore next */ []);
|
|
40
|
+
this.yearDelta = computed(() => this.#visibleDelta("year", this.deltaSecondary(), this.#yearPoints()), ...ngDevMode ? [{ debugName: "yearDelta" }] : /* istanbul ignore next */ []);
|
|
41
41
|
this.baselineText = computed(() => this.deltaPrimary()?.baseline ? this.#baselineLine() : "", ...ngDevMode ? [{ debugName: "baselineText" }] : /* istanbul ignore next */ []);
|
|
42
42
|
this.label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
|
|
43
43
|
this.value = input.required(...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
|
|
@@ -45,12 +45,18 @@ var RtStatTileComponent = class RtStatTileComponent {
|
|
|
45
45
|
this.deltaPrimary = input(null, ...ngDevMode ? [{ debugName: "deltaPrimary" }] : /* istanbul ignore next */ []);
|
|
46
46
|
this.deltaSecondary = input(null, ...ngDevMode ? [{ debugName: "deltaSecondary" }] : /* istanbul ignore next */ []);
|
|
47
47
|
this.hint = input(null, ...ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []);
|
|
48
|
+
this.growthIsWorse = input(false, {
|
|
49
|
+
...ngDevMode ? { debugName: "growthIsWorse" } : /* istanbul ignore next */ {},
|
|
50
|
+
transform: booleanAttribute
|
|
51
|
+
});
|
|
48
52
|
}
|
|
49
53
|
#baselineParams;
|
|
50
54
|
#baselineLine;
|
|
51
|
-
#
|
|
55
|
+
#primaryPoints;
|
|
56
|
+
#yearPoints;
|
|
57
|
+
#visibleDelta(key, delta, pointsText) {
|
|
52
58
|
if (!delta || delta.percent === null) return null;
|
|
53
|
-
return deltaRow(
|
|
59
|
+
return deltaRow(key, delta, delta.percent, pointsText);
|
|
54
60
|
}
|
|
55
61
|
static {
|
|
56
62
|
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
@@ -111,12 +117,19 @@ var RtStatTileComponent = class RtStatTileComponent {
|
|
|
111
117
|
isSignal: true,
|
|
112
118
|
isRequired: false,
|
|
113
119
|
transformFunction: null
|
|
120
|
+
},
|
|
121
|
+
growthIsWorse: {
|
|
122
|
+
classPropertyName: "growthIsWorse",
|
|
123
|
+
publicName: "growthIsWorse",
|
|
124
|
+
isSignal: true,
|
|
125
|
+
isRequired: false,
|
|
126
|
+
transformFunction: null
|
|
114
127
|
}
|
|
115
128
|
},
|
|
116
129
|
host: { classAttribute: "rt-stat-tile" },
|
|
117
130
|
ngImport: i0,
|
|
118
|
-
template: "<article rtBlock=\"rt-stat-tile\" qa-dataid=\"stat-tile\">\n <header rtElem=\"head\">\n <span rtElem=\"label\" qa-dataid=\"stat-tile-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <rt-icon rtElem=\"hint\" qa-dataid=\"stat-tile-hint\" name=\"info-circle\" size=\"xs\" [attr.title]=\"hintText\" />\n }\n </header>\n\n <div rtElem=\"value-row\">\n <p rtElem=\"value\" qa-dataid=\"stat-tile-value\">{{ value() }}</p>\n\n @if (primaryDelta(); as row) {\n <span\n rtElem=\"delta\"\n qa-dataid=\"stat-tile-delta\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n <rt-icon qa-dataid=\"stat-tile-delta-icon\" size=\"xs\" [name]=\"row.icon\" />\n <span qa-dataid=\"stat-tile-delta-percent\">{{ row.text }}</span>\n </span>\n }\n\n @if (yearDelta(); as row) {\n <span\n rtElem=\"year\"\n qa-dataid=\"stat-tile-year\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n {{ t().uiYearOverYear }} {{ row.text }}\n </span>\n }\n </div>\n\n @if (baselineText(); as text) {\n <p rtElem=\"baseline\" qa-dataid=\"stat-tile-baseline\">{{ text }}</p>\n }\n\n @if (secondary(); as text) {\n <p rtElem=\"secondary\" qa-dataid=\"stat-tile-secondary\">{{ text }}</p>\n }\n</article>\n",
|
|
119
|
-
styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-stat-tile{display:block}rt-stat-tile .rt-stat-tile{--rt-stat-tile-year-radius: var(--rt-radius-sm);display:flex;height:100%;flex-direction:column;gap:var(--rt-space-2)}rt-stat-tile .rt-stat-tile__head{display:flex;align-items:center;gap:var(--rt-space-1)}rt-stat-tile .rt-stat-tile__label{color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-semibold)}rt-stat-tile .rt-stat-tile__hint{color:var(--rt-color-text-muted);cursor:help}rt-stat-tile .rt-stat-tile__value-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--rt-space-4)}rt-stat-tile .rt-stat-tile__value{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-3xl);font-weight:var(--rt-font-weight-bold);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);gap:var(--rt-space-1);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta--direction--up{color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__delta--direction--down{color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__year{flex:0 0 auto;padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-stat-tile-year-radius);background:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:var(--rt-leading-none);white-space:nowrap}rt-stat-tile .rt-stat-tile__year--direction--up{background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__year--direction--down{background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__baseline{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);text-overflow:ellipsis;white-space:nowrap}rt-stat-tile .rt-stat-tile__secondary{overflow:hidden;margin:auto 0 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-overflow:ellipsis;white-space:nowrap}}\n"],
|
|
131
|
+
template: "<article rtBlock=\"rt-stat-tile\" qa-dataid=\"stat-tile\" [rtMod]=\"{ 'growth-worse': growthIsWorse() }\">\n <header rtElem=\"head\">\n <span rtElem=\"label\" qa-dataid=\"stat-tile-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <rt-icon rtElem=\"hint\" qa-dataid=\"stat-tile-hint\" name=\"info-circle\" size=\"xs\" [attr.title]=\"hintText\" />\n }\n </header>\n\n <div rtElem=\"value-row\">\n <p rtElem=\"value\" qa-dataid=\"stat-tile-value\">{{ value() }}</p>\n\n @if (primaryDelta(); as row) {\n <span\n rtElem=\"delta\"\n qa-dataid=\"stat-tile-delta\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n <rt-icon qa-dataid=\"stat-tile-delta-icon\" size=\"xs\" [name]=\"row.icon\" />\n <span qa-dataid=\"stat-tile-delta-percent\">{{ row.text }}</span>\n </span>\n }\n\n @if (yearDelta(); as row) {\n <span\n rtElem=\"year\"\n qa-dataid=\"stat-tile-year\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n {{ t().uiYearOverYear }} {{ row.text }}\n </span>\n }\n </div>\n\n @if (baselineText(); as text) {\n <p rtElem=\"baseline\" qa-dataid=\"stat-tile-baseline\">{{ text }}</p>\n }\n\n @if (secondary(); as text) {\n <p rtElem=\"secondary\" qa-dataid=\"stat-tile-secondary\">{{ text }}</p>\n }\n</article>\n",
|
|
132
|
+
styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-stat-tile{display:block}rt-stat-tile .rt-stat-tile{--rt-stat-tile-year-radius: var(--rt-radius-sm);display:flex;height:100%;flex-direction:column;gap:var(--rt-space-2)}rt-stat-tile .rt-stat-tile__head{display:flex;align-items:center;gap:var(--rt-space-1)}rt-stat-tile .rt-stat-tile__label{color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-semibold)}rt-stat-tile .rt-stat-tile__hint{color:var(--rt-color-text-muted);cursor:help}rt-stat-tile .rt-stat-tile__value-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--rt-space-4)}rt-stat-tile .rt-stat-tile__value{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-3xl);font-weight:var(--rt-font-weight-bold);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);gap:var(--rt-space-1);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta--direction--up{color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__delta--direction--down{color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__year{flex:0 0 auto;padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-stat-tile-year-radius);background:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:var(--rt-leading-none);white-space:nowrap}rt-stat-tile .rt-stat-tile__year--direction--up{background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__year--direction--down{background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__baseline{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);text-overflow:ellipsis;white-space:nowrap}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__delta--direction--up{color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__delta--direction--down{color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__year--direction--up{background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__year--direction--down{background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__secondary{overflow:hidden;margin:auto 0 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-overflow:ellipsis;white-space:nowrap}}\n"],
|
|
120
133
|
dependencies: [
|
|
121
134
|
{
|
|
122
135
|
kind: "component",
|
|
@@ -174,8 +187,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
174
187
|
ModDirective
|
|
175
188
|
],
|
|
176
189
|
host: { class: BEM_BLOCK },
|
|
177
|
-
template: "<article rtBlock=\"rt-stat-tile\" qa-dataid=\"stat-tile\">\n <header rtElem=\"head\">\n <span rtElem=\"label\" qa-dataid=\"stat-tile-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <rt-icon rtElem=\"hint\" qa-dataid=\"stat-tile-hint\" name=\"info-circle\" size=\"xs\" [attr.title]=\"hintText\" />\n }\n </header>\n\n <div rtElem=\"value-row\">\n <p rtElem=\"value\" qa-dataid=\"stat-tile-value\">{{ value() }}</p>\n\n @if (primaryDelta(); as row) {\n <span\n rtElem=\"delta\"\n qa-dataid=\"stat-tile-delta\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n <rt-icon qa-dataid=\"stat-tile-delta-icon\" size=\"xs\" [name]=\"row.icon\" />\n <span qa-dataid=\"stat-tile-delta-percent\">{{ row.text }}</span>\n </span>\n }\n\n @if (yearDelta(); as row) {\n <span\n rtElem=\"year\"\n qa-dataid=\"stat-tile-year\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n {{ t().uiYearOverYear }} {{ row.text }}\n </span>\n }\n </div>\n\n @if (baselineText(); as text) {\n <p rtElem=\"baseline\" qa-dataid=\"stat-tile-baseline\">{{ text }}</p>\n }\n\n @if (secondary(); as text) {\n <p rtElem=\"secondary\" qa-dataid=\"stat-tile-secondary\">{{ text }}</p>\n }\n</article>\n",
|
|
178
|
-
styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-stat-tile{display:block}rt-stat-tile .rt-stat-tile{--rt-stat-tile-year-radius: var(--rt-radius-sm);display:flex;height:100%;flex-direction:column;gap:var(--rt-space-2)}rt-stat-tile .rt-stat-tile__head{display:flex;align-items:center;gap:var(--rt-space-1)}rt-stat-tile .rt-stat-tile__label{color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-semibold)}rt-stat-tile .rt-stat-tile__hint{color:var(--rt-color-text-muted);cursor:help}rt-stat-tile .rt-stat-tile__value-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--rt-space-4)}rt-stat-tile .rt-stat-tile__value{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-3xl);font-weight:var(--rt-font-weight-bold);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);gap:var(--rt-space-1);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta--direction--up{color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__delta--direction--down{color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__year{flex:0 0 auto;padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-stat-tile-year-radius);background:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:var(--rt-leading-none);white-space:nowrap}rt-stat-tile .rt-stat-tile__year--direction--up{background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__year--direction--down{background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__baseline{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);text-overflow:ellipsis;white-space:nowrap}rt-stat-tile .rt-stat-tile__secondary{overflow:hidden;margin:auto 0 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-overflow:ellipsis;white-space:nowrap}}\n"]
|
|
190
|
+
template: "<article rtBlock=\"rt-stat-tile\" qa-dataid=\"stat-tile\" [rtMod]=\"{ 'growth-worse': growthIsWorse() }\">\n <header rtElem=\"head\">\n <span rtElem=\"label\" qa-dataid=\"stat-tile-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <rt-icon rtElem=\"hint\" qa-dataid=\"stat-tile-hint\" name=\"info-circle\" size=\"xs\" [attr.title]=\"hintText\" />\n }\n </header>\n\n <div rtElem=\"value-row\">\n <p rtElem=\"value\" qa-dataid=\"stat-tile-value\">{{ value() }}</p>\n\n @if (primaryDelta(); as row) {\n <span\n rtElem=\"delta\"\n qa-dataid=\"stat-tile-delta\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n <rt-icon qa-dataid=\"stat-tile-delta-icon\" size=\"xs\" [name]=\"row.icon\" />\n <span qa-dataid=\"stat-tile-delta-percent\">{{ row.text }}</span>\n </span>\n }\n\n @if (yearDelta(); as row) {\n <span\n rtElem=\"year\"\n qa-dataid=\"stat-tile-year\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n {{ t().uiYearOverYear }} {{ row.text }}\n </span>\n }\n </div>\n\n @if (baselineText(); as text) {\n <p rtElem=\"baseline\" qa-dataid=\"stat-tile-baseline\">{{ text }}</p>\n }\n\n @if (secondary(); as text) {\n <p rtElem=\"secondary\" qa-dataid=\"stat-tile-secondary\">{{ text }}</p>\n }\n</article>\n",
|
|
191
|
+
styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-stat-tile{display:block}rt-stat-tile .rt-stat-tile{--rt-stat-tile-year-radius: var(--rt-radius-sm);display:flex;height:100%;flex-direction:column;gap:var(--rt-space-2)}rt-stat-tile .rt-stat-tile__head{display:flex;align-items:center;gap:var(--rt-space-1)}rt-stat-tile .rt-stat-tile__label{color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-semibold)}rt-stat-tile .rt-stat-tile__hint{color:var(--rt-color-text-muted);cursor:help}rt-stat-tile .rt-stat-tile__value-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--rt-space-4)}rt-stat-tile .rt-stat-tile__value{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-3xl);font-weight:var(--rt-font-weight-bold);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);gap:var(--rt-space-1);line-height:var(--rt-leading-none)}rt-stat-tile .rt-stat-tile__delta--direction--up{color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__delta--direction--down{color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__year{flex:0 0 auto;padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-stat-tile-year-radius);background:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:var(--rt-leading-none);white-space:nowrap}rt-stat-tile .rt-stat-tile__year--direction--up{background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__year--direction--down{background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile__baseline{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);text-overflow:ellipsis;white-space:nowrap}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__delta--direction--up{color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__delta--direction--down{color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__year--direction--up{background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}rt-stat-tile .rt-stat-tile--growth-worse .rt-stat-tile__year--direction--down{background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}rt-stat-tile .rt-stat-tile__secondary{overflow:hidden;margin:auto 0 0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);text-overflow:ellipsis;white-space:nowrap}}\n"]
|
|
179
192
|
}]
|
|
180
193
|
}],
|
|
181
194
|
propDecorators: {
|
|
@@ -226,6 +239,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
226
239
|
alias: "hint",
|
|
227
240
|
required: false
|
|
228
241
|
}]
|
|
242
|
+
}],
|
|
243
|
+
growthIsWorse: [{
|
|
244
|
+
type: i0.Input,
|
|
245
|
+
args: [{
|
|
246
|
+
isSignal: true,
|
|
247
|
+
alias: "growthIsWorse",
|
|
248
|
+
required: false
|
|
249
|
+
}]
|
|
229
250
|
}]
|
|
230
251
|
}
|
|
231
252
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rt-tools-ui-kit-v2-stat-tile.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/stat-tile/rt-stat-tile.component.ts","../../../projects/ui-kit-v2/src/lib/components/stat-tile/rt-stat-tile.component.html"],"sourcesContent":["import { computed, inject, input, ChangeDetectionStrategy, Component, InputSignal, Signal, ViewEncapsulation } 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 { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { IRtStatTile } from './rt-stat-tile.model';\n\nconst BEM_BLOCK: string = 'rt-stat-tile';\n\n/** Строка дельты в готовом к рендеру виде — вся ветвистость решена здесь. */\ninterface IDeltaRow {\n key: string;\n label: string;\n text: string;\n baseline: string;\n direction: IRtStatTile.Direction;\n icon: IRtIcon.Name;\n}\n\nconst DIRECTION_ICONS: Readonly<Record<IRtStatTile.Direction, IRtIcon.Name>> = Object.freeze({\n up: 'arrow-up',\n down: 'arrow-down',\n flat: 'minus',\n unknown: 'minus',\n});\n\nfunction directionOf(percent: number | null): IRtStatTile.Direction {\n if (percent === null) {\n return 'unknown';\n }\n if (percent > 0) {\n return 'up';\n }\n return percent < 0 ? 'down' : 'flat';\n}\n\n/**\n * Нулевая база — «—»: «+100 %» от нуля вводит в заблуждение, «∞» нечитаемо.\n * Дробная часть — через запятую, как и остальные проценты в интерфейсе: кит\n * форматирует сам, потому что не может зависеть от логики приложений.\n */\nfunction percentText(percent: number | null): string {\n if (percent === null) {\n return '—';\n }\n const sign: string = percent > 0 ? '+' : '';\n return `${sign}${String(percent).replace('.', ',')} %`;\n}\n\nfunction deltaRow(key: string, delta: IRtStatTile.Delta | null): IDeltaRow | null {\n if (!delta) {\n return null;\n }\n const direction: IRtStatTile.Direction = directionOf(delta.percent);\n return {\n key,\n direction,\n label: delta.label,\n text: percentText(delta.percent),\n baseline: delta.baseline ?? '',\n icon: DIRECTION_ICONS[direction],\n };\n}\n\n/**\n * KPI-плитка: крупное значение, необязательная вторичная строка и до двух строк\n * сравнения (к предыдущему периоду и к прошлому году).\n *\n * Компонент ничего не считает и не форматирует: `value` и `baseline` приходят\n * готовыми строками, `percent` — посчитанным числом. Так плитка не знает ни про\n * валюты, ни про методику метрик и остаётся общей для любого домена.\n *\n * Нулевая база показывается как «—»: «+100 %» от нуля вводит в заблуждение, а\n * «∞» нечитаемо.\n */\n@Component({\n selector: 'rt-stat-tile',\n templateUrl: './rt-stat-tile.component.html',\n styleUrl: './rt-stat-tile.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtStatTileComponent {\n /**\n * Значение базы уходит параметром перевода, а не подставляется в готовую\n * строку: Transloco интерполирует `{{value}}` в момент перевода, и к\n * возвращённой строке подставлять уже нечего — от подписи оставалась одна\n * приставка («было», «was»), а само число пропадало.\n */\n readonly #baselineParams: Signal<Record<string, string>> = computed((): Record<string, string> => ({\n value: this.deltaPrimary()?.baseline ?? '',\n }));\n\n readonly #baselineLine: Signal<string> = rtKitLabel('uiBaseline', this.#baselineParams);\n\n /**\n * Дельта к предыдущему периоду — рядом со значением. Пустая база (`percent`\n * = null) не рисуется вовсе: «—» рядом с цифрой читается как поломка, а не\n * как «сравнивать не с чем».\n */\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly primaryDelta: Signal<IDeltaRow | null> = computed((): IDeltaRow | null => this.#visibleDelta(this.deltaPrimary()));\n\n /** Годовое сравнение — отдельным компактным чипом, без слов в строке. */\n protected readonly yearDelta: Signal<IDeltaRow | null> = computed((): IDeltaRow | null => this.#visibleDelta(this.deltaSecondary()));\n\n /**\n * База сравнения — короткой строкой «было X» и ничем больше: всё остальное\n * (занятость, состав воронки) живёт во вторичной строке, иначе одна плитка\n * растягивает ряд под самую длинную подпись.\n */\n protected readonly baselineText: Signal<string> = computed((): string => (this.deltaPrimary()?.baseline ? this.#baselineLine() : ''));\n\n /** Подпись метрики. */\n public readonly label: InputSignal<string> = input.required<string>();\n\n /** Уже отформатированное значение — плитка его не преобразует. */\n public readonly value: InputSignal<string> = input.required<string>();\n\n /** Вторичная строка под значением (занятость, доля оценки и т.п.). */\n public readonly secondary: InputSignal<string | null> = input<string | null>(null);\n\n /** Сравнение с предыдущим равным периодом. */\n public readonly deltaPrimary: InputSignal<IRtStatTile.Delta | null> = input<IRtStatTile.Delta | null>(null);\n\n /** Сравнение с тем же периодом год назад. */\n public readonly deltaSecondary: InputSignal<IRtStatTile.Delta | null> = input<IRtStatTile.Delta | null>(null);\n\n /** Пояснение методики — нативный title на иконке подсказки. */\n public readonly hint: InputSignal<string | null> = input<string | null>(null);\n\n #visibleDelta(delta: IRtStatTile.Delta | null): IDeltaRow | null {\n if (!delta || delta.percent === null) {\n return null;\n }\n return deltaRow('primary', delta);\n }\n}\n","<article rtBlock=\"rt-stat-tile\" qa-dataid=\"stat-tile\">\n <header rtElem=\"head\">\n <span rtElem=\"label\" qa-dataid=\"stat-tile-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <rt-icon rtElem=\"hint\" qa-dataid=\"stat-tile-hint\" name=\"info-circle\" size=\"xs\" [attr.title]=\"hintText\" />\n }\n </header>\n\n <div rtElem=\"value-row\">\n <p rtElem=\"value\" qa-dataid=\"stat-tile-value\">{{ value() }}</p>\n\n @if (primaryDelta(); as row) {\n <span\n rtElem=\"delta\"\n qa-dataid=\"stat-tile-delta\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n <rt-icon qa-dataid=\"stat-tile-delta-icon\" size=\"xs\" [name]=\"row.icon\" />\n <span qa-dataid=\"stat-tile-delta-percent\">{{ row.text }}</span>\n </span>\n }\n\n @if (yearDelta(); as row) {\n <span\n rtElem=\"year\"\n qa-dataid=\"stat-tile-year\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n {{ t().uiYearOverYear }} {{ row.text }}\n </span>\n }\n </div>\n\n @if (baselineText(); as text) {\n <p rtElem=\"baseline\" qa-dataid=\"stat-tile-baseline\">{{ text }}</p>\n }\n\n @if (secondary(); as text) {\n <p rtElem=\"secondary\" qa-dataid=\"stat-tile-secondary\">{{ text }}</p>\n }\n</article>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rt-tools-ui-kit-v2-stat-tile.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/stat-tile/rt-stat-tile.component.ts","../../../projects/ui-kit-v2/src/lib/components/stat-tile/rt-stat-tile.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n computed,\n inject,\n input,\n ChangeDetectionStrategy,\n Component,\n InputSignal,\n InputSignalWithTransform,\n Signal,\n ViewEncapsulation,\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 { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { IRtStatTile } from './rt-stat-tile.model';\n\nconst BEM_BLOCK: string = 'rt-stat-tile';\n\n/** Строка дельты в готовом к рендеру виде — вся ветвистость решена здесь. */\ninterface IDeltaRow {\n key: string;\n label: string;\n text: string;\n baseline: string;\n direction: IRtStatTile.Direction;\n icon: IRtIcon.Name;\n}\n\nconst DIRECTION_ICONS: Readonly<Record<IRtStatTile.Direction, IRtIcon.Name>> = Object.freeze({\n up: 'arrow-up',\n down: 'arrow-down',\n flat: 'minus',\n unknown: 'minus',\n});\n\nfunction directionOf(percent: number | null): IRtStatTile.Direction {\n if (percent === null) {\n return 'unknown';\n }\n if (percent > 0) {\n return 'up';\n }\n return percent < 0 ? 'down' : 'flat';\n}\n\n/**\n * Число со знаком роста. Дробная часть — через запятую, как и остальные\n * проценты в интерфейсе: кит форматирует сам, потому что не может зависеть от\n * логики приложений.\n */\nfunction signedNumber(value: number): string {\n const sign: string = value > 0 ? '+' : '';\n return `${sign}${String(value).replace('.', ',')}`;\n}\n\n/**\n * Строка изменения с известной базой. Пункты пишутся переведённой подписью\n * («+10 п.п.», «+10 pp») — её текст приходит готовым, проценты кит дописывает сам.\n */\nfunction deltaRow(key: string, delta: IRtStatTile.Delta, percent: number, pointsText: string): IDeltaRow {\n const direction: IRtStatTile.Direction = directionOf(percent);\n return {\n key,\n direction,\n label: delta.label,\n text: delta.unit === 'points' ? pointsText : `${signedNumber(percent)} %`,\n baseline: delta.baseline ?? '',\n icon: DIRECTION_ICONS[direction],\n };\n}\n\n/**\n * KPI-плитка: крупное значение, необязательная вторичная строка и до двух строк\n * сравнения (к предыдущему периоду и к прошлому году).\n *\n * Компонент ничего не считает и не форматирует: `value` и `baseline` приходят\n * готовыми строками, `percent` — посчитанным числом. Так плитка не знает ни про\n * валюты, ни про методику метрик и остаётся общей для любого домена.\n *\n * Строка с неизвестной базой не рисуется: «+100 %» от нуля вводит в\n * заблуждение, а «—» рядом с цифрой читается как поломка.\n *\n * Цвет строки говорит, хорошо изменение или плохо, а стрелка — только знак\n * числа: у метрики, где рост хуже (отмены, потерянная выручка), рост красный и\n * со стрелкой вверх.\n */\n@Component({\n selector: 'rt-stat-tile',\n templateUrl: './rt-stat-tile.component.html',\n styleUrl: './rt-stat-tile.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtStatTileComponent {\n /**\n * Значение базы уходит параметром перевода, а не подставляется в готовую\n * строку: Transloco интерполирует `{{value}}` в момент перевода, и к\n * возвращённой строке подставлять уже нечего — от подписи оставалась одна\n * приставка («было», «was»), а само число пропадало.\n */\n readonly #baselineParams: Signal<Record<string, string>> = computed((): Record<string, string> => ({\n value: this.deltaPrimary()?.baseline ?? '',\n }));\n\n readonly #baselineLine: Signal<string> = rtKitLabel('uiBaseline', this.#baselineParams);\n\n /** Пункты — тоже параметром подписи: порядок числа и единицы знает язык, а не кит. */\n readonly #primaryPoints: Signal<string> = rtKitLabel(\n 'uiPercentagePoints',\n computed((): Record<string, string> => ({ value: signedNumber(this.deltaPrimary()?.percent ?? 0) }))\n );\n\n readonly #yearPoints: Signal<string> = rtKitLabel(\n 'uiPercentagePoints',\n computed((): Record<string, string> => ({ value: signedNumber(this.deltaSecondary()?.percent ?? 0) }))\n );\n\n /** Подписи кита для шаблона — приставка годового чипа. */\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n /**\n * Дельта к предыдущему периоду — рядом со значением. Пустая база (`percent`\n * = null) не рисуется вовсе: «—» рядом с цифрой читается как поломка, а не\n * как «сравнивать не с чем».\n */\n protected readonly primaryDelta: Signal<IDeltaRow | null> = computed((): IDeltaRow | null =>\n this.#visibleDelta('primary', this.deltaPrimary(), this.#primaryPoints())\n );\n\n /** Годовое сравнение — отдельным компактным чипом, без слов в строке. */\n protected readonly yearDelta: Signal<IDeltaRow | null> = computed((): IDeltaRow | null =>\n this.#visibleDelta('year', this.deltaSecondary(), this.#yearPoints())\n );\n\n /**\n * База сравнения — короткой строкой «было X» и ничем больше: всё остальное\n * (занятость, состав воронки) живёт во вторичной строке, иначе одна плитка\n * растягивает ряд под самую длинную подпись.\n */\n protected readonly baselineText: Signal<string> = computed((): string => (this.deltaPrimary()?.baseline ? this.#baselineLine() : ''));\n\n /** Подпись метрики. */\n public readonly label: InputSignal<string> = input.required<string>();\n\n /** Уже отформатированное значение — плитка его не преобразует. */\n public readonly value: InputSignal<string> = input.required<string>();\n\n /** Вторичная строка под значением (занятость, доля оценки и т.п.). */\n public readonly secondary: InputSignal<string | null> = input<string | null>(null);\n\n /** Сравнение с предыдущим равным периодом. */\n public readonly deltaPrimary: InputSignal<IRtStatTile.Delta | null> = input<IRtStatTile.Delta | null>(null);\n\n /** Сравнение с тем же периодом год назад. */\n public readonly deltaSecondary: InputSignal<IRtStatTile.Delta | null> = input<IRtStatTile.Delta | null>(null);\n\n /** Пояснение методики — нативный title на иконке подсказки. */\n public readonly hint: InputSignal<string | null> = input<string | null>(null);\n\n /**\n * Рост метрики плох для владельца (отмены, потерянная выручка): рост обеих\n * строк красный, падение зелёное. Стрелка по-прежнему показывает знак числа.\n */\n public readonly growthIsWorse: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n #visibleDelta(key: string, delta: IRtStatTile.Delta | null, pointsText: string): IDeltaRow | null {\n if (!delta || delta.percent === null) {\n return null;\n }\n return deltaRow(key, delta, delta.percent, pointsText);\n }\n}\n","<article rtBlock=\"rt-stat-tile\" qa-dataid=\"stat-tile\" [rtMod]=\"{ 'growth-worse': growthIsWorse() }\">\n <header rtElem=\"head\">\n <span rtElem=\"label\" qa-dataid=\"stat-tile-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <rt-icon rtElem=\"hint\" qa-dataid=\"stat-tile-hint\" name=\"info-circle\" size=\"xs\" [attr.title]=\"hintText\" />\n }\n </header>\n\n <div rtElem=\"value-row\">\n <p rtElem=\"value\" qa-dataid=\"stat-tile-value\">{{ value() }}</p>\n\n @if (primaryDelta(); as row) {\n <span\n rtElem=\"delta\"\n qa-dataid=\"stat-tile-delta\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n <rt-icon qa-dataid=\"stat-tile-delta-icon\" size=\"xs\" [name]=\"row.icon\" />\n <span qa-dataid=\"stat-tile-delta-percent\">{{ row.text }}</span>\n </span>\n }\n\n @if (yearDelta(); as row) {\n <span\n rtElem=\"year\"\n qa-dataid=\"stat-tile-year\"\n [attr.data-direction]=\"row.direction\"\n [rtMod]=\"{ direction: row.direction }\"\n [attr.title]=\"row.label\">\n {{ t().uiYearOverYear }} {{ row.text }}\n </span>\n }\n </div>\n\n @if (baselineText(); as text) {\n <p rtElem=\"baseline\" qa-dataid=\"stat-tile-baseline\">{{ text }}</p>\n }\n\n @if (secondary(); as text) {\n <p rtElem=\"secondary\" qa-dataid=\"stat-tile-secondary\">{{ text }}</p>\n }\n</article>\n"],"mappings":";;;;;AAqBA,MAAM,YAAoB;AAY1B,MAAM,kBAAyE,OAAO,OAAO;CACzF,IAAI;CACJ,MAAM;CACN,MAAM;CACN,SAAS;AACZ,CAAA;AAED,SAAS,YAAY,SAAsB;CACvC,IAAI,YAAY,MACZ,OAAO;CAEX,IAAI,UAAU,GACV,OAAO;CAEX,OAAO,UAAU,IAAI,SAAS;AAClC;AAOA,SAAS,aAAa,OAAa;CAE/B,OAAO,GADc,QAAQ,IAAI,MAAM,KACtB,OAAO,KAAK,CAAC,CAAC,QAAQ,KAAK,GAAG;AACnD;AAMA,SAAS,SAAS,KAAa,OAA0B,SAAiB,YAAkB;CACxF,MAAM,YAAmC,YAAY,OAAO;CAC5D,OAAO;EACH;EACA;EACA,OAAO,MAAM;EACb,MAAM,MAAM,SAAS,WAAW,aAAa,GAAG,aAAa,OAAO,EAAC;EACrE,UAAU,MAAM,YAAY;EAC5B,MAAM,gBAAgB;CACzB;AACL;AAkCA,IAAa,sBAAb,MAAa,oBAAmB;CAjBhC,cAAA;EAwBa,KAAA,kBAAkD,gBAAwC,EAC/F,OAAO,KAAK,aAAY,CAAE,EAAE,YAAY,GAC3C,IAAE,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;EAEM,KAAA,gBAAgC,WAAW,cAAc,KAAK,eAAe;EAG7E,KAAA,iBAAiC,WACtC,sBACA,gBAAwC,EAAE,OAAO,aAAa,KAAK,aAAY,CAAE,EAAE,WAAW,CAAC,EAAC,EAAG,CAAC;EAG/F,KAAA,cAA8B,WACnC,sBACA,gBAAwC,EAAE,OAAO,aAAa,KAAK,eAAc,CAAE,EAAE,WAAW,CAAC,EAAC,EAAG,CAAC;EAIvF,KAAA,IAA4B,OAAO,aAAa;EAOhD,KAAA,eAAyC,eACxD,KAAK,cAAc,WAAW,KAAK,aAAY,GAAI,KAAK,eAAc,CAAE,GAC3E,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;EAGkB,KAAA,YAAsC,eACrD,KAAK,cAAc,QAAQ,KAAK,eAAc,GAAI,KAAK,YAAW,CAAE,GACvE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAOkB,KAAA,eAA+B,eAAwB,KAAK,aAAY,CAAE,EAAE,WAAW,KAAK,cAAa,IAAK,IAAI,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;EAGrH,KAAA,QAA6B,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAGrD,KAAA,QAA6B,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAGrD,KAAA,YAAwC,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAGlE,KAAA,eAAsD,MAAgC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;EAG3F,KAAA,iBAAwD,MAAgC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;EAG7F,KAAA,OAAmC,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAM7D,KAAA,gBAAiE,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,gBAAA,+BAAA,CAAA;GAC/G,WAAW;EAAgB,CAAA;CASlC;CAzEY;CAIA;CAGA;CAKA;CAuDT,cAAc,KAAa,OAAiC,YAAkB;EAC1E,IAAI,CAAC,SAAS,MAAM,YAAY,MAC5B,OAAO;EAEX,OAAO,SAAS,KAAK,OAAO,MAAM,SAAS,UAAU;CACzD;CA/ES;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAmB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAAnB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAmB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,WAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,cAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,gBAAA;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,eAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,MAAA,EAAA,gBAAA,eAAA;GAAA,UAAA;GAAA,UC5GhC;GA2CA,QAAA,CAAA,ioFAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MDwDQ;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;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;;AAMP,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAjB/B,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAc,iBAGP,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IAEL;IACA;IACA;IACA;GACH;GAAA,MACK,EACF,OAAO,UACV;GAAA,UAAA;GAAA,QAAA,CAAA,ioFAAA;EAAA,CAAA"}
|
|
@@ -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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"}
|