F: Remove unusable save to image feature
This commit is contained in:
parent
091182cf5f
commit
957313dd43
|
@ -45,7 +45,6 @@ This readme file is used mostly to document project dependencies and conventions
|
|||
- js-file-download
|
||||
- use-debounce
|
||||
- qrcode.react
|
||||
- html-to-image
|
||||
- zustand
|
||||
- zod
|
||||
- @hookform/resolvers
|
||||
|
|
5
TODO.txt
5
TODO.txt
|
@ -8,9 +8,10 @@ For more specific TODOs see comments in code
|
|||
- Landing page
|
||||
- Design first user experience
|
||||
- Demo sandbox for anonymous users
|
||||
- Save react-flow to vector image
|
||||
|
||||
User profile:
|
||||
- Settings + settings server persistency
|
||||
- Settings server persistency
|
||||
- Profile pictures
|
||||
- Custom LibraryItem lists
|
||||
- Custom user filters and sharing filters
|
||||
|
@ -39,7 +40,6 @@ User profile:
|
|||
|
||||
[Tech]
|
||||
- duplicate syntax parsing and type info calculations to client. Consider moving backend to Nodejs or embedding c++ lib
|
||||
- Testing E2E playwright
|
||||
|
||||
|
||||
[Deployment]
|
||||
|
@ -60,7 +60,6 @@ Research and consider integration
|
|||
- skeleton loading
|
||||
https://react.dev/reference/react/Suspense
|
||||
|
||||
- backend error message unification
|
||||
- drf-messages
|
||||
https://drf-standardized-errors.readthedocs.io/en/latest/error_response.html
|
||||
|
||||
|
|
7
rsconcept/frontend/package-lock.json
generated
7
rsconcept/frontend/package-lock.json
generated
|
@ -19,7 +19,6 @@
|
|||
"axios": "^1.7.9",
|
||||
"clsx": "^2.1.1",
|
||||
"global": "^4.4.0",
|
||||
"html-to-image": "^1.11.13",
|
||||
"js-file-download": "^0.4.12",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.0.0",
|
||||
|
@ -6719,12 +6718,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-to-image": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz",
|
||||
"integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
"axios": "^1.7.9",
|
||||
"clsx": "^2.1.1",
|
||||
"global": "^4.4.0",
|
||||
"html-to-image": "^1.11.13",
|
||||
"js-file-download": "^0.4.12",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.0.0",
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
IconExecute,
|
||||
IconFitImage,
|
||||
IconGrid,
|
||||
IconImage,
|
||||
IconLineStraight,
|
||||
IconLineWave,
|
||||
IconNewItem,
|
||||
|
@ -82,9 +81,6 @@ export function HelpOssGraph() {
|
|||
<li>
|
||||
<IconSave className='inline-icon' /> Сохранить положения
|
||||
</li>
|
||||
<li>
|
||||
<IconImage className='inline-icon' /> Сохранить в SVG
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
IconGraphInputs,
|
||||
IconGraphMaximize,
|
||||
IconGraphOutputs,
|
||||
IconImage,
|
||||
IconNewItem,
|
||||
IconOSS,
|
||||
IconPredecessor,
|
||||
|
@ -85,9 +84,6 @@ export function HelpRSGraphTerm() {
|
|||
<IconTypeGraph className='inline-icon' /> Открыть{' '}
|
||||
<LinkTopic text='граф ступеней' topic={HelpTopic.UI_TYPE_GRAPH} />
|
||||
</li>
|
||||
<li>
|
||||
<IconImage className='inline-icon' /> Сохранить в формат PNG
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||
|
|
|
@ -4,8 +4,6 @@ import { useEffect, useState } from 'react';
|
|||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
Background,
|
||||
getNodesBounds,
|
||||
getViewportForBounds,
|
||||
type Node,
|
||||
ReactFlow,
|
||||
useEdgesState,
|
||||
|
@ -13,7 +11,6 @@ import {
|
|||
useOnSelectionChange,
|
||||
useReactFlow
|
||||
} from 'reactflow';
|
||||
import { toPng } from 'html-to-image';
|
||||
|
||||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useLibrary } from '@/features/library';
|
||||
|
@ -21,7 +18,6 @@ import { useLibrary } from '@/features/library';
|
|||
import { Overlay } from '@/components/Container';
|
||||
import { useMainHeight } from '@/stores/appLayout';
|
||||
import { useTooltipsStore } from '@/stores/tooltips';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
||||
|
@ -203,39 +199,6 @@ export function OssFlow() {
|
|||
promptRelocateConstituents(target, getPositions());
|
||||
}
|
||||
|
||||
function handleSaveImage() {
|
||||
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
||||
if (canvas === null) {
|
||||
toast.error(errorMsg.imageFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
const imageWidth = PARAMETER.ossImageWidth;
|
||||
const imageHeight = PARAMETER.ossImageHeight;
|
||||
const nodesBounds = getNodesBounds(nodes);
|
||||
const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, ZOOM_MIN, ZOOM_MAX);
|
||||
toPng(canvas, {
|
||||
backgroundColor: APP_COLORS.bgDefault,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
style: {
|
||||
width: String(imageWidth),
|
||||
height: String(imageHeight),
|
||||
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom})`
|
||||
}
|
||||
})
|
||||
.then(dataURL => {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('download', `${schema.alias}.png`);
|
||||
a.setAttribute('href', dataURL);
|
||||
a.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
toast.error(errorMsg.imageFailed);
|
||||
});
|
||||
}
|
||||
|
||||
function handleContextMenu(event: React.MouseEvent<Element>, node: OssNode) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
@ -306,7 +269,6 @@ export function OssFlow() {
|
|||
onExecute={handleExecuteSelected}
|
||||
onResetPositions={() => setToggleReset(prev => !prev)}
|
||||
onSavePositions={handleSavePositions}
|
||||
onSaveImage={handleSaveImage}
|
||||
/>
|
||||
</Overlay>
|
||||
{menuProps ? (
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
IconExecute,
|
||||
IconFitImage,
|
||||
IconGrid,
|
||||
IconImage,
|
||||
IconLineStraight,
|
||||
IconLineWave,
|
||||
IconNewItem,
|
||||
|
@ -34,7 +33,6 @@ interface ToolbarOssGraphProps {
|
|||
onEdit: () => void;
|
||||
onExecute: () => void;
|
||||
onFitView: () => void;
|
||||
onSaveImage: () => void;
|
||||
onSavePositions: () => void;
|
||||
onResetPositions: () => void;
|
||||
}
|
||||
|
@ -45,7 +43,6 @@ export function ToolbarOssGraph({
|
|||
onEdit,
|
||||
onExecute,
|
||||
onFitView,
|
||||
onSaveImage,
|
||||
onSavePositions,
|
||||
onResetPositions
|
||||
}: ToolbarOssGraphProps) {
|
||||
|
@ -127,11 +124,6 @@ export function ToolbarOssGraph({
|
|||
}
|
||||
onClick={toggleEdgeAnimate}
|
||||
/>
|
||||
<MiniButton
|
||||
icon={<IconImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сохранить изображение'
|
||||
onClick={onSaveImage}
|
||||
/>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_OSS_GRAPH}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import { getNodesBounds, getViewportForBounds, useReactFlow } from 'reactflow';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import clsx from 'clsx';
|
||||
import { toPng } from 'html-to-image';
|
||||
|
||||
import { BadgeHelp, HelpTopic } from '@/features/help';
|
||||
import { MiniSelectorOSS } from '@/features/library';
|
||||
|
@ -15,26 +13,21 @@ import {
|
|||
IconDestroy,
|
||||
IconFilter,
|
||||
IconFitImage,
|
||||
IconImage,
|
||||
IconNewItem,
|
||||
IconText,
|
||||
IconTextOff,
|
||||
IconTypeGraph
|
||||
} from '@/components/Icons';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
||||
import { useMutatingRSForm } from '../../../backend/useMutatingRSForm';
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
|
||||
import { VIEW_PADDING, ZOOM_MAX, ZOOM_MIN } from './TGFlow';
|
||||
import { VIEW_PADDING } from './TGFlow';
|
||||
|
||||
export function ToolbarTermGraph() {
|
||||
const isProcessing = useMutatingRSForm();
|
||||
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||
const {
|
||||
schema, //
|
||||
selected,
|
||||
|
@ -49,7 +42,7 @@ export function ToolbarTermGraph() {
|
|||
const filter = useTermGraphStore(state => state.filter);
|
||||
const setFilter = useTermGraphStore(state => state.setFilter);
|
||||
|
||||
const { fitView, getNodes } = useReactFlow();
|
||||
const { fitView } = useReactFlow();
|
||||
|
||||
function handleShowTypeGraph() {
|
||||
const typeInfo = schema.items.map(item => ({
|
||||
|
@ -79,39 +72,6 @@ export function ToolbarTermGraph() {
|
|||
});
|
||||
}
|
||||
|
||||
function handleSaveImage() {
|
||||
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
||||
if (canvas === null) {
|
||||
toast.error(errorMsg.imageFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
const imageWidth = PARAMETER.ossImageWidth;
|
||||
const imageHeight = PARAMETER.ossImageHeight;
|
||||
const nodesBounds = getNodesBounds(getNodes());
|
||||
const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, ZOOM_MIN, ZOOM_MAX);
|
||||
toPng(canvas, {
|
||||
backgroundColor: darkMode ? APP_COLORS.bgDefaultDark : APP_COLORS.bgDefaultLight,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
style: {
|
||||
width: String(imageWidth),
|
||||
height: String(imageHeight),
|
||||
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom * 2})`
|
||||
}
|
||||
})
|
||||
.then(dataURL => {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('download', `${schema.alias}.png`);
|
||||
a.setAttribute('href', dataURL);
|
||||
a.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
toast.error(errorMsg.imageFailed);
|
||||
});
|
||||
}
|
||||
|
||||
function handleFitView() {
|
||||
setTimeout(() => {
|
||||
fitView({ duration: PARAMETER.zoomDuration, padding: VIEW_PADDING });
|
||||
|
@ -186,11 +146,6 @@ export function ToolbarTermGraph() {
|
|||
title='Граф ступеней'
|
||||
onClick={handleShowTypeGraph}
|
||||
/>
|
||||
<MiniButton
|
||||
icon={<IconImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сохранить изображение'
|
||||
onClick={handleSaveImage}
|
||||
/>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_GRAPH_TERM}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
/** Semantic colors for application. */
|
||||
// prettier-ignore
|
||||
export const APP_COLORS = {
|
||||
bgDefaultLight: '#fafafa',
|
||||
bgDefaultDark: '#171717',
|
||||
bgDefault: 'var(--clr-prim-100)',
|
||||
bgInput: 'var(--clr-prim-0)',
|
||||
bgControls: 'var(--clr-prim-200)',
|
||||
|
|
Loading…
Reference in New Issue
Block a user