dnd functionality works.
This commit is contained in:
73
src/store/prompt-dnd.test.tsx
Normal file
73
src/store/prompt-dnd.test.tsx
Normal 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
83
src/store/prompt-dnd.tsx
Normal 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();
|
||||
}
|
Reference in New Issue
Block a user