feat: mobile support (#831)
This commit is contained in:
committed by
GitHub
parent
7744df1103
commit
83c9d91b88
@ -249,6 +249,7 @@ const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({
|
||||
{...convertMuiTextFieldProps(props)}
|
||||
inputRef={ref}
|
||||
cursor="pointer"
|
||||
inputClassName="truncate"
|
||||
/>
|
||||
<NowButton
|
||||
key="mobile-date-now"
|
||||
@ -282,6 +283,7 @@ const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({
|
||||
forSingleList={forSingleList}
|
||||
cursor="pointer"
|
||||
disabled={disabled}
|
||||
wrapperClassName="!w-date-widget"
|
||||
>
|
||||
<LocalizationProvider key="localization-provider" dateAdapter={AdapterDateFns}>
|
||||
{dateTimePicker}
|
||||
|
@ -350,7 +350,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
|
||||
if (Array.isArray(internalValue) ? internalValue.length === 0 : isEmpty(internalValue)) {
|
||||
return (
|
||||
<div key="selection" className="flex flex-col gap-2 px-3 pt-2 pb-4">
|
||||
<div key="controls" className="flex gap-2">
|
||||
<div key="controls" className="flex gap-2 flex-col xs:flex-row">
|
||||
<Button
|
||||
buttonRef={uploadButtonRef}
|
||||
color="primary"
|
||||
@ -388,7 +388,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
|
||||
)}
|
||||
>
|
||||
{renderedImagesLinks}
|
||||
<div key="controls" className="flex gap-2">
|
||||
<div key="controls" className="flex gap-2 flex-col xs:flex-row">
|
||||
<Button
|
||||
buttonRef={uploadButtonRef}
|
||||
color="primary"
|
||||
|
@ -364,7 +364,7 @@ const ListControl: FC<WidgetControlProps<ValueOrNestedValue[], ListField>> = pro
|
||||
<Menu
|
||||
label={t('editor.editorWidgets.list.addType', { item: label })}
|
||||
variant="outlined"
|
||||
className="w-full z-20"
|
||||
buttonClassName="w-full z-20"
|
||||
data-testid="list-type-add"
|
||||
disabled={disabled}
|
||||
>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { VFileMessage } from 'vfile-message';
|
||||
|
||||
import { withMdxImage } from '@staticcms/core/components/common/image/Image';
|
||||
@ -11,20 +11,33 @@ import { processShortcodeConfigToMdx } from './plate/serialization/slate/process
|
||||
|
||||
import type { MarkdownField, WidgetPreviewProps } from '@staticcms/core/interface';
|
||||
import type { FC } from 'react';
|
||||
import type { UseMdxState } from './plate/hooks/useMdx';
|
||||
|
||||
interface FallbackComponentProps {
|
||||
error: string;
|
||||
interface MdxComponentProps {
|
||||
state: UseMdxState;
|
||||
}
|
||||
|
||||
function FallbackComponent({ error }: FallbackComponentProps) {
|
||||
const message = new VFileMessage(error);
|
||||
message.fatal = true;
|
||||
return (
|
||||
<pre>
|
||||
<code>{String(message)}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
// Create a preview component that can handle errors with try-catch block; for catching invalid JS expressions errors that ErrorBoundary cannot catch.
|
||||
const MdxComponent: FC<MdxComponentProps> = ({ state }) => {
|
||||
const Result = useMemo(() => state.file?.result as FC | undefined, [state]);
|
||||
|
||||
if (!Result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return <Result key="result" />;
|
||||
} catch (error) {
|
||||
const message = new VFileMessage(String(error));
|
||||
message.fatal = true;
|
||||
|
||||
return (
|
||||
<pre key="error">
|
||||
<code>{String(message)}</code>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const MarkdownPreview: FC<WidgetPreviewProps<string, MarkdownField>> = previewProps => {
|
||||
const { value, collection, field } = previewProps;
|
||||
@ -40,7 +53,7 @@ const MarkdownPreview: FC<WidgetPreviewProps<string, MarkdownField>> = previewPr
|
||||
);
|
||||
|
||||
const [state, setValue] = useMdx(`editor-${id}.mdx`, value ?? '');
|
||||
const [prevValue, setPrevValue] = useState('');
|
||||
const [prevValue, setPrevValue] = useState<string | null>(null);
|
||||
useEffect(() => {
|
||||
if (prevValue !== value) {
|
||||
const parsedValue = processShortcodeConfigToMdx(getShortcodes(), value ?? '');
|
||||
@ -49,35 +62,13 @@ const MarkdownPreview: FC<WidgetPreviewProps<string, MarkdownField>> = previewPr
|
||||
}
|
||||
}, [prevValue, setValue, value]);
|
||||
|
||||
// Create a preview component that can handle errors with try-catch block; for catching invalid JS expressions errors that ErrorBoundary cannot catch.
|
||||
const MdxComponent = useCallback(() => {
|
||||
if (!state.file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return (state.file.result as FC)({});
|
||||
} catch (error) {
|
||||
return <FallbackComponent error={String(error)} />;
|
||||
}
|
||||
}, [state.file]);
|
||||
|
||||
return useMemo(() => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{state.file && state.file.result ? (
|
||||
<MDXProvider components={components}>
|
||||
<MdxComponent />
|
||||
</MDXProvider>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [MdxComponent]);
|
||||
return (
|
||||
<div key="markdown-preview">
|
||||
<MDXProvider components={components}>
|
||||
<MdxComponent state={state} />{' '}
|
||||
</MDXProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownPreview;
|
||||
|
@ -280,6 +280,7 @@ const PlateEditor: FC<PlateEditorProps> = ({
|
||||
...editableProps,
|
||||
onFocus,
|
||||
onBlur,
|
||||
className: '!outline-none',
|
||||
}}
|
||||
>
|
||||
<div key="editor-inner-wrapper" ref={innerEditorContainerRef}>
|
||||
|
@ -40,7 +40,7 @@ const ShortcodeToolbarButton: FC<ShortcodeToolbarButtonProps> = ({ disabled }) =
|
||||
keepMounted
|
||||
hideDropdownIcon
|
||||
variant="text"
|
||||
className="
|
||||
buttonClassName="
|
||||
py-0.5
|
||||
px-0.5
|
||||
h-6
|
||||
|
@ -8,6 +8,7 @@ import { VFileMessage } from 'vfile-message';
|
||||
|
||||
import useDebouncedCallback from '@staticcms/core/lib/hooks/useDebouncedCallback';
|
||||
import flattenListItemParagraphs from '../serialization/slate/flattenListItemParagraphs';
|
||||
import useDebounce from '@staticcms/core/lib/hooks/useDebounce';
|
||||
|
||||
export interface UseMdxState {
|
||||
file: VFile | null;
|
||||
@ -49,10 +50,11 @@ export default function useMdx(
|
||||
);
|
||||
|
||||
const setValue = useDebouncedCallback(setValueCallback, 100);
|
||||
const debouncedState = useDebounce(state, 150);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(input);
|
||||
}, [input, setValue]);
|
||||
|
||||
return [state, setValue];
|
||||
return [debouncedState, setValue];
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export function getToolbarButtons(
|
||||
keepMounted
|
||||
hideDropdownIcon
|
||||
variant="text"
|
||||
className="
|
||||
buttonClassName="
|
||||
py-0.5
|
||||
px-0.5
|
||||
h-6
|
||||
|
@ -322,7 +322,7 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
|
||||
label={
|
||||
<>
|
||||
{Array.isArray(selectedValue) && selectedValue.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-2 w-full p-2 pr-0 max-w-fit">
|
||||
<div className="flex flex-wrap gap-2 p-2 pr-0 w-relation-widget-label">
|
||||
{selectedValue.map(selectValue => {
|
||||
const option = uniqueOptionsByValue[selectValue];
|
||||
return (
|
||||
|
@ -87,7 +87,14 @@ const UUIDControl: FC<WidgetControlProps<string, UUIDField>> = ({
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<TextField type="text" inputRef={ref} value={internalValue} disabled={disabled} readonly />
|
||||
<TextField
|
||||
type="text"
|
||||
inputRef={ref}
|
||||
value={internalValue}
|
||||
disabled={disabled}
|
||||
readonly
|
||||
inputClassName="truncate"
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user