From a5dd247fd03804770386fbc6d6ae307c721df33a Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Mon, 10 Nov 2025 13:26:57 +0100 Subject: [PATCH 1/3] tmp Signed-off-by: David BRAQUART --- .../save/save-spreadsheet-button.tsx | 43 ++++++++++++++++--- src/hooks/use-navigator-clipboard.ts | 34 +++++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 src/hooks/use-navigator-clipboard.ts diff --git a/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx b/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx index 5cd3f5b7e7..85061c883b 100644 --- a/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx +++ b/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx @@ -19,9 +19,11 @@ import { spreadsheetStyles } from '../../../spreadsheet.style'; import { useSelector } from 'react-redux'; import type { AppState } from '../../../../../redux/reducer'; import SaveNamingFilterDialog from './save-naming-filter-dialog'; +import { useClipboard } from 'hooks/use-navigator-clipboard'; enum SpreadsheetSaveOptionId { SAVE_MODEL = 'SAVE_MODEL', + COPY_CSV = 'COPY_CSV', EXPORT_CSV = 'EXPORT_CSV', SAVE_FILTER = 'SAVE_FILTER', } @@ -51,12 +53,14 @@ export default function SaveSpreadsheetButton({ const [anchorEl, setAnchorEl] = useState(null); const customSaveDialogOpen = useStateBoolean(false); const saveFilterDialogOpen = useStateBoolean(false); - const { downloadCSVData } = useCsvExport(); + const { downloadCSVData, getCSVData } = useCsvExport(); const language = useSelector((state: AppState) => state.computedLanguage); const handleClick = useCallback((event: MouseEvent) => setAnchorEl(event.currentTarget), []); const handleClose = useCallback(() => setAnchorEl(null), []); + const clipboard = useClipboard(); + const spreadsheetOptions = useMemo>( () => ({ [SpreadsheetSaveOptionId.SAVE_MODEL]: { @@ -64,6 +68,30 @@ export default function SaveSpreadsheetButton({ label: 'spreadsheet/save/options/model', action: customSaveDialogOpen.setTrue, }, + [SpreadsheetSaveOptionId.COPY_CSV]: { + id: SpreadsheetSaveOptionId.COPY_CSV, + label: 'CPY', + action: () => { + const getDataAsCsv = gridRef.current?.api.getDataAsCsv; + if (!getDataAsCsv) { + console.error('Export API is not available.'); + return; + } + // No active calculation: 1 pinned row (default empty line); some calculations: > 1 pinned rows + const calculationRowNumber = gridRef.current?.api.getGridOption('pinnedBottomRowData')?.length ?? 0; + const csvRows = getCSVData({ + // Filter out the rowIndex column and the hidden columns before exporting to CSV + columns: columns.filter((col) => col.colId !== ROW_INDEX_COLUMN_ID && !col.hide), + tableName: tableDefinition.name, + language: language, + getDataAsCsv, + skipPinnedBottom: calculationRowNumber === 1, // skip last empty line + }); + clipboard.copy(csvRows ?? ''); + console.error('DBG DBR', clipboard.state); + }, + disabled: dataSize === 0, + }, [SpreadsheetSaveOptionId.EXPORT_CSV]: { id: SpreadsheetSaveOptionId.EXPORT_CSV, label: 'spreadsheet/save/options/csv', @@ -73,7 +101,6 @@ export default function SaveSpreadsheetButton({ console.error('Export API is not available.'); return; } - // No active calculation: 1 pinned row (default empty line); some calculations: > 1 pinned rows const calculationRowNumber = gridRef.current?.api.getGridOption('pinnedBottomRowData')?.length ?? 0; downloadCSVData({ @@ -96,14 +123,16 @@ export default function SaveSpreadsheetButton({ }), [ customSaveDialogOpen.setTrue, - saveFilterDialogOpen.setTrue, dataSize, - columns, - downloadCSVData, - gridRef, - tableDefinition.name, + saveFilterDialogOpen.setTrue, tableDefinition.type, + tableDefinition.name, + gridRef, + getCSVData, + columns, language, + clipboard, + downloadCSVData, ] ); diff --git a/src/hooks/use-navigator-clipboard.ts b/src/hooks/use-navigator-clipboard.ts new file mode 100644 index 0000000000..c4f5525652 --- /dev/null +++ b/src/hooks/use-navigator-clipboard.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React from 'react'; + +type CopyState = 'READY' | 'SUCCESS' | Error; + +export const useClipboard = ({ delay = 2500 } = {}) => { + const [state, setState] = React.useState('READY'); + const [copyTimeout, setCopyTimeout] = React.useState>(); + + function handleCopyResult(result: CopyState) { + setState(result); + clearTimeout(copyTimeout); + setCopyTimeout(setTimeout(() => setState('READY'), delay)); + } + + function copy(valueToCopy: string) { + if ('clipboard' in navigator) { + navigator.clipboard + .writeText(valueToCopy) + .then(() => handleCopyResult('SUCCESS')) + .catch((error) => error instanceof Error && handleCopyResult(error)); + } else { + handleCopyResult(new Error('`useClipboard`: Navigation Clipboard is not supported')); + } + } + + return { copy, state }; +}; From 0154ee64ac9d9283578ac3d28625262590aae1e4 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Thu, 13 Nov 2025 15:24:06 +0100 Subject: [PATCH 2/3] clean code Signed-off-by: David BRAQUART --- .../save/save-spreadsheet-button.tsx | 96 +++++++++++-------- src/hooks/use-navigator-clipboard.ts | 34 ------- src/translations/spreadsheet-en.ts | 6 +- src/translations/spreadsheet-fr.ts | 6 +- 4 files changed, 66 insertions(+), 76 deletions(-) delete mode 100644 src/hooks/use-navigator-clipboard.ts diff --git a/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx b/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx index 85061c883b..f1a160db8d 100644 --- a/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx +++ b/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx @@ -5,12 +5,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { type MouseEvent, type RefObject, useCallback, useMemo, useState } from 'react'; +import { type MouseEvent, type RefObject, useCallback, useEffect, useMemo, useState } from 'react'; import { Button, Menu, MenuItem } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import SaveIcon from '@mui/icons-material/Save'; import SaveSpreadsheetDialog from './save-spreadsheet-dialog'; -import { EquipmentType, FILTER_EQUIPMENTS, useCsvExport, useStateBoolean } from '@gridsuite/commons-ui'; +import { + CsvDownloadProps, + EquipmentType, + FILTER_EQUIPMENTS, + useClipboard, + useCsvExport, + useSnackMessage, + useStateBoolean, +} from '@gridsuite/commons-ui'; import { ROW_INDEX_COLUMN_ID } from '../../../constants'; import { type SpreadsheetTabDefinition } from '../../../types/spreadsheet.type'; import type { AgGridReact } from 'ag-grid-react'; @@ -19,7 +27,6 @@ import { spreadsheetStyles } from '../../../spreadsheet.style'; import { useSelector } from 'react-redux'; import type { AppState } from '../../../../../redux/reducer'; import SaveNamingFilterDialog from './save-naming-filter-dialog'; -import { useClipboard } from 'hooks/use-navigator-clipboard'; enum SpreadsheetSaveOptionId { SAVE_MODEL = 'SAVE_MODEL', @@ -59,8 +66,44 @@ export default function SaveSpreadsheetButton({ const handleClick = useCallback((event: MouseEvent) => setAnchorEl(event.currentTarget), []); const handleClose = useCallback(() => setAnchorEl(null), []); + const { snackInfo, snackError } = useSnackMessage(); const clipboard = useClipboard(); + useEffect(() => { + if (clipboard.copyState === 'SUCCESS') { + snackInfo({ headerId: 'spreadsheet/clipboard/success' }); + clipboard.ready(); + } else if (clipboard.copyState === 'ERROR') { + snackError({ headerId: 'spreadsheet/clipboard/error' }); + clipboard.ready(); + } + }, [clipboard, snackInfo, snackError]); + + const getCsvProps = useCallback( + (csvCase: SpreadsheetSaveOptionId.COPY_CSV | SpreadsheetSaveOptionId.EXPORT_CSV) => { + const gridCsvFunction = + csvCase === SpreadsheetSaveOptionId.COPY_CSV + ? gridRef.current?.api.getDataAsCsv + : gridRef.current?.api.exportDataAsCsv; + if (!gridCsvFunction) { + console.error('Csv API is not available.'); + return; + } + // No active calculation: 1 pinned row (default empty line); some calculations: > 1 pinned rows + const calculationRowNumber = gridRef.current?.api.getGridOption('pinnedBottomRowData')?.length ?? 0; + return { + // Filter out the rowIndex column and the hidden columns before exporting to CSV + columns: columns.filter((col) => col.colId !== ROW_INDEX_COLUMN_ID && !col.hide), + tableName: tableDefinition.name, + language: language, + getDataAsCsv: csvCase === SpreadsheetSaveOptionId.COPY_CSV ? gridCsvFunction : undefined, + exportDataAsCsv: csvCase === SpreadsheetSaveOptionId.COPY_CSV ? undefined : gridCsvFunction, + skipPinnedBottom: calculationRowNumber === 1, + } as CsvDownloadProps; + }, + [columns, gridRef, language, tableDefinition.name] + ); + const spreadsheetOptions = useMemo>( () => ({ [SpreadsheetSaveOptionId.SAVE_MODEL]: { @@ -70,47 +113,23 @@ export default function SaveSpreadsheetButton({ }, [SpreadsheetSaveOptionId.COPY_CSV]: { id: SpreadsheetSaveOptionId.COPY_CSV, - label: 'CPY', + label: 'spreadsheet/save/options/csv/copy', action: () => { - const getDataAsCsv = gridRef.current?.api.getDataAsCsv; - if (!getDataAsCsv) { - console.error('Export API is not available.'); - return; + const csvProps = getCsvProps(SpreadsheetSaveOptionId.COPY_CSV); + if (csvProps) { + clipboard.copy(getCSVData(csvProps) ?? ''); } - // No active calculation: 1 pinned row (default empty line); some calculations: > 1 pinned rows - const calculationRowNumber = gridRef.current?.api.getGridOption('pinnedBottomRowData')?.length ?? 0; - const csvRows = getCSVData({ - // Filter out the rowIndex column and the hidden columns before exporting to CSV - columns: columns.filter((col) => col.colId !== ROW_INDEX_COLUMN_ID && !col.hide), - tableName: tableDefinition.name, - language: language, - getDataAsCsv, - skipPinnedBottom: calculationRowNumber === 1, // skip last empty line - }); - clipboard.copy(csvRows ?? ''); - console.error('DBG DBR', clipboard.state); }, disabled: dataSize === 0, }, [SpreadsheetSaveOptionId.EXPORT_CSV]: { id: SpreadsheetSaveOptionId.EXPORT_CSV, - label: 'spreadsheet/save/options/csv', + label: 'spreadsheet/save/options/csv/export', action: () => { - const exportDataAsCsv = gridRef.current?.api.exportDataAsCsv; - if (!exportDataAsCsv) { - console.error('Export API is not available.'); - return; + const csvProps = getCsvProps(SpreadsheetSaveOptionId.EXPORT_CSV); + if (csvProps) { + downloadCSVData(csvProps); } - // No active calculation: 1 pinned row (default empty line); some calculations: > 1 pinned rows - const calculationRowNumber = gridRef.current?.api.getGridOption('pinnedBottomRowData')?.length ?? 0; - downloadCSVData({ - // Filter out the rowIndex column and the hidden columns before exporting to CSV - columns: columns.filter((col) => col.colId !== ROW_INDEX_COLUMN_ID && !col.hide), - tableName: tableDefinition.name, - language: language, - exportDataAsCsv, - skipPinnedBottom: calculationRowNumber === 1, // skip last empty line - }); }, disabled: dataSize === 0, }, @@ -126,12 +145,9 @@ export default function SaveSpreadsheetButton({ dataSize, saveFilterDialogOpen.setTrue, tableDefinition.type, - tableDefinition.name, - gridRef, - getCSVData, - columns, - language, + getCsvProps, clipboard, + getCSVData, downloadCSVData, ] ); diff --git a/src/hooks/use-navigator-clipboard.ts b/src/hooks/use-navigator-clipboard.ts deleted file mode 100644 index c4f5525652..0000000000 --- a/src/hooks/use-navigator-clipboard.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import React from 'react'; - -type CopyState = 'READY' | 'SUCCESS' | Error; - -export const useClipboard = ({ delay = 2500 } = {}) => { - const [state, setState] = React.useState('READY'); - const [copyTimeout, setCopyTimeout] = React.useState>(); - - function handleCopyResult(result: CopyState) { - setState(result); - clearTimeout(copyTimeout); - setCopyTimeout(setTimeout(() => setState('READY'), delay)); - } - - function copy(valueToCopy: string) { - if ('clipboard' in navigator) { - navigator.clipboard - .writeText(valueToCopy) - .then(() => handleCopyResult('SUCCESS')) - .catch((error) => error instanceof Error && handleCopyResult(error)); - } else { - handleCopyResult(new Error('`useClipboard`: Navigation Clipboard is not supported')); - } - } - - return { copy, state }; -}; diff --git a/src/translations/spreadsheet-en.ts b/src/translations/spreadsheet-en.ts index f782fc51e5..dd988c6bec 100644 --- a/src/translations/spreadsheet-en.ts +++ b/src/translations/spreadsheet-en.ts @@ -43,7 +43,8 @@ const spreadsheetEn = { 'spreadsheet/save/options/model': 'As a model', 'spreadsheet/save/options/filter': 'As a filter', 'spreadsheet/save/options/collection': 'Collection', - 'spreadsheet/save/options/csv': 'Export CSV', + 'spreadsheet/save/options/csv/copy': 'Copy CSV', + 'spreadsheet/save/options/csv/export': 'Export CSV', 'spreadsheet/save/dialog_title': 'Save a spreadsheet model', 'spreadsheet/collection/save/collection_name_dialog_title': 'Save a spreadsheet collection', 'spreadsheet/collection/save/button_tooltip': 'Save collection', @@ -139,6 +140,9 @@ const spreadsheetEn = { 'spreadsheet/global-model-edition/replace': 'Replace in formula', 'spreadsheet/global-model-edition/replace_all': 'Replace all', + 'spreadsheet/clipboard/success': 'Sheet content copied to system clipboard', + 'spreadsheet/clipboard/error': 'Could not copy sheet content to system clipboard', + // calculations 'spreadsheet/calculation/sum': 'Sum', 'spreadsheet/calculation/average': 'Average', diff --git a/src/translations/spreadsheet-fr.ts b/src/translations/spreadsheet-fr.ts index 335c1fa99d..6826e99eed 100644 --- a/src/translations/spreadsheet-fr.ts +++ b/src/translations/spreadsheet-fr.ts @@ -45,7 +45,8 @@ const spreadsheetFr = { 'spreadsheet/save/options/model': 'En tant que modèle', 'spreadsheet/save/options/filter': 'En tant que filtre', 'spreadsheet/save/options/collection': 'Collection', - 'spreadsheet/save/options/csv': 'Export CSV', + 'spreadsheet/save/options/csv/copy': 'Copie CSV', + 'spreadsheet/save/options/csv/export': 'Export CSV', 'spreadsheet/save/dialog_title': 'Enregistrer un modèle de tableur', 'spreadsheet/collection/save/collection_name_dialog_title': 'Enregistrer une collection de tableurs', 'spreadsheet/save/error_message': 'Une erreur est survenue lors de la création du modèle de tableur', @@ -144,6 +145,9 @@ const spreadsheetFr = { 'spreadsheet/global-model-edition/replace': 'Remplacer dans formule', 'spreadsheet/global-model-edition/replace_all': 'Tout remplacer', + 'spreadsheet/clipboard/success': 'Le contenu de la feuille a été copié dans le presse-papier système', + 'spreadsheet/clipboard/error': "Le contenu de la feuille n'a pas pu être copié dans le presse-papier système", + // calculations 'spreadsheet/calculation/sum': 'Somme', 'spreadsheet/calculation/average': 'Moyenne', From 98e12d848b683c10431b8354372e9abaede3c10f Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Thu, 13 Nov 2025 18:59:01 +0100 Subject: [PATCH 3/3] dont need a hook nor useEffect Signed-off-by: David BRAQUART --- .../save/save-spreadsheet-button.tsx | 30 +++++++++---------- src/translations/spreadsheet-en.ts | 7 ++--- src/translations/spreadsheet-fr.ts | 9 +++--- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx b/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx index f1a160db8d..0a0488b292 100644 --- a/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx +++ b/src/components/spreadsheet-view/spreadsheet/spreadsheet-toolbar/save/save-spreadsheet-button.tsx @@ -5,16 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { type MouseEvent, type RefObject, useCallback, useEffect, useMemo, useState } from 'react'; +import { type MouseEvent, type RefObject, useCallback, useMemo, useState } from 'react'; import { Button, Menu, MenuItem } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import SaveIcon from '@mui/icons-material/Save'; import SaveSpreadsheetDialog from './save-spreadsheet-dialog'; import { + copyToClipboard, CsvDownloadProps, EquipmentType, FILTER_EQUIPMENTS, - useClipboard, useCsvExport, useSnackMessage, useStateBoolean, @@ -60,24 +60,21 @@ export default function SaveSpreadsheetButton({ const [anchorEl, setAnchorEl] = useState(null); const customSaveDialogOpen = useStateBoolean(false); const saveFilterDialogOpen = useStateBoolean(false); - const { downloadCSVData, getCSVData } = useCsvExport(); const language = useSelector((state: AppState) => state.computedLanguage); const handleClick = useCallback((event: MouseEvent) => setAnchorEl(event.currentTarget), []); const handleClose = useCallback(() => setAnchorEl(null), []); const { snackInfo, snackError } = useSnackMessage(); - const clipboard = useClipboard(); + const { downloadCSVData, getCSVData } = useCsvExport(); + + const onClipboardCopy = useCallback(() => { + snackInfo({ headerId: 'spreadsheet/save/options/csv/clipboard/success' }); + }, [snackInfo]); - useEffect(() => { - if (clipboard.copyState === 'SUCCESS') { - snackInfo({ headerId: 'spreadsheet/clipboard/success' }); - clipboard.ready(); - } else if (clipboard.copyState === 'ERROR') { - snackError({ headerId: 'spreadsheet/clipboard/error' }); - clipboard.ready(); - } - }, [clipboard, snackInfo, snackError]); + const onClipboardError = useCallback(() => { + snackError({ headerId: 'spreadsheet/save/options/csv/clipboard/error' }); + }, [snackError]); const getCsvProps = useCallback( (csvCase: SpreadsheetSaveOptionId.COPY_CSV | SpreadsheetSaveOptionId.EXPORT_CSV) => { @@ -113,11 +110,11 @@ export default function SaveSpreadsheetButton({ }, [SpreadsheetSaveOptionId.COPY_CSV]: { id: SpreadsheetSaveOptionId.COPY_CSV, - label: 'spreadsheet/save/options/csv/copy', + label: 'spreadsheet/save/options/csv/clipboard', action: () => { const csvProps = getCsvProps(SpreadsheetSaveOptionId.COPY_CSV); if (csvProps) { - clipboard.copy(getCSVData(csvProps) ?? ''); + copyToClipboard(getCSVData(csvProps) ?? '', onClipboardCopy, onClipboardError); } }, disabled: dataSize === 0, @@ -146,8 +143,9 @@ export default function SaveSpreadsheetButton({ saveFilterDialogOpen.setTrue, tableDefinition.type, getCsvProps, - clipboard, getCSVData, + onClipboardCopy, + onClipboardError, downloadCSVData, ] ); diff --git a/src/translations/spreadsheet-en.ts b/src/translations/spreadsheet-en.ts index dd988c6bec..07d1e4e18f 100644 --- a/src/translations/spreadsheet-en.ts +++ b/src/translations/spreadsheet-en.ts @@ -43,8 +43,10 @@ const spreadsheetEn = { 'spreadsheet/save/options/model': 'As a model', 'spreadsheet/save/options/filter': 'As a filter', 'spreadsheet/save/options/collection': 'Collection', - 'spreadsheet/save/options/csv/copy': 'Copy CSV', 'spreadsheet/save/options/csv/export': 'Export CSV', + 'spreadsheet/save/options/csv/clipboard': 'Copy CSV', + 'spreadsheet/save/options/csv/clipboard/success': 'Sheet content copied to system clipboard', + 'spreadsheet/save/options/csv/clipboard/error': 'Could not copy sheet content to system clipboard', 'spreadsheet/save/dialog_title': 'Save a spreadsheet model', 'spreadsheet/collection/save/collection_name_dialog_title': 'Save a spreadsheet collection', 'spreadsheet/collection/save/button_tooltip': 'Save collection', @@ -140,9 +142,6 @@ const spreadsheetEn = { 'spreadsheet/global-model-edition/replace': 'Replace in formula', 'spreadsheet/global-model-edition/replace_all': 'Replace all', - 'spreadsheet/clipboard/success': 'Sheet content copied to system clipboard', - 'spreadsheet/clipboard/error': 'Could not copy sheet content to system clipboard', - // calculations 'spreadsheet/calculation/sum': 'Sum', 'spreadsheet/calculation/average': 'Average', diff --git a/src/translations/spreadsheet-fr.ts b/src/translations/spreadsheet-fr.ts index 6826e99eed..8bebc0c215 100644 --- a/src/translations/spreadsheet-fr.ts +++ b/src/translations/spreadsheet-fr.ts @@ -45,8 +45,12 @@ const spreadsheetFr = { 'spreadsheet/save/options/model': 'En tant que modèle', 'spreadsheet/save/options/filter': 'En tant que filtre', 'spreadsheet/save/options/collection': 'Collection', - 'spreadsheet/save/options/csv/copy': 'Copie CSV', 'spreadsheet/save/options/csv/export': 'Export CSV', + 'spreadsheet/save/options/csv/clipboard': 'Copie CSV', + 'spreadsheet/save/options/csv/clipboard/success': + 'Le contenu de la feuille a été copié dans le presse-papier système', + 'spreadsheet/save/options/csv/clipboard/error': + "Le contenu de la feuille n'a pas pu être copié dans le presse-papier système", 'spreadsheet/save/dialog_title': 'Enregistrer un modèle de tableur', 'spreadsheet/collection/save/collection_name_dialog_title': 'Enregistrer une collection de tableurs', 'spreadsheet/save/error_message': 'Une erreur est survenue lors de la création du modèle de tableur', @@ -145,9 +149,6 @@ const spreadsheetFr = { 'spreadsheet/global-model-edition/replace': 'Remplacer dans formule', 'spreadsheet/global-model-edition/replace_all': 'Tout remplacer', - 'spreadsheet/clipboard/success': 'Le contenu de la feuille a été copié dans le presse-papier système', - 'spreadsheet/clipboard/error': "Le contenu de la feuille n'a pas pu être copié dans le presse-papier système", - // calculations 'spreadsheet/calculation/sum': 'Somme', 'spreadsheet/calculation/average': 'Moyenne',