Rasterex APINPM Package/docs/components/collaboration/api

API References

Set the embedded viewer user and configure Canvas collaboration rooms using the SDK.

Overview

Use viewer.collaboration to set the embedded viewer user and enable or disable Canvas collaboration configuration.

Collaboration commands are available after the viewer is mounted and Canvas is ready.

  • 1Recommended Order:
  • 21. Wait for mount() and ready().
  • 32. Call viewer.collaboration.setUser(...).
  • 43. Confirm success === true.
  • 54. Call viewer.collaboration.enable(...).
  • 65. Open or activate a document so Canvas can resolve the configured or default room.
typescript
import { createViewer } from "@rasterex/viewer";

const viewer = createViewer({
  container: "#viewer"
});

await viewer.mount();
await viewer.ready();
Overview

Set User

Register user details before joining rooms:

  • setUser(...) sends the user details to Canvas and waits for setUserResult. The Canvas result is not request-ID correlated, so the SDK rejects overlapping setUser(...) calls for one viewer iframe.
typescript
const result = await viewer.collaboration.setUser({
  username: "j.smith",
  displayName: "Jane Smith",
  email: "jane.smith@example.com",
  timeoutMs: 10000
});

if (!result.success) {
  throw new Error(result.reason ?? "Unable to set collaboration user");
}
Set User

Listen For Set User Result

Subscribe to user state events:

  • Most applications can use the setUser(...) promise directly. Use the event when your UI needs a shared collaboration log.
typescript
const unsubscribe = viewer.collaboration.on("setUserResult", (result) => {
  console.log(result.success, result.reason);
});

// To clean up:
unsubscribe();
Listen For Set User Result

Enable & Disable Collaboration

Configure and join document-scoped collaboration rooms. The roomId parameter is optional; if omitted, Canvas will fall back to a default room for the current document context:

  • enable(...) and disable(...) config payloads return void because Canvas does not currently emit a documented collaborationResult event.
typescript
// Enable collaboration (resolves default room automatically)
viewer.collaboration.enable({
  collaborationTooltip: {
    title: "Collaboration",
    messageTemplate: "{senderDisplayName} updated the document",
    duration: 3000,
    position: ["center", 0]
  }
});

// Disable collaboration
viewer.collaboration.disable();
Enable & Disable Collaboration

Configure Directly

Send custom properties shape directly if needed:

  • Prefer enable(...) and disable(...) for simple toggles.
typescript
viewer.collaboration.configure({
  enabled: true,
  canFileOpen: true,
  localStoreAnnotation: false,
  collaborationRoomId: "document_collaboration_room_A1"
});
Configure Directly

Room Fields & Document Context

Canvas joins the configured room internally after collaboration is enabled and document context is available.

If no room is supplied, Canvas uses a default room for the current document. Note that the current SDK does not expose direct room join, leave, create, delete, participant, or presenter commands.

  • Canvas resolves room information from the viewer URL query string or collaboration config. roomId and collaborationRoomId are both supported as config keys.
typescript
// Enable collaboration first
viewer.collaboration.enable({
  roomId: "document_collaboration_room_A1"
});

// Then open the document
await viewer.documents.open({
  url: "https://files.example.com/floor-plan.pdf",
  displayName: "Floor Plan.pdf"
});
Room Fields & Document Context

Tooltip Configuration

Customize notification tooltips displayed during multi-user operations:

  • Supported template parameters are Canvas-defined:
  • - {senderUsername}: Evaluates to the user's username.
  • - {senderDisplayName}: Evaluates to the user's display name.
typescript
viewer.collaboration.enable({
  collaborationTooltip: {
    iconSrc: "/assets/annotation.svg",
    title: "Annotation added",
    messageTemplate: "{senderDisplayName} added an annotation",
    duration: 3000,
    position: ["center", 0]
  }
});
Tooltip Configuration

Types Reference

Typings and payload configuration models:

typescript
type CollaborationUserPayload = {
  username: string;
  displayName?: string;
  email?: string;
};

type SetUserResultPayload = {
  success: boolean;
  reason?: string;
};

type CollaborationTooltipConfig = {
  iconSrc?: string;
  title?: string;
  message?: string;
  messageTemplate?: string;
  duration?: number;
  position?: [number | "center", number];
};

type CollaborationConfigPayload = {
  enabled?: boolean;
  canCollaborate?: boolean;
  roomId?: string;
  collaborationRoomId?: string;
  canFileOpen?: boolean;
  localStoreAnnotation?: boolean;
  dbBackendApiKey?: string;
  collaborationTooltip?: CollaborationTooltipConfig;
  [key: string]: unknown;
};
Types Reference

Validation and Errors

The SDK performs strict validation on collaboration inputs:

  • setUser(...) requires a non-empty string for username.
  • displayName and email must be strings if provided.
  • configure(...), enable(...), and disable(...) require object payloads.
  • enabled and canCollaborate must be booleans if provided.
  • roomId and collaborationRoomId must be non-empty strings if provided.
  • collaborationTooltip.duration must be finite if provided.
  • collaborationTooltip.position must map to [number | "center", number] if provided.
typescript
try {
  viewer.collaboration.enable({
    roomId: "" // Throws validation error at runtime
  });
} catch (err) {
  console.error(err);
}
Validation and Errors

Vanilla Example

Complete integration code setup for Vanilla HTML/JS environments:

<div id="viewer" style="height: 640px"></div>

<div>
  <button type="button" id="enable-collaboration">Enable Collaboration</button>
  <button type="button" id="disable-collaboration">Disable Collaboration</button>
</div>

<p id="status"></p>
Vanilla Example

React Integration

Complete React Component implementation details:

tsx
import { useEffect, useRef, useState } from "react";
import { createViewer, type RasterexViewer } from "@rasterex/viewer";

export function CollaborationPanel() {
  const hostRef = useRef<HTMLDivElement | null>(null);
  const viewerRef = useRef<RasterexViewer | null>(null);
  const [status, setStatus] = useState("Loading viewer");

  useEffect(() => {
    let disposed = false;
    const viewer = createViewer({ container: hostRef.current! });
    viewerRef.current = viewer;

    const unsubscribe = viewer.collaboration.on("setUserResult", (result) => {
      if (!disposed) {
        setStatus(result.success ? "User ready" : `User failed: ${result.reason ?? "unknown"}`);
      }
    });

    async function start() {
      await viewer.mount();
      await viewer.ready();
      if (!disposed) setStatus("Ready");
    }

    void start().catch((error) => {
      if (!disposed) setStatus(error instanceof Error ? error.message : "Viewer failed");
    });

    return () => {
      disposed = true;
      unsubscribe();
      viewer.destroy();
      viewerRef.current = null;
    };
  }, []);

  async function enableCollaboration() {
    try {
      const viewer = viewerRef.current;
      if (!viewer) return;

      const result = await viewer.collaboration.setUser({
        username: "j.smith",
        displayName: "Jane Smith",
        email: "jane.smith@example.com",
        timeoutMs: 10000
      });

      if (!result.success) {
        setStatus(result.reason ?? "Unable to set user");
        return;
      }

      viewer.collaboration.enable({
        roomId: "document_collaboration_room_A1",
        collaborationTooltip: {
          title: "Collaboration",
          messageTemplate: "{senderDisplayName} updated the document",
          duration: 3000,
          position: ["center", 0]
        }
      });

      await viewer.documents.open({
        url: "https://files.example.com/floor-plan.pdf",
        displayName: "Floor Plan.pdf"
      });

      setStatus("Collaboration enabled");
    } catch (error) {
      setStatus(error instanceof Error ? error.message : "Collaboration failed");
    }
  }

  function disableCollaboration() {
    viewerRef.current?.collaboration.disable({
      roomId: "document_collaboration_room_A1"
    });
    setStatus("Collaboration disabled");
  }

  return (
    <section>
      <div ref={hostRef} style={{ height: 640 }} />
      <button type="button" onClick={() => void enableCollaboration()}>
        Enable Collaboration
      </button>
      <button type="button" onClick={disableCollaboration}>
        Disable Collaboration
      </button>
      <p>{status}</p>
    </section>
  );
}
React Integration

Next.js Integration

Ensure the SDK functions client-side inside client component contexts:

tsx
"use client";

import { useEffect, useRef, useState } from "react";
import { createViewer, type RasterexViewer } from "@rasterex/viewer";

export default function CollaborationPage() {
  const hostRef = useRef<HTMLDivElement | null>(null);
  const viewerRef = useRef<RasterexViewer | null>(null);
  const [status, setStatus] = useState("Loading viewer");

  useEffect(() => {
    let disposed = false;
    const viewer = createViewer({ container: hostRef.current! });
    viewerRef.current = viewer;

    async function start() {
      await viewer.mount();
      await viewer.ready();
      if (!disposed) setStatus("Ready");
    }

    void start().catch((error) => {
      if (!disposed) setStatus(error instanceof Error ? error.message : "Viewer failed");
    });

    return () => {
      disposed = true;
      viewer.destroy();
      viewerRef.current = null;
    };
  }, []);

  async function enableCollaboration() {
    const viewer = viewerRef.current;
    if (!viewer) return;

    try {
      const result = await viewer.collaboration.setUser({
        username: "j.smith",
        displayName: "Jane Smith"
      });

      if (!result.success) {
        setStatus(result.reason ?? "Unable to set user");
        return;
      }

      viewer.collaboration.enable({
        roomId: "document_collaboration_room_A1"
      });

      await viewer.documents.open({
        url: "https://files.example.com/floor-plan.pdf",
        displayName: "Floor Plan.pdf"
      });

      setStatus("Collaboration enabled");
    } catch (error) {
      setStatus(error instanceof Error ? error.message : "Collaboration failed");
    }
  }

  return (
    <main>
      <div ref={hostRef} style={{ height: 640 }} />
      <button type="button" onClick={() => void enableCollaboration()}>
        Enable Collaboration
      </button>
      <p>{status}</p>
    </main>
  );
}
Next.js Integration

Angular Integration

Complete Angular component implementation details:

typescript
import {
  AfterViewInit,
  Component,
  ElementRef,
  OnDestroy,
  ViewChild
} from "@angular/core";
import { createViewer, type RasterexViewer } from "@rasterex/viewer";

@Component({
  selector: "app-collaboration-panel",
  template: `
    <div #viewerHost class="viewer-host"></div>
    <button type="button" (click)="enableCollaboration()">Enable Collaboration</button>
    <button type="button" (click)="disableCollaboration()">Disable Collaboration</button>
    <p>{{ status }}</p>
  `,
  styles: [".viewer-host { height: 640px; }"]
})
export class CollaborationPanelComponent implements AfterViewInit, OnDestroy {
  @ViewChild("viewerHost", { static: true })
  private viewerHost!: ElementRef<HTMLDivElement>;

  protected status = "Loading viewer";
  private viewer: RasterexViewer | null = null;
  private cleanup: (() => void) | null = null;

  async ngAfterViewInit() {
    const viewer = createViewer({
      container: this.viewerHost.nativeElement
    });
    this.viewer = viewer;

    this.cleanup = viewer.collaboration.on("setUserResult", (result) => {
      this.status = result.success ? "User ready" : `User failed: ${result.reason ?? "unknown"}`;
    });

    try {
      await viewer.mount();
      await viewer.ready();
      this.status = "Ready";
    } catch (error) {
      this.status = error instanceof Error ? error.message : "Viewer failed";
    }
  }

  async enableCollaboration() {
    if (!this.viewer) return;

    try {
      const result = await this.viewer.collaboration.setUser({
        username: "j.smith",
        displayName: "Jane Smith",
        email: "jane.smith@example.com"
      });

      if (!result.success) {
        this.status = result.reason ?? "Unable to set user";
        return;
      }

      this.viewer.collaboration.enable({
        roomId: "document_collaboration_room_A1"
      });

      await this.viewer.documents.open({
        url: "https://files.example.com/floor-plan.pdf",
        displayName: "Floor Plan.pdf"
      });

      this.status = "Collaboration enabled";
    } catch (error) {
      this.status = error instanceof Error ? error.message : "Collaboration failed";
    }
  }

  disableCollaboration() {
    this.viewer?.collaboration.disable({
      roomId: "document_collaboration_room_A1"
    });
    this.status = "Collaboration disabled";
  }

  ngOnDestroy() {
    this.cleanup?.();
    this.viewer?.destroy();
  }
}
Angular Integration

Canvas Broker Mapping

Broker message mapping for collaboration surface:

  • viewer.collaboration.setUser(...) maps to setUser / setUserResult.
  • viewer.collaboration.on("setUserResult", ...) listens to setUserResult.
  • viewer.collaboration.configure(...) maps to collaboration.
  • viewer.collaboration.enable(...) maps to collaboration with enabled: true.
  • viewer.collaboration.disable(...) maps to collaboration with enabled: false.