diff --git a/.gitignore b/.gitignore index 10e71d1..5605d67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /node_modules public/build +build/* package-lock.json # Tests @@ -7,3 +8,4 @@ package-lock.json /playwright-report/ /blob-report/ /playwright/.cache/ + diff --git a/README.md b/README.md index e803d71..836b9ed 100644 --- a/README.md +++ b/README.md @@ -21,19 +21,19 @@ Web component created using [lit](https://lit.dev/) 1. Copy and paste the following code inside the `` tag of website where the component should appear, replacing `GOOGLE-API-KEY` with your key ``` - + ``` 1. Optional. To center the map on a favourite farm, replace the latitude and longitude with its `centerLat` and `centerLng` coordinates ``` - + ``` 1. Optional. To override the data source, set the value of `dataSource`, eg ``` - + ``` ## For developers 🤖 diff --git a/dev-utils/dev-build.js b/dev-utils/dev-build.js new file mode 100644 index 0000000..a4e1404 --- /dev/null +++ b/dev-utils/dev-build.js @@ -0,0 +1,49 @@ +import rollupPluginCommonjs from "@rollup/plugin-commonjs"; +import rollupPluginResolve from "@rollup/plugin-node-resolve"; +import rollupPluginReplace from "@rollup/plugin-replace"; +import rollupPluginTerser from "@rollup/plugin-terser"; +import { rollup } from "rollup"; + +const inputOptions = { + input: ["./src/alpaca-map.js"], + plugins: [ + rollupPluginResolve({ + preferBuiltins: true + }), + rollupPluginCommonjs({ + include: /node_modules/ + }), + rollupPluginReplace({ + "process.env.NODE_ENV": JSON.stringify("production"), + preventAssignment: true, + }), + /* + rollupPluginTerser({ + format: { + comments: false + } + }), + */ + ], +}; + +const outputOptionsList = [ + { + preserveModules: true, + dir: './public/build', + // file: "./public/build/alpaca-map.js", + format: "es", + // sourcemap: true, + interop: 'esModule', + }, +]; + +let bundle = await rollup(inputOptions); + +for (const outputOptions of outputOptionsList) { + await bundle.write(outputOptions); +} + +if (bundle) { + await bundle.close(); +} diff --git a/dev-server.js b/dev-utils/dev-server.js similarity index 86% rename from dev-server.js rename to dev-utils/dev-server.js index 6573d16..d5222f5 100644 --- a/dev-server.js +++ b/dev-utils/dev-server.js @@ -8,7 +8,7 @@ const fastify = Fastify({ }); fastify.register(fastifyStatic, { - root: path.join(import.meta.dirname, "./public"), + root: path.join(import.meta.dirname, "../public"), prefix: "/", }); diff --git a/build.js b/dev-utils/pub-build.js similarity index 65% rename from build.js rename to dev-utils/pub-build.js index 2d4ef3c..1ff7198 100644 --- a/build.js +++ b/dev-utils/pub-build.js @@ -1,29 +1,33 @@ import rollupPluginCommonjs from "@rollup/plugin-commonjs"; import rollupPluginResolve from "@rollup/plugin-node-resolve"; import rollupPluginReplace from "@rollup/plugin-replace"; -import rollupPluginTerser from "@rollup/plugin-terser"; import { rollup } from "rollup"; const inputOptions = { input: ["./src/alpaca-map.js"], + external: ['lit'], plugins: [ - rollupPluginResolve({ preferBuiltins: true }), - rollupPluginCommonjs({ include: /node_modules/ }), + rollupPluginResolve({ + exportConditions: ['production'], + preferBuiltins: true + }), + rollupPluginCommonjs({ + include: /node_modules/ + }), rollupPluginReplace({ "process.env.NODE_ENV": JSON.stringify("production"), preventAssignment: true, }), - rollupPluginTerser({ format: { comments: false } }), ], }; const outputOptionsList = [ { - // preserveModules: true, - // dir: './public/build', - file: "./public/build/alpaca-map.js", + preserveModules: true, + entryFileNames: '[name].mjs', + dir: './build/node', format: "es", - sourcemap: true, + interop: 'esModule', }, ]; diff --git a/package.json b/package.json index ad23429..742033d 100644 --- a/package.json +++ b/package.json @@ -2,37 +2,43 @@ "name": "@purplebugs/alpaca-map", "type": "module", "version": "0.0.4", - "main": "src/alpaca-map.js", + "exports": { + ".": "./build/node/src/alpaca-map.mjs" + }, "files": [ "package.json", "README.md", + "build", "src" ], "scripts": { - "build:watch": "node --watch-path=./src build.js", - "build": "node build.js", + "build:publish:node": "node ./dev-utils/pub-build.js", + "build:watch": "node --watch-path=./src ./dev-utils/dev-build.js", + "build": "node ./dev-utils/dev-build.js", "prettier": "prettier . --write", - "start": "node dev-server.js", + "start": "node ./dev-utils/dev-server.js", "test": "node --test", - "test-ui": "playwright test" + "test-ui": "playwright test", + "prepublishOnly": "npm run build:publish:node" }, "author": "", "license": "MIT", "description": "", "devDependencies": { + "@googlemaps/js-api-loader": "1.16.8", + "@googlemaps/markerclusterer": "^2.5.3", "@fastify/static": "7.0.4", "@playwright/test": "^1.45.1", "@rollup/plugin-commonjs": "26.0.1", "@rollup/plugin-node-resolve": "15.2.3", "@rollup/plugin-replace": "5.0.7", "@rollup/plugin-terser": "0.4.4", - "@types/node": "^20.14.10", - "fastify": "4.28.0", - "prettier": "3.3.2", - "rollup": "4.18.0" + "@types/node": "20.14.10", + "fastify": "4.28.1", + "prettier": "3.3.3", + "rollup": "4.21.3" }, "dependencies": { - "@googlemaps/markerclusterer": "^2.5.3", - "lit": "^3.1.4" + "lit": "3.1.4" } } diff --git a/public/index.html b/public/index.html index d218080..4e27f28 100644 --- a/public/index.html +++ b/public/index.html @@ -31,7 +31,7 @@

Alpaca Map web component - index.html page header

--> - + @@ -41,6 +41,6 @@

Alpaca Map web component - index.html page header

- + diff --git a/src/alpaca-map.js b/src/alpaca-map.js index e82c154..e704a36 100644 --- a/src/alpaca-map.js +++ b/src/alpaca-map.js @@ -1,6 +1,8 @@ import { compareExact, compareSparse } from "./utils.js"; import { LitElement, html, css } from "lit"; import { MarkerClusterer } from "@googlemaps/markerclusterer"; +import { Loader } from "@googlemaps/js-api-loader"; + import STYLED_MAP_TYPE from "./styles-map.js"; import stylesGoogle from "./styles-google.js"; import "./alpaca-map-marker.js"; @@ -8,7 +10,7 @@ import "./alpaca-map-icon.js"; export default class AlpacaMap extends LitElement { static properties = { - key: { type: String }, + apiKey: { type: String }, centerLat: { type: Number }, centerLng: { type: Number }, dataSource: { type: String }, @@ -18,8 +20,19 @@ export default class AlpacaMap extends LitElement { stylesGoogle, css` /********* Overall layout *********/ - :host { + background-color: white; + display: inline-block; + border: 1px solid black; + height: 500px; + width: 600px; + + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 85px 1fr 80px; + grid-column-gap: 0px; + grid-row-gap: 0px; + /* Ref: https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design#responsive_typography */ /* font-size: calc(1.5rem + 3vw); */ @@ -55,13 +68,30 @@ export default class AlpacaMap extends LitElement { --public-farm: var(--green); } - .web-component-container { - border: 1px solid black; + /* Custom minimalistic scrollbar */ + ::-webkit-scrollbar { + width: 20px; + } + + ::-webkit-scrollbar-track { + background-color: transparent; + } + + ::-webkit-scrollbar-thumb { + background-color: #d6dee1; + border-radius: 20px; + border: 6px solid transparent; + background-clip: content-box; + } + + ::-webkit-scrollbar-thumb:hover { + background-color: #a8bbbf; } .header-container { background-color: white; - padding: 0.5em; + margin: 0.5em; + overflow: hidden; } .map-container { @@ -119,12 +149,11 @@ export default class AlpacaMap extends LitElement { flex-direction: row; flex-wrap: nowrap; overflow: auto; - - width: 100%; box-sizing: border-box; } .toggle { + flex: 0 0 auto; color: #006ce4; background-color: rgba(0, 108, 228, 0.06); border: solid #006ce4 0.15em; @@ -161,7 +190,7 @@ export default class AlpacaMap extends LitElement { #map { height: 100%; - width: auto; + width: 100%; background-color: var(--pale-blue); } `, @@ -169,81 +198,59 @@ export default class AlpacaMap extends LitElement { constructor() { super(); - this.key; this.map; + this.apiKey = ''; this.centerLat = 60.472; this.centerLng = 8.4689; this.dataSource = "http://localhost:3000/api/companies"; // TODO set default depending on environment this.farms = []; - this.cluster = null; + this.cluster = null; } - // When element is connected to the DOM connectedCallback() is called. - // This is needed in order to know the value of this.key which is passed in from the attribute - connectedCallback() { - super.connectedCallback(); - - // This loads the google scripts. - // We do this here, because at this point we have values from the attributes set on the element - ((g) => { - var h, - a, - k, - p = "The Google Maps JavaScript API", - c = "google", - l = "importLibrary", - q = "__ib__", - m = document, - b = window; - b = b[c] || (b[c] = {}); - var d = b.maps || (b.maps = {}), - r = new Set(), - e = new URLSearchParams(), - u = () => - h || - (h = new Promise(async (f, n) => { - await (a = m.createElement("script")); - e.set("libraries", [...r] + ""); - for (k in g) - e.set( - k.replace(/[A-Z]/g, (t) => "_" + t[0].toLowerCase()), - g[k] - ); - e.set("callback", c + ".maps." + q); - a.src = `https://maps.${c}apis.com/maps/api/js?` + e; - d[q] = f; - a.onerror = () => (h = n(Error(p + " could not load."))); - a.nonce = m.querySelector("script[nonce]")?.nonce || ""; - m.head.append(a); - })); - d[l] - ? console.warn(p + " only loads once. Ignoring:", g) - : (d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n))); - })({ key: this.key, v: "weekly" }); + super.connectedCallback() + + console.log('Using the followig ApiKey', this.apiKey) + + // Ref; https://developers.google.com/maps/documentation/javascript/load-maps-js-api#js-api-loader + const loader = new Loader({ + apiKey: this.apiKey, + version: "weekly", + }); + + loader.load().then(async () => { + await this.initMap(); + }); } + + // When element has rendered markup in the DOM firstUpdated() is called - async firstUpdated() { + async initMap() { async function fetchFarms(dataSource) { - const response = await fetch(dataSource); - const farms = await response.json(); - console.log("farms", farms); + let arr = []; + try { + const response = await fetch(dataSource); + const farms = await response.json(); + // console.log("farms", farms); + + arr = farms?.items || []; + } catch(error) { - return farms?.items || []; + } + return arr; } // Set default location const center = { lat: this.centerLat, lng: this.centerLng }; - console.log("center", center); - - // Load data to populate the map - this.farms = await fetchFarms(this.dataSource); // Import Google Map scripts so we can use them const { Map, InfoWindow } = await google.maps.importLibrary("maps"); const { AdvancedMarkerElement } = await google.maps.importLibrary("marker"); + // Load data to populate the map + this.farms = await fetchFarms(this.dataSource); + // Get the element in the shadow DOM to render the map in const el = this.renderRoot.querySelector("#map"); @@ -320,14 +327,7 @@ export default class AlpacaMap extends LitElement { map: this.map, algorithmOptions: { radius: 100, - /* - Ref: https://www.npmjs.com/package/supercluster - minPoints: 10, - minZoom: 4, - maxZoom: 16, - radius: 100, - maxZoom: 16, - */ + }, }); this.cluster.addMarkers(markers); @@ -372,126 +372,122 @@ export default class AlpacaMap extends LitElement { this.cluster.clearMarkers(); this.cluster.addMarkers(markers); - - console.log("markers.length", markers.length); } render() { return html` -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
- -
+ + + +
+ + `; } }