1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

added format buttons

This commit is contained in:
Fedor Katurov 2021-02-26 14:54:40 +07:00
parent 3fdbf6208b
commit 5cf3d22466
9 changed files with 158 additions and 15 deletions

View file

@ -1,4 +1,12 @@
import React, { FC, KeyboardEventHandler, memo, useCallback, useEffect, useMemo } from 'react';
import React, {
FC,
KeyboardEventHandler,
memo,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { Textarea } from '~/components/input/Textarea';
import styles from './styles.module.scss';
import { Filler } from '~/components/containers/Filler';
@ -19,8 +27,9 @@ import { getFileType } from '~/utils/uploader';
import { useRandomPhrase } from '~/constants/phrases';
import { ERROR_LITERAL } from '~/constants/errors';
import { CommentFormAttaches } from '~/components/comment/CommentFormAttaches';
import { CommentFormAttachButtons } from '~/components/comment/CommentFormButtons';
import { CommentFormAttachButtons } from '~/components/comment/CommentFormAttachButtons';
import { CommentFormDropzone } from '~/components/comment/CommentFormDropzone';
import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormatButtons';
const mapStateToProps = (state: IState) => ({
node: selectNode(state),
@ -51,12 +60,11 @@ const CommentFormUnconnected: FC<IProps> = memo(
uploadUploadFiles,
nodeCancelCommentEdit,
}) => {
const [textarea, setTextarea] = useState<HTMLTextAreaElement>();
const comment = useMemo(() => comment_data[id], [comment_data, id]);
const onUpload = useCallback(
(files: File[]) => {
console.log(files);
const items: IFileWithUUID[] = files.map(
(file: File): IFileWithUUID => ({
file,
@ -182,6 +190,7 @@ const CommentFormUnconnected: FC<IProps> = memo(
disabled={is_sending_comment}
placeholder={placeholder}
minRows={2}
setRef={setTextarea}
/>
{comment.error && (
@ -203,6 +212,7 @@ const CommentFormUnconnected: FC<IProps> = memo(
<Group horizontal className={styles.buttons}>
<CommentFormAttachButtons onUpload={onUpload} />
<CommentFormFormatButtons element={textarea} handler={onInput} />
<Filler />

View file

@ -24,7 +24,6 @@
background: transparentize(black, 0.8);
padding: $gap / 2;
border-radius: 0 0 $radius $radius;
flex-wrap: wrap;
}
.uploads {

View file

@ -0,0 +1,59 @@
import React, { FC, useCallback } from 'react';
import { ButtonGroup } from '~/components/input/ButtonGroup';
import { Button } from '~/components/input/Button';
import { useFormatWrapper } from '~/utils/hooks/useFormatWrapper';
import styles from './styles.module.scss';
interface IProps {
element: HTMLTextAreaElement;
handler: (val: string) => void;
}
const CommentFormFormatButtons: FC<IProps> = ({ element, handler }) => {
const wrap = useCallback(
(prefix = '', suffix = '') => useFormatWrapper(element, handler, prefix, suffix),
[element, handler]
);
return (
<ButtonGroup className={styles.wrap}>
<Button
onClick={wrap('**', '**')}
iconLeft="bold"
size="small"
color="gray"
iconOnly
type="button"
/>
<Button
onClick={wrap('*', '*')}
iconLeft="italic"
size="small"
color="gray"
iconOnly
type="button"
/>
<Button
onClick={wrap('## ', '')}
iconLeft="title"
size="small"
color="gray"
iconOnly
type="button"
/>
<Button
onClick={wrap('[ссылка](', ')')}
iconLeft="link"
size="small"
color="gray"
iconOnly
type="button"
/>
</ButtonGroup>
);
};
export { CommentFormFormatButtons };

View file

@ -0,0 +1,8 @@
@import "~/styles/variables.scss";
.wrap {
display: flex;
flex-wrap: wrap;
height: 32px;
overflow: hidden;
}

View file

@ -1,6 +1,9 @@
import React, { HTMLAttributes } from 'react';
import styles from './styles.module.scss';
import classNames from 'classnames';
type IProps = HTMLAttributes<HTMLDivElement> & {};
export const ButtonGroup = ({ children }: IProps) => <div className={styles.wrap}>{children}</div>;
export const ButtonGroup = ({ children, className }: IProps) => (
<div className={classNames(styles.wrap, className)}>{children}</div>
);

View file

@ -1,6 +1,6 @@
import React, {
ChangeEvent,
LegacyRef,
DetailedHTMLProps,
memo,
TextareaHTMLAttributes,
useCallback,
@ -14,7 +14,10 @@ import autosize from 'autosize';
import styles from '~/styles/common/inputs.module.scss';
import { Icon } from '../Icon';
type IProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
type IProps = DetailedHTMLProps<
TextareaHTMLAttributes<HTMLTextAreaElement>,
HTMLTextAreaElement
> & {
value: string;
placeholder?: string;
rows?: number;
@ -26,6 +29,7 @@ type IProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
status?: 'error' | 'success' | '';
title?: string;
seamless?: boolean;
setRef?: (r: HTMLTextAreaElement) => void;
};
const Textarea = memo<IProps>(
@ -40,12 +44,12 @@ const Textarea = memo<IProps>(
status = '',
seamless,
value,
setRef,
...props
}) => {
const [rows, setRows] = useState(minRows || 1);
const [focused, setFocused] = useState(false);
const textarea: LegacyRef<HTMLTextAreaElement> = useRef(null);
const ref = useRef<HTMLTextAreaElement>();
const onInput = useCallback(
({ target }: ChangeEvent<HTMLTextAreaElement>) => handler(target.value),
@ -56,12 +60,13 @@ const Textarea = memo<IProps>(
const onBlur = useCallback(() => setFocused(false), [setFocused]);
useEffect(() => {
if (!textarea.current) return;
if (!ref.current) return;
autosize(textarea.current);
autosize(ref.current);
setRef(ref.current);
return () => autosize.destroy(textarea.current);
}, [textarea.current]);
return () => autosize.destroy(ref.current);
}, [ref.current]);
return (
<div
@ -80,7 +85,7 @@ const Textarea = memo<IProps>(
placeholder={placeholder}
className={classNames(styles.textarea, className)}
onChange={onInput}
ref={textarea}
ref={ref}
onFocus={onFocus}
onBlur={onBlur}
style={{

View file

@ -212,6 +212,26 @@ const Sprites: FC<{}> = () => (
<path d="M14 12c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-9c-4.97 0-9 4.03-9 9H0l4 4 4-4H5c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.51 0-2.91-.49-4.06-1.3l-1.42 1.44C8.04 20.3 9.94 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9z" />
</g>
<g id="bold" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z" />
</g>
<g id="italic" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z" />
</g>
<g id="link" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z" />
</g>
<g id="title" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M5 4v3h5.5v12h3V7H19V4z" />
</g>
<g id="sad" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M0 0h24v24H0V0z" fill="none" />

View file

@ -0,0 +1,39 @@
import { MouseEventHandler, useCallback } from 'react';
export const useFormatWrapper = (
target: HTMLTextAreaElement,
onChange: (val: string) => void,
prefix = '',
suffix = ''
) => {
return useCallback<MouseEventHandler>(
event => {
event.preventDefault();
if (!target) return;
const start = target.selectionStart;
const end = target.selectionEnd;
const selection = target.value.substring(start, end);
const replacement = prefix + selection + suffix;
onChange(
target.value.substring(0, start) +
replacement +
target.value.substring(end, target.value.length)
);
target.focus();
setTimeout(() => {
if (start === end) {
target.selectionEnd = end + prefix.length;
} else {
target.selectionEnd = end + prefix.length + suffix.length;
}
}, 0);
},
[target, onChange, prefix, suffix]
);
};