Skip to content

Commit

Permalink
feat: update TextCopyToClipboard component with new specifications
Browse files Browse the repository at this point in the history
  • Loading branch information
tonai committed Feb 20, 2025
1 parent d815925 commit 8a4b511
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 131 deletions.
5 changes: 4 additions & 1 deletion assets/components/Utils/MenuList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -98,7 +99,9 @@ const MenuList: FC<MenuListProps> = ({ menuOpenButtonProps, items = [], disabled
<MenuItem
key={i}
onClick={(e: MouseEvent<HTMLElement>) => {
handleClose();
if (item.autoClose !== false) {
handleClose();
}
item.onClick?.(e);
}}
disabled={item.disabled}
Expand Down
120 changes: 69 additions & 51 deletions assets/components/Utils/TextCopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,91 @@
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<TextCopyToClipboardProps> = (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 (
<div className={cx(classes.root, className)}>
{label && (
<span>
<strong>{label}</strong> :
</span>
)}
<span className={classes.textBox}>{text}</span>
<Button iconId="ri-file-copy-line" priority="tertiary no outline" title={title} onClick={copyToClipboard} disabled={disabled} />
{children}
</div>
<Input
{...inputProps}
{...(textArea
? {
textArea: true,
nativeTextAreaProps: {
...nativeTextAreaProps,
readOnly: true,
value: text,
},
}
: {
textArea: false,
nativeInputProps: {
...nativeInputProps,
readOnly: true,
value: text,
},
})}
label={
<div className={classes.label}>
{label}
<Button
className={classes.button}
disabled={disabled}
iconId={copied ? "fr-icon-check-line" : "ri-file-copy-line"}
iconPosition="right"
priority="tertiary"
title={title}
onClick={() => copy(text)}
>
<span>{copied ? t("alert_copied") : t("copy")}</span>
</Button>
</div>
}
disabled={disabled}
/>
);
};

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",
},
},
}));
},
}));
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,14 @@ const MetadataTab: FC<MetadataTabProps> = ({ datastoreId, metadataQuery }) => {
key={`${layer.offering_id}_${layer.name}`}
title={layer.name}
content={
<>
<strong>{"Type"}</strong> : {layer.endpoint_type}
<TextCopyToClipboard text={layer.endpoint_url ?? ""} />
</>
<TextCopyToClipboard
label={
<span>
<strong>{"Type"}</strong> : {layer.endpoint_type}
</span>
}
text={layer.endpoint_url ?? ""}
/>
}
/>
)) ?? ""}
Expand All @@ -203,14 +207,18 @@ const MetadataTab: FC<MetadataTabProps> = ({ datastoreId, metadataQuery }) => {
key={`${styleFile.url}`}
title={styleFile.name}
content={
<>
{styleFile?.description !== "" && (
<>
<strong>Description</strong> : {styleFile.description}
</>
)}
<TextCopyToClipboard text={styleFile.url ?? ""} />
</>
<TextCopyToClipboard
label={
styleFile?.description !== "" ? (
<span>
<strong>Description</strong> : {styleFile.description}
</span>
) : (
""
)
}
text={styleFile.url ?? ""}
/>
}
/>
)) ?? ""}
Expand All @@ -222,14 +230,18 @@ const MetadataTab: FC<MetadataTabProps> = ({ datastoreId, metadataQuery }) => {
key={`${capFile.url}`}
title={capFile.name}
content={
<>
{capFile?.description !== "" && (
<>
<strong>Description</strong> : {capFile.description}
</>
)}
<TextCopyToClipboard text={capFile.url ?? ""} />
</>
<TextCopyToClipboard
label={
capFile?.description !== "" ? (
<span>
<strong>Description</strong> : {capFile.description}
</span>
) : (
""
)
}
text={capFile.url ?? ""}
/>
}
/>
)) ?? ""}
Expand All @@ -241,15 +253,18 @@ const MetadataTab: FC<MetadataTabProps> = ({ datastoreId, metadataQuery }) => {
key={`${document.url}`}
title={document.name}
content={
<>
{document?.description !== null && document?.description !== "" && (
<>
<strong>Description</strong> : {document.description}
</>
)}

<TextCopyToClipboard text={document.url ?? ""} />
</>
<TextCopyToClipboard
label={
document?.description !== null && document?.description !== "" ? (
<span>
<strong>Description</strong> : {document.description}
</span>
) : (
""
)
}
text={document.url ?? ""}
/>
}
/>
)) ?? ""}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type ServicesListItemProps = {
const ServicesListItem: FC<ServicesListItemProps> = ({ 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}`,
Expand Down Expand Up @@ -68,8 +68,9 @@ const ServicesListItem: FC<ServicesListItemProps> = ({ 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");
Expand Down
19 changes: 10 additions & 9 deletions assets/entrepot/pages/service/view/DiffuseServiceTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ const DiffuseServiceTab: FC<DiffuseServiceTabProps> = ({ service }) => {
<TextCopyToClipboard text={"<iframe width='600' height='40..."} className="fr-mb-4w" disabled /> */}

{/* Adresse du service de données */}
<div className={fr.cx("fr-grid-row")}>
<strong>Adresse du service de données</strong>
</div>

<TextCopyToClipboard text={service?.share_url ?? "Indisponible"} disabled={!service?.share_url} className="fr-mb-1w" />
<TextCopyToClipboard
label="Adresse du service de données"
text={service?.share_url ?? "Indisponible"}
disabled={!service?.share_url}
className="fr-mb-1w"
/>

{currentStyleLayers && (
<>
Expand All @@ -42,13 +43,13 @@ const DiffuseServiceTab: FC<DiffuseServiceTabProps> = ({ service }) => {
</div>
{currentStyleLayers?.map((layer) => (
<div key={layer.annexe_id} className={fr.cx("fr-mt-2v")}>
<span>
{(() => {
<TextCopyToClipboard
label={(() => {
const splitLayerName = layer.name?.split(":");
return splitLayerName?.[1] ?? splitLayerName?.[0] ?? layer.name;
})()}
</span>
<TextCopyToClipboard text={layer.url} successMessage="URL copiée" />
text={layer.url}
/>
</div>
))}
</>
Expand Down
65 changes: 38 additions & 27 deletions assets/entrepot/pages/users/keys/UserKeysListTab/UserKeyLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,33 @@ const UserKeyLink: FC<UserKeyLinkProps> = ({ permissionId, hash, offeringId }) =
return (
<div className={fr.cx("fr-mb-3v")}>
{wmtsCapabilitiesUrl && (
<TextCopyToClipboard className={fr.cx("fr-mb-4v")} label="WMTS" text={wmtsCapabilitiesUrl} successMessage={tCommon("url_copied")}>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: wmtsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</TextCopyToClipboard>
<TextCopyToClipboard
addon={
<Button
title={tCommon("new_window")}
priority="secondary"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: wmtsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
}
className={fr.cx("fr-mb-4v")}
label="WMTS"
text={wmtsCapabilitiesUrl}
/>
)}
{tmsCapabilitiesUrl && (
<TextCopyToClipboard label="TMS" text={tmsCapabilitiesUrl} successMessage={tCommon("url_copied")}>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: tmsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</TextCopyToClipboard>
<TextCopyToClipboard
addon={
<Button
title={tCommon("new_window")}
priority="secondary"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: tmsCapabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
}
label="TMS"
text={tmsCapabilitiesUrl}
/>
)}
</div>
);
Expand All @@ -72,15 +79,19 @@ const UserKeyLink: FC<UserKeyLinkProps> = ({ permissionId, hash, offeringId }) =
const capabilitiesUrl = `${rootUrl}?service=${service}&version=${version}&request=GetCapabilities&apikey=${hash}`;

return (
<TextCopyToClipboard className={fr.cx("fr-mb-4v")} text={capabilitiesUrl} successMessage={tCommon("url_copied")}>
<Button
className={fr.cx("fr-ml-2v", "fr-col-3")}
title={tCommon("new_window")}
priority="tertiary no outline"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: capabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
</TextCopyToClipboard>
<TextCopyToClipboard
addon={
<Button
title={tCommon("new_window")}
priority="secondary"
iconId={"fr-icon-external-link-line"}
linkProps={{ href: capabilitiesUrl, target: "_blank", rel: "noreferrer" }}
/>
}
className={fr.cx("fr-mb-4v")}
label={offering?.type}
text={capabilitiesUrl}
/>
);
}
};
Expand Down
Loading

0 comments on commit 8a4b511

Please sign in to comment.