Forráskód Böngészése

feat: add typograph copy render (#2408)

* feat: custom render copy button

* chore: update declaration format

---------

Co-authored-by: pointhalo <[email protected]>
泷涯 1 éve
szülő
commit
765161d6b5

+ 14 - 1
content/basic/typography/index-en-US.md

@@ -292,10 +292,11 @@ Copying of text can be supported by configuring the `copyable` property.
 When copyable is configured as true, the default copied content is children itself. Note that at this time, children only support string type.    
 When copyable is configured as object, you can specify the content copied to the clipboard through `copyable.content`, which is no longer strongly associated with children.   
 At this time, children will no longer limit the type, but `copyable.content` still needs to be a string.  
+You can use the `copyable.render` attribute to customize the copyable button render.
 
 ```jsx live=true
 import React from 'react';
-import { Typography, TextArea } from '@douyinfe/semi-ui';
+import { Typography, TextArea, Button } from '@douyinfe/semi-ui';
 import { IconSetting } from '@douyinfe/semi-icons';
 
 function Demo() {
@@ -308,6 +309,18 @@ function Demo() {
             <Paragraph copyable={{ onCopy: () => Toast.success({ content: 'Successfully copied.' }) }}>Click the right icon to copy.</Paragraph>
             Timestamp: <Numeral truncate="ceil" copyable underline>{new Date().getTime()/1000}s</Numeral>
             <Paragraph copyable={{ icon: <IconSetting style={{ color: 'var(--semi-color-link)' }}/> }}>Custom Copy Node</Paragraph>
+            <Paragraph copyable={{
+                content: 'Custom render!',
+                render: (copied, doCopy, config) => {
+                    return (
+                        <Button size="small" onClick={doCopy}>
+                            <span>{copied ? 'Copy success' : `Click to copy: ${config.content}`}</span>
+                        </Button>
+                    );
+                }
+            }}>
+                Custom Copy Render
+            </Paragraph>
             <br/>
             <br/>
             <Text type="secondary">Paste here: </Text>

+ 14 - 1
content/basic/typography/index.md

@@ -280,10 +280,11 @@ function Demo() {
 可通过配置 copyable 属性支持文本的复制。  
 当 copyable 配置为 true时,默认复制内容为 children 本身,注意,此时 children 只支持 string类型传入    
 当 copyable 配置为 object 时,可通过 `copyable.content` 指定复制至粘贴板的内容,与 children 不再强关联, 此时 children 将不再限定类型,但 `copyable.content` 仍需要为 string    
+可以通过 `copyable.render` 属性,自定义复制按钮的渲染逻辑
 
 ```jsx live=true
 import React from 'react';
-import { Typography, TextArea } from '@douyinfe/semi-ui';
+import { Typography, TextArea, Button } from '@douyinfe/semi-ui';
 import { IconSetting } from '@douyinfe/semi-icons';
 
 function Demo() {
@@ -296,6 +297,18 @@ function Demo() {
             <Paragraph copyable={{ onCopy: () => Toast.success({ content: '复制文本成功' }) }}>点击右边的图标复制文本。</Paragraph>
             时间戳: <Numeral truncate="ceil" copyable underline>{new Date().getTime()/1000}s</Numeral>
             <Paragraph copyable={{ icon: <IconSetting style={{ color: 'var(--semi-color-link)' }}/> }}>自定义复制节点</Paragraph>
+            <Paragraph copyable={{
+                content: 'Custom render!',
+                render: (copied, doCopy, config) => {
+                    return (
+                        <Button size="small" onClick={doCopy}>
+                            <span>{copied ? '复制成功' : `点击复制:${config.content}`}</span>
+                        </Button>
+                    );
+                }
+            }}>
+                自定义复制渲染
+            </Paragraph>
             <br/>
             <br/>
             <Text type="secondary">粘贴区域:</Text>

+ 35 - 0
packages/semi-ui/typography/__test__/typography.test.js

@@ -124,4 +124,39 @@ describe(`Typography`, () => {
         expect(typographyParagraph.find('.semi-typography').children().at(0).text()).toEqual('Key: code');
     });
 
+    it('custom copy render', () => {
+        const { Text } = Typography;
+        const code = 'code';
+
+        const typographyParagraph = mount(
+            <Text
+                style={{ marginTop: 6, color: 'var(--semi-color-text-2)' }}
+                ellipsis={{ showTooltip: { opts: { style: { wordBreak: 'break-word' } } } }}
+                copyable={{
+                    content: code,
+                    render: (copied, doCopy, config) => {
+                        return (
+                            <span className="test-copy-button" onClick={doCopy}>
+                                <span className="test-copied">{String(copied)}</span>
+                                <span className="test-copy-content">{config.content}</span>
+                            </span>
+                        );
+                    }
+                }}
+            >
+                Key: {code}
+            </Text>
+        );
+
+        // test basic render
+        expect(typographyParagraph.find('.test-copied').text()).toEqual('false');
+        expect(typographyParagraph.find('.test-copy-content').text()).toEqual(code);
+
+        // test copy
+        const trigger = typographyParagraph.find('.test-copy-button');
+        expect(trigger.length).toEqual(1);
+        trigger.at(0).simulate('click');
+        expect(typographyParagraph.find('.test-copied').text()).toEqual('true');
+    });
+
 });

+ 8 - 2
packages/semi-ui/typography/copyable.tsx

@@ -10,6 +10,7 @@ import { IconCopy, IconTick } from '@douyinfe/semi-icons';
 import { BaseProps } from '../_base/baseComponent';
 import { Locale } from '../locale/interface';
 import isEnterPress from '@douyinfe/semi-foundation/utils/isEnterPress';
+import { CopyableConfig } from './title';
 
 const prefixCls = cssClasses.PREFIX;
 
@@ -20,7 +21,8 @@ export interface CopyableProps extends BaseProps {
     forwardRef?: React.RefObject<any>;
     successTip?: React.ReactNode;
     icon?: React.ReactNode;
-    onCopy?: (e: React.MouseEvent, content: string, res: boolean) => void
+    onCopy?: (e: React.MouseEvent, content: string, res: boolean) => void;
+    render?: (copied: boolean, doCopy: (e: React.MouseEvent) => void, configs: CopyableConfig) => React.ReactNode
 }
 interface CopyableState {
     copied: boolean;
@@ -133,13 +135,17 @@ export class Copyable extends React.PureComponent<CopyableProps, CopyableState>
     }
 
     render() {
-        const { style, className, forwardRef, copyTip } = this.props;
+        const { style, className, forwardRef, copyTip, render } = this.props;
         const { copied } = this.state;
         const finalCls = cls(className, {
             [`${prefixCls}-action-copy`]: !copied,
             [`${prefixCls}-action-copied`]: copied,
         });
 
+        if (render) {
+            return render(copied, this.copy, this.props);
+        }
+
         return (
             <LocaleConsumer componentName="Typography">
                 {(locale: Locale['Typography']) => (

+ 3 - 1
packages/semi-ui/typography/title.tsx

@@ -13,7 +13,9 @@ export interface CopyableConfig {
     successTip?: React.ReactNode;
     icon?: React.ReactNode;
 
-    onCopy?(e: React.MouseEvent, content: string, res: boolean): void
+    onCopy?: (e: React.MouseEvent, content: string, res: boolean) => void;
+
+    render?: (copied: boolean, doCopy: (e: React.MouseEvent) => void, configs: CopyableConfig) => React.ReactNode
 }
 
 export type LinkType = React.AnchorHTMLAttributes<HTMLAnchorElement> | boolean;