Skip to content

Commit

Permalink
[UI] Improve drag-and-drop support in APL editor
Browse files Browse the repository at this point in the history
  • Loading branch information
hillerstorm committed Dec 21, 2024
1 parent e0bd514 commit 82b6b4c
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 14 deletions.
2 changes: 1 addition & 1 deletion ui/core/components/individual_sim_ui/apl_values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ export function valueListFieldConfig(field: string): AplHelpers.APLPickerBuilder
index: number,
config: ListItemPickerConfig<Player<any>, APLValue | undefined>,
) => new APLValuePicker(parent, player, config),
allowedActions: ['create', 'delete'],
allowedActions: ['copy', 'create', 'delete', 'move'],
actions: {
create: {
useIcon: true,
Expand Down
143 changes: 131 additions & 12 deletions ui/core/components/pickers/list_picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
const item: ItemPickerPair<ItemType> = { elem: itemContainer, picker: itemPicker, idx: index };

if (this.actionEnabled('move')) {
itemContainer.classList.add('draggable');
if (this.config.itemLabel) {
itemContainer.classList.add(this.config.itemLabel.toLowerCase().replace(' ', '-'));
}

const moveButton = ListPicker.makeActionElem('list-picker-item-move', 'fa-arrows-up-down');
itemHeader.appendChild(moveButton);

Expand All @@ -225,11 +230,42 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
moveButtonTooltip?.destroy();
});

moveButton.draggable = true;
moveButton.addEventListener(
'mouseenter',
() => {
moveButton.classList.add('hover');
},
{ signal: this.signal },
);

moveButton.addEventListener(
'mouseleave',
() => {
moveButton.classList.remove('hover');
},
{ signal: this.signal },
);

moveButton.addEventListener(
'mousedown',
() => {
itemContainer.setAttribute('draggable', 'true');
},
{ signal: this.signal },
);

moveButton.addEventListener(
'mouseup',
() => {
itemContainer.removeAttribute('draggable');
},
{ signal: this.signal },
);

itemContainer.addEventListener(
'dragstart',
event => {
if (event.target == moveButton) {
if (event.target == itemContainer) {
event.dataTransfer!.dropEffect = 'move';
event.dataTransfer!.effectAllowed = 'move';
itemContainer.classList.add('dragfrom');
Expand All @@ -246,10 +282,14 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
itemContainer.addEventListener(
'dragenter',
event => {
if (!curDragData || curDragData.listPicker != this) {
if (
!curDragData ||
curDragData.listPicker.config.itemLabel !== this.config.itemLabel ||
(this.config.itemLabel === 'Action' && curDragData.listPicker !== this)
) {
return;
}
event.preventDefault();
event.stopPropagation();
dragEnterCounter++;
itemContainer.classList.add('dragto');
},
Expand All @@ -259,7 +299,11 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
itemContainer.addEventListener(
'dragleave',
event => {
if (!curDragData || curDragData.listPicker != this) {
if (
!curDragData ||
curDragData.listPicker.config.itemLabel !== this.config.itemLabel ||
(this.config.itemLabel === 'Action' && curDragData.listPicker !== this)
) {
return;
}
event.preventDefault();
Expand All @@ -274,30 +318,73 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
itemContainer.addEventListener(
'dragover',
event => {
if (!curDragData || curDragData.listPicker != this) {
if (!curDragData || curDragData.listPicker.config.itemLabel !== this.config.itemLabel) {
return;
}
if (this.config.itemLabel === 'Action' && curDragData.listPicker !== this) {
event.dataTransfer!.dropEffect = 'none';
return;
}
event.dataTransfer!.dropEffect = 'move';
event.stopPropagation();
event.preventDefault();
},
{ signal: this.signal },
);

itemContainer.addEventListener(
'dragend',
event => {
if (
!curDragData ||
curDragData.listPicker.config.itemLabel !== this.config.itemLabel ||
(this.config.itemLabel === 'Action' && curDragData.listPicker !== this)
) {
return;
}
event.stopPropagation();
itemContainer.removeAttribute('draggable');
curDragData.item.elem.removeAttribute('draggable');
curDragData.item.elem.classList.remove('dragfrom');
[...document.querySelectorAll('.dragto,.hover')].forEach(elem => {
elem.classList.remove('dragto');
elem.classList.remove('hover');
});
curDragData = null;
},
{ signal: this.signal },
);

itemContainer.addEventListener(
'drop',
event => {
if (!curDragData || curDragData.listPicker != this) {
if (!curDragData || curDragData.listPicker.config.itemLabel !== this.config.itemLabel) {
return;
}
event.preventDefault();
dragEnterCounter = 0;
itemContainer.classList.remove('dragto');
event.stopPropagation();
itemContainer.removeAttribute('draggable');
curDragData.item.elem.removeAttribute('draggable');
curDragData.item.elem.classList.remove('dragfrom');
[...document.querySelectorAll('.dragto,.hover')].forEach(elem => {
elem.classList.remove('dragto');
elem.classList.remove('hover');
});

const srcIdx = curDragData.item.idx;
const dstIdx = index;
const newList = this.config.getValue(this.modObject);
const arrElem = newList[srcIdx];
newList.splice(srcIdx, 1);
let arrElem;

if (curDragData.listPicker !== this) {
const oldList = curDragData.listPicker.config.getValue(curDragData.listPicker.modObject);
arrElem = oldList[srcIdx];
oldList.splice(srcIdx, 1);
curDragData.listPicker.config.setValue(TypedEvent.nextEventID(), curDragData.listPicker.modObject, oldList);
} else {
arrElem = newList[srcIdx];
newList.splice(srcIdx, 1);
}

newList.splice(dstIdx, 0, arrElem);
this.config.setValue(TypedEvent.nextEventID(), this.modObject, newList);

Expand Down Expand Up @@ -326,6 +413,22 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
{ signal: this.signal },
);
this.addOnDisposeCallback(() => copyButtonTooltip?.destroy());

copyButton.addEventListener(
'mouseenter',
() => {
copyButton.classList.add('hover');
},
{ signal: this.signal },
);

copyButton.addEventListener(
'mouseleave',
() => {
copyButton.classList.remove('hover');
},
{ signal: this.signal },
);
}

if (this.actionEnabled('delete')) {
Expand All @@ -350,6 +453,22 @@ export class ListPicker<ModObject, ItemType> extends Input<ModObject, Array<Item
{ signal: this.signal },
);
this.addOnDisposeCallback(() => deleteButtonTooltip?.destroy());

deleteButton.addEventListener(
'mouseenter',
() => {
deleteButton.classList.add('hover');
},
{ signal: this.signal },
);

deleteButton.addEventListener(
'mouseleave',
() => {
deleteButton.classList.remove('hover');
},
{ signal: this.signal },
);
}
}

Expand Down
7 changes: 6 additions & 1 deletion ui/scss/core/components/_list_picker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
.list-picker-item-container {
border: var(--border-default);

&.draggable:has(> .list-picker-item-header .list-picker-item-move.hover),
&.draggable.value:has(> .list-picker-item-header .list-picker-item-copy.hover),
&.draggable.value:has(> .list-picker-item-header .list-picker-item-delete.hover) {
filter: drop-shadow(-1px 1px 1px rgba(255, 255, 255, 0.45)) drop-shadow(1px -1px 1px rgba(255, 255, 255, 0.45));
}

&:not(:last-child) {
margin-bottom: var(--spacer-3);
}
Expand All @@ -32,7 +38,6 @@
padding: 0;
border: 0;
margin: 0;
flex: 0;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

.list-picker-item-header {
align-items: flex-start;
justify-content: flex-end;
}

& > .list-picker-item {
Expand Down

0 comments on commit 82b6b4c

Please sign in to comment.