Skip to content

Commit

Permalink
Merge pull request #393 from vordgi/rd-392
Browse files Browse the repository at this point in the history
rd-392 support github styled blockquotes
  • Loading branch information
vordgi authored Nov 13, 2024
2 parents a5bcc6c + f00ab6f commit beafa14
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 23 deletions.
11 changes: 11 additions & 0 deletions packages/robindoc/src/assets/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ body {
overflow: hidden auto;
scroll-padding-top: calc(var(--header-height) + 20px);
scrollbar-color: var(--r-neutral-400) var(--r-neutral-200);
white-space: pre;

@media screen and (width < $sm) {
scroll-padding-top: calc(var(--header-height) + 60px);
Expand Down Expand Up @@ -141,6 +142,11 @@ svg {
--r-secondary-900: var(--rb-secondary-900, #1e3a8a);
--r-secondary-950: var(--rb-secondary-950, #172554);
--r-success: #15803d;
--r-note: #0e7490;
--r-tip: #047857;
--r-important: #a21caf;
--r-warning: #b45309;
--r-caution: #b91c1c;
--r-link: var(--r-secondary-600);
--r-link-hovered: var(--r-secondary-800);
--r-link-active: var(--r-primary-700);
Expand Down Expand Up @@ -247,6 +253,11 @@ svg {
--r-secondary-900: var(--rb-secondary-100, #dbeafe);
--r-secondary-950: var(--rb-secondary-50, #eff6ff);
--r-success: #86efac;
--r-note: #22d3ee;
--r-tip: #34d399;
--r-important: #e879f9;
--r-warning: #fbbf24;
--r-caution: #f87171;
--r-link: var(--r-secondary-500);
--r-link-hovered: var(--r-secondary-800);
--r-link-active: var(--r-primary-700);
Expand Down
43 changes: 27 additions & 16 deletions packages/robindoc/src/components/elements/article/document.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import React from "react";
import parse, { attributesToProps, DOMNode, domToReact, HTMLReactParserOptions, Text } from "html-react-parser";
import { type BundledLanguage } from "shiki/langs";
import { type TokensList, type Token, type Tokens } from "marked";
import { type RobinProps, type Components } from "@src/core/types/content";
import { type BaseProvider } from "@src/core/providers/base";
import { NavContentLink } from "@src/components/blocks/nav-content-link";
import { AnchorHeading } from "@src/components/blocks/anchor-heading";
import { CodeSection } from "@src/components/blocks/code-section";
import { Table, Thead, Tr, Th, Tbody, Td } from "@src/components/ui/table";
import { CodeSpan } from "@src/components/ui/code-span";
import { CodeBlock } from "@src/components/ui/code-block";
import { Img } from "@src/components/ui/img";
import { NavContentLink } from "@src/components/blocks/nav-content-link";
import { Block } from "@src/components/ui/block";
import { Blockquote } from "@src/components/ui/blockquote";
import { Paragraph } from "@src/components/ui/paragraph";
import { Strong } from "@src/components/ui/strong";
import { CodeBlock } from "@src/components/ui/code-block";
import { CodeSpan } from "@src/components/ui/code-span";
import { Del } from "@src/components/ui/del";
import { Em } from "@src/components/ui/em";
import { Hr } from "@src/components/ui/hr";
import { Heading } from "@src/components/ui/heading";
import { Tabs } from "@src/components/ui/tabs";
import { Hr } from "@src/components/ui/hr";
import { Img } from "@src/components/ui/img";
import { ListItem, OrderedList, UnorderedList } from "@src/components/ui/list";
import { Paragraph } from "@src/components/ui/paragraph";
import { Strong } from "@src/components/ui/strong";
import { Table, Tbody, Td, Th, Thead, Tr } from "@src/components/ui/table";
import { Tabs } from "@src/components/ui/tabs";
import { TaskListItem, TaskOrderedList, TaskUnorderedList } from "@src/components/ui/task-list";
import { type BaseProvider } from "@src/core/providers/base";
import { type Components, type RobinProps } from "@src/core/types/content";
import parse, { attributesToProps, DOMNode, domToReact, HTMLReactParserOptions, Text } from "html-react-parser";
import { type Token, type Tokens, type TokensList } from "marked";
import React from "react";
import { type BundledLanguage } from "shiki/langs";

import { type PagesType } from "./types";
import {
formatId,
formatLinkHref,
isNewCodeToken,
parseBlockqoute,
parseCodeLang,
parseMarkdown,
validateComponentName,
Expand Down Expand Up @@ -247,7 +248,17 @@ export const Document: React.FC<ContentProps> = ({
case "em":
return <Em>{token.tokens ? <DocumentToken token={token.tokens} /> : token.raw}</Em>;
case "blockquote":
return <Blockquote>{token.tokens ? <DocumentToken token={token.tokens} /> : token.raw}</Blockquote>;
const { token: blockquoteToken, type } = parseBlockqoute(token);

return (
<Blockquote type={type}>
{blockquoteToken.tokens ? (
<DocumentToken token={blockquoteToken.tokens} />
) : (
blockquoteToken.raw
)}
</Blockquote>
);
case "codespan":
const inlineCode = token.raw.replace(/^`|`$/g, "");
const hightlightMatch = inlineCode.match(/(.+){:([a-zA-Z]+)}$/);
Expand Down
33 changes: 31 additions & 2 deletions packages/robindoc/src/components/elements/article/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import GithubSlugger from "github-slugger";
import matter from "gray-matter";
import { lexer, type Token } from "marked";
import { lexer, type Tokens, type Token } from "marked";
import { dirname, join } from "path";
import { PagesType } from "./types";

import { type BlockquoteType } from "@src/components/ui/blockquote";
import { type PagesType } from "./types";

export type AnchorData = {
title: string;
Expand Down Expand Up @@ -110,3 +112,30 @@ export const formatLinkHref = (href: string, pathname: string, pages?: PagesType
}
return { href: finalHref, external };
};

export const parseBlockqoute = (token: Tokens.Blockquote | Tokens.Generic) => {
const typeMatch = token.raw.match(/^> \[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\n/);
if (typeMatch) {
const [raw, type] = typeMatch;
const newToken = { ...token, raw: token.raw.substring(raw.length) };
if (newToken.tokens) {
const firstToken = { ...newToken.tokens[0] } as Tokens.Paragraph;
firstToken.raw = firstToken.raw.substring(type.length + 4);
firstToken.text = firstToken.text.substring(type.length + 4);
if (firstToken.tokens[0]) {
const firstSubtoken = { ...firstToken.tokens[0] } as Tokens.Paragraph;
firstSubtoken.raw = firstSubtoken.raw.substring(type.length + 4);
firstSubtoken.text = firstSubtoken.text.substring(type.length + 4);
firstToken.tokens[0] = firstSubtoken;
}
newToken.tokens[0] = firstToken;
}

return {
token: newToken,
type: type.toLowerCase() as BlockquoteType,
};
}

return { token, type: null };
};
55 changes: 53 additions & 2 deletions packages/robindoc/src/components/ui/blockquote/blockquote.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,62 @@
.r-blockquote {
background-color: var(--r-neutral-100);
padding: 12px;
border-left: 6px solid var(--r-primary-800);
padding: 8px 12px 8px 20px;
border-left: 6px solid var(--r-neutral-600);
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
font: inherit;
margin-inline: 0;
margin-block-start: 1em;
margin-block-end: 1em;
}

.r-blockquote-title {
font-weight: 600;
}

.r-blockquote-icon {
display: inline-block;
vertical-align: middle;
margin-top: -2px;
margin-right: 8px;
}

.r-blockquote-note {
border-left-color: var(--r-note);

.r-blockquote-title {
color: var(--r-note);
}
}

.r-blockquote-tip {
border-left-color: var(--r-tip);

.r-blockquote-title {
color: var(--r-tip);
}
}

.r-blockquote-important {
border-left-color: var(--r-important);

.r-blockquote-title {
color: var(--r-important);
}
}

.r-blockquote-warning {
border-left-color: var(--r-warning);

.r-blockquote-title {
color: var(--r-warning);
}
}

.r-blockquote-caution {
border-left-color: var(--r-caution);

.r-blockquote-title {
color: var(--r-caution);
}
}
9 changes: 9 additions & 0 deletions packages/robindoc/src/components/ui/blockquote/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CautionIcon, ImportantIcon, NoteIcon, TipIcon, WarningIcon } from "./icons";

export const TYPES_DATA = {
note: { title: "Note", icon: NoteIcon },
tip: { title: "Tip", icon: TipIcon },
important: { title: "Important", icon: ImportantIcon },
warning: { title: "Warning", icon: WarningIcon },
caution: { title: "Caution", icon: CautionIcon },
};
41 changes: 41 additions & 0 deletions packages/robindoc/src/components/ui/blockquote/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";

export const NoteIcon = () => (
<>
<path d="m18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2" />
<path d="M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z" />
<path d="M8 18h1" />
</>
);

export const TipIcon = () => (
<>
<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5" />
<path d="M9 18h6" />
<path d="M10 22h4" />
</>
);

export const ImportantIcon = () => (
<>
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
<path d="M12 7v2" />
<path d="M12 13h.01" />
</>
);

export const WarningIcon = () => (
<>
<circle cx="12" cy="12" r="10" />
<line x1="12" x2="12" y1="8" y2="12" />
<line x1="12" x2="12.01" y1="16" y2="16" />
</>
);

export const CautionIcon = () => (
<>
<path d="m15 9-6 6" />
<path d="M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z" />
<path d="m9 9 6 6" />
</>
);
33 changes: 31 additions & 2 deletions packages/robindoc/src/components/ui/blockquote/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import React from "react";
import clsx from "clsx";

import { TYPES_DATA } from "./data";

import "./blockquote.scss";

export type BlockquoteType = "note" | "tip" | "important" | "warning" | "caution";

interface BlockquoteProps {
className?: string;
type?: BlockquoteType | null;
}

export const Blockquote: React.FC<React.PropsWithChildren<BlockquoteProps>> = ({ className, children }) => {
return <blockquote className={clsx("r-blockquote", className)}>{children}</blockquote>;
export const Blockquote: React.FC<React.PropsWithChildren<BlockquoteProps>> = ({ className, type, children }) => {
const { icon: Icon, title } = type && type in TYPES_DATA ? TYPES_DATA[type] : {};

return (
<blockquote className={clsx("r-blockquote", type && `r-blockquote-${type}`, className)}>
{title && Icon && (
<p className="r-blockquote-title">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="r-blockquote-icon"
>
<Icon />
</svg>
{title}
</p>
)}
{children}
</blockquote>
);
};
2 changes: 1 addition & 1 deletion site/src/components/sections/features/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { Heading } from 'robindoc/lib/components/ui/heading'
import { Paragraph } from 'robindoc/lib/components/ui/paragraph'
import { ContentLink } from 'robindoc/lib/components/ui/content-link'

import { VisibleWrapper } from '../../ui/visible-wrapper';
import { SourcesImg } from './images/sources';
import { ToolsImg } from './images/tools';

import './features.scss';
import { VisibleWrapper } from '../../ui/visible-wrapper';

export const Features: React.FC = () => {
return (
Expand Down

0 comments on commit beafa14

Please sign in to comment.