@bpmn-nova/react 0.3.4-preview → 0.3.6-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/README.md +15 -237
- package/dist/index.d.ts +79 -0
- package/dist/index.js +57 -3
- package/dist/styles.css +72 -12
- package/llms-full.txt +3924 -826
- package/llms.txt +64 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ BPMN Nova 的 React 18+ Adapter,提供 Studio、Designer、Viewer、审批轨
|
|
|
4
4
|
|
|
5
5
|
> **English summary:** React 18+ components and refs for BPMN Nova process design, viewing, approval traces, properties, themes, and SVG export.
|
|
6
6
|
|
|
7
|
-
> 当前版本为 `0.3.
|
|
7
|
+
> 当前版本为 `0.3.6-preview`。React 与 React DOM 由宿主工程提供,本包不会替应用选择或升级框架版本。
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -14,35 +14,19 @@ BPMN Nova 的 React 18+ Adapter,提供 Studio、Designer、Viewer、审批轨
|
|
|
14
14
|
npm install @bpmn-nova/react@preview
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
import '@bpmn-nova/react/styles.css'
|
|
19
|
-
```
|
|
17
|
+
只安装本包;匹配版本的 `@bpmn-nova/studio` 会作为依赖自动解析。公共 TypeScript 类型直接从 `@bpmn-nova/react` 导入,不需要为类型额外添加 Studio 直依赖。
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
CSS 必须显式导入 `@bpmn-nova/react/styles.css`;视觉容器及其父级需要具有可计算高度。
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import type {
|
|
27
|
-
BpmnNode,
|
|
28
|
-
EdgeType,
|
|
29
|
-
ElementSelection,
|
|
30
|
-
NodeType,
|
|
31
|
-
ProcessModel,
|
|
32
|
-
} from '@bpmn-nova/react'
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
公共类型门面还包含 `BpmnEdge`、`EngineId` 和 `LayoutOptions`。
|
|
36
|
-
|
|
37
|
-
使用代码生成助手或 IDE Agent 接入时,从随包的 `llms.txt` 开始;需要完整接口和定制资料时再读取 `llms-full.txt`。两份文件都包含在 npm tarball 中,不依赖源码仓库可见性。
|
|
38
|
-
|
|
39
|
-
## 流程设计
|
|
21
|
+
## 最小用法
|
|
40
22
|
|
|
41
23
|
```jsx
|
|
42
24
|
import { useRef } from 'react'
|
|
43
25
|
import { BpmnStudio } from '@bpmn-nova/react'
|
|
44
26
|
import '@bpmn-nova/react/styles.css'
|
|
45
27
|
|
|
28
|
+
const STUDIO_CONFIG = { ui: { controlSize: 'medium' } }
|
|
29
|
+
|
|
46
30
|
export function WorkflowEditor({ initialXml }) {
|
|
47
31
|
const studioRef = useRef(null)
|
|
48
32
|
|
|
@@ -52,6 +36,7 @@ export function WorkflowEditor({ initialXml }) {
|
|
|
52
36
|
ref={studioRef}
|
|
53
37
|
xml={initialXml}
|
|
54
38
|
engine="flowable"
|
|
39
|
+
config={STUDIO_CONFIG}
|
|
55
40
|
mode="design"
|
|
56
41
|
allowedModes={['design']}
|
|
57
42
|
theme="auto"
|
|
@@ -64,224 +49,17 @@ export function WorkflowEditor({ initialXml }) {
|
|
|
64
49
|
}
|
|
65
50
|
```
|
|
66
51
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`xml`/`model` 是外部替换输入,`onChange` 的 XML 是草稿输出。宿主回写完全相同的导出 XML 不会重新导入或清空撤销历史;切换服务器 revision 或流程时传入不同 XML 会替换模型。Activiti 宿主应显式使用 `engine="activiti"`。
|
|
70
|
-
|
|
71
|
-
## 组合宿主头部与业务属性面板
|
|
72
|
-
|
|
73
|
-
默认 `BpmnStudio` 是完整工作台。通过 `regions={{ right: 'hidden' }}` 隐藏 Nova Properties 后,Canvas 会获得该轨道的全部宽度;宿主属性面板应作为外部兄弟区域渲染。
|
|
74
|
-
|
|
75
|
-
```jsx
|
|
76
|
-
<BpmnStudio
|
|
77
|
-
ref={studioRef}
|
|
78
|
-
xml={xml}
|
|
79
|
-
engine="activiti"
|
|
80
|
-
mode="design"
|
|
81
|
-
allowedModes={['design']}
|
|
82
|
-
allowedNodeTypes={supportedNodeTypes}
|
|
83
|
-
allowedEdgeTypes={['sequenceFlow']}
|
|
84
|
-
regions={{ right: 'hidden' }}
|
|
85
|
-
theme="auto"
|
|
86
|
-
headerStart={({ state, actions }) => (
|
|
87
|
-
<HostProcessIdentity onBack={back} />
|
|
88
|
-
)}
|
|
89
|
-
headerActions={({ actions, mode }) => (
|
|
90
|
-
<HostWorkflowActions
|
|
91
|
-
disabled={mode !== 'design'}
|
|
92
|
-
onValidate={() => actions.validate()}
|
|
93
|
-
onSave={() => saveDraft(actions.exportXml())}
|
|
94
|
-
onPublish={() => publishProcess(actions)}
|
|
95
|
-
/>
|
|
96
|
-
)}
|
|
97
|
-
onChange={handleChange}
|
|
98
|
-
onValidation={(event) => handleValidation(event)}
|
|
99
|
-
onSelectionChange={(selection, element) => {
|
|
100
|
-
setSelectedElement(selection ? element : null)
|
|
101
|
-
}}
|
|
102
|
-
/>
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
`headerStart` 只替换 Nova Brand;`headerActions` 只替换默认“校验 / 导入 / 导出”动作组,适合组合“校验 / 保存 / 发布”;`header` 可完整替换 Header。它们都通过 React Portal 在当前组件树内渲染,保留 Context 和生命周期。默认 Header 的最佳视图只保留在底部缩放区。
|
|
106
|
-
|
|
107
|
-
`actions.validate()` 以 `toolbar` 来源先更新 Nova 默认状态栏,再调用 `onValidation` 并返回 issues;Ref `validate()` 使用 `api` 来源。事件的 `valid` 仅由 error 决定,warning 会保留在 `issues` 中但不默认阻止发布。保存与发布仍由宿主负责。
|
|
108
|
-
|
|
109
|
-
```js
|
|
110
|
-
async function publishProcess(actions) {
|
|
111
|
-
const issues = actions.validate()
|
|
112
|
-
if (issues.some((issue) => issue.level === 'error')) return
|
|
113
|
-
await publishXml(actions.exportXml())
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
宿主属性面板以 `onSelectionChange` 为选择状态来源;它覆盖节点、连线、多选、清空与键盘选择。`onElementClick` 只是点击观察事件,不能代替选择状态。宿主通过稳定 BPMN Element ID 关联业务配置,并自行负责保存、发布、权限与服务端事务。
|
|
118
|
-
|
|
119
|
-
## Mode 双向同步与副标题刷新
|
|
120
|
-
|
|
121
|
-
```jsx
|
|
122
|
-
import { useCallback, useRef, useState } from 'react'
|
|
123
|
-
|
|
124
|
-
const [mode, setMode] = useState('design')
|
|
125
|
-
const summaries = useRef(new Map())
|
|
126
|
-
const resolveSubtitle = useCallback(
|
|
127
|
-
({ node }) => summaries.current.has(node.id)
|
|
128
|
-
? summaries.current.get(node.id)
|
|
129
|
-
: undefined,
|
|
130
|
-
[],
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
<BpmnStudio
|
|
134
|
-
ref={studioRef}
|
|
135
|
-
xml={xml}
|
|
136
|
-
mode={mode}
|
|
137
|
-
allowedModes={['design', 'viewer']}
|
|
138
|
-
onModeChange={(event) => setMode(event.mode)}
|
|
139
|
-
nodeSubtitleResolver={resolveSubtitle}
|
|
140
|
-
headerStart={({ mode: actualMode }) => (
|
|
141
|
-
<HostHeader mode={actualMode} />
|
|
142
|
-
)}
|
|
143
|
-
/>
|
|
144
|
-
|
|
145
|
-
function updateArchiveSummary() {
|
|
146
|
-
summaries.current.set('ServiceTask_Archive', '归档到采购系统')
|
|
147
|
-
studioRef.current?.refreshPresentation()
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
Ref 的 `mode` Getter 与 `setMode()` 始终反映 Shell 实际状态。Resolver 返回 `undefined` 保留默认值、`null` 移除副标题行、字符串替换显示;函数引用变化会自动刷新,稳定闭包内部数据变化需显式调用 `refreshPresentation()`。Resolver 只影响 Design/Viewer 标准卡片和 SVG,不覆盖 Instance Runtime Presentation,也不修改 XML、History、Selection 或 Viewport。
|
|
152
|
-
|
|
153
|
-
## 只读展示与审批轨迹
|
|
154
|
-
|
|
155
|
-
```jsx
|
|
156
|
-
import { BpmnViewer } from '@bpmn-nova/react'
|
|
157
|
-
import '@bpmn-nova/react/styles.css'
|
|
158
|
-
|
|
159
|
-
export function ApprovalTrace({ xml, runtime, resolveAsset }) {
|
|
160
|
-
return (
|
|
161
|
-
<div style={{ height: 720 }}>
|
|
162
|
-
<BpmnViewer
|
|
163
|
-
xml={xml}
|
|
164
|
-
engine="flowable"
|
|
165
|
-
runtime={runtime}
|
|
166
|
-
projection="compact"
|
|
167
|
-
responsive
|
|
168
|
-
theme="auto"
|
|
169
|
-
runtimeAssetResolver={resolveAsset}
|
|
170
|
-
onTraceClick={(event) => console.log(event)}
|
|
171
|
-
/>
|
|
172
|
-
</div>
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
- `approval`:实际发生的有效路径。
|
|
178
|
-
- `compact`:移动时间线、审批动作、图片和附件。
|
|
179
|
-
- `standard`:完整 BPMN 叠加运行状态。
|
|
180
|
-
|
|
181
|
-

|
|
182
|
-
|
|
183
|
-
## Runtime Action 与附件
|
|
184
|
-
|
|
185
|
-
```js
|
|
186
|
-
const runtime = {
|
|
187
|
-
processInstanceId: 'purchase-20260824',
|
|
188
|
-
status: 'running',
|
|
189
|
-
activities: [{
|
|
190
|
-
id: 'activity-manager-1',
|
|
191
|
-
elementId: 'UserTask_Manager',
|
|
192
|
-
visitId: 'visit-manager-1',
|
|
193
|
-
status: 'completed',
|
|
194
|
-
assignee: '李经理',
|
|
195
|
-
}],
|
|
196
|
-
actions: [{
|
|
197
|
-
id: 'approve-manager-1',
|
|
198
|
-
type: 'approve',
|
|
199
|
-
elementId: 'UserTask_Manager',
|
|
200
|
-
visitId: 'visit-manager-1',
|
|
201
|
-
occurredAt: '2026-08-24T10:12:00+08:00',
|
|
202
|
-
actor: { id: 'manager-li', name: '李经理' },
|
|
203
|
-
content: {
|
|
204
|
-
plainText: '资料完整,同意提交总经理审批。',
|
|
205
|
-
blocks: [{ type: 'file', assetId: 'purchase-checklist' }],
|
|
206
|
-
assets: [{
|
|
207
|
-
id: 'purchase-checklist',
|
|
208
|
-
name: '采购核验清单.txt',
|
|
209
|
-
mediaType: 'text/plain',
|
|
210
|
-
size: 248,
|
|
211
|
-
}],
|
|
212
|
-
},
|
|
213
|
-
}],
|
|
214
|
-
visitedEdges: [],
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const resolveAsset = async (asset, { purpose, signal }) => {
|
|
218
|
-
const response = await fetch(
|
|
219
|
-
`/api/runtime-assets/${encodeURIComponent(asset.id)}?purpose=${purpose}`,
|
|
220
|
-
{ signal },
|
|
221
|
-
)
|
|
222
|
-
return response.ok ? response.url : null
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Runtime Snapshot 只保存资源引用;上传、存储和权限签发由宿主负责。
|
|
227
|
-
|
|
228
|
-
## 主题和 SVG 导出
|
|
229
|
-
|
|
230
|
-
```jsx
|
|
231
|
-
<BpmnStudio
|
|
232
|
-
ref={studioRef}
|
|
233
|
-
xml={xml}
|
|
234
|
-
theme={{
|
|
235
|
-
mode: 'auto',
|
|
236
|
-
dark: {
|
|
237
|
-
colors: {
|
|
238
|
-
canvas: '#0b1018',
|
|
239
|
-
surface: '#18202b',
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
}}
|
|
243
|
-
/>
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
```js
|
|
247
|
-
studioRef.current?.openSvgExportPreview({
|
|
248
|
-
theme: 'current',
|
|
249
|
-
filename: '采购申请审批流程.svg',
|
|
250
|
-
})
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
主题属性变化会更新现有实例,不会重新挂载画布、清除选择或关闭详情。
|
|
254
|
-
|
|
255
|
-
## Properties 与自定义 Renderer
|
|
256
|
-
|
|
257
|
-
本包还导出:
|
|
258
|
-
|
|
259
|
-
- `BpmnDesigner`、`BpmnCanvas`、`BpmnPalettePanel`、`BpmnPropertiesPanel`
|
|
260
|
-
- `useBpmnStudio`
|
|
261
|
-
- `createReactPropertyComponent`
|
|
262
|
-
- `createReactRuntimeDetailsComponent`
|
|
263
|
-
- `createReactRuntimeTransitionDetailsComponent`
|
|
264
|
-
- `createReactRuntimeTimelineComponent`
|
|
265
|
-
- `createStudioController`、`createEmptyProcess`、`importBpmn`、`exportBpmn`
|
|
266
|
-
- Palette、Properties、Context Menu、Icon 和 Template Registry 的公开创建函数
|
|
267
|
-
|
|
268
|
-
完整工作台可通过 `allowedNodeTypes` 与 `allowedEdgeTypes` 限制宿主支持的图元类型,通过 `allowedModes` 限制可切换的工作台模式。图元限制同时作用于 XML 导入、Palette、连接、快捷新增、模板和节点类型转换。
|
|
269
|
-
|
|
270
|
-
## 常见问题
|
|
52
|
+
`initialXml` 由宿主提供。组件负责实例生命周期;SSR 应用只在客户端挂载。`xml` / `model` 用于外部替换输入,事件中的 XML 是草稿输出。
|
|
271
53
|
|
|
272
|
-
|
|
273
|
-
- **无样式:** 导入一次 `@bpmn-nova/react/styles.css`。
|
|
274
|
-
- **Hooks/React 重复:** React 与 React DOM 应由宿主提供并保持单实例。
|
|
275
|
-
- **SSR:** 将可视组件放到客户端挂载阶段。
|
|
276
|
-
- **包选择:** React 项目只直接安装 `@bpmn-nova/react`。
|
|
54
|
+
## 完整文档
|
|
277
55
|
|
|
278
|
-
|
|
56
|
+
- [官网与文档](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/index.md)
|
|
57
|
+
- [React 接入、宿主组合和完整示例](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/guide/react.md)
|
|
58
|
+
- [组件参数与事件](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/components/index.md)、[公开 API](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/api/index.md)
|
|
59
|
+
- [审批轨迹接入](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/guide/runtime.md)、[自定义扩展](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/customization/index.md)
|
|
60
|
+
- [更新日志](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/src/changelog.md)
|
|
279
61
|
|
|
280
|
-
|
|
281
|
-
- npm 包内 `llms-full.txt`:完整 AI 上下文
|
|
282
|
-
- [完整项目能力](https://github.com/daxiangme/bpmn-nova)
|
|
283
|
-
- [React/Vue 组件说明](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/COMPONENTS.md)
|
|
284
|
-
- [公开 Interface](https://github.com/daxiangme/bpmn-nova/blob/dev/docs/API.md)
|
|
62
|
+
官网尚未部署,以上链接暂指向仓库文档。AI 接入从随包的 `llms.txt` 开始;`llms-full.txt` 提供完整离线上下文,两份文件都包含在 npm tarball 中。
|
|
285
63
|
|
|
286
64
|
## License
|
|
287
65
|
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ import type { ProcessInstanceSnapshot } from '@bpmn-nova/studio/runtime'
|
|
|
25
25
|
import type { SvgExportArtifact, SvgExportOptions, SvgExportPreviewController } from '@bpmn-nova/studio/export-svg'
|
|
26
26
|
import type {
|
|
27
27
|
BpmnCanvas as CanvasInstance,
|
|
28
|
+
BpmnStudioConfig,
|
|
28
29
|
BpmnStudioController,
|
|
29
30
|
BpmnStudioShell,
|
|
30
31
|
ContextMenuRegistry,
|
|
@@ -34,12 +35,17 @@ import type {
|
|
|
34
35
|
StudioShellActions,
|
|
35
36
|
StudioMode,
|
|
36
37
|
StudioModeChangeEvent,
|
|
38
|
+
StudioPanelSelection,
|
|
39
|
+
StudioSidebarChangeEvent,
|
|
40
|
+
StudioSidebarSide,
|
|
41
|
+
StudioSidebarState,
|
|
37
42
|
StudioValidationEvent,
|
|
38
43
|
StudioValidationIssue,
|
|
39
44
|
StudioShellOptions,
|
|
40
45
|
StudioShellRegions,
|
|
41
46
|
StudioShellSlots,
|
|
42
47
|
StudioState,
|
|
48
|
+
StudioUiContext,
|
|
43
49
|
TemplateRegistry,
|
|
44
50
|
} from '@bpmn-nova/studio'
|
|
45
51
|
import type { NovaThemeInput, NovaThemeState, RuntimeAppearanceOptions } from '@bpmn-nova/studio/theme'
|
|
@@ -83,13 +89,58 @@ export {
|
|
|
83
89
|
registerFlowableProperties,
|
|
84
90
|
} from '@bpmn-nova/studio'
|
|
85
91
|
export type {
|
|
92
|
+
BpmnStudioConfig,
|
|
86
93
|
NodeSubtitleResolver,
|
|
87
94
|
NodeSubtitleResolverContext,
|
|
95
|
+
StudioControlSize,
|
|
96
|
+
StudioModelingConfig,
|
|
97
|
+
StudioPanelSelection,
|
|
98
|
+
StudioRightPanelConfig,
|
|
99
|
+
StudioSidebarChangeEvent,
|
|
100
|
+
StudioSidebarChangeSource,
|
|
101
|
+
StudioSidebarConfig,
|
|
102
|
+
StudioSidebarSide,
|
|
103
|
+
StudioSidebarState,
|
|
104
|
+
StudioSvgExportOptions,
|
|
105
|
+
StudioUiConfig,
|
|
106
|
+
StudioUiContext,
|
|
88
107
|
StudioValidationEvent,
|
|
89
108
|
StudioValidationIssue,
|
|
90
109
|
StudioValidationSource,
|
|
110
|
+
StudioViewerConfig,
|
|
91
111
|
} from '@bpmn-nova/studio'
|
|
92
112
|
|
|
113
|
+
/** Runtime integration facade. Types since 0.3.5-preview; inspectRuntime / formatRuntimeInstant since 0.3.6-preview. */
|
|
114
|
+
export type {
|
|
115
|
+
ActivityInstance,
|
|
116
|
+
ActivityStatus,
|
|
117
|
+
ActivityVisitState,
|
|
118
|
+
ProcessInstanceSnapshot,
|
|
119
|
+
RuntimeApprovalAction,
|
|
120
|
+
RuntimeApprovalActionType,
|
|
121
|
+
RuntimeApprovalContent,
|
|
122
|
+
RuntimeApprovalContentBlock,
|
|
123
|
+
RuntimeAssetRef,
|
|
124
|
+
RuntimeDiagnostic,
|
|
125
|
+
RuntimeEdgeVisit,
|
|
126
|
+
RuntimeInstant,
|
|
127
|
+
RuntimeParticipant,
|
|
128
|
+
RuntimeTransition,
|
|
129
|
+
} from '@bpmn-nova/studio/runtime'
|
|
130
|
+
export {
|
|
131
|
+
activityState,
|
|
132
|
+
createRuntimePresentation,
|
|
133
|
+
formatRuntimeInstant,
|
|
134
|
+
inspectRuntime,
|
|
135
|
+
normalizeRuntime,
|
|
136
|
+
parseRuntimeInstant,
|
|
137
|
+
} from '@bpmn-nova/studio/runtime'
|
|
138
|
+
export type {
|
|
139
|
+
RuntimeAssetPurpose,
|
|
140
|
+
RuntimeAssetResolver,
|
|
141
|
+
RuntimeTraceClickEvent,
|
|
142
|
+
} from '@bpmn-nova/studio/viewer'
|
|
143
|
+
|
|
93
144
|
export interface BpmnNovaVisualProps {
|
|
94
145
|
theme?: NovaThemeInput
|
|
95
146
|
runtimeAppearance?: RuntimeAppearanceOptions
|
|
@@ -104,8 +155,12 @@ export interface BpmnModelProps {
|
|
|
104
155
|
}
|
|
105
156
|
export interface UseBpmnStudioOptions extends BpmnModelProps {
|
|
106
157
|
studio?: BpmnStudioController
|
|
158
|
+
config?: BpmnStudioConfig
|
|
159
|
+
/** @deprecated Use config.modeling.propertiesProfile. */
|
|
107
160
|
propertiesProfile?: 'business' | 'developer'
|
|
161
|
+
/** @deprecated Use config.modeling.allowedNodeTypes. */
|
|
108
162
|
allowedNodeTypes?: NodeType[]
|
|
163
|
+
/** @deprecated Use config.modeling.allowedEdgeTypes. */
|
|
109
164
|
allowedEdgeTypes?: EdgeType[]
|
|
110
165
|
}
|
|
111
166
|
export interface UseBpmnStudioResult { studio: BpmnStudioController; state: StudioState; commands: StudioCommands }
|
|
@@ -152,8 +207,12 @@ export const BpmnPalettePanel: ForwardRefExoticComponent<BpmnPalettePanelProps &
|
|
|
152
207
|
|
|
153
208
|
export interface BpmnStudioProps extends BpmnNovaVisualProps, BpmnModelProps {
|
|
154
209
|
studio?: BpmnStudioController
|
|
210
|
+
config?: BpmnStudioConfig
|
|
211
|
+
/** @deprecated Use config.modeling.propertiesProfile. */
|
|
155
212
|
propertiesProfile?: 'business' | 'developer'
|
|
213
|
+
/** @deprecated Use config.modeling.allowedNodeTypes. */
|
|
156
214
|
allowedNodeTypes?: NodeType[]
|
|
215
|
+
/** @deprecated Use config.modeling.allowedEdgeTypes. */
|
|
157
216
|
allowedEdgeTypes?: EdgeType[]
|
|
158
217
|
iconRegistry?: IconRegistry
|
|
159
218
|
paletteRegistry?: PaletteRegistry
|
|
@@ -165,25 +224,35 @@ export interface BpmnStudioProps extends BpmnNovaVisualProps, BpmnModelProps {
|
|
|
165
224
|
nodeSubtitleResolver?: NodeSubtitleResolver
|
|
166
225
|
slots?: StudioShellSlots
|
|
167
226
|
layout?: StudioShellOptions['layout']
|
|
227
|
+
/** @deprecated Use config.ui.regions. */
|
|
168
228
|
regions?: StudioShellRegions
|
|
169
229
|
headerStart?: BpmnStudioRenderSlot
|
|
170
230
|
headerActions?: BpmnStudioRenderSlot
|
|
171
231
|
header?: BpmnStudioRenderSlot
|
|
232
|
+
right?: BpmnStudioRenderSlot
|
|
172
233
|
runtime?: ProcessInstanceSnapshot | null
|
|
173
234
|
mode?: StudioMode
|
|
174
235
|
allowedModes?: readonly StudioMode[]
|
|
175
236
|
projection?: ViewerProjection
|
|
237
|
+
/** @deprecated Use config.viewer.responsive. */
|
|
176
238
|
responsive?: boolean
|
|
239
|
+
/** @deprecated Use config.viewer.projectionOptions. */
|
|
177
240
|
projectionOptions?: Array<{ value: ViewerProjection; label: string }>
|
|
241
|
+
/** @deprecated Use config.ui.sidebarWidth.left. */
|
|
178
242
|
leftWidth?: number
|
|
243
|
+
/** @deprecated Use config.ui.sidebarWidth.right. */
|
|
179
244
|
rightWidth?: number
|
|
245
|
+
/** @deprecated Use config.viewer.timeline. */
|
|
180
246
|
timeline?: ViewerOptions['timeline']
|
|
247
|
+
/** @deprecated Use config.viewer.runtimeDetails. */
|
|
181
248
|
runtimeDetails?: ViewerOptions['runtimeDetails']
|
|
249
|
+
/** @deprecated Use config.viewer.runtimeTraceOptions. */
|
|
182
250
|
runtimeTraceOptions?: ViewerOptions['runtimeTraceOptions']
|
|
183
251
|
runtimePresenter?: ViewerOptions['runtimePresenter']
|
|
184
252
|
runtimeTraceProjector?: ViewerOptions['runtimeTraceProjector']
|
|
185
253
|
runtimeAssetResolver?: ViewerOptions['runtimeAssetResolver']
|
|
186
254
|
runtimeTimelineRenderer?: RuntimeTimelineRenderer
|
|
255
|
+
/** @deprecated Use config.export. */
|
|
187
256
|
svgExport?: ViewerOptions['svgExport']
|
|
188
257
|
runtimeDetailsRenderer?: RuntimeDetailsRenderer | null
|
|
189
258
|
runtimeTransitionDetailsRenderer?: RuntimeTransitionDetailsRenderer | null
|
|
@@ -197,6 +266,7 @@ export interface BpmnStudioProps extends BpmnNovaVisualProps, BpmnModelProps {
|
|
|
197
266
|
onScopeChange?: (activeScopeId: string, scopePath: StudioState['scopePath'], state: StudioState) => void
|
|
198
267
|
onModeChange?: (event: StudioModeChangeEvent) => void
|
|
199
268
|
onValidation?: (event: StudioValidationEvent) => void
|
|
269
|
+
onSidebarChange?: (event: StudioSidebarChangeEvent) => void
|
|
200
270
|
}
|
|
201
271
|
export interface BpmnStudioRenderContext {
|
|
202
272
|
readonly studio: BpmnStudioController
|
|
@@ -204,6 +274,8 @@ export interface BpmnStudioRenderContext {
|
|
|
204
274
|
readonly actions: StudioShellActions
|
|
205
275
|
readonly state: Readonly<StudioState>
|
|
206
276
|
readonly mode: StudioMode
|
|
277
|
+
readonly ui: StudioUiContext
|
|
278
|
+
readonly panelSelection: StudioPanelSelection
|
|
207
279
|
}
|
|
208
280
|
export type BpmnStudioRenderSlot = ReactNode | ((context: BpmnStudioRenderContext) => ReactNode)
|
|
209
281
|
export interface BpmnStudioHandle {
|
|
@@ -211,10 +283,17 @@ export interface BpmnStudioHandle {
|
|
|
211
283
|
readonly shell: BpmnStudioShell | null
|
|
212
284
|
readonly actions: StudioShellActions | null
|
|
213
285
|
readonly mode: StudioMode | undefined
|
|
286
|
+
getConfig(): Readonly<BpmnStudioConfig> | undefined
|
|
287
|
+
setConfig(config?: BpmnStudioConfig): Readonly<BpmnStudioConfig> | undefined
|
|
214
288
|
getMode(): StudioMode | undefined
|
|
215
289
|
getAllowedModes(): readonly StudioMode[]
|
|
216
290
|
setMode(mode: StudioMode): boolean
|
|
217
291
|
setAllowedModes(modes: readonly StudioMode[]): readonly StudioMode[]
|
|
292
|
+
getSidebarState(): StudioSidebarState | undefined
|
|
293
|
+
setSidebarCollapsed(side: StudioSidebarSide, collapsed: boolean): boolean
|
|
294
|
+
subscribeSidebarChange(listener: (event: StudioSidebarChangeEvent) => void): () => void
|
|
295
|
+
getPanelSelection(): StudioPanelSelection | undefined
|
|
296
|
+
subscribePanelSelection(listener: (selection: StudioPanelSelection) => void): () => void
|
|
218
297
|
refreshPresentation(): void
|
|
219
298
|
validate(): StudioValidationIssue[]
|
|
220
299
|
exportXml(engine?: EngineId): string
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,15 @@ import { createEmptyProcess } from '@bpmn-nova/studio';
|
|
|
7
7
|
import { BpmnCanvas as CoreCanvas, BpmnStudioShell as CoreStudioShell, createInteractionController, createStudioController, createTemplateRegistry } from '@bpmn-nova/studio';
|
|
8
8
|
import { createDefaultPaletteRegistry, PalettePanel as CorePalettePanel } from '@bpmn-nova/studio';
|
|
9
9
|
|
|
10
|
+
export {
|
|
11
|
+
activityState,
|
|
12
|
+
createRuntimePresentation,
|
|
13
|
+
formatRuntimeInstant,
|
|
14
|
+
inspectRuntime,
|
|
15
|
+
normalizeRuntime,
|
|
16
|
+
parseRuntimeInstant,
|
|
17
|
+
} from '@bpmn-nova/studio/runtime';
|
|
18
|
+
|
|
10
19
|
export {
|
|
11
20
|
createContextMenuRegistry,
|
|
12
21
|
createDefaultContextMenuRegistry,
|
|
@@ -51,7 +60,7 @@ function resolveModel({ model, xml, engine = 'flowable' }) {
|
|
|
51
60
|
|
|
52
61
|
export function useBpmnStudio(options = {}) {
|
|
53
62
|
const ownedRef = useRef(null);
|
|
54
|
-
if (!options.studio && !ownedRef.current) ownedRef.current = createStudioController({ model: resolveModel(options), propertiesProfile: options.propertiesProfile, allowedNodeTypes: options.allowedNodeTypes, allowedEdgeTypes: options.allowedEdgeTypes });
|
|
63
|
+
if (!options.studio && !ownedRef.current) ownedRef.current = createStudioController({ model: resolveModel(options), config: options.config, propertiesProfile: options.propertiesProfile, allowedNodeTypes: options.allowedNodeTypes, allowedEdgeTypes: options.allowedEdgeTypes });
|
|
55
64
|
const studio = options.studio || ownedRef.current;
|
|
56
65
|
const [state, setState] = useState(() => studio.getState());
|
|
57
66
|
useEffect(() => studio.subscribe((event) => setState(event.state)), [studio]);
|
|
@@ -111,12 +120,21 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
111
120
|
const [headerPortal, setHeaderPortal] = useState(null);
|
|
112
121
|
const [headerStartPortal, setHeaderStartPortal] = useState(null);
|
|
113
122
|
const [headerActionsPortal, setHeaderActionsPortal] = useState(null);
|
|
123
|
+
const [rightPortal, setRightPortal] = useState(null);
|
|
124
|
+
const nativeRightSlotRef = useRef(null);
|
|
125
|
+
if (!nativeRightSlotRef.current) nativeRightSlotRef.current = (context) => {
|
|
126
|
+
const portal = { target: context.container, context };
|
|
127
|
+
setRightPortal(portal);
|
|
128
|
+
return () => setRightPortal((current) => current === portal ? null : current);
|
|
129
|
+
};
|
|
114
130
|
const callbacksRef = useRef({});
|
|
115
131
|
callbacksRef.current = props;
|
|
116
|
-
if (!props.studio && !ownedStudioRef.current) ownedStudioRef.current = createStudioController({ model: resolveModel(props), propertiesProfile: props.propertiesProfile, allowedNodeTypes: props.allowedNodeTypes, allowedEdgeTypes: props.allowedEdgeTypes });
|
|
132
|
+
if (!props.studio && !ownedStudioRef.current) ownedStudioRef.current = createStudioController({ model: resolveModel(props), config: props.config, propertiesProfile: props.propertiesProfile, allowedNodeTypes: props.allowedNodeTypes, allowedEdgeTypes: props.allowedEdgeTypes });
|
|
117
133
|
const studio = props.studio || ownedStudioRef.current;
|
|
118
134
|
const [studioState, setStudioState] = useState(() => studio.getState());
|
|
119
135
|
const [actualMode, setActualMode] = useState(() => props.mode || props.allowedModes?.[0] || 'design');
|
|
136
|
+
const [actualUi, setActualUi] = useState(() => Object.freeze({ controlSize: 'medium', controlHeight: '32px' }));
|
|
137
|
+
const [panelSelection, setPanelSelection] = useState(() => Object.freeze({ selection: studio.selection, selectedElement: studio.getSelectedElement(), trace: null }));
|
|
120
138
|
const subtitleResolverRef = useRef(props.nodeSubtitleResolver);
|
|
121
139
|
subtitleResolverRef.current = props.nodeSubtitleResolver;
|
|
122
140
|
const stableSubtitleResolverRef = useRef((context) => subtitleResolverRef.current?.(context));
|
|
@@ -124,11 +142,22 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
124
142
|
studio,
|
|
125
143
|
get shell() { return shellRef.current; },
|
|
126
144
|
get actions() { return shellRef.current?.actions || null; },
|
|
145
|
+
getConfig: () => shellRef.current?.getConfig(),
|
|
146
|
+
setConfig: (config) => {
|
|
147
|
+
const result = shellRef.current?.setConfig(config);
|
|
148
|
+
if (shellRef.current) setActualUi(Object.freeze({ ...shellRef.current.ui }));
|
|
149
|
+
return result;
|
|
150
|
+
},
|
|
127
151
|
get mode() { return shellRef.current?.getMode(); },
|
|
128
152
|
getMode: () => shellRef.current?.getMode(),
|
|
129
153
|
getAllowedModes: () => shellRef.current?.getAllowedModes() || [],
|
|
130
154
|
setMode: (mode) => shellRef.current?.setMode(mode) || false,
|
|
131
155
|
setAllowedModes: (modes) => shellRef.current?.setAllowedModes(modes) || [],
|
|
156
|
+
getSidebarState: () => shellRef.current?.getSidebarState(),
|
|
157
|
+
setSidebarCollapsed: (side, collapsed) => shellRef.current?.setSidebarCollapsed(side, collapsed) || false,
|
|
158
|
+
subscribeSidebarChange: (listener) => shellRef.current?.subscribeSidebarChange(listener) || (() => {}),
|
|
159
|
+
getPanelSelection: () => shellRef.current?.getPanelSelection(),
|
|
160
|
+
subscribePanelSelection: (listener) => shellRef.current?.subscribePanelSelection(listener) || (() => {}),
|
|
132
161
|
refreshPresentation: () => shellRef.current?.refreshPresentation(),
|
|
133
162
|
validate: () => shellRef.current?.validate() || [],
|
|
134
163
|
exportXml: (engine) => studio.exportXml(engine),
|
|
@@ -140,6 +169,7 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
140
169
|
const hasNativeHeader = props.header !== undefined && props.header !== null;
|
|
141
170
|
const hasNativeHeaderStart = props.headerStart !== undefined && props.headerStart !== null;
|
|
142
171
|
const hasNativeHeaderActions = props.headerActions !== undefined && props.headerActions !== null;
|
|
172
|
+
const hasNativeRight = props.right !== undefined && props.right !== null;
|
|
143
173
|
useEffect(() => {
|
|
144
174
|
if (!hostRef.current) return undefined;
|
|
145
175
|
setStudioState(studio.getState());
|
|
@@ -154,9 +184,11 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
154
184
|
if (hasNativeHeaderStart) shellSlots.headerStart = createPortalSlot(setHeaderStartPortal);
|
|
155
185
|
if (hasNativeHeaderActions) shellSlots.headerActions = createPortalSlot(setHeaderActionsPortal);
|
|
156
186
|
}
|
|
187
|
+
if (hasNativeRight) shellSlots.right = nativeRightSlotRef.current;
|
|
157
188
|
const shell = new CoreStudioShell({
|
|
158
189
|
container: hostRef.current,
|
|
159
190
|
studio,
|
|
191
|
+
config: props.config,
|
|
160
192
|
iconRegistry: props.iconRegistry,
|
|
161
193
|
paletteRegistry: props.paletteRegistry,
|
|
162
194
|
propertiesRegistry: props.propertiesRegistry,
|
|
@@ -192,6 +224,9 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
192
224
|
projectionOptions: props.projectionOptions,
|
|
193
225
|
leftWidth: props.leftWidth,
|
|
194
226
|
rightWidth: props.rightWidth,
|
|
227
|
+
timeline: props.timeline,
|
|
228
|
+
runtimeDetails: props.runtimeDetails,
|
|
229
|
+
runtimeTraceOptions: props.runtimeTraceOptions,
|
|
195
230
|
theme: props.theme,
|
|
196
231
|
runtimeAppearance: props.runtimeAppearance,
|
|
197
232
|
svgExport: props.svgExport,
|
|
@@ -199,19 +234,28 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
199
234
|
});
|
|
200
235
|
shellRef.current = shell;
|
|
201
236
|
setActualMode(shell.getMode());
|
|
237
|
+
setActualUi(Object.freeze({ ...shell.ui }));
|
|
202
238
|
const offMode = shell.subscribeMode((event) => {
|
|
203
239
|
setActualMode(event.mode);
|
|
204
240
|
callbacksRef.current.onModeChange?.(event);
|
|
205
241
|
});
|
|
206
242
|
const offValidation = shell.subscribeValidation((event) => callbacksRef.current.onValidation?.(event));
|
|
243
|
+
setPanelSelection(shell.getPanelSelection());
|
|
244
|
+
const offPanelSelection = shell.subscribePanelSelection(setPanelSelection);
|
|
245
|
+
const offSidebar = shell.subscribeSidebarChange((event) => callbacksRef.current.onSidebarChange?.(event));
|
|
207
246
|
const off = studio.subscribe((event) => {
|
|
208
247
|
setStudioState(event.state);
|
|
209
248
|
if (event.type === 'modelChanged') callbacksRef.current.onChange?.(studio.model, event.reason, studio.exportXml());
|
|
210
249
|
if (event.type === 'selectionChanged') callbacksRef.current.onSelectionChange?.(studio.selection, studio.getSelectedElement());
|
|
211
250
|
if (event.type === 'scopeChanged') callbacksRef.current.onScopeChange?.(event.activeScopeId, event.scopePath, studio.getState());
|
|
212
251
|
});
|
|
213
|
-
return () => { offMode(); offValidation(); off(); shell.destroy(); shellRef.current = null; };
|
|
252
|
+
return () => { offMode(); offValidation(); offPanelSelection(); offSidebar(); off(); shell.destroy(); shellRef.current = null; };
|
|
214
253
|
}, [studio, hasNativeHeader, hasNativeHeaderStart, hasNativeHeaderActions]);
|
|
254
|
+
// The content render function may change each render; only slot ownership
|
|
255
|
+
// changes replace the right mount. Neither case reconstructs the Shell.
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
shellRef.current?._setRightSlot(hasNativeRight ? nativeRightSlotRef.current : props.slots?.right);
|
|
258
|
+
}, [studio, hasNativeRight, props.slots?.right, hasNativeHeader, hasNativeHeaderStart, hasNativeHeaderActions]);
|
|
215
259
|
useEffect(() => () => {
|
|
216
260
|
if (!props.studio && ownedStudioRef.current === studio) {
|
|
217
261
|
studio.destroy();
|
|
@@ -234,6 +278,12 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
234
278
|
if (props.mode) shell.setMode(props.mode);
|
|
235
279
|
}, [props.allowedModes, props.mode]);
|
|
236
280
|
useEffect(() => { if (props.projection) shellRef.current?.setProjection(props.projection); }, [props.projection]);
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
const shell = shellRef.current;
|
|
283
|
+
if (!shell) return;
|
|
284
|
+
shell.setConfig(props.config);
|
|
285
|
+
setActualUi(Object.freeze({ ...shell.ui }));
|
|
286
|
+
}, [props.config]);
|
|
237
287
|
useEffect(() => { if (props.regions !== undefined) shellRef.current?.setRegions(props.regions); }, [props.regions]);
|
|
238
288
|
useEffect(() => { if (props.theme !== undefined) shellRef.current?.setTheme(props.theme); }, [props.theme]);
|
|
239
289
|
useEffect(() => { if (props.runtimeAppearance !== undefined) shellRef.current?.setRuntimeAppearance(props.runtimeAppearance); }, [props.runtimeAppearance]);
|
|
@@ -241,6 +291,7 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
241
291
|
useEffect(() => {
|
|
242
292
|
const shell = shellRef.current;
|
|
243
293
|
if (!shell) return;
|
|
294
|
+
if (props.timeline === undefined && props.runtimeDetails === undefined && props.runtimeTraceOptions === undefined) return;
|
|
244
295
|
shell.rendererOptions.timeline = props.timeline;
|
|
245
296
|
shell.rendererOptions.runtimeDetails = props.runtimeDetails;
|
|
246
297
|
shell.rendererOptions.runtimeTraceOptions = props.runtimeTraceOptions;
|
|
@@ -254,6 +305,8 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
254
305
|
actions: portal.context.actions,
|
|
255
306
|
state: studioState,
|
|
256
307
|
mode: actualMode,
|
|
308
|
+
ui: actualUi,
|
|
309
|
+
panelSelection,
|
|
257
310
|
};
|
|
258
311
|
return createPortal(typeof slot === 'function' ? slot(context) : slot, portal.target, key);
|
|
259
312
|
};
|
|
@@ -262,6 +315,7 @@ export const BpmnStudio = forwardRef(function BpmnStudio(props, ref) {
|
|
|
262
315
|
renderSlot(props.header, headerPortal, 'bpmn-nova-header'),
|
|
263
316
|
renderSlot(props.headerStart, headerStartPortal, 'bpmn-nova-header-start'),
|
|
264
317
|
renderSlot(props.headerActions, headerActionsPortal, 'bpmn-nova-header-actions'),
|
|
318
|
+
renderSlot(props.right, rightPortal, 'bpmn-nova-right'),
|
|
265
319
|
);
|
|
266
320
|
});
|
|
267
321
|
|