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 onSave: (form: FormDefinition) => void;
|
||||||
private history: FormDefinition[] = [];
|
private history: FormDefinition[] = [];
|
||||||
private historyIndex = 0;
|
private historyIndex = 0;
|
||||||
|
private dragSourceIndex: number | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
app: App,
|
app: App,
|
||||||
|
|
@ -221,10 +222,17 @@ export class FormBuilderModal extends Modal {
|
||||||
for (let i = 0; i < this.draft.fields.length; i++) {
|
for (let i = 0; i < this.draft.fields.length; i++) {
|
||||||
const field = this.draft.fields[i];
|
const field = this.draft.fields[i];
|
||||||
const fieldEl = container.createDiv({ cls: 'ff-builder-field' });
|
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
|
// Field header with number, label preview, and action buttons
|
||||||
const headerEl = fieldEl.createDiv({ cls: 'ff-builder-field-header' });
|
const headerEl = fieldEl.createDiv({ cls: 'ff-builder-field-header' });
|
||||||
|
|
||||||
|
headerEl.createSpan({
|
||||||
|
cls: 'ff-drag-handle',
|
||||||
|
text: '\u2807',
|
||||||
|
});
|
||||||
|
|
||||||
headerEl.createSpan({
|
headerEl.createSpan({
|
||||||
cls: 'ff-builder-field-num',
|
cls: 'ff-builder-field-num',
|
||||||
text: `#${i + 1}`,
|
text: `#${i + 1}`,
|
||||||
|
|
@ -283,6 +291,57 @@ export class FormBuilderModal extends Modal {
|
||||||
this.render();
|
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
|
// Field body — settings
|
||||||
const bodyEl = fieldEl.createDiv({ cls: 'ff-builder-field-body' });
|
const bodyEl = fieldEl.createDiv({ cls: 'ff-builder-field-body' });
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue