@wq/material-native 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +29 -0
  2. package/babel.config.cjs +10 -0
  3. package/jest.config.js +9 -0
  4. package/package.json +58 -0
  5. package/setup-jest.js +5 -0
  6. package/src/App.js +48 -0
  7. package/src/components/Breadcrumbs.js +3 -0
  8. package/src/components/Button.js +46 -0
  9. package/src/components/ButtonLink.js +14 -0
  10. package/src/components/CancelButton.js +3 -0
  11. package/src/components/CheckboxButton.js +3 -0
  12. package/src/components/Chip.js +67 -0
  13. package/src/components/Container.js +3 -0
  14. package/src/components/DeleteForm.js +64 -0
  15. package/src/components/Divider.js +3 -0
  16. package/src/components/ExpandableListItem.js +46 -0
  17. package/src/components/ExpansionPanel.js +22 -0
  18. package/src/components/Fab.js +29 -0
  19. package/src/components/Fieldset.js +3 -0
  20. package/src/components/FieldsetArray.js +21 -0
  21. package/src/components/FileArray.js +3 -0
  22. package/src/components/FlatFieldset.js +3 -0
  23. package/src/components/Footer.js +3 -0
  24. package/src/components/FooterContent.js +3 -0
  25. package/src/components/FormError.js +15 -0
  26. package/src/components/FormRoot.js +3 -0
  27. package/src/components/FormatJson.js +15 -0
  28. package/src/components/Header.js +22 -0
  29. package/src/components/HomeLink.js +3 -0
  30. package/src/components/HorizontalView.js +22 -0
  31. package/src/components/IconButton.js +32 -0
  32. package/src/components/IconSubmitButton.js +12 -0
  33. package/src/components/Link.js +15 -0
  34. package/src/components/List.js +3 -0
  35. package/src/components/ListItem.js +22 -0
  36. package/src/components/ListItemLink.js +13 -0
  37. package/src/components/ListSubheader.js +3 -0
  38. package/src/components/Main.js +3 -0
  39. package/src/components/Pagination.js +3 -0
  40. package/src/components/Popup.js +3 -0
  41. package/src/components/RadioButton.js +3 -0
  42. package/src/components/ScrollView.js +22 -0
  43. package/src/components/SidePanel.js +3 -0
  44. package/src/components/Spinner.js +16 -0
  45. package/src/components/SubmitButton.js +17 -0
  46. package/src/components/Switch.js +3 -0
  47. package/src/components/Table.js +3 -0
  48. package/src/components/TableBody.js +3 -0
  49. package/src/components/TableCell.js +3 -0
  50. package/src/components/TableContainer.js +2 -0
  51. package/src/components/TableHead.js +3 -0
  52. package/src/components/TablePagination.js +3 -0
  53. package/src/components/TableRow.js +3 -0
  54. package/src/components/TableTitle.js +3 -0
  55. package/src/components/Text.js +3 -0
  56. package/src/components/Typography.js +39 -0
  57. package/src/components/View.js +3 -0
  58. package/src/components/index.js +119 -0
  59. package/src/hooks.js +6 -0
  60. package/src/icons.js +9 -0
  61. package/src/index.js +45 -0
  62. package/src/inputs/Checkbox.js +38 -0
  63. package/src/inputs/DateTime.js +61 -0
  64. package/src/inputs/File.js +3 -0
  65. package/src/inputs/HelperText.js +3 -0
  66. package/src/inputs/Hidden.js +22 -0
  67. package/src/inputs/Image.js +3 -0
  68. package/src/inputs/Input.js +72 -0
  69. package/src/inputs/Radio.js +3 -0
  70. package/src/inputs/Select.js +52 -0
  71. package/src/inputs/Toggle.js +3 -0
  72. package/src/inputs/date-utils.js +21 -0
  73. package/src/inputs/index.js +30 -0
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { Button } from "react-native";
3
+ import { useNav } from "@wq/react";
4
+ import PropTypes from "prop-types";
5
+
6
+ export default function Link({ to, children }) {
7
+ const onPress = useNav(to);
8
+ // FIXME: Use styled text instead?
9
+ return <Button title={children} onPress={onPress} />;
10
+ }
11
+
12
+ Link.propTypes = {
13
+ to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
14
+ children: PropTypes.node,
15
+ };
@@ -0,0 +1,3 @@
1
+ import { Fragment } from "react";
2
+
3
+ export default Fragment;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { useIcon } from "@wq/react";
3
+ import { List } from "react-native-paper";
4
+ import PropTypes from "prop-types";
5
+
6
+ export default function ListItem({ children, description, icon, ...rest }) {
7
+ const Icon = useIcon(icon);
8
+ return (
9
+ <List.Item
10
+ title={children}
11
+ description={description}
12
+ left={Icon ? (props) => <List.Icon icon={Icon} {...props} /> : null}
13
+ {...rest}
14
+ />
15
+ );
16
+ }
17
+
18
+ ListItem.propTypes = {
19
+ children: PropTypes.node,
20
+ description: PropTypes.node,
21
+ icon: PropTypes.string,
22
+ };
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import { useNav, useComponents } from "@wq/react";
3
+ import PropTypes from "prop-types";
4
+
5
+ export default function ListItemLink({ to, ...rest }) {
6
+ const { ListItem } = useComponents(),
7
+ onPress = useNav(to);
8
+ return <ListItem onPress={onPress} {...rest} />;
9
+ }
10
+
11
+ ListItemLink.propTypes = {
12
+ to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
13
+ };
@@ -0,0 +1,3 @@
1
+ import { List } from "react-native-paper";
2
+
3
+ export default List.Subheader;
@@ -0,0 +1,3 @@
1
+ import { Fragment } from "react";
2
+
3
+ export default Fragment;
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { ScrollView as PaperScrollView } from "react-native";
3
+ import { useTheme } from "react-native-paper";
4
+ import PropTypes from "prop-types";
5
+
6
+ export default function ScrollView({ style, ...rest }) {
7
+ const theme = useTheme();
8
+ return (
9
+ <PaperScrollView
10
+ style={{
11
+ flex: 1,
12
+ backgroundColor: theme.colors.background,
13
+ ...style,
14
+ }}
15
+ {...rest}
16
+ />
17
+ );
18
+ }
19
+
20
+ ScrollView.propTypes = {
21
+ style: PropTypes.object,
22
+ };
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { ActivityIndicator, Modal, Portal } from "react-native-paper";
3
+ import { useSpinner } from "@wq/react";
4
+
5
+ export default function Spinner() {
6
+ const { active } = useSpinner();
7
+
8
+ // FIXME: text, type
9
+ return (
10
+ <Portal>
11
+ <Modal visible={active}>
12
+ <ActivityIndicator />
13
+ </Modal>
14
+ </Portal>
15
+ );
16
+ }
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { useComponents } from "@wq/react";
3
+ import { useFormikContext } from "formik";
4
+
5
+ export default function SubmitButton(props) {
6
+ const { Button } = useComponents(),
7
+ { isSubmitting, submitForm } = useFormikContext();
8
+
9
+ return (
10
+ <Button
11
+ mode="contained"
12
+ disabled={isSubmitting}
13
+ onPress={submitForm}
14
+ {...props}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,3 @@
1
+ import { DataTable } from "react-native-paper";
2
+
3
+ export default DataTable;
@@ -0,0 +1,3 @@
1
+ import { Fragment } from "react";
2
+
3
+ export default Fragment;
@@ -0,0 +1,3 @@
1
+ import { DataTable } from "react-native-paper";
2
+
3
+ export default DataTable.Cell;
@@ -0,0 +1,2 @@
1
+ import { Fragment } from "react";
2
+ export default Fragment;
@@ -0,0 +1,3 @@
1
+ import { DataTable } from "react-native-paper";
2
+
3
+ export default DataTable.Header;
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,3 @@
1
+ import { DataTable } from "react-native-paper";
2
+
3
+ export default DataTable.Row;
@@ -0,0 +1,3 @@
1
+ import { DataTable } from "react-native-paper";
2
+
3
+ export default DataTable.Title;
@@ -0,0 +1,3 @@
1
+ import { Text } from "react-native";
2
+
3
+ export default Text;
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import {
3
+ Text,
4
+ Subheading,
5
+ Title,
6
+ Paragraph,
7
+ Headline,
8
+ Caption,
9
+ } from "react-native-paper";
10
+ import PropTypes from "prop-types";
11
+
12
+ const variants = {
13
+ default: Text,
14
+ h1: Headline,
15
+ h2: Headline,
16
+ h3: Headline,
17
+ h4: Headline,
18
+ h5: Headline,
19
+ h6: Title,
20
+ subtitle1: Subheading,
21
+ subtitle2: Subheading,
22
+ body1: Paragraph,
23
+ body2: Paragraph,
24
+ caption: Caption,
25
+ overline: Caption,
26
+ };
27
+
28
+ export default function Typography({ variant, children, ...rest }) {
29
+ const Component = variants[variant] || variants.default;
30
+ if (variant === "overline" && typeof children === "string") {
31
+ children = children.toUpperCase();
32
+ }
33
+ return <Component {...rest}>{children}</Component>;
34
+ }
35
+
36
+ Typography.propTypes = {
37
+ variant: PropTypes.string,
38
+ children: PropTypes.node,
39
+ };
@@ -0,0 +1,3 @@
1
+ import { View } from "react-native";
2
+
3
+ export default View;
@@ -0,0 +1,119 @@
1
+ import Container from "./Container.js";
2
+ import Header from "./Header.js";
3
+ import Main from "./Main.js";
4
+ import Footer from "./Footer.js";
5
+ import FooterContent from "./FooterContent.js";
6
+ import View from "./View.js";
7
+ import ScrollView from "./ScrollView.js";
8
+ import HorizontalView from "./HorizontalView.js";
9
+ import Text from "./Text.js";
10
+ import Typography from "./Typography.js";
11
+ import FormatJson from "./FormatJson.js";
12
+
13
+ import Link from "./Link.js";
14
+ import Button from "./Button.js";
15
+ import ButtonLink from "./ButtonLink.js";
16
+ import HomeLink from "./HomeLink.js";
17
+ import IconButton from "./IconButton.js";
18
+ import Fab from "./Fab.js";
19
+ import Chip from "./Chip.js";
20
+ import Switch from "./Switch.js";
21
+ import CheckboxButton from "./CheckboxButton.js";
22
+ import RadioButton from "./RadioButton.js";
23
+
24
+ import List from "./List.js";
25
+ import ListItem from "./ListItem.js";
26
+ import ListItemLink from "./ListItemLink.js";
27
+ import ListSubheader from "./ListSubheader.js";
28
+ import ExpansionPanel from "./ExpansionPanel.js";
29
+ import ExpandableListItem from "./ExpandableListItem.js";
30
+ import Divider from "./Divider.js";
31
+
32
+ import Table from "./Table.js";
33
+ import TableHead from "./TableHead.js";
34
+ import TableBody from "./TableBody.js";
35
+ import TableRow from "./TableRow.js";
36
+ import TableTitle from "./TableTitle.js";
37
+ import TableCell from "./TableCell.js";
38
+ import TableContainer from "./TableContainer.js";
39
+ import TablePagination from "./TablePagination.js";
40
+
41
+ import FormRoot from "./FormRoot.js";
42
+ import FormError from "./FormError.js";
43
+ import Fieldset from "./Fieldset.js";
44
+ import FlatFieldset from "./FlatFieldset.js";
45
+ import FieldsetArray from "./FieldsetArray.js";
46
+ import FileArray from "./FileArray.js";
47
+ import CancelButton from "./CancelButton.js";
48
+ import SubmitButton from "./SubmitButton.js";
49
+ import IconSubmitButton from "./IconSubmitButton.js";
50
+ import DeleteForm from "./DeleteForm.js";
51
+
52
+ import Spinner from "./Spinner.js";
53
+ import Popup from "./Popup.js";
54
+ import SidePanel from "./SidePanel.js";
55
+ import Breadcrumbs from "./Breadcrumbs.js";
56
+ import Pagination from "./Pagination.js";
57
+
58
+ export {
59
+ Container,
60
+ Header,
61
+ Main,
62
+ Footer,
63
+ FooterContent,
64
+ View,
65
+ ScrollView,
66
+ HorizontalView,
67
+ Text,
68
+ Typography,
69
+ FormatJson,
70
+ };
71
+
72
+ export {
73
+ Link,
74
+ Button,
75
+ ButtonLink,
76
+ HomeLink,
77
+ IconButton,
78
+ Fab,
79
+ Chip,
80
+ Switch,
81
+ CheckboxButton,
82
+ RadioButton,
83
+ };
84
+
85
+ export {
86
+ List,
87
+ ListItem,
88
+ ListItemLink,
89
+ ListSubheader,
90
+ ExpansionPanel,
91
+ ExpandableListItem,
92
+ Divider,
93
+ };
94
+
95
+ export {
96
+ Table,
97
+ TableHead,
98
+ TableBody,
99
+ TableRow,
100
+ TableTitle,
101
+ TableCell,
102
+ TableContainer,
103
+ TablePagination,
104
+ };
105
+
106
+ export {
107
+ FormRoot,
108
+ FormError,
109
+ Fieldset,
110
+ FlatFieldset,
111
+ FieldsetArray,
112
+ FileArray,
113
+ CancelButton,
114
+ SubmitButton,
115
+ IconSubmitButton,
116
+ DeleteForm,
117
+ };
118
+
119
+ export { Spinner, Popup, SidePanel, Breadcrumbs, Pagination };
package/src/hooks.js ADDED
@@ -0,0 +1,6 @@
1
+ import { useWindowDimensions } from "react-native";
2
+
3
+ export function useMinWidth(minWidth) {
4
+ const { width } = useWindowDimensions();
5
+ return width >= minWidth;
6
+ }
package/src/icons.js ADDED
@@ -0,0 +1,9 @@
1
+ export const Add = "plus",
2
+ Edit = "pencil",
3
+ Delete = "delete",
4
+ Success = "check",
5
+ Error = "alert-circle",
6
+ Pending = "sync",
7
+ GpsStart = "crosshairs-gps",
8
+ GpsStop = "crosshairs-off",
9
+ Search = "magnify";
package/src/index.js ADDED
@@ -0,0 +1,45 @@
1
+ import react from "@wq/react";
2
+ import App from "./App.js";
3
+ import * as components from "./components/index.js";
4
+ import * as inputs from "./inputs/index.js";
5
+ import * as icons from "./icons.js";
6
+ import { format, parse } from "./inputs/date-utils.js";
7
+
8
+ export default {
9
+ name: "material",
10
+ dependencies: [react],
11
+
12
+ config: {
13
+ theme: {
14
+ primary: "#7500ae",
15
+ secondary: "#0088bd",
16
+ },
17
+ inputFormat: {
18
+ date: "yyyy-MM-dd",
19
+ time: "HH:mm",
20
+ datetime: "yyyy-MM-dd HH:mm",
21
+ },
22
+ },
23
+
24
+ components: { App, ...components },
25
+ inputs: { ...inputs },
26
+ icons: { ...icons },
27
+
28
+ init(config) {
29
+ if (!config) {
30
+ return;
31
+ }
32
+ if (config.theme) {
33
+ Object.assign(this.config.theme, config.theme);
34
+ }
35
+ if (config.inputFormat) {
36
+ Object.assign(this.config.inputFormat, config.inputFormat);
37
+ }
38
+ },
39
+ };
40
+
41
+ export { App };
42
+ export * from "./components/index.js";
43
+ export * from "./inputs/index.js";
44
+ export * from "./hooks.js";
45
+ export { format, parse };
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import { useField } from "formik";
3
+ import { Checkbox as PaperCheckbox, useTheme } from "react-native-paper";
4
+ import { Text, View } from "react-native";
5
+ import PropTypes from "prop-types";
6
+
7
+ export default function Checkbox({ name, label }) {
8
+ const theme = useTheme(),
9
+ [, meta, helpers] = useField(name),
10
+ { value } = meta,
11
+ { setValue } = helpers;
12
+
13
+ function toggleChecked() {
14
+ setValue(!value);
15
+ }
16
+
17
+ return (
18
+ <View
19
+ style={{ flexDirection: "row", alignItems: "center", padding: 8 }}
20
+ >
21
+ <PaperCheckbox.Android
22
+ status={value ? "checked" : "unchecked"}
23
+ onPress={toggleChecked}
24
+ />
25
+ <Text
26
+ style={{ color: theme.colors.text, fontSize: 16, flex: 1 }}
27
+ onPress={toggleChecked}
28
+ >
29
+ {label}
30
+ </Text>
31
+ </View>
32
+ );
33
+ }
34
+
35
+ Checkbox.propTypes = {
36
+ name: PropTypes.string,
37
+ label: PropTypes.string,
38
+ };
@@ -0,0 +1,61 @@
1
+ import React, { useState } from "react";
2
+ import DateTimePickerModal from "react-native-modal-datetime-picker";
3
+ import { Text } from "react-native";
4
+ import { TouchableRipple, TextInput } from "react-native-paper";
5
+ import { useField } from "formik";
6
+ import PropTypes from "prop-types";
7
+ import { format, parse } from "./date-utils.js";
8
+
9
+ const displayFormat = {
10
+ date: (value) => parse.date(value).toLocaleDateString(),
11
+ time: (value) => parse.time(value).toLocaleTimeString(),
12
+ datetime: (value) => parse.datetime(value).toLocaleString(),
13
+ };
14
+
15
+ export default function DateTime({ name, type, label }) {
16
+ type = type.toLowerCase();
17
+
18
+ const [, meta, helpers] = useField(name),
19
+ { value } = meta,
20
+ { setValue } = helpers;
21
+
22
+ const [show, setShow] = useState(false);
23
+
24
+ function showPicker() {
25
+ setShow(true);
26
+ }
27
+ function hidePicker() {
28
+ setShow(false);
29
+ }
30
+ function onConfirm(val) {
31
+ hidePicker();
32
+ setValue(format[type](val));
33
+ }
34
+
35
+ return (
36
+ <>
37
+ <TextInput
38
+ label={label}
39
+ value={value ? displayFormat[type](value) : ""}
40
+ render={({ style, value }) => (
41
+ <TouchableRipple onPress={showPicker}>
42
+ <Text style={style}>{value}</Text>
43
+ </TouchableRipple>
44
+ )}
45
+ />
46
+ <DateTimePickerModal
47
+ isVisible={show}
48
+ mode={type}
49
+ date={parse[type](value) || new Date()}
50
+ onConfirm={onConfirm}
51
+ onCancel={hidePicker}
52
+ />
53
+ </>
54
+ );
55
+ }
56
+
57
+ DateTime.propTypes = {
58
+ name: PropTypes.string,
59
+ type: PropTypes.string,
60
+ label: PropTypes.string,
61
+ };
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,3 @@
1
+ import { HelperText } from "react-native-paper";
2
+
3
+ export default HelperText;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { Field, ErrorMessage } from "formik";
3
+ import { HelperText } from "react-native-paper";
4
+ import PropTypes from "prop-types";
5
+
6
+ function Empty() {
7
+ return null;
8
+ }
9
+
10
+ export default function Hidden(props) {
11
+ const { name } = props;
12
+ return (
13
+ <>
14
+ <Field {...props} component={Empty} />
15
+ <ErrorMessage type="error" component={HelperText} name={name} />
16
+ </>
17
+ );
18
+ }
19
+
20
+ Hidden.propTypes = {
21
+ name: PropTypes.string,
22
+ };
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { TextInput } from "react-native-paper";
3
+ import { useField } from "formik";
4
+ import { useHtmlInput } from "@wq/react";
5
+ import PropTypes from "prop-types";
6
+
7
+ const keyboards = {
8
+ int: "number-pad",
9
+ decimal: "decimal-pad",
10
+ tel: "phone-pad",
11
+ email: "email-address",
12
+ };
13
+
14
+ export default function Input(props) {
15
+ const { name, type, label, style, min, max } = props,
16
+ { maxLength } = useHtmlInput(props),
17
+ [, meta, helpers] = useField(name),
18
+ { value } = meta,
19
+ { setValue, setTouched } = helpers;
20
+
21
+ function handleChange(value) {
22
+ if (type === "int" || type === "decimal") {
23
+ if (type === "int") {
24
+ value = parseInt(value);
25
+ } else {
26
+ value = +value;
27
+ }
28
+ if (Number.isNaN(value)) {
29
+ setValue(null);
30
+ } else {
31
+ if (typeof min === "number" && value < min) {
32
+ value = min;
33
+ }
34
+ if (typeof max === "number" && value > max) {
35
+ value = max;
36
+ }
37
+ setValue(value);
38
+ }
39
+ } else {
40
+ setValue(value);
41
+ }
42
+ }
43
+
44
+ let formatValue;
45
+ if (type === "int" || type === "decimal") {
46
+ formatValue = typeof value === "number" ? "" + value : "";
47
+ } else {
48
+ formatValue = value;
49
+ }
50
+
51
+ return (
52
+ <TextInput
53
+ label={label}
54
+ multiline={type === "text"}
55
+ keyboardType={keyboards[type] || "default"}
56
+ maxLength={maxLength}
57
+ onChangeText={handleChange}
58
+ onBlur={() => setTouched(true)}
59
+ value={formatValue}
60
+ style={style}
61
+ />
62
+ );
63
+ }
64
+
65
+ Input.propTypes = {
66
+ name: PropTypes.string,
67
+ type: PropTypes.string,
68
+ label: PropTypes.string,
69
+ style: PropTypes.object,
70
+ min: PropTypes.number,
71
+ max: PropTypes.number,
72
+ };
@@ -0,0 +1,3 @@
1
+ export default function NotImplemented() {
2
+ return null; // FIXME
3
+ }