Browse Source

Bugfix/fix carousel component (#3709)

* Fix carousel component

* Update changelog
pull/3731/head
Daniel Idem 5 months ago
committed by GitHub
parent
commit
8018236942
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 2
      apps/client/src/app/pages/landing/landing-page.html
  3. 12
      libs/ui/src/lib/carousel/carousel-item.directive.ts
  4. 7
      libs/ui/src/lib/carousel/carousel.component.html
  5. 64
      libs/ui/src/lib/carousel/carousel.component.ts

1
CHANGELOG.md

@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental) - Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental)
- Fixed an issue on the portfolio activities page by loading the data only once - Fixed an issue on the portfolio activities page by loading the data only once
- Fixed an issue in the carousel component for the testimonial section on the landing page
## 2.105.0 - 2024-08-21 ## 2.105.0 - 2024-08-21

2
apps/client/src/app/pages/landing/landing-page.html

@ -331,7 +331,7 @@
<div class="col-md-8 offset-md-2"> <div class="col-md-8 offset-md-2">
<gf-carousel [aria-label]="'Testimonials'"> <gf-carousel [aria-label]="'Testimonials'">
@for (testimonial of testimonials; track testimonial) { @for (testimonial of testimonials; track testimonial) {
<div gf-carousel-item> <div #carouselItem gf-carousel-item>
<div class="d-flex px-4"> <div class="d-flex px-4">
<gf-logo <gf-logo
class="mr-3 mt-2 pt-1" class="mr-3 mt-2 pt-1"

12
libs/ui/src/lib/carousel/carousel-item.directive.ts

@ -1,16 +1,8 @@
import { FocusableOption } from '@angular/cdk/a11y'; import { Directive, ElementRef } from '@angular/core';
import { Directive, ElementRef, HostBinding } from '@angular/core';
@Directive({ @Directive({
selector: '[gf-carousel-item]' selector: '[gf-carousel-item]'
}) })
export class CarouselItem implements FocusableOption { export class CarouselItem {
@HostBinding('attr.role') readonly role = 'listitem';
@HostBinding('tabindex') tabindex = '-1';
public constructor(readonly element: ElementRef<HTMLElement>) {} public constructor(readonly element: ElementRef<HTMLElement>) {}
public focus() {
this.element.nativeElement.focus({ preventScroll: true });
}
} }

7
libs/ui/src/lib/carousel/carousel.component.html

@ -11,12 +11,7 @@
</button> </button>
} }
<div <div #contentWrapper class="overflow-hidden" role="region">
#contentWrapper
class="overflow-hidden"
role="region"
(keyup)="onKeydown($event)"
>
<div #list class="d-flex carousel-content" role="list" tabindex="0"> <div #list class="d-flex carousel-content" role="list" tabindex="0">
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>

64
libs/ui/src/lib/carousel/carousel.component.ts

@ -1,24 +1,18 @@
import { FocusKeyManager } from '@angular/cdk/a11y';
import { LEFT_ARROW, RIGHT_ARROW, TAB } from '@angular/cdk/keycodes';
import { import {
AfterContentInit,
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ContentChildren, contentChildren,
ElementRef, ElementRef,
HostBinding, HostBinding,
Inject, Inject,
Input, Input,
Optional, Optional,
QueryList,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations'; import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
import { CarouselItem } from './carousel-item.directive';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [MatButtonModule], imports: [MatButtonModule],
@ -28,9 +22,7 @@ import { CarouselItem } from './carousel-item.directive';
styleUrls: ['./carousel.component.scss'], styleUrls: ['./carousel.component.scss'],
templateUrl: './carousel.component.html' templateUrl: './carousel.component.html'
}) })
export class GfCarouselComponent implements AfterContentInit { export class GfCarouselComponent {
@ContentChildren(CarouselItem) public items!: QueryList<CarouselItem>;
@HostBinding('class.animations-disabled') @HostBinding('class.animations-disabled')
public readonly animationsDisabled: boolean; public readonly animationsDisabled: boolean;
@ -38,11 +30,11 @@ export class GfCarouselComponent implements AfterContentInit {
@ViewChild('list') public list!: ElementRef<HTMLElement>; @ViewChild('list') public list!: ElementRef<HTMLElement>;
public items = contentChildren('carouselItem', { read: ElementRef });
public showPrevArrow = false; public showPrevArrow = false;
public showNextArrow = true; public showNextArrow = true;
private index = 0; private index = 0;
private keyManager!: FocusKeyManager<CarouselItem>;
private position = 0; private position = 0;
public constructor( public constructor(
@ -51,12 +43,8 @@ export class GfCarouselComponent implements AfterContentInit {
this.animationsDisabled = animationsModule === 'NoopAnimations'; this.animationsDisabled = animationsModule === 'NoopAnimations';
} }
public ngAfterContentInit() {
this.keyManager = new FocusKeyManager<CarouselItem>(this.items);
}
public next() { public next() {
for (let i = this.index; i < this.items.length; i++) { for (let i = this.index; i < this.items().length; i++) {
if (this.isOutOfView(i)) { if (this.isOutOfView(i)) {
this.index = i; this.index = i;
this.scrollToActiveItem(); this.scrollToActiveItem();
@ -65,31 +53,6 @@ export class GfCarouselComponent implements AfterContentInit {
} }
} }
public onKeydown({ keyCode }: KeyboardEvent) {
const manager = this.keyManager;
const previousActiveIndex = manager.activeItemIndex;
if (keyCode === LEFT_ARROW) {
manager.setPreviousItemActive();
} else if (keyCode === RIGHT_ARROW) {
manager.setNextItemActive();
} else if (keyCode === TAB && !manager.activeItem) {
manager.setFirstItemActive();
}
if (
manager.activeItemIndex != null &&
manager.activeItemIndex !== previousActiveIndex
) {
this.index = manager.activeItemIndex;
this.updateItemTabIndices();
if (this.isOutOfView(this.index)) {
this.scrollToActiveItem();
}
}
}
public previous() { public previous() {
for (let i = this.index; i > -1; i--) { for (let i = this.index; i > -1; i--) {
if (this.isOutOfView(i)) { if (this.isOutOfView(i)) {
@ -101,8 +64,7 @@ export class GfCarouselComponent implements AfterContentInit {
} }
private isOutOfView(index: number, side?: 'start' | 'end') { private isOutOfView(index: number, side?: 'start' | 'end') {
const { offsetWidth, offsetLeft } = const { offsetWidth, offsetLeft } = this.items()[index].nativeElement;
this.items.toArray()[index].element.nativeElement;
if ((!side || side === 'start') && offsetLeft - this.position < 0) { if ((!side || side === 'start') && offsetLeft - this.position < 0) {
return true; return true;
@ -120,33 +82,23 @@ export class GfCarouselComponent implements AfterContentInit {
return; return;
} }
const itemsArray = this.items.toArray();
let targetItemIndex = this.index; let targetItemIndex = this.index;
if (this.index > 0 && !this.isOutOfView(this.index - 1)) { if (this.index > 0 && !this.isOutOfView(this.index - 1)) {
targetItemIndex = targetItemIndex =
itemsArray.findIndex((_, i) => !this.isOutOfView(i)) + 1; this.items().findIndex((_, i) => !this.isOutOfView(i)) + 1;
} }
this.position = this.position = this.items()[targetItemIndex].nativeElement.offsetLeft;
itemsArray[targetItemIndex].element.nativeElement.offsetLeft;
this.list.nativeElement.style.transform = `translateX(-${this.position}px)`; this.list.nativeElement.style.transform = `translateX(-${this.position}px)`;
this.showPrevArrow = this.index > 0; this.showPrevArrow = this.index > 0;
this.showNextArrow = false; this.showNextArrow = false;
for (let i = itemsArray.length - 1; i > -1; i--) { for (let i = this.items().length - 1; i > -1; i--) {
if (this.isOutOfView(i, 'end')) { if (this.isOutOfView(i, 'end')) {
this.showNextArrow = true; this.showNextArrow = true;
break; break;
} }
} }
} }
private updateItemTabIndices() {
this.items.forEach((item: CarouselItem) => {
if (this.keyManager != null) {
item.tabindex = item === this.keyManager.activeItem ? '0' : '-1';
}
});
}
} }

Loading…
Cancel
Save