mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
dd2e8f258d
* Initial plan * Support double-click submit in document explorer * Remove test * Fix double-click submit in document explorer Single click now sets the selection instead of toggling it, so the two clicks preceding a dblclick no longer flicker the selection on/off. Submit handlers accept the node directly to avoid the stale-state race across the click sequence, and button onClick handlers are wrapped so the synthetic MouseEvent isn't passed in as the path argument. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * PR feedback --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Tom Moor <tom@getoutline.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import { observer } from "mobx-react";
|
||
import * as React from "react";
|
||
import { Trans, useTranslation } from "react-i18next";
|
||
import { toast } from "sonner";
|
||
import styled from "styled-components";
|
||
import type { NavigationNode } from "@shared/types";
|
||
import type Document from "~/models/Document";
|
||
import Button from "~/components/Button";
|
||
import Switch from "~/components/Switch";
|
||
import Text from "~/components/Text";
|
||
import useCollectionTrees from "~/hooks/useCollectionTrees";
|
||
import useStores from "~/hooks/useStores";
|
||
import { FlexContainer, Footer } from "./Components";
|
||
import DocumentExplorer from "./DocumentExplorer";
|
||
|
||
type Props = {
|
||
/** The original document to duplicate */
|
||
document: Document;
|
||
onSubmit: (documents: Document[]) => void;
|
||
};
|
||
|
||
function DocumentCopy({ document, onSubmit }: Props) {
|
||
const { t } = useTranslation();
|
||
const { policies } = useStores();
|
||
const collectionTrees = useCollectionTrees();
|
||
const [publish, setPublish] = React.useState<boolean>(!!document.publishedAt);
|
||
const [copying, setCopying] = React.useState<boolean>(false);
|
||
const [recursive, setRecursive] = React.useState<boolean>(true);
|
||
const [selectedPath, selectPath] = React.useState<NavigationNode | null>(
|
||
null
|
||
);
|
||
|
||
const items = React.useMemo(() => {
|
||
const nodes = collectionTrees.filter((node) =>
|
||
node.collectionId
|
||
? policies.get(node.collectionId)?.abilities.createDocument
|
||
: true
|
||
);
|
||
|
||
return nodes;
|
||
}, [policies, collectionTrees]);
|
||
|
||
const copy = async (path = selectedPath) => {
|
||
if (!path) {
|
||
toast.message(t("Select a location to copy"));
|
||
return;
|
||
}
|
||
|
||
try {
|
||
setCopying(true);
|
||
const result = await document.duplicate({
|
||
publish,
|
||
recursive,
|
||
title: document.title,
|
||
collectionId: path.collectionId,
|
||
...(path.type === "document" ? { parentDocumentId: path.id } : {}),
|
||
});
|
||
|
||
toast.success(t("Document copied"));
|
||
onSubmit(result);
|
||
} catch (_err) {
|
||
toast.error(t("Couldn’t copy the document, try again?"));
|
||
} finally {
|
||
setCopying(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<FlexContainer column>
|
||
<DocumentExplorer
|
||
items={items}
|
||
onSubmit={copy}
|
||
onSelect={selectPath}
|
||
defaultValue={document.parentDocumentId || document.collectionId || ""}
|
||
/>
|
||
<OptionsContainer>
|
||
{document.collectionId && (
|
||
<Text size="small">
|
||
<Switch
|
||
name="publish"
|
||
label={t("Publish")}
|
||
labelPosition="right"
|
||
checked={publish}
|
||
onChange={setPublish}
|
||
/>
|
||
</Text>
|
||
)}
|
||
{document.publishedAt && document.childDocuments.length > 0 && (
|
||
<Text size="small">
|
||
<Switch
|
||
name="recursive"
|
||
label={t("Include nested documents")}
|
||
labelPosition="right"
|
||
checked={recursive}
|
||
onChange={setRecursive}
|
||
/>
|
||
</Text>
|
||
)}
|
||
</OptionsContainer>
|
||
<Footer justify="space-between" align="center" gap={8}>
|
||
<Text ellipsis type="secondary">
|
||
{selectedPath ? (
|
||
<Trans
|
||
defaults="Copy to <em>{{ location }}</em>"
|
||
values={{ location: selectedPath.title }}
|
||
components={{ em: <strong /> }}
|
||
/>
|
||
) : (
|
||
t("Select a location to copy")
|
||
)}
|
||
</Text>
|
||
<Button disabled={!selectedPath || copying} onClick={() => copy()}>
|
||
{copying ? `${t("Copying")}…` : t("Copy")}
|
||
</Button>
|
||
</Footer>
|
||
</FlexContainer>
|
||
);
|
||
}
|
||
|
||
const OptionsContainer = styled.div`
|
||
border-top: 1px solid ${(props) => props.theme.horizontalRule};
|
||
padding: 16px 24px 0;
|
||
margin-bottom: -1px;
|
||
background: ${(props) => props.theme.modalBackground};
|
||
z-index: 1;
|
||
`;
|
||
|
||
export default observer(DocumentCopy);
|