1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-03-15 12:29:16 +01:00

(ui) allow form inputs to be disabled, possibly with a callback

This commit is contained in:
Sylvain 2022-05-04 14:59:28 +02:00
parent 2f0a8064c2
commit 1f2d3867aa
9 changed files with 66 additions and 17 deletions

View File

@ -96,7 +96,7 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
<form className="provider-form" onSubmit={handleSubmit(onSubmit)}>
<FormInput id="name"
register={register}
readOnly={action === 'update'}
disabled={action === 'update'}
rules={{ required: true }}
label={t('app.admin.authentication.provider_form.name')} />
<FormSelect id="providable_type"
@ -104,7 +104,7 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
options={buildProvidableTypeOptions()}
label={t('app.admin.authentication.provider_form.authentication_type')}
onChange={onProvidableTypeChange}
readOnly={action === 'update'}
disabled={action === 'update'}
rules={{ required: true }} />
{providableType === 'DatabaseProvider' && <DatabaseForm register={register} />}
{providableType === 'OAuth2Provider' && <Oauth2Form register={register} strategyName={strategyName} />}

View File

@ -20,7 +20,8 @@ interface FabTextEditorProps {
image?: boolean,
onChange?: (content: string) => void,
placeholder?: string,
error?: string
error?: string,
readOnly?: boolean,
}
export interface FabTextEditorRef {
@ -30,7 +31,7 @@ export interface FabTextEditorRef {
/**
* This component is a WYSIWYG text editor
*/
export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, FabTextEditorProps> = ({ label, paragraphTools, content, limit = 400, video, image, onChange, placeholder, error }, ref: RefObject<FabTextEditorRef>) => {
export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, FabTextEditorProps> = ({ label, paragraphTools, content, limit = 400, video, image, onChange, placeholder, error, readOnly = false }, 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
@ -70,6 +71,7 @@ export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, Fab
}
})
],
editable: readOnly,
content,
onUpdate: ({ editor }) => {
onChange(editor.getHTML());

View File

@ -8,7 +8,7 @@ export interface AbstractFormItemProps<TFieldValues> extends PropsWithChildren<A
label?: string,
tooltip?: ReactNode,
className?: string,
disabled?: boolean,
disabled?: boolean|((id: string) => boolean),
readOnly?: boolean
onLabelClick?: (event: React.MouseEvent<HTMLLabelElement, MouseEvent>) => void,
}
@ -18,8 +18,9 @@ export interface AbstractFormItemProps<TFieldValues> extends PropsWithChildren<A
* 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, onLabelClick, children }: AbstractFormItemProps<TFieldValues>) => {
const [isDirty, setIsDirty] = useState(false);
const [fieldError, setFieldError] = useState(error);
const [isDirty, setIsDirty] = useState<boolean>(false);
const [fieldError, setFieldError] = useState<{ message: string }>(error);
const [isDisabled, setIsDisabled] = useState<boolean>(false);
useEffect(() => {
setIsDirty(_get(formState?.dirtyFields, id));
@ -30,6 +31,14 @@ export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label,
setFieldError(error);
}, [error]);
useEffect(() => {
if (typeof disabled === 'function') {
setIsDisabled(disabled(id));
} else {
setIsDisabled(disabled);
}
}, [disabled]);
// Compose classnames from props
const classNames = [
'form-item',
@ -38,7 +47,7 @@ export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label,
`${isDirty && warning ? 'is-warned' : ''}`,
`${rules && rules.required ? 'is-required' : ''}`,
`${readOnly ? 'is-readonly' : ''}`,
`${disabled ? 'is-disabled' : ''}`
`${isDisabled ? 'is-disabled' : ''}`
].join(' ');
/**

View File

@ -64,7 +64,7 @@ export const FormInput = <TFieldValues extends FieldValues, TInputType>({ id, re
})}
type={type}
step={step}
disabled={disabled}
disabled={typeof disabled === 'function' ? disabled(id) : disabled}
readOnly={readOnly}
placeholder={placeholder}
accept={accept} />

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import Select from 'react-select';
import { Controller, Path } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields';
@ -26,6 +26,16 @@ type selectOption<TOptionValue> = { value: TOptionValue, label: string };
* It is a multi-select component.
*/
export const FormMultiSelect = <TFieldValues extends FieldValues, TContext extends object, TOptionValue>({ id, label, tooltip, className, control, placeholder, options, valuesDefault, error, rules, disabled, onChange, formState, readOnly, warning, expectedResult }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
useEffect(() => {
if (typeof disabled === 'function') {
setIsDisabled(disabled(id) || readOnly);
} else {
setIsDisabled(disabled || readOnly);
}
}, [disabled]);
/**
* The following callback will trigger the onChange callback, if it was passed to this component,
* when the selected option changes.
@ -74,6 +84,7 @@ export const FormMultiSelect = <TFieldValues extends FieldValues, TContext exten
}}
placeholder={placeholder}
options={options}
isDisabled={isDisabled}
isMulti />
} />
</AbstractFormItem>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { FormControlledComponent } from '../../models/form-component';
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
import { FieldValues } from 'react-hook-form/dist/types/fields';
@ -18,8 +18,17 @@ interface FormRichTextProps<TFieldValues, TContext extends object> extends FormC
/**
* 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>) => {
export const FormRichText = <TFieldValues extends FieldValues, TContext extends object>({ id, label, tooltip, className, control, valueDefault, error, warning, rules, disabled, readOnly, formState, limit, paragraphTools, video, image }: FormRichTextProps<TFieldValues, TContext>) => {
const textEditorRef = React.useRef<FabTextEditorRef>();
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
useEffect(() => {
if (typeof disabled === 'function') {
setIsDisabled(disabled(id) || readOnly);
} else {
setIsDisabled(disabled || readOnly);
}
}, [disabled]);
/**
* Callback triggered when the user clicks to get the focus on the editor.
@ -40,7 +49,14 @@ export const FormRichText = <TFieldValues extends FieldValues, TContext extends
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} ref={textEditorRef} />
<FabTextEditor onChange={onChange}
content={value}
limit={limit}
paragraphTools={paragraphTools}
video={video}
image={image}
readOnly={isDisabled}
ref={textEditorRef} />
} />
</AbstractFormItem>
);

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import Select from 'react-select';
import { Controller, Path } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields';
@ -25,6 +25,16 @@ type selectOption<TOptionValue> = { value: TOptionValue, label: string };
* This component is a wrapper for react-select to use with react-hook-form
*/
export const FormSelect = <TFieldValues extends FieldValues, TContext extends object, TOptionValue>({ id, label, tooltip, className, control, placeholder, options, valueDefault, error, warning, rules, disabled, onChange, readOnly, clearable, formState }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
useEffect(() => {
if (typeof disabled === 'function') {
setIsDisabled(disabled(id) || readOnly);
} else {
setIsDisabled(disabled || readOnly);
}
}, [disabled]);
/**
* The following callback will trigger the onChange callback, if it was passed to this component,
* when the selected option changes.
@ -53,7 +63,7 @@ export const FormSelect = <TFieldValues extends FieldValues, TContext extends ob
onChange(val.value);
}}
placeholder={placeholder}
isDisabled={readOnly}
isDisabled={isDisabled}
isClearable={clearable}
options={options} />
} />

View File

@ -42,7 +42,7 @@ export const FormSwitch = <TFieldValues, TContext extends object>({ id, label, t
height={19}
width={40}
ref={ref}
disabled={disabled}
disabled={typeof disabled === 'function' ? disabled(id) : disabled}
readOnly={readOnly} />
} />
</AbstractFormItem>

View File

@ -18,6 +18,7 @@ import { PaymentScheduleSummary } from '../payment-schedule/payment-schedule-sum
import { PaymentSchedule } from '../../models/payment-schedule';
import { LocalPaymentModal } from '../payment/local-payment/local-payment-modal';
import { User } from '../../models/user';
import { TDateISO } from '../../typings/date-iso';
declare const Application: IApplication;
@ -83,7 +84,7 @@ const RenewModal: React.FC<RenewModalProps> = ({ isOpen, toggleModal, subscripti
/**
* Return the formatted localized date for the given date
*/
const formatDateTime = (date: Date): string => {
const formatDateTime = (date: Date|TDateISO): string => {
return t('app.admin.free_extend_modal.DATE_TIME', { DATE: FormatLib.date(date), TIME: FormatLib.time(date) });
};