diff --git a/packages/webapp/src/components/Forms/FRichEditor.tsx b/packages/webapp/src/components/Forms/FRichEditor.tsx new file mode 100644 index 0000000000..d490f87f50 --- /dev/null +++ b/packages/webapp/src/components/Forms/FRichEditor.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { FieldConfig, FieldProps } from 'formik'; +import { Field } from '@blueprintjs-formik/core'; +import { RichEditor, RichEditorProps } from '../../components/RichEditor'; + +export interface FRichEditorProps + extends Omit, + RichEditorProps { + name: string; + value?: string; +} + +interface FieldToRichEditorProps + extends FieldProps, + Omit {} + +/** + * Transformes the field props to `RichEditor` props. + * @param {FieldToRichEditorProps} + * @returns {HTMLSelectProps} + */ +function fieldToRichEditor({ + field: { onBlur: onFieldBlur, ...field }, + form: { touched, errors, ...form }, + ...props +}: FieldToRichEditorProps): RichEditorProps { + return { + ...field, + ...props, + onChange: (value: string) => { + form.setFieldValue(field.name, value); + }, + }; +} + +/** + * Transformes field props to `RichEditor` props. + * @param {FieldToRichEditorProps} + * @returns {JSX.Element} + */ +function FieldToRichEditor({ ...props }: FieldToRichEditorProps): JSX.Element { + return ; +} + +/** + * Rich editor wrapper to bind with Formik. + * @param {FRichEditorProps} props - + * @returns {JSX.Element} + */ +export function FRichEditor({ ...props }: FRichEditorProps): JSX.Element { + return ; +} diff --git a/packages/webapp/src/components/Forms/index.tsx b/packages/webapp/src/components/Forms/index.tsx index d4fb2aec08..c638ac0299 100644 --- a/packages/webapp/src/components/Forms/index.tsx +++ b/packages/webapp/src/components/Forms/index.tsx @@ -4,4 +4,5 @@ export * from './FMoneyInputGroup'; export * from './BlueprintFormik'; export * from './InputPrependText'; export * from './InputPrependButton'; -export * from './MoneyInputGroup'; \ No newline at end of file +export * from './MoneyInputGroup'; +export * from './FRichEditor'; \ No newline at end of file diff --git a/packages/webapp/src/components/RichEditor/RichEditor.style.scss b/packages/webapp/src/components/RichEditor/RichEditor.style.scss new file mode 100644 index 0000000000..942fdf81eb --- /dev/null +++ b/packages/webapp/src/components/RichEditor/RichEditor.style.scss @@ -0,0 +1,66 @@ +/* Basic editor styles */ +.tiptap { + color: #222; + + &:focus-visible { + outline: none; + } + + >*+* { + margin-top: 0.75em; + } + + ul, + ol { + padding: 0 1rem; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } + + code { + background: rgba(#ffffff, 0.1); + color: rgba(#ffffff, 0.6); + border: 1px solid rgba(#ffffff, 0.1); + border-radius: 0.5rem; + padding: 0.2rem; + } + + pre { + background: rgba(#ffffff, 0.1); + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + + code { + color: inherit; + padding: 0; + background: none; + font-size: 0.8rem; + border: none; + } + } + + img { + max-width: 100%; + height: auto; + } + + blockquote { + margin-left: 0; + padding-left: 1rem; + border-left: 2px solid rgba(#ffffff, 0.4); + + hr { + border: none; + border-top: 2px solid rgba(#ffffff, 0.1); + margin: 2rem 0; + } + } +} \ No newline at end of file diff --git a/packages/webapp/src/components/RichEditor/RichEditor.tsx b/packages/webapp/src/components/RichEditor/RichEditor.tsx new file mode 100644 index 0000000000..da82fb09fb --- /dev/null +++ b/packages/webapp/src/components/RichEditor/RichEditor.tsx @@ -0,0 +1,58 @@ +// @ts-nocheck +import { Color } from '@tiptap/extension-color'; +import ListItem from '@tiptap/extension-list-item'; +import TextStyle from '@tiptap/extension-text-style'; +import { EditorProvider } from '@tiptap/react'; +import StarterKit from '@tiptap/starter-kit'; +import { useUncontrolled } from '@/hooks/useUncontrolled'; +import { Box } from '../Layout/Box'; +import './RichEditor.style.scss'; + +const extensions = [ + Color.configure({ types: [TextStyle.name, ListItem.name] }), + TextStyle.configure({ types: [ListItem.name] }), + StarterKit.configure({ + bulletList: { + keepMarks: true, + keepAttributes: false, + }, + orderedList: { + keepMarks: true, + keepAttributes: false, + }, + }), +]; + +export interface RichEditorProps { + value?: string; + initialValue?: string; + onChange?: (value: string) => void; + className?: string; +} +export const RichEditor = ({ + value, + initialValue, + onChange, + className, +}: RichEditorProps) => { + const [content, handleChange] = useUncontrolled({ + value, + initialValue, + onChange, + finalValue: '', + }); + + const handleBlur = ({ editor }) => { + handleChange(editor.getHTML()); + }; + + return ( + + + + ); +}; diff --git a/packages/webapp/src/components/RichEditor/index.ts b/packages/webapp/src/components/RichEditor/index.ts new file mode 100644 index 0000000000..226b701f3c --- /dev/null +++ b/packages/webapp/src/components/RichEditor/index.ts @@ -0,0 +1 @@ +export * from './RichEditor'; \ No newline at end of file diff --git a/packages/webapp/src/containers/SendMailNotification/RichEditor.tsx b/packages/webapp/src/containers/SendMailNotification/RichEditor.tsx new file mode 100644 index 0000000000..e540fd6da6 --- /dev/null +++ b/packages/webapp/src/containers/SendMailNotification/RichEditor.tsx @@ -0,0 +1,63 @@ +// @ts-nocheck +import './styles.scss'; +import { Color } from '@tiptap/extension-color'; +import ListItem from '@tiptap/extension-list-item'; +import TextStyle from '@tiptap/extension-text-style'; +import { EditorProvider } from '@tiptap/react'; +import StarterKit from '@tiptap/starter-kit'; +import { Box } from '@/components'; +import styled from 'styled-components'; +import { useUncontrolled } from '@/hooks/useUncontrolled'; + +const extensions = [ + Color.configure({ types: [TextStyle.name, ListItem.name] }), + TextStyle.configure({ types: [ListItem.name] }), + StarterKit.configure({ + bulletList: { + keepMarks: true, + keepAttributes: false, + }, + orderedList: { + keepMarks: true, + keepAttributes: false, + }, + }), +]; + +export interface RichEditorProps { + value?: string; + initialValue?: string; + onChange?: (value: string) => void; + className?: string; +} +export const RichEditor = ({ + value, + initialValue, + onChange, + className, +}: RichEditorProps) => { + const [content, handleChange] = useUncontrolled({ + value, + initialValue, + finalValue: '', + onChange, + }); + + return ( + + + + ); +}; + +const Root = styled(Box)` + padding: 15px; + border: 1px solid #dedfe9; + border-top: 0; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +`; diff --git a/packages/webapp/src/containers/SendMailNotification/SendMailNotificationForm.tsx b/packages/webapp/src/containers/SendMailNotification/SendMailNotificationForm.tsx index a23b108afd..ec5263c2f9 100644 --- a/packages/webapp/src/containers/SendMailNotification/SendMailNotificationForm.tsx +++ b/packages/webapp/src/containers/SendMailNotification/SendMailNotificationForm.tsx @@ -1,8 +1,15 @@ // @ts-nocheck import { Form, useFormikContext } from 'formik'; -import { FFormGroup, FInputGroup, FMultiSelect } from '@/components'; +import { + FFormGroup, + FInputGroup, + FMultiSelect, + FRichEditor, + FSwitch, + Hint, +} from '@/components'; import styled from 'styled-components'; -import { Button, Classes, Intent } from '@blueprintjs/core'; +import { Button, Classes, Intent, Position } from '@blueprintjs/core'; import { saveInvoke } from '@/utils'; interface SendMailNotificationFormProps { @@ -24,25 +31,47 @@ export function SendMailNotificationForm({ + } name={'from'} inline={true} fastField={true} > @@ -56,6 +85,12 @@ export function SendMailNotificationForm({ + + + + + +
@@ -82,16 +117,33 @@ export function SendMailNotificationForm({ ); } +const AttachFormGroup = styled(FFormGroup)` + background: #f8f9fb; + margin-top: 0.6rem; + padding: 4px 14px; + border-radius: 5px; + border: 1px solid #dcdcdd; +`; + +const MailMessageEditor = styled(FRichEditor)` + padding: 15px; + border: 1px solid #dedfe9; + border-top: 0; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +`; + const HeaderBox = styled('div')` border-top-right-radius: 5px; border-top-left-radius: 5px; border: 1px solid #dddfe9; - padding: 15px; + border-bottom: 2px solid #eaeaef; + padding: 6px 15px; .bp4-form-group { margin: 0; - padding-top: 12px; - padding-bottom: 12px; + padding-top: 8px; + padding-bottom: 8px; &:not(:last-of-type) { border-bottom: 1px solid #dddfe9; @@ -114,5 +166,19 @@ const HeaderBox = styled('div')` } .bp4-input { + border-color: transparent; + padding: 0; + + &:focus, + &.bp4-active { + box-shadow: 0 0 0 0; + } + } + + .bp4-input-ghost { + margin-top: 5px; + } + .bp4-tag-input-values { + margin: 0; } `;