@bpmn-nova/vue 0.3.1-preview
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -0
- package/README.md +192 -0
- package/dist/index.d.ts +167 -0
- package/dist/index.js +287 -0
- package/dist/styles.css +1435 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 BPMN Nova contributors
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# @bpmn-nova/vue
|
|
2
|
+
|
|
3
|
+
BPMN Nova 的 Vue 3.3+ Adapter,提供 Studio、Designer、Viewer、审批轨迹、Properties 和 Expose 实例方法。
|
|
4
|
+
|
|
5
|
+
> **English summary:** Vue 3.3+ components and exposed instance methods for BPMN Nova process design, viewing, approval traces, properties, themes, and SVG export.
|
|
6
|
+
|
|
7
|
+
> 当前版本为 `0.3.1-preview`。Vue 由宿主工程提供,本包不会替应用选择或升级框架版本。
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @bpmn-nova/vue@preview
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import '@bpmn-nova/vue/styles.css'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
只安装本包;`@bpmn-nova/studio` 会作为内部依赖自动解析。容器必须有明确高度。
|
|
22
|
+
|
|
23
|
+
## 流程设计
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<script setup>
|
|
27
|
+
import { ref } from 'vue'
|
|
28
|
+
import { BpmnStudio } from '@bpmn-nova/vue'
|
|
29
|
+
import '@bpmn-nova/vue/styles.css'
|
|
30
|
+
|
|
31
|
+
defineProps({ xml: String })
|
|
32
|
+
const studioRef = ref(null)
|
|
33
|
+
|
|
34
|
+
function handleChange(model, reason, nextXml) {
|
|
35
|
+
console.log(reason, nextXml)
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div style="height: 720px">
|
|
41
|
+
<BpmnStudio
|
|
42
|
+
ref="studioRef"
|
|
43
|
+
:xml="xml"
|
|
44
|
+
engine="flowable"
|
|
45
|
+
mode="design"
|
|
46
|
+
theme="auto"
|
|
47
|
+
@change="handleChange"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Expose 提供 `exportXml()`、`fitView()`、`setTheme()`、`exportSvg()` 和 `openSvgExportPreview()`。
|
|
54
|
+
|
|
55
|
+
## 只读展示与审批轨迹
|
|
56
|
+
|
|
57
|
+
```vue
|
|
58
|
+
<script setup>
|
|
59
|
+
import { BpmnViewer } from '@bpmn-nova/vue'
|
|
60
|
+
import '@bpmn-nova/vue/styles.css'
|
|
61
|
+
|
|
62
|
+
defineProps({
|
|
63
|
+
xml: String,
|
|
64
|
+
runtime: Object,
|
|
65
|
+
runtimeAssetResolver: Function,
|
|
66
|
+
})
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<template>
|
|
70
|
+
<div style="height: 720px">
|
|
71
|
+
<BpmnViewer
|
|
72
|
+
:xml="xml"
|
|
73
|
+
engine="flowable"
|
|
74
|
+
:runtime="runtime"
|
|
75
|
+
projection="compact"
|
|
76
|
+
responsive
|
|
77
|
+
theme="auto"
|
|
78
|
+
:runtime-asset-resolver="runtimeAssetResolver"
|
|
79
|
+
@trace-click="event => console.log(event)"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `approval`:实际发生的有效路径。
|
|
86
|
+
- `compact`:移动时间线、审批动作、图片和附件。
|
|
87
|
+
- `standard`:完整 BPMN 叠加运行状态。
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
## Runtime Action 与附件
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
const runtime = {
|
|
95
|
+
processInstanceId: 'purchase-20260824',
|
|
96
|
+
status: 'running',
|
|
97
|
+
activities: [{
|
|
98
|
+
id: 'activity-manager-1',
|
|
99
|
+
elementId: 'UserTask_Manager',
|
|
100
|
+
visitId: 'visit-manager-1',
|
|
101
|
+
status: 'completed',
|
|
102
|
+
assignee: '李经理',
|
|
103
|
+
}],
|
|
104
|
+
actions: [{
|
|
105
|
+
id: 'approve-manager-1',
|
|
106
|
+
type: 'approve',
|
|
107
|
+
elementId: 'UserTask_Manager',
|
|
108
|
+
visitId: 'visit-manager-1',
|
|
109
|
+
occurredAt: '2026-08-24T10:12:00+08:00',
|
|
110
|
+
actor: { id: 'manager-li', name: '李经理' },
|
|
111
|
+
content: {
|
|
112
|
+
plainText: '资料完整,同意提交总经理审批。',
|
|
113
|
+
blocks: [{ type: 'file', assetId: 'purchase-checklist' }],
|
|
114
|
+
assets: [{
|
|
115
|
+
id: 'purchase-checklist',
|
|
116
|
+
name: '采购核验清单.txt',
|
|
117
|
+
mediaType: 'text/plain',
|
|
118
|
+
size: 248,
|
|
119
|
+
}],
|
|
120
|
+
},
|
|
121
|
+
}],
|
|
122
|
+
visitedEdges: [],
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const runtimeAssetResolver = async (asset, { purpose, signal }) => {
|
|
126
|
+
const response = await fetch(
|
|
127
|
+
`/api/runtime-assets/${encodeURIComponent(asset.id)}?purpose=${purpose}`,
|
|
128
|
+
{ signal },
|
|
129
|
+
)
|
|
130
|
+
return response.ok ? response.url : null
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Runtime Snapshot 只保存资源引用;上传、存储和权限签发由宿主负责。
|
|
135
|
+
|
|
136
|
+
## 主题和 SVG 导出
|
|
137
|
+
|
|
138
|
+
```vue
|
|
139
|
+
<BpmnStudio
|
|
140
|
+
ref="studioRef"
|
|
141
|
+
:xml="xml"
|
|
142
|
+
:theme="{
|
|
143
|
+
mode: 'auto',
|
|
144
|
+
dark: {
|
|
145
|
+
colors: {
|
|
146
|
+
canvas: '#0b1018',
|
|
147
|
+
surface: '#18202b',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
}"
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
studioRef.value?.openSvgExportPreview({
|
|
156
|
+
theme: 'current',
|
|
157
|
+
filename: '采购申请审批流程.svg',
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
主题属性变化会更新现有实例,不会重新挂载画布、清除选择或关闭详情。
|
|
162
|
+
|
|
163
|
+
## Properties 与自定义 Renderer
|
|
164
|
+
|
|
165
|
+
本包还导出:
|
|
166
|
+
|
|
167
|
+
- `BpmnDesigner`、`BpmnCanvas`、`BpmnPropertiesPanel`
|
|
168
|
+
- `useBpmnStudio`
|
|
169
|
+
- `createVuePropertyComponent`
|
|
170
|
+
- `createVueRuntimeDetailsComponent`
|
|
171
|
+
- `createVueRuntimeTransitionDetailsComponent`
|
|
172
|
+
- `createVueRuntimeTimelineComponent`
|
|
173
|
+
|
|
174
|
+
## 常见问题
|
|
175
|
+
|
|
176
|
+
- **无高度:** 为外层容器设置明确高度。
|
|
177
|
+
- **无样式:** 导入一次 `@bpmn-nova/vue/styles.css`。
|
|
178
|
+
- **Vue 重复:** Vue 应由宿主提供并保持单实例。
|
|
179
|
+
- **SSR:** 将可视组件放到客户端挂载阶段。
|
|
180
|
+
- **包选择:** Vue 项目只直接安装 `@bpmn-nova/vue`。
|
|
181
|
+
|
|
182
|
+
## 文档与 AI
|
|
183
|
+
|
|
184
|
+
- [完整项目能力](https://github.com/daxiangme/bpmn-nova)
|
|
185
|
+
- [React/Vue 组件说明](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/COMPONENTS.md)
|
|
186
|
+
- [公开 Interface](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/API.md)
|
|
187
|
+
- [AI 接入入口](https://raw.githubusercontent.com/daxiangme/bpmn-nova/dev/llms.txt)
|
|
188
|
+
- [AI 完整上下文](https://raw.githubusercontent.com/daxiangme/bpmn-nova/dev/llms-full.txt)
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Component, DefineComponent } from 'vue'
|
|
2
|
+
import type { BpmnEdge, BpmnNode, ElementSelection, EngineId, LayoutOptions, ProcessModel } from '@bpmn-nova/studio'
|
|
3
|
+
import type { BpmnDesigner as DesignerInstance } from '@bpmn-nova/studio/designer'
|
|
4
|
+
import type { IconRegistry } from '@bpmn-nova/studio'
|
|
5
|
+
import type { PaletteRegistry } from '@bpmn-nova/studio'
|
|
6
|
+
import type { PropertiesContext, PropertiesProvider, PropertiesRegistry, PropertyEntry } from '@bpmn-nova/studio'
|
|
7
|
+
import type { ProcessInstanceSnapshot } from '@bpmn-nova/studio/runtime'
|
|
8
|
+
import type { SvgExportArtifact, SvgExportOptions, SvgExportPreviewController } from '@bpmn-nova/studio/export-svg'
|
|
9
|
+
import type {
|
|
10
|
+
BpmnCanvas as CanvasInstance,
|
|
11
|
+
BpmnStudioController,
|
|
12
|
+
BpmnStudioShell,
|
|
13
|
+
InteractionController,
|
|
14
|
+
StudioCommands,
|
|
15
|
+
StudioShellOptions,
|
|
16
|
+
StudioShellSlots,
|
|
17
|
+
StudioState,
|
|
18
|
+
TemplateRegistry,
|
|
19
|
+
} from '@bpmn-nova/studio'
|
|
20
|
+
import type { NovaThemeInput, NovaThemeState, RuntimeAppearanceOptions } from '@bpmn-nova/studio/theme'
|
|
21
|
+
import type {
|
|
22
|
+
BpmnViewer as ViewerInstance,
|
|
23
|
+
RuntimeDetailsRenderer,
|
|
24
|
+
RuntimeTimelineRenderer,
|
|
25
|
+
RuntimeTraceClickEvent,
|
|
26
|
+
RuntimeTransitionDetailsRenderer,
|
|
27
|
+
ViewerOptions,
|
|
28
|
+
ViewerProjection,
|
|
29
|
+
} from '@bpmn-nova/studio/viewer'
|
|
30
|
+
|
|
31
|
+
export interface BpmnNovaVisualProps {
|
|
32
|
+
theme?: NovaThemeInput
|
|
33
|
+
runtimeAppearance?: RuntimeAppearanceOptions
|
|
34
|
+
onThemeChange?: (state: NovaThemeState) => void
|
|
35
|
+
}
|
|
36
|
+
export interface BpmnModelProps { model?: ProcessModel; xml?: string; engine?: EngineId }
|
|
37
|
+
export interface UseBpmnStudioOptions extends BpmnModelProps { studio?: BpmnStudioController; propertiesProfile?: 'business' | 'developer' }
|
|
38
|
+
export interface UseBpmnStudioResult { studio: BpmnStudioController; state: StudioState; commands: StudioCommands }
|
|
39
|
+
export function useBpmnStudio(options?: UseBpmnStudioOptions): UseBpmnStudioResult
|
|
40
|
+
|
|
41
|
+
export interface BpmnCanvasProps extends BpmnNovaVisualProps {
|
|
42
|
+
studio: BpmnStudioController
|
|
43
|
+
interactions?: InteractionController
|
|
44
|
+
templates?: TemplateRegistry
|
|
45
|
+
iconRegistry?: IconRegistry
|
|
46
|
+
nodeRenderers?: Record<string, Function>
|
|
47
|
+
nodeRenderer?: Function
|
|
48
|
+
svgExport?: ViewerOptions['svgExport']
|
|
49
|
+
}
|
|
50
|
+
export interface BpmnCanvasExposed {
|
|
51
|
+
getCanvas(): CanvasInstance | null
|
|
52
|
+
fitView(): void
|
|
53
|
+
setTheme(theme: NovaThemeInput): NovaThemeState | undefined
|
|
54
|
+
exportSvg(options?: SvgExportOptions): Promise<SvgExportArtifact> | undefined
|
|
55
|
+
openSvgExportPreview(options?: SvgExportOptions & { previewTitle?: string; onDownload?: (artifact: SvgExportArtifact) => void }): SvgExportPreviewController | undefined
|
|
56
|
+
}
|
|
57
|
+
export const BpmnCanvas: DefineComponent<BpmnCanvasProps>
|
|
58
|
+
|
|
59
|
+
export interface BpmnStudioProps extends BpmnNovaVisualProps, BpmnModelProps {
|
|
60
|
+
studio?: BpmnStudioController
|
|
61
|
+
propertiesProfile?: 'business' | 'developer'
|
|
62
|
+
iconRegistry?: IconRegistry
|
|
63
|
+
paletteRegistry?: PaletteRegistry
|
|
64
|
+
propertiesRegistry?: PropertiesRegistry
|
|
65
|
+
templateRegistry?: TemplateRegistry
|
|
66
|
+
nodeRenderers?: Record<string, Function>
|
|
67
|
+
nodeRenderer?: Function
|
|
68
|
+
slotsConfig?: StudioShellSlots
|
|
69
|
+
layout?: StudioShellOptions['layout']
|
|
70
|
+
leftWidth?: number
|
|
71
|
+
rightWidth?: number
|
|
72
|
+
runtime?: ProcessInstanceSnapshot | null
|
|
73
|
+
runtimePresenter?: ViewerOptions['runtimePresenter']
|
|
74
|
+
runtimeTraceProjector?: ViewerOptions['runtimeTraceProjector']
|
|
75
|
+
runtimeAssetResolver?: ViewerOptions['runtimeAssetResolver']
|
|
76
|
+
runtimeTimelineRenderer?: RuntimeTimelineRenderer
|
|
77
|
+
svgExport?: ViewerOptions['svgExport']
|
|
78
|
+
onRuntimeTraceItemClick?: ViewerOptions['onRuntimeTraceItemClick']
|
|
79
|
+
runtimeDetailsRenderer?: RuntimeDetailsRenderer | null
|
|
80
|
+
onRuntimeDetailsOpen?: ViewerOptions['onRuntimeDetailsOpen']
|
|
81
|
+
runtimeTransitionDetailsRenderer?: RuntimeTransitionDetailsRenderer | null
|
|
82
|
+
onRuntimeTransitionDetailsOpen?: ViewerOptions['onRuntimeTransitionDetailsOpen']
|
|
83
|
+
timeline?: ViewerOptions['timeline']
|
|
84
|
+
runtimeDetails?: ViewerOptions['runtimeDetails']
|
|
85
|
+
runtimeTraceOptions?: ViewerOptions['runtimeTraceOptions']
|
|
86
|
+
onTraceClick?: (event: RuntimeTraceClickEvent) => void
|
|
87
|
+
onElementClick?: ViewerOptions['onElementClick']
|
|
88
|
+
mode?: 'design' | 'viewer' | 'instance'
|
|
89
|
+
projection?: ViewerProjection
|
|
90
|
+
responsive?: boolean
|
|
91
|
+
projectionOptions?: Array<{ value: ViewerProjection; label: string }>
|
|
92
|
+
}
|
|
93
|
+
export interface BpmnStudioExposed {
|
|
94
|
+
getStudio(): BpmnStudioController
|
|
95
|
+
getShell(): BpmnStudioShell | null
|
|
96
|
+
exportXml(engine?: EngineId): string
|
|
97
|
+
fitView(): void
|
|
98
|
+
setTheme(theme: NovaThemeInput): NovaThemeState | undefined
|
|
99
|
+
exportSvg(options?: SvgExportOptions): Promise<SvgExportArtifact> | undefined
|
|
100
|
+
openSvgExportPreview(options?: SvgExportOptions & { previewTitle?: string; onDownload?: (artifact: SvgExportArtifact) => void }): SvgExportPreviewController | null | undefined
|
|
101
|
+
}
|
|
102
|
+
export const BpmnStudio: DefineComponent<BpmnStudioProps>
|
|
103
|
+
|
|
104
|
+
export interface BpmnDesignerProps extends BpmnNovaVisualProps, BpmnModelProps { svgExport?: ViewerOptions['svgExport'] }
|
|
105
|
+
export interface BpmnDesignerExposed {
|
|
106
|
+
getInstance(): DesignerInstance | null
|
|
107
|
+
exportXml(engine?: EngineId): string
|
|
108
|
+
fitView(): void
|
|
109
|
+
beautify(options?: LayoutOptions): void
|
|
110
|
+
rerouteEdges(options?: LayoutOptions): void
|
|
111
|
+
validate(): Array<{ level: 'error' | 'warning'; elementId?: string; message: string }>
|
|
112
|
+
setTheme(theme: NovaThemeInput): NovaThemeState | undefined
|
|
113
|
+
exportSvg(options?: SvgExportOptions): Promise<SvgExportArtifact> | undefined
|
|
114
|
+
openSvgExportPreview(options?: SvgExportOptions & { previewTitle?: string; onDownload?: (artifact: SvgExportArtifact) => void }): SvgExportPreviewController | undefined
|
|
115
|
+
}
|
|
116
|
+
export const BpmnDesigner: DefineComponent<BpmnDesignerProps>
|
|
117
|
+
|
|
118
|
+
export interface BpmnViewerProps extends BpmnNovaVisualProps, BpmnModelProps, Omit<ViewerOptions, 'container' | 'model' | 'theme' | 'runtimeAppearance' | 'onThemeChange'> {}
|
|
119
|
+
export interface BpmnViewerExposed {
|
|
120
|
+
getInstance(): ViewerInstance | null
|
|
121
|
+
fitView(): void
|
|
122
|
+
setProjection(projection: ViewerProjection): void
|
|
123
|
+
setDisplayOptions(options: Parameters<ViewerInstance['setDisplayOptions']>[0]): void
|
|
124
|
+
setTheme(theme: NovaThemeInput): NovaThemeState | undefined
|
|
125
|
+
exportSvg(options?: SvgExportOptions): Promise<SvgExportArtifact> | undefined
|
|
126
|
+
openSvgExportPreview(options?: SvgExportOptions & { previewTitle?: string; onDownload?: (artifact: SvgExportArtifact) => void }): SvgExportPreviewController | undefined
|
|
127
|
+
}
|
|
128
|
+
export const BpmnViewer: DefineComponent<BpmnViewerProps>
|
|
129
|
+
|
|
130
|
+
export interface VuePropertyComponentProps {
|
|
131
|
+
entry: PropertyEntry
|
|
132
|
+
context: PropertiesContext
|
|
133
|
+
value: unknown
|
|
134
|
+
onChange(value: unknown): void
|
|
135
|
+
}
|
|
136
|
+
export function createVuePropertyComponent(component: Component<VuePropertyComponentProps>): (context: Record<string, unknown>) => void | (() => void)
|
|
137
|
+
export interface BpmnPropertiesPanelProps extends BpmnNovaVisualProps {
|
|
138
|
+
studio?: BpmnStudioController
|
|
139
|
+
designer?: BpmnStudioController | DesignerInstance
|
|
140
|
+
registry?: PropertiesRegistry
|
|
141
|
+
providers?: PropertiesProvider[]
|
|
142
|
+
dataProviders?: Record<string, unknown>
|
|
143
|
+
components?: Record<string, Component<VuePropertyComponentProps>>
|
|
144
|
+
}
|
|
145
|
+
export interface BpmnPropertiesPanelExposed {
|
|
146
|
+
getPanel(): unknown
|
|
147
|
+
getRegistry(): PropertiesRegistry | null
|
|
148
|
+
render(): void
|
|
149
|
+
setTheme(theme: NovaThemeInput): NovaThemeState | undefined
|
|
150
|
+
}
|
|
151
|
+
export const BpmnPropertiesPanel: DefineComponent<BpmnPropertiesPanelProps>
|
|
152
|
+
|
|
153
|
+
export function createVueRuntimeDetailsComponent(component: Component<Omit<Parameters<RuntimeDetailsRenderer>[0], 'container'>>): RuntimeDetailsRenderer
|
|
154
|
+
export function createVueRuntimeTransitionDetailsComponent(component: Component<Omit<Parameters<RuntimeTransitionDetailsRenderer>[0], 'container'>>): RuntimeTransitionDetailsRenderer
|
|
155
|
+
export function createVueRuntimeTimelineComponent(component: Component<Omit<Parameters<RuntimeTimelineRenderer>[0], 'container'>>): RuntimeTimelineRenderer
|
|
156
|
+
|
|
157
|
+
export interface BpmnStudioEmits {
|
|
158
|
+
change: [model: ProcessModel, reason: string, xml: string]
|
|
159
|
+
'selection-change': [selection: ElementSelection | null, element: StudioState['selectedElement']]
|
|
160
|
+
'scope-change': [activeScopeId: string, scopePath: StudioState['scopePath'], state: StudioState]
|
|
161
|
+
'element-click': [payload: Parameters<NonNullable<ViewerOptions['onElementClick']>>[0]]
|
|
162
|
+
'trace-click': [payload: RuntimeTraceClickEvent]
|
|
163
|
+
}
|
|
164
|
+
export interface BpmnDesignerEmits {
|
|
165
|
+
change: [model: ProcessModel, reason: string, xml: string]
|
|
166
|
+
'selection-change': [selection: ElementSelection | null, element: BpmnNode | BpmnEdge | null]
|
|
167
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { createApp, defineComponent, h, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
|
2
|
+
import { BpmnDesigner as CoreDesigner } from '@bpmn-nova/studio/designer';
|
|
3
|
+
import { BpmnViewer as CoreViewer } from '@bpmn-nova/studio/viewer';
|
|
4
|
+
import { importBpmn, exportBpmn } from '@bpmn-nova/studio';
|
|
5
|
+
import { createEmptyProcess } from '@bpmn-nova/studio';
|
|
6
|
+
import { BpmnCanvas as CoreCanvas, BpmnStudioShell as CoreStudioShell, createInteractionController, createStudioController, createTemplateRegistry } from '@bpmn-nova/studio';
|
|
7
|
+
|
|
8
|
+
export function createVueRuntimeDetailsComponent(Component) {
|
|
9
|
+
return ({ container, ...context }) => {
|
|
10
|
+
const app = createApp(Component, context);
|
|
11
|
+
app.mount(container);
|
|
12
|
+
return () => app.unmount();
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createVueRuntimeTransitionDetailsComponent(Component) {
|
|
17
|
+
return createVueRuntimeDetailsComponent(Component);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createVueRuntimeTimelineComponent(Component) {
|
|
21
|
+
return createVueRuntimeDetailsComponent(Component);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveModel(props) {
|
|
25
|
+
if (props.model) return props.model;
|
|
26
|
+
if (props.xml) return importBpmn(props.xml, props.engine);
|
|
27
|
+
return createEmptyProcess(props.engine || 'flowable');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useBpmnStudio(options = {}) {
|
|
31
|
+
const studio = options.studio || createStudioController({ model: resolveModel(options), propertiesProfile: options.propertiesProfile });
|
|
32
|
+
const state = reactive(studio.getState());
|
|
33
|
+
const off = studio.subscribe((event) => Object.assign(state, event.state));
|
|
34
|
+
onBeforeUnmount(() => { off(); if (!options.studio) studio.destroy(); });
|
|
35
|
+
return { studio, state, commands: studio.commands };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const BpmnCanvas = defineComponent({
|
|
39
|
+
name: 'BpmnNovaCanvas',
|
|
40
|
+
props: { studio: { type: Object, required: true }, interactions: Object, templates: Object, iconRegistry: Object, nodeRenderers: Object, nodeRenderer: Function, svgExport: Object, theme: [Object, String], onThemeChange: Function },
|
|
41
|
+
setup(props, { expose, attrs }) {
|
|
42
|
+
const host = ref(null);
|
|
43
|
+
let canvas = null;
|
|
44
|
+
onMounted(() => {
|
|
45
|
+
const interactions = props.interactions || createInteractionController({ studio: props.studio, templates: props.templates || createTemplateRegistry() });
|
|
46
|
+
canvas = new CoreCanvas({ container: host.value, studio: props.studio, interactions, theme: props.theme, onThemeChange: props.onThemeChange, rendererOptions: { iconRegistry: props.iconRegistry, nodeRenderers: props.nodeRenderers, nodeRenderer: props.nodeRenderer, svgExport: props.svgExport } });
|
|
47
|
+
requestAnimationFrame(() => canvas.fitView());
|
|
48
|
+
});
|
|
49
|
+
onBeforeUnmount(() => canvas?.destroy());
|
|
50
|
+
watch(() => props.theme, (value) => { if (value !== undefined) canvas?.setTheme(value); });
|
|
51
|
+
expose({ getCanvas: () => canvas, fitView: () => canvas?.fitView(), setTheme: (value) => canvas?.setTheme(value), exportSvg: (options) => canvas?.exportSvg(options), openSvgExportPreview: (options) => canvas?.openSvgExportPreview(options) });
|
|
52
|
+
return () => h('div', { ...attrs, ref: host, style: { width: '100%', height: '100%', minHeight: '320px', ...(attrs.style || {}) } });
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const BpmnStudio = defineComponent({
|
|
57
|
+
name: 'BpmnNovaStudio',
|
|
58
|
+
props: {
|
|
59
|
+
studio: Object, model: Object, xml: String, engine: { type: String, default: 'flowable' }, propertiesProfile: String,
|
|
60
|
+
iconRegistry: Object, paletteRegistry: Object, propertiesRegistry: Object, templateRegistry: Object,
|
|
61
|
+
nodeRenderers: Object, nodeRenderer: Function, slotsConfig: Object, layout: Function, leftWidth: Number, rightWidth: Number,
|
|
62
|
+
runtime: Object, runtimePresenter: Function, runtimeTraceProjector: Function, runtimeAssetResolver: Function, runtimeTimelineRenderer: Function, onRuntimeTraceItemClick: Function, runtimeDetailsRenderer: Function, onRuntimeDetailsOpen: Function,
|
|
63
|
+
runtimeTransitionDetailsRenderer: Function, onRuntimeTransitionDetailsOpen: Function,
|
|
64
|
+
timeline: Object, runtimeDetails: Object, runtimeTraceOptions: Object, onTraceClick: Function, onElementClick: Function,
|
|
65
|
+
mode: String, projection: String, responsive: { type: Boolean, default: false }, projectionOptions: Array,
|
|
66
|
+
theme: [Object, String], runtimeAppearance: Object, svgExport: Object, onThemeChange: Function,
|
|
67
|
+
},
|
|
68
|
+
emits: ['change', 'selection-change', 'scope-change', 'element-click', 'trace-click'],
|
|
69
|
+
setup(props, { emit, expose, attrs }) {
|
|
70
|
+
const host = ref(null);
|
|
71
|
+
const owned = !props.studio;
|
|
72
|
+
const studio = props.studio || createStudioController({ model: resolveModel(props), propertiesProfile: props.propertiesProfile });
|
|
73
|
+
let shell = null;
|
|
74
|
+
let off = null;
|
|
75
|
+
onMounted(() => {
|
|
76
|
+
shell = new CoreStudioShell({ container: host.value, studio, iconRegistry: props.iconRegistry, paletteRegistry: props.paletteRegistry, propertiesRegistry: props.propertiesRegistry, templateRegistry: props.templateRegistry, rendererOptions: { nodeRenderers: props.nodeRenderers, nodeRenderer: props.nodeRenderer, runtimePresenter: props.runtimePresenter, runtimeTraceProjector: props.runtimeTraceProjector, runtimeAssetResolver: props.runtimeAssetResolver, runtimeTraceOptions: props.runtimeTraceOptions, runtimeTimelineRenderer: props.runtimeTimelineRenderer, timeline: props.timeline, runtimeDetails: props.runtimeDetails, onRuntimeTraceItemClick: (payload) => props.onRuntimeTraceItemClick?.(payload), runtimeDetailsRenderer: props.runtimeDetailsRenderer, onRuntimeDetailsOpen: (payload) => props.onRuntimeDetailsOpen?.(payload), runtimeTransitionDetailsRenderer: props.runtimeTransitionDetailsRenderer, onRuntimeTransitionDetailsOpen: (payload) => props.onRuntimeTransitionDetailsOpen?.(payload), onElementClick: (payload) => emit('element-click', payload), onTraceClick: (payload) => emit('trace-click', payload) }, slots: props.slotsConfig || {}, layout: props.layout, runtime: props.runtime, mode: props.mode, projection: props.projection, responsive: props.responsive, projectionOptions: props.projectionOptions, leftWidth: props.leftWidth, rightWidth: props.rightWidth, theme: props.theme, runtimeAppearance: props.runtimeAppearance, svgExport: props.svgExport, onThemeChange: props.onThemeChange });
|
|
77
|
+
off = studio.subscribe((event) => {
|
|
78
|
+
if (event.type === 'modelChanged') emit('change', studio.model, event.reason, studio.exportXml());
|
|
79
|
+
if (event.type === 'selectionChanged') emit('selection-change', studio.selection, studio.getSelectedElement());
|
|
80
|
+
if (event.type === 'scopeChanged') emit('scope-change', event.activeScopeId, event.scopePath, studio.getState());
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
watch(() => props.model, (value) => { if (value && value !== studio.model) studio.setModel(value); });
|
|
84
|
+
watch(() => props.xml, (value) => { if (value) studio.importXml(value, props.engine); });
|
|
85
|
+
watch(() => props.runtime, (value) => { if (value !== undefined) shell?.setRuntime(value); });
|
|
86
|
+
watch(() => props.mode, (value) => { if (value) shell?.setMode(value); });
|
|
87
|
+
watch(() => props.projection, (value) => { if (value) shell?.setProjection(value); });
|
|
88
|
+
watch(() => props.theme, (value) => { if (value !== undefined) shell?.setTheme(value); });
|
|
89
|
+
watch(() => props.runtimeAppearance, (value) => { if (value !== undefined) shell?.setRuntimeAppearance(value); });
|
|
90
|
+
watch(() => [props.timeline, props.runtimeDetails, props.runtimeTraceOptions], ([timeline, runtimeDetails, runtimeTraceOptions]) => {
|
|
91
|
+
if (!shell) return;
|
|
92
|
+
shell.rendererOptions.timeline = timeline;
|
|
93
|
+
shell.rendererOptions.runtimeDetails = runtimeDetails;
|
|
94
|
+
shell.rendererOptions.runtimeTraceOptions = runtimeTraceOptions;
|
|
95
|
+
shell.viewer?.setDisplayOptions({ timeline, runtimeDetails, runtimeTraceOptions });
|
|
96
|
+
});
|
|
97
|
+
onBeforeUnmount(() => { off?.(); shell?.destroy(); if (owned) studio.destroy(); });
|
|
98
|
+
expose({ getStudio: () => studio, getShell: () => shell, exportXml: (engine) => studio.exportXml(engine), exportSvg: (options) => shell?.exportSvg(options), openSvgExportPreview: (options) => shell?.openSvgExportPreview(options), fitView: () => shell?.canvas?.fitView(), setTheme: (value) => shell?.setTheme(value) });
|
|
99
|
+
return () => h('div', { ...attrs, ref: host, style: { width: '100%', height: '100%', minHeight: '520px', ...(attrs.style || {}) } });
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export const BpmnDesigner = defineComponent({
|
|
104
|
+
name: 'ModernBpmnDesigner',
|
|
105
|
+
props: {
|
|
106
|
+
xml: String,
|
|
107
|
+
model: Object,
|
|
108
|
+
engine: { type: String, default: 'flowable' },
|
|
109
|
+
theme: [Object, String],
|
|
110
|
+
svgExport: Object,
|
|
111
|
+
onThemeChange: Function,
|
|
112
|
+
},
|
|
113
|
+
emits: ['change', 'selection-change'],
|
|
114
|
+
setup(props, { emit, expose, attrs }) {
|
|
115
|
+
const host = ref(null);
|
|
116
|
+
let instance = null;
|
|
117
|
+
|
|
118
|
+
onMounted(() => {
|
|
119
|
+
instance = new CoreDesigner({
|
|
120
|
+
container: host.value,
|
|
121
|
+
model: resolveModel(props),
|
|
122
|
+
onChange(model, reason) { emit('change', model, reason, exportBpmn(model, model.engine)); },
|
|
123
|
+
onSelectionChange(selection, element) { emit('selection-change', selection, element); },
|
|
124
|
+
theme: props.theme,
|
|
125
|
+
svgExport: props.svgExport,
|
|
126
|
+
onThemeChange: props.onThemeChange,
|
|
127
|
+
});
|
|
128
|
+
requestAnimationFrame(() => instance.renderer.fitView());
|
|
129
|
+
});
|
|
130
|
+
onBeforeUnmount(() => instance?.destroy());
|
|
131
|
+
|
|
132
|
+
watch(() => props.engine, (value) => {
|
|
133
|
+
if (instance && value && value !== instance.model.engine) instance.setEngine(value);
|
|
134
|
+
});
|
|
135
|
+
watch(() => props.model, (value) => { if (instance && value && value !== instance.model) instance.setModel(value); });
|
|
136
|
+
watch(() => props.xml, (value) => { if (instance && value) instance.setModel(importBpmn(value, props.engine)); });
|
|
137
|
+
watch(() => props.theme, (value) => { if (value !== undefined) instance?.setTheme(value); });
|
|
138
|
+
|
|
139
|
+
expose({
|
|
140
|
+
getInstance: () => instance,
|
|
141
|
+
exportXml: (engine) => instance?.exportXml(engine) || '',
|
|
142
|
+
fitView: () => instance?.renderer.fitView(),
|
|
143
|
+
beautify: (options) => instance?.beautify(options),
|
|
144
|
+
rerouteEdges: (options) => instance?.rerouteEdges(options),
|
|
145
|
+
validate: () => instance?.validate() || [],
|
|
146
|
+
setTheme: (value) => instance?.setTheme(value),
|
|
147
|
+
exportSvg: (options) => instance?.exportSvg(options),
|
|
148
|
+
openSvgExportPreview: (options) => instance?.openSvgExportPreview(options),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return () => h('div', { ...attrs, ref: host, style: { width: '100%', height: '100%', minHeight: '320px', ...(attrs.style || {}) } });
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
export const BpmnViewer = defineComponent({
|
|
156
|
+
name: 'ModernBpmnViewer',
|
|
157
|
+
props: {
|
|
158
|
+
xml: String,
|
|
159
|
+
model: Object,
|
|
160
|
+
engine: { type: String, default: 'flowable' },
|
|
161
|
+
runtime: Object,
|
|
162
|
+
iconRegistry: Object,
|
|
163
|
+
nodeRenderers: Object,
|
|
164
|
+
nodeRenderer: Function,
|
|
165
|
+
timeline: Object,
|
|
166
|
+
runtimeDetails: Object,
|
|
167
|
+
runtimePresenter: Function,
|
|
168
|
+
runtimeTraceProjector: Function,
|
|
169
|
+
runtimeAssetResolver: Function,
|
|
170
|
+
runtimeTraceOptions: Object,
|
|
171
|
+
runtimeTimelineRenderer: Function,
|
|
172
|
+
onRuntimeTraceItemClick: Function,
|
|
173
|
+
onProjectionChange: Function,
|
|
174
|
+
runtimeDetailsRenderer: Function,
|
|
175
|
+
onRuntimeDetailsOpen: Function,
|
|
176
|
+
runtimeTransitionDetailsRenderer: Function,
|
|
177
|
+
onRuntimeTransitionDetailsOpen: Function,
|
|
178
|
+
onTraceClick: Function,
|
|
179
|
+
responsive: { type: Boolean, default: false },
|
|
180
|
+
projection: { type: String, default: undefined },
|
|
181
|
+
theme: [Object, String],
|
|
182
|
+
runtimeAppearance: Object,
|
|
183
|
+
svgExport: Object,
|
|
184
|
+
onThemeChange: Function,
|
|
185
|
+
},
|
|
186
|
+
emits: ['element-click', 'trace-click'],
|
|
187
|
+
setup(props, { emit, expose, attrs }) {
|
|
188
|
+
const host = ref(null);
|
|
189
|
+
let instance = null;
|
|
190
|
+
onMounted(() => {
|
|
191
|
+
instance = new CoreViewer({
|
|
192
|
+
container: host.value,
|
|
193
|
+
model: resolveModel(props),
|
|
194
|
+
runtime: props.runtime,
|
|
195
|
+
iconRegistry: props.iconRegistry,
|
|
196
|
+
nodeRenderers: props.nodeRenderers,
|
|
197
|
+
nodeRenderer: props.nodeRenderer,
|
|
198
|
+
timeline: props.timeline,
|
|
199
|
+
runtimeDetails: props.runtimeDetails,
|
|
200
|
+
runtimePresenter: props.runtimePresenter,
|
|
201
|
+
runtimeTraceProjector: props.runtimeTraceProjector,
|
|
202
|
+
runtimeAssetResolver: props.runtimeAssetResolver,
|
|
203
|
+
runtimeTraceOptions: props.runtimeTraceOptions,
|
|
204
|
+
runtimeTimelineRenderer: props.runtimeTimelineRenderer,
|
|
205
|
+
onRuntimeTraceItemClick: (payload) => props.onRuntimeTraceItemClick?.(payload),
|
|
206
|
+
onProjectionChange: (payload) => props.onProjectionChange?.(payload),
|
|
207
|
+
runtimeDetailsRenderer: props.runtimeDetailsRenderer,
|
|
208
|
+
onRuntimeDetailsOpen: (payload) => props.onRuntimeDetailsOpen?.(payload),
|
|
209
|
+
runtimeTransitionDetailsRenderer: props.runtimeTransitionDetailsRenderer,
|
|
210
|
+
onRuntimeTransitionDetailsOpen: (payload) => props.onRuntimeTransitionDetailsOpen?.(payload),
|
|
211
|
+
projection: props.projection,
|
|
212
|
+
responsive: props.responsive,
|
|
213
|
+
onElementClick(payload) { emit('element-click', payload); },
|
|
214
|
+
onTraceClick(payload) { emit('trace-click', payload); },
|
|
215
|
+
theme: props.theme,
|
|
216
|
+
runtimeAppearance: props.runtimeAppearance,
|
|
217
|
+
svgExport: props.svgExport,
|
|
218
|
+
onThemeChange: props.onThemeChange,
|
|
219
|
+
});
|
|
220
|
+
requestAnimationFrame(() => instance.fitView());
|
|
221
|
+
});
|
|
222
|
+
onBeforeUnmount(() => instance?.destroy());
|
|
223
|
+
watch(() => props.runtime, (value) => instance?.setRuntime(value));
|
|
224
|
+
watch(() => props.projection, (value) => instance?.setProjection(value));
|
|
225
|
+
watch(() => props.theme, (value) => { if (value !== undefined) instance?.setTheme(value); });
|
|
226
|
+
watch(() => props.runtimeAppearance, (value) => { if (value !== undefined) instance?.setRuntimeAppearance(value); });
|
|
227
|
+
watch(() => [props.timeline, props.runtimeDetails, props.runtimeTraceOptions, props.runtimeAssetResolver], ([timeline, runtimeDetails, runtimeTraceOptions, runtimeAssetResolver]) => instance?.setDisplayOptions({ timeline, runtimeDetails, runtimeTraceOptions, runtimeAssetResolver }));
|
|
228
|
+
watch(() => props.model, (value) => { if (value) instance?.setModel(value); });
|
|
229
|
+
watch(() => props.xml, (value) => { if (value) instance?.setModel(importBpmn(value, props.engine)); });
|
|
230
|
+
expose({ getInstance: () => instance, fitView: () => instance?.fitView(), setProjection: (p) => instance?.setProjection(p), setDisplayOptions: (value) => instance?.setDisplayOptions(value), setTheme: (value) => instance?.setTheme(value), exportSvg: (options) => instance?.exportSvg(options), openSvgExportPreview: (options) => instance?.openSvgExportPreview(options) });
|
|
231
|
+
return () => h('div', { ...attrs, ref: host, style: { width: '100%', height: '100%', minHeight: '260px', ...(attrs.style || {}) } });
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
// Properties Panel -----------------------------------------------------------
|
|
237
|
+
import { createDefaultPropertiesRegistry, PropertiesPanel as CorePropertiesPanel } from '@bpmn-nova/studio';
|
|
238
|
+
|
|
239
|
+
export function createVuePropertyComponent(Component) {
|
|
240
|
+
return ({ container, entry, context, value, commit }) => {
|
|
241
|
+
const app = createApp(Component, { entry, context, value, onChange: commit });
|
|
242
|
+
app.mount(container);
|
|
243
|
+
return () => app.unmount();
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const BpmnPropertiesPanel = defineComponent({
|
|
248
|
+
name: 'BpmnNovaPropertiesPanel',
|
|
249
|
+
props: {
|
|
250
|
+
studio: Object,
|
|
251
|
+
designer: Object,
|
|
252
|
+
registry: Object,
|
|
253
|
+
providers: Array,
|
|
254
|
+
dataProviders: Object,
|
|
255
|
+
components: Object,
|
|
256
|
+
theme: [Object, String],
|
|
257
|
+
onThemeChange: Function,
|
|
258
|
+
},
|
|
259
|
+
setup(props, { expose, attrs }) {
|
|
260
|
+
const host = ref(null);
|
|
261
|
+
let panel = null;
|
|
262
|
+
let registry = null;
|
|
263
|
+
let offSelection = null;
|
|
264
|
+
let offChange = null;
|
|
265
|
+
|
|
266
|
+
const mountPanel = () => {
|
|
267
|
+
const studio = props.studio || props.designer;
|
|
268
|
+
if (!host.value || !studio) return;
|
|
269
|
+
panel?.destroy();
|
|
270
|
+
offSelection?.();
|
|
271
|
+
offChange?.();
|
|
272
|
+
registry = props.registry || createDefaultPropertiesRegistry({ studio, dataProviders: props.dataProviders || {} });
|
|
273
|
+
for (const provider of props.providers || []) registry.registerProvider(provider.priority ?? 500, provider);
|
|
274
|
+
for (const [name, Component] of Object.entries(props.components || {})) registry.registerComponent(name, createVuePropertyComponent(Component));
|
|
275
|
+
panel = new CorePropertiesPanel({ container: host.value, registry, studio, theme: props.theme, onThemeChange: props.onThemeChange });
|
|
276
|
+
panel.render();
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
onMounted(mountPanel);
|
|
280
|
+
watch(() => props.designer, mountPanel);
|
|
281
|
+
watch(() => props.studio, mountPanel);
|
|
282
|
+
watch(() => props.theme, (value) => { if (value !== undefined) panel?.setTheme(value); });
|
|
283
|
+
onBeforeUnmount(() => { offSelection?.(); offChange?.(); panel?.destroy(); });
|
|
284
|
+
expose({ getPanel: () => panel, getRegistry: () => registry, render: () => panel?.render(), setTheme: (value) => panel?.setTheme(value) });
|
|
285
|
+
return () => h('div', { ...attrs, ref: host, style: { width: '100%', height: '100%', overflow: 'auto', ...(attrs.style || {}) } });
|
|
286
|
+
},
|
|
287
|
+
});
|