grid-plan is a Vue3 component to create blueprints for rooms, datacenters, racks, etc.
Create available component types, and place, resize or delete them from the blueprint.
grid-plan ships with:
- config options to customize the looks of your blueprint
- slots to customize icons related to component types
- slots to create your available components menu and your inventory
npm i grid-plan
Grid Plan requires Three.js as peer dependency.
npm install grid-plan three # or yarn add grid-plan three # or pnpm add grid-plan three
You can declare the component globally in your main.js:
import { createApp } from "vue"; import App from "./App.vue"; import { GridPlan } from "grid-plan"; const app = createApp(App); app.component("GridPlan", GridPlan); app.mount("#app");
Or you can import it directly in your Vue file:
<script setup> import { GridPlan } from "grid-plan"; </script>
Prop name | TS type | Required | Description |
---|---|---|---|
availableTypes | GridPlanItemType[] | YES | The types of components that can be placed on the blueprint |
placedItems | GridPlanItem[] | YES (can be empty) | Components already placed on the blueprint |
readonly | boolean | NO (default: false) | Blueprint will be readonly if true |
config | GridPlanConfig | NO (default config will be applied) | Configuration object to customize looks |
- Script:
import { ref } from "vue"; import { GridPlan } from "grid-plan"; const availableTypes = ref([ { typeId: 1, color: "#3366DD", description: "server", icon: "server", iconColor: "#FFFFFF", depth: 2, // optional item height (defaults to 1 (grid cell size) if not provided) }, { typeId: 2, color: "#DD6633", description: "power", icon: "bolt", iconColor: "#FFFFFF", }, { typeId: 3, color: "#71a4a8", description: "monitor", icon: "deviceLaptop", iconColor: "#1A1A1A", }, ]); const placedItems = ref([ { x: 0, y: 0, h: 1, w: 12, typeId: 1 }, // it's a server component { x: 0, y: 1, h: 4, w: 12, typeId: 3 }, // it's a monitor component ]); // Config is optional // You can provide a partial config, as missing attributes will use defaults const config = ref({ abscissaType: "alphabetic", accordionMenuTitle: "Menu", coordinatesBackground: "#2A2A2A", coordinatesColor: "#8A8A8A", crosshairBackground: "#4A4A4A", fontFamily: "Arial", grid3dPosition: "top", gridFill: "#3A3A3A", gridHeight: 42, gridHighlightColor: "#00FF00", gridStroke: "#1A1A1A", gridStrokeWidth: 0.02, gridWidth: 12, handleFill: "#FFFFFF", handleSize: 0.3, iconColor: "#1A1A1A", nonSelectedOpacity: 0.3, ordinatesType: "numeric", showCrosshair: true, showGrid3d: true, tooltipColor: "#FFFFFF", useAccordionMenu: true, useGradient: true, useShadow: true, showBox: true, boxThickness: 0.3, boxHeight: 1, boxColor: "#5A5A5A", }); // Events function selectType(menuItem) { // Triggered when a menu item is selected console.log("SELECT TYPE", menuItem); } function change(item) { // Triggered whenever an item is changed console.log("CHANGED", item); } function deleteItem(item) { // Triggered whenever an item is deleted console.log("DELETED", item); } function selectItem(item) { // Triggered whenever an item is selected console.log("SELECTED", item); } function createdItem(item) { // Triggered whenever an item is created console.log("CREATED", item); } function unselected() { // Triggered when an item is unselected // Pressing ESC will trigger unselect // Selecting an already selected item will trigger unselect console.log("BLUEPRINT IS NOW UNSELECTED"); }
- Template:
<template> <GridPlan ref="plan" :availableTypes :config :placedItems @change="change" @delete="deleteItem" @select="selectItem" @created="createdItem" @unselect="unselected" @selectType="selectType" > <!-- A slot to do whatever you need before the component --> <template #before="{ items, deleteItem, focusItem, getFocusState, activeEntity }" > <div>ACTIVE ENTITY: {{ activeEntity }}</div> <div v-for="item in items"> {{ item.description }} <button @click="deleteItem(item)">DELETE</button> <button @click="focusItem(item)">DELETE</button> FOCUS STATE: {{ getFocusState(item) }} </div> </template> <!-- Create your available types menu --> <template #availableType="{ availableType }"> <!-- The click event is managed by the component, that will select the type to be used when adding a component to the blueprint --> <button>{{ availableType.description }}</button> </template> <!-- Create your inventory, which will be displayed inside a details element (target the .grid-plan-inventory css class to style) --> <template #inventory="{ item, deleteItem, focusItem, isFocused }"> <div :style="`display: flex; flex-direction:row; flex-wrap: wrap; border: 1px solid ${item.color};width: fit-content; padding: 6px 12px; gap:6px; ${isFocused ? 'background: #FFFFFF20' : ''}`" > <span>{{ item.description }}</span> <span>x:{{ item.x }}</span> <span>y:{{ item.y }}</span> <span>h:{{ item.h }}</span> <span>w:{{ item.w }}</span> <button @click="deleteItem">DELETE</button> <button @click="focusItem"> {{ isFocused ? 'UNFOCUS' : 'FOCUS' }} </button> </div> </template> <!-- Use your own svg icons for component types (not necessary if the icon provided is part of the available icons) --> <template #componentIcon="{ placedItem, maxSize }"> <svg v-if="placedItem.description === 'server'" viewBox="0 0 24 24" stroke-width="1.5" stroke="#FFFFFF" fill="none" stroke-linecap="round" stroke-linejoin="round" :style="`width:${maxSize}px; position: absolute; transform: scale(0.8, 0.8)`" > <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /> <path d="M3 12m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /> <path d="M7 8l0 .01" /> <path d="M7 16l0 .01" /> <path d="M11 8h6" /> <path d="M11 16h6" /> </svg> </template> <!-- Or use this slot to display a single letter or an icon font --> <template #componentText="{ placedItem }"> {{ placedItem.icon }} </template> <!-- A slot to do whatever you need after the component --> <template #after="{ items, deleteItem, focusItem, getFocusState, activeEntity }" > <div>ACTIVE ENTITY: {{ activeEntity }}</div> <div v-for="item in items"> {{ item.description }} <button @click="deleteItem(item)">DELETE</button> <button @click="focusItem(item)">DELETE</button> FOCUS STATE: {{ getFocusState(item) }} </div> </template> </GridPlan> </template>
Attribute | TS type | Default | Description |
---|---|---|---|
abscissaType | "alphabetic" OR "numeric" | "alphabetic" | Display abscissa coordinates as letters or numbers |
accordionMenuTitle | string | "Menu" | Text content of detail summary |
coordinatesBackground | string | "#2A2A2A" | Background color of the coordinates cells |
coordinatesColor | string | "#8A8A8A" | Text color of the coordinates cells |
crosshairBackground | string | "#4A4A4A" | Background color of the crosshair |
fontFamily | string | "Arial" | Font used for all elements in the component |
grid3dPosition | "top" OR "bottom" | "top" | Display 3d blueprint on top or below |
gridFill | string | "#3A3A3A" | Background color of unused blueprint cells |
gridHeight | number | 20 | The height of the blueprint in cell units |
gridHighlightColor | string | "#FFFFFF" | The contour of available cells on hover |
gridStroke | string | "#1A1A1A" | The color of grid lines |
gridWidth | number | 20 | The width of the blueprint in cell units |
handleFill | string | "#FFFFFF" | The color of resize handles |
handleSize | number | 0.3 | The handle size |
iconColor | string | "#1A1A1A" | The text color when using the #componentText slot |
inventoryTitle | string | "Inventory" | The text content of the inventory details summary element |
nonSelectedOpacity | number | 0.3 | The opacity of non selected components when a component is selected |
ordinatesType | "alphabetic" OR "numeric" | "numeric" | Display ordinate coordinates as letters or numbers |
showCrosshair | boolean | true | Show crosshair when hovering available cells |
showGrid3d | boolean | true | Show the 3d blueprint |
showInventory | boolean | true | Show inventory of placed components inside a details HTML element |
tooltipColor | string | "#FFFFFF" | The tooltip text color |
useAccordionMenu | boolean | true | Display the menu inside a details HTML element |
useGradient | boolean | true | Shows components with a subtle gradient |
useShadow | boolean | true | Show selected item with a drop shadow |
showBox | boolean | true | Show a 'wall' around the grid |
boxThickness | number | 0.3 | Box thickness, defaults to 1/3 of a grid cell |
boxHeight | number | 1 | Box height, defaults to the same size as a grid cell |
boxColor | string | "#5A5A5A" | Box color |
Grid Plan does not ship css. To customize the styling of the menu and inventory, target the following css classes:
.grid-plan-main { } .grid-plan-menu { } .grid-plan-menu__summary { } /* If useAccordionMenu is true */ .grid-plan-menu__body { } /* If useAccordionMenu is true */ .grid-plan-inventory { } .grid-plan-inventory__summary { } .grid-plan-inventory__body { } .grid-plan-grid { } .grid-plan-grid-3d { } .grid-plan-grid-3d-label { }
A set of icons is provided by grid-plan. These icons are adapted from the great Tabler icons open source icon library.
Icons are used in availableTypes:
const availableTypes = ref([ { color: '#6376DD', description: 'router', icon: 'router', typeId: 1, iconColor: '#FFFFFF' }, {...} ])
Icon name |
---|
airConditioning |
alertTriangle |
analyze |
archive |
armchair |
award |
bath |
battery |
bed |
bell |
bellSchool |
bolt |
boltOff |
books |
bulb |
bulfOff |
burger |
calculator |
camera |
cctv |
chefHat |
circleKey |
circuitCapacitor |
circuitCell |
circuitGround |
circuitSwitchClosed |
circuitSwitchOpen |
clock |
cloud |
cloudComputing |
coffee |
cpu |
cricuitLoop |
database |
deviceDesktop |
deviceDesktopOff |
deviceDualScreen |
deviceImac |
deviceImacOff |
deviceLaptop |
deviceLaptopOff |
deviceTablet |
deviceTabletOff |
deviceTv |
deviceTvOff |
deviceUsb |
devicesPc |
devicesPcOff |
disabled |
door |
doorEnter |
doorExit |
elevator |
elevatorOff |
escalator |
escalatorDown |
escalatorUp |
fingerprint |
firstAidKit |
folder |
folders |
headphones |
headset |
hexagon |
home |
key |
keyboard |
leaf |
lock |
lockAccess |
man |
microphone |
microscope |
network |
networkOff |
package |
packages |
paperclip |
phone |
plant |
plugConnected |
power |
printer |
printerOff |
prism |
propeller |
propellerOff |
reportAnalytics |
robot |
router |
salad |
server |
serverBolt |
serverCog |
serverOff |
shredder |
sofa |
solarPanel |
soup |
squareKey |
stack |
toilet |
toiletPaper |
toolsKitchen |
trafficCone |
trash |
trolley |
volume |
wall |
washMachine |
wave |
wifi |
windMill |
windmillOff |
window |
world |