react-artasys-ui 0.1.3 → 0.1.5

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,7 +1,11 @@
1
1
  import { FormHTMLAttributes, ReactNode } from 'react';
2
+ import { TSpinner } from '../Spinner';
2
3
  interface IForm extends FormHTMLAttributes<HTMLFormElement> {
3
4
  children: ReactNode;
4
5
  wait?: boolean;
6
+ progress?: number;
7
+ progressRadius?: boolean;
8
+ spinnerColor?: TSpinner['color'];
5
9
  }
6
- declare const Form: ({ children, wait, className, onSubmit, ...props }: IForm) => JSX.Element;
10
+ declare const Form: ({ children, wait, progress, progressRadius, className, spinnerColor, onSubmit, ...props }: IForm) => JSX.Element;
7
11
  export default Form;
@@ -0,0 +1,7 @@
1
+ interface IProgress {
2
+ value?: number | string;
3
+ size?: 'small' | 'middle' | 'large';
4
+ radius?: boolean;
5
+ }
6
+ declare const Progress: ({ value, size, radius }: IProgress) => JSX.Element;
7
+ export default Progress;
@@ -0,0 +1,2 @@
1
+ import Progress from "./Progress";
2
+ export default Progress;
@@ -0,0 +1,7 @@
1
+ import { LiHTMLAttributes } from "react";
2
+ export interface IOption extends LiHTMLAttributes<HTMLLIElement> {
3
+ value?: string;
4
+ children?: string | React.ReactElement;
5
+ }
6
+ declare const Option: ({ children, value, onClick, ...props }: IOption) => JSX.Element;
7
+ export default Option;
@@ -0,0 +1,14 @@
1
+ /// <reference types="react" />
2
+ import { IElement } from "../Form/Element";
3
+ import type { IOption } from "./Option";
4
+ export declare const Context: import("react").Context<{
5
+ selected: string;
6
+ setSelect: (value: string) => void;
7
+ setTitle: (title: string) => void;
8
+ }>;
9
+ export interface ISelect extends Omit<IElement, 'children' | 'onChange'> {
10
+ children?: React.FunctionComponentElement<IOption>[];
11
+ onChange?: (value: string) => void;
12
+ }
13
+ declare const Select: import("react").ForwardRefExoticComponent<ISelect & import("react").RefAttributes<HTMLInputElement>>;
14
+ export default Select;
@@ -0,0 +1,9 @@
1
+ import { ForwardRefExoticComponent, FC, RefAttributes } from "react";
2
+ import { ISelect } from "./Select";
3
+ import Option, { IOption } from "./Option";
4
+ interface StaticComponent extends ForwardRefExoticComponent<ISelect & RefAttributes<HTMLInputElement>> {
5
+ Option: FC<IOption>;
6
+ }
7
+ declare const Select: StaticComponent;
8
+ export { Option as SelectOption };
9
+ export default Select;
@@ -1,6 +1,7 @@
1
- export type TSpinner = {
1
+ import { AllHTMLAttributes } from "react";
2
+ export type TSpinner = Omit<AllHTMLAttributes<HTMLDivElement>, 'size' | 'color'> & {
2
3
  size?: 'small' | 'middle' | 'large';
3
- color?: 'default' | 'contrast';
4
+ color?: 'contrast' | 'orange';
4
5
  };
5
- declare const Spinner: ({ size, color }: TSpinner) => JSX.Element;
6
+ declare const Spinner: ({ size, color, ...props }: TSpinner) => JSX.Element;
6
7
  export default Spinner;
package/lib/index.d.ts CHANGED
@@ -7,7 +7,9 @@ import File, { TFileData, TFileMime } from "./File";
7
7
  import Button from "./Button";
8
8
  import UploadImages, { IUploadImages, TImageData, IImage } from "./UploadImages";
9
9
  import Checkbox from "./Checkbox";
10
+ import Progress from "./Progress";
11
+ import Select, { SelectOption } from "./Select";
10
12
  declare const UI: {};
11
- export { Form, FormElement, Element, useForm, Input, TextArea, Spinner, File, Button, UploadImages, Checkbox };
13
+ export { Form, FormElement, Element, useForm, Input, TextArea, Spinner, File, Button, UploadImages, Checkbox, Progress, Select, SelectOption };
12
14
  export type { TFileData, TFileMime, IUploadImages, IImage, TImageData, IElement };
13
15
  export default UI;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-artasys-ui",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -59,7 +59,7 @@
59
59
  margin-left: 5px;
60
60
  height: 100%;
61
61
  color: #7A7A73;
62
- font-size: 1.2em;
62
+ font-size: 1.2rem;
63
63
  z-index: -1;
64
64
  user-select: none;
65
65
  transition: transform 0.3s;
package/src/Form/Form.tsx CHANGED
@@ -1,14 +1,20 @@
1
1
  import { FormHTMLAttributes, ReactNode, FormEvent } from 'react';
2
2
  import styles from './style.module.css';
3
3
 
4
- import Spinner from '../Spinner';
4
+ import Spinner,{
5
+ TSpinner
6
+ } from '../Spinner';
7
+ import Progress from '../Progress';
5
8
 
6
9
  interface IForm extends FormHTMLAttributes<HTMLFormElement> {
7
10
  children: ReactNode;
8
11
  wait?: boolean;
12
+ progress?: number;
13
+ progressRadius?: boolean;
14
+ spinnerColor?: TSpinner['color'];
9
15
  }
10
16
 
11
- const Form = ({children, wait, className, onSubmit, ...props}: IForm) => {
17
+ const Form = ({children, wait, progress, progressRadius = true, className, spinnerColor, onSubmit, ...props}: IForm) => {
12
18
 
13
19
  const submit = (e: FormEvent<HTMLFormElement>) => {
14
20
  e.preventDefault();
@@ -26,7 +32,7 @@ const Form = ({children, wait, className, onSubmit, ...props}: IForm) => {
26
32
  return(<form {...props} onSubmit={submit} className={classes.join(' ')}>
27
33
  {children}
28
34
  <div className={styles['wait-indicator']}>
29
- <Spinner size="large" color="contrast"/>
35
+ {typeof progress === 'number' ? <Progress radius={progressRadius} value={progress}/> : <Spinner color={spinnerColor}/>}
30
36
  </div>
31
37
  </form>);
32
38
  };
@@ -0,0 +1,28 @@
1
+ import styles from "./style.module.css";
2
+
3
+ interface IProgress {
4
+ value?: number | string;
5
+ size?: 'small' | 'middle' | 'large';
6
+ radius?: boolean;
7
+ };
8
+
9
+ const Progress = ({value = 0, size, radius = false}: IProgress) => {
10
+ value = Math.ceil(Number(value) * 10) / 10;
11
+ if (value > 100) {
12
+ value = 100;
13
+ }
14
+ if (value < 0) {
15
+ value = 0;
16
+ }
17
+
18
+ const progress = radius ? (360 / 100 * value) + 'deg' : value + '%';
19
+
20
+ const classes = ['ui-progress'];
21
+ classes.push(styles['container']);
22
+ if (size) classes.push(styles[size]);
23
+ if (radius) classes.push(styles['radius']);
24
+
25
+ return(<div className={classes.join(' ')} data-progress={value} style={{'--progress': progress} as React.CSSProperties}/>);
26
+ };
27
+
28
+ export default Progress;
@@ -0,0 +1,3 @@
1
+ import Progress from "./Progress";
2
+
3
+ export default Progress;
@@ -0,0 +1,72 @@
1
+ /* Progress */
2
+
3
+ .container {
4
+ --size: 150px;
5
+ --color-rgb: 94, 190, 214;
6
+ position: relative;
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ width: 100%;
11
+ min-width: var(--size);
12
+ max-width: var(--size);
13
+ box-sizing: content-box !important;
14
+ aspect-ratio: 10/1;
15
+ border-radius: 2px;
16
+ padding: 5px 0;
17
+ overflow: hidden;
18
+ }
19
+
20
+ .container::before {
21
+ content: "";
22
+ position: absolute;
23
+ display: block;
24
+ width: 100%;
25
+ height: 100%;
26
+ max-width: 100%;
27
+ max-height: 100%;
28
+ background: linear-gradient(to right, #5EBED6 var(--progress), #BED4DB 0);
29
+ }
30
+
31
+ .container.radius {
32
+ aspect-ratio: 1/1;
33
+ border-radius: 50%;
34
+ padding: unset;
35
+ }
36
+
37
+ .container.radius::before {
38
+ --border-width: clamp(0.2em, 10%, 0.5em);
39
+ border-radius: 50%;
40
+ aspect-ratio: 1/1;
41
+ mask: radial-gradient(
42
+ farthest-side,
43
+ transparent calc(100% - var(--border-width) - 0.5px),
44
+ #000 calc(100% - var(--border-width) + 0.5px)
45
+ );
46
+ background: conic-gradient(#5EBED6 var(--progress), #BED4DB 0deg);
47
+ }
48
+
49
+ .container::after {
50
+ content: attr(data-progress) '%';
51
+ font-size: 1.5em;
52
+ color: #FFFFFF;
53
+ mix-blend-mode: lighten;
54
+ z-index: 1;
55
+ }
56
+
57
+ .container.radius::after {
58
+ mix-blend-mode: unset;
59
+ color: #1D1D1B;
60
+ }
61
+
62
+ .container.small {
63
+ --size: 30px;
64
+ }
65
+
66
+ .container.middle {
67
+ --size: 60px;
68
+ }
69
+
70
+ .container.large {
71
+ --size: 120px;
72
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ LiHTMLAttributes,
3
+ MouseEvent,
4
+ useContext,
5
+ useEffect
6
+ } from "react";
7
+ import { Context } from "./Select";
8
+ import styles from "./style.module.css";
9
+
10
+ export interface IOption extends LiHTMLAttributes<HTMLLIElement> {
11
+ value?: string;
12
+ children?: string | React.ReactElement;
13
+ };
14
+
15
+ const Option = ({children, value, onClick, ...props}: IOption) => {
16
+ const context = useContext(Context);
17
+
18
+ const handleClick = (e: MouseEvent<HTMLLIElement>) => {
19
+ if (typeof value === 'undefined') return;
20
+ context.setSelect(value);
21
+ if (typeof onClick === 'function') {
22
+ onClick(e);
23
+ }
24
+ };
25
+
26
+ useEffect(() => {
27
+ if (value === context.selected && children) {
28
+ context.setTitle(children?.toString());
29
+ }
30
+ }, [context.selected]);
31
+
32
+ const classes = ['ui-select-option'];
33
+ if (context.selected === value) classes.push(styles['active'], 'active');
34
+
35
+ return(<li {...props} onClick={handleClick} className={classes.join(' ')}>{children}</li>);
36
+ };
37
+
38
+ export default Option;
@@ -0,0 +1,74 @@
1
+ import {
2
+ forwardRef,
3
+ createContext,
4
+ useState,
5
+ useEffect,
6
+ useRef
7
+ } from "react";
8
+ import Element,{
9
+ IElement
10
+ } from "../Form/Element";
11
+ import type { IOption } from "./Option";
12
+ import styles from "./style.module.css";
13
+
14
+ export const Context = createContext({
15
+ selected: '',
16
+ setSelect: (value: string) => {},
17
+ setTitle: (title: string) => {}
18
+ });
19
+
20
+ export interface ISelect extends Omit<IElement, 'children' | 'onChange'> {
21
+ children?: React.FunctionComponentElement<IOption>[];
22
+ onChange?: (value: string) => void;
23
+ };
24
+
25
+ const Select = forwardRef<HTMLInputElement, ISelect>(({children, onChange, value, ...props}, ref) => {
26
+ const containerRef = useRef<HTMLDivElement>(null);
27
+
28
+ const [selected, setSelected] = useState('');
29
+ const [title, setTitle] = useState<string>();
30
+
31
+ const open = () => {
32
+ const element = containerRef.current;
33
+ element?.classList.add(styles['opened']);
34
+ element?.focus();
35
+ };
36
+
37
+ const close = () => {
38
+ const element = containerRef.current;
39
+ element?.classList.remove(styles['opened']);
40
+ };
41
+
42
+ const setSelect = (value: string) => {
43
+ if (typeof onChange === 'function') {
44
+ onChange(value);
45
+ }
46
+ setSelected(value);
47
+ close();
48
+ };
49
+
50
+ useEffect(() => {
51
+ if (typeof value === 'undefined') return;
52
+ setSelected(String(value));
53
+ }, [value]);
54
+
55
+ return(<Element {...props} styleContainer={{zIndex: 1}}>
56
+ { (props) => <Context.Provider value={{
57
+ selected,
58
+ setSelect,
59
+ setTitle
60
+ }}>
61
+ <input {...props} type="hidden" value={selected} ref={ref}/>
62
+ <div className={styles['container']} ref={containerRef} tabIndex={1} onBlur={close}>
63
+ <div className={styles['select']} onClick={open}>
64
+ <span className={styles['title']}>{title}</span>
65
+ </div>
66
+ <ul className={styles['select-list']}>
67
+ {children}
68
+ </ul>
69
+ </div>
70
+ </Context.Provider> }
71
+ </Element>);
72
+ });
73
+
74
+ export default Select;
@@ -0,0 +1,27 @@
1
+ import {
2
+ forwardRef,
3
+ ForwardRefExoticComponent,
4
+ FC,
5
+ RefAttributes
6
+ } from "react";
7
+ import {
8
+ default as SelectUI,
9
+ ISelect
10
+ } from "./Select";
11
+ import Option,{
12
+ IOption
13
+ } from "./Option";
14
+
15
+ interface StaticComponent extends ForwardRefExoticComponent<ISelect & RefAttributes<HTMLInputElement>> {
16
+ Option: FC<IOption>;
17
+ };
18
+
19
+ const Select: StaticComponent = {
20
+ ...forwardRef<HTMLInputElement, ISelect>(({...args}, ref): JSX.Element => <SelectUI {...args} ref={ref}/>),
21
+ Option
22
+ } as StaticComponent;
23
+
24
+ export {
25
+ Option as SelectOption
26
+ };
27
+ export default Select;
@@ -0,0 +1,78 @@
1
+ /* Select */
2
+
3
+ .container {
4
+ position: relative;
5
+ display: flex;
6
+ flex-direction: column;
7
+ justify-content: center;
8
+ min-height: 25px;
9
+ cursor: pointer;
10
+ }
11
+
12
+ .select {
13
+ display: flex;
14
+ flex-direction: row;
15
+ align-items: center;
16
+ min-height: 20px;
17
+ padding: 10px;
18
+ }
19
+
20
+ .select::after {
21
+ content: "";
22
+ display: block;
23
+ width: 0;
24
+ height: 0;
25
+ border-left: 5px solid transparent;
26
+ border-right: 5px solid transparent;
27
+ border-top: 10px solid #5EBED6;
28
+ transition: rotate 0.3s;
29
+ }
30
+
31
+ .container.opened > .select::after {
32
+ rotate: 180deg;
33
+ }
34
+
35
+ .select-list {
36
+ position: absolute;
37
+ display: flex;
38
+ flex-direction: column;
39
+ width: 100%;
40
+ top: calc(100% - 1px);
41
+ left: -1px;
42
+ margin: 0;
43
+ padding: 0;
44
+ list-style-type: none;
45
+ background-color: #FFFFFF;
46
+ box-shadow: 0px 4px 5px 0px rgb(0 0 0 / 10%);
47
+ box-shadow: 0px 4px 5px 0px rgb(0 0 0 / 10%);
48
+ border: 1px solid #CCCCCC;
49
+ border-top: 0;
50
+ opacity: 0;
51
+ visibility: hidden;
52
+ transform: translateY(-10%);
53
+ transition: transform 0.3s ease-in-out, opacity 0.3s, visibility 0.3s;
54
+ }
55
+
56
+ .container.opened > .select-list {
57
+ opacity: 1;
58
+ transform: translateY(0);
59
+ visibility: visible;
60
+ }
61
+
62
+ .title, .select-list > li {
63
+ font-size: 20px;
64
+ color: #1D1D1B;
65
+ }
66
+
67
+ .title {
68
+ flex: 1;
69
+ }
70
+
71
+ .select-list > li {
72
+ padding: 5px;
73
+ cursor: pointer;
74
+ }
75
+
76
+ .select-list > li:hover, .select-list > li.active {
77
+ background-color: #BED4DB;
78
+ }
@@ -1,21 +1,21 @@
1
- import React from "react";
1
+ import { AllHTMLAttributes } from "react";
2
2
  import styles from "./style.module.css";
3
3
 
4
- export type TSpinner = {
4
+ export type TSpinner = Omit<AllHTMLAttributes<HTMLDivElement>, 'size' | 'color'> & {
5
5
  size?: 'small' | 'middle' | 'large';
6
- color?: 'default' | 'contrast';
6
+ color?: 'contrast' | 'orange';
7
7
  }
8
8
 
9
- const Spinner = ({size, color = 'default'}: TSpinner) => {
9
+ const Spinner = ({size, color, ...props}: TSpinner) => {
10
10
 
11
11
  const classes = ['ui-spinner'];
12
12
 
13
13
  classes.push(styles['spinner']);
14
- if (size) classes.push(styles[size]);
15
- if (color) classes.push(styles[color]);
16
- classes.push(styles['animate']);
14
+ if (size) classes.push(styles[size], 'ui-spinner-' + size);
15
+ if (color) classes.push(styles[color], 'ui-spinner-' + color);
16
+ if (props.className) classes.push(props.className);
17
17
 
18
- return(<div className={classes.join(' ')}></div>);
18
+ return(<div {...props} className={classes.join(' ')}></div>);
19
19
  };
20
20
 
21
21
  export default Spinner;
@@ -1,13 +1,22 @@
1
+ /* Spinner */
2
+
1
3
  .spinner {
4
+ --color-rgb: 94, 190, 214;
5
+ width: 100%;
6
+ height: 100%;
7
+ max-width: 100px;
8
+ max-height: 100px;
9
+ aspect-ratio: 1/1;
2
10
  padding-left: calc(-0.5rem / 2);
3
11
  padding-right: calc(-0.5rem / 2);
4
12
  }
5
13
 
6
14
  .spinner::before {
7
- content: '';
15
+ content: "";
8
16
  display: inline-block;
9
- width: 1rem;
10
- height: 1rem;
17
+ width: 100%;
18
+ max-width: 90%;
19
+ max-height: 90%;
11
20
  --border-width: clamp(0.2em, 10%, 0.5em);
12
21
  border-radius: 50%;
13
22
  aspect-ratio: 1/1;
@@ -17,56 +26,34 @@
17
26
  #000 calc(100% - var(--border-width) + 0.5px)
18
27
  );
19
28
  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;
29
+ background: linear-gradient(to top, rgba(var(--color-rgb), 1), rgba(var(--color-rgb), 0.5)) 100% 0/50% 100% no-repeat,
30
+ linear-gradient(rgba(var(--color-rgb), 0.5) 50%, transparent 95%) 0 0/50% 100% no-repeat;
31
+ animation: button-spinner-border .85s linear infinite;
22
32
  }
23
33
 
24
- .spinner.default::before {
25
- border-color: #FFFFFF;
34
+ .spinner.contrast {
35
+ --color-rgb: 255, 255, 255;
26
36
  }
27
37
 
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;
38
+ .spinner.orange {
39
+ --color-rgb: 251, 163, 29;
31
40
  }
32
41
 
33
- .spinner.small::before {
42
+ .spinner.small {
34
43
  width: 1rem;
35
44
  height: 1rem;
36
45
  }
37
46
 
38
- .spinner.middle::before {
47
+ .spinner.middle {
39
48
  width: 4rem;
40
49
  height: 4rem;
41
50
  }
42
51
 
43
- .spinner.large::before {
52
+ .spinner.large {
44
53
  width: 8rem;
45
54
  height: 8rem;
46
55
  }
47
56
 
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
57
  @keyframes button-spinner-border {
71
58
  to{
72
59
  transform:rotate(360deg)
@@ -78,7 +78,7 @@ const Image = ({onChange, onClick, onId, onRemove, ...props}: IImage) => {
78
78
  <span className={styles['close']} onClick={handleRemove} onBlur={handleCancelRemove} tabIndex={1}/>
79
79
  <img src={src ?? base64?.toString()} onLoad={handleLoad}/>
80
80
  {wait && <span className={styles['wait']}>
81
- <Spinner size="small" color="contrast"/>
81
+ <Spinner/>
82
82
  </span>}
83
83
  </span>);
84
84
  };
package/src/index.ts CHANGED
@@ -19,6 +19,10 @@ import UploadImages,{
19
19
  IImage
20
20
  } from "./UploadImages";
21
21
  import Checkbox from "./Checkbox";
22
+ import Progress from "./Progress";
23
+ import Select,{
24
+ SelectOption
25
+ } from "./Select";
22
26
 
23
27
  const UI = {
24
28
 
@@ -35,7 +39,10 @@ export {
35
39
  File,
36
40
  Button,
37
41
  UploadImages,
38
- Checkbox
42
+ Checkbox,
43
+ Progress,
44
+ Select,
45
+ SelectOption
39
46
  };
40
47
  export type {
41
48
  TFileData,