mui-tiptap
Version:
A Material-UI (MUI) styled WYSIWYG rich text editor, using Tiptap
95 lines (94 loc) • 4.9 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { styled, useThemeProps } from "@mui/material/styles";
import { clsx } from "clsx";
import FieldContainer from "./FieldContainer";
import MenuBar from "./MenuBar";
import RichTextContent from "./RichTextContent";
import { richTextFieldClasses, } from "./RichTextField.classes";
import { useRichTextEditorContext } from "./context";
import useDebouncedFocus from "./hooks/useDebouncedFocus";
import { getUtilityComponentName } from "./styles";
import DebounceRender from "./utils/DebounceRender";
const componentName = getUtilityComponentName("RichTextField");
const RichTextFieldRoot = styled(FieldContainer, {
name: componentName,
slot: "root",
overridesResolver: (props, styles) => [
styles.root,
props.ownerState.variant === "outlined" && styles.outlined,
props.ownerState.variant === "standard" && styles.standard,
],
})(({ theme, ownerState }) => ({
// This first class is added to allow convenient user overrides. Users can
// similarly override the other classes below.
// TODO(Steven DeMartini): We could seemingly switch to using `styled()`
// wrappers for the content and menu bar elements, and apply the styles
// directly rather than with nested selectors.
...(ownerState.variant === "standard" && {
// We don't need horizontal spacing when not using the outlined variant
[`& .${richTextFieldClasses.content}`]: {
padding: theme.spacing(1.5, 0),
},
[`& .${richTextFieldClasses.menuBarContent}`]: {
padding: theme.spacing(1, 0),
},
}),
...(ownerState.variant === "outlined" && {
// Add padding around the input area and menu bar, since they're
// contained in the outline
[`& .${richTextFieldClasses.content}`]: {
padding: theme.spacing(1.5),
},
[`& .${richTextFieldClasses.menuBarContent}`]: {
padding: theme.spacing(1, 1.5),
},
}),
}));
/**
* Renders the Tiptap rich text editor content and a controls menu bar.
*
* With the "outlined" variant, renders a bordered UI similar to the Material UI
* `TextField`. The "standard" variant does not have an outline/border.
*
* Must be a child of the RichTextEditorProvider so that the `editor` context is
* available.
*/
export default function RichTextField(inProps) {
var _a, _b;
const props = useThemeProps({ props: inProps, name: componentName });
const { variant = "outlined", controls, disableDebounceRenderControls = false, disabled, className, classes = {}, footer, MenuBarProps, RichTextContentProps, sx, ...fieldContainerProps } = props;
const editor = useRichTextEditorContext();
const ownerState = {
variant,
disabled,
disableDebounceRenderControls,
};
// Because the user interactions with the editor menu bar buttons unfocus the editor
// (since it's not part of the editor content), we'll debounce our visual focused
// state so that the (outlined) field focus styles don't "flash" whenever that happens
const isFieldFocused = useDebouncedFocus({ editor });
return (_jsxs(RichTextFieldRoot, { ...fieldContainerProps, variant: variant, focused: !disabled && isFieldFocused, disabled: disabled, className: clsx([
richTextFieldClasses.root,
classes.root,
variant === "outlined"
? [richTextFieldClasses.outlined, classes.outlined]
: [richTextFieldClasses.standard, classes.standard],
className,
]), ownerState: ownerState, sx: sx, children: [controls && (_jsx(MenuBar, { ...MenuBarProps, classes: {
...MenuBarProps === null || MenuBarProps === void 0 ? void 0 : MenuBarProps.classes,
root: clsx([
richTextFieldClasses.menuBar,
classes.menuBar,
(_a = MenuBarProps === null || MenuBarProps === void 0 ? void 0 : MenuBarProps.classes) === null || _a === void 0 ? void 0 : _a.root,
]),
content: clsx([
richTextFieldClasses.menuBarContent,
classes.menuBarContent,
(_b = MenuBarProps === null || MenuBarProps === void 0 ? void 0 : MenuBarProps.classes) === null || _b === void 0 ? void 0 : _b.content,
]),
}, children: disableDebounceRenderControls ? (controls) : (_jsx(DebounceRender, { children: controls })) })), _jsx(RichTextContent, { ...RichTextContentProps, className: clsx([
richTextFieldClasses.content,
classes.content,
RichTextContentProps === null || RichTextContentProps === void 0 ? void 0 : RichTextContentProps.className,
]) }), footer] }));
}