|
| 1 | +import type { CreateMarkerInterface, CreatePopupInterface, IconOptions, MarkerInterface } from '@helsingborg-stad/openstreetmap'; |
| 2 | +import type { CreateMarker, MarkerConfig } from './openstreetmapInterface'; |
| 3 | + |
| 4 | +class Marker implements CreateMarker { |
| 5 | + /** |
| 6 | + * @param markerCreator - Factory used to create map marker instances. |
| 7 | + * @param popupCreator - Factory used to create popup instances bound to markers. |
| 8 | + */ |
| 9 | + constructor( |
| 10 | + private markerCreator: CreateMarkerInterface, |
| 11 | + private popupCreator: CreatePopupInterface, |
| 12 | + ) {} |
| 13 | + |
| 14 | + /** |
| 15 | + * Creates a map marker at the position defined in options. When a content |
| 16 | + * string is provided, a popup is bound to the marker and click listeners |
| 17 | + * are attached to toggle the highlighted icon state. |
| 18 | + * |
| 19 | + * @param options - Configuration for the marker's position, icon, color, and popup content. |
| 20 | + * @returns The created MarkerInterface instance. |
| 21 | + */ |
| 22 | + public create(options: MarkerConfig): MarkerInterface { |
| 23 | + const marker = this.markerCreator.create({ |
| 24 | + position: { lat: options.lat, lng: options.lng }, |
| 25 | + ...this.getIconOptions(this.getMarkerContent(options)), |
| 26 | + }); |
| 27 | + |
| 28 | + if (options.content) { |
| 29 | + const popup = this.popupCreator.create(); |
| 30 | + popup.bindTo(marker); |
| 31 | + popup.setContent(options.content); |
| 32 | + |
| 33 | + this.addClickListener(marker, options); |
| 34 | + } |
| 35 | + |
| 36 | + return marker; |
| 37 | + } |
| 38 | + |
| 39 | + /** |
| 40 | + * Attaches popupopen and popupclose event listeners to the marker so that |
| 41 | + * its icon switches to the highlighted state while the popup is open and |
| 42 | + * reverts to the default state when the popup closes. |
| 43 | + * |
| 44 | + * @param marker - The marker to attach listeners to. |
| 45 | + * @param options - Marker configuration used to regenerate the icon HTML. |
| 46 | + */ |
| 47 | + private addClickListener(marker: MarkerInterface, options: MarkerConfig) { |
| 48 | + marker.addListener('popupopen', () => { |
| 49 | + marker.setIcon(this.getIconOptions(this.getHighlightedMarkerContent(options))); |
| 50 | + }); |
| 51 | + |
| 52 | + marker.addListener('popupclose', () => { |
| 53 | + marker.setIcon(this.getIconOptions(this.getMarkerContent(options))); |
| 54 | + }); |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Builds an IconOptions object with a fixed 32×32 size and a centred |
| 59 | + * horizontal anchor at [16, 2]. |
| 60 | + * |
| 61 | + * @param html - The HTML string to use as the icon's visual content. |
| 62 | + * @returns An IconOptions object ready to pass to the marker creator. |
| 63 | + */ |
| 64 | + private getIconOptions(html: string): IconOptions { |
| 65 | + return { |
| 66 | + html: html, |
| 67 | + iconSize: [32, 32], |
| 68 | + iconAnchor: [16, 2], |
| 69 | + }; |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Generates the HTML string for the highlighted (active/open-popup) marker |
| 74 | + * icon, rendered as a white circle with a coloured border and icon. |
| 75 | + * |
| 76 | + * @param options - Marker configuration supplying colour and icon values. |
| 77 | + * @returns An HTML string representing the highlighted marker icon. |
| 78 | + */ |
| 79 | + private getHighlightedMarkerContent(options: MarkerConfig): string { |
| 80 | + const [color, icon] = this.getColorAndIcon(options); |
| 81 | + |
| 82 | + return `<span style="background-color: white; border: solid 2px ${color}; color: ${color}; font-size: 20px; padding: 4px; border-radius: 50%;" data-material-symbol="${icon}" class="interactive-map__highlighted-marker material-symbols material-symbols-rounded material-symbols-sharp material-symbols-outlined material-symbols--filled"></span>`; |
| 83 | + } |
| 84 | + |
| 85 | + /** |
| 86 | + * Generates the HTML string for the default marker icon, rendered as a |
| 87 | + * solid coloured circle with a white icon. |
| 88 | + * |
| 89 | + * @param options - Marker configuration supplying colour and icon values. |
| 90 | + * @returns An HTML string representing the default marker icon. |
| 91 | + */ |
| 92 | + private getMarkerContent(options: MarkerConfig): string { |
| 93 | + const [color, icon] = this.getColorAndIcon(options); |
| 94 | + |
| 95 | + return `<span style="background-color: ${color}; border: solid 2px ${color}; color: white; font-size: 20px; padding: 4px; border-radius: 50%;" data-material-symbol="${icon}" class="material-symbols material-symbols-rounded material-symbols-sharp material-symbols-outlined material-symbols--filled"></span>`; |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * Extracts the colour and icon name from the marker options, falling back |
| 100 | + * to the default colour (#E04A39) and icon (location_on) when not provided. |
| 101 | + * |
| 102 | + * @param options - Marker configuration that may contain colour and icon overrides. |
| 103 | + * @returns A tuple of [color, icon] strings. |
| 104 | + */ |
| 105 | + private getColorAndIcon(options: MarkerConfig): [string, string] { |
| 106 | + const color = options.color ?? '#E04A39'; |
| 107 | + const icon = options.icon ?? 'location_on'; |
| 108 | + |
| 109 | + return [color, icon]; |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +export default Marker; |
0 commit comments