From 641cb3787b69ae96359ca49462feac77c783c70a Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 5 Jul 2023 14:52:01 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=9C=A8codepen?= =?UTF-8?q?=E4=B8=AD=E6=89=93=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/theme-api/index.ts | 1 + src/client/theme-api/openCodePen.ts | 105 ++++++++++++++++++ src/client/theme-default/locales/en-US.json | 2 +- src/client/theme-default/locales/zh-CN.json | 1 + .../slots/PreviewerActions/index.tsx | 22 +++- 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/client/theme-api/openCodePen.ts diff --git a/src/client/theme-api/index.ts b/src/client/theme-api/index.ts index 2f2e0066c3..a7861af38d 100644 --- a/src/client/theme-api/index.ts +++ b/src/client/theme-api/index.ts @@ -23,6 +23,7 @@ export { useSiteData } from './context'; export { DumiDemo } from './DumiDemo'; export { DumiDemoGrid } from './DumiDemoGrid'; export { DumiPage } from './DumiPage'; +export { openCodePen } from './openCodePen'; export { openCodeSandbox } from './openCodeSandbox'; export { openStackBlitz } from './openStackBlitz'; export type { IPreviewerProps } from './types'; diff --git a/src/client/theme-api/openCodePen.ts b/src/client/theme-api/openCodePen.ts new file mode 100644 index 0000000000..f5e182d01f --- /dev/null +++ b/src/client/theme-api/openCodePen.ts @@ -0,0 +1,105 @@ +import type { IPreviewerProps } from 'dumi'; + +const CODEPEN_API = 'https://codepen.io/pen/define'; + +const getDependencyData = (opts: IPreviewerProps) => { + const isTSX = Boolean(opts.asset.dependencies?.['index.tsx']); + let dep: Record = {}; + let jsx = ''; + Object.keys(opts.asset.dependencies).forEach((current) => { + const { type, value } = opts.asset.dependencies[current]; + if (type === 'NPM') { + dep[current] = value; + } else { + jsx += value; + } + }); + + /** + * 依赖的 npm 包, 将所有的npm 包转成cdn 引入 + */ + const dependencyNpm = Object.keys(dep).map((name) => { + return `${name}@${dep[name]}/dist/${name}.min.js`; + }); + + /** codepen + * 需要替换成 const { Space } = antd + * */ + const codepenPrefillConfig = { + title: `例子`, + html: ` + + + + + + + +
+ + + `, + js: `const { createRoot } = ReactDOM;\n${jsx + .replace( + /import\s+(?:React,\s+)?{(\s+[^}]*\s+)}\s+from\s+'react'/, + `const { $1 } = React;`, + ) + .replace( + /import\s+{(\s+[^}]*\s+)}\s+from\s+'([\w\s]+)'/g, + 'const { $1 } = $2;', + ) + .replace( + /import\s+{(\s+[^}]*\s+)}\s+from\s+'@ant-design\/icons';/, + 'const { $1 } = icons;', + ) + .replace(/import\s+([\w]+)\s+from\s+'lodash'/, '') + .replace("import moment from 'moment';", '') + .replace("import React from 'react';", '') + .replace( + /import\s+{\s+(.*)\s+}\s+from\s+'react-router';/, + 'const { $1 } = ReactRouter;', + ) + .replace(/([A-Za-z]*)\s+as\s+([A-Za-z]*)/, '$1:$2') + .replace( + /export default/, + 'const ComponentDemo =', + )}\n\ncreateRoot(mountNode).render();\n`, + editors: '001', + css: '', + js_external: [ + 'react@18/umd/react.development.js', + 'react-dom@18/umd/react-dom.development.js', + 'dayjs@1/dayjs.min.js', + `@ant-design/icons/dist/index.umd.js`, + 'react-router-dom/dist/umd/react-router-dom.production.min.js', + 'react-router/dist/umd/react-router.production.min.js', + 'lodash@4.17.21/lodash.js', + ] + .concat(dependencyNpm) + .map((url) => `https://unpkg.com/${url}`) + .join(';'), + js_pre_processor: isTSX ? 'typescript' : 'javascript', + }; + return codepenPrefillConfig; +}; + +export const openCodePen = (data: IPreviewerProps, opts?: { api?: string }) => { + const form = document.createElement('form'); + const input = document.createElement('input'); + const CSBData = getDependencyData(data); + + form.method = 'POST'; + form.target = '_blank'; + form.style.display = 'none'; + form.action = opts?.api || CODEPEN_API; + form.appendChild(input); + form.setAttribute('data-demo', data.assets?.id || ''); + + input.name = 'data'; + input.value = JSON.stringify(CSBData); + + document.body.appendChild(form); + + form.submit(); + form.remove(); +}; diff --git a/src/client/theme-default/locales/en-US.json b/src/client/theme-default/locales/en-US.json index d65698bbc8..f36f749eb7 100644 --- a/src/client/theme-default/locales/en-US.json +++ b/src/client/theme-default/locales/en-US.json @@ -19,7 +19,7 @@ "previewer.actions.sketch.divider": "------------------------", "previewer.actions.sketch.guide": "How to paste to Sketch?", "previewer.actions.codesandbox": "Open in CodeSandbox", - "previewer.actions.codepen": "Open in CodePen (Not implemented)", + "previewer.actions.codepen": "Open in CodePen", "previewer.actions.stackblitz": "Open in StackBlitz", "previewer.actions.separate": "Open in separate page", "404.title": "PAGE NOT FOUND", diff --git a/src/client/theme-default/locales/zh-CN.json b/src/client/theme-default/locales/zh-CN.json index 421352e465..27b4603685 100644 --- a/src/client/theme-default/locales/zh-CN.json +++ b/src/client/theme-default/locales/zh-CN.json @@ -19,6 +19,7 @@ "previewer.actions.sketch.symbol": "拷贝为 Sketch Symbol", "previewer.actions.sketch.divider": "----------------------", "previewer.actions.sketch.guide": "如何粘贴到 SKetch?", + "previewer.actions.codepen": "在 CodePen 中打开", "previewer.actions.stackblitz": "在 StackBlitz 中打开", "previewer.actions.separate": "在独立页面中打开", "404.title": "页面未找到", diff --git a/src/client/theme-default/slots/PreviewerActions/index.tsx b/src/client/theme-default/slots/PreviewerActions/index.tsx index e08d4ef142..b600422fcf 100644 --- a/src/client/theme-default/slots/PreviewerActions/index.tsx +++ b/src/client/theme-default/slots/PreviewerActions/index.tsx @@ -1,10 +1,12 @@ import { ReactComponent as IconCheck } from '@ant-design/icons-svg/inline-svg/outlined/check.svg'; import { ReactComponent as IconCodeSandbox } from '@ant-design/icons-svg/inline-svg/outlined/code-sandbox.svg'; +import { ReactComponent as IconCodePen } from '@ant-design/icons-svg/inline-svg/outlined/codepen.svg'; import { ReactComponent as IconSketch } from '@ant-design/icons-svg/inline-svg/outlined/sketch.svg'; import { ReactComponent as IconStackBlitz } from '@ant-design/icons-svg/inline-svg/outlined/thunderbolt.svg'; import copy from 'copy-to-clipboard'; import { getSketchJSON, + openCodePen, openCodeSandbox, openStackBlitz, useIntl, @@ -19,7 +21,13 @@ export interface IPreviewerActionsProps extends IPreviewerProps { /** * disabled actions */ - disabledActions?: ('CSB' | 'STACKBLITZ' | 'EXTERNAL' | 'HTML2SKETCH')[]; + disabledActions?: ( + | 'CSB' + | 'STACKBLITZ' + | 'EXTERNAL' + | 'HTML2SKETCH' + | 'CODEPEN' + )[]; extra?: ReactNode; forceShowCode?: boolean; demoContainer: HTMLDivElement | HTMLIFrameElement; @@ -73,6 +81,18 @@ const PreviewerActions: FC = (props) => { )} + {!props.disabledActions?.includes('CODEPEN') && ( + + )} {!props.disabledActions?.includes('STACKBLITZ') && (