@rt-tools/ui-kit-v2 0.34.2 → 0.34.3

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.
@@ -1,6 +1,7 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, Directive, ElementRef, ViewEncapsulation, booleanAttribute, effect, inject, input, numberAttribute, signal } from "@angular/core";
3
3
  import { FocusMonitor } from "@angular/cdk/a11y";
4
+ import { hasModifierKey } from "@angular/cdk/keycodes";
4
5
  import { Overlay, OverlayConfig, ScrollStrategyOptions } from "@angular/cdk/overlay";
5
6
  import { ComponentPortal } from "@angular/cdk/portal";
6
7
  import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
@@ -188,6 +189,15 @@ var RtTooltipDirective = class RtTooltipDirective {
188
189
  onClick() {
189
190
  if (this.hideOnClick() || this.#tooltipRef === null) this.hide();
190
191
  }
192
+ onKeydown(event) {
193
+ if (event.key !== "Escape" || hasModifierKey(event)) return;
194
+ const wasOpen = this.#tooltipRef !== null;
195
+ this.hide();
196
+ if (wasOpen) {
197
+ event.preventDefault();
198
+ event.stopPropagation();
199
+ }
200
+ }
191
201
  #syncText(text) {
192
202
  if (this.#tooltipRef === null) return;
193
203
  if (text.trim() === "") {
@@ -290,7 +300,8 @@ var RtTooltipDirective = class RtTooltipDirective {
290
300
  "mouseenter": "show()",
291
301
  "mouseleave": "hide()",
292
302
  "focusout": "hide()",
293
- "click": "onClick()"
303
+ "click": "onClick()",
304
+ "keydown": "onKeydown($event)"
294
305
  } },
295
306
  exportAs: ["rtTooltip"],
296
307
  ngImport: i0
@@ -311,7 +322,8 @@ i0.ɵɵngDeclareClassMetadata({
311
322
  "(mouseenter)": "show()",
312
323
  "(mouseleave)": "hide()",
313
324
  "(focusout)": "hide()",
314
- "(click)": "onClick()"
325
+ "(click)": "onClick()",
326
+ "(keydown)": "onKeydown($event)"
315
327
  }
316
328
  }]
317
329
  }],
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-tooltip.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/tooltip/rt-tooltip.component.ts","../../../projects/ui-kit-v2/src/lib/components/tooltip/rt-tooltip.logic.ts","../../../projects/ui-kit-v2/src/lib/components/tooltip/rt-tooltip.directive.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, signal, ViewEncapsulation, WritableSignal } from '@angular/core';\n\nconst BEM_BLOCK: string = 'rt-tooltip';\n\n/**\n * Презентационная панель tooltip'а. Рендерится директивой `[rtTooltip]` через\n * CDK `ComponentPortal` в overlay; текст выставляется императивно\n * (`ref.instance.text.set(...)`) — отдельного `input()` нет, т.к. компонент\n * создаётся вручную, а не в шаблоне.\n *\n * `ViewEncapsulation.None` — стили префиксованы `.rt-tooltip` (единообразно с\n * остальным common/ui). `role=\"tooltip\"` — вспомогательный ярлык для AT.\n */\n@Component({\n selector: 'rt-tooltip',\n template: '{{ text() }}',\n styleUrl: './rt-tooltip.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: BEM_BLOCK,\n role: 'tooltip',\n },\n})\nexport class RtTooltipComponent {\n public readonly text: WritableSignal<string> = signal<string>('');\n}\n","import { ConnectedPosition } from '@angular/cdk/overlay';\n\nimport { IRtTooltip } from './rt-tooltip.model';\n\n/** Зазор между подсказкой и host'ом. */\nconst TOOLTIP_GAP_PX: number = 6;\n\nconst POSITION_BY_PLACEMENT: Readonly<Record<IRtTooltip.Placement, ConnectedPosition>> = {\n top: { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -TOOLTIP_GAP_PX },\n bottom: { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: TOOLTIP_GAP_PX },\n left: { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -TOOLTIP_GAP_PX },\n right: { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: TOOLTIP_GAP_PX },\n};\n\nconst FALLBACKS: Readonly<Record<IRtTooltip.Placement, readonly IRtTooltip.Placement[]>> = {\n top: ['top', 'bottom'],\n bottom: ['bottom', 'top'],\n left: ['left', 'right', 'top', 'bottom'],\n right: ['right', 'left', 'top', 'bottom'],\n};\n\n/**\n * Позиции подсказки по порядку попыток: заданная сторона, противоположная, а у боковой ещё\n * верх и низ — на узком экране сбоку не помещается ни одна сторона, и подсказка, придвинутая\n * к краю, легла бы на сам host. Бока заданы логическими `start`/`end`: в письме справа налево\n * `left` встаёт справа — как и вся раскладка кита.\n */\n/**\n * Сторона подсказки: своя на элементе, иначе из настроек кита, иначе сверху, а под материальным\n * набором снизу, как у первого кита.\n */\nexport function tooltipPlacement(\n own: IRtTooltip.Placement | null,\n configured: IRtTooltip.Placement | null,\n materialPreset: boolean\n): IRtTooltip.Placement {\n return own ?? configured ?? (materialPreset ? 'bottom' : 'top');\n}\n\nexport function tooltipPositions(placement: IRtTooltip.Placement): ConnectedPosition[] {\n return FALLBACKS[placement].map((side: IRtTooltip.Placement): ConnectedPosition => ({ ...POSITION_BY_PLACEMENT[side] }));\n}\n\n/** Размеры узла, по которым судят об обрезке: видимая ширина и ширина содержимого. */\nexport interface IRtTooltipBox {\n readonly clientWidth: number;\n readonly scrollWidth: number;\n}\n\n/**\n * Текст узла обрезан: содержимое шире видимой коробки. Высота не судится — узел с видимым\n * переполнением по высоте показывает всё, и его подсказка только повторяла бы текст.\n */\nexport function isTooltipTextCut(box: IRtTooltipBox): boolean {\n return box.scrollWidth > box.clientWidth;\n}\n","import { FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';\nimport { NumberInput } from '@angular/cdk/coercion';\nimport {\n ConnectedPosition,\n FlexibleConnectedPositionStrategy,\n Overlay,\n OverlayConfig,\n OverlayRef,\n ScrollStrategyOptions,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport {\n booleanAttribute,\n ComponentRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n numberAttribute,\n OnDestroy,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { carryThemeScope, IRtKitConfig, materialPresetClassesOf, rtKitDefault } from '@rt-tools/ui-kit-v2/core';\nimport { RtTooltipComponent } from './rt-tooltip.component';\nimport { isTooltipTextCut, tooltipPlacement, tooltipPositions } from './rt-tooltip.logic';\nimport { IRtTooltip } from './rt-tooltip.model';\n\nconst SHOW_DELAY_MS: number = 300;\nconst VIEWPORT_MARGIN: number = 8;\n\n/**\n * Tooltip-директива поверх CDK Overlay. Показывает короткую подсказку\n * (`rtTooltip` = строка) на hover/focus host'а. Движок — CDK `flexibleConnectedTo`\n * (авто-flip у края viewport, reposition при скролле), так что подсказка не\n * режется `overflow: hidden` предками (таблицы, aside, карточки).\n *\n * Триггеры: `mouseenter` и фокус с клавиатуры показывают (с задержкой `SHOW_DELAY_MS`),\n * `mouseleave`/`focusout`/`click` прячут. Фокус, поставленный мышью или кодом, подсказку не\n * показывает, как у Material: меню возвращает фокус кнопке после выбора пункта мышью, и\n * подсказка кнопки всплывала поверх того, что пункт открыл. Пустой текст → no-op (директива\n * выключена), поэтому её можно безусловно вешать на icon-кнопки и включать\n * выставлением строки.\n *\n * @example\n * ```html\n * <rt-icon-button icon=\"ico-trash\" ariaLabel=\"Удалить\" tooltip=\"Удалить\" />\n * <button rtButton [rtTooltip]=\"'Подсказка'\">…</button>\n * ```\n */\n@Directive({\n selector: '[rtTooltip]',\n exportAs: 'rtTooltip',\n host: {\n '(mouseenter)': 'show()',\n '(mouseleave)': 'hide()',\n '(focusout)': 'hide()',\n '(click)': 'onClick()',\n },\n})\nexport class RtTooltipDirective implements OnDestroy {\n readonly #overlay: Overlay = inject(Overlay);\n readonly #elementRef: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #scrollStrategies: ScrollStrategyOptions = inject(ScrollStrategyOptions);\n readonly #focusMonitor: FocusMonitor = inject(FocusMonitor);\n\n #overlayRef: OverlayRef | null = null;\n #tooltipRef: ComponentRef<RtTooltipComponent> | null = null;\n #showTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * Сторона из настроек кита. Не умолчание входа: обёртка кита, передающая свой `null`, перебила\n * бы настройку приложения.\n */\n readonly #placementDefault: IRtTooltip.Placement | null = rtKitDefault(\n 'tooltip',\n (it: IRtKitConfig.Tooltip): IRtTooltip.Placement | undefined => it.placement,\n null\n );\n /**\n * Задержка показа по умолчанию: из настроек кита `components.tooltip.showDelay`, без них —\n * `SHOW_DELAY_MS`. Не умолчание входа — по той же причине, что и сторона.\n */\n readonly #showDelayDefault: number = rtKitDefault(\n 'tooltip',\n (it: IRtKitConfig.Tooltip): number | undefined => it.showDelay,\n SHOW_DELAY_MS\n );\n\n /**\n * Текст подсказки. Принимает `null`/`undefined` (частый результат date-пайпа\n * или nullable-полей модели) и нормализует их в пустую строку — иначе\n * биндинги вида `value | date` ломали бы строгую типизацию шаблона. Пустая\n * строка → no-op (директива выключена).\n */\n public readonly text: InputSignalWithTransform<string, string | null | undefined> = input<string, string | null | undefined>('', {\n alias: 'rtTooltip',\n transform: (value: string | null | undefined): string => value ?? '',\n });\n\n /**\n * Сторона подсказки. `null` — сторона из настроек кита `components.tooltip.placement`, без них\n * сверху, а под материальным набором снизу, как у первого кита.\n */\n public readonly placement: InputSignal<IRtTooltip.Placement | null> = input<IRtTooltip.Placement | null>(null, {\n alias: 'rtTooltipPlacement',\n });\n\n /**\n * Задержка показа этой подсказки в миллисекундах. `null` — общая задержка из настроек кита, без\n * них 300.\n */\n public readonly showDelay: InputSignalWithTransform<number | null, NumberInput> = input<number | null, NumberInput>(null, {\n alias: 'rtTooltipShowDelay',\n transform: (value: NumberInput): number | null => {\n const delay: number = value === null || value === undefined || value === '' ? Number.NaN : numberAttribute(value, Number.NaN);\n return delay >= 0 ? delay : null;\n },\n });\n\n /**\n * Подсказка только у обрезанного текста: хост меряется в момент показа, и подсказка не\n * появляется, пока его содержимое помещается целиком. Мерить заранее не нужно — показ идёт\n * только отсюда, и признак в эту минуту всегда свежий.\n */\n public readonly whenTruncated: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n alias: 'rtTooltipWhenTruncated',\n transform: booleanAttribute,\n });\n\n /**\n * Клик прячет открытую подсказку — по умолчанию, как раньше; умолчание на всё приложение —\n * `components.tooltip.hideOnClick` в настройках кита. `false` оставляет её открытой:\n * кнопка-переключатель показывает в ней новое состояние сразу после нажатия. Отложенный показ\n * клик снимает при любом значении — касание на тач-экране (mouseenter + click) подсказку не\n * показывает.\n */\n public readonly hideOnClick: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(\n rtKitDefault('tooltip', (it: IRtKitConfig.Tooltip): boolean | undefined => it.hideOnClick, true),\n {\n alias: 'rtTooltipHideOnClick',\n transform: booleanAttribute,\n }\n );\n\n constructor() {\n // Новый текст доходит до открытой панели на месте, без повторного наведения.\n effect((): void => this.#syncText(this.text()));\n this.#focusMonitor\n .monitor(this.#elementRef, true)\n .pipe(takeUntilDestroyed())\n .subscribe((origin: FocusOrigin): void => {\n if (origin === 'keyboard') {\n this.show();\n }\n });\n }\n\n public ngOnDestroy(): void {\n this.#focusMonitor.stopMonitoring(this.#elementRef);\n this.#clearTimer();\n this.#disposeOverlay();\n }\n\n protected show(): void {\n if (this.text().trim() === '' || this.#tooltipRef !== null) {\n return;\n }\n this.#clearTimer();\n this.#showTimer = setTimeout((): void => this.#attach(), this.showDelay() ?? this.#showDelayDefault);\n }\n\n protected hide(): void {\n this.#clearTimer();\n this.#overlayRef?.detach();\n this.#tooltipRef = null;\n }\n\n protected onClick(): void {\n if (this.hideOnClick() || this.#tooltipRef === null) {\n this.hide();\n }\n }\n\n #syncText(text: string): void {\n if (this.#tooltipRef === null) {\n return;\n }\n if (text.trim() === '') {\n this.hide();\n return;\n }\n this.#tooltipRef.instance.text.set(text);\n this.#tooltipRef.changeDetectorRef.markForCheck();\n this.#overlayRef?.updatePosition();\n }\n\n #attach(): void {\n if (this.text().trim() === '' || (this.whenTruncated() && !isTooltipTextCut(this.#elementRef.nativeElement))) {\n return;\n }\n const overlayRef: OverlayRef = this.#ensureOverlay();\n // Коробка переживает закрытие, а кусок темы вокруг host'а мог смениться — переносим на каждом показе.\n carryThemeScope(overlayRef.overlayElement, this.#elementRef.nativeElement);\n const portal: ComponentPortal<RtTooltipComponent> = new ComponentPortal(RtTooltipComponent);\n this.#tooltipRef = overlayRef.attach(portal);\n this.#tooltipRef.instance.text.set(this.text());\n this.#tooltipRef.changeDetectorRef.markForCheck();\n }\n\n #ensureOverlay(): OverlayRef {\n if (this.#overlayRef !== null) {\n return this.#overlayRef;\n }\n const presetClasses: string[] = materialPresetClassesOf(this.#elementRef.nativeElement);\n const placement: IRtTooltip.Placement = tooltipPlacement(this.placement(), this.#placementDefault, presetClasses.length > 0);\n const positions: ConnectedPosition[] = tooltipPositions(placement);\n\n const positionStrategy: FlexibleConnectedPositionStrategy = this.#overlay\n .position()\n .flexibleConnectedTo(this.#elementRef)\n .withPush(true)\n .withViewportMargin(VIEWPORT_MARGIN)\n .withFlexibleDimensions(false)\n .withPositions(positions);\n\n const config: OverlayConfig = new OverlayConfig({\n positionStrategy,\n scrollStrategy: this.#scrollStrategies.reposition(),\n hasBackdrop: false,\n panelClass: ['rt-tooltip-panel', ...presetClasses],\n });\n this.#overlayRef = this.#overlay.create(config);\n return this.#overlayRef;\n }\n\n #clearTimer(): void {\n if (this.#showTimer !== null) {\n clearTimeout(this.#showTimer);\n this.#showTimer = null;\n }\n }\n\n #disposeOverlay(): void {\n this.#overlayRef?.dispose();\n this.#overlayRef = null;\n this.#tooltipRef = null;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-tooltip.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/tooltip/rt-tooltip.component.ts","../../../projects/ui-kit-v2/src/lib/components/tooltip/rt-tooltip.logic.ts","../../../projects/ui-kit-v2/src/lib/components/tooltip/rt-tooltip.directive.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, signal, ViewEncapsulation, WritableSignal } from '@angular/core';\n\nconst BEM_BLOCK: string = 'rt-tooltip';\n\n/**\n * Презентационная панель tooltip'а. Рендерится директивой `[rtTooltip]` через\n * CDK `ComponentPortal` в overlay; текст выставляется императивно\n * (`ref.instance.text.set(...)`) — отдельного `input()` нет, т.к. компонент\n * создаётся вручную, а не в шаблоне.\n *\n * `ViewEncapsulation.None` — стили префиксованы `.rt-tooltip` (единообразно с\n * остальным common/ui). `role=\"tooltip\"` — вспомогательный ярлык для AT.\n */\n@Component({\n selector: 'rt-tooltip',\n template: '{{ text() }}',\n styleUrl: './rt-tooltip.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: BEM_BLOCK,\n role: 'tooltip',\n },\n})\nexport class RtTooltipComponent {\n public readonly text: WritableSignal<string> = signal<string>('');\n}\n","import { ConnectedPosition } from '@angular/cdk/overlay';\n\nimport { IRtTooltip } from './rt-tooltip.model';\n\n/** Зазор между подсказкой и host'ом. */\nconst TOOLTIP_GAP_PX: number = 6;\n\nconst POSITION_BY_PLACEMENT: Readonly<Record<IRtTooltip.Placement, ConnectedPosition>> = {\n top: { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -TOOLTIP_GAP_PX },\n bottom: { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: TOOLTIP_GAP_PX },\n left: { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -TOOLTIP_GAP_PX },\n right: { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: TOOLTIP_GAP_PX },\n};\n\nconst FALLBACKS: Readonly<Record<IRtTooltip.Placement, readonly IRtTooltip.Placement[]>> = {\n top: ['top', 'bottom'],\n bottom: ['bottom', 'top'],\n left: ['left', 'right', 'top', 'bottom'],\n right: ['right', 'left', 'top', 'bottom'],\n};\n\n/**\n * Позиции подсказки по порядку попыток: заданная сторона, противоположная, а у боковой ещё\n * верх и низ — на узком экране сбоку не помещается ни одна сторона, и подсказка, придвинутая\n * к краю, легла бы на сам host. Бока заданы логическими `start`/`end`: в письме справа налево\n * `left` встаёт справа — как и вся раскладка кита.\n */\n/**\n * Сторона подсказки: своя на элементе, иначе из настроек кита, иначе сверху, а под материальным\n * набором снизу, как у первого кита.\n */\nexport function tooltipPlacement(\n own: IRtTooltip.Placement | null,\n configured: IRtTooltip.Placement | null,\n materialPreset: boolean\n): IRtTooltip.Placement {\n return own ?? configured ?? (materialPreset ? 'bottom' : 'top');\n}\n\nexport function tooltipPositions(placement: IRtTooltip.Placement): ConnectedPosition[] {\n return FALLBACKS[placement].map((side: IRtTooltip.Placement): ConnectedPosition => ({ ...POSITION_BY_PLACEMENT[side] }));\n}\n\n/** Размеры узла, по которым судят об обрезке: видимая ширина и ширина содержимого. */\nexport interface IRtTooltipBox {\n readonly clientWidth: number;\n readonly scrollWidth: number;\n}\n\n/**\n * Текст узла обрезан: содержимое шире видимой коробки. Высота не судится — узел с видимым\n * переполнением по высоте показывает всё, и его подсказка только повторяла бы текст.\n */\nexport function isTooltipTextCut(box: IRtTooltipBox): boolean {\n return box.scrollWidth > box.clientWidth;\n}\n","import { FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';\nimport { NumberInput } from '@angular/cdk/coercion';\nimport { hasModifierKey } from '@angular/cdk/keycodes';\nimport {\n ConnectedPosition,\n FlexibleConnectedPositionStrategy,\n Overlay,\n OverlayConfig,\n OverlayRef,\n ScrollStrategyOptions,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport {\n booleanAttribute,\n ComponentRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n numberAttribute,\n OnDestroy,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { carryThemeScope, IRtKitConfig, materialPresetClassesOf, rtKitDefault } from '@rt-tools/ui-kit-v2/core';\nimport { RtTooltipComponent } from './rt-tooltip.component';\nimport { isTooltipTextCut, tooltipPlacement, tooltipPositions } from './rt-tooltip.logic';\nimport { IRtTooltip } from './rt-tooltip.model';\n\nconst SHOW_DELAY_MS: number = 300;\nconst VIEWPORT_MARGIN: number = 8;\n\n/**\n * Tooltip-директива поверх CDK Overlay. Показывает короткую подсказку\n * (`rtTooltip` = строка) на hover/focus host'а. Движок — CDK `flexibleConnectedTo`\n * (авто-flip у края viewport, reposition при скролле), так что подсказка не\n * режется `overflow: hidden` предками (таблицы, aside, карточки).\n *\n * Триггеры: `mouseenter` и фокус с клавиатуры показывают (с задержкой `SHOW_DELAY_MS`),\n * `mouseleave`/`focusout`/`click`/Escape прячут. Фокус, поставленный мышью или кодом, подсказку не\n * показывает, как у Material: меню возвращает фокус кнопке после выбора пункта мышью, и\n * подсказка кнопки всплывала поверх того, что пункт открыл. Пустой текст → no-op (директива\n * выключена), поэтому её можно безусловно вешать на icon-кнопки и включать\n * выставлением строки.\n *\n * @example\n * ```html\n * <rt-icon-button icon=\"ico-trash\" ariaLabel=\"Удалить\" tooltip=\"Удалить\" />\n * <button rtButton [rtTooltip]=\"'Подсказка'\">…</button>\n * ```\n */\n@Directive({\n selector: '[rtTooltip]',\n exportAs: 'rtTooltip',\n host: {\n '(mouseenter)': 'show()',\n '(mouseleave)': 'hide()',\n '(focusout)': 'hide()',\n '(click)': 'onClick()',\n '(keydown)': 'onKeydown($event)',\n },\n})\nexport class RtTooltipDirective implements OnDestroy {\n readonly #overlay: Overlay = inject(Overlay);\n readonly #elementRef: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #scrollStrategies: ScrollStrategyOptions = inject(ScrollStrategyOptions);\n readonly #focusMonitor: FocusMonitor = inject(FocusMonitor);\n\n #overlayRef: OverlayRef | null = null;\n #tooltipRef: ComponentRef<RtTooltipComponent> | null = null;\n #showTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * Сторона из настроек кита. Не умолчание входа: обёртка кита, передающая свой `null`, перебила\n * бы настройку приложения.\n */\n readonly #placementDefault: IRtTooltip.Placement | null = rtKitDefault(\n 'tooltip',\n (it: IRtKitConfig.Tooltip): IRtTooltip.Placement | undefined => it.placement,\n null\n );\n /**\n * Задержка показа по умолчанию: из настроек кита `components.tooltip.showDelay`, без них —\n * `SHOW_DELAY_MS`. Не умолчание входа — по той же причине, что и сторона.\n */\n readonly #showDelayDefault: number = rtKitDefault(\n 'tooltip',\n (it: IRtKitConfig.Tooltip): number | undefined => it.showDelay,\n SHOW_DELAY_MS\n );\n\n /**\n * Текст подсказки. Принимает `null`/`undefined` (частый результат date-пайпа\n * или nullable-полей модели) и нормализует их в пустую строку — иначе\n * биндинги вида `value | date` ломали бы строгую типизацию шаблона. Пустая\n * строка → no-op (директива выключена).\n */\n public readonly text: InputSignalWithTransform<string, string | null | undefined> = input<string, string | null | undefined>('', {\n alias: 'rtTooltip',\n transform: (value: string | null | undefined): string => value ?? '',\n });\n\n /**\n * Сторона подсказки. `null` — сторона из настроек кита `components.tooltip.placement`, без них\n * сверху, а под материальным набором снизу, как у первого кита.\n */\n public readonly placement: InputSignal<IRtTooltip.Placement | null> = input<IRtTooltip.Placement | null>(null, {\n alias: 'rtTooltipPlacement',\n });\n\n /**\n * Задержка показа этой подсказки в миллисекундах. `null` — общая задержка из настроек кита, без\n * них 300.\n */\n public readonly showDelay: InputSignalWithTransform<number | null, NumberInput> = input<number | null, NumberInput>(null, {\n alias: 'rtTooltipShowDelay',\n transform: (value: NumberInput): number | null => {\n const delay: number = value === null || value === undefined || value === '' ? Number.NaN : numberAttribute(value, Number.NaN);\n return delay >= 0 ? delay : null;\n },\n });\n\n /**\n * Подсказка только у обрезанного текста: хост меряется в момент показа, и подсказка не\n * появляется, пока его содержимое помещается целиком. Мерить заранее не нужно — показ идёт\n * только отсюда, и признак в эту минуту всегда свежий.\n */\n public readonly whenTruncated: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n alias: 'rtTooltipWhenTruncated',\n transform: booleanAttribute,\n });\n\n /**\n * Клик прячет открытую подсказку — по умолчанию, как раньше; умолчание на всё приложение —\n * `components.tooltip.hideOnClick` в настройках кита. `false` оставляет её открытой:\n * кнопка-переключатель показывает в ней новое состояние сразу после нажатия. Отложенный показ\n * клик снимает при любом значении — касание на тач-экране (mouseenter + click) подсказку не\n * показывает.\n */\n public readonly hideOnClick: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(\n rtKitDefault('tooltip', (it: IRtKitConfig.Tooltip): boolean | undefined => it.hideOnClick, true),\n {\n alias: 'rtTooltipHideOnClick',\n transform: booleanAttribute,\n }\n );\n\n constructor() {\n // Новый текст доходит до открытой панели на месте, без повторного наведения.\n effect((): void => this.#syncText(this.text()));\n this.#focusMonitor\n .monitor(this.#elementRef, true)\n .pipe(takeUntilDestroyed())\n .subscribe((origin: FocusOrigin): void => {\n if (origin === 'keyboard') {\n this.show();\n }\n });\n }\n\n public ngOnDestroy(): void {\n this.#focusMonitor.stopMonitoring(this.#elementRef);\n this.#clearTimer();\n this.#disposeOverlay();\n }\n\n protected show(): void {\n if (this.text().trim() === '' || this.#tooltipRef !== null) {\n return;\n }\n this.#clearTimer();\n this.#showTimer = setTimeout((): void => this.#attach(), this.showDelay() ?? this.#showDelayDefault);\n }\n\n protected hide(): void {\n this.#clearTimer();\n this.#overlayRef?.detach();\n this.#tooltipRef = null;\n }\n\n protected onClick(): void {\n if (this.hideOnClick() || this.#tooltipRef === null) {\n this.hide();\n }\n }\n\n /**\n * Escape прячет подсказку и снимает отложенный показ, фокус остаётся на хосте. Событие гасится,\n * только когда панель была открыта: при закрытой Escape доходит до диалога или панели выше.\n */\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape' || hasModifierKey(event)) {\n return;\n }\n const wasOpen: boolean = this.#tooltipRef !== null;\n this.hide();\n if (wasOpen) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n #syncText(text: string): void {\n if (this.#tooltipRef === null) {\n return;\n }\n if (text.trim() === '') {\n this.hide();\n return;\n }\n this.#tooltipRef.instance.text.set(text);\n this.#tooltipRef.changeDetectorRef.markForCheck();\n this.#overlayRef?.updatePosition();\n }\n\n #attach(): void {\n if (this.text().trim() === '' || (this.whenTruncated() && !isTooltipTextCut(this.#elementRef.nativeElement))) {\n return;\n }\n const overlayRef: OverlayRef = this.#ensureOverlay();\n // Коробка переживает закрытие, а кусок темы вокруг host'а мог смениться — переносим на каждом показе.\n carryThemeScope(overlayRef.overlayElement, this.#elementRef.nativeElement);\n const portal: ComponentPortal<RtTooltipComponent> = new ComponentPortal(RtTooltipComponent);\n this.#tooltipRef = overlayRef.attach(portal);\n this.#tooltipRef.instance.text.set(this.text());\n this.#tooltipRef.changeDetectorRef.markForCheck();\n }\n\n #ensureOverlay(): OverlayRef {\n if (this.#overlayRef !== null) {\n return this.#overlayRef;\n }\n const presetClasses: string[] = materialPresetClassesOf(this.#elementRef.nativeElement);\n const placement: IRtTooltip.Placement = tooltipPlacement(this.placement(), this.#placementDefault, presetClasses.length > 0);\n const positions: ConnectedPosition[] = tooltipPositions(placement);\n\n const positionStrategy: FlexibleConnectedPositionStrategy = this.#overlay\n .position()\n .flexibleConnectedTo(this.#elementRef)\n .withPush(true)\n .withViewportMargin(VIEWPORT_MARGIN)\n .withFlexibleDimensions(false)\n .withPositions(positions);\n\n const config: OverlayConfig = new OverlayConfig({\n positionStrategy,\n scrollStrategy: this.#scrollStrategies.reposition(),\n hasBackdrop: false,\n panelClass: ['rt-tooltip-panel', ...presetClasses],\n });\n this.#overlayRef = this.#overlay.create(config);\n return this.#overlayRef;\n }\n\n #clearTimer(): void {\n if (this.#showTimer !== null) {\n clearTimeout(this.#showTimer);\n this.#showTimer = null;\n }\n }\n\n #disposeOverlay(): void {\n this.#overlayRef?.dispose();\n this.#overlayRef = null;\n this.#tooltipRef = null;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/ui-kit-v2",
3
- "version": "0.34.2",
3
+ "version": "0.34.3",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Yauheni Krumin",
6
6
  "type": "module",
@@ -30,6 +30,9 @@ no-op, а не пустая панель под курсором. Поэтому
30
30
 
31
31
  - Показывают и наведение, и фокус с клавиатуры; прячут уход курсора, потеря фокуса и **клик**
32
32
  (панель мешала бы смотреть на результат нажатия).
33
+ - **Escape** прячет открытую подсказку и гасится на хосте; фокус остаётся на хосте, следующее
34
+ наведение или фокус с клавиатуры показывают её снова. При закрытой подсказке Escape идёт дальше,
35
+ и диалог или панель выше по дереву закрываются, как раньше.
33
36
  - У переключателя ставят `rtTooltipHideOnClick="false"`: клик оставляет подсказку, а новый текст
34
37
  `[rtTooltip]` («Включить» → «Выключить») появляется в открытой панели на месте. Отложенный показ клик
35
38
  снимает при любом значении, поэтому касание на тач-экране подсказку не показывает.
@@ -32,7 +32,7 @@ export declare namespace IRtTooltip {
32
32
  * режется `overflow: hidden` предками (таблицы, aside, карточки).
33
33
  *
34
34
  * Триггеры: `mouseenter` и фокус с клавиатуры показывают (с задержкой `SHOW_DELAY_MS`),
35
- * `mouseleave`/`focusout`/`click` прячут. Фокус, поставленный мышью или кодом, подсказку не
35
+ * `mouseleave`/`focusout`/`click`/Escape прячут. Фокус, поставленный мышью или кодом, подсказку не
36
36
  * показывает, как у Material: меню возвращает фокус кнопке после выбора пункта мышью, и
37
37
  * подсказка кнопки всплывала поверх того, что пункт открыл. Пустой текст → no-op (директива
38
38
  * выключена), поэтому её можно безусловно вешать на icon-кнопки и включать
@@ -82,6 +82,11 @@ export declare class RtTooltipDirective implements OnDestroy {
82
82
  protected show(): void;
83
83
  protected hide(): void;
84
84
  protected onClick(): void;
85
+ /**
86
+ * Escape прячет подсказку и снимает отложенный показ, фокус остаётся на хосте. Событие гасится,
87
+ * только когда панель была открыта: при закрытой Escape доходит до диалога или панели выше.
88
+ */
89
+ protected onKeydown(event: KeyboardEvent): void;
85
90
  static ɵfac: i0.ɵɵFactoryDeclaration<RtTooltipDirective, never>;
86
91
  static ɵdir: i0.ɵɵDirectiveDeclaration<RtTooltipDirective, "[rtTooltip]", ["rtTooltip"], {
87
92
  "text": {