react-artasys-ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/__tests__/static.test.js +0 -0
  4. package/babel.config.json +3 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.LICENSE.txt +9 -0
  7. package/dist/index.js.map +1 -0
  8. package/lib/File/File.d.ts +17 -0
  9. package/lib/File/index.d.ts +3 -0
  10. package/lib/Form/Element/Element.d.ts +13 -0
  11. package/lib/Form/Element/index.d.ts +3 -0
  12. package/lib/Form/Form.d.ts +7 -0
  13. package/lib/Form/index.d.ts +5 -0
  14. package/lib/Form/types.d.ts +7 -0
  15. package/lib/Form/useForm.d.ts +17 -0
  16. package/lib/Input/Input.d.ts +7 -0
  17. package/lib/Input/index.d.ts +2 -0
  18. package/lib/Spinner/Spinner.d.ts +6 -0
  19. package/lib/Spinner/index.d.ts +2 -0
  20. package/lib/TextArea/TextArea.d.ts +7 -0
  21. package/lib/TextArea/index.d.ts +2 -0
  22. package/lib/index.d.ts +9 -0
  23. package/package.json +52 -0
  24. package/src/File/File.tsx +84 -0
  25. package/src/File/index.tsx +9 -0
  26. package/src/Form/Element/Element.tsx +42 -0
  27. package/src/Form/Element/index.tsx +7 -0
  28. package/src/Form/Element/style.module.css +91 -0
  29. package/src/Form/Form.tsx +28 -0
  30. package/src/Form/index.tsx +8 -0
  31. package/src/Form/style.module.css +30 -0
  32. package/src/Form/types.ts +10 -0
  33. package/src/Form/useForm.tsx +68 -0
  34. package/src/Input/Input.tsx +45 -0
  35. package/src/Input/index.tsx +3 -0
  36. package/src/Input/style.module.css +16 -0
  37. package/src/Spinner/Spinner.tsx +13 -0
  38. package/src/Spinner/index.tsx +3 -0
  39. package/src/Spinner/style.module.css +74 -0
  40. package/src/TextArea/TextArea.tsx +57 -0
  41. package/src/TextArea/index.tsx +3 -0
  42. package/src/index.ts +28 -0
  43. package/tsconfig.json +118 -0
  44. package/types.d.ts +1 -0
  45. package/webpack.config.js +60 -0
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "react-artasys-ui",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "scripts": {
8
+ "test": "jest",
9
+ "build": "webpack",
10
+ "copy-css": "copyfiles -u 1 src/**/*.module.css lib/"
11
+ },
12
+ "jest": {
13
+ "transform": {
14
+ "^.+\\.[t|j]sx?$": "babel-jest"
15
+ }
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/SergoMorello/react.artasys.ui.git"
20
+ },
21
+ "keywords": [],
22
+ "author": "Sergey Serov",
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/SergoMorello/react.artasys.ui/issues"
26
+ },
27
+ "homepage": "https://github.com/SergoMorello/react.artasys.ui#readme",
28
+ "devDependencies": {
29
+ "@babel/preset-env": "^7.22.20",
30
+ "@types/jest": "^29.5.0",
31
+ "@types/react": "^18.2.0",
32
+ "@types/react-dom": "^18.2.0",
33
+ "autoprefixer": "^10.4.16",
34
+ "babel-jest": "^29.5.0",
35
+ "copyfiles": "^2.4.1",
36
+ "css-loader": "^6.8.1",
37
+ "jest": "^29.5.0",
38
+ "postcss-loader": "^7.3.3",
39
+ "style-loader": "^3.3.3",
40
+ "ts-loader": "^9.5.0",
41
+ "typescript": "^5.2.2",
42
+ "typescript-plugin-css-modules": "^5.0.1",
43
+ "webpack-cli": "^5.1.4"
44
+ },
45
+ "dependencies": {
46
+ "react-hook-form": "^7.47.0"
47
+ },
48
+ "peerDependencies": {
49
+ "react": "^18.2.0",
50
+ "react-dom": "^18.2.0"
51
+ }
52
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ useEffect,
3
+ useRef,
4
+ MouseEvent,
5
+ AllHTMLAttributes
6
+ } from "react";
7
+
8
+ export type TFileMime = 'image/png' | 'image/jpg' | 'image/jpeg' | 'image/gif';
9
+
10
+ export type TFileData = {
11
+ name: string;
12
+ type: TFileMime;
13
+ size: number;
14
+ mime: TFileMime;
15
+ data: string;
16
+ base64?: string | null | ArrayBuffer;
17
+ };
18
+
19
+ interface TFile<T = any> extends Omit<AllHTMLAttributes<T>, 'onChange' | 'accept'> {
20
+ onChange?: (data: TFileData) => void;
21
+ multiple?: boolean;
22
+ accept?: TFileMime[];
23
+ };
24
+
25
+ const File = ({onChange, children, multiple, accept, ...props}: TFile) => {
26
+ const selector = useRef<HTMLInputElement>();
27
+
28
+ const handleChange = (e: Event) => {
29
+ const target = e.target as HTMLInputElement
30
+ if (target.files) {
31
+ for(const file of target.files) {
32
+ const reader = new FileReader();
33
+ reader.onload = (evt) => {
34
+ const data = evt.target?.result?.toString().split(';base64,');
35
+ const mime = (data?.[0].replace('data:', '') ?? '') as TFileMime;
36
+ const contentData = data?.[1] ?? '';
37
+ if (typeof onChange === 'function') {
38
+ selector.current!.value = '';
39
+ onChange({
40
+ name: file.name,
41
+ type: file.type as TFileMime,
42
+ size: file.size,
43
+ mime: mime,
44
+ data: contentData,
45
+ base64: evt.target?.result
46
+ })
47
+ }
48
+ };
49
+ reader.readAsDataURL(file);
50
+ }
51
+ }
52
+ };
53
+
54
+ const handleDialog = (e: MouseEvent<HTMLDivElement>) => {
55
+ selector.current!.click();
56
+ e.preventDefault();
57
+ };
58
+
59
+ const mount = () => {
60
+ const fileSelector = document.createElement('input');
61
+ fileSelector.setAttribute('type', 'file');
62
+ fileSelector.onchange = handleChange;
63
+ if (accept && accept.length > 0) {
64
+ fileSelector.setAttribute('accept', accept.join(','));
65
+ }
66
+
67
+ if (multiple) {
68
+ fileSelector.setAttribute('multiple', 'multiple');
69
+ }
70
+ return fileSelector;
71
+ };
72
+
73
+ useEffect(() => {
74
+ selector.current = mount();
75
+ return () => {
76
+ selector.current?.remove();
77
+ }
78
+ }, []);
79
+ return(<div {...props} onClick={handleDialog}>
80
+ {children}
81
+ </div>)
82
+ };
83
+
84
+ export default File;
@@ -0,0 +1,9 @@
1
+ import File,{
2
+ TFileData,
3
+ TFileMime
4
+ } from "./File";
5
+ export type {
6
+ TFileData,
7
+ TFileMime
8
+ };
9
+ export default File;
@@ -0,0 +1,42 @@
1
+ import {
2
+ useState,
3
+ useEffect,
4
+ AllHTMLAttributes,
5
+ ReactElement
6
+ } from "react";
7
+ import styles from "./style.module.css";
8
+
9
+ export interface IElement<T = any> extends Omit<AllHTMLAttributes<T>, 'children'> {
10
+ children?: ((props: AllHTMLAttributes<T>) => ReactElement) | AllHTMLAttributes<T>["children"];
11
+ error?: string;
12
+ disabled?: boolean;
13
+ placeholder?: string;
14
+ styleContainer?: React.HTMLAttributes<T>["style"];
15
+ classNameContainer?: string;
16
+ beforeElement?: React.ReactElement;
17
+ afterElement?: React.ReactElement;
18
+ }
19
+
20
+ const Element = ({children, beforeElement, afterElement, error, placeholder, disabled, styleContainer, classNameContainer, ...props}: IElement) => {
21
+ const [currentError, setCurrentError] = useState('');
22
+
23
+ useEffect(() => {
24
+ setCurrentError(error ?? '');
25
+ },[error]);
26
+
27
+ return(<>
28
+ <label
29
+ className={
30
+ styles['container'] + (currentError ? ' ' + styles['error'] : '') + (disabled ? ' ' + styles['disabled'] : '') + (classNameContainer ? ' ' + classNameContainer : '')}
31
+ style={styleContainer}
32
+ >
33
+ {beforeElement}
34
+ {typeof children === 'function' ? children(props) : null}
35
+ {afterElement}
36
+ {placeholder && <span className={styles['placeholder']}>{placeholder}</span>}
37
+ </label>
38
+ {currentError && <div className={styles['error']}>{currentError}</div>}
39
+ </>);
40
+ }
41
+
42
+ export default Element;
@@ -0,0 +1,7 @@
1
+ import Element,{
2
+ IElement
3
+ } from "./Element";
4
+ export type {
5
+ IElement
6
+ }
7
+ export default Element;
@@ -0,0 +1,91 @@
1
+ /* Input */
2
+
3
+ .container {
4
+ display: flex;
5
+ align-items: center;
6
+ width: 100%;
7
+ min-height: 50px;
8
+ position: relative;
9
+ border: 1px solid #CCCCCC;
10
+ border-radius: 3px;
11
+ z-index: 0;
12
+ }
13
+
14
+ .container > input, .container > textarea {
15
+ width: 100% !important;
16
+ min-height: 100% !important;
17
+ max-height: 500px !important;
18
+ max-height: 50dvh !important;
19
+ padding: 10px 5px !important;
20
+ margin: 0 !important;
21
+ font-size: 1.3em !important;
22
+ border: 0 !important;
23
+ box-sizing: border-box !important;
24
+ background-color: transparent !important;
25
+ outline: none;
26
+ resize: none;
27
+ }
28
+
29
+ .container.disabled {
30
+ background-color: #EFEFEF;
31
+ }
32
+
33
+ .container > input:-webkit-autofill,
34
+ .container > input:-webkit-autofill:hover,
35
+ .container > input:-webkit-autofill:focus,
36
+ .container > input:-webkit-autofill:active{
37
+ -webkit-background-clip: text;
38
+ }
39
+
40
+ .container > input:focus {
41
+ outline: none;
42
+ }
43
+
44
+ .container > .placeholder {
45
+ position: absolute;
46
+ display: flex;
47
+ justify-content: center;
48
+ align-items: center;
49
+ top: 0;
50
+ left: 5px;
51
+ color: #7A7A73;
52
+ font-size: 1.2em;
53
+ z-index: -1;
54
+ user-select: none;
55
+ transition: transform 0.3s;
56
+ }
57
+
58
+ .container > .placeholder::before {
59
+ content: "";
60
+ position: absolute;
61
+ display: block;
62
+ width: 100%;
63
+ height: 5px;
64
+ background-color: #FFFFFF;
65
+ z-index: -1;
66
+ }
67
+
68
+ .container > input:required ~ .placeholder::after {
69
+ content: "*";
70
+ }
71
+
72
+ .container > input:focus ~ .placeholder,
73
+ .container > textarea:focus ~ .placeholder,
74
+ .container > input:not([value=""]) ~ .placeholder,
75
+ .container > textarea:not(:empty) ~ .placeholder,
76
+ .container > input:not(:empty) ~ .placeholder {
77
+ transform: translate(-10%, -55%) scale(0.8);
78
+ z-index: 0;
79
+ }
80
+
81
+ .container.error {
82
+ border-color: #FF6D6D;
83
+ }
84
+
85
+ .container.error > .placeholder {
86
+ color: #FF6D6D;
87
+ }
88
+
89
+ .error {
90
+ color: #FF6D6D;
91
+ }
@@ -0,0 +1,28 @@
1
+ import { FormHTMLAttributes, ReactNode, FormEvent } from 'react';
2
+ import styles from './style.module.css';
3
+
4
+ import Spinner from '../Spinner';
5
+
6
+ interface IForm extends FormHTMLAttributes<HTMLFormElement> {
7
+ children: ReactNode;
8
+ wait?: boolean;
9
+ }
10
+
11
+ const Form = ({children, wait, className, onSubmit, ...props}: IForm) => {
12
+
13
+ const submit = (e: FormEvent<HTMLFormElement>) => {
14
+ e.preventDefault();
15
+ if (typeof onSubmit === 'function') {
16
+ onSubmit(e);
17
+ }
18
+ };
19
+
20
+ return(<form {...props} onSubmit={submit} className={styles['container'] + (wait ? ' ' + styles['wait'] : '') + (className ? ' ' + className : '')}>
21
+ {children}
22
+ <div className={styles['wait-indicator']}>
23
+ <Spinner size="large" color="contrast"/>
24
+ </div>
25
+ </form>);
26
+ };
27
+
28
+ export default Form;
@@ -0,0 +1,8 @@
1
+ import Form from "./Form";
2
+ import useForm from "./useForm";
3
+ import Element from "./Element";
4
+ export {
5
+ useForm,
6
+ Element as FormElement
7
+ };
8
+ export default Form;
@@ -0,0 +1,30 @@
1
+ /* Form */
2
+
3
+ .container {
4
+ flex: 1;
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: center;
8
+ gap: 10px;
9
+ }
10
+
11
+ .container > .wait-indicator {
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ position: absolute;
16
+ top: 0;
17
+ left: 0;
18
+ width: 100%;
19
+ height: 100%;
20
+ background-color: #FFFFFF9F;
21
+ backdrop-filter: blur(2px);
22
+ z-index: -1;
23
+ opacity: 0;
24
+ transition: opacity 0.3s;
25
+ }
26
+
27
+ .container.wait > .wait-indicator {
28
+ z-index: 1;
29
+ opacity: 1;
30
+ }
@@ -0,0 +1,10 @@
1
+ import type {
2
+ FieldValues,
3
+ } from "react-hook-form";
4
+
5
+ export type TError<TFieldValues extends FieldValues> = {
6
+ errors?: {
7
+ [key in keyof TFieldValues]: string[];
8
+ };
9
+ message: string;
10
+ }
@@ -0,0 +1,68 @@
1
+ import { useState, FunctionComponent } from "react";
2
+ import type { TError } from "./types";
3
+ import styles from "./style.module.css";
4
+
5
+ import {
6
+ useForm as useHookForm,
7
+ UseFormReturn,
8
+ RegisterOptions,
9
+ UseFormRegisterReturn,
10
+ FieldValues,
11
+ UseFormRegister,
12
+ FieldPath
13
+ } from "react-hook-form";
14
+
15
+ type TuseErrors<TFieldValues extends FieldValues = FieldValues> = UseFormReturn<TFieldValues> & {
16
+ GlobalError: FunctionComponent;
17
+ setErrors: (e: TError<TFieldValues>) => void;
18
+ };
19
+
20
+ const useForm = <TFieldValues extends FieldValues = FieldValues>(): TuseErrors<TFieldValues> => {
21
+ const [globalError, setGlobalError] = useState<TError<TFieldValues> | null>();
22
+
23
+ const form = useHookForm<TFieldValues>({
24
+ reValidateMode: 'onChange'
25
+ });
26
+
27
+ const register = <TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>(name: TFieldName, options?: RegisterOptions<TFieldValues, TFieldName>): UseFormRegisterReturn<TFieldName> => {
28
+ const inputRegister = form.register<TFieldName>(name, options);
29
+ const onChange = (e: {
30
+ target: any;
31
+ type?: any;
32
+ }) => {
33
+ setGlobalError(null);
34
+ form.clearErrors(name);
35
+ return inputRegister.onChange(e);
36
+ };
37
+ const errors = form?.formState.errors;
38
+ return {...inputRegister, onChange, error: errors?.[name]?.message?.toString()} as UseFormRegisterReturn<TFieldName>;
39
+ };
40
+
41
+ const setErrors = (e: TError<TFieldValues>) => {
42
+ if (e.errors) {
43
+ Object.keys(e.errors).forEach((field) => form.setError(field as FieldPath<TFieldValues>, {message: e.errors?.[field]?.[0]}));
44
+ }else{
45
+ setGlobalError(e);
46
+ }
47
+ };
48
+
49
+ const GlobalError = () => (<>{(globalError && !globalError.errors) && <div className={styles['global-error']}>{globalError.message}</div>}</>);
50
+
51
+ return {
52
+ ...form,
53
+ register,
54
+ GlobalError,
55
+ setErrors
56
+ };
57
+ };
58
+
59
+ export const validate = {
60
+ email: {
61
+ pattern: {
62
+ value: /\S+@\S+\.\S+/,
63
+ message: "Неверный адрес электронной почты",
64
+ }
65
+ }
66
+ };
67
+
68
+ export default useForm;
@@ -0,0 +1,45 @@
1
+ import {
2
+ forwardRef,
3
+ useState,
4
+ ChangeEvent,
5
+ useEffect
6
+ } from "react";
7
+ import Element,{
8
+ IElement
9
+ } from "../Form/Element";
10
+
11
+
12
+ interface IInput extends IElement<HTMLInputElement> {
13
+ onChangeText?: (text: string) => void;
14
+ }
15
+
16
+ const Input = forwardRef<HTMLInputElement, IInput>(({onChange, onInput, onChangeText, ...props}, ref) => {
17
+ const [currentValue, setCurrentValue] = useState(props.value);
18
+
19
+ const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
20
+ if (typeof onChange === 'function') {
21
+ onChange(e);
22
+ }
23
+
24
+ if (typeof onChangeText === 'function') {
25
+ onChangeText(e.target.value);
26
+ }
27
+ };
28
+
29
+ const handleInput = (e: ChangeEvent<HTMLInputElement>) => {
30
+ if (typeof onInput === 'function') {
31
+ onInput(e);
32
+ }
33
+ setCurrentValue(e.target.value);
34
+ };
35
+
36
+ useEffect(() => {
37
+ setCurrentValue(props.value);
38
+ }, [props.value]);
39
+
40
+ return(<Element {...props}>
41
+ { (props) => <input {...props} placeholder="" onChange={handleChange} value={currentValue} onInput={handleInput} ref={ref}/> }
42
+ </Element>);
43
+ });
44
+
45
+ export default Input;
@@ -0,0 +1,3 @@
1
+ import Input from "./Input";
2
+
3
+ export default Input;
@@ -0,0 +1,16 @@
1
+ .container > input {
2
+ width: calc(100% - 10px);
3
+ margin: 15px 0 15px 0;
4
+ font-size: 18px;
5
+ padding: 10px 0 10px 15px;
6
+ border: 2px solid #E2E8F2;
7
+ resize: none;
8
+ outline: none;
9
+ transition: 0.2s;
10
+ -webkit-transition: 0.2s;
11
+ }
12
+
13
+ .container > input[type="text"]:focus {
14
+ border: 2px solid #FBA31D;
15
+ border-bottom: 2px solid #000;
16
+ }
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import styles from "./style.module.css";
3
+
4
+ type TSpinner = {
5
+ size?: 'small' | 'middle' | 'large';
6
+ color?: 'default' | 'contrast';
7
+ }
8
+
9
+ const Spinner = ({size, color = 'default'}: TSpinner) => {
10
+ return(<div className={styles['spinner'] + (size ? ' ' + styles[size] : '') + (color ? ' ' + styles[color] : '') + ' ' + styles['animate']}></div>);
11
+ };
12
+
13
+ export default Spinner;
@@ -0,0 +1,3 @@
1
+ import Spinner from "./Spinner";
2
+
3
+ export default Spinner;
@@ -0,0 +1,74 @@
1
+ .spinner {
2
+ padding-left: calc(-0.5rem / 2);
3
+ padding-right: calc(-0.5rem / 2);
4
+ }
5
+
6
+ .spinner::before {
7
+ content: '';
8
+ display: inline-block;
9
+ width: 1rem;
10
+ height: 1rem;
11
+ --border-width: clamp(0.2em, 10%, 0.5em);
12
+ border-radius: 50%;
13
+ aspect-ratio: 1/1;
14
+ --mask: radial-gradient(
15
+ farthest-side,
16
+ transparent calc(100% - var(--border-width) - 0.5px),
17
+ #000 calc(100% - var(--border-width) + 0.5px)
18
+ );
19
+ mask: var(--mask);
20
+ background: linear-gradient(to top, rgba(251, 163, 29, 1), rgba(251, 163, 29, 0.5)) 100% 0/50% 100% no-repeat,
21
+ linear-gradient(rgba(251, 163, 29, 0.5) 50%, transparent 95%) 0 0/50% 100% no-repeat;
22
+ }
23
+
24
+ .spinner.default::before {
25
+ border-color: #FFFFFF;
26
+ }
27
+
28
+ .spinner.contrast::before {
29
+ background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.5)) 100% 0/50% 100% no-repeat,
30
+ linear-gradient(rgba(255, 255, 255, 0.5) 50%, transparent 95%) 0 0/50% 100% no-repeat;
31
+ }
32
+
33
+ .spinner.small::before {
34
+ width: 1rem;
35
+ height: 1rem;
36
+ }
37
+
38
+ .spinner.middle::before {
39
+ width: 4rem;
40
+ height: 4rem;
41
+ }
42
+
43
+ .spinner.large::before {
44
+ width: 8rem;
45
+ height: 8rem;
46
+ }
47
+
48
+ .spinner.big::before {
49
+ width: 2rem;
50
+ height: 2rem;
51
+ }
52
+
53
+ .spinner.full {
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
57
+ width: 100%;
58
+ height: 100%;
59
+ }
60
+
61
+ .spinner.full::before {
62
+ width: 80%;
63
+ height: 80%;
64
+ }
65
+
66
+ .spinner.animate::before {
67
+ animation: button-spinner-border .85s linear infinite;
68
+ }
69
+
70
+ @keyframes button-spinner-border {
71
+ to{
72
+ transform:rotate(360deg)
73
+ }
74
+ }
@@ -0,0 +1,57 @@
1
+ import {
2
+ forwardRef,
3
+ useRef,
4
+ useEffect,
5
+ useState,
6
+ ChangeEvent,
7
+ useImperativeHandle
8
+ } from "react";
9
+ import Element,{
10
+ IElement
11
+ } from "../Form/Element";
12
+
13
+ interface ITextArea extends IElement<HTMLTextAreaElement> {
14
+ onChangeText?: (text: string) => void;
15
+ }
16
+
17
+ const TextArea = forwardRef<HTMLTextAreaElement, ITextArea>(({onChange, onInput, onChangeText, ...props}, ref) => {
18
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
19
+ const [currentValue, setCurrentValue] = useState(props.value);
20
+
21
+ const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
22
+ if (typeof onChange === 'function') {
23
+ onChange(e);
24
+ }
25
+
26
+ if (typeof onChangeText === 'function') {
27
+ onChangeText(e.target.value);
28
+ }
29
+ };
30
+
31
+ const handleInput = (e: ChangeEvent<HTMLTextAreaElement>) => {
32
+ if (typeof onInput === 'function') {
33
+ onInput(e);
34
+ }
35
+ setCurrentValue(e.target.value);
36
+ };
37
+
38
+ useEffect(() => {
39
+ if (!textareaRef.current) return;
40
+
41
+ textareaRef.current.style.height = '0';
42
+ const height = textareaRef.current.scrollHeight;
43
+ textareaRef.current.style.height = height + 'px';
44
+ },[currentValue]);
45
+
46
+ useEffect(() => {
47
+ setCurrentValue(props.value);
48
+ }, [props.value]);
49
+
50
+ useImperativeHandle(ref, () => textareaRef.current as HTMLTextAreaElement);
51
+
52
+ return(<Element {...props}>
53
+ { (props) => <textarea {...props} placeholder="" value={currentValue} onChange={handleChange} onInput={handleInput} ref={textareaRef}/> }
54
+ </Element>);
55
+ });
56
+
57
+ export default TextArea;
@@ -0,0 +1,3 @@
1
+ import TextArea from "./TextArea";
2
+
3
+ export default TextArea;
package/src/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ import Form,{
2
+ useForm
3
+ } from "./Form";
4
+ import Input from "./Input";
5
+ import TextArea from "./TextArea";
6
+ import Spinner from "./Spinner";
7
+ import File,{
8
+ TFileData,
9
+ TFileMime
10
+ } from "./File";
11
+
12
+ const UI = {
13
+
14
+ };
15
+
16
+ export {
17
+ Form,
18
+ useForm,
19
+ Input,
20
+ TextArea,
21
+ Spinner,
22
+ File
23
+ };
24
+ export type {
25
+ TFileData,
26
+ TFileMime
27
+ };
28
+ export default UI;