From e03c688c807f314814041649013e110eb49a298a Mon Sep 17 00:00:00 2001 From: Svyatoslav Zaytsev Date: Thu, 9 Nov 2023 12:23:12 +0600 Subject: [PATCH] feat(kit): fix intersecting ranges --- .../directives/highlight/examples/4/index.ts | 2 +- .../highlight/highlight.directive.ts | 95 +++++++++++++++---- 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/projects/demo/src/modules/directives/highlight/examples/4/index.ts b/projects/demo/src/modules/directives/highlight/examples/4/index.ts index 190ec527aef63..d4490aee3b436 100644 --- a/projects/demo/src/modules/directives/highlight/examples/4/index.ts +++ b/projects/demo/src/modules/directives/highlight/examples/4/index.ts @@ -6,8 +6,8 @@ import {encapsulation} from '@demo/emulate/encapsulation'; selector: 'tui-highlight-example-4', templateUrl: './index.html', styleUrls: ['./index.less'], - changeDetection, encapsulation, + changeDetection, }) export class TuiHighlightExample4 { readonly rows = [ diff --git a/projects/kit/directives/highlight/highlight.directive.ts b/projects/kit/directives/highlight/highlight.directive.ts index bde107ddc5447..76932096bb73b 100644 --- a/projects/kit/directives/highlight/highlight.directive.ts +++ b/projects/kit/directives/highlight/highlight.directive.ts @@ -20,8 +20,8 @@ import { tuiToArray, } from '@taiga-ui/cdk'; import {TuiToRegexpPipe} from '@taiga-ui/kit/pipes'; -import {Observable, Subject} from 'rxjs'; -import {mergeAll, switchMap, takeUntil} from 'rxjs/operators'; +import {merge, Observable, Subject} from 'rxjs'; +import {switchMap, takeUntil} from 'rxjs/operators'; import {TuiHighlightComponent} from './highlight.component'; @@ -45,9 +45,7 @@ export class TuiHighlightDirective implements OnChanges { svgNodeFilter, ); - private readonly clearHighlights$ = new Subject(); - - private readonly addHighlight$ = new Subject>(); + private readonly highlight$ = new Subject>>(); private readonly cf: ComponentFactory; @@ -76,9 +74,9 @@ export class TuiHighlightDirective implements OnChanges { this.updateHighlights(); }); - this.clearHighlights$ + this.highlight$ .pipe( - switchMap(() => this.addHighlight$.pipe(mergeAll())), + switchMap(highlights => merge(...highlights)), takeUntil(destroy$), ) .subscribe(); @@ -117,33 +115,90 @@ export class TuiHighlightDirective implements OnChanges { } private updateHighlights(): void { - this.clearHighlights$.next(); - if (!this.match) { return; } + this.highlight$.next(this.createHighlights()); + } + + private createHighlights(): ReadonlyArray> { const hostRect = this.el.nativeElement.getBoundingClientRect(); + const highlights: Array> = []; + for (const node of this.getNodes()) { + const occurrences: TuiHighlightOccurrence[] = []; + for (const occurrence of this.getOccurrences(node.nodeValue)) { - const range = this.createRange(node, occurrence); + if (!this.tuiHighlightMultiOccurrences) { + return [ + this.createHighlight( + hostRect, + this.createRange(node, occurrence), + ), + ]; + } - this.addHighlight$.next( - this.createHighlight(hostRect, range.getBoundingClientRect()), - ); + occurrences[occurrence.index] = occurrence; + } - if (!this.tuiHighlightMultiOccurrences) { - return; + if (!occurrences.length) { + continue; + } + + const mergedOccurrences: TuiHighlightOccurrence[] = []; + + for (const occurrence of occurrences) { + if (!occurrence) { + continue; + } + + const lastMergedOccurrence = mergedOccurrences.at(-1); + + if ( + lastMergedOccurrence && + occurrence.index <= + lastMergedOccurrence.index + lastMergedOccurrence.length + ) { + const end = Math.max( + lastMergedOccurrence.index + lastMergedOccurrence.length, + occurrence.index + occurrence.length, + ); + + lastMergedOccurrence.length = end - lastMergedOccurrence.index; + } else { + mergedOccurrences.push(occurrence); + + if (lastMergedOccurrence) { + highlights.push( + this.createHighlight( + hostRect, + this.createRange(node, lastMergedOccurrence), + ), + ); + } } } + + const lastMergedOccurrence = mergedOccurrences.at(-1); + + if (lastMergedOccurrence) { + highlights.push( + this.createHighlight( + hostRect, + this.createRange(node, lastMergedOccurrence), + ), + ); + } } + + return highlights; } - private createHighlight( - hostRect: DOMRect, - {left, top, width, height}: DOMRect, - ): Observable { + private createHighlight(hostRect: DOMRect, range: Range): Observable { + const {left, top, width, height} = range.getBoundingClientRect(); + return new Observable(() => { const ref = this.vcr.createComponent(this.cf); const {instance} = ref; @@ -177,7 +232,7 @@ export class TuiHighlightDirective implements OnChanges { for (const item of this.patterns) { for (const match of source.matchAll(item)) { - if (tuiIsNumber(match.index) && match.length) { + if (tuiIsNumber(match.index) && match[0].length) { yield { index: match.index, length: match[0].length,