Skip to content

ContentLayout Component

A flexible layout component for creating consistent page headers with breadcrumb-style titles, action buttons, and body content areas. Perfect for admin panels, dashboards, and content management interfaces.


Table of Contents


Basic Usage

Simple Layout

vue
<template>
  <ContentLayout
    :title="[{ name: 'Dashboard' }]"
  >
    <template #body>
      <p>Your content goes here</p>
    </template>
  </ContentLayout>
</template>

<script setup>
import { ContentLayout } from 'dolphin-components';
</script>

With Action Button

vue
<template>
  <ContentLayout
    :title="[{ name: 'Users' }]"
    :actions="[
      { title: 'Add User', emit: 'onCreate', class: 'btn btn-primary' }
    ]"
    @onCreate="handleCreate"
  >
    <template #body>
      <p>User list content</p>
    </template>
  </ContentLayout>
</template>

<script setup>
import { ContentLayout } from 'dolphin-components';

const handleCreate = () => {
  console.log('Create new user');
};
</script>

Props

title

Type: ContentTitle[]
Default: []
Required: No

An array of title objects that creates a breadcrumb-style navigation header.

ContentTitle Interface:

typescript
interface ContentTitle {
  name: string;           // Display text
  link?: string;          // Optional route link
  count?: number;         // Optional item count badge
}

Examples:

vue
<!-- Single title -->
<ContentLayout :title="[{ name: 'Dashboard' }]" />

<!-- Breadcrumb navigation -->
<ContentLayout 
  :title="[
    { name: 'Home', link: '/' },
    { name: 'Users', link: '/users' },
    { name: 'Edit User' }
  ]"
/>

<!-- With item count -->
<ContentLayout 
  :title="[
    { name: 'Products', count: 150 }
  ]"
/>

actions

Type: ContentAction[]
Default: []
Required: No

An array of action button configurations displayed in the header.

ContentAction Interface:

typescript
interface ContentAction {
  title: string;          // Button text
  emit: string;           // Event name to emit on click
  class?: string;         // Custom CSS classes (default: 'btn btn-primary')
}

Examples:

vue
<!-- Single action -->
<ContentLayout 
  :actions="[
    { title: 'Create', emit: 'onCreate' }
  ]"
  @onCreate="handleCreate"
/>

<!-- Multiple actions with custom classes -->
<ContentLayout 
  :actions="[
    { title: 'Save', emit: 'onSave', class: 'btn btn-primary' },
    { title: 'Cancel', emit: 'onCancel', class: 'btn btn-outline-secondary' },
    { title: 'Delete', emit: 'onDelete', class: 'btn btn-danger' }
  ]"
  @onSave="handleSave"
  @onCancel="handleCancel"
  @onDelete="handleDelete"
/>

bodyClass

Type: string
Default: ""
Required: No

Custom CSS classes to apply to the body content area.

Examples:

vue
<!-- Add padding -->
<ContentLayout body-class="p-6">
  <template #body>
    <p>Padded content</p>
  </template>
</ContentLayout>

<!-- Custom background -->
<ContentLayout body-class="bg-gray-50 rounded-lg">
  <template #body>
    <p>Content with background</p>
  </template>
</ContentLayout>

<!-- Multiple classes -->
<ContentLayout body-class="p-8 bg-white shadow-md rounded-xl">
  <template #body>
    <p>Styled content</p>
  </template>
</ContentLayout>

titleClass

Type: string
Default: ""
Required: No

Custom CSS classes to apply to the title/header area.

Examples:

vue
<!-- Custom background -->
<ContentLayout title-class="bg-blue-50 border-b-2 border-blue-200">
  <template #body>
    <p>Content</p>
  </template>
</ContentLayout>

<!-- Custom padding -->
<ContentLayout title-class="py-6 px-8">
  <template #body>
    <p>Content</p>
  </template>
</ContentLayout>

disableMinHeight

Type: boolean
Default: false
Required: No

Disables the default minimum height constraint on the body content area. Useful for pages that need to be compact or have dynamic heights.

Examples:

vue
<!-- Default: Has minimum height -->
<ContentLayout>
  <template #body>
    <p>Content with minimum height</p>
  </template>
</ContentLayout>

<!-- Disabled: No minimum height -->
<ContentLayout :disable-min-height="true">
  <template #body>
    <p>Compact content without minimum height</p>
  </template>
</ContentLayout>

Slots

body (Default Slot)

The main content area of the layout.

vue
<ContentLayout :title="[{ name: 'Page Title' }]">
  <template #body>
    <!-- Your main content here -->
    <div>
      <h2>Section Title</h2>
      <p>Content goes here</p>
    </div>
  </template>
</ContentLayout>

action-before

Insert custom elements before the action buttons in the header.

vue
<ContentLayout 
  :title="[{ name: 'Products' }]"
  :actions="[{ title: 'Add Product', emit: 'onCreate' }]"
>
  <template #action-before>
    <button class="btn btn-outline-secondary">
      <Icons name="Filter" />
      Filter
    </button>
  </template>
  
  <template #body>
    <p>Product list</p>
  </template>
</ContentLayout>

action-after

Insert custom elements after the action buttons in the header.

vue
<ContentLayout 
  :title="[{ name: 'Settings' }]"
  :actions="[{ title: 'Save', emit: 'onSave' }]"
>
  <template #action-after>
    <button class="btn btn-outline-danger">
      <Icons name="Trash" />
    </button>
  </template>
  
  <template #body>
    <p>Settings content</p>
  </template>
</ContentLayout>

Styling

CSS Classes

The component uses the following CSS classes that you can customize:

css
/* Main container */
.content-div {
  /* Wrapper for entire component */
}

/* Title/Header section */
.content-title-div {
  /* Header container */
  /* Can be customized via titleClass prop */
}

.content-title {
  /* Breadcrumb/title container */
}

/* Action buttons section */
.content-action {
  /* Action buttons container */
}

/* Body content section */
.content-body {
  /* Main content container */
  /* Can be customized via bodyClass prop */
}

.content-body-min-height {
  /* Minimum height class (can be disabled) */
}

Custom Styling Examples

vue
<!-- Custom header styling -->
<ContentLayout
  title-class="bg-gradient-to-r from-blue-500 to-purple-600 text-white py-6"
  :title="[{ name: 'Dashboard' }]"
>
  <template #body>
    <p>Content</p>
  </template>
</ContentLayout>

<!-- Custom body styling -->
<ContentLayout
  :title="[{ name: 'Products' }]"
  body-class="bg-gray-50 p-8 rounded-xl shadow-inner"
>
  <template #body>
    <p>Content</p>
  </template>
</ContentLayout>

<!-- Combined custom styling -->
<ContentLayout
  title-class="border-b-4 border-blue-500 pb-4"
  body-class="p-6 bg-white shadow-lg rounded-lg mt-4"
  :title="[{ name: 'Settings' }]"
>
  <template #body>
    <p>Content</p>
  </template>
</ContentLayout>

Best Practices

1. Use Breadcrumb Navigation for Deep Pages

vue
<!-- Good: Clear navigation hierarchy -->
<ContentLayout
  :title="[
    { name: 'Dashboard', link: '/' },
    { name: 'Products', link: '/products' },
    { name: 'Category', link: '/products/category' },
    { name: 'Edit Product' }
  ]"
>
  <template #body>...</template>
</ContentLayout>

2. Show Item Counts for List Pages

vue
<!-- Good: Shows total count -->
<ContentLayout
  :title="[{ name: 'Users', count: totalUsers }]"
  :actions="[{ title: 'Add User', emit: 'onCreate' }]"
>
  <template #body>...</template>
</ContentLayout>
vue
<!-- Good: Primary action on the right -->
<ContentLayout
  :actions="[
    { title: 'Cancel', emit: 'onCancel', class: 'btn btn-outline-secondary' },
    { title: 'Save', emit: 'onSave', class: 'btn btn-primary' }
  ]"
>
  <template #body>...</template>
</ContentLayout>

4. Use Consistent Action Button Classes

vue
<!-- Good: Semantic button classes -->
<ContentLayout
  :actions="[
    { title: 'Delete', emit: 'onDelete', class: 'btn btn-danger' },
    { title: 'Edit', emit: 'onEdit', class: 'btn btn-primary' }
  ]"
>
  <template #body>...</template>
</ContentLayout>

5. Disable Minimum Height for Compact Sections

vue
<!-- Good: Compact layout for small sections -->
<ContentLayout
  :title="[{ name: 'Quick Stats' }]"
  :disable-min-height="true"
>
  <template #body>
    <div class="flex gap-4">
      <!-- Compact content -->
    </div>
  </template>
</ContentLayout>

6. Use Slots for Complex Action Areas

vue
<!-- Good: Custom actions with search -->
<ContentLayout :title="[{ name: 'Products' }]">
  <template #action-before>
    <input type="text" placeholder="Search..." class="input-text" />
    <button class="btn btn-outline-secondary">Filter</button>
  </template>
  
  <template #action-after>
    <button class="btn btn-primary">Add Product</button>
  </template>
  
  <template #body>...</template>
</ContentLayout>

TypeScript Support

The component is fully typed with TypeScript interfaces:

typescript
interface ContentTitle {
  name: string;
  link?: string;
  count?: number;
}

interface ContentAction {
  title: string;
  emit: string;
  class?: string;
}

interface ContentProps {
  title?: ContentTitle[];
  actions?: ContentAction[];
  bodyClass?: string;
  titleClass?: string;
  disableMinHeight?: boolean;
}

Usage with TypeScript

vue
<script setup lang="ts">
import { ref } from 'vue';
import { ContentLayout, type ContentTitle, type ContentAction } from 'dolphin-components';

const pageTitle = ref<ContentTitle[]>([
  { name: 'Dashboard', link: '/' },
  { name: 'Users', count: 150 }
]);

const pageActions = ref<ContentAction[]>([
  { title: 'Add User', emit: 'onCreate', class: 'btn btn-primary' }
]);

const handleCreate = (): void => {
  console.log('Creating new user');
};
</script>

<template>
  <ContentLayout
    :title="pageTitle"
    :actions="pageActions"
    @onCreate="handleCreate"
  >
    <template #body>
      <p>Content</p>
    </template>
  </ContentLayout>
</template>

Troubleshooting

Title/Breadcrumbs Not Showing

Make sure you're passing the title prop correctly:

vue
<!-- ❌ Wrong -->
<ContentLayout title="Dashboard" />

<!-- ✅ Correct -->
<ContentLayout :title="[{ name: 'Dashboard' }]" />

Actions Not Triggering

Ensure you're listening to the correct event:

vue
<!-- ❌ Wrong: emit name doesn't match event listener -->
<ContentLayout
  :actions="[{ title: 'Save', emit: 'onSave' }]"
  @save="handleSave"
/>

<!-- ✅ Correct: emit matches event listener -->
<ContentLayout
  :actions="[{ title: 'Save', emit: 'onSave' }]"
  @onSave="handleSave"
/>

Make sure Vue Router is installed and configured:

javascript
// main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    // your routes
  ]
});

const app = createApp(App);
app.use(router);
app.mount('#app');

Minimum Height Issue

If you want the body to be more compact:

vue
<!-- Disable minimum height -->
<ContentLayout :disable-min-height="true">
  <template #body>
    <p>Compact content</p>
  </template>
</ContentLayout>