Skip to content

Commit a32610e

Browse files
committed
WIP
- some progress, sort of - specifically had to rename files to match their counterparts that they `require` - but still not working properly
1 parent 915b74e commit a32610e

10 files changed

Lines changed: 223 additions & 11 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//= require @babel/runtime/helpers/asyncToGenerator.js
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//= require @babel/runtime/helpers/defineProperty.js
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/* global defra */
2+
window.GOVUK = window.GOVUK || {}
3+
window.GOVUK.Modules = window.GOVUK.Modules || {};
4+
5+
import InteractiveMap from '@defra/interactive-map'
6+
import maplibreProvider from '@defra/interactive-map/providers/maplibre'
7+
8+
(function (Modules) {
9+
class Map {
10+
constructor ($module) {
11+
console.log('constructor')
12+
this.$module = $module
13+
this.map_element = this.$module.querySelector('.app-c-map')
14+
this.map_id = this.$module.getAttribute('id')
15+
const cspWorker = this.$module.getAttribute('data-csp-worker')
16+
17+
this.interactPlugin = defra.interactPlugin({
18+
deselectOnClickOutside: true
19+
})
20+
21+
const config = {
22+
mapProvider: maplibreProvider({ workerUrl: cspWorker }),
23+
behaviour: 'inline',
24+
mapStyle: {
25+
url: window.GOVUK.mapComponentStyles,
26+
backgroundColor: '#f5f5f0'
27+
},
28+
plugins: [this.interactPlugin],
29+
urlPosition: 'none',
30+
minZoom: 4,
31+
maxZoom: 16,
32+
center: [-0.09, 51.505],
33+
containerHeight: '500px'
34+
}
35+
const passedConfig = JSON.parse(this.$module.getAttribute('data-config')) || {}
36+
this.config = Object.assign(config, passedConfig)
37+
38+
this.markers = JSON.parse(this.$module.getAttribute('data-markers')) || []
39+
this.geoJsonUrl = this.$module.getAttribute('data-geojson')
40+
if (this.geoJsonUrl && !this.geoJsonUrl.startsWith('/')) {
41+
console.error(`Error: external URLs for geoJSON are not allowed: ${this.geoJsonUrl}`)
42+
this.geoJsonUrl = false
43+
}
44+
this.headingLevel = parseInt(this.$module.getAttribute('data-heading-level')) || 2
45+
46+
this.markerOptions = {
47+
symbol: 'circle',
48+
backgroundColor: '#1d70b8',
49+
foregroundColor: '#FFFFFF',
50+
haloWidth: 3,
51+
selectedWidth: 8
52+
}
53+
}
54+
55+
init () {
56+
const id = this.$module.getAttribute('id')
57+
this.$module.setAttribute('id', '')
58+
this.map_element.setAttribute('id', id)
59+
this.map_element.classList.add('app-c-map--enabled')
60+
61+
this.map = new InteractiveMap(this.map_id, this.config)
62+
63+
/* istanbul ignore next */
64+
this.map.on('map:ready', () => {
65+
this.addAllMarkers()
66+
})
67+
68+
/* istanbul ignore next */
69+
this.map.on('interact:selectionchange', (e) => {
70+
if (e.selectedMarkers.length > 0) {
71+
let marker = parseInt(e.selectedMarkers[0].replace('marker-', ''))
72+
marker = this.markers[marker]
73+
this.map.addPanel('the-panel', {
74+
focus: false,
75+
label: marker.name,
76+
html: this.createPopupContent(marker),
77+
mobile: { slot: 'drawer', dismissible: true },
78+
tablet: { slot: 'left-top', dismissible: true, width: '280px' },
79+
desktop: { slot: 'left-top', dismissible: true, width: '280px' }
80+
})
81+
} else {
82+
this.map.hidePanel('the-panel')
83+
}
84+
})
85+
86+
/* istanbul ignore next */
87+
this.map.on('app:panelclosed', (e) => {
88+
this.interactPlugin.clear()
89+
})
90+
}
91+
92+
createPopupContent (feature) {
93+
const heading = `h${this.headingLevel}`
94+
let popupContent = `<${heading} class="govuk-heading-s govuk-!-margin-bottom-2">${feature.properties.name}</${heading}>`
95+
if (feature.properties.description) {
96+
popupContent = `${popupContent} ${feature.properties.description}`
97+
}
98+
popupContent = this.removeScript(popupContent)
99+
return popupContent
100+
}
101+
102+
removeScript (input) {
103+
do {
104+
input = input.replace(/<[\s]*script/g, '')
105+
} while (input.includes('<script'))
106+
return input
107+
}
108+
109+
async addAllMarkers () {
110+
if (this.geoJsonUrl) {
111+
try {
112+
const response = await fetch(this.geoJsonUrl)
113+
if (!response.ok) {
114+
throw new Error(`Response status: ${response.status}`)
115+
}
116+
const result = await response.json()
117+
if (this.markers) {
118+
this.markers = this.markers.concat(result.features)
119+
}
120+
} catch (error) {
121+
console.error(`${error}, with geojson at ${this.geoJsonUrl}`)
122+
}
123+
}
124+
this.markers.sort((a, b) => {
125+
const nameA = a.properties.name.toUpperCase()
126+
const nameB = b.properties.name.toUpperCase()
127+
if (nameA < nameB) {
128+
return -1
129+
}
130+
if (nameA > nameB) {
131+
return 1
132+
}
133+
return 0 // names are equal
134+
})
135+
this.addMarkers()
136+
137+
// only fit to bounds if there are more than one markers
138+
if (this.markers.length > 0 && !this.config.bounds) {
139+
this.map.fitToBounds({
140+
type: 'FeatureCollection',
141+
features: this.markers
142+
})
143+
}
144+
if (this.markers.length > 0) {
145+
this.interactPlugin.enable()
146+
this.addPopupsList()
147+
}
148+
}
149+
150+
addMarkers () {
151+
const allowedColours = {
152+
blue: '#1d70b8',
153+
green: '#0f7a52',
154+
orange: '#f47738',
155+
red: '#ca3535'
156+
}
157+
const allowedSymbols = ['circle', 'pin', 'square']
158+
this.markers.forEach((marker, index) => {
159+
if (marker.marker) {
160+
const colour = marker.marker.colour || false
161+
if (colour) {
162+
if (allowedColours[colour]) {
163+
marker.marker.backgroundColor = allowedColours[colour]
164+
}
165+
delete marker.marker.colour
166+
}
167+
const symbol = marker.marker.symbol || false
168+
if (!(symbol && allowedSymbols.includes(symbol))) {
169+
delete marker.marker.symbol
170+
}
171+
}
172+
const options = Object.assign(Object.assign({}, this.markerOptions), marker.marker || {})
173+
this.map.addMarker(`marker-${index}`, marker.geometry.coordinates, options)
174+
})
175+
}
176+
177+
addPopupsList () {
178+
const popupsListWrapper = this.$module.querySelector('.app-c-map__markers-list')
179+
const popupsListEl = this.$module.querySelector('.js-list-markers ol')
180+
181+
if (popupsListWrapper && popupsListEl) {
182+
popupsListWrapper.classList.add('app-c-map__markers-list--visible')
183+
const popupsList = []
184+
this.markers.forEach(marker => {
185+
popupsList.push(this.createPopupContent(marker))
186+
})
187+
popupsList.forEach(popup => {
188+
const listItem = document.createElement('li')
189+
listItem.innerHTML = popup
190+
popupsListEl.appendChild(listItem)
191+
})
192+
}
193+
}
194+
}
195+
Modules.Map = Map
196+
})(window.GOVUK.Modules)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//= require @babel/runtime/helpers/objectWithoutProperties.js

app/views/components/_map.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
<% end %>
6565

6666
<% unless @include_script[:script_included] %>
67-
<%= javascript_include_tag "components/map.js", integrity: false, type: "module" %>
67+
<%= javascript_importmap_tags %>
68+
<%= javascript_import_module_tag "components/map/map" %>
6869
<% @include_script[:script_included] = true %>
6970
<% end %>
7071

config/importmap.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22

33
# Pin the default entry point required by javascript_importmap_tags
44
pin "application", preload: true
5+
pin "components/map/map", preload: true
6+
pin "components/map/index", preload: true
7+
pin "components/map/im-core", preload: true
8+
pin "components/map/im-shell", preload: true
9+
pin "components/map/maplibre", preload: true
510

6-
pin "@defra/interactive-map", to: "components/map/defra.js"
11+
pin "@defra/interactive-map", to: "components/map/index.js"
712
pin "@defra/interactive-map/providers/maplibre", to: "components/map/maplibre.js"
813
# pin "@defra/interactive-map/plugins/interact", to: "./node_modules/@defra/interactive-map/plugins/interact/dist/esm/index.js"
9-
pin "components/map", to: "components/map.js"
14+
15+
pin "components/map/asyncToGenerator", preload: true
16+
pin "components/map/defineProperty", preload: true
17+
pin "components/map/objectWithoutProperties", preload: true
1018

1119
# try to avoid errors relating to babel, which is referenced by the defra ESM code
12-
pin "@babel/runtime/helpers/asyncToGenerator", to: "@babel/runtime/helpers/asyncToGenerator.js"
13-
pin "@babel/runtime/helpers/defineProperty", to: "@babel/runtime/helpers/defineProperty.js"
14-
pin "@babel/runtime/helpers/objectWithoutProperties", to: "@babel/runtime/helpers/objectWithoutProperties.js"
20+
pin "@babel/runtime/helpers/asyncToGenerator", to: "components/map/asyncToGenerator.js"
21+
pin "@babel/runtime/helpers/defineProperty", to: "components/map/defineProperty.js"
22+
pin "@babel/runtime/helpers/objectWithoutProperties", to: "components/map/objectWithoutProperties.js"

config/initializers/assets.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
Rails.application.config.assets.paths << Rails.root.join("node_modules")
99

1010
# Precompile map.js independently so it can be fetched directly by the importmap
11-
Rails.application.config.assets.precompile += %w( components/map.js )
12-
Rails.application.config.assets.precompile += %w( components/map/defra.js )
11+
Rails.application.config.assets.precompile += %w( components/map/map.js )
12+
13+
Rails.application.config.assets.precompile += %w( components/map/index.js )
14+
Rails.application.config.assets.precompile += %w( components/map/im-core.js )
15+
Rails.application.config.assets.precompile += %w( components/map/im-shell.js )
1316
Rails.application.config.assets.precompile += %w( components/map/maplibre.js )
14-
Rails.application.config.assets.precompile += %w( @babel/runtime/helpers/asyncToGenerator.js )
15-
Rails.application.config.assets.precompile += %w( @babel/runtime/helpers/defineProperty.js )
16-
Rails.application.config.assets.precompile += %w( @babel/runtime/helpers/objectWithoutProperties.js )
17+
18+
Rails.application.config.assets.precompile += %w( components/map/asyncToGenerator.js )
19+
Rails.application.config.assets.precompile += %w( components/map/defineProperty.js )
20+
Rails.application.config.assets.precompile += %w( components/map/objectWithoutProperties.js )

0 commit comments

Comments
 (0)