1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(bug) focus the text edition area when the user clicks on the editor

This commit is contained in:
Sylvain 2022-05-03 11:22:27 +02:00
parent 8602fefce6
commit 48fd47f8d9
3 changed files with 53 additions and 24 deletions

View File

@ -1,9 +1,6 @@
import React from 'react';
import React, { forwardRef, RefObject, useImperativeHandle, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { IApplication } from '../../../models/application';
import { Loader } from '../loader';
import { useEditor, EditorContent } from '@tiptap/react';
import { useEditor, EditorContent, Editor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import Placeholder from '@tiptap/extension-placeholder';
import CharacterCount from '@tiptap/extension-character-count';
@ -14,8 +11,6 @@ import Image from '@tiptap/extension-image';
import { MenuBar } from './menu-bar';
import { WarningOctagon } from 'phosphor-react';
declare const Application: IApplication;
interface FabTextEditorProps {
label?: string,
paragraphTools?: boolean,
@ -28,14 +23,26 @@ interface FabTextEditorProps {
error?: string
}
export interface FabTextEditorRef {
focus: () => void
}
/**
* This component is a WYSIWYG text editor
*/
export const FabTextEditor: React.FC<FabTextEditorProps> = ({ label, paragraphTools, content, limit = 400, video, image, onChange, placeholder, error }) => {
export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, FabTextEditorProps> = ({ label, paragraphTools, content, limit = 400, video, image, onChange, placeholder, error }, ref: RefObject<FabTextEditorRef>) => {
const { t } = useTranslation('shared');
const placeholderText = placeholder || t('app.shared.text_editor.text_placeholder');
// TODO: Add ctrl+click on link to visit
const editorRef: React.MutableRefObject<Editor | null> = useRef(null);
// the methods in useImperativeHandle are exposed to the parent component
useImperativeHandle(ref, () => ({
focus () {
focusEditor();
}
}), []);
// Setup the editor
// Extensions add functionalities to the editor (Bold, Italic…)
// Events fire action (onUpdate -> get the content as HTML)
@ -69,10 +76,17 @@ export const FabTextEditor: React.FC<FabTextEditorProps> = ({ label, paragraphTo
}
});
/**
* Callback triggered when the label is clicked: we want to focus the text edition zone
*/
const focusEditor = () => {
editor.commands.focus('start');
editorRef.current?.commands?.focus();
};
// bind the editor to the ref, once it is ready
if (!editor) return null;
editorRef.current = editor;
return (
<>
{label && <label onClick={focusEditor} className="fab-textEditor-label">{label}</label>}
@ -93,12 +107,4 @@ export const FabTextEditor: React.FC<FabTextEditorProps> = ({ label, paragraphTo
);
};
const FabTextEditorWrapper: React.FC<FabTextEditorProps> = ({ label, paragraphTools, content, limit, video, image, placeholder, error }) => {
return (
<Loader>
<FabTextEditor label={label} paragraphTools={paragraphTools} content={content} limit={limit} video={video} image={image} placeholder={placeholder} error={error} />
</Loader>
);
};
Application.Components.component('fabTextEditor', react2angular(FabTextEditorWrapper, ['label', 'paragraphTools', 'content', 'limit', 'video', 'image', 'placeholder', 'error']));
export default forwardRef(FabTextEditor);

View File

@ -10,13 +10,14 @@ export interface AbstractFormItemProps<TFieldValues> extends PropsWithChildren<A
className?: string,
disabled?: boolean,
readOnly?: boolean
onLabelClick?: (event: React.MouseEvent<HTMLLabelElement, MouseEvent>) => void,
}
/**
* This abstract component should not be used directly.
* Other forms components that are intended to be used with react-hook-form must extend this component.
*/
export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label, tooltip, className, disabled, readOnly, error, warning, rules, formState, children }: AbstractFormItemProps<TFieldValues>) => {
export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label, tooltip, className, disabled, readOnly, error, warning, rules, formState, onLabelClick, children }: AbstractFormItemProps<TFieldValues>) => {
const [isDirty, setIsDirty] = useState(false);
const [fieldError, setFieldError] = useState(error);
@ -40,8 +41,18 @@ export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label,
`${disabled ? 'is-disabled' : ''}`
].join(' ');
/**
* This function is called when the label is clicked.
* It is used to focus the input.
*/
function handleLabelClick (event: React.MouseEvent<HTMLLabelElement, MouseEvent>) {
if (typeof onLabelClick === 'function') {
onLabelClick(event);
}
}
return (
<label className={classNames}>
<label className={classNames} onClick={handleLabelClick}>
{label && <div className='form-item-header'>
<p>{label}</p>
{tooltip && <div className="item-tooltip">

View File

@ -2,7 +2,7 @@ import React from 'react';
import { FormControlledComponent } from '../../models/form-component';
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import { FabTextEditor } from '../base/text-editor/fab-text-editor';
import FabTextEditor, { FabTextEditorRef } from '../base/text-editor/fab-text-editor';
import { Controller, Path } from 'react-hook-form';
import { FieldPath } from 'react-hook-form/dist/types/path';
import { FieldPathValue, UnpackNestedValue } from 'react-hook-form/dist/types';
@ -16,19 +16,31 @@ interface FormRichTextProps<TFieldValues, TContext extends object> extends FormC
}
/**
* THis component is a rich-text editor to use with react-hook-form.
* This component is a rich-text editor to use with react-hook-form.
*/
export const FormRichText = <TFieldValues extends FieldValues, TContext extends object>({ id, label, tooltip, className, control, valueDefault, error, warning, rules, disabled, formState, limit, paragraphTools, video, image }: FormRichTextProps<TFieldValues, TContext>) => {
const textEditorRef = React.useRef<FabTextEditorRef>();
/**
* Callback triggered when the user clicks to get the focus on the editor.
* We do not want the default behavior (focus the first child, which is the Bold button)
* but we want to focus the text edition area.
*/
function focusTextEditor (event: React.MouseEvent<HTMLLabelElement, MouseEvent>) {
event.preventDefault();
textEditorRef.current.focus();
}
return (
<AbstractFormItem id={id} label={label} tooltip={tooltip}
className={`form-rich-text ${className || ''}`}
error={error} warning={warning} rules={rules}
disabled={disabled} formState={formState}>
disabled={disabled} formState={formState} onLabelClick={focusTextEditor}>
<Controller name={id as FieldPath<TFieldValues>}
control={control}
defaultValue={valueDefault as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>}
render={({ field: { onChange, value } }) =>
<FabTextEditor onChange={onChange} content={value} limit={limit} paragraphTools={paragraphTools} video={video} image={image} />
<FabTextEditor onChange={onChange} content={value} limit={limit} paragraphTools={paragraphTools} video={video} image={image} ref={textEditorRef} />
} />
</AbstractFormItem>
);