npm install @particulatesolutions/cms-editorThis library includes vue-i18n and provides default translations in German and English. You must set up vue-i18n in your application for the components to display translated text.
- Import and set up vue-i18n with the library's default translations:
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import { defaultTranslations } from '@particulatesolutions/cms-editor'
const i18n = createI18n({
legacy: false,
locale: 'de', // or 'en'
fallbackLocale: 'de',
messages: {
de: {
...defaultTranslations.de,
// Add your other German translations here
},
en: {
...defaultTranslations.en,
// Add your other English translations here
}
}
})
app.use(i18n)- Switching languages dynamically:
import { useI18n } from 'vue-i18n'
const { locale } = useI18n()
// Switch to English
locale.value = 'en'
// Switch to German
locale.value = 'de'You can override any library translation by providing your own values:
const i18n = createI18n({
locale: 'en',
messages: {
en: {
...defaultTranslations.en,
// Override specific library translations
general: {
save: 'Save Changes', // Override 'Save' with 'Save Changes'
unnamed: 'Untitled Item' // Override 'Unnamed' with 'Untitled Item'
},
// Override other sections as needed
contentBlockEdit: {
title: 'Main Heading'
}
}
}
})The library provides translations in German (de) and English (en) organized into the following sections:
- general: Common labels (save, unnamed, dash)
- componentPalette: Component palette labels
- contentBlock: Content block labels
- contentBlockEdit: Content block editing form labels
- buttonTable: Button table labels
- metadataPanel: SEO metadata form labels
- projectExplore: Project explore dialog labels
Refer to the defaultTranslations export for the complete list of available keys and their values.
import {
CMSContentArea,
CMSComponentPalette,
CMSMetadataPanel,
type ContentBlock,
type ComponentType
} from '@particulatesolutions/cms-editor'<script setup lang="ts">
import { ref } from 'vue'
import {
CMSContentArea,
CMSComponentPalette,
type ContentBlock,
type ComponentType
} from '@particulatesolutions/cms-editor'
// Define your component types (typically loaded from API)
const componentTypes = ref<ComponentType[]>([
{ id: '1', name: 'Text Block', type: 'text', icon: 'fa-align-left' },
{ id: '2', name: 'Image Block', type: 'image', icon: 'fa-image' },
{ id: '3', name: 'Projects', type: 'projects', icon: 'fa-list' },
])
// Define your content blocks
const contentBlocks = ref<ContentBlock[]>([
{
id: 'block-1',
name: 'Welcome Section',
componentType: 'text',
order: 0,
settings: {
title: 'Welcome',
text: 'This is a sample block',
positioning: 'image-left',
theme: 'light',
},
},
])
// Handle events emitted by components
function handleAddBlock(block: ContentBlock, index: number) {
contentBlocks.value.splice(index, 0, block)
}
function handleRemoveBlock(blockId: string) {
const index = contentBlocks.value.findIndex(b => b.id === blockId)
if (index !== -1) {
contentBlocks.value.splice(index, 1)
}
}
function handleReorder(fromIndex: number, toIndex: number) {
const [removed] = contentBlocks.value.splice(fromIndex, 1)
contentBlocks.value.splice(toIndex, 0, removed)
}
function handleSaveBlock(blockId: string, formData: any) {
const block = contentBlocks.value.find(b => b.id === blockId)
if (block) {
Object.assign(block.settings, formData)
}
}
</script>
<template>
<div class="cms-editor">
<aside>
<CMSComponentPalette :components="componentTypes" />
</aside>
<main>
<CMSContentArea
:blocks="contentBlocks"
:components="componentTypes"
@add:block="handleAddBlock"
@remove:block="handleRemoveBlock"
@reorder="handleReorder"
@save:block="handleSaveBlock"
/>
</main>
</div>
</template>The library follows a props/events pattern:
- Components accept data via props
- Components communicate changes via emitted events
- Parent application handles all state management and data persistence
- No internal state management library required
This design provides maximum flexibility and allows the parent application to integrate with any backend or state management solution.
The library exports comprehensive TypeScript types for type-safe development:
- ContentBlock - Base interface for all content blocks (union type)
- GenericContentBlock - Standard content blocks with ContentBlockSettings
- ProjectContentBlock - Project listing blocks with ProjectSettings
- ContentBlockSettings - Settings for generic blocks (title, text, imageUrl, positioning, theme, buttons, etc.)
- ProjectSettings - Settings for project listing blocks (filters, sorting, display options)
- ContentBlockFormValues - Form data structure for editing generic blocks
- ContentBlockButton - Button configuration with icon, text, URL, type, and ariaLabel
- ComponentType - Available component types with id, name, type, and icon
- ContentBlockPosition - 'text-left' | 'image-left' | 'text-top' | 'image-top'
- ContentBlockTheme - 'light' | 'dark'
Manage buttons within content blocks with drag-and-drop reordering.
Props:
buttons: ContentBlockButton[]- Array of button objects
Emits:
update:buttons- When buttons are modified
Drag-and-drop palette displaying available block types that can be dragged to the content area.
Props:
components: ComponentType[]- Array of available component types
Main canvas for composing pages. Handles drag-and-drop from the palette and reordering of existing blocks.
Props:
blocks: ContentBlock[]- Array of content blockscomponents: ComponentType[]- Array of available component typesvisible?: boolean- Controls visibility of edit dialogs
Emits:
add:block(block: ContentBlock, index: number)- When a block is added from paletteremove:block(blockId: string)- When a block is removedafter:toggle-visibility(blockId: string, visibilityBefore: boolean, visibilityAfter: boolean)- When block visibility is toggledreorder(fromIndex: number, toIndex: number)- When blocks are reorderedsave:block(blockId: string, formData: ContentBlockFormValues)- When block settings are savedsave:project-settings(blockId: string, settings: ProjectSettings)- When project block settings are saved
Individual block renderer with edit, remove, and visibility toggle controls.
Props:
block: ContentBlock- The content block to renderblockTypeName: string- Display name of the block type
Emits:
edit- When edit button is clickedremove- When remove button is clickedtoggle-visibility- When visibility toggle is clicked
Dialog for editing generic content block settings.
Props:
content-block: ContentBlock- The block being editedvisible: boolean- Controls dialog visibility
Emits:
update:visible- When dialog visibility changessave:block(blockId: string, formData: ContentBlockFormValues)- When form is submitted
SEO metadata form for managing title, description, and Open Graph tags.
Props:
modelValue: any- Current metadata object
Emits:
update:modelValue- When metadata is updated
Dialog for configuring project listing blocks with filters, sorting, and display options.
Props:
content-block: ContentBlock- The project block being editedvisible: boolean- Controls dialog visibility
Emits:
update:visible- When dialog visibility changessave:project-settings(blockId: string, settings: ProjectSettings)- When settings are saved
- Vue 3.5+
- PrimeVue 4.5+
- @primevue/themes 4.5+
- @primevue/forms 4.5+
- @primeuix/themes 2.0+
- @primeuix/styled (latest)
- @vueuse/integrations 14.0+
- zod 4.2+
- sortablejs 1.15+
- vue-i18n 11.0+
- tailwindcss 4.1+ (self-contained, no setup required)
- @tailwindcss/vite 4.1+
- tailwindcss-primeui 0.6+
This library uses Tailwind CSS v4 for utility classes and styling. The compiled CSS bundle (dist/index.css) is fully self-contained and includes all necessary Tailwind utilities used by the components.
Parent applications do NOT need to:
- Install Tailwind CSS
- Configure Tailwind
- Include Tailwind in their build process
Simply import the library's CSS:
import '@particulatesolutions/cms-editor/style.css'The generated CSS is minimal (~2KB gzipped) and includes only the Tailwind utilities actually used by the components, plus component-specific styles.
This package includes a demo app for testing and developing components visually.
# Install dependencies
npm install
# Run demo app with hot reload (http://localhost:5173)
npm run dev
# Build library for distribution
npm run build
# Watch build mode (for local development with file: protocol)
npm run build:watch
# Run unit tests
npm run test:unit
# Type checking
npm run type-check
# Lint code
npm run lintDemo vs Library Build:
npm run dev- Runs the demo app (App.vue) with all components showcasednpm run build- Builds only the library (from src/index.ts) to dist/- Demo files (App.vue, main.ts) are never included in the published package
- The library has no internal state management (Pinia) - parent app manages state
MIT