Appearance
Vue 3
@jbaysolutions/floorplan-vue is the library as native Vue 3 components. It uses your app's own Vue, so it's the smallest of the three packages. This page covers how the components fit into a Vue app. For every prop, event and method, see the component pages: Editor, 2D viewer, Markers and 3D viewer.
At a glance
| In Vue | |
|---|---|
| Components | FloorPlanEditor, FloorPlanViewer, FloorPlanViewer3D |
| Import from | @jbaysolutions/floorplan-vue; the 3D viewer from @jbaysolutions/floorplan-vue/3d |
| Stylesheet | Import @jbaysolutions/floorplan-vue/style.css once |
| Data in | Props: :plan, :markers, theme, :show-grid… The editor uses v-model |
| Changes out | v-model, v-model:floor, v-model:mode (3D) |
| Events | @plan-click, @marker-move, @change… in kebab-case |
| Methods | On a template ref: viewer.value.focusMarker('ap-1') |
| Custom markers | The #marker slot |
Install
You need Vue 3.4 or newer. three.js 0.160 or newer is an optional peer dependency, needed only by the 3D viewer.
bash
npm install @jbaysolutions/floorplan-vue
# optional, only for FloorPlanViewer3D
npm install threeThe package ships ES modules and CommonJS builds, with types, for both the main entry and /3d.
The stylesheet
The components need the library stylesheet. Import it once, for example in main.ts:
ts
import '@jbaysolutions/floorplan-vue/style.css'The 3D viewer uses the same stylesheet.
Imports
Import the components where you use them:
ts
import { FloorPlanEditor, FloorPlanViewer, createEmptyPlan, normalizePlan } from '@jbaysolutions/floorplan-vue'
import { FloorPlanViewer3D } from '@jbaysolutions/floorplan-vue/3d'The 3D viewer has its own entry, so three.js is only loaded by the pages that import it.
Registering globally
The package's default export is a Vue plugin (also exported as FloorPlanPlugin). It registers FloorPlanEditor and FloorPlanViewer for every template in the app:
ts
import { createApp } from 'vue'
import FloorPlan from '@jbaysolutions/floorplan-vue'
import '@jbaysolutions/floorplan-vue/style.css'
import App from './App.vue'
createApp(App).use(FloorPlan).mount('#app')The 3D viewer is never registered globally, so three.js stays out of your app until you import it.
Props and v-model
The viewers take the plan as a prop. The editor changes the plan, so you bind it with v-model:
vue
<script setup lang="ts">
import { ref } from 'vue'
import { FloorPlanEditor, FloorPlanViewer, createEmptyPlan, type Marker } from '@jbaysolutions/floorplan-vue'
const plan = ref(createEmptyPlan('Head office'))
const markers = ref<Marker[]>([])
const floorId = ref<string | null>(null) // null: start on the ground floor
</script>
<template>
<div style="height: 600px">
<FloorPlanEditor v-model="plan" v-model:floor="floorId" theme="paper" :grid-size="10" />
</div>
<div style="height: 400px">
<FloorPlanViewer :plan="plan" :markers="markers" v-model:floor="floorId" show-grid />
</div>
</template>v-model(editor only) is the plan. The editor emits a new object after every completed change, not on every mouse move. If you don't usev-model, pass:model-valueand listen to@change.v-model:flooris the id of the floor shown. It works on all three components and is optional: use it to start on a particular floor or to keep several components on the same floor. See Multi-floor buildings.v-model:mode(3D viewer only) is the building view:'floor','stacked'or'exploded'.
Props follow the usual Vue rules: camelCase in <script>, kebab-case in templates (:show-grid, markers-draggable, :selected-marker-id). Booleans can be written as bare attributes.
Give the parent a height
Every component fills 100% of its parent's width and height (minimum 240 px). If the parent has no height, the component won't be visible.
Events
Listen with @ and the kebab-case event name:
vue
<FloorPlanViewer
:plan="plan"
:markers="markers"
markers-draggable
@plan-click="onClick"
@marker-click="(marker, event) => (selected = marker.id)"
@marker-move="onMove"
@room-hover="(room) => (hovered = room?.name ?? null)"
/>| Component | Events |
|---|---|
FloorPlanEditor | update:modelValue, change, selection-change, update:floor |
FloorPlanViewer | plan-click, marker-click, marker-drag, marker-move, room-hover, update:floor |
FloorPlanViewer3D | plan-click, marker-click, room-hover, update:floor, update:mode |
Most events send one object, such as { x, y, room, floor, originalEvent } for plan-click. marker-click sends two arguments: the marker and the DOM event. The viewers never change your data: on marker-move, update the marker in your own array.
Try it: click the plan to place a marker, then drag it.
Loading the live demo…
Click the plan to see the event.
Methods: template refs
The components' methods are reached through a template ref. Type the ref with InstanceType to get the methods typed too:
vue
<script setup lang="ts">
import { ref } from 'vue'
import { FloorPlanEditor, FloorPlanViewer } from '@jbaysolutions/floorplan-vue'
const editor = ref<InstanceType<typeof FloorPlanEditor>>()
const viewer = ref<InstanceType<typeof FloorPlanViewer>>()
function showAccessPoint() {
viewer.value?.focusMarker('ap-1') // switches to the marker's floor if needed
}
</script>
<template>
<button @click="editor?.undo()">Undo</button>
<div style="height: 600px"><FloorPlanEditor ref="editor" v-model="plan" /></div>
<div style="height: 400px"><FloorPlanViewer ref="viewer" :plan="plan" :markers="markers" /></div>
</template>A few examples: editor.undo(), editor.addFloor('above'), editor.getPlan(), viewer.fit(), viewer.focus(x, y), viewer.focusRoom(id), viewer3d.setMode('exploded'), viewer3d.snapshot(). The component pages list them all.
Custom markers
Both viewers have a marker slot to draw each marker with your own template:
vue
<FloorPlanViewer :plan="plan" :markers="sensors">
<template #marker="{ marker, selected }">
<div class="sensor" :class="{ selected }">
{{ marker.data.temperature }}°C
</div>
</template>
</FloorPlanViewer>| Component | Slot props |
|---|---|
FloorPlanViewer | { marker, selected, dragging, zoom } |
FloorPlanViewer3D | { marker, selected, occluded } (occluded: hidden behind a wall or slab) |
Your template is placed with its top-left corner on the marker's point. Centre it with transform: translate(-50%, -50%), or lift a pin above the point with translate(-50%, -100%).
The viewers' default slot draws extra content on top of the view, such as your own overlays or buttons. See Markers for the marker fields.
The 3D viewer
Import it from /3d. It takes the same plan and markers as the 2D viewer:
vue
<script setup lang="ts">
import { ref } from 'vue'
import { FloorPlanViewer3D } from '@jbaysolutions/floorplan-vue/3d'
const mode = ref<'floor' | 'stacked' | 'exploded'>('stacked')
</script>
<template>
<div style="height: 600px">
<FloorPlanViewer3D :plan="plan" :markers="markers" v-model:mode="mode" v-model:floor="floorId" @plan-click="onClick" />
</div>
</template>Loading it lazily
To keep three.js out of your main bundle until it's needed, put the 3D view in its own component and load that with defineAsyncComponent:
ts
import { defineAsyncComponent } from 'vue'
const Plan3D = defineAsyncComponent(() => import('./Plan3D.vue')) // Plan3D.vue imports FloorPlanViewer3DYour bundler then splits Plan3D.vue, the 3D viewer and three.js into a separate chunk that loads the first time Plan3D renders. Plan3D.vue is also a good place to call registerPack3D or change THEMES_3D, since both must happen before the 3D viewer mounts.
Add-on packs
Register packs once, before the components first render:
ts
import { registerPack } from '@jbaysolutions/floorplan-vue'
import { registerPack3D } from '@jbaysolutions/floorplan-vue/3d'
import { garden } from '@jbaysolutions/floorplan-pack-garden'
import { garden3D } from '@jbaysolutions/floorplan-pack-garden/3d'
registerPack(garden)
registerPack3D(garden3D) // only if you use the 3D viewerSee Add-on packs.
TypeScript
Types are included. The main entry exports the data model and event types (FloorPlan, Floor, Room, Marker, MarkerVariant, PlanClickEvent, MarkerMoveEvent, Selection, ThemeName…) and the data helpers (createEmptyPlan, normalizePlan, createFloor, formatArea, FURNITURE_CATALOG…, see Data model). The /3d entry adds THEMES_3D, registerPack3D and the pack types.
Props and events are typed on the components themselves, so vue-tsc and the Vue editor extension check your templates. For template refs, use InstanceType<typeof FloorPlanViewer> as shown above.
Not on Vue 3?
Vue 2 apps can't use this package, but they can use the HTML elements, which carry their own Vue 3 inside. React apps have their own package.