Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/app/components/pagination/pagination.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@if (pagination().totalItemCount >= 0) {
<small style="margin-right: 16px">
{{ 'Page' | translate }} {{ pagination().pageIndex + 1 }} {{ 'of' | translate }}
{{ pagination().pageCount }}
</small>
<small style="margin-right: 16px">
{{ 'Results' | translate }}
{{ pagination().pageIndex * pagination().pageSize + 1 }}
-
{{
[(pagination().pageIndex + 1) * pagination().pageSize, pagination().totalItemCount]
| math: 'min'
}}
{{ 'of' | translate }} {{ pagination().totalItemCount }}
</small>
}
<button mat-icon-button (click)="firstPage()" [disabled]="!canNavigateFirst()">
<mat-icon>first_page</mat-icon>
</button>
<button mat-icon-button (click)="previousPage()" [disabled]="!canNavigatePrevious()">
<mat-icon>chevron_left</mat-icon>
</button>
<button mat-icon-button (click)="nextPage()" [disabled]="!canNavigateNext()">
<mat-icon>chevron_right</mat-icon>
</button>
<button mat-icon-button (click)="lastPage()" [disabled]="!canNavigateLast()">
<mat-icon>last_page</mat-icon>
</button>
8 changes: 8 additions & 0 deletions src/app/components/pagination/pagination.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:host {
grid-row: 3;
grid-column: 1 / -1;
flex-direction: row;
display: flex;
justify-content: flex-end;
align-items: center;
}
49 changes: 49 additions & 0 deletions src/app/components/pagination/pagination.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Component, computed, model } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { TranslatePipe } from 'src/app/pipes';
import { MathPipe } from 'src/app/pipes/math.pipe';

export type Pagination = {
pageCount: number;
pageSize: number;
pageIndex: number;
totalItemCount: number;
};

@Component({
selector: 'app-pagination',
imports: [MatIconModule, MatButtonModule, TranslatePipe, MathPipe],
templateUrl: './pagination.component.html',
styleUrl: './pagination.component.scss',
})
export class PaginationComponent {
pagination = model.required<Pagination>();

canNavigatePrevious = computed(() => this.pagination().pageIndex > 0);
canNavigateFirst = computed(() => this.pagination().pageIndex > 0);
canNavigateNext = computed(() => {
const { pageIndex, pageCount } = this.pagination();
return pageIndex + 1 < pageCount;
});
canNavigateLast = computed(() => {
const { pageIndex, pageCount } = this.pagination();
return pageIndex + 1 < pageCount;
});

public firstPage() {
this.pagination.update(state => ({ ...state, pageIndex: 0 }));
}
public previousPage() {
this.pagination.update(state => ({
...state,
pageIndex: Math.max(0, state.pageIndex - 1),
}));
}
public nextPage() {
this.pagination.update(state => ({ ...state, pageIndex: state.pageIndex + 1 }));
}
public lastPage() {
this.pagination.update(state => ({ ...state, pageIndex: state.pageCount - 1 }));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<mat-icon class="material-symbols-filled">tune</mat-icon>

@if (numFilterOptions() > 0) {
<span>{{ numFilterOptions() }}</span>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
:host {
display: flex;
flex-direction: row;
align-items: center;
padding: 8px;
gap: 8px;
border-radius: 24px;
background-color: var(--brand-color);
color: white;

&:hover,
&:focus,
&:focus-within {
cursor: pointer;
}
}

:host.has-active-filters {
padding: 8px 12px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component, input } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';

@Component({
selector: 'app-explore-filter-sidenav-toggle',
imports: [MatIconModule, MatTooltipModule],
templateUrl: './explore-filter-sidenav-toggle.component.html',
styleUrl: './explore-filter-sidenav-toggle.component.scss',
})
export class ExploreFilterSidenavToggleComponent {
numFilterOptions = input<number>(0);
}
Original file line number Diff line number Diff line change
@@ -1,91 +1,93 @@
<app-explore-filter-option
label="Sort by"
placeholder="Select sorting option"
[options]="filterOptions.sortBy"
(optionSelected)="onFilterOptionSelected($event, 'sortBy')"
[selectedOptions]="selectedSortByOption()"
/>
@if (filterOptions(); as filterOptions) {
@if (filterOptions.sortBy && filterOptions.sortBy.length > 0) {
<app-explore-filter-option
label="Sort by"
placeholder="Select sorting option"
[options]="filterOptions.sortBy"
(optionSelected)="onFilterOptionSelected($event, 'sortBy')"
[selectedOptions]="selectedSortByOption()"
/>
<mat-divider />
}
@if (filterOptions.mediaType && filterOptions.mediaType.length > 0) {
<app-explore-filter-option
label="Media types"
placeholder="Select media types"
[options]="filterOptions.mediaType"
(optionSelected)="onFilterOptionSelected($event, 'mediaType')"
[multiple]="true"
[selectedOptions]="selectedMediaTypeOptions()"
/>
<mat-divider />
}
@if (filterOptions.annotation && filterOptions.annotation.length > 0) {
<app-explore-filter-option
label="Annotations"
placeholder="Select annotation options"
[options]="filterOptions.annotation"
(optionSelected)="onFilterOptionSelected($event, 'annotation')"
[multiple]="true"
[selectedOptions]="selectedAnnotationOptions()"
/>
<mat-divider />
}
@if (isAuthenticated() && filterOptions.access && filterOptions.access.length > 0) {
<app-explore-filter-option
label="Access"
placeholder="Select access options"
[options]="filterOptions.access"
(optionSelected)="onFilterOptionSelected($event, 'access')"
[multiple]="true"
[selectedOptions]="selectedAccessOptions()"
/>
<mat-divider />
}
@if (filterOptions.licence && filterOptions.licence.length > 0) {
<app-explore-filter-option
label="Licences"
placeholder="Select licence options"
[options]="filterOptions.licence"
(optionSelected)="onFilterOptionSelected($event, 'licence')"
[multiple]="true"
[selectedOptions]="selectedLicenceOptions()"
/>
<mat-divider />
}
@if (filterOptions.misc && filterOptions.misc.length > 0) {
<app-explore-filter-option
label="Other"
placeholder="Select other options"
[options]="filterOptions.misc"
(optionSelected)="onFilterOptionSelected($event, 'misc')"
[multiple]="true"
[selectedOptions]="selectedMiscOptions()"
/>
}

<mat-divider />
<div class="button-row">
<button
mat-flat-button
class="pill"
color="primary"
style="align-self: flex-start"
(click)="closeSidenav()"
>
<mat-icon class="material-symbols-filled">arrow_forward</mat-icon>
{{ 'Show results' | translate }}
@if (service.resultCount() >= 0) {
<span class="result-count">({{ service.resultCount() }})</span>
}
</button>

<app-explore-filter-option
label="Media types"
placeholder="Select media types"
[options]="filterOptions.mediaType"
(optionSelected)="onFilterOptionSelected($event, 'mediaType')"
[multiple]="true"
[selectedOptions]="selectedMediaTypeOptions()"
/>

<mat-divider />

<app-explore-filter-option
label="Annotations"
placeholder="Select annotation options"
[options]="filterOptions.annotation"
(optionSelected)="onFilterOptionSelected($event, 'annotation')"
[multiple]="true"
[selectedOptions]="selectedAnnotationOptions()"
/>

<mat-divider />

@if (isAuthenticated()) {
<app-explore-filter-option
label="Access"
placeholder="Select access options"
[options]="filterOptions.access"
(optionSelected)="onFilterOptionSelected($event, 'access')"
[multiple]="true"
[selectedOptions]="selectedAccessOptions()"
/>

<mat-divider />
<button
mat-flat-button
color="primary"
class="clear-all-button"
(click)="clearAllFilterOptions()"
[disabled]="numFilterOptions() === 0"
>
<mat-icon class="material-symbols-filled">refresh</mat-icon>
{{ 'Clear all' | translate }}
</button>
</div>
}

<app-explore-filter-option
label="Licences"
placeholder="Select licence options"
[options]="filterOptions.licence"
(optionSelected)="onFilterOptionSelected($event, 'licence')"
[multiple]="true"
[selectedOptions]="selectedLicenceOptions()"
/>

<mat-divider />

<app-explore-filter-option
label="Other"
placeholder="Select other options"
[options]="filterOptions.misc"
(optionSelected)="onFilterOptionSelected($event, 'misc')"
[multiple]="true"
[selectedOptions]="selectedMiscOptions()"
/>

<div class="button-row">
<button
mat-flat-button
class="pill"
color="primary"
style="align-self: flex-start"
(click)="closeSidenav()"
>
<mat-icon class="material-symbols-filled">arrow_forward</mat-icon>
{{ 'Show results' | translate }}
@if (service.resultCount() >= 0) {
<span class="result-count">({{ service.resultCount() }})</span>
}
</button>

<button
mat-flat-button
color="primary"
class="clear-all-button"
(click)="clearAllFilterOptions()"
[disabled]="numFilterOptions() === 0"
>
<mat-icon class="material-symbols-filled">refresh</mat-icon>
{{ 'Clear all' | translate }}
</button>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ import { toSignal } from '@angular/core/rxjs-interop';
export type ExploreFilterSidenavData = {
options: ExploreFilterOption[];
category: ExploreCategory;
filterOptions?: {
sortBy?: ExploreFilterOption[];
mediaType?: ExploreFilterOption[];
annotation?: ExploreFilterOption[];
access?: ExploreFilterOption[];
licence?: ExploreFilterOption[];
misc?: ExploreFilterOption[];
};
};

const DEFAULT_FILTER_OPTIONS: NonNullable<ExploreFilterSidenavData['filterOptions']> = {
sortBy: SortByOptions,
mediaType: MediaTypeOptions,
annotation: AnnotationOptions,
access: AccessOptions,
licence: LicenceOptions,
misc: MiscOptions,
};

@Injectable({ providedIn: 'root' })
Expand Down Expand Up @@ -105,20 +122,14 @@ export class ExploreFilterSidenavComponent implements SidenavComponent {
});
}

public filterOptions = {
sortBy: SortByOptions,
mediaType: MediaTypeOptions,
annotation: AnnotationOptions,
access: AccessOptions,
licence: LicenceOptions,
misc: MiscOptions,
};
filterOptions = computed(() => this.dataInput()?.filterOptions ?? DEFAULT_FILTER_OPTIONS);

selectedSortByOption = computed(() => {
const selectedOptions = this.selectedFilterOptions();
const availableOptions = this.filterOptions();
const selected = selectedOptions.find(option => option.category === 'sortBy');
if (selected) return [selected];
const defaultOption = SortByOptions.find(option => option.default);
const defaultOption = (availableOptions.sortBy ?? SortByOptions).find(option => option.default);
return defaultOption ? [defaultOption] : [];
});

Expand Down
12 changes: 3 additions & 9 deletions src/app/pages/explore/explore.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,16 @@ <h1 class="page-heading">{{ 'Explore' | translate }}</h1>
[initialValue]="searchText()"
/>

<div
class="filter-sidenav-toggle"
<app-explore-filter-sidenav-toggle
[numFilterOptions]="numFilterOptions()"
[class.has-active-filters]="numFilterOptions() > 0"
[matTooltip]="
numFilterOptions() > 0
? ('You have activated filters, modifying the search results' | translate)
: ''
"
(click)="openFilterSidenav()"
>
<mat-icon class="material-symbols-filled">tune</mat-icon>

@if (numFilterOptions() > 0) {
<span>{{ numFilterOptions() }}</span>
}
</div>
/>
</aside>
</app-tabs>

Expand Down
Loading
Loading