Skip to content

Commit 05c67b7

Browse files
authored
Merge pull request #490 from fahad-aot/feature/fwf-4401-Drag-Drop-sort
Feature/fwf 4401 drag drop sort
2 parents 84cf521 + 462a89a commit 05c67b7

File tree

5 files changed

+238
-33
lines changed

5 files changed

+238
-33
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React, { useState, useEffect } from "react";
2+
import { FormVariableIcon,DraggableIcon } from "../SvgIcons/index";
3+
import { StyleServices } from "@formsflow/service";
4+
5+
interface FilterItem {
6+
label: string;
7+
name: string;
8+
isChecked: boolean;
9+
sortOrder: number;
10+
isTaskVariable: boolean;
11+
}
12+
13+
interface DragAndDropFilterProps {
14+
items: FilterItem[];
15+
onUpdate: (updatedItems: FilterItem[]) => void;
16+
}
17+
18+
export const DragandDropSort: React.FC<DragAndDropFilterProps> = ({ items, onUpdate }) => {
19+
20+
const darkColor = StyleServices.getCSSVariable('--ff-gray-darkest');
21+
22+
const [filterItems, setFilterItems] = useState<FilterItem[]>(items);
23+
const [draggingIndex, setDraggingIndex] = useState<number | null>(null);
24+
25+
useEffect(() => {
26+
onUpdate(filterItems);
27+
}, [filterItems, onUpdate]);
28+
29+
const onDragStart = (e: React.DragEvent<HTMLLIElement>, index: number) => {
30+
e.stopPropagation();
31+
e.dataTransfer.setData("drag-index", index.toString());
32+
setDraggingIndex(index);
33+
};
34+
35+
const onDragOver = (e: React.DragEvent<HTMLLIElement>) => {
36+
e.preventDefault();
37+
};
38+
39+
40+
const onDragEnter = (e: React.DragEvent<HTMLLIElement>, targetIndex: number) => {
41+
e.preventDefault();
42+
if (draggingIndex === null || draggingIndex === targetIndex) return;
43+
44+
setFilterItems((prevItems) => {
45+
const updatedItems = [...prevItems];
46+
const [draggedItem] = updatedItems.splice(draggingIndex, 1);
47+
updatedItems.splice(targetIndex, 0, draggedItem);
48+
return updatedItems.map((item, index) => ({ ...item, sortOrder: index + 1, isMoving: true, }));
49+
});
50+
setDraggingIndex(targetIndex);
51+
};
52+
53+
const onDrop = (e: React.DragEvent<HTMLLIElement>) => {
54+
e.preventDefault();
55+
setDraggingIndex(null);
56+
};
57+
58+
const onDragEnd = () => {
59+
setDraggingIndex(null);
60+
};
61+
62+
const onCheckboxChange = (index: number) => {
63+
setFilterItems((prevItems) =>
64+
prevItems.map((item, i) =>
65+
i === index ? { ...item, isChecked: !item.isChecked } : item
66+
)
67+
);
68+
};
69+
return (
70+
<div className="drag-drop-container">
71+
<ul>
72+
{filterItems.map((item, index) => (
73+
<li
74+
key={item.name}
75+
draggable
76+
className={`draggable-item ${draggingIndex === index ? "dragging" : ""} `}
77+
onDragOver={onDragOver}
78+
onDrop={onDrop}
79+
onDragEnter={(e) => onDragEnter(e, index)}
80+
onDragEnd={onDragEnd}
81+
onDragStart={(e) => onDragStart(e, index)}
82+
>
83+
<span
84+
className="draggable-icon"
85+
>
86+
<DraggableIcon />
87+
</span>
88+
<div className="checkbox-container">
89+
<input
90+
data-testid={`${item.name}-checkbox`}
91+
type="checkbox"
92+
className="form-check-input"
93+
checked={item.isChecked}
94+
onChange={() => onCheckboxChange(index)}
95+
/>
96+
</div>
97+
{item.label}
98+
<div className="dotted-line"></div>
99+
{item.isTaskVariable && (
100+
<FormVariableIcon color={darkColor} />
101+
) }
102+
</li>
103+
))}
104+
</ul>
105+
</div>
106+
);
107+
};
108+

forms-flow-components/src/components/SvgIcons/index.tsx

Lines changed: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export const CloseIcon = ({
221221
height={height}
222222
viewBox="0 0 14 14"
223223
fill="none"
224-
onClick={onClick}
224+
onClick={onClick}
225225
{...props}
226226
>
227227
<path
@@ -240,25 +240,25 @@ export const CloseIcon = ({
240240
);
241241

242242
export const DeleteIcon = ({ color = baseColor, onClick, ...props }) => (
243-
<svg xmlns="http://www.w3.org/2000/svg"
243+
<svg
244+
xmlns="http://www.w3.org/2000/svg"
244245
width="14"
245246
height="16"
246247
viewBox="0 0 14 16"
247248
fill="none"
248249
onClick={onClick}
249250
{...props}
250251
>
251-
<path d="M7 8L7 11"
252-
stroke={color}
253-
strokeWidth="2"
254-
strokeLinecap="round" />
255-
<path d="M2.41176 3.94737L3.82353 15H10.1765L11.5882 3.94737M1 3.94737H4.52941M13 3.94737H9.47059M4.52941 3.94737L5.23529 1H8.76471L9.47059 3.94737M4.52941 3.94737H9.47059"
256-
stroke={color}
257-
strokeWidth="2"
258-
strokeLinecap="round"
259-
strokeLinejoin="round" />
260-
</svg>
261-
)
252+
<path d="M7 8L7 11" stroke={color} strokeWidth="2" strokeLinecap="round" />
253+
<path
254+
d="M2.41176 3.94737L3.82353 15H10.1765L11.5882 3.94737M1 3.94737H4.52941M13 3.94737H9.47059M4.52941 3.94737L5.23529 1H8.76471L9.47059 3.94737M4.52941 3.94737H9.47059"
255+
stroke={color}
256+
strokeWidth="2"
257+
strokeLinecap="round"
258+
strokeLinejoin="round"
259+
/>
260+
</svg>
261+
);
262262

263263
export const SuccessIcon = ({ color = baseColor, ...props }) => (
264264
<svg
@@ -471,7 +471,6 @@ export const AngleLeftIcon = ({ color = baseColor, onClick, ...props }) => (
471471
</svg>
472472
);
473473

474-
475474
export const UploadIcon = ({ color = baseColor, ...props }) => (
476475
<svg
477476
xmlns="http://www.w3.org/2000/svg"
@@ -594,7 +593,7 @@ export const CopyIcon = ({ color = baseColor, ...props }) => (
594593
</defs>
595594
</svg>
596595
);
597-
export const NewInfoIcon = ({ color = grayColor, ...props }) => (
596+
export const NewInfoIcon = ({ color = grayColor, ...props }) => (
598597
<svg
599598
width="16"
600599
height="16"
@@ -638,22 +637,24 @@ export const TrashIcon = ({ color = baseColor, ...props }) => (
638637
);
639638

640639
export const TickIcon = ({ color = baseColor, ...props }) => (
641-
<svg
642-
xmlns="http://www.w3.org/2000/svg"
643-
width="16"
644-
height="13"
645-
viewBox="0 0 16 13"
646-
fill="none">
647-
<path
648-
d="M1 6.96876L6.69828 12L15 1"
649-
stroke={color}
650-
strokeWidth="2"
651-
strokeLinecap="round"
652-
strokeLinejoin="round"/>
653-
</svg>
640+
<svg
641+
xmlns="http://www.w3.org/2000/svg"
642+
width="16"
643+
height="13"
644+
viewBox="0 0 16 13"
645+
fill="none"
646+
>
647+
<path
648+
d="M1 6.96876L6.69828 12L15 1"
649+
stroke={color}
650+
strokeWidth="2"
651+
strokeLinecap="round"
652+
strokeLinejoin="round"
653+
/>
654+
</svg>
654655
);
655656

656-
export const DropdownIcon = ({color = baseColor}) => (
657+
export const DropdownIcon = ({ color = baseColor }) => (
657658
<svg
658659
xmlns="http://www.w3.org/2000/svg"
659660
width="16"
@@ -670,7 +671,7 @@ export const DropdownIcon = ({color = baseColor}) => (
670671
/>
671672
</svg>
672673
);
673-
export const StarPremiumIcon = ({ color = baseColor,...props }) => (
674+
export const StarPremiumIcon = ({ color = baseColor, ...props }) => (
674675
<svg
675676
xmlns="http://www.w3.org/2000/svg"
676677
width="16"
@@ -755,6 +756,55 @@ export const SortIcon = ({ color = grayColor, ...props }) => (
755756
</svg>
756757
);
757758

759+
export const DraggableIcon = ({ color = baseColor, ...props }) => (
760+
<svg
761+
width="16"
762+
height="14"
763+
viewBox="0 0 16 14"
764+
fill={color}
765+
xmlns="http://www.w3.org/2000/svg"
766+
>
767+
<path
768+
d="M1.5 4H16.5"
769+
stroke={color}
770+
strokeWidth="2"
771+
strokeLinecap="round"
772+
/>
773+
<path
774+
d="M1.5 12H16.5"
775+
stroke={color}
776+
strokeWidth="2"
777+
strokeLinecap="round"
778+
/>
779+
</svg>
780+
);
781+
782+
export const FormVariableIcon = ({ color = baseColor, ...props }) => (
783+
<svg
784+
width="16"
785+
height="16"
786+
viewBox="0 0 16 16"
787+
fill="none"
788+
xmlns="http://www.w3.org/2000/svg"
789+
>
790+
<path
791+
d="M3 2H13V0H3V2ZM13 2V14H15V2H13ZM13 14H3V16H13V14ZM3 14V2H1V14H3ZM3 14H3H1C1 15.1046 1.89543 16 3 16V14ZM13 14V16C14.1046 16 15 15.1046 15 14H13ZM13 2H15C15 0.895431 14.1046 0 13 0V2ZM3 0C1.89543 0 1 0.895431 1 2H3V2V0Z"
792+
fill={color}
793+
/>
794+
<path
795+
d="M6 8L8 8"
796+
stroke={color}
797+
strokeWidth="2"
798+
strokeLinecap="round"
799+
/>
800+
<path
801+
d="M6 5H10"
802+
stroke={color}
803+
strokeWidth="2"
804+
strokeLinecap="round"
805+
/>
806+
</svg>
807+
);
758808
export const ConnectIcon = ({ color = baseColor, ...props }) => (
759809
<svg
760810
width="10"

forms-flow-components/src/components/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ export * from "./CustomComponents/NoDataFound";
2323
export * from "./CustomComponents/ShowPremiumIcon";
2424
export * from "./CustomComponents/MultiSelect";
2525
export * from "./CustomComponents/DropdownMultiselect";
26-
export * from "./CustomComponents/FormSubmissionHistoryModal";
26+
export * from "./CustomComponents/FormSubmissionHistoryModal";
27+
export * from "./CustomComponents/DragandDropSort";

forms-flow-theme/scss/_forms.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ select option:hover {
621621
}
622622

623623
//custom switch for sdpr
624-
.form-check-input[type=checkbox] {
624+
.custom-switch-checkbox {
625625
background-color: #EDEBE9;
626626
border: none;
627627
position: relative;

forms-flow-theme/scss/_modal.scss

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ $gray-medium: var(--ff-gray-medium);
77
$gray-medium-dark: var(--ff-gray-medium-dark);
88
$black-color: var(--ff-black);
99

10+
1011
.modal {
1112
.modal-dialog {
1213
display: flex;
@@ -988,4 +989,49 @@ $black-color: var(--ff-black);
988989
border: 0.125rem dotted $primary;
989990
padding: 0.1875rem;
990991
border-radius: var(--radius-sm); /* Optional: Add some rounding */
991-
}
992+
}
993+
.drag-drop-container {
994+
display: flex;
995+
flex-direction: column;
996+
padding: var(--spacer-200) var(--spacer-225);
997+
gap: var(--spacer-150);
998+
999+
.draggable-item {
1000+
display: flex;
1001+
flex-direction: row;
1002+
align-items: center;
1003+
gap: var(--spacer-100);
1004+
border-radius: var(--radius-sm);
1005+
padding: var(--spacer-050) ;
1006+
transition:
1007+
transform 3s cubic-bezier(0.25, 1, 0.5, 1),
1008+
background-color 3s ease-in-out,
1009+
opacity 3s ease-in-out,
1010+
box-shadow 3s ease-in-out;
1011+
1012+
.draggable-icon {
1013+
display: flex;
1014+
align-self: center;
1015+
cursor: grab;
1016+
}
1017+
&:active {
1018+
cursor: grabbing;
1019+
}
1020+
1021+
&.dragging {
1022+
background-color: $gray-medium;
1023+
opacity: 1;
1024+
z-index: 1000;
1025+
transform: scale(1.03);
1026+
}
1027+
.checkbox-container{
1028+
display: flex;
1029+
margin-bottom: 0.188rem;
1030+
}
1031+
}
1032+
}
1033+
1034+
.dotted-line {
1035+
flex-grow: 1;
1036+
border-bottom: 1px dashed $gray-medium;
1037+
}

0 commit comments

Comments
 (0)