2024-07-27 21:08:16 +05:30
|
|
|
|
import capitalize from "lodash/capitalize";
|
2024-09-09 22:46:37 +05:30
|
|
|
|
import isEmpty from "lodash/isEmpty";
|
2025-02-22 20:23:19 +05:30
|
|
|
|
import noop from "lodash/noop";
|
2021-01-13 22:00:25 -08:00
|
|
|
|
import { observer } from "mobx-react";
|
2024-02-03 16:53:39 -05:00
|
|
|
|
import { EditIcon, InputIcon, RestoreIcon, SearchIcon } from "outline-icons";
|
2020-08-08 22:53:59 -07:00
|
|
|
|
import * as React from "react";
|
2021-01-13 22:00:25 -08:00
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
import { useHistory } from "react-router-dom";
|
2022-04-09 20:25:54 -07:00
|
|
|
|
import { useMenuState, MenuButton, MenuButtonHTMLProps } from "reakit/Menu";
|
2021-03-21 11:50:49 +05:30
|
|
|
|
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
2023-10-22 17:30:24 -04:00
|
|
|
|
import { toast } from "sonner";
|
2021-01-13 22:00:25 -08:00
|
|
|
|
import styled from "styled-components";
|
2021-12-19 13:58:16 -08:00
|
|
|
|
import breakpoint from "styled-components-breakpoint";
|
2024-04-29 19:56:42 -04:00
|
|
|
|
import { s } from "@shared/styles";
|
2025-02-22 20:23:19 +05:30
|
|
|
|
import { SubscriptionType, UserPreference } from "@shared/types";
|
2022-07-17 11:31:55 +01:00
|
|
|
|
import { getEventFiles } from "@shared/utils/files";
|
2021-11-29 06:40:55 -08:00
|
|
|
|
import Document from "~/models/Document";
|
|
|
|
|
import ContextMenu from "~/components/ContextMenu";
|
|
|
|
|
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
|
2021-12-19 13:58:16 -08:00
|
|
|
|
import Separator from "~/components/ContextMenu/Separator";
|
2021-11-29 06:40:55 -08:00
|
|
|
|
import Template from "~/components/ContextMenu/Template";
|
2022-12-31 12:26:39 -05:00
|
|
|
|
import CollectionIcon from "~/components/Icons/CollectionIcon";
|
2022-01-28 20:23:02 -08:00
|
|
|
|
import Switch from "~/components/Switch";
|
2021-12-30 16:54:02 -08:00
|
|
|
|
import { actionToMenuItem } from "~/actions";
|
2022-08-08 17:31:53 +02:00
|
|
|
|
import {
|
|
|
|
|
pinDocument,
|
2024-06-24 18:08:30 -04:00
|
|
|
|
createTemplateFromDocument,
|
2022-08-26 12:17:13 +05:30
|
|
|
|
subscribeDocument,
|
|
|
|
|
unsubscribeDocument,
|
2022-08-08 17:31:53 +02:00
|
|
|
|
moveDocument,
|
|
|
|
|
deleteDocument,
|
|
|
|
|
permanentlyDeleteDocument,
|
|
|
|
|
downloadDocument,
|
|
|
|
|
importDocument,
|
|
|
|
|
starDocument,
|
|
|
|
|
unstarDocument,
|
|
|
|
|
duplicateDocument,
|
|
|
|
|
archiveDocument,
|
2022-11-13 10:19:09 -08:00
|
|
|
|
openDocumentHistory,
|
|
|
|
|
openDocumentInsights,
|
2023-01-12 22:39:14 -05:00
|
|
|
|
publishDocument,
|
|
|
|
|
unpublishDocument,
|
2023-02-26 14:19:12 -05:00
|
|
|
|
printDocument,
|
|
|
|
|
openDocumentComments,
|
2023-09-10 15:46:12 -04:00
|
|
|
|
createDocumentFromTemplate,
|
|
|
|
|
createNestedDocument,
|
2023-11-21 22:27:54 -05:00
|
|
|
|
shareDocument,
|
2023-12-16 11:30:29 -05:00
|
|
|
|
copyDocument,
|
2024-04-25 22:44:15 -04:00
|
|
|
|
searchInDocument,
|
2024-11-09 09:45:59 -05:00
|
|
|
|
leaveDocument,
|
2024-07-27 21:08:16 +05:30
|
|
|
|
moveTemplate,
|
2022-08-08 17:31:53 +02:00
|
|
|
|
} from "~/actions/definitions/documents";
|
2021-12-30 16:54:02 -08:00
|
|
|
|
import useActionContext from "~/hooks/useActionContext";
|
2024-09-09 22:46:37 +05:30
|
|
|
|
import useBoolean from "~/hooks/useBoolean";
|
2023-09-04 19:19:43 -04:00
|
|
|
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
2022-03-27 19:52:11 -07:00
|
|
|
|
import useMobile from "~/hooks/useMobile";
|
2022-02-23 21:33:18 -08:00
|
|
|
|
import usePolicy from "~/hooks/usePolicy";
|
2022-10-13 05:18:43 +05:30
|
|
|
|
import useRequest from "~/hooks/useRequest";
|
2021-11-29 06:40:55 -08:00
|
|
|
|
import useStores from "~/hooks/useStores";
|
2025-02-22 20:23:19 +05:30
|
|
|
|
import { MenuItem, MenuItemButton } from "~/types";
|
2023-09-10 15:46:12 -04:00
|
|
|
|
import { documentEditPath } from "~/utils/routeHelpers";
|
2024-09-09 22:46:37 +05:30
|
|
|
|
import { MenuContext, useMenuContext } from "./MenuContext";
|
2017-05-17 19:36:31 -07:00
|
|
|
|
|
2021-11-29 06:40:55 -08:00
|
|
|
|
type Props = {
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Document for which the menu is to be shown */
|
2021-11-29 06:40:55 -08:00
|
|
|
|
document: Document;
|
|
|
|
|
isRevision?: boolean;
|
2021-12-19 13:58:16 -08:00
|
|
|
|
/** Pass true if the document is currently being displayed */
|
|
|
|
|
showDisplayOptions?: boolean;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Whether to display menu as a modal */
|
2021-11-29 06:40:55 -08:00
|
|
|
|
modal?: boolean;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Whether to include the option of toggling embeds as menu item */
|
2021-11-29 06:40:55 -08:00
|
|
|
|
showToggleEmbeds?: boolean;
|
|
|
|
|
showPin?: boolean;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Label for menu button */
|
2022-04-09 20:25:54 -07:00
|
|
|
|
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Invoked when the "Find and replace" menu item is clicked */
|
2024-01-10 05:07:05 -08:00
|
|
|
|
onFindAndReplace?: () => void;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Invoked when the "Rename" menu item is clicked */
|
2023-10-17 18:15:35 -04:00
|
|
|
|
onRename?: () => void;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Invoked when menu is opened */
|
2021-11-29 06:40:55 -08:00
|
|
|
|
onOpen?: () => void;
|
2024-09-09 22:46:37 +05:30
|
|
|
|
/** Invoked when menu is closed */
|
2021-11-29 06:40:55 -08:00
|
|
|
|
onClose?: () => void;
|
|
|
|
|
};
|
2017-05-17 19:36:31 -07:00
|
|
|
|
|
2024-09-09 22:46:37 +05:30
|
|
|
|
type MenuTriggerProps = {
|
|
|
|
|
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
|
|
|
|
|
onTrigger: () => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const MenuTrigger: React.FC<MenuTriggerProps> = ({ label, onTrigger }) => {
|
2021-01-13 22:00:25 -08:00
|
|
|
|
const { t } = useTranslation();
|
2024-09-09 22:46:37 +05:30
|
|
|
|
|
2025-02-20 10:14:21 +05:30
|
|
|
|
const { subscriptions, pins } = useStores();
|
2024-09-09 22:46:37 +05:30
|
|
|
|
const { model: document, menuState } = useMenuContext<Document>();
|
|
|
|
|
|
2025-02-20 10:14:21 +05:30
|
|
|
|
const {
|
|
|
|
|
loading: auxDataLoading,
|
|
|
|
|
loaded: auxDataLoaded,
|
|
|
|
|
request: auxDataRequest,
|
|
|
|
|
} = useRequest(() =>
|
|
|
|
|
Promise.all([
|
|
|
|
|
subscriptions.fetchOne({
|
|
|
|
|
documentId: document.id,
|
2025-02-22 20:23:19 +05:30
|
|
|
|
event: SubscriptionType.Document,
|
2025-02-20 10:14:21 +05:30
|
|
|
|
}),
|
2025-02-22 20:23:19 +05:30
|
|
|
|
document.collectionId
|
|
|
|
|
? subscriptions.fetchOne({
|
|
|
|
|
collectionId: document.collectionId,
|
|
|
|
|
event: SubscriptionType.Document,
|
|
|
|
|
})
|
|
|
|
|
: noop,
|
2025-02-20 10:14:21 +05:30
|
|
|
|
pins.fetchOne({
|
|
|
|
|
documentId: document.id,
|
|
|
|
|
collectionId: document.collectionId ?? null,
|
|
|
|
|
}),
|
|
|
|
|
])
|
2022-10-13 05:18:43 +05:30
|
|
|
|
);
|
|
|
|
|
|
2024-09-09 22:46:37 +05:30
|
|
|
|
const handlePointerEnter = React.useCallback(() => {
|
2025-02-20 10:14:21 +05:30
|
|
|
|
if (!auxDataLoading && !auxDataLoaded) {
|
|
|
|
|
void auxDataRequest();
|
2024-09-09 22:46:37 +05:30
|
|
|
|
void document.loadRelations();
|
2022-10-13 05:18:43 +05:30
|
|
|
|
}
|
2025-02-20 10:14:21 +05:30
|
|
|
|
}, [auxDataLoading, auxDataLoaded, auxDataRequest, document]);
|
2022-10-13 05:18:43 +05:30
|
|
|
|
|
2024-09-09 22:46:37 +05:30
|
|
|
|
return label ? (
|
|
|
|
|
<MenuButton
|
|
|
|
|
{...menuState}
|
|
|
|
|
onPointerEnter={handlePointerEnter}
|
|
|
|
|
onClick={onTrigger}
|
|
|
|
|
>
|
|
|
|
|
{label}
|
|
|
|
|
</MenuButton>
|
|
|
|
|
) : (
|
|
|
|
|
<OverflowMenuButton
|
|
|
|
|
aria-label={t("Show document menu")}
|
|
|
|
|
onPointerEnter={handlePointerEnter}
|
|
|
|
|
onClick={onTrigger}
|
|
|
|
|
{...menuState}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type MenuContentProps = {
|
|
|
|
|
onOpen?: () => void;
|
|
|
|
|
onClose?: () => void;
|
|
|
|
|
onFindAndReplace?: () => void;
|
|
|
|
|
onRename?: () => void;
|
|
|
|
|
showDisplayOptions?: boolean;
|
|
|
|
|
showToggleEmbeds?: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-05 23:01:16 -05:00
|
|
|
|
const MenuContent: React.FC<MenuContentProps> = observer(function MenuContent_({
|
2024-09-09 22:46:37 +05:30
|
|
|
|
onOpen,
|
|
|
|
|
onClose,
|
|
|
|
|
onFindAndReplace,
|
|
|
|
|
onRename,
|
|
|
|
|
showDisplayOptions,
|
|
|
|
|
showToggleEmbeds,
|
2024-12-05 23:01:16 -05:00
|
|
|
|
}) {
|
2024-09-09 22:46:37 +05:30
|
|
|
|
const user = useCurrentUser();
|
|
|
|
|
const { model: document, menuState } = useMenuContext<Document>();
|
|
|
|
|
const can = usePolicy(document);
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const { policies, collections } = useStores();
|
|
|
|
|
|
|
|
|
|
const collection = document.collectionId
|
|
|
|
|
? collections.get(document.collectionId)
|
|
|
|
|
: undefined;
|
|
|
|
|
|
|
|
|
|
const context = useActionContext({
|
|
|
|
|
isContextMenu: true,
|
|
|
|
|
activeDocumentId: document.id,
|
|
|
|
|
activeCollectionId: document.collectionId ?? undefined,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const isMobile = useMobile();
|
2019-01-19 00:23:39 -08:00
|
|
|
|
|
2021-01-13 22:00:25 -08:00
|
|
|
|
const handleRestore = React.useCallback(
|
2021-11-29 06:40:55 -08:00
|
|
|
|
async (
|
|
|
|
|
ev: React.SyntheticEvent,
|
|
|
|
|
options?: {
|
|
|
|
|
collectionId: string;
|
|
|
|
|
}
|
|
|
|
|
) => {
|
2021-01-13 22:00:25 -08:00
|
|
|
|
await document.restore(options);
|
2024-07-27 21:08:16 +05:30
|
|
|
|
toast.success(
|
|
|
|
|
t("{{ documentName }} restored", {
|
|
|
|
|
documentName: capitalize(document.noun),
|
|
|
|
|
})
|
|
|
|
|
);
|
2021-01-13 22:00:25 -08:00
|
|
|
|
},
|
2023-10-22 17:30:24 -04:00
|
|
|
|
[t, document]
|
2021-01-13 22:00:25 -08:00
|
|
|
|
);
|
2020-09-01 00:03:05 -03:00
|
|
|
|
|
2021-08-06 22:58:26 +05:30
|
|
|
|
const restoreItems = React.useMemo(
|
|
|
|
|
() => [
|
2021-11-29 06:40:55 -08:00
|
|
|
|
...collections.orderedData.reduce<MenuItem[]>((filtered, collection) => {
|
2021-08-06 22:58:26 +05:30
|
|
|
|
const can = policies.abilities(collection.id);
|
|
|
|
|
|
2023-04-30 09:38:47 -04:00
|
|
|
|
if (can.createDocument) {
|
2021-08-06 22:58:26 +05:30
|
|
|
|
filtered.push({
|
2021-11-29 06:40:55 -08:00
|
|
|
|
type: "button",
|
|
|
|
|
onClick: (ev) =>
|
|
|
|
|
handleRestore(ev, {
|
|
|
|
|
collectionId: collection.id,
|
|
|
|
|
}),
|
2024-04-29 19:56:42 -04:00
|
|
|
|
icon: <CollectionIcon collection={collection} />,
|
|
|
|
|
title: collection.name,
|
2021-08-06 22:58:26 +05:30
|
|
|
|
});
|
|
|
|
|
}
|
2021-11-29 06:40:55 -08:00
|
|
|
|
|
2021-08-06 22:58:26 +05:30
|
|
|
|
return filtered;
|
|
|
|
|
}, []),
|
|
|
|
|
],
|
|
|
|
|
[collections.orderedData, handleRestore, policies]
|
|
|
|
|
);
|
2024-09-09 22:46:37 +05:30
|
|
|
|
|
|
|
|
|
return !isEmpty(can) ? (
|
|
|
|
|
<ContextMenu
|
|
|
|
|
{...menuState}
|
|
|
|
|
aria-label={t("Document options")}
|
|
|
|
|
onOpen={onOpen}
|
|
|
|
|
onClose={onClose}
|
|
|
|
|
>
|
|
|
|
|
<Template
|
|
|
|
|
{...menuState}
|
|
|
|
|
items={[
|
|
|
|
|
{
|
|
|
|
|
type: "button",
|
|
|
|
|
title: t("Restore"),
|
|
|
|
|
visible:
|
2024-10-06 18:07:11 +05:30
|
|
|
|
!!(document.isWorkspaceTemplate || collection?.isActive) &&
|
|
|
|
|
!!(can.restore || can.unarchive),
|
2024-09-09 22:46:37 +05:30
|
|
|
|
onClick: (ev) => handleRestore(ev),
|
|
|
|
|
icon: <RestoreIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "submenu",
|
|
|
|
|
title: t("Restore"),
|
|
|
|
|
visible:
|
2024-10-06 18:07:11 +05:30
|
|
|
|
!(document.isWorkspaceTemplate || collection?.isActive) &&
|
|
|
|
|
!!(can.restore || can.unarchive) &&
|
2024-09-09 22:46:37 +05:30
|
|
|
|
restoreItems.length !== 0,
|
|
|
|
|
style: {
|
|
|
|
|
left: -170,
|
|
|
|
|
position: "relative",
|
|
|
|
|
top: -40,
|
|
|
|
|
},
|
|
|
|
|
icon: <RestoreIcon />,
|
|
|
|
|
hover: true,
|
|
|
|
|
items: [
|
|
|
|
|
{
|
|
|
|
|
type: "heading",
|
|
|
|
|
title: t("Choose a collection"),
|
|
|
|
|
},
|
|
|
|
|
...restoreItems,
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
actionToMenuItem(starDocument, context),
|
|
|
|
|
actionToMenuItem(unstarDocument, context),
|
2025-02-22 20:23:19 +05:30
|
|
|
|
{
|
|
|
|
|
...actionToMenuItem(subscribeDocument, context),
|
|
|
|
|
disabled: collection?.isSubscribed,
|
|
|
|
|
tooltip: collection?.isSubscribed
|
|
|
|
|
? t("Subscription inherited from collection")
|
|
|
|
|
: undefined,
|
|
|
|
|
} as MenuItemButton,
|
|
|
|
|
{
|
|
|
|
|
...actionToMenuItem(unsubscribeDocument, context),
|
|
|
|
|
disabled: collection?.isSubscribed,
|
|
|
|
|
tooltip: collection?.isSubscribed
|
|
|
|
|
? t("Subscription inherited from collection")
|
|
|
|
|
: undefined,
|
|
|
|
|
} as MenuItemButton,
|
2024-09-09 22:46:37 +05:30
|
|
|
|
{
|
|
|
|
|
type: "button",
|
|
|
|
|
title: `${t("Find and replace")}…`,
|
|
|
|
|
visible: !!onFindAndReplace && isMobile,
|
|
|
|
|
onClick: () => onFindAndReplace?.(),
|
|
|
|
|
icon: <SearchIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "separator",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "route",
|
|
|
|
|
title: t("Edit"),
|
|
|
|
|
to: documentEditPath(document),
|
|
|
|
|
visible:
|
|
|
|
|
!!can.update && user.separateEditMode && !document.template,
|
|
|
|
|
icon: <EditIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "button",
|
|
|
|
|
title: `${t("Rename")}…`,
|
|
|
|
|
visible: !!can.update && !user.separateEditMode && !!onRename,
|
|
|
|
|
onClick: () => onRename?.(),
|
|
|
|
|
icon: <InputIcon />,
|
|
|
|
|
},
|
|
|
|
|
actionToMenuItem(shareDocument, context),
|
|
|
|
|
actionToMenuItem(createNestedDocument, context),
|
|
|
|
|
actionToMenuItem(importDocument, context),
|
|
|
|
|
actionToMenuItem(createTemplateFromDocument, context),
|
|
|
|
|
actionToMenuItem(duplicateDocument, context),
|
|
|
|
|
actionToMenuItem(publishDocument, context),
|
|
|
|
|
actionToMenuItem(unpublishDocument, context),
|
|
|
|
|
actionToMenuItem(archiveDocument, context),
|
|
|
|
|
actionToMenuItem(moveDocument, context),
|
|
|
|
|
actionToMenuItem(moveTemplate, context),
|
|
|
|
|
actionToMenuItem(pinDocument, context),
|
|
|
|
|
actionToMenuItem(createDocumentFromTemplate, context),
|
|
|
|
|
{
|
|
|
|
|
type: "separator",
|
|
|
|
|
},
|
|
|
|
|
actionToMenuItem(openDocumentComments, context),
|
|
|
|
|
actionToMenuItem(openDocumentHistory, context),
|
|
|
|
|
actionToMenuItem(openDocumentInsights, context),
|
|
|
|
|
actionToMenuItem(downloadDocument, context),
|
|
|
|
|
actionToMenuItem(copyDocument, context),
|
|
|
|
|
actionToMenuItem(printDocument, context),
|
|
|
|
|
actionToMenuItem(searchInDocument, context),
|
|
|
|
|
{
|
|
|
|
|
type: "separator",
|
|
|
|
|
},
|
|
|
|
|
actionToMenuItem(deleteDocument, context),
|
|
|
|
|
actionToMenuItem(permanentlyDeleteDocument, context),
|
2024-11-09 09:45:59 -05:00
|
|
|
|
actionToMenuItem(leaveDocument, context),
|
2024-09-09 22:46:37 +05:30
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
{(showDisplayOptions || showToggleEmbeds) && can.update && (
|
|
|
|
|
<>
|
|
|
|
|
<Separator />
|
|
|
|
|
<DisplayOptions>
|
|
|
|
|
{showToggleEmbeds && (
|
|
|
|
|
<Style>
|
|
|
|
|
<ToggleMenuItem
|
|
|
|
|
width={26}
|
|
|
|
|
height={14}
|
|
|
|
|
label={t("Enable embeds")}
|
|
|
|
|
labelPosition="left"
|
|
|
|
|
checked={!document.embedsDisabled}
|
|
|
|
|
onChange={
|
|
|
|
|
document.embedsDisabled
|
|
|
|
|
? document.enableEmbeds
|
|
|
|
|
: document.disableEmbeds
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</Style>
|
|
|
|
|
)}
|
|
|
|
|
{showDisplayOptions && !isMobile && (
|
|
|
|
|
<Style>
|
|
|
|
|
<ToggleMenuItem
|
|
|
|
|
width={26}
|
|
|
|
|
height={14}
|
|
|
|
|
label={t("Full width")}
|
|
|
|
|
labelPosition="left"
|
|
|
|
|
checked={document.fullWidth}
|
|
|
|
|
onChange={(ev) => {
|
|
|
|
|
const fullWidth = ev.currentTarget.checked;
|
|
|
|
|
user.setPreference(
|
|
|
|
|
UserPreference.FullWidthDocuments,
|
|
|
|
|
fullWidth
|
|
|
|
|
);
|
|
|
|
|
void user.save();
|
|
|
|
|
document.fullWidth = fullWidth;
|
|
|
|
|
void document.save();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</Style>
|
|
|
|
|
)}
|
|
|
|
|
</DisplayOptions>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</ContextMenu>
|
|
|
|
|
) : null;
|
2024-12-05 23:01:16 -05:00
|
|
|
|
});
|
2024-09-09 22:46:37 +05:30
|
|
|
|
|
|
|
|
|
function DocumentMenu({
|
|
|
|
|
document,
|
|
|
|
|
modal = true,
|
|
|
|
|
showToggleEmbeds,
|
|
|
|
|
showDisplayOptions,
|
|
|
|
|
label,
|
|
|
|
|
onRename,
|
|
|
|
|
onOpen,
|
|
|
|
|
onClose,
|
|
|
|
|
}: Props) {
|
|
|
|
|
const { collections, documents } = useStores();
|
|
|
|
|
const menuState = useMenuState({
|
|
|
|
|
modal,
|
|
|
|
|
unstable_preventOverflow: true,
|
|
|
|
|
unstable_fixed: true,
|
|
|
|
|
unstable_flip: true,
|
|
|
|
|
});
|
|
|
|
|
const history = useHistory();
|
|
|
|
|
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const [isMenuVisible, showMenu] = useBoolean(false);
|
|
|
|
|
const file = React.useRef<HTMLInputElement>(null);
|
|
|
|
|
|
|
|
|
|
const collection = document.collectionId
|
|
|
|
|
? collections.get(document.collectionId)
|
|
|
|
|
: undefined;
|
|
|
|
|
|
2021-11-29 06:40:55 -08:00
|
|
|
|
const stopPropagation = React.useCallback((ev: React.SyntheticEvent) => {
|
2021-03-21 11:50:49 +05:30
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const handleFilePicked = React.useCallback(
|
2022-07-17 11:31:55 +01:00
|
|
|
|
async (ev: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
const files = getEventFiles(ev);
|
2021-03-21 11:50:49 +05:30
|
|
|
|
|
|
|
|
|
// Because this is the onChange handler it's possible for the change to be
|
|
|
|
|
// from previously selecting a file to not selecting a file – aka empty
|
|
|
|
|
if (!files.length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!collection) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const file = files[0];
|
|
|
|
|
const importedDocument = await documents.import(
|
|
|
|
|
file,
|
|
|
|
|
document.id,
|
|
|
|
|
collection.id,
|
|
|
|
|
{
|
|
|
|
|
publish: true,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
history.push(importedDocument.url);
|
|
|
|
|
} catch (err) {
|
2023-10-22 17:30:24 -04:00
|
|
|
|
toast.error(err.message);
|
2021-03-21 11:50:49 +05:30
|
|
|
|
throw err;
|
2024-11-06 23:28:23 -05:00
|
|
|
|
} finally {
|
|
|
|
|
ev.target.value = "";
|
2021-03-21 11:50:49 +05:30
|
|
|
|
}
|
|
|
|
|
},
|
2023-10-22 17:30:24 -04:00
|
|
|
|
[history, collection, documents, document.id]
|
2021-03-21 11:50:49 +05:30
|
|
|
|
);
|
|
|
|
|
|
2021-01-13 22:00:25 -08:00
|
|
|
|
return (
|
|
|
|
|
<>
|
2021-03-21 11:50:49 +05:30
|
|
|
|
<VisuallyHidden>
|
2022-03-16 23:41:06 -07:00
|
|
|
|
<label>
|
|
|
|
|
{t("Import document")}
|
|
|
|
|
<input
|
|
|
|
|
type="file"
|
|
|
|
|
ref={file}
|
|
|
|
|
onChange={handleFilePicked}
|
|
|
|
|
onClick={stopPropagation}
|
|
|
|
|
accept={documents.importFileTypes.join(", ")}
|
|
|
|
|
tabIndex={-1}
|
|
|
|
|
/>
|
|
|
|
|
</label>
|
2021-03-21 11:50:49 +05:30
|
|
|
|
</VisuallyHidden>
|
2024-09-09 22:46:37 +05:30
|
|
|
|
<MenuContext.Provider value={{ model: document, menuState }}>
|
|
|
|
|
<MenuTrigger label={label} onTrigger={showMenu} />
|
|
|
|
|
{isMenuVisible ? (
|
|
|
|
|
<MenuContent
|
|
|
|
|
onOpen={onOpen}
|
|
|
|
|
onClose={onClose}
|
|
|
|
|
onRename={onRename}
|
|
|
|
|
showDisplayOptions={showDisplayOptions}
|
|
|
|
|
showToggleEmbeds={showToggleEmbeds}
|
|
|
|
|
/>
|
|
|
|
|
) : null}
|
|
|
|
|
</MenuContext.Provider>
|
2021-01-13 22:00:25 -08:00
|
|
|
|
</>
|
|
|
|
|
);
|
2017-05-17 19:36:31 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-28 20:23:02 -08:00
|
|
|
|
const ToggleMenuItem = styled(Switch)`
|
|
|
|
|
* {
|
2021-12-19 13:58:16 -08:00
|
|
|
|
font-weight: normal;
|
2023-04-07 22:43:34 -04:00
|
|
|
|
color: ${s("textSecondary")};
|
2021-12-19 13:58:16 -08:00
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2023-12-29 17:34:26 -05:00
|
|
|
|
const DisplayOptions = styled.div`
|
|
|
|
|
padding: 8px 0 0;
|
|
|
|
|
`;
|
|
|
|
|
|
2021-12-19 13:58:16 -08:00
|
|
|
|
const Style = styled.div`
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
|
|
|
|
${breakpoint("tablet")`
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
`};
|
|
|
|
|
`;
|
|
|
|
|
|
2021-01-13 22:00:25 -08:00
|
|
|
|
export default observer(DocumentMenu);
|