From 07eb5ba82d4e69b33fe022d93698ec3d57a7bca6 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 26 Sep 2023 16:41:41 +0800 Subject: [PATCH] docs: move copy button into code previewer (#45099) --- .../builtins/Previewer/CodePreviewer.tsx | 40 ++--------- .../builtins/Previewer/DesignPreviewer.tsx | 2 +- .dumi/theme/common/CodePreview.tsx | 67 +++++++++++++++++-- 3 files changed, 65 insertions(+), 44 deletions(-) diff --git a/.dumi/theme/builtins/Previewer/CodePreviewer.tsx b/.dumi/theme/builtins/Previewer/CodePreviewer.tsx index 8af1d33912..7b4d5519cf 100644 --- a/.dumi/theme/builtins/Previewer/CodePreviewer.tsx +++ b/.dumi/theme/builtins/Previewer/CodePreviewer.tsx @@ -1,11 +1,5 @@ import React, { useContext, useEffect, useRef, useState } from 'react'; -import { - CheckOutlined, - LinkOutlined, - SnippetsOutlined, - ThunderboltOutlined, - UpOutlined, -} from '@ant-design/icons'; +import { LinkOutlined, ThunderboltOutlined, UpOutlined } from '@ant-design/icons'; import type { Project } from '@stackblitz/sdk'; import stackblitzSdk from '@stackblitz/sdk'; import { Alert, Badge, Space, Tooltip } from 'antd'; @@ -13,7 +7,6 @@ import { createStyles, css } from 'antd-style'; import classNames from 'classnames'; import { FormattedMessage, useSiteData } from 'dumi'; import LZString from 'lz-string'; -import CopyToClipboard from 'react-copy-to-clipboard'; import type { AntdPreviewerProps } from '.'; import useLocation from '../../../hooks/useLocation'; @@ -125,8 +118,6 @@ const CodePreviewer: React.FC = (props) => { const riddleIconRef = useRef(null); const codepenIconRef = useRef(null); const [codeExpand, setCodeExpand] = useState(false); - const [copyTooltipOpen, setCopyTooltipOpen] = useState(false); - const [copied, setCopied] = useState(false); const [codeType, setCodeType] = useState('tsx'); const { theme } = useContext(SiteContext); @@ -147,18 +138,6 @@ const CodePreviewer: React.FC = (props) => { track({ type: 'expand', demo }); }; - const handleCodeCopied = (demo: string) => { - setCopied(true); - track({ type: 'copy', demo }); - }; - - const onCopyTooltipOpenChange = (open: boolean) => { - setCopyTooltipOpen(open); - if (open) { - setCopied(false); - } - }; - useEffect(() => { if (asset.id === hash.slice(1)) { anchorRef.current?.click(); @@ -483,17 +462,6 @@ createRoot(document.getElementById('container')).render(); - handleCodeCopied(asset.id)}> - } - > - {React.createElement(copied && copyTooltipOpen ? CheckOutlined : SnippetsOutlined, { - className: 'code-box-code-copy code-box-code-action', - })} - - }> ); if (!style) { return; } - const styleTag = document.createElement('style'); + const styleTag = document.createElement('style') as HTMLStyleElement; styleTag.type = 'text/css'; styleTag.innerHTML = style; - styleTag['data-demo-url'] = demoUrl; + (styleTag as any)['data-demo-url'] = demoUrl; document.head.appendChild(styleTag); return () => { document.head.removeChild(styleTag); @@ -576,7 +544,7 @@ createRoot(document.getElementById('container')).render(); if (version) { return ( - + {codeBox} ); diff --git a/.dumi/theme/builtins/Previewer/DesignPreviewer.tsx b/.dumi/theme/builtins/Previewer/DesignPreviewer.tsx index c64ad7966b..d3b591aeba 100644 --- a/.dumi/theme/builtins/Previewer/DesignPreviewer.tsx +++ b/.dumi/theme/builtins/Previewer/DesignPreviewer.tsx @@ -5,7 +5,7 @@ import { CheckOutlined, SketchOutlined } from '@ant-design/icons'; import { nodeToGroup } from 'html2sketch'; import copy from 'copy-to-clipboard'; import { App } from 'antd'; -import type { AntdPreviewerProps } from '.'; +import type { AntdPreviewerProps } from './Previewer'; const useStyle = createStyles(({ token, css }) => ({ wrapper: css` diff --git a/.dumi/theme/common/CodePreview.tsx b/.dumi/theme/common/CodePreview.tsx index 827630fcc0..b58b4661d0 100644 --- a/.dumi/theme/common/CodePreview.tsx +++ b/.dumi/theme/common/CodePreview.tsx @@ -1,8 +1,43 @@ import React, { useEffect, useMemo } from 'react'; -import { Tabs } from 'antd'; +import { Tabs, Typography, Button } from 'antd'; import toReactElement from 'jsonml-to-react-element'; import JsonML from 'jsonml.js/lib/utils'; import Prism from 'prismjs'; +import { createStyles } from 'antd-style'; + +const useStyle = createStyles(({ token, css }) => { + const { colorIcon, colorBgTextHover, antCls } = token; + + return { + code: css` + position: relative; + `, + + copyButton: css` + color: ${colorIcon}; + position: absolute; + top: 0; + inset-inline-end: 16px; + width: 32px; + text-align: center; + background: ${colorBgTextHover}; + padding: 0; + `, + + copyIcon: css` + ${antCls}-typography-copy { + margin-inline-start: 0; + } + ${antCls}-typography-copy:not(${antCls}-typography-copy-success) { + color: ${colorIcon}; + + &:hover { + color: ${colorIcon}; + } + } + `, + }; +}); const LANGS = { tsx: 'TypeScript', @@ -40,7 +75,7 @@ const CodePreview: React.FC = ({ onCodeTypeChange, }) => { // 避免 Tabs 数量不稳定的闪动问题 - const initialCodes = {}; + const initialCodes = {} as Record<'tsx' | 'jsx' | 'style', string>; if (sourceCode) { initialCodes.tsx = ''; } @@ -51,7 +86,11 @@ const CodePreview: React.FC = ({ initialCodes.style = ''; } const [highlightedCodes, setHighlightedCodes] = React.useState(initialCodes); - + const sourceCodes = { + tsx: sourceCode, + jsx: jsxCode, + style: styleCode, + } as Record<'tsx' | 'jsx' | 'style', string>; useEffect(() => { const codes = { tsx: Prism.highlight(sourceCode, Prism.languages.javascript, 'jsx'), @@ -59,7 +98,7 @@ const CodePreview: React.FC = ({ style: Prism.highlight(styleCode, Prism.languages.css, 'css'), }; // 去掉空的代码类型 - Object.keys(codes).forEach((key) => { + Object.keys(codes).forEach((key: keyof typeof codes) => { if (!codes[key]) { delete codes[key]; } @@ -68,12 +107,22 @@ const CodePreview: React.FC = ({ }, [jsxCode, sourceCode, styleCode]); const langList = Object.keys(highlightedCodes); + + const { styles } = useStyle(); + const items = useMemo( () => - langList.map((lang) => ({ + langList.map((lang: keyof typeof LANGS) => ({ label: LANGS[lang], key: lang, - children: toReactComponent(['pre', { lang, highlighted: highlightedCodes[lang] }]), + children: ( +
+ {toReactComponent(['pre', { lang, highlighted: highlightedCodes[lang] }])} + +
+ ), })), [JSON.stringify(highlightedCodes)], ); @@ -85,7 +134,11 @@ const CodePreview: React.FC = ({ if (langList.length === 1) { return toReactComponent([ 'pre', - { lang: langList[0], highlighted: highlightedCodes[langList[0]], className: 'highlight' }, + { + lang: langList[0], + highlighted: highlightedCodes[langList[0] as keyof typeof LANGS], + className: 'highlight', + }, ]); }