-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GSoC2024] Implemented drag and drop feature #7624
base: develop
Are you sure you want to change the base?
[GSoC2024] Implemented drag and drop feature #7624
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally im not sure if this solution is good in UX terms. Design with arrows is a bit outdated and not so comfortable to use (if I want to place 10th attribute to the first place I need to make 10 clicks 😟).
Lets return to original proposal, as the author of 7370 mentiones, it sould be implemented as drag-n-drop. I belive we need to use dots icon that is shown only on hover.
It should be something similar to:
In CVAT, we use react-grid-layout to help us implement draggable and responsible layouts on analytics page and canvas-layout. I would suggest to implement this feature using specific rows-only draggable layout.
Also, please, add a link to the issue you're trying to resolve in PR description. And dont forget to add a changelog entry.
Ok, I will make the changes |
Hey I think react-grid-layout doesn't works on Forms. |
What do you mean by doesnt work on Forms? Could you describe the soultion you are trying to implement so we can discuss it? |
I was trying to implement the same but after wrapping with < ReactGridLayout >, this.renderAttributes was not returning anything. Most probably because fieldInstances was empty when calling from < ReactGridLayout >. |
I think we dont need to pass |
Hey, I have almost completed it but I am facing a problem. The thing is when we change the layout, I update the attributes list and this layout updates based on this new attributes list. These were the changes that I have made: url. |
Hi @KrishavRajSingh , As I can see you are trying to update form after layout changes and then it re-renders. I think we can fix this problem by managing layout somewhere outside of the form. There should be some state for the layout. By default it should be empty, then on first render of the form we set it up. If user makes some changes, we set the state of the layout object. When the form is submitted ( |
Hi @KrishavRajSingh, |
I am looking into this. |
Thanks for this, I was finally able to implement this drag&drop. Drag.Drop_CVAT.mp4 |
The attributes will come from the front-end as it is, then a new variable display_order is introduced on the server side whose value is equal to the index of the attribute in attributes. "display_order" is set for each attribute and then it is saved to the databse. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Out of diff range and nitpick comments (1)
cvat/apps/engine/views.py (1)
Line range hint
2714-2714
: Remove unnecessary f-string.- f"" + ""
Review Details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (6)
- cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
- cvat/apps/engine/migrations/0080_alter_attributespec_options_and_more.py (1 hunks)
- cvat/apps/engine/migrations/0081_alter_attributespec_options.py (1 hunks)
- cvat/apps/engine/models.py (1 hunks)
- cvat/apps/engine/serializers.py (4 hunks)
- cvat/apps/engine/views.py (2 hunks)
Files skipped from review due to trivial changes (2)
- cvat/apps/engine/migrations/0080_alter_attributespec_options_and_more.py
- cvat/apps/engine/migrations/0081_alter_attributespec_options.py
Additional Context Used
Ruff (5)
cvat/apps/engine/serializers.py (2)
711-711: f-string without any placeholders
1146-1146: f-string without any placeholders
cvat/apps/engine/views.py (3)
44-44:
cvat.apps.dataset_manager.views
imported but unused
1967-1967: f-string without any placeholders
2714-2714: f-string without any placeholders
Additional comments not posted (13)
cvat-ui/src/components/labels-editor/label-form.tsx (4)
8-10
: The new imports forRGL
andWidthProvider
fromreact-grid-layout
are necessary for the drag-and-drop functionality. Ensure thatreact-grid-layout
is listed as a dependency in yourpackage.json
.Also applies to: 20-20
31-31
: TheReactGridLayout
constant is correctly defined usingWidthProvider
andRGL
. This setup is essential for the drag-and-drop functionality.
464-496
: The inclusion of the drag-and-drop handle in therenderAttribute
method is correct and aligns with the new feature requirements.
703-730
: The integration ofReactGridLayout
withForm.List
for the drag-and-drop functionality is correct. Ensure that the layout state is managed correctly and consistently.Verification successful
The integration of
ReactGridLayout
withForm.List
for the drag-and-drop functionality is correct. The layout state is being managed correctly usingthis.formRef.current?.setFieldsValue
andthis.formRef.current?.getFieldValue
.Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the layout state management in the `LabelForm` component. # Test: Search for the layout state management. Expect: Proper handling of layout state. rg --type typescript 'layout' cvat-ui/src/components/labels-editor/label-form.tsxLength of output: 116
Script:
#!/bin/bash # Description: Verify the layout state management in the `LabelForm` component. # Test: Search for the layout state management. Expect: Proper handling of layout state. rg --type tsx 'layout' cvat-ui/src/components/labels-editor/label-form.tsxLength of output: 102
Script:
#!/bin/bash # Description: Verify the layout state management in the `LabelForm` component. # Test: Search for the layout state management. Expect: Proper handling of layout state. rg 'layout' cvat-ui/src/components/labels-editor/label-form.tsxLength of output: 482
cvat/apps/engine/models.py (3)
861-861
: The addition of theupdated_time
field to theAttributeSpec
model is a good practice for tracking the last modification time.
862-862
: The addition of thedisplay_order
field to theAttributeSpec
model will help manage the order of attributes explicitly.
867-867
: The addition of theordering
option in theMeta
class of theAttributeSpec
model ensures that attributes are ordered by thedisplay_order
field by default.cvat/apps/engine/serializers.py (6)
238-238
: Addition ofdisplay_order
field is consistent with the new drag-and-drop feature.
242-242
: Inclusion ofdisplay_order
in thefields
list ensures proper serialization.
409-409
: Initialization ofdisplay_order
to 0 is necessary for setting the initial order of attributes.
432-432
: Setting and incrementingdisplay_order
ensures unique ordering of attributes.
438-438
: Incrementingdisplay_order
after saving each attribute ensures correct ordering.
491-492
: Addingdisplay_order
to the attributes dictionary ensures correct ordering for new attributes.
@@ -49,7 +49,7 @@ | |||
from cvat.apps.engine.frame_provider import FrameProvider | |||
from cvat.apps.engine.media_extractors import get_mime | |||
from cvat.apps.engine.models import ( | |||
ClientFile, Job, JobType, Label, SegmentType, Task, Project, Issue, Data, | |||
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import.
- import cvat.apps.dataset_manager.views # pylint: disable=unused-import
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data, | |
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@klakhov, I have verified the imports in the cvat/apps/engine/views.py
file. The import statement import cvat.apps.dataset_manager.views # pylint: disable=unused-import
is indeed unused and can be safely removed.
Here is the updated import section without the unused import:
from cvat.apps.engine.models import (
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data,
)
Would you like me to open a new GitHub issue to track this change, or will you handle it in this PR?
Analysis chain
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Scan for unused imports in the specified Python file
flake8 --select=F401 cvat/apps/engine/views.py
Length of output: 122
Script:
#!/bin/bash
# List all import statements in the file
grep -E '^import |^from ' cvat/apps/engine/views.py > imports.txt
# Check usage of each imported module or symbol
while read -r line; do
# Extract the module or symbol name
symbol=$(echo "$line" | sed -E 's/^import |^from ([^ ]+) import .*/\1/')
# Check if the symbol is used in the file
if ! grep -q "$symbol" cvat/apps/engine/views.py; then
echo "Unused import: $line"
fi
done < imports.txt
Length of output: 11277
New Learnings Added
User: klakhov
PR: cvat-ai/cvat#7624
File: cvat/apps/engine/views.py:52-52
Timestamp: 2024-05-23T13:50:52.155Z
Learning: The import statement `import cvat.apps.dataset_manager.views # pylint: disable=unused-import` in `cvat/apps/engine/views.py` is unused and can be removed.
Note: Learnings are effective only in the context of specific code segments. To apply general coding guidelines, please configure review instructions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
@@ -2196,7 +2196,7 @@ class LabelViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, | |||
mixins.RetrieveModelMixin, mixins.DestroyModelMixin, PartialUpdateModelMixin | |||
): | |||
queryset = Label.objects.prefetch_related( | |||
'attributespec_set', | |||
Prefetch('attributespec_set', queryset=AttributeSpec.objects.order_by('display_order')), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unnecessary f-string.
- f""
+ ""
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
Prefetch('attributespec_set', queryset=AttributeSpec.objects.order_by('display_order')), | |
Prefetch('attributespec_set', queryset=AttributeSpec.objects.order_by('display_order')), |
const arr: SerializedAttribute[] = []; // to store the updated attributes | ||
const updatedLayout = this.formRef.current?.getFieldValue('layout'); | ||
if (updatedLayout) { | ||
const attr = values.attributes; | ||
updatedLayout.forEach((obj: any, index: number) => { | ||
arr[obj.y] = attr[index]; | ||
}); | ||
} | ||
values.attributes = arr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for updating the attributes based on the new layout state is correct. Consider adding comments to explain the purpose of each step for better readability.
+ // Store the updated attributes based on the new layout
const arr: SerializedAttribute[] = [];
const updatedLayout = this.formRef.current?.getFieldValue('layout');
if (updatedLayout) {
const attr = values.attributes;
updatedLayout.forEach((obj: any, index: number) => {
arr[obj.y] = attr[index];
});
}
values.attributes = arr;
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
const arr: SerializedAttribute[] = []; // to store the updated attributes | |
const updatedLayout = this.formRef.current?.getFieldValue('layout'); | |
if (updatedLayout) { | |
const attr = values.attributes; | |
updatedLayout.forEach((obj: any, index: number) => { | |
arr[obj.y] = attr[index]; | |
}); | |
} | |
values.attributes = arr; | |
// Store the updated attributes based on the new layout | |
const arr: SerializedAttribute[] = []; // to store the updated attributes | |
const updatedLayout = this.formRef.current?.getFieldValue('layout'); | |
if (updatedLayout) { | |
const attr = values.attributes; | |
updatedLayout.forEach((obj: any, index: number) => { | |
arr[obj.y] = attr[index]; | |
}); | |
} | |
values.attributes = arr; |
Do I still need updated_date as I am using display_order for the sorting field?? |
Any comments? Please, try to finalize the pull request this week. |
Hi @KrishavRajSingh , |
IMHO it is worthy of a dedicated pull request if we want these fields in Attribute and Label model. |
Alright, lets keep it for dedicated PR. @KrishavRajSingh, lets remove updated_date for now. |
Removed it, you may review it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Outside diff range and nitpick comments (2)
cvat/apps/engine/serializers.py (2)
Line range hint
711-711
: Remove unnecessary f-string.- raise exceptions.NotFound(f'Attribute with id #{attr_id} does not exist') + raise exceptions.NotFound('Attribute with id #{} does not exist'.format(attr_id))
Line range hint
1146-1146
: Remove unnecessary f-string.- raise serializers.ValidationError(f'The specified project #{project_id} does not exist.') + raise serializers.ValidationError('The specified project #{} does not exist.'.format(project_id))
Review Details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (4)
- cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
- cvat/apps/engine/migrations/0082_remove_attributespec_updated_time.py (1 hunks)
- cvat/apps/engine/models.py (1 hunks)
- cvat/apps/engine/serializers.py (4 hunks)
Files skipped from review due to trivial changes (1)
- cvat/apps/engine/migrations/0082_remove_attributespec_updated_time.py
Files skipped from review as they are similar to previous changes (2)
- cvat-ui/src/components/labels-editor/label-form.tsx
- cvat/apps/engine/models.py
Additional Context Used
Ruff (2)
cvat/apps/engine/serializers.py (2)
711-711: f-string without any placeholders
1146-1146: f-string without any placeholders
Additional comments not posted (4)
cvat/apps/engine/serializers.py (4)
432-432
: Ensure thatdisplay_order
is updated correctly during attribute updates.This line correctly sets the
display_order
for each attribute during the update process, ensuring that the order is maintained as per user interaction.
438-438
: Incrementdisplay_order
to maintain the correct sequence.This line correctly increments the
display_order
after updating each attribute, ensuring that the order is sequential and consistent.
492-492
: Setdisplay_order
during attribute creation to maintain order.This line correctly initializes the
display_order
for new attributes, ensuring that the order is maintained from the creation phase.
494-494
: Incrementdisplay_order
after creating each attribute to maintain the correct sequence.This line correctly increments the
display_order
after creating each attribute, ensuring that the order is sequential and consistent.
@KrishavRajSingh , You need to update the shema |
Done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review Details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- cvat/schema.yml (3 hunks)
Additional comments not posted (3)
cvat/schema.yml (3)
4-4
: API version updated to reflect new features.
6333-6334
: Addeddisplay_order
field to schema to support attribute reordering.
6361-6362
: Consistent addition ofdisplay_order
across different schema sections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Outside diff range and nitpick comments (2)
cvat/apps/engine/serializers.py (2)
Line range hint
421-450
: Ensuredisplay_order
is initialized correctly to support incremental attribute additions.- display_order = 0 + display_order = models.AttributeSpec.objects.filter(label=db_label).aggregate(Max('display_order'))['display_order__max'] or 0
Line range hint
725-725
: Replace f-strings with regular strings when there are no placeholders.- f'label:update Skeleton id:{db_skeleton.id} for label_id:{db_label.id}' + 'label:update Skeleton id:{} for label_id:{}'.format(db_skeleton.id, db_label.id) - f'label:create Skeleton id:{db_skeleton.id} for label_id:{db_label.id}' + 'label:create Skeleton id:{} for label_id:{}'.format(db_skeleton.id, db_label.id)Also applies to: 1160-1160
Review Details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- cvat/apps/engine/serializers.py (4 hunks)
- cvat/schema.yml (2 hunks)
Files skipped from review as they are similar to previous changes (1)
- cvat/schema.yml
Additional Context Used
Ruff (2)
cvat/apps/engine/serializers.py (2)
725-725: f-string without any placeholders
1160-1160: f-string without any placeholders
@@ -235,10 +235,11 @@ class AttributeSerializer(serializers.ModelSerializer): | |||
values = DelimitedStringListField(allow_empty=True, | |||
child=serializers.CharField(allow_blank=True, max_length=200), | |||
) | |||
display_order = serializers.IntegerField(required=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a default value for display_order
to ensure consistent behavior when not explicitly set.
- display_order = serializers.IntegerField(required=False)
+ display_order = serializers.IntegerField(required=False, default=0)
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
display_order = serializers.IntegerField(required=False) | |
display_order = serializers.IntegerField(required=False, default=0) |
display_order = 0 | ||
for attr in attributes: | ||
if attr.get('id', None): | ||
del attr['id'] | ||
|
||
attr['display_order'] = display_order | ||
models.AttributeSpec.objects.create(label=db_label, **attr) | ||
display_order += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize display_order
based on existing attributes to support incremental additions.
- display_order = 0
+ display_order = models.AttributeSpec.objects.filter(label=db_label).aggregate(Max('display_order'))['display_order__max'] or 0
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
display_order = 0 | |
for attr in attributes: | |
if attr.get('id', None): | |
del attr['id'] | |
attr['display_order'] = display_order | |
models.AttributeSpec.objects.create(label=db_label, **attr) | |
display_order += 1 | |
display_order = models.AttributeSpec.objects.filter(label=db_label).aggregate(Max('display_order'))['display_order__max'] or 0 | |
for attr in attributes: | |
if attr.get('id', None): | |
del attr['id'] | |
attr['display_order'] = display_order | |
models.AttributeSpec.objects.create(label=db_label, **attr) | |
display_order += 1 |
@@ -49,7 +49,7 @@ | |||
from cvat.apps.engine.frame_provider import FrameProvider | |||
from cvat.apps.engine.media_extractors import get_mime | |||
from cvat.apps.engine.models import ( | |||
ClientFile, Job, JobType, Label, SegmentType, Task, Project, Issue, Data, | |||
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import.
@@ -417,6 +418,7 @@ def update_label( | |||
except models.InvalidLabel as exc: | |||
raise exceptions.ValidationError(str(exc)) from exc | |||
|
|||
display_order = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use default value in model instead of implicit 0
} | ||
|
||
.cvat-drag-drop { | ||
cursor: pointer | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | |
} | |
@@ -63,6 +67,16 @@ export default class LabelForm extends React.Component<Props> { | |||
const { | |||
label, onSubmit, onSkeletonSubmit, onCancel, resetSkeleton, | |||
} = this.props; | |||
const arr: SerializedAttribute[] = []; // to store the updated attributes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets use more descriptive variable name instead of comment
@@ -63,6 +67,16 @@ export default class LabelForm extends React.Component<Props> { | |||
const { | |||
label, onSubmit, onSkeletonSubmit, onCancel, resetSkeleton, | |||
} = this.props; | |||
const arr: SerializedAttribute[] = []; // to store the updated attributes | |||
const updatedLayout = this.formRef.current?.getFieldValue('layout'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we store layout in the form, but not in the state of the component?
We dont need to send layout to the server as its in the attributes fields
Also please check failing linters and tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Review Details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
- cvat-ui/src/components/labels-editor/styles.scss (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- cvat-ui/src/components/labels-editor/styles.scss
Additional Context Used
Biome (20)
cvat-ui/src/components/labels-editor/label-form.tsx (20)
75-75: Unexpected any. Specify a different type.
157-157: Unexpected any. Specify a different type.
163-163: Unexpected any. Specify a different type.
163-163: Unexpected any. Specify a different type.
166-166: Unexpected any. Specify a different type.
166-166: Unexpected any. Specify a different type.
182-182: Unexpected any. Specify a different type.
196-196: Unexpected any. Specify a different type.
196-196: Unexpected any. Specify a different type.
241-241: Unexpected any. Specify a different type.
241-241: Unexpected any. Specify a different type.
246-246: Unexpected any. Specify a different type.
323-323: Unexpected any. Specify a different type.
345-345: Unexpected any. Specify a different type.
345-345: Unexpected any. Specify a different type.
349-349: Unexpected any. Specify a different type.
398-398: Unexpected any. Specify a different type.
408-408: Unexpected any. Specify a different type.
408-408: Unexpected any. Specify a different type.
426-426: Unexpected any. Specify a different type.
Additional comments not posted (4)
cvat-ui/src/components/labels-editor/label-form.tsx (4)
Line range hint
20-31
: Import and setup ofreact-grid-layout
for drag-and-drop functionality.This setup is crucial for the drag-and-drop feature and has been correctly implemented. The
WidthProvider
HOC is used to manage the grid's width, which is a common practice withreact-grid-layout
.
465-501
: Implementation of the drag-and-drop UI for attributes.The UI components for drag-and-drop are well-structured and follow the design requirements. The use of
HolderOutlined
for the drag handle is consistent with the initial feedback from the review comments.
704-730
: Integration ofReactGridLayout
for managing attribute layout.The integration of
ReactGridLayout
to manage the layout of attributes dynamically is a good use of the library. TheonLayoutChange
handler updates the form's layout state correctly, ensuring that the attribute order is preserved.
70-79
: Handling of attribute reordering logic in the form submission.The logic to reorder attributes based on the layout from the form is well-implemented. However, consider using a more specific type than
any
for theobj
parameter in the loop to enhance type safety.
if (updatedLayout) { | ||
// Updating attributes based on updatedLayout before sending it to server | ||
const attr = values.attributes; | ||
updatedLayout.forEach((obj: any, index: number) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a more specific type than any
.
Using any
can lead to potential runtime errors due to lack of type safety. Consider defining a more specific type for the obj
parameter.
- updatedLayout.forEach((obj: any, index: number) => {
+ updatedLayout.forEach((obj: LayoutObj, index: number) => {
Committable suggestion was skipped due low confidence.
I have fixed almost everything, but I am not able to run the tests locally. Please help me with that. |
@KrishavRajSingh |
Hey, I am getting thia issue while generating cvat-sdk for tests. |
Added the feature of dragging attributes
Motivation and context
resolves #7370
Checklist
develop
branch(cvat-canvas,
cvat-core,
cvat-data and
cvat-ui)
License
Feel free to contact the maintainers if that's a concern.
Summary by CodeRabbit
New Features
Style