From f00ab6f557e85b6f7a8c427e811c73222669e3c4 Mon Sep 17 00:00:00 2001 From: Vordgi Date: Wed, 13 Nov 2024 16:16:58 +0400 Subject: [PATCH] rd-392 support github styled blockquotes --- packages/robindoc/src/assets/base.scss | 11 ++++ .../components/elements/article/document.tsx | 43 +++++++++------ .../src/components/elements/article/utils.ts | 33 ++++++++++- .../components/ui/blockquote/blockquote.scss | 55 ++++++++++++++++++- .../src/components/ui/blockquote/data.ts | 9 +++ .../src/components/ui/blockquote/icons.tsx | 41 ++++++++++++++ .../src/components/ui/blockquote/index.tsx | 33 ++++++++++- .../components/sections/features/index.tsx | 2 +- 8 files changed, 204 insertions(+), 23 deletions(-) create mode 100644 packages/robindoc/src/components/ui/blockquote/data.ts create mode 100644 packages/robindoc/src/components/ui/blockquote/icons.tsx diff --git a/packages/robindoc/src/assets/base.scss b/packages/robindoc/src/assets/base.scss index c3a513d9..d17fbbca 100644 --- a/packages/robindoc/src/assets/base.scss +++ b/packages/robindoc/src/assets/base.scss @@ -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); @@ -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); @@ -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); diff --git a/packages/robindoc/src/components/elements/article/document.tsx b/packages/robindoc/src/components/elements/article/document.tsx index 0a776767..8537b966 100644 --- a/packages/robindoc/src/components/elements/article/document.tsx +++ b/packages/robindoc/src/components/elements/article/document.tsx @@ -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, @@ -247,7 +248,17 @@ export const Document: React.FC = ({ case "em": return {token.tokens ? : token.raw}; case "blockquote": - return
{token.tokens ? : token.raw}
; + const { token: blockquoteToken, type } = parseBlockqoute(token); + + return ( +
+ {blockquoteToken.tokens ? ( + + ) : ( + blockquoteToken.raw + )} +
+ ); case "codespan": const inlineCode = token.raw.replace(/^`|`$/g, ""); const hightlightMatch = inlineCode.match(/(.+){:([a-zA-Z]+)}$/); diff --git a/packages/robindoc/src/components/elements/article/utils.ts b/packages/robindoc/src/components/elements/article/utils.ts index 91092166..39cbed8d 100644 --- a/packages/robindoc/src/components/elements/article/utils.ts +++ b/packages/robindoc/src/components/elements/article/utils.ts @@ -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; @@ -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 }; +}; diff --git a/packages/robindoc/src/components/ui/blockquote/blockquote.scss b/packages/robindoc/src/components/ui/blockquote/blockquote.scss index 87020204..f1cba5af 100644 --- a/packages/robindoc/src/components/ui/blockquote/blockquote.scss +++ b/packages/robindoc/src/components/ui/blockquote/blockquote.scss @@ -1,7 +1,7 @@ .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; @@ -9,3 +9,54 @@ 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); + } +} diff --git a/packages/robindoc/src/components/ui/blockquote/data.ts b/packages/robindoc/src/components/ui/blockquote/data.ts new file mode 100644 index 00000000..83bae36e --- /dev/null +++ b/packages/robindoc/src/components/ui/blockquote/data.ts @@ -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 }, +}; diff --git a/packages/robindoc/src/components/ui/blockquote/icons.tsx b/packages/robindoc/src/components/ui/blockquote/icons.tsx new file mode 100644 index 00000000..f37818c0 --- /dev/null +++ b/packages/robindoc/src/components/ui/blockquote/icons.tsx @@ -0,0 +1,41 @@ +import React from "react"; + +export const NoteIcon = () => ( + <> + + + + +); + +export const TipIcon = () => ( + <> + + + + +); + +export const ImportantIcon = () => ( + <> + + + + +); + +export const WarningIcon = () => ( + <> + + + + +); + +export const CautionIcon = () => ( + <> + + + + +); diff --git a/packages/robindoc/src/components/ui/blockquote/index.tsx b/packages/robindoc/src/components/ui/blockquote/index.tsx index 66d6103a..ed7d3c5b 100644 --- a/packages/robindoc/src/components/ui/blockquote/index.tsx +++ b/packages/robindoc/src/components/ui/blockquote/index.tsx @@ -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> = ({ className, children }) => { - return
{children}
; +export const Blockquote: React.FC> = ({ className, type, children }) => { + const { icon: Icon, title } = type && type in TYPES_DATA ? TYPES_DATA[type] : {}; + + return ( +
+ {title && Icon && ( +

+ + + + {title} +

+ )} + {children} +
+ ); }; diff --git a/site/src/components/sections/features/index.tsx b/site/src/components/sections/features/index.tsx index 4eb1cb2b..33aefaf7 100644 --- a/site/src/components/sections/features/index.tsx +++ b/site/src/components/sections/features/index.tsx @@ -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 (