mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
119 lines
3.0 KiB
TypeScript
119 lines
3.0 KiB
TypeScript
import {useCallback, useState} from 'react';
|
|
import styled from '@emotion/styled';
|
|
|
|
import {space} from 'sentry/styles/space';
|
|
|
|
interface TruncateProps {
|
|
value: string;
|
|
className?: string;
|
|
expandDirection?: 'left' | 'right';
|
|
expandable?: boolean;
|
|
leftTrim?: boolean;
|
|
maxLength?: number;
|
|
minLength?: number;
|
|
trimRegex?: RegExp;
|
|
}
|
|
|
|
function Truncate({
|
|
value,
|
|
trimRegex,
|
|
className,
|
|
minLength = 15,
|
|
maxLength = 50,
|
|
leftTrim = false,
|
|
expandable = true,
|
|
expandDirection = 'right',
|
|
}: TruncateProps) {
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
|
|
const onFocus = useCallback(
|
|
() => setIsExpanded(value.length > maxLength),
|
|
[value, maxLength]
|
|
);
|
|
|
|
const onBlur = useCallback(() => setIsExpanded(false), []);
|
|
|
|
const isTruncated = value.length > maxLength;
|
|
let shortValue: React.ReactNode = '';
|
|
|
|
if (isTruncated) {
|
|
const slicedValue = leftTrim
|
|
? value.slice(value.length - (maxLength - 4), value.length)
|
|
: value.slice(0, maxLength - 4);
|
|
|
|
// Try to trim to values from the regex
|
|
if (trimRegex && leftTrim) {
|
|
const valueIndex = slicedValue.search(trimRegex);
|
|
shortValue = (
|
|
<span>
|
|
…{' '}
|
|
{valueIndex > 0 && valueIndex <= maxLength - minLength
|
|
? slicedValue.slice(slicedValue.search(trimRegex), slicedValue.length)
|
|
: slicedValue}
|
|
</span>
|
|
);
|
|
} else if (trimRegex && !leftTrim) {
|
|
const matches = slicedValue.match(trimRegex);
|
|
let lastIndex = matches
|
|
? slicedValue.lastIndexOf(matches[matches.length - 1]) + 1
|
|
: slicedValue.length;
|
|
if (lastIndex <= minLength) {
|
|
lastIndex = slicedValue.length;
|
|
}
|
|
shortValue = <span>{slicedValue.slice(0, lastIndex)} …</span>;
|
|
} else if (leftTrim) {
|
|
shortValue = <span>… {slicedValue}</span>;
|
|
} else {
|
|
shortValue = <span>{slicedValue} …</span>;
|
|
}
|
|
} else {
|
|
shortValue = value;
|
|
}
|
|
|
|
return (
|
|
<Wrapper
|
|
className={className}
|
|
onMouseOver={expandable ? onFocus : undefined}
|
|
onMouseOut={expandable ? onBlur : undefined}
|
|
onFocus={expandable ? onFocus : undefined}
|
|
onBlur={expandable ? onBlur : undefined}
|
|
>
|
|
<span>{shortValue}</span>
|
|
{isTruncated && (
|
|
<FullValue expanded={isExpanded} expandDirection={expandDirection}>
|
|
{value}
|
|
</FullValue>
|
|
)}
|
|
</Wrapper>
|
|
);
|
|
}
|
|
|
|
const Wrapper = styled('span')`
|
|
position: relative;
|
|
`;
|
|
|
|
export const FullValue = styled('span')<{
|
|
expandDirection: 'left' | 'right';
|
|
expanded: boolean;
|
|
}>`
|
|
display: none;
|
|
position: absolute;
|
|
background: ${p => p.theme.background};
|
|
padding: ${space(0.5)};
|
|
border: 1px solid ${p => p.theme.innerBorder};
|
|
white-space: nowrap;
|
|
border-radius: ${space(0.5)};
|
|
top: -5px;
|
|
${p => p.expandDirection === 'left' && 'right: -5px;'}
|
|
${p => p.expandDirection === 'right' && 'left: -5px;'}
|
|
|
|
${p =>
|
|
p.expanded &&
|
|
`
|
|
z-index: ${p.theme.zIndex.truncationFullValue};
|
|
display: block;
|
|
`}
|
|
`;
|
|
|
|
export default Truncate;
|