💄(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:
Anthony LC
2024-09-17 17:35:24 +02:00
committed by Anthony LC
parent 5bd78b8068
commit b37acf3138
5 changed files with 47 additions and 27 deletions

View File

@@ -20,7 +20,7 @@ test.describe('Doc Table Content', () => {
await page.getByLabel('Open the document options').click(); await page.getByLabel('Open the document options').click();
await page await page
.getByRole('button', { .getByRole('button', {
name: 'Table of content', name: 'Table of contents',
}) })
.click(); .click();
@@ -30,6 +30,8 @@ test.describe('Doc Table Content', () => {
await editor.locator('.bn-block-outer').last().fill('/'); await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 1').click(); await page.getByText('Heading 1').click();
await page.keyboard.type('Hello World'); 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(); 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 editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 2').click(); 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(); await page.locator('.bn-block-outer').last().click();
@@ -58,15 +60,15 @@ test.describe('Doc Table Content', () => {
const another = panel.getByText('Another World'); const another = panel.getByText('Another World');
await expect(hello).toBeVisible(); 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(hello).toHaveAttribute('aria-selected', 'true');
await expect(superW).toBeVisible(); 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(superW).toHaveAttribute('aria-selected', 'false');
await expect(another).toBeVisible(); 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 expect(another).toHaveAttribute('aria-selected', 'false');
await hello.click(); await hello.click();

View File

@@ -36,10 +36,10 @@ export const Panel = ({
$width="100%" $width="100%"
$maxWidth="20rem" $maxWidth="20rem"
$position="sticky" $position="sticky"
$maxHeight="96vh" $maxHeight="99vh"
$height="100%" $height="100%"
$css={` $css={`
top: 2vh; top: 0vh;
transition: ${transition}; transition: ${transition};
${ ${
!isOpen && !isOpen &&

View File

@@ -91,7 +91,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
icon={<span className="material-icons">summarize</span>} icon={<span className="material-icons">summarize</span>}
size="small" size="small"
> >
<Text $theme="primary">{t('Table of content')}</Text> <Text $theme="primary">{t('Table of contents')}</Text>
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {

View File

@@ -46,6 +46,7 @@ export const Heading = ({
block: 'start', block: 'start',
}); });
}} }}
$css="text-align: left;"
> >
<Text <Text
$theme="primary" $theme="primary"

View File

@@ -10,11 +10,28 @@ import { useDocTableContentStore } from '../stores';
import { Heading } from './Heading'; 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 = { type HeadingBlock = {
id: string; id: string;
type: string; type: string;
text: string; text: string;
content: HeadingBlock[]; content: HeadingBlock[];
contentText: string;
props: { props: {
level: number; level: number;
}; };
@@ -31,9 +48,14 @@ export const TableContent = ({ doc }: TableContentProps) => {
const editor = docsStore?.[doc.id]?.editor; const editor = docsStore?.[doc.id]?.editor;
const headingFiltering = useCallback( const headingFiltering = useCallback(
() => () =>
editor?.document.filter( editor?.document
(block) => block.type === 'heading', .filter((block) => block.type === 'heading')
) as unknown as HeadingBlock[], .map((block) => ({
...block,
contentText: recursiveTextContent(
block.content as unknown as HeadingBlock['content'],
),
})) as unknown as HeadingBlock[],
[editor?.document], [editor?.document],
); );
@@ -71,7 +93,7 @@ export const TableContent = ({ doc }: TableContentProps) => {
for (const heading of headings) { for (const heading of headings) {
const elHeading = document.body.querySelector( 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) { if (!elHeading) {
@@ -121,21 +143,16 @@ export const TableContent = ({ doc }: TableContentProps) => {
<Panel setIsPanelOpen={setClosePanel}> <Panel setIsPanelOpen={setClosePanel}>
<Box $padding="small" $maxHeight="95%"> <Box $padding="small" $maxHeight="95%">
<Box $overflow="auto"> <Box $overflow="auto">
{headings?.map((heading) => { {headings?.map((heading) => (
const content = heading.content?.[0]; <Heading
const text = content?.type === 'text' ? content.text : ''; editor={editor}
headingId={heading.id}
return ( level={heading.props.level}
<Heading text={heading.contentText}
editor={editor} key={heading.id}
headingId={heading.id} isHighlight={headingIdHighlight === heading.id}
level={heading.props.level} />
text={text} ))}
key={heading.id}
isHighlight={headingIdHighlight === heading.id}
/>
);
})}
</Box> </Box>
<Box <Box
$height="1px" $height="1px"