npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@beespokeio/vector-map

v0.2.2

Published

Vue and JavaScript component for rendering a Portugal vector map by municipalities.

Readme

PortugalMap

Vanilla JavaScript library for rendering an SVG vector map of Portugal by municipalities, using GeoJSON files.

The package is published as @beespokeio/vector-map.

It does not depend on Vue, React, or any other framework. It uses D3 only for geographic projection and SVG path generation.

The Vue wrapper is optional and lives in vue/PortugalMapVue.js. The core js/PortugalMap.js remains framework-agnostic.

Files

js/PortugalMap.js
vue/PortugalMapVue.js
css/portugal-map.css
assets/maps/concelhos_pt_high.geojson
assets/maps/concelhos_pt_low.geojson
assets/maps/distritos_pt_high.geojson
assets/maps/distritos_pt_low.geojson
tests/example1.html
tests/example2.html
tests/example3.html

Installation

In Vue/Vite apps, install the package from this folder or publish it to your registry.

cd map
npm install
npm run build

In a local Vue app, you can install it by path:

npm install /BeeSpoke/components/map

Then import it in the Vue app:

import PortugalMapVue from "@beespokeio/vector-map";
import "@beespokeio/vector-map/style.css";

If you are consuming the local build directly before publishing:

import PortugalMapVue from "/path/to/components/map/dist/portugal-map.js";
import "/path/to/components/map/dist/portugal-map.css";

The build outputs:

dist/portugal-map.js
dist/portugal-map.umd.cjs
dist/portugal-map.css

For the JavaScript core, you can also import:

import { PortugalMap } from "@beespokeio/vector-map";

d3-geo is imported by the package and included in the bundle. You no longer need to load d3-array or d3-geo globally when using the build.

For direct browser usage without a build step, add an import map for d3-geo:

<script type="importmap">
  {
    "imports": {
      "d3-geo": "https://cdn.jsdelivr.net/npm/d3-geo@3/+esm"
    }
  }
</script>

Example

Minimal usage with the included files:

<link rel="stylesheet" href="/css/portugal-map.css">

<div id="mapa-portugal"></div>

<script type="importmap">
  {
    "imports": {
      "d3-geo": "https://cdn.jsdelivr.net/npm/d3-geo@3/+esm"
    }
  }
</script>

<script type="module">
  import PortugalMap from "/js/PortugalMap.js";

  const map = new PortugalMap("#mapa-portugal", {});

  map.render();
</script>

Example with options:

<link rel="stylesheet" href="/css/portugal-map.css">

<div id="mapa-portugal"></div>

<script type="importmap">
  {
    "imports": {
      "d3-geo": "https://cdn.jsdelivr.net/npm/d3-geo@3/+esm"
    }
  }
</script>

<script type="module">
  import PortugalMap from "/js/PortugalMap.js";

  const map = new PortugalMap("#mapa-portugal", {
    resolution: "high",
    idField: "DICO",
    labelField: "Concelho",
    selectedId: "1820",
    highlightedIds: ["0101", "1106"],
    showIslands: true,
    allowZoom: true,
    zoomLvl: 3,
    showTooltip: true,
    values: {
      "1820": {
        label: "TABUAÇO",
        value: 10,
        color: "#008fbd"
      }
    },
    onSelect(item) {
      console.log("Selected:", item);
    }
  });

  map.render();
</script>

Constructor

const map = new PortugalMap(container, options);

container can be a CSS selector or a DOM element:

new PortugalMap("#mapa-portugal", options);
new PortugalMap(document.querySelector("#mapa-portugal"), options);

Options

| Property | Type | Default | Description | | --- | --- | --- | --- | | mapUrl | string \| null | null | Manual URL for the GeoJSON file. When set, it takes priority over resolution and mapUrls. | | mapUrls | object | see below | GeoJSON file URLs by resolution. | | resolution | "high" \| "low" | "high" | GeoJSON resolution to load when mapUrl is not set. | | districtMode | boolean | false | Switches the built-in defaults from municipality data to district/area data. It defaults to DI / Distrito and the bundled district GeoJSON files unless you override them explicitly. | | idField | string | "DICO" | GeoJSON property name used as the municipality ID. | | labelField | string | "Concelho" | GeoJSON property name used as the municipality label. | | selectedId | string \| null | null | Initially selected municipality ID. | | highlightedIds | array | [] | List of highlighted IDs. | | disabledIds | array | [] | IDs that stay visible but cannot be selected or keyboard-activated. | | values | object | {} | External data associated by municipality ID. It can define label, labelHtml, value, and color. | | legends | array | [] | Legend entries rendered inside the map. Each item accepts { color, label }. | | legendTitle | string | "" | Optional title shown above the legend items. | | legendPosition | "bottom-left" \| "bottom-right" \| "top-left" \| "top-right" | "bottom-left" | Legend corner position inside the map. | | showTooltip | boolean | false | Shows a tooltip with the municipality name on hover. | | tooltipClassName | string | "portugal-map__tooltip" | CSS class applied to the tooltip element. | | width | number | 600 | SVG viewBox width. | | height | number | 800 | SVG viewBox height. | | allowZoom | boolean | false | When true, centers the map on selectedId and applies zoom. | | zoomOnInit | boolean | true | When false, does not zoom to the initial selectedId on first render. Later selections still apply zoom. | | allowScrollZoom | boolean | false | Enables mouse wheel zoom over the map. | | allowDragPan | boolean | false | Enables mouse drag panning while zoom is active. | | disableAnimations | boolean | false | Disables animated viewport zoom transitions and uses instant transform updates instead. | | minZoom | number | 1 | Minimum allowed zoom level. | | maxZoom | number | 8 | Maximum allowed zoom level. | | zoomStep | number | 0.5 | Increment used by zoom buttons and mouse wheel. | | zoomLvl | number | 2 | Zoom multiplier used when allowZoom is active. Values lower than 1 are treated as 1. | | showZoomControls | boolean | false | Shows internal +, -, and reset buttons over the map. | | zoomControlsPosition | "bottom-left" \| "bottom-right" \| "top-left" \| "top-right" | "top-right" | Corner position for the internal zoom controls. | | className | string | "portugal-map" | CSS class applied to the SVG. | | defaultFill | string | "#edf3f5" | Fill color for municipalities without a special state. | | disabledFill | string | "#e3eaed" | Fill color used for IDs listed in disabledIds. | | selectedFill | string | "#008fbd" | Fill color for the selected municipality. | | highlightedFill | string | "#cceaf3" | Fill color for highlighted municipalities. | | hoverFill | string | "#d8eef6" | Fill color applied on hover. | | stroke | string | "#aebdc4" | Base municipality stroke color. | | selectedStroke | string \| null | null | Optional stroke color for the selected municipality. When null, stroke is used. | | selectedStrokeWidth | number \| null | null | Optional stroke width for the selected municipality. When null, the normal CSS is used. | | useIslandInsets | boolean | true | When enabled, renders mainland Portugal, the Azores, and Madeira in separate areas, without real geographic distance. | | showIslands | boolean | true | Shows or hides the Azores and Madeira. | | insetLabels | boolean | true | Shows labels in the archipelago insets. | | insetBoxes | object | see below | Defines position, size, and padding for mainland Portugal, the Azores, and Madeira. | | onSelect | function \| null | null | Callback called when a municipality is selected. | | onHover | function \| null | null | Callback called when hovering a municipality. | | onLeave | function \| null | null | Callback called when leaving a municipality. |

GeoJSON Resolution

By default, the widget loads the high-resolution version:

const map = new PortugalMap("#mapa-portugal", {
  resolution: "high"
});

To load the smaller file:

const map = new PortugalMap("#mapa-portugal", {
  resolution: "low"
});

Default URLs:

mapUrls: {
  high: new URL("../assets/maps/concelhos_pt_high.geojson", import.meta.url).href,
  low: new URL("../assets/maps/concelhos_pt_low.geojson", import.meta.url).href
}

This means that, when you import js/PortugalMap.js or the package build, the included GeoJSON files are resolved automatically without passing mapUrls.

You can also pass your own URLs:

const map = new PortugalMap("#mapa-portugal", {
  resolution: "low",
  mapUrls: {
    high: "/assets/maps/concelhos_pt_high.geojson",
    low: "/assets/maps/concelhos_pt_low.geojson"
  }
});

If mapUrl is set, that URL is used directly and resolution is ignored:

const map = new PortugalMap("#mapa-portugal", {
  mapUrl: "/assets/maps/portugal-concelhos.geojson"
});

To render districts or areas by DI instead of municipalities by DICO, use the included district files:

const map = new PortugalMap("#mapa-portugal", {
  mapUrl: new URL("../assets/maps/distritos_pt_high.geojson", import.meta.url).href,
  idField: "DI",
  labelField: "Distrito"
});

Island Insets

By default, the widget moves the Azores and Madeira closer to mainland Portugal in small boxes on the left side.

insetBoxes: {
  continent: {
    x: 220,
    y: 20,
    width: 340,
    height: 740
  },
  azores: {
    x: 20,
    y: 430,
    width: 170,
    height: 125,
    padding: 16
  },
  madeira: {
    x: 20,
    y: 585,
    width: 170,
    height: 90,
    padding: 18,
    shapeOffsetX: 12,
    shapeOffsetY: 0
  }
}

padding moves the shapes away from the box edge. shapeOffsetX and shapeOffsetY let you visually adjust the shapes inside the box.

To hide the islands:

const map = new PortugalMap("#mapa-portugal", {
  showIslands: false
});

When showIslands is false and useIslandInsets is enabled, mainland Portugal automatically uses the central map area instead of the narrower box used while the archipelagos are visible.

Or after rendering:

map.setShowIslands(false);
map.setShowIslands(true);

Values

values associates external data with a municipality by ID.

values: {
  "1820": {
    label: "TABUAÇO",
    labelHtml: "TABUAÇO <strong>10</strong>",
    value: 10,
    color: "#008fbd"
  }
}

Color priority:

  1. values[id].color
  2. selectedFill
  3. highlightedFill
  4. defaultFill

If labelHtml exists, the tooltip renders it as HTML. Otherwise, it uses label as plain text.

Tooltip

To show a tooltip with the municipality name on hover:

const map = new PortugalMap("#mapa-portugal", {
  showTooltip: true
});

The tooltip uses the calculated label for the municipality. If values[id].label exists, it takes priority over the field defined in labelField.

To customize the CSS class:

const map = new PortugalMap("#mapa-portugal", {
  showTooltip: true,
  tooltipClassName: "my-map-tooltip"
});

Zooming To The Selected Municipality

When allowZoom is enabled, the map recenters the municipality defined by selectedId and applies the factor set in zoomLvl.

const map = new PortugalMap("#mapa-portugal", {
  selectedId: "1820",
  allowZoom: true,
  zoomOnInit: false,
  allowScrollZoom: true,
  allowDragPan: true,
  minZoom: 1,
  maxZoom: 6,
  zoomStep: 0.5,
  zoomLvl: 3,
  showZoomControls: true
});

To disable zoom, set allowZoom: false or clear selectedId. To keep the initial municipality selected without zooming on first render, use zoomOnInit: false. For direct interaction, enable allowScrollZoom for the mouse wheel and allowDragPan for dragging the map. Use minZoom, maxZoom, and zoomStep to control zoom limits and pace.

Event Payload

onSelect, onHover, and onLeave receive:

{
  id: "1820",
  label: "TABUAÇO",
  value: 10,
  data: {
    label: "TABUAÇO",
    value: 10,
    color: "#008fbd"
  },
  feature: {}
}

Public Methods

render()

Loads the GeoJSON and draws the map.

await map.render();

setSelected(id)

Updates the selected municipality.

map.setSelected("1820");

setValues(values)

Updates the external data associated with municipalities.

map.setValues({
  "1820": {
    value: 20,
    color: "#008fbd"
  }
});

setHighlightedIds(ids)

Updates the highlighted municipalities.

map.setHighlightedIds(["1820", "1106"]);

setShowIslands(showIslands)

Shows or hides the Azores and Madeira insets.

map.setShowIslands(false);

setShowTooltip(showTooltip)

Shows or hides the hover tooltip.

map.setShowTooltip(true);

setAllowZoom(allowZoom)

Enables or disables zoom centered on the selected municipality.

map.setAllowZoom(true);

setZoomLvl(zoomLvl)

Updates the zoom multiplier.

map.setZoomLvl(4);

setMinZoom(minZoom)

Updates the minimum zoom limit.

map.setMinZoom(1);

setMaxZoom(maxZoom)

Updates the maximum zoom limit.

map.setMaxZoom(6);

setZoomStep(zoomStep)

Updates the increment used by zoomIn(), zoomOut(), and scroll.

map.setZoomStep(0.5);

zoomIn(step)

Increases the current zoom. Automatically enables allowZoom.

map.zoomIn();
map.zoomIn(0.5);

zoomOut(step)

Decreases the current zoom without going below 1. Automatically enables allowZoom.

map.zoomOut();
map.zoomOut(0.5);

resetZoom()

Returns to the base zoom and disables allowZoom.

map.resetZoom();

destroy()

Removes the SVG and clears internal listeners.

map.destroy();

CSS Classes

Each municipality always receives:

portugal-map__region

Additional classes:

is-selected
is-highlighted
has-value

SVG groups receive:

portugal-map__group
portugal-map__group--continent
portugal-map__group--azores
portugal-map__group--madeira

Vue Wrapper

The Vue wrapper is a thin layer over the vanilla core. It only handles the Vue lifecycle, synchronizes props, and converts core callbacks into Vue events.

Import:

import PortugalMapVue from "@beespokeio/vector-map";
import "@beespokeio/vector-map/style.css";

Usage:

<template>
  <PortugalMapVue
    v-model:selected-id="selectedId"
    :highlighted-ids="highlightedIds"
    :values="values"
    :allow-zoom="true"
    :zoom-on-init="false"
    :allow-scroll-zoom="true"
    :allow-drag-pan="true"
    :min-zoom="1"
    :max-zoom="6"
    :zoom-step="0.5"
    :zoom-lvl="3"
    show-zoom-controls
    show-tooltip
    @select="handleSelect"
  />
</template>

Example without v-model:

<template>
  <PortugalMapVue
    :selected-id="selectedId"
    :show-islands="showIslands"
    @select="handleSelect"
    @hover="handleHover"
    @leave="handleLeave"
  />
</template>

Emitted events:

update:selectedId
select
hover
leave
ready
error

Main wrapper props:

mapUrl
mapUrls
resolution
districtMode
idField
labelField
selectedId
highlightedIds
disabledIds
values
legends
legendTitle
legendPosition
showTooltip
showIslands
width
height
allowZoom
zoomOnInit
allowScrollZoom
allowDragPan
disableAnimations
minZoom
maxZoom
zoomStep
zoomLvl
showZoomControls
zoomControlsPosition
defaultFill
disabledFill
selectedFill
highlightedFill
hoverFill
stroke
selectedStroke
selectedStrokeWidth
useIslandInsets
insetLabels
insetBoxes

If you need to control zoom programmatically in the Vue wrapper, use a ref:

<template>
  <PortugalMapVue ref="mapRef" :selected-id="selectedId" :allow-zoom="true" />
  <button @click="mapRef?.zoomIn()">Zoom +</button>
  <button @click="mapRef?.zoomOut()">Zoom -</button>
</template>

The tests/example3.html file shows the Vue wrapper working via CDN/import map.

Manual Tests

The files in tests/ are HTML examples for manual validation. Serve them over HTTP because the component loads GeoJSON files with fetch().

cd /BeeSpoke/components/map
source /home/fernando/.nvm/nvm.sh
nvm use 22
npm install
npm run build
python3 -m http.server 8000

Then open in the browser:

http://localhost:8000/tests/example1.html
http://localhost:8000/tests/example2.html
http://localhost:8000/tests/example3.html

Expected validation:

  • example1.html: the map appears, Tabuaço starts selected, hover shows the tooltip, and clicking updates the text.
  • example2.html: the basic map appears without errors.
  • example3.html: the Vue wrapper appears and the buttons select/clear, hide/show islands, and update zoom.
  • example3.html: the Vue wrapper appears and the buttons select/clear, hide/show islands, and the map shows internal zoom and reset controls.

The examples use CDN/import maps for d3-geo and, for Vue, also for vue, so they require internet access.

Legends And District Mode

You can position the legend with legendPosition, give it a title with legendTitle, and switch the built-in defaults to district data with districtMode:

const map = new PortugalMap("#mapa-portugal", {
  districtMode: true,
  selectedId: "01",
  legends: [
    { color: "#008fbd", label: "Selected" },
    { color: "#cceaf3", label: "Highlighted" }
  ],
  legendTitle: "Legend",
  legendPosition: "bottom-right"
});

Supported legendPosition values:

bottom-left
bottom-right
top-left
top-right

Disabled IDs

You can pass disabledIds to keep some areas visible but non-selectable:

const map = new PortugalMap("#mapa-portugal", {
  disabledIds: ["0303", "1820"]
});

You can also customize their color and disable zoom/reset animations:

const map = new PortugalMap("#mapa-portugal", {
  disabledIds: ["0303", "1820"],
  disabledFill: "#d9e1e5",
  disableAnimations: true
});