Skip to content

Commit

Permalink
extract dropindicator
Browse files Browse the repository at this point in the history
  • Loading branch information
georgimkv committed Dec 22, 2023
1 parent e0b0aad commit c241625
Show file tree
Hide file tree
Showing 13 changed files with 436 additions and 159 deletions.
1 change: 1 addition & 0 deletions packages/main/src/DropIndicator.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div></div>
145 changes: 145 additions & 0 deletions packages/main/src/DropIndicator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import DropPlacement from "./types/DropPlacement.js";
import Orientation from "./types/Orientation.js";

import DropIndicatorTemplate from "./generated/templates/DropIndicatorTemplate.lit.js";

// Styles
import DropIndicatorCss from "./generated/themes/DropIndicator.css.js";

/**
* @class
*
* <h3 class="comment-api-title">Overview</h3>
*
*
* <h3>Usage</h3>
*
* For the <code>ui5-drop-indicator</code>
* <h3>ES6 Module Import</h3>
*
* <code>import @ui5/webcomponents/dist/DropIndicator.js";</code>
*
* @constructor
* @extends UI5Element
* @private
*/
@customElement({
tag: "ui5-drop-indicator",
renderer: litRender,
styles: DropIndicatorCss,
template: DropIndicatorTemplate,
dependencies: [],
})
class DropIndicator extends UI5Element {
/**
* Defines the placement of the indicator relative to the item.
*
* @default "Between"
* @public
*/
@property({ type: DropPlacement, defaultValue: DropPlacement.Between })
placement!: `${DropPlacement}`;

/**
* Defines the placement of the indicator relative to the item.
*
* @default "Vertical"
* @public
*/
@property({ type: Orientation, defaultValue: Orientation.Vertical })
orientation!: `${Orientation}`;

/**
* Defines the id of the element where the drop indicator will be shown.
*
* @default ""
* @public
*/
@property()
target!: string;

@property({ type: Object, defaultValue: undefined })
_owner!: Node;

// /**
// * Sets the position of the indicator.
// *
// * @private
// */
// @property({ validator: Integer, defaultValue: 0, noAttribute: true })
// position!: number;

get _positionProperty() {
if (this.orientation === Orientation.Vertical) {
return "left";
}

return "top";
}

onAfterRendering() {
if (!this.target) {
return;
}

const container = this._owner as HTMLElement;
const element = container.querySelector(`[id="${this.target}"`);
if (!element) {
return;
}

const {
left, width, right, top, bottom, height,
} = element.getBoundingClientRect();

const {
top: containerTop,
left: containerLeft,
} = container.getBoundingClientRect();

let position = 0;
if (this.orientation === Orientation.Vertical) {
switch (this.placement) {
case DropPlacement.Before:
position = left;
break;
case DropPlacement.On:
position = left + width / 2;
break;
case DropPlacement.After:
position = right;
break;
}

position -= containerLeft;
}

if (this.orientation === Orientation.Horizontal) {
switch (this.placement) {
case DropPlacement.Before:
position = top;
break;
case DropPlacement.On:
position = top + height / 2;
break;
case DropPlacement.After:
position = bottom;
break;
}

position -= containerTop;
}

const positionValue = position ? `${position}px` : "";
this.style[this._positionProperty] = positionValue;
console.log(this.style[this._positionProperty]);
}
}

DropIndicator.define();

export default DropIndicator;
6 changes: 6 additions & 0 deletions packages/main/src/List.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
@focusin="{{_onfocusin}}"
@keydown="{{_onkeydown}}"
@drop="{{_ondrop}}"
@dragend="{{_ondragendorleave}}"
@dragleave="{{_ondragendorleave}}"
@dragover="{{_ondragover}}"
@ui5-_press={{onItemPress}}
@ui5-close={{onItemClose}}
Expand Down Expand Up @@ -78,6 +80,10 @@
></ui5-busy-indicator>
</div>
{{/if}}

{{#if _reorderItems}}
<ui5-drop-indicator orientation="Horizontal" placement="Between" ._owner="{{this}}"></ui5-drop-indicator>
{{/if}}
</div>


Expand Down
98 changes: 41 additions & 57 deletions packages/main/src/List.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ import type {
} from "./ListItem.js";
import ListSeparators from "./types/ListSeparators.js";
import BusyIndicator from "./BusyIndicator.js";
import DropIndicator from "./DropIndicator.js";
import Orientation from "./types/Orientation.js";
import { getElementAtCoordinate } from "./util/DragAndDropHelper.js";

// Template
import ListTemplate from "./generated/templates/ListTemplate.lit.js";
Expand Down Expand Up @@ -158,7 +161,10 @@ type ListItemClickEventDetail = {
renderer: litRender,
template: ListTemplate,
styles: [browserScrollbarCSS, listCss],
dependencies: [BusyIndicator],
dependencies: [
BusyIndicator,
DropIndicator,
],
})
/**
* Fired when an item is activated, unless the item's <code>type</code> property
Expand Down Expand Up @@ -1159,13 +1165,39 @@ class List extends UI5Element {
return this.growingIntersectionObserver;
}

_ondragendorleave() {
// this doesn't get rerendered when a list is in a popover, maybe similar to issue #7681
this.dropIndicatorDOM.target = "";
}

_ondragover(e: DragEvent) {
const dropIndicator = this.dropIndicatorDOM;
const draggedElement = (e.target as ListItemBase)?.closest("[ui5-li-custom]");
if (!draggedElement) {
dropIndicator.target = "";
return;
}

// the item past this point qualifies to be dropped.
// calling prevent default allows the drop event to fire later
e.preventDefault();

const found = getElementAtCoordinate(
this.items,
e.clientY,
Orientation.Vertical,
);

if (!found) {
return;
}

// draw drop indicator
dropIndicator.target = found.closestElement.id;
dropIndicator.placement = found.dropPlacement;
}

_ondrop(e: DragEvent) {
// this.dropIndicatorDOM.style.left = "";

if (!e.dataTransfer) {
return;
}
Expand All @@ -1174,13 +1206,13 @@ class List extends UI5Element {
const droppedItemIndex = this.items.findIndex(item => item.id === id);
const droppedItem = this.items[droppedItemIndex];
if (!droppedItem) {
console.warn("didnt find dropped tab in this tabcontainer");
return;
}

const result = this._findItemAtCoordinates(
e.clientX,
const result = getElementAtCoordinate(
this.items,
e.clientY,
Orientation.Vertical,
);

if (!result) {
Expand All @@ -1194,61 +1226,13 @@ class List extends UI5Element {
},
destination: {
element: this,
index: this.items.indexOf(result.closestItem),
index: this.items.indexOf(result.closestElement as ListItemBase),
},
});
}

_findItemAtCoordinates(x: number, y: number) {
let closestOffset = Number.NEGATIVE_INFINITY,
closestItem: Element | null = null;

// determine which item is most closest to x
for (let i = 0; i < this.items.length; i++) {
const item = this.items[i],
{ top, height } = item.getBoundingClientRect(),
offset = y - top - height / 2;

if (offset <= 0 && offset > closestOffset) {
closestOffset = offset;
closestItem = item;
}
}

if (!closestItem) {
return null;
}

const { top, /* width, */ bottom } = closestItem.getBoundingClientRect(),
distanceToTopBorder = Math.abs(y - top),
distanceToBottomBorder = Math.abs(top - y);

const smallestDistance = Math.min(
distanceToTopBorder,
distanceToBottomBorder,
);

let dropIndicatorY = Number.NEGATIVE_INFINITY;
switch (smallestDistance) {
case distanceToTopBorder:
dropIndicatorY = top;
break;
case distanceToBottomBorder:
dropIndicatorY = bottom;
break;
}

return {
closestItem: closestItem as ListItemBase,
dropIndicator: {
y: dropIndicatorY,
type: "Between",
},
};
}

get dropIndicatorDOM(): HTMLElement {
return this.shadowRoot!.querySelector(".ui5-drop-indicator")!;
get dropIndicatorDOM(): DropIndicator {
return this.shadowRoot!.querySelector("[ui5-drop-indicator]")!;
}
}

Expand Down
5 changes: 0 additions & 5 deletions packages/main/src/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,17 +481,12 @@ class Tab extends UI5Element implements ITab, ITabbable {
}

const draggedTabInStrip = e.target as ITab;
// draggedTabInStrip.getTabInStripDomRef()!.setAttribute("data-ui5-dragging", "true");

e.dataTransfer.clearData();
e.dataTransfer.setData("text/plain", `${draggedTabInStrip.id}`);
e.dataTransfer.dropEffect = "move";
}

_onTabDragEnd(e: DragEvent) {
// e.target!.getTabInStripDomRef()!.removeAttribute("data-ui5-dragging");
}

get overflowState() {
return (this.disabled || this.isSingleClickArea) ? ListItemType.Inactive : ListItemType.Active;
}
Expand Down
7 changes: 3 additions & 4 deletions packages/main/src/TabContainer.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
@focusin="{{_onHeaderFocusin}}"
@drop="{{_onHeaderDrop}}"
@dragover="{{_onHeaderDragOver}}"
{{!-- @dragenter="{{_onHeaderDragEnter}}"
@dragend="{{_onHeaderDragEnd}}"
@dragleave="{{_onHeaderDragLeave}}" --}}
@dragleave="{{_onHeaderDragEndOrLeave}}"
@dragend="{{_onHeaderDragEndOrLeave}}"
part="tabstrip"
>
<div
Expand Down Expand Up @@ -75,7 +74,7 @@
{{/if}}

{{#if reorderTabs}}
<div class="ui5-drop-indicator" data-ui5-indicator-orientation="Vertical"></div>
<ui5-drop-indicator orientation="Vertical"></ui5-drop-indicator>
{{/if}}
</div>

Expand Down
Loading

0 comments on commit c241625

Please sign in to comment.