Add Export Functionality to Process Flow Canvas
- Introduced a new ExportCanvasModal component for exporting the process flow as PNG or PDF. - Enhanced ProcessFlowCanvas with exportToPNG and exportToPDF methods, utilizing html2canvas and jsPDF for rendering. - Updated the process builder page to integrate the export modal, allowing users to initiate exports easily. - Added UI elements for selecting export format and quality, improving user experience during the export process. - Implemented error handling and progress indicators for export operations, ensuring better feedback for users.
This commit is contained in:
parent
80038e00a3
commit
c4a143bb2d
441
components/process-flow/ExportCanvasModal.vue
Normal file
441
components/process-flow/ExportCanvasModal.vue
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="show" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" @click="closeModal">
|
||||||
|
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4" @click.stop>
|
||||||
|
<!-- Modal Header -->
|
||||||
|
<div class="px-6 py-4 border-b border-gray-200">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900">Export Canvas</h3>
|
||||||
|
<button
|
||||||
|
@click="closeModal"
|
||||||
|
class="text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
|
>
|
||||||
|
<span class="material-icons">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Body -->
|
||||||
|
<div class="px-6 py-4 space-y-4">
|
||||||
|
<!-- Export Format -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Export Format
|
||||||
|
</label>
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
@click="selectedFormat = 'png'"
|
||||||
|
:class="[
|
||||||
|
'flex items-center justify-center px-4 py-3 border rounded-lg transition-all',
|
||||||
|
selectedFormat === 'png'
|
||||||
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
|
: 'border-gray-300 hover:border-gray-400'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span class="material-icons mr-2 text-sm">image</span>
|
||||||
|
PNG Image
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="selectedFormat = 'pdf'"
|
||||||
|
:class="[
|
||||||
|
'flex items-center justify-center px-4 py-3 border rounded-lg transition-all',
|
||||||
|
selectedFormat === 'pdf'
|
||||||
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
|
: 'border-gray-300 hover:border-gray-400'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span class="material-icons mr-2 text-sm">picture_as_pdf</span>
|
||||||
|
PDF Document
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Quality Settings -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Quality
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="selectedQuality"
|
||||||
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
>
|
||||||
|
<option value="1">Low (1x)</option>
|
||||||
|
<option value="2">Standard (2x)</option>
|
||||||
|
<option value="3">High (3x)</option>
|
||||||
|
<option value="4">Ultra (4x)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Include Background -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input
|
||||||
|
id="includeBackground"
|
||||||
|
v-model="includeBackground"
|
||||||
|
type="checkbox"
|
||||||
|
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
|
>
|
||||||
|
<label for="includeBackground" class="ml-2 block text-sm text-gray-700">
|
||||||
|
Include background pattern
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- File Name -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
File Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="fileName"
|
||||||
|
type="text"
|
||||||
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
placeholder="Enter file name"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PDF Options (when PDF is selected) -->
|
||||||
|
<div v-if="selectedFormat === 'pdf'" class="space-y-3">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Page Size
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="pdfPageSize"
|
||||||
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
>
|
||||||
|
<option value="a4">A4 (210 × 297 mm)</option>
|
||||||
|
<option value="a3">A3 (297 × 420 mm)</option>
|
||||||
|
<option value="letter">Letter (8.5 × 11 in)</option>
|
||||||
|
<option value="legal">Legal (8.5 × 14 in)</option>
|
||||||
|
<option value="auto">Auto-fit</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Orientation
|
||||||
|
</label>
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
@click="pdfOrientation = 'portrait'"
|
||||||
|
:class="[
|
||||||
|
'flex items-center justify-center px-3 py-2 border rounded-lg transition-all',
|
||||||
|
pdfOrientation === 'portrait'
|
||||||
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
|
: 'border-gray-300 hover:border-gray-400'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span class="material-icons mr-1 text-sm">stay_current_portrait</span>
|
||||||
|
Portrait
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="pdfOrientation = 'landscape'"
|
||||||
|
:class="[
|
||||||
|
'flex items-center justify-center px-3 py-2 border rounded-lg transition-all',
|
||||||
|
pdfOrientation === 'landscape'
|
||||||
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
|
: 'border-gray-300 hover:border-gray-400'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span class="material-icons mr-1 text-sm">stay_current_landscape</span>
|
||||||
|
Landscape
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Export Progress -->
|
||||||
|
<div v-if="isExporting" class="flex flex-col items-center justify-center py-4">
|
||||||
|
<div class="flex items-center space-x-2 mb-3">
|
||||||
|
<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-blue-600"></div>
|
||||||
|
<span class="text-sm text-gray-600">{{ exportProgress }}</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
@click="forceClose"
|
||||||
|
class="text-xs text-gray-500 hover:text-gray-700 underline"
|
||||||
|
>
|
||||||
|
Cancel Export
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Footer -->
|
||||||
|
<div class="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
|
||||||
|
<button
|
||||||
|
@click="closeModal"
|
||||||
|
:disabled="isExporting"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="handleExport"
|
||||||
|
:disabled="isExporting || !fileName.trim()"
|
||||||
|
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
|
||||||
|
>
|
||||||
|
<span class="material-icons mr-1 text-sm">download</span>
|
||||||
|
Export {{ selectedFormat.toUpperCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
|
// Props
|
||||||
|
const props = defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
processName: {
|
||||||
|
type: String,
|
||||||
|
default: 'process'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Emits
|
||||||
|
const emit = defineEmits(['close', 'export'])
|
||||||
|
|
||||||
|
// State
|
||||||
|
const selectedFormat = ref('png')
|
||||||
|
const selectedQuality = ref('2')
|
||||||
|
const includeBackground = ref(true)
|
||||||
|
const fileName = ref('')
|
||||||
|
const isExporting = ref(false)
|
||||||
|
const exportProgress = ref('')
|
||||||
|
|
||||||
|
// PDF specific options
|
||||||
|
const pdfPageSize = ref('a4')
|
||||||
|
const pdfOrientation = ref('landscape')
|
||||||
|
|
||||||
|
// Computed
|
||||||
|
const exportOptions = computed(() => ({
|
||||||
|
format: selectedFormat.value,
|
||||||
|
quality: parseInt(selectedQuality.value),
|
||||||
|
includeBackground: includeBackground.value,
|
||||||
|
fileName: fileName.value.trim(),
|
||||||
|
pdf: {
|
||||||
|
pageSize: pdfPageSize.value,
|
||||||
|
orientation: pdfOrientation.value
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Watch for prop changes to set default filename
|
||||||
|
watch(() => props.processName, (newName) => {
|
||||||
|
if (newName && !fileName.value) {
|
||||||
|
fileName.value = newName.replace(/[^a-zA-Z0-9]/g, '_')
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// Watch format changes to update filename extension
|
||||||
|
watch(selectedFormat, (newFormat) => {
|
||||||
|
if (fileName.value) {
|
||||||
|
const nameWithoutExt = fileName.value.replace(/\.(png|pdf)$/i, '')
|
||||||
|
fileName.value = `${nameWithoutExt}.${newFormat}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
const closeModal = () => {
|
||||||
|
if (!isExporting.value) {
|
||||||
|
resetForm()
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
selectedFormat.value = 'png'
|
||||||
|
selectedQuality.value = '2'
|
||||||
|
includeBackground.value = true
|
||||||
|
fileName.value = ''
|
||||||
|
isExporting.value = false
|
||||||
|
exportProgress.value = ''
|
||||||
|
pdfPageSize.value = 'a4'
|
||||||
|
pdfOrientation.value = 'landscape'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force close modal (for error recovery)
|
||||||
|
const forceClose = () => {
|
||||||
|
resetForm()
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExport = async () => {
|
||||||
|
if (!fileName.value.trim() || isExporting.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
isExporting.value = true
|
||||||
|
exportProgress.value = 'Preparing export...'
|
||||||
|
|
||||||
|
// Update progress during export
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isExporting.value) {
|
||||||
|
exportProgress.value = 'Capturing canvas...'
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isExporting.value) {
|
||||||
|
exportProgress.value = 'Processing image...'
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
// Emit export event with options
|
||||||
|
await emit('export', exportOptions.value)
|
||||||
|
|
||||||
|
// Success state - ensure we reset the loading state
|
||||||
|
exportProgress.value = 'Export completed!'
|
||||||
|
isExporting.value = false
|
||||||
|
|
||||||
|
// Close modal after successful export
|
||||||
|
setTimeout(() => {
|
||||||
|
closeModal()
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Export error:', error)
|
||||||
|
exportProgress.value = 'Export failed - ' + (error.message || 'Unknown error')
|
||||||
|
isExporting.value = false
|
||||||
|
setTimeout(() => {
|
||||||
|
exportProgress.value = ''
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update export progress
|
||||||
|
const updateProgress = (message) => {
|
||||||
|
exportProgress.value = message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose methods for parent component
|
||||||
|
defineExpose({
|
||||||
|
updateProgress,
|
||||||
|
forceClose,
|
||||||
|
resetForm
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Custom styling for better modal appearance */
|
||||||
|
.modal-enter-active, .modal-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-enter-from, .modal-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure proper z-index for dropdowns */
|
||||||
|
select {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced button styles */
|
||||||
|
button {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover:not(:disabled) {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format selection buttons */
|
||||||
|
.format-button {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.format-button::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.format-button:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quality select styling */
|
||||||
|
select:focus {
|
||||||
|
ring-width: 2px;
|
||||||
|
ring-color: rgb(59 130 246);
|
||||||
|
ring-opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox styling */
|
||||||
|
input[type="checkbox"]:checked {
|
||||||
|
background-color: rgb(59 130 246);
|
||||||
|
border-color: rgb(59 130 246);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading spinner animation */
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-spin {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress indicator styling */
|
||||||
|
.export-progress {
|
||||||
|
background: linear-gradient(90deg, #3b82f6, #60a5fa, #3b82f6);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: progressShimmer 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progressShimmer {
|
||||||
|
0% {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal backdrop blur effect */
|
||||||
|
.fixed.inset-0 {
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
-webkit-backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced modal shadow */
|
||||||
|
.bg-white.rounded-lg.shadow-xl {
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File input styling */
|
||||||
|
input[type="text"]:focus {
|
||||||
|
ring-width: 2px;
|
||||||
|
ring-color: rgb(59 130 246);
|
||||||
|
ring-opacity: 0.5;
|
||||||
|
border-color: rgb(59 130 246);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.bg-white.rounded-lg.shadow-xl {
|
||||||
|
margin: 1rem;
|
||||||
|
max-width: calc(100vw - 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid.grid-cols-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -106,6 +106,7 @@ const {
|
|||||||
onEdgesChange,
|
onEdgesChange,
|
||||||
onConnect,
|
onConnect,
|
||||||
fitView,
|
fitView,
|
||||||
|
fitBounds,
|
||||||
project,
|
project,
|
||||||
removeNodes,
|
removeNodes,
|
||||||
removeEdges,
|
removeEdges,
|
||||||
@ -890,6 +891,303 @@ const onDragOver = (event) => {
|
|||||||
event.dataTransfer.dropEffect = "copy";
|
event.dataTransfer.dropEffect = "copy";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Export functions for canvas export
|
||||||
|
async function exportToPNG(options = {}) {
|
||||||
|
try {
|
||||||
|
const { default: html2canvas } = await import('html2canvas');
|
||||||
|
|
||||||
|
// Find Vue Flow elements
|
||||||
|
const vueFlowContainer = document.querySelector('.vue-flow');
|
||||||
|
const vueFlowViewport = document.querySelector('.vue-flow__viewport');
|
||||||
|
|
||||||
|
if (!vueFlowContainer || !vueFlowViewport) {
|
||||||
|
throw new Error('Vue Flow elements not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Vue Flow elements found:', { container: vueFlowContainer, viewport: vueFlowViewport });
|
||||||
|
|
||||||
|
// Wait for any animations or transitions to complete
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Get current nodes from Vue Flow state
|
||||||
|
const currentNodes = nodes.value || [];
|
||||||
|
|
||||||
|
console.log('Current nodes for export:', currentNodes.length);
|
||||||
|
|
||||||
|
if (currentNodes.length === 0) {
|
||||||
|
throw new Error('No nodes found to export');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store original viewport
|
||||||
|
const originalViewport = getViewport();
|
||||||
|
|
||||||
|
// Use Vue Flow's fitView to ensure all nodes are visible and centered
|
||||||
|
fitView({
|
||||||
|
padding: 0.2,
|
||||||
|
includeHiddenNodes: false,
|
||||||
|
minZoom: 0.5,
|
||||||
|
maxZoom: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for the view to settle
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Use html2canvas to capture the entire Vue Flow container
|
||||||
|
const html2canvasOptions = {
|
||||||
|
backgroundColor: options.includeBackground ? '#f8fafc' : 'white',
|
||||||
|
scale: options.quality || 2,
|
||||||
|
useCORS: true,
|
||||||
|
allowTaint: false,
|
||||||
|
foreignObjectRendering: true,
|
||||||
|
imageTimeout: 20000,
|
||||||
|
logging: false,
|
||||||
|
removeContainer: false,
|
||||||
|
// Capture the entire container instead of just viewport
|
||||||
|
width: vueFlowContainer.offsetWidth,
|
||||||
|
height: vueFlowContainer.offsetHeight,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
scrollX: 0,
|
||||||
|
scrollY: 0,
|
||||||
|
windowWidth: vueFlowContainer.offsetWidth,
|
||||||
|
windowHeight: vueFlowContainer.offsetHeight,
|
||||||
|
onclone: function(clonedDoc) {
|
||||||
|
const clonedContainer = clonedDoc.querySelector('.vue-flow');
|
||||||
|
const clonedViewport = clonedDoc.querySelector('.vue-flow__viewport');
|
||||||
|
|
||||||
|
if (clonedContainer && clonedViewport) {
|
||||||
|
// Ensure container styles don't interfere with capture
|
||||||
|
clonedContainer.style.overflow = 'visible';
|
||||||
|
clonedContainer.style.position = 'relative';
|
||||||
|
clonedViewport.style.overflow = 'visible';
|
||||||
|
|
||||||
|
// Make sure all content is visible in the cloned document
|
||||||
|
const allElements = clonedContainer.querySelectorAll('*');
|
||||||
|
allElements.forEach(el => {
|
||||||
|
// Force visibility of all elements
|
||||||
|
if (el.style.display === 'none') {
|
||||||
|
el.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (el.style.visibility === 'hidden') {
|
||||||
|
el.style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
if (el.style.opacity === '0' || el.style.opacity === '') {
|
||||||
|
el.style.opacity = '1';
|
||||||
|
}
|
||||||
|
// Remove any transforms that might hide content
|
||||||
|
if (el.style.transform && el.style.transform.includes('scale(0)')) {
|
||||||
|
el.style.transform = el.style.transform.replace(/scale\([0-9.]*\)/, 'scale(1)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Cloned document prepared, elements made visible');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ignoreElements: function(element) {
|
||||||
|
// Only ignore UI controls, not the actual process content
|
||||||
|
return element.classList.contains('vue-flow__controls') ||
|
||||||
|
element.classList.contains('vue-flow__minimap') ||
|
||||||
|
(element.classList.contains('vue-flow__background') && !options.includeBackground);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('html2canvas options:', html2canvasOptions);
|
||||||
|
|
||||||
|
// Capture the Vue Flow container
|
||||||
|
const canvas = await html2canvas(vueFlowContainer, html2canvasOptions);
|
||||||
|
|
||||||
|
console.log('Canvas captured successfully:', canvas.width, 'x', canvas.height);
|
||||||
|
|
||||||
|
// Restore original viewport
|
||||||
|
setViewport(originalViewport);
|
||||||
|
|
||||||
|
// Convert to blob and download
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
reject(new Error('Failed to create image blob'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = options.fileName || 'process-flow.png';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
resolve();
|
||||||
|
}, 'image/png', 0.95);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error exporting to PNG:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportToPDF(options = {}) {
|
||||||
|
try {
|
||||||
|
// Use the same improved capture logic as PNG export
|
||||||
|
const { default: html2canvas } = await import('html2canvas');
|
||||||
|
|
||||||
|
const vueFlowContainer = document.querySelector('.vue-flow');
|
||||||
|
const vueFlowViewport = document.querySelector('.vue-flow__viewport');
|
||||||
|
|
||||||
|
if (!vueFlowContainer || !vueFlowViewport) {
|
||||||
|
throw new Error('Vue Flow elements not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Vue Flow elements found for PDF:', { container: vueFlowContainer, viewport: vueFlowViewport });
|
||||||
|
|
||||||
|
// Wait for any animations or transitions to complete
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Get current nodes from Vue Flow state
|
||||||
|
const currentNodes = nodes.value || [];
|
||||||
|
|
||||||
|
console.log('Current nodes for PDF export:', currentNodes.length);
|
||||||
|
|
||||||
|
if (currentNodes.length === 0) {
|
||||||
|
throw new Error('No nodes found to export');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store original viewport
|
||||||
|
const originalViewport = getViewport();
|
||||||
|
|
||||||
|
// Use Vue Flow's fitView to ensure all nodes are visible and centered
|
||||||
|
fitView({
|
||||||
|
padding: 0.2,
|
||||||
|
includeHiddenNodes: false,
|
||||||
|
minZoom: 0.5,
|
||||||
|
maxZoom: 2
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for the view to settle
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Use html2canvas to capture the entire viewport
|
||||||
|
const html2canvasOptions = {
|
||||||
|
backgroundColor: options.includeBackground ? '#f8fafc' : '#ffffff',
|
||||||
|
scale: options.quality || 2,
|
||||||
|
useCORS: true,
|
||||||
|
allowTaint: false,
|
||||||
|
foreignObjectRendering: true,
|
||||||
|
imageTimeout: 20000,
|
||||||
|
logging: false,
|
||||||
|
removeContainer: false,
|
||||||
|
// Capture the entire viewport
|
||||||
|
width: vueFlowViewport.offsetWidth,
|
||||||
|
height: vueFlowViewport.offsetHeight,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
scrollX: 0,
|
||||||
|
scrollY: 0,
|
||||||
|
windowWidth: vueFlowViewport.offsetWidth,
|
||||||
|
windowHeight: vueFlowViewport.offsetHeight,
|
||||||
|
onclone: function(clonedDoc) {
|
||||||
|
const clonedContainer = clonedDoc.querySelector('.vue-flow');
|
||||||
|
const clonedViewport = clonedDoc.querySelector('.vue-flow__viewport');
|
||||||
|
|
||||||
|
if (clonedContainer && clonedViewport) {
|
||||||
|
// Ensure container styles don't interfere with capture
|
||||||
|
clonedContainer.style.overflow = 'visible';
|
||||||
|
clonedContainer.style.position = 'relative';
|
||||||
|
clonedViewport.style.overflow = 'visible';
|
||||||
|
|
||||||
|
// Make sure all content is visible in the cloned document
|
||||||
|
const allElements = clonedContainer.querySelectorAll('*');
|
||||||
|
allElements.forEach(el => {
|
||||||
|
// Force visibility of all elements
|
||||||
|
if (el.style.display === 'none') {
|
||||||
|
el.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (el.style.visibility === 'hidden') {
|
||||||
|
el.style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
if (el.style.opacity === '0' || el.style.opacity === '') {
|
||||||
|
el.style.opacity = '1';
|
||||||
|
}
|
||||||
|
// Remove any transforms that might hide content
|
||||||
|
if (el.style.transform && el.style.transform.includes('scale(0)')) {
|
||||||
|
el.style.transform = el.style.transform.replace(/scale\([0-9.]*\)/, 'scale(1)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ignoreElements: function(element) {
|
||||||
|
// Only ignore UI controls, not the actual process content
|
||||||
|
return element.classList.contains('vue-flow__controls') ||
|
||||||
|
element.classList.contains('vue-flow__minimap') ||
|
||||||
|
(element.classList.contains('vue-flow__background') && !options.includeBackground);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Capture the Vue Flow container
|
||||||
|
const canvas = await html2canvas(vueFlowContainer, html2canvasOptions);
|
||||||
|
|
||||||
|
// Restore original viewport
|
||||||
|
setViewport(originalViewport);
|
||||||
|
|
||||||
|
// Now create PDF
|
||||||
|
const { jsPDF } = await import('jspdf');
|
||||||
|
const imgData = canvas.toDataURL('image/png');
|
||||||
|
|
||||||
|
console.log('PDF Canvas captured:', canvas.width, 'x', canvas.height);
|
||||||
|
|
||||||
|
// Calculate dimensions
|
||||||
|
const imgWidth = canvas.width;
|
||||||
|
const imgHeight = canvas.height;
|
||||||
|
|
||||||
|
// PDF page configurations
|
||||||
|
const pageConfigs = {
|
||||||
|
a4: { width: 210, height: 297 },
|
||||||
|
a3: { width: 297, height: 420 },
|
||||||
|
letter: { width: 216, height: 279 },
|
||||||
|
legal: { width: 216, height: 356 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let pageConfig = pageConfigs[options.pdf?.pageSize] || pageConfigs.a4;
|
||||||
|
const orientation = options.pdf?.orientation || 'landscape';
|
||||||
|
|
||||||
|
if (orientation === 'landscape') {
|
||||||
|
[pageConfig.width, pageConfig.height] = [pageConfig.height, pageConfig.width];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create PDF
|
||||||
|
const pdf = new jsPDF({
|
||||||
|
orientation: orientation,
|
||||||
|
unit: 'mm',
|
||||||
|
format: options.pdf?.pageSize === 'auto' ? [imgWidth * 0.26458, imgHeight * 0.26458] : [pageConfig.width, pageConfig.height]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.pdf?.pageSize === 'auto') {
|
||||||
|
// Auto-fit: scale image to fit exactly
|
||||||
|
pdf.addImage(imgData, 'PNG', 0, 0, imgWidth * 0.26458, imgHeight * 0.26458);
|
||||||
|
} else {
|
||||||
|
// Fixed page size: scale to fit within margins
|
||||||
|
const margin = 10;
|
||||||
|
const maxWidth = pageConfig.width - (margin * 2);
|
||||||
|
const maxHeight = pageConfig.height - (margin * 2);
|
||||||
|
|
||||||
|
const scaleFactor = Math.min(maxWidth / (imgWidth * 0.26458), maxHeight / (imgHeight * 0.26458));
|
||||||
|
const scaledWidth = (imgWidth * 0.26458) * scaleFactor;
|
||||||
|
const scaledHeight = (imgHeight * 0.26458) * scaleFactor;
|
||||||
|
|
||||||
|
const x = (pageConfig.width - scaledWidth) / 2;
|
||||||
|
const y = (pageConfig.height - scaledHeight) / 2;
|
||||||
|
|
||||||
|
pdf.addImage(imgData, 'PNG', x, y, scaledWidth, scaledHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the PDF
|
||||||
|
pdf.save(options.fileName || 'process-flow.pdf');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error exporting to PDF:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Define methods to expose to parent components
|
// Define methods to expose to parent components
|
||||||
defineExpose({
|
defineExpose({
|
||||||
updateNode,
|
updateNode,
|
||||||
@ -901,6 +1199,9 @@ defineExpose({
|
|||||||
// Add Vue Flow save/restore methods
|
// Add Vue Flow save/restore methods
|
||||||
toObject,
|
toObject,
|
||||||
fromObject,
|
fromObject,
|
||||||
|
// Add export methods
|
||||||
|
exportToPNG,
|
||||||
|
exportToPDF,
|
||||||
// Add direct access to Vue Flow methods for production fallbacks
|
// Add direct access to Vue Flow methods for production fallbacks
|
||||||
setNodes: (newNodes) => {
|
setNodes: (newNodes) => {
|
||||||
try {
|
try {
|
||||||
|
@ -60,7 +60,9 @@
|
|||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"floating-vue": "^2.0.0-beta.24",
|
"floating-vue": "^2.0.0-beta.24",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"jspdf": "^3.0.1",
|
||||||
"luxon": "^3.1.0",
|
"luxon": "^3.1.0",
|
||||||
"maska": "^1.5.0",
|
"maska": "^1.5.0",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
|
@ -23,6 +23,7 @@ import SubprocessNodeConfigurationModal from '~/components/process-flow/Subproce
|
|||||||
import ProcessTemplatesModal from '~/components/ProcessTemplatesModal.vue';
|
import ProcessTemplatesModal from '~/components/ProcessTemplatesModal.vue';
|
||||||
import ProcessSettingsModal from '~/components/process-flow/ProcessSettingsModal.vue';
|
import ProcessSettingsModal from '~/components/process-flow/ProcessSettingsModal.vue';
|
||||||
import ProcessHistoryModal from '~/components/ProcessHistoryModal.vue';
|
import ProcessHistoryModal from '~/components/ProcessHistoryModal.vue';
|
||||||
|
import ExportCanvasModal from '~/components/process-flow/ExportCanvasModal.vue';
|
||||||
|
|
||||||
// Define page meta
|
// Define page meta
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@ -94,6 +95,7 @@ const showTemplatesModal = ref(false);
|
|||||||
const showProcessSettings = ref(false);
|
const showProcessSettings = ref(false);
|
||||||
const showDropdown = ref(false);
|
const showDropdown = ref(false);
|
||||||
const showProcessHistoryModal = ref(false);
|
const showProcessHistoryModal = ref(false);
|
||||||
|
const showExportModal = ref(false);
|
||||||
|
|
||||||
// Add mobile responsive state
|
// Add mobile responsive state
|
||||||
const showLeftPanel = ref(true);
|
const showLeftPanel = ref(true);
|
||||||
@ -1830,6 +1832,29 @@ const handleProcessRestored = (restoredProcess) => {
|
|||||||
toast.success('Process has been restored successfully');
|
toast.success('Process has been restored successfully');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle canvas export
|
||||||
|
const handleExportCanvas = async (options) => {
|
||||||
|
try {
|
||||||
|
if (!processFlowCanvas.value) {
|
||||||
|
throw new Error('Canvas not available for export');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Starting export with options:', options);
|
||||||
|
|
||||||
|
if (options.format === 'png') {
|
||||||
|
await processFlowCanvas.value.exportToPNG(options);
|
||||||
|
toast.success('Canvas exported as PNG successfully');
|
||||||
|
} else if (options.format === 'pdf') {
|
||||||
|
await processFlowCanvas.value.exportToPDF(options);
|
||||||
|
toast.success('Canvas exported as PDF successfully');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Export error:', error);
|
||||||
|
toast.error(`Export failed: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Navigate to variables page
|
// Navigate to variables page
|
||||||
const navigateToVariables = () => {
|
const navigateToVariables = () => {
|
||||||
confirmNavigation('/variables');
|
confirmNavigation('/variables');
|
||||||
@ -2324,6 +2349,14 @@ const sendToBack = () => {
|
|||||||
<span class="hidden lg:inline">History</span>
|
<span class="hidden lg:inline">History</span>
|
||||||
</RsButton>
|
</RsButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Export button - Hidden on mobile -->
|
||||||
|
<div class="mr-1 md:mr-2 border-r border-gray-200 pr-1 md:pr-2 hidden md:block">
|
||||||
|
<RsButton @click="showExportModal = true" variant="secondary" size="sm" :disabled="!hasCurrentProcess">
|
||||||
|
<Icon name="material-symbols:download" class="mr-0 md:mr-1" />
|
||||||
|
<span class="hidden lg:inline">Export</span>
|
||||||
|
</RsButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Secondary actions -->
|
<!-- Secondary actions -->
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@ -2343,6 +2376,10 @@ const sendToBack = () => {
|
|||||||
<Icon name="material-symbols:history" class="mr-2 w-4 h-4" />
|
<Icon name="material-symbols:history" class="mr-2 w-4 h-4" />
|
||||||
<span>History</span>
|
<span>History</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button @click="showExportModal = true; showDropdown = false" :disabled="!hasCurrentProcess" class="w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center disabled:opacity-50">
|
||||||
|
<Icon name="material-symbols:download" class="mr-2 w-4 h-4" />
|
||||||
|
<span>Export</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button @click="showProcessSettings = true; showDropdown = false" class="w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center">
|
<button @click="showProcessSettings = true; showDropdown = false" class="w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center">
|
||||||
<Icon name="material-symbols:tune" class="mr-2 w-4 h-4" />
|
<Icon name="material-symbols:tune" class="mr-2 w-4 h-4" />
|
||||||
@ -3001,6 +3038,14 @@ const sendToBack = () => {
|
|||||||
@close="showProcessHistoryModal = false"
|
@close="showProcessHistoryModal = false"
|
||||||
@restored="handleProcessRestored"
|
@restored="handleProcessRestored"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Export Canvas Modal -->
|
||||||
|
<ExportCanvasModal
|
||||||
|
:show="showExportModal"
|
||||||
|
:process-name="processStore.currentProcess?.name || 'process'"
|
||||||
|
@close="showExportModal = false"
|
||||||
|
@export="handleExportCanvas"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</client-only>
|
</client-only>
|
||||||
</div>
|
</div>
|
||||||
|
137
yarn.lock
137
yarn.lock
@ -969,6 +969,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.12.5", "@babel/runtime@^7.26.7":
|
||||||
|
version "7.27.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6"
|
||||||
|
integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==
|
||||||
|
|
||||||
"@babel/standalone@^7.22.9":
|
"@babel/standalone@^7.22.9":
|
||||||
version "7.22.10"
|
version "7.22.10"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.22.10.tgz#0a39a85488d61d301751cc074ea77c44aacb9d07"
|
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.22.10.tgz#0a39a85488d61d301751cc074ea77c44aacb9d07"
|
||||||
@ -2303,6 +2308,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||||
|
|
||||||
|
"@types/raf@^3.4.0":
|
||||||
|
version "3.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.3.tgz#85f1d1d17569b28b8db45e16e996407a56b0ab04"
|
||||||
|
integrity sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==
|
||||||
|
|
||||||
"@types/resize-observer-browser@^0.1.7":
|
"@types/resize-observer-browser@^0.1.7":
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz#294aaadf24ac6580b8fbd1fe3ab7b59fe85f9ef3"
|
resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz#294aaadf24ac6580b8fbd1fe3ab7b59fe85f9ef3"
|
||||||
@ -2330,6 +2340,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
|
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
|
||||||
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
|
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
|
||||||
|
|
||||||
|
"@types/trusted-types@^2.0.7":
|
||||||
|
version "2.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
|
||||||
|
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
|
||||||
|
|
||||||
"@types/web-bluetooth@^0.0.16":
|
"@types/web-bluetooth@^0.0.16":
|
||||||
version "0.0.16"
|
version "0.0.16"
|
||||||
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8"
|
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8"
|
||||||
@ -3075,6 +3090,11 @@ at-least-node@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||||
|
|
||||||
|
atob@^2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
|
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||||
|
|
||||||
attr-accept@^2.2.2:
|
attr-accept@^2.2.2:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
|
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
|
||||||
@ -3136,6 +3156,11 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
base64-arraybuffer@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
|
||||||
|
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
|
||||||
|
|
||||||
base64-js@^1.3.1:
|
base64-js@^1.3.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
@ -3211,6 +3236,11 @@ browserslist@^4.0.0, browserslist@^4.21.4, browserslist@^4.21.5, browserslist@^4
|
|||||||
node-releases "^2.0.13"
|
node-releases "^2.0.13"
|
||||||
update-browserslist-db "^1.0.11"
|
update-browserslist-db "^1.0.11"
|
||||||
|
|
||||||
|
btoa@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73"
|
||||||
|
integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==
|
||||||
|
|
||||||
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
|
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
|
||||||
version "0.2.13"
|
version "0.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||||
@ -3314,6 +3344,20 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001517:
|
|||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601"
|
||||||
integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==
|
integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==
|
||||||
|
|
||||||
|
canvg@^3.0.11:
|
||||||
|
version "3.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/canvg/-/canvg-3.0.11.tgz#4b4290a6c7fa36871fac2b14e432eff33b33cf2b"
|
||||||
|
integrity sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
"@types/raf" "^3.4.0"
|
||||||
|
core-js "^3.8.3"
|
||||||
|
raf "^3.4.1"
|
||||||
|
regenerator-runtime "^0.13.7"
|
||||||
|
rgbcolor "^1.0.1"
|
||||||
|
stackblur-canvas "^2.0.0"
|
||||||
|
svg-pathdata "^6.0.3"
|
||||||
|
|
||||||
chalk@^2.4.2:
|
chalk@^2.4.2:
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||||
@ -3573,6 +3617,11 @@ core-js-pure@^3.30.2:
|
|||||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.32.0.tgz#5d79f85da7a4373e9a06494ccbef995a4c639f8b"
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.32.0.tgz#5d79f85da7a4373e9a06494ccbef995a4c639f8b"
|
||||||
integrity sha512-qsev1H+dTNYpDUEURRuOXMvpdtAnNEvQWS/FMJ2Vb5AY8ZP4rAPQldkE27joykZPJTe0+IVgHZYh1P5Xu1/i1g==
|
integrity sha512-qsev1H+dTNYpDUEURRuOXMvpdtAnNEvQWS/FMJ2Vb5AY8ZP4rAPQldkE27joykZPJTe0+IVgHZYh1P5Xu1/i1g==
|
||||||
|
|
||||||
|
core-js@^3.6.0, core-js@^3.8.3:
|
||||||
|
version "3.44.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.44.0.tgz#db4fd4fa07933c1d6898c8b112a1119a9336e959"
|
||||||
|
integrity sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||||
@ -3650,6 +3699,13 @@ css-declaration-sorter@^6.3.1:
|
|||||||
resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71"
|
resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71"
|
||||||
integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==
|
integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==
|
||||||
|
|
||||||
|
css-line-break@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
|
||||||
|
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
|
||||||
|
dependencies:
|
||||||
|
utrie "^1.0.2"
|
||||||
|
|
||||||
css-loader@^5.0.0:
|
css-loader@^5.0.0:
|
||||||
version "5.2.7"
|
version "5.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae"
|
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae"
|
||||||
@ -4125,6 +4181,13 @@ domhandler@^5.0.2, domhandler@^5.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.3.0"
|
domelementtype "^2.3.0"
|
||||||
|
|
||||||
|
dompurify@^3.2.4:
|
||||||
|
version "3.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.6.tgz#ca040a6ad2b88e2a92dc45f38c79f84a714a1cad"
|
||||||
|
integrity sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==
|
||||||
|
optionalDependencies:
|
||||||
|
"@types/trusted-types" "^2.0.7"
|
||||||
|
|
||||||
domutils@^2.8.0:
|
domutils@^2.8.0:
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
||||||
@ -4849,6 +4912,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
|||||||
node-domexception "^1.0.0"
|
node-domexception "^1.0.0"
|
||||||
web-streams-polyfill "^3.0.3"
|
web-streams-polyfill "^3.0.3"
|
||||||
|
|
||||||
|
fflate@^0.8.1:
|
||||||
|
version "0.8.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
|
||||||
|
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
|
||||||
|
|
||||||
file-entry-cache@^6.0.1:
|
file-entry-cache@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||||
@ -5337,6 +5405,14 @@ html-tags@^3.3.1:
|
|||||||
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
|
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
|
||||||
integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==
|
integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==
|
||||||
|
|
||||||
|
html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1:
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
|
||||||
|
integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
|
||||||
|
dependencies:
|
||||||
|
css-line-break "^2.1.0"
|
||||||
|
text-segmentation "^1.0.3"
|
||||||
|
|
||||||
http-assert@^1.3.0:
|
http-assert@^1.3.0:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
||||||
@ -5884,6 +5960,21 @@ jsonwebtoken@^8.5.1:
|
|||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
semver "^5.6.0"
|
semver "^5.6.0"
|
||||||
|
|
||||||
|
jspdf@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jspdf/-/jspdf-3.0.1.tgz#d81e1964f354f60412516eb2449ea2cccd4d2a3b"
|
||||||
|
integrity sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.26.7"
|
||||||
|
atob "^2.1.2"
|
||||||
|
btoa "^1.2.1"
|
||||||
|
fflate "^0.8.1"
|
||||||
|
optionalDependencies:
|
||||||
|
canvg "^3.0.11"
|
||||||
|
core-js "^3.6.0"
|
||||||
|
dompurify "^3.2.4"
|
||||||
|
html2canvas "^1.0.0-rc.5"
|
||||||
|
|
||||||
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
|
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
|
||||||
version "3.3.5"
|
version "3.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
|
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
|
||||||
@ -7138,6 +7229,11 @@ perfect-scrollbar@^1.5.5:
|
|||||||
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz#41a211a2fb52a7191eff301432134ea47052b27f"
|
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz#41a211a2fb52a7191eff301432134ea47052b27f"
|
||||||
integrity sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==
|
integrity sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==
|
||||||
|
|
||||||
|
performance-now@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
|
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
|
||||||
|
|
||||||
picocolors@^0.2.1:
|
picocolors@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
|
||||||
@ -7897,6 +7993,13 @@ radix3@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.0.1.tgz#de0ac16234f8a63288645854a54fc26e45a4a8eb"
|
resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.0.1.tgz#de0ac16234f8a63288645854a54fc26e45a4a8eb"
|
||||||
integrity sha512-y+AcwZ3HcUIGc9zGsNVf5+BY/LxL+z+4h4J3/pp8jxSmy1STaCocPS3qrj4tA5ehUSzqtqK+0Aygvz/r/8vy4g==
|
integrity sha512-y+AcwZ3HcUIGc9zGsNVf5+BY/LxL+z+4h4J3/pp8jxSmy1STaCocPS3qrj4tA5ehUSzqtqK+0Aygvz/r/8vy4g==
|
||||||
|
|
||||||
|
raf@^3.4.1:
|
||||||
|
version "3.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
|
||||||
|
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
|
||||||
|
dependencies:
|
||||||
|
performance-now "^2.1.0"
|
||||||
|
|
||||||
randombytes@^2.1.0:
|
randombytes@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||||
@ -7990,6 +8093,11 @@ regenerate@^1.4.2:
|
|||||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
|
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
|
||||||
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
|
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.7:
|
||||||
|
version "0.13.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||||
|
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||||
|
|
||||||
regenerator-runtime@^0.14.0:
|
regenerator-runtime@^0.14.0:
|
||||||
version "0.14.0"
|
version "0.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
||||||
@ -8105,6 +8213,11 @@ reusify@^1.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
|
rgbcolor@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d"
|
||||||
|
integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==
|
||||||
|
|
||||||
rimraf@^3.0.2:
|
rimraf@^3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||||
@ -8393,6 +8506,11 @@ stable@^0.1.8:
|
|||||||
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||||
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
|
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
|
||||||
|
|
||||||
|
stackblur-canvas@^2.0.0:
|
||||||
|
version "2.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz#af931277d0b5096df55e1f91c530043e066989b6"
|
||||||
|
integrity sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==
|
||||||
|
|
||||||
standard-as-callback@^2.1.0:
|
standard-as-callback@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
|
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
|
||||||
@ -8593,6 +8711,11 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
|
svg-pathdata@^6.0.3:
|
||||||
|
version "6.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac"
|
||||||
|
integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==
|
||||||
|
|
||||||
svg-tags@^1.0.0:
|
svg-tags@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
|
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
|
||||||
@ -8791,6 +8914,13 @@ terser@^5.0.0, terser@^5.17.4:
|
|||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map-support "~0.5.20"
|
source-map-support "~0.5.20"
|
||||||
|
|
||||||
|
text-segmentation@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
|
||||||
|
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
|
||||||
|
dependencies:
|
||||||
|
utrie "^1.0.2"
|
||||||
|
|
||||||
text-table@^0.2.0:
|
text-table@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
@ -9159,6 +9289,13 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||||
|
|
||||||
|
utrie@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
|
||||||
|
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer "^1.0.2"
|
||||||
|
|
||||||
uuid@^10.0.0:
|
uuid@^10.0.0:
|
||||||
version "10.0.0"
|
version "10.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user