From 36466f955e0cd25e230a95f6b7d9b52843b85320 Mon Sep 17 00:00:00 2001 From: jaywcjlove <398188662@qq.com> Date: Wed, 8 Mar 2023 00:20:53 +0800 Subject: [PATCH] feat: refactor code. --- core/README.md | 298 +++++++++++++------ core/src/Code.tsx | 21 ++ core/src/CodePreview.tsx | 64 ++++ core/src/Preview.tsx | 16 + core/src/Toolbar.tsx | 68 +++++ core/src/index.tsx | 104 +------ core/src/store.tsx | 20 ++ website/package.json | 2 +- website/src/pages/doc/index.tsx | 14 +- website/src/pages/example/index.tsx | 117 +++++--- website/src/pages/markdown-example/index.tsx | 14 +- 11 files changed, 498 insertions(+), 240 deletions(-) create mode 100644 core/src/Code.tsx create mode 100644 core/src/CodePreview.tsx create mode 100644 core/src/Preview.tsx create mode 100644 core/src/Toolbar.tsx create mode 100644 core/src/store.tsx diff --git a/core/README.md b/core/README.md index 0a2b9b6..b694423 100644 --- a/core/README.md +++ b/core/README.md @@ -15,16 +15,23 @@ npm install react-code-preview-layout --save ```jsx mdx:preview import React from "react" import CodeLayout from "react-code-preview-layout" + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;` const Demo = () => { return ( - {code}} - copyNodes={code} - > -
这里是示例展示
+ + +
这里是示例展示
+
+ Code Example + + {code} +
); } @@ -36,17 +43,23 @@ export default Demo; ```jsx mdx:preview import React from "react" import CodeLayout from "react-code-preview-layout" + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;` const Demo = () => { return ( - {code}} - copyNodes={code} - disableCheckered - > -
这里方格背景被禁用了
+ + +
这里是示例展示
+
+ Code Example + + {code} +
); } @@ -55,17 +68,25 @@ export default Demo; ## 无边框 -```jsx mdx:preview +```jsx mdx:preview import React from "react" import CodeLayout from "react-code-preview-layout" + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;` + const Demo = () => ( - {code}} - text={code} - bordered={false} - > -
示例内容
+ + +
示例内容
+
+ Code Example + + {code} +
); export default Demo; @@ -73,76 +94,112 @@ export default Demo; ## Preview Background Color -```jsx mdx:preview +```jsx mdx:preview?title=Preview Background Color import React from "react" import CodeLayout from "react-code-preview-layout" -const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;` + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + +const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;`; + const Demo = () => ( - {code}} - text={code} - bordered={false} - background="#009688b0" - > -
示例内容
+ + +
示例内容
+
+ Code Example + + {code} +
); export default Demo; ``` -## 自定义操作按钮 +## Custom Action Button -```jsx mdx:preview?title=自定义操作按钮 +```jsx mdx:preview?title=Custom Action Button import React from "react" import CodeLayout from "react-code-preview-layout" -const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;` + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + +const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;`; + const Demo = () => { return ( - {code}} - copyNodes={code} - toolbar={
按钮
} - > -
示例内容
+ + +
示例内容
+
+ 按钮}>Code Example + + {code} +
); } export default Demo; ``` -## 禁用工具栏 +## Disable Toolbar -```jsx mdx:preview?title=禁用工具栏 -import React from "react" +```jsx mdx:preview?title=Disable Toolbar +import React, { useState } from "react" import CodeLayout from "react-code-preview-layout" + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;`; -const Demo = ()=> ( - {code}} - text={code} - disableToolbar - > -
示例内容
-
-); +const Demo = ()=> { + const [visible, setVisible] = useState(true); + return ( +
+ + + +
示例内容
+
+ Code Example + + {code} + +
+
+ ) +}; export default Demo; ``` -## 禁用代码展示 +## Disable code display -```jsx mdx:preview?title=禁用代码展示 +```jsx mdx:preview?title=Disable code display import React from "react" import CodeLayout from "react-code-preview-layout" + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;`; const Demo = ()=> ( - {code}} - text={code} - disableCode - > -
示例内容
+ + +
示例内容
+
+ + Code Example +
); export default Demo; @@ -152,39 +209,46 @@ export default Demo; 可以使用 [`@uiw/react-codepen`](https://github.com/uiwjs/react-codepen) 和 [`@uiw/react-codesandbox`](https://github.com/uiwjs/react-codesandbox) 组件添加代码预览按钮,他们用于动态地将代码示例生成 [`codepen`](https://github.com/uiwjs/react-codepen) 项目和 [`codesandbox`](https://codesandbox.io/) 项目。 -```jsx mdx:preview?title=添加 Codepen 按钮 +```jsx mdx:preview?title=Disable code display import React from "react" import CodeLayout from "react-code-preview-layout" import Codepen from '@uiw/react-codepen'; -const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;` + +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + +const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;`; const codePenOptions = { title: `demo`, - includeModule: ['uiw'], + // includeModule: ['uiw'], js: `${code.replace("export default", 'const APP_render =')}\nReactDOM.createRoot(document.getElementById("container")).render()`, html: '
', css_external: `https://unpkg.com/uiw@4.21.2/dist/uiw.min.css`, js_external: `https://unpkg.com/react@18.x/umd/react.development.js;https://unpkg.com/react-dom@18.x/umd/react-dom.development.js;https://unpkg.com/classnames@2.2.6/index.js;https://unpkg.com/uiw@4.21.2/dist/uiw.min.js;https://unpkg.com/@uiw/codepen-require-polyfill@1.1.3/index.js`, } +const extra = ( + + + + + +); + const Demo = () => ( - {code} - } - text={code} - toolbarExtra={ - - - - - - } - > -
示例内容
+ + +
示例内容
+
+ + Code Example + + {code}
); export default Demo; @@ -192,32 +256,78 @@ export default Demo; ## Props +```jsx +import CodeLayout from "react-code-preview-layout" + + + +
示例内容
+
+ + Code Example + + {code} +
+``` + ```ts -interface CodeLayoutProps extends React.DetailedHTMLProps, HTMLDivElement> { +export interface CodePreviewProps extends React.HTMLAttributes { prefixCls?: string; - /** 原始 代码块 渲染**/ - code?: React.ReactNode; - text?: string; - /** 标题部分,也可以放置按钮 **/ - toolbar?: React.ReactNode; - /** 额外内容,展示 toolbar 右侧内容 */ - toolbarExtra?: React.ReactNode; - disableToolbar?: boolean; - disableCode?: boolean; - disablePreview?: boolean; - /** 禁用方格背景 */ - disableCheckered?: boolean; - codeProps?: React.DetailedHTMLProps, HTMLDivElement>; /** - * 是否需要边框 + * Whether border is required * @default true */ bordered?: boolean; + /** disable checkered */ + disableCheckered?: boolean; +} +declare type CodePreviewComponent = React.FC> & { + Preview: typeof Preview; + Code: typeof Code; + Toolbar: typeof Toolbar; +}; +``` + +`` + +```typescript +export interface PreviewProps extends React.HTMLAttributes { +} +``` + +`` + +```typescript +export interface CodeProps extends React.HTMLAttributes { } +``` + +`` + +```typescript +export interface ToolbarProps extends React.HTMLAttributes { + extra?: React.ReactNode; /** - * 是否显示复制按钮 + * Display cope button * @default true */ copied?: boolean; + /** + * Collapse code display? + * @default true + */ + collapse?: boolean; + /** + * Display Toolbar? + * @default true + */ + visible?: boolean; + /** + * Show button or not + * @default true + */ + visibleButton?: boolean; + /** Code to be copied */ + text?: string; } ``` diff --git a/core/src/Code.tsx b/core/src/Code.tsx new file mode 100644 index 0000000..dff542d --- /dev/null +++ b/core/src/Code.tsx @@ -0,0 +1,21 @@ +import React, { useContext } from 'react'; +import { Context } from './store'; +import { CODE_PREVIEW_PREFIX } from './CodePreview'; + +export interface CodeProps extends React.HTMLAttributes {} + +export const Code = React.forwardRef((props, ref) => { + const { className, children, ...htmlProps } = props; + const cls = [`${CODE_PREVIEW_PREFIX}-code`, className].filter(Boolean).join(' ').trim(); + const store = useContext(Context); + if (store.collapse) { + return null; + } + return ( +
+ {children} +
+ ); +}); + +Code.displayName = 'uiw.CodeLayoutCode'; diff --git a/core/src/CodePreview.tsx b/core/src/CodePreview.tsx new file mode 100644 index 0000000..3aea3c8 --- /dev/null +++ b/core/src/CodePreview.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { Code } from './Code'; +import { Preview } from './Preview'; +import { Toolbar } from './Toolbar'; +import { Provider } from './store'; +import './styles.css'; + +export const CODE_PREVIEW_PREFIX = 'w-rcpl'; + +export interface CodePreviewProps extends React.HTMLAttributes { + prefixCls?: string; + /** + * Whether border is required + * @default true + */ + bordered?: boolean; + /** disable checkered */ + disableCheckered?: boolean; +} + +const Internal = React.forwardRef((props, ref) => { + const { + children, + prefixCls = CODE_PREVIEW_PREFIX, + className, + bordered = true, + disableCheckered, + ...divProps + } = props; + const cls = [prefixCls, className, bordered ? `w-bordered` : null, disableCheckered ? `w-disable-checkered` : null] + .filter(Boolean) + .join(' ') + .trim(); + return ( +
+ {React.Children.map(children, (child: React.ReactNode, key) => { + if (!React.isValidElement(child)) return child; + return React.cloneElement(child, { ...child.props, key }); + })} +
+ ); +}); + +const InternalCodePreview = (props: CodePreviewProps, ref?: React.ForwardedRef) => { + return ( + + + + ); +}; + +type CodePreviewComponent = React.FC> & { + Preview: typeof Preview; + Code: typeof Code; + Toolbar: typeof Toolbar; +}; + +export const CodePreview: CodePreviewComponent = React.forwardRef( + InternalCodePreview, +) as unknown as CodePreviewComponent; + +CodePreview.Preview = Preview; +CodePreview.Toolbar = Toolbar; +CodePreview.Code = Code; diff --git a/core/src/Preview.tsx b/core/src/Preview.tsx new file mode 100644 index 0000000..3dd0eb3 --- /dev/null +++ b/core/src/Preview.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { CODE_PREVIEW_PREFIX } from './CodePreview'; + +export interface PreviewProps extends React.HTMLAttributes {} + +export const Preview = React.forwardRef((props, ref) => { + const { className, children, ...htmlProps } = props; + const cls = [`${CODE_PREVIEW_PREFIX}-preview`, className].filter(Boolean).join(' ').trim(); + return ( +
+ {children} +
+ ); +}); + +Preview.displayName = 'uiw.Preview'; diff --git a/core/src/Toolbar.tsx b/core/src/Toolbar.tsx new file mode 100644 index 0000000..597dafa --- /dev/null +++ b/core/src/Toolbar.tsx @@ -0,0 +1,68 @@ +import React, { useContext, useEffect } from 'react'; +import { CODE_PREVIEW_PREFIX } from './CodePreview'; +import { Copied } from './Copied'; +import { ExpandIcon } from './icons'; +import { Context } from './store'; + +export interface ToolbarProps extends React.HTMLAttributes { + extra?: React.ReactNode; + /** + * Display cope button + * @default true + */ + copied?: boolean; + /** + * Collapse code display? + * @default true + */ + collapse?: boolean; + /** + * Display Toolbar? + * @default true + */ + visible?: boolean; + /** + * Show button or not + * @default true + */ + visibleButton?: boolean; + /** Code to be copied */ + text?: string; +} + +export const Toolbar = React.forwardRef((props, ref) => { + const { + className, + children, + extra, + text = '', + copied = true, + collapse = true, + visibleButton = true, + visible = true, + ...htmlProps + } = props; + const store = useContext(Context); + const cls = [`${CODE_PREVIEW_PREFIX}-toolbar`, className].filter(Boolean).join(' ').trim(); + useEffect(() => store.dispatch!({ collapse }), [collapse]); + if (!visible) { + return null; + } + const handleClick = () => store.dispatch!({ collapse: !store.collapse }); + return ( +
+
{children}
+
+ {extra} + {copied && } + {visibleButton && ( + + )} +
+
+ ); +}); + +Toolbar.displayName = 'uiw.Toolbar'; diff --git a/core/src/index.tsx b/core/src/index.tsx index 19f5e26..5e22161 100644 --- a/core/src/index.tsx +++ b/core/src/index.tsx @@ -1,102 +1,4 @@ -import { forwardRef, useState } from 'react'; -import { Copied } from './Copied'; -import { ExpandIcon } from './icons'; -import './styles.css'; +import { CodePreview } from './CodePreview'; +export * from './CodePreview'; -export interface CodeLayoutProps extends React.DetailedHTMLProps, HTMLDivElement> { - prefixCls?: string; - /** 原始 代码块 渲染 **/ - code?: React.ReactNode; - text?: string; - /** Title section, you can also place buttons **/ - toolbar?: React.ReactNode; - /** 额外内容,展示 toolbar 右侧内容 */ - toolbarExtra?: React.ReactNode; - disableToolbar?: boolean; - disableCode?: boolean; - disablePreview?: boolean; - /** 禁用方格背景 */ - disableCheckered?: boolean; - /** Configure the preview background color. */ - background?: string; - codeProps?: React.DetailedHTMLProps, HTMLDivElement>; - /** - * 是否需要边框 - * @default true - */ - bordered?: boolean; - /** - * 是否显示复制按钮 - * @default true - */ - copied?: boolean; -} - -/** react-code-preview-layout 缩写 */ -const PRE_FIX = 'w-rcpl'; - -const CodeLayout = forwardRef((props, ref) => { - const [showCode, setShowCode] = useState(false); - const { - children, - toolbar, - bordered = true, - disableCheckered = false, - disablePreview = false, - disableCode = false, - disableToolbar = false, - text = '', - background = '', - copied = true, - toolbarExtra, - code, - prefixCls = PRE_FIX, - className, - codeProps, - ...other - } = props; - const cls = [prefixCls, className, bordered ? `w-bordered` : null, disableCheckered ? `w-disable-checkered` : null] - .filter(Boolean) - .join(' ') - .trim(); - - const style: React.CSSProperties = !background - ? {} - : { - backgroundColor: background, - backgroundImage: 'none', - }; - return ( -
- {!disablePreview && ( -
- {children} -
- )} - {!disableToolbar && ( -
-
{toolbar}
-
- {toolbarExtra} - {copied && } - {!disableCode && ( - - )} -
-
- )} - {!disableCode && !disableToolbar && ( -
- {code} -
- )} -
- ); -}); - -export default CodeLayout; +export default CodePreview; diff --git a/core/src/store.tsx b/core/src/store.tsx new file mode 100644 index 0000000..fb38ee2 --- /dev/null +++ b/core/src/store.tsx @@ -0,0 +1,20 @@ +import { useReducer, createContext } from 'react'; + +export interface InitialState { + collapse?: boolean; +} + +interface ContextValue extends InitialState { + dispatch?: React.Dispatch; +} +export const initialState: InitialState = {}; +export const Context = createContext({}); + +export const reducer = (state: InitialState, action: InitialState): InitialState => { + return { ...state, ...action }; +}; + +export const Provider: React.FC> = ({ children, value }) => { + const [state, dispatch] = useReducer(reducer, { ...initialState, ...value }); + return {children}; +}; diff --git a/website/package.json b/website/package.json index 6b5f032..eec852c 100644 --- a/website/package.json +++ b/website/package.json @@ -25,7 +25,7 @@ "@uiw/react-github-corners": "^1.5.14", "@uiw/react-markdown-preview": "^4.1.0", "@wcj/dark-mode": "^1.0.14", - "react-code-preview-layout": "2.0.2", + "react-code-preview-layout": "2.1.1", "react-router-dom": "^6.3.0", "uiw": "^4.21.6" }, diff --git a/website/src/pages/doc/index.tsx b/website/src/pages/doc/index.tsx index 032c0f4..c6d8dff 100644 --- a/website/src/pages/doc/index.tsx +++ b/website/src/pages/doc/index.tsx @@ -3,6 +3,10 @@ import CodeLayout from 'react-code-preview-layout'; import MarkdownPreview from '@uiw/react-markdown-preview'; import { getMetaId, isMeta, getURLParameters } from 'markdown-react-code-preview-loader'; +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const Doc = () => ( ( const code = data.data[metaId].value || ''; const param = getURLParameters(meta); return ( - } text={code}> - + + + + + {param.title || 'Code Example'} + + + ); } diff --git a/website/src/pages/example/index.tsx b/website/src/pages/example/index.tsx index 4f9d7c8..5f475ec 100644 --- a/website/src/pages/example/index.tsx +++ b/website/src/pages/example/index.tsx @@ -4,11 +4,14 @@ import CodeSandbox from '@uiw/react-codesandbox'; import Codepen from '@uiw/react-codepen'; import { Button } from 'uiw'; +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + const code = `import React from "react";\nimport { Button } from "uiw";\nconst Demo = ()=>{\n return
\n};\nexport default Demo;`; const codePenOptions = { title: `demo`, - includeModule: ['uiw'], js: `${code.replace( 'export default', 'const APP_render =', @@ -47,70 +50,104 @@ const App = () => {
基础 - {code}} text={code}> - + + + + + 示例 + +
{code}
+
禁用方格背景 - {code}} text={code} disableCheckered> - + + + + + 示例 + +
{code}
+
无边框 - {code}} text={code} bordered={false}> - + + + + + 示例 + +
{code}
+
第三方预览按钮 - {code}} - text={code} - toolbarExtra={ - - - - - - - - - - - - - } - > - + + + + + + + + + + + + + + + + + } + > + 示例 + + +
{code}
+
自定义操作按钮 - {code}} - text={code} - toolbarExtra={} - > - + + + + + 操作按钮}> + 示例 + + +
{code}
+
禁用工具栏 - {code}} text={code} disableToolbar> - + + + + + +
{code}
+
diff --git a/website/src/pages/markdown-example/index.tsx b/website/src/pages/markdown-example/index.tsx index 589c6c2..82aa4e3 100644 --- a/website/src/pages/markdown-example/index.tsx +++ b/website/src/pages/markdown-example/index.tsx @@ -4,6 +4,10 @@ import { getCodeString } from 'rehype-rewrite'; import { getMetaId, isMeta, getURLParameters } from 'markdown-react-code-preview-loader'; import data from './README.md'; +const Preview = CodeLayout.Preview; +const Code = CodeLayout.Code; +const Toolbar = CodeLayout.Toolbar; + export default function MarkdownExample() { return ( } text={code}> - + + + + + {param.title || 'Code Example'} + + + ); }