-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
caf6191
commit b6d5cfd
Showing
13 changed files
with
544 additions
and
273 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import * as React from 'react'; | ||
import classNames from 'classnames'; | ||
import { TooltipProps } from 'antd'; | ||
import EllipsisText from './EllipsisText'; | ||
import EllipsisWidth from './EllipsisWidth'; | ||
import EllipsisLine from './EllipsisLine'; | ||
import EllipsisLineClamp from './EllipsisLineClamp'; | ||
|
||
// @ts-ignore | ||
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined; | ||
|
||
export interface EllipsisProps { | ||
tooltip?: boolean; | ||
tooltipProps?: TooltipProps; | ||
length?: number; | ||
lines?: number; | ||
fullWidthRecognition?: boolean; | ||
className?: string; | ||
width?: number | string; | ||
style?: React.CSSProperties; | ||
prefix?: string; | ||
children: React.ReactNode; | ||
} | ||
|
||
const Ellipsis = (props: EllipsisProps) => { | ||
const { | ||
children, | ||
lines, | ||
length, | ||
width, | ||
className, | ||
tooltip = true, | ||
style, | ||
fullWidthRecognition = false, | ||
prefix = 'plus-ellipsis', | ||
tooltipProps = {}, | ||
...restProps | ||
} = props; | ||
|
||
const cls = classNames(`${prefix}-ellipsis`, className, { | ||
[`${prefix}-width-mode`]: width, | ||
[`${prefix}-line`]: lines && !isSupportLineClamp, | ||
[`${prefix}-lineClamp`]: lines && isSupportLineClamp, | ||
}); | ||
|
||
// 一种限制都没有返回原值 | ||
if (!lines && !length && !width) { | ||
return ( | ||
<span className={cls} {...restProps}> | ||
{children} | ||
</span> | ||
); | ||
} | ||
|
||
if (width) { | ||
return ( | ||
<EllipsisWidth className={cls} prefix={prefix} style={style} tooltip={tooltip} tooltipProps={tooltipProps} width={width} {...restProps}> | ||
{children} | ||
</EllipsisWidth> | ||
); | ||
} | ||
|
||
// 字数限制 | ||
if (length) { | ||
return ( | ||
<EllipsisText | ||
className={cls} | ||
prefix={prefix} | ||
tooltipProps={tooltipProps} | ||
length={length} | ||
text={children || ''} | ||
tooltip={tooltip} | ||
fullWidthRecognition={fullWidthRecognition} | ||
{...restProps} | ||
/> | ||
); | ||
} | ||
|
||
if (isSupportLineClamp) { | ||
return ( | ||
<EllipsisLineClamp className={cls} prefix={prefix} tooltip={tooltip} tooltipProps={tooltipProps} lines={lines} {...restProps}> | ||
{children} | ||
</EllipsisLineClamp> | ||
); | ||
} | ||
|
||
return ( | ||
<EllipsisLine className={cls} prefix={prefix} tooltip={tooltip} tooltipProps={tooltipProps} lines={lines} {...restProps}> | ||
{children} | ||
</EllipsisLine> | ||
); | ||
}; | ||
|
||
export default Ellipsis; |
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,150 @@ | ||
import * as React from 'react'; | ||
import ResizeObserver from 'resize-observer-polyfill'; | ||
import { Tooltip } from 'antd'; | ||
import { EllipsisProps } from './Ellipsis'; | ||
|
||
const bisection = (th: number, m: number, b: number, e: any, text: string, shadowNode: { innerHTML: string; offsetHeight: any }): number => { | ||
const suffix = '...'; | ||
let mid = m; | ||
let end = e; | ||
let begin = b; | ||
shadowNode.innerHTML = text.substring(0, mid) + suffix; | ||
let sh = shadowNode.offsetHeight; | ||
|
||
if (sh <= th) { | ||
shadowNode.innerHTML = text.substring(0, mid + 1) + suffix; | ||
sh = shadowNode.offsetHeight; | ||
if (sh > th || mid === begin) { | ||
return mid; | ||
} | ||
begin = mid; | ||
if (end - begin === 1) { | ||
mid = 1 + begin; | ||
} else { | ||
mid = Math.floor((end - begin) / 2) + begin; | ||
} | ||
return bisection(th, mid, begin, end, text, shadowNode); | ||
} | ||
if (mid - 1 < 0) { | ||
return mid; | ||
} | ||
shadowNode.innerHTML = text.substring(0, mid - 1) + suffix; | ||
sh = shadowNode.offsetHeight; | ||
if (sh <= th) { | ||
return mid - 1; | ||
} | ||
end = mid; | ||
mid = Math.floor((end - begin) / 2) + begin; | ||
return bisection(th, mid, begin, end, text, shadowNode); | ||
}; | ||
|
||
type EllipsisLineProps = Omit<EllipsisProps, 'length' | 'width' | 'fullWidthRecognition'>; | ||
|
||
const EllipsisLine = (props: EllipsisLineProps) => { | ||
const { className, prefix, lines = 2, tooltip, tooltipProps, children, ...restProps } = props; | ||
|
||
const [tooltipVisible, setTooltipVisible] = React.useState<boolean>(false); | ||
const [targetCount, setTargetCount] = React.useState<number>(0); | ||
const [text, setText] = React.useState<string>(''); | ||
|
||
const rootRef = React.useRef<HTMLDivElement>(null); | ||
const contentRef = React.useRef<HTMLDivElement>(null); | ||
const nodeRef = React.useRef<HTMLSpanElement>(null); | ||
const shadowRef = React.useRef<HTMLDivElement>(null); | ||
const shadowChildrenRef = React.useRef<HTMLDivElement>(null); | ||
|
||
const handleTooltipVisibleChange = (visible: boolean) => { | ||
const node = nodeRef.current; | ||
if (!node) { | ||
return; | ||
} | ||
|
||
const shadowChildren = shadowChildrenRef.current; | ||
const text = (shadowChildren!.innerText || shadowChildren!.textContent || '') as string; | ||
const nextVisible = visible && targetCount < (text!.length || 0); | ||
|
||
setTooltipVisible(nextVisible); | ||
}; | ||
|
||
const computeLine = () => { | ||
const root = rootRef.current!; | ||
const content = contentRef.current!; | ||
const shadow = shadowRef.current!; | ||
const shadowChildren = shadowChildrenRef.current!; | ||
|
||
const text = shadowChildren.innerText || shadowChildren.textContent; | ||
const lineHeight = parseInt(getComputedStyle(root).lineHeight, 10); | ||
const targetHeight = lines * lineHeight; | ||
content.style.height = `${targetHeight}px`; | ||
const totalHeight = shadowChildren.offsetHeight; | ||
const shadowNode = shadow.firstChild! as HTMLDivElement; | ||
|
||
if (totalHeight <= targetHeight) { | ||
setText(text!); | ||
setTargetCount(text!.length); | ||
return; | ||
} | ||
|
||
// bisection | ||
const len = text!.length; | ||
const mid = Math.ceil(len / 2); | ||
|
||
const count = bisection(targetHeight, mid, 0, len, text!, shadowNode); | ||
|
||
setText(text!); | ||
setTargetCount(count); | ||
}; | ||
|
||
React.useEffect(() => { | ||
const resizeObserver = new ResizeObserver((entries) => { | ||
entries.forEach((entry) => { | ||
if (entry.target === contentRef.current) { | ||
computeLine(); | ||
} | ||
}); | ||
}); | ||
resizeObserver.observe(contentRef.current!); | ||
return () => { | ||
resizeObserver.disconnect(); | ||
}; | ||
}, []); | ||
|
||
React.useEffect(() => { | ||
computeLine(); | ||
}, [lines, children]); | ||
|
||
const childNode = ( | ||
<span ref={nodeRef}> | ||
{targetCount > 0 && text.substring(0, targetCount)} | ||
{targetCount > 0 && targetCount < text.length && '...'} | ||
</span> | ||
); | ||
|
||
return ( | ||
<div {...restProps} ref={rootRef} className={className}> | ||
<div ref={contentRef}> | ||
{tooltip ? ( | ||
<Tooltip | ||
{...tooltipProps} | ||
overlayClassName={`${prefix}-tooltip`} | ||
title={text} | ||
open={tooltipVisible} | ||
onOpenChange={handleTooltipVisibleChange} | ||
> | ||
{childNode} | ||
</Tooltip> | ||
) : ( | ||
childNode | ||
)} | ||
<div className={`${prefix}-shadow`} ref={shadowChildrenRef}> | ||
{children} | ||
</div> | ||
<div className={`${prefix}-shadow`} ref={shadowRef}> | ||
<span>{text}</span> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default EllipsisLine; |
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,52 @@ | ||
import * as React from 'react'; | ||
import { Tooltip } from 'antd'; | ||
import { EllipsisProps } from './Ellipsis'; | ||
|
||
type EllipsisLineClampProps = Omit<EllipsisProps, 'length' | 'width' | 'fullWidthRecognition'>; | ||
|
||
const EllipsisLineClamp = (props: EllipsisLineClampProps) => { | ||
const { className, prefix, lines, tooltip, tooltipProps, children, ...restProps } = props; | ||
|
||
const [tooltipVisible, setTooltipVisible] = React.useState<boolean>(false); | ||
const lineClampNodeRef = React.useRef<HTMLDivElement>(null); | ||
|
||
const handleTooltipVisibleChange = (visible: boolean) => { | ||
const node = lineClampNodeRef.current; | ||
if (!node) { | ||
return; | ||
} | ||
|
||
const nextVisible = visible && (node.offsetHeight < node.scrollHeight || node.offsetWidth < node.scrollWidth); | ||
|
||
setTooltipVisible(nextVisible); | ||
}; | ||
|
||
//行数限制 | ||
|
||
// support document.body.style.webkitLineClamp | ||
const id = `plus-ellipsis-${`${new Date().getTime()}${Math.floor(Math.random() * 100)}`}`; | ||
const style = `#${id}{-webkit-line-clamp:${lines};-webkit-box-orient: vertical;}`; | ||
|
||
const node = ( | ||
<div ref={lineClampNodeRef} id={id} className={className} {...restProps}> | ||
<style>{style}</style> | ||
{children} | ||
</div> | ||
); | ||
|
||
return tooltip ? ( | ||
<Tooltip | ||
{...tooltipProps} | ||
overlayClassName={`${prefix}-tooltip`} | ||
title={children} | ||
open={tooltipVisible} | ||
onOpenChange={handleTooltipVisibleChange} | ||
> | ||
{node} | ||
</Tooltip> | ||
) : ( | ||
node | ||
); | ||
}; | ||
|
||
export default EllipsisLineClamp; |
Oops, something went wrong.