feat(nx-dev): add deepdive callout component

This commit is contained in:
Juri 2024-08-23 00:16:55 +02:00 committed by Juri Strumpflohner
parent 61ecd4b4f8
commit daf5837a21
7 changed files with 75 additions and 109 deletions

View File

@ -94,14 +94,14 @@ Your content goes here.
{% /callout %} {% /callout %}
``` ```
#### Disclosure #### Deep Dives
A disclosure can be used for less important information that is initially collapsed. These are special callouts that are collapsed with the intention of containing more deep-dive information about the topic which isn't required to understand right away.
```markdown ```markdown
{% disclosure title="string" %} {% callout type="deepdive" title="string" %}
Your content goes here. Your deep-dive content goes here.
{% /disclosure %} {% /callout %}
``` ```
#### Cards #### Cards

View File

@ -251,7 +251,7 @@ If we don't use the `nx affected` command in CI, no matter how our repo is struc
Note that the 50% chance of any project being modified is an arbitrary number. If we had picked a lower chance of being modified all the expected values would decrease as well. Every repository is different, but this illustrates that a flatter structure will help speed up your CI pipeline. Note that the 50% chance of any project being modified is an arbitrary number. If we had picked a lower chance of being modified all the expected values would decrease as well. Every repository is different, but this illustrates that a flatter structure will help speed up your CI pipeline.
{% disclosure title="The Math Behind the Expected Number of Affected Projects" %} {% callout title="The Math Behind the Expected Number of Affected Projects" type="deepdive" %}
**Definitions:** **Definitions:**
@ -290,7 +290,7 @@ Note that the 50% chance of any project being modified is an arbitrary number. I
**Expected Number of Affected Projects:** **Expected Number of Affected Projects:**
a(1) + a(2) + a(3) = 0.5 + 0.5 + 0.5 = 1.5 a(1) + a(2) + a(3) = 0.5 + 0.5 + 0.5 = 1.5
{% /disclosure %} {% /callout %}
## Reduce Wasted Time with Remote Caching ## Reduce Wasted Time with Remote Caching

View File

@ -33,8 +33,8 @@ distribute-on:
large-changeset: 10 linux-medium-js large-changeset: 10 linux-medium-js
``` ```
{% callout type="note" title="How is the size of the PR determined?" %} {% callout type="deepdive" title="How is the size of the PR determined?" %}
To determine the size of the PR, Nx Cloud calculates the ratio between the number of [affected projects](/ci/features/affected) and the total number of projects in the workspace. It then categorizes it as small, medium, or large. To determine the size of the PR, Nx Cloud calculates the relationship between the number of [affected projects](/ci/features/affected) and the total number of projects in the workspace. It then assigns it to one of the three categories: small, medium, or large.
{% /callout %} {% /callout %}
You can then reference it in your CI pipeline configuration: You can then reference it in your CI pipeline configuration:

View File

@ -19,8 +19,6 @@ import { CallToAction } from './lib/tags/call-to-action.component';
import { callToAction } from './lib/tags/call-to-action.schema'; import { callToAction } from './lib/tags/call-to-action.schema';
import { Card, Cards, LinkCard } from './lib/tags/cards.component'; import { Card, Cards, LinkCard } from './lib/tags/cards.component';
import { card, cards, linkCard } from './lib/tags/cards.schema'; import { card, cards, linkCard } from './lib/tags/cards.schema';
import { Disclosure } from './lib/tags/disclosure.component';
import { disclosure } from './lib/tags/disclosure.schema';
import { GithubRepository } from './lib/tags/github-repository.component'; import { GithubRepository } from './lib/tags/github-repository.component';
import { githubRepository } from './lib/tags/github-repository.schema'; import { githubRepository } from './lib/tags/github-repository.schema';
import { StackblitzButton } from './lib/tags/stackblitz-button.component'; import { StackblitzButton } from './lib/tags/stackblitz-button.component';
@ -78,7 +76,6 @@ export const getMarkdocCustomConfig = (
'call-to-action': callToAction, 'call-to-action': callToAction,
card, card,
cards, cards,
disclosure,
'link-card': linkCard, 'link-card': linkCard,
'github-repository': githubRepository, 'github-repository': githubRepository,
'stackblitz-button': stackblitzButton, 'stackblitz-button': stackblitzButton,
@ -107,7 +104,6 @@ export const getMarkdocCustomConfig = (
CallToAction, CallToAction,
Card, Card,
Cards, Cards,
Disclosure,
LinkCard, LinkCard,
CustomLink, CustomLink,
FenceWrapper, FenceWrapper,

View File

@ -1,13 +1,19 @@
'use client';
import React, { useState, useEffect } from 'react';
import cx from 'classnames';
import { import {
ChevronRightIcon,
ChevronDownIcon,
CheckCircleIcon, CheckCircleIcon,
ExclamationCircleIcon, ExclamationCircleIcon,
HandRaisedIcon, HandRaisedIcon,
InformationCircleIcon, InformationCircleIcon,
AcademicCapIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import cx from 'classnames';
import { ReactNode } from 'react';
type CalloutType = 'note' | 'warning' | 'check' | 'caution'; type CalloutType = 'note' | 'warning' | 'check' | 'caution' | 'deepdive';
const typeMap: Record< const typeMap: Record<
CalloutType, CalloutType,
{ {
@ -26,7 +32,7 @@ const typeMap: Record<
/> />
), ),
backgroundColor: 'bg-slate-50 dark:bg-slate-800/40', backgroundColor: 'bg-slate-50 dark:bg-slate-800/40',
borderColor: 'ring-slate-100 dark:ring-slate-700', borderColor: 'border-slate-200 dark:border-slate-700',
titleColor: 'text-slate-600 dark:text-slate-300', titleColor: 'text-slate-600 dark:text-slate-300',
textColor: 'text-slate-700 dark:text-slate-400', textColor: 'text-slate-700 dark:text-slate-400',
}, },
@ -38,7 +44,7 @@ const typeMap: Record<
/> />
), ),
backgroundColor: 'bg-yellow-50 dark:bg-yellow-900/30', backgroundColor: 'bg-yellow-50 dark:bg-yellow-900/30',
borderColor: 'ring-yellow-100 dark:ring-yellow-900', borderColor: 'border-yellow-200 dark:border-yellow-800',
titleColor: 'text-yellow-600 dark:text-yellow-400', titleColor: 'text-yellow-600 dark:text-yellow-400',
textColor: 'text-yellow-700 dark:text-yellow-600', textColor: 'text-yellow-700 dark:text-yellow-600',
}, },
@ -50,7 +56,7 @@ const typeMap: Record<
/> />
), ),
backgroundColor: 'bg-green-50 dark:bg-green-900/30', backgroundColor: 'bg-green-50 dark:bg-green-900/30',
borderColor: 'ring-green-100 dark:ring-green-900', borderColor: 'border-green-200 dark:border-green-800',
titleColor: 'text-green-600 dark:text-green-400', titleColor: 'text-green-600 dark:text-green-400',
textColor: 'text-green-700 dark:text-green-600', textColor: 'text-green-700 dark:text-green-600',
}, },
@ -59,43 +65,82 @@ const typeMap: Record<
<HandRaisedIcon className="h-5 w-5 text-red-500" aria-hidden="true" /> <HandRaisedIcon className="h-5 w-5 text-red-500" aria-hidden="true" />
), ),
backgroundColor: 'bg-red-50 dark:bg-red-900/30', backgroundColor: 'bg-red-50 dark:bg-red-900/30',
borderColor: 'ring-red-100 dark:ring-red-900', borderColor: 'border-red-200 dark:border-red-800',
titleColor: 'text-red-600 dark:text-red-400', titleColor: 'text-red-600 dark:text-red-400',
textColor: 'text-red-700 dark:text-red-600', textColor: 'text-red-700 dark:text-red-600',
}, },
deepdive: {
icon: (
<AcademicCapIcon className="h-5 w-5 text-blue-500" aria-hidden="true" />
),
backgroundColor: 'bg-blue-50 dark:bg-blue-900/30',
borderColor: 'border-blue-200 dark:border-blue-800',
titleColor: 'text-white-600 dark:text-white-400',
textColor: 'text-white-700 dark:text-white-600',
},
}; };
export function Callout({ export function Callout({
title, title,
type, type,
children, children,
defaultExpanded = false,
}: { }: {
title: string; title: string;
type: CalloutType; type: CalloutType;
children: ReactNode; children: ReactNode;
defaultExpanded?: boolean;
}) { }) {
const [isOpen, setIsOpen] = useState(type !== 'deepdive');
const ui = typeMap[type] || typeMap.note; const ui = typeMap[type] || typeMap.note;
const isCollapsible = type === 'deepdive';
useEffect(() => {
if (isCollapsible) {
setIsOpen(defaultExpanded);
}
}, [defaultExpanded, isCollapsible]);
const toggleOpen = () => {
if (isCollapsible) {
setIsOpen(!isOpen);
}
};
// We use `<span>`s because we are inside `<p>`s
return ( return (
<span <div
className={cx( className={cx(
'my-6 block rounded-md p-4 ring-1', 'my-6 overflow-hidden rounded-md border',
ui.backgroundColor, ui.borderColor,
ui.borderColor ui.backgroundColor
)} )}
> >
<span className="flex"> <div
onClick={toggleOpen}
className={cx(
'flex w-full items-center justify-between p-4',
'transition-colors duration-200 hover:bg-opacity-80',
{ 'cursor-pointer': isCollapsible }
)}
>
<span className="flex items-center">
<span className="flex-shrink-0">{ui.icon}</span> <span className="flex-shrink-0">{ui.icon}</span>
<span className="ml-3"> <span className={cx('ml-3 text-sm font-medium', ui.titleColor)}>
<span className={cx('mt-0 block text-sm font-medium', ui.titleColor)}>
{title} {title}
</span> </span>
<span className={cx('prose-sm mt-2 block', ui.textColor)}>
{children}
</span>
</span>
</span>
</span> </span>
{isCollapsible &&
(isOpen ? (
<ChevronDownIcon className="h-5 w-5" />
) : (
<ChevronRightIcon className="h-5 w-5" />
))}
</div>
{isOpen && (
<div className="px-4 pb-4 pt-0">
<span className={cx('prose-sm block', ui.textColor)}>{children}</span>
</div>
)}
</div>
); );
} }

View File

@ -1,61 +0,0 @@
'use client';
import {
ChevronDoubleUpIcon,
ChevronDoubleDownIcon,
} from '@heroicons/react/24/outline';
import cx from 'classnames';
import { ReactNode, useState } from 'react';
const ui = {
upIcon: (
<ChevronDoubleUpIcon
className="h-5 w-5 text-slate-500 dark:text-slate-300"
aria-hidden="true"
/>
),
downIcon: (
<ChevronDoubleDownIcon
className="h-5 w-5 text-slate-500 dark:text-slate-300"
aria-hidden="true"
/>
),
backgroundColor: 'bg-slate-50 dark:bg-slate-800/40',
borderColor: 'ring-slate-100 dark:ring-slate-700',
titleColor: 'text-slate-600 dark:text-slate-300',
textColor: 'text-slate-700 dark:text-slate-400',
};
export function Disclosure({
title,
children,
}: {
title: string;
children: ReactNode;
}) {
const [collapsed, setCollapsed] = useState(true);
// We use `<span>`s because we are inside `<p>`s
return (
<span
className={cx(
'my-6 block flex-col rounded-md bg-slate-50 ring-1 ring-slate-100 dark:bg-slate-800/40 dark:ring-slate-700',
ui.backgroundColor,
ui.borderColor
)}
>
<span
onClick={() => setCollapsed(!collapsed)}
className="flex flex-shrink-0 cursor-pointer p-4"
>
{collapsed ? ui.downIcon : ui.upIcon}
<span className={cx('ml-3 block text-sm font-medium', ui.titleColor)}>
{title}
</span>
</span>
<span className={cx('block p-4 pl-12 pt-0', collapsed ? 'hidden' : '')}>
<span className={cx('prose-sm block', ui.textColor)}>{children}</span>
</span>
</span>
);
}

View File

@ -1,14 +0,0 @@
import { Schema } from '@markdoc/markdoc';
export const disclosure: Schema = {
render: 'Disclosure',
description: 'Display the enclosed content in a collapsible box',
children: ['paragraph', 'tag', 'list'],
attributes: {
title: {
type: 'String',
required: true,
description: 'The title displayed at the top of the collapsible box',
},
},
};