Skip to content

Commit

Permalink
feat: add cluster highlight on hover
Browse files Browse the repository at this point in the history
  • Loading branch information
marc-gavanier committed Jan 10, 2024
1 parent b20ec49 commit 704c650
Show file tree
Hide file tree
Showing 19 changed files with 204 additions and 110 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="d-print-none my-4 mx-sm-4 mx-2">
<div class="d-print-none py-4 px-sm-4 px-2">
<div class="row g-2">
<a
class="text-decoration-none link-dark link-lieu stretched-link col"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ <h1 class="visually-hidden">Liste des lieux d'inclusion numérique</h1>
#item
*cdkVirtualFor="let lieuMediationNumerique of lieuxMediationNumerique; trackBy: trackByLieuId"
[attr.id]="lieuMediationNumerique.id"
[class.list-group-item-hover]="hoverId === lieuMediationNumerique.id"
[class.list-group-item-hover]="
hoverId === lieuMediationNumerique.id ||
clustersPresenter.lieuxIdsInClusterId($any(hoverId)).includes(lieuMediationNumerique.id)
"
[ngClass]="{ 'bg-muted-light': lieuMediationNumerique.prive }"
class="list-group-item p-0">
<app-mediation-numerique-list-item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ActivatedRoute } from '@angular/router';
import { LabelNational } from '@gouvfr-anct/lieux-de-mediation-numerique';
import { FeatureConfiguration } from '../../../../../root';
import { LieuMediationNumeriqueListItemPresentation } from '../../../presenters';
import { ClustersPresenter } from '../../../../core/presenters/clusters';
import { CartographieLayout } from '../../../layouts';

const itemById =
Expand Down Expand Up @@ -53,7 +54,11 @@ export class LieuxMediationNumeriqueListComponent {

@ViewChildren('item') public items!: QueryList<ElementRef>;

public constructor(public readonly route: ActivatedRoute, public readonly cartographieLayout: CartographieLayout) {}
public constructor(
public readonly route: ActivatedRoute,
public readonly cartographieLayout: CartographieLayout,
public readonly clustersPresenter: ClustersPresenter
) {}

public scrollTo(focusId: string): void {
setTimeout((): void => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
<ng-container *ngIf="displayTooltip">
<mgl-popup
*ngIf="highlight | async as highlighted"
anchor="bottom"
[offset]="[0, -40]"
[lngLat]="[highlighted.longitude, highlighted.latitude]"
[closeButton]="false">
<div *ngIf="highlighted.prise_rdv" class="text-center text-primary fs-6 p-2" style="background-color: #c0c0ff">
Prise de RDV en ligne disponible
</div>
<div
class="text-center p-3 border-bottom border-3 text-wrap"
[ngClass]="highlighted.status?.label === 'Ouvert' ? 'border-success' : 'border-muted'">
<b class="fs-5">{{ highlighted.nom }}</b>
<div *ngIf="highlighted.status; else noOpeningHours" class="text-muted">
<span [class.text-primary]="highlighted.status.label === 'Ouvert'">
{{ highlighted.status.label }}
</span>
·
<small>{{ highlighted.status.limite }}</small>
<ng-container *ngIf="highlightedLieu | async as highlighted">
<mgl-popup
*ngIf="highlighted.id"
anchor="bottom"
[offset]="[0, -40]"
[lngLat]="[highlighted.longitude, highlighted.latitude]"
[closeButton]="false">
<div *ngIf="highlighted.prise_rdv" class="text-center text-primary fs-6 p-2" style="background-color: #c0c0ff">
Prise de RDV en ligne disponible
</div>
<span class="my-1 fs-4" *ngIf="highlighted.prive">Ce lieu n'accueille pas de public</span>
<ng-template #noOpeningHours>
<div class="text-muted">Horaires inconnus</div>
</ng-template>
</div>
</mgl-popup>
<div
class="text-center p-3 border-bottom border-3 text-wrap"
[ngClass]="highlighted.status?.label === 'Ouvert' ? 'border-success' : 'border-muted'">
<b class="fs-5">{{ highlighted.nom }}</b>
<div *ngIf="highlighted.status; else noOpeningHours" class="text-muted">
<span [class.text-primary]="highlighted.status.label === 'Ouvert'">
{{ highlighted.status.label }}
</span>
·
<small>{{ highlighted.status.limite }}</small>
</div>
<span class="my-1 fs-4" *ngIf="highlighted.prive">Ce lieu n'accueille pas de public</span>
<ng-template #noOpeningHours>
<div class="text-muted">Horaires inconnus</div>
</ng-template>
</div>
</mgl-popup>
</ng-container>
</ng-container>
<ng-container *ngFor="let cluster of lieuxMediationNumeriqueClusters">
<ng-container *ngIf="cluster.properties['cluster'] === true; else lieuMarker">
<mgl-marker [lngLat]="$any(cluster.geometry.coordinates)">
<mgl-marker
[lngLat]="$any(cluster.geometry.coordinates)"
[className]="clustersPresenter.clusterIdFromLieuId(hoverId) === cluster.properties['cluster_id'] ? 'marker-hover' : ''">
<button
tabindex="-1"
class="btn text-white marker-cluster"
(click)="
selectCluster.emit(
$any({
latitude: cluster.geometry.coordinates[1],
longitude: cluster.geometry.coordinates[0]
})
)
">
(click)="selectCluster.emit($any(cluster))"
(mouseenter)="highlight.emit(cluster.properties['cluster_id'])"
(mouseleave)="highlight.emit()">
<svg width="52" height="64" viewBox="0 0 52 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M49.642 26.497C49.642 47.855 26 61.137 26 61.137S2.358 47.854 2.358 26.496c0-13.09 10.584-23.7 23.642-23.7s23.642 10.61 23.642 23.7z"
Expand All @@ -49,7 +48,9 @@
stroke="#fff" />
</svg>
<div class="marker-cluster-fg d-flex">
<span class="m-auto text-white fw-bold fs-6 mt-4">{{ cluster.properties['point_count'] ?? 0 }}</span>
<span class="m-auto text-white fw-bold fs-6 mt-4">
{{ cluster.properties['point_count'] ?? 0 }}
</span>
</div>
</button>
</mgl-marker>
Expand All @@ -63,7 +64,7 @@
[ngClass]="{ 'opacity-50': cluster.properties['prive'] }"
class="btn"
(click)="showDetails.emit($any(cluster.properties))"
(mouseenter)="highlight.emit($any(cluster.properties))"
(mouseenter)="highlight.emit(cluster.properties['id'])"
(mouseleave)="highlight.emit()">
<svg
[ngClass]="cluster.properties['labels_nationaux']?.includes($any('CNFS')) ? 'marker-cnfs' : 'marker-default'"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { Localisation } from '@gouvfr-anct/lieux-de-mediation-numerique';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AnyProps, ClusterFeature } from 'supercluster';
import { LieuMediationNumeriquePresentation } from '../../../../core/presenters';
import { ClustersPresenter } from '../../../../core/presenters/clusters';
import { Cluster } from '../../../models';
import { LieuMediationNumeriqueOnMapPresentation } from '../../../presenters';

Expand All @@ -10,6 +13,8 @@ export type LieuMediationNumeriqueCluster = {
properties: { cluster?: boolean } | LieuMediationNumeriquePresentation;
};

const matchingLieuInForCluster = (id: string) => (cluster: Cluster) => cluster.properties['id'] === id;

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-lieu-mediation-numerique-markers',
Expand All @@ -24,9 +29,17 @@ export class LieuMediationNumeriqueMarkersComponent {
@Output() public showDetails: EventEmitter<LieuMediationNumeriqueOnMapPresentation> =
new EventEmitter<LieuMediationNumeriqueOnMapPresentation>();

@Output() public selectCluster: EventEmitter<Localisation> = new EventEmitter<Localisation>();
@Output() public selectCluster: EventEmitter<ClusterFeature<AnyProps>> = new EventEmitter<ClusterFeature<AnyProps>>();

@Output() public highlight: EventEmitter<string> = new EventEmitter<string>();

public constructor(public clustersPresenter: ClustersPresenter) {}

@Output() public highlight: EventEmitter<LieuMediationNumeriqueOnMapPresentation | undefined> = new EventEmitter<
LieuMediationNumeriqueOnMapPresentation | undefined
>();
public highlightedLieu: Observable<LieuMediationNumeriqueOnMapPresentation | undefined> = this.highlight.pipe(
map((id: string) => {
return this.lieuxMediationNumeriqueClusters.find(matchingLieuInForCluster(id))?.properties as
| LieuMediationNumeriqueOnMapPresentation
| undefined;
})
);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ <h3 class="card-title text-primary">
[lieuxMediationNumeriqueClusters]="clusters"
[selectedId]="(markersPresenter.selected$ | async) ?? ''"
[hoverId]="(markersPresenter.highlighted$ | async) ?? ''"
(highlight)="onHighlight($event?.id)"
(highlight)="onHighlight($event)"
(showDetails)="onShowDetails($event)"
(selectCluster)="onSelectCluster($event)"></app-lieu-mediation-numerique-markers>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { CLUSTER_TOKEN, INITIAL_POSITION_TOKEN, ZOOM_LEVEL_TOKEN } from '../../../../root';
import { AddressPresenter, AddressRepository, SEARCHABLE_TOKEN } from '../../../adresse';
import { LieuxMediationNumeriquePresenter, MarkersPresenter } from '../../../core/presenters';
import { ClustersPresenter } from '../../../core/presenters/clusters';
import { LieuxMediationNumeriqueRepository } from '../../../core/repositories';
import { AddressPresenter, AddressRepository, SEARCHABLE_TOKEN } from '../../../adresse';
import { LieuxMediationNumeriqueDetailsPresenter } from '../../presenters';
import { INITIAL_POSITION_TOKEN, ZOOM_LEVEL_TOKEN } from '../../../../root';

export const cartographieLayoutProviders = [
{
Expand Down Expand Up @@ -31,5 +32,10 @@ export const cartographieLayoutProviders = [
deps: [ZOOM_LEVEL_TOKEN, INITIAL_POSITION_TOKEN],
provide: MarkersPresenter,
useClass: MarkersPresenter
},
{
deps: [MarkersPresenter, CLUSTER_TOKEN],
provide: ClustersPresenter,
useClass: ClustersPresenter
}
];
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BehaviorSubject, combineLatest, Observable, of, Subject, tap, withLates
import { map } from 'rxjs/operators';
import { LngLatBounds, MapLibreEvent } from 'maplibre-gl';
import { MatomoTracker } from 'ngx-matomo';
import Supercluster from 'supercluster';
import { AnyProps, ClusterFeature } from 'supercluster';
import { Localisation } from '@gouvfr-anct/lieux-de-mediation-numerique';
import { ASSETS_TOKEN, AssetsConfiguration, ZOOM_LEVEL_TOKEN, ZoomLevelConfiguration } from '../../../../root';
import { NO_LOCALISATION } from '../../../core/models';
Expand All @@ -26,6 +26,7 @@ import {
toLocalisationFromFilterFormPresentation,
WithLieuxCount
} from '../../../core/presenters';
import { ClustersPresenter } from '../../../core/presenters/clusters';
import { ifAny } from '../../../core/utilities';
import { AddressType, ResultFoundPresentation } from '../../../adresse';
import { LieuMediationNumeriqueCluster } from '../../components';
Expand Down Expand Up @@ -77,15 +78,6 @@ const toLieuMediationNumeriqueToGeoJsonFeature = (lieu: LieuMediationNumeriquePr
};
};

const toCluster = ([lieuxMediationNumerique, zoom, [topLeft, bottomRight]]: [
LieuMediationNumeriquePresentation[],
number,
[Localisation, Localisation]
]): Cluster[] =>
new Supercluster({ radius: 35, maxZoom: 16 })
.load(lieuxMediationNumerique.map(toLieuMediationNumeriqueToGeoJsonFeature))
.getClusters([topLeft.longitude, bottomRight.latitude, bottomRight.longitude, topLeft.latitude], zoom);

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './cartographie.layout.html',
Expand Down Expand Up @@ -158,7 +150,9 @@ export class CartographieLayout {
this._loadingState$.next(false);
}),
withLatestFrom(this.currentZoom$, this.markersPresenter.boundingBox$),
map(toCluster)
map((clusterParams: [LieuMediationNumeriquePresentation[], number, [Localisation, Localisation]]) =>
this._clusterPresenter.fromLieuxMediationNumerique(clusterParams)
)
);

public allLieuxMediationNumerique$: Observable<LieuMediationNumeriquePresentation[]> =
Expand Down Expand Up @@ -190,6 +184,7 @@ export class CartographieLayout {
public readonly router: Router,
public readonly route: ActivatedRoute,
public readonly markersPresenter: MarkersPresenter,
private readonly _clusterPresenter: ClustersPresenter,
@Inject(ASSETS_TOKEN) public readonly assetsConfiguration: AssetsConfiguration,
@Inject(ZOOM_LEVEL_TOKEN)
private readonly _zoomLevel: ZoomLevelConfiguration,
Expand Down Expand Up @@ -220,8 +215,14 @@ export class CartographieLayout {
this.toggleMapForSmallDevices();
}

public onSelectCluster(localisation: Localisation): void {
this.markersPresenter.center(localisation, this.markersPresenter.getZoom() + 2);
public onSelectCluster(cluster: ClusterFeature<AnyProps>): void {
this.markersPresenter.center(
Localisation({
latitude: cluster.geometry.coordinates[1],
longitude: cluster.geometry.coordinates[0]
}),
this._clusterPresenter.expansionZoom(cluster.properties.cluster_id)
);
}

public onHighlight(highlightedId?: string): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { CollapseModule, ModalModule, OffcanvasModule, TextSeparatorModule } fro
import { CheckboxArrayModule } from '../../../core/directives';
import { DistancePipeModule, PhonePipeModule } from '../../../core/pipes';
import { AdresseModule, UserLocationModule } from '../../../adresse';
import { components } from '../../components';
import { pages } from '../../pages';
import { CartographieLayout } from '../../layouts';
import { components } from '../../components';

@NgModule({
declarations: [CartographieLayout, ...pages, ...components],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { HttpClient } from '@angular/common/http';
import { INITIAL_POSITION_TOKEN, POSITION_CONFIGURATION, ZOOM_LEVEL_CONFIGURATION, ZOOM_LEVEL_TOKEN } from '../../../../root';
import {
CLUSTER_CONFIGURATION,
CLUSTER_TOKEN,
INITIAL_POSITION_TOKEN,
POSITION_CONFIGURATION,
ZOOM_LEVEL_CONFIGURATION,
ZOOM_LEVEL_TOKEN
} from '../../../../root';
import { AddressHttp, AddressRepository } from '../../../adresse';

export const cartographieProviders = [
Expand All @@ -11,6 +18,10 @@ export const cartographieProviders = [
provide: ZOOM_LEVEL_TOKEN,
useValue: ZOOM_LEVEL_CONFIGURATION
},
{
provide: CLUSTER_TOKEN,
useValue: CLUSTER_CONFIGURATION
},
{
deps: [HttpClient],
provide: AddressRepository,
Expand Down
Loading

0 comments on commit 704c650

Please sign in to comment.