Map Dataset
Library for creating, listing, styling, identifying, and grouping map layers. Vue and React packages share the same core (@hungpvq/map-dataset).
- Vue:
@hungpvq/vue-map-dataset - React:
@hungpvq/react-map-dataset
Demo: Vue · React · source in apps/vue/demo-map and apps/react/demo-map.
Installation
Vue
npm install @hungpvq/vue-map-dataset @hungpvq/vue-map-core @hungpvq/map-dataset @hungpvq/map-corePeer packages you also need (already used by typical map apps):
npm install maplibre-gl @mdi/js @jamescoyle/vue-icon @hungpvq/vue-draggable @hungpvq/shared @hungpvq/shared-store @hungpvq/shared-logReact
npm install @hungpvq/react-map-dataset @hungpvq/react-map-core @hungpvq/map-dataset @hungpvq/map-corenpm install maplibre-gl @mdi/js @mdi/react @hungpvq/react-draggable @hungpvq/shared @hungpvq/shared-store @hungpvq/shared-logImport styles once at the app root. Use the full set — shared cores and framework adapters and draggable. Omitting any line leaves map chrome, list/menus/tables, or panels unstyled.
Vue
import '@hungpvq/map-core/style.css';
import '@hungpvq/map-dataset/style.css';
import '@hungpvq/vue-map-core/style.css';
import '@hungpvq/vue-map-dataset/style.css';
import '@hungpvq/vue-draggable/style.css';React
import '@hungpvq/map-core/style.css';
import '@hungpvq/map-dataset/style.css';
import '@hungpvq/react-map-core/style.css';
import '@hungpvq/react-map-dataset/style.css';
import '@hungpvq/react-draggable/style.css';Import paths (breaking major): domain APIs live on subpaths — e.g. createGeoJsonDataset from @hungpvq/map-dataset/geojson, highlight from @hungpvq/map-dataset/highlight, attribute-table from @hungpvq/map-dataset/attribute-table, LIST_VIEW_MENU_* from @hungpvq/map-dataset/menu, LayerSimpleMapboxBuild from @hungpvq/map-dataset/style. Root keeps DatasetService, tree helpers, and shared IDataset types. See Stable API.
Setup
Bootstrap once with installMapApp (theme + built-in UI: legend, opacity, toggle show, Add to group, Export, Attribute table, style editor). Without this step those components do not render. Prefer installMapApp over createDatasetRegistryPlugin() alone.
Vue
import { createApp } from 'vue';
import { createStoreRegistryPlugin } from '@hungpvq/shared-store';
import { installMapApp } from '@hungpvq/vue-map-dataset';
import App from './App.vue';
const app = createApp(App);
app.use(createStoreRegistryPlugin());
installMapApp(app);
app.mount('#app');React
import { installMapApp } from '@hungpvq/react-map-dataset';
installMapApp();Features
- Dataset tree: source, layer, list UI, identify, highlight, data management
- Layer list: show/hide, opacity, delete, drag-and-drop groups
- Reorder in the list: Move up / Move down
- Add to group from the layer context menu
- Custom menus: extra / bottom / prebottom / context menu
- Menu hidden / disabled from layer data or external state (Pinia, React context)
- Custom context-menu UI via
setComponentMenuKey - Identify, style editor, legends
- Export GeoJSON layers as GeoJSON / KML / CSV / Shapefile
- Attribute table for GeoJSON feature properties (click a row to zoom / highlight)
Quick start (Vue)
<template>
<Map :mapId="mapId" @map-loaded="onMapLoaded">
<LayerControl position="top-left" show>
<template #endList="{ mapId }">
<BaseMapCard :mapId="mapId" />
</template>
</LayerControl>
<IdentifyControl position="top-right" />
<HighlightPointer enable-click />
<ComponentManagementControl />
</Map>
</template>
<script setup lang="ts">
import type { MapSimple } from '@hungpvq/map-core';
import { getUUIDv4 } from '@hungpvq/shared';
import { Map, BaseMapCard } from '@hungpvq/vue-map-core';
import {
LayerControl,
IdentifyControl,
ComponentManagementControl,
useMapDataset,
} from '@hungpvq/vue-map-dataset';
import { createRootDataset, createDatasetPartListViewUiComponentBuilder, createMultiMapboxLayerComponent } from '@hungpvq/map-dataset';
import { createDatasetPartGeojsonSourceComponent } from '@hungpvq/map-dataset/geojson';
import { createHighlightPart } from '@hungpvq/map-dataset/highlight';
import { LayerSimpleMapboxBuild } from '@hungpvq/map-dataset/style';
import { ref } from 'vue';
import HighlightPointer from './HighlightPointer.vue'; // app-local shell (bindPointer)
import '@hungpvq/map-core/style.css';
import '@hungpvq/map-dataset/style.css';
import '@hungpvq/vue-map-core/style.css';
import '@hungpvq/vue-map-dataset/style.css';
import '@hungpvq/vue-draggable/style.css';
const mapId = ref(getUUIDv4());
function onMapLoaded(map: MapSimple) {
const { addDataset } = useMapDataset(map.id);
const dataset = createRootDataset('Sample');
const list = createDatasetPartListViewUiComponentBuilder('Hanoi')
.setColor('#ff6b6b')
.build();
const source = createDatasetPartGeojsonSourceComponent('source', {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'Hanoi' },
geometry: { type: 'Point', coordinates: [105.8342, 21.0285] },
},
],
});
const layer = createMultiMapboxLayerComponent('layer', [
new LayerSimpleMapboxBuild().setStyleType('point').setColor(list.color).build(),
]);
dataset.add(source);
dataset.add(list);
dataset.add(layer);
dataset.add(createHighlightPart());
addDataset(dataset);
}
</script>Quick start (React)
import type { MapSimple } from '@hungpvq/map-core';
import { Map, BaseMapCard } from '@hungpvq/react-map-core';
import {
LayerControl,
IdentifyControl,
ComponentManagementControl,
useMapDataset,
} from '@hungpvq/react-map-dataset';
import {
createRootDataset,
createDatasetPartListViewUiComponentBuilder,
} from '@hungpvq/map-dataset';
import { createHighlightPart } from '@hungpvq/map-dataset/highlight';
import { HighlightPointer } from './HighlightPointer'; // app-local shell
import '@hungpvq/map-core/style.css';
import '@hungpvq/map-dataset/style.css';
import '@hungpvq/react-map-core/style.css';
import '@hungpvq/react-map-dataset/style.css';
import '@hungpvq/react-draggable/style.css';
function Page() {
function onMapLoaded(map: MapSimple) {
const { addDataset } = useMapDataset(map.id);
const dataset = createRootDataset('Sample');
dataset.add(
createDatasetPartListViewUiComponentBuilder('Layer').build(),
);
dataset.add(createHighlightPart());
addDataset(dataset);
}
return (
<Map onMapLoaded={onMapLoaded}>
<LayerControl
position="top-left"
show
endList={({ mapId }) => <BaseMapCard mapId={mapId} />}
/>
<IdentifyControl position="top-right" />
<HighlightPointer enableClick />
<ComponentManagementControl />
</Map>
);
}Shorthand for a full GeoJSON layer: createGeoJsonDataset.
Create-layer parses GIS and reprojects CRS in a Web Worker. Vite / native ESM: zero config. Webpack / CDN / static: configureGisWorker({ url }) + @hungpvq/map-dataset/geojson-worker. This monorepo needs worker.format: 'es' (+ nxViteTsPaths on worker.plugins). Install optional GIS peers when using CreateControl — see CreateControl.
Domain src/extra vs adapter extra
| Location | Role | Published? |
|---|---|---|
libs/map-core/map-dataset/src/extra/ | Framework-agnostic helpers (field builders, locales, …) that feed domain entries | No — not a package export; use @hungpvq/map-dataset / @hungpvq/map-dataset/<domain> |
libs/vue|react/map-dataset/src/extra/ | Framework UI for menu actions / condition context (ToggleShow, DatasetMenuButton, …) | Internal only; surfaces via adapter root named exports |
Import builders, identify, menu protocol, create-control GIS APIs from @hungpvq/map-dataset/... — never from adapter extra/ barrels. Do not reintroduce adapter builder / model / services re-exports of domain code.
Layer list drag types (LayerListItem, LayerListTreeNode, LayerListGroupTree, ListViewGroupRef, IListViewUI, IGroupListViewUI) are Stable type exports on @hungpvq/map-dataset — see List UI and LayerControl.
Next
- GIS worker — npm app vs Nx monorepo setup so parse + CRS stay off the main thread
- Create a dataset — tree, source, layer, list UI
- Components — LayerControl, Identify, Highlight, DatasetControl, props / events
- Menus —
createMenuBuilder,setHidden/setDisabled, custom menu component - Events —
toggleShow/changeOpacityon list nodes - Attribute table — feature properties, click to zoom
- Export — GeoJSON / KML / CSV / Shapefile