ok-canvas-export 1.0.4
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 +21 -0
- package/README.md +118 -0
- package/dist/calculateDimensions.d.ts +47 -0
- package/dist/calculateDimensions.d.ts.map +1 -0
- package/dist/calculateDimensions.js +184 -0
- package/dist/calculateDimensions.js.map +1 -0
- package/dist/exportDiagram.d.ts +23 -0
- package/dist/exportDiagram.d.ts.map +1 -0
- package/dist/exportDiagram.js +141 -0
- package/dist/exportDiagram.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/renderHighQuality.d.ts +5 -0
- package/dist/renderHighQuality.d.ts.map +1 -0
- package/dist/renderHighQuality.js +46 -0
- package/dist/renderHighQuality.js.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/useDiagramExport.d.ts +18 -0
- package/dist/useDiagramExport.d.ts.map +1 -0
- package/dist/useDiagramExport.js +40 -0
- package/dist/useDiagramExport.js.map +1 -0
- package/dist/utils/download.d.ts +13 -0
- package/dist/utils/download.d.ts.map +1 -0
- package/dist/utils/download.js +60 -0
- package/dist/utils/download.js.map +1 -0
- package/dist/utils/serializer.d.ts +23 -0
- package/dist/utils/serializer.d.ts.map +1 -0
- package/dist/utils/serializer.js +115 -0
- package/dist/utils/serializer.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# OK-CANVAS-EXPORT LIBRARY
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/ok-canvas-export)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Export SVG diagrams and canvases as high-quality images with full render pass support.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Supports PNG and SVG export formats
|
|
11
|
+
- High-quality rendering with adjustable scale
|
|
12
|
+
- TypeScript support
|
|
13
|
+
- Zero dependencies (except React)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add ok-canvas-export
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { useDiagramExport } from 'ok-canvas-export';
|
|
25
|
+
import { useRef } from 'react';
|
|
26
|
+
|
|
27
|
+
const MyDiagram = () => {
|
|
28
|
+
const svgRef = useRef<SVGSVGElement>(null);
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
exportDiagram,
|
|
32
|
+
isExporting,
|
|
33
|
+
exportAllTables,
|
|
34
|
+
} = useDiagramExport({
|
|
35
|
+
svgRef,
|
|
36
|
+
diagramData,
|
|
37
|
+
getTableHeight,
|
|
38
|
+
projectName: 'my-project',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const handleExport = async () => {
|
|
42
|
+
await exportDiagram({
|
|
43
|
+
format: 'png',
|
|
44
|
+
background: 'white',
|
|
45
|
+
scale: 2,
|
|
46
|
+
margin: 100,
|
|
47
|
+
onProgress: (status) => {
|
|
48
|
+
console.log('Export status:', status);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
<svg ref={svgRef}>
|
|
56
|
+
{/* Your diagram content */}
|
|
57
|
+
</svg>
|
|
58
|
+
<button onClick={handleExport} disabled={isExporting}>
|
|
59
|
+
Export Diagram
|
|
60
|
+
</button>
|
|
61
|
+
</>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
### `useDiagramExport`
|
|
69
|
+
|
|
70
|
+
Hook that provides diagram export functionality.
|
|
71
|
+
|
|
72
|
+
**Parameters:**
|
|
73
|
+
- `svgRef`: React ref to the SVG element
|
|
74
|
+
- `diagramData`: Object containing tables and relationships
|
|
75
|
+
- `getTableHeight`: Function that returns table height based on fields
|
|
76
|
+
- `projectName`: Optional project name for filename
|
|
77
|
+
|
|
78
|
+
**Returns:**
|
|
79
|
+
- `isExporting`: Boolean indicating if export is in progress
|
|
80
|
+
- `exportDiagram`: Function to trigger export
|
|
81
|
+
- `exportAllTables`: Boolean flag for full render mode
|
|
82
|
+
- `setExportAllTables`: Function to toggle full render mode
|
|
83
|
+
|
|
84
|
+
### `exportDiagram`
|
|
85
|
+
|
|
86
|
+
Core export function.
|
|
87
|
+
|
|
88
|
+
**Options:**
|
|
89
|
+
- `format`: `'png'` | `'svg'` (default: `'png'`)
|
|
90
|
+
- `background`: `'white'` | `'transparent'` (default: `'white'`)
|
|
91
|
+
- `scale`: Number for quality scaling (default: `2`)
|
|
92
|
+
- `margin`: Number for padding around content (default: `100`)
|
|
93
|
+
- `fileName`: Custom filename override
|
|
94
|
+
- `onProgress`: Callback for export status updates
|
|
95
|
+
|
|
96
|
+
## How It Works
|
|
97
|
+
|
|
98
|
+
1. Activates full render mode to bypass viewport culling
|
|
99
|
+
2. Prepares clean SVG clone with proper dimensions
|
|
100
|
+
3. Serializes SVG with CSS variables resolved
|
|
101
|
+
4. Renders to high-quality canvas with proper scaling
|
|
102
|
+
5. Downloads as PNG or SVG file
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
The library expects tables to have:
|
|
107
|
+
- `id`: `string`
|
|
108
|
+
- `x`: `number`
|
|
109
|
+
- `y`: `number`
|
|
110
|
+
- `fields`: `array`
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
115
|
+
|
|
116
|
+
## Contributing
|
|
117
|
+
|
|
118
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DiagramData, GetTableHeight } from './types';
|
|
2
|
+
export interface Dimensions {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Calculates dimensions based on diagram data.
|
|
10
|
+
*
|
|
11
|
+
* This function is kept compatible with the original API and tests.
|
|
12
|
+
*
|
|
13
|
+
* It calculates the total diagram size from the table coordinates,
|
|
14
|
+
* assuming the table width is 280px plus the configured padding.
|
|
15
|
+
*/
|
|
16
|
+
export declare const calculateDimensions: (diagramData: DiagramData | undefined | null, getTableHeight: GetTableHeight, margin?: number) => Dimensions;
|
|
17
|
+
/**
|
|
18
|
+
* Calculates the visible bounds of an SVG's graphical content.
|
|
19
|
+
*
|
|
20
|
+
* This function is intended for generic SVG exports.
|
|
21
|
+
*
|
|
22
|
+
* It does NOT rely on:
|
|
23
|
+
*
|
|
24
|
+
* - width
|
|
25
|
+
* - height
|
|
26
|
+
* - viewport
|
|
27
|
+
* - scroll container
|
|
28
|
+
*
|
|
29
|
+
* Instead, it attempts to determine the real graphical bounds.
|
|
30
|
+
*/
|
|
31
|
+
export declare const getSvgContentBounds: (svg: SVGSVGElement) => Dimensions | null;
|
|
32
|
+
/**
|
|
33
|
+
* Calculates the dimensions that should be used for a real SVG export.
|
|
34
|
+
*
|
|
35
|
+
* The content bounds are preferred.
|
|
36
|
+
*
|
|
37
|
+
* The returned x/y values are important because the content does not
|
|
38
|
+
* necessarily start at 0,0.
|
|
39
|
+
*/
|
|
40
|
+
export declare const calculateSvgDimensions: (svg: SVGSVGElement, margin?: number) => Dimensions;
|
|
41
|
+
/**
|
|
42
|
+
* Calculates dimensions based on an SVG element and its scroll container.
|
|
43
|
+
*
|
|
44
|
+
* Kept for backwards compatibility.
|
|
45
|
+
*/
|
|
46
|
+
export declare const getScrollDimensions: (svg: SVGSVGElement) => Dimensions;
|
|
47
|
+
//# sourceMappingURL=calculateDimensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculateDimensions.d.ts","sourceRoot":"","sources":["../src/calculateDimensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK3D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAC9B,aAAa,WAAW,GAAG,SAAS,GAAG,IAAI,EAC3C,gBAAgB,cAAc,EAC9B,SAAQ,MAAY,KACnB,UAqDF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,GAC9B,KAAK,aAAa,KACjB,UAAU,GAAG,IAgFf,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,aAAa,EAClB,SAAQ,MAAY,KACnB,UAmDF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,KAAK,aAAa,KACjB,UAkCF,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
const TABLE_WIDTH = 280;
|
|
2
|
+
const TABLE_PADDING = 40;
|
|
3
|
+
/**
|
|
4
|
+
* Calculates dimensions based on diagram data.
|
|
5
|
+
*
|
|
6
|
+
* This function is kept compatible with the original API and tests.
|
|
7
|
+
*
|
|
8
|
+
* It calculates the total diagram size from the table coordinates,
|
|
9
|
+
* assuming the table width is 280px plus the configured padding.
|
|
10
|
+
*/
|
|
11
|
+
export const calculateDimensions = (diagramData, getTableHeight, margin = 100) => {
|
|
12
|
+
if (!diagramData?.tables?.length) {
|
|
13
|
+
return {
|
|
14
|
+
width: 100 + margin * 2,
|
|
15
|
+
height: 100 + margin * 2,
|
|
16
|
+
x: -margin,
|
|
17
|
+
y: -margin,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const minX = Math.min(...diagramData.tables.map((table) => table.x));
|
|
21
|
+
const minY = Math.min(...diagramData.tables.map((table) => table.y));
|
|
22
|
+
const maxX = Math.max(...diagramData.tables.map((table) => table.x + TABLE_WIDTH + TABLE_PADDING));
|
|
23
|
+
const maxY = Math.max(...diagramData.tables.map((table) => table.y +
|
|
24
|
+
getTableHeight(table.fields) +
|
|
25
|
+
TABLE_PADDING));
|
|
26
|
+
/*
|
|
27
|
+
* Keep the original expected behavior:
|
|
28
|
+
*
|
|
29
|
+
* width = maxX - minX
|
|
30
|
+
* height = maxY - minY
|
|
31
|
+
*
|
|
32
|
+
* with margin added on both sides.
|
|
33
|
+
*/
|
|
34
|
+
return {
|
|
35
|
+
x: minX - margin,
|
|
36
|
+
y: minY - margin,
|
|
37
|
+
width: Math.max(100, maxX - minX + margin * 2),
|
|
38
|
+
height: Math.max(100, maxY - minY + margin * 2),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Calculates the visible bounds of an SVG's graphical content.
|
|
43
|
+
*
|
|
44
|
+
* This function is intended for generic SVG exports.
|
|
45
|
+
*
|
|
46
|
+
* It does NOT rely on:
|
|
47
|
+
*
|
|
48
|
+
* - width
|
|
49
|
+
* - height
|
|
50
|
+
* - viewport
|
|
51
|
+
* - scroll container
|
|
52
|
+
*
|
|
53
|
+
* Instead, it attempts to determine the real graphical bounds.
|
|
54
|
+
*/
|
|
55
|
+
export const getSvgContentBounds = (svg) => {
|
|
56
|
+
const elements = Array.from(svg.querySelectorAll('g, path, rect, circle, ellipse, line, polyline, polygon, text, use, image'));
|
|
57
|
+
let minX = Infinity;
|
|
58
|
+
let minY = Infinity;
|
|
59
|
+
let maxX = -Infinity;
|
|
60
|
+
let maxY = -Infinity;
|
|
61
|
+
const updateBounds = (x, y, width, height) => {
|
|
62
|
+
if (!Number.isFinite(x) ||
|
|
63
|
+
!Number.isFinite(y) ||
|
|
64
|
+
!Number.isFinite(width) ||
|
|
65
|
+
!Number.isFinite(height)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (width < 0 || height < 0) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
minX = Math.min(minX, x);
|
|
72
|
+
minY = Math.min(minY, y);
|
|
73
|
+
maxX = Math.max(maxX, x + width);
|
|
74
|
+
maxY = Math.max(maxY, y + height);
|
|
75
|
+
};
|
|
76
|
+
for (const element of elements) {
|
|
77
|
+
/*
|
|
78
|
+
* Ignore the artificial canvas background.
|
|
79
|
+
*/
|
|
80
|
+
if (element.matches('[data-export-bg="canvas"]') ||
|
|
81
|
+
element.closest('[data-export-bg="canvas"]')) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const bbox = element.getBBox();
|
|
86
|
+
updateBounds(bbox.x, bbox.y, bbox.width, bbox.height);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/*
|
|
90
|
+
* getBBox() can fail in environments where the SVG
|
|
91
|
+
* has not been rendered yet.
|
|
92
|
+
*/
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!Number.isFinite(minX) ||
|
|
96
|
+
!Number.isFinite(minY) ||
|
|
97
|
+
!Number.isFinite(maxX) ||
|
|
98
|
+
!Number.isFinite(maxY)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
x: minX,
|
|
103
|
+
y: minY,
|
|
104
|
+
width: Math.max(1, maxX - minX),
|
|
105
|
+
height: Math.max(1, maxY - minY),
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Calculates the dimensions that should be used for a real SVG export.
|
|
110
|
+
*
|
|
111
|
+
* The content bounds are preferred.
|
|
112
|
+
*
|
|
113
|
+
* The returned x/y values are important because the content does not
|
|
114
|
+
* necessarily start at 0,0.
|
|
115
|
+
*/
|
|
116
|
+
export const calculateSvgDimensions = (svg, margin = 100) => {
|
|
117
|
+
const bounds = getSvgContentBounds(svg);
|
|
118
|
+
if (bounds) {
|
|
119
|
+
return {
|
|
120
|
+
x: bounds.x - margin,
|
|
121
|
+
y: bounds.y - margin,
|
|
122
|
+
width: bounds.width + margin * 2,
|
|
123
|
+
height: bounds.height + margin * 2,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/*
|
|
127
|
+
* Fallback to the SVG viewBox.
|
|
128
|
+
*/
|
|
129
|
+
const viewBox = svg.viewBox?.baseVal;
|
|
130
|
+
if (viewBox &&
|
|
131
|
+
Number.isFinite(viewBox.width) &&
|
|
132
|
+
Number.isFinite(viewBox.height) &&
|
|
133
|
+
viewBox.width > 0 &&
|
|
134
|
+
viewBox.height > 0) {
|
|
135
|
+
return {
|
|
136
|
+
x: viewBox.x - margin,
|
|
137
|
+
y: viewBox.y - margin,
|
|
138
|
+
width: viewBox.width + margin * 2,
|
|
139
|
+
height: viewBox.height + margin * 2,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/*
|
|
143
|
+
* Final fallback to width/height attributes.
|
|
144
|
+
*/
|
|
145
|
+
const width = Number(svg.getAttribute('width')) ||
|
|
146
|
+
svg.clientWidth ||
|
|
147
|
+
1;
|
|
148
|
+
const height = Number(svg.getAttribute('height')) ||
|
|
149
|
+
svg.clientHeight ||
|
|
150
|
+
1;
|
|
151
|
+
return {
|
|
152
|
+
x: -margin,
|
|
153
|
+
y: -margin,
|
|
154
|
+
width: width + margin * 2,
|
|
155
|
+
height: height + margin * 2,
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Calculates dimensions based on an SVG element and its scroll container.
|
|
160
|
+
*
|
|
161
|
+
* Kept for backwards compatibility.
|
|
162
|
+
*/
|
|
163
|
+
export const getScrollDimensions = (svg) => {
|
|
164
|
+
const scrollContainer = svg.parentElement ||
|
|
165
|
+
svg.closest('.overflow-auto') ||
|
|
166
|
+
svg.closest('.overflow-scroll');
|
|
167
|
+
let width = Number(svg.getAttribute('width')) ||
|
|
168
|
+
svg.clientWidth ||
|
|
169
|
+
1;
|
|
170
|
+
let height = Number(svg.getAttribute('height')) ||
|
|
171
|
+
svg.clientHeight ||
|
|
172
|
+
1;
|
|
173
|
+
if (scrollContainer) {
|
|
174
|
+
width = Math.max(width, scrollContainer.scrollWidth);
|
|
175
|
+
height = Math.max(height, scrollContainer.scrollHeight);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
x: 0,
|
|
179
|
+
y: 0,
|
|
180
|
+
width,
|
|
181
|
+
height,
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=calculateDimensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculateDimensions.js","sourceRoot":"","sources":["../src/calculateDimensions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ExportOptions, DiagramData, GetTableHeight } from './types';
|
|
2
|
+
export interface ExportDiagramParams {
|
|
3
|
+
svgRef: React.RefObject<SVGSVGElement>;
|
|
4
|
+
diagramData: DiagramData;
|
|
5
|
+
getTableHeight: GetTableHeight;
|
|
6
|
+
projectName?: string;
|
|
7
|
+
setExportAllTables?: (value: boolean) => void;
|
|
8
|
+
showToast?: (message: string, type?: string) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Exports the complete SVG diagram regardless of the
|
|
12
|
+
* visible viewport.
|
|
13
|
+
*
|
|
14
|
+
* The export process is:
|
|
15
|
+
*
|
|
16
|
+
* 1. Render all diagram content.
|
|
17
|
+
* 2. Calculate the real SVG content bounds.
|
|
18
|
+
* 3. Create an independent SVG clone.
|
|
19
|
+
* 4. Set a viewBox covering the complete content.
|
|
20
|
+
* 5. Convert the SVG to PNG when requested.
|
|
21
|
+
*/
|
|
22
|
+
export declare const exportDiagram: (params: ExportDiagramParams, options?: ExportOptions) => Promise<void>;
|
|
23
|
+
//# sourceMappingURL=exportDiagram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exportDiagram.d.ts","sourceRoot":"","sources":["../src/exportDiagram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,cAAc,EACf,MAAM,SAAS,CAAC;AAsBjB,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,CACV,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,KACV,IAAI,CAAC;CACX;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,mBAAmB,EAC3B,UAAS,aAAkB,KAC1B,OAAO,CAAC,IAAI,CAoPd,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { calculateSvgDimensions, } from './calculateDimensions';
|
|
2
|
+
import { renderHighQuality, } from './renderHighQuality';
|
|
3
|
+
import { serializeSvg, createSvgUrl, prepareSvgForExport, } from './utils/serializer';
|
|
4
|
+
import { downloadBlob, generateFileName, canvasToBlob, } from './utils/download';
|
|
5
|
+
/**
|
|
6
|
+
* Exports the complete SVG diagram regardless of the
|
|
7
|
+
* visible viewport.
|
|
8
|
+
*
|
|
9
|
+
* The export process is:
|
|
10
|
+
*
|
|
11
|
+
* 1. Render all diagram content.
|
|
12
|
+
* 2. Calculate the real SVG content bounds.
|
|
13
|
+
* 3. Create an independent SVG clone.
|
|
14
|
+
* 4. Set a viewBox covering the complete content.
|
|
15
|
+
* 5. Convert the SVG to PNG when requested.
|
|
16
|
+
*/
|
|
17
|
+
export const exportDiagram = async (params, options = {}) => {
|
|
18
|
+
const { svgRef, diagramData, getTableHeight, projectName, setExportAllTables, showToast, } = params;
|
|
19
|
+
const { format = 'png', background = 'white', scale = 2, margin = 100, fileName, onProgress, } = options;
|
|
20
|
+
if (!svgRef.current) {
|
|
21
|
+
onProgress?.({
|
|
22
|
+
type: 'error',
|
|
23
|
+
message: 'SVG reference not available',
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
let objectUrl = null;
|
|
28
|
+
try {
|
|
29
|
+
onProgress?.({
|
|
30
|
+
type: 'preparing',
|
|
31
|
+
});
|
|
32
|
+
showToast?.('Preparing export...', 'info');
|
|
33
|
+
/*
|
|
34
|
+
* Tell the consuming application to render all
|
|
35
|
+
* currently virtualized/cullable content.
|
|
36
|
+
*/
|
|
37
|
+
setExportAllTables?.(true);
|
|
38
|
+
/*
|
|
39
|
+
* Give React time to render the complete diagram.
|
|
40
|
+
*/
|
|
41
|
+
await new Promise((resolve) => {
|
|
42
|
+
setTimeout(resolve, 300);
|
|
43
|
+
});
|
|
44
|
+
onProgress?.({
|
|
45
|
+
type: 'rendering',
|
|
46
|
+
});
|
|
47
|
+
const sourceSvg = svgRef.current;
|
|
48
|
+
/*
|
|
49
|
+
* IMPORTANT:
|
|
50
|
+
*
|
|
51
|
+
* Do NOT use calculateDimensions() here.
|
|
52
|
+
*
|
|
53
|
+
* calculateDimensions() exists for diagram-data based
|
|
54
|
+
* compatibility.
|
|
55
|
+
*
|
|
56
|
+
* For a generic SVG export we need the real SVG bounds.
|
|
57
|
+
*/
|
|
58
|
+
const dimensions = calculateSvgDimensions(sourceSvg, margin);
|
|
59
|
+
const { x, y, width, height, } = dimensions;
|
|
60
|
+
/*
|
|
61
|
+
* Prepare a completely independent SVG clone.
|
|
62
|
+
*/
|
|
63
|
+
const exportSvg = prepareSvgForExport(sourceSvg, width, height, background, margin, x, y);
|
|
64
|
+
onProgress?.({
|
|
65
|
+
type: 'serializing',
|
|
66
|
+
});
|
|
67
|
+
/*
|
|
68
|
+
* Serialize the prepared SVG.
|
|
69
|
+
*/
|
|
70
|
+
const svgMarkup = serializeSvg(exportSvg);
|
|
71
|
+
/*
|
|
72
|
+
* SVG export does not require canvas.
|
|
73
|
+
*/
|
|
74
|
+
if (format === 'svg') {
|
|
75
|
+
const blob = new Blob([svgMarkup], {
|
|
76
|
+
type: 'image/svg+xml;charset=utf-8',
|
|
77
|
+
});
|
|
78
|
+
const finalFileName = fileName ||
|
|
79
|
+
generateFileName(projectName, 'svg');
|
|
80
|
+
downloadBlob(blob, finalFileName);
|
|
81
|
+
onProgress?.({
|
|
82
|
+
type: 'complete',
|
|
83
|
+
});
|
|
84
|
+
showToast?.('Export completed successfully', 'success');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
/*
|
|
88
|
+
* Create an object URL for the SVG.
|
|
89
|
+
*/
|
|
90
|
+
const image = new Image();
|
|
91
|
+
objectUrl = createSvgUrl(svgMarkup);
|
|
92
|
+
await new Promise((resolve, reject) => {
|
|
93
|
+
image.onload = () => {
|
|
94
|
+
resolve();
|
|
95
|
+
};
|
|
96
|
+
image.onerror = () => {
|
|
97
|
+
reject(new Error('Failed to load SVG image'));
|
|
98
|
+
};
|
|
99
|
+
image.src = objectUrl;
|
|
100
|
+
});
|
|
101
|
+
/*
|
|
102
|
+
* Render the complete SVG into a high-quality canvas.
|
|
103
|
+
*/
|
|
104
|
+
const canvas = await renderHighQuality(image, width, height, scale, background);
|
|
105
|
+
onProgress?.({
|
|
106
|
+
type: 'downloading',
|
|
107
|
+
});
|
|
108
|
+
/*
|
|
109
|
+
* Convert the canvas to PNG.
|
|
110
|
+
*/
|
|
111
|
+
const blob = await canvasToBlob(canvas);
|
|
112
|
+
const finalFileName = fileName ||
|
|
113
|
+
generateFileName(projectName, 'png');
|
|
114
|
+
downloadBlob(blob, finalFileName);
|
|
115
|
+
onProgress?.({
|
|
116
|
+
type: 'complete',
|
|
117
|
+
});
|
|
118
|
+
showToast?.('Export completed successfully', 'success');
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error('Export error:', error);
|
|
122
|
+
onProgress?.({
|
|
123
|
+
type: 'error',
|
|
124
|
+
message: 'Export failed',
|
|
125
|
+
});
|
|
126
|
+
showToast?.('Failed to export file', 'error');
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
/*
|
|
130
|
+
* Always restore the normal rendering state.
|
|
131
|
+
*/
|
|
132
|
+
setExportAllTables?.(false);
|
|
133
|
+
/*
|
|
134
|
+
* Always release the object URL.
|
|
135
|
+
*/
|
|
136
|
+
if (objectUrl) {
|
|
137
|
+
URL.revokeObjectURL(objectUrl);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=exportDiagram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exportDiagram.js","sourceRoot":"","sources":["../src/exportDiagram.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { useDiagramExport, } from './useDiagramExport';
|
|
2
|
+
export { exportDiagram, } from './exportDiagram';
|
|
3
|
+
export { calculateDimensions, calculateSvgDimensions, getSvgContentBounds, getScrollDimensions, } from './calculateDimensions';
|
|
4
|
+
export { renderHighQuality, } from './renderHighQuality';
|
|
5
|
+
export { serializeSvg, createSvgUrl, prepareSvgForExport, } from './utils/serializer';
|
|
6
|
+
export { downloadBlob, generateFileName, canvasToBlob, } from './utils/download';
|
|
7
|
+
export type { ExportOptions, ExportStatus, ExportContext, DiagramData, GetTableHeight, } from './types';
|
|
8
|
+
export { useDiagramExport as default, } from './useDiagramExport';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,gBAAgB,IAAI,OAAO,GAC5B,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { useDiagramExport, } from './useDiagramExport';
|
|
2
|
+
export { exportDiagram, } from './exportDiagram';
|
|
3
|
+
export { calculateDimensions, calculateSvgDimensions, getSvgContentBounds, getScrollDimensions, } from './calculateDimensions';
|
|
4
|
+
export { renderHighQuality, } from './renderHighQuality';
|
|
5
|
+
export { serializeSvg, createSvgUrl, prepareSvgForExport, } from './utils/serializer';
|
|
6
|
+
export { downloadBlob, generateFileName, canvasToBlob, } from './utils/download';
|
|
7
|
+
export { useDiagramExport as default, } from './useDiagramExport';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAU1B,OAAO,EACL,gBAAgB,IAAI,OAAO,GAC5B,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders an SVG image to a high-quality canvas.
|
|
3
|
+
*/
|
|
4
|
+
export declare const renderHighQuality: (image: HTMLImageElement, width: number, height: number, scale?: number, background?: "white" | "transparent") => Promise<HTMLCanvasElement>;
|
|
5
|
+
//# sourceMappingURL=renderHighQuality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderHighQuality.d.ts","sourceRoot":"","sources":["../src/renderHighQuality.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,gBAAgB,EACvB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,QAAO,MAAU,EACjB,aAAY,OAAO,GAAG,aAA6B,KAClD,OAAO,CAAC,iBAAiB,CAuG3B,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders an SVG image to a high-quality canvas.
|
|
3
|
+
*/
|
|
4
|
+
export const renderHighQuality = async (image, width, height, scale = 2, background = 'transparent') => {
|
|
5
|
+
const MAX_CANVAS_SIZE = 16384;
|
|
6
|
+
const safeWidth = Math.max(1, width);
|
|
7
|
+
const safeHeight = Math.max(1, height);
|
|
8
|
+
const safeScale = Math.max(0.1, scale);
|
|
9
|
+
const effectiveScale = Math.min(safeScale, MAX_CANVAS_SIZE /
|
|
10
|
+
Math.max(safeWidth, safeHeight, 1));
|
|
11
|
+
const canvas = document.createElement('canvas');
|
|
12
|
+
canvas.width = Math.max(1, Math.floor(safeWidth *
|
|
13
|
+
effectiveScale));
|
|
14
|
+
canvas.height = Math.max(1, Math.floor(safeHeight *
|
|
15
|
+
effectiveScale));
|
|
16
|
+
const context = canvas.getContext('2d', {
|
|
17
|
+
alpha: background !== 'white',
|
|
18
|
+
});
|
|
19
|
+
if (!context) {
|
|
20
|
+
throw new Error('Unable to create 2D canvas context');
|
|
21
|
+
}
|
|
22
|
+
context.imageSmoothingEnabled =
|
|
23
|
+
true;
|
|
24
|
+
context.imageSmoothingQuality =
|
|
25
|
+
'high';
|
|
26
|
+
/*
|
|
27
|
+
* Background.
|
|
28
|
+
*/
|
|
29
|
+
if (background === 'white') {
|
|
30
|
+
context.fillStyle =
|
|
31
|
+
'#ffffff';
|
|
32
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
36
|
+
}
|
|
37
|
+
/*
|
|
38
|
+
* The canvas already contains the scaled dimensions,
|
|
39
|
+
* so we draw using the original SVG dimensions.
|
|
40
|
+
*/
|
|
41
|
+
context.drawImage(image, 0, 0, safeWidth *
|
|
42
|
+
effectiveScale, safeHeight *
|
|
43
|
+
effectiveScale);
|
|
44
|
+
return canvas;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=renderHighQuality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderHighQuality.js","sourceRoot":"","sources":["../src/renderHighQuality.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACpC,KAAuB,EACvB,KAAa,EACb,MAAc,EACd,QAAgB,CAAC,EACjB,aAAsC,aAAa,EACvB,EAAE;IAC9B,MAAM,eAAe,GAAG,KAAK,CAAC;IAE9B,MAAM,SAAS,GACb,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAErB,MAAM,UAAU,GACd,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAEtB,MAAM,SAAS,GACb,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAEvB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAC7B,SAAS,EACT,eAAe;QACb,IAAI,CAAC,GAAG,CACN,SAAS,EACT,UAAU,EACV,CAAC,CACF,CACJ,CAAC;IAEF,MAAM,MAAM,GACV,QAAQ,CAAC,aAAa,CACpB,QAAQ,CACT,CAAC;IAEJ,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CACrB,CAAC,EACD,IAAI,CAAC,KAAK,CACR,SAAS;QACP,cAAc,CACjB,CACF,CAAC;IAEF,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CACtB,CAAC,EACD,IAAI,CAAC,KAAK,CACR,UAAU;QACR,cAAc,CACjB,CACF,CAAC;IAEF,MAAM,OAAO,GACX,MAAM,CAAC,UAAU,CACf,IAAI,EACJ;QACE,KAAK,EACH,UAAU,KAAK,OAAO;KACzB,CACF,CAAC;IAEJ,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,oCAAoC,CACrC,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,qBAAqB;QAC3B,IAAI,CAAC;IAEP,OAAO,CAAC,qBAAqB;QAC3B,MAAM,CAAC;IAET;;OAEG;IACH,IACE,UAAU,KAAK,OAAO,EACtB,CAAC;QACD,OAAO,CAAC,SAAS;YACf,SAAS,CAAC;QAEZ,OAAO,CAAC,QAAQ,CACd,CAAC,EACD,CAAC,EACD,MAAM,CAAC,KAAK,EACZ,MAAM,CAAC,MAAM,CACd,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,SAAS,CACf,CAAC,EACD,CAAC,EACD,MAAM,CAAC,KAAK,EACZ,MAAM,CAAC,MAAM,CACd,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,SAAS,CACf,KAAK,EACL,CAAC,EACD,CAAC,EACD,SAAS;QACP,cAAc,EAChB,UAAU;QACR,cAAc,CACjB,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface ExportOptions {
|
|
2
|
+
format?: 'png' | 'svg';
|
|
3
|
+
background?: 'white' | 'transparent';
|
|
4
|
+
scale?: number;
|
|
5
|
+
margin?: number;
|
|
6
|
+
fileName?: string;
|
|
7
|
+
onProgress?: (status: ExportStatus) => void;
|
|
8
|
+
}
|
|
9
|
+
export interface DiagramData {
|
|
10
|
+
tables: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
fields: any[];
|
|
15
|
+
}>;
|
|
16
|
+
relationships: any[];
|
|
17
|
+
}
|
|
18
|
+
export type ExportStatus = {
|
|
19
|
+
type: 'preparing';
|
|
20
|
+
} | {
|
|
21
|
+
type: 'rendering';
|
|
22
|
+
} | {
|
|
23
|
+
type: 'serializing';
|
|
24
|
+
} | {
|
|
25
|
+
type: 'downloading';
|
|
26
|
+
} | {
|
|
27
|
+
type: 'complete';
|
|
28
|
+
} | {
|
|
29
|
+
type: 'error';
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
export interface GetTableHeight {
|
|
33
|
+
(fields: any[]): number;
|
|
34
|
+
}
|
|
35
|
+
export interface ExportContext {
|
|
36
|
+
isExporting: boolean;
|
|
37
|
+
exportDiagram: (options?: ExportOptions) => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAEvB,UAAU,CAAC,EACP,OAAO,GACP,aAAa,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,UAAU,CAAC,EAAE,CACX,MAAM,EAAE,YAAY,KACjB,IAAI,CAAC;CACX;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,MAAM,EAAE,GAAG,EAAE,CAAC;KACf,CAAC,CAAC;IAEH,aAAa,EAAE,GAAG,EAAE,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GACpB;IACE,IAAI,EAAE,WAAW,CAAC;CACnB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;CACnB,GACD;IACE,IAAI,EAAE,aAAa,CAAC;CACrB,GACD;IACE,IAAI,EAAE,aAAa,CAAC;CACrB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;CAClB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,WAAW,cAAc;IAC7B,CACE,MAAM,EAAE,GAAG,EAAE,GACZ,MAAM,CAAC;CACX;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,OAAO,CAAC;IAErB,aAAa,EAAE,CACb,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExportContext, DiagramData, GetTableHeight } from './types';
|
|
2
|
+
export interface UseDiagramExportParams {
|
|
3
|
+
svgRef: React.RefObject<SVGSVGElement>;
|
|
4
|
+
diagramData?: DiagramData | null;
|
|
5
|
+
getTableHeight?: GetTableHeight;
|
|
6
|
+
projectName?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Hook that provides SVG diagram export functionality.
|
|
10
|
+
*
|
|
11
|
+
* The exporter automatically calculates the real SVG content bounds,
|
|
12
|
+
* so diagramData is optional for generic SVG usage.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useDiagramExport: ({ svgRef, diagramData, getTableHeight, projectName, }: UseDiagramExportParams) => ExportContext & {
|
|
15
|
+
exportAllTables: boolean;
|
|
16
|
+
setExportAllTables: (value: boolean) => void;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=useDiagramExport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDiagramExport.d.ts","sourceRoot":"","sources":["../src/useDiagramExport.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACX,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAEvC,WAAW,CAAC,EACR,WAAW,GACX,IAAI,CAAC;IAET,cAAc,CAAC,EACb,cAAc,CAAC;IAEjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,uDAK9B,sBAAsB,KAAG,aAAa,GAAG;IAC1C,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,CAClB,KAAK,EAAE,OAAO,KACX,IAAI,CAAC;CAqDX,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useState, useCallback, } from 'react';
|
|
2
|
+
import { exportDiagram, } from './exportDiagram';
|
|
3
|
+
/**
|
|
4
|
+
* Hook that provides SVG diagram export functionality.
|
|
5
|
+
*
|
|
6
|
+
* The exporter automatically calculates the real SVG content bounds,
|
|
7
|
+
* so diagramData is optional for generic SVG usage.
|
|
8
|
+
*/
|
|
9
|
+
export const useDiagramExport = ({ svgRef, diagramData, getTableHeight, projectName, }) => {
|
|
10
|
+
const [isExporting, setIsExporting,] = useState(false);
|
|
11
|
+
const [exportAllTables, setExportAllTables,] = useState(false);
|
|
12
|
+
const handleExport = useCallback(async (options = {}) => {
|
|
13
|
+
setIsExporting(true);
|
|
14
|
+
try {
|
|
15
|
+
await exportDiagram({
|
|
16
|
+
svgRef,
|
|
17
|
+
diagramData,
|
|
18
|
+
getTableHeight,
|
|
19
|
+
projectName,
|
|
20
|
+
setExportAllTables,
|
|
21
|
+
}, options);
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
setIsExporting(false);
|
|
25
|
+
setExportAllTables(false);
|
|
26
|
+
}
|
|
27
|
+
}, [
|
|
28
|
+
svgRef,
|
|
29
|
+
diagramData,
|
|
30
|
+
getTableHeight,
|
|
31
|
+
projectName,
|
|
32
|
+
]);
|
|
33
|
+
return {
|
|
34
|
+
isExporting,
|
|
35
|
+
exportDiagram: handleExport,
|
|
36
|
+
exportAllTables,
|
|
37
|
+
setExportAllTables,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=useDiagramExport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDiagramExport.js","sourceRoot":"","sources":["../src/useDiagramExport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,WAAW,GACZ,MAAM,OAAO,CAAC;AASf,OAAO,EACL,aAAa,GACd,MAAM,iBAAiB,CAAC;AAezB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAC/B,MAAM,EACN,WAAW,EACX,cAAc,EACd,WAAW,GACY,EAKvB,EAAE;IACF,MAAM,CACJ,WAAW,EACX,cAAc,EACf,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpB,MAAM,CACJ,eAAe,EACf,kBAAkB,EACnB,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpB,MAAM,YAAY,GAChB,WAAW,CACT,KAAK,EACH,UAAyB,EAAE,EAC3B,EAAE;QACF,cAAc,CAAC,IAAI,CAAC,CAAC;QAErB,IAAI,CAAC;YACH,MAAM,aAAa,CACjB;gBACE,MAAM;gBACN,WAAW;gBACX,cAAc;gBACd,WAAW;gBACX,kBAAkB;aACnB,EACD,OAAO,CACR,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,cAAc,CAAC,KAAK,CAAC,CAAC;YAEtB,kBAAkB,CAChB,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC,EACD;QACE,MAAM;QACN,WAAW;QACX,cAAc;QACd,WAAW;KACZ,CACF,CAAC;IAEJ,OAAO;QACL,WAAW;QACX,aAAa,EACX,YAAY;QACd,eAAe;QACf,kBAAkB;KACnB,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads a Blob as a file.
|
|
3
|
+
*/
|
|
4
|
+
export declare const downloadBlob: (blob: Blob, fileName: string) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Generates a safe filename.
|
|
7
|
+
*/
|
|
8
|
+
export declare const generateFileName: (projectName: string | undefined, format: "png" | "svg") => string;
|
|
9
|
+
/**
|
|
10
|
+
* Converts a canvas to a PNG Blob.
|
|
11
|
+
*/
|
|
12
|
+
export declare const canvasToBlob: (canvas: HTMLCanvasElement) => Promise<Blob>;
|
|
13
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/utils/download.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,IAAI,EACV,UAAU,MAAM,KACf,IAqCF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,aACI,MAAM,GACN,SAAS,EACb,QACI,KAAK,GACL,KAAK,KACR,MAiBF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,QAAQ,iBAAiB,KACxB,OAAO,CAAC,IAAI,CA2Cd,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads a Blob as a file.
|
|
3
|
+
*/
|
|
4
|
+
export const downloadBlob = (blob, fileName) => {
|
|
5
|
+
const url = URL.createObjectURL(blob);
|
|
6
|
+
const link = document.createElement('a');
|
|
7
|
+
link.href = url;
|
|
8
|
+
link.download = fileName;
|
|
9
|
+
try {
|
|
10
|
+
document.body?.appendChild(link);
|
|
11
|
+
link.click();
|
|
12
|
+
document.body?.removeChild(link);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
link.click();
|
|
16
|
+
}
|
|
17
|
+
/*
|
|
18
|
+
* Give the browser a chance to start the download before
|
|
19
|
+
* revoking the object URL.
|
|
20
|
+
*/
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
URL.revokeObjectURL(url);
|
|
23
|
+
}, 100);
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Generates a safe filename.
|
|
27
|
+
*/
|
|
28
|
+
export const generateFileName = (projectName, format) => {
|
|
29
|
+
const safeName = (projectName ||
|
|
30
|
+
'diagram')
|
|
31
|
+
.replace(/\s+/g, '_')
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[^a-z0-9_-]/g, '');
|
|
34
|
+
return `${safeName}.${format}`;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Converts a canvas to a PNG Blob.
|
|
38
|
+
*/
|
|
39
|
+
export const canvasToBlob = (canvas) => {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
try {
|
|
42
|
+
if (typeof canvas.toBlob !==
|
|
43
|
+
'function') {
|
|
44
|
+
reject(new Error('Canvas.toBlob is not supported'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
canvas.toBlob((blob) => {
|
|
48
|
+
if (!blob) {
|
|
49
|
+
reject(new Error('Failed to create PNG blob'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
resolve(blob);
|
|
53
|
+
}, 'image/png');
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
reject(error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/utils/download.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,IAAU,EACV,QAAgB,EACV,EAAE;IACR,MAAM,GAAG,GACP,GAAG,CAAC,eAAe,CACjB,IAAI,CACL,CAAC;IAEJ,MAAM,IAAI,GACR,QAAQ,CAAC,aAAa,CACpB,GAAG,CACJ,CAAC;IAEJ,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;IAChB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAEzB,IAAI,CAAC;QACH,QAAQ,CAAC,IAAI,EAAE,WAAW,CACxB,IAAI,CACL,CAAC;QAEF,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,QAAQ,CAAC,IAAI,EAAE,WAAW,CACxB,IAAI,CACL,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,CAAC,eAAe,CACjB,GAAG,CACJ,CAAC;IACJ,CAAC,EAAE,GAAG,CAAC,CAAC;AACV,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,WAEa,EACb,MAES,EACD,EAAE;IACV,MAAM,QAAQ,GACZ,CACE,WAAW;QACX,SAAS,CACV;SACE,OAAO,CACN,MAAM,EACN,GAAG,CACJ;SACA,WAAW,EAAE;SACb,OAAO,CACN,cAAc,EACd,EAAE,CACH,CAAC;IAEN,OAAO,GAAG,QAAQ,IAAI,MAAM,EAAE,CAAC;AACjC,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,MAAyB,EACV,EAAE;IACjB,OAAO,IAAI,OAAO,CAChB,CACE,OAAO,EACP,MAAM,EACN,EAAE;QACF,IAAI,CAAC;YACH,IACE,OAAO,MAAM,CAAC,MAAM;gBACpB,UAAU,EACV,CAAC;gBACD,MAAM,CACJ,IAAI,KAAK,CACP,gCAAgC,CACjC,CACF,CAAC;gBAEF,OAAO;YACT,CAAC;YAED,MAAM,CAAC,MAAM,CACX,CACE,IAAI,EACJ,EAAE;gBACF,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,MAAM,CACJ,IAAI,KAAK,CACP,2BAA2B,CAC5B,CACF,CAAC;oBAEF,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,EACD,WAAW,CACZ,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads are handled elsewhere.
|
|
3
|
+
*
|
|
4
|
+
* This file contains SVG serialization and preparation utilities.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Serializes an SVG element to a string.
|
|
8
|
+
*
|
|
9
|
+
* CSS custom properties are resolved using the current document styles.
|
|
10
|
+
*/
|
|
11
|
+
export declare const serializeSvg: (svg: SVGSVGElement) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a Blob URL from SVG markup.
|
|
14
|
+
*/
|
|
15
|
+
export declare const createSvgUrl: (svgMarkup: string) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a clean SVG clone for export.
|
|
18
|
+
*
|
|
19
|
+
* sourceX/sourceY represent the origin of the exported
|
|
20
|
+
* coordinate system.
|
|
21
|
+
*/
|
|
22
|
+
export declare const prepareSvgForExport: (sourceSvg: SVGSVGElement, width: number, height: number, background: "white" | "transparent", margin?: number, sourceX?: number, sourceY?: number) => SVGSVGElement;
|
|
23
|
+
//# sourceMappingURL=serializer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../src/utils/serializer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,aAAa,KACjB,MAwDF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,WAAW,MAAM,KAChB,MAUF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC9B,WAAW,aAAa,EACxB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,YAAY,OAAO,GAAG,aAAa,EACnC,SAAQ,MAAU,EAClB,UAAS,MAAU,EACnB,UAAS,MAAU,KAClB,aAuIF,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads are handled elsewhere.
|
|
3
|
+
*
|
|
4
|
+
* This file contains SVG serialization and preparation utilities.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Serializes an SVG element to a string.
|
|
8
|
+
*
|
|
9
|
+
* CSS custom properties are resolved using the current document styles.
|
|
10
|
+
*/
|
|
11
|
+
export const serializeSvg = (svg) => {
|
|
12
|
+
const serializer = new XMLSerializer();
|
|
13
|
+
const styles = typeof document !== 'undefined'
|
|
14
|
+
? getComputedStyle(document.documentElement)
|
|
15
|
+
: null;
|
|
16
|
+
let markup = serializer.serializeToString(svg);
|
|
17
|
+
/*
|
|
18
|
+
* Resolve CSS custom properties.
|
|
19
|
+
*/
|
|
20
|
+
if (styles) {
|
|
21
|
+
markup = markup.replace(/var\(--([^)]+)\)/g, (_match, name) => {
|
|
22
|
+
const value = styles
|
|
23
|
+
.getPropertyValue(`--${name}`)
|
|
24
|
+
?.trim();
|
|
25
|
+
return (value ||
|
|
26
|
+
`var(--${name})`);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/*
|
|
30
|
+
* Ensure SVG has the proper namespace.
|
|
31
|
+
*/
|
|
32
|
+
if (!markup.includes('xmlns="http://www.w3.org/2000/svg"')) {
|
|
33
|
+
markup = markup.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
|
|
34
|
+
}
|
|
35
|
+
return markup;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Creates a Blob URL from SVG markup.
|
|
39
|
+
*/
|
|
40
|
+
export const createSvgUrl = (svgMarkup) => {
|
|
41
|
+
return URL.createObjectURL(new Blob([svgMarkup], {
|
|
42
|
+
type: 'image/svg+xml;charset=utf-8',
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Creates a clean SVG clone for export.
|
|
47
|
+
*
|
|
48
|
+
* sourceX/sourceY represent the origin of the exported
|
|
49
|
+
* coordinate system.
|
|
50
|
+
*/
|
|
51
|
+
export const prepareSvgForExport = (sourceSvg, width, height, background, margin = 0, sourceX = 0, sourceY = 0) => {
|
|
52
|
+
const clone = sourceSvg.cloneNode(true);
|
|
53
|
+
/*
|
|
54
|
+
* Remove viewport/background elements.
|
|
55
|
+
*/
|
|
56
|
+
const bgNodes = clone.querySelectorAll('[data-export-bg="canvas"]');
|
|
57
|
+
bgNodes.forEach((node) => {
|
|
58
|
+
node.remove();
|
|
59
|
+
});
|
|
60
|
+
/*
|
|
61
|
+
* Set final dimensions.
|
|
62
|
+
*/
|
|
63
|
+
clone.setAttribute('width', String(width));
|
|
64
|
+
clone.setAttribute('height', String(height));
|
|
65
|
+
/*
|
|
66
|
+
* The viewBox is what allows us to export content
|
|
67
|
+
* that exists outside the original viewport.
|
|
68
|
+
*/
|
|
69
|
+
clone.setAttribute('viewBox', `${sourceX} ${sourceY} ${width} ${height}`);
|
|
70
|
+
/*
|
|
71
|
+
* Prevent unexpected aspect-ratio behavior.
|
|
72
|
+
*/
|
|
73
|
+
clone.setAttribute('preserveAspectRatio', 'xMinYMin meet');
|
|
74
|
+
/*
|
|
75
|
+
* Add explicit background.
|
|
76
|
+
*/
|
|
77
|
+
if (background === 'white') {
|
|
78
|
+
const whiteRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
79
|
+
whiteRect.setAttribute('x', String(sourceX));
|
|
80
|
+
whiteRect.setAttribute('y', String(sourceY));
|
|
81
|
+
whiteRect.setAttribute('width', String(width));
|
|
82
|
+
whiteRect.setAttribute('height', String(height));
|
|
83
|
+
whiteRect.setAttribute('fill', '#ffffff');
|
|
84
|
+
/*
|
|
85
|
+
* Keep background behind the diagram.
|
|
86
|
+
*/
|
|
87
|
+
clone.insertBefore(whiteRect, clone.firstChild);
|
|
88
|
+
}
|
|
89
|
+
/*
|
|
90
|
+
* Backwards compatibility:
|
|
91
|
+
*
|
|
92
|
+
* Older consumers/tests may call this function without
|
|
93
|
+
* sourceX/sourceY and expect the old margin behavior.
|
|
94
|
+
*
|
|
95
|
+
* In the real export flow sourceX/sourceY are provided,
|
|
96
|
+
* so we do NOT modify existing transforms.
|
|
97
|
+
*/
|
|
98
|
+
if (margin > 0 &&
|
|
99
|
+
sourceX === 0 &&
|
|
100
|
+
sourceY === 0) {
|
|
101
|
+
const scaledGroup = clone.querySelector('g[transform*="scale"]');
|
|
102
|
+
if (scaledGroup) {
|
|
103
|
+
const existingTransform = scaledGroup.getAttribute('transform') || '';
|
|
104
|
+
/*
|
|
105
|
+
* Preserve the original scale/transform.
|
|
106
|
+
*
|
|
107
|
+
* We prepend translate instead of replacing
|
|
108
|
+
* the transform entirely.
|
|
109
|
+
*/
|
|
110
|
+
scaledGroup.setAttribute('transform', `translate(${margin / 2}, ${margin / 2}) ${existingTransform}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return clone;
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=serializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../src/utils/serializer.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ok-canvas-export",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "Export complete SVG diagrams and canvases as high-quality images with full render pass to bypass viewport culling",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc --outDir dist",
|
|
21
|
+
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
22
|
+
"prepublishOnly": "bun test && bun run build",
|
|
23
|
+
"test": "bun test",
|
|
24
|
+
"test:watch": "bun test --watch",
|
|
25
|
+
"test:coverage": "bun test --coverage",
|
|
26
|
+
"publish:github": "npm publish --registry=https://npm.pkg.github.com"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"svg",
|
|
30
|
+
"canvas",
|
|
31
|
+
"export",
|
|
32
|
+
"diagram",
|
|
33
|
+
"image",
|
|
34
|
+
"render",
|
|
35
|
+
"png",
|
|
36
|
+
"high-quality",
|
|
37
|
+
"viewport",
|
|
38
|
+
"culling",
|
|
39
|
+
"react",
|
|
40
|
+
"bun"
|
|
41
|
+
],
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"sideEffects": false,
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/react": "^19.2.18",
|
|
49
|
+
"@types/react-dom": "^19.2.5",
|
|
50
|
+
"happy-dom": "^20.11.6",
|
|
51
|
+
"typescript": "^5.7.0"
|
|
52
|
+
}
|
|
53
|
+
}
|