Skip to content

Commit

Permalink
feat: support custom theme (#95)
Browse files Browse the repository at this point in the history
* feat: support theme type and default theme slots

* feat: support runtime and theme types

* chore: adjust default theme type

* feat: custom theme doc
  • Loading branch information
sanyuan0704 authored Oct 16, 2022
1 parent 82b73b1 commit 7127807
Show file tree
Hide file tree
Showing 47 changed files with 859 additions and 101 deletions.
16 changes: 14 additions & 2 deletions docs/.island/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export default defineConfig({
},
markdown: {
rehypePlugins: [],
remarkPlugins: [],
lineNumbers: true
remarkPlugins: []
},
route: {
exclude: ['custom.tsx', '**/fragments/**']
Expand Down Expand Up @@ -197,6 +196,19 @@ function getSidebar(lang: 'zh' | 'en') {
link: getLink('/api/config-extension')
}
]
},
{
text: getText('Client API', 'Client API'),
items: [
{
text: getText('运行时 API', 'Runtime API'),
link: getLink('/api/api-runtime')
},
{
text: getText('默认主题', 'Default Theme'),
link: getLink('/api/api-theme')
}
]
}
]
};
Expand Down
19 changes: 19 additions & 0 deletions docs/en/api/api-runtime.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Runtime API

import UsePageData from '../fragments/use-page-data.mdx';
import Content from '../fragments/content.mdx';
import RouteHook from '../fragments/route-hook.mdx';

In the process of theme development, we generally need the following key APIs to obtain page data or status:

## `usePageData`

<UsePageData />

## Content

<Content />

## 路由 Hook

<RouteHook />
68 changes: 68 additions & 0 deletions docs/en/api/api-theme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Default Theme API

The default theme contains a series of components, you can implement custom themes by overriding or extending these components.

## Layout

The `Layout` component is the entry point component of the theme, which is used by Island.js to render the layout of the entire documentation site.

```tsx
// theme/index.tsx
import { Layout } from 'islandjs';
```

It provides a series of slots through Props to achieve expansion, and the types are defined as follows:

```tsx
interface LayoutProps {
/* Before home hero */
beforeHero?: React.ReactNode;
/* After home hero */
afterHero?: React.ReactNode;
/* Before home features */
beforeFeatures?: React.ReactNode;
/* After home features */
afterFeatures?: React.ReactNode;
/* Before doc footer */
beforeDocFooter?: React.ReactNode;
/* Doc page front */
beforeDoc?: React.ReactNode;
/* Doc page end */
afterDoc?: React.ReactNode;
/* Before the title of the navigation bar in the upper left corner */
beforeNavTitle?: React.ReactNode;
/* After the title of the navigation bar in the upper left corner
*/
afterNavTitle?: React.ReactNode;
/* Above the right outline column */
beforeOutline?: React.ReactNode;
/* Below the outline column on the right */
afterOutline?: React.ReactNode;
/* Top of the entire page */
top?: React.ReactNode;
/* Bottom of the entire page */
bottom?: React.ReactNode;
}
```

## HomeLayout

The `HomeLayout` component is the theme's home page layout component, which is used by Island.js to render the home page layout.

```tsx
// theme/index.tsx
import { HomeLayout } from 'islandjs';
```

## NotFoundLayout

The `NotFoundLayout` component is the theme's 404 page layout component, which is used by Island.js to render the 404 page layout.

## setup

`setup` is used to add an initialization logic, such as global event binding.

```tsx
// theme/index.tsx
import { setup } from 'islandjs';
```
2 changes: 1 addition & 1 deletion docs/en/api/config-theme.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Theme Config

import Locales from '../fragments/locales.md';
import Locales from '../fragments/locales.mdx';

The theme config is under the `themeConfig` in root config. For example:

Expand Down
13 changes: 13 additions & 0 deletions docs/en/fragments/content.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Get the body MDX component content, that is, the doc content:

```ts
import { Content } from 'islandjs/runtime';

function Layout() {
return (
<div>
<Content />
</div>
);
}
```
File renamed without changes.
10 changes: 10 additions & 0 deletions docs/en/fragments/route-hook.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Island.js uses [react-router-dom](https://reactrouter.com/) internally to implement routing, so you can use `react-router-dom` Hook directly, for example:

```ts
import { useLocation } from 'islandjs/runtime';

function Layout() {
const location = useLocation();
return <div>Current location: {location.pathname}</div>;
}
```
45 changes: 45 additions & 0 deletions docs/en/fragments/use-page-data.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Hook for the user to get all the data of the page, such as:

```tsx
import { usePageData } from 'islandjs/runtime';

function Layout() {
const pageData = usePageData();
return <div>{pageData.title}</div>;
}
```

The type of `usePageData` is as follows:

```ts
const usePageData: () => PageData;
```

The type of `PageData` is as follows:

```ts
export interface PageData {
// Site general information, including theme configuration themeConfig information
siteData: SiteData;
// Last update time
lastUpdatedTime?: string;
// Page title
title?: string;
// Page description
description?: string;
// Front Matter of the page
frontmatter?: FrontMatterMeta;
// Page type
pageType: PageType;
// TOC data
toc?: Header[];
// Current route path
routePath: string;
// The path of the page, remove query and hash
pagePath: string;
// Doc content of the page
content?: string;
}
```

Based on this API, you can almost get all the data you need.
3 changes: 0 additions & 3 deletions docs/en/guide/custom-theme.md

This file was deleted.

193 changes: 193 additions & 0 deletions docs/en/guide/custom-theme.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Custom Theme

import UsePageData from '../fragments/use-page-data.mdx';
import Content from '../fragments/content.mdx';
import RouteHook from '../fragments/route-hook.mdx';

Island.js has a built-in set of default themes out of the box, and the internal components also provide config to customize, including:

- [Navbar](/en/guide/navbar)
- [Home Page](/en/guide/home-page)
- [API Page](/en/guide/api-page)
- [Doc Page](/en/guide/doc-page)
- [Locales](/en/guide/i18n)
- [Search](/en/guide/search)

You can click on the corresponding page to view specific configuration items. This article is to introduce how to develop a custom theme.

## Extend default theme

In most cases, you do not want to develop a theme from scratch, but want to extend it based on the default theme. In this case, you can refer to the following methods for theme development.

:::tip
If you want to develop a custom theme from scratch, you can go to
[Develop custom theme from scratch](/en/guide/custom-theme#develop-custom-theme-from-scratch)
:::

### 1. Basic Structure

By default, you need to create a `theme` directory in the `.island` directory, and then create an `index.ts(x)` file in this directory, which is your theme entry file:

```bash
.island
├── config.ts
└── theme
└── index.tsx
```

You can write the `theme/index.tsx` file as follows:

```ts
// `theme/index.tsx`
import React from 'react';
import {
Layout as DefaultLayout,
NotFoundLayout,
HomeLayout,
setup
} from 'islandjs/theme';
// Add some custom styles
import './custom.css';

const Layout = () => <DefaultLayout beforeHero={<div>beforeHero</div>} />;

// Export the three components and the setup function
export { Layout, HomeLayout, NotFoundLayout, setup };
```

As you can see, you can get and export the various components of the default theme from `islandjs/theme`, including:

- **Layout component**, the layout component of the page
- **HomeLayout component**, the layout component of the home page, used by the Layout component
- **NotFoundLayout component**, the layout component of the 404 page, used by the Layout component
- Also includes **setup function**, used to add an initialization logic, such as global event binding, which must be exported

### 2. Use Slot

It is worth noting that the Layout component has designed a series of props to support slot elements. You can use these props to extend the layout of the default theme. For example, change the above Layout component to the following form:

```tsx
import { Layout as DefaultLayout } from 'islandjs/theme';

// Show all props below
const Layout = () => (
<DefaultLayout
/* Before home hero */
beforeHero={<div>beforeHero</div>}
/* After home hero */
afterHero={<div>afterHero</div>}
/* Before home features */
beforeFeatures={<div>beforeFeatures</div>}
/* After home features */
afterFeatures={<div>afterFeatures</div>}
/* Before doc footer */
beforeDocFooter={<div>beforeDocFooter</div>}
/* Doc page front */
beforeDoc={<div>beforeDoc</div>}
/* Doc page end */
afterDoc={<div>afterDoc</div>}
/* Before the title of the navigation bar in the upper left corner */
beforeNavTitle={<span>😄</span>}
/* After the title of the navigation bar in the upper left corner
*/
afterNavTitle={<div>afterNavTitle</div>}
/* Above the right outline column */
beforeOutline={<div>beforeOutline</div>}
/* Below the outline column on the right */
afterOutline={<div>afterOutline</div>}
/* Top of the entire page */
top={<div>top</div>}
/* Bottom of the entire page */
bottom={<div>bottom</div>}
/>
);
```

### 3. Custom Component

To extend the components of the default theme, in addition to the slots, you can also customize the Home page and 404 page components, such as:

```tsx
import { Layout, setup } from 'islandjs/theme';
// Customize Home Page
const HomeLayout = () => <div>Home</div>;
// Customize 404 page

const NotFoundLayout = () => <div>404</div>;

export { Layout, HomeLayout, NotFoundLayout, setup };
```

Of course, you may need to use page data during the development process, you can get it through the [`usePageData`](/en/guide/custom-theme#usepagedata) Hook.

## Develop custom theme from scratch

### 1. Basic Structure

Of course, if you're going to develop a custom theme from scratch, you'll need to understand what makes up a theme.

By default, you need to create a `theme` directory in the `.island` directory, and then create an `index.ts(x)` file in this directory, which is your theme entry file:

```bash
.island
├── config.ts
└── theme
└── index.tsx
```

In the `theme/index.tsx` file, you need to export a Layout component, which is the entry component of your theme:

```ts
// theme/index.tsx
function Layout() {
return <div>Custom Theme Layout</div>;
}

// setup function, which will be called when the page is initialized. It is generally used to monitor global events. It can be an empty function.
const setup = () => {};

// Export the Layout component and setup function
// Both must be exported
export { Layout, setup };
```

This Layout component will be used by Island.js to render the layout of the entire documentation site. You can import your custom components in this component, such as:

```ts
// theme/index.tsx
import { Navbar } from './Navbar';

function Layout() {
return (
<div>
<Navbar />
<div>Custom Theme Layout</div>
</div>
);
}

export { Layout };

// theme/Navbar.tsx
export function Navbar() {
return <div>Custom Navbar</div>;
}
```

So the question is, how does the theme component get the page data and the content of the body MDX component? This requires the use of the `Runtime API` of Island.js.

### 2. Integrate Runtime API

In the process of theme development, we generally need the following key APIs:

#### usePageData

<UsePageData />

#### Content

<Content />

#### Route Hook

<RouteHook />
Loading

1 comment on commit 7127807

@vercel
Copy link

@vercel vercel bot commented on 7127807 Oct 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.