Skip to content

Commit 5139ae3

Browse files
committed
feat: support overriding TopBar
1 parent db02e31 commit 5139ae3

File tree

8 files changed

+120
-21
lines changed

8 files changed

+120
-21
lines changed

packages/astro/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
".": "./dist/index.js",
1313
"./types": "./types.d.ts",
1414
"./default/pages/index.astro": "./dist/default/pages/index.astro",
15-
"./default/pages/[...slug].astro": "./dist/default/pages/[...slug].astro"
15+
"./default/pages/[...slug].astro": "./dist/default/pages/[...slug].astro",
16+
"./default/components/TopBar.astro": "./dist/default/components/TopBar.astro"
1617
},
1718
"files": [
1819
"dist"
Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,13 @@
1-
---
2-
import Logo from './Logo.astro';
3-
import { LoginButton } from './LoginButton';
4-
import { ThemeSwitch } from './ThemeSwitch';
5-
import { useAuth } from './setup';
6-
7-
interface Props {
8-
logoLink: string;
9-
}
10-
11-
const { logoLink } = Astro.props;
12-
---
13-
141
<nav
152
class="bg-tk-elements-topBar-backgroundColor border-b border-tk-elements-app-borderColor flex max-w-full items-center p-3 px-4 min-h-[56px]"
163
>
174
<div class="flex flex-1">
18-
<Logo logoLink={logoLink} />
5+
<slot name="logo" />
196
</div>
207
<div>
21-
<ThemeSwitch client:load transition:persist />
8+
<slot name="theme-switch" />
229
</div>
2310
<div>
24-
{useAuth && <LoginButton client:load transition:persist />}
11+
<slot name="login-button" />
2512
</div>
2613
</nav>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
import { TopBar } from 'tutorialkit:override-components';
3+
import { ThemeSwitch } from './ThemeSwitch';
4+
import { LoginButton } from './LoginButton';
5+
import Logo from './Logo.astro';
6+
import { useAuth } from './setup';
7+
8+
interface Props {
9+
logoLink: string;
10+
}
11+
12+
const { logoLink } = Astro.props;
13+
---
14+
15+
<TopBar>
16+
<Logo slot="logo" logoLink={logoLink ?? '/'} />
17+
18+
<ThemeSwitch client:load transition:persist slot="theme-switch" />
19+
20+
{useAuth && <LoginButton client:load transition:persist slot="login-button" />}
21+
</TopBar>

packages/astro/src/default/pages/[...slug].astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
import type { InferGetStaticPropsType } from 'astro';
3-
import TopBar from '../components/TopBar.astro';
3+
import TopBarWrapper from '../components/TopBarWrapper.astro';
44
import MainContainer from '../components/MainContainer.astro';
55
import Layout from '../layouts/Layout.astro';
66
import '../styles/base.css';
@@ -19,7 +19,7 @@ const { lesson, logoLink, navList, title } = Astro.props as Props;
1919
<Layout title={title}>
2020
<div id="previews-container"></div>
2121
<main class="max-w-full flex flex-col h-screen overflow-hidden" data-swap-root>
22-
<TopBar logoLink={logoLink ?? '/'} />
22+
<TopBarWrapper logoLink={logoLink ?? '/'} />
2323
<MainContainer lesson={lesson} navList={navList} />
2424
</main>
2525
</Layout>

packages/astro/src/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { updateMarkdownConfig } from './remark/index.js';
66
import { tutorialkitCore } from './vite-plugins/core.js';
77
import { userlandCSS, watchUserlandCSS } from './vite-plugins/css.js';
88
import { tutorialkitStore } from './vite-plugins/store.js';
9+
import { overrideComponents, type OverrideComponentsOptions } from './vite-plugins/override-components.js';
910
import { WebContainerFiles } from './webcontainer-files/index.js';
1011

1112
export const unoCSSConfig = {
@@ -27,6 +28,11 @@ export interface Options {
2728
*/
2829
defaultRoutes?: boolean | 'tutorial-only';
2930

31+
/**
32+
* Override components of TutorialKit.
33+
*/
34+
components?: OverrideComponentsOptions;
35+
3036
/**
3137
* The value of the Cross-Origin-Embedder-Policy header for the dev server.
3238
* This is required for webcontainer to works.
@@ -62,7 +68,12 @@ export interface Options {
6268
};
6369
}
6470

65-
export default function createPlugin({ defaultRoutes = true, isolation, enterprise }: Options = {}): AstroIntegration {
71+
export default function createPlugin({
72+
defaultRoutes = true,
73+
components,
74+
isolation,
75+
enterprise,
76+
}: Options = {}): AstroIntegration {
6677
const webcontainerFiles = new WebContainerFiles();
6778

6879
let _config: AstroConfig;
@@ -96,6 +107,7 @@ export default function createPlugin({ defaultRoutes = true, isolation, enterpri
96107
userlandCSS,
97108
tutorialkitStore,
98109
tutorialkitCore,
110+
overrideComponents(components),
99111
process.env.TUTORIALKIT_DEV ? (await import('vite-plugin-inspect')).default() : null,
100112
],
101113
},
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* A plugin that lets users to override TutorialKit's components.
3+
*
4+
* The virtual module can be imported as:
5+
*
6+
* ```ts
7+
* import { TopBar } from 'tutorialkit:override-components';
8+
*
9+
* <TopBar />
10+
* ```
11+
*
12+
* User can override the components in `astro.config.ts`:
13+
*
14+
* ```ts
15+
* export default defineConfig({
16+
* integrations: [
17+
* tutorialkit({
18+
* components: {
19+
* TopBar: './CustomTopBar.astro',
20+
* },
21+
* }),
22+
* ],
23+
* });
24+
* ```
25+
*/
26+
import type { VitePlugin } from '../types.js';
27+
28+
export interface OverrideComponentsOptions {
29+
/**
30+
* Component for overriding the top bar.
31+
*
32+
* This component has 3 slots that are used to pass TutorialKit's default components:
33+
* - `logo`: Logo of the application
34+
* - `theme-switch`: Switch for changing the theme
35+
* - `login-button`: TODO, add description here
36+
*
37+
* Usage:
38+
* ```jsx
39+
* <slot name="logo" />
40+
* <slot name="theme-switch" />
41+
* <slot name="login-button" />
42+
* ```
43+
*/
44+
TopBar?: string;
45+
}
46+
47+
const virtualModuleId = 'tutorialkit:override-components';
48+
const resolvedId = `\0${virtualModuleId}`;
49+
50+
export function overrideComponents(options?: OverrideComponentsOptions): VitePlugin {
51+
const topBar = options?.TopBar || '@tutorialkit/astro/default/components/TopBar.astro';
52+
53+
return {
54+
name: 'tutorialkit-override-components-plugin',
55+
resolveId(id) {
56+
if (id === virtualModuleId) {
57+
return resolvedId;
58+
}
59+
60+
return undefined;
61+
},
62+
async load(id) {
63+
if (id === resolvedId) {
64+
return `
65+
export { default as TopBar } from '${topBar}';
66+
`;
67+
}
68+
69+
return undefined;
70+
},
71+
};
72+
}

packages/astro/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"moduleResolution": "Bundler",
88
"outDir": "dist"
99
},
10-
"include": ["src"],
10+
"include": ["src", "types.d.ts"],
1111
"references": [
1212
{ "path": "../runtime" },
1313
{ "path": "../types" },

packages/astro/types.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ declare module 'tutorialkit:store' {
77
declare module 'tutorialkit:core' {
88
export const webcontainer: import('@webcontainer/api').WebContainer;
99
}
10+
11+
declare module 'tutorialkit:override-components' {
12+
const topBar: typeof import('./src/default/components/TopBar.astro').default;
13+
14+
export { topBar as TopBar };
15+
}

0 commit comments

Comments
 (0)