From 42698bf27a7b83273368c5d20abdd955d07212d6 Mon Sep 17 00:00:00 2001 From: lskramarov Date: Thu, 18 Jun 2026 17:49:17 +0300 Subject: [PATCH] feat(title): support custom content and multiple text elements (#DS-3672) --- packages/components-dev/title/styles.scss | 2 - packages/components-dev/title/template.html | 17 -- .../components/core/pop-up/pop-up-trigger.ts | 179 +++++++++++++++--- .../filter-bar/filter-bar.module.ts | 2 - .../filter-bar/pipes/pipe-date.html | 2 +- .../components/filter-bar/pipes/pipe-date.ts | 2 - .../filter-bar/pipes/pipe-datetime.html | 2 +- .../filter-bar/pipes/pipe-datetime.ts | 2 - .../filter-bar/pipes/pipe-multi-select.html | 2 +- .../filter-bar/pipes/pipe-multi-select.ts | 2 - .../pipes/pipe-multi-tree-select.html | 2 +- .../pipes/pipe-multi-tree-select.ts | 2 - .../filter-bar/pipes/pipe-readonly.ts | 4 +- .../filter-bar/pipes/pipe-select.html | 2 +- .../filter-bar/pipes/pipe-select.ts | 2 - .../filter-bar/pipes/pipe-text.html | 2 +- .../components/filter-bar/pipes/pipe-text.ts | 2 - .../components/filter-bar/pipes/pipe-title.ts | 127 ------------- .../filter-bar/pipes/pipe-tree-select.html | 2 +- .../filter-bar/pipes/pipe-tree-select.ts | 2 - packages/components/filter-bar/public-api.ts | 1 - .../components/title/title.directive.spec.ts | 109 ++++++++++- packages/components/title/title.directive.ts | 122 +++++++++++- packages/components/title/title.en.md | 53 ++++++ packages/components/title/title.ru.md | 53 ++++++ .../components/tooltip/tooltip.component.ts | 60 +++++- .../filter-bar-custom-pipe-example.ts | 4 +- .../docs-examples/components/title/index.ts | 18 +- .../title-custom-content-example.css | 20 ++ .../title-custom-content-example.ts | 40 ++++ .../title-list-option-example.css | 10 + .../title-list-option-example.ts | 57 ++++++ .../title-multiple-text-example.css | 25 +++ .../title-multiple-text-example.ts | 24 +++ .../title-overview/title-overview-example.css | 19 -- .../title-overview/title-overview-example.ts | 19 -- .../title-vertical-overflow-example.css | 11 ++ .../title-vertical-overflow-example.ts | 21 ++ packages/docs-examples/example-module.ts | 60 ++++++ tools/cspell-locales/ru.json | 2 + tools/public_api_guard/components/core.api.md | 63 +----- .../components/filter-bar.api.md | 31 +-- .../public_api_guard/components/title.api.md | 19 +- .../components/tooltip.api.md | 31 +-- 44 files changed, 833 insertions(+), 398 deletions(-) delete mode 100644 packages/components/filter-bar/pipes/pipe-title.ts create mode 100644 packages/docs-examples/components/title/title-custom-content/title-custom-content-example.css create mode 100644 packages/docs-examples/components/title/title-custom-content/title-custom-content-example.ts create mode 100644 packages/docs-examples/components/title/title-list-option/title-list-option-example.css create mode 100644 packages/docs-examples/components/title/title-list-option/title-list-option-example.ts create mode 100644 packages/docs-examples/components/title/title-multiple-text/title-multiple-text-example.css create mode 100644 packages/docs-examples/components/title/title-multiple-text/title-multiple-text-example.ts create mode 100644 packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.css create mode 100644 packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.ts diff --git a/packages/components-dev/title/styles.scss b/packages/components-dev/title/styles.scss index 77538c94ab..c6fcd4ca8d 100644 --- a/packages/components-dev/title/styles.scss +++ b/packages/components-dev/title/styles.scss @@ -1,8 +1,6 @@ .dev-container { max-width: 400px; - border: 1px solid red; - padding: 24px; display: flex; diff --git a/packages/components-dev/title/template.html b/packages/components-dev/title/template.html index 46dbf3d514..78edebe0df 100644 --- a/packages/components-dev/title/template.html +++ b/packages/components-dev/title/template.html @@ -28,29 +28,12 @@
-
Responsive text with tooltip

{{ longValue }}
-
-
Tooltip tracking for parent & child as parameters
-
- -
-
-
- {{ field }} -
-
-
- - - -
-
Select/autocomplete with long KbqOptions and tooltips

diff --git a/packages/components/core/pop-up/pop-up-trigger.ts b/packages/components/core/pop-up/pop-up-trigger.ts index 13584bc1ac..666f377dcf 100644 --- a/packages/components/core/pop-up/pop-up-trigger.ts +++ b/packages/components/core/pop-up/pop-up-trigger.ts @@ -89,6 +89,12 @@ const getOffset = ( return offset; }; +/** + * Abstract base directive for hover/focus/click-triggered pop-ups (e.g. tooltip and popover). It manages the + * CDK overlay lifecycle, binds the trigger event listeners, resolves placement via a flexible connected position + * strategy, and controls show/hide timing. Concrete subclasses supply the pop-up component type, its content, + * and the overlay configuration. + */ @Directive({ host: { '(mouseenter)': 'hovered.next(true)', @@ -96,27 +102,49 @@ const getOffset = ( } }) export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { - /** Stream that emits when the popupTrigger is hovered. */ + /** Stream that emits when the popupTrigger is hovered. + * @docs-private */ readonly hovered = new BehaviorSubject(false); + /** RxJS scheduler that drives the hide timeout; `undefined` falls back to the default async scheduler. + * @docs-private */ protected readonly scheduler = inject(AsyncScheduler, { optional: true }) || undefined; - + /** CDK Overlay service used to create and position the pop-up overlay. + * @docs-private */ protected readonly overlay: Overlay = inject(Overlay); + /** Reference to the host element the pop-up is anchored to. + * @docs-private */ protected readonly elementRef = inject>(ElementRef); + /** Angular zone, used to run the hide timer outside Angular and re-enter when hiding. + * @docs-private */ protected readonly ngZone: NgZone = inject(NgZone); + /** CDK ScrollDispatcher, used to track ancestor scroll containers so the pop-up repositions on scroll. + * @docs-private */ protected readonly scrollDispatcher: ScrollDispatcher = inject(ScrollDispatcher); + /** View container the pop-up component portal is attached to. + * @docs-private */ protected readonly hostView: ViewContainerRef = inject(ViewContainerRef); + /** Text direction (LTR/RTL) applied to the overlay when a `Directionality` is available. + * @docs-private */ protected readonly direction = inject(Directionality, { optional: true }); + /** Destroy reference used to auto-unsubscribe streams when the directive is destroyed. + * @docs-private */ protected readonly destroyRef = inject(DestroyRef); + /** Nearest `CdkScrollable` ancestor, if any. + * @docs-private */ protected readonly scrollable = inject(CdkScrollable, { optional: true }); - + /** Factory returning the overlay scroll strategy; provided by the concrete subclass. + * @docs-private */ protected abstract scrollStrategy: () => ScrollStrategy; - /** @docs-private */ + /** Optional element used to anchor and measure the pop-up instead of the host element. + * @docs-private */ protected externalNativeElement: HTMLElement; + /** Change detector used to refresh the trigger when its open state changes. */ private popUpChangeDetectorRef = inject(ChangeDetectorRef); + /** Whether the pop-up overlay is currently open. */ get isOpen(): boolean { return this._isOpen; } @@ -127,69 +155,124 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { this.popUpChangeDetectorRef.markForCheck(); } + /** Backing field for `isOpen`. */ private _isOpen: boolean = false; + /** Delay in milliseconds before the pop-up is shown. + * @docs-private */ enterDelay: number = 0; + /** Delay in milliseconds before the pop-up is hidden. + * @docs-private */ leaveDelay: number = 0; - + /** Name of the DOM event that last triggered show/hide (e.g. `mouseenter`, `focus`, `click`). + * @docs-private */ triggerName: string; - + /** Reference to the created overlay, or `null` when no overlay exists. + * @docs-private */ overlayRef: OverlayRef | null; - + /** Placement used to keep the pop-up stuck within the window bounds. + * @docs-private */ stickToWindow: KbqStickToWindowPlacementValues; - + /** Host container element of the pop-up. + * @docs-private */ container: HTMLElement; - + /** Whether the pop-up is disabled and therefore never shown; implemented by the subclass. + * @docs-private */ abstract disabled: boolean; + /** Whether the arrow/pointer is rendered; implemented by the subclass. + * @docs-private */ abstract arrow: boolean; + /** Space/comma-separated list of trigger events (`hover`, `focus`, `click`, `keydown`); implemented by the subclass. + * @docs-private */ abstract trigger: string; + /** Extra CSS class applied to the pop-up; implemented by the subclass. + * @docs-private */ abstract customClass: string; + /** Pop-up content as a string or template; implemented by the subclass. + * @docs-private */ abstract content: string | TemplateRef; - + /** Emits when the resolved placement changes; implemented by the subclass. + * @docs-private */ abstract placementChange: EventEmitter; + /** Emits when the pop-up visibility changes; implemented by the subclass. + * @docs-private */ abstract visibleChange: EventEmitter; - + /** CSS selector of the element used as the overlay's transform origin; implemented by the subclass. + * @docs-private */ protected abstract originSelector: string; + /** Base overlay configuration (panel classes, etc.); implemented by the subclass. + * @docs-private */ protected abstract overlayConfig: OverlayConfig; - + /** Current resolved placement of the pop-up. + * @docs-private */ protected placement: KbqPopUpPlacementValues = PopUpPlacements.Top; + /** Ordered list of fallback placements to try before the default priority. + * @docs-private */ protected placementPriority: string | string[] | null = null; - + /** Whether the pop-up is currently visible. + * @docs-private */ protected visible = false; - + /** Backing field for the subclass `content` input. + * @docs-private */ protected _content: string | TemplateRef; + /** Backing field for the subclass `disabled` input. + * @docs-private */ protected _disabled: boolean; + /** Backing field for the subclass `customClass` input. + * @docs-private */ protected _customClass: string; - + /** Component portal attached to the overlay to render the pop-up. + * @docs-private */ protected portal: ComponentPortal; + /** Currently attached pop-up component instance, or `null` when closed. + * @docs-private */ protected instance: any | null; - + /** Map of event name to the handler currently bound on the host element. + * @docs-private */ protected listeners = new Map(); + /** Subscription to the streams that should close the pop-up. + * @docs-private */ protected closingActionsSubscription: Subscription; - + /** Map of placement name to its CDK connected position pair. + * @docs-private */ protected readonly availablePositions: { [key: string]: ConnectionPositionPair } = POSITION_MAP; - + /** Last mouse event, used to position the pop-up relative to the cursor. + * @docs-private */ protected mouseEvent?: MouseEvent; + /** Flexible connected position strategy driving the overlay. + * @docs-private */ protected strategy: FlexibleConnectedPositionStrategy; - /** Hide pop-up with timeout. Need if you want to show pop-up after leaving trigger */ + /** Hide pop-up with timeout. Need if you want to show pop-up after leaving trigger + * @docs-private */ protected hideWithTimeout: boolean = false; - /** prevents closure by any event */ + /** prevents closure by any event + * @docs-private */ protected preventClose: boolean = false; + /** Applies the placement (and modifier) CSS classes to the open pop-up; implemented by the subclass. + * @docs-private */ abstract updateClassMap(newPlacement?: string): void; + /** Pushes the current inputs (content, arrow, offset, …) into the open pop-up; implemented by the subclass. + * @docs-private */ abstract updateData(): void; + /** Returns the stream of events that should close the pop-up; implemented by the subclass. + * @docs-private */ abstract closingActions(): Observable; + /** Returns the component type to attach as the pop-up overlay; implemented by the subclass. + * @docs-private */ abstract getOverlayHandleComponentType(): Type; + /** Binds the DOM event listeners for the configured triggers. */ ngOnInit(): void { this.initListeners(); } + /** Disposes the overlay and removes all bound event listeners. */ ngOnDestroy(): void { this.overlayRef?.dispose(); @@ -198,6 +281,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { this.listeners.clear(); } + /** Sets the placement (falling back to `Top` on an unknown value) and refreshes the classes and position. + * @docs-private */ updatePlacement(value: KbqPopUpPlacementValues) { if (POSITION_TO_CSS_MAP[value]) { this.placement = value; @@ -215,6 +300,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { } } + /** Sets the ordered list of fallback placements, or clears it when the value is empty. + * @docs-private */ updatePlacementPriority(value) { if (value && value.length > 0) { this.placementPriority = value; @@ -223,6 +310,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { } } + /** Coerces and applies an external visibility value by showing or hiding the pop-up when it changes. + * @docs-private */ updateVisible(externalValue: boolean) { const value = coerceBooleanProperty(externalValue); @@ -237,16 +326,22 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { } } + /** Closes the pop-up when `Escape` is pressed while it is open. Bound to the host `keydown` event. + * @docs-private */ keydownHandler(event: KeyboardEvent) { if (this.isOpen && event.keyCode === ESCAPE) { this.hide(); } } + /** Hides the pop-up. Bound to the host `touchend` event. + * @docs-private */ touchendHandler() { this.hide(); } + /** Creates the overlay (if needed) and shows the pop-up after `delay` ms, wiring its visibility stream. + * @docs-private */ show(delay: number = this.enterDelay): void { if (this.disabled || this.instance) { return; @@ -293,6 +388,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { } } + /** Hides the pop-up after `delay` ms, honoring `preventClose` and the hover state on mouseleave. + * @docs-private */ hide(delay: number = this.leaveDelay) { if (this.preventClose) return; @@ -304,6 +401,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { } } + /** Detaches the overlay (if attached) and clears the current pop-up instance. + * @docs-private */ detach = (): void => { if (this.overlayRef?.hasAttached()) { this.overlayRef.detach(); @@ -312,7 +411,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { this.instance = null; }; - /** Create the overlay config and position strategy */ + /** Create the overlay config and position strategy + * @docs-private */ createOverlay(): OverlayRef { if (this.overlayRef) { return this.overlayRef; @@ -344,14 +444,16 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { return this.overlayRef; } + /** Resets the overlay position origin back to the host element. + * @docs-private */ resetOrigin() { this.strategy.setOrigin(this.getNativeElement()); } + /** Maps the CDK connection position back to a placement name, emits `placementChange`, and updates classes. + * @docs-private */ onPositionChange = ($event: ConnectedOverlayPositionChange): void => { - if (!this.instance) { - return; - } + if (!this.instance) return; let newPlacement = this.placement; @@ -379,6 +481,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { this.instance.detectChanges(); }; + /** Rebinds the host DOM listeners (click/hover/focus/keydown) according to the configured triggers. + * @docs-private */ initListeners() { this.clearListeners(); @@ -409,6 +513,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { this.listeners.forEach(this.addEventListener); } + /** Returns the `mouseleave` handler — a delayed hide when `hideWithTimeout` is set, otherwise an immediate hide. + * @docs-private */ getMouseLeaveListener() { if (this.hideWithTimeout) { return () => setTimeout(() => this.hide(), this.leaveDelay); @@ -417,7 +523,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { return this.hide; } - /** Updates the position of the current popover. */ + /** Updates the position of the current popover. + * @docs-private */ updatePosition(reapplyPosition: boolean = false) { this.overlayRef = this.createOverlay(); @@ -432,24 +539,27 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { } } + /** Moves browser focus to the pop-up's native element. + * @docs-private */ focus() { this.getNativeElement().focus(); } - /** @docs-private */ + /** Returns the element the pop-up is bound to: the external element when set, otherwise the host element. + * @docs-private */ getNativeElement(): HTMLElement { return this.externalNativeElement || this.elementRef.nativeElement; } - /** @docs-private */ + /** Sets an external element to anchor and measure the pop-up against instead of the host element. + * @docs-private */ setExternalNativeElement(value: HTMLElement) { this.externalNativeElement = value; } /** * Returns a list of positions that are aligned with the element's dimensions and offsets. - * @protected - */ + * @docs-private */ protected getAdjustedPositions(): ConnectionPositionPair[] { const res: ConnectionPositionPair[] = []; @@ -467,6 +577,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { return res; } + /** Maps a priority placement value (or array of values) to the matching connected position pairs. + * @docs-private */ protected getPriorityPlacementStrategy(value: string | string[]): ConnectionPositionPair[] { const result: ConnectionPositionPair[] = []; const possiblePositions = Object.keys(this.availablePositions); @@ -484,6 +596,8 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { return result; } + /** Returns the positions to try: the custom `placementPriority` when set, otherwise the default strategy. + * @docs-private */ protected getPrioritizedPositions() { if (this.placementPriority) { return this.getPriorityPlacementStrategy(this.placementPriority); @@ -492,12 +606,15 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { return POSITION_PRIORITY_STRATEGY[this.placement]; } + /** Removes all bound host event listeners and clears the listener map. + * @docs-private */ protected clearListeners() { this.listeners.forEach(this.removeEventListener); this.listeners.clear(); } + /** Wraps a trigger handler so it records the active trigger name and mouse event before running. */ private createListener(name: string, listener: () => void): [string, (event: unknown) => void] { return [ name, @@ -510,20 +627,24 @@ export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { ]; } + /** Remembers the last mouse event on `mouseenter`, used for cursor-relative positioning. */ private saveMouseEvent(event: MouseEvent) { if (this.triggerName === 'mouseenter') { this.mouseEvent = event; } } + /** Binds a single event listener to the pop-up's native element. */ private addEventListener = (listener: EventListener, event: string) => { this.getNativeElement().addEventListener(event, listener); }; + /** Unbinds a single event listener from the pop-up's native element. */ private removeEventListener = (listener: EventListener, event: string) => { this.getNativeElement().removeEventListener(event, listener); }; + /** (Re)subscribes to the closing-action streams and hides the pop-up, ignoring prevented click events. */ private subscribeOnClosingActions() { this.closingActionsSubscription?.unsubscribe(); diff --git a/packages/components/filter-bar/filter-bar.module.ts b/packages/components/filter-bar/filter-bar.module.ts index 09ea64b600..a7b277440d 100644 --- a/packages/components/filter-bar/filter-bar.module.ts +++ b/packages/components/filter-bar/filter-bar.module.ts @@ -9,7 +9,6 @@ import { KbqPipeAdd } from './pipe-add'; import { KbqPipeDirective } from './pipe.directive'; import { KbqPipeButton } from './pipes/pipe-button'; import { KbqPipeState } from './pipes/pipe-state'; -import { KbqPipeTitleDirective } from './pipes/pipe-title'; const COMPONENTS = [ KbqFilterBar, @@ -20,7 +19,6 @@ const COMPONENTS = [ KbqPipeAdd, KbqPipeDirective, KbqPipeButton, - KbqPipeTitleDirective, KbqPipeState ]; diff --git a/packages/components/filter-bar/pipes/pipe-date.html b/packages/components/filter-bar/pipes/pipe-date.html index 9a271d35ab..dc1b41898a 100644 --- a/packages/components/filter-bar/pipes/pipe-date.html +++ b/packages/components/filter-bar/pipes/pipe-date.html @@ -4,7 +4,7 @@ kbqPopover [disabled]="data.disabled" [kbqPipeState]="data" - [kbqPipeTitle]="pipeTooltip" + [kbq-title]="pipeTooltip" [kbqPopoverArrow]="false" [kbqPopoverSize]="'custom'" [kbqPopoverClass]="'kbq-pipe-date__popover'" diff --git a/packages/components/filter-bar/pipes/pipe-date.ts b/packages/components/filter-bar/pipes/pipe-date.ts index c76f7cfc7b..db9d29acb3 100644 --- a/packages/components/filter-bar/pipes/pipe-date.ts +++ b/packages/components/filter-bar/pipes/pipe-date.ts @@ -23,7 +23,6 @@ import { KbqDateTimeValue } from '../filter-bar.types'; import { KbqBasePipe } from './base-pipe'; import { KbqPipeButton } from './pipe-button'; import { KbqPipeState } from './pipe-state'; -import { KbqPipeTitleDirective } from './pipe-title'; @Component({ selector: 'kbq-pipe-date', @@ -41,7 +40,6 @@ import { KbqPipeTitleDirective } from './pipe-title'; KbqFormattersModule, KbqPipeButton, KbqTitleModule, - KbqPipeTitleDirective, FormsModule ], templateUrl: 'pipe-date.html', diff --git a/packages/components/filter-bar/pipes/pipe-datetime.html b/packages/components/filter-bar/pipes/pipe-datetime.html index d4d70bb5ea..24facf0bc0 100644 --- a/packages/components/filter-bar/pipes/pipe-datetime.html +++ b/packages/components/filter-bar/pipes/pipe-datetime.html @@ -4,7 +4,7 @@ kbqPopover [disabled]="data.disabled" [kbqPipeState]="data" - [kbqPipeTitle]="pipeTooltip" + [kbq-title]="pipeTooltip" [kbqPopoverArrow]="false" [kbqPopoverSize]="'custom'" [kbqPopoverClass]="'kbq-pipe-date__popover'" diff --git a/packages/components/filter-bar/pipes/pipe-datetime.ts b/packages/components/filter-bar/pipes/pipe-datetime.ts index 6edee572c2..3d0bea8c3a 100644 --- a/packages/components/filter-bar/pipes/pipe-datetime.ts +++ b/packages/components/filter-bar/pipes/pipe-datetime.ts @@ -23,7 +23,6 @@ import { KbqDateTimeValue } from '../filter-bar.types'; import { KbqBasePipe } from './base-pipe'; import { KbqPipeButton } from './pipe-button'; import { KbqPipeState } from './pipe-state'; -import { KbqPipeTitleDirective } from './pipe-title'; @Component({ selector: 'kbq-pipe-datetime', @@ -41,7 +40,6 @@ import { KbqPipeTitleDirective } from './pipe-title'; KbqFormattersModule, KbqPipeButton, KbqTitleModule, - KbqPipeTitleDirective, FormsModule ], templateUrl: 'pipe-datetime.html', diff --git a/packages/components/filter-bar/pipes/pipe-multi-select.html b/packages/components/filter-bar/pipes/pipe-multi-select.html index 2dd70a9c58..ae21488acc 100644 --- a/packages/components/filter-bar/pipes/pipe-multi-select.html +++ b/packages/components/filter-bar/pipes/pipe-multi-select.html @@ -3,7 +3,7 @@ multiple [compareWith]="compareByValue" [disabled]="data.disabled" - [kbqPipeTitle]="pipeTooltip" + [kbq-title]="pipeTooltip" [class]="{ 'kbq-active': select.panelOpen }" [panelClass]="'kbq-pipe-multiselect__panel'" [selectAllHandler]="selectAllHandler" diff --git a/packages/components/filter-bar/pipes/pipe-multi-select.ts b/packages/components/filter-bar/pipes/pipe-multi-select.ts index 499ed5b504..ecf3458f30 100644 --- a/packages/components/filter-bar/pipes/pipe-multi-select.ts +++ b/packages/components/filter-bar/pipes/pipe-multi-select.ts @@ -24,7 +24,6 @@ import { KbqSelectValue } from '../filter-bar.types'; import { KbqBasePipe } from './base-pipe'; import { KbqPipeButton } from './pipe-button'; import { KbqPipeState } from './pipe-state'; -import { KbqPipeTitleDirective } from './pipe-title'; @Component({ selector: 'kbq-pipe-multi-select', @@ -37,7 +36,6 @@ import { KbqPipeTitleDirective } from './pipe-title'; KbqBadgeModule, KbqPipeButton, KbqTitleModule, - KbqPipeTitleDirective, NgTemplateOutlet, KbqIconModule, KbqInputModule, diff --git a/packages/components/filter-bar/pipes/pipe-multi-tree-select.html b/packages/components/filter-bar/pipes/pipe-multi-tree-select.html index 1d3b95dad4..0d0ad771bc 100644 --- a/packages/components/filter-bar/pipes/pipe-multi-tree-select.html +++ b/packages/components/filter-bar/pipes/pipe-multi-tree-select.html @@ -2,7 +2,7 @@ #select [autoSelect]="false" [disabled]="data.disabled" - [kbqPipeTitle]="pipeTooltip" + [kbq-title]="pipeTooltip" [multiple]="true" [class]="{ 'kbq-active': select.panelOpen }" [ngModel]="selected" diff --git a/packages/components/filter-bar/pipes/pipe-multi-tree-select.ts b/packages/components/filter-bar/pipes/pipe-multi-tree-select.ts index c6edfa8222..e60a2d93ce 100644 --- a/packages/components/filter-bar/pipes/pipe-multi-tree-select.ts +++ b/packages/components/filter-bar/pipes/pipe-multi-tree-select.ts @@ -23,7 +23,6 @@ import { KbqPipeTemplate, KbqSelectValue, KbqTreeSelectFlatNode, KbqTreeSelectNo import { getId, KbqBasePipe, KbqPipeMinWidth } from './base-pipe'; import { KbqPipeButton } from './pipe-button'; import { KbqPipeState } from './pipe-state'; -import { KbqPipeTitleDirective } from './pipe-title'; @Component({ selector: 'kbq-pipe-multi-tree-select', @@ -33,7 +32,6 @@ import { KbqPipeTitleDirective } from './pipe-title'; KbqPipeState, KbqPipeButton, KbqTitleModule, - KbqPipeTitleDirective, KbqPipeMinWidth, KbqIconModule, KbqInputModule, diff --git a/packages/components/filter-bar/pipes/pipe-readonly.ts b/packages/components/filter-bar/pipes/pipe-readonly.ts index ecb716db08..57fdfd87d2 100644 --- a/packages/components/filter-bar/pipes/pipe-readonly.ts +++ b/packages/components/filter-bar/pipes/pipe-readonly.ts @@ -4,7 +4,6 @@ import { KbqTitleModule } from '@koobiq/components/title'; import { KbqBasePipe, KbqPipeMinWidth } from './base-pipe'; import { KbqPipeButton } from './pipe-button'; import { KbqPipeState } from './pipe-state'; -import { KbqPipeTitleDirective } from './pipe-title'; @Component({ selector: 'kbq-pipe-readonly', @@ -12,12 +11,11 @@ import { KbqPipeTitleDirective } from './pipe-title'; KbqButtonModule, KbqTitleModule, KbqPipeState, - KbqPipeTitleDirective, KbqPipeMinWidth, KbqPipeButton ], template: ` - -
- -
`, styleUrls: ['./title-overview-example.css'], changeDetection: ChangeDetectionStrategy.OnPush diff --git a/packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.css b/packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.css new file mode 100644 index 0000000000..3aff5509ab --- /dev/null +++ b/packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.css @@ -0,0 +1,11 @@ +.example-title-vertical-overflow { + display: -webkit-box; + + max-width: 280px; + overflow: hidden; + overflow-wrap: break-word; + text-overflow: ellipsis; + + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} diff --git a/packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.ts b/packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.ts new file mode 100644 index 0000000000..b6050b4fb8 --- /dev/null +++ b/packages/docs-examples/components/title/title-vertical-overflow/title-vertical-overflow-example.ts @@ -0,0 +1,21 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { KbqTitleModule } from '@koobiq/components/title'; + +/** + * @title Multiline text + */ +@Component({ + selector: 'title-vertical-overflow-example', + imports: [KbqTitleModule], + template: ` +
+ {{ longValue }} +
+ `, + styleUrls: ['./title-vertical-overflow-example.css'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class TitleVerticalOverflowExample { + longValue = + 'A long multiline text clamped to two lines. When it does not fit into the allotted lines, the title directive shows a tooltip with the full content.'; +} diff --git a/packages/docs-examples/example-module.ts b/packages/docs-examples/example-module.ts index cf1a5f03b4..77308dd0e3 100644 --- a/packages/docs-examples/example-module.ts +++ b/packages/docs-examples/example-module.ts @@ -5341,6 +5341,45 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "timezone-trigger-overview-example.ts", "importPath": "components/timezone" }, + "title-custom-content": { + "packagePath": "components/title/title-custom-content", + "title": "Custom tooltip content", + "componentName": "TitleCustomContentExample", + "files": [ + "title-custom-content-example.ts", + "./title-custom-content-example.css" + ], + "selector": "title-custom-content-example", + "additionalComponents": [], + "primaryFile": "title-custom-content-example.ts", + "importPath": "components/title" + }, + "title-list-option": { + "packagePath": "components/title/title-list-option", + "title": "Two-line list options", + "componentName": "TitleListOptionExample", + "files": [ + "title-list-option-example.ts", + "./title-list-option-example.css" + ], + "selector": "title-list-option-example", + "additionalComponents": [], + "primaryFile": "title-list-option-example.ts", + "importPath": "components/title" + }, + "title-multiple-text": { + "packagePath": "components/title/title-multiple-text", + "title": "Multiple text elements", + "componentName": "TitleMultipleTextExample", + "files": [ + "title-multiple-text-example.ts", + "./title-multiple-text-example.css" + ], + "selector": "title-multiple-text-example", + "additionalComponents": [], + "primaryFile": "title-multiple-text-example.ts", + "importPath": "components/title" + }, "title-overview": { "packagePath": "components/title/title-overview", "title": "Title", @@ -5354,6 +5393,19 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = { "primaryFile": "title-overview-example.ts", "importPath": "components/title" }, + "title-vertical-overflow": { + "packagePath": "components/title/title-vertical-overflow", + "title": "Multiline text", + "componentName": "TitleVerticalOverflowExample", + "files": [ + "title-vertical-overflow-example.ts", + "./title-vertical-overflow-example.css" + ], + "selector": "title-vertical-overflow-example", + "additionalComponents": [], + "primaryFile": "title-vertical-overflow-example.ts", + "importPath": "components/title" + }, "toast-actions-overview": { "packagePath": "components/toast/toast-actions-overview", "title": "Toast actions", @@ -7215,7 +7267,15 @@ return import('@koobiq/docs-examples/components/timezone'); return import('@koobiq/docs-examples/components/timezone'); case 'timezone-trigger-overview': return import('@koobiq/docs-examples/components/timezone'); + case 'title-custom-content': +return import('@koobiq/docs-examples/components/title'); + case 'title-list-option': +return import('@koobiq/docs-examples/components/title'); + case 'title-multiple-text': +return import('@koobiq/docs-examples/components/title'); case 'title-overview': +return import('@koobiq/docs-examples/components/title'); + case 'title-vertical-overflow': return import('@koobiq/docs-examples/components/title'); case 'toast-actions-overview': return import('@koobiq/docs-examples/components/toast'); diff --git a/tools/cspell-locales/ru.json b/tools/cspell-locales/ru.json index 779785d485..b623bfa245 100644 --- a/tools/cspell-locales/ru.json +++ b/tools/cspell-locales/ru.json @@ -49,6 +49,8 @@ "дозаполнял", "дропдаун", "дропдауна", + "двустрочные", + "двустрочных", "двухаргументной", "задизейбленные", "задизейблены", diff --git a/tools/public_api_guard/components/core.api.md b/tools/public_api_guard/components/core.api.md index f23e9f7879..eabd042866 100644 --- a/tools/public_api_guard/components/core.api.md +++ b/tools/public_api_guard/components/core.api.md @@ -2969,141 +2969,80 @@ export type KbqPopUpPlacementValues = KbqEnumValues; // @public (undocumented) export type KbqPopUpSizeValues = KbqEnumValues; -// @public (undocumented) +// @public export abstract class KbqPopUpTrigger implements OnInit, OnDestroy { - // (undocumented) abstract arrow: boolean; - // (undocumented) protected readonly availablePositions: { [key: string]: ConnectionPositionPair; }; - // (undocumented) protected clearListeners(): void; - // (undocumented) abstract closingActions(): Observable; - // (undocumented) protected closingActionsSubscription: Subscription; - // (undocumented) container: HTMLElement; - // (undocumented) abstract content: string | TemplateRef; - // (undocumented) protected _content: string | TemplateRef; createOverlay(): OverlayRef; - // (undocumented) abstract customClass: string; - // (undocumented) protected _customClass: string; - // (undocumented) protected readonly destroyRef: DestroyRef; - // (undocumented) detach: () => void; - // (undocumented) protected readonly direction: Directionality | null; - // (undocumented) abstract disabled: boolean; - // (undocumented) protected _disabled: boolean; - // (undocumented) protected readonly elementRef: ElementRef; - // (undocumented) enterDelay: number; protected externalNativeElement: HTMLElement; - // (undocumented) focus(): void; protected getAdjustedPositions(): ConnectionPositionPair[]; - // (undocumented) getMouseLeaveListener(): (delay?: number) => void; getNativeElement(): HTMLElement; - // (undocumented) abstract getOverlayHandleComponentType(): Type; - // (undocumented) protected getPrioritizedPositions(): ConnectionPositionPair[]; - // (undocumented) protected getPriorityPlacementStrategy(value: string | string[]): ConnectionPositionPair[]; - // (undocumented) hide(delay?: number): void; protected hideWithTimeout: boolean; - // (undocumented) protected readonly hostView: ViewContainerRef; readonly hovered: BehaviorSubject; - // (undocumented) initListeners(): void; - // (undocumented) protected instance: any | null; - // (undocumented) get isOpen(): boolean; set isOpen(value: boolean); - // (undocumented) keydownHandler(event: KeyboardEvent): void; - // (undocumented) leaveDelay: number; - // (undocumented) protected listeners: Map; - // (undocumented) protected mouseEvent?: MouseEvent; - // (undocumented) ngOnDestroy(): void; - // (undocumented) ngOnInit(): void; - // (undocumented) protected readonly ngZone: NgZone; - // (undocumented) onPositionChange: ($event: ConnectedOverlayPositionChange) => void; - // (undocumented) protected abstract originSelector: string; - // (undocumented) protected readonly overlay: Overlay; - // (undocumented) protected abstract overlayConfig: OverlayConfig; - // (undocumented) overlayRef: OverlayRef | null; - // (undocumented) protected placement: KbqPopUpPlacementValues; - // (undocumented) abstract placementChange: EventEmitter; - // (undocumented) protected placementPriority: string | string[] | null; - // (undocumented) protected portal: ComponentPortal; protected preventClose: boolean; - // (undocumented) resetOrigin(): void; - // (undocumented) protected readonly scheduler: AsyncScheduler | undefined; - // (undocumented) protected readonly scrollable: CdkScrollable | null; - // (undocumented) protected readonly scrollDispatcher: ScrollDispatcher; - // (undocumented) protected abstract scrollStrategy: () => ScrollStrategy; setExternalNativeElement(value: HTMLElement): void; - // (undocumented) show(delay?: number): void; - // (undocumented) stickToWindow: KbqStickToWindowPlacementValues; - // (undocumented) protected strategy: FlexibleConnectedPositionStrategy; - // (undocumented) touchendHandler(): void; - // (undocumented) abstract trigger: string; - // (undocumented) triggerName: string; - // (undocumented) abstract updateClassMap(newPlacement?: string): void; - // (undocumented) abstract updateData(): void; - // (undocumented) updatePlacement(value: KbqPopUpPlacementValues): void; - // (undocumented) updatePlacementPriority(value: any): void; updatePosition(reapplyPosition?: boolean): void; - // (undocumented) updateVisible(externalValue: boolean): void; - // (undocumented) protected visible: boolean; - // (undocumented) abstract visibleChange: EventEmitter; // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration, never, never, {}, {}, never, never, true, never>; diff --git a/tools/public_api_guard/components/filter-bar.api.md b/tools/public_api_guard/components/filter-bar.api.md index 15ce6be15f..e504d8129c 100644 --- a/tools/public_api_guard/components/filter-bar.api.md +++ b/tools/public_api_guard/components/filter-bar.api.md @@ -27,14 +27,12 @@ import { KbqOption } from '@koobiq/components/core'; import { KbqPopoverTrigger } from '@koobiq/components/popover'; import { KbqPseudoCheckboxState } from '@koobiq/components/core'; import { KbqSelect } from '@koobiq/components/select'; -import { KbqTooltipTrigger } from '@koobiq/components/tooltip'; import { KbqTreeFlatDataSource } from '@koobiq/components/tree'; import { KbqTreeFlattener } from '@koobiq/components/tree'; import { KbqTreeOption } from '@koobiq/components/tree'; import { KbqTreeSelect } from '@koobiq/components/tree-select'; import * as _koobiq_components_core from '@koobiq/components/core'; import { Observable } from 'rxjs'; -import { OnDestroy } from '@angular/core'; import { OnInit } from '@angular/core'; import { PopUpPlacements } from '@koobiq/components/core'; import { PopUpSizes } from '@koobiq/components/core'; @@ -219,7 +217,7 @@ export class KbqFilterBarModule { // (undocumented) static ɵinj: i0.ɵɵInjectorDeclaration; // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public @@ -614,33 +612,6 @@ export class KbqPipeTextComponent extends KbqBasePipe implements static ɵfac: i0.ɵɵFactoryDeclaration; } -// @public (undocumented) -export class KbqPipeTitleDirective extends KbqTooltipTrigger implements AfterViewInit, OnDestroy { - constructor(); - // (undocumented) - get child(): HTMLElement; - // (undocumented) - handleElementEnter(): void; - // (undocumented) - hideTooltip(): void; - // (undocumented) - get isOverflown(): boolean; - // (undocumented) - ngAfterViewInit(): void; - // (undocumented) - ngOnDestroy(): void; - // (undocumented) - get parent(): HTMLElement; - // (undocumented) - readonly resizeStream: Subject; - // (undocumented) - readonly viewValue: i0.InputSignal>; - // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; - // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration; -} - // @public (undocumented) export class KbqPipeTreeSelectComponent extends KbqBasePipe implements OnInit, AfterViewInit { constructor(); diff --git a/tools/public_api_guard/components/title.api.md b/tools/public_api_guard/components/title.api.md index f6a126fdb4..1c3b26707c 100644 --- a/tools/public_api_guard/components/title.api.md +++ b/tools/public_api_guard/components/title.api.md @@ -10,38 +10,27 @@ import * as i1 from '@koobiq/components/tooltip'; import { KbqTooltipTrigger } from '@koobiq/components/tooltip'; import { OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; +import { TemplateRef } from '@angular/core'; -// @public (undocumented) +// @public export class KbqTitleDirective extends KbqTooltipTrigger implements AfterViewInit, OnDestroy { - // (undocumented) get child(): HTMLElement; - // (undocumented) handleElementEnter(): void; - // (undocumented) get hasOnlyText(): boolean; - // (undocumented) hideTooltip(): void; - // (undocumented) get isHorizontalOverflown(): boolean; - // (undocumented) get isOverflown(): boolean; - // (undocumented) get isVerticalOverflown(): boolean; - // (undocumented) ngAfterViewInit(): void; - // (undocumented) ngOnDestroy(): void; - // (undocumented) get parent(): HTMLElement; - // (undocumented) readonly resizeStream: Subject; + readonly titleContent: i0.InputSignal>; set trigger(value: string); - // (undocumented) get trigger(): string; - // (undocumented) get viewValue(): string; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } diff --git a/tools/public_api_guard/components/tooltip.api.md b/tools/public_api_guard/components/tooltip.api.md index 63192beec9..6acb089d97 100644 --- a/tools/public_api_guard/components/tooltip.api.md +++ b/tools/public_api_guard/components/tooltip.api.md @@ -83,41 +83,29 @@ export class KbqToolTipModule { // @public export function kbqTooltipScrollStrategyFactory(overlay: Overlay): () => ScrollStrategy; -// @public (undocumented) +// @public export class KbqTooltipTrigger extends KbqPopUpTrigger implements AfterViewInit, OnChanges, OnDestroy { constructor(); - // (undocumented) protected applyRelativeToPointer(): void; - // (undocumented) arrow: boolean; - // (undocumented) closingActions(): rxjs.Observable; - // (undocumented) get color(): string; set color(value: KbqComponentColors | string); - // (undocumented) get content(): string | TemplateRef; set content(content: string | TemplateRef); - // (undocumented) get context(): any; set context(ctx: any); - // (undocumented) get customClass(): string; set customClass(value: string); - // (undocumented) get disabled(): boolean; set disabled(value: boolean); - // (undocumented) enterDelay: number; - // (undocumented) protected focusMonitor: FocusMonitor; readonly forDisabledComponent: i0.InputSignal> | undefined>; - // (undocumented) getOverlayHandleComponentType(): Type; header: string | TemplateRef; hideWithTimeout: boolean; readonly ignoreTooltipPointerEvents: i0.InputSignal; - // (undocumented) leaveDelay: number; modifier: TooltipModifier | `${TooltipModifier}`; // (undocumented) @@ -128,48 +116,31 @@ export class KbqTooltipTrigger extends KbqPopUpTrigger impl static ngAcceptInputType_offset: unknown; // (undocumented) static ngAcceptInputType_relativeToPointer: unknown; - // (undocumented) ngAfterViewInit(): void; ngOnChanges(changes: SimpleChanges): void; - // (undocumented) ngOnDestroy(): void; - // (undocumented) offset: number | null; - // (undocumented) protected originSelector: string; - // (undocumented) protected overlayConfig: OverlayConfig; - // (undocumented) protected parentPopup: KbqParentPopup | null; - // (undocumented) readonly placementChange: EventEmitter; relativeToPointer: boolean; protected renderer: Renderer2; - // (undocumented) protected scrollStrategy: () => ScrollStrategy; - // (undocumented) show(delay?: number): void; showForElement(element: HTMLElement): void; showForMouseEvent(event: MouseEvent): void; - // (undocumented) get tooltipPlacement(): KbqPopUpPlacementValues; set tooltipPlacement(value: KbqPopUpPlacementValues); - // (undocumented) get tooltipPlacementPriority(): string | string[] | null; set tooltipPlacementPriority(value: string | string[] | null); - // (undocumented) get tooltipVisible(): boolean; set tooltipVisible(value: boolean); - // (undocumented) get trigger(): string; set trigger(value: string); - // (undocumented) protected _trigger: string; - // (undocumented) updateClassMap(newPlacement?: string): void; - // (undocumented) updateData(): void; - // (undocumented) readonly visibleChange: EventEmitter; // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration;