Skip to content

Commit 4139d58

Browse files
committed
fix: Fixed empty dropabable element
1 parent 1134f88 commit 4139d58

7 files changed

Lines changed: 137 additions & 77 deletions

File tree

.vscode/launch.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "chrome",
9+
"request": "launch",
10+
"name": "Launch Chrome against localhost",
11+
"url": "http://localhost:3000",
12+
"webRoot": "${workspaceFolder}"
13+
}
14+
]
15+
}

src/app/page.tsx

Lines changed: 12 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -35,68 +35,10 @@ import { MobileNotification } from "@/components/form-builder/ui/mobile-notifica
3535
import { useIsMobile } from "@/hooks/use-mobile";
3636
import SocialLinks from "@/components/form-builder/sidebar/socialLinks";
3737
import { OpenJsonDialog } from "@/components/form-builder/dialogs/open-json-dialog";
38-
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
3938
import { useForm } from "react-hook-form";
40-
import { FormComponentModel } from "@/models/FormComponent";
41-
import { Viewports } from "@/types/form-builder.types";
39+
import { getGridRows, updateColSpans } from "@/lib/utils";
4240
import { RenderEditorComponent } from "@/components/form-builder/helpers/render-editor-component";
43-
44-
const getGridRows = (
45-
items: FormComponentModel[],
46-
viewport: Viewports
47-
): FormComponentModel[][] => {
48-
const rows: FormComponentModel[][] = [];
49-
let currentRow: FormComponentModel[] = [];
50-
let currentRowSpan = 0;
51-
52-
items.forEach((item) => {
53-
const colSpan = +item.getField("properties.style.colSpan", viewport) || 12;
54-
55-
// If adding this item would exceed 12 columns, start a new row
56-
if (currentRowSpan + colSpan > 12) {
57-
if (currentRow.length > 0) {
58-
rows.push([...currentRow]);
59-
}
60-
currentRow = [item];
61-
currentRowSpan = colSpan;
62-
} else {
63-
currentRow.push(item);
64-
currentRowSpan += colSpan;
65-
}
66-
});
67-
68-
// Add the last row if it has items
69-
if (currentRow.length > 0) {
70-
rows.push(currentRow);
71-
}
72-
73-
return rows;
74-
};
75-
76-
export function updateColSpans(
77-
updateItems: FormComponentModel[]
78-
): { id: string; span: number }[] {
79-
if (!updateItems.length) return [];
80-
81-
const totalColumns = 12;
82-
const itemCount = updateItems.length;
83-
84-
// Calculate base span and remainder
85-
const baseSpan = Math.floor(totalColumns / itemCount);
86-
const remainder = totalColumns % itemCount;
87-
88-
const adjustedSpans: { id: string; span: number }[] = [];
89-
90-
// Distribute spans equally, with remainder distributed to first few items
91-
updateItems.forEach((item, index) => {
92-
if (!item) return;
93-
// Add one extra column to the first 'remainder' items
94-
const span = index < remainder ? baseSpan + 1 : baseSpan;
95-
adjustedSpans.push({ id: item.id, span });
96-
});
97-
98-
return adjustedSpans;
99-
}
41+
import { FormComponentModel } from "@/models/FormComponent";
10042

10143
export default function FormBuilderPage() {
10244
const isMobile = useIsMobile();
@@ -118,7 +60,7 @@ export default function FormBuilderPage() {
11860
code: string;
11961
dependenciesImports: DependenciesImports;
12062
}>({ code: "", dependenciesImports: {} });
121-
const [activeId, setActiveId] = useState<string | null>(null);
63+
const [draggingDOMElement, setDraggingDOMElement] = useState<HTMLElement | null>(null);
12264
const form = useForm();
12365

12466
// Memoize static values
@@ -281,7 +223,10 @@ export default function FormBuilderPage() {
281223
const handleDragStart = useCallback(
282224
(event: DragStartEvent) => {
283225
selectComponent(null);
284-
setActiveId(event.active.id as string);
226+
const element = document.querySelector(`[data-item-id="${event.active.data.current?.component.id}"]`);
227+
if (element) {
228+
setDraggingDOMElement(element as HTMLElement);
229+
}
285230
},
286231
[selectComponent]
287232
);
@@ -388,9 +333,11 @@ export default function FormBuilderPage() {
388333
<SidebarRight />
389334
</div>
390335
<DragOverlay>
391-
<div>
392-
Form element
393-
</div>
336+
{draggingDOMElement && (
337+
<div className="bg-white p-2 rounded-md shadow opacity-80">
338+
<div dangerouslySetInnerHTML={{ __html: draggingDOMElement.innerHTML }} className="max-h-52 overflow-hidden" />
339+
</div>
340+
)}
394341
</DragOverlay>
395342
</DndContext>
396343
</SidebarProvider>

src/components/form-builder/canvas/generate-canvas-grid.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,30 @@ import { useFormBuilderStore } from "@/stores/form-builder-store";
66
import { useForm } from "react-hook-form";
77
import { memo } from "react";
88
import { useDroppable } from "@dnd-kit/core";
9+
import { cn } from "@/lib/utils";
910
// Memoize the empty state component
10-
const EmptyState = memo(() => {
11-
const { setNodeRef } = useDroppable({
11+
const EmptyState = memo(() => {
12+
const { setNodeRef, isOver } = useDroppable({
1213
id: "empty-state",
1314
data: {
1415
index: 0,
1516
},
1617
});
1718

19+
console.log(isOver);
20+
1821
return (
19-
<div ref={setNodeRef} className="p-6 text-center text-sm text-muted-foreground bg-black/5 rounded-lg max-w-md mx-auto border-dashed border-2 border-slate-300">
20-
Please add a component to the form
21-
</div>
22-
)});
22+
<div
23+
ref={setNodeRef}
24+
className={cn(
25+
"p-6 text-center text-sm text-muted-foreground bg-black/5 rounded-lg max-w-md mx-auto border-dashed border-2 border-slate-300",
26+
isOver && "border-primary"
27+
)}
28+
>
29+
Please add a component to the form
30+
</div>
31+
);
32+
});
2333

2434
EmptyState.displayName = "EmptyState";
2535

src/components/form-builder/canvas/sortable-row.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { UseFormReturn } from "react-hook-form";
44
import { GripVertical } from "lucide-react";
55
import { Button } from "@/components/ui/button";
6-
import { cn, generateTWClassesForAllViewports } from "@/lib/utils";
6+
import { cn, generateTWClassesForAllViewports, getGridRows, updateColSpans } from "@/lib/utils";
77
import { RenderEditorComponent } from "../helpers/render-editor-component";
88
import { DropdownComponents } from "../helpers/dropdown-components";
99
import { useFormBuilderStore } from "@/stores/form-builder-store";
@@ -81,11 +81,14 @@ export const RowColumn = ({
8181
},
8282
});
8383

84+
const components = useFormBuilderStore((state) => state.components);
85+
const viewport = useFormBuilderStore((state) => state.viewport);
8486
const selectedComponent = useFormBuilderStore(
8587
(state) => state.selectedComponent
8688
);
8789
const selectComponent = useFormBuilderStore((state) => state.selectComponent);
8890
const removeComponent = useFormBuilderStore((state) => state.removeComponent);
91+
const updateComponent = useFormBuilderStore((state) => state.updateComponent);
8992
const mode = useFormBuilderStore((state) => state.mode);
9093

9194
const columnStyle = useMemo(
@@ -117,6 +120,33 @@ export const RowColumn = ({
117120
[component, selectComponent, mode, columnIsDragging]
118121
);
119122

123+
const handleDelete = (id: string) => {
124+
125+
const gridRows = getGridRows(components, viewport);
126+
let activeRowItems =
127+
gridRows.find((row) =>
128+
row.some((item) => item.id === id)
129+
) || [];
130+
131+
132+
activeRowItems = activeRowItems.filter(
133+
(item) => item.id !== id
134+
);
135+
136+
const updatedActiveItems = updateColSpans([...activeRowItems]);
137+
138+
updatedActiveItems.forEach((item) => {
139+
updateComponent(
140+
item.id,
141+
"properties.style.colSpan",
142+
`${item.span}`,
143+
false
144+
);
145+
});
146+
147+
removeComponent(id);
148+
};
149+
120150
return (
121151
<div
122152
ref={setNodeRef}
@@ -189,7 +219,7 @@ export const RowColumn = ({
189219
)}
190220
onClick={(e) => {
191221
e.stopPropagation();
192-
removeComponent(component.id);
222+
handleDelete(component.id);
193223
}}
194224
>
195225
<Icons.Trash2Icon className="size-3" />

src/components/form-builder/helpers/render-editor-component.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export interface FormComponentProps {
2323
}
2424

2525
export function RenderEditorComponent({ form, component }: FormComponentProps) {
26-
const { removeComponent, selectedComponent, viewport, updateComponent } =
26+
const { selectedComponent, viewport, updateComponent } =
2727
useFormBuilderStore();
2828
const mode = useFormBuilderStore((state) => state.mode);
2929
const componentViews = getComponentViews(component);
@@ -34,7 +34,7 @@ export function RenderEditorComponent({ form, component }: FormComponentProps) {
3434
control={form.control}
3535
name={component.id}
3636
render={({ field }) => (
37-
<FormItem className={cn(mode === "editor" && "group/component")}>
37+
<FormItem className={cn(mode === "editor" && "group/component")} data-item-id={component.id}>
3838
<div className="flex items-center select-none">
3939
<FormLabel
4040
className={cn(
@@ -55,7 +55,7 @@ export function RenderEditorComponent({ form, component }: FormComponentProps) {
5555
)}
5656
/>
5757
) : (
58-
<div className={cn("relative flex flex-col h-full")} key={component.id}>
58+
<div className={cn("relative flex flex-col h-full")} key={component.id} data-item-id={component.id}>
5959
<FormWysiwygEditor
6060
value={component.content || ""}
6161
onChange={(content) => {

src/components/form-builder/sidebar/sidebarLeft.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export function SidebarLeft() {
9393
<SidebarMenuButton
9494
onClick={() => addComponent(component)}
9595
className="h-12 hover:bg-slate-100 cursor-pointer"
96+
data-item-id={component.id}
9697
>
9798
<div
9899
{...columnAttributes}

src/lib/utils.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FormComponentStyles } from "@/types/FormComponent.types";
2-
import { Viewports, FormRow } from "@/types/form-builder.types";
2+
import { Viewports } from "@/types/form-builder.types";
33
import { FormComponentModel } from "@/models/FormComponent";
44
import { type ClassValue, clsx } from "clsx";
55
import { twMerge } from "tailwind-merge";
@@ -312,3 +312,60 @@ export const escapeHtml = (text: string, whitelist?: EscapeHtmlWhitelist[]): str
312312
return text;
313313

314314
};
315+
316+
export const getGridRows = (
317+
items: FormComponentModel[],
318+
viewport: Viewports
319+
): FormComponentModel[][] => {
320+
const rows: FormComponentModel[][] = [];
321+
let currentRow: FormComponentModel[] = [];
322+
let currentRowSpan = 0;
323+
324+
items.forEach((item) => {
325+
const colSpan = +item.getField("properties.style.colSpan", viewport) || 12;
326+
327+
// If adding this item would exceed 12 columns, start a new row
328+
if (currentRowSpan + colSpan > 12) {
329+
if (currentRow.length > 0) {
330+
rows.push([...currentRow]);
331+
}
332+
currentRow = [item];
333+
currentRowSpan = colSpan;
334+
} else {
335+
currentRow.push(item);
336+
currentRowSpan += colSpan;
337+
}
338+
});
339+
340+
// Add the last row if it has items
341+
if (currentRow.length > 0) {
342+
rows.push(currentRow);
343+
}
344+
345+
return rows;
346+
};
347+
348+
export const updateColSpans = (
349+
updateItems: FormComponentModel[]
350+
): { id: string; span: number }[] => {
351+
if (!updateItems.length) return [];
352+
353+
const totalColumns = 12;
354+
const itemCount = updateItems.length;
355+
356+
// Calculate base span and remainder
357+
const baseSpan = Math.floor(totalColumns / itemCount);
358+
const remainder = totalColumns % itemCount;
359+
360+
const adjustedSpans: { id: string; span: number }[] = [];
361+
362+
// Distribute spans equally, with remainder distributed to first few items
363+
updateItems.forEach((item, index) => {
364+
if (!item) return;
365+
// Add one extra column to the first 'remainder' items
366+
const span = index < remainder ? baseSpan + 1 : baseSpan;
367+
adjustedSpans.push({ id: item.id, span });
368+
});
369+
370+
return adjustedSpans;
371+
}

0 commit comments

Comments
 (0)