add proper responsiveness

This commit is contained in:
Ishaan Gupta 2026-05-24 00:36:51 +05:30
parent 9a410f68c0
commit 092e80ca55

View file

@ -1,6 +1,7 @@
"use client"
import { useState, useEffect, useRef, useCallback } from "react"
import { AnimatePresence, motion } from "motion/react"
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
import type { z } from "zod"
import { cn } from "@lib/utils"
@ -194,6 +195,7 @@ function TimelineCard({
isSelected = false,
onToggleSelection,
indent = false,
isLast = false,
}: {
doc: DocumentWithMemories
onOpenDocument: (doc: DocumentWithMemories) => void
@ -201,6 +203,7 @@ function TimelineCard({
isSelected?: boolean
onToggleSelection?: (doc: DocumentWithMemories) => void
indent?: boolean
isLast?: boolean
}) {
const preview = getPreviewText(doc)
const typeLabel = doc.type
@ -221,10 +224,11 @@ function TimelineCard({
<button
type="button"
className={cn(
"relative w-full text-left px-4 py-3 cursor-pointer transition-colors",
"relative w-full text-left px-3 py-3 cursor-pointer transition-colors sm:px-4",
indent
? "bg-transparent hover:bg-white/[0.04]"
: "rounded-2xl border border-[#252B35] bg-[#1B1F24] hover:bg-[#21262D]",
? "bg-[#121820]/95 hover:bg-[#17202A]"
: "rounded-xl border border-[#252B35] bg-[#1B1F24] hover:bg-[#21262D] sm:rounded-2xl",
indent && !isLast && "border-b border-[#252B35]/70",
isSelectionMode && canSelect && "pl-10",
isSelectionMode && isSelected && "border-[#369BFD]/70 bg-[#00173C]/45",
dmSansClassName(),
@ -349,7 +353,7 @@ function GroupCard({
<div>
<div
className={cn(
"flex w-full items-stretch rounded-2xl border border-[#252B35] bg-[#1B1F24] transition-colors hover:bg-[#21262D]",
"flex w-full items-stretch rounded-xl border border-[#252B35] bg-[#1B1F24] transition-colors hover:bg-[#21262D] sm:rounded-2xl",
isExpanded && "rounded-b-none border-b-transparent",
isSelectionMode &&
(isGroupSelected || isGroupPartial) &&
@ -374,14 +378,16 @@ function GroupCard({
<button
type="button"
className={cn(
"flex min-w-0 flex-1 cursor-pointer items-center justify-between gap-3 py-3 pr-4 text-left",
isSelectionMode && selectableDocs.length > 0 ? "pl-0" : "pl-4",
"flex min-w-0 flex-1 cursor-pointer items-start justify-between gap-2 py-3 pr-3 text-left sm:items-center sm:gap-3 sm:pr-4",
isSelectionMode && selectableDocs.length > 0
? "pl-0"
: "pl-3 sm:pl-4",
dmSansClassName(),
)}
onClick={onToggle}
aria-expanded={isExpanded}
>
<div className="flex items-center gap-2.5 min-w-0 flex-1">
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-x-2.5 gap-y-1">
<DocumentIcon
type={firstDoc.type}
source={firstDoc.source ?? undefined}
@ -392,13 +398,13 @@ function GroupCard({
{countLabel}
</span>
{preview && (
<span className="text-[12px] text-white/35 truncate">
<span className="basis-full text-[12px] leading-relaxed text-white/35 line-clamp-2 sm:basis-auto sm:truncate sm:leading-normal">
· {preview}
</span>
)}
{totalMemories > 0 && (
<span
className="text-[11px] font-medium shrink-0 ml-auto"
className="shrink-0 text-[11px] font-medium sm:ml-auto"
style={{
background:
"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
@ -414,33 +420,55 @@ function GroupCard({
<ChevronDownIcon
className={cn(
"size-3.5 text-white/20 shrink-0 transition-transform duration-200",
"mt-0.5 size-3.5 text-white/20 shrink-0 transition-transform duration-200 sm:mt-0",
isExpanded && "rotate-180",
)}
/>
</button>
</div>
{isExpanded && (
<div
id={`group-${expandKey}`}
className="border border-t-0 border-[#252B35] rounded-b-2xl overflow-hidden divide-y divide-[#252B35]"
>
{group.docs.map((doc) => (
<TimelineCard
key={doc.id}
doc={doc}
onOpenDocument={onOpenDocument}
isSelectionMode={isSelectionMode}
isSelected={doc.id ? selectedDocumentIds.has(doc.id) : false}
onToggleSelection={(doc) => {
if (doc.id) onToggleSelection?.(doc.id)
}}
indent
/>
))}
</div>
)}
<AnimatePresence initial={false}>
{isExpanded && (
<motion.div
id={`group-${expandKey}`}
className="overflow-hidden rounded-b-2xl border border-t-0 border-[#252B35] bg-[#121820]/95 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
initial={{ height: 0, opacity: 0, y: -4 }}
animate={{ height: "auto", opacity: 1, y: 0 }}
exit={{ height: 0, opacity: 0, y: -3 }}
transition={{
height: { duration: 0.28, ease: [0.4, 0, 0.2, 1] },
opacity: { duration: 0.18 },
y: { duration: 0.22, ease: [0.4, 0, 0.2, 1] },
}}
>
{group.docs.map((doc, index) => (
<motion.div
key={doc.id}
initial={{ opacity: 0, y: -4 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -3 }}
transition={{
duration: 0.18,
delay: Math.min(index * 0.025, 0.12),
ease: [0.4, 0, 0.2, 1],
}}
>
<TimelineCard
doc={doc}
onOpenDocument={onOpenDocument}
isSelectionMode={isSelectionMode}
isSelected={doc.id ? selectedDocumentIds.has(doc.id) : false}
onToggleSelection={(doc) => {
if (doc.id) onToggleSelection?.(doc.id)
}}
indent
isLast={index === group.docs.length - 1}
/>
</motion.div>
))}
</motion.div>
)}
</AnimatePresence>
</div>
)
}
@ -506,57 +534,121 @@ export function TimelineView({
return (
<div
className={cn(
"w-full max-w-[780px] mx-auto py-4 pb-12 space-y-6",
"w-full max-w-[820px] mx-auto py-3 pb-12 space-y-5 sm:py-4 sm:space-y-7",
dmSansClassName(),
)}
>
{periodGroups.map((period) => (
<div key={period.label} className="grid grid-cols-[88px_1fr] gap-x-4">
<div className="pt-3 text-right shrink-0">
<span className="text-[10px] text-white/30 font-medium uppercase tracking-[0.15em] leading-none">
{period.label}
</span>
</div>
{periodGroups.map((period, periodIndex) => {
const periodHasExpandedGroup = period.typeGroups.some((group) =>
expandedGroups.has(`${period.label}::${group.categoryInfo.key}`),
)
<div className="space-y-1.5 min-w-0">
{period.typeGroups.map((group) => {
const expandKey = `${period.label}::${group.categoryInfo.key}`
return (
<div
key={period.label}
className="grid grid-cols-[22px_minmax(0,1fr)] gap-x-2 gap-y-2 sm:grid-cols-[96px_24px_minmax(0,1fr)] sm:gap-x-3 sm:gap-y-0"
>
<div className="col-start-2 row-start-1 shrink-0 pt-0 sm:col-start-1 sm:row-start-1 sm:pt-3 sm:text-right">
<span className="inline-flex rounded-full border border-[#252B35] bg-[#0D121A] px-2.5 py-1 text-[10px] font-medium uppercase leading-none tracking-[0.15em] text-white/45 sm:border-transparent sm:bg-transparent sm:px-0 sm:py-0 sm:text-white/30">
{period.label}
</span>
</div>
if (group.docs.length === 1) {
const doc = group.docs[0]
if (!doc) return null
<div className="relative col-start-1 row-start-1 row-span-2 flex justify-center sm:col-start-2 sm:row-start-1 sm:row-span-1">
<div
className={cn(
"absolute top-4 bottom-[-20px] w-px bg-[#1F2835] sm:top-5 sm:bottom-[-28px]",
periodIndex === periodGroups.length - 1 && "bottom-2",
)}
/>
<motion.div
className={cn(
"absolute top-4 bottom-[-20px] w-px origin-top bg-linear-to-b from-[#369BFD] via-[#369BFD]/70 to-transparent sm:top-5 sm:bottom-[-28px]",
periodIndex === periodGroups.length - 1 && "bottom-2",
)}
initial={false}
animate={{
scaleY: periodHasExpandedGroup ? 1 : 0,
opacity: periodHasExpandedGroup ? 1 : 0,
}}
transition={{ duration: 0.34, ease: [0.4, 0, 0.2, 1] }}
/>
<motion.div
className="relative mt-1.5 flex size-3 items-center justify-center rounded-full border border-[#369BFD]/35 bg-[#0D121A] shadow-[0_0_0_4px_rgba(54,155,253,0.06)] sm:mt-3"
initial={false}
animate={{
borderColor: periodHasExpandedGroup
? "rgba(54,155,253,0.8)"
: "rgba(54,155,253,0.35)",
boxShadow: periodHasExpandedGroup
? "0 0 0 7px rgba(54,155,253,0.09)"
: "0 0 0 4px rgba(54,155,253,0.06)",
}}
transition={{ duration: 0.28, ease: [0.4, 0, 0.2, 1] }}
>
<AnimatePresence>
{periodHasExpandedGroup && (
<motion.div
className="absolute inset-[-6px] rounded-full border border-[#369BFD]/35"
initial={{ scale: 0.6, opacity: 0.8 }}
animate={{ scale: 1.45, opacity: 0 }}
exit={{ opacity: 0 }}
transition={{
duration: 0.52,
ease: [0.2, 0.8, 0.2, 1],
}}
/>
)}
</AnimatePresence>
<motion.div
className="size-1.5 rounded-full bg-[#369BFD]"
initial={false}
animate={{ scale: periodHasExpandedGroup ? 1.15 : 1 }}
transition={{ duration: 0.24, ease: [0.4, 0, 0.2, 1] }}
/>
</motion.div>
</div>
<div className="col-start-2 row-start-2 min-w-0 space-y-1.5 sm:col-start-3 sm:row-start-1">
{period.typeGroups.map((group) => {
const expandKey = `${period.label}::${group.categoryInfo.key}`
if (group.docs.length === 1) {
const doc = group.docs[0]
if (!doc) return null
return (
<TimelineCard
key={expandKey}
doc={doc}
onOpenDocument={onOpenDocument}
isSelectionMode={isSelectionMode}
isSelected={
doc.id ? selectedDocumentIds.has(doc.id) : false
}
onToggleSelection={handleTimelineCardSelection}
/>
)
}
return (
<TimelineCard
<GroupCard
key={expandKey}
doc={doc}
group={group}
expandKey={expandKey}
isExpanded={expandedGroups.has(expandKey)}
onToggle={() => toggleGroup(expandKey)}
onOpenDocument={onOpenDocument}
isSelectionMode={isSelectionMode}
isSelected={
doc.id ? selectedDocumentIds.has(doc.id) : false
}
onToggleSelection={handleTimelineCardSelection}
selectedDocumentIds={selectedDocumentIds}
onToggleSelection={onToggleSelection}
/>
)
}
return (
<GroupCard
key={expandKey}
group={group}
expandKey={expandKey}
isExpanded={expandedGroups.has(expandKey)}
onToggle={() => toggleGroup(expandKey)}
onOpenDocument={onOpenDocument}
isSelectionMode={isSelectionMode}
selectedDocumentIds={selectedDocumentIds}
onToggleSelection={onToggleSelection}
/>
)
})}
})}
</div>
</div>
</div>
))}
)
})}
<div ref={sentinelRef} className="h-1" />
</div>