mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
M: Improve node positioning in OSS
This commit is contained in:
parent
64861ac9ad
commit
d5531d00eb
|
@ -197,6 +197,7 @@ class OperationSchema:
|
||||||
parent = parents.get(cst.pk)
|
parent = parents.get(cst.pk)
|
||||||
assert parent is not None
|
assert parent is not None
|
||||||
Inheritance.objects.create(
|
Inheritance.objects.create(
|
||||||
|
operation=operation,
|
||||||
child=cst,
|
child=cst,
|
||||||
parent=parent
|
parent=parent
|
||||||
)
|
)
|
||||||
|
|
|
@ -140,13 +140,11 @@ function PickSubstitutions({
|
||||||
() => [
|
() => [
|
||||||
columnHelper.accessor(item => item.substitution_source?.alias ?? 'N/A', {
|
columnHelper.accessor(item => item.substitution_source?.alias ?? 'N/A', {
|
||||||
id: 'left_schema',
|
id: 'left_schema',
|
||||||
header: 'Операция',
|
|
||||||
size: 100,
|
size: 100,
|
||||||
cell: props => <div className='min-w-[10.5rem] text-ellipsis text-right'>{props.getValue()}</div>
|
cell: props => <div className='min-w-[10.5rem] text-ellipsis text-left'>{props.getValue()}</div>
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(item => item.substitution?.alias ?? 'N/A', {
|
columnHelper.accessor(item => item.substitution?.alias ?? 'N/A', {
|
||||||
id: 'left_alias',
|
id: 'left_alias',
|
||||||
header: () => <span className='pl-3'>Имя</span>,
|
|
||||||
size: 65,
|
size: 65,
|
||||||
cell: props =>
|
cell: props =>
|
||||||
props.row.original.substitution ? (
|
props.row.original.substitution ? (
|
||||||
|
@ -157,13 +155,11 @@ function PickSubstitutions({
|
||||||
}),
|
}),
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: 'status',
|
id: 'status',
|
||||||
header: '',
|
|
||||||
size: 40,
|
size: 40,
|
||||||
cell: () => <IconPageRight size='1.2rem' />
|
cell: () => <IconPageRight size='1.2rem' />
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(item => item.original?.alias ?? 'N/A', {
|
columnHelper.accessor(item => item.original?.alias ?? 'N/A', {
|
||||||
id: 'right_alias',
|
id: 'right_alias',
|
||||||
header: () => <span className='pl-3'>Имя</span>,
|
|
||||||
size: 65,
|
size: 65,
|
||||||
cell: props =>
|
cell: props =>
|
||||||
props.row.original.original ? (
|
props.row.original.original ? (
|
||||||
|
@ -174,9 +170,8 @@ function PickSubstitutions({
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(item => item.original_source?.alias ?? 'N/A', {
|
columnHelper.accessor(item => item.original_source?.alias ?? 'N/A', {
|
||||||
id: 'right_schema',
|
id: 'right_schema',
|
||||||
header: 'Операция',
|
|
||||||
size: 100,
|
size: 100,
|
||||||
cell: props => <div className='min-w-[8rem] text-ellipsis'>{props.getValue()}</div>
|
cell: props => <div className='min-w-[8rem] text-ellipsis text-right'>{props.getValue()}</div>
|
||||||
}),
|
}),
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
|
|
|
@ -118,7 +118,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
),
|
),
|
||||||
[alias, comment, title, attachedID, oss, createSchema]
|
[alias, comment, title, attachedID, oss, createSchema, setAlias]
|
||||||
);
|
);
|
||||||
|
|
||||||
const synthesisPanel = useMemo(
|
const synthesisPanel = useMemo(
|
||||||
|
@ -137,7 +137,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
),
|
),
|
||||||
[oss, alias, comment, title, inputs]
|
[oss, alias, comment, title, inputs, setAlias]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -92,7 +92,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
),
|
),
|
||||||
[alias, comment, title]
|
[alias, comment, title, setAlias]
|
||||||
);
|
);
|
||||||
|
|
||||||
const argumentsPanel = useMemo(
|
const argumentsPanel = useMemo(
|
||||||
|
@ -106,7 +106,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
),
|
),
|
||||||
[oss, target, inputs]
|
[oss, target, inputs, setInputs]
|
||||||
);
|
);
|
||||||
|
|
||||||
const synthesisPanel = useMemo(
|
const synthesisPanel = useMemo(
|
||||||
|
|
|
@ -46,7 +46,7 @@ function FormCreateItem() {
|
||||||
|
|
||||||
const location = useMemo(() => combineLocation(head, body), [head, body]);
|
const location = useMemo(() => combineLocation(head, body), [head, body]);
|
||||||
const isValid = useMemo(() => validateLocation(location), [location]);
|
const isValid = useMemo(() => validateLocation(location), [location]);
|
||||||
const [initLocation] = useLocalStorage<string>(storage.librarySearchLocation, '');
|
const [initLocation, setInitLocation] = useLocalStorage<string>(storage.librarySearchLocation, '');
|
||||||
|
|
||||||
const [fileName, setFileName] = useState('');
|
const [fileName, setFileName] = useState('');
|
||||||
const [file, setFile] = useState<File | undefined>();
|
const [file, setFile] = useState<File | undefined>();
|
||||||
|
@ -81,6 +81,7 @@ function FormCreateItem() {
|
||||||
file: file,
|
file: file,
|
||||||
fileName: file?.name
|
fileName: file?.name
|
||||||
};
|
};
|
||||||
|
setInitLocation(location);
|
||||||
createItem(data, newItem => {
|
createItem(data, newItem => {
|
||||||
toast.success(information.newLibraryItem);
|
toast.success(information.newLibraryItem);
|
||||||
if (itemType == LibraryItemType.RSFORM) {
|
if (itemType == LibraryItemType.RSFORM) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ function InputNode(node: OssNodeInternal) {
|
||||||
disabled={!hasFile}
|
disabled={!hasFile}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center text-sm'>
|
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center'>
|
||||||
{node.data.label}
|
{node.data.label}
|
||||||
{controller.showTooltip && !node.dragging ? (
|
{controller.showTooltip && !node.dragging ? (
|
||||||
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
||||||
|
|
|
@ -33,7 +33,7 @@ function OperationNode(node: OssNodeInternal) {
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center text-sm'>
|
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center'>
|
||||||
{node.data.label}
|
{node.data.label}
|
||||||
{controller.showTooltip && !node.dragging ? (
|
{controller.showTooltip && !node.dragging ? (
|
||||||
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { OssNode } from '@/models/miscellaneous';
|
import { OssNode } from '@/models/miscellaneous';
|
||||||
import { OperationID } from '@/models/oss';
|
import { OperationID, OperationType } from '@/models/oss';
|
||||||
import { PARAMETER, storage } from '@/utils/constants';
|
import { PARAMETER, storage } from '@/utils/constants';
|
||||||
import { errors } from '@/utils/labels';
|
import { errors } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -127,19 +127,32 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
if (!controller.schema) {
|
if (!controller.schema) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = { x: 0, y: 0 };
|
let target = { x: 0, y: 0 };
|
||||||
const positions = getPositions();
|
const positions = getPositions();
|
||||||
|
if (positions.length == 0) {
|
||||||
if (inputs.length <= 1) {
|
|
||||||
target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||||
|
}
|
||||||
|
if (inputs.length <= 1) {
|
||||||
|
let inputsNodes = positions.filter(pos =>
|
||||||
|
controller.schema!.items.find(
|
||||||
|
operation => operation.operation_type === OperationType.INPUT && operation.id === pos.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (inputsNodes.length > 0) {
|
||||||
|
inputsNodes = positions;
|
||||||
|
}
|
||||||
|
const maxX = Math.max(...inputsNodes.map(node => node.position_x));
|
||||||
|
const minY = Math.min(...inputsNodes.map(node => node.position_y));
|
||||||
|
target.x = maxX + 180;
|
||||||
|
target.y = minY;
|
||||||
} else {
|
} else {
|
||||||
const inputsNodes = positions.filter(pos => inputs.includes(pos.id));
|
const inputsNodes = positions.filter(pos => inputs.includes(pos.id));
|
||||||
const maxY = Math.max(...inputsNodes.map(node => node.position_y));
|
const maxY = Math.max(...inputsNodes.map(node => node.position_y));
|
||||||
const minX = Math.min(...inputsNodes.map(node => node.position_x));
|
const minX = Math.min(...inputsNodes.map(node => node.position_x));
|
||||||
const maxX = Math.max(...inputsNodes.map(node => node.position_x));
|
const maxX = Math.max(...inputsNodes.map(node => node.position_x));
|
||||||
|
|
||||||
target.y = maxY + 100;
|
|
||||||
target.x = Math.ceil((maxX + minX) / 2 / PARAMETER.ossGridSize) * PARAMETER.ossGridSize;
|
target.x = Math.ceil((maxX + minX) / 2 / PARAMETER.ossGridSize) * PARAMETER.ossGridSize;
|
||||||
|
target.y = maxY + 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
let flagIntersect = false;
|
let flagIntersect = false;
|
||||||
|
@ -154,8 +167,13 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
target.y += PARAMETER.ossMinDistance;
|
target.y += PARAMETER.ossMinDistance;
|
||||||
}
|
}
|
||||||
} while (flagIntersect);
|
} while (flagIntersect);
|
||||||
|
controller.promptCreateOperation({
|
||||||
controller.promptCreateOperation(target.x, target.y, inputs, positions);
|
x: target.x,
|
||||||
|
y: target.y,
|
||||||
|
inputs: inputs,
|
||||||
|
positions: positions,
|
||||||
|
callback: () => flow.fitView({ duration: PARAMETER.zoomDuration })
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[controller, getPositions, flow]
|
[controller, getPositions, flow]
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,8 +26,17 @@ import {
|
||||||
OperationID
|
OperationID
|
||||||
} from '@/models/oss';
|
} from '@/models/oss';
|
||||||
import { UserID, UserLevel } from '@/models/user';
|
import { UserID, UserLevel } from '@/models/user';
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
|
export interface ICreateOperationPrompt {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
inputs: OperationID[];
|
||||||
|
positions: IOperationPosition[];
|
||||||
|
callback: (newID: OperationID) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IOssEditContext {
|
export interface IOssEditContext {
|
||||||
schema?: IOperationSchema;
|
schema?: IOperationSchema;
|
||||||
selected: OperationID[];
|
selected: OperationID[];
|
||||||
|
@ -51,7 +60,7 @@ export interface IOssEditContext {
|
||||||
openOperationSchema: (target: OperationID) => void;
|
openOperationSchema: (target: OperationID) => void;
|
||||||
|
|
||||||
savePositions: (positions: IOperationPosition[], callback?: () => void) => void;
|
savePositions: (positions: IOperationPosition[], callback?: () => void) => void;
|
||||||
promptCreateOperation: (x: number, y: number, inputs: OperationID[], positions: IOperationPosition[]) => void;
|
promptCreateOperation: (props: ICreateOperationPrompt) => void;
|
||||||
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
|
@ -97,6 +106,8 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
||||||
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
||||||
const [initialInputs, setInitialInputs] = useState<OperationID[]>([]);
|
const [initialInputs, setInitialInputs] = useState<OperationID[]>([]);
|
||||||
|
const [createCallback, setCreateCallback] = useState<((newID: OperationID) => void) | undefined>(undefined);
|
||||||
|
|
||||||
const [positions, setPositions] = useState<IOperationPosition[]>([]);
|
const [positions, setPositions] = useState<IOperationPosition[]>([]);
|
||||||
const [targetOperationID, setTargetOperationID] = useState<OperationID | undefined>(undefined);
|
const [targetOperationID, setTargetOperationID] = useState<OperationID | undefined>(undefined);
|
||||||
const targetOperation = useMemo(
|
const targetOperation = useMemo(
|
||||||
|
@ -209,24 +220,27 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
[model]
|
[model]
|
||||||
);
|
);
|
||||||
|
|
||||||
const promptCreateOperation = useCallback(
|
const promptCreateOperation = useCallback(({ x, y, inputs, positions, callback }: ICreateOperationPrompt) => {
|
||||||
(x: number, y: number, inputs: OperationID[], positions: IOperationPosition[]) => {
|
setInsertPosition({ x: x, y: y });
|
||||||
setInsertPosition({ x: x, y: y });
|
setInitialInputs(inputs);
|
||||||
setInitialInputs(inputs);
|
setPositions(positions);
|
||||||
setPositions(positions);
|
setCreateCallback(() => callback);
|
||||||
setShowCreateOperation(true);
|
setShowCreateOperation(true);
|
||||||
},
|
}, []);
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCreateOperation = useCallback(
|
const handleCreateOperation = useCallback(
|
||||||
(data: IOperationCreateData) => {
|
(data: IOperationCreateData) => {
|
||||||
data.positions = positions;
|
data.positions = positions;
|
||||||
data.item_data.position_x = insertPosition.x;
|
data.item_data.position_x = insertPosition.x;
|
||||||
data.item_data.position_y = insertPosition.y;
|
data.item_data.position_y = insertPosition.y;
|
||||||
model.createOperation(data, operation => toast.success(information.newOperation(operation.alias)));
|
model.createOperation(data, operation => {
|
||||||
|
toast.success(information.newOperation(operation.alias));
|
||||||
|
if (createCallback) {
|
||||||
|
setTimeout(() => createCallback(operation.id), PARAMETER.refreshTimeout);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[model, positions, insertPosition]
|
[model, positions, insertPosition, createCallback]
|
||||||
);
|
);
|
||||||
|
|
||||||
const promptEditOperation = useCallback((target: OperationID, positions: IOperationPosition[]) => {
|
const promptEditOperation = useCallback((target: OperationID, positions: IOperationPosition[]) => {
|
||||||
|
|
|
@ -145,12 +145,12 @@ function EditorRSExpression({
|
||||||
const controls = useMemo(
|
const controls = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<RSEditorControls
|
<RSEditorControls
|
||||||
isOpen={showControls && (!disabled || model.processing)}
|
isOpen={showControls && (!disabled || (model.processing && !activeCst?.is_inherited))}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[showControls, disabled, model.processing, handleEdit]
|
[showControls, disabled, model.processing, handleEdit, activeCst]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -66,6 +66,8 @@
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: var(--cl-bg-120);
|
background-color: var(--cl-bg-120);
|
||||||
|
|
|
@ -17,6 +17,8 @@ export const PARAMETER = {
|
||||||
ossContextMenuWidth: 200, // pixels - width of OSS context menu
|
ossContextMenuWidth: 200, // pixels - width of OSS context menu
|
||||||
ossGridSize: 10, // pixels - size of OSS grid
|
ossGridSize: 10, // pixels - size of OSS grid
|
||||||
ossMinDistance: 20, // pixels - minimum distance between node centers
|
ossMinDistance: 20, // pixels - minimum distance between node centers
|
||||||
|
ossDistanceX: 180, // pixels - insert x-distance between node centers
|
||||||
|
ossDistanceY: 100, // pixels - insert y-distance between node centers
|
||||||
|
|
||||||
graphHoverXLimit: 0.4, // ratio to clientWidth used to determine which side of screen popup should be
|
graphHoverXLimit: 0.4, // ratio to clientWidth used to determine which side of screen popup should be
|
||||||
graphHoverYLimit: 0.6, // ratio to clientHeight used to determine which side of screen popup should be
|
graphHoverYLimit: 0.6, // ratio to clientHeight used to determine which side of screen popup should be
|
||||||
|
|
Loading…
Reference in New Issue
Block a user