{"id":21430163,"url":"https://github.com/cuba-platform/cuba-dnd","last_synced_at":"2026-02-17T05:01:55.301Z","repository":{"id":150931985,"uuid":"93508434","full_name":"cuba-platform/cuba-dnd","owner":"cuba-platform","description":"Provides Drag \u0026 Drop functionality for platform UI components","archived":false,"fork":false,"pushed_at":"2024-12-27T09:28:20.000Z","size":321,"stargazers_count":2,"open_issues_count":1,"forks_count":1,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-04-19T18:53:54.123Z","etag":null,"topics":["cuba-component","cuba-platform","drag-and-drop","drag-n-drop"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cuba-platform.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-06T10:58:30.000Z","updated_at":"2022-02-16T12:57:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"f99df190-d5ba-4c49-a9af-4af7ab4d2c5a","html_url":"https://github.com/cuba-platform/cuba-dnd","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/cuba-platform/cuba-dnd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Fcuba-dnd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Fcuba-dnd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Fcuba-dnd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Fcuba-dnd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cuba-platform","download_url":"https://codeload.github.com/cuba-platform/cuba-dnd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuba-platform%2Fcuba-dnd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29534444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T05:00:25.817Z","status":"ssl_error","status_checked_at":"2026-02-17T04:57:16.126Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuba-component","cuba-platform","drag-and-drop","drag-n-drop"],"created_at":"2024-11-22T22:21:06.647Z","updated_at":"2026-02-17T05:01:55.270Z","avatar_url":"https://github.com/cuba-platform.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CUBA DnD Add-on\n\n[![license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)\n[![Build Status](https://travis-ci.org/cuba-platform/cuba-dnd.svg?branch=master)](https://travis-ci.org/cuba-platform/cuba-dnd)\n\n## Overview\nThe add-on enables using drag and drop features in [CUBA](https://www.cuba-platform.com) applications.\n\n### Dependencies\n - [Vaadin add-on dragdroplayouts](https://github.com/parttio/dragdroplayouts)\n - [Vaadin DragAndDropWrapper](https://vaadin.com/vaadin-documentation-portlet/framework/advanced/advanced-dragndrop.html)\n\n### Main features\nUI components with Drag-and-Drop functionality:\n  - DDVerticalLayout;\n  - DDHorizontalLayout;\n  - DDGridLayout;\n  - DDCssLayout;\n  - DDAbsoluteLayout;\n  - DragAndDropWrapper.\n\n## Installation\n\nThe add-on can be added to your project in one of the ways described below. Installation from the Marketplace is the simplest way. The last version of the add-on compatible with the used version of the platform will be installed.\nAlso, you can install the add-on by coordinates choosing the required version of the add-on from the table.\n\nIn case you want to install the add-on by manual editing or by building from sources see the complete add-ons installation guide in [CUBA Platform documentation](https://doc.cuba-platform.com/manual-latest/manual.html#app_components_usage).\n\n## From the Marketplace\n\n1. Open your application in CUBA Studio. Check the latest version of CUBA Studio on the [CUBA Platform site](https://www.cuba-platform.com/download/previous-studio/).\n2. Go to *CUBA -\u003e Marketplace* in the main menu.\n\n ![marketplace](sreenshots/marketplace.png)\n\n3. Find the *Drag \u0026 Drop* add-on there.\n\n ![addons](sreenshots/addons.png)\n\n4. Click *Install* and apply the changes.\nThe add-on corresponding to the used platform version will be installed.\n\n## By Coordinates\n\n1. Open your application in CUBA Studio. Check the latest version of CUBA Studio on the [CUBA Platform site](https://www.cuba-platform.com/download/previous-studio/).\n2. Go to *CUBA -\u003e Marketplace* in the main menu.\n3. Click the icon in the upper-right corner.\n\n ![by-coordinates](sreenshots/by-coordinates.png)\n\n4. Paste the add-on coordinates in the corresponding field as follows:\n\n `com.haulmont.addon.dnd:cuba-dnd-global:\u003cadd-on version\u003e`\n\n where `\u003cadd-on version\u003e` is compatible with the used version of the CUBA platform.\n\n | Platform Version | Add-on Version |\n|-----------------|----------------|\n| 7.2.x           | 1.7.0         |\n| 7.1.x           | 1.6.0         |\n| 7.0.x           | 1.5.0         |\n| 6.10.x          | 1.4.0         |\n| 6.9.x           | 1.3.0         |\n| 6.8.x           | 1.2.0         |\n| 6.7.x           | 1.1.0         |\n| 6.6.x           | 1.0.0         |\n\n5. Click *Install* and apply the changes. The add-on will be installed to your project.\n\n## Usage\n\nThis add-on contains components that implement Drag-and-Drop functionality. To handle a component's drop action, it is necessary to implement DropHandler interface that contains two methods:\n- `drop(DragAndDropEvent event)` - method that handles transferable component's drop;\n- `getCriterion()` - returns the AcceptCriterion that is used to evaluate whether the dragged component will be handed over to drop method.\n\nIn order to accept a Drop event, components must provide the `AcceptCriterion` object. `AcceptCriterion` interface has two main inheritors - `ServerSideCriterion` and `AcceptCriterionWrapper`. If you want to check an accept criterion on a browser side, you have to implement `AcceptCriterionWrapper` interface.\n\n### Quick Start\n\nAfter installation the add-on in your CUBA application follow the steps below:\n\n1. Create a simple screen.\n2. Add the following namespace in the XML descriptor of the screen:\n\n ```\nxmlns:dnd=\"http://schemas.haulmont.com/dnd/0.1/drag-and-drop.xsd\"\n```\n\n#### Sample Task\n\nLet's try to create a small todo-list app. It will provide a predefined set of todo-actions that can be added to the todo-list.\n\nIn this app we will implement the following features:\n - dragging components to the list;\n - reordering components in the list;\n - deleting components.\n\n#### Step-by-Step Guide\nCreate a new CUBA project and add the given add-on to it.\n\n![](/sreenshots/1-Adding-addon.png \"Adding add-on\")\n\nIn the GENERIC UI tab create a new screen using \"Blank screen\" template.\n\nWe will divide this screen into two panels: the first panel is a palette that contains actions, the second is a dashboard that contains the list of actions.\nFirst of all, add the next namespace to the XML descriptor of the screen:\n```\nxmlns:dnd=\"http://schemas.haulmont.com/dnd/0.1/drag-and-drop.xsd\"\n```\n\nand add the following block:\n```xml\n\u003clayout\u003e\n    \u003chbox id=\"root\"\n          height=\"100%\"\n          width=\"100%\"\n          spacing=\"true\"\n          expand=\"rootSpacer\"\u003e\n        \u003cgroupBox id=\"rootPalette\"\n                  caption=\"Todo actions\"\n                  height=\"100%\"\n                  width=\"200px\"\u003e\n            \u003cdnd:dndVBoxLayout id=\"palette\"\n                               dragMode=\"CLONE\"\n                               height=\"AUTO\"\n                               spacing=\"true\"\n                               width=\"100%\"\u003e\n            \u003c/dnd:dndVBoxLayout\u003e\n        \u003c/groupBox\u003e\n        \u003cgroupBox id=\"rootDashboard\"\n                  caption=\"Todo today\"\n                  width=\"500px\"\n                  height=\"100%\"\u003e\n            \u003cscrollBox width=\"100%\" height=\"100%\"\u003e\n                \u003cdnd:dndVBoxLayout id=\"dashboard\"\n                                   dragMode=\"CLONE\"\n                                   height=\"AUTO\"\n                                   spacing=\"true\"\n                                   width=\"100%\"\u003e\n                \u003c/dnd:dndVBoxLayout\u003e\n            \u003c/scrollBox\u003e\n        \u003c/groupBox\u003e\n        \u003clabel id=\"rootSpacer\"/\u003e\n    \u003c/hbox\u003e\n\u003c/layout\u003e\n```\nComponent with the \"rootPalette\" id contains the set of predefined actions.\nThese components can be dragged by users. Component with the \"dashboard\" id contains a todo-list.\n\nPalette and Dashboard have property `dragMode` which indicates that their components can be dragged.  \n\nThis is how the screen looks in the running app:\n\n![](/sreenshots/2-xml-without-buttons.png \"View of the screen in the running app\")\n\nNext, we should add actions that can be dragged by users and added in their list.\nWe will use buttons as the components for drag and drop.\nAdd a set of buttons to the palette with follow ids: call, chat, meeting, buy.\n\nYour XML descriptor should look like this:\n\n```xml\n\u003cdnd:dndVBoxLayout id=\"palette\"\n                   dragMode=\"CLONE\"\n                   height=\"AUTO\"\n                   spacing=\"true\"\n                   width=\"100%\"\u003e\n      \u003cbutton id=\"call\"\n              caption=\"Call\"\n              height=\"40px\"\n              width=\"100%\"/\u003e\n      \u003cbutton id=\"chat\"\n              caption=\"Chat\"\n              height=\"40px\"\n              width=\"100%\"/\u003e\n      \u003cbutton id=\"meeting\"\n              caption=\"Meeting\"\n              height=\"40px\"\n              width=\"100%\"/\u003e\n      \u003cbutton id=\"buy\"\n              caption=\"Buy\"\n              height=\"40px\"\n              width=\"100%\"/\u003e\n\u003c/dnd:dndVBoxLayout\u003e\n```\n\nMake sure that the buttons are added and they can be dragged:\n\n![](/sreenshots/3-xml-with-buttons.png \"View of the screen with draggable buttons\")\n\nIn the screen controller set `DropHandler` to the dashboard component:\n\n```Java\n@Inject\nprivate DDVerticalLayout dashboard;\n\n@Override\npublic void init(Map\u003cString, Object\u003e params) {\n    dashboard.setDropHandler(new DropHandler() {\n       @Override\n       public void drop(DragAndDropEvent event) {\n\n       }\n\n       @Override\n       public AcceptCriterion getCriterion() {\n           return AcceptCriterion.ACCEPT_ALL;\n       }\n   });\n}\n```\n\nThe methods `drop()` and `getCriterion()` are invoked every time when the dragged component is dropped to the layout.\nIn the `drop()` method we should implement our logic to handle component's drop.  \n\nMethod `getCriterion()` allows us to specify the criteria to accept dropping various components to this layout\n(e.g. only Labels and/or Buttons etc.). In this case we accept all components: `AcceptCriterion.ACCEPT_ALL`.\n\nIf we now run this app and try to drag a button to the dashboard, we will see that the dropping area is not highlighted.\nThis is due to the dashboard property height which is set to AUTO. To expand this layout we should write some CSS in the extension theme of the app.\nTo do this, we create a theme extension in Project Properties.\n\n![](/sreenshots/4-theme-extention.png \"Create extension theme\")  \n\nAdd the following code to the `halo-ext.scss`:\n\n```css\n.min-height{\n    min-height:50px;\n}\n```\n\nand in the XML descriptor of the screen add `stylename=\"min-height\"` to the dashboard component. Launch the app and check:\n\n![](/sreenshots/5-drop-area.png \"Drop area\")  \n\nNow we should create some panel to display the added action in the list. For this purpose, create the method that returns Component.\n\n```Java\npublic Component createDashboardElement(Component component) {\n\n}\n```\n\nFor the main component added directly to the dashboard we will use `GroupBoxLayout`. Then we will add `HBoxLayout` to the `GroupBoxLayout` and fill it with the components:\n- Label with appropriate serial number;\n- Label with name of action;\n- LookupField;\n- Button to remove from list.\n\nFirstly, create `GroupBoxLayout` and `HBoxLayout`, specify width 100% for both components and add spacing to `HBoxLayout`:\n\n```Java\nGroupBoxLayout groupBox = factory.create(GroupBoxLayout.class);\ngroupBox.setWidth(\"100%\");\n\nHBoxLayout layout = factory.create(HBoxLayout.class);\nlayout.setWidth(\"100%\");\nlayout.setSpacing(true);\n```\n\nNext, create main components for this panel. No need to set LookupField width as it will be expanded in HBoxLayout:\n\n```Java\nLabel countLabel = factory.create(Label.class);\ncountLabel.setId(\"countLabel\");\ncountLabel.setWidth(\"30px\");\n\nLabel titleLabel = factory.create(Label.class);\ntitleLabel.setValue(((Button) component).getCaption());\ntitleLabel.setWidth(\"60px\");\n\nLookupField lookupField = factory.create(LookupField.class);\n\nButton deleteButton = factory.create(Button.class);\ndeleteButton.setIcon(\"font-icon:TIMES\");\n```\n\nCreate `BaseAction` for the delete button. We'll implement the logic of deletion a bit later:\n\n```Java\nBaseAction action = new BaseAction(\"remove\") {\n    @Override\n    public void actionPerform(Component component) {\n    }\n};\naction.setCaption(\"\");\ndeleteButton.setAction(action);\n\n```\n\nIt remains to add the created components:\n\n```Java\nlayout.add(countLabel);\nlayout.add(titleLabel);\nlayout.add(lookupField);\nlayout.expand(lookupField);\nlayout.add(deleteButton);\ngroupBox.add(layout);\n```\n\nNow we should handle component's drop to the dashboard. `DragAndDropEvent` event variable contains information about which component is being dragged, its source layout, to which component, to which position etc. To get this information you should use the following classes:\n- LayoutBoundTransferable;\n- DDVerticalLayoutTargetDetails.\n\nIn the `drop()` method we get references to the objects `LayoutBoundTransferable` and `DDVerticalLayoutTargetDetails`.\n\n```java\nLayoutBoundTransferable t = (LayoutBoundTransferable) event.getTransferable();\nDDVerticalLayoutTargetDetails details = (DDVerticalLayoutTargetDetails) event.getTargetDetails();\n```\n\n`LayoutBoundTransferable` contains two main methods:\n- getSourceComponent() - return the layout from which transferable component was dragged;\n- getTransferableComponent() - return transferable component;\n\n```Java\nComponent sourceLayout = t.getSourceComponent();\nComponent tComponent = t.getTransferableComponent();\n```\n\n`DDVerticalLayoutTargetDetails` contains information about layout to drop the component to (receiving layout).\nThe main methods are:\n- `getTarget()` - return layout to which drop;\n- `getOverComponent()` - return child component of the target layout, to which drop transferable component;\n- `getDropLocation()` - return drop location (Middle, Top, Bottom);\n- `getOverIndex()` - return index of child component;\nGet references to the layout to drop the transferable component to and `VerticalDropLocation`:\n\n```Java\nDDVerticalLayout targetLayout = (DDVerticalLayout) details.getTarget();\nVerticalDropLocation loc = details.getDropLocation();\n```\n\nNext we should check whether transferable component is null. It is possible if we drag  highlighted html5 text, as it is not a component.\n\n```Java\nif (tComponent == null) {\n    return;\n}\n```\n\nDefine the index of component to which we drop (indexTo) and the index of transferable component in the target layout (indexFrom). The last index is necessary if the component is replaced in its layout. If a component is dragged from another layout, indexFrom will be -1.\n\n```Java\nint indexTo = details.getOverIndex();\nint indexFrom = targetLayout.indexOf(tComponent);\n```\n\nCreate reference to the object that will be added to the layout and check whether component is dragged from another layout, or it is just a reordering within the source layout;\n\n```Java\nComponent componentToAdd;\nif (sourceLayout == targetLayout) {\n\n} else {\n\n}\n```\n\nIf this condition is fulfilled, we should assign the componentToAdd reference to the dragged component. Then we should check whether the component has changed its position or not. If the component has not changed its location, then finish method.\n\n```Java\ncomponentToAdd = tComponent;\nif (indexFrom == indexTo) {\n    return;\n}\n```\n\nIf the component has changed its location, we must delete it from its layout. Then check if the index of the receiving component is bigger than the index of the previous location, then we need to reduce it by one. This is because of removing the component from layout.\n\n```Java\ntargetLayout.remove(tComponent);\nif (indexTo \u003e indexFrom) {\n    indexTo--;\n}\n```\n\nIf the dragged component does not hit any position, indexTo will be -1. Since the component has already been removed from the layout, it must be added to its previous position.\n\n```Java\nif (indexTo == -1) {\n    targetLayout.add(componentToAdd, indexFrom);\n}\n```\n\nIn case when component is dragged from another layout, it is necessary to create an appropriate component to display it in the dashboard. If the dragged component does not hit any of the components in the layout, we need to add it to the end of the list of layout components.\n\n```Java\nif (sourceLayout == targetLayout) {\n    // some code\n} else {\n    componentToAdd = createDashboardElement(tComponent);\n    if (indexTo == -1) {\n        targetLayout.add(componentToAdd, targetLayout.getOwnComponents().size());\n    }\n}\n```\n\nNext we should check if the component hits to the child component of layout. If it hits, we check the drop location.\nIf location is `MIDDLE` or `BOTTOM`, we must increase indexTo by one to add below a component to which the component was dragged.\n\n```java\nif (indexTo != -1) {\n    if (loc == VerticalDropLocation.MIDDLE || loc == VerticalDropLocation.BOTTOM) {\n        indexTo++;\n    }\n    targetLayout.add(componentToAdd, indexTo);\n}\n```\n\nAs a result, the drop method looks like:\n\n```Java\npublic void drop(DragAndDropEvent event) {\n    LayoutBoundTransferable t = (LayoutBoundTransferable) event.getTransferable();\n    DDVerticalLayoutTargetDetails details = (DDVerticalLayoutTargetDetails) event.getTargetDetails();\n\n    Component sourceLayout = t.getSourceComponent();\n    DDVerticalLayout targetLayout = (DDVerticalLayout) details.getTarget();\n    Component tComponent = t.getTransferableComponent();\n\n    VerticalDropLocation loc = details.getDropLocation();\n\n    int indexTo = details.getOverIndex();\n    int indexFrom = targetLayout.indexOf(tComponent);\n\n    if (tComponent == null) {\n        return;\n    }\n    Component componentToAdd;\n\n    if (sourceLayout == targetLayout) {\n        componentToAdd = tComponent;\n        if (indexFrom == indexTo) {\n            return;\n        }\n        targetLayout.remove(tComponent);\n        if (indexTo \u003e indexFrom) {\n            indexTo--;\n        }\n        if (indexTo == -1) {\n            targetLayout.add(tComponent, indexFrom);\n        }\n    } else {\n        componentToAdd = createDashboardElement(tComponent);\n        if (indexTo == -1) {\n            targetLayout.add(componentToAdd, targetLayout.getOwnComponents().size());\n        }\n    }\n\n    if (indexTo != -1) {\n        if (loc == VerticalDropLocation.MIDDLE || loc == VerticalDropLocation.BOTTOM) {\n            indexTo++;\n        }\n        targetLayout.add(componentToAdd, indexTo);\n    }\n}\n```\n\nLaunch the app and check:\n\n![](/sreenshots/6-check-adding.png \"Check adding components\")  \n\nNote that serial numbers are not assigned to dashboard components. Create the method to fix it:\n\n```Java\npublic void updateDashboardComponents(DDVerticalLayout layout) {\n\n}\n```\nWe need to set values to all countLabel. To achieve this, it is necessary to get the list of child dashboard components.\n\n```Java\nList\u003cComponent\u003e components = new ArrayList\u003c\u003e(layout.getOwnComponents());\nint count = 0;\nfor (Component component : components) {\n     GroupBoxLayout groupBox = (GroupBoxLayout) component;\n     HBoxLayout hBoxLayout = (HBoxLayout) Iterables.get(groupBox.getComponents(), 0);\n     Label label = (Label)  Iterables.get(hBoxLayout.getComponents(), 0);\n     label.setValue(++count);\n}\n```\n\nNow at the end of `drop()` method add the follow invocation:\n\n```Java\nupdateDashboardComponents(targetLayout);\n```\n\nIt remains to implement the component removing from the dashboard. Add this code to `actionPerfom()` method:\n\n```Java\npublic void actionPerform(Component component) {\n    HBoxLayout hBox = (HBoxLayout) component.getParent();\n    GroupBoxLayout groupBox = (GroupBoxLayout) hBox.getParent();\n    DDVerticalLayout ddLayout = (DDVerticalLayout) groupBox.getParent();\n    ddLayout.remove(groupBox);\n    updateDashboardComponents(ddLayout);\n}\n```\n\nLaunch the app and check that the serial numbers are updated and the components are removed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuba-platform%2Fcuba-dnd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcuba-platform%2Fcuba-dnd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuba-platform%2Fcuba-dnd/lists"}