diff --git a/packages/editor/dist/components/menu/menu.d.ts b/packages/editor/dist/components/menu/menu.d.ts index efd7acfdd..fd9896f7b 100644 --- a/packages/editor/dist/components/menu/menu.d.ts +++ b/packages/editor/dist/components/menu/menu.d.ts @@ -2,7 +2,6 @@ import { PropsWithChildren } from "react"; import { FlexProps } from "rebass"; import { MenuOptions } from "./useMenu"; import { MenuItem as MenuItemType } from "./types"; -import "react-spring-bottom-sheet/dist/style.css"; declare type MenuProps = MenuContainerProps & { items: MenuItemType[]; closeMenu: () => void; diff --git a/packages/editor/dist/components/menu/menu.js b/packages/editor/dist/components/menu/menu.js index c6ab846c5..04627c01b 100644 --- a/packages/editor/dist/components/menu/menu.js +++ b/packages/editor/dist/components/menu/menu.js @@ -44,8 +44,7 @@ import { getPosition } from "./useMenu"; import MenuItem from "./menuitem"; // import { useMenuTrigger, useMenu, getPosition } from "../../hooks/useMenu"; import Modal from "react-modal"; -import { BottomSheet } from "react-spring-bottom-sheet"; -import "react-spring-bottom-sheet/dist/style.css"; +import Sheet from "react-modal-sheet"; import { useIsMobile } from "../../toolbar/stores/toolbar-store"; // import { store as selectionStore } from "../../stores/selectionstore"; function useMenuFocus(items, onAction, onClose) { @@ -272,5 +271,15 @@ export function MenuPresenter(props) { } export function ActionSheetPresenter(props) { var _a = props.items, items = _a === void 0 ? [] : _a, isOpen = props.isOpen, _b = props.onClose, onClose = _b === void 0 ? function () { } : _b, children = props.children, sx = props.sx, _c = props.blocking, blocking = _c === void 0 ? true : _c, containerProps = __rest(props, ["items", "isOpen", "onClose", "children", "sx", "blocking"]); - return (_jsx(BottomSheet, __assign({ open: isOpen, onDismiss: onClose, blocking: blocking }, { children: props.children ? (props.children) : (_jsx(Menu, __assign({ items: items, closeMenu: onClose, sx: __assign({ flex: 1, boxShadow: "none", border: "none" }, sx) }, containerProps))) }))); + return (_jsxs(Sheet, __assign({ isOpen: isOpen, onClose: onClose, springConfig: { + stiffness: 300, + damping: 30, + mass: 0.2, + duration: 300, + } }, { children: [_jsxs(Sheet.Container, __assign({ style: { + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + boxShadow: "none", + paddingBottom: 30, + } }, { children: [_jsx(Sheet.Header, {}), _jsx(Sheet.Content, { children: props.children ? (props.children) : (_jsx(Menu, __assign({ items: items, closeMenu: onClose, sx: __assign({ flex: 1, boxShadow: "none", border: "none" }, sx) }, containerProps))) })] })), _jsx(Sheet.Backdrop, {})] }))); } diff --git a/packages/editor/dist/extensions/attachment/attachment.d.ts b/packages/editor/dist/extensions/attachment/attachment.d.ts index 66f655cae..0671bb5d4 100644 --- a/packages/editor/dist/extensions/attachment/attachment.d.ts +++ b/packages/editor/dist/extensions/attachment/attachment.d.ts @@ -1,7 +1,8 @@ import { Node } from "@tiptap/core"; +export declare type AttachmentType = "image" | "file"; export interface AttachmentOptions { onDownloadAttachment: (attachment: Attachment) => boolean; - onOpenAttachmentPicker: () => boolean; + onOpenAttachmentPicker: (type: AttachmentType) => boolean; } export declare type Attachment = AttachmentProgress & { hash: string; @@ -17,7 +18,7 @@ export declare type AttachmentProgress = { declare module "@tiptap/core" { interface Commands { attachment: { - openAttachmentPicker: () => ReturnType; + openAttachmentPicker: (type: AttachmentType) => ReturnType; insertAttachment: (attachment: Attachment) => ReturnType; downloadAttachment: (attachment: Attachment) => ReturnType; setProgress: (progress: AttachmentProgress) => ReturnType; diff --git a/packages/editor/dist/extensions/attachment/attachment.js b/packages/editor/dist/extensions/attachment/attachment.js index ac9ee03ca..f5f142e2c 100644 --- a/packages/editor/dist/extensions/attachment/attachment.js +++ b/packages/editor/dist/extensions/attachment/attachment.js @@ -68,11 +68,9 @@ export var AttachmentNode = Node.create({ return _this.options.onDownloadAttachment(attachment); }; }, - openAttachmentPicker: function () { - return function (_a) { - return _this.options.onOpenAttachmentPicker(); - }; - }, + openAttachmentPicker: function (type) { return function () { + return _this.options.onOpenAttachmentPicker(type); + }; }, setProgress: function (options) { return function (_a) { var e_1, _b; diff --git a/packages/editor/dist/toolbar/tools/block.js b/packages/editor/dist/toolbar/tools/block.js index 4aac14c4d..a5df5693f 100644 --- a/packages/editor/dist/toolbar/tools/block.js +++ b/packages/editor/dist/toolbar/tools/block.js @@ -124,7 +124,9 @@ var imageActionSheet = function (editor) { return ({ type: "menuitem", title: "Upload from disk", icon: "upload", - onClick: function () { }, + onClick: function () { + return editor === null || editor === void 0 ? void 0 : editor.chain().focus().openAttachmentPicker("image").run(); + }, }, { key: "upload-from-url", @@ -215,7 +217,7 @@ var attachment = function (editor) { return ({ title: "Attachment", icon: "attachment", isChecked: editor === null || editor === void 0 ? void 0 : editor.isActive("attachment"), - onClick: function () { return editor === null || editor === void 0 ? void 0 : editor.chain().focus().openAttachmentPicker().run(); }, + onClick: function () { return editor === null || editor === void 0 ? void 0 : editor.chain().focus().openAttachmentPicker("file").run(); }, }); }; var tasklist = function (editor) { return ({ key: "tasklist", diff --git a/packages/editor/dist/toolbar/tools/inline.js b/packages/editor/dist/toolbar/tools/inline.js index 09d068db6..01d5aa5bf 100644 --- a/packages/editor/dist/toolbar/tools/inline.js +++ b/packages/editor/dist/toolbar/tools/inline.js @@ -27,7 +27,7 @@ var __read = (this && this.__read) || function (o, n) { }; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { ToolButton } from "../components/tool-button"; -import { MenuPresenter } from "../../components/menu/menu"; +import { PopupPresenter } from "../../components/menu/menu"; import { useRef, useState } from "react"; import { Flex } from "rebass"; import { Input } from "@rebass/forms"; @@ -84,7 +84,7 @@ export function Link(props) { : editor.state.doc.textBetween(from, to); setText(selectedText); setIsOpen(true); - }, toggled: isOpen || !!isEditing }), _jsx(MenuPresenter, __assign({ options: { + }, toggled: isOpen || !!isEditing }), _jsx(PopupPresenter, __assign({ mobile: "sheet", desktop: "menu", options: { type: "menu", position: { target: targetRef.current || buttonRef.current || undefined, @@ -110,5 +110,5 @@ export function Link(props) { commandChain.run(); setIsOpen(false); }, - } }, { children: _jsxs(Flex, __assign({ sx: { p: 1, width: 300, flexDirection: "column" } }, { children: [_jsx(Input, { type: "text", placeholder: "Link text", value: text, onChange: function (e) { return setText(e.target.value); } }), _jsx(Input, { type: "url", sx: { mt: 1 }, autoFocus: true, placeholder: "https://example.com/", value: href, onChange: function (e) { return setHref(e.target.value); } })] })) })) }))] })); + } }, { children: _jsxs(Flex, __assign({ sx: { p: 1, flexDirection: "column" } }, { children: [_jsx(Input, { type: "text", placeholder: "Link text", value: text, onChange: function (e) { return setText(e.target.value); } }), _jsx(Input, { type: "url", sx: { mt: 1 }, autoFocus: true, placeholder: "https://example.com/", value: href, onChange: function (e) { return setHref(e.target.value); } })] })) })) }))] })); } diff --git a/packages/editor/package-lock.json b/packages/editor/package-lock.json index ae3894be4..58b2a13ba 100644 --- a/packages/editor/package-lock.json +++ b/packages/editor/package-lock.json @@ -47,6 +47,7 @@ "re-resizable": "^6.9.9", "react-color": "^2.19.3", "react-modal": "^3.14.4", + "react-modal-sheet": "file:../react-modal-sheet", "react-spring-bottom-sheet": "^3.4.0", "react-toggle": "^4.1.2", "reactjs-popup": "^2.0.5", @@ -77,6 +78,37 @@ "typescript": "^4.7.2", "typescript-plugin-css-modules": "^3.4.0", "web-vitals": "^2.1.4" + }, + "peerDependencies": { + "framer-motion": "^4.1.17" + } + }, + "../react-modal-sheet": { + "version": "1.5.1", + "license": "MIT", + "dependencies": { + "react-merge-refs": "1.1.0" + }, + "devDependencies": { + "@types/react": "17.0.37", + "@types/react-dom": "17.0.11", + "@types/styled-components": "5.1.7", + "csstype": "3.0.6", + "framer-motion": "^4.1.17", + "husky": "4.2.5", + "react": "17.0.2", + "react-dom": "17.0.2", + "styled-components": "5.3.3", + "tsdx": "0.14.1", + "tslib": "2.1.0", + "typescript": "4.5.5" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "framer-motion": ">=2", + "react": ">=16" } }, "../themeprovider": { @@ -11080,6 +11112,35 @@ "node": ">=0.10.0" } }, + "node_modules/framer-motion": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-4.1.17.tgz", + "integrity": "sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==", + "peer": true, + "dependencies": { + "framesync": "5.3.0", + "hey-listen": "^1.0.8", + "popmotion": "9.3.6", + "style-value-types": "4.1.4", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0", + "react-dom": ">=16.8 || ^17.0.0" + } + }, + "node_modules/framesync": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-5.3.0.tgz", + "integrity": "sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -11699,6 +11760,12 @@ "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "peer": true + }, "node_modules/highlight.js": { "version": "11.5.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.1.tgz", @@ -17058,6 +17125,18 @@ "node": ">=6" } }, + "node_modules/popmotion": { + "version": "9.3.6", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-9.3.6.tgz", + "integrity": "sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==", + "peer": true, + "dependencies": { + "framesync": "5.3.0", + "hey-listen": "^1.0.8", + "style-value-types": "4.1.4", + "tslib": "^2.1.0" + } + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -19276,6 +19355,10 @@ "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" } }, + "node_modules/react-modal-sheet": { + "resolved": "../react-modal-sheet", + "link": true + }, "node_modules/react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -21910,6 +21993,16 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/style-value-types": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-4.1.4.tgz", + "integrity": "sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==", + "peer": true, + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/styled-system": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz", @@ -33994,6 +34087,29 @@ "map-cache": "^0.2.2" } }, + "framer-motion": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-4.1.17.tgz", + "integrity": "sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==", + "peer": true, + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "framesync": "5.3.0", + "hey-listen": "^1.0.8", + "popmotion": "9.3.6", + "style-value-types": "4.1.4", + "tslib": "^2.1.0" + } + }, + "framesync": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-5.3.0.tgz", + "integrity": "sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==", + "peer": true, + "requires": { + "tslib": "^2.1.0" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -34462,6 +34578,12 @@ "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "peer": true + }, "highlight.js": { "version": "11.5.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.1.tgz", @@ -38590,6 +38712,18 @@ "ts-pnp": "^1.1.6" } }, + "popmotion": { + "version": "9.3.6", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-9.3.6.tgz", + "integrity": "sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==", + "peer": true, + "requires": { + "framesync": "5.3.0", + "hey-listen": "^1.0.8", + "style-value-types": "4.1.4", + "tslib": "^2.1.0" + } + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -40443,6 +40577,24 @@ "warning": "^4.0.3" } }, + "react-modal-sheet": { + "version": "file:../react-modal-sheet", + "requires": { + "@types/react": "17.0.37", + "@types/react-dom": "17.0.11", + "@types/styled-components": "5.1.7", + "csstype": "3.0.6", + "framer-motion": "^4.1.17", + "husky": "4.2.5", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-merge-refs": "1.1.0", + "styled-components": "5.3.3", + "tsdx": "0.14.1", + "tslib": "2.1.0", + "typescript": "4.5.5" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -42571,6 +42723,16 @@ "schema-utils": "^2.7.0" } }, + "style-value-types": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-4.1.4.tgz", + "integrity": "sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==", + "peer": true, + "requires": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "styled-system": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz", diff --git a/packages/editor/package.json b/packages/editor/package.json index 865792949..439f998fb 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -43,6 +43,7 @@ "re-resizable": "^6.9.9", "react-color": "^2.19.3", "react-modal": "^3.14.4", + "react-modal-sheet": "file:../react-modal-sheet", "react-spring-bottom-sheet": "^3.4.0", "react-toggle": "^4.1.2", "reactjs-popup": "^2.0.5", @@ -74,6 +75,9 @@ "typescript-plugin-css-modules": "^3.4.0", "web-vitals": "^2.1.4" }, + "peerDependencies": { + "framer-motion": "^4.1.17" + }, "scripts": { "build": "./scripts/build.sh", "watch": "./scripts/build.sh --watch" diff --git a/packages/editor/src/components/menu/menu.tsx b/packages/editor/src/components/menu/menu.tsx index c6607e11c..219567db3 100644 --- a/packages/editor/src/components/menu/menu.tsx +++ b/packages/editor/src/components/menu/menu.tsx @@ -14,8 +14,7 @@ import { MenuItem as MenuItemType /*ResolvedMenuItem*/ } from "./types"; // import { useMenuTrigger, useMenu, getPosition } from "../../hooks/useMenu"; import Modal from "react-modal"; import { ThemeProvider } from "emotion-theming"; -import { BottomSheet } from "react-spring-bottom-sheet"; -import "react-spring-bottom-sheet/dist/style.css"; +import Sheet from "react-modal-sheet"; import { useIsMobile } from "../../toolbar/stores/toolbar-store"; // import { store as selectionStore } from "../../stores/selectionstore"; @@ -424,22 +423,45 @@ export function ActionSheetPresenter( } = props; return ( - - {props.children ? ( - props.children - ) : ( - - )} - + + + + + {props.children ? ( + props.children + ) : ( + + )} + + + + + ); } diff --git a/packages/editor/src/extensions/attachment/attachment.ts b/packages/editor/src/extensions/attachment/attachment.ts index e9c8433de..d9f4c42a4 100644 --- a/packages/editor/src/extensions/attachment/attachment.ts +++ b/packages/editor/src/extensions/attachment/attachment.ts @@ -3,10 +3,11 @@ import { findChildren, ReactNodeViewRenderer } from "../react"; import { Attribute } from "@tiptap/core"; import { AttachmentComponent } from "./component"; +export type AttachmentType = "image" | "file"; export interface AttachmentOptions { // HTMLAttributes: Record; onDownloadAttachment: (attachment: Attachment) => boolean; - onOpenAttachmentPicker: () => boolean; + onOpenAttachmentPicker: (type: AttachmentType) => boolean; } export type Attachment = AttachmentProgress & { @@ -25,7 +26,7 @@ export type AttachmentProgress = { declare module "@tiptap/core" { interface Commands { attachment: { - openAttachmentPicker: () => ReturnType; + openAttachmentPicker: (type: AttachmentType) => ReturnType; insertAttachment: (attachment: Attachment) => ReturnType; downloadAttachment: (attachment: Attachment) => ReturnType; setProgress: (progress: AttachmentProgress) => ReturnType; @@ -94,11 +95,9 @@ export const AttachmentNode = Node.create({ ({}) => { return this.options.onDownloadAttachment(attachment); }, - openAttachmentPicker: - () => - ({}) => { - return this.options.onOpenAttachmentPicker(); - }, + openAttachmentPicker: (type: AttachmentType) => () => { + return this.options.onOpenAttachmentPicker(type); + }, setProgress: (options) => ({ state, tr, dispatch }) => { diff --git a/packages/editor/src/toolbar/tools/block.tsx b/packages/editor/src/toolbar/tools/block.tsx index 281437d00..15cb33e1e 100644 --- a/packages/editor/src/toolbar/tools/block.tsx +++ b/packages/editor/src/toolbar/tools/block.tsx @@ -156,7 +156,8 @@ const imageActionSheet = (editor: Editor | null): MenuItem => ({ type: "menuitem", title: "Upload from disk", icon: "upload", - onClick: () => {}, + onClick: () => + editor?.chain().focus().openAttachmentPicker("image").run(), }, { key: "upload-from-url", @@ -290,7 +291,7 @@ const attachment = (editor: Editor | null): MenuItem => ({ title: "Attachment", icon: "attachment", isChecked: editor?.isActive("attachment"), - onClick: () => editor?.chain().focus().openAttachmentPicker().run(), + onClick: () => editor?.chain().focus().openAttachmentPicker("file").run(), }); const tasklist = (editor: Editor | null): MenuItem => ({ diff --git a/packages/editor/src/toolbar/tools/inline.tsx b/packages/editor/src/toolbar/tools/inline.tsx index 181c6da4a..ef2a58ece 100644 --- a/packages/editor/src/toolbar/tools/inline.tsx +++ b/packages/editor/src/toolbar/tools/inline.tsx @@ -1,7 +1,7 @@ import { ToolProps } from "../types"; import { Editor } from "@tiptap/core"; import { ToolButton } from "../components/tool-button"; -import { MenuPresenter } from "../../components/menu/menu"; +import { MenuPresenter, PopupPresenter } from "../../components/menu/menu"; import { useRef, useState } from "react"; import { Flex } from "rebass"; import { Input } from "@rebass/forms"; @@ -143,7 +143,9 @@ export function Link(props: ToolProps) { }} toggled={isOpen || !!isEditing} /> - setIsOpen(false) }} > - + - + ); }