├── .prettierrc
├── .husky
└── pre-commit
├── public
├── icon.jpeg
├── sns
│ ├── x.webp
│ ├── discord.png
│ └── github.png
├── notion-data
│ ├── 126ce18c-fd83-80a5-8260-d757c56405b2
│ │ ├── image_1.png
│ │ ├── image_2.png
│ │ ├── image_3.png
│ │ ├── image_4.png
│ │ ├── image_5.png
│ │ ├── image_6.png
│ │ ├── image_7.png
│ │ ├── image_8.png
│ │ ├── image_9.png
│ │ ├── image_10.png
│ │ ├── image_11.png
│ │ ├── image_12.png
│ │ ├── image_13.png
│ │ ├── image_14.png
│ │ ├── image_15.png
│ │ ├── image_16.png
│ │ ├── image_17.png
│ │ ├── image_18.png
│ │ ├── image_19.png
│ │ ├── image_20.png
│ │ ├── image_21.png
│ │ ├── image_22.png
│ │ ├── image_23.png
│ │ ├── image_24.png
│ │ ├── image_25.png
│ │ ├── image_26.png
│ │ ├── image_27.png
│ │ ├── image_28.png
│ │ └── image_29.png
│ ├── 127ce18c-fd83-805c-bebd-d6772e18bf02
│ │ ├── image_1.gif
│ │ ├── image_2.gif
│ │ ├── image_3.gif
│ │ ├── image_4.gif
│ │ ├── image_5.gif
│ │ ├── image_6.gif
│ │ └── image_7.gif
│ └── 12ace18c-fd83-8071-b3a5-dd8d21da61cf
│ │ ├── image_1.png
│ │ ├── image_2.png
│ │ ├── image_3.png
│ │ ├── image_4.png
│ │ ├── image_5.png
│ │ ├── image_6.png
│ │ ├── image_7.png
│ │ ├── image_8.png
│ │ ├── image_9.png
│ │ ├── image_10.png
│ │ ├── image_11.png
│ │ ├── image_12.png
│ │ ├── image_13.png
│ │ ├── image_14.png
│ │ ├── image_15.png
│ │ ├── image_16.png
│ │ ├── image_17.png
│ │ ├── image_18.png
│ │ ├── image_19.png
│ │ ├── image_20.png
│ │ ├── image_21.png
│ │ ├── image_22.png
│ │ ├── image_23.png
│ │ ├── image_24.png
│ │ ├── image_25.png
│ │ ├── image_26.png
│ │ ├── image_27.png
│ │ ├── image_28.png
│ │ └── image_29.png
├── robots.txt
└── sitemap.xml
├── app
├── fonts
│ ├── Pretendard-Bold.woff
│ ├── Pretendard-Thin.woff
│ ├── Pretendard-Regular.woff
│ └── Pretendard-SemiBold.woff
├── [lang]
│ ├── page.tsx
│ ├── showcase
│ │ └── page.tsx
│ ├── contributing
│ │ └── page.tsx
│ ├── tutorial
│ │ └── page.tsx
│ ├── docs
│ │ └── [group]
│ │ │ └── [slug]
│ │ │ └── page.tsx
│ └── layout.tsx
├── layout.tsx
└── globals.css
├── components
├── showcase
│ ├── assets
│ │ ├── chapdo-blog.png
│ │ └── modern-tech.png
│ └── index.tsx
├── theme-provider.tsx
├── docs
│ ├── index.ts
│ ├── toc.tsx
│ ├── dynamic-layout.tsx
│ ├── navigation-button.tsx
│ ├── sidebar.tsx
│ ├── docs-layout.tsx
│ └── mobile-sidebar.tsx
├── notion-renderer.tsx
├── ui
│ ├── footer.tsx
│ └── language-selector.tsx
└── landing
│ └── hero.tsx
├── postcss.config.mjs
├── lib
├── utils.ts
├── generateTOC.ts
├── prevent-scrollbar.ts
└── mdx.ts
├── i18n
├── generate-static-params.ts
├── use-current-language.ts
├── internal
│ ├── interpolate.ts
│ ├── client-translations-provider.tsx
│ └── load-translations.ts
├── supported-languages.ts
├── translations-provider.tsx
├── get-preferred-language.ts
├── messages
│ ├── types.ts
│ ├── ko.ts
│ ├── cn.ts
│ ├── ja.ts
│ └── en.ts
├── get-server-translations.ts
├── get-server-current-language.ts
├── index.ts
└── use-translations.ts
├── next.config.mjs
├── next-sitemap.config.js
├── hooks
├── usePreventScroll.ts
├── useIsMobile.ts
└── useClickOutside.ts
├── constants
├── constants.ts
└── group.ts
├── .gitignore
├── tsconfig.json
├── middleware.ts
├── content
└── guide
│ ├── cn
│ ├── 01. getting-started
│ │ └── 01. introduction.md
│ ├── 03. block-types
│ │ ├── 07. paragraph.md
│ │ ├── 06. quote.md
│ │ ├── 02. heading.md
│ │ ├── 03. bulleted-list-item.md
│ │ ├── 05. numbered-list-item.md
│ │ ├── 08. callout.md
│ │ └── 09. toggle.md
│ └── 02. customization-guide
│ │ └── 03. custom-style.md
│ ├── ja
│ ├── 03. block-types
│ │ ├── 07. paragraph.md
│ │ ├── 06. quote.md
│ │ ├── 02. heading.md
│ │ ├── 03. bulleted-list-item.md
│ │ ├── 05. numbered-list-item.md
│ │ ├── 08. callout.md
│ │ └── 09. toggle.md
│ ├── 01. getting-started
│ │ └── 01. introduction.md
│ └── 02. customization-guide
│ │ └── 03. custom-style.md
│ ├── ko
│ ├── 03. block-types
│ │ ├── 07. Paragraph.md
│ │ ├── 06. Quote.md
│ │ ├── 02. heading.md
│ │ ├── 03. bulleted-list-item.md
│ │ ├── 08. Callout.md
│ │ ├── 05. numbered-list-item.md
│ │ └── 09. Toggle.md
│ ├── 01. getting-started
│ │ └── 01. introduction.md
│ └── 02. customization-guide
│ │ └── 03. custom-style.md
│ └── en
│ └── 03. block-types
│ ├── 07. Paragraph.md
│ ├── 02. heading.md
│ ├── 06. Quote.md
│ ├── 03. bulleted-list-item.md
│ └── 05. numbered-list-item.md
├── package.json
├── CONTRIBUTING-KR.md
├── README.md
├── tailwind.config.ts
└── CONTRIBUTING.md
/.prettierrc:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npx lint-staged
--------------------------------------------------------------------------------
/public/icon.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/icon.jpeg
--------------------------------------------------------------------------------
/public/sns/x.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/sns/x.webp
--------------------------------------------------------------------------------
/public/sns/discord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/sns/discord.png
--------------------------------------------------------------------------------
/public/sns/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/sns/github.png
--------------------------------------------------------------------------------
/app/fonts/Pretendard-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/app/fonts/Pretendard-Bold.woff
--------------------------------------------------------------------------------
/app/fonts/Pretendard-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/app/fonts/Pretendard-Thin.woff
--------------------------------------------------------------------------------
/app/fonts/Pretendard-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/app/fonts/Pretendard-Regular.woff
--------------------------------------------------------------------------------
/app/fonts/Pretendard-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/app/fonts/Pretendard-SemiBold.woff
--------------------------------------------------------------------------------
/components/showcase/assets/chapdo-blog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/components/showcase/assets/chapdo-blog.png
--------------------------------------------------------------------------------
/components/showcase/assets/modern-tech.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/components/showcase/assets/modern-tech.png
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_1.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_2.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_3.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_4.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_5.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_6.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_7.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_8.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_9.png
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_1.gif
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_2.gif
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_3.gif
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_4.gif
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_5.gif
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_6.gif
--------------------------------------------------------------------------------
/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/127ce18c-fd83-805c-bebd-d6772e18bf02/image_7.gif
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_1.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_2.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_3.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_4.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_5.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_6.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_7.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_8.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_9.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # *
2 | User-agent: *
3 | Allow: /
4 |
5 | # Host
6 | Host: https://notionpresso.com
7 |
8 | # Sitemaps
9 | Sitemap: https://notionpresso.com/sitemap.xml
10 |
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_10.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_11.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_12.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_13.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_14.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_15.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_16.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_17.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_18.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_19.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_20.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_21.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_22.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_23.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_24.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_25.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_26.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_27.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_28.png
--------------------------------------------------------------------------------
/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/126ce18c-fd83-80a5-8260-d757c56405b2/image_29.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_10.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_11.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_12.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_13.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_14.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_15.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_16.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_17.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_18.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_19.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_20.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_21.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_22.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_23.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_24.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_25.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_26.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_27.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_28.png
--------------------------------------------------------------------------------
/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/notionpresso/docs/HEAD/public/notion-data/12ace18c-fd83-8071-b3a5-dd8d21da61cf/image_29.png
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/i18n/generate-static-params.ts:
--------------------------------------------------------------------------------
1 | import { SUPPORTED_LANGUAGES } from "./supported-languages";
2 |
3 | export const generateStaticParams = () => {
4 | return SUPPORTED_LANGUAGES.map((lang) => ({ lang }));
5 | };
6 |
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | https://notionpresso.com/sitemap-0.xml
4 |
--------------------------------------------------------------------------------
/app/[lang]/page.tsx:
--------------------------------------------------------------------------------
1 | import { LandingHero } from "@/components/landing/hero";
2 |
3 | export const runtime = "edge";
4 |
5 | export { generateStaticParams } from "@/i18n";
6 |
7 | export default function Home() {
8 | return ;
9 | }
10 |
--------------------------------------------------------------------------------
/app/[lang]/showcase/page.tsx:
--------------------------------------------------------------------------------
1 | import Showcase from "@/components/showcase";
2 |
3 | export const runtime = "edge";
4 |
5 | export { generateStaticParams } from "@/i18n";
6 |
7 | export default function ShowcasePage() {
8 | return ;
9 | }
10 |
--------------------------------------------------------------------------------
/i18n/use-current-language.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useContext } from "react";
4 | import { TranslationsContext } from "./internal/client-translations-provider";
5 |
6 | export function useCurrentLanguage() {
7 | const { lang } = useContext(TranslationsContext);
8 | return lang;
9 | }
10 |
--------------------------------------------------------------------------------
/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ThemeProvider as NextThemesProvider } from "next-themes";
4 | import { type ThemeProviderProps } from "next-themes/dist/types";
5 |
6 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
7 | return {children} ;
8 | }
9 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | async redirects() {
4 | return [
5 | {
6 | source: '/:lang/docs',
7 | destination: '/:lang/docs/getting-started/introduction',
8 | permanent: true,
9 | }
10 | ]
11 | }
12 | };
13 |
14 | export default nextConfig;
15 |
--------------------------------------------------------------------------------
/components/docs/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Sidebar } from "./sidebar";
2 | export { default as TOC } from "./toc";
3 | export { default as DynamicLayout } from "./dynamic-layout";
4 | export { default as MobileSidebar } from "./mobile-sidebar";
5 | export { default as NavigationButton } from "./navigation-button";
6 | export { default as DocsLayout } from "./docs-layout";
7 |
--------------------------------------------------------------------------------
/i18n/internal/interpolate.ts:
--------------------------------------------------------------------------------
1 | export function interpolate(
2 | template: string,
3 | variables: { [key: string]: string | number },
4 | ): string {
5 | return template.replace(/\{(\w+)\}/g, (match, key) => {
6 | if (variables.hasOwnProperty(key)) {
7 | return String(variables[key]);
8 | } else {
9 | return match; // 변수에 해당하는 키가 없을 경우 원래 템플릿을 유지합니다.
10 | }
11 | });
12 | }
13 |
--------------------------------------------------------------------------------
/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | module.exports = {
3 | siteUrl: "https://notionpresso.com",
4 | generateRobotsTxt: true,
5 | // additionalPaths: [
6 | // {
7 | // href: 'https://notionpresso.com/ko',
8 | // hrefLang: 'ko',
9 | // },
10 | // {
11 | // href: 'https://notionpresso.com/en',
12 | // hrefLang: 'en',
13 | // },
14 | // ]
15 | };
16 |
--------------------------------------------------------------------------------
/hooks/usePreventScroll.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import preventScroll from "@/lib/prevent-scrollbar";
3 |
4 | interface UsePreventScrollProps {
5 | isOpen: boolean;
6 | }
7 |
8 | const usePreventScroll = ({ isOpen }: UsePreventScrollProps) => {
9 | useEffect(() => {
10 | preventScroll(isOpen);
11 |
12 | return () => {
13 | preventScroll(false);
14 | };
15 | }, [isOpen]);
16 | };
17 |
18 | export default usePreventScroll;
19 |
--------------------------------------------------------------------------------
/constants/constants.ts:
--------------------------------------------------------------------------------
1 | export const SNS_LIST = [
2 | {
3 | id: 1,
4 | name: "Github",
5 | url: "https://github.com/notionpresso",
6 | imgUrl: "/sns/github.png",
7 | },
8 | {
9 | id: 2,
10 | name: "X",
11 | url: "https://x.com/notionpres81989",
12 | imgUrl: "/sns/x.webp",
13 | },
14 | {
15 | id: 3,
16 | name: "Discord",
17 | url: "https://discord.gg/yAjKwDb7xR",
18 | imgUrl: "/sns/discord.png",
19 | },
20 | ] as const;
21 |
--------------------------------------------------------------------------------
/constants/group.ts:
--------------------------------------------------------------------------------
1 | export const GROUPS = [
2 | { id: "getting-started", name: "Getting Started", order: "01" },
3 | { id: "customization-guide", name: "Customization Guide", order: "02" },
4 | { id: "block-types", name: "Block Types", order: "03" },
5 | ] as const;
6 |
7 | export type GroupId = (typeof GROUPS)[number]["id"];
8 |
9 | export function getGroupOrder(groupId: GroupId): string {
10 | const group = GROUPS.find((g) => g.id === groupId);
11 | return group ? group.order : "00";
12 | }
13 |
--------------------------------------------------------------------------------
/components/notion-renderer.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Notion } from "@notionpresso/react";
4 |
5 | export const NotionRenderer = ({
6 | blocks,
7 | title,
8 | cover,
9 | }: {
10 | blocks: any[];
11 | title: string;
12 | cover?: string;
13 | }) => {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/lib/generateTOC.ts:
--------------------------------------------------------------------------------
1 | export interface TOCItem {
2 | id: string;
3 | title: string;
4 | }
5 |
6 | export function generateTOC(content: string): TOCItem[] {
7 | const lines = content.split("\n");
8 | const toc: TOCItem[] = [];
9 |
10 | lines.forEach((line) => {
11 | if (line.startsWith("
")) {
12 | const title = line.slice(4, -5).trim();
13 | const id = title.toLowerCase().replace(/[^a-z0-9가-힣]+/g, "-");
14 | toc.push({ id, title });
15 | }
16 | });
17 |
18 | return toc;
19 | }
20 |
--------------------------------------------------------------------------------
/i18n/supported-languages.ts:
--------------------------------------------------------------------------------
1 | export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
2 |
3 | export const DEFAULT_LANGUAGE = "en";
4 |
5 | export const LANGUAGE_LIST = [
6 | {
7 | title: "English",
8 | locale: "en",
9 | },
10 | {
11 | title: "한국어",
12 | locale: "ko",
13 | },
14 | {
15 | title: "简体中文",
16 | locale: "cn",
17 | },
18 | {
19 | title: "日本語",
20 | locale: "ja",
21 | },
22 | ];
23 |
24 | export const SUPPORTED_LANGUAGES = LANGUAGE_LIST.map(
25 | (language) => language.locale,
26 | );
27 |
--------------------------------------------------------------------------------
/i18n/translations-provider.tsx:
--------------------------------------------------------------------------------
1 | "use server";
2 | import { ClientTranslationsProvider } from "./internal/client-translations-provider";
3 | import { loadTranslations } from "./internal/load-translations";
4 |
5 | export default async function TranslationsProvider({
6 | children,
7 | lang,
8 | }: {
9 | children: React.ReactNode;
10 | lang: string;
11 | }) {
12 | const messages = await loadTranslations(lang);
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
38 | # husky
39 | .husky/_
40 |
--------------------------------------------------------------------------------
/hooks/useIsMobile.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState, useEffect } from "react";
4 |
5 | const useIsMobile = (options?: { default?: boolean }) => {
6 | const [mobile, setMobile] = useState(options?.default ?? false);
7 |
8 | useEffect(() => {
9 | const checkIsMobile = () => {
10 | setMobile(window.innerWidth < 768);
11 | };
12 |
13 | checkIsMobile();
14 | window.addEventListener("resize", checkIsMobile);
15 |
16 | return () => window.removeEventListener("resize", checkIsMobile);
17 | }, []);
18 |
19 | return mobile;
20 | };
21 |
22 | export default useIsMobile;
23 |
--------------------------------------------------------------------------------
/i18n/get-preferred-language.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest } from "next/server";
2 | import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "./supported-languages";
3 |
4 | export function getPreferredLanguage(request: NextRequest): string {
5 | const acceptLanguage = request.headers.get("accept-language");
6 | if (!acceptLanguage) return DEFAULT_LANGUAGE;
7 |
8 | const langs = acceptLanguage.split(",").map((lang) => lang.split(";")[0]);
9 | for (const lang of langs) {
10 | const shortLang = lang.slice(0, 2).toLowerCase();
11 | if (SUPPORTED_LANGUAGES.includes(shortLang)) {
12 | return shortLang;
13 | }
14 | }
15 | return DEFAULT_LANGUAGE;
16 | }
17 |
--------------------------------------------------------------------------------
/hooks/useClickOutside.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react";
2 |
3 | export function useClickOutside(
4 | handler: () => void,
5 | ) {
6 | const ref = useRef(null);
7 |
8 | useEffect(() => {
9 | const handleClickOutside = (event: MouseEvent) => {
10 | if (ref.current && !ref.current.contains(event.target as Node)) {
11 | handler();
12 | }
13 | };
14 |
15 | document.addEventListener("mousedown", handleClickOutside);
16 | return () => document.removeEventListener("mousedown", handleClickOutside);
17 | }, [handler]);
18 |
19 | return ref;
20 | }
21 |
22 | export default useClickOutside;
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/i18n/messages/types.ts:
--------------------------------------------------------------------------------
1 | export type Messages = {
2 | header: {
3 | home: string;
4 | blog: string;
5 | github: string;
6 | docs: string;
7 | tutorial: string;
8 | contributing: string;
9 | showcase: string;
10 | };
11 | home: {
12 | title: string;
13 | getStarted: string;
14 | readMore: string;
15 | };
16 | metadata: {
17 | title: string;
18 | description: string;
19 | keywords: string[];
20 | og: {
21 | title: string;
22 | description: string;
23 | siteName: string;
24 | imageAlt: string;
25 | };
26 | twitter: {
27 | title: string;
28 | description: string;
29 | imageAlt: string;
30 | };
31 | };
32 | };
33 |
--------------------------------------------------------------------------------
/components/docs/toc.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | interface TOCItem {
4 | id: string;
5 | title: string;
6 | }
7 |
8 | interface TOCProps {
9 | items: TOCItem[];
10 | }
11 |
12 | export default function TOC({ items }: TOCProps) {
13 | return (
14 |
15 |
16 | {items.map((item) => (
17 |
18 |
22 | {item.title}
23 |
24 |
25 | ))}
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/i18n/get-server-translations.ts:
--------------------------------------------------------------------------------
1 | import { Messages } from "./messages/types";
2 | import { loadTranslations } from "./internal/load-translations";
3 | import { interpolate } from "./internal/interpolate";
4 | import { getServerCurrentLanguage } from "./get-server-current-language";
5 |
6 | type Variables = { [key: string]: string | number };
7 |
8 | export async function getServerTranslations(namespace: keyof Messages) {
9 | let lang = getServerCurrentLanguage();
10 | const messages = await loadTranslations(lang);
11 |
12 | function t(key: string, variables?: Variables): string {
13 | const template = (messages[namespace] as any)?.[key] || "";
14 |
15 | if (variables) {
16 | return interpolate(template, variables);
17 | } else {
18 | return template;
19 | }
20 | }
21 |
22 | return t;
23 | }
24 |
--------------------------------------------------------------------------------
/i18n/internal/client-translations-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { createContext } from "react";
4 | import { Messages } from "../messages/types";
5 |
6 | interface TranslationsContextType {
7 | messages: Messages;
8 | lang: string;
9 | }
10 |
11 | export const TranslationsContext = createContext({
12 | messages: {} as Messages,
13 | lang: "",
14 | });
15 |
16 | interface TranslationsProviderProps {
17 | messages: Messages;
18 | lang: string;
19 | children: React.ReactNode;
20 | }
21 |
22 | export const ClientTranslationsProvider: React.FC<
23 | TranslationsProviderProps
24 | > = ({ messages, lang, children }) => {
25 | return (
26 |
27 | {children}
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/app/[lang]/contributing/page.tsx:
--------------------------------------------------------------------------------
1 | import { NotionRenderer } from "@/components/notion-renderer";
2 | //TODO: dynamic import is needed for better performance (don't need to import all the content at once)
3 | import * as ko from "@/content/contributing/ko/130ce18c-fd83-8040-8015-eaa450df6523.json";
4 | import * as en from "@/content/contributing/en/130ce18c-fd83-8017-9202-fdf73b9f1c9c.json";
5 |
6 | export const runtime = "edge";
7 |
8 | export { generateStaticParams } from "@/i18n";
9 |
10 | export default function TutorialPage({ params }: { params: { lang: string } }) {
11 | const content = params.lang === "ko" ? ko : en;
12 | const title = content.properties.title.title[0].plain_text;
13 |
14 | return (
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/i18n/get-server-current-language.ts:
--------------------------------------------------------------------------------
1 | // i18n/get-current-language.ts
2 |
3 | import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from "./supported-languages";
4 |
5 | import { staticGenerationAsyncStorage } from "next/dist/client/components/static-generation-async-storage.external";
6 |
7 | export function getServerCurrentLanguage(): string {
8 | let lang = DEFAULT_LANGUAGE; // 기본 언어 설정
9 |
10 | if (typeof window === "undefined") {
11 | const store = staticGenerationAsyncStorage.getStore();
12 | const pathname = store?.urlPathname;
13 |
14 | if (pathname) {
15 | // 경로명에서 언어 코드 추출 (예: /ko/...)
16 | const segments = pathname.split("/");
17 | if (segments.length > 1 && segments[1]) {
18 | const potentialLang = segments[1];
19 | if (SUPPORTED_LANGUAGES.includes(potentialLang as any)) {
20 | lang = potentialLang;
21 | }
22 | }
23 | }
24 | }
25 |
26 | return lang;
27 | }
28 |
--------------------------------------------------------------------------------
/i18n/index.ts:
--------------------------------------------------------------------------------
1 | import { getServerTranslations } from "./get-server-translations";
2 | import { useTranslations } from "./use-translations";
3 | import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from "./supported-languages";
4 | import { useCurrentLanguage } from "./use-current-language";
5 | import { ClientTranslationsProvider } from "./internal/client-translations-provider";
6 | import TranslationsProvider from "./translations-provider";
7 | import { getPreferredLanguage } from "./get-preferred-language";
8 | import { getServerCurrentLanguage } from "./get-server-current-language";
9 | import { generateStaticParams } from "./generate-static-params";
10 |
11 | export {
12 | useTranslations,
13 | getServerTranslations,
14 | SUPPORTED_LANGUAGES,
15 | DEFAULT_LANGUAGE,
16 | useCurrentLanguage,
17 | ClientTranslationsProvider,
18 | TranslationsProvider,
19 | getPreferredLanguage,
20 | getServerCurrentLanguage,
21 | generateStaticParams,
22 | };
23 |
--------------------------------------------------------------------------------
/i18n/messages/ko.ts:
--------------------------------------------------------------------------------
1 | import { Messages } from "./types";
2 |
3 | const ko: Messages = {
4 | header: {
5 | home: "홈",
6 | blog: "블로그",
7 | github: "깃허브",
8 | docs: "문서",
9 | tutorial: "튜토리얼",
10 | contributing: "기여하기",
11 | showcase: "쇼케이스",
12 | },
13 | home: {
14 | title: "커피 한 잔과\n노션프레소",
15 | getStarted: "시작하기",
16 | readMore: "문서보기",
17 | },
18 | metadata: {
19 | title: "노션프레소 문서",
20 | description:
21 | "React 애플리케이션에서 Notion 페이지를 렌더링하기 위한 강력한 도구인 노션프레소 라이브러리의 종합 문서입니다.",
22 | keywords: ["리액트", "노션", "커스텀", "문서", "라이브러리", "렌더링"],
23 | og: {
24 | title: "노션프레소 문서",
25 | description: "React 앱에서 Notion 페이지를 렌더링하는 방법을 배워보세요.",
26 | siteName: "노션프레소",
27 | imageAlt: "노션프레소 문서",
28 | },
29 | twitter: {
30 | title: "노션프레소 문서",
31 | description: "React 앱에서 Notion 페이지를 렌더링하는 방법을 배워보세요.",
32 | imageAlt: "노션프레소 문서",
33 | },
34 | },
35 | };
36 |
37 | export default ko;
38 |
--------------------------------------------------------------------------------
/i18n/messages/cn.ts:
--------------------------------------------------------------------------------
1 | import { Messages } from "./types";
2 |
3 | const cn: Messages = {
4 | header: {
5 | home: "首页",
6 | blog: "博客",
7 | github: "GitHub",
8 | docs: "文档",
9 | tutorial: "教程",
10 | contributing: "贡献",
11 | showcase: "展示",
12 | },
13 | home: {
14 | title: "一杯咖啡和\nNotionpresso",
15 | getStarted: "开始使用",
16 | readMore: "查看文档",
17 | },
18 | metadata: {
19 | title: "Notionpresso 文档",
20 | description:
21 | "Notionpresso库的综合文档,这是一个在React应用程序中渲染Notion页面的强大工具。",
22 | keywords: ["react", "notion", "自定义", "文档", "库", "渲染"],
23 | og: {
24 | title: "Notionpresso 文档",
25 | description: "学习如何使用Notionpresso在React应用中渲染Notion页面。",
26 | siteName: "Notionpresso",
27 | imageAlt: "Notionpresso 文档",
28 | },
29 | twitter: {
30 | title: "Notionpresso 文档",
31 | description: "学习如何使用Notionpresso在React应用中渲染Notion页面。",
32 | imageAlt: "Notionpresso 文档",
33 | },
34 | },
35 | };
36 |
37 | export default cn;
38 |
--------------------------------------------------------------------------------
/components/docs/dynamic-layout.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 |
3 | interface DynamicLayoutProps {
4 | sidebar: React.ReactNode;
5 | toc: React.ReactNode;
6 | mobileSidebar: React.ReactNode;
7 | children: React.ReactNode;
8 | }
9 |
10 | export default function DynamicLayout({
11 | sidebar,
12 | toc,
13 | mobileSidebar,
14 | children,
15 | }: DynamicLayoutProps) {
16 | return (
17 |
22 | {/* Sidebar for desktop */}
23 |
{sidebar}
24 |
25 | {/* Mobile sidebar */}
26 |
{mobileSidebar}
27 |
28 | {/* Main content */}
29 |
{children}
30 |
31 | {/* TOC - only visible on desktop */}
32 |
{toc}
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/i18n/internal/load-translations.ts:
--------------------------------------------------------------------------------
1 | import type { Messages } from "../messages/types";
2 | import { SUPPORTED_LANGUAGES, SupportedLanguage } from "../supported-languages";
3 |
4 | export async function loadTranslations(lang: string): Promise {
5 | let messages: Messages;
6 |
7 | // 언어 코드 검증
8 | const isSupported = SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);
9 | const languageToLoad = isSupported ? lang : "en"; // 지원되지 않는 언어의 경우 기본 언어로 폴백
10 |
11 | try {
12 | messages = await import(`../messages/${languageToLoad}`).then(
13 | (module) => module.default,
14 | );
15 | } catch (error) {
16 | console.error(
17 | `Failed to load messages for language: ${languageToLoad}`,
18 | error,
19 | );
20 | // 메시지 로드 실패 시 기본 언어로 폴백
21 | if (languageToLoad !== "en") {
22 | messages = await import("../messages/en").then(
23 | (module) => module.default,
24 | );
25 | } else {
26 | throw new Error("Failed to load default language messages.");
27 | }
28 | }
29 |
30 | return messages;
31 | }
32 |
--------------------------------------------------------------------------------
/middleware.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import type { NextRequest } from "next/server";
3 | import { SUPPORTED_LANGUAGES } from "@/i18n/supported-languages";
4 | import { getPreferredLanguage } from "./i18n/get-preferred-language";
5 |
6 | export function middleware(
7 | request: NextRequest,
8 | _response?: NextResponse,
9 | ): NextResponse {
10 | const { pathname } = request.nextUrl;
11 | const response = NextResponse.next();
12 |
13 | if (
14 | SUPPORTED_LANGUAGES.some(
15 | (lang) => pathname.startsWith(`/${lang}/`) || pathname === `/${lang}`,
16 | )
17 | ) {
18 | return response;
19 | }
20 |
21 | const lang = getPreferredLanguage(request);
22 | const newUrl = new URL(`/${lang}${pathname}`, request.url);
23 |
24 | const redirectResponse = NextResponse.redirect(newUrl);
25 |
26 | return redirectResponse;
27 | }
28 |
29 | export const config = {
30 | matcher: [
31 | "/((?!api|_next/static|_next/image|favicon\\.ico|.*\\.(?:png|jpg|jpeg|gif|webp|svg|ico|bmp|tiff|css|js|map|json|txt|xml|woff|woff2|eot|ttf|otf|txt)$).*)",
32 | ],
33 | };
34 |
--------------------------------------------------------------------------------
/i18n/messages/ja.ts:
--------------------------------------------------------------------------------
1 | import { Messages } from "./types";
2 |
3 | const ja: Messages = {
4 | header: {
5 | home: "ホーム",
6 | blog: "ブログ",
7 | github: "ギットハブ",
8 | docs: "ドキュメント",
9 | tutorial: "チュートリアル",
10 | contributing: "貢献する",
11 | showcase: "ショーケース",
12 | },
13 | home: {
14 | title: "コーヒー一杯と\nノーションプレッソ",
15 | getStarted: "始める",
16 | readMore: "ドキュメントを見る",
17 | },
18 | metadata: {
19 | title: "ノーションプレッソ ドキュメント",
20 | description:
21 | "リアクトアプリケーションでノーションページをレンダリングするための強力なツール、ノーションプレッソライブラリーの総合ドキュメントです。",
22 | keywords: [
23 | "リアクト",
24 | "ノーション",
25 | "カスタム",
26 | "ドキュメント",
27 | "ライブラリー",
28 | "レンダリング",
29 | ],
30 | og: {
31 | title: "ノーションプレッソ ドキュメント",
32 | description:
33 | "リアクトアプリでノーションページをレンダリングする方法を学びましょう。",
34 | siteName: "ノーションプレッソ",
35 | imageAlt: "ノーションプレッソ ドキュメント",
36 | },
37 | twitter: {
38 | title: "ノーションプレッソ ドキュメント",
39 | description:
40 | "リアクトアプリでノーションページをレンダリングする方法を学びましょう。",
41 | imageAlt: "ノーションプレッソ ドキュメント",
42 | },
43 | },
44 | };
45 |
46 | export default ja;
47 |
--------------------------------------------------------------------------------
/components/ui/footer.tsx:
--------------------------------------------------------------------------------
1 | import { SNS_LIST } from "@/constants/constants";
2 | import { cn } from "@/lib/utils";
3 | import ThemeSelector from "./theme-selector";
4 | import Image from "next/image";
5 |
6 | export default function Footer() {
7 | return (
8 |
9 | {/* left */}
10 |
11 |
Released under the MIT License.
12 |
Copyright NotionPresso
13 |
14 | {/* right */}
15 |
16 |
17 | {SNS_LIST.map((item) => (
18 |
28 |
35 |
36 | ))}
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/components/docs/navigation-button.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { ChevronRightIcon, ChevronLeftIcon } from "@heroicons/react/24/solid";
3 |
4 | interface NavigationButtonProps {
5 | document: {
6 | group: string;
7 | slug: string;
8 | title: string;
9 | };
10 | lang: string;
11 | direction: "prev" | "next";
12 | }
13 |
14 | export default function NavigationButton({
15 | document,
16 | lang,
17 | direction,
18 | }: NavigationButtonProps) {
19 | return (
20 |
24 | {direction === "prev" ? (
25 |
26 |
27 |
{document.title}
28 |
29 | ) : (
30 |
31 |
{document.title}
32 |
33 |
34 | )}
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/i18n/messages/en.ts:
--------------------------------------------------------------------------------
1 | import { Messages } from "./types";
2 |
3 | const en: Messages = {
4 | header: {
5 | home: "Home",
6 | blog: "Blog",
7 | github: "Github",
8 | docs: "Docs",
9 | tutorial: "Tutorial",
10 | contributing: "Contributing",
11 | showcase: "Showcase",
12 | },
13 | home: {
14 | title: "Just a cup of coffee\nwith NotionPresso",
15 | getStarted: "Get Started",
16 | readMore: "Read the Docs",
17 | },
18 | metadata: {
19 | title: "Notionpresso Docs",
20 | description:
21 | "Comprehensive documentation for Notionpresso library, a powerful tool for rendering Notion pages in React applications.",
22 | keywords: [
23 | "react",
24 | "notion",
25 | "custom",
26 | "documentation",
27 | "library",
28 | "rendering",
29 | ],
30 | og: {
31 | title: "Notionpresso Documentation",
32 | description:
33 | "Learn how to use Notionpresso to render Notion pages in your React apps.",
34 | siteName: "Notionpresso",
35 | imageAlt: "Notionpresso Documentation",
36 | },
37 | twitter: {
38 | title: "Notionpresso Documentation",
39 | description:
40 | "Learn how to use Notionpresso to render Notion pages in your React apps.",
41 | imageAlt: "Notionpresso Documentation",
42 | },
43 | },
44 | };
45 |
46 | export default en;
47 |
--------------------------------------------------------------------------------
/app/[lang]/tutorial/page.tsx:
--------------------------------------------------------------------------------
1 | import { NotionRenderer } from "@/components/notion-renderer";
2 | //TODO: dynamic import is needed for better performance (don't need to import all the content at once)
3 | import * as ko from "@/content/tutorial/ko/126ce18c-fd83-80a5-8260-d757c56405b2.json";
4 | import * as en from "@/content/tutorial/en/12ace18c-fd83-8071-b3a5-dd8d21da61cf.json";
5 | import type { Metadata } from "next";
6 |
7 | export const runtime = "edge";
8 |
9 | export { generateStaticParams } from "@/i18n";
10 |
11 | export async function generateMetadata({
12 | params,
13 | }: {
14 | params: { lang: string };
15 | }): Promise {
16 | const content = params.lang === "ko" ? ko : en;
17 | const title = content.properties.title.title[0].plain_text;
18 |
19 | return {
20 | title,
21 | description:
22 | params.lang === "ko"
23 | ? "Notionpresso 튜토리얼 페이지입니다."
24 | : "Notionpresso tutorial page",
25 | openGraph: {
26 | title,
27 | description:
28 | params.lang === "ko"
29 | ? "Notionpresso로 노션 페이지를 쉽게 웹사이트로 변환하세요"
30 | : "Convert your Notion pages to websites easily with Notionpresso",
31 | locale: params.lang,
32 | type: "website",
33 | },
34 | };
35 | }
36 |
37 | export default function TutorialPage({ params }: { params: { lang: string } }) {
38 | const content = params.lang === "ko" ? ko : en;
39 | const title = content.properties.title.title[0].plain_text;
40 |
41 | return (
42 |
43 |
44 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import "./globals.css";
3 |
4 | export const metadata: Metadata = {
5 | metadataBase: new URL("https://notionpresso.com"),
6 | title: "Notionpresso Docs",
7 | description:
8 | "Comprehensive documentation for Notionpresso library, a powerful tool for rendering Notion pages in React applications.",
9 | keywords: "react, notion, custom, documentation, library, rendering",
10 | icons: {
11 | icon: "/icon.jpeg",
12 | shortcut: "/icon.jpeg",
13 | apple: "/icon.jpeg",
14 | },
15 | robots: {
16 | index: true,
17 | follow: true,
18 | googleBot: {
19 | index: true,
20 | follow: true,
21 | "max-video-preview": -1,
22 | "max-image-preview": "large",
23 | "max-snippet": -1,
24 | },
25 | },
26 | openGraph: {
27 | title: "Notionpresso Documentation",
28 | description:
29 | "Learn how to use Notionpresso to render Notion pages in your React apps.",
30 | type: "website",
31 | url: "https://notionpresso.com",
32 | siteName: "Notionpresso",
33 | images: [
34 | {
35 | url: "/icon.jpeg",
36 | alt: "Notionpresso Documentation",
37 | },
38 | ],
39 | },
40 | twitter: {
41 | card: "summary_large_image",
42 | title: "Notionpresso Documentation",
43 | description:
44 | "Learn how to use Notionpresso to render Notion pages in your React apps.",
45 | images: ["/icon.jpeg"],
46 | },
47 | };
48 |
49 | export default function RootLayout({
50 | children,
51 | }: Readonly<{
52 | children: React.ReactNode;
53 | }>) {
54 | return <>{children}>;
55 | }
56 |
--------------------------------------------------------------------------------
/lib/prevent-scrollbar.ts:
--------------------------------------------------------------------------------
1 | type GapMode = "padding" | "margin";
2 |
3 | interface GapOffset {
4 | left: number;
5 | top: number;
6 | right: number;
7 | gap: number;
8 | }
9 |
10 | const zeroGap = {
11 | left: 0,
12 | top: 0,
13 | right: 0,
14 | gap: 0,
15 | };
16 |
17 | const parse = (x: string | null) => parseInt(x || "", 10) || 0;
18 |
19 | const getOffset = (gapMode: GapMode): number[] => {
20 | const cs = window.getComputedStyle(document.body);
21 |
22 | const left = cs[gapMode === "padding" ? "paddingLeft" : "marginLeft"];
23 | const top = cs[gapMode === "padding" ? "paddingTop" : "marginTop"];
24 | const right = cs[gapMode === "padding" ? "paddingRight" : "marginRight"];
25 |
26 | return [parse(left), parse(top), parse(right)];
27 | };
28 |
29 | const getGapWidth = (gapMode: GapMode = "margin"): GapOffset => {
30 | if (typeof window === "undefined") {
31 | return zeroGap;
32 | }
33 |
34 | const offsets = getOffset(gapMode);
35 | const documentWidth = document.documentElement.clientWidth;
36 | const windowWidth = window.innerWidth;
37 |
38 | return {
39 | left: offsets[0],
40 | top: offsets[1],
41 | right: offsets[2],
42 | gap: Math.max(0, windowWidth - documentWidth + offsets[2] - offsets[0]),
43 | };
44 | };
45 |
46 | const preventScroll = (isOpen: boolean) => {
47 | if (isOpen) {
48 | const { gap } = getGapWidth("padding");
49 | document.body.style.overflow = "hidden";
50 | document.body.style.paddingRight = `${gap}px`;
51 | } else {
52 | document.body.style.overflow = "";
53 | document.body.style.paddingRight = "";
54 | }
55 | };
56 |
57 | export default preventScroll;
58 |
--------------------------------------------------------------------------------
/i18n/use-translations.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useContext } from "react";
4 | import { TranslationsContext } from "./internal/client-translations-provider";
5 | import { interpolate } from "./internal/interpolate";
6 | import { Messages } from "./messages/types";
7 |
8 | // 중첩된 키를 점으로 연결하여 추출하는 유틸리티 타입
9 | type NestedKeyOf = {
10 | [Key in keyof TObj & (string | number)]: TObj[Key] extends object
11 | ? `${Key}` | `${Key}.${NestedKeyOf}`
12 | : `${Key}`;
13 | }[keyof TObj & (string | number)];
14 |
15 | export function useTranslations(
16 | namespace: Namespace,
17 | ) {
18 | const { messages } = useContext(TranslationsContext);
19 |
20 | function t<
21 | Key extends NestedKeyOf,
22 | Variables extends { [key: string]: any } = {},
23 | >(key: Key, variables?: Variables): string {
24 | // 전체 키 생성 (네임스페이스 포함)
25 | const fullKey = `${namespace}.${key}`;
26 |
27 | // 키를 '.'으로 분할하여 중첩된 객체에서 값을 찾습니다.
28 | const keys = fullKey.split(".");
29 | let message: any = messages;
30 |
31 | for (const k of keys) {
32 | if (message && k in message) {
33 | message = message[k];
34 | } else {
35 | console.warn(`Translation for key "${fullKey}" not found.`);
36 | return fullKey;
37 | }
38 | }
39 |
40 | if (typeof message === "string") {
41 | if (variables) {
42 | return interpolate(message, variables);
43 | } else {
44 | return message;
45 | }
46 | } else {
47 | console.warn(`Translation for key "${fullKey}" is not a string.`);
48 | return fullKey;
49 | }
50 | }
51 |
52 | return t;
53 | }
54 |
--------------------------------------------------------------------------------
/content/guide/cn/01. getting-started/01. introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "getting-started"
3 | order: 1
4 | title: "项目介绍"
5 | slug: "introduction"
6 | description: "介绍 Notionpresso 项目的概述、主要功能和应用可能性。"
7 | ---
8 |
9 | # Notionpresso 项目介绍
10 |
11 | ## 什么是 Notionpresso?
12 |
13 | Notionpresso 是一个能够轻松将 Notion 强大的内容创作功能转换为网站的工具。使用这个库,您可以将在 Notion 中创建的内容直接渲染到基于 React 的网站上。对开发者来说,它是一个能够快速产生结果的工具;对内容创作者来说,它是一个便捷的管理工具 —— 可以说是"为开发者和创作者准备的魔法药水"。✨
14 |
15 | ## 主要功能和价值
16 |
17 | ### 🛠️ Notion 数据的 React 渲染
18 |
19 | 您可以轻松地将 Notion 中创建的文本、图片、列表、代码块等各种内容转换为 React 组件。Notion 页面将以以下结构在网页中重生:
20 |
21 | ```jsx
22 | import { Notion, type NotionPageData } from 'Notionpresso';
23 |
24 | interface NotionPageProps {
25 | pageData: NotionPageData;
26 | }
27 |
28 | function NotionPage({ pageData }: NotionPageProps) {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 | ```
40 |
41 | ## 目标用户和他们应用的可能性
42 |
43 | ### 开发者
44 |
45 | 开发者可以在网站上轻松渲染 Notion 中创建的内容,并进行自定义。如果您想快速构建网站或超越商业服务的限制,Notionpresso 是您的答案。
46 |
47 | ### 设计师和创作者
48 |
49 | 设计师和创作者可以直接在网站上反映 Notion 中创建的内容,从而保持设计的一致性并快速更新内容。
50 |
51 | ### 应用可能性示例
52 |
53 | - **个人博客**: 您可以在 Notion 中轻松创建文章,并自动在网站上反映。
54 | - **公司简介和招聘页面**: 您可以使用 Notion 快速更新公司简介和招聘信息。
55 | - **作品集网站**: 您可以轻松更新项目和作品,从而使作品集管理变得更加容易。
56 |
57 | ## 为什么选择 Notionpresso
58 |
59 | 1. **开发者为中心的自定义**: 在其他商业服务中,您无法自定义到如此精细的程度。通过代码,您可以获得无限的可能性,这就是 Notionpresso 的魅力所在。
60 |
61 | 2. **开源的自由**: 一切都是透明的,您可以直接操作。如果您需要,您可以修改代码并将其托管在自己的服务器上,而无需承担任何费用。
62 |
63 | 3. **快速结果和高效率**: 您可以在 Notion 中创建内容,并通过简单的设置直接在网站上使用。内容创作和部署的过程变得更加简单。
64 |
65 | Notionpresso 是您将 Notion 页面转换为令人惊叹的网站的桥梁。现在,您可以摆脱复杂的设置,直接在 Notion 上开始!
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.1.0",
4 | "private": true,
5 | "description": "NotionPresso Documentation site built with Next.js",
6 | "author": {
7 | "name": "notionpresso",
8 | "email": "helper.notionpresso@gmail.com",
9 | "url": "https://notionpresso.com"
10 | },
11 | "homepage": "https://notionpresso.com",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/notionpresso/docs"
15 | },
16 | "bugs": {
17 | "url": "https://github.com/notionpresso/docs/issues"
18 | },
19 | "keywords": [
20 | "nextjs",
21 | "react",
22 | "typescript",
23 | "tailwindcss",
24 | "documentation"
25 | ],
26 | "license": "MIT",
27 | "scripts": {
28 | "dev": "next dev",
29 | "prod": "next build && next start",
30 | "build": "next build",
31 | "postbuild": "next-sitemap",
32 | "start": "next start",
33 | "lint": "next lint",
34 | "prepare": "husky"
35 | },
36 | "dependencies": {
37 | "@heroicons/react": "2.1.5",
38 | "@notionpresso/react": "^0.0.5",
39 | "@types/hast": "3.0.4",
40 | "clsx": "2.1.1",
41 | "gray-matter": "4.0.3",
42 | "highlight.js": "^11.10.0",
43 | "next": "14.2.11",
44 | "next-sitemap": "^4.2.3",
45 | "next-themes": "0.3.0",
46 | "react": "^18",
47 | "react-dom": "^18",
48 | "react-markdown": "9.0.1",
49 | "rehype-highlight": "7.0.0",
50 | "rehype-raw": "7.0.0",
51 | "remark": "15.0.1",
52 | "remark-gfm": "4.0.0",
53 | "remark-html": "16.0.1",
54 | "tailwind-merge": "2.5.4"
55 | },
56 | "devDependencies": {
57 | "@tailwindcss/typography": "^0.5.15",
58 | "@types/node": "^20",
59 | "@types/react": "^18",
60 | "@types/react-dom": "^18",
61 | "husky": "^9.1.6",
62 | "lint-staged": "^15.2.10",
63 | "postcss": "^8",
64 | "prettier": "^3.3.3",
65 | "tailwindcss": "^3.4.1",
66 | "typescript": "^5"
67 | },
68 | "lint-staged": {
69 | "**/*.{js,jsx,ts,tsx}": [
70 | "prettier --write"
71 | ],
72 | "**/*.{json,css,scss,md}": [
73 | "prettier --write"
74 | ]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/components/docs/sidebar.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { ChevronRightIcon } from "@heroicons/react/24/solid";
3 | import ThemeSelector from "../ui/theme-selector";
4 |
5 | type Document = {
6 | title: string;
7 | slug: string;
8 | group: string;
9 | order: number;
10 | };
11 |
12 | type SidebarProps = {
13 | documents: Document[];
14 | currentSlug: string;
15 | currentGroup: string;
16 | lang: string;
17 | };
18 |
19 | export default function Sidebar({
20 | documents,
21 | currentSlug,
22 | currentGroup,
23 | lang,
24 | }: SidebarProps) {
25 | const groupedDocuments = documents.reduce(
26 | (acc, doc) => {
27 | if (!acc[doc.group]) {
28 | acc[doc.group] = [];
29 | }
30 | acc[doc.group].push(doc);
31 | return acc;
32 | },
33 | {} as Record,
34 | );
35 |
36 | return (
37 |
38 | {Object.entries(groupedDocuments).map(([group, docs]) => (
39 |
40 |
41 | {group}
42 |
43 |
44 | {docs.map((doc) => (
45 |
46 |
54 | {doc.title}
55 |
56 |
57 |
58 | ))}
59 |
60 |
61 | ))}
62 |
63 |
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/CONTRIBUTING-KR.md:
--------------------------------------------------------------------------------
1 | # 문서 가이드 및 기여
2 |
3 | [프로젝트 전체 기여 가이드 참고](../../CONTRIBUTING-KR.md)
4 |
5 | ## 목차
6 |
7 | 1. 웹사이트 구조
8 | 2. 페이지별 내용
9 | 3. 기술 스택
10 | 4. 콘텐츠 관리
11 | 5. 다국어 지원
12 | 6. 개발 로드맵
13 | 7. 참고 사항
14 |
15 | ## 1. 웹사이트 구조
16 |
17 | ```
18 | /
19 | ├── 랜딩 페이지
20 | ├── 문서 (Docs)
21 | ├── 블로그 (Blog)
22 | ├── 쇼케이스 (Showcase)
23 | └── [기타 페이지들]
24 | ```
25 |
26 | ## 2. 페이지별 내용
27 |
28 | ### 2.1 랜딩 페이지
29 |
30 | - 프로젝트 소개
31 | - 주요 기능 하이라이트
32 | - Notion으로 블로그 셀프 호스팅 가이드 (튜토리얼 형식)
33 | - 시작하기 버튼 (문서로 연결)
34 |
35 | ### 2.2 문서 (Docs)
36 |
37 | ```
38 | +------------------+
39 | | Sidebar |
40 | | - 카테고리 1 |
41 | | - 문서 1 |
42 | | - 문서 2 |
43 | | - 카테고리 2 |
44 | | - 문서 3 |
45 | | - 문서 4 |
46 | +------------------+
47 | | |
48 | | Content |
49 | | |
50 | | [문서 내용] |
51 | | |
52 | +------------------+
53 | ```
54 |
55 | ### 2.3 블로그 (Blog)
56 |
57 | - 개발 과정, 업데이트, 팁 등의 포스트
58 | - 태그 및 날짜별 정렬 기능
59 |
60 | ### 2.4 쇼케이스 (Showcase)
61 |
62 | - 사용자들의 커스텀 Notion 페이지 전시
63 | - 필터링 및 정렬 기능
64 |
65 | ## 3. 기술 스택
66 |
67 | - Frontend: Next.js
68 | - CSS: TailwindCSS
69 | - UI 라이브러리: Radix UI 또는 Acenti/UI (필요시 사용)
70 | - 마크다운 렌더링: Next.js 공식 문서 참고
71 |
72 | ## 4. 콘텐츠 관리
73 |
74 | ### 4.1 문서 (Docs)
75 |
76 | - 위치: `/content/docs/[lang]/[category]/[document].md`
77 | - Frontmatter:
78 | - group: 카테고리
79 | - order: 문서 순서
80 | - title: 문서 제목
81 | - description: 문서 설명
82 |
83 | ### 4.2 블로그 (Blog)
84 |
85 | - 위치: `/content/blog/[lang]/[post].md`
86 | - Frontmatter:
87 | - title: 포스트 제목
88 | - description: 포스트 설명
89 | - date: 작성일
90 |
91 | ## 5. 다국어 지원
92 |
93 | - 지원 언어: 한국어(kr), 영어(en), 중국어(cn)
94 | - 마크다운 파일: 언어별로 분리 관리
95 | - UI 텍스트: 다국어 지원 라이브러리 사용
96 |
97 | ## 6. 개발 로드맵
98 |
99 | 1. 기본 Next.js 프로젝트 설정
100 | 2. TailwindCSS 통합
101 | 3. 마크다운 렌더링 시스템 구축
102 | 4. 다국어 지원 시스템 구현
103 | 5. 랜딩 페이지 개발
104 | 6. 문서 페이지 개발
105 | 7. 블로그 페이지 개발
106 | 8. 쇼케이스 페이지 개발
107 | 9. UI/UX 개선 및 최적화
108 | 10. 콘텐츠 작성 및 번역
109 | 11. 베타 테스트 및 피드백 수집
110 | 12. 공식 런칭
111 |
112 | ## 7. 참고 사항
113 |
114 | - 문서와 블로그 포스트는 마크다운 형식으로 관리
115 | - 랜딩 페이지는 Notionpresso 라이브러리를 활용한 실제 예시 포함
116 | - 지속적인 콘텐츠 업데이트 및 커뮤니티 참여 독려
117 |
--------------------------------------------------------------------------------
/app/[lang]/docs/[group]/[slug]/page.tsx:
--------------------------------------------------------------------------------
1 | import { DocsLayout } from "@/components/docs";
2 | import { SUPPORTED_LANGUAGES } from "@/i18n/supported-languages";
3 | import { getAllDocuments, getDocumentBySlug } from "@/lib/mdx";
4 | import { Metadata } from "next";
5 |
6 | interface GuidePageProps {
7 | params: {
8 | lang: string;
9 | group: string;
10 | slug: string;
11 | };
12 | }
13 |
14 | // Since we're using `fs` during build time, we should avoid the Edge runtime
15 | // export const runtime = 'edge';
16 |
17 | // Generate static params at build time
18 | export async function generateStaticParams() {
19 | const supportedLanguages = SUPPORTED_LANGUAGES;
20 | const params: GuidePageProps["params"][] = [];
21 |
22 | for (const lang of supportedLanguages) {
23 | const allDocuments = getAllDocuments(lang);
24 | allDocuments.forEach((doc) => {
25 | params.push({
26 | lang,
27 | group: doc.group,
28 | slug: doc.slug,
29 | });
30 | });
31 | }
32 |
33 | return params;
34 | }
35 |
36 | // Generate metadata at build time
37 | export async function generateMetadata({
38 | params,
39 | }: GuidePageProps): Promise {
40 | const { lang, group, slug } = params;
41 | const allDocuments = getAllDocuments(lang);
42 | const document = getDocumentBySlug(lang, group, slug, allDocuments);
43 |
44 | return {
45 | title: `${document.title} | Notionpresso Docs`,
46 | description: document.content.slice(0, 160),
47 | openGraph: {
48 | title: `${document.title} | Notionpresso Docs`,
49 | description: document.content.slice(0, 160),
50 | // url: "",
51 | },
52 | alternates: {
53 | // canonical: "",
54 | },
55 | };
56 | }
57 |
58 | export default async function Page({ params }: GuidePageProps) {
59 | const { lang, group, slug } = params;
60 |
61 | // Fetch data during the rendering of the page
62 | const allDocuments = getAllDocuments(lang);
63 | const { content, title, prevDocument, nextDocument } = getDocumentBySlug(
64 | lang,
65 | group,
66 | slug,
67 | allDocuments,
68 | );
69 |
70 | return (
71 |
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /* CSS Reset */
6 | * {
7 | margin: 0;
8 | padding: 0;
9 | box-sizing: border-box;
10 | }
11 |
12 | html,
13 | body {
14 | overflow-y: scroll;
15 | height: 100%;
16 | }
17 |
18 | img,
19 | picture,
20 | video,
21 | canvas,
22 | svg {
23 | display: block;
24 | max-width: 100%;
25 | }
26 |
27 | input,
28 | button,
29 | textarea,
30 | select {
31 | font: inherit;
32 | }
33 |
34 | button {
35 | cursor: pointer;
36 | }
37 |
38 | a {
39 | text-decoration: none;
40 | color: inherit;
41 | }
42 |
43 | ul,
44 | ol {
45 | list-style: none;
46 | }
47 |
48 | /* Global Styles */
49 | body {
50 | line-height: 1.6;
51 | background-color: #f8f9fa;
52 | color: #343a40;
53 | }
54 |
55 | h1,
56 | h2,
57 | h3,
58 | h4,
59 | h5,
60 | h6 {
61 | font-weight: bold;
62 | line-height: 1.2;
63 | }
64 |
65 | @keyframes shake {
66 | 0% {
67 | transform: rotate(0deg);
68 | }
69 | 25% {
70 | transform: rotate(5deg);
71 | }
72 | 50% {
73 | transform: rotate(0eg);
74 | }
75 | 75% {
76 | transform: rotate(-5deg);
77 | }
78 | 100% {
79 | transform: rotate(0deg);
80 | }
81 | }
82 | .logo-image {
83 | transition: transform 0.1s ease-in-out;
84 | }
85 | .logo-image:hover {
86 | animation: shake 0.5s ease-in-out;
87 | }
88 |
89 | .prose table {
90 | width: 100%;
91 | border-collapse: collapse;
92 | }
93 |
94 | .prose th,
95 | .prose td {
96 | border: 1px solid #ddd;
97 | padding: 8px;
98 | text-align: left;
99 | }
100 |
101 | .prose th {
102 | background-color: #f2f2f2;
103 | }
104 |
105 | /* For WebKit browsers (Chrome, Safari) */
106 | ::-webkit-scrollbar {
107 | width: 8px;
108 | height: 8px;
109 | }
110 |
111 | ::-webkit-scrollbar-thumb {
112 | background-color: #fe6f21;
113 | border-radius: 10px;
114 | }
115 |
116 | ::-webkit-scrollbar-track {
117 | background: transparent;
118 | }
119 |
120 | ::-webkit-scrollbar-corner {
121 | background: transparent;
122 | }
123 |
124 | /* For Firefox */
125 | * {
126 | scrollbar-width: thin;
127 | scrollbar-color: #fe6f21 transparent;
128 | }
129 |
130 | /* Optional: Hover effects for better interactivity */
131 | ::-webkit-scrollbar-thumb:hover {
132 | background-color: #e85f11;
133 | }
134 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/07. paragraph.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 7
4 | title: "段落块"
5 | slug: "paragraph"
6 | description: "详细介绍 Notionpresso 的段落块类型。"
7 | ---
8 |
9 | # 段落块
10 |
11 | 段落块是表示普通文本的最基本块类型。
12 |
13 | ## 数据结构
14 |
15 | 段落块的 Notion API 数据结构如下:
16 |
17 | ```json
18 | {
19 | "type": "paragraph",
20 | "paragraph": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a paragraph.",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a paragraph.",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 包含文本内容和样式信息的数组。
46 | - `color`: 指定文本的颜色。
47 |
48 | ## React 组件
49 |
50 | Notionpresso 中用于渲染段落块的组件如下:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { ParagraphArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type ParagraphProps = {
59 | children?: React.ReactNode;
60 | } & ParagraphArgs;
61 |
62 | const Paragraph: React.FC = ({ children, ...props }) => {
63 | const {
64 | paragraph: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 | {children}
73 |
74 | );
75 | };
76 |
77 | export default Paragraph;
78 | ```
79 |
80 | ## 使用示例
81 |
82 | 段落块的使用示例如下:
83 |
84 | ```jsx
85 | import { Notion } from "@notionpresso/react";
86 |
87 | function MyNotionPage({ blocks }) {
88 | return (
89 |
90 |
91 |
92 | );
93 | }
94 | ```
95 |
96 | 这里 `blocks` 是来自 Notion API 的块数据数组。
97 |
98 | ## 样式
99 |
100 | 段落块的样式可以通过以下 CSS 类进行自定义:
101 |
102 | - `.notion-block`: 应用于所有 Notion 块的基本样式
103 | - `.notion-paragraph`: 段落块的特定样式
104 | - `.notion-paragraph-content`: 段落内容样式
105 |
106 | 如果需要额外的样式,可以对这些类进行 CSS 编写。
107 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NotionPresso Documentation
2 |
3 |
4 |
5 |
6 |
7 |
NotionPresso Documentation
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | NotionPresso is an open-source project that enables developers to create their own blog using Notion as a CMS. It's the first React rendering library that utilizes Notion's official API, offering unlimited customization possibilities.
20 |
21 | ## Key Features
22 |
23 | ### Technical Highlights
24 |
25 | - **First Official API-based React Library**: Built on Notion's official API for stable and extensible rendering
26 | - **Complete TypeScript Support**: Fully typed components and APIs for better development experience
27 | - **Customizable Component System**: Flexible architecture allowing infinite customization possibilities
28 |
29 | ### For Blog Creators
30 |
31 | - **Free Blog Hosting**: Create and host your blog completely free using our guides
32 | - **Quick Deployment**: Set up your blog in minutes with our step-by-step deployment guide
33 | - **Familiar Writing Experience**: Use Notion's powerful editor for content creation
34 |
35 | ## Getting Started
36 |
37 | ```bash
38 | # Install the core package
39 | npm install @notionpresso/react
40 |
41 | # For CLI tools
42 | npm install -g @notionpresso/cli
43 | ```
44 |
45 | For detailed guides and documentation, visit our [official documentation](https://notionpresso.com).
46 |
47 | ## Documentation
48 |
49 | - [Getting Started Guide](https://notionpresso.com/docs/getting-started/introduction)
50 | - [Tutorial](https://notionpresso.com/tutorial)
51 | - [Blog Template](https://nextjs-blog-template.pages.dev/)
52 | - [Showcase](https://notionpresso.com/showcase)
53 |
54 | ## Contributing
55 |
56 | We welcome contributions! Please see our [contributing guide](https://notionpresso.com/contributing) for details.
57 |
58 | ## License
59 |
60 | MIT © [NotionPresso](https://notionpresso.com)
61 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/07. paragraph.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 2
4 | title: "段落ブロック"
5 | slug: "paragraph"
6 | description: "ノーションプレッソの段落ブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # 段落ブロック
10 |
11 | 段落ブロックは、一般的なテキストを表現する最も基本的なブロックタイプです。
12 |
13 | ## データ構造
14 |
15 | 段落ブロックのノーションAPIデータ構造は以下の通りです:
16 |
17 | ```json
18 | {
19 | "type": "paragraph",
20 | "paragraph": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a paragraph.",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a paragraph.",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: テキストの内容とスタイル情報を含む配列です。
46 | - `color`: テキストの色を指定します。
47 |
48 | ## Reactコンポーネント
49 |
50 | Notionpressoで段落ブロックをレンダリングするコンポーネントは以下の通りです:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { ParagraphArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type ParagraphProps = {
59 | children?: React.ReactNode;
60 | } & ParagraphArgs;
61 |
62 | const Paragraph: React.FC = ({ children, ...props }) => {
63 | const {
64 | paragraph: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 | {children}
73 |
74 | );
75 | };
76 |
77 | export default Paragraph;
78 | ```
79 |
80 | ## 使用例
81 |
82 | 段落ブロックを使用する例は以下の通りです:
83 |
84 | ```jsx
85 | import { Notion } from "@notionpresso/react";
86 |
87 | function MyNotionPage({ blocks }) {
88 | return (
89 |
90 |
91 |
92 | );
93 | }
94 | ```
95 |
96 | ここで`blocks`は、Notion APIから受け取ったブロックデータの配列です。
97 |
98 | ## スタイリング
99 |
100 | 段落ブロックのスタイルは、以下のCSSクラスを使用してカスタマイズできます:
101 |
102 | - `.notion-block`: すべてのNotionブロックに適用されるデフォルトのスタイル
103 | - `.notion-paragraph`: 段落ブロックの特定のスタイル
104 | - `.notion-paragraph-content`: 段落の内容のスタイル
105 |
106 | 追加のスタイリングが必要な場合は、これらのクラスを対象にCSSを記述することができます。
107 |
--------------------------------------------------------------------------------
/app/[lang]/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import { ThemeProvider } from "@/components/theme-provider";
4 | import Header from "@/components/ui/header";
5 | import Script from "next/script";
6 | import TranslationsProvider from "@/i18n/translations-provider";
7 | import { getServerTranslations } from "@/i18n";
8 | import Footer from "@/components/ui/footer";
9 |
10 | const pretendard = localFont({
11 | src: "../fonts/Pretendard-Regular.woff",
12 | variable: "--font-pretendard",
13 | weight: "100 900",
14 | });
15 |
16 | export async function generateMetadata(): Promise {
17 | const t = await getServerTranslations("metadata");
18 | return {
19 | title: t("title"),
20 | description: t("description"),
21 | keywords: t("keywords"),
22 | openGraph: {
23 | title: t("og.title"),
24 | description: t("og.description"),
25 | siteName: t("og.siteName"),
26 | images: [
27 | {
28 | url: "/icon.jpeg",
29 | alt: t("og.imageAlt"),
30 | },
31 | ],
32 | },
33 | twitter: {
34 | title: t("twitter.title"),
35 | description: t("twitter.description"),
36 | images: [
37 | {
38 | url: "/icon.jpeg",
39 | alt: t("twitter.imageAlt"),
40 | },
41 | ],
42 | },
43 | };
44 | }
45 |
46 | export default function RootLayout({
47 | children,
48 | params,
49 | }: Readonly<{
50 | children: React.ReactNode;
51 | params: { lang: string };
52 | }>) {
53 | return (
54 |
55 |
56 | {params.lang === "ko" && (
57 | <>
58 |
63 |
69 | >
70 | )}
71 |
72 |
73 |
74 |
75 |
76 | {children}
77 |
78 |
79 |
80 |
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/07. Paragraph.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 2
4 | title: "Paragraph 블록"
5 | slug: "paragraph"
6 | description: "Notionpresso의 Paragraph 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Paragraph 블록
10 |
11 | Paragraph 블록은 일반 텍스트를 표현하는 가장 기본적인 블록 타입입니다.
12 |
13 | ## 데이터 구조
14 |
15 | Paragraph 블록의 Notion API 데이터 구조는 다음과 같습니다:
16 |
17 | ```json
18 | {
19 | "type": "paragraph",
20 | "paragraph": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a paragraph.",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a paragraph.",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 텍스트의 내용과 스타일 정보를 담고 있는 배열입니다.
46 | - `color`: 텍스트의 색상을 지정합니다.
47 |
48 | ## React 컴포넌트
49 |
50 | Notionpresso에서 Paragraph 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { ParagraphArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type ParagraphProps = {
59 | children?: React.ReactNode;
60 | } & ParagraphArgs;
61 |
62 | const Paragraph: React.FC = ({ children, ...props }) => {
63 | const {
64 | paragraph: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 | {children}
73 |
74 | );
75 | };
76 |
77 | export default Paragraph;
78 | ```
79 |
80 | ## 사용 예시
81 |
82 | Paragraph 블록을 사용하는 예시는 다음과 같습니다:
83 |
84 | ```jsx
85 | import { Notion } from "@notionpresso/react";
86 |
87 | function MyNotionPage({ blocks }) {
88 | return (
89 |
90 |
91 |
92 | );
93 | }
94 | ```
95 |
96 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
97 |
98 | ## 스타일링
99 |
100 | Paragraph 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
101 |
102 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
103 | - `.notion-paragraph`: Paragraph 블록 특정 스타일
104 | - `.notion-paragraph-content`: Paragraph 내용의 스타일
105 |
106 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다.
107 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | // tailwind.config.ts
2 | import type { Config } from "tailwindcss";
3 | import plugin from "tailwindcss/plugin";
4 |
5 | const config: Config = {
6 | content: [
7 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
8 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
9 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
10 | ],
11 | darkMode: "class",
12 | theme: {
13 | extend: {
14 | colors: {
15 | primary: {
16 | DEFAULT: "#FE6F21",
17 | ...{
18 | "50": "#fff6ed",
19 | "100": "#ffebd4",
20 | "200": "#ffd2a8",
21 | "300": "#ffb270",
22 | "400": "#ff8637",
23 | "500": "#ff6f20",
24 | "600": "#f04906",
25 | "700": "#c73407",
26 | "800": "#9e2a0e",
27 | "900": "#7f260f",
28 | "950": "#450f05",
29 | },
30 | },
31 | neutral: {
32 | DEFAULT: "#000000",
33 | ...{
34 | "50": "#f6f6f6",
35 | "100": "#e7e7e7",
36 | "200": "#d1d1d1",
37 | "300": "#b0b0b0",
38 | "400": "#888888",
39 | "500": "#6d6d6d",
40 | "600": "#5d5d5d",
41 | "700": "#4f4f4f",
42 | "800": "#454545",
43 | "900": "#3d3d3d",
44 | "950": "#000000",
45 | },
46 | },
47 | // 다크모드용 색상 추가
48 | background: {
49 | DEFAULT: "white",
50 | dark: "#000000",
51 | },
52 | text: {
53 | DEFAULT: "#000000",
54 | dark: "#ffffff",
55 | },
56 | border: {
57 | DEFAULT: "#e7e7e7",
58 | dark: "#454545",
59 | },
60 | },
61 | fontSize: {
62 | display: ["3rem", { lineHeight: "1.2", fontWeight: "700" }], // 48px
63 | h1: ["2.5rem", { lineHeight: "1.2", fontWeight: "700" }], // 40px
64 | h2: ["2rem", { lineHeight: "1.2", fontWeight: "700" }], // 32px
65 | h3: ["1.5rem", { lineHeight: "1.2", fontWeight: "700" }], // 24px
66 | subhead1: ["1.25rem", { lineHeight: "1.2", fontWeight: "700" }], // 20px
67 | subhead2: ["1rem", { lineHeight: "1.2", fontWeight: "700" }], // 16px
68 | subhead3: ["0.875rem", { lineHeight: "1.2", fontWeight: "700" }], // 14px
69 | body1: ["1rem", { lineHeight: "1.5" }], // 16px
70 | body2: ["0.875rem", { lineHeight: "1.5" }], // 14px
71 | body3: ["0.75rem", { lineHeight: "1.5" }], // 12px
72 | },
73 | },
74 | },
75 | plugins: [
76 | require("@tailwindcss/typography"),
77 | plugin(function ({ addVariant }) {
78 | addVariant(
79 | "prose-block-code",
80 | '&.prose :where(pre)>code:not(:where([class~="not-prose"] *))',
81 | );
82 | }),
83 | ],
84 | };
85 | export default config;
86 |
--------------------------------------------------------------------------------
/components/landing/hero.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cn } from "@/lib/utils";
4 | import { motion } from "framer-motion";
5 | import { ArrowRightIcon } from "@heroicons/react/24/solid";
6 | import { useRouter } from "next/navigation";
7 | import { useTranslations } from "@/i18n";
8 |
9 | export function LandingHero() {
10 | const router = useRouter();
11 | const t = useTranslations("home");
12 |
13 | return (
14 |
22 |
23 |
29 |
35 |
40 | {t("title")}
41 |
42 |
43 |
44 |
router.push("/tutorial")}
53 | whileHover={{ scale: 1.02 }}
54 | whileTap={{ scale: 0.98 }}
55 | >
56 | {t("getStarted")}
57 |
58 |
59 |
60 |
router.push("/docs")}
69 | whileHover={{ scale: 1.02 }}
70 | whileTap={{ scale: 0.98 }}
71 | >
72 | {t("readMore")}
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/content/guide/en/03. block-types/07. Paragraph.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 2
4 | title: "Paragraph Block"
5 | slug: "paragraph"
6 | description: "Detailed explanation of the Paragraph block type in Notionpresso."
7 | ---
8 |
9 | # Paragraph Block
10 |
11 | The Paragraph block is the most basic block type used to represent plain text.
12 |
13 | ## Data Structure
14 |
15 | The Notion API data structure for a Paragraph block is as follows:
16 |
17 | ```json
18 | {
19 | "type": "paragraph",
20 | "paragraph": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a paragraph.",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a paragraph.",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: An array containing the content and style information of the text.
46 | - `color`: Specifies the color of the text.
47 |
48 | ## React Component
49 |
50 | The component that renders the Paragraph block in Notionpresso is as follows:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { ParagraphArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type ParagraphProps = {
59 | children?: React.ReactNode;
60 | } & ParagraphArgs;
61 |
62 | const Paragraph: React.FC = ({ children, ...props }) => {
63 | const {
64 | paragraph: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 | {children}
73 |
74 | );
75 | };
76 |
77 | export default Paragraph;
78 | ```
79 |
80 | ## Usage Example
81 |
82 | Here's an example of how to use the Paragraph block:
83 |
84 | ```jsx
85 | import { Notion } from "@notionpresso/react";
86 |
87 | function MyNotionPage({ blocks }) {
88 | return (
89 |
90 |
91 |
92 | );
93 | }
94 | ```
95 |
96 | Here, `blocks` is an array of block data received from the Notion API.
97 |
98 | ## Styling
99 |
100 | The style of the Paragraph block can be customized through the following CSS classes:
101 |
102 | - `.notion-block`: Basic style applied to all Notion blocks
103 | - `.notion-paragraph`: Specific style for Paragraph blocks
104 | - `.notion-paragraph-content`: Style for the content of the paragraph
105 |
106 | If additional styling is needed, you can write CSS targeting these classes.
107 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/06. quote.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 6
4 | title: "引用块"
5 | slug: "quote"
6 | description: "详细介绍 Notionpresso 的引用块类型。"
7 | ---
8 |
9 | # 引用块
10 |
11 | 引用块用于显示来自其他来源的引用文本。通常以视觉上独特的样式呈现。
12 |
13 | ## 数据结构
14 |
15 | 引用块的 Notion API 数据结构如下:
16 |
17 | ```json
18 | {
19 | "type": "quote",
20 | "quote": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a quote",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a quote",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 包含引用文本及其样式信息的数组。
46 | - `color`: 指定引用文本的颜色。
47 |
48 | ## React 组件
49 |
50 | Notionpresso 中用于渲染引用块的组件如下:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { QuoteArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type QuoteProps = {
59 | children?: React.ReactNode;
60 | } & QuoteArgs;
61 |
62 | const Quote: React.FC = ({ children, ...props }) => {
63 | const {
64 | quote: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 |
73 | {children}
74 |
75 |
76 | );
77 | };
78 |
79 | export default Quote;
80 | ```
81 |
82 | ## 使用示例
83 |
84 | 引用块的使用示例如下:
85 |
86 | ```jsx
87 | import { Notion } from "@notionpresso/react";
88 |
89 | function MyNotionPage({ blocks }) {
90 | return (
91 |
92 |
93 |
94 | );
95 | }
96 | ```
97 |
98 | 这里 `blocks` 是来自 Notion API 的块数据数组。
99 |
100 | ## 样式
101 |
102 | 引用块的样式可以通过以下 CSS 类进行自定义:
103 |
104 | - `.notion-block`: 应用于所有 Notion 块的基本样式
105 | - `.notion-quote`: 引用块的特定样式
106 | - `.notion-quote-content`: 引用文本的样式
107 |
108 | 如果需要额外的样式,可以对这些类进行 CSS 编写。通常,引用块具有以下样式特征:
109 |
110 | - 左侧或两侧的粗边框
111 | - 略微的缩进
112 | - 背景色变化
113 | - 字体样式变化(例如,斜体)
114 |
115 | 例如,可以应用以下 CSS:
116 |
117 | ```css
118 | .notion-quote {
119 | border-left: 4px solid #ccc;
120 | padding-left: 16px;
121 | margin-left: 0;
122 | margin-right: 0;
123 | font-style: italic;
124 | background-color: #f9f9f9;
125 | }
126 | ```
127 |
128 | ## 嵌套支持
129 |
130 | 引用块可以包含其他块。这通过 `children` prop 处理。例如,可以在引用文本中包含列表或其他文本块。
131 |
132 | ## 注意事项
133 |
134 | - 通常,引用块用于短引用文本。对于长引用文本,为了提高可读性,可能需要将其分成多个引用块。
135 | - 最好在引用块下方添加单独的段落块,以指明引用文本的来源。
136 | - 为了考虑可访问性,对于屏幕阅读器用户,建议添加适当的 ARIA 属性。例如,可以使用 `role="blockquote"` 属性。
137 |
--------------------------------------------------------------------------------
/components/ui/language-selector.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState, useRef } from "react";
4 | import { LanguageIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
5 | import { useRouter, usePathname } from "next/navigation";
6 | import { cn } from "@/lib/utils";
7 | import { LANGUAGE_LIST } from "@/i18n/supported-languages";
8 | import useClickOutside from "@/hooks/useClickOutside";
9 | import { useCurrentLanguage } from "@/i18n";
10 |
11 | interface LanguageSelectorProps {
12 | variant?: "white" | "orange";
13 | }
14 |
15 | export function LanguageSelector({ variant = "white" }: LanguageSelectorProps) {
16 | const currentLang = useCurrentLanguage();
17 | const [isOpen, setIsOpen] = useState(false);
18 | const popoverRef = useRef(null);
19 | const containerRef = useClickOutside(() => setIsOpen(false));
20 | const pathname = usePathname();
21 | const router = useRouter();
22 |
23 | const changeLanguage = (newLang: string) => {
24 | const newPathname = pathname.replace(`/${currentLang}`, `/${newLang}`);
25 | router.push(newPathname);
26 | setIsOpen(false);
27 | };
28 |
29 | return (
30 |
31 |
setIsOpen(!isOpen)}
33 | className={cn(
34 | "inline-flex items-center justify-center gap-1 rounded-lg p-2",
35 | variant === "white" && ["text-white dark:text-primary-400"],
36 | variant === "orange" && ["text-primary-400 dark:text-primary-400"],
37 | )}
38 | >
39 |
40 |
46 |
47 |
48 | {isOpen && (
49 |
61 |
62 | {LANGUAGE_LIST.map((language) => (
63 | changeLanguage(language.locale)}
66 | className={cn(
67 | "w-full text-left px-4 py-2",
68 | "text-sm text-black dark:text-primary-400",
69 | "transition-colors duration-150",
70 | language.locale === currentLang &&
71 | "bg-neutral-100 dark:bg-black",
72 | )}
73 | >
74 | {language.title}
75 |
76 | ))}
77 |
78 |
79 | )}
80 |
81 | );
82 | }
83 |
84 | export default LanguageSelector;
85 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Documentation Guide and Contribution
2 |
3 | [한국어 버전(Korean Version)](./CONTRIBUTING-KR.md)
4 |
5 | [Refer to the overall project contribution guide](../../CONTRIBUTING.md)
6 |
7 | ## Table of Contents
8 |
9 | 1. Website Structure
10 | 2. Page-specific Content
11 | 3. Technology Stack
12 | 4. Content Management
13 | 5. Multilingual Support
14 | 6. Development Roadmap
15 | 7. Additional Notes
16 |
17 | ## 1. Website Structure
18 |
19 | ```
20 | /
21 | ├── Landing Page
22 | ├── Documentation (Docs)
23 | ├── Blog
24 | ├── Showcase
25 | └── [Other Pages]
26 | ```
27 |
28 | ## 2. Page-specific Content
29 |
30 | ### 2.1 Landing Page
31 |
32 | - Project introduction
33 | - Key features highlight
34 | - Self-hosting guide with Notion (tutorial format)
35 | - "Get Started" button (linking to documentation)
36 |
37 | ### 2.2 Documentation (Docs)
38 |
39 | ```
40 | +------------------+
41 | | Sidebar |
42 | | - Category 1 |
43 | | - Document 1 |
44 | | - Document 2 |
45 | | - Category 2 |
46 | | - Document 3 |
47 | | - Document 4 |
48 | +------------------+
49 | | |
50 | | Content |
51 | | |
52 | | [Document Content]|
53 | | |
54 | +------------------+
55 | ```
56 |
57 | ### 2.3 Blog
58 |
59 | - Posts about development process, updates, tips, etc.
60 | - Sorting functionality by tags and dates
61 |
62 | ### 2.4 Showcase
63 |
64 | - Display of users' custom Notion pages
65 | - Filtering and sorting functionality
66 |
67 | ## 3. Technology Stack
68 |
69 | - Frontend: Next.js
70 | - CSS: TailwindCSS
71 | - UI Library: Radix UI or Aceternity UI (used as needed)
72 | - Markdown Rendering: Refer to Next.js official documentation
73 |
74 | ## 4. Content Management
75 |
76 | ### 4.1 Documentation (Docs)
77 |
78 | - Location: `/content/docs/[lang]/[category]/[document].md`
79 | - Frontmatter:
80 | - group: category
81 | - order: document order
82 | - title: document title
83 | - description: document description
84 |
85 | ### 4.2 Blog
86 |
87 | - Location: `/content/blog/[lang]/[post].md`
88 | - Frontmatter:
89 | - title: post title
90 | - description: post description
91 | - date: publication date
92 |
93 | ## 5. Multilingual Support
94 |
95 | - Supported languages: Korean (kr), English (en), Chinese (cn)
96 | - Markdown files: Managed separately for each language
97 | - UI text: Use multilingual support library
98 |
99 | ## 6. Development Roadmap
100 |
101 | 1. Basic Next.js project setup
102 | 2. TailwindCSS integration
103 | 3. Markdown rendering system implementation
104 | 4. Multilingual support system implementation
105 | 5. Landing page development
106 | 6. Documentation page development
107 | 7. Blog page development
108 | 8. Showcase page development
109 | 9. UI/UX improvements and optimization
110 | 10. Content creation and translation
111 | 11. Beta testing and feedback collection
112 | 12. Official launch
113 |
114 | ## 7. Additional Notes
115 |
116 | - Manage documents and blog posts in Markdown format
117 | - Include practical examples using the Notionpresso library on the landing page
118 | - Encourage continuous content updates and community participation
119 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/02. heading.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 3
4 | title: "标题块"
5 | slug: "notion-block-heading"
6 | description: "详细介绍 Notionpresso 的标题块类型。"
7 | ---
8 |
9 | # 标题块
10 |
11 | 标题块用于表示文档的结构,支持三个级别(标题 1、标题 2、标题 3)。
12 |
13 | ## 数据结构
14 |
15 | 标题块的 Notion API 数据结构如下(以标题 1 为例):
16 |
17 | ```json
18 | {
19 | "type": "heading_1",
20 | "heading_1": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a heading",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a heading",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "is_toggleable": false
42 | }
43 | }
44 | ```
45 |
46 | - `type`: "heading_1", "heading_2", 或 "heading_3" 其中之一。
47 | - `rich_text`: 包含标题文本内容和样式信息的数组。
48 | - `color`: 指定标题的颜色。
49 | - `is_toggleable`: 表示是否使用切换功能。
50 |
51 | ## React 组件
52 |
53 | Notionpresso 中用于渲染标题块的组件如下:
54 |
55 | ```jsx
56 | import React, { useMemo } from "react";
57 | import type { HeadingsProps, HeadingConfig } from "../types";
58 | import { getColorCss } from "../utils";
59 | import RichText from "./internal/rich-text";
60 |
61 | const Headings: React.FC = ({ children, type, ...props }) => {
62 | const {
63 | [type]: { color, rich_text: texts, is_toggleable },
64 | } = props;
65 |
66 | const { headingTag: HeadingTag, headingClassName } =
67 | useMemo(() => {
68 | switch (type) {
69 | case "heading_2":
70 | return { headingTag: "h2", headingClassName: "notion-h2" };
71 | case "heading_3":
72 | return { headingTag: "h3", headingClassName: "notion-h3" };
73 | default:
74 | return { headingTag: "h1", headingClassName: "notion-h1" };
75 | }
76 | }, [type]);
77 |
78 | return (
79 |
82 |
83 |
84 |
85 | {children}
86 |
87 | );
88 | };
89 |
90 | export default Headings;
91 | ```
92 |
93 | ## 使用示例
94 |
95 | 以下是使用标题块的示例:
96 |
97 | ```jsx
98 | import { Notion } from "@notionpresso/react";
99 |
100 | function MyNotionPage({ blocks }) {
101 | return (
102 |
103 |
104 |
105 | );
106 | }
107 | ```
108 |
109 | 这里 `blocks` 是从 Notion API 接收到的块数据数组。
110 |
111 | ## 样式
112 |
113 | 标题块的样式可以通过以下 CSS 类进行自定义:
114 |
115 | - `.notion-block`: 应用于所有 Notion 块的基本样式
116 | - `.notion-h1`, `.notion-h2`, `.notion-h3`: 针对每个标题级别的特定样式
117 | - `.notion-h1-content`, `.notion-h2-content`, `.notion-h3-content`: 针对每个标题内容的样式
118 |
119 | 如果需要额外的样式,可以针对这些类编写 CSS。
120 |
121 | ## 注意事项
122 |
123 | - 标题块是表示文档结构的重要元素,因此建议使用适当的标题级别。
124 | - 如果 `is_toggleable` 属性为 true,则该标题将具有切换功能。在这种情况下,可能需要额外的逻辑。
125 |
--------------------------------------------------------------------------------
/components/docs/docs-layout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useEffect } from "react";
4 | import ReactMarkdown from "react-markdown";
5 | import rehypeRaw from "rehype-raw";
6 | import rehypeHighlight from "rehype-highlight";
7 | import remarkGfm from "remark-gfm";
8 | import "highlight.js/styles/github-dark.css"; // 다크 테마
9 | import {
10 | DynamicLayout,
11 | MobileSidebar,
12 | NavigationButton,
13 | Sidebar,
14 | TOC,
15 | } from "@/components/docs";
16 | import { generateTOC } from "@/lib/generateTOC";
17 |
18 | interface GuidePageProps {
19 | params: {
20 | lang: string;
21 | group: string;
22 | slug: string;
23 | };
24 | content: string;
25 | title: string;
26 | prevDocument: any;
27 | nextDocument: any;
28 | allDocuments: any[];
29 | }
30 |
31 | export default function DocsLayout({
32 | params,
33 | content,
34 | title,
35 | prevDocument,
36 | nextDocument,
37 | allDocuments,
38 | }: GuidePageProps) {
39 | const { lang, group, slug } = params;
40 | const tocItems = generateTOC(content);
41 |
42 | useEffect(() => {
43 | const headings = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
44 | headings.forEach((heading) => {
45 | heading.id =
46 | heading.textContent?.toLowerCase().replace(/\s+/g, "-") ?? "";
47 | });
48 | }, [content]);
49 |
50 | return (
51 |
59 | }
60 | mobileSidebar={
61 |
67 | }
68 | toc={ }
69 | >
70 |
71 |
72 | {group} > {title}
73 |
74 |
79 | {content}
80 |
81 |
82 |
83 |
84 |
85 | {prevDocument && (
86 |
91 | )}
92 |
93 |
94 | {nextDocument && (
95 |
100 | )}
101 |
102 |
103 |
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/06. quote.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 6
4 | title: "引用ブロック"
5 | slug: "quote"
6 | description: "ノーションプレッソの引用ブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # 引用ブロック
10 |
11 | 引用ブロックは、他のソースからの引用テキストを表示するために使用されます。通常、視覚的に区別されるスタイルで表現されます。
12 |
13 | ## データ構造
14 |
15 | 引用ブロックのノーションAPIデータ構造は以下の通りです:
16 |
17 | ```json
18 | {
19 | "type": "quote",
20 | "quote": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a quote",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a quote",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 引用文のテキスト内容とスタイル情報を含む配列です。
46 | - `color`: 引用文の色を指定します。
47 |
48 | ## Reactコンポーネント
49 |
50 | Notionpressoで引用ブロックをレンダリングするコンポーネントは以下の通りです:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { QuoteArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type QuoteProps = {
59 | children?: React.ReactNode;
60 | } & QuoteArgs;
61 |
62 | const Quote: React.FC = ({ children, ...props }) => {
63 | const {
64 | quote: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 |
73 | {children}
74 |
75 |
76 | );
77 | };
78 |
79 | export default Quote;
80 | ```
81 |
82 | ## 使用例
83 |
84 | 引用ブロックを使用する例は以下の通りです:
85 |
86 | ```jsx
87 | import { Notion } from "@notionpresso/react";
88 |
89 | function MyNotionPage({ blocks }) {
90 | return (
91 |
92 |
93 |
94 | );
95 | }
96 | ```
97 |
98 | ここで`blocks`は、Notion APIから受け取ったブロックデータの配列です。
99 |
100 | ## スタイリング
101 |
102 | 引用ブロックのスタイルは、以下のCSSクラスを使用してカスタマイズできます:
103 |
104 | - `.notion-block`: すべてのNotionブロックに適用される基本スタイル
105 | - `.notion-quote`: 引用ブロックの特定のスタイル
106 | - `.notion-quote-content`: 引用文の内容のスタイル
107 |
108 | 追加のスタイリングが必要な場合は、これらのクラスを対象にCSSを記述することができます。通常、引用ブロックは以下のようなスタイル特性を持ちます:
109 |
110 | - 左または両側に太い枠線
111 | - 若干のインデント
112 | - 背景色の変更
113 | - フォントスタイルの変更(例:斜体)
114 |
115 | 例えば、以下のようなCSSを適用することができます:
116 |
117 | ```css
118 | .notion-quote {
119 | border-left: 4px solid #ccc;
120 | padding-left: 16px;
121 | margin-left: 0;
122 | margin-right: 0;
123 | font-style: italic;
124 | background-color: #f9f9f9;
125 | }
126 | ```
127 |
128 | ## ネストのサポート
129 |
130 | 引用ブロックは、他のブロックを含むことができます。これは`children`プロップを使用して処理されます。例えば、引用文の中にリストや他のテキストブロックを含むことができます。
131 |
132 | ## 注意点
133 |
134 | - 引用ブロックは通常、短い引用文に使用されます。長い引用文の場合、読みやすさのために複数の引用ブロックに分割することが考えられます。
135 | - 引用文の出典を明示することが重要です。これは、引用ブロックの下に別のParagraphブロックを追加することで実現できます。
136 | - アクセシビリティを考慮し、スクリーンリーダーを使用するユーザーのために適切なARIA属性を追加することが重要です。例えば、`role="blockquote"`属性を使用することができます。
137 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/03. bulleted-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 4
4 | title: "项目符号列表项块"
5 | slug: "notion-block-bulleted-list-item"
6 | description: "详细介绍 Notionpresso 的项目符号列表项块类型。"
7 | ---
8 |
9 | # 项目符号列表项块
10 |
11 | 项目符号列表项块用于创建无序列表。每个项目以项目符号开始。
12 |
13 | ## 数据结构
14 |
15 | 项目符号列表项块的 Notion API 数据结构如下:
16 |
17 | ```json
18 | {
19 | "type": "bulleted_list_item",
20 | "bulleted_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a bulleted list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a bulleted list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 列表项的文本内容和样式信息。
46 | - `color`: 文本的颜色。
47 |
48 | ## React 组件
49 |
50 | Notionpresso 中用于渲染项目符号列表项块的组件如下:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { BulletedListItemArgs } from "../types";
55 | import { bulletedListItemMarker, getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type BulletedListItemProps = {
59 | children?: React.ReactNode;
60 | } & BulletedListItemArgs;
61 |
62 | const BulletedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | bulleted_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = bulletedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default BulletedListItem;
95 | ```
96 |
97 | ## 使用示例
98 |
99 | 使用项目符号列表项块的示例如下:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | 这里 `blocks` 是来自 Notion API 的块数据数组。
114 |
115 | ## 样式
116 |
117 | 项目符号列表项块的样式可以通过以下 CSS 类进行自定义:
118 |
119 | - `.notion-block`: 所有 Notion 块的默认样式
120 | - `.notion-list-bulleted`: 项目符号列表项块的特定样式
121 | - `.notion-list-bulleted-content`: 列表项内容的样式
122 | - `.notion-list-marker`: 项目符号的样式
123 |
124 | 如果需要额外的样式,可以为这些类编写 CSS。
125 |
126 | ## 嵌套列表处理
127 |
128 | 项目符号列表项块可以嵌套。嵌套列表通过 `children` prop 处理。嵌套级别不同,项目符号的形状也会有所不同,这由 `bulletedListItemMarker.getMarker` 函数处理。
129 |
130 | ## 注意事项
131 |
132 | - 项目符号列表项块连续使用时会自动组合成一个列表。
133 | - 使用嵌套列表时,建议使用适当的缩进以明确层次结构。
134 | - 项目符号的形状遵循 Notion 的默认样式,但可以通过 CSS 进行自定义。
135 |
--------------------------------------------------------------------------------
/content/guide/ja/01. getting-started/01. introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "getting-started"
3 | order: 1
4 | title: "プロジェクト紹介"
5 | slug: "introduction"
6 | description: "Notionpressoプロジェクトの概要、主な機能、そして活用可能性について紹介します。"
7 | ---
8 |
9 | # Notionpressoプロジェクト紹介
10 |
11 | ## Notionpressoとは?
12 |
13 | Notionpressoは、Notionの強力なコンテンツ作成機能をウェブサイトに簡単に取り込むことができるツールです。このライブラリを使用すると、Notionで作成したコンテンツをそのままReactベースのウェブサイトでレンダリングすることができます。開発者には素早く結果を生み出せるツールとなり、コンテンツ作成者には便利な管理ツールとなる、まさに「開発者と作成者の両方のためのマジックポーション」のような存在です。✨
14 |
15 | ## 主な機能と提供する価値
16 |
17 | ### 🛠️ NotionデータのReactレンダリング
18 |
19 | Notionで作成した文章、画像、リスト、コードブロックなど、様々なコンテンツをReactコンポーネントに簡単に変換できます。以下のような構造でNotionページがウェブで生まれ変わります:
20 |
21 | ```jsx
22 | import { Notion, type NotionPageData } from 'Notionpresso';
23 |
24 | interface NotionPageProps {
25 | pageData: NotionPageData;
26 | }
27 |
28 | function NotionPage({ pageData }: NotionPageProps) {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 | ```
40 |
41 | この例では、`NotionPageData` 型はNotionページのデータ構造を定義します。これにより、タイプスクリプトの型チェック機能を利用して、より安定したコードを作成できます。
42 |
43 | ### 🎨 カスタマイズの自由
44 |
45 | シーエスエスだけをいじることで、簡単にテーマを変更したり、基本提供されているコンポーネントを部分的に変更して、必要な部分だけをカスタマイズできます。さらに、完全に新しいコンポーネントを作成して、Notionpressoに組み込むことも可能です。コードを扱う開発者には無限の可能性を提供します。
46 |
47 | ### 🚀 迅速なサイト構築
48 |
49 | シーエルアイを使用して、Notionデータを簡単に抽出し、ネクストジェーエスと同様のフレームワークを使用して、迅速に静的サイトを構築できます。いくつかのコマンドだけでサイトを構築するプロセスは、テキストから直接ウェブサイトが完成するような感覚を与えます。
50 |
51 | ```bash
52 | npx npresso --page YOUR_PAGE_URL --auth YOUR_API_KEY
53 | ```
54 |
55 | シーエルアイを実行すると、以下のようなフォルダ構造でNotionデータが抽出されます:
56 |
57 | ```
58 | my-notion-project/
59 | ├── notion-data/
60 | │ ├── page-id-1.json
61 | │ ├── page-id-2.json
62 | │ └── ...
63 | └── public/
64 | └── notion-dadta/
65 | ├── page-id-1/
66 | │ ├── image1.png
67 | │ └── image2.jpg
68 | ├── page-id-2/
69 | │ └── image1.png
70 | └── ...
71 | ```
72 |
73 | - `notion-data/`: 各Notionページの内容がJSON形式で保存されます。ファイル名はページのスラッグに基づいて生成されます。
74 | - `public/notion-data/`: Notionページで使用された画像は、各ページごとに分類されて保存されます。
75 |
76 | このように抽出されたデータは、Notionpressoコンポーネントと簡単に連携でき、Notionのコンテンツを迅速にウェブサイトに変換できます。JSONファイルの内容をReactコンポーネントに渡すだけで、Notionページの構造とスタイルがそのままウェブサイトに反映されます。
77 |
78 | ## ターゲット利用者とそれらの活用可能性
79 |
80 | ### 開発者
81 |
82 | Notionで作成したコンテンツをウェブサイトに簡単にレンダリングし、カスタマイズできる自由が提供されます。迅速にウェブサイトを構築したい場合や、商用サービスの限界を超えて、望むスタイルにグラデーションしたい場合、Notionpressoが答えです。
83 |
84 | ### デザイナーとクリエイター
85 |
86 | Notionで作成したコンテンツを直接ウェブに反映できるため、デザインの一貫性を維持しながら、迅速にコンテンツを更新できます。
87 |
88 | ### 活用可能性の例
89 |
90 | - **個人のブログ**: Notionで簡単に記事を作成し、自動的にウェブサイトに反映されます。
91 | - **会社の紹介と採用ページ**: Notionを活用して、会社のプロフィールや採用情報を迅速に更新できます。
92 | - **ポートフォリオサイト**: プロジェクトと作業物を簡単に更新できるため、ポートフォリオの管理が一層楽になります。
93 |
94 | ## Notionpressoを選択する理由
95 |
96 | 1. **開発者中心のカスタマイズ**: 他の商用サービスでは、手の届かない細かい部分まで修正できます。コードを通しての無限の可能性、これがNotionpressoの魅力です。
97 |
98 | 2. **オープンソースの自由**: すべてが透明であり、各自の手で直接操作できます。必要に応じてコードを修正し、プロジェクトを自分のサーバーでセルフホスティングできるため、コストの負担もありません。
99 |
100 | 3. **迅速な結果と高い効率性**: Notionでコンテンツを作成し、簡単な設定だけでウェブサイトで直接使用できるため、コンテンツ作成と配布のプロセスが一層簡素化されます。
101 |
102 | Notionpressoは、Notionのページを魅力的なウェブサイトに変換するための橋渡しをします。これで、複雑な設定は終わり、Notionから直接開始してみましょう!
103 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/06. Quote.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 6
4 | title: "Quote 블록"
5 | slug: "quote"
6 | description: "Notionpresso의 Quote 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Quote 블록
10 |
11 | Quote 블록은 다른 출처에서 인용한 텍스트를 표시하는 데 사용됩니다. 일반적으로 시각적으로 구분되는 스타일로 표현됩니다.
12 |
13 | ## 데이터 구조
14 |
15 | Quote 블록의 Notion API 데이터 구조는 다음과 같습니다:
16 |
17 | ```json
18 | {
19 | "type": "quote",
20 | "quote": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a quote",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a quote",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 인용문의 텍스트 내용과 스타일 정보를 담고 있는 배열입니다.
46 | - `color`: 인용문의 색상을 지정합니다.
47 |
48 | ## React 컴포넌트
49 |
50 | Notionpresso에서 Quote 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { QuoteArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type QuoteProps = {
59 | children?: React.ReactNode;
60 | } & QuoteArgs;
61 |
62 | const Quote: React.FC = ({ children, ...props }) => {
63 | const {
64 | quote: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 |
73 | {children}
74 |
75 |
76 | );
77 | };
78 |
79 | export default Quote;
80 | ```
81 |
82 | ## 사용 예시
83 |
84 | Quote 블록을 사용하는 예시는 다음과 같습니다:
85 |
86 | ```jsx
87 | import { Notion } from "@notionpresso/react";
88 |
89 | function MyNotionPage({ blocks }) {
90 | return (
91 |
92 |
93 |
94 | );
95 | }
96 | ```
97 |
98 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
99 |
100 | ## 스타일링
101 |
102 | Quote 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
103 |
104 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
105 | - `.notion-quote`: Quote 블록 특정 스타일
106 | - `.notion-quote-content`: 인용문 내용의 스타일
107 |
108 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다. 일반적으로 Quote 블록은 다음과 같은 스타일 특성을 가집니다:
109 |
110 | - 왼쪽 또는 양쪽에 두꺼운 테두리
111 | - 약간의 들여쓰기
112 | - 배경색 변경
113 | - 폰트 스타일 변경 (예: 기울임꼴)
114 |
115 | 예를 들어, 다음과 같은 CSS를 적용할 수 있습니다:
116 |
117 | ```css
118 | .notion-quote {
119 | border-left: 4px solid #ccc;
120 | padding-left: 16px;
121 | margin-left: 0;
122 | margin-right: 0;
123 | font-style: italic;
124 | background-color: #f9f9f9;
125 | }
126 | ```
127 |
128 | ## 중첩 지원
129 |
130 | Quote 블록은 다른 블록을 포함할 수 있습니다. 이는 `children` prop을 통해 처리됩니다. 예를 들어, 인용문 안에 리스트나 다른 텍스트 블록을 포함할 수 있습니다.
131 |
132 | ## 주의사항
133 |
134 | - Quote 블록은 일반적으로 짧은 인용문에 사용됩니다. 긴 인용문의 경우, 가독성을 위해 여러 개의 Quote 블록으로 나누는 것이 좋을 수 있습니다.
135 | - 인용문의 출처를 명시하는 것이 좋습니다. 이는 Quote 블록 아래에 별도의 Paragraph 블록으로 추가할 수 있습니다.
136 | - 접근성을 고려하여, 스크린 리더 사용자를 위해 적절한 ARIA 속성을 추가하는 것이 좋습니다. 예를 들어, `role="blockquote"` 속성을 사용할 수 있습니다.
137 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/02. heading.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 3
4 | title: "見出しブロック"
5 | slug: "notion-block-heading"
6 | description: "ノーションプレッソの見出しブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # 見出しブロック
10 |
11 | 見出しブロックは文書の構造を表現するために使用され、3つのレベル(見出し1、見出し2、見出し3)をサポートしています。
12 |
13 | ## データ構造
14 |
15 | 見出しブロックのノーションAPIデータ構造は以下の通りです(見出し1の例):
16 |
17 | ```json
18 | {
19 | "type": "heading_1",
20 | "heading_1": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a heading",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a heading",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "is_toggleable": false
42 | }
43 | }
44 | ```
45 |
46 | - `type`: "heading_1", "heading_2", または "heading_3" のいずれかです。
47 | - `rich_text`: 見出しテキストの内容とスタイル情報を含む配列です。
48 | - `color`: 見出しの色を指定します。
49 | - `is_toggleable`: トグル機能を使用するかどうかを示すフラグです。
50 |
51 | ## Reactコンポーネント
52 |
53 | Notionpressoで見出しブロックをレンダリングするコンポーネントは以下の通りです:
54 |
55 | ```jsx
56 | import React, { useMemo } from "react";
57 | import type { HeadingsProps, HeadingConfig } from "../types";
58 | import { getColorCss } from "../utils";
59 | import RichText from "./internal/rich-text";
60 |
61 | const Headings: React.FC = ({ children, type, ...props }) => {
62 | const {
63 | [type]: { color, rich_text: texts, is_toggleable },
64 | } = props;
65 |
66 | const { headingTag: HeadingTag, headingClassName } =
67 | useMemo(() => {
68 | switch (type) {
69 | case "heading_2":
70 | return { headingTag: "h2", headingClassName: "notion-h2" };
71 | case "heading_3":
72 | return { headingTag: "h3", headingClassName: "notion-h3" };
73 | default:
74 | return { headingTag: "h1", headingClassName: "notion-h1" };
75 | }
76 | }, [type]);
77 |
78 | return (
79 |
82 |
83 |
84 |
85 | {children}
86 |
87 | );
88 | };
89 |
90 | export default Headings;
91 | ```
92 |
93 | ## 使用例
94 |
95 | 見出しブロックを使用する例は以下の通りです:
96 |
97 | ```jsx
98 | import { Notion } from "@notionpresso/react";
99 |
100 | function MyNotionPage({ blocks }) {
101 | return (
102 |
103 |
104 |
105 | );
106 | }
107 | ```
108 |
109 | ここで `blocks` はNotion APIから取得したブロックデータの配列です。
110 |
111 | ## スタイリング
112 |
113 | 見出しブロックのスタイルは以下のCSSクラスを使用してカスタマイズできます:
114 |
115 | - `.notion-block`: すべてのNotionブロックに適用されるデフォルトのスタイル
116 | - `.notion-h1`, `.notion-h2`, `.notion-h3`: 各見出しレベルに対する特定のスタイル
117 | - `.notion-h1-content`, `.notion-h2-content`, `.notion-h3-content`: 各見出しの内容のスタイル
118 |
119 | 追加のスタイリングが必要な場合は、これらのクラスを対象にCSSを記述することができます。
120 |
121 | ## 注意点
122 |
123 | - 見出しブロックは文書の構造を表現する重要な要素であるため、適切なレベルの見出しを使用することが推奨されます。
124 | - `is_toggleable` プロパティが true の場合、その見出しはトグル機能を持つことになります。この場合、追加のロジックが必要になる場合があります。
125 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/02. heading.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 3
4 | title: "Heading 블록"
5 | slug: "notion-block-heading"
6 | description: "Notionpresso의 Heading 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Heading 블록
10 |
11 | Heading 블록은 문서의 구조를 나타내는 데 사용되며, 세 가지 레벨(Heading 1, Heading 2, Heading 3)을 지원합니다.
12 |
13 | ## 데이터 구조
14 |
15 | Heading 블록의 Notion API 데이터 구조는 다음과 같습니다 (Heading 1의 예시):
16 |
17 | ```json
18 | {
19 | "type": "heading_1",
20 | "heading_1": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a heading",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a heading",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "is_toggleable": false
42 | }
43 | }
44 | ```
45 |
46 | - `type`: "heading_1", "heading_2", 또는 "heading_3" 중 하나입니다.
47 | - `rich_text`: 헤딩 텍스트의 내용과 스타일 정보를 담고 있는 배열입니다.
48 | - `color`: 헤딩의 색상을 지정합니다.
49 | - `is_toggleable`: 토글 기능 사용 여부를 나타냅니다.
50 |
51 | ## React 컴포넌트
52 |
53 | Notionpresso에서 Heading 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
54 |
55 | ```jsx
56 | import React, { useMemo } from "react";
57 | import type { HeadingsProps, HeadingConfig } from "../types";
58 | import { getColorCss } from "../utils";
59 | import RichText from "./internal/rich-text";
60 |
61 | const Headings: React.FC = ({ children, type, ...props }) => {
62 | const {
63 | [type]: { color, rich_text: texts, is_toggleable },
64 | } = props;
65 |
66 | const { headingTag: HeadingTag, headingClassName } =
67 | useMemo(() => {
68 | switch (type) {
69 | case "heading_2":
70 | return { headingTag: "h2", headingClassName: "notion-h2" };
71 | case "heading_3":
72 | return { headingTag: "h3", headingClassName: "notion-h3" };
73 | default:
74 | return { headingTag: "h1", headingClassName: "notion-h1" };
75 | }
76 | }, [type]);
77 |
78 | return (
79 |
82 |
83 |
84 |
85 | {children}
86 |
87 | );
88 | };
89 |
90 | export default Headings;
91 | ```
92 |
93 | ## 사용 예시
94 |
95 | Heading 블록을 사용하는 예시는 다음과 같습니다:
96 |
97 | ```jsx
98 | import { Notion } from "@notionpresso/react";
99 |
100 | function MyNotionPage({ blocks }) {
101 | return (
102 |
103 |
104 |
105 | );
106 | }
107 | ```
108 |
109 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
110 |
111 | ## 스타일링
112 |
113 | Heading 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
114 |
115 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
116 | - `.notion-h1`, `.notion-h2`, `.notion-h3`: 각 헤딩 레벨에 대한 특정 스타일
117 | - `.notion-h1-content`, `.notion-h2-content`, `.notion-h3-content`: 각 헤딩 내용의 스타일
118 |
119 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다.
120 |
121 | ## 주의사항
122 |
123 | - Heading 블록은 문서의 구조를 나타내는 중요한 요소이므로, 적절한 레벨의 헤딩을 사용하는 것이 좋습니다.
124 | - `is_toggleable` 속성이 true인 경우, 해당 헤딩은 토글 기능을 가지게 됩니다. 이 경우 추가적인 로직이 필요할 수 있습니다.
125 |
--------------------------------------------------------------------------------
/content/guide/ko/01. getting-started/01. introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "getting-started"
3 | order: 1
4 | title: "프로젝트 소개"
5 | slug: "introduction"
6 | description: "Notionpresso 프로젝트의 개요, 주요 기능, 그리고 활용 가능성에 대해 소개합니다."
7 | ---
8 |
9 | # Notionpresso 프로젝트 소개
10 |
11 | ## Notionpresso이란?
12 |
13 | Notionpresso은 Notion의 강력한 콘텐츠 작성 기능을 웹사이트로 손쉽게 가져올 수 있게 해주는 도구입니다. 이 라이브러리를 사용하면 Notion에서 작성한 콘텐츠를 그대로 React 기반의 웹사이트에서 렌더링할 수 있습니다. 개발자에게는 빠르게 결과를 만들어낼 수 있는 도구가 되고, 콘텐츠 작성자에게는 편리한 관리 도구가 되는, 그야말로 "개발자와 작성자 모두를 위한 매직 포션" 같은 존재입니다. ✨
14 |
15 | ## 주요 기능 및 제공하는 가치
16 |
17 | ### 🛠️ Notion 데이터의 React 렌더링
18 |
19 | Notion에서 작성한 글, 이미지, 리스트, 코드 블록 등 다양한 콘텐츠를 React 컴포넌트로 쉽게 변환할 수 있습니다. 다음과 같은 구조로 Notion 페이지가 웹에서 재탄생합니다:
20 |
21 | ```jsx
22 | import { Notion, type NotionPageData } from 'Notionpresso';
23 |
24 | interface NotionPageProps {
25 | pageData: NotionPageData;
26 | }
27 |
28 | function NotionPage({ pageData }: NotionPageProps) {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 | ```
40 |
41 | 이 예제에서 `NotionPageData` 타입은 Notion 페이지의 데이터 구조를 정의합니다. 이를 통해 TypeScript의 타입 체크 기능을 활용하여 더 안정적인 코드를 작성할 수 있습니다.
42 |
43 | ### 🎨 커스터마이징의 자유
44 |
45 | CSS만 건드려서 간단하게 테마를 바꾸거나, 기본 제공 컴포넌트를 부분적으로 수정해 필요한 부분만 커스터마이징할 수 있습니다. 심지어 완전히 새로운 컴포넌트를 만들어 Notionpresso에 결합할 수도 있어요. 코드를 다루는 개발자에게는 무한한 가능성을 제공합니다.
46 |
47 | ### 🚀 빠른 사이트 구축
48 |
49 | @notionpresso/cli CLI를 사용해 Notion 데이터를 쉽게 추출하고, Next.js와 같은 프레임워크를 통해 빠르게 정적 사이트를 구축할 수 있습니다. 몇 가지 명령어만으로 사이트를 구축하는 과정은 마치 텍스트에서 바로 웹사이트가 완성되는 느낌을 줍니다.
50 |
51 | ```bash
52 | npx npresso --page YOUR_PAGE_URL --auth YOUR_API_KEY
53 | ```
54 |
55 | @notionpresso/cli를 실행하면 다음과 같은 폴더 구조로 Notion 데이터가 추출됩니다:
56 |
57 | ```
58 | my-notion-project/
59 | ├── notion-data/
60 | │ ├── page-id-1.json
61 | │ ├── page-id-2.json
62 | │ └── ...
63 | └── public/
64 | └── notion-dadta/
65 | ├── page-id-1/
66 | │ ├── image1.png
67 | │ └── image2.jpg
68 | ├── page-id-2/
69 | │ └── image1.png
70 | └── ...
71 | ```
72 |
73 | - `notion-data/`: 각 Notion 페이지의 내용이 JSON 형식으로 저장됩니다. 파일명은 페이지의 slug를 기반으로 생성됩니다.
74 | - `public/notion-data/`: Notion 페이지에서 사용된 이미지들이 각 페이지별로 분류되어 저장됩니다.
75 |
76 | 이렇게 추출된 데이터는 Notionpresso 컴포넌트와 쉽게 연동되어, Notion의 콘텐츠를 빠르게 웹사이트로 변환할 수 있습니다. JSON 파일의 내용을 React 컴포넌트에 전달하기만 하면, Notion 페이지의 구조와 스타일이 그대로 웹사이트에 반영됩니다.
77 |
78 | ## 타겟 사용자와 그들의 활용 가능성
79 |
80 | ### 개발자
81 |
82 | Notion에서 작성한 콘텐츠를 웹사이트에 쉽게 렌더링하고, 커스터마이징할 수 있는 자유가 주어집니다. 빠르게 웹사이트를 구축하고 싶거나, 상용 서비스의 한계를 넘어 원하는 스타일로 꾸미고 싶다면 Notionpresso이 답입니다.
83 |
84 | ### 디자이너 및 크리에이터
85 |
86 | Notion에서 작성한 콘텐츠를 직접 웹에 반영할 수 있어 디자인에 일관성을 유지하면서 빠르게 콘텐츠를 업데이트할 수 있습니다.
87 |
88 | ### 활용 가능성 예시
89 |
90 | - **개인 블로그**: Notion에서 쉽게 글을 작성하고, 자동으로 웹사이트에 반영합니다.
91 | - **회사 소개 및 채용 페이지**: Notion을 활용해 회사 프로필이나 채용 정보를 빠르게 업데이트할 수 있습니다.
92 | - **포트폴리오 사이트**: 프로젝트와 작업물을 손쉽게 업데이트할 수 있어, 포트폴리오 관리가 한결 쉬워집니다.
93 |
94 | ## Notionpresso을 선택해야 하는 이유
95 |
96 | 1. **개발자 중심의 커스터마이징**: 다른 상용 서비스에서는 손댈 수 없는 세밀한 부분까지 수정할 수 있습니다. 코드를 통한 무한한 가능성, 이것이 Notionpresso의 매력입니다.
97 |
98 | 2. **오픈소스의 자유**: 모든 것이 투명하고, 여러분의 손에서 직접 다룰 수 있습니다. 필요하면 코드를 수정하고, 프로젝트를 자신의 서버에서 셀프 호스팅할 수 있어 비용 부담도 없습니다.
99 |
100 | 3. **빠른 결과와 높은 효율성**: Notion에서 콘텐츠를 작성하고, 간단한 설정만으로 웹사이트에서 바로 사용할 수 있습니다. 콘텐츠 작성과 배포의 과정이 한층 더 간소화됩니다.
101 |
102 | Notionpresso은 여러분의 Notion 페이지를 멋진 웹사이트로 바꾸는 다리입니다. 이제 복잡한 설정은 그만, Notion에서 바로 시작해 보세요!
103 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/05. numbered-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 5
4 | title: "编号列表项块"
5 | slug: "numbered-list-item"
6 | description: "详细介绍 Notionpresso 的编号列表项块类型。"
7 | ---
8 |
9 | # 编号列表项块
10 |
11 | 编号列表项块用于创建有序列表。每个项目以顺序编号开始。
12 |
13 | ## 数据结构
14 |
15 | 编号列表项块的 Notion API 数据结构如下:
16 |
17 | ```json
18 | {
19 | "type": "numbered_list_item",
20 | "numbered_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a numbered list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a numbered list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 列表项的文本内容和样式信息。
46 | - `color`: 文本的颜色。
47 |
48 | ## React 组件
49 |
50 | Notionpresso 中用于渲染编号列表项块的组件如下:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { NumberedListItemArgs } from "../types";
55 | import { getColorCss, numberedListItemMarker } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type NumberedListProps = {
59 | children?: React.ReactNode;
60 | } & NumberedListItemArgs;
61 |
62 | const NumberedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | numbered_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = numberedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default NumberedListItem;
95 | ```
96 |
97 | ## 使用示例
98 |
99 | 使用编号列表项块的示例如下:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | 这里 `blocks` 是从 Notion API 接收到的块数据数组。
114 |
115 | ## 样式
116 |
117 | 编号列表项块的样式可以通过以下 CSS 类进行自定义:
118 |
119 | - `.notion-block`: 所有 Notion 块的默认样式
120 | - `.notion-list-numbered`: 编号列表项块的特定样式
121 | - `.notion-list-numbered-content`: 列表项内容的样式
122 | - `.notion-list-marker`: 编号样式
123 |
124 | 如果需要更多样式,可以对这些类进行样式编写。
125 |
126 | ## 编号生成方式
127 |
128 | 编号生成方式由 `numberedListItemMarker.getMarker` 函数决定。该函数会考虑当前项的位置和嵌套级别,返回适当的编号或字母。默认情况下,编号顺序如下:
129 |
130 | 1. 顶级级别:1, 2, 3, ...
131 | 2. 第二级别:a, b, c, ...
132 | 3. 第三级别:i, ii, iii, ...
133 |
134 | 可以根据需要进行自定义。
135 |
136 | ## 处理嵌套列表
137 |
138 | 编号列表项块也可以嵌套。嵌套列表通过 `children` prop 处理。嵌套级别不同,编号生成方式也会有所不同,这由 `numberedListItemMarker.getMarker` 函数处理。
139 |
140 | ## 注意事项
141 |
142 | - 编号列表项块在连续使用时会自动组合成一个列表。
143 | - 使用嵌套列表时,建议使用适当的缩进以明确层次结构。
144 | - 编号生成方式遵循 Notion 的默认样式,但可以通过 CSS 进行自定义。
145 | - 如果列表中断后再重新开始,编号生成可能会重新开始,因此需要小心。
146 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/08. callout.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 7
4 | title: "标注块"
5 | slug: "callout"
6 | description: "详细介绍 Notionpresso 的标注块类型。"
7 | ---
8 |
9 | # 标注块
10 |
11 | 标注块用于强调需要引起注意的内容。通常使用图标和彩色背景来实现视觉上的突出显示。
12 |
13 | ## 数据结构
14 |
15 | 标注块的 Notion API 数据结构如下:
16 |
17 | ```json
18 | {
19 | "type": "callout",
20 | "callout": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a callout",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a callout",
37 | "href": null
38 | }
39 | ],
40 | "icon": {
41 | "type": "emoji",
42 | "emoji": "💡"
43 | },
44 | "color": "gray_background"
45 | }
46 | }
47 | ```
48 |
49 | - `rich_text`: Callout 的文本内容和样式信息。
50 | - `icon`: Callout 旁边显示的图标信息。可以是 Emoji 或外部图像 URL。
51 | - `color`: Callout 的背景颜色。
52 |
53 | ## React 组件
54 |
55 | Notionpresso 中渲染标注块的组件如下:
56 |
57 | ```jsx
58 | import React from "react";
59 | import type { CalloutArgs } from "../types";
60 | import { getColorCss } from "../utils";
61 | import RichText from "./internal/rich-text";
62 | import Icon from "./internal/icon";
63 |
64 | type CalloutProps = {
65 | children?: React.ReactNode;
66 | } & CalloutArgs;
67 |
68 | const Callout: React.FC = ({ children, ...props }) => {
69 | const { callout } = props;
70 |
71 | return (
72 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | {children}
84 |
85 | );
86 | };
87 |
88 | export default Callout;
89 | ```
90 |
91 | ## 使用示例
92 |
93 | 标注块的使用示例如下:
94 |
95 | ```jsx
96 | import { Notion } from "@notionpresso/react";
97 |
98 | function MyNotionPage({ blocks }) {
99 | return (
100 |
101 |
102 |
103 | );
104 | }
105 | ```
106 |
107 | 这里 `blocks` 是从 Notion API 接收到的块数据数组。
108 |
109 | ## 图标处理
110 |
111 | 标注块的图标通过 `Icon` 组件处理。这个组件支持 Emoji 和外部图像 URL。根据图标类型选择合适的渲染方式。
112 |
113 | ## 样式
114 |
115 | 标注块的样式可以通过以下 CSS 类进行自定义:
116 |
117 | - `.notion-block`: 所有 Notion 块的默认样式
118 | - `.notion-callout`: Callout 块的特定样式
119 | - `.notion-callout-content`: Callout 内容的样式
120 | - `.notion-callout-icon`: Callout 图标的样式
121 | - `.notion-callout-text`: Callout 文本的样式
122 |
123 | 如果需要额外的样式,可以对这些类进行编写 CSS。例如:
124 |
125 | ```css
126 | .notion-callout {
127 | border-radius: 4px;
128 | padding: 16px;
129 | display: flex;
130 | align-items: center;
131 | }
132 |
133 | .notion-callout-icon {
134 | margin-right: 12px;
135 | font-size: 24px;
136 | }
137 |
138 | .notion-callout-text {
139 | flex: 1;
140 | }
141 | ```
142 |
143 | ## 颜色处理
144 |
145 | 标注块的 `color` 属性决定了背景颜色。使用 `getColorCss` 工具函数生成合适的 CSS 类。这个函数将 Notion 的颜色名称转换为 CSS 类。
146 |
147 | ## 嵌套支持
148 |
149 | 标注块可以包含其他块。这通过 `children` prop 处理。例如,可以在标注块中包含列表或其他文本块。
150 |
151 | ## 注意事项
152 |
153 | - 标注块用于强调重要信息或警告。然而,过度使用可能会分散注意力。因此,应适当使用。
154 | - 图标和背景颜色的组合应满足可访问性标准。特别是文本和背景之间应有足够的对比度。
155 | - 对于屏幕阅读器用户,建议为图标提供适当的替代文本。
156 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/03. bulleted-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 4
4 | title: "箇条書きリストアイテムブロック"
5 | slug: "notion-block-bulleted-list-item"
6 | description: "ノーションプレッソの箇条書きリストアイテムブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # 箇条書きリストアイテムブロック
10 |
11 | 箇条書きリストアイテムブロックは、順序のないリストを作成するために使用されます。各項目は箇条書き記号(バレット)で始まります。
12 |
13 | ## データ構造
14 |
15 | 箇条書きリストアイテムブロックのノーションAPIデータ構造は以下の通りです:
16 |
17 | ```json
18 | {
19 | "type": "bulleted_list_item",
20 | "bulleted_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a bulleted list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a bulleted list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: リストアイテムのテキスト内容とスタイル情報を含む配列です。
46 | - `color`: テキストの色を指定します。
47 |
48 | ## Reactコンポーネント
49 |
50 | Notionpressoで箇条書きリストアイテムブロックをレンダリングするコンポーネントは以下の通りです:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { BulletedListItemArgs } from "../types";
55 | import { bulletedListItemMarker, getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type BulletedListItemProps = {
59 | children?: React.ReactNode;
60 | } & BulletedListItemArgs;
61 |
62 | const BulletedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | bulleted_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = bulletedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default BulletedListItem;
95 | ```
96 |
97 | ## 使用例
98 |
99 | 箇条書きリストアイテムブロックを使用する例は以下の通りです:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | ここで`blocks`は、Notion APIから受け取ったブロックデータの配列です。
114 |
115 | ## スタイリング
116 |
117 | 箇条書きリストアイテムブロックのスタイルは、以下のCSSクラスを使用してカスタマイズできます:
118 |
119 | - `.notion-block`: すべてのNotionブロックに適用されるデフォルトのスタイル
120 | - `.notion-list-bulleted`: 箇条書きリストアイテムブロックの特定のスタイル
121 | - `.notion-list-bulleted-content`: リストアイテムの内容のスタイル
122 | - `.notion-list-marker`: 箇条書き記号のスタイル
123 |
124 | 追加のスタイリングが必要な場合は、これらのクラスに対してCSSを記述するだけです。
125 |
126 | ## 入れ子のリストの処理
127 |
128 | 箇条書きリストアイテムは入れ子にすることができます。入れ子のリストは`children` propを使用して処理されます。レベルに応じて箇条書き記号の形状が変わる可能性があります。これは`bulletedListItemMarker.getMarker`関数で処理されます。
129 |
130 | ## 注意点
131 |
132 | - 箇条書きリストアイテムブロックは、連続して使用される場合、自動的に1つのリストにグループ化されます。
133 | - 入れ子のリストを使用する場合は、適切なインデントを使用して階層構造を明確にすることが推奨されます。
134 | - 箇条書き記号の形状は、Notionのデフォルトのスタイルに従いますが、CSSを使用してカスタマイズすることができます。
135 |
--------------------------------------------------------------------------------
/lib/mdx.ts:
--------------------------------------------------------------------------------
1 | // lib/mdx.ts
2 |
3 | import fs from "fs";
4 | import path from "path";
5 | import matter from "gray-matter";
6 | import { getGroupOrder, GroupId } from "@/constants/group";
7 |
8 | const contentDirectory = path.join(process.cwd(), "content", "guide");
9 |
10 | export interface Document {
11 | slug: string;
12 | title: string;
13 | group: string;
14 | content: string;
15 | }
16 |
17 | export interface DocumentData extends Document {
18 | prevDocument: Document | null;
19 | nextDocument: Document | null;
20 | }
21 |
22 | export function getAllDocuments(lang: string): Document[] {
23 | const langDir = path.join(contentDirectory, lang);
24 | const documents = getAllDocumentsRecursive(langDir, lang);
25 | return documents;
26 | }
27 |
28 | function getAllDocumentsRecursive(dir: string, lang: string): Document[] {
29 | if (!fs.existsSync(dir)) {
30 | return [];
31 | }
32 |
33 | const entries = fs.readdirSync(dir);
34 | let documents: Document[] = [];
35 |
36 | entries.forEach((entry) => {
37 | const entryPath = path.join(dir, entry);
38 | const stat = fs.statSync(entryPath);
39 |
40 | if (stat.isDirectory()) {
41 | documents = documents.concat(getAllDocumentsRecursive(entryPath, lang));
42 | } else if (entry.endsWith(".md")) {
43 | const fileContents = fs.readFileSync(entryPath, "utf8");
44 | const { data, content } = matter(fileContents);
45 | const groupOrder = getGroupOrder(data.group as GroupId);
46 |
47 | documents.push({
48 | slug: data.slug,
49 | title: data.title || "Untitled",
50 | group: data.group || "",
51 | content: content,
52 | });
53 | }
54 | });
55 |
56 | return documents;
57 | }
58 |
59 | export function getDocumentBySlug(
60 | lang: string,
61 | group: string,
62 | slug: string,
63 | allDocuments: Document[],
64 | ): DocumentData {
65 | const NOT_FOUND_DOCUMENT: DocumentData = {
66 | slug: "",
67 | content: "Document not found
",
68 | title: "Not Found",
69 | group: "",
70 | prevDocument: null,
71 | nextDocument: null,
72 | };
73 |
74 | if (!lang || !group || !slug) {
75 | return NOT_FOUND_DOCUMENT;
76 | }
77 |
78 | // Filter documents in the same group and sort them if necessary
79 | const documentsInGroup = allDocuments.filter((doc) => doc.group === group);
80 |
81 | const currentIndex = documentsInGroup.findIndex((doc) => doc.slug === slug);
82 |
83 | if (currentIndex === -1) {
84 | return NOT_FOUND_DOCUMENT;
85 | }
86 |
87 | const document = documentsInGroup[currentIndex];
88 |
89 | let prevDocument =
90 | currentIndex > 0 ? documentsInGroup[currentIndex - 1] : null;
91 | let nextDocument =
92 | currentIndex < documentsInGroup.length - 1
93 | ? documentsInGroup[currentIndex + 1]
94 | : null;
95 |
96 | const allGroups = Array.from(new Set(allDocuments.map((doc) => doc.group)));
97 | const currentGroupIndex = allGroups.indexOf(group);
98 |
99 | if (!prevDocument && currentGroupIndex > 0) {
100 | const prevGroup = allGroups[currentGroupIndex - 1];
101 | const prevGroupDocs = allDocuments.filter((doc) => doc.group === prevGroup);
102 | prevDocument = prevGroupDocs[prevGroupDocs.length - 1];
103 | }
104 |
105 | if (!nextDocument && currentGroupIndex < allGroups.length - 1) {
106 | const nextGroup = allGroups[currentGroupIndex + 1];
107 | const nextGroupDocs = allDocuments.filter((doc) => doc.group === nextGroup);
108 | nextDocument = nextGroupDocs[0];
109 | }
110 |
111 | return {
112 | ...document,
113 | prevDocument,
114 | nextDocument,
115 | };
116 | }
117 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/03. bulleted-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 4
4 | title: "Bulleted List Item 블록"
5 | slug: "notion-block-bulleted-list-item"
6 | description: "Notionpresso의 Bulleted List Item 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Bulleted List Item 블록
10 |
11 | Bulleted List Item 블록은 순서가 없는 목록을 만드는 데 사용됩니다. 각 항목은 글머리 기호(bullet)로 시작합니다.
12 |
13 | ## 데이터 구조
14 |
15 | Bulleted List Item 블록의 Notion API 데이터 구조는 다음과 같습니다:
16 |
17 | ```json
18 | {
19 | "type": "bulleted_list_item",
20 | "bulleted_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a bulleted list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a bulleted list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 리스트 항목의 텍스트 내용과 스타일 정보를 담고 있는 배열입니다.
46 | - `color`: 텍스트의 색상을 지정합니다.
47 |
48 | ## React 컴포넌트
49 |
50 | Notionpresso에서 Bulleted List Item 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { BulletedListItemArgs } from "../types";
55 | import { bulletedListItemMarker, getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type BulletedListItemProps = {
59 | children?: React.ReactNode;
60 | } & BulletedListItemArgs;
61 |
62 | const BulletedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | bulleted_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = bulletedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default BulletedListItem;
95 | ```
96 |
97 | ## 사용 예시
98 |
99 | Bulleted List Item 블록을 사용하는 예시는 다음과 같습니다:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
114 |
115 | ## 스타일링
116 |
117 | Bulleted List Item 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
118 |
119 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
120 | - `.notion-list-bulleted`: Bulleted List Item 블록 특정 스타일
121 | - `.notion-list-bulleted-content`: 리스트 항목 내용의 스타일
122 | - `.notion-list-marker`: 글머리 기호의 스타일
123 |
124 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다.
125 |
126 | ## 중첩된 리스트 처리
127 |
128 | Bulleted List Item은 중첩될 수 있습니다. 중첩된 리스트는 `children` prop을 통해 처리됩니다. 중첩 레벨에 따라 글머리 기호의 모양이 달라질 수 있으며, 이는 `bulletedListItemMarker.getMarker` 함수에서 처리됩니다.
129 |
130 | ## 주의사항
131 |
132 | - Bulleted List Item 블록은 연속적으로 사용될 때 자동으로 하나의 리스트로 그룹화됩니다.
133 | - 중첩된 리스트를 사용할 때는 적절한 들여쓰기를 통해 계층 구조를 명확히 표현하는 것이 좋습니다.
134 | - 글머리 기호의 모양은 Notion의 기본 스타일을 따르지만, CSS를 통해 커스터마이즈할 수 있습니다.
135 |
--------------------------------------------------------------------------------
/components/docs/mobile-sidebar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/solid";
4 | import Link from "next/link";
5 | import { useState } from "react";
6 | import ThemeSelector from "../ui/theme-selector";
7 |
8 | type Document = {
9 | title: string;
10 | slug: string;
11 | group: string;
12 | order: number;
13 | };
14 |
15 | type MobileSidebarProps = {
16 | documents: Document[];
17 | currentSlug: string;
18 | currentGroup: string;
19 | lang: string;
20 | };
21 |
22 | export default function MobileSidebar({
23 | documents,
24 | currentSlug,
25 | currentGroup,
26 | lang,
27 | }: MobileSidebarProps) {
28 | const [isOpen, setIsOpen] = useState(false);
29 | const [openGroup, setOpenGroup] = useState(null);
30 |
31 | const groupedDocuments = documents.reduce(
32 | (acc, doc) => {
33 | if (!acc[doc.group]) {
34 | acc[doc.group] = [];
35 | }
36 | acc[doc.group].push(doc);
37 | return acc;
38 | },
39 | {} as Record,
40 | );
41 |
42 | const toggleDropdown = () => setIsOpen(!isOpen);
43 | const toggleGroup = (group: string) => {
44 | setOpenGroup(openGroup === group ? null : group);
45 | };
46 |
47 | return (
48 |
49 |
53 | Menu
54 |
59 |
60 | {isOpen && (
61 |
62 | {Object.entries(groupedDocuments).map(([group, docs]) => (
63 |
64 |
toggleGroup(group)}
66 | className="flex items-center justify-between w-full text-left font-bold mb-2"
67 | >
68 | {group}
69 |
74 |
75 | {openGroup === group && (
76 |
77 | {docs.map((doc) => (
78 |
79 |
87 | {doc.title}
88 |
89 |
90 |
91 | ))}
92 |
93 | )}
94 |
95 | ))}
96 |
97 |
98 |
99 |
100 | )}
101 |
102 | );
103 | }
104 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/05. numbered-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 5
4 | title: "番号付きリストアイテムブロック"
5 | slug: "numbered-list-item"
6 | description: "ノーションプレッソの番号付きリストアイテムブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # 番号付きリストアイテムブロック
10 |
11 | 番号付きリストアイテムブロックは、順序のあるリストを作成するために使用されます。各項目は連番で始まります。
12 |
13 | ## データ構造
14 |
15 | 番号付きリストアイテムブロックのノーションAPIデータ構造は以下の通りです:
16 |
17 | ```json
18 | {
19 | "type": "numbered_list_item",
20 | "numbered_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a numbered list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a numbered list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: リストアイテムのテキスト内容とスタイル情報を含む配列です。
46 | - `color`: テキストの色を指定します。
47 |
48 | ## Reactコンポーネント
49 |
50 | Notionpressoで番号付きリストアイテムブロックをレンダリングするコンポーネントは以下の通りです:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { NumberedListItemArgs } from "../types";
55 | import { getColorCss, numberedListItemMarker } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type NumberedListProps = {
59 | children?: React.ReactNode;
60 | } & NumberedListItemArgs;
61 |
62 | const NumberedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | numbered_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = numberedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default NumberedListItem;
95 | ```
96 |
97 | ## 使用例
98 |
99 | 番号付きリストアイテムブロックを使用する例は以下の通りです:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | ここで`blocks`は、NotionAPIから受け取ったブロックデータの配列です。
114 |
115 | ## スタイリング
116 |
117 | 番号付きリストアイテムブロックのスタイルは、以下のCSSクラスを使用してカスタマイズできます:
118 |
119 | - `.notion-block`: すべてのNotionブロックに適用される基本スタイル
120 | - `.notion-list-numbered`: 番号付きリストアイテムブロックの特定のスタイル
121 | - `.notion-list-numbered-content`: リストアイテムの内容のスタイル
122 | - `.notion-list-marker`: 番号のスタイル
123 |
124 | 追加のスタイリングが必要な場合は、これらのクラスを対象にCSSを記述することができます。
125 |
126 | ## 番号付けの方法
127 |
128 | 番号付きリストアイテムの番号付けの方法は、`numberedListItemMarker.getMarker`関数によって決定されます。この関数は、現在のアイテムの位置とネストレベルを考慮して、適切な番号または文字を返します。デフォルトでは、次の順序に従います:
129 |
130 | 1. 最上位レベル: 1, 2, 3, ...
131 | 2. 2番目のレベル: a, b, c, ...
132 | 3. 3番目のレベル: i, ii, iii, ...
133 |
134 | この順序は、必要に応じてカスタマイズできます。
135 |
136 | ## ネストされたリストの処理
137 |
138 | 番号付きリストアイテムはネストすることもできます。ネストされたリストは`children`propを使用して処理されます。ネストレベルに応じて、番号付けの方法が変わる可能性があります。これは`numberedListItemMarker.getMarker`関数によって処理されます。
139 |
140 | ## 注意点
141 |
142 | - 番号付きリストアイテムブロックは、連続して使用される場合、自動的に1つのリストにグループ化されます。
143 | - ネストされたリストを使用する場合は、適切なインデントを使用して階層構造を明確にすることが推奨されます。
144 | - 番号付けの方法は、Notionのデフォルトスタイルに従いますが、CSSを使用してカスタマイズすることも可能です。
145 | - リストが中断された後に再開される場合、番号付けが新しく開始される可能性があるため、注意が必要です。
146 |
--------------------------------------------------------------------------------
/content/guide/cn/02. customization-guide/03. custom-style.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "customization-guide"
3 | order: 3
4 | title: "CSS 结构和样式指南"
5 | slug: "css-structure-and-styling"
6 | description: "了解 Notionpresso 的 CSS 结构并学习有效的样式设计方法。"
7 | ---
8 |
9 | # CSS 结构和样式指南
10 |
11 | Notionpresso 提供了灵活且可扩展的 CSS 结构。本节将介绍 CSS 变量系统、主要样式设计要点以及缩进应用原理。
12 |
13 | ## CSS 变量系统
14 |
15 | Notionpresso 使用 CSS 变量定义全局样式。这使得主题更改和整体样式调整变得容易。
16 |
17 | 主要的 CSS 变量如下:
18 |
19 | ```css
20 | .notion {
21 | --notion-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
22 | "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif,
23 | "Segoe UI Emoji", "Segoe UI Symbol";
24 | --notion-max-width: 720px;
25 | --notion-header-height: 45px;
26 | --notion-indent: 27px;
27 |
28 | /* 颜色变量 */
29 | --fg-color: rgb(55, 53, 47);
30 | --fg-color-0: rgba(55, 53, 47, 0.09);
31 | --fg-color-1: rgba(55, 53, 47, 0.16);
32 | --fg-color-2: rgba(55, 53, 47, 0.4);
33 | --fg-color-3: rgba(55, 53, 47, 0.6);
34 | --fg-color-4: #000;
35 | --fg-color-5: rgb(233, 233, 231);
36 | --fg-color-6: rgba(55, 53, 47, 0.8);
37 | --fg-color-icon: var(--fg-color);
38 |
39 | --bg-color: #fff;
40 | --bg-color-0: rgba(135, 131, 120, 0.15);
41 | --bg-color-1: rgb(247, 246, 243);
42 | --bg-color-2: rgba(135, 131, 120, 0.15);
43 |
44 | --select-color-0: rgb(46, 170, 220);
45 | --select-color-1: rgba(35, 131, 226, 0.28);
46 | --select-color-2: #d9eff8;
47 | }
48 | ```
49 |
50 | 通过重新定义这些变量,可以轻松更改整体样式。
51 |
52 | ## 主要样式设计要点
53 |
54 | 1. **类命名规则**: 所有类都以 `notion-` 开头。这可以防止样式冲突。
55 |
56 | 例如: `.notion-block`, `.notion-h1`, `.notion-toggle`
57 |
58 | 2. **块级样式**: 所有块级元素都应用 `notion-block` 类。
59 |
60 | ```css
61 | .notion-block {
62 | display: block;
63 | }
64 | ```
65 |
66 | 3. **颜色应用**: 颜色通过 CSS 变量应用。
67 |
68 | ```css
69 | .notion-text {
70 | color: var(--fg-color);
71 | }
72 | ```
73 |
74 | 4. **响应式设计**: 使用 `--notion-max-width` 变量实现响应式设计。
75 |
76 | ```css
77 | .notion-body {
78 | width: 100%;
79 | max-width: var(--notion-max-width);
80 | margin: 0 auto;
81 | }
82 | ```
83 |
84 | ## 缩进应用原理
85 |
86 | Notionpresso 使用递归结构实现缩进。这通过 CSS 处理如下:
87 |
88 | ```css
89 | .notion-block > .notion-block {
90 | margin-left: var(--notion-indent);
91 | }
92 |
93 | .notion-block > .notion-display-contents > .notion-block {
94 | margin-left: var(--notion-indent);
95 | }
96 | ```
97 |
98 | 这些 CSS 规则自动应用于嵌套块。可以通过调整 `--notion-indent` 变量轻松更改缩进程度。
99 |
100 | ## 暗模式和自定义主题
101 |
102 | Notionpresso 默认支持暗模式。暗模式通过以下方式实现:
103 |
104 | ```css
105 | [data-theme="dark"] .notion {
106 | --fg-color: rgba(255, 255, 255, 0.9);
107 | --bg-color: #2f3437;
108 | /* 其他暗模式相关变量... */
109 | }
110 | ```
111 |
112 | 此外,可以轻松应用自定义主题。使用 `data-theme` 属性应用所需的主题:
113 |
114 | ```jsx
115 | {/* Notion 内容 */}
116 | ```
117 |
118 | 并且可以在 CSS 中定义样式如下:
119 |
120 | ```css
121 | .notion[data-theme="custom"] {
122 | --fg-color: #333;
123 | --bg-color: #f4f4f4;
124 | /* 其他自定义主题相关变量... */
125 | }
126 | ```
127 |
128 | 使用这种方法,可以通过选择器优先级轻松覆盖默认样式。
129 |
130 | ## 样式定制示例
131 |
132 | 1. **全局字体更改**:
133 |
134 | ```css
135 | .notion[data-theme="custom"] {
136 | --notion-font: "Roboto", sans-serif;
137 | }
138 | ```
139 |
140 | 2. **标题样式更改**:
141 |
142 | ```css
143 | .notion[data-theme="custom"] .notion-h1 {
144 | font-size: 2.5em;
145 | color: var(--select-color-0);
146 | border-bottom: 2px solid var(--fg-color-1);
147 | }
148 | ```
149 |
150 | 3. **缩进调整**:
151 |
152 | ```css
153 | .notion[data-theme="custom"] {
154 | --notion-indent: 20px;
155 | }
156 | ```
157 |
158 | 4. **暗模式实现**:
159 |
160 | ```css
161 | [data-theme="dark"] .notion[data-theme="custom"] {
162 | --fg-color: rgba(255, 255, 255, 0.9);
163 | --bg-color: #2f3437;
164 | /* 其他颜色变量也应适当调整 */
165 | }
166 | ```
167 |
168 | 通过遵循这些 CSS 结构和样式指南,可以使用 Notionpresso 实现一致且易于维护的样式。此外,可以根据需要进行更详细的定制。
169 |
--------------------------------------------------------------------------------
/content/guide/en/03. block-types/02. heading.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 3
4 | title: "Heading Block"
5 | slug: "notion-block-heading"
6 | description: "Detailed explanation of the Heading block type in Notionpresso."
7 | ---
8 |
9 | # Heading Block
10 |
11 | Heading blocks are used to represent the structure of a document and support three levels (Heading 1, Heading 2, Heading 3).
12 |
13 | ## Data Structure
14 |
15 | The Notion API data structure for a Heading block is as follows (example for Heading 1):
16 |
17 | ```json
18 | {
19 | "type": "heading_1",
20 | "heading_1": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a heading",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a heading",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "is_toggleable": false
42 | }
43 | }
44 | ```
45 |
46 | - `type`: Can be "heading_1", "heading_2", or "heading_3".
47 | - `rich_text`: An array containing the content and style information of the heading text.
48 | - `color`: Specifies the color of the heading.
49 | - `is_toggleable`: Indicates whether the toggle feature is used.
50 |
51 | ## React Component
52 |
53 | The component that renders the Heading block in Notionpresso is as follows:
54 |
55 | ```jsx
56 | import React, { useMemo } from "react";
57 | import type { HeadingsProps, HeadingConfig } from "../types";
58 | import { getColorCss } from "../utils";
59 | import RichText from "./internal/rich-text";
60 |
61 | const Headings: React.FC = ({ children, type, ...props }) => {
62 | const {
63 | [type]: { color, rich_text: texts, is_toggleable },
64 | } = props;
65 |
66 | const { headingTag: HeadingTag, headingClassName } =
67 | useMemo(() => {
68 | switch (type) {
69 | case "heading_2":
70 | return { headingTag: "h2", headingClassName: "notion-h2" };
71 | case "heading_3":
72 | return { headingTag: "h3", headingClassName: "notion-h3" };
73 | default:
74 | return { headingTag: "h1", headingClassName: "notion-h1" };
75 | }
76 | }, [type]);
77 |
78 | return (
79 |
82 |
83 |
84 |
85 | {children}
86 |
87 | );
88 | };
89 |
90 | export default Headings;
91 | ```
92 |
93 | ## Usage Example
94 |
95 | Here's an example of how to use the Heading block:
96 |
97 | ```jsx
98 | import { Notion } from "@notionpresso/react";
99 |
100 | function MyNotionPage({ blocks }) {
101 | return (
102 |
103 |
104 |
105 | );
106 | }
107 | ```
108 |
109 | Here, `blocks` is an array of block data received from the Notion API.
110 |
111 | ## Styling
112 |
113 | The style of the Heading block can be customized through the following CSS classes:
114 |
115 | - `.notion-block`: Basic style applied to all Notion blocks
116 | - `.notion-h1`, `.notion-h2`, `.notion-h3`: Specific styles for each heading level
117 | - `.notion-h1-content`, `.notion-h2-content`, `.notion-h3-content`: Styles for the content of each heading
118 |
119 | If additional styling is needed, you can write CSS targeting these classes.
120 |
121 | ## Notes
122 |
123 | - As Heading blocks are important elements representing the structure of a document, it's recommended to use appropriate heading levels.
124 | - If the `is_toggleable` property is true, the heading will have a toggle functionality. In this case, additional logic may be required.
125 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/08. callout.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 7
4 | title: "コールアウトブロック"
5 | slug: "callout"
6 | description: "ノーションプレッソのコールアウトブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # コールアウトブロック
10 |
11 | コールアウトブロックは、注目を集める必要のある内容を強調するために使用されます。主にアイコンと共に色付きの背景で表示され、視覚的に目立つようになっています。
12 |
13 | ## データ構造
14 |
15 | コールアウトブロックのノーションAPIデータ構造は以下の通りです:
16 |
17 | ```json
18 | {
19 | "type": "callout",
20 | "callout": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a callout",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a callout",
37 | "href": null
38 | }
39 | ],
40 | "icon": {
41 | "type": "emoji",
42 | "emoji": "💡"
43 | },
44 | "color": "gray_background"
45 | }
46 | }
47 | ```
48 |
49 | - `rich_text`: Calloutのテキスト内容とスタイル情報を含む配列です。
50 | - `icon`: Calloutと共に表示されるアイコン情報です。これは絵文字か外部画像URLのいずれかになります。
51 | - `color`: Calloutの背景色を指定します。
52 |
53 | ## Reactコンポーネント
54 |
55 | Notionpressoでコールアウトブロックをレンダリングするコンポーネントは以下の通りです:
56 |
57 | ```jsx
58 | import React from "react";
59 | import type { CalloutArgs } from "../types";
60 | import { getColorCss } from "../utils";
61 | import RichText from "./internal/rich-text";
62 | import Icon from "./internal/icon";
63 |
64 | type CalloutProps = {
65 | children?: React.ReactNode;
66 | } & CalloutArgs;
67 |
68 | const Callout: React.FC = ({ children, ...props }) => {
69 | const { callout } = props;
70 |
71 | return (
72 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | {children}
84 |
85 | );
86 | };
87 |
88 | export default Callout;
89 | ```
90 |
91 | ## 使用例
92 |
93 | コールアウトブロックを使用する例は以下の通りです:
94 |
95 | ```jsx
96 | import { Notion } from "@notionpresso/react";
97 |
98 | function MyNotionPage({ blocks }) {
99 | return (
100 |
101 |
102 |
103 | );
104 | }
105 | ```
106 |
107 | ここで`blocks`は、Notion APIから受け取ったブロックデータの配列です。
108 |
109 | ## アイコンの処理
110 |
111 | コールアウトブロックのアイコンは、`Icon`コンポーネントを使用して処理されます。このコンポーネントは、絵文字と外部画像URLの両方をサポートしています。アイコンのタイプに応じて、適切なレンダリング方法が選択されます。
112 |
113 | ## スタイリング
114 |
115 | コールアウトブロックのスタイルは、以下のCSSクラスを使用してカスタマイズできます:
116 |
117 | - `.notion-block`: すべてのNotionブロックに適用されるデフォルトのスタイル
118 | - `.notion-callout`: コールアウトブロックの特定のスタイル
119 | - `.notion-callout-content`: コールアウトの内容のスタイル
120 | - `.notion-callout-icon`: コールアウトのアイコンのスタイル
121 | - `.notion-callout-text`: コールアウトのテキストのスタイル
122 |
123 | 追加のスタイリングが必要な場合は、これらのクラスを対象にCSSを記述するだけです。例えば:
124 |
125 | ```css
126 | .notion-callout {
127 | border-radius: 4px;
128 | padding: 16px;
129 | display: flex;
130 | align-items: center;
131 | }
132 |
133 | .notion-callout-icon {
134 | margin-right: 12px;
135 | font-size: 24px;
136 | }
137 |
138 | .notion-callout-text {
139 | flex: 1;
140 | }
141 | ```
142 |
143 | ## カラーの処理
144 |
145 | コールアウトの`color`属性は、背景色を決定します。`getColorCss`ユーティリティ関数を使用して、適切なCSSクラスを生成します。この関数は、Notionのカラー名をCSSクラスに変換します。
146 |
147 | ## ネストのサポート
148 |
149 | コールアウトブロックは、他のブロックを含むことができます。これは、`children`プロップを使用して処理されます。例えば、コールアウトの中にリストや他のテキストブロックを含むことができます。
150 |
151 | ## 注意点
152 |
153 | - コールアウトブロックは、重要な情報や警告を強調するために効果的です。ただし、過度の使用は、注意を分散させる可能性があるため、適切に使用する必要があります。
154 | - アイコンと背景色の組み合わせが、アクセシビリティ基準を満たしているかどうかを確認する必要があります。特に、テキストと背景の間に十分なコントラストがあることを確認する必要があります。
155 | - スクリーンリーダーを使用するユーザーのために、アイコンに適切な代替テキストを提供することが推奨されます。
156 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/08. Callout.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 7
4 | title: "Callout 블록"
5 | slug: "callout"
6 | description: "Notionpresso의 Callout 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Callout 블록
10 |
11 | Callout 블록은 주목을 끌어야 하는 내용을 강조하는 데 사용됩니다. 주로 아이콘과 함께 색상이 있는 배경으로 표시되어 시각적으로 눈에 띄게 만듭니다.
12 |
13 | ## 데이터 구조
14 |
15 | Callout 블록의 Notion API 데이터 구조는 다음과 같습니다:
16 |
17 | ```json
18 | {
19 | "type": "callout",
20 | "callout": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a callout",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a callout",
37 | "href": null
38 | }
39 | ],
40 | "icon": {
41 | "type": "emoji",
42 | "emoji": "💡"
43 | },
44 | "color": "gray_background"
45 | }
46 | }
47 | ```
48 |
49 | - `rich_text`: Callout의 텍스트 내용과 스타일 정보를 담고 있는 배열입니다.
50 | - `icon`: Callout과 함께 표시될 아이콘 정보입니다. 이모지나 외부 이미지 URL이 될 수 있습니다.
51 | - `color`: Callout의 배경색을 지정합니다.
52 |
53 | ## React 컴포넌트
54 |
55 | Notionpresso에서 Callout 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
56 |
57 | ```jsx
58 | import React from "react";
59 | import type { CalloutArgs } from "../types";
60 | import { getColorCss } from "../utils";
61 | import RichText from "./internal/rich-text";
62 | import Icon from "./internal/icon";
63 |
64 | type CalloutProps = {
65 | children?: React.ReactNode;
66 | } & CalloutArgs;
67 |
68 | const Callout: React.FC = ({ children, ...props }) => {
69 | const { callout } = props;
70 |
71 | return (
72 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | {children}
84 |
85 | );
86 | };
87 |
88 | export default Callout;
89 | ```
90 |
91 | ## 사용 예시
92 |
93 | Callout 블록을 사용하는 예시는 다음과 같습니다:
94 |
95 | ```jsx
96 | import { Notion } from "@notionpresso/react";
97 |
98 | function MyNotionPage({ blocks }) {
99 | return (
100 |
101 |
102 |
103 | );
104 | }
105 | ```
106 |
107 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
108 |
109 | ## 아이콘 처리
110 |
111 | Callout 블록의 아이콘은 `Icon` 컴포넌트를 통해 처리됩니다. 이 컴포넌트는 이모지와 외부 이미지 URL을 모두 지원합니다. 아이콘 타입에 따라 적절한 렌더링 방식을 선택합니다.
112 |
113 | ## 스타일링
114 |
115 | Callout 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
116 |
117 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
118 | - `.notion-callout`: Callout 블록 특정 스타일
119 | - `.notion-callout-content`: Callout 내용의 스타일
120 | - `.notion-callout-icon`: Callout 아이콘의 스타일
121 | - `.notion-callout-text`: Callout 텍스트의 스타일
122 |
123 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다. 예를 들어:
124 |
125 | ```css
126 | .notion-callout {
127 | border-radius: 4px;
128 | padding: 16px;
129 | display: flex;
130 | align-items: center;
131 | }
132 |
133 | .notion-callout-icon {
134 | margin-right: 12px;
135 | font-size: 24px;
136 | }
137 |
138 | .notion-callout-text {
139 | flex: 1;
140 | }
141 | ```
142 |
143 | ## 색상 처리
144 |
145 | Callout의 `color` 속성은 배경색을 결정합니다. `getColorCss` 유틸리티 함수를 사용하여 적절한 CSS 클래스를 생성합니다. 이 함수는 Notion의 색상 이름을 CSS 클래스로 변환합니다.
146 |
147 | ## 중첩 지원
148 |
149 | Callout 블록은 다른 블록을 포함할 수 있습니다. 이는 `children` prop을 통해 처리됩니다. 예를 들어, Callout 안에 리스트나 다른 텍스트 블록을 포함할 수 있습니다.
150 |
151 | ## 주의사항
152 |
153 | - Callout 블록은 주요 정보나 경고를 강조하는 데 효과적입니다. 그러나 과도한 사용은 오히려 주의를 분산시킬 수 있으므로 적절히 사용해야 합니다.
154 | - 아이콘과 배경색의 조합이 접근성 기준을 충족하는지 확인해야 합니다. 특히 텍스트와 배경 간의 충분한 대비를 유지해야 합니다.
155 | - 스크린 리더 사용자를 위해 아이콘에 적절한 대체 텍스트를 제공하는 것이 좋습니다.
156 |
--------------------------------------------------------------------------------
/content/guide/en/03. block-types/06. Quote.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 6
4 | title: "Quote Block"
5 | slug: "quote"
6 | description: "Detailed explanation of the Quote block type in Notionpresso."
7 | ---
8 |
9 | # Quote Block
10 |
11 | The Quote block is used to display text quoted from another source. It is typically represented in a visually distinct style.
12 |
13 | ## Data Structure
14 |
15 | The Notion API data structure for a Quote block is as follows:
16 |
17 | ```json
18 | {
19 | "type": "quote",
20 | "quote": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a quote",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a quote",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: An array containing the text content and style information of the quote.
46 | - `color`: Specifies the color of the quote.
47 |
48 | ## React Component
49 |
50 | The component that renders the Quote block in Notionpresso is as follows:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { QuoteArgs } from "../types";
55 | import { getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type QuoteProps = {
59 | children?: React.ReactNode;
60 | } & QuoteArgs;
61 |
62 | const Quote: React.FC = ({ children, ...props }) => {
63 | const {
64 | quote: { color, rich_text: texts },
65 | } = props;
66 |
67 | return (
68 |
69 |
70 |
71 |
72 |
73 | {children}
74 |
75 |
76 | );
77 | };
78 |
79 | export default Quote;
80 | ```
81 |
82 | ## Usage Example
83 |
84 | Here's an example of how to use the Quote block:
85 |
86 | ```jsx
87 | import { Notion } from "@notionpresso/react";
88 |
89 | function MyNotionPage({ blocks }) {
90 | return (
91 |
92 |
93 |
94 | );
95 | }
96 | ```
97 |
98 | Here, `blocks` is an array of block data received from the Notion API.
99 |
100 | ## Styling
101 |
102 | The style of the Quote block can be customized through the following CSS classes:
103 |
104 | - `.notion-block`: Basic style applied to all Notion blocks
105 | - `.notion-quote`: Specific style for Quote blocks
106 | - `.notion-quote-content`: Style for the content of the quote
107 |
108 | If additional styling is needed, you can write CSS targeting these classes. Typically, Quote blocks have the following style characteristics:
109 |
110 | - Thick border on the left or both sides
111 | - Slight indentation
112 | - Changed background color
113 | - Changed font style (e.g., italics)
114 |
115 | For example, you can apply the following CSS:
116 |
117 | ```css
118 | .notion-quote {
119 | border-left: 4px solid #ccc;
120 | padding-left: 16px;
121 | margin-left: 0;
122 | margin-right: 0;
123 | font-style: italic;
124 | background-color: #f9f9f9;
125 | }
126 | ```
127 |
128 | ## Nesting Support
129 |
130 | Quote blocks can contain other blocks. This is handled through the `children` prop. For example, you can include lists or other text blocks within a quote.
131 |
132 | ## Notes
133 |
134 | - Quote blocks are generally used for short quotations. For longer quotes, it might be better to split them into multiple Quote blocks for readability.
135 | - It's good practice to specify the source of the quote. This can be added as a separate Paragraph block below the Quote block.
136 | - For accessibility, it's recommended to add appropriate ARIA attributes for screen reader users. For example, you can use the `role="blockquote"` attribute.
137 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/05. numbered-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 5
4 | title: "Numbered List Item 블록"
5 | slug: "numbered-list-item"
6 | description: "Notionpresso의 Numbered List Item 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Numbered List Item 블록
10 |
11 | Numbered List Item 블록은 순서가 있는 목록을 만드는 데 사용됩니다. 각 항목은 순차적인 번호로 시작합니다.
12 |
13 | ## 데이터 구조
14 |
15 | Numbered List Item 블록의 Notion API 데이터 구조는 다음과 같습니다:
16 |
17 | ```json
18 | {
19 | "type": "numbered_list_item",
20 | "numbered_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a numbered list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a numbered list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: 리스트 항목의 텍스트 내용과 스타일 정보를 담고 있는 배열입니다.
46 | - `color`: 텍스트의 색상을 지정합니다.
47 |
48 | ## React 컴포넌트
49 |
50 | Notionpresso에서 Numbered List Item 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { NumberedListItemArgs } from "../types";
55 | import { getColorCss, numberedListItemMarker } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type NumberedListProps = {
59 | children?: React.ReactNode;
60 | } & NumberedListItemArgs;
61 |
62 | const NumberedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | numbered_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = numberedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default NumberedListItem;
95 | ```
96 |
97 | ## 사용 예시
98 |
99 | Numbered List Item 블록을 사용하는 예시는 다음과 같습니다:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
114 |
115 | ## 스타일링
116 |
117 | Numbered List Item 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
118 |
119 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
120 | - `.notion-list-numbered`: Numbered List Item 블록 특정 스타일
121 | - `.notion-list-numbered-content`: 리스트 항목 내용의 스타일
122 | - `.notion-list-marker`: 번호의 스타일
123 |
124 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다.
125 |
126 | ## 번호 매기기 방식
127 |
128 | Numbered List Item의 번호 매기기 방식은 `numberedListItemMarker.getMarker` 함수에 의해 결정됩니다. 이 함수는 현재 항목의 위치와 중첩 레벨을 고려하여 적절한 번호나 문자를 반환합니다. 기본적으로 다음과 같은 순서를 따릅니다:
129 |
130 | 1. 최상위 레벨: 1, 2, 3, ...
131 | 2. 두 번째 레벨: a, b, c, ...
132 | 3. 세 번째 레벨: i, ii, iii, ...
133 |
134 | 이 순서는 필요에 따라 커스터마이즈할 수 있습니다.
135 |
136 | ## 중첩된 리스트 처리
137 |
138 | Numbered List Item도 중첩될 수 있습니다. 중첩된 리스트는 `children` prop을 통해 처리됩니다. 중첩 레벨에 따라 번호 매기기 방식이 달라질 수 있으며, 이는 `numberedListItemMarker.getMarker` 함수에서 처리됩니다.
139 |
140 | ## 주의사항
141 |
142 | - Numbered List Item 블록은 연속적으로 사용될 때 자동으로 하나의 리스트로 그룹화됩니다.
143 | - 중첩된 리스트를 사용할 때는 적절한 들여쓰기를 통해 계층 구조를 명확히 표현하는 것이 좋습니다.
144 | - 번호 매기기 방식은 Notion의 기본 스타일을 따르지만, CSS를 통해 커스터마이즈할 수 있습니다.
145 | - 리스트가 중단되었다가 다시 시작될 경우, 번호 매기기가 새로 시작될 수 있으므로 주의가 필요합니다.
146 |
--------------------------------------------------------------------------------
/content/guide/ja/02. customization-guide/03. custom-style.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "customization-guide"
3 | order: 3
4 | title: "CSS構造とスタイリングガイド"
5 | slug: "css-structure-and-styling"
6 | description: "ノーションプレッソのCSS構造を理解し、効果的なスタイリング方法を学習します。"
7 | ---
8 |
9 | # CSS構造とスタイリングガイド
10 |
11 | ノーションプレッソは、柔軟で拡張可能なCSS構造を提供します。このセクションでは、CSS変数システム、主要なスタイリングポイント、そしてインデントの適用原理について説明します。
12 |
13 | ## CSS変数システム
14 |
15 | ノーションプレッソはCSS変数を使用してグローバルスタイルを定義します。これにより、テーマの変更や全体的なスタイルの調整を簡単に行うことができます。
16 |
17 | 主要なCSS変数は以下の通りです:
18 |
19 | ```css
20 | .notion {
21 | --notion-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
22 | "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif,
23 | "Segoe UI Emoji", "Segoe UI Symbol";
24 | --notion-max-width: 720px;
25 | --notion-header-height: 45px;
26 | --notion-indent: 27px;
27 |
28 | /* 色変数 */
29 | --fg-color: rgb(55, 53, 47);
30 | --fg-color-0: rgba(55, 53, 47, 0.09);
31 | --fg-color-1: rgba(55, 53, 47, 0.16);
32 | --fg-color-2: rgba(55, 53, 47, 0.4);
33 | --fg-color-3: rgba(55, 53, 47, 0.6);
34 | --fg-color-4: #000;
35 | --fg-color-5: rgb(233, 233, 231);
36 | --fg-color-6: rgba(55, 53, 47, 0.8);
37 | --fg-color-icon: var(--fg-color);
38 |
39 | --bg-color: #fff;
40 | --bg-color-0: rgba(135, 131, 120, 0.15);
41 | --bg-color-1: rgb(247, 246, 243);
42 | --bg-color-2: rgba(135, 131, 120, 0.15);
43 |
44 | --select-color-0: rgb(46, 170, 220);
45 | --select-color-1: rgba(35, 131, 226, 0.28);
46 | --select-color-2: #d9eff8;
47 | }
48 | ```
49 |
50 | これらの変数を再定義することで、全体的なスタイルを簡単に変更できます。
51 |
52 | ## 主要なスタイリングポイント
53 |
54 | 1. **クラス命名規則**: すべてのクラスは`notion-`で始まります。これはスタイルの衝突を防ぎます。
55 |
56 | 例: `.notion-block`, `.notion-h1`, `.notion-toggle`
57 |
58 | 2. **ブロックレベルスタイル**: すべてのブロックレベル要素には`notion-block`クラスが適用されます。
59 |
60 | ```css
61 | .notion-block {
62 | display: block;
63 | }
64 | ```
65 |
66 | 3. **色の適用**: 色はCSS変数を使用して適用されます。
67 |
68 | ```css
69 | .notion-text {
70 | color: var(--fg-color);
71 | }
72 | ```
73 |
74 | 4. **レスポンシブデザイン**: `--notion-max-width`変数を使用してレスポンシブレイアウトを実装します。
75 |
76 | ```css
77 | .notion-body {
78 | width: 100%;
79 | max-width: var(--notion-max-width);
80 | margin: 0 auto;
81 | }
82 | ```
83 |
84 | ## インデントの適用原理
85 |
86 | ノーションプレッソは再帰的な構造を使用してインデントを実装します。これはCSSで以下のように処理されます:
87 |
88 | ```css
89 | .notion-block > .notion-block {
90 | margin-left: var(--notion-indent);
91 | }
92 |
93 | .notion-block > .notion-display-contents > .notion-block {
94 | margin-left: var(--notion-indent);
95 | }
96 | ```
97 |
98 | このCSSルールは、入れ子のブロックに対して自動的にインデントを適用します。`--notion-indent`変数を調整することで、インデントのレベルを簡単に変更できます。
99 |
100 | ## ダークモードとカスタムテーマ
101 |
102 | ノーションプレッソはデフォルトでダークモードをサポートしています。ダークモードは以下のように実装されています:
103 |
104 | ```css
105 | [data-theme="dark"] .notion {
106 | --fg-color: rgba(255, 255, 255, 0.9);
107 | --bg-color: #2f3437;
108 | /* 他のダークモード関連変数... */
109 | }
110 | ```
111 |
112 | さらに、カスタムテーマを簡単に適用できます。`data-theme`属性を使用して、希望するテーマを適用できます:
113 |
114 | ```jsx
115 | {/* Notionコンテンツ*/}
116 | ```
117 |
118 | そして、CSSで以下のようにスタイルを定義できます:
119 |
120 | ```css
121 | .notion[data-theme="custom"] {
122 | --fg-color: #333;
123 | --bg-color: #f4f4f4;
124 | /* 他のカスタムテーマ関連変数... */
125 | }
126 | ```
127 |
128 | この方法を使用すると、セレクタの優先順位を利用して、デフォルトのスタイルを簡単にオーバーライドできます。
129 |
130 | ## スタイルのカスタマイズの例
131 |
132 | 1. **グローバルフォントの変更**:
133 |
134 | ```css
135 | .notion[data-theme="custom"] {
136 | --notion-font: "Roboto", sans-serif;
137 | }
138 | ```
139 |
140 | 2. **ヘッディングスタイルの変更**:
141 |
142 | ```css
143 | .notion[data-theme="custom"] .notion-h1 {
144 | font-size: 2.5em;
145 | color: var(--select-color-0);
146 | border-bottom: 2px solid var(--fg-color-1);
147 | }
148 | ```
149 |
150 | 3. **インデントの調整**:
151 |
152 | ```css
153 | .notion[data-theme="custom"] {
154 | --notion-indent: 20px;
155 | }
156 | ```
157 |
158 | 4. **ダークモードの実装**:
159 |
160 | ```css
161 | [data-theme="dark"] .notion[data-theme="custom"] {
162 | --fg-color: rgba(255, 255, 255, 0.9);
163 | --bg-color: #2f3437;
164 | /* 他の色変数も適切に調整 */
165 | }
166 | ```
167 |
168 | これらのCSS構造とスタイリングガイドに従うことで、ノーションプレッソを使用して、一貫性のあるスタイルを実装できます。さらに、必要に応じて、細かいカスタマイズも可能です。
169 |
--------------------------------------------------------------------------------
/content/guide/cn/03. block-types/09. toggle.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 8
4 | title: "折叠块"
5 | slug: "toggle"
6 | description: "详细介绍 Notionpresso 的折叠块类型。"
7 | ---
8 |
9 | # 折叠块
10 |
11 | 折叠块用于创建可折叠的内容。当用户点击折叠块时,隐藏的内容会展开或收起,是一个交互式元素。
12 |
13 | ## 数据结构
14 |
15 | 折叠块的 Notion API 数据结构如下:
16 |
17 | ```json
18 | {
19 | "type": "toggle",
20 | "toggle": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "Click to toggle",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "Click to toggle",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "children": [
42 | // 嵌套的块...
43 | ]
44 | }
45 | }
46 | ```
47 |
48 | - `rich_text`: 折叠块的显示文本和样式信息,是一个数组。
49 | - `color`: 折叠块文本的颜色。
50 | - `children`: 当折叠块展开时显示的嵌套块的数组。
51 |
52 | ## React 组件
53 |
54 | Notionpresso 中用于渲染折叠块的组件如下:
55 |
56 | ```jsx
57 | import React, { useState, useCallback } from "react";
58 | import type { ToggleArgs } from "../types";
59 | import { getColorCss } from "../utils";
60 | import RichText from "./internal/rich-text";
61 |
62 | type ToggleProps = {
63 | children?: React.ReactNode;
64 | } & ToggleArgs;
65 |
66 | const Toggle: React.FC = ({ children, ...props }) => {
67 | const {
68 | toggle: { color, rich_text: texts },
69 | } = props;
70 |
71 | const [open, setOpen] = useState(false);
72 |
73 | const toggleOpen = useCallback(() => setOpen((prevOpen) => !prevOpen), []);
74 |
75 | return (
76 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | {open && children}
96 |
97 | );
98 | };
99 |
100 | export default Toggle;
101 | ```
102 |
103 | ## 使用示例
104 |
105 | 使用折叠块的示例如下:
106 |
107 | ```jsx
108 | import { Notion } from "@notionpresso/react";
109 |
110 | function MyNotionPage({ blocks }) {
111 | return (
112 |
113 |
114 |
115 | );
116 | }
117 | ```
118 |
119 | 这里 `blocks` 是从 Notion API 接收到的块数据数组。
120 |
121 | ## 状态管理
122 |
123 | 折叠块内部使用 `useState` 钩子来管理展开/折叠状态。`open` 变量表示折叠块的当前状态,`toggleOpen` 函数用于切换该状态。
124 |
125 | ## 样式
126 |
127 | 折叠块的样式可以通过以下 CSS 类来定制:
128 |
129 | - `.notion-block`: 所有 Notion 块的默认样式
130 | - `.notion-toggle`: 折叠块的特定样式
131 | - `.notion-toggle-open`: 折叠块展开时的样式
132 | - `.notion-toggle-content`: 折叠块内容的样式
133 | - `.notion-toggle-button`: 折叠块按钮的样式
134 | - `.notion-toggle-button-arrow`: 折叠块箭头的样式
135 |
136 | 如果需要额外的样式,可以为这些类编写 CSS。例如:
137 |
138 | ```css
139 | .notion-toggle {
140 | margin-bottom: 8px;
141 | }
142 |
143 | .notion-toggle-button {
144 | cursor: pointer;
145 | background: none;
146 | border: none;
147 | padding: 0;
148 | margin-right: 4px;
149 | }
150 |
151 | .notion-toggle-button-arrow {
152 | width: 0;
153 | height: 0;
154 | border-left: 5px solid transparent;
155 | border-right: 5px solid transparent;
156 | border-top: 5px solid #333;
157 | transition: transform 0.3s ease;
158 | }
159 |
160 | .notion-toggle-button-arrow-opened {
161 | transform: rotate(180deg);
162 | }
163 | ```
164 |
165 | ## 嵌套支持
166 |
167 | 折叠块可以包含其他块。这通过 `children` prop 来处理。只有在折叠块展开时才会渲染 `children`。
168 |
169 | ## 可访问性考虑
170 |
171 | - 使用 `aria-expanded` 属性来通知屏幕阅读器折叠块的当前状态。
172 | - 为了支持键盘导航,建议为折叠块按钮添加 `tabIndex={0}`。
173 | - 为了清楚地描述折叠块的功能,建议为折叠块按钮提供适当的 `aria-label`。
174 |
175 | ## 注意事项
176 |
177 | - 如果折叠块内部包含太多内容,可能会影响用户体验,因此建议只包含适当数量的内容。
178 | - 折叠块的展开/折叠状态不会保存到服务器上,因此刷新页面后所有折叠块都会恢复到默认状态(折叠)。
179 | - 深层次的嵌套折叠块可能会让用户感到困惑,因此建议限制嵌套层次。
180 |
--------------------------------------------------------------------------------
/content/guide/en/03. block-types/03. bulleted-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 4
4 | title: "Bulleted List Item Block"
5 | slug: "notion-block-bulleted-list-item"
6 | description: "Detailed explanation of the Bulleted List Item block type in Notionpresso."
7 | ---
8 |
9 | # Bulleted List Item Block
10 |
11 | The Bulleted List Item block is used to create unordered lists. Each item starts with a bullet point.
12 |
13 | ## Data Structure
14 |
15 | The Notion API data structure for a Bulleted List Item block is as follows:
16 |
17 | ```json
18 | {
19 | "type": "bulleted_list_item",
20 | "bulleted_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a bulleted list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a bulleted list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: An array containing the text content and style information of the list item.
46 | - `color`: Specifies the color of the text.
47 |
48 | ## React Component
49 |
50 | The component that renders the Bulleted List Item block in Notionpresso is as follows:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { BulletedListItemArgs } from "../types";
55 | import { bulletedListItemMarker, getColorCss } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type BulletedListItemProps = {
59 | children?: React.ReactNode;
60 | } & BulletedListItemArgs;
61 |
62 | const BulletedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | bulleted_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = bulletedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default BulletedListItem;
95 | ```
96 |
97 | ## Usage Example
98 |
99 | Here's an example of how to use the Bulleted List Item block:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | Here, `blocks` is an array of block data received from the Notion API.
114 |
115 | ## Styling
116 |
117 | The style of the Bulleted List Item block can be customized through the following CSS classes:
118 |
119 | - `.notion-block`: Basic style applied to all Notion blocks
120 | - `.notion-list-bulleted`: Specific style for Bulleted List Item blocks
121 | - `.notion-list-bulleted-content`: Style for the content of list items
122 | - `.notion-list-marker`: Style for bullet points
123 |
124 | If additional styling is needed, you can write CSS targeting these classes.
125 |
126 | ## Handling Nested Lists
127 |
128 | Bulleted List Items can be nested. Nested lists are handled through the `children` prop. The shape of the bullet point may change depending on the nesting level, which is handled by the `bulletedListItemMarker.getMarker` function.
129 |
130 | ## Notes
131 |
132 | - When Bulleted List Item blocks are used consecutively, they are automatically grouped into a single list.
133 | - When using nested lists, it's good to clearly express the hierarchical structure through appropriate indentation.
134 | - The shape of the bullet points follows Notion's default style, but can be customized through CSS.
135 |
--------------------------------------------------------------------------------
/content/guide/ko/02. customization-guide/03. custom-style.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "customization-guide"
3 | order: 3
4 | title: "CSS 구조 및 스타일링 가이드"
5 | slug: "css-structure-and-styling"
6 | description: "Notionpresso의 CSS 구조를 이해하고 효과적인 스타일링 방법을 학습합니다."
7 | ---
8 |
9 | # CSS 구조 및 스타일링 가이드
10 |
11 | Notionpresso은 유연하고 확장 가능한 CSS 구조를 제공합니다. 이 섹션에서는 CSS 변수 시스템, 주요 스타일링 포인트, 그리고 Indent 적용 원리에 대해 설명합니다.
12 |
13 | ## CSS 변수 시스템
14 |
15 | Notionpresso은 CSS 변수를 사용하여 전역 스타일을 정의합니다. 이를 통해 테마 변경이나 전체적인 스타일 조정을 쉽게 할 수 있습니다.
16 |
17 | 주요 CSS 변수들은 다음과 같습니다:
18 |
19 | ```css
20 | .notion {
21 | --notion-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
22 | "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif,
23 | "Segoe UI Emoji", "Segoe UI Symbol";
24 | --notion-max-width: 720px;
25 | --notion-header-height: 45px;
26 | --notion-indent: 27px;
27 |
28 | /* 색상 변수 */
29 | --fg-color: rgb(55, 53, 47);
30 | --fg-color-0: rgba(55, 53, 47, 0.09);
31 | --fg-color-1: rgba(55, 53, 47, 0.16);
32 | --fg-color-2: rgba(55, 53, 47, 0.4);
33 | --fg-color-3: rgba(55, 53, 47, 0.6);
34 | --fg-color-4: #000;
35 | --fg-color-5: rgb(233, 233, 231);
36 | --fg-color-6: rgba(55, 53, 47, 0.8);
37 | --fg-color-icon: var(--fg-color);
38 |
39 | --bg-color: #fff;
40 | --bg-color-0: rgba(135, 131, 120, 0.15);
41 | --bg-color-1: rgb(247, 246, 243);
42 | --bg-color-2: rgba(135, 131, 120, 0.15);
43 |
44 | --select-color-0: rgb(46, 170, 220);
45 | --select-color-1: rgba(35, 131, 226, 0.28);
46 | --select-color-2: #d9eff8;
47 | }
48 | ```
49 |
50 | 이러한 변수들을 재정의하여 전체적인 스타일을 쉽게 변경할 수 있습니다.
51 |
52 | ## 주요 스타일링 포인트
53 |
54 | 1. **클래스 네이밍 규칙**: 모든 클래스는 `notion-`으로 시작합니다. 이는 스타일 충돌을 방지합니다.
55 |
56 | 예: `.notion-block`, `.notion-h1`, `.notion-toggle`
57 |
58 | 2. **블록 레벨 스타일**: 모든 블록 레벨 요소에는 `notion-block` 클래스가 적용됩니다.
59 |
60 | ```css
61 | .notion-block {
62 | display: block;
63 | }
64 | ```
65 |
66 | 3. **색상 적용**: 색상은 CSS 변수를 통해 적용됩니다.
67 |
68 | ```css
69 | .notion-text {
70 | color: var(--fg-color);
71 | }
72 | ```
73 |
74 | 4. **반응형 디자인**: `--notion-max-width` 변수를 활용하여 반응형 레이아웃을 구현합니다.
75 |
76 | ```css
77 | .notion-body {
78 | width: 100%;
79 | max-width: var(--notion-max-width);
80 | margin: 0 auto;
81 | }
82 | ```
83 |
84 | ## Indent 적용 원리
85 |
86 | Notionpresso은 재귀적인 구조를 통해 들여쓰기를 구현합니다. 이는 CSS에서 다음과 같이 처리됩니다:
87 |
88 | ```css
89 | .notion-block > .notion-block {
90 | margin-left: var(--notion-indent);
91 | }
92 |
93 | .notion-block > .notion-display-contents > .notion-block {
94 | margin-left: var(--notion-indent);
95 | }
96 | ```
97 |
98 | 이 CSS 규칙은 중첩된 블록에 대해 자동으로 들여쓰기를 적용합니다. `--notion-indent` 변수를 조정하여 들여쓰기 정도를 쉽게 변경할 수 있습니다.
99 |
100 | ## 다크 모드 및 커스텀 테마
101 |
102 | Notionpresso은 기본적으로 다크 모드를 지원합니다. 다크 모드는 다음과 같이 구현되어 있습니다:
103 |
104 | ```css
105 | [data-theme="dark"] .notion {
106 | --fg-color: rgba(255, 255, 255, 0.9);
107 | --bg-color: #2f3437;
108 | /* 기타 다크 모드 관련 변수들... */
109 | }
110 | ```
111 |
112 | 또한, 커스텀 테마를 쉽게 적용할 수 있습니다. `data-theme` 속성을 사용하여 원하는 테마를 적용할 수 있습니다:
113 |
114 | ```jsx
115 | {/* Notion 컨텐츠 */}
116 | ```
117 |
118 | 그리고 CSS에서 다음과 같이 스타일을 정의할 수 있습니다:
119 |
120 | ```css
121 | .notion[data-theme="custom"] {
122 | --fg-color: #333;
123 | --bg-color: #f4f4f4;
124 | /* 기타 커스텀 테마 관련 변수들... */
125 | }
126 | ```
127 |
128 | 이 방식을 사용하면 선택자 우선순위를 활용하여 기본 스타일을 쉽게 오버라이딩할 수 있습니다.
129 |
130 | ## 스타일 커스터마이징 예시
131 |
132 | 1. **글로벌 폰트 변경**:
133 |
134 | ```css
135 | .notion[data-theme="custom"] {
136 | --notion-font: "Roboto", sans-serif;
137 | }
138 | ```
139 |
140 | 2. **헤딩 스타일 변경**:
141 |
142 | ```css
143 | .notion[data-theme="custom"] .notion-h1 {
144 | font-size: 2.5em;
145 | color: var(--select-color-0);
146 | border-bottom: 2px solid var(--fg-color-1);
147 | }
148 | ```
149 |
150 | 3. **들여쓰기 조정**:
151 |
152 | ```css
153 | .notion[data-theme="custom"] {
154 | --notion-indent: 20px;
155 | }
156 | ```
157 |
158 | 4. **다크 모드 구현**:
159 |
160 | ```css
161 | [data-theme="dark"] .notion[data-theme="custom"] {
162 | --fg-color: rgba(255, 255, 255, 0.9);
163 | --bg-color: #2f3437;
164 | /* 다른 색상 변수들도 적절히 조정 */
165 | }
166 | ```
167 |
168 | 이러한 CSS 구조와 스타일링 가이드를 따르면, Notionpresso을 사용하여 일관성 있고 유지보수가 쉬운 스타일을 구현할 수 있습니다. 또한, 필요에 따라 세부적인 커스터마이징도 가능합니다.
169 |
--------------------------------------------------------------------------------
/content/guide/ja/03. block-types/09. toggle.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 8
4 | title: "トグルブロック"
5 | slug: "toggle"
6 | description: "ノーションプレッソのトグルブロックタイプに関する詳細な説明です。"
7 | ---
8 |
9 | # トグルブロック
10 |
11 | トグルブロックは、折りたたみ可能なコンテンツを作成するために使用されます。ユーザーがトグルをクリックすると、隠れた内容が展開または折りたたまれるインタラクティブな要素です。
12 |
13 | ## データ構造
14 |
15 | トグルブロックのノーションAPIデータ構造は以下の通りです:
16 |
17 | ```json
18 | {
19 | "type": "toggle",
20 | "toggle": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "Click to toggle",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "Click to toggle",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "children": [
42 | // 中にネストされたブロック...
43 | ]
44 | }
45 | }
46 | ```
47 |
48 | - `rich_text`: トグルの表示テキストとスタイル情報を含む配列です。
49 | - `color`: トグルテキストの色を指定します。
50 | - `children`: トグルが展開されたときに表示される中にネストされたブロックの配列です。
51 |
52 | ## Reactコンポーネント
53 |
54 | Notionpressoでトグルブロックをレンダリングするコンポーネントは以下の通りです:
55 |
56 | ```jsx
57 | import React, { useState, useCallback } from "react";
58 | import type { ToggleArgs } from "../types";
59 | import { getColorCss } from "../utils";
60 | import RichText from "./internal/rich-text";
61 |
62 | type ToggleProps = {
63 | children?: React.ReactNode;
64 | } & ToggleArgs;
65 |
66 | const Toggle: React.FC = ({ children, ...props }) => {
67 | const {
68 | toggle: { color, rich_text: texts },
69 | } = props;
70 |
71 | const [open, setOpen] = useState(false);
72 |
73 | const toggleOpen = useCallback(() => setOpen((prevOpen) => !prevOpen), []);
74 |
75 | return (
76 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | {open && children}
96 |
97 | );
98 | };
99 |
100 | export default Toggle;
101 | ```
102 |
103 | ## 使用例
104 |
105 | トグルブロックを使用する例は以下の通りです:
106 |
107 | ```jsx
108 | import { Notion } from "@notionpresso/react";
109 |
110 | function MyNotionPage({ blocks }) {
111 | return (
112 |
113 |
114 |
115 | );
116 | }
117 | ```
118 |
119 | ここで`blocks`は、Notion APIから受け取ったブロックデータの配列です。
120 |
121 | ## 状態管理
122 |
123 | トグルブロックは、内部で`useState`フックを使用して開閉状態を管理します。`open`変数は、トグルの現在の状態を表し、`toggleOpen`関数はこの状態を切り替えます。
124 |
125 | ## スタイリング
126 |
127 | トグルブロックのスタイルは、以下のCSSクラスを使用してカスタマイズできます:
128 |
129 | - `.notion-block`: すべてのNotionブロックに適用される基本スタイル
130 | - `.notion-toggle`: Toggleブロックの特定スタイル
131 | - `.notion-toggle-open`: Toggleが開いているときのスタイル
132 | - `.notion-toggle-content`: Toggleの内容のスタイル
133 | - `.notion-toggle-button`: Toggleボタンのスタイル
134 | - `.notion-toggle-button-arrow`: Toggle矢印のスタイル
135 |
136 | 必要に応じて、これらのクラスを対象にCSSを記述することができます。例えば:
137 |
138 | ```css
139 | .notion-toggle {
140 | margin-bottom: 8px;
141 | }
142 |
143 | .notion-toggle-button {
144 | cursor: pointer;
145 | background: none;
146 | border: none;
147 | padding: 0;
148 | margin-right: 4px;
149 | }
150 |
151 | .notion-toggle-button-arrow {
152 | width: 0;
153 | height: 0;
154 | border-left: 5px solid transparent;
155 | border-right: 5px solid transparent;
156 | border-top: 5px solid #333;
157 | transition: transform 0.3s ease;
158 | }
159 |
160 | .notion-toggle-button-arrow-opened {
161 | transform: rotate(180deg);
162 | }
163 | ```
164 |
165 | ## 中にネストされたものをサポート
166 |
167 | トグルブロックは、他のブロックを含むことができます。これは`children`プロップを使用して処理されます。トグルが開いているときにのみ`children`がレンダリングされます。
168 |
169 | ## アクセシビリティに関する考慮事項
170 |
171 | - `aria-expanded`属性を使用して、トグルの現在の状態をスクリーンリーダーに通知します。
172 | - キーボードナビゲーションをサポートするために、トグルボタンに`tabIndex={0}`を追加することが推奨されます。
173 | - トグルボタンに適切な`aria-label`を提供して、その機能を明確に説明することが推奨されます。
174 |
175 | ## 注意点
176 |
177 | - トグルの内部には、使用者の体験を低下させる可能性があるため、適切な量のコンテンツのみを含むことが推奨されます。
178 | - トグルの開閉状態は、サーバーに保存されないため、ページを再読み込みすると、すべてのトグルがデフォルトの状態(閉じた状態)に戻ります。
179 | - 深いレベルのネストされたトグルは、使用者を混乱させる可能性があるため、可能な限りネストレベルを制限することが推奨されます。
180 |
--------------------------------------------------------------------------------
/content/guide/ko/03. block-types/09. Toggle.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 8
4 | title: "Toggle 블록"
5 | slug: "toggle"
6 | description: "Notionpresso의 Toggle 블록 타입에 대한 상세 설명입니다."
7 | ---
8 |
9 | # Toggle 블록
10 |
11 | Toggle 블록은 접을 수 있는 콘텐츠를 생성하는 데 사용됩니다. 사용자가 토글을 클릭하면 숨겨진 내용이 펼쳐지거나 접히는 인터랙티브한 요소입니다.
12 |
13 | ## 데이터 구조
14 |
15 | Toggle 블록의 Notion API 데이터 구조는 다음과 같습니다:
16 |
17 | ```json
18 | {
19 | "type": "toggle",
20 | "toggle": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "Click to toggle",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "Click to toggle",
37 | "href": null
38 | }
39 | ],
40 | "color": "default",
41 | "children": [
42 | // 중첩된 블록들...
43 | ]
44 | }
45 | }
46 | ```
47 |
48 | - `rich_text`: 토글의 표시 텍스트와 스타일 정보를 담고 있는 배열입니다.
49 | - `color`: 토글 텍스트의 색상을 지정합니다.
50 | - `children`: 토글을 펼쳤을 때 표시될 중첩된 블록들의 배열입니다.
51 |
52 | ## React 컴포넌트
53 |
54 | Notionpresso에서 Toggle 블록을 렌더링하는 컴포넌트는 다음과 같습니다:
55 |
56 | ```jsx
57 | import React, { useState, useCallback } from "react";
58 | import type { ToggleArgs } from "../types";
59 | import { getColorCss } from "../utils";
60 | import RichText from "./internal/rich-text";
61 |
62 | type ToggleProps = {
63 | children?: React.ReactNode;
64 | } & ToggleArgs;
65 |
66 | const Toggle: React.FC = ({ children, ...props }) => {
67 | const {
68 | toggle: { color, rich_text: texts },
69 | } = props;
70 |
71 | const [open, setOpen] = useState(false);
72 |
73 | const toggleOpen = useCallback(() => setOpen((prevOpen) => !prevOpen), []);
74 |
75 | return (
76 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | {open && children}
96 |
97 | );
98 | };
99 |
100 | export default Toggle;
101 | ```
102 |
103 | ## 사용 예시
104 |
105 | Toggle 블록을 사용하는 예시는 다음과 같습니다:
106 |
107 | ```jsx
108 | import { Notion } from "@notionpresso/react";
109 |
110 | function MyNotionPage({ blocks }) {
111 | return (
112 |
113 |
114 |
115 | );
116 | }
117 | ```
118 |
119 | 여기서 `blocks`는 Notion API로부터 받아온 블록 데이터 배열입니다.
120 |
121 | ## 상태 관리
122 |
123 | Toggle 블록은 내부적으로 `useState` 훅을 사용하여 열림/닫힘 상태를 관리합니다. `open` 상태 변수는 토글의 현재 상태를 나타내며, `toggleOpen` 함수는 이 상태를 전환합니다.
124 |
125 | ## 스타일링
126 |
127 | Toggle 블록의 스타일은 다음 CSS 클래스를 통해 커스터마이즈할 수 있습니다:
128 |
129 | - `.notion-block`: 모든 Notion 블록에 적용되는 기본 스타일
130 | - `.notion-toggle`: Toggle 블록 특정 스타일
131 | - `.notion-toggle-open`: 토글이 열린 상태일 때의 스타일
132 | - `.notion-toggle-content`: 토글 내용의 스타일
133 | - `.notion-toggle-button`: 토글 버튼의 스타일
134 | - `.notion-toggle-button-arrow`: 토글 화살표의 스타일
135 |
136 | 추가적인 스타일링이 필요한 경우, 이 클래스들을 대상으로 CSS를 작성하면 됩니다. 예를 들어:
137 |
138 | ```css
139 | .notion-toggle {
140 | margin-bottom: 8px;
141 | }
142 |
143 | .notion-toggle-button {
144 | cursor: pointer;
145 | background: none;
146 | border: none;
147 | padding: 0;
148 | margin-right: 4px;
149 | }
150 |
151 | .notion-toggle-button-arrow {
152 | width: 0;
153 | height: 0;
154 | border-left: 5px solid transparent;
155 | border-right: 5px solid transparent;
156 | border-top: 5px solid #333;
157 | transition: transform 0.3s ease;
158 | }
159 |
160 | .notion-toggle-button-arrow-opened {
161 | transform: rotate(180deg);
162 | }
163 | ```
164 |
165 | ## 중첩 지원
166 |
167 | Toggle 블록은 다른 블록을 포함할 수 있습니다. 이는 `children` prop을 통해 처리됩니다. 토글이 열린 상태일 때만 `children`이 렌더링됩니다.
168 |
169 | ## 접근성 고려사항
170 |
171 | - `aria-expanded` 속성을 사용하여 토글의 현재 상태를 스크린 리더에 알립니다.
172 | - 키보드 네비게이션을 지원하기 위해 토글 버튼에 `tabIndex={0}`를 추가하는 것이 좋습니다.
173 | - 토글 버튼에 적절한 `aria-label`을 제공하여 그 기능을 명확히 설명하는 것이 좋습니다.
174 |
175 | ## 주의사항
176 |
177 | - 토글 내부에 너무 많은 콘텐츠를 넣으면 사용자 경험이 저하될 수 있으므로 적절한 양의 콘텐츠만 포함하는 것이 좋습니다.
178 | - 토글의 열림/닫힘 상태는 서버에 저장되지 않으므로, 페이지를 새로고침하면 모든 토글이 기본 상태(닫힘)로 돌아갑니다.
179 | - 깊은 수준의 중첩된 토글은 사용자를 혼란스럽게 할 수 있으므로 가능한 한 중첩 수준을 제한하는 것이 좋습니다.
180 |
--------------------------------------------------------------------------------
/components/showcase/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ModernTechPortfolio from "./assets/modern-tech.png";
3 | import ChapdoBlog from "./assets/chapdo-blog.png";
4 | import Link from "next/link";
5 |
6 | interface ShowcaseItem {
7 | title: string;
8 | description: string;
9 | imageUrl: string;
10 | badge: string;
11 | link: string;
12 | }
13 |
14 | const SHOWCASE_ITEMS: ShowcaseItem[] = [
15 | {
16 | title: "Modern Tech Portfolio",
17 | description:
18 | "A tech blog with distinctive design and optimized performance",
19 | imageUrl: ModernTechPortfolio.src,
20 | badge: "Default Design",
21 | link: "https://nextjs-blog-template.pages.dev/",
22 | },
23 | {
24 | title: "CHAPDO blog",
25 | description: "This is Chapdo's personal blog.",
26 | imageUrl: ChapdoBlog.src,
27 | badge: "Chapdo blog Design",
28 | link: "https://www.chapdo.life/",
29 | },
30 | ];
31 |
32 | const PLACEHOLDER_COUNT = 2;
33 |
34 | const ShowcasePage = () => {
35 | return (
36 |
37 | {/* Hero Section */}
38 |
39 | {/* Gradient Background */}
40 |
41 |
42 |
43 |
44 |
45 | Create Limitless Experiences
46 |
47 |
48 | Break free from Notion's limitations. Your imagination becomes
49 | reality with NotionPresso
50 |
51 |
52 |
53 |
54 |
55 | {/* Showcase Grid */}
56 |
57 |
58 |
59 | {/* Showcase Cards */}
60 | {SHOWCASE_ITEMS.map((item, index) => (
61 |
65 |
66 |
71 |
72 |
73 | {item.badge}
74 |
75 |
76 |
77 |
78 |
79 | {item.title}
80 |
81 |
82 | {item.description}
83 |
84 |
85 |
89 | Visit Site →
90 |
91 |
92 |
93 |
94 | ))}
95 |
96 | {/* Placeholder Cards */}
97 | {[...Array(PLACEHOLDER_COUNT)].map((_, index) => (
98 |
106 | ))}
107 |
108 |
109 |
110 |
111 | );
112 | };
113 |
114 | export default ShowcasePage;
115 |
--------------------------------------------------------------------------------
/content/guide/en/03. block-types/05. numbered-list-item.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: "block-types"
3 | order: 5
4 | title: "Numbered List Item Block"
5 | slug: "numbered-list-item"
6 | description: "Detailed explanation of the Numbered List Item block type in Notionpresso."
7 | ---
8 |
9 | # Numbered List Item Block
10 |
11 | The Numbered List Item block is used to create ordered lists. Each item starts with a sequential number.
12 |
13 | ## Data Structure
14 |
15 | The Notion API data structure for a Numbered List Item block is as follows:
16 |
17 | ```json
18 | {
19 | "type": "numbered_list_item",
20 | "numbered_list_item": {
21 | "rich_text": [
22 | {
23 | "type": "text",
24 | "text": {
25 | "content": "This is a numbered list item",
26 | "link": null
27 | },
28 | "annotations": {
29 | "bold": false,
30 | "italic": false,
31 | "strikethrough": false,
32 | "underline": false,
33 | "code": false,
34 | "color": "default"
35 | },
36 | "plain_text": "This is a numbered list item",
37 | "href": null
38 | }
39 | ],
40 | "color": "default"
41 | }
42 | }
43 | ```
44 |
45 | - `rich_text`: An array containing the text content and style information of the list item.
46 | - `color`: Specifies the color of the text.
47 |
48 | ## React Component
49 |
50 | The component that renders the Numbered List Item block in Notionpresso is as follows:
51 |
52 | ```jsx
53 | import React from "react";
54 | import type { NumberedListItemArgs } from "../types";
55 | import { getColorCss, numberedListItemMarker } from "../utils";
56 | import RichText from "./internal/rich-text";
57 |
58 | type NumberedListProps = {
59 | children?: React.ReactNode;
60 | } & NumberedListItemArgs;
61 |
62 | const NumberedListItem: React.FC = ({
63 | children,
64 | ...props
65 | }) => {
66 | const {
67 | numbered_list_item: { rich_text: texts, color },
68 | } = props;
69 | const { marker, format } = numberedListItemMarker.getMarker(props);
70 |
71 | return (
72 |
76 |
77 |
78 |
82 | {marker}
83 |
84 |
85 |
86 |
87 |
88 | {children}
89 |
90 |
91 | );
92 | };
93 |
94 | export default NumberedListItem;
95 | ```
96 |
97 | ## Usage Example
98 |
99 | Here's an example of how to use the Numbered List Item block:
100 |
101 | ```jsx
102 | import { Notion } from "@notionpresso/react";
103 |
104 | function MyNotionPage({ blocks }) {
105 | return (
106 |
107 |
108 |
109 | );
110 | }
111 | ```
112 |
113 | Here, `blocks` is an array of block data received from the Notion API.
114 |
115 | ## Styling
116 |
117 | The style of the Numbered List Item block can be customized through the following CSS classes:
118 |
119 | - `.notion-block`: Basic style applied to all Notion blocks
120 | - `.notion-list-numbered`: Specific style for Numbered List Item blocks
121 | - `.notion-list-numbered-content`: Style for the content of list items
122 | - `.notion-list-marker`: Style for numbers
123 |
124 | If additional styling is needed, you can write CSS targeting these classes.
125 |
126 | ## Numbering System
127 |
128 | The numbering system for Numbered List Items is determined by the `numberedListItemMarker.getMarker` function. This function returns an appropriate number or character considering the current item's position and nesting level. By default, it follows this order:
129 |
130 | 1. Top level: 1, 2, 3, ...
131 | 2. Second level: a, b, c, ...
132 | 3. Third level: i, ii, iii, ...
133 |
134 | This order can be customized as needed.
135 |
136 | ## Handling Nested Lists
137 |
138 | Numbered List Items can also be nested. Nested lists are handled through the `children` prop. The numbering system may change depending on the nesting level, which is handled by the `numberedListItemMarker.getMarker` function.
139 |
140 | ## Notes
141 |
142 | - When Numbered List Item blocks are used consecutively, they are automatically grouped into a single list.
143 | - When using nested lists, it's good to clearly express the hierarchical structure through appropriate indentation.
144 | - The numbering system follows Notion's default style, but can be customized through CSS.
145 | - If a list is interrupted and then resumed, the numbering may start over, so care should be taken.
146 |
--------------------------------------------------------------------------------