feat: mobile support (#831)

This commit is contained in:
Daniel Lautzenheiser
2023-05-31 11:55:53 -04:00
committed by GitHub
parent 7744df1103
commit 83c9d91b88
67 changed files with 2210 additions and 1056 deletions

View File

@ -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}

View File

@ -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"

View File

@ -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}
>

View File

@ -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;

View File

@ -280,6 +280,7 @@ const PlateEditor: FC<PlateEditorProps> = ({
...editableProps,
onFocus,
onBlur,
className: '!outline-none',
}}
>
<div key="editor-inner-wrapper" ref={innerEditorContainerRef}>

View File

@ -40,7 +40,7 @@ const ShortcodeToolbarButton: FC<ShortcodeToolbarButtonProps> = ({ disabled }) =
keepMounted
hideDropdownIcon
variant="text"
className="
buttonClassName="
py-0.5
px-0.5
h-6

View File

@ -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];
}

View File

@ -64,7 +64,7 @@ export function getToolbarButtons(
keepMounted
hideDropdownIcon
variant="text"
className="
buttonClassName="
py-0.5
px-0.5
h-6

View File

@ -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 (

View File

@ -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>
);
};