Skip to content

Commit bed0dea

Browse files
ImTotemclaude
andcommitted
feat: Sheet 드래그 리사이즈 + 단축URL 말줄임표 + 컬럼 너비 조정
- SheetContent에 resizable prop 추가 — 좌측 드래그 핸들로 너비 조절 - LinkSheet, MemberSheet에 resizable 적용 - 단축 URL을 break-all 대신 truncate(말줄임표)로 변경 - 제목 컬럼 12%, 단축 URL 컬럼 30%로 너비 재배분 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent df9c575 commit bed0dea

4 files changed

Lines changed: 58 additions & 4 deletions

File tree

src/components/common/MemberSheet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function MemberSheet({ memberId, open, onOpenChange }: MemberSheetProps)
2323

2424
return (
2525
<Sheet open={open} onOpenChange={onOpenChange}>
26-
<SheetContent side="right" className="overflow-y-auto">
26+
<SheetContent side="right" resizable className="overflow-y-auto">
2727
{isLoading && (
2828
<>
2929
<SheetHeader>

src/components/ui/sheet.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,80 @@ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
3636
)
3737
}
3838

39+
function ResizeHandle({
40+
side,
41+
onResize,
42+
}: {
43+
side: "left" | "right"
44+
onResize: (delta: number) => void
45+
}) {
46+
const handlePointerDown = (e: React.PointerEvent) => {
47+
e.preventDefault()
48+
const startX = e.clientX
49+
const onMove = (ev: PointerEvent) => {
50+
const delta = side === "right" ? startX - ev.clientX : ev.clientX - startX
51+
onResize(delta)
52+
}
53+
const onUp = () => {
54+
document.removeEventListener("pointermove", onMove)
55+
document.removeEventListener("pointerup", onUp)
56+
}
57+
document.addEventListener("pointermove", onMove)
58+
document.addEventListener("pointerup", onUp)
59+
}
60+
61+
return (
62+
<div
63+
className={cn(
64+
"absolute inset-y-0 z-50 w-1 cursor-col-resize hover:bg-primary/20 active:bg-primary/30",
65+
side === "right" ? "left-0" : "right-0"
66+
)}
67+
onPointerDown={handlePointerDown}
68+
/>
69+
)
70+
}
71+
3972
function SheetContent({
4073
className,
4174
children,
4275
side = "right",
4376
showCloseButton = true,
77+
resizable = false,
78+
defaultWidth = 384,
79+
minWidth = 280,
80+
maxWidth = 800,
4481
...props
4582
}: SheetPrimitive.Popup.Props & {
4683
side?: "top" | "right" | "bottom" | "left"
4784
showCloseButton?: boolean
85+
resizable?: boolean
86+
defaultWidth?: number
87+
minWidth?: number
88+
maxWidth?: number
4889
}) {
90+
const [width, setWidth] = React.useState(defaultWidth)
91+
const canResize = resizable && (side === "left" || side === "right")
92+
4993
return (
5094
<SheetPortal>
5195
<SheetOverlay />
5296
<SheetPrimitive.Popup
5397
data-slot="sheet-content"
5498
data-side={side}
99+
style={canResize ? { width: `${width}px`, maxWidth: "none" } : undefined}
55100
className={cn(
56-
"fixed z-50 flex flex-col gap-4 bg-background bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
101+
"fixed z-50 flex flex-col gap-4 bg-background bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem]",
102+
!canResize && "data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
57103
className
58104
)}
59105
{...props}
60106
>
107+
{canResize && (
108+
<ResizeHandle
109+
side={side}
110+
onResize={(delta) => setWidth((w) => Math.min(maxWidth, Math.max(minWidth, w + delta)))}
111+
/>
112+
)}
61113
{children}
62114
{showCloseButton && (
63115
<SheetPrimitive.Close

src/pages/links/LinkSheet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function LinkSheet({ linkId, open, onOpenChange, onEdit }: LinkSheetProps
7878
return (
7979
<>
8080
<Sheet open={open} onOpenChange={onOpenChange}>
81-
<SheetContent side="right" className="overflow-y-auto">
81+
<SheetContent side="right" resizable className="overflow-y-auto">
8282
{isLoading && (
8383
<>
8484
<SheetHeader>

src/pages/links/LinksPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ export function LinksPage() {
6666
sortable: true,
6767
filterType: "text",
6868
filterParamKey: "title",
69+
className: "w-[12%]",
6970
},
7071
{
7172
id: "code",
7273
header: "단축 URL",
7374
cell: (l) => (
7475
<Button
7576
variant="link"
76-
className="h-auto whitespace-normal break-all p-0 text-left text-blue-600"
77+
className="h-auto max-w-full truncate p-0 text-blue-600"
7778
onClick={(e) => {
7879
e.stopPropagation();
7980
navigator.clipboard.writeText(shortUrl(l.code));
@@ -86,6 +87,7 @@ export function LinksPage() {
8687
sortable: true,
8788
filterType: "text",
8889
filterParamKey: "code",
90+
className: "w-[30%]",
8991
},
9092
{
9193
id: "creatorId",

0 commit comments

Comments
 (0)