@asteby/metacore-maps

中文文档
0.2.25发布于 2026/11/04Apache-2.0TypeScriptES Module近 30 天下载 6352

Lazy maps for metacore hosts and addons — MapView, markers, clusters, routes, geofences, LocationPicker, TrackingTimeline and the declarative map/timeline renderers. MapLibre GL loads on demand; no API keys.

原始使用文档
文档目录7 节
  1. Install
  2. Configure the base map (once, in the host)
  3. Components
  4. Accessibility
  5. Declarative views (kernel contract)
  6. Testing
  7. ChoroplethLayer

@asteby/metacore-maps

Lazy maps for the metacore ERP and its addons. One package, three jobs:

  • Components — MapView, Marker, ClusterLayer, Polyline/Route, Geofence, LocationPicker, TrackingTimeline.
  • Declarative renderers — view_type: map and kind: map | timeline detail panels of the kernel's manifest, registered in @asteby/metacore-runtime-react's registry.
  • No lock-in, no keys — MapLibre GL renders; the base map is a style (or PMTiles) URL your host serves. No API key, no third party seeing your customers' locations.

Performance contract (plan §0.2): the package's own initial chunk is ~11 KB gzip (enforced by src/size-budget.test.ts, limit 15 KB) and maplibre-gl is never in a static import graph — it is fetched the first time a map mounts. The registration entry (/register) is under 1 KB, so a host that never shows a map pays almost nothing.

Install

pnpm add @asteby/metacore-maps maplibre-gl
# optional, only for pmtiles:// base maps
pnpm add pmtiles

react, maplibre-gl and (for the renderers) @asteby/metacore-runtime-react, @asteby/metacore-sdk, react-i18next are peers. In a federated page @asteby/metacore-maps and maplibre-gl are lazy shares (METACORE_PAGE_SHARES): the remote never bundles its own copy.

Configure the base map (once, in the host)

import { configureMaps } from '@asteby/metacore-maps'

configureMaps({
  style: {
    light: 'https://cdn.example.com/map/light.json', // style JSON URL, or an inline style object
    dark: 'https://cdn.example.com/map/dark.json',
  },
  // PMTiles (self-hosted, range requests, no key): style sources use `pmtiles://https://…/mx.pmtiles`.
  pmtiles: () => import('pmtiles'),
  attribution: '© OpenStreetMap contributors', // credit for the data in the tiles you serve
  center: [-102.5, 23.6], zoom: 4,             // used when there is nothing to fit
})

Without style the map draws on a plain themed background (your points, routes and zones still render) and makes no network request besides MapLibre itself. Helpers: blankStyle(scheme), rasterStyle(['https://tiles/{z}/{x}/{y}.png'], attribution).

The colour scheme follows the host (.dark class / data-theme="dark" on <html>, then the OS); pass theme="light|dark" to pin it. Layers survive a theme switch.

Components

import { MapView, Route, Geofence, LocationPicker, TrackingTimeline } from '@asteby/metacore-maps'

// Points with popups (focusable markers; clusters automatically above 150 points)
<MapView
  ariaLabel="Envíos activos"
  points={[
    { key: 's1', lat: 19.43, lng: -99.13, label: 'TRK-1', status: 'in_transit', statusLabel: 'En tránsito',
      details: [{ label: 'Paquetería', value: 'DHL' }] },
  ]}
  colors={{ in_transit: '#0072B2' }}
  onSelect={(p) => console.log(p.key)}
>
  {/* layers and extra markers are children */}
  <Route path={[{ lat: 19.4, lng: -99.1 }, { lat: 19.9, lng: -100.2 }]} fit />
  <Geofence zones={[{ key: 'z', label: 'Cobertura', center: { lat: 19.43, lng: -99.13 }, radiusM: 5000 }]} />
</MapView>

// Pick a place: click, drag the pin, type coordinates, device GPS, optional address search
<LocationPicker value={point} onChange={setPoint} geocode={(q) => api.geocode(q)} />

// Shipment tracking — no map required
<TrackingTimeline
  events={[{ status: 'En tránsito', at: '2026-10-17T09:00:00Z', location: { label: 'Querétaro', lat: 20.59, lng: -100.39 } }]}
  timeZone="America/Mexico_City"
/>
ExportWhat it does
MapViewContainer + lazy MapLibre. Props: points, cluster, colors, fit, theme, mapStyle, height, labels, selectedKey, onSelect, onMapClick, listExtra, children.
MarkerFocusable DOM marker (aria-label, glyph, popup children, draggable). A few hundred at most.
ClusterLayerWebGL points with native clustering for thousands of features.
Polyline / RouteLine over ordered points; Route adds numbered stops. Straight segments — no routing service needed.
GeofencePolygons and circles (center + radiusM).
LocationPickerMap + labelled latitude/longitude inputs (the accessible path) + GPS + optional geocoder.
TrackingTimelineStatus / time / place events, with state spelled out in words. Helpers toTrackingEvents, sortTrackingEvents.
MapListThe list equivalent of a map (used by MapView).
configureMaps, loadMaplibre, useMap, useMapSchemeHost config, the shared lazy loader, and hooks for custom layers.
distanceKm, formatDistance, isValidPoint, parseLatLng, directionsUrlRe-exported from @asteby/storefront/geo — the one geo module shared with the storefronts.

Accessibility

  • Every map has an equivalent list ("Ver lista"), keyboard reachable, with each status written out and an "open in map" link. When WebGL or MapLibre are unavailable the list is the only view and a message says so.
  • Markers are buttons with aria-label; colour is never the only channel (status text, glyphs); popups are built from text nodes (no HTML injection).
  • prefers-reduced-motion makes fitBounds / easeTo instant. Strings are overridable through labels (and metacore.maps.* keys in the renderers).

Declarative views (kernel contract)

// host start-up — tiny and MapLibre-free
import { registerMapRenderers } from '@asteby/metacore-maps/register'

registerMapRenderers({ config: { style: { light: '…', dark: '…' } } })

This calls registerViewRenderer('map', …), registerPanelRenderer('map', …) and registerPanelRenderer('timeline', …) with lazy loaders (/view, /panel, /timeline-panel). Pass { timeline: false } to keep the runtime's built-in timeline panel. Manifest fragments it renders:

{ "view_type": "map",
  "view": { "map": { "point": { "lat": "dest_lat", "lng": "dest_lng" }, "color_by": "status",
                      "popup": ["tracking_number", "carrier", "status"], "cluster": true,
                      "layers": [{ "kind": "points", "model": "Branch", "point": { "lat": "lat", "lng": "lng" }, "label": "name" },
                                 { "kind": "zones",  "model": "Zone",   "geometry": "area", "label": "name" }] } } }

{ "kind": "map",      "map": { "point": {…}, "route": { "relation": "stops", "point": {…}, "order_by": "seq" } } }
{ "kind": "timeline", "timeline": { "date_field": "at", "title_field": "description", "status_field": "status",
                                    "source": { "capability": "commerce.shipping.track", "input": { "tracking_number": "record.tracking_number" }, "items": "events" } } }

Rows become features through the same pure helpers the runtime uses for its list fallback (@asteby/metacore-runtime-react/views/map), so both renderers read the manifest identically. Option colours and labels of the color_by column come from the model metadata; renderers display what the server returns and never compute business values. Timeline events pick up location/city and lat/lng columns when present.

Testing

jsdom/happy-dom have no WebGL. In your own tests pass a stub through the loader:

configureMaps({ loadMaplibre: async () => myStub, supportsWebGL: () => true })

ChoroplethLayer

<MapView points={[]}>
  <ChoroplethLayer loader={() => import('./estados.topo.json')} values={{ JAL: 120, CMX: 340 }} keyProperty="code" labelProperty="name" onRegions={setState} />
</MapView>
<ChoroplethList regions={state.regions} label="Ventas por estado" noData="Sin datos" />

The geometry (GeoJSON or TopoJSON) comes from the host/region through loader, which is called once, when the layer mounts. The SDK ships no shapes. ChoroplethList is the accessible equivalent (every region with its value).

版本与发布记录

@asteby/metacore-maps 共发布 35 个版本,首次发布于 2026/10/18;过去 12 个月发布了 35 个版本。

版本发布日期距今
0.2.25latest2026/11/043 天前
0.2.242026/11/035 天前
0.2.232026/11/026 天前
0.2.222026/11/026 天前
0.2.212026/11/016 天前
0.2.202026/11/016 天前

关于 @asteby/metacore-maps 的常见问题

@asteby/metacore-maps 是什么?

Lazy maps for metacore hosts and addons — MapView, markers, clusters, routes, geofences, LocationPicker, TrackingTimeline and the declarative map/timeline renderers. MapLibre GL loads on demand; no API keys.

如何安装 @asteby/metacore-maps?

在项目目录运行 npm install @asteby/metacore-maps,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=18)、react-dom(>=18)、maplibre-gl(>=4)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@asteby/metacore-maps 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@asteby/metacore-maps 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@asteby/metacore-maps 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@asteby/metacore-maps 可以商用吗?

@asteby/metacore-maps 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@asteby/metacore-maps 的下载量有多少?

根据 npm 官方下载统计,@asteby/metacore-maps 在 2026/10/07 – 2026/11/05 这 30 天内被下载 6,352 次,日均约 212 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@asteby/metacore-maps 还在维护吗?

最新版本 0.2.25 发布于 2026/11/04(3 天前);至今共发布 35 个版本,其中过去 12 个月发布了 35 个;首次发布于 2026/10/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。