mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
R: Refactor layout data structure
This commit is contained in:
parent
e101aea631
commit
c92641f671
|
@ -68,8 +68,6 @@ class TestLibraryViewset(EndpointTester):
|
|||
self.assertEqual(response.data['access_policy'], data['access_policy'])
|
||||
self.assertEqual(response.data['visible'], data['visible'])
|
||||
self.assertEqual(response.data['read_only'], data['read_only'])
|
||||
self.assertEqual(oss.layout().data['operations'], [])
|
||||
self.assertEqual(oss.layout().data['blocks'], [])
|
||||
|
||||
self.logout()
|
||||
data = {'title': 'Title2'}
|
||||
|
|
|
@ -41,7 +41,7 @@ class LibraryViewSet(viewsets.ModelViewSet):
|
|||
else:
|
||||
serializer.save()
|
||||
if serializer.data.get('item_type') == m.LibraryItemType.OPERATION_SCHEMA:
|
||||
Layout.objects.create(oss=serializer.instance, data={'operations': [], 'blocks': []})
|
||||
Layout.objects.create(oss=serializer.instance, data=[])
|
||||
|
||||
def perform_update(self, serializer) -> None:
|
||||
instance = serializer.save()
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_layout(apps, schema_editor):
|
||||
Layout = apps.get_model('oss', 'Layout')
|
||||
|
||||
for layout in Layout.objects.all():
|
||||
previous_data = layout.data
|
||||
new_layout = []
|
||||
|
||||
for operation in previous_data['operations']:
|
||||
new_layout.append({
|
||||
'nodeID': 'o' + str(operation['id']),
|
||||
'x': operation['x'],
|
||||
'y': operation['y'],
|
||||
'width': 150,
|
||||
'height': 40
|
||||
})
|
||||
|
||||
for block in previous_data['blocks']:
|
||||
new_layout.append({
|
||||
'nodeID': 'b' + str(block['id']),
|
||||
'x': block['x'],
|
||||
'y': block['y'],
|
||||
'width': block['width'],
|
||||
'height': block['height']
|
||||
})
|
||||
|
||||
layout.data = new_layout
|
||||
layout.save(update_fields=['data'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oss', '0011_remove_operation_position_x_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_layout),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.1 on 2025-06-11 10:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oss', '0012 restructure_layout'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='layout',
|
||||
name='data',
|
||||
field=models.JSONField(default=list, verbose_name='Расположение'),
|
||||
),
|
||||
]
|
|
@ -13,7 +13,7 @@ class Layout(Model):
|
|||
|
||||
data = JSONField(
|
||||
verbose_name='Расположение',
|
||||
default=dict
|
||||
default=list
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -40,7 +40,7 @@ class OperationSchema:
|
|||
def create(**kwargs) -> 'OperationSchema':
|
||||
''' Create LibraryItem via OperationSchema. '''
|
||||
model = LibraryItem.objects.create(item_type=LibraryItemType.OPERATION_SCHEMA, **kwargs)
|
||||
Layout.objects.create(oss=model, data={'operations': [], 'blocks': []})
|
||||
Layout.objects.create(oss=model, data=[])
|
||||
return OperationSchema(model)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -2,16 +2,9 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
|
||||
class OperationNodeSerializer(serializers.Serializer):
|
||||
''' Operation position. '''
|
||||
id = serializers.IntegerField()
|
||||
x = serializers.FloatField()
|
||||
y = serializers.FloatField()
|
||||
|
||||
|
||||
class BlockNodeSerializer(serializers.Serializer):
|
||||
class NodeSerializer(serializers.Serializer):
|
||||
''' Block position. '''
|
||||
id = serializers.IntegerField()
|
||||
nodeID = serializers.CharField()
|
||||
x = serializers.FloatField()
|
||||
y = serializers.FloatField()
|
||||
width = serializers.FloatField()
|
||||
|
@ -19,13 +12,8 @@ class BlockNodeSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class LayoutSerializer(serializers.Serializer):
|
||||
''' Layout for OperationSchema. '''
|
||||
blocks = serializers.ListField(
|
||||
child=BlockNodeSerializer()
|
||||
)
|
||||
operations = serializers.ListField(
|
||||
child=OperationNodeSerializer()
|
||||
)
|
||||
''' Serializer: Layout data. '''
|
||||
data = serializers.ListField(child=NodeSerializer()) # type: ignore
|
||||
|
||||
|
||||
class SubstitutionExSerializer(serializers.Serializer):
|
||||
|
|
|
@ -13,7 +13,7 @@ from apps.rsform.serializers import SubstitutionSerializerBase
|
|||
from shared import messages as msg
|
||||
|
||||
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
|
||||
from .basics import LayoutSerializer, SubstitutionExSerializer
|
||||
from .basics import NodeSerializer, SubstitutionExSerializer
|
||||
|
||||
|
||||
class OperationSerializer(serializers.ModelSerializer):
|
||||
|
@ -52,7 +52,9 @@ class CreateBlockSerializer(serializers.Serializer):
|
|||
model = Block
|
||||
fields = 'title', 'description', 'parent'
|
||||
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
item_data = BlockCreateData()
|
||||
width = serializers.FloatField()
|
||||
height = serializers.FloatField()
|
||||
|
@ -100,7 +102,10 @@ class UpdateBlockSerializer(serializers.Serializer):
|
|||
model = Block
|
||||
fields = 'title', 'description', 'parent'
|
||||
|
||||
layout = LayoutSerializer(required=False)
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer(),
|
||||
required=False
|
||||
)
|
||||
target = PKField(many=False, queryset=Block.objects.all())
|
||||
item_data = UpdateBlockData()
|
||||
|
||||
|
@ -127,7 +132,9 @@ class UpdateBlockSerializer(serializers.Serializer):
|
|||
|
||||
class DeleteBlockSerializer(serializers.Serializer):
|
||||
''' Serializer: Delete block. '''
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
target = PKField(many=False, queryset=Block.objects.all().only('oss_id'))
|
||||
|
||||
def validate(self, attrs):
|
||||
|
@ -142,7 +149,9 @@ class DeleteBlockSerializer(serializers.Serializer):
|
|||
|
||||
class MoveItemsSerializer(serializers.Serializer):
|
||||
''' Serializer: Move items to another parent. '''
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
operations = PKField(many=True, queryset=Operation.objects.all().only('oss_id', 'parent'))
|
||||
blocks = PKField(many=True, queryset=Block.objects.all().only('oss_id', 'parent'))
|
||||
destination = PKField(many=False, queryset=Block.objects.all().only('oss_id'), allow_null=True)
|
||||
|
@ -196,8 +205,12 @@ class CreateOperationSerializer(serializers.Serializer):
|
|||
'alias', 'operation_type', 'title', \
|
||||
'description', 'result', 'parent'
|
||||
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
item_data = CreateOperationData()
|
||||
width = serializers.FloatField()
|
||||
height = serializers.FloatField()
|
||||
position_x = serializers.FloatField()
|
||||
position_y = serializers.FloatField()
|
||||
create_schema = serializers.BooleanField(default=False, required=False)
|
||||
|
@ -230,7 +243,10 @@ class UpdateOperationSerializer(serializers.Serializer):
|
|||
model = Operation
|
||||
fields = 'alias', 'title', 'description', 'parent'
|
||||
|
||||
layout = LayoutSerializer(required=False)
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer(),
|
||||
required=False
|
||||
)
|
||||
target = PKField(many=False, queryset=Operation.objects.all())
|
||||
item_data = UpdateOperationData()
|
||||
arguments = PKField(many=True, queryset=Operation.objects.all().only('oss_id', 'result_id'), required=False)
|
||||
|
@ -297,7 +313,9 @@ class UpdateOperationSerializer(serializers.Serializer):
|
|||
|
||||
class DeleteOperationSerializer(serializers.Serializer):
|
||||
''' Serializer: Delete operation. '''
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
target = PKField(many=False, queryset=Operation.objects.all().only('oss_id', 'result'))
|
||||
keep_constituents = serializers.BooleanField(default=False, required=False)
|
||||
delete_schema = serializers.BooleanField(default=False, required=False)
|
||||
|
@ -314,7 +332,9 @@ class DeleteOperationSerializer(serializers.Serializer):
|
|||
|
||||
class TargetOperationSerializer(serializers.Serializer):
|
||||
''' Serializer: Target single operation. '''
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
target = PKField(many=False, queryset=Operation.objects.all().only('oss_id', 'result_id'))
|
||||
|
||||
def validate(self, attrs):
|
||||
|
@ -329,7 +349,9 @@ class TargetOperationSerializer(serializers.Serializer):
|
|||
|
||||
class SetOperationInputSerializer(serializers.Serializer):
|
||||
''' Serializer: Set input schema for operation. '''
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
target = PKField(many=False, queryset=Operation.objects.all())
|
||||
input = PKField(
|
||||
many=False,
|
||||
|
@ -366,7 +388,9 @@ class OperationSchemaSerializer(serializers.ModelSerializer):
|
|||
substitutions = serializers.ListField(
|
||||
child=SubstitutionExSerializer()
|
||||
)
|
||||
layout = LayoutSerializer()
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
|
@ -459,7 +483,7 @@ class RelocateConstituentsSerializer(serializers.Serializer):
|
|||
|
||||
return attrs
|
||||
|
||||
# ====== Internals =================================================================================
|
||||
# ====== Internals ============
|
||||
|
||||
|
||||
def _collect_descendants(start_blocks: list[Block]) -> set[int]:
|
||||
|
|
|
@ -59,14 +59,11 @@ class TestChangeAttributes(EndpointTester):
|
|||
self.operation3.refresh_from_db()
|
||||
self.ks3 = RSForm(self.operation3.result)
|
||||
|
||||
self.layout_data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation3.pk, 'x': 0, 'y': 0},
|
||||
],
|
||||
'blocks': []
|
||||
}
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
]
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
|
|
@ -57,14 +57,11 @@ class TestChangeConstituents(EndpointTester):
|
|||
self.ks3 = RSForm(self.operation3.result)
|
||||
self.assertEqual(self.ks3.constituents().count(), 4)
|
||||
|
||||
self.layout_data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation3.pk, 'x': 0, 'y': 0},
|
||||
],
|
||||
'blocks': []
|
||||
}
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
]
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
|
|
@ -107,16 +107,13 @@ class TestChangeOperations(EndpointTester):
|
|||
convention='KS5D4'
|
||||
)
|
||||
|
||||
self.layout_data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation3.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation4.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation5.pk, 'x': 0, 'y': 0},
|
||||
],
|
||||
'blocks': []
|
||||
}
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation4.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation5.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40}
|
||||
]
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
|
|
@ -107,16 +107,13 @@ class TestChangeSubstitutions(EndpointTester):
|
|||
convention='KS5D4'
|
||||
)
|
||||
|
||||
self.layout_data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation3.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation4.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation5.pk, 'x': 0, 'y': 0},
|
||||
],
|
||||
'blocks': []
|
||||
}
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation4.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation5.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
]
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
|
|
@ -49,16 +49,14 @@ class TestOssBlocks(EndpointTester):
|
|||
title='3',
|
||||
parent=self.block1
|
||||
)
|
||||
self.layout_data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
],
|
||||
'blocks': [
|
||||
{'id': self.block1.pk, 'x': 0, 'y': 0, 'width': 0.5, 'height': 0.5},
|
||||
{'id': self.block2.pk, 'x': 0, 'y': 0, 'width': 0.5, 'height': 0.5},
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
|
||||
{'nodeID': 'b' + str(self.block1.pk), 'x': 0, 'y': 0, 'width': 0.5, 'height': 0.5},
|
||||
{'nodeID': 'b' + str(self.block2.pk), 'x': 0, 'y': 0, 'width': 0.5, 'height': 0.5},
|
||||
]
|
||||
}
|
||||
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
@ -88,7 +86,7 @@ class TestOssBlocks(EndpointTester):
|
|||
self.assertEqual(len(response.data['oss']['blocks']), 3)
|
||||
new_block = response.data['new_block']
|
||||
layout = response.data['oss']['layout']
|
||||
item = [item for item in layout['blocks'] if item['id'] == new_block['id']][0]
|
||||
item = [item for item in layout if item['nodeID'] == 'b' + str(new_block['id'])][0]
|
||||
self.assertEqual(new_block['title'], data['item_data']['title'])
|
||||
self.assertEqual(new_block['description'], data['item_data']['description'])
|
||||
self.assertEqual(new_block['parent'], None)
|
||||
|
|
|
@ -54,14 +54,11 @@ class TestOssOperations(EndpointTester):
|
|||
alias='3',
|
||||
operation_type=OperationType.SYNTHESIS
|
||||
)
|
||||
self.layout_data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation3.pk, 'x': 0, 'y': 0},
|
||||
],
|
||||
'blocks': []
|
||||
}
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
]
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
@ -87,7 +84,9 @@ class TestOssOperations(EndpointTester):
|
|||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
|
||||
}
|
||||
self.executeBadData(data=data)
|
||||
|
@ -102,7 +101,7 @@ class TestOssOperations(EndpointTester):
|
|||
self.assertEqual(len(response.data['oss']['operations']), 4)
|
||||
new_operation = response.data['new_operation']
|
||||
layout = response.data['oss']['layout']
|
||||
item = [item for item in layout['operations'] if item['id'] == new_operation['id']][0]
|
||||
item = [item for item in layout if item['nodeID'] == 'o' + str(new_operation['id'])][0]
|
||||
self.assertEqual(new_operation['alias'], data['item_data']['alias'])
|
||||
self.assertEqual(new_operation['operation_type'], data['item_data']['operation_type'])
|
||||
self.assertEqual(new_operation['title'], data['item_data']['title'])
|
||||
|
@ -111,6 +110,8 @@ class TestOssOperations(EndpointTester):
|
|||
self.assertEqual(new_operation['parent'], None)
|
||||
self.assertEqual(item['x'], data['position_x'])
|
||||
self.assertEqual(item['y'], data['position_y'])
|
||||
self.assertEqual(item['width'], data['width'])
|
||||
self.assertEqual(item['height'], data['height'])
|
||||
self.operation1.refresh_from_db()
|
||||
|
||||
self.executeForbidden(data=data, item=self.unowned_id)
|
||||
|
@ -132,7 +133,9 @@ class TestOssOperations(EndpointTester):
|
|||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
|
||||
}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
|
@ -160,6 +163,8 @@ class TestOssOperations(EndpointTester):
|
|||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50,
|
||||
'arguments': [self.operation1.pk, self.operation3.pk]
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
|
@ -185,7 +190,9 @@ class TestOssOperations(EndpointTester):
|
|||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
new_operation = response.data['new_operation']
|
||||
|
@ -207,7 +214,9 @@ class TestOssOperations(EndpointTester):
|
|||
'create_schema': True,
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
data['item_data']['result'] = None
|
||||
|
@ -244,7 +253,7 @@ class TestOssOperations(EndpointTester):
|
|||
self.login()
|
||||
response = self.executeOK(data=data)
|
||||
layout = response.data['layout']
|
||||
deleted_items = [item for item in layout['operations'] if item['id'] == data['target']]
|
||||
deleted_items = [item for item in layout if item['nodeID'] == 'o' + str(data['target'])]
|
||||
self.assertEqual(len(response.data['operations']), 2)
|
||||
self.assertEqual(len(deleted_items), 0)
|
||||
|
||||
|
|
|
@ -55,11 +55,11 @@ class TestOssViewset(EndpointTester):
|
|||
alias='3',
|
||||
operation_type=OperationType.SYNTHESIS
|
||||
)
|
||||
self.layout_data = {'operations': [
|
||||
{'id': self.operation1.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation2.pk, 'x': 0, 'y': 0},
|
||||
{'id': self.operation3.pk, 'x': 0, 'y': 0},
|
||||
], 'blocks': []}
|
||||
self.layout_data = [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 0, 'y': 0, 'width': 150, 'height': 40}
|
||||
]
|
||||
layout = self.owned.layout()
|
||||
layout.data = self.layout_data
|
||||
layout.save()
|
||||
|
@ -107,10 +107,9 @@ class TestOssViewset(EndpointTester):
|
|||
self.assertEqual(arguments[1]['argument'], self.operation2.pk)
|
||||
|
||||
layout = response.data['layout']
|
||||
self.assertEqual(layout['blocks'], [])
|
||||
self.assertEqual(layout['operations'][0], {'id': self.operation1.pk, 'x': 0, 'y': 0})
|
||||
self.assertEqual(layout['operations'][1], {'id': self.operation2.pk, 'x': 0, 'y': 0})
|
||||
self.assertEqual(layout['operations'][2], {'id': self.operation3.pk, 'x': 0, 'y': 0})
|
||||
self.assertEqual(layout[0], self.layout_data[0])
|
||||
self.assertEqual(layout[1], self.layout_data[1])
|
||||
self.assertEqual(layout[2], self.layout_data[2])
|
||||
|
||||
self.executeOK(item=self.unowned_id)
|
||||
self.executeForbidden(item=self.private_id)
|
||||
|
@ -126,23 +125,21 @@ class TestOssViewset(EndpointTester):
|
|||
self.populateData()
|
||||
self.executeBadData(item=self.owned_id)
|
||||
|
||||
data = {'operations': [], 'blocks': []}
|
||||
data = {'data': []}
|
||||
self.executeOK(data=data)
|
||||
|
||||
data = {
|
||||
'operations': [
|
||||
{'id': self.operation1.pk, 'x': 42.1, 'y': 1337},
|
||||
{'id': self.operation2.pk, 'x': 36.1, 'y': 1437},
|
||||
{'id': self.operation3.pk, 'x': 36.1, 'y': 1435}
|
||||
], 'blocks': []
|
||||
}
|
||||
data = {'data': [
|
||||
{'nodeID': 'o' + str(self.operation1.pk), 'x': 42.1, 'y': 1337, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation2.pk), 'x': 36.1, 'y': 1437, 'width': 150, 'height': 40},
|
||||
{'nodeID': 'o' + str(self.operation3.pk), 'x': 36.1, 'y': 1435, 'width': 150, 'height': 40}
|
||||
]}
|
||||
self.toggle_admin(True)
|
||||
self.executeOK(data=data, item=self.unowned_id)
|
||||
|
||||
self.toggle_admin(False)
|
||||
self.executeOK(data=data, item=self.owned_id)
|
||||
self.owned.refresh_from_db()
|
||||
self.assertEqual(self.owned.layout().data, data)
|
||||
self.assertEqual(self.owned.layout().data, data['data'])
|
||||
|
||||
self.executeForbidden(data=data, item=self.unowned_id)
|
||||
self.executeForbidden(data=data, item=self.private_id)
|
||||
|
|
|
@ -91,7 +91,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
''' Endpoint: Update schema layout. '''
|
||||
serializer = s.LayoutSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
m.OperationSchema(self.get_object()).update_layout(serializer.validated_data)
|
||||
m.OperationSchema(self.get_object()).update_layout(serializer.validated_data['data'])
|
||||
return Response(status=c.HTTP_200_OK)
|
||||
|
||||
@extend_schema(
|
||||
|
@ -120,8 +120,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
children_operations: list[m.Operation] = serializer.validated_data['children_operations']
|
||||
with transaction.atomic():
|
||||
new_block = oss.create_block(**serializer.validated_data['item_data'])
|
||||
layout['blocks'].append({
|
||||
'id': new_block.pk,
|
||||
layout.append({
|
||||
'nodeID': 'b' + str(new_block.pk),
|
||||
'x': serializer.validated_data['position_x'],
|
||||
'y': serializer.validated_data['position_y'],
|
||||
'width': serializer.validated_data['width'],
|
||||
|
@ -205,7 +205,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
oss = m.OperationSchema(self.get_object())
|
||||
block = cast(m.Block, serializer.validated_data['target'])
|
||||
layout = serializer.validated_data['layout']
|
||||
layout['blocks'] = [x for x in layout['blocks'] if x['id'] != block.pk]
|
||||
layout = [x for x in layout if x['nodeID'] != 'b' + str(block.pk)]
|
||||
with transaction.atomic():
|
||||
oss.delete_block(block)
|
||||
oss.update_layout(layout)
|
||||
|
@ -274,10 +274,12 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
layout = serializer.validated_data['layout']
|
||||
with transaction.atomic():
|
||||
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
|
||||
layout['operations'].append({
|
||||
'id': new_operation.pk,
|
||||
layout.append({
|
||||
'nodeID': 'o' + str(new_operation.pk),
|
||||
'x': serializer.validated_data['position_x'],
|
||||
'y': serializer.validated_data['position_y']
|
||||
'y': serializer.validated_data['position_y'],
|
||||
'width': serializer.validated_data['width'],
|
||||
'height': serializer.validated_data['height']
|
||||
})
|
||||
oss.update_layout(layout)
|
||||
|
||||
|
@ -384,7 +386,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
operation = cast(m.Operation, serializer.validated_data['target'])
|
||||
old_schema = operation.result
|
||||
layout = serializer.validated_data['layout']
|
||||
layout['operations'] = [x for x in layout['operations'] if x['id'] != operation.pk]
|
||||
layout = [x for x in layout if x['nodeID'] != 'o' + str(operation.pk)]
|
||||
with transaction.atomic():
|
||||
oss.delete_operation(operation.pk, serializer.validated_data['keep_constituents'])
|
||||
oss.update_layout(layout)
|
||||
|
|
|
@ -50,7 +50,7 @@ export const ossApi = {
|
|||
axiosPatch({
|
||||
endpoint: `/api/oss/${itemID}/update-layout`,
|
||||
request: {
|
||||
data: data,
|
||||
data: { data: data },
|
||||
successMessage: isSilent ? undefined : infoMsg.changesSaved
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -90,7 +90,7 @@ export class OssLoader {
|
|||
this.graph.topologicalOrder().forEach(operationID => {
|
||||
const operation = this.operationByID.get(operationID)!;
|
||||
const schema = this.items.find(item => item.id === operation.result);
|
||||
const position = this.oss.layout.operations.find(item => item.id === operationID);
|
||||
const position = this.oss.layout.find(item => item.nodeID === operation.nodeID);
|
||||
operation.x = position?.x ?? 0;
|
||||
operation.y = position?.y ?? 0;
|
||||
operation.is_consolidation = this.inferConsolidation(operationID);
|
||||
|
@ -104,7 +104,7 @@ export class OssLoader {
|
|||
|
||||
private inferBlockAttributes() {
|
||||
this.oss.blocks.forEach(block => {
|
||||
const geometry = this.oss.layout.blocks.find(item => item.id === block.id);
|
||||
const geometry = this.oss.layout.find(item => item.nodeID === block.nodeID);
|
||||
block.x = geometry?.x ?? 0;
|
||||
block.y = geometry?.y ?? 0;
|
||||
block.width = geometry?.width ?? BLOCK_NODE_MIN_WIDTH;
|
||||
|
|
|
@ -72,11 +72,8 @@ export type IRelocateConstituentsDTO = z.infer<typeof schemaRelocateConstituents
|
|||
/** Represents {@link IConstituenta} reference. */
|
||||
export type IConstituentaReference = z.infer<typeof schemaConstituentaReference>;
|
||||
|
||||
/** Represents {@link IOperation} position. */
|
||||
export type IOperationPosition = z.infer<typeof schemaOperationPosition>;
|
||||
|
||||
/** Represents {@link IBlock} position. */
|
||||
export type IBlockPosition = z.infer<typeof schemaBlockPosition>;
|
||||
/** Represents {@link IOperationSchema} node position. */
|
||||
export type INodePosition = z.infer<typeof schemaNodePosition>;
|
||||
|
||||
// ====== Schemas ======
|
||||
export const schemaOperationType = z.enum(Object.values(OperationType) as [OperationType, ...OperationType[]]);
|
||||
|
@ -108,24 +105,15 @@ export const schemaCstSubstituteInfo = schemaSubstituteConstituents.extend({
|
|||
substitution_term: z.string()
|
||||
});
|
||||
|
||||
export const schemaOperationPosition = z.strictObject({
|
||||
id: z.number(),
|
||||
x: z.number(),
|
||||
y: z.number()
|
||||
});
|
||||
|
||||
export const schemaBlockPosition = z.strictObject({
|
||||
id: z.number(),
|
||||
export const schemaNodePosition = z.strictObject({
|
||||
nodeID: z.string(),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number()
|
||||
});
|
||||
|
||||
export const schemaOssLayout = z.strictObject({
|
||||
operations: z.array(schemaOperationPosition),
|
||||
blocks: z.array(schemaBlockPosition)
|
||||
});
|
||||
export const schemaOssLayout = z.array(schemaNodePosition);
|
||||
|
||||
export const schemaOperationSchema = schemaLibraryItem.extend({
|
||||
editors: z.number().array(),
|
||||
|
@ -188,6 +176,8 @@ export const schemaCreateOperation = z.strictObject({
|
|||
}),
|
||||
position_x: z.number(),
|
||||
position_y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
arguments: z.array(z.number()),
|
||||
create_schema: z.boolean()
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
import { type ICreateOperationDTO, OperationType, schemaCreateOperation } from '../../backend/types';
|
||||
import { useCreateOperation } from '../../backend/use-create-operation';
|
||||
import { describeOperationType, labelOperationType } from '../../labels';
|
||||
import { type LayoutManager } from '../../models/oss-layout-api';
|
||||
import { type LayoutManager, OPERATION_NODE_HEIGHT, OPERATION_NODE_WIDTH } from '../../models/oss-layout-api';
|
||||
|
||||
import { TabInputOperation } from './tab-input-operation';
|
||||
import { TabSynthesisOperation } from './tab-synthesis-operation';
|
||||
|
@ -54,6 +54,8 @@ export function DlgCreateOperation() {
|
|||
position_x: defaultX,
|
||||
position_y: defaultY,
|
||||
arguments: initialInputs,
|
||||
width: OPERATION_NODE_WIDTH,
|
||||
height: OPERATION_NODE_HEIGHT,
|
||||
create_schema: false,
|
||||
layout: manager.layout
|
||||
},
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import {
|
||||
type IBlockPosition,
|
||||
type ICreateBlockDTO,
|
||||
type ICreateOperationDTO,
|
||||
type IOperationPosition,
|
||||
type IOssLayout
|
||||
} from '../backend/types';
|
||||
import { type ICreateBlockDTO, type ICreateOperationDTO, type INodePosition, type IOssLayout } from '../backend/types';
|
||||
|
||||
import { type IOperationSchema } from './oss';
|
||||
import { type Position2D, type Rectangle2D } from './oss-layout';
|
||||
|
@ -12,8 +6,8 @@ import { type Position2D, type Rectangle2D } from './oss-layout';
|
|||
export const GRID_SIZE = 10; // pixels - size of OSS grid
|
||||
const MIN_DISTANCE = 2 * GRID_SIZE; // pixels - minimum distance between nodes
|
||||
|
||||
const OPERATION_NODE_WIDTH = 150;
|
||||
const OPERATION_NODE_HEIGHT = 40;
|
||||
export const OPERATION_NODE_WIDTH = 150;
|
||||
export const OPERATION_NODE_HEIGHT = 40;
|
||||
|
||||
/** Layout manipulations for {@link IOperationSchema}. */
|
||||
export class LayoutManager {
|
||||
|
@ -30,27 +24,30 @@ export class LayoutManager {
|
|||
}
|
||||
|
||||
/** Calculate insert position for a new {@link IOperation} */
|
||||
newOperationPosition(data: ICreateOperationDTO): Position2D {
|
||||
let result = { x: data.position_x, y: data.position_y };
|
||||
const operations = this.layout.operations;
|
||||
const parentNode = this.layout.blocks.find(pos => pos.id === data.item_data.parent);
|
||||
if (operations.length === 0) {
|
||||
newOperationPosition(data: ICreateOperationDTO): Rectangle2D {
|
||||
let result = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`);
|
||||
if (this.oss.operations.length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const operations = this.layout.filter(pos => pos.nodeID.startsWith('o'));
|
||||
if (data.arguments.length !== 0) {
|
||||
result = calculatePositionFromArgs(data.arguments, operations);
|
||||
const pos = calculatePositionFromArgs(
|
||||
operations.filter(node => data.arguments.includes(Number(node.nodeID.slice(1))))
|
||||
);
|
||||
result.x = pos.x;
|
||||
result.y = pos.y;
|
||||
} else if (parentNode) {
|
||||
result.x = parentNode.x + MIN_DISTANCE;
|
||||
result.y = parentNode.y + MIN_DISTANCE;
|
||||
} else {
|
||||
result = this.calculatePositionForFreeOperation(result);
|
||||
const pos = this.calculatePositionForFreeOperation(result);
|
||||
result.x = pos.x;
|
||||
result.y = pos.y;
|
||||
}
|
||||
|
||||
result = preventOverlap(
|
||||
{ ...result, width: OPERATION_NODE_WIDTH, height: OPERATION_NODE_HEIGHT },
|
||||
operations.map(node => ({ ...node, width: OPERATION_NODE_WIDTH, height: OPERATION_NODE_HEIGHT }))
|
||||
);
|
||||
result = preventOverlap(result, operations);
|
||||
|
||||
if (parentNode) {
|
||||
const borderX = result.x + OPERATION_NODE_WIDTH + MIN_DISTANCE;
|
||||
|
@ -64,18 +61,18 @@ export class LayoutManager {
|
|||
// TODO: trigger cascading updates
|
||||
}
|
||||
|
||||
return { x: result.x, y: result.y };
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Calculate insert position for a new {@link IBlock} */
|
||||
newBlockPosition(data: ICreateBlockDTO): Rectangle2D {
|
||||
const block_nodes = data.children_blocks
|
||||
.map(id => this.layout.blocks.find(block => block.id === id))
|
||||
.map(id => this.layout.find(block => block.nodeID === `b${id}`))
|
||||
.filter(node => !!node);
|
||||
const operation_nodes = data.children_operations
|
||||
.map(id => this.layout.operations.find(operation => operation.id === id))
|
||||
.map(id => this.layout.find(operation => operation.nodeID === `o${id}`))
|
||||
.filter(node => !!node);
|
||||
const parentNode = this.layout.blocks.find(pos => pos.id === data.item_data.parent);
|
||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`);
|
||||
|
||||
let result: Rectangle2D = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||
|
||||
|
@ -98,19 +95,21 @@ export class LayoutManager {
|
|||
|
||||
if (block_nodes.length === 0 && operation_nodes.length === 0) {
|
||||
if (parentNode) {
|
||||
const siblings = this.oss.blocks.filter(block => block.parent === parentNode.id).map(block => block.id);
|
||||
const siblings = this.oss.blocks
|
||||
.filter(block => block.parent === data.item_data.parent)
|
||||
.map(block => block.nodeID);
|
||||
if (siblings.length > 0) {
|
||||
result = preventOverlap(
|
||||
result,
|
||||
this.layout.blocks.filter(block => siblings.includes(block.id))
|
||||
this.layout.filter(node => siblings.includes(node.nodeID))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.id);
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.nodeID);
|
||||
if (rootBlocks.length > 0) {
|
||||
result = preventOverlap(
|
||||
result,
|
||||
this.layout.blocks.filter(block => rootBlocks.includes(block.id))
|
||||
this.layout.filter(node => rootBlocks.includes(node.nodeID))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +132,34 @@ export class LayoutManager {
|
|||
|
||||
/** Update layout when parent changes */
|
||||
onOperationChangeParent(targetID: number, newParent: number | null) {
|
||||
console.error('not implemented', targetID, newParent);
|
||||
const targetNode = this.layout.find(pos => pos.nodeID === `o${targetID}`);
|
||||
if (!targetNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newParent === null) {
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.nodeID);
|
||||
const blocksPositions = this.layout.filter(pos => rootBlocks.includes(pos.nodeID));
|
||||
if (blocksPositions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const operationPositions = this.layout.filter(pos => pos.nodeID.startsWith('o') && pos.nodeID !== `o${targetID}`);
|
||||
const newRect = preventOverlap(targetNode, [...blocksPositions, ...operationPositions]);
|
||||
targetNode.x = newRect.x;
|
||||
targetNode.y = newRect.y;
|
||||
return;
|
||||
} else {
|
||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${newParent}`);
|
||||
if (!parentNode) {
|
||||
return;
|
||||
}
|
||||
if (rectanglesOverlap(parentNode, targetNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: fix position based on parent
|
||||
}
|
||||
}
|
||||
|
||||
/** Update layout when parent changes */
|
||||
|
@ -142,17 +168,16 @@ export class LayoutManager {
|
|||
}
|
||||
|
||||
private calculatePositionForFreeOperation(initial: Position2D): Position2D {
|
||||
const operations = this.layout.operations;
|
||||
if (operations.length === 0) {
|
||||
if (this.oss.operations.length === 0) {
|
||||
return initial;
|
||||
}
|
||||
|
||||
const freeInputs = this.oss.operations
|
||||
.filter(operation => operation.arguments.length === 0 && operation.parent === null)
|
||||
.map(operation => operation.id);
|
||||
let inputsPositions = operations.filter(pos => freeInputs.includes(pos.id));
|
||||
.map(operation => operation.nodeID);
|
||||
let inputsPositions = this.layout.filter(pos => freeInputs.includes(pos.nodeID));
|
||||
if (inputsPositions.length === 0) {
|
||||
inputsPositions = operations;
|
||||
inputsPositions = this.layout.filter(pos => pos.nodeID.startsWith('o'));
|
||||
}
|
||||
const maxX = Math.max(...inputsPositions.map(node => node.x));
|
||||
const minY = Math.min(...inputsPositions.map(node => node.y));
|
||||
|
@ -163,8 +188,8 @@ export class LayoutManager {
|
|||
}
|
||||
|
||||
private calculatePositionForFreeBlock(initial: Rectangle2D): Rectangle2D {
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.id);
|
||||
const blocksPositions = this.layout.blocks.filter(pos => rootBlocks.includes(pos.id));
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.nodeID);
|
||||
const blocksPositions = this.layout.filter(pos => rootBlocks.includes(pos.nodeID));
|
||||
if (blocksPositions.length === 0) {
|
||||
return initial;
|
||||
}
|
||||
|
@ -211,11 +236,10 @@ function preventOverlap(target: Rectangle2D, fixedRectangles: Rectangle2D[]): Re
|
|||
return target;
|
||||
}
|
||||
|
||||
function calculatePositionFromArgs(args: number[], operations: IOperationPosition[]): Position2D {
|
||||
const argNodes = operations.filter(pos => args.includes(pos.id));
|
||||
const maxY = Math.max(...argNodes.map(node => node.y));
|
||||
const minX = Math.min(...argNodes.map(node => node.x));
|
||||
const maxX = Math.max(...argNodes.map(node => node.x));
|
||||
function calculatePositionFromArgs(args: INodePosition[]): Position2D {
|
||||
const maxY = Math.max(...args.map(node => node.y));
|
||||
const minX = Math.min(...args.map(node => node.x));
|
||||
const maxX = Math.max(...args.map(node => node.x));
|
||||
return {
|
||||
x: Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE,
|
||||
y: maxY + 2 * OPERATION_NODE_HEIGHT + MIN_DISTANCE
|
||||
|
@ -224,8 +248,8 @@ function calculatePositionFromArgs(args: number[], operations: IOperationPositio
|
|||
|
||||
function calculatePositionFromChildren(
|
||||
initial: Rectangle2D,
|
||||
operations: IOperationPosition[],
|
||||
blocks: IBlockPosition[]
|
||||
operations: INodePosition[],
|
||||
blocks: INodePosition[]
|
||||
): Rectangle2D {
|
||||
let left = undefined;
|
||||
let top = undefined;
|
||||
|
@ -249,11 +273,11 @@ function calculatePositionFromChildren(
|
|||
top = top === undefined ? operation.y - MIN_DISTANCE : Math.min(top, operation.y - MIN_DISTANCE);
|
||||
right =
|
||||
right === undefined
|
||||
? Math.max(left + initial.width, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE)
|
||||
: Math.max(right, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE);
|
||||
? Math.max(left + initial.width, operation.x + operation.width + MIN_DISTANCE)
|
||||
: Math.max(right, operation.x + operation.width + MIN_DISTANCE);
|
||||
bottom = !bottom
|
||||
? Math.max(top + initial.height, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE)
|
||||
: Math.max(bottom, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE);
|
||||
? Math.max(top + initial.height, operation.y + operation.height + MIN_DISTANCE)
|
||||
: Math.max(bottom, operation.y + operation.height + MIN_DISTANCE);
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { type Node, useReactFlow } from 'reactflow';
|
|||
import { type IOssLayout } from '../../../backend/types';
|
||||
import { type IOperationSchema } from '../../../models/oss';
|
||||
import { type Position2D } from '../../../models/oss-layout';
|
||||
import { OPERATION_NODE_HEIGHT, OPERATION_NODE_WIDTH } from '../../../models/oss-layout-api';
|
||||
import { useOssEdit } from '../oss-edit-context';
|
||||
|
||||
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from './graph/block-node';
|
||||
|
@ -14,22 +15,24 @@ export function useGetLayout() {
|
|||
return function getLayout(): IOssLayout {
|
||||
const nodes = getNodes();
|
||||
const nodeById = new Map(nodes.map(node => [node.id, node]));
|
||||
return {
|
||||
operations: nodes
|
||||
return [
|
||||
...nodes
|
||||
.filter(node => node.type !== 'block')
|
||||
.map(node => ({
|
||||
id: schema.itemByNodeID.get(node.id)!.id,
|
||||
...computeAbsolutePosition(node, schema, nodeById)
|
||||
nodeID: node.id,
|
||||
...computeAbsolutePosition(node, schema, nodeById),
|
||||
width: OPERATION_NODE_WIDTH,
|
||||
height: OPERATION_NODE_HEIGHT
|
||||
})),
|
||||
blocks: nodes
|
||||
...nodes
|
||||
.filter(node => node.type === 'block')
|
||||
.map(node => ({
|
||||
id: schema.itemByNodeID.get(node.id)!.id,
|
||||
nodeID: node.id,
|
||||
...computeAbsolutePosition(node, schema, nodeById),
|
||||
width: node.width ?? BLOCK_NODE_MIN_WIDTH,
|
||||
height: node.height ?? BLOCK_NODE_MIN_HEIGHT
|
||||
}))
|
||||
};
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user