@wq/material-native 2.0.0-alpha.0 → 2.0.0-alpha.2
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.
- package/babel.config.cjs +1 -1
- package/jest.config.js +1 -5
- package/package.json +16 -17
- package/src/components/{ExpansionPanel.js → Accordion.js} +2 -2
- package/src/components/BottomNavigation.js +3 -0
- package/src/components/BottomNavigationAction.js +3 -0
- package/src/components/CheckboxButton.js +25 -2
- package/src/components/Fab.js +0 -1
- package/src/components/FileArray.js +11 -2
- package/src/components/Img.js +102 -0
- package/src/components/ListItem.js +17 -1
- package/src/components/NavMenuFixed.js +3 -0
- package/src/components/NavMenuPopup.js +3 -0
- package/src/components/Popup.js +18 -2
- package/src/components/RadioButton.js +25 -2
- package/src/components/SidePanel.js +93 -2
- package/src/components/Switch.js +17 -2
- package/src/components/TabGroup.js +38 -0
- package/src/components/TabItem.js +16 -0
- package/src/components/TableCell.js +23 -1
- package/src/components/index.js +17 -2
- package/src/icons.js +16 -1
- package/src/inputs/Checkbox.js +22 -13
- package/src/inputs/DateTime.js +8 -5
- package/src/inputs/File.js +175 -2
- package/src/inputs/HelperText.js +28 -2
- package/src/inputs/Hidden.js +5 -4
- package/src/inputs/Image.js +5 -2
- package/src/inputs/Input.js +17 -11
- package/src/inputs/Radio.js +31 -2
- package/src/inputs/Select.js +22 -16
- package/src/inputs/Toggle.js +37 -2
- package/setup-jest.js +0 -5
package/babel.config.cjs
CHANGED
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wq/material-native",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.2",
|
|
4
4
|
"description": "React Native and Expo bindings for @wq/material",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -28,31 +28,30 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://wq.io/@wq/material",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@wq/react": "^2.0.0-alpha.
|
|
32
|
-
"date-fns": "^2.
|
|
31
|
+
"@wq/react": "^2.0.0-alpha.2",
|
|
32
|
+
"date-fns": "^2.30.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@react-native-async-storage/async-storage": "*",
|
|
36
|
-
"@react-native-community/datetimepicker": "*",
|
|
37
|
-
"@react-native-community/masked-view": "*",
|
|
38
36
|
"@react-native-community/netinfo": "*",
|
|
39
|
-
"@react-navigation/native": "*",
|
|
40
37
|
"@react-navigation/stack": "*",
|
|
38
|
+
"expo-application": "*",
|
|
39
|
+
"expo-document-picker": "*",
|
|
40
|
+
"expo-image-picker": "*",
|
|
41
41
|
"react-native-modal-datetime-picker": "*",
|
|
42
42
|
"react-native-paper": "*",
|
|
43
43
|
"react-native-picker-select": "*"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@react-native-async-storage/async-storage": "^1.
|
|
47
|
-
"@react-native-community/
|
|
48
|
-
"@react-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"react-native-
|
|
55
|
-
"react-native-
|
|
56
|
-
"react-native-picker-select": "^8.0.4"
|
|
46
|
+
"@react-native-async-storage/async-storage": "^1.19.1",
|
|
47
|
+
"@react-native-community/netinfo": "^9.4.1",
|
|
48
|
+
"@react-navigation/stack": "^6.3.17",
|
|
49
|
+
"expo-application": "^5.3.0",
|
|
50
|
+
"expo-document-picker": "^11.5.4",
|
|
51
|
+
"expo-image-picker": "^14.3.2",
|
|
52
|
+
"jest-expo": "^49.0.0",
|
|
53
|
+
"react-native-modal-datetime-picker": "^17.0.0",
|
|
54
|
+
"react-native-paper": "^5.10.1",
|
|
55
|
+
"react-native-picker-select": "github:Expensify/react-native-picker-select#eae0585"
|
|
57
56
|
}
|
|
58
57
|
}
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { List } from "react-native-paper";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
|
|
5
|
-
export default function
|
|
5
|
+
export default function Accordion({ summary, children, open, onToggle }) {
|
|
6
6
|
let handleToggle;
|
|
7
7
|
if (onToggle) {
|
|
8
8
|
handleToggle = () => onToggle(!open);
|
|
@@ -14,7 +14,7 @@ export default function ExpansionPanel({ summary, children, open, onToggle }) {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Accordion.propTypes = {
|
|
18
18
|
summary: PropTypes.node,
|
|
19
19
|
children: PropTypes.node,
|
|
20
20
|
open: PropTypes.bool,
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Checkbox as PaperCheckbox, useTheme } from "react-native-paper";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
|
|
5
|
+
const STATUSES = {
|
|
6
|
+
true: "checked",
|
|
7
|
+
false: "unchecked",
|
|
8
|
+
undefined: "indeterminate",
|
|
9
|
+
null: "indeterminate",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function Checkbox({ checked, color, ...props }) {
|
|
13
|
+
const status = STATUSES[checked],
|
|
14
|
+
theme = useTheme();
|
|
15
|
+
if (color === "primary") {
|
|
16
|
+
color = theme.colors.primary;
|
|
17
|
+
} else if (color === "secondary") {
|
|
18
|
+
color = theme.colors.accent;
|
|
19
|
+
}
|
|
20
|
+
return <PaperCheckbox status={status} {...props} />;
|
|
3
21
|
}
|
|
22
|
+
|
|
23
|
+
Checkbox.propTypes = {
|
|
24
|
+
checked: PropTypes.bool,
|
|
25
|
+
color: PropTypes.string,
|
|
26
|
+
};
|
package/src/components/Fab.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { List } from "react-native-paper";
|
|
3
|
+
import FieldsetArray from "./FieldsetArray.js";
|
|
4
|
+
|
|
5
|
+
export default function FileArray(props) {
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
{props.label && <List.Subheader>{props.label}</List.Subheader>}
|
|
9
|
+
<FieldsetArray {...props} />
|
|
10
|
+
</>
|
|
11
|
+
);
|
|
3
12
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { Image, TouchableOpacity, Dimensions } from "react-native";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
|
|
5
|
+
export default function Img({ src, onPress, onClick, style, ...props }) {
|
|
6
|
+
const [source, setSource] = useState(src ? { uri: src } : null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!src) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (typeof src !== "string") {
|
|
12
|
+
setSource(src);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
Image.getSize(src, (width, height) => {
|
|
16
|
+
setSource({ uri: src, width, height });
|
|
17
|
+
});
|
|
18
|
+
}, [src]);
|
|
19
|
+
|
|
20
|
+
let resizeMode;
|
|
21
|
+
if (
|
|
22
|
+
style &&
|
|
23
|
+
typeof style.maxHeight === "number" &&
|
|
24
|
+
typeof style.maxWidth === "string" &&
|
|
25
|
+
style.maxWidth.endsWith("vw")
|
|
26
|
+
) {
|
|
27
|
+
resizeMode = "contain";
|
|
28
|
+
const ratio =
|
|
29
|
+
source && source.width && source.height
|
|
30
|
+
? source.width / source.height
|
|
31
|
+
: 1,
|
|
32
|
+
maxWidth =
|
|
33
|
+
(+style.maxWidth.replace(/vw/g, "") / 100) *
|
|
34
|
+
Dimensions.get("window").width;
|
|
35
|
+
style = {
|
|
36
|
+
...style,
|
|
37
|
+
maxHeight: undefined,
|
|
38
|
+
maxWidth: undefined,
|
|
39
|
+
height: style.maxHeight,
|
|
40
|
+
width: Math.min(ratio * style.maxHeight, maxWidth),
|
|
41
|
+
};
|
|
42
|
+
} else if (
|
|
43
|
+
style &&
|
|
44
|
+
typeof style.maxHeight === "string" &&
|
|
45
|
+
typeof style.maxWidth === "string"
|
|
46
|
+
) {
|
|
47
|
+
resizeMode = "contain";
|
|
48
|
+
style = {
|
|
49
|
+
...style,
|
|
50
|
+
maxHeight: undefined,
|
|
51
|
+
maxWidth: undefined,
|
|
52
|
+
height: style.maxHeight.replace("vh", "%"),
|
|
53
|
+
width: style.maxWidth.replace("vw", "%"),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const hasSize =
|
|
58
|
+
(source && (source === src || (source.width && source.height))) ||
|
|
59
|
+
props.width ||
|
|
60
|
+
props.height ||
|
|
61
|
+
(style && style.width) ||
|
|
62
|
+
(style && style.height);
|
|
63
|
+
if (src && !hasSize) {
|
|
64
|
+
return null;
|
|
65
|
+
} else if (onPress || onClick) {
|
|
66
|
+
return (
|
|
67
|
+
<TouchableOpacity
|
|
68
|
+
onPress={onPress || onClick}
|
|
69
|
+
style={{
|
|
70
|
+
...style,
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
justifyContent: "center",
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<Image
|
|
76
|
+
resizeMode={resizeMode}
|
|
77
|
+
style={style}
|
|
78
|
+
source={source}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
</TouchableOpacity>
|
|
82
|
+
);
|
|
83
|
+
} else {
|
|
84
|
+
return (
|
|
85
|
+
<Image
|
|
86
|
+
resizeMode={resizeMode}
|
|
87
|
+
style={style}
|
|
88
|
+
source={source}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Img.propTypes = {
|
|
96
|
+
src: PropTypes.oneOfType(PropTypes.string, PropTypes.number),
|
|
97
|
+
width: PropTypes.number,
|
|
98
|
+
height: PropTypes.number,
|
|
99
|
+
onPress: PropTypes.func,
|
|
100
|
+
onClick: PropTypes.func,
|
|
101
|
+
style: PropTypes.object,
|
|
102
|
+
};
|
|
@@ -3,13 +3,26 @@ import { useIcon } from "@wq/react";
|
|
|
3
3
|
import { List } from "react-native-paper";
|
|
4
4
|
import PropTypes from "prop-types";
|
|
5
5
|
|
|
6
|
-
export default function ListItem({
|
|
6
|
+
export default function ListItem({
|
|
7
|
+
children,
|
|
8
|
+
description,
|
|
9
|
+
icon,
|
|
10
|
+
dense,
|
|
11
|
+
style,
|
|
12
|
+
onClick,
|
|
13
|
+
...rest
|
|
14
|
+
}) {
|
|
7
15
|
const Icon = useIcon(icon);
|
|
8
16
|
return (
|
|
9
17
|
<List.Item
|
|
10
18
|
title={children}
|
|
11
19
|
description={description}
|
|
12
20
|
left={Icon ? (props) => <List.Icon icon={Icon} {...props} /> : null}
|
|
21
|
+
onPress={onClick}
|
|
22
|
+
style={{
|
|
23
|
+
...(dense && { paddingTop: 4, paddingBottom: 4 }),
|
|
24
|
+
...style,
|
|
25
|
+
}}
|
|
13
26
|
{...rest}
|
|
14
27
|
/>
|
|
15
28
|
);
|
|
@@ -19,4 +32,7 @@ ListItem.propTypes = {
|
|
|
19
32
|
children: PropTypes.node,
|
|
20
33
|
description: PropTypes.node,
|
|
21
34
|
icon: PropTypes.string,
|
|
35
|
+
dense: PropTypes.bool,
|
|
36
|
+
style: PropTypes.object,
|
|
37
|
+
onClick: PropTypes.func,
|
|
22
38
|
};
|
package/src/components/Popup.js
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Modal, Portal } from "react-native-paper";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
|
|
5
|
+
export default function Popup({ open, onClose, children, ...rest }) {
|
|
6
|
+
return (
|
|
7
|
+
<Portal>
|
|
8
|
+
<Modal visible={open} onDismiss={onClose} {...rest}>
|
|
9
|
+
{children}
|
|
10
|
+
</Modal>
|
|
11
|
+
</Portal>
|
|
12
|
+
);
|
|
3
13
|
}
|
|
14
|
+
|
|
15
|
+
Popup.propTypes = {
|
|
16
|
+
open: PropTypes.bool,
|
|
17
|
+
onClose: PropTypes.func,
|
|
18
|
+
children: PropTypes.node,
|
|
19
|
+
};
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { RadioButton as PaperRadioButton, useTheme } from "react-native-paper";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
|
|
5
|
+
const STATUSES = {
|
|
6
|
+
true: "checked",
|
|
7
|
+
false: "unchecked",
|
|
8
|
+
undefined: "unchecked",
|
|
9
|
+
null: "unchecked",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function RadioButton({ checked, color, ...rest }) {
|
|
13
|
+
const status = STATUSES[checked],
|
|
14
|
+
theme = useTheme();
|
|
15
|
+
if (color === "primary") {
|
|
16
|
+
color = theme.colors.primary;
|
|
17
|
+
} else if (color === "secondary") {
|
|
18
|
+
color = theme.colors.accent;
|
|
19
|
+
}
|
|
20
|
+
return <PaperRadioButton status={status} {...rest} />;
|
|
3
21
|
}
|
|
22
|
+
|
|
23
|
+
RadioButton.propTypes = {
|
|
24
|
+
checked: PropTypes.bool,
|
|
25
|
+
color: PropTypes.string,
|
|
26
|
+
};
|
|
@@ -1,3 +1,94 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { useComponents } from "@wq/react";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import { useTheme } from "react-native-paper";
|
|
5
|
+
import { useMinWidth } from "../hooks.js";
|
|
6
|
+
import PropTypes from "prop-types";
|
|
7
|
+
|
|
8
|
+
export default function SidePanel({
|
|
9
|
+
anchor: anchorSpec = "top-left",
|
|
10
|
+
children,
|
|
11
|
+
compactChildren,
|
|
12
|
+
onChange,
|
|
13
|
+
}) {
|
|
14
|
+
const mobile = !useMinWidth(900),
|
|
15
|
+
theme = useTheme(),
|
|
16
|
+
anchor = anchorSpec.endsWith("right") ? "right" : "left",
|
|
17
|
+
vAnchor = anchorSpec.startsWith("bottom") ? "bottom" : "top",
|
|
18
|
+
[open, setOpen] = useState(!mobile),
|
|
19
|
+
{ IconButton } = useComponents(),
|
|
20
|
+
drawerStyle = {},
|
|
21
|
+
iconStyle = {};
|
|
22
|
+
|
|
23
|
+
drawerStyle.zIndex = 1000;
|
|
24
|
+
drawerStyle.width = open ? 280 : 50;
|
|
25
|
+
drawerStyle.borderColor = "rgba(0, 0, 0, 0.12)";
|
|
26
|
+
|
|
27
|
+
if (mobile && !open) {
|
|
28
|
+
drawerStyle.backgroundColor = theme.colors.background;
|
|
29
|
+
drawerStyle.position = "absolute";
|
|
30
|
+
if (vAnchor === "bottom") {
|
|
31
|
+
drawerStyle.bottom = 0;
|
|
32
|
+
drawerStyle.borderTopWidth = 1;
|
|
33
|
+
} else {
|
|
34
|
+
drawerStyle.top = 0;
|
|
35
|
+
drawerStyle.borderBottomWidth = 1;
|
|
36
|
+
}
|
|
37
|
+
if (anchor === "right") {
|
|
38
|
+
drawerStyle.right = 1;
|
|
39
|
+
} else {
|
|
40
|
+
drawerStyle.left = 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (anchor === "right") {
|
|
45
|
+
iconStyle.transform = [{ rotate: "180deg" }];
|
|
46
|
+
drawerStyle.borderLeftWidth = 1;
|
|
47
|
+
} else {
|
|
48
|
+
drawerStyle.borderRightWidth = 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (onChange) {
|
|
53
|
+
onChange(open);
|
|
54
|
+
}
|
|
55
|
+
}, [open, onChange]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<View style={drawerStyle}>
|
|
59
|
+
{open ? (
|
|
60
|
+
<View
|
|
61
|
+
style={{
|
|
62
|
+
display: "flex",
|
|
63
|
+
flexDirection: "row",
|
|
64
|
+
position: "sticky",
|
|
65
|
+
top: 0,
|
|
66
|
+
zIndex: 100,
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
{anchor === "left" && <View style={{ flex: 1 }} />}
|
|
70
|
+
<IconButton
|
|
71
|
+
icon="panel-close"
|
|
72
|
+
style={iconStyle}
|
|
73
|
+
onClick={() => setOpen(false)}
|
|
74
|
+
/>
|
|
75
|
+
{anchor === "right" && <View style={{ flex: 1 }} />}
|
|
76
|
+
</View>
|
|
77
|
+
) : (
|
|
78
|
+
<IconButton
|
|
79
|
+
icon="panel-open"
|
|
80
|
+
style={iconStyle}
|
|
81
|
+
onClick={() => setOpen(true)}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
{open ? children : compactChildren}
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
3
87
|
}
|
|
88
|
+
|
|
89
|
+
SidePanel.propTypes = {
|
|
90
|
+
children: PropTypes.node,
|
|
91
|
+
compactChildren: PropTypes.node,
|
|
92
|
+
onChange: PropTypes.func,
|
|
93
|
+
anchor: PropTypes.string,
|
|
94
|
+
};
|
package/src/components/Switch.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Switch as PaperSwitch, useTheme } from "react-native-paper";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
|
|
5
|
+
export default function Switch({ checked: value, color, ...rest }) {
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
if (color === "primary") {
|
|
8
|
+
color = theme.colors.primary;
|
|
9
|
+
} else if (color === "secondary") {
|
|
10
|
+
color = theme.colors.accent;
|
|
11
|
+
}
|
|
12
|
+
return <PaperSwitch value={value} {...rest} />;
|
|
3
13
|
}
|
|
14
|
+
|
|
15
|
+
Switch.propTypes = {
|
|
16
|
+
checked: PropTypes.bool,
|
|
17
|
+
color: PropTypes.string,
|
|
18
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { useIconComponents } from "@wq/react";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import { SegmentedButtons } from "react-native-paper";
|
|
5
|
+
import PropTypes from "prop-types";
|
|
6
|
+
|
|
7
|
+
export default function TabGroup({ children, style, ...rest }) {
|
|
8
|
+
const tabs = React.Children.toArray(children),
|
|
9
|
+
[value, setValue] = useState(tabs[0].props.value),
|
|
10
|
+
activeTab = tabs.find((tab) => tab.props.value === value),
|
|
11
|
+
handleChange = (tab) => setValue(tab),
|
|
12
|
+
icons = useIconComponents(),
|
|
13
|
+
buttons = tabs.map((tab) => ({
|
|
14
|
+
value: tab.props.value,
|
|
15
|
+
label: tab.props.label,
|
|
16
|
+
icon: icons[tab.props.icon],
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View style={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
|
21
|
+
<SegmentedButtons
|
|
22
|
+
value={value}
|
|
23
|
+
onValueChange={handleChange}
|
|
24
|
+
buttons={buttons}
|
|
25
|
+
style={{ padding: 8, ...style }}
|
|
26
|
+
{...rest}
|
|
27
|
+
/>
|
|
28
|
+
<View style={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
|
29
|
+
{activeTab && activeTab.props.children}
|
|
30
|
+
</View>
|
|
31
|
+
</View>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TabGroup.propTypes = {
|
|
36
|
+
children: PropTypes.node,
|
|
37
|
+
style: PropTypes.object,
|
|
38
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
|
|
4
|
+
export default function TabItem({ value, icon, label, children }) {
|
|
5
|
+
/* eslint no-unused-vars: off */
|
|
6
|
+
|
|
7
|
+
// All rendering handled by TabGroup
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
TabItem.propTypes = {
|
|
12
|
+
value: PropTypes.string,
|
|
13
|
+
icon: PropTypes.string,
|
|
14
|
+
label: PropTypes.string,
|
|
15
|
+
children: PropTypes.node,
|
|
16
|
+
};
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
1
3
|
import { DataTable } from "react-native-paper";
|
|
4
|
+
import PropTypes from "prop-types";
|
|
2
5
|
|
|
3
|
-
export default
|
|
6
|
+
export default function TableCell({ interactive, children, ...rest }) {
|
|
7
|
+
const Cell = interactive ? InteractiveCell : DataTable.Cell;
|
|
8
|
+
return <Cell {...rest}>{children}</Cell>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function InteractiveCell(props) {
|
|
12
|
+
return (
|
|
13
|
+
<View
|
|
14
|
+
style={{
|
|
15
|
+
flex: 1,
|
|
16
|
+
flexDirection: "row",
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
...props.style,
|
|
19
|
+
}}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
TableCell.propTypes = { interactive: PropTypes.bool, children: PropTypes.node };
|
package/src/components/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import Container from "./Container.js";
|
|
2
2
|
import Header from "./Header.js";
|
|
3
|
+
import NavMenuPopup from "./NavMenuPopup.js";
|
|
4
|
+
import NavMenuFixed from "./NavMenuFixed.js";
|
|
3
5
|
import Main from "./Main.js";
|
|
4
6
|
import Footer from "./Footer.js";
|
|
5
7
|
import FooterContent from "./FooterContent.js";
|
|
@@ -7,6 +9,7 @@ import View from "./View.js";
|
|
|
7
9
|
import ScrollView from "./ScrollView.js";
|
|
8
10
|
import HorizontalView from "./HorizontalView.js";
|
|
9
11
|
import Text from "./Text.js";
|
|
12
|
+
import Img from "./Img.js";
|
|
10
13
|
import Typography from "./Typography.js";
|
|
11
14
|
import FormatJson from "./FormatJson.js";
|
|
12
15
|
|
|
@@ -16,6 +19,10 @@ import ButtonLink from "./ButtonLink.js";
|
|
|
16
19
|
import HomeLink from "./HomeLink.js";
|
|
17
20
|
import IconButton from "./IconButton.js";
|
|
18
21
|
import Fab from "./Fab.js";
|
|
22
|
+
import TabGroup from "./TabGroup.js";
|
|
23
|
+
import TabItem from "./TabItem.js";
|
|
24
|
+
import BottomNavigation from "./BottomNavigation.js";
|
|
25
|
+
import BottomNavigationAction from "./BottomNavigationAction.js";
|
|
19
26
|
import Chip from "./Chip.js";
|
|
20
27
|
import Switch from "./Switch.js";
|
|
21
28
|
import CheckboxButton from "./CheckboxButton.js";
|
|
@@ -25,7 +32,7 @@ import List from "./List.js";
|
|
|
25
32
|
import ListItem from "./ListItem.js";
|
|
26
33
|
import ListItemLink from "./ListItemLink.js";
|
|
27
34
|
import ListSubheader from "./ListSubheader.js";
|
|
28
|
-
import
|
|
35
|
+
import Accordion from "./Accordion.js";
|
|
29
36
|
import ExpandableListItem from "./ExpandableListItem.js";
|
|
30
37
|
import Divider from "./Divider.js";
|
|
31
38
|
|
|
@@ -58,6 +65,8 @@ import Pagination from "./Pagination.js";
|
|
|
58
65
|
export {
|
|
59
66
|
Container,
|
|
60
67
|
Header,
|
|
68
|
+
NavMenuPopup,
|
|
69
|
+
NavMenuFixed,
|
|
61
70
|
Main,
|
|
62
71
|
Footer,
|
|
63
72
|
FooterContent,
|
|
@@ -65,6 +74,7 @@ export {
|
|
|
65
74
|
ScrollView,
|
|
66
75
|
HorizontalView,
|
|
67
76
|
Text,
|
|
77
|
+
Img,
|
|
68
78
|
Typography,
|
|
69
79
|
FormatJson,
|
|
70
80
|
};
|
|
@@ -76,6 +86,10 @@ export {
|
|
|
76
86
|
HomeLink,
|
|
77
87
|
IconButton,
|
|
78
88
|
Fab,
|
|
89
|
+
TabGroup,
|
|
90
|
+
TabItem,
|
|
91
|
+
BottomNavigation,
|
|
92
|
+
BottomNavigationAction,
|
|
79
93
|
Chip,
|
|
80
94
|
Switch,
|
|
81
95
|
CheckboxButton,
|
|
@@ -87,7 +101,8 @@ export {
|
|
|
87
101
|
ListItem,
|
|
88
102
|
ListItemLink,
|
|
89
103
|
ListSubheader,
|
|
90
|
-
|
|
104
|
+
Accordion,
|
|
105
|
+
Accordion as ExpansionPanel,
|
|
91
106
|
ExpandableListItem,
|
|
92
107
|
Divider,
|
|
93
108
|
};
|
package/src/icons.js
CHANGED
|
@@ -4,6 +4,21 @@ export const Add = "plus",
|
|
|
4
4
|
Success = "check",
|
|
5
5
|
Error = "alert-circle",
|
|
6
6
|
Pending = "sync",
|
|
7
|
+
Close = "close",
|
|
8
|
+
Expand = "chevron-down",
|
|
9
|
+
Collapse = "chevron-up",
|
|
10
|
+
PanelOpen = "chevron-right",
|
|
11
|
+
PanelClose = "chevron-left",
|
|
7
12
|
GpsStart = "crosshairs-gps",
|
|
8
13
|
GpsStop = "crosshairs-off",
|
|
9
|
-
Search = "magnify"
|
|
14
|
+
Search = "magnify",
|
|
15
|
+
List = "view-list",
|
|
16
|
+
Detail = "information",
|
|
17
|
+
Map = "map",
|
|
18
|
+
Home = "home",
|
|
19
|
+
BreadcrumbSeparator = "chevron-right",
|
|
20
|
+
Menu = "menu",
|
|
21
|
+
Login = "login",
|
|
22
|
+
Logout = "logout",
|
|
23
|
+
Config = "cog",
|
|
24
|
+
External = "open-in-new";
|
package/src/inputs/Checkbox.js
CHANGED
|
@@ -2,9 +2,10 @@ import React from "react";
|
|
|
2
2
|
import { useField } from "formik";
|
|
3
3
|
import { Checkbox as PaperCheckbox, useTheme } from "react-native-paper";
|
|
4
4
|
import { Text, View } from "react-native";
|
|
5
|
+
import HelperText from "./HelperText.js";
|
|
5
6
|
import PropTypes from "prop-types";
|
|
6
7
|
|
|
7
|
-
export default function Checkbox({ name, label }) {
|
|
8
|
+
export default function Checkbox({ name, label, hint }) {
|
|
8
9
|
const theme = useTheme(),
|
|
9
10
|
[, meta, helpers] = useField(name),
|
|
10
11
|
{ value } = meta,
|
|
@@ -15,19 +16,26 @@ export default function Checkbox({ name, label }) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
return (
|
|
18
|
-
<View
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<Text
|
|
26
|
-
style={{ color: theme.colors.text, fontSize: 16, flex: 1 }}
|
|
27
|
-
onPress={toggleChecked}
|
|
19
|
+
<View>
|
|
20
|
+
<View
|
|
21
|
+
style={{
|
|
22
|
+
flexDirection: "row",
|
|
23
|
+
alignItems: "center",
|
|
24
|
+
padding: 8,
|
|
25
|
+
}}
|
|
28
26
|
>
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
<PaperCheckbox.Android
|
|
28
|
+
status={value ? "checked" : "unchecked"}
|
|
29
|
+
onPress={toggleChecked}
|
|
30
|
+
/>
|
|
31
|
+
<Text
|
|
32
|
+
style={{ color: theme.colors.text, fontSize: 16, flex: 1 }}
|
|
33
|
+
onPress={toggleChecked}
|
|
34
|
+
>
|
|
35
|
+
{label}
|
|
36
|
+
</Text>
|
|
37
|
+
</View>
|
|
38
|
+
<HelperText name={name} hint={hint} />
|
|
31
39
|
</View>
|
|
32
40
|
);
|
|
33
41
|
}
|
|
@@ -35,4 +43,5 @@ export default function Checkbox({ name, label }) {
|
|
|
35
43
|
Checkbox.propTypes = {
|
|
36
44
|
name: PropTypes.string,
|
|
37
45
|
label: PropTypes.string,
|
|
46
|
+
hint: PropTypes.string,
|
|
38
47
|
};
|
package/src/inputs/DateTime.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import DateTimePickerModal from "react-native-modal-datetime-picker";
|
|
3
|
-
import { Text } from "react-native";
|
|
3
|
+
import { Text, View } from "react-native";
|
|
4
4
|
import { TouchableRipple, TextInput } from "react-native-paper";
|
|
5
5
|
import { useField } from "formik";
|
|
6
|
-
import PropTypes from "prop-types";
|
|
7
6
|
import { format, parse } from "./date-utils.js";
|
|
7
|
+
import HelperText from "./HelperText.js";
|
|
8
|
+
import PropTypes from "prop-types";
|
|
8
9
|
|
|
9
10
|
const displayFormat = {
|
|
10
11
|
date: (value) => parse.date(value).toLocaleDateString(),
|
|
@@ -12,7 +13,7 @@ const displayFormat = {
|
|
|
12
13
|
datetime: (value) => parse.datetime(value).toLocaleString(),
|
|
13
14
|
};
|
|
14
15
|
|
|
15
|
-
export default function DateTime({ name, type, label }) {
|
|
16
|
+
export default function DateTime({ name, type, label, hint }) {
|
|
16
17
|
type = type.toLowerCase();
|
|
17
18
|
|
|
18
19
|
const [, meta, helpers] = useField(name),
|
|
@@ -33,7 +34,7 @@ export default function DateTime({ name, type, label }) {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
return (
|
|
36
|
-
|
|
37
|
+
<View>
|
|
37
38
|
<TextInput
|
|
38
39
|
label={label}
|
|
39
40
|
value={value ? displayFormat[type](value) : ""}
|
|
@@ -43,6 +44,7 @@ export default function DateTime({ name, type, label }) {
|
|
|
43
44
|
</TouchableRipple>
|
|
44
45
|
)}
|
|
45
46
|
/>
|
|
47
|
+
<HelperText name={name} hint={hint} />
|
|
46
48
|
<DateTimePickerModal
|
|
47
49
|
isVisible={show}
|
|
48
50
|
mode={type}
|
|
@@ -50,7 +52,7 @@ export default function DateTime({ name, type, label }) {
|
|
|
50
52
|
onConfirm={onConfirm}
|
|
51
53
|
onCancel={hidePicker}
|
|
52
54
|
/>
|
|
53
|
-
|
|
55
|
+
</View>
|
|
54
56
|
);
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -58,4 +60,5 @@ DateTime.propTypes = {
|
|
|
58
60
|
name: PropTypes.string,
|
|
59
61
|
type: PropTypes.string,
|
|
60
62
|
label: PropTypes.string,
|
|
63
|
+
hint: PropTypes.string,
|
|
61
64
|
};
|
package/src/inputs/File.js
CHANGED
|
@@ -1,3 +1,176 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { useField } from "formik";
|
|
3
|
+
import { useComponents } from "@wq/react";
|
|
4
|
+
import { View, Text } from "react-native";
|
|
5
|
+
import { Button, List, Menu } from "react-native-paper";
|
|
6
|
+
import * as Application from "expo-application";
|
|
7
|
+
import * as DocumentPicker from "expo-document-picker";
|
|
8
|
+
import * as ImagePicker from "expo-image-picker";
|
|
9
|
+
import { format } from "./date-utils.js";
|
|
10
|
+
import HelperText from "./HelperText.js";
|
|
11
|
+
import PropTypes from "prop-types";
|
|
12
|
+
|
|
13
|
+
export default function File({ name, accept, capture, required, label, hint }) {
|
|
14
|
+
const [, { value }, { setValue }] = useField(name),
|
|
15
|
+
[imageOpen, setImageOpen] = useState(false),
|
|
16
|
+
[menuOpen, setMenuOpen] = useState(false),
|
|
17
|
+
{ Img, Popup } = useComponents(),
|
|
18
|
+
acceptImage = accept && accept.startsWith("image/"),
|
|
19
|
+
showImage = () => setImageOpen(true),
|
|
20
|
+
hideImage = () => setImageOpen(false),
|
|
21
|
+
showMenu = () => setMenuOpen(true),
|
|
22
|
+
hideMenu = () => setMenuOpen(false);
|
|
23
|
+
|
|
24
|
+
async function handleFile(mode) {
|
|
25
|
+
const value = await mode(accept, capture);
|
|
26
|
+
if (value || mode === clear) {
|
|
27
|
+
setValue(value);
|
|
28
|
+
}
|
|
29
|
+
hideMenu();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const modes = capture
|
|
33
|
+
? [takePhoto]
|
|
34
|
+
: acceptImage
|
|
35
|
+
? [takePhoto, pickFile]
|
|
36
|
+
: [pickFile];
|
|
37
|
+
|
|
38
|
+
if (value && !required) {
|
|
39
|
+
modes.push(clear);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<View>
|
|
44
|
+
{label && <List.Subheader>{label}</List.Subheader>}
|
|
45
|
+
<View
|
|
46
|
+
style={{
|
|
47
|
+
flexDirection: "row",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
padding: 8,
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
{isImage(value) && (
|
|
53
|
+
<Img
|
|
54
|
+
onPress={showImage}
|
|
55
|
+
style={{ width: 64, height: 64 }}
|
|
56
|
+
src={typeof value === "string" ? value : value.uri}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
{value && !isImage(value) && <List.Icon icon="attachment" />}
|
|
60
|
+
<Text style={{ flex: 1, padding: 8 }}>
|
|
61
|
+
{value ? value.name : "No file selected"}
|
|
62
|
+
</Text>
|
|
63
|
+
<Menu
|
|
64
|
+
visible={menuOpen}
|
|
65
|
+
onDismiss={hideMenu}
|
|
66
|
+
anchor={
|
|
67
|
+
<Button
|
|
68
|
+
onPress={
|
|
69
|
+
modes.length > 1
|
|
70
|
+
? showMenu
|
|
71
|
+
: () => handleFile(modes[0])
|
|
72
|
+
}
|
|
73
|
+
icon={value ? "pencil" : "plus"}
|
|
74
|
+
mode="contained-tonal"
|
|
75
|
+
>
|
|
76
|
+
{value
|
|
77
|
+
? "Change"
|
|
78
|
+
: capture
|
|
79
|
+
? "Add Photo"
|
|
80
|
+
: acceptImage
|
|
81
|
+
? "Add Image"
|
|
82
|
+
: "Add File"}
|
|
83
|
+
</Button>
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
{modes.map((mode) => (
|
|
87
|
+
<Menu.Item
|
|
88
|
+
key={mode.icon}
|
|
89
|
+
onPress={() => handleFile(mode)}
|
|
90
|
+
leadingIcon={mode.icon}
|
|
91
|
+
title={mode.label}
|
|
92
|
+
/>
|
|
93
|
+
))}
|
|
94
|
+
</Menu>
|
|
95
|
+
</View>
|
|
96
|
+
<HelperText name={name} hint={hint} />
|
|
97
|
+
<Popup open={value && imageOpen} onClose={hideImage}>
|
|
98
|
+
<Img
|
|
99
|
+
onPress={hideImage}
|
|
100
|
+
resizeMode="contain"
|
|
101
|
+
style={{
|
|
102
|
+
width: "90%",
|
|
103
|
+
height: "90%",
|
|
104
|
+
margin: "5%",
|
|
105
|
+
}}
|
|
106
|
+
src={typeof value === "string" ? value : value && value.uri}
|
|
107
|
+
/>
|
|
108
|
+
</Popup>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
3
111
|
}
|
|
112
|
+
File.propTypes = {
|
|
113
|
+
name: PropTypes.string,
|
|
114
|
+
accept: PropTypes.string,
|
|
115
|
+
capture: PropTypes.string,
|
|
116
|
+
required: PropTypes.bool,
|
|
117
|
+
label: PropTypes.string,
|
|
118
|
+
hint: PropTypes.string,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
function isImage(value) {
|
|
122
|
+
if (typeof value === "string") {
|
|
123
|
+
return IMAGE_EXTENSIONS.some((ext) =>
|
|
124
|
+
value.toLowerCase().endsWith(ext)
|
|
125
|
+
);
|
|
126
|
+
} else {
|
|
127
|
+
return (
|
|
128
|
+
value && value.type && value.type.startsWith("image/") && value.uri
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function generateName() {
|
|
134
|
+
const appName = Application.applicationName.replace(/ /, ""),
|
|
135
|
+
timestamp = new Date(),
|
|
136
|
+
date = format.date(timestamp).replace(/-/g, ""),
|
|
137
|
+
time = format.time(timestamp).replace(/:/g, "");
|
|
138
|
+
return `${appName}_${date}_${time}.jpg`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function pickFile(accept) {
|
|
142
|
+
const result = await DocumentPicker.getDocumentAsync({ type: accept });
|
|
143
|
+
if (result.assets && result.assets.length > 0) {
|
|
144
|
+
const { name, mimeType: type, uri } = result.assets[0];
|
|
145
|
+
return { name, type, uri };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
pickFile.icon = "folder";
|
|
149
|
+
pickFile.label = "Pick File";
|
|
150
|
+
|
|
151
|
+
async function takePhoto(accept, capture) {
|
|
152
|
+
const options = {};
|
|
153
|
+
if (capture) {
|
|
154
|
+
options.cameraType =
|
|
155
|
+
capture === "user"
|
|
156
|
+
? ImagePicker.CameraType.front
|
|
157
|
+
: ImagePicker.CameraType.back;
|
|
158
|
+
}
|
|
159
|
+
const result = await ImagePicker.launchCameraAsync(options);
|
|
160
|
+
if (result.assets && result.assets.length > 0) {
|
|
161
|
+
const name = generateName(),
|
|
162
|
+
type = "image/jpeg",
|
|
163
|
+
{ uri } = result.assets[0];
|
|
164
|
+
return { name, type, uri };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
takePhoto.icon = "camera";
|
|
168
|
+
takePhoto.label = "Take Photo";
|
|
169
|
+
|
|
170
|
+
function clear() {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
clear.icon = "delete";
|
|
174
|
+
clear.label = "Remove";
|
|
175
|
+
|
|
176
|
+
const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif"];
|
package/src/inputs/HelperText.js
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useFormikContext, getIn } from "formik";
|
|
3
|
+
import { HelperText as FormHelperText } from "react-native-paper";
|
|
4
|
+
import PropTypes from "prop-types";
|
|
2
5
|
|
|
3
|
-
export default HelperText
|
|
6
|
+
export default function HelperText({ name, hint }) {
|
|
7
|
+
const { errors, touched } = useFormikContext(),
|
|
8
|
+
error = getIn(errors, name),
|
|
9
|
+
showError = !!error && !!getIn(touched, name);
|
|
10
|
+
|
|
11
|
+
if (showError) {
|
|
12
|
+
hint = error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!hint) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<FormHelperText type={showError ? "error" : "info"}>
|
|
21
|
+
{hint}
|
|
22
|
+
</FormHelperText>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
HelperText.propTypes = {
|
|
27
|
+
name: PropTypes.string,
|
|
28
|
+
hint: PropTypes.string,
|
|
29
|
+
};
|
package/src/inputs/Hidden.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Field
|
|
3
|
-
import
|
|
2
|
+
import { Field } from "formik";
|
|
3
|
+
import HelperText from "./HelperText.js";
|
|
4
4
|
import PropTypes from "prop-types";
|
|
5
5
|
|
|
6
6
|
function Empty() {
|
|
@@ -8,15 +8,16 @@ function Empty() {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export default function Hidden(props) {
|
|
11
|
-
const { name } = props;
|
|
11
|
+
const { name, hint } = props;
|
|
12
12
|
return (
|
|
13
13
|
<>
|
|
14
14
|
<Field {...props} component={Empty} />
|
|
15
|
-
<
|
|
15
|
+
<HelperText name={name} hint={hint} />
|
|
16
16
|
</>
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
Hidden.propTypes = {
|
|
21
21
|
name: PropTypes.string,
|
|
22
|
+
hint: PropTypes.string,
|
|
22
23
|
};
|
package/src/inputs/Image.js
CHANGED
package/src/inputs/Input.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
2
3
|
import { TextInput } from "react-native-paper";
|
|
3
4
|
import { useField } from "formik";
|
|
4
5
|
import { useHtmlInput } from "@wq/react";
|
|
6
|
+
import HelperText from "./HelperText.js";
|
|
5
7
|
import PropTypes from "prop-types";
|
|
6
8
|
|
|
7
9
|
const keyboards = {
|
|
@@ -12,7 +14,7 @@ const keyboards = {
|
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export default function Input(props) {
|
|
15
|
-
const { name, type, label, style, min, max } = props,
|
|
17
|
+
const { name, type, label, hint, style, min, max } = props,
|
|
16
18
|
{ maxLength } = useHtmlInput(props),
|
|
17
19
|
[, meta, helpers] = useField(name),
|
|
18
20
|
{ value } = meta,
|
|
@@ -49,16 +51,19 @@ export default function Input(props) {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
return (
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
<View style={{ flex: (style || {}).flex }}>
|
|
55
|
+
<TextInput
|
|
56
|
+
label={label}
|
|
57
|
+
multiline={type === "text"}
|
|
58
|
+
keyboardType={keyboards[type] || "default"}
|
|
59
|
+
maxLength={maxLength}
|
|
60
|
+
onChangeText={handleChange}
|
|
61
|
+
onBlur={() => setTouched(true)}
|
|
62
|
+
value={formatValue}
|
|
63
|
+
style={style}
|
|
64
|
+
/>
|
|
65
|
+
<HelperText name={name} hint={hint} />
|
|
66
|
+
</View>
|
|
62
67
|
);
|
|
63
68
|
}
|
|
64
69
|
|
|
@@ -66,6 +71,7 @@ Input.propTypes = {
|
|
|
66
71
|
name: PropTypes.string,
|
|
67
72
|
type: PropTypes.string,
|
|
68
73
|
label: PropTypes.string,
|
|
74
|
+
hint: PropTypes.string,
|
|
69
75
|
style: PropTypes.object,
|
|
70
76
|
min: PropTypes.number,
|
|
71
77
|
max: PropTypes.number,
|
package/src/inputs/Radio.js
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { RadioButton, List } from "react-native-paper";
|
|
4
|
+
import HelperText from "./HelperText.js";
|
|
5
|
+
import { useField } from "formik";
|
|
6
|
+
import PropTypes from "prop-types";
|
|
7
|
+
|
|
8
|
+
export default function Radio({ name, choices, label, hint }) {
|
|
9
|
+
const [, { value }, { setValue }] = useField(name);
|
|
10
|
+
return (
|
|
11
|
+
<View>
|
|
12
|
+
<List.Subheader>{label}</List.Subheader>
|
|
13
|
+
<RadioButton.Group onValueChange={setValue} value={value}>
|
|
14
|
+
{choices.map((choice) => (
|
|
15
|
+
<RadioButton.Item
|
|
16
|
+
key={choice.name}
|
|
17
|
+
value={choice.name}
|
|
18
|
+
label={choice.label}
|
|
19
|
+
/>
|
|
20
|
+
))}
|
|
21
|
+
</RadioButton.Group>
|
|
22
|
+
<HelperText name={name} hint={hint} />
|
|
23
|
+
</View>
|
|
24
|
+
);
|
|
3
25
|
}
|
|
26
|
+
|
|
27
|
+
Radio.propTypes = {
|
|
28
|
+
name: PropTypes.string,
|
|
29
|
+
label: PropTypes.string,
|
|
30
|
+
hint: PropTypes.string,
|
|
31
|
+
choices: PropTypes.arrayOf(PropTypes.object),
|
|
32
|
+
};
|
package/src/inputs/Select.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import RNPickerSelect from "react-native-picker-select";
|
|
3
|
+
import { View } from "react-native";
|
|
3
4
|
import { TextInput, useTheme } from "react-native-paper";
|
|
4
5
|
import { useField } from "formik";
|
|
6
|
+
import HelperText from "./HelperText.js";
|
|
5
7
|
import PropTypes from "prop-types";
|
|
6
8
|
|
|
7
|
-
export default function Select({ name, choices, label }) {
|
|
9
|
+
export default function Select({ name, choices, label, hint }) {
|
|
8
10
|
const theme = useTheme(),
|
|
9
11
|
[, meta, helpers] = useField(name),
|
|
10
12
|
{ value } = meta,
|
|
@@ -27,26 +29,30 @@ export default function Select({ name, choices, label }) {
|
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
return (
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
<View>
|
|
33
|
+
<TextInput
|
|
34
|
+
label={label}
|
|
35
|
+
value={"-"}
|
|
36
|
+
render={() => (
|
|
37
|
+
<RNPickerSelect
|
|
38
|
+
value={value}
|
|
39
|
+
onValueChange={setValue}
|
|
40
|
+
items={choices.map(({ name, label }) => ({
|
|
41
|
+
value: name,
|
|
42
|
+
label,
|
|
43
|
+
}))}
|
|
44
|
+
style={styles}
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
/>
|
|
48
|
+
<HelperText name={name} hint={hint} />
|
|
49
|
+
</View>
|
|
45
50
|
);
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
Select.propTypes = {
|
|
49
54
|
name: PropTypes.string,
|
|
50
55
|
label: PropTypes.string,
|
|
56
|
+
hint: PropTypes.string,
|
|
51
57
|
choices: PropTypes.arrayOf(PropTypes.object),
|
|
52
58
|
};
|
package/src/inputs/Toggle.js
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, Text } from "react-native";
|
|
3
|
+
import { ToggleButton, List } from "react-native-paper";
|
|
4
|
+
import HelperText from "./HelperText.js";
|
|
5
|
+
import { useField } from "formik";
|
|
6
|
+
import PropTypes from "prop-types";
|
|
7
|
+
|
|
8
|
+
export default function Toggle({ name, choices, label, hint, style }) {
|
|
9
|
+
const [, { value }, { setValue }] = useField(name);
|
|
10
|
+
return (
|
|
11
|
+
<View>
|
|
12
|
+
<List.Subheader>{label}</List.Subheader>
|
|
13
|
+
<ToggleButton.Row
|
|
14
|
+
onValueChange={setValue}
|
|
15
|
+
value={value}
|
|
16
|
+
style={{ paddingLeft: 16, paddingBottom: 8, ...style }}
|
|
17
|
+
>
|
|
18
|
+
{choices.map((choice) => (
|
|
19
|
+
<ToggleButton
|
|
20
|
+
key={choice.name}
|
|
21
|
+
value={choice.name}
|
|
22
|
+
icon={() => <Text>{choice.label}</Text>}
|
|
23
|
+
style={{ width: undefined, padding: 8 }}
|
|
24
|
+
/>
|
|
25
|
+
))}
|
|
26
|
+
</ToggleButton.Row>
|
|
27
|
+
<HelperText name={name} hint={hint} />
|
|
28
|
+
</View>
|
|
29
|
+
);
|
|
3
30
|
}
|
|
31
|
+
|
|
32
|
+
Toggle.propTypes = {
|
|
33
|
+
name: PropTypes.string,
|
|
34
|
+
label: PropTypes.string,
|
|
35
|
+
hint: PropTypes.string,
|
|
36
|
+
choices: PropTypes.arrayOf(PropTypes.object),
|
|
37
|
+
style: PropTypes.object,
|
|
38
|
+
};
|