I read a lot about how to implementing external files into stencil but nothing worked yet like I want it. The speciality is that stencil runs in a Liferay theme. But I think that shouldn't matter.
I installed the MarkerClustererPlus from google via npm and in the stencil config i copy the min file into an "asset" folder like this:
import { Config } from '@stencil/core';
import { sass } from '@stencil/sass';
import nodePolyfills from 'rollup-plugin-node-polyfills';
export const config: Config = {
namespace: 'gwkp-components',
srcDir: 'src/js/components',
plugins: [
sass({
injectGlobalPaths: [
'build/css/clay/_mixins.scss',
'build/css/fonts/font-awesome/scss/_mixins.scss',
'build/css/fonts/font-awesome/scss/_variables.scss',
'build/css/fonts/font-awesome/scss/_core.scss',
'src/js/components/utils/_stencilvars.scss',
'build/css/fonts/font-awesome/scss/regular.scss',
'build/css/fonts/font-awesome/scss/light.scss',
'build/css/fonts/font-awesome/scss/solid.scss',
'build/css/clay/bootstrap/_functions.scss',
'build/css/clay/bootstrap/_mixins.scss',
'build/css/clay/bootstrap/_variables.scss',
'src/css/gw/_colors.scss',
'src/css/gw/_variables.scss',
'src/css/gw/bem/_includes.scss'
]
})
],
outputTargets: [
{
type: 'www',
buildDir: '.',
dir: 'build/js/components',
serviceWorker: null, // disable service workers
copy: [
{ src: '../../../build/css/fonts/font-awesome/webfonts', dest: 'webfonts' },
{ src: '../../../node_modules/@google/markerclustererplus/dist/markerclustererplus.min.js', dest: 'assets/js/markerclustererplus.min.js' }
]
}
],
rollupPlugins: {
after: [
nodePolyfills()
]
}
};
Now the question arises how to get access to the min.js file? Do I need an import statement and declaring a variable or create a script tag? I tried both in some way but nothing worked so far. I need it only in one component.
At the end I wanna use the following code in the component:
var markerCluster = new MarkerClusterer(this.map, this.markerMap,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
No need to copy node_modules/@google/markerclustererplus/dist/markerclustererplus.min.js
to assets/js/markerclustererplus.min.js
.
In your component's tsx file just import MarkerCluster
import MarkerClusterer from "@google/markerclustererplus";
You'll need to have the google maps javascript library already loaded. As far as I'm aware you'll have to load it via a script tag but this can be done dynamically.
I'm using // @ts-ignore
in my examples but you can install the Google Maps types if you prefer.
The final component should look something like the following:
import { h, Component, Host, Element, Listen } from "@stencil/core";
import MarkerClusterer from "@google/markerclustererplus";
@Component({
tag: "my-component",
styles: `
:host {
position: absolute;
width: 100%;
height: 100%;
}
.map {
height: 100%;
}
`,
shadow: true,
})
export class MyComponent {
@Element() el: HTMLElement;
private googleMapsApiKey = "YOUR_API_KEY_HERE";
private locations = [
{ lat: -31.56391, lng: 147.154312 },
{ lat: -33.718234, lng: 150.363181 },
{ lat: -33.727111, lng: 150.371124 },
{ lat: -33.848588, lng: 151.209834 },
{ lat: -33.851702, lng: 151.216968 },
{ lat: -34.671264, lng: 150.863657 },
{ lat: -35.304724, lng: 148.662905 },
{ lat: -36.817685, lng: 175.699196 },
{ lat: -36.828611, lng: 175.790222 },
{ lat: -37.75, lng: 145.116667 },
{ lat: -37.759859, lng: 145.128708 },
{ lat: -37.765015, lng: 145.133858 },
{ lat: -37.770104, lng: 145.143299 },
{ lat: -37.7737, lng: 145.145187 },
{ lat: -37.774785, lng: 145.137978 },
{ lat: -37.819616, lng: 144.968119 },
{ lat: -38.330766, lng: 144.695692 },
{ lat: -39.927193, lng: 175.053218 },
{ lat: -41.330162, lng: 174.865694 },
{ lat: -42.734358, lng: 147.439506 },
{ lat: -42.734358, lng: 147.501315 },
{ lat: -42.735258, lng: 147.438 },
{ lat: -43.999792, lng: 170.463352 },
];
private _mapEl: HTMLElement;
componentDidLoad() {
if (document.getElementById("maps-script")) {
this.initMap();
return;
}
// Create the script tag, set the appropriate attributes
var script = document.createElement("script");
script.id = "maps-script";
script.src = `https://maps.googleapis.com/maps/api/js?key=${this.googleMapsApiKey}&callback=initMap`;
script.defer = true;
// Attach your callback function to the `window` object
// @ts-ignore
window.initMap = () => this.initMap();
// Append the 'script' element to 'head'
document.head.appendChild(script);
}
initMap() {
// @ts-ignore
const map = new google.maps.Map(this._mapEl, {
zoom: 3,
center: { lat: -28.024, lng: 140.887 },
});
// Create an array of alphabetical characters used to label the markers.
const labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Add some markers to the map.
// Note: The code uses the JavaScript Array.prototype.map() method to
// create an array of markers based on a given "locations" array.
// The map() method here has nothing to do with the Google Maps API.
const markers = this.locations.map((location, i) => {
// @ts-ignore
return new google.maps.Marker({
position: location,
label: labels[i % labels.length],
});
});
// Add a marker clusterer to manage the markers.
const markerCluster = new MarkerClusterer(map, markers, {
imagePath:
"https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m",
});
}
render() {
return (
<Host>
<div class="map" ref={(el) => (this._mapEl = el)}></div>
</Host>
);
}
}
Here is a Working example on webcomponents.dev