sfn-diagram-react 0.4.0 → 0.6.0
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 +154 -13
- package/dist/index.cjs +166 -25
- package/dist/index.d.cts +112 -20
- package/dist/index.d.ts +112 -20
- package/dist/index.js +168 -28
- package/package.json +14 -10
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npm install sfn-diagram-react sfn-diagram
|
|
|
15
15
|
pnpm add sfn-diagram-react sfn-diagram
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
`react` and `sfn-diagram` are peer dependencies.
|
|
18
|
+
`react` and `sfn-diagram` are peer dependencies. `sfn-diagram >= 1.7.0` is required — earlier versions don't have the options this package forwards (`collapse`, `catchHandling`, `showVariables`, `generateHtml`, `edgeOverrides`' qualified-id form, and `layout`/`theme` on the Mermaid path).
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
@@ -58,20 +58,161 @@ emphasized, and per-state duration and retry counts are annotated.
|
|
|
58
58
|
`history` accepts a `GetExecutionHistory` events array, the raw command output, or a
|
|
59
59
|
JSON string of either. In the browser, pass the JSON string.
|
|
60
60
|
|
|
61
|
+
`history` and `before` (diff mode, below) cannot be combined — core has no API for
|
|
62
|
+
overlaying an execution onto a diff. Combining them calls `onError` and renders `null`,
|
|
63
|
+
the same way any other invalid input is handled.
|
|
64
|
+
|
|
65
|
+
**`format="mermaid"` combined with `history` ignores every other diagram option** —
|
|
66
|
+
`catchHandling`, `collapse`, `edgeStyle`, `showVariables`, and the rest. Core's
|
|
67
|
+
Mermaid execution-overlay function (`generateMermaidExecution`) accepts only the ASL
|
|
68
|
+
definition, `history`, `layout`, and `theme`; unlike the plain Mermaid path
|
|
69
|
+
(`generateMermaid`), which honours the full option set. The SVG execution overlay is
|
|
70
|
+
unaffected — it honours every option.
|
|
71
|
+
|
|
72
|
+
### Interactive viewer (`format="html"`)
|
|
73
|
+
|
|
74
|
+
Set `format="html"` to render the full interactive pan/zoom/search viewer instead of
|
|
75
|
+
static markup. Since embedding a complete HTML *document* is only possible in an
|
|
76
|
+
`<iframe>`, that's what this renders — as a sandboxed `srcDoc`, not a same-origin embed.
|
|
77
|
+
Consequences worth knowing:
|
|
78
|
+
|
|
79
|
+
- Page CSS does not reach the iframe's content — style it via the diagram options
|
|
80
|
+
(`theme`, `customColors` are not exposed by this package yet — file an issue if you
|
|
81
|
+
need them) rather than page-level CSS.
|
|
82
|
+
- The iframe has no intrinsic height; size it yourself (`style={{ height: 600 }}`, or a
|
|
83
|
+
wrapper with a fixed aspect ratio).
|
|
84
|
+
- `onStateClick` does not fire from inside it — clicks inside a sandboxed iframe never
|
|
85
|
+
reach the parent document's event handlers.
|
|
86
|
+
- `history` cannot be combined with `format="html"` yet — core's `generateHtml` accepts
|
|
87
|
+
`history` and `diff` overlays since sfn-diagram 1.8, but this hook does not wire them
|
|
88
|
+
up. Combining them calls `onError` and renders `null`.
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<SfnDiagram definition={definition} format="html" style={{ height: 600, width: '100%' }} />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Give the iframe a `title` (defaults to `'Step Functions diagram'`) when a page renders
|
|
95
|
+
more than one, so screen readers can tell them apart.
|
|
96
|
+
|
|
97
|
+
### Diff mode
|
|
98
|
+
|
|
99
|
+
Pass `before` to render a diff instead of a plain diagram: `definition` becomes the
|
|
100
|
+
*after* side and `before` the original side. Added, modified, and removed states are
|
|
101
|
+
coloured (green / yellow / red) instead of the machine's normal styling.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<SfnDiagram before={previousDefinition} definition={definition} />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- `before` accepts an ASL object or a JSON string, same as `definition`.
|
|
108
|
+
- `before` cannot be combined with `history` or `format="html"` — core has no diff API
|
|
109
|
+
for either combination. Combining them calls `onError` and renders `null`.
|
|
110
|
+
- The **SVG diff** (`generateDiff`) honours every diagram option this package forwards
|
|
111
|
+
(`collapse`, `catchHandling`, `showIcons`, and the rest).
|
|
112
|
+
- The **Mermaid diff** (`generateMermaidDiff`) accepts only the two definitions — every
|
|
113
|
+
other prop (`layout`, `theme`, `collapse`, …) is silently ignored in `format="mermaid"`
|
|
114
|
+
diff mode.
|
|
115
|
+
|
|
116
|
+
### `onStateClick`
|
|
117
|
+
|
|
118
|
+
Fires when a state node is clicked, SVG format only:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<SfnDiagram
|
|
122
|
+
definition={definition}
|
|
123
|
+
onStateClick={({ stateId }) => console.log('clicked', stateId)}
|
|
124
|
+
/>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- Not invoked for `format="mermaid"` (no clickable elements) or `format="html"` (the
|
|
128
|
+
sandboxed iframe's clicks never reach the parent document).
|
|
129
|
+
- `stateId` is the graph node id, which equals the state name for every state whose
|
|
130
|
+
name is unique across the machine. A nested state whose name repeats elsewhere in the
|
|
131
|
+
machine gets an id qualified by its scope — read it off the rendered `data-state-id`
|
|
132
|
+
attribute rather than assuming the bare name.
|
|
133
|
+
- No click listener is attached at all when `onStateClick` is omitted.
|
|
134
|
+
|
|
135
|
+
### Imperative handle
|
|
136
|
+
|
|
137
|
+
`SfnDiagram` forwards a `ref` exposing `{ getSvg(): string | null }` — the rendered SVG
|
|
138
|
+
markup for `format="svg"`, or `null` for every other format (including an errored
|
|
139
|
+
render):
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
const ref = useRef<SfnDiagramHandle>(null)
|
|
143
|
+
<SfnDiagram definition={definition} ref={ref} />
|
|
144
|
+
// later: ref.current?.getSvg()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
There is no programmatic zoom/pan control — core doesn't expose the interactive
|
|
148
|
+
viewer's controller from a public subpath yet, so a meaningful pan/zoom handle needs a
|
|
149
|
+
core change first.
|
|
150
|
+
|
|
151
|
+
### `useSfnDiagram`
|
|
152
|
+
|
|
153
|
+
For the raw output — the SVG/HTML string, Mermaid code, dimensions, and metadata —
|
|
154
|
+
without mounting anything, use the hook `<SfnDiagram>` is built on:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { useSfnDiagram } from 'sfn-diagram-react'
|
|
158
|
+
|
|
159
|
+
function DownloadSvgButton({ definition }: { definition: object }) {
|
|
160
|
+
const result = useSfnDiagram({ definition })
|
|
161
|
+
if (result.type !== 'svg') return null
|
|
162
|
+
|
|
163
|
+
const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
|
|
164
|
+
return (
|
|
165
|
+
<a download="diagram.svg" href={href}>
|
|
166
|
+
Download SVG
|
|
167
|
+
</a>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
It accepts the same option-bearing props as `<SfnDiagram>` (`before`, `definition`,
|
|
173
|
+
`format`, `history`, plus every diagram option below) — just not the presentational
|
|
174
|
+
ones (`className`, `onError`, `onStateClick`, `style`, `title`). Errors are returned as
|
|
175
|
+
`{ type: 'error', error }` rather than thrown.
|
|
176
|
+
|
|
61
177
|
## Props
|
|
62
178
|
|
|
63
|
-
| Prop | Type | Default | Description |
|
|
64
|
-
| --- | --- | --- | --- |
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
|
|
74
|
-
|
|
179
|
+
| Prop | Type | Default | Applies to | Description |
|
|
180
|
+
| --- | --- | --- | --- | --- |
|
|
181
|
+
| `before` | `object \| string` | — | svg, mermaid | The *original* side of a diff; `definition` becomes the *after* side. See [Diff mode](#diff-mode). |
|
|
182
|
+
| `catchHandling` | `'hide' \| 'show'` | `'show'` | svg, mermaid¹ | `'hide'` drops Catch error edges and handler-only nodes. |
|
|
183
|
+
| `className` | `string` | — | all | Applied to the wrapper element (`div`, `pre`, or `iframe`). |
|
|
184
|
+
| `collapse` | `string[] \| boolean` | — | svg, mermaid¹ | Collapse Parallel/Map containers into a placeholder node. `true` collapses every container; an array collapses only the named ones. |
|
|
185
|
+
| `definition` | `object \| string` | — (required) | all | ASL definition as an object or JSON string. |
|
|
186
|
+
| `edgeOverrides` | `Record<string, EdgeStyleOverride>` | — | svg only | Per-edge style overrides keyed by `GraphEdge.id` (preferred, e.g. `Route->Work#choice#1`) or the legacy bare `${from}->${to}` (broad-matches every edge between that pair). |
|
|
187
|
+
| `edgeStyle` | `'straight' \| 'curved' \| 'orthogonal'` | `'curved'` | svg, mermaid¹ | Edge path rendering style. |
|
|
188
|
+
| `format` | `'svg' \| 'mermaid' \| 'html'` | `'svg'` | — | Output format. See [Interactive viewer](#interactive-viewer-formathtml) for `'html'`. |
|
|
189
|
+
| `history` | `HistoryEvent[] \| GetExecutionHistoryCommandOutput \| string` | — | svg, mermaid | Execution history; when set, renders an execution overlay. See [Execution overlay](#execution-overlay). |
|
|
190
|
+
| `iconPosition` | `'left' \| 'top' \| 'right'` | `'left'` | svg only | Position of AWS service icons relative to the node label. |
|
|
191
|
+
| `iconSize` | `number` | `24` | svg only | Size of AWS service icons in pixels. |
|
|
192
|
+
| `layout` | `'TB' \| 'LR' \| 'RL' \| 'BT'` | `'TB'` | svg, mermaid | Graph layout direction. |
|
|
193
|
+
| `nodeOverrides` | `Record<string, Partial<NodeStyle>>` | — | svg only | Per-node style overrides keyed by graph node id. |
|
|
194
|
+
| `onError` | `(error: Error) => void` | — | all | Called when the definition fails to parse/render. |
|
|
195
|
+
| `onStateClick` | `(params: { event, stateId }) => void` | — | svg only | See [`onStateClick`](#onstateclick). |
|
|
196
|
+
| `showIcons` | `boolean` | `false` | svg only | Whether to display AWS service icons on Task state nodes. |
|
|
197
|
+
| `showVariables` | `boolean` | `true` | svg, mermaid¹ | Whether to annotate nodes with the variables they assign via ASL `Assign`. |
|
|
198
|
+
| `style` | `React.CSSProperties` | — | all | Applied to the wrapper element. |
|
|
199
|
+
| `theme` | `'light' \| 'dark' \| CustomTheme` | `'light'` | svg, mermaid | Diagram theme. |
|
|
200
|
+
| `title` | `string` | `'Step Functions diagram'` | html only | Accessible title for the rendered iframe. |
|
|
201
|
+
|
|
202
|
+
¹ Ignored when `format="mermaid"` is combined with `history` — see
|
|
203
|
+
[Execution overlay](#execution-overlay). Applies normally to plain `format="mermaid"`
|
|
204
|
+
(no `history`). `format="mermaid"` diff mode (the `before` prop) is a separate
|
|
205
|
+
carve-out with its own rule — see [Diff mode](#diff-mode).
|
|
206
|
+
|
|
207
|
+
When the definition is invalid the component renders `null` and (if provided) calls
|
|
208
|
+
`onError`.
|
|
209
|
+
|
|
210
|
+
**Memoize object-valued props.** `collapse` (as an array), `nodeOverrides`,
|
|
211
|
+
`edgeOverrides`, and a `CustomTheme` object for `theme` are all things a consumer might
|
|
212
|
+
inline in JSX. An inline object or array is referentially unstable — a different
|
|
213
|
+
reference on every render — which re-triggers this component's internal memo and a
|
|
214
|
+
full re-layout each time, even though nothing actually changed. Hoist them to a
|
|
215
|
+
constant or wrap them in `useMemo` if you re-render often.
|
|
75
216
|
|
|
76
217
|
## License
|
|
77
218
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,59 +1,200 @@
|
|
|
1
|
+
'use client'
|
|
1
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
3
|
let react = require("react");
|
|
3
4
|
let sfn_diagram = require("sfn-diagram");
|
|
4
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
-
//#region src/
|
|
6
|
-
function
|
|
7
|
-
const
|
|
8
|
-
|
|
6
|
+
//#region src/useSfnDiagram.ts
|
|
7
|
+
function toAslString(params) {
|
|
8
|
+
const { definition } = params;
|
|
9
|
+
return typeof definition === "string" ? definition : JSON.stringify(definition);
|
|
10
|
+
}
|
|
11
|
+
function omitUndefinedValues(options) {
|
|
12
|
+
const result = {};
|
|
13
|
+
for (const key of Object.keys(options)) if (options[key] !== void 0) result[key] = options[key];
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate a Step Functions diagram and return the raw output - SVG or HTML
|
|
18
|
+
* markup, Mermaid code, dimensions, and metadata - without rendering it.
|
|
19
|
+
*
|
|
20
|
+
* This is the same generation logic `<SfnDiagram>` runs internally, exposed
|
|
21
|
+
* directly for a consumer who needs the underlying string or metadata rather
|
|
22
|
+
* than mounted markup - for example, building a data URI for `<img src>`, a
|
|
23
|
+
* download button, or a custom render target.
|
|
24
|
+
*
|
|
25
|
+
* @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
|
|
26
|
+
* the presentational ones (`className`, `onError`, `onStateClick`, `style`,
|
|
27
|
+
* `title`).
|
|
28
|
+
* @returns A {@link SfnDiagramResult} discriminated union. Errors are returned
|
|
29
|
+
* as `{ type: 'error', error }` rather than thrown.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* function DownloadSvgButton({ definition }: { definition: object }) {
|
|
34
|
+
* const result = useSfnDiagram({ definition })
|
|
35
|
+
* if (result.type !== 'svg') return null
|
|
36
|
+
*
|
|
37
|
+
* const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
|
|
38
|
+
* return <a download="diagram.svg" href={href}>Download SVG</a>
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
function useSfnDiagram(params) {
|
|
43
|
+
const { before, catchHandling, collapse, definition, edgeOverrides, edgeStyle, format = "svg", history, iconPosition, iconSize, layout = "TB", nodeOverrides, showIcons, showVariables, theme = "light" } = params;
|
|
44
|
+
const asl = (0, react.useMemo)(() => toAslString({ definition }), [definition]);
|
|
45
|
+
const beforeAsl = (0, react.useMemo)(() => before === void 0 ? void 0 : toAslString({ definition: before }), [before]);
|
|
46
|
+
return (0, react.useMemo)(() => {
|
|
9
47
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
48
|
+
const diagramOptions = omitUndefinedValues({
|
|
49
|
+
catchHandling,
|
|
50
|
+
collapse,
|
|
51
|
+
edgeOverrides,
|
|
52
|
+
edgeStyle,
|
|
53
|
+
iconPosition,
|
|
54
|
+
iconSize,
|
|
55
|
+
layout,
|
|
56
|
+
nodeOverrides,
|
|
57
|
+
showIcons,
|
|
58
|
+
showVariables,
|
|
59
|
+
theme
|
|
60
|
+
});
|
|
61
|
+
if (beforeAsl !== void 0) {
|
|
62
|
+
if (history) throw new Error("history and before cannot be combined");
|
|
63
|
+
if (format === "html") throw new Error("before and format=\"html\" cannot be combined");
|
|
64
|
+
if (format === "mermaid") {
|
|
65
|
+
const output = (0, sfn_diagram.generateMermaidDiff)({
|
|
66
|
+
after: asl,
|
|
67
|
+
before: beforeAsl
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
code: output.code,
|
|
71
|
+
metadata: output.metadata,
|
|
72
|
+
type: "mermaid"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const output = (0, sfn_diagram.generateDiff)({
|
|
76
|
+
after: asl,
|
|
77
|
+
before: beforeAsl,
|
|
78
|
+
...diagramOptions
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
height: output.height,
|
|
82
|
+
metadata: output.metadata,
|
|
83
|
+
svg: output.svg,
|
|
84
|
+
type: "svg",
|
|
85
|
+
width: output.width
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (format === "html") {
|
|
89
|
+
if (history) throw new Error("history and format=\"html\" cannot be combined");
|
|
90
|
+
const output = (0, sfn_diagram.generateHtml)({
|
|
13
91
|
aslDefinition: asl,
|
|
14
|
-
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
92
|
+
...diagramOptions
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
height: output.height,
|
|
96
|
+
html: output.html,
|
|
97
|
+
metadata: output.metadata,
|
|
98
|
+
type: "html",
|
|
99
|
+
width: output.width
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (format === "mermaid") {
|
|
103
|
+
const output = history ? (0, sfn_diagram.generateMermaidExecution)({
|
|
20
104
|
aslDefinition: asl,
|
|
21
105
|
history,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}) : (0, sfn_diagram.generateSvg)({
|
|
106
|
+
...diagramOptions
|
|
107
|
+
}) : (0, sfn_diagram.generateMermaid)({
|
|
25
108
|
aslDefinition: asl,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
109
|
+
...diagramOptions
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
code: output.code,
|
|
113
|
+
metadata: output.metadata,
|
|
114
|
+
type: "mermaid"
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const output = history ? (0, sfn_diagram.generateExecution)({
|
|
118
|
+
aslDefinition: asl,
|
|
119
|
+
history,
|
|
120
|
+
...diagramOptions
|
|
121
|
+
}) : (0, sfn_diagram.generateSvg)({
|
|
122
|
+
aslDefinition: asl,
|
|
123
|
+
...diagramOptions
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
height: output.height,
|
|
127
|
+
metadata: output.metadata,
|
|
128
|
+
svg: output.svg,
|
|
129
|
+
type: "svg",
|
|
130
|
+
width: output.width
|
|
29
131
|
};
|
|
30
132
|
} catch (err) {
|
|
31
133
|
return {
|
|
32
|
-
|
|
33
|
-
|
|
134
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
135
|
+
type: "error"
|
|
34
136
|
};
|
|
35
137
|
}
|
|
36
138
|
}, [
|
|
37
139
|
asl,
|
|
140
|
+
beforeAsl,
|
|
141
|
+
catchHandling,
|
|
142
|
+
collapse,
|
|
143
|
+
edgeOverrides,
|
|
144
|
+
edgeStyle,
|
|
38
145
|
format,
|
|
39
146
|
history,
|
|
147
|
+
iconPosition,
|
|
148
|
+
iconSize,
|
|
40
149
|
layout,
|
|
150
|
+
nodeOverrides,
|
|
151
|
+
showIcons,
|
|
152
|
+
showVariables,
|
|
41
153
|
theme
|
|
42
154
|
]);
|
|
43
|
-
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/SfnDiagram.tsx
|
|
158
|
+
const SfnDiagram = (0, react.forwardRef)(function SfnDiagram({ className, onError, onStateClick, style, title = "Step Functions diagram", ...diagramParams }, ref) {
|
|
159
|
+
const result = useSfnDiagram(diagramParams);
|
|
160
|
+
(0, react.useImperativeHandle)(ref, () => ({ getSvg: () => result.type === "svg" ? result.svg : null }), [result]);
|
|
161
|
+
const handleStateClick = (0, react.useCallback)((event) => {
|
|
162
|
+
const stateId = (event.target instanceof Element ? event.target : null)?.closest("[data-state-id]")?.getAttribute("data-state-id");
|
|
163
|
+
if (stateId) onStateClick?.({
|
|
164
|
+
event,
|
|
165
|
+
stateId
|
|
166
|
+
});
|
|
167
|
+
}, [onStateClick]);
|
|
168
|
+
const reportedResult = (0, react.useRef)(null);
|
|
169
|
+
(0, react.useEffect)(() => {
|
|
170
|
+
if (result.type !== "error") {
|
|
171
|
+
reportedResult.current = null;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (reportedResult.current === result) return;
|
|
175
|
+
reportedResult.current = result;
|
|
44
176
|
onError?.(result.error);
|
|
45
|
-
|
|
46
|
-
|
|
177
|
+
}, [onError, result]);
|
|
178
|
+
if (result.type === "error") return null;
|
|
47
179
|
if (result.type === "mermaid") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
48
180
|
className,
|
|
49
181
|
style,
|
|
50
182
|
children: result.code
|
|
51
183
|
});
|
|
184
|
+
if (result.type === "html") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
|
|
185
|
+
className,
|
|
186
|
+
sandbox: "allow-downloads allow-scripts",
|
|
187
|
+
srcDoc: result.html,
|
|
188
|
+
style,
|
|
189
|
+
title
|
|
190
|
+
});
|
|
52
191
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
53
192
|
className,
|
|
54
193
|
dangerouslySetInnerHTML: { __html: result.svg },
|
|
194
|
+
onClick: onStateClick ? handleStateClick : void 0,
|
|
55
195
|
style
|
|
56
196
|
});
|
|
57
|
-
}
|
|
197
|
+
});
|
|
58
198
|
//#endregion
|
|
59
199
|
exports.SfnDiagram = SfnDiagram;
|
|
200
|
+
exports.useSfnDiagram = useSfnDiagram;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,33 +1,125 @@
|
|
|
1
|
-
import * as _$react from "react";
|
|
2
|
-
import { ExecutionHistoryInput, LayoutDirection, ThemeOption } from "sfn-diagram";
|
|
3
1
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { DiagramOptions, DiffOutput, ExecutionHistoryInput, ExecutionOutput, HtmlOutput, MermaidDiffOutput, MermaidExecutionOutput, MermaidOutput, SvgOutput } from "sfn-diagram";
|
|
3
|
+
//#region src/useSfnDiagram.d.ts
|
|
4
|
+
interface UseSfnDiagramParams extends Pick<DiagramOptions, 'catchHandling' | 'collapse' | 'edgeOverrides' | 'edgeStyle' | 'iconPosition' | 'iconSize' | 'layout' | 'nodeOverrides' | 'showIcons' | 'showVariables' | 'theme'> {
|
|
5
|
+
/**
|
|
6
|
+
* When set, `definition` is treated as the *after* side of a diff and `before`
|
|
7
|
+
* as the *original* side: the rendered diagram highlights added, modified, and
|
|
8
|
+
* removed states rather than the plain machine. Accepts an ASL object or a
|
|
9
|
+
* JSON string. Cannot be combined with `history` (core's SVG and Mermaid diff
|
|
10
|
+
* functions take no history) or with `format="html"` (core's `generateHtml`
|
|
11
|
+
* accepts a diff since sfn-diagram 1.8, but this hook does not wire it up yet).
|
|
12
|
+
*
|
|
13
|
+
* The Mermaid diff (`generateMermaidDiff`) ignores every option in this
|
|
14
|
+
* type's `Pick<DiagramOptions, ...>` list - `layout`, `theme`, and the rest -
|
|
15
|
+
* since core's diff-specific function accepts only the two definitions. The
|
|
16
|
+
* SVG diff honours them.
|
|
17
|
+
*/
|
|
18
|
+
before?: object | string;
|
|
7
19
|
definition: object | string;
|
|
8
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Output format. `'svg'` and `'mermaid'` render static markup; `'html'` renders
|
|
22
|
+
* the interactive pan/zoom/search viewer as a self-contained HTML document.
|
|
23
|
+
* Cannot be combined with `history`.
|
|
24
|
+
* @default 'svg'
|
|
25
|
+
*/
|
|
26
|
+
format?: 'html' | 'mermaid' | 'svg';
|
|
9
27
|
/**
|
|
10
28
|
* Optional execution history. When provided, the diagram is rendered as an
|
|
11
29
|
* execution overlay: states are coloured by outcome, the taken path is
|
|
12
30
|
* emphasized, and per-state duration / retry counts are annotated.
|
|
13
31
|
* Accepts a GetExecutionHistory events array, the raw command output, or a
|
|
14
|
-
* JSON string of either.
|
|
32
|
+
* JSON string of either. Cannot be combined with `format="html"`.
|
|
15
33
|
*/
|
|
16
34
|
history?: ExecutionHistoryInput;
|
|
17
|
-
|
|
35
|
+
}
|
|
36
|
+
/** Result of {@link useSfnDiagram} - the raw output of whichever core function the params select. */
|
|
37
|
+
type SfnDiagramResult = {
|
|
38
|
+
error: Error;
|
|
39
|
+
type: 'error';
|
|
40
|
+
} | {
|
|
41
|
+
height: number;
|
|
42
|
+
html: string;
|
|
43
|
+
metadata: HtmlOutput['metadata'];
|
|
44
|
+
type: 'html';
|
|
45
|
+
width: number;
|
|
46
|
+
} | {
|
|
47
|
+
code: string;
|
|
48
|
+
metadata: MermaidDiffOutput['metadata'] | MermaidExecutionOutput['metadata'] | MermaidOutput['metadata'];
|
|
49
|
+
type: 'mermaid';
|
|
50
|
+
} | {
|
|
51
|
+
height: number;
|
|
52
|
+
metadata: DiffOutput['metadata'] | ExecutionOutput['metadata'] | SvgOutput['metadata'];
|
|
53
|
+
svg: string;
|
|
54
|
+
type: 'svg';
|
|
55
|
+
width: number;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Generate a Step Functions diagram and return the raw output - SVG or HTML
|
|
59
|
+
* markup, Mermaid code, dimensions, and metadata - without rendering it.
|
|
60
|
+
*
|
|
61
|
+
* This is the same generation logic `<SfnDiagram>` runs internally, exposed
|
|
62
|
+
* directly for a consumer who needs the underlying string or metadata rather
|
|
63
|
+
* than mounted markup - for example, building a data URI for `<img src>`, a
|
|
64
|
+
* download button, or a custom render target.
|
|
65
|
+
*
|
|
66
|
+
* @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
|
|
67
|
+
* the presentational ones (`className`, `onError`, `onStateClick`, `style`,
|
|
68
|
+
* `title`).
|
|
69
|
+
* @returns A {@link SfnDiagramResult} discriminated union. Errors are returned
|
|
70
|
+
* as `{ type: 'error', error }` rather than thrown.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* function DownloadSvgButton({ definition }: { definition: object }) {
|
|
75
|
+
* const result = useSfnDiagram({ definition })
|
|
76
|
+
* if (result.type !== 'svg') return null
|
|
77
|
+
*
|
|
78
|
+
* const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
|
|
79
|
+
* return <a download="diagram.svg" href={href}>Download SVG</a>
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function useSfnDiagram(params: UseSfnDiagramParams): SfnDiagramResult;
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/SfnDiagram.d.ts
|
|
86
|
+
interface SfnDiagramProps extends UseSfnDiagramParams {
|
|
87
|
+
className?: string;
|
|
18
88
|
onError?: (error: Error) => void;
|
|
89
|
+
/**
|
|
90
|
+
* Called when a state node is clicked. SVG format only - a click inside the
|
|
91
|
+
* sandboxed `format="html"` iframe does not reach this handler, and Mermaid
|
|
92
|
+
* output has no clickable elements. `stateId` is the graph node id, which
|
|
93
|
+
* equals the state name for every state whose name is unique across the
|
|
94
|
+
* machine; for a nested state whose name repeats elsewhere in the machine the
|
|
95
|
+
* id is qualified by its scope, so read it off the rendered `data-state-id`
|
|
96
|
+
* rather than assuming the bare name.
|
|
97
|
+
*/
|
|
98
|
+
onStateClick?: (params: OnStateClickParams) => void;
|
|
19
99
|
style?: React.CSSProperties;
|
|
20
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Accessible title for the rendered iframe when `format` is `'html'`. Ignored
|
|
102
|
+
* for every other format. Set this when a page renders more than one diagram
|
|
103
|
+
* so screen readers can distinguish between them.
|
|
104
|
+
* @default 'Step Functions diagram'
|
|
105
|
+
*/
|
|
106
|
+
title?: string;
|
|
107
|
+
}
|
|
108
|
+
/** Parameters passed to {@link SfnDiagramProps.onStateClick}. */
|
|
109
|
+
interface OnStateClickParams {
|
|
110
|
+
event: React.MouseEvent<HTMLDivElement>;
|
|
111
|
+
stateId: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Imperative handle exposed via `ref` on {@link SfnDiagram}. There is no
|
|
115
|
+
* programmatic zoom/pan control - core has no public API for it yet (the
|
|
116
|
+
* interactive viewer's controller is not exported from a public subpath) -
|
|
117
|
+
* this handle only surfaces the rendered markup.
|
|
118
|
+
*/
|
|
119
|
+
interface SfnDiagramHandle {
|
|
120
|
+
/** The rendered SVG markup for `format="svg"`, or `null` for every other format (including an errored render). */
|
|
121
|
+
getSvg(): string | null;
|
|
21
122
|
}
|
|
22
|
-
declare
|
|
23
|
-
className,
|
|
24
|
-
definition,
|
|
25
|
-
format,
|
|
26
|
-
history,
|
|
27
|
-
layout,
|
|
28
|
-
onError,
|
|
29
|
-
style,
|
|
30
|
-
theme
|
|
31
|
-
}: SfnDiagramProps): _$react.JSX.Element | null;
|
|
123
|
+
export declare const SfnDiagram: import("react").ForwardRefExoticComponent<SfnDiagramProps & import("react").RefAttributes<SfnDiagramHandle>>;
|
|
32
124
|
//#endregion
|
|
33
|
-
export {
|
|
125
|
+
export type { OnStateClickParams, SfnDiagramHandle, SfnDiagramProps, SfnDiagramResult, UseSfnDiagramParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,125 @@
|
|
|
1
|
-
import * as _$react from "react";
|
|
2
|
-
import { ExecutionHistoryInput, LayoutDirection, ThemeOption } from "sfn-diagram";
|
|
3
1
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { DiagramOptions, DiffOutput, ExecutionHistoryInput, ExecutionOutput, HtmlOutput, MermaidDiffOutput, MermaidExecutionOutput, MermaidOutput, SvgOutput } from "sfn-diagram";
|
|
3
|
+
//#region src/useSfnDiagram.d.ts
|
|
4
|
+
interface UseSfnDiagramParams extends Pick<DiagramOptions, 'catchHandling' | 'collapse' | 'edgeOverrides' | 'edgeStyle' | 'iconPosition' | 'iconSize' | 'layout' | 'nodeOverrides' | 'showIcons' | 'showVariables' | 'theme'> {
|
|
5
|
+
/**
|
|
6
|
+
* When set, `definition` is treated as the *after* side of a diff and `before`
|
|
7
|
+
* as the *original* side: the rendered diagram highlights added, modified, and
|
|
8
|
+
* removed states rather than the plain machine. Accepts an ASL object or a
|
|
9
|
+
* JSON string. Cannot be combined with `history` (core's SVG and Mermaid diff
|
|
10
|
+
* functions take no history) or with `format="html"` (core's `generateHtml`
|
|
11
|
+
* accepts a diff since sfn-diagram 1.8, but this hook does not wire it up yet).
|
|
12
|
+
*
|
|
13
|
+
* The Mermaid diff (`generateMermaidDiff`) ignores every option in this
|
|
14
|
+
* type's `Pick<DiagramOptions, ...>` list - `layout`, `theme`, and the rest -
|
|
15
|
+
* since core's diff-specific function accepts only the two definitions. The
|
|
16
|
+
* SVG diff honours them.
|
|
17
|
+
*/
|
|
18
|
+
before?: object | string;
|
|
7
19
|
definition: object | string;
|
|
8
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Output format. `'svg'` and `'mermaid'` render static markup; `'html'` renders
|
|
22
|
+
* the interactive pan/zoom/search viewer as a self-contained HTML document.
|
|
23
|
+
* Cannot be combined with `history`.
|
|
24
|
+
* @default 'svg'
|
|
25
|
+
*/
|
|
26
|
+
format?: 'html' | 'mermaid' | 'svg';
|
|
9
27
|
/**
|
|
10
28
|
* Optional execution history. When provided, the diagram is rendered as an
|
|
11
29
|
* execution overlay: states are coloured by outcome, the taken path is
|
|
12
30
|
* emphasized, and per-state duration / retry counts are annotated.
|
|
13
31
|
* Accepts a GetExecutionHistory events array, the raw command output, or a
|
|
14
|
-
* JSON string of either.
|
|
32
|
+
* JSON string of either. Cannot be combined with `format="html"`.
|
|
15
33
|
*/
|
|
16
34
|
history?: ExecutionHistoryInput;
|
|
17
|
-
|
|
35
|
+
}
|
|
36
|
+
/** Result of {@link useSfnDiagram} - the raw output of whichever core function the params select. */
|
|
37
|
+
type SfnDiagramResult = {
|
|
38
|
+
error: Error;
|
|
39
|
+
type: 'error';
|
|
40
|
+
} | {
|
|
41
|
+
height: number;
|
|
42
|
+
html: string;
|
|
43
|
+
metadata: HtmlOutput['metadata'];
|
|
44
|
+
type: 'html';
|
|
45
|
+
width: number;
|
|
46
|
+
} | {
|
|
47
|
+
code: string;
|
|
48
|
+
metadata: MermaidDiffOutput['metadata'] | MermaidExecutionOutput['metadata'] | MermaidOutput['metadata'];
|
|
49
|
+
type: 'mermaid';
|
|
50
|
+
} | {
|
|
51
|
+
height: number;
|
|
52
|
+
metadata: DiffOutput['metadata'] | ExecutionOutput['metadata'] | SvgOutput['metadata'];
|
|
53
|
+
svg: string;
|
|
54
|
+
type: 'svg';
|
|
55
|
+
width: number;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Generate a Step Functions diagram and return the raw output - SVG or HTML
|
|
59
|
+
* markup, Mermaid code, dimensions, and metadata - without rendering it.
|
|
60
|
+
*
|
|
61
|
+
* This is the same generation logic `<SfnDiagram>` runs internally, exposed
|
|
62
|
+
* directly for a consumer who needs the underlying string or metadata rather
|
|
63
|
+
* than mounted markup - for example, building a data URI for `<img src>`, a
|
|
64
|
+
* download button, or a custom render target.
|
|
65
|
+
*
|
|
66
|
+
* @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
|
|
67
|
+
* the presentational ones (`className`, `onError`, `onStateClick`, `style`,
|
|
68
|
+
* `title`).
|
|
69
|
+
* @returns A {@link SfnDiagramResult} discriminated union. Errors are returned
|
|
70
|
+
* as `{ type: 'error', error }` rather than thrown.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* function DownloadSvgButton({ definition }: { definition: object }) {
|
|
75
|
+
* const result = useSfnDiagram({ definition })
|
|
76
|
+
* if (result.type !== 'svg') return null
|
|
77
|
+
*
|
|
78
|
+
* const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
|
|
79
|
+
* return <a download="diagram.svg" href={href}>Download SVG</a>
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function useSfnDiagram(params: UseSfnDiagramParams): SfnDiagramResult;
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/SfnDiagram.d.ts
|
|
86
|
+
interface SfnDiagramProps extends UseSfnDiagramParams {
|
|
87
|
+
className?: string;
|
|
18
88
|
onError?: (error: Error) => void;
|
|
89
|
+
/**
|
|
90
|
+
* Called when a state node is clicked. SVG format only - a click inside the
|
|
91
|
+
* sandboxed `format="html"` iframe does not reach this handler, and Mermaid
|
|
92
|
+
* output has no clickable elements. `stateId` is the graph node id, which
|
|
93
|
+
* equals the state name for every state whose name is unique across the
|
|
94
|
+
* machine; for a nested state whose name repeats elsewhere in the machine the
|
|
95
|
+
* id is qualified by its scope, so read it off the rendered `data-state-id`
|
|
96
|
+
* rather than assuming the bare name.
|
|
97
|
+
*/
|
|
98
|
+
onStateClick?: (params: OnStateClickParams) => void;
|
|
19
99
|
style?: React.CSSProperties;
|
|
20
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Accessible title for the rendered iframe when `format` is `'html'`. Ignored
|
|
102
|
+
* for every other format. Set this when a page renders more than one diagram
|
|
103
|
+
* so screen readers can distinguish between them.
|
|
104
|
+
* @default 'Step Functions diagram'
|
|
105
|
+
*/
|
|
106
|
+
title?: string;
|
|
107
|
+
}
|
|
108
|
+
/** Parameters passed to {@link SfnDiagramProps.onStateClick}. */
|
|
109
|
+
interface OnStateClickParams {
|
|
110
|
+
event: React.MouseEvent<HTMLDivElement>;
|
|
111
|
+
stateId: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Imperative handle exposed via `ref` on {@link SfnDiagram}. There is no
|
|
115
|
+
* programmatic zoom/pan control - core has no public API for it yet (the
|
|
116
|
+
* interactive viewer's controller is not exported from a public subpath) -
|
|
117
|
+
* this handle only surfaces the rendered markup.
|
|
118
|
+
*/
|
|
119
|
+
interface SfnDiagramHandle {
|
|
120
|
+
/** The rendered SVG markup for `format="svg"`, or `null` for every other format (including an errored render). */
|
|
121
|
+
getSvg(): string | null;
|
|
21
122
|
}
|
|
22
|
-
declare
|
|
23
|
-
className,
|
|
24
|
-
definition,
|
|
25
|
-
format,
|
|
26
|
-
history,
|
|
27
|
-
layout,
|
|
28
|
-
onError,
|
|
29
|
-
style,
|
|
30
|
-
theme
|
|
31
|
-
}: SfnDiagramProps): _$react.JSX.Element | null;
|
|
123
|
+
export declare const SfnDiagram: import("react").ForwardRefExoticComponent<SfnDiagramProps & import("react").RefAttributes<SfnDiagramHandle>>;
|
|
32
124
|
//#endregion
|
|
33
|
-
export {
|
|
125
|
+
export type { OnStateClickParams, SfnDiagramHandle, SfnDiagramProps, SfnDiagramResult, UseSfnDiagramParams };
|
package/dist/index.js
CHANGED
|
@@ -1,58 +1,198 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
'use client'
|
|
2
|
+
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react";
|
|
3
|
+
import { generateDiff, generateExecution, generateHtml, generateMermaid, generateMermaidDiff, generateMermaidExecution, generateSvg } from "sfn-diagram";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
//#region src/
|
|
5
|
-
function
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
//#region src/useSfnDiagram.ts
|
|
6
|
+
function toAslString(params) {
|
|
7
|
+
const { definition } = params;
|
|
8
|
+
return typeof definition === "string" ? definition : JSON.stringify(definition);
|
|
9
|
+
}
|
|
10
|
+
function omitUndefinedValues(options) {
|
|
11
|
+
const result = {};
|
|
12
|
+
for (const key of Object.keys(options)) if (options[key] !== void 0) result[key] = options[key];
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate a Step Functions diagram and return the raw output - SVG or HTML
|
|
17
|
+
* markup, Mermaid code, dimensions, and metadata - without rendering it.
|
|
18
|
+
*
|
|
19
|
+
* This is the same generation logic `<SfnDiagram>` runs internally, exposed
|
|
20
|
+
* directly for a consumer who needs the underlying string or metadata rather
|
|
21
|
+
* than mounted markup - for example, building a data URI for `<img src>`, a
|
|
22
|
+
* download button, or a custom render target.
|
|
23
|
+
*
|
|
24
|
+
* @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
|
|
25
|
+
* the presentational ones (`className`, `onError`, `onStateClick`, `style`,
|
|
26
|
+
* `title`).
|
|
27
|
+
* @returns A {@link SfnDiagramResult} discriminated union. Errors are returned
|
|
28
|
+
* as `{ type: 'error', error }` rather than thrown.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* function DownloadSvgButton({ definition }: { definition: object }) {
|
|
33
|
+
* const result = useSfnDiagram({ definition })
|
|
34
|
+
* if (result.type !== 'svg') return null
|
|
35
|
+
*
|
|
36
|
+
* const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
|
|
37
|
+
* return <a download="diagram.svg" href={href}>Download SVG</a>
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
function useSfnDiagram(params) {
|
|
42
|
+
const { before, catchHandling, collapse, definition, edgeOverrides, edgeStyle, format = "svg", history, iconPosition, iconSize, layout = "TB", nodeOverrides, showIcons, showVariables, theme = "light" } = params;
|
|
43
|
+
const asl = useMemo(() => toAslString({ definition }), [definition]);
|
|
44
|
+
const beforeAsl = useMemo(() => before === void 0 ? void 0 : toAslString({ definition: before }), [before]);
|
|
45
|
+
return useMemo(() => {
|
|
8
46
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
47
|
+
const diagramOptions = omitUndefinedValues({
|
|
48
|
+
catchHandling,
|
|
49
|
+
collapse,
|
|
50
|
+
edgeOverrides,
|
|
51
|
+
edgeStyle,
|
|
52
|
+
iconPosition,
|
|
53
|
+
iconSize,
|
|
54
|
+
layout,
|
|
55
|
+
nodeOverrides,
|
|
56
|
+
showIcons,
|
|
57
|
+
showVariables,
|
|
58
|
+
theme
|
|
59
|
+
});
|
|
60
|
+
if (beforeAsl !== void 0) {
|
|
61
|
+
if (history) throw new Error("history and before cannot be combined");
|
|
62
|
+
if (format === "html") throw new Error("before and format=\"html\" cannot be combined");
|
|
63
|
+
if (format === "mermaid") {
|
|
64
|
+
const output = generateMermaidDiff({
|
|
65
|
+
after: asl,
|
|
66
|
+
before: beforeAsl
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
code: output.code,
|
|
70
|
+
metadata: output.metadata,
|
|
71
|
+
type: "mermaid"
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const output = generateDiff({
|
|
75
|
+
after: asl,
|
|
76
|
+
before: beforeAsl,
|
|
77
|
+
...diagramOptions
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
height: output.height,
|
|
81
|
+
metadata: output.metadata,
|
|
82
|
+
svg: output.svg,
|
|
83
|
+
type: "svg",
|
|
84
|
+
width: output.width
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (format === "html") {
|
|
88
|
+
if (history) throw new Error("history and format=\"html\" cannot be combined");
|
|
89
|
+
const output = generateHtml({
|
|
12
90
|
aslDefinition: asl,
|
|
13
|
-
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
91
|
+
...diagramOptions
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
height: output.height,
|
|
95
|
+
html: output.html,
|
|
96
|
+
metadata: output.metadata,
|
|
97
|
+
type: "html",
|
|
98
|
+
width: output.width
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (format === "mermaid") {
|
|
102
|
+
const output = history ? generateMermaidExecution({
|
|
19
103
|
aslDefinition: asl,
|
|
20
104
|
history,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}) : generateSvg({
|
|
105
|
+
...diagramOptions
|
|
106
|
+
}) : generateMermaid({
|
|
24
107
|
aslDefinition: asl,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
108
|
+
...diagramOptions
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
code: output.code,
|
|
112
|
+
metadata: output.metadata,
|
|
113
|
+
type: "mermaid"
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const output = history ? generateExecution({
|
|
117
|
+
aslDefinition: asl,
|
|
118
|
+
history,
|
|
119
|
+
...diagramOptions
|
|
120
|
+
}) : generateSvg({
|
|
121
|
+
aslDefinition: asl,
|
|
122
|
+
...diagramOptions
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
height: output.height,
|
|
126
|
+
metadata: output.metadata,
|
|
127
|
+
svg: output.svg,
|
|
128
|
+
type: "svg",
|
|
129
|
+
width: output.width
|
|
28
130
|
};
|
|
29
131
|
} catch (err) {
|
|
30
132
|
return {
|
|
31
|
-
|
|
32
|
-
|
|
133
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
134
|
+
type: "error"
|
|
33
135
|
};
|
|
34
136
|
}
|
|
35
137
|
}, [
|
|
36
138
|
asl,
|
|
139
|
+
beforeAsl,
|
|
140
|
+
catchHandling,
|
|
141
|
+
collapse,
|
|
142
|
+
edgeOverrides,
|
|
143
|
+
edgeStyle,
|
|
37
144
|
format,
|
|
38
145
|
history,
|
|
146
|
+
iconPosition,
|
|
147
|
+
iconSize,
|
|
39
148
|
layout,
|
|
149
|
+
nodeOverrides,
|
|
150
|
+
showIcons,
|
|
151
|
+
showVariables,
|
|
40
152
|
theme
|
|
41
153
|
]);
|
|
42
|
-
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/SfnDiagram.tsx
|
|
157
|
+
const SfnDiagram = forwardRef(function SfnDiagram({ className, onError, onStateClick, style, title = "Step Functions diagram", ...diagramParams }, ref) {
|
|
158
|
+
const result = useSfnDiagram(diagramParams);
|
|
159
|
+
useImperativeHandle(ref, () => ({ getSvg: () => result.type === "svg" ? result.svg : null }), [result]);
|
|
160
|
+
const handleStateClick = useCallback((event) => {
|
|
161
|
+
const stateId = (event.target instanceof Element ? event.target : null)?.closest("[data-state-id]")?.getAttribute("data-state-id");
|
|
162
|
+
if (stateId) onStateClick?.({
|
|
163
|
+
event,
|
|
164
|
+
stateId
|
|
165
|
+
});
|
|
166
|
+
}, [onStateClick]);
|
|
167
|
+
const reportedResult = useRef(null);
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
if (result.type !== "error") {
|
|
170
|
+
reportedResult.current = null;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (reportedResult.current === result) return;
|
|
174
|
+
reportedResult.current = result;
|
|
43
175
|
onError?.(result.error);
|
|
44
|
-
|
|
45
|
-
|
|
176
|
+
}, [onError, result]);
|
|
177
|
+
if (result.type === "error") return null;
|
|
46
178
|
if (result.type === "mermaid") return /* @__PURE__ */ jsx("pre", {
|
|
47
179
|
className,
|
|
48
180
|
style,
|
|
49
181
|
children: result.code
|
|
50
182
|
});
|
|
183
|
+
if (result.type === "html") return /* @__PURE__ */ jsx("iframe", {
|
|
184
|
+
className,
|
|
185
|
+
sandbox: "allow-downloads allow-scripts",
|
|
186
|
+
srcDoc: result.html,
|
|
187
|
+
style,
|
|
188
|
+
title
|
|
189
|
+
});
|
|
51
190
|
return /* @__PURE__ */ jsx("div", {
|
|
52
191
|
className,
|
|
53
192
|
dangerouslySetInnerHTML: { __html: result.svg },
|
|
193
|
+
onClick: onStateClick ? handleStateClick : void 0,
|
|
54
194
|
style
|
|
55
195
|
});
|
|
56
|
-
}
|
|
196
|
+
});
|
|
57
197
|
//#endregion
|
|
58
|
-
export { SfnDiagram };
|
|
198
|
+
export { SfnDiagram, useSfnDiagram };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sfn-diagram-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "React component for AWS Step Functions ASL diagrams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsdown",
|
|
30
30
|
"dev": "tsdown --watch",
|
|
31
|
-
"test": "vitest run",
|
|
31
|
+
"test": "pnpm run build && vitest run",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"lint": "eslint .",
|
|
34
34
|
"prepublishOnly": "pnpm run build && pnpm run validate",
|
|
@@ -39,13 +39,17 @@
|
|
|
39
39
|
"keywords": [
|
|
40
40
|
"aws",
|
|
41
41
|
"step-functions",
|
|
42
|
+
"stepfunctions",
|
|
42
43
|
"react",
|
|
44
|
+
"component",
|
|
43
45
|
"diagram",
|
|
44
46
|
"asl",
|
|
45
47
|
"state-machine",
|
|
46
48
|
"visualization",
|
|
47
49
|
"svg",
|
|
48
|
-
"mermaid"
|
|
50
|
+
"mermaid",
|
|
51
|
+
"html",
|
|
52
|
+
"typescript"
|
|
49
53
|
],
|
|
50
54
|
"author": "Yusuf Afzal",
|
|
51
55
|
"license": "MIT",
|
|
@@ -57,14 +61,14 @@
|
|
|
57
61
|
"bugs": {
|
|
58
62
|
"url": "https://github.com/yusufaf/sfn-diagram/issues"
|
|
59
63
|
},
|
|
60
|
-
"homepage": "https://
|
|
64
|
+
"homepage": "https://sfn.yusufaf.dev/ecosystem/react/",
|
|
61
65
|
"peerDependencies": {
|
|
62
66
|
"react": "^18.0.0 || ^19.0.0",
|
|
63
|
-
"sfn-diagram": "^1.
|
|
67
|
+
"sfn-diagram": "^1.7.0"
|
|
64
68
|
},
|
|
65
69
|
"devDependencies": {
|
|
66
|
-
"@arethetypeswrong/cli": "^0.18.
|
|
67
|
-
"@testing-library/jest-dom": "^
|
|
70
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
71
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
68
72
|
"@testing-library/react": "^16.0.0",
|
|
69
73
|
"@types/react": "^18.3.0",
|
|
70
74
|
"@types/react-dom": "^18.3.0",
|
|
@@ -73,9 +77,9 @@
|
|
|
73
77
|
"react": "^18.3.0",
|
|
74
78
|
"react-dom": "^18.3.0",
|
|
75
79
|
"sfn-diagram": "workspace:*",
|
|
76
|
-
"tsdown": "^0.
|
|
80
|
+
"tsdown": "^0.23.0",
|
|
77
81
|
"typescript": "^5.9.2",
|
|
78
|
-
"vite": "^
|
|
79
|
-
"vitest": "^
|
|
82
|
+
"vite": "^8.3.0",
|
|
83
|
+
"vitest": "^4.1.11"
|
|
80
84
|
}
|
|
81
85
|
}
|