@reactivers/generic-ui 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/.babelrc +3 -0
  2. package/README.md +36 -0
  3. package/dist/bundle.css +1 -0
  4. package/dist/index.cjs.js +1566 -0
  5. package/dist/index.esm.js +1510 -0
  6. package/package.json +68 -0
  7. package/rollup.config.js +36 -0
  8. package/src/components/Badge/index.js +19 -0
  9. package/src/components/Button/index.js +63 -0
  10. package/src/components/Card/index.js +51 -0
  11. package/src/components/ColorPicker/index.js +44 -0
  12. package/src/components/EmptyResult/index.js +30 -0
  13. package/src/components/FadeAnimation/index.js +35 -0
  14. package/src/components/Form/index.js +25 -0
  15. package/src/components/Grid/index.js +1 -0
  16. package/src/components/Header/index.js +20 -0
  17. package/src/components/Image/index.js +63 -0
  18. package/src/components/IncDecField/index.js +35 -0
  19. package/src/components/InfiniteScroll/index.js +150 -0
  20. package/src/components/ListItem/index.js +81 -0
  21. package/src/components/Mapper/index.js +12 -0
  22. package/src/components/Modal/index.js +87 -0
  23. package/src/components/Notification/index.js +69 -0
  24. package/src/components/OverflowImages/index.js +38 -0
  25. package/src/components/Popover/index.js +109 -0
  26. package/src/components/Rate/index.js +33 -0
  27. package/src/components/Section/index.js +26 -0
  28. package/src/components/Selectfield/index.js +63 -0
  29. package/src/components/Show/index.js +15 -0
  30. package/src/components/Tag/index.js +67 -0
  31. package/src/components/TextListField/index.js +85 -0
  32. package/src/components/Textfield/index.js +51 -0
  33. package/src/components/ThreeDot/index.js +22 -0
  34. package/src/components/Upload/index.js +14 -0
  35. package/src/css/index.css +110 -0
  36. package/src/index.js +32 -0
  37. package/src/utils/styles.js +116 -0
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@reactivers/generic-ui",
3
+ "version": "0.1.0",
4
+ "mainiife": "dist/index.iife.js",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.esm.js",
7
+ "source": "src/index.js",
8
+ "private": false,
9
+ "dependencies": {
10
+ "@testing-library/jest-dom": "^5.11.4",
11
+ "@testing-library/react": "^11.1.0",
12
+ "@testing-library/user-event": "^12.1.10",
13
+ "history": "^5.0.0",
14
+ "rc-dialog": "^8.5.1",
15
+ "rc-field-form": "^1.18.1",
16
+ "rc-notification": "^4.5.4",
17
+ "rc-upload": "^3.3.4",
18
+ "react-color": "^2.19.3",
19
+ "react-flexbox-grid": "^2.1.2",
20
+ "react-router-dom": "^5.2.0"
21
+ },
22
+ "devDependencies": {
23
+ "@babel/cli": "^7.12.10",
24
+ "@babel/core": "^7.12.10",
25
+ "@babel/preset-env": "^7.12.11",
26
+ "@babel/preset-react": "^7.12.10",
27
+ "@rollup/plugin-babel": "^5.2.2",
28
+ "npm-run-all": "^4.1.5",
29
+ "postcss": "^8.2.4",
30
+ "rollup": "^2.38.0",
31
+ "rollup-plugin-commonjs": "^10.1.0",
32
+ "rollup-plugin-delete": "^2.0.0",
33
+ "rollup-plugin-node-resolve": "^5.2.0",
34
+ "rollup-plugin-peer-deps-external": "^2.2.4",
35
+ "rollup-plugin-postcss": "^4.0.0"
36
+ },
37
+ "peerDependencies": {
38
+ "@reactivers/use-utils": "1.0.0",
39
+ "moment": "^2.29.1",
40
+ "react": "^17.0.1",
41
+ "react-dom": "^17.0.1"
42
+ },
43
+ "scripts": {
44
+ "build": "rollup -c",
45
+ "build-watch": "rollup -c -w",
46
+ "dev": "npm-run-all --parallel build-watch",
47
+ "prepublishOnly": "npm run build",
48
+ "prepare": "npm run build"
49
+ },
50
+ "eslintConfig": {
51
+ "extends": [
52
+ "react-app",
53
+ "react-app/jest"
54
+ ]
55
+ },
56
+ "browserslist": {
57
+ "production": [
58
+ ">0.2%",
59
+ "not dead",
60
+ "not op_mini all"
61
+ ],
62
+ "development": [
63
+ "last 1 chrome version",
64
+ "last 1 firefox version",
65
+ "last 1 safari version"
66
+ ]
67
+ }
68
+ }
@@ -0,0 +1,36 @@
1
+ import babel from '@rollup/plugin-babel';
2
+ import external from 'rollup-plugin-peer-deps-external';
3
+ import del from 'rollup-plugin-delete';
4
+ import pkg from './package.json';
5
+ import path from 'path'
6
+ import resolve from "rollup-plugin-node-resolve";
7
+ import postcss from 'rollup-plugin-postcss'
8
+
9
+ export default {
10
+ input: pkg.source,
11
+ output: [
12
+ {file: pkg.main, format: 'cjs'},
13
+ {file: pkg.module, format: 'esm'}
14
+ ],
15
+ watch: {
16
+ include: [pkg.source, "src/*"],
17
+ exclude: 'node_modules/**'
18
+ },
19
+ plugins: [
20
+ external({
21
+ includeDependencies: true
22
+ }),
23
+ resolve(),
24
+ postcss({
25
+ minimize: true,
26
+ extensions: ['.css'],
27
+ extract: path.resolve('dist/bundle.css')
28
+ }),
29
+ babel({
30
+ exclude: 'node_modules/**',
31
+ extensions: ['.js', '.jsx', '.ts', '.tsx']
32
+ }),
33
+ del({targets: ['dist/*']}),
34
+ ],
35
+ external: Object.keys(pkg.peerDependencies || {})
36
+ };
@@ -0,0 +1,19 @@
1
+ import React from 'react'
2
+
3
+ const Badge = props => {
4
+ const {title, children} = props;
5
+ return (
6
+ <div style={{position: 'relative'}}>
7
+ <div style={{
8
+ position: 'absolute',
9
+ right: 0,
10
+ top: 0,
11
+ borderRadius: 10,
12
+ backgroundColor: '#eee'
13
+ }}>{title}</div>
14
+ {children}
15
+ </div>
16
+ )
17
+ }
18
+
19
+ export default Badge
@@ -0,0 +1,63 @@
1
+ import { coalasce, takeIf } from "@reactivers/use-utils";
2
+ import { useCallback } from 'react';
3
+ import appStyles from "../../utils/styles";
4
+ import Show from "../Show";
5
+
6
+ const Button = props => {
7
+ const {
8
+ style,
9
+ icon,
10
+ title,
11
+ className: _className,
12
+ iconSize: _iconSize,
13
+ onClick: _onClick,
14
+ htmlType: _htmlType,
15
+ children,
16
+ } = props;
17
+
18
+ const iconSize = coalasce(_iconSize, 32);
19
+ const htmlType = coalasce(_htmlType, "button");
20
+ const iconButton = !children && !title;
21
+
22
+ let className = `no-select `
23
+
24
+ const onClick = useCallback((e) => {
25
+ if (htmlType !== 'submit') e.preventDefault()
26
+ if (_onClick) _onClick(e);
27
+ }, [htmlType, _onClick])
28
+
29
+ if (_className) className += ` ${_className || ""}`
30
+
31
+ return (
32
+ <button style={{
33
+ justifyContent: 'center',
34
+ alignItems: 'center',
35
+ width: takeIf(iconButton, iconSize),
36
+ minWidth: takeIf(iconButton, iconSize),
37
+ height: takeIf(iconButton, iconSize),
38
+ minHeight: takeIf(iconButton, iconSize),
39
+ borderRadius: takeIf(iconButton, "50%"),
40
+ ...(style || {}),
41
+ }}
42
+ type={htmlType}
43
+ onClick={onClick}
44
+ className={className}>
45
+ <Show condition={icon}>
46
+ <div style={{
47
+ marginRight: takeIf(!iconButton, 8),
48
+ fontSize: takeIf(iconButton, 18, 12),
49
+ width: takeIf(iconButton, "100%", 12),
50
+ height: takeIf(iconButton, "100%", 12),
51
+ ...appStyles.center,
52
+ }}>
53
+ {icon}
54
+ </div>
55
+ </Show>
56
+ <div>
57
+ {children || title}
58
+ </div>
59
+ </button>
60
+ )
61
+ }
62
+
63
+ export default Button;
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import ListItem from "../ListItem";
3
+ import appStyles from "../../utils/styles";
4
+ import Show from "../Show";
5
+
6
+ const Card = props => {
7
+ const {
8
+ style,
9
+ avatar,
10
+ title,
11
+ titleRenderer,
12
+ titleStyle,
13
+ headerStyle,
14
+ titleContainerStyle,
15
+ description,
16
+ onHeaderClick,
17
+ subtitle,
18
+ onTitleClick,
19
+ className,
20
+ cardStyle,
21
+ childrenContainerStyle,
22
+ children
23
+ } = props;
24
+ return (
25
+ <div style={{borderRadius: 10, padding: 16, ...(style || {})}} className={className}>
26
+ <Show condition={avatar || title || titleRenderer || description || subtitle}>
27
+ <ListItem
28
+ avatar={avatar}
29
+ title={title}
30
+ titleRenderer={titleRenderer}
31
+ style={{margin: 0, padding: 0, ...(titleContainerStyle || {})}}
32
+ titleContainerStyle={headerStyle}
33
+ titleStyle={{fontSize: 18, ...(titleStyle || {})}}
34
+ description={description}
35
+ subtitle={subtitle}
36
+ onTitleClick={onTitleClick}
37
+ onClick={onHeaderClick}
38
+ />
39
+ </Show>
40
+ <Show condition={children}>
41
+ <div style={{...appStyles.card, ...(cardStyle || {})}}>
42
+ <div style={{...(childrenContainerStyle || {})}}>
43
+ {children}
44
+ </div>
45
+ </div>
46
+ </Show>
47
+ </div>
48
+ )
49
+ }
50
+
51
+ export default Card
@@ -0,0 +1,44 @@
1
+ import React, {useCallback} from 'react';
2
+ import {SwatchesPicker} from "react-color";
3
+ import Popover from "../Popover";
4
+ import Show from "../Show";
5
+
6
+ const ColorPicker = props => {
7
+ const {
8
+ label,
9
+ value: _value,
10
+ onChange: _onChange,
11
+ title,
12
+ inputClassName,
13
+ colorClassName,
14
+ children,
15
+ } = props;
16
+
17
+ const onChange = useCallback(({hex}) => {
18
+ _onChange(hex)
19
+ }, [_onChange])
20
+
21
+ return (
22
+ <Popover overlay={
23
+ <div style={{backgroundColor: 'white', padding: 16}}>
24
+ <h3>{title || "Renk"}</h3>
25
+ <SwatchesPicker onChange={onChange}/>
26
+ </div>
27
+ }>
28
+ <>
29
+ <Show condition={children}>
30
+ {children}
31
+ </Show>
32
+ <Show condition={!children}>
33
+ <div className={inputClassName}>
34
+ <p style={{fontWeight: 500}}>{label}</p>
35
+ <div className={colorClassName} style={{backgroundColor: _value, height: 32, width: '100%'}}/>
36
+ </div>
37
+ </Show>
38
+ </>
39
+ </Popover>
40
+ )
41
+ }
42
+
43
+
44
+ export default ColorPicker;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import appStyles from "../../utils/styles";
3
+
4
+ const EmptyResult = props => {
5
+ const {icon, title, style, iconClassName, size: _size} = props;
6
+ const size = _size || 120
7
+ return (
8
+ <div style={{...(style || {})}}>
9
+ <div style={{
10
+ width: '100%',
11
+ ...appStyles.centerInColumn,
12
+ }}>
13
+ <div className={iconClassName} style={{...appStyles.center, ...appStyles.rounded(size)}}>
14
+ {icon}
15
+ </div>
16
+ </div>
17
+ <div>
18
+ <p style={{
19
+ textAlign: 'center',
20
+ fontWeight: '100',
21
+ fontSize: 24,
22
+ whiteSpace: 'pre-wrap',
23
+ color: 'black',
24
+ marginTop: 16
25
+ }}>{title}</p>
26
+ </div>
27
+ </div>
28
+ )
29
+ }
30
+ export default EmptyResult;
@@ -0,0 +1,35 @@
1
+ import React, {useEffect, useState} from "react";
2
+
3
+ const FadeAnimation = props => {
4
+ const {style, type: _type, duration: _duration, delay: _delay, onAnimationComplete, children} = props;
5
+ const duration = _duration || 100
6
+ const delay = _delay || 0
7
+ const type = _type || "in";
8
+ const [opacity, setOpacity] = useState(type === "in" ? 0 : 1);
9
+ const toValue = type === "in" ? 1 : 0;
10
+
11
+ useEffect(() => {
12
+ if (opacity === toValue && onAnimationComplete) {
13
+ setTimeout(() => {
14
+ onAnimationComplete()
15
+ }, duration + delay)
16
+ }
17
+ }, [opacity])
18
+
19
+ useEffect(() => {
20
+ if (type === "in") {
21
+ setOpacity(toValue)
22
+ } else {
23
+ setOpacity(toValue)
24
+ }
25
+ }, [type])
26
+
27
+
28
+ return (
29
+ <div style={{opacity: opacity, transition: `${duration}`, transitionDelay: delay, ...(style || {})}}>
30
+ {children}
31
+ </div>
32
+ )
33
+ }
34
+
35
+ export default FadeAnimation;
@@ -0,0 +1,25 @@
1
+ import { isNullOrUndefined } from '@reactivers/use-utils';
2
+ import Form, { Field as FField, useForm } from "rc-field-form";
3
+
4
+ const Field = props => {
5
+ const { style, name, children } = props;
6
+
7
+ return (
8
+ <div style={{ width: '100%', margin: '16px 0', ...(style || {}) }}>
9
+ <FieldOrChildren name={name} parentProps={props}>
10
+ {children}
11
+ </FieldOrChildren>
12
+ </div>
13
+ )
14
+ }
15
+
16
+ const FieldOrChildren = props => {
17
+ const { name, parentProps, children } = props;
18
+ if (isNullOrUndefined(name)) {
19
+ return children
20
+ }
21
+ return <FField {...parentProps} />
22
+ }
23
+
24
+ export { Form, Field, useForm };
25
+
@@ -0,0 +1 @@
1
+ export { Grid, Row, Col } from 'react-flexbox-grid';
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import appStyles from "../../utils/styles";
3
+ import Show from "../Show";
4
+
5
+ const Header = (props) => {
6
+ const {title, titleRenderer, style, titleStyle, rightContent} = props;
7
+
8
+ return (
9
+ <div style={{...appStyles.row, alignItems: 'center', minHeight: 48, ...(style || {})}}>
10
+ <div style={{flex: 1, ...(titleStyle || {})}}>
11
+ <Show condition={titleRenderer}>
12
+ <div style={{margin: 0, ...appStyles.cardTitle}}>{title}</div>
13
+ </Show>
14
+ </div>
15
+ {rightContent}
16
+ </div>
17
+ )
18
+ }
19
+
20
+ export default Header
@@ -0,0 +1,63 @@
1
+ import { coalasce, takeIf } from "@reactivers/use-utils";
2
+ import { useCallback, useState } from 'react';
3
+ import appStyles from '../../utils/styles';
4
+ import Show from "../Show";
5
+
6
+ const Image = props => {
7
+ const {
8
+ style,
9
+ className,
10
+ hidePlaceholder,
11
+ src,
12
+ alt,
13
+ onLoad: _onLoad,
14
+ placeholder: _placheholder,
15
+ size: _size,
16
+ ...rest
17
+ } = props;
18
+
19
+ const [loaded, setLoaded] = useState(false);
20
+
21
+ const size = takeIf(_size, { width: _size, height: _size, borderRadius: '50%' }, {});
22
+ const placeholder = coalasce(_placheholder, "P");
23
+ const fontSize = takeIf(isNaN(_size / 2), 24, _size / 2);
24
+ const displayImage = takeIf(loaded, undefined, 'none');
25
+
26
+ const onLoad = useCallback(() => {
27
+ setLoaded(true)
28
+ if (_onLoad)
29
+ _onLoad()
30
+ }, [_onLoad])
31
+
32
+ return (
33
+ <div style={{
34
+ ...size,
35
+ ...appStyles.defaultShadow,
36
+ ...appStyles.center,
37
+ backgroundColor: "#eee",
38
+ overflow: "hidden",
39
+ ...style,
40
+ }} className={className}>
41
+ <Show condition={src}>
42
+ <img
43
+ onLoad={onLoad}
44
+ src={src}
45
+ alt={alt}
46
+ style={{
47
+ ...appStyles.roundedImage,
48
+ ...style,
49
+ display: displayImage
50
+ }}
51
+ {...rest}
52
+ />
53
+ </Show>
54
+ <Show condition={!loaded && !hidePlaceholder}>
55
+ <div style={{ width: '100%', height: '100%', ...appStyles.center }}>
56
+ <p style={{ margin: 0, fontSize, fontWeight: 'bold', padding: 4 }}>{placeholder}</p>
57
+ </div>
58
+ </Show>
59
+ </div>
60
+ )
61
+ }
62
+
63
+ export default Image;
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import Button from "../Button";
3
+ import appStyles from "../../utils/styles";
4
+
5
+ const IncDecField = props => {
6
+ const {value, onChange, minusIcon, plusIcon, size: _size, style, minusDisabled, plusDisabled, children} = props;
7
+ return (
8
+ <div style={{
9
+ marginVertical: 16,
10
+ ...appStyles.row,
11
+ ...appStyles.spreadHorizontally,
12
+ ...(style || {})
13
+ }}>
14
+ <Button icon={minusIcon}
15
+ disabled={minusDisabled}
16
+ type='primary'
17
+ style={{
18
+ borderRadius: 10,
19
+ }}
20
+ onClick={() => onChange(value - 1)}
21
+ />
22
+ {children}
23
+ <Button icon={plusIcon}
24
+ disabled={plusDisabled}
25
+ type='primary'
26
+ style={{
27
+ borderRadius: 10,
28
+ }}
29
+ onClick={() => onChange(value + 1)}
30
+ />
31
+ </div>
32
+ )
33
+ }
34
+
35
+ export default IncDecField;
@@ -0,0 +1,150 @@
1
+ import { useApi } from "@reactivers/use-utils";
2
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
+ import Mapper from "../Mapper";
4
+ import Show from "../Show";
5
+
6
+ const InfiniteScrollView = forwardRef((props, ref) => {
7
+ const {
8
+ style,
9
+ endpoint,
10
+ apiOptions: _apiOptions,
11
+ shimmer,
12
+ onDataChange,
13
+ render,
14
+ pageSize: _pageSize,
15
+ empty,
16
+ reload,
17
+ filterOptions,
18
+ onReload,
19
+ loadingRenderer
20
+ } = props;
21
+ const apiOptions = _apiOptions || {};
22
+ const { method, params, onSuccess: apiOptionsOnSuccess } = apiOptions;
23
+
24
+ const pageSize = _pageSize || 5;
25
+ const [page, setPage] = useState(1);
26
+ const [data, setData] = useState([]);
27
+ const [filter, setFilter] = useState({});
28
+ const reloaderRef = useRef(null)
29
+
30
+
31
+ const updateDataByIndex = (index, item) => {
32
+ setData(oldData => {
33
+ oldData[index] = item;
34
+ return oldData;
35
+ })
36
+ }
37
+
38
+ useImperativeHandle(ref, () => ({
39
+ updateDataByIndex
40
+ }))
41
+
42
+ const containerView = useRef(null);
43
+
44
+ const onSuccess = response => {
45
+ if (apiOptionsOnSuccess) {
46
+ apiOptionsOnSuccess(response)
47
+ }
48
+ setData(oldData => {
49
+ const newData = response.data.currentPage === 1 ? response.data.results || [] : [...oldData, ...(response.data.results || [])]
50
+ if (onDataChange)
51
+ onDataChange(newData)
52
+ return newData;
53
+ })
54
+ }
55
+
56
+ const { fetched, firstTimeFetched, load: apiLoad, response } = useApi({ onSuccess })
57
+ const { pageCount } = response.data || {};
58
+ const hasNextPage = (page || 1) < (pageCount || 2);
59
+
60
+ const load = useCallback(() => {
61
+ const hasNextPage = (page || 1) <= (pageCount || 2);
62
+ if (!hasNextPage) return;
63
+
64
+ const _endpoint = `${endpoint}/${page}/${pageSize}`;
65
+ const _method = method || (filterOptions ? "POST" : "GET");
66
+ const _params = params || (filterOptions ? filter : undefined);
67
+
68
+ apiLoad({ endpoint: _endpoint, method: _method, params: _params })
69
+
70
+ }, [page, pageCount, endpoint, pageSize, method, params, filterOptions, apiLoad, filter])
71
+
72
+ const nextPage = useCallback(() => {
73
+ if (fetched && hasNextPage) {
74
+ setPage(oldPage => oldPage + 1);
75
+ }
76
+ }, [fetched, hasNextPage])
77
+
78
+ const onRefresh = useCallback(() => {
79
+ if (page === 1) {
80
+ load()
81
+ } else {
82
+ setPage(1)
83
+ }
84
+ }, [load, page]);
85
+
86
+ const shouldFetchNextPage = useCallback(() => {
87
+ if (!reloaderRef.current)
88
+ return false;
89
+ const reloaderRects = reloaderRef.current.getClientRects();
90
+ const reloaderOffsetY = reloaderRects[0].top;
91
+ const shouldFetch = reloaderOffsetY - 20 <= window.innerHeight
92
+ return shouldFetch;
93
+ }, [reloaderRef])
94
+
95
+ const onScroll = useCallback((event) => {
96
+ if (shouldFetchNextPage()) {
97
+ nextPage()
98
+ }
99
+ }, [shouldFetchNextPage, nextPage])
100
+
101
+ useEffect(() => {
102
+ if (shouldFetchNextPage())
103
+ nextPage()
104
+ }, [shouldFetchNextPage, nextPage])
105
+
106
+ useEffect(() => {
107
+ load()
108
+ }, [load])
109
+
110
+ useEffect(() => {
111
+ const appLayout = document.getElementsByTagName("body")[0];
112
+ appLayout.onscroll = onScroll
113
+ }, [onScroll])
114
+
115
+ useEffect(() => {
116
+ if (reload) {
117
+ onRefresh();
118
+ onReload()
119
+ }
120
+ }, [onRefresh, onReload, reload])
121
+
122
+ if (!firstTimeFetched) {
123
+ return shimmer ? <props.shimmer /> : <props.loadingRenderer />
124
+ }
125
+ const hasData = !!data.length
126
+
127
+ return (
128
+ <div style={{ padding: 16, ...(style || {}) }}
129
+ ref={containerView}>
130
+ <Show condition={hasData}>
131
+ <Mapper items={data} map={(item, index) => render(item, index, { page, pageSize })} />
132
+ <Show condition={hasNextPage}>
133
+ <div ref={reloaderRef}>
134
+ <Show condition={shimmer}>
135
+ <props.shimmer />
136
+ </Show>
137
+ <Show condition={loadingRenderer}>
138
+ <props.loadingRenderer style={{ marginTop: 16 }} />
139
+ </Show>
140
+ </div>
141
+ </Show>
142
+ </Show>
143
+ <Show condition={!hasData}>
144
+ {empty}
145
+ </Show>
146
+ </div>
147
+ )
148
+ })
149
+
150
+ export default InfiniteScrollView;