💄(frontend) improve ui table of contents
- keep correctly the text on the left side - improve accuracy highlightment heading when scrolling - display full heading text when text transform is applied - fix typo
This commit is contained in:
@@ -20,7 +20,7 @@ test.describe('Doc Table Content', () => {
|
||||
await page.getByLabel('Open the document options').click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Table of content',
|
||||
name: 'Table of contents',
|
||||
})
|
||||
.click();
|
||||
|
||||
@@ -30,6 +30,8 @@ test.describe('Doc Table Content', () => {
|
||||
await editor.locator('.bn-block-outer').last().fill('/');
|
||||
await page.getByText('Heading 1').click();
|
||||
await page.keyboard.type('Hello World');
|
||||
await editor.getByText('Hello').dblclick();
|
||||
await page.getByRole('button', { name: 'Strike' }).click();
|
||||
|
||||
await page.locator('.bn-block-outer').last().click();
|
||||
|
||||
@@ -40,7 +42,7 @@ test.describe('Doc Table Content', () => {
|
||||
|
||||
await editor.locator('.bn-block-outer').last().fill('/');
|
||||
await page.getByText('Heading 2').click();
|
||||
await page.keyboard.type('Super World');
|
||||
await page.keyboard.type('Super World', { delay: 100 });
|
||||
|
||||
await page.locator('.bn-block-outer').last().click();
|
||||
|
||||
@@ -58,15 +60,15 @@ test.describe('Doc Table Content', () => {
|
||||
const another = panel.getByText('Another World');
|
||||
|
||||
await expect(hello).toBeVisible();
|
||||
await expect(hello).toHaveCSS('font-size', '19.2px');
|
||||
await expect(hello).toHaveCSS('font-size', /19/);
|
||||
await expect(hello).toHaveAttribute('aria-selected', 'true');
|
||||
|
||||
await expect(superW).toBeVisible();
|
||||
await expect(superW).toHaveCSS('font-size', '16px');
|
||||
await expect(superW).toHaveCSS('font-size', /16/);
|
||||
await expect(superW).toHaveAttribute('aria-selected', 'false');
|
||||
|
||||
await expect(another).toBeVisible();
|
||||
await expect(another).toHaveCSS('font-size', '12.8px');
|
||||
await expect(another).toHaveCSS('font-size', /12/);
|
||||
await expect(another).toHaveAttribute('aria-selected', 'false');
|
||||
|
||||
await hello.click();
|
||||
|
||||
@@ -36,10 +36,10 @@ export const Panel = ({
|
||||
$width="100%"
|
||||
$maxWidth="20rem"
|
||||
$position="sticky"
|
||||
$maxHeight="96vh"
|
||||
$maxHeight="99vh"
|
||||
$height="100%"
|
||||
$css={`
|
||||
top: 2vh;
|
||||
top: 0vh;
|
||||
transition: ${transition};
|
||||
${
|
||||
!isOpen &&
|
||||
|
||||
@@ -91,7 +91,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
icon={<span className="material-icons">summarize</span>}
|
||||
size="small"
|
||||
>
|
||||
<Text $theme="primary">{t('Table of content')}</Text>
|
||||
<Text $theme="primary">{t('Table of contents')}</Text>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
||||
@@ -46,6 +46,7 @@ export const Heading = ({
|
||||
block: 'start',
|
||||
});
|
||||
}}
|
||||
$css="text-align: left;"
|
||||
>
|
||||
<Text
|
||||
$theme="primary"
|
||||
|
||||
@@ -10,11 +10,28 @@ import { useDocTableContentStore } from '../stores';
|
||||
|
||||
import { Heading } from './Heading';
|
||||
|
||||
const recursiveTextContent = (content: HeadingBlock['content']): string => {
|
||||
if (!content) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return content.reduce((acc, content) => {
|
||||
if (content.type === 'text') {
|
||||
return acc + content.text;
|
||||
} else if (content.type === 'link') {
|
||||
return acc + recursiveTextContent(content.content);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, '');
|
||||
};
|
||||
|
||||
type HeadingBlock = {
|
||||
id: string;
|
||||
type: string;
|
||||
text: string;
|
||||
content: HeadingBlock[];
|
||||
contentText: string;
|
||||
props: {
|
||||
level: number;
|
||||
};
|
||||
@@ -31,9 +48,14 @@ export const TableContent = ({ doc }: TableContentProps) => {
|
||||
const editor = docsStore?.[doc.id]?.editor;
|
||||
const headingFiltering = useCallback(
|
||||
() =>
|
||||
editor?.document.filter(
|
||||
(block) => block.type === 'heading',
|
||||
) as unknown as HeadingBlock[],
|
||||
editor?.document
|
||||
.filter((block) => block.type === 'heading')
|
||||
.map((block) => ({
|
||||
...block,
|
||||
contentText: recursiveTextContent(
|
||||
block.content as unknown as HeadingBlock['content'],
|
||||
),
|
||||
})) as unknown as HeadingBlock[],
|
||||
[editor?.document],
|
||||
);
|
||||
|
||||
@@ -71,7 +93,7 @@ export const TableContent = ({ doc }: TableContentProps) => {
|
||||
|
||||
for (const heading of headings) {
|
||||
const elHeading = document.body.querySelector(
|
||||
`.bn-block-outer[data-id="${heading.id}"]`,
|
||||
`.bn-block-outer[data-id="${heading.id}"] [data-content-type="heading"]:first-child`,
|
||||
);
|
||||
|
||||
if (!elHeading) {
|
||||
@@ -121,21 +143,16 @@ export const TableContent = ({ doc }: TableContentProps) => {
|
||||
<Panel setIsPanelOpen={setClosePanel}>
|
||||
<Box $padding="small" $maxHeight="95%">
|
||||
<Box $overflow="auto">
|
||||
{headings?.map((heading) => {
|
||||
const content = heading.content?.[0];
|
||||
const text = content?.type === 'text' ? content.text : '';
|
||||
|
||||
return (
|
||||
<Heading
|
||||
editor={editor}
|
||||
headingId={heading.id}
|
||||
level={heading.props.level}
|
||||
text={text}
|
||||
key={heading.id}
|
||||
isHighlight={headingIdHighlight === heading.id}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{headings?.map((heading) => (
|
||||
<Heading
|
||||
editor={editor}
|
||||
headingId={heading.id}
|
||||
level={heading.props.level}
|
||||
text={heading.contentText}
|
||||
key={heading.id}
|
||||
isHighlight={headingIdHighlight === heading.id}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Box
|
||||
$height="1px"
|
||||
|
||||
Reference in New Issue
Block a user