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 = () => {
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'}
+
+
+
);
}