mirror of
https://github.com/suitenumerique/docs.git
synced 2026-08-31 04:09:43 +02:00
✨(frontend) add word count to doc header toolbox
We can now see the word count in the doc header toolbox.
This commit is contained in:
@@ -9,6 +9,7 @@ and this project adheres to
|
||||
### Added
|
||||
|
||||
- ✨(frontend) Add "Copy link to block" feature #2547
|
||||
- ✨(frontend) add word count to doc header toolbox #2549
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ test.describe('Doc Header', () => {
|
||||
}) => {
|
||||
await createDoc(page, 'doc-update', browserName, 1);
|
||||
|
||||
await writeInEditor({ page, text: 'Hello Content' });
|
||||
|
||||
const card = page.getByLabel(
|
||||
'It is the card information about the document.',
|
||||
);
|
||||
@@ -93,6 +95,7 @@ test.describe('Doc Header', () => {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Download' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText('Word count: 2 words').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('it updates the title doc and check the broadcast', async ({
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { DocsBlockNoteEditor } from './types';
|
||||
|
||||
const HEX_COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
|
||||
|
||||
export const getWordCount = (editor?: DocsBlockNoteEditor): number => {
|
||||
const doc = editor?._tiptapEditor?.state.doc;
|
||||
const text = doc ? doc.textBetween(0, doc.content.size, '\n') : '';
|
||||
const trimmed = text.trim();
|
||||
|
||||
return trimmed ? trimmed.split(/\s+/).length : 0;
|
||||
};
|
||||
|
||||
export const sanitizeColor = (color: string): string => {
|
||||
return HEX_COLOR_REGEX.test(color) ? color : randomColor();
|
||||
};
|
||||
|
||||
+31
-2
@@ -5,11 +5,16 @@ import {
|
||||
useTreeContext,
|
||||
} from '@gouvfr-lasuite/ui-components';
|
||||
import { Present } from '@gouvfr-lasuite/ui-components/icons';
|
||||
import { announce } from '@react-aria/live-announcer';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Text } from '@/components/Text';
|
||||
import { useEditorStore } from '@/docs/doc-editor/stores/useEditorStore';
|
||||
import { getWordCount } from '@/docs/doc-editor/utils';
|
||||
import { printDocumentWithStyles } from '@/docs/doc-export/utils_print';
|
||||
import { usePresenterStore } from '@/docs/doc-presenter/stores';
|
||||
import { useAuth } from '@/features/auth';
|
||||
import ContentCopyIcon from '@/icons/copy.svg';
|
||||
@@ -25,7 +30,6 @@ import StarIcon from '@/icons/star.svg';
|
||||
import DeleteIcon from '@/icons/trash.svg';
|
||||
import { useFocusStore, useResponsiveStore } from '@/stores';
|
||||
|
||||
import { printDocumentWithStyles } from '../../doc-export/utils_print';
|
||||
import {
|
||||
KEY_DOC,
|
||||
KEY_LIST_DOC,
|
||||
@@ -96,15 +100,34 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
const [isModalHistoryOpen, setIsModalHistoryOpen] = useState(false);
|
||||
const [isModalLeaveOpen, setIsModalLeaveOpen] = useState(false);
|
||||
|
||||
const { editor } = useEditorStore();
|
||||
const wordCountLabel = useMemo(() => {
|
||||
if (openDropdown) {
|
||||
return t('Word count: {{count}} words', {
|
||||
count: getWordCount(editor),
|
||||
description:
|
||||
'In the document options menu, showing the number of words in the document.',
|
||||
});
|
||||
}
|
||||
}, [editor, openDropdown, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wordCountLabel) {
|
||||
announce(wordCountLabel, 'polite');
|
||||
}
|
||||
}, [wordCountLabel]);
|
||||
|
||||
const { restoreFocus, addLastFocus } = useFocusStore();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
const copyDocLink = useCopyDocLink(doc.id);
|
||||
|
||||
const openPresenter = usePresenterStore((state) => state.open);
|
||||
const { mutate: duplicateDoc } = useDuplicateDoc({
|
||||
onSuccess: (data) => {
|
||||
void router.push(`/docs/${data.id}`);
|
||||
},
|
||||
});
|
||||
|
||||
const removeFavoriteDoc = useDeleteFavoriteDoc({
|
||||
listInvalidQueries: [KEY_LIST_DOC, KEY_DOC, KEY_LIST_FAVORITE_DOC],
|
||||
});
|
||||
@@ -232,6 +255,9 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
},
|
||||
isHidden: !doc.abilities.destroy,
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -241,6 +267,9 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
isOpen={openDropdown}
|
||||
shouldCloseOnInteractOutside={() => true}
|
||||
onOpenChange={setOpenDropdown}
|
||||
bottomMessage={
|
||||
wordCountLabel && <Text $variation="tertiary">{wordCountLabel}</Text>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
aria-label={t('Open the document options')}
|
||||
|
||||
@@ -887,6 +887,9 @@
|
||||
"Shared with {{count}} users_many": "Shared with {{count}} users",
|
||||
"Shared with {{count}} users_one": "Shared with {{count}} user",
|
||||
"Shared with {{count}} users_other": "Shared with {{count}} users",
|
||||
"Word count: {{count}} words_one": "Word count: {{count}} word",
|
||||
"Word count: {{count}} words_many": "Word count: {{count}} words",
|
||||
"Word count: {{count}} words_other": "Word count: {{count}} words",
|
||||
"days_many": "days",
|
||||
"days_one": "day",
|
||||
"days_other": "days"
|
||||
|
||||
Reference in New Issue
Block a user