Skip to content
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

feat(ui5-list,ui5-tree): make drag&drop feature public #8904

Merged
merged 5 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions packages/main/src/List.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,52 @@ type ListItemClickEventDetail = {
item: { type: HTMLElement },
},
})

/**
* Fired when a movable list item is moved over a potential drop target during a dragging operation.
*
* If the new position is valid, prevent the default action of the event using `preventDefault()`.
* @param {object} source Contains information about the moved element under `element` property.
* @param {object} destination Contains information about the destination of the moved element. Has `element` and `placement` properties.
* @public
* @since 2.0.0
* @allowPreventDefault
*/

@event<ListMoveEventDetail>("move-over", {
detail: {
/**
* @public
*/
source: { type: Object },
/**
* @public
*/
destination: { type: Object },
},
})

/**
* Fired when a movable list item is dropped onto a drop target.
*
* **Note:** `move` event is fired only if there was a preceding `move-over` with prevented default action.
* @param {object} source Contains information about the moved element under `element` property.
* @param {object} destination Contains information about the destination of the moved element. Has `element` and `placement` properties.
* @public
* @allowPreventDefault
*/
@event<ListMoveEventDetail>("move", {
detail: {
/**
* @public
*/
source: { type: Object },
/**
* @public
*/
destination: { type: Object },
},
})
class List extends UI5Element {
/**
* Defines the component header text.
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/ListItemBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class ListItemBase extends UI5Element implements ITabbable {
/**
* Defines whether the item is movable.
* @default false
* @private
* @public
* @since 2.0.0
yanaminkova marked this conversation as resolved.
Show resolved Hide resolved
*/
@property({ type: Boolean })
movable!: boolean;
Expand Down
8 changes: 7 additions & 1 deletion packages/website/docs/_components_pages/main/List/List.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Modes from "../../../_samples/main/List/Modes/Modes.md";
import NoData from "../../../_samples/main/List/NoData/NoData.md";
import GroupHeaders from "../../../_samples/main/List/GroupHeaders/GroupHeaders.md";
import SeparationTypes from "../../../_samples/main/List/SeparationTypes/SeparationTypes.md";
import DragAndDrop from "../../../_samples/main/List/DragAndDrop/DragAndDrop.md";

<%COMPONENT_OVERVIEW%>

Expand Down Expand Up @@ -48,4 +49,9 @@ The <b>The ListItemGroup</b> can be used to start group section and implement gr
### Separators
The <b>separators</b> options (<b>All</b>, <b>Inner</b>, <b>None</b>) allows the outer lines (Inner) or all the lines (None) to be hidden.

<SeparationTypes />
<SeparationTypes />

### Drag And Drop
The list items are draggable through the use of the <b>movable</b> property on <b>ListItem</b>.

<DragAndDrop />
6 changes: 6 additions & 0 deletions packages/website/docs/_components_pages/main/Tree/Tree.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ slug: ../../Tree

import Basic from "../../../_samples/main/Tree/Basic/Basic.md";
import CustomTreeItems from "../../../_samples/main/Tree/CustomTreeItems/CustomTreeItems.md";
import DragAndDrop from "../../../_samples/main/Tree/DragAndDrop/DragAndDrop.md";

<%COMPONENT_OVERVIEW%>

Expand All @@ -16,3 +17,8 @@ import CustomTreeItems from "../../../_samples/main/Tree/CustomTreeItems/CustomT

### Custom Tree Items
<CustomTreeItems />

### Drag And Drop
The tree items are draggable through the use of the <b>movable</b> property on <b>TreeItem</b>.

<DragAndDrop />
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

<Editor html={html} js={js} />
45 changes: 45 additions & 0 deletions packages/website/docs/_samples/main/List/DragAndDrop/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import "@ui5/webcomponents/dist/List.js";
import "@ui5/webcomponents/dist/StandardListItem.js";

import "@ui5/webcomponents-icons/dist/checklist-item.js";

const list = document.getElementById('listDnd1');
const handleBeforeItemMove = (e) => {
const { destination, source } = e.detail;

if (destination.placement === 'Before' || destination.placement === 'After') {
e.preventDefault();
}

};

const listHandleMoveOver = (e) => {
const { destination, source } = e.detail;

if (!list.contains(source.element)) {
return;
}

handleBeforeItemMove(e);
};

const handleMove = (e) => {
const { destination, source } = e.detail;
const parent = destination.element.closest('[ui5-list]');

if (destination.placement === 'Before') {
parent.insertBefore(source.element, destination.element);
} else if (destination.placement === 'After') {
const nextElement = Array.from(parent.children).at(
Array.from(parent.children).indexOf(destination.element) + 1
);

parent.insertBefore(source.element, nextElement);
} else if (destination.placement === 'On') {
destination.element.prepend(source.element);
}
};

list.addEventListener('ui5-move-over', listHandleMoveOver);
yanaminkova marked this conversation as resolved.
Show resolved Hide resolved
list.addEventListener('ui5-move', handleMove);

27 changes: 27 additions & 0 deletions packages/website/docs/_samples/main/List/DragAndDrop/sample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
</head>

<body style="background-color: var(--sapBackgroundColor)">
<!-- playground-fold-end -->

<ui5-list id="listDnd1">
<ui5-li icon="checklist-item" movable>Item #1</ui5-li>
<ui5-li icon="checklist-item" movable>Item #2</ui5-li>
<ui5-li icon="checklist-item" movable>Item #3</ui5-li>
<ui5-li icon="checklist-item" movable>Item #4</ui5-li>
<ui5-li icon="checklist-item" movable>Item #5</ui5-li>
</ui5-list>

<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

<Editor html={html} js={js} />
65 changes: 65 additions & 0 deletions packages/website/docs/_samples/main/Tree/DragAndDrop/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import "@ui5/webcomponents/dist/Tree.js";
import "@ui5/webcomponents/dist/TreeItem.js";
import "@ui5/webcomponents/dist/Title.js";
import "@ui5/webcomponents/dist/Label.js";


const tree1 = document.getElementById("tree");
const handleBeforeItemMove = (e) => {
const { destination, source } = e.detail;

if (destination.placement === "Before" || destination.placement === "After") {
e.preventDefault();
}

if (destination.placement === "On" && "allowsNesting" in destination.element.dataset) {
e.preventDefault();
}

console.log(`Moving "${source.element.text}" ${destination.placement.toLowerCase()} "${destination.element.text}"`);
};

const tree1HandleMoveOver = (e) => {
const { destination, source } = e.detail;

if (!tree1.contains(source.element)) {
return;
}

handleBeforeItemMove(e);
};

const handleMove = (e) => {
const { destination, source } = e.detail;
const parent = destination.element.parentNode.closest("[ui5-tree-item]") ||
destination.element.closest("[ui5-tree]");

if (destination.placement === "Before") {
parent.insertBefore(
source.element,
destination.element
);
} else if (destination.placement === "After") {
const nextElement = Array.from(parent.children).at(Array.from(parent.children).indexOf(destination.element) + 1);

parent.insertBefore(
source.element,
nextElement,
);
} else if (destination.placement === "On") {
destination.element.prepend(source.element);
}
};

tree1.addEventListener("ui5-move-over", tree1HandleMoveOver);
yanaminkova marked this conversation as resolved.
Show resolved Hide resolved
tree1.addEventListener("ui5-move", handleMove);

const densityCb = document.getElementById("density");
yanaminkova marked this conversation as resolved.
Show resolved Hide resolved
densityCb.addEventListener("ui5-change", e => {
document.body.classList.toggle("ui5-content-density-compact", e.target.checked);
});

const reorderCb = document.getElementById("reorder");
reorderCb.addEventListener("ui5-change", e => {
tree1.items.forEach((item) => item.movable = e.target.checked);
});
51 changes: 51 additions & 0 deletions packages/website/docs/_samples/main/Tree/DragAndDrop/sample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
</head>

<body style="background-color: var(--sapBackgroundColor)">
<!-- playground-fold-end -->
<ui5-tree id="tree" no-data-text="No data" mode="MultiSelect" accessible-name="Tree with accessibleName">

<ui5-tree-item movable text="Tree 1" icon="paste" additional-text="Available" indeterminate selected additional-text-state="Information" accessible-name="Tree item with accessibleName">
<ui5-title slot="content">
<ui5-label>Tree 1</ui5-label>
<ui5-label>Tree 1</ui5-label>
</ui5-title>

<ui5-tree-item movable expanded text="Tree 1.1" additional-text="Re-stock" additional-text-state="Negative" indeterminate selected>
<ui5-tree-item movable text="Tree 1.1.1" additional-text="Required" additional-text-state="Critical" selected></ui5-tree-item>
<ui5-tree-item movable text="Tree 1.1.2" additional-text="Available" additional-text-state="Positive"></ui5-tree-item>
</ui5-tree-item>
</ui5-tree-item>

<ui5-tree-item movable data-allows-nesting text="Tree 2(Allows Nesting)" icon="copy">
<ui5-tree-item movable id="firstCollapsedItem" text="Tree 2.1">
<ui5-tree-item movable text="Tree 2.1.1"></ui5-tree-item>
<ui5-tree-item movable text="Tree 2.1.2">
<ui5-tree-item movable text="Tree 2.1.2.1"></ui5-tree-item>
<ui5-tree-item movable text="Tree 2.1.2.2"></ui5-tree-item>
<ui5-tree-item movable text="Tree 2.1.2.3"></ui5-tree-item>
<ui5-tree-item movable text="Tree 2.1.2.5"></ui5-tree-item>
</ui5-tree-item>
</ui5-tree-item>
<ui5-tree-item movable text="Tree 2.2"></ui5-tree-item>
<ui5-tree-item movable text="Tree 2.3"></ui5-tree-item>
</ui5-tree-item>

<ui5-tree-item movable text="Tree 3 (no icon)">
</ui5-tree-item>

</ui5-tree>

<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->