-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #97 from ant-design/feat/Snippet
✨ feat: new Components Snippet
- Loading branch information
Showing
17 changed files
with
701 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Snippet } from '@ant-design/pro-editor'; | ||
|
||
export default () => { | ||
return <Snippet language="sh">pnpm install @ant-design/pro-chat</Snippet>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
--- | ||
nav: 组件 | ||
group: Content | ||
title: Snippet | ||
description: The Snippet component is used to display a code snippet with syntax highlighting. It can be customized with a symbol before the content and a language for syntax highlighting. The component is also copyable with a CopyButton included by default. | ||
--- | ||
|
||
## Default | ||
|
||
<code src="./demos/index.tsx" nopadding></code> | ||
|
||
## APIs | ||
|
||
<API></API> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import HighLighter from '@/Highlight/components/HighLighter'; | ||
import CopyButton from '@/components/CopyButton'; | ||
import Spotlight from '@/components/Spotlight'; | ||
import { useThemeMode } from 'antd-style'; | ||
import { memo } from 'react'; | ||
import { DivProps } from 'react-layout-kit'; | ||
import { getPrefixCls } from '..'; | ||
import { useStyles } from './style'; | ||
|
||
export interface SnippetProps extends DivProps { | ||
/** | ||
* @description The content to be displayed inside the Snippet component | ||
*/ | ||
children: string; | ||
/** | ||
* @description Whether the Snippet component is copyable or not | ||
* @default true | ||
*/ | ||
copyable?: boolean; | ||
/** | ||
* @description The language of the content inside the Snippet component | ||
* @default 'tsx' | ||
*/ | ||
language?: string; | ||
/** | ||
* @description Whether add spotlight background | ||
* @default false | ||
*/ | ||
spotlight?: boolean; | ||
/** | ||
* @description The symbol to be displayed before the content inside the Snippet component | ||
*/ | ||
symbol?: string; | ||
/** | ||
* @description The type of the Snippet component | ||
* @default 'ghost' | ||
*/ | ||
type?: 'ghost' | 'block'; | ||
|
||
prefixCls?: string; | ||
} | ||
|
||
const Snippet = memo<SnippetProps>((props) => { | ||
const { | ||
symbol = '$', | ||
language = 'tsx', | ||
children, | ||
copyable = true, | ||
prefixCls: customPrefixCls, | ||
type = 'ghost', | ||
spotlight, | ||
className, | ||
...rest | ||
} = props; | ||
const prefixCls = getPrefixCls('snippet', customPrefixCls); | ||
const { isDarkMode } = useThemeMode(); | ||
|
||
const { styles, cx } = useStyles({ | ||
type, | ||
prefixCls, | ||
}); | ||
return ( | ||
<div className={cx(styles.container, className)} {...rest}> | ||
{spotlight && <Spotlight />} | ||
<HighLighter language={language} prefixCls={prefixCls} theme={isDarkMode ? 'dark' : 'light'}> | ||
{[symbol, children].filter(Boolean).join(' ')} | ||
</HighLighter> | ||
{copyable && <CopyButton content={children} />} | ||
</div> | ||
); | ||
}); | ||
|
||
export { Snippet }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { createStyles } from 'antd-style'; | ||
|
||
export const useStyles = createStyles(({ css, cx, token }, { type, prefixCls }) => { | ||
const typeStylish = css` | ||
background-color: ${type === 'block' ? token.colorFillTertiary : 'transparent'}; | ||
border: 1px solid ${type === 'block' ? 'transparent' : token.colorBorder}; | ||
`; | ||
|
||
return { | ||
container: cx( | ||
`${prefixCls}-container`, | ||
typeStylish, | ||
css` | ||
position: relative; | ||
overflow: hidden; | ||
display: inline-flex; | ||
gap: 8px; | ||
align-items: center; | ||
max-width: 100%; | ||
height: 38px; | ||
padding: 0 8px 0 12px; | ||
border-radius: ${token.borderRadius}px; | ||
transition: background-color 100ms ${token.motionEaseOut}; | ||
&:hover { | ||
background-color: ${token.colorFillTertiary}; | ||
} | ||
.${prefixCls}-shiki { | ||
position: relative; | ||
overflow: hidden; | ||
flex: 1; | ||
} | ||
pre { | ||
overflow-x: auto !important; | ||
overflow-y: hidden !important; | ||
display: flex; | ||
align-items: center; | ||
width: 100%; | ||
height: 36px !important; | ||
margin: 0 !important; | ||
line-height: 1; | ||
background: none !important; | ||
} | ||
code[class*='language-'] { | ||
background: none !important; | ||
} | ||
`, | ||
), | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { CopyOutlined } from '@ant-design/icons'; | ||
import copy from 'copy-to-clipboard'; | ||
import { memo } from 'react'; | ||
|
||
import ActionIcon from '@/ActionIcon'; | ||
import { useCopied } from '@/hooks/useCopied'; | ||
import { type TooltipProps } from 'antd'; | ||
import { DivProps } from 'react-layout-kit'; | ||
|
||
export interface CopyButtonProps extends DivProps { | ||
/** | ||
* @description Additional class name | ||
*/ | ||
className?: string; | ||
/** | ||
* @description The text content to be copied | ||
*/ | ||
content: string; | ||
/** | ||
* @description The placement of the tooltip | ||
* @enum ['top', 'left', 'right', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom'] | ||
* @default 'right' | ||
*/ | ||
placement?: TooltipProps['placement']; | ||
} | ||
|
||
const CopyButton = memo<CopyButtonProps>( | ||
({ content, className, placement = 'right', ...props }) => { | ||
const { copied, setCopied } = useCopied(); | ||
|
||
return ( | ||
<ActionIcon | ||
{...props} | ||
className={className} | ||
icon={<CopyOutlined size={12} />} | ||
onClick={() => { | ||
copy(content); | ||
setCopied(); | ||
}} | ||
placement={placement} | ||
title={copied ? '✅ Success' : 'Copy'} | ||
/> | ||
); | ||
}, | ||
); | ||
|
||
export default CopyButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { memo, useEffect, useRef, useState } from 'react'; | ||
import { DivProps } from 'react-layout-kit'; | ||
import { useStyles } from './style'; | ||
|
||
const useMouseOffset = (): any => { | ||
const [offset, setOffset] = useState<{ x: number; y: number }>(); | ||
const [outside, setOutside] = useState(true); | ||
const reference = useRef<HTMLDivElement>(); | ||
|
||
useEffect(() => { | ||
if (reference.current && reference.current.parentElement) { | ||
const element = reference.current.parentElement; | ||
|
||
// debounce? | ||
const onMouseMove = (e: MouseEvent) => { | ||
const bound = element.getBoundingClientRect(); | ||
setOffset({ x: e.clientX - bound.x, y: e.clientY - bound.y }); | ||
setOutside(false); | ||
}; | ||
|
||
const onMouseLeave = () => { | ||
setOutside(true); | ||
}; | ||
element.addEventListener('mousemove', onMouseMove); | ||
element.addEventListener('mouseleave', onMouseLeave); | ||
return () => { | ||
element.removeEventListener('mousemove', onMouseMove); | ||
element.removeEventListener('mouseleave', onMouseLeave); | ||
}; | ||
} | ||
}, []); | ||
|
||
return [offset, outside, reference] as const; | ||
}; | ||
|
||
export interface SpotlightProps extends DivProps { | ||
/** | ||
* @description The size of the spotlight circle | ||
* @default 64 | ||
*/ | ||
size?: number; | ||
} | ||
|
||
const Spotlight = memo<SpotlightProps>(({ className, size = 64, ...properties }) => { | ||
const [offset, outside, reference] = useMouseOffset(); | ||
const { styles, cx } = useStyles({ offset, outside, size }); | ||
|
||
return <div className={cx(styles, className)} ref={reference} {...properties} />; | ||
}); | ||
|
||
export default Spotlight; |
Oops, something went wrong.