中文版本: README.zh.md
A Vue 3 component library for building Camunda 7 BPMN modelers and process viewers with Naive UI. Provides production-ready components for process design, execution monitoring, and workflow management.
- 🎨 BPMN Modeler — Full-featured process designer based on
camunda-bpmn-js - 👁️ Process Viewer — Read-only viewer with execution state highlighting & timeline
- ⚙️ Property Panels — Complete Camunda 7 property editors (forms, scripts, connectors, DMN, etc.)
- 🌓 Theme & i18n — Built-in light/dark themes, Chinese/English with custom locale support
- 🧪 BPMNLint validation — Built-in process validation with per-field feedback and a lint panel
- 🔍 Camunda Form Task — Full form task support (fields, enums, constraints, live preview)
- 📦 Tree-shakable — ESM + UMD builds with TypeScript declarations
- 🔌 Extensible — Slot-based extension for custom property tabs, toolbar buttons, and more
- 🎯 Framework agnostic core — Peer dependencies on Vue 3, Naive UI, @vueuse/core
# npm
npm install @zeng-alt/camunda7-ui naive-ui @vueuse/core vue
# pnpm (recommended)
pnpm add @zeng-alt/camunda7-ui naive-ui @vueuse/core vue
# yarn
yarn add @zeng-alt/camunda7-ui naive-ui @vueuse/core vuePeer dependencies:
vue@>=3.5.13,naive-ui@>=2.44.1,@vueuse/core@>=13.0.0, plus@codemirror/*packages for the script editor
<script setup lang="ts">
import {
CamundaConfigProvider,
BpmnModelerProcess,
BpmnProcessViewer,
} from '@zeng-alt/camunda7-ui'
import '@zeng-alt/camunda7-ui/style.css'
import 'naive-ui/dist/style.css' // or use naive-ui theme provider
</script>
<template>
<CamundaConfigProvider theme="light" locale="en-US">
<!-- BPMN Modeler (editable) -->
<BpmnModelerProcess
:xml="initialXml"
:pro-designer="true"
:auto-stash="true"
@save-xml="handleSave"
/>
<!-- Process Viewer (read-only with execution state) -->
<BpmnProcessViewer
:process-xml="processXml"
:execution-state="executionState"
:show-timeline="true"
/>
</CamundaConfigProvider>
</template>| Component | Description |
|---|---|
CamundaConfigProvider |
Theme, locale, and lookup configuration provider (required wrapper) |
BpmnModelerProcess |
Full-featured BPMN process designer with property panel |
BpmnProcessViewer |
Read-only process viewer with execution state & timeline |
CamundaPropertiesPanel |
Standalone property panel (used internally by Modeler) |
BpmnPreviewModal |
Modal showing a live BPMN preview |
| Base components | Reusable editors: GeneralPanel, FormPanel, FormFieldEditor, FormPreview, ScriptFields, ImplementationExtraFields, IOAssignmentPanel, ExecutionListenersPanel, TaskListenersPanel, FieldInjections, ConnectorFields, DmnFields, ErrorFields, ExternalTaskFields, TimerDefinitionFields, ConditionalDefinitionFields, MessageDefinitionFields, SignalDefinitionFields, EscalationDefinitionFields, CompensationDefinitionFields, LinkDefinitionFields, ExtensionPropertiesPanel, MultiInstanceFields, AsyncCheckboxes, DocumentationPanel, HintTooltip, InMappings/OutMappings, LintPanel, and more |
Required wrapper component that provides theme, locale, and lookup functions to all descendant components.
| Prop | Type | Default | Description |
|---|---|---|---|
theme |
'light' | 'dark' |
'light' |
Global theme |
locale |
string |
'zh-CN' |
Current locale (e.g., zh-CN, en-US) |
localeFallback |
string |
undefined |
Fallback locale when translation missing |
localeMessages |
Record<string, Record<string, any>> |
undefined |
Custom translation overrides |
availableLocales |
LocaleOption[] |
[{label: '中文', value: 'zh-CN'}, {label: 'English', value: 'en-US'}] |
Locale selector options |
lookups |
Partial<CamundaLookups> |
undefined |
Scoped lookup functions (see below) |
| Key | Signature | Used By |
|---|---|---|
searchUsers |
(name: string, pageNo?: number, pageSize?: number) => Promise<PageResult> |
Assignee/candidate pickers |
searchUserGroups |
(name: string) => Promise<CamundaLookupItem[]> |
Candidate group pickers |
fetchProcessList |
() => Promise<ProcessLookupItem[]> |
Call activity, DMN decision refs |
searchJavaClasses |
(name: string) => Promise<CamundaLookupItem[]> |
Service task class picker |
searchDelegateExpressions |
(name: string) => Promise<CamundaLookupItem[]> |
Delegate expression picker |
searchExternalTopics |
(name: string) => Promise<CamundaLookupItem[]> |
External task topic picker |
searchDecisionRefs |
(name: string) => Promise<ProcessLookupItem[]> |
Business rule task DMN picker |
searchFormRefs |
(name: string) => Promise<ProcessLookupItem[]> |
Form reference picker |
searchFormKeys |
(name: string) => Promise<CamundaLookupItem[]> |
Form key picker |
| Slot | Description |
|---|---|
default |
Default slot for child components |
Full-featured BPMN 2.0 process designer with canvas, property panel, toolbar, and mode switching.
| Prop | Type | Default | Description |
|---|---|---|---|
theme |
'light' | 'dark' |
undefined |
Theme (falls back to provider) |
locale |
string |
undefined |
Locale (falls back to provider) |
localeFallback |
string |
undefined |
Fallback locale |
localeMessages |
Record<string, Record<string, any>> |
undefined |
Custom translations |
availableLocales |
LocaleOption[] |
[zh-CN, en-US] |
Locale selector options |
| Prop | Type | Default | Description |
|---|---|---|---|
xml |
string |
undefined |
Initial BPMN XML (auto-imported) |
proDesigner |
boolean |
true |
Professional mode (all nodes/properties) |
showDesignerSwitch |
boolean |
true |
Show Pro/Restricted toggle button |
designerConfig |
DesignerConfig |
undefined |
Restricted mode configuration |
| Prop | Type | Default | Description |
|---|---|---|---|
autoStash |
boolean |
true |
Auto-save XML to localStorage |
stashKey |
string |
'camunda7-ui:stash:xml' |
localStorage key |
size |
'small' | 'medium' | 'large' |
'small' |
Property panel form size |
extraTabLabels |
Record<string, string> |
{} |
Custom labels for extension tabs |
| Prop | Type | Description |
|---|---|---|
onSaveXml |
(xml: string) => void |
Called when user clicks Save |
onSearchUsers |
(name, pageNo?, pageSize?) => PageResult |
Paginated user search |
onSearchUserGroups |
(name) => CamundaLookupItem[] |
User group search |
onFetchProcessList |
() => ProcessLookupItem[] |
Process definitions for call activity/DMN |
onSearchJavaClasses |
(name) => CamundaLookupItem[] |
Java class search |
onSearchDelegateExpressions |
(name) => CamundaLookupItem[] |
Delegate expression search |
onSearchExternalTopics |
(name) => CamundaLookupItem[] |
External task topic search |
onSearchDecisionRefs |
(name) => ProcessLookupItem[] |
DMN decision search |
onSearchFormRefs |
(name) => ProcessLookupItem[] |
Form reference search |
onSearchFormKeys |
(name) => CamundaLookupItem[] |
Form key search |
userResolver |
string |
Expression for resolving assignees (default: 'approverResolver.getUsers') |
groupResolver |
string |
Expression for resolving candidate groups (default: 'approverResolver.getUserGroups') |
| Event | Payload | Description |
|---|---|---|
update:theme |
theme: 'light' | 'dark' |
Theme changed via toolbar |
update:locale |
locale: string |
Locale changed via toolbar |
update:proDesigner |
proDesigner: boolean |
Mode switched via toggle |
All slots are forwarded to the internal BpmnModelerProcessContent.
| Slot | Context | Description |
|---|---|---|
start-event-extra |
— | Extra tabs for Start Event properties |
end-event-extra |
— | Extra tabs for End Event properties |
intermediate-throw-event-extra |
— | Extra tabs for Intermediate Throw Event |
intermediate-catch-event-extra |
— | Extra tabs for Intermediate Catch Event |
task-extra |
— | Extra tabs for Task types (User, Service, Script, etc.) |
gateway-extra |
— | Extra tabs for Gateway properties |
buttons |
— | Custom buttons in ModelerToolbar (right side) |
footer |
— | Custom content in DesignerSwitch footer |
<BpmnModelerProcess :xml="xml" @save-xml="save">
<template #buttons>
<n-button @click="customAction">Custom</n-button>
</template>
<template #task-extra="scope">
<CustomTaskTab v-bind="scope" />
</template>
</BpmnModelerProcess>const modelerRef = ref<InstanceType<typeof BpmnModelerProcess>>()
// Get current process info (XML, name, id, version)
const info = await modelerRef.value?.getProcessInfo()
// info: { xml: string, name: string, id: string, version: string } | null
// Run bpmnlint validation
const result = await modelerRef.value?.validate()
// result: ValidateResult | nullThe component exposes
getProcessInfo()andvalidate()viaexpose().ValidateResultprovidestotal,errors,warnings,infos,reports[], andbyElement(grouped by element id). For lower-level control, use the composables (e.g.useLint) exported from@zeng-alt/camunda7-ui.
Read-only process viewer with execution state visualization (highlighting, badges, timeline).
| Prop | Type | Default | Description |
|---|---|---|---|
theme |
'light' | 'dark' |
undefined |
Theme (falls back to provider) |
locale |
string |
undefined |
Locale (falls back to provider) |
localeFallback |
string |
undefined |
Fallback locale |
localeMessages |
Record<string, Record<string, any>> |
undefined |
Custom translations |
availableLocales |
LocaleOption[] |
[zh-CN, en-US] |
Locale selector options |
| Prop | Type | Default | Description |
|---|---|---|---|
processXml |
string |
'' |
BPMN XML to display |
executionState |
ProcessExecutionState | null |
null |
Execution state for highlighting |
showTimeline |
boolean |
false |
Show right-side timeline panel |
| Prop | Type | Description |
|---|---|---|
onSearchUsers |
(name: string) => any |
Resolve assignee names in tooltips |
onSearchUserGroups |
(name: string) => any |
Resolve candidate group names in tooltips |
| Event | Payload | Description |
|---|---|---|
update:theme |
theme: 'light' | 'dark' |
Theme toggled via toolbar |
update:locale |
locale: string |
Locale changed via toolbar |
| Slot | Description |
|---|---|
default |
Not used (viewer has fixed layout) |
interface ProcessExecutionState {
elements: Record<string, {
status: 'pending' | 'active' | 'completed' | 'rejected'
visitCount: number
rejectCount: number
assignee?: string
candidateUsers?: string[]
candidateGroups?: string[]
}>
}Sequence flow (connection) states are derived automatically from element states and diagram topology — you don't need to provide them. Rules: target active → flow active; source rejected → flow rejected; source completed → flow completed; otherwise pending.
const viewerRef = ref<InstanceType<typeof BpmnProcessViewer>>()
// Programmatic control
viewerRef.value?.zoomIn()
viewerRef.value?.zoomOut()
viewerRef.value?.fitViewport()A live BPMN preview modal built on NavigatedViewer. It auto-sizes the dialog to the diagram content (with a 640×480 minimum, capped at 95vw/95vh), so small diagrams open small and large ones fit the viewport. The diagram uses the default fit-viewport scaling (never zoomed in past 100%).
| Prop | Type | Default | Description |
|---|---|---|---|
xml |
string |
'' |
BPMN XML to preview |
title |
string |
'' |
Modal title (defaults to the built-in "Preview" label) |
theme |
'light' | 'dark' |
undefined |
Theme (falls back to provider) |
locale |
string |
undefined |
Locale (falls back to provider) |
width |
string | number |
800 |
Fallback width when the content size is not known yet |
height |
string | number |
600 |
Fallback height when the content size is not known yet |
| Method | Description |
|---|---|
open(xml?) |
Open the modal and (re)load the given XML, falling back to the xml prop |
close() |
Close the modal |
| Event | Payload | Description |
|---|---|---|
close |
— | Fired when the modal is closed (X / ESC / mask click) |
<script setup lang="ts">
import { ref } from 'vue'
import { BpmnPreviewModal, type ThemeType } from '@zeng-alt/camunda7-ui'
const theme = ref<ThemeType>('dark')
const previewRef = ref<InstanceType<typeof BpmnPreviewModal> | null>(null)
function handlePreview(xml: string) {
previewRef.value?.open(xml)
}
</script>
<template>
<n-button @click="handlePreview(currentXml)">Preview</n-button>
<BpmnPreviewModal ref="previewRef" :theme="theme" />
</template>To preview the current modeler state, grab the XML first:
async function handlePreview(modeler: any) {
const { xml } = await modeler.saveXML({ format: true })
previewRef.value?.open(xml)
}All UI text uses the built-in lightweight i18n system (no vue-i18n required).
import { useCamundaI18n } from '@zeng-alt/camunda7-ui'
const { t } = useCamundaI18n()
t('bpmnPanel.general.name') // 'Name'
t('bpmnPanel.tabs.userTask') // 'User Task'src/locales/zh.json— Chinese (default)src/locales/en.json— English
<CamundaConfigProvider
:locale-messages="{
'en-US': {
bpmnPanel: { general: { name: 'Process Name' } }
}
}"
>
<BpmnModelerProcess />
</CamundaConfigProvider>light— Default light themedark— Dark mode
The library uses UnoCSS with a custom theme. Override CSS variables or extend uno.config.ts:
// uno.config.ts (in your app)
import { defineConfig } from 'unocss'
import presetCamunda7 from '@zeng-alt/camunda7-ui/uno-preset' // if published
export default defineConfig({
presets: [
presetCamunda7,
// your overrides
],
theme: {
colors: {
primary: '#your-brand-color',
dark: '#1a1a2e',
light_border: '#e0e0e0',
dark_border: '#333',
},
},
})The provider applies compact spacing overrides automatically. To customize further:
import { create, NConfigProvider } from 'naive-ui'
const naive = create({
components: {
Button: { /* ... */ },
},
})
<CamundaConfigProvider>
<NConfigProvider :theme-overrides="customOverrides">
<App />
</NConfigProvider>
</CamundaConfigProvider>src/
├── components/
│ ├── config-provider/ # CamundaConfigProvider
│ ├── bpmn-modeler-process/ # BpmnModelerProcess (editor)
│ │ ├── components/ # Toolbar, DesignerSwitch, Dialogs, PreviewModal
│ │ ├── composables/ # useBpmnModeler, useXmlStash, useDiagramActions
│ │ └── features/configurable-nodes/ # Restricted mode palette
│ ├── bpmn-viewer/ # BpmnProcessViewer, NodeTooltip, Legend, TimelinePanel
│ └── bpmn-panel/ # Property panels & base components
│ ├── base/ # Reusable field components (GeneralPanel, FormPanel, etc.)
│ ├── task/ # Task-specific extra fields
│ ├── events/ # Event-specific extra fields
│ ├── subprocess/ # SubProcess / AdHoc / Transaction fields
│ ├── flow/ # Sequence flow fields
│ ├── call-activity/ # Call activity fields
│ ├── gateways/ # Gateway panel
│ ├── swimlanes/ # Pool / Lane / Collaboration
│ ├── data/ # Data store/object references
│ ├── group/ # Group panel
│ ├── association/ text-annotation/
│ └── lint/ # LintPanel, LintFieldFeedback
├── composables/ # Shared composables
│ ├── useBpmnProperties.ts # Property panel helpers
│ ├── useFormSize.ts # Form size scaling
│ ├── useMultiInstance.ts # Multi-instance logic
│ ├── useCamundaLookups.ts # Scoped lookup injection
│ └── useLint.ts / useLintField.ts # bpmnlint integration
├── lint/ # bpmnlint rules + config (camunda7RuleFactories, linterConfig)
├── locales/ # i18n (zh.json, en.json)
├── utils/bpmn/ # BPMN helpers (elementType, uid, getDefinitions)
└── index.ts # Library entry (exports virtual:uno.css + all components)
The modeler ships with built-in bpmnlint validation.
Call validate() on the modeler ref to lint the whole diagram:
const result = await modelerRef.value?.validate()
// result: ValidateResult | null
// { total, errors, warnings, infos, reports: LintReport[], byElement }useLint(getModeler)— subscribes to the modeler'slintingservice, exposingissuesByElement,issuesFor(elementId),lintingActive,refresh()useLintField(getModeler, getBusinessObjectId, fieldPath, localeKeyPrefix?)— maps lint issues to a specific property field, returning NaiveUI{ status, feedback }for inline validation
import { useLint, useLintField, type ValidateResult } from '@zeng-alt/camunda7-ui'Custom rule factories are exported as camunda7RuleFactories, bundled into linterConfig. Configure strictness via LintPanel in the properties panel. The lint config lives in src/lint/ and can be extended in your own build.
# Install dependencies
pnpm install
# Start dev server (playground at http://localhost:5173)
pnpm dev
# Type-check + build library (outputs to dist/)
pnpm build
# Format code (oxfmt: no semicolons, single quotes)
pnpm formatTests are run with Vitest + @vue/test-utils (jsdom). Co-located spec files live in __tests__/ folders next to the code under test and are excluded from the library type-check/build.
# Run all tests once
pnpm test
# Watch mode
pnpm test:watch
# Run tests with coverage report (v8)
pnpm test:coverageCurrent coverage focuses on pure logic and a few representative components:
src/utils/bpmn— element type/icon resolution, template registry,uid/getDefinitionssrc/lint/rules.ts— the customcamunda7/*validation rulessrc/composables/useBpmnProperties— modeler property read/write helperssrc/components/bpmn-panel/base/DocumentationPanel— example component test with mocked modeler
Browser APIs used by components (e.g. matchMedia, ResizeObserver) are stubbed in src/test/setup.ts.
The playground/ directory contains a demo app (main.ts, App.vue) that imports the library via the camunda7-ui alias (resolves to src/index.ts). Use it for manual testing.
- Library entry:
src/index.ts— exportsvirtual:uno.cssside-effect + all components - Build: Vite lib mode →
dist/camunda7-ui.{es,umd}.js,dist/camunda7-ui.css,dist/index.d.ts - Externals (peer deps, not bundled):
vue,naive-ui,@vueuse/core,@codemirror/* - Formatter:
oxfmt— enforces no-semicolon + single-quote style (see.oxfmtrc.json)
pnpm build
cd dist
npm publish --access publicOnly dist/ is published (see files in package.json).
AGPL-3.0-only — see LICENSE for details.
- Fork the repository
- Create a feature branch:
git checkout -b feat/amazing-feature - Commit changes:
git commit -m 'feat: add amazing feature' - Push to branch:
git push origin feat/amazing-feature - Open a Pull Request
Please run pnpm format and pnpm build before submitting.
- 📖 Documentation: GitHub Wiki (TODO)
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
Built on top of amazing open-source projects:
- camunda-bpmn-js — BPMN modeling toolkit
- Naive UI — Vue 3 component library
- UnoCSS — Atomic CSS engine
- VueUse — Composition API utilities