dnd functionality works.

This commit is contained in:
Jordan
2024-02-29 09:11:13 -08:00
parent d9c1282d99
commit ee266ea372
13 changed files with 486 additions and 50 deletions

View File

@ -0,0 +1,73 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { $dragDropState, startDrag, startHoverOver, endHoverOver, isPromptItemDragSource, isPromptItemDropCandidate, isPromptItemDropTarget, completeDrop } from "./prompt-dnd";
import { Category, Library, LibraryItem, Nugget } from "../lib/prompt";
import { v4 as uuid4 } from "uuid";
describe("drag and drop", () => {
beforeEach(() => {
$dragDropState.set({});
});
const source = {
id: uuid4(),
score: 0,
item: {
id: uuid4(),
prompt: "zany",
category: Category.vibes,
} as LibraryItem
} as Nugget;
const target = {
id: uuid4(),
score: 0,
item: {
id: uuid4(),
prompt: "wild",
category: Category.vibes,
} as LibraryItem
} as Nugget;
it("should start drag", () => {
startDrag(source);
expect($dragDropState.get().currentSourceId).toEqual(source.id);
});
it("should start hover over", () => {
startHoverOver(target);
expect($dragDropState.get().currentDropCandidateId).toEqual(target.id);
});
it("should end hover over", () => {
endHoverOver();
expect($dragDropState.get().currentDropCandidateId).toEqual(null);
});
it("should check if item is a drag source", () => {
startDrag(source)
expect(isPromptItemDragSource($dragDropState, source)).toBeTruthy();
});
it("should check if item is a drop candidate", () => {
startDrag(source);
startHoverOver(target);
expect(isPromptItemDropCandidate($dragDropState, target)).toBeTruthy();
});
it("should check if item is a drop target", () => {
startDrag(source);
expect(isPromptItemDropTarget($dragDropState, target)).toBeTruthy();
});
it("should complete drop", () => {
startDrag(source);
startHoverOver(target);
endHoverOver();
completeDrop();
expect($dragDropState.get().currentSourceId).toBeFalsy();
expect($dragDropState.get().currentDropCandidateId).toBeFalsy();
});
});

83
src/store/prompt-dnd.tsx Normal file
View File

@ -0,0 +1,83 @@
import { Atom, atom, computed } from "nanostores"
import { $composition, Nugget, Operation, PromptItem, addToOperation, itemIsNugget, itemIsOperation, lassoNuggets } from "../lib/prompt";
import { Op } from "../lib/operator";
export type DragDropState = {
currentSourceId?: string | null,
currentDropCandidateId?: string | null,
}
export const $dragDropState = atom<DragDropState>({});
$dragDropState.subscribe((value) => {
console.log("drag-n-drop: %x", value);
})
export const $sourceItem = computed($dragDropState, (dnd) => {
const comp = $composition.get()
return comp.find(i => i.id === dnd.currentSourceId);
});
export const $dropCandidate = computed($dragDropState, (dnd) => {
const comp = $composition.get()
return comp.find(i => i.id === dnd.currentDropCandidateId);
});
export function startDrag(item: PromptItem) {
$dragDropState.set({ ...$dragDropState.get(), currentSourceId: item.id });
}
export function startHoverOver(item: PromptItem) {
$dragDropState.set({ ...$dragDropState.get(), currentDropCandidateId: item.id });
}
export function endHoverOver() {
$dragDropState.set({ ...$dragDropState.get(), currentDropCandidateId: null })
}
export const $isDragInProgress = computed($dragDropState, (dragDropState) => true);
export function isPromptItemDragSource($dds: Atom<DragDropState>, promptItem: PromptItem) {
return $dds.get().currentSourceId === promptItem.id;
}
export function isPromptItemDropTarget($dds: Atom<DragDropState>, promptItem: PromptItem) {
return $dds.get().currentSourceId && $dds.get().currentSourceId !== promptItem.id;
}
export function isPromptItemDropCandidate($dds: Atom<DragDropState>, promptItem: PromptItem) {
return $dds.get().currentDropCandidateId === promptItem.id;
}
export function cancelDrop() {
$dragDropState.set({});
};
export function completeDrop() {
const source = $sourceItem.get();
const target = $dropCandidate.get();
if (!(source && target)) return;
if (itemIsOperation(target)) {
const nSource = source as Nugget;
const nTarget = target as Operation;
const c1 = nSource.item.category;
const c2 = nTarget.items[0].item.category;
if (c1 != c2) {
console.error("Category mismatch: cannot drop a %s into %s", c1, c2);
} else {
addToOperation(source.id, target.id);
}
}
else if (itemIsNugget(target) && itemIsNugget(source)) {
const nTarget = target as Nugget;
const nSource = source as Nugget;
const c1 = nSource.item.category;
const c2 = nTarget.item.category;
if (c1 != c2) {
console.error("Category mismatch: cannot drop a %s into %s", c1, c2);
} else {
lassoNuggets(source.id, target.id, Op.AND)
}
}
cancelDrop();
}