mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-13 20:30:36 +03:00
F: Improve Graph selection and View retention
This commit is contained in:
parent
935cd42306
commit
54073dbbed
|
@ -79,7 +79,7 @@ export const ossApi = {
|
|||
successMessage: infoMsg.changesSaved
|
||||
}
|
||||
}),
|
||||
deleteBlock: ({ itemID, data }: { itemID: number; data: IDeleteBlockDTO }) =>
|
||||
deleteBlock: ({ itemID, data }: { itemID: number; data: IDeleteBlockDTO; beforeUpdate?: () => void }) =>
|
||||
axiosPatch<IDeleteBlockDTO, IOperationSchemaDTO>({
|
||||
schema: schemaOperationSchema,
|
||||
endpoint: `/api/oss/${itemID}/delete-block`,
|
||||
|
@ -101,7 +101,7 @@ export const ossApi = {
|
|||
}
|
||||
}
|
||||
}),
|
||||
deleteReference: ({ itemID, data }: { itemID: number; data: IDeleteReferenceDTO }) =>
|
||||
deleteReference: ({ itemID, data }: { itemID: number; data: IDeleteReferenceDTO; beforeUpdate?: () => void }) =>
|
||||
axiosPatch<IDeleteReferenceDTO, IOperationSchemaDTO>({
|
||||
schema: schemaOperationSchema,
|
||||
endpoint: `/api/oss/${itemID}/delete-reference`,
|
||||
|
@ -168,7 +168,7 @@ export const ossApi = {
|
|||
successMessage: infoMsg.changesSaved
|
||||
}
|
||||
}),
|
||||
deleteOperation: ({ itemID, data }: { itemID: number; data: IDeleteOperationDTO }) =>
|
||||
deleteOperation: ({ itemID, data }: { itemID: number; data: IDeleteOperationDTO; beforeUpdate?: () => void }) =>
|
||||
axiosPatch<IDeleteOperationDTO, IOperationSchemaDTO>({
|
||||
schema: schemaOperationSchema,
|
||||
endpoint: `/api/oss/${itemID}/delete-operation`,
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
|
||||
|
||||
import { KEYS } from '@/backend/configuration';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { ossApi } from './api';
|
||||
import { type IDeleteBlockDTO } from './types';
|
||||
|
@ -13,7 +14,11 @@ export const useDeleteBlock = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-block'],
|
||||
mutationFn: ossApi.deleteBlock,
|
||||
onSuccess: async data => {
|
||||
onSuccess: async (data, variables) => {
|
||||
if (variables.beforeUpdate) {
|
||||
variables.beforeUpdate();
|
||||
await new Promise(resolve => setTimeout(resolve, PARAMETER.minimalTimeout));
|
||||
}
|
||||
updateTimestamp(data.id, data.time_update);
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
await Promise.allSettled([
|
||||
|
@ -24,6 +29,8 @@ export const useDeleteBlock = () => {
|
|||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
deleteBlock: (data: { itemID: number; data: IDeleteBlockDTO }) => mutation.mutateAsync(data)
|
||||
deleteBlock: (data: { itemID: number; data: IDeleteBlockDTO; beforeUpdate?: () => void }) => {
|
||||
mutation.mutate(data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
|
||||
|
||||
import { KEYS } from '@/backend/configuration';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { ossApi } from './api';
|
||||
import { type IDeleteOperationDTO } from './types';
|
||||
|
@ -13,7 +14,11 @@ export const useDeleteOperation = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-operation'],
|
||||
mutationFn: ossApi.deleteOperation,
|
||||
onSuccess: async data => {
|
||||
onSuccess: async (data, variables) => {
|
||||
if (variables.beforeUpdate) {
|
||||
variables.beforeUpdate();
|
||||
await new Promise(resolve => setTimeout(resolve, PARAMETER.minimalTimeout));
|
||||
}
|
||||
updateTimestamp(data.id, data.time_update);
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
await Promise.allSettled([
|
||||
|
@ -24,6 +29,8 @@ export const useDeleteOperation = () => {
|
|||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
deleteOperation: (data: { itemID: number; data: IDeleteOperationDTO }) => mutation.mutateAsync(data)
|
||||
deleteOperation: (data: { itemID: number; data: IDeleteOperationDTO; beforeUpdate?: () => void }) => {
|
||||
mutation.mutate(data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
|
||||
|
||||
import { KEYS } from '@/backend/configuration';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { ossApi } from './api';
|
||||
import { type IDeleteReferenceDTO } from './types';
|
||||
|
@ -13,7 +14,11 @@ export const useDeleteReference = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-reference'],
|
||||
mutationFn: ossApi.deleteReference,
|
||||
onSuccess: async data => {
|
||||
onSuccess: async (data, variables) => {
|
||||
if (variables.beforeUpdate) {
|
||||
variables.beforeUpdate();
|
||||
await new Promise(resolve => setTimeout(resolve, PARAMETER.minimalTimeout));
|
||||
}
|
||||
updateTimestamp(data.id, data.time_update);
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
await Promise.allSettled([
|
||||
|
@ -24,6 +29,8 @@ export const useDeleteReference = () => {
|
|||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
deleteReference: (data: { itemID: number; data: IDeleteReferenceDTO }) => mutation.mutateAsync(data)
|
||||
deleteReference: (data: { itemID: number; data: IDeleteReferenceDTO; beforeUpdate?: () => void }) => {
|
||||
mutation.mutate(data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,10 +17,11 @@ export interface DlgDeleteOperationProps {
|
|||
oss: IOperationSchema;
|
||||
target: IOperationInput | IOperationSynthesis;
|
||||
layout: IOssLayout;
|
||||
beforeDelete?: () => void;
|
||||
}
|
||||
|
||||
export function DlgDeleteOperation() {
|
||||
const { oss, target, layout } = useDialogsStore(state => state.props as DlgDeleteOperationProps);
|
||||
const { oss, target, layout, beforeDelete } = useDialogsStore(state => state.props as DlgDeleteOperationProps);
|
||||
const { deleteOperation } = useDeleteOperation();
|
||||
|
||||
const { handleSubmit, control } = useForm<IDeleteOperationDTO>({
|
||||
|
@ -34,7 +35,7 @@ export function DlgDeleteOperation() {
|
|||
});
|
||||
|
||||
function onSubmit(data: IDeleteOperationDTO) {
|
||||
return deleteOperation({ itemID: oss.id, data: data });
|
||||
return deleteOperation({ itemID: oss.id, data: data, beforeUpdate: beforeDelete });
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -17,10 +17,11 @@ export interface DlgDeleteReferenceProps {
|
|||
oss: IOperationSchema;
|
||||
target: IOperationReference;
|
||||
layout: IOssLayout;
|
||||
beforeDelete?: () => void;
|
||||
}
|
||||
|
||||
export function DlgDeleteReference() {
|
||||
const { oss, target, layout } = useDialogsStore(state => state.props as DlgDeleteReferenceProps);
|
||||
const { oss, target, layout, beforeDelete } = useDialogsStore(state => state.props as DlgDeleteReferenceProps);
|
||||
const { deleteReference } = useDeleteReference();
|
||||
|
||||
const { handleSubmit, control } = useForm<IDeleteReferenceDTO>({
|
||||
|
@ -35,7 +36,7 @@ export function DlgDeleteReference() {
|
|||
const keep_connections = useWatch({ control, name: 'keep_connections' });
|
||||
|
||||
function onSubmit(data: IDeleteReferenceDTO) {
|
||||
return deleteReference({ itemID: oss.id, data: data });
|
||||
return deleteReference({ itemID: oss.id, data: data, beforeUpdate: beforeDelete });
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -38,7 +38,7 @@ interface MenuOperationProps {
|
|||
|
||||
export function MenuOperation({ operation, onHide }: MenuOperationProps) {
|
||||
const { items: libraryItems } = useLibrary();
|
||||
const { schema, setSelected, navigateOperationSchema, isMutable, canDeleteOperation } = useOssEdit();
|
||||
const { schema, setSelected, navigateOperationSchema, isMutable, canDeleteOperation, deselectAll } = useOssEdit();
|
||||
const isProcessing = useMutatingOss();
|
||||
const getLayout = useGetLayout();
|
||||
|
||||
|
@ -115,7 +115,8 @@ export function MenuOperation({ operation, onHide }: MenuOperationProps) {
|
|||
showDeleteReference({
|
||||
oss: schema,
|
||||
target: operation,
|
||||
layout: getLayout()
|
||||
layout: getLayout(),
|
||||
beforeDelete: deselectAll
|
||||
});
|
||||
break;
|
||||
case OperationType.INPUT:
|
||||
|
@ -123,7 +124,8 @@ export function MenuOperation({ operation, onHide }: MenuOperationProps) {
|
|||
showDeleteOperation({
|
||||
oss: schema,
|
||||
target: operation,
|
||||
layout: getLayout()
|
||||
layout: getLayout(),
|
||||
beforeDelete: deselectAll
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
const [containMovement, setContainMovement] = useState(false);
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
|
||||
const prevSelected = useRef<string[]>([]);
|
||||
|
||||
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
||||
const ids = nodes.map(node => node.id);
|
||||
|
@ -39,19 +40,21 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
onChange: onSelectionChange
|
||||
});
|
||||
|
||||
const resetGraph = useCallback(() => {
|
||||
const reloadData = useCallback(() => {
|
||||
const newNodes: Node[] = schema.hierarchy.topologicalOrder().map(nodeID => {
|
||||
const item = schema.itemByNodeID.get(nodeID)!;
|
||||
if (item.nodeType === NodeType.BLOCK) {
|
||||
return {
|
||||
id: nodeID,
|
||||
type: 'block',
|
||||
selected: prevSelected.current.includes(item.nodeID),
|
||||
data: { label: item.title, block: item },
|
||||
position: computeRelativePosition(schema, { x: item.x, y: item.y }, item.parent),
|
||||
style: {
|
||||
width: item.width,
|
||||
height: item.height
|
||||
},
|
||||
|
||||
parentId: item.parent ? constructNodeID(NodeType.BLOCK, item.parent) : undefined,
|
||||
zIndex: Z_BLOCK
|
||||
};
|
||||
|
@ -59,6 +62,7 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
return {
|
||||
id: item.nodeID,
|
||||
type: item.operation_type.toString(),
|
||||
selected: prevSelected.current.includes(item.nodeID),
|
||||
data: { label: item.alias, operation: item },
|
||||
position: computeRelativePosition(schema, { x: item.x, y: item.y }, item.parent),
|
||||
parentId: item.parent ? constructNodeID(NodeType.BLOCK, item.parent) : undefined,
|
||||
|
@ -83,19 +87,23 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
|
||||
setNodes(newNodes);
|
||||
setEdges(newEdges);
|
||||
|
||||
setTimeout(() => fitView(flowOptions.fitViewOptions), PARAMETER.refreshTimeout);
|
||||
}, [schema, setNodes, setEdges, edgeAnimate, edgeStraight, fitView]);
|
||||
}, [schema, setNodes, setEdges, edgeAnimate, edgeStraight]);
|
||||
|
||||
useEffect(() => {
|
||||
resetGraph();
|
||||
}, [schema, edgeAnimate, edgeStraight, resetGraph]);
|
||||
reloadData();
|
||||
}, [schema, edgeAnimate, edgeStraight, reloadData]);
|
||||
|
||||
function resetView() {
|
||||
setTimeout(() => fitView(flowOptions.fitViewOptions), PARAMETER.refreshTimeout);
|
||||
}
|
||||
|
||||
const prevSelected = useRef<string[]>([]);
|
||||
function resetGraph() {
|
||||
setSelected([]);
|
||||
prevSelected.current = [];
|
||||
reloadData();
|
||||
setTimeout(() => fitView(flowOptions.fitViewOptions), PARAMETER.refreshTimeout);
|
||||
}
|
||||
|
||||
if (
|
||||
viewportInitialized &&
|
||||
(prevSelected.current.length !== selected.length || prevSelected.current.some((id, i) => id !== selected[i]))
|
||||
|
|
|
@ -46,8 +46,16 @@ export const flowOptions = {
|
|||
|
||||
export function OssFlow() {
|
||||
const mainHeight = useMainHeight();
|
||||
const { navigateOperationSchema, schema, selected, setSelected, selectedItems, isMutable, canDeleteOperation } =
|
||||
useOssEdit();
|
||||
const {
|
||||
navigateOperationSchema,
|
||||
schema,
|
||||
selected,
|
||||
setSelected,
|
||||
selectedItems,
|
||||
isMutable,
|
||||
deselectAll,
|
||||
canDeleteOperation
|
||||
} = useOssEdit();
|
||||
const { screenToFlowPosition } = useReactFlow();
|
||||
const { containMovement, nodes, onNodesChange, edges, onEdgesChange, resetGraph, resetView } = useOssFlow();
|
||||
const store = useStoreApi();
|
||||
|
@ -157,7 +165,8 @@ export function OssFlow() {
|
|||
showDeleteReference({
|
||||
oss: schema,
|
||||
target: item,
|
||||
layout: getLayout()
|
||||
layout: getLayout(),
|
||||
beforeDelete: deselectAll
|
||||
});
|
||||
break;
|
||||
case OperationType.INPUT:
|
||||
|
@ -165,14 +174,19 @@ export function OssFlow() {
|
|||
showDeleteOperation({
|
||||
oss: schema,
|
||||
target: item,
|
||||
layout: getLayout()
|
||||
layout: getLayout(),
|
||||
beforeDelete: deselectAll
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (!window.confirm(promptText.deleteBlock)) {
|
||||
return;
|
||||
}
|
||||
void deleteBlock({ itemID: schema.id, data: { target: item.id, layout: getLayout() } });
|
||||
void deleteBlock({
|
||||
itemID: schema.id,
|
||||
data: { target: item.id, layout: getLayout() },
|
||||
beforeUpdate: deselectAll
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user