Skip to content
Last update: June 24, 2025

VcBlade

VcBlade is a container component that implements the blade pattern for UI organization. Blades are panels that slide in from the right side of the screen and can be stacked to create a navigation hierarchy. They are ideal for displaying details, forms, and related content while maintaining context.

Basic Usage

<template>
  <VcBlade
    title="Product Details"
    icon="material-shopping-cart"
    width="40%"
    :closable="true"
    :toolbar-items="toolbarItems"
    @close="handleClose"
    @expand="handleExpand"
    @collapse="handleCollapse"
  >
    <!-- Blade content here -->
    <div class="tw-p-4">
      Content of the blade goes here
    </div>
  </VcBlade>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { VcBlade, IBladeToolbar } from '@vc-shell/framework';

const toolbarItems = ref<IBladeToolbar[]>([
  { 
    id: 'refresh', 
    icon: 'material-refresh', 
    title: 'Refresh',
    clickHandler: () => refreshData()
  },
  { 
    id: 'add', 
    icon: 'material-add', 
    title: 'Add New',
    clickHandler: () => addNewItem()
  }
]);

function refreshData() {
  // Handle refresh action
}

function addNewItem() {
  // Handle add new item action
}

function handleClose() {
  // Handle blade close
}

function handleExpand() {
  // Handle blade expand
}

function handleCollapse() {
  // Handle blade collapse
}
</script>

Props

Prop Type Default Description
icon string - Icon displayed in the blade header
title string - Title displayed in the blade header
subtitle string - Subtitle displayed in the blade header
width number \| string "30%" Width of the blade (can be in pixels or percentage)
expanded boolean false Whether the blade is expanded to full width. If not provided, defaults to false.
closable boolean true Whether the blade can be closed by the user
toolbarItems IBladeToolbar[] [] Array of toolbar items to display
modified boolean undefined Indicates whether there are unsaved changes in the blade content. Defaults to undefined.

Events

Event Parameters Description
close - Emitted when the close button is clicked
expand - Emitted when the expand button is clicked
collapse - Emitted when the collapse button is clicked

Slots

Slot Name Description
default The main content of the blade
actions Custom actions to display in the blade header
backButton Custom back button for mobile view
widgets Deprecated. Use useWidgets() composable instead. Slot for blade widgets.

Toolbar Items

The toolbarItems prop accepts an array of IBladeToolbar objects with the following properties:

interface IBladeToolbar {
  id: string;
  title: string | Ref<string>;
  icon: string;
  clickHandler?: () => void;
  disabled?: boolean | Ref<boolean>;
  isVisible?: boolean | Ref<boolean>;
}

CSS Variables

The blade component uses CSS variables for theming, which can be customized:

:root {
  --blade-background-color: var(--additional-50);   /* Background color of the blade */
  --blade-color-error: var(--danger-500);           /* Color for error notifications in the blade */
  --blade-color-unsaved-changes: var(--secondary-600); /* Background color for unsaved changes notification */

  --blade-border-color: var(--neutrals-200);        /* Border color for the blade */
  --blade-icon-color: var(--secondary-400);         /* Default icon color in the blade */
  --blade-icon-hover-color: var(--secondary-500);   /* Icon color on hover */

  --blade-widgets-width: 50px;                      /* Width of the widgets sidebar when collapsed */
  --blade-widgets-width-expanded: 120px;            /* Width of the widgets sidebar when expanded */

  --blade-shadow-color: var(--primary-700);         /* Color base for the blade shadow */
  --blade-shadow: 2px 2px 8px rgb(from var(--blade-shadow-color) r g b / 14%); /* Shadow effect for the blade */

  --blade-text-color: var(--additional-50);         /* Text color for blade notifications */

  --blade-widgets-bg-color: var(--neutrals-100);    /* Background color of the widgets area */
  --blade-widgets-more-color: var(--neutrals-600);  /* Color for "more" indicator in widgets area */
}

Integration with Blade Navigation

VcBlade is designed to work with the VC-Shell blade navigation system. When used with the useBladeNavigation composable, it provides a powerful way to navigate between different views:

<script lang="ts" setup>
import { VcBlade, useBladeNavigation } from '@vc-shell/framework';
import ProductEditForm from './ProductEditForm.vue';

const { openBlade } = useBladeNavigation();

function openEditView(productId) {
  openBlade({
    blade: ProductEditForm,
    param: productId,
    onClose() {
      // Handle close event
    }
  });
}
</script>

Mobile Support

VcBlade automatically adapts to mobile screens. On mobile devices:

  • Blades take up the full width of the screen
  • A back button can be shown (provided through the backButton slot)
  • Navigation is simplified with a more app-like experience

Examples

Basic Blade with Toolbar

<template>
  <VcBlade
    title="Products"
    width="50%"
    :toolbar-items="toolbarItems"
    @close="$emit('close')"
  >
    <VcTable
      :columns="columns"
      :items="products"
      @item-click="onProductClick"
    />
  </VcBlade>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { VcBlade, VcTable, IBladeToolbar, useBladeNavigation } from '@vc-shell/framework';
import ProductDetails from './ProductDetails.vue';

const { openBlade } = useBladeNavigation();

const toolbarItems = ref<IBladeToolbar[]>([
  { 
    id: 'refresh', 
    icon: 'material-refresh', 
    title: 'Refresh', 
    clickHandler: () => loadProducts()
  },
  { 
    id: 'add', 
    icon: 'material-add', 
    title: 'Add Product', 
    clickHandler: () => addProduct()
  }
]);

const columns = ref([
  { id: 'name', title: 'Name', sortable: true },
  { id: 'sku', title: 'SKU', sortable: true },
  { id: 'price', title: 'Price', sortable: true }
]);

const products = ref([
  { id: '1', name: 'Product A', sku: 'SKU001', price: 29.99 },
  { id: '2', name: 'Product B', sku: 'SKU002', price: 49.99 },
  { id: '3', name: 'Product C', sku: 'SKU003', price: 19.99 }
]);

function onProductClick(product) {
  openBlade({
    blade: ProductDetails,
    param: product.id
  });
}

function loadProducts() {
  // Reload products
}

function addProduct() {
  openBlade({
    blade: ProductDetails,
    options: { isNew: true }
  });
}
</script>

Blade with Error Message

<template>
  <VcBlade
    title="Product Details"
    width="40%"
    :modified="hasChanges"
    @close="confirmClose"
  >
    <div v-if="error" class="tw-text-red-500 tw-p-4">
      {{ error }}
    </div>

    <VcForm 
      v-if="product" 
      :model="product"
      @update:model="hasChanges = true"
    >
      <!-- Form fields -->
    </VcForm>
  </VcBlade>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { VcBlade, VcForm, usePopup } from '@vc-shell/framework';

const product = ref(null);
const error = ref('');
const hasChanges = ref(false);
const { showConfirmation } = usePopup();

async function loadProduct(id) {
  try {
    // Load product
  } catch (err) {
    error.value = 'Failed to load product: ' + err.message;
  }
}

async function confirmClose() {
  if (hasChanges.value) {
    const confirmed = await showConfirmation('You have unsaved changes. Are you sure you want to close?');
    if (confirmed) {
      closeForm();
    }
  } else {
    closeForm();
  }
}

function closeForm() {
  // Close logic
}
</script>

Blade with Custom Actions

<template>
  <VcBlade
    title="Image Preview"
    width="60%"
    :closable="true"
  >
    <template #actions>
      <VcButton 
        variant="icon" 
        icon="material-download" 
        @click="downloadImage"
      />
      <VcButton 
        variant="icon" 
        icon="material-share" 
        @click="shareImage"
      />
    </template>

    <div class="tw-p-4">
      <VcImage :src="imageUrl" aspect="16x9" />
    </div>
  </VcBlade>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { VcBlade, VcButton, VcImage } from '@vc-shell/framework';

const imageUrl = ref('https://example.com/image.jpg');

function downloadImage() {
  // Download logic
}

function shareImage() {
  // Share logic
}
</script>