Skip to content

Commit

Permalink
feat: refactor code.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaywcjlove committed Mar 7, 2023
1 parent 3d7e0a1 commit 36466f9
Show file tree
Hide file tree
Showing 11 changed files with 498 additions and 240 deletions.
298 changes: 204 additions & 94 deletions core/README.md

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions core/src/Code.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement> {}

export const Code = React.forwardRef<HTMLDivElement, CodeProps>((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 (
<div {...htmlProps} className={cls} ref={ref}>
{children}
</div>
);
});

Code.displayName = 'uiw.CodeLayoutCode';
64 changes: 64 additions & 0 deletions core/src/CodePreview.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement> {
prefixCls?: string;
/**
* Whether border is required
* @default true
*/
bordered?: boolean;
/** disable checkered */
disableCheckered?: boolean;
}

const Internal = React.forwardRef<HTMLDivElement, CodePreviewProps>((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 (
<div ref={ref} {...divProps} className={cls}>
{React.Children.map(children, (child: React.ReactNode, key) => {
if (!React.isValidElement(child)) return child;
return React.cloneElement(child, { ...child.props, key });
})}
</div>
);
});

const InternalCodePreview = (props: CodePreviewProps, ref?: React.ForwardedRef<HTMLDivElement>) => {
return (
<Provider value={{ collapse: false }}>
<Internal {...props} ref={ref} />
</Provider>
);
};

type CodePreviewComponent = React.FC<React.PropsWithRef<CodePreviewProps>> & {
Preview: typeof Preview;
Code: typeof Code;
Toolbar: typeof Toolbar;
};

export const CodePreview: CodePreviewComponent = React.forwardRef<HTMLDivElement>(
InternalCodePreview,
) as unknown as CodePreviewComponent;

CodePreview.Preview = Preview;
CodePreview.Toolbar = Toolbar;
CodePreview.Code = Code;
16 changes: 16 additions & 0 deletions core/src/Preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { CODE_PREVIEW_PREFIX } from './CodePreview';

export interface PreviewProps extends React.HTMLAttributes<HTMLDivElement> {}

export const Preview = React.forwardRef<HTMLDivElement, PreviewProps>((props, ref) => {
const { className, children, ...htmlProps } = props;
const cls = [`${CODE_PREVIEW_PREFIX}-preview`, className].filter(Boolean).join(' ').trim();
return (
<div {...htmlProps} className={cls} ref={ref}>
{children}
</div>
);
});

Preview.displayName = 'uiw.Preview';
68 changes: 68 additions & 0 deletions core/src/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement> {
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<HTMLDivElement, ToolbarProps>((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 (
<div className={cls} {...htmlProps} ref={ref}>
<div className={`${CODE_PREVIEW_PREFIX}-title`}>{children}</div>
<div className={`${CODE_PREVIEW_PREFIX}-extra`}>
{extra}
{copied && <Copied text={text} />}
{visibleButton && (
<button onClick={handleClick}>
<ExpandIcon />
</button>
)}
</div>
</div>
);
});

Toolbar.displayName = 'uiw.Toolbar';
104 changes: 3 additions & 101 deletions core/src/index.tsx
Original file line number Diff line number Diff line change
@@ -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<React.HTMLAttributes<HTMLDivElement>, 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<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
/**
* 是否需要边框
* @default true
*/
bordered?: boolean;
/**
* 是否显示复制按钮
* @default true
*/
copied?: boolean;
}

/** react-code-preview-layout 缩写 */
const PRE_FIX = 'w-rcpl';

const CodeLayout = forwardRef<HTMLDivElement, CodeLayoutProps>((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 (
<div ref={ref} {...other} className={cls}>
{!disablePreview && (
<div className={`${prefixCls}-preview`} style={style}>
{children}
</div>
)}
{!disableToolbar && (
<div className={`${prefixCls}-toolbar`}>
<div className={`${prefixCls}-title`}>{toolbar}</div>
<div className={`${prefixCls}-extra`}>
{toolbarExtra}
{copied && <Copied text={text} />}
{!disableCode && (
<button onClick={() => setShowCode(!showCode)}>
<ExpandIcon />
</button>
)}
</div>
</div>
)}
{!disableCode && !disableToolbar && (
<div
{...codeProps}
className={`${prefixCls}-code ${codeProps?.className || ''} ${showCode ? 'w-display' : 'w-hidden'}`}
>
{code}
</div>
)}
</div>
);
});

export default CodeLayout;
export default CodePreview;
20 changes: 20 additions & 0 deletions core/src/store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useReducer, createContext } from 'react';

export interface InitialState {
collapse?: boolean;
}

interface ContextValue extends InitialState {
dispatch?: React.Dispatch<InitialState>;
}
export const initialState: InitialState = {};
export const Context = createContext<ContextValue>({});

export const reducer = (state: InitialState, action: InitialState): InitialState => {
return { ...state, ...action };
};

export const Provider: React.FC<React.PropsWithChildren<{ value?: InitialState }>> = ({ children, value }) => {
const [state, dispatch] = useReducer(reducer, { ...initialState, ...value });
return <Context.Provider value={{ ...state, dispatch }}>{children}</Context.Provider>;
};
2 changes: 1 addition & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
14 changes: 12 additions & 2 deletions website/src/pages/doc/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => (
<MarkdownPreview
disableCopy={true}
Expand All @@ -20,8 +24,14 @@ const Doc = () => (
const code = data.data[metaId].value || '';
const param = getURLParameters(meta);
return (
<CodeLayout toolbar={param.title || '示例展示'} code={<code {...rest} />} text={code}>
<Child />
<CodeLayout>
<Preview>
<Child />
</Preview>
<Toolbar text={code}>{param.title || 'Code Example'}</Toolbar>
<Code>
<code {...rest} />
</Code>
</CodeLayout>
);
}
Expand Down
Loading

0 comments on commit 36466f9

Please sign in to comment.