From 8a4b51189736cc09ec1ca9cc59d3659d819269d6 Mon Sep 17 00:00:00 2001 From: Tony CABAYE Date: Thu, 20 Feb 2025 16:10:11 +0100 Subject: [PATCH] feat: update TextCopyToClipboard component with new specifications --- assets/components/Utils/MenuList.tsx | 5 +- .../components/Utils/TextCopyToClipboard.tsx | 120 ++++++++++-------- .../DatasheetView/MetadataTab/MetadataTab.tsx | 73 ++++++----- .../ServiceListTab/ServicesListItem.tsx | 5 +- .../pages/service/view/DiffuseServiceTab.tsx | 19 +-- .../keys/UserKeysListTab/UserKeyLink.tsx | 65 ++++++---- .../keys/UserKeysListTab/UserKeysListTab.tsx | 1 - assets/entrepot/pages/users/me/Me.tsx | 2 +- assets/hooks/useCopyToClipboard.ts | 28 ++-- assets/utils/browser.ts | 3 + assets/utils/index.ts | 1 + 11 files changed, 191 insertions(+), 131 deletions(-) create mode 100644 assets/utils/browser.ts diff --git a/assets/components/Utils/MenuList.tsx b/assets/components/Utils/MenuList.tsx index 942192d3..c1f427ab 100644 --- a/assets/components/Utils/MenuList.tsx +++ b/assets/components/Utils/MenuList.tsx @@ -8,6 +8,7 @@ import { FC, memo, MouseEvent, useId, useMemo, useState } from "react"; import { symToStr } from "tsafe/symToStr"; type MenuListItemCommon = { + autoClose?: boolean; disabled?: boolean; text: string; iconId?: FrIconClassName | RiIconClassName; @@ -98,7 +99,9 @@ const MenuList: FC = ({ menuOpenButtonProps, items = [], disabled ) => { - handleClose(); + if (item.autoClose !== false) { + handleClose(); + } item.onClick?.(e); }} disabled={item.disabled} diff --git a/assets/components/Utils/TextCopyToClipboard.tsx b/assets/components/Utils/TextCopyToClipboard.tsx index 1fcba2e8..58620313 100644 --- a/assets/components/Utils/TextCopyToClipboard.tsx +++ b/assets/components/Utils/TextCopyToClipboard.tsx @@ -1,43 +1,66 @@ import { fr } from "@codegouvfr/react-dsfr"; import Button from "@codegouvfr/react-dsfr/Button"; -import { FC, memo, ReactNode } from "react"; +import Input, { InputProps } from "@codegouvfr/react-dsfr/Input"; +import { FC, memo } from "react"; import { symToStr } from "tsafe/symToStr"; import { tss } from "tss-react"; import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { useTranslation } from "@/i18n"; -type TextCopyToClipboardProps = { - label?: string; +interface TextCopyToClipboardProps extends InputProps.Common { + disabled?: boolean; + nativeInputProps?: InputProps.RegularInput["nativeInputProps"]; + nativeTextAreaProps?: InputProps.TextArea["nativeTextAreaProps"]; text: string; + textArea?: boolean; title?: string; - className?: string; - successMessage?: string; - disabled?: boolean; - children?: ReactNode; -}; +} const TextCopyToClipboard: FC = (props) => { - const { t: tCommon } = useTranslation("Common"); - const { children, label, text, title = tCommon("copy_to_clipboard"), successMessage, className, disabled = false } = props; - const copy = useCopyToClipboard(); - const { classes, cx } = useStyles({ disabled }); - - function copyToClipboard() { - copy(text, successMessage); - } + const { t } = useTranslation("Common"); + const { disabled = false, label, nativeInputProps, nativeTextAreaProps, text, textArea, title = t("copy_to_clipboard"), ...inputProps } = props; + const { classes } = useStyles({ disabled }); + const { copied, copy } = useCopyToClipboard(); return ( -
- {label && ( - - {label} : - - )} - {text} -
+ + {label} + + + } + disabled={disabled} + /> ); }; @@ -45,29 +68,24 @@ TextCopyToClipboard.displayName = symToStr({ TextCopyToClipboard }); export default memo(TextCopyToClipboard); -const useStyles = tss - .withName(TextCopyToClipboard.displayName) - .withParams<{ disabled: boolean }>() - .create(({ disabled }) => ({ - root: { - display: "flex", - flexDirection: "row", - width: "100%", - position: "relative", - gap: fr.spacing("2v"), - flexWrap: "nowrap", - alignItems: "center", - }, - textBox: { - display: "block", - flex: "1", - borderRadius: "0.25rem 0.25rem 0 0", - color: disabled ? fr.colors.decisions.text.disabled.grey.default : fr.colors.decisions.text.default.grey.default, - userSelect: disabled ? "none" : "auto", - cursor: disabled ? "not-allowed" : "auto", - overflow: disabled ? "hidden" : "auto", - backgroundColor: disabled ? fr.colors.decisions.background.disabled.grey.default : fr.colors.decisions.background.alt.blueFrance.default, - padding: fr.spacing("2v"), - whiteSpace: "pre", +const useStyles = tss.withName(TextCopyToClipboard.displayName).create(() => ({ + label: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + }, + button: { + [fr.breakpoints.down("lg")]: { + maxWidth: "2.5rem", + paddingLeft: "0.5rem", + paddingRight: "0.5rem", + "& span": { + display: "none", + }, + "&::after": { + "--icon-size": "1.5rem !important", + margin: "0 !important", + }, }, - })); + }, +})); diff --git a/assets/entrepot/pages/datasheet/DatasheetView/MetadataTab/MetadataTab.tsx b/assets/entrepot/pages/datasheet/DatasheetView/MetadataTab/MetadataTab.tsx index f438537b..759a7c3d 100644 --- a/assets/entrepot/pages/datasheet/DatasheetView/MetadataTab/MetadataTab.tsx +++ b/assets/entrepot/pages/datasheet/DatasheetView/MetadataTab/MetadataTab.tsx @@ -188,10 +188,14 @@ const MetadataTab: FC = ({ datastoreId, metadataQuery }) => { key={`${layer.offering_id}_${layer.name}`} title={layer.name} content={ - <> - {"Type"} : {layer.endpoint_type} - - + + {"Type"} : {layer.endpoint_type} + + } + text={layer.endpoint_url ?? ""} + /> } /> )) ?? ""} @@ -203,14 +207,18 @@ const MetadataTab: FC = ({ datastoreId, metadataQuery }) => { key={`${styleFile.url}`} title={styleFile.name} content={ - <> - {styleFile?.description !== "" && ( - <> - Description : {styleFile.description} - - )} - - + + Description : {styleFile.description} + + ) : ( + "" + ) + } + text={styleFile.url ?? ""} + /> } /> )) ?? ""} @@ -222,14 +230,18 @@ const MetadataTab: FC = ({ datastoreId, metadataQuery }) => { key={`${capFile.url}`} title={capFile.name} content={ - <> - {capFile?.description !== "" && ( - <> - Description : {capFile.description} - - )} - - + + Description : {capFile.description} + + ) : ( + "" + ) + } + text={capFile.url ?? ""} + /> } /> )) ?? ""} @@ -241,15 +253,18 @@ const MetadataTab: FC = ({ datastoreId, metadataQuery }) => { key={`${document.url}`} title={document.name} content={ - <> - {document?.description !== null && document?.description !== "" && ( - <> - Description : {document.description} - - )} - - - + + Description : {document.description} + + ) : ( + "" + ) + } + text={document.url ?? ""} + /> } /> )) ?? ""} diff --git a/assets/entrepot/pages/datasheet/DatasheetView/ServiceListTab/ServicesListItem.tsx b/assets/entrepot/pages/datasheet/DatasheetView/ServiceListTab/ServicesListItem.tsx index 8cc15965..c56b8381 100644 --- a/assets/entrepot/pages/datasheet/DatasheetView/ServiceListTab/ServicesListItem.tsx +++ b/assets/entrepot/pages/datasheet/DatasheetView/ServiceListTab/ServicesListItem.tsx @@ -27,7 +27,7 @@ type ServicesListItemProps = { const ServicesListItem: FC = ({ service, datasheetName, datastoreId }) => { const queryClient = useQueryClient(); const setMessage = useSnackbarStore((state) => state.setMessage); - const copy = useCopyToClipboard(); + const { copied, copy } = useCopyToClipboard(); const unpublishServiceConfirmModal = createModal({ id: `unpublish-service-confirm-modal-${service._id}`, @@ -68,8 +68,9 @@ const ServicesListItem: FC = ({ service, datasheetName, d date={service?.configuration?.last_event?.date} menuListItems={[ { + autoClose: false, text: "Copier l’URL de diffusion", - iconId: "ri-file-copy-2-line", + iconId: copied ? "fr-icon-check-line" : "ri-file-copy-line", onClick: async () => { if (!service.share_url) { setMessage("URL de diffusion indisponible"); diff --git a/assets/entrepot/pages/service/view/DiffuseServiceTab.tsx b/assets/entrepot/pages/service/view/DiffuseServiceTab.tsx index 3edc007f..450e23e0 100644 --- a/assets/entrepot/pages/service/view/DiffuseServiceTab.tsx +++ b/assets/entrepot/pages/service/view/DiffuseServiceTab.tsx @@ -29,11 +29,12 @@ const DiffuseServiceTab: FC = ({ service }) => {