feat(nx-dev): add course preview component

This commit is contained in:
Juri 2025-04-16 11:21:07 +02:00 committed by Juri Strumpflohner
parent 5dcec84e63
commit 4f8b407a75
7 changed files with 91 additions and 11 deletions

View File

@ -299,6 +299,14 @@ Have a more decent button-like widget that you can place below sections of a tut
{% video-link link="https://youtu.be/OQ-Zc5tcxJE?t=64" /%} {% video-link link="https://youtu.be/OQ-Zc5tcxJE?t=64" /%}
``` ```
#### Course video embed
This is for embedding a video just like with the Youtube component, but in addition to have a link to a Nx Course (nx.dev/courses) video to improve the discoverability of these courses.
```markdown
{% course-video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="/courses/pnpm-nx-next/lessons-01-nx-init" title="Initialize Nx in Your Project with nx init" /%}
```
#### Project Details View #### Project Details View
Embed a Project Details View that is identical what is shown in Nx Console or `nx show project myproject --web` Embed a Project Details View that is identical what is shown in Nx Console or `nx show project myproject --web`

View File

@ -21,10 +21,7 @@ an existing NPM/Yarn or PNPM-based monorepo setup, you can easily add Nx to get
This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and
add an `nx.json` for configuring caching and task pipelines. add an `nx.json` for configuring caching and task pipelines.
{% youtube {% course-video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="/courses/pnpm-nx-next/lessons-01-nx-init" title="Initialize Nx in Your Project with nx init" /%}
src="https://www.youtube.com/embed/ngdoUQBvAjo"
title="Add Nx to a PNPM workspaces monorepo"
width="100%" /%}
## Installing Nx ## Installing Nx

View File

@ -52,21 +52,24 @@ pre,
iframe[src*='youtube'] { iframe[src*='youtube'] {
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
} }
.prose iframe,
.prose img { .prose iframe:not(.not-prose):not(.not-prose *),
.prose img:not(.not-prose):not(.not-prose *) {
display: block; display: block;
margin: 2rem auto; margin: 2rem auto;
} }
.prose iframe {
.prose iframe:not(.not-prose):not(.not-prose *) {
width: 100%; width: 100%;
max-width: 560px; max-width: 560px;
} }
.prose img {
.prose img:not(.not-prose):not(.not-prose *) {
width: max-content; width: max-content;
} }
.prose iframe[src^="https://staging.nx.app"], .prose iframe:not(.not-prose):not(.not-prose *)[src^="https://staging.nx.app"],
.prose iframe[src^="https://nx.app"] .prose iframe:not(.not-prose):not(.not-prose *)[src^="https://nx.app"]
{ {
height: 80vh; height: 80vh;
max-width: 100%; max-width: 100%;

View File

@ -17,6 +17,7 @@ export * from './lib/tweet';
export * from './lib/typography'; export * from './lib/typography';
export * from './lib/github-star-widget'; export * from './lib/github-star-widget';
export * from './lib/youtube.component'; export * from './lib/youtube.component';
export * from './lib/course-video';
export * from './lib/x-icon'; export * from './lib/x-icon';
export * from './lib/discord-icon'; export * from './lib/discord-icon';
export * from './lib/trusted-by'; export * from './lib/trusted-by';

View File

@ -0,0 +1,48 @@
import { ButtonLink } from './button';
import { YouTube } from './youtube.component';
export interface CourseVideoProps {
/**
* The YouTube video URL
*/
src: string;
courseTitle: string;
courseUrl: string;
}
export function CourseVideo({
src: videoUrl,
courseTitle,
courseUrl,
}: CourseVideoProps): JSX.Element {
return (
<div className="not-prose mx-auto max-w-4xl">
<div className="overflow-hidden rounded-xl bg-slate-50 shadow-sm ring-1 ring-slate-900/5 dark:bg-slate-800/50 dark:ring-slate-700/50">
<div className="aspect-video w-full">
<YouTube
src={videoUrl}
title={courseTitle}
width="100%"
disableRoundedCorners={true}
/>
</div>
<div className="px-6 py-4">
<div className="flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:justify-between">
<h3 className="text-center text-xl font-medium text-slate-900 sm:text-left dark:text-white">
{courseTitle}
</h3>
<ButtonLink
href={courseUrl}
variant="primary"
size="small"
title={`Watch the full "${courseTitle}" course`}
className="whitespace-nowrap"
>
Watch full course
</ButtonLink>
</div>
</div>
</div>
</div>
);
}

View File

@ -39,6 +39,8 @@ import { Tab, Tabs } from './lib/tags/tabs.component';
import { tab, tabs } from './lib/tags/tabs.schema'; import { tab, tabs } from './lib/tags/tabs.schema';
import { Tweet, tweet } from '@nx/nx-dev/ui-common'; import { Tweet, tweet } from '@nx/nx-dev/ui-common';
import { YouTube, youtube } from '@nx/nx-dev/ui-common'; import { YouTube, youtube } from '@nx/nx-dev/ui-common';
import { CourseVideo } from '@nx/nx-dev/ui-common';
import { courseVideo } from './lib/tags/course-video.schema';
import { VideoLink, videoLink } from './lib/tags/video-link.component'; import { VideoLink, videoLink } from './lib/tags/video-link.component';
// import { SvgAnimation, svgAnimation } from './lib/tags/svg-animation.component'; // import { SvgAnimation, svgAnimation } from './lib/tags/svg-animation.component';
import { Pill } from './lib/tags/pill.component'; import { Pill } from './lib/tags/pill.component';
@ -91,6 +93,7 @@ export const getMarkdocCustomConfig = (
toc: tableOfContents, toc: tableOfContents,
tweet, tweet,
youtube, youtube,
'course-video': courseVideo,
'video-link': videoLink, 'video-link': videoLink,
metrics, metrics,
// 'svg-animation': svgAnimation, // 'svg-animation': svgAnimation,
@ -123,6 +126,7 @@ export const getMarkdocCustomConfig = (
Testimonial, Testimonial,
Tweet, Tweet,
YouTube, YouTube,
CourseVideo,
VideoLink, VideoLink,
VideoPlayer, VideoPlayer,
Metrics, Metrics,
@ -176,4 +180,4 @@ export const renderMarkdown: (
}; };
}; };
export { Metrics, VideoLink, GithubRepository }; export { Metrics, VideoLink, GithubRepository, CourseVideo };

View File

@ -0,0 +1,19 @@
import { Schema } from '@markdoc/markdoc';
export const courseVideo: Schema = {
render: 'CourseVideo',
attributes: {
src: {
type: 'String',
required: true,
},
courseTitle: {
type: 'String',
required: true,
},
courseUrl: {
type: 'String',
required: true,
},
},
};