Skip to content

Commit df3504e

Browse files
hf-krechanMaxim Uhlemann
andauthored
fix(scroll behaviour): Fix Search Result Scrolling Behaviour (#538)
* fix scrolling behaviour * improve search experience * Use real header height for scroll offset of ah-table * Fix header height * Improve timeout --------- Co-authored-by: Maxim Uhlemann <[email protected]>
1 parent 0247481 commit df3504e

File tree

6 files changed

+69
-57
lines changed

6 files changed

+69
-57
lines changed

src/app/features/ahbs/components/ahb-table/ahb-table.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="relative bg-hf-pastell-rose">
22
<table class="table-auto w-full text-sm text-gray-500">
3-
<thead>
3+
<thead #header>
44
<tr>
55
<th
66
scope="col"

src/app/features/ahbs/components/ahb-table/ahb-table.component.ts

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class AhbTableComponent {
3232
formatVersion = input.required<string>();
3333
pruefi = input.required<string>();
3434

35-
selectElement = output<{ element: HTMLElement; offsetY: number }>();
35+
scrollToElement = output<{ element: HTMLElement; offsetY: number }>();
3636

3737
private highlightSignal = computed(() => this.highlight());
3838
markIndex = signal(0);
@@ -50,33 +50,20 @@ export class AhbTableComponent {
5050
return Array.from(nativeElement.querySelectorAll('mark'));
5151
});
5252

53-
selectedMarkElement = computed(() => {
54-
const markIndex = this.markIndex();
55-
const markElements = this.markElements();
56-
if (markElements.length === 0 || markElements.length < markIndex + 1) {
57-
return null;
58-
}
59-
return markElements[this.markIndex()];
60-
});
61-
6253
constructor(private readonly elementRef: ElementRef) {
54+
// Watch for highlight changes to reset the index
6355
effect(() => {
64-
const selectedMarkElement = this.selectedMarkElement();
65-
if (selectedMarkElement === null) {
66-
return;
56+
const highlight = this.highlight();
57+
if (highlight) {
58+
this.markIndex.set(0);
59+
setTimeout(() => this.applyCurrentMark());
6760
}
68-
// make selected element orange
69-
const markElements = this.markElements();
70-
markElements.forEach(el => el.classList.remove('bg-orange-500'));
71-
selectedMarkElement.classList.add('bg-orange-500');
72-
// notify outer scroll container
73-
this.scrollToHighlightedElement();
7461
});
7562
}
7663

7764
resetMarkIndex() {
7865
this.markIndex.set(0);
79-
this.scrollToHighlightedElement();
66+
this.applyCurrentMark();
8067
}
8168

8269
getHighlight(): string | undefined {
@@ -85,30 +72,43 @@ export class AhbTableComponent {
8572

8673
nextResult() {
8774
const markElements = this.markElements();
88-
if (markElements.length > 0) {
89-
const nextIndex = (this.markIndex() + 1) % markElements.length;
90-
this.markIndex.set(nextIndex);
91-
this.scrollToHighlightedElement();
92-
}
75+
if (markElements.length === 0) return;
76+
77+
const nextIndex = (this.markIndex() + 1) % markElements.length;
78+
this.markIndex.set(nextIndex);
79+
this.applyCurrentMark();
9380
}
9481

9582
previousResult() {
9683
const markElements = this.markElements();
97-
if (markElements.length > 0) {
98-
const previousIndex = (this.markIndex() - 1 + markElements.length) % markElements.length;
99-
this.markIndex.set(previousIndex);
100-
this.scrollToHighlightedElement();
101-
}
84+
if (markElements.length === 0) return;
85+
86+
const previousIndex = (this.markIndex() - 1 + markElements.length) % markElements.length;
87+
this.markIndex.set(previousIndex);
88+
this.applyCurrentMark();
10289
}
10390

104-
scrollToHighlightedElement() {
105-
const selectedElement = this.selectedMarkElement();
106-
const header = this.header();
107-
const headerHeight = header?.nativeElement.getBoundingClientRect().height ?? 0;
108-
this.selectElement.emit({
109-
element: selectedElement || this.elementRef.nativeElement,
110-
offsetY: headerHeight,
111-
});
91+
private applyCurrentMark() {
92+
const markElements = this.markElements();
93+
const currentIndex = this.markIndex();
94+
95+
if (markElements.length === 0 || currentIndex >= markElements.length) {
96+
return;
97+
}
98+
99+
// Update highlighting
100+
markElements.forEach(el => el.classList.remove('bg-orange-500'));
101+
const currentElement = markElements[currentIndex];
102+
currentElement.classList.add('bg-orange-500');
103+
104+
// Notify parent component to scroll to this element
105+
if (currentElement) {
106+
const headerHeight = this.header()?.nativeElement.getBoundingClientRect().height ?? 0;
107+
this.scrollToElement.emit({
108+
element: currentElement,
109+
offsetY: headerHeight + 20,
110+
});
111+
}
112112
}
113113

114114
// determines each time the section_name changes to add a bold rule in ahb-table.component.html

src/app/features/ahbs/views/ahb-page/ahb-page.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
</app-header>
2020

2121
<!-- Scrollable Content -->
22-
<div class="flex-1 overflow-auto relative bg-hf-pastell-rot" *ngIf="!errorOccurred">
22+
<div #scroll class="flex-1 overflow-auto relative bg-hf-pastell-rot" *ngIf="!errorOccurred">
2323
<!-- Metadata Section -->
2424
@if (ahb$ | async; as ahb) {
2525
<div class="flex flex-col md:flex-row mb-10">
@@ -58,7 +58,7 @@ <h3 class="font-bold text-[20px]">
5858
[highlight]="searchQuery()"
5959
[formatVersion]="formatVersion()"
6060
[pruefi]="pruefi()"
61-
(selectElement)="scrollToElement($event.element, $event.offsetY)" />
61+
(scrollToElement)="scrollToElement($event.element, $event.offsetY)" />
6262
} @else {
6363
<div class="h-full flex items-center justify-center">
6464
<p class="text-xl">AHB-Tabelle wird geladen ...</p>

src/app/features/ahbs/views/ahb-page/ahb-page.component.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,33 +127,29 @@ export class AhbPageComponent implements OnInit, OnDestroy {
127127

128128
triggerSearch(query: string | undefined) {
129129
if (!query) return;
130-
131-
const tableComponent = this.table();
132-
if (tableComponent) {
133-
setTimeout(() => {
134-
tableComponent.resetMarkIndex();
135-
}, 0);
136-
}
137130
}
138131

139132
scrollToElement(element: HTMLElement, offsetY: number): void {
140133
const scrollContainer = this.scroll();
141-
if (!scrollContainer?.nativeElement) {
142-
return;
143-
}
144-
const containerRect = scrollContainer.nativeElement.getBoundingClientRect();
134+
if (!scrollContainer?.nativeElement) return;
135+
136+
// Get the scroll container
137+
const container = scrollContainer.nativeElement;
138+
139+
// Calculate position accounting for header offset
145140
const elementRect = element.getBoundingClientRect();
146-
const scrollTop =
147-
elementRect.top - containerRect.top + scrollContainer.nativeElement.scrollTop - offsetY;
148-
scrollContainer.nativeElement.scrollTo({
141+
const containerRect = container.getBoundingClientRect();
142+
const scrollTop = elementRect.top - containerRect.top + container.scrollTop - offsetY;
143+
144+
// Scroll smoothly to the element
145+
container.scrollTo({
149146
top: scrollTop,
150147
behavior: 'smooth',
151148
});
152149
}
153150

154151
onSearchQueryChange(query: string | undefined) {
155152
this.searchQuery.set(query);
156-
this.triggerSearch(query);
157153
}
158154

159155
onNextClick() {

src/app/shared/components/input-search-enhanced/input-search-enhanced.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
type="text"
1010
[(ngModel)]="searchQuery"
1111
placeholder="Suchbegriff eingeben ..."
12-
(keyup.enter)="keyupEnter.emit()" />
12+
(keydown)="onKeyDown($event)" />
1313
@if (totalResults()) {
1414
<div class="align-middle pe-5 text-gray-500">{{ selectedPosition() }}/{{ totalResults() }}</div>
1515
}

src/app/shared/components/input-search-enhanced/input-search-enhanced.component.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ export class InputSearchEnhancedComponent {
4646
this.updateURL(currentQuery);
4747
});
4848
}
49+
50+
// Handle Enter and Shift+Enter key press to navigate
51+
onKeyDown(event: KeyboardEvent) {
52+
if (event.key === 'Enter') {
53+
if (event.shiftKey) {
54+
// Shift+Enter goes to previous result
55+
event.preventDefault();
56+
this.previousClick.emit();
57+
} else {
58+
// Enter goes to next result
59+
event.preventDefault();
60+
this.nextClick.emit();
61+
}
62+
}
63+
}
64+
4965
updateURL(query: string | undefined): void {
5066
this.router.navigate([], {
5167
relativeTo: this.route,

0 commit comments

Comments
 (0)