Views
A view is a configuration object that defines a projection of the graph onto the canvas: which entities and links reach it, and how they are laid out. Views are data: the package ships none of its own and knows no view name in advance. physical and logical in these examples are ids you chose.
interface AssetView {
id: string
label: string
linkFilter?: (link: AssetLink) => boolean
entityFilter?: (entity: AssetEntity) => boolean
showSublabels?: boolean
layout?: { engine: string; options?: unknown }
hideIsolated?: boolean
}Filtering by layers
Without a linkFilter, a link belongs to a view when its layers contain the view id:
const links: AssetLink[] = [
{ id: 'l1', source: 'router', target: 'laptop', layers: ['physical'] },
{ id: 'l2', source: 'vpn', target: 'laptop', layers: ['logical'] },
{ id: 'l3', source: 'internet', target: 'router', layers: ['physical', 'logical'] },
]l3 is in both views; the other two in one each. One array of links describes several topologies.
Order of application
The order of projection is fixed and observable:
entityFilterpicks candidate entities.linkFilterkeeps the links whose both ends survived step 1.hideIsolateddrops entities left without a single visible link.
hideIsolated only considers links: an entity without a visible incident link is not drawn. Ancestors do not rescue descendants, and descendants do not rescue ancestors — the hierarchy plays no part in projection. It belongs to collapsing, and the filter's keepPathToRoot keeps the map connected during search.
Switching views
<AssetMap v-model:view="view" :entities="entities" :links="links" :views="views" />The built-in switcher appears in the toolbar as soon as there are two views or more. An unknown or missing view id falls back to the first view rather than rendering an empty map, so a value persisted from an older configuration cannot blank the screen.
Switching clears the selection and any traced path. A node selected in one view need not exist in the next, and a selection you cannot see still dims everything around it. With autoFitView on, the canvas refits too — a different view is a different graph, and the old viewport describes a layout that is gone.
Show source
<script setup lang="ts">
import { computed, ref } from 'vue'
import { AssetMap } from 'vue-asset-map'
import type { AssetView } from 'vue-asset-map'
import { projectGraph, buildGraph, resolveView } from 'vue-asset-map/core'
import { dagreLayout } from 'vue-asset-map/layout-dagre'
import { useSiteTheme } from '../theme/useSiteTheme'
import { useSiteLocale } from '../theme/useSiteLocale'
import { entities, links, views as sourceViews, statusColors, kindIcons } from './fixture'
const theme = useSiteTheme()
const locale = useSiteLocale()
// A view can carry its own layout (see the "Views" guide page): copied here so
// each projection is not just a filtered subset of the same picture but visibly
// its own shape. The shared `sourceViews` array is never mutated — each entry
// is spread into a new object.
const views: AssetView[] = sourceViews.map((view) =>
view.id === 'logical'
? { ...view, layout: { engine: dagreLayout, options: { rankdir: 'LR', ranksep: 90 } } }
: { ...view, layout: { engine: 'radial', options: { rootId: 'internet', radius: 170, ringGap: 140 } } },
)
const view = ref('physical')
// The same projection the map runs internally, used here only for the counter.
const graph = buildGraph(entities, links)
const visible = computed(() => {
const result = projectGraph(graph, resolveView(views, view.value))
return { nodes: result.entityIds.size, links: result.linkIds.size }
})
</script>
<template>
<AssetMap
v-model:view="view"
class="demo-map"
:entities="entities"
:links="links"
:views="views"
:theme="theme"
:locale="locale"
:theme-config="{ statusColors, kindIcons }"
auto-fit-view
>
<template #toolbar-right>
<span class="demo-toolbar-button">{{ visible.nodes }} nodes · {{ visible.links }} links</span>
</template>
</AssetMap>
</template>
The counter in that demo is computed by the application, not by the map, using the same core function the map runs internally:
import { buildGraph, projectGraph, resolveView } from 'vue-asset-map/core'
const graph = buildGraph(entities, links)
const result = projectGraph(graph, resolveView(views, 'physical'))
result.entityIds.size
result.linkIds.sizeView layout
const views: AssetView[] = [
{ id: 'physical', label: 'Physical', layout: { engine: 'radial', options: { radius: 240 } } },
{ id: 'logical', label: 'VPN overlay', layout: { engine: 'dagre', options: { rankdir: 'LR' } } },
]The layout prop on the component outranks the view's own, since props sit closer to the consumer.
Filters beyond layers
linkFilter and entityFilter replace the default rule entirely when given:
const criticalOnly: AssetView = {
id: 'critical',
label: 'Critical assets',
entityFilter: (entity) => entity.meta?.severity === 'critical',
hideIsolated: false,
}With hideIsolated: false the survivors stay on the map even where the filter cut every link between them, which is usually what you want for a view built on entity properties rather than on topology.
Sublabels per view
showSublabels: false hides the address lines in a view. Node geometry does not change — the label plate is a fixed size — so switching views does not resize anything or jolt the layout.