feat: Add print, duplicate, template to command bar
This commit is contained in:
@ -1,11 +1,18 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
import invariant from "invariant";
|
||||||
import {
|
import {
|
||||||
|
DownloadIcon,
|
||||||
|
DuplicateIcon,
|
||||||
StarredIcon,
|
StarredIcon,
|
||||||
|
PrintIcon,
|
||||||
|
UnstarredIcon,
|
||||||
DocumentIcon,
|
DocumentIcon,
|
||||||
NewDocumentIcon,
|
NewDocumentIcon,
|
||||||
|
ShapesIcon,
|
||||||
ImportIcon,
|
ImportIcon,
|
||||||
} from "outline-icons";
|
} from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import DocumentTemplatize from "scenes/DocumentTemplatize";
|
||||||
import { createAction } from "actions";
|
import { createAction } from "actions";
|
||||||
import { DocumentSection } from "actions/sections";
|
import { DocumentSection } from "actions/sections";
|
||||||
import getDataTransferFiles from "utils/getDataTransferFiles";
|
import getDataTransferFiles from "utils/getDataTransferFiles";
|
||||||
@ -50,15 +57,113 @@ export const createDocument = createAction({
|
|||||||
activeCollectionId && history.push(newDocumentPath(activeCollectionId)),
|
activeCollectionId && history.push(newDocumentPath(activeCollectionId)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const starDocument = createAction({
|
||||||
|
name: ({ t }) => t("Star"),
|
||||||
|
section: DocumentSection,
|
||||||
|
icon: <StarredIcon />,
|
||||||
|
keywords: "favorite bookmark",
|
||||||
|
visible: ({ activeDocumentId, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
!document?.isStarred && stores.policies.abilities(activeDocumentId).star
|
||||||
|
);
|
||||||
|
},
|
||||||
|
perform: ({ activeDocumentId, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
document?.star();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const unstarDocument = createAction({
|
||||||
|
name: ({ t }) => t("Unstar"),
|
||||||
|
section: DocumentSection,
|
||||||
|
icon: <UnstarredIcon />,
|
||||||
|
keywords: "unfavorite unbookmark",
|
||||||
|
visible: ({ activeDocumentId, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
!!document?.isStarred &&
|
||||||
|
stores.policies.abilities(activeDocumentId).unstar
|
||||||
|
);
|
||||||
|
},
|
||||||
|
perform: ({ activeDocumentId, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
document?.unstar();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const downloadDocument = createAction({
|
||||||
|
name: ({ t, isContextMenu }) =>
|
||||||
|
isContextMenu ? t("Download") : t("Download document"),
|
||||||
|
section: DocumentSection,
|
||||||
|
icon: <DownloadIcon />,
|
||||||
|
keywords: "export",
|
||||||
|
visible: ({ activeDocumentId, stores }) =>
|
||||||
|
!!activeDocumentId && stores.policies.abilities(activeDocumentId).download,
|
||||||
|
perform: ({ activeDocumentId, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
document?.download();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const duplicateDocument = createAction({
|
||||||
|
name: ({ t, isContextMenu }) =>
|
||||||
|
isContextMenu ? t("Duplicate") : t("Duplicate document"),
|
||||||
|
section: DocumentSection,
|
||||||
|
icon: <DuplicateIcon />,
|
||||||
|
keywords: "copy",
|
||||||
|
visible: ({ activeDocumentId, stores }) =>
|
||||||
|
!!activeDocumentId && stores.policies.abilities(activeDocumentId).update,
|
||||||
|
perform: async ({ activeDocumentId, t, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
invariant(document, "Document must exist");
|
||||||
|
|
||||||
|
const duped = await document.duplicate();
|
||||||
|
|
||||||
|
// when duplicating, go straight to the duplicated document content
|
||||||
|
history.push(duped.url);
|
||||||
|
stores.toasts.showToast(t("Document duplicated"), { type: "success" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const printDocument = createAction({
|
||||||
|
name: ({ t, isContextMenu }) =>
|
||||||
|
isContextMenu ? t("Print") : t("Print document"),
|
||||||
|
section: DocumentSection,
|
||||||
|
icon: <PrintIcon />,
|
||||||
|
visible: ({ activeDocumentId }) => !!activeDocumentId,
|
||||||
|
perform: async () => {
|
||||||
|
window.print();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const importDocument = createAction({
|
export const importDocument = createAction({
|
||||||
name: ({ t }) => t("Import document"),
|
name: ({ t, activeDocumentId }) => t("Import document"),
|
||||||
section: DocumentSection,
|
section: DocumentSection,
|
||||||
icon: <ImportIcon />,
|
icon: <ImportIcon />,
|
||||||
keywords: "upload",
|
keywords: "upload",
|
||||||
visible: ({ activeCollectionId, stores }) =>
|
visible: ({ activeCollectionId, activeDocumentId, stores }) => {
|
||||||
!!activeCollectionId &&
|
if (activeDocumentId) {
|
||||||
stores.policies.abilities(activeCollectionId).update,
|
return !!stores.policies.abilities(activeDocumentId).createChildDocument;
|
||||||
perform: ({ activeCollectionId, stores }) => {
|
}
|
||||||
|
if (activeCollectionId) {
|
||||||
|
return !!stores.policies.abilities(activeCollectionId).update;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
perform: ({ activeCollectionId, activeDocumentId, stores }) => {
|
||||||
const { documents, toasts } = stores;
|
const { documents, toasts } = stores;
|
||||||
|
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
@ -71,7 +176,7 @@ export const importDocument = createAction({
|
|||||||
const file = files[0];
|
const file = files[0];
|
||||||
const document = await documents.import(
|
const document = await documents.import(
|
||||||
file,
|
file,
|
||||||
null,
|
activeDocumentId,
|
||||||
activeCollectionId,
|
activeCollectionId,
|
||||||
{
|
{
|
||||||
publish: true,
|
publish: true,
|
||||||
@ -90,8 +195,47 @@ export const importDocument = createAction({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const createTemplate = createAction({
|
||||||
|
name: ({ t }) => t("Templatize"),
|
||||||
|
section: DocumentSection,
|
||||||
|
icon: <ShapesIcon />,
|
||||||
|
keywords: "new create template",
|
||||||
|
visible: ({ activeCollectionId, activeDocumentId, stores }) => {
|
||||||
|
if (!activeDocumentId) return false;
|
||||||
|
|
||||||
|
const document = stores.documents.get(activeDocumentId);
|
||||||
|
invariant(document, "Document must exist");
|
||||||
|
|
||||||
|
return (
|
||||||
|
!!activeCollectionId &&
|
||||||
|
stores.policies.abilities(activeCollectionId).update &&
|
||||||
|
!document.isTemplate
|
||||||
|
);
|
||||||
|
},
|
||||||
|
perform: ({ activeDocumentId, stores, t, event }) => {
|
||||||
|
event?.preventDefault();
|
||||||
|
event?.stopPropagation();
|
||||||
|
|
||||||
|
stores.dialogs.openModal({
|
||||||
|
title: t("Create template"),
|
||||||
|
content: (
|
||||||
|
<DocumentTemplatize
|
||||||
|
documentId={activeDocumentId}
|
||||||
|
onSubmit={stores.dialogs.closeAllModals}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const rootDocumentActions = [
|
export const rootDocumentActions = [
|
||||||
openDocument,
|
openDocument,
|
||||||
createDocument,
|
createDocument,
|
||||||
|
createTemplate,
|
||||||
importDocument,
|
importDocument,
|
||||||
|
downloadDocument,
|
||||||
|
starDocument,
|
||||||
|
unstarDocument,
|
||||||
|
duplicateDocument,
|
||||||
|
printDocument,
|
||||||
];
|
];
|
||||||
|
@ -88,6 +88,10 @@ const Animator = styled(KBarAnimator)`
|
|||||||
${breakpoint("desktopLarge")`
|
${breakpoint("desktopLarge")`
|
||||||
max-width: 740px;
|
max-width: 740px;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default observer(CommandBar);
|
export default observer(CommandBar);
|
||||||
|
@ -26,5 +26,8 @@ export default function useCommandBarActions(actions: Action[]) {
|
|||||||
actions.map((action) => actionToKBar(action, context))
|
actions.map((action) => actionToKBar(action, context))
|
||||||
);
|
);
|
||||||
|
|
||||||
useRegisterActions(registerable, [registerable.length, location.pathname]);
|
useRegisterActions(registerable, [
|
||||||
|
...registerable.map((r) => r.id),
|
||||||
|
location.pathname,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,7 @@ function DocumentMenu({
|
|||||||
isOpen={showTemplateModal}
|
isOpen={showTemplateModal}
|
||||||
>
|
>
|
||||||
<DocumentTemplatize
|
<DocumentTemplatize
|
||||||
document={document}
|
documentId={document.id}
|
||||||
onSubmit={() => setShowTemplateModal(false)}
|
onSubmit={() => setShowTemplateModal(false)}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -1,26 +1,30 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
import invariant from "invariant";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation, Trans } from "react-i18next";
|
import { useTranslation, Trans } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import Document from "models/Document";
|
|
||||||
import Button from "components/Button";
|
import Button from "components/Button";
|
||||||
import Flex from "components/Flex";
|
import Flex from "components/Flex";
|
||||||
import HelpText from "components/HelpText";
|
import HelpText from "components/HelpText";
|
||||||
|
import useStores from "hooks/useStores";
|
||||||
import useToasts from "hooks/useToasts";
|
import useToasts from "hooks/useToasts";
|
||||||
import { documentUrl } from "utils/routeHelpers";
|
import { documentUrl } from "utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
document: Document,
|
documentId: string,
|
||||||
onSubmit: () => void,
|
onSubmit: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
function DocumentTemplatize({ document, onSubmit }: Props) {
|
function DocumentTemplatize({ documentId, onSubmit }: Props) {
|
||||||
const [isSaving, setIsSaving] = useState();
|
const [isSaving, setIsSaving] = useState();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { showToast } = useToasts();
|
const { showToast } = useToasts();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { documents } = useStores();
|
||||||
|
const document = documents.get(documentId);
|
||||||
|
invariant(document, "Document must exist");
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = React.useCallback(
|
||||||
async (ev: SyntheticEvent<>) => {
|
async (ev: SyntheticEvent<>) => {
|
||||||
|
Reference in New Issue
Block a user