From ce6dc77222d31c5f209e6ffd4e350d51b4f6e9f7 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Thu, 28 May 2026 16:05:14 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=9A(frontend)=20move=20doc=20toolbox?= =?UTF-8?q?=20to=20Floating=20Bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are modifying the doc header, we are moving elements to a floating bar. In this case, we are moving the doc toolbox to the floating bar. We adapted the toolbox with the updated actions. We update the doc header to use all empty space. We moved the emoji part from the toolbox directly to the header. --- CHANGELOG.md | 1 + .../__tests__/app-impress/doc-export.spec.ts | 43 +--- .../__tests__/app-impress/doc-header.spec.ts | 73 +++---- .../__tests__/app-impress/doc-version.spec.ts | 2 +- .../src/cunningham/cunningham-style.css | 2 +- .../__tests__/DocHeaderEmoji.spec.tsx | 61 ++++++ .../__tests__/DocToolBoxEmoji.spec.tsx | 103 ---------- .../__tests__/DocToolBoxLicence.spec.tsx | 36 +++- .../docs/doc-header/components/DocHeader.tsx | 90 +++++--- .../doc-header/components/DocHeaderInfo.tsx | 6 +- .../docs/doc-header/components/DocTitle.tsx | 17 +- .../docs/doc-header/components/DocToolBox.tsx | 193 ++++++++---------- .../doc-header/components/FloatingBar.tsx | 21 +- .../components/RightPanelCollapseButton.tsx | 16 +- .../components/DocEditorSkeleton.tsx | 75 +++---- .../impress/src/tests/__mocks__/svgMock.tsx | 2 + src/frontend/apps/impress/vitest.config.ts | 8 + 17 files changed, 352 insertions(+), 397 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderEmoji.spec.tsx delete mode 100644 src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxEmoji.spec.tsx create mode 100644 src/frontend/apps/impress/src/tests/__mocks__/svgMock.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a8e0aad..e85e8de6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to - ♻️(backend) allow global search in sub documents - ✨(backend) add a breadcrumb in the search response +- ♻️(frontend) move doc action buttons to fix toolbar #2360 ### Fixed diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index 71fb3dccd..dcc8afc3d 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -7,6 +7,7 @@ import { PDFParse } from 'pdf-parse'; import { TestLanguage, + clickInEditorMenu, createDoc, verifyDocName, waitForLanguageSwitch, @@ -24,11 +25,7 @@ test.describe('Doc Export', () => { browserName, }) => { await createDoc(page, 'doc-editor', browserName, 1); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); await expect(page.getByTestId('modal-export-title')).toBeVisible(); await expect( @@ -54,11 +51,7 @@ test.describe('Doc Export', () => { test('it exports the doc to docx', async ({ page, browserName }) => { const randomDoc = await overrideDocContent({ page, browserName }); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); await page.getByRole('combobox', { name: 'Format' }).click(); await page.getByRole('option', { name: 'Docx' }).click(); @@ -84,11 +77,7 @@ test.describe('Doc Export', () => { test('it exports the doc to odt', async ({ page, browserName }) => { const randomDoc = await overrideDocContent({ page, browserName }); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); await page.getByRole('combobox', { name: 'Format' }).click(); await page.getByRole('option', { name: 'Odt' }).click(); @@ -139,11 +128,7 @@ test.describe('Doc Export', () => { // Give some time for the image to be fully processed await page.waitForTimeout(1000); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); await page.getByRole('combobox', { name: 'Format' }).click(); await page.getByRole('option', { name: 'HTML' }).click(); @@ -231,11 +216,7 @@ test.describe('Doc Export', () => { .fill('https://docs.numerique.gouv.fr/assets/logo-gouv.png'); await page.getByText('Embed image').click(); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -322,11 +303,7 @@ test.describe('Doc Export', () => { await overrideDocContent({ page, browserName }); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); await page.getByRole('combobox', { name: 'Format' }).click(); await page.getByRole('option', { name: 'Print' }).click(); @@ -375,11 +352,7 @@ test.describe('Doc Export', () => { const randomDoc = await overrideDocContent({ page, browserName }); - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); + await clickInEditorMenu(page, 'Download'); const downloadPromise = page.waitForEvent('download', (download) => { return download.suggestedFilename().includes(`${randomDoc}.pdf`); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts index 20c2bd155..5c5e91a6d 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from '@playwright/test'; import { + clickInEditorMenu, clickInEditorShareButton, createDoc, getGridRow, @@ -83,15 +84,14 @@ test.describe('Doc Header', () => { await page.getByRole('button', { name: 'close' }).first().click(); - await expect(card.getByText('Public document')).toBeVisible(); - + await expect(card.getByText('Public ·')).toBeVisible(); await expect(card.getByText('Owner ·')).toBeVisible(); await expect(page.getByRole('button', { name: 'Share' })).toBeVisible(); + await page + .getByRole('button', { name: 'Open the document options' }) + .click(); await expect( - page.getByRole('button', { name: 'Export the document' }), - ).toBeVisible(); - await expect( - page.getByRole('button', { name: 'Open the document options' }), + page.getByRole('menuitem', { name: 'Download' }), ).toBeVisible(); }); @@ -186,17 +186,15 @@ test.describe('Doc Header', () => { await createDoc(page, 'doc-update-emoji', browserName, 1); const emojiPicker = page.locator('.--docs--doc-title').getByRole('button'); - const optionMenu = page.getByLabel('Open the document options'); - const addEmojiMenuItem = page.getByRole('menuitem', { name: 'Add emoji' }); - const removeEmojiMenuItem = page.getByRole('menuitem', { - name: 'Remove emoji', + const addEmoji = page.getByRole('button', { name: 'Add icon' }); + const removeEmoji = page.getByRole('button', { + name: 'Remove icon', }); // Top parent should not have emoji picker await expect(emojiPicker).toBeHidden(); - await optionMenu.click(); - await expect(addEmojiMenuItem).toBeHidden(); - await expect(removeEmojiMenuItem).toBeHidden(); + await expect(addEmoji).toBeHidden(); + await expect(removeEmoji).toBeHidden(); await page.keyboard.press('Escape'); const { name: docChild } = await createRootSubPage( @@ -211,9 +209,8 @@ test.describe('Doc Header', () => { await expect(emojiPicker).toBeHidden(); // Add emoji - await optionMenu.click(); - await expect(removeEmojiMenuItem).toBeHidden(); - await addEmojiMenuItem.click(); + await expect(removeEmoji).toBeHidden(); + await addEmoji.click(); // The 1 April the emoji is a fish await expect(emojiPicker).toHaveText(/📄|🐟/); @@ -235,17 +232,15 @@ test.describe('Doc Header', () => { await expect(row.getByText('😀')).toBeVisible(); // Remove emoji - await optionMenu.click(); - await expect(addEmojiMenuItem).toBeHidden(); - await removeEmojiMenuItem.click(); + await expect(addEmoji).toBeHidden(); + await removeEmoji.click(); await expect(emojiPicker).toBeHidden(); }); test('it deletes the doc', async ({ page, browserName }) => { const [randomDoc] = await createDoc(page, 'doc-delete', browserName, 1); - await page.getByLabel('Open the document options').click(); - await page.getByRole('menuitem', { name: 'Delete document' }).click(); + await clickInEditorMenu(page, 'Delete'); await expect( page.getByRole('heading', { name: 'Delete a doc' }), @@ -301,15 +296,12 @@ test.describe('Doc Header', () => { page.getByRole('textbox', { name: 'Document title' }), ).toContainText('Mocked document'); - await expect( - page.getByRole('button', { name: 'Export the document' }), - ).toBeVisible(); - await page.getByLabel('Open the document options').click(); await expect( - page.getByRole('menuitem', { name: 'Delete document' }), - ).toBeDisabled(); + page.getByRole('menuitem', { name: 'Download' }), + ).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Delete' })).toBeHidden(); // Click somewhere else to close the options await page.locator('body').click({ position: { x: 0, y: 0 } }); @@ -381,14 +373,12 @@ test.describe('Doc Header', () => { page.getByRole('textbox', { name: 'Document title' }), ).toContainText('Mocked document'); - await expect( - page.getByRole('button', { name: 'Export the document' }), - ).toBeVisible(); await page.getByLabel('Open the document options').click(); await expect( - page.getByRole('menuitem', { name: 'Delete document' }), - ).toBeDisabled(); + page.getByRole('menuitem', { name: 'Download' }), + ).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Delete' })).toBeHidden(); // Click somewhere else to close the options await page.locator('body').click({ position: { x: 0, y: 0 } }); @@ -453,14 +443,12 @@ test.describe('Doc Header', () => { page.getByRole('heading', { name: 'Mocked document' }), ).toBeVisible(); - await expect( - page.getByRole('button', { name: 'Export the document' }), - ).toBeVisible(); await page.getByLabel('Open the document options').click(); await expect( - page.getByRole('menuitem', { name: 'Delete document' }), - ).toBeDisabled(); + page.getByRole('menuitem', { name: 'Download' }), + ).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Delete' })).toBeHidden(); // Click somewhere else to close the options await page.locator('body').click({ position: { x: 0, y: 0 } }); @@ -707,13 +695,18 @@ test.describe('Documents Header mobile', () => { await goToGridDoc(page); - await expect(page.getByRole('button', { name: 'Copy link' })).toBeHidden(); await page.getByLabel('Open the document options').click(); await expect( page.getByRole('menuitem', { name: 'Copy link' }), ).toBeVisible(); - await page.getByRole('menuitem', { name: 'Share' }).click(); - await expect(page.getByRole('button', { name: 'Copy link' })).toBeVisible(); + await page.keyboard.press('Escape'); + await page.getByRole('button', { name: 'Share' }).click(); + const shareModal = page.getByRole('dialog', { + name: 'Share the document', + }); + await expect( + shareModal.getByRole('button', { name: 'Copy link' }), + ).toBeVisible(); }); test('it checks the close button on Share modal', async ({ page }) => { diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts index 7294a52b2..7a698b027 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts @@ -116,7 +116,7 @@ test.describe('Doc Version', () => { await page.getByLabel('Open the document options').click(); await expect( page.getByRole('menuitem', { name: 'Version history' }), - ).toBeDisabled(); + ).toBeHidden(); }); test('it restores the doc version', async ({ page, browserName }) => { diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-style.css b/src/frontend/apps/impress/src/cunningham/cunningham-style.css index 9c3074223..673c0668c 100644 --- a/src/frontend/apps/impress/src/cunningham/cunningham-style.css +++ b/src/frontend/apps/impress/src/cunningham/cunningham-style.css @@ -93,7 +93,7 @@ * Tooltip */ .c__tooltip { - padding: var(--c--globals--font--sizes--sm) var(--c--globals--spacings--xxs); + padding: var(--c--globals--font--sizes--t) var(--c--globals--spacings--xxs); } .c__tooltip .react-aria-OverlayArrow svg { diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderEmoji.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderEmoji.spec.tsx new file mode 100644 index 000000000..caa82a8da --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderEmoji.spec.tsx @@ -0,0 +1,61 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; + +import { AppWrapper } from '@/tests/utils'; + +const mockUpdateDocEmoji = vi.fn(); + +vi.mock('@/docs/doc-management', async () => { + const actual = await vi.importActual('@/docs/doc-management'); + return { + ...actual, + useDocTitleUpdate: () => ({ updateDocEmoji: mockUpdateDocEmoji }), + }; +}); + +import { DocHeader } from '../components/DocHeader'; + +const doc = { + id: 'doc-1', + title: 'My document', + is_favorite: false, + nb_accesses_direct: 1, + abilities: { + versions_list: true, + destroy: true, + partial_update: true, + duplicate: true, + accesses_view: true, + }, +} as any; + +describe('DocHeader - Add emoji (April Fools easter egg)', () => { + beforeEach(() => { + vi.useFakeTimers(); + mockUpdateDocEmoji.mockClear(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + [ + { emoji: '🐟', date: '2026-04-01' }, + { emoji: '📄', date: '2026-03-30' }, + { emoji: '📄', date: '2026-04-02' }, + ].forEach(({ emoji, date }) => { + test(`uses ${emoji} emoji on ${date}`, () => { + vi.setSystemTime(new Date(date)); + + render(, { wrapper: AppWrapper }); + + fireEvent.click(screen.getByRole('button', { name: 'Add icon' })); + + expect(mockUpdateDocEmoji).toHaveBeenCalledWith( + 'doc-1', + 'My document', + emoji, + ); + }); + }); +}); diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxEmoji.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxEmoji.spec.tsx deleted file mode 100644 index 5e0024cce..000000000 --- a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxEmoji.spec.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { render } from '@testing-library/react'; -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; - -import { DropdownMenuOption } from '@/components'; -import { AppWrapper } from '@/tests/utils'; - -const mockUpdateDocEmoji = vi.fn(); -let capturedOptions: DropdownMenuOption[] = []; - -vi.mock('@/components', async () => { - const actual = await vi.importActual('@/components'); - return { - ...actual, - DropdownMenu: ({ options }: { options: DropdownMenuOption[] }) => { - capturedOptions = options; - return null; - }, - }; -}); - -vi.mock('next/router', async () => ({ - ...(await vi.importActual('next/router')), - useRouter: () => ({ push: vi.fn() }), -})); - -vi.mock('@/docs/doc-management', async () => { - const actual = await vi.importActual('@/docs/doc-management'); - return { - ...actual, - useDocTitleUpdate: () => ({ updateDocEmoji: mockUpdateDocEmoji }), - useDocUtils: () => ({ isChild: true, isTopRoot: false }), - useCopyDocLink: () => vi.fn(), - useCreateFavoriteDoc: () => ({ mutate: vi.fn() }), - useDeleteFavoriteDoc: () => ({ mutate: vi.fn() }), - useDuplicateDoc: () => ({ mutate: vi.fn() }), - }; -}); - -vi.mock('@/stores', () => ({ - useFocusStore: (selector?: (state: any) => any) => { - const state = { addLastFocus: vi.fn(), restoreFocus: vi.fn() }; - return selector ? selector(state) : state; - }, - useResponsiveStore: () => ({ - isSmallMobile: false, - isMobile: false, - isDesktop: true, - }), -})); - -vi.mock('../hooks/useCopyCurrentEditorToClipboard', () => ({ - useCopyCurrentEditorToClipboard: () => vi.fn(), -})); - -import { DocToolBox } from '../components/DocToolBox'; - -const doc = { - id: 'doc-1', - title: 'My document', - is_favorite: false, - nb_accesses_direct: 1, - abilities: { - versions_list: true, - destroy: true, - partial_update: true, - duplicate: true, - accesses_view: true, - }, -} as any; - -describe('DocToolBox - Add emoji (April Fools easter egg)', () => { - beforeEach(() => { - vi.useFakeTimers(); - mockUpdateDocEmoji.mockClear(); - }); - - afterEach(() => { - vi.useRealTimers(); - }); - - [ - { emoji: '🐟', date: '2026-04-01' }, - { emoji: '📄', date: '2026-03-30' }, - { emoji: '📄', date: '2026-04-02' }, - ].forEach(({ emoji, date }) => { - test(`uses ${emoji} emoji on ${date}`, () => { - vi.setSystemTime(new Date(date)); - - render(, { wrapper: AppWrapper }); - - const addEmojiOption = capturedOptions.find( - (o) => o.label === 'Add emoji', - ); - void addEmojiOption?.callback?.(); - - expect(mockUpdateDocEmoji).toHaveBeenCalledWith( - 'doc-1', - 'My document', - emoji, - ); - }); - }); -}); diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxLicence.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxLicence.spec.tsx index e0e693d31..087940fa0 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxLicence.spec.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocToolBoxLicence.spec.tsx @@ -9,9 +9,33 @@ vi.mock('next/router', async () => ({ ...(await vi.importActual('next/router')), useRouter: () => ({ push: vi.fn(), + pathname: '/docs/doc-1', }), })); +vi.mock('@gouvfr-lasuite/ui-kit', async () => { + const actual = await vi.importActual('@gouvfr-lasuite/ui-kit'); + return { + ...actual, + DropdownMenu: ({ options, children }: any) => ( + <> + {children} +
    + {options + .filter((o: any) => !o.isHidden) + .map((o: any) => ( +
  • {o.label}
  • + ))} +
+ + ), + }; +}); + +vi.mock('../hooks/useCopyCurrentEditorToClipboard', () => ({ + useCopyCurrentEditorToClipboard: () => vi.fn(), +})); + const doc = { nb_accesses: 1, abilities: { @@ -39,9 +63,7 @@ describe('DocToolBox - Licence', () => { wrapper: AppWrapper, }); - expect( - await screen.findByLabelText('Export the document'), - ).toBeInTheDocument(); + expect(await screen.findByText('Download')).toBeInTheDocument(); }, 15000); test('The export button is not rendered when MIT version is activated', async () => { @@ -53,12 +75,6 @@ describe('DocToolBox - Licence', () => { wrapper: AppWrapper, }); - expect( - screen.getByLabelText('Open the document options'), - ).toBeInTheDocument(); - - expect( - screen.queryByLabelText('Export the document'), - ).not.toBeInTheDocument(); + expect(screen.queryByText('Download')).not.toBeInTheDocument(); }, 15000); }); diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx index 43c4a7eab..2a71202ed 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx @@ -1,62 +1,90 @@ +import { Button } from '@gouvfr-lasuite/cunningham-react'; import { useTranslation } from 'react-i18next'; +import RemoveEmojiSVG from '@/assets/icons/ui-kit/face-remove.svg'; +import AddEmojiSVG from '@/assets/icons/ui-kit/face.svg'; import { Box, HorizontalSeparator } from '@/components'; -import { useCunninghamTheme } from '@/cunningham'; -import { Doc, useIsCollaborativeEditable } from '@/docs/doc-management'; -import { useResponsiveStore } from '@/stores'; +import { + Doc, + getEmojiAndTitle, + useDocTitleUpdate, + useDocUtils, + useIsCollaborativeEditable, +} from '@/docs/doc-management'; import { AlertNetwork } from './AlertNetwork'; import { AlertRestore } from './AlertRestore'; import { DocHeaderInfo } from './DocHeaderInfo'; import { DocTitle } from './DocTitle'; -import { DocToolBox } from './DocToolBox'; interface DocHeaderProps { doc: Doc; } export const DocHeader = ({ doc }: DocHeaderProps) => { - const { spacingsTokens } = useCunninghamTheme(); - const { isDesktop } = useResponsiveStore(); const { t } = useTranslation(); const { isEditable } = useIsCollaborativeEditable(doc); const isDeletedDoc = !!doc.deleted_at; + // Emoji Management + const { emoji } = getEmojiAndTitle(doc.title ?? ''); + const { updateDocEmoji } = useDocTitleUpdate(); + const { isTopRoot } = useDocUtils(doc); + const displayEmojiButton = doc.abilities.partial_update && !isTopRoot; return ( <> - {isDeletedDoc && } - {!isEditable && } - - - - - - - - {!isDeletedDoc && } - + {isDeletedDoc && } + {!isEditable && } - + + + {displayEmojiButton && ( + + )} + + + + + ); diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx index 273c15c89..8de22bd4a 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx @@ -2,7 +2,7 @@ import { t } from 'i18next'; import PublicSVG from '@/assets/icons/ui-kit/public.svg'; import ProtedtedSVG from '@/assets/icons/ui-kit/vpn_lock.svg'; -import { Text } from '@/components'; +import { Box, Text } from '@/components'; import { useConfig } from '@/core'; import { Doc, @@ -40,7 +40,7 @@ export const DocHeaderInfo = ({ doc }: DocHeaderInfoProps) => { } return ( - <> + { {dateToDisplay} - + ); }; diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx index 7a3ce2ea2..7c87ac542 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx @@ -42,11 +42,7 @@ export const DocTitleText = () => { return ( - + {currentDoc?.title || untitledDocument} @@ -110,7 +106,7 @@ const DocTitleEmojiPicker = ({ doc }: DocTitleProps) => { }; const DocTitleInput = ({ doc }: DocTitleProps) => { - const { isLargeScreen } = useResponsiveStore(); + const { isSmallMobile } = useResponsiveStore(); const { t } = useTranslation(); const { isTopRoot } = useDocUtils(doc); const { untitledDocument } = useTrans(); @@ -200,7 +196,6 @@ const DocTitleInput = ({ doc }: DocTitleProps) => { $minHeight="40px" > {!isTopRoot && } - { } onPasteCapture={handlePaste} onDropCapture={handleDrop} - $padding={{ right: 'big' }} $css={css` &[contenteditable='true']:empty:not(:focus):before { content: '${untitledDocument}'; @@ -227,11 +221,12 @@ const DocTitleInput = ({ doc }: DocTitleProps) => { pointer-events: none; font-style: italic; } - font-size: ${isLargeScreen - ? 'var(--c--globals--font--sizes--h2)' - : 'var(--c--globals--font--sizes--sm)'}; + font-size: ${isSmallMobile + ? 'var(--c--globals--font--sizes--h4)' + : 'var(--c--globals--font--sizes--h2)'}; font-weight: 700; outline: none; + width: 100%; `} > {titleDisplay} diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx index 7d2f749e0..78380ca3d 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx @@ -1,42 +1,36 @@ import { Button, useModal } from '@gouvfr-lasuite/cunningham-react'; -import { useTreeContext } from '@gouvfr-lasuite/ui-kit'; +import { + DropdownMenu, + DropdownMenuOption, + useTreeContext, +} from '@gouvfr-lasuite/ui-kit'; import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { css } from 'styled-components'; import AddLinkSVG from '@/assets/icons/ui-kit/add_link.svg'; import ContentCopySVG from '@/assets/icons/ui-kit/content_copy.svg'; import DeleteSVG from '@/assets/icons/ui-kit/delete.svg'; import DownloadSVG from '@/assets/icons/ui-kit/download.svg'; -import RemoveEmojiSVG from '@/assets/icons/ui-kit/face-remove.svg'; -import AddEmojiSVG from '@/assets/icons/ui-kit/face.svg'; +import SharedSVG from '@/assets/icons/ui-kit/group.svg'; import HistorySVG from '@/assets/icons/ui-kit/history.svg'; import KeepSVG from '@/assets/icons/ui-kit/keep.svg'; import KeepOffSVG from '@/assets/icons/ui-kit/keep_off.svg'; import MarkdownCopySVG from '@/assets/icons/ui-kit/markdown_copy.svg'; -import { - Box, - DropdownMenu, - DropdownMenuOption, - Icon, - IconOptions, -} from '@/components'; -import { useCunninghamTheme } from '@/cunningham'; +import MoreSVG from '@/assets/icons/ui-kit/more_horiz.svg'; import { Doc, KEY_DOC, KEY_LIST_DOC, KEY_LIST_FAVORITE_DOC, - getEmojiAndTitle, useCopyDocLink, useCreateFavoriteDoc, useDeleteFavoriteDoc, - useDocTitleUpdate, useDocUtils, useDuplicateDoc, } from '@/docs/doc-management'; +import { useAuth } from '@/features/auth'; import { useFocusStore, useResponsiveStore } from '@/stores'; import { useCopyCurrentEditorToClipboard } from '../hooks/useCopyCurrentEditorToClipboard'; @@ -57,6 +51,14 @@ const ModalSelectVersion = dynamic( { ssr: false }, ); +const DocShareModal = dynamic( + () => + import('@/docs/doc-share/components/DocShareModal').then((mod) => ({ + default: mod.DocShareModal, + })), + { ssr: false }, +); + const ModalExport = process.env.NEXT_PUBLIC_PUBLISH_AS_MIT === 'false' ? dynamic( @@ -76,16 +78,18 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { const { t } = useTranslation(); const treeContext = useTreeContext(); const router = useRouter(); - const { isChild, isTopRoot } = useDocUtils(doc); - - const { spacingsTokens, colorsTokens } = useCunninghamTheme(); + const { isTopRoot } = useDocUtils(doc); + const { authenticated } = useAuth(); + const copyCurrentEditorToClipboard = useCopyCurrentEditorToClipboard(); + const [openDropdown, setOpenDropdown] = useState(false); const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false); const [isModalExportOpen, setIsModalExportOpen] = useState(false); + const shareModal = useModal(); const selectHistoryModal = useModal(); - const { addLastFocus, restoreFocus } = useFocusStore(); - const { isSmallMobile, isMobile } = useResponsiveStore(); + const { restoreFocus } = useFocusStore(); + const { isMobile } = useResponsiveStore(); const copyDocLink = useCopyDocLink(doc.id); const { mutate: duplicateDoc } = useDuplicateDoc({ onSuccess: (data) => { @@ -99,19 +103,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { listInvalidQueries: [KEY_LIST_DOC, KEY_DOC, KEY_LIST_FAVORITE_DOC], }); - // Emoji Management - const { emoji } = getEmojiAndTitle(doc.title ?? ''); - const { updateDocEmoji } = useDocTitleUpdate(); - const options: DropdownMenuOption[] = [ - { - label: t('Export'), - icon: