mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-22 15:24:55 +00:00
add markdown editor
This commit is contained in:
parent
f6a16038f5
commit
3d279d1327
@ -38,6 +38,7 @@ import Route from 'Common/Types/API/Route';
|
||||
import Exception from 'Common/Types/Exception/Exception';
|
||||
import HashedString from 'Common/Types/HashedString';
|
||||
import Input from '../Input/Input';
|
||||
import Markdown from '../Markdown.tsx/Markdown';
|
||||
|
||||
export const DefaultValidateFunction: Function = (
|
||||
_values: FormValues<JSONObject>
|
||||
@ -105,7 +106,7 @@ const BasicForm: Function = <T extends Object>(
|
||||
if (props.showAsColumns && props.showAsColumns > 2) {
|
||||
throw new BadDataException(
|
||||
'showAsCOlumns should be <= 2. It is currently ' +
|
||||
props.showAsColumns
|
||||
props.showAsColumns
|
||||
);
|
||||
}
|
||||
|
||||
@ -153,7 +154,7 @@ const BasicForm: Function = <T extends Object>(
|
||||
placeholder={field.placeholder || ''}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[fieldName]
|
||||
: ''
|
||||
}
|
||||
@ -165,45 +166,45 @@ const BasicForm: Function = <T extends Object>(
|
||||
|
||||
{(field.fieldType === FormFieldSchemaType.Dropdown ||
|
||||
field.fieldType ===
|
||||
FormFieldSchemaType.MultiSelectDropdown) && (
|
||||
<Field name={fieldName}>
|
||||
{({ form }: any) => {
|
||||
return (
|
||||
<Dropdown
|
||||
onChange={async (
|
||||
value:
|
||||
| DropdownValue
|
||||
| Array<DropdownValue>
|
||||
) => {
|
||||
await form.setFieldValue(
|
||||
fieldName,
|
||||
value,
|
||||
true
|
||||
);
|
||||
}}
|
||||
onBlur={async () => {
|
||||
await form.setFieldTouched(
|
||||
fieldName,
|
||||
true
|
||||
);
|
||||
}}
|
||||
isMultiSelect={
|
||||
field.fieldType ===
|
||||
FormFieldSchemaType.MultiSelectDropdown
|
||||
}
|
||||
options={field.dropdownOptions || []}
|
||||
placeholder={field.placeholder || ''}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[fieldName]
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
)}
|
||||
FormFieldSchemaType.MultiSelectDropdown) && (
|
||||
<Field name={fieldName}>
|
||||
{({ form }: any) => {
|
||||
return (
|
||||
<Dropdown
|
||||
onChange={async (
|
||||
value:
|
||||
| DropdownValue
|
||||
| Array<DropdownValue>
|
||||
) => {
|
||||
await form.setFieldValue(
|
||||
fieldName,
|
||||
value,
|
||||
true
|
||||
);
|
||||
}}
|
||||
onBlur={async () => {
|
||||
await form.setFieldTouched(
|
||||
fieldName,
|
||||
true
|
||||
);
|
||||
}}
|
||||
isMultiSelect={
|
||||
field.fieldType ===
|
||||
FormFieldSchemaType.MultiSelectDropdown
|
||||
}
|
||||
options={field.dropdownOptions || []}
|
||||
placeholder={field.placeholder || ''}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[fieldName]
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{field.fieldType === FormFieldSchemaType.LongText && (
|
||||
<Field name={fieldName}>
|
||||
@ -226,10 +227,45 @@ const BasicForm: Function = <T extends Object>(
|
||||
}}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[
|
||||
fieldName
|
||||
]
|
||||
fieldName
|
||||
]
|
||||
: ''
|
||||
}
|
||||
placeholder={field.placeholder || ''}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{field.fieldType === FormFieldSchemaType.Markdown && (
|
||||
<Field name={fieldName}>
|
||||
{({ form }: any) => {
|
||||
return (
|
||||
<>
|
||||
<Markdown
|
||||
onChange={async (text: string) => {
|
||||
await form.setFieldValue(
|
||||
fieldName,
|
||||
text,
|
||||
true
|
||||
);
|
||||
}}
|
||||
onBlur={async () => {
|
||||
await form.setFieldTouched(
|
||||
fieldName,
|
||||
true
|
||||
);
|
||||
}}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[
|
||||
fieldName
|
||||
]
|
||||
: ''
|
||||
}
|
||||
placeholder={field.placeholder || ''}
|
||||
@ -261,10 +297,10 @@ const BasicForm: Function = <T extends Object>(
|
||||
}}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[
|
||||
fieldName
|
||||
]
|
||||
fieldName
|
||||
]
|
||||
: false
|
||||
}
|
||||
/>
|
||||
@ -287,38 +323,38 @@ const BasicForm: Function = <T extends Object>(
|
||||
field.fieldType === FormFieldSchemaType.Date ||
|
||||
field.fieldType === FormFieldSchemaType.Port ||
|
||||
field.fieldType === FormFieldSchemaType.PositveNumber) && (
|
||||
<Field
|
||||
tabIndex={index + 1}
|
||||
name={fieldName}
|
||||
disabled={isDisabled || field.disabled}
|
||||
>
|
||||
{({ form }: FieldProps) => {
|
||||
return (
|
||||
<Input
|
||||
className="form-control"
|
||||
type={fieldType as 'text'}
|
||||
onChange={(text: string) => {
|
||||
form.setFieldValue(
|
||||
fieldName,
|
||||
text,
|
||||
true
|
||||
);
|
||||
}}
|
||||
onBlur={() => {
|
||||
form.setFieldTouched(fieldName, true);
|
||||
}}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[fieldName]
|
||||
: ''
|
||||
}
|
||||
placeholder={field.placeholder || ''}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
)}
|
||||
<Field
|
||||
tabIndex={index + 1}
|
||||
name={fieldName}
|
||||
disabled={isDisabled || field.disabled}
|
||||
>
|
||||
{({ form }: FieldProps) => {
|
||||
return (
|
||||
<Input
|
||||
className="form-control"
|
||||
type={fieldType as 'text'}
|
||||
onChange={(text: string) => {
|
||||
form.setFieldValue(
|
||||
fieldName,
|
||||
text,
|
||||
true
|
||||
);
|
||||
}}
|
||||
onBlur={() => {
|
||||
form.setFieldTouched(fieldName, true);
|
||||
}}
|
||||
initialValue={
|
||||
initialValues &&
|
||||
(initialValues as any)[fieldName]
|
||||
? (initialValues as any)[fieldName]
|
||||
: ''
|
||||
}
|
||||
placeholder={field.placeholder || ''}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
)}
|
||||
|
||||
<ErrorMessage
|
||||
className="mt-1 text-danger"
|
||||
@ -340,17 +376,15 @@ const BasicForm: Function = <T extends Object>(
|
||||
if (field.validation) {
|
||||
if (field.validation.minLength) {
|
||||
if (content.trim().length < field.validation?.minLength) {
|
||||
return `${field.title || name} cannot be less than ${
|
||||
field.validation.minLength
|
||||
} characters.`;
|
||||
return `${field.title || name} cannot be less than ${field.validation.minLength
|
||||
} characters.`;
|
||||
}
|
||||
}
|
||||
|
||||
if (field.validation.maxLength) {
|
||||
if (content.trim().length > field.validation?.maxLength) {
|
||||
return `${field.title || name} cannot be more than ${
|
||||
field.validation.maxLength
|
||||
} characters.`;
|
||||
return `${field.title || name} cannot be more than ${field.validation.maxLength
|
||||
} characters.`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,17 +426,15 @@ const BasicForm: Function = <T extends Object>(
|
||||
|
||||
if (field.validation.maxValue) {
|
||||
if (content > field.validation?.maxValue) {
|
||||
return `${field.title || name} should not be more than ${
|
||||
field.validation?.maxValue
|
||||
}.`;
|
||||
return `${field.title || name} should not be more than ${field.validation?.maxValue
|
||||
}.`;
|
||||
}
|
||||
}
|
||||
|
||||
if (field.validation.minValue) {
|
||||
if (content < field.validation?.minValue) {
|
||||
return `${field.title || name} should not be less than ${
|
||||
field.validation?.minValue
|
||||
}.`;
|
||||
return `${field.title || name} should not be less than ${field.validation?.minValue
|
||||
}.`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -429,7 +461,7 @@ const BasicForm: Function = <T extends Object>(
|
||||
field.validation?.toMatchField &&
|
||||
entity[field.validation?.toMatchField] &&
|
||||
(entity[field.validation?.toMatchField] as string).trim() !==
|
||||
content.trim()
|
||||
content.trim()
|
||||
) {
|
||||
return `${field.title} should match ${field.validation?.toMatchField}`;
|
||||
}
|
||||
@ -483,79 +515,79 @@ const BasicForm: Function = <T extends Object>(
|
||||
values: FormValues<T>
|
||||
) => void | object | Promise<FormikErrors<FormValues<T>>>) &
|
||||
Function = (values: FormValues<T>): FormikErrors<FormValues<T>> => {
|
||||
const errors: JSONObject = {};
|
||||
const entries: JSONObject = { ...values } as JSONObject;
|
||||
const errors: JSONObject = {};
|
||||
const entries: JSONObject = { ...values } as JSONObject;
|
||||
|
||||
for (const field of props.fields) {
|
||||
const name: string = field.overideFieldKey
|
||||
? field.overideFieldKey
|
||||
: (Object.keys(field.field)[0] as string);
|
||||
for (const field of props.fields) {
|
||||
const name: string = field.overideFieldKey
|
||||
? field.overideFieldKey
|
||||
: (Object.keys(field.field)[0] as string);
|
||||
|
||||
if (name in entries) {
|
||||
const content: string | undefined = entries[name]?.toString();
|
||||
if (name in entries) {
|
||||
const content: string | undefined = entries[name]?.toString();
|
||||
|
||||
// Check Required fields.
|
||||
const resultRequired: string | null = validateRequired(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultRequired) {
|
||||
errors[name] = resultRequired;
|
||||
// Check Required fields.
|
||||
const resultRequired: string | null = validateRequired(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultRequired) {
|
||||
errors[name] = resultRequired;
|
||||
}
|
||||
|
||||
// Check for valid email data.
|
||||
const resultValidateData: string | null = validateData(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultValidateData) {
|
||||
errors[name] = resultValidateData;
|
||||
}
|
||||
|
||||
const resultMatch: string | null = validateMatchField(
|
||||
content,
|
||||
field,
|
||||
entries
|
||||
);
|
||||
|
||||
if (resultMatch) {
|
||||
errors[name] = resultMatch;
|
||||
}
|
||||
|
||||
// check for length of content
|
||||
const result: string | null = validateLength(content, field);
|
||||
if (result) {
|
||||
errors[name] = result;
|
||||
}
|
||||
|
||||
// check for date
|
||||
const resultDate: string | null = validateDate(content, field);
|
||||
if (resultDate) {
|
||||
errors[name] = resultDate;
|
||||
}
|
||||
|
||||
// check for length of content
|
||||
const resultMaxMinValue: string | null =
|
||||
validateMaxValueAndMinValue(content, field);
|
||||
|
||||
if (resultMaxMinValue) {
|
||||
errors[name] = resultMaxMinValue;
|
||||
}
|
||||
} else if (field.required) {
|
||||
errors[name] = `${field.title || name} is required.`;
|
||||
}
|
||||
|
||||
// Check for valid email data.
|
||||
const resultValidateData: string | null = validateData(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultValidateData) {
|
||||
errors[name] = resultValidateData;
|
||||
}
|
||||
|
||||
const resultMatch: string | null = validateMatchField(
|
||||
content,
|
||||
field,
|
||||
entries
|
||||
);
|
||||
|
||||
if (resultMatch) {
|
||||
errors[name] = resultMatch;
|
||||
}
|
||||
|
||||
// check for length of content
|
||||
const result: string | null = validateLength(content, field);
|
||||
if (result) {
|
||||
errors[name] = result;
|
||||
}
|
||||
|
||||
// check for date
|
||||
const resultDate: string | null = validateDate(content, field);
|
||||
if (resultDate) {
|
||||
errors[name] = resultDate;
|
||||
}
|
||||
|
||||
// check for length of content
|
||||
const resultMaxMinValue: string | null =
|
||||
validateMaxValueAndMinValue(content, field);
|
||||
|
||||
if (resultMaxMinValue) {
|
||||
errors[name] = resultMaxMinValue;
|
||||
}
|
||||
} else if (field.required) {
|
||||
errors[name] = `${field.title || name} is required.`;
|
||||
}
|
||||
}
|
||||
|
||||
let customValidateResult: JSONObject = {};
|
||||
let customValidateResult: JSONObject = {};
|
||||
|
||||
if (props.onValidate) {
|
||||
customValidateResult = props.onValidate(values);
|
||||
}
|
||||
if (props.onValidate) {
|
||||
customValidateResult = props.onValidate(values);
|
||||
}
|
||||
|
||||
return { ...errors, ...customValidateResult } as FormikErrors<
|
||||
FormValues<T>
|
||||
>;
|
||||
};
|
||||
return { ...errors, ...customValidateResult } as FormikErrors<
|
||||
FormValues<T>
|
||||
>;
|
||||
};
|
||||
|
||||
const formRef: any = useRef<any>(null);
|
||||
|
||||
@ -669,21 +701,19 @@ const BasicForm: Function = <T extends Object>(
|
||||
|
||||
<div className={`col-lg-12 flex`}>
|
||||
<div
|
||||
className={`col-lg-${
|
||||
12 / (props.showAsColumns || 1)
|
||||
} ${
|
||||
(props.showAsColumns || 1) > 1
|
||||
className={`col-lg-${12 / (props.showAsColumns || 1)
|
||||
} ${(props.showAsColumns || 1) > 1
|
||||
? 'pr-10'
|
||||
: ''
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{props.fields &&
|
||||
props.fields.map(
|
||||
(field: DataField<T>, i: number) => {
|
||||
if (
|
||||
i %
|
||||
(props.showAsColumns ||
|
||||
1) ===
|
||||
(props.showAsColumns ||
|
||||
1) ===
|
||||
0
|
||||
) {
|
||||
return getFormField(
|
||||
@ -698,13 +728,11 @@ const BasicForm: Function = <T extends Object>(
|
||||
</div>
|
||||
{(props.showAsColumns || 1) > 1 && (
|
||||
<div
|
||||
className={`col-lg-${
|
||||
12 / (props.showAsColumns || 1)
|
||||
} ${
|
||||
(props.showAsColumns || 1) > 1
|
||||
className={`col-lg-${12 / (props.showAsColumns || 1)
|
||||
} ${(props.showAsColumns || 1) > 1
|
||||
? 'pl-10'
|
||||
: ''
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{props.fields &&
|
||||
props.fields.map(
|
||||
@ -714,8 +742,8 @@ const BasicForm: Function = <T extends Object>(
|
||||
) => {
|
||||
if (
|
||||
i %
|
||||
(props.showAsColumns ||
|
||||
1) !==
|
||||
(props.showAsColumns ||
|
||||
1) !==
|
||||
0
|
||||
) {
|
||||
return getFormField(
|
||||
|
@ -17,6 +17,7 @@ enum FormFieldSchemaType {
|
||||
Checkbox,
|
||||
Port,
|
||||
EncryptedText,
|
||||
Markdown
|
||||
}
|
||||
|
||||
export default FormFieldSchemaType;
|
||||
|
74
CommonUI/src/Components/Markdown.tsx/Markdown.tsx
Normal file
74
CommonUI/src/Components/Markdown.tsx/Markdown.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import MDEditor from '@uiw/react-md-editor';
|
||||
|
||||
export interface ComponentProps {
|
||||
initialValue?: undefined | string;
|
||||
onClick?: undefined | (() => void);
|
||||
placeholder?: undefined | string;
|
||||
className?: undefined | string;
|
||||
onChange?: undefined | ((value: string) => void);
|
||||
value?: string | undefined;
|
||||
onFocus?: (() => void) | undefined;
|
||||
onBlur?: (() => void) | undefined;
|
||||
}
|
||||
|
||||
const Markdown: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [value, setValue] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (props.initialValue) {
|
||||
setValue(props.initialValue);
|
||||
}
|
||||
|
||||
if (props.value) {
|
||||
setValue(props.value);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(props.value ? props.value : '');
|
||||
}, [props.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.initialValue) {
|
||||
setValue(props.initialValue);
|
||||
}
|
||||
}, [props.initialValue]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex ${props.className}`}
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
props.onFocus && props.onFocus();
|
||||
}}
|
||||
>
|
||||
<MDEditor
|
||||
height={200}
|
||||
value={value}
|
||||
onBlur={() => {
|
||||
if (props.onBlur) {
|
||||
props.onBlur();
|
||||
}
|
||||
}}
|
||||
placeholder={props.placeholder}
|
||||
onChange={(text: string | undefined) => {
|
||||
if (text) {
|
||||
setValue(text);
|
||||
if (props.onChange) {
|
||||
props.onChange(text);
|
||||
}
|
||||
}
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Markdown;
|
@ -17,6 +17,7 @@ enum FieldType {
|
||||
Actions = 'Actions',
|
||||
Boolean = 'Boolean',
|
||||
Entity = 'Entity',
|
||||
Markdown = 'Markdown'
|
||||
}
|
||||
|
||||
export default FieldType;
|
||||
|
Loading…
Reference in New Issue
Block a user