Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

좌측 내비게이션에 외부 링크 기능 구현 #474

Merged
merged 1 commit into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/layouts/sidebar/DocsNavMenu.astro
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const serverSystemVersion = readServerSystemVersion();
</li>
);
}
if (item.slug === "===") {
if (item.path === "===") {
return (
<li>
<hr class="mx-2 my-4" />
Expand Down
40 changes: 27 additions & 13 deletions src/layouts/sidebar/LeftSidebarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,41 @@ import type { SystemVersion } from "~/type";
function LeftSidebarItem(props: NavMenuPage) {
const systemVersion = useSystemVersion();
if (props.items.length > 0) return <FolderLink {...props} />;
const { title, slug } = props;
const { title, path } = props;
const pageSlug = slugSignal.value;
const isActive = pageSlug === slug;
const href = `/docs${slug}`;
const isActive = pageSlug === path;
const [href, isExternal] = (() => {
try {
return [new URL(path).toString(), true];
} catch (e) {
return [`/docs${path}`, false];
}
})();
return (
<JustLink
title={title}
href={href}
isExternal={isExternal}
isActive={isActive}
systemVersion={props.systemVersion ?? systemVersion}
/>
);
}
export default LeftSidebarItem;

function FolderLink({ title, slug, items, systemVersion }: NavMenuPage) {
const openSignal = useComputed(() => !!navOpenStatesSignal.value[slug]);
function FolderLink({ title, path, items, systemVersion }: NavMenuPage) {
const openSignal = useComputed(() => !!navOpenStatesSignal.value[path]);
const open = openSignal.value;
const pageSlug = slugSignal.value;
const isActive = pageSlug === slug;
const isActive = pageSlug === path;
return (
<div data-system-version={systemVersion}>
<div
class={`flex ${getLinkStyle(isActive)} pr-0`}
data-active={isActive && "active"} // true로 지정하면 SSR시에는 값 없이 attr key만 들어감
>
<a
href={["/docs", slug, systemVersion && `?v=${systemVersion}`]
href={["/docs", path, systemVersion && `?v=${systemVersion}`]
.filter(Boolean)
.join("")}
class="grow"
Expand All @@ -47,7 +54,7 @@ function FolderLink({ title, slug, items, systemVersion }: NavMenuPage) {
onClick={() => {
navOpenStatesSignal.value = {
...navOpenStatesSignal.value,
[slug]: !open,
[path]: !open,
};
}}
>
Expand All @@ -63,7 +70,7 @@ function FolderLink({ title, slug, items, systemVersion }: NavMenuPage) {
<div class={`${open ? "block" : "hidden"} pl-2`}>
<ul class="flex flex-col gap-1 border-l pl-2">
{items.map((item) => (
<li key={item.slug} data-system-version={item.systemVersion}>
<li key={item.path} data-system-version={item.systemVersion}>
<LeftSidebarItem {...item} />
</li>
))}
Expand All @@ -76,6 +83,7 @@ function FolderLink({ title, slug, items, systemVersion }: NavMenuPage) {
export interface JustLinkProps {
title: string;
href: string;
isExternal?: boolean;
isActive: boolean;
systemVersion?: SystemVersion | undefined;
event?: {
Expand All @@ -86,19 +94,21 @@ export interface JustLinkProps {
export function JustLink({
title,
href,
isExternal,
isActive,
systemVersion,
event,
}: JustLinkProps) {
return (
<a
data-system-version={systemVersion}
href={systemVersion ? `${href}?v=${systemVersion}` : href}
href={!isExternal && systemVersion ? `${href}?v=${systemVersion}` : href}
class={getLinkStyle(isActive)}
data-active={isActive && "active"}
onClick={() => event && trackEvent(event.name, event.props)}
target={isExternal ? "_blank" : "_self"}
>
<LinkTitle title={title} />
<LinkTitle title={title} isExternal={isExternal} />
</a>
);
}
Expand All @@ -113,11 +123,15 @@ export function getLinkStyle(isActive: boolean): string {

interface LinkTitleProps {
title: string;
isExternal?: boolean | undefined;
}
function LinkTitle({ title }: LinkTitleProps) {
function LinkTitle({ title, isExternal }: LinkTitleProps) {
return (
<span class="flex gap-2 py-1">
<span class="flex items-center gap-2 py-1">
<span>{title || <span class="text-red">(unknown page)</span>}</span>
{isExternal && (
<i class="i-ic-baseline-open-in-new inline-block opacity-70" />
)}
</span>
);
}
29 changes: 22 additions & 7 deletions src/state/server-only/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface NavMenu {
export type NavMenuItem = NavMenuPage | NavMenuGroup;
export interface NavMenuPage {
type: "page";
slug: string;
path: string;
title: string;
items: NavMenuPage[];
systemVersion?: SystemVersion | undefined;
Expand All @@ -50,15 +50,15 @@ export const navMenuItemsKo = toNavMenuItems(
export const navMenu = { en: navMenuItemsEn, ko: navMenuItemsKo };

export interface NavMenuSystemVersions {
[slug: string]: SystemVersion;
[path: string]: SystemVersion;
}
export function calcNavMenuSystemVersions(
navMenuItems: NavMenuItem[],
): NavMenuSystemVersions {
const result: NavMenuSystemVersions = {};
for (const item of iterNavMenuItems(navMenuItems)) {
if (!("slug" in item)) continue;
if (item.systemVersion) result[item.slug] = item.systemVersion;
if (!("path" in item)) continue;
if (item.systemVersion) result[item.path] = item.systemVersion;
}
return result;
}
Expand All @@ -84,7 +84,7 @@ function* iterNavMenuAncestors(
if (item.type === "group") {
yield* iterNavMenuAncestors(item.items, ancestors);
} else if (item.type === "page") {
const { slug, items } = item;
const { path: slug, items } = item;
yield { slug, ancestors };
yield* iterNavMenuAncestors(items, [...ancestors, slug]);
}
Expand All @@ -107,7 +107,7 @@ function toNavMenuItems(
if (typeof item === "string") {
return {
type: "page",
slug: item,
path: item,
title: frontmatters[item]?.["title"] || "",
items: [],
systemVersion,
Expand All @@ -116,7 +116,7 @@ function toNavMenuItems(
const _systemVersion = item.systemVersion || systemVersion;
return {
type: "page",
slug: item.slug,
path: item.slug,
title: frontmatters[item.slug]?.["title"] || "",
items: item.items
? (toNavMenuItems(
Expand All @@ -127,6 +127,21 @@ function toNavMenuItems(
: [],
systemVersion: _systemVersion,
};
} else if ("href" in item) {
const _systemVersion = item.systemVersion || systemVersion;
return {
type: "page",
path: item.href,
title: item.label,
items: item.items
? (toNavMenuItems(
item.items,
frontmatters,
_systemVersion,
) as NavMenuPage[])
: [],
systemVersion: _systemVersion,
};
} else {
const _systemVersion = item.systemVersion || systemVersion;
return {
Expand Down
12 changes: 11 additions & 1 deletion src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@ export type SystemVersion = z.infer<typeof SystemVersion>;
export type YamlNavMenuToplevelItem =
| YamlNavMenuPageSugar
| YamlNavMenuPage
| YamlNavMenuExternalPage
| YamlNavMenuGroup;
export type YamlNavMenuItem = YamlNavMenuPageSugar | YamlNavMenuPage;
export type YamlNavMenuItem =
| YamlNavMenuPageSugar
| YamlNavMenuPage
| YamlNavMenuExternalPage;
type YamlNavMenuPageSugar = string;
interface YamlNavMenuPage {
slug: string;
items: YamlNavMenuItem[];
systemVersion?: SystemVersion;
}
interface YamlNavMenuExternalPage {
label: string;
href: string;
items?: YamlNavMenuItem[];
systemVersion?: SystemVersion;
}
interface YamlNavMenuGroup {
label: string;
items: YamlNavMenuItem[];
Expand Down