mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-28 16:06:47 +01:00
feat: make editor ui more user friendly on mobile
This commit is contained in:
21
packages/editor/dist/components/menu/menu.d.ts
vendored
21
packages/editor/dist/components/menu/menu.d.ts
vendored
@@ -2,6 +2,7 @@ import { PropsWithChildren } from "react";
|
|||||||
import { FlexProps } from "rebass";
|
import { FlexProps } from "rebass";
|
||||||
import { MenuOptions } from "./useMenu";
|
import { MenuOptions } from "./useMenu";
|
||||||
import { MenuItem as MenuItemType } from "./types";
|
import { MenuItem as MenuItemType } from "./types";
|
||||||
|
import "react-spring-bottom-sheet/dist/style.css";
|
||||||
declare type MenuProps = MenuContainerProps & {
|
declare type MenuProps = MenuContainerProps & {
|
||||||
items: MenuItemType[];
|
items: MenuItemType[];
|
||||||
closeMenu: () => void;
|
closeMenu: () => void;
|
||||||
@@ -10,12 +11,24 @@ export declare function Menu(props: MenuProps): JSX.Element;
|
|||||||
declare type MenuContainerProps = FlexProps & {
|
declare type MenuContainerProps = FlexProps & {
|
||||||
title?: string;
|
title?: string;
|
||||||
};
|
};
|
||||||
|
export declare type PopupType = "sheet" | "menu" | "none";
|
||||||
|
export declare type PopupPresenterProps = MenuPresenterProps & ActionSheetPresenterProps & {
|
||||||
|
mobile?: PopupType;
|
||||||
|
desktop?: PopupType;
|
||||||
|
};
|
||||||
|
export declare function PopupPresenter(props: PropsWithChildren<PopupPresenterProps>): JSX.Element | null;
|
||||||
export declare type MenuPresenterProps = MenuContainerProps & {
|
export declare type MenuPresenterProps = MenuContainerProps & {
|
||||||
items: MenuItemType[];
|
items?: MenuItemType[];
|
||||||
options: MenuOptions;
|
onClose?: () => void;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
options?: MenuOptions;
|
||||||
className?: string;
|
|
||||||
};
|
};
|
||||||
export declare function MenuPresenter(props: PropsWithChildren<MenuPresenterProps>): JSX.Element;
|
export declare function MenuPresenter(props: PropsWithChildren<MenuPresenterProps>): JSX.Element;
|
||||||
|
export declare type ActionSheetPresenterProps = MenuContainerProps & {
|
||||||
|
items?: MenuItemType[];
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose?: () => void;
|
||||||
|
blocking?: boolean;
|
||||||
|
};
|
||||||
|
export declare function ActionSheetPresenter(props: PropsWithChildren<ActionSheetPresenterProps>): JSX.Element;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
19
packages/editor/dist/components/menu/menu.js
vendored
19
packages/editor/dist/components/menu/menu.js
vendored
@@ -44,6 +44,9 @@ import { getPosition } from "./useMenu";
|
|||||||
import MenuItem from "./menuitem";
|
import MenuItem from "./menuitem";
|
||||||
// import { useMenuTrigger, useMenu, getPosition } from "../../hooks/useMenu";
|
// import { useMenuTrigger, useMenu, getPosition } from "../../hooks/useMenu";
|
||||||
import Modal from "react-modal";
|
import Modal from "react-modal";
|
||||||
|
import { BottomSheet } from "react-spring-bottom-sheet";
|
||||||
|
import "react-spring-bottom-sheet/dist/style.css";
|
||||||
|
import { useIsMobile } from "../../toolbar/stores/toolbar-store";
|
||||||
// import { store as selectionStore } from "../../stores/selectionstore";
|
// import { store as selectionStore } from "../../stores/selectionstore";
|
||||||
function useMenuFocus(items, onAction, onClose) {
|
function useMenuFocus(items, onAction, onClose) {
|
||||||
var _a = __read(useState(-1), 2), focusIndex = _a[0], setFocusIndex = _a[1];
|
var _a = __read(useState(-1), 2), focusIndex = _a[0], setFocusIndex = _a[1];
|
||||||
@@ -202,8 +205,18 @@ function MenuContainer(props) {
|
|||||||
wordWrap: "break-word",
|
wordWrap: "break-word",
|
||||||
} }, { children: title }))), children] })));
|
} }, { children: title }))), children] })));
|
||||||
}
|
}
|
||||||
|
export function PopupPresenter(props) {
|
||||||
|
var _a = props.mobile, mobile = _a === void 0 ? "menu" : _a, _b = props.desktop, desktop = _b === void 0 ? "menu" : _b, restProps = __rest(props, ["mobile", "desktop"]);
|
||||||
|
var isMobile = useIsMobile();
|
||||||
|
if (isMobile && mobile === "sheet")
|
||||||
|
return _jsx(ActionSheetPresenter, __assign({}, restProps));
|
||||||
|
else if (mobile === "menu" || desktop === "menu")
|
||||||
|
return _jsx(MenuPresenter, __assign({}, restProps));
|
||||||
|
else
|
||||||
|
return props.isOpen ? _jsx(_Fragment, { children: props.children }) : null;
|
||||||
|
}
|
||||||
export function MenuPresenter(props) {
|
export function MenuPresenter(props) {
|
||||||
var className = props.className, options = props.options, items = props.items, isOpen = props.isOpen, onClose = props.onClose, children = props.children, containerProps = __rest(props, ["className", "options", "items", "isOpen", "onClose", "children"]);
|
var className = props.className, _a = props.options, options = _a === void 0 ? { type: "menu", position: {} } : _a, _b = props.items, items = _b === void 0 ? [] : _b, isOpen = props.isOpen, _c = props.onClose, onClose = _c === void 0 ? function () { } : _c, children = props.children, containerProps = __rest(props, ["className", "options", "items", "isOpen", "onClose", "children"]);
|
||||||
var position = options.position, type = options.type;
|
var position = options.position, type = options.type;
|
||||||
var isAutocomplete = type === "autocomplete";
|
var isAutocomplete = type === "autocomplete";
|
||||||
var contentRef = useRef();
|
var contentRef = useRef();
|
||||||
@@ -255,3 +268,7 @@ export function MenuPresenter(props) {
|
|||||||
},
|
},
|
||||||
} }, { children: props.children ? (props.children) : (_jsx(Menu, __assign({ items: items, closeMenu: onClose }, containerProps))) })));
|
} }, { children: props.children ? (props.children) : (_jsx(Menu, __assign({ items: items, closeMenu: onClose }, containerProps))) })));
|
||||||
}
|
}
|
||||||
|
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))) })));
|
||||||
|
}
|
||||||
|
|||||||
9
packages/editor/dist/components/responsive/index.d.ts
vendored
Normal file
9
packages/editor/dist/components/responsive/index.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { PropsWithChildren } from "react";
|
||||||
|
declare type ResponsiveContainerProps = {
|
||||||
|
mobile?: JSX.Element;
|
||||||
|
desktop?: JSX.Element;
|
||||||
|
};
|
||||||
|
export declare function ResponsiveContainer(props: ResponsiveContainerProps): JSX.Element | null;
|
||||||
|
export declare function DesktopOnly(props: PropsWithChildren<{}>): JSX.Element;
|
||||||
|
export declare function MobileOnly(props: PropsWithChildren<{}>): JSX.Element;
|
||||||
|
export {};
|
||||||
15
packages/editor/dist/components/responsive/index.js
vendored
Normal file
15
packages/editor/dist/components/responsive/index.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
||||||
|
import { useToolbarStore } from "../../toolbar/stores/toolbar-store";
|
||||||
|
export function ResponsiveContainer(props) {
|
||||||
|
var isMobile = useToolbarStore(function (store) { return store.isMobile; });
|
||||||
|
if (isMobile)
|
||||||
|
return props.mobile || null;
|
||||||
|
else
|
||||||
|
return props.desktop || null;
|
||||||
|
}
|
||||||
|
export function DesktopOnly(props) {
|
||||||
|
return _jsx(ResponsiveContainer, { desktop: _jsx(_Fragment, { children: props.children }) });
|
||||||
|
}
|
||||||
|
export function MobileOnly(props) {
|
||||||
|
return _jsx(ResponsiveContainer, { mobile: _jsx(_Fragment, { children: props.children }) });
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import { ThemeProvider } from "emotion-theming";
|
|||||||
import { Resizable } from "re-resizable";
|
import { Resizable } from "re-resizable";
|
||||||
import { ToolButton } from "../../toolbar/components/tool-button";
|
import { ToolButton } from "../../toolbar/components/tool-button";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { PopupPresenter, } from "../../components/menu/menu";
|
||||||
import { EmbedPopup } from "../../toolbar/popups/embed-popup";
|
import { EmbedPopup } from "../../toolbar/popups/embed-popup";
|
||||||
export function EmbedComponent(props) {
|
export function EmbedComponent(props) {
|
||||||
var _a = props.node.attrs, src = _a.src, width = _a.width, height = _a.height, align = _a.align;
|
var _a = props.node.attrs, src = _a.src, width = _a.width, height = _a.height, align = _a.align;
|
||||||
@@ -58,14 +59,27 @@ export function EmbedComponent(props) {
|
|||||||
width: ref.clientWidth,
|
width: ref.clientWidth,
|
||||||
height: ref.clientHeight,
|
height: ref.clientHeight,
|
||||||
});
|
});
|
||||||
}, lockAspectRatio: true }, { children: [_jsx(Flex, __assign({ sx: { position: "relative", justifyContent: "end" } }, { children: isToolbarVisible && (_jsx(ImageToolbar, { editor: editor, align: align, height: height || 0, width: width || 0, src: src })) })), _jsx(Box, __assign({ as: "iframe", ref: embedRef, src: src, width: "100%", height: "100%", sx: {
|
}, lockAspectRatio: true }, { children: [_jsx(Flex, __assign({ width: "100%", sx: {
|
||||||
border: isActive
|
position: "relative",
|
||||||
? "2px solid var(--primary)"
|
justifyContent: "end",
|
||||||
: "2px solid transparent",
|
borderTop: "20px solid var(--bgSecondary)",
|
||||||
borderRadius: "default",
|
// borderLeft: "20px solid var(--bgSecondary)",
|
||||||
|
borderTopLeftRadius: "default",
|
||||||
|
borderTopRightRadius: "default",
|
||||||
|
borderColor: isActive ? "border" : "bgSecondary",
|
||||||
|
cursor: "pointer",
|
||||||
|
":hover": {
|
||||||
|
borderColor: "border",
|
||||||
|
},
|
||||||
|
} }, { children: isToolbarVisible && (_jsx(EmbedToolbar, { editor: editor, align: align, height: height || 0, width: width || 0, src: src })) })), _jsx(Box, __assign({ as: "iframe", ref: embedRef, src: src, width: "100%", height: "100%", sx: {
|
||||||
|
border: "none",
|
||||||
|
// border: isActive
|
||||||
|
// ? "2px solid var(--primary)"
|
||||||
|
// : "2px solid transparent",
|
||||||
|
// borderRadius: "default",
|
||||||
} }, props))] })) })) })) }));
|
} }, props))] })) })) })) }));
|
||||||
}
|
}
|
||||||
function ImageToolbar(props) {
|
function EmbedToolbar(props) {
|
||||||
var editor = props.editor, height = props.height, width = props.width, src = props.src;
|
var editor = props.editor, height = props.height, width = props.width, src = props.src;
|
||||||
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
return (_jsxs(Flex, __assign({ sx: {
|
return (_jsxs(Flex, __assign({ sx: {
|
||||||
@@ -101,5 +115,5 @@ function ImageToolbar(props) {
|
|||||||
mr: 1,
|
mr: 1,
|
||||||
borderRight: "1px solid var(--border)",
|
borderRight: "1px solid var(--border)",
|
||||||
":last-of-type": { mr: 0, pr: 0, borderRight: "none" },
|
":last-of-type": { mr: 0, pr: 0, borderRight: "none" },
|
||||||
} }, { children: _jsx(ToolButton, { toggled: isOpen, title: "Embed properties", id: "embedProperties", icon: "more", onClick: function () { return setIsOpen(function (s) { return !s; }); } }) }))] })), isOpen && (_jsx(EmbedPopup, { title: "Embed properties", icon: "close", onClose: function () { return setIsOpen(false); }, embed: props, onSourceChanged: function (src) { }, onSizeChanged: function (size) { return editor.commands.setEmbedSize(size); } }))] })));
|
} }, { children: _jsx(ToolButton, { toggled: isOpen, title: "Embed properties", id: "embedProperties", icon: "more", onClick: function () { return setIsOpen(function (s) { return !s; }); } }) }))] })), _jsx(PopupPresenter, __assign({ isOpen: isOpen, desktop: "none", mobile: "sheet", onClose: function () { return setIsOpen(false); }, blocking: true }, { children: _jsx(EmbedPopup, { title: "Embed properties", icon: "close", onClose: function () { return setIsOpen(false); }, embed: props, onSourceChanged: function (src) { }, onSizeChanged: function (size) { return editor.commands.setEmbedSize(size); } }) }))] })));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,15 +26,15 @@ var __read = (this && this.__read) || function (o, n) {
|
|||||||
return ar;
|
return ar;
|
||||||
};
|
};
|
||||||
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
import { Box, Flex, Image, Text } from "rebass";
|
import { Box, Flex, Image } from "rebass";
|
||||||
import { NodeViewWrapper } from "@tiptap/react";
|
import { NodeViewWrapper } from "@tiptap/react";
|
||||||
import { ThemeProvider } from "emotion-theming";
|
import { ThemeProvider } from "emotion-theming";
|
||||||
import { Resizable } from "re-resizable";
|
import { Resizable } from "re-resizable";
|
||||||
import { ToolButton } from "../../toolbar/components/tool-button";
|
import { ToolButton } from "../../toolbar/components/tool-button";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { PopupPresenter, } from "../../components/menu/menu";
|
||||||
import { Popup } from "../../toolbar/components/popup";
|
import { Popup } from "../../toolbar/components/popup";
|
||||||
import { Toggle } from "../../components/toggle";
|
import { ImageProperties } from "../../toolbar/popups/image-properties";
|
||||||
import { Input } from "@rebass/forms";
|
|
||||||
export function ImageComponent(props) {
|
export function ImageComponent(props) {
|
||||||
var _a = props.node
|
var _a = props.node
|
||||||
.attrs, src = _a.src, alt = _a.alt, title = _a.title, width = _a.width, height = _a.height, align = _a.align, float = _a.float;
|
.attrs, src = _a.src, alt = _a.alt, title = _a.title, width = _a.width, height = _a.height, align = _a.align, float = _a.float;
|
||||||
@@ -75,23 +75,6 @@ export function ImageComponent(props) {
|
|||||||
function ImageToolbar(props) {
|
function ImageToolbar(props) {
|
||||||
var editor = props.editor, float = props.float, height = props.height, width = props.width;
|
var editor = props.editor, float = props.float, height = props.height, width = props.width;
|
||||||
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
var onSizeChange = useCallback(function (newWidth, newHeight) {
|
|
||||||
var size = newWidth
|
|
||||||
? {
|
|
||||||
width: newWidth,
|
|
||||||
height: newWidth * (height / width),
|
|
||||||
}
|
|
||||||
: newHeight
|
|
||||||
? {
|
|
||||||
width: newHeight * (width / height),
|
|
||||||
height: newHeight,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
};
|
|
||||||
editor.chain().setImageSize(size).run();
|
|
||||||
}, [width, height]);
|
|
||||||
return (_jsxs(Flex, __assign({ sx: {
|
return (_jsxs(Flex, __assign({ sx: {
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@@ -125,21 +108,10 @@ function ImageToolbar(props) {
|
|||||||
mr: 1,
|
mr: 1,
|
||||||
borderRight: "1px solid var(--border)",
|
borderRight: "1px solid var(--border)",
|
||||||
":last-of-type": { mr: 0, pr: 0, borderRight: "none" },
|
":last-of-type": { mr: 0, pr: 0, borderRight: "none" },
|
||||||
} }, { children: _jsx(ToolButton, { toggled: isOpen, title: "Image properties", id: "imageProperties", icon: "more", onClick: function () { return setIsOpen(function (s) { return !s; }); } }) }))] })), isOpen && (_jsx(Popup, __assign({ title: "Image properties", action: {
|
} }, { children: _jsx(ToolButton, { toggled: isOpen, title: "Image properties", id: "imageProperties", icon: "more", onClick: function () { return setIsOpen(function (s) { return !s; }); } }) }))] })), _jsx(PopupPresenter, __assign({ mobile: "sheet", desktop: "none", isOpen: isOpen, onClose: function () { return setIsOpen(false); }, blocking: false }, { children: _jsx(Popup, __assign({ title: "Image properties", action: {
|
||||||
icon: "close",
|
icon: "close",
|
||||||
onClick: function () {
|
onClick: function () {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
},
|
},
|
||||||
} }, { children: _jsxs(Flex, __assign({ sx: { width: 200, flexDirection: "column", p: 1 } }, { children: [_jsxs(Flex, __assign({ sx: { justifyContent: "space-between", alignItems: "center" } }, { children: [_jsx(Text, __assign({ variant: "body" }, { children: "Floating?" })), _jsx(Toggle, { checked: float, onClick: function () {
|
} }, { children: _jsx(ImageProperties, __assign({}, props)) })) }))] })));
|
||||||
return editor
|
|
||||||
.chain()
|
|
||||||
.setImageAlignment({ float: !float, align: "left" })
|
|
||||||
.run();
|
|
||||||
} })] })), _jsxs(Flex, __assign({ sx: { alignItems: "center", mt: 2 } }, { children: [_jsx(Input, { type: "number", placeholder: "Width", value: width, sx: {
|
|
||||||
mr: 2,
|
|
||||||
p: 1,
|
|
||||||
fontSize: "body",
|
|
||||||
}, onChange: function (e) { return onSizeChange(e.target.valueAsNumber); } }), _jsx(Input, { type: "number", placeholder: "Height", value: height, sx: { p: 1, fontSize: "body" }, onChange: function (e) {
|
|
||||||
return onSizeChange(undefined, e.target.valueAsNumber);
|
|
||||||
} })] }))] })) })))] })));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ import { Button, Text } from "rebass";
|
|||||||
import { Icon } from "./icon";
|
import { Icon } from "./icon";
|
||||||
import { Icons } from "../icons";
|
import { Icons } from "../icons";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import { MenuPresenter } from "../../components/menu/menu";
|
||||||
import { useToolbarContext } from "../hooks/useToolbarContext";
|
import { useToolbarLocation } from "../stores/toolbar-store";
|
||||||
export function Dropdown(props) {
|
export function Dropdown(props) {
|
||||||
var items = props.items, selectedItem = props.selectedItem, buttonRef = props.buttonRef, menuWidth = props.menuWidth;
|
var items = props.items, selectedItem = props.selectedItem, buttonRef = props.buttonRef, menuWidth = props.menuWidth;
|
||||||
var internalRef = useRef();
|
var internalRef = useRef();
|
||||||
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
var toolbarLocation = useToolbarContext().toolbarLocation;
|
var toolbarLocation = useToolbarLocation();
|
||||||
return (_jsxs(_Fragment, { children: [_jsxs(Button, __assign({ ref: function (ref) {
|
return (_jsxs(_Fragment, { children: [_jsxs(Button, __assign({ ref: function (ref) {
|
||||||
internalRef.current = ref;
|
internalRef.current = ref;
|
||||||
if (buttonRef)
|
if (buttonRef)
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ export function Popup(props) {
|
|||||||
return (_jsxs(Flex, __assign({ sx: {
|
return (_jsxs(Flex, __assign({ sx: {
|
||||||
bg: "background",
|
bg: "background",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
borderRadius: "default",
|
// borderRadius: "default",
|
||||||
border: "1px solid var(--border)",
|
// border: "1px solid var(--border)",
|
||||||
boxShadow: "menu",
|
// boxShadow: "menu",
|
||||||
} }, { children: [title && (_jsxs(Flex, __assign({ sx: {
|
} }, { children: [title && (_jsxs(Flex, __assign({ sx: {
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@@ -41,5 +41,5 @@ export function Popup(props) {
|
|||||||
}
|
}
|
||||||
function PopupButton(props) {
|
function PopupButton(props) {
|
||||||
var text = props.text, loading = props.loading, icon = props.icon, iconColor = props.iconColor, iconSize = props.iconSize, restProps = __rest(props, ["text", "loading", "icon", "iconColor", "iconSize"]);
|
var text = props.text, loading = props.loading, icon = props.icon, iconColor = props.iconColor, iconSize = props.iconSize, restProps = __rest(props, ["text", "loading", "icon", "iconColor", "iconSize"]);
|
||||||
return (_jsx(Button, __assign({ variant: "dialog", sx: { p: 1, px: 2 } }, restProps, { children: loading ? (_jsx(Icon, { path: Icons.loading, size: 16, rotate: true, color: "primary" })) : icon ? (_jsx(Icon, { path: Icons[icon], size: iconSize || 18, color: iconColor || "icon" })) : (text) })));
|
return (_jsx(Button, __assign({ variant: "icon", sx: { p: 1, px: 2 } }, restProps, { children: loading ? (_jsx(Icon, { path: Icons.loading, size: 16, rotate: true, color: "primary" })) : icon ? (_jsx(Icon, { path: Icons[icon], size: iconSize || 18, color: iconColor || "icon" })) : (text) })));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,12 +43,12 @@ import { Icons } from "../icons";
|
|||||||
import { Icon } from "./icon";
|
import { Icon } from "./icon";
|
||||||
import { ToolButton } from "./tool-button";
|
import { ToolButton } from "./tool-button";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import { MenuPresenter } from "../../components/menu/menu";
|
||||||
import { useToolbarContext } from "../hooks/useToolbarContext";
|
import { useToolbarLocation } from "../stores/toolbar-store";
|
||||||
export function SplitButton(props) {
|
export function SplitButton(props) {
|
||||||
var menuPresenterProps = props.menuPresenterProps, children = props.children, toolButtonProps = __rest(props, ["menuPresenterProps", "children"]);
|
var menuPresenterProps = props.menuPresenterProps, children = props.children, toolButtonProps = __rest(props, ["menuPresenterProps", "children"]);
|
||||||
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
var ref = useRef(null);
|
var ref = useRef(null);
|
||||||
var toolbarLocation = useToolbarContext().toolbarLocation;
|
var toolbarLocation = useToolbarLocation();
|
||||||
return (_jsxs(_Fragment, { children: [_jsxs(Flex, __assign({ ref: ref, sx: {
|
return (_jsxs(_Fragment, { children: [_jsxs(Flex, __assign({ ref: ref, sx: {
|
||||||
borderRadius: "default",
|
borderRadius: "default",
|
||||||
bg: isOpen ? "hover" : "transparent",
|
bg: isOpen ? "hover" : "transparent",
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ export declare type ToolButtonProps = ButtonProps & {
|
|||||||
iconSize?: number;
|
iconSize?: number;
|
||||||
toggled: boolean;
|
toggled: boolean;
|
||||||
buttonRef?: React.MutableRefObject<HTMLButtonElement | null | undefined>;
|
buttonRef?: React.MutableRefObject<HTMLButtonElement | null | undefined>;
|
||||||
|
variant?: "small" | "normal";
|
||||||
};
|
};
|
||||||
export declare function ToolButton(props: ToolButtonProps): JSX.Element;
|
export declare function ToolButton(props: ToolButtonProps): JSX.Element;
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import { Button } from "rebass";
|
|||||||
import { Icons } from "../icons";
|
import { Icons } from "../icons";
|
||||||
import { Icon } from "./icon";
|
import { Icon } from "./icon";
|
||||||
export function ToolButton(props) {
|
export function ToolButton(props) {
|
||||||
var id = props.id, icon = props.icon, iconSize = props.iconSize, iconColor = props.iconColor, toggled = props.toggled, sx = props.sx, buttonRef = props.buttonRef, buttonProps = __rest(props, ["id", "icon", "iconSize", "iconColor", "toggled", "sx", "buttonRef"]);
|
var id = props.id, icon = props.icon, iconSize = props.iconSize, iconColor = props.iconColor, toggled = props.toggled, sx = props.sx, buttonRef = props.buttonRef, _a = props.variant, variant = _a === void 0 ? "normal" : _a, buttonProps = __rest(props, ["id", "icon", "iconSize", "iconColor", "toggled", "sx", "buttonRef", "variant"]);
|
||||||
return (_jsx(Button, __assign({ ref: buttonRef, tabIndex: -1, id: "tool-".concat(id), sx: __assign({ p: 1, m: 0, bg: toggled ? "hover" : "transparent", mr: 1, ":hover": { bg: "hover" }, ":last-of-type": {
|
return (_jsx(Button, __assign({ ref: buttonRef, tabIndex: -1, id: "tool-".concat(id), sx: __assign({ p: variant === "small" ? "3px" : 1, borderRadius: variant === "small" ? "small" : "default", m: 0, bg: toggled ? "hover" : "transparent", mr: variant === "small" ? 0 : 1, ":hover": { bg: "hover" }, ":last-of-type": {
|
||||||
mr: 0,
|
mr: 0,
|
||||||
} }, sx) }, buttonProps, { children: _jsx(Icon, { path: Icons[icon], color: iconColor || "text", size: iconSize || 18 }) })));
|
} }, sx) }, buttonProps, { children: _jsx(Icon, { path: Icons[icon], color: iconColor || "text", size: iconSize || variant === "small" ? 16 : 18 }) })));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ var __assign = (this && this.__assign) || function () {
|
|||||||
};
|
};
|
||||||
return __assign.apply(this, arguments);
|
return __assign.apply(this, arguments);
|
||||||
};
|
};
|
||||||
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
||||||
import { TableRowFloatingMenu, TableColumnFloatingMenu } from "./table";
|
import { TableRowFloatingMenu, TableColumnFloatingMenu, TableFloatingMenu, } from "./table/table";
|
||||||
import { SearchReplaceFloatingMenu } from "./search-replace";
|
import { SearchReplaceFloatingMenu } from "./search-replace";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../components/responsive";
|
||||||
export function EditorFloatingMenus(props) {
|
export function EditorFloatingMenus(props) {
|
||||||
return (_jsxs(_Fragment, { children: [_jsx(TableRowFloatingMenu, __assign({}, props)), _jsx(TableColumnFloatingMenu, __assign({}, props)), _jsx(SearchReplaceFloatingMenu, __assign({}, props))] }));
|
return (_jsxs(_Fragment, { children: [_jsxs(DesktopOnly, { children: [_jsx(TableRowFloatingMenu, __assign({}, props)), _jsx(TableColumnFloatingMenu, __assign({}, props))] }), _jsx(MobileOnly, { children: _jsx(TableFloatingMenu, __assign({}, props)) }), _jsx(SearchReplaceFloatingMenu, __assign({}, props))] }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,88 +9,21 @@ var __assign = (this && this.__assign) || function () {
|
|||||||
};
|
};
|
||||||
return __assign.apply(this, arguments);
|
return __assign.apply(this, arguments);
|
||||||
};
|
};
|
||||||
var __read = (this && this.__read) || function (o, n) {
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
||||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
import { PopupPresenter, } from "../../components/menu/menu";
|
||||||
if (!m) return o;
|
import { SearchReplacePopup } from "../popups/search-replace";
|
||||||
var i = m.call(o), r, ar = [], e;
|
|
||||||
try {
|
|
||||||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
||||||
}
|
|
||||||
catch (error) { e = { error: error }; }
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
||||||
}
|
|
||||||
finally { if (e) throw e.error; }
|
|
||||||
}
|
|
||||||
return ar;
|
|
||||||
};
|
|
||||||
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
||||||
import { Input } from "@rebass/forms";
|
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
|
||||||
import { Flex } from "rebass";
|
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
|
||||||
import { Popup } from "../components/popup";
|
|
||||||
import { ToolButton } from "../components/tool-button";
|
|
||||||
export function SearchReplaceFloatingMenu(props) {
|
export function SearchReplaceFloatingMenu(props) {
|
||||||
var editor = props.editor;
|
var editor = props.editor;
|
||||||
var _a = editor.storage
|
var isSearching = editor.storage.searchreplace.isSearching;
|
||||||
.searchreplace, isSearching = _a.isSearching, selectedText = _a.selectedText;
|
|
||||||
var _b = __read(useState(false), 2), matchCase = _b[0], setMatchCase = _b[1];
|
|
||||||
var _c = __read(useState(false), 2), matchWholeWord = _c[0], setMatchWholeWord = _c[1];
|
|
||||||
var _d = __read(useState(false), 2), enableRegex = _d[0], setEnableRegex = _d[1];
|
|
||||||
var replaceText = useRef("");
|
|
||||||
var searchInputRef = useRef();
|
|
||||||
var search = useCallback(function (term) {
|
|
||||||
editor.commands.search(term, {
|
|
||||||
matchCase: matchCase,
|
|
||||||
enableRegex: enableRegex,
|
|
||||||
matchWholeWord: matchWholeWord,
|
|
||||||
});
|
|
||||||
}, [matchCase, enableRegex, matchWholeWord]);
|
|
||||||
useEffect(function () {
|
|
||||||
if (!searchInputRef.current)
|
|
||||||
return;
|
|
||||||
search(searchInputRef.current.value);
|
|
||||||
}, [search, matchCase, matchWholeWord, enableRegex]);
|
|
||||||
useEffect(function () {
|
|
||||||
if (isSearching && selectedText) {
|
|
||||||
if (searchInputRef.current) {
|
|
||||||
var input_1 = searchInputRef.current;
|
|
||||||
setTimeout(function () {
|
|
||||||
input_1.value = selectedText;
|
|
||||||
input_1.focus();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
search(selectedText);
|
|
||||||
}
|
|
||||||
}, [isSearching, selectedText, search]);
|
|
||||||
if (!isSearching)
|
if (!isSearching)
|
||||||
return null;
|
return null;
|
||||||
return (_jsx(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
return (_jsx(_Fragment, { children: _jsx(PopupPresenter, __assign({ mobile: "sheet", desktop: "menu", isOpen: true, onClose: function () { return editor.commands.endSearch(); }, options: {
|
||||||
type: "autocomplete",
|
type: "autocomplete",
|
||||||
position: {
|
position: {
|
||||||
target: document.querySelector(".editor-toolbar") || "mouse",
|
target: document.querySelector(".editor-toolbar") || "mouse",
|
||||||
isTargetAbsolute: true,
|
isTargetAbsolute: true,
|
||||||
location: "below",
|
location: "below",
|
||||||
align: "end",
|
align: "end",
|
||||||
},
|
},
|
||||||
} }, { children: _jsx(Popup, { children: _jsxs(Flex, __assign({ sx: { p: 1, flexDirection: "column" } }, { children: [_jsxs(Flex, __assign({ sx: { alignItems: "start", flexShrink: 0 } }, { children: [_jsxs(Flex, __assign({ sx: {
|
} }, { children: _jsx(SearchReplacePopup, { editor: editor }) })) }));
|
||||||
position: "relative",
|
|
||||||
mr: 1,
|
|
||||||
width: 200,
|
|
||||||
alignItems: "center",
|
|
||||||
} }, { children: [_jsx(Input, { defaultValue: selectedText, ref: searchInputRef, autoFocus: true, sx: { p: 1 }, placeholder: "Find", onChange: function (e) {
|
|
||||||
search(e.target.value);
|
|
||||||
} }), _jsxs(Flex, __assign({ sx: {
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
mr: 0,
|
|
||||||
} }, { children: [_jsx(ToolButton, { sx: {
|
|
||||||
mr: 0,
|
|
||||||
}, toggled: matchCase, title: "Match case", id: "matchCase", icon: "caseSensitive", onClick: function () { return setMatchCase(function (s) { return !s; }); }, iconSize: 14 }), _jsx(ToolButton, { sx: {
|
|
||||||
mr: 0,
|
|
||||||
}, toggled: matchWholeWord, title: "Match whole word", id: "matchWholeWord", icon: "wholeWord", onClick: function () { return setMatchWholeWord(function (s) { return !s; }); }, iconSize: 14 }), _jsx(ToolButton, { sx: {
|
|
||||||
mr: 0,
|
|
||||||
}, toggled: enableRegex, title: "Enable regex", id: "enableRegex", icon: "regex", onClick: function () { return setEnableRegex(function (s) { return !s; }); }, iconSize: 14 })] }))] })), _jsx(ToolButton, { toggled: false, title: "Previous match", id: "previousMatch", icon: "previousMatch", onClick: function () { return editor.commands.moveToPreviousResult(); }, sx: { mr: 0 }, iconSize: 16 }), _jsx(ToolButton, { toggled: false, title: "Next match", id: "nextMatch", icon: "nextMatch", onClick: function () { return editor.commands.moveToNextResult(); }, sx: { mr: 0 }, iconSize: 16 }), _jsx(ToolButton, { toggled: false, title: "Close", id: "close", icon: "close", onClick: function () { return editor.chain().focus().endSearch().run(); }, iconSize: 16, sx: { mr: 0 } })] })), _jsxs(Flex, __assign({ sx: { alignItems: "start", flexShrink: 0, mt: 1 } }, { children: [_jsx(Input, { sx: { p: 1, width: 200, mr: 1 }, placeholder: "Replace", onChange: function (e) { return (replaceText.current = e.target.value); } }), _jsx(ToolButton, { toggled: false, title: "Replace", id: "replace", icon: "replaceOne", onClick: function () { return editor.commands.replace(replaceText.current); }, sx: { mr: 0 }, iconSize: 16 }), _jsx(ToolButton, { toggled: false, title: "Replace all", id: "replaceAll", icon: "replaceAll", onClick: function () { return editor.commands.replaceAll(replaceText.current); }, sx: { mr: 0 }, iconSize: 16 })] }))] })) }) })));
|
|
||||||
}
|
}
|
||||||
|
|||||||
178
packages/editor/dist/toolbar/floatingmenus/table.js
vendored
178
packages/editor/dist/toolbar/floatingmenus/table.js
vendored
@@ -45,6 +45,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|||||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var __rest = (this && this.__rest) || function (s, e) {
|
||||||
|
var t = {};
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||||
|
t[p] = s[p];
|
||||||
|
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||||
|
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||||
|
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||||
|
t[p[i]] = s[p[i]];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
var __read = (this && this.__read) || function (o, n) {
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
if (!m) return o;
|
if (!m) return o;
|
||||||
@@ -74,15 +85,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|||||||
import { Slider } from "@rebass/forms";
|
import { Slider } from "@rebass/forms";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { Flex, Text } from "rebass";
|
import { Flex, Text } from "rebass";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import { ActionSheetPresenter, MenuPresenter, } from "../../components/menu/menu";
|
||||||
import { Popup } from "../components/popup";
|
import { Popup } from "../components/popup";
|
||||||
import { ToolButton } from "../components/tool-button";
|
import { ToolButton } from "../components/tool-button";
|
||||||
import { selectedRect } from "prosemirror-tables";
|
import { selectedRect } from "prosemirror-tables";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../components/responsive";
|
||||||
export function TableRowFloatingMenu(props) {
|
export function TableRowFloatingMenu(props) {
|
||||||
var editor = props.editor;
|
var editor = props.editor;
|
||||||
var theme = editor.storage.theme;
|
// const theme = editor.storage.theme as Theme;
|
||||||
var _a = __read(useState(null), 2), position = _a[0], setPosition = _a[1];
|
var _a = __read(useState(null), 2), position = _a[0], setPosition = _a[1];
|
||||||
var _b = __read(useState(false), 2), isMenuOpen = _b[0], setIsMenuOpen = _b[1];
|
|
||||||
useEffect(function () {
|
useEffect(function () {
|
||||||
var _a;
|
var _a;
|
||||||
if (!editor.isActive("tableCell") &&
|
if (!editor.isActive("tableCell") &&
|
||||||
@@ -111,18 +122,23 @@ export function TableRowFloatingMenu(props) {
|
|||||||
}, [editor.state.selection]);
|
}, [editor.state.selection]);
|
||||||
if (!position)
|
if (!position)
|
||||||
return null;
|
return null;
|
||||||
return (_jsxs(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
return (_jsx(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
||||||
type: "autocomplete",
|
type: "autocomplete",
|
||||||
position: position,
|
position: position,
|
||||||
} }, { children: [_jsxs(Flex, __assign({ sx: {
|
} }, { children: _jsxs(Flex, __assign({ sx: {
|
||||||
bg: "background",
|
bg: "background",
|
||||||
flexWrap: "nowrap",
|
flexWrap: "nowrap",
|
||||||
borderRadius: "default",
|
borderRadius: "default",
|
||||||
opacity: isMenuOpen ? 1 : 0.3,
|
// opacity: isMenuOpen ? 1 : 0.3,
|
||||||
":hover": {
|
":hover": {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
},
|
},
|
||||||
} }, { children: [_jsx(ToolButton, { toggled: isMenuOpen, title: "Row properties", id: "properties", icon: "more", onClick: function () { return setIsMenuOpen(true); }, iconSize: 16, sx: { mr: 0, p: "3px", borderRadius: "small" } }), _jsx(ToolButton, { toggled: false, title: "Insert row below", id: "insertRowBelow", icon: "insertRowBelow", onClick: function () { return editor.chain().focus().addRowAfter().run(); }, sx: { mr: 0, p: "3px", borderRadius: "small" }, iconSize: 16 })] })), _jsx(MenuPresenter, { isOpen: isMenuOpen, onClose: function () {
|
} }, { children: [_jsx(RowProperties, { title: "Row properties", editor: editor, variant: "small", icon: "more" }), _jsx(InsertRowBelow, { title: "Insert row below", icon: "insertRowBelow", editor: editor, variant: "small" })] })) })));
|
||||||
|
}
|
||||||
|
function RowProperties(props) {
|
||||||
|
var editor = props.editor, toolProps = __rest(props, ["editor"]);
|
||||||
|
var _a = __read(useState(false), 2), isMenuOpen = _a[0], setIsMenuOpen = _a[1];
|
||||||
|
return (_jsxs(_Fragment, { children: [_jsx(ToolButton, __assign({ toggled: isMenuOpen }, toolProps, { onClick: function () { return setIsMenuOpen(true); } })), _jsx(MenuPresenter, { isOpen: isMenuOpen, onClose: function () {
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
editor.commands.focus();
|
editor.commands.focus();
|
||||||
}, options: {
|
}, options: {
|
||||||
@@ -157,47 +173,20 @@ export function TableRowFloatingMenu(props) {
|
|||||||
onClick: function () { return editor.chain().focus().deleteRow().run(); },
|
onClick: function () { return editor.chain().focus().deleteRow().run(); },
|
||||||
icon: "deleteRow",
|
icon: "deleteRow",
|
||||||
},
|
},
|
||||||
] })] })));
|
] })] }));
|
||||||
}
|
}
|
||||||
export function TableColumnFloatingMenu(props) {
|
function InsertRowBelow(props) {
|
||||||
|
var editor = props.editor, toolProps = __rest(props, ["editor"]);
|
||||||
|
return (_jsx(ToolButton, __assign({ toggled: false }, toolProps, { onClick: function () { return editor.chain().focus().addRowAfter().run(); } })));
|
||||||
|
}
|
||||||
|
function ColumnProperties(props) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var editor = props.editor;
|
var editor = props.editor, currentCell = props.currentCell, toolProps = __rest(props, ["editor", "currentCell"]);
|
||||||
var _a = __read(useState(null), 2), position = _a[0], setPosition = _a[1];
|
var _a = __read(useState(false), 2), isMenuOpen = _a[0], setIsMenuOpen = _a[1];
|
||||||
var isInsideCellSelection = !editor.state.selection.empty &&
|
var isInsideCellSelection = !editor.state.selection.empty &&
|
||||||
editor.state.selection.$anchor.node().type.name === "tableCell";
|
editor.state.selection.$anchor.node().type.name === "tableCell";
|
||||||
var _b = __read(useState(false), 2), isMenuOpen = _b[0], setIsMenuOpen = _b[1];
|
var _b = __read(useState(false), 2), showCellProps = _b[0], setShowCellProps = _b[1];
|
||||||
var _c = __read(useState(false), 2), showCellProps = _c[0], setShowCellProps = _c[1];
|
var _c = __read(useState(null), 2), menuPosition = _c[0], setMenuPosition = _c[1];
|
||||||
var _d = __read(useState(null), 2), menuPosition = _d[0], setMenuPosition = _d[1];
|
|
||||||
useEffect(function () {
|
|
||||||
var _a;
|
|
||||||
if (!editor.isActive("tableCell") &&
|
|
||||||
!editor.isActive("tableRow") &&
|
|
||||||
!editor.isActive("tableHeader")) {
|
|
||||||
setPosition(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var $from = editor.state.selection.$from;
|
|
||||||
var selectedNode = $from.node();
|
|
||||||
var pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
|
||||||
var currentCell = (_a = editor.view.nodeDOM(pos)) === null || _a === void 0 ? void 0 : _a.closest("td,th");
|
|
||||||
var currentTable = currentCell === null || currentCell === void 0 ? void 0 : currentCell.closest("table");
|
|
||||||
if (!currentCell || !currentTable)
|
|
||||||
return;
|
|
||||||
setPosition(function (old) {
|
|
||||||
if ((old === null || old === void 0 ? void 0 : old.target) === currentCell)
|
|
||||||
return old;
|
|
||||||
return {
|
|
||||||
isTargetAbsolute: true,
|
|
||||||
location: "top",
|
|
||||||
align: "center",
|
|
||||||
yAnchor: currentTable,
|
|
||||||
yOffset: -2,
|
|
||||||
target: currentCell,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [editor.state.selection]);
|
|
||||||
if (!position)
|
|
||||||
return null;
|
|
||||||
var columnProperties = [
|
var columnProperties = [
|
||||||
{
|
{
|
||||||
key: "addColumnLeft",
|
key: "addColumnLeft",
|
||||||
@@ -259,7 +248,7 @@ export function TableColumnFloatingMenu(props) {
|
|||||||
onClick: function () {
|
onClick: function () {
|
||||||
setShowCellProps(true);
|
setShowCellProps(true);
|
||||||
setMenuPosition({
|
setMenuPosition({
|
||||||
target: position.target || undefined,
|
target: currentCell || undefined,
|
||||||
isTargetAbsolute: true,
|
isTargetAbsolute: true,
|
||||||
yOffset: 10,
|
yOffset: 10,
|
||||||
location: "below",
|
location: "below",
|
||||||
@@ -277,20 +266,9 @@ export function TableColumnFloatingMenu(props) {
|
|||||||
onClick: function () { return editor.chain().focus().deleteTable().run(); },
|
onClick: function () { return editor.chain().focus().deleteTable().run(); },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return (_jsxs(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
return (_jsxs(_Fragment, { children: [_jsx(ToolButton, __assign({ toggled: isMenuOpen }, toolProps, { onClick: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||||
type: "autocomplete",
|
return [2 /*return*/, setIsMenuOpen(true)];
|
||||||
position: position,
|
}); }); } })), _jsx(MenuPresenter, { isOpen: isMenuOpen, onClose: function () {
|
||||||
} }, { children: [_jsxs(Flex, __assign({ sx: {
|
|
||||||
bg: "background",
|
|
||||||
flexWrap: "nowrap",
|
|
||||||
borderRadius: "default",
|
|
||||||
opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
|
||||||
":hover": {
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
} }, { children: [_jsx(ToolButton, { toggled: isMenuOpen, title: "Column properties", id: "properties", icon: "more", onClick: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
||||||
return [2 /*return*/, setIsMenuOpen(true)];
|
|
||||||
}); }); }, iconSize: 16, sx: { mr: 0, p: "3px", borderRadius: "small" } }), _jsx(ToolButton, { toggled: false, title: "Insert column right", id: "insertColumnRight", icon: "plus", onClick: function () { return editor.chain().focus().addColumnAfter().run(); }, sx: { mr: 0, p: "3px", borderRadius: "small" }, iconSize: 16 })] })), _jsx(MenuPresenter, { isOpen: isMenuOpen, onClose: function () {
|
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
editor.commands.focus();
|
editor.commands.focus();
|
||||||
}, options: {
|
}, options: {
|
||||||
@@ -301,13 +279,67 @@ export function TableColumnFloatingMenu(props) {
|
|||||||
{ type: "seperator", key: "cellSeperator" }
|
{ type: "seperator", key: "cellSeperator" }
|
||||||
], false), __read(cellProperties), false), [
|
], false), __read(cellProperties), false), [
|
||||||
{ type: "seperator", key: "tableSeperator" }
|
{ type: "seperator", key: "tableSeperator" }
|
||||||
], false), __read(tableProperties), false) }), _jsx(MenuPresenter, __assign({ isOpen: showCellProps, onClose: function () {
|
], false), __read(tableProperties), false) }), _jsx(DesktopOnly, { children: _jsx(MenuPresenter, __assign({ isOpen: showCellProps, onClose: function () {
|
||||||
setShowCellProps(false);
|
setShowCellProps(false);
|
||||||
editor.commands.focus();
|
editor.commands.focus();
|
||||||
}, options: {
|
}, options: {
|
||||||
type: "menu",
|
type: "menu",
|
||||||
position: menuPosition || {},
|
position: menuPosition || {},
|
||||||
}, items: [] }, { children: _jsx(CellProperties, { editor: editor, onClose: function () { return setShowCellProps(false); } }) }))] })));
|
}, items: [] }, { children: _jsx(CellProperties, { editor: editor, onClose: function () { return setShowCellProps(false); } }) })) }), _jsx(MobileOnly, { children: _jsx(ActionSheetPresenter, __assign({ isOpen: showCellProps, onClose: function () {
|
||||||
|
setShowCellProps(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}, items: [] }, { children: _jsx(CellProperties, { editor: editor, onClose: function () { return setShowCellProps(false); } }) })) })] }));
|
||||||
|
}
|
||||||
|
function InsertColumnRight(props) {
|
||||||
|
var editor = props.editor, toolProps = __rest(props, ["editor"]);
|
||||||
|
return (_jsx(ToolButton, __assign({}, toolProps, { toggled: false, onClick: function () { return editor.chain().focus().addColumnAfter().run(); } })));
|
||||||
|
}
|
||||||
|
export function TableColumnFloatingMenu(props) {
|
||||||
|
var editor = props.editor;
|
||||||
|
var _a = __read(useState(null), 2), position = _a[0], setPosition = _a[1];
|
||||||
|
useEffect(function () {
|
||||||
|
var _a;
|
||||||
|
if (!editor.isActive("tableCell") &&
|
||||||
|
!editor.isActive("tableRow") &&
|
||||||
|
!editor.isActive("tableHeader")) {
|
||||||
|
setPosition(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var $from = editor.state.selection.$from;
|
||||||
|
var selectedNode = $from.node();
|
||||||
|
var pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
||||||
|
var currentCell = (_a = editor.view.nodeDOM(pos)) === null || _a === void 0 ? void 0 : _a.closest("td,th");
|
||||||
|
var currentTable = currentCell === null || currentCell === void 0 ? void 0 : currentCell.closest("table");
|
||||||
|
if (!currentCell || !currentTable)
|
||||||
|
return;
|
||||||
|
setPosition(function (old) {
|
||||||
|
if ((old === null || old === void 0 ? void 0 : old.target) === currentCell)
|
||||||
|
return old;
|
||||||
|
return {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
location: "top",
|
||||||
|
align: "center",
|
||||||
|
yAnchor: currentTable,
|
||||||
|
yOffset: 2,
|
||||||
|
target: currentCell,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [editor.state.selection]);
|
||||||
|
if (!position)
|
||||||
|
return null;
|
||||||
|
return (_jsx(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
||||||
|
type: "autocomplete",
|
||||||
|
position: position,
|
||||||
|
} }, { children: _jsxs(Flex, __assign({ sx: {
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: 0.3,
|
||||||
|
// opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
} }, { children: [_jsx(ColumnProperties, { currentCell: position.target, title: "Column properties", editor: editor, icon: "more", variant: "small" }), _jsx(InsertColumnRight, { editor: editor, title: "Insert column right", variant: "small", icon: "plus" })] })) })));
|
||||||
}
|
}
|
||||||
function CellProperties(props) {
|
function CellProperties(props) {
|
||||||
var editor = props.editor, onClose = props.onClose;
|
var editor = props.editor, onClose = props.onClose;
|
||||||
@@ -317,7 +349,7 @@ function CellProperties(props) {
|
|||||||
icon: "close",
|
icon: "close",
|
||||||
iconColor: "error",
|
iconColor: "error",
|
||||||
onClick: onClose,
|
onClick: onClose,
|
||||||
} }, { children: _jsxs(Flex, __assign({ sx: { flexDirection: "column", width: 200, px: 1, mb: 2 } }, { children: [_jsx(ColorPickerTool, { color: attributes.backgroundColor, title: "Background color", icon: "backgroundColor", onColorChange: function (color) {
|
} }, { children: _jsxs(Flex, __assign({ sx: { flexDirection: "column", px: 1, mb: 2 } }, { children: [_jsx(ColorPickerTool, { color: attributes.backgroundColor, title: "Background color", icon: "backgroundColor", onColorChange: function (color) {
|
||||||
return editor.commands.setCellAttribute("backgroundColor", color);
|
return editor.commands.setCellAttribute("backgroundColor", color);
|
||||||
} }), _jsx(ColorPickerTool, { color: attributes.color, title: "Text color", icon: "textColor", onColorChange: function (color) {
|
} }), _jsx(ColorPickerTool, { color: attributes.color, title: "Text color", icon: "textColor", onColorChange: function (color) {
|
||||||
return editor.commands.setCellAttribute("color", color);
|
return editor.commands.setCellAttribute("color", color);
|
||||||
|
|||||||
6
packages/editor/dist/toolbar/floatingmenus/table/actions.d.ts
vendored
Normal file
6
packages/editor/dist/toolbar/floatingmenus/table/actions.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
declare function moveColumnRight(editor: Editor): void;
|
||||||
|
declare function moveColumnLeft(editor: Editor): void;
|
||||||
|
declare function moveRowDown(editor: Editor): void;
|
||||||
|
declare function moveRowUp(editor: Editor): void;
|
||||||
|
export { moveColumnLeft, moveColumnRight, moveRowDown, moveRowUp };
|
||||||
108
packages/editor/dist/toolbar/floatingmenus/table/actions.js
vendored
Normal file
108
packages/editor/dist/toolbar/floatingmenus/table/actions.js
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { selectedRect } from "prosemirror-tables";
|
||||||
|
function moveColumnRight(editor) {
|
||||||
|
var tr = editor.state.tr;
|
||||||
|
var rect = selectedRect(editor.state);
|
||||||
|
if (rect.right === rect.map.width)
|
||||||
|
return;
|
||||||
|
var transaction = moveColumn(tr, rect, rect.left, rect.left + 1);
|
||||||
|
if (!transaction)
|
||||||
|
return;
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
function moveColumnLeft(editor) {
|
||||||
|
var tr = editor.state.tr;
|
||||||
|
var rect = selectedRect(editor.state);
|
||||||
|
if (rect.left === 0)
|
||||||
|
return;
|
||||||
|
var transaction = moveColumn(tr, rect, rect.left, rect.left - 1);
|
||||||
|
if (!transaction)
|
||||||
|
return;
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
function moveRowDown(editor) {
|
||||||
|
var tr = editor.state.tr;
|
||||||
|
var rect = selectedRect(editor.state);
|
||||||
|
if (rect.top + 1 === rect.map.height)
|
||||||
|
return;
|
||||||
|
var transaction = moveRow(tr, rect, rect.top, rect.top + 1);
|
||||||
|
if (!transaction)
|
||||||
|
return;
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
function moveRowUp(editor) {
|
||||||
|
var tr = editor.state.tr;
|
||||||
|
var rect = selectedRect(editor.state);
|
||||||
|
if (rect.top === 0)
|
||||||
|
return;
|
||||||
|
var transaction = moveRow(tr, rect, rect.top, rect.top - 1);
|
||||||
|
if (!transaction)
|
||||||
|
return;
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
function moveColumn(tr, rect, from, to) {
|
||||||
|
var fromCells = getColumnCells(rect, from);
|
||||||
|
var toCells = getColumnCells(rect, to);
|
||||||
|
return moveCells(tr, rect, fromCells, toCells);
|
||||||
|
}
|
||||||
|
function getColumnCells(_a, col) {
|
||||||
|
var map = _a.map, table = _a.table;
|
||||||
|
var cells = [];
|
||||||
|
for (var row = 0; row < map.height;) {
|
||||||
|
var index = row * map.width + col;
|
||||||
|
if (index >= map.map.length)
|
||||||
|
break;
|
||||||
|
var pos = map.map[index];
|
||||||
|
var cell = table.nodeAt(pos);
|
||||||
|
if (!cell)
|
||||||
|
continue;
|
||||||
|
cells.push({ cell: cell, pos: pos });
|
||||||
|
row += cell.attrs.rowspan;
|
||||||
|
console.log(cell.textContent);
|
||||||
|
}
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
function moveRow(tr, rect, from, to) {
|
||||||
|
var fromCells = getRowCells(rect, from);
|
||||||
|
var toCells = getRowCells(rect, to);
|
||||||
|
return moveCells(tr, rect, fromCells, toCells);
|
||||||
|
}
|
||||||
|
function getRowCells(_a, row) {
|
||||||
|
var map = _a.map, table = _a.table;
|
||||||
|
var cells = [];
|
||||||
|
for (var col = 0, index = row * map.width; col < map.width; col++, index++) {
|
||||||
|
if (index >= map.map.length)
|
||||||
|
break;
|
||||||
|
var pos = map.map[index];
|
||||||
|
var cell = table.nodeAt(pos);
|
||||||
|
if (!cell)
|
||||||
|
continue;
|
||||||
|
cells.push({ cell: cell, pos: pos });
|
||||||
|
col += cell.attrs.colspan - 1;
|
||||||
|
}
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
function moveCells(tr, rect, fromCells, toCells) {
|
||||||
|
if (fromCells.length !== toCells.length)
|
||||||
|
return;
|
||||||
|
var mapStart = tr.mapping.maps.length;
|
||||||
|
for (var i = 0; i < toCells.length; ++i) {
|
||||||
|
var fromCell = fromCells[i];
|
||||||
|
var toCell = toCells[i];
|
||||||
|
var fromStart = tr.mapping
|
||||||
|
.slice(mapStart)
|
||||||
|
.map(rect.tableStart + fromCell.pos);
|
||||||
|
var fromEnd = fromStart + fromCell.cell.nodeSize;
|
||||||
|
var fromSlice = tr.doc.slice(fromStart, fromEnd);
|
||||||
|
var toStart = tr.mapping
|
||||||
|
.slice(mapStart)
|
||||||
|
.map(rect.tableStart + toCell.pos);
|
||||||
|
var toEnd = toStart + toCell.cell.nodeSize;
|
||||||
|
var toSlice = tr.doc.slice(toStart, toEnd);
|
||||||
|
tr.replace(toStart, toEnd, fromSlice);
|
||||||
|
fromStart = tr.mapping.slice(mapStart).map(rect.tableStart + fromCell.pos);
|
||||||
|
fromEnd = fromStart + fromCell.cell.nodeSize;
|
||||||
|
tr.replace(fromStart, fromEnd, toSlice);
|
||||||
|
}
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
export { moveColumnLeft, moveColumnRight, moveRowDown, moveRowUp };
|
||||||
1
packages/editor/dist/toolbar/floatingmenus/table/index.d.ts
vendored
Normal file
1
packages/editor/dist/toolbar/floatingmenus/table/index.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { TableColumnFloatingMenu, TableFloatingMenu, TableRowFloatingMenu, } from "./table";
|
||||||
1
packages/editor/dist/toolbar/floatingmenus/table/index.js
vendored
Normal file
1
packages/editor/dist/toolbar/floatingmenus/table/index.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { TableColumnFloatingMenu, TableFloatingMenu, TableRowFloatingMenu, } from "./table";
|
||||||
5
packages/editor/dist/toolbar/floatingmenus/table/table.d.ts
vendored
Normal file
5
packages/editor/dist/toolbar/floatingmenus/table/table.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="react" />
|
||||||
|
import { FloatingMenuProps } from "../types";
|
||||||
|
export declare function TableRowFloatingMenu(props: FloatingMenuProps): JSX.Element | null;
|
||||||
|
export declare function TableColumnFloatingMenu(props: FloatingMenuProps): JSX.Element | null;
|
||||||
|
export declare function TableFloatingMenu(props: FloatingMenuProps): JSX.Element | null;
|
||||||
149
packages/editor/dist/toolbar/floatingmenus/table/table.js
vendored
Normal file
149
packages/editor/dist/toolbar/floatingmenus/table/table.js
vendored
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Flex } from "rebass";
|
||||||
|
import { MenuPresenter } from "../../../components/menu/menu";
|
||||||
|
import { ColumnProperties, InsertColumnRight, InsertRowBelow, RowProperties, } from "./tools";
|
||||||
|
import { getToolbarElement } from "../../utils/dom";
|
||||||
|
import { useToolbarLocation } from "../../stores/toolbar-store";
|
||||||
|
export function TableRowFloatingMenu(props) {
|
||||||
|
var editor = props.editor;
|
||||||
|
// const theme = editor.storage.theme as Theme;
|
||||||
|
var _a = __read(useState(null), 2), position = _a[0], setPosition = _a[1];
|
||||||
|
useEffect(function () {
|
||||||
|
var _a;
|
||||||
|
if (!editor.isActive("tableCell") &&
|
||||||
|
!editor.isActive("tableRow") &&
|
||||||
|
!editor.isActive("tableHeader")) {
|
||||||
|
setPosition(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var $from = editor.state.selection.$from;
|
||||||
|
var selectedNode = $from.node();
|
||||||
|
var pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
||||||
|
var currentRow = (_a = editor.view.nodeDOM(pos)) === null || _a === void 0 ? void 0 : _a.closest("tr");
|
||||||
|
if (!currentRow)
|
||||||
|
return;
|
||||||
|
setPosition(function (old) {
|
||||||
|
if ((old === null || old === void 0 ? void 0 : old.target) === currentRow)
|
||||||
|
return old;
|
||||||
|
return {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
location: "left",
|
||||||
|
xOffset: -5,
|
||||||
|
target: currentRow,
|
||||||
|
// parent: editor.view.dom as HTMLElement,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [editor.state.selection]);
|
||||||
|
if (!position)
|
||||||
|
return null;
|
||||||
|
return (_jsx(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
||||||
|
type: "autocomplete",
|
||||||
|
position: position,
|
||||||
|
} }, { children: _jsxs(Flex, __assign({ sx: {
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: isMenuOpen ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
} }, { children: [_jsx(RowProperties, { title: "Row properties", editor: editor, variant: "small", icon: "more" }), _jsx(InsertRowBelow, { title: "Insert row below", icon: "insertRowBelow", editor: editor, variant: "small" })] })) })));
|
||||||
|
}
|
||||||
|
export function TableColumnFloatingMenu(props) {
|
||||||
|
var editor = props.editor;
|
||||||
|
var _a = __read(useState(null), 2), position = _a[0], setPosition = _a[1];
|
||||||
|
useEffect(function () {
|
||||||
|
var _a;
|
||||||
|
if (!editor.isActive("tableCell") &&
|
||||||
|
!editor.isActive("tableRow") &&
|
||||||
|
!editor.isActive("tableHeader")) {
|
||||||
|
setPosition(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var $from = editor.state.selection.$from;
|
||||||
|
var selectedNode = $from.node();
|
||||||
|
var pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
||||||
|
var currentCell = (_a = editor.view.nodeDOM(pos)) === null || _a === void 0 ? void 0 : _a.closest("td,th");
|
||||||
|
var currentTable = currentCell === null || currentCell === void 0 ? void 0 : currentCell.closest("table");
|
||||||
|
if (!currentCell || !currentTable)
|
||||||
|
return;
|
||||||
|
setPosition(function (old) {
|
||||||
|
if ((old === null || old === void 0 ? void 0 : old.target) === currentCell)
|
||||||
|
return old;
|
||||||
|
return {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
location: "top",
|
||||||
|
align: "center",
|
||||||
|
yAnchor: currentTable,
|
||||||
|
yOffset: 2,
|
||||||
|
target: currentCell,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [editor.state.selection]);
|
||||||
|
if (!position)
|
||||||
|
return null;
|
||||||
|
return (_jsx(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
||||||
|
type: "autocomplete",
|
||||||
|
position: position,
|
||||||
|
} }, { children: _jsxs(Flex, __assign({ sx: {
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: 0.3,
|
||||||
|
// opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
} }, { children: [_jsx(ColumnProperties, { currentCell: position.target, title: "Column properties", editor: editor, icon: "more", variant: "small" }), _jsx(InsertColumnRight, { editor: editor, title: "Insert column right", variant: "small", icon: "plus" })] })) })));
|
||||||
|
}
|
||||||
|
export function TableFloatingMenu(props) {
|
||||||
|
var editor = props.editor;
|
||||||
|
var toolbarLocation = useToolbarLocation();
|
||||||
|
if (!editor.isActive("table"))
|
||||||
|
return null;
|
||||||
|
return (_jsx(MenuPresenter, __assign({ isOpen: true, items: [], onClose: function () { }, options: {
|
||||||
|
type: "autocomplete",
|
||||||
|
position: {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
target: getToolbarElement(),
|
||||||
|
location: toolbarLocation === "bottom" ? "top" : "below",
|
||||||
|
},
|
||||||
|
} }, { children: _jsxs(Flex, __assign({ sx: {
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: 0.3,
|
||||||
|
// opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
} }, { children: [_jsx(RowProperties, { title: "Row properties", editor: editor, variant: "normal", icon: "rowProperties" }), _jsx(InsertRowBelow, { title: "Insert row below", icon: "insertRowBelow", editor: editor, variant: "normal" }), _jsx(ColumnProperties, { title: "Column properties", editor: editor, icon: "columnProperties", variant: "normal" }), _jsx(InsertColumnRight, { editor: editor, title: "Insert column right", variant: "normal", icon: "insertColumnRight" })] })) })));
|
||||||
|
}
|
||||||
14
packages/editor/dist/toolbar/floatingmenus/table/tools.d.ts
vendored
Normal file
14
packages/editor/dist/toolbar/floatingmenus/table/tools.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/// <reference types="react" />
|
||||||
|
import { ToolButtonProps } from "../../components/tool-button";
|
||||||
|
import { ToolProps } from "../../types";
|
||||||
|
declare type TableToolProps = ToolProps & {
|
||||||
|
variant: ToolButtonProps["variant"];
|
||||||
|
};
|
||||||
|
export declare function RowProperties(props: TableToolProps): JSX.Element;
|
||||||
|
export declare function InsertRowBelow(props: TableToolProps): JSX.Element;
|
||||||
|
declare type ColumnPropertiesProps = TableToolProps & {
|
||||||
|
currentCell?: HTMLElement;
|
||||||
|
};
|
||||||
|
export declare function ColumnProperties(props: ColumnPropertiesProps): JSX.Element;
|
||||||
|
export declare function InsertColumnRight(props: TableToolProps): JSX.Element;
|
||||||
|
export {};
|
||||||
242
packages/editor/dist/toolbar/floatingmenus/table/tools.js
vendored
Normal file
242
packages/editor/dist/toolbar/floatingmenus/table/tools.js
vendored
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (_) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var __rest = (this && this.__rest) || function (s, e) {
|
||||||
|
var t = {};
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||||
|
t[p] = s[p];
|
||||||
|
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||||
|
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||||
|
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||||
|
t[p[i]] = s[p[i]];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||||
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||||
|
if (ar || !(i in from)) {
|
||||||
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||||
|
ar[i] = from[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to.concat(ar || Array.prototype.slice.call(from));
|
||||||
|
};
|
||||||
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { MenuPresenter, PopupPresenter, } from "../../../components/menu/menu";
|
||||||
|
import { ToolButton } from "../../components/tool-button";
|
||||||
|
import { CellProperties } from "../../popups/cell-properties";
|
||||||
|
import { moveColumnLeft, moveColumnRight, moveRowDown, moveRowUp, } from "./actions";
|
||||||
|
export function RowProperties(props) {
|
||||||
|
var editor = props.editor, toolProps = __rest(props, ["editor"]);
|
||||||
|
var _a = __read(useState(false), 2), isMenuOpen = _a[0], setIsMenuOpen = _a[1];
|
||||||
|
return (_jsxs(_Fragment, { children: [_jsx(ToolButton, __assign({ toggled: isMenuOpen }, toolProps, { onClick: function () { return setIsMenuOpen(true); } })), _jsx(MenuPresenter, { isOpen: isMenuOpen, onClose: function () {
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}, options: {
|
||||||
|
type: "menu",
|
||||||
|
position: {},
|
||||||
|
}, items: [
|
||||||
|
{
|
||||||
|
key: "addRowAbove",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Add row above",
|
||||||
|
onClick: function () { return editor.chain().focus().addRowBefore().run(); },
|
||||||
|
icon: "insertRowAbove",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveRowUp",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move row up",
|
||||||
|
onClick: function () { return moveRowUp(editor); },
|
||||||
|
icon: "moveRowUp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveRowDown",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move row down",
|
||||||
|
onClick: function () { return moveRowDown(editor); },
|
||||||
|
icon: "moveRowDown",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "deleteRow",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Delete row",
|
||||||
|
onClick: function () { return editor.chain().focus().deleteRow().run(); },
|
||||||
|
icon: "deleteRow",
|
||||||
|
},
|
||||||
|
] })] }));
|
||||||
|
}
|
||||||
|
export function InsertRowBelow(props) {
|
||||||
|
var editor = props.editor, toolProps = __rest(props, ["editor"]);
|
||||||
|
return (_jsx(ToolButton, __assign({ toggled: false }, toolProps, { onClick: function () { return editor.chain().focus().addRowAfter().run(); } })));
|
||||||
|
}
|
||||||
|
export function ColumnProperties(props) {
|
||||||
|
var _this = this;
|
||||||
|
var editor = props.editor, currentCell = props.currentCell, toolProps = __rest(props, ["editor", "currentCell"]);
|
||||||
|
var _a = __read(useState(false), 2), isMenuOpen = _a[0], setIsMenuOpen = _a[1];
|
||||||
|
var isInsideCellSelection = !editor.state.selection.empty &&
|
||||||
|
editor.state.selection.$anchor.node().type.name === "tableCell";
|
||||||
|
var _b = __read(useState(false), 2), showCellProps = _b[0], setShowCellProps = _b[1];
|
||||||
|
var _c = __read(useState(null), 2), menuPosition = _c[0], setMenuPosition = _c[1];
|
||||||
|
var columnProperties = [
|
||||||
|
{
|
||||||
|
key: "addColumnLeft",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Add column left",
|
||||||
|
onClick: function () { return editor.chain().focus().addColumnBefore().run(); },
|
||||||
|
icon: "insertColumnLeft",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "addColumnRight",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Add column right",
|
||||||
|
onClick: function () { return editor.chain().focus().addColumnAfter().run(); },
|
||||||
|
icon: "insertColumnRight",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveColumnLeft",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move column left",
|
||||||
|
onClick: function () { return moveColumnLeft(editor); },
|
||||||
|
icon: "moveColumnLeft",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveColumnRight",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move column right",
|
||||||
|
onClick: function () { return moveColumnRight(editor); },
|
||||||
|
icon: "moveColumnRight",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "deleteColumn",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Delete column",
|
||||||
|
onClick: function () { return editor.chain().focus().deleteColumn().run(); },
|
||||||
|
icon: "deleteColumn",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
var mergeSplitProperties = [
|
||||||
|
{
|
||||||
|
key: "splitCells",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Split cells",
|
||||||
|
onClick: function () { return editor.chain().focus().splitCell().run(); },
|
||||||
|
icon: "splitCells",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "mergeCells",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Merge cells",
|
||||||
|
onClick: function () { return editor.chain().focus().mergeCells().run(); },
|
||||||
|
icon: "mergeCells",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
var cellProperties = [
|
||||||
|
{
|
||||||
|
key: "cellProperties",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Cell properties",
|
||||||
|
onClick: function () {
|
||||||
|
setShowCellProps(true);
|
||||||
|
setMenuPosition({
|
||||||
|
target: currentCell || undefined,
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
yOffset: 10,
|
||||||
|
location: "below",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: "cellProperties",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
var tableProperties = [
|
||||||
|
{
|
||||||
|
key: "deleteTable",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Delete table",
|
||||||
|
icon: "deleteTable",
|
||||||
|
onClick: function () { return editor.chain().focus().deleteTable().run(); },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (_jsxs(_Fragment, { children: [_jsx(ToolButton, __assign({ toggled: isMenuOpen }, toolProps, { onClick: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, setIsMenuOpen(true)];
|
||||||
|
}); }); } })), _jsx(PopupPresenter, { isOpen: isMenuOpen, onClose: function () {
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}, mobile: "sheet", items: isInsideCellSelection
|
||||||
|
? __spreadArray(__spreadArray([], __read(mergeSplitProperties), false), __read(cellProperties), false) : __spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(columnProperties), false), [
|
||||||
|
{ type: "seperator", key: "cellSeperator" }
|
||||||
|
], false), __read(cellProperties), false), [
|
||||||
|
{ type: "seperator", key: "tableSeperator" }
|
||||||
|
], false), __read(tableProperties), false) }), _jsx(PopupPresenter, __assign({ isOpen: showCellProps, onClose: function () {
|
||||||
|
setShowCellProps(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}, options: {
|
||||||
|
type: "menu",
|
||||||
|
position: menuPosition || {},
|
||||||
|
}, mobile: "sheet" }, { children: _jsx(CellProperties, { editor: editor, onClose: function () { return setShowCellProps(false); } }) }))] }));
|
||||||
|
}
|
||||||
|
export function InsertColumnRight(props) {
|
||||||
|
var editor = props.editor, toolProps = __rest(props, ["editor"]);
|
||||||
|
return (_jsx(ToolButton, __assign({}, toolProps, { toggled: false, onClick: function () { return editor.chain().focus().addColumnAfter().run(); } })));
|
||||||
|
}
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
export declare type ToolbarLocation = "top" | "bottom";
|
|
||||||
export declare const ToolbarContext: React.Context<{
|
export declare const ToolbarContext: React.Context<{
|
||||||
currentPopup?: string | undefined;
|
currentPopup?: string | undefined;
|
||||||
setCurrentPopup?: React.Dispatch<React.SetStateAction<string | undefined>> | undefined;
|
setCurrentPopup?: React.Dispatch<React.SetStateAction<string | undefined>> | undefined;
|
||||||
toolbarLocation?: ToolbarLocation | undefined;
|
|
||||||
}>;
|
}>;
|
||||||
export declare function useToolbarContext(): {
|
export declare function useToolbarContext(): {
|
||||||
currentPopup?: string | undefined;
|
currentPopup?: string | undefined;
|
||||||
setCurrentPopup?: React.Dispatch<React.SetStateAction<string | undefined>> | undefined;
|
setCurrentPopup?: React.Dispatch<React.SetStateAction<string | undefined>> | undefined;
|
||||||
toolbarLocation?: ToolbarLocation | undefined;
|
|
||||||
};
|
};
|
||||||
|
|||||||
2
packages/editor/dist/toolbar/icons.d.ts
vendored
2
packages/editor/dist/toolbar/icons.d.ts
vendored
@@ -31,12 +31,14 @@ export declare const Icons: {
|
|||||||
upload: string;
|
upload: string;
|
||||||
attachment: string;
|
attachment: string;
|
||||||
table: string;
|
table: string;
|
||||||
|
rowProperties: string;
|
||||||
insertRowBelow: string;
|
insertRowBelow: string;
|
||||||
insertRowAbove: string;
|
insertRowAbove: string;
|
||||||
moveRowDown: string;
|
moveRowDown: string;
|
||||||
moveRowUp: string;
|
moveRowUp: string;
|
||||||
deleteRow: string;
|
deleteRow: string;
|
||||||
toggleHeaderRow: string;
|
toggleHeaderRow: string;
|
||||||
|
columnProperties: string;
|
||||||
insertColumnRight: string;
|
insertColumnRight: string;
|
||||||
insertColumnLeft: string;
|
insertColumnLeft: string;
|
||||||
moveColumnRight: string;
|
moveColumnRight: string;
|
||||||
|
|||||||
6
packages/editor/dist/toolbar/icons.js
vendored
6
packages/editor/dist/toolbar/icons.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import { mdiAttachment, mdiBorderHorizontal, mdiCheck, mdiChevronDown, mdiCodeBraces, mdiCodeTags, mdiDotsVertical, mdiFormatAlignCenter, mdiFormatAlignJustify, mdiFormatAlignLeft, mdiFormatAlignRight, mdiFormatBold, mdiFormatClear, mdiFormatColorHighlight, mdiFormatColorText, mdiFormatItalic, mdiFormatListBulleted, mdiFormatListNumbered, mdiFormatQuoteClose, mdiFormatStrikethrough, mdiFormatSubscript, mdiFormatSuperscript, mdiFormatTextdirectionLToR, mdiFormatTextdirectionRToL, mdiFormatUnderline, mdiImage, mdiInvertColorsOff, mdiLinkPlus, mdiLoading, mdiTable, mdiTableBorder, mdiTableRowPlusBefore, mdiTableRowRemove, mdiTableColumnPlusAfter, mdiTableColumnPlusBefore, mdiTableColumnRemove, mdiUploadOutline, mdiPlus, mdiSquareRoundedBadgeOutline, mdiFormatColorFill, mdiBorderAllVariant, mdiClose, mdiSortDescending, mdiArrowExpandRight, mdiArrowExpandLeft, mdiArrowExpandDown, mdiArrowExpandUp, mdiTrashCanOutline, mdiTableMergeCells, mdiTableSplitCell, mdiDeleteOutline, mdiDownloadOutline, mdiFormatListCheckbox, mdiDrag, mdiCheckboxMarkedOutline, mdiChevronUp, mdiArrowUp, mdiArrowDown, mdiRegex, mdiFormatLetterCase, mdiFormatLetterMatches, mdiMoviePlusOutline, mdiLink, mdiChevronRight, } from "@mdi/js";
|
import { mdiAttachment, mdiBorderHorizontal, mdiCheck, mdiChevronDown, mdiCodeBraces, mdiCodeTags, mdiDotsVertical, mdiFormatAlignCenter, mdiFormatAlignJustify, mdiFormatAlignLeft, mdiFormatAlignRight, mdiFormatBold, mdiFormatClear, mdiFormatColorHighlight, mdiFormatColorText, mdiFormatItalic, mdiFormatListBulleted, mdiFormatListNumbered, mdiFormatQuoteClose, mdiFormatStrikethrough, mdiFormatSubscript, mdiFormatSuperscript, mdiFormatTextdirectionLToR, mdiFormatTextdirectionRToL, mdiFormatUnderline, mdiImage, mdiInvertColorsOff, mdiLinkPlus, mdiLoading, mdiTable, mdiTableBorder, mdiTableRowPlusAfter, mdiTableRowPlusBefore, mdiTableRowRemove, mdiTableColumnPlusAfter, mdiTableColumnPlusBefore, mdiTableColumnRemove, mdiUploadOutline, mdiPlus, mdiSquareRoundedBadgeOutline, mdiFormatColorFill, mdiBorderAllVariant, mdiClose, mdiSortDescending, mdiArrowExpandRight, mdiArrowExpandLeft, mdiArrowExpandDown, mdiArrowExpandUp, mdiTrashCanOutline, mdiTableMergeCells, mdiTableSplitCell, mdiDeleteOutline, mdiDownloadOutline, mdiFormatListCheckbox, mdiDrag, mdiCheckboxMarkedOutline, mdiChevronUp, mdiArrowUp, mdiArrowDown, mdiRegex, mdiFormatLetterCase, mdiFormatLetterMatches, mdiMoviePlusOutline, mdiLink, mdiChevronRight, mdiTableColumnWidth, mdiTableRowHeight, } from "@mdi/js";
|
||||||
export var Icons = {
|
export var Icons = {
|
||||||
bold: mdiFormatBold,
|
bold: mdiFormatBold,
|
||||||
italic: mdiFormatItalic,
|
italic: mdiFormatItalic,
|
||||||
@@ -32,12 +32,14 @@ export var Icons = {
|
|||||||
upload: mdiUploadOutline,
|
upload: mdiUploadOutline,
|
||||||
attachment: mdiAttachment,
|
attachment: mdiAttachment,
|
||||||
table: mdiTable,
|
table: mdiTable,
|
||||||
insertRowBelow: mdiPlus,
|
rowProperties: mdiTableRowHeight,
|
||||||
|
insertRowBelow: mdiTableRowPlusAfter,
|
||||||
insertRowAbove: mdiTableRowPlusBefore,
|
insertRowAbove: mdiTableRowPlusBefore,
|
||||||
moveRowDown: mdiArrowExpandDown,
|
moveRowDown: mdiArrowExpandDown,
|
||||||
moveRowUp: mdiArrowExpandUp,
|
moveRowUp: mdiArrowExpandUp,
|
||||||
deleteRow: mdiTableRowRemove,
|
deleteRow: mdiTableRowRemove,
|
||||||
toggleHeaderRow: mdiTableBorder,
|
toggleHeaderRow: mdiTableBorder,
|
||||||
|
columnProperties: mdiTableColumnWidth,
|
||||||
insertColumnRight: mdiTableColumnPlusAfter,
|
insertColumnRight: mdiTableColumnPlusAfter,
|
||||||
insertColumnLeft: mdiTableColumnPlusBefore,
|
insertColumnLeft: mdiTableColumnPlusBefore,
|
||||||
moveColumnRight: mdiArrowExpandRight,
|
moveColumnRight: mdiArrowExpandRight,
|
||||||
|
|||||||
8
packages/editor/dist/toolbar/popups/cellproperties.d.ts
vendored
Normal file
8
packages/editor/dist/toolbar/popups/cellproperties.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/// <reference types="react" />
|
||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
declare type CellPropertiesProps = {
|
||||||
|
editor: Editor;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
export declare function CellProperties(props: CellPropertiesProps): JSX.Element;
|
||||||
|
export {};
|
||||||
83
packages/editor/dist/toolbar/popups/cellproperties.js
vendored
Normal file
83
packages/editor/dist/toolbar/popups/cellproperties.js
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
||||||
|
import { Slider } from "@rebass/forms";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { Flex, Text } from "rebass";
|
||||||
|
import { MenuPresenter } from "../../components/menu/menu";
|
||||||
|
import { Popup } from "../components/popup";
|
||||||
|
import { ToolButton } from "../components/tool-button";
|
||||||
|
export function CellProperties(props) {
|
||||||
|
var editor = props.editor, onClose = props.onClose;
|
||||||
|
var attributes = editor.getAttributes("tableCell");
|
||||||
|
return (_jsx(Popup, __assign({ title: "Cell properties", action: {
|
||||||
|
icon: "close",
|
||||||
|
iconColor: "error",
|
||||||
|
onClick: onClose,
|
||||||
|
} }, { children: _jsxs(Flex, __assign({ sx: { flexDirection: "column", px: 1, mb: 2 } }, { children: [_jsx(ColorPickerTool, { color: attributes.backgroundColor, title: "Background color", icon: "backgroundColor", onColorChange: function (color) {
|
||||||
|
return editor.commands.setCellAttribute("backgroundColor", color);
|
||||||
|
} }), _jsx(ColorPickerTool, { color: attributes.color, title: "Text color", icon: "textColor", onColorChange: function (color) {
|
||||||
|
return editor.commands.setCellAttribute("color", color);
|
||||||
|
} }), _jsx(ColorPickerTool, { color: attributes.borderColor, title: "Border color", icon: "borderColor", onColorChange: function (color) {
|
||||||
|
return editor.commands.setCellAttribute("borderColor", color);
|
||||||
|
} }), _jsxs(Flex, __assign({ sx: { flexDirection: "column" } }, { children: [_jsxs(Flex, __assign({ sx: {
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
mt: 1,
|
||||||
|
} }, { children: [_jsx(Text, __assign({ variant: "body" }, { children: "Border width" })), _jsxs(Text, __assign({ variant: "body" }, { children: [attributes.borderWidth || 1, "px"] }))] })), _jsx(Slider, { min: 1, max: 5, value: attributes.borderWidth || 1, onChange: function (e) {
|
||||||
|
editor.commands.setCellAttribute("borderWidth", e.target.valueAsNumber);
|
||||||
|
} })] }))] })) })));
|
||||||
|
}
|
||||||
|
function ColorPickerTool(props) {
|
||||||
|
var color = props.color, title = props.title, icon = props.icon, onColorChange = props.onColorChange;
|
||||||
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
|
var buttonRef = useRef(null);
|
||||||
|
return (_jsxs(_Fragment, { children: [_jsxs(Flex, __assign({ sx: { justifyContent: "space-between", alignItems: "center", mt: 1 } }, { children: [_jsx(Text, __assign({ variant: "body" }, { children: title })), _jsx(ToolButton, { buttonRef: buttonRef, toggled: isOpen, title: title, id: icon, icon: icon, iconSize: 16, sx: {
|
||||||
|
p: "2.5px",
|
||||||
|
borderRadius: "small",
|
||||||
|
backgroundColor: color || "transparent",
|
||||||
|
":hover": { bg: color, filter: "brightness(90%)" },
|
||||||
|
}, onClick: function () { return setIsOpen(true); } })] })), _jsx(MenuPresenter, __assign({ isOpen: isOpen, onClose: function () { return setIsOpen(false); }, items: [], options: {
|
||||||
|
type: "menu",
|
||||||
|
position: {
|
||||||
|
target: buttonRef.current || undefined,
|
||||||
|
location: "below",
|
||||||
|
align: "center",
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
yOffset: 5,
|
||||||
|
},
|
||||||
|
} }, { children: _jsx(Flex, { sx: {
|
||||||
|
flexDirection: "column",
|
||||||
|
bg: "background",
|
||||||
|
boxShadow: "menu",
|
||||||
|
border: "1px solid var(--border)",
|
||||||
|
borderRadius: "default",
|
||||||
|
p: 1,
|
||||||
|
width: 160,
|
||||||
|
} }) }))] }));
|
||||||
|
}
|
||||||
@@ -92,23 +92,28 @@ export function EmbedPopup(props) {
|
|||||||
src: _src,
|
src: _src,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
} }, { children: _jsxs(Flex, __assign({ sx: { width: 300, flexDirection: "column", p: 1 } }, { children: [error && (_jsxs(Text, __assign({ variant: "error", sx: {
|
} }, { children: _jsxs(Flex, __assign({ sx: { flexDirection: "column", p: 1 } }, { children: [error && (_jsxs(Text, __assign({ variant: "error", sx: {
|
||||||
bg: "errorBg",
|
bg: "errorBg",
|
||||||
color: "error",
|
color: "error",
|
||||||
p: 1,
|
p: 1,
|
||||||
borderRadius: "default",
|
borderRadius: "default",
|
||||||
} }, { children: ["Error: ", error] }))), _jsxs(Flex, { children: [_jsx(Button, __assign({ variant: "dialog", sx: {
|
} }, { children: ["Error: ", error] }))), _jsxs(Flex, __assign({ sx: { mb: 1 } }, { children: [_jsx(Button, __assign({ variant: "dialog", sx: {
|
||||||
p: 1,
|
pb: 1,
|
||||||
mr: 1,
|
mr: 1,
|
||||||
|
borderRadius: 0,
|
||||||
color: embedSource === "url" ? "primary" : "text",
|
color: embedSource === "url" ? "primary" : "text",
|
||||||
}, onClick: function () { return setEmbedSource("url"); } }, { children: "From link" })), _jsx(Button, __assign({ variant: "dialog", sx: {
|
borderBottom: "2px solid",
|
||||||
p: 1,
|
borderBottomColor: embedSource === "url" ? "primary" : "transparent",
|
||||||
|
}, onClick: function () { return setEmbedSource("url"); } }, { children: "From URL" })), _jsx(Button, __assign({ variant: "dialog", sx: {
|
||||||
|
pb: 1,
|
||||||
|
borderRadius: 0,
|
||||||
color: embedSource === "code" ? "primary" : "text",
|
color: embedSource === "code" ? "primary" : "text",
|
||||||
}, onClick: function () { return setEmbedSource("code"); } }, { children: "From code" }))] }), embedSource === "url" ? (_jsx(Input, { placeholder: "Embed source URL", value: src, autoFocus: true, onChange: function (e) { return setSrc(e.target.value); }, sx: { p: 1, mt: 1, fontSize: "body" } })) : (_jsx(Textarea, { autoFocus: true, variant: "forms.input", sx: { fontSize: "subBody", fontFamily: "monospace", mt: 1 }, minHeight: 100, onChange: function (e) { return setSrc(e.target.value); }, placeholder: "Paste embed code here. Only iframes are supported." })), embedSource === "url" ? (_jsxs(Flex, __assign({ sx: { alignItems: "center", mt: 2 } }, { children: [_jsx(Input, { type: "number", placeholder: "Width", value: width, sx: {
|
borderBottom: "2px solid",
|
||||||
mr: 2,
|
borderBottomColor: embedSource === "code" ? "primary" : "transparent",
|
||||||
p: 1,
|
}, onClick: function () { return setEmbedSource("code"); } }, { children: "From code" }))] })), embedSource === "url" ? (_jsx(Input, { placeholder: "Enter embed source URL", value: src, autoFocus: true, onChange: function (e) { return setSrc(e.target.value); }, sx: { mt: 1, fontSize: "body" } })) : (_jsx(Textarea, { autoFocus: true, variant: "forms.input", sx: { fontSize: "subBody", fontFamily: "monospace", mt: 1 }, minHeight: [200, 100], onChange: function (e) { return setSrc(e.target.value); }, placeholder: "Paste embed code here. Only iframes are supported." })), embedSource === "url" ? (_jsxs(Flex, __assign({ sx: { alignItems: "center", mt: 1 } }, { children: [_jsx(Input, { type: "number", placeholder: "Width", value: width, sx: {
|
||||||
|
mr: 1,
|
||||||
fontSize: "body",
|
fontSize: "body",
|
||||||
}, onChange: function (e) { return onSizeChange(e.target.valueAsNumber); } }), _jsx(Input, { type: "number", placeholder: "Height", value: height, sx: { p: 1, fontSize: "body" }, onChange: function (e) { return onSizeChange(undefined, e.target.valueAsNumber); } })] }))) : null] })) })));
|
}, onChange: function (e) { return onSizeChange(e.target.valueAsNumber); } }), _jsx(Input, { type: "number", placeholder: "Height", value: height, sx: { fontSize: "body" }, onChange: function (e) { return onSizeChange(undefined, e.target.valueAsNumber); } })] }))) : null] })) })));
|
||||||
}
|
}
|
||||||
function getAttribute(document, id) {
|
function getAttribute(document, id) {
|
||||||
var element = document.querySelector("[".concat(id, "]"));
|
var element = document.querySelector("[".concat(id, "]"));
|
||||||
|
|||||||
7
packages/editor/dist/toolbar/popups/imageproperties.d.ts
vendored
Normal file
7
packages/editor/dist/toolbar/popups/imageproperties.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/// <reference types="react" />
|
||||||
|
import { ImageAlignmentOptions, ImageSizeOptions } from "../../extensions/image";
|
||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
export declare type ImagePropertiesProps = ImageSizeOptions & ImageAlignmentOptions & {
|
||||||
|
editor: Editor;
|
||||||
|
};
|
||||||
|
export declare function ImageProperties(props: ImagePropertiesProps): JSX.Element;
|
||||||
46
packages/editor/dist/toolbar/popups/imageproperties.js
vendored
Normal file
46
packages/editor/dist/toolbar/popups/imageproperties.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
|
import { Flex, Text } from "rebass";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { Toggle } from "../../components/toggle";
|
||||||
|
import { Input } from "@rebass/forms";
|
||||||
|
export function ImageProperties(props) {
|
||||||
|
var height = props.height, width = props.width, float = props.float, editor = props.editor;
|
||||||
|
var onSizeChange = useCallback(function (newWidth, newHeight) {
|
||||||
|
var size = newWidth
|
||||||
|
? {
|
||||||
|
width: newWidth,
|
||||||
|
height: newWidth * (height / width),
|
||||||
|
}
|
||||||
|
: newHeight
|
||||||
|
? {
|
||||||
|
width: newHeight * (width / height),
|
||||||
|
height: newHeight,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
};
|
||||||
|
editor.chain().setImageSize(size).run();
|
||||||
|
}, [width, height]);
|
||||||
|
return (_jsxs(Flex, __assign({ sx: { width: 200, flexDirection: "column", p: 1 } }, { children: [_jsxs(Flex, __assign({ sx: { justifyContent: "space-between", alignItems: "center" } }, { children: [_jsx(Text, __assign({ variant: "body" }, { children: "Floating?" })), _jsx(Toggle, { checked: float, onClick: function () {
|
||||||
|
return editor
|
||||||
|
.chain()
|
||||||
|
.setImageAlignment({ float: !float, align: "left" })
|
||||||
|
.run();
|
||||||
|
} })] })), _jsxs(Flex, __assign({ sx: { alignItems: "center", mt: 2 } }, { children: [_jsx(Input, { type: "number", placeholder: "Width", value: width, sx: {
|
||||||
|
mr: 2,
|
||||||
|
p: 1,
|
||||||
|
fontSize: "body",
|
||||||
|
}, onChange: function (e) { return onSizeChange(e.target.valueAsNumber); } }), _jsx(Input, { type: "number", placeholder: "Height", value: height, sx: { p: 1, fontSize: "body" }, onChange: function (e) { return onSizeChange(undefined, e.target.valueAsNumber); } })] }))] })));
|
||||||
|
}
|
||||||
6
packages/editor/dist/toolbar/popups/searchreplace.d.ts
vendored
Normal file
6
packages/editor/dist/toolbar/popups/searchreplace.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/// <reference types="react" />
|
||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
export declare type SearchReplacePopupProps = {
|
||||||
|
editor: Editor;
|
||||||
|
};
|
||||||
|
export declare function SearchReplacePopup(props: SearchReplacePopupProps): JSX.Element;
|
||||||
103
packages/editor/dist/toolbar/popups/searchreplace.js
vendored
Normal file
103
packages/editor/dist/toolbar/popups/searchreplace.js
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
|
import { Input } from "@rebass/forms";
|
||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { Flex } from "rebass";
|
||||||
|
import { ToolButton } from "../components/tool-button";
|
||||||
|
export function SearchReplacePopup(props) {
|
||||||
|
var editor = props.editor;
|
||||||
|
var selectedText = editor.storage.searchreplace.selectedText;
|
||||||
|
var _a = __read(useState(false), 2), matchCase = _a[0], setMatchCase = _a[1];
|
||||||
|
var _b = __read(useState(false), 2), matchWholeWord = _b[0], setMatchWholeWord = _b[1];
|
||||||
|
var _c = __read(useState(false), 2), enableRegex = _c[0], setEnableRegex = _c[1];
|
||||||
|
var replaceText = useRef("");
|
||||||
|
var searchInputRef = useRef();
|
||||||
|
var search = useCallback(function (term) {
|
||||||
|
editor.commands.search(term, {
|
||||||
|
matchCase: matchCase,
|
||||||
|
enableRegex: enableRegex,
|
||||||
|
matchWholeWord: matchWholeWord,
|
||||||
|
});
|
||||||
|
}, [matchCase, enableRegex, matchWholeWord]);
|
||||||
|
useEffect(function () {
|
||||||
|
if (!searchInputRef.current)
|
||||||
|
return;
|
||||||
|
search(searchInputRef.current.value);
|
||||||
|
}, [search, matchCase, matchWholeWord, enableRegex]);
|
||||||
|
useEffect(function () {
|
||||||
|
if (selectedText) {
|
||||||
|
if (searchInputRef.current) {
|
||||||
|
var input_1 = searchInputRef.current;
|
||||||
|
setTimeout(function () {
|
||||||
|
input_1.value = selectedText;
|
||||||
|
input_1.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
search(selectedText);
|
||||||
|
}
|
||||||
|
}, [selectedText, search]);
|
||||||
|
return (
|
||||||
|
// <MenuPresenter
|
||||||
|
// isOpen
|
||||||
|
// items={[]}
|
||||||
|
// onClose={() => {}}
|
||||||
|
// options={{
|
||||||
|
// type: "autocomplete",
|
||||||
|
// position: {
|
||||||
|
// target:
|
||||||
|
// document.querySelector<HTMLElement>(".editor-toolbar") || "mouse",
|
||||||
|
// isTargetAbsolute: true,
|
||||||
|
// location: "below",
|
||||||
|
// align: "end",
|
||||||
|
// },
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Popup>
|
||||||
|
_jsxs(Flex, __assign({ sx: { p: 1, flexDirection: "column" } }, { children: [_jsxs(Flex, __assign({ sx: { alignItems: "start", flexShrink: 0 } }, { children: [_jsxs(Flex, __assign({ sx: {
|
||||||
|
position: "relative",
|
||||||
|
mr: 1,
|
||||||
|
width: 200,
|
||||||
|
alignItems: "center",
|
||||||
|
} }, { children: [_jsx(Input, { defaultValue: selectedText, ref: searchInputRef, autoFocus: true, sx: { p: 1 }, placeholder: "Find", onChange: function (e) {
|
||||||
|
search(e.target.value);
|
||||||
|
} }), _jsxs(Flex, __assign({ sx: {
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
mr: 0,
|
||||||
|
} }, { children: [_jsx(ToolButton, { sx: {
|
||||||
|
mr: 0,
|
||||||
|
}, toggled: matchCase, title: "Match case", id: "matchCase", icon: "caseSensitive", onClick: function () { return setMatchCase(function (s) { return !s; }); }, iconSize: 14 }), _jsx(ToolButton, { sx: {
|
||||||
|
mr: 0,
|
||||||
|
}, toggled: matchWholeWord, title: "Match whole word", id: "matchWholeWord", icon: "wholeWord", onClick: function () { return setMatchWholeWord(function (s) { return !s; }); }, iconSize: 14 }), _jsx(ToolButton, { sx: {
|
||||||
|
mr: 0,
|
||||||
|
}, toggled: enableRegex, title: "Enable regex", id: "enableRegex", icon: "regex", onClick: function () { return setEnableRegex(function (s) { return !s; }); }, iconSize: 14 })] }))] })), _jsx(ToolButton, { toggled: false, title: "Previous match", id: "previousMatch", icon: "previousMatch", onClick: function () { return editor.commands.moveToPreviousResult(); }, sx: { mr: 0 }, iconSize: 16 }), _jsx(ToolButton, { toggled: false, title: "Next match", id: "nextMatch", icon: "nextMatch", onClick: function () { return editor.commands.moveToNextResult(); }, sx: { mr: 0 }, iconSize: 16 }), _jsx(ToolButton, { toggled: false, title: "Close", id: "close", icon: "close", onClick: function () { return editor.chain().focus().endSearch().run(); }, iconSize: 16, sx: { mr: 0 } })] })), _jsxs(Flex, __assign({ sx: { alignItems: "start", flexShrink: 0, mt: 1 } }, { children: [_jsx(Input, { sx: { p: 1, width: 200, mr: 1 }, placeholder: "Replace", onChange: function (e) { return (replaceText.current = e.target.value); } }), _jsx(ToolButton, { toggled: false, title: "Replace", id: "replace", icon: "replaceOne", onClick: function () { return editor.commands.replace(replaceText.current); }, sx: { mr: 0 }, iconSize: 16 }), _jsx(ToolButton, { toggled: false, title: "Replace all", id: "replaceAll", icon: "replaceAll", onClick: function () { return editor.commands.replaceAll(replaceText.current); }, sx: { mr: 0 }, iconSize: 16 })] }))] }))
|
||||||
|
// </Popup>
|
||||||
|
// </MenuPresenter>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ declare type TableSize = {
|
|||||||
rows: number;
|
rows: number;
|
||||||
};
|
};
|
||||||
export declare type TablePopupProps = {
|
export declare type TablePopupProps = {
|
||||||
onClose: (size: TableSize) => void;
|
onInsertTable: (size: TableSize) => void;
|
||||||
|
cellSize?: number;
|
||||||
|
autoExpand?: boolean;
|
||||||
};
|
};
|
||||||
export declare function TablePopup(props: TablePopupProps): JSX.Element;
|
export declare function TablePopup(props: TablePopupProps): JSX.Element;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -28,12 +28,14 @@ var __read = (this && this.__read) || function (o, n) {
|
|||||||
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
import { Box, Flex, Text } from "rebass";
|
import { Box, Flex, Text } from "rebass";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { Popup } from "../components/popup";
|
||||||
|
import { Input } from "@rebass/forms";
|
||||||
var MAX_COLUMNS = 20;
|
var MAX_COLUMNS = 20;
|
||||||
var MAX_ROWS = 20;
|
var MAX_ROWS = 20;
|
||||||
var MIN_COLUMNS = 12;
|
var MIN_COLUMNS = 12;
|
||||||
var MIN_ROWS = 6;
|
var MIN_ROWS = 6;
|
||||||
export function TablePopup(props) {
|
export function TablePopup(props) {
|
||||||
var onClose = props.onClose;
|
var onInsertTable = props.onInsertTable, cellSize = props.cellSize, autoExpand = props.autoExpand;
|
||||||
var _a = __read(useState({
|
var _a = __read(useState({
|
||||||
column: 0,
|
column: 0,
|
||||||
row: 0,
|
row: 0,
|
||||||
@@ -43,6 +45,8 @@ export function TablePopup(props) {
|
|||||||
rows: MIN_ROWS,
|
rows: MIN_ROWS,
|
||||||
}), 2), tableSize = _b[0], setTableSize = _b[1];
|
}), 2), tableSize = _b[0], setTableSize = _b[1];
|
||||||
useEffect(function () {
|
useEffect(function () {
|
||||||
|
if (!autoExpand)
|
||||||
|
return;
|
||||||
setTableSize(function (old) {
|
setTableSize(function (old) {
|
||||||
var columns = old.columns, rows = old.rows;
|
var columns = old.columns, rows = old.rows;
|
||||||
var column = cellLocation.column, row = cellLocation.row;
|
var column = cellLocation.column, row = cellLocation.row;
|
||||||
@@ -58,31 +62,57 @@ export function TablePopup(props) {
|
|||||||
: Math.min(old.rows + rowFactor, MAX_ROWS),
|
: Math.min(old.rows + rowFactor, MAX_ROWS),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [cellLocation]);
|
}, [cellLocation, autoExpand]);
|
||||||
return (_jsxs(Flex, __assign({ sx: { p: 1, flexDirection: "column", alignItems: "center" } }, { children: [_jsx(Box, __assign({ sx: {
|
return (_jsx(Popup, __assign({ title: "Insert table", action: {
|
||||||
display: "grid",
|
icon: "check",
|
||||||
gridTemplateColumns: "1fr ".repeat(tableSize.columns),
|
onClick: function () {
|
||||||
gap: "3px",
|
onInsertTable({
|
||||||
bg: "background",
|
columns: cellLocation.column,
|
||||||
} }, { children: Array(tableSize.columns * tableSize.rows)
|
rows: cellLocation.row,
|
||||||
.fill(0)
|
});
|
||||||
.map(function (_, index) { return (_jsx(Box, { width: 15, height: 15, sx: {
|
},
|
||||||
border: "1px solid var(--disabled)",
|
} }, { children: _jsxs(Flex, __assign({ sx: { p: 1, flexDirection: "column", alignItems: "center" } }, { children: [_jsx(Box, __assign({ sx: {
|
||||||
borderRadius: "2px",
|
display: "grid",
|
||||||
bg: isCellHighlighted(index, cellLocation, tableSize)
|
gridTemplateColumns: "repeat(".concat(tableSize.columns, ", minmax(").concat(cellSize || 15, "px, 1fr))"),
|
||||||
? "disabled"
|
gap: "3px",
|
||||||
: "transparent",
|
bg: "background",
|
||||||
":hover": {
|
width: "100%",
|
||||||
bg: "disabled",
|
}, onTouchMove: function (e) {
|
||||||
},
|
var touch = e.touches.item(0);
|
||||||
}, onMouseEnter: function () {
|
var element = document.elementFromPoint(touch.pageX, touch.pageY);
|
||||||
setCellLocation(getCellLocation(index, tableSize));
|
if (!element)
|
||||||
}, onClick: function () {
|
return;
|
||||||
onClose({
|
var index = element.dataset.index;
|
||||||
columns: cellLocation.column,
|
if (!index)
|
||||||
rows: cellLocation.row,
|
return;
|
||||||
});
|
setCellLocation(getCellLocation(parseInt(index), tableSize));
|
||||||
} })); }) })), _jsxs(Text, __assign({ variant: "body", sx: { mt: 1 } }, { children: [cellLocation.column, "x", cellLocation.row] }))] })));
|
} }, { children: Array(tableSize.columns * tableSize.rows)
|
||||||
|
.fill(0)
|
||||||
|
.map(function (_, index) { return (_jsx(Box, { "data-index": index, height: cellSize || 15, sx: {
|
||||||
|
border: "1px solid var(--disabled)",
|
||||||
|
borderRadius: "2px",
|
||||||
|
bg: isCellHighlighted(index, cellLocation, tableSize)
|
||||||
|
? "disabled"
|
||||||
|
: "transparent",
|
||||||
|
}, onTouchStart: function () {
|
||||||
|
setCellLocation(getCellLocation(index, tableSize));
|
||||||
|
}, onMouseEnter: function () {
|
||||||
|
setCellLocation(getCellLocation(index, tableSize));
|
||||||
|
}, onClick: function () {
|
||||||
|
onInsertTable({
|
||||||
|
columns: cellLocation.column,
|
||||||
|
rows: cellLocation.row,
|
||||||
|
});
|
||||||
|
} })); }) })), _jsxs(Flex, __assign({ sx: {
|
||||||
|
display: ["flex", "none"],
|
||||||
|
my: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
} }, { children: [_jsx(Input, { placeholder: "".concat(cellLocation.column, " columns"), sx: { mr: 1 }, type: "number", value: cellLocation.column, onChange: function (e) {
|
||||||
|
setCellLocation(function (l) { return (__assign(__assign({}, l), { column: e.target.valueAsNumber || 0 })); });
|
||||||
|
} }), _jsx(Input, { placeholder: "".concat(cellLocation.row, " rows"), type: "number", value: cellLocation.row, onChange: function (e) {
|
||||||
|
setCellLocation(function (l) { return (__assign(__assign({}, l), { row: e.target.valueAsNumber || 0 })); });
|
||||||
|
} })] })), _jsxs(Text, __assign({ variant: "body", sx: { mt: 1, display: ["none", "block"] } }, { children: [cellLocation.column, "x", cellLocation.row] }))] })) })));
|
||||||
}
|
}
|
||||||
function getCellLocation(index, tableSize) {
|
function getCellLocation(index, tableSize) {
|
||||||
var cellIndex = index + 1;
|
var cellIndex = index + 1;
|
||||||
|
|||||||
11
packages/editor/dist/toolbar/stores/toolbarstore.d.ts
vendored
Normal file
11
packages/editor/dist/toolbar/stores/toolbarstore.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export declare type ToolbarLocation = "top" | "bottom";
|
||||||
|
interface ToolbarState {
|
||||||
|
isMobile: boolean;
|
||||||
|
setIsMobile: (isMobile: boolean) => void;
|
||||||
|
toolbarLocation: ToolbarLocation;
|
||||||
|
setToolbarLocation: (location: ToolbarLocation) => void;
|
||||||
|
}
|
||||||
|
export declare const useToolbarStore: import("zustand").UseBoundStore<ToolbarState, import("zustand").StoreApi<ToolbarState>>;
|
||||||
|
export declare function useToolbarLocation(): ToolbarLocation;
|
||||||
|
export declare function useIsMobile(): boolean;
|
||||||
|
export {};
|
||||||
21
packages/editor/dist/toolbar/stores/toolbarstore.js
vendored
Normal file
21
packages/editor/dist/toolbar/stores/toolbarstore.js
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import create from "zustand";
|
||||||
|
export var useToolbarStore = create(function (set) { return ({
|
||||||
|
isMobile: false,
|
||||||
|
setIsMobile: function (isMobile) {
|
||||||
|
return set(function (state) {
|
||||||
|
state.isMobile = isMobile;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toolbarLocation: "top",
|
||||||
|
setToolbarLocation: function (location) {
|
||||||
|
return set(function (state) {
|
||||||
|
state.toolbarLocation = location;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}); });
|
||||||
|
export function useToolbarLocation() {
|
||||||
|
return useToolbarStore(function (store) { return store.toolbarLocation; });
|
||||||
|
}
|
||||||
|
export function useIsMobile() {
|
||||||
|
return useToolbarStore(function (store) { return store.isMobile; });
|
||||||
|
}
|
||||||
3
packages/editor/dist/toolbar/toolbar.d.ts
vendored
3
packages/editor/dist/toolbar/toolbar.d.ts
vendored
@@ -1,10 +1,11 @@
|
|||||||
/// <reference types="react" />
|
/// <reference types="react" />
|
||||||
import { ThemeConfig } from "@notesnook/theme/dist/theme/types";
|
import { ThemeConfig } from "@notesnook/theme/dist/theme/types";
|
||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { ToolbarLocation } from "./hooks/useToolbarContext";
|
import { ToolbarLocation } from "./stores/toolbar-store";
|
||||||
declare type ToolbarProps = ThemeConfig & {
|
declare type ToolbarProps = ThemeConfig & {
|
||||||
editor: Editor | null;
|
editor: Editor | null;
|
||||||
location: ToolbarLocation;
|
location: ToolbarLocation;
|
||||||
|
isMobile?: boolean;
|
||||||
};
|
};
|
||||||
export declare function Toolbar(props: ToolbarProps): JSX.Element | null;
|
export declare function Toolbar(props: ToolbarProps): JSX.Element | null;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
22
packages/editor/dist/toolbar/toolbar.js
vendored
22
packages/editor/dist/toolbar/toolbar.js
vendored
@@ -44,14 +44,20 @@ import { ThemeProvider } from "emotion-theming";
|
|||||||
import { EditorFloatingMenus } from "./floating-menus";
|
import { EditorFloatingMenus } from "./floating-menus";
|
||||||
import { getToolDefinition } from "./tool-definitions";
|
import { getToolDefinition } from "./tool-definitions";
|
||||||
import { ToolButton } from "./components/tool-button";
|
import { ToolButton } from "./components/tool-button";
|
||||||
import { useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { MenuPresenter } from "../components/menu";
|
import { MenuPresenter } from "../components/menu";
|
||||||
import { Popup } from "./components/popup";
|
import { Popup } from "./components/popup";
|
||||||
import { ToolbarContext, useToolbarContext, } from "./hooks/useToolbarContext";
|
import { ToolbarContext, useToolbarContext } from "./hooks/useToolbarContext";
|
||||||
|
import { useToolbarLocation, useToolbarStore, } from "./stores/toolbar-store";
|
||||||
export function Toolbar(props) {
|
export function Toolbar(props) {
|
||||||
var editor = props.editor, theme = props.theme, accent = props.accent, scale = props.scale, location = props.location;
|
var editor = props.editor, theme = props.theme, accent = props.accent, scale = props.scale, location = props.location, isMobile = props.isMobile;
|
||||||
var themeProperties = useTheme({ accent: accent, theme: theme, scale: scale });
|
var themeProperties = useTheme({ accent: accent, theme: theme, scale: scale });
|
||||||
var _a = __read(useState(), 2), currentPopup = _a[0], setCurrentPopup = _a[1];
|
var _a = __read(useState(), 2), currentPopup = _a[0], setCurrentPopup = _a[1];
|
||||||
|
var _b = useToolbarStore(), setIsMobile = _b.setIsMobile, setToolbarLocation = _b.setToolbarLocation;
|
||||||
|
useEffect(function () {
|
||||||
|
setIsMobile(isMobile || false);
|
||||||
|
setToolbarLocation(location);
|
||||||
|
}, [isMobile, location]);
|
||||||
var tools = [
|
var tools = [
|
||||||
["insertBlock"],
|
["insertBlock"],
|
||||||
[
|
[
|
||||||
@@ -75,7 +81,10 @@ export function Toolbar(props) {
|
|||||||
];
|
];
|
||||||
if (!editor)
|
if (!editor)
|
||||||
return null;
|
return null;
|
||||||
return (_jsxs(ThemeProvider, __assign({ theme: themeProperties }, { children: [_jsx(ToolbarContext.Provider, __assign({ value: { setCurrentPopup: setCurrentPopup, currentPopup: currentPopup, toolbarLocation: location } }, { children: _jsx(Flex, __assign({ className: "editor-toolbar", sx: { flexWrap: ["nowrap", "wrap"], overflowX: ["auto", "hidden"] } }, { children: tools.map(function (tools) {
|
return (_jsxs(ThemeProvider, __assign({ theme: themeProperties }, { children: [_jsx(ToolbarContext.Provider, __assign({ value: {
|
||||||
|
setCurrentPopup: setCurrentPopup,
|
||||||
|
currentPopup: currentPopup,
|
||||||
|
} }, { children: _jsx(Flex, __assign({ className: "editor-toolbar", sx: { flexWrap: ["nowrap", "wrap"], overflowX: ["auto", "hidden"] } }, { children: tools.map(function (tools) {
|
||||||
return (_jsx(ToolbarGroup, { tools: tools, editor: editor, sx: {
|
return (_jsx(ToolbarGroup, { tools: tools, editor: editor, sx: {
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
pr: 2,
|
pr: 2,
|
||||||
@@ -94,13 +103,14 @@ function ToolbarGroup(props) {
|
|||||||
else {
|
else {
|
||||||
var Component = findToolById(toolId);
|
var Component = findToolById(toolId);
|
||||||
var toolDefinition = getToolDefinition(toolId);
|
var toolDefinition = getToolDefinition(toolId);
|
||||||
return _jsx(Component, __assign({ editor: editor, id: toolId }, toolDefinition));
|
return _jsx(Component, __assign({ editor: editor }, toolDefinition));
|
||||||
}
|
}
|
||||||
}) })));
|
}) })));
|
||||||
}
|
}
|
||||||
function MoreTools(props) {
|
function MoreTools(props) {
|
||||||
var popupId = props.popupId;
|
var popupId = props.popupId;
|
||||||
var _a = useToolbarContext(), currentPopup = _a.currentPopup, setCurrentPopup = _a.setCurrentPopup, toolbarLocation = _a.toolbarLocation;
|
var _a = useToolbarContext(), currentPopup = _a.currentPopup, setCurrentPopup = _a.setCurrentPopup;
|
||||||
|
var toolbarLocation = useToolbarLocation();
|
||||||
var buttonRef = useRef();
|
var buttonRef = useRef();
|
||||||
var show = popupId === currentPopup;
|
var show = popupId === currentPopup;
|
||||||
var setShow = function (state) {
|
var setShow = function (state) {
|
||||||
|
|||||||
102
packages/editor/dist/toolbar/tools/block.js
vendored
102
packages/editor/dist/toolbar/tools/block.js
vendored
@@ -27,16 +27,17 @@ var __read = (this && this.__read) || function (o, n) {
|
|||||||
};
|
};
|
||||||
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
import { Icons } from "../icons";
|
import { Icons } from "../icons";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import { ActionSheetPresenter, } from "../../components/menu/menu";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Icon } from "../components/icon";
|
import { Icon } from "../components/icon";
|
||||||
import { Button } from "rebass";
|
import { Button } from "rebass";
|
||||||
|
import { EmbedPopup } from "../popups/embed-popup";
|
||||||
import { TablePopup } from "../popups/table-popup";
|
import { TablePopup } from "../popups/table-popup";
|
||||||
import { useToolbarContext } from "../hooks/useToolbarContext";
|
import { useToolbarLocation } from "../stores/toolbar-store";
|
||||||
export function InsertBlock(props) {
|
export function InsertBlock(props) {
|
||||||
var buttonRef = useRef();
|
var buttonRef = useRef();
|
||||||
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
var toolbarLocation = useToolbarContext().toolbarLocation;
|
var toolbarLocation = useToolbarLocation();
|
||||||
return (_jsxs(_Fragment, { children: [_jsx(Button, __assign({ ref: buttonRef, sx: {
|
return (_jsxs(_Fragment, { children: [_jsx(Button, __assign({ ref: buttonRef, sx: {
|
||||||
p: 1,
|
p: 1,
|
||||||
m: 0,
|
m: 0,
|
||||||
@@ -48,23 +49,15 @@ export function InsertBlock(props) {
|
|||||||
":last-of-type": {
|
":last-of-type": {
|
||||||
mr: 0,
|
mr: 0,
|
||||||
},
|
},
|
||||||
}, onMouseDown: function (e) { return e.preventDefault(); }, onClick: function () { return setIsOpen(function (s) { return !s; }); } }, { children: _jsx(Icon, { path: Icons.plus, size: 18, color: "primary" }) })), _jsx(MenuPresenter, { options: {
|
}, onMouseDown: function (e) { return e.preventDefault(); }, onClick: function () { return setIsOpen(function (s) { return !s; }); } }, { children: _jsx(Icon, { path: Icons.plus, size: 18, color: "primary" }) })), _jsx(ActionSheetPresenter, { title: "Choose a block to insert", isOpen: isOpen, items: [
|
||||||
type: "menu",
|
|
||||||
position: {
|
|
||||||
target: buttonRef.current || undefined,
|
|
||||||
isTargetAbsolute: true,
|
|
||||||
location: toolbarLocation === "bottom" ? "top" : "below",
|
|
||||||
yOffset: 5,
|
|
||||||
},
|
|
||||||
}, isOpen: isOpen, items: [
|
|
||||||
tasklist(editor),
|
tasklist(editor),
|
||||||
horizontalRule(editor),
|
horizontalRule(editor),
|
||||||
codeblock(editor),
|
codeblock(editor),
|
||||||
blockquote(editor),
|
blockquote(editor),
|
||||||
image(editor),
|
imageActionSheet(editor),
|
||||||
attachment(editor),
|
attachment(editor),
|
||||||
embed(editor),
|
embedActionSheet(editor),
|
||||||
table(editor),
|
tableActionSheet(editor),
|
||||||
], onClose: function () { return setIsOpen(false); } })] }));
|
], onClose: function () { return setIsOpen(false); } })] }));
|
||||||
}
|
}
|
||||||
var horizontalRule = function (editor) { return ({
|
var horizontalRule = function (editor) { return ({
|
||||||
@@ -113,6 +106,38 @@ var image = function (editor) { return ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}); };
|
}); };
|
||||||
|
var imageActionSheet = function (editor) { return ({
|
||||||
|
key: "image",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Image",
|
||||||
|
icon: "image",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "imageOptions",
|
||||||
|
type: "menuitem",
|
||||||
|
component: function (_a) {
|
||||||
|
var onClick = _a.onClick;
|
||||||
|
var _b = __read(useState(true), 2), isOpen = _b[0], setIsOpen = _b[1];
|
||||||
|
return (_jsx(ActionSheetPresenter, { isOpen: isOpen, onClose: function () { return setIsOpen(false); }, items: [
|
||||||
|
{
|
||||||
|
key: "upload-from-disk",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Upload from disk",
|
||||||
|
icon: "upload",
|
||||||
|
onClick: function () { },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "upload-from-url",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Attach from URL",
|
||||||
|
icon: "link",
|
||||||
|
onClick: function () { },
|
||||||
|
},
|
||||||
|
] }));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}); };
|
||||||
var embed = function (editor) { return ({
|
var embed = function (editor) { return ({
|
||||||
key: "embed",
|
key: "embed",
|
||||||
type: "menuitem",
|
type: "menuitem",
|
||||||
@@ -128,7 +153,7 @@ var table = function (editor) { return ({
|
|||||||
{
|
{
|
||||||
key: "table-size-selector",
|
key: "table-size-selector",
|
||||||
type: "menuitem",
|
type: "menuitem",
|
||||||
component: function (props) { return (_jsx(TablePopup, { onClose: function (size) {
|
component: function (props) { return (_jsx(TablePopup, { onInsertTable: function (size) {
|
||||||
var _a;
|
var _a;
|
||||||
editor === null || editor === void 0 ? void 0 : editor.chain().focus().insertTable({
|
editor === null || editor === void 0 ? void 0 : editor.chain().focus().insertTable({
|
||||||
rows: size.rows,
|
rows: size.rows,
|
||||||
@@ -139,6 +164,51 @@ var table = function (editor) { return ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}); };
|
}); };
|
||||||
|
var embedActionSheet = function (editor) { return ({
|
||||||
|
key: "embed",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Embed",
|
||||||
|
icon: "embed",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "table-size-selector",
|
||||||
|
type: "menuitem",
|
||||||
|
component: function (_a) {
|
||||||
|
var onClick = _a.onClick;
|
||||||
|
var _b = __read(useState(true), 2), isOpen = _b[0], setIsOpen = _b[1];
|
||||||
|
return (_jsx(ActionSheetPresenter, __assign({ isOpen: isOpen, onClose: function () { return setIsOpen(false); }, items: [] }, { children: _jsx(EmbedPopup, { title: "Insert embed", icon: "check", onClose: function (embed) {
|
||||||
|
editor === null || editor === void 0 ? void 0 : editor.chain().insertEmbed(embed).run();
|
||||||
|
setIsOpen(false);
|
||||||
|
onClick === null || onClick === void 0 ? void 0 : onClick();
|
||||||
|
} }) })));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}); };
|
||||||
|
var tableActionSheet = function (editor) { return ({
|
||||||
|
key: "table",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Table",
|
||||||
|
icon: "table",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "table-size-selector",
|
||||||
|
type: "menuitem",
|
||||||
|
component: function (_a) {
|
||||||
|
var onClick = _a.onClick;
|
||||||
|
var _b = __read(useState(true), 2), isOpen = _b[0], setIsOpen = _b[1];
|
||||||
|
return (_jsx(ActionSheetPresenter, __assign({ isOpen: isOpen, onClose: function () { return setIsOpen(false); }, items: [] }, { children: _jsx(TablePopup, { cellSize: 30, autoExpand: false, onInsertTable: function (size) {
|
||||||
|
editor === null || editor === void 0 ? void 0 : editor.chain().focus().insertTable({
|
||||||
|
rows: size.rows,
|
||||||
|
cols: size.columns,
|
||||||
|
}).run();
|
||||||
|
setIsOpen(false);
|
||||||
|
onClick === null || onClick === void 0 ? void 0 : onClick();
|
||||||
|
} }) })));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}); };
|
||||||
var attachment = function (editor) { return ({
|
var attachment = function (editor) { return ({
|
||||||
key: "attachment",
|
key: "attachment",
|
||||||
type: "menuitem",
|
type: "menuitem",
|
||||||
|
|||||||
38
packages/editor/dist/toolbar/tools/font.js
vendored
38
packages/editor/dist/toolbar/tools/font.js
vendored
@@ -1,3 +1,14 @@
|
|||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
var __read = (this && this.__read) || function (o, n) {
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
if (!m) return o;
|
if (!m) return o;
|
||||||
@@ -14,23 +25,32 @@ var __read = (this && this.__read) || function (o, n) {
|
|||||||
}
|
}
|
||||||
return ar;
|
return ar;
|
||||||
};
|
};
|
||||||
import { jsx as _jsx } from "react/jsx-runtime";
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
||||||
import { Dropdown } from "../components/dropdown";
|
import { Dropdown } from "../components/dropdown";
|
||||||
|
import { Box, Button } from "rebass";
|
||||||
var defaultFontSizes = [
|
var defaultFontSizes = [
|
||||||
12, 14, 16, 18, 20, 24, 28, 32, 36, 42, 48, 60, 72, 100,
|
8, 12, 14, 16, 18, 20, 24, 28, 32, 36, 42, 48, 60, 72, 100,
|
||||||
];
|
];
|
||||||
export function FontSize(props) {
|
export function FontSize(props) {
|
||||||
var editor = props.editor;
|
var editor = props.editor;
|
||||||
var currentFontSize = defaultFontSizes.find(function (size) {
|
var currentFontSize = defaultFontSizes.find(function (size) {
|
||||||
return editor.isActive("textStyle", { fontSize: "".concat(size, "px") });
|
return editor.isActive("textStyle", { fontSize: "".concat(size, "px") });
|
||||||
}) || 16;
|
}) || 16;
|
||||||
return (_jsx(Dropdown, { selectedItem: "".concat(currentFontSize, "px"), items: defaultFontSizes.map(function (size) { return ({
|
return (_jsx(Dropdown, { selectedItem: "".concat(currentFontSize, "px"), items: [
|
||||||
key: "".concat(size, "px"),
|
{
|
||||||
type: "menuitem",
|
key: "font-sizes",
|
||||||
title: "".concat(size, "px"),
|
type: "menuitem",
|
||||||
isChecked: size === currentFontSize,
|
component: function () { return (_jsx(Box, __assign({ sx: { display: "grid", gridTemplateColumns: "repeat(5, 1fr)" } }, { children: defaultFontSizes.map(function (size) { return (_jsxs(Button, __assign({ variant: "menuitem" }, { children: [size, "px"] }))); }) }))); },
|
||||||
onClick: function () { return editor.chain().focus().setFontSize("".concat(size, "px")).run(); },
|
},
|
||||||
}); }), menuWidth: 100 }));
|
],
|
||||||
|
// items={defaultFontSizes.map((size) => ({
|
||||||
|
// key: `${size}px`,
|
||||||
|
// type: "menuitem",
|
||||||
|
// title: `${size}px`,
|
||||||
|
// isChecked: size === currentFontSize,
|
||||||
|
// onClick: () => editor.chain().focus().setFontSize(`${size}px`).run(),
|
||||||
|
// }))}
|
||||||
|
menuWidth: 100 }));
|
||||||
}
|
}
|
||||||
var fontFamilies = {
|
var fontFamilies = {
|
||||||
System: "Open Sans",
|
System: "Open Sans",
|
||||||
|
|||||||
8
packages/editor/dist/toolbar/tools/inline.js
vendored
8
packages/editor/dist/toolbar/tools/inline.js
vendored
@@ -33,8 +33,8 @@ import { Flex } from "rebass";
|
|||||||
import { Input } from "@rebass/forms";
|
import { Input } from "@rebass/forms";
|
||||||
import { Popup } from "../components/popup";
|
import { Popup } from "../components/popup";
|
||||||
function InlineTool(props) {
|
function InlineTool(props) {
|
||||||
var editor = props.editor, title = props.title, id = props.id, icon = props.icon, isToggled = props.isToggled, onClick = props.onClick;
|
var editor = props.editor, title = props.title, icon = props.icon, isToggled = props.isToggled, onClick = props.onClick;
|
||||||
return (_jsx(ToolButton, { title: title, id: id, icon: icon, onClick: function () { return onClick(editor); }, toggled: isToggled(editor) }));
|
return (_jsx(ToolButton, { title: title, id: icon, icon: icon, onClick: function () { return onClick(editor); }, toggled: isToggled(editor) }));
|
||||||
}
|
}
|
||||||
export function Italic(props) {
|
export function Italic(props) {
|
||||||
return (_jsx(InlineTool, __assign({}, props, { isToggled: function (editor) { return editor.isActive("italic"); }, onClick: function (editor) { return editor.chain().focus().toggleItalic().run(); } })));
|
return (_jsx(InlineTool, __assign({}, props, { isToggled: function (editor) { return editor.isActive("italic"); }, onClick: function (editor) { return editor.chain().focus().toggleItalic().run(); } })));
|
||||||
@@ -63,7 +63,7 @@ export function ClearFormatting(props) {
|
|||||||
} })));
|
} })));
|
||||||
}
|
}
|
||||||
export function Link(props) {
|
export function Link(props) {
|
||||||
var editor = props.editor, id = props.id, title = props.title, icon = props.icon;
|
var editor = props.editor, title = props.title, icon = props.icon;
|
||||||
var buttonRef = useRef(null);
|
var buttonRef = useRef(null);
|
||||||
var targetRef = useRef();
|
var targetRef = useRef();
|
||||||
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
var _a = __read(useState(false), 2), isOpen = _a[0], setIsOpen = _a[1];
|
||||||
@@ -71,7 +71,7 @@ export function Link(props) {
|
|||||||
var _c = __read(useState(), 2), text = _c[0], setText = _c[1];
|
var _c = __read(useState(), 2), text = _c[0], setText = _c[1];
|
||||||
var currentUrl = editor.getAttributes("link").href;
|
var currentUrl = editor.getAttributes("link").href;
|
||||||
var isEditing = !!currentUrl;
|
var isEditing = !!currentUrl;
|
||||||
return (_jsxs(_Fragment, { children: [_jsx(ToolButton, { ref: buttonRef, title: title, id: id, icon: icon, onClick: function () {
|
return (_jsxs(_Fragment, { children: [_jsx(ToolButton, { id: icon, ref: buttonRef, title: title, icon: icon, onClick: function () {
|
||||||
if (isEditing)
|
if (isEditing)
|
||||||
setHref(currentUrl);
|
setHref(currentUrl);
|
||||||
var _a = editor.state.selection, from = _a.from, to = _a.to, $from = _a.$from;
|
var _a = editor.state.selection, from = _a.from, to = _a.to, $from = _a.$from;
|
||||||
|
|||||||
2
packages/editor/dist/toolbar/types.d.ts
vendored
2
packages/editor/dist/toolbar/types.d.ts
vendored
@@ -1,9 +1,7 @@
|
|||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { IconNames } from "./icons";
|
import { IconNames } from "./icons";
|
||||||
import { ToolId } from "./tools";
|
|
||||||
export declare type ToolProps = ToolDefinition & {
|
export declare type ToolProps = ToolDefinition & {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
id: ToolId;
|
|
||||||
};
|
};
|
||||||
export declare type ToolDefinition = {
|
export declare type ToolDefinition = {
|
||||||
icon: IconNames;
|
icon: IconNames;
|
||||||
|
|||||||
1
packages/editor/dist/toolbar/utils/dom.d.ts
vendored
Normal file
1
packages/editor/dist/toolbar/utils/dom.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export declare function getToolbarElement(): HTMLElement;
|
||||||
3
packages/editor/dist/toolbar/utils/dom.js
vendored
Normal file
3
packages/editor/dist/toolbar/utils/dom.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function getToolbarElement() {
|
||||||
|
return (document.querySelector(".editor-toolbar") || undefined);
|
||||||
|
}
|
||||||
276
packages/editor/package-lock.json
generated
276
packages/editor/package-lock.json
generated
@@ -41,6 +41,7 @@
|
|||||||
"re-resizable": "^6.9.5",
|
"re-resizable": "^6.9.5",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-modal": "^3.14.4",
|
"react-modal": "^3.14.4",
|
||||||
|
"react-spring-bottom-sheet": "^3.4.0",
|
||||||
"react-toggle": "^4.1.2",
|
"react-toggle": "^4.1.2",
|
||||||
"reactjs-popup": "^2.0.5",
|
"reactjs-popup": "^2.0.5",
|
||||||
"rebass": "^4.0.7",
|
"rebass": "^4.0.7",
|
||||||
@@ -3081,6 +3082,11 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@juggle/resize-observer": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
|
||||||
|
},
|
||||||
"node_modules/@mdi/js": {
|
"node_modules/@mdi/js": {
|
||||||
"version": "6.6.96",
|
"version": "6.6.96",
|
||||||
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-6.6.96.tgz",
|
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-6.6.96.tgz",
|
||||||
@@ -3245,6 +3251,33 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reach/portal": {
|
||||||
|
"version": "0.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.13.2.tgz",
|
||||||
|
"integrity": "sha512-g74BnCdtuTGthzzHn2cWW+bcyIYb0iIE/yRsm89i8oNzNgpopbkh9UY8TPbhNlys52h7U60s4kpRTmcq+JqsTA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reach/utils": "0.13.2",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || 17.x",
|
||||||
|
"react-dom": "^16.8.0 || 17.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reach/utils": {
|
||||||
|
"version": "0.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz",
|
||||||
|
"integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/warning": "^3.0.0",
|
||||||
|
"tslib": "^2.1.0",
|
||||||
|
"warning": "^4.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || 17.x",
|
||||||
|
"react-dom": "^16.8.0 || 17.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rebass/forms": {
|
"node_modules/@rebass/forms": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@rebass/forms/-/forms-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@rebass/forms/-/forms-4.0.6.tgz",
|
||||||
@@ -4703,6 +4736,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/warning": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI="
|
||||||
|
},
|
||||||
"node_modules/@types/webpack": {
|
"node_modules/@types/webpack": {
|
||||||
"version": "4.41.32",
|
"version": "4.41.32",
|
||||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
|
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
|
||||||
@@ -5123,6 +5161,28 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xstate/react": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xstate/react/-/react-1.6.3.tgz",
|
||||||
|
"integrity": "sha512-NCUReRHPGvvCvj2yLZUTfR0qVp6+apc8G83oXSjN4rl89ZjyujiKrTff55bze/HrsvCsP/sUJASf2n0nzMF1KQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"use-isomorphic-layout-effect": "^1.0.0",
|
||||||
|
"use-subscription": "^1.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@xstate/fsm": "^1.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0",
|
||||||
|
"xstate": "^4.11.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@xstate/fsm": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"xstate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -6333,6 +6393,11 @@
|
|||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/body-scroll-lock": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg=="
|
||||||
|
},
|
||||||
"node_modules/bonjour": {
|
"node_modules/bonjour": {
|
||||||
"version": "3.5.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
||||||
@@ -10562,6 +10627,14 @@
|
|||||||
"readable-stream": "^2.3.6"
|
"readable-stream": "^2.3.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/focus-trap": {
|
||||||
|
"version": "6.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz",
|
||||||
|
"integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tabbable": "^5.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.14.9",
|
"version": "1.14.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||||
@@ -18891,6 +18964,37 @@
|
|||||||
"semver": "bin/semver"
|
"semver": "bin/semver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-spring": {
|
||||||
|
"version": "8.0.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
|
||||||
|
"integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"prop-types": "^15.5.8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16.8.0",
|
||||||
|
"react-dom": ">= 16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-spring-bottom-sheet": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-spring-bottom-sheet/-/react-spring-bottom-sheet-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-zKwTymxrTRMHPjfBiMw8reQlWoVqlCGMTefmMYkAlBvR7n3hBe5sntuQJAEjmrAnA+cLSGp44mtmgBtT2ksL5Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@juggle/resize-observer": "^3.2.0",
|
||||||
|
"@reach/portal": "^0.13.0",
|
||||||
|
"@xstate/react": "^1.2.0",
|
||||||
|
"body-scroll-lock": "^3.1.5",
|
||||||
|
"focus-trap": "^6.2.2",
|
||||||
|
"react-spring": "^8.0.27",
|
||||||
|
"react-use-gesture": "^8.0.1",
|
||||||
|
"xstate": "^4.15.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.14.0 || 17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-toggle": {
|
"node_modules/react-toggle": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz",
|
||||||
@@ -18904,6 +19008,15 @@
|
|||||||
"react-dom": ">= 15.3.0 < 18"
|
"react-dom": ">= 15.3.0 < 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-use-gesture": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-CXzUNkulUdgouaAlvAsC5ZVo0fi9KGSBSk81WrE4kOIcJccpANe9zZkAYr5YZZhqpicIFxitsrGVS4wmoMun9A==",
|
||||||
|
"deprecated": "This package is no longer maintained. Please use @use-gesture/react instead",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reactcss": {
|
"node_modules/reactcss": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
@@ -21553,6 +21666,11 @@
|
|||||||
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tabbable": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-6G/8EWRFx8CiSe2++/xHhXkmCRq2rHtDtZbQFHx34cvDfZzIBfvwG9zGUNTWMXWLCYvDj3aQqOzdl3oCxKuBkQ=="
|
||||||
|
},
|
||||||
"node_modules/table": {
|
"node_modules/table": {
|
||||||
"version": "6.8.0",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
|
||||||
@@ -22158,8 +22276,7 @@
|
|||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tsutils": {
|
"node_modules/tsutils": {
|
||||||
"version": "3.21.0",
|
"version": "3.21.0",
|
||||||
@@ -22675,6 +22792,38 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-isomorphic-layout-effect": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/use-subscription": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-87x6MjiIVE/BWqtxfiRvM6jfvGudN+UeVOnWi7qKYp2c0YJn5+Z5Jt0kZw6Tt+8hs7kw/BWo2WBhizJSAZsQJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util": {
|
"node_modules/util": {
|
||||||
"version": "0.11.1",
|
"version": "0.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||||
@@ -24723,6 +24872,15 @@
|
|||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/xstate": {
|
||||||
|
"version": "4.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xstate/-/xstate-4.32.0.tgz",
|
||||||
|
"integrity": "sha512-62gETqwnw4pBRe+tVWMt8hLgWEU8lq2qO8VN5PWmTELceRVt3I1bu1cwdraVRHUn4Bb2lnhNzn1A73oShuC+8g==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/xstate"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xtend": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
@@ -27036,6 +27194,11 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@juggle/resize-observer": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
|
||||||
|
},
|
||||||
"@mdi/js": {
|
"@mdi/js": {
|
||||||
"version": "6.6.96",
|
"version": "6.6.96",
|
||||||
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-6.6.96.tgz",
|
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-6.6.96.tgz",
|
||||||
@@ -27160,6 +27323,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
|
||||||
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw=="
|
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw=="
|
||||||
},
|
},
|
||||||
|
"@reach/portal": {
|
||||||
|
"version": "0.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.13.2.tgz",
|
||||||
|
"integrity": "sha512-g74BnCdtuTGthzzHn2cWW+bcyIYb0iIE/yRsm89i8oNzNgpopbkh9UY8TPbhNlys52h7U60s4kpRTmcq+JqsTA==",
|
||||||
|
"requires": {
|
||||||
|
"@reach/utils": "0.13.2",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@reach/utils": {
|
||||||
|
"version": "0.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz",
|
||||||
|
"integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/warning": "^3.0.0",
|
||||||
|
"tslib": "^2.1.0",
|
||||||
|
"warning": "^4.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rebass/forms": {
|
"@rebass/forms": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@rebass/forms/-/forms-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@rebass/forms/-/forms-4.0.6.tgz",
|
||||||
@@ -28246,6 +28428,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/warning": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI="
|
||||||
|
},
|
||||||
"@types/webpack": {
|
"@types/webpack": {
|
||||||
"version": "4.41.32",
|
"version": "4.41.32",
|
||||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
|
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
|
||||||
@@ -28582,6 +28769,15 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@xstate/react": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xstate/react/-/react-1.6.3.tgz",
|
||||||
|
"integrity": "sha512-NCUReRHPGvvCvj2yLZUTfR0qVp6+apc8G83oXSjN4rl89ZjyujiKrTff55bze/HrsvCsP/sUJASf2n0nzMF1KQ==",
|
||||||
|
"requires": {
|
||||||
|
"use-isomorphic-layout-effect": "^1.0.0",
|
||||||
|
"use-subscription": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -29556,6 +29752,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"body-scroll-lock": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg=="
|
||||||
|
},
|
||||||
"bonjour": {
|
"bonjour": {
|
||||||
"version": "3.5.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
||||||
@@ -32882,6 +33083,14 @@
|
|||||||
"readable-stream": "^2.3.6"
|
"readable-stream": "^2.3.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"focus-trap": {
|
||||||
|
"version": "6.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz",
|
||||||
|
"integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==",
|
||||||
|
"requires": {
|
||||||
|
"tabbable": "^5.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.14.9",
|
"version": "1.14.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||||
@@ -39463,6 +39672,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-spring": {
|
||||||
|
"version": "8.0.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
|
||||||
|
"integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"prop-types": "^15.5.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-spring-bottom-sheet": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-spring-bottom-sheet/-/react-spring-bottom-sheet-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-zKwTymxrTRMHPjfBiMw8reQlWoVqlCGMTefmMYkAlBvR7n3hBe5sntuQJAEjmrAnA+cLSGp44mtmgBtT2ksL5Q==",
|
||||||
|
"requires": {
|
||||||
|
"@juggle/resize-observer": "^3.2.0",
|
||||||
|
"@reach/portal": "^0.13.0",
|
||||||
|
"@xstate/react": "^1.2.0",
|
||||||
|
"body-scroll-lock": "^3.1.5",
|
||||||
|
"focus-trap": "^6.2.2",
|
||||||
|
"react-spring": "^8.0.27",
|
||||||
|
"react-use-gesture": "^8.0.1",
|
||||||
|
"xstate": "^4.15.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-toggle": {
|
"react-toggle": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz",
|
||||||
@@ -39471,6 +39704,12 @@
|
|||||||
"classnames": "^2.2.5"
|
"classnames": "^2.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-use-gesture": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-CXzUNkulUdgouaAlvAsC5ZVo0fi9KGSBSk81WrE4kOIcJccpANe9zZkAYr5YZZhqpicIFxitsrGVS4wmoMun9A==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"reactcss": {
|
"reactcss": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
@@ -41631,6 +41870,11 @@
|
|||||||
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tabbable": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-6G/8EWRFx8CiSe2++/xHhXkmCRq2rHtDtZbQFHx34cvDfZzIBfvwG9zGUNTWMXWLCYvDj3aQqOzdl3oCxKuBkQ=="
|
||||||
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"version": "6.8.0",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
|
||||||
@@ -42093,8 +42337,7 @@
|
|||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"tsutils": {
|
"tsutils": {
|
||||||
"version": "3.21.0",
|
"version": "3.21.0",
|
||||||
@@ -42476,6 +42719,26 @@
|
|||||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"use-isomorphic-layout-effect": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"use-subscription": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-87x6MjiIVE/BWqtxfiRvM6jfvGudN+UeVOnWi7qKYp2c0YJn5+Z5Jt0kZw6Tt+8hs7kw/BWo2WBhizJSAZsQJA==",
|
||||||
|
"requires": {
|
||||||
|
"use-sync-external-store": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"util": {
|
"util": {
|
||||||
"version": "0.11.1",
|
"version": "0.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||||
@@ -44176,6 +44439,11 @@
|
|||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"xstate": {
|
||||||
|
"version": "4.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xstate/-/xstate-4.32.0.tgz",
|
||||||
|
"integrity": "sha512-62gETqwnw4pBRe+tVWMt8hLgWEU8lq2qO8VN5PWmTELceRVt3I1bu1cwdraVRHUn4Bb2lnhNzn1A73oShuC+8g=="
|
||||||
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"re-resizable": "^6.9.5",
|
"re-resizable": "^6.9.5",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-modal": "^3.14.4",
|
"react-modal": "^3.14.4",
|
||||||
|
"react-spring-bottom-sheet": "^3.4.0",
|
||||||
"react-toggle": "^4.1.2",
|
"react-toggle": "^4.1.2",
|
||||||
"reactjs-popup": "^2.0.5",
|
"reactjs-popup": "^2.0.5",
|
||||||
"rebass": "^4.0.7",
|
"rebass": "^4.0.7",
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import { MenuItem as MenuItemType /*ResolvedMenuItem*/ } from "./types";
|
|||||||
// import { useMenuTrigger, useMenu, getPosition } from "../../hooks/useMenu";
|
// import { useMenuTrigger, useMenu, getPosition } from "../../hooks/useMenu";
|
||||||
import Modal from "react-modal";
|
import Modal from "react-modal";
|
||||||
import { ThemeProvider } from "emotion-theming";
|
import { ThemeProvider } from "emotion-theming";
|
||||||
|
import { BottomSheet } from "react-spring-bottom-sheet";
|
||||||
|
import "react-spring-bottom-sheet/dist/style.css";
|
||||||
|
import { useIsMobile } from "../../toolbar/stores/toolbar-store";
|
||||||
// import { store as selectionStore } from "../../stores/selectionstore";
|
// import { store as selectionStore } from "../../stores/selectionstore";
|
||||||
|
|
||||||
function useMenuFocus(
|
function useMenuFocus(
|
||||||
@@ -258,20 +261,35 @@ function MenuContainer(props: PropsWithChildren<MenuContainerProps>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PopupType = "sheet" | "menu" | "none";
|
||||||
|
export type PopupPresenterProps = MenuPresenterProps &
|
||||||
|
ActionSheetPresenterProps & {
|
||||||
|
mobile?: PopupType;
|
||||||
|
desktop?: PopupType;
|
||||||
|
};
|
||||||
|
export function PopupPresenter(props: PropsWithChildren<PopupPresenterProps>) {
|
||||||
|
const { mobile = "menu", desktop = "menu", ...restProps } = props;
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
if (isMobile && mobile === "sheet")
|
||||||
|
return <ActionSheetPresenter {...restProps} />;
|
||||||
|
else if (mobile === "menu" || desktop === "menu")
|
||||||
|
return <MenuPresenter {...restProps} />;
|
||||||
|
else return props.isOpen ? <>{props.children}</> : null;
|
||||||
|
}
|
||||||
|
|
||||||
export type MenuPresenterProps = MenuContainerProps & {
|
export type MenuPresenterProps = MenuContainerProps & {
|
||||||
items: MenuItemType[];
|
items?: MenuItemType[];
|
||||||
options: MenuOptions;
|
onClose?: () => void;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
options?: MenuOptions;
|
||||||
className?: string;
|
|
||||||
};
|
};
|
||||||
export function MenuPresenter(props: PropsWithChildren<MenuPresenterProps>) {
|
export function MenuPresenter(props: PropsWithChildren<MenuPresenterProps>) {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
options,
|
options = { type: "menu", position: {} },
|
||||||
items,
|
items = [],
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose = () => {},
|
||||||
children,
|
children,
|
||||||
...containerProps
|
...containerProps
|
||||||
} = props;
|
} = props;
|
||||||
@@ -384,3 +402,43 @@ export function MenuPresenter(props: PropsWithChildren<MenuPresenterProps>) {
|
|||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ActionSheetPresenterProps = MenuContainerProps & {
|
||||||
|
items?: MenuItemType[];
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose?: () => void;
|
||||||
|
blocking?: boolean;
|
||||||
|
};
|
||||||
|
export function ActionSheetPresenter(
|
||||||
|
props: PropsWithChildren<ActionSheetPresenterProps>
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
items = [],
|
||||||
|
isOpen,
|
||||||
|
onClose = () => {},
|
||||||
|
children,
|
||||||
|
sx,
|
||||||
|
blocking = true,
|
||||||
|
...containerProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BottomSheet open={isOpen} onDismiss={onClose} blocking={blocking}>
|
||||||
|
{props.children ? (
|
||||||
|
props.children
|
||||||
|
) : (
|
||||||
|
<Menu
|
||||||
|
items={items}
|
||||||
|
closeMenu={onClose}
|
||||||
|
sx={{
|
||||||
|
flex: 1,
|
||||||
|
boxShadow: "none",
|
||||||
|
border: "none",
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
{...containerProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</BottomSheet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
21
packages/editor/src/components/responsive/index.tsx
Normal file
21
packages/editor/src/components/responsive/index.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
import { useToolbarStore } from "../../toolbar/stores/toolbar-store";
|
||||||
|
|
||||||
|
type ResponsiveContainerProps = {
|
||||||
|
mobile?: JSX.Element;
|
||||||
|
desktop?: JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ResponsiveContainer(props: ResponsiveContainerProps) {
|
||||||
|
const isMobile = useToolbarStore((store) => store.isMobile);
|
||||||
|
if (isMobile) return props.mobile || null;
|
||||||
|
else return props.desktop || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DesktopOnly(props: PropsWithChildren<{}>) {
|
||||||
|
return <ResponsiveContainer desktop={<>{props.children}</>} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MobileOnly(props: PropsWithChildren<{}>) {
|
||||||
|
return <ResponsiveContainer mobile={<>{props.children}</>} />;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Flex, Image, ImageProps, Text } from "rebass";
|
import { Box, Button, Flex, Image, ImageProps, Text } from "rebass";
|
||||||
import { NodeViewWrapper, NodeViewProps, FloatingMenu } from "@tiptap/react";
|
import { NodeViewWrapper, NodeViewProps, FloatingMenu } from "@tiptap/react";
|
||||||
import { ThemeConfig } from "@notesnook/theme/dist/theme/types";
|
import { ThemeConfig } from "@notesnook/theme/dist/theme/types";
|
||||||
import { ThemeProvider } from "emotion-theming";
|
import { ThemeProvider } from "emotion-theming";
|
||||||
@@ -8,7 +8,11 @@ import { ToolButton } from "../../toolbar/components/tool-button";
|
|||||||
import { findToolById, ToolId } from "../../toolbar/tools";
|
import { findToolById, ToolId } from "../../toolbar/tools";
|
||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import {
|
||||||
|
ActionSheetPresenter,
|
||||||
|
MenuPresenter,
|
||||||
|
PopupPresenter,
|
||||||
|
} from "../../components/menu/menu";
|
||||||
import { Popup } from "../../toolbar/components/popup";
|
import { Popup } from "../../toolbar/components/popup";
|
||||||
import { Toggle } from "../../components/toggle";
|
import { Toggle } from "../../components/toggle";
|
||||||
import { Input } from "@rebass/forms";
|
import { Input } from "@rebass/forms";
|
||||||
@@ -18,6 +22,9 @@ import {
|
|||||||
EmbedSizeOptions,
|
EmbedSizeOptions,
|
||||||
} from "./embed";
|
} from "./embed";
|
||||||
import { EmbedPopup } from "../../toolbar/popups/embed-popup";
|
import { EmbedPopup } from "../../toolbar/popups/embed-popup";
|
||||||
|
import { Icon } from "../../toolbar/components/icon";
|
||||||
|
import { Icons } from "../../toolbar/icons";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../components/responsive";
|
||||||
|
|
||||||
export function EmbedComponent(props: NodeViewProps) {
|
export function EmbedComponent(props: NodeViewProps) {
|
||||||
const { src, width, height, align } = props.node.attrs as EmbedAttributes &
|
const { src, width, height, align } = props.node.attrs as EmbedAttributes &
|
||||||
@@ -61,9 +68,27 @@ export function EmbedComponent(props: NodeViewProps) {
|
|||||||
}}
|
}}
|
||||||
lockAspectRatio={true}
|
lockAspectRatio={true}
|
||||||
>
|
>
|
||||||
<Flex sx={{ position: "relative", justifyContent: "end" }}>
|
{/* <Flex sx={{ position: "relative", justifyContent: "end" }}>
|
||||||
|
|
||||||
|
</Flex> */}
|
||||||
|
<Flex
|
||||||
|
width={"100%"}
|
||||||
|
sx={{
|
||||||
|
position: "relative",
|
||||||
|
justifyContent: "end",
|
||||||
|
borderTop: "20px solid var(--bgSecondary)",
|
||||||
|
// borderLeft: "20px solid var(--bgSecondary)",
|
||||||
|
borderTopLeftRadius: "default",
|
||||||
|
borderTopRightRadius: "default",
|
||||||
|
borderColor: isActive ? "border" : "bgSecondary",
|
||||||
|
cursor: "pointer",
|
||||||
|
":hover": {
|
||||||
|
borderColor: "border",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
{isToolbarVisible && (
|
{isToolbarVisible && (
|
||||||
<ImageToolbar
|
<EmbedToolbar
|
||||||
editor={editor}
|
editor={editor}
|
||||||
align={align}
|
align={align}
|
||||||
height={height || 0}
|
height={height || 0}
|
||||||
@@ -79,10 +104,11 @@ export function EmbedComponent(props: NodeViewProps) {
|
|||||||
width={"100%"}
|
width={"100%"}
|
||||||
height={"100%"}
|
height={"100%"}
|
||||||
sx={{
|
sx={{
|
||||||
border: isActive
|
border: "none",
|
||||||
? "2px solid var(--primary)"
|
// border: isActive
|
||||||
: "2px solid transparent",
|
// ? "2px solid var(--primary)"
|
||||||
borderRadius: "default",
|
// : "2px solid transparent",
|
||||||
|
// borderRadius: "default",
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@@ -98,7 +124,7 @@ type ImageToolbarProps = Required<EmbedAttributes> &
|
|||||||
editor: Editor;
|
editor: Editor;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ImageToolbar(props: ImageToolbarProps) {
|
function EmbedToolbar(props: ImageToolbarProps) {
|
||||||
const { editor, height, width, src } = props;
|
const { editor, height, width, src } = props;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
@@ -184,7 +210,13 @@ function ImageToolbar(props: ImageToolbarProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{isOpen && (
|
<PopupPresenter
|
||||||
|
isOpen={isOpen}
|
||||||
|
desktop="none"
|
||||||
|
mobile="sheet"
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
blocking={true}
|
||||||
|
>
|
||||||
<EmbedPopup
|
<EmbedPopup
|
||||||
title="Embed properties"
|
title="Embed properties"
|
||||||
icon="close"
|
icon="close"
|
||||||
@@ -193,7 +225,7 @@ function ImageToolbar(props: ImageToolbarProps) {
|
|||||||
onSourceChanged={(src) => {}}
|
onSourceChanged={(src) => {}}
|
||||||
onSizeChanged={(size) => editor.commands.setEmbedSize(size)}
|
onSizeChanged={(size) => editor.commands.setEmbedSize(size)}
|
||||||
/>
|
/>
|
||||||
)}
|
</PopupPresenter>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,16 @@ import { ToolButton } from "../../toolbar/components/tool-button";
|
|||||||
import { findToolById, ToolId } from "../../toolbar/tools";
|
import { findToolById, ToolId } from "../../toolbar/tools";
|
||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import {
|
||||||
|
ActionSheetPresenter,
|
||||||
|
MenuPresenter,
|
||||||
|
PopupPresenter,
|
||||||
|
} from "../../components/menu/menu";
|
||||||
import { Popup } from "../../toolbar/components/popup";
|
import { Popup } from "../../toolbar/components/popup";
|
||||||
import { Toggle } from "../../components/toggle";
|
import { Toggle } from "../../components/toggle";
|
||||||
import { Input } from "@rebass/forms";
|
import { Input } from "@rebass/forms";
|
||||||
|
import { ImageProperties } from "../../toolbar/popups/image-properties";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../components/responsive";
|
||||||
|
|
||||||
export function ImageComponent(props: ImageProps & NodeViewProps) {
|
export function ImageComponent(props: ImageProps & NodeViewProps) {
|
||||||
const { src, alt, title, width, height, align, float } = props.node
|
const { src, alt, title, width, height, align, float } = props.node
|
||||||
@@ -106,28 +112,6 @@ function ImageToolbar(props: ImageToolbarProps) {
|
|||||||
const { editor, float, height, width } = props;
|
const { editor, float, height, width } = props;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const onSizeChange = useCallback(
|
|
||||||
(newWidth?: number, newHeight?: number) => {
|
|
||||||
const size: ImageSizeOptions = newWidth
|
|
||||||
? {
|
|
||||||
width: newWidth,
|
|
||||||
height: newWidth * (height / width),
|
|
||||||
}
|
|
||||||
: newHeight
|
|
||||||
? {
|
|
||||||
width: newHeight * (width / height),
|
|
||||||
height: newHeight,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
editor.chain().setImageSize(size).run();
|
|
||||||
},
|
|
||||||
[width, height]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
@@ -210,7 +194,13 @@ function ImageToolbar(props: ImageToolbarProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{isOpen && (
|
<PopupPresenter
|
||||||
|
mobile="sheet"
|
||||||
|
desktop="none"
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
blocking={false}
|
||||||
|
>
|
||||||
<Popup
|
<Popup
|
||||||
title="Image properties"
|
title="Image properties"
|
||||||
action={{
|
action={{
|
||||||
@@ -220,46 +210,9 @@ function ImageToolbar(props: ImageToolbarProps) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex sx={{ width: 200, flexDirection: "column", p: 1 }}>
|
<ImageProperties {...props} />
|
||||||
<Flex
|
|
||||||
sx={{ justifyContent: "space-between", alignItems: "center" }}
|
|
||||||
>
|
|
||||||
<Text variant={"body"}>Floating?</Text>
|
|
||||||
<Toggle
|
|
||||||
checked={float}
|
|
||||||
onClick={() =>
|
|
||||||
editor
|
|
||||||
.chain()
|
|
||||||
.setImageAlignment({ float: !float, align: "left" })
|
|
||||||
.run()
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex sx={{ alignItems: "center", mt: 2 }}>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Width"
|
|
||||||
value={width}
|
|
||||||
sx={{
|
|
||||||
mr: 2,
|
|
||||||
p: 1,
|
|
||||||
fontSize: "body",
|
|
||||||
}}
|
|
||||||
onChange={(e) => onSizeChange(e.target.valueAsNumber)}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Height"
|
|
||||||
value={height}
|
|
||||||
sx={{ p: 1, fontSize: "body" }}
|
|
||||||
onChange={(e) =>
|
|
||||||
onSizeChange(undefined, e.target.valueAsNumber)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Popup>
|
</Popup>
|
||||||
)}
|
</PopupPresenter>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Icons } from "../icons";
|
|||||||
import { MenuPresenter, MenuPresenterProps } from "../../components/menu/menu";
|
import { MenuPresenter, MenuPresenterProps } from "../../components/menu/menu";
|
||||||
import { MenuItem } from "../../components/menu/types";
|
import { MenuItem } from "../../components/menu/types";
|
||||||
import { useToolbarContext } from "../hooks/useToolbarContext";
|
import { useToolbarContext } from "../hooks/useToolbarContext";
|
||||||
|
import { useToolbarLocation } from "../stores/toolbar-store";
|
||||||
|
|
||||||
type DropdownProps = {
|
type DropdownProps = {
|
||||||
selectedItem: string | JSX.Element;
|
selectedItem: string | JSX.Element;
|
||||||
@@ -16,7 +17,7 @@ export function Dropdown(props: DropdownProps) {
|
|||||||
const { items, selectedItem, buttonRef, menuWidth } = props;
|
const { items, selectedItem, buttonRef, menuWidth } = props;
|
||||||
const internalRef = useRef<any>();
|
const internalRef = useRef<any>();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { toolbarLocation } = useToolbarContext();
|
const toolbarLocation = useToolbarLocation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ export function Popup(props: PropsWithChildren<PopupProps>) {
|
|||||||
sx={{
|
sx={{
|
||||||
bg: "background",
|
bg: "background",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
borderRadius: "default",
|
// borderRadius: "default",
|
||||||
border: "1px solid var(--border)",
|
// border: "1px solid var(--border)",
|
||||||
boxShadow: "menu",
|
// boxShadow: "menu",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{title && (
|
{title && (
|
||||||
@@ -52,7 +52,7 @@ type PopupButtonProps = ButtonProps & {
|
|||||||
function PopupButton(props: PopupButtonProps) {
|
function PopupButton(props: PopupButtonProps) {
|
||||||
const { text, loading, icon, iconColor, iconSize, ...restProps } = props;
|
const { text, loading, icon, iconColor, iconSize, ...restProps } = props;
|
||||||
return (
|
return (
|
||||||
<Button variant="dialog" sx={{ p: 1, px: 2 }} {...restProps}>
|
<Button variant="icon" sx={{ p: 1, px: 2 }} {...restProps}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Icon path={Icons.loading} size={16} rotate color="primary" />
|
<Icon path={Icons.loading} size={16} rotate color="primary" />
|
||||||
) : icon ? (
|
) : icon ? (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Icon } from "./icon";
|
|||||||
import { ToolButton, ToolButtonProps } from "./tool-button";
|
import { ToolButton, ToolButtonProps } from "./tool-button";
|
||||||
import { MenuPresenter, MenuPresenterProps } from "../../components/menu/menu";
|
import { MenuPresenter, MenuPresenterProps } from "../../components/menu/menu";
|
||||||
import { useToolbarContext } from "../hooks/useToolbarContext";
|
import { useToolbarContext } from "../hooks/useToolbarContext";
|
||||||
|
import { useToolbarLocation } from "../stores/toolbar-store";
|
||||||
|
|
||||||
type SplitButtonProps = ToolButtonProps & {
|
type SplitButtonProps = ToolButtonProps & {
|
||||||
menuPresenterProps?: Partial<MenuPresenterProps>;
|
menuPresenterProps?: Partial<MenuPresenterProps>;
|
||||||
@@ -15,7 +16,7 @@ export function SplitButton(props: PropsWithChildren<SplitButtonProps>) {
|
|||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const { toolbarLocation } = useToolbarContext();
|
const toolbarLocation = useToolbarLocation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type ToolButtonProps = ButtonProps & {
|
|||||||
iconSize?: number;
|
iconSize?: number;
|
||||||
toggled: boolean;
|
toggled: boolean;
|
||||||
buttonRef?: React.MutableRefObject<HTMLButtonElement | null | undefined>;
|
buttonRef?: React.MutableRefObject<HTMLButtonElement | null | undefined>;
|
||||||
|
variant?: "small" | "normal";
|
||||||
};
|
};
|
||||||
export function ToolButton(props: ToolButtonProps) {
|
export function ToolButton(props: ToolButtonProps) {
|
||||||
const {
|
const {
|
||||||
@@ -20,6 +21,7 @@ export function ToolButton(props: ToolButtonProps) {
|
|||||||
toggled,
|
toggled,
|
||||||
sx,
|
sx,
|
||||||
buttonRef,
|
buttonRef,
|
||||||
|
variant = "normal",
|
||||||
...buttonProps
|
...buttonProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -29,10 +31,11 @@ export function ToolButton(props: ToolButtonProps) {
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
id={`tool-${id}`}
|
id={`tool-${id}`}
|
||||||
sx={{
|
sx={{
|
||||||
p: 1,
|
p: variant === "small" ? "3px" : 1,
|
||||||
|
borderRadius: variant === "small" ? "small" : "default",
|
||||||
m: 0,
|
m: 0,
|
||||||
bg: toggled ? "hover" : "transparent",
|
bg: toggled ? "hover" : "transparent",
|
||||||
mr: 1,
|
mr: variant === "small" ? 0 : 1,
|
||||||
":hover": { bg: "hover" },
|
":hover": { bg: "hover" },
|
||||||
":last-of-type": {
|
":last-of-type": {
|
||||||
mr: 0,
|
mr: 0,
|
||||||
@@ -44,7 +47,7 @@ export function ToolButton(props: ToolButtonProps) {
|
|||||||
<Icon
|
<Icon
|
||||||
path={Icons[icon]}
|
path={Icons[icon]}
|
||||||
color={iconColor || "text"}
|
color={iconColor || "text"}
|
||||||
size={iconSize || 18}
|
size={iconSize || variant === "small" ? 16 : 18}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import { TableRowFloatingMenu, TableColumnFloatingMenu } from "./table";
|
import {
|
||||||
|
TableRowFloatingMenu,
|
||||||
|
TableColumnFloatingMenu,
|
||||||
|
TableFloatingMenu,
|
||||||
|
} from "./table/table";
|
||||||
import { SearchReplaceFloatingMenu } from "./search-replace";
|
import { SearchReplaceFloatingMenu } from "./search-replace";
|
||||||
import { FloatingMenuProps } from "./types";
|
import { FloatingMenuProps } from "./types";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../components/responsive";
|
||||||
|
|
||||||
export function EditorFloatingMenus(props: FloatingMenuProps) {
|
export function EditorFloatingMenus(props: FloatingMenuProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableRowFloatingMenu {...props} />
|
<DesktopOnly>
|
||||||
<TableColumnFloatingMenu {...props} />
|
<TableRowFloatingMenu {...props} />
|
||||||
|
<TableColumnFloatingMenu {...props} />
|
||||||
|
</DesktopOnly>
|
||||||
|
<MobileOnly>
|
||||||
|
<TableFloatingMenu {...props} />
|
||||||
|
</MobileOnly>
|
||||||
<SearchReplaceFloatingMenu {...props} />
|
<SearchReplaceFloatingMenu {...props} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,187 +1,38 @@
|
|||||||
import { Input } from "@rebass/forms";
|
import {
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
ActionSheetPresenter,
|
||||||
import { Flex } from "rebass";
|
MenuPresenter,
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
PopupPresenter,
|
||||||
|
} from "../../components/menu/menu";
|
||||||
import { SearchStorage } from "../../extensions/search-replace";
|
import { SearchStorage } from "../../extensions/search-replace";
|
||||||
import { Popup } from "../components/popup";
|
|
||||||
import { ToolButton } from "../components/tool-button";
|
|
||||||
import { FloatingMenuProps } from "./types";
|
import { FloatingMenuProps } from "./types";
|
||||||
|
import { SearchReplacePopup } from "../popups/search-replace";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../components/responsive";
|
||||||
|
|
||||||
export function SearchReplaceFloatingMenu(props: FloatingMenuProps) {
|
export function SearchReplaceFloatingMenu(props: FloatingMenuProps) {
|
||||||
const { editor } = props;
|
const { editor } = props;
|
||||||
const { isSearching, selectedText } = editor.storage
|
const { isSearching } = editor.storage.searchreplace as SearchStorage;
|
||||||
.searchreplace as SearchStorage;
|
|
||||||
|
|
||||||
const [matchCase, setMatchCase] = useState(false);
|
|
||||||
const [matchWholeWord, setMatchWholeWord] = useState(false);
|
|
||||||
const [enableRegex, setEnableRegex] = useState(false);
|
|
||||||
const replaceText = useRef("");
|
|
||||||
const searchInputRef = useRef<HTMLInputElement>();
|
|
||||||
|
|
||||||
const search = useCallback(
|
|
||||||
(term: string) => {
|
|
||||||
editor.commands.search(term, {
|
|
||||||
matchCase,
|
|
||||||
enableRegex,
|
|
||||||
matchWholeWord,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[matchCase, enableRegex, matchWholeWord]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!searchInputRef.current) return;
|
|
||||||
search(searchInputRef.current.value);
|
|
||||||
}, [search, matchCase, matchWholeWord, enableRegex]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isSearching && selectedText) {
|
|
||||||
if (searchInputRef.current) {
|
|
||||||
const input = searchInputRef.current;
|
|
||||||
setTimeout(() => {
|
|
||||||
input.value = selectedText;
|
|
||||||
input.focus();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
search(selectedText);
|
|
||||||
}
|
|
||||||
}, [isSearching, selectedText, search]);
|
|
||||||
|
|
||||||
if (!isSearching) return null;
|
if (!isSearching) return null;
|
||||||
return (
|
return (
|
||||||
<MenuPresenter
|
<>
|
||||||
isOpen
|
<PopupPresenter
|
||||||
items={[]}
|
mobile="sheet"
|
||||||
onClose={() => {}}
|
desktop="menu"
|
||||||
options={{
|
isOpen
|
||||||
type: "autocomplete",
|
onClose={() => editor.commands.endSearch()}
|
||||||
position: {
|
options={{
|
||||||
target:
|
type: "autocomplete",
|
||||||
document.querySelector<HTMLElement>(".editor-toolbar") || "mouse",
|
position: {
|
||||||
isTargetAbsolute: true,
|
target:
|
||||||
location: "below",
|
document.querySelector<HTMLElement>(".editor-toolbar") || "mouse",
|
||||||
align: "end",
|
isTargetAbsolute: true,
|
||||||
},
|
location: "below",
|
||||||
}}
|
align: "end",
|
||||||
>
|
},
|
||||||
<Popup>
|
}}
|
||||||
<Flex sx={{ p: 1, flexDirection: "column" }}>
|
>
|
||||||
<Flex sx={{ alignItems: "start", flexShrink: 0 }}>
|
<SearchReplacePopup editor={editor} />
|
||||||
<Flex
|
</PopupPresenter>
|
||||||
sx={{
|
</>
|
||||||
position: "relative",
|
|
||||||
mr: 1,
|
|
||||||
width: 200,
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
defaultValue={selectedText}
|
|
||||||
ref={searchInputRef}
|
|
||||||
autoFocus
|
|
||||||
sx={{ p: 1 }}
|
|
||||||
placeholder="Find"
|
|
||||||
onChange={(e) => {
|
|
||||||
search(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
mr: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToolButton
|
|
||||||
sx={{
|
|
||||||
mr: 0,
|
|
||||||
}}
|
|
||||||
toggled={matchCase}
|
|
||||||
title="Match case"
|
|
||||||
id="matchCase"
|
|
||||||
icon="caseSensitive"
|
|
||||||
onClick={() => setMatchCase((s) => !s)}
|
|
||||||
iconSize={14}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
sx={{
|
|
||||||
mr: 0,
|
|
||||||
}}
|
|
||||||
toggled={matchWholeWord}
|
|
||||||
title="Match whole word"
|
|
||||||
id="matchWholeWord"
|
|
||||||
icon="wholeWord"
|
|
||||||
onClick={() => setMatchWholeWord((s) => !s)}
|
|
||||||
iconSize={14}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
sx={{
|
|
||||||
mr: 0,
|
|
||||||
}}
|
|
||||||
toggled={enableRegex}
|
|
||||||
title="Enable regex"
|
|
||||||
id="enableRegex"
|
|
||||||
icon="regex"
|
|
||||||
onClick={() => setEnableRegex((s) => !s)}
|
|
||||||
iconSize={14}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Previous match"
|
|
||||||
id="previousMatch"
|
|
||||||
icon="previousMatch"
|
|
||||||
onClick={() => editor.commands.moveToPreviousResult()}
|
|
||||||
sx={{ mr: 0 }}
|
|
||||||
iconSize={16}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Next match"
|
|
||||||
id="nextMatch"
|
|
||||||
icon="nextMatch"
|
|
||||||
onClick={() => editor.commands.moveToNextResult()}
|
|
||||||
sx={{ mr: 0 }}
|
|
||||||
iconSize={16}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Close"
|
|
||||||
id="close"
|
|
||||||
icon="close"
|
|
||||||
onClick={() => editor.chain().focus().endSearch().run()}
|
|
||||||
iconSize={16}
|
|
||||||
sx={{ mr: 0 }}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex sx={{ alignItems: "start", flexShrink: 0, mt: 1 }}>
|
|
||||||
<Input
|
|
||||||
sx={{ p: 1, width: 200, mr: 1 }}
|
|
||||||
placeholder="Replace"
|
|
||||||
onChange={(e) => (replaceText.current = e.target.value)}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Replace"
|
|
||||||
id="replace"
|
|
||||||
icon="replaceOne"
|
|
||||||
onClick={() => editor.commands.replace(replaceText.current)}
|
|
||||||
sx={{ mr: 0 }}
|
|
||||||
iconSize={16}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Replace all"
|
|
||||||
id="replaceAll"
|
|
||||||
icon="replaceAll"
|
|
||||||
onClick={() => editor.commands.replaceAll(replaceText.current)}
|
|
||||||
sx={{ mr: 0 }}
|
|
||||||
iconSize={16}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Popup>
|
|
||||||
</MenuPresenter>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,665 +0,0 @@
|
|||||||
import { Theme } from "@notesnook/theme";
|
|
||||||
import { Slider } from "@rebass/forms";
|
|
||||||
import { Editor, findParentNodeClosestToPos } from "@tiptap/core";
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
||||||
import { Flex, Text } from "rebass";
|
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
|
||||||
import { getElementPosition, MenuOptions } from "../../components/menu/useMenu";
|
|
||||||
import { Popup } from "../components/popup";
|
|
||||||
import { ToolButton } from "../components/tool-button";
|
|
||||||
import { IconNames } from "../icons";
|
|
||||||
// import { ColorPicker, DEFAULT_COLORS } from "../tools/colors";
|
|
||||||
import { FloatingMenuProps } from "./types";
|
|
||||||
import { selectedRect, TableMap, TableRect } from "prosemirror-tables";
|
|
||||||
import { Transaction } from "prosemirror-state";
|
|
||||||
import { MenuItem } from "../../components/menu/types";
|
|
||||||
|
|
||||||
export function TableRowFloatingMenu(props: FloatingMenuProps) {
|
|
||||||
const { editor } = props;
|
|
||||||
const theme = editor.storage.theme as Theme;
|
|
||||||
const [position, setPosition] = useState<MenuOptions["position"] | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!editor.isActive("tableCell") &&
|
|
||||||
!editor.isActive("tableRow") &&
|
|
||||||
!editor.isActive("tableHeader")
|
|
||||||
) {
|
|
||||||
setPosition(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { $from } = editor.state.selection;
|
|
||||||
|
|
||||||
const selectedNode = $from.node();
|
|
||||||
const pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
|
||||||
|
|
||||||
const currentRow = (editor.view.nodeDOM(pos) as HTMLElement)?.closest("tr");
|
|
||||||
if (!currentRow) return;
|
|
||||||
setPosition((old) => {
|
|
||||||
if (old?.target === currentRow) return old;
|
|
||||||
|
|
||||||
return {
|
|
||||||
isTargetAbsolute: true,
|
|
||||||
location: "left",
|
|
||||||
xOffset: -5,
|
|
||||||
target: currentRow,
|
|
||||||
// parent: editor.view.dom as HTMLElement,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [editor.state.selection]);
|
|
||||||
|
|
||||||
if (!position) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuPresenter
|
|
||||||
isOpen
|
|
||||||
items={[]}
|
|
||||||
onClose={() => {}}
|
|
||||||
options={{
|
|
||||||
type: "autocomplete",
|
|
||||||
position,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
bg: "background",
|
|
||||||
flexWrap: "nowrap",
|
|
||||||
borderRadius: "default",
|
|
||||||
opacity: isMenuOpen ? 1 : 0.3,
|
|
||||||
":hover": {
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToolButton
|
|
||||||
toggled={isMenuOpen}
|
|
||||||
title="Row properties"
|
|
||||||
id="properties"
|
|
||||||
icon="more"
|
|
||||||
onClick={() => setIsMenuOpen(true)}
|
|
||||||
iconSize={16}
|
|
||||||
sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Insert row below"
|
|
||||||
id="insertRowBelow"
|
|
||||||
icon="insertRowBelow"
|
|
||||||
onClick={() => editor.chain().focus().addRowAfter().run()}
|
|
||||||
sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
|
||||||
iconSize={16}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<MenuPresenter
|
|
||||||
isOpen={isMenuOpen}
|
|
||||||
onClose={() => {
|
|
||||||
setIsMenuOpen(false);
|
|
||||||
editor.commands.focus();
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
type: "menu",
|
|
||||||
position: {},
|
|
||||||
}}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: "addRowAbove",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Add row above",
|
|
||||||
onClick: () => editor.chain().focus().addRowBefore().run(),
|
|
||||||
icon: "insertRowAbove",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "moveRowUp",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Move row up",
|
|
||||||
onClick: () => moveRowUp(editor),
|
|
||||||
icon: "moveRowUp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "moveRowDown",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Move row down",
|
|
||||||
onClick: () => moveRowDown(editor),
|
|
||||||
icon: "moveRowDown",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "deleteRow",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Delete row",
|
|
||||||
onClick: () => editor.chain().focus().deleteRow().run(),
|
|
||||||
icon: "deleteRow",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</MenuPresenter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TableColumnFloatingMenu(props: FloatingMenuProps) {
|
|
||||||
const { editor } = props;
|
|
||||||
const [position, setPosition] = useState<MenuOptions["position"] | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const isInsideCellSelection =
|
|
||||||
!editor.state.selection.empty &&
|
|
||||||
editor.state.selection.$anchor.node().type.name === "tableCell";
|
|
||||||
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
const [showCellProps, setShowCellProps] = useState(false);
|
|
||||||
const [menuPosition, setMenuPosition] = useState<
|
|
||||||
MenuOptions["position"] | null
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!editor.isActive("tableCell") &&
|
|
||||||
!editor.isActive("tableRow") &&
|
|
||||||
!editor.isActive("tableHeader")
|
|
||||||
) {
|
|
||||||
setPosition(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let { $from } = editor.state.selection;
|
|
||||||
|
|
||||||
const selectedNode = $from.node();
|
|
||||||
const pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
|
||||||
|
|
||||||
const currentCell = (editor.view.nodeDOM(pos) as HTMLElement)?.closest(
|
|
||||||
"td,th"
|
|
||||||
);
|
|
||||||
const currentTable = currentCell?.closest("table");
|
|
||||||
|
|
||||||
if (!currentCell || !currentTable) return;
|
|
||||||
|
|
||||||
setPosition((old) => {
|
|
||||||
if (old?.target === currentCell) return old;
|
|
||||||
|
|
||||||
return {
|
|
||||||
isTargetAbsolute: true,
|
|
||||||
location: "top",
|
|
||||||
align: "center",
|
|
||||||
yAnchor: currentTable,
|
|
||||||
yOffset: -2,
|
|
||||||
target: currentCell as HTMLElement,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [editor.state.selection]);
|
|
||||||
|
|
||||||
if (!position) return null;
|
|
||||||
|
|
||||||
const columnProperties: MenuItem[] = [
|
|
||||||
{
|
|
||||||
key: "addColumnLeft",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Add column left",
|
|
||||||
onClick: () => editor.chain().focus().addColumnBefore().run(),
|
|
||||||
icon: "insertColumnLeft",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "addColumnRight",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Add column right",
|
|
||||||
onClick: () => editor.chain().focus().addColumnAfter().run(),
|
|
||||||
icon: "insertColumnRight",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "moveColumnLeft",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Move column left",
|
|
||||||
onClick: () => moveColumnLeft(editor),
|
|
||||||
icon: "moveColumnLeft",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "moveColumnRight",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Move column right",
|
|
||||||
onClick: () => moveColumnRight(editor),
|
|
||||||
icon: "moveColumnRight",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "deleteColumn",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Delete column",
|
|
||||||
onClick: () => editor.chain().focus().deleteColumn().run(),
|
|
||||||
icon: "deleteColumn",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const mergeSplitProperties: MenuItem[] = [
|
|
||||||
{
|
|
||||||
key: "splitCells",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Split cells",
|
|
||||||
onClick: () => editor.chain().focus().splitCell().run(),
|
|
||||||
icon: "splitCells",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "mergeCells",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Merge cells",
|
|
||||||
onClick: () => editor.chain().focus().mergeCells().run(),
|
|
||||||
icon: "mergeCells",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const cellProperties: MenuItem[] = [
|
|
||||||
{
|
|
||||||
key: "cellProperties",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Cell properties",
|
|
||||||
onClick: () => {
|
|
||||||
setShowCellProps(true);
|
|
||||||
setMenuPosition({
|
|
||||||
target: position.target || undefined,
|
|
||||||
isTargetAbsolute: true,
|
|
||||||
yOffset: 10,
|
|
||||||
location: "below",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: "cellProperties",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const tableProperties: MenuItem[] = [
|
|
||||||
{
|
|
||||||
key: "deleteTable",
|
|
||||||
type: "menuitem",
|
|
||||||
title: "Delete table",
|
|
||||||
icon: "deleteTable",
|
|
||||||
onClick: () => editor.chain().focus().deleteTable().run(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuPresenter
|
|
||||||
isOpen
|
|
||||||
items={[]}
|
|
||||||
onClose={() => {}}
|
|
||||||
options={{
|
|
||||||
type: "autocomplete",
|
|
||||||
position,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
bg: "background",
|
|
||||||
flexWrap: "nowrap",
|
|
||||||
borderRadius: "default",
|
|
||||||
opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
|
||||||
":hover": {
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToolButton
|
|
||||||
toggled={isMenuOpen}
|
|
||||||
title="Column properties"
|
|
||||||
id="properties"
|
|
||||||
icon="more"
|
|
||||||
onClick={async () => setIsMenuOpen(true)}
|
|
||||||
iconSize={16}
|
|
||||||
sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
toggled={false}
|
|
||||||
title="Insert column right"
|
|
||||||
id="insertColumnRight"
|
|
||||||
icon="plus"
|
|
||||||
onClick={() => editor.chain().focus().addColumnAfter().run()}
|
|
||||||
sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
|
||||||
iconSize={16}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<MenuPresenter
|
|
||||||
isOpen={isMenuOpen}
|
|
||||||
onClose={() => {
|
|
||||||
setIsMenuOpen(false);
|
|
||||||
editor.commands.focus();
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
type: "menu",
|
|
||||||
position: {},
|
|
||||||
}}
|
|
||||||
items={
|
|
||||||
isInsideCellSelection
|
|
||||||
? [...mergeSplitProperties, ...cellProperties]
|
|
||||||
: [
|
|
||||||
...columnProperties,
|
|
||||||
{ type: "seperator", key: "cellSeperator" },
|
|
||||||
...cellProperties,
|
|
||||||
{ type: "seperator", key: "tableSeperator" },
|
|
||||||
...tableProperties,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MenuPresenter
|
|
||||||
isOpen={showCellProps}
|
|
||||||
onClose={() => {
|
|
||||||
setShowCellProps(false);
|
|
||||||
editor.commands.focus();
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
type: "menu",
|
|
||||||
position: menuPosition || {},
|
|
||||||
}}
|
|
||||||
items={[]}
|
|
||||||
>
|
|
||||||
<CellProperties
|
|
||||||
editor={editor}
|
|
||||||
onClose={() => setShowCellProps(false)}
|
|
||||||
/>
|
|
||||||
</MenuPresenter>
|
|
||||||
</MenuPresenter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
type CellPropertiesProps = { editor: Editor; onClose: () => void };
|
|
||||||
function CellProperties(props: CellPropertiesProps) {
|
|
||||||
const { editor, onClose } = props;
|
|
||||||
const attributes = editor.getAttributes("tableCell");
|
|
||||||
console.log(attributes);
|
|
||||||
return (
|
|
||||||
<Popup
|
|
||||||
title="Cell properties"
|
|
||||||
action={{
|
|
||||||
icon: "close",
|
|
||||||
iconColor: "error",
|
|
||||||
onClick: onClose,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex sx={{ flexDirection: "column", width: 200, px: 1, mb: 2 }}>
|
|
||||||
<ColorPickerTool
|
|
||||||
color={attributes.backgroundColor}
|
|
||||||
title="Background color"
|
|
||||||
icon="backgroundColor"
|
|
||||||
onColorChange={(color) =>
|
|
||||||
editor.commands.setCellAttribute("backgroundColor", color)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ColorPickerTool
|
|
||||||
color={attributes.color}
|
|
||||||
title="Text color"
|
|
||||||
icon="textColor"
|
|
||||||
onColorChange={(color) =>
|
|
||||||
editor.commands.setCellAttribute("color", color)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ColorPickerTool
|
|
||||||
color={attributes.borderColor}
|
|
||||||
title="Border color"
|
|
||||||
icon="borderColor"
|
|
||||||
onColorChange={(color) =>
|
|
||||||
editor.commands.setCellAttribute("borderColor", color)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Flex sx={{ flexDirection: "column" }}>
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
mt: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text variant={"body"}>Border width</Text>
|
|
||||||
<Text variant={"body"}>{attributes.borderWidth || 1}px</Text>
|
|
||||||
</Flex>
|
|
||||||
<Slider
|
|
||||||
min={1}
|
|
||||||
max={5}
|
|
||||||
value={attributes.borderWidth || 1}
|
|
||||||
onChange={(e) => {
|
|
||||||
editor.commands.setCellAttribute(
|
|
||||||
"borderWidth",
|
|
||||||
e.target.valueAsNumber
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Popup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
type ColorPickerToolProps = {
|
|
||||||
color: string;
|
|
||||||
title: string;
|
|
||||||
icon: IconNames;
|
|
||||||
onColorChange: (color?: string) => void;
|
|
||||||
};
|
|
||||||
function ColorPickerTool(props: ColorPickerToolProps) {
|
|
||||||
const { color, title, icon, onColorChange } = props;
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex
|
|
||||||
sx={{ justifyContent: "space-between", alignItems: "center", mt: 1 }}
|
|
||||||
>
|
|
||||||
<Text variant={"body"}>{title}</Text>
|
|
||||||
<ToolButton
|
|
||||||
buttonRef={buttonRef}
|
|
||||||
toggled={isOpen}
|
|
||||||
title={title}
|
|
||||||
id={icon}
|
|
||||||
icon={icon}
|
|
||||||
iconSize={16}
|
|
||||||
sx={{
|
|
||||||
p: "2.5px",
|
|
||||||
borderRadius: "small",
|
|
||||||
backgroundColor: color || "transparent",
|
|
||||||
":hover": { bg: color, filter: "brightness(90%)" },
|
|
||||||
}}
|
|
||||||
onClick={() => setIsOpen(true)}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<MenuPresenter
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={() => setIsOpen(false)}
|
|
||||||
items={[]}
|
|
||||||
options={{
|
|
||||||
type: "menu",
|
|
||||||
position: {
|
|
||||||
target: buttonRef.current || undefined,
|
|
||||||
location: "below",
|
|
||||||
align: "center",
|
|
||||||
isTargetAbsolute: true,
|
|
||||||
yOffset: 5,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
flexDirection: "column",
|
|
||||||
bg: "background",
|
|
||||||
boxShadow: "menu",
|
|
||||||
border: "1px solid var(--border)",
|
|
||||||
borderRadius: "default",
|
|
||||||
p: 1,
|
|
||||||
width: 160,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* <ColorPicker
|
|
||||||
colors={DEFAULT_COLORS}
|
|
||||||
color={color}
|
|
||||||
onClear={() => onColorChange()}
|
|
||||||
onChange={(color) => onColorChange(color)}
|
|
||||||
/> */}
|
|
||||||
</Flex>
|
|
||||||
</MenuPresenter>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Done:
|
|
||||||
* insertTable
|
|
||||||
*
|
|
||||||
* addRowBefore
|
|
||||||
* addRowAfter
|
|
||||||
* deleteRow
|
|
||||||
*
|
|
||||||
* addColumnBefore
|
|
||||||
* addColumnAfter
|
|
||||||
* deleteColumn
|
|
||||||
*
|
|
||||||
* setCellAttribute
|
|
||||||
*
|
|
||||||
* deleteTable
|
|
||||||
*
|
|
||||||
* mergeCells
|
|
||||||
* splitCell
|
|
||||||
* mergeOrSplit
|
|
||||||
*
|
|
||||||
* toggleHeaderColumn
|
|
||||||
* toggleHeaderRow
|
|
||||||
* toggleHeaderCell
|
|
||||||
* fixTables
|
|
||||||
* goToNextCell
|
|
||||||
* goToPreviousCell
|
|
||||||
*/
|
|
||||||
|
|
||||||
function moveColumnRight(editor: Editor) {
|
|
||||||
const { tr } = editor.state;
|
|
||||||
const rect = selectedRect(editor.state);
|
|
||||||
if (rect.right === rect.map.width) return;
|
|
||||||
|
|
||||||
const transaction = moveColumn(tr, rect, rect.left, rect.left + 1);
|
|
||||||
if (!transaction) return;
|
|
||||||
|
|
||||||
editor.view.dispatch(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveColumnLeft(editor: Editor) {
|
|
||||||
const { tr } = editor.state;
|
|
||||||
const rect = selectedRect(editor.state);
|
|
||||||
if (rect.left === 0) return;
|
|
||||||
|
|
||||||
const transaction = moveColumn(tr, rect, rect.left, rect.left - 1);
|
|
||||||
if (!transaction) return;
|
|
||||||
|
|
||||||
editor.view.dispatch(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveRowDown(editor: Editor) {
|
|
||||||
const { tr } = editor.state;
|
|
||||||
const rect = selectedRect(editor.state);
|
|
||||||
if (rect.top + 1 === rect.map.height) return;
|
|
||||||
|
|
||||||
const transaction = moveRow(tr, rect, rect.top, rect.top + 1);
|
|
||||||
if (!transaction) return;
|
|
||||||
|
|
||||||
editor.view.dispatch(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveRowUp(editor: Editor) {
|
|
||||||
const { tr } = editor.state;
|
|
||||||
const rect = selectedRect(editor.state);
|
|
||||||
if (rect.top === 0) return;
|
|
||||||
|
|
||||||
const transaction = moveRow(tr, rect, rect.top, rect.top - 1);
|
|
||||||
if (!transaction) return;
|
|
||||||
|
|
||||||
editor.view.dispatch(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveColumn(
|
|
||||||
tr: Transaction<any>,
|
|
||||||
rect: TableRect,
|
|
||||||
from: number,
|
|
||||||
to: number
|
|
||||||
) {
|
|
||||||
let fromCells = getColumnCells(rect, from);
|
|
||||||
let toCells = getColumnCells(rect, to);
|
|
||||||
|
|
||||||
return moveCells(tr, rect, fromCells, toCells);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getColumnCells({ map, table }: TableRect, col: number) {
|
|
||||||
let cells = [];
|
|
||||||
for (let row = 0; row < map.height; ) {
|
|
||||||
let index = row * map.width + col;
|
|
||||||
if (index >= map.map.length) break;
|
|
||||||
|
|
||||||
let pos = map.map[index];
|
|
||||||
|
|
||||||
let cell = table.nodeAt(pos);
|
|
||||||
if (!cell) continue;
|
|
||||||
cells.push({ cell, pos });
|
|
||||||
|
|
||||||
row += cell.attrs.rowspan;
|
|
||||||
console.log(cell.textContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveRow(
|
|
||||||
tr: Transaction<any>,
|
|
||||||
rect: TableRect,
|
|
||||||
from: number,
|
|
||||||
to: number
|
|
||||||
) {
|
|
||||||
let fromCells = getRowCells(rect, from);
|
|
||||||
let toCells = getRowCells(rect, to);
|
|
||||||
return moveCells(tr, rect, fromCells, toCells);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRowCells({ map, table }: TableRect, row: number) {
|
|
||||||
let cells = [];
|
|
||||||
for (let col = 0, index = row * map.width; col < map.width; col++, index++) {
|
|
||||||
if (index >= map.map.length) break;
|
|
||||||
|
|
||||||
let pos = map.map[index];
|
|
||||||
let cell = table.nodeAt(pos);
|
|
||||||
|
|
||||||
if (!cell) continue;
|
|
||||||
cells.push({ cell, pos });
|
|
||||||
|
|
||||||
col += cell.attrs.colspan - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveCells(
|
|
||||||
tr: Transaction<any>,
|
|
||||||
rect: TableRect,
|
|
||||||
fromCells: any[],
|
|
||||||
toCells: any[]
|
|
||||||
) {
|
|
||||||
if (fromCells.length !== toCells.length) return;
|
|
||||||
let mapStart = tr.mapping.maps.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < toCells.length; ++i) {
|
|
||||||
const fromCell = fromCells[i];
|
|
||||||
const toCell = toCells[i];
|
|
||||||
|
|
||||||
let fromStart = tr.mapping
|
|
||||||
.slice(mapStart)
|
|
||||||
.map(rect.tableStart + fromCell.pos);
|
|
||||||
let fromEnd = fromStart + fromCell.cell.nodeSize;
|
|
||||||
const fromSlice = tr.doc.slice(fromStart, fromEnd);
|
|
||||||
|
|
||||||
const toStart = tr.mapping
|
|
||||||
.slice(mapStart)
|
|
||||||
.map(rect.tableStart + toCell.pos);
|
|
||||||
const toEnd = toStart + toCell.cell.nodeSize;
|
|
||||||
const toSlice = tr.doc.slice(toStart, toEnd);
|
|
||||||
|
|
||||||
tr.replace(toStart, toEnd, fromSlice);
|
|
||||||
|
|
||||||
fromStart = tr.mapping.slice(mapStart).map(rect.tableStart + fromCell.pos);
|
|
||||||
fromEnd = fromStart + fromCell.cell.nodeSize;
|
|
||||||
tr.replace(fromStart, fromEnd, toSlice);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr;
|
|
||||||
}
|
|
||||||
163
packages/editor/src/toolbar/floating-menus/table/actions.ts
Normal file
163
packages/editor/src/toolbar/floating-menus/table/actions.ts
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import { Theme } from "@notesnook/theme";
|
||||||
|
import { Slider } from "@rebass/forms";
|
||||||
|
import { Editor, findParentNodeClosestToPos } from "@tiptap/core";
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { Flex, Text } from "rebass";
|
||||||
|
import {
|
||||||
|
ActionSheetPresenter,
|
||||||
|
MenuPresenter,
|
||||||
|
} from "../../../components/menu/menu";
|
||||||
|
import {
|
||||||
|
getElementPosition,
|
||||||
|
MenuOptions,
|
||||||
|
} from "../../../components/menu/useMenu";
|
||||||
|
import { Popup } from "../../components/popup";
|
||||||
|
import { ToolButton, ToolButtonProps } from "../../components/tool-button";
|
||||||
|
import { IconNames } from "../../icons";
|
||||||
|
// import { ColorPicker, DEFAULT_COLORS } from "../tools/colors";
|
||||||
|
import { FloatingMenuProps } from "../types";
|
||||||
|
import { selectedRect, TableMap, TableRect } from "prosemirror-tables";
|
||||||
|
import { Transaction } from "prosemirror-state";
|
||||||
|
import { MenuItem } from "../../../components/menu/types";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../../components/responsive";
|
||||||
|
import { ToolProps } from "../../types";
|
||||||
|
|
||||||
|
function moveColumnRight(editor: Editor) {
|
||||||
|
const { tr } = editor.state;
|
||||||
|
const rect = selectedRect(editor.state);
|
||||||
|
if (rect.right === rect.map.width) return;
|
||||||
|
|
||||||
|
const transaction = moveColumn(tr, rect, rect.left, rect.left + 1);
|
||||||
|
if (!transaction) return;
|
||||||
|
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveColumnLeft(editor: Editor) {
|
||||||
|
const { tr } = editor.state;
|
||||||
|
const rect = selectedRect(editor.state);
|
||||||
|
if (rect.left === 0) return;
|
||||||
|
|
||||||
|
const transaction = moveColumn(tr, rect, rect.left, rect.left - 1);
|
||||||
|
if (!transaction) return;
|
||||||
|
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveRowDown(editor: Editor) {
|
||||||
|
const { tr } = editor.state;
|
||||||
|
const rect = selectedRect(editor.state);
|
||||||
|
if (rect.top + 1 === rect.map.height) return;
|
||||||
|
|
||||||
|
const transaction = moveRow(tr, rect, rect.top, rect.top + 1);
|
||||||
|
if (!transaction) return;
|
||||||
|
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveRowUp(editor: Editor) {
|
||||||
|
const { tr } = editor.state;
|
||||||
|
const rect = selectedRect(editor.state);
|
||||||
|
if (rect.top === 0) return;
|
||||||
|
|
||||||
|
const transaction = moveRow(tr, rect, rect.top, rect.top - 1);
|
||||||
|
if (!transaction) return;
|
||||||
|
|
||||||
|
editor.view.dispatch(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveColumn(
|
||||||
|
tr: Transaction<any>,
|
||||||
|
rect: TableRect,
|
||||||
|
from: number,
|
||||||
|
to: number
|
||||||
|
) {
|
||||||
|
let fromCells = getColumnCells(rect, from);
|
||||||
|
let toCells = getColumnCells(rect, to);
|
||||||
|
|
||||||
|
return moveCells(tr, rect, fromCells, toCells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumnCells({ map, table }: TableRect, col: number) {
|
||||||
|
let cells = [];
|
||||||
|
for (let row = 0; row < map.height; ) {
|
||||||
|
let index = row * map.width + col;
|
||||||
|
if (index >= map.map.length) break;
|
||||||
|
|
||||||
|
let pos = map.map[index];
|
||||||
|
|
||||||
|
let cell = table.nodeAt(pos);
|
||||||
|
if (!cell) continue;
|
||||||
|
cells.push({ cell, pos });
|
||||||
|
|
||||||
|
row += cell.attrs.rowspan;
|
||||||
|
console.log(cell.textContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveRow(
|
||||||
|
tr: Transaction<any>,
|
||||||
|
rect: TableRect,
|
||||||
|
from: number,
|
||||||
|
to: number
|
||||||
|
) {
|
||||||
|
let fromCells = getRowCells(rect, from);
|
||||||
|
let toCells = getRowCells(rect, to);
|
||||||
|
return moveCells(tr, rect, fromCells, toCells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowCells({ map, table }: TableRect, row: number) {
|
||||||
|
let cells = [];
|
||||||
|
for (let col = 0, index = row * map.width; col < map.width; col++, index++) {
|
||||||
|
if (index >= map.map.length) break;
|
||||||
|
|
||||||
|
let pos = map.map[index];
|
||||||
|
let cell = table.nodeAt(pos);
|
||||||
|
|
||||||
|
if (!cell) continue;
|
||||||
|
cells.push({ cell, pos });
|
||||||
|
|
||||||
|
col += cell.attrs.colspan - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveCells(
|
||||||
|
tr: Transaction<any>,
|
||||||
|
rect: TableRect,
|
||||||
|
fromCells: any[],
|
||||||
|
toCells: any[]
|
||||||
|
) {
|
||||||
|
if (fromCells.length !== toCells.length) return;
|
||||||
|
let mapStart = tr.mapping.maps.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < toCells.length; ++i) {
|
||||||
|
const fromCell = fromCells[i];
|
||||||
|
const toCell = toCells[i];
|
||||||
|
|
||||||
|
let fromStart = tr.mapping
|
||||||
|
.slice(mapStart)
|
||||||
|
.map(rect.tableStart + fromCell.pos);
|
||||||
|
let fromEnd = fromStart + fromCell.cell.nodeSize;
|
||||||
|
const fromSlice = tr.doc.slice(fromStart, fromEnd);
|
||||||
|
|
||||||
|
const toStart = tr.mapping
|
||||||
|
.slice(mapStart)
|
||||||
|
.map(rect.tableStart + toCell.pos);
|
||||||
|
const toEnd = toStart + toCell.cell.nodeSize;
|
||||||
|
const toSlice = tr.doc.slice(toStart, toEnd);
|
||||||
|
|
||||||
|
tr.replace(toStart, toEnd, fromSlice);
|
||||||
|
|
||||||
|
fromStart = tr.mapping.slice(mapStart).map(rect.tableStart + fromCell.pos);
|
||||||
|
fromEnd = fromStart + fromCell.cell.nodeSize;
|
||||||
|
tr.replace(fromStart, fromEnd, toSlice);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { moveColumnLeft, moveColumnRight, moveRowDown, moveRowUp };
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export {
|
||||||
|
TableColumnFloatingMenu,
|
||||||
|
TableFloatingMenu,
|
||||||
|
TableRowFloatingMenu,
|
||||||
|
} from "./table";
|
||||||
234
packages/editor/src/toolbar/floating-menus/table/table.tsx
Normal file
234
packages/editor/src/toolbar/floating-menus/table/table.tsx
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Flex } from "rebass";
|
||||||
|
import { MenuPresenter } from "../../../components/menu/menu";
|
||||||
|
import { MenuOptions } from "../../../components/menu/useMenu";
|
||||||
|
// import { ColorPicker, DEFAULT_COLORS } from "../tools/colors";
|
||||||
|
import { FloatingMenuProps } from "../types";
|
||||||
|
import {
|
||||||
|
ColumnProperties,
|
||||||
|
InsertColumnRight,
|
||||||
|
InsertRowBelow,
|
||||||
|
RowProperties,
|
||||||
|
} from "./tools";
|
||||||
|
import { getToolbarElement } from "../../utils/dom";
|
||||||
|
import { useToolbarContext } from "../../hooks/useToolbarContext";
|
||||||
|
import { useToolbarLocation } from "../../stores/toolbar-store";
|
||||||
|
|
||||||
|
export function TableRowFloatingMenu(props: FloatingMenuProps) {
|
||||||
|
const { editor } = props;
|
||||||
|
// const theme = editor.storage.theme as Theme;
|
||||||
|
const [position, setPosition] = useState<MenuOptions["position"] | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!editor.isActive("tableCell") &&
|
||||||
|
!editor.isActive("tableRow") &&
|
||||||
|
!editor.isActive("tableHeader")
|
||||||
|
) {
|
||||||
|
setPosition(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { $from } = editor.state.selection;
|
||||||
|
|
||||||
|
const selectedNode = $from.node();
|
||||||
|
const pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
||||||
|
|
||||||
|
const currentRow = (editor.view.nodeDOM(pos) as HTMLElement)?.closest("tr");
|
||||||
|
if (!currentRow) return;
|
||||||
|
setPosition((old) => {
|
||||||
|
if (old?.target === currentRow) return old;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
location: "left",
|
||||||
|
xOffset: -5,
|
||||||
|
target: currentRow,
|
||||||
|
// parent: editor.view.dom as HTMLElement,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [editor.state.selection]);
|
||||||
|
|
||||||
|
if (!position) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuPresenter
|
||||||
|
isOpen
|
||||||
|
items={[]}
|
||||||
|
onClose={() => {}}
|
||||||
|
options={{
|
||||||
|
type: "autocomplete",
|
||||||
|
position,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: isMenuOpen ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RowProperties
|
||||||
|
title="Row properties"
|
||||||
|
editor={editor}
|
||||||
|
variant="small"
|
||||||
|
icon="more"
|
||||||
|
/>
|
||||||
|
<InsertRowBelow
|
||||||
|
title="Insert row below"
|
||||||
|
icon="insertRowBelow"
|
||||||
|
editor={editor}
|
||||||
|
variant="small"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</MenuPresenter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TableColumnFloatingMenu(props: FloatingMenuProps) {
|
||||||
|
const { editor } = props;
|
||||||
|
const [position, setPosition] = useState<MenuOptions["position"] | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!editor.isActive("tableCell") &&
|
||||||
|
!editor.isActive("tableRow") &&
|
||||||
|
!editor.isActive("tableHeader")
|
||||||
|
) {
|
||||||
|
setPosition(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let { $from } = editor.state.selection;
|
||||||
|
|
||||||
|
const selectedNode = $from.node();
|
||||||
|
const pos = selectedNode.isTextblock ? $from.before() : $from.pos;
|
||||||
|
|
||||||
|
const currentCell = (editor.view.nodeDOM(pos) as HTMLElement)?.closest(
|
||||||
|
"td,th"
|
||||||
|
);
|
||||||
|
const currentTable = currentCell?.closest("table");
|
||||||
|
|
||||||
|
if (!currentCell || !currentTable) return;
|
||||||
|
|
||||||
|
setPosition((old) => {
|
||||||
|
if (old?.target === currentCell) return old;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
location: "top",
|
||||||
|
align: "center",
|
||||||
|
yAnchor: currentTable,
|
||||||
|
yOffset: 2,
|
||||||
|
target: currentCell as HTMLElement,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [editor.state.selection]);
|
||||||
|
|
||||||
|
if (!position) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuPresenter
|
||||||
|
isOpen
|
||||||
|
items={[]}
|
||||||
|
onClose={() => {}}
|
||||||
|
options={{
|
||||||
|
type: "autocomplete",
|
||||||
|
position,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: 0.3,
|
||||||
|
// opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ColumnProperties
|
||||||
|
currentCell={position.target as HTMLElement}
|
||||||
|
title="Column properties"
|
||||||
|
editor={editor}
|
||||||
|
icon="more"
|
||||||
|
variant={"small"}
|
||||||
|
/>
|
||||||
|
<InsertColumnRight
|
||||||
|
editor={editor}
|
||||||
|
title="Insert column right"
|
||||||
|
variant={"small"}
|
||||||
|
icon="plus"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</MenuPresenter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TableFloatingMenu(props: FloatingMenuProps) {
|
||||||
|
const { editor } = props;
|
||||||
|
const toolbarLocation = useToolbarLocation();
|
||||||
|
if (!editor.isActive("table")) return null;
|
||||||
|
return (
|
||||||
|
<MenuPresenter
|
||||||
|
isOpen
|
||||||
|
items={[]}
|
||||||
|
onClose={() => {}}
|
||||||
|
options={{
|
||||||
|
type: "autocomplete",
|
||||||
|
position: {
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
target: getToolbarElement(),
|
||||||
|
location: toolbarLocation === "bottom" ? "top" : "below",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
bg: "background",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
borderRadius: "default",
|
||||||
|
// opacity: 0.3,
|
||||||
|
// opacity: isMenuOpen || showCellProps ? 1 : 0.3,
|
||||||
|
":hover": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RowProperties
|
||||||
|
title="Row properties"
|
||||||
|
editor={editor}
|
||||||
|
variant="normal"
|
||||||
|
icon="rowProperties"
|
||||||
|
/>
|
||||||
|
<InsertRowBelow
|
||||||
|
title="Insert row below"
|
||||||
|
icon="insertRowBelow"
|
||||||
|
editor={editor}
|
||||||
|
variant="normal"
|
||||||
|
/>
|
||||||
|
<ColumnProperties
|
||||||
|
title="Column properties"
|
||||||
|
editor={editor}
|
||||||
|
icon="columnProperties"
|
||||||
|
variant={"normal"}
|
||||||
|
/>
|
||||||
|
<InsertColumnRight
|
||||||
|
editor={editor}
|
||||||
|
title="Insert column right"
|
||||||
|
variant={"normal"}
|
||||||
|
icon="insertColumnRight"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</MenuPresenter>
|
||||||
|
);
|
||||||
|
}
|
||||||
262
packages/editor/src/toolbar/floating-menus/table/tools.tsx
Normal file
262
packages/editor/src/toolbar/floating-menus/table/tools.tsx
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
import { Theme } from "@notesnook/theme";
|
||||||
|
import { Slider } from "@rebass/forms";
|
||||||
|
import { Editor, findParentNodeClosestToPos } from "@tiptap/core";
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { Flex, Text } from "rebass";
|
||||||
|
import {
|
||||||
|
ActionSheetPresenter,
|
||||||
|
MenuPresenter,
|
||||||
|
PopupPresenter,
|
||||||
|
} from "../../../components/menu/menu";
|
||||||
|
import {
|
||||||
|
getElementPosition,
|
||||||
|
MenuOptions,
|
||||||
|
} from "../../../components/menu/useMenu";
|
||||||
|
import { Popup } from "../../components/popup";
|
||||||
|
import { ToolButton, ToolButtonProps } from "../../components/tool-button";
|
||||||
|
import { IconNames } from "../../icons";
|
||||||
|
// import { ColorPicker, DEFAULT_COLORS } from "../tools/colors";
|
||||||
|
import { FloatingMenuProps } from "../types";
|
||||||
|
import { selectedRect, TableMap, TableRect } from "prosemirror-tables";
|
||||||
|
import { Transaction } from "prosemirror-state";
|
||||||
|
import { MenuItem } from "../../../components/menu/types";
|
||||||
|
import { DesktopOnly, MobileOnly } from "../../../components/responsive";
|
||||||
|
import { ToolProps } from "../../types";
|
||||||
|
import { CellProperties } from "../../popups/cell-properties";
|
||||||
|
import {
|
||||||
|
moveColumnLeft,
|
||||||
|
moveColumnRight,
|
||||||
|
moveRowDown,
|
||||||
|
moveRowUp,
|
||||||
|
} from "./actions";
|
||||||
|
|
||||||
|
type TableToolProps = ToolProps & { variant: ToolButtonProps["variant"] };
|
||||||
|
|
||||||
|
export function RowProperties(props: TableToolProps) {
|
||||||
|
const { editor, ...toolProps } = props;
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ToolButton
|
||||||
|
toggled={isMenuOpen}
|
||||||
|
{...toolProps}
|
||||||
|
onClick={() => setIsMenuOpen(true)}
|
||||||
|
// iconSize={16}
|
||||||
|
// sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
||||||
|
/>
|
||||||
|
<MenuPresenter
|
||||||
|
isOpen={isMenuOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
type: "menu",
|
||||||
|
position: {},
|
||||||
|
}}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: "addRowAbove",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Add row above",
|
||||||
|
onClick: () => editor.chain().focus().addRowBefore().run(),
|
||||||
|
icon: "insertRowAbove",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveRowUp",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move row up",
|
||||||
|
onClick: () => moveRowUp(editor),
|
||||||
|
icon: "moveRowUp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveRowDown",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move row down",
|
||||||
|
onClick: () => moveRowDown(editor),
|
||||||
|
icon: "moveRowDown",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "deleteRow",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Delete row",
|
||||||
|
onClick: () => editor.chain().focus().deleteRow().run(),
|
||||||
|
icon: "deleteRow",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InsertRowBelow(props: TableToolProps) {
|
||||||
|
const { editor, ...toolProps } = props;
|
||||||
|
return (
|
||||||
|
<ToolButton
|
||||||
|
toggled={false}
|
||||||
|
{...toolProps}
|
||||||
|
onClick={() => editor.chain().focus().addRowAfter().run()}
|
||||||
|
// sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
||||||
|
// iconSize={16}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColumnPropertiesProps = TableToolProps & {
|
||||||
|
currentCell?: HTMLElement;
|
||||||
|
};
|
||||||
|
export function ColumnProperties(props: ColumnPropertiesProps) {
|
||||||
|
const { editor, currentCell, ...toolProps } = props;
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
const isInsideCellSelection =
|
||||||
|
!editor.state.selection.empty &&
|
||||||
|
editor.state.selection.$anchor.node().type.name === "tableCell";
|
||||||
|
|
||||||
|
const [showCellProps, setShowCellProps] = useState(false);
|
||||||
|
const [menuPosition, setMenuPosition] = useState<
|
||||||
|
MenuOptions["position"] | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
const columnProperties: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: "addColumnLeft",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Add column left",
|
||||||
|
onClick: () => editor.chain().focus().addColumnBefore().run(),
|
||||||
|
icon: "insertColumnLeft",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "addColumnRight",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Add column right",
|
||||||
|
onClick: () => editor.chain().focus().addColumnAfter().run(),
|
||||||
|
icon: "insertColumnRight",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveColumnLeft",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move column left",
|
||||||
|
onClick: () => moveColumnLeft(editor),
|
||||||
|
icon: "moveColumnLeft",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "moveColumnRight",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Move column right",
|
||||||
|
onClick: () => moveColumnRight(editor),
|
||||||
|
icon: "moveColumnRight",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "deleteColumn",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Delete column",
|
||||||
|
onClick: () => editor.chain().focus().deleteColumn().run(),
|
||||||
|
icon: "deleteColumn",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mergeSplitProperties: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: "splitCells",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Split cells",
|
||||||
|
onClick: () => editor.chain().focus().splitCell().run(),
|
||||||
|
icon: "splitCells",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "mergeCells",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Merge cells",
|
||||||
|
onClick: () => editor.chain().focus().mergeCells().run(),
|
||||||
|
icon: "mergeCells",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const cellProperties: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: "cellProperties",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Cell properties",
|
||||||
|
onClick: () => {
|
||||||
|
setShowCellProps(true);
|
||||||
|
setMenuPosition({
|
||||||
|
target: currentCell || undefined,
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
yOffset: 10,
|
||||||
|
location: "below",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: "cellProperties",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const tableProperties: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: "deleteTable",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Delete table",
|
||||||
|
icon: "deleteTable",
|
||||||
|
onClick: () => editor.chain().focus().deleteTable().run(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ToolButton
|
||||||
|
toggled={isMenuOpen}
|
||||||
|
{...toolProps}
|
||||||
|
onClick={async () => setIsMenuOpen(true)}
|
||||||
|
/>
|
||||||
|
<PopupPresenter
|
||||||
|
isOpen={isMenuOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}}
|
||||||
|
mobile="sheet"
|
||||||
|
items={
|
||||||
|
isInsideCellSelection
|
||||||
|
? [...mergeSplitProperties, ...cellProperties]
|
||||||
|
: [
|
||||||
|
...columnProperties,
|
||||||
|
{ type: "seperator", key: "cellSeperator" },
|
||||||
|
...cellProperties,
|
||||||
|
{ type: "seperator", key: "tableSeperator" },
|
||||||
|
...tableProperties,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PopupPresenter
|
||||||
|
isOpen={showCellProps}
|
||||||
|
onClose={() => {
|
||||||
|
setShowCellProps(false);
|
||||||
|
editor.commands.focus();
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
type: "menu",
|
||||||
|
position: menuPosition || {},
|
||||||
|
}}
|
||||||
|
mobile="sheet"
|
||||||
|
>
|
||||||
|
<CellProperties
|
||||||
|
editor={editor}
|
||||||
|
onClose={() => setShowCellProps(false)}
|
||||||
|
/>
|
||||||
|
</PopupPresenter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InsertColumnRight(props: TableToolProps) {
|
||||||
|
const { editor, ...toolProps } = props;
|
||||||
|
return (
|
||||||
|
<ToolButton
|
||||||
|
{...toolProps}
|
||||||
|
toggled={false}
|
||||||
|
onClick={() => editor.chain().focus().addColumnAfter().run()}
|
||||||
|
// sx={{ mr: 0, p: "3px", borderRadius: "small" }}
|
||||||
|
// iconSize={16}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
|
|
||||||
export type ToolbarLocation = "top" | "bottom";
|
|
||||||
|
|
||||||
export const ToolbarContext = React.createContext<{
|
export const ToolbarContext = React.createContext<{
|
||||||
currentPopup?: string;
|
currentPopup?: string;
|
||||||
setCurrentPopup?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
setCurrentPopup?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
toolbarLocation?: ToolbarLocation;
|
|
||||||
}>({});
|
}>({});
|
||||||
|
|
||||||
export function useToolbarContext() {
|
export function useToolbarContext() {
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ import {
|
|||||||
mdiMoviePlusOutline,
|
mdiMoviePlusOutline,
|
||||||
mdiLink,
|
mdiLink,
|
||||||
mdiChevronRight,
|
mdiChevronRight,
|
||||||
|
mdiTableRow,
|
||||||
|
mdiTableColumn,
|
||||||
|
mdiTableColumnWidth,
|
||||||
|
mdiTableRowHeight,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
|
||||||
export const Icons = {
|
export const Icons = {
|
||||||
@@ -113,12 +117,14 @@ export const Icons = {
|
|||||||
upload: mdiUploadOutline,
|
upload: mdiUploadOutline,
|
||||||
attachment: mdiAttachment,
|
attachment: mdiAttachment,
|
||||||
table: mdiTable,
|
table: mdiTable,
|
||||||
insertRowBelow: mdiPlus,
|
rowProperties: mdiTableRowHeight,
|
||||||
|
insertRowBelow: mdiTableRowPlusAfter,
|
||||||
insertRowAbove: mdiTableRowPlusBefore,
|
insertRowAbove: mdiTableRowPlusBefore,
|
||||||
moveRowDown: mdiArrowExpandDown,
|
moveRowDown: mdiArrowExpandDown,
|
||||||
moveRowUp: mdiArrowExpandUp,
|
moveRowUp: mdiArrowExpandUp,
|
||||||
deleteRow: mdiTableRowRemove,
|
deleteRow: mdiTableRowRemove,
|
||||||
toggleHeaderRow: mdiTableBorder,
|
toggleHeaderRow: mdiTableBorder,
|
||||||
|
columnProperties: mdiTableColumnWidth,
|
||||||
insertColumnRight: mdiTableColumnPlusAfter,
|
insertColumnRight: mdiTableColumnPlusAfter,
|
||||||
insertColumnLeft: mdiTableColumnPlusBefore,
|
insertColumnLeft: mdiTableColumnPlusBefore,
|
||||||
moveColumnRight: mdiArrowExpandRight,
|
moveColumnRight: mdiArrowExpandRight,
|
||||||
|
|||||||
145
packages/editor/src/toolbar/popups/cell-properties.tsx
Normal file
145
packages/editor/src/toolbar/popups/cell-properties.tsx
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { Slider } from "@rebass/forms";
|
||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { Flex, Text } from "rebass";
|
||||||
|
import { MenuPresenter } from "../../components/menu/menu";
|
||||||
|
import { Popup } from "../components/popup";
|
||||||
|
import { ToolButton } from "../components/tool-button";
|
||||||
|
import { IconNames } from "../icons";
|
||||||
|
|
||||||
|
type CellPropertiesProps = { editor: Editor; onClose: () => void };
|
||||||
|
export function CellProperties(props: CellPropertiesProps) {
|
||||||
|
const { editor, onClose } = props;
|
||||||
|
const attributes = editor.getAttributes("tableCell");
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
title="Cell properties"
|
||||||
|
action={{
|
||||||
|
icon: "close",
|
||||||
|
iconColor: "error",
|
||||||
|
onClick: onClose,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex sx={{ flexDirection: "column", px: 1, mb: 2 }}>
|
||||||
|
<ColorPickerTool
|
||||||
|
color={attributes.backgroundColor}
|
||||||
|
title="Background color"
|
||||||
|
icon="backgroundColor"
|
||||||
|
onColorChange={(color) =>
|
||||||
|
editor.commands.setCellAttribute("backgroundColor", color)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ColorPickerTool
|
||||||
|
color={attributes.color}
|
||||||
|
title="Text color"
|
||||||
|
icon="textColor"
|
||||||
|
onColorChange={(color) =>
|
||||||
|
editor.commands.setCellAttribute("color", color)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ColorPickerTool
|
||||||
|
color={attributes.borderColor}
|
||||||
|
title="Border color"
|
||||||
|
icon="borderColor"
|
||||||
|
onColorChange={(color) =>
|
||||||
|
editor.commands.setCellAttribute("borderColor", color)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Flex sx={{ flexDirection: "column" }}>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
mt: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text variant={"body"}>Border width</Text>
|
||||||
|
<Text variant={"body"}>{attributes.borderWidth || 1}px</Text>
|
||||||
|
</Flex>
|
||||||
|
<Slider
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
value={attributes.borderWidth || 1}
|
||||||
|
onChange={(e) => {
|
||||||
|
editor.commands.setCellAttribute(
|
||||||
|
"borderWidth",
|
||||||
|
e.target.valueAsNumber
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
type ColorPickerToolProps = {
|
||||||
|
color: string;
|
||||||
|
title: string;
|
||||||
|
icon: IconNames;
|
||||||
|
onColorChange: (color?: string) => void;
|
||||||
|
};
|
||||||
|
function ColorPickerTool(props: ColorPickerToolProps) {
|
||||||
|
const { color, title, icon, onColorChange } = props;
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
sx={{ justifyContent: "space-between", alignItems: "center", mt: 1 }}
|
||||||
|
>
|
||||||
|
<Text variant={"body"}>{title}</Text>
|
||||||
|
<ToolButton
|
||||||
|
buttonRef={buttonRef}
|
||||||
|
toggled={isOpen}
|
||||||
|
title={title}
|
||||||
|
id={icon}
|
||||||
|
icon={icon}
|
||||||
|
iconSize={16}
|
||||||
|
sx={{
|
||||||
|
p: "2.5px",
|
||||||
|
borderRadius: "small",
|
||||||
|
backgroundColor: color || "transparent",
|
||||||
|
":hover": { bg: color, filter: "brightness(90%)" },
|
||||||
|
}}
|
||||||
|
onClick={() => setIsOpen(true)}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<MenuPresenter
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
items={[]}
|
||||||
|
options={{
|
||||||
|
type: "menu",
|
||||||
|
position: {
|
||||||
|
target: buttonRef.current || undefined,
|
||||||
|
location: "below",
|
||||||
|
align: "center",
|
||||||
|
isTargetAbsolute: true,
|
||||||
|
yOffset: 5,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
flexDirection: "column",
|
||||||
|
bg: "background",
|
||||||
|
boxShadow: "menu",
|
||||||
|
border: "1px solid var(--border)",
|
||||||
|
borderRadius: "default",
|
||||||
|
p: 1,
|
||||||
|
width: 160,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <ColorPicker
|
||||||
|
colors={DEFAULT_COLORS}
|
||||||
|
color={color}
|
||||||
|
onClear={() => onColorChange()}
|
||||||
|
onChange={(color) => onColorChange(color)}
|
||||||
|
/> */}
|
||||||
|
</Flex>
|
||||||
|
</MenuPresenter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -100,7 +100,7 @@ export function EmbedPopup(props: EmbedPopupProps) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex sx={{ width: 300, flexDirection: "column", p: 1 }}>
|
<Flex sx={{ flexDirection: "column", p: 1 }}>
|
||||||
{error && (
|
{error && (
|
||||||
<Text
|
<Text
|
||||||
variant={"error"}
|
variant={"error"}
|
||||||
@@ -115,23 +115,31 @@ export function EmbedPopup(props: EmbedPopupProps) {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{/* <Flex sx={{ position: "relative", alignItems: "center" }}> */}
|
{/* <Flex sx={{ position: "relative", alignItems: "center" }}> */}
|
||||||
<Flex>
|
<Flex sx={{ mb: 1 }}>
|
||||||
<Button
|
<Button
|
||||||
variant={"dialog"}
|
variant={"dialog"}
|
||||||
sx={{
|
sx={{
|
||||||
p: 1,
|
pb: 1,
|
||||||
mr: 1,
|
mr: 1,
|
||||||
|
borderRadius: 0,
|
||||||
color: embedSource === "url" ? "primary" : "text",
|
color: embedSource === "url" ? "primary" : "text",
|
||||||
|
borderBottom: "2px solid",
|
||||||
|
borderBottomColor:
|
||||||
|
embedSource === "url" ? "primary" : "transparent",
|
||||||
}}
|
}}
|
||||||
onClick={() => setEmbedSource("url")}
|
onClick={() => setEmbedSource("url")}
|
||||||
>
|
>
|
||||||
From link
|
From URL
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={"dialog"}
|
variant={"dialog"}
|
||||||
sx={{
|
sx={{
|
||||||
p: 1,
|
pb: 1,
|
||||||
|
borderRadius: 0,
|
||||||
color: embedSource === "code" ? "primary" : "text",
|
color: embedSource === "code" ? "primary" : "text",
|
||||||
|
borderBottom: "2px solid",
|
||||||
|
borderBottomColor:
|
||||||
|
embedSource === "code" ? "primary" : "transparent",
|
||||||
}}
|
}}
|
||||||
onClick={() => setEmbedSource("code")}
|
onClick={() => setEmbedSource("code")}
|
||||||
>
|
>
|
||||||
@@ -140,31 +148,30 @@ export function EmbedPopup(props: EmbedPopupProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
{embedSource === "url" ? (
|
{embedSource === "url" ? (
|
||||||
<Input
|
<Input
|
||||||
placeholder="Embed source URL"
|
placeholder="Enter embed source URL"
|
||||||
value={src}
|
value={src}
|
||||||
autoFocus
|
autoFocus
|
||||||
onChange={(e) => setSrc(e.target.value)}
|
onChange={(e) => setSrc(e.target.value)}
|
||||||
sx={{ p: 1, mt: 1, fontSize: "body" }}
|
sx={{ mt: 1, fontSize: "body" }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Textarea
|
<Textarea
|
||||||
autoFocus
|
autoFocus
|
||||||
variant={"forms.input"}
|
variant={"forms.input"}
|
||||||
sx={{ fontSize: "subBody", fontFamily: "monospace", mt: 1 }}
|
sx={{ fontSize: "subBody", fontFamily: "monospace", mt: 1 }}
|
||||||
minHeight={100}
|
minHeight={[200, 100]}
|
||||||
onChange={(e) => setSrc(e.target.value)}
|
onChange={(e) => setSrc(e.target.value)}
|
||||||
placeholder="Paste embed code here. Only iframes are supported."
|
placeholder="Paste embed code here. Only iframes are supported."
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{embedSource === "url" ? (
|
{embedSource === "url" ? (
|
||||||
<Flex sx={{ alignItems: "center", mt: 2 }}>
|
<Flex sx={{ alignItems: "center", mt: 1 }}>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Width"
|
placeholder="Width"
|
||||||
value={width}
|
value={width}
|
||||||
sx={{
|
sx={{
|
||||||
mr: 2,
|
mr: 1,
|
||||||
p: 1,
|
|
||||||
fontSize: "body",
|
fontSize: "body",
|
||||||
}}
|
}}
|
||||||
onChange={(e) => onSizeChange(e.target.valueAsNumber)}
|
onChange={(e) => onSizeChange(e.target.valueAsNumber)}
|
||||||
@@ -173,7 +180,7 @@ export function EmbedPopup(props: EmbedPopupProps) {
|
|||||||
type="number"
|
type="number"
|
||||||
placeholder="Height"
|
placeholder="Height"
|
||||||
value={height}
|
value={height}
|
||||||
sx={{ p: 1, fontSize: "body" }}
|
sx={{ fontSize: "body" }}
|
||||||
onChange={(e) => onSizeChange(undefined, e.target.valueAsNumber)}
|
onChange={(e) => onSizeChange(undefined, e.target.valueAsNumber)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
75
packages/editor/src/toolbar/popups/image-properties.tsx
Normal file
75
packages/editor/src/toolbar/popups/image-properties.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { Button, Flex, Text } from "rebass";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { Popup } from "../components/popup";
|
||||||
|
import { Toggle } from "../../components/toggle";
|
||||||
|
import { Input, Textarea } from "@rebass/forms";
|
||||||
|
import {
|
||||||
|
ImageAlignmentOptions,
|
||||||
|
ImageSizeOptions,
|
||||||
|
} from "../../extensions/image";
|
||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
|
||||||
|
export type ImagePropertiesProps = ImageSizeOptions &
|
||||||
|
ImageAlignmentOptions & { editor: Editor };
|
||||||
|
export function ImageProperties(props: ImagePropertiesProps) {
|
||||||
|
const { height, width, float, editor } = props;
|
||||||
|
|
||||||
|
const onSizeChange = useCallback(
|
||||||
|
(newWidth?: number, newHeight?: number) => {
|
||||||
|
const size: ImageSizeOptions = newWidth
|
||||||
|
? {
|
||||||
|
width: newWidth,
|
||||||
|
height: newWidth * (height / width),
|
||||||
|
}
|
||||||
|
: newHeight
|
||||||
|
? {
|
||||||
|
width: newHeight * (width / height),
|
||||||
|
height: newHeight,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.chain().setImageSize(size).run();
|
||||||
|
},
|
||||||
|
[width, height]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex sx={{ width: 200, flexDirection: "column", p: 1 }}>
|
||||||
|
<Flex sx={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||||
|
<Text variant={"body"}>Floating?</Text>
|
||||||
|
<Toggle
|
||||||
|
checked={float}
|
||||||
|
onClick={() =>
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.setImageAlignment({ float: !float, align: "left" })
|
||||||
|
.run()
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex sx={{ alignItems: "center", mt: 2 }}>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Width"
|
||||||
|
value={width}
|
||||||
|
sx={{
|
||||||
|
mr: 2,
|
||||||
|
p: 1,
|
||||||
|
fontSize: "body",
|
||||||
|
}}
|
||||||
|
onChange={(e) => onSizeChange(e.target.valueAsNumber)}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="Height"
|
||||||
|
value={height}
|
||||||
|
sx={{ p: 1, fontSize: "body" }}
|
||||||
|
onChange={(e) => onSizeChange(undefined, e.target.valueAsNumber)}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
184
packages/editor/src/toolbar/popups/search-replace.tsx
Normal file
184
packages/editor/src/toolbar/popups/search-replace.tsx
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import { Input } from "@rebass/forms";
|
||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { Flex } from "rebass";
|
||||||
|
import { SearchStorage } from "../../extensions/search-replace";
|
||||||
|
import { ToolButton } from "../components/tool-button";
|
||||||
|
import { Editor } from "@tiptap/core";
|
||||||
|
|
||||||
|
export type SearchReplacePopupProps = { editor: Editor };
|
||||||
|
export function SearchReplacePopup(props: SearchReplacePopupProps) {
|
||||||
|
const { editor } = props;
|
||||||
|
const { selectedText } = editor.storage.searchreplace as SearchStorage;
|
||||||
|
|
||||||
|
const [matchCase, setMatchCase] = useState(false);
|
||||||
|
const [matchWholeWord, setMatchWholeWord] = useState(false);
|
||||||
|
const [enableRegex, setEnableRegex] = useState(false);
|
||||||
|
const replaceText = useRef("");
|
||||||
|
const searchInputRef = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
const search = useCallback(
|
||||||
|
(term: string) => {
|
||||||
|
editor.commands.search(term, {
|
||||||
|
matchCase,
|
||||||
|
enableRegex,
|
||||||
|
matchWholeWord,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[matchCase, enableRegex, matchWholeWord]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!searchInputRef.current) return;
|
||||||
|
search(searchInputRef.current.value);
|
||||||
|
}, [search, matchCase, matchWholeWord, enableRegex]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedText) {
|
||||||
|
if (searchInputRef.current) {
|
||||||
|
const input = searchInputRef.current;
|
||||||
|
setTimeout(() => {
|
||||||
|
input.value = selectedText;
|
||||||
|
input.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
search(selectedText);
|
||||||
|
}
|
||||||
|
}, [selectedText, search]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
// <MenuPresenter
|
||||||
|
// isOpen
|
||||||
|
// items={[]}
|
||||||
|
// onClose={() => {}}
|
||||||
|
// options={{
|
||||||
|
// type: "autocomplete",
|
||||||
|
// position: {
|
||||||
|
// target:
|
||||||
|
// document.querySelector<HTMLElement>(".editor-toolbar") || "mouse",
|
||||||
|
// isTargetAbsolute: true,
|
||||||
|
// location: "below",
|
||||||
|
// align: "end",
|
||||||
|
// },
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Popup>
|
||||||
|
<Flex sx={{ p: 1, flexDirection: "column" }}>
|
||||||
|
<Flex sx={{ alignItems: "start", flexShrink: 0 }}>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
position: "relative",
|
||||||
|
mr: 1,
|
||||||
|
width: 200,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
defaultValue={selectedText}
|
||||||
|
ref={searchInputRef}
|
||||||
|
autoFocus
|
||||||
|
sx={{ p: 1 }}
|
||||||
|
placeholder="Find"
|
||||||
|
onChange={(e) => {
|
||||||
|
search(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
mr: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ToolButton
|
||||||
|
sx={{
|
||||||
|
mr: 0,
|
||||||
|
}}
|
||||||
|
toggled={matchCase}
|
||||||
|
title="Match case"
|
||||||
|
id="matchCase"
|
||||||
|
icon="caseSensitive"
|
||||||
|
onClick={() => setMatchCase((s) => !s)}
|
||||||
|
iconSize={14}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
sx={{
|
||||||
|
mr: 0,
|
||||||
|
}}
|
||||||
|
toggled={matchWholeWord}
|
||||||
|
title="Match whole word"
|
||||||
|
id="matchWholeWord"
|
||||||
|
icon="wholeWord"
|
||||||
|
onClick={() => setMatchWholeWord((s) => !s)}
|
||||||
|
iconSize={14}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
sx={{
|
||||||
|
mr: 0,
|
||||||
|
}}
|
||||||
|
toggled={enableRegex}
|
||||||
|
title="Enable regex"
|
||||||
|
id="enableRegex"
|
||||||
|
icon="regex"
|
||||||
|
onClick={() => setEnableRegex((s) => !s)}
|
||||||
|
iconSize={14}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<ToolButton
|
||||||
|
toggled={false}
|
||||||
|
title="Previous match"
|
||||||
|
id="previousMatch"
|
||||||
|
icon="previousMatch"
|
||||||
|
onClick={() => editor.commands.moveToPreviousResult()}
|
||||||
|
sx={{ mr: 0 }}
|
||||||
|
iconSize={16}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
toggled={false}
|
||||||
|
title="Next match"
|
||||||
|
id="nextMatch"
|
||||||
|
icon="nextMatch"
|
||||||
|
onClick={() => editor.commands.moveToNextResult()}
|
||||||
|
sx={{ mr: 0 }}
|
||||||
|
iconSize={16}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
toggled={false}
|
||||||
|
title="Close"
|
||||||
|
id="close"
|
||||||
|
icon="close"
|
||||||
|
onClick={() => editor.chain().focus().endSearch().run()}
|
||||||
|
iconSize={16}
|
||||||
|
sx={{ mr: 0 }}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex sx={{ alignItems: "start", flexShrink: 0, mt: 1 }}>
|
||||||
|
<Input
|
||||||
|
sx={{ p: 1, width: 200, mr: 1 }}
|
||||||
|
placeholder="Replace"
|
||||||
|
onChange={(e) => (replaceText.current = e.target.value)}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
toggled={false}
|
||||||
|
title="Replace"
|
||||||
|
id="replace"
|
||||||
|
icon="replaceOne"
|
||||||
|
onClick={() => editor.commands.replace(replaceText.current)}
|
||||||
|
sx={{ mr: 0 }}
|
||||||
|
iconSize={16}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
toggled={false}
|
||||||
|
title="Replace all"
|
||||||
|
id="replaceAll"
|
||||||
|
icon="replaceAll"
|
||||||
|
onClick={() => editor.commands.replaceAll(replaceText.current)}
|
||||||
|
sx={{ mr: 0 }}
|
||||||
|
iconSize={16}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
// </Popup>
|
||||||
|
// </MenuPresenter>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import { Box, Flex, Text } from "rebass";
|
import { Box, Button, Flex, Text } from "rebass";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Popup } from "../components/popup";
|
import { Popup } from "../components/popup";
|
||||||
|
import { Input } from "@rebass/forms";
|
||||||
|
import { Icon } from "../components/icon";
|
||||||
|
import { Icons } from "../icons";
|
||||||
|
|
||||||
const MAX_COLUMNS = 20;
|
const MAX_COLUMNS = 20;
|
||||||
const MAX_ROWS = 20;
|
const MAX_ROWS = 20;
|
||||||
@@ -11,10 +14,12 @@ type CellLocation = { column: number; row: number };
|
|||||||
type TableSize = { columns: number; rows: number };
|
type TableSize = { columns: number; rows: number };
|
||||||
|
|
||||||
export type TablePopupProps = {
|
export type TablePopupProps = {
|
||||||
onClose: (size: TableSize) => void;
|
onInsertTable: (size: TableSize) => void;
|
||||||
|
cellSize?: number;
|
||||||
|
autoExpand?: boolean;
|
||||||
};
|
};
|
||||||
export function TablePopup(props: TablePopupProps) {
|
export function TablePopup(props: TablePopupProps) {
|
||||||
const { onClose } = props;
|
const { onInsertTable, cellSize, autoExpand } = props;
|
||||||
const [cellLocation, setCellLocation] = useState<CellLocation>({
|
const [cellLocation, setCellLocation] = useState<CellLocation>({
|
||||||
column: 0,
|
column: 0,
|
||||||
row: 0,
|
row: 0,
|
||||||
@@ -25,6 +30,7 @@ export function TablePopup(props: TablePopupProps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!autoExpand) return;
|
||||||
setTableSize((old) => {
|
setTableSize((old) => {
|
||||||
const { columns, rows } = old;
|
const { columns, rows } = old;
|
||||||
const { column, row } = cellLocation;
|
const { column, row } = cellLocation;
|
||||||
@@ -43,50 +49,109 @@ export function TablePopup(props: TablePopupProps) {
|
|||||||
: Math.min(old.rows + rowFactor, MAX_ROWS),
|
: Math.min(old.rows + rowFactor, MAX_ROWS),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [cellLocation]);
|
}, [cellLocation, autoExpand]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex sx={{ p: 1, flexDirection: "column", alignItems: "center" }}>
|
<Popup
|
||||||
<Box
|
title="Insert table"
|
||||||
sx={{
|
action={{
|
||||||
display: "grid",
|
icon: "check",
|
||||||
gridTemplateColumns: "1fr ".repeat(tableSize.columns),
|
onClick: () => {
|
||||||
gap: "3px",
|
onInsertTable({
|
||||||
bg: "background",
|
columns: cellLocation.column,
|
||||||
}}
|
rows: cellLocation.row,
|
||||||
>
|
});
|
||||||
{Array(tableSize.columns * tableSize.rows)
|
},
|
||||||
.fill(0)
|
}}
|
||||||
.map((_, index) => (
|
>
|
||||||
<Box
|
<Flex sx={{ p: 1, flexDirection: "column", alignItems: "center" }}>
|
||||||
width={15}
|
<Box
|
||||||
height={15}
|
sx={{
|
||||||
sx={{
|
display: "grid",
|
||||||
border: "1px solid var(--disabled)",
|
gridTemplateColumns: `repeat(${tableSize.columns}, minmax(${
|
||||||
borderRadius: "2px",
|
cellSize || 15
|
||||||
bg: isCellHighlighted(index, cellLocation, tableSize)
|
}px, 1fr))`, // "1fr ".repeat(tableSize.columns),
|
||||||
? "disabled"
|
gap: "3px",
|
||||||
: "transparent",
|
bg: "background",
|
||||||
":hover": {
|
width: "100%",
|
||||||
bg: "disabled",
|
}}
|
||||||
},
|
onTouchMove={(e) => {
|
||||||
}}
|
const touch = e.touches.item(0);
|
||||||
onMouseEnter={() => {
|
const element = document.elementFromPoint(
|
||||||
setCellLocation(getCellLocation(index, tableSize));
|
touch.pageX,
|
||||||
}}
|
touch.pageY
|
||||||
onClick={() => {
|
) as HTMLElement;
|
||||||
onClose({
|
if (!element) return;
|
||||||
columns: cellLocation.column,
|
const index = element.dataset.index;
|
||||||
rows: cellLocation.row,
|
if (!index) return;
|
||||||
});
|
setCellLocation(getCellLocation(parseInt(index), tableSize));
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
))}
|
{Array(tableSize.columns * tableSize.rows)
|
||||||
</Box>
|
.fill(0)
|
||||||
<Text variant={"body"} sx={{ mt: 1 }}>
|
.map((_, index) => (
|
||||||
{cellLocation.column}x{cellLocation.row}
|
<Box
|
||||||
</Text>
|
data-index={index}
|
||||||
</Flex>
|
height={cellSize || 15}
|
||||||
|
sx={{
|
||||||
|
border: "1px solid var(--disabled)",
|
||||||
|
borderRadius: "2px",
|
||||||
|
bg: isCellHighlighted(index, cellLocation, tableSize)
|
||||||
|
? "disabled"
|
||||||
|
: "transparent",
|
||||||
|
}}
|
||||||
|
onTouchStart={() => {
|
||||||
|
setCellLocation(getCellLocation(index, tableSize));
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
setCellLocation(getCellLocation(index, tableSize));
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
onInsertTable({
|
||||||
|
columns: cellLocation.column,
|
||||||
|
rows: cellLocation.row,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
display: ["flex", "none"],
|
||||||
|
my: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder={`${cellLocation.column} columns`}
|
||||||
|
sx={{ mr: 1 }}
|
||||||
|
type="number"
|
||||||
|
value={cellLocation.column}
|
||||||
|
onChange={(e) => {
|
||||||
|
setCellLocation((l) => ({
|
||||||
|
...l,
|
||||||
|
column: e.target.valueAsNumber || 0,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder={`${cellLocation.row} rows`}
|
||||||
|
type="number"
|
||||||
|
value={cellLocation.row}
|
||||||
|
onChange={(e) => {
|
||||||
|
setCellLocation((l) => ({
|
||||||
|
...l,
|
||||||
|
row: e.target.valueAsNumber || 0,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Text variant={"body"} sx={{ mt: 1, display: ["none", "block"] }}>
|
||||||
|
{cellLocation.column}x{cellLocation.row}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Popup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
31
packages/editor/src/toolbar/stores/toolbar-store.ts
Normal file
31
packages/editor/src/toolbar/stores/toolbar-store.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import create from "zustand";
|
||||||
|
|
||||||
|
export type ToolbarLocation = "top" | "bottom";
|
||||||
|
|
||||||
|
interface ToolbarState {
|
||||||
|
isMobile: boolean;
|
||||||
|
setIsMobile: (isMobile: boolean) => void;
|
||||||
|
toolbarLocation: ToolbarLocation;
|
||||||
|
setToolbarLocation: (location: ToolbarLocation) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useToolbarStore = create<ToolbarState>((set) => ({
|
||||||
|
isMobile: false,
|
||||||
|
setIsMobile: (isMobile) =>
|
||||||
|
set((state) => {
|
||||||
|
state.isMobile = isMobile;
|
||||||
|
}),
|
||||||
|
toolbarLocation: "top",
|
||||||
|
setToolbarLocation: (location) =>
|
||||||
|
set((state) => {
|
||||||
|
state.toolbarLocation = location;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export function useToolbarLocation() {
|
||||||
|
return useToolbarStore((store) => store.toolbarLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useIsMobile() {
|
||||||
|
return useToolbarStore((store) => store.isMobile);
|
||||||
|
}
|
||||||
@@ -8,14 +8,16 @@ import { EditorFloatingMenus } from "./floating-menus";
|
|||||||
import { getToolDefinition } from "./tool-definitions";
|
import { getToolDefinition } from "./tool-definitions";
|
||||||
import { Dropdown } from "./components/dropdown";
|
import { Dropdown } from "./components/dropdown";
|
||||||
import { ToolButton } from "./components/tool-button";
|
import { ToolButton } from "./components/tool-button";
|
||||||
import { useContext, useRef, useState } from "react";
|
import { useContext, useEffect, useRef, useState } from "react";
|
||||||
import { MenuPresenter } from "../components/menu";
|
import { MenuPresenter } from "../components/menu";
|
||||||
import { Popup } from "./components/popup";
|
import { Popup } from "./components/popup";
|
||||||
|
import { ToolbarContext, useToolbarContext } from "./hooks/useToolbarContext";
|
||||||
import {
|
import {
|
||||||
ToolbarContext,
|
|
||||||
ToolbarLocation,
|
ToolbarLocation,
|
||||||
useToolbarContext,
|
useToolbarLocation,
|
||||||
} from "./hooks/useToolbarContext";
|
useToolbarStore,
|
||||||
|
} from "./stores/toolbar-store";
|
||||||
|
|
||||||
// type Colors = {
|
// type Colors = {
|
||||||
// text: string;
|
// text: string;
|
||||||
// background: string;
|
// background: string;
|
||||||
@@ -31,11 +33,18 @@ type ToolbarDefinition = ToolbarGroupDefinition[];
|
|||||||
type ToolbarProps = ThemeConfig & {
|
type ToolbarProps = ThemeConfig & {
|
||||||
editor: Editor | null;
|
editor: Editor | null;
|
||||||
location: ToolbarLocation;
|
location: ToolbarLocation;
|
||||||
|
isMobile?: boolean;
|
||||||
};
|
};
|
||||||
export function Toolbar(props: ToolbarProps) {
|
export function Toolbar(props: ToolbarProps) {
|
||||||
const { editor, theme, accent, scale, location } = props;
|
const { editor, theme, accent, scale, location, isMobile } = props;
|
||||||
const themeProperties = useTheme({ accent, theme, scale });
|
const themeProperties = useTheme({ accent, theme, scale });
|
||||||
const [currentPopup, setCurrentPopup] = useState<string | undefined>();
|
const [currentPopup, setCurrentPopup] = useState<string | undefined>();
|
||||||
|
const { setIsMobile, setToolbarLocation } = useToolbarStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsMobile(isMobile || false);
|
||||||
|
setToolbarLocation(location);
|
||||||
|
}, [isMobile, location]);
|
||||||
|
|
||||||
const tools: ToolbarDefinition = [
|
const tools: ToolbarDefinition = [
|
||||||
["insertBlock"],
|
["insertBlock"],
|
||||||
@@ -63,7 +72,10 @@ export function Toolbar(props: ToolbarProps) {
|
|||||||
return (
|
return (
|
||||||
<ThemeProvider theme={themeProperties}>
|
<ThemeProvider theme={themeProperties}>
|
||||||
<ToolbarContext.Provider
|
<ToolbarContext.Provider
|
||||||
value={{ setCurrentPopup, currentPopup, toolbarLocation: location }}
|
value={{
|
||||||
|
setCurrentPopup,
|
||||||
|
currentPopup,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
className="editor-toolbar"
|
className="editor-toolbar"
|
||||||
@@ -112,7 +124,7 @@ function ToolbarGroup(props: ToolbarGroupProps) {
|
|||||||
} else {
|
} else {
|
||||||
const Component = findToolById(toolId);
|
const Component = findToolById(toolId);
|
||||||
const toolDefinition = getToolDefinition(toolId);
|
const toolDefinition = getToolDefinition(toolId);
|
||||||
return <Component editor={editor} id={toolId} {...toolDefinition} />;
|
return <Component editor={editor} {...toolDefinition} />;
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -126,8 +138,9 @@ type MoreToolsProps = {
|
|||||||
};
|
};
|
||||||
function MoreTools(props: MoreToolsProps) {
|
function MoreTools(props: MoreToolsProps) {
|
||||||
const { popupId } = props;
|
const { popupId } = props;
|
||||||
const { currentPopup, setCurrentPopup, toolbarLocation } =
|
const { currentPopup, setCurrentPopup } = useToolbarContext();
|
||||||
useToolbarContext();
|
const toolbarLocation = useToolbarLocation();
|
||||||
|
|
||||||
const buttonRef = useRef<HTMLButtonElement | null>();
|
const buttonRef = useRef<HTMLButtonElement | null>();
|
||||||
|
|
||||||
const show = popupId === currentPopup;
|
const show = popupId === currentPopup;
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import { Editor } from "@tiptap/core";
|
|||||||
import { ToolButton } from "../components/tool-button";
|
import { ToolButton } from "../components/tool-button";
|
||||||
import { ToolId } from ".";
|
import { ToolId } from ".";
|
||||||
import { IconNames, Icons } from "../icons";
|
import { IconNames, Icons } from "../icons";
|
||||||
import { MenuPresenter } from "../../components/menu/menu";
|
import {
|
||||||
|
ActionSheetPresenter,
|
||||||
|
MenuPresenter,
|
||||||
|
} from "../../components/menu/menu";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { Dropdown } from "../components/dropdown";
|
import { Dropdown } from "../components/dropdown";
|
||||||
import { Icon } from "../components/icon";
|
import { Icon } from "../components/icon";
|
||||||
@@ -12,12 +15,12 @@ import { Popup } from "../components/popup";
|
|||||||
import { EmbedPopup } from "../popups/embed-popup";
|
import { EmbedPopup } from "../popups/embed-popup";
|
||||||
import { TablePopup } from "../popups/table-popup";
|
import { TablePopup } from "../popups/table-popup";
|
||||||
import { MenuItem } from "../../components/menu/types";
|
import { MenuItem } from "../../components/menu/types";
|
||||||
import { useToolbarContext } from "../hooks/useToolbarContext";
|
import { useToolbarLocation } from "../stores/toolbar-store";
|
||||||
|
|
||||||
export function InsertBlock(props: ToolProps) {
|
export function InsertBlock(props: ToolProps) {
|
||||||
const buttonRef = useRef<HTMLButtonElement | null>();
|
const buttonRef = useRef<HTMLButtonElement | null>();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { toolbarLocation } = useToolbarContext();
|
const toolbarLocation = useToolbarLocation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -40,7 +43,7 @@ export function InsertBlock(props: ToolProps) {
|
|||||||
>
|
>
|
||||||
<Icon path={Icons.plus} size={18} color={"primary"} />
|
<Icon path={Icons.plus} size={18} color={"primary"} />
|
||||||
</Button>
|
</Button>
|
||||||
<MenuPresenter
|
{/* <MenuPresenter
|
||||||
options={{
|
options={{
|
||||||
type: "menu",
|
type: "menu",
|
||||||
position: {
|
position: {
|
||||||
@@ -62,6 +65,21 @@ export function InsertBlock(props: ToolProps) {
|
|||||||
table(editor),
|
table(editor),
|
||||||
]}
|
]}
|
||||||
onClose={() => setIsOpen(false)}
|
onClose={() => setIsOpen(false)}
|
||||||
|
/> */}
|
||||||
|
<ActionSheetPresenter
|
||||||
|
title="Choose a block to insert"
|
||||||
|
isOpen={isOpen}
|
||||||
|
items={[
|
||||||
|
tasklist(editor),
|
||||||
|
horizontalRule(editor),
|
||||||
|
codeblock(editor),
|
||||||
|
blockquote(editor),
|
||||||
|
imageActionSheet(editor),
|
||||||
|
attachment(editor),
|
||||||
|
embedActionSheet(editor),
|
||||||
|
tableActionSheet(editor),
|
||||||
|
]}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -117,6 +135,44 @@ const image = (editor: Editor | null): MenuItem => ({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const imageActionSheet = (editor: Editor | null): MenuItem => ({
|
||||||
|
key: "image",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Image",
|
||||||
|
icon: "image",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "imageOptions",
|
||||||
|
type: "menuitem",
|
||||||
|
component: function ({ onClick }) {
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
return (
|
||||||
|
<ActionSheetPresenter
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: "upload-from-disk",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Upload from disk",
|
||||||
|
icon: "upload",
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "upload-from-url",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Attach from URL",
|
||||||
|
icon: "link",
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const embed = (editor: Editor | null): MenuItem => ({
|
const embed = (editor: Editor | null): MenuItem => ({
|
||||||
key: "embed",
|
key: "embed",
|
||||||
type: "menuitem",
|
type: "menuitem",
|
||||||
@@ -135,7 +191,7 @@ const table = (editor: Editor | null): MenuItem => ({
|
|||||||
type: "menuitem",
|
type: "menuitem",
|
||||||
component: (props) => (
|
component: (props) => (
|
||||||
<TablePopup
|
<TablePopup
|
||||||
onClose={(size) => {
|
onInsertTable={(size) => {
|
||||||
editor
|
editor
|
||||||
?.chain()
|
?.chain()
|
||||||
.focus()
|
.focus()
|
||||||
@@ -152,6 +208,82 @@ const table = (editor: Editor | null): MenuItem => ({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const embedActionSheet = (editor: Editor | null): MenuItem => ({
|
||||||
|
key: "embed",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Embed",
|
||||||
|
icon: "embed",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "table-size-selector",
|
||||||
|
type: "menuitem",
|
||||||
|
component: function ({ onClick }) {
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
return (
|
||||||
|
<ActionSheetPresenter
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
items={[]}
|
||||||
|
>
|
||||||
|
<EmbedPopup
|
||||||
|
title="Insert embed"
|
||||||
|
icon="check"
|
||||||
|
onClose={(embed) => {
|
||||||
|
editor?.chain().insertEmbed(embed).run();
|
||||||
|
setIsOpen(false);
|
||||||
|
onClick?.();
|
||||||
|
}}
|
||||||
|
// embed={props}
|
||||||
|
// onSourceChanged={(src) => {}}
|
||||||
|
// onSizeChanged={(size) => editor.commands.setEmbedSize(size)}
|
||||||
|
/>
|
||||||
|
</ActionSheetPresenter>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableActionSheet = (editor: Editor | null): MenuItem => ({
|
||||||
|
key: "table",
|
||||||
|
type: "menuitem",
|
||||||
|
title: "Table",
|
||||||
|
icon: "table",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "table-size-selector",
|
||||||
|
type: "menuitem",
|
||||||
|
component: function ({ onClick }) {
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
return (
|
||||||
|
<ActionSheetPresenter
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
items={[]}
|
||||||
|
>
|
||||||
|
<TablePopup
|
||||||
|
cellSize={30}
|
||||||
|
autoExpand={false}
|
||||||
|
onInsertTable={(size) => {
|
||||||
|
editor
|
||||||
|
?.chain()
|
||||||
|
.focus()
|
||||||
|
.insertTable({
|
||||||
|
rows: size.rows,
|
||||||
|
cols: size.columns,
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
setIsOpen(false);
|
||||||
|
onClick?.();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ActionSheetPresenter>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const attachment = (editor: Editor | null): MenuItem => ({
|
const attachment = (editor: Editor | null): MenuItem => ({
|
||||||
key: "attachment",
|
key: "attachment",
|
||||||
type: "menuitem",
|
type: "menuitem",
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { Dropdown } from "../components/dropdown";
|
|||||||
import { ToolId } from ".";
|
import { ToolId } from ".";
|
||||||
import { MenuItem } from "../../components/menu/types";
|
import { MenuItem } from "../../components/menu/types";
|
||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
|
import { Box, Button, Flex } from "rebass";
|
||||||
|
import { Slider } from "@rebass/forms";
|
||||||
|
|
||||||
const defaultFontSizes = [
|
const defaultFontSizes = [
|
||||||
12, 14, 16, 18, 20, 24, 28, 32, 36, 42, 48, 60, 72, 100,
|
8, 12, 14, 16, 18, 20, 24, 28, 32, 36, 42, 48, 60, 72, 100,
|
||||||
];
|
];
|
||||||
export function FontSize(props: ToolProps) {
|
export function FontSize(props: ToolProps) {
|
||||||
const { editor } = props;
|
const { editor } = props;
|
||||||
@@ -17,13 +19,28 @@ export function FontSize(props: ToolProps) {
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
selectedItem={`${currentFontSize}px`}
|
selectedItem={`${currentFontSize}px`}
|
||||||
items={defaultFontSizes.map((size) => ({
|
items={[
|
||||||
key: `${size}px`,
|
{
|
||||||
type: "menuitem",
|
key: "font-sizes",
|
||||||
title: `${size}px`,
|
type: "menuitem",
|
||||||
isChecked: size === currentFontSize,
|
component: () => (
|
||||||
onClick: () => editor.chain().focus().setFontSize(`${size}px`).run(),
|
<Box
|
||||||
}))}
|
sx={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)" }}
|
||||||
|
>
|
||||||
|
{defaultFontSizes.map((size) => (
|
||||||
|
<Button variant={"menuitem"}>{size}px</Button>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
// items={defaultFontSizes.map((size) => ({
|
||||||
|
// key: `${size}px`,
|
||||||
|
// type: "menuitem",
|
||||||
|
// title: `${size}px`,
|
||||||
|
// isChecked: size === currentFontSize,
|
||||||
|
// onClick: () => editor.chain().focus().setFontSize(`${size}px`).run(),
|
||||||
|
// }))}
|
||||||
menuWidth={100}
|
menuWidth={100}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ type InlineToolProps = ToolProps & {
|
|||||||
onClick: (editor: Editor) => boolean;
|
onClick: (editor: Editor) => boolean;
|
||||||
};
|
};
|
||||||
function InlineTool(props: InlineToolProps) {
|
function InlineTool(props: InlineToolProps) {
|
||||||
const { editor, title, id, icon, isToggled, onClick } = props;
|
const { editor, title, icon, isToggled, onClick } = props;
|
||||||
return (
|
return (
|
||||||
<ToolButton
|
<ToolButton
|
||||||
title={title}
|
title={title}
|
||||||
id={id}
|
id={icon}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
onClick={() => onClick(editor)}
|
onClick={() => onClick(editor)}
|
||||||
toggled={isToggled(editor)}
|
toggled={isToggled(editor)}
|
||||||
@@ -107,7 +107,7 @@ export function ClearFormatting(props: ToolProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Link(props: ToolProps) {
|
export function Link(props: ToolProps) {
|
||||||
const { editor, id, title, icon } = props;
|
const { editor, title, icon } = props;
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
const targetRef = useRef<HTMLElement>();
|
const targetRef = useRef<HTMLElement>();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@@ -119,9 +119,9 @@ export function Link(props: ToolProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
id={icon}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
title={title}
|
title={title}
|
||||||
id={id}
|
|
||||||
icon={icon}
|
icon={icon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isEditing) setHref(currentUrl);
|
if (isEditing) setHref(currentUrl);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { IconNames } from "./icons";
|
import { IconNames } from "./icons";
|
||||||
import { ToolId } from "./tools";
|
|
||||||
|
|
||||||
export type ToolProps = ToolDefinition & {
|
export type ToolProps = ToolDefinition & {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
id: ToolId;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToolDefinition = {
|
export type ToolDefinition = {
|
||||||
|
|||||||
5
packages/editor/src/toolbar/utils/dom.ts
Normal file
5
packages/editor/src/toolbar/utils/dom.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export function getToolbarElement() {
|
||||||
|
return (
|
||||||
|
(document.querySelector(".editor-toolbar") as HTMLElement) || undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -80,12 +80,19 @@ What's next:
|
|||||||
|
|
||||||
## Optimize toolbar & editor UI for mobile
|
## Optimize toolbar & editor UI for mobile
|
||||||
|
|
||||||
1. Refactor tools to be more easily configurable
|
1. Refactor tools to be more easily configurable (partially done)
|
||||||
2. Implement sub groups in toolbar
|
2. Implement sub groups in toolbar (done)
|
||||||
3. Move all popups to /popups directory
|
3. Move all popups to /popups directory (partially done)
|
||||||
4. Implement mobile positioning logic in menu/popup presenter
|
4. Implement mobile positioning logic in menu/popup presenter (done)
|
||||||
5. Add support for repositioning toolbar (top/bottom)
|
5. Add support for repositioning toolbar (top/bottom) (done)
|
||||||
6. Move all popups to be shown as bottom sheets on mobile
|
6. Move all popups to be shown as bottom sheets on mobile (partially done)
|
||||||
|
7. Figure out how to make interactive widgets selectable in editor (e.g. iframe & table)
|
||||||
|
8. Create popup header for use in action sheet (done)
|
||||||
|
1. Header contains title & action(s)
|
||||||
|
9. Implement logic to open inline popups (image/cell properties) as an action sheet (done)
|
||||||
|
10. Open search replace popup as action sheet (done)
|
||||||
|
11. Improve font size menu (done)
|
||||||
|
12. Move table context toolbars to bottom (done)
|
||||||
|
|
||||||
### Refactor tools
|
### Refactor tools
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user