Skip to content

Commit 11b8153

Browse files
dragging/inserting between rows
1 parent 13d746d commit 11b8153

File tree

4 files changed

+66
-51
lines changed

4 files changed

+66
-51
lines changed

datahub-web-react/src/app/homeV3/context/hooks/useDragAndDrop.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface DraggedModuleData {
1414
export interface DroppableData {
1515
rowIndex: number;
1616
moduleIndex?: number; // If undefined, drop at the end of the row
17+
insertNewRow?: boolean; // If true, create a new row at this position
1718
}
1819

1920
export function useDragAndDrop() {
@@ -51,6 +52,7 @@ export function useDragAndDrop() {
5152
module: draggedData.module,
5253
fromPosition: draggedData.position,
5354
toPosition,
55+
insertNewRow: droppableData.insertNewRow,
5456
});
5557
},
5658
[moveModule],

datahub-web-react/src/app/homeV3/context/hooks/useModuleOperations.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface MoveModuleInput {
2929
module: PageModuleFragment;
3030
fromPosition: ModulePositionInput;
3131
toPosition: ModulePositionInput;
32+
insertNewRow?: boolean;
3233
}
3334

3435
// Helper types for shared operations
@@ -200,6 +201,7 @@ export function useModuleOperations(
200201
module: PageModuleFragment,
201202
fromPosition: ModulePositionInput,
202203
toPosition: ModulePositionInput,
204+
insertNewRow?: boolean,
203205
): PageTemplateFragment | null => {
204206
if (!template) return null;
205207

@@ -241,8 +243,14 @@ export function useModuleOperations(
241243
? toRowIndex - 1
242244
: toRowIndex;
243245

244-
if (adjustedToRowIndex >= newRows.length) {
245-
// Create new row
246+
if (insertNewRow) {
247+
// Insert a new row at the specified position
248+
const newRow = {
249+
modules: [module],
250+
};
251+
newRows.splice(adjustedToRowIndex, 0, newRow);
252+
} else if (adjustedToRowIndex >= newRows.length) {
253+
// Create new row at the end
246254
newRows.push({
247255
modules: [module],
248256
});
@@ -404,7 +412,7 @@ export function useModuleOperations(
404412
return;
405413
}
406414

407-
const { module, fromPosition, toPosition } = input;
415+
const { module, fromPosition, toPosition, insertNewRow } = input;
408416
const { template: templateToUpdate, isPersonal } = getTemplateToUpdate(context);
409417

410418
if (!templateToUpdate) {
@@ -430,7 +438,7 @@ export function useModuleOperations(
430438
}
431439

432440
// Update template state
433-
const updatedTemplate = moveModuleInTemplate(templateToUpdate, module, fromPosition, toPosition);
441+
const updatedTemplate = moveModuleInTemplate(templateToUpdate, module, fromPosition, toPosition, insertNewRow);
434442

435443
if (!updatedTemplate) {
436444
console.error('Failed to update template during move operation');

datahub-web-react/src/app/homeV3/context/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface MoveModuleInput {
2626
module: PageModuleFragment;
2727
fromPosition: ModulePositionInput;
2828
toPosition: ModulePositionInput;
29+
insertNewRow?: boolean;
2930
}
3031

3132
// Context state shape

datahub-web-react/src/app/homeV3/template/Template.tsx

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import Module from '@app/homeV3/module/Module';
1818
const Wrapper = styled.div`
1919
display: flex;
2020
flex-direction: column;
21-
gap: ${spacing.md};
21+
gap: ${spacing.xsm};
2222
`;
2323

2424
// Additional margin to have width of content excluding side buttons
@@ -27,37 +27,15 @@ const StyledAddModulesButton = styled(AddModuleButton)<{ $hasRows?: boolean }>`
2727
`;
2828

2929
const NewRowDropZone = styled.div<{ $isOver?: boolean }>`
30-
min-height: 60px;
31-
border-radius: 8px;
32-
margin: 0 48px;
33-
display: flex;
34-
align-items: center;
35-
justify-content: center;
3630
transition: all 0.2s ease;
37-
border: 2px dashed transparent;
3831
3932
${({ $isOver }) =>
4033
$isOver &&
4134
`
42-
background-color: rgba(59, 130, 246, 0.1);
43-
border-color: #3b82f6;
44-
min-height: 80px;
35+
border: 2px solid #CAC3F1;
4536
`}
4637
`;
4738

48-
const NewRowDropText = styled.div<{ $isOver?: boolean }>`
49-
color: #6b7280;
50-
font-size: 14px;
51-
text-align: center;
52-
transition: all 0.2s ease;
53-
54-
${({ $isOver }) =>
55-
$isOver &&
56-
`
57-
color: #3b82f6;
58-
font-weight: 500;
59-
`}
60-
`;
6139

6240
// Styled wrapper for drag overlay to make it look like it's floating
6341
const DragOverlayWrapper = styled.div`
@@ -75,24 +53,27 @@ interface Props {
7553
}
7654

7755
// Memoized new row drop zone component
78-
const NewRowDropZoneComponent = memo(function NewRowDropZoneComponent({ rowIndex }: { rowIndex: number }) {
56+
const NewRowDropZoneComponent = memo(function NewRowDropZoneComponent({
57+
rowIndex,
58+
insertNewRow = false
59+
}: {
60+
rowIndex: number;
61+
insertNewRow?: boolean;
62+
}) {
7963
const {
8064
isOver,
8165
setNodeRef,
8266
} = useDroppable({
83-
id: `new-row-drop-zone-${rowIndex}`,
67+
id: `new-row-drop-zone-${rowIndex}${insertNewRow ? '-insert' : ''}`,
8468
data: {
8569
rowIndex,
8670
moduleIndex: 0, // First position in new row
71+
insertNewRow, // Flag to indicate this should create a new row at this position
8772
},
8873
});
8974

9075
return (
91-
<NewRowDropZone ref={setNodeRef} $isOver={isOver}>
92-
<NewRowDropText $isOver={isOver}>
93-
{isOver ? 'Drop here to create a new row' : 'Drop a module here to create a new row'}
94-
</NewRowDropText>
95-
</NewRowDropZone>
76+
<NewRowDropZone ref={setNodeRef} $isOver={isOver} />
9677
);
9778
});
9879

@@ -130,18 +111,44 @@ function Template({ className }: Props) {
130111
handleDragEnd(event);
131112
}, [handleDragEnd]);
132113

133-
// Memoize the template rows to prevent unnecessary re-renders
134-
const templateRows = useMemo(() =>
135-
wrappedRows.map((row, i) => (
136-
<TemplateRow
137-
key={`templateRow-${i}`}
138-
row={row}
139-
rowIndex={i}
140-
modulesAvailableToAdd={modulesAvailableToAdd}
141-
/>
142-
)),
143-
[wrappedRows, modulesAvailableToAdd]
144-
);
114+
// Memoize the template rows with drop zones between them
115+
const templateRowsWithDropZones = useMemo(() => {
116+
const result: React.ReactElement[] = [];
117+
118+
wrappedRows.forEach((row, i) => {
119+
// Add drop zone before the first row (for inserting at beginning)
120+
if (i === 0) {
121+
result.push(
122+
<NewRowDropZoneComponent
123+
key={`drop-zone-before-${i}`}
124+
rowIndex={i}
125+
insertNewRow={true}
126+
/>
127+
);
128+
}
129+
130+
// Add the actual row
131+
result.push(
132+
<TemplateRow
133+
key={`templateRow-${i}`}
134+
row={row}
135+
rowIndex={i}
136+
modulesAvailableToAdd={modulesAvailableToAdd}
137+
/>
138+
);
139+
140+
// Add drop zone after each row (for inserting between/after rows)
141+
result.push(
142+
<NewRowDropZoneComponent
143+
key={`drop-zone-after-${i}`}
144+
rowIndex={i + 1}
145+
insertNewRow={true}
146+
/>
147+
);
148+
});
149+
150+
return result;
151+
}, [wrappedRows, modulesAvailableToAdd]);
145152

146153
return (
147154
<Wrapper className={className}>
@@ -150,10 +157,7 @@ function Template({ className }: Props) {
150157
onDragStart={handleDragStart}
151158
onDragEnd={handleDragEndWithCleanup}
152159
>
153-
{templateRows}
154-
155-
{/* Drop zone for creating new rows */}
156-
<NewRowDropZoneComponent rowIndex={wrappedRows.length} />
160+
{templateRowsWithDropZones}
157161

158162
<DragOverlay>
159163
{activeModule && (

0 commit comments

Comments
 (0)