mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Add image save function
This commit is contained in:
parent
69a3cdc7aa
commit
ac85bfdba7
|
@ -25,6 +25,7 @@ export { FiBellOff as IconFollowOff } from 'react-icons/fi';
|
|||
export { LuChevronDown as IconDropArrow } from 'react-icons/lu';
|
||||
export { LuChevronUp as IconDropArrowUp } from 'react-icons/lu';
|
||||
|
||||
export { LuImage as IconImage } from 'react-icons/lu';
|
||||
export { TbColumns as IconList } from 'react-icons/tb';
|
||||
export { TbColumnsOff as IconListOff } from 'react-icons/tb';
|
||||
export { BiFontFamily as IconText } from 'react-icons/bi';
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import clsx from 'clsx';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import InfoConstituenta from '@/components/info/InfoConstituenta';
|
||||
|
@ -16,6 +17,7 @@ import { applyNodeSizing } from '@/models/miscellaneousAPI';
|
|||
import { ConstituentaID, CstType } from '@/models/rsform';
|
||||
import { colorBgGraphNode } from '@/styling/color';
|
||||
import { PARAMETER, storage } from '@/utils/constants';
|
||||
import { convertBase64ToBlob } from '@/utils/utils';
|
||||
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
import GraphSelectors from './GraphSelectors';
|
||||
|
@ -149,6 +151,18 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
[setFilterParams]
|
||||
);
|
||||
|
||||
const handleSaveImage = useCallback(() => {
|
||||
if (!graphRef?.current) {
|
||||
return;
|
||||
}
|
||||
const data = graphRef.current.exportCanvas();
|
||||
try {
|
||||
fileDownload(convertBase64ToBlob(data), 'graph.png', 'data:image/png;base64');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, [graphRef]);
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
// Hotkeys implementation
|
||||
if (!controller.isContentEditable || controller.isProcessing) {
|
||||
|
@ -235,6 +249,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
onCreate={handleCreateCst}
|
||||
onDelete={handleDeleteCst}
|
||||
onResetViewpoint={() => setToggleResetView(prev => !prev)}
|
||||
onSaveImage={handleSaveImage}
|
||||
toggleOrbit={() => setOrbit(prev => !prev)}
|
||||
toggleFoldDerived={handleFoldDerived}
|
||||
toggleNoText={() =>
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
IconDestroy,
|
||||
IconFilter,
|
||||
IconFitImage,
|
||||
IconImage,
|
||||
IconNewItem,
|
||||
IconRotate3D,
|
||||
IconText,
|
||||
|
@ -31,6 +32,7 @@ interface GraphToolbarProps {
|
|||
onCreate: () => void;
|
||||
onDelete: () => void;
|
||||
onResetViewpoint: () => void;
|
||||
onSaveImage: () => void;
|
||||
|
||||
toggleFoldDerived: () => void;
|
||||
toggleNoText: () => void;
|
||||
|
@ -48,7 +50,8 @@ function GraphToolbar({
|
|||
showParamsDialog,
|
||||
onCreate,
|
||||
onDelete,
|
||||
onResetViewpoint
|
||||
onResetViewpoint,
|
||||
onSaveImage
|
||||
}: GraphToolbarProps) {
|
||||
const controller = useRSEdit();
|
||||
|
||||
|
@ -112,6 +115,11 @@ function GraphToolbar({
|
|||
onClick={onDelete}
|
||||
/>
|
||||
) : null}
|
||||
<MiniButton
|
||||
icon={<IconImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сохранить изображение'
|
||||
onClick={onSaveImage}
|
||||
/>
|
||||
<BadgeHelp topic={HelpTopic.GRAPH_TERM} className='max-w-[calc(100vw-4rem)]' offset={4} />
|
||||
</div>
|
||||
<SelectGraphToolbar
|
||||
|
|
|
@ -81,3 +81,17 @@ export function isResponseHtml(response?: AxiosResponse) {
|
|||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
return header.includes('text/html');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert base64 string to Blob uint8.
|
||||
*/
|
||||
export function convertBase64ToBlob(base64String: string): Uint8Array {
|
||||
const arr = base64String.split(',');
|
||||
const bstr = atob(arr[1]);
|
||||
let n = bstr.length;
|
||||
const uint8Array = new Uint8Array(n);
|
||||
while (n--) {
|
||||
uint8Array[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return uint8Array;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user