Skip to content

Commit

Permalink
feat: added "note block" in markdown component
Browse files Browse the repository at this point in the history
  • Loading branch information
ShionTerunaga committed Nov 22, 2023
1 parent 15de13a commit 6dee0b0
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/little-papayas-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@yamada-ui/markdown": minor
---

When write note in `Markdown` component, can express a supplementary explanation using Yamada UI `Alert` component.
53 changes: 35 additions & 18 deletions packages/components/markdown/src/markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Alert, AlertIcon, AlertDescription } from "@yamada-ui/alert"
import {
Alert,
AlertIcon,
AlertDescription,
AlertProps,
} from "@yamada-ui/alert"
import {
ui,
forwardRef,
Expand Down Expand Up @@ -29,6 +34,7 @@ import rehypeRaw from "rehype-raw"
import remarkGfm from "remark-gfm"
import { CodeThemeNames, codeThemes } from "./code-theme"
import { remarkUIComponent } from "./remark-ui-component"

export type MarkdownComponents = Components
export type MarkdownComponentProps<Y extends keyof JSX.IntrinsicElements> =
ComponentPropsWithoutRef<Y> & ReactMarkdownProps
Expand All @@ -40,11 +46,33 @@ export type MarkdownComponentUnorderedListProps = UnorderedListProps
export type MarkdownComponentTableCellProps = TableDataCellProps
export type MarkdownComponentTableRowProps = TableRowProps

const uiComponents = ({
codeProps,
noteProps,
}: Pick<MarkdownProps, "codeProps" | "noteProps">): Components =>
({
code: (props: CodeProps) => {
return <Code {...codeProps} {...props} />
},
note: ({ children, ...rest }: any) => {
return (
<Alert mb="4" {...noteProps} {...rest}>
<AlertIcon />
<AlertDescription>{children}</AlertDescription>
</Alert>
)
},
}) as Components

type MarkdownOptions = ReactMarkdownOptions & {
/**
* If provided, this will set the theme for the code.
*/
code?: { theme?: CodeThemeNames | ColorModeArray<CodeThemeNames> }
codeProps?: { theme?: CodeThemeNames | ColorModeArray<CodeThemeNames> }
/**
* If provided, this will set the theme for the note.
*/
noteProps?: AlertProps
}

export type MarkdownProps = Omit<HTMLUIProps<"div">, "children"> &
Expand All @@ -53,14 +81,14 @@ export type MarkdownProps = Omit<HTMLUIProps<"div">, "children"> &

export const Markdown = forwardRef<MarkdownProps, "div">((props, ref) => {
const [css, mergedProps] = useComponentStyle("Markdown", props)
console.log(css)
let {
className,
remarkPlugins,
rehypePlugins,
linkTarget = "_blank",
components,
code,
codeProps,
noteProps,
...rest
} = omitThemeProps(mergedProps)

Expand All @@ -71,20 +99,9 @@ export const Markdown = forwardRef<MarkdownProps, "div">((props, ref) => {
]
rehypePlugins = [rehypeRaw, ...filterEmpty(rehypePlugins ?? [])]
components = {
code: (props: CodeProps) => {
console.log(props)
return <Code {...code} {...props} />
},
note: ({ className, children, ...rest }: any) => {
return (
<Alert status={className} {...rest}>
<AlertIcon />
<AlertDescription>{children}</AlertDescription>
</Alert>
)
},
...uiComponents({ codeProps, noteProps }),
...components,
}
} as MarkdownComponents

return (
<ui.div
Expand All @@ -101,7 +118,7 @@ export const Markdown = forwardRef<MarkdownProps, "div">((props, ref) => {
)
})

const Code: FC<MarkdownComponentCodeProps & MarkdownOptions["code"]> = ({
const Code: FC<MarkdownComponentCodeProps & MarkdownOptions["codeProps"]> = ({
inline,
className,
children,
Expand Down
49 changes: 26 additions & 23 deletions packages/components/markdown/src/remark-ui-component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Dict } from "@yamada-ui/utils"
import { Plugin } from "unified"
import { visit } from "unist-util-visit"

export const remarkUIComponent: Plugin = () => (tree) => {
visit(tree, "paragraph", (node: any) => {
try {
const { name, attributes, children } = getValidChildren(node.children)
console.log(node)
const { name, properties, children } = getValidChildren(node.children)

switch (name) {
case "note":
insertElement({ name: "note", attributes, children })(node)
insertElement({ name: "note", properties, children })(node)
break
default:
break
Expand All @@ -18,22 +20,23 @@ export const remarkUIComponent: Plugin = () => (tree) => {

const getValidChildren = (
children: any[],
): { name: string; attributes: any[]; children: any[] } => {
): { name: string; properties: Dict; children: any[] } => {
if (!children.length) throw new Error()

if (children.length === 1) {
const { type, value } = children[0]

if (type !== "text" || !value) throw new Error()

const [, name, content] = value.match(/^:::(\w+)\s+([\s\S]*?)\s*:::$/) ?? []

if (!name || !content) throw new Error()

const { attributes, resolvedContent } = getAttributes(content)
const { properties, resolvedContent } = getProperties(content)

return {
name,
attributes,
properties,
children: [{ type: "text", value: resolvedContent }],
}
} else {
Expand All @@ -47,7 +50,7 @@ const getValidChildren = (

if (!name) throw new Error()

const { attributes, resolvedContent } = getAttributes(firstChildContent)
const { properties, resolvedContent } = getProperties(firstChildContent)

if (resolvedContent) {
children[0].value = resolvedContent
Expand All @@ -61,40 +64,40 @@ const getValidChildren = (
children.pop()
}

return { name, attributes, children }
return { name, properties, children }
}
}

const getAttributes = (
const getProperties = (
content: string = "",
): { attributes: any[]; resolvedContent: string } => {
): { properties: Dict; resolvedContent: string } => {
const properties: Dict = {}
const reg = /(\w+)=([^\s]+)(?=\s|$)/g
const attributes = [...content.matchAll(reg)].map(([, name, value]) => ({
type: "paragraph",
name,
value: value.trim(),
}))
const results = [...content.matchAll(reg)]

results.forEach(([, name, value]) => {
properties[name] = value.trim()
})

const resolvedContent = content.replace(reg, "").replace(/^\s+/, "")
return { attributes, resolvedContent }

return { properties, resolvedContent }
}
const insertElement =
({
name,
children = [],
attributes = [],
properties = {},
}: {
name: string
children: any[]
attributes?: any[]
properties?: Dict
}) =>
(node: any) => {
node.type = "custom"
node.name = name
node.data = {
hName: "note",
hName: name,
hChildren: children,
hProperties: {
className: `${attributes[0].value}`,
},
hProperties: properties,
}
}
16 changes: 2 additions & 14 deletions stories/components/data-display/markdown.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ const meta: Meta<typeof Markdown> = {
export default meta

export const basic: Story = () => {
return <Markdown>{MESSAGE}</Markdown>
return <Markdown>{README}</Markdown>
}

export const withCodeTheme: Story = () => {
return <Markdown code={{ theme: "materialDark" }}>{README}</Markdown>
return <Markdown codeProps={{ theme: "materialDark" }}>{README}</Markdown>
}

export const customComponent: Story = () => {
Expand All @@ -39,18 +39,6 @@ export const customComponent: Story = () => {
)
}

const MESSAGE = `
:::note status=info
やっほーーーー!!
:::
# あいうえお
- なんだってばよ
あっかんべー
`
const README = `
<p align="center">
<img src="https://raw.githubusercontent.com/hirotomoyamada/yamada-ui/main/logo/[email protected]" alt="Yamada UI" width="480" />
Expand Down

0 comments on commit 6dee0b0

Please sign in to comment.