style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
@@ -22,30 +22,32 @@
|
||||
* - Delete: caller removes item from cache; no rollback (D-09)
|
||||
*/
|
||||
|
||||
import { useState } from 'react'
|
||||
import { GripVertical, Trash2 } from 'lucide-react'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import type { ListItem } from '../api/listsClient.js'
|
||||
import { useState } from 'react';
|
||||
import { GripVertical, Trash2 } from 'lucide-react';
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import type { ListItem } from '../api/listsClient.js';
|
||||
|
||||
/**
|
||||
* Convert a dnd-kit Transform object to a CSS transform string.
|
||||
* Equivalent to CSS.Transform.toString() from @dnd-kit/utilities (not a direct
|
||||
* dependency; inline to avoid adding @dnd-kit/utilities as a separate dep).
|
||||
*/
|
||||
function transformToString(transform: { x: number; y: number; scaleX: number; scaleY: number } | null): string | undefined {
|
||||
if (!transform) return undefined
|
||||
const { x, y } = transform
|
||||
return `translate3d(${x ? Math.round(x) : 0}px, ${y ? Math.round(y) : 0}px, 0)`
|
||||
function transformToString(
|
||||
transform: { x: number; y: number; scaleX: number; scaleY: number } | null,
|
||||
): string | undefined {
|
||||
if (!transform) return undefined;
|
||||
const { x, y } = transform;
|
||||
return `translate3d(${x ? Math.round(x) : 0}px, ${y ? Math.round(y) : 0}px, 0)`;
|
||||
}
|
||||
|
||||
interface ItemRowProps {
|
||||
item: ListItem
|
||||
item: ListItem;
|
||||
/** Whether this is an active (unchecked) item — shows drag handle */
|
||||
isActive: boolean
|
||||
onCheck: (itemId: number, checked: boolean) => void
|
||||
onDelete: (itemId: number) => void
|
||||
isActive: boolean;
|
||||
onCheck: (itemId: number, checked: boolean) => void;
|
||||
onDelete: (itemId: number) => void;
|
||||
/** Opacity for optimistic pending state (e.g. 0.6 while add is confirming) */
|
||||
optimisticOpacity?: number
|
||||
optimisticOpacity?: number;
|
||||
}
|
||||
|
||||
export function ItemRow({
|
||||
@@ -55,52 +57,47 @@ export function ItemRow({
|
||||
onDelete,
|
||||
optimisticOpacity = 1,
|
||||
}: ItemRowProps) {
|
||||
const [hovered, setHovered] = useState(false)
|
||||
const [swipeRevealed, setSwipeRevealed] = useState(false)
|
||||
const [touchStartX, setTouchStartX] = useState<number | null>(null)
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const [swipeRevealed, setSwipeRevealed] = useState(false);
|
||||
const [touchStartX, setTouchStartX] = useState<number | null>(null);
|
||||
|
||||
// useSortable is always called (React hook rules), but listeners are only
|
||||
// attached to the handle button when isActive=true.
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({ id: item.id })
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: item.id,
|
||||
});
|
||||
|
||||
function handleCheckboxClick() {
|
||||
onCheck(item.id, !item.checked)
|
||||
onCheck(item.id, !item.checked);
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
setSwipeRevealed(false)
|
||||
onDelete(item.id)
|
||||
setSwipeRevealed(false);
|
||||
onDelete(item.id);
|
||||
}
|
||||
|
||||
function handleTouchStart(e: React.TouchEvent) {
|
||||
setTouchStartX(e.touches[0].clientX)
|
||||
setTouchStartX(e.touches[0].clientX);
|
||||
}
|
||||
|
||||
function handleTouchEnd(e: React.TouchEvent) {
|
||||
if (touchStartX === null) return
|
||||
const deltaX = touchStartX - e.changedTouches[0].clientX
|
||||
if (touchStartX === null) return;
|
||||
const deltaX = touchStartX - e.changedTouches[0].clientX;
|
||||
if (deltaX > 60) {
|
||||
// Swipe-left: reveal delete zone
|
||||
setSwipeRevealed(true)
|
||||
setSwipeRevealed(true);
|
||||
} else if (deltaX < -20) {
|
||||
// Swipe-right: hide delete zone
|
||||
setSwipeRevealed(false)
|
||||
setSwipeRevealed(false);
|
||||
}
|
||||
setTouchStartX(null)
|
||||
setTouchStartX(null);
|
||||
}
|
||||
|
||||
// D-14: transformToString + transition animates remote reorders arriving
|
||||
// via SSE (Plan 06). The transition fallback 'transform 150ms ease-out' applies
|
||||
// when dnd-kit's own transition is not active (i.e. for non-drag CSS changes).
|
||||
const transformStr = transformToString(transform)
|
||||
const computedTransition = transition ?? 'transform 150ms ease-out'
|
||||
const transformStr = transformToString(transform);
|
||||
const computedTransition = transition ?? 'transform 150ms ease-out';
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -129,7 +126,9 @@ export function ItemRow({
|
||||
gap: 'var(--space-2)',
|
||||
minHeight: '44px',
|
||||
padding: 'var(--space-2) var(--space-4)',
|
||||
background: isDragging ? 'var(--color-surface-raised, var(--color-surface))' : 'var(--color-surface)',
|
||||
background: isDragging
|
||||
? 'var(--color-surface-raised, var(--color-surface))'
|
||||
: 'var(--color-surface)',
|
||||
transform: swipeRevealed ? 'translateX(-80px)' : 'translateX(0)',
|
||||
transition: 'transform 200ms ease',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
@@ -190,13 +189,7 @@ export function ItemRow({
|
||||
}}
|
||||
>
|
||||
{item.checked && (
|
||||
<svg
|
||||
width="12"
|
||||
height="9"
|
||||
viewBox="0 0 12 9"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg width="12" height="9" viewBox="0 0 12 9" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M1 4L4.5 7.5L11 1"
|
||||
stroke="white"
|
||||
@@ -269,5 +262,5 @@ export function ItemRow({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user