Vue 3 Integration (Composition API)

Integrating Strelit UI Kit with Vue 3 is clean and natural using <script setup> and Vue's built-in <Teleport> or programmatic createApp() mounting.

1. Installation

Install the Strelit UI Kit package in your Vue 3 project:

npm install strelit-ui-kit

2. Docking Workspace Component (`DockingWorkspace.vue`)

In Vue 3, we can use <Teleport> to project reactive Vue components directly into the DOM containers created by Strelit Layout:

<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive } from 'vue';
import { StrelitLayout, LayoutConfig } from 'strelit-ui-kit';
import 'strelit-ui-kit/dist/css/strelit-base.css';
import 'strelit-ui-kit/dist/css/themes/strelit-dark-theme.css';

import TelemetryViewer from './TelemetryViewer.vue';
import LogStreamViewer from './LogStreamViewer.vue';

interface PortalSlot {
  id: string;
  target: HTMLElement;
  type: 'telemetry' | 'logs';
}

const layoutContainer = ref<HTMLElement | null>(null);
let layoutInstance: StrelitLayout | null = null;
const activeSlots = reactive<PortalSlot[]>([]);

onMounted(() => {
  if (!layoutContainer.value) return;

  const config: LayoutConfig = {
    content: [{
      type: 'row',
      content: [
        {
          type: 'component',
          componentName: 'vuePortal',
          title: 'Live Telemetry',
          componentState: { slotType: 'telemetry', id: 'slot-telemetry' }
        },
        {
          type: 'component',
          componentName: 'vuePortal',
          title: 'Event Stream',
          componentState: { slotType: 'logs', id: 'slot-logs' }
        }
      ]
    }]
  };

  layoutInstance = new StrelitLayout(config, layoutContainer.value);

  layoutInstance.registerComponent('vuePortal', (container, state) => {
    const el = container.getElement()[0] || container.getElement();
    
    activeSlots.push({
      id: state.id,
      target: el,
      type: state.slotType
    });

    container.on('destroy', () => {
      const idx = activeSlots.findIndex(s => s.id === state.id);
      if (idx !== -1) activeSlots.splice(idx, 1);
    });
  });

  layoutInstance.init();

  window.addEventListener('resize', () => layoutInstance?.updateSize());
});

onUnmounted(() => {
  layoutInstance?.destroy();
});
</script>

<template>
  <div class="workspace-viewport">
    <div ref="layoutContainer" class="layout-target"></div>

    <!-- Teleport reactive Vue components into Strelit containers -->
    <Teleport v-for="slot in activeSlots" :key="slot.id" :to="slot.target">
      <TelemetryViewer v-if="slot.type === 'telemetry'" />
      <LogStreamViewer v-else-if="slot.type === 'logs'" />
    </Teleport>
  </div>
</template>

<style scoped>
.workspace-viewport {
  width: 100vw;
  height: 100vh;
  position: relative;
  overflow: hidden;
}
.layout-target {
  width: 100%;
  height: 100%;
}
</style>

Benefits of Vue 3 Teleport Pattern

  • Preserved Reactivity: Pinia stores, Vue 3 ref, and computed properties continue updating without re-initialization.
  • Scoped CSS: Scoped styles in your .vue single-file components work without modification.
  • Clean Destruction: Removing panels from Strelit triggers standard Vue lifecycle cleanup.