From b27c75271c23ff36057ebdf5f39025a8f40eb1e7 Mon Sep 17 00:00:00 2001 From: waterplea Date: Mon, 1 Aug 2022 15:51:57 +0300 Subject: [PATCH] feat(core): `Hint` support 12 directions --- .../bar-chart/bar-chart.component.ts | 7 - .../components/bar-chart/bar-chart.style.less | 4 - .../bar-chart/bar-chart.template.html | 9 +- .../line-chart/line-chart.style.less | 4 - .../line-chart/line-chart.template.html | 17 +- .../components/pie-chart/pie-chart.module.ts | 4 +- .../components/pie-chart/pie-chart.style.less | 4 - .../pie-chart/pie-chart.template.html | 12 +- .../components/preview/preview.module.ts | 2 - .../preview/zoom/preview-zoom.template.html | 4 +- .../abstract/abstract-driver.directive.ts | 20 ++ .../core/abstract/abstract-hint-options.ts | 12 - projects/core/abstract/abstract-hint.ts | 40 --- projects/core/abstract/driver.ts | 11 + projects/core/abstract/index.ts | 7 +- projects/core/abstract/position-accessor.ts | 15 + projects/core/abstract/rect-accessor.ts | 12 + projects/core/abstract/vehicle.ts | 12 + .../hints-host/hint-box/hint-box.component.ts | 294 ------------------ .../hints-host/hint-box/hint-box.module.ts | 14 - .../hints-host/hint-box/hint-box.style.less | 145 --------- .../hint-box/hint-box.template.html | 17 - .../hints-host/hints-host.component.ts | 12 +- .../hints-host/hints-host.module.ts | 3 +- .../hints-host/hints-host.template.html | 2 +- projects/core/components/hints-host/index.ts | 2 - .../components/tooltip/tooltip.component.ts | 35 +-- projects/core/constants/hint-directions.ts | 16 + projects/core/constants/index.ts | 1 + .../hint-controller.directive.ts | 9 +- .../directives/hint/hint-driver.directive.ts | 9 + .../directives/hint/hint-host.directive.ts | 16 + .../directives/hint/hint-hover.directive.ts | 39 +++ .../directives/hint/hint-manual.directive.ts | 20 ++ projects/core/directives/hint/hint-options.ts | 24 +- .../directives/hint/hint-pointer.directive.ts | 28 ++ .../hint/hint-position.directive.ts | 92 ++++++ .../directives/hint/hint-position.service.ts | 27 ++ .../core/directives/hint/hint.component.ts | 87 ++++++ .../core/directives/hint/hint.directive.ts | 161 +++------- projects/core/directives/hint/hint.module.ts | 33 +- .../core/directives/hint/hint.providers.ts | 10 + projects/core/directives/hint/hint.style.less | 50 +++ projects/core/directives/hint/index.ts | 8 + projects/core/directives/index.ts | 2 - projects/core/directives/manual-hint/index.ts | 3 - .../manual-hint/manual-hint-options.ts | 26 -- .../manual-hint/manual-hint.directive.ts | 41 --- .../manual-hint/manual-hint.module.ts | 9 - .../core/directives/manual-hint/package.json | 7 - .../test/manual-hint.directive.spec.ts | 104 ------- .../core/directives/pointer-hint/index.ts | 3 - .../core/directives/pointer-hint/package.json | 7 - .../pointer-hint/pointer-hint-options.ts | 31 -- .../pointer-hint/pointer-hint.directive.ts | 123 -------- .../pointer-hint/pointer-hint.module.ts | 9 - .../test/pointer-hint.directive.spec.ts | 99 ------ projects/core/services/hint.service.ts | 23 +- projects/core/types/hint-direction.ts | 13 + projects/core/types/index.ts | 1 + projects/core/types/point.ts | 2 +- projects/demo/src/modules/app/app.const.ts | 2 +- projects/demo/src/modules/app/app.routes.ts | 16 +- projects/demo/src/modules/app/pages.ts | 54 ++-- .../modules/components/abstract/control.ts | 15 +- .../src/modules/components/abstract/hint.ts | 15 +- .../inherited-documentation.component.ts | 13 +- .../line-clamp/examples/1/index.html | 19 +- .../line-clamp/examples/1/index.less | 10 +- .../line-clamp/line-clamp.module.ts | 3 +- .../line-clamp/line-clamp.template.html | 2 +- .../primitive-textfield.component.ts | 15 +- .../components/slider/examples/6/index.html | 6 +- .../components/slider/slider.module.ts | 4 +- .../components/tooltip/examples/4/index.ts | 2 +- .../components/tooltip/tooltip.component.ts | 17 +- .../hint-controller.component.ts | 17 +- .../examples/1/index.html | 4 +- .../examples/1/index.less | 0 .../examples/1/index.ts | 4 +- .../examples/import/import-module.md | 4 +- .../examples/import/insert-template.md | 8 +- .../hint-manual.component.ts} | 8 +- .../hint-manual.module.ts} | 16 +- .../hint-manual.template.html} | 14 +- .../examples/1/index.html | 3 +- .../examples/1/index.less | 0 .../examples/1/index.ts | 4 +- .../examples/import/import-module.md | 0 .../examples/import/insert-template.md | 8 + .../hint-pointer.component.ts} | 8 +- .../hint-pointer.module.ts} | 16 +- .../hint-pointer.template.html} | 9 +- .../examples/import/insert-template.md | 3 - .../input-copy/input-copy.component.ts | 4 +- projects/kit/components/line-clamp/index.ts | 1 + .../line-clamp-box.component.ts | 19 ++ .../line-clamp-box/line-clamp-box.style.less | 16 + .../line-clamp-box.template.html | 1 + .../line-clamp-position.directive.ts | 21 ++ .../line-clamp/line-clamp.component.ts | 9 +- .../line-clamp/line-clamp.module.ts | 8 +- .../line-clamp/line-clamp.style.less | 4 + .../line-clamp/line-clamp.template.html | 3 +- 104 files changed, 857 insertions(+), 1401 deletions(-) create mode 100644 projects/core/abstract/abstract-driver.directive.ts delete mode 100644 projects/core/abstract/abstract-hint-options.ts delete mode 100644 projects/core/abstract/abstract-hint.ts create mode 100644 projects/core/abstract/driver.ts create mode 100644 projects/core/abstract/position-accessor.ts create mode 100644 projects/core/abstract/rect-accessor.ts create mode 100644 projects/core/abstract/vehicle.ts delete mode 100644 projects/core/components/hints-host/hint-box/hint-box.component.ts delete mode 100644 projects/core/components/hints-host/hint-box/hint-box.module.ts delete mode 100644 projects/core/components/hints-host/hint-box/hint-box.style.less delete mode 100644 projects/core/components/hints-host/hint-box/hint-box.template.html create mode 100644 projects/core/constants/hint-directions.ts create mode 100644 projects/core/directives/hint/hint-driver.directive.ts create mode 100644 projects/core/directives/hint/hint-host.directive.ts create mode 100644 projects/core/directives/hint/hint-hover.directive.ts create mode 100644 projects/core/directives/hint/hint-manual.directive.ts create mode 100644 projects/core/directives/hint/hint-pointer.directive.ts create mode 100644 projects/core/directives/hint/hint-position.directive.ts create mode 100644 projects/core/directives/hint/hint-position.service.ts create mode 100644 projects/core/directives/hint/hint.component.ts create mode 100644 projects/core/directives/hint/hint.providers.ts create mode 100644 projects/core/directives/hint/hint.style.less delete mode 100644 projects/core/directives/manual-hint/index.ts delete mode 100644 projects/core/directives/manual-hint/manual-hint-options.ts delete mode 100644 projects/core/directives/manual-hint/manual-hint.directive.ts delete mode 100644 projects/core/directives/manual-hint/manual-hint.module.ts delete mode 100644 projects/core/directives/manual-hint/package.json delete mode 100644 projects/core/directives/manual-hint/test/manual-hint.directive.spec.ts delete mode 100644 projects/core/directives/pointer-hint/index.ts delete mode 100644 projects/core/directives/pointer-hint/package.json delete mode 100644 projects/core/directives/pointer-hint/pointer-hint-options.ts delete mode 100644 projects/core/directives/pointer-hint/pointer-hint.directive.ts delete mode 100644 projects/core/directives/pointer-hint/pointer-hint.module.ts delete mode 100644 projects/core/directives/pointer-hint/test/pointer-hint.directive.spec.ts create mode 100644 projects/core/types/hint-direction.ts rename projects/demo/src/modules/directives/{manual-hint => hint-manual}/examples/1/index.html (81%) rename projects/demo/src/modules/directives/{manual-hint => hint-manual}/examples/1/index.less (100%) rename projects/demo/src/modules/directives/{manual-hint => hint-manual}/examples/1/index.ts (79%) rename projects/demo/src/modules/directives/{manual-hint => hint-manual}/examples/import/import-module.md (55%) rename projects/demo/src/modules/directives/{manual-hint => hint-manual}/examples/import/insert-template.md (58%) rename projects/demo/src/modules/directives/{manual-hint/manual-hint.component.ts => hint-manual/hint-manual.component.ts} (80%) rename projects/demo/src/modules/directives/{manual-hint/manual-hint.module.ts => hint-manual/hint-manual.module.ts} (64%) rename projects/demo/src/modules/directives/{manual-hint/manual-hint.template.html => hint-manual/hint-manual.template.html} (84%) rename projects/demo/src/modules/directives/{pointer-hint => hint-pointer}/examples/1/index.html (70%) rename projects/demo/src/modules/directives/{pointer-hint => hint-pointer}/examples/1/index.less (100%) rename projects/demo/src/modules/directives/{pointer-hint => hint-pointer}/examples/1/index.ts (71%) rename projects/demo/src/modules/directives/{pointer-hint => hint-pointer}/examples/import/import-module.md (100%) create mode 100644 projects/demo/src/modules/directives/hint-pointer/examples/import/insert-template.md rename projects/demo/src/modules/directives/{pointer-hint/pointer-hint.component.ts => hint-pointer/hint-pointer.component.ts} (80%) rename projects/demo/src/modules/directives/{pointer-hint/pointer-hint.module.ts => hint-pointer/hint-pointer.module.ts} (58%) rename projects/demo/src/modules/directives/{pointer-hint/pointer-hint.template.html => hint-pointer/hint-pointer.template.html} (92%) delete mode 100644 projects/demo/src/modules/directives/pointer-hint/examples/import/insert-template.md create mode 100644 projects/kit/components/line-clamp/line-clamp-box/line-clamp-box.component.ts create mode 100644 projects/kit/components/line-clamp/line-clamp-box/line-clamp-box.style.less create mode 100644 projects/kit/components/line-clamp/line-clamp-box/line-clamp-box.template.html create mode 100644 projects/kit/components/line-clamp/line-clamp-position.directive.ts diff --git a/projects/addon-charts/components/bar-chart/bar-chart.component.ts b/projects/addon-charts/components/bar-chart/bar-chart.component.ts index 97e12722a708b..21c5941114337 100644 --- a/projects/addon-charts/components/bar-chart/bar-chart.component.ts +++ b/projects/addon-charts/components/bar-chart/bar-chart.component.ts @@ -67,13 +67,6 @@ export class TuiBarChartComponent { return this.max || this.getMax(this.value, this.collapsed); } - @tuiPure - getContentContext(index: number): TuiContextWithImplicit { - return { - $implicit: index, - }; - } - readonly percentMapper: TuiMapper = ( set, collapsed: boolean, diff --git a/projects/addon-charts/components/bar-chart/bar-chart.style.less b/projects/addon-charts/components/bar-chart/bar-chart.style.less index 3cb3f6e68b0d5..ef0395f2c760d 100644 --- a/projects/addon-charts/components/bar-chart/bar-chart.style.less +++ b/projects/addon-charts/components/bar-chart/bar-chart.style.less @@ -40,7 +40,3 @@ box-shadow: 0 0 0 2px var(--tui-focus); } } - -.t-text { - white-space: pre-wrap; -} diff --git a/projects/addon-charts/components/bar-chart/bar-chart.template.html b/projects/addon-charts/components/bar-chart/bar-chart.template.html index 4af286298cd1c..8624cc7582545 100644 --- a/projects/addon-charts/components/bar-chart/bar-chart.template.html +++ b/projects/addon-charts/components/bar-chart/bar-chart.template.html @@ -29,10 +29,9 @@ #hint="polymorpheus" polymorpheus > -
+ + + {{ text }} + diff --git a/projects/addon-charts/components/line-chart/line-chart.style.less b/projects/addon-charts/components/line-chart/line-chart.style.less index e50dbb414923e..7a86a35982936 100644 --- a/projects/addon-charts/components/line-chart/line-chart.style.less +++ b/projects/addon-charts/components/line-chart/line-chart.style.less @@ -77,10 +77,6 @@ } } -.t-text { - white-space: pre-wrap; -} - .t-hint { .shadow(); position: absolute; diff --git a/projects/addon-charts/components/line-chart/line-chart.template.html b/projects/addon-charts/components/line-chart/line-chart.template.html index 1e0560e9e6409..246093fa2a95f 100644 --- a/projects/addon-charts/components/line-chart/line-chart.template.html +++ b/projects/addon-charts/components/line-chart/line-chart.template.html @@ -82,20 +82,21 @@ [style.bottom.%]="getBottom(point[1])" > + -
+ > + {{ text }} +
-
+ > + {{ text }} +
diff --git a/projects/addon-charts/components/pie-chart/pie-chart.module.ts b/projects/addon-charts/components/pie-chart/pie-chart.module.ts index 35c8bd72daad7..7e993e107837b 100644 --- a/projects/addon-charts/components/pie-chart/pie-chart.module.ts +++ b/projects/addon-charts/components/pie-chart/pie-chart.module.ts @@ -1,7 +1,7 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {TuiHoveredModule, TuiRepeatTimesModule} from '@taiga-ui/cdk'; -import {TuiPointerHintModule} from '@taiga-ui/core'; +import {TuiHintModule} from '@taiga-ui/core'; import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus'; import {TuiPieChartComponent} from './pie-chart.component'; @@ -13,7 +13,7 @@ import {TuiPieChartDirective} from './pie-chart.directive'; TuiRepeatTimesModule, TuiHoveredModule, PolymorpheusModule, - TuiPointerHintModule, + TuiHintModule, ], declarations: [TuiPieChartComponent, TuiPieChartDirective], exports: [TuiPieChartComponent], diff --git a/projects/addon-charts/components/pie-chart/pie-chart.style.less b/projects/addon-charts/components/pie-chart/pie-chart.style.less index fbbab72754e73..99fb91861ebf4 100644 --- a/projects/addon-charts/components/pie-chart/pie-chart.style.less +++ b/projects/addon-charts/components/pie-chart/pie-chart.style.less @@ -54,7 +54,3 @@ .t-placeholder { fill: var(--tui-base-03); } - -.t-text { - white-space: pre-wrap; -} diff --git a/projects/addon-charts/components/pie-chart/pie-chart.template.html b/projects/addon-charts/components/pie-chart/pie-chart.template.html index 28a616ce1ddce..c88591e078d6c 100644 --- a/projects/addon-charts/components/pie-chart/pie-chart.template.html +++ b/projects/addon-charts/components/pie-chart/pie-chart.template.html @@ -33,6 +33,7 @@ *tuiRepeatTimes="let index of segments.length" fill="currentColor" automation-id="tui-pie-chart__segment" + tuiHintPointer tuiHintMode="onDark" tuiHintDirection="top-right" d="" @@ -40,7 +41,7 @@ [attr.transform]="getTransform(index)" [style.color]="getColor(index)" [tuiPieChart]="segments[index]" - [tuiPointerHint]="getHint(hint)" + [tuiHint]="getHint(hint)" (tuiHoveredChange)="onHovered($event, index)" > @@ -56,9 +57,8 @@ #template let-index > -
+ + + {{ text }} +
diff --git a/projects/addon-preview/components/preview/preview.module.ts b/projects/addon-preview/components/preview/preview.module.ts index 45b53419b6c5c..dd11b13527f81 100644 --- a/projects/addon-preview/components/preview/preview.module.ts +++ b/projects/addon-preview/components/preview/preview.module.ts @@ -8,7 +8,6 @@ import { TuiButtonModule, TuiHintModule, TuiLoaderModule, - TuiManualHintModule, TuiSvgModule, } from '@taiga-ui/core'; import {TuiSliderModule} from '@taiga-ui/kit'; @@ -31,7 +30,6 @@ import {TuiPreviewZoomComponent} from './zoom/preview-zoom.component'; TuiSliderModule, TuiHintModule, TuiDragModule, - TuiManualHintModule, TuiResizeModule, TuiSvgModule, TuiLoaderModule, diff --git a/projects/addon-preview/components/preview/zoom/preview-zoom.template.html b/projects/addon-preview/components/preview/zoom/preview-zoom.template.html index cefaf4a96ecf0..732be789d96b9 100644 --- a/projects/addon-preview/components/preview/zoom/preview-zoom.template.html +++ b/projects/addon-preview/components/preview/zoom/preview-zoom.template.html @@ -13,8 +13,8 @@
diff --git a/projects/core/abstract/abstract-driver.directive.ts b/projects/core/abstract/abstract-driver.directive.ts new file mode 100644 index 0000000000000..fd64ccc40f481 --- /dev/null +++ b/projects/core/abstract/abstract-driver.directive.ts @@ -0,0 +1,20 @@ +import {Directive, Inject, Self} from '@angular/core'; +import {TuiDestroyService} from '@taiga-ui/cdk'; +import {Observable} from 'rxjs'; +import {distinctUntilChanged, takeUntil} from 'rxjs/operators'; + +import {TuiDriver} from './driver'; +import {TuiVehicle} from './vehicle'; + +@Directive() +export class AbstractTuiDriverDirective { + constructor( + @Self() @Inject(TuiDestroyService) destroy$: Observable, + @Inject(TuiDriver) driver$: Observable, + @Inject(TuiVehicle) vehicle: TuiVehicle, + ) { + driver$.pipe(distinctUntilChanged(), takeUntil(destroy$)).subscribe(value => { + vehicle.toggle(value); + }); + } +} diff --git a/projects/core/abstract/abstract-hint-options.ts b/projects/core/abstract/abstract-hint-options.ts deleted file mode 100644 index 6ae25c5330f78..0000000000000 --- a/projects/core/abstract/abstract-hint-options.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {TuiDirection, TuiHintMode} from '@taiga-ui/core/types'; - -export interface TuiAbstractHintOptions { - readonly mode: TuiHintMode | null; - readonly direction: TuiDirection; -} - -/** Default values for abstract hint options */ -export const TUI_ABSTRACT_HINT_DEFAULT_OPTIONS: TuiAbstractHintOptions = { - mode: null, - direction: `bottom-left`, -}; diff --git a/projects/core/abstract/abstract-hint.ts b/projects/core/abstract/abstract-hint.ts deleted file mode 100644 index dd292d2c6cdf1..0000000000000 --- a/projects/core/abstract/abstract-hint.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {Directive, ElementRef, Input, OnDestroy} from '@angular/core'; -import {TuiActiveZoneDirective, tuiDefaultProp} from '@taiga-ui/cdk'; -import {TuiHintService} from '@taiga-ui/core/services'; -import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; - -import {TuiAbstractHintOptions} from './abstract-hint-options'; - -@Directive() -export abstract class AbstractTuiHint implements OnDestroy { - @Input(`tuiHintMode`) - @tuiDefaultProp() - mode: TuiAbstractHintOptions['mode'] = this.options.mode; - - @Input(`tuiHintDirection`) - @tuiDefaultProp() - direction: TuiAbstractHintOptions['direction'] = this.options.direction; - - content: PolymorpheusContent = ``; - - protected constructor( - protected readonly elementRef: ElementRef, - protected readonly hintService: TuiHintService, - readonly activeZone: TuiActiveZoneDirective | null, - protected readonly options: TuiAbstractHintOptions, - ) {} - - abstract getElementClientRect(): ClientRect; - - ngOnDestroy(): void { - this.hideTooltip(); - } - - protected showTooltip(): void { - this.hintService.add(this); - } - - protected hideTooltip(): void { - this.hintService.remove(this); - } -} diff --git a/projects/core/abstract/driver.ts b/projects/core/abstract/driver.ts new file mode 100644 index 0000000000000..69f81fc232cce --- /dev/null +++ b/projects/core/abstract/driver.ts @@ -0,0 +1,11 @@ +import {ExistingProvider, Type} from '@angular/core'; +import {Observable} from 'rxjs'; + +export abstract class TuiDriver extends Observable {} + +export function tuiAsDriver(useExisting: Type): ExistingProvider { + return { + provide: TuiDriver, + useExisting, + }; +} diff --git a/projects/core/abstract/index.ts b/projects/core/abstract/index.ts index ed353f0b06953..99435ab1d5cbb 100644 --- a/projects/core/abstract/index.ts +++ b/projects/core/abstract/index.ts @@ -1,4 +1,7 @@ +export * from './abstract-driver.directive'; export * from './abstract-dropdown'; -export * from './abstract-hint'; -export * from './abstract-hint-options'; export * from './abstract-textfield-host'; +export * from './driver'; +export * from './position-accessor'; +export * from './rect-accessor'; +export * from './vehicle'; diff --git a/projects/core/abstract/position-accessor.ts b/projects/core/abstract/position-accessor.ts new file mode 100644 index 0000000000000..9483907f61e20 --- /dev/null +++ b/projects/core/abstract/position-accessor.ts @@ -0,0 +1,15 @@ +import {ExistingProvider, Type} from '@angular/core'; +import {TuiPoint} from '@taiga-ui/core/types'; + +export abstract class TuiPositionAccessor { + abstract getPosition(rect: ClientRect): TuiPoint; +} + +export function tuiAsPositionAccessor( + useExisting: Type, +): ExistingProvider { + return { + provide: TuiPositionAccessor, + useExisting, + }; +} diff --git a/projects/core/abstract/rect-accessor.ts b/projects/core/abstract/rect-accessor.ts new file mode 100644 index 0000000000000..16076f09be28a --- /dev/null +++ b/projects/core/abstract/rect-accessor.ts @@ -0,0 +1,12 @@ +import {ExistingProvider, Type} from '@angular/core'; + +export abstract class TuiRectAccessor { + abstract getClientRect(): ClientRect; +} + +export function tuiAsRectAccessor(useExisting: Type): ExistingProvider { + return { + provide: TuiRectAccessor, + useExisting, + }; +} diff --git a/projects/core/abstract/vehicle.ts b/projects/core/abstract/vehicle.ts new file mode 100644 index 0000000000000..0ab17fbcac2d3 --- /dev/null +++ b/projects/core/abstract/vehicle.ts @@ -0,0 +1,12 @@ +import {ExistingProvider, Type} from '@angular/core'; + +export abstract class TuiVehicle { + abstract toggle(value: boolean): void; +} + +export function tuiAsVehicle(useExisting: Type): ExistingProvider { + return { + provide: TuiVehicle, + useExisting, + }; +} diff --git a/projects/core/components/hints-host/hint-box/hint-box.component.ts b/projects/core/components/hints-host/hint-box/hint-box.component.ts deleted file mode 100644 index e28a7def07241..0000000000000 --- a/projects/core/components/hints-host/hint-box/hint-box.component.ts +++ /dev/null @@ -1,294 +0,0 @@ -import {AnimationOptions} from '@angular/animations'; -import { - ChangeDetectionStrategy, - Component, - ElementRef, - forwardRef, - HostBinding, - Inject, - NgZone, - ViewChild, -} from '@angular/core'; -import {ANIMATION_FRAME, WINDOW} from '@ng-web-apis/common'; -import { - TuiContextWithImplicit, - TuiDestroyService, - tuiPure, - tuiPx, - tuiZonefree, -} from '@taiga-ui/cdk'; -import {AbstractTuiHint} from '@taiga-ui/core/abstract'; -import {tuiFadeIn} from '@taiga-ui/core/animations'; -import {TuiHintDirective} from '@taiga-ui/core/directives'; -import {TuiPointerHintDirective} from '@taiga-ui/core/directives/pointer-hint'; -import {TuiMedia} from '@taiga-ui/core/interfaces'; -import {TUI_ANIMATION_OPTIONS, TUI_MEDIA} from '@taiga-ui/core/tokens'; -import {TuiDirection, TuiHintMode} from '@taiga-ui/core/types'; -import {tuiIsMobile} from '@taiga-ui/core/utils'; -import {POLYMORPHEUS_CONTEXT} from '@tinkoff/ng-polymorpheus'; -import {Observable} from 'rxjs'; -import {takeUntil} from 'rxjs/operators'; - -import {TuiHintsHostComponent} from '../hints-host.component'; - -const SPACE = 8; -const BORDER_WIDTH = 1; -const LEFT_PADDING = 16; -const TOP_PADDING = 12; -const ARROW_SIZE = 8; -const ARROW_OFFSET = 16; -const ARROWHEAD_OFFSET = ARROW_OFFSET + (ARROW_SIZE * Math.sqrt(2)) / 2; -const reverseDirectionsVertical: {[key in TuiDirection]: TuiDirection} = { - 'top-left': `bottom-left`, - 'top-right': `bottom-right`, - 'bottom-left': `top-left`, - 'bottom-right': `top-right`, - left: `right`, - right: `left`, - 'top-middle': `bottom-middle`, - 'bottom-middle': `top-middle`, -}; -const reverseDirectionsHorizontal: {[key in TuiDirection]: TuiDirection} = { - 'top-left': `top-right`, - 'top-right': `top-left`, - 'bottom-left': `bottom-right`, - 'bottom-right': `bottom-left`, - left: `right`, - right: `left`, - 'top-middle': `top-middle`, - 'bottom-middle': `bottom-middle`, -}; - -// TODO: consider abstracting UI and move to CDK, split hint and overflow -@Component({ - selector: `tui-hint-box[hint]`, - templateUrl: `./hint-box.template.html`, - styleUrls: [`./hint-box.style.less`], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [TuiDestroyService], - animations: [tuiFadeIn], -}) -export class TuiHintBoxComponent { - @ViewChild(`arrow`) - private readonly arrow?: ElementRef; - - @HostBinding(`@tuiFadeIn`) - readonly animation = {value: ``, ...this.options} as const; - - constructor( - @Inject(ANIMATION_FRAME) animationFrame$: Observable, - @Inject(TuiDestroyService) destroy$: Observable, - @Inject(NgZone) ngZone: NgZone, - @Inject(TUI_ANIMATION_OPTIONS) private readonly options: AnimationOptions, - @Inject(ElementRef) private readonly elementRef: ElementRef, - @Inject(WINDOW) private readonly windowRef: Window, - @Inject(TUI_MEDIA) private readonly media: TuiMedia, - @Inject(forwardRef(() => TuiHintsHostComponent)) - private readonly hintsHost: TuiHintsHostComponent, - @Inject(POLYMORPHEUS_CONTEXT) - private readonly context: TuiContextWithImplicit, - ) { - animationFrame$ - .pipe(tuiZonefree(ngZone), takeUntil(destroy$)) - .subscribe(() => this.calculatePosition()); - } - - get hint(): AbstractTuiHint { - return this.context.$implicit; - } - - @tuiPure - @HostBinding(`class._untouchable`) - get isUntouchable(): boolean { - return this.hint instanceof TuiPointerHintDirective; - } - - @HostBinding(`attr.data-mode`) - get mode(): TuiHintMode | null { - return this.hint.mode; - } - - get isMobile(): boolean { - return tuiIsMobile(this.windowRef, this.media); - } - - onHovered(hovered: boolean): void { - if (this.hint instanceof TuiHintDirective) { - this.hint.componentHovered$.next(hovered); - } - } - - /** - * Calculates wrapper position. - * Styles are set directly to avoid visual shake of element - */ - private calculatePosition(): void { - if (this.mode !== `overflow`) { - this.calculateCoordinates(); - } else { - this.setOverflowStyles(); - } - } - - private calculateCoordinates(): void { - if (this.isMobile) { - this.calculateMobileCoordinates(); - - return; - } - - const hostRect = this.hint.getElementClientRect(); - const portalRect = this.hintsHost.clientRect; - const tooltip = this.elementRef.nativeElement; - const {style} = tooltip; - const tooltipRect = tooltip.getBoundingClientRect(); - const isHostLong = hostRect.width > ARROWHEAD_OFFSET * 2; - const directions: TuiDirection[] = [ - `left`, - `right`, - `bottom-left`, - `bottom-right`, - `bottom-middle`, - `top-left`, - `top-right`, - `top-middle`, - ]; - - let top = 0; - let left = 0; - let {direction} = this.hint; - - const horizontalTop = - hostRect.top + hostRect.height / 2 - tooltipRect.height / 2 - portalRect.top; - const horizontalLeft = - hostRect.left - tooltipRect.width - SPACE - portalRect.left; - const horizontalRight = hostRect.left + hostRect.width + SPACE - portalRect.left; - const verticalBottom = hostRect.bottom + SPACE - portalRect.top; - const verticalTop = hostRect.top - tooltipRect.height - SPACE - portalRect.top; - const verticalRight = isHostLong - ? hostRect.left - portalRect.left - : hostRect.left + hostRect.width / 2 - ARROWHEAD_OFFSET - portalRect.left; - const verticalLeft = isHostLong - ? hostRect.left - tooltipRect.width + hostRect.width - portalRect.left - : hostRect.left - - tooltipRect.width + - hostRect.width / 2 + - ARROWHEAD_OFFSET - - portalRect.left; - const verticalMiddle = - hostRect.left - tooltipRect.width / 2 + hostRect.width / 2 - portalRect.left; - - directions.splice(directions.indexOf(direction), 1); - - // eslint-disable-next-line no-constant-condition - while (true) { - switch (direction) { - case `left`: - top = horizontalTop; - left = horizontalLeft; - break; - case `right`: - top = horizontalTop; - left = horizontalRight; - break; - case `top-right`: - top = verticalTop; - left = verticalRight; - break; - case `top-left`: - top = verticalTop; - left = verticalLeft; - break; - case `bottom-right`: - top = verticalBottom; - left = verticalRight; - break; - case `bottom-left`: - top = verticalBottom; - left = verticalLeft; - break; - case `bottom-middle`: - top = verticalBottom; - left = verticalMiddle; - break; - case `top-middle`: - top = verticalTop; - left = verticalMiddle; - break; - } - - const verticalFit = - top + portalRect.top > SPACE && - top + tooltipRect.height + SPACE + portalRect.top < - this.windowRef.innerHeight; - const horizontalFit = - left > SPACE && - left + tooltipRect.width + SPACE + portalRect.left < portalRect.width; - - if (directions.length === 0 || (verticalFit && horizontalFit)) { - break; - } - - direction = verticalFit - ? reverseDirectionsHorizontal[direction] - : reverseDirectionsVertical[direction]; - direction = - directions.splice(directions.indexOf(direction), 1)[0] || direction; - } - - style.top = tuiPx(top); - style.left = tuiPx(left); - - tooltip.setAttribute(`data-tui-host-direction`, direction); - } - - private calculateMobileCoordinates(): void { - const hostRect = this.hint.getElementClientRect(); - const portalRect = this.hintsHost.clientRect; - const tooltip = this.elementRef.nativeElement; - const {style} = tooltip; - const tooltipRect = tooltip.getBoundingClientRect(); - const verticalTop = hostRect.top - tooltipRect.height - SPACE - portalRect.top; - const verticalBottom = hostRect.bottom + SPACE - portalRect.top; - const verticalTopFit = - verticalTop + portalRect.top > SPACE && - hostRect.top < this.windowRef.innerHeight; - const verticalBottomFit = - hostRect.bottom > 0 && - hostRect.bottom + 2 * SPACE + tooltipRect.height < this.windowRef.innerHeight; - const direction = - (this.hint.direction.includes(`top`) && verticalTopFit) || !verticalBottomFit - ? `top` - : `bottom`; - const attemptedLeft = - portalRect.left + hostRect.left + hostRect.width / 2 - tooltipRect.width / 2; - const left = Math.max( - attemptedLeft + tooltipRect.width + SPACE > portalRect.right - ? portalRect.right - SPACE - tooltipRect.width - : attemptedLeft, - SPACE * 2, - ); - - style.left = tuiPx(left); - style.top = direction === `top` ? tuiPx(verticalTop) : tuiPx(verticalBottom); - - if (this.arrow) { - this.arrow.nativeElement.style.left = tuiPx( - hostRect.left <= SPACE * 2 && hostRect.width > ARROW_OFFSET * 2 - ? ARROW_OFFSET - : hostRect.left + hostRect.width / 2 - left - ARROW_SIZE / 2, - ); - } - - tooltip.setAttribute(`data-tui-host-direction`, direction); - } - - private setOverflowStyles(): void { - const hostRect = this.hint.getElementClientRect(); - const {style} = this.elementRef.nativeElement; - - style.top = tuiPx(hostRect.top - window.innerHeight - TOP_PADDING - BORDER_WIDTH); - style.left = tuiPx(hostRect.left - LEFT_PADDING - BORDER_WIDTH); - style.width = tuiPx(hostRect.width + LEFT_PADDING * 2 + BORDER_WIDTH * 2); - } -} diff --git a/projects/core/components/hints-host/hint-box/hint-box.module.ts b/projects/core/components/hints-host/hint-box/hint-box.module.ts deleted file mode 100644 index ad8fccec03ac0..0000000000000 --- a/projects/core/components/hints-host/hint-box/hint-box.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {CommonModule} from '@angular/common'; -import {NgModule} from '@angular/core'; -import {TuiHoveredModule} from '@taiga-ui/cdk'; -import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus'; - -import {TuiHintBoxComponent} from './hint-box.component'; - -@NgModule({ - imports: [CommonModule, PolymorpheusModule, TuiHoveredModule], - declarations: [TuiHintBoxComponent], - exports: [TuiHintBoxComponent], - entryComponents: [TuiHintBoxComponent], -}) -export class TuiHintBoxModule {} diff --git a/projects/core/components/hints-host/hint-box/hint-box.style.less b/projects/core/components/hints-host/hint-box/hint-box.style.less deleted file mode 100644 index a269350b670f9..0000000000000 --- a/projects/core/components/hints-host/hint-box/hint-box.style.less +++ /dev/null @@ -1,145 +0,0 @@ -@import '../../../styles/taiga-ui-local.less'; - -@arrowSize: 0.5rem; -@arrowOffset: 0.875rem; -@arrowCompensation: 0.2rem; // 8px * 1.4 - 8px ATTENTION - -:host { - position: absolute; - top: 0; - left: 0; - max-width: 18rem; - min-height: var(--tui-height-m); - background: var(--tui-primary); - border-radius: var(--tui-radius-l); - color: var(--tui-primary-text); - box-sizing: border-box; - - &:not([data-mode='overflow']):before, - .t-arrow { - content: ''; - position: absolute; - width: @arrowSize; - height: @arrowSize; - border-radius: 0.125rem; - box-sizing: border-box; - background-color: inherit; - transform: rotate(45deg); - } - - &[data-tui-host-direction='top'] .t-arrow { - bottom: (-@arrowSize / 2); - } - - &[data-tui-host-direction='bottom'] .t-arrow { - top: (-@arrowSize / 2); - } - - &[data-tui-host-direction='top']:before, - &[data-tui-host-direction='bottom']:before { - display: none; - } - - &[data-tui-host-direction='top-left']:before, - &[data-tui-host-direction='top-right']:before, - &[data-tui-host-direction='top-middle']:before { - bottom: (-@arrowSize / 2); - } - - &[data-tui-host-direction='bottom-left']:before, - &[data-tui-host-direction='bottom-right']:before, - &[data-tui-host-direction='bottom-middle']:before { - top: (-@arrowSize / 2); - } - - &[data-tui-host-direction='bottom-middle']:before, - &[data-tui-host-direction='top-middle']:before { - left: calc(50% - @arrowCompensation); - } - - &[data-tui-host-direction='top-left']:before, - &[data-tui-host-direction='bottom-left']:before { - right: @arrowOffset + @arrowCompensation; - } - - &[data-tui-host-direction='top-right']:before, - &[data-tui-host-direction='bottom-right']:before { - left: @arrowOffset + @arrowCompensation; - } - - &[data-tui-host-direction='left']:before, - &[data-tui-host-direction='right']:before { - top: 50%; - margin-top: (-@arrowSize / 2); - } - - &[data-tui-host-direction='left']:before { - right: (-@arrowSize / 2); - } - - &[data-tui-host-direction='right']:before { - left: (-@arrowSize / 2); - } - - &[data-mode='error'] { - background-color: var(--tui-error-fill); - } - - &[data-mode='onDark'], - &[data-mode='overflow'] { - .shadow(2); - border: 1px solid var(--tui-base-03); - background-color: var(--tui-base-01); - color: var(--tui-text-01); - - &:before { - border: 1px solid var(--tui-base-03); - } - - &[data-tui-host-direction='left']:before { - border-left-color: transparent; - border-bottom-color: transparent; - } - - &[data-tui-host-direction='right']:before { - border-right-color: transparent; - border-top-color: transparent; - } - - &[data-tui-host-direction='top-left']:before, - &[data-tui-host-direction='top-right']:before, - &[data-tui-host-direction='top-middle']:before { - border-left-color: transparent; - border-top-color: transparent; - } - - &[data-tui-host-direction='bottom-left']:before, - &[data-tui-host-direction='bottom-right']:before, - &[data-tui-host-direction='bottom-middle']:before { - border-right-color: transparent; - border-bottom-color: transparent; - } - } - - &[data-mode='overflow'] { - max-width: none; - } - - &._untouchable { - pointer-events: none; - } -} - -.t-tooltip { - font: var(--tui-font-text-s); - word-wrap: break-word; - padding: @space * 3 @space * 4; - - :host[data-mode='overflow'] & { - font: inherit; - } -} - -.t-text { - white-space: pre-wrap; -} diff --git a/projects/core/components/hints-host/hint-box/hint-box.template.html b/projects/core/components/hints-host/hint-box/hint-box.template.html deleted file mode 100644 index f37079b4fcdcf..0000000000000 --- a/projects/core/components/hints-host/hint-box/hint-box.template.html +++ /dev/null @@ -1,17 +0,0 @@ - -
-
- -
diff --git a/projects/core/components/hints-host/hints-host.component.ts b/projects/core/components/hints-host/hints-host.component.ts index a970ae5223c82..9ce59efe0bd88 100644 --- a/projects/core/components/hints-host/hints-host.component.ts +++ b/projects/core/components/hints-host/hints-host.component.ts @@ -1,12 +1,9 @@ -import {ChangeDetectionStrategy, Component, ElementRef, Inject} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Inject} from '@angular/core'; import {TUI_PARENT_ANIMATION} from '@taiga-ui/cdk'; import {TuiHint} from '@taiga-ui/core/interfaces'; import {TuiHintService} from '@taiga-ui/core/services'; -import {PolymorpheusComponent} from '@tinkoff/ng-polymorpheus'; import {Observable} from 'rxjs'; -import {TuiHintBoxComponent} from './hint-box/hint-box.component'; - @Component({ selector: `tui-hints-host`, templateUrl: `./hints-host.template.html`, @@ -18,14 +15,7 @@ import {TuiHintBoxComponent} from './hint-box/hint-box.component'; }, }) export class TuiHintsHostComponent { - readonly component = new PolymorpheusComponent(TuiHintBoxComponent); - constructor( - @Inject(ElementRef) private readonly elementRef: ElementRef, @Inject(TuiHintService) readonly hints$: Observable, ) {} - - get clientRect(): ClientRect { - return this.elementRef.nativeElement.getBoundingClientRect(); - } } diff --git a/projects/core/components/hints-host/hints-host.module.ts b/projects/core/components/hints-host/hints-host.module.ts index 5a540115cffbc..ba2493a966b0b 100644 --- a/projects/core/components/hints-host/hints-host.module.ts +++ b/projects/core/components/hints-host/hints-host.module.ts @@ -3,11 +3,10 @@ import {NgModule} from '@angular/core'; import {TuiActiveZoneModule} from '@taiga-ui/cdk'; import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus'; -import {TuiHintBoxModule} from './hint-box/hint-box.module'; import {TuiHintsHostComponent} from './hints-host.component'; @NgModule({ - imports: [CommonModule, PolymorpheusModule, TuiHintBoxModule, TuiActiveZoneModule], + imports: [CommonModule, PolymorpheusModule, TuiActiveZoneModule], declarations: [TuiHintsHostComponent], exports: [TuiHintsHostComponent], }) diff --git a/projects/core/components/hints-host/hints-host.template.html b/projects/core/components/hints-host/hints-host.template.html index 98015f79b43ef..75a14f3fef1b2 100644 --- a/projects/core/components/hints-host/hints-host.template.html +++ b/projects/core/components/hints-host/hints-host.template.html @@ -5,5 +5,5 @@ [attr.id]="hint.id" [tuiActiveZoneParent]="hint.activeZone" > - + diff --git a/projects/core/components/hints-host/index.ts b/projects/core/components/hints-host/index.ts index 9020ddb89d61b..728a3e97f0c4a 100644 --- a/projects/core/components/hints-host/index.ts +++ b/projects/core/components/hints-host/index.ts @@ -1,4 +1,2 @@ -export * from './hint-box/hint-box.component'; -export * from './hint-box/hint-box.module'; export * from './hints-host.component'; export * from './hints-host.module'; diff --git a/projects/core/components/tooltip/tooltip.component.ts b/projects/core/components/tooltip/tooltip.component.ts index fc900941de1a5..bb3529ca7eefc 100644 --- a/projects/core/components/tooltip/tooltip.component.ts +++ b/projects/core/components/tooltip/tooltip.component.ts @@ -7,7 +7,11 @@ import { Input, } from '@angular/core'; import {TUI_IS_MOBILE, tuiDefaultProp, TuiDestroyService} from '@taiga-ui/cdk'; -import {TUI_HINT_OPTIONS, TuiHintOptions} from '@taiga-ui/core/directives'; +import { + TUI_HINT_OPTIONS, + TuiHintControllerDirective, + TuiHintOptions, +} from '@taiga-ui/core/directives'; import {MODE_PROVIDER} from '@taiga-ui/core/providers'; import {TUI_MODE} from '@taiga-ui/core/tokens'; import {TuiBrightness, TuiHintMode} from '@taiga-ui/core/types'; @@ -21,30 +25,11 @@ import {takeUntil} from 'rxjs/operators'; styleUrls: [`./tooltip.style.less`], changeDetection: ChangeDetectionStrategy.OnPush, providers: [TuiDestroyService, MODE_PROVIDER], + inputs: [`content`, `direction`, `mode`, `showDelay`, `hideDelay`], }) -export class TuiTooltipComponent { +export class TuiTooltipComponent extends TuiHintControllerDirective { private globalMode: TuiBrightness | null = null; - @Input() - @tuiDefaultProp() - content: PolymorpheusContent = ``; - - @Input() - @tuiDefaultProp() - mode: TuiHintOptions['mode'] = this.options.mode; - - @Input() - @tuiDefaultProp() - direction: TuiHintOptions['direction'] = this.options.direction; - - @Input() - @tuiDefaultProp() - showDelay: TuiHintOptions['tuiHintShowDelay'] = this.options.tuiHintShowDelay; - - @Input() - @tuiDefaultProp() - hideDelay: TuiHintOptions['tuiHintHideDelay'] = this.options.tuiHintHideDelay; - @Input() @tuiDefaultProp() describeId = ``; @@ -52,9 +37,11 @@ export class TuiTooltipComponent { constructor( @Inject(TuiDestroyService) destroy$: Observable, @Inject(TUI_MODE) mode$: Observable, + @Inject(TUI_HINT_OPTIONS) options: TuiHintOptions, @Inject(TUI_IS_MOBILE) private readonly isMobile: boolean, - @Inject(TUI_HINT_OPTIONS) private readonly options: TuiHintOptions, ) { + super(options); + mode$.pipe(takeUntil(destroy$)).subscribe(mode => { this.globalMode = mode; }); @@ -76,6 +63,6 @@ export class TuiTooltipComponent { } get icon(): PolymorpheusContent { - return this.options.tooltipIcon; + return this.options.icon; } } diff --git a/projects/core/constants/hint-directions.ts b/projects/core/constants/hint-directions.ts new file mode 100644 index 0000000000000..b92dfaf9c8b50 --- /dev/null +++ b/projects/core/constants/hint-directions.ts @@ -0,0 +1,16 @@ +import {TuiHintDirection} from '@taiga-ui/core/types'; + +export const TUI_HINT_DIRECTIONS: readonly TuiHintDirection[] = [ + `bottom-left`, + `bottom`, + `bottom-right`, + `top-left`, + `top`, + `top-right`, + `left-top`, + `left`, + `left-bottom`, + `right-top`, + `right`, + `right-bottom`, +]; diff --git a/projects/core/constants/index.ts b/projects/core/constants/index.ts index 0787aa4c55e88..30bc9105b6a9e 100644 --- a/projects/core/constants/index.ts +++ b/projects/core/constants/index.ts @@ -5,6 +5,7 @@ export * from './default-marker-handler'; export * from './described-by'; export * from './editing-keys'; export * from './events'; +export * from './hint-directions'; export * from './mask-caret-trap'; export * from './regexp'; export * from './version'; diff --git a/projects/core/directives/hint-controller/hint-controller.directive.ts b/projects/core/directives/hint-controller/hint-controller.directive.ts index 29f86a5c9dfa6..ba287061974fc 100644 --- a/projects/core/directives/hint-controller/hint-controller.directive.ts +++ b/projects/core/directives/hint-controller/hint-controller.directive.ts @@ -24,19 +24,20 @@ export class TuiHintControllerDirective extends AbstractTuiController { @tuiDefaultProp() direction: TuiHintOptions['direction'] = this.options.direction; + // TODO: Refactor @Input(`tuiHintMode`) @tuiDefaultProp() - mode: TuiHintOptions['mode'] = this.options.mode; + mode: any = null; @Input(`tuiHintShowDelay`) @tuiDefaultProp() - showDelay: TuiHintOptions['tuiHintShowDelay'] = this.options.tuiHintShowDelay; + showDelay: TuiHintOptions['showDelay'] = this.options.showDelay; @Input(`tuiHintHideDelay`) @tuiDefaultProp() - hideDelay: TuiHintOptions['tuiHintHideDelay'] = this.options.tuiHintHideDelay; + hideDelay: TuiHintOptions['hideDelay'] = this.options.hideDelay; - constructor(@Inject(TUI_HINT_OPTIONS) private readonly options: TuiHintOptions) { + constructor(@Inject(TUI_HINT_OPTIONS) protected readonly options: TuiHintOptions) { super(); } } diff --git a/projects/core/directives/hint/hint-driver.directive.ts b/projects/core/directives/hint/hint-driver.directive.ts new file mode 100644 index 0000000000000..9a90884308f8b --- /dev/null +++ b/projects/core/directives/hint/hint-driver.directive.ts @@ -0,0 +1,9 @@ +import {Directive} from '@angular/core'; +import {TuiDestroyService} from '@taiga-ui/cdk'; +import {AbstractTuiDriverDirective} from '@taiga-ui/core/abstract'; + +@Directive({ + selector: `[tuiHint]`, + providers: [TuiDestroyService], +}) +export class TuiHintDriverDirective extends AbstractTuiDriverDirective {} diff --git a/projects/core/directives/hint/hint-host.directive.ts b/projects/core/directives/hint/hint-host.directive.ts new file mode 100644 index 0000000000000..e5eafa1bb4f75 --- /dev/null +++ b/projects/core/directives/hint/hint-host.directive.ts @@ -0,0 +1,16 @@ +import {Directive, Input} from '@angular/core'; +import {EMPTY_CLIENT_RECT} from '@taiga-ui/cdk'; +import {tuiAsRectAccessor, TuiRectAccessor} from '@taiga-ui/core/abstract'; + +@Directive({ + selector: `[tuiHint][tuiHintHost]`, + providers: [tuiAsRectAccessor(TuiHintHostDirective)], +}) +export class TuiHintHostDirective extends TuiRectAccessor { + @Input() + tuiHintHost?: HTMLElement; + + getClientRect(): ClientRect { + return this.tuiHintHost?.getBoundingClientRect() || EMPTY_CLIENT_RECT; + } +} diff --git a/projects/core/directives/hint/hint-hover.directive.ts b/projects/core/directives/hint/hint-hover.directive.ts new file mode 100644 index 0000000000000..4960172d4010a --- /dev/null +++ b/projects/core/directives/hint/hint-hover.directive.ts @@ -0,0 +1,39 @@ +import {Directive, Inject, Input} from '@angular/core'; +import {tuiDefaultProp, TuiHoveredService} from '@taiga-ui/cdk'; +import {tuiAsDriver, TuiDriver} from '@taiga-ui/core/abstract'; +import {merge, Observable, of, Subject} from 'rxjs'; +import {delay, switchMap} from 'rxjs/operators'; + +import {TUI_HINT_OPTIONS, TuiHintOptions} from './hint-options'; + +@Directive({ + selector: `[tuiHint]:not(ng-container)`, + providers: [tuiAsDriver(TuiHintHoverDirective), TuiHoveredService], +}) +export class TuiHintHoverDirective extends TuiDriver { + private readonly toggle$ = new Subject(); + private readonly stream$ = merge(this.toggle$, this.hovered$).pipe( + switchMap(visible => + of(visible).pipe(delay(visible ? this.showDelay : this.hideDelay)), + ), + ); + + @Input(`tuiHintShowDelay`) + @tuiDefaultProp() + showDelay: TuiHintOptions['showDelay'] = this.options.showDelay; + + @Input(`tuiHintHideDelay`) + @tuiDefaultProp() + hideDelay: TuiHintOptions['hideDelay'] = this.options.hideDelay; + + constructor( + @Inject(TuiHoveredService) private readonly hovered$: Observable, + @Inject(TUI_HINT_OPTIONS) private readonly options: TuiHintOptions, + ) { + super(subscriber => this.stream$.subscribe(subscriber)); + } + + toggle(visible: boolean): void { + this.toggle$.next(visible); + } +} diff --git a/projects/core/directives/hint/hint-manual.directive.ts b/projects/core/directives/hint/hint-manual.directive.ts new file mode 100644 index 0000000000000..5204fbdd66f7b --- /dev/null +++ b/projects/core/directives/hint/hint-manual.directive.ts @@ -0,0 +1,20 @@ +import {Directive, Input} from '@angular/core'; +import {tuiAsDriver, TuiDriver} from '@taiga-ui/core/abstract'; +import {Subject} from 'rxjs'; + +@Directive({ + selector: `[tuiHint][tuiHintManual]`, + providers: [tuiAsDriver(TuiHintManualDirective)], +}) +export class TuiHintManualDirective extends TuiDriver { + private readonly stream$ = new Subject(); + + @Input() + set tuiHintManual(visible: boolean) { + this.stream$.next(visible); + } + + constructor() { + super(subscriber => this.stream$.subscribe(subscriber)); + } +} diff --git a/projects/core/directives/hint/hint-options.ts b/projects/core/directives/hint/hint-options.ts index 43e95076be63f..4ffa0a95117f7 100644 --- a/projects/core/directives/hint/hint-options.ts +++ b/projects/core/directives/hint/hint-options.ts @@ -1,26 +1,24 @@ import {InjectionToken, ValueProvider} from '@angular/core'; -import { - TUI_ABSTRACT_HINT_DEFAULT_OPTIONS, - TuiAbstractHintOptions, -} from '@taiga-ui/core/abstract'; +import {TuiHintDirection} from '@taiga-ui/core/types'; import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; -export interface TuiHintOptions extends TuiAbstractHintOptions { - readonly tuiHintShowDelay: number; - readonly tuiHintHideDelay: number; - readonly tooltipIcon: PolymorpheusContent; +export interface TuiHintOptions { + readonly direction: TuiHintDirection; + readonly showDelay: number; + readonly hideDelay: number; + readonly icon: PolymorpheusContent; } /** Default values for hint options */ export const TUI_HINT_DEFAULT_OPTIONS: TuiHintOptions = { - ...TUI_ABSTRACT_HINT_DEFAULT_OPTIONS, - tuiHintShowDelay: 500, - tuiHintHideDelay: 200, - tooltipIcon: `tuiIconTooltipLarge`, + direction: `bottom-left`, + showDelay: 500, + hideDelay: 200, + icon: `tuiIconTooltipLarge`, }; export const TUI_HINT_OPTIONS = new InjectionToken( - `Default parameters for hint directive`, + `[TUI_HINT_OPTIONS] Default parameters for hint directive`, { factory: () => TUI_HINT_DEFAULT_OPTIONS, }, diff --git a/projects/core/directives/hint/hint-pointer.directive.ts b/projects/core/directives/hint/hint-pointer.directive.ts new file mode 100644 index 0000000000000..ce3f0f48d6e91 --- /dev/null +++ b/projects/core/directives/hint/hint-pointer.directive.ts @@ -0,0 +1,28 @@ +import {Directive, HostListener} from '@angular/core'; +import {EMPTY_CLIENT_RECT, tuiPointToClientRect} from '@taiga-ui/cdk'; +import {tuiAsDriver, tuiAsRectAccessor, TuiRectAccessor} from '@taiga-ui/core/abstract'; + +import {TuiHintHoverDirective} from './hint-hover.directive'; + +@Directive({ + selector: `[tuiHint][tuiHintPointer]`, + providers: [ + tuiAsRectAccessor(TuiHintPointerDirective), + tuiAsDriver(TuiHintPointerDirective), + ], +}) +export class TuiHintPointerDirective + extends TuiHintHoverDirective + implements TuiRectAccessor +{ + private currentRect = EMPTY_CLIENT_RECT; + + @HostListener(`mousemove.silent`, [`$event`]) + onMove({clientX, clientY}: MouseEvent): void { + this.currentRect = tuiPointToClientRect(clientX, clientY); + } + + getClientRect(): ClientRect { + return this.currentRect; + } +} diff --git a/projects/core/directives/hint/hint-position.directive.ts b/projects/core/directives/hint/hint-position.directive.ts new file mode 100644 index 0000000000000..9852653dc7e16 --- /dev/null +++ b/projects/core/directives/hint/hint-position.directive.ts @@ -0,0 +1,92 @@ +import {Directive, Inject, Input} from '@angular/core'; +import {WINDOW} from '@ng-web-apis/common'; +import {tuiDefaultProp} from '@taiga-ui/cdk'; +import { + tuiAsPositionAccessor, + TuiPositionAccessor, + TuiRectAccessor, +} from '@taiga-ui/core/abstract'; +import {TUI_HINT_DIRECTIONS} from '@taiga-ui/core/constants'; +import {TuiHintDirection, TuiPoint} from '@taiga-ui/core/types'; + +import {TUI_HINT_OPTIONS, TuiHintOptions} from './hint-options'; + +const OFFSET = 8; +const ARROW_OFFSET = 22; + +@Directive({ + selector: `[tuiHint]:not([tuiHintCustomPosition])`, + providers: [tuiAsPositionAccessor(TuiHintPositionDirective)], +}) +export class TuiHintPositionDirective implements TuiPositionAccessor { + private readonly points: Record = + TUI_HINT_DIRECTIONS.reduce( + (acc, direction) => ({...acc, [direction]: [0, 0]}), + {} as any, + ); + + @Input(`tuiHintDirection`) + @tuiDefaultProp() + direction: TuiHintOptions['direction'] = this.options.direction; + + constructor( + @Inject(TUI_HINT_OPTIONS) private readonly options: TuiHintOptions, + @Inject(WINDOW) private readonly windowRef: Window, + @Inject(TuiRectAccessor) private readonly accessor: TuiRectAccessor, + ) {} + + getPosition({width, height}: ClientRect): TuiPoint { + const hostRect = this.accessor.getClientRect(); + const leftCenter = hostRect.left + hostRect.width / 2; + const topCenter = hostRect.top + hostRect.height / 2; + + this.points[`top-left`][0] = hostRect.top - height - OFFSET; + this.points[`top-left`][1] = leftCenter - width + ARROW_OFFSET; + this.points.top[0] = this.points[`top-left`][0]; + this.points.top[1] = leftCenter - width / 2; + this.points[`top-right`][0] = this.points[`top-left`][0]; + this.points[`top-right`][1] = leftCenter - ARROW_OFFSET; + + this.points[`bottom-left`][0] = hostRect.bottom + OFFSET; + this.points[`bottom-left`][1] = this.points[`top-left`][1]; + this.points.bottom[0] = this.points[`bottom-left`][0]; + this.points.bottom[1] = this.points.top[1]; + this.points[`bottom-right`][0] = this.points[`bottom-left`][0]; + this.points[`bottom-right`][1] = this.points[`top-right`][1]; + + this.points[`left-top`][0] = topCenter - height + ARROW_OFFSET; + this.points[`left-top`][1] = hostRect.left - width - OFFSET; + this.points.left[0] = topCenter - height / 2; + this.points.left[1] = this.points[`left-top`][1]; + this.points[`left-bottom`][0] = topCenter - ARROW_OFFSET; + this.points[`left-bottom`][1] = this.points[`left-top`][1]; + + this.points[`right-top`][0] = this.points[`left-top`][0]; + this.points[`right-top`][1] = hostRect.right + OFFSET; + this.points.right[0] = this.points.left[0]; + this.points.right[1] = this.points[`right-top`][1]; + this.points[`right-bottom`][0] = this.points[`left-bottom`][0]; + this.points[`right-bottom`][1] = this.points[`right-top`][1]; + + if (this.checkPosition(this.points[this.direction], width, height)) { + return this.points[this.direction]; + } + + const direction = TUI_HINT_DIRECTIONS.find(direction => + this.checkPosition(this.points[direction], width, height), + ); + + return this.points[direction || this.direction]; + } + + private checkPosition([top, left]: TuiPoint, width: number, height: number): boolean { + const {innerHeight, innerWidth} = this.windowRef; + + return ( + top > OFFSET && + left > OFFSET && + top + height < innerHeight - OFFSET && + left + width < innerWidth - OFFSET + ); + } +} diff --git a/projects/core/directives/hint/hint-position.service.ts b/projects/core/directives/hint/hint-position.service.ts new file mode 100644 index 0000000000000..20e10801d7f93 --- /dev/null +++ b/projects/core/directives/hint/hint-position.service.ts @@ -0,0 +1,27 @@ +import {ElementRef, Inject, Injectable, NgZone} from '@angular/core'; +import {ANIMATION_FRAME} from '@ng-web-apis/common'; +import {tuiZonefree} from '@taiga-ui/cdk'; +import {TuiPositionAccessor} from '@taiga-ui/core/abstract'; +import {TuiPoint} from '@taiga-ui/core/types'; +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; + +@Injectable() +export class TuiHintPositionService extends Observable { + constructor( + @Inject(ElementRef) {nativeElement}: ElementRef, + @Inject(ANIMATION_FRAME) animationFrame: Observable, + @Inject(NgZone) ngZone: NgZone, + @Inject(TuiPositionAccessor) accessor: TuiPositionAccessor, + ) { + super(subscriber => + animationFrame + .pipe( + map(() => nativeElement.getBoundingClientRect()), + map(rect => accessor.getPosition(rect)), + tuiZonefree(ngZone), + ) + .subscribe(subscriber), + ); + } +} diff --git a/projects/core/directives/hint/hint.component.ts b/projects/core/directives/hint/hint.component.ts new file mode 100644 index 0000000000000..bcf27eb8787f6 --- /dev/null +++ b/projects/core/directives/hint/hint.component.ts @@ -0,0 +1,87 @@ +import {AnimationOptions} from '@angular/animations'; +import { + ChangeDetectionStrategy, + Component, + ElementRef, + HostBinding, + Inject, +} from '@angular/core'; +import { + tuiClamp, + TuiContextWithImplicit, + TuiDestroyService, + TuiHoveredService, + tuiPure, + tuiPx, +} from '@taiga-ui/cdk'; +import {TuiDriver, TuiRectAccessor} from '@taiga-ui/core/abstract'; +import {tuiFadeIn} from '@taiga-ui/core/animations'; +import {TuiHint} from '@taiga-ui/core/interfaces'; +import {TUI_ANIMATION_OPTIONS} from '@taiga-ui/core/tokens'; +import {TuiPoint} from '@taiga-ui/core/types'; +import {POLYMORPHEUS_CONTEXT, PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; +import {Observable} from 'rxjs'; +import {takeUntil} from 'rxjs/operators'; + +import {TuiHintHoverDirective} from './hint-hover.directive'; +import {TuiHintPointerDirective} from './hint-pointer.directive'; +import {TuiHintPositionService} from './hint-position.service'; + +@Component({ + selector: `tui-hint`, + template: ` + {{ text }} + `, + styleUrls: [`./hint.style.less`], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [TuiDestroyService, TuiHintPositionService, TuiHoveredService], + animations: [tuiFadeIn], +}) +export class TuiHintComponent { + @HostBinding(`@tuiFadeIn`) + readonly animation = {value: ``, ...this.options} as const; + + constructor( + @Inject(TuiHoveredService) hovered$: Observable, + @Inject(TuiHintPositionService) position$: Observable, + @Inject(TuiDestroyService) destroy$: Observable, + @Inject(TuiRectAccessor) protected readonly accessor: TuiRectAccessor, + @Inject(ElementRef) private readonly elementRef: ElementRef, + @Inject(TUI_ANIMATION_OPTIONS) private readonly options: AnimationOptions, + @Inject(POLYMORPHEUS_CONTEXT) + private readonly context: TuiContextWithImplicit, + @Inject(TuiDriver) private readonly driver: TuiDriver, + ) { + position$.pipe(takeUntil(destroy$)).subscribe(([top, left]) => { + this.update(top, left); + }); + + if (driver instanceof TuiHintHoverDirective) { + hovered$.pipe(takeUntil(destroy$)).subscribe(hover => driver.toggle(hover)); + } + } + + @tuiPure + @HostBinding(`class._untouchable`) + get isUntouchable(): boolean { + return this.driver instanceof TuiHintPointerDirective; + } + + get content(): PolymorpheusContent { + return this.context.$implicit.content; + } + + private update(top: number, left: number): void { + const {nativeElement} = this.elementRef; + const {height, width} = nativeElement.getBoundingClientRect(); + const {style} = nativeElement; + const rect = this.accessor.getClientRect(); + const beakTop = rect.top + rect.height / 2 - top; + const beakLeft = rect.left + rect.width / 2 - left; + + style.top = tuiPx(top); + style.left = tuiPx(left); + style.setProperty(`--top`, tuiPx(tuiClamp(beakTop, 0, height - 1))); + style.setProperty(`--left`, tuiPx(tuiClamp(beakLeft, 0, width - 1))); + } +} diff --git a/projects/core/directives/hint/hint.directive.ts b/projects/core/directives/hint/hint.directive.ts index 970d9438936c7..5a1a2891887f4 100644 --- a/projects/core/directives/hint/hint.directive.ts +++ b/projects/core/directives/hint/hint.directive.ts @@ -2,130 +2,64 @@ import { Directive, ElementRef, Inject, + INJECTOR, Input, + OnChanges, OnDestroy, Optional, - Renderer2, - Self, } from '@angular/core'; +import {TuiActiveZoneDirective, tuiDefaultProp} from '@taiga-ui/cdk'; import { - TuiActiveZoneDirective, - tuiDefaultProp, - TuiDestroyService, - TuiHoveredService, - TuiObscuredService, - TuiParentsScrollService, - tuiRequiredSetter, -} from '@taiga-ui/cdk'; -import {AbstractTuiHint} from '@taiga-ui/core/abstract'; + tuiAsRectAccessor, + tuiAsVehicle, + TuiRectAccessor, + TuiVehicle, +} from '@taiga-ui/core/abstract'; import {DESCRIBED_BY} from '@taiga-ui/core/constants'; +import {TuiHint} from '@taiga-ui/core/interfaces'; import {TuiHintService} from '@taiga-ui/core/services'; -import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; -import {combineLatest, of, Subject} from 'rxjs'; -import { - delay, - distinctUntilChanged, - map, - startWith, - switchMap, - take, - takeUntil, -} from 'rxjs/operators'; +import {PolymorpheusComponent, PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; -import {TUI_HINT_OPTIONS, TuiHintOptions} from './hint-options'; +import {TUI_HINT_COMPONENT} from './hint.providers'; export const HINT_HOVERED_CLASS = `_hint_hovered`; @Directive({ selector: `[tuiHint]:not(ng-container)`, providers: [ - TuiObscuredService, - TuiParentsScrollService, - TuiDestroyService, - TuiHoveredService, + tuiAsRectAccessor(TuiHintDirective), + tuiAsVehicle(TuiHintDirective), + { + provide: PolymorpheusComponent, + deps: [TUI_HINT_COMPONENT, INJECTOR], + useClass: PolymorpheusComponent, + }, ], }) -export class TuiHintDirective extends AbstractTuiHint implements OnDestroy { - @Input() - tuiHintId?: string; - +export class TuiHintDirective + implements OnDestroy, OnChanges, TuiHint, TuiRectAccessor, TuiVehicle +{ @Input() @tuiDefaultProp() - tuiHintShowDelay: TuiHintOptions['tuiHintShowDelay'] = this.options.tuiHintShowDelay; + tuiHintId = ``; - @Input() + @Input(`tuiHint`) @tuiDefaultProp() - tuiHintHideDelay: TuiHintOptions['tuiHintHideDelay'] = this.options.tuiHintHideDelay; + content: PolymorpheusContent = ``; + // TODO: remove this method @Input() - @tuiDefaultProp() - tuiHintHost: HTMLElement | null = null; - - @Input() - @tuiRequiredSetter() - set tuiHint(value: PolymorpheusContent) { - if (!value) { - this.hideTooltip(); - this.content = ``; - - return; - } - - this.content = value; - } - - readonly componentHovered$ = new Subject(); + tuiHintMode: any; constructor( - @Inject(Renderer2) private readonly renderer: Renderer2, - @Inject(ElementRef) elementRef: ElementRef, - @Inject(TuiHintService) hintService: TuiHintService, - @Inject(TuiDestroyService) - destroy$: TuiDestroyService, - @Inject(TuiObscuredService) - @Self() - obscured$: TuiObscuredService, - @Inject(TuiHoveredService) hoveredService: TuiHoveredService, + @Inject(ElementRef) private readonly elementRef: ElementRef, + @Inject(PolymorpheusComponent) + readonly component: PolymorpheusComponent, @Optional() @Inject(TuiActiveZoneDirective) - activeZone: TuiActiveZoneDirective | null, - @Inject(TUI_HINT_OPTIONS) protected readonly options: TuiHintOptions, + readonly activeZone: TuiActiveZoneDirective | null, + @Inject(TuiHintService) private readonly hintService: TuiHintService, ) { - super(elementRef, hintService, activeZone, options); - - // @bad TODO: Use private provider - combineLatest(hoveredService, this.componentHovered$.pipe(startWith(false))) - .pipe( - map( - ([directiveHovered, componentHovered]) => - directiveHovered || componentHovered, - ), - switchMap(visible => { - this.toggleClass(visible); - - return of(visible).pipe( - delay(visible ? this.tuiHintShowDelay : this.tuiHintHideDelay), - ); - }), - switchMap(visible => - visible && this.mode !== `overflow` - ? obscured$.pipe( - map(obscured => !obscured), - take(2), - ) - : of(visible), - ), - distinctUntilChanged(), - takeUntil(destroy$), - ) - .subscribe(visible => { - if (visible) { - this.showTooltip(); - } else { - this.hideTooltip(); - } - }); - this.hintService.register(this); } @@ -133,37 +67,26 @@ export class TuiHintDirective extends AbstractTuiHint implements OnDestroy { return this.tuiHintId ? this.tuiHintId + DESCRIBED_BY : null; } - get host(): HTMLElement { - return this.tuiHintHost ? this.tuiHintHost : this.elementRef.nativeElement; - } - - getElementClientRect(): ClientRect { - return this.host.getBoundingClientRect(); + ngOnChanges(): void { + if (!this.content) { + this.toggle(false); + } } ngOnDestroy(): void { + this.toggle(false); this.hintService.unregister(this); } - protected showTooltip(): void { - if (this.content === ``) { - return; - } - - this.toggleClass(true); - this.hintService.add(this); - } - - protected hideTooltip(): void { - this.toggleClass(false); - this.hintService.remove(this); + getClientRect(): ClientRect { + return this.elementRef.nativeElement.getBoundingClientRect(); } - private toggleClass(add: boolean): void { - if (add) { - this.renderer.addClass(this.elementRef.nativeElement, HINT_HOVERED_CLASS); + toggle(show: boolean): void { + if (show && this.content) { + this.hintService.add(this); } else { - this.renderer.removeClass(this.elementRef.nativeElement, HINT_HOVERED_CLASS); + this.hintService.remove(this); } } } diff --git a/projects/core/directives/hint/hint.module.ts b/projects/core/directives/hint/hint.module.ts index 1fbb93f1bab39..43d09b66cea39 100644 --- a/projects/core/directives/hint/hint.module.ts +++ b/projects/core/directives/hint/hint.module.ts @@ -1,9 +1,38 @@ +import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; +import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus'; +import {TuiHintComponent} from './hint.component'; import {TuiHintDirective} from './hint.directive'; +import {TuiHintDriverDirective} from './hint-driver.directive'; +import {TuiHintHostDirective} from './hint-host.directive'; +import {TuiHintHoverDirective} from './hint-hover.directive'; +import {TuiHintManualDirective} from './hint-manual.directive'; +import {TuiHintPointerDirective} from './hint-pointer.directive'; +import {TuiHintPositionDirective} from './hint-position.directive'; @NgModule({ - declarations: [TuiHintDirective], - exports: [TuiHintDirective], + imports: [CommonModule, PolymorpheusModule], + declarations: [ + TuiHintComponent, + TuiHintDirective, + TuiHintDriverDirective, + TuiHintHostDirective, + TuiHintHoverDirective, + TuiHintManualDirective, + TuiHintPointerDirective, + TuiHintPositionDirective, + ], + exports: [ + TuiHintComponent, + TuiHintDirective, + TuiHintDriverDirective, + TuiHintHostDirective, + TuiHintHoverDirective, + TuiHintManualDirective, + TuiHintPointerDirective, + TuiHintPositionDirective, + ], + entryComponents: [TuiHintComponent], }) export class TuiHintModule {} diff --git a/projects/core/directives/hint/hint.providers.ts b/projects/core/directives/hint/hint.providers.ts new file mode 100644 index 0000000000000..b8ae38b09a20e --- /dev/null +++ b/projects/core/directives/hint/hint.providers.ts @@ -0,0 +1,10 @@ +import {InjectionToken, Type} from '@angular/core'; + +import {TuiHintComponent} from './hint.component'; + +export const TUI_HINT_COMPONENT = new InjectionToken>( + `[TUI_HINT_COMPONENT] A component to display a hint`, + { + factory: () => TuiHintComponent, + }, +); diff --git a/projects/core/directives/hint/hint.style.less b/projects/core/directives/hint/hint.style.less new file mode 100644 index 0000000000000..e2ffa51e0fbd9 --- /dev/null +++ b/projects/core/directives/hint/hint.style.less @@ -0,0 +1,50 @@ +@import '../../styles/taiga-ui-local.less'; + +:host { + position: absolute; + top: 0; + left: 0; + max-width: 18rem; + min-height: var(--tui-height-m); + padding: 0.75rem 1rem; + background: var(--tui-primary); + border-radius: var(--tui-radius-l); + color: var(--tui-primary-text); + box-sizing: border-box; + font: var(--tui-font-text-s); + white-space: pre-line; + word-wrap: break-word; + + &:after { + content: ''; + position: absolute; + top: var(--top); + left: var(--left); + width: 0.5rem; + height: 0.5rem; + border-radius: 0.125rem; + box-sizing: border-box; + background-color: inherit; + transform: translate(-50%, -50%) rotate(45deg); + } + + &[data-mode='error'] { + background-color: var(--tui-error-fill); + } + + &[data-mode='onDark'], + &[data-mode='overflow'] { + .shadow(2); + border: 1px solid var(--tui-base-03); + background-color: var(--tui-base-01); + color: var(--tui-text-01); + } + + &[data-mode='overflow'] { + max-width: none; + } + + &._untouchable { + pointer-events: none; + } +} diff --git a/projects/core/directives/hint/index.ts b/projects/core/directives/hint/index.ts index dd7b89c72d0c3..2feee240db769 100644 --- a/projects/core/directives/hint/index.ts +++ b/projects/core/directives/hint/index.ts @@ -1,3 +1,11 @@ +export * from './hint.component'; export * from './hint.directive'; export * from './hint.module'; +export * from './hint.providers'; +export * from './hint-driver.directive'; +export * from './hint-host.directive'; +export * from './hint-hover.directive'; +export * from './hint-manual.directive'; export * from './hint-options'; +export * from './hint-pointer.directive'; +export * from './hint-position.service'; diff --git a/projects/core/directives/index.ts b/projects/core/directives/index.ts index 94a66627118a9..a7c2b668b274a 100644 --- a/projects/core/directives/index.ts +++ b/projects/core/directives/index.ts @@ -3,10 +3,8 @@ export * from '@taiga-ui/core/directives/dropdown'; export * from '@taiga-ui/core/directives/dropdown-controller'; export * from '@taiga-ui/core/directives/hint'; export * from '@taiga-ui/core/directives/hint-controller'; -export * from '@taiga-ui/core/directives/manual-hint'; export * from '@taiga-ui/core/directives/mask-accessor'; export * from '@taiga-ui/core/directives/mode'; -export * from '@taiga-ui/core/directives/pointer-hint'; export * from '@taiga-ui/core/directives/scroll-into-view'; export * from '@taiga-ui/core/directives/textfield-controller'; export * from '@taiga-ui/core/directives/wrapper'; diff --git a/projects/core/directives/manual-hint/index.ts b/projects/core/directives/manual-hint/index.ts deleted file mode 100644 index 82863c7caf8cb..0000000000000 --- a/projects/core/directives/manual-hint/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './manual-hint.directive'; -export * from './manual-hint.module'; -export * from './manual-hint-options'; diff --git a/projects/core/directives/manual-hint/manual-hint-options.ts b/projects/core/directives/manual-hint/manual-hint-options.ts deleted file mode 100644 index b45ed83785138..0000000000000 --- a/projects/core/directives/manual-hint/manual-hint-options.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {InjectionToken, ValueProvider} from '@angular/core'; -import { - TUI_ABSTRACT_HINT_DEFAULT_OPTIONS, - TuiAbstractHintOptions, -} from '@taiga-ui/core/abstract'; - -export type TuiManualHintOptions = TuiAbstractHintOptions; - -/** Default values for manual hint options */ -export const TUI_MANUAL_HINT_DEFAULT_OPTIONS: TuiManualHintOptions = { - ...TUI_ABSTRACT_HINT_DEFAULT_OPTIONS, -}; - -export const TUI_MANUAL_HINT_OPTIONS = new InjectionToken( - `Default parameters for manual hint directive`, - { - factory: () => TUI_MANUAL_HINT_DEFAULT_OPTIONS, - }, -); - -export const tuiManualHintOptionsProvider: ( - options: Partial, -) => ValueProvider = (options: Partial) => ({ - provide: TUI_MANUAL_HINT_OPTIONS, - useValue: {...TUI_MANUAL_HINT_DEFAULT_OPTIONS, ...options}, -}); diff --git a/projects/core/directives/manual-hint/manual-hint.directive.ts b/projects/core/directives/manual-hint/manual-hint.directive.ts deleted file mode 100644 index 4cec138101926..0000000000000 --- a/projects/core/directives/manual-hint/manual-hint.directive.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Directive, ElementRef, Inject, Input, Optional} from '@angular/core'; -import {TuiActiveZoneDirective, tuiDefaultProp, tuiRequiredSetter} from '@taiga-ui/cdk'; -import {AbstractTuiHint} from '@taiga-ui/core/abstract'; -import {TuiHintService} from '@taiga-ui/core/services'; -import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; - -import {TUI_MANUAL_HINT_OPTIONS, TuiManualHintOptions} from './manual-hint-options'; - -@Directive({ - selector: `[tuiManualHint]:not(ng-container)`, -}) -export class TuiManualHintDirective extends AbstractTuiHint { - @Input(`tuiManualHint`) - @tuiDefaultProp() - content: PolymorpheusContent = ``; - - @Input() - @tuiRequiredSetter() - set tuiManualHintShow(show: boolean) { - if (show) { - this.showTooltip(); - } else { - this.hideTooltip(); - } - } - - constructor( - @Inject(ElementRef) elementRef: ElementRef, - @Inject(TuiHintService) hintService: TuiHintService, - @Optional() - @Inject(TuiActiveZoneDirective) - activeZone: TuiActiveZoneDirective | null, - @Inject(TUI_MANUAL_HINT_OPTIONS) protected readonly options: TuiManualHintOptions, - ) { - super(elementRef, hintService, activeZone, options); - } - - getElementClientRect(): ClientRect { - return this.elementRef.nativeElement.getBoundingClientRect(); - } -} diff --git a/projects/core/directives/manual-hint/manual-hint.module.ts b/projects/core/directives/manual-hint/manual-hint.module.ts deleted file mode 100644 index 11b3f08c07a6b..0000000000000 --- a/projects/core/directives/manual-hint/manual-hint.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {NgModule} from '@angular/core'; - -import {TuiManualHintDirective} from './manual-hint.directive'; - -@NgModule({ - declarations: [TuiManualHintDirective], - exports: [TuiManualHintDirective], -}) -export class TuiManualHintModule {} diff --git a/projects/core/directives/manual-hint/package.json b/projects/core/directives/manual-hint/package.json deleted file mode 100644 index 289d2d373a50c..0000000000000 --- a/projects/core/directives/manual-hint/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ngPackage": { - "lib": { - "entryFile": "index.ts" - } - } -} diff --git a/projects/core/directives/manual-hint/test/manual-hint.directive.spec.ts b/projects/core/directives/manual-hint/test/manual-hint.directive.spec.ts deleted file mode 100644 index c9f916e1c9c50..0000000000000 --- a/projects/core/directives/manual-hint/test/manual-hint.directive.spec.ts +++ /dev/null @@ -1,104 +0,0 @@ -import {CommonModule} from '@angular/common'; -import {Component, TemplateRef} from '@angular/core'; -import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {configureTestSuite} from '@taiga-ui/testing'; - -import {TuiRootModule} from '../../../components/root/root.module'; -import {TuiManualHintModule} from '../manual-hint.module'; - -type Hint = string | TemplateRef> | undefined | null; - -describe(`ManualHint`, () => { - @Component({ - template: ` - -
- Tooltip host -
-
- `, - styles: [ - ` - .host { - position: fixed; - top: 0; - left: 0; - right: 0; - } - `, - ], - }) - class TestComponent { - hint: Hint = `Tooltip text`; - show = false; - host = true; - } - - let fixture: ComponentFixture; - let component: TestComponent; - - configureTestSuite(() => { - TestBed.configureTestingModule({ - imports: [ - CommonModule, - TuiManualHintModule, - TuiRootModule, - NoopAnimationsModule, - ], - declarations: [TestComponent], - }); - }); - - beforeEach(() => { - document.body.style.margin = `0`; - fixture = TestBed.createComponent(TestComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it(`Tooltip is showed with flag`, fakeAsync(() => { - component.show = true; - - fixture.detectChanges(); - tick(); - - expect(getTooltip()).not.toBeNull(); - })); - - it(`Tooltip disappears if host disappears with flag`, async () => { - component.show = true; - - fixture.detectChanges(); - - component.host = false; - - fixture.detectChanges(); - - await fixture.whenStable(); - - fixture.detectChanges(); - expect(getTooltip()).toBeNull(); - }); - - it(`Tooltip is not showed with no flag`, async () => { - component.show = false; - - fixture.detectChanges(); - - await fixture.whenStable(); - - expect(getTooltip()).toBeNull(); - }); - - function getTooltip(): Element | null { - return document.querySelector(`[automation-id=tui-hint-box__tooltip]`); - } -}); diff --git a/projects/core/directives/pointer-hint/index.ts b/projects/core/directives/pointer-hint/index.ts deleted file mode 100644 index 64456eca588b1..0000000000000 --- a/projects/core/directives/pointer-hint/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './pointer-hint.directive'; -export * from './pointer-hint.module'; -export * from './pointer-hint-options'; diff --git a/projects/core/directives/pointer-hint/package.json b/projects/core/directives/pointer-hint/package.json deleted file mode 100644 index 289d2d373a50c..0000000000000 --- a/projects/core/directives/pointer-hint/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ngPackage": { - "lib": { - "entryFile": "index.ts" - } - } -} diff --git a/projects/core/directives/pointer-hint/pointer-hint-options.ts b/projects/core/directives/pointer-hint/pointer-hint-options.ts deleted file mode 100644 index 18bbf815588f2..0000000000000 --- a/projects/core/directives/pointer-hint/pointer-hint-options.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {InjectionToken, ValueProvider} from '@angular/core'; -import { - TUI_ABSTRACT_HINT_DEFAULT_OPTIONS, - TuiAbstractHintOptions, -} from '@taiga-ui/core/abstract'; - -export interface TuiPointerHintOptions extends TuiAbstractHintOptions { - readonly tuiHintShowDelay: number; - readonly tuiHintHideDelay: number; -} - -/** Default values for pointer hint options */ -export const TUI_POINTER_HINT_DEFAULT_OPTIONS: TuiPointerHintOptions = { - ...TUI_ABSTRACT_HINT_DEFAULT_OPTIONS, - tuiHintShowDelay: 0, - tuiHintHideDelay: 0, -}; - -export const TUI_POINTER_HINT_OPTIONS = new InjectionToken( - `Default parameters for pointer hint directive`, - { - factory: () => TUI_POINTER_HINT_DEFAULT_OPTIONS, - }, -); - -export const tuiPointerHintOptionsProvider: ( - options: Partial, -) => ValueProvider = (options: Partial) => ({ - provide: TUI_POINTER_HINT_OPTIONS, - useValue: {...TUI_POINTER_HINT_DEFAULT_OPTIONS, ...options}, -}); diff --git a/projects/core/directives/pointer-hint/pointer-hint.directive.ts b/projects/core/directives/pointer-hint/pointer-hint.directive.ts deleted file mode 100644 index 2134fe1b79caa..0000000000000 --- a/projects/core/directives/pointer-hint/pointer-hint.directive.ts +++ /dev/null @@ -1,123 +0,0 @@ -import {Directive, ElementRef, Inject, Input} from '@angular/core'; -import { - tuiDefaultProp, - TuiDestroyService, - TuiHoveredService, - tuiRequiredSetter, - typedFromEvent, -} from '@taiga-ui/cdk'; -import {AbstractTuiHint} from '@taiga-ui/core/abstract'; -import {TuiHintService} from '@taiga-ui/core/services'; -import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; -import {Observable, of} from 'rxjs'; -import { - delay, - distinctUntilChanged, - filter, - startWith, - switchMap, - takeUntil, -} from 'rxjs/operators'; - -import {TUI_POINTER_HINT_OPTIONS, TuiPointerHintOptions} from './pointer-hint-options'; - -@Directive({ - selector: `[tuiPointerHint]:not(ng-container)`, - providers: [TuiDestroyService, TuiHoveredService], -}) -export class TuiPointerHintDirective extends AbstractTuiHint { - private currentMouseRect = this.mousePositionToClientRect(); - - @Input() - @tuiDefaultProp() - tuiHintShowDelay: TuiPointerHintOptions['tuiHintShowDelay'] = - this.options.tuiHintShowDelay; - - @Input() - @tuiDefaultProp() - tuiHintHideDelay: TuiPointerHintOptions['tuiHintHideDelay'] = - this.options.tuiHintHideDelay; - - @Input() - @tuiRequiredSetter() - set tuiPointerHint(value: PolymorpheusContent) { - if (!value) { - this.hideTooltip(); - this.content = ``; - - return; - } - - this.content = value; - } - - content: PolymorpheusContent = ``; - - constructor( - @Inject(ElementRef) elementRef: ElementRef, - @Inject(TuiHintService) hintService: TuiHintService, - @Inject(TuiDestroyService) - private readonly destroy$: TuiDestroyService, - @Inject(TuiHoveredService) hoveredService: TuiHoveredService, - @Inject(TUI_POINTER_HINT_OPTIONS) - protected readonly options: TuiPointerHintOptions, - ) { - super(elementRef, hintService, null, options); - - const hint$ = hoveredService.pipe( - filter(() => !!this.content), - startWith(false), - distinctUntilChanged(), - ); - - hint$ - .pipe( - switchMap(visible => - of(visible).pipe( - delay(visible ? this.tuiHintShowDelay : this.tuiHintHideDelay), - ), - ), - takeUntil(destroy$), - ) - .subscribe({ - next: visible => { - if (visible) { - this.showTooltip(); - } else { - this.hideTooltip(); - } - }, - complete: () => { - this.hideTooltip(); - }, - }); - - this.initMouseMoveSubscription(); - } - - getElementClientRect(): ClientRect { - return this.currentMouseRect; - } - - private initMouseMoveSubscription(): void { - const mouseMove$: Observable = typedFromEvent( - this.elementRef.nativeElement, - `mousemove`, - ); - - mouseMove$.pipe(takeUntil(this.destroy$)).subscribe(({clientX, clientY}) => { - this.currentMouseRect = this.mousePositionToClientRect(clientX, clientY); - }); - } - - private mousePositionToClientRect(x: number = 0, y: number = 0): ClientRect { - return { - left: x, - right: x, - top: y, - bottom: y, - width: 0, - height: 0, - }; - } -} diff --git a/projects/core/directives/pointer-hint/pointer-hint.module.ts b/projects/core/directives/pointer-hint/pointer-hint.module.ts deleted file mode 100644 index 4774d763355af..0000000000000 --- a/projects/core/directives/pointer-hint/pointer-hint.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {NgModule} from '@angular/core'; - -import {TuiPointerHintDirective} from './pointer-hint.directive'; - -@NgModule({ - declarations: [TuiPointerHintDirective], - exports: [TuiPointerHintDirective], -}) -export class TuiPointerHintModule {} diff --git a/projects/core/directives/pointer-hint/test/pointer-hint.directive.spec.ts b/projects/core/directives/pointer-hint/test/pointer-hint.directive.spec.ts deleted file mode 100644 index 6b3cb26de9fb8..0000000000000 --- a/projects/core/directives/pointer-hint/test/pointer-hint.directive.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -import {Component, ElementRef, ViewChild} from '@angular/core'; -import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {configureTestSuite} from '@taiga-ui/testing'; - -import {TuiRootModule} from '../../../components/root/root.module'; -import {TuiPointerHintDirective} from '../pointer-hint.directive'; -import {TuiPointerHintModule} from '../pointer-hint.module'; - -describe(`PointerHint`, () => { - @Component({ - template: ` - -
- Tooltip host -
-
- `, - styles: [ - ` - .host { - position: fixed; - top: 0; - left: 0; - right: 0; - } - `, - ], - }) - class TestComponent { - @ViewChild(TuiPointerHintDirective, {static: true}) - directive!: TuiPointerHintDirective; - - @ViewChild(`hintHost`, {static: true}) - host!: ElementRef; - - hint = `Tooltip text`; - } - - let fixture: ComponentFixture; - let component: TestComponent; - let hostElement: HTMLElement; - let directive: TuiPointerHintDirective; - - configureTestSuite(() => { - TestBed.configureTestingModule({ - imports: [TuiPointerHintModule, TuiRootModule, NoopAnimationsModule], - declarations: [TestComponent], - }); - }); - - beforeEach(() => { - document.body.style.margin = `0`; - fixture = TestBed.createComponent(TestComponent); - component = fixture.componentInstance; - hostElement = component.host.nativeElement; - directive = component.directive; - fixture.detectChanges(); - }); - - it(`is not shown by default`, fakeAsync(() => { - expect(getTooltip()).toBeNull(); - })); - - it(`has default clientRect`, fakeAsync(() => { - const clientRect = directive.getElementClientRect(); - - expect(clientRect.left).toBe(0); - expect(clientRect.top).toBe(0); - })); - - it(`recalculates clientRect from user mouse moving`, fakeAsync(() => { - const hoverEvent = new MouseEvent(`hover`); - - hostElement.dispatchEvent(hoverEvent); - - component.host.nativeElement.dispatchEvent( - new MouseEvent(`mousemove`, { - clientX: 10, - clientY: 10, - }), - ); - - const clientRect = directive.getElementClientRect(); - - expect(clientRect.left).toBe(10); - expect(clientRect.top).toBe(10); - })); - - function getTooltip(): Element | null { - return document.querySelector(`[automation-id=tui-hint-box__tooltip]`); - } -}); diff --git a/projects/core/services/hint.service.ts b/projects/core/services/hint.service.ts index 18c1723aa5466..29a2a90c71f97 100644 --- a/projects/core/services/hint.service.ts +++ b/projects/core/services/hint.service.ts @@ -1,45 +1,42 @@ import {Injectable} from '@angular/core'; +import {DESCRIBED_BY} from '@taiga-ui/core/constants'; +import {TuiHint} from '@taiga-ui/core/interfaces'; import {BehaviorSubject} from 'rxjs'; -// It prevented seps cycling -type TuiHintDirective = any; - -type AbstractTuiHint = any; - /** * Service for displaying hints/tooltips */ @Injectable({ providedIn: `root`, }) -export class TuiHintService extends BehaviorSubject { +export class TuiHintService extends BehaviorSubject { /** * TODO: * We need the following logic for desribedBy * move it into another service that can register hints and * manage it using TuiHintService inside */ - private directives: readonly TuiHintDirective[] = []; + private directives: readonly TuiHint[] = []; constructor() { super([]); } - add(directive: AbstractTuiHint): void { + add(directive: TuiHint): void { this.next(this.value.concat(directive)); } - remove(directive: AbstractTuiHint): void { + remove(directive: TuiHint): void { if (this.value.includes(directive)) { this.next(this.value.filter(hint => hint !== directive)); } } - register(directive: TuiHintDirective): void { + register(directive: TuiHint): void { this.directives = [...this.directives, directive]; } - unregister(directive: TuiHintDirective): void { + unregister(directive: TuiHint): void { this.remove(directive); this.directives = this.directives.filter(dir => dir !== directive); } @@ -60,7 +57,7 @@ export class TuiHintService extends BehaviorSubject } } - private findDirectiveWithHintId(id: string): TuiHintDirective | undefined { - return this.directives.find(directive => directive.tuiHintId === id); + private findDirectiveWithHintId(id: string): TuiHint | undefined { + return this.directives.find(directive => directive.id === id + DESCRIBED_BY); } } diff --git a/projects/core/types/hint-direction.ts b/projects/core/types/hint-direction.ts new file mode 100644 index 0000000000000..60a008b5527c3 --- /dev/null +++ b/projects/core/types/hint-direction.ts @@ -0,0 +1,13 @@ +export type TuiHintDirection = + | 'top-left' + | 'top' + | 'top-right' + | 'bottom-left' + | 'bottom' + | 'bottom-right' + | 'left-top' + | 'left' + | 'left-bottom' + | 'right-top' + | 'right' + | 'right-bottom'; diff --git a/projects/core/types/index.ts b/projects/core/types/index.ts index 673125383c818..f68790f724dc5 100644 --- a/projects/core/types/index.ts +++ b/projects/core/types/index.ts @@ -5,6 +5,7 @@ export * from './decimal-symbol'; export * from './dialog-size'; export * from './direction'; export * from './dropdown-width'; +export * from './hint-direction'; export * from './hint-mode'; export * from './marker-handler'; export * from './option-role'; diff --git a/projects/core/types/point.ts b/projects/core/types/point.ts index 32ed0604be6d1..fff4bd9ed523b 100644 --- a/projects/core/types/point.ts +++ b/projects/core/types/point.ts @@ -1 +1 @@ -export type TuiPoint = Readonly<[number, number]>; +export type TuiPoint = Readonly<[top: number, left: number]>; diff --git a/projects/demo/src/modules/app/app.const.ts b/projects/demo/src/modules/app/app.const.ts index a67f8d611524c..5419820f5c696 100644 --- a/projects/demo/src/modules/app/app.const.ts +++ b/projects/demo/src/modules/app/app.const.ts @@ -1,5 +1,5 @@ export const SEE_ALSO_GROUPS: ReadonlyArray = [ - [`Tooltip`, `Hint`, `ManualHint`], + [`Tooltip`, `Hint`, `HintManual`, `HintPointer`], [`LineChart`, `LineDaysChart`], [`PrimitiveTextfield`, `Input`, `TextArea`], [ diff --git a/projects/demo/src/modules/app/app.routes.ts b/projects/demo/src/modules/app/app.routes.ts index cad57e1f28786..d532888d2ea24 100644 --- a/projects/demo/src/modules/app/app.routes.ts +++ b/projects/demo/src/modules/app/app.routes.ts @@ -1164,21 +1164,21 @@ export const ROUTES = [ }, }, { - path: `directives/manual-hint`, + path: `directives/hint-manual`, loadChildren: async () => - (await import(`../directives/manual-hint/manual-hint.module`)) - .ExampleTuiManualHintModule, + (await import(`../directives/hint-manual/hint-manual.module`)) + .ExampleTuiHintManualModule, data: { - title: `ManualHint`, + title: `HintManual`, }, }, { - path: `directives/pointer-hint`, + path: `directives/hint-pointer`, loadChildren: async () => - (await import(`../directives/pointer-hint/pointer-hint.module`)) - .ExampleTuiPointerHintModule, + (await import(`../directives/hint-pointer/hint-pointer.module`)) + .ExampleTuiHintPointerModule, data: { - title: `PointerHint`, + title: `HintPointer`, }, }, { diff --git a/projects/demo/src/modules/app/pages.ts b/projects/demo/src/modules/app/pages.ts index f7f1ff783f04b..9931c74e42faa 100644 --- a/projects/demo/src/modules/app/pages.ts +++ b/projects/demo/src/modules/app/pages.ts @@ -368,6 +368,36 @@ export const pages: TuiDocPages = [ keywords: `buttongroup, форма, поле, кнопка, группировка, группа, Group`, route: `/components/group`, }, + { + section: $localize`Components`, + title: `Hint`, + subPages: [ + { + section: $localize`Components`, + title: `Tooltip`, + keywords: `tooltip, тултип, hint, подсказка, помощь, help`, + route: `/components/tooltip`, + }, + { + section: $localize`Tools`, + title: `Hint`, + keywords: `tooltip, тултип, hint, подсказка, помощь, help, хинт`, + route: `/directives/hint`, + }, + { + section: $localize`Tools`, + title: `ManualHint`, + keywords: `tooltip, тултип, hint, подсказка, помощь, help, manual, программный, хинт`, + route: `/directives/hint-manual`, + }, + { + section: $localize`Tools`, + title: `PointerHint`, + keywords: `tooltip, тултип, hint, подсказка, помощь, help, хинт, курсор`, + route: `/directives/hint-pointer`, + }, + ], + }, { section: $localize`Components`, title: `MarkerIcon`, @@ -790,12 +820,6 @@ export const pages: TuiDocPages = [ keywords: `инпут, форма, ввод, toggle, переключение`, route: `/components/toggle`, }, - { - section: $localize`Components`, - title: `Tooltip`, - keywords: `tooltip, тултип, hint, подсказка, помощь, help`, - route: `/components/tooltip`, - }, { section: $localize`Components`, title: `Tree`, @@ -965,36 +989,18 @@ export const pages: TuiDocPages = [ keywords: `поиск, подсветка, search, find, найти`, route: `/directives/highlight`, }, - { - section: $localize`Tools`, - title: `Hint`, - keywords: `tooltip, тултип, hint, подсказка, помощь, help, хинт`, - route: `/directives/hint`, - }, { section: $localize`Tools`, title: `LazyLoading`, keywords: `img, skeleton, скелетон, загрузка, картинки`, route: `/directives/lazy-loading`, }, - { - section: $localize`Tools`, - title: `ManualHint`, - keywords: `tooltip, тултип, hint, подсказка, помощь, help, manual, программный, хинт`, - route: `/directives/manual-hint`, - }, { section: $localize`Tools`, title: `Overscroll`, keywords: `overscroll, scroll, скролл`, route: `/directives/overscroll`, }, - { - section: $localize`Tools`, - title: `PointerHint`, - keywords: `tooltip, тултип, hint, подсказка, помощь, help, хинт, курсор`, - route: `/directives/pointer-hint`, - }, { section: $localize`Tools`, title: `Pan`, diff --git a/projects/demo/src/modules/components/abstract/control.ts b/projects/demo/src/modules/components/abstract/control.ts index 4b939abc2313c..58ccc0a1d85f6 100644 --- a/projects/demo/src/modules/components/abstract/control.ts +++ b/projects/demo/src/modules/components/abstract/control.ts @@ -3,7 +3,7 @@ import {TuiAutofillFieldName, TuiInputMode, TuiInputType} from '@taiga-ui/cdk'; import { DEFAULT_MAX_HEIGHT, DEFAULT_MIN_HEIGHT, - TuiDirection, + TUI_HINT_DIRECTIONS, TuiDropdownWidth, TuiHintMode, TuiHorizontalDirection, @@ -39,16 +39,7 @@ export abstract class AbstractExampleTuiControl readonly hintContentVariants: readonly string[] = [`Some content`]; - readonly hintDirectionVariants: readonly TuiDirection[] = [ - `left`, - `right`, - `bottom-left`, - `bottom-right`, - `bottom-middle`, - `top-left`, - `top-right`, - `top-middle`, - ]; + readonly hintDirectionVariants = TUI_HINT_DIRECTIONS; readonly hintModeVariants: readonly TuiHintMode[] = [`error`, `onDark`]; @@ -118,7 +109,7 @@ export abstract class AbstractExampleTuiControl hintContent: string | null = null; - hintDirection: TuiDirection = this.hintDirectionVariants[2]; + hintDirection = this.hintDirectionVariants[0]; hintMode: TuiHintMode | null = null; diff --git a/projects/demo/src/modules/components/abstract/hint.ts b/projects/demo/src/modules/components/abstract/hint.ts index 6aadc0cc0a1df..86e0858801a05 100644 --- a/projects/demo/src/modules/components/abstract/hint.ts +++ b/projects/demo/src/modules/components/abstract/hint.ts @@ -1,20 +1,11 @@ -import {TuiDirection, TuiHintMode} from '@taiga-ui/core'; +import {TUI_HINT_DIRECTIONS, TuiHintMode} from '@taiga-ui/core'; export abstract class AbstractExampleTuiHint { readonly modeVariants: readonly TuiHintMode[] = [`error`, `onDark`]; mode: TuiHintMode | null = null; - readonly directionVariants: readonly TuiDirection[] = [ - `left`, - `right`, - `bottom-left`, - `bottom-right`, - `bottom-middle`, - `top-left`, - `top-right`, - `top-middle`, - ]; + readonly directionVariants = TUI_HINT_DIRECTIONS; - direction: TuiDirection = this.directionVariants[5]; + direction = this.directionVariants[0]; } diff --git a/projects/demo/src/modules/components/abstract/inherited-documentation/inherited-documentation.component.ts b/projects/demo/src/modules/components/abstract/inherited-documentation/inherited-documentation.component.ts index 04aa78dd26d15..cb29e5c612eb6 100644 --- a/projects/demo/src/modules/components/abstract/inherited-documentation/inherited-documentation.component.ts +++ b/projects/demo/src/modules/components/abstract/inherited-documentation/inherited-documentation.component.ts @@ -1,6 +1,6 @@ import {Component, Inject, Input} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; -import {TuiDirection, TuiHintMode} from '@taiga-ui/core'; +import {TUI_HINT_DIRECTIONS, TuiHintMode} from '@taiga-ui/core'; import {AbstractExampleTuiControl} from '../control'; import {AbstractExampleTuiHint} from '../hint'; @@ -19,16 +19,7 @@ export class InheritedDocumentationComponent { readonly booleanVariants: readonly boolean[] = [false, true]; - readonly directionVariants: readonly TuiDirection[] = [ - `left`, - `right`, - `bottom-left`, - `bottom-right`, - `bottom-middle`, - `top-left`, - `top-right`, - `top-middle`, - ]; + readonly directionVariants = TUI_HINT_DIRECTIONS; readonly modeVariants: readonly TuiHintMode[] = [`error`, `onDark`]; diff --git a/projects/demo/src/modules/components/line-clamp/examples/1/index.html b/projects/demo/src/modules/components/line-clamp/examples/1/index.html index bdc4f148df332..5cf2023eabda0 100644 --- a/projects/demo/src/modules/components/line-clamp/examples/1/index.html +++ b/projects/demo/src/modules/components/line-clamp/examples/1/index.html @@ -1,14 +1,29 @@ - + + Use + white-space: nowrap + to expand to the right + + + + +
Daenerys of the House Targaryen, the First of Her Name, The Unburnt, Queen of the Andals, the Rhoynar and the First Men, Queen of Meereen, Khaleesi of the Great Grass Sea, Protector of the Realm, Lady Regent of the Seven Kingdoms, Breaker of Chains and Mother of Dragons
+ +
Jorah Mormont of House Mormont, Lord of Bear Island
+
diff --git a/projects/demo/src/modules/components/line-clamp/examples/1/index.less b/projects/demo/src/modules/components/line-clamp/examples/1/index.less index 4cc0c405a240f..f2b3105005691 100644 --- a/projects/demo/src/modules/components/line-clamp/examples/1/index.less +++ b/projects/demo/src/modules/components/line-clamp/examples/1/index.less @@ -1,13 +1,15 @@ @import 'taiga-ui-local'; -:host { - display: block; -} - .island { width: 20rem; + margin-bottom: 1rem; + box-sizing: border-box; } .hint { font: var(--tui-font-text-s); } + +.no-wrap { + white-space: nowrap; +} diff --git a/projects/demo/src/modules/components/line-clamp/line-clamp.module.ts b/projects/demo/src/modules/components/line-clamp/line-clamp.module.ts index 0cd51b968adaa..24a5202f56662 100644 --- a/projects/demo/src/modules/components/line-clamp/line-clamp.module.ts +++ b/projects/demo/src/modules/components/line-clamp/line-clamp.module.ts @@ -2,7 +2,7 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; import {TuiAddonDocModule, tuiGenerateRoutes} from '@taiga-ui/addon-doc'; -import {TuiButtonModule} from '@taiga-ui/core'; +import {TuiButtonModule, TuiNotificationModule} from '@taiga-ui/core'; import {TuiIslandModule, TuiLineClampModule} from '@taiga-ui/kit'; import {TuiLineClampExample1} from './examples/1'; @@ -13,6 +13,7 @@ import {ExampleTuiLineClampComponent} from './line-clamp.component'; @NgModule({ imports: [ CommonModule, + TuiNotificationModule, TuiLineClampModule, TuiAddonDocModule, RouterModule.forChild(tuiGenerateRoutes(ExampleTuiLineClampComponent)), diff --git a/projects/demo/src/modules/components/line-clamp/line-clamp.template.html b/projects/demo/src/modules/components/line-clamp/line-clamp.template.html index 8d7d289d4363a..cfa5888787701 100644 --- a/projects/demo/src/modules/components/line-clamp/line-clamp.template.html +++ b/projects/demo/src/modules/components/line-clamp/line-clamp.template.html @@ -4,7 +4,7 @@ type="components" > -

Component cuts overflowed text with "..." and shows it by hover

+

Component cuts overflown text with "..." and shows it by hover

diff --git a/projects/demo/src/modules/components/slider/slider.module.ts b/projects/demo/src/modules/components/slider/slider.module.ts index 4a53c2423d8c3..ce2ef794b3e97 100644 --- a/projects/demo/src/modules/components/slider/slider.module.ts +++ b/projects/demo/src/modules/components/slider/slider.module.ts @@ -5,8 +5,8 @@ import {RouterModule} from '@angular/router'; import {TuiAddonDocModule, tuiGenerateRoutes} from '@taiga-ui/addon-doc'; import { TuiButtonModule, + TuiHintModule, TuiLinkModule, - TuiManualHintModule, TuiModeModule, TuiNotificationModule, } from '@taiga-ui/core'; @@ -31,7 +31,7 @@ import {ExampleTuiSliderComponent} from './slider.component'; TuiNotificationModule, TuiModeModule, TuiButtonModule, - TuiManualHintModule, + TuiHintModule, RouterModule.forChild(tuiGenerateRoutes(ExampleTuiSliderComponent)), ], declarations: [ diff --git a/projects/demo/src/modules/components/tooltip/examples/4/index.ts b/projects/demo/src/modules/components/tooltip/examples/4/index.ts index 3c3d0e4076fda..37cfec51a727a 100644 --- a/projects/demo/src/modules/components/tooltip/examples/4/index.ts +++ b/projects/demo/src/modules/components/tooltip/examples/4/index.ts @@ -10,7 +10,7 @@ import {tuiHintOptionsProvider} from '@taiga-ui/core'; encapsulation, providers: [ tuiHintOptionsProvider({ - tooltipIcon: `tuiIconCameraLarge`, + icon: `tuiIconCameraLarge`, }), ], }) diff --git a/projects/demo/src/modules/components/tooltip/tooltip.component.ts b/projects/demo/src/modules/components/tooltip/tooltip.component.ts index a70511954a567..0ff5741ccf9e6 100644 --- a/projects/demo/src/modules/components/tooltip/tooltip.component.ts +++ b/projects/demo/src/modules/components/tooltip/tooltip.component.ts @@ -1,7 +1,7 @@ import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDocExample} from '@taiga-ui/addon-doc'; -import {TuiDirection, TuiHintMode} from '@taiga-ui/core'; +import {TUI_HINT_DIRECTIONS, TuiHintMode} from '@taiga-ui/core'; @Component({ selector: `example-tooltip`, @@ -40,18 +40,9 @@ export class ExampleTuiTooltipComponent { mode: TuiHintMode | null = null; - directionVariants: readonly TuiDirection[] = [ - `left`, - `right`, - `bottom-left`, - `bottom-right`, - `bottom-middle`, - `top-left`, - `top-right`, - `top-middle`, - ]; - - direction: TuiDirection = this.directionVariants[2]; + directionVariants = TUI_HINT_DIRECTIONS; + + direction = this.directionVariants[0]; describeId = ``; diff --git a/projects/demo/src/modules/directives/hint-controller/hint-controller.component.ts b/projects/demo/src/modules/directives/hint-controller/hint-controller.component.ts index 32d6622600b64..389f755ca7aa6 100644 --- a/projects/demo/src/modules/directives/hint-controller/hint-controller.component.ts +++ b/projects/demo/src/modules/directives/hint-controller/hint-controller.component.ts @@ -1,7 +1,7 @@ import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDocExample} from '@taiga-ui/addon-doc'; -import {TuiDirection, TuiHintMode} from '@taiga-ui/core'; +import {TUI_HINT_DIRECTIONS, TuiHintMode} from '@taiga-ui/core'; @Component({ selector: `example-tui-hint-controller`, @@ -23,18 +23,9 @@ export class ExampleTuiHintControllerComponent { tuiHintMode: TuiHintMode | null = null; - readonly directionVariants: readonly TuiDirection[] = [ - `left`, - `right`, - `bottom-left`, - `bottom-right`, - `bottom-middle`, - `top-left`, - `top-right`, - `top-middle`, - ]; - - tuiHintDirection: TuiDirection = this.directionVariants[2]; + readonly directionVariants = TUI_HINT_DIRECTIONS; + + tuiHintDirection = this.directionVariants[0]; tuiHintShowDelay = 500; diff --git a/projects/demo/src/modules/directives/manual-hint/examples/1/index.html b/projects/demo/src/modules/directives/hint-manual/examples/1/index.html similarity index 81% rename from projects/demo/src/modules/directives/manual-hint/examples/1/index.html rename to projects/demo/src/modules/directives/hint-manual/examples/1/index.html index d8d4f64e38fc7..9e2a319d7af97 100644 --- a/projects/demo/src/modules/directives/manual-hint/examples/1/index.html +++ b/projects/demo/src/modules/directives/hint-manual/examples/1/index.html @@ -1,8 +1,8 @@ @@ -11,8 +11,8 @@ diff --git a/projects/demo/src/modules/directives/manual-hint/manual-hint.component.ts b/projects/demo/src/modules/directives/hint-manual/hint-manual.component.ts similarity index 80% rename from projects/demo/src/modules/directives/manual-hint/manual-hint.component.ts rename to projects/demo/src/modules/directives/hint-manual/hint-manual.component.ts index 258143f329852..6be4328a19f4d 100644 --- a/projects/demo/src/modules/directives/manual-hint/manual-hint.component.ts +++ b/projects/demo/src/modules/directives/hint-manual/hint-manual.component.ts @@ -6,17 +6,17 @@ import {AbstractExampleTuiHint} from '../../components/abstract/hint'; import {ABSTRACT_PROPS_ACCESSOR} from '../../components/abstract/inherited-documentation/abstract-props-accessor'; @Component({ - selector: `example-manual-hint`, - templateUrl: `./manual-hint.template.html`, + selector: `example-hint-manual`, + templateUrl: `./hint-manual.template.html`, changeDetection, providers: [ { provide: ABSTRACT_PROPS_ACCESSOR, - useExisting: forwardRef(() => ExampleTuiManualHintComponent), + useExisting: forwardRef(() => ExampleTuiHintManualComponent), }, ], }) -export class ExampleTuiManualHintComponent extends AbstractExampleTuiHint { +export class ExampleTuiHintManualComponent extends AbstractExampleTuiHint { readonly exampleModule = import(`./examples/import/import-module.md?raw`); readonly exampleHtml = import(`./examples/import/insert-template.md?raw`); diff --git a/projects/demo/src/modules/directives/manual-hint/manual-hint.module.ts b/projects/demo/src/modules/directives/hint-manual/hint-manual.module.ts similarity index 64% rename from projects/demo/src/modules/directives/manual-hint/manual-hint.module.ts rename to projects/demo/src/modules/directives/hint-manual/hint-manual.module.ts index e96a4835cbf1b..207154c013b8d 100644 --- a/projects/demo/src/modules/directives/manual-hint/manual-hint.module.ts +++ b/projects/demo/src/modules/directives/hint-manual/hint-manual.module.ts @@ -4,29 +4,29 @@ import {RouterModule} from '@angular/router'; import {TuiAddonDocModule, tuiGenerateRoutes} from '@taiga-ui/addon-doc'; import { TuiButtonModule, + TuiHintModule, TuiLinkModule, - TuiManualHintModule, TuiModeModule, } from '@taiga-ui/core'; import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus'; import {InheritedDocumentationModule} from '../../components/abstract/inherited-documentation/inherited-documentation.module'; -import {TuiManualHintExample1} from './examples/1'; -import {ExampleTuiManualHintComponent} from './manual-hint.component'; +import {TuiHintManualExample1} from './examples/1'; +import {ExampleTuiHintManualComponent} from './hint-manual.component'; @NgModule({ imports: [ TuiButtonModule, - TuiManualHintModule, + TuiHintModule, TuiLinkModule, TuiModeModule, PolymorpheusModule, CommonModule, InheritedDocumentationModule, TuiAddonDocModule, - RouterModule.forChild(tuiGenerateRoutes(ExampleTuiManualHintComponent)), + RouterModule.forChild(tuiGenerateRoutes(ExampleTuiHintManualComponent)), ], - declarations: [ExampleTuiManualHintComponent, TuiManualHintExample1], - exports: [ExampleTuiManualHintComponent], + declarations: [ExampleTuiHintManualComponent, TuiHintManualExample1], + exports: [ExampleTuiHintManualComponent], }) -export class ExampleTuiManualHintModule {} +export class ExampleTuiHintManualModule {} diff --git a/projects/demo/src/modules/directives/manual-hint/manual-hint.template.html b/projects/demo/src/modules/directives/hint-manual/hint-manual.template.html similarity index 84% rename from projects/demo/src/modules/directives/manual-hint/manual-hint.template.html rename to projects/demo/src/modules/directives/hint-manual/hint-manual.template.html index fb37c475192ef..ae9e1f3bd80dd 100644 --- a/projects/demo/src/modules/directives/manual-hint/manual-hint.template.html +++ b/projects/demo/src/modules/directives/hint-manual/hint-manual.template.html @@ -1,5 +1,5 @@ @@ -12,7 +12,7 @@ heading="Basic" [content]="example1" > - +
@@ -21,8 +21,8 @@