fix: folder collection path (#549)
This commit is contained in:
committed by
GitHub
parent
93915dac35
commit
8f7237ab7c
@ -238,7 +238,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
|
||||
|
||||
const handleOpenMediaLibrary = useMediaInsert(
|
||||
internalValue,
|
||||
{ field, controlID },
|
||||
{ collection, field, controlID, forImage },
|
||||
handleOnChange,
|
||||
);
|
||||
|
||||
@ -312,10 +312,11 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
|
||||
replaceIndex: index,
|
||||
allowMultiple: false,
|
||||
config,
|
||||
collection,
|
||||
field,
|
||||
});
|
||||
},
|
||||
[config, controlID, field, openMediaLibrary, internalValue],
|
||||
[openMediaLibrary, controlID, internalValue, config, collection, field],
|
||||
);
|
||||
|
||||
// TODO Readd when multiple uploads is supported
|
||||
|
@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles';
|
||||
import partial from 'lodash/partial';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import EditorControl from '@staticcms/core/components/Editor/EditorControlPane/EditorControl';
|
||||
import EditorControl from '@staticcms/core/components/editor/EditorControlPane/EditorControl';
|
||||
import ListItemTopBar from '@staticcms/core/components/UI/ListItemTopBar';
|
||||
import Outline from '@staticcms/core/components/UI/Outline';
|
||||
import { colors } from '@staticcms/core/components/UI/styles';
|
||||
|
@ -52,7 +52,7 @@ const ListControlWrapper = createControlWrapper({
|
||||
path: 'list',
|
||||
});
|
||||
|
||||
jest.mock('@staticcms/core/components/Editor/EditorControlPane/EditorControl', () => {
|
||||
jest.mock('@staticcms/core/components/editor/EditorControlPane/EditorControl', () => {
|
||||
return jest.fn(props => {
|
||||
const { parentPath, field, value } = props;
|
||||
return (
|
||||
|
@ -7,6 +7,7 @@ import { getShortcodes } from '../../lib/registry';
|
||||
import withShortcodeMdxComponent from './mdx/withShortcodeMdxComponent';
|
||||
import useMdx from './plate/hooks/useMdx';
|
||||
import { processShortcodeConfigToMdx } from './plate/serialization/slate/processShortcodeConfig';
|
||||
import { withMdxImage } from '@staticcms/core/components/common/image/Image';
|
||||
|
||||
import type { MarkdownField, WidgetPreviewProps } from '@staticcms/core/interface';
|
||||
import type { FC } from 'react';
|
||||
@ -26,13 +27,14 @@ function FallbackComponent({ error }: FallbackComponentProps) {
|
||||
}
|
||||
|
||||
const MarkdownPreview: FC<WidgetPreviewProps<string, MarkdownField>> = previewProps => {
|
||||
const { value } = previewProps;
|
||||
const { value, collection, field } = previewProps;
|
||||
|
||||
const components = useMemo(
|
||||
() => ({
|
||||
Shortcode: withShortcodeMdxComponent({ previewProps }),
|
||||
img: withMdxImage({ collection, field }),
|
||||
}),
|
||||
[previewProps],
|
||||
[collection, field, previewProps],
|
||||
);
|
||||
|
||||
const [state, setValue] = useMdx(value ?? '');
|
||||
|
@ -5,11 +5,10 @@ import Popper from '@mui/material/Popper';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useFocused } from 'slate-react';
|
||||
|
||||
import useDebounce from '@staticcms/core/lib/hooks/useDebounce';
|
||||
import useIsMediaAsset from '@staticcms/core/lib/hooks/useIsMediaAsset';
|
||||
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
|
||||
import { useWindowEvent } from '@staticcms/core/lib/util/window.util';
|
||||
|
||||
import type { Collection, Entry, FileOrImageField, MarkdownField } from '@staticcms/core/interface';
|
||||
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||
@ -65,7 +64,6 @@ export interface MediaPopoverProps<T extends FileOrImageField | MarkdownField> {
|
||||
onUrlChange: (newValue: string) => void;
|
||||
onTextChange?: (newValue: string) => void;
|
||||
onClose: (shouldFocus: boolean) => void;
|
||||
mediaOpen?: boolean;
|
||||
onMediaToggle?: (open: boolean) => void;
|
||||
onMediaChange: (newValue: string) => void;
|
||||
onRemove?: () => void;
|
||||
@ -87,7 +85,6 @@ const MediaPopover = <T extends FileOrImageField | MarkdownField>({
|
||||
onUrlChange,
|
||||
onTextChange,
|
||||
onClose,
|
||||
mediaOpen,
|
||||
onMediaToggle,
|
||||
onMediaChange,
|
||||
onRemove,
|
||||
@ -101,9 +98,9 @@ const MediaPopover = <T extends FileOrImageField | MarkdownField>({
|
||||
|
||||
const [editing, setEditing] = useState(inserting);
|
||||
|
||||
const hasEditorFocus = useFocused();
|
||||
const [hasFocus, setHasFocus] = useState(false);
|
||||
const debouncedHasFocus = useDebounce(hasFocus, 150);
|
||||
useWindowEvent('mediaLibraryClose', () => {
|
||||
onMediaToggle?.(false);
|
||||
});
|
||||
|
||||
const handleClose = useCallback(
|
||||
(shouldFocus: boolean) => {
|
||||
@ -155,60 +152,11 @@ const MediaPopover = <T extends FileOrImageField | MarkdownField>({
|
||||
}
|
||||
}, [anchorEl, editing, inserting, urlDisabled]);
|
||||
|
||||
const [
|
||||
{ prevAnchorEl, prevHasEditorFocus, prevHasFocus, prevDebouncedHasFocus },
|
||||
setPrevFocusState,
|
||||
] = useState<{
|
||||
prevAnchorEl: HTMLElement | null;
|
||||
prevHasEditorFocus: boolean;
|
||||
prevHasFocus: boolean;
|
||||
prevDebouncedHasFocus: boolean;
|
||||
}>({
|
||||
prevAnchorEl: anchorEl,
|
||||
prevHasEditorFocus: hasEditorFocus,
|
||||
prevHasFocus: hasFocus,
|
||||
prevDebouncedHasFocus: debouncedHasFocus,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (mediaOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (anchorEl && !prevHasEditorFocus && hasEditorFocus) {
|
||||
handleClose(false);
|
||||
}
|
||||
|
||||
if (anchorEl && (prevHasFocus || prevDebouncedHasFocus) && !hasFocus && !debouncedHasFocus) {
|
||||
handleClose(false);
|
||||
}
|
||||
|
||||
setPrevFocusState({
|
||||
prevAnchorEl: anchorEl,
|
||||
prevHasEditorFocus: hasEditorFocus,
|
||||
prevHasFocus: hasFocus,
|
||||
prevDebouncedHasFocus: debouncedHasFocus,
|
||||
});
|
||||
}, [
|
||||
anchorEl,
|
||||
debouncedHasFocus,
|
||||
handleClose,
|
||||
hasEditorFocus,
|
||||
hasFocus,
|
||||
mediaOpen,
|
||||
prevAnchorEl,
|
||||
prevDebouncedHasFocus,
|
||||
prevHasEditorFocus,
|
||||
prevHasFocus,
|
||||
]);
|
||||
|
||||
const handleFocus = useCallback(() => {
|
||||
setHasFocus(true);
|
||||
onFocus?.();
|
||||
}, [onFocus]);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
setHasFocus(false);
|
||||
onBlur?.();
|
||||
}, [onBlur]);
|
||||
|
||||
@ -220,7 +168,11 @@ const MediaPopover = <T extends FileOrImageField | MarkdownField>({
|
||||
[onMediaChange, onMediaToggle],
|
||||
);
|
||||
|
||||
const handleOpenMediaLibrary = useMediaInsert(url, { field, forImage }, handleMediaChange);
|
||||
const handleOpenMediaLibrary = useMediaInsert(
|
||||
url,
|
||||
{ collection, field, forImage },
|
||||
handleMediaChange,
|
||||
);
|
||||
|
||||
const handleMediaOpen = useCallback(() => {
|
||||
onMediaToggle?.(true);
|
||||
|
@ -12,6 +12,7 @@ import { useFocused } from 'slate-react';
|
||||
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
|
||||
import { isEmpty } from '@staticcms/core/lib/util/string.util';
|
||||
import { MediaPopover } from '@staticcms/markdown';
|
||||
import useDebounce from '@staticcms/core/lib/hooks/useDebounce';
|
||||
|
||||
import type { Collection, Entry, MarkdownField } from '@staticcms/core/interface';
|
||||
import type { MdImageElement, MdValue } from '@staticcms/markdown';
|
||||
@ -35,13 +36,32 @@ const withImageElement = ({ containerRef, collection, entry, field }: WithImageE
|
||||
const { url, alt } = element;
|
||||
const [internalUrl, setInternalUrl] = useState(url);
|
||||
const [internalAlt, setInternalAlt] = useState(alt);
|
||||
const [popoverHasFocus, setPopoverHasFocus] = useState(false);
|
||||
const debouncedPopoverHasFocus = useDebounce(popoverHasFocus, 100);
|
||||
|
||||
const [mediaOpen, setMediaOpen] = useState(false);
|
||||
const imageRef = useRef<HTMLImageElement | null>(null);
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLImageElement | null>(null);
|
||||
const hasEditorFocus = useFocused();
|
||||
const debouncedHasEditorFocus = useDebounce(hasEditorFocus, 100);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
setAnchorEl(null);
|
||||
if (!popoverHasFocus && !mediaOpen) {
|
||||
setAnchorEl(null);
|
||||
}
|
||||
}, [mediaOpen, popoverHasFocus]);
|
||||
|
||||
const handlePopoverFocus = useCallback(() => {
|
||||
setPopoverHasFocus(true);
|
||||
}, []);
|
||||
|
||||
const handlePopoverBlur = useCallback(() => {
|
||||
setPopoverHasFocus(false);
|
||||
}, []);
|
||||
|
||||
const handleMediaToggle = useCallback(() => {
|
||||
setMediaOpen(oldMediaOpen => !oldMediaOpen);
|
||||
}, []);
|
||||
|
||||
const handleChange = useCallback(
|
||||
@ -96,7 +116,28 @@ const withImageElement = ({ containerRef, collection, entry, field }: WithImageE
|
||||
const selection = usePlateSelection();
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasEditorFocus || !selection) {
|
||||
if (
|
||||
hasEditorFocus ||
|
||||
debouncedHasEditorFocus ||
|
||||
mediaOpen ||
|
||||
popoverHasFocus ||
|
||||
debouncedPopoverHasFocus
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleClose();
|
||||
}, [
|
||||
debouncedHasEditorFocus,
|
||||
debouncedPopoverHasFocus,
|
||||
handleClose,
|
||||
hasEditorFocus,
|
||||
mediaOpen,
|
||||
popoverHasFocus,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasEditorFocus || !selection || mediaOpen || popoverHasFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -109,12 +150,24 @@ const withImageElement = ({ containerRef, collection, entry, field }: WithImageE
|
||||
}
|
||||
|
||||
if (node !== element && node !== firstChild) {
|
||||
handleClose();
|
||||
if (anchorEl) {
|
||||
handleClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
handleOpenPopover();
|
||||
}, [handleClose, hasEditorFocus, element, selection, editor, handleOpenPopover]);
|
||||
}, [
|
||||
handleClose,
|
||||
hasEditorFocus,
|
||||
element,
|
||||
selection,
|
||||
editor,
|
||||
handleOpenPopover,
|
||||
mediaOpen,
|
||||
popoverHasFocus,
|
||||
anchorEl,
|
||||
]);
|
||||
|
||||
return (
|
||||
<span onBlur={handleBlur}>
|
||||
@ -140,6 +193,9 @@ const withImageElement = ({ containerRef, collection, entry, field }: WithImageE
|
||||
onMediaChange={handleMediaChange}
|
||||
onRemove={handleRemove}
|
||||
forImage
|
||||
onFocus={handlePopoverFocus}
|
||||
onBlur={handlePopoverBlur}
|
||||
onMediaToggle={handleMediaToggle}
|
||||
/>
|
||||
{children}
|
||||
</span>
|
||||
|
@ -2,17 +2,21 @@ import {
|
||||
findNodePath,
|
||||
focusEditor,
|
||||
getEditorString,
|
||||
getNode,
|
||||
replaceNodeChildren,
|
||||
setNodes,
|
||||
unwrapLink,
|
||||
upsertLink,
|
||||
usePlateSelection,
|
||||
} from '@udecode/plate';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useFocused } from 'slate-react';
|
||||
|
||||
import useDebounce from '@staticcms/core/lib/hooks/useDebounce';
|
||||
import MediaPopover from '../../common/MediaPopover';
|
||||
|
||||
import type { Collection, Entry, MarkdownField } from '@staticcms/core/interface';
|
||||
import type { MdLinkElement, MdValue } from '@staticcms/markdown';
|
||||
import type { PlateRenderElementProps } from '@udecode/plate';
|
||||
import type { PlateRenderElementProps, TText } from '@udecode/plate';
|
||||
import type { FC, MouseEvent } from 'react';
|
||||
|
||||
export interface WithLinkElementProps {
|
||||
@ -24,19 +28,49 @@ export interface WithLinkElementProps {
|
||||
|
||||
const withLinkElement = ({ containerRef, collection, field, entry }: WithLinkElementProps) => {
|
||||
const LinkElement: FC<PlateRenderElementProps<MdValue, MdLinkElement>> = ({
|
||||
attributes,
|
||||
attributes: { ref: _ref, ...attributes },
|
||||
children,
|
||||
nodeProps,
|
||||
element,
|
||||
editor,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLAnchorElement | null>(null);
|
||||
const urlRef = useRef<HTMLAnchorElement | null>(null);
|
||||
|
||||
const { url } = element;
|
||||
const path = findNodePath(editor, element);
|
||||
|
||||
const [internalUrl, setInternalUrl] = useState(url);
|
||||
const [internalText, setInternalText] = useState(getEditorString(editor, path));
|
||||
const [popoverHasFocus, setPopoverHasFocus] = useState(false);
|
||||
const debouncedPopoverHasFocus = useDebounce(popoverHasFocus, 100);
|
||||
|
||||
const [mediaOpen, setMediaOpen] = useState(false);
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLAnchorElement | null>(null);
|
||||
const hasEditorFocus = useFocused();
|
||||
const debouncedHasEditorFocus = useDebounce(hasEditorFocus, 100);
|
||||
|
||||
const handleOpenPopover = useCallback(() => {
|
||||
setAnchorEl(urlRef.current);
|
||||
}, []);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
if (!popoverHasFocus && !mediaOpen) {
|
||||
setAnchorEl(null);
|
||||
}
|
||||
}, [mediaOpen, popoverHasFocus]);
|
||||
|
||||
const handlePopoverFocus = useCallback(() => {
|
||||
setPopoverHasFocus(true);
|
||||
}, []);
|
||||
|
||||
const handlePopoverBlur = useCallback(() => {
|
||||
setPopoverHasFocus(false);
|
||||
}, []);
|
||||
|
||||
const handleMediaToggle = useCallback(() => {
|
||||
setMediaOpen(oldMediaOpen => !oldMediaOpen);
|
||||
}, []);
|
||||
|
||||
const handleClick = useCallback((event: MouseEvent<HTMLAnchorElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
@ -50,11 +84,29 @@ const withLinkElement = ({ containerRef, collection, field, entry }: WithLinkEle
|
||||
focusEditor(editor, editor.selection);
|
||||
}, [editor]);
|
||||
|
||||
const selection = usePlateSelection();
|
||||
|
||||
const handleChange = useCallback(
|
||||
(newUrl: string, newText: string) => {
|
||||
const path = findNodePath(editor, element);
|
||||
path && setNodes<MdLinkElement>(editor, { url: newUrl }, { at: path });
|
||||
upsertLink(editor, { url: newUrl, text: newText });
|
||||
|
||||
if (path) {
|
||||
setNodes(
|
||||
editor,
|
||||
{ ...element, url: newUrl, children: [{ text: newText }] },
|
||||
{ at: path },
|
||||
);
|
||||
|
||||
if (newText?.length && newText !== getEditorString(editor, path)) {
|
||||
replaceNodeChildren<TText>(editor, {
|
||||
at: path,
|
||||
nodes: { text: newText },
|
||||
insertOptions: {
|
||||
select: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[editor, element],
|
||||
);
|
||||
@ -72,9 +124,83 @@ const withLinkElement = ({ containerRef, collection, field, entry }: WithLinkEle
|
||||
handleChange(internalUrl, internalText);
|
||||
}, [handleChange, internalText, internalUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
hasEditorFocus ||
|
||||
debouncedHasEditorFocus ||
|
||||
mediaOpen ||
|
||||
popoverHasFocus ||
|
||||
debouncedPopoverHasFocus
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleClose();
|
||||
}, [
|
||||
debouncedHasEditorFocus,
|
||||
debouncedPopoverHasFocus,
|
||||
handleClose,
|
||||
hasEditorFocus,
|
||||
mediaOpen,
|
||||
popoverHasFocus,
|
||||
]);
|
||||
useEffect(() => {
|
||||
if (
|
||||
hasEditorFocus ||
|
||||
debouncedHasEditorFocus ||
|
||||
mediaOpen ||
|
||||
popoverHasFocus ||
|
||||
debouncedPopoverHasFocus
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleClose();
|
||||
}, [
|
||||
debouncedHasEditorFocus,
|
||||
debouncedPopoverHasFocus,
|
||||
handleClose,
|
||||
hasEditorFocus,
|
||||
mediaOpen,
|
||||
popoverHasFocus,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasEditorFocus || !selection || mediaOpen || popoverHasFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = getNode(editor, selection.anchor.path);
|
||||
const firstChild =
|
||||
'children' in element && element.children.length > 0 ? element.children[0] : undefined;
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node !== element && node !== firstChild) {
|
||||
if (anchorEl) {
|
||||
handleClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
handleOpenPopover();
|
||||
}, [
|
||||
handleClose,
|
||||
hasEditorFocus,
|
||||
element,
|
||||
selection,
|
||||
editor,
|
||||
handleOpenPopover,
|
||||
mediaOpen,
|
||||
popoverHasFocus,
|
||||
anchorEl,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<a {...attributes} href={url} {...nodeProps} onClick={handleClick}>
|
||||
<span onBlur={handleBlur}>
|
||||
<a ref={urlRef} {...attributes} href={url} {...nodeProps} onClick={handleClick}>
|
||||
{children}
|
||||
</a>
|
||||
<MediaPopover
|
||||
@ -90,8 +216,11 @@ const withLinkElement = ({ containerRef, collection, field, entry }: WithLinkEle
|
||||
onClose={handleClose}
|
||||
onMediaChange={handleMediaChange}
|
||||
onRemove={handleRemove}
|
||||
onFocus={handlePopoverFocus}
|
||||
onBlur={handlePopoverBlur}
|
||||
onMediaToggle={handleMediaToggle}
|
||||
/>
|
||||
</>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import EditorControl from '@staticcms/core/components/Editor/EditorControlPane/EditorControl';
|
||||
import EditorControl from '@staticcms/core/components/editor/EditorControlPane/EditorControl';
|
||||
import ObjectWidgetTopBar from '@staticcms/core/components/UI/ObjectWidgetTopBar';
|
||||
import Outline from '@staticcms/core/components/UI/Outline';
|
||||
import { transientOptions } from '@staticcms/core/lib';
|
||||
|
@ -35,7 +35,7 @@ const ObjectControlWrapper = createControlWrapper({
|
||||
path: 'object',
|
||||
});
|
||||
|
||||
jest.mock('@staticcms/core/components/Editor/EditorControlPane/EditorControl', () => {
|
||||
jest.mock('@staticcms/core/components/editor/EditorControlPane/EditorControl', () => {
|
||||
return jest.fn(props => {
|
||||
const { parentPath, fieldName, field } = props;
|
||||
return (
|
||||
|
Reference in New Issue
Block a user