mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-13 00:03:27 +10:00
Merge pull request #2244 from m4dd0c/draggable-tags
feat(dialogs): skills, custom-section, and interests dialogs support draggable tags.
This commit is contained in:
@ -30,6 +30,10 @@ const formSchema = customSectionSchema;
|
|||||||
|
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
export const CustomSectionDialog = () => {
|
export const CustomSectionDialog = () => {
|
||||||
const { payload } = useDialog<CustomSection>("custom");
|
const { payload } = useDialog<CustomSection>("custom");
|
||||||
|
|
||||||
@ -40,6 +44,24 @@ export const CustomSectionDialog = () => {
|
|||||||
|
|
||||||
const [pendingKeyword, setPendingKeyword] = useState("");
|
const [pendingKeyword, setPendingKeyword] = useState("");
|
||||||
|
|
||||||
|
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleDrop = (
|
||||||
|
e: React.DragEvent,
|
||||||
|
dropIndex: number,
|
||||||
|
field: { value: string[]; onChange: (value: string[]) => void },
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (draggedIndex === null) return;
|
||||||
|
|
||||||
|
const newKeywords = [...field.value];
|
||||||
|
const [draggedItem] = newKeywords.splice(draggedIndex, 1);
|
||||||
|
newKeywords.splice(dropIndex, 0, draggedItem);
|
||||||
|
|
||||||
|
field.onChange(newKeywords);
|
||||||
|
setDraggedIndex(null);
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (!payload) return null;
|
if (!payload) return null;
|
||||||
|
|
||||||
@ -172,9 +194,17 @@ export const CustomSectionDialog = () => {
|
|||||||
<motion.div
|
<motion.div
|
||||||
key={item}
|
key={item}
|
||||||
layout
|
layout
|
||||||
|
draggable
|
||||||
initial={{ opacity: 0, y: -50 }}
|
initial={{ opacity: 0, y: -50 }}
|
||||||
animate={{ opacity: 1, y: 0, transition: { delay: index * 0.1 } }}
|
animate={{ opacity: 1, y: 0, transition: { delay: index * 0.1 } }}
|
||||||
exit={{ opacity: 0, x: -50 }}
|
exit={{ opacity: 0, x: -50 }}
|
||||||
|
onDragStart={() => {
|
||||||
|
setDraggedIndex(index);
|
||||||
|
}}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={(e) => {
|
||||||
|
handleDrop(e, index, field);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
|
|||||||
@ -24,6 +24,10 @@ const formSchema = interestSchema;
|
|||||||
|
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
export const InterestsDialog = () => {
|
export const InterestsDialog = () => {
|
||||||
const form = useForm<FormValues>({
|
const form = useForm<FormValues>({
|
||||||
defaultValues: defaultInterest,
|
defaultValues: defaultInterest,
|
||||||
@ -31,6 +35,23 @@ export const InterestsDialog = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [pendingKeyword, setPendingKeyword] = useState("");
|
const [pendingKeyword, setPendingKeyword] = useState("");
|
||||||
|
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleDrop = (
|
||||||
|
e: React.DragEvent,
|
||||||
|
dropIndex: number,
|
||||||
|
field: { value: string[]; onChange: (value: string[]) => void },
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (draggedIndex === null) return;
|
||||||
|
|
||||||
|
const newKeywords = [...field.value];
|
||||||
|
const [draggedItem] = newKeywords.splice(draggedIndex, 1);
|
||||||
|
newKeywords.splice(dropIndex, 0, draggedItem);
|
||||||
|
|
||||||
|
field.onChange(newKeywords);
|
||||||
|
setDraggedIndex(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionDialog<FormValues>
|
<SectionDialog<FormValues>
|
||||||
@ -76,9 +97,17 @@ export const InterestsDialog = () => {
|
|||||||
<motion.div
|
<motion.div
|
||||||
key={item}
|
key={item}
|
||||||
layout
|
layout
|
||||||
|
draggable
|
||||||
initial={{ opacity: 0, y: -50 }}
|
initial={{ opacity: 0, y: -50 }}
|
||||||
animate={{ opacity: 1, y: 0, transition: { delay: index * 0.1 } }}
|
animate={{ opacity: 1, y: 0, transition: { delay: index * 0.1 } }}
|
||||||
exit={{ opacity: 0, x: -50 }}
|
exit={{ opacity: 0, x: -50 }}
|
||||||
|
onDragStart={() => {
|
||||||
|
setDraggedIndex(index);
|
||||||
|
}}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={(e) => {
|
||||||
|
handleDrop(e, index, field);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
|
|||||||
@ -25,6 +25,10 @@ const formSchema = skillSchema;
|
|||||||
|
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
export const SkillsDialog = () => {
|
export const SkillsDialog = () => {
|
||||||
const form = useForm<FormValues>({
|
const form = useForm<FormValues>({
|
||||||
defaultValues: defaultSkill,
|
defaultValues: defaultSkill,
|
||||||
@ -32,6 +36,23 @@ export const SkillsDialog = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [pendingKeyword, setPendingKeyword] = useState("");
|
const [pendingKeyword, setPendingKeyword] = useState("");
|
||||||
|
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleDrop = (
|
||||||
|
e: React.DragEvent,
|
||||||
|
dropIndex: number,
|
||||||
|
field: { value: string[]; onChange: (value: string[]) => void },
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (draggedIndex === null) return;
|
||||||
|
|
||||||
|
const newKeywords = [...field.value];
|
||||||
|
const [draggedItem] = newKeywords.splice(draggedIndex, 1);
|
||||||
|
newKeywords.splice(dropIndex, 0, draggedItem);
|
||||||
|
|
||||||
|
field.onChange(newKeywords);
|
||||||
|
setDraggedIndex(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionDialog<FormValues>
|
<SectionDialog<FormValues>
|
||||||
@ -122,9 +143,17 @@ export const SkillsDialog = () => {
|
|||||||
<motion.div
|
<motion.div
|
||||||
key={item}
|
key={item}
|
||||||
layout
|
layout
|
||||||
|
draggable
|
||||||
initial={{ opacity: 0, y: -50 }}
|
initial={{ opacity: 0, y: -50 }}
|
||||||
animate={{ opacity: 1, y: 0, transition: { delay: index * 0.1 } }}
|
animate={{ opacity: 1, y: 0, transition: { delay: index * 0.1 } }}
|
||||||
exit={{ opacity: 0, x: -50 }}
|
exit={{ opacity: 0, x: -50 }}
|
||||||
|
onDragStart={() => {
|
||||||
|
setDraggedIndex(index);
|
||||||
|
}}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={(e) => {
|
||||||
|
handleDrop(e, index, field);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
|
|||||||
Reference in New Issue
Block a user