feat: drag-and-drop into groups/parent notes + fix ⋯ button styling
Drag-and-drop: - 3-zone drop indicator for groups and parent notes (files with children): top 25% = drop above, middle 50% = drop into, bottom 25% = drop below - 2-zone for regular files/separators/spacers (unchanged) - 'Drop into' shows background highlight instead of border line - Circular reference guard: can't drop a group into itself or descendants ⋯ button: - No visible button chrome when not hovered (no border, no background) - Subtle opacity (0.6) at rest, full opacity on hover - Hover shows text color change only, no background highlight
This commit is contained in:
+63
-20
@@ -390,7 +390,7 @@ export class WaypointView extends ItemView {
|
|||||||
});
|
});
|
||||||
rowEl.addEventListener('dragend', () => {
|
rowEl.addEventListener('dragend', () => {
|
||||||
this.dragId = null;
|
this.dragId = null;
|
||||||
container.querySelectorAll('.waypoint-bm-dragging, .waypoint-bm-drop-line, .waypoint-bm-drop-below').forEach(el => {
|
container.querySelectorAll('.waypoint-bm-dragging, .waypoint-bm-drop-line, .waypoint-bm-drop-below, .waypoint-bm-drop-into').forEach(el => {
|
||||||
el.removeClass('waypoint-bm-dragging');
|
el.removeClass('waypoint-bm-dragging');
|
||||||
el.removeClass('waypoint-bm-drop-line');
|
el.removeClass('waypoint-bm-drop-line');
|
||||||
el.removeClass('waypoint-bm-drop-below');
|
el.removeClass('waypoint-bm-drop-below');
|
||||||
@@ -399,16 +399,17 @@ export class WaypointView extends ItemView {
|
|||||||
rowEl.addEventListener('dragenter', (e) => {
|
rowEl.addEventListener('dragenter', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.dragId || this.dragId === item.id) return;
|
if (!this.dragId || this.dragId === item.id) return;
|
||||||
this.showDropIndicator(rowEl, e);
|
this.showDropIndicator(rowEl, e, false);
|
||||||
});
|
});
|
||||||
rowEl.addEventListener('dragover', (e) => {
|
rowEl.addEventListener('dragover', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.dragId || this.dragId === item.id) return;
|
if (!this.dragId || this.dragId === item.id) return;
|
||||||
this.showDropIndicator(rowEl, e);
|
this.showDropIndicator(rowEl, e, false);
|
||||||
});
|
});
|
||||||
rowEl.addEventListener('dragleave', () => {
|
rowEl.addEventListener('dragleave', () => {
|
||||||
rowEl.removeClass('waypoint-bm-drop-line');
|
rowEl.removeClass('waypoint-bm-drop-line');
|
||||||
rowEl.removeClass('waypoint-bm-drop-below');
|
rowEl.removeClass('waypoint-bm-drop-below');
|
||||||
|
rowEl.removeClass('waypoint-bm-drop-into');
|
||||||
});
|
});
|
||||||
rowEl.addEventListener('drop', (e) => {
|
rowEl.addEventListener('drop', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -448,7 +449,7 @@ export class WaypointView extends ItemView {
|
|||||||
});
|
});
|
||||||
rowEl.addEventListener('dragend', () => {
|
rowEl.addEventListener('dragend', () => {
|
||||||
this.dragId = null;
|
this.dragId = null;
|
||||||
container.querySelectorAll('.waypoint-bm-dragging, .waypoint-bm-drop-line, .waypoint-bm-drop-below').forEach(el => {
|
container.querySelectorAll('.waypoint-bm-dragging, .waypoint-bm-drop-line, .waypoint-bm-drop-below, .waypoint-bm-drop-into').forEach(el => {
|
||||||
el.removeClass('waypoint-bm-dragging');
|
el.removeClass('waypoint-bm-dragging');
|
||||||
el.removeClass('waypoint-bm-drop-line');
|
el.removeClass('waypoint-bm-drop-line');
|
||||||
el.removeClass('waypoint-bm-drop-below');
|
el.removeClass('waypoint-bm-drop-below');
|
||||||
@@ -457,16 +458,17 @@ export class WaypointView extends ItemView {
|
|||||||
rowEl.addEventListener('dragenter', (e) => {
|
rowEl.addEventListener('dragenter', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.dragId || this.dragId === item.id) return;
|
if (!this.dragId || this.dragId === item.id) return;
|
||||||
this.showDropIndicator(rowEl, e);
|
this.showDropIndicator(rowEl, e, false);
|
||||||
});
|
});
|
||||||
rowEl.addEventListener('dragover', (e) => {
|
rowEl.addEventListener('dragover', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.dragId || this.dragId === item.id) return;
|
if (!this.dragId || this.dragId === item.id) return;
|
||||||
this.showDropIndicator(rowEl, e);
|
this.showDropIndicator(rowEl, e, false);
|
||||||
});
|
});
|
||||||
rowEl.addEventListener('dragleave', () => {
|
rowEl.addEventListener('dragleave', () => {
|
||||||
rowEl.removeClass('waypoint-bm-drop-line');
|
rowEl.removeClass('waypoint-bm-drop-line');
|
||||||
rowEl.removeClass('waypoint-bm-drop-below');
|
rowEl.removeClass('waypoint-bm-drop-below');
|
||||||
|
rowEl.removeClass('waypoint-bm-drop-into');
|
||||||
});
|
});
|
||||||
rowEl.addEventListener('drop', (e) => {
|
rowEl.addEventListener('drop', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -507,30 +509,34 @@ export class WaypointView extends ItemView {
|
|||||||
rowEl.addClass('waypoint-bm-dragging');
|
rowEl.addClass('waypoint-bm-dragging');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const canAcceptChildren = isGroup || (item.children && item.children.length > 0);
|
||||||
|
|
||||||
rowEl.addEventListener('dragend', () => {
|
rowEl.addEventListener('dragend', () => {
|
||||||
this.dragId = null;
|
this.dragId = null;
|
||||||
container.querySelectorAll('.waypoint-bm-dragging, .waypoint-bm-drop-line, .waypoint-bm-drop-below').forEach(el => {
|
container.querySelectorAll('.waypoint-bm-dragging, .waypoint-bm-drop-line, .waypoint-bm-drop-below, .waypoint-bm-drop-into').forEach(el => {
|
||||||
el.removeClass('waypoint-bm-dragging');
|
el.removeClass('waypoint-bm-dragging');
|
||||||
el.removeClass('waypoint-bm-drop-line');
|
el.removeClass('waypoint-bm-drop-line');
|
||||||
el.removeClass('waypoint-bm-drop-below');
|
el.removeClass('waypoint-bm-drop-below');
|
||||||
|
el.removeClass('waypoint-bm-drop-into');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
rowEl.addEventListener('dragenter', (e) => {
|
rowEl.addEventListener('dragenter', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.dragId || this.dragId === item.id) return;
|
if (!this.dragId || this.dragId === item.id) return;
|
||||||
this.showDropIndicator(rowEl, e);
|
this.showDropIndicator(rowEl, e, canAcceptChildren);
|
||||||
});
|
});
|
||||||
|
|
||||||
rowEl.addEventListener('dragover', (e) => {
|
rowEl.addEventListener('dragover', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.dragId || this.dragId === item.id) return;
|
if (!this.dragId || this.dragId === item.id) return;
|
||||||
this.showDropIndicator(rowEl, e);
|
this.showDropIndicator(rowEl, e, canAcceptChildren);
|
||||||
});
|
});
|
||||||
|
|
||||||
rowEl.addEventListener('dragleave', () => {
|
rowEl.addEventListener('dragleave', () => {
|
||||||
rowEl.removeClass('waypoint-bm-drop-line');
|
rowEl.removeClass('waypoint-bm-drop-line');
|
||||||
rowEl.removeClass('waypoint-bm-drop-below');
|
rowEl.removeClass('waypoint-bm-drop-below');
|
||||||
|
rowEl.removeClass('waypoint-bm-drop-into');
|
||||||
});
|
});
|
||||||
|
|
||||||
rowEl.addEventListener('drop', (e) => {
|
rowEl.addEventListener('drop', (e) => {
|
||||||
@@ -538,12 +544,18 @@ export class WaypointView extends ItemView {
|
|||||||
this.dragId = null;
|
this.dragId = null;
|
||||||
rowEl.removeClass('waypoint-bm-drop-line');
|
rowEl.removeClass('waypoint-bm-drop-line');
|
||||||
rowEl.removeClass('waypoint-bm-drop-below');
|
rowEl.removeClass('waypoint-bm-drop-below');
|
||||||
|
rowEl.removeClass('waypoint-bm-drop-into');
|
||||||
|
|
||||||
const draggedId = e.dataTransfer?.getData('text/plain');
|
const draggedId = e.dataTransfer?.getData('text/plain');
|
||||||
if (!draggedId || draggedId === item.id) return;
|
if (!draggedId || draggedId === item.id) return;
|
||||||
|
|
||||||
const dropAbove = (rowEl as any).__dropAbove;
|
const dropInto = (rowEl as any).__dropInto;
|
||||||
this.moveBookmarkToPosition(draggedId, item.id, dropAbove);
|
if (dropInto && canAcceptChildren) {
|
||||||
|
this.moveBookmarkToGroup(draggedId, item.id);
|
||||||
|
} else {
|
||||||
|
const dropAbove = (rowEl as any).__dropAbove;
|
||||||
|
this.moveBookmarkToPosition(draggedId, item.id, dropAbove);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Group: chevron + icon + label ──
|
// ── Group: chevron + icon + label ──
|
||||||
@@ -808,25 +820,47 @@ export class WaypointView extends ItemView {
|
|||||||
menu.showAtPosition({ x: event.clientX, y: event.clientY });
|
menu.showAtPosition({ x: event.clientX, y: event.clientY });
|
||||||
}
|
}
|
||||||
|
|
||||||
private showDropIndicator(el: HTMLElement, e: MouseEvent): void {
|
private showDropIndicator(el: HTMLElement, e: MouseEvent, isGroupLike: boolean): void {
|
||||||
// Clear indicator from all siblings first
|
// Clear all indicators
|
||||||
const parent = el.parentElement;
|
const parent = el.parentElement;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent.querySelectorAll('.waypoint-bm-drop-line, .waypoint-bm-drop-below').forEach(el2 => {
|
parent.querySelectorAll('.waypoint-bm-drop-line, .waypoint-bm-drop-below, .waypoint-bm-drop-into').forEach(el2 => {
|
||||||
el2.removeClass('waypoint-bm-drop-line');
|
el2.removeClass('waypoint-bm-drop-line');
|
||||||
el2.removeClass('waypoint-bm-drop-below');
|
el2.removeClass('waypoint-bm-drop-below');
|
||||||
|
el2.removeClass('waypoint-bm-drop-into');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const rect = el.getBoundingClientRect();
|
const rect = el.getBoundingClientRect();
|
||||||
const midY = rect.top + rect.height / 2;
|
const y = e.clientY;
|
||||||
const above = e.clientY < midY;
|
|
||||||
|
|
||||||
el.addClass('waypoint-bm-drop-line');
|
if (isGroupLike) {
|
||||||
if (!above) {
|
// 3-zone: top 25% = above, middle 50% = into, bottom 25% = below
|
||||||
el.addClass('waypoint-bm-drop-below');
|
const topThreshold = rect.top + rect.height * 0.25;
|
||||||
|
const bottomThreshold = rect.top + rect.height * 0.75;
|
||||||
|
|
||||||
|
if (y < topThreshold) {
|
||||||
|
el.addClass('waypoint-bm-drop-line');
|
||||||
|
(el as any).__dropAbove = true;
|
||||||
|
(el as any).__dropInto = false;
|
||||||
|
} else if (y > bottomThreshold) {
|
||||||
|
el.addClass('waypoint-bm-drop-line');
|
||||||
|
el.addClass('waypoint-bm-drop-below');
|
||||||
|
(el as any).__dropAbove = false;
|
||||||
|
(el as any).__dropInto = false;
|
||||||
|
} else {
|
||||||
|
el.addClass('waypoint-bm-drop-into');
|
||||||
|
(el as any).__dropAbove = false;
|
||||||
|
(el as any).__dropInto = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 2-zone: top half = above, bottom half = below
|
||||||
|
const above = y < rect.top + rect.height / 2;
|
||||||
|
el.addClass('waypoint-bm-drop-line');
|
||||||
|
if (!above) el.addClass('waypoint-bm-drop-below');
|
||||||
|
(el as any).__dropAbove = above;
|
||||||
|
(el as any).__dropInto = false;
|
||||||
}
|
}
|
||||||
(el as any).__dropAbove = above;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveBookmarkToGroup(itemId: string, targetGroupId: string | null): void {
|
private moveBookmarkToGroup(itemId: string, targetGroupId: string | null): void {
|
||||||
@@ -846,6 +880,15 @@ export class WaypointView extends ItemView {
|
|||||||
const item = removeRecursive(this.plugin.waypointData.bookmarks);
|
const item = removeRecursive(this.plugin.waypointData.bookmarks);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
|
// Prevent dropping into self or own descendant
|
||||||
|
if (targetGroupId) {
|
||||||
|
const isDescendant = (parent: BookmarkItem, targetId: string): boolean => {
|
||||||
|
if (parent.id === targetId) return true;
|
||||||
|
return parent.children.some(c => isDescendant(c, targetId));
|
||||||
|
};
|
||||||
|
if (item.id === targetGroupId || isDescendant(item, targetGroupId)) return;
|
||||||
|
}
|
||||||
|
|
||||||
if (targetGroupId) {
|
if (targetGroupId) {
|
||||||
const findGroup = (items: BookmarkItem[]): BookmarkItem | null => {
|
const findGroup = (items: BookmarkItem[]): BookmarkItem | null => {
|
||||||
for (const i of items) {
|
for (const i of items) {
|
||||||
|
|||||||
+10
-2
@@ -44,11 +44,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: color 80ms, background 80ms;
|
transition: color 80ms, background 80ms;
|
||||||
|
box-shadow: none;
|
||||||
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.waypoint-header-more:hover {
|
.waypoint-header-more:hover {
|
||||||
color: var(--text-accent);
|
color: var(--text-normal);
|
||||||
background-color: var(--background-modifier-active-hover);
|
background: none;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Calendar ── */
|
/* ── Calendar ── */
|
||||||
@@ -287,6 +290,11 @@
|
|||||||
border-bottom: 3px solid var(--text-accent) !important;
|
border-bottom: 3px solid var(--text-accent) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.waypoint-favorites .waypoint-bm-drop-into {
|
||||||
|
background-color: var(--background-modifier-active-hover) !important;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Separator & Spacer ── */
|
/* ── Separator & Spacer ── */
|
||||||
|
|
||||||
.waypoint-favorites .waypoint-bookmark-item.waypoint-bookmark-separator {
|
.waypoint-favorites .waypoint-bookmark-item.waypoint-bookmark-separator {
|
||||||
|
|||||||
Reference in New Issue
Block a user