-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: breadcrumb 컴포넌트 설치 * chore: dropdown menu 컴포넌트 설치 * feat: space breadcrumb 컴포넌트 작성 - 데이터를 가져오는 것에 따라 추가 수정 필요 * feat: space 페이지의 header도 yjs connection에 접근할 수 있도록 yjs provider 끌어올림 * chore: lockfile 재생성 * fix: 중복 로직 삭제 * refactor: breadcrumb 보여줄 아이템 계산 로직 가독성 개선
- Loading branch information
Showing
9 changed files
with
752 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { Link } from "react-router-dom"; | ||
|
||
import { | ||
Breadcrumb, | ||
BreadcrumbEllipsis, | ||
BreadcrumbItem, | ||
BreadcrumbLink, | ||
BreadcrumbList, | ||
BreadcrumbPage, | ||
BreadcrumbSeparator, | ||
} from "./ui/breadcrumb"; | ||
import { | ||
DropdownMenu, | ||
DropdownMenuContent, | ||
DropdownMenuItem, | ||
DropdownMenuTrigger, | ||
} from "./ui/dropdown-menu"; | ||
|
||
type SpacePath = { | ||
name: string; | ||
urlPath: string; | ||
}; | ||
|
||
function splitSpacePaths(spacePaths: SpacePath[], itemCountToDisplay: number) { | ||
// 처음 스페이스는 무조건 보여준다. | ||
const firstSpacePath = spacePaths[0]; | ||
|
||
// 중간 스페이스들은 ...으로 표시하고, 클릭 시 드롭다운 메뉴로 보여준다. | ||
const hiddenSpacePaths = spacePaths.slice(1, -2); | ||
|
||
// 마지막 (n-1)개 스페이스는 무조건 보여준다. | ||
const lastItemCount = Math.min(spacePaths.length, itemCountToDisplay - 1); | ||
const shownSpacePaths = spacePaths.slice(-lastItemCount); | ||
|
||
return [firstSpacePath, hiddenSpacePaths, shownSpacePaths] as const; | ||
} | ||
|
||
type HiddenItemsProps = { | ||
spacePaths: SpacePath[]; | ||
}; | ||
|
||
function HiddenItems({ spacePaths }: HiddenItemsProps) { | ||
return ( | ||
<> | ||
<BreadcrumbItem> | ||
<DropdownMenu> | ||
<DropdownMenuTrigger> | ||
<BreadcrumbEllipsis /> | ||
</DropdownMenuTrigger> | ||
<DropdownMenuContent> | ||
{spacePaths.map(({ name, urlPath }) => ( | ||
<DropdownMenuItem key={urlPath} asChild> | ||
<Link to={`/space/${urlPath}`}>{name}</Link> | ||
</DropdownMenuItem> | ||
))} | ||
</DropdownMenuContent> | ||
</DropdownMenu> | ||
</BreadcrumbItem> | ||
<BreadcrumbSeparator /> | ||
</> | ||
); | ||
} | ||
|
||
type SpaceBreadcrumbItemProps = { | ||
spacePath: SpacePath; | ||
isPage?: boolean; | ||
}; | ||
|
||
function SpaceBreadcrumbItem({ spacePath, isPage }: SpaceBreadcrumbItemProps) { | ||
if (isPage) { | ||
return ( | ||
<BreadcrumbItem> | ||
<BreadcrumbPage className="truncate max-w-20"> | ||
{spacePath.name} | ||
</BreadcrumbPage> | ||
</BreadcrumbItem> | ||
); | ||
} | ||
|
||
return ( | ||
<BreadcrumbItem> | ||
<BreadcrumbLink asChild> | ||
<Link className="truncate max-w-20" to={`/space/${spacePath.urlPath}`}> | ||
{spacePath.name} | ||
</Link> | ||
</BreadcrumbLink> | ||
<BreadcrumbSeparator /> | ||
</BreadcrumbItem> | ||
); | ||
} | ||
|
||
type SpaceBreadcrumbProps = { | ||
spacePaths: SpacePath[]; | ||
itemCountToDisplay?: number; | ||
}; | ||
|
||
export default function SpaceBreadcrumb({ | ||
spacePaths, | ||
itemCountToDisplay = 3, | ||
}: SpaceBreadcrumbProps) { | ||
// [처음, (...중간...), 직전, 현재] | ||
const [firstSpacePath, hiddenSpacePaths, shownSpacePaths] = splitSpacePaths( | ||
spacePaths, | ||
itemCountToDisplay, | ||
); | ||
|
||
return ( | ||
<Breadcrumb> | ||
<BreadcrumbList> | ||
{firstSpacePath && <SpaceBreadcrumbItem spacePath={firstSpacePath} />} | ||
{hiddenSpacePaths.length > 0 && ( | ||
<HiddenItems spacePaths={hiddenSpacePaths} /> | ||
)} | ||
{shownSpacePaths.map((spacePath, index) => ( | ||
<SpaceBreadcrumbItem | ||
key={spacePath.urlPath} | ||
spacePath={spacePath} | ||
isPage={index === shownSpacePaths.length - 1} | ||
/> | ||
))} | ||
</BreadcrumbList> | ||
</Breadcrumb> | ||
); | ||
} |
33 changes: 33 additions & 0 deletions
33
packages/frontend/src/components/space/SpacePageHeader.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import SpaceBreadcrumb from "../SpaceBreadcrumb"; | ||
import { Button } from "../ui/button"; | ||
|
||
export default function SpacePageHeader() { | ||
return ( | ||
<header className="fixed z-20 top-0 inset-x-0 h-16 bg-background/50 backdrop-blur-lg"> | ||
<div className="container mx-auto px-6 h-full flex flex-row items-center justify-between"> | ||
<div className="flex-1"> | ||
<SpaceBreadcrumb | ||
spacePaths={[ | ||
{ name: "하나", urlPath: "1" }, | ||
{ name: "셋", urlPath: "3" }, | ||
{ name: "넷", urlPath: "4" }, | ||
{ name: "다섯", urlPath: "5" }, | ||
{ name: "여섯", urlPath: "6" }, | ||
{ name: "일곱", urlPath: "7" }, | ||
{ name: "여덟", urlPath: "8" }, | ||
{ name: "아홉", urlPath: "9" }, | ||
{ | ||
name: "엄청 긴 제목을 가진 스페이스다아아아아아아아아아아아아아", | ||
urlPath: "2", | ||
}, | ||
{ name: "열", urlPath: "10" }, | ||
]} | ||
/> | ||
</div> | ||
<div className="flex-0"> | ||
<Button variant="outline">공유</Button> | ||
</div> | ||
</div> | ||
</header> | ||
); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import * as React from "react" | ||
import { Slot } from "@radix-ui/react-slot" | ||
import { ChevronRight, MoreHorizontal } from "lucide-react" | ||
|
||
import { cn } from "@/lib/utils" | ||
|
||
const Breadcrumb = React.forwardRef< | ||
HTMLElement, | ||
React.ComponentPropsWithoutRef<"nav"> & { | ||
separator?: React.ReactNode | ||
} | ||
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />) | ||
Breadcrumb.displayName = "Breadcrumb" | ||
|
||
const BreadcrumbList = React.forwardRef< | ||
HTMLOListElement, | ||
React.ComponentPropsWithoutRef<"ol"> | ||
>(({ className, ...props }, ref) => ( | ||
<ol | ||
ref={ref} | ||
className={cn( | ||
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
BreadcrumbList.displayName = "BreadcrumbList" | ||
|
||
const BreadcrumbItem = React.forwardRef< | ||
HTMLLIElement, | ||
React.ComponentPropsWithoutRef<"li"> | ||
>(({ className, ...props }, ref) => ( | ||
<li | ||
ref={ref} | ||
className={cn("inline-flex items-center gap-1.5", className)} | ||
{...props} | ||
/> | ||
)) | ||
BreadcrumbItem.displayName = "BreadcrumbItem" | ||
|
||
const BreadcrumbLink = React.forwardRef< | ||
HTMLAnchorElement, | ||
React.ComponentPropsWithoutRef<"a"> & { | ||
asChild?: boolean | ||
} | ||
>(({ asChild, className, ...props }, ref) => { | ||
const Comp = asChild ? Slot : "a" | ||
|
||
return ( | ||
<Comp | ||
ref={ref} | ||
className={cn("transition-colors hover:text-foreground", className)} | ||
{...props} | ||
/> | ||
) | ||
}) | ||
BreadcrumbLink.displayName = "BreadcrumbLink" | ||
|
||
const BreadcrumbPage = React.forwardRef< | ||
HTMLSpanElement, | ||
React.ComponentPropsWithoutRef<"span"> | ||
>(({ className, ...props }, ref) => ( | ||
<span | ||
ref={ref} | ||
role="link" | ||
aria-disabled="true" | ||
aria-current="page" | ||
className={cn("font-normal text-foreground", className)} | ||
{...props} | ||
/> | ||
)) | ||
BreadcrumbPage.displayName = "BreadcrumbPage" | ||
|
||
const BreadcrumbSeparator = ({ | ||
children, | ||
className, | ||
...props | ||
}: React.ComponentProps<"li">) => ( | ||
<li | ||
role="presentation" | ||
aria-hidden="true" | ||
className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)} | ||
{...props} | ||
> | ||
{children ?? <ChevronRight />} | ||
</li> | ||
) | ||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator" | ||
|
||
const BreadcrumbEllipsis = ({ | ||
className, | ||
...props | ||
}: React.ComponentProps<"span">) => ( | ||
<span | ||
role="presentation" | ||
aria-hidden="true" | ||
className={cn("flex h-9 w-9 items-center justify-center", className)} | ||
{...props} | ||
> | ||
<MoreHorizontal className="h-4 w-4" /> | ||
<span className="sr-only">More</span> | ||
</span> | ||
) | ||
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis" | ||
|
||
export { | ||
Breadcrumb, | ||
BreadcrumbList, | ||
BreadcrumbItem, | ||
BreadcrumbLink, | ||
BreadcrumbPage, | ||
BreadcrumbSeparator, | ||
BreadcrumbEllipsis, | ||
} |
Oops, something went wrong.