feat: add drag & drop field reordering to form builder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
03c471a60b
commit
8b9140ae6c
1 changed files with 59 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ export class FormBuilderModal extends Modal {
|
|||
private onSave: (form: FormDefinition) => void;
|
||||
private history: FormDefinition[] = [];
|
||||
private historyIndex = 0;
|
||||
private dragSourceIndex: number | null = null;
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
|
|
@ -221,10 +222,17 @@ export class FormBuilderModal extends Modal {
|
|||
for (let i = 0; i < this.draft.fields.length; i++) {
|
||||
const field = this.draft.fields[i];
|
||||
const fieldEl = container.createDiv({ cls: 'ff-builder-field' });
|
||||
fieldEl.setAttribute('draggable', 'true');
|
||||
fieldEl.dataset.index = String(i);
|
||||
|
||||
// Field header with number, label preview, and action buttons
|
||||
const headerEl = fieldEl.createDiv({ cls: 'ff-builder-field-header' });
|
||||
|
||||
headerEl.createSpan({
|
||||
cls: 'ff-drag-handle',
|
||||
text: '\u2807',
|
||||
});
|
||||
|
||||
headerEl.createSpan({
|
||||
cls: 'ff-builder-field-num',
|
||||
text: `#${i + 1}`,
|
||||
|
|
@ -283,6 +291,57 @@ export class FormBuilderModal extends Modal {
|
|||
this.render();
|
||||
});
|
||||
|
||||
// --- Drag & Drop ---
|
||||
fieldEl.addEventListener('dragstart', (e: DragEvent) => {
|
||||
this.dragSourceIndex = i;
|
||||
fieldEl.addClass('ff-dragging');
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
});
|
||||
|
||||
fieldEl.addEventListener('dragend', () => {
|
||||
this.dragSourceIndex = null;
|
||||
fieldEl.removeClass('ff-dragging');
|
||||
});
|
||||
|
||||
fieldEl.addEventListener('dragover', (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
if (this.dragSourceIndex === null || this.dragSourceIndex === i) return;
|
||||
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
|
||||
|
||||
const rect = fieldEl.getBoundingClientRect();
|
||||
const midY = rect.top + rect.height / 2;
|
||||
fieldEl.removeClass('ff-drop-above', 'ff-drop-below');
|
||||
if (e.clientY < midY) {
|
||||
fieldEl.addClass('ff-drop-above');
|
||||
} else {
|
||||
fieldEl.addClass('ff-drop-below');
|
||||
}
|
||||
});
|
||||
|
||||
fieldEl.addEventListener('dragleave', () => {
|
||||
fieldEl.removeClass('ff-drop-above', 'ff-drop-below');
|
||||
});
|
||||
|
||||
fieldEl.addEventListener('drop', (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
fieldEl.removeClass('ff-drop-above', 'ff-drop-below');
|
||||
if (this.dragSourceIndex === null || this.dragSourceIndex === i) return;
|
||||
|
||||
const rect = fieldEl.getBoundingClientRect();
|
||||
const midY = rect.top + rect.height / 2;
|
||||
const insertBefore = e.clientY < midY;
|
||||
|
||||
this.pushSnapshot();
|
||||
const [moved] = this.draft.fields.splice(this.dragSourceIndex, 1);
|
||||
let targetIndex = insertBefore ? i : i + 1;
|
||||
if (this.dragSourceIndex < i) targetIndex--;
|
||||
this.draft.fields.splice(targetIndex, 0, moved);
|
||||
this.dragSourceIndex = null;
|
||||
this.render();
|
||||
});
|
||||
|
||||
// Field body — settings
|
||||
const bodyEl = fieldEl.createDiv({ cls: 'ff-builder-field-body' });
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue