|
|
@@ -0,0 +1,93 @@
|
|
|
+import React, {
|
|
|
+ ChangeEvent,
|
|
|
+ ForwardedRef,
|
|
|
+ InputHTMLAttributes,
|
|
|
+ forwardRef,
|
|
|
+ useEffect,
|
|
|
+ useState,
|
|
|
+} from 'react';
|
|
|
+import { ProsemirrorService } from '@vendure/admin-ui/core';
|
|
|
+import { useRichTextEditor } from '../react-hooks/use-rich-text-editor';
|
|
|
+
|
|
|
+export type RichTextEditorType = InputHTMLAttributes<HTMLInputElement> & {
|
|
|
+ /**
|
|
|
+ * @description
|
|
|
+ * Control the DOM attributes of the editable element. May be either an object or a function going from an editor state to an object.
|
|
|
+ * By default, the element will get a class "ProseMirror", and will have its contentEditable attribute determined by the editable prop.
|
|
|
+ * Additional classes provided here will be added to the class. For other attributes, the value provided first (as in someProp) will be used.
|
|
|
+ * Copied from real property description.
|
|
|
+ */
|
|
|
+ attributes?: Record<string, string>;
|
|
|
+ readOnly?: boolean;
|
|
|
+ onMount?: (editor: ProsemirrorService) => void;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description
|
|
|
+ * A rich text editor component which uses ProseMirror (rich text editor) under the hood.
|
|
|
+ *
|
|
|
+ * @example
|
|
|
+ * ```ts
|
|
|
+ * import { RichTextEditor } from '@vendure/admin-ui/react';
|
|
|
+ * import React from 'react';
|
|
|
+ *
|
|
|
+ * export function MyComponent() {
|
|
|
+ * const onSubmit = async (e: React.FormEvent) => {
|
|
|
+ * e.preventDefault();
|
|
|
+ * const form = new FormData(e.target as HTMLFormElement);
|
|
|
+ * const content = form.get("content");
|
|
|
+ * console.log(content);
|
|
|
+ * };
|
|
|
+ *
|
|
|
+ * return (
|
|
|
+ * <form className="w-full" onSubmit={onSubmit}>
|
|
|
+ * <RichTextEditor
|
|
|
+ * name="content"
|
|
|
+ * readOnly={false}
|
|
|
+ * onMount={(e) => console.log("Mounted", e)}
|
|
|
+ * />
|
|
|
+ * <button type="submit" className="btn btn-primary">
|
|
|
+ * Submit
|
|
|
+ * </button>
|
|
|
+ * </form>
|
|
|
+ * );
|
|
|
+ * }
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * @docsCategory react-components
|
|
|
+ */
|
|
|
+export const RichTextEditor = forwardRef((props: RichTextEditorType, ref: ForwardedRef<HTMLInputElement>) => {
|
|
|
+ const [data, setData] = useState<string>('');
|
|
|
+ const { readOnly, ...rest } = props;
|
|
|
+ const { ref: _ref, editor } = useRichTextEditor({
|
|
|
+ attributes: props.attributes,
|
|
|
+ isReadOnly: () => readOnly || false,
|
|
|
+ onTextInput: text => {
|
|
|
+ setData(text);
|
|
|
+ if (props.onChange) {
|
|
|
+ props.onChange({
|
|
|
+ target: { value: text },
|
|
|
+ } as ChangeEvent<HTMLInputElement>);
|
|
|
+ }
|
|
|
+ if (ref && 'current' in ref && ref.current) {
|
|
|
+ ref.current.value = text;
|
|
|
+ const event = new Event('input', {
|
|
|
+ bubbles: true,
|
|
|
+ cancelable: true,
|
|
|
+ });
|
|
|
+ ref.current.dispatchEvent(event);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ useEffect(() => {
|
|
|
+ if (props.onMount && editor) props.onMount(editor);
|
|
|
+ }, []);
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <div ref={_ref} {...rest} />
|
|
|
+ <input type="hidden" value={data} ref={ref} />
|
|
|
+ </>
|
|
|
+ );
|
|
|
+});
|
|
|
+
|
|
|
+RichTextEditor.displayName = 'RichTextEditor';
|