react-artasys-ui 0.0.2 → 0.0.4

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.
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
2
  interface IButton extends React.ButtonHTMLAttributes<HTMLButtonElement> {
3
3
  wait?: boolean;
4
+ classNameContainer?: string;
5
+ styleContainer?: React.HTMLAttributes<HTMLDivElement>['style'];
4
6
  }
5
- declare const Button: ({ children, wait, ...props }: IButton) => JSX.Element;
7
+ declare const Button: ({ children, className, classNameContainer, styleContainer, wait, ...props }: IButton) => JSX.Element;
6
8
  export default Button;
@@ -1,7 +1,16 @@
1
- import { TFileData } from "../File";
2
- import { TUploadImageData } from "./UploadImages";
1
+ import type { TFileData } from "../File";
3
2
  export type TImageData = TFileData & {
3
+ id?: string | number;
4
4
  setWait: (status: boolean) => void;
5
+ setId: (id: string | number) => void;
5
6
  };
6
- declare const Image: ({ onChange, onClick, ...props }: TUploadImageData) => JSX.Element;
7
+ export interface IImage extends TFileData {
8
+ onChange?: (data: TImageData) => void;
9
+ onId?: (id: string | number) => void;
10
+ onRemove?: () => void;
11
+ onClick?: (data: TImageData) => void;
12
+ id?: string | number;
13
+ src?: string;
14
+ }
15
+ declare const Image: ({ onChange, onClick, onId, onRemove, ...props }: IImage) => JSX.Element;
7
16
  export default Image;
@@ -1,9 +1,9 @@
1
- import { TFileData } from "../File";
2
- import { TImageData } from "./Image";
1
+ import { TImageData, IImage } from "./Image";
3
2
  export interface IUploadImages {
4
3
  onChange?: (data: TImageData) => void;
4
+ onChangeArray?: (data: IImage[]) => void;
5
5
  onClick?: (data: TImageData) => void;
6
+ imagesArray?: TImageData[];
6
7
  }
7
- export type TUploadImageData = TFileData & IUploadImages & {};
8
- declare const UploadImages: ({ onChange, onClick }: IUploadImages) => JSX.Element;
8
+ declare const UploadImages: ({ imagesArray, onChange, onChangeArray, onClick }: IUploadImages) => JSX.Element;
9
9
  export default UploadImages;
@@ -1,4 +1,5 @@
1
- import UploadImages, { TUploadImageData, IUploadImages } from "./UploadImages";
1
+ import UploadImages, { IUploadImages } from "./UploadImages";
2
+ import type { IImage } from "./Image";
2
3
  import { TImageData } from "./Image";
3
- export type { TUploadImageData, IUploadImages, TImageData };
4
+ export type { IUploadImages, TImageData, IImage };
4
5
  export default UploadImages;
package/lib/index.d.ts CHANGED
@@ -5,8 +5,8 @@ import TextArea from "./TextArea";
5
5
  import Spinner from "./Spinner";
6
6
  import File, { TFileData, TFileMime } from "./File";
7
7
  import Button from "./Button";
8
- import UploadImages, { TUploadImageData, IUploadImages, TImageData } from "./UploadImages";
8
+ import UploadImages, { IUploadImages, TImageData, IImage } from "./UploadImages";
9
9
  declare const UI: {};
10
10
  export { Form, FormElement, Element, useForm, Input, TextArea, Spinner, File, Button, UploadImages };
11
- export type { TFileData, TFileMime, TUploadImageData, IUploadImages, TImageData, IElement };
11
+ export type { TFileData, TFileMime, IUploadImages, IImage, TImageData, IElement };
12
12
  export default UI;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-artasys-ui",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -3,15 +3,17 @@ import styles from "./style.module.css";
3
3
  import Spinner from "../Spinner";
4
4
 
5
5
  interface IButton extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6
- wait?: boolean
6
+ wait?: boolean;
7
+ classNameContainer?: string;
8
+ styleContainer?: React.HTMLAttributes<HTMLDivElement>['style'];
7
9
  }
8
10
 
9
- const Button = ({children, wait = false, ...props}: IButton) => {
11
+ const Button = ({children, className, classNameContainer, styleContainer, wait = false, ...props}: IButton) => {
10
12
 
11
- return(<div className={styles['container']}>
12
- <button {...props}>{children}</button>
13
+ return(<div className={styles['container'] + (classNameContainer ? ' ' + classNameContainer : '')} style={styleContainer}>
14
+ <button {...props} className={className}>{children}</button>
13
15
  <div className={styles['wait-indicator'] + (wait ? ' ' + styles['active'] : '')}>
14
- <Spinner size="middle"/>
16
+ <Spinner size="small" color="contrast"/>
15
17
  </div>
16
18
  </div>)
17
19
  };
@@ -31,9 +31,11 @@ const Element = ({children, beforeElement, afterElement, error, placeholder, dis
31
31
  style={styleContainer}
32
32
  >
33
33
  {beforeElement}
34
- {typeof children === 'function' ? children(props) : null}
34
+ <div className={styles['element']}>
35
+ {typeof children === 'function' ? children(props) : null}
36
+ {placeholder && <span className={styles['placeholder']}>{placeholder}</span>}
37
+ </div>
35
38
  {afterElement}
36
- {placeholder && <span className={styles['placeholder']}>{placeholder}</span>}
37
39
  </label>
38
40
  {currentError && <div className={styles['error']}>{currentError}</div>}
39
41
  </>);
@@ -4,21 +4,27 @@
4
4
  display: flex;
5
5
  align-items: center;
6
6
  width: 100%;
7
- min-height: 50px;
7
+ min-height: 45px;
8
8
  position: relative;
9
9
  border: 1px solid #CCCCCC;
10
10
  border-radius: 3px;
11
11
  z-index: 0;
12
12
  }
13
13
 
14
- .container > input, .container > textarea {
14
+ .element {
15
+ align-self: stretch;
16
+ width: 100%;
17
+ min-height: 100%;
18
+ }
19
+
20
+ .element > input, .element > textarea {
15
21
  width: 100% !important;
22
+ height: 100% !important;
16
23
  min-height: 100% !important;
17
24
  max-height: 500px !important;
18
25
  max-height: 50dvh !important;
19
- padding: 10px 5px !important;
20
26
  margin: 0 !important;
21
- font-size: 1.3em !important;
27
+ font-size: 1.3em;
22
28
  border: 0 !important;
23
29
  box-sizing: border-box !important;
24
30
  background-color: transparent !important;
@@ -30,24 +36,25 @@
30
36
  background-color: #EFEFEF;
31
37
  }
32
38
 
33
- .container > input:-webkit-autofill,
34
- .container > input:-webkit-autofill:hover,
35
- .container > input:-webkit-autofill:focus,
36
- .container > input:-webkit-autofill:active{
39
+ .element > input:-webkit-autofill,
40
+ .element > input:-webkit-autofill:hover,
41
+ .element > input:-webkit-autofill:focus,
42
+ .element > input:-webkit-autofill:active{
37
43
  -webkit-background-clip: text;
38
44
  }
39
45
 
40
- .container > input:focus {
46
+ .element > input:focus {
41
47
  outline: none;
42
48
  }
43
49
 
44
- .container > .placeholder {
50
+ .element > .placeholder {
45
51
  position: absolute;
46
52
  display: flex;
47
53
  justify-content: center;
48
54
  align-items: center;
49
55
  top: 0;
50
- left: 5px;
56
+ margin-left: 5px;
57
+ height: 100%;
51
58
  color: #7A7A73;
52
59
  font-size: 1.2em;
53
60
  z-index: -1;
@@ -55,7 +62,11 @@
55
62
  transition: transform 0.3s;
56
63
  }
57
64
 
58
- .container > .placeholder::before {
65
+ .element > textarea ~ .placeholder {
66
+ max-height: 2em;
67
+ }
68
+
69
+ .element > .placeholder::before {
59
70
  content: "";
60
71
  position: absolute;
61
72
  display: block;
@@ -65,16 +76,16 @@
65
76
  z-index: -1;
66
77
  }
67
78
 
68
- .container > input:required ~ .placeholder::after {
79
+ .element > input:required ~ .placeholder::after {
69
80
  content: "*";
70
81
  }
71
82
 
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);
83
+ .element > input:focus ~ .placeholder,
84
+ .element > textarea:focus ~ .placeholder,
85
+ .element > input:not([value=""]) ~ .placeholder,
86
+ .element > textarea:not(:empty) ~ .placeholder,
87
+ .element > input:not(:empty) ~ .placeholder {
88
+ transform: translate(-10%, -50%) scale(0.8);
78
89
  z-index: 0;
79
90
  }
80
91
 
@@ -82,7 +93,7 @@
82
93
  border-color: #FF6D6D;
83
94
  }
84
95
 
85
- .container.error > .placeholder {
96
+ .container.error .placeholder {
86
97
  color: #FF6D6D;
87
98
  }
88
99
 
@@ -1,37 +1,82 @@
1
- import { useEffect, useState } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
2
  import Spinner from "../Spinner";
3
- import { TFileData } from "../File";
3
+ import type { TFileData } from "../File";
4
4
  import styles from "./style.module.css";
5
5
 
6
- import { TUploadImageData } from "./UploadImages";
7
-
8
6
  export type TImageData = TFileData & {
7
+ id?: string | number;
9
8
  setWait: (status: boolean) => void;
9
+ setId: (id: string | number) => void;
10
+ };
11
+
12
+ export interface IImage extends TFileData {
13
+ onChange?: (data: TImageData) => void;
14
+ onId?: (id: string | number) => void;
15
+ onRemove?: () => void;
16
+ onClick?: (data: TImageData) => void;
17
+ id?: string | number;
18
+ src?: string;
10
19
  };
11
20
 
12
- const Image = ({onChange, onClick, ...props}: TUploadImageData) => {
13
- const { base64 } = props;
21
+ const Image = ({onChange, onClick, onId, onRemove, ...props}: IImage) => {
22
+ const { base64, src } = props;
14
23
  const [wait, setWait] = useState(true);
24
+ const confirmRemove = useRef(false);
25
+ const currentId = useRef<string | number | undefined>();
26
+ const setId = (id: string | number) => {
27
+ currentId.current = id;
28
+ if (typeof onId !== 'function') return;
29
+ onId(id);
30
+ };
15
31
 
16
32
  const handleClick = () => {
17
33
  if (typeof onClick === 'function') {
18
34
  onClick({
19
35
  ...props,
20
- setWait
36
+ setWait,
37
+ setId,
38
+ id: currentId.current
21
39
  });
22
40
  }
23
41
  };
24
42
 
43
+ const handleLoad = () => {
44
+ setWait(false);
45
+ };
46
+
47
+ const handleRemove = (e: React.MouseEvent<HTMLSpanElement>) => {
48
+ if (!e.target) return;
49
+ const element = (e.target as HTMLSpanElement);
50
+
51
+ const remove = () => {
52
+ if (!confirmRemove.current) return;
53
+ confirmRemove.current = false;
54
+ element.blur();
55
+ if (typeof onRemove === 'function') {
56
+ onRemove();
57
+ }
58
+ };
59
+ remove();
60
+ confirmRemove.current = true;
61
+ };
62
+
63
+ const handleCancelRemove = () => {
64
+ confirmRemove.current = false;
65
+ };
66
+
25
67
  useEffect(() => {
26
- if (typeof onChange !== 'function') return;
68
+ if (typeof onChange !== 'function' || src) return;
27
69
  onChange({
28
70
  ...props,
29
- setWait
71
+ setWait,
72
+ setId,
73
+ id: currentId.current
30
74
  });
31
75
  }, []);
32
76
 
33
77
  return(<span className={styles['image']} onClick={handleClick}>
34
- <img src={base64 as string}/>
78
+ <span className={styles['close']} onClick={handleRemove} onBlur={handleCancelRemove} tabIndex={1}/>
79
+ <img src={src ?? base64?.toString()} onLoad={handleLoad}/>
35
80
  {wait && <span className={styles['wait']}>
36
81
  <Spinner size="small" color="contrast"/>
37
82
  </span>}
@@ -1,29 +1,55 @@
1
- import { useState } from "react";
1
+ import { useEffect, useState } from "react";
2
2
  import File,{
3
3
  TFileData
4
4
  } from "../File";
5
5
  import styles from "./style.module.css";
6
6
 
7
7
  import Image,{
8
- TImageData
8
+ TImageData,
9
+ IImage
9
10
  } from "./Image";
10
11
 
11
12
  export interface IUploadImages {
12
13
  onChange?: (data: TImageData) => void;
14
+ onChangeArray?: (data: IImage[]) => void;
13
15
  onClick?: (data: TImageData) => void;
16
+ imagesArray?: TImageData[];
14
17
  };
15
18
 
16
- export type TUploadImageData = TFileData & IUploadImages & {};
17
-
18
- const UploadImages = ({onChange, onClick}: IUploadImages) => {
19
- const [images, setImages] = useState<TUploadImageData[]>([]);
19
+ const UploadImages = ({imagesArray, onChange, onChangeArray, onClick}: IUploadImages) => {
20
+ const [images, setImages] = useState<IImage[]>([]);
20
21
 
21
22
  const handleChange = (image: TFileData) => {
22
- setImages((images) => [...images, {...image, onChange, onClick}]);
23
+ setImages((images) => [...images, {...image}]);
24
+ };
25
+
26
+ const handleChangeId = (image: IImage) => {
27
+ return (id: string | number) => {
28
+ setImages((images) => images.map((currentImage) => {
29
+ if (image === currentImage) {
30
+ currentImage.id = id;
31
+ }
32
+ return currentImage;
33
+ }));
34
+ }
23
35
  };
36
+
37
+ const handleRemove = (image: IImage) => {
38
+ return () => setImages((images) => images.filter((currentImage) => currentImage !== image));
39
+ };
40
+
41
+ useEffect(() => {
42
+ if (typeof onChangeArray !== 'function') return;
43
+ onChangeArray(images);
44
+ }, [images]);
45
+
46
+ useEffect(() => {
47
+ if (!imagesArray) return;
48
+ setImages(imagesArray);
49
+ }, [imagesArray]);
24
50
 
25
51
  return(<div className={styles['container']}>
26
- {images.map((image, index) => <Image key={(image.name + index)} {...image} />)}
52
+ {images.map((image, index) => <Image key={(image.name + index)} {...image} onChange={onChange} onClick={onClick} onRemove={handleRemove(image)} onId={handleChangeId(image)}/>)}
27
53
  <File onChange={handleChange} accept={['image/png']} className={styles['new']} multiple/>
28
54
  </div>);
29
55
  };
@@ -1,13 +1,13 @@
1
1
  import UploadImages,{
2
- TUploadImageData,
3
2
  IUploadImages
4
3
  } from "./UploadImages";
4
+ import type { IImage } from "./Image";
5
5
  import {
6
6
  TImageData
7
7
  } from "./Image";
8
8
  export type {
9
- TUploadImageData,
10
9
  IUploadImages,
11
- TImageData
10
+ TImageData,
11
+ IImage
12
12
  };
13
13
  export default UploadImages;
@@ -38,6 +38,49 @@
38
38
  object-fit: cover;
39
39
  }
40
40
 
41
+ /* Close */
42
+ .image .close {
43
+ display: flex;
44
+ position: absolute;
45
+ top: 0;
46
+ right: 0;
47
+ justify-content: center;
48
+ align-items: center;
49
+ width: 20px;
50
+ height: 20px;
51
+ cursor: pointer;
52
+ opacity: 0.7;
53
+ transition: transform 0.3s, opacity 0.3s;
54
+ }
55
+
56
+ .image .close::before, .image .close::after {
57
+ content: "";
58
+ display: block;
59
+ position: absolute;
60
+ width: 100%;
61
+ height: 3px;
62
+ background-color: #1D1D1B;
63
+ }
64
+
65
+ .image .close::before {
66
+ transform: rotate(45deg);
67
+ }
68
+
69
+ .image .close::after {
70
+ transform: rotate(-45deg);
71
+ }
72
+
73
+ .image .close:focus {
74
+ background-color: red;
75
+ width: 100%;
76
+ height: 100%;
77
+ transition: width 0.1s, height 0.3s;
78
+ }
79
+
80
+ .image .close:focus::before, .image .close:focus::after {
81
+ background-color: #FFFFFF;
82
+ }
83
+
41
84
  .new {
42
85
  display: flex;
43
86
  justify-content: center;
package/src/index.ts CHANGED
@@ -14,9 +14,9 @@ import File,{
14
14
  } from "./File";
15
15
  import Button from "./Button";
16
16
  import UploadImages,{
17
- TUploadImageData,
18
17
  IUploadImages,
19
- TImageData
18
+ TImageData,
19
+ IImage
20
20
  } from "./UploadImages";
21
21
 
22
22
  const UI = {
@@ -38,8 +38,8 @@ export {
38
38
  export type {
39
39
  TFileData,
40
40
  TFileMime,
41
- TUploadImageData,
42
41
  IUploadImages,
42
+ IImage,
43
43
  TImageData,
44
44
  IElement
45
45
  };
package/webpack.config.js CHANGED
@@ -2,7 +2,7 @@ const path = require('path');
2
2
 
3
3
  module.exports = {
4
4
  entry: {
5
- index: '/src/index.ts',
5
+ index: '/src/index.ts'
6
6
  },
7
7
  target: 'web',
8
8
  mode: 'production', //production | development
@@ -46,7 +46,6 @@ module.exports = {
46
46
  path: path.resolve(__dirname, 'dist'),
47
47
  library: 'UI',
48
48
  libraryTarget: 'umd',
49
- chunkFilename: '[name].chunk.js',
50
49
  auxiliaryComment: 'Arta System UI'
51
50
  },
52
51
  externals: {