diff --git a/app.config.ts b/app.config.ts
index 314fe1172..1b2db31c6 100644
--- a/app.config.ts
+++ b/app.config.ts
@@ -1,21 +1,29 @@
import { readFile } from "node:fs/promises";
-import path from "node:path";
import yaml from "@rollup/plugin-yaml";
import { defineConfig } from "@solidjs/start/config";
import vinxiMdxPkg from "@vinxi/plugin-mdx";
+import rehypeSlug from "rehype-slug";
import remarkFrontmatter from "remark-frontmatter";
import unocss from "unocss/vite";
import { imagetools } from "vite-imagetools";
// 현재 Vinxi export 설정 이슈로 파일을 직접 가져와야 함
import type { CustomizableConfig } from "./node_modules/vinxi/dist/types/lib/vite-dev";
+import { indexFilesMapping } from "./src/misc/contentIndex";
const { default: vinxiMdx } = vinxiMdxPkg;
export default defineConfig({
server: {
preset: "vercel",
+ prerender: {
+ routes: [
+ ...Object.keys(indexFilesMapping).map(
+ (fileName) => `/content-index/${fileName}.json`,
+ ),
+ ],
+ },
},
extensions: ["ts", "tsx", "mdx"],
vite: () =>
@@ -34,6 +42,7 @@ export default defineConfig({
jsxImportSource: "solid-js",
providerImportSource: "solid-mdx",
remarkPlugins: [remarkFrontmatter],
+ rehypePlugins: [rehypeSlug],
}),
imagetools({
defaultDirectives: (url) => {
diff --git a/package.json b/package.json
index c65784bbd..cb3cf57bc 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,13 @@
"lint": "NODE_OPTIONS=\"$NODE_OPTIONS --loader ts-node/esm\" eslint .",
"eslint": "NODE_OPTIONS=\"$NODE_OPTIONS --loader ts-node/esm\" eslint"
},
- "imports": {
- "#content": "./src/content/__generated__/index.ts"
- },
+ "imports": {
+ "#content": "./src/content/__generated__/index.ts",
+ "#server-only": {
+ "browser": "./src/misc/server-only/browserError.ts",
+ "default": "./src/misc/server-only/serverNoop.ts"
+ }
+ },
"dependencies": {
"@eslint/js": "^8.57.0",
"@iconify-json/ic": "^1.1.17",
@@ -72,6 +76,7 @@
"monaco-editor": "^0.47.0",
"pretendard": "^1.3.9",
"prettier": "^3.2.5",
+ "rehype-slug": "^6.0.0",
"rehype-stringify": "^10.0.0",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 693112a90..0d0b1940b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -187,6 +187,9 @@ importers:
prettier:
specifier: ^3.2.5
version: 3.2.5
+ rehype-slug:
+ specifier: ^6.0.0
+ version: 6.0.0
rehype-stringify:
specifier: ^10.0.0
version: 10.0.0
@@ -3162,6 +3165,9 @@ packages:
resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==}
hasBin: true
+ github-slugger@2.0.0:
+ resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -3266,6 +3272,9 @@ packages:
hast-util-from-parse5@8.0.1:
resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
+ hast-util-heading-rank@3.0.0:
+ resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
+
hast-util-parse-selector@4.0.0:
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
@@ -4538,6 +4547,9 @@ packages:
resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
engines: {node: '>= 0.4'}
+ rehype-slug@6.0.0:
+ resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==}
+
rehype-stringify@10.0.0:
resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==}
@@ -8705,6 +8717,8 @@ snapshots:
pathe: 1.1.2
tar: 6.2.1
+ github-slugger@2.0.0: {}
+
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@@ -8837,6 +8851,10 @@ snapshots:
vfile-location: 5.0.2
web-namespaces: 2.0.1
+ hast-util-heading-rank@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
hast-util-parse-selector@4.0.0:
dependencies:
'@types/hast': 3.0.4
@@ -10697,6 +10715,14 @@ snapshots:
es-errors: 1.3.0
set-function-name: 2.0.2
+ rehype-slug@6.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ github-slugger: 2.0.0
+ hast-util-heading-rank: 3.0.0
+ hast-util-to-string: 3.0.0
+ unist-util-visit: 5.0.0
+
rehype-stringify@10.0.0:
dependencies:
'@types/hast': 3.0.4
diff --git a/src/app.tsx b/src/app.tsx
index c01c6b265..c6ddf4756 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -3,6 +3,7 @@ import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { Suspense } from "solid-js";
+import { NotFoundBoundary } from "./components/404";
import Trackers from "./layouts/trackers/Trackers";
export default function App() {
@@ -15,7 +16,9 @@ export default function App() {
- {props.children}
+
+ {props.children}
+
)}
>
diff --git a/src/components/404.tsx b/src/components/404.tsx
new file mode 100644
index 000000000..24af3b9cc
--- /dev/null
+++ b/src/components/404.tsx
@@ -0,0 +1,58 @@
+import { HttpStatusCode } from "@solidjs/start";
+import { ErrorBoundary, type JSXElement } from "solid-js";
+
+import portoneGradientBg from "~/assets/portone-gradient-bg.png?imagetools";
+import portoneLogoWhite from "~/assets/portone-logo-white.png?imagetools";
+
+import Picture from "./Picture";
+
+export class NotFoundError extends Error {
+ constructor() {
+ super("Not Found");
+ this.name = "NotFoundError";
+ }
+}
+
+export function NotFoundBoundary(props: { children: JSXElement }) {
+ return (
+ {
+ if (err instanceof Error && err.name === "NotFoundError") {
+ return ;
+ }
+ throw err;
+ }}
+ >
+ {props.children}
+
+ );
+}
+
+function NotFoundPage() {
+ return (
+ <>
+
+
+
+
+
+
404
+
+ 페이지를 찾을 수 없습니다
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/gitbook/ContentRef.astro b/src/components/gitbook/ContentRef.astro
deleted file mode 100644
index 6145f502d..000000000
--- a/src/components/gitbook/ContentRef.astro
+++ /dev/null
@@ -1,24 +0,0 @@
----
-import * as path from "node:path";
-
-import { getEntry } from "astro:content";
-
-interface Props {
- slug: string;
-}
-const { slug } = Astro.props;
-const entry = await getEntry("docs", slug.slice(1));
-const frontmatter = entry?.data;
-const title = frontmatter?.title;
----
-
-{
- title && (
-
-
- {title}
-
-
-
- )
-}
diff --git a/src/components/gitbook/ContentRef.tsx b/src/components/gitbook/ContentRef.tsx
new file mode 100644
index 000000000..4c13da683
--- /dev/null
+++ b/src/components/gitbook/ContentRef.tsx
@@ -0,0 +1,31 @@
+import { cache, createAsync } from "@solidjs/router";
+import { createMemo, Show } from "solid-js";
+
+interface Props {
+ slug: string;
+}
+
+const getEntryData = cache(async (slug: string) => {
+ "use server";
+
+ const { docs } = await import("#content");
+ return (docs as Record)[slug]
+ ?.frontmatter;
+}, "docs/entry");
+
+export default function ContentRef(props: Props) {
+ const slug = createMemo(() => props.slug.slice(1));
+ const entryData = createAsync(() => getEntryData(slug()));
+ const title = createMemo(() => entryData()?.title);
+
+ return (
+
+
+
+ {title()}
+
+
+
+
+ );
+}
diff --git a/src/components/gitbook/VersionGate.astro b/src/components/gitbook/VersionGate.astro
deleted file mode 100644
index 3810603eb..000000000
--- a/src/components/gitbook/VersionGate.astro
+++ /dev/null
@@ -1,18 +0,0 @@
----
-import { readServerSystemVersion } from "~/state/system-version/server";
-
-import Impl from "./VersionGate";
-
-interface Props {
- default: "v1" | "v2";
-}
-
-const { default: version } = Astro.props;
-const serverSystemVersion = readServerSystemVersion();
----
-
-
- {Astro.slots.has("v1") ? : null}
- {Astro.slots.has("v2") ? : null}
-
-
diff --git a/src/components/gitbook/VersionGate.tsx b/src/components/gitbook/VersionGate.tsx
index 182f7822d..367c95ff2 100644
--- a/src/components/gitbook/VersionGate.tsx
+++ b/src/components/gitbook/VersionGate.tsx
@@ -1,48 +1,14 @@
-import type React from "react";
+import { type JSXElement, Show } from "solid-js";
-import { useSystemVersion } from "#state/system-version";
-import { useServerFallback } from "~/misc/useServerFallback";
-import type { SystemVersion } from "~/type";
+import { useSystemVersion } from "~/state/system-version";
interface Props {
- serverSystemVersion: SystemVersion;
- default?: SystemVersion;
- v1?: React.ReactNode;
- v2?: React.ReactNode;
- children?: React.ReactNode;
+ v: "v1" | "v2";
+ children?: JSXElement;
}
-const isEmptyStaticHtml = (node: React.ReactNode) => {
- if (!(node && typeof node === "object" && "props" in node)) return false;
- const props = node.props as unknown;
-
- return Boolean(
- props &&
- typeof props === "object" &&
- "value" in props &&
- !JSON.parse(JSON.stringify(props.value)),
- );
-};
-
export default function VersionGate(props: Props) {
- const systemVersion = useServerFallback(
- useSystemVersion(),
- props.serverSystemVersion,
- );
-
- const hasV1 = !isEmptyStaticHtml(props.v1);
- const hasV2 = !isEmptyStaticHtml(props.v2);
- const v1 = hasV1 ? props.v1 : null;
- const v2 = hasV2 ? props.v2 : null;
+ const { systemVersion } = useSystemVersion();
- return (
- <>
- {
- {
- v1: props.default === "v1" ? v1 ?? props.children : v1,
- v2: props.default === "v2" ? v2 ?? props.children : v2,
- }[systemVersion]
- }
- >
- );
+ return {props.children};
}
diff --git a/src/content/docs/ko/readme.mdx b/src/content/docs/ko/readme.mdx
index cf89a019e..3201ccb0b 100644
--- a/src/content/docs/ko/readme.mdx
+++ b/src/content/docs/ko/readme.mdx
@@ -4,20 +4,18 @@ description: 포트원 결제 연동 가이드입니다. 빠른 시간 안에
targetVersions: ["v1", "v2"]
---
-import EasyGuideLink from "~/components/EasyGuideLink.tsx";
-import ContentRef from "~/components/gitbook/ContentRef.astro";
-import VersionGate from "~/components/gitbook/VersionGate.astro";
+import EasyGuideLink from "~/components/EasyGuideLink";
+import ContentRef from "~/components/gitbook/ContentRef";
+import VersionGate from "~/components/gitbook/VersionGate";
-
+
+ ## 포트원 V2 신모듈 알아보기
-## 포트원 V2 신모듈 알아보기
-
-새로워진 포트원 V2 신모듈에 대해 소개합니다.
-
-
+ 새로워진 포트원 V2 신모듈에 대해 소개합니다.
+
## 연동 준비하기
@@ -26,96 +24,90 @@ import VersionGate from "~/components/gitbook/VersionGate.astro";
-
-
-
-## 결제창 연동하기
-
-해당 가이드를 통해 결제창을 손쉽게 연동할 수 있습니다.
-
-
+
+ ## 결제창 연동하기
-
+ 해당 가이드를 통해 결제창을 손쉽게 연동할 수 있습니다.
-
+
-
-
+
-## 인증결제 연동하기
+
+
-해당 가이드를 통해 결제창(SDK) 결제를 손쉽게 연동할 수 있습니다.
+
+ ## 인증결제 연동하기
-
+ 해당 가이드를 통해 결제창(SDK) 결제를 손쉽게 연동할 수 있습니다.
-## 수기(키인)결제 연동하기
+
-해당 가이드를 통해 API 결제를 손쉽게 연동할 수 있습니다.
+ ## 수기(키인)결제 연동하기
-
+ 해당 가이드를 통해 API 결제를 손쉽게 연동할 수 있습니다.
-## 빌링키 결제 연동하기
+
-해당 가이드를 통해 빌링키 결제를 손쉽게 연동할 수 있습니다.
+ ## 빌링키 결제 연동하기
-
+ 해당 가이드를 통해 빌링키 결제를 손쉽게 연동할 수 있습니다.
-
+
## 결제 결과 누락 없이 수신받기
해당 가이드를 통해 안정적으로 결제 결과를 수신받을 수 있습니다.
-
-
-
+
+
-
-
-## 본인인증 연동하기
+
+
+
-해당 가이드를 통해 본인인증을 손쉽게 연동할 수 있습니다.
+
+ ## 본인인증 연동하기
-
+ 해당 가이드를 통해 본인인증을 손쉽게 연동할 수 있습니다.
+
-
-
-## 기타 서비스 연동하기
+
+ ## 기타 서비스 연동하기
-해당 가이드를 통해 부가적인 서비스 연동을 손쉽게 처리할 수 있습니다.
+ 해당 가이드를 통해 부가적인 서비스 연동을 손쉽게 처리할 수 있습니다.
-
+
-
+
-
+
-
+
-
+
-## TIP
+ ## TIP
-결제창 연동 시 꼭 확인해 보세요.
+ 결제창 연동 시 꼭 확인해 보세요.
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
## 관리자 콘솔 사용하기
@@ -128,8 +120,11 @@ import VersionGate from "~/components/gitbook/VersionGate.astro";
포트원에서 제공하는 API 명세를 확인할 수 있습니다.
-
-
+
+
+
+
+
@@ -137,31 +132,28 @@ import VersionGate from "~/components/gitbook/VersionGate.astro";
결제 연동 JS SDK 명세를 확인할 수 있습니다.
-
-
-
+
+
-
-
-## FAQ
+
+
+
-
+
+ ## FAQ
+
## PG사별 결제 연동 가이드
각 PG사별 결제 연동 가이드를 안내합니다.
-
-
-
-
-
-
-
-
+
+
+
-
+
+
diff --git a/src/layouts/gnb/VersionSwitch.tsx b/src/layouts/gnb/VersionSwitch.tsx
index 8a48886c1..5d33adbce 100644
--- a/src/layouts/gnb/VersionSwitch.tsx
+++ b/src/layouts/gnb/VersionSwitch.tsx
@@ -1,6 +1,6 @@
import { useLocation, useNavigate } from "@solidjs/router";
import clsx from "clsx";
-import { createEffect, createSignal } from "solid-js";
+import { createEffect, createSignal, startTransition } from "solid-js";
import { useSystemVersion } from "~/state/system-version";
import type { SystemVersion } from "~/type";
@@ -75,7 +75,9 @@ export function VersionSwitch(props: VersionSwitchProps) {
style={{ transition: "margin 0.1s" }}
onClick={() => {
const newVersion = systemVersion() !== "v1" ? "v1" : "v2";
- setSystemVersion(newVersion);
+ void startTransition(() => {
+ setSystemVersion(newVersion);
+ });
setShowPopover(false);
const mappedPath =
diff --git a/src/layouts/rest-api/nav-menu/NavMenu.astro b/src/layouts/rest-api/nav-menu/NavMenu.astro
index 679acf3ab..1a2390338 100644
--- a/src/layouts/rest-api/nav-menu/NavMenu.astro
+++ b/src/layouts/rest-api/nav-menu/NavMenu.astro
@@ -1,5 +1,5 @@
---
-import LeftSidebar from "~/layouts/sidebar/LeftSidebar.astro";
+import LeftSidebar from "~/layouts/sidebar/LeftSidebar";
import NavMenuLink from "./NavMenuLink.astro";
diff --git a/src/layouts/sidebar/DocsNavMenu.astro b/src/layouts/sidebar/DocsNavMenu.astro
deleted file mode 100644
index 91c5fa4b4..000000000
--- a/src/layouts/sidebar/DocsNavMenu.astro
+++ /dev/null
@@ -1,125 +0,0 @@
----
-import { navOpenStatesSignal, slugSignal } from "~/state/nav";
-import { calcNavMenuAncestors, navMenu } from "~/state/server-only/nav";
-import { readServerSystemVersion } from "~/state/system-version/server";
-import type { Lang } from "~/type";
-
-import DropdownLink from "./DropdownLink";
-import LeftSidebar from "./LeftSidebar.astro";
-import LeftSidebarItem from "./LeftSidebarItem";
-import { SearchButton } from "./search";
-
-interface Props {
- lang: Lang;
- slug: string;
-}
-const { lang, slug } = Astro.props;
-
-const navMenuItems = navMenu[lang] || [];
-const navMenuAncestors = calcNavMenuAncestors(navMenuItems);
-const navOpenStates = (navOpenStatesSignal.value = Object.fromEntries([
- ...(navMenuAncestors[slug]?.map((ancestor) => [ancestor, true]) || []),
- [slug, true],
-]));
-slugSignal.value = slug;
-
-const serverSystemVersion = readServerSystemVersion();
----
-
-
-
-
-
-
-
-
-
-
diff --git a/src/layouts/sidebar/DocsNavMenu.tsx b/src/layouts/sidebar/DocsNavMenu.tsx
new file mode 100644
index 000000000..0c449f861
--- /dev/null
+++ b/src/layouts/sidebar/DocsNavMenu.tsx
@@ -0,0 +1,154 @@
+import { cache, createAsync, useLocation } from "@solidjs/router";
+import {
+ createContext,
+ createEffect,
+ createMemo,
+ createRenderEffect,
+ createSignal,
+ For,
+ onMount,
+ Show,
+ useContext,
+} from "solid-js";
+
+import { calcNavMenuAncestors } from "~/state/nav";
+import { useSystemVersion } from "~/state/system-version";
+import type { Lang } from "~/type";
+
+import DropdownLink from "./DropdownLink";
+import LeftSidebar from "./LeftSidebar";
+import LeftSidebarItem from "./LeftSidebarItem";
+import { SearchButton } from "./search";
+
+interface Props {
+ lang: Lang;
+ slug: string;
+}
+
+const getNavMenuItems = cache(async (lang: Lang) => {
+ "use server";
+
+ const { navMenu } = await import("~/state/server-only/nav");
+ return navMenu[lang] || [];
+}, "nav/menu-items");
+
+const navOpenStates = createContext({
+ openNavs: (): Set => new Set(),
+ toggleNav: (_: string) => {},
+});
+
+export const useNavOpenStates = () => useContext(navOpenStates);
+
+export default function DocsNavMenu(props: Props) {
+ const { systemVersion } = useSystemVersion();
+ const location = useLocation();
+ const navMenuItems = createAsync(() => getNavMenuItems(props.lang));
+ const navMenuAncestors = createMemo(() => {
+ const items = navMenuItems();
+ if (!items) return null;
+ return calcNavMenuAncestors(items);
+ });
+ const [openNavs, setOpenNavs] = createSignal>(new Set());
+
+ createRenderEffect(() => {
+ const ancestors = navMenuAncestors();
+ if (!ancestors) return;
+ const slug = `/${props.lang}/${props.slug}`;
+ setOpenNavs(new Set([...(ancestors[slug] ?? []), slug]));
+ });
+
+ onMount(() => {
+ const prevOpenNavs = JSON.parse(
+ globalThis.sessionStorage?.getItem("openNavs") || "[]",
+ ) as string[];
+ setOpenNavs((openNavs) => new Set([...prevOpenNavs, ...openNavs]));
+ });
+
+ createEffect(() => {
+ const openNavsStr = JSON.stringify([...openNavs()]);
+ globalThis.sessionStorage.setItem("openNavs", openNavsStr);
+ });
+
+ const toggleNav = (slug: string) => {
+ setOpenNavs((openNavs) => {
+ const newOpenNavs = new Set(openNavs);
+ if (newOpenNavs.has(slug)) newOpenNavs.delete(slug);
+ else newOpenNavs.add(slug);
+ return newOpenNavs;
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/layouts/sidebar/DropdownLink.tsx b/src/layouts/sidebar/DropdownLink.tsx
index 1355800d4..6603b6c53 100644
--- a/src/layouts/sidebar/DropdownLink.tsx
+++ b/src/layouts/sidebar/DropdownLink.tsx
@@ -1,57 +1,57 @@
-import { useSignal } from "@preact/signals";
import clsx from "clsx";
-import type React from "preact/compat";
+import { createSignal, For, type JSXElement, Show } from "solid-js";
export interface DropdownLinkProps {
items: DropdownItem[];
pathname: string;
}
export interface DropdownItem {
- label: React.ReactNode;
+ label: JSXElement;
link: string;
}
-export default function DropdownLink({ items, pathname }: DropdownLinkProps) {
- const showItemsSignal = useSignal(false);
+export default function DropdownLink(props: DropdownLinkProps) {
+ const [showItems, setShowItems] = createSignal(false);
+
return (
- {showItemsSignal.value && (
+
- )}
+
);
diff --git a/src/layouts/sidebar/LeftSidebar.astro b/src/layouts/sidebar/LeftSidebar.astro
deleted file mode 100644
index 41cf00744..000000000
--- a/src/layouts/sidebar/LeftSidebar.astro
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
diff --git a/src/layouts/sidebar/LeftSidebar.tsx b/src/layouts/sidebar/LeftSidebar.tsx
new file mode 100644
index 000000000..a1db8ab85
--- /dev/null
+++ b/src/layouts/sidebar/LeftSidebar.tsx
@@ -0,0 +1,20 @@
+import type { JSXElement } from "solid-js";
+
+import { useSidebarContext } from "./context";
+
+export default function LeftSidebar(props: { children: JSXElement }) {
+ const sidebarOpen = useSidebarContext();
+
+ return (
+
+ );
+}
diff --git a/src/layouts/sidebar/LeftSidebarItem.tsx b/src/layouts/sidebar/LeftSidebarItem.tsx
index 101de8105..3a17b5831 100644
--- a/src/layouts/sidebar/LeftSidebarItem.tsx
+++ b/src/layouts/sidebar/LeftSidebarItem.tsx
@@ -1,82 +1,98 @@
-import { useComputed } from "@preact/signals";
+import { createMemo, For, onMount, Show } from "solid-js";
-import { useSystemVersion } from "#state/system-version";
-import { navOpenStatesSignal, slugSignal } from "~/state/nav";
-import type { NavMenuPage } from "~/state/server-only/nav";
+import { type NavMenuPage } from "~/state/nav";
+import { useSystemVersion } from "~/state/system-version";
import type { SystemVersion } from "~/type";
-function LeftSidebarItem(props: NavMenuPage) {
- const systemVersion = useSystemVersion();
- if (props.items.length > 0) return ;
- const { title, path } = props;
- const pageSlug = slugSignal.value;
- const isActive = pageSlug === path;
- const [href, isExternal] = (() => {
+import { useNavOpenStates } from "./DocsNavMenu";
+
+function LeftSidebarItem(props: NavMenuPage & { pageSlug: string }) {
+ const { systemVersion } = useSystemVersion();
+ const isActive = createMemo(() => props.pageSlug === props.path);
+ const path = createMemo(() => {
try {
- return [new URL(path).toString(), true];
+ return { href: new URL(props.path).toString(), isExternal: true };
} catch (e) {
- return [`/docs${path}`, false];
+ return { href: `/docs${props.path}`, isExternal: false };
}
- })();
+ });
return (
-
+ }
+ >
+
+
);
}
export default LeftSidebarItem;
-function FolderLink({ title, path, items, systemVersion }: NavMenuPage) {
- const openSignal = useComputed(() => !!navOpenStatesSignal.value[path]);
- const open = openSignal.value;
- const pageSlug = slugSignal.value;
- const isActive = pageSlug === path;
+function FolderLink(
+ props: NavMenuPage & { pageSlug: string; isActive: boolean },
+) {
+ const { systemVersion } = useSystemVersion();
+ const { openNavs, toggleNav } = useNavOpenStates();
+ const isOpen = createMemo(() => openNavs().has(props.path));
+ let anchorRef: HTMLDivElement | undefined;
+
+ onMount(() => {
+ if (props.isActive && anchorRef) {
+ const scrollArea = document.getElementById("nav-menu")!;
+ scrollArea.scrollTop =
+ anchorRef.getBoundingClientRect().top -
+ scrollArea.getBoundingClientRect().top -
+ 50;
+ }
+ });
+
return (
-
-
-
-
-
-
-
-
-
- {items.map((item) => (
- -
-
-
- ))}
-
+
+
+
+
+
+
+
+
+
+
+
+ {(item) => (
+
+
+
+ )}
+
+
+
-
+
);
}
@@ -91,25 +107,26 @@ export interface JustLinkProps {
props: object;
};
}
-export function JustLink({
- title,
- href,
- isExternal,
- isActive,
- systemVersion,
- event,
-}: JustLinkProps) {
+export function JustLink(props: JustLinkProps) {
+ const { systemVersion } = useSystemVersion();
+
return (
-
event && trackEvent(event.name, event.props)}
- target={isExternal ? "_blank" : "_self"}
- >
-
-
+
+
+ props.event && trackEvent(props.event.name, props.event.props)
+ }
+ target={props.isExternal ? "_blank" : "_self"}
+ >
+
+
+
);
}
@@ -125,13 +142,20 @@ interface LinkTitleProps {
title: string;
isExternal?: boolean | undefined;
}
-export function LinkTitle({ title, isExternal }: LinkTitleProps) {
+export function LinkTitle(props: LinkTitleProps) {
return (
- {title || (unknown page)}
- {isExternal && (
+
+ (unknown page)}
+ >
+ {props.title}
+
+
+
- )}
+
);
}
diff --git a/src/layouts/sidebar/RightSidebar.tsx b/src/layouts/sidebar/RightSidebar.tsx
index 400b35595..e9f68cd14 100644
--- a/src/layouts/sidebar/RightSidebar.tsx
+++ b/src/layouts/sidebar/RightSidebar.tsx
@@ -1,7 +1,13 @@
-import type React from "preact/compat";
-import { useEffect, useState } from "react";
+import {
+ createEffect,
+ createSignal,
+ For,
+ type JSXElement,
+ mergeProps,
+ Show,
+} from "solid-js";
-import { useSystemVersion } from "#state/system-version";
+import { useSystemVersion } from "~/state/system-version";
export interface RightSidebarProps {
lang: string;
@@ -14,52 +20,52 @@ export interface TocItem {
text: string;
children: TocItem[];
}
-function RightSidebar({
- lang,
- slug,
- editThisPagePrefix = "https://github.com/portone-io/developers.portone.io/blob/main/src/content/docs",
-}: RightSidebarProps) {
- const [toc, setToc] = useState
(null);
- const systemVersion = useSystemVersion();
+function RightSidebar(_props: RightSidebarProps) {
+ const props = mergeProps(_props, {
+ editThisPagePrefix:
+ "https://github.com/portone-io/developers.portone.io/blob/main/src/content/docs",
+ });
- useEffect(() => {
- setToc(headingsToToc(lang));
- }, [systemVersion]);
+ const [toc, setToc] = createSignal(null);
+ const { systemVersion } = useSystemVersion();
+
+ createEffect(() => {
+ void systemVersion();
+ setToc(headingsToToc(props.lang));
+ });
return (
- {toc && (
+
- )}
+
);
}
@@ -69,32 +75,32 @@ export default RightSidebar;
interface LinkProps {
href: string;
icon?: string;
- label: React.ReactNode;
- children?: React.ReactNode;
+ label: JSXElement;
+ children?: JSXElement;
}
-function SidebarItem({ href, icon, label, children }: LinkProps) {
+function SidebarItem(props: LinkProps) {
return (
{
- if (!href.startsWith("#")) return;
+ if (!props.href.startsWith("#")) return;
e.preventDefault();
- const slug = href.slice(1);
- history.replaceState(null, "", href);
+ const slug = props.href.slice(1);
+ history.replaceState(null, "", props.href);
document.getElementById(slug)?.scrollIntoView({ behavior: "smooth" });
}}
>
- {icon && (
+
<>
- {" "}
+ {" "}
>
- )}
- {label}
+
+ {props.label}
- {children}
+ {props.children}
);
}
diff --git a/src/layouts/sidebar/search.tsx b/src/layouts/sidebar/search.tsx
index ae5945dbc..a1fbd687a 100644
--- a/src/layouts/sidebar/search.tsx
+++ b/src/layouts/sidebar/search.tsx
@@ -1,19 +1,48 @@
-import { computed, signal } from "@preact/signals";
import Fuse from "fuse.js";
-import * as React from "react";
+import {
+ createContext,
+ createMemo,
+ createResource,
+ createSignal,
+ For,
+ type JSXElement,
+ Match,
+ Show,
+ Switch,
+ useContext,
+} from "solid-js";
-import { useSystemVersion } from "#state/system-version";
import { lazy } from "~/misc/async";
-import type { NavMenuSystemVersions } from "~/state/server-only/nav";
+import type { NavMenuSystemVersions } from "~/state/nav";
+import { useSystemVersion } from "~/state/system-version";
+
+const SearchContext = createContext({
+ open: (): boolean => false,
+ setOpen: (_: boolean): void => {},
+});
+
+export function SearchProvider(props: { children: JSXElement }) {
+ const [open, setOpen] = createSignal(false);
+
+ return (
+
+ {props.children}
+
+ );
+}
+
+export const useSearchContext = () => useContext(SearchContext);
export interface SearchButtonProps {
lang: string;
}
export function SearchButton({ lang }: SearchButtonProps) {
+ const { setOpen } = useSearchContext();
+
return (