+ | undefined
+ ) =>
+ (event: any) => {
+ if (readOnly) {
+ return;
+ }
- return (
-
-
- {innerValue}
-
- {children}
-
- );
- }
-);
+ const text = event.currentTarget.textContent || "";
+
+ if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) {
+ event?.preventDefault();
+ return;
+ }
+
+ if (text !== lastValue.current) {
+ lastValue.current = text;
+ onChange?.(text);
+ }
+
+ callback?.(event);
+ };
+
+ // This is to account for being within a React.Suspense boundary, in this
+ // case the component may be rendered with display: none. React 18 may solve
+ // this in the future by delaying useEffect hooks:
+ // https://github.com/facebook/react/issues/14536#issuecomment-861980492
+ const isVisible = useOnScreen(contentRef);
+
+ React.useEffect(() => {
+ if (autoFocus && isVisible && !disabled && !readOnly) {
+ contentRef.current?.focus();
+ }
+ }, [autoFocus, disabled, isVisible, readOnly, contentRef]);
+
+ React.useEffect(() => {
+ if (contentRef.current && value !== contentRef.current.textContent) {
+ setInnerValue(value);
+ }
+ }, [value, contentRef]);
+
+ // Ensure only plain text can be pasted into input when pasting from another
+ // rich text source. Note: If `onPaste` prop is passed then it takes
+ // priority over this behavior.
+ const handlePaste = React.useCallback(
+ (event: React.ClipboardEvent) => {
+ event.preventDefault();
+ const text = event.clipboardData.getData("text/plain");
+ window.document.execCommand("insertText", false, text);
+ },
+ []
+ );
+
+ return (
+
+
+ {innerValue}
+
+ {children}
+
+ );
+});
function placeCaret(element: HTMLElement, atStart: boolean) {
if (
diff --git a/app/components/ContextMenu/MenuItem.tsx b/app/components/ContextMenu/MenuItem.tsx
index dace2fba3..858cc43b3 100644
--- a/app/components/ContextMenu/MenuItem.tsx
+++ b/app/components/ContextMenu/MenuItem.tsx
@@ -22,6 +22,7 @@ type Props = {
level?: number;
icon?: React.ReactElement;
children?: React.ReactNode;
+ ref?: React.LegacyRef | undefined;
};
const MenuItem = (
@@ -80,7 +81,7 @@ const MenuItem = (
);
},
- [active, as, hide, icon, onClick, ref, selected]
+ [active, as, hide, icon, onClick, ref, children, selected]
);
return (
diff --git a/app/components/ContextMenu/Template.tsx b/app/components/ContextMenu/Template.tsx
index 4ebebaada..c66a24088 100644
--- a/app/components/ContextMenu/Template.tsx
+++ b/app/components/ContextMenu/Template.tsx
@@ -44,37 +44,35 @@ type SubMenuProps = MenuStateReturn & {
title: React.ReactNode;
};
-const SubMenu = React.forwardRef(
- (
- { templateItems, title, parentMenuState, ...rest }: SubMenuProps,
- ref: React.LegacyRef
- ) => {
- const { t } = useTranslation();
- const theme = useTheme();
- const menu = useMenuState();
+const SubMenu = React.forwardRef(function _Template(
+ { templateItems, title, parentMenuState, ...rest }: SubMenuProps,
+ ref: React.LegacyRef
+) {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const menu = useMenuState();
- return (
- <>
-
- {(props) => (
-
- {title}
-
- )}
-
-
-
-
-
- >
- );
- }
-);
+ return (
+ <>
+
+ {(props) => (
+
+ {title}
+
+ )}
+
+
+
+
+
+ >
+ );
+});
export function filterTemplateItems(items: TMenuItem[]): TMenuItem[] {
return items
diff --git a/app/components/ContextMenu/index.tsx b/app/components/ContextMenu/index.tsx
index 64547c873..6c24ef1bb 100644
--- a/app/components/ContextMenu/index.tsx
+++ b/app/components/ContextMenu/index.tsx
@@ -46,6 +46,7 @@ type Props = MenuStateReturn & {
onClose?: () => void;
/** Called when the context menu is clicked. */
onClick?: (ev: React.MouseEvent) => void;
+ children?: React.ReactNode;
};
const ContextMenu: React.FC = ({
@@ -54,7 +55,7 @@ const ContextMenu: React.FC = ({
onClose,
parentMenuState,
...rest
-}) => {
+}: Props) => {
const previousVisible = usePrevious(rest.visible);
const maxHeight = useMenuHeight({
visible: rest.visible,
diff --git a/app/components/DocumentBreadcrumb.tsx b/app/components/DocumentBreadcrumb.tsx
index 09f94b90b..e260cc886 100644
--- a/app/components/DocumentBreadcrumb.tsx
+++ b/app/components/DocumentBreadcrumb.tsx
@@ -17,6 +17,7 @@ import {
} from "~/utils/routeHelpers";
type Props = {
+ children?: React.ReactNode;
document: Document;
onlyText?: boolean;
};
@@ -58,7 +59,7 @@ const DocumentBreadcrumb: React.FC = ({
document,
children,
onlyText,
-}) => {
+}: Props) => {
const { collections } = useStores();
const { t } = useTranslation();
const category = useCategory(document);
@@ -129,7 +130,11 @@ const DocumentBreadcrumb: React.FC = ({
);
}
- return ;
+ return (
+
+ {children}
+
+ );
};
const SmallSlash = styled(GoToIcon)`
diff --git a/app/components/DocumentExplorer.tsx b/app/components/DocumentExplorer.tsx
index ae18b31c8..54a4377e9 100644
--- a/app/components/DocumentExplorer.tsx
+++ b/app/components/DocumentExplorer.tsx
@@ -335,16 +335,21 @@ function DocumentExplorer({ onSubmit, onSelect, items }: Props) {
const innerElementType = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
- >(({ style, ...rest }, ref) => (
-
- ));
+ >(function innerElementType(
+ { style, ...rest }: React.HTMLAttributes,
+ ref
+ ) {
+ return (
+
+ );
+ });
return (
diff --git a/app/components/DocumentMeta.tsx b/app/components/DocumentMeta.tsx
index 98c8e6f2a..03bd2a473 100644
--- a/app/components/DocumentMeta.tsx
+++ b/app/components/DocumentMeta.tsx
@@ -15,6 +15,7 @@ import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
type Props = {
+ children?: React.ReactNode;
showCollection?: boolean;
showPublished?: boolean;
showLastViewed?: boolean;
@@ -36,7 +37,7 @@ const DocumentMeta: React.FC = ({
replace,
to,
...rest
-}) => {
+}: Props) => {
const { t } = useTranslation();
const { collections } = useStores();
const user = useCurrentUser();
diff --git a/app/components/EventBoundary.tsx b/app/components/EventBoundary.tsx
index 5eb0c6722..83ec19dea 100644
--- a/app/components/EventBoundary.tsx
+++ b/app/components/EventBoundary.tsx
@@ -1,10 +1,11 @@
import * as React from "react";
type Props = {
+ children?: React.ReactNode;
className?: string;
};
-const EventBoundary: React.FC = ({ children, className }) => {
+const EventBoundary: React.FC = ({ children, className }: Props) => {
const handleClick = React.useCallback((event: React.SyntheticEvent) => {
event.preventDefault();
event.stopPropagation();
diff --git a/app/components/EventListItem.tsx b/app/components/EventListItem.tsx
index b82354e0a..c8cdeb740 100644
--- a/app/components/EventListItem.tsx
+++ b/app/components/EventListItem.tsx
@@ -160,15 +160,16 @@ const EventListItem = ({ event, latest, document, ...rest }: Props) => {
);
};
-const BaseItem = React.forwardRef(
- ({ to, ...rest }: ItemProps, ref?: React.Ref) => {
- if (to) {
- return ;
- }
-
- return ;
+const BaseItem = React.forwardRef(function _BaseItem(
+ { to, ...rest }: ItemProps,
+ ref?: React.Ref
+) {
+ if (to) {
+ return ;
}
-);
+
+ return ;
+});
const Subtitle = styled.span`
svg {
diff --git a/app/components/ExportDialog.tsx b/app/components/ExportDialog.tsx
index fc51b9243..231b36edf 100644
--- a/app/components/ExportDialog.tsx
+++ b/app/components/ExportDialog.tsx
@@ -99,7 +99,7 @@ function ExportDialog({ collection, onSubmit }: Props) {
)}
{items.map((item) => (
-