diff --git a/apps/web/src/components/input/chip-input.tsx b/apps/web/src/components/input/chip-input.tsx
index 5f88b8280..410741e80 100644
--- a/apps/web/src/components/input/chip-input.tsx
+++ b/apps/web/src/components/input/chip-input.tsx
@@ -12,6 +12,7 @@ import { rectSortingStrategy, SortableContext, sortableKeyboardCoordinates, useS
import { CSS } from "@dnd-kit/utilities";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
+import { useReducedMotion } from "motion/react";
import * as React from "react";
import { createPortal } from "react-dom";
import { Badge } from "@reactive-resume/ui/components/badge";
@@ -21,6 +22,7 @@ import { Input } from "@reactive-resume/ui/components/input";
import { Kbd } from "@reactive-resume/ui/components/kbd";
import { cn } from "@reactive-resume/utils/style";
import { useControlledState } from "@/hooks/use-controlled-state";
+import { DRAG_SETTLE } from "@/libs/motion";
const RETURN_KEY = "Enter";
const COMMA_KEY = ",";
@@ -55,15 +57,21 @@ type ChipDragOverlayProps = {
};
function ChipDragOverlay({ activeChip }: ChipDragOverlayProps) {
+ const reduceMotion = useReducedMotion();
const overlay = (
- {activeChip ? : null}
+
+ {activeChip ? : null}
+
);
return createPortal(overlay, document.body);
}
function ChipItem({ id, chip, index, isEditing, onEdit, onRemove }: ChipItemProps) {
- const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
+ id,
+ transition: DRAG_SETTLE,
+ });
const style = {
transition,
diff --git a/apps/web/src/features/resume/editor/write/entry-card.tsx b/apps/web/src/features/resume/editor/write/entry-card.tsx
index 065cf4eea..2001767f4 100644
--- a/apps/web/src/features/resume/editor/write/entry-card.tsx
+++ b/apps/web/src/features/resume/editor/write/entry-card.tsx
@@ -25,6 +25,7 @@ import { toast } from "@reactive-resume/ui/components/toast";
import { useBreakpoint } from "@reactive-resume/ui/hooks/use-breakpoint";
import { cn } from "@reactive-resume/utils/style";
import { useCurrentResume, useResumeStore, useUpdateResumeData } from "@/features/resume/builder/draft";
+import { DRAG_SETTLE } from "@/libs/motion";
import { getCompatibleMoveTargets, getSourceSectionTitle, moveItem } from "@/libs/resume/move-item";
import { useEditorStore } from "../store";
import { EntryFields } from "./entries";
@@ -65,7 +66,7 @@ export function EntryCard({ section, entryId, index, count, page, locked, onMove
const open = useEditorStore((state) => state.selection?.kind === "item" && state.selection.itemId === entryId);
const autoFocus = useEditorStore((state) => state.focusEntryId === entryId);
const select = useEditorStore((state) => state.select);
- const sortable = useSortable({ id: entryId, disabled: locked });
+ const sortable = useSortable({ id: entryId, disabled: locked, transition: DRAG_SETTLE });
const isPhone = useBreakpoint() === "mobile";
// The first field took focus as it mounted; later openings shouldn't steal focus again.
@@ -101,9 +102,9 @@ export function EntryCard({ section, entryId, index, count, page, locked, onMove
data-entry-id={entryId}
style={{ transform: CSS.Translate.toString(sortable.transform), transition: sortable.transition }}
className={cn(
- "group/entry relative scroll-mt-[60px] rounded-[10px] border border-line bg-surface transition-[border-color,box-shadow] duration-quick",
+ "group/entry relative scroll-mt-[60px] rounded-[10px] border border-line bg-surface transition-[border-color,box-shadow] duration-quick ease-enter",
open && "border-accent shadow-e1",
- sortable.isDragging && "z-10 opacity-40",
+ sortable.isDragging && "z-10 bg-raised shadow-e2",
)}
>
diff --git a/apps/web/src/features/resume/editor/write/section-row.tsx b/apps/web/src/features/resume/editor/write/section-row.tsx
index adabe064f..53840044a 100644
--- a/apps/web/src/features/resume/editor/write/section-row.tsx
+++ b/apps/web/src/features/resume/editor/write/section-row.tsx
@@ -28,6 +28,7 @@ import { cn } from "@reactive-resume/utils/style";
import { IconPicker } from "@/components/input/icon-picker";
import { useCurrentBuilderResumeSelector, useResumeStore, useUpdateResumeData } from "@/features/resume/builder/draft";
import { usePrompt } from "@/hooks/use-confirm";
+import { DRAG_SETTLE } from "@/libs/motion";
import { getSectionTitle } from "@/libs/resume/section";
import { pendingProposals } from "../proposals/proposals";
import { useEditorStore } from "../store";
@@ -64,7 +65,7 @@ export function SectionRow({ section, locked, onMove, children }: SectionRowProp
const open = useEditorStore((state) => state.openSections.includes(section.id));
const setOpen = useEditorStore((state) => state.setSectionOpen);
const updateResumeData = useUpdateResumeData();
- const sortable = useSortable({ id: section.id, disabled: locked });
+ const sortable = useSortable({ id: section.id, disabled: locked, transition: DRAG_SETTLE });
const toggleHidden = () =>
updateResumeData(
@@ -87,7 +88,10 @@ export function SectionRow({ section, locked, onMove, children }: SectionRowProp
id={`sidebar-${section.id}`}
data-section-id={section.id}
style={{ transform: CSS.Translate.toString(sortable.transform), transition: sortable.transition }}
- className={cn("relative scroll-mt-[60px] rounded-lg", sortable.isDragging && "z-10 opacity-40")}
+ className={cn(
+ "relative scroll-mt-[60px] rounded-lg transition-shadow duration-quick ease-enter",
+ sortable.isDragging && "z-10 bg-raised shadow-e2",
+ )}
>
-
+
{activeId ? : null}
@@ -407,7 +409,10 @@ type SortableLayoutItemProps = {
};
function SortableLayoutItem({ id, pageIndex, columnId }: SortableLayoutItemProps) {
- const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
+ id,
+ transition: DRAG_SETTLE,
+ });
const style: CSSProperties = { transform: CSS.Transform.toString(transform), transition };