ferns-ui 0.26.0 → 0.26.1

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 (67) hide show
  1. package/dist/ActionSheet.d.ts +1 -1
  2. package/dist/ActionSheet.js +1 -2
  3. package/dist/ActionSheet.js.map +1 -1
  4. package/dist/CheckBox.d.ts +1 -4
  5. package/dist/CheckBox.js +33 -20
  6. package/dist/CheckBox.js.map +1 -1
  7. package/dist/Common.d.ts +44 -10
  8. package/dist/Common.js.map +1 -1
  9. package/dist/DateTimeField.android.d.ts +1 -1
  10. package/dist/DateTimeField.android.js +58 -16
  11. package/dist/DateTimeField.android.js.map +1 -1
  12. package/dist/DateTimeField.d.ts +2 -2
  13. package/dist/DateTimeField.ios.d.ts +1 -1
  14. package/dist/DateTimeField.ios.js +43 -12
  15. package/dist/DateTimeField.ios.js.map +1 -1
  16. package/dist/DateTimeField.js.map +1 -1
  17. package/dist/Field.js +2 -2
  18. package/dist/Field.js.map +1 -1
  19. package/dist/Mask.d.ts +2 -3
  20. package/dist/MediaQuery.d.ts +1 -0
  21. package/dist/MediaQuery.js +16 -0
  22. package/dist/MediaQuery.js.map +1 -1
  23. package/dist/Page.js.map +1 -1
  24. package/dist/SegmentedControl.js +6 -2
  25. package/dist/SegmentedControl.js.map +1 -1
  26. package/dist/SelectList.d.ts +1 -1
  27. package/dist/SelectList.js +9 -8
  28. package/dist/SelectList.js.map +1 -1
  29. package/dist/SplitPage.d.ts +3 -21
  30. package/dist/SplitPage.js +78 -23
  31. package/dist/SplitPage.js.map +1 -1
  32. package/dist/SplitPage.native.d.ts +3 -0
  33. package/dist/SplitPage.native.js +75 -0
  34. package/dist/SplitPage.native.js.map +1 -0
  35. package/dist/TapToEdit.js +11 -17
  36. package/dist/TapToEdit.js.map +1 -1
  37. package/dist/TextField.js +11 -6
  38. package/dist/TextField.js.map +1 -1
  39. package/dist/Utilities.d.ts +1 -0
  40. package/dist/Utilities.js +18 -2
  41. package/dist/Utilities.js.map +1 -1
  42. package/dist/WithLabel.d.ts +3 -3
  43. package/dist/WithLabel.js +3 -0
  44. package/dist/WithLabel.js.map +1 -1
  45. package/dist/index.d.ts +5 -4
  46. package/dist/index.js +1 -0
  47. package/dist/index.js.map +1 -1
  48. package/package.json +2 -1
  49. package/src/ActionSheet.tsx +2 -3
  50. package/src/CheckBox.tsx +85 -60
  51. package/src/Common.ts +49 -10
  52. package/src/DateTimeField.android.tsx +71 -26
  53. package/src/DateTimeField.ios.tsx +55 -13
  54. package/src/DateTimeField.tsx +2 -2
  55. package/src/Field.tsx +4 -4
  56. package/src/Mask.tsx +2 -2
  57. package/src/MediaQuery.ts +14 -0
  58. package/src/Page.tsx +0 -1
  59. package/src/SegmentedControl.tsx +3 -3
  60. package/src/SelectList.tsx +9 -2
  61. package/src/SplitPage.native.tsx +156 -0
  62. package/src/SplitPage.tsx +136 -47
  63. package/src/TapToEdit.tsx +9 -22
  64. package/src/TextField.tsx +26 -17
  65. package/src/Utilities.tsx +24 -3
  66. package/src/WithLabel.tsx +6 -3
  67. package/src/index.tsx +6 -5
@@ -1,9 +1,10 @@
1
1
  import DateTimePicker from "@react-native-community/datetimepicker";
2
2
  import moment from "moment-timezone";
3
- import React, {ReactElement} from "react";
3
+ import React, {ReactElement, useMemo, useState} from "react";
4
+ import {TextInput} from "react-native";
4
5
 
5
- import {Box} from "./Box";
6
6
  import {DateTimeFieldProps} from "./Common";
7
+ import {Unifier} from "./Unifier";
7
8
  import {WithLabel} from "./WithLabel";
8
9
 
9
10
  export const DateTimeField = ({
@@ -11,8 +12,45 @@ export const DateTimeField = ({
11
12
  value,
12
13
  onChange,
13
14
  errorMessage,
15
+ pickerType = "default",
16
+ dateFormat,
14
17
  errorMessageColor,
15
18
  }: DateTimeFieldProps): ReactElement => {
19
+ // const [showCalendar, setShowCalendar] = useState(false);
20
+ // const [showClock, setShowClock] = useState(false);
21
+ // const [tempDate, setTempDate] = useState<Date>();
22
+ const [pickerMode, setPickerMode] = useState(mode);
23
+ const [showPicker, setShowPicker] = useState(false);
24
+
25
+ const showCalendarFirst = mode === "datetime" || mode === "date";
26
+
27
+ const defaultFormat = useMemo(() => {
28
+ if (dateFormat) {
29
+ return dateFormat;
30
+ } else {
31
+ if (mode === "date") {
32
+ return "MMMM Do YYYY";
33
+ } else if (mode === "time") {
34
+ return "h:mm a";
35
+ } else {
36
+ return "MMMM Do YYYY, h:mm a";
37
+ }
38
+ }
39
+ }, [mode, dateFormat]);
40
+
41
+ const showMode = (currentMode: "date" | "time") => {
42
+ setShowPicker(true);
43
+ setPickerMode(currentMode);
44
+ };
45
+
46
+ const showDatePicker = () => {
47
+ showMode("date");
48
+ };
49
+
50
+ const showTimePicker = () => {
51
+ showMode("time");
52
+ };
53
+
16
54
  return (
17
55
  <WithLabel
18
56
  label={errorMessage}
@@ -21,35 +59,42 @@ export const DateTimeField = ({
21
59
  labelSize="sm"
22
60
  >
23
61
  <WithLabel>
24
- <Box maxWidth={300}>
62
+ <TextInput
63
+ inputMode="none"
64
+ style={{
65
+ flex: 1,
66
+ paddingTop: 10,
67
+ paddingRight: 10,
68
+ paddingBottom: 10,
69
+ paddingLeft: 10,
70
+ height: 40,
71
+ width: "100%",
72
+ color: Unifier.theme.darkGray,
73
+ fontFamily: Unifier.theme.primaryFont,
74
+ borderWidth: 1,
75
+ }}
76
+ value={moment(value).format(defaultFormat)}
77
+ onPressIn={() => {
78
+ showCalendarFirst ? showDatePicker() : showTimePicker();
79
+ }}
80
+ />
81
+ {showPicker && (
25
82
  <DateTimePicker
26
- display="spinner"
27
- mode={mode === "datetime" ? "date" : mode}
83
+ display={pickerType}
84
+ mode={pickerMode}
28
85
  testID="dateTimePicker"
29
- value={moment(value).toDate()}
30
- onChange={(event: any, date: any) => {
31
- if (!date) {
32
- return;
86
+ value={value}
87
+ onChange={(event, date) => {
88
+ if (date) {
89
+ onChange(date);
90
+ if (pickerMode === "date" && mode === "datetime") {
91
+ showTimePicker();
92
+ }
33
93
  }
34
- onChange(value);
94
+ setShowPicker(false);
35
95
  }}
36
96
  />
37
- {mode === "datetime" && (
38
- <DateTimePicker
39
- display="spinner"
40
- mode="time"
41
- testID="dateTimePicker"
42
- value={moment(value).toDate()}
43
- onChange={(event: any, date: any) => {
44
- // fix to append to date object
45
- if (!date) {
46
- return;
47
- }
48
- onChange(value);
49
- }}
50
- />
51
- )}
52
- </Box>
97
+ )}
53
98
  </WithLabel>
54
99
  </WithLabel>
55
100
  );
@@ -1,9 +1,10 @@
1
1
  import DateTimePicker from "@react-native-community/datetimepicker";
2
2
  import moment from "moment-timezone";
3
- import React, {ReactElement} from "react";
3
+ import React, {ReactElement, useMemo, useState} from "react";
4
+ import {TextInput} from "react-native";
4
5
 
5
- import {Box} from "./Box";
6
6
  import {DateTimeFieldProps} from "./Common";
7
+ import {Unifier} from "./Unifier";
7
8
  import {WithLabel} from "./WithLabel";
8
9
 
9
10
  export const DateTimeField = ({
@@ -12,29 +13,70 @@ export const DateTimeField = ({
12
13
  onChange,
13
14
  errorMessage,
14
15
  errorMessageColor,
16
+ dateFormat,
17
+ pickerType = "inline",
18
+ label,
15
19
  }: DateTimeFieldProps): ReactElement => {
20
+ const [showPicker, setShowPicker] = useState(false);
21
+
22
+ const defaultFormat = useMemo(() => {
23
+ if (dateFormat) {
24
+ return dateFormat;
25
+ } else {
26
+ if (mode === "date") {
27
+ return "MMMM Do YYYY";
28
+ } else if (mode === "time") {
29
+ return "h:mm a";
30
+ } else {
31
+ return "MMMM Do YYYY, h:mm a";
32
+ }
33
+ }
34
+ }, [mode, dateFormat]);
35
+
16
36
  return (
17
- <WithLabel
18
- label={errorMessage}
19
- labelColor={errorMessageColor || "red"}
20
- labelPlacement="after"
21
- labelSize="sm"
22
- >
23
- <WithLabel>
24
- <Box maxWidth={300}>
37
+ <WithLabel label={label} labelSize="lg">
38
+ <WithLabel
39
+ label={errorMessage}
40
+ labelColor={errorMessageColor || "red"}
41
+ labelPlacement="after"
42
+ labelSize="sm"
43
+ >
44
+ <TextInput
45
+ inputMode="none"
46
+ style={{
47
+ flex: 1,
48
+ paddingTop: 10,
49
+ paddingRight: 10,
50
+ paddingBottom: 10,
51
+ paddingLeft: 10,
52
+ height: 40,
53
+ width: "100%",
54
+ color: Unifier.theme.darkGray,
55
+ fontFamily: Unifier.theme.primaryFont,
56
+ borderWidth: 1,
57
+ }}
58
+ value={moment(value).format(defaultFormat)}
59
+ onPressIn={() => {
60
+ setShowPicker(!showPicker);
61
+ }}
62
+ />
63
+
64
+ {showPicker && (
25
65
  <DateTimePicker
26
- display="spinner"
66
+ accentColor={Unifier.theme.primary}
67
+ display={pickerType}
27
68
  mode={mode}
69
+ style={{alignSelf: "flex-start"}}
28
70
  testID="dateTimePicker"
29
71
  value={moment(value).toDate()}
30
72
  onChange={(event: any, date: any) => {
31
73
  if (!date) {
32
74
  return;
33
75
  }
34
- onChange(value);
76
+ onChange(date);
35
77
  }}
36
78
  />
37
- </Box>
79
+ )}
38
80
  </WithLabel>
39
81
  </WithLabel>
40
82
  );
@@ -4,7 +4,7 @@ import DateTimePickerWeb from "react-datetime-picker";
4
4
  import TimePicker from "react-time-picker";
5
5
 
6
6
  import {Box} from "./Box";
7
- import {DateTimeFieldProps} from "./Common";
7
+ import {DateTimeFieldProps, WithChildren} from "./Common";
8
8
  import {WithLabel} from "./WithLabel";
9
9
 
10
10
  export const DateTimeField = ({
@@ -13,7 +13,7 @@ export const DateTimeField = ({
13
13
  onChange,
14
14
  errorMessage,
15
15
  errorMessageColor,
16
- }: DateTimeFieldProps): ReactElement => {
16
+ }: WithChildren<DateTimeFieldProps>): ReactElement => {
17
17
  return (
18
18
  <WithLabel
19
19
  label={errorMessage}
package/src/Field.tsx CHANGED
@@ -2,7 +2,7 @@ import React from "react";
2
2
 
3
3
  import {Box} from "./Box";
4
4
  import {CheckBox} from "./CheckBox";
5
- import {AddressInterface, FieldWithLabelsProps, TextFieldType} from "./Common";
5
+ import {AddressInterface, FieldWithLabelsProps, ReactChildren, TextFieldType} from "./Common";
6
6
  import {USSTATESLIST} from "./Constants";
7
7
  import {CustomSelect} from "./CustomSelect";
8
8
  import {FieldWithLabels} from "./FieldWithLabels";
@@ -66,11 +66,11 @@ export const Field = ({
66
66
  onChange(switchValue);
67
67
  };
68
68
 
69
- const renderField = () => {
69
+ const renderField = (): ReactChildren => {
70
70
  if (type === "select") {
71
71
  if (!options) {
72
72
  console.error("Field with type=select require options");
73
- return null;
73
+ return undefined;
74
74
  }
75
75
  return (
76
76
  <SelectList
@@ -85,7 +85,7 @@ export const Field = ({
85
85
  } else if (type === "multiselect") {
86
86
  if (!options) {
87
87
  console.error("Field with type=multiselect require options");
88
- return null;
88
+ return undefined;
89
89
  }
90
90
  return (
91
91
  <Box width="100%">
package/src/Mask.tsx CHANGED
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
  import {View} from "react-native";
3
3
 
4
- import {MaskProps} from "./Common";
4
+ import {MaskProps, ReactChildren} from "./Common";
5
5
 
6
- export function Mask(props: MaskProps): React.ReactElement | null {
6
+ export function Mask(props: MaskProps): ReactChildren {
7
7
  if (props.shape === "rounded") {
8
8
  return <View style={{overflow: "hidden", borderRadius: 12}}>{props.children}</View>;
9
9
  } else if (props.shape === "circle") {
package/src/MediaQuery.ts CHANGED
@@ -26,3 +26,17 @@ export function mediaQueryLargerThan(size: "xs" | "sm" | "md" | "lg"): boolean {
26
26
  }
27
27
  return false;
28
28
  }
29
+
30
+ export function mediaQuerySmallerThan(size: "xs" | "sm" | "md" | "lg"): boolean {
31
+ const media = mediaQuery();
32
+ if (size === "lg") {
33
+ return true;
34
+ } else if (size === "md") {
35
+ return ["xs", "sm", "md"].includes(media);
36
+ } else if (size === "sm") {
37
+ return ["xs", "sm"].includes(media);
38
+ } else if (size === "xs") {
39
+ return ["xs"].includes(media);
40
+ }
41
+ return false;
42
+ }
package/src/Page.tsx CHANGED
@@ -97,7 +97,6 @@ export class Page extends React.Component<PageProps, {}> {
97
97
  padding={this.props.padding !== undefined ? this.props.padding : 2}
98
98
  scroll={this.props.scroll === undefined ? true : this.props.scroll}
99
99
  width="100%"
100
- // color="ligh"
101
100
  >
102
101
  {this.renderHeader()}
103
102
  {this.props.loading === true && (
@@ -27,7 +27,7 @@ export const SegmentedControl = ({
27
27
  }
28
28
 
29
29
  if (!multiselect && selectedItemIndexes?.length && selectedItemIndexes?.length > 1) {
30
- console.warn("Muliple selections not allowed without multiselect flag");
30
+ console.warn("Multiple selections not allowed without multiselect flag");
31
31
  return null;
32
32
  }
33
33
 
@@ -49,7 +49,7 @@ export const SegmentedControl = ({
49
49
  display="flex"
50
50
  height={40}
51
51
  justifyContent="between"
52
- padding={1}
52
+ // padding={1}
53
53
  rounding={3}
54
54
  width="100%"
55
55
  >
@@ -58,7 +58,7 @@ export const SegmentedControl = ({
58
58
  key={index}
59
59
  color={isTabActive(index)}
60
60
  height="100%"
61
- paddingX={2}
61
+ // paddingX={2}
62
62
  rounding={3}
63
63
  width={`${100 / items.length}%`}
64
64
  >
@@ -30,18 +30,25 @@ export function SelectList({
30
30
  labelColor,
31
31
  style,
32
32
  placeholder,
33
+ disabled,
33
34
  }: SelectListProps) {
34
35
  const withLabelProps = {label, labelColor};
35
36
 
37
+ let backgroundColor = style?.backgroundColor || Unifier.theme.white;
38
+ if (disabled) {
39
+ backgroundColor = Unifier.theme.lightGray;
40
+ }
41
+
36
42
  return (
37
43
  <WithLabel {...withLabelProps}>
38
44
  <RNPickerSelect
39
- // Icon only needed for iOs, web and android use default icons
40
45
  Icon={() => {
46
+ // Icon only needed for iOS, web and android use default icons
41
47
  return Platform.OS === "ios" ? (
42
48
  <Icon color="darkGray" name="angle-down" size="md" />
43
49
  ) : null;
44
50
  }}
51
+ disabled={disabled}
45
52
  items={options}
46
53
  placeholder={placeholder ? {label: placeholder, value: ""} : {}}
47
54
  style={{
@@ -54,7 +61,7 @@ export function SelectList({
54
61
  borderColor: style?.borderColor || Unifier.theme.gray,
55
62
  borderWidth: style?.borderWidth || 1,
56
63
  borderRadius: style?.borderRadius || 5,
57
- backgroundColor: style?.backgroundColor || Unifier.theme.white,
64
+ backgroundColor,
58
65
  },
59
66
  inputIOS: {
60
67
  paddingVertical: 12,
@@ -0,0 +1,156 @@
1
+ // TODO: Update SplitPage native to have desktop UX for tablet sized screens
2
+ import React, {Children, useCallback, useEffect, useState} from "react";
3
+ import {Dimensions, ListRenderItemInfo, View} from "react-native";
4
+ import {SwiperFlatList} from "react-native-swiper-flatlist";
5
+
6
+ import {Box} from "./Box";
7
+ import {SplitPageProps} from "./Common";
8
+ import {FlatList} from "./FlatList";
9
+ import {IconButton} from "./IconButton";
10
+ import {Spinner} from "./Spinner";
11
+ import {Unifier} from "./Unifier";
12
+
13
+ export const SplitPage = ({
14
+ children,
15
+ loading = false,
16
+ color,
17
+ keyboardOffset,
18
+ renderListViewItem,
19
+ renderListViewHeader,
20
+ renderContent,
21
+ onSelectionChange = () => {},
22
+ listViewData,
23
+ listViewExtraData,
24
+ bottomNavBarHeight,
25
+ showItemList,
26
+ }: SplitPageProps) => {
27
+ const [selectedId, setSelectedId] = useState<number | undefined>(undefined);
28
+
29
+ const elementArray = Children.toArray(children);
30
+ const {width} = Dimensions.get("window");
31
+
32
+ const onItemSelect = useCallback(
33
+ (item: ListRenderItemInfo<any>) => {
34
+ setSelectedId(item.index);
35
+ onSelectionChange(item);
36
+ },
37
+ [onSelectionChange]
38
+ );
39
+
40
+ const onItemDeselect = useCallback(() => {
41
+ setSelectedId(undefined);
42
+ onSelectionChange(undefined);
43
+ }, [onSelectionChange]);
44
+
45
+ useEffect(() => {
46
+ if (showItemList) {
47
+ onItemDeselect();
48
+ }
49
+ }, [showItemList, onItemDeselect]);
50
+
51
+ if (!children && !renderContent) {
52
+ console.warn("A child node is required");
53
+ return null;
54
+ }
55
+
56
+ const renderItem = (itemInfo: ListRenderItemInfo<any>) => {
57
+ return (
58
+ <Box
59
+ onClick={() => {
60
+ Unifier.utils.haptic();
61
+ onItemSelect(itemInfo);
62
+ }}
63
+ >
64
+ {renderListViewItem(itemInfo)}
65
+ </Box>
66
+ );
67
+ };
68
+
69
+ const renderList = () => {
70
+ if (selectedId !== undefined) {
71
+ return null;
72
+ }
73
+
74
+ return (
75
+ <View
76
+ style={{
77
+ width: "100%",
78
+ maxWidth: "100%",
79
+ flexGrow: 1,
80
+ flexShrink: 0,
81
+ display: "flex",
82
+ flexDirection: "column",
83
+ paddingBottom: bottomNavBarHeight,
84
+ }}
85
+ >
86
+ {renderListViewHeader && renderListViewHeader()}
87
+ <FlatList
88
+ data={listViewData}
89
+ extraData={listViewExtraData}
90
+ keyExtractor={(item) => item.id}
91
+ renderItem={renderItem}
92
+ />
93
+ </View>
94
+ );
95
+ };
96
+
97
+ const renderListContent = () => {
98
+ if (selectedId === undefined) {
99
+ return null;
100
+ }
101
+ return (
102
+ <Box flex="grow" padding={2}>
103
+ <Box width="100%">
104
+ <IconButton
105
+ accessibilityLabel="close"
106
+ icon="times"
107
+ iconColor="darkGray"
108
+ onClick={() => onItemDeselect()}
109
+ />
110
+ </Box>
111
+ {renderContent && renderContent(selectedId)}
112
+ </Box>
113
+ );
114
+ };
115
+
116
+ const renderChildrenContent = () => {
117
+ if (selectedId === undefined) {
118
+ return null;
119
+ }
120
+ return (
121
+ <SwiperFlatList
122
+ nestedScrollEnabled
123
+ renderAll
124
+ showPagination={elementArray.length > 1}
125
+ style={{width: "100%"}}
126
+ >
127
+ {elementArray.map((element, i) => {
128
+ return (
129
+ <View
130
+ key={i}
131
+ style={{width, height: elementArray.length > 1 ? "95%" : "100%", padding: 4}}
132
+ >
133
+ {element}
134
+ </View>
135
+ );
136
+ })}
137
+ </SwiperFlatList>
138
+ );
139
+ };
140
+
141
+ const renderMainContent = renderContent ? renderListContent() : renderChildrenContent();
142
+
143
+ return (
144
+ <Box
145
+ avoidKeyboard
146
+ color={color || "lightGray"}
147
+ flex="grow"
148
+ height="100%"
149
+ keyboardOffset={keyboardOffset}
150
+ width="100%"
151
+ >
152
+ {loading === true && <Spinner color={Unifier.theme.darkGray as any} size="md" />}
153
+ {selectedId === undefined ? renderList() : renderMainContent}
154
+ </Box>
155
+ );
156
+ };