Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions src/components/EncodingPanel/EncodingShelf.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import type { EncodingChannel, DetectedField, FieldType } from '../../types';
import type { EncodingChannel, FieldType, DragPayload } from '../../types';
import { useApp } from '../../context/AppContext';

const TYPE_COLORS: Record<FieldType, string> = {
Expand All @@ -25,12 +25,12 @@ export function EncodingShelf({ channel, label }: EncodingShelfProps) {
const { state, assignField, removeField } = useApp();
const [isOver, setIsOver] = useState(false);
const [isHoveredRemove, setIsHoveredRemove] = useState(false);
const [isDragging, setIsDragging] = useState(false);

const assignedField = state.encodings[channel];

const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
setIsOver(true);
};

Expand All @@ -42,11 +42,46 @@ export function EncodingShelf({ channel, label }: EncodingShelfProps) {
e.preventDefault();
setIsOver(false);

const fieldData = e.dataTransfer.getData('application/json');
if (fieldData) {
const field = JSON.parse(fieldData) as DetectedField;
assignField(channel, field);
const data = e.dataTransfer.getData('application/json');
if (!data) return;

const payload = JSON.parse(data) as DragPayload;

// Dropping on same channel - no-op
if (payload.sourceType === 'encodingShelf' && payload.sourceChannel === channel) {
return;
}

// Move from another encoding shelf
if (payload.sourceType === 'encodingShelf' && payload.sourceChannel) {
const targetField = assignedField;

if (targetField) {
// Swap: move target field to source channel
assignField(payload.sourceChannel, targetField);
} else {
// No field in target - just remove from source
removeField(payload.sourceChannel);
}
}

assignField(channel, payload.field);
};

const handlePillDragStart = (e: React.DragEvent) => {
if (!assignedField) return;
const payload: DragPayload = {
field: assignedField,
sourceType: 'encodingShelf',
sourceChannel: channel
};
e.dataTransfer.setData('application/json', JSON.stringify(payload));
e.dataTransfer.effectAllowed = 'move';
setIsDragging(true);
};

const handlePillDragEnd = () => {
setIsDragging(false);
};

const handleRemove = () => {
Expand Down Expand Up @@ -115,6 +150,9 @@ export function EncodingShelf({ channel, label }: EncodingShelfProps) {
>
{assignedField ? (
<div
draggable
onDragStart={handlePillDragStart}
onDragEnd={handlePillDragEnd}
style={{
display: 'flex',
alignItems: 'center',
Expand All @@ -125,6 +163,9 @@ export function EncodingShelf({ channel, label }: EncodingShelfProps) {
border: '1px solid var(--color-border)',
borderRadius: '6px',
animation: 'fadeIn 0.2s ease-out',
cursor: isDragging ? 'grabbing' : 'grab',
opacity: isDragging ? 0.5 : 1,
transition: 'opacity 0.2s ease',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
Expand Down
5 changes: 3 additions & 2 deletions src/components/FieldList/FieldPill.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import type { DetectedField, FieldType } from '../../types';
import type { DetectedField, FieldType, DragPayload } from '../../types';
import { useApp } from '../../context/AppContext';

const TYPE_COLORS: Record<FieldType, string> = {
Expand Down Expand Up @@ -27,7 +27,8 @@ export function FieldPill({ field, index }: FieldPillProps) {
const [isDragging, setIsDragging] = useState(false);

const handleDragStart = (e: React.DragEvent) => {
e.dataTransfer.setData('application/json', JSON.stringify(field));
const payload: DragPayload = { field, sourceType: 'fieldList' };
e.dataTransfer.setData('application/json', JSON.stringify(payload));
e.dataTransfer.effectAllowed = 'copy';
setIsDragging(true);
};
Expand Down
6 changes: 6 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export interface DetectedField {

export type EncodingChannel = 'x' | 'y' | 'color' | 'size' | 'shape' | 'row' | 'column';

export interface DragPayload {
field: DetectedField;
sourceType: 'fieldList' | 'encodingShelf';
sourceChannel?: EncodingChannel;
}

export type EncodingState = {
[K in EncodingChannel]?: DetectedField;
};
Expand Down