# Plan Annotations

This example will show you how to create a plugin that use the annotation API to create, edit and delete annotations on a PDF plan.

The plugin will be a button that you can click to add an annotation anywhere on a plan. Once the annotation is added you can drag & drop it to change its position. You can also "select" an annotation by clicking it and delete it by pressing the <Delete> key.

# Demo

# Setup

First lets setup a viewer with a simple configuration and register a custom plugin:

// file: main.js
import makeBIMDataViewer from "@bimdata/viewer";
import PlanAnnotationsPlugin from "./plan-annotations-plugin.js";

const viewer = makeBIMDataViewer({
  api: {
    // ...
  },
});

viewer.registerPlugin(PlanAnnotationsPlugin);

viewer.mount("#app", "plan");

# Create the plugin definition

Next, we'll define our plugin configuration:

// file: plan-annotations-plugin.js
import PlanAnnotationsPluginComponent from "./PlanAnnotationsPlugin.js";

export default {
  name: "planAnnotations",
  component: PlanAnnotationsPluginComponent,
  addToWindows: ["plan"],
  button: {
    position: "right",
    keepOpen: true,
    tooltip: "planAnnotations.tooltip",
    icon: {
      component: "BIMDataIconLocation",
      options: { size: "m" },
    },
  },
  i18n: {
    en: {
      tooltip: "PDF Annotations",
    },
    fr: {
      tooltip: "Annotations PDF",
    },
  },
};

# Create plugin components

Then we'll create the plugin component that will hold the logic:

// file: PdfAnnotationsPlugin.js
import PlanAnnotationComponent from "./PlanAnnotation.vue";

export default {
  data() {
    return {
      index: 0
    };
  },
  render() {
    return null;
  },
  onOpen() {
    const state = this.$viewer.state;
    const context = this.$viewer.localContext;
    context.startAnnotationMode(({ x, y }) => {
      const annotation = state.addAnnotation({
        component: PlanAnnotationComponent,
        props: {
          index: ++this.index,
          moveTo: position => Object.assign(annotation, position),
          remove: () => state.removeAnnotation(annotation),
        },
        x,
        y,
        z: 0,
      });
      context.stopAnnotationMode();
      this.$close();
    });
  },
};

Finally we'll add the component that will materialize the PDF annotation on the plan:

// file: PlanAnnotation.js
export default {
  template: `
    <div
      class="plan-annotation"
      :class="{ grabbing }"
      ref="marker"
      tabindex="0"
      @keyup.delete="remove"
    >
      {{ index }}
    </div>
  `,
  props: {
    localContext: Object,
    index: Number,
    moveTo: Function,
    remove: Function,
  },
  data() {
    return {
      grabbing: false,
    };
  },
  mounted() {
    this.$refs.marker.addEventListener("mousedown", this.onMouseDown);
  },
  beforeUnmount() {
    this.$refs.marker.removeEventListener("mousedown", this.onMouseDown);
  },
  methods: {
    onMouseDown() {
      this.grabbing = true;
      document.addEventListener("mouseup", this.onMouseUp);
      document.addEventListener("mousemove", this.onMouseMove);
    },
    onMouseUp() {
      this.grabbing = false;
      document.removeEventListener("mousemove", this.onMouseMove);
    },
    onMouseMove(event) {
      const engine2d = this.localContext.viewer.viewer;
      const { x: cx, y: cy } = engine2d.canvas.getBoundingClientRect();
      const { x, y } = this.$refs.marker.getBoundingClientRect();

      const { movementX, movementY } = event;

      const position = engine2d.camera.getPosition({
        x: (x - cx) + movementX,
        y: (y - cy) + movementY,
      });

      this.moveTo(position);
    }
  },
};

You can also add the following rules to your page stylesheet:

.plan-annotation {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--color-primary);
  background-color: var(--color-high);
  font-weight: bold;

  display: flex;
  justify-content: center;
  align-items: center;

  user-select: none;
  cursor: grab;
}

.plan-annotation.grabbing {
  cursor: grabbing;
}

.plan-annotation:focus {
  border: 2px solid var(--color-high);
  background-color: var(--color-warning);
}

Notice the use of the BIMData css variables (opens new window) like --color-primary. It allows to stay in tune with the global theme and to track colors that may have been changed when the viewer was initialized.