diff --git a/apps/datahub/src/app/organization/organization-details/organization-details.component.ts b/apps/datahub/src/app/organization/organization-details/organization-details.component.ts index a089aca95..c4877241b 100644 --- a/apps/datahub/src/app/organization/organization-details/organization-details.component.ts +++ b/apps/datahub/src/app/organization/organization-details/organization-details.component.ts @@ -11,21 +11,16 @@ import { CatalogRecord, Organization, } from '@geonetwork-ui/common/domain/model/record' -import { - ButtonComponent, - PreviousNextButtonsComponent, -} from '@geonetwork-ui/ui/inputs' import { TranslateModule } from '@ngx-translate/core' import { BlockListComponent, - CarouselComponent, MaxLinesComponent, + PreviousNextButtonsComponent, } from '@geonetwork-ui/ui/layout' import { LetDirective } from '@ngrx/component' import { ErrorComponent, ErrorType, - LinkCardComponent, RelatedRecordCardComponent, UiElementsModule, } from '@geonetwork-ui/ui/elements' @@ -57,12 +52,8 @@ import { startWith } from 'rxjs/operators' standalone: true, imports: [ CommonModule, - ButtonComponent, TranslateModule, - CarouselComponent, - BlockListComponent, LetDirective, - LinkCardComponent, PreviousNextButtonsComponent, UiElementsModule, UiSearchModule, diff --git a/apps/datahub/src/app/record/record-apis/record-apis.component.html b/apps/datahub/src/app/record/record-apis/record-apis.component.html index dd22ded98..3ad41c9fb 100644 --- a/apps/datahub/src/app/record/record-apis/record-apis.component.html +++ b/apps/datahub/src/app/record/record-apis/record-apis.component.html @@ -6,13 +6,15 @@ record.metadata.api

- + 1 - } - updateView() { this.changeDetector.detectChanges() } - get isFirstStep() { - return this.carousel?.isFirstStep - } - - get isLastStep() { - return this.carousel?.isLastStep - } - openRecordApiForm(link: DatasetServiceDistribution) { this.selectedApiLink = link this.setStyle(link) @@ -88,12 +78,4 @@ export class RecordApisComponent implements OnInit { this.maxHeight = link === undefined ? '0px' : '500px' this.opacity = link === undefined ? 0 : 1 } - - changeStepOrPage(direction: string) { - if (direction === 'next') { - this.carousel?.slideToNext() - } else { - this.carousel?.slideToPrevious() - } - } } diff --git a/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.html b/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.html index b97bc8a20..8d7acc549 100644 --- a/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.html +++ b/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.html @@ -6,10 +6,8 @@ record.metadata.links

diff --git a/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.ts b/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.ts index cef2c555c..799d6dea6 100644 --- a/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.ts +++ b/apps/datahub/src/app/record/record-otherlinks/record-otherlinks.component.ts @@ -6,8 +6,12 @@ import { ViewChild, } from '@angular/core' import { MdViewFacade } from '@geonetwork-ui/feature/record' -import { BlockListComponent, CarouselComponent } from '@geonetwork-ui/ui/layout' -import { PreviousNextButtonsComponent } from '@geonetwork-ui/ui/inputs' +import { + BlockListComponent, + CarouselComponent, + Paginable, + PreviousNextButtonsComponent, +} from '@geonetwork-ui/ui/layout' import { CommonModule } from '@angular/common' import { LinkCardComponent } from '@geonetwork-ui/ui/elements' import { LetDirective } from '@ngrx/component' @@ -34,34 +38,15 @@ export class RecordOtherlinksComponent implements AfterViewInit { @ViewChild(CarouselComponent) carousel: CarouselComponent @ViewChild(BlockListComponent) list: BlockListComponent + get paginableElement(): Paginable { + return this.carousel || this.list + } constructor( public facade: MdViewFacade, private changeDetector: ChangeDetectorRef ) {} - get isFirstStepOrPage() { - return this.carousel?.isFirstStep ?? this.list?.isFirstPage ?? true - } - - get isLastStepOrPage() { - return this.carousel?.isLastStep ?? this.list?.isLastPage ?? false - } - - get hasPagination() { - return (this.carousel?.stepsCount || this.list?.pagesCount) > 1 - } - - changeStepOrPage(direction: string) { - if (direction === 'next') { - this.list?.nextPage() - this.carousel?.slideToNext() - } else { - this.carousel?.slideToPrevious() - this.list?.previousPage() - } - } - updateView() { this.changeDetector.detectChanges() } diff --git a/apps/metadata-editor/src/app/records/records-list.component.html b/apps/metadata-editor/src/app/records/records-list.component.html index 215e2e950..b5be0a567 100644 --- a/apps/metadata-editor/src/app/records/records-list.component.html +++ b/apps/metadata-editor/src/app/records/records-list.component.html @@ -7,10 +7,6 @@ >
- +
diff --git a/apps/metadata-editor/src/app/records/records-list.component.spec.ts b/apps/metadata-editor/src/app/records/records-list.component.spec.ts index 2a277e859..06835d444 100644 --- a/apps/metadata-editor/src/app/records/records-list.component.spec.ts +++ b/apps/metadata-editor/src/app/records/records-list.component.spec.ts @@ -10,7 +10,7 @@ import { Router } from '@angular/router' import { BehaviorSubject } from 'rxjs' import { datasetRecordsFixture } from '@geonetwork-ui/common/fixtures' import { MockBuilder } from 'ng-mocks' -import { PaginationButtonsComponent } from '@geonetwork-ui/ui/elements' +import { PaginationButtonsComponent } from '@geonetwork-ui/ui/layout' const results = [{ md: true }] const currentPage = 5 diff --git a/apps/metadata-editor/src/app/records/records-list.component.ts b/apps/metadata-editor/src/app/records/records-list.component.ts index 8f53f6d99..8659cc2ac 100644 --- a/apps/metadata-editor/src/app/records/records-list.component.ts +++ b/apps/metadata-editor/src/app/records/records-list.component.ts @@ -8,12 +8,10 @@ import { SearchService, } from '@geonetwork-ui/feature/search' import { UiSearchModule } from '@geonetwork-ui/ui/search' -import { - PaginationButtonsComponent, - UiElementsModule, -} from '@geonetwork-ui/ui/elements' +import { UiElementsModule } from '@geonetwork-ui/ui/elements' import { TranslateModule } from '@ngx-translate/core' import { UiInputsModule } from '@geonetwork-ui/ui/inputs' +import { Paginable, PaginationButtonsComponent } from '@geonetwork-ui/ui/layout' export const allSearchFields = [ 'uuid', @@ -41,7 +39,7 @@ export const allSearchFields = [ PaginationButtonsComponent, ], }) -export class RecordsListComponent implements OnInit { +export class RecordsListComponent implements OnInit, Paginable { constructor( private router: Router, public searchFacade: SearchFacade, @@ -51,10 +49,13 @@ export class RecordsListComponent implements OnInit { ngOnInit(): void { this.searchFacade.setConfigRequestFields(allSearchFields) this.searchFacade.setPageSize(15) - } - paginate(page: number) { - this.searchService.setPage(page) + this.searchFacade.currentPage$.subscribe((page) => { + this.currentPage_ = page + }) + this.searchFacade.totalPages$.subscribe((total) => { + this.totalPages_ = total + }) } editRecord(record: CatalogRecord) { @@ -64,4 +65,31 @@ export class RecordsListComponent implements OnInit { duplicateRecord(record: CatalogRecord) { this.router.navigate(['/duplicate', record.uniqueIdentifier]) } + + // these are 0 based + totalPages_: number + currentPage_: number + + // Paginable API + get isFirstPage() { + return this.currentPage_ === 0 + } + get isLastPage() { + return this.currentPage_ === this.totalPages_ - 1 + } + get pagesCount() { + return this.totalPages_ + } + get currentPage() { + return this.currentPage_ + 1 // this is 1-based for the Paginable API + } + goToPage(page: number) { + this.searchService.setPage(page - 1) + } + goToNextPage() { + this.searchService.setPage(this.currentPage_ + 1) + } + goToPrevPage() { + this.searchService.setPage(this.currentPage_ - 1) + } } diff --git a/libs/feature/catalog/src/lib/organisations/organisations.component.html b/libs/feature/catalog/src/lib/organisations/organisations.component.html index 3d253d16f..0e6e6c1c2 100644 --- a/libs/feature/catalog/src/lib/organisations/organisations.component.html +++ b/libs/feature/catalog/src/lib/organisations/organisations.component.html @@ -26,9 +26,5 @@
- +
diff --git a/libs/feature/catalog/src/lib/organisations/organisations.component.ts b/libs/feature/catalog/src/lib/organisations/organisations.component.ts index 565a631c6..474b34ec5 100644 --- a/libs/feature/catalog/src/lib/organisations/organisations.component.ts +++ b/libs/feature/catalog/src/lib/organisations/organisations.component.ts @@ -14,16 +14,14 @@ import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/orga import { SortByField } from '@geonetwork-ui/common/domain/model/search' import { createFuzzyFilter } from '@geonetwork-ui/util/shared' import { ORGANIZATION_PAGE_URL_TOKEN } from '../organization-url.token' -import { - ContentGhostComponent, - PaginationComponent, -} from '@geonetwork-ui/ui/elements' +import { ContentGhostComponent } from '@geonetwork-ui/ui/elements' import { CommonModule } from '@angular/common' import { OrganisationPreviewComponent, OrganisationsFilterComponent, OrganisationsResultComponent, } from '@geonetwork-ui/ui/catalog' +import { Paginable, PaginationComponent } from '@geonetwork-ui/ui/layout' @Component({ selector: 'gn-ui-organisations', @@ -40,7 +38,7 @@ import { PaginationComponent, ], }) -export class OrganisationsComponent { +export class OrganisationsComponent implements Paginable { @Input() itemsOnPage = 12 @Output() orgSelect = new EventEmitter() @@ -89,10 +87,6 @@ export class OrganisationsComponent { ) ) - protected setCurrentPage(page: number): void { - this.currentPage$.next(page) - } - protected setFilterBy(value: string): void { this.currentPage$.next(1) this.filterBy$.next(value) @@ -140,4 +134,27 @@ export class OrganisationsComponent { if (!this.urlTemplate) return null return this.urlTemplate.replace('${name}', organisation.name) } + + // Paginable API + get isFirstPage() { + return this.currentPage === 1 + } + get isLastPage() { + return this.currentPage === this.totalPages + } + get pagesCount() { + return this.totalPages + } + get currentPage() { + return this.currentPage$.value + } + goToPage(index: number) { + this.currentPage$.next(index) + } + goToNextPage() { + this.goToPage(this.currentPage - 1) + } + goToPrevPage() { + this.goToPage(this.currentPage + 1) + } } diff --git a/libs/ui/layout/src/lib/block-list/block-list.component.html b/libs/ui/layout/src/lib/block-list/block-list.component.html index 17e706ddd..8dc5b8a66 100644 --- a/libs/ui/layout/src/lib/block-list/block-list.component.html +++ b/libs/ui/layout/src/lib/block-list/block-list.component.html @@ -14,7 +14,7 @@ diff --git a/libs/ui/layout/src/lib/block-list/block-list.component.spec.ts b/libs/ui/layout/src/lib/block-list/block-list.component.spec.ts index 2b28278dd..39b4a4b45 100644 --- a/libs/ui/layout/src/lib/block-list/block-list.component.spec.ts +++ b/libs/ui/layout/src/lib/block-list/block-list.component.spec.ts @@ -103,7 +103,7 @@ describe('BlockListComponent', () => { beforeEach(() => { component.pageSize = 2 component.goToPage(2) - component.previousPage() + component.goToPrevPage() }) it('changes to previous page', () => { expect(component['currentPage']).toEqual(1) @@ -114,7 +114,7 @@ describe('BlockListComponent', () => { beforeEach(() => { component.pageSize = 2 component.goToPage(1) - component.nextPage() + component.goToNextPage() }) it('changes to next page', () => { expect(component['currentPage']).toEqual(2) diff --git a/libs/ui/layout/src/lib/block-list/block-list.component.stories.ts b/libs/ui/layout/src/lib/block-list/block-list.component.stories.ts index 31f06fdd7..7e921a611 100644 --- a/libs/ui/layout/src/lib/block-list/block-list.component.stories.ts +++ b/libs/ui/layout/src/lib/block-list/block-list.component.stories.ts @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/angular' -import { BlockListComponent } from './block-list.component' import { componentWrapperDecorator } from '@storybook/angular' +import { BlockListComponent } from './block-list.component' const meta: Meta = { component: BlockListComponent, diff --git a/libs/ui/layout/src/lib/block-list/block-list.component.ts b/libs/ui/layout/src/lib/block-list/block-list.component.ts index 5a0937c76..405f3460c 100644 --- a/libs/ui/layout/src/lib/block-list/block-list.component.ts +++ b/libs/ui/layout/src/lib/block-list/block-list.component.ts @@ -10,6 +10,7 @@ import { ViewChild, } from '@angular/core' import { CommonModule } from '@angular/common' +import { Paginable } from '../paginable.interface' @Component({ selector: 'gn-ui-block-list', @@ -19,7 +20,7 @@ import { CommonModule } from '@angular/common' standalone: true, imports: [CommonModule], }) -export class BlockListComponent implements AfterViewInit { +export class BlockListComponent implements AfterViewInit, Paginable { @Input() pageSize = 5 @Input() containerClass = '' @Input() paginationContainerClass = 'w-full bottom-0 top-auto' @@ -30,20 +31,23 @@ export class BlockListComponent implements AfterViewInit { protected minHeight = 0 - protected currentPage = 0 + protected currentPage_ = 0 protected get pages() { return new Array(this.pagesCount).fill(0).map((_, i) => i) } get isFirstPage() { - return this.currentPage === 0 + return this.currentPage_ === 0 } get isLastPage() { - return this.currentPage === this.pagesCount - 1 + return this.currentPage_ === this.pagesCount - 1 } get pagesCount() { return this.blocks ? Math.ceil(this.blocks.length / this.pageSize) : 1 } + get currentPage() { + return this.currentPage_ + 1 // this is 1-based + } constructor(private changeDetector: ChangeDetectorRef) {} @@ -59,25 +63,29 @@ export class BlockListComponent implements AfterViewInit { protected refreshBlocksVisibility = () => { this.blocks.forEach((block, index) => { block.nativeElement.style.display = - index >= this.currentPage * this.pageSize && - index < (this.currentPage + 1) * this.pageSize + index >= this.currentPage_ * this.pageSize && + index < (this.currentPage_ + 1) * this.pageSize ? null : 'none' }) } - public goToPage(index: number) { - this.currentPage = Math.max(Math.min(index, this.pagesCount - 1), 0) + // pageIndex is 1-based + public goToPage(pageIndex: number) { + this.currentPage_ = Math.max( + Math.min(pageIndex - 1, this.pagesCount - 1), + 0 + ) this.changeDetector.detectChanges() this.refreshBlocksVisibility() } - public previousPage() { + public goToPrevPage() { if (this.isFirstPage) return this.goToPage(this.currentPage - 1) } - public nextPage() { + public goToNextPage() { if (this.isLastPage) return this.goToPage(this.currentPage + 1) } diff --git a/libs/ui/layout/src/lib/carousel/carousel.component.spec.ts b/libs/ui/layout/src/lib/carousel/carousel.component.spec.ts index 7301cb0d1..c6b7fc1f1 100644 --- a/libs/ui/layout/src/lib/carousel/carousel.component.spec.ts +++ b/libs/ui/layout/src/lib/carousel/carousel.component.spec.ts @@ -73,7 +73,7 @@ describe('CarouselComponent', () => { }) describe('click on step', () => { beforeEach(() => { - component.scrollToStep(2) + component.goToPage(2) }) it('calls #scrollTo', () => { expect(component.emblaApi.scrollTo).toHaveBeenCalledWith(2) @@ -88,30 +88,30 @@ describe('CarouselComponent', () => { it('emits the current step index', () => { const spy = jest.fn() component.currentStepChange.subscribe(spy) - component.scrollToStep(2) + component.goToPage(2) expect(spy).toHaveBeenCalledWith(2) expect(spy).toHaveBeenCalledTimes(1) }) }) - describe('isFirstStep', () => { + describe('isFirstPage', () => { it('returns true if the current step is the first one', () => { - expect(component.isFirstStep).toBe(true) + expect(component.isFirstPage).toBe(true) }) it('returns false if the current step is not the first one', () => { - component.scrollToStep(2) - expect(component.isFirstStep).toBe(false) + component.goToPage(2) + expect(component.isFirstPage).toBe(false) }) }) - describe('isLastStep', () => { + describe('isLastPage', () => { it('returns true if the current step is the last one', () => { - component.scrollToStep(3) - expect(component.isLastStep).toBe(true) + component.goToPage(3) + expect(component.isLastPage).toBe(true) }) it('returns false if the current step is not the last one', () => { - component.scrollToStep(1) - expect(component.isLastStep).toBe(false) + component.goToPage(1) + expect(component.isLastPage).toBe(false) }) }) }) diff --git a/libs/ui/layout/src/lib/carousel/carousel.component.ts b/libs/ui/layout/src/lib/carousel/carousel.component.ts index abb751af2..9894ee58a 100644 --- a/libs/ui/layout/src/lib/carousel/carousel.component.ts +++ b/libs/ui/layout/src/lib/carousel/carousel.component.ts @@ -11,6 +11,7 @@ import { } from '@angular/core' import EmblaCarousel, { EmblaCarouselType } from 'embla-carousel' import { CommonModule } from '@angular/common' +import { Paginable } from '../paginable.interface' @Component({ selector: 'gn-ui-carousel', @@ -20,7 +21,7 @@ import { CommonModule } from '@angular/common' standalone: true, imports: [CommonModule], }) -export class CarouselComponent implements AfterViewInit { +export class CarouselComponent implements AfterViewInit, Paginable { @ViewChild('carouselOverflowContainer') carouselOverflowContainer: ElementRef @Input() containerClass = '' @@ -38,15 +39,30 @@ export class CarouselComponent implements AfterViewInit { this.changeDetector.detectChanges() } - get isFirstStep() { + // Paginable API + get isFirstPage() { return this.currentStep === 0 } - get isLastStep() { + get isLastPage() { return this.currentStep === this.steps.length - 1 } - get stepsCount() { + get currentPage() { + return this.currentStep + 1 // this is 1-based + } + get pagesCount() { return this.steps.length } + public goToPage(stepIndex: number) { + this.emblaApi.scrollTo(stepIndex - 1) // this is 0-based + } + public goToPrevPage() { + if (this.isFirstPage) return + this.emblaApi.scrollPrev() + } + public goToNextPage() { + if (this.isLastPage) return + this.emblaApi.scrollNext() + } constructor(private changeDetector: ChangeDetectorRef) {} @@ -63,18 +79,4 @@ export class CarouselComponent implements AfterViewInit { .on('reInit', this.refreshSteps) .on('select', this.refreshSteps) } - - public scrollToStep(stepIndex: number) { - this.emblaApi.scrollTo(stepIndex) - } - - public slideToPrevious() { - if (this.isFirstStep) return - this.emblaApi.scrollPrev() - } - - public slideToNext() { - if (this.isLastStep) return - this.emblaApi.scrollNext() - } }