168 lines
4.7 KiB
TypeScript

import React, { useCallback, useState } from 'react';
import styled from 'styled-components';
import { Editor } from '@app/entityV2/shared/tabs/Documentation/components/editor/Editor';
import { Button, Tooltip } from '@src/alchemy-components';
const LINE_HEIGHT = 1.5;
const ShowMoreWrapper = styled.div`
align-items: start;
justify-content: center;
display: flex;
flex-direction: column;
`;
const MarkdownContainer = styled.div<{ lineLimit?: number | null }>`
max-width: 100%;
position: relative;
flex: 1;
${(props) =>
props.lineLimit &&
props.lineLimit <= 1 &&
`
display: flex;
align-items: center;
gap: 4px;
`}
`;
const CustomButton = styled(Button)`
padding: 8px 0px;
`;
const MarkdownViewContainer = styled.div<{ scrollableY: boolean }>`
display: block;
overflow-wrap: break-word;
word-wrap: break-word;
overflow-x: hidden;
overflow-y: ${(props) => (props.scrollableY ? 'auto' : 'hidden')};
flex: 1;
`;
const CompactEditor = styled(Editor)<{ limit: number | null; customStyle?: React.CSSProperties }>`
.remirror-theme {
max-width: 100%;
}
.remirror-editor.ProseMirror {
${({ limit }) => limit && `max-height: ${limit * LINE_HEIGHT}em;`}
h1 {
font-size: 1.4em;
}
h2 {
font-size: 1.3em;
}
h3 {
font-size: 1.2em;
}
h4 {
font-size: 1.1em;
}
h5,
h6 {
font-size: 1em;
}
p {
${(props) => props?.customStyle?.fontSize && `font-size: ${props?.customStyle?.fontSize}`};
margin-bottom: 0;
}
padding: 0;
}
`;
const FixedLineHeightEditor = styled(CompactEditor)<{ customStyle?: React.CSSProperties }>`
.remirror-editor.ProseMirror {
* {
line-height: ${LINE_HEIGHT};
font-size: 1em !important;
margin-top: 0;
margin-bottom: 0;
}
p {
font-size: ${(props) => (props?.customStyle?.fontSize ? props?.customStyle?.fontSize : '1em')} !important;
}
}
`;
const MoreIndicator = styled.span`
break-word: normal;
`;
export type Props = {
content: string;
lineLimit?: number | null;
fixedLineHeight?: boolean;
customStyle?: React.CSSProperties;
scrollableY?: boolean; // Whether the viewer is vertically scrollable.
handleShowMore?: () => void;
hideShowMore?: boolean;
};
export default function CompactMarkdownViewer({
content,
lineLimit = 4,
fixedLineHeight = false,
customStyle = {},
scrollableY = true,
handleShowMore,
hideShowMore,
}: Props) {
const [isShowingMore, setIsShowingMore] = useState(false);
const [isTruncated, setIsTruncated] = useState(false);
const measuredRef = useCallback((node: HTMLDivElement | null) => {
if (node !== null) {
const resizeObserver = new ResizeObserver(() => {
setIsTruncated(node.scrollHeight > node.clientHeight + 1);
});
resizeObserver.observe(node);
}
}, []);
const StyledEditor = fixedLineHeight ? FixedLineHeightEditor : CompactEditor;
return (
<MarkdownContainer lineLimit={lineLimit}>
<MarkdownViewContainer scrollableY={scrollableY} ref={measuredRef}>
<StyledEditor
customStyle={customStyle}
limit={isShowingMore ? null : lineLimit}
content={content}
readOnly
/>
</MarkdownViewContainer>
{hideShowMore && isTruncated && (
<Tooltip title={content}>
<MoreIndicator>...</MoreIndicator>
</Tooltip>
)}
{!hideShowMore &&
(isShowingMore || isTruncated) && ( // "show more" when isTruncated, "show less" when isShowingMore
<ShowMoreWrapper>
<CustomButton
variant="text"
size={lineLimit && lineLimit <= 1 ? 'sm' : undefined}
onClick={(e) => {
if (handleShowMore) {
handleShowMore();
} else {
setIsShowingMore(!isShowingMore);
}
e.stopPropagation();
}}
>
{isShowingMore ? 'show less' : 'show more'}
</CustomButton>
</ShowMoreWrapper>
)}
</MarkdownContainer>
);
}